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.
| Layer | What it is | What crosses | What it costs |
|---|---|---|---|
| 1 · Instruction file | AGENTS.md, CLAUDE.md | Standing project rules, re-read at every startup | Nothing about this session |
| 2 · Handoff artifact | A written state file | Decisions, open threads, the next step | Only what someone wrote down |
| 3 · Transcript conversion | Parser and writer over the on-disk JSONL | Every turn, in order, as prose | Tool calls, identifiers, cache — and a format the vendor may break |
| 4 · Live protocol or store | ACP client, A2A peer, MCP memory server | Continuous shared state | Both ends must already speak it |
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.
Where a session actually lives
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.
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.
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 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
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
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
| Situation | Reach for | Why |
|---|---|---|
| You re-explain the same thing every session | Instruction file | It was never session state; stop transferring it |
| Quota died mid-task; continue in another CLI | Handoff artifact | Minutes to write, works between any two agents, re-checkable |
| Handing work to a teammate | Handoff artifact, plus the branch | A person needs the decisions, not the keystrokes |
| You want the same thread in your editor | ACP client | One UI over many agents — still one session per agent |
| Facts should outlive every session | MCP memory server | Durable, cross-client, and worth the setup at team scale |
| Another team's agent should do a piece of work | A2A | Delegation across a trust boundary; artifacts come back |
| You want the literal conversation moved | Reconsider | The 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:
- Agent Interoperability — the standards layer this post keeps bumping into.
- Context Engineering — deciding what belongs in the window in the first place.
- Prompt Caching — why a transfer has a token bill attached.
- The Agent Harness — the system prompt and tool surface that a transcript silently assumes.
- Why Interop Matters: The M×N Problem — the structure ACP and A2A are each solving one slice of.
- ACP: What Happened — the other ACP, and why search results conflate them.
- Context Compaction — what survives a summary, and what does not.
- Shared Memory & Blackboard — the multi-agent version of the same question.
Sources:
- Claude Code Docs — Manage sessions
- Agent Client Protocol — Session Setup
- Agent Client Protocol — Agents
- Zed Blog — Claude Code: Now in Beta in Zed
- openai/codex — Session / Rollout Files
- A2A Protocol — Core Concepts
- Linux Foundation — A2A surpasses 150 organizations
- AuthSec — Transfer Claude Code sessions to Codex and Gemini
- ai-muninn — A live-state handoff protocol for Claude Code and Codex
- Jon Aquino — Exporting a Claude Code session to Codex
- Mem0 — Introducing OpenMemory MCP