baa-conductor

git clone 

commit
41a4dc3
parent
fe001a6
author
im_wower
date
2026-03-24 10:58:21 +0800 CST
chore: move desktop bugs and browser-control tasks into coordination/

Move from Desktop to version-controlled coordination/ directory:
- bugs/ -> coordination/bugs/ (BUG-008, BUG-009, BUG-010 + FIX cards)
- baa-conductor-browser-control-tasks/ -> coordination/tasks/browser-control/

Also rename BUG-009 codexd turn status bug to BUG-010 to avoid
collision with BUG-009 conductor-daemon test listener leak.
12 files changed,  +1040, -0
A coordination/bugs/BUG-008-codexd-second-thread-turn-timeout.md
+99, -0
  1@@ -0,0 +1,99 @@
  2+# BUG-008: codexd 第二个 session/thread 发 turn 后 app-server 响应超时,反复 reconnect 直至 failed
  3+
  4+## 现象
  5+
  6+向 codexd 创建第一个 session 并发 turn,成功完成(turn.completed)。
  7+
  8+创建第二个 session(新 threadId)并发 turn,消息被 app-server 接收(item/started + item/completed),但此后等待响应时超时,触发 Reconnecting... N/5,最终 failed。
  9+
 10+第一次 turn 的完整回复已在 events.jsonl 里出现(seq 55,agentMessage 完整),证明链路本身通;第二次 turn 卡在等 app-server 返回内容阶段。
 11+
 12+## 触发路径
 13+
 14+```text
 15+POST /v1/codexd/sessions  -> session A (thread A) 创建成功
 16+POST /v1/codexd/turn      -> turn 成功,Codex 回复,turn.completed
 17+
 18+POST /v1/codexd/sessions  -> session B (thread B) 创建成功
 19+POST /v1/codexd/turn      -> item/started + item/completed(消息已发)
 20+                          -> 等待 ~30s
 21+                          -> Reconnecting... 2/5
 22+                          -> Reconnecting... 3/5  ...
 23+                          -> Reconnecting... 5/5
 24+                          -> lastTurnStatus: failed
 25+```
 26+
 27+## 根因
 28+
 29+当前判断(未完全确认):
 30+
 31+codex app-server 是单进程,内部以 thread 为单位管理会话。第一个 thread idle 后,第二个 thread 发起 turn 时,app-server 内部可能出现:
 32+
 33+- 旧 thread 未完全 idle,新 thread 的响应流被阻塞
 34+- app-server 在处理第二个 thread 时内部状态机卡住,不产出 output token,导致 codexd 侧等待超时
 35+- timeout waiting for child process to exit 说明 codexd 等 app-server 子进程退出时超时,但子进程实际未退出(child.status: running),可能是 codexd 的生命周期管理和 app-server 的 thread 模型存在错配
 36+
 37+仍需验证的点:
 38+- 重启 codexd 后只建一个 session 是否稳定复现成功
 39+- 两个 session 复用同一个 threadId 是否能规避
 40+- app-server stdout 里是否有内容但 codexd 未读到
 41+
 42+## 复现步骤
 43+
 44+```bash
 45+# 1. 创建 session A,发 turn,等 turn.completed
 46+curl -X POST http://127.0.0.1:4319/v1/codexd/sessions \
 47+  -H 'Content-Type: application/json' \
 48+  -d '{"purpose":"duplex"}'
 49+
 50+curl -X POST http://127.0.0.1:4319/v1/codexd/turn \
 51+  -H 'Content-Type: application/json' \
 52+  -d '{"sessionId":"<sessionId_A>","input":"你是谁?"}'
 53+
 54+# 等 turn.completed 后,创建 session B
 55+curl -X POST http://127.0.0.1:4319/v1/codexd/sessions \
 56+  -H 'Content-Type: application/json' \
 57+  -d '{"purpose":"duplex"}'
 58+
 59+curl -X POST http://127.0.0.1:4319/v1/codexd/turn \
 60+  -H 'Content-Type: application/json' \
 61+  -d '{"sessionId":"<sessionId_B>","input":"1+1等于几?"}'
 62+
 63+# 轮询,预期出现 lastTurnStatus: failed + Reconnecting... N/5
 64+curl http://127.0.0.1:4319/v1/codexd/sessions/<sessionId_B>
 65+```
 66+
 67+## 当前影响
 68+
 69+- 单次会话(只建一个 session)可以正常完成,链路通
 70+- 多 session 场景(顺序创建多个 thread)大概率失败
 71+- 影响 codexd 作为 duplex 对话代理的实用性,每次对话理论上都会建新 session/thread
 72+
 73+## 修复建议
 74+
 75+### 方案 A(推荐调查方向):复用已有 thread,不每次新建
 76+
 77+如果 app-server 单进程内多 thread 存在状态竞争,先在 codexd 里改为默认复用同一个 thread,通过 turn 的上下文区分会话,而不是每个 session 建独立 thread。
 78+
 79+### 方案 B:session 结束时显式关闭 thread
 80+
 81+在创建新 session 前,先对旧 session 发 close,确保 app-server 内旧 thread 完全 idle 后再启动新 thread。
 82+
 83+### 方案 C:检查 codexd 读取 app-server stdout 的逻辑
 84+
 85+timeout waiting for child process to exit 可能是 codexd 误认为子进程已退出。检查 app-server-transport.ts 里 onClose 的触发条件,确认是否在第二个 thread 上错误触发了关闭逻辑。
 86+
 87+## 严重程度
 88+
 89+High —— 单次对话能通,但多轮/多 session 场景(即 duplex 核心用法)无法稳定工作。
 90+
 91+## 发现时间
 92+
 93+2026-03-23 by Claude
 94+
 95+## 备注
 96+
 97+- events.jsonl seq 55 完整记录了第一次 turn 的回复,Codex 回复内容:「我是 Codex,一个在你的工作区里直接读代码、改代码、跑命令并帮你解决工程问题的 AI 编程助手。」
 98+- seq 59 turn.completed 出现,第一次链路完全通
 99+- 第二次失败的 error detail: codexErrorInfo.responseStreamDisconnected.httpStatusCode = null,additionalDetails = "timeout waiting for child process to exit"
100+- child 进程(PID 20904)在失败后仍处于 running 状态,不是 crash
A coordination/bugs/BUG-009-conductor-daemon-index-test-leaks-local-listener.md
+123, -0
  1@@ -0,0 +1,123 @@
  2+# BUG-009: conductor-daemon 的 `index.test.js` 会遗留本地 HTTP listener,导致 `node --test` 长时间不退出
  3+
  4+## 现象
  5+
  6+运行:
  7+
  8+```bash
  9+cd /Users/george/code/baa-conductor
 10+node --test apps/conductor-daemon/src/index.test.js
 11+```
 12+
 13+测试进程可能长时间不退出,父进程会一直挂在 `node --test` 上。
 14+
 15+现场检查时可观察到:
 16+
 17+- 子进程仍在监听随机本地端口;本次现场是 `127.0.0.1:61147`
 18+- `curl http://127.0.0.1:61147/healthz` 返回 `ok`
 19+- `curl http://127.0.0.1:61147/v1/system/state` 还能读到测试写入的状态,包含 `requested_by: "integration_test"`
 20+- Node 采样显示主线程在 `libuv` 的 `kevent` 里睡眠,不是忙等
 21+- Node 诊断报告只剩一个被引用的 libuv `tcp` handle:监听 `127.0.0.1:61147`
 22+
 23+这说明不是 Node 自身卡死,而是测试遗留了本地 HTTP server,事件循环一直不空。
 24+
 25+## 触发路径
 26+
 27+```text
 28+node --test apps/conductor-daemon/src/index.test.js
 29+-> 最后一个测试:
 30+   "ConductorRuntime exposes a local Firefox websocket bridge over the local API listener"
 31+-> runtime.start() 启动本地 HTTP listener + Firefox WS bridge
 32+-> 测试流程在清理前异常 / 卡住
 33+-> runtime.stop() 与 rmSync(...) 没执行
 34+-> 本地 listener 残留
 35+-> 因为 test runner 带了 --test-timeout=0,进程可一直挂住
 36+```
 37+
 38+## 根因
 39+
 40+当前判断基本明确:
 41+
 42+- 最后一个测试位于 `/Users/george/code/baa-conductor/apps/conductor-daemon/src/index.test.js:1671`
 43+- 该测试的清理逻辑是顺序写在测试尾部的,不在 `try/finally` 里
 44+- 只要在 `waitForWebSocketOpen`、`queue.next(...)`、`fetch(...)`、`socket.close(...)`、`runtime.stop()` 之前任一处卡住或抛错,清理就不会执行
 45+- 清理没执行时,`ConductorRuntime` 起的本地 HTTP server 会继续监听随机端口
 46+- 现场对 `73386` 发送 `SIGUSR2` 后生成的诊断报告:
 47+  `/Users/george/code/baa-conductor/report.20260323.231806.73386.0.001.json`
 48+  其中唯一仍被 `ref` 住的有效 libuv handle 是监听 `127.0.0.1:61147` 的 `tcp` handle
 49+
 50+仍需补的确认点:
 51+
 52+- 是测试里某个 `await queue.next(...)` 真正超时未返回,还是某次断言失败后直接跳过清理
 53+- 是否只有最后一个 Firefox WS 测试缺 `finally`,还是同文件里其它起 server 的测试也需要统一收口
 54+
 55+## 复现步骤
 56+
 57+```bash
 58+cd /Users/george/code/baa-conductor
 59+
 60+node --test apps/conductor-daemon/src/index.test.js
 61+
 62+# 另开一个终端,找出残留 listener
 63+lsof -nP -iTCP -sTCP:LISTEN | rg 'node'
 64+
 65+# 如果测试挂住,随机端口还能回 healthz
 66+curl -sS http://127.0.0.1:<port>/healthz
 67+
 68+# 打诊断报告,确认 libuv 剩余句柄
 69+kill -USR2 <child_node_pid>
 70+jq '.libuv' /Users/george/code/baa-conductor/report.*.<child_node_pid>.*.json
 71+```
 72+
 73+## 当前影响
 74+
 75+- 影响 `apps/conductor-daemon/src/index.test.js` 的本地测试稳定性
 76+- 会把发起测试的上层 `codex` / shell 会话一起挂住,形成看似“父进程卡死”的现象
 77+- 会在本机留下随机本地端口 listener,干扰后续排障
 78+- 当前证据指向测试/清理路径问题,不指向线上 `conductor-daemon` 主服务逻辑
 79+
 80+## 修复建议
 81+
 82+### 方案 A(推荐)
 83+
 84+把 `/Users/george/code/baa-conductor/apps/conductor-daemon/src/index.test.js:1671` 这个测试改成严格的 `try/finally` 收口:
 85+
 86+- `runtime.start()` 后立即建立清理责任
 87+- `finally` 里无条件执行 `queue.stop()`、`socket.close(...)`、`await runtime.stop()`、`rmSync(...)`
 88+- `socket.close(...)` 最好等待 `close` 事件或至少做好幂等清理
 89+
 90+### 方案 B
 91+
 92+给所有会起本地 HTTP server / WebSocket server 的测试加统一 helper,例如:
 93+
 94+- `withRuntimeFixture(...)`
 95+- helper 内部统一 `start/stop/cleanup`
 96+
 97+这样可以避免单个测试遗漏收尾。
 98+
 99+### 方案 C
100+
101+给相关测试补一个更短的 fail-fast 超时,而不是完全依赖 `--test-timeout=0`。
102+
103+即使清理再漏,测试也不会无限挂住。
104+
105+## 严重程度
106+
107+Medium
108+
109+原因:
110+
111+- 不直接影响线上 `conductor-daemon` 服务
112+- 但会稳定破坏本地测试和 agent 会话,且不加人工干预就可能无限挂住
113+
114+## 发现时间
115+
116+2026-03-23 by Codex
117+
118+## 备注
119+
120+- 现场残留端口是 `127.0.0.1:61147`
121+- 残留状态里可读到:
122+  - `mode: "paused"`
123+  - `requested_by: "integration_test"`
124+- 这与测试 `/Users/george/code/baa-conductor/apps/conductor-daemon/src/index.test.js:1671` 的行为一致
A coordination/bugs/BUG-010-codexd-turn-status-stuck-inprogress.md
+108, -0
  1@@ -0,0 +1,108 @@
  2+# BUG-010: codexd turn 状态停留在 inProgress,未更新为 completed
  3+
  4+## 现象
  5+
  6+向 codexd 提交 turn 后,Codex 实际上已经产出完整回复(agentMessage 在 events.jsonl 中有记录),但 session 的 `lastTurnStatus` 始终停留在 `inProgress`,不更新为 `completed`。
  7+
  8+同时 recentEvents 里出现 `Reconnecting... N/5`,最终 turn 被标记为 `failed` 或一直挂着。
  9+
 10+已确认的 agentMessage(通过 events.jsonl 直接读取):
 11+- "可以,我现在能稳定工作。"
 12+- "1+1等于2。"
 13+- 其他多条完整回复
 14+
 15+说明 Codex 子进程正常生成了答案,问题在 codexd 的状态回传链路上。
 16+
 17+## 触发路径
 18+
 19+```text
 20+POST /v1/codex/turn { sessionId, input }
 21+-> accepted: true,turnId 返回
 22+-> app-server.turn.started 事件出现
 23+-> Codex 产出 agentMessage(item/completed 事件记录到 events.jsonl)
 24+-> Reconnecting... 2/5 开始出现
 25+-> turn 状态卡在 inProgress(未收到 completed 信号)
 26+-> 轮询 GET /v1/codex/sessions/:id 永远看到 lastTurnStatus: inProgress
 27+```
 28+
 29+## 根因
 30+
 31+当前判断(未完全确认):
 32+
 33+codex app-server 通过 stdio 和 codexd 通信。Codex 产出回复后,app-server 需要发送 turn 完成的通知(如 `thread/turn/completed` 或类似事件),codexd 收到后才更新 session 状态。
 34+
 35+`Reconnecting... N/5` + `responseStreamDisconnected` + `timeout waiting for child process to exit` 说明 codexd 在等待 app-server 发送完成信号时,stdio 流断开了或子进程退出了,导致:
 36+
 37+1. codexd 未收到 turn completed 通知
 38+2. session 状态停在 inProgress
 39+3. codexd 尝试重连 app-server,但 turn 的完成状态已经丢失
 40+
 41+agentMessage 能写入 events.jsonl,说明内容已经流过来了,但后续的 turn 完成握手没有成功完成。
 42+
 43+仍需验证:
 44+- app-server 是否发出了 turn/completed 事件
 45+- codexd 的 app-server-transport.ts 是否在 stdio 关闭时提前触发了 onClose
 46+- 重连后 codexd 是否尝试恢复 turn 状态
 47+
 48+## 复现步骤
 49+
 50+```bash
 51+# 1. 创建 session
 52+curl -s -X POST https://conductor.makefile.so/v1/codex/sessions \
 53+  -H 'Content-Type: application/json' \
 54+  -H 'Authorization: Bearer <TOKEN>' \
 55+  -d '{"purpose":"duplex"}'
 56+# 记录 sessionId
 57+
 58+# 2. 发 turn
 59+curl -s -X POST https://conductor.makefile.so/v1/codex/turn \
 60+  -H 'Content-Type: application/json' \
 61+  -H 'Authorization: Bearer <TOKEN>' \
 62+  -d '{"sessionId":"<sessionId>","input":"你好,用一句话介绍自己"}'
 63+
 64+# 3. 持续轮询 session 状态(等 60s+)
 65+curl -s https://conductor.makefile.so/v1/codex/sessions/<sessionId> \
 66+  -H 'Authorization: Bearer <TOKEN>'
 67+# 预期 lastTurnStatus 卡在 inProgress 或变成 failed
 68+
 69+# 4. 直接读 events.jsonl 验证 Codex 实际已回复
 70+grep agentMessage /Users/george/code/baa-conductor/logs/codexd/codexd/events.jsonl \
 71+  | python3 -c "import sys,json; [print(json.loads(l)['detail']['params']['item']['text']) for l in sys.stdin]"
 72+# 预期:能看到完整回复内容,证明 Codex 已经回答了
 73+```
 74+
 75+## 当前影响
 76+
 77+High —— 虽然 Codex 能正常回答,但调用方(conductor / AI caller)无法通过 session 状态 API 得知 turn 已完成,导致:
 78+
 79+- 轮询永远得不到 completed 信号
 80+- 回复内容只能通过绕路读 events.jsonl 获取,无法走正式 API
 81+- conductor 的任务编排无法基于 turn 完成事件触发后续动作
 82+
 83+## 修复建议
 84+
 85+### 方案 A(推荐调查方向):检查 app-server-transport.ts 的 onClose 时机
 86+
 87+确认 stdio 流在 Codex 回复完成后是否提前触发了关闭,导致 codexd 认为连接断开而未处理 turn/completed 事件。
 88+
 89+### 方案 B:在 codexd daemon 里增加 turn 超时完成兜底
 90+
 91+如果检测到 agentMessage 的 item/completed 事件已经收到,但 turn 状态还未更新,超过一定时间后主动将 turn 标记为 completed。
 92+
 93+### 方案 C:在重连逻辑里恢复 turn 状态
 94+
 95+重连成功后,检查是否有已收到完整内容但未完成的 turn,主动将其推进到 completed。
 96+
 97+## 严重程度
 98+
 99+High —— 核心链路(发消息 → 得到完成状态)不通,调用方无法可靠地知道 Codex 什么时候回复完了。
100+
101+## 发现时间
102+
103+2026-03-24 by Claude
104+
105+## 备注
106+
107+- BUG-008 和本 bug 密切相关,BUG-008 描述的是多 session 失败,本 bug 更精确地定位到:即使单 session 也存在 turn 状态不更新的问题
108+- events.jsonl 里已确认的完整 agentMessage:「可以,我现在能稳定工作。」,对应 turn 019d1b95-b947-72c1-b6c2-02ee0a56202e
109+- lastTurnStatus 可能的值:inProgress / completed / failed;问题是 completed 从未出现
A coordination/bugs/BUG-TEMPLATE.md
+98, -0
 1@@ -0,0 +1,98 @@
 2+# BUG-XXX: 简短标题
 3+
 4+## 现象
 5+
 6+清楚描述可观察到的问题现象。
 7+
 8+建议包含:
 9+
10+- 哪个接口 / 页面 / 脚本出问题
11+- 返回了什么
12+- 预期应该是什么
13+- 是否稳定复现
14+
15+## 触发路径
16+
17+写清楚最短触发链路,例如:
18+
19+```text
20+POST /v1/exec
21+-> 某个 shell 命令
22+-> 某个脚本
23+-> 失败 / 超时 / 错误返回
24+```
25+
26+## 根因
27+
28+如果已经确认根因,就直接写。
29+
30+如果还没完全确认,可以写:
31+
32+- 当前判断
33+- 高概率根因
34+- 仍需验证的点
35+
36+## 复现步骤
37+
38+给出别人可以直接照抄的最小复现步骤。
39+
40+示例:
41+
42+```bash
43+curl -s http://100.71.210.78:4317/some/endpoint \
44+  -X POST \
45+  -H 'Content-Type: application/json' \
46+  -d '{"foo":"bar"}'
47+```
48+
49+## 当前影响
50+
51+写清楚影响面:
52+
53+- 是否影响主流程
54+- 是否只影响某类目录 / 某类脚本 / 某个页面
55+- 是完全不可用还是降级可用
56+
57+## 修复建议
58+
59+如果已经有候选方案,按顺序写:
60+
61+### 方案 A(推荐)
62+
63+写最直接、最建议的修法。
64+
65+### 方案 B
66+
67+写备选方案。
68+
69+### 方案 C
70+
71+如果有额外兜底方案再写。
72+
73+## 严重程度
74+
75+从下面选一个,并简单解释:
76+
77+- Low
78+- Medium
79+- High
80+
81+## 发现时间
82+
83+格式建议:
84+
85+`YYYY-MM-DD by <发现者>`
86+
87+例如:
88+
89+`2026-03-22 by Codex`
90+
91+## 备注
92+
93+可选。
94+
95+写一些不适合放在上面结构里的补充信息,例如:
96+
97+- 临时绕过方式
98+- 是否已经在线上观察到
99+- 是否已有相关日志文件
A coordination/bugs/FIX-BUG-008.md
+79, -0
 1@@ -0,0 +1,79 @@
 2+# FIX-BUG-008
 3+
 4+```text
 5+你在仓库 /Users/george/code/baa-conductor 工作。
 6+
 7+必须从当前 main 基线开始:main@02b3d6a。
 8+不要从其他任务分支切出。
 9+如果本地已有旧 worktree/分支,不要复用,重新从 main 建干净 worktree。
10+
11+任务:修复 codexd 在第二个 session/thread 上发 turn 容易超时并反复 reconnect 的问题
12+建议分支:fix/codexd-second-thread-timeout
13+
14+先读:
15+1. /Users/george/Desktop/bugs/BUG-008-codexd-second-thread-turn-timeout.md
16+2. /Users/george/code/baa-conductor/apps/codexd/src/daemon.ts
17+3. /Users/george/code/baa-conductor/apps/codexd/src/app-server-transport.ts
18+4. /Users/george/code/baa-conductor/packages/codex-app-server/src/*
19+5. /Users/george/code/baa-conductor/docs/runtime/codexd.md
20+
21+背景:
22+- 现在第一个 session/thread 的 turn 能成功完成
23+- 第二个新 session/thread 的 turn 容易卡住
24+- 之后会进入 reconnect,最终 failed
25+- 这直接破坏 codexd 作为 duplex 多会话代理的核心能力
26+
27+目标:
28+- 让 codexd 在同一个独立 app-server child 上,顺序处理多个 session/thread 的 turn 时稳定工作
29+- 至少修到:
30+  - 第一轮 session 正常
31+  - 第二轮新 session 也能正常完成 turn
32+  - 不再出现“第二个 thread 稳定失败”的模式
33+
34+要求:
35+- 只在这些范围内工作:
36+  - apps/codexd/**
37+  - packages/codex-app-server/**
38+  - docs/runtime/**
39+- 不要修改 apps/conductor-daemon/**
40+- 不要恢复 codex exec 正式模式
41+- 不要引入新的架构方向
42+
43+建议调查方向:
44+1. codexd 的 app-server client 生命周期
45+- 第二个 thread 开始前,client / transport 是否仍然处于健康状态
46+- 有没有错误把“thread 完成”当成“child/stream 结束”
47+
48+2. transport 关闭逻辑
49+- 检查 app-server-transport.ts
50+- 确认 onClose / disconnect / reconnect 是否在第二个 thread 上被误触发
51+
52+3. thread 模型
53+- 检查 createSession / createTurn 对 threadId 的处理
54+- 确认新 thread 的 turnStart / turnSteer 是否和当前状态机兼容
55+
56+4. 最小修复优先
57+- 优先修“第二个 session/thread turn 失败”
58+- 不要顺手做大重构
59+
60+验收标准:
61+- 增加一个稳定复现并覆盖修复的测试
62+- 至少覆盖:
63+  - 第一个 session turn 完成
64+  - 第二个 session turn 也完成
65+  - 不出现 reconnect/fail
66+- 至少跑:
67+  - npx --yes pnpm -F @baa-conductor/codexd typecheck
68+  - npx --yes pnpm -F @baa-conductor/codexd build
69+  - node --test apps/codexd/src/index.test.js
70+  - 如有必要,加你新增的专项 test
71+  - git diff --check
72+
73+结束时:
74+- 提交并推送该分支
75+- 最终回复里明确说明:
76+  - 根因是什么
77+  - 第二个 thread 为什么之前会失败
78+  - 你改了哪些生命周期/transport/session 逻辑
79+  - 现在如何验证多个 session/thread 已经能顺序工作
80+```
A coordination/bugs/README.md
+23, -0
 1@@ -0,0 +1,23 @@
 2+# bugs
 3+
 4+当前目录只保留:
 5+
 6+- 仍未修复、仍有效的 bug
 7+- 一个通用 `BUG-TEMPLATE.md`
 8+- 对应 bug 的修复任务卡
 9+
