Handoffs, replies, and deliberation
ContextRelay connects Claude Code and Codex so they can work as one team. But the two agents are separate processes, and neither can see the other's hidden reasoning - they only ever share what gets written into a message or the shared ledger. So how they talk matters.
There are three communication modes, each backed by a small set of MCP tools (and, on the Claude side, slash commands that wrap them):
| Mode | What it is | Reach for it when... |
|---|---|---|
| Reply | A live message delivered to the peer as a new turn, recorded in the ledger. | You are mid-task and want ongoing back-and-forth. |
| Handoff | A structured transfer: a reason, a concrete next task, and the context to do it. | You are passing ownership or delegating a discrete piece of work. |
| Deliberate | One bounded debate-and-converge pass, returning the peer's reply for you to synthesize. | You face a decision or tradeoff and want a second opinion before acting. |
The rest of this page explains each mode, the WHY behind it, and the exact tools and commands.
Claude and Codex run different products, so their tools are named from each agent's point of view. Claude calls reply and handoff; Codex calls send_to_claude and handoff_to_claude. The concepts are the same; only the labels flip. This page shows both sides.
Reply: live, ongoing conversation
A reply sends a message to the peer that lands as a new turn in their session and is appended to the shared ledger. This is the everyday channel for collaboration within a shared task: answering a question, sharing a finding, confirming a plan, nudging the other agent forward.
- Claude → Codex: the
replytool. Passtext; optionally passchat_id(from the inbound message tag) andrequire_reply: truewhen you genuinely need an answer back. You can also sethandles_handoff_idto mark that this reply resolves a specific handoff. - Codex → Claude: the
send_to_claudetool. Passtext; optionallyhandles_handoff_id.
Replies do not transfer ownership and do not, by themselves, define a next task. Use them for dialogue; use a handoff when you want the peer to own the next step.
Because agents cannot read each other's internal thoughts, a useful reply states the goal, what changed, the files touched, any blocker, and the next step. A reply that says only "done" forces the peer to go re-derive everything from the ledger.
How messages actually reach Claude
ContextRelay delivers Codex's messages to Claude in one of two modes, resolved automatically:
- Push - when Claude Code advertises the
claude/channelcapability (which it does when you launch viactxrelay claude), the daemon pushes Codex's messages straight into Claude's session as<channel source="contextrelay" ...>tags. - Pull - otherwise, or if a push fails or lags, ContextRelay falls back to a queue that Claude drains explicitly with
get_messages, or waits on withwait_for_messages.
The mode is detected from the connected client's capabilities and can flip between push and pull during a session as conditions change. You normally don't manage this; it's worth knowing only so the pull-mode tools make sense:
get_messages- drain any queued Codex messages right now.wait_for_messages- long-poll for the next message (thetimeout_sis clamped to 1–60 seconds, default 10), then return the same payload asget_messages. Use it afterreply(require_reply=true).
On the Codex side, the mirror of "wait for the other agent" is wait_for_claude, which waits for a Claude reply in the ledger and can be scoped to a specific handoff id so Codex only returns when its handoff is answered - far better than a shell sleep/poll loop.
Handoff: a structured transfer of ownership
A reply continues a conversation; a handoff changes who is driving. It records a structured transfer in the ledger and delivers it live to the peer, who is now expected to take the next turn.
A good handoff always carries four things - and the tools encode them:
reason- why control is passing.ask- the concrete next task, stated explicitly and actionably.context_refs- the files, ledger entries, or diff refs the peer should inspect (pass[]if none are obvious yet).- Who speaks next - implicit in a handoff: the recipient owns the next turn.
- Claude → Codex: the
handofftool (reason,ask,context_refs).textis accepted only as a compatibility alias forask- preferask. - Codex → Claude: the
handoff_to_claudetool (reason,ask,context_refs). Codex can additionally setwait_for_reply: trueto block until Claude answers this handoff (withtimeout_seconds, default 90, max 300) - handy for validation requests where Codex needs the answer before continuing.
The slash-command shortcut
In Claude, you don't have to hand-build the tool call. The slash command wraps it:
/contextrelay:handoff <the concrete task for Codex>
This sets a Slash command handoff reason, rewrites your task into an explicit ask, attaches obvious context_refs, and tells you afterward that Codex has the next turn. (The command deliberately does not edit files - it only delegates.)
Reach for a handoff when you are blocked, when the peer is better suited to the work, or when you would otherwise stop to ask the human something the peer can answer first. For a worked example, see the delegate-to-Codex tutorial.
The structured fields drive the tooling, but also say, in your own message, why you're handing off and what "done" looks like. The clearer the ask, the less the peer has to guess - and the less it routes the critical work back to you.
Deliberate: one bounded second opinion
Sometimes you don't want to hand off the work - you want a second mind on a decision: a design choice, a tradeoff, a risk call, a "which approach?" question. That's deliberation: a single, bounded debate-and-converge pass with the peer, after which you synthesize the outcome.
- Claude → Codex: the
deliberate_with_codextool. - Codex → Claude: the
deliberate_with_claudetool.
Both take a question, your opening_position (your concise independent view, strongest reason, and remaining uncertainty before hearing the peer), optional context_refs, and a bounded wait. The point of stating an opening position first is to get genuine independent judgment rather than passive agreement.
After the peer replies, summarize the result in four parts:
- Current consensus - what you now agree on.
- Remaining disagreement - what's still open.
- Decision / next action - what you're going to do.
Then record that synthesis as a durable note so the decision survives the conversation.
The slash-command shortcut
/contextrelay:deliberate <the decision, question, or tradeoff>
This prompts Claude to write an opening position (My independent view is: / Strongest reason: / Assumptions / uncertainty:), call deliberate_with_codex, synthesize Codex's reply into consensus / disagreement / decision, and append_note the outcome. It is intentionally one Codex response and one Claude synthesis - deliberation is a convergence step, not an open loop.
Deliberation is bounded by design. If the peer answers, summarize what changed, decide the next step, and move on. If you're still stuck after a round, that's usually the signal to escalate to the human - not to keep volleying.
Live calls are bounded - long reviews go async
This is the single most important operational caveat on this page.
Live deliberation and wait calls are time-limited at the bridge layer. The live-wait window defaults to 90 seconds, and the per-call timeout_s on wait_for_messages and deliberate_with_codex is clamped to a maximum of 60 seconds. A genuinely long task - a thorough risk review, a big-diff read - will not fit inside one live call.
For anything that will take the peer more than a minute or two, do not block on a single long live deliberation. Instead:
- Send the request as a plain
reply(or ahandoff), with the context in the message and the ledger. - Let the live wait expire - the timeout policy can keep reconciling the handoff in the background (e.g.
after_live_wait: "poll_background"). - Pick up the peer's answer later with
read_context, orwait_for_messages/wait_for_claudescoped to the handoff id.
The work still happens and the result is durable in the ledger; you just don't hold a live socket open waiting for it. The risk-review tutorial walks through this pattern end to end.
Choosing the right mode
A quick decision guide:
- Just talking it through? →
reply/send_to_claude. - Handing over a concrete next task or ownership? →
handoff/handoff_to_claude(or/contextrelay:handoff). - Need a second opinion on a decision before you act? →
deliberate_with_codex/deliberate_with_claude(or/contextrelay:deliberate), then synthesize. - Expecting the work to take a while? → reply or handoff, then pick up the result from the ledger - don't block on a long live call.
All three modes write to the same shared ledger, so whichever you choose, the message, handoff, or decision becomes part of the auditable record of the session.
contextrelay, ctxrelay, and context-relay are interchangeable names for the same CLI (package @proofofwork-agency/contextrelay). Slash commands are available to Claude once the ContextRelay plugin is installed; Codex's MCP tools are available after ctxrelay codex-mcp install or when launched via ctxrelay codex.
Next steps
- Tutorial: delegate an implementation to Codex via handoff - a handoff, end to end.
- Tutorial: run a risk-review before a big change - the async review pattern in practice.
- MCP tools reference - every Claude and Codex tool, with full parameters.
- The shared durable ledger - where every reply, handoff, and decision is recorded.
- Coordinator and git-write policy - who owns git when a handoff lands on the other agent.