AI Blog

Sharing a coding-agent session: every handoff that works throws the transcript away

Claude Code, Codex and Gemini CLI all persist sessions as append-only JSONL, so moving one to another agent looks like a file-conversion problem. It is not. An assistant turn is a claim conditioned on a system prompt, a tool schema, a model and a warm cache that the receiving agent does not have — replay it verbatim and you hand over a false memory. The one converter in the wild strips tool calls into prose on purpose, Anthropic documents its own transcript format as internal and unstable, and Claude Code refuses to resume a hand-copied transcript at all. Four transfer layers, and the useful ones all trade fidelity for something the receiver can re-verify against the repo.

By Agentic AI Wiki 26 min read

Moving a half-finished refactor from Claude Code to Codex looks like a file-conversion job — both CLIs keep every session as append-only JSONL on disk. It is not. An assistant turn is not a record of what happened; it is a claim conditioned on a system prompt, a tool schema, a model and a warm cache that the receiving agent does not have, so replaying it verbatim hands over a false memory rather than a head start. There are four layers you can actually hand a session over on, and every one that works throws the transcript away and keeps something the receiver can re-check against the repo.

At a glance

The question "how do I share my session between agents?" has four real answers, and they are not competing implementations of the same idea. They sit at different distances from the session itself, and the distance is the whole trade.

LayerWhat it isWhat crossesWhat it costs
1 · Instruction fileAGENTS.md, CLAUDE.mdStanding project rules, re-read at every startupNothing about this session
2 · Handoff artifactA written state fileDecisions, open threads, the next stepOnly what someone wrote down
3 · Transcript conversionParser and writer over the on-disk JSONLEvery turn, in order, as proseTool calls, identifiers, cache — and a format the vendor may break
4 · Live protocol or storeACP client, A2A peer, MCP memory serverContinuous shared stateBoth ends must already speak it
The four layers a coding-agent session can be handed over on Agent A on the left and Agent B on the right, connected by four horizontal lanes. Layer one is the instruction file, AGENTS.md or CLAUDE.md, which carries standing project rules and nothing about the session. Layer two is the handoff artifact, a written state file, which carries decisions and the next step but only what someone wrote down. Layer three is transcript conversion, a parser and writer pair over the on-disk JSONL, which carries turn order and prose but drops tool calls, identifiers and the cache. Layer four is a live protocol or shared store such as an ACP client or an MCP memory server, which carries continuous state but requires both ends to speak it. MORE PORTABLE, LESS OF THIS SESSION MORE OF THIS SESSION, LESS PORTABLE AGENT A Claude Code holds the session AGENT B Codex CLI needs to continue LAYER 1 — INSTRUCTION FILE AGENTS.md / CLAUDE.md committed, re-read at startup WHAT CROSSES Standing project rules — nothing about this session LAYER 2 — HANDOFF ARTIFACT A written state file decisions, next step, gotchas WHAT CROSSES Claims the receiver can re-verify against the repo LAYER 3 — TRANSCRIPT CONVERSION parser to writer, over JSONL every turn, in order WHAT CROSSES Prose only — tool calls, ids and the cache do not survive LAYER 4 — LIVE PROTOCOL / STORE ACP client, MCP memory a shared substrate, not a file WHAT CROSSES Continuous state — if both ends already speak it
The further down you go, the more of this session crosses — and the fewer pairs of agents it crosses between.

Score them and the shape of the problem falls out. One row carries the whole session. It is the weakest row on every other axis, and that is not an implementation gap waiting to be closed.

