Skip to content

Commit 24b744e

Browse files
committed
docs: anet 支持 codex CLI 完整方案 (深度设计 - codex 0.130 app-server protocol)
通信龙 hands-on 调研 + 整合 SDK马 + 工程马 quick estimates。 关键 finding: codex 0.130 已 ship 完整 JSON-RPC app-server daemon protocol (75 ClientRequest + 63 ServerNotification + 9 ServerRequest methods dumped via codex app-server generate-json-schema). 架构: agent-node spawn codex app-server stdio child + JSON-RPC bridge, push-driven 真 daemon, 跟 claude-code-cli 行为对称. 工作量 ~500-600 行总量, 2-3 天 ship 2.1.8 大 feature. 7 open questions 等 Vincent 拍板 (approval policy whitelist / escalate target / sandbox / thread lifecycle / multi-agent etc). Author-Agent: 通信龙 Helpers: 通信SDK马 (quick estimate + RFC-005/006 author), 通信工程马 (cli.ts implementer estimate) Refs: #14, RFC-005, RFC-002, 6429bc0 codex CLI 研究, PR openai/codex#21424
1 parent 4a6f451 commit 24b744e

1 file changed

Lines changed: 347 additions & 0 deletions

File tree

docs/anet-codex-code-cli-design.md

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
# anet 支持 codex CLI 的完整方案(深度设计)
2+
3+
> **作者**: 通信龙(hands-on 跑 codex CLI 0.130 + 整合通信SDK马 + 通信工程马 quick estimates)
4+
> **日期**: 2026-05-13
5+
> **状态**: 草案 — 决策依据 doc,不是 RFC(RFC-006 由通信SDK马起完整 RFC)
6+
> **触发**: Vincent telegram 4019 "2.1.8 大 feature = codex-cli 成功支持" + 4040+4048 PR #21424 pointer + 4060+4061 "出方案 + 研究深刻一点"
7+
> **关联**: RFC-005 (TUI mode, superseded) / RFC-002 Phase 2 (SDK runtimes Telegram bind)
8+
9+
## TL;DR
10+
11+
🎯 **架构选定**: agent-node 内 spawn `codex app-server --listen stdio` 作 child process,通过 JSON-RPC stdio 跟 codex daemon 双向通信。**Push-driven 真 daemon**,跟 claude-code-cli 行为对称。
12+
13+
📊 **工作量**: agent-node +150-200 行 bridge / cli.ts +30-50 行 (delegate) / Docker E2E +300 行 / 总约 500-600 行。**2-3 天 ship 2.1.8 大 feature**
14+
15+
⚠️ **关键 timing**: codex 0.130 已 ship JSON-RPC app-server protocol(stable,跟 PR #21424 unrelated 不必等)。protocol 已 dump 75 ClientRequest + 63 ServerNotification + 9 ServerRequest method enum 实测。
16+
17+
**架构对称性**: 用户对 anet network 视角看,`codex-code-cli` runtime 等价 `claude-code-cli` runtime — 都能接收 send_task 自动响应。
18+
19+
## 1. 现状 + Gap
20+
21+
### 1.1 anet 现有 4 个 runtime
22+
23+
`agent-network/bin/cli.ts:140`:
24+
25+
```ts
26+
type RuntimeName = "claude-code-cli" | "codex-sdk" | "claude-agent-sdk" | "http-api";
27+
```
28+
29+
| Runtime | 协议 | daemon? | claude/codex |
30+
|---|---|---|---|
31+
| `claude-code-cli` | spawn `claude` 二进制 + channel plugin daemon layer | ✅ yes | claude |
32+
| `claude-agent-sdk` | npm SDK `@anthropic-ai/claude-agent-sdk` (programmatic) | ✅ yes | claude |
33+
| `codex-sdk` | npm SDK `@openai/codex-sdk` | ✅ yes | codex |
34+
| `http-api` | OpenAI-compatible HTTP fetch | ✅ yes | both |
35+
36+
**对称性 gap**: claude 侧有 CLI 二进制 path (claude-code-cli) + SDK path (claude-agent-sdk) 双轨;codex 侧仅有 SDK,**缺 codex CLI 二进制 path**
37+
38+
### 1.2 用户实证需求
39+
40+
Vincent `~/.codex/config.toml` 含半成品配置:
41+
42+
```toml
43+
[mcp_servers.commhub-proxy]
44+
command = "bun"
45+
args = ["/home/vansin/agent-orchestra/proxy/commhub-proxy.ts"]
46+
```
47+
48+
`commhub-proxy.ts` 不存在。用户**真实尝试过**这条路径,但**没产品化**
49+
50+
### 1.3 codex CLI 0.130 protocol stable
51+
52+
```bash
53+
$ codex --version
54+
codex-cli 0.130.0
55+
```
56+
57+
`codex app-server generate-json-schema --out /tmp/codex-schema` dump 实测 39 schema files,含 `ClientRequest.json` / `ServerNotification.json` / `ServerRequest.json` / `JSONRPCMessage.json` etc.
58+
59+
**关键 API**:
60+
61+
- 75 ClientRequest methods (anet → codex daemon 单向 send)
62+
- 63 ServerNotification methods (codex daemon → anet streaming push)
63+
- 9 ServerRequest methods (codex daemon → anet 反向 ask, anet 必须 reply)
64+
- 1 ClientNotification method
65+
66+
## 2. 完整 Protocol Flow
67+
68+
### 2.1 Push 入口 — `turn/start`
69+
70+
```json
71+
{
72+
"jsonrpc": "2.0",
73+
"id": 1,
74+
"method": "turn/start",
75+
"params": {
76+
"threadId": "<uuid>",
77+
"input": [{"type": "text", "text": "<task content from commhub_send_task>"}]
78+
}
79+
}
80+
```
81+
82+
anet wrapper 接到 commhub `new_task` event → 立即 send 这个 RPC → codex daemon 处理 turn
83+
84+
### 2.2 codex daemon Streaming output (ServerNotification)
85+
86+
```
87+
codex daemon → anet wrapper stream notifications:
88+
- turn/started (turn 开始)
89+
- item/started (item 处理开始)
90+
- item/agentMessage/delta (流式输出,chunks)
91+
- item/completed (item 完成)
92+
- turn/completed (turn 结束 — anet 看到这个 = 全部完成)
93+
```
94+
95+
anet wrapper 聚合 `item/agentMessage/delta` → 拿 final output → 通过 commhub MCP tools (codex 内部 call) 自动回复
96+
97+
### 2.3 反向 ask — ServerRequest 9 个 approval/elicitation 流程
98+
99+
codex daemon 处理过程中 push reverse request 给 anet wrapper:
100+
101+
| ServerRequest | 用途 | anet wrapper 应对 |
102+
|---|---|---|
103+
| `applyPatchApproval` | 改文件前 ask | Smart policy: auto-approve safe / escalate dangerous |
104+
| `execCommandApproval` | 执行 shell 命令前 ask | Smart policy: white-list read-only / escalate destructive |
105+
| `item/permissions/requestApproval` | 通用 permission | escalate |
106+
| `item/tool/call` | call MCP tool | anet wrapper 转给 commhub tool call |
107+
| `item/tool/requestUserInput` | 需要用户输入 | escalate 指挥室 via commhub_send_task |
108+
| `mcpServer/elicitation/request` | MCP server 询问 | escalate 或 auto-fill from config |
109+
| `applyPatchApproval/grantRoot` | 文件 root permission | 慎重 escalate |
110+
| `account/chatgptAuthTokens/refresh` | OpenAI auth refresh | passthrough |
111+
| `execCommandApproval` parsedCmd | 命令解析 metadata | base auto-approve 判断 |
112+
113+
### 2.4 完整 sequence diagram
114+
115+
```
116+
[commhub-server]
117+
|
118+
| SSE (new_task event for codex-bot)
119+
v
120+
[agent-node (codex-code-cli runtime)]
121+
|
122+
| JSON-RPC stdio
123+
v
124+
[codex app-server child process]
125+
|
126+
1. Init: {method: "initialize"} → ack
127+
2. Thread: {method: "thread/start", params: {...}} → threadId
128+
3. Push prompt: {method: "turn/start", params: {threadId, input: [task]}}
129+
4. codex inference (LLM call, may use commhub MCP tools)
130+
5. (codex 想 call shell)
131+
<-- {method: "execCommandApproval", params: {command, cwd, ...}} ServerRequest
132+
--> {result: {approved: true}} ServerResponse (anet 决策)
133+
6. Stream: notifications/item/agentMessage/delta (output chunks)
134+
7. Done: notifications/turn/completed
135+
|
136+
v
137+
[agent-node bridge]
138+
- Aggregate final output
139+
- POST commhub reply (via commhub_send_task or commhub_reply MCP tool)
140+
|
141+
v
142+
[commhub]
143+
```
144+
145+
## 3. Implementation Plan
146+
147+
### 3.1 cli.ts 改动 (~30-50 行)
148+
149+
```ts
150+
// L140 RuntimeName enum
151+
type RuntimeName = "claude-code-cli" | "codex-sdk" | "codex-code-cli" | "claude-agent-sdk" | "http-api";
152+
153+
// L142-149 normalizeRuntime
154+
function normalizeRuntime(r: string): RuntimeName {
155+
switch (r) {
156+
case "codex-cli":
157+
case "codex-code-cli":
158+
return "codex-code-cli";
159+
// ... existing branches
160+
}
161+
}
162+
163+
// L596 assertStartCompatibility
164+
if (profile.runtime === "codex-code-cli") {
165+
const v = execFileSync("codex", ["--version"], {encoding: "utf-8"}).trim();
166+
// verify codex >= 0.130.0
167+
}
168+
169+
// L634 checkRuntimeDependency
170+
if (profile.runtime === "codex-code-cli") {
171+
if (!commandExists("codex")) {
172+
warn("Install codex CLI: npm i -g @openai/codex@latest");
173+
}
174+
}
175+
176+
// L1612-1701 launchAgent — delegate 给 agent-node (类比 codex-sdk runtime path)
177+
case "codex-code-cli":
178+
return spawnAgentNode(profile, {runtime: "codex-code-cli"});
179+
```
180+
181+
### 3.2 agent-node 改动 (~150-200 行)
182+
183+
新加 `agent-node/src/runtime/codex-code-cli.ts`:
184+
185+
```ts
186+
// Spawn codex app-server child + JSON-RPC bridge
187+
class CodexCodeCliRuntime {
188+
private child: ChildProcess;
189+
private threadId?: string;
190+
private pendingRequests = new Map<RequestId, Resolver>();
191+
192+
async start(profile: Profile) {
193+
this.child = spawn("codex", ["app-server"], {
194+
stdio: ["pipe", "pipe", "inherit"],
195+
env: {...process.env, COMMHUB_TOKEN: profile.token, ...}
196+
});
197+
198+
// Stream JSON-RPC notifications/responses from child.stdout
199+
this.startNotificationLoop();
200+
201+
// Initialize handshake
202+
await this.rpc("initialize", {protocolVersion: "2024-11-05", ...});
203+
204+
// Start persistent thread for this agent-node session
205+
const r = await this.rpc("thread/start", {/* threadId or auto-gen */});
206+
this.threadId = r.threadId;
207+
}
208+
209+
// commhub SSE handler — new task arrives
210+
async onNewTask(task: TaskEvent) {
211+
// Push prompt to codex
212+
await this.rpc("turn/start", {
213+
threadId: this.threadId,
214+
input: [{type: "text", text: task.task}]
215+
});
216+
// Notifications stream in via startNotificationLoop
217+
}
218+
219+
// Handle codex push notifications + reverse requests
220+
private startNotificationLoop() {
221+
this.child.stdout.on("data", (chunk) => {
222+
for (const msg of parseJsonRpcStream(chunk)) {
223+
if (msg.method === "turn/completed") {
224+
this.flushTaskReply(); // aggregated output → commhub
225+
} else if (msg.method === "execCommandApproval") {
226+
this.handleApproval(msg); // smart policy
227+
} else if (msg.method === "item/agentMessage/delta") {
228+
this.appendOutput(msg.params); // streaming chunks aggregate
229+
}
230+
// ... other 60+ notification types
231+
}
232+
});
233+
}
234+
235+
// Smart approval policy
236+
private handleApproval(req: ServerRequest) {
237+
if (req.method === "execCommandApproval") {
238+
const cmd = req.params.parsedCmd[0]?.cmd;
239+
const safe = ["ls", "cat", "grep", "git status", "git log"].includes(cmd);
240+
if (safe) return this.reply(req.id, {approved: true});
241+
// Escalate dangerous to 指挥室 via commhub_send_task
242+
this.escalate(req).then(approval => this.reply(req.id, approval));
243+
}
244+
// ... 9 server request types
245+
}
246+
}
247+
```
248+
249+
### 3.3 Approval Policy 决策矩阵
250+
251+
| ServerRequest | 安全级别 | 默认策略 | 配置 override |
252+
|---|---|---|---|
253+
| `execCommandApproval` 白名单 (ls/cat/grep/git status/git log) | safe | auto-approve | `profile.flags.codex.autoApproveCmd: ["..."]` |
254+
| `execCommandApproval` 其他 | dangerous | escalate 指挥室 | `profile.flags.codex.autoApproveAll: true` (危, dev only) |
255+
| `applyPatchApproval` 限同 cwd 内 | medium | auto-approve | escalate threshold by 文件数/lines |
256+
| `applyPatchApproval` 涉及 grantRoot | dangerous | escalate | 永不 auto-approve |
257+
| `item/permissions/requestApproval` | dangerous | escalate ||
258+
| `mcpServer/elicitation/request` | depends | escalate 或 auto-fill commhub fields ||
259+
| `item/tool/call` (codex 调 MCP tool) | safe | passthrough (anet 不拦) ||
260+
| `item/tool/requestUserInput` | medium | escalate 指挥室 ||
261+
262+
### 3.4 Docker E2E test L0-L7 (~300 行)
263+
264+
通信测试马 PR #43 scaffold 复用 + L3/L6 改 + 新增 L7:
265+
266+
| Level | Check | 命令 |
267+
|---|---|---|
268+
| L0 | prerequisites | `which codex && which anet` |
269+
| L1 | hub up | `anet hub start` + `curl /health` |
270+
| L2 | node create | `anet node create test-bot --runtime codex-code-cli` + config 写盘 |
271+
| L3 | child spawn verify | `pgrep -f "codex app-server"` (代替 codex no-subcommand) |
272+
| L4 | env injection | `/proc/<pid>/environ``COMMHUB_TOKEN` |
273+
| L5 | JSON-RPC handshake | netcat / fifo 读 child stdin/stdout 验 initialize ack |
274+
| L6 | push verify | `anet commhub_send_task --alias test-bot --task "hello"` → 验 codex 收到 `turn/start` → 流式 output → reply 回 commhub |
275+
| L7 | cross-runtime | 起 codex-code-cli + claude-code-cli 两 node → A `send_task` B → 都能接收 daemon push |
276+
277+
### 3.5 cases doc (通信文档马 follow-up PR)
278+
279+
`docs-site/docs/cases/codex-code-cli-bot.md` ZH+EN — 5 步 user walkthrough:
280+
281+
1. 装 codex CLI: `npm i -g @openai/codex@latest`
282+
2. 升 anet: `npm i -g @sleep2agi/agent-network@latest`
283+
3. 创节点: `anet node create my-codex --runtime codex-code-cli`
284+
4. 启动: `anet node start my-codex`
285+
5. 跟其他 agent 通信: `anet commhub_send_task --alias my-codex --task "task content"`
286+
287+
## 4. 风险评估 + Mitigation
288+
289+
| Risk | Severity | Mitigation |
290+
|---|---|---|
291+
| codex 0.130 protocol stabilize 本周 (PR #22404 #22414 #22386 仍在改) | 🔴 high | Pin `codex@0.130.0` exact + 监控 release notes + integration test runs daily on `latest` |
292+
| Single-client daemon limit (issue #21551 just merged design RFC) | 🟡 medium | anet wrapper 一节点对应一 codex daemon 1:1,用户不能并发 attach |
293+
| Approval flow 决策 false positive (auto-approve 错的命令) | 🔴 high | Conservative whitelist (read-only only by default) + sandbox flags + escalate 指挥室 fallback |
294+
| Token bridge (codex daemon credential vs anet ntok_) | 🟡 medium | Phase 1: 不让 codex daemon 自己 auth (用本地 stdio 不需 token),Phase 2: 如要 ws transport 再加 JWT |
295+
| codex daemon crash / supervision | 🟡 medium | agent-node supervisor watcher: crash 自动 respawn (类比 claude-agent-sdk pattern) |
296+
| 多 agent-node 同机器 stdio port 冲突 | 🟢 low | stdio 不用 port,per-child 隔离 OK |
297+
| API protocol breaking change in 0.131+ | 🟡 medium | Schema codegen + version check, fail-fast if mismatch |
298+
299+
## 5. Timeline (2-3 天 ship 2.1.8 大 feature)
300+
301+
**Day 1** (today):
302+
- ✅ 通信龙 hands-on 调研完成 (本 doc)
303+
- ⏳ 通信SDK马 起 RFC-006 design doc (90-120 min)
304+
- ⏳ 通信工程马 dive `codex-sdk` runtime 现有 agent-node spawn pattern (60 min)
305+
306+
**Day 2**:
307+
- 通信工程马 实施 agent-node `codex-code-cli.ts` runtime adapter (~200 行) — focus JSON-RPC bridge + push handler + approval smart policy
308+
- 通信工程马 cli.ts 加 RuntimeName + setup wizard entry (~50 行)
309+
- 通信SDK马 review code + spawn 协议 sanity check
310+
- 通信测试马 起 PR #43 update + 新加 L6 push verify + L7 cross-runtime
311+
312+
**Day 3**:
313+
- 联合 smoke test 跑通 1 个 commhub_send_task → codex 响应 → reply
314+
- Vincent mac mini 亲测
315+
- Ship preview `2.1.8-preview.N` (含 codex-code-cli runtime)
316+
- Vincent 亲测通过 → 升 latest `2.1.8` stable **大 feature**
317+
318+
## 6. RFC-005 → RFC-006 Migration
319+
320+
- **RFC-005** mark `Superseded by RFC-006` (TUI mode + pull-on-prompt 是 wrong abstraction)
321+
- **RFC-006** codex-code-cli runtime via app-server daemon mode — 完整 push-driven,跟 claude-code-cli 行为对称
322+
- 通信工程马 `~/anet-work/rfc-005-codex-code-cli/` 6 edit worktree git stash 保留 backup (5/6 edit 可复用,仅 launchAgent spawn 段重写)
323+
- 通信SDK马 起 RFC-006 时引用本设计 doc + 之前 6429bc0 codex CLI 研究 doc + PR #21424 + protocol schema dump
324+
325+
## 7. Open Questions (待 Vincent 拍板)
326+
327+
1. **Approval policy whitelist** — 默认 auto-approve 哪些 shell 命令? (我建议 read-only: ls / cat / grep / git status / git log)
328+
2. **escalate target** — 当 codex 询问 dangerous approval 时,escalate 哪个 alias? (建议 `指挥室` = Vincent,但用户的个人 codex node escalate 给"用户自己",可能 escalate via telegram channel)
329+
3. **Sandbox flags default**`--ignore-user-config` + `--sandbox` mode? (建议 sandbox = "workspace-write" 限制 in cwd)
330+
4. **Thread lifecycle** — per agent-node session 一个 thread (长 context) 还是 per task new thread (clean isolation)? (建议 per session,跟 claude-code-cli 一致)
331+
5. **Multi-agent codex daemon** — 单机器多 codex-code-cli node 是否限制? (建议 limit by RAM, document 1 daemon ~200MB)
332+
333+
## 8. 结论
334+
335+
**anet 支持 codex CLI 完全可行** — codex 0.130 已 ship 完整 daemon RPC protocol
336+
**架构方向定了** — agent-node + codex app-server stdio bridge
337+
**工作量 manageable**~500-600 行总量,2-3 天 ship
338+
⚠️ **timing 注意** — Pin codex@0.130 + 监控 protocol stabilize 本周变化
339+
⚠️ **Approval policy 需 Vincent 拍板** — 7 个 open questions 决定后实施
340+
341+
后续动作:
342+
- 通信SDK马 起 RFC-006 design (90-120 min)
343+
- 通信工程马 dive codex-sdk runtime 现有 pattern (60 min)
344+
- Vincent 答 7 个 open questions
345+
- 实施开干
346+
347+
— END —

0 commit comments

Comments
 (0)