Codex App-Server Protocol: How Threads Are Created and Resumed in the OpenAI Plugin
The Codex app-server protocol uses a line-delimited JSON-RPC transport over pipes or Unix sockets, with thread/start and thread/resume methods managing thread lifecycles.
The Codex app-server protocol defines how the OpenAI Codex plugin communicates with a backing server process to create and manage interactive coding sessions. This lightweight, text-based protocol enables the plugin to spawn new threads for isolated work and resume existing threads to maintain context across interactions.
JSON-RPC Transport Layer
The protocol runs over newline-separated JSON messages. Each payload is a single JSON object terminated by \n, allowing simple line-based parsing without complex framing.
Two transport implementations coexist in plugins/codex/scripts/lib/app-server.mjs:
SpawnedCodexAppServerClient– Direct child-process pipe to a spawnedcodexbinaryBrokerCodexAppServerClient– Unix socket connection through a broker process
Both inherit common logic from AppServerClientBase at lines 57-78, which handles message serialization, response correlation, and error handling.
Message Routing
Incoming data accumulates in lineBuffer. Each newline triggers handleLine (lines 18-31), which parses the JSON and categorizes the payload:
| Direction | Handling logic |
|---|---|
| Server → Client request | Routed to registered handlers via onRequest |
| Response to client request | Resolved against pending promise by numeric id |
| Notification | Dispatched to onNotification handlers |
Error responses are wrapped as ProtocolError objects carrying the JSON-RPC error code and optional data field (lines 44-55).
Request Lifecycle
The request(method, params) method (lines 86-98) generates a unique numeric id, stores a pending promise, and sends the JSON line via sendMessage. The caller awaits resolution when the matching response arrives.
// Core request pattern from app-server.mjs
async request(method, params) {
const id = this.nextId++;
const message = { jsonrpc: "2.0", id, method, params };
this.pending.set(id, { resolve, reject });
this.sendMessage(JSON.stringify(message));
// Returns promise resolved by response handler
}
Thread Creation via thread/start
New threads are created through the "thread/start" JSON-RPC method defined in app-server-protocol.d.ts (lines 59-73). This method accepts a ThreadStartParams object specifying:
cwd– Working directory for the sessionmodel– Model identifier (nullable for default)approvalPolicy– When user approval is required ("never","prompt", etc.)sandbox– Sandbox mode ("read-only","modifiable", etc.)serviceNameandephemeralflags
High-Level Wrapper: startThread
The codex.mjs module provides startThread (lines 32-47) as a convenience wrapper. It uses buildThreadParams (lines 62-71) to assemble defaults, then forwards to the underlying client:
// From plugins/codex/scripts/lib/codex.mjs
export async function startThread(client, arg, name) {
const thread = await client.request(
"thread/start",
buildThreadParams(arg) // Assembles ThreadStartParams
);
if (name) thread.name = name;
return thread;
}
Practical Example: Creating a Thread
import { CodexAppServerClient } from "./plugins/codex/scripts/lib/app-server.mjs";
async function createThread(cwd) {
const client = await CodexAppServerClient.connect(cwd);
const thread = await client.request("thread/start", {
cwd,
model: null, // Use default model
approvalPolicy: "never", // Auto-approve low-risk actions
sandbox: "read-only", // Restrict filesystem access
serviceName: "codex",
ephemeral: true // Clean up on process exit
});
console.log("New thread ID:", thread.thread.id);
await client.close();
}
Thread Resumption via thread/resume
Existing threads are resumed through the "thread/resume" method, which accepts ThreadResumeParams containing the thread ID to reactivate plus optional overrides for model, sandbox, or approval settings.
High-Level Wrapper: resumeThread
The resumeThread function (lines 50-52) is simpler than its creation counterpart—it delegates directly to buildResumeParams (lines 74-82) without additional naming logic:
// From plugins/codex/scripts/lib/codex.mjs
export function resumeThread(client, arg) {
return client.request("thread/resume", buildResumeParams(arg));
}
buildResumeParams constructs the payload with thread ID, cwd, and any overridden settings from the input argument.
Practical Example: Resuming a Thread
import { CodexAppServerClient } from "./plugins/codex/scripts/lib/app-server.mjs";
async function continueThread(cwd, threadId) {
const client = await CodexAppServerClient.connect(cwd);
const resumed = await client.request("thread/resume", {
threadId, // Required: existing thread to reactivate
cwd,
model: null, // Override or null to preserve
approvalPolicy: "never",
sandbox: "read-only"
});
console.log("Resumed thread:", resumed.thread.id);
await client.close();
}
Connection Bootstrap
Static method CodexAppServerClient.connect (lines 35-53) implements the connection strategy:
- Detect transport – Checks
CODEX_BROKER_SOCKET_PATHenvironment variable or explicit options to choose broker vs. direct spawn - Instantiate client – Creates
BrokerCodexAppServerClientorSpawnedCodexAppServerClient - Initialize handshake – Sends
"initialize"request and waits for server capabilities
The resulting client is ready for thread/start or thread/resume calls.
Summary
- The Codex app-server protocol is line-delimited JSON-RPC with transport abstraction over pipes or Unix sockets
AppServerClientBaseinapp-server.mjshandles message framing, request correlation, and error wrapping- Thread creation uses
"thread/start"withThreadStartParamsassembled bybuildThreadParamsincodex.mjs - Thread resumption uses
"thread/resume"withThreadResumeParamsfrombuildResumeParams CodexAppServerClient.connectbootstraps the connection and completes the initialization handshake
Frequently Asked Questions
What transport protocols does the Codex app-server support?
The protocol supports two transports: direct child-process pipes via SpawnedCodexAppServerClient and Unix domain sockets via BrokerCodexAppServerClient. Both use identical JSON-RPC message framing. The connect method auto-detects based on the CODEX_BROKER_SOCKET_PATH environment variable.
How are thread IDs managed across sessions?
Thread IDs are generated server-side during thread/start and returned in the response payload. To resume, the client passes this same ID to thread/resume. The server maintains thread state, allowing context preservation across disconnections and reconnections.
Can sandbox or approval settings change when resuming a thread?
Yes. The ThreadResumeParams type in app-server-protocol.d.ts declares sandbox, approvalPolicy, and model as optional fields. When provided, these override the original thread settings; when null, the server preserves existing values.
Where is the protocol specification defined?
Type definitions for all methods, parameters, and return types reside in plugins/codex/scripts/lib/app-server-protocol.d.ts. The runtime implementation and transport logic are in plugins/codex/scripts/lib/app-server.mjs, with high-level wrappers in plugins/codex/scripts/lib/codex.mjs.
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 →