How `importExternalAgentSession` Works for Session Transfer in OpenAI Codex Plugin

importExternalAgentSession validates and resolves Claude Code transcript paths before converting them into resumable Codex threads.

Session transfer in the OpenAI Codex plugin relies on a dedicated helper that safely imports external agent transcripts. The resolveClaudeSessionPath function in plugins/codex/scripts/lib/claude-session-transfer.mjs serves as the core implementation for this importExternalAgentSession workflow, invoked by the codex transfer CLI command defined in plugins/codex/commands/transfer.md.

Source Determination: Flags vs. Environment

The function first establishes where to find the Claude transcript. It checks for a --source <path> CLI flag; if absent, it falls back to the CODEX_COMPANION_TRANSCRIPT_PATH environment variable. If neither is provided, the function throws an error immediately.

This dual-source approach gives users flexibility: one-off transfers use explicit flags, while repeated workflows benefit from shell-level configuration.

Path Resolution and Normalization

Once a source is identified, the function handles platform-specific path conventions:

  • Tilde expansion: Converts ~ and ~/... to absolute home-directory paths via resolveUserPath (lines 10–18)
  • Absolute path enforcement: Calls ensureAbsolutePath from plugins/codex/scripts/lib/fs.mjs to eliminate relative ambiguity
// From claude-session-transfer.mjs, lines 10-18
function resolveUserPath(inputPath) {
  if (inputPath.startsWith('~/')) {
    return path.join(os.homedir(), inputPath.slice(2));
  }
  return inputPath;
}

File Type Validation

The function enforces strict format compliance. Using path.extname, it verifies the target is a .jsonl file—the structured JSON Lines format Claude uses for transcript storage. Non-compliant paths are rejected before any filesystem operations occur.

Security Sandboxing via Canonicalization

Two fs.realpathSync calls establish a trusted boundary:

  1. Resolves the transcript file to its canonical absolute path
  2. Resolves Claude's projects directory at ~/.claude/projects
// From claude-session-transfer.mjs, lines 34-36
const realSource = fs.realpathSync(source);
const realProjectsDir = fs.realpathSync(
  path.join(os.homedir(), '.claude', 'projects')
);

The function then computes the relative path between these canonical locations. If the transcript lies outside ~/.claude/projects or is the directory itself, it throws a security error. This prevents path-traversal attacks and confines imports to Claude's official project storage.

// From claude-session-transfer.mjs, lines 39-42
const relative = path.relative(realProjectsDir, realSource);
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
  throw new Error('Transcript must be inside ~/.claude/projects');
}

Integration with Session Transfer Pipeline

Upon successful validation, resolveClaudeSessionPath returns the canonical absolute path. This value is consumed by codex-companion.mjs, the CLI entry point for the transfer sub-command. The companion script:

  1. Reads the .jsonl transcript
  2. Creates a resumable Codex thread record
  3. Outputs a codex resume <session-id> command for later use

# Explicit source flag

codex transfer --source ~/Claude/projects/my-app/session.jsonl

# Environment variable approach

export CODEX_COMPANION_TRANSCRIPT_PATH=~/Claude/projects/my-app/session.jsonl
codex transfer

Both invocations execute equivalent logic:

import { resolveClaudeSessionPath } from "./lib/claude-session-transfer.mjs";

const transcriptPath = resolveClaudeSessionPath(process.cwd(), { source: cliSource });
const transcript = JSON.parse(fs.readFileSync(transcriptPath, "utf8"));
const sessionId = await createResumableCodexThread(transcript);
console.log(`codex resume ${sessionId}`);

Key Design Benefits

  • Deterministic identification: Symbolic links and relative notation resolve to consistent paths
  • Minimal attack surface: Directory-confinement rule blocks arbitrary file imports
  • Zero-copy workflow: Transcripts remain in place; only metadata crosses into Codex

Summary

  • The importExternalAgentSession workflow centers on resolveClaudeSessionPath in plugins/codex/scripts/lib/claude-session-transfer.mjs
  • Source resolution prefers --source flags, then CODEX_COMPANION_TRANSCRIPT_PATH environment variable
  • Path normalization handles tilde expansion and enforces absolute paths
  • .jsonl file type validation ensures format compatibility with Claude transcripts
  • Security sandboxing via fs.realpathSync and path.relative restricts imports to ~/.claude/projects
  • Validated paths feed into codex-companion.mjs to create resumable Codex threads

Frequently Asked Questions

What file format does importExternalAgentSession require?

The function strictly requires .jsonl (JSON Lines) files—the format Claude uses for transcript storage. This is enforced via path.extname validation before any content processing occurs.

Why does the function reject transcripts outside ~/.claude/projects?

The path.relative security check prevents path-traversal attacks and ensures only legitimate Claude project files can be imported. This sandboxing protects against accidental or malicious inclusion of unrelated files from the filesystem.

Can I transfer a session without specifying --source every time?

Yes. Set the CODEX_COMPANION_TRANSCRIPT_PATH environment variable to your default transcript location. The function checks this variable when no --source flag is provided, enabling streamlined repeated transfers.

fs.realpathSync canonicalizes all paths, resolving symbolic links to their actual locations before the security boundary check. This ensures the sandbox validation operates on true filesystem locations, not potentially misleading symlink paths.

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 →