Subagents
Let an agent delegate a task to another agent that runs in its own thread and reports back.
A subagent is an ordinary agent that another agent can start with a task. It works in its own thread, with its own log, and its final answer comes back to the parent as a tool result. Use subagents to keep a focused worker's context clean, to run work in parallel, or to give a narrow job a cheaper model.
Add a subagent
List the agents a parent may start in subagents. The parent's model gets a spawn_agent tool and sees the names in its system prompt.
const researcher = agent({
name: "researcher",
instructions: "Research the question and answer in two sentences.",
model: scriptedModel({ responses: [use("search_docs", { query: "refunds" }, "r1"), say("Refunds take 5 days.")] }),
tools: [searchDocs],
budget: { max_model_requests: 10 },
});
const lead = agent({
name: "lead",
instructions: "Delegate research to the researcher, then answer.",
model: scriptedModel({
responses: [
use("spawn_agent", { agent: "researcher", prompt: "How long do refunds take?" }, "s1"),
say("Refunds take about 5 days."),
],
}),
tools: [searchDocs], // a subagent only gets tools its parent also has
subagents: [researcher],
});
const store = sqlite(":memory:");
const result = await lead.run("How long do refunds take?", { store });The examples use the scripted model so they run offline; use and say are small helpers that build a scripted tool call and a scripted reply. With a real model, the model decides when to call spawn_agent.
What the model calls
spawn_agent takes:
agentstringrequiredOne of the names in subagents. Any other name fails without running anything.
promptstringrequiredThe task. The subagent sees only this, not the parent's conversation.
backgroundbooleandefault falsefalse waits for the subagent and returns its final answer as the call's result. true returns at once, so the parent keeps working while the subagent runs; the answer arrives later in the parent's conversation. See Background subagents.
A subagent with structured output hands the parent its value as canonical JSON text.
Background subagents
A background subagent's answer can arrive after the parent has already replied. When it does, the parent wakes up and takes another turn to react to it, so it never sits on a result until the next message.
run()waits for them. A run that starts background subagents returns only once each of them has reported and the parent has reacted. Itsoutputis the parent's last answer, given after the last result came in. Earlier answers stay in the thread's timeline.- It costs more turns. Every wake is a model turn, counted against the parent's budgets and turn limit.
- The status can change after an answer. If a wake turn fails or runs out of budget, the run returns
failedorbudget_exhausted, even though an earlier turn had answered. - A run that ends another way stops its subagents. If a turn fails, runs out of budget or hands off, no wake turn runs. Each subagent still running gets a cancel (and so does every subagent below it), finishes the step it is on, and its result is recorded before
run()returns, so the next run of the thread starts clean. Cancelling the thread ends that wait too. - A result that arrives mid-turn is just part of that turn: the parent sees it before its next model call, and no extra turn runs.
run()waits for background subagents with no time limit; cancel the thread to stop waiting. A cancel ends the runcancelledat once, even while a subagent is stuck, and reaches every subagent below it, including one that is itself only waiting on its own background subagents; none of them wakes again. Cancel from the process that runs the thread (or through its host): a cancel from another process waits until that run lets go of the thread. If you start the next run right away, it picks up any subagent still finishing its step and records its result, rather than failing. In Python,run_syncruns each call in its own event loop, so this doesn't carry across tworun_synccalls: the next call resumes the interrupted step from the thread's log instead, and that can repeat one model call.- After a handoff the parent never wakes. Its run returns
handed_offonce the subagents it left running have stopped and their results are recorded. - Each wake belongs to the run that started the subagent. Results from two different runs never share a wake turn, and a wake acts for the person whose run started the subagent.
- Under a host, if the process dies while a background subagent runs, the next host start finishes that subagent and wakes the parent. A channel conversation then gets the wake turn's answer as a further reply.
What a subagent may do
A subagent can only narrow what its parent may do, never widen it.
- Tools. It gets its own tools filtered to the names its parent also has. Give the parent every tool its subagents need.
- Permissions. Each call is decided under the subagent's own permissions and again under its parent's. The stricter answer wins. See Permissions & approvals.
- Budgets. A parent's budget covers every subagent it starts, and their subagents. A subagent can also have its own, tighter
budget; when it runs out, the subagent stops and the parent carries on. See Budgets. - One at a time per name. While a subagent is still running, starting another with the same name is refused. Start it again once it has finished.
Sandboxes differ by language today. In TypeScript a subagent runs without a sandbox, so it has no shell or file tools. In Python a subagent configured with the same sandbox provider as its parent shares the parent's sandbox; a subagent whose parent has no sandbox can't have one.
See what ran
Every subagent has its own thread, so you can inspect, fork or test it like any other. children() lists the subagents a thread started and how each ended.
for (const child of await result.thread.children()) {
console.log(child.child_thread_id, child.status); // ... completed
}A child's status is running, completed, failed, cancelled or budget_exhausted.
Crashes, approvals and cancellation
- Crash-safe. The subagent's thread is recorded before it starts. If the process dies, the next run of the parent resumes the same subagent instead of starting a second one.
- Approvals bubble up. If a subagent stops to wait for an approval, the parent's run stops too and returns
parked, with a pending entry of kindchildwhose id is the subagent's thread id. Open that thread, approve there, then continue the parent. See Human-in-the-loop. - Cancel the tree.
cancelon a thread also stops every unfinished subagent under it.
Hooks
Two hooks cover subagents. subagentStart (subagent_start in Python) can allow or deny a spawn. subagentStop (subagent_stop) sees the finished result and can send the subagent back to work with a reason. See Hooks.
The task board
The lead and every subagent it starts share one task list and one mailbox, so work can be split up, claimed and reported on without the lead relaying every detail. The tools come with subagents; there is nothing else to configure.
| Tool | Who has it | What it does |
|---|---|---|
team_task_create | lead and subagents | Adds a task (subject, optional description, optional blocked_by task ids). Returns the new task id. |
team_task_claim | lead and subagents | Claims an open task whose blockers are all completed. Only one subagent can win a claim. |
team_task_update | lead and subagents | Marks a task you claimed completed or failed, or released to put it back in the pool. |
send_message | lead and subagents | Sends text to a subagent by agent name, or to * for everyone. A name that isn't the lead or one of its subagents is refused, and the error lists the names that are. |
A subagent is identified by its agent name. A message arrives as reference material just before the recipient's next model call, prefixed with the sender's name, and is delivered once. Tasks, claims and messages are recorded in the lead's log; claims are atomic, blockers are respected, and a call repeated after a restart gets the same answer, so nothing is claimed or sent twice.
This board is separate from Teams (agent({team})), whose members have their own threads and mail.