Skip to main content

Run a multi-agent Session

This guide creates two worker Agents, places them in a coordinator roster, and observes the persistent child Threads that the coordinator starts while handling a Session.

Multi-agent execution uses the ordinary Session and Event APIs. Delegation is not a client-side endpoint: a coordinator receives the private list_agents and send_to_agent model tools and decides when to call them.

Prerequisites

  • Complete Getting started.
  • Use a Messages-compatible model that can call tools. The deterministic local model is useful for platform smoke tests but does not make open-ended delegation decisions.
  • Keep every roster Agent on the same inference_geo value, or leave it unset everywhere.

The examples use the local API at http://localhost:8080.

Create the worker Agents

RESEARCHER_ID=$(
curl -sS http://localhost:8080/v1/agents \
-H 'content-type: application/json' \
-d '{
"name": "researcher",
"model": "claude-model-id",
"system": "Investigate the question and return a concise evidence-backed report."
}' | jq -r .id
)

REVIEWER_ID=$(
curl -sS http://localhost:8080/v1/agents \
-H 'content-type: application/json' \
-d '{
"name": "reviewer",
"model": "claude-model-id",
"system": "Review the proposed answer, identify errors, and return corrections."
}' | jq -r .id
)

Agent names identify callable roster members inside one Session and therefore must be distinct.

Create the coordinator

COORDINATOR_ID=$(
curl -sS http://localhost:8080/v1/agents \
-H 'content-type: application/json' \
-d "{
\"name\": \"coordinator\",
\"model\": \"claude-model-id\",
\"system\": \"Delegate independent work in parallel. When review depends on a research result, send the completed report to the reviewer as a follow-up task, then synthesize the final answer.\",
\"multiagent\": {
\"type\": \"coordinator\",
\"agents\": [\"$RESEARCHER_ID\", \"$REVIEWER_ID\"]
}
}" | jq -r .id
)

Mango resolves every roster entry to an immutable Agent Version when the coordinator Version is written. Session creation then expands those pins into complete Session-owned Agent snapshots, so later Agent updates cannot drift a running Session.

Start and prompt the Session

Create a cloud Environment and Session as in the quick start, using COORDINATOR_ID as the Session Agent. Then send a normal user.message:

curl -sS "http://localhost:8080/v1/sessions/$SESSION_ID/events" \
-H 'content-type: application/json' \
-d '{
"events": [{
"type": "user.message",
"content": [{"type":"text","text":"Compare two approaches, have the reviewer challenge the result, and give me one recommendation."}]
}]
}' | jq

The coordinator may create child Threads asynchronously. Each child owns its Agent snapshot, event ledger, provider transcript, usage, retry state, live preview stream, and Temporal Workflow while sharing the Session sandbox and attached resources.

send_to_agent does not block the coordinator turn. Independent work can run in parallel, but dependencies remain the coordinator's responsibility: one child does not receive another child's future report automatically. Wait for the prerequisite report to arrive on the primary Thread, then send the dependent child a self-contained task. Follow-ups can name the existing child Thread so it continues with its prior context.

Observe the Threads

List the primary and child Threads:

curl -sS "http://localhost:8080/v1/sessions/$SESSION_ID/threads" | jq

Read one child ledger independently:

curl -sS \
"http://localhost:8080/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" |
jq

The primary Session stream contains condensed lifecycle and directional message projections. A completed child answer arrives as a report and wakes a later coordinator synthesis turn; it is not duplicated as a primary agent.message. The runtime preserves from_agent_name and from_session_thread_id when it presents that report to the coordinator model, so the internal Agent message cannot be mistaken for a new user message.

Answer a child action

If a child needs confirmation or a custom/self-hosted tool result, Mango projects an answerable event onto the primary Session stream. Reply to that event ID through the ordinary Session Events endpoint. An optional session_thread_id may be echoed as a routing check; a conflicting value is rejected.

An interrupt without session_thread_id targets the primary and every active child. Include a child ID to interrupt only that Thread.

Current limits

  • Ordinary roster Agents are supported; the distinct upstream advisor consultation lifecycle is not.
  • Child transcripts are durable and independently compacted. Compacted message projections are stored as immutable internal snapshots, and compaction is observable through agent.thread_context_compacted on the owning Thread.
  • Shared Session list-cost budgets are not enforced across concurrent Threads.
  • Exact hosted preview suppression for report-only coordinator turns remains a compatibility boundary.

See Session Threads for the HTTP contract and API compatibility for the current support boundary.