OpenAI Codex CLI splash image

Codex 源码里的生产级 Agent Runtime:沙箱、Subagent、Skills 与 App Server

读 OpenAI Codex 开源仓库,看一个 agent runtime 如何把权限、并发协作、能力注册和前端状态同步做成工程系统。

源码出处

基于 OpenAI Codex 开源仓库 整理,阅读版本为 commit 7d47056(2026-05-22)。本文是源码导读与工程拆解,不代表官方设计文档。

Runtime Engineering

生产级 Agent 的重点,不在 prompt,而在 runtime

读 Codex 源码最有价值的地方,是看它如何把“会写代码的模型”变成“能在真实机器上协作、执行、受控、可恢复的系统”。 这篇只看四个硬工程问题:沙箱、Subagent 调度、Skill 注册,以及 app-server API。

Client UI
app-server
Thread / Turn / Item
AgentControl
SandboxManager
SkillsManager

1. 沙箱:先改文件系统视图,再收紧系统调用

Codex 的执行链路不是简单地“跑一个子进程”。在 Linux 上,它组合了三层保护:进程启动前的 hardening、bubblewrap 构造文件系统视图、再用 no_new_privs 和 seccomp 限制危险 syscall。核心入口可以从 core/src/exec.rs 追到 sandboxing/src/manager.rslinux-sandbox

0
process-hardening

进程 main 之前先禁 core dump、ptrace attach,并清理 LD_*/DYLD_* 这类高风险环境变量。

1
SandboxManager

根据平台和权限 profile 选择 macOS seatbelt、Linux seccomp/bubblewrap、Windows restricted token,或不启用沙箱。

2
bwrap filesystem view

默认只读挂载根目录;只把允许写入的 workspace root 重新 bind 成可写;敏感目录可再覆盖成只读或不可读。

3
no_new_privs + seccomp

执行真正命令前再落下 syscall 过滤器,限制 ptrace、process_vm、io_uring、网络 socket 等能力。

为什么要分两步

文件系统隔离回答“能读写哪里”;seccomp 回答“能向内核请求什么”。只做前者挡不住进程注入和网络请求,只做后者又很难表达 workspace 级别的读写策略。

失败怎么识别

exec.rs 会根据退出码、信号和常见错误文案把失败归因到 sandbox denied、timeout、signal 等类型,让上层能给出更清楚的状态。

2. Subagent:不是递归聊天,而是一棵受控 thread tree

Subagent 相关工具在 multi_agents_v2 目录下:spawn_agentsend_messagewait_agentclose_agent。真正的调度核心在 agent/control.rsagent/registry.rs

Main agent root thread
explorer read-only question
worker bounded patch
verifier parallel checks
InterAgentCommunication
status watcher
completion callback
01

spawn:解析 agent type、model、reasoning effort、fork mode,发出 spawn begin/end 事件。

02

registry:检查每个 session 的最大 thread 数,预留 path/nickname,防止同一棵 agent 树里引用冲突。

03

fork or fresh:可以新建空 thread,也可以从父 thread 历史里 fork,fork 时会过滤未完成和不适合复制的 rollout item。

04

wait / closewait_agent 订阅 mailbox 状态,按 deadline 返回;close_agent 会连同 descendants 一起关闭。

3. Skills:能力模块化,关键是发现、过滤、缓存和注入

Skills 的源码在 core-skills/src。 它把能力包装成 SKILL.md,再用不同 scope 的 root 扫描出来:repo、user、system、admin。插件也能贡献 skill roots。

roots repo/.agents/skills $HOME/.agents/skills $CODEX_HOME/skills/.system /etc/codex/skills plugin skill roots
loader 最多扫描 6 层 跳过 dot dirs 解析 YAML frontmatter 读取 agents/openai.yaml
runtime 按 scope 排序 去重 path 按产品限制过滤 显式 mention 时注入全文
---
name: skill-creator
description: Create or update a Codex skill
metadata:
  short-description: Create skills
---

# Instructions
...

一个细节很重要:app-server 的 skills/list 不只是返回名字。协议里会返回每个 cwd 的 skills、errors、enabled 状态和 interface 元数据;本地 skill 文件变化后会发 skills/changed,前端把它当 invalidation signal 再重新 list。

4. App server:前端不是轮询日志,而是订阅状态流

codex app-server 是 Codex 给 VS Code 等富客户端用的接口层。README 明确写了它用 JSON-RPC 2.0 形态通信,传输支持 stdio、 websocket、unix socket;核心状态模型是 ThreadTurnItem

Client
App server
initialize
initialized ack
thread/start
thread/started
turn/start
turn/started
item/started · item/delta · item/completed
turn/completed

连接 gate

ConnectionRpcGate 保证连接关闭后不会再启动新的 handler,但已经拿到 token 的 handler 可以完成,避免半截响应。

请求串行化

RequestSerializationQueues 按 thread、path、process、watch id 等 key 排队;同 key 写操作 FIFO,不同 key 可以并发。

状态同步

服务端 notification 覆盖 thread status、turn started/completed、item started/completed、agent message delta、tool output delta 等事件。

反向请求

服务端也能向客户端发 approval、dynamic tool、auth refresh 这类请求,并在 OutgoingMessageSender 里追踪 callback。

最聪明的读法:让 Agent 读 Agent Runtime

这类仓库适合从“请求进来后去哪了”开始读:前端请求进入 app-server,变成 thread/turn/item;执行命令时进入 exec 和 sandbox; 拆任务时进入 AgentControl 和 Registry;能力扩展时进入 SkillsManager 和 loader。这样读,看到的是工程边界,而不是提示词技巧。

安全边界SandboxManager + platform sandbox
协作边界AgentControl + InterAgentCommunication
能力边界SkillsManager + SKILL.md + plugins
产品边界app-server JSON-RPC + notifications

No comments yet