The four handoff layers scored on five axes A matrix of four transfer layers against five axes: cross-vendor reach today, how much of this session it carries, whether the receiver can verify what it is told, whether it survives a version bump, and how little setup it needs. The instruction file and the handoff artifact score strongly on everything except carrying the session. Transcript conversion is the only row that carries every turn, and it is weak on every other axis. Live protocols and shared stores sit in the middle throughout. One row carries the session. It is weak everywhere else. Cross-vendortoday Carries thissession Receiver canverify it Survives aversion bump Setup costto start Instruction file 30+ agents None of it It is in the repo Plain text One file Handoff artifact Any agent What you wrote Re-checkable Plain text You write it Transcript conversion Per-pair code Every turn Asserted only Internal format Run a tool Live protocol / store If both speak it Facts, not turns Depends Versioned spec Run a server Strong Partial Weak
Fidelity to the transcript and usefulness to the receiver point in opposite directions.

Where a session actually lives

Where each coding agent keeps its session on disk Three columns of on-disk session storage. Claude Code writes JSONL under a projects directory keyed by encoded working directory, with subagent transcripts in their own sidechain files. Codex CLI writes date-partitioned rollout JSONL files that its context manager replays on resume. Gemini CLI writes session JSON under a temporary project directory. Below, a band notes the shape all three share: append-only JSONL, one line per turn, each linked to its parent. A final highlighted band notes what they do not share: entry semantics, with Anthropic documenting its format as internal and able to change on any release. AGENT Claude Code ~/.claude/projects/ <encoded-cwd>/ <session-id>.jsonl Each line carries uuid, parentUuid, sessionId, cwd. Subagents get their own sidechain files. AGENT Codex CLI ~/.codex/sessions/ YYYY/MM/DD/ rollout-<ts>-<id>.jsonl A rollout stream of response items. The context manager replays them to rebuild state on resume. AGENT Gemini CLI ~/.gemini/tmp/ <project>/chats/ session-*.json A native ACP agent, so an editor can drive it — and it still keeps its own private session file. WHAT ALL THREE SHARE Append-only JSONL, one line per turn, each line linked to its parent. WHAT NONE OF THEM SHARE Entry semantics. Anthropic documents its format as internal to Claude Code and subject to change between versions — a parser can break on any release.
Three agents, three paths, one shape — and no shared meaning underneath it.

The three major CLI agents are close to identical in storage strategy. Claude Code writes ~/.claude/projects/<project>/<session-id>.jsonl, where <project> is the working directory path with non-alphanumeric characters replaced by hyphens; each line is a JSON object for a message, tool use or metadata entry, and subagents get their own sidechain transcripts. Codex CLI writes ~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<uuid>.jsonl, a rollout stream its context manager replays to rebuild model state when you run codex resume. Gemini CLI keeps session JSON under ~/.gemini/tmp/. All three are local-first, append-only, and readable with jq.

That similarity is exactly what makes the file-conversion intuition so appealing, and there are two documented facts that should kill it before you write a line of code.

The first is that the format is not a contract. Anthropic's own session documentation says the entry format "is internal to Claude Code and changes between versions, so scripts that parse these files directly can break on any release," and points you at /export or the scripting interfaces instead. A converter built on the raw JSONL is not integrating with a format; it is depending on an implementation detail that its vendor has explicitly reserved the right to change.

The second is subtler and more telling. Claude Code will resume a session ID started in a different project directory — but the cross-project search resolves the ID only when exactly one other project holds a transcript with messages for it, so a hand-copied duplicate makes it report not-found rather than resume an arbitrary copy. The vendor treats the transcript as an identity, not a portable document. Copying the file does not copy the session, and the tool is built to say so.

Layer 1 — the instruction file, which carries none of the session

AGENTS.md is the closest thing the ecosystem has to a settled standard. OpenAI released it in August 2025 and handed it to the Linux Foundation's Agentic AI Foundation alongside MCP; by 2026 it is read by 30-plus agents and lives in 60,000-plus repositories. Claude Code is the notable holdout — as of August 2026 it still loads CLAUDE.md, which you resolve by pointing one file at the other with an import line rather than maintaining two.

