How Session Transfer from Claude to Codex Works Internally
Session transfer from Claude to Codex is a pure RPC-driven migration that validates the transcript locally, sends it to the Codex daemon via JSON-RPC, and retrieves the new thread ID from a ledger file keyed by SHA-256 hash.
The openai/codex-plugin-cc repository implements this transfer through a CLI companion that bridges Claude’s exported conversation history into native Codex threads. This article dissects the internal mechanics using the actual source implementation.
Core Components of the Transfer Pipeline
The architecture relies on three distinct utilities spread across the plugins/codex/scripts/lib/ directory:
resolveClaudeSessionPath
Located in plugins/codex/scripts/lib/claude-session-transfer.mjs (lines 20-44), this function acts as a security and validation gate. It expands ~ shortcuts to absolute paths, verifies the file ends with .jsonl, and guarantees the transcript resides within the Claude projects directory (~/.claude/projects). If any check fails, it throws a descriptive error before network activity begins.
importExternalAgentSession
Defined in plugins/codex/scripts/lib/codex.mjs (lines 58-78), this function orchestrates the RPC communication. It wraps the operation in withDirectAppServer, which establishes a direct connection to the Codex app-server via CodexAppServerClient.connect. The function then dispatches the migration request and installs a temporary notification handler that resolves a Promise upon receiving the EXTERNAL_AGENT_IMPORT_COMPLETED event. A configurable EXTERNAL_AGENT_IMPORT_TIMEOUT_MS guards against hanging imports.
importedThreadIdForSource
Also in plugins/codex/scripts/lib/codex.mjs (lines 61-78), this utility performs the post-import lookup. It reads the ledger file at $CODEX_HOME/external_agent_session_imports.json and matches records where both the source_path and content_sha256 fields correspond to the supplied transcript. This dual-key approach ensures deterministic thread ID retrieval.
Step-by-Step Execution Flow
When a user invokes the transfer command, the following sequence executes:
-
CLI Entry Point –
handleTransferinplugins/codex/scripts/codex-companion.mjs(lines 26-33) parses arguments and delegates toexecuteTransfer. -
Path Resolution –
resolveClaudeSessionPath(cwd, {source})converts relative paths to absolute, validates the.jsonlextension, and confirms the file lives under~/.claude/projects. -
Server Connection –
importExternalAgentSessionestablishes a direct RPC channel to the Codex daemon. -
Payload Construction –
externalAgentSessionMigration(sourcePath, cwd)(lines 81-99 ofcodex.mjs) builds a JSON-RPC-compatible payload containing amigrationItemsarray with a single entry of type"SESSIONS"and the absolute source path. -
RPC Dispatch – The client sends
client.request("externalAgentConfig/import", params)and awaits theEXTERNAL_AGENT_IMPORT_COMPLETEDnotification (handled byrequestExternalAgentSessionImportat lines 70-93). -
Thread ID Lookup – Upon completion,
importedThreadIdForSource(sourcePath)queries the ledger to retrieve theimported_thread_idassociated with the file’s SHA-256 hash. -
Result Rendering –
executeTransferreturns an object containingthreadId, a resume command (codex resume <threadId>), and the source path.renderTransferResultformats this for stdout.
CLI Usage Example
Transfer a Claude session using the companion script:
node plugins/codex/scripts/codex-companion.mjs transfer --source ~/my-project/session.jsonl
Example output:
Transferred the Claude session into a Codex thread with visible turn history.
Codex session ID: 7e2c9f93-a1b4-4c12-b8f7-d3e5a6b9c0d1
Resume in Codex: codex resume 7e2c9f93-a1b4-4c12-b8f7-d3e5a6b9c0d1
Under the hood, the CLI expands ~/my-project/session.jsonl to an absolute path, validates it, opens the RPC connection, and performs the migration.
Summary
- Validation-First Architecture:
resolveClaudeSessionPathensures only valid.jsonlfiles from the Claude projects directory proceed to import. - RPC-Driven Migration: The
externalAgentConfig/importmethod andEXTERNAL_AGENT_IMPORT_COMPLETEDnotification provide a synchronous handshake with the Codex daemon. - Deterministic Ledger: The import ledger at
$CODEX_HOME/external_agent_session_imports.jsonuses SHA-256 content hashing to map transcripts to thread IDs, making repeated transfers idempotent. - Timeout Protection:
EXTERNAL_AGENT_IMPORT_TIMEOUT_MSprevents the CLI from hanging if the daemon fails to respond.
Frequently Asked Questions
What file format is required for the Claude session?
The transfer requires a *.jsonl (JSON Lines) file containing the Claude conversation history. The resolveClaudeSessionPath function in claude-session-transfer.mjs explicitly validates this extension and rejects other formats before any network activity occurs.
Where must the Claude transcript be located?
For security validation, the transcript must reside within the Claude projects directory at ~/.claude/projects. The path resolution logic expands ~ shortcuts and verifies the absolute path falls within this directory tree, preventing arbitrary file access.
How does the system handle duplicate transfers?
The ledger file ($CODEX_HOME/external_agent_session_imports.json) keys each import by both the absolute file path and the SHA-256 hash of the file contents. When importedThreadIdForSource queries the ledger, it matches on both criteria, ensuring that importing the same transcript multiple times returns the existing thread ID rather than creating a duplicate.
What happens if the Codex daemon is unresponsive?
The importExternalAgentSession function implements a timeout mechanism using EXTERNAL_AGENT_IMPORT_TIMEOUT_MS. If the daemon fails to emit the EXTERNAL_AGENT_IMPORT_COMPLETED notification within this window, the Promise rejects with a timeout error, and the CLI exits gracefully rather than hanging indefinitely.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →