# How the Codex Plugin Communicates with Codex: WebSocket RPC and Thread Management

> Discover how the Codex plugin communicates with Codex using WebSocket RPC and thread management. Learn about the broker, CodexAppServerClient, and real-time notifications.

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

---

**The Codex plugin communicates with Codex through a WebSocket-based JSON-RPC protocol mediated by a broker, using the `CodexAppServerClient` class to manage thread lifecycles and stream real-time notifications.**

The **openai/codex-plugin-cc** repository implements a sophisticated client-server architecture that transforms the plugin into a "Companion" for running Codex sessions. Understanding how the Codex plugin communicates with Codex reveals a resilient system built around WebSocket connections, persistent thread management, and streaming progress updates.

## Connection Setup and Authentication

Communication begins in `plugins/codex/scripts/lib/app-server.mjs` with the **`CodexAppServerClient`** class. This client establishes a **WebSocket** connection (with HTTP fallback) to a broker endpoint defined by the environment variable `BROKER_ENDPOINT_ENV`.

The authentication flow uses the service name `claude_code_codex_plugin` and performs a JSON-RPC handshake before any commands are exchanged. The `broker-lifecycle.mjs` module handles loading or creating the broker session and injecting the endpoint URL, while `process.mjs` verifies that the Codex binary is available (`binaryAvailable`) before attempting any RPC calls.

## Thread Lifecycle Management

Once connected, the plugin manages Codex interactions through a thread-based model defined in `plugins/codex/scripts/lib/codex.mjs`. When a user invokes commands like `codex review` or `codex result`, the plugin constructs **`ThreadStartParams`** or **`ThreadResumeParams`** objects.

- **`thread.start`**: Creates a new thread on the Codex side for fresh conversations.
- **`thread.resume`**: Reconnects to an existing thread using a locally stored thread ID.

The client methods `sendThreadStart()` and `sendThreadResume()` handle these transitions, while `sendTurnInput()` feeds user prompts into the active thread.

## Real-Time Message Streaming

While Codex processes requests, the system streams **notifications** back to the plugin through the `onNotification` callback. These **`AppServerNotification`** objects include types like `phase`, `message`, and `stderrMessage`.

The plugin aggregates these notifications into an internal **`TurnCaptureState`**, updating the UI through the `onProgress` callback. Helper functions like `emitProgress()` and `cleanCodexStderr()` in `codex.mjs` sanitize the output by stripping unnecessary warnings before displaying results to the user.

## Error Handling and Resilience

The communication layer includes robust error handling for production reliability. The plugin enforces a **2-minute import timeout** for external agents and implements retry logic specifically for `BROKER_BUSY_RPC_CODE` errors. When the broker reports a busy state, the client automatically retries the connection rather than failing immediately.

## Implementation Examples

### Starting a New Codex Thread

```javascript
import { CodexAppServerClient } from "./app-server.mjs";
import { buildThreadParams } from "./codex.mjs";

async function startCodexThread(cwd, options) {
  const client = new CodexAppServerClient();
  const threadParams = buildThreadParams(cwd, options);
  const { threadId } = await client.sendThreadStart(threadParams);
  return threadId;
}

```

### Resuming an Existing Thread

```javascript
import { CodexAppServerClient } from "./app-server.mjs";
import { buildResumeParams, buildTurnInput } from "./codex.mjs";

async function continueCodexThread(threadId, cwd, prompt) {
  const client = new CodexAppServerClient();
  const resumeParams = buildResumeParams(threadId, cwd);
  await client.sendThreadResume(resumeParams);
  const turnInput = buildTurnInput(prompt);
  await client.sendTurnInput(turnInput);
}

```

### Processing Streamed Notifications

```javascript
client.onNotification = (notification) => {
  // Forward to UI or log
  emitProgress(onProgress, notification.message, notification.phase);
};

```

## Key Source Files

The communication protocol is implemented across these critical files in the `plugins/codex/scripts/lib` directory:

- **`app-server.mjs`** – Implements `CodexAppServerClient` with WebSocket/JSON-RPC methods including `sendThreadStart`, `sendThreadResume`, and `sendUserInput`.
- **`codex.mjs`** – Core helper for building thread parameters, formatting progress, and aggregating Codex responses.
- **`broker-lifecycle.mjs`** – Manages broker session loading and endpoint resolution via `BROKER_ENDPOINT_ENV`.
- **`process.mjs`** – Validates Codex binary availability and handles platform-specific process spawning.
- **`codex-companion.mjs`** – Entry point that wires CLI commands to the client logic.

## Summary

- The Codex plugin uses **WebSocket-based JSON-RPC** to communicate with the Codex backend through a broker-mediated architecture.
- **Thread management** distinguishes between starting new conversations (`thread.start`) and resuming existing ones (`thread.resume`).
- **Real-time notifications** stream progress updates through `AppServerNotification` objects processed by the `onProgress` callback.
- **Error resilience** includes automatic retries for broker busy states (`BROKER_BUSY_RPC_CODE`) and a 2-minute timeout for external agent imports.
- All communication logic resides in `plugins/codex/scripts/lib`, with `CodexAppServerClient` serving as the primary abstraction layer.

## Frequently Asked Questions

### What protocol does the Codex plugin use to communicate with Codex?

The plugin uses a **WebSocket-based JSON-RPC protocol** with HTTP fallback. The `CodexAppServerClient` class in `app-server.mjs` manages the connection, authenticating with the service name `claude_code_codex_plugin` and routing all messages through a broker endpoint defined by `BROKER_ENDPOINT_ENV`.

### How does the plugin handle connection failures or busy brokers?

The plugin implements specific error handling for `BROKER_BUSY_RPC_CODE`, automatically retrying connections when the broker reports a busy state. Additionally, it enforces a **2-minute import timeout** for external agents and sanitizes stderr output using `cleanCodexStderr()` to prevent unnecessary warnings from disrupting the user experience.

### What is the difference between thread.start and thread.resume?

`thread.start` creates a **new conversation thread** on the Codex side when initiating a fresh request, while `thread.resume` reconnects to an **existing thread** using a locally stored thread ID. The plugin determines which method to call based on whether a previous thread ID exists for the current session context.

### Where is the broker endpoint configured?

The broker endpoint is configured through the **`BROKER_ENDPOINT_ENV`** environment variable, which is resolved by the `broker-lifecycle.mjs` module. This module loads or creates the broker session and injects the endpoint URL into the `CodexAppServerClient` before establishing the WebSocket connection.