10+当前有效 bug:
11+
12+1. `BUG-008` — codexd 第二个 session/thread 发 turn 容易超时(已部分缓解,待正式关闭)
13+2. `BUG-009` — conductor-daemon 测试遗留 HTTP listener 导致进程挂住(无修复卡)
14+3. `BUG-010` — codexd turn 状态卡在 inProgress,与 BUG-008 相关(无修复卡)
15+
16+修复任务:
17+
18+- FIX-BUG-008:`/Users/george/code/baa-conductor/coordination/bugs/FIX-BUG-008.md`
19+
20+## 编号规则
21+
22+- BUG-XXX:bug 报告
23+- FIX-BUG-XXX:对应修复任务卡(给 Codex 执行)
24+- 编号按发现顺序递增,不复用
A coordination/tasks/browser-control/README.md
+86, -0
 1@@ -0,0 +1,86 @@
 2+# baa-conductor 浏览器控制当前任务
 3+
 4+## 最短用法
 5+
 6+以后直接对 Codex 说:
 7+
 8+```text
 9+读 /Users/george/Desktop/baa-conductor-browser-control-tasks/T-BRW002.md,完成任务。
10+```
11+
12+或:
13+
14+```text
15+读 /Users/george/Desktop/baa-conductor-browser-control-tasks/T-BRW003.md,完成任务。
16+```
17+
18+或:
19+
20+```text
21+读 /Users/george/Desktop/baa-conductor-browser-control-tasks/T-BRW004.md,完成任务。
22+```
23+
24+这些任务卡已经包含:
25+
26+- 仓库和基线要求
27+- 分支 / worktree 约束
28+- 修改范围限制
29+- 验收标准
30+- 结束时必须说明的内容
31+
32+## 当前状态
33+
34+- `T-BRW001` 已完成:
35+  - 分支:`feat/browser-ws-command-surface`
36+  - 提交:`667fc6a`
37+  - 状态:已合进 `main` 并已推送
38+- `VERIFY-BROWSER-CONNECTION` 已完成
39+- 目前真正活跃的开发任务:
40+  - `T-BRW002`
41+  - `T-BRW003`
42+- `T-BRW004` 还不能开始,必须等 `T-BRW002`、`T-BRW003` 都合进主线
43+
44+## 当前可执行顺序
45+
46+1. 先做 `T-BRW002`
47+2. 再做 `T-BRW003`
48+   - `T-BRW001` 的 WS command surface 已经在当前 `main`
49+3. 最后在三项都合主线后做 `T-BRW004`
50+
51+当前唯一有效基线:
52+
53+- 仓库:`/Users/george/code/baa-conductor`
54+- 分支:`main`
55+- 提交:`main@667fc6a`
56+
57+目标:
58+
59+- 让 `conductor` 通过本地 Firefox WS bridge 控制浏览器插件
60+- 增加网页控制能力:
61+  - 打开/聚焦标签页
62+  - 请求凭证刷新
63+  - 代理页面 API 请求
64+  - 发起 Claude 网页对话
65+  - 读取当前对话内容
66+- 外部调用方只控制 `conductor` HTTP;不直接控制插件
67+
68+约束:
69+
70+- `Firefox` 插件仍然只跑在 `mini`
71+- 本地双向通讯用 `/ws/firefox`
72+- 远程/CLI/网页 AI 只打 `conductor` HTTP
73+- 先做 `Claude` 页面能力,`ChatGPT/Gemini` 只保留现有监测/凭证逻辑
74+- 不把 `/ws/firefox` 暴露成公网入口
75+
76+推荐顺序:
77+
78+1. `T-BRW001` 已完成并合主线
79+2. 当前先做 `T-BRW002`
80+3. 再做 `T-BRW003`
81+4. 最后跑 `T-BRW004`
82+
83+当前任务:
84+
85+- `T-BRW002` Firefox 插件 Claude HTTP 代理能力
86+- `T-BRW003` conductor 浏览器 HTTP 接口
87+- `T-BRW004` 文档与 e2e smoke 收口
A coordination/tasks/browser-control/T-BRW001.md
+77, -0
 1@@ -0,0 +1,77 @@
 2+你在仓库 `/Users/george/code/baa-conductor` 工作。
 3+
 4+必须从当前 `main` 基线开始:`main@8a3a964`。
 5+不要从其他任务分支切出。
 6+如果本地已有旧 worktree/分支,不要复用,重新从 `main` 建干净 worktree。
 7+
 8+任务:Firefox WS bridge server command surface
 9+建议分支:`feat/browser-ws-command-surface`
10+
11+先读:
12+1. `/Users/george/code/baa-conductor/apps/conductor-daemon/src/firefox-ws.ts`
13+2. `/Users/george/code/baa-conductor/docs/api/firefox-local-ws.md`
14+3. `/Users/george/code/baa-conductor/plugins/baa-firefox/controller.js`
15+4. `/Users/george/code/baa-conductor/docs/firefox/README.md`
16+
17+背景:
18+- 现在 Firefox WS 已支持:
19+  - `state_snapshot`
20+  - `action_request`
21+  - `credentials`
22+  - `api_endpoints`
23+- 插件端其实已经能处理服务端下发的:
24+  - `open_tab`
25+  - `api_request`
26+  - `request_credentials`
27+  - `reload`
28+- 但 `conductor-daemon` 现在还没有正式把这些 server -> client 命令做成可调用面,`api_request` 仍然是 `not_implemented`
29+
30+目标:
31+- 先把 Firefox WS bridge 的服务端“命令面”补齐
32+- 让 `conductor` 具备向插件发消息、等待回包、管理请求生命周期的基础能力
33+- 这一步先做 transport / registry / request-response,不做 Claude 页面业务路由
34+
35+要求:
36+- 只在这些范围内工作:
37+  - `apps/conductor-daemon/**`
38+  - `docs/api/**`
39+  - `docs/firefox/**`
40+- 不要修改 `plugins/baa-firefox/**`
41+- 不要改 `codexd`
42+- 不要做最终 `/v1/browser/*` HTTP 产品面
43+
44+需要完成:
45+1. 在 `firefox-ws.ts` 中增加 server -> client 命令发送基础设施
46+   - 能按 `clientId` 或默认活跃 client 发送命令
47+   - 能跟踪 request id
48+   - 能等待 `api_response`
49+   - 能处理超时、client disconnect、replacement
50+
51+2. 正式支持这些 outbound 命令
52+   - `open_tab`
53+   - `request_credentials`
54+   - `reload`
55+   - `api_request`
56+
57+3. 增加一个最小 bridge 服务层
58+   - 例如 `FirefoxBridgeService` / `FirefoxCommandBroker`
59+   - 不要把复杂逻辑塞满 `firefox-ws.ts`
60+
61+4. 文档更新
62+   - `docs/api/firefox-local-ws.md`
63+   - 写清哪些消息已经正式双向支持
64+
65+验收标准:
66+- 有可测试的 server -> client request/response 基础能力
67+- `api_request` 不再是 `not_implemented`
68+- `node --test apps/conductor-daemon/src/index.test.js` 通过
69+- `pnpm -F @baa-conductor/conductor-daemon typecheck` 通过
70+- `pnpm -F @baa-conductor/conductor-daemon build` 通过
71+- `git diff --check` 通过
72+
73+结束时:
74+- 提交并推送该分支
75+- 在最终回复里明确说明:
76+  - 新增了哪些 WS outbound 命令
77+  - request-response 生命周期怎么做
78+  - 还没做哪些 HTTP 产品接口
A coordination/tasks/browser-control/T-BRW002.md
+104, -0
  1@@ -0,0 +1,104 @@
  2+## 最短提示词
  3+
  4+```text
  5+读 /Users/george/Desktop/baa-conductor-browser-control-tasks/T-BRW002.md,完成任务。
  6+```
  7+
  8+上面这一句就够了;本文件已经包含完整约束、修改范围、验收标准和结束要求。
  9+
 10+你在仓库 `/Users/george/code/baa-conductor` 工作。
 11+
 12+必须从当前 `main` 基线开始:`main@667fc6a`。
 13+不要从其他任务分支切出。
 14+如果本地已有旧 worktree/分支,不要复用,重新从 `main` 建干净 worktree。
 15+
 16+任务:Firefox 插件 Claude HTTP 代理能力
 17+建议分支:`feat/firefox-claude-page-automation`
 18+
 19+先读:
 20+1. `/Users/george/code/baa-conductor/plugins/baa-firefox/controller.js`
 21+2. `/Users/george/code/baa-conductor/plugins/baa-firefox/content-script.js`
 22+3. `/Users/george/code/baa-conductor/plugins/baa-firefox/page-interceptor.js`
 23+4. `/Users/george/code/baa-conductor/docs/firefox/README.md`
 24+5. `/Users/george/code/baa/baa-firefox/README.md`
 25+6. `/Users/george/code/baa/baa-extension/background.js`
 26+7. `/Users/george/code/baa/baa-extension/page-interceptor.js`
 27+
 28+背景:
 29+- 现在插件已有:
 30+  - Claude 页面右下角浮层
 31+  - controller 页
 32+  - request hook / credential / endpoint 监测
 33+- 页面内代理链路其实已经存在:
 34+  - service/controller 能收到 `api_request`
 35+  - content script 能把请求转到页面上下文
 36+  - page interceptor 能在页面里用真实凭证发 `fetch`
 37+- 参考仓库 `baa-firefox` / `baa-extension` 的主线也不是 DOM 自动化,而是:
 38+  - 拦截真实请求
 39+  - 学习凭证和 endpoint
 40+  - 在页面上下文里直接代发 HTTP 请求
 41+
 42+目标:
 43+- 先只做 `Claude` 页面
 44+- 让插件本地具备:
 45+  - 基于已捕获的凭证和 endpoint 发起一轮 Claude HTTP 对话
 46+  - 基于 API 响应 / SSE 读取当前对话内容
 47+  - 给后续服务端暴露最小页面状态
 48+- 这些能力后续会通过 `conductor` 的 WS bridge 被调用
 49+- 本任务主线是 HTTP-first,不是 DOM-first
 50+
 51+要求:
 52+- 只在这些范围内工作:
 53+  - `plugins/baa-firefox/**`
 54+  - `docs/firefox/**`
 55+- 不要改 `apps/conductor-daemon/**`
 56+- 不要做 ChatGPT/Gemini 自动化
 57+- 保持当前中文 UI
 58+- 不要把“解析输入框、点击发送按钮”当主方案
 59+- 只有 API 无法提供的信息,才允许加最小 DOM 兜底
 60+
 61+需要完成:
 62+1. 基于当前代理链路补齐 Claude HTTP 能力
 63+   - 复用已捕获的 `credentials`
 64+   - 复用已发现的 Claude endpoint
 65+   - 能在页面上下文里代发 Claude 请求
 66+   - 能把 HTTP 响应和 SSE 结果整理成可消费结果
 67+
 68+2. 增加或收口插件内部消息协议
 69+   - 例如:
 70+     - `claude_send`
 71+     - `claude_read_conversation`
 72+     - `claude_read_state`
 73+   - 这些消息优先驱动 HTTP 代理,不要求模拟真实 DOM 点击
 74+
 75+3. 结果结构要适合后续服务端消费
 76+   - 当前对话标题(如果可取)
 77+   - 最近消息列表
 78+   - role/content/timestamp(能取多少取多少)
 79+   - 页面 busy 状态
 80+   - 当前 URL
 81+
 82+4. 文档更新
 83+   - `docs/firefox/README.md`
 84+   - 写清当前只支持 Claude
 85+   - 写清当前主线是凭证抓取 + endpoint 发现 + 页面内 HTTP 代理
 86+   - 明确哪些信息来自 API / SSE,哪些是 DOM 兜底
 87+
 88+验收标准:
 89+- 插件端可以通过 runtime message:
 90+  - 发送 Claude prompt
 91+  - 读取当前对话内容
 92+  - 读取当前页面状态
 93+- `node --check plugins/baa-firefox/controller.js`
 94+- `node --check plugins/baa-firefox/background.js`
 95+- `node --check plugins/baa-firefox/content-script.js`
 96+- `node --check plugins/baa-firefox/page-interceptor.js`
 97+- `git diff --check`
 98+
 99+结束时:
