# How Session Transfer Works Between Claude Code and Codex

> Learn how session transfer converts Claude Code .jsonl files to Codex threads using the codex transfer command. Understand the RPC calls and resumable thread ID return.

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

---

**Session transfer between Claude Code and Codex converts Claude's `.jsonl` transcript files into Codex threads via the `codex transfer` command, which validates the source path, calls the Codex app-server's `externalAgentSessionImport` RPC method, and returns a resumable thread ID.**

The `openai/codex-plugin-cc` repository enables seamless migration of conversation history from Claude Code to OpenAI Codex through a structured pipeline. When developers need to switch between AI coding assistants without losing context, the session transfer feature imports Claude's transcript format directly into Codex's thread architecture. This process involves path resolution, RPC communication with the Codex app-server, and comprehensive error handling to ensure reliable migration of conversation history.

## The Session Transfer Pipeline

The transfer process follows a strictly ordered pipeline that validates, imports, and formats the session data.

### Step 1: Path Resolution and Security Validation

The `resolveClaudeSessionPath()` function in `plugins/codex/scripts/lib/claude-session-transfer.mjs` (lines 20-44) performs initial validation. It expands `~` and relative paths to absolute paths, verifies the file ends with a `.jsonl` extension, and ensures the transcript resides within the Claude projects directory at `$HOME/.claude/projects`. This security boundary prevents unauthorized access to files outside the Claude Code workspace.

### Step 2: App-Server Import Request

The `importExternalAgentSession()` function in `plugins/codex/scripts/lib/codex.mjs` (lines 58-70) handles the core migration logic. It first validates that the Codex CLI is installed, then constructs a migration request containing the transcript path. The function calls `requestExternalAgentSessionImport()`, which communicates with the Codex app-server via the `externalAgentSessionImport` RPC method to create a new thread from the transcript.

### Step 3: Thread ID Extraction

After the app-server processes the import, the `importedThreadIdForSource()` function (lines 80-88 in `plugins/codex/scripts/lib/codex.mjs`) extracts the newly created thread ID from the response. If the ID cannot be located, the function raises a detailed error that includes stderr output from Codex, providing actionable debugging information.

### Step 4: Result Formatting and Output

The `executeTransfer()` function in `plugins/codex/scripts/codex-companion.mjs` (lines 25-38) orchestrates the final output. It wraps the thread ID, constructs the resume command (`codex resume <thread-id>`), and includes the original source path in a payload object. The `renderTransferResult()` function (lines 16-22) then formats this data into a human-readable console message.

### Progress Reporting and Error Handling

Throughout the pipeline, `emitProgress()` reports status updates such as "Importing Claude session" and "Claude session imported". Error handling includes specific checks for missing transcripts, out-of-scope paths, and outdated Codex versions lacking the `externalAgentSessionImport` RPC method.

## Implementation Examples

### Programmatic Session Transfer

You can invoke the transfer logic programmatically using the same functions that power 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 & validate the transcript path
  const transcriptPath = resolveClaudeSessionPath(cwd, { source });

  // Step 2 – Ask Codex to import the transcript
  const { threadId } = await importExternalAgentSession(cwd, {
    sourcePath: transcriptPath,
    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

Behind the scenes, the `codex transfer` command executes the same flow:

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

```

This invokes `executeTransfer()` in `plugins/codex/scripts/codex-companion.mjs` with the provided source path.

## Key Source Files

| File | Purpose | Key Functions |
|------|---------|---------------|
| `plugins/codex/scripts/lib/claude-session-transfer.mjs` | Path resolution and security validation | `resolveClaudeSessionPath()` |
| `plugins/codex/scripts/lib/codex.mjs` | Codex app-server client and RPC handling | `importExternalAgentSession()`, `importedThreadIdForSource()` |
| `plugins/codex/scripts/codex-companion.mjs` | CLI orchestration and output formatting | `executeTransfer()`, `renderTransferResult()` |

## Summary

- **Session transfer** converts Claude Code `.jsonl` transcripts into Codex threads via the `codex transfer` command.
- **Path validation** ensures transcripts reside within `$HOME/.claude/projects` and end with the `.jsonl` extension.
- **RPC communication** uses the `externalAgentSessionImport` method to create threads in the Codex app-server.
- **Thread resumption** generates a `codex resume <thread-id>` command for immediate continuation of the conversation.
- **Error handling** provides specific guidance for missing files, permission errors, and version incompatibility.

## Frequently Asked Questions

### What file format does Claude Code use for sessions?

Claude Code stores sessions as **`.jsonl` (JSON Lines)** transcript files located in `$HOME/.claude/projects`. The transfer pipeline specifically validates this extension and directory structure before processing.

### How does the transfer mechanism communicate with Codex?

The `importExternalAgentSession()` function in `plugins/codex/scripts/lib/codex.mjs` communicates with the **Codex app-server** via the `externalAgentSessionImport` RPC method. This requires a compatible Codex CLI version; older versions will trigger an error prompting the user to update.

### Can I resume a transferred session immediately?

Yes. Upon successful transfer, the CLI outputs a `codex resume <thread-id>` command that allows immediate continuation of the conversation in Codex with full access to the imported conversation history.

### What happens if the transcript path is outside the Claude projects directory?

The `resolveClaudeSessionPath()` function enforces a security boundary that rejects paths outside `$HOME/.claude/projects`. This prevents unauthorized file access and ensures only legitimate Claude Code transcripts are imported.