baa-conductor

git clone 

commit
7c7e4c5
parent
6391ada
author
im_wower
date
2026-03-29 03:54:51 +0800 CST
docs: update T-BUG-029 with root cause from diagnostic logs

Root cause identified via T-S054 plugin logs:
1. Main SSE stream (/backend-api/f/conversation) is aborted, chunks discarded
2. isRelevantStreamUrl too broad, matches auxiliary endpoints
3. Need to extract candidate from chunks even on abort

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 files changed,  +77, -18
M tasks/T-BUG-029.md
+77, -18
  1@@ -41,17 +41,40 @@
  2 
  3 ## 目标
  4 
  5-修复 ChatGPT 使用思考模式(o3/o4 模型)时,final-message 提取器只捕获到 "Thought for a few seconds" 思考标记,漏掉真正 assistant 回复的问题。
  6+修复 ChatGPT final-message 捕获完全失败的问题。当前 ChatGPT 回复的 baa 指令永远无法被提取。
  7 
  8 ## 背景
  9 
 10-手动验证 ChatGPT 闭环时发现:ChatGPT 回复包含 baa 指令块,但 conductor 收到的 raw_text 是 "Thought for a couple of seconds"。
 11+通过 T-S054 的插件诊断日志(`logs/baa-plugin/`)已定位到根本原因。**不是之前猜测的 thinking 模式问题**,而是两个并发问题:
 12 
 13-ChatGPT 的思考模式 SSE 流结构:
 14-- 先发 thinking/reasoning 相关的 message(status 为 in_progress)
 15-- 再发真正的 assistant 回复(status 为 finished_successfully 或 completed)
 16+### 根本原因 1:主对话 SSE 流被 abort
 17 
 18-`extractChatgptCandidateFromText` 可能把 thinking 标记当成了最终回复。
 19+ChatGPT 当前的主对话 SSE 流路径是 `/backend-api/f/conversation`(新路径,带 `/f/` 前缀)。该流在传输过程中被浏览器或 ChatGPT 前端 abort:
 20+
 21+```
 22+[SSE] tab=8 chatgpt error /backend-api/f/conversation duration=-ms error=The operation was aborted.
 23+```
 24+
 25+`observeSse` 中当 `detail.done !== true` 时返回 null,而 abort 不会触发 `done=true`,所以已收集的 SSE chunks 被丢弃,relay 永远是 null。
 26+
 27+### 根本原因 2:辅助请求匹配但无 assistant 内容
 28+
 29+`isRelevantStreamUrl` 对 ChatGPT 的判断是 `url.includes("/conversation")`,过于宽泛。以下辅助路径都匹配了但不含 assistant 回复:
 30+
 31+- `/backend-api/conversation/implicit_message_feedback`
 32+- `/backend-api/f/conversation/prepare`
 33+
 34+这些请求的 FM-SSE 和 FM-NET 全部返回 `relay=null, assistant=-`。
 35+
 36+### 诊断日志证据
 37+
 38+```
 39+sse_stream_start  POST /backend-api/f/conversation          ← 主流开始
 40+sse_stream_start  POST /backend-api/conversation/implicit_message_feedback  ← 辅助流
 41+sse_stream_done   POST /backend-api/conversation/implicit_message_feedback  ← 辅助流结束
 42+[FM-SSE] relay=null                                          ← 辅助流无内容
 43+[SSE] error /backend-api/f/conversation error=The operation was aborted.  ← 主流被 abort!
 44+```
 45 
 46 ## 涉及仓库
 47 
 48@@ -69,24 +92,60 @@ ChatGPT 的思考模式 SSE 流结构:
 49 
 50 ## 必须完成
 51 
 52-### 1. 排查
 53+### 1. 修复 SSE abort 时丢弃已收集数据的问题
 54+
 55+**文件**:`plugins/baa-firefox/final-message.js` 的 `observeSse` 函数
 56+
 57+当前逻辑:`detail.done !== true` 时返回 null,已收集的 chunks 被丢弃。
 58+
 59+**修复**:当 `detail.error` 或 SSE 流异常结束时(abort),如果已有收集到的 chunks,仍然尝试从中提取 candidate 并生成 relay。不能简单丢弃。
 60+
 61+```javascript
 62+// 伪代码:在 error/abort 分支中
 63+if (detail.error && stream.chunks.length > 0) {
 64+  // 尝试从已有 chunks 提取 candidate
 65+  const fullText = stream.chunks.join("\n\n");
 66+  const candidate = extractChatgptCandidateFromText(fullText, context);
 67+  const relay = buildRelayEnvelope(platform, candidate, meta.observedAt);
 68+  state.activeStream = null;
 69+  if (relay && !hasSeenRelay(state, relay)) return relay;
 70+}
 71+```
 72+
 73+### 2. 收紧 ChatGPT 的 isRelevantStreamUrl
 74+
 75+**文件**:`plugins/baa-firefox/final-message.js` 的 `isRelevantStreamUrl` 函数
 76+
 77+当前:`lower.includes("/conversation")` — 太宽泛,匹配了 `implicit_message_feedback` 和 `prepare` 等辅助流。
 78+
 79+**修复**:只匹配主对话流路径:
 80+
 81+```javascript
 82+if (platform === "chatgpt") {
 83+  // 匹配 /backend-api/conversation 和 /backend-api/f/conversation
 84+  // 排除 /conversation/implicit_message_feedback、/conversation/prepare 等辅助路径
 85+  return /\/(?:backend-api\/)?(?:f\/)?conversation\/?$/i.test(lower)
 86+    || lower.includes("/backend-api/conversation") && !lower.includes("/implicit_message_feedback") && !lower.includes("/prepare");
 87+}
 88+```
 89+
 90+或更简单的方式:排除已知的辅助路径。
 91+
 92+### 3. 处理 page-interceptor 中 SSE abort 的事件传递
 93 
 94-- 先看 `logs/baa-ingest/` 的日志,了解 conductor 收到了什么
 95-- 在 ChatGPT 页面用浏览器 DevTools 观察 SSE 流的原始数据,确认思考模式下的消息结构
 96-- 确认 `extractChatgptCandidateFromText` 和 `extractChatgptCandidateFromChunk` 的当前行为
 97+**文件**:`plugins/baa-firefox/page-interceptor.js`
 98 
 99-### 2. 修复
