This page documents the BespokeEventHandler system that translates core codex events into JSON-RPC notifications for IDE clients connected to the app server. When a thread executes (running model inference, executing tools, applying patches), the core emits a stream of EventMsg variants. The app server must translate these into client-facing ServerNotification messages that conform to the protocol schema.
For information about the request handling and thread/turn management APIs, see Thread and Turn Management API. For the overall app server architecture, see CodexMessageProcessor and Request Handling.
The event translation system bridges the gap between the core's internal event model and the client-facing protocol. Events flow from codex-core through the app server's translation layer to IDE clients.
Event Translation Pipeline
Sources: codex-rs/app-server/src/bespoke_event_handling.rs107-427 codex-rs/app-server/src/codex_message_processor.rs1-250
The apply_bespoke_event_handling function is the central dispatch point for event translation. It matches on the EventMsg variant and produces appropriate protocol notifications.
Function Signature and Dispatch
Sources: codex-rs/app-server/src/bespoke_event_handling.rs107-427
The translation layer supports both V1 (legacy) and V2 APIs simultaneously. The ApiVersion enum controls which protocol structures are used.
| API Version | Item Lifecycle | Approval Pattern | Example Notifications |
|---|---|---|---|
| V1 | Implicit (legacy) | Direct approval params | ApplyPatchApprovalParams, ExecCommandApprovalParams |
| V2 | Explicit (started/delta/completed) | Request/response with item_id | ItemStartedNotification, CommandExecutionRequestApprovalParams |
V1 vs V2 Translation Example - File Changes
Sources: codex-rs/app-server/src/bespoke_event_handling.rs130-199 codex-rs/app-server/src/codex_message_processor.rs284-321
The V2 API uses a structured item lifecycle where each conceptual operation (file change, command execution, agent message) is represented as a ThreadItem with distinct lifecycle events.
Item Lifecycle States
ThreadItem Types with Lifecycle
| Item Type | Started Event | Delta Events | Completed Event | Status Field |
|---|---|---|---|---|
AgentMessage | item/started | item/agentMessage/delta | item/completed | N/A (text content) |
CommandExecution | item/started | item/commandExecution/outputDelta | item/completed | CommandExecutionStatus |
FileChange | item/started | item/fileChange/outputDelta | item/completed | PatchApplyStatus |
McpToolCall | item/started | N/A | item/completed | McpToolCallStatus |
CollabAgentToolCall | item/started | N/A | item/completed | CollabAgentToolCallStatus |
Sources: codex-rs/app-server/src/bespoke_event_handling.rs160-174 codex-rs/app-server-protocol/src/protocol/v2.rs823-1165
Command execution (shell commands) follows a complex translation pattern because the core uses tool call IDs while the protocol uses item IDs.
Command Execution Translation Flow
Sources: codex-rs/app-server/src/bespoke_event_handling.rs226-268 codex-rs/app-server/src/thread_state.rs1-100
Approval requests are bidirectional: the server sends a request to the client, which responds with a decision that gets forwarded back to the core as an Op.
Approval Request/Response Cycle
Approval Decision Mapping
| Client Decision | Core ReviewDecision | Behavior |
|---|---|---|
Accept | ReviewDecision::Approve | Run command once |
AcceptForSession | ReviewDecision::ApproveAndTrust | Trust for session |
AcceptWithExecpolicyAmendment | ReviewDecision::ApproveAndAddExecPolicyAmendment | Update exec policy |
Decline | ReviewDecision::Reject | Skip command, continue turn |
Cancel | ReviewDecision::Cancel | Skip and interrupt turn |
Sources: codex-rs/app-server/src/bespoke_event_handling.rs226-268 codex-rs/app-server/src/bespoke_event_handling.rs597-649
File changes (apply_patch operations) are translated similarly to command execution but with different item types and approval params.
File Change Translation Details
FileUpdateChange Structure
The protocol exposes file changes as structured updates with kind, path, and line ranges:
kind: "create", "update", "delete"path: Absolute path to the filefromLines: Optional line range for updates/deletescontent: New content for creates/updatesSources: codex-rs/app-server/src/bespoke_event_handling.rs125-199 codex-rs/app-server/src/bespoke_event_handling.rs717-808
MCP (Model Context Protocol) tool calls are handled differently because they come from external servers. The translation constructs item notifications from begin/end events.
MCP Tool Call Event Flow
McpToolCallStatus Values
InProgress: Tool is executing on the MCP serverSuccess: Tool completed with resultFailed: Tool failed with errorSources: codex-rs/app-server/src/bespoke_event_handling.rs359-381 codex-rs/app-server/src/bespoke_event_handling.rs809-891
Dynamic tools (custom tools defined at runtime via turn/start params) use a request/response pattern similar to approvals.
Dynamic Tool Request/Response Flow
Dynamic Tool Content Items
The client response includes a list of content items:
InputText: Plain text resultInputImage: Base64-encoded image dataInputAudio: Base64-encoded audio dataSources: codex-rs/app-server/src/bespoke_event_handling.rs324-358 codex-rs/app-server/src/dynamic_tools.rs1-100
The RequestUserInput event allows tools to ask the user questions mid-execution. This is an experimental V2-only feature.
Request User Input Structure
Question Types
options field contains predefined choicesis_secret: true for password-like fieldsis_other: true allows custom text inputSources: codex-rs/app-server/src/bespoke_event_handling.rs271-323
The TurnComplete event marks the end of a turn and triggers cleanup and final state computation.
Turn Completion Process
TurnStatus Values
completed: Turn finished successfullyinterrupted: User or system interrupted the turnfailed: Turn failed with an errorSources: codex-rs/app-server/src/bespoke_event_handling.rs121-124 codex-rs/app-server/src/bespoke_event_handling.rs892-996
The ThreadState tracks transient per-turn state to ensure items are only started once and to coordinate approval requests.
ThreadState Structure
State Reset Pattern
State is reset after each TurnComplete event to prepare for the next turn:
Sources: codex-rs/app-server/src/thread_state.rs1-100 codex-rs/app-server/src/bespoke_event_handling.rs153-159
Events are streamed from core threads to clients through a multi-layer architecture.
Streaming Architecture Components
Backpressure Handling
All channels are bounded with CHANNEL_CAPACITY = 128. If a client is slow to consume notifications:
send() on the channel blocksSources: codex-rs/app-server/src/codex_message_processor.rs1400-1500 codex-rs/app-server/src/transport.rs40-43 codex-rs/app-server/src/outgoing_message.rs54-108
Clients can opt out of specific notifications during initialization to reduce bandwidth or handle only certain events.
Opt-Out Mechanism
Common Opt-Out Patterns
item/agentMessage/delta to reduce bandwidth for streaming textitem/commandExecution/outputDelta if only final status is neededcodex/event/session_configured when using V2 APIsSources: codex-rs/app-server/src/transport.rs367-405 codex-rs/app-server-protocol/src/protocol/common.rs67-69
The translation layer includes error handling to ensure that failures in event processing don't crash the entire thread.
Error Recovery Patterns
| Error Scenario | Recovery Strategy | Impact |
|---|---|---|
| Client disconnects mid-approval | oneshot::Receiver drops, core times out | Turn continues or fails cleanly |
| Serialization failure | Log error, skip notification | Event lost but thread continues |
| Translation logic panic | Task isolation via tokio::spawn | Single event lost, others proceed |
| Approval response timeout | Core receives no response, applies default | Turn proceeds with rejection |
Approval Response Handling
Sources: codex-rs/app-server/src/bespoke_event_handling.rs143-146 codex-rs/app-server/src/bespoke_event_handling.rs597-649
apply_bespoke_event_handling is the central function that maps core EventMsg to protocol ServerNotificationsend_request() and oneshot channelsThreadState ensures items start only once and coordinates multi-connection deliverySources: codex-rs/app-server/src/bespoke_event_handling.rs1-1200 codex-rs/app-server/src/codex_message_processor.rs1-2500
Refresh this wiki