# Delegating to sub-agents

# Delegating to sub-agents

Consumer-facing guide to `agents.delegate` — the delegation primitive that lets an agent (or an API caller) dispatch a **sub-agent** to investigate one goal and report back. For the general agent-session model see [Agents](../agents.md); for authoring the named agents a delegation can target, see the `GET/POST /agents` roster API.

## What delegation is

A delegation hands one bounded investigation to a sub-agent. The sub-agent is a **named prompt** — an Amdahl library agent (like `researcher`), a workspace agent you authored, or an ephemeral inline prompt supplied just for this dispatch — running with an investigation-only toolkit: data queries, cluster search, knowledge-base search, external market search, and session memory.

Sub-agents **investigate and suggest; they never commit**. A sub cannot write documents, curate long-term memory, send notifications, or publish anything. It returns its findings to the caller, and the caller decides what to do with them.

## The goal harness

Every delegation carries three fields that shape what comes back:

| Field            | Required | What it does                                                                                |
| ---------------- | -------- | ------------------------------------------------------------------------------------------- |
| `task`           | yes      | What the sub-agent should do — its opening instruction.                                     |
| `desired_output` | no       | Freeform description of what you want back (shape, depth, format). Defaults to the task.    |
| `context`        | no       | What the sub-agent should know before starting (why you are asking, what you already have). |

The sub-agent's reply is expected to be an **answer to exactly the `desired_output`**, plus evidence references and a short list of suggestions for anything the workspace should capture. If the answer comes back thin, re-dispatch with a sharper `desired_output` and richer `context` — the conversation's cost cap bounds how far that loop can run.

## Picking the agent

Pass exactly one of:

- `agent` — a roster agent: an Amdahl library slug (`researcher`), a workspace agent slug or id. Browse the roster via `GET /agents` (or the `agent://list` resource).
- `prompt` — an ephemeral inline agent: `{ "name": "...", "prompt": "..." }`. Never saved to the roster; the prompt is snapshotted onto the run record for auditability.

**Restricted rosters.** A run started with a fixed agent roster (a [Routine](../routines.md) whose `config.agents` lists several agents) may only delegate to those agents: any other `agent` ref — and any inline `prompt` — is refused with `agent_not_allowed`, and the error's `details.allowed` echoes the permitted set. Runs without a roster are unrestricted.

## Example

```bash
curl -X POST https://api.amdahl.com/api/platform/v1/agents/delegate \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "researcher",
    "task": "Investigate churn signals in our call corpus for Q3",
    "desired_output": "A ranked list of the top 5 churn risks, each with supporting quotes",
    "context": "We are assembling a QBR brief; renewals close next month"
  }'
```

Response:

```json
{
  "success": true,
  "run_id": "3f6f2f4e-...",
  "conversation_id": "9a1c8d7b-...",
  "agent": { "id": "d3b1...", "slug": "researcher", "name": "Researcher", "source": "library" },
  "status": "queued"
}
```

## Async by design

Delegation never blocks for the child's answer. The call returns the child `run_id` immediately; the sub-agent runs in the background. Two ways to observe it:

- **Poll** the run over REST until `status` reaches a terminal value (`complete`, `error`, or `canceled`; a run paused on a human question shows `awaiting_input`):

  ```bash
  curl https://api.amdahl.com/api/platform/v1/agent-runs/3f6f2f4e-... \
    -H "Authorization: Bearer $API_KEY"
  ```

  ```json
  {
    "id": "3f6f2f4e-...",
    "conversation_id": "9a1c8d7b-...",
    "status": "complete",
    "summary": "Top 5 churn risks, ranked, with supporting quotes ...",
    "tokens_used": { "input": 18324, "output": 2210 },
    "created_at": "2026-07-10T22:41:00Z",
    "completed_at": "2026-07-10T22:42:31Z"
  }
  ```

  The full row also carries the run's step events and conversation log for tracing. (Agents observing a delegation from inside the platform read the same row through `read_resource` as `agent_run://<id>` — the URI form is agent-side sugar over this REST endpoint.)

- **Stream**: when the caller is itself an agent run, the child's progress is teed onto the parent's SSE stream as `child_progress` frames, with a final `child_terminated` frame when the sub finishes. Console sessions render this as nested activity automatically.

There is no `session_id` input: the child is bound to the **calling run's conversation** automatically. A delegation fired from inside an agent session lands in that same conversation (so cost caps, history, and streaming all stay in one place); a delegation fired directly over REST gets its own conversation.

## Depth is one level

A sub-agent cannot delegate further. The sub toolkit structurally omits the delegation tool, and a delegation attempted by a run that is already a sub-agent is rejected with `delegation_depth_exceeded`. Decompose at the top: the dispatching agent fans out the investigations and folds the answers together itself.

## Errors

| Code                        | Meaning                                                             |
| --------------------------- | ------------------------------------------------------------------- |
| `invalid_argument`          | Missing `task`, or not exactly one of `agent` / `prompt`.           |
| `not_found`                 | The `agent` ref matched no library or workspace agent.              |
| `delegation_depth_exceeded` | The caller is itself a sub-agent (depth is 1).                      |
| `enqueue_failed`            | The child run could not be queued; nothing was left behind — retry. |
