# 实测记录(2026-10-25,VM,codex-cli 0.164.0-alpha.9) ## 环境 - 本机已有官方 codex:`/opt/hatch-image/bin/codex`,`codex-cli 0.164.0-alpha.9` - 无 cargo/rustc,2 核、可用内存约 0.5GB:从 Rust 源码现编不现实, 因此“照搬”走的是官方二进制自带的 app-server(即开源 core 编译产物), 而不是在本机重新编译一遍源码。协议 schema 同样由该二进制导出。 - `codex doctor`:除“active model provider auth env var is missing”外 其余健康;MCP 有可选配置问题(缺 env var 的 server)。 ## 协议导出 - `codex app-server generate-json-schema --out …` 成功,导出 2.6MB, 本仓库只收了两个合并文件:`protocol/codex_app_server_protocol.schemas.json` 与 `protocol/codex_app_server_protocol.v2.schemas.json`。 - 从 v2 schema 确认的方法:initialize、initialized、model/list、 thread/start、thread/resume、thread/fork、thread/list、thread/archive、 thread/delete、turn/start、turn/steer、turn/interrupt、turn/completed。 - ThreadStartParams:cwd、model、modelProvider、sandbox、approvalPolicy、 approvalsReviewer、baseInstructions、developerInstructions 等。 - TurnStartParams:threadId、input(text)、model、effort、cwd、 parentTurnId、rootTurnId(委派链路用)、sandboxPolicy、approvalPolicy 等。 - features:`multi_agent` 为 stable/true,`multi_agent_v2` 为 stable/false。 ## 客户端链路实测 隔离 CODEX_HOME 跑 client.py(见 README): - initialize:成功。 - thread/start:成功,thread id 如 `01a17394-…`,cwd 正确落到 work/job-a。 - turn/start:成功,收到 turn/started 与 userMessage 事件。 - 模型调用:失败,`401 Unauthorized: Missing bearer token`, 重试 5 次后 turn/completed 状态 failed,耗时约 17 秒。 - 结论:协议、线程、回合、事件流、错误回推全部走通; 唯一缺的是模型登录,这一步在隔离目录里本就没有, 在已登录的机器上不需要改任何代码。 ## 宿主编排实测(2026-10-25,host.py,中转站 gpt-6.1-sol,low 档位) 测试仓:`work/host-repo`,git 初始化,`sample.py` 只有 `hello()`。 隔离 CODEX_HOME(/tmp/codex-host-test,配了中转站 config), 配对二进制 `bin/codex`。 - **并行**:`parallel` 一次派 host-a(加 add2)和 host-b(加 mul2), 单个 app-server 进程内两个 thread 并发,约 70 秒双双 completed, 各自 worktree 里的 `sample.py` 正确,互不污染。 首跑曾因隔离目录缺中转站 config 而 401 到 api.openai.com, 补 config 后重跑通过——这是测试配置问题,不是 host 代码问题。 - **评审**:`review host-a` 给出 `M sample.py` + 完整 diff。 - **委派**:`delegate --parent-task host-a` 子任务只读检查 add2, 结论列了 NaN/inf、字符串拼接、TypeError 等边界,未改文件; `--report-to-parent` 后主 thread 汇总了该结论。 - **fork / 续跑**:`fork host-a -> host-a-v2` 拿到新 thread id; `run host-a` 续跑一回合“一句话说 add2 是干嘛的”成功。 - **目标循环**:`goal goal1` 目标是创建 `result.txt`(内容 goal-ok), 第 1 轮验收 `test -f result.txt` 通过,状态 goal-met。 - **合并**:`merge host-b` 先在 worktree 提交、再 `--no-ff` 合并, 主仓 `sample.py` 已含 mul2,worktree 已清理。 注意:merge 前必须先提交 worktree 改动,否则分支本身还是 旧 HEAD,合不到东西——这个 bug 在实测中发现并已修。 - **自动化**:`automations --add daily-check` + `run-due`, 到期任务自动派发并 completed,进评审队列。 - **状态**:`host-state.json` 当前任务:host-a completed、 host-b merged、host-a-review completed、host-a-v2 forked、 goal1 goal-met、daily-check-* completed。 ## 宿主编排第二批对齐试验(2026-10-25 晚,中转站 gpt-6.1-sol,low 档位) 上一批测的是“能跑起来”,这一批测的是“边界和控制对不对齐”, 全部通过,只有一个协议坑被挖出来并已修成 host.py 的命令: - **沙箱边界**:`--sandbox read-only` 的任务让 agent 建文件, agent 回答“当前工作目录是只读文件系统”,文件确实没建出来。 边界不是摆设,是真拦住了。 - **审批 deny**:`--approval-policy untrusted --approval deny`, agent 要跑 `echo approval-test-123`,宿主 deny 后 agent 明确说 “自动审批拒绝了”,没拿到输出。注意 host.py 里审批回答的 payload 是按实测反推的 `{"decision": "deny"}`,这条路是通的。 - **模型内子代理**:3 个子代理并行检查 sample.py,安全 / 质量 / 测试覆盖三份视角汇总回来,未改任何文件,结论还点名了主仓 已合并的 mul2 和 0% 测试覆盖——子代理不是走形式。 - **中断**:长诗任务跑了 5 秒后发 `turn/interrupt`,turn/completed 状态直接是 `interrupted`,不用等它写完。 - **compact**:直接对旧 thread 调 `thread/compact/start` 会报 `thread not found`,先 `thread/resume` 再 compact 就通了。 这是协议层面的先后顺序,host.py 已加 `compact` 命令, 内部就是 resume + compact,不让用户踩这个坑。 - **合并冲突**:conflict-a(hello-a)和 conflict-b(hello-b)改同一行, 先合 a 成功,主仓变 hello-a;再合 b 时 host 打印“合并冲突, 已 abort”,主仓仍是 hello-a 且干净,b 的 worktree 留着等人工解。 - **自动化幂等**:给 skip-test 设 interval 86400、last_run 为现在, `run-due` 没派发它。同一轮里到期的 daily-check 被派发了, 但那次调用我没传中转站 key,turn 401 失败,已清理掉那个 误建任务和 worktree——这是测试调用失误,不是 host 逻辑问题。 ## 宿主编排第三批对齐试验(2026-10-25 晚,ChatGPT 宿主能力) 这一批测的是 ChatGPT 宿主最有区分度的几件:宿主给工具、 宿主换模型、运行中改向、多任务并行上限、评审的未追踪文件、 以及失败时宿主是否稳得住。 - **宿主提供 MCP 工具(最重要的一条,带一个真修)**: 新写了 `examples/mcp_echo.py`(零依赖 stdio MCP,两个工具), 在隔离 CODEX_HOME 里注册为 `[mcp_servers.echo]`。 工具确实被 agent 发现了(工具名带 mcp__echo__ 前缀),但前两轮 都被审批拦住:第一轮 approval-policy=never 时直接报 “MCP tool call requires approval”;第二轮 untrusted + host 自动 放行时仍报 “user rejected MCP tool call”。 查 rollout 才找到根因:MCP 审批走的不是通用 approval, 而是 `mcpServer/elicitation/request`(mode=form),必须按 MCP elicitation 协议回 `action=accept/decline + content`, 回通用 `{"decision": ...}` 会被当成拒绝。 host.py 已按这个协议分流回答,第三轮 `mcp-test3` 成功: agent 拿到了固定事实和 20+22=42,且未改文件。 结论:宿主给工具这条路是通的,但审批 payload 不能一刀切。 - **模型切换**:同一 thread host-a(原 gpt-6.1-sol)用 `--model gpt-6-sol` 续跑一回合,turn 的 model 字段确实是 gpt-6-sol,回答正确——宿主可以在 thread 内换模型。 - **运行中改向(steer)**:长诗任务跑 5 秒后发 `turn/steer` (expectedTurnId 校验),turn 完成时已按新指令只剩 4 行短诗, 不是等旧任务跑完再开新回合。 - **3 并行**:host-c/div2、host-d/pow2、host-e/mod2 三个 worktree 同时跑,全部 completed,文件各自正确、互不污染。 - **未追踪文件评审**:`review goal1` 时 diff 为空,但 status 和 untracked 栏都列出了 result.txt——评审不能只看 diff, 未追踪文件要单独看,这一点 host.py 的 review 是对的。 - **失败不崩宿主**:用不存在的模型派发,task 状态为 failed, 错误是中转站 404(模型不可用),host.py 没崩、没卡住, 该任务的 worktree 和分支已清理。 - **未完成项**:另有一项仓库级上下文试验,其工具输出未通过 安全检查,本批报告不采用它的细节、不做结论;上面六项 不依赖它,结论独立成立。 ## 生产级试验(2026-10-25 晚,真实项目仓,不是玩具 sample.py) 试验仓 `work/prod-repo`:core.py(add/sub)+ features/ 独立模块 + tests/ 标准库 unittest,验收命令就是 `python3 -m unittest discover -s tests`。 起点 2 个测试,终点主仓 5 个功能、17 个测试全部通过。 - **并行生产**:prod-pow 和 prod-mod 各自建功能文件 + 测试文件, 不碰共享文件,并行完成,各自 worktree 4 个测试通过,互不污染。 - **目标循环(真实测试验收)**:prod-safe-div 1 轮通过,验收是 整个测试套件,不是“文件存在”。 - **评审闭环**:prod-parse 先实现了一个 naive 版本,委派只读评审 找出 5 条边界问题,汇总回主任务后,主任务修复为严格校验 (只接受 str、只接受 ASCII 十进制、空串抛错等),测试从 5 个 涨到 8 个,全部通过。评审任务本身没改任何文件。 - **合并链**:pow、mod、parse、safe-div、clamp 依次 `--no-ff` 合并进主仓,每次合并后主仓测试都跑过,最终 17 个测试 OK。 合并前评审时发现一个缺口并已修:新文件默认 untracked, git diff 里看不到内容,review 现在会把未追踪文本文件内容 也打印出来,否则评审等于没看新文件。 - **自动化**:prod-nightly-tests(只读跑测试)run-due 派发完成, 紧接着再 run-due 一次全部跳过,幂等正确。 - **MCP(生产仓)**:prod-mcp 在生产仓 worktree 里调用宿主 MCP, 拿到固定事实和 7+8=15,未改文件。 - **失败与恢复(生产级真正挖出来的东西)**: 1. 委派评审第一轮因 shell 超时 300 秒被杀,turn_aborted, 且任务登记只在 turn 完成后才落盘,等于任务丢了。 已修:dispatch / delegate / goal / run-due 都先落盘再跑 turn, 中断后可用 run 续跑。 2. fork 换 worktree 后沙箱被服务端重置为 read-only,fork 时传 sandbox 参数不认;同 cwd fork 则保留 workspace-write。 已修:turn 级 sandboxPolicy 会覆盖 thread 级设置,run 时按 任务沙箱传,fork 隔离 worktree + 写操作已验证通过。 3. fork 拷工作区文件时把 .git 文件也拷过去了,新 worktree 的指针指回旧 worktree,git worktree 校验失败。 已修:拷文件时跳过 .git,已坏的 v2 清理、v3 指针已修复。 4. 目标循环第一版只说“验收未通过”,不告诉 agent 错在哪, prod-retry 3 轮原地不动(测试数一直是 1)。 已修:目标循环把验收命令和失败输出喂给下一轮,且验收命令 本身要打印诊断信息(如 test_count=1, need >=3), prod-retry2 第 1 轮失败、第 2 轮补到 3 个测试、目标达成。 - **生产级结论**:本地、无 GUI 的宿主编排,ChatGPT 宿主在 本地能做的那部分,基本对齐了——并行隔离、测试验收、评审门、 委派修复闭环、合并链、自动化、MCP、模型切换、改向/中断、 失败恢复,都在真实项目上跑通了。 没对齐的仍然是云端和界面那部分:云端派发、推送通知、 GitHub/Slack 触发、CI/PR 集成、GUI inline 评论、Computer Use, 以及一个已知局限:host-state.json 是单文件,多 host.py 进程同时写会有 read-modify-write 竞态,同一进程内的并行 是安全的,跨进程并行没有锁,不要两个 host.py 进程同时跑 同一个 state 文件。 ## 生产加固(2026-10-26,state 锁 / 项目配置 / 定时自动化) - **state 锁**:`host-state.json.lock` + fcntl 锁,锁只包住读/写 短临界区,不包整个模型 turn;保存时在锁里重读磁盘,按任务名 覆盖、按自动化名合并,tmp 文件带 pid。 实测:两个进程同时加载、睡 0.5 秒、各自加一个任务再保存, 最终两个任务都在,没有合并逻辑的话后保存的会把先保存的覆盖掉。 - **项目配置**:`host.py init` 生成 `host-config.json`(repo、 model、effort、沙箱、审批、codex_home、state,无密钥), `host.py config` 打印有效配置,优先级 CLI > 配置 > 内置默认; dispatch/goal 不传 --repo 时用配置里的 repo,没配则明确报错。 实测:/tmp 测试配置 init、config、repo 缺省路径都通; 项目里默认没有 host-config.json,旧调用方式不受影响。 - **定时自动化**:run-due 现在写 `host-run.log`(派发和跳过都记), `automations --cron-line` 只打印 crontab 行,不装机; `automations --install-cron` 才真正写 crontab 且幂等。 实测:未到期的自动化 run-due 输出为空,但日志里有一条 skip; cron 行已验证格式正确,本次没有实际装机——装机后模型任务 会自动跑、花中转站额度,所以装机这一步留给用户显式下令。 另注意:VM 会被平台回收,crontab 不一定活过回收,常驻机器 用 crontab,VM 上更稳的是 Muse cron 定时调 run-due。 - **未修 / 已知局限**:锁只能防同一台机器上的多进程竞态, 不能防两台机器同时写同一个共享 state 文件;定时任务本身 仍需外部调度器(crontab 或 Muse cron)触发,host.py 自己 不常驻。 ## 补充:原始协议探针(同一晚,隔离目录) - `model/list` 成功:返回 `gpt-6.1-sol`,推理档位 low/medium/high/ xhigh/max/ultra,`multiAgentVersion` 为 `v2`。 - `thread/list` 在隔离目录下为空,符合预期(不污染真实线程)。 - 附带警告(不影响核心链路):本机缺 bubblewrap 时用内置兜底; Code Mode 因缺 `codex-code-mode-host` 可执行文件而 fail closed, 需该功能时要另装该组件。 ## 工具执行修复(2026-10-25,用户选“先修好工具执行问题”后) 根因是两层缺件,不是中转站问题,rollout 日志里有明文证据: 1. **缺 code-mode host**:系统里的 codex 是裸二进制, `/opt/hatch-image/bin` 是只读挂载,旁边没有 `codex-code-mode-host`,app-server 报 “Code Mode is unavailable … host executable was not found”, 工具 fail closed,agent 称“写入工具不可用”。 修复:从官方 npm 平台包 `@openai/codex@0.164.0-alpha.9-linux-x64` 里取出同版本 `codex-code-mode-host`(73MB),与 codex 放到同一目录。 注意:host 只认真实可执行文件旁边的路径,放 PATH 里没用; 系统目录只读,所以配对目录只能放 workspace,不能直接补系统目录。 2. **缺 bwrap**:配对后工具能启动了,但写文件仍 3 次 instant failed, rollout 里 panic 明文是 “bubblewrap is unavailable: no system bwrap was found on PATH and no bundled codex-resources/bwrap binary was found next to the Codex executable”。 修复:把系统目录里的 `codex-resources/bwrap` 也复制到配对目录。 修复后配对目录:`bin/codex`(293MB)+ `bin/codex-code-mode-host`(74MB)+ `bin/codex-resources/bwrap`(502KB),合计约 366MB。 这是本机 VM 的补丁,Mac 上 npm 正常安装自带这三件套,不需要它; `start.sh` 已改成优先用 `bin/codex`,`client.py` 也支持 `CODEX_BIN` 环境变量指定配对二进制。 修复后实测(中转站,gpt-6.1-sol,effort low,workspace-write): - 回合 10.3 秒完成,状态 completed,不是 failed。 - `commandExecution` 状态 completed,命令是 `printf '%s\n' 'codex-ok' > test-result.txt && cat test-result.txt`。 - 落盘验证:`work/xcm-test/test-result.txt` 内容确为 `codex-ok`。 - 此前失败的两个版本(max 60 秒、配对后无 bwrap 113 秒)均未建文件, 对照说明修的是执行环境,不是 prompt 或模型问题。 ## 未实测 - 真实模型出结果的一回合(缺登录,VM 里不放密钥)。——已过期: 上面中转站实测已覆盖,见“工具执行修复”一节。 - ws / unix 传输的多客户端并发(start.sh 已把参数透传给官方 app-server,传输层本身是官方实现的,未在本机多客户端压测)。 - 审批请求的完整回包字段:各版本间字段有差异,client.py 默认 用 never 绕开审批;on-request + auto/deny 的回包是尽力而为, 生产交互客户端应以本版 protocol schema 为准再对齐一次。