baa-conductor

git clone 

commit
d04922a
parent
293d471
author
codex@macbookpro
date
2026-03-27 13:42:38 +0800 CST
docs: sync progress docs and bug status from code review
12 files changed,  +441, -283
A PROGRESS/2026-03-27-current-code-progress.md
+228, -0
  1@@ -0,0 +1,228 @@
  2+# 当前代码进度核对(2026-03-27)
  3+
  4+## 结论摘要
  5+
  6+- 当前“功能代码基线”按已提交代码看是 `main@25be868`,主题是 `restore managed firefox shell tabs on startup`。
  7+- 当前浏览器桥接主线已经完成到“Firefox 本地 WS bridge + Claude 代发 + 结构化 action_result + shell_runtime + 登录态持久化”的阶段。
  8+- 代码和自动化测试都表明:`/describe/business`、`/describe/control`、`GET /v1/browser`、`POST /v1/browser/actions`、`POST /v1/browser/request`、`POST /v1/browser/request/cancel` 已经形成正式主链路。
  9+- 目前不应再把系统描述成“只有 Claude 专用页面路径”;当前是“通用 browser surface 已落地,但正式 relay 仍只有 Claude 接通,其它平台主要停留在空壳页和元数据链路”。
 10+- 当前仍不能写成“全部收尾完成”。剩余未闭项主要是:真实 Firefox 手工 smoke 未完成、`BUG-011` 到 `BUG-014` 仍待修、`BUG-014` 当前代码仍然存在、风控状态仍是进程内内存态。
 11+
 12+## 本次核对依据
 13+
 14+### 代码核对范围
 15+
 16+- `../apps/conductor-daemon/src/local-api.ts`
 17+- `../apps/conductor-daemon/src/firefox-bridge.ts`
 18+- `../apps/conductor-daemon/src/firefox-ws.ts`
 19+- `../apps/conductor-daemon/src/browser-request-policy.ts`
 20+- `../apps/conductor-daemon/src/browser-types.ts`
 21+- `../plugins/baa-firefox/controller.js`
 22+- `../scripts/runtime/verify-mini.sh`
 23+
 24+### 本次实际执行的验证
 25+
 26+- `pnpm -C /Users/george/code/baa-conductor -F @baa-conductor/conductor-daemon build`
 27+  - 结果:通过
 28+- `node --test /Users/george/code/baa-conductor/apps/conductor-daemon/src/index.test.js`
 29+  - 结果:`25/25` 通过
 30+- `node --test /Users/george/code/baa-conductor/tests/browser/browser-control-e2e-smoke.test.mjs`
 31+  - 结果:`3/3` 通过
 32+
 33+## 当前已完成功能
 34+
 35+### 1. 本地 API 和两层 describe 已落地
 36+
 37+- 代码里已经固定保留两层 describe:
 38+  - `/describe/business`
 39+  - `/describe/control`
 40+- 浏览器正式接口已经收口到:
 41+  - `GET /v1/browser`
 42+  - `POST /v1/browser/actions`
 43+  - `POST /v1/browser/request`
 44+  - `POST /v1/browser/request/cancel`
 45+- 证据:
 46+  - `../apps/conductor-daemon/src/local-api.ts:3230-3250`
 47+  - `../apps/conductor-daemon/src/local-api.ts:4700-4875`
 48+  - `../apps/conductor-daemon/src/index.test.js:1734-1795`
 49+  - `../apps/conductor-daemon/src/index.test.js:1840-1955`
 50+
 51+### 2. `GET /v1/browser` 已经是统一读面
 52+
 53+- 当前读面不只是 bridge 在线状态,还包含:
 54+  - `bridge`
 55+  - `current_client`
 56+  - `claude`
 57+  - `records`
 58+  - `policy`
 59+  - `summary`
 60+- `current_client` 已带:
 61+  - `shell_runtime`
 62+  - `last_action_result`
 63+- merged records 已带活跃态和持久化态合并视图。
 64+- 证据:
 65+  - `../apps/conductor-daemon/src/local-api.ts:2747-2825`
 66+  - `../apps/conductor-daemon/src/browser-types.ts:103-150`
 67+  - `../apps/conductor-daemon/src/index.test.js:1840-1858`
 68+  - `../tests/browser/browser-control-e2e-smoke.test.mjs:387-404`
 69+
 70+### 3. 通用 browser request/cancel/SSE 主链路已落地
 71+
 72+- `POST /v1/browser/request` 已支持 buffered JSON 和正式 SSE。
 73+- SSE 事件类型已经明确实现为:
 74+  - `stream_open`
 75+  - `stream_event`
 76+  - `stream_end`
 77+  - `stream_error`
 78+- `POST /v1/browser/request/cancel` 已接通取消链路。
 79+- 证据:
 80+  - `../apps/conductor-daemon/src/local-api.ts:4717-4789`
 81+  - `../apps/conductor-daemon/src/local-api.ts:4792-4875`
 82+  - `../apps/conductor-daemon/src/firefox-bridge.ts:701-739`
 83+  - `../apps/conductor-daemon/src/firefox-bridge.ts:792-917`
 84+  - `../tests/browser/browser-control-e2e-smoke.test.mjs:513-645`
 85+  - `../apps/conductor-daemon/src/index.test.js:1894-1955`
 86+
 87+### 4. 浏览器风控策略已在 `conductor-daemon` 内实现
 88+
 89+- 默认策略已实装,不只是文档约定:
 90+  - 抖动
 91+  - 平台限流
 92+  - 单 `client/platform` 并发
 93+  - 失败退避
 94+  - 熔断
 95+  - stream open/idle timeout
 96+  - stream buffer 上限
 97+- 证据:
 98+  - `../apps/conductor-daemon/src/browser-request-policy.ts:11-145`
 99+  - `../apps/conductor-daemon/src/local-api.ts:2810-2825`
