# How the OpenAI Codex Plugin Interacts with the Local Codex CLI: Architecture and Implementation

> Discover how the OpenAI Codex plugin interacts with the local Codex CLI using a JSON-RPC client. Learn about its architecture, process spawning, and socket connections for seamless integration.

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

---

**The OpenAI Codex plugin communicates with the local Codex CLI through a JSON-RPC client that either spawns a direct `codex app-server` process or connects via a shared broker socket, wrapping low-level binary checks and message handling into high-level JavaScript APIs.**

The `openai/codex-plugin-cc` repository implements a thin JavaScript wrapper that bridges the gap between the Codex plugin interface and the locally-installed `codex` binary. This architecture enables seamless integration by detecting the CLI, managing process lifecycle, and translating between high-level API calls and low-level JSON-RPC messages.

## Architecture Overview

The interaction model consists of three distinct layers that handle everything from binary verification to result aggregation. Understanding this stack is essential for debugging connection issues or extending the plugin's functionality.

1. **Availability Verification** – Validates that the `codex` binary exists and that the `app-server` subcommand is functional
2. **Transport Layer** – Manages JSON-RPC communication through either direct process spawning or broker-mediated connections
3. **Orchestration Layer** – Provides high-level functions like `runAppServerTurn()` that coordinate thread creation, message sending, and progress capture

## Binary Detection and Availability Checks

Before establishing any connection, the plugin verifies the local environment through `getCodexAvailability()` in `plugins/codex/scripts/lib/codex.mjs` (lines 886-894). This function relies on `binaryAvailable("codex", …)` defined in `plugins/codex/scripts/lib/process.mjs` to confirm the binary exists in the system PATH and responds to version checks.

If the binary is missing or the `app-server` subcommand is unsupported, the plugin returns early with clear error states, preventing failed spawn attempts that would otherwise clutter the logs.

## JSON-RPC Communication Layer

All communication with the Codex CLI happens through the `CodexAppServerClient` class defined in `plugins/codex/scripts/lib/app-server.mjs`. The client abstracts the transport mechanism, allowing the rest of the codebase to remain agnostic about whether it's talking to a direct process or a shared broker session.

### Direct Process Spawning

When no shared broker exists, `CodexAppServerClient.connect()` instantiates a `SpawnedCodexAppServerClient` that launches the CLI using Node.js `spawn`:

```javascript
spawn("codex", ["app-server"], …)

```

As implemented in `app-server.mjs` (lines 89-97), this creates a dedicated subprocess that listens for JSON-RPC requests on stdio. The `AppServerClientBase` class then parses incoming lines, matches request IDs, and forwards notifications to registered handlers.

### Broker-Based Connections

For shared sessions, the client connects to a `BrokerCodexAppServerClient` instead. The broker lifecycle is managed by `plugins/codex/scripts/lib/broker-lifecycle.mjs`, which creates platform-specific endpoints via `createBrokerEndpoint()` in `plugins/codex/scripts/lib/broker-endpoint.mjs`. On Unix systems, this creates a Unix socket; on Windows, it uses a named pipe.

This approach allows multiple plugin operations to share a single Codex CLI process, reducing memory overhead and maintaining conversation state across separate API calls.

## High-Level API Operations

The public API exposed in `plugins/codex/scripts/lib/codex.mjs` orchestrates complex workflows through four primary functions: `runAppServerTurn()`, `runAppServerReview()`, `importExternalAgentSession()`, and `getCodexAuthStatus()`.

### Executing a Turn

The `runAppServerTurn()` function handles the complete lifecycle of a single prompt execution:

1. Calls `withAppServer()` or `withDirectAppServer()` to obtain a `CodexAppServerClient`
2. Sends a `thread/start` request to create conversation context
3. Issues a `turn/start` request containing the prompt, model selection (e.g., `gpt-4o-mini`), and sandbox mode (`read-only` or `full`)
4. Registers a notification handler that receives JSON-RPC notifications like `item/started` and `item/completed`

While the remote operation runs, the helper `captureTurn()` (lines 559-610 in `codex.mjs`) builds a `TurnCaptureState` object that aggregates progress updates, file changes, command executions, reasoning summaries, and final answer detection.

### Running Reviews

`runAppServerReview()` follows a similar pattern but targets the `review/start` endpoint. This function accepts a target specification—either a file path or directory—and returns structured review output suitable for inline display or export. The review thread maintains isolation from task threads, preventing context contamination between coding and review operations.

### Importing External Sessions

