"Multi-agent orchestration" sounds like a framework. A message bus, a scheduler, a supervisor tree, a state machine, probably a YAML file and a diagram with swimlanes.
Here is the entire mechanism.
Part 1 ยท One extra tool
The model gets one extra tool. Its implementation is runAgentLoop โ the
function that is calling it.
async run(args, context) {
for await (const event of runAgentLoop({
messages: [{ role: "user", content: briefing(args.task) }],
toolbox: context.toolbox, // same kitchen
gate: true, // same handbrake
delegate: false, // โ cooks don't hire cooks
budget: context.budget, // same wallet
})) {
context.emit(event); // forward its trace upward
if (event.type === "done") return event.finalText; // its report
}
}That is it. Eight lines of substance.
Why it is this small
Look at the signature of the loop. It takes messages in, and yields events out.
A sub-agent is a thing you give a task to, which then gets on with it and reports back.
Those are the same sentence. There was never anything to build here โ only something to notice.
Read the five arguments again, because four of them are the design:
| Argument | Why |
|---|---|
messages | a fresh conversation โ this is the whole point, see the ceiling |
toolbox | the same servers. A worker is not sandboxed differently. |
gate: true | the same approval rules. A worker cannot do what the boss could not. |
delegate: false | cooks don't hire cooks โ this is the recursion seatbelt, and it works by absence |
budget | the same wallet, by reference, so MAX_TREE_TOKENS stays true |
Part 2 ยท What the orchestrator actually receives
This is the part that makes the whole thing compose.
A worker's entire forty-second investigation of eighty cookie jars โ twenty
tool calls, three iterations, thousands of tokens of inspection reports โ
arrives in the orchestrator's conversation as one paragraph of text, in an
ordinary tool_result block.
{
"type": "tool_result",
"tool_use_id": "toolu_01ABCโฆ",
"content": "Inspected jars 1-80. Found 12 tampered: 4, 11, 19, 23, 31, 38, 44, 52, 57, 63, 71, 78. Seal broken on 4 and 38; the rest are weight anomalies with no AUTHORIZED note."
}The orchestrator reads it exactly like it reads a dice roll. It has no idea an agent produced it.
Part 3 ยท The two sentences in the briefing that do the work
The briefing handed to a worker is short. Two clauses in it are load-bearing, and both are about context economics rather than correctness.
"Reply with a short report, not a transcript."
A worker that returns its whole trace hands the context problem straight back to the orchestrator โ which is the exact thing delegation was supposed to fix.
Without this sentence you have paid for three extra agents, three extra system prompts, three extra tool lists, and moved the ceiling nowhere.
"If you run out of room, report what you DID establish."
Turns a partial failure into partial data.
A worker that hits its iteration cap and says nothing is a hole in the answer. One that says "I inspected jars 1 through 54 and found three tampered; 55โ80 were not reached" is a result the orchestrator can act on โ and a human can see the gap.
โ ๐ท hired #1 "jars 1-20" โ ๐ท hired #2 "jars 21-40" โ ๐ท hired #3 "jars 41-60" โ โ #1 "jars 1-20" โ 3 iters, 14,654 tokens, end_turn
Note that last line: each worker reports its own iteration count and token bill, because each worker is a real run. That is what makes the comparison in the measurement trap possible at all.
Part 4 ยท The honest question
Sub-agents are impressive to watch. Three lanes light up, tool calls scroll past, reports come back, and the whole thing feels like an upgrade.
Feeling like an upgrade is not being one.
Count what delegation costs, every single run:
| Cost | Paid |
|---|---|
| each worker re-reads the system prompt | ร 3 |
| each worker re-reads the tool definitions | ร 3 |
| the orchestrator's own loop | ร 1 |
| a summarising trip home | ร 1 |
The cost of delegation is real, immediate, and paid on every single run. The benefit is conditional on the job being big enough.
So measure it
scripts/08-compare.ts runs the same eval cases twice โ once with
spawn_agent withheld, once with it offered. Same model, same prompt, same
toolbox, same gate, same seatbelts. One flag differs.
60 jars โ the demo size
one agent: 100% ยท ~99k tokens
a crew: 100% ยท ~83k tokens
A wash. Same answer, and the cost difference is inside run-to-run variance.
240 jars โ past the ceiling
one agent: 0 of 36 ยท 439k tokens
a crew: 36 of 36 ยท 248k tokens
Correct and roughly half the price.
Part 5 ยท Which raises the thing that cannot live on a server
Look again at what spawn_agent needs: context.toolbox, context.budget,
context.emit.
None of that survives a trip over HTTP. The budget in particular is a shared mutable object three other agents are drawing from concurrently โ serialise it and you have a copy, and copies diverge.
So the loop has to learn about a kind of tool it has never had before, and that turns out to be a security boundary as well as a plumbing detail.
What you now know
- A sub-agent is one extra tool whose implementation is the loop that is calling it.
- The orchestrator receives its report as ordinary text in a
tool_resultโ which is why the gate, the trace, the budget and replay all work on it for free. - Two English sentences in the briefing carry the architecture: report, don't transcribe, and report partial findings.
- Delegation costs are paid every run; benefits are conditional on size. Measure your own crossover.
spawn_agentcannot be a server tool, because shared mutable state cannot cross a network boundary.