Inside the Codex harness: how a model becomes a working agent
Follow a request from the interface to the model, through tools and permissions, and back to a saved conversation. Then configure Astra to plan and Sol to implement.
What a harness actually does
A language model can propose a command, write a patch, or explain a test failure. A harness is the surrounding software that gives those proposals a working environment: it supplies instructions, exposes tools, executes permitted actions, returns results to the model, and keeps the conversation going.
In Codex, a request such as “fix the failing CSV export” can become a sequence of repository searches, file reads, edits, test runs, and further corrections. The model chooses candidate actions. The harness carries them out through concrete interfaces and decides whether the request can proceed under the active permissions. The test runner supplies evidence about what happened. The final answer should reflect that evidence.
The workbench analogy. The model provides judgment; the harness supplies the workbench, instruments, job notes, access controls, and record of the work. The analogy has a limit: the model can still choose a poor repair, and a successful command does not establish that the whole task is correct.
The open source repository contains the native Codex runtime, interfaces, protocols, tools, and supporting libraries under Apache 2.0. It does not contain the weights or training implementation of GPT-6 Astra or GPT-5.6 Sol, and inspecting it does not reveal the entire infrastructure behind hosted Codex products. README License Core
| Term | Meaning here | Example |
|---|---|---|
| Model | The service that predicts responses and tool calls from supplied context. | GPT-6 Astra or GPT-5.6 Sol. |
| Harness | The runtime that assembles context, drives the loop, connects tools, applies policy, and stores state. | The Codex Rust runtime and supporting crates. |
| Product or custom application | The interface and workflow around that runtime. | A terminal assistant, editor integration, or maintenance service. |
Changing a model can change the agent's decisions. Changing the harness can change what it can observe, execute, remember, and recover from. Neither change guarantees a correct result.
The architecture in one picture
The dominant implementation is a Rust workspace under codex-rs. A crate is a Rust package with a defined interface. Codex splits responsibilities across crates rather than putting the entire agent in the terminal UI.
The current terminal interface uses App Server client infrastructure through an embedded client, a local daemon, or a remote connection. If its local daemon connection fails, the TUI falls back to the embedded client. codex exec uses an embedded App Server. These boundaries can live in the same process. An external application can talk to codex app-server over its application protocol. The TypeScript SDK takes a distinct path: it launches the CLI with --experimental-json and exchanges JSONL events with codex exec. TUI Exec SDK
At this snapshot the main loop lives in core/src/session/turn.rs, and the thread handle is CodexThread. Older diagrams centered on a removed core/src/codex.rs path do not match this tree.
ModelClient carries session-lifetime authentication, provider, conversation, and transport state. A per-turn ModelClientSession can stream several Responses API requests and cache a WebSocket. Each turn passes its model and reasoning effort explicitly. These HTTP/SSE and WebSocket paths are separate from the client-facing App Server application protocol. Users authenticate the open source harness through ChatGPT login or an API key; the repository does not contain model weights. Model client
Main components and implementation anchors
| Component | Plain English job | Source anchor |
|---|---|---|
| CLI dispatcher | Selects interactive, headless, App Server, or execution server entry points. | cli/src/main.rs |
| TUI and headless exec | Render the conversation or emit machine-readable progress as runtime clients. | tui/src/lib.rs; exec/src/lib.rs |
| App Server and protocol | Expose thread operations, turn input, events, settings, and approvals. | app-server-protocol; app-server-client |
| ThreadManager / CodexThread | Create and recover conversations and supply a live handle. | core/src/thread_manager.rs |
| Session / turn loop | Own running state and repeatedly sample the model and process results. | core/src/session/turn.rs |
| TurnContext / StepContext | Carry turn state and capture matching tools, environments, settings, and instructions for one request. | core/src/session/*context.rs |
| Configuration and context | Resolve layered settings, managed constraints, repository instructions, skills, and model-visible state. | config/src/loader; core/src/context |
| ModelClient / provider | Authenticate model requests, stream responses, and manage transport state. | core/src/client.rs; model-provider |
| ToolRouter and registry | Align the tools advertised to the model with executable runtimes. | core/src/tools/router.rs |
| ToolOrchestrator / sandbox | Apply execution approval, select a sandbox, and handle permitted retries. | core/src/tools/orchestrator.rs |
| Execution environments | Provide filesystem and subprocess control locally or through an execution server. | exec-server/src/environment.rs |
| MCP, skills, plugins, hooks | Add external capabilities, reusable instructions, packaged integrations, and lifecycle behavior. | core/src/mcp.rs; skills; hooks |
| AgentControl | Spawn and communicate with child agents with separate thread state. | core/src/agent/control |
| ThreadStore / rollout / state | Persist conversation history and queryable metadata and support recovery. | thread-store; rollout; state |
| Telemetry | Record configured tool, token, timing, log, metric, and trace signals. | otel/src |
This is a map of the main runtime path, not an inventory of every crate.
A turn starts before the model sees your words
The request is only one input. The harness also has to decide which instructions apply, what tools exist, which directory is in scope, how much access those tools receive, and what has already happened in the session.
“Use a worker model for code.”
Text tells the agent what process to follow. It can guide planning and delegation, but text alone cannot instantiate a child with a specific model.
model and reasoning_effort
The orchestrator has to pass the actual fields when it creates work. That is the enforceable selection point.
The model does not automatically see the whole repository. Files become part of its reasoning when their content is supplied in context or returned through tools. A folder containing a million lines of code is different from a model request containing those million lines.
AGENTS.md discovery finds the project boundary, normally through .git, then collects applicable instruction files from that root to the current working directory. AGENTS.override.md is the preferred local override filename. Discovery respects configured size limits, filesystem access, and project trust. In this snapshot, project instructions are skipped for an untrusted project. Global and thread instructions can also come from the host. Discovery Manager
Skills package reusable instructions and related resources. They can tell an agent how to perform a workflow, but the agent must still apply those instructions through available tools. A skill is not a neural model, and naming a tool in a skill does not create that capability.
TOML configuration has a different job. It selects models, reasoning effort, permissions, integrations, and feature settings through layered precedence rules. Managed requirements are handled separately, and some sensitive settings such as model-provider endpoints are restricted in project-local configuration. Config loader
Expert detail: world state and compaction
The world_state implementation defines sections for environments, permissions, models, tools, multi-agent mode, and instructions. It can serialize snapshots and render changes against prior state. This is context maintenance machinery, not an external world model that automatically knows what happens outside supplied tools.
Compaction manages a finite context window through local or provider-supported paths, selected by capabilities and configuration. It replaces or summarizes the active conversation representation. It does not train the model, delete project files, or make memory unlimited. Later details must survive in retained context, a summary, files, or fresh tool reads. World state Compaction
Follow one request through the agent loop
A thread is a conversation that can survive multiple user requests. A turn is one run of work triggered by input. One turn can contain several model requests and many structured tool calls. This replay is illustrative, not a captured execution.
The request enters a thread
A user asks Codex to fix a CSV export. The client starts or resumes a thread, then begins a turn with that input.
The model has not acted yet. This step begins with the user's requested outcome.
Creates or resumes the thread, accepts the input, and emits the start of a turn.
App Server's application protocol includes operations such as thread/start, thread/resume, and turn/start, and notifications such as turn/started, item/agentMessage/delta, and turn/completed. Approval can travel back to the client while work is running. These application messages are distinct from the API requests sent to a model. The default stdio transport uses one JSON message per line, with the JSON-RPC header omitted on the wire; WebSocket transport is experimental in this snapshot. Protocol
For a particular sampling request, StepContext captures a matching tool router, MCP binding, environment snapshot, and loaded AGENTS.md value. This keeps the tool descriptions shown to the model aligned with the execution path. Codex consumes streamed model output, routes calls, records results, accounts for pending input, and samples again when follow-up work is needed. StepContext Turn loop
Concurrency, interruption, and cancellation
A session has at most one running task at a time, while multiple threads can exist and some tool calls can run concurrently. Runtimes that support parallel calls use a shared read lock; other calls take an exclusive write lock. User interruption, new pending input, and cancellation are runtime concerns. One turn therefore does not mean one process or only one tool running.
A tool call passes through two different controls
The sandbox defines what the process can reach. The approval policy defines when the harness should stop and ask a person before trying an action. Treating them as the same switch hides useful detail.
Where code may act
Filesystem and network boundaries constrain the process that actually executes a tool.
When a person is asked
The harness can pause before a request that needs broader or sensitive access.
How commands are classified
Rules can recognize command prefixes and decide whether they may run, need approval, or should be rejected.
A tool definition describes a callable capability; a handler or runtime implements it. ToolRouter keeps the plan advertised to the model aligned with the executable registry. The available set can include shell and process tools, patching, MCP-backed tools, host-supplied dynamic tools, and agent coordination, depending on configuration and feature state. Router Tool plan
ToolOrchestrator centralizes approval, sandbox selection, attempts, and permitted retry handling for runtimes using the shell or process path. The execution-policy engine supports Starlark-style prefix rules with allow, prompt, and forbidden decisions. A sandbox failure does not grant unrestricted access. Broader retry is conditional on policy and authorization. Orchestrator Exec policy
| Platform | Inspected process sandbox path | Qualification |
|---|---|---|
| macOS | Seatbelt through sandbox-exec | Effective filesystem and network policy still determines access. |
| Linux | Bubblewrap plus a legacy Landlock path | The selected path depends on filesystem policy; Linux is not Landlock-only here. |
| Windows | Elevated and restricted-token implementations | Capabilities differ from the Unix backends. |
These are operating-system mechanisms, not one universal Docker container around every Codex action. Execution environments provide filesystem and subprocess interfaces locally or through codex-exec-server. Remote registry and relay contracts establish connection paths, but they do not establish that every account can use every hosted environment.
MCP is a separate route. It connects the agent to configured tool and resource providers over local stdio or HTTP-related transports and authentication machinery. Effects occur under that service's tool contract and permissions. A local shell sandbox should not be described as protection around every external service action. MCP Client
Expert detail: Code Mode is another tool interface
The repository includes a V8-based Code Mode runtime. A process-owned provider can launch a separate host lazily, and a gRPC-backed provider also exists. This coordinates JavaScript tool cells where enabled. It is separate from running a user's Node.js application, separate from the Rust turn loop, and separate from arbitrary shell access. The crate's presence does not mean every model or client exposes Code Mode.
The transcript is also recovery machinery
There are several kinds of state. The live session tracks running work and services. The active context is used for inference. Persistent conversation storage supports resume after in-memory objects are gone. The working project holds actual files and can diverge from conversation state if an effect finishes just before a connection fails.
Steering and cancellation
New input can be injected into active work, and a running turn can be cancelled. The harness coordinates this state so the record stays coherent.
Resume, fork, compact
A previous session can continue, branch into a new session, or carry a condensed representation when the full history no longer fits comfortably.
Memory has layers. The current context window is the model's immediate working set. ThreadStore is a storage-neutral interface keyed by thread ID. In the local path, JSONL rollouts remain the canonical durable replay and SQLite supplies metadata plus projected history for queries. Project files such as AGENTS.md are loaded instructions. These layers solve different problems.
Operational telemetry is another stream. The repository uses tracing and OpenTelemetry libraries for configured logs, metrics, and traces. These can show which tool ran or where time was spent. They do not prove the task answer is correct and should not be confused with model-visible conversation history. ThreadStore Local store Telemetry
The technologies and why they are there
| Technology | Role | What to infer |
|---|---|---|
| Rust, Cargo, Rust 2024 | Native runtime workspace of crates | Most core behavior is implemented here. |
| Tokio, async channels, cancellation tokens | Async I/O, streamed events, coordination, cancellation | The runtime can wait on streams and tools without one blocking function. |
| Ratatui and Crossterm | Terminal rendering and input | They implement the interface, not the reasoning model. |
| Serde, JSON, TOML | Wire messages and configuration | Interfaces use structured contracts. |
| Responses API, HTTP/SSE, WebSockets | Model requests and streamed responses | Model transport is separate from App Server protocol. |
| JSON-RPC-style messages | App Server operations; exec server has a distinct protocol | Similar envelopes do not make endpoints interchangeable. |
| TypeScript and Node.js | SDK, CLI launcher, tooling | The SDK wraps the native CLI rather than reimplementing the loop. |
| SQLite via SQLx and JSONL | Metadata queries and replayable conversation history | Persistence serves more than one purpose. |
| Seatbelt, bubblewrap/Landlock, Windows backends | Process and filesystem restriction | Enforcement depends on platform and effective policy. |
| MCP Rust client | External tools and resources | External services retain their own authorization boundaries. |
| V8 and Code Mode host | Optional JavaScript tool orchestration | This is a capability subsystem, not the whole harness. |
| OpenTelemetry and tracing | Runtime observability | Diagnostics are execution evidence, not a quality verdict. |
| Bazel, Cargo, pnpm, Prettier | Build, test, packaging, maintenance | Contributors encounter multiple build surfaces. |
A planner can delegate, but only the runtime makes the assignment real
A root agent can keep the broad problem, research the repository, and review the result while a bounded worker implements a specific change. The split becomes reliable when the task carries explicit model fields, file scope, acceptance criteria, and a return path for review.
AGENTS.md records how the team should work. The orchestration call enacts that instruction by selecting the worker model and reasoning level. The root agent still validates the change. workspace policyConsider an illustrative maintenance request: a CSV export misaligns columns when a customer name contains a comma. The root inspects the issue, identifies relevant files, and defines acceptance criteria. The handoff should preserve the public export API, cover commas, quotes, and embedded newlines under the existing format contract, add targeted regression coverage, run the relevant test, and return changed paths plus actual output.
Separate conversations make responsibilities clearer, but child agents can operate on the same working files. Parallel workers need explicit file ownership or worktree isolation arranged by the application. A child thread does not imply a fresh container.
A copyable three-file configuration
model = "gpt-6-astra"
model_reasoning_effort = "high"
[features]
multi_agent_v2 = true
[agents]
enabled = true
default_subagent_model = "gpt-5.6-sol"
default_subagent_reasoning_effort = "medium"
[agents.implementer]
description = "Makes bounded code changes assigned by the planner."
config_file = "agents/implementer.toml"
model = "gpt-5.6-sol"
model_reasoning_effort = "medium"
developer_instructions = "Implement only the assigned change within the named files. Follow repository instructions. Run relevant validation and report changes, results, and limitations."
Example V2 spawn request
"task_name": "implement_fix",
"agent_type": "implementer",
"model": "gpt-5.6-sol",
"reasoning_effort": "medium",
"fork_turns": "none",
"message": "Change only named files. Meet the stated acceptance criteria and report validation."
}
Some host tools do not expose agent_type; omit it on those surfaces. Use fork_turns: none with explicit model selection. Current source contains a guidance and V2-handler mismatch around full-history overrides, so this example avoids that edge.
cd /path/to/your/trusted-project
codex
TOML parsed + schema checked · source reviewed
The snippets were parsed and checked against core/config.schema.json at commit 944d6fd1ba4baab69dbedd205282dc72ec20abb5. Model effort metadata was inspected. No fresh authenticated model invocation or end-to-end installation test was run. Availability depends on the installed client, effective configuration, and account. Runtime or UI and CLI overrides can supersede project model settings; managed requirements can constrain allowed values.
What “always delegate” can mean
AGENTS.md tells the root to delegate each code change. Configuration routes the root and worker models. These layers are useful and inspectable, but prose is still interpreted by a model. A hard product requirement needs enforcement at the host boundary: validate delegation requests, constrain which session receives write-capable tools or credentials, and cover shell, filesystem, patch, and MCP routes. A read-only parent cannot grant an ordinary child authority beyond inherited restrictions.
If a required model or subagent capability is unavailable, the workflow should report the condition instead of silently substituting another model. This article itself used the described split: implementation was assigned to GPT-5.6 Sol at medium effort; GPT-6 Astra at high effort handled source review, planning, and configuration audit. That records the process used, not an end-to-end test of the downloadable bundle.
Add the smallest layer that matches the job
Codex exposes several ways to extend behavior. They overlap at the edges, so the practical choice depends on whether you need instructions, a new callable capability, an external protocol, or lifecycle automation.
| Need | Choose | What it adds | Key limit |
|---|---|---|---|
| Repeat a documented workflow | Skill | Instructions plus optional scripts, templates, and references | Guides an agent; it does not itself grant access |
| Bundle related Codex additions | Plugin | A packaged set of skills, apps, or MCP integrations | The bundle still relies on its underlying capabilities |
| Call an external service or tool server | MCP server | Typed tools and resources exposed through a common protocol | Connection, authentication, and approval still apply |
| Work with a connected product | App or connector | Product-specific actions surfaced as callable tools | Available actions depend on the installed connector |
| React to lifecycle events | Hook | Configured automation at supported points in the run | Managed requirements can restrict which hooks load |
| Set project-specific conduct | AGENTS.md | Scoped instructions discovered from the project tree | Text describes behavior; runtime policy controls capability |
For a small automation, the Codex TypeScript SDK can create a thread, run a request, stream events, request structured output, and resume saved work. It wraps the native CLI; it is different from the OpenAI API SDK and a general-purpose Agents SDK. For a richer client, App Server exposes the conversation lifecycle and approval exchange. MCP supplies another boundary for external capabilities.
A skill contains reusable instructions and related resources. A plugin manifest can package skills, MCP configuration, and hooks. Hooks register behavior at lifecycle points such as PreToolUse, PostToolUse, SessionStart, SubagentStart, Stop, and Interrupt. Hook execution does not automatically grant approval and is not the same as repository instructions. At the native end, ExtensionRegistryBuilder accepts typed contributions for context, tools, lifecycle events, MCP servers, approval review, usage, and turn admission. That source-level extensibility is not a promise of a stable binary plugin ABI. Plugin Hooks Rust extensions
A useful test: if the change only tells the agent how to perform a familiar job, start with instructions or a skill. If it needs a new action in another system, expose a tool through an app or MCP server.
How to judge a harness design
Start with the work it must perform. Identify what information reaches the model, which tools can cause effects, who authorizes those effects, what is saved, and how a failure is recognized. For the illustrative CSV repair, useful completion evidence is a reviewed diff and an appropriate passing regression test. Agent count and a confident final message are not completion evidence.
For an expert review, follow each boundary in the source: client request to runtime, runtime request to model, model tool call to executor, executor result to history, and live state to persistence. Ask whether tools and configuration are captured consistently, whether retries can duplicate effects, whether cancellation leaves work running, and whether permissions apply to the actual execution destination. These are design questions for an application, not a generic safety certificate.
The harness supplies machinery for useful work over many steps. Your application still supplies the task, domain tools, acceptance criteria, and decisions about consequential effects.
Glossary
| Term | Meaning |
|---|---|
| Harness | Software around a model that runs the working agent loop. |
| Thread | A conversation with an identity that can be saved and resumed. |
| Turn | One input-triggered run, potentially containing many model and tool steps. |
| Sampling request | A request for the model to generate its next response. |
| Context window | The finite input and generated material available to an inference context. |
| Tool | A named capability with an input contract and execution path. |
| Sandbox | Mechanisms restricting an executing program's access. |
| Approval | A policy decision about whether an action may proceed. |
| MCP | Model Context Protocol, used to connect tool and resource providers. |
| JSONL | JSON Lines, with one JSON record per line. |
| JSON-RPC | A convention for requests, responses, and notifications encoded as JSON. |
| SSE | Server-Sent Events, an HTTP streaming mechanism. |
| Rollout | Codex's persisted record of conversation and runtime items. |
| Compaction | Reworking active history to fit a context budget. |
| Subagent | A child agent with a separate thread and delegated task. |
| Crate | A Rust package or library unit. |
| Reasoning effort | A model configuration control, not proof of answer quality. |
Sources and method
The architectural claims were checked against the local openai/codex source snapshot at full commit 944d6fd1ba4baab69dbedd205282dc72ec20abb5. Links below are pinned so readers can inspect the same files after the main branch changes. Public product documentation was checked on September 12, 2026. A source snapshot is not a promise that every released binary or account exposes every feature.
The figures are original explanatory drawings of inspected interfaces. The CSV scenario is hypothetical. Configuration snippets are parsed and schema checked, not authenticated execution tests. No throughput, token savings, model quality, or end-to-end reliability benchmark was run. This article examines the coding-agent runtime and extension seams; it does not certify every crate or reconstruct private hosted infrastructure.
- Pinned Codex source tree
Repository snapshot used for this reading, plus the Apache 2.0 license and top-level product boundaries.
- TUI · headless exec · App Server client
Client entry paths and embedded, daemon, or remote runtime connections.
- App Server protocol · TypeScript SDK exec wrapper
Thread and turn messages, notifications, approvals, and the SDK's CLI process path.
- ThreadManager · CodexThread · Session · turn loop
Live conversation ownership, turn state, and the repeated model and tool cycle.
- TurnContext · StepContext · tool parallelism
Request-scoped consistency, active-turn state, and parallel execution gates.
- AGENTS discovery · skills · configuration loader
Instruction discovery, reusable workflow guidance, trust, and configuration precedence.
- world state · local compaction · remote compaction
Model-visible state sections, changed-state rendering, and finite-context handling.
- ModelClient · provider layer
Authentication, per-turn model selection, Responses API streaming, and transport state.
- ToolRouter · registry · ToolOrchestrator · execution policy
Tool advertisement, dispatch, approval, sandbox selection, and command classification.
- platform sandboxes · execution environments · execution server
Local and remote filesystem and subprocess paths plus platform-specific enforcement.
- MCP integration · plugin manifest · hooks · native extension registry
External services, packaged capabilities, lifecycle events, and typed Rust contributions.
- ThreadStore · rollouts · state index · telemetry
Canonical local JSONL replay, SQLite metadata and projected history, and operational diagnostics.
- agent configuration · role loader · V2 spawn handler
Named roles, default and explicit child model fields, effort validation, fork behavior, and inherited permissions.
- App Server documentation · subagent configuration · GPT-6 Astra · GPT-5.6 Sol
Public documentation checked on September 12, 2026. Access depends on client and account.
Diagram arrows show conceptual flow and responsibility. They are intentionally less detailed than the internal Rust call graph. Product behavior and configuration syntax should be checked against the repository revision and official documentation you are using.
Related reading: From the Codex Harness to Cloud Agents: Using the Agents API maps these runtime boundaries to managed and self-hosted API sessions.