100+- 提交并推送该分支
101+- 在最终回复里明确说明:
102+  - Claude 能力是如何基于凭证、endpoint 和页面内 HTTP 代理实现的
103+  - 提供了哪些 runtime message
104+  - 哪些信息来自 API / SSE,哪些信息仍依赖最小 DOM 兜底
105+  - 当前有哪些已知限制
A coordination/tasks/browser-control/T-BRW003.md
+87, -0
 1@@ -0,0 +1,87 @@
 2+## 最短提示词
 3+
 4+```text
 5+读 /Users/george/Desktop/baa-conductor-browser-control-tasks/T-BRW003.md,完成任务。
 6+```
 7+
 8+上面这一句就够了;本文件已经包含完整约束、修改范围、验收标准和结束要求。
 9+
10+你在仓库 `/Users/george/code/baa-conductor` 工作。
11+
12+必须从当前 `main` 基线开始:`main@667fc6a`。
13+不要从其他无关任务分支切出。
14+如果本地已有旧 worktree/分支,不要复用,重新建干净 worktree。
15+
16+任务:conductor 浏览器 HTTP 接口
17+建议分支:`feat/conductor-browser-http-surface`
18+
19+先读:
20+1. `/Users/george/code/baa-conductor/apps/conductor-daemon/src/local-api.ts`
21+2. `/Users/george/code/baa-conductor/apps/conductor-daemon/src/firefox-ws.ts`
22+3. `/Users/george/code/baa-conductor/docs/api/README.md`
23+4. `/Users/george/code/baa-conductor/docs/api/firefox-local-ws.md`
24+5. `/Users/george/code/baa-conductor/docs/firefox/README.md`
25+
26+背景:
27+- 目标是:外部调用方只打 `conductor` HTTP
28+- `conductor` 再通过本地 `/ws/firefox` 控制 Firefox 插件
29+- 当前还没有正式的 `/v1/browser/*` 产品面
30+- 插件侧 Claude 主线应以“页面内 HTTP 代理”能力为主,而不是 DOM 自动化
31+
32+目标:
33+- 增加最小但正式的浏览器 HTTP 接口
34+- 让 CLI / 网页 AI / 其他调用方可以通过 `conductor`:
35+  - 打开 Claude 页面
36+  - 发起 Claude 对话
37+  - 读取当前 Claude 对话内容
38+  - 读取浏览器 bridge 状态
39+
40+要求:
41+- 只在这些范围内工作:
42+  - `apps/conductor-daemon/**`
43+  - `docs/api/**`
44+  - `README.md`
45+- 不要修改 `plugins/baa-firefox/**`
46+- 不要引入公网暴露 `/ws/firefox`
47+- 当前先只做 Claude
48+
49+建议接口:
50+1. `GET /v1/browser`
51+   - bridge 状态摘要
52+   - 当前 client
53+   - Claude 页基本状态(如果能读到)
54+
55+2. `POST /v1/browser/claude/open`
56+   - 打开或聚焦 Claude 标签页
57+
58+3. `POST /v1/browser/claude/send`
59+   - 发送一轮 prompt
60+
61+4. `GET /v1/browser/claude/current`
62+   - 读取当前 Claude 对话内容和页面状态
63+
64+5. 可选:
65+   - `POST /v1/browser/claude/reload`
66+   - `POST /v1/browser/credentials/request`
67+
68+接口原则:
69+- 先本地可用
70+- 返回 JSON
71+- 出错结构统一
72+- 如果当前没有活跃 Firefox client,要返回清晰的 `503` / `409` 风格错误
73+- Claude 动作优先通过本地 WS 转发到插件已有的 HTTP 代理能力
74+
75+验收标准:
76+- `conductor` 有正式 `/v1/browser/*` 接口
77+- 至少具备 open / send / current 这 3 个动作
78+- `pnpm -F @baa-conductor/conductor-daemon typecheck`
79+- `pnpm -F @baa-conductor/conductor-daemon build`
80+- `node --test apps/conductor-daemon/src/index.test.js`
81+- `git diff --check`
82+
83+结束时:
84+- 提交并推送该分支
85+- 在最终回复里明确说明:
86+  - 新增了哪些 `/v1/browser/*` 接口
87+  - 哪些动作通过本地 WS 转发给插件的页面内 HTTP 代理
88+  - 当前只支持哪些平台
A coordination/tasks/browser-control/T-BRW004.md
+75, -0
 1@@ -0,0 +1,75 @@
 2+## 最短提示词
 3+
 4+```text
 5+读 /Users/george/Desktop/baa-conductor-browser-control-tasks/T-BRW004.md,完成任务。
 6+```
 7+
 8+上面这一句就够了;本文件已经包含完整约束、修改范围、验收标准和结束要求。
 9+
