openai/codex
github.com/openai/codex ↗ · 4000 files · 42.5 MB · scan capped
Codex CLI is a coding agent from OpenAI that runs locally on your computer.
Overview
Codex is OpenAI's local coding-agent CLI and library: a Rust workspace (`codex-rs`) that runs multi-turn agent sessions against model providers, executes tools under an OS sandbox and approval policy, and persists transcripts as rollout JSONL. Users reach it through the `codex` binary (interactive TUI, `exec`, `resume`, MCP, app-server), a thin Node wrapper `@openai/codex`, and TypeScript SDKs. Distinctive pieces are the protocol-typed event stream, execpolicy/sandbox enforcement, and a large crate split that keeps the agent loop, TUI, and app-server JSON-RPC API apart.
System map
The repo is a pnpm/Bazel monorepo whose product lives in `codex-rs`. The `codex-cli` crate's `main.rs` dispatches subcommands into the TUI, non-interactive exec, MCP server, and app-server. `codex-core` owns the agent session, config, tools, models, and rollout persistence. `codex-app-server` exposes a JSON-RPC v2 API over stdio. Builds are Cargo via `just` plus Bazel CI. Storage is local files: `~/.codex/config.toml`, auth.json, and session rollout JSONL under `~/.codex/sessions`. External edges are the Responses/Chat Completions APIs, MCP servers, execve wrappers, and OS sandboxes (Seatbelt on macOS, bubblewrap/Landlock on Linux, restricted tokens on Windows).
- The published Node package `@openai/codex` is a launcher, not the agent. Its bin is `bin/codex.js`, which selects a native Rust binary by target triple and spawns it.
- The Rust CLI entrypoint is `codex-cli/src/main.rs`. `MultitoolCli` parses subcommands and `cli_main` matches `Tui`, `Exec`, `Mcp`, `AppServer`, `Resume`, `Fork`, `Review`, `Login`, `Sandbox`, and others before calling `arg0_dispatch_or_else`.
- Workspace membership and shared deps live in `codex-rs/Cargo.toml` (package version 0.0.0, edition 2024, resolver 3). Feature flags such as `vt100-tests`, `experimental-service-tier-flag`, and `login-server-sandbox` are declared on the workspace package.
- CI's blocking workflow `.github/workflows/blocking-ci.yml` runs fmt, package-manager pinning, a `cargo-shear` workspace check, and Bazel tests via `.github/actions/bazel-ci`. `bazel.yml` is the reusable workflow that sets `CARGO_BAZEL_REPIN` and calls `bazel test` with a target from `bazel-targets.txt`.
- Model traffic goes through `ModelClient` in `codex-rs/core/src/client.rs`, which selects Responses API vs Chat Completions from `WireApi` and retries with `max_retries`. Auth tokens come from `AuthManager` backed by `~/.codex/auth.json` (ChatGPT OAuth or API key).
- The app-server is a separate binary (`codex-app-server`) speaking JSON-RPC over stdio. `MessageProcessor` dispatches `thread/start`, `thread/resume`, `thread/fork`, `turn/start`, `turn/steer`, and `review/start` into `CodexMessageProcessor` and thread handlers.
- The interactive UI is the `codex-tui` crate. `tui/src/lib.rs` runs `App::run`, which owns the ratatui terminal, the `ChatWidget`, and a `FrameRequester`. Styling rules are documented in `codex-rs/tui/styles.md` and enforced by AGENTS.md.
- Sandboxing is platform-split. macOS uses Seatbelt profiles from `codex-rs/sandboxing/src/seatbelt.rs`; Linux uses bubblewrap plus Landlock (`codex-rs/linux-sandbox` and `codex-rs/sandboxing`); Windows uses a restricted token helper. Policy selection is `SandboxPolicy` (`read-only`, `workspace-write`, `danger-full-access`) resolved in `core/src/sandboxing/mod.rs`.
- The TypeScript SDK (`sdk/typescript`) is a thin client of the app-server protocol. `Codex` spawns the CLI with `app-server --stdio`, speaks JSON-RPC, and exposes `startThread`, `resumeThread`, and `Thread.run`. A Python SDK lives under `sdk/python`.
Feature catalog
User-facing surface is the `codex` multitool plus the app-server JSON-RPC API and the SDK wrappers. Interactive mode is a full-screen TUI with composer, approval overlays, and slash commands. Headless mode is `codex exec` (and `codex review`) for scripts and CI. Account features cover ChatGPT device-code login, API-key login, and `codex login`. Tooling includes shell, unified exec, apply_patch, MCP servers, skills, web search, and an execpolicy command allow/deny language. Session features include resume, fork, compact, and forking a thread for review.
- `codex` with no subcommand starts the interactive TUI. `codex exec` (alias `e`) runs `codex_exec::run_main` for non-interactive prompts, with `--json` JSONL events, `--output-schema`, `--sandbox`, `--full-auto`, and image inputs.
- `codex resume` and `codex fork` reopen a rollout. Resume continues the selected session; fork copies history into a new thread id. Both are CLI subcommands that flow into core session startup with a rollout path.
- `codex mcp` serves the agent as an MCP server (`mcp serve`) and manages configured MCP servers (`mcp add`, `list`, `get`, `remove`). Server configs live in config.toml under `[mcp_servers.<id>]` with stdio or streamable HTTP transport.
- Login is `codex login` / `codex logout`. ChatGPT login uses a device-code or browser OAuth flow implemented in `codex-login`; API-key login stores a key in `auth.json` under `CODEX_HOME`. `codex login status` reports the active mode.
- Slash commands in the TUI are registered in `tui/src/slash_command.rs` (`SlashCommand` enum: model, personality, approvals, review, new, resume, fork, compact, diff, mention, mcp, skills, logout, quit, and others) and rendered by the composer popup.
- Approval modes are `untrusted`, `on-request`, and `never`, mapped to `AskForApproval`. Sandbox presets exposed to users are read-only, workspace-write, and danger-full-access. `--full-auto` selects workspace-write plus on-request (or never, depending on the preset path).
- The SDK `Thread` API mirrors app-server turns. `thread.start()` sends `thread/start`; `thread.run(prompt)` sends `turn/start` and yields streamed items (agent message, command execution, file change, mcp tool call, error). `CodexOptions` selects `apiKey`, `baseUrl`, `codexPathOverride`, and config overrides.
- Cloud/environment tasks are represented by `codex exec` environment options and the `environment` module, including `codex cloud` style tasks that delegate to a remote environment when configured. Local exec remains the default.
- `codex sandbox` can run a single command under the Linux/macOS sandbox helper for debugging policy, separate from an agent turn. The Linux helper binary is `codex-linux-sandbox`.
Data flow
A user prompt enters either as a TUI `Op::UserTurn`, an exec submit, or an app-server `turn/start` params object. Core enqueues it on the session submission channel. `submission_loop` turns it into model input: prior rollout items plus a bounded developer/user context built from `core/context` fragments. The model streams Responses events; tool calls are dispatched by the tool router, their outputs are appended as function-call items, and the loop continues until the turn completes. Side effects (shell stdout, patch files, MCP results) are approved, sandboxed, then written back into the event stream and the rollout JSONL. Clients render `Event` values; nothing rewrites history except explicit compact, which summarizes and replaces older items under a size cap.
- `ModelClient::stream` posts to `/responses` or the Chat Completions endpoint. SSE frames are parsed into internal response items (`ResponseItem`) that the session converts into protocol events and rollout records.
- Shell output crosses a sandbox boundary. The orchestrator builds a `SandboxCommand`, asks the approval policy (`AskForApproval` plus execpolicy), then `spawn`s via the platform sandbox. Stdout/stderr are captured, truncated, and emitted as `ExecCommandEnd`.
- The TUI does not own business state. `ChatWidget` subscribes to core `Event`s, maps them to ratatui lines, and submits `Op`s when the composer submits or an approval overlay resolves. Transcript rendering helpers live in `tui/src/history_cell.rs` and wrapping helpers in `tui/src/wrapping.rs`.
- Auth data flows from `auth.json` or `CODEX_API_KEY` into `AuthManager::auth()`, which attaches a bearer token on `ModelClient` requests. Tokens are refreshed by the login crate; the CLI never puts the API key on the command line of the model request.
- Config override precedence is CLI flags over profile over project `.codex/config.toml` over user `config.toml` over system config. The resolved `Config` is the only object the session reads after startup.
Behavior traces
Three flows dominate. Startup parses CLI args, loads config and auth, builds a Codex session, and either enters the TUI event loop or the exec one-shot loop. A primary turn submits user input, streams a model response, dispatches tool calls under approval and sandbox, and records rollout items until `TurnComplete`. Persistence is continuous during the turn (rollout appends) and finalized on shutdown or resume. App-server clients follow the same core path after a JSON-RPC translation step.
- Process startup: `cli::main` → `arg0_dispatch_or_else` (so sandbox helpers can re-exec under a different argv0) → `MultitoolCli::parse` → match on `Subcommand`. For the default path, `tui_main` loads config, builds `TuiConfig`, and calls `codex_tui::run_main` → `App::run`.
- Exec one-shot: `codex exec` → `exec::run_main` builds a `Codex` with the prompt as the initial `Op::UserTurn`, reads the event channel until `TurnComplete` or `Error`, prints human text or JSONL, and exits non-zero on error. `--output-schema` validates the final message.
- Tool call with approval: model emits a function call → router looks up the handler → if policy is `on-request` and execpolicy does not pre-approve, core emits `ExecApprovalRequest` → TUI modal or exec auto-decision returns `Op::ExecApproval` → sandbox spawn → `ExecCommandEnd` → function output appended → model continues.
- SDK run: `Codex` constructor starts `codex app-server` with `--stdio` → `initialize` JSON-RPC → `startThread` calls `thread/start` → `run` calls `turn/start` and demultiplexes notifications into typed `ThreadEvent`s until `turn/completed`.
- Login flow: `codex login` → login crate starts device-code or localhost callback → tokens written via `AuthManager` to `$CODEX_HOME/auth.json` with mode `chatgpt` or `apikey`. Later `ModelClient` calls `AuthManager::auth()` on each request and refreshes when the access token is near expiry.
Testing signals
Rust tests use `cargo nextest` through `just test -p <crate>`, not raw `cargo test`. Core favors integration tests in `codex-rs/core/tests` (and `core/suite`) built on `core_test_support::responses`, which mounts SSE fixtures with `wiremock` and drives a `TestCodex`. The TUI uses `insta` snapshot tests. App-server tests speak the public JSON-RPC API via `TestAppServer`. CI is Bazel-first (`blocking-ci.yml`, `bazel.yml`) plus `cargo-deny`, codespell, and CLA checks. Python tooling under `scripts/` has its own pytest setup. Large areas — real OS sandbox integration, live provider calls, and Seatbelt-under-Seatbelt — are intentionally skipped when `CODEX_SANDBOX` or `CODEX_SANDBOX_NETWORK_DISABLED` is set.
- Core integration tests construct a mock HTTPS Responses server. The canonical pattern is `responses::mount_sse_once` + `ev_response_created` / `ev_function_call` / `ev_completed`, then `codex.submit(Op::UserTurn)` and assertions on `ResponseMock::single_request()`.
- `TestCodexBuilder::build_with_auto_env()` is the default constructor so tests work when the app-server and exec OS differ. `wait_for_event` is preferred over timeout variants; `mount_sse_once` is preferred over match/sequence helpers.
- TUI tests are snapshot-based with `insta`. UI changes must update snapshots via `just test -p codex-tui`, then `cargo insta pending-snapshots -p codex-tui` and `cargo insta accept -p codex-tui`. Helpers live beside views; `vt100` tests are feature-gated (`vt100-tests`).
- New test modules must be sibling files included with `#[cfg(test)] #[path = "..._tests.rs"] mod tests;`, not inline modules. Assertions should use `pretty_assertions::assert_eq` on whole objects. Tests must not mutate process environment.
- Sandbox and network tests early-exit when `CODEX_SANDBOX=seatbelt` or `CODEX_SANDBOX_NETWORK_DISABLED=1`. Agents running under the Codex sandbox are expected to skip those tests rather than weaken the checks. The env-var names are centralized and must not be edited casually.
- CI blocking path is `.github/workflows/blocking-ci.yml`: `cargo fmt -- --check`, a package-manager pin check, `cargo shear` (which builds with `bazel build //...`), and the reusable Bazel test workflow. `cargo-deny.yml` audits licenses and advisories.
- Python under `scripts/` is a separate uv project (`codex-scripts`, requires Python >= 3.12). It is used for release and maintenance scripts, not the agent loop. `python-runtime-build.yml` and `python-runtime-release.yml` build the standalone Python runtime shipped for some Codex features.
- Exec and login have crate-local tests (`codex-rs/exec`, `codex-rs/login`). SDK TypeScript tests live next to `sdk/typescript` and mock the stdio JSON-RPC transport rather than spawning a real model. There is no single cross-language end-to-end suite in CI that hits the live OpenAI API.
- Coverage gaps called out by repo policy: agent behavior changes must add a `core/suite` integration test; unit tests of statically defined tables are discouraged; negative tests for deleted logic are discouraged. UI without an insta snapshot is considered incomplete.
Risk map
The highest-risk surfaces are command execution, the sandbox policy resolver, auth token storage, and the app-server stdio API that can drive all of the above. `codex-core` is an explicit blast-radius hotspot the repo asks contributors not to grow. Large orchestration files (`chatwidget.rs`, `app.rs`, `chat_composer.rs`, `codex.rs`) are fragile coupling points. History compaction and context fragments can invalidate the model prompt cache or exceed token caps. Sandbox escapes are the security property the project exists to protect; tests often skip them under nested sandbox. Legacy v1 app-server methods still exist and must not gain new surface.
- Shell-out is the primary security boundary. Tool handlers spawn user-model-supplied commands. Safety depends on `SandboxPolicy`, execpolicy allow/deny, and the platform sandbox all agreeing. A mismatch between the approval overlay and the spawn path is a command-execution bug.
- `danger-full-access` and approval `never` disable the sandbox and prompts. Config, CLI flags, and execpolicy can each select this. Any new flag that defaults toward this combination widens blast radius for every SDK and CI caller.
- Auth secrets live in `$CODEX_HOME/auth.json` (default `~/.codex/auth.json`) and may also be supplied by `CODEX_API_KEY`. The file contains access and refresh tokens. Log paths and debug traces that dump request headers would leak them.
- The macOS sandbox uses Seatbelt (`sandbox-exec`) profiles built in `sandboxing/src/seatbelt.rs`. Tests that themselves call Seatbelt exit early when `CODEX_SANDBOX=seatbelt`, so nested-agent runs do not exercise the real profile. Profile edits are easy to ship untested.
- Linux isolation depends on `codex-linux-sandbox` (bubblewrap) plus Landlock. If bwrap is missing, the core falls back according to config (`sandbox_mode` and network flags). A silent downgrade is a security bug; the code path is in `core/src/sandboxing/mod.rs`.
- Network access inside the sandbox is denied by default and selectively re-enabled. The env var `CODEX_SANDBOX_NETWORK_DISABLED` is also used by the agent harness to skip networked tests. AGENTS.md forbids modifying code related to `CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR` or `CODEX_SANDBOX_ENV_VAR`.
- App-server stdio is a local trust boundary, not a remote auth boundary. Any process that can spawn `codex app-server` can start turns, exec commands, and read thread history. Experimental methods are gated but still compiled in.
- TUI orchestration files are called out as high-touch and should not grow: `tui/src/app.rs`, `bottom_pane/chat_composer.rs`, `bottom_pane/footer.rs`, `chatwidget.rs`, `bottom_pane/mod.rs`. New behavior belongs in a new module. These files are the most likely merge-conflict and regression sites.
- Context rules are load-bearing for cost and correctness: no history rewrite, no unbounded injected items, hard cap, and a P0 review if a fragment can exceed 1k tokens. Compact and new system fragments are the usual way to violate this.
- File IO outside the repo is normal and expected: `CODEX_HOME` (`~/.codex`), session JSONL, shell cwd, and apply_patch targets. Workspace-write mode still allows writes anywhere inside the writable roots derived from cwd and config `writable_roots`. Path canonicalization bugs become arbitrary-write bugs.
- Change-size guidance caps non-mechanical diffs at about 800 lines and complex logic at 500. Oversized patches to `codex.rs` or `chatwidget.rs` are the usual review failure mode, not a compiler failure.
Extension points
Codex is extended by adding a tool spec and handler, an MCP server entry, a feature flag, a slash command, a CLI subcommand, or an app-server v2 method. Model providers plug in through `WireApi` and config `model_providers`. Skills, execpolicy rules, and sandbox policies are user-level extension points that do not require a fork. New crates are preferred over new modules in `codex-core`. Protocol extensions must update serde and ts-rs annotations together and regenerate schema fixtures.
- TUI slash commands are the `SlashCommand` enum and its parse table in `tui/src/slash_command.rs`. Adding a command means a variant, a description, a dispatch arm in the composer/chat widget path, and an insta snapshot.
- CLI subcommands are the `Subcommand` enum in `cli/src/main.rs` (clap). New top-level commands belong there only if they are user-facing binaries; library behavior belongs in a crate the CLI calls.
- Model providers extend via config `[model_providers.<id>]` (base_url, wire_api `responses` or `chat`, env_key). `ModelClient` switches on `WireApi`. A new HTTP shape requires a client arm, not a TUI change.
- Execpolicy is a user-authored policy language (`prefix_rule`, pattern rules) interpreted by the `codex-execpolicy` crate. New rule kinds are crate changes; new rules of existing kinds are config-only.
- Context injection extends through `ContextualUserFragment` implementors under `core/src/context`. New fragments must be bounded structs. This is the only supported way to add text the model sees every turn.
- Sandbox policies extend by adding a `SandboxPolicy` variant or a platform executor. macOS profiles are strings in `seatbelt.rs`; Linux uses the `linux-sandbox` crate; Windows uses the Windows sandbox crate. All three must stay behaviorally aligned.
- Internal agent roles and graphs (`codex-agent-roles`, `codex-agent-identity`, `codex-agent-graph-store`) are separate workspace crates for multi-agent structure, not part of the CLI multitool match. They are the intended home for new agent-graph concepts instead of `codex-core`.
- Config schema is generated. After any `ConfigToml` or nested config type change, `just write-config-schema` rewrites `codex-rs/core/config.schema.json`. Docs that mirror keys live in `docs/config.md` and `docs/example-config.md`.
Contributor handoff
Work in `codex-rs` unless the task is the Node launcher or the TS/Python SDK. Read AGENTS.md before editing. Prefer a new crate or a new module over growing `codex-core`, `chatwidget.rs`, or `app.rs`. Run `just fmt` after edits and `just test -p <crate>` for the crate you touched. Do not hand-edit generated schema without the just recipe. Do not touch sandbox env-var constants. Match existing clap, serde, and ratatui styles. Integration tests, not unit tests, are required for agent-loop changes.
- Style traps: inline `format!` args; collapse nested ifs; prefer method references; no `#[async_trait]` or `#[allow(async_fn_in_trait)]` — use RPITIT with `impl Future<Output = T> + Send`. Avoid boolean or ambiguous `Option` parameters. Positional `None`/bool/number literals need `/*param_name*/` comments.
- TUI style traps: use ratatui `Stylize` (`"text".dim()`, `.red()`) rather than `Span::styled`. Do not use `.white()`. Wrap plain strings with `textwrap::wrap` and ratatui lines with `tui/src/wrapping.rs`. Prefix lines with `prefix_lines` from line_utils. UI changes need insta snapshots.
- Do not edit `CODEX_SANDBOX_NETWORK_DISABLED` or `CODEX_SANDBOX` plumbing. Those constants exist so tests skip when the agent harness has already sandboxed the process. Changing them breaks CI and the sandbox contract.
- Module size trap: files over ~800 LoC should gain a new module, not more methods. `chatwidget.rs` in particular should not grow standalone methods. When extracting, move the tests and the doc comment with the code.
- Tracing trap: instrument the function definition with `#[tracing::instrument]`, not `.instrument()` at the callsite, and do not double-instrument a callee that already has the attribute.
- Test authoring trap: agent behavior belongs in `core/suite` using `mount_sse_once` and `pretty_assertions`. Do not add tests that only assert static tables, do not add negative tests for removed logic, and do not put test-only helpers on the public crate API.
- Docs trap: do not add product docs under `docs/` except app-server API docs. Contributor rules live in `AGENTS.md`; user docs (`config.md`, `sandbox.md`, `exec.md`, `authentication.md`) are the existing canonical pages and should be updated in place rather than duplicated.
Languages
| Language | Files | Share of source |
|---|---|---|
| Rust | 4739 | 79% |
| TypeScript | 764 | 13% |
| JSON | 367 | docs/config |
| Python | 198 | 3% |
| Markdown | 182 | docs/config |
| TOML | 177 | 3% |
| SQL | 71 | 1% |
| YAML | 60 | docs/config |
Start reading here
Probable source entrypoint: codex-rs/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
codex-rs/ (3494 files) sdk/ (121 files) third_party/ (60 files)
Source layout — codex-rs/
| Path | Files |
|---|---|
codex-rs/agent-graph-store/ | 6 |
codex-rs/agent-identity/ | 2 |
codex-rs/agent-roles/ | 5 |
codex-rs/analytics/ | 28 |
codex-rs/ansi-escape/ | 3 |
codex-rs/app-server-client/ | 5 |
codex-rs/app-server-daemon/ | 31 |
codex-rs/app-server-protocol/ | 55 |
codex-rs/app-server-protocol-noop-macros/ | 2 |
codex-rs/Cargo.toml | 1 |
codex-rs/README.md | 1 |
Repository structure
| Top-level path | Files |
|---|---|
codex-rs/ | 3494 |
sdk/ | 121 |
.github/ | 113 |
third_party/ | 60 |
scripts/ | 58 |
patches/ | 31 |
tools/ | 30 |
bazel/ | 23 |
Docs found: AGENTS.mdREADME.mddocs/CLA.mddocs/agents_md.mddocs/authentication.mddocs/config.mddocs/contributing.mddocs/example-config.mddocs/exec.mddocs/execpolicy.md
Package
package.json — codex-monorepo, 1 declared dependencies.
prettier
CI & config entrypoints
README.md— Repository readmeAGENTS.md— Agent instructions.github/workflows/bazel.yml— GitHub Actions workflow.github/workflows/blob-size-policy.yml— GitHub Actions workflow.github/workflows/blocking-ci.yml— GitHub Actions workflow.github/workflows/cargo-deny.yml— GitHub Actions workflow.github/workflows/cla.yml— GitHub Actions workflow.github/workflows/close-stale-contributor-prs.yml— GitHub Actions workflow.github/workflows/codespell.yml— GitHub Actions workflow.github/workflows/issue-deduplicator.yml— GitHub Actions workflow
Structural health signals
Average score 5/10 across 96 analyzed source files; 96 hotspots surfaced.
codex-rs/cli/src/doctor.rs— 1419 lines, score 4 (Large file, Deep nesting)codex-rs/core/src/realtime_conversation.rs— 1393 lines, score 4 (Large file, Deep nesting)codex-rs/app-server-test-client/src/lib.rs— 1346 lines, score 4 (Large file, Deep nesting)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
a1f40f3f13262026-09-22 — Expose hosted plugin extensions in app-server summaries (#47263)d3e5840932222026-09-22 — Subscribe authors to message-board channels created by posting (#47259)3d5cdc51d00c2026-09-22 — Test spawn settings reporting after child removal (#47258)0160eb3497bc2026-09-22 — Preserve explicit message-board unsubscribes when posting (#47257)fdd89e78ac332026-09-22 — Return agent snapshots from `close_agent` (#47252)