4. Tasks and Turns¶
From User Input to Request Construction
Overview¶
This document describes the internal request processing flow of Codex, focusing on how user input is transformed into Task and Turn abstractions, and how the final LLM prompt is constructed.
Input Processing: From Op::UserTurn to Task Creation¶
Input Entry Point¶
User interactions are encapsulated as Op::UserTurn (or the legacy Op::UserInput) and sent to the Daemon. Op::UserTurn contains the configuration required for a specific execution cycle:
// codex-rs/protocol/src/protocol.rs
pub enum Op {
UserTurn {
items: Vec<UserInput>,
cwd: PathBuf,
approval_policy: AskForApproval,
sandbox_policy: SandboxPolicy,
model: String,
effort: Option<ReasoningEffortConfig>,
summary: Option<ReasoningSummaryConfig>,
service_tier: Option<Option<ServiceTier>>,
final_output_json_schema: Option<Value>,
collaboration_mode: Option<CollaborationMode>,
personality: Option<Personality>,
},
// ...
}
Input Routing Logic¶
When a new input arrives, the Session attempts to route it using the following priority:
- Steering: The session calls
steer_input. If a task is currently active and accepts the steering (i.e., theexpected_turn_idmatches or isNone), the input is queued into thepending_inputof the active turn. - Task Spawning: If no task is active or steering fails, the session creates a new
ActiveTurnand spawns aRegularTask.
ContextManager: Centralized State History¶
The ContextManager (located in codex-rs/core/src/context_manager/history.rs) serves as the Single Source of Truth for the conversation history.
- Storage: It maintains a
Vec<ResponseItem>containing messages, tool calls, and outputs. - Filtering: The
for_promptmethod prepares the history for the LLM. It acts as a safety boundary, normalizing items and filtering out internal states (like compression snapshots) before they hit the API. (We will explore the details of this transformation pipeline in Chapter 7). - Token Tracking: It tracks token usage but does not trigger compaction; compaction is an external task triggered by the session logic.
Abstractions: Task vs. Turn¶
Task: The Execution Controller¶
A Task is a unit of control flow, typically running as a tokio::spawn handle. It implements the SessionTask trait.
- Responsibility: Manages the lifecycle (start, cancel, finish), handles
CancellationTokenpropagation, and reports status to theSession. - Types: Includes
RegularTask(standard turns),ReviewTask,CompactTask, andUndoTask.
Turn: The Data Flow Executor¶
A Turn (represented by the run_turn function in codex-rs/core/src/codex.rs) is the logic execution unit within a task.
- Responsibility: Executes the sampling loop, interacts with the
ModelClient, handles tool execution, and records results into theContextManager. - Events: Emits
EventMsg(e.g.,OutputTextDelta,ToolCallStarted) to the UI using theturn_idfor scoping.
The run_turn Sampling Loop¶
Session State Transition¶
The Session tracks execution via active_turn. Only one ActiveTurn exists per user request cycle.
sequenceDiagram
participant S as Session
participant AT as ActiveTurn State
participant T as Task (JoinHandle)
participant R as run_turn Loop
Note over S: Op::UserTurn received
S->>S: Check active_turn
alt No active_turn
S->>AT: Create ActiveTurn
S->>T: Spawn Task
T->>R: Invoke run_turn()
loop Sampling
R->>R: Sampling & Tool Execution
end
R-->>T: Return last_agent_message
T->>S: on_task_finished()
S->>AT: Remove Task/Cleanup
S->>S: active_turn = None
else Steering
S->>AT: Push to pending_input
end Sampling Logic¶
run_turn executes a while loop. In each iteration: 1. Input Consumption: Consumes any pending_input queued via steering. 2. Prompt Rebuild: Reconstructs the complete Prompt (history + tools + instructions). 3. Model Interaction: Calls the LLM and streams responses. 4. Follow-up Determination: Sets needs_follow_up to true if tool calls are pending or more input was steered.
Termination Criteria¶
The loop terminates when SamplingRequestResult.needs_follow_up is false.
false: Model returned only text, or a terminal error occurred.true: Model requested tool calls, or token limits triggered an auto-compaction that requires a re-sampling.
Prompt Construction¶
The Prompt Structure¶
The Prompt (defined in codex-rs/core/src/client_common.rs) is the payload sent to the LLM:
pub struct Prompt {
pub input: Vec<ResponseItem>,
pub(crate) tools: Vec<ToolSpec>,
pub(crate) parallel_tool_calls: bool,
pub base_instructions: BaseInstructions,
pub personality: Option<Personality>,
pub output_schema: Option<Value>,
}
Tool Loading Mechanics¶
Tools are dynamically injected into each sampling request via the ToolRouter. Codex avoids overloading the LLM context by loading MCP/App tools only when necessary.
- Built-in Tools: Always loaded based on session
Config(e.g.,local_shell,read_file). - Explicit Mentions: If the user mentions a tool explicitly (e.g.,
@github), it is added to the active tool list viafilter_connectors_for_input. - Implicit Discovery (BM25): The
search_tool_bm25is always injected. If the LLM needs a tool not currently loaded, it calls this search tool. The discovered tools are saved to the session state and become available in subsequent requests.
sequenceDiagram
participant Turn as run_turn
participant Session as Session State
participant LLM as Model Client
Note over Turn: 1. Initialization
Turn->>Turn: Load Built-in Tools (Shell, File ops)
Note over Turn: 2. Resolve Active Tools
Turn->>Session: Get user mentions & prior tool selections
Session-->>Turn: Active tool identifiers (e.g., "@github")
Note over Turn: 3. Final Assembly
Turn->>Turn: Combine Built-in, Mentioned, and Selected tools
Turn->>Turn: Always Add search_tool_bm25
Turn->>LLM: Send Prompt (with Active Tools)
opt 4. LLM Discovers New Tools
LLM->>Turn: ToolCall: search_tool_bm25(query="jira")
Turn->>Session: Search & Save "jira" to active selections
Note over Turn,Session: Discovered tools will be included <br/>in the next sampling round automatically.
end Skill Loading and Injection¶
Skills consist of metadata and the SKILL.md content.
- Phase 1 (Discovery): At session start, the
SkillsManagerscans roots. It readsSKILL.mdfiles to extract YAML frontmatter (name, description). It also checksagents/openai.yamlforinterface,policy, anddependencies. - Phase 2 (Injection): During a turn, if a skill is mentioned via
$skill-name,build_skill_injectionsreads the fullSKILL.mdcontent and records it into the conversation history asSkillInstructions.
sequenceDiagram
participant R as run_turn
participant S as SkillsManager
participant FS as File System
participant CM as ContextManager
Note over R: Check for $skill-name in input
R->>S: Resolve SkillMetadata
S->>FS: Read SKILL.md (content)
FS-->>S: File Content
S-->>R: SkillInstructions Item
R->>CM: record_conversation_items(SkillInstructions)
Note over CM: Skill content is now in history Final Construction Flow¶
- Instructions: Fetch
BaseInstructionsfromSession. - History: Fetch filtered history from
ContextManager. - Tools: Build
ToolRouter(Built-in + Mentioned MCP + BM25). - Skills: Inject content of mentioned skills into history.
- Assembly: Combine all components into the
Promptand dispatch viaModelClient.