10+你在仓库 `/Users/george/code/baa-conductor` 工作。
11+
12+必须从当前 `main` 基线开始。
13+开始前先确认:
14+
15+- `T-BRW002` 已经合进主线
16+- `T-BRW003` 已经合进主线
17+
18+如果这两个条件有任意一个不满足,不要开始本任务。
19+
20+不要从其他任务分支切出。
21+
22+任务:浏览器控制文档与 e2e smoke 收口
23+建议分支:`feat/browser-control-e2e-smoke`
24+
25+先读:
26+1. `/Users/george/code/baa-conductor/docs/api/README.md`
27+2. `/Users/george/code/baa-conductor/docs/firefox/README.md`
28+3. `/Users/george/code/baa-conductor/docs/api/firefox-local-ws.md`
29+4. `/Users/george/code/baa-conductor/plugins/baa-firefox/docs/conductor-control.md`
30+
31+目标:
32+- 在功能分支都合入后,做一轮最小闭环:
33+  - `conductor HTTP`
34+  - `/ws/firefox`
35+  - 插件
36+  - Claude 页面
37+- 并把文档、curl 示例、smoke 脚本收口
38+- Claude 侧闭环以“页面内 HTTP 代理”链路为准,不要求 DOM 自动化冒充用户点击
39+
40+要求:
41+- 只在这些范围内工作:
42+  - `docs/**`
43+  - `scripts/runtime/**`
44+  - `tests/**`
45+- 不要继续大改业务代码
46+- 重点是收口和验证
47+
48+需要完成:
49+1. 增加最小 smoke
50+   - 打开 Claude 页
51+   - 读 bridge 状态
52+   - 发一轮 prompt
53+   - 读取当前对话内容
54+
55+2. 更新文档
56+   - AI/CLI 先读哪个 describe
57+   - 浏览器控制接口怎么调
58+   - 哪些能力只在本地可用
59+
60+3. 给出最小 curl 示例
61+   - `/v1/browser`
62+   - `/v1/browser/claude/open`
63+   - `/v1/browser/claude/send`
64+   - `/v1/browser/claude/current`
65+
66+验收标准:
67+- 至少有一条 smoke 脚本或测试能跑通整个链路
68+- 文档和实际接口一致
69+- `git diff --check`
70+
71+结束时:
72+- 提交并推送该分支
73+- 在最终回复里明确说明:
74+  - smoke 覆盖了哪些步骤
75+  - 文档入口在哪
76+  - 还有哪些平台暂未支持
A coordination/tasks/browser-control/VERIFY-BROWSER-CONNECTION.md
+81, -0
 1@@ -0,0 +1,81 @@
 2+你在仓库 `/Users/george/code/baa-conductor` 工作。
 3+
 4+必须基于当前主线验证,不要新建功能分支,不要修改代码。
 5+当前基线:`main@8a3a964`
 6+
 7+当前状态:此任务已完成,保留为归档验证卡;除非需要重新验证当前 live 环境,否则不要重复执行。
 8+
 9+任务:验证 Firefox 插件与本地 `conductor` / `codexd` 的连接状态,确认当前连接问题到底在服务端、插件端,还是页面端。
10+
11+先读:
12+1. `/Users/george/code/baa-conductor/docs/firefox/README.md`
13+2. `/Users/george/code/baa-conductor/docs/api/firefox-local-ws.md`
14+3. `/Users/george/code/baa-conductor/docs/api/README.md`
15+4. `/Users/george/code/baa-conductor/plugins/baa-firefox/controller.js`
16+5. `/Users/george/code/baa-conductor/plugins/baa-firefox/background.js`
17+
18+目标:
19+- 不改代码,只做一次清晰的连接验证
20+- 给出当前链路每一段是否正常:
21+  - `conductor`
22+  - `status-api`
23+  - `codexd`
24+  - Firefox 本地 HTTP
25+  - Firefox 本地 WS
26+  - `controller.html` 是否只保留一个
27+- 如果有失败,要明确定位在哪一层
28+
29+需要验证的内容:
30+
31+1. 服务端健康
32+- `http://100.71.210.78:4317/healthz`
33+- `http://100.71.210.78:4317/rolez`
34+- `http://100.71.210.78:4318/v1/status`
35+- `http://127.0.0.1:4319/healthz`
36+- `http://127.0.0.1:4319/describe`
37+- `http://100.71.210.78:4317/v1/codex`
38+
39+2. Firefox WS 握手
40+- 验证 `ws://100.71.210.78:4317/ws/firefox`
41+- 至少确认:
42+  - 能连接
43+  - 能收到 `state_snapshot`
44+  - 如果用相同 `clientId` 建第二个连接,旧连接会被替换
45+
46+3. 插件默认配置
47+- 核对插件默认值是否是:
48+  - `Control API = http://100.71.210.78:4317`
49+  - `Local API = http://100.71.210.78:4317`
50+  - `WS = ws://100.71.210.78:4317/ws/firefox`
51+
52+4. 浏览器侧状态
53+- 当前是否只存在一个 `controller.html`
54+- 如果开了多个,确认插件现在是否会自动收敛成一个
55+- 如果 Firefox 已加载插件,确认管理页里:
56+  - 本地 WS 是否显示已连接
57+  - 本地 HTTP 是否显示已连接
58+  - 最近错误是否为空
59+
60+5. 结果判定
61+- 明确给出:
62+  - 服务端 OK / 不 OK
63+  - WS OK / 不 OK
64+  - HTTP 同步 OK / 不 OK
65+  - 插件 UI OK / 不 OK
66+  - 如果不 OK,最可能的根因是什么
67+
68+建议命令:
69+- `curl`
70+- 本地 node/ws 最小握手脚本
71+- 如有必要,可用浏览器/插件状态检查
72+
73+不要做的事:
74+- 不修改代码
75+- 不提交任何变更
76+- 不新建分支
77+
78+最终回复必须包含:
79+1. 你实际跑了哪些验证命令
80+2. 每一层的结果
81+3. 如果有问题,问题在服务端、插件端还是页面端
82+4. 下一步最小修复建议