Skip to content

Event System: Communication between UI and Daemon

Getting start introduces UI and Daemon communicates with custom protocol.

This passage focuses on the event system between UI and Daemon communication, and skip any details of the custom protocol, as the green areas in the diagram.

sequenceDiagram
    autonumber
    actor User
    participant Daemon
    participant Model

    rect rgb(200, 220, 255)
    User ->>+ Daemon: Query Go LOC
    end

    Note over Daemon, Model: Interact with Model<br>......<br> get results.

    rect rgb(200, 220, 255)
    Daemon -->>- User: Deliver final message

    end

Event System

The event system defines the communication protocol between UI and Daemon using two types of messages:

  • Operations (Op): Messages sent from UI to Daemon, for example:

    • Op::UserTurn - user sends input
    • Op::ExecApproval - user approves command execution
  • Events (EventMsg): Messages sent from Daemon to UI

    • EventMsg::AgentMessage - model's response
    • EventMsg::ExecApprovalRequest - daemon requests approval

Communication Channel Characteristics

The UI-Daemon communication is full-duplex with two independent channels:

  • Submission Queue (SQ): UI → Daemon (carries Op messages)
  • Event Queue (EQ): Daemon → UI (carries EventMsg messages)

Each channel operates independently:

  • UI sends Op messages through SQ at any time
  • Daemon pushes EventMsg messages through EQ at any time
  • Neither channel blocks the other

Ordering guarantee:

  • Each channel preserves message order (FIFO)
  • Op::UserTurn(A) sent before Op::UserTurn(B) → Daemon receives A before B
  • EventMsg::X sent before EventMsg::Y → UI receives X before Y

Sequential Op behavior: When UI sends Op::A immediately followed by Op::B:

Scenario Protocol-level behavior Implementation detail
General case Daemon receives A, then receives B How Daemon processes the queue depends on Op types
Op::Interrupt followed by any Op Interrupt received first, takes effect immediately Interrupt preempts current operation
Multiple Op::UserTurn Received in order Subsequent turns typically abort previous work (Daemon's choice)

Key distinction: The protocol guarantees delivery order (A then B), but how they affect the system state depends on the Daemon's processing logic for each specific Op type.

Scope boundary: This section describes protocol-level guarantees:

  • Message delivery and ordering are guaranteed by the transport layer
  • How Daemon processes queued Op messages is an implementation detail
  • How UI handles received EventMsg messages is an implementation detail

Why Event System?

Unlike traditional HTTP request-response models where a client sends a request and receives a complete response, Codex interacts with the Model via Server-Sent Events (SSE) protocol.

The Model returns a stream of events instead of a single response:

event: response.output_text.delta
data: {"delta": "I'll"}

event: response.output_text.delta
data: {"delta": " analyze"}

event: response.output_item.done
data: {"type": "function_call", ...}

This means the Daemon natively receives and processes events from the Model, and consequently pushes events to the UI. Conversely, the UI pushes events (operations) to the Daemon to control the flow—approving actions, providing input, or interrupting execution.

Therefore, the event system is used for UI-Daemon communication not as a design pattern for abstraction, but because it fundamentally matches the Model's streaming architecture.

Event Semantics

Op Semantics

Operations from UI to Daemon carry different meanings and have different effects on the system state.

Lifecycle Operations

Op Semantics Effect on System Contains Key Characteristic
Op::UserTurn "User wants to accomplish a task" Creates new Task, aborts currently running Task user message, cwd, approval policy, sandbox policy, model selection Destructive - replaces ongoing work
Op::Interrupt "User wants to immediately stop" Preempts running operation immediately (none) Highest priority, cannot be ignored

Approval Operations

Op Semantics Effect on System Contains Responds To
Op::ExecApproval "User's decision on safety check" Allows or blocks command execution decision (Allow/Deny), command ID EventMsg::ExecApprovalRequest
Op::PatchApproval "User's decision on code modification" Allows or blocks patch application decision (Allow/Deny), patch ID EventMsg::ApplyPatchApprovalRequest

Response Operations

Op Semantics Effect on System Contains Purpose
Op::UserInputAnswer "User provides requested information" Resumes paused Turn with provided data user's response to EventMsg::RequestUserInput Task completion (different from approval)

EventMsg Semantics

Events from Daemon to UI indicate state changes and provide information about ongoing operations.

Lifecycle Events

EventMsg Meaning UI Should Display Indicates Contains
TurnStarted A Turn has begun "Processing..." indicator Work in progress turn_id, model_context_window
TurnComplete A Turn finished successfully Hide "Processing..." indicator Turn succeeded, may lead to another response_id (for resuming)
TurnAborted A Turn was stopped "Interrupted" message User stopped or error occurred (none)
Error Something went wrong Error message Task cannot continue error details
Warning Non-fatal issue Warning message Task continues warning details

Output Events

EventMsg Meaning UI Should Display Indicates
AgentMessageDelta Streaming text fragment Append to display More content coming
AgentMessage Complete message Replace accumulated deltas Message complete
AgentReasoningDelta Streaming thinking fragment Append to reasoning display More reasoning coming
AgentReasoning Complete reasoning Replace accumulated deltas Reasoning complete
ExecCommandBegin Command execution starts Show "Running..." indicator Command in progress
ExecCommandOutputDelta Command output fragment Append to output More output coming
ExecCommandEnd Command execution ends Hide "Running..." indicator Command finished
PatchApplyBegin Patch application starts Show "Applying..." indicator Patch in progress
PatchApplyEnd Patch application ends Hide "Applying..." indicator Patch finished

Request Events

EventMsg Meaning UI Should Display Purpose
ExecApprovalRequest Daemon requests approval for operation Approval dialog Safety check
RequestUserInput LLM requests information from user Input prompt Task completion

Task Examples

Example 1: Simple Task

User asks a simple question, LLM responds directly without tool execution.

UI      Op              EventMsg       Daemon
───     ────────────    ────────────    ────
        Op::UserTurn →
                        ← EventMsg::TurnStarted
                        ← EventMsg::AgentMessageDelta*
                        ← EventMsg::AgentMessage
                        ← EventMsg::TurnComplete

Example 2: Task with Tool Execution

User requests file deletion, LLM requires approval, executes command, then confirms.

UI      Op              EventMsg       Daemon
───     ────────────    ────────────    ────
        Op::UserTurn →
                        ← EventMsg::TurnStarted
                        ← EventMsg::AgentMessageDelta*
                        ← EventMsg::ExecApprovalRequest
        Op::ExecApproval →
                        ← EventMsg::ExecCommandBegin
                        ← EventMsg::ExecCommandOutputDelta*
                        ← EventMsg::ExecCommandEnd
                        ← EventMsg::AgentMessage
                        ← EventMsg::TurnComplete

Example 3: Task with Follow-up Questions

User asks to refactor code, LLM needs to know which function, asks user, then continues.

UI      Op              EventMsg       Daemon
───     ────────────    ────────────    ────
        Op::UserTurn →
                        ← EventMsg::TurnStarted
                        ← EventMsg::AgentMessage
                        ← EventMsg::RequestUserInput
        Op::UserInputAnswer →
                        ← EventMsg::AgentMessage
                        ← EventMsg::TurnComplete

Example 4: Interrupted Task

User realizes they asked the wrong question, interrupts current task and starts over.

UI      Op              EventMsg       Daemon
───     ────────────    ────────────    ────
        Op::UserTurn (Task A) →
                        ← EventMsg::TurnStarted
        Op::Interrupt →
                        ← EventMsg::TurnAborted
        Op::UserTurn (Task B) →
                        ← EventMsg::TurnStarted
        ← ...

Concurrent Op Handling

The Daemon maintains an internal state machine that determines how to handle concurrent or sequential Op messages. Different Op types trigger different state transitions.

Scenario: Multiple Op in sequence

Op sequence Daemon behavior State transition
UserTurn(A)UserTurn(B) Abort Task A, start Task B Running → Aborted → Running
UserTurnInterrupt Immediate preemption, no graceful shutdown Running → Aborted
ExecApprovalRequestInterrupt Abort the pending approval Waiting → Aborted
RequestUserInputInterrupt Cancel the information request Waiting → Aborted

Key principle: Only one Task runs at a time. New UserTurn or Interrupt always affects the current state.

Note: The detailed state machine implementation and concurrency control mechanisms will be covered in the next chapter "User Request Lifecycle". This section focuses on the protocol-level behavior—what happens when multiple Op messages are sent in sequence.