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:
- Channels - Submission/event pipes created in
spawn.tx_sub/rx_sub: submissions intosubmission_loop(owned byCodex).tx_event/rx_event: events out to UI (Session holdstx_event).
- 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).
- Configuration - Model, approval policy, cwd, etc.
- 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 becomesSomewhen 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.