Multi-agent patterns
Multi-agent coordination in teaspill is three primitives: spawn a child, send a message, observe what others are doing. Everything else on this page is a pattern built from those three.
The golden rule
Worth restating from the wake model, because every pattern below leans on it: spawn returns the child's id immediately — never its result. The child runs concurrently; its result arrives later as a child_finished message that wakes the parent. An agent that has delegated everything it can ends its turn, and the platform wakes it when there is news.
Fan-out / gather
The workhorse pattern: split a task across N children in one wake, gather results as they arrive, finish when all are in.
wake 1 spawn_agent × 3 → three child ids, immediately
wait() → turn ends
wakes 2–4 child_finished arrives → record the result in state,
(one wake per child) count 1/3 … 2/3 … 3/3
final wake all results present → finish(combined result)
The children run concurrently with each other and with anything else the parent does. Order of completion doesn't matter — each child_finished is its own wake, and the parent's state (a counter, a list of findings) carries the gather across wakes.
Pipeline
For staged work, chain instead of fanning out: each agent send_messages its output to the next stage and finishes. A researcher hands findings to a summarizer, which hands a draft to a reviewer. No stage waits on another — each sleeps until its input arrives, and the pipeline's state is the messages in flight, all durable.
The alternative shape — a parent orchestrating each stage in sequence, spawning stage N+1 when stage N's child_finished arrives — trades a little latency for one place that sees the whole flow. Both are idiomatic; prefer the orchestrator when you want one timeline that tells the whole story.
Deterministic spawn
A parent that might retry — or that must never double-create a worker — supplies the child's id itself, derived from the task:
spawn_agent({ type: "summarizer", args: { text }, id: `summary-of-${articleId}` })
Spawning the same (type, id) twice reattaches to the same child instead of creating a duplicate (how reattach behaves). This makes "one worker per subtask" a naming convention instead of bookkeeping code.
Watching another agent
Observation doesn't need messages. Any agent's catalog row — type, status, parent, tags — is subscribable, and its timeline is readable, by UIs and backends alike. A dashboard watching a fleet of workers is a catalog subscription; an audit view of one worker is its timeline. Agents themselves get list_children() for a snapshot of their own children's status.
What arrives on a parent's wake
When a child finishes, the parent's next wake carries a child_finished message with the child's id, its outcome (success, error, interrupted, or archived), and whatever result the child passed to finish. Messages from non-child agents arrive as ordinary messages tagged with the sender's entity URL. Either way it arrives as an ordinary wake — the parent's harness sees the news as input and acts on it.
- Don't wait synchronously for a child. There is nothing to wait on — spawn, end the turn, and the result wakes you.
- Don't poll. An agent that re-checks
list_childrenin a loop is burning turns to learn what achild_finishedwake would have told it for free. - Don't use timelines as a message bus. Timelines are history — one agent reading another's timeline to decide what to do next recreates the coupling teaspill removes. Coordination is messages; timelines are for reading the story afterward.
Next steps
Patterns cover agents talking to each other — including steering a busy peer mid-run. For intervening from the outside — interrupting, pausing, archiving — continue to Lifecycle & control, which also covers where steer fits.