Fifteen months of cross-agent standards Three parallel timelines from June 2025 to August 2026. AGENTS.md was released by OpenAI in August 2025, transferred to the Linux Foundation's Agentic AI Foundation in late 2025, and reached 60,000 or more repositories by May 2026. The Agent Client Protocol was created by Zed in August 2025 and gained a registry co-launched with JetBrains in January 2026, listing 40 or more agents by 2026. A2A went to the Linux Foundation in June 2025, passed 150 organisations at its one-year mark in April 2026, and reached version 1.0. A footnote records that Claude Code still reads CLAUDE.md as of August 2026. The plumbing standardised. The session did not. AGENTS.md Released by OpenAI To the Linux Foundation 60k+ repos ACP · client↔agent Created by Zed Registry, with JetBrains 40+ agents listed A2A · agent↔agent To the Linux Foundation 150+ orgs at one year v1.0 JUN 2025 JAN 2026 AUG 2026 Claude Code still reads CLAUDE.md as of August 2026 — point one file at the other with an import.
Instructions, editor integration and delegation all standardised inside fifteen months. The session transcript did not.

Nothing about your current session crosses this layer, which is precisely why it is the highest-leverage place to start. Before you build a bridge, audit what you actually keep re-explaining. A large share of it — the build command, the directory that is off-limits, the test that is flaky on CI, the reason the codebase uses advisory locks — is standing knowledge that has been misfiled as episodic. It does not need to be transferred between sessions because it should never have been session-scoped in the first place. The cheapest session transfer is the one you do not need.

What is left after that audit is genuinely task-local, and that is the real handoff problem. It is much smaller than it looked.

Layer 2 — the handoff artifact, and the rule that makes it work

The pattern practitioners actually run is unglamorous: the outgoing agent writes a state file, the incoming agent is told to read it in full before doing anything else. People reach for it when a quota runs out mid-task, when a specific model is better at the next step, or when the work crosses a person. Published versions converge on a similar shape — a handful of fixed sections, updated on state transitions rather than on a timer, with a hard rule to re-verify the last step before trusting it.

That re-verification rule is not a safety belt bolted onto the pattern. It is the pattern, and it generalises into a test you can apply line by line: every line in a handoff file is either a fact with a command that re-checks it, or a decision, labelled as one. There is no third category. Anything that is neither is decoration, and decoration is where a handoff quietly goes wrong — the receiving agent treats a stale assertion as ground truth and builds on it.

Every line in a handoff file is either re-checkable or a labelled decision Two columns. On the left, three things a handoff file claims: that a migration is applied, that a named test is the failing one, and that advisory locks were chosen over Redis. On the right, what lets the receiving agent trust each: a single command for the first two, and for the third, nothing — it is a decision, and must be labelled as one so the receiver either accepts it or re-opens it rather than discovering the difference by testing. WHAT THE HANDOFF CLAIMS WHAT LETS THE RECEIVER TRUST IT FACT Migration 0042 is applied on dev added the audit_log table ONE COMMAND psql -c '\dt' | grep audit_log Costs the receiver two seconds. FACT test_retry is the one still failing the other four now pass ONE COMMAND pytest tests/test_sender.py -x Disagreement shows up immediately. DECISION Advisory locks, not Redis because the lock has to die with the transaction NOTHING RE-CHECKS THIS So label it a decision, not a fact. The receiver either accepts it or re-opens it. What it must never do is discover the difference halfway through the work.
Facts get a command. Decisions get a label. Everything else is decoration.

Written out, the whole thing fits on a screen:

# HANDOFF 2026-08-28T14:20Z · from: claude-code · to: codex

## Goal
Retry-with-backoff on the email sender. Must not double-send.

## Verified facts — each line names the command that re-checks it
- migration 0042 applied      psql -c '\dt' | grep audit_log
- 4 of 5 sender tests pass     pytest tests/test_sender.py
- lint clean on this branch    ruff check services/email

## Decisions — nothing re-checks these; accept or re-open
- Postgres advisory locks, not Redis: the lock must die with the transaction.
- Backoff capped at 5 attempts: the SLA is 15 minutes, not durability.

## Open
- test_retry fails: the fake clock does not advance inside the retry loop.

## Do not touch
- services/billing/* — unrelated migration in flight.

Two details earn their place. Updating on state transitions rather than on a timer matters because the transitions are the events; a file written every five minutes is stale in a way nobody can detect, while a file written when the test suite went green is either current or obviously not. And the "do not touch" section does work that no summary does — it transfers a constraint the receiving agent has no way to infer from the repository.

Layer 3 — the converter that deletes the most valuable-looking data

Tools that move sessions between CLIs do exist, and their architecture is the same one you would design: a parser per agent, a writer per agent, and a neutral intermediate form between them, so adding a CLI means adding one module on each side rather than another entry in an N-by-N matrix. One published implementation is around 600 lines of Python with no network component — it reads the three session directories and writes the target's format.

The transcript conversion pipeline and what it deliberately discards A left-to-right pipeline: the source agent's JSONL file on disk, a per-agent parser, a neutral session form holding ordered turns plus prose summaries of tool calls, a per-agent writer, and the target agent's resumable session. A dashed arrow drops from the parser into a band below labelled dropped on purpose, listing tool use blocks, tool result blocks, reasoning traces, absolute paths and credentials, the warm prompt cache, and the system prompt those turns answered. SOURCE Claude Code append-only .jsonl on disk PARSER per agent one module per CLI NEUTRAL FORM NeutralSession ordered turns, plus prose summaries of the tool calls WRITER per agent emits target format TARGET Codex resumable DROPPED ON PURPOSE The receiving model needs understanding, not replay. • tool_use blocks • tool_result blocks • reasoning traces • absolute paths, credentials • the warm prompt cache • the system prompt behind them "edited services/email/sender.py:82-94" ← what survives instead
The pipeline's neutral form is prose. The structured part of the transcript is discarded on the way in.

The interesting decision is what the neutral form holds: an ordered list of user and assistant turns, plus prose summaries of the tool calls. Tool invocations are not mapped across agents — they are stripped and narrated. A recorded edit becomes the string edited services/email/sender.py:82-94 sitting alongside the assistant turn. The stated reason is that the receiving model needs understanding, not replay capability.

Read that as an admission and it is the most useful sentence in this whole area. The richest, most structured, most faithful part of the transcript — the exact record of what the agent did — is the part a working converter deliberately destroys. That is not a shortcut taken for schedule reasons. It is the correct design, and the reason why is worth being precise about.

It also matches what the vendors ship. Claude Code's own /export produces a rendered transcript for a person to read, not a structured artifact for another agent to ingest; the structured interfaces it documents — claude -p --output-format json, the transcript_path that hooks receive, the Agent SDK — are for scripting against your own running session, not for handing history to a different vendor's CLI. Nobody is shipping the lossless bridge because there is nothing coherent to ship.

What breaks on the way across

Three things that break when a transcript crosses to another agent Three columns. The tool schema column notes that recorded tool calls name tools the receiving agent does not have, so a replayed call is an instruction it cannot run. The system prompt column notes that every assistant turn answered instructions the receiver never read, so the turn keeps its authority and loses its reason. The cache and environment column notes that the prompt cache is keyed to one provider's exact prefix and that flags such as the MCP config and added directories were never in the transcript at all. BREAKS FIRST The tool schema A recorded call names Edit; the receiver has apply_patch. Every tool_result points at a tool_use id that no longer resolves, so a replayed call is an unrunnable instruction. BREAKS QUIETLY The system prompt Each assistant turn answered instructions the receiver never read. The turn keeps its authority and loses its reason — which is the definition of a false memory. NEVER MADE THE FILE Cache and environment The prompt cache is keyed to one provider's exact prefix. MCP config, added directories and settings files were never in the transcript — even a same-agent resume asks you to pass them again.
Three independent failures, and only the first one is a format problem.

The tool schema breaks first and most visibly. A Claude Code transcript contains tool_use blocks naming that harness's tools, each with an id, and tool_result blocks referencing those ids. Codex has a different tool surface. You have two options and both are bad: map tool-for-tool, which is a combinatorial job that breaks every time either side ships a new tool or renames an argument, or delete the calls. Converters delete them, and the dangling ids are why.

The system prompt breaks quietly, and this is the failure that actually costs you. Every assistant turn in that transcript was generated in response to instructions the receiving agent never read — a different harness prompt, different tool descriptions, a different model's disposition. Replayed into a new agent, those turns arrive in the assistant role, which is the role a model treats as its own past reasoning. The turn keeps its authority and loses its justification. The receiving agent now "remembers" doing work it did not do, under rules it never saw, and has no mechanism to doubt it. That is a false memory in the precise sense: confidently held, structurally indistinguishable from a real one, and wrong.

The cache and the environment were never in the file at all. Claude Code's documentation is blunt about this even for its own resumes: a resumed session does not restore --mcp-config, --settings, --plugin-dir, --fallback-model, or directories added with --add-dir; you pass them again. If a same-agent, same-machine resume cannot reconstruct the environment from the transcript, a cross-vendor one certainly cannot. And the economics bite in the same place: when you resume a Claude Code session that has been idle for about an hour and runs over 100,000 tokens, it offers to summarise precisely because the prompt cache has expired and the next request will reprocess the full history once regardless of which option you pick. A cross-agent transfer pays that bill by construction — you are re-sending the entire conversation to a provider that has never seen it, with no prefix to hit.

Only the first of these three is a format problem. The other two survive any format you invent, which is why "someone should standardise this" is not the answer it sounds like.

Layer 4 — the live options, and what each one is actually for

Three live topologies for sharing agent state, and what each one does not do Three panels. The first shows the Agent Client Protocol: an editor at the top driving Claude Code through an adapter and Gemini CLI natively; it solves one editor driving many agents but session identifiers still belong to the agent that minted them. The second shows A2A: a client agent delegating a task under a context identifier to a remote agent, which returns artifacts; it crosses vendor boundaries but transfers deliverables rather than the peer's history. The third shows a shared MCP memory server that two agents both read and write; it gives durable cross-agent facts but stores only what was deliberately saved. TOPOLOGY 1 ACP — client to agent Zed / JetBrains Claude Code via adapter Gemini CLI native SOLVES One editor drives many agents. session/load replays history into the client for display. DOES NOT SOLVE A session id belongs to the agent that minted it. Two agents in one window are still two sessions. TOPOLOGY 2 A2A — agent to agent Client agent task + contextId Remote agent artifacts SOLVES Delegation across vendor and org boundaries. One contextId groups the related tasks. DOES NOT SOLVE It returns artifacts, not the peer's reasoning. The remote agent's history stays remote, and that is the design. TOPOLOGY 3 MCP — a shared store Claude Code Codex Memory server SOLVES Durable facts both agents read and write, across machines and across sessions. DOES NOT SOLVE It holds what someone chose to save. That is a memory, not a transcript — and the choosing is the hard part.
Three genuinely live options. None of them moves a transcript, and none of them is trying to.

ACP standardises the socket, not the state

Zed's Agent Client Protocol, created in August 2025, does for editors and agents what LSP did for editors and languages: JSON-RPC over stdio, so any client can drive any agent. JetBrains joined, the two co-launched a registry in January 2026, and 40-plus agents are listed. Claude Code and Codex CLI both reach it through Zed-built adapters; Gemini CLI is native.

The session methods look promising and then do not deliver what you want. session/new opens a context, session/load reconnects to a stored one, and session/resume reconnects without replay. But loading means the agent replays the conversation to the client as a stream of update notifications, so the editor can repaint the thread — and it only responds once every entry has been streamed. The history belongs to the agent that produced it; the client is being brought up to date, not handed the state. Running Claude Code and Codex side by side in one Zed window gives you two agents in two threads, not one shared session. ACP solved editor-to-agent, which was a real N-by-M problem, and it does not claim to have solved agent-to-agent.

One naming trap worth flagging: this is not the only ACP. An Agent Communication Protocol also existed, went to the Linux Foundation in July 2025, and folded into A2A — search results still cheerfully mix the two, so check which one a page means before you take its advice.

A2A transfers deliverables, deliberately

A2A reached v1.0 under Linux Foundation governance, having been contributed in June 2025 and passed 150 organisations at its one-year mark. Its model is delegation: a client agent sends a task to a remote agent, a server-generated contextId groups related tasks into one interaction, and results come back as artifacts. What does not come back is the remote agent's reasoning or history — the peer is opaque by design, because the whole point is crossing an organisational boundary where you neither can nor should inspect the other side's internals. A2A is the right protocol for "have that agent do this thing," and structurally the wrong one for "become me."

Shared memory moves facts, and the choosing is the work

MCP memory servers are the most practical live option today, because any MCP client can connect to one. Write a fact in Codex, read it in Claude Code. Implementations differ in how they store it — file-backed markdown with URL addressing on one end, managed semantic stores on the other — but the shape is common: a durable pool both agents read and write.

The catch is the same one as layer 2, wearing better clothes. A memory server holds what someone chose to save, and choosing is the hard part. Swapping a markdown file for a database does not decide what belongs in it. Every live option converges on the same conclusion from a different direction: agents share facts, not turns.

When to pick which

SituationReach forWhy
You re-explain the same thing every sessionInstruction fileIt was never session state; stop transferring it
Quota died mid-task; continue in another CLIHandoff artifactMinutes to write, works between any two agents, re-checkable
Handing work to a teammateHandoff artifact, plus the branchA person needs the decisions, not the keystrokes
You want the same thread in your editorACP clientOne UI over many agents — still one session per agent
Facts should outlive every sessionMCP memory serverDurable, cross-client, and worth the setup at team scale
Another team's agent should do a piece of workA2ADelegation across a trust boundary; artifacts come back
You want the literal conversation movedReconsiderThe receiver inherits authority without justification

If you take one operational habit from this: write the handoff file as you work, not when you need it. A file assembled at the moment the quota dies is written by an agent that has already lost the thread. A file updated at each state transition is written by one that still has it.

FAQ

Can I copy a session JSONL to another machine and resume it?

Not reliably, and Claude Code is built to refuse. Its cross-project session lookup resolves an ID only when exactly one project holds a transcript for it, so a hand-copied duplicate reports not-found rather than resuming an arbitrary copy. Add that the entry format is documented as internal and able to change on any release, and file-copying is the least durable option available.

Is there an official cross-agent session format?

No, and the gap is not an oversight. AGENTS.md standardised instructions, ACP standardised editor-to-agent communication, A2A standardised delegation. None of them standardises a transcript, because a transcript's meaning depends on a system prompt and a tool schema that are agent-specific by construction.

Does ACP let my editor hand a Claude Code session to Codex?

No. ACP session IDs are minted and stored by the agent that created them, and session/load replays history from the agent to the client for display. Running both agents in one Zed window gives you two threads side by side, not one shared session.

Is pasting the output of /compact good enough?

It is layer 2 done carelessly. A compaction summary is prose without provenance: the receiving agent cannot tell which lines are current facts and which are stale assertions. Add the re-check command after each factual claim and label the decisions, and the same summary becomes a real handoff.

Does an MCP memory server remove the need for a handoff file?

It removes the need to re-state standing facts, which is layer 1's job done with better plumbing. It does not capture the state of the task you are in the middle of — what you just verified, what you decided, what is still broken. Those are still yours to write down.

Will better tooling eventually make lossless transfer work?

Format conversion will keep improving, and it will keep dropping tool calls. Two of the three failures — inherited authority from a foreign system prompt, and a cache that only exists on one provider's side — are properties of how language models consume context, not defects in a serialiser.

Further reading

On this wiki:

Sources: