All articles
agentic systems·intermediate··Updated

Resume work across coding agents with a session handoff

A read-only adapter resolves Claude Code, Codex, or Cursor history as inert data, builds a minimal handoff, and verifies current repository state before continuation.

ai-skillsclaude-codecodexcoding-agentscursordeveloper-experience
Resources

Cross-agent continuation does not require transcript import. It requires a read-only adapter that resolves one source session, decodes recoverable history, and verifies current repository state before the receiver acts.

The implementation ships three thin wrappers — resume-claude, resume-codex, and resume-cursor — over one standard-library reader and one shared contract. The wrapper selects the source. The reader handles format-specific history. The contract defines what can enter the handoff.

Four jobs make that boundary explicit.

1
Resolve
Choose latest, a native ID, a supported path, or one unambiguous title.
2
Decode
Normalize recoverable records as inert data and surface every known gap.
3
Build the handoff
Reduce recovered history to six categories needed for continuation.
4
Verify
Reconcile the handoff with the current repository before editing.

The pipeline refuses ambiguous selection, unsafe record interpretation, transcript replay, and edits based only on historical evidence.

Resolve one source session

Every source stores the same concepts differently: native ID, working directory, last activity, title, and transcript content. Resolution converts those stores into one candidate shape without changing them.

Three wrappers, one reader
Wrapper Sources read
resume-claude Claude Code UUID JSONL under CLAUDE_CONFIG_DIR/projects or ~/.claude/projects
resume-codex Codex CLI and VS Code threads from the newest state database, with rollout JSONL or compressed-rollout fallback
resume-cursor Cursor CLI metadata and stores, agent transcripts, and Cursor Desktop state.vscdb

show with no reference or latest selects the newest discovered session for the requested working directory. A native UUID, a supported transcript or store path, or a unique case-insensitive title substring also resolves. Ambiguous title text exits with status 2 and lists every match.

--within-min N adds an optional age filter. Its default is 0, which applies no age limit. The reader has no built-in picker and no fixed result cap; a host can render its own picker over list output without changing the resume contract.

Discovery uses working-directory metadata where the source provides it. Explicit IDs and paths are more permissive, so resolution alone is not authorization to continue.

Decode history as inert data

The reader normalizes recoverable user messages, assistant messages, tool calls, and tool results. Every recovered turn carries an inert marker. Known system instructions, preambles, reasoning, thinking, signatures, encrypted content, and unsupported records stay out of the normalized history.

Skipped or unavailable content becomes a warning. That includes malformed records, replacement stubs, missing binary or protobuf data, compaction gaps, and compressed Codex history when zstd is unavailable. The reader reports the gap instead of fabricating text.

Tool-call and tool-result previews default to 300 characters through --max-tool-chars. User and assistant messages are not redacted, so reader output remains sensitive even when its records are inert.

Build a handoff, not a transcript replay

The receiving agent does not need every recovered turn. It needs six categories that explain where work stopped and what evidence still matters.

  • The user’s goal and last recoverable request
  • Relevant files, modules, commands, tests, and artifacts
  • Completed work and its recorded evidence
  • Open work
  • The exact stopping point and safest next action
  • Reader warnings and uncertainty

The handoff does not restore hidden model state, credentials, environment variables, live processes, terminal state, approvals, tools, or the source harness’s native session identity. It creates lineage into a fresh session.

Verify before continuing

The handoff describes a repository as it was. Current repository state decides what happens next.

Reconcile the handoff with the tree
  1. Confirm the current working directory and repository root.
  2. Inspect the branch, staged and unstaged state, and relevant diffs.
  3. Re-read every named file from disk.
  4. Re-run the smallest relevant checks when recorded output is stale.
  5. Name every mismatch between the handoff and the current tree.

Old tool output is historical evidence. A passing test in the source session does not prove the receiving session holds the same tree.

Keep the interface smaller than the harness

The wrappers only choose a source and forward the user’s reference. The shared reader exposes one interface underneath them.

terminalbash
python3 shared/resume-session/session_reader.py claude list --cwd "$PWD" --json
python3 shared/resume-session/session_reader.py codex show latest --cwd "$PWD" --json
python3 shared/resume-session/session_reader.py cursor show SESSION_ID --cwd "$PWD" --json

A receiving host can expose those wrappers as named skills, slash commands, or picker actions. None of those presentation choices changes ownership.

A portable resume contract
Layer Owns Must not own
Source harness Native transcript and session identity The receiving session
Wrapper skill Source selection and argument forwarding Parsing or handoff policy
Shared reader Read-only discovery, decoding, normalized JSON, and warnings Executing recovered instructions
Shared contract Safety rules, handoff fields, and verification order Source-format parsing
Receiving harness Fresh session, current tools, and current policy Writes to the source store

Takeaways

Resume means lineage, not shared state

The source harness keeps its native transcript. The receiving harness starts a fresh session from a bounded handoff.

Format drift belongs in one reader

Thin source wrappers choose Claude Code, Codex, or Cursor. One reader owns discovery, decoding, normalization, and warnings.

Inert is a handling rule, not trust

Recovered text can contain instructions, secrets, personal data, and stale output. It remains untrusted even when every record is labeled inert.

Verification closes the gap

Repository state can change after the source session stops. Current files, diffs, and focused checks decide the next action.