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 仅做了代码路径上的隔离确认,未在浏览器端做回归实测。