Appearance
OpenAI Codex Source Map
本页把 OpenAI 官方
openai/codex仓库作为 First-party Engineering Case Study 接入本知识库。它用于理解 production coding-agent / Harness 的实现边界,不承担规范定义。源码引用固定到稳定版
rust-v0.150.1(2026-08-27)。仓库采用 Apache-2.0;这里研究的是开源 CLI / Runtime / SDK / Tool / Sandbox 等工程实现,不把背后的 OpenAI 模型权重、训练系统或服务端实现视为开源内容。
Codex 源码阅读规则
源码解析严格分三层:
- Source Fact:源码直接能证明什么。必须能落到固定 tag、file、symbol 和调用链。
- Design Inference:从多个 Source Fact 可以合理提炼出的通用 Harness invariant。
- Product-specific Choice:哪些只是 Codex
rust-v0.150.1的产品级实现选择,不能提升为行业标准。
禁止从 文件名、类型名、变量名 直接推导通用架构定义。看到 Session、Turn、Sandbox、Thread 等名字时,先追 lifecycle、ownership、state transition 和 authority,再决定能否与本知识库 Domain Language 建立映射。
每一个 Codex Case 必须回答:
- 看的是哪个 tag、哪个 file、哪个 symbol?
- 实际调用链是什么?
- 源码明确证明了什么?
- 能提炼什么通用 Harness invariant?
- 哪些只是 Codex 当前实现的 trade-off?
- Shadow Harness 因此要增加、修改或验证什么?
只得到“Codex 这里也这么做”不算完成源码阅读。
Domain Mapping Rule
Codex 类型名不自动等于本知识库 Domain Language。
尤其要避免下面这个错误:
text
Codex Turn
= canonical Turn本知识库 canonical Turn 表示一次逻辑上的:
text
Model Decision
→ Action / Observation而 Codex rust-v0.150.1 的 session/turn.rs::run_turn() 本身包含 sampling loop:一次 Codex Turn 可能经历多次:
text
model sampling
→ tool execution
→ tool result
→ next sampling
→ ...
→ final assistant message因此一个 Codex Turn 可能覆盖多个 canonical logical Turn,而且也不能机械按 sampling 次数建立 1:1 映射,因为一次 model response 还可能包含多个 output item。
原则:不按类型名直接建立概念映射,应比较 lifecycle、ownership、state transition 和 authority。
为什么值得读
Codex 与 Grok Bot case 的角色不同:
- Codex:OpenAI 官方、持续演进的 coding-agent Runtime,一手实现更适合验证 Agent loop、Tool Runtime、Session/Turn、Context projection、Compaction、Approval/Sandbox 和 public SDK boundary;
- Grok Bot Reconstruction:更适合观察复杂产品层的 Host / Coordinator / Renderer / MCP / Durable Policy 如何组合。
两者都只作为实现案例。先学 canonical concept,再提出具体问题,再读最小相关调用链;不要按仓库目录从头漫游。
本轮固定阅读面
这一轮不继续扩大 Codex 覆盖文章,只把已有 7 个落点做深;Tool 与 Foundations 内各增加一个必要的纵向 Case,用来补齐 unified exec 与 public SDK boundary。
三条关键调用链
1. Codex Turn 不是一次 model decision
text
run_turn(input)
→ pre-sampling compaction check
→ context update / first StepContext
→ loop
→ clone history
→ for_prompt(...)
→ run_sampling_request(...)
→ ResponseItem(s)
→ tool execution if needed
→ post-sampling token status
→ optional mid-turn compaction
→ continue when follow-up is required
→ final assistant message / stop condition
→ Turn completerun_turn() 顶部注释直接说明:如果模型请求 function call,Runtime 执行后会把 output 放到 下一次 sampling request;只有只返回 assistant message 等终止条件满足时,Codex Turn 才完成。
这意味着知识库中的:
text
canonical Turn
= one logical Model Decision → Action/Observation cycle不能直接用 Codex Turn 类型名替代。
2. Provider output 不能直接获得 Tool 执行权
最小真实调用链:
text
ResponseEvent::OutputItemDone(ResponseItem)
→ handle_output_item_done(...)
→ ToolRouter::build_tool_call(ResponseItem)
→ normalized ToolCall
→ ToolCallRuntime::handle_tool_call(...)
→ ToolRouter::tool_runtime(&ToolCall) / ToolRegistry::tool(...)
→ ToolRouter::dispatch_tool_call_with_terminal_outcome(...)
→ ToolRegistry::dispatch_any_with_terminal_outcome(...)
→ concrete CoreToolRuntime / ToolExecutor
→ AnyToolResult
→ AnyToolResult::into_response()
→ ResponseInputItem
→ next sampling必须明确三层不是一回事:
text
Provider output
≠ Domain ToolCall
≠ Executable Runtime通用 invariant:Provider 返回的 function/tool item 不能直接获得执行权,必须先规范化为内部 domain call,再经 registry/runtime resolution、policy/hook/handler boundary 后执行。
3. Source-of-truth state 不等于 model context
text
Session / SessionState
→ history: ContextManager + runtime-owned state
→ context/world-state updates
→ clone_history()
→ ContextManager::for_prompt(input_modalities)
→ normalized / filtered request projection
→ run_sampling_request(...)
→ model requestContextManager::for_prompt() 会 normalize history,并按模型输入 modality 丢弃或转换不适合发送的内容。因此“Runtime 中真实保存了什么”和“这一次真正发送给模型什么”是两个层次。
Unified Exec:从 ToolCall 到 OS side effect
固定源码:
codex-rs/core/src/tools/handlers/unified_exec.rscodex-rs/core/src/tools/handlers/unified_exec/exec_command.rs- cancellation / dispatch gate 补充看
codex-rs/core/src/tools/parallel.rs
最小链:
text
ToolInvocation(Function arguments)
→ ExecCommandHandler::handle_call(...)
→ parse environment/workdir
→ resolve_tool_environment(...)
→ parse ExecCommandArgs
→ resolve_sandbox_permissions(...)
→ apply_granted_turn_permissions(...)
→ approval / permission checks
→ allocate_process_id()
→ get_command(...)
→ ExecCommandRequest
→ UnifiedExecProcessManager::exec_command(...)
→ output / sandbox denial / error
→ ToolOutput并行 Runtime 在 ToolCallRuntime::handle_tool_call_with_source() 中用 cancellation token 与 dispatch task 竞争;取消时会 abort dispatch 并生成 aborted result。这说明真正的 side effect 不只是“Router 找到 handler”,还包括 execution admission、environment、permission、process lifecycle 与 cancellation。
不要过度推导:上述两个 unified-exec 文件能证明 Runtime 如何把请求交给 UnifiedExecProcessManager,但不能单凭这里断言所有平台最终使用哪一个具体 spawn syscall / sandbox backend;那需要继续追 process manager 和 platform executor。
Compaction:Pre-turn protection + Mid-turn continuation protection
Pre-turn / pre-sampling
text
run_turn(...)
→ run_pre_sampling_compact(...)
→ context_window_token_status(...)
→ token_limit_reached?
→ run_auto_compact(..., CompactionPhase::PreTurn)
→ normal samplingMid-turn
text
sampling
→ tool / response completion
→ context_window_token_status(...)
→ needs_follow_up + threshold / new-context request
→ should_roll_over
→ run_auto_compact(..., CompactionPhase::MidTurn)
→ continue
→ next samplingSource Fact 能证明:Runtime 决定什么时候触发 compact,以及 compact 后是否继续 sampling。
它不能证明:
text
Runtime triggered compact
= summary is semantically losslesssummary fidelity 仍必须通过 protected constraints、pending actions、artifact/reference、critical failure history 等回归测试验证。
Approval / Sandbox:概念边界不等于源码目录边界
概念层:
text
Authorization / Approval
≠ Sandbox Containment实现层并不是“一文件一概念”:
text
exec_policy.rs
+ core/tools/sandboxing.rs
+ sandboxing/src/manager.rs
+ active Turn state
→ one execution decisionExecPolicyManager::create_exec_approval_requirement_for_command() 会把 policy evaluation 映射为:
text
ExecApprovalRequirement::Skip
ExecApprovalRequirement::NeedsApproval
ExecApprovalRequirement::Forbidden这些是 Runtime 的 policy result。core/src/tools/sandboxing.rs 定义 approval primitives、sandbox override/attempt,并通过 SandboxAttempt::env_for(...) / env_for_exec_server(...) 调用 codex_sandboxing::SandboxManager::transform(...)。SandboxManager 的真实定义位于 codex-rs/sandboxing/src/manager.rs,其中 select_initial(...) / should_sandbox(...) 负责 sandbox 选择判定,transform(...) 把 SandboxTransformRequest 转成 host-native SandboxExecRequest。
所以教学上必须分清:
- Approval / Authorization:这次动作是否被允许、是否需要人或 policy 授权;
- Sandbox Containment:动作真的运行时,文件、网络、进程等能力边界在哪里;
- 源码组织:为了工程复用,两类机制可能在多个文件里交叉协作。
概念边界不等于源码目录边界。
Sandbox Manager 精确定位
Tag: rust-v0.150.1
File:
Symbols:
SandboxManager::select_initial(...)SandboxManager::should_sandbox(...)SandboxManager::transform(...)SandboxTypeSandboxTransformRequestSandboxExecRequest
codex-rs/sandboxing/src/lib.rs 只是把这些 manager 类型 re-export 给 codex_sandboxing::* 使用;阅读调用点时不要因此误判定义位置。
Runtime ownership 图
源码可以支持下面这个 ownership 视角:
text
Session
│
├─ SessionState # session lifetime mutable state
│ ├─ history: ContextManager
│ ├─ token / auto-compact window state
│ ├─ latest_rate_limits
│ ├─ previous_turn_settings
│ ├─ granted permissions by environment
│ └─ ...
│
└─ active_turn: ActiveTurn
├─ RunningTask / cancellation
└─ TurnState # single active-turn mutable state
├─ pending approvals / permissions / user input
├─ pending input / mailbox phase
├─ turn-granted permissions
├─ tool_calls
└─ token_usage_at_turn_start
run_turn(...)
└─ sampling → tool execution → next sampling → completion和 Shadow Harness 对照时只对职责,不对名字:
Codex rust-v0.150.1 | Shadow Harness | 对照原则 |
|---|---|---|
SessionState | RunState / Session store 中的部分职责 | 只比较 lifetime/authority;不能说二者等价 |
TurnState | TurnState 中的 active-control 部分 | 名字相同也不自动等价,先比较 pending state 与 transition owner |
ContextManager::for_prompt() 输出 | ContextSnapshot / request projection | 这是 model-visible projection,不是全部 Runtime source state |
SessionConfiguration / permission profile / tool registry | Runtime authority / policy config | 模型可以感知部分配置,但不能拥有这些 authority |
Public SDK Boundary
固定看 TypeScript SDK:
sdk/typescript/src/codex.tssdk/typescript/src/thread.tssdk/typescript/src/threadOptions.tssdk/typescript/src/exec.ts
对外暴露的主要概念很少:
text
Codex
→ startThread(options)
→ resumeThread(id, options)
Thread
→ run(input, turnOptions)
→ runStreamed(input, turnOptions)
Turn result
→ items
→ finalResponse
→ usagesandboxMode、approvalPolicy、working directory、network access 等以 configuration surface 暴露;CodexExec.run() 再把它们投影成 CLI argument / config 并启动 Codex executable。内部的 SessionState、TurnState、ToolRegistry、compaction loop、approval store、process manager 并没有直接泄漏给 SDK 使用者。
通用设计启发是:Harness 内部可以很复杂,但 public API 应保持稳定、少量、语义清晰。 这是一条 Design Inference,不意味着所有 Harness 都必须采用 Thread/Turn 这套名称。
13 周对应位置
| 周 | Codex 只读这部分 |
|---|---|
| W1 | session/turn.rs:先纠正 Turn 粒度,再看 sdk/typescript 的 public boundary |
| W3 | stream_events_utils.rs → router.rs → parallel.rs → registry.rs,再下钻 unified exec 两个文件 |
| W4 | ContextManager::for_prompt + context_window_token_status + pre-turn / mid-turn compaction |
| W6 | session/session.rs + state/session.rs + state/turn.rs:按 owner / lifetime / authority 画图 |
| W8 | exec_policy.rs + tools/sandboxing.rs:追 Skip / NeedsApproval / Forbidden 到 execution boundary |
| W11 | 用 unified exec + sandbox attempt 验证 permission / containment,不扩大到更多文章 |
| W13 | 回看上述调用链,和 Shadow Harness 做 invariant / trade-off / delta architecture diff |
每次源码阅读的输出格式
text
Source Fact
固定 tag + file + symbol + 调用链能直接证明什么
Design Inference
哪条 invariant 可以跨实现复用
Product-specific Choice
哪些只是 Codex 当前实现
Shadow Harness Delta
我们应该增加/修改/验证什么
Do-not-over-infer
这组源码不能证明什么版本升级时先更新本页固定 tag,再校验所有路径/符号;禁止固定 rust-v0.150.1 却引用只在 main 存在的文件,也不要静默把链接改成 main。