# How Session Import and External Agent Transfer Work in Codex

> Learn how Codex imports Claude chat sessions and handles external agent transfers by validating paths, creating migration payloads, and confirming completion.

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

---

**Codex imports Claude chat sessions by validating transcript paths, constructing JSON-RPC migration payloads, and waiting for the app-server to confirm completion via a notification handler.**

The openai/codex-plugin-cc repository enables seamless migration of Claude conversation history into Codex through a structured three-stage process. This session import and external agent transfer functionality converts local `.jsonl` transcript files into fully-fledged Codex threads while preserving the complete conversation context.

## Stage 1: Locating the Claude Transcript

The process begins with **path validation** to ensure security and file integrity. The `resolveClaudeSessionPath` function in `plugins/codex/scripts/lib/claude-session-transfer.mjs` (lines 20‑44) accepts a user-provided path and enforces three critical constraints:

- The file must exist and use the `.jsonl` extension
- The path must reside within the `~/.claude/projects` directory tree
- The function returns an absolute path for downstream processing

If validation fails, the function raises descriptive errors indicating whether the file is missing, has an incorrect extension, or lives outside the permitted directory.

```javascript
// plugins/codex/scripts/lib/claude-session-transfer.mjs
export function resolveClaudeSessionPath(cwd, options = {}) {
  // Validates path is inside ~/.claude/projects and ends with .jsonl
  // Returns absolute path or throws descriptive error
}

```

## Stage 2: Creating the Migration Payload

Once the transcript path is verified, `externalAgentSessionMigration` in `plugins/codex/scripts/lib/codex.mjs` (lines 81‑99) constructs a JSON-RPC-compatible migration object. This payload describes the session to be transferred using the `SESSIONS` item type:

```javascript
// plugins/codex/scripts/lib/codex.mjs
function externalAgentSessionMigration(sourcePath, cwd) {
  return {
    migrationItems: [{
      itemType: "SESSIONS",
      description: `Transfer Claude session ${path.basename(sourcePath)}`,
      cwd: null,
      details: {
        plugins: [],
        sessions: [{ path: sourcePath, cwd, title: null }],
        mcpServers: [],
        hooks: [],
        subagents: [],
        commands: []
      }
    }]
  };
}

```

The migration object includes the source file path, working directory context, and empty arrays for related resources like MCP servers and subagents that may be configured later.

## Stage 3: Requesting the Import

The `requestExternalAgentSessionImport` function (lines 101‑130 in the same file) handles the actual RPC communication. It temporarily overrides the client's notification handler to listen for the `externalAgentConfig/import/completed` event while sending the migration payload via the `externalAgentConfig/import` method:

```javascript
// plugins/codex/scripts/lib/codex.mjs
async function requestExternalAgentSessionImport(client, params) {
  const previousHandler = client.notificationHandler;
  // Set up temporary handler for completion notification
  client.setNotificationHandler((msg) => {
    if (msg.method === "externalAgentConfig/import/completed") {
      resolveCompleted();
      return;
    }
    previousHandler?.(msg);
  });
  
  await client.request("externalAgentConfig/import", params);
  await completed; // Resolves when server confirms import
}

```

Upon successful import, Codex creates a ledger entry in [`external_agent_session_imports.json`](https://github.com/openai/codex-plugin-cc/blob/main/external_agent_session_imports.json) to record the transfer for later lookups.

## Running the Transfer Command

The **transfer command** ([`plugins/codex/commands/transfer.md`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/commands/transfer.md)) wires these three functions together into a user-friendly CLI workflow. You can invoke it with an explicit source path:

```bash
codex transfer --source ~/.claude/projects/my-chat.jsonl

```

Alternatively, set the `CODEX_COMPANION_TRANSCRIPT_PATH` environment variable to skip the flag:

```bash
export CODEX_COMPANION_TRANSCRIPT_PATH=~/.claude/projects/chat.jsonl
codex transfer

```

When the app-server supports the import capability, this creates a new Codex thread containing the complete Claude conversation history.

## Programmatic Implementation

For custom integrations, import the helper functions directly and manage the app-server connection manually:

```javascript
import { resolveClaudeSessionPath } from "./plugins/codex/scripts/lib/claude-session-transfer.mjs";
import { externalAgentSessionMigration, requestExternalAgentSessionImport } from "./plugins/codex/scripts/lib/codex.mjs";
import { CodexAppServerClient } from "./plugins/codex/scripts/lib/app-server.mjs";

async function importClaudeSession(cwd, source) {
  const sourcePath = resolveClaudeSessionPath(cwd, { source });
  const migration = externalAgentSessionMigration(sourcePath, cwd);
  const client = await CodexAppServerClient.connect(cwd);
  
  await requestExternalAgentSessionImport(client, migration);
  await client.close();
}

// Example usage
await importClaudeSession(process.cwd(), "~/.claude/projects/my-chat.jsonl");

```

This approach gives you full control over the client lifecycle and error handling while using the same validation and migration logic as the CLI.

## Summary

- **Path validation** occurs via `resolveClaudeSessionPath` in `plugins/codex/scripts/lib/claude-session-transfer.mjs`, which restricts imports to `.jsonl` files within `~/.claude/projects`.
- **Payload construction** uses `externalAgentSessionMigration` to create JSON-RPC compatible migration objects with `itemType: "SESSIONS"`.
- **RPC execution** happens through `requestExternalAgentSessionImport`, which waits for the `externalAgentConfig/import/completed` notification before resolving.
- **Ledger recording** stores import history in [`external_agent_session_imports.json`](https://github.com/openai/codex-plugin-cc/blob/main/external_agent_session_imports.json) for status tracking.
- **CLI access** is provided by the `codex transfer` command, which orchestrates the entire workflow.

## Frequently Asked Questions

### What file format does Codex require for session imports?

Codex strictly requires **JSON Lines (`.jsonl`)** files that contain Claude transcript data. The `resolveClaudeSessionPath` function enforces this extension and rejects any other file types before processing begins.

### How does Codex confirm that an import succeeded?

The system uses an **asynchronous notification pattern**. After sending the `externalAgentConfig/import` RPC request, `requestExternalAgentSessionImport` waits for the server to emit an `externalAgentConfig/import/completed` notification. The function rejects the promise if this confirmation times out or if the server returns an error.

### Can I import sessions from agents other than Claude?

The current implementation in `openai/codex-plugin-cc` specifically targets **Claude transcripts** stored in `~/.claude/projects`. While the migration payload structure in `externalAgentSessionMigration` is generic enough to support other sources, the path validation logic and `SESSIONS` item type are currently optimized for Claude conversation files.

### Where does Codex store the import history?

After a successful transfer, Codex records the operation in **[`external_agent_session_imports.json`](https://github.com/openai/codex-plugin-cc/blob/main/external_agent_session_imports.json)**, which functions as a ledger for tracking which sessions have been imported. This enables the `codex status` command to display imported sessions and prevent duplicate transfers.