100+
101+### 5. 登录态元数据持久化已经落地,而且不暴露原始凭证
102+
103+- 当前代码会持久化:
104+  - `account`
105+  - `credential_fingerprint`
106+  - endpoint metadata
107+  - freshness / status
108+- 当前代码不会把原始 `cookie` / header 值暴露到 `GET /v1/browser`。
109+- 断连和重启后,持久化记录仍可读。
110+- 证据:
111+  - `../apps/conductor-daemon/src/firefox-ws.ts:1330-1365`
112+  - `../apps/conductor-daemon/src/index.test.js:3434-3560`
113+  - `../tests/browser/browser-control-e2e-smoke.test.mjs:833-977`
114+
115+### 6. 浏览器登录态 aging 已落地
116+
117+- 浏览器登录态会从 `fresh -> stale -> lost` 演进。
118+- 这部分不只在文档里,自动化测试已覆盖。
119+- 证据:
120+  - `../apps/conductor-daemon/src/index.test.js:3575-3650`
121+  - `../tests/browser/browser-control-e2e-smoke.test.mjs:996-1076`
122+
123+### 7. 结构化 `action_result` 和 `shell_runtime` 已接入主线
124+
125+- 插件侧已经回传结构化 `action_result`:
126+  - `accepted`
127+  - `completed`
128+  - `failed`
129+  - `reason`
130+  - `target`
131+  - `result`
132+  - `results`
133+  - `shell_runtime`
134+- `conductor-daemon` 已消费并挂到:
135+  - `current_client.last_action_result`
136+  - `current_client.shell_runtime`
137+  - `records[].live.shell_runtime`
138+- 证据:
139+  - `../plugins/baa-firefox/controller.js:3218-3284`
140+  - `../apps/conductor-daemon/src/firefox-ws.ts:1268-1328`
141+  - `../apps/conductor-daemon/src/local-api.ts:4328-4375`
142+  - `../apps/conductor-daemon/src/index.test.js:1849-1877`
143+  - `../tests/browser/browser-control-e2e-smoke.test.mjs:428-512`
144+
145+### 8. Firefox 插件空壳页模型和启动恢复逻辑已存在
146+
147+- 插件管理动作已支持:
148+  - `plugin_status`
149+  - `ws_reconnect`
150+  - `controller_reload`
151+  - `tab_open`
152+  - `tab_focus`
153+  - `tab_reload`
154+  - `tab_restore`
155+- `desired / actual / drift` 模型已在插件侧维护。
156+- `2026-03-27` 的代码跟进已经补上:
157+  - 启动时自动恢复缺失的 desired shell tabs
158+  - 识别平台根页并纳入受管理 shell 集合
159+- 证据:
160+  - `../plugins/baa-firefox/controller.js:3053-3100`
161+  - `../plugins/baa-firefox/controller.js:3120-3146`
162+  - `../plugins/baa-firefox/controller.js:3366-3455`
163+  - `../plugins/baa-firefox/controller.js:4401-4425`
164+
165+### 9. 自动化和运维入口已具备
166+
167+- 根脚本已经提供:
168+  - `pnpm test`
169+  - `pnpm smoke`
170+  - `pnpm verify:mini`
171+- `verify-mini.sh` 当前是正式 on-node 静态 + 运行态检查入口。
172+- 证据:
173+  - `../package.json:5-13`
174+  - `../scripts/runtime/verify-mini.sh`
175+
176+## 当前未完成 / 待复核
177+
178+### 1. 真实 Firefox 手工 smoke 仍未完成
179+
180+- 代码和任务文档里都没有新增“真实 Firefox.app 上手动关 tab -> tab_restore -> WS 重连 -> 状态恢复”的实测结论。
181+- 当前自动化 smoke 是 bridge / relay / persistence 层面的模拟 E2E,不等于真实 Firefox 桌面手工验收。
182+
183+### 2. `BUG-011` 到 `BUG-014` 仍然是 open backlog
184+
185+- 当前不能写成“bug backlog 已清空”。
186+- 尤其 `BUG-014` 目前从代码可直接确认仍存在:
187+  - `../plugins/baa-firefox/controller.js:3384-3389` 的 `ws_reconnect` 仍通过 `setTimeout` 延后真正重连
188+  - `../plugins/baa-firefox/controller.js:3254-3256` 里 `completed` 默认仍会按 `true` 回传
189+- 这意味着 `ws_reconnect` 的 `action_result.completed` 语义现在仍然偏早。
190+
191+### 3. 正式 browser relay 仍然只有 Claude 接通
192+
193+- `chatgpt` 和 `gemini` 在插件侧已有平台定义、空壳页和元数据路径。
194+- 但从 `local-api.ts` 的正式说明和测试覆盖看,真正走通 request / SSE / legacy wrapper 的还是 Claude。
195+- 因此当前应表述为:
196+  - Claude:正式 relay 已可用
197+  - ChatGPT / Gemini:元数据和 shell runtime 已有,通用 relay 未见同等级落地验证
198+
199+### 4. 风控状态仍是进程内内存态
200+
201+- 默认策略本身已实现。
202+- 但策略运行态计数并未持久化,进程重启后限流 / 退避 / 熔断状态会重置。
203+
204+### 5. 启动自动恢复逻辑目前主要靠代码核对,不是现成自动化用例
205+
206+- 这次 `25be868` 的启动恢复改动已经在 `controller.js` 中存在。
207+- 但仓库里当前没有直接覆盖“浏览器启动后自动后台 `tab_restore`”的独立测试文件。
208+- 因此这部分目前更适合写成:
209+  - “代码已落地”
210+  - “建议继续补专项自动化或真实 Firefox 手工验收”
211+
212+## 建议给 Claude 重点复核的点
213+
214+1. 复核 `../plugins/baa-firefox/controller.js:3090-3100`、`3366-3455`、`4401-4425`
215+   - 目标:确认启动自动恢复和根页 adopt 逻辑确实能把 desired/actual 调和起来
216+2. 复核 `../plugins/baa-firefox/controller.js:3384-3389` 与 `3218-3256`
217+   - 目标:确认 `BUG-014` 的判断是否准确
218+3. 复核 `../apps/conductor-daemon/src/local-api.ts:2747-2825`、`4328-4375`、`4717-4875`
219+   - 目标:确认当前正式 browser 读写面和 action_result/stream/cancel 的外部合同描述是否准确
220+4. 复核 `../apps/conductor-daemon/src/browser-request-policy.ts:117-145`
221+   - 目标:确认默认风控参数已经实装,而不是仅在文档里声明
222+5. 复核 `../apps/conductor-daemon/src/index.test.js` 和 `../tests/browser/browser-control-e2e-smoke.test.mjs`
223+   - 目标:确认本文写的“自动化已验证范围”没有夸大
224+
225+## 适合对外同步的简版结论
226+
227+如果只写一段给外部协作者看,可以用下面这版:
228+
229+> 当前代码已经完成单节点 `mini` 主接口收口,以及 Firefox 本地 bridge 下的 Claude browser relay 主链路。`GET /v1/browser`、`POST /v1/browser/actions`、`POST /v1/browser/request`、`POST /v1/browser/request/cancel`、正式 SSE、结构化 `action_result`、`shell_runtime`、登录态元数据持久化都已落地,并已通过 `conductor-daemon build`、`index.test.js`(25/25)和 browser-control e2e smoke(3/3)。当前剩余缺口主要是:真实 Firefox 手工 smoke 未完成、`BUG-011`~`BUG-014` 仍待修、正式 relay 仍只有 Claude 接通、以及风控运行态仍是进程内内存态。
A PROGRESS/README.md
+13, -0
 1@@ -0,0 +1,13 @@
 2+# PROGRESS
 3+
 4+这个目录用于沉淀“按当前代码实际状态核对后的进度文档”,方便继续交给 Claude 或人工复核。
 5+
 6+当前最新报告:
 7+
 8+- [`2026-03-27-current-code-progress.md`](./2026-03-27-current-code-progress.md)
 9+
