第 19 章 Coding Agent 系统
成熟的 Coding Agent 不是“会改代码的模型”,而是一个持续组织上下文、调度工具、约束副作用并验证结果的工程运行时。同一个运行时在不同人手里表现差异很大,差别主要来自使用者提供的任务、上下文、边界和验证。
资料核验日期:2026-10-22。 本章围绕三个产品展开:OpenAI 的 Codex CLI、Anthropic 的 Claude Code,以及开源项目 OpenCode(只讨论 2.x 版本线)。前四部分——组成、启动、执行、异常——讲这类系统怎样实现;后两部分——实践、调优——讲怎样用好它。每个讲实现的部分都以一张三产品对照表开头;实践与调优可以单独阅读,遇到机制名词时回到前文对应小节。
组织强制策略与默认配置的 Codex 官方说明于 2026-10-29 补核,范围仅限下文标注的新增来源;其他产品事实与固定源码保留各自核验日期。
组成部分于 2026-10-30 对照第 17 章的架构与核心对象补充调研:重点复核 Codex 的对象与活动状态、OpenCode 的执行声明与恢复,以及 Claude Code 的会话和 Checkpoint 边界。新增的组件分工、通用对象映射与状态迁移表属于系统推导,不宣称三款产品实现了第 17 章的全部可靠性协议;未涉及的产品事实保留原核验日期。
本章是领域特殊案例:仓库、patch、测试和 verifier 是代码任务独有制品,ALR 的只读告警诊断不能替代真实代码修改。它只用于第 19 章比较 Coding Agent;其中任务输入、代码证据、编辑/命令动作、会话状态和测试验收,分别对照 ALR 的告警输入、指标/日志/Runbook、只读工具与审批后建单、Run 状态和诊断报告验收。两类案例不得复用彼此的标识或授权边界。
三个产品的公开程度不同,本章的结论因此带有不同的证据等级。
| 标记 | 含义 | 使用方式 |
|---|---|---|
| 源码事实 | 公开仓库固定提交中可定位的文件与符号 | Codex 固定在 86e17e98c7f9(提交时间 2026-10-22 06:32 UTC),OpenCode 固定在 c93aaae37157(包版本 2.1.2);不引用主分支 |
| 官方文档事实 | 官方文档页的陈述 | 文档滚动更新、没有版本快照,一律注明核验日期;文档写明版本门槛的,保留“vX 及以后” |
| 系统推导 | 根据公开证据做的架构解释 | 帮助理解,不等于产品内部的精确实现 |
| 本书建议 | 本书依据前述事实归纳的使用建议 | 只出现在实践与调优两部分,与官方建议分开标注 |
Claude Code 没有公开核心源码,本章对它只引用官方文档,不使用逆向、泄露代码或社区猜测。官方没有使用“闭源”一词,仓库许可证只声明版权所有并指向商业条款,因此本章写作“核心程序未按开源许可证公开”。
还有一条限制需要提前说明:本章的行为结论来自阅读源码与官方文档,没有经过运行验证或故障注入。文档与源码不一致的地方,分别说明文档承诺和固定提交实现;固定提交未必对应读者安装的发行版;只能在源码里找到、文档没有承诺的行为,会标注“源码事实,非文档承诺”。版本敏感的内容(默认权限模式、命令名、配置键、模型名与别名)一律写明核验日期,正文不硬写模型名。
本章用同一组问题比较三个产品:谁控制状态,启动怎样形成上下文,执行怎样推进,异常怎样收敛,实践中怎样授权,调优怎样验证。第 20–22 章沿用这六维度,分别深入 Pi、OpenClaw 和 Hermes。
| 维度 | 本章应交付的判断 | 通用机制主位置 |
|---|---|---|
| 组成 | 组件怎样协作、核心对象归谁管理、事实存在哪里、哪些事件推进状态 | Runtime 架构 |
| 启动 | 规则、能力、身份怎样进入首轮 | 上下文工程 |
| 执行 | 模型提案与确定性执行在哪里分界 | 工具系统 |
| 异常 | 取消、恢复与未知外部效果怎样解释 | 副作用与核验 |
| 实践 | 哪些条件下可以授予哪些能力 | 治理控制点 |
| 调优 | 假设、变量与证据是否支持变更 | 评估闭环 |
19.1 组成:架构、组件、核心对象、存储与状态机
阅读这一节,可以依次回答五个问题:请求进入哪个宿主;哪些组件决定和执行下一步;会话、轮次、工具调用与工作区如何关联;哪些事实能在重启后保留;哪些事件可以改变执行状态。第 17 章提供通用 Runtime 的参考设计,本节解释这些职责在 Coding Agent 中的具体落点及公开证据的边界。
19.1.1 从代码生成到可审查交付
普通代码生成器的产物是文本片段。Coding Agent 的产物存在于仓库和执行环境里:它读取文件,向模型发起请求,调用工具修改代码、运行命令,再把结果交给人审查。模型的回答只是交付说明,不能代替真实的文件差异和验证结果。
三个产品可以抽象成同一条工作路径。这是本书用于分析的共同模型,不是任何产品的源码模块图:
flowchart LR
A[用户任务] --> B[建立会话和任务上下文]
B --> C[读取规则与代码证据]
C --> D[模型选择下一步]
D --> E{需要工具吗?}
E -- 是 --> F[权限裁决与工具执行]
F --> G[记录结果并反馈给模型]
G --> D
E -- 否 --> H[候选完成]
H --> I[检查差异和验证证据]
I --> J[向用户交付]
这条路径涉及四层事实,每一层都有一个容易混淆的地方:
| 层次 | 核心问题 | 不能混为一谈的事实 |
|---|---|---|
| 任务与上下文 | 这一轮模型要解决什么、能看到哪些信息? | 磁盘文件存在,不代表已进入上下文 |
| 模型与工具 | 模型提出什么行动,运行时如何执行? | 模型生成工具调用,不代表工具已经成功 |
| 权限与隔离 | 哪些动作可以执行,动作影响到什么范围? | 获得审批不等于进程被沙箱约束 |
| 验证与交付 | 哪些证据足以支持“完成”? | 模型说测试通过,不等于测试进程实际成功 |
一次模型响应可以只含解释,也可以含一个或多个工具请求。运行时要把工具请求解析为可执行操作,检查参数与权限,调用本地或外部能力,把结果归入会话,再决定是否继续采样。所以主循环是一个受控状态机,不是单次提示词调用。工具可能失败、超时、被用户拒绝或被中断,可靠的运行时要把这些情况作为不同结果回送给模型,而不是把失败输出包装成成功;取消也不会撤回已经发生的外部副作用,远端请求、数据库写入和命令进程各有各的恢复语义。这些问题在 19.4 逐一展开。
19.1.2 三个产品与证据基础
| 项目 | Codex CLI | Claude Code | OpenCode(2.x) |
|---|---|---|---|
| 源码可见性 | CLI、SDK、App Server 等开源;IDE 扩展与 Codex 云端不开源;openai/codex 仓库不接受外部代码贡献[1] | 核心程序未按开源许可证公开:仓库 LICENSE 只声明版权所有并指向商业条款,仓库内是文档、示例、插件与变更记录;沙箱运行时是单独的开源包[55][36] | 开源,MIT 许可证,默认分支 dev[69] |
| 本章的证据 | 固定提交的源码,加官方文档(learn.chatgpt.com) | 官方文档;更新记录在核验日最新为 2.1.308[56] | 固定提交的源码(包版本 2.1.2),加 2.x 官方文档 |
| 版本与地址风险 | 文档站已从 developers.openai.com 迁到 learn.chatgpt.com,旧地址永久重定向;旧的 /codex/security 现在指向另一个产品 Codex Security(漏洞扫描),不是沙箱与审批页[8] | 默认值与行为带版本门槛,模型别名随版本变化 | 1.x 与 2.x 命令名相同;2.x 有三处破坏性变更:插件 API、服务端 API 与客户端契约、终端配置改为单个 cli.json;2.x 仍读取 1.x 的配置位置并在内存中归一化[59] |
| 官方使用建议 | 有最佳实践指南[19] | 有最佳实践页与成本页[32][33] | 2.x 文档中未见最佳实践或调优专页,只有功能文档 |
这张表也说明了开源与可审查的关系。源码可见提升了实现的可审计性,但代码存在不代表部署时启用了对应配置,默认功能也不代表每个版本、每个入口的行为一致;源码审查替代不了运行验证、权限测试和故障演练。闭源产品也并非不可分析:官方行为文档、协议说明和配置接口足以支撑严谨的系统比较,前提是把可观察事实和内部猜测分开。Claude Code 一栏的证据上限就是官方文档,本章不越过它。
固定提交并不是形式要求。Codex 固定提交的时间是 2026-10-22 06:32 UTC,核验当天主分支已经前进了约 4 小时,其中被本章引用的 state/turn.rs 当天就改了注释措辞(机制未变)[20]。如果引用主分支,同一个链接几小时后就可能不再支持原来的论断。
19.1.3 运行时组成对照
下表按职责归纳三个产品的公开组成。同一格里的职责不意味着同一个进程或同一个源码模块;Claude Code 一列只描述官方文档公开的行为,不推测私有模块。
| 职责 | Codex CLI | Claude Code | OpenCode 2.x |
|---|---|---|---|
| 入口与交互 | TUI、codex exec 与外部客户端。固定提交中 TUI 与 exec 以进程内方式承载 App Server 客户端;外部客户端经 stdio、Unix socket 或 WebSocket 连入[2][25] | 终端、桌面、IDE、claude.ai/code、Remote Control、Slack、CI/CD;官方称各界面共享同一个循环[31] | TUI、Mini、非交互 run、桌面、网页(pair);默认连接当前用户的共享后台服务[57] |
| 会话与执行对象 | 对外是 Thread → Turn → Item;核心里 Session 持有至多一个活动 Turn[2][20] | Session 保存在本地 JSONL;resume 沿用同一会话 ID 追加,fork 复制到新 ID[31][38] | 服务端持有 Session;执行以 drain 与 step 推进,开始时写入持久化的执行声明(claim)[71][72] |
| 配置与上下文 | 分层 config.toml(项目层需受信)、AGENTS.md 指令链[3][6] | 分层 Settings、CLAUDE.md、auto memory、按需加载的 Skills[41][40][43] | 全局、项目、系统配置;指令只认 AGENTS.md;Skills 仍兼容读取 .claude/skills[58][61][65] |
| 模型接入 | 模型提供商配置,含 --oss 本地提供商与 Bedrock 提供商[4] | Claude 模型,经 Anthropic API、云厂商平台或网关访问;缓存与推理发生在服务端[34][46] | 来自 models.dev 的 Provider 与模型目录;可接本地模型[60] |
| 扩展与工具 | Skills、MCP、子 Agent、Hooks、Rules(实验)[10][11][12] | Skills、MCP、Hooks、Subagents、Plugins[45] | Skills、MCP、Commands、Plugins、Agents;MCP 工具默认经 Code Mode 的 execute 命名空间调用[65] |
| 权限与实际执行 | 审批策略与沙箱模式分开配置;沙箱机制在独立 crate 中实现[7][21] | 权限模式与规则;沙箱默认关闭,只覆盖 Shell 命令[34][36] | 有序权限规则与只能收紧的 Policies;未发现内置 OS 沙箱,Shell 以宿主用户权限运行[63][64] |
| 持久化与恢复 | rollout 会话文件;thread/resume、fork、revert[2][4] | JSONL 会话与 Checkpoint(只覆盖文件编辑)[31][37] | Session API、快照与回退、共享服务模式下的重启续跑[66][68][71] |
运行时组成对照。虚线框表示逻辑职责分组,不代表三款产品具有相同的进程划分;框内卡片不表示调用顺序。Claude Code 一列采用官方行为文档,不推测私有模块。图中标注的源码与文档依据见本节相邻引用和章末参考资料,点击图片查看 SVG。
19.1.4 参考架构:控制面、执行面与组件协作
前面的组成图可以按第 17 章的组件分工重新阅读:控制面管理会话、执行资格、等待与终止,执行面组织上下文、调用模型和工具,存储保存继续执行所需的事实;工作区、模型服务和远端资源分别保有自己的权威状态。 这是职责划分,同一个进程可以同时承担两面;App Server 或共享后台服务也不天然等于完整的持久工作流引擎。
| 逻辑组件 | 输入与输出 | 管理的状态或事实 | 协作边界 |
|---|---|---|---|
| 客户端与协议适配 | 用户输入、审批答复 → 请求;事件 → 界面 | 连接、订阅、输入草稿与展示状态 | 展示是宿主状态的视图;连接断开不自行证明执行终止 |
| 会话与执行控制 | 开始、追加输入、中断 → 活动执行或等待 | 会话身份、活动轮次、待处理输入、取消信号 | 决定哪一轮可以推进,关联上下文构造、工具结果和持久记录 |
| 配置与能力装配 | 分层配置、指令、能力定义 → 生效配置与可用能力 | 模型选择、规则来源、工具 Schema、Skill/MCP 可用性 | 配置存在、能力可见、账号已授权和本次动作获准是不同事实 |
| 上下文构造与压缩 | 历史、代码读取、工具观察、规则 → 本轮模型输入 | 已选材料、摘要及其来源 | 从存储和工作区取证;完整会话记录不等于实际发送的上下文 |
| 模型适配器 | 上下文与工具定义 → 文本、工具提案、结束原因和用量 | 请求与流式响应的临时状态 | 连接模型服务;提案还须经过解析、校验和权限裁决 |
| 工具路由与动作裁决 | 完整工具提案 → 拒绝、等待审批或执行请求 | 调用关联、权限决定、待决审批 | 路由选择实现,策略决定能否执行;审批答复要回到原请求 |
| 执行后端与工具适配 | 已放行请求 → 文件变化、进程输出或远端回执 | 进程、工具作业、退出码与实际观察 | Shell、文件工具和 MCP 的边界不同,不能假定共用同一沙箱 |
| 子 Agent 协调 | 有边界的子任务 → 子会话与结果通知 | 父子关联、等待、取消及结果接收 | 独立上下文不自动隔离文件系统;父任务仍需检查最终差异 |
| 持久化与恢复 | 消息、执行变化、快照 → 可查询记录与恢复依据 | 历史、未结执行、快照引用等 | 具体保存哪些状态须逐产品核验,不能由一个 resume 命令反推 |
| 验证与交付职责 | 当前代码、命令结果、验收要求 → 差异与证据说明 | 被验证的代码版本、验证结果和未完成事项 | 可以由循环、脚本与人工共同承担,公开证据不足以认定三者均有独立 verifier 服务 |
上述表格是分析模型。Codex 的工具路由、审批、沙箱编排可沿不同源码文件定位;活动 Turn 与工具等待也有具体状态对象。[20][21] Claude Code 官方以 harness 概括工具供应和上下文管理,没有公开这些职责是否分属内部模块。[31] OpenCode 则可沿服务端执行控制、runner、权限及工具实现追踪这些边界。[71][72][75][76]
一次工具循环跨越三类流。控制流是开始轮次、权限裁决、等待答复、取消和完成;数据流是代码与历史进入上下文,模型提案进入工具,工具观察回到下一次模型请求;事实记录是消息、执行变化、文件差异和验证结果的保存。流式 token 与进度通知用于及时展示,不能代替已经完成的工具观察,更不能证明外部写入已生效。具体派发顺序见 19.3,失败与恢复见 19.4。
19.1.5 核心对象:会话、执行、调用、工作区与产物
三个产品都把会话、本轮执行和工作区看作不同的东西,但对象的名字和边界各不相同。
Codex 的外部协议用 Thread、Turn、Item 三层:Thread 是会话,包含多个 Turn,Turn 包含消息、命令、文件变化等 Item[2]。核心里的 Session 保有线程、历史、配置和各类服务,长期存在;一个 Session 在任一时刻至多有一个运行中的任务。活动 Turn 的状态(取消令牌、待决审批、待处理输入、工具调用)挂在 Session 的 active_turn 之下,并不是独立于 Session 的对象[20]。所以会话可以继续存在而某个 Turn 已经结束,Turn 也可以被中断或追加输入,而不必重建会话;对持久化、统计、恢复和取消来说,这是两个不同的状态对象。
Claude Code 把对话、工具调用和结果写进本地明文 JSONL 会话文件。官方说明,resume 以同一会话 ID 重新打开并追加新消息,fork 则把历史复制到新的会话 ID;同一个会话在两个终端同时恢复且不 fork 时,两边的消息会交错写进同一份记录。官方还声明 JSONL 条目格式属于内部实现,随版本变化,不建议直接解析[31][38]。
OpenCode 的会话归服务端持有。源码里执行过程以 drain 和 step 推进:开始执行时,Started 事件和执行声明在同一个事务里写入,这是它能在进程重启后续跑的基础(见 19.4.4)[71][72]。
会话历史与工作区是第三组区别。官方文档明确写到,切换 Git 分支后 Claude 看到的是新分支的文件,会话历史保持不变[31]。这个例子说明,磁盘上有文件不代表已进入上下文,会话里有描述也不代表工作区仍是那个样子。会话记录是上下文资产,工作区是外部事实,本轮是短时执行状态,三者不能互相替代。
为了和第 17 章的对象身份对照,可以按语义建立下面的映射。左列是本书的分析对象,右列只列已有公开证据,不是统一的产品数据模型,也不是一一对应的类型别名。
| 分析对象 | 表达什么、何时变化 | 产品落点与不能混同的对象 |
|---|---|---|
| Session / Thread | 持续交互及其历史;新轮次结束后仍可保留 | Codex Thread 对外、Session 对内;Claude Code 和 OpenCode 使用 Session。会话 ID 不等于进程或连接 ID |
| Task / Run | 要完成的目标,以及在某组约束下的一次执行 | 产品轮次承担其中部分职责,但长期目标可能跨轮;一次用户请求也可能经历多次模型采样,不能直接写成 Turn = Run |
| Turn / 活动执行 | 用户输入触发的一段工作,可被追加输入或中断 | Codex 有明确 Turn;Claude Code 文档描述一轮交互;OpenCode 以执行控制和 drain 推进。轮次结束不自动证明业务目标达成 |
| Step / 模型步骤 | 当前推进位置与依赖;模型步骤产生文本或工具提案 | OpenCode runner 中的 step 是具体实现概念,不能仅凭名字推断它具有通用持久 Step 的依赖与条件提交协议 |
| Item / Message / Tool Call | 输入输出记录,以及工具请求和观察的关联 | Codex Item 可以是消息、命令或文件变化,不是所有 Item 都是工具调用;另两款通过会话中的消息、工具使用与结果表达历史 |
| Attempt / 工具作业 | 一次实际请求、执行尝试或异步进程的生命期 | 一次工具提案可能经历审批、重试和多次轮询;call ID、请求 ID、进程 ID 各有用途,不能互换 |
| Approval / Wait | 暂停哪个请求、等待谁的什么答复 | Codex TurnState 有待决审批、用户输入等等待者;其他产品公开审批接口或行为。内存等待者不等于重启后仍可恢复的持久等待 |
| Workspace / Worktree | 代码、未提交差异、分支和运行环境 | 工作区独立于会话;多个会话可能改同一目录,fork 历史不自动复制工作区 |
| Artifact / Verification | 补丁、报告、测试输出及其对应代码版本 | 本书用于交付分析的对象;测试进程成功与最终代码被验收是两件事,不能假定产品内置统一产物登记表 |
| Operation / 外部动作 | 一个稳定业务意图及其远端生效事实 | 推送、创建 PR、部署、迁移等需要独立核验;工具调用记录不能自行构成第 17 章的 Operation 账本 |
产品映射的直接依据是 Codex 协议与 Turn 状态、Claude Code 会话说明,以及 OpenCode 的执行和 runner 源码;Task / Run、Artifact / Verification 与 Operation 的统一边界是本书推导。[2][20][31][38][71][72]
一个实用的关联链是:会话关联多轮交互,一轮关联多次模型请求和工具调用,工具调用关联执行尝试与观察,文件修改关联工作区版本,验证关联实际执行时的代码。若进入远端写入,再关联稳定业务标识和回执。这里最重要的不是对象名称,而是重试是否换了尝试身份、继续执行是否仍属于原轮次、验证是否仍适用于当前文件。
19.1.6 核心存储:历史、执行状态、工作区与恢复依据
第 17 章的存储分工区分消息、运行状态、事件、Operation、Checkpoint、Artifact 和 Memory。用它检查 Coding Agent 时,应先问“丢失这份信息后,还能证明什么”,再问使用 JSONL、数据库还是 Git。下面是存储职责,不要求每一行部署一个独立数据库。
| 存储职责 | 保存的内容与管理方 | 权威用途与恢复限制 |
|---|---|---|
| 会话与消息记录 | 宿主持久化用户消息、助手输出、工具请求与结果 | 支持历史查询和上下文重建;不证明磁盘、进程和远端系统仍处于记录中的状态 |
| 活动执行与等待记录 | 会话控制管理当前任务、待处理输入、取消、审批和工具等待 | 决定当前能否继续;内存状态、持久意图与完整恢复点需要分开核验 |
| 工作区与版本控制 | 文件系统和 Git 保存当前文件、差异、索引及提交 | 当前代码的权威事实;只有 commit SHA 不能标识尚未提交的编辑,需同时检查相关文件内容 |
| 文件快照与回退记录 | 产品快照设施保存被覆盖范围内的旧文件或恢复引用 | 用于指定范围的文件撤销;不等于持久执行 Checkpoint,也不覆盖数据库和部署 |
| 有效配置与能力定义 | 配置设施、指令文件、Skill 和 MCP 配置提供执行依据 | 恢复前重新解析并核对权限;磁盘上的当前配置不能自动证明历史轮次采用了什么配置 |
| Memory 与上下文摘要 | 记忆设施或会话压缩保存偏好、经验和连续任务信息 | 帮助后续检索和推理;摘要有损,不是进度账本、审批记录或验证证据 |
| 产物与验证载荷 | 工作区、测试系统或交付流程保存 diff、报告、输出与环境信息 | 支撑可审查交付;保存位置与保留规则须显式约定,不能默认所有工具输出永久保留 |
| 凭证与外部连接 | 各产品认证设施和外部服务保存凭证、账号授权关系 | 工具配置不等于可调用权限;本文不把凭证内容纳入普通会话、摘要或日志 |
| 观测与审计记录 | 日志、指标、Trace 或策略事件解释调用和裁决 | 用于诊断和追责;普通日志的完整性、顺序与留存不能按任务账本假定 |
三款产品已经公开的存储与恢复落点如下。此处只到公开机制为止,不补造数据库表名、完整 Schema 或事务保证。
| 产品 | 已确认的落点 | 这些记录能够说明什么 |
|---|---|---|
| Codex | 持久化 rollout 会话记录;核心 Session 与 TurnState 保存活动状态;协议提供历史读取与 resume/fork/revert。[2][20][27] | 会话可保留并继续;固定提交的待决审批等包含内存中的 oneshot 等待者,不能据此宣称恢复会重建全部等待或接管原进程 |
| Claude Code | 本地 JSONL 会话、另行保存的文件 Checkpoint,以及指令和 auto memory。[31][37][40] | 对话续接、有限文件回退和跨会话信息各有机制;官方没有公开一套可据此断言的 Task/Run/Operation 状态库 |
| OpenCode 2.x | 服务端 Session 记录;Started 与持久 claim 同事务;快照使用数据目录中的独立 Git 对象库。[71][66] | 托管服务可借未释放 claim 识别未结执行并续跑;快照恢复文件,两者都不保证远端副作用只发生一次 |
尤其要区分三个容易都被叫作“日志”的东西:会话记录保存交互内容,执行变化记录解释生命周期,诊断日志帮助定位故障。OpenCode 的持久 claim 与易失事件流说明它们并非同一个载体;断线期间的事件需要通过重读会话状态和消息弥补,不能把通知流视为完整可重放账本。[68][71] 同样,CLI 输入历史也不能替代完整会话。
恢复的最小问题集是:对话读回了吗,活动执行还能继续吗,工作区还是原版本吗,未决调用是否已生效,验证是否仍有效?只恢复第一项,适合称为会话续接;若要承诺第 17 章意义上的持久执行,还需逐项证明状态提交、执行权、未决动作核验与版本恢复协议。第 19.4 节按产品列出当前能证明到哪一步。
19.1.7 核心状态机:交互执行、工具调用与交付验收
以 Codex 的 App Server 接入路径为例,状态机需要分层阅读:Turn 状态回答这一轮是否结束,轮内工具状态回答某个请求是否获准和完成,补丁验收回答最终代码是否有充分证据交付。 如果工具触及远端系统,还要独立核验外部效果。这些维度可以同时变化,不能用界面上的“忙碌”或一条 completed 记录包办全部含义。
图 19-3:Codex 的三层状态视图。A 层保留官方 Turn 状态名;B 层是根据工具路由、审批、执行和观察回送归纳的轮内语义模型;C 层是本书提出的交付验收契约。B、C 层不是 Codex 新增的 TurnStatus,也不意味着存在独立 verifier 服务。依据为 App Server 官方文档,以及本章固定提交的 Turn、工具与中断源码;核验于 2026-10-30。[2][20][21][22] 点击图片查看 SVG。
A 层:Turn 的官方生命周期。 Thread 是持续会话,Turn 是一次用户请求及后续工作。客户端通过 turn/start 开始新轮次,服务端以 turn/started 报告 inProgress;轮内可能多次采样、调用工具和等待审批,仍属于同一个 Turn。[2]
| 当前状态 | 触发与条件 | 状态变化及对外事件 |
|---|---|---|
| 尚未创建本轮 | 宿主接受 turn/start | 创建新 Turn;turn/started 中状态为 inProgress |
inProgress | turn/steer 的 expectedTurnId 匹配活动轮次 | 追加输入,仍为 inProgress;不产生新的 turn/started |
inProgress | 模型循环正常结束 | completed;通过 turn/completed 报告 |
inProgress | 遇到使本轮无法继续的错误 | failed;错误信息及 turn/completed 说明失败 |
inProgress | 接受 turn/interrupt 并完成中断处理 | interrupted;通过 turn/completed 报告 |
表中只有 inProgress、completed、failed、interrupted 是这里讨论的官方 Turn 状态;“尚未创建”是说明入口,并非枚举值。尤其注意,turn/completed 是结束通知名,不代表其中的 turn.status 一定为 completed,它也会报告中断或失败。[2] 一个工具报错可以作为观察回送后继续本轮,不能据此直接把 Turn 改为 failed。本轮进入终态后,同一 Thread 仍可开始新的 Turn;thread/resume 续接会话,不意味着旧终态转回运行,也不恢复旧文件内容。
B 层:inProgress 内部的工具调用与等待。 图中“模型采样 → 参数与权限裁决 → 派发与执行 → 回送观察 → 下一次采样”是单个工具请求的语义路径。allow、ask、deny 是图上的分析标签,用来概括放行、等待审批和拒绝,不是一套统一的 Codex 工具状态枚举。无需工具的模型响应可以直接进入本轮收尾;多个工具调用也不必按图逐个串行。
审批请求必须关联到原 Turn 和工具 Item。获准后才派发;拒绝则形成对应观察,允许模型在原授权范围内调整方案,而不是换工具绕过拒绝。协议中的命令与文件变更 Item 可以通过 item/started、审批请求及 item/completed 展示过程;命令或文件变更的最终状态包括 completed、failed、declined,它们不能直接当作整个 Turn 的终态。[2] 具体路由与裁决顺序见 19.3.3。
这些控制确实有状态载体。固定提交里,RunningTask 持有取消令牌和任务句柄;TurnState 保存待决审批、用户输入、动态工具等待、输入队列与邮箱投递相位。待决审批通过关联键找到等待答复的通道;这说明“等待”有具体关联对象,但内存中的通道不能证明重启后仍可重建该等待。[20]
中断可发生在采样、工具执行或审批等待期间。固定源码先传播取消,再处理任务终止、被中断标记、待决等待者和会话记录;因此 turn/interrupt 的请求响应与最后的结束通知应分开理解。[22] 本轮停止推进也不能证明命令未产生文件变化或远端写入已撤销。创建 PR 请求超时而回执丢失时,要核验原动作;第 17 章的 UNKNOWN 是描述这种未确认效果的分析状态,不是此处新增的 Codex Turn 状态。
C 层:补丁验证与可审查交付。 “候选差异 → 验证中 → 针对该版本的通过 → 可审查交付”是本书的验收契约。它可以跨越多个工具调用,必要时也可跨 Turn;正常结束一轮不保证这条验收链已经走完。测试失败时可以继续编辑和重验,验证后文件或相关环境再次改变,则需重新判断旧结果是否仍然有效。
例如用户要求修复登录超时,Codex 在同一个 inProgress Turn 内编辑文件、运行测试,测试失败后将错误作为观察交回模型,再修订并运行测试。测试命令完成只结束对应 Item;最终代码和证据核对后才能支持交付声明。若用户在测试时中断,本轮最后为 interrupted,已经写入的补丁仍可能留在磁盘。下一轮应先检查差异和测试进程,再决定继续验证或重新修改,而不是把中断理解为自动回滚。交付至少说明对应代码、测试命令、实际结果与未覆盖条件。
这个 Codex 例子展示了“同一轮次中的内部循环”与“轮次自身生命周期”的区别。迁移到 OpenCode 时,还要考虑终止原因如何决定 claim 是否保留;迁移到 Claude Code 时,只能根据官方公开的中断和恢复行为建立对应关系,不能直接套用 Codex 的枚举与内部对象。[71][31][38]
由此得到四条架构检查条件:
- 审批与动作绑定:答复必须回到原请求;参数或权限变化后是否需要重新审批,按该产品契约核查。
- 取消与效果分离:停止模型循环不能证明命令已停、文件未改或远端请求未生效。
- 恢复与状态重建对应:resume、快照回退和进程续跑分别恢复不同对象。
- 完成与证据对应:轮次结束、工具进程退出和代码通过验收分别判断。
19.1.8 接入面与进程边界
Codex 的变化最大。固定提交中,TUI 的 Cargo.toml 已经不再依赖 codex-core,TUI 和 codex exec 都通过进程内的 App Server 客户端接入,源码注释写明这样做是为了保留 App Server 的语义,同时避免进程边界[25]。协议 crate 的 README 仍然写着“core 与 TUI 之间的内部类型”,已经落后于代码[29]。更准确的说法是:TUI、exec 和外部客户端共用 App Server 的请求与事件语义,TUI 以进程内方式承载它。App Server 有 stdio(默认)、Unix socket、WebSocket 和关闭四种传输;官方文档称 WebSocket 为 experimental and unsupported,codex app-server 命令主要用于开发与调试,可能不经通知变化[2][14][25]。
Claude Code 的官方描述是:它是围绕模型的外围层,提供工具并管理模型所见的上下文,也就是所谓 agentic harness;各个界面共用同一个循环[31]。官方没有公开进程和线程的划分。
OpenCode 2.x 把界面放在服务端之上。CLI 默认发现或启动当前用户的共享后台服务,客户端连上它;--standalone 启动私有服务,--server 连接指定服务,两者不能同时给出[57][70]。私有服务由客户端拉起子进程,客户端关闭其标准输入时服务随之退出[70]。这个细节决定了“关闭界面是否等于结束服务”的答案:共享服务模式下不等于,私有服务模式下等于。
三者共同的取向,是把“怎样呈现任务”和“怎样执行任务”分开,前端不必复制一套模型循环。这是对公开协议与职责的架构概括(系统推导),不代表所有界面的功能完全相同。
19.1.9 模型接入与扩展层
OpenCode 把模型接入做成可替换的部分:Provider 与模型目录来自 models.dev,Provider 通过 package 字段选择运行时包,本地模型(Ollama、LM Studio、vLLM)可自动发现,任意 OpenAI 兼容服务可手动配置[60]。官方文档同时承认,这个抽象并不抹平差异:选项是 Provider 特定的,别的 Provider 包可能忽略或拒绝;目录之外的自定义模型默认假设 200,000 的上下文和 32,000 的输出上限,文档称这是回退假设,不是检测到的能力[60]。Codex 的仓库也含适配组件,配置文档列出自定义模型提供商、--oss 本地模式和 Bedrock 提供商[4]。Claude Code 使用 Claude 模型,通过 Anthropic API、云厂商平台或网关访问,缓存和推理都发生在服务端,“本地执行工具”并不意味着在本地运行模型[46]。
扩展层方面,三个产品的名字相近,但在循环中的位置不同。Claude Code 官方的划分最清楚:Skill 提供可复用的知识与工作流,MCP 接入外部服务,Hook 在生命周期事件上执行确定性动作,Subagent 把有边界的工作交给独立上下文,Plugin 只是打包与分发形式[45]。Codex 的 Skill 采用渐进披露:启动时只带每个技能的名称与描述,选用时才读完整的 SKILL.md;描述列表最多占模型上下文窗口的 2%(窗口未知时为 8,000 字符)[11]。OpenCode 的 Commands 里,! 嵌入的 Shell 块在工具权限流程之外运行,文档要求只使用可信来源;2.x 的插件 API 与 1.x 不兼容,1.x 插件不能在 2.x 运行[65]。这些扩展的内部注册、调度与持久化方式,凡是没有公开源码或官方实现说明的,本章不作断言。
19.1.10 权限、沙箱与审批:三层各管什么
本节以17.5 跨层安全控制为参照比较产品。权限相关的机制可以分成三层:决策层决定一个动作是否放行,隔离层限制放行后的进程能碰到什么,管理层决定哪些上限不能由用户或项目放宽;业务资源的最终鉴权仍由对应服务实施。
| 层次 | Codex CLI | Claude Code | OpenCode 2.x |
|---|---|---|---|
| 决策:动作是否放行 | 审批策略 on-request、never、granular(审批策略值 untrusted 已退役,项目 trust_level="untrusted" 仍有效);auto-review 可以代替用户审核已经需要审批的动作,但它是“换审核人”,不是授权[8][9] | 六种权限模式;规则按 deny → ask → allow 求值,首个匹配生效;PreToolUse Hook 的决定不能绕过规则[34][35] | 有序规则,三元组为 action、resource、effect,最后一个匹配者生效;多资源操作中任一资源命中 deny 即 deny[63][75] |
| 隔离:放行后能碰什么 | 沙箱模式 read-only、workspace-write、danger-full-access;workspace-write 下 .git、.agents、.codex、.aws 递归只读[7] | 沙箱默认关闭,支持 macOS、Linux、WSL2;只覆盖 Shell 命令及其子进程,文件工具、MCP、Hook 在沙箱之外;官方称它降低风险,但不是完整的隔离边界[36] | 在本章核查的权限、Shell 插件和进程执行路径中未见 OS 沙箱装配;不能据此断言所有部署都没有外层隔离[63] |
| 管理:谁能收紧 | Managed requirements 是管理员强制约束,managed defaults 是启动值;普通配置中的默认值不能当作组织上限[85] | managed 设置优先级最高,managed 中的 deny 不能被覆盖[41][35] | Policies:二元、不弹窗、只能收紧;来源越宽优先级越高[64] |
审批与隔离是两件事。Codex 官方的表述是:沙箱定义技术边界,审批策略决定在越过边界之前何时停下来询问[7]。同意运行某个命令不会让命令变安全;沙箱放行某个路径,也不意味着命令符合业务意图(系统推导)。代码层面也能看到这种分工:Codex 的路由、审批阶段和沙箱选择与重试编排分处不同文件,沙箱机制本身在独立的 crate 里[21]。
OpenCode 的默认配置需要特别注意:每个 Agent 的默认策略是 * * allow,只有读取 .env 一类文件会询问,项目目录外的读写默认也不询问[63]。所以“ask 会暂停动作”在默认配置下只覆盖很少的动作,想让它生效,要自己添加规则。
组织治理还要区分“配置优先级”和“不可放宽的约束”。Codex 官方要求保护 requirements 文件及其父目录,避免运行 Codex 的用户修改;普通 config.toml 或 managed defaults 仅提供启动值。官方也建议清点 CLI、IDE、桌面、云端及远程主机等执行面,变更后同时测试允许与阻断路径。[85] 这不能被概括成所有配置字段都按同一套优先级合并;Claude Code 与 OpenCode 的规则求值仍分别按上表核查。
另一类常见的误解,是把角色或提示词当作边界。OpenCode 文档明确写到,隐藏某个 Agent 只控制可见性,不是安全手段[62];Claude Code 文档则说,在 CLAUDE.md 或 Skill 里写“不要编辑 .env”只是请求而不是保证,必须发生的约束要放进 Hook[45]。
19.1.11 贯通案例:一次修复怎样穿过组件、存储与状态
以“修复登录超时竞态,提交可审查补丁,不推送远端”为假设任务。这里不描述某款产品的实测过程,而是用同一个案例检查上述架构;具体失败分析与调优实验见 19.6.2。
- 受理与关联:客户端发送输入,会话控制关联当前 Session/Thread 并开始执行。目标和禁止推送的边界进入任务上下文;工作区位置、基线及已有差异另行确认,会话记录不能替代这次检查。
- 读取与采样:上下文组件读取项目指令、相关代码和失败证据,构造模型输入。模型返回编辑或命令提案,工具路由检查完整参数,权限组件裁决;需要审批时关联原调用等待,获准后才交给对应后端。
- 编辑与观察:文件后端修改工作区,将路径、结果或错误作为观察回送;若产品支持该范围的快照,则由其快照机制保存恢复依据。模型历史记录“做过编辑”,当前文件内容证明“现在是什么”。
- 验证与修订:命令后端运行竞态测试并记录实际退出码、输出和对应代码。失败时本轮可以继续编辑和重验;这次失败是工具/验证结果,未必立即使整轮进入失败终态。
- 中断与续接:若命令发出后客户端断开,先判断宿主是否仍运行。重新接入时读取会话与实际文件,检查原命令是否仍在运行或已产生结果;文件快照、会话 resume 与 OpenCode 托管服务续跑各按自身契约处理,不凭摘要重复发命令。
- 交付与结束:验收针对最终差异通过后,交付补丁、测试证据、未覆盖条件和剩余事项,再结束本轮。若最终编辑发生在测试之后,先重新验证受影响内容。此时仍未执行远端写入。
若后来用户明确要求创建 PR,授权范围发生变化,需要新动作裁决。创建请求超时而远端是否成功未知时,先查询对应分支和 PR;无法确定就报告未决事实并停止重复创建。这一分支需要外部动作身份与核验能力,不能由对话回退或文件快照解决。
这个案例把责任落到了具体位置:宿主决定是否继续,上下文组件决定模型看到什么,工具与后端产生实际变化,存储保存历史和恢复依据,验证证据约束完成声明。产品之间的差异,主要体现在这些职责由谁承载、哪些状态持久化、异常后能恢复到哪里。
19.2 启动:从进程启动到首个模型请求
19.2.1 三个时间尺度
启动不是一个瞬间。进程启动准备服务与连接,会话初始化选择工作区与配置并建立或恢复历史,轮次启动把新的输入送进模型与工具循环。三者可以连续发生,也可以相隔很久:一个后台服务可以承载多个会话,一个会话可以执行多轮。恢复历史只是读回记录;旧的命令进程是否恢复、文件是否回到历史时刻,因产品而异,见 19.4.4。
后面三小节分别描述三个产品的启动路径,19.2.5 说明配置与指令什么时候生效,19.2.6 对照“就绪”在三个产品里分别指什么。
19.2.2 Codex:App Server 路径
以客户端启动 codex app-server 为例,协议路径是:启动服务、握手、建立或恢复 Thread、启动 Turn、消费事件直到 Turn 结束。TUI 与 codex exec 在固定提交中以进程内方式承载同一套 App Server 语义,没有进程边界,但步骤相同[25]。
| 阶段 | 动作 | 完成后得到什么 |
|---|---|---|
| 启动服务 | 选择传输:--listen 默认 stdio://,可取 unix://、ws://IP:PORT 或 off[25] | 可接受连接的本地服务;此时还没有用户任务 |
| 连接握手 | 每个连接发送一次 initialize,再发 initialized 通知[2] | 连接完成协议初始化 |
| 建立或恢复会话 | thread/start 指定模型、工作目录、审批策略与沙箱;thread/resume 按 threadId 恢复;响应带回已加载的指令文件路径 instructionSources;必需的 MCP 服务器初始化失败时,二者都会失败[2] | Thread 身份与会话配置 |
| 启动轮次 | turn/start 提交用户输入和可选的本轮覆盖参数[2] | 活动 Turn |
| 执行循环 | 模型采样、工具调用、审批、命令与文件变化,以 item/* 与增量事件回传 | 消息、命令、文件变化等 Item |
| 收束本轮 | 收到 turn/completed,状态为 completed、interrupted 或 failed | 已结束的 Turn;Thread 可接收下一轮 |
握手的硬顺序是连接、initialize、建立或恢复 Thread、启动 Turn。官方文档说,握手之前的任何请求都会被拒绝,重复的 initialize 返回 “Already initialized”[2]。源码给出了更精确的细节:放行门设在 initialize 请求上,未初始化时返回 “Not initialized”;initialized 通知在服务端只被记录,并不参与放行[25]。所以“握手前的业务请求被拒绝”成立,“必须先发 initialized 才放行”则不是服务端强制的。另外,turn/completed 的状态可能是 interrupted 或 failed,不能仅凭事件名认定需求已经实现[2]。
有两处需要标注版本的不一致。其一,--code-mode-host 控制的是 App Server 向其 Code Mode 宿主发起的外向连接,不改变 --listen,也就是客户端怎样连入 App Server;这是两条不同的连接。官方文档的示例写的是 wss:// 地址,而固定提交的源码只接受 http:// 或 https://,并在测试里把 wss:// 列为非法端点,所以本章不写具体的地址方案[2][25]。其二,code_mode_host 特性默认开启,code_mode 特性仍在开发中、默认关闭,“默认会启动本地宿主”不能写成“默认启用 Code Mode”[28]。
19.2.3 Claude Code:Harness 与 Session
官方公开的是产品行为、配置与生命周期事件,没有公开足以重建核心程序的源码。下表按依赖关系归纳启动过程,不是私有函数调用顺序,也不表示各项准备严格串行。
| 阶段 | 官方资料能确认的行为 | 需要区分的边界 |
|---|---|---|
| 启动与配置 | 在项目目录运行 claude;配置优先级从高到低为 managed、命令行、project local、共享 project、user[41] | 起始权限模式依版本而定(见下) |
| 指令与能力 | 从当前目录向上逐级载入 CLAUDE.md;auto memory 的 MEMORY.md 载入前 200 行或 25KB,先到者为准;载入 MCP 工具名与 Skill 的名称和描述[40][31][43] | 子目录里的 CLAUDE.md 要等 Claude 读、写或编辑该子目录的文件时才载入;MEMORY.md 的专题文件不在启动时载入 |
| 建立或恢复 Session | 新会话从全新的上下文窗口开始;--continue 与 --resume 以同一会话 ID 继续[31][38] | 恢复的是历史,读到的仍是当前工作区的文件 |
| SessionStart Hook | 匹配值有 startup、resume、clear、compact、fork 五种[42] | 见下 |
| 首次输入与循环 | 处理输入,组合上下文,进入收集上下文、行动、验证的循环[31] | 每次工具使用都受权限规则控制 |
起始权限模式是版本敏感项。官方权限页写明:Claude Code v2.1.283 及以后,交互终端与 VS Code 会话内置的起始模式是 auto(由分类器模型审查动作);claude -p 与 Agent SDK 在没有指定模式时,v2.1.302 及以后默认为 Manual,调用需要审批的动作会被拒绝,除非规则或 Hook 允许[34]。更新记录把 auto 作为默认起始模式的条目放在 2.1.284 的小节里,与权限页的版本号有出入[56],所以正文不写死版本号,使用者以所用版本的文档为准。
SessionStart Hook 里最容易误判的是“界面出现”与“首个模型请求可发送”。对交互式启动、启动时恢复和 /clear,Hook 在后台运行,用户可以立即输入,但 Claude 的首次响应要等 Hook 完成;等待期间提交的 prompt 要到 Hook 结束才会送达,按 Esc 可以取回输入,Hook 继续运行。会话内的 /resume 则相反,要等 Hook 完成后才切换会话[42]。另一个事件 Setup 不在普通启动时触发,只用于 --init-only,或 -p 下的 --init 与 --maintenance;它不能阻塞,输出会被丢弃[42]。
19.2.4 OpenCode 2.x:连接服务与装配会话
OpenCode 的默认启动方式是发现或启动当前用户的共享后台服务,再让本地客户端连接它;该服务拥有会话、配置、集成、权限和工具执行[57]。源码里的连接决策是这样展开的:--server 与 --standalone 同时给出会直接报错;--server 先做一次超时 5 秒的 server.info 健康检查,版本不一致只警告并继续;--standalone,或服务配置为 disabled,走私有进程;否则发现或启动共享服务[70]。
私有服务的启动路径更值得细看。客户端以子进程执行 serve --stdio --port 0,随机生成密码并通过环境变量传给子进程,把子进程标准输出的第一行 {url} 当作就绪信号;子进程在就绪前退出就报错。源码注释把客户端持有的标准输入称为“所有权租约”,服务端在 stdio 模式下读到输入结束就退出[70]。
| 阶段 | 初始化或执行动作 | 完成后得到什么 |
|---|---|---|
| 选择连接方式 | 共享服务发现或启动,或按参数选择私有服务、指定服务 | 到目标服务的连接 |
| 准备工作区配置 | 服务端按工作区生效配置、项目指令与集成;目录、凭证与配置按位置隔离[60] | 当前工作区的配置与指令范围 |
| 选择 Agent 与模型 | 确定主 Agent 与 Provider / Model;缺少凭证时用 /connect,再用 /models 选模型[60] | 可用于该会话的角色、模型与访问条件 |
| 建立或继续会话 | 经 Session 接口建立或读取会话,客户端提交输入[68] | 会话身份与消息 |
| 执行与权限交互 | 服务端运行模型与工具;规则为 ask 时等待客户端决定[63] | 工具结果,或明确的拒绝 |
配置里声明了 Provider 并不证明持有有效凭证,/connect 之后还要选择模型。诊断共享服务可以用 opencode service status|start|stop|restart,以及 opencode api get /api/info[57]。
19.2.5 配置与指令:谁生效、何时加载
“目录里有配置或说明文件”不等于“本次会话已经采用它”。三个产品对此各有规则。
| Codex CLI | Claude Code | OpenCode 2.x | |
|---|---|---|---|
| 配置层次(高到低) | 命令行与 --config、项目 .codex/config.toml(从根到当前目录,近者胜)、--profile 文件、用户、云端托管、系统、内置默认;项目层只在项目受信时生效[3] | managed、命令行、project local、共享 project、user;列表合并,managed 的 deny 不可覆盖[41] | 全局、项目、系统三处位置;2.x 仍读取 1.x 的位置并归一化[58][59] |
| 指令文件 | AGENTS.md;全局目录先读 AGENTS.override.md,否则 AGENTS.md;项目从根(默认以 .git 为标记)走到当前目录,每目录至多一个文件,靠近当前目录的在后[6][26] | CLAUDE.md,从当前目录向上逐级拼接;子目录的按需载入;以 user message 的形式在系统提示之后送达[40] | 只认 AGENTS.md,不回退到 CLAUDE.md;全局加上从当前目录向上的所有 AGENTS.md,不解决它们之间的冲突;下层目录的文件在读取文件或列目录时按需加载、就近优先[61] |
| 加载时机 | 每次运行(TUI 为每个会话开始时)重建指令链,没有缓存需要手工清除[6] | 会话开始载入;会话中编辑 CLAUDE.md 不会立刻生效,要到 /clear、/compact 或重启[46] | 根与全局文件的修改在下一次模型请求前以“指令更新”注入;已加载的嵌套文件被修改不会被自动检测,需要新会话[61] |
| 大小上限 | project_doc_max_bytes 默认 32 KiB,是各层合计的上限(固定提交的合计口径;不据此推断其他发行版)[6][26] | 启动时单个 CLAUDE.md 大于 4 MiB 才整份跳过;小于等于 4 MiB 全文加载,子目录文件另有较小上限,官方建议少于 200 行;官方还指出,过长会让规则被淹没[40][32] | instructions 数组可追加文件、glob 与 HTTP(S) 地址,地址拉取超时 5 秒[61][77] |
| 如何核对实际加载了什么 | 响应中的 instructionSources;或在仓库根运行 codex --ask-for-approval never "Summarize the current instructions."[2][6] | 运行 /context 确认文件已载入[32] | 未找到专门的核对手段 |
Codex 还有一道受信门:项目配置层只在项目被信任时加载,源码的表述是“加载但禁用”,禁用范围包括项目本地配置、Hook 和执行策略[26]。AGENTS.md 的处理在源码里另有一个条件,项目被显式标为 untrusted 时只返回用户层指令,而配置层的条件是“未被标为 trusted 即禁用”,两处边界情形不完全相同,本章没有运行验证[26]。
Claude Code 官方把这两种载体的性质写得很清楚:CLAUDE.md 与 Skill 是模型要解释的指令,Hook 是确定性执行;“不要编辑 .env”写在前者里只是请求[45]。因此指令文件应当承载模型猜不到的事实(命令、风格、约定),必须成立的约束则交给权限规则或 Hook。这一点在 19.5.4 展开。
19.2.6 就绪条件对照
启动时序对照。三幅时序沿生命线向下阅读:实线为调用或请求,虚线为响应,橙色为通知或界面展示;alt 表示条件分支,opt 表示可选 Hook,loop 表示模型与工具的重复交互。参与者是逻辑角色,不保证对应独立进程;Claude Code 仅描述官方可观察顺序。图中标注的依据见附录 B。
| 观察点 | Codex App Server | Claude Code 本地会话 | OpenCode 2.x |
|---|---|---|---|
| 进程或服务可用 | 服务启动并能接受连接 | CLI 已启动,界面可以显示 | 共享、私有或指定服务可被客户端连接 |
| 会话可用 | Thread 已建立或恢复,工作目录与执行设置已确定 | Session 已建立或恢复,指令与启动 Hook 参与上下文准备 | 服务端已确定工作区、Session、Agent 与模型条件 |
| 本轮可执行 | 连接已握手,turn/start 被接受;工具执行仍可能等待审批 | 输入经过启动等待与本轮处理后进入循环;工具仍逐次受控 | 输入已提交,模型与工具条件满足;规则为 ask 时动作会暂停 |
| 完成的证据 | Turn 状态、Item、文件差异与实际检查结果 | Session 中的工具结果、文件差异与检查结果 | Session 消息、工具结果、事件与实际工作区差异 |
通用的 Runtime 设计里,常把启动拆成加载配置、注册能力、编译策略、检查依赖、发布就绪几类职责。三个产品把这些职责分散在进程、工作区、会话、轮次四个阶段,部分延迟到首次使用,不能据此断言每个产品都有同名模块、会在启动时探测全部外部服务,或都会先跑完验证再接收输入。实践中应当分别检查连接、会话、模型请求与工具执行的可用条件:服务启动了,不代表会话建好;会话建好了,不代表某次工具调用已经获准。
19.3 执行:一轮任务怎样推进
19.3.1 共同的循环:收集上下文、行动、验证
Claude Code 官方把工作过程描述为三个阶段:收集上下文、采取行动、验证结果;这些阶段会按任务需要交错,修复类任务会反复穿插编辑和验证,理解类任务可能只需要检索[31]。官方同时把 Claude Code 定位为围绕模型的外围层,提供工具并管理上下文[31]。
Codex 的 Turn 主循环在源码里有一段骨架性的注释:模型请求函数调用时,运行时执行它,并在下一次采样请求里把输出送回模型;模型只返回助手消息时,运行时把它记入历史并认为这一轮完成[20]。这段注释描述的只是骨架。实际的 run_turn 还包含采样前压缩、Hook、MCP 启动要求、采样后压缩、Stop Hook、收尾压缩,以及邮箱抢占,引用这段注释时要注明它写的是骨架[20]。OpenCode 的 Session Runner 也一样:每一步请求之前先尝试自动压缩,处理步数上限,失败后再分流为重试、续写、输出超长、已压缩、整体恢复几类结果[72]。
三者共享的工程原则,是把模型的计划和工具的执行分开:模型提出候选动作,运行时解析、授权、执行并记录结果,再把观察带回下一轮。这个分工使人可以在循环中途介入。工具结果成为后续决策的新观察,用户的纠正也同样成为新输入,下一小节会看到三个产品各自怎样接收这类输入。
19.3.2 三个产品的循环实现与控制点
| 控制点 | Codex CLI | Claude Code | OpenCode 2.x |
|---|---|---|---|
| 循环的承载 | Session 持有至多一个活动 Turn;run_turn 反复采样[20] | 官方描述的三阶段循环;内部结构未公开[31] | Session Runner 的 drain 与 step;每步前先尝试压缩[72] |
| 运行中追加输入 | turn/steer 把输入追加到进行中的 Turn;评审与压缩任务不接受同轮 steer[2][27] | 工具运行时输入并回车,消息排队,在同一轮内于工具调用完成后读取;Ctrl+Enter 立即发送[49] | 工作中按 Enter 是 steer,Ctrl+X 再 Enter 是排队[57] |
| 中断 | turn/interrupt,Turn 以 interrupted 结束[2] | Esc 立即停止,取消运行中的工具调用,保留上下文[31] | session.interrupt,先确认、后异步清理[68][71] |
| 步数或轮数上限 | 本章未找到默认的步数上限 | -p 下可用 --max-turns,官方 CLI 参考写明默认无限制[50] | Agent 的 steps 达到上限后,最后一步保留工具定义但 toolChoice 设为 none,并要求只用文本总结;新的用户输入重置额度[62][72] |
| 压缩的相位 | 采样前、回合中、回合后三个相位[23] | 临近上限时自动压缩;空闲时也可压缩[31][41] | 每步请求前检查阈值;溢出后恢复一次[73] |
Codex 的 Turn 状态值得多说一句。活动 Turn 里有取消令牌、待决审批、待处理输入和工具调用,所以追加输入、审批等待和中断都发生在同一个状态对象里,而会话本身不受影响[20]。OpenCode 的循环则带有明确的持久化边界:每个模型步骤请求前后各捕获一次文件快照,步骤结束时把消耗的 token 和成本写进事件(见 19.6.1)[66][72][77]。
19.3.3 工具路由与权限裁决在循环中的位置
三个产品都把“模型能看到的工具声明”与“真正可执行的工具”分开。Codex 的 ToolRouter 里,model_visible_specs(模型可见的声明)和 registry(可执行的注册表)是两个不同的字段,模型返回的调用要先转换成内部的 ToolCall 再分发[21]。OpenCode 的 Tool.snapshot 同样返回模型可见的定义和执行函数,被 * 整体拒绝的工具会从定义中移除[76]。工具声明不等于执行权限,这是后面各种裁决的前提。
Codex 把审批、沙箱选择和重试串成一个流程:先按审批策略决定是否放行,再在选定的沙箱里首次尝试;如果被沙箱拒绝,按策略决定是否请求“去掉沙箱重试”的批准,批准后再做第二次尝试。源码注释说明,在 never 或 on-request 策略下不会去掉沙箱重试,而是返回一条保留原始输出的简短拒绝;文件头的注释还写明,自动审核给出的批准只适用于已审核的授权范围,升级沙箱重试需要对这次重试重新审核[21]。每次沙箱结果(拒绝、超时、信号、升级)都会作为遥测事件记录[21]。MCP 工具走同一套审批:官方称带有破坏性注解的 app 或 MCP 工具调用始终需要审批(除非该工具同时声明了读取注解);default_tools_approval_mode 可取 auto、prompt、writes、approve,MCP 服务器的启动超时默认 10 秒,工具超时默认 60 秒[8][12]。
动作裁决与审批等待。图以 Codex 的工具执行编排为主线,表示逻辑控制流,不表示每个节点都是独立进程;自动审核已批准的范围扩大时,重试须重新审核。失败结果的细分和中断清理顺序见 19.4.2、19.4.4。[20][21][22][25]
等待发生在工具调用与实际执行之间:运行时发出审批请求,当前调用等待决定,活动 Turn 仍持有待决审批和取消令牌;审批回复到来后才沿批准、拒绝或中止分支继续。Denied 是不执行该动作并把拒绝作为结果交回模型;Abort 则结束当前 Turn。等待期间发生中断时,运行时先让任务观察到取消,再清理待决审批,避免把中断误呈现为普通拒绝[20][21][22]。审批可以由人或自动审核承担,但批准本身不替代执行时的沙箱约束。
Claude Code 的裁决顺序是:规则先于模式,按 deny、ask、allow 求值,首个匹配生效,具体度不改变顺序;PreToolUse Hook 的决定不能绕过规则,命中 deny 仍然阻断,命中 ask 仍然询问[35]。auto 模式在此之上加入一个分类器模型,在动作执行前审查:分类器看到的是用户消息、非只读的工具调用和 CLAUDE.md,工具结果被剥离,以免文件或网页里的敌对内容直接操纵分类器;读取、搜索和工作目录内的编辑不经过分类器。受保护的路径(.git、.claude、.mcp.json 等)的写入永远不被自动批准,bypass 模式除外[34]。官方还写明,企业与 API 账户上分类器调用计入 token 用量,每次检查要多一次往返,开销主要来自 Shell 命令和网络操作[34]。
OpenCode 的求值写在 permission.ts 里:先取该 Agent 与会话的规则;只要任一资源命中 deny 就直接 deny,已保存的批准不参与;否则合并已保存的批准,任一资源为 ask 则 ask,否则 allow;最后触发插件的 permission.evaluate 钩子,Policies 就是在这一步生效的[75]。规则为 ask 时,工具调用以 Deferred 挂起,客户端用 once、always 或 reject 回复,always 会写入项目级的持久 allow 规则,并且不会覆盖已配置的 deny[63][75]。
与图中的 Codex 流程对照,OpenCode 明确使用 Deferred 等待权限回复;Claude Code 的公开资料描述规则、Hook 与分类器的裁决行为,但不足以据此推断其内部等待对象。Hermes 的危险命令审批与无人值守、超时处理另见 22.5.2,不应套用图中的 Codex 分支。[35][75]
这三套机制的共同点是:每一次动作都要单独裁决,不是在进入循环之前检查一次。差别在于谁来裁决——用户、规则、分类器,还是插件钩子——以及裁决之后进程还受不受沙箱约束(见权限、沙箱与审批)。
19.3.4 上下文管理:加载、压缩与按需读取
Claude Code 官方给出的上下文内容包括对话、文件与命令结果、项目指令、载入的 Skill 和系统指令。接近上限时,它先清理较旧的工具输出,必要时再摘要对话,官方明说,对话早期的详细指令可能因此丢失[31]。压缩之后哪些内容还在,官方也列了出来:根目录的 CLAUDE.md、无作用域的规则和 auto memory 会从磁盘重新注入;已调用的 Skill 正文重新注入,单个上限 5,000 token、合计上限 25,000 token,旧的先丢;嵌套的 CLAUDE.md 与按路径作用域的规则是读到文件时才载入,载入后进入消息历史,压缩时和其他内容一起被摘要,需要再次读取才会回来[39][40]。触发点未设置时在模型的上下文上限处压缩,可以用 autoCompactWindow、/autocompact 或环境变量调整[48];自 v2.1.290 起还有空闲压缩,在提示缓存过期之前趁空闲时压缩[41]。MCP 工具定义默认延迟加载,只有工具名和服务器说明进入上下文,但这只在受支持的模型上成立,Foundry 上的 Azure 托管部署、设置了 CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS 或使用 HIPAA 配置时会关闭[54]。用户可以用 /compact <指令>、CLAUDE.md 里的 Compact Instructions 段落,以及 /rewind 中的“从此处摘要”来控制保留什么[31][37]。
Codex 的自动压缩由阈值触发:当前范围内的 token 数达到自动压缩上限(含缓冲),或活动上下文达到模型的完整窗口,就强制压缩。源码里默认的上限是模型配置值与解析后窗口的 90% 之中较小的一个,官方文档只说“未设置时使用模型默认值”[23][5]。压缩分采样前、回合中、回合后三个相位,回合后压缩由 model_post_turn_compact_threshold_percent 控制,省略或设为 0 表示关闭[23][26]。压缩提示词要求产出一份“交接摘要”,包含进度、约束和剩余事项;它并没有声称摘要包含工作区的文件状态;压缩后保留的用户消息最多 20,000 token[23]。
OpenCode 2.x 的自动压缩默认开启:接近上限时总结整个当前对话,并在摘要旁保留最近约 15,000 token(compaction.keep.tokens 的默认值);之后的压缩基于上一个检查点之后的对话更新已有摘要。每个步骤请求前都会检查:估算上下文达到天花板就压缩,天花板是窗口减去缓冲,缓冲未设置时取窗口的 15% 与 16k 中较大者;窗口未知则不触发自动压缩[67][73]。官方文档直言“压缩是有损的”,摘要是结构化文本(目标、决策、进行中的工作、阻塞与下一步、相关文件),保留的近期对话里较长的工具输出会被缩短;固定指令与工具 schema 占满窗口时,压缩无效[67]。关于压缩用哪个模型,文档说“没有单独的压缩模型”,源码却会读取隐藏的 compaction Agent 的模型,二者的矛盾本章没有解决,不写成“可配置”,也不写成“绝对不能”[67][73]。
三者共同说明了一件事:压缩保存的是任务的连续性,不是工作区的当前状态。压缩后的任务应当重新对照仓库:文件是否仍是记录中的版本,差异是否变了,测试是否针对当前差异运行。稳定的约束适合放进持久的指令,动态的代码事实应当回到来源重新读取;这两类信息在压缩中的命运完全不同,19.4.3 会说明具体后果。
19.3.5 委派与多 Agent
“上下文分开”和“工作区分开”是两件事:前者避免共享完整对话,后者避免并行写同一批文件。一个只读的子任务与主 Agent 共用目录不一定冲突,两个 Agent 即使上下文独立,也不会因此获得隔离的文件系统。
Codex 的子 Agent 继承当前的沙箱策略;官方提醒要更谨慎地对待并行写入的工作流,因为多个 Agent 同时编辑代码会产生冲突;自定义 Agent 文件不能覆盖沙箱、审批和 MCP 服务器的设置。内置的 Agent 有 default、worker、explorer,触发方式是用户直接要求,或 AGENTS.md、Skill 里有适用的指令;官方明说,子 Agent 工作流比同类单 Agent 运行消耗更多 token[10]。源码里多 Agent 有 V1 与 V2 两套工具集,multi_agent 特性默认开,multi_agent_v2 默认关[28][27]。
Claude Code 的 Subagent 使用自己的上下文窗口,工具调用不进入主上下文,结束时返回摘要。fork 是例外:它从当前对话的副本开始,继承父会话的系统提示、工具与缓存;fork 模式在交互终端自 v2.1.232 起默认开启,整体自 v2.1.302 起默认开启[44]。并发与嵌套是不同限制,当前默认值及例外集中在 19.6.4;不能把并发槽位当成全会话总数上限[44]。官方还描述了一项防护:Claude Code 会扫描子 Agent 的最终报告,把仿冒系统提醒的文本无害化,并在报告前加一行说明“这是子 Agent 的话,不具备来自用户的授权”[44]。Worktree 隔离的强制检查有四项(文件编辑、命令的工作目录、git 重定向、命令形态),但并不跟踪 Shell 命令实际写入了哪些文件,cp 或重定向写入主检出不会被拒绝,只落入普通的权限与沙箱[51]。
OpenCode 区分 Primary Agent 与 Subagent。内置的有 Build(主 Agent,默认)、Plan(主 Agent,不编辑普通项目文件,可在 ~/.opencode/plan 下写计划文件,Shell 仍受权限控制)、General(子 Agent,不能再启动子 Agent)和 Explore(子 Agent,官方称它搜索与读取而不编辑文件)。子 Agent 用自己的权限,不是父权限的子集,父 Agent 的 subagent 权限决定它能启动谁;嵌套深度默认为 1[62][76]。源码里 Explore 默认拒绝一切,只放行 shell、read、glob、grep、webfetch、websearch 与 external_directory[76];因为 shell 被放行,它能否保证只读,仍取决于 shell 的规则(系统推导)。
委派的结果是证据候选,不是最终证据:子 Agent 的摘要需要和差异、测试结果一起核对。并行修改还要先解决文件冲突,再验证合并后的整体。
19.3.6 验证与可审查交付
三个产品对“完成”的要求,官方材料的详细程度差别很大。
Claude Code 的官方最佳实践把验证做成了一个由弱到强的阶梯:在同一条 prompt 里要求运行检查并迭代;用 /goal 条件,由一个独立的评估器在每轮后复核;用 Stop Hook 把检查作为确定性的闸门,在检查通过前阻止本轮结束;用验证子 Agent 或动态工作流,让一个新的模型尝试推翻结果,使做事的 Agent 不是给自己打分的那个[32]。其中有两条边界需要记住:/goal 的评估器不调用工具,只能判断 Claude 已经呈现在对话里的内容[53];Stop Hook 连续阻止结束有上限,默认连续 8 次,下一次阻止会被覆盖;调用工具会重置连续计数,可用环境变量调整,详见 19.6.4。因此它不是不可绕过的发布闸门[42]。
Codex 提供 /review 与 codex review,目标可以是未提交的改动、与基础分支的差异、某个提交或自定义提示;评审在源码里是独立的任务类型,不接受同轮 steer[14][20][27]。Codex 的 auto-review 要与它区分开:它是审批动作的审核人,不是代码评审,官方明说它是“换审核人”而不是授予权限[9]。评审规则可以写进最近的 AGENTS.md,官方建议把格式与 lint 检查留给 CI[6]。
OpenCode 的 2.x 文档没有专门的验证页,只有会话差异的接口 GET /api/session/{id}/diff,可以读到本会话的文件变化[68]。
这些材料指向同一个结论:验证要以当前差异和真实的执行结果为准,不以模型的自述为准。“测试通过”要能追溯到命令、退出状态和对应的代码版本;diff、命令输出、权限决策和未覆盖的范围,应当是人可以核对的交付物,而不只是一段说明。
19.4 异常:失败、中断与恢复
19.4.1 异常分类与证据现状
Coding Agent 的异常不止“工具报错”。至少有八类:工具失败与拒绝、用户中断与取消、上下文溢出与压缩失败、模型请求失败与重试、用量与限额、进程崩溃与会话恢复、文件回退,以及子 Agent 的失败与晚到结果。下表给出三个产品在每一类上的公开程度。
| 异常类型 | Codex CLI | Claude Code | OpenCode 2.x |
|---|---|---|---|
| 工具失败与拒绝 | 源码、文档 | 文档 | 源码、文档 |
| 中断与取消 | 源码、文档 | 文档 | 源码、文档 |
| 上下文溢出与压缩失败 | 源码 | 文档 | 文档、源码 |
| 模型请求失败与重试 | 源码、文档 | 文档 | 源码、文档 |
| 用量与限额 | 源码、文档 | 文档 | 源码;文档只有 Console 预算 |
| 崩溃后的恢复 | 未找到进程崩溃后续跑的描述;有 resume、fork | 文档(resume 的语义) | 源码有,文档没有对应页 |
| 文件回退 | 未找到现行的文件级撤销 | 文档 | 文档、源码 |
| 子 Agent 失败与晚到 | 源码、文档 | 文档 | 源码、文档 |
需要提醒的是,Codex 与 OpenCode 在这一部分的许多结论只来自源码,官方文档没有承诺,下文会逐处标注“源码事实,非文档承诺”;Claude Code 则完全依赖官方文档。这些结论没有经过故障注入验证。
19.4.2 工具失败、拒绝与取消:作为不同的结果回送
失败、拒绝、取消是三种不同的结果,可靠的运行时要分别回送,而不是合并成一个“失败”。
Codex 的工具失败默认不终止 Turn。RespondToModel 类的错误被写成工具输出,并把“需要后续采样”置为真,让下一次采样继续;只有 Fatal 才向上返回致命错误。非零退出码、超时和沙箱拒绝同样带着输出回送给模型。apply_patch 还有一个值得注意的情形:补丁可能在已经提交了一个前缀之后才被拒绝,事件仍然标记为失败,但本轮的差异会消费这个前缀,也就是文件修改不是原子的[21]。工具调用被取消时,回送给模型的是 “tool call cancelled”[20]。
Codex 的审批决定区分了“拒绝”与“中止”:Denied 表示这个动作不要执行,但会话继续,Agent 应当另想办法;Abort 表示 Agent 在用户下一条指令之前什么都不要做[21]。App Server 把客户端的 decline 映射为 Denied(“rejected by user”),把 cancel 映射为 Abort,客户端响应本身出错则映射为 Denied(“approval request failed”);两种拒绝的 Item 状态是 declined,请求失败是 failed[25][2]。随后 Denied 变成回送模型的拒绝结果,Abort 变成 TurnAborted 结束本轮,回送的文案会被截断到 900 token。源码还留有一条 TODO:ToolError::Rejected 同时被用于“用户拒绝”和部分“运行时失败”,所以不能把所有 declined 都解读为用户拒绝[21]。策略层面还有第三种拒绝:approval_policy = never 时,本该弹窗的请求直接得到拒绝理由,不会询问[21]。自动审核的拒绝则带着强约束文案——不得通过变通、间接执行或绕过策略去追求同一结果;官方文档称,同一个 Turn 内连续 3 次拒绝,或最近 50 次审核中累计 10 次拒绝,会中断本轮,审核的构建、会话和解析失败按“失败即关闭”处理,超时单独呈现,动作同样不会运行[9][8]。源码中的阈值是同样的 3、10、50,但应用服务器的说明写明,只有 circuit_break_action = "strict" 时,TurnAborted 才携带“拒绝过多”的错误,文档没有区分这一点,默认值下的行为本章没有验证[30][27]。
Claude Code 在权限提示上按 Esc 等于拒绝这个动作;后台子 Agent 的权限提示里,Esc 只拒绝那一次调用而不停止子 Agent[31][49]。auto 模式下被分类器拦截的动作会列入 /permissions 的“最近拒绝”,可以按 r 手动批准后重试;Claude 会收到拒绝的原因。连续被拦截 3 次或累计 20 次,auto 模式暂停并退回逐次询问,阈值不可配置;-p 下没有可询问的对象,被拦截的动作不运行,但 Claude 继续而不是停止整个运行[34]。Hook 的失败语义是一个容易被忽略的细节:PreToolUse 的 command、http、mcp_tool 类 Hook 超时,默认不阻断工具调用,command/http Hook 可在 v2.1.295 起配置 onFailure: "block" 改变失败行为,不能把这个配置泛化到 mcp_tool;退出码 2 在支持的事件上可阻断,其他非零退出码在默认语义下是非阻断错误[42]。也就是说,用 Hook 做强制检查时,要明确超时与失败时的行为,否则检查本身出问题会让动作放行。
OpenCode 的拒绝分两种形态。reject 不带说明,产生 DeclinedError:工具调用以 “The user declined this tool call” 失败,随后这一步以中断结束,回合停止;带说明的 reject 产生 CorrectedError,说明作为反馈交给模型,模型可以继续。同一个会话里其他待处理的权限请求会被一并拒绝。配置里的 deny 则产生 BlockedError,消息是 “Permission denied: <动作>” 或策略给出的原因[75][72]。非交互的 opencode run 在没有人可以批准时,默认自动拒绝并附上反馈,让模型“不要这个动作,继续”;--auto 则对没有被明确 deny 的权限自动批准。这是源码事实,非文档承诺[77]。
同一个“拒绝”在三个产品里后果不同:Codex 的 Denied 让 Agent 另想办法,Abort 则让它停下;OpenCode 不带说明的拒绝会让回合停下,带说明的才继续;Claude Code 的 auto 拦截会把原因告诉模型。对使用者的含义是,拒绝时附上理由,通常比单纯点拒绝更能让 Agent 改道(本书建议,OpenCode 的 CorrectedError 是这一做法的机制基础)。
19.4.3 上下文溢出与有损压缩
压缩是为了在上下文受限时继续推进,代价是细节丢失,而且压缩本身也会失败。
Codex 的自动压缩是阈值触发的,不是溢出之后的补救。普通采样请求遇到 ContextWindowExceeded 时,源码的处理是标记总 token 已满并把错误抛出,这条路径里没有自动压缩后重试;唯一的例外是自动审核的预算耗尽时的一次压缩重试[20]。压缩失败按相位处理:采样前压缩失败,先把新输入写入历史(保证不丢),再报错并结束本轮;回合中失败,报错并结束本轮;回合后失败,只发警告并保留已经完成的回答[20]。压缩请求自己遇到超限时,从最老的历史项开始裁剪并重试,注释说明从开头裁剪是为了保住前缀缓存并保留近期消息;只剩一项仍超限则失败[23]。这意味着单条过大的输入(比如超大的工具输出)仍可能让当前 Turn 直接失败,tool_output_token_limit 是前置的防线[5](系统推导)。
Claude Code 在接近上限时给出 “Prompt is too long” 或 “Context limit reached” 提示,自动压缩被关闭时会附带说明;自动压缩失败时错误带有原因[47]。如果单个文件或工具输出巨大,使压缩后立刻重新填满,数次之后会停止自动压缩并报错,不会无限循环,官方没有给出具体次数[31]。PreCompact Hook 以退出码 2 可以阻止压缩;如果这次压缩是为了从已返回的上下文超限错误中恢复,被阻止后底层错误会上浮,当前请求失败[42]。一个很实际的后果是:对话中口头说明的边界(比如“不要 push”)会被 auto 分类器当作阻断信号,但它不会被存成规则,而是每次从会话记录里重读,如果压缩把这条消息摘要掉,边界就可能丢失,官方的结论是硬性保证要用 deny 规则[34]。
OpenCode 的溢出恢复是:Provider 拒绝请求为“过长”且尚未产生输出时,压缩并重试一次;第二次仍被拒绝就作为错误返回。把自动压缩关掉,溢出恢复也一并关闭,只有用户可以压缩。溢出时压缩的目标会逐步缩小,首次取估算上下文的 70%,之后依次是 50% 和 35%[67][73]。压缩失败会发布失败事件,自动压缩失败使该步骤以失败结束;进程死亡留下的“运行中”压缩,会在下一次 drain 开头被标记为中断[72]。
三者的共同点是,摘要保存的是任务的连续性。恢复的第一步应当是回到仓库核对,而不是相信摘要:文件是否仍是记录中的版本,差异是否变了,验证针对的是否还是当前代码。让摘要保存目标、关键证据、差异、验证结果和待办,比保存对话的复述更有用(本书建议)。
19.4.4 中断与恢复:恢复了什么,没有恢复什么
中断。 Codex 的取消令牌贯穿采样、工具与审批等待。中断的流程是:先发取消,给任务 100 毫秒的宽限期自行结束,仍未结束就强制 abort,然后写入“被中断”标记,运行中断 Hook,发出 TurnAborted,并把会话记录刷到磁盘。源码注释解释了两处顺序:标记要先持久可见再发事件,因为有的客户端收到事件后会同步重读会话记录;待决审批要等取消被任务观察到之后再清理,避免审批等待以“模型可见的拒绝”的形式先于 TurnAborted 出现[22]。被中断的 Turn 会向模型历史写入一条说明:用户有意中断了上一轮,运行中的统一执行进程可能仍在后台运行,被中止的命令可能已部分执行[22]。这条说明由 agents.interrupt_message 控制,默认开启[5]。
Claude Code 的 Esc 立即停止,取消运行中的工具调用,保留上下文;用户中断不触发 Stop Hook[31][42]。OpenCode 的中断接口先确认受理、后异步清理,空闲时是空操作。终态事件带有原因,user 会取消后台作业并释放执行声明,shutdown 保留声明以便重启续跑[68][71]。被中断的步骤与工具作为 aborted 错误写进助手消息,仍处于流式或运行中的工具调用,会在下一次 drain 开头被统一标记为失败:“Tool execution interrupted”[72]。
会话恢复。 恢复读回的是历史,不等于世界回到了当时的样子。
Codex 的 thread/resume 有三种入口,对非运行中的线程按 history、非空 path、thread_id 的顺序取优先级,官方源码注释建议尽量只用 thread_id;恢复时使用与记录不同的模型,会发出警告,并在下一轮应用一次性的模型切换指令。thread/fork 可以在指定轮次之前或包含该轮分叉;分叉时若快照停在 Turn 中间,会追加与真实中断一样的 <turn_aborted> 标记[27][2]。“恢复不会接管旧的命令进程”这一点,本章没有找到 Codex 的直接陈述,只有间接证据:中断标记自己承认后台进程可能仍在运行,thread/revert 不改文件。所以对 Codex,这句话只能写成推导。
Claude Code 官方列出了恢复时的状态:恢复压缩后的摘要加未被摘要的消息,沿用原来的模型;恢复时仍在运行、被进程结束打断的工具调用既不会完成也不会重跑,Claude 会看到“被截断”的标记,并被告知先检查它是否已经生效(除非设置了 CLAUDE_CODE_RESUME_INTERRUPTED_TURN);有效的 /goal 延续但计数清零;会话级定时任务(未过期)恢复,后台 Bash 与 monitor 不恢复,后台子 Agent 与工作流变成“未完成”的备注;--mcp-config、--settings 等参数要重新传入[38]。官方还提到,后台会话(研究预览)是独立进程,可以脱离终端持续运行,这时“会话在不等于命令还在跑”的说法就不适用了[38]。会话文件写入失败有明确警告,例如磁盘写满时会提示最近的消息可能无法用于恢复,工作可以继续,但之后 --resume 可能缺少这些消息[47]。
OpenCode 的恢复机制最完整,也最容易被文档遗漏。开始执行时,Started 事件和执行声明(claim)在同一个事务里写入;成功、失败和用户中断释放声明,关停引起的中断保留声明。进程崩溃、SIGKILL 和优雅重启在数据库里留下相同的特征,启动时由重启续跑逻辑接手,并注入一条合成消息:“The server restarted while you were working. Continue from where you left off without repeating completed work.”[71]。源码注释把恢复语义写得很直白:恢复是至少一次的,本地协调防止的是并发的 drain,不是崩溃之后重复的外部副作用[71]。每个回合最多续跑 10 次,次数持久保存、只被终态事件重置,超过后以失败终结;运行中的后台 Shell 被记为 “Command cancelled because the server restarted”,运行中的后台子 Agent 会被恢复并在完成后通知父会话[71]。续跑只在服务以 service 模式运行(即共享后台服务)时安装,前台 serve 与 --standalone 私有进程不走这条路径(源码事实加推导)[71]。文档里没有对应的专页,这些都是源码事实,非文档承诺。OpenCode 的事件流按契约是易失的:慢消费者会溢出并使流失败,断线期间的事件不会补发,需要重新读取会话消息[68]。
19.4.5 回退边界:文件快照与外部副作用
三个产品的回退机制覆盖的对象并不一样。
| Claude Code | Codex CLI | OpenCode 2.x | |
|---|---|---|---|
| 回退什么 | 文件编辑与对话 | 对话历史 | 文件与对话 |
| 机制 | 每个启动一轮的用户 prompt 创建一个 checkpoint,编辑前快照;/rewind 提供六种动作(恢复代码和对话、仅恢复对话、仅恢复代码、从此处摘要、摘要至此处、取消)[37] | thread/revert 删除指定轮次及之后的所有 Turn,只改持久化的对话历史,不改本地文件;thread/rollback 已移除[27][2] | 每个模型步骤请求前尝试一次快照,步骤成功或失败时再一次;/undo 暂存回退,/redo 取消,提交新的 prompt 即提交回退[66][74] |
| 不覆盖 | 只被 Bash 修改、未被文件编辑工具纳入跟踪的文件;子 Agent 的编辑(通常);未纳入跟踪的外部与并发会话改动;数据库、API、部署等远端系统[37][31] | 文件系统;本章没有找到现行的文件级撤销机制,源码注释写明快照已不再产生[21] | 被忽略的文件、活动目录之外的文件、Git 元数据、进程与数据库等副作用[66] |
| 限制 | 只保留最近 100 个 checkpoint 的文件快照,约 30 天后清理,清理后回退可能失败;符号链接与硬链接被跳过并警告;官方明说不能替代版本控制[37][47] | 仅支持持久化的分页线程;官方最佳实践把“在同一批文件上不用 worktree 跑实时任务”列为常见错误[19][15] | 尽力而为,捕获失败只记警告;范围是位于 Git worktree 内的活动目录,已跟踪与未被忽略的未跟踪文件,单个文件上限 2 MiB;会话运行中调用回退返回 409;没有 Git 仓库时只能回退对话[66][74] |
三点补充。第一,OpenCode 的快照存放在数据目录下独立的 Git 对象库里,不创建提交、不移动分支,但可能包含完整的文件内容,官方提醒要把数据目录当作敏感数据;提交回退后消息从活动视图中移除,但不会从历史或快照存储中删除,官方明说这不是安全擦除;恢复会覆盖当前内容,包括取消暂存时[66]。第二,Codex 文档的指标表里仍有 task.undo 一项,但命令文档与源码里没有找到对应的用户入口,所以本章不写“Codex 有文件回滚”[4]。第三,Claude Code 的回退菜单在回退到较早的位置后,如果没有发新 prompt 就退出,被回退的消息可能在下次恢复时回来;2026-10-30 补核的文档还明确,文件一旦被文件编辑工具纳入跟踪,后续 Bash 对该文件的改动也可以被回退,因此不能笼统说“Bash 改动都不覆盖”。[37]
这三套机制共同的边界是:回退能力必须和副作用类型对应。文件快照覆盖不了数据库迁移、API 请求、远端仓库操作和部署,这些需要单独的幂等、补偿或审计设计。
19.4.6 多 Agent 的晚到、冲突与收敛
Codex 对子 Agent 消息的归属有明确建模。回答已经展示之后,晚到的子 Agent 邮件保持排队,留给下一轮;如果之后同一任务又有显式的同轮工作(被 steer 的用户提示,或未打标签的前言后的工具调用),才重新打开“当前轮”。子 Agent 的终态结果回送父 Agent 是尽力而为,指标区分“入队成功”和“投递失败”[20][27]。V2 的 interrupt_agent 不允许中断根 Agent 与自己,返回的是中断之前观察到的状态快照;目标未加载或已死亡视为成功中断[27]。在非交互流程里,需要新审批的动作会失败,并把错误回传给父工作流;非活动线程的审批请求,也可能在用户看着主线程时冒出来[10]。
Claude Code 的子 Agent 因 API 错误提前结束时,前台子 Agent 返回带“被中断未完成”标记的部分输出,后台子 Agent 被标记失败,通知中带有错误名和最后的输出,不丢失已完成的工作;配置了备用模型链时自动切换,并在报告首行标注;并发上限为 20,超过后 Claude 被告知不要重试[47][44]。Agent teams 是实验功能,默认关闭,/resume 与 /rewind 不恢复进程内的队友[44]。
OpenCode 的子 Agent 失败以工具失败的形式返回父 Agent,并带上子会话 ID,方便继续:“Subagent failed (sessionID: …)”或“Subagent cancelled”;前台子 Agent 被中断时,同时中断子会话并取消作业;后台子 Agent 立即返回“正在后台工作”,完成后通知父会话[76]。
晚到的结果、被取消的任务和失败的子任务,都需要有明确的归属和终止条件。“spawn 之后等待”不是协作的全部,消息属于哪一轮、取消之后迟到的结果怎么处理,是更容易出问题的部分。
19.4.7 限额、限流与 Provider 故障
| 异常维度 | 产品 | 固定来源下的行为 |
|---|---|---|
| 自动重试 | Codex CLI | 退避从 200 毫秒起翻倍,最多 10% 抖动;stream_max_retries 默认 5(源码上限 100),request_max_retries 默认 4,stream_idle_timeout_ms 默认 300000[5][24] |
| 自动重试 | Claude Code | 瞬时失败最多重试 10 次,指数退避(CLAUDE_CODE_MAX_RETRIES 默认 10)[47] |
| 自动重试 | OpenCode 2.x | 指数退避自 2 秒起,间隔上限 10 秒,最多 10 次,带抖动,总等待约 84 秒;Provider 的 retryAfter 取较大者,上限 15 分钟;传输超时类最多 3 次;响应头与流式块间隔的超时默认各 5 分钟[72][60] |
| 不重试的情形 | Codex CLI | ContextWindowExceeded、UsageLimitReached、QuotaExceeded、InvalidRequest、Sandbox、Interrupted 等;ServerOverloaded 仅当服务端给出建议延迟时才重试[24] |
| 不重试的情形 | Claude Code | TLS 证书失败;已完成一个文本块或工具调用之后的服务器错误、断连或停滞;响应已完整之后的失败;组织策略拒绝[47] |
| 不重试的情形 | OpenCode 2.x | 未找到不重试情形的完整清单;限流与 Provider 内部错误属于可重试的分支[72] |
| 流中断之后 | Codex CLI | 连接类失败默认无限重试,5 秒起翻倍、上限 60 秒(unbounded_connection_retries 默认开);重试用尽后可从 WebSocket 回退到 HTTPS[24] |
| 流中断之后 | Claude Code | 无人值守时,对“含文本、无工具调用”的截断响应自动让 Claude 继续,最多连续 3 次;交互会话里回复 continue[47] |
| 流中断之后 | OpenCode 2.x | 尚未输出可整体重试;已开始输出的中断流重试并注入“上一次回答被中断,从中断处继续”的合成提示;传输层的整体恢复只尝试一次[72] |
| 用量限额 | Codex CLI | UsageLimitReached 不自动重试,但会刷新并保存最新的速率限制快照;TUI 在用量达 50%、75%、90%、95% 时预警,90% 时建议切换到更省额度的模型;官方称活动 Turn 触及限额时,Agent 可以继续该 Turn,受公平使用限制[24][18] |
| 用量限额 | Claude Code | session 与 weekly 限额跨模型共享,换模型无效;Opus 或 Sonnet 系列限额只限该系列;v2.1.234 起,交互会话在 claude.ai 订阅登录下可等待重置后自动续跑,且不重发上一条消息,再次触限最多再等 2 次[47][49] |
| 用量限额 | OpenCode 2.x | 文档只写了 Console 的预算:超限请求返回 HTTP 429,属于产品说明,不是本地运行时功能;本地 Provider 的限流走重试分支[60][72] |
Claude Code 的重试规则里有一条值得单独引用的理由:已经完成了一个文本块或工具调用之后,如果出现服务器错误、断连或停滞,不会整体重试,因为“重新发送请求可能把同一批工具调用执行两次”,所以改为保留 Claude 已完成的输出,运行已完成的工具调用,并从它们的结果继续(v2.1.199 起)[47]。这是“重试要先区分有没有副作用”最直接的官方例证。无人值守时可以设置 CLAUDE_CODE_RETRY_WATCHDOG=1,对 429 与 529 无限重试,但对“花费上限或用量积分耗尽”的 429 立即失败[47]。
OpenCode 的输出被长度截断时,源码追加一条合成提示,要求把剩余工作拆小;连续三条助手消息都因长度结束就失败,错误是“仍然被截断”[72]。
以上数字只对各自产品、各自版本、各自配置口径成立,不能互相比较。
19.4.8 公开证据未覆盖的保证
以下范围限定为本章固定源码提交与已列官方文档。没有直接证据支持的机制不当作默认保证;新版本需重新核对,不能从检索未命中推断实现绝对不存在。
- Codex:现行的文件级撤销机制;Plan mode 是否有运行时的强制,只在提示词模板里读到“可以执行不改变状态的动作,不得执行会改变状态的动作”的规则,不能写成沙箱级的保证[29];进程崩溃后自动续跑的机制;“恢复不接管旧进程”的直接陈述。
- Claude Code:Agent Loop 的内部终止判据与线程模型;会话 JSONL 的条目格式(官方称属内部实现);压缩“反复重新填满”保护的具体次数[31][38]。
- OpenCode:中止时运行中的 Shell 子进程是否被终止;重启后待处理的权限请求是否保留,源码里只见内存中的映射,位置关闭时会被取消;分叉的内部实现;压缩模型是否可配置,文档与源码互相矛盾[67][73][75]。
19.5 实践:按影响范围决定授权
19.5.1 先定义可审查交付
一个任务应说明允许读取与修改的路径、预期产物、验证命令、停止条件和外部副作用边界。代码差异与运行证据是验收对象;最终说明不能替代仓库状态。通用任务契约见上下文工程,本章关注三种产品怎样承载它。
| 工作 | 可先授予的能力 | 增加能力前的证据 |
|---|---|---|
| 阅读与定位 | 项目范围只读工具 | 来源可追溯、敏感路径不可读 |
| 小范围可逆编辑 | 明确工作区写入 | diff 可审查,验证命令可信 |
| 运行构建或测试 | 限定命令和环境 | 依赖、网络、子进程与凭据边界清楚 |
| 外发、部署或数据变更 | 按具体对象单独授权 | 影响预览、当前身份、批准与恢复责任 |
| 长时间无人值守 | 受限执行器与有界任务 | 预算、停止、日志、未知效果和人工接管均可观察 |
“只运行测试”也要看仓库脚本。测试命令可能安装依赖、调用网络或触发钩子,不能仅凭名字判为无副作用。工作区隔离能降低文件冲突,不能自动隔离共享数据库、发布凭据和外部接口。对并发写入指定单一写者或使用隔离工作区,合并时再验证共同状态。
19.5.2 策略、沙箱与 Hook 的执行强度
权限策略决定动作是否获准,OS 沙箱决定进程能实际触达什么,Hook 决定某些生命周期点运行什么逻辑。Hook 的超时、错误和放行策略会改变阻断强度;存在默认放行或未覆盖的执行路径时,不能把它等同于资源端 deny。先检查有效策略与沙箱,再把 Hook 用作补充检查与审计。
下表把隔离实现、网络入口和启动失败分开。它描述核验日的文档及固定源码,没有在这些平台上实测。网络默认指受限执行路径;解除沙箱、排除命令或外层容器策略会改变结果。[78][79][80]
| 产品与平台 | OS 实现与覆盖范围 | 网络默认与配置 | 无法实施隔离时 |
|---|---|---|---|
| Codex,macOS | Seatbelt,按实际沙箱策略限制工具进程 | 受限模式默认不启用网络;workspace-write 可设 network_access=true,托管代理/域名策略另行控制 | 无法实施所需受限策略时拒绝执行,不能解释成默认裸跑 |
| Codex,Linux/WSL | bubblewrap 与 seccomp;固定源码保留旧 Landlock 兼容路径 | bwrap 按策略加 --unshare-net;受管代理可形成有策略的网络路径 | legacy Landlock 不能隔离 App Server Unix socket,受限文件系统策略下会拒绝该不兼容组合 |
| Codex,原生 Windows | 官方区分 MXC/进程隔离与旧 elevated/unelevated 路径;WSL 使用 Linux 路径 | 取决于所选实现与有效策略,不能套用 Linux namespace 结论 | 必须核对实际后端与启动日志,本文不声称全后端能力相同 |
| Claude Code,macOS | Seatbelt(sandbox-exec);沙箱默认关闭,启用后覆盖 Shell 及子进程 | 无直接外连,走本地代理;allowedDomains 初始为空,未列域名按权限模式处理 | 默认依赖缺失/平台不支持时可无沙箱运行;sandbox.failIfUnavailable=true 改为启动失败 |
| Claude Code,Linux/WSL2 | bubblewrap+socat,范围同上;原生 Windows 不支持此沙箱 | strictAllowlist 等策略可拒绝未列域名;WebFetch 自有权限规则,不受此列表直接约束 | 除 failIfUnavailable,还应检查 excludedCommands 和无沙箱重试是否被允许 |
| OpenCode,所查固定提交 | 检查 Permission.evaluate、Shell 插件与 Shell.create 路径,未见同类 OS 沙箱装配 | 本章所查命令路径依赖宿主网络;webfetch 规则不自动约束 shell 内的 curl | 需要由宿主容器/专用账户/网络策略提供隔离;这里是有限源码观察,不是全产品否定结论 |
启用沙箱也不自动隐藏继承的环境变量或已有凭据。一个允许网络的命令若拿到广权限令牌,仍可能调用远端生产服务;本地只读策略不能撤回这种远端权限。部署时用受限测试身份,核对一个禁止路径、一个未允许域名和一次沙箱启动失败,分别确认拒绝发生在哪里;这些是本书建议的验收动作,不是本章已经执行的实验。
把产品配置变成部署验收
以下是本书建议的验收矩阵,复用17.5.6 的跨层测试,不表示本章已经运行这些产品实验。使用合成秘密、测试账户和受控接收端,固定产品版本、平台、启动参数及生效配置。
| 验收问题 | Codex CLI 检查位置 | Claude Code 检查位置 | OpenCode 2.x 检查位置 |
|---|---|---|---|
| 项目能否放宽组织禁止项 | requirements 与实际生效约束 | managed 规则及配置合并结果 | Policies 与权限规则组合结果 |
| 获准执行后能否读取测试秘密 | 实际读限制、环境变量和后端 | Read / Shell 各自权限与沙箱读取范围 | 工具权限与宿主文件/凭证隔离 |
| 命令是否能绕过网络控制 | 代理、直接连接、当前批准范围 | Shell 代理、排除命令、无沙箱重试 | Shell 宿主网络与 webfetch 独立规则 |
| Shell 断网后能否经别的工具外发 | MCP / app / 浏览器等实际启用路径 | MCP、文件工具、Hook、WebFetch 等独立路径 | 外部工具、插件与服务端授权路径 |
| 必需隔离不可用时会发生什么 | 当前平台后端与失败记录 | failIfUnavailable、excludedCommands 与重试设置 | 外层容器/执行服务是否拒绝启动 |
矩阵中的检查位置来自权限、沙箱与审批和本节前表的事实边界,不能跨列套用配置键。每项同时保存一次允许操作和一次禁止操作的实际结果;若前置规则已拦截,另用合法测试夹具独立验证后端,不能据此声称沙箱也已验证。只读工作流也要测试数据读取和外发,审批通过也要核对实际执行范围。
19.5.3 按可逆性和影响范围逐级放权
任务设计保留四项输入:目标、当前上下文、约束、完成条件。Codex 将它们归纳为 Goal、Context、Constraints、Done when;Claude Code 的最佳实践强调给出可执行验证、尽早纠偏和控制上下文。[19][32] 这些输入不等于权限:一份清楚的任务描述仍需匹配实际执行边界。
| 决策问题 | 可采用的工作方式 | 放权前要看到的证据 | 回退边界 |
|---|---|---|---|
| 只需理解代码吗 | 限范围读取与搜索,按需补充材料 | 来源、适用版本、未决问题 | 可重新调查,仍需控制敏感内容读取 |
| 需要修改本地代码吗 | 独立分支/检出、限定写入目录、运行验证 | 实际 diff、测试输出和影响范围 | git/checkpoint 只覆盖各自跟踪的文件变化 |
| 要安装依赖或联网测试吗 | 明确网络出口、测试身份与依赖来源 | 依赖变更、锁文件、有效沙箱与域名策略 | 远端调用及安装脚本的效果不由文件回退撤销 |
| 要提交、发布或改远端数据吗 | 将候选产物、批准和执行分开 | 具体版本、目标、审批范围及资源端回执 | 需要远端恢复流程,不能通过会话 rewind 撤销 |
| 要批量或并行修改吗 | 先少量试跑、独立工作区、明确写者和合并者 | 单项差异、整体测试与冲突处理 | 分支隔离不隔离共享数据库、凭据或外部 API |
Claude plan 模式用于先分析,但不是 OS 只读保证:auto 可用时允许分类器批准的命令,交互会话中 bypass 可用时还有例外,判断以有效权限模式与沙箱为准。[34] Claude 的 checkpoint 不跟踪 Bash 造成的全部文件变化,不能替代版本控制;worktree 分开文件检出,也不自动隔离网络和凭据。[32][51]
把稳定、无法从代码猜到的约定放进项目指令;把条件使用的知识放进 Skill;让 MCP 或工具提供外部能力;用 Hook 做有明确失败语义的检查;用子 Agent 分隔调查上下文。不要为每一种输入重复维护同一条长规则。项目指令过长时先删除可从代码恢复的信息,再观察是否影响任务验收;一条强调过的句子不构成强制边界。[32][45]
例如“修复会话超时后的登录失败”,完成条件应是能复现的失败用例通过、既有测试未回归、公共接口保持约定。Agent 交付 diff 和实际执行记录,使用者再决定是否提交。若测试需要生产令牌,先提供受限测试身份,不能因本地修改可逆就放开生产权限。批量迁移也先固定清单,在少量文件验证后扩大;每批冻结输入与验收口径,失败时保留是哪一批、哪些文件和哪些检查尚未完成。
19.6 调优手册:从组件改动到可审查补丁
本节使用局部代码修复案例,因为 ALR 的只读诊断不能替代补丁和测试验收:用户问题对应任务输入,代码/失败测试对应证据,编辑对应行动,diff 与执行结果对应产物。以下目录、测试名、配置组合和运行轨迹均为教学夹具,未在三个产品上开展横向基准。
19.6.1 按组件评估与优化
先从同一仓库快照重放失败,找首次偏离,而不是看到“没修好”就提高推理强度。通用诊断单见组件调优,本章将它落到代码任务。
| 组件 | 定位证据 | 具体优化 | 不能用什么代替验收 |
|---|---|---|---|
| 指令文件 | 实际加载的规则、cwd、嵌套目录与冲突 | 写清可编辑范围、真实测试命令、不得削弱断言 | 文件存在或模型复述规则 |
| Skill | 是否触发、首个有效定位、复现与验证产物 | 为竞态修复补“复现→状态分析→反例→补丁→回归” | 多读一个 Skill 文件 |
| 模型 / effort | 相同代码、失败测试和观察下的推理差异 | 单独比较模型或强度,保留总预算 | 同名 high,或更自信的解释 |
| 工具 / MCP | 搜索范围、错误、测试退出码、截断和异步结果 | 收窄搜索,保留完整日志引用,明确未完成/失败 | 测试命令被调用或终端有绿色文字 |
| 仓库知识 | 依赖锁、目标版本文档、历史设计与实际代码 | 只检索适用版本,保留符号/文件来源 | 最新网页或相似 issue |
| Memory / 上下文 | 压缩前后任务范围、已验证与未验证状态 | 保留基线 commit、限制、失败位置、待跑检查 | “继续之前的修复” |
| 编排 | 同文件并发写入、重复研究、集成失败 | 默认单一写者;确需委派时隔离并统一合并验收 | 各分支单测通过 |
Codex 用 AGENTS.md 承载项目指导、Skill 承载可复用方法;Claude Code 对应项目指令与技能机制,OpenCode 2.x 的指令/技能入口按其自身配置加载,不能直接复制另一产品的目录和优先级。[6][11][40][43][61][65] 调优时比较最终有效材料,而非三个配置文件看起来是否一致。
模型实验也要记录实际配置:Codex 提供模型、推理与速度相关选项,Claude 的 effort 受模型支持和模式约束,同名强度不能当作同一计算量。[17][48]
指令可以从“修好后运行测试”改为下面这份项目规则候选。它是行为要求;范围与发布限制还要由执行环境和 CI 强制检查。
Edit scope: src/auth and tests/auth only.
Do not change public timeout defaults, lockfiles, or test expectations to hide failures.
Before editing: reproduce the target failure and identify its state transition.
Before completion: report the final diff and actual results of required checks.
If a check cannot run, name the reason and leave verification incomplete.
Do not commit, push, publish, or deploy without separate authorization.
工具输出也应让模型区分“零个测试”和“测试通过”。团队封装测试工具时,建议返回 command / cwd / source_revision / exit_code / collected / passed / failed / skipped / complete / log_ref。这些是教学契约字段,不是三个产品已有的统一 API。超时或截断后保留日志引用;不得把 exit_code=0, collected=0 当成完成了要求的测试。
19.6.2 案例:登录超时修复为何越改越快、却仍然错误
假设夹具仓库中 src/auth/login.ts 管理请求完成、超时与用户取消,tests/auth/login.test.ts 有对应测试;项目已锁定工具链,目标检查命令为 npm run test:auth,类型检查为 npm run typecheck。这些是待准备的示例文件和脚本,不是本仓库实际可运行实验。
基线 Bad Case:Agent 读取大量无关日志后,把“修复超时”简化为“延长 timeout”;将公开默认值调大,只运行正常登录测试,然后声明全部完成。首次偏离是没有把目标失败和不变量写入任务契约,后续还存在搜索范围过宽和验收不完整。这里换模型可能改变表现,但不能证明契约缺口已经被修好。
先冻结缺陷代码、锁文件、测试数据、工具权限和模型。测试夹具用可控时钟与请求替身,不依赖真实网络抖动。独立验收至少覆盖:
| 测试 | 夹具输入 | 必需断言 |
|---|---|---|
| 正常完成 | 截止时间前收到有效响应 | 仅完成一次,清理计时器与监听器 |
| 确认超时 | 响应未到,推进到截止时间 | 返回约定超时结果,并取消在途请求 |
| 用户先取消 | 取消发生在截止时间前 | 保留取消语义,不被后来计时器改写 |
| 超时后迟到成功 | 超时已终态,再注入成功响应 | 不创建登录会话,不二次结算 |
| 取消/完成竞争 | 按夹具定义的两种可控顺序触发 | 遵守已声明优先级,终态只提交一次 |
| 工具或环境失败 | 检查超时、缺依赖、零测试收集 | 不宣布测试通过,保留未验证原因 |
先定义竞争语义再写断言;不能把调度偶然性写成“谁大概更早”。验收测试和执行脚本由独立评审保护,Agent 可以补充回归测试,不能删除或放宽独立断言。只检查其自行新增的测试,会让候选同时改题和答题。
最小改动方案分开进行:
- 只改任务指令,加入范围、不变量与完成证据;检查它是否仍通过延长默认值绕过缺陷。
- 只改 Skill,将步骤写成“原版复现→标出请求状态与唯一终态→构造迟到/取消反例→最小补丁→目标与关联回归”。每步产物分别是失败记录、状态草图、测试、diff 和执行回执。
- 只改搜索与工具输出策略:先搜索目标符号及其调用者,读取匹配范围;测试输出先给摘要和完整日志引用。少读无关日志,但不能截掉反例失败。
- 只改交接摘要,保留基线、允许目录、终态不变量、当前 diff、实际已跑与待跑检查;在长任务中插入一次压缩再要求继续。
- 固定上述证据,再单独比较模型或 effort;若候选漏掉迟到响应,不能因为更快就通过。
预期轨迹是:
同一快照复现迟到响应失败
→ 读取登录状态转换和测试约定
→ 补取消、超时与迟到结果的反例
→ 修改终态保护与清理逻辑,不延长默认超时掩盖问题
→ 运行目标、关联回归和类型检查
→ 按最终代码状态独立检查 diff 与测试证据
→ 返回补丁、已验证行为和仍未验证的边界
这里不预先规定实现必须采用某一种状态机或 API;等价正确实现也应通过。验收绑定最终 diff/hash,测试后又改文件就要重验。
| 实验 | 单一变量 | 预期收益 | 否定该假设的观察 |
|---|---|---|---|
| A | 指令中的完成契约 | 减少虚假完成 | 规则可见但仍只跑正常路径 |
| B | Skill 的反例步骤 | 提升竞态覆盖 | 测试更多但迟到成功仍创建会话 |
| C | 搜索/日志结果策略 | 减少重复读取与输入量 | 降本来自遗漏错误日志 |
| D | 压缩后的任务状态 | 保留修改范围和待验证项 | 继续任务时改了依赖或忘记检查 |
| E | 推理强度 | 同验收下改变耗时/成本 | 更快但关键切片由 passed 变 failed |
先从同一基线分别运行,再将有效改动组成候选并做消融。开发案例用于选择方案,留出修复任务用于确认泛化;不能只把同一个 bug 多写几种提示当成独立样本。
19.6.3 统一评测、配置发布与回滚
三个产品使用同一任务验收,但保留入口差异。Codex 非交互输出与沙箱、Claude 非交互参数、OpenCode API/事件各有契约;交互时能询问用户,不意味着 CI 无人值守也能完成。[13][50][68]
若使用 Codex 自动化,还需另查执行身份、环境权限与失败记录,不能把一次本地交互任务的批准直接复用为定时任务的授权。[16]
| 记录项 | 本章实际要保存什么 |
|---|---|
| 制品 | 产品版本、入口、模型/推理设置、规则/Skill/插件 hash、有效工具与策略 |
| 任务 | 仓库基线与初始工作区差异、锁文件、环境、修改范围、独立测试版本 |
| 运行 | 全部尝试、工具调用、拒绝/超时/取消、压缩、人工纠正和终态 |
| 验收 | 最终 diff/hash、实际测试命令与结果、范围检查和审阅记录 |
| 总账 | 输入/输出/缓存/推理用量、费用来源、总时长及未完成任务 |
计价桶先对齐:Claude 本地费用是估算,OTel 输入与缓存分桶需分开;Codex 和 OpenCode 的推理统计也不能直接按同名字段相加。[33][52][4][77] 对所有分配任务计费,包括失败、重试、摘要、验证和人工返工记录;模型自称通过但无回执时不是验收成功。没有价格或订阅无法归因到单次任务时,报告用量与不可比项,不填零成本。
团队发布的是一组 Agent 工作配置,不是把补丁自动发到生产。离线与灰度按统一发布手册执行:先在隔离仓库跑冻结回归,独立留出集通过 Gate 后,限定一种修复任务或少量受控仓库灰度。默认保持单一写者即可完成本例;不要把新增委派作为必需优化。
出现目录越界、削弱测试、泄密、未批准远端操作或关键反例退化,立即停止候选相关派发。其他质量、耗时和费用停止线须事先结合负载声明,不给出不分项目的统一百分比。
回滚分三个对象处理:
- Agent 配置:恢复经过验证的规则、Skill、模型和工具组合,确认新会话实际加载;在途会话选择排空、暂停或重建,不把旧摘要无条件带入新组合。
- 代码补丁:保留初始用户改动和当前 diff,按审阅批准撤销候选修改;不能用一次整仓硬重置清除用户工作。回退后重新运行相关检查。
- 外部动作:已经推送、发消息或修改远端状态的动作沿原身份核验与补偿,不能靠 rewind、快照或恢复配置撤销。文件回退能力的产品边界见 19.4.5。
19.6.4 版本敏感事实:值、适用路径和证据
下表集中记录容易被误当成永久常量的值。源码固定点与滚动文档属于不同证据,均不自动对应读者机器上的二进制;源码与文档不同,应保留差异并核对发行版,不能一概宣称某一方覆盖另一方。除注明外,下面各项核验于 2026-10-24,未运行产品实验。
| 主张/事实 | 值与适用条件 | 证据与升级边界 |
|---|---|---|
| Codex 子 Agent 深度 | 固定提交 V1 的 agents.max_depth 默认 1;配置注释明确 V2 忽略此字段 | 源码事实,86e17e98c7f9,config/mod.rs 的 DEFAULT_AGENT_MAX_DEPTH 与 V1 注释[81];先确认工具集 V1/V2,不能把 1 泛化为所有多 Agent 路径 |
| Codex 受信配置 | 项目 trust_level="untrusted" 仍可禁用项目配置;退役的是审批策略同名值 | 官方文档事实,permissions/agent-approvals-security[78];分开检查项目资源加载与工具审批的有效配置 |
| Codex Linux 实现开关 | 固定提交 UseLinuxSandboxBwrap 已标 Removed;UseLegacyLandlock 标 Deprecated,默认 false | 源码事实,features/src/lib.rs;bwrap.rs 的网络 namespace 参数[79];不复制旧文章中的启用 bwrap 实验开关;检查受限策略兼容性 |
| Claude Stop Hook 连续阻止 | 默认 8,工具调用重置;CLAUDE_CODE_STOP_HOOK_BLOCK_CAP 可调整 | 官方文档事实,Hooks 的 Stop 章节[82];不是一次任务只允许 8 次,更不是不可覆盖的提交闸门 |
| Claude Hook 失败时阻断 | command/http 的 onFailure:"block" 要求 v2.1.295+;默认 continue | 官方文档事实,Hooks 的 Block the action when a hook fails[82];检查事件是否可阻断、Hook 类型、超时与启动错误,不推广到所有 Hook |
| Claude 启动指令文件 | CLAUDE.md ≤4 MiB 全文加载,>4 MiB 整份跳过;子目录规则另有限制 | 官方文档事实,Memory 的文件上限说明[83];200 行是建议,不是启动硬上限;导入文件仍占上下文 |
| Claude 子 Agent 嵌套/并发 | v2.1.219 起默认最多主会话下三层;并发默认 20,v2.1.217+,ultracode 豁免;恢复等入口还有例外 | 官方文档事实,Subagents 的 depth/concurrency 章节[84];两个环境变量分别控制深度与并发;不限制全会话累计创建数 |
| OpenCode 权限匹配 | 最后匹配生效,无匹配 fallback 为 ask;Agent 默认策略可先匹配 allow | 源码事实,c93aaae37157,Permission.evaluate[75];区分算法兜底和实际规则,不把 ask 说成默认所有动作都会询问 |
| OpenCode 子 Agent 深度 | experimental.subagent_depth ?? 1,达到上限拒绝启动 | 源码事实,固定提交 tool/plugin/subagent.ts 的 depth 检查[76];启动权限与子 Agent 自身权限分别检查 |
版本表承担数字与配置的追溯,正文承担选择逻辑。性能改善、隔离有效性和任务完成率都需要对应环境下的测量;本章没有把文档事实或源码观察记成运行验证。
本章小结
三个产品都把模型放进了一个由上下文、工具、权限和验证组成的闭环,公开程度和实现重点不同:Codex 与 OpenCode 可以沿固定提交的源码追踪运行时,Claude Code 只能依据官方文档分析它的外部行为与配置边界。
从实现看,有几条判断反复出现。会话、本轮执行和工作区是三种不同的对象,会话历史不能替代工作区的当前状态。界面出现不等于首个模型请求可以发送,服务启动也不等于会话已经建立。审批决定动作是否放行,沙箱限制放行后的进程能碰什么,两者不能互相替代。拒绝、失败和取消是不同的结果,应当分别回送。压缩保存的是任务的连续性,不是工作区的事实,恢复之后先回仓库核对。回退能力必须和副作用类型对应,文件快照覆盖不了数据库、接口和部署。
组成部分可以沿五条线阅读:控制面与执行面的组件协作,Session/Turn/调用/工作区的对象关联,历史与运行事实的存储分工,交互、工具、外部效果和验收的独立状态,以及贯穿这些边界的修复链路。第 17 章的 Task、Run、Operation 和持久恢复协议是检查框架;某个产品拥有会话、快照或 resume 命令,不代表它已经提供这套框架的全部保证。
任务、上下文、边界和验证共同决定交付;权限按可逆性与影响范围逐级放开。调优从可复现失败出发,把指令、方法、工具、上下文和模型分开实验,再用最终补丁与独立测试验收。成本包括失败、重试、摘要和验证;配置回退、补丁回退和外部动作补偿分别处理。
本章基于静态源码与官方文档,未运行产品。源码、文档与安装版本可能不同,升级时按 19.6.4 复核;OpenCode 的实践部分为功能解释与本书建议。
附录 A:阅读一个 Coding Agent 仓库的方法
分析另一个 Coding Agent 时,可以沿着下面的问题追源码,而不是从目录树开始随机浏览。表中的入口来自本章已经核对过的固定提交。
| 问题 | 应找的代码或文档 | 本章的例子 |
|---|---|---|
| 一轮会话如何开始和结束? | Session 或 Turn 的入口、请求队列、取消路径 | Codex 的 run_turn 与任务中断;OpenCode 的 drain 与执行声明[20][22][71][72] |
| 模型请求如何携带上下文? | 提示词与上下文构造、历史存储、压缩逻辑 | Codex 的压缩模块;OpenCode 的 compaction.ts[23][73] |
| 工具如何注册和执行? | 工具 schema、路由器、执行器、结果格式 | Codex 的 ToolRouter;OpenCode 的 Tool.snapshot[21][76] |
| 谁能批准或拒绝操作? | 权限规则、审批请求、Hook、策略的优先级 | Codex 的审批阶段与编排;OpenCode 的 permission.ts[21][75] |
| 进程实际受哪些资源限制? | 沙箱、路径控制、网络策略、子进程入口 | Codex 的独立沙箱 crate;OpenCode 的 shell 权限说明[21][63] |
| 多 Agent 如何通信和隔离? | 子任务创建、消息投递、工作区、取消与晚到结果 | Codex 的邮箱投递相位;OpenCode 的子 Agent 工具[20][76] |
| 什么证据能证明完成? | 测试结果、差异、会话状态、交付协议 | 会话差异接口、评审任务[68][27] |
| 哪些功能只在托管环境可用? | 云端接口、认证层、开源清单 | Codex 的开源清单[1] |
读源码时有三条纪律。第一,结论绑定提交、版本或核验日期;主分支当天就会前进。第二,README 和注释可能滞后于代码,例如 Codex 的协议 README 仍在描述已经不存在的 core 与 TUI 的直接通信,要以代码和依赖关系为准[29][25]。第三,没有公开证据时,明确写“未找到”或“未知”,比补造一套内部架构更可靠。
参考资料
官方文档是滚动页面,没有版本快照;源码一律钉到固定提交。Codex 固定提交的提交时间为 2026-10-22 06:32 UTC,OpenCode 固定提交对应包版本 2.1.2。
-
OpenAI,Open Source,Codex 开源组件清单与非开源组件说明;核验于 2026-10-22。
-
OpenAI,Codex App Server,连接握手、Thread / Turn / Item、审批、错误与限流事件;持续更新,核验于 2026-10-22;对象定义与生命周期于 2026-10-30 补核。
-
OpenAI,Config basics,分层配置、受信项目与特性开关;持续更新,核验于 2026-10-22。
-
OpenAI,Advanced configuration,模型提供商、OpenTelemetry 与指标、状态位置;持续更新,核验于 2026-10-22。
-
OpenAI,Configuration reference,配置键与默认值;持续更新,核验于 2026-10-22。
-
OpenAI,Custom instructions with AGENTS.md,指令发现、加载与大小上限;持续更新,核验于 2026-10-22。
-
OpenAI,Sandboxing,沙箱模式与受保护路径;持续更新,核验于 2026-10-22。
-
OpenAI,Agent approvals & security,审批策略与安全;旧地址
developers.openai.com/codex/security现指向另一产品 Codex Security,不要使用;持续更新,核验于 2026-10-22。 -
OpenAI,Auto-review,自动审核与连续拒绝的中断条件;持续更新,核验于 2026-10-22。
-
OpenAI,Subagents,子 Agent 的继承、配置与成本;持续更新,核验于 2026-10-22。
-
OpenAI,Build skills,Skill 的渐进披露与上下文预算;持续更新,核验于 2026-10-22。
-
OpenAI,MCP,MCP 配置、超时与审批;持续更新,核验于 2026-10-22。
-
OpenAI,Non-interactive mode,
codex exec的沙箱、输出与 CI 凭证;持续更新,核验于 2026-10-22。 -
OpenAI,Developer commands,命令与斜杠命令;持续更新,核验于 2026-10-22。
-
OpenAI,Git worktrees,worktree 的行为;持续更新,核验于 2026-10-22。
-
OpenAI,Automations,定时任务的权限与失败;持续更新,核验于 2026-10-22。
-
OpenAI,Models;Model selection;Speed,模型、推理强度与快速模式;持续更新,核验于 2026-10-22。
-
OpenAI,Pricing,用量、限额与节省额度的建议;持续更新,核验于 2026-10-22。
-
OpenAI,Best practices,官方最佳实践指南(HTML 页,
.md孪生页返回 404);持续更新,核验于 2026-10-22。 -
OpenAI,core/src/session/turn.rs;core/src/session/session.rs;core/src/state/session.rs;core/src/state/turn.rs;core/src/function_tool.rs,Turn 主循环、Session 与 Turn 状态、取消与失败的回送;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,core/src/tools/router.rs;core/src/tools/approvals.rs;core/src/tools/sandboxing.rs;core/src/tools/orchestrator.rs;core/src/tools/events.rs;core/src/stream_events_utils.rs;protocol/src/protocol.rs;core/src/config/mod.rs,工具路由、审批、沙箱选择与重试、结果回送、审批决定类型;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,core/src/tasks/mod.rs;core/src/context/turn_aborted.rs,中断流程与被中断标记;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,core/src/session/context_window.rs;core/src/compact.rs;protocol/src/openai_models.rs,上下文窗口、压缩触发与失败处理;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,core/src/responses_retry.rs;async-utils/src/backoff.rs;model-provider-info/src/lib.rs;protocol/src/error.rs,重试、退避、可重试错误与用量限制错误;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,app-server/src/lib.rs;app-server/src/message_processor.rs;app-server/src/request_processors/initialize_processor.rs;app-server/src/in_process.rs;app-server/src/bespoke_event_handling.rs;app-server/src/code_mode_host.rs;app-server-transport/src/transport/mod.rs;tui/src/lib.rs;tui/Cargo.toml;cli/src/main.rs,App Server 的传输、握手、审批映射,TUI 的进程内接入;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,config/src/loader/mod.rs;config/src/config_layer_source.rs;config/src/config_toml.rs;core/src/agents_md.rs,配置分层、受信项目与 AGENTS.md 加载;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,core/src/agent/control/interrupt.rs;core/src/agent/control/completion.rs;core/src/session/input_queue.rs;core/src/thread_manager.rs;app-server-protocol/src/protocol/v2/thread.rs;app-server/README.md;protocol/src/protocol.rs,多 Agent 控制、邮箱投递、fork、revert 与协议类型;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,features/src/lib.rs,特性开关与默认值;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,core/README.md;protocol/README.md;collaboration-mode-templates/templates/plan.md,README 自述与 Plan mode 的提示词模板;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
OpenAI,ext/guardian-reviewer/src/circuit_breaker.rs,自动审核的断路器常量;固定提交
86e17e98c7f9,核验于 2026-10-22。 -
Anthropic,How Claude Code works,Agent Loop、会话、上下文、压缩、Checkpoint;持续更新,核验于 2026-10-22;harness 职责、JSONL 会话与分支边界于 2026-10-30 补核。
-
Anthropic,Best practices,验证、规划、指令文件、权限、会话管理、并行与失败模式;持续更新,核验于 2026-10-22。
-
Anthropic,Manage costs effectively,成本度量、降本手段、长会话成本、Agent teams;持续更新,核验于 2026-10-22。
-
Anthropic,Permission modes,权限模式、默认模式、auto 模式的分类器与回退;持续更新,核验于 2026-10-22。
-
Anthropic,Configure permissions,权限规则的求值顺序与 Hook 的优先关系;持续更新,核验于 2026-10-22。
-
Anthropic,Sandboxing,沙箱的覆盖范围与限制;持续更新,核验于 2026-10-22。
-
Anthropic,Checkpointing,Checkpoint 与 rewind 的范围、限制;持续更新,核验于 2026-10-22;2026-10-30 补核文件跟踪、后续 Bash 改动与回退的边界。
-
Anthropic,Manage sessions;Agent view,会话存储、恢复与后台会话;持续更新,核验于 2026-10-22。
-
Anthropic,Context window,压缩后保留的内容;持续更新,核验于 2026-10-22。
-
Anthropic,How Claude remembers your project,CLAUDE.md 与 auto memory 的加载;持续更新,核验于 2026-10-22。
-
Anthropic,Settings files and precedence;Settings reference,配置优先级、自动压缩与空闲压缩设置;持续更新,核验于 2026-10-22。
-
Anthropic,Hooks reference,SessionStart、Setup、Stop、PreToolUse 与失败语义;持续更新,核验于 2026-10-22。
-
Anthropic,Extend Claude with skills,Skill 的按需加载;持续更新,核验于 2026-10-22。
-
Anthropic,Create custom subagents;Agent teams,子 Agent 的上下文、fork、并发与失败;Agent teams 的限制;持续更新,核验于 2026-10-22。
-
Anthropic,Extend Claude Code,Skill、MCP、Hook、Subagent、Plugin 的职责与选择;持续更新,核验于 2026-10-22。
-
Anthropic,Prompt caching,缓存机制、有效期、失效与保持的动作;持续更新,核验于 2026-10-22。
-
Anthropic,Error reference,错误分类、自动重试、用量与限额;持续更新,核验于 2026-10-22。
-
Anthropic,Model configuration,推理强度与自动压缩窗口;持续更新,核验于 2026-10-22。
-
Anthropic,Interactive mode,消息排队、中断与用量限额后的自动续跑;持续更新,核验于 2026-10-22。
-
Anthropic,Run Claude Code programmatically;CLI reference,非交互模式与命令行参数;持续更新,核验于 2026-10-22。
-
Anthropic,Worktrees,worktree 隔离的强制范围;持续更新,核验于 2026-10-22。
-
Anthropic,Monitoring usage,OpenTelemetry 指标与事件;持续更新,核验于 2026-10-22。
-
Anthropic,Goal,
/goal条件与评估;持续更新,核验于 2026-10-22。 -
Anthropic,MCP,MCP 工具搜索与延迟加载;持续更新,核验于 2026-10-22。
-
Anthropic,Claude Code GitHub repository;LICENSE.md,仓库内容与版权声明;核验于 2026-10-22。
-
Anthropic,Changelog,版本锚点(核验日最新为 2.1.308);持续更新,核验于 2026-10-22。
-
OpenCode,CLI;CLI commands;TUI,共享后台服务、standalone 与 server 连接、命令、steer 与排队;持续更新,核验于 2026-10-22。
-
OpenCode,Config,配置位置与工具输出限制;持续更新,核验于 2026-10-22。
-
OpenCode,Migrate from V1,1.x 与 2.x 的差异与被忽略的配置键;持续更新,核验于 2026-10-22。
-
OpenCode,Providers;Models;Console budgets,Provider 与模型目录、变体、超时、提示缓存、Console 预算;持续更新,核验于 2026-10-22。
-
OpenCode,Instructions,AGENTS.md 的加载与注入;持续更新,核验于 2026-10-22。
-
OpenCode,Agents,Primary Agent、Subagent、内置 Agent、步数上限;持续更新,核验于 2026-10-22。
-
OpenCode,Permissions,权限规则、默认基线与审批;持续更新,核验于 2026-10-22。
-
OpenCode,Policies,只能收紧的策略;持续更新,核验于 2026-10-22。
-
OpenCode,Tools;Skills;Commands;Plugins;MCP servers;Warming,工具、Skill、命令、插件、MCP 与缓存保温;持续更新,核验于 2026-10-22。
-
OpenCode,Snapshots,快照与回退;持续更新,核验于 2026-10-22。
-
OpenCode,Compaction,自动压缩与溢出恢复;持续更新,核验于 2026-10-22。
-
OpenCode,API,Session、事件、权限、回退、差异与压缩接口;持续更新,核验于 2026-10-22。
-
Anomaly Innovations,OpenCode repository,开源项目与 MIT 许可证;核验于 2026-10-22。
-
Anomaly Innovations,packages/cli/src/services/server-connection.ts;packages/cli/src/services/standalone.ts;packages/cli/src/server-process.ts,连接决策、私有服务启动与所有权租约;固定提交
c93aaae37157,核验于 2026-10-22。 -
Anomaly Innovations,packages/core/src/session/execution.ts;packages/core/src/session/execution/restart.ts;packages/server/src/process.ts,中断、写前声明与重启续跑;固定提交
c93aaae37157,核验于 2026-10-22。 -
Anomaly Innovations,packages/core/src/session/runner/llm.ts;packages/core/src/session/runner/step.ts;packages/core/src/session/runner/retry.ts;packages/core/src/model-retry.ts,模型循环、失败分流与重试;固定提交
c93aaae37157,核验于 2026-10-22。 -
Anomaly Innovations,packages/core/src/session/compaction.ts,自动压缩与溢出恢复;固定提交
c93aaae37157,核验于 2026-10-22。 -
Anomaly Innovations,packages/core/src/session/revert.ts;packages/core/src/session.ts;packages/core/src/snapshot.ts,回退与快照;固定提交
c93aaae37157,核验于 2026-10-22。 -
Anomaly Innovations,packages/core/src/permission.ts;packages/core/src/config/plugin/policy.ts,权限求值与 Policies;固定提交
c93aaae37157,核验于 2026-10-22。 -
Anomaly Innovations,packages/core/src/tool.ts;packages/core/src/tool/plugin/subagent.ts;packages/core/src/plugin/agent.ts,工具注册、子 Agent 工具与内置 Agent 权限;固定提交
c93aaae37157,核验于 2026-10-22。 -
Anomaly Innovations,packages/core/src/session/usage.ts;packages/cli/src/commands/handlers/stats.ts;packages/cli/src/run/noninteractive.ts;packages/core/src/config/plugin/instruction.ts,成本计算、用量统计、非交互运行与指令文件;固定提交
c93aaae37157,核验于 2026-10-22。 -
OpenAI,Codex permissions 与 sandboxing,另见 Sandboxing 与 Agent approvals security;滚动官方文档;核验于 2026-10-24。
-
OpenAI,Codex Linux/macOS 沙箱实现,另见同提交 sandboxing/src/seatbelt.rs 与 features/src/lib.rs;核查 create_bwrap_command_args、create_seatbelt_command_args 及特性状态;核验于 2026-10-24。
-
Anthropic / Anomaly Innovations,Claude Code sandboxing,滚动官方文档;核查实现、默认值、网络与 failIfUnavailable;另见 OpenCode Shell 插件 及同提交 shell.ts;核验于 2026-10-24。
-
OpenAI,Codex agent depth configuration,DEFAULT_AGENT_MAX_DEPTH;同文件 L979 明确 V1、V2 条件;核验于 2026-10-24。
-
Anthropic,Claude Code Hooks reference,滚动官方文档;Stop 连续计数和 onFailure 版本条件;核验于 2026-10-24。
-
Anthropic,Claude Code Memory reference,滚动官方文档;启动时与子目录文件上限分别核查;核验于 2026-10-24。
-
Anthropic,Claude Code Subagents reference,滚动官方文档;depth limit 与 concurrent subagent limit,含版本变化和例外;核验于 2026-10-24。
-
OpenAI,Codex security cookbook:Start here 与 Where configuration belongs,滚动官方文档;核查 requirements / defaults 的执行强度、策略文件保护与允许/阻断验收;核验于 2026-10-29。



