Skip to content
预计 1 分钟右侧目录可快速定位

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 源码阅读规则 ​

源码解析严格分三层:

  1. Source Fact:源码直接能证明什么。必须能落到固定 tag、file、symbol 和调用链。
  2. Design Inference:从多个 Source Fact 可以合理提炼出的通用 Harness invariant。
  3. Product-specific Choice:哪些只是 Codex rust-v0.150.1 的产品级实现选择,不能提升为行业标准。

禁止从 文件名、类型名、变量名 直接推导通用架构定义。看到 Session、Turn、Sandbox、Thread 等名字时,先追 lifecycle、ownership、state transition 和 authority,再决定能否与本知识库 Domain Language 建立映射。

每一个 Codex Case 必须回答:

  1. 看的是哪个 tag、哪个 file、哪个 symbol?
  2. 实际调用链是什么?
  3. 源码明确证明了什么?
  4. 能提炼什么通用 Harness invariant?
  5. 哪些只是 Codex 当前实现的 trade-off?
  6. 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。

Case固定源码核心问题
Turn lifecycle / granularitycodex-rs/core/src/session/turn.rs一个 Codex Turn 为什么可能包含多次 sampling / tool continuation?
Public SDK boundarysdk/typescript/src/codex.ts, sdk/typescript/src/thread.ts, sdk/typescript/src/threadOptions.ts, sdk/typescript/src/exec.ts内部 Runtime 很复杂时,对外 API 如何仍保持 Thread / Turn / run / resume / config 的小表面?
Tool normalization / dispatchcodex-rs/core/src/stream_events_utils.rs, codex-rs/core/src/tools/router.rs, codex-rs/core/src/tools/parallel.rs, codex-rs/core/src/tools/registry.rsProvider output 怎样失去直接执行权,变成内部 domain call 再由 Runtime resolve?
Unified exec side effectcodex-rs/core/src/tools/handlers/unified_exec.rs, codex-rs/core/src/tools/handlers/unified_exec/exec_command.rs一个已经过 Router 的 shell action 怎样变成受控 OS side effect?
Context projection / compactioncodex-rs/core/src/context_manager/history.rs, codex-rs/core/src/session/context_window.rs, codex-rs/core/src/session/turn.rs, codex-rs/core/src/tasks/compact.rssource state 怎样投影成 model request;pre-turn 与 mid-turn compaction 分别保护什么?
Runtime / Session ownershipcodex-rs/core/src/session/session.rs, codex-rs/core/src/state/session.rs, codex-rs/core/src/state/turn.rs哪些状态属于 Session lifetime,哪些属于 active Turn,哪些只是 model projection?
Approval runtime statecodex-rs/core/src/state/turn.rs, codex-rs/core/src/exec_policy.rs, codex-rs/core/src/tools/sandboxing.rsapproval request / result 是什么 runtime state,resume 时为什么不能只记一个 bool?
Authorization + sandbox containmentcodex-rs/core/src/exec_policy.rs, codex-rs/core/src/tools/sandboxing.rs, codex-rs/sandboxing/src/manager.rsAuthorization / Approval 与 Sandbox Containment 为什么概念不同、实现却共同参与一次 execution decision?

三条关键调用链 ​

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 complete

run_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 request

ContextManager::for_prompt() 会 normalize history,并按模型输入 modality 丢弃或转换不适合发送的内容。因此“Runtime 中真实保存了什么”和“这一次真正发送给模型什么”是两个层次。

Unified Exec:从 ToolCall 到 OS side effect ​

固定源码:

最小链:

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 sampling

Mid-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 sampling

Source Fact 能证明:Runtime 决定什么时候触发 compact,以及 compact 后是否继续 sampling。

它不能证明:

text
Runtime triggered compact
= summary is semantically lossless

summary 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 decision

ExecPolicyManager::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(...)
  • SandboxType
  • SandboxTransformRequest
  • SandboxExecRequest

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.1Shadow Harness对照原则
SessionStateRunState / Session store 中的部分职责只比较 lifetime/authority;不能说二者等价
TurnStateTurnState 中的 active-control 部分名字相同也不自动等价,先比较 pending state 与 transition owner
ContextManager::for_prompt() 输出ContextSnapshot / request projection这是 model-visible projection,不是全部 Runtime source state
SessionConfiguration / permission profile / tool registryRuntime authority / policy config模型可以感知部分配置,但不能拥有这些 authority

Public SDK Boundary ​

固定看 TypeScript SDK:

对外暴露的主要概念很少:

text
Codex
→ startThread(options)
→ resumeThread(id, options)

Thread
→ run(input, turnOptions)
→ runStreamed(input, turnOptions)

Turn result
→ items
→ finalResponse
→ usage

sandboxMode、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 只读这部分
W1session/turn.rs:先纠正 Turn 粒度,再看 sdk/typescript 的 public boundary
W3stream_events_utils.rs → router.rs → parallel.rs → registry.rs,再下钻 unified exec 两个文件
W4ContextManager::for_prompt + context_window_token_status + pre-turn / mid-turn compaction
W6session/session.rs + state/session.rs + state/turn.rs:按 owner / lifetime / authority 画图
W8exec_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。

AI Engineering · Agent · Harness · Backend Systems