一句话结论
Claude 不是在模型内部“切换成 Codex”。真实发生的事情是:Claude Code 的插件命令触发一个 Claude 子代理,子代理再通过 Bash 启动本地 Node 脚本 codex-companion.mjs,这个桥接脚本再通过 Codex App Server 的 JSON-RPC 协议 把任务送到 codex app-server 运行时。
01 · 完整调用链
| 阶段 | 发生了什么 | 对应角色 |
|---|---|---|
| 1. Slash 命令 | 用户输入 /codex:rescue ...,命中插件命令定义 | Claude Code 插件 |
| 2. 子代理路由 | 命令规则要求把任务转给 codex:codex-rescue | Claude 子代理系统 |
| 3. 本地命令执行 | 子代理仅执行一条 Bash:node codex-companion.mjs task ... | Bash 工具 |
| 4. 桥接编排 | codex-companion.mjs 解析参数、恢复线程、记录 job、决定前后台 | Node 桥接脚本 |
| 5. 协议请求 | 桥接脚本发 thread/start、turn/start 等 JSON-RPC 请求 | Codex App Server Client |
| 6. 真正执行 | Codex runtime 在自己的 thread / turn 中推理、调用工具、改文件并返回结果 | codex app-server |
02 · Claude 与 Codex 之间到底靠什么通信
别把这件事想成“Claude 把请求直接打到 Codex API”。在这个插件方案里,通信其实分成了 两段:
| 分段 | 实现方式 | 重点理解 |
|---|---|---|
| Claude → 插件脚本 | 插件命令 + 子代理 + Bash 工具调用 | Claude 最终是在本地启动一个 Node 进程,不是内部切换模型 |
| 插件脚本 → Codex runtime | Codex App Server JSON-RPC | 这里才是结构化协议层,请求如 initialize、thread/start、turn/start |
也就是说,Claude 负责调度本地工具,而 Codex App Server 负责 runtime 协议。这两层不要混在一起看。
03 · codex-companion.mjs 到底是什么
它不是 Codex 本体,而是插件侧的桥接器 / 编排器。Claude 把任务交给它,它再把任务交给 Codex runtime。
- 解析
task、review、status、result、cancel等命令。 - 处理
--resume、--fresh、--background、--model、--effort。 - 在当前仓库下记录 job 状态、
threadId、turnId、日志文件。 - 决定是直接启动
codex app-server,还是复用 broker 共享运行时。 - 把 Codex 返回的事件、错误、最终输出整理成 Claude 可展示的文本。
/codex:rescue
-> commands/rescue.md
-> codex:codex-rescue
-> node codex-companion.mjs task ...
-> runAppServerTurn(...)
-> codex app-server JSON-RPC
-> Codex thread / turn 执行
04 · 源码定位表:每个文件在这条链里负责什么
这一节的目标不是把所有文件都看完,而是让你先建立文件职责地图。这样后面继续深挖时,不会在仓库里盲找。
| 文件 | 角色 | 你进去主要看什么 |
|---|---|---|
plugins/codex/commands/rescue.md | Claude 插件命令定义 | Slash 命令如何路由到子代理、何时询问 resume、最终要求子代理只执行什么命令 |
plugins/codex/agents/codex-rescue.md | Claude 子代理 prompt 契约 | 为什么它被设计成“薄转发器”、默认是否带 --write、如何处理 --resume / --fresh |
plugins/codex/scripts/codex-companion.mjs | 插件侧桥接主入口 | 命令解析、task/review/status/result/cancel 分发、前后台 job、resume-candidate 逻辑 |
plugins/codex/scripts/lib/codex.mjs | Codex runtime 协议适配层 | runAppServerTurn()、runAppServerReview()、thread/start、turn/start、事件捕获与结果整理 |
plugins/codex/scripts/lib/app-server.mjs | App Server 客户端 | JSON-RPC message 如何走、何时 spawn("codex", ["app-server"])、何时走 broker |
plugins/codex/scripts/app-server-broker.mjs | 共享运行时 broker | 本地 socket 如何复用同一个 Codex runtime、busy 时怎么拒绝并发请求 |
plugins/codex/scripts/session-lifecycle-hook.mjs | Claude 会话生命周期 hook | SessionStart / SessionEnd 时怎样注入环境变量、清理 broker、清理后台任务 |
plugins/codex/scripts/lib/state.mjs | 仓库级状态与 job 存储 | 为什么 resume 和 status 是“按工作区”工作的、job 文件落在哪里 |
plugins/codex/scripts/lib/tracked-jobs.mjs | 任务跟踪层 | threadId / turnId / logFile / sessionId 如何随着执行过程被记录下来 |
05 · 逐文件讲解顺序:第一次读这个插件建议怎么走
推荐按“从外到内”的顺序读。也就是先看 Claude 这边怎样发起,再看桥接脚本怎样接,再看 Codex runtime 怎样吃进去。
| 顺序 | 先读哪个 | 为什么先读它 |
|---|---|---|
| Step 1 | plugins/codex/commands/rescue.md | 先看用户命令入口,能马上知道 Claude 被要求做什么、不该做什么 |
| Step 2 | plugins/codex/agents/codex-rescue.md | 再看子代理 prompt,理解为什么它几乎不自己思考、只负责转发 |
| Step 3 | plugins/codex/scripts/codex-companion.mjs | 这是整条链真正的总调度器,先建立全局视角最值 |
| Step 4 | plugins/codex/scripts/lib/codex.mjs | 看桥接脚本怎样调用 App Server,以及 thread / turn 是怎么跑起来的 |
| Step 5 | plugins/codex/scripts/lib/app-server.mjs | 看清 JSON-RPC 客户端、stdio 直连和 broker 复用的分叉点 |
| Step 6 | plugins/codex/scripts/app-server-broker.mjs | 如果你关心性能、复用和 socket,这一层最关键 |
| Step 7 | plugins/codex/scripts/session-lifecycle-hook.mjs + lib/state.mjs | 最后补会话环境变量、resume、status、cancel 这些“为什么能记住上次任务”的外围机制 |
读法建议:第一次不要一上来就钻 app-server.mjs。如果你还没先看清 rescue.md 和 codex-companion.mjs,你很容易只看到协议细节,却不知道整条链为什么要这样设计。
06 · 这样传输会不会慢?是不是应该全改成 socket?
通常不是这里慢。真正耗时的往往是 模型推理、工具执行、文件扫描、首次启动 runtime,而不是本地进程间这点通信开销。
| 模式 | 承载通道 | 适合什么 |
|---|---|---|
| 直接模式 | stdio | 单次直连、简单稳定、跨平台 |
| 共享模式 | broker + 本地 socket | 复用一个长期存活的 Codex runtime,减少重复启动成本 |
关键点:不是“未来能不能改成 socket”,而是这个插件方案本来就同时支持 stdio 和 本地 socket broker 两条路径。
07 · 为什么有时会掉到错误的工作目录
如果 Claude 侧转发任务时没有把正确的 cwd 或会话上下文带过去,Codex runtime 看到的工作目录就可能不是项目根,而是插件安装目录、当前 shell 目录,甚至你之前看到的 ~/.local/bin。这种情况通常不是协议层出了问题,而是桥接时的上下文绑定有偏差。
08 · 关键源码锚点
reference/reference_skill/codex-plugin-cc/plugins/codex/commands/rescue.mdreference/reference_skill/codex-plugin-cc/plugins/codex/agents/codex-rescue.mdreference/reference_skill/codex-plugin-cc/plugins/codex/scripts/codex-companion.mjsreference/reference_skill/codex-plugin-cc/plugins/codex/scripts/lib/codex.mjsreference/reference_skill/codex-plugin-cc/plugins/codex/scripts/lib/app-server.mjsreference/reference_skill/codex-plugin-cc/plugins/codex/scripts/app-server-broker.mjsreference/reference_skill/codex-plugin-cc/plugins/codex/scripts/session-lifecycle-hook.mjs
09 · 课程里最该记住的分层
Claude 负责插件命令与子代理调度,codex-companion.mjs 负责桥接与编排,Codex App Server 负责协议与运行时,Codex 模型只是在最底层真正做推理的那一层。把这四层分开看,这个系统就会一下子清楚很多。