Move agent conversations without flattening away the structures that make them usable.
Provider-neutral session portability with a shared conversation IR, deterministic plans, and fidelity-aware adapters. This project succeeds codex-to-claude; its original Codex β Claude flow remains available through the same commands and the legacy executable alias.
Newest first.
The linked pull requests below are historical work from the codex-to-claude predecessor repository.
- π§ Importer-matrix foundation β a lossless raw-envelope store, typed historical IR, archive/project/session/date selection, deterministic plans and loss reports, and a version-gated Codex Desktop 26.721.41059 target adapter now live beside the original converter. The built-in Codex importer was audited because β50 succeededβ did not preserve native tool calls, reasoning, images, compaction, world state, or turn context in the observed targets. See the version-pinned research.
- π§· A conversation you carried on in Claude is left alone β
--forcetreated "Claude replayed the history into the file" and "you answered in it" as the same kind of change and overwrote both, which cost a message. It now tells them apart and refuses the second, naming what would have been deleted and counting only lines somebody typed. Continuing an import also makes Claude fork it and repoint the record at a session of its own; those records are recognised as no longer ours, left alone rather than duplicated, and searched for messages sent there. (#9, #10) - π A dry run stays dry β
npm run import -- --dry-runused to import for real. npm owns--dry-runand--force, parses them as its own config and passes an empty argv on, so the flag never arrived and the write path ran. It now detects the swallowed flag and refuses;npm run import:dryhas it baked in. (#6) - π§ Memory citations out of the reply β when Codex answers from its memory files it appends an
<oai-mem-citation>block, which Codex Desktop parses back out and never shows. Imports used to end an answer with raw markup; the citation is now a readable metadata line after the reply. (#3) - π·οΈ Titles the way Codex titles them β Codex names the threads you start from the app and lists that name, not your opening message. Imports read those names from
~/.codex/session_index.jsonl, so a conversation is called the same thing on both sides. (#2) - π macOS support β the Claude Desktop session-record store resolves per platform now instead of assuming the Windows layout, and project grouping follows current Codex Desktop builds, which dropped the threadβproject map in favour of
local-projectsroot paths. (#1)
On this page β Quick start Β· Importer matrix Β· How it works Β· Features Β· Choosing conversations Β· Requirements Β· Safety Β· Limitations
Reference β CLI Β· Conversion Β· Formats
git clone https://github.com/Agentryx-ai/pass-the-thread
cd pass-the-thread
node --experimental-strip-types --experimental-sqlite src/threadpass.ts list$ threadpass list
20 conversation(s), 20 after refinements. [vscode:19 cli:1]
By project:
3 Agentryx
2 ReTalk
2 Itineva
1 ModuBoza
1 (no project)
...
$ threadpass import --dry-run # writes nothing
$ threadpass import --title-prefix "[Codex] "Restart Claude Desktop. The conversations are in the sidebar under the same projects, and you can open and continue them.
Flags belong on node src/threadpass.ts, not on npm run β see
CLI.
threadpass handoff header|resolve|read creates, selects, and reads inert
session handoff files, matched on exact project identity and never executed.
--allow-cross-project authorizes reading a file outside the current project
and marks the body untrusted β see CLI.
The original Codex β Claude commands remain available. The provider matrix adds the first reverse adapter through the same threadpass entrypoint:
Codex ββ shared typed IR + transactional adapter βββΊ Claude
Claude ββ shared typed IR + 41059 adapter βββββββββββΊ Codex
Start with a read-only inventory or a saved deterministic plan:
threadpass scan --archive all
threadpass plan --archive active --project-scope existing-targets \
--render-mode semantic \
--evidence reference/codex-desktop/26.721.41059/manifest.json \
--out import-plan.jsonThere is no implicit 30-day or 50-session cap. Filters include exact sessions, projects, archive state, existing-target projects, inclusive date bounds, and an explicit --limit. existing-targets means the canonical target project/group/root already exists; it does not mean that the exact target conversation exists. Plans report those facts separately as targetProjectExists and targetConversationExists (plus an exact/collision state). Unknown project membership, archive state, or activity time stays visible in an all/unfiltered inventory and fails closed if a requested filter needs it. Every selected source revision remains byte-exact in the bridge sidecar. semantic (default) renders supported meaning into the target; verbatim renders the entire canonical source as inert historical text. Historical task/access records and superseded Goal events always remain inert. A separately identified authoritative active Goal is restored by default in either render mode; --goal-mode skip keeps it as history without live activation.
Applying a reverse plan is intentionally strict and Windows-only for this first target. It requires Codex Desktop to be closed, the exact saved plan digest, and the pinned 26.721.41059 evidence manifest. The loader re-hashes the installed app.asar and codex.exe; a copied manifest by itself is not accepted as live-version evidence. Apply probes once under the lock, performs Goal/journal/sidecar setup, then full-hashes the active Appx identity again immediately before the first Codex target mutation. A mismatch fails closed. That second probe is the exact batch-start support snapshot; updates during the subsequent multi-session batch are outside atomicity, so artifacts are not re-hashed per session. Unknown/new builds can still be scanned and planned, but receive no private-write capability. Research or dry-run planning for another installed build requires a user-supplied provenance manifest that binds those live artifacts; that manifest does not itself enable writes. The plan independently binds rollout, thread-index, archive, project-identity, and Goal capabilities. apply --dry-run writes nothing, reports static blockers, and names live gates it cannot prove without applying. See CLI.
Semantic Claude history is only made resumable when its compact summary can be
recovered into a nonempty Codex replacement_history; otherwise planning fails
closed. Verbatim mode instead keeps compact records as inert archival context
without claiming native resume semantics. Both modes enforce a conservative
serialized UTF-8 byte limit; the offline check does not claim to calculate
provider tokens. Project roots are canonicalized before registration so
equivalent path spellings do not create a second Codex project identity.
Forward plans (--direction codex-to-claude) are now applicable too. They bind
the typed renderer and exact before/after hashes for transcripts and Claude
Desktop wrapper records. --dry-run is strictly zero-mutation; existing files
require --allow-overwrite plus an unchanged-target proof. Before the first
target write, immutable backups and a durable operation journal are created, so
an interrupted uncommitted batch can be reconciled or rolled back with
threadpass recover --operation <id>. Unsupported protocol, world/access state
and unknown records remain in the canonical sidecar instead of leaking as raw
chat; task notifications are readable inert metadata, and archived state is
preserved in the Claude wrapper. Semantic compaction activates only the latest
portable boundary and requires its replacement to contain safely renderable,
nonempty content; earlier boundaries remain sidecar-only.
Claude Desktop keeps a conversation in two places, and needs both to show it:
| Layer | Location | Role |
|---|---|---|
| Session record | <app-data>/Claude/claude-code-sessions/<account>/<device>/local_<uuid>.json |
Builds the conversation list. Points at a transcript by cliSessionId and cwd. |
| Transcript | ~/.claude/projects/<encoded-cwd>/<cliSessionId>.jsonl |
The conversation itself. |
The wrapper sessionId and transcript/CLI cliSessionId are distinct identities and are validated separately. Codex plans likewise label the source rollout id, native/index thread id, and deterministic target thread id by role even when native evidence makes two values equal. Writing only a transcript leaves it invisible, so this writes both.
~/.codex/sessions/**/rollout-*.jsonl
β
ββ select what Codex Desktop lists
ββ convert rollout items to transcript lines
ββ validate replay invariants
β
ββββΊ ~/.claude/projects/<enc-cwd>/<id>.jsonl
ββββΊ claude-code-sessions/.../local_<uuid>.json
- π The same list you already see β selection replicates the Codex Desktop sidebar, including its project grouping, so you pick from what you recognise
- π Whole conversations, not just text β messages, tool calls, tool results, images and sub-agent reports
- π·οΈ Titles that read like titles β Codex's own conversation names, and its injected boilerplate marked as metadata so it never becomes one
- π§ Registered, so Claude actually lists it β the session record is written alongside the transcript
- π Sandbox and approval mapped β Codex's two settings collapse into Claude's one
permissionMode - π Long sessions still open β imports start from the context Codex compacted to; 39 MB of history became 6.5 MB across 20 conversations
- β Validated before writing β every conversion is replay-checked, and one that would fail on resume is repaired or refused
- π§Ύ Semantic or verbatim, in both directions β semantic conversion is the default;
--render-mode verbatimkeeps exact UTF-8 source text inert when canonical representation matters more than native rendering - π Your edits win β a conversation you continued in Claude is skipped, not overwritten
What it deliberately does not do
Out of scope today: migrating settings, skills, plugins and MCP servers; network/account-backed history that has no local transcript; and pretending historical tasks, superseded goals, or grants are safe live control state. Only the canonical current Goal can be restored, through a separately versioned and verified target-native capability.
Why this is its own repository
It was written for Agentryx, an AI-native agent harness in development, which needs conversation history to move between providers. It depends on the on-disk formats of two proprietary desktop apps, and those change on someone else's schedule. That churn is easier to handle in a small project that can be fixed and released on its own. It works without Agentryx.
Codex Desktop groups by project. Conversations without one appear only under Recents, and plenty of people never look at them, so membership is a filter:
| Flag | Imports |
|---|---|
| (default) | everything the sidebar shows |
--projects-only |
only conversations in a project |
--projectless-only |
only the Recents ones |
--project <name> |
one project, by Codex name or path |
--include-archived, --archived-only |
include or restrict to archived threads |
--interactive-only |
drop codex exec automation runs |
list prints the per-project breakdown first. Further limits: --since-days,
--max, --from, --to, --id. Full flag reference in CLI.
Windows or macOS, and Node.js 22.6 or newer. The runtime has no production dependencies; development installs TypeScript and Node declarations for strict type checking.
Every path resolves per platform: the Claude Desktop session-record store from
%APPDATA%, ~/Library/Application Support or $XDG_CONFIG_HOME, and
~/.codex and ~/.claude from the home directory. Linux follows the same rules
but has not been run there; --sessions-root and --claude-home override any
of it.
This writes into another application's local data, so it stays cautious.
- It creates files and deletes none. The only records it rewrites are ones it
wrote itself, and only with
--force. --dry-runprints every target path and writes nothing.- If you continued an imported conversation in Claude, the transcript changed since the import and it is skipped, with a note.
--forceoverrides that for a transcript Claude only rewrote β opening a conversation makes it replay the history back into the file β and says what it overwrote. It stops at a transcript holding messages you sent after the import, naming the first one, and does not offer to override: the flag exists to get past replay duplicates, not to discard conversation.- To undo an import, delete the transcripts and the
local_*.jsonrecords it created. Both are listed in its output. - Prefer running with Claude Desktop closed.
- Built on undocumented internals of two proprietary desktop apps. They can change at any time.
- Claude β Codex writes are supported only for the exact audited Codex Desktop 26.721.41059 artifacts and are still experimental; create and inspect a plan first, close Codex, and keep a backup.
- Tested on Windows 11 and macOS 26. Linux resolves the same way but has not been run there.
- Codex encrypts its compaction summaries, so an import shows where compaction happened but not what it said.
- Sub-agent threads arrive as messages, not as separate threads.
- JSONL sources are read incrementally with strict UTF-8 validation and byte-exact hashing. A selected conversion still builds its typed sidecar in memory, while bulk Claude inventory scans release transcript envelopes as they go.
| CLI | Commands, every flag, exit codes |
| Conversion | What each Codex item becomes, injected context, permissions, compaction, replay repairs |
| Formats | The on-disk shapes both sides use, reconstructed by observation |
- openai/codex-plugin-cc, calling Codex from Claude Code
- inmzhang/transession, CLI session translation by session id
- Codex CLI
/import, Claude into Codex
npm run verifyUnofficial, and not affiliated with OpenAI or Anthropic. "Codex" and "Claude" are trademarks of their respective owners. Back up anything you care about.
Apache 2.0, see LICENSE and NOTICE.
You can use, modify and ship this, including commercially. Keep the copyright notice and the NOTICE file, and say what you changed. The license does not grant rights to the project's name or marks.