baa-conductor


baa-conductor / bugs / archive
im_wower  ·  2026-03-28

BUG-022-final-message-claude-missing.md

  1# BUG-022: final-message.js 缺少 Claude 平台支持,有机回复永远不会被捕获
  2
  3## 状态
  4
  5- 已修复(2026-03-28,代码已合入主线)
  6
  7## 当前代码结论
  8
  9- `isRelevantStreamUrl()` 现在已补齐 Claude `/completion` 识别
 10- `observeSse()``observeNetwork()` 都已显式支持 Claude final-message 提取
 11- Claude 的空 `completion` 事件不会再把元数据误拼进 `raw_text`
 12- 当前自动化验证覆盖了 SSE 和 buffered/network 两条 Claude relay 路径
 13
 14> 提交者:Claude(代码审查 + 实测验证)
 15> 日期:2026-03-27
 16
 17## 现象
 18
 19用户在 mini 的 Firefox 上打开 Claude 对话,发消息请求 AI 输出 baa 代码块。Claude 正常回复了,但 conductor 的 instruction_ingest 没有收到任何 Claude 平台的 final_message。recent_ingests 全是 Gemini,0 条 Claude。
 20
 21## 根因
 22
 23`plugins/baa-firefox/final-message.js``isRelevantStreamUrl` 函数缺少 Claude 分支:
 24
 25```javascript
 26function isRelevantStreamUrl(platform, url) {
 27    if (platform === "chatgpt") {
 28      return lower.includes("/conversation");
 29    }
 30    if (platform === "gemini") {
 31      return lower.includes("streamgenerate") || ...;
 32    }
 33    return false;  // ← Claude 走到这里,永远 false
 34}
 35```
 36
 37`observeSse` 第一行就返回 null:
 38```javascript
 39if (!isRelevantStreamUrl(state.platform, detail.url)) {
 40  return null;  // Claude 的 SSE 永远不会被处理
 41}
 42```
 43
 44同时,`observeSse` 最终提取候选时也没有 Claude 分支:
 45```javascript
 46const finalCandidate = state.platform === "chatgpt"
 47  ? extractChatgptCandidateFromText(...)
 48  : extractGeminiCandidateFromText(...);  // Claude 走 Gemini 分支,无法提取
 49```
 50
 51## 缺失的三样东西
 52
 531. `isRelevantStreamUrl` 缺少 Claude case:URL 包含 `/completion`
 542. `extractClaudeCandidateFromText` 不存在:需要解析 Claude SSE 格式
 553. `observeSse` 的候选提取缺少 Claude 分支
 56
 57## Claude SSE 格式
 58
 59```
 60event: completion
 61data: {"type":"completion","id":"chatcompl_xxx","completion":" hello","stop_reason":null,...}
 62
 63event: completion
 64data: {"type":"completion","id":"chatcompl_xxx","completion":" world","stop_reason":"end_turn",...}
 65```
 66
 67- `completion` 字段拼接 = 完整回复文本
 68- `id` 字段 = assistant message id
 69- URL 格式:`/api/organizations/{org}/chat_conversations/{conv_id}/completion`
 70  → conv_id 可从 URL 提取
 71
 72## 修复
 73
 74### 1. isRelevantStreamUrl 加 Claude
 75
 76```javascript
 77if (platform === "claude") {
 78  return lower.includes("/completion");
 79}
 80```
 81
 82### 2. 新增 extractClaudeCandidateFromText
 83
 84```javascript
 85function extractClaudeConversationIdFromUrl(url) {
 86  const match = String(url || "").match(
 87    /\/chat_conversations\/([a-f0-9-]+)\/completion/i
 88  );
 89  return match?.[1] || null;
 90}
 91
 92function extractClaudeCandidateFromText(text, context) {
 93  let fullText = "";
 94  let assistantMessageId = null;
 95
 96  for (const line of String(text || "").split("\n")) {
 97    const trimmed = line.trim();
 98    if (!trimmed.startsWith("data:")) continue;
 99    const payload = parseJson(trimmed.slice(5).trimStart());
100    if (!payload || payload.type !== "completion") continue;
101    if (typeof payload.completion === "string") {
102      fullText += payload.completion;
103    }
104    if (!assistantMessageId && payload.id) {
105      assistantMessageId = String(payload.id);
106    }
107  }
108
109  const rawText = normalizeMessageText(fullText);
110  if (!rawText) return null;
111
112  return {
113    assistantMessageId: assistantMessageId || null,
114    conversationId: extractClaudeConversationIdFromUrl(context.url)
115      || extractClaudeConversationIdFromUrl(context.pageUrl),
116    rawText
117  };
118}
119```
120
121### 3. observeSse 加 Claude 分支
122
123```javascript
124let finalCandidate;
125if (state.platform === "chatgpt") {
126  finalCandidate = extractChatgptCandidateFromText(fullText, context);
127} else if (state.platform === "claude") {
128  finalCandidate = extractClaudeCandidateFromText(fullText, context);
129} else {
130  finalCandidate = extractGeminiCandidateFromText(fullText, context);
131}
132```
133
134## 严重度
135
136**Critical** — Claude 是 Phase 1 的主要平台,没有这个修复整个 BAA 指令系统的端到端闭环对 Claude 完全不工作
137
138## 验收
139
1401. 用户在 Firefox 上的 Claude 对话中发消息
1412. Claude 回复后,conductor 的 `instruction_ingest.recent_ingests` 出现 `platform: "claude"` 的记录
1423. 如果回复包含 baa 代码块,`status` 应为 `"executed"` 而非 `"ignored_no_instructions"`