How Session Transfer Works Between Claude Code and Codex
The codex transfer command migrates Claude Code session transcripts (.jsonl files) into new Codex threads by validating the source path, calling the Codex app-server's externalAgentSessionImport RPC method, and returning a resumable thread ID.
The openai/codex-plugin-cc repository enables seamless migration of conversation history from Claude Code to OpenAI Codex. When developers need to switch AI coding assistants mid-session, the session transfer feature preserves context by converting Claude's .jsonl transcripts into Codex-compatible thread formats.
The Session Transfer Pipeline
The transfer operates through a six-step pipeline defined in plugins/codex/scripts/codex-companion.mjs and its library dependencies.
Step 1: Path Resolution and Validation
The resolveClaudeSessionPath() function in plugins/codex/scripts/lib/claude-session-transfer.mjs handles the initial input validation. It expands ~ and relative paths, ensures the file ends with .jsonl, and verifies the path resides inside the Claude projects directory ($HOME/.claude/projects). This security boundary prevents arbitrary file access.
Step 2: Import Request to Codex
The importExternalAgentSession() function in plugins/codex/scripts/lib/codex.mjs manages the core migration logic. It first validates that the Codex CLI is installed, then calls requestExternalAgentSessionImport() with a migration request containing the validated transcript path. This communicates with the Codex app-server via the externalAgentSessionImport RPC method.
Step 3: Thread ID Extraction
After the app-server processes the import, the function extracts the newly created thread ID via importedThreadIdForSource(). If the ID cannot be found, the function raises a detailed error including any stderr from Codex to aid debugging.
Step 4: Payload Construction
The executeTransfer() function in plugins/codex/scripts/codex-companion.mjs wraps the thread ID, a resume command (codex resume <thread-id>), and the original source path into a structured payload object. This prepares the data for user-facing output.
Step 5: Result Rendering
The renderTransferResult() function builds a human-readable message that prints to the console, confirming the successful transfer and providing the resume command.
Step 6: Progress and Error Handling
Throughout the pipeline, emitProgress() reports status updates such as "Importing Claude session" and "Claude session imported". Errors are enriched with actionable hints, such as updating Codex if the RPC method externalAgentSessionImport is missing.
Code Implementation Examples
Manually Invoking the Transfer Logic
You can programmatically trigger the same transfer logic used by the CLI:
import { resolveClaudeSessionPath } from "./plugins/codex/scripts/lib/claude-session-transfer.mjs";
import { importExternalAgentSession } from "./plugins/codex/scripts/lib/codex.mjs";
async function transferClaudeSession(cwd, source) {
// Step 1 – Resolve & validate the transcript path
const transcriptPath = resolveClaudeSessionPath(cwd, { source });
// Step 2 – Ask Codex to import the transcript
const { threadId } = await importExternalAgentSession(cwd, {
sourcePath: transcriptPath,
// optional progress callback
onProgress: (msg, state) => console.log(`[${state}] ${msg}`)
});
// Step 3 – Return a ready-to-run resume command
return {
threadId,
resumeCommand: `codex resume ${threadId}`
};
}
// Example usage
(async () => {
const cwd = process.cwd();
const result = await transferClaudeSession(cwd, "~/myproject/session.jsonl");
console.log(`✅ Imported, resume with: ${result.resumeCommand}`);
})();
CLI Command Structure
When users run the CLI command:
codex transfer --source ~/myproject/session.jsonl
The command dispatcher calls executeTransfer() in plugins/codex/scripts/codex-companion.mjs:
await executeTransfer(process.cwd(), { source: "~/myproject/session.jsonl" });
This internal call triggers the full pipeline from path resolution through result rendering.
Key Source Files
The session transfer feature spans three main files in the openai/codex-plugin-cc repository:
-
plugins/codex/scripts/lib/claude-session-transfer.mjs: Implements path resolution and safety checks for Claude transcripts, including theresolveClaudeSessionPath()function. -
plugins/codex/scripts/lib/codex.mjs: Contains the core Codex app-server client implementingimportExternalAgentSession(),requestExternalAgentSessionImport(), andimportedThreadIdForSource(). -
plugins/codex/scripts/codex-companion.mjs: Serves as the CLI entry point, orchestrating the transfer flow throughexecuteTransfer()andrenderTransferResult().
Summary
- Session transfer converts Claude Code
.jsonltranscripts into Codex threads using thecodex transfercommand. - Path validation ensures transcripts reside within
$HOME/.claude/projectsand use the.jsonlextension. - RPC communication occurs via
externalAgentSessionImportto the Codex app-server. - Resume capability generates a
codex resume <thread-id>command for immediate continuation. - Error handling provides actionable hints for common failures like missing CLI installations or outdated Codex versions.
Frequently Asked Questions
What file format does Claude Code use for sessions?
Claude Code stores session transcripts as .jsonl (JSON Lines) files. The resolveClaudeSessionPath() function explicitly validates this extension, rejecting any files that do not end with .jsonl to ensure proper parsing by the transfer pipeline.
How does the transfer validate session file security?
The resolveClaudeSessionPath() function enforces a security boundary by verifying that the transcript path resides inside the Claude projects directory ($HOME/.claude/projects). This prevents the transfer command from accessing arbitrary files outside the expected scope, ensuring only legitimate Claude sessions are imported.
What happens if the Codex app-server lacks the import RPC method?
If the externalAgentSessionImport RPC method is missing, the error handler in plugins/codex/scripts/lib/codex.mjs returns a detailed message suggesting the user update their Codex CLI installation. This check occurs during the importExternalAgentSession() validation phase before attempting the actual import.
Can I programmatically resume a transferred session?
Yes. After successful transfer, the executeTransfer() function returns a thread ID that can be used with the codex resume <thread-id> command. The programmatic API also returns this ID via importExternalAgentSession(), allowing automated workflows to immediately resume sessions without manual CLI interaction.
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 →