Skip to content

3. Op Dispatch and State Changes

Previous chapter UI Events described events between UI and Daemon.

This chapter describes how Daemon processes Op and the resulting state changes.

Async side effects (task execution, model interaction, state transitions) are not covered.

Session Creation

spawn sets up channels, builds Session configuration/services, then creates Session:

  1. Channels - Submission/event pipes created in spawn.
    • tx_sub/rx_sub: submissions into submission_loop (owned by Codex).
    • tx_event/rx_event: events out to UI (Session holds tx_event).
  2. Session Fields - Session-scoped state and handles.
    • agent_status: derived from emitted events (PendingInit, Running, Completed, Errored, Shutdown, NotFound).
    • active_turn: current running tasks (internal).
    • state: session state (configuration + history + rate limits + other session data).
  3. Configuration - Model, approval policy, cwd, etc.
  4. Services/Resources - Base instructions, Skills, MCP tools, and other managers.

Then submission_loop starts listening for Op from UI.

Note: active_turn is user-session-level state (same level as Task), not the protocol-level turn (one interaction round). All Handler operations work with this.

Op Scope

This chapter covers Op processing*and immediate state changes.

Async effects are out of scope, even though async execution is where the real work happens. Op processing is just the trigger.

Sync changes internal state and triggers tasks. Async execution (model interaction) is where Codex does its work.

Op is handled by Handler, not Session. Handler interprets Op and calls Session methods. Session does not know about Op. Session only exposes methods (spawn_task, abort_all_tasks, etc.).

Op Synchronized Effects

Op::UserTurn

Handler: user_input_or_turn (codex-rs/core/src/codex.rs:3981)

High-level effect:

  • Refresh session configuration from Op and create a new TurnContext.
  • Try to inject the input into an existing active turn.
  • Only if there is no active turn, spawn a new task (async execution).

Internal state changes (sync):

  • session_configuration: updated.
  • active_turn: only becomes Some when a new task is spawned.

Op::Interrupt

Handler: interrupt (codex-rs/core/src/codex.rs:3956)

High-level effect:

  • Interrupt the current turn if it exists (abort all running tasks).
  • If there is no active turn, only cancel MCP startup.

Internal state changes (sync):

  • active_turn: cleared when aborting tasks.
  • All running tasks' cancellation tokens are cancelled.

Op::ExecApproval

Handler: exec_approval (codex-rs/core/src/codex.rs:4117)

High-level effect:

  • Apply execpolicy amendment if provided.
  • If decision is Abort, interrupt the current turn.
  • Otherwise, notify the matching pending approval.

Internal state changes (sync):

  • TurnState.pending_approvals: remove the matching approval only on non-abort decisions.

Op::UserInputAnswer

Handler: request_user_input_response (codex-rs/core/src/codex.rs:4168)

High-level effect:

  • Deliver the user's response to the waiting request.

Internal state changes (sync):

  • TurnState.pending_user_input: remove the matching request.