# Codex app-server(无 GUI 版,照搬开源核心) 目标:不做桌面 UI、不重写 agent 循环,把 Codex 开源能给到的全部 原样接过来,做成一个可以被任何客户端(pi agent、你自己的 App、 脚本)调用的 app-server。 ## 一句话架构 ``` 你的客户端 / pi agent │ stdio JSON-RPC(或 unix / ws,见 start.sh) ▼ client.py ──► codex app-server(官方二进制,codex-core 原样) │ thread/start · turn/start · 事件流 · 审批请求 ▼ agent 循环 + 沙箱 + 工具 + MCP + skills + 线程持久化 ``` 本仓库自己写的只有宿主层:`client.py`(协议客户端)、 `run_parallel.py`(多 thread 编排)、`start.sh`、`config.example.toml`。 agent 循环、沙箱、工具执行、上下文压缩、线程存储,全部是官方 codex 二进制里的,不是我复刻的简化版。 实测版本:本机 `codex-cli 0.164.0-alpha.9`,协议定义就是从它 导出的,见 `protocol/`。 ## 目录 - `start.sh`:启动官方 app-server,支持 stdio / unix / ws 三种传输。 - `client.py`:零依赖 Python 客户端,演示完整链路。 - `run_parallel.py`:宿主层多 agent 编排(见下文“子代理”)。 - `config.example.toml`:沙箱、审批、MCP、features 的配置示例。 - `protocol/`:`codex app-server generate-json-schema` 原样导出的 协议 schema(v1 + v2),客户端的方法名和字段以它为准。 - `examples/subagent-prompt.txt`:让主 agent 派生子代理的 prompt 模板。 - `examples/jobs.json`:并行审查的 job 示例。 - `work/job-a`、`work/job-b`:并行示例用的两个只读样例目录。 ## 快速开始 ### 1. 先确认登录(这一步不在开源里) 在你自己的机器上先跑一次 `codex login`(ChatGPT 登录), 或配好带 API key 的 model provider。否则会出现下面实测里 的状况:thread 能建、turn 能发,但模型调用返回 401, turn 以 failed 结束——这是登录问题,不是 app-server 问题。 ### 2. 跑一回合 ```bash cd ~/workspace/codex-app-server python3 client.py --prompt "修掉 README 里的错别字" \ --cwd /path/to/your/repo \ --sandbox workspace-write --approval-policy on-request ``` 只看协议、不跑模型: ```bash python3 client.py --list-threads python3 client.py --thread-id <上次的 thread id> --prompt "继续" ``` ### 3. 换传输方式 ```bash ./start.sh --listen unix:///tmp/codex.sock ./start.sh --listen ws://127.0.0.1:8080 # 再用你自己的客户端连这个 socket / ws,而不是 stdio ``` ### 4. 配置 把 `config.example.toml` 复制到 `~/.codex/config.toml` 再改, 不要把登录信息、API key、你自己的 relay 地址写进本仓库。 沙箱三档:`read-only` / `workspace-write` / `danger-full-access`; 审批三档:`untrusted` / `on-request` / `never`。 ## 开源照搬清单(全部来自官方 app-server) | 能力 | 来源 | 说明 | |---|---|---| | Agent 循环 | codex-core | 推理 → 工具 → 结果 → 再推理,直到 turn 完成 | | Thread 生命周期 | app-server | thread/start、resume、fork、archive、delete、list,事件历史持久化 | | Turn 与流式事件 | app-server | turn/start、turn/steer、turn/interrupt、turn/completed,agent 文本/工具事件流式回推 | | 沙箱 | codex-core | macOS Seatbelt、Linux Landlock/bubblewrap;本机 VM 的系统 codex 缺 host 和 bwrap,工具会 fail closed,已用 `bin/` 配对三件套修复,见 TEST-REPORT.md“工具执行修复” | | 审批 | app-server | 命令、改文件、权限、MCP 询问等请求回推给客户端裁决;也可配 `approvalsReviewer=auto_review` 让子代理先审 | | 工具 | codex-core | shell、文件读写、搜索、改代码(apply_patch)、图片等 | | MCP / skills / 插件 | app-server | thread 内按需加载,`features list` 可看本版开关 | | AGENTS.md | codex-core | 全局、仓库根、子目录三层合并,thread 启动时自动读 | | 上下文压缩 | codex-core | 长会话自动 compact,不用客户端自己截断 | | exec-server 拆分 | codex-core | 推理(app-server)与执行(exec-server)可分开部署,见 `codex exec-server --help` | | Goal / 多 agent 开关 | features | 本版 `multi_agent` 已是 stable,`codex features list` 可验证 | ## 子代理:两层,不要混 ### 内置子代理(开源已给) 主 agent 在 prompt 明确要求时派生 worker / explorer 等子代理, 各自独立上下文、并行跑、主 agent 汇总。模板见 `examples/subagent-prompt.txt`,要点是:分工写清楚、要求分开 列结果、最后合并去重、只列有证据的项。 限制(重要):子代理继承父 thread 的沙箱和审批设置, 不是独立权限边界。“只读评审”是指令约束,不是技术隔离。 真要隔离,改代码的 job 必须走下面一层。 ### 宿主编排(开源没给,自己写,已给示例) `run_parallel.py` 就是这一层:宿主并行开多个 thread, 每个 thread 一个独立工作目录(生产上用 git worktree), 最后宿主收集结果再汇总。桌面版“多线程指挥台”的感觉 主要就是这一层,不是 app-server 本身。 ```bash python3 run_parallel.py --jobs examples/jobs.json ``` 示例默认是只读审查(read-only + never),要并行改代码时: 每个 job 换成各自的 worktree、改成 workspace-write, 事后逐个 review diff 再合并,冲突仍需人工处理—— core 里没有自动解决冲突的调度器。 ## 宿主编排(host.py,无 GUI,对齐 ChatGPT / Codex 桌面版宿主) ChatGPT / Codex 桌面版作为宿主,本质不是再写一个 agent,而是 管好这几件:线程、并行、隔离、委派、长任务、评审、自动化、 边界。`host.py` 就是这一层,一个 app-server 进程托管多个 thread, 后台读线程按 threadId 分发 turn/completed,所以多 thread 的 turn 可以真正并发(不是轮流排队)。 | ChatGPT / 桌面版宿主能力 | host.py | 状态 | |---|---|---| | 多 thread 并行 | `host.py parallel --jobs …`,一个 app-server 进程内多 thread 并发 | 已实测 | | 一任务一 git worktree 隔离 | `dispatch` 自动 `git worktree add -b host/`,非 git 仓库退化为独立目录拷贝 | 已实测 | | diff 评审 | `host.py review `:status + diff + untracked | 已实测 | | 评审后合并 / 丢弃 | `merge`:先在 worktree 提交,再 `--no-ff` 合并进主仓,冲突自动 abort;`discard`:删 worktree 和分支 | 已实测(host-b 已合并进主仓) | | 线程续跑 / fork / 归档 | `run`(resume + 新回合)、`fork`、`archive`、`list` | 已实测 | | 宿主级子代理委派 | `delegate`:子 thread(默认 read-only)跑完,结果可 `--report-to-parent` 汇总回主 thread | 已实测 | | 模型内子代理 | prompt 明确要求派生,见 `examples/subagent-prompt.txt`;与宿主委派是两层,别混 | 已实测(模板 + 协议) | | 长任务 / 目标循环 | `goal`:同一 thread 多回合,直到 `--verify` 验收命令通过或到 `--max-rounds` | 已实测(1 轮通过) | | 自动化 / 评审队列 | `automations --add` + `run-due`:到期的任务自动派发,结果进 host-state.json 评审队列 | 已实测 | | 沙箱边界 | read-only thread 的写文件请求被拦下,文件确实没建出来 | 已实测 | | 审批控制 | untrusted + deny 时 agent 明确收到审批拒绝,没拿到命令输出 | 已实测 | | 模型内子代理 | 3 子代理并行检查并汇总,视角是真的不同,不是重复一句话 | 已实测 | | 中断 / 续跑控制 | `turn/interrupt` 后状态为 interrupted;`compact` 需先 resume(host.py 的 compact 命令已包好这一步) | 已实测 | | 合并冲突处理 | 先合的进主仓,后合同一行的冲突 abort,主仓干净,冲突 worktree 留着人工解 | 已实测 | | 自动化幂等 | 未到期的自动化 run-due 时不重复派发 | 已实测 | | 宿主提供 MCP 工具 | 隔离 CODEX_HOME 注册 stdio MCP,agent 能发现并调用;注意 MCP 审批是 elicitation 协议,不是通用 decision 字段,host.py 已分流处理 | 已实测(修过一轮才通) | | 模型切换 | 同一 thread 内换模型续跑,turn 的 model 字段随之改变 | 已实测 | | 运行中改向 | `turn/steer`(expectedTurnId 校验)能把进行中的长任务改成短任务 | 已实测 | | 3 并行 | 3 个 worktree 任务同时跑,全部完成且互不污染 | 已实测 | | 未追踪文件评审 | review 时 diff 为空也要看 untracked 栏 | 已实测 | | 失败不崩宿主 | 不存在的模型导致 task failed 并带上游 404 错误,host 不崩 | 已实测 | | 生产级项目闭环 | 真实仓:并行功能开发 + 测试套件验收 + 评审修复闭环 + 合并链,主仓从 2 个测试涨到 17 个全部通过 | 已实测 | | 目标循环反馈 | 验收失败时把命令和失败输出喂给下一轮,否则 agent 会原地不动 | 已实测(修过一轮才通) | | 中断恢复 | dispatch/delegate/goal/run-due 先落盘再跑 turn,中断后可用 run 续跑 | 已实测(委派超时后修) | | fork 隔离 | fork 建新 worktree + 拷工作区文件(跳过 .git)+ turn 级 sandboxPolicy 覆盖 read-only 重置 | 已实测(修过两轮才通) | | 跨进程 state 竞态 | host-state.json 单文件,多 host.py 进程同时写会丢任务,同一进程内并行安全 | 已修:锁文件 + 保存时合并,实测双进程并发保存不丢 | | 项目配置 | 每次都要传 model/repo/state 等参数 | 已修:host-config.json + init/config,CLI 优先于配置,密钥只走环境变量 | | 定时自动化 | run-due 要手动跑,跑完没日志 | 已修:host-run.log 记录派发/跳过,automations --cron-line 生成 crontab 行,--install-cron 才装机 | | 边界:cwd、沙箱、审批、模型 | dispatch / turn 参数里定,见 `config.example.toml` | 已实测 | | 任务登记 | `host-state.json`:task -> thread/worktree/状态/diff;agent 状态本身仍在 CODEX_HOME,不另存第二份 | 已实测 | **没对齐的部分(诚实说,不是偷工):** - 云端派发、推送通知、Slack / GitHub 触发:那是 ChatGPT 托管服务, 开源里没有。`run-due` 要靠你自己的 cron 定时调,才等于“定时自动化”。 - GUI 的 inline 评论、side-by-side diff、点按钮批准:无 GUI 版 本来就不要这层,评审就是 git diff 文本 + merge/discard 命令。 - Computer Use:另一条管线,不在宿主编排里。 - 并行改同一文件:worktree 能防互相踩,但两个分支都在文件末尾 追加时,合并第二支仍可能冲突,host 会 abort 等人工解冲突, 不会自动猜合并结果——这和桌面版一样,是正确做法,不是 bug。 ## 开源没给到的(别指望照搬) - 模型本身、ChatGPT 登录、计费、Codex Cloud 云端沙箱。 - GitHub / Slack 等渠道的托管派发。 - 桌面版的 diff 评审 UI、多线程看板、通知。 - Computer Use(屏幕操作)是另一条管线,不在 CLI 中间层里; 本版 features 里虽然有 `computer_use` 开关,但那不是 “复制 app-server 就自动得到一个桌面操作 agent”。 ## 安全 - 常驻服务不要用 `danger-full-access`,也不要把 `--dangerously-bypass-approvals-and-sandbox` 写进启动脚本, 那是给外部已经沙箱化的环境准备的。 - 审批策略默认:自动化用 `never` + `workspace-write` 或更严; 交互使用再用 `on-request`,由客户端(人)逐条裁决。 - `client.py --approval auto` 会放行所有审批请求,只用在 你完全信任的仓库和一次性任务里,默认是 deny。 - ws 传输对外暴露时务必配 `--ws-auth`,不要裸奔在公网。 ## 本机实测记录(2026-10-25) 隔离 `CODEX_HOME`(不污染你真实的 `~/.codex`)下跑: ``` python3 client.py --codex-home /tmp/codex-client-test \ --cwd work/job-a --sandbox read-only --approval-policy never \ --prompt "只用一句话回答:2+2等于几?不要调用任何工具。" ``` 结果: - `initialize` 成功,返回 codexHome / platformOs。 - `thread/start` 成功,拿到 thread id,model 为默认模型。 - `turn/start` 成功,收到 `turn/started`、userMessage 事件。 - 模型调用处失败:`401 Unauthorized: Missing bearer token`, 随后 `turn/completed` 状态为 failed。 原因:隔离目录里没有任何登录/provider 密钥,且默认 provider 指向 api.openai.com。这是预期内的登录边界, 不是协议或客户端 bug。在你已登录的机器上同一条链路 会直接出结果,不需要改代码。 完整输出见 `TEST-REPORT.md`。