10+阅读建议:
11+
12+1. 先读最新报告里的“结论摘要”和“当前未完成 / 待复核”
13+2. 再按报告里的代码路径和测试文件逐项抽查
14+3. 如果后续代码继续推进,新增同日期报告,保留旧报告做时间点对照
M bugs/BUG-015-sse-stream-open-timeout.md
+35, -61
 1@@ -1,63 +1,37 @@
 2 # BUG-015: SSE 流式模式 stream_open_timeout
 3 
 4-## 现象
 5-
 6-通过 `POST /v1/browser/request` 以 `responseMode: "sse"` 向 Claude completion 端点发请求时,conductor 返回 `stream_open_timeout`:
 7-
 8-```json
 9-{
10-  "error": "stream_open_timeout",
11-  "message": "Browser stream did not open within 10000ms.",
12-  "partial": {"buffered_bytes": 0, "event_count": 0, "last_seq": 0, "opened": false}
13-}
14-```
15-
16-同样的请求用 `responseMode: "buffered"` 能正常拿到 200 响应和完整 SSE 文本。
17-
18-## 触发路径
19-
20-```
21-conductor → firefox-bridge sendApiRequest(responseMode=sse)
22-→ WS → Firefox 插件
23-→ 插件用浏览器 fetch() 发起请求
24-→ 插件应该回传 stream_open / stream_event / stream_end
25-→ 但 conductor 侧 10 秒内没收到 stream_open
26-→ FirefoxBridgeApiStreamSession openTimer 触发 → stream_open_timeout
27-```
28-
29-## 根因分析
30-
31-最可能的原因是 Firefox 插件的 controller.js 中,SSE 拦截逻辑只在 buffered 模式下工作(直接返回完整 response),没有实现 stream_open / stream_event / stream_end 的逐事件回传给 conductor WS。
32-
33-需要排查:
34-1. controller.js 中是否有处理 `responseMode: "sse"` 的分支
35-2. 是否有发送 `stream_open`、`stream_event`、`stream_end` WS 消息的代码路径
36-3. 如果没有,需要在插件侧实现 SSE 流拦截:用 ReadableStream reader 逐行解析 SSE,每收到一个 `data:` 行就回传一个 `stream_event`
37-
38-## 复现
39-
40-```bash
41-# 先创建对话拿到 conv uuid,然后:
42-curl -s https://conductor.makefile.so/v1/browser/request \
43-  -X POST -H "Content-Type: application/json" \
44-  -H "Authorization: Bearer $TOKEN" \
45-  -d '{
46-    "platform": "claude",
47-    "path": "/api/organizations/{org}/chat_conversations/{conv}/completion",
48-    "method": "POST",
49-    "responseMode": "sse",
50-    "body": {"prompt": "hello", "model": "claude-sonnet-4-6", "timezone": "Asia/Shanghai", "attachments": [], "files": [], "rendering_mode": "raw"}
51-  }'
52-# 返回 stream_error: stream_open_timeout
53-# 改为 "responseMode": "buffered" 则正常返回 200
54-```
55-
56-## 严重度
57-
58-High — SSE 是需求文档(FIREFOX_BRIDGE_CONTROL_REQUIREMENTS)定义的首批正式能力
59-
60-## 影响
61-
62-- 所有流式 API 调用(Claude completion、ChatGPT conversation)只能用 buffered 模式
63-- buffered 模式需要等完整响应返回才能读取,延迟高且无法做流式 UI
64-- conductor 侧的 stream session(seq tracking、buffer overflow、idle timeout)全套逻辑已实现但无法使用
65+## 状态
66+
67+- `已关闭(2026-03-27 代码核对)`
68+
69+## 当前代码核对结论
70+
71+原报告里的核心判断是“Firefox 插件还没有实现正式 SSE 流式回传”。这个结论与当前代码不一致。
72+
73+当前代码已经具备完整的 SSE 主链路:
74+
75+- `plugins/baa-firefox/controller.js`
76+  - 收到 `api_request` 时,已经按 `response_mode === "sse"` 走单独分支
77+  - `handlePageSse(...)` 已经把页面侧事件转成 `stream_open` / `stream_event` / `stream_end` / `stream_error`
78+- `plugins/baa-firefox/page-interceptor.js`
79+  - 已实现 `streamProxyResponse(...)`
80+  - 会用 `ReadableStream.getReader()` 逐块读取响应体
81+  - 会在页面内发出 open / chunk / done / error 事件
82+- 自动化验证已覆盖这条链路:
83+  - `tests/browser/browser-control-e2e-smoke.test.mjs`
84+  - `apps/conductor-daemon/src/index.test.js`
85+
86+## 为什么关闭
87+
88+- 这张卡的根因描述是“插件侧缺少 SSE 实现”
89+- 按当前代码核对,这个实现已经存在,且有自动化覆盖
90+- 因此这张卡不再适合作为 open bug 保留
91+
92+## 保留说明
93+
94+- 如果线上环境仍然出现 `stream_open_timeout`
95+- 应当新开一张 bug,按新的复现条件排查:
96+  - 实际请求路径
97+  - 页面注入是否成功
98+  - 是否命中了特定平台 / 特定响应格式 / 特定页面环境
99+- 不再沿用本卡“插件尚未实现 SSE”的旧根因
M bugs/BUG-016-custom-headers-not-forwarded.md
+23, -42
 1@@ -1,53 +1,34 @@
 2 # BUG-016: browser/request 的自定义 headers 未透传到上游
 3 
 4-## 现象
 5+## 状态
 6 
 7-通过 `POST /v1/browser/request` 发送带 `headers` 字段的请求时,自定义 header 没有被附加到插件侧的 fetch 请求中。
 8+- `已关闭(2026-03-27 代码核对)`
 9 
