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 structured resume packets at the moment of the handoff. The target agent receives a packet formatted for its CLI so it can start with the current Relay context instead of a blank prompt.

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. In the interactive REPL, /use <agent> and daemon-triggered automatic handoff both use this same machinery.

When a handoff fires

These triggers produce a handoff:

  1. Manual switch in the REPL. /use codex, /use claude, /use gemini, or /use opencode routes the switch through a checkpoint when there is live context.
  2. Rate limit reached. Relay detects known provider rate-limit strings in live output, a configured hook, the PTY wrapper, or the opt-in proxy.
  3. Explicit handoff command. Run /relay, /handoff, or agent-relay <agent> to prepare a target-specific resume packet.
  4. Command-mode boundaries. run, chat, and race write checkpoints and artifacts as part of their managed workflows.

In every case the handoff write is atomic — partial resume packets are not left behind.

Where the packet lives

Inside the session directory:

terminal
.agent-relay/sessions/<session-id>/resume/

The exact filename depends on the target agent. You can read or diff these files before launch; treat them as sensitive session artifacts.

Resume-packet anatomy

A resume packet contains this kind of information:

json
{
  "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"
}

The on-disk implementation is versioned in agent-relay-tool; fields are added conservatively so older session data remains readable.

How the next agent consumes it

When Relay launches the second agent, it formats the packet into the target agent's expected startup context. 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_task description.

The new agent starts from this packet, then continues the same Relay session lineage.

Chained handoffs

A handoff doesn't have to stop at two agents. Each target can receive a fresh resume packet, and Relay treats chained handoffs as a normal pattern:

Rendering…

Use /metrics in the interactive shell, or agent-relay metrics from another terminal, 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:

terminal
ls .agent-relay/sessions/<id>/resume/
cat .agent-relay/sessions/<id>/resume/<target>.md

If you want to remove a decision the previous agent got wrong, delete the entry and save before launching the target. The next agent will not see it.

Next