How the Codex Plugin Communicates with Codex: WebSocket RPC and Thread Management
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
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
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
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– ImplementsCodexAppServerClientwith WebSocket/JSON-RPC methods includingsendThreadStart,sendThreadResume, andsendUserInput.codex.mjs– Core helper for building thread parameters, formatting progress, and aggregating Codex responses.broker-lifecycle.mjs– Manages broker session loading and endpoint resolution viaBROKER_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
AppServerNotificationobjects processed by theonProgresscallback. - 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, withCodexAppServerClientserving 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →