Handoffs
A handoff is the moment one agent stops working and another agent continues from exactly the same point. Agent Relay makes this work by writing a structured resume packet at the moment of the handoff — a single JSON file the next agent reads as its starting context.
The packet captures what was decided, what was tried, what got stuck, and what's currently true about the working tree. It is the entirety of what the next agent needs to keep moving without re-reading the whole session log.
When a handoff fires
Three triggers produce a handoff:
- Rate limit reached. Agent Relay detects the provider's rate-limit
response, finalizes the current turn, and writes
resume.json. - Manual stop. You can
Ctrl-Can activeagent-relay run. The handoff is written before the process exits. - Explicit cut. Run
agent-relay handofffrom another shell to force a checkpoint at the next turn boundary.
In every case the handoff is atomic — partial writes never happen.
Either you have a complete resume.json, or you don't.
Where the packet lives
Inside the session directory:
.agent-relay/sessions/<session-id>/resume.jsonYou can read it, diff it, or check it into git for a permanent audit trail.
Resume-packet anatomy
A real resume.json looks like this:
{
"session_id": "01J9X3M7K5VBHQEN6T2F4D8RPZ",
"previous_agent": "claude-code",
"checkpoint_reason": "rate_limit",
"turn_count": 14,
"decisions": [
{
"id": "d1",
"summary": "Switched test runner from unittest to pytest",
"why": "unittest's async fixtures fought the new auth middleware"
}
],
"blockers": [
{
"id": "b1",
"summary": "tests/auth/test_session.py::test_refresh_flow is still red",
"evidence": "pytest exit code 1, traceback at turns/012-stderr.log"
}
],
"touched_files": [
{ "path": "src/auth/session.py", "status": "modified" },
{ "path": "tests/auth/test_session.py", "status": "modified" }
],
"validation_state": {
"tests": "red",
"lint": "clean",
"typecheck": "clean"
},
"next_task": "Make test_refresh_flow pass without regressing test_basic_login"
}Every field is a stable contract — the schema is versioned in
agent-relay-tool and changes are additive.
How the next agent consumes it
When you launch the second agent, Agent Relay reads the packet, formats it into a concise system-prompt prelude, and starts the new run. The next agent sees:
- The previous agent's name and why the handoff happened.
- A bulleted list of decisions already made (so it doesn't relitigate them).
- Current blockers with pointers to evidence on disk.
- A list of touched files and their state (modified / created / deleted).
- The current validation snapshot — tests/lint/typecheck status.
- A specific
next_taskdescription.
The new agent's first turn is reading this packet. Its second turn is acting on it.
Chained handoffs
A handoff doesn't have to stop at two agents. Each new session can itself
produce a resume.json, which the next agent reads, and so on. Agent
Relay treats this as a normal pattern:
session A (claude-code, rate-limited)
↓ resume.json
session B (codex, completed first half)
↓ resume.json
session C (claude-code, finished)
Use agent-relay metrics to roll up token cost and wall time across the
whole chain — see Watch & Metrics.
Inspecting a handoff
Before launching the next agent, you can read or edit the packet:
cat .agent-relay/sessions/<id>/resume.json | jqIf you want to remove a decision the previous agent got wrong, delete the entry and save. The next agent will not see it.