The `importExternalAgentSession()` function enables migration from other AI assistants (such as Claude) into the Codex ecosystem. It accepts a source path pointing to an external session file, transforms the conversation history into Codex's native format, and returns a new `threadId` that preserves the context for continued work.

## Notification Handling and State Aggregation

Real-time progress reporting relies on the `emitProgress()` and `emitLogEvent()` functions that translate Codex notifications into a friendly progress API. The `TurnCaptureState` class collects these events into a structured result containing:

- **Status**: Completion state (success, error, or cancelled)
- **Thread and Turn IDs**: References for subsequent operations
- **Final Message**: The aggregated response content
- **Metadata**: File modifications, executed commands, and reasoning traces

This aggregation allows the plugin to return a single resolved object while the underlying transport handles streaming, partial results, and asynchronous notifications.

## Practical Implementation Examples

### Running a Single Codex Turn

```javascript
import { runAppServerTurn } from
  'plugins/codex/scripts/lib/codex.mjs';

// Execute a prompt in the current working directory
const result = await runAppServerTurn(process.cwd(), {
  prompt: 'Write a function that returns the nth Fibonacci number.',
  model: 'gpt-4o-mini',
  sandbox: 'read-only',
  persistThread: false,
  onProgress: (msg) => console.log('[Codex]', msg)
});

console.log('Turn status:', result.status);
console.log('Final message:', result.finalMessage);

```

### Starting a Review Thread

```javascript
import { runAppServerReview } from
  'plugins/codex/scripts/lib/codex.mjs';

const review = await runAppServerReview(process.cwd(), {
  target: { type: 'file', path: 'src/util.js' },
  delivery: 'inline',
  onProgress: (msg) => console.log('[Review]', msg)
});

console.log('Review output:', review.reviewText);

```

### Importing a Claude Session

```javascript
import { importExternalAgentSession } from
  'plugins/codex/scripts/lib/codex.mjs';

const importResult = await importExternalAgentSession(process.cwd(), {
  sourcePath: '/home/user/.claude/session.json',
  onProgress: (msg) => console.log('[Import]', msg)
});

console.log('Imported thread ID:', importResult.threadId);

```

## Summary

- **Binary Verification**: The plugin validates the `codex` binary through `getCodexAvailability()` and `binaryAvailable()` before attempting any operations.

- **Transport Flexibility**: `CodexAppServerClient.connect()` chooses between direct process spawning (`SpawnedCodexAppServerClient`) and broker connections (`BrokerCodexAppServerClient`) based on session sharing requirements.

- **JSON-RPC Protocol**: All communication uses JSON-RPC over stdio (direct) or platform-specific sockets (broker), with `AppServerClientBase` handling message parsing and notification routing.

- **High-Level Orchestration**: Functions like `runAppServerTurn()` abstract the complexity of thread management, progress capture, and result aggregation through `TurnCaptureState`.

- **Extensible Architecture**: The separation between transport (`app-server.mjs`), lifecycle management (`broker-lifecycle.mjs`), and API surface (`codex.mjs`) allows for easy extension and testing.

## Frequently Asked Questions

### How does the plugin locate the Codex CLI binary?

The plugin uses `binaryAvailable("codex", …)` in `plugins/codex/scripts/lib/process.mjs` to check PATH availability, followed by `getCodexAvailability()` in `codex.mjs` to verify the `app-server` subcommand functionality. If the binary is missing, the plugin returns an availability error before attempting to spawn any processes.

### What is the difference between direct and broker connections?

**Direct connections** spawn a new `codex app-server` process for each operation via `SpawnedCodexAppServerClient`, providing complete isolation. **Broker connections** use `BrokerCodexAppServerClient` to connect to a shared socket managed by `broker-lifecycle.mjs`, allowing multiple operations to share a single CLI process and maintain persistent state across calls.

### How does the plugin handle real-time progress updates?

The plugin registers notification handlers that receive JSON-RPC messages (e.g., `item/started`, `item/completed`) from the CLI. The `captureTurn()` helper aggregates these into a `TurnCaptureState` object while `emitProgress()` translates internal notifications into user-facing callbacks, enabling real-time streaming of file changes, command executions, and reasoning steps.

### Can the plugin import conversations from other AI assistants?

Yes, through `importExternalAgentSession()` in `codex.mjs`. This function reads external session files (such as Claude exports), transforms the conversation history into Codex's native thread format, and returns a new `threadId` that allows continued work within the Codex ecosystem while preserving the original context.