Start with a normal request: ask Codex to fix a bug in the current
repository and run a check if needed. If Codex were only a chat shell,
the implementation could forward that sentence to a model and stream
text back. A real coding agent has to do more. It may read files,
edit files, run commands, inspect test output, obey AGENTS.md,
wait for approval, retry inside a sandbox, return tool output to the
model, and keep the client informed with reviewable progress.
That changes the first source-reading question. Do not ask which directory is "the important one." Ask instead: when a user request enters the system, which owner decides the next step? Here, an owner is the runtime component responsible for a state boundary, not a label on an org chart. Every time the owner changes, the source route should change with it.
The overview claim is simple: Codex is not a UI shell, and the model does not directly control the filesystem. Codex is a governed agent runtime: entries accept intent, protocol types carry operations, session and turn code own work, tool gates mediate side effects, and events plus rollout records leave evidence that clients can project.
OpenAI's "Unrolling the Codex agent loop" gives this route its product-level frame: Codex runs a continuing agent loop across user intent, model reasoning, tool calls, and environment feedback. This source note reads the public harness code by asking which structure takes over at each step and what record it leaves behind.
Evidence boundary. This article treats official documentation as the product-level contract and the public openai/codex source as implementation evidence. Source links point to one fixed public snapshot. Terms such as governed runtime, model projection, and client projection are engineering readings from visible types, functions, and call boundaries; they are not claims about private OpenAI service topology.
The reading contract for this overview is concrete. After reading it, you should be able to narrate one turn as a route, not a pile of folder names. The route follows five questions:
- Why is entry code not the architecture by itself?
- What typed carrier holds user intent after it leaves the entry surface?
- Which object owns context, cancellation, model streaming, and the tool loop for a turn?
- Why is model output not the same thing as command or file authority?
- Why is client-visible progress only one projection of runtime evidence?
1. Follow a Request Before Walking the Tree
The Codex repository is large enough to make almost any starting point feel central. The CLI shows commands. The TUI shows user-visible state. The app-server exposes multi-client protocol. The tools directory shows shell and patch behavior. The sandbox code shows containment. All of those surfaces are real, but none of them is the whole system.
The safer move is to follow the bug-fix request. At each stage, stop reading the current layer once you know what it handed to the next owner.
| Turn stage | Reading question | Start here | Later article |
|---|---|---|---|
| Entry | Which product surface accepted the request, and what does it refuse to decide locally? | CLI, TUI, app-server, MCP server, remote-control. | Entries and clients. |
| Typed carrier | Which operation carries intent, and which event carries output back? | Submission, Op, Event, EventMsg. |
Protocol and event stream. |
| Session / turn | Who owns context, cancellation, pending input, model streaming, and tool continuation? | Codex facade, Session, run_turn. |
Session and turn loop. |
| Context / model | Which ledger is stored, and which view is sent to the model? | ContextManager, TurnContext, for_prompt(). |
Context management. |
| Tool authority | When the model asks for action, who can approve, reject, sandbox, retry, and record output? | ToolRouter, ToolOrchestrator, exec, apply_patch, hooks. |
Tools, permission, and sandboxing. |
| Projection / evidence | How is the client view separated from durable evidence? | Events, turn items, rollout, app-server mapping, TUI rendering. | Client projection and recovery. |
Read this table as a gear-shift rule. When the owner changes, change files. When the owner has not changed, keep reading the current layer. This keeps the first file you understand from becoming the fake center of the architecture.
2. Entry Accepts Intent; Protocol Creates the First Boundary
Codex has several entry surfaces, but the first architectural boundary
is not a single entry. It is the moment the entry converts a request
into a typed operation.
SessionSource
already places Cli, VSCode, Exec,
Mcp, and SubAgent in the same session-source vocabulary.
That is more than an entry inventory. It says an entry is a source
label before work enters the shared runtime.
2.1 Submission and Op Turn Intent into Typed Data
At the protocol layer,
Submission
is not chat text. It carries an id, an op,
an optional client user-message id, and trace context.
Op
is not a single prompt field either. It can represent user input,
thread settings, approval, interrupt, realtime conversation, tool
refresh, and other operation families.
That lifts the request out of the UI sentence. A bug-fix request can travel with working directory, thread settings, additional context, output schema, or approval policy. The source claim should therefore be precise: an entry submitted a typed operation with a correlation id.
2.2 The Codex Facade Is a Queue Pair, Not a Widget
After protocol, the operation reaches the high-level
Codex
facade. That struct holds tx_sub, rx_event,
agent status, and the session handle.
submit()
wraps an operation in a generated submission id and sends it to the
queue;
next_event()
receives runtime events.
This is the first invariant: clients submit operations; the runtime emits events. Once that queue pair is visible, a TUI state machine or app-server handler becomes a client surface, not the whole agent runtime.
3. A Turn Is the Control Unit
Inside session ownership, the turn is where context, model streaming,
tool calls, pending input, and cancellation meet.
run_turn
states the sampling loop in plain terms: the model returns function
calls or an assistant message. If it returns a function call, the
runtime executes the tool and sends output back in the next sampling
request. If it sends only an assistant message, Codex records it and
completes the turn.
The deeper point is not that the code calls a model in a loop. The
point is where ownership sits around that loop. Before sampling,
the turn handles pre-sampling compaction, context updates, skills and
plugins, hooks, input recording, and previous-turn settings. Only
then does it project history through for_prompt() into
model input. Later articles unpack those names; here the important
point is that they sit around sampling and decide whether the work unit can continue.
one user turn, shape-level:
submit typed Op
-> session accepts Submission
-> run_turn owns this request
-> update context / skills / hooks
-> project history into model input
-> stream model response
-> execute requested tools through authority gates
-> emit events and durable evidence
The turn context is not a message either.
TurnContext
carries model information, cwd, permission profile, approval policy,
sandbox settings, skills context, output schema, environment, network
policy, and session source. Treat it as the structured snapshot for
this runtime unit. The second article follows how that snapshot enters
the context ledger.
4. Model Output Requests Action; Tool Gate Grants Authority
The easiest phrase to say is "the model ran a command." The source
route is stricter. The model can request a tool call only through the
model-visible tool specs for this turn.
build_prompt()
builds a Prompt from projected input, router.model_visible_specs(),
parallel tool-call capability, base instructions, personality, and
output schema. Tool schema is model-visible capability, not execution.
Those specs come through
built_tools()
and
ToolRouter:
MCP tools, plugins, apps, dynamic tools, and extension executors are
merged into a turn-scoped router. When the model returns a tool call,
router dispatch
hands it to the registered executor.
Before concrete execution, authority still crosses another layer.
The
ToolOrchestrator module header
names the sequence directly: approval, sandbox selection, attempt,
and escalation retry on sandbox denial. The implementation then checks
approval policy, filesystem sandbox policy, tool requirements, hooks,
workspace roots, network policy, and platform sandbox settings before
an effect can happen.
"The model edited a file" is a user-facing shortcut. Source-level wording is: the model requested a tool call, and runtime authority routed, approved, sandboxed, executed, and recorded it.
5. The Client View Is a Projection, Not the Only Source of Truth
Runtime output is typed too.
Event and EventMsg
cover warnings, context compaction, rollback, turn lifecycle, assistant
deltas, tool calls, token counts, hook lifecycle, and more. A client
can render them as chat bubbles, progress rows, diffs, approval
prompts, or status text. Those screens are projections over runtime
facts.
For example, one shell command can produce events that let the UI show command start, output deltas, and command completion in real time. The rollout material saved afterward serves a different purpose: it gives reload and resume enough evidence to reconstruct what happened.
Some facts are more durable.
RolloutItem
stores session metadata, response items, compacted records, turn
context, and event messages. That is why resume, rollback, and compact
cannot be explained only by what remains visible on screen. The UI is
the current projection; rollout and history are part of the recovery
and audit surface.
6. Reading From Here
This overview only establishes the route. Later articles follow the same turn chain and focus on one owner at a time. The goal is not a source encyclopedia; it is a sequence a reader can replay. Each stop returns to the same three questions: who receives the work, who records the fact, and who renders the view?
| Part | Main question | Owner to track |
|---|---|---|
| I. Overview | How does one user turn move through entry, protocol, runtime, tool gate, and evidence? | Submission, Codex, run_turn, ToolOrchestrator. |
| II. Context management | Why is context a projectable, compactable, recoverable ledger rather than chat history? | ContextManager, TurnContext, for_prompt(), compaction. |
| III. Protocol and events | How do clients and runtime share facts through typed requests and events? | Op, EventMsg, app-server protocol, generated schema. |
| IV. Tools | How do tool specs enter the model, and how are tool calls dispatched, parallelized, and recorded? | ToolRouter, registry, MCP, dynamic tools, extension tools. |
| V. Permission and sandboxing | Where do approval, hooks, sandboxing, and exec policy intercept side effects? | ToolOrchestrator, exec policy, hooks, sandboxing, apply_patch. |
| VI. Client projection | How do TUI, app-server, and persisted records render the same runtime evidence? | Event mapping, turn items, rollout, thread status. |
| VII. Extensions and multi-agent | How do skills, plugins, MCP, and subagents enter as bounded runtime inputs for the current turn? | Skills manager, plugins manager, MCP exposure, tool_search, AgentControl. |
7. Common Misreadings
| Misreading | Better reading | Why it matters |
|---|---|---|
| The CLI is the center of Codex. | The CLI is one entry surface; runtime handoff uses Submission and Event. |
Argument parsing should not be mistaken for session or turn semantics. |
| A turn is one chat exchange. | A turn is a control unit that may contain multiple sampling requests, tool calls, pending input, and compaction. | Otherwise one user request with several model calls looks mysterious. |
| The model runs commands directly. | The model requests tool calls; execution authority lives in router, approval, hooks, sandbox, and handlers. | This is where the most important safety and audit boundaries sit. |
| The UI history is runtime truth. | UI is a projection over events and state; recovery also depends on rollout, history, and turn context. | Resume, rollback, and compaction are not display-layer behavior. |
8. Transferable Rules
Use the same questions when reading another agent system:
| Reading move | Question to ask first | Invariant protected |
|---|---|---|
| Read entry code. | Which typed carrier receives intent? | UI text does not get mistaken for runtime work. |
| Read runtime code. | Who owns context, cancellation, state changes, and completion? | The real control point is not hidden behind the model call. |
| Read tool code. | Which gates sit between model output and side effects? | Capability exposure, approval, sandbox, execution, and evidence stay separate. |
| Read client code. | Which events and persisted records produce the screen? | Presentation, recovery, and model-visible views are not collapsed. |
| Read the series. | Which owner is this article explaining? | The route stays gradual instead of expanding every concept at once. |
The next article starts with the context owner.
Codex context is not a plain chat log; it is a ledger that can be
projected into a model request, rewritten by compaction, and recovered
from rollout. With this overview in place, ContextManager,
TurnContext, and for_prompt() become parts of
the route rather than isolated names.
Sources
- Pinned openai/codex source snapshot
- Official Codex documentation
- OpenAI: Unrolling the Codex agent loop
- Submission
- Op
- Event / EventMsg
- SessionSource
- Codex facade
- submit / submit_with_id
- next_event
- run_turn mainline
- TurnContext
- build_prompt
- built_tools
- ToolRouter
- ToolOrchestrator sequence
- RolloutItem