10-具体场景:ChatGPT 的 `/backend-api/conversation` 端点需要 `openai-sentinel-chat-requirements-token` header,但插件侧的 fetch 只携带了浏览器自动附加的 cookie 和标准 header,缺少 sentinel token,导致 ChatGPT 返回 422。
11+## 当前代码核对结论
12 
13-## 触发路径
14+原报告里的核心判断是“自定义 headers 没有进入插件侧真实 fetch”。这个结论与当前代码不一致。
15 
16-```
17-conductor POST /v1/browser/request {
18-  platform: "chatgpt",
19-  path: "/backend-api/conversation",
20-  headers: {"openai-sentinel-chat-requirements-token": "gAAAA..."},
21-  body: {action: "next", messages: [...]}
22-}
23-→ conductor 将 headers 字段放入 WS 消息
24-→ Firefox 插件收到 api_request
25-→ 插件用 fetch() 发请求,但没有从 WS 消息中读取 headers 并附加
26-→ ChatGPT 返回 422: Input should be a valid dictionary
27-```
28+当前链路已经具备 header 透传:
29 
30-## 根因分析
31+- `apps/conductor-daemon/src/firefox-bridge.ts`
32+  - `apiRequest(...)` / `streamRequest(...)` 已把 `headers` 放进 `api_request`
33+- `apps/conductor-daemon/src/index.test.js`
34+  - 已断言 bridge 发出的 `api_request` 消息里包含传入的 `headers`
35+- `plugins/baa-firefox/page-interceptor.js`
36+  - 在 `__baa_proxy_request__` 处理里,会把 `detail.headers` 合并进页面内 `fetch(...)`
37+  - 同时会过滤 `cookie`、`host`、`origin`、`referer`、`sec-*` 等 forbidden headers
38 
39-需要排查 controller.js 中处理 `api_request` 的代码:
40-1. 是否从 WS 消息中读取了 `message.headers` 字段
41-2. 是否在 `fetch(url, { headers: ... })` 中合并了自定义 headers
42-3. 如果只透传了 cookie(通过 `credentials: "include"`),自定义 header 会丢失
43+## 为什么关闭
44 
45-## 修复方向
46+- 这张卡描述的是“headers 完全未透传”
47+- 按当前代码核对,这个描述已经不成立
48+- 因此不再作为 open bug 保留
49 
50-在插件侧的 api_request 处理中,从 WS 消息读取 `message.headers`(一个 key-value 对象),合并到 fetch 的 headers 中:
51+## 保留说明
52 
53-```javascript
54-const customHeaders = message.headers || {};
55-const fetchHeaders = {
56-  "Content-Type": "application/json",
57-  ...customHeaders
58-};
59-const response = await fetch(url, { method, headers: fetchHeaders, body, credentials: "include" });
60-```
61-
62-注意安全:不应允许覆盖 `Cookie`、`Host`、`Origin` 等浏览器管控的 header。可以维护一个黑名单过滤。
63-
64-## 严重度
65-
66-Medium — 影响 ChatGPT 对话发送和其他需要自定义 header 的 API 调用
67-
68-## 影响
69-
70-- ChatGPT `/backend-api/conversation` 无法使用(需要 sentinel token header)
71-- 任何需要非标准 header 的 API 调用都无法通过 browser/request 完成
72+- 如果特定 header 仍在线上环境失效
73+- 更可能是:
74+  - 命中了 forbidden header 过滤
75+  - header 名被上游要求大小写或格式约束
76+  - 某个平台对页面内 fetch header 有额外限制
77+- 这类情况应按具体 header / 平台 / 请求路径新开 bug,不再沿用“完全未透传”这一版描述
M bugs/BUG-017-buffered-sse-raw-text.md
+24, -5
 1@@ -1,5 +1,9 @@
 2 # BUG-017: buffered 模式对 SSE 端点返回原始文本而非解析后的 JSON
 3 
 4+## 状态
 5+
 6+- `待修复(2026-03-27 代码核对确认)`
 7+
 8 ## 现象
 9 
