- commit
- fa6290a
- parent
- d6242b6
- author
- im_wower
- date
- 2026-03-28 03:13:07 +0800 CST
docs(claude): BUG-023 Claude final-message not captured in real Firefox — runtime/injection issue
1 files changed,
+101,
-0
1@@ -0,0 +1,101 @@
2+# BUG-023: Claude 有机回复在实际 Firefox 环境中仍未被 final-message 捕获
3+
4+> 提交者:Claude(实测验证)
5+> 日期:2026-03-28
6+
7+## 现象
8+
9+BUG-022 修复后(`9f3ca77`),final-message.js 已新增 Claude 平台支持(`isRelevantStreamUrl` + `extractClaudeCandidateFromText`),自动化测试通过。但在 mini 的真实 Firefox 环境中,用户在 Claude 页面正常聊天,conductor 的 `instruction_ingest` 始终没有收到 Claude 平台的 final_message。
10+
11+具体表现:
12+- `GET /v1/browser` 的 `instruction_ingest.recent_ingests` 全是 Gemini,0 条 Claude
13+- `bridge.clients[0].final_messages` 为空数组
14+- `claude.lastActivityAt` 始终为 0,说明 page-interceptor 的 SSE 事件没有到达 controller.js
15+
16+同时 Gemini 的 final_message 观察器正常工作(有 44 条 ingest 记录),证明 conductor 侧的 ingest pipeline 本身没问题。
17+
18+## 已排除的原因
19+
20+1. **代码未更新**:`git pull` 确认 main 分支已包含 `9f3ca77`(fix: relay Claude final messages)
21+2. **conductor 未重启**:已重启 conductor daemon,加载了最新代码
22+3. **插件未重载**:已在 `about:debugging` 重载扩展
23+4. **凭证问题**:`request_credentials` 返回 `freshness=fresh headers=19`,API 代发正常
24+5. **page-interceptor 未注入**:endpoint 采集正常(45 个 Claude 端点),说明 page-interceptor 的 `emitNet` 路径在工作
25+
26+## 关键线索
27+
28+### 线索 1:Claude 的 `lastActivityAt` 始终为 0
29+
30+这意味着 `handlePageSse` 中的 `applyObservedClaudeSse(data, tabId)` 从未被调用。但 `handlePageNetwork` 中的 `applyObservedClaudeResponse` 在工作(endpoint 采集正常)。
31+
32+说明 page-interceptor 的 `emitNet`(`__baa_net__`)在工作,但 `emitSse`(`__baa_sse__`)对 Claude completion 请求没有触发。
33+
34+### 线索 2:tab_reload 打开新 tab 而非 reload 当前 tab
35+
36+`POST /v1/browser/actions {"action":"tab_reload","platform":"claude"}` 不是 reload 用户正在聊天的 tab,而是 reload 的是 shell tab(`claude.ai/#baa-shell`)。用户实际发消息的 tab(`claude.ai/chat/xxx`)不受影响,仍然运行旧版 content-script。
37+
38+这导致了一个根本问题:**用户聊天的 tab 和插件受管的 shell tab 不是同一个 tab**。
39+
40+### 线索 3:shell_runtime 显示 drift
41+
42+之前观察到 Claude shell_runtime 的状态:
43+```json
44+{
45+ "actual": {
46+ "exists": false,
47+ "issue": "non_shell",
48+ "candidate_tab_id": 16,
49+ "candidate_url": "https://claude.ai/chat/xxx"
50+ },
51+ "drift": {
52+ "aligned": false,
53+ "needs_restore": true,
54+ "reason": "shell_missing"
55+ }
56+}
57+```
58+
59+用户在 `claude.ai/chat/xxx` 聊天,但插件认为 shell tab 应该是 `claude.ai/#baa-shell`,所以标记为 `non_shell` + `shell_missing`。
60+
61+## 根因假设
62+
63+page-interceptor 的 `isSse` 判断和 `streamSse` 函数在 Claude 页面上应该能正常工作(代码逻辑上 `pathname.endsWith("/completion")` 能匹配)。但 SSE 事件可能在以下环节断掉:
64+
65+### 假设 A:content-script 未注入到用户聊天 tab
66+
67+插件 reload 后,只有**新打开或 reload 的页面**才会注入新版 content-script。用户之前已经打开的 Claude 对话 tab 仍然运行旧版(没有 delivery-adapters.js,final-message.js 也可能是旧版或未加载)。
68+
69+`tab_reload` action 只操作 shell tab,不操作用户正在聊天的对话 tab。
70+
71+**验证方法**:在用户聊天的 Claude tab 上按 F5 手动刷新,再发消息。
72+
73+### 假设 B:page-interceptor 的 SSE 拦截对 Claude 不生效
74+
75+page-interceptor 的 `streamSse` 需要 `response.clone().body.getReader()` 来读 SSE。如果 Claude 的 fetch 请求使用了某种方式导致 response 不可 clone 或 body 不可 read,SSE 事件就不会被 emit。
76+
77+emitNet 能工作但 emitSse 不能工作,说明 `isSse` 返回 true 后走了 `streamSse` 分支,但 `streamSse` 内部的 reader 可能没产出任何 chunk。
78+
79+**验证方法**:在 Claude 页面的 devtools console 中检查 `__baa_sse__` 事件是否被 dispatch。
80+
81+### 假设 C:controller.js 中 FINAL_MESSAGE_HELPERS 为 null
82+
83+如果 final-message.js 加载失败(语法错误等),`globalThis.BAAFinalMessage` 为 undefined,controller.js 的 `FINAL_MESSAGE_HELPERS` 为 null,所有 final message 观察都会静默跳过。
84+
85+但 Gemini 的 final_message 在工作,说明 FINAL_MESSAGE_HELPERS 不为 null(除非 controller 页面和 Claude 页面用的是不同的 JS 上下文——但 FINAL_MESSAGE_HELPERS 是在 controller.html 中加载的,不是在 content-script 中)。
86+
87+## 建议排查步骤
88+
89+1. **手动刷新用户聊天的 Claude tab**(F5),再发消息,看 `lastActivityAt` 是否变化
90+2. **在 Claude tab 的 devtools console** 执行 `window.dispatchEvent(new CustomEvent("__baa_sse__", {detail:{test:true}}))` 看 controller 是否收到
91+3. **检查 page-interceptor 的 streamSse 是否对 Claude completion 触发**:在 devtools 中查看 `window.fetch` 是否被 patch(应该是 `patchedFetch`)
92+4. **检查 emitSse 在 Claude 页面上是否被调用**:在 page-interceptor.js 的 `streamSse` 函数入口加 `console.log`
93+
94+## 严重度
95+
96+**Critical** — BAA 指令系统对 Claude 的端到端闭环完全依赖 final-message 观察器。不解决这个问题,整个闭环对 Claude 不工作。
97+
98+## 与 BUG-022 的关系
99+
100+BUG-022 修复了 final-message.js 缺少 Claude 支持的代码问题。BUG-023 是在代码修复后、实际 Firefox 环境中仍然不工作的运行时问题。两个 bug 的层面不同:
101+- BUG-022 = 代码层(已修)
102+- BUG-023 = 运行时/注入层(待排查)