baa-conductor

git clone 

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
A bugs/BUG-023-claude-final-message-not-captured-in-practice.md
+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 = 运行时/注入层(待排查)