100+确认 SSE 流被 abort 时,page-interceptor 是否发出了 `__baa_sse__` 事件。当前可能只在正常结束时发 `done: true`,abort 时什么都不发。
101 
102-- 修改 `extractChatgptCandidateFromText` / `extractChatgptCandidateFromChunk`
103-- 确保跳过 thinking/reasoning 类型的消息
104-- 只提取 status 为 `finished_successfully` / `completed` 且 content 非空的 assistant 回复
105-- 评分逻辑应优先选择更长、有实际内容的 candidate
106+如果 abort 时没发事件,需要在 `streamSse` 的 catch/finally 中补发一个事件(带 `error: true` 和已收集的 chunks)。
107 
108-### 3. 验证
109+### 4. 验证
110 
111-- ChatGPT 思考模式下回复含 baa 指令 → conductor 收到完整回复(不是 "Thought for...")
112-- ChatGPT 非思考模式下回复 → 行为不变
113+- ChatGPT 回复含 baa 指令 → conductor 收到完整回复并提取到指令
114+- `logs/baa-plugin/` 中 FM-SSE 不再全是 `relay=null`
115+- `logs/baa-ingest/` 中 `blocks_count > 0`
116 - Claude 和 Gemini 的 final-message 不受影响
117+- 辅助流(`implicit_message_feedback`、`prepare`)不再触发 final-message 观察器
118 
119 ## 验收标准
120