10 通过 `POST /v1/browser/request` 以 `responseMode: "buffered"` 请求 Claude completion 端点时,`response` 字段返回的是原始 SSE 文本字符串,而不是解析后的 JSON 对象:
11@@ -10,9 +14,24 @@
12 
13 调用方需要自己解析 SSE 格式文本来提取 completion 内容,增加了使用复杂度。
14 
15-## 根因
16+## 当前代码核对结论
17+
18+这张 bug 当前仍然成立,而且根因可以从现有代码直接看出来:
19+
20+- `plugins/baa-firefox/page-interceptor.js`
21+  - buffered 路径会直接 `await response.text()`
22+  - 然后把 `body: responseBody` 原样发回 `__baa_proxy_response__`
23+- `plugins/baa-firefox/controller.js`
24+  - `handlePageProxyResponse(...)` 会把这个 `body` 继续作为普通 buffered body 回传
25+- `apps/conductor-daemon/src/local-api.ts`
26+  - `parseBrowserProxyBody(...)` 只会尝试 `JSON.parse(...)`
27+  - SSE 文本不是合法 JSON,因此最终仍会以原始字符串返回
28+
29+补充说明:
30 
31-插件侧对 buffered 请求,不管上游 Content-Type 是 `application/json` 还是 `text/event-stream`,都用 `response.text()` 读取并原样返回。对于 JSON 端点(如 `/api/organizations`)这没问题,但对于 SSE 端点(如 completion),返回的是 SSE 格式的文本。
32+- 当前 legacy Claude helper `sendClaudePrompt(...)` 会单独调用 `parseClaudeSseText(...)`
33+- 但 generic `POST /v1/browser/request` 的 buffered 路径不会做这层解析
34+- 所以这张 bug 的影响范围是“generic browser/request buffered + SSE 端点”,不是所有 Claude 相关接口都受影响
35 
36 ## 严重度
37 
38@@ -22,9 +41,9 @@ Low — 功能可用(调用方自行解析),但体验不好
39 
40 有两种方案:
41 
42-### 方案 A(推荐):插件侧检测 Content-Type 并解析
43+### 方案 A(推荐):在代理 buffered 路径解析 SSE 文本
44 
45-当 buffered 响应的 Content-Type 是 `text/event-stream` 时,插件侧解析 SSE 文本,提取所有 `data:` 行,返回结构化结果:
46+当 buffered 响应的 Content-Type 是 `text/event-stream` 时,在页面代理层或 controller buffered 收口层解析 SSE 文本,提取所有 `data:` 行,返回结构化结果:
47 
48 ```json
49 {
50@@ -47,4 +66,4 @@ Low — 功能可用(调用方自行解析),但体验不好
51 
52 在 describe/business 的 browser/request 合约中说明:buffered 模式请求 SSE 端点时,response 字段是原始 SSE 文本,调用方需自行解析。
53 
54-方案 A 更友好但改动大,方案 B 零改动。首版可以先用方案 B,后续再优化。
55+方案 A 更友好;方案 B 可以作为短期兼容说明,但不应再把当前行为写成“已经返回结构化 JSON”。
M bugs/FIX-BUG-015.md
+21, -102
  1@@ -1,112 +1,31 @@
  2 # FIX-BUG-015: 插件侧实现 SSE 流式回传
  3 
  4-## 关联 Bug
  5-
  6-BUG-015-sse-stream-open-timeout.md
  7-
  8-## 目标
  9-
 10-让 `POST /v1/browser/request` 的 `responseMode: "sse"` 在 Firefox 插件侧正确工作,实现 SSE 流拦截和逐事件回传。
 11-
 12-## 修改文件
 13-
 14-`plugins/baa-firefox/controller.js`
 15-
 16-## 背景
 17-
 18-conductor 侧的 SSE 基础设施已完整实现:
 19-- `FirefoxBridgeApiStreamSession`:seq tracking、buffer overflow 保护、idle timeout、open timeout
 20-- `firefox-ws.ts`:`handleStreamOpen`、`handleStreamEvent`、`handleStreamEnd`、`handleStreamError`
 21-- `local-api.ts`:SSE HTTP response 生成(`text/event-stream`)
 22-
 23-缺失的是插件侧:收到 `api_request` 且 `responseMode === "sse"` 时,需要用流式方式回传而不是等 buffered 响应。
 24-
 25-## 修改方案
 26-
 27-在 controller.js 中处理 api_request 的代码路径里,加入 `responseMode === "sse"` 分支:
 28+## 状态
 29 
 30-### 1. 检测 responseMode
 31+- `无需执行(2026-03-27 代码核对)`
 32 
 33-当收到 WS 消息 `type: "api_request"` 时,读取 `message.responseMode`(或 `message.response_mode`)。如果值为 `"sse"`,走流式处理路径。
 34-
 35-### 2. 流式 fetch + ReadableStream 解析
 36-
 37-```javascript
 38-// 伪代码
 39-const response = await fetch(url, { method, headers, body, credentials: "include" });
 40-
 41-// 发 stream_open
 42-wsSend({
 43-  type: "stream_open",
 44-  id: requestId,
 45-  streamId: streamId,
 46-  status: response.status,
 47-  meta: { headers: Object.fromEntries(response.headers.entries()) }
 48-});
 49-
 50-// 逐行读取 SSE
 51-const reader = response.body.getReader();
 52-const decoder = new TextDecoder();
 53-let buffer = "";
 54-let seq = 0;
 55-
 56-while (true) {
 57-  const { done, value } = await reader.read();
 58-  if (done) break;
 59-
 60-  buffer += decoder.decode(value, { stream: true });
 61-  const lines = buffer.split("\n");
 62-  buffer = lines.pop(); // 保留未完成的行
 63-
 64-  for (const line of lines) {
 65-    if (line.startsWith("data: ")) {
 66-      seq++;
 67-      wsSend({
 68-        type: "stream_event",
 69-        id: requestId,
 70-        streamId: streamId,
 71-        seq: seq,
 72-        event: "message",
 73-        data: line.slice(6),
 74-        raw: line
 75-      });
 76-    }
 77-  }
 78-}
 79-
 80-// 发 stream_end
 81-wsSend({
 82-  type: "stream_end",
 83-  id: requestId,
 84-  streamId: streamId,
 85-  status: response.status
 86-});
 87-```
 88-
 89-### 3. 错误处理
 90+## 关联 Bug
 91 
 92-fetch 失败或 reader 异常时发 `stream_error`:
 93+BUG-015-sse-stream-open-timeout.md
 94 
 95-```javascript
 96-wsSend({
 97-  type: "stream_error",
 98-  id: requestId,
 99-  streamId: streamId,
100-  code: "fetch_error",
101-  message: error.message,
102-  status: null
103-});
104-```
105+## 结论
106 
107-### 4. 取消支持
108+这张 fix 卡对应的工作,按当前代码核对已经存在:
109 
110-收到 `type: "request_cancel"` 时,调用 `reader.cancel()` 并发 `stream_end`。
111+- `plugins/baa-firefox/controller.js`
112+  - 已有 `response_mode === "sse"` 分支
113+  - 已有 `handlePageSse(...)`
114+- `plugins/baa-firefox/page-interceptor.js`
115+  - 已有 `streamProxyResponse(...)`
116+  - 已有 `ReadableStream` 逐块读取和事件转发
117+- `tests/browser/browser-control-e2e-smoke.test.mjs`
118+  - 已覆盖 `stream_open` / `stream_event` / `stream_end`
119 
120-## 验收标准
121+## 现在该怎么做
122 
123-1. `responseMode: "sse"` 请求 Claude completion 端点,conductor 返回 `text/event-stream` 响应
124-2. 响应中包含正确的 `stream_open`、多个 `stream_event`(每个带递增 seq)、`stream_end`
125-3. `stream_event.data` 包含 Claude 的原始 SSE data 字段内容
126-4. 流中途断开或出错时正确返回 `stream_error`
127-5. `responseMode: "buffered"` 行为不受影响
128-6. `pnpm typecheck` 和 `pnpm test` 通过
129+- 不再按“补 SSE 实现”开工
130+- 如果线上仍能复现 `stream_open_timeout`
131+- 应新开基于真实复现条件的 bug,重点排查:
132+  - 页面注入是否失效
133+  - 请求是否走到了错误标签页
134+  - 特定平台 / 特定 content-type / 特定页面环境下的兼容问题
M bugs/FIX-BUG-016.md
+15, -56
 1@@ -1,66 +1,25 @@
 2 # FIX-BUG-016: 插件侧透传自定义 headers
 3 
 4-## 关联 Bug
 5-
 6-BUG-016-custom-headers-not-forwarded.md
 7-
 8-## 目标
 9-
10-让 `POST /v1/browser/request` 的 `headers` 字段在插件侧 fetch 时正确透传。
11-
12-## 修改文件
13-
14-`plugins/baa-firefox/controller.js`
15+## 状态
16 
17-## 修改方案
18+- `无需执行(2026-03-27 代码核对)`
19 
20-### 1. 读取 headers
21-
22-在 api_request 消息处理中,读取 `message.headers`:
23-
24-```javascript
25-const customHeaders = {};
26-if (message.headers && typeof message.headers === "object") {
27-  for (const [key, value] of Object.entries(message.headers)) {
28-    if (typeof key === "string" && typeof value === "string") {
29-      customHeaders[key] = value;
30-    }
31-  }
32-}
33-```
34-
35-### 2. 安全过滤
36-
37-不允许覆盖浏览器管控的 header:
38+## 关联 Bug
39 
40-```javascript
41-const BLOCKED_HEADERS = new Set([
42-  "cookie", "host", "origin", "referer",
43-  "sec-fetch-dest", "sec-fetch-mode", "sec-fetch-site",
44-  "connection", "upgrade"
45-]);
46+BUG-016-custom-headers-not-forwarded.md
47 
48-for (const key of Object.keys(customHeaders)) {
49-  if (BLOCKED_HEADERS.has(key.toLowerCase())) {
50-    delete customHeaders[key];
51-  }
52-}
53-```
54+## 结论
55 
56-### 3. 合并到 fetch
57+这张 fix 卡对应的“header 透传实现”按当前代码核对已经存在:
58 
59-```javascript
60-const fetchHeaders = {
61-  "Content-Type": "application/json",
62-  ...customHeaders
63-};
64-const response = await fetch(url, { method, headers: fetchHeaders, body, credentials: "include" });
65-```
66+- bridge 层会把 `headers` 放入 `api_request`
67+- 页面注入层会把 `detail.headers` 合并进真实 `fetch(...)`
68+- forbidden headers 过滤也已经存在
69 
70-## 验收标准
71+## 现在该怎么做
72 
73-1. 发送带 `headers: {"X-Custom": "test"}` 的请求,上游能收到该 header
74-2. 发送带 `headers: {"Cookie": "evil"}` 的请求,Cookie header 被过滤不透传
75-3. ChatGPT `/backend-api/conversation` 配合 sentinel token header 能成功发送对话
76-4. 不带 headers 字段的请求行为不变
77-5. `pnpm typecheck` 和 `pnpm test` 通过
78+- 不再按“补 header 透传实现”开工
79+- 如果线上仍复现 header 问题,应针对具体 header 新开 bug,重点排查:
80+  - 是否命中 forbidden header 过滤
81+  - 是否被页面环境或上游接口额外限制
82+  - 是否只在某个平台 / 某条路径出现
M bugs/FIX-BUG-017.md
+27, -11
 1@@ -10,11 +10,18 @@ buffered 模式请求 SSE 端点时,插件侧解析 SSE 文本,返回结构
 2 
 3 ## 修改文件
 4 
 5-`plugins/baa-firefox/controller.js`
 6+- `plugins/baa-firefox/page-interceptor.js`
 7+- 如需在 bridge 收口层兜底,可同时调整 `plugins/baa-firefox/controller.js`
 8+- 测试建议同步更新 `tests/browser/browser-control-e2e-smoke.test.mjs`
 9 
10 ## 修改方案
11 
12-在 api_request 的 buffered 响应路径中,检测 `response.headers.get("content-type")`:
13+当前代码的真实问题点在页面代理层 buffered 路径,而不只是 controller。推荐优先在 `page-interceptor.js` 的 `__baa_proxy_request__` buffered 分支处理:
14+
15+- 当前这里会直接 `await response.text()` 后把原始字符串放进 `body`
16+- 当 `content-type` 是 `text/event-stream` 时,应该先解析,再把结构化结果塞进 `body`
17+
18+伪代码:
19 
20 ```javascript
21 const contentType = response.headers.get("content-type") || "";
22@@ -38,13 +45,20 @@ if (contentType.includes("text/event-stream")) {
23     }
24   }
25 
26-  wsSend({
27-    type: "api_response",
28-    id: requestId,
29-    ok: true,
30+  emit("__baa_proxy_response__", {
31+    id,
32+    platform: pageRule.platform,
33+    url,
34+    method,
35+    ok: response.ok,
36     status: response.status,
37-    body: { content_type: "text/event-stream", events, full_text: fullText }
38-  });
39+    body: {
40+      content_type: "text/event-stream",
41+      events,
42+      full_text: fullText,
43+      raw
44+    }
45+  }, pageRule);
46 } else {
47   // 现有 JSON/text 处理逻辑
48 }
49@@ -52,6 +66,8 @@ if (contentType.includes("text/event-stream")) {
50 
51 ## 验收标准
52 
53-1. buffered 模式请求 Claude completion,`response.body` 包含 `events` 数组和 `full_text` 字段
54-2. buffered 模式请求 JSON 端点(如 `/api/organizations`),行为不变
55-3. `pnpm typecheck` 和 `pnpm test` 通过
56+1. buffered 模式请求 Claude completion,`response.body` 不再是原始 SSE 字符串,而是结构化对象
57+2. 结构化对象至少包含 `events` 和 `full_text`
58+3. buffered 模式请求普通 JSON 端点(如 `/api/organizations`)行为不变
59+4. legacy Claude helper 行为不回退
60+5. `pnpm typecheck` 和 `pnpm test` 通过
M bugs/README.md
+17, -3
 1@@ -12,6 +12,8 @@
 2 1. `BUG-008` — 已随 `BUG-010` 一并修复
 3 2. `BUG-009` — 已修复
 4 3. `BUG-010` — 已修复
 5+4. `BUG-015` — 按当前代码核对,SSE 流式回传主链路已落地,不再作为“插件缺少 SSE 实现”的 open bug 保留
 6+5. `BUG-016` — 按当前代码核对,自定义 headers 已进入 bridge -> plugin -> page fetch 链路,不再作为“headers 完全未透传”的 open bug 保留
 7 
 8 ## 待修复
 9 
10@@ -21,11 +23,23 @@
11 | BUG-012 | `BUG-012-*.md` | browser-request-policy waiter 死锁 | Medium | FIX-BUG-012.md |
12 | BUG-013 | `BUG-013-*.md` | stream session timer 未清除 | Low | FIX-BUG-013.md |
13 | BUG-014 | `BUG-014-*.md` | ws_reconnect 提前报 completed=true | Low-Medium | FIX-BUG-014.md |
14-| BUG-015 | `BUG-015-*.md` | SSE 流式模式 stream_open_timeout | High | FIX-BUG-015.md |
15-| BUG-016 | `BUG-016-*.md` | browser/request 自定义 headers 未透传 | Medium | FIX-BUG-016.md |
16 | BUG-017 | `BUG-017-*.md` | buffered 模式 SSE 端点返回原始文本 | Low | FIX-BUG-017.md |
17 
18-修复优先级:BUG-015 > BUG-011 > BUG-016 > BUG-012 > BUG-014 > BUG-017 > BUG-013
19+修复优先级:BUG-011 > BUG-012 > BUG-014 > BUG-017 > BUG-013
20+
21+## 当前代码核对结论(2026-03-27)
22+
23+- `BUG-015` 的“插件侧缺少 SSE 实现”结论与当前代码不一致:
24+  - `plugins/baa-firefox/controller.js` 已有 `response_mode === "sse"` 分支
25+  - `plugins/baa-firefox/page-interceptor.js` 已实现 `streamProxyResponse(...)`
26+  - 现有测试已覆盖 `stream_open` / `stream_event` / `stream_end`
27+- `BUG-016` 的“自定义 headers 完全未透传”结论与当前代码不一致:
28+  - `firefox-bridge.ts` 已把 `headers` 放入 `api_request`
29+  - `page-interceptor.js` 会把 `detail.headers` 合并进实际 `fetch(...)`
30+  - 同时保留 forbidden header 过滤
31+- `BUG-017` 仍然成立:
32+  - buffered 模式请求 SSE 端点时,当前仍回原始 SSE 文本
33+- 如果 `BUG-015` / `BUG-016` 仍在线上环境复现,应以新的复现条件重新开卡,不再沿用“缺少实现”这一版根因描述
34 
35 ## 优化建议
36 
M plans/STATUS_SUMMARY.md
+15, -2
 1@@ -2,11 +2,12 @@
 2 
 3 ## 当前时间
 4 
 5-- `2026-03-26`
 6+- `2026-03-27`
 7 
 8 ## 当前代码基线
 9 
10-- 主线基线:`main@07895cd`
11+- 浏览器控制主链路收口基线:`main@07895cd`
12+- 最近功能代码提交:`main@25be868`(启动时自动恢复受管 Firefox shell tabs)
13 - 任务文档已统一收口到 `tasks/`
14 - 当前活动任务见 `tasks/TASK_OVERVIEW.md`
15 - `T-S001` 到 `T-S025` 已经完成
16@@ -15,6 +16,7 @@
17 
18 - `已完成`:`T-S001` 到 `T-S025`
19 - `当前 TODO`:无高优先级主线任务
20+- `待处理缺陷`:`BUG-011` 到 `BUG-014`(见 `bugs/README.md`)
21 - `低优先级 TODO`:`4318/status-api` 兼容层删旧与解耦
22 
23 当前新的主需求文档:
24@@ -62,10 +64,13 @@
25 
26 1. `T-S023`:已完成,通用 browser request / cancel / SSE 链路和首版风控策略已接入主线
27 2. `T-S024`:已完成,README / docs / smoke / 状态视图已同步到正式口径
28+3. `T-S025`:已完成,`shell_runtime`、结构化 `action_result` 和控制读面已接入主线
29+4. `2026-03-27` 跟进修复:Firefox 插件管理页启动、浏览器重开或扩展重载后,会自动恢复之前明确启用过、但当前缺失的 shell tab;平台根页也会被收进受管理 shell 集合
30 
31 当前策略:
32 
33 - 当前没有高优先级主线 blocker
34+- 当前 bug backlog 单独留在 `bugs/` 目录,不把它和主线需求任务混写
35 - 当前不把大文件拆分当作主线 blocker
36 - 以下重构工作顺延到下一轮专门重构任务:
37   - `apps/conductor-daemon/src/local-api.ts`
38@@ -74,6 +79,12 @@
39   - `apps/conductor-daemon/src/index.ts`
40   - `apps/conductor-daemon/src/index.test.js`
41 
42+## 当前缺陷 backlog
43+
44+- 当前待修:`BUG-011`、`BUG-012`、`BUG-013`、`BUG-014`
45+- 对应修复卡:`FIX-BUG-011.md`、`FIX-BUG-012.md`、`FIX-BUG-013.md`、`FIX-BUG-014.md`
46+- 当前没有 bug fix 正在主线开发中;如需继续推进,直接从 `bugs/` 目录对应 fix 卡开工
47+
48 ## 低优先级 TODO
49 
50 下面这些继续保留,但不再是当前最高优先级:
51@@ -109,6 +120,7 @@
52 - `T-S023`:通用 browser request / cancel / SSE 链路、`stream_*` 事件模型和首版浏览器风控策略已经接入 `conductor` 与 Firefox bridge
53 - `T-S024`:README、API / Firefox / runtime 文档、browser smoke 和任务状态视图已同步到正式主线口径
54 - `T-S025`:`shell_runtime`、结构化 `action_result` 和控制读面已接入主线;唯一残余风险是当前机器缺少 `Firefox.app`,因此未完成真实 Firefox 手工 smoke
55+- `2026-03-27` 跟进修复:Firefox 插件现在会在管理页启动、浏览器重开或扩展重载后自动执行一次后台 `tab_restore`,并会把用户手工打开的 Claude `new`、ChatGPT 根页和 Gemini `app` 收进受管理 shell 集合
56 - 根级 `pnpm smoke` 已进主线,覆盖 runtime public-api compatibility、legacy absence、codexd e2e 和 browser-control e2e smoke
57 
58 ## 4318 依赖盘点与结论
59@@ -140,5 +152,6 @@
60 - `status-api` 的终局已经先收口到“保留为 opt-in 兼容层”;真正删除它之前,还要先清 `4318` 调用方并拆掉当前构建时复用
61 - 风控状态当前仍是进程内内存态;`conductor` 重启后,限流、退避和熔断计数会重置
62 - 正式 browser HTTP relay 当前仍只支持 Claude;其它平台目前只有空壳页和元数据链路,没有接入通用 request / SSE 合同
63+- `BUG-011` 到 `BUG-014` 当前都还没有进入“已修复”状态;其中 `BUG-014` 直接影响 `ws_reconnect` 的 `action_result.completed` 语义
64 - 当前机器未发现 `Firefox.app`,因此尚未执行“手动关 tab -> tab_restore -> WS 重连后状态回报恢复”的真实 Firefox 手工 smoke;这是当前唯一环境型残余风险
65 - 当前多个源码文件已超过常规体量,但拆分重构已明确延后到浏览器桥接主线收口之后再做
M tasks/T-S025.md
+11, -0
 1@@ -27,6 +27,8 @@
 2 - `2026-03-26`:代码闭环已接通;`shell_runtime`、结构化 `action_result`、`/v1/browser` 与 `/describe/control` 已同步落地。
 3 - `2026-03-26`:自动化验证已通过,包括 `apps/conductor-daemon/src/index.test.js` 和 `tests/browser/browser-control-e2e-smoke.test.mjs`。
 4 - `2026-03-26`:真实 Firefox 手工 smoke 在当前机器上受阻;未发现 `Firefox.app`(已检查 `/Applications` 与 `~/Applications`),因此无法在本环境完成“真实 Firefox 手工验收”。
 5+- `2026-03-27`:后续代码跟进已补上“插件管理页启动 / 浏览器重开 / 扩展重载后自动恢复 desired shell tabs”,并支持把平台根页收进受管理 shell 集合。
 6+- `2026-03-27`:`BUG-014` 已登记;当前 `ws_reconnect` 仍会在真正断开 / 重连前回报 `completed=true`,需要后续单独修复。
 7 
 8 ## 建议分支名
 9 
10@@ -167,6 +169,14 @@
11   - `current_client.last_action_result`
12   - `claude.shell_runtime`
13   - `records[].live.shell_runtime`
14+- Firefox 插件现在会在管理页启动、浏览器重开或扩展重载后自动执行一次后台 `tab_restore`,恢复之前明确启用过、但当前缺失的 shell tab;如果用户手工打开 Claude `new`、ChatGPT 根页或 Gemini `app`,插件也会把它们纳入受管理 shell 集合。
15+
16+## 后续代码跟进
17+
18+- `2026-03-27`:提交 `25be868` 已补上启动时的受管 shell tab 自动恢复逻辑,收口了“插件重启后 desired 仍在、actual 丢失但没有自动调和”的空窗。
19+- `2026-03-27`:同一轮跟进里,`verify-mini.sh` 的 wrapper 调用也改成数组参数拼装,降低空参数场景下的脚本调用风险。
20+- `2026-03-27`:当前没有新增手工 smoke 结果;环境阻塞仍然是本机缺少可启动的 `Firefox.app`。
21+- `2026-03-27`:当前仍需后续单独处理 `BUG-014`,不应把它误判为已随本轮启动恢复修复一并关闭。
22 
23 ## 自动化验证
24 
25@@ -184,6 +194,7 @@
26   - 状态回报恢复
27 - 结果:当前环境阻塞,未执行
28 - 阻塞原因:本机未发现可启动的 `Firefox.app`(已检查 `/Applications`、`~/Applications` 和 Spotlight `org.mozilla.firefox` bundle id),因此无法在这台机器上完成真实 Firefox 手工 smoke。
29+- 截至 `2026-03-27`:该状态未变化;本轮只补了启动恢复代码和文档同步,没有新增真实 Firefox 手工验收结果。
30 
31 ## 交付要求
32 
M tasks/TASK_OVERVIEW.md
+12, -1
 1@@ -9,12 +9,14 @@
 2 - `control-api.makefile.so`、Cloudflare Worker、D1 只剩迁移期 legacy 兼容残留和依赖盘点用途
 3 - `baa-hand` / `baa-shell` 只保留为接口语义参考,不再作为主系统维护
 4 - 当前任务卡都放在本目录
 5-- 当前任务基线:`main@07895cd`
 6+- 浏览器控制主链路收口基线:`main@07895cd`
 7+- 最近功能代码提交:`main@25be868`(启动时自动恢复受管 Firefox shell tabs)
 8 
 9 ## 状态分类
10 
11 - `已完成`:`T-S001` 到 `T-S025`
12 - `当前 TODO`:无高优先级主线任务
13+- `待处理缺陷`:`BUG-011` 到 `BUG-014`(见 `../bugs/README.md`)
14 - `低优先级 TODO`:`4318/status-api` 兼容层删旧与解耦
15 
16 当前新的主需求文档:
17@@ -56,6 +58,7 @@
18 
19 - 根级 `pnpm smoke`,覆盖 repo 内可自举的 runtime compatibility / legacy absence / codexd e2e / browser-control e2e smoke
20 - 根级 `pnpm verify:mini`,作为 `mini` 节点 on-node 静态+运行态检查入口
21+- `2026-03-27` 跟进修复:Firefox 插件管理页启动、浏览器重开或扩展重载后,会自动恢复之前明确启用过、但当前缺失的 shell tab;平台根页也会被收进受管理 shell 集合
22 
23 说明:
24 
25@@ -64,6 +67,7 @@
26 ## 当前活动任务
27 
28 - 当前没有高优先级活动任务卡;如需继续推进,直接新开后续任务
29+- 当前可直接执行的缺陷修复卡位于 `../bugs/`:`FIX-BUG-011.md`、`FIX-BUG-012.md`、`FIX-BUG-013.md`、`FIX-BUG-014.md`
30 
31 ## 当前主线收口情况
32 
33@@ -79,6 +83,12 @@
34 8. [`T-S024.md`](./T-S024.md):已完成,README / docs / smoke / 状态视图已经同步到正式口径
35 9. [`T-S025.md`](./T-S025.md):已完成,`shell_runtime`、结构化 `action_result` 和控制读面已接入;唯一剩余风险是当前机器缺少 `Firefox.app`,未完成真实手工 smoke
36 
37+最近代码跟进:
38+
39+- `2026-03-27`:Firefox 插件管理页启动、浏览器重开或扩展重载后,会自动恢复之前明确启用过、但当前缺失的 shell tab
40+- `2026-03-27`:如果用户手工打开 Claude `new`、ChatGPT 根页或 Gemini `app` 这类平台根页,插件会把它们纳入受管理 shell 集合
41+- `2026-03-27`:`verify-mini.sh` 的 wrapper 调用已收口为数组参数组装,避免空参数场景下的命令拼接问题
42+
43 建议并行关系:
44 
45 - `T-S017` 与 `T-S018` 已并行完成
46@@ -92,6 +102,7 @@
47 
48 - 风控状态当前仍是进程内内存态;`conductor` 重启后,限流、退避和熔断计数会重置
49 - 正式 browser HTTP relay 当前仍只支持 Claude;其它平台目前只有空壳页和元数据链路,没有接入通用 request / SSE 合同
50+- `BUG-011` 到 `BUG-014` 当前都还是待修状态;其中 `BUG-014` 直接影响 `ws_reconnect` 的 `completed` 语义
51 - runtime smoke 仍依赖仓库根已有 `state/`、`runs/`、`worktrees/`、`logs/launchd/`、`logs/codexd/`、`tmp/` 等本地运行目录;这是现有脚本前提,不是本轮功能回归
52 - 当前机器未发现 `Firefox.app`,因此尚未执行“手动关 tab -> `tab_restore` -> WS 重连后状态回报恢复”的真实 Firefox 手工 smoke;这是当前唯一残余风险
53