- commit
- 8c3090b
- parent
- e7c1701
- author
- codex@macbookpro
- date
- 2026-03-30 15:45:40 +0800 CST
docs: add BUG-028 for gemini shell final-message parsing
2 files changed,
+154,
-0
1@@ -0,0 +1,153 @@
2+# BUG-028: Gemini shell 页经 `conductor` API 发送后,`final_message` 返回协议碎片而非可读回复
3+
4+> 提交者:Codex
5+> 日期:2026-03-30
6+
7+## 现象
8+
9+当前通过内网 `conductor` 接口对 Gemini shell 页发消息,发送链路本身可以成功:
10+
11+- `POST /v1/browser/gemini/send` 返回 `200`
12+- `proxy.path` 指向 `/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate`
13+- `proxy.status` 为 `200`
14+
15+但后续读取 `GET /v1/browser/gemini/current` 时,`final_message` / `messages[0].text` 不是人类可读的 assistant 回复,而是 Gemini 协议层原始片段,例如:
16+
17+```text
18+[[[[2,15,2],1,0,[1774860674,754000000],80,80],[[2,4,2],1,8,[1774865703,894000000],25,23]],"e6fa609c3fa255c0"]
19+```
20+
21+在同一轮验证里,发送的 prompt 明确要求:
22+
23+```text
24+Reply with exactly: conductor-ok-731. No punctuation, no explanation.
25+```
26+
27+预期至少应能在 `current.final_message.raw_text` 或 `messages[].text` 中读到 `conductor-ok-731` 或等价的可读 assistant 文本;当前没有。
28+
29+## 触发路径
30+
31+```text
32+POST /v1/browser/actions { action: "tab_reload", platform: "gemini" }
33+-> POST /v1/browser/actions { action: "request_credentials", platform: "gemini" }
34+-> Gemini shell 页重新抓到 fresh 凭证
35+-> POST /v1/browser/gemini/send { prompt: "Reply with exactly: conductor-ok-731..." }
36+-> upstream StreamGenerate 返回 200
37+-> Firefox 插件继续上报 browser.final_message
38+-> conductor /v1/browser/gemini/current 读到的不是 assistant 文本,而是 Gemini 协议碎片
39+```
40+
41+## 根因
42+
43+根因还未完全确认,但高概率在 Gemini shell 页的最终消息提取逻辑:
44+
45+- `browser.gemini.send` 的发送链路已经通了,说明问题不在 `local-api` 路由分发本身
46+- `Gemini` 当前页的 `request_hooks` 与凭证刷新后均正常,说明问题不在“没有请求发出去”
47+- `plugins/baa-firefox/final-message.js` 负责 Gemini 响应解析与 `browser.final_message` 上报
48+- 当前 live 数据表明它在 shell 页上拿到的是 `batchexecute` / `StreamGenerate` 相关协议片段,但没有稳定提取出最终 assistant 文本
49+
50+当前判断更像是:
51+
52+- 解析器把 shell 页上的非最终文本帧、元数据帧或 batchexecute 结构误判成了最终消息
53+- 或者 `shell_page: true` 这一路径缺少稳定的 Gemini DOM / 协议 fallback
54+
55+## 复现步骤
56+
57+以下步骤可在 `mini` 内网直接复现。
58+
59+先确保 Gemini shell 页凭证是 fresh:
60+
61+```bash
62+curl -sS -X POST http://100.71.210.78:4317/v1/browser/actions \
63+ -H 'content-type: application/json' \
64+ --data '{"action":"tab_reload","platform":"gemini"}'
65+
66+curl -sS -X POST http://100.71.210.78:4317/v1/browser/actions \
67+ -H 'content-type: application/json' \
68+ --data '{"action":"request_credentials","platform":"gemini"}'
69+
70+curl -sS http://100.71.210.78:4317/v1/browser/gemini/current
71+```
72+
73+验证点:
74+
75+- `data.page.credentials.freshness` 应为 `fresh`
76+- `data.page.credentials.header_count` 应大于 `0`
77+
78+然后发一条最小可验证消息:
79+
80+```bash
81+curl -sS -X POST http://100.71.210.78:4317/v1/browser/gemini/send \
82+ -H 'content-type: application/json' \
83+ --data '{"prompt":"Reply with exactly: conductor-ok-731. No punctuation, no explanation."}'
84+```
85+
86+2026-03-30 实测关键返回:
87+
88+- HTTP 成功
89+- JSON `ok: true`
90+- `data.proxy.path = "/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate"`
91+- `data.proxy.status = 200`
92+
93+等待数秒后再读:
94+
95+```bash
96+curl -sS http://100.71.210.78:4317/v1/browser/gemini/current
97+```
98+
99+当前实际结果:
100+
101+- `data.final_message.raw_text` 是协议碎片
102+- `data.messages[0].text` 也是协议碎片
103+- 看不到 `conductor-ok-731`
104+
105+## 当前影响
106+
107+- `browser.gemini.send` 不能形成“发出消息 -> 回读可读回复”的稳定闭环
108+- 从 `conductor` 侧无法把 Gemini shell 页当成可靠的文本对话目标来验证
109+- 上层如果依赖 `GET /v1/browser/gemini/current` 获取最终回复,会把协议层噪声误当业务文本
110+- 这不是“Gemini 完全不可发”,而是“发送可用,回复提取失真”
111+
112+## 修复建议
113+
114+### 方案 A(推荐)
115+
116+修正 `plugins/baa-firefox/final-message.js` 对 Gemini shell 页的最终消息提取逻辑:
117+
118+- 明确区分 `StreamGenerate` / `batchexecute` 中的最终 assistant 文本与元数据帧
119+- 对 shell 页增加更严格的候选打分与过滤
120+- 不把纯数组/时间戳/ID 结构误判成最终 assistant 消息
121+
122+### 方案 B
123+
124+当协议解析拿不到稳定文本时,对 Gemini shell 页增加 DOM fallback:
125+
126+- 发送完成后回读当前页面最后一条 assistant 文本
127+- 仅在协议提取结果明显不是自然语言时启用
128+
129+### 方案 C
130+
131+在 `GET /v1/browser/gemini/current` 侧增加“非文本 final_message”保护:
132+
133+- 如果 `raw_text` 看起来是纯协议碎片,则不要把它直接作为 `messages[].text`
134+- 返回明确的 `unparsed_final_message` / `parse_status`,避免上层误用
135+
136+## 严重程度
137+
138+**High**
139+
140+Gemini shell 的发送能力已经暴露为正式 legacy helper 路由,但当前不能稳定拿到可读回复,等于闭环只完成了一半。
141+
142+## 发现时间
143+
144+`2026-03-30 by Codex`
145+
146+## 备注
147+
148+相关代码位置:
149+
150+- Gemini 响应解析与候选提取:[plugins/baa-firefox/final-message.js](/Users/george/code/baa-conductor/plugins/baa-firefox/final-message.js#L487)
151+- Gemini `browser.final_message` 上报入口:[plugins/baa-firefox/final-message.js](/Users/george/code/baa-conductor/plugins/baa-firefox/final-message.js#L869)
152+- conductor 接收 `browser.final_message`:[apps/conductor-daemon/src/firefox-ws.ts](/Users/george/code/baa-conductor/apps/conductor-daemon/src/firefox-ws.ts#L1232)
153+- Gemini legacy helper 路由:[apps/conductor-daemon/src/local-api.ts](/Users/george/code/baa-conductor/apps/conductor-daemon/src/local-api.ts#L596)
154+- 当前测试只覆盖 stubbed happy path,未覆盖 live shell 协议碎片场景:[apps/conductor-daemon/src/index.test.js](/Users/george/code/baa-conductor/apps/conductor-daemon/src/index.test.js#L3418)
+1,
-0
1@@ -15,6 +15,7 @@ bugs/
2
3 - `BUG-026`:[`BUG-026-artifact-repo-root-fallback-broken.md`](./BUG-026-artifact-repo-root-fallback-broken.md)
4 - `BUG-027`:[`BUG-027-startup-plugin-diagnostic-events-lost-before-ws-open.md`](./BUG-027-startup-plugin-diagnostic-events-lost-before-ws-open.md)
5+- `BUG-028`:[`BUG-028-gemini-shell-final-message-raw-protocol.md`](./BUG-028-gemini-shell-final-message-raw-protocol.md)
6 - `OPT-002`:[`OPT-002-executor-timeout.md`](./OPT-002-executor-timeout.md)
7 - `OPT-003`:[`OPT-003-policy-configurable.md`](./OPT-003-policy-configurable.md)
8 - `OPT-004`:[`OPT-004-final-message-claude-sse-fallback.md`](./OPT-004-final-message-claude-sse-fallback.md)