How Session Transfer Between Claude Code and Codex Works: A Technical Deep Dive

The codex transfer command imports a Claude Code session (stored as a .jsonl transcript) into a new Codex thread by validating the source path, calling the Codex app-server via RPC, and returning a resumable thread ID.

Session transfer between Claude Code and Codex enables developers to migrate conversation history from Claude Code into OpenAI's Codex CLI. This mechanism, implemented in the openai/codex-plugin-cc repository, converts Claude's JSONL transcript format into a Codex-compatible thread through a six-step validation and import pipeline.

The Session Transfer Pipeline

The transfer operation follows a strict sequence defined across three core modules. Each step enforces security constraints and provides granular error reporting.

Step 1: Resolving the Claude Transcript Path

The process begins in plugins/codex/scripts/lib/claude-session-transfer.mjs with the resolveClaudeSessionPath() function. This utility expands tildes (~) and relative paths, validates that the file uses the .jsonl extension, and critically restricts access to the Claude projects directory ($HOME/.claude/projects). Paths outside this scope are rejected to prevent arbitrary file access.

Step 2: Importing the Session into Codex

Once validated, the transcript moves to the Codex importer in plugins/codex/scripts/lib/codex.mjs. The importExternalAgentSession() function first verifies that the Codex CLI is installed, then constructs a migration request containing the transcript path. It transmits this via requestExternalAgentSessionImport() to the Codex app-server's RPC interface.

Step 3: Extracting the Thread ID

After the app-server processes the import, the code calls importedThreadIdForSource() to extract the newly created thread identifier from the RPC response. If the ID is missing, the function raises a detailed error that includes stderr output from Codex, enabling rapid debugging of version mismatches or protocol errors.

Step 4: Building the Transfer Payload

The executeTransfer() function in plugins/codex/scripts/codex-companion.mjs orchestrates the high-level workflow. It wraps the thread ID, generates a resume command (codex resume <thread-id>), and packages these with the original source path into a structured payload object for consumption by the CLI renderer.

Step 5: Rendering the Result

renderTransferResult() (also in codex-companion.mjs) transforms the payload into human-readable console output. This includes the Codex session ID and the exact command needed to resume the conversation in the new environment.

Step 6: Progress Reporting and Error Handling

Throughout the pipeline, emitProgress() broadcasts status updates such as "Importing Claude session" and "Claude session imported". Error handlers enrich failures with actionable hints—for example, prompting users to update Codex if the RPC method externalAgentSessionImport is unavailable in their installed version.

End-to-End Execution Flow

When a user executes the transfer command, the following chain occurs:

  1. The CLI receives codex transfer --source path/to/session.jsonl
  2. executeTransfer() invokes resolveClaudeSessionPath() to validate the transcript location
  3. importExternalAgentSession() transmits the migration request to the Codex app-server
  4. The server creates a new thread from the transcript and returns the thread ID
  5. The companion script formats the result and outputs:

Transferred the Claude session into a Codex thread with visible turn history.
Codex session ID: 12345-abcde
Resume in Codex: codex resume 12345-abcde

If validation fails—such as when the transcript lies outside $HOME/.claude/projects or the Codex version lacks RPC support—the process halts immediately with a descriptive error message.

Implementing Session Transfer Programmatically

You can invoke the transfer logic directly from Node.js without using 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 and validate the transcript path
  const transcriptPath = resolveClaudeSessionPath(cwd, { source });

  // Step 2 – Request import from the Codex app-server
  const { threadId } = await importExternalAgentSession(cwd, {
    sourcePath: transcriptPath,
    onProgress: (msg, state) => console.log(`[${state}] ${msg}`)
  });

  // Step 3 – Return resumable session details
  return {
    threadId,
    resumeCommand: `codex resume ${threadId}`
  };
}

// Example invocation
(async () => {
  const cwd = process.cwd();
  const result = await transferClaudeSession(cwd, "~/myproject/session.jsonl");
  console.log(`✅ Imported, resume with: ${result.resumeCommand}`);
})();

For command-line usage, the implementation simplifies to:

codex transfer --source ~/myproject/session.jsonl

This executes the same internal flow through executeTransfer() in plugins/codex/scripts/codex-companion.mjs.

Key Source Files and Their Roles

  • plugins/codex/scripts/lib/claude-session-transfer.mjs: Contains path resolution logic and security checks for Claude transcript files.

  • plugins/codex/scripts/lib/codex.mjs: Implements the Codex app-server client, including importExternalAgentSession() and RPC communication handlers.

  • plugins/codex/scripts/codex-companion.mjs: CLI entry point that orchestrates the transfer flow through executeTransfer() and renderTransferResult().

  • plugins/codex/commands/transfer.md: User-facing documentation describing the codex transfer command syntax and options.

Summary

  • Session transfer migrates Claude Code .jsonl transcripts into Codex threads via the codex transfer command.
  • resolveClaudeSessionPath() enforces that source files reside within $HOME/.claude/projects for security.
  • importExternalAgentSession() handles RPC communication with the Codex app-server to create the new thread.
  • The pipeline returns a thread ID and generates a codex resume <thread-id> command for immediate continuation.
  • Comprehensive error handling provides actionable fixes for path violations, missing CLI installations, or outdated Codex versions.

Frequently Asked Questions

What file format does the session transfer require?

The mechanism requires a .jsonl transcript file generated by Claude Code. The resolveClaudeSessionPath() function explicitly checks for this extension and rejects other formats. Additionally, the file must reside within the $HOME/.claude/projects directory to prevent unauthorized access to arbitrary system files.

How does the transfer handle Codex version incompatibility?

The importExternalAgentSession() function validates the Codex CLI installation before attempting RPC communication. If the app-server lacks the externalAgentSessionImport method—common in older Codex versions—the error handler emits a specific message advising users to update their Codex CLI installation, often including stderr output from the failed RPC call for diagnostic clarity.

Can I resume a transferred session without using the CLI?

While the transfer process itself requires the CLI to validate and import the transcript, the output provides a standard Codex resume command (codex resume <thread-id>). You can programmatically extract this thread ID from the executeTransfer() return value and use it with any Codex client that supports thread resumption, as the thread persists on the Codex app-server independently of the import mechanism.

What happens if the transcript path contains relative directories or tildes?

The resolveClaudeSessionPath() function automatically expands tildes (~) to the user's home directory and resolves relative paths against the current working directory. However, after normalization, the absolute path must still fall within $HOME/.claude/projects; otherwise, the function throws a validation error before any import attempt occurs.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →