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

> Explore the technical deep dive into session transfer between Claude Code and Codex. Learn how the codex transfer command imports sessions and returns a resumable thread ID.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: deep-dive
- Published: 2026-07-07

---

**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:

```javascript
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:

```bash
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`](https://github.com/openai/codex-plugin-cc/blob/main/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.