How to Configure Handoff Routing Between Agents Using orchestrate.py Events
The orchestrate.py script in the anthropics/financial-services repository provides a reference event loop that extracts JSON handoff requests from a source Claude Managed Agent's text stream, validates them against an allow-list and schema, and routes them to target agents using the Anthropic SDK's sessions.steer method.
The orchestrate.py module implements a lightweight, event-driven routing system for Claude Managed Agents (CMAs) that requires no external orchestration services. By parsing structured JSON payloads embedded in the source agent's output, you can configure handoff routing between agents using environment variables and hard-coded allow-lists. This guide walks through the complete configuration process based on the reference implementation in the repository.
How Handoff Routing Works in orchestrate.py
The routing logic follows a six-step validation pipeline defined in /scripts/orchestrate.py. Understanding this flow is essential before configuring your routing environment.
Step 1: Load Environment Configuration
The script initializes by reading SOURCE_SESSION_ID and AGENT_IDS from environment variables. The AGENT_IDS variable must contain a JSON map where keys are agent slugs and values are deployed CMA agent IDs.
# From /scripts/orchestrate.py lines 85-89
run(
os.environ["SOURCE_SESSION_ID"],
json.loads(os.environ.get("AGENT_IDS", "{}"))
)
Step 2: Stream Output Events
The orchestrator opens a streaming session on the source agent using the Anthropic SDK. It filters for message_delta events that contain a text field.
# From /scripts/orchestrate.py lines 68-71
for event in stream:
if event.type != "message_delta":
continue
text = event.delta.text
Step 3: Extract Handoff Requests
A regular expression HANDOFF_RE scans the text for JSON blobs where "type" equals "handoff_request".
# From /scripts/orchestrate.py lines 40-42
HANDOFF_RE = re.compile(r'\{"type":\s*"handoff_request".*?\}', re.DOTALL)
Step 4: Validate Against Allow-Lists and Schema
Extracted payloads must pass two checks: the target_agent must exist in ALLOWED_TARGETS, and the payload must validate against HANDOFF_PAYLOAD_SCHEMA.
# From /scripts/orchestrate.py lines 55-60
if target not in ALLOWED_TARGETS:
return False
jsonschema.validate(instance=payload, schema=HANDOFF_PAYLOAD_SCHEMA)
Step 5: Resolve Target Agent IDs
The script maps the validated slug to a deployed agent ID using the AGENT_IDS dictionary.
# From /scripts/orchestrate.py lines 75-78
target_id = agent_ids.get(target_slug)
if not target_id:
return False
Step 6: Forward the Event
Finally, the orchestrator calls client.beta.agents.sessions.steer to inject the event into the target agent's session.
# From /scripts/orchestrate.py lines 79-82
client.beta.agents.sessions.steer(
agent_id=target_id,
input=handoff["payload"]["event"]
)
Configuring the Routing Environment
To enable handoff routing, you must configure three components: the allowed targets list, the agent ID mapping, and the source session identifier.
Define Allowed Targets in ALLOWED_TARGETS
Edit lines 23-27 in /scripts/orchestrate.py to specify which agent slugs can receive handoffs:
# Lines 23-27 in /scripts/orchestrate.py
ALLOWED_TARGETS = {
"pitch-agent",
"market-researcher",
"valuation-reviewer"
}
Publish the Agent ID Mapping
Set the AGENT_IDS environment variable to a JSON object mapping slugs to deployed CMA agent IDs:
export AGENT_IDS='{
"pitch-agent": "agent-01abcdef",
"market-researcher": "agent-02ghijkl",
"valuation-reviewer": "agent-03mnopqr"
}'
These IDs are returned when you deploy agents through the CMA deployment process.
Select the Source Session
Provide the active session ID of the agent that will emit handoff requests:
export SOURCE_SESSION_ID="session-xyz123"
(Optional) Extend the Payload Schema
If your workflow requires additional context fields, modify HANDOFF_PAYLOAD_SCHEMA (lines 29-38) to include new properties:
# Lines 29-38 in /scripts/orchestrate.py
HANDOFF_PAYLOAD_SCHEMA = {
"type": "object",
"required": ["event"],
"properties": {
"event": {"type": "string"},
"context_ref": {"type": "string"},
"priority": {"type": "integer"} # Added field
}
}
Practical Code Examples
Sample Handoff Request Format
When the source agent outputs this JSON anywhere in its text stream, orchestrate.py processes the routing:
{
"type": "handoff_request",
"target_agent": "valuation-reviewer",
"payload": {
"event": "Please evaluate the DCF model for ticker AAPL using the latest earnings guidance.",
"context_ref": "doc:financials/2024/Q2"
}
}
The script validates that "valuation-reviewer" appears in ALLOWED_TARGETS, verifies the payload structure, then steers the corresponding agent with the event string.
Running the Orchestrator Locally
Execute the script after setting the required environment variables:
# Set required environment variables
export SOURCE_SESSION_ID="session-xyz123"
export AGENT_IDS='{
"pitch-agent": "agent-01abcdef",
"valuation-reviewer": "agent-03mnopqr"
}'
# Run the orchestrator
python -m scripts.orchestrate
The process runs silently on successful routing; parsing and validation failures are ignored and the loop continues processing new events.
Adding a New Target Agent
First, extend the allow-list in the script:
# In /scripts/orchestrate.py, lines 23-27
ALLOWED_TARGETS = {
"pitch-agent", "market-researcher", "valuation-reviewer",
"risk-analyzer"
}
Then update the environment mapping:
export AGENT_IDS='{
"risk-analyzer": "agent-04stuvwx"
}'
Now handoff requests specifying "target_agent": "risk-analyzer" will route to the new agent.
Key Files in the Reference Implementation
Understanding these files helps when customizing handoff routing:
/scripts/orchestrate.py— Core event loop handling extraction, validation, and steering logic./scripts/check.py— Validates manifests and skill synchronization; useful for verifying that agents referenced inAGENT_IDSexist in the repository./plugins/agent-plugins/*/.claude-plugin/plugin.json— Defines each agent's slug and deployment metadata used inALLOWED_TARGETS./scripts/sync-agent-skills.py— Propagates skill changes to agent bundles, ensuring target agents remain compatible for handoff reception.
Summary
- Handoff routing in
orchestrate.pyrelies on regex extraction of JSON payloads from a source agent's text stream. - Configure allowed targets by modifying the
ALLOWED_TARGETSset (lines 23-27) in/scripts/orchestrate.py. - Map agent slugs to deployed IDs using the
AGENT_IDSenvironment variable JSON object. - Validate payloads against
HANDOFF_PAYLOAD_SCHEMA(lines 29-38) to ensure required fields like"event"are present. - The orchestrator uses
client.beta.agents.sessions.steerto forward events to resolved target agents. - No external orchestration service is required for the prototype implementation.
Frequently Asked Questions
What happens if a handoff request specifies a target not in ALLOWED_TARGETS?
The orchestrator silently drops the request. According to lines 55-60 in /scripts/orchestrate.py, if the target_agent value is not found in the ALLOWED_TARGETS set, the function returns False before attempting validation or routing. This security measure prevents unauthorized agent steering.
Can I modify the handoff payload schema to include custom fields?
Yes. Edit HANDOFF_PAYLOAD_SCHEMA defined at lines 29-38 in /scripts/orchestrate.py to add new properties. For example, you could add a "priority" integer or "metadata" object. Ensure that source agents emitting handoffs include these fields in their JSON payloads to pass validation.
How does the orchestrator handle invalid JSON in the text stream?
Invalid JSON or regex non-matches are silently ignored. The HANDOFF_RE pattern at lines 40-42 extracts candidate JSON blobs, but if extraction or subsequent json.loads() parsing fails, the event loop continues without interruption. Only successfully parsed and validated payloads proceed to the steering phase.
Where do I find the agent_id values for the AGENT_IDS mapping?
Agent IDs are returned by the Claude Managed Agent (CMA) deployment process. Check the deployment output logs or use /scripts/check.py to lint manifests and verify agent configurations. The slugs used as keys should match those defined in the respective agent's plugin.json files under /plugins/agent-plugins/.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →