baa-conductor

git clone 

baa-conductor / tasks
im_wower  ·  2026-03-29

T-BUG-029.md

  1# Task T-BUG-029:修复 ChatGPT 思考模式下 final-message 提取遗漏
  2
  3## 状态
  4
  5- 当前状态:`已完成`
  6- 规模预估:`M`
  7- 依赖任务:`T-S046`(需要日志辅助排查 ChatGPT SSE 原始数据)
  8- 建议执行者:`Claude`(需要理解 ChatGPT SSE 流结构和 final-message 提取逻辑)
  9
 10## 直接给对话的提示词
 11
 12`/Users/george/code/baa-conductor/tasks/T-BUG-029.md` 任务文档,完成开发任务。
 13
 14如需补背景,再读:
 15
 16- `/Users/george/code/baa-conductor/plans/NEXT_WAVE_REQUIREMENTS.md`(需求 1)
 17- `/Users/george/code/baa-conductor/plugins/baa-firefox/final-message.js`
 18- `/Users/george/code/baa-conductor/logs/baa-ingest/`(T-S046 完成后有日志可看)
 19
 20## 当前基线
 21
 22- 仓库:`/Users/george/code/baa-conductor`
 23- 分支基线:`main`(T-S046 合并后)
 24- 提交:`<T-S046 合并后的提交>`
 25
 26## 分支与 worktree(强制)
 27
 28- 分支名:`bug/chatgpt-thinking-final-message`
 29- worktree 路径:`/Users/george/code/baa-conductor-chatgpt-thinking-final-message`
 30
 31开工步骤:
 32
 331. `cd /Users/george/code/baa-conductor`
 342. `git worktree add ../baa-conductor-chatgpt-thinking-final-message -b bug/chatgpt-thinking-final-message main`
 353. `cd ../baa-conductor-chatgpt-thinking-final-message`
 36
 37完成后提交与推送(由执行者完成,不要合并):
 38
 391. 在 worktree 里提交所有变更(包括更新后的任务文档)
 402. `git push -u origin bug/chatgpt-thinking-final-message`
 41
 42## 目标
 43
 44修复 ChatGPT final-message 捕获完全失败的问题。当前 ChatGPT 回复的 baa 指令永远无法被提取。
 45
 46## 背景
 47
 48通过 T-S054 的插件诊断日志(`logs/baa-plugin/`)已定位到根本原因。**不是之前猜测的 thinking 模式问题**,而是两个并发问题:
 49
 50### 根本原因 1:主对话 SSE 流被 abort
 51
 52ChatGPT 当前的主对话 SSE 流路径是 `/backend-api/f/conversation`(新路径,带 `/f/` 前缀)。该流在传输过程中被浏览器或 ChatGPT 前端 abort:
 53
 54```
 55[SSE] tab=8 chatgpt error /backend-api/f/conversation duration=-ms error=The operation was aborted.
 56```
 57
 58`observeSse` 中当 `detail.done !== true` 时返回 null,而 abort 不会触发 `done=true`,所以已收集的 SSE chunks 被丢弃,relay 永远是 null。
 59
 60### 根本原因 2:辅助请求匹配但无 assistant 内容
 61
 62`isRelevantStreamUrl` 对 ChatGPT 的判断是 `url.includes("/conversation")`,过于宽泛。以下辅助路径都匹配了但不含 assistant 回复:
 63
 64- `/backend-api/conversation/implicit_message_feedback`
 65- `/backend-api/f/conversation/prepare`
 66
 67这些请求的 FM-SSE 和 FM-NET 全部返回 `relay=null, assistant=-` 68
 69### 诊断日志证据
 70
 71```
 72sse_stream_start  POST /backend-api/f/conversation          ← 主流开始
 73sse_stream_start  POST /backend-api/conversation/implicit_message_feedback  ← 辅助流
 74sse_stream_done   POST /backend-api/conversation/implicit_message_feedback  ← 辅助流结束
 75[FM-SSE] relay=null                                          ← 辅助流无内容
 76[SSE] error /backend-api/f/conversation error=The operation was aborted.  ← 主流被 abort!
 77```
 78
 79## 涉及仓库
 80
 81- `/Users/george/code/baa-conductor`
 82
 83## 允许修改的目录
 84
 85- `/Users/george/code/baa-conductor/plugins/baa-firefox/final-message.js`
 86- `/Users/george/code/baa-conductor/plugins/baa-firefox/` (如需加测试)
 87
 88## 尽量不要修改
 89
 90- `/Users/george/code/baa-conductor/apps/`
 91- `/Users/george/code/baa-conductor/packages/`
 92
 93## 必须完成
 94
 95### 1. 修复 SSE abort 时丢弃已收集数据的问题
 96
 97**文件**:`plugins/baa-firefox/final-message.js` 的 `observeSse` 函数
 98
 99当前逻辑:`detail.done !== true` 时返回 null,已收集的 chunks 被丢弃。
100
101**修复**:当 `detail.error` 或 SSE 流异常结束时(abort),如果已有收集到的 chunks,仍然尝试从中提取 candidate 并生成 relay。不能简单丢弃。
102
103```javascript
104// 伪代码:在 error/abort 分支中
105if (detail.error && stream.chunks.length > 0) {
106  // 尝试从已有 chunks 提取 candidate
107  const fullText = stream.chunks.join("\n\n");
108  const candidate = extractChatgptCandidateFromText(fullText, context);
109  const relay = buildRelayEnvelope(platform, candidate, meta.observedAt);
110  state.activeStream = null;
111  if (relay && !hasSeenRelay(state, relay)) return relay;
112}
113```
114
115### 2. 收紧 ChatGPT 的 isRelevantStreamUrl
116
117**文件**:`plugins/baa-firefox/final-message.js` 的 `isRelevantStreamUrl` 函数
118
119当前:`lower.includes("/conversation")` — 太宽泛,匹配了 `implicit_message_feedback``prepare` 等辅助流。
120
121**修复**:只匹配主对话流路径:
122
123```javascript
124if (platform === "chatgpt") {
125  // 匹配 /backend-api/conversation 和 /backend-api/f/conversation
126  // 排除 /conversation/implicit_message_feedback、/conversation/prepare 等辅助路径
127  return /\/(?:backend-api\/)?(?:f\/)?conversation\/?$/i.test(lower)
128    || lower.includes("/backend-api/conversation") && !lower.includes("/implicit_message_feedback") && !lower.includes("/prepare");
129}
130```
131
132或更简单的方式:排除已知的辅助路径。
133
134### 3. 处理 page-interceptor 中 SSE abort 的事件传递
135
136**文件**:`plugins/baa-firefox/page-interceptor.js`
137
138确认 SSE 流被 abort 时,page-interceptor 是否发出了 `__baa_sse__` 事件。当前可能只在正常结束时发 `done: true`,abort 时什么都不发。
139
140如果 abort 时没发事件,需要在 `streamSse` 的 catch/finally 中补发一个事件(带 `error: true` 和已收集的 chunks)。
141
142### 4. 验证
143
144- ChatGPT 回复含 baa 指令 → conductor 收到完整回复并提取到指令
145- `logs/baa-plugin/` 中 FM-SSE 不再全是 `relay=null`
146- `logs/baa-ingest/``blocks_count > 0`
147- Claude 和 Gemini 的 final-message 不受影响
148- 辅助流(`implicit_message_feedback`、`prepare`)不再触发 final-message 观察器
149
150## 验收标准
151
152- ChatGPT 思考模式回复 `@conductor::status` → conductor 提取到指令并执行
153- `logs/baa-ingest/` 日志中 raw_text 是完整回复内容
154- Claude final-message 不受影响
155
156## 推荐验证命令
157
158- `cd /Users/george/code/baa-conductor-chatgpt-thinking-final-message && pnpm build`
159- 实际在 ChatGPT 中触发一轮 baa 指令闭环验证
160
161## 执行记录
162
163> 以下内容由执行任务的 AI 填写,创建任务时留空。
164
165### 开始执行
166
167- 执行者:`Codex (GPT-5)`
168- 开始时间:`2026-03-29 03:45:00 CST(约)`
169- 状态变更:`待开始` → `进行中`
170
171### 完成摘要
172
173- 完成时间:`2026-03-29 04:02:01 CST`
174- 状态变更:`进行中` → `已完成`
175- 修改了哪些文件:
176  - `plugins/baa-firefox/final-message.js`
177  - `plugins/baa-firefox/page-interceptor.js`
178  - `plugins/baa-firefox/controller.js`
179  - `plugins/baa-firefox/final-message.test.cjs`
180  - `plugins/baa-firefox/page-interceptor.test.cjs`
181  - `tasks/T-BUG-029.md`
182- 核心实现思路:
183  - 收紧 ChatGPT final-message 观察 URL,只接受根对话流 `/conversation`、`/backend-api/conversation`、`/backend-api/f/conversation`,排除 `implicit_message_feedback`、`prepare` 等辅助流。
184  - `observeSse``detail.error` 终止时不再直接丢弃已缓存 chunks,而是复用正常收尾逻辑,从已有 SSE 文本中提取 candidate 并生成 relay。
185  - `page-interceptor``streamSse` 在 abort/error 收尾前先 flush 尚未按 `\n\n` 切出的尾部 buffer,再发出带 `error``__baa_sse__` 事件,避免最后一段数据静默丢失。
186  - `controller.js``[FM-SSE]` 调试日志扩展到 `done``error` 两种收尾,便于确认 abort 修复后的 relay 产出。
187- 跑了哪些测试:
188  - `node --test plugins/baa-firefox/final-message.test.cjs`
189  - `node --test plugins/baa-firefox/page-interceptor.test.cjs`
190  - `git diff --check`
191  - `pnpm build`(失败:worktree 缺少依赖,`tsc` 不存在,见下方问题记录)
192
193### 执行过程中遇到的问题
194
195> 记录执行过程中遇到的阻塞、环境问题、临时绕过方案等。合并时由合并者判断是否需要修复或建新任务。
196
197- worktree 当前未安装依赖,执行 `pnpm build` 时多个 workspace 包直接报 `Command "tsc" not found`,属于环境缺失而非本次改动引入的编译错误。
198
199### 剩余风险
200
201- 尚未在真实 ChatGPT 页面做一轮 `@conductor::status` 闭环验证,因此 `logs/baa-plugin/``logs/baa-ingest/` 的最终验收还需要人工浏览器联调确认。
202- Claude 与 Gemini 仅做了代码路径上的隔离确认,未在浏览器端做回归实测。