Skip to content

fix(gemini): clear activeMsgIdRef on stop to prevent message filtering bug#1354

Merged
piorpua merged 1 commit intoiOfficeAI:mainfrom
BillionClaw:clawoss/fix/stop-button-resend-bug
Mar 17, 2026
Merged

fix(gemini): clear activeMsgIdRef on stop to prevent message filtering bug#1354
piorpua merged 1 commit intoiOfficeAI:mainfrom
BillionClaw:clawoss/fix/stop-button-resend-bug

Conversation

@BillionClaw
Copy link
Copy Markdown
Contributor

When clicking the stop button during a CLI conversation, the \ was not being reset. This caused subsequent messages to have their events incorrectly filtered because the ref still held the old message ID from the stopped request.

The bug manifested as the AI continuing to respond to the previous (stopped) question instead of the new one, because stream events for the new message were being filtered out.

Fixes #1303

…g bug

When clicking the stop button, the activeMsgIdRef was not being cleared.
This caused subsequent messages to have their events incorrectly filtered
because the ref still held the old message ID from the stopped request.

Fixes iOfficeAI#1303
@piorpua piorpua self-assigned this Mar 17, 2026
@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 17, 2026

Code Review:fix(gemini): clear activeMsgIdRef on stop to prevent message filtering bug (#1354)

变更概述

此 PR 仅修改 GeminiSendBox.tsx 中的 resetState 回调,在停止按钮触发时额外清空 activeMsgIdRef.current(置为 null)。涉及模块为 Gemini 对话的消息流状态管理逻辑。


方案评估

结论:✅ 方案合理

activeMsgIdRef 被用于过滤"非当前请求"产生的事件(防止旧请求的 thought 消息干扰新请求)。在 Agent/CLI 多轮对话场景中,setActiveMsgId 仅在用户主动发消息时调用,后端自动续接的轮次不会更新该 ref。若停止后 ref 仍保留旧 msg_id,下一轮后端自动续接的 thought 事件(携带新 msg_id)会被误过滤,导致思考进度等 UI 无响应。清空为 null 后,过滤条件 activeMsgIdRef.current && ... 短路为 false,所有新事件畅通无阻直到下一次 setActiveMsgId 调用。方案简洁正确,与现有架构一致。


问题清单

🔵 LOW — resetState 未同步更新 waitingResponseRef.current

文件src/renderer/pages/conversation/gemini/GeminiSendBox.tsx,第 385–395 行

问题代码

const resetState = useCallback(() => {
  setWaitingResponse(false);        // ← 异步 state 更新
  setStreamRunning(false);
  streamRunningRef.current = false; // ← ref 同步更新 ✓
  setHasActiveTools(false);
  hasActiveToolsRef.current = false; // ← ref 同步更新 ✓
  setThought({ subject: '', description: '' });
  hasContentInTurnRef.current = false;
  activeMsgIdRef.current = null;    // ← 本 PR 新增,ref 同步更新 ✓
}, []);

问题说明:文件其他位置(如 finish 事件处理、初始化)均采用 setXxx(false) + xxxRef.current = false 双写模式,以确保 ref 在下一个事件到来前就是最新值。但 resetStatewaitingResponseRef.current 没有同步置 false,仅靠 React 异步 setState,若在渲染周期之间有新事件到来,waitingResponseRef.current 读到的仍是旧值 true。本 PR 中新增的 activeMsgIdRef.current = null 处理方式反而是正确的,因此该不一致更为明显。

注意:这是本 PR 暴露出的已有缺陷,不是 PR 引入的新问题,不阻塞合并。

修复建议

const resetState = useCallback(() => {
  setWaitingResponse(false);
  waitingResponseRef.current = false;   // add this
  setStreamRunning(false);
  streamRunningRef.current = false;
  setHasActiveTools(false);
  hasActiveToolsRef.current = false;
  setThought({ subject: '', description: '' });
  hasContentInTurnRef.current = false;
  activeMsgIdRef.current = null;
}, []);

🔵 LOW — 缺少对应的回归测试

文件:无测试文件变更

问题说明:PR 修复了一个具体的竞态 bug(停止后 activeMsgIdRef 残留导致 agent 多轮 thought 被过滤),但没有添加对应的单元测试或集成测试。若未来 resetState 被误修改,该 bug 会无声复现。

修复建议:在 tests/unit/tests/integration/ 下为 useGeminiMessage hook 添加测试,验证调用 resetState()activeMsgIdRef.currentnull,以及后续事件不会被过滤。


汇总

# 严重级别 文件 问题
1 🔵 LOW GeminiSendBox.tsx:385 resetState 未同步更新 waitingResponseRef.current(已有缺陷,本 PR 更加凸显)
2 🔵 LOW (无测试文件) 缺少 activeMsgIdRef 清空逻辑的回归测试

结论

⚠️ 有条件批准 — 核心修复逻辑正确,方案合理,变更极小且聚焦。两个 LOW 问题均不阻塞合并,建议后续在 resetState 中补齐 waitingResponseRef.current = false 并补充测试用例。


本报告由本地 /pr-review 命令生成,包含完整项目上下文,无截断限制。

@piorpua piorpua merged commit 30cf56e into iOfficeAI:main Mar 17, 2026
piorpua pushed a commit that referenced this pull request Mar 17, 2026
… tests

- Add `waitingResponseRef.current = false` inside `resetState` to keep the
  ref in sync with the state update. Previously only `setWaitingResponse(false)`
  was called (async React update), leaving the ref stale until the next render.
  Every other write-site for waitingResponse already performs the double-write.

- Export `useGeminiMessage` so it can be unit-tested in isolation.

- Add three regression tests in `tests/unit/useGeminiMessage.dom.test.ts`:
  1. resetState() resets `running` to false
  2. resetState() clears activeMsgIdRef so thought events from new messages
     are no longer filtered
  3. Full lifecycle: old request → resetState → new request → correct filtering

- Include GeminiSendBox.tsx in vitest coverage tracking.

Follow-up to #1354
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 与cli问答时,点击停止按钮后,重发修正的问题,ai还是继续上个问题回复

2 participants