修复: B-32+33 流式收尾清队列+回滚 running toolCall(走查 P0)

- B-32:四路径(onStreamTimeout in useAiStream / AiError case in useAiEvents / stopChat+approveToolCall catch in useAiSend)补 state.queue=[] 清队列——drainQueue 仅 AiCompleted 触发,此前三路径不清致生成中入队消息静默丢失。
- B-33:onStreamTimeout 合并单遍扫描回滚 status===running toolCall→rejected(B-260615-07 残留)——approveToolCall 乐观置 running,后端 hang 时看门狗只复位 streaming 不碰 toolCalls 致卡片永久骨架屏无重审入口,现回滚恢复。
批2 wjg2t6j8d,vue-tsc 0 err
This commit is contained in:
2026-06-15 05:30:06 +08:00
parent 8720b9424d
commit f93b7584ff
3 changed files with 18 additions and 7 deletions

View File

@@ -25,20 +25,28 @@ export function onStreamTimeout() {
state.streaming = false
state.generatingConvId = null
state.currentText = ''
state.queue = [] // B-32:超时收尾同步清队列,防生成中入队的消息静默丢失
clearStreamWatchdog()
// 文案区分:若已有 completed 工具调用,说明工具执行完后续回复被中断(常见为审批后
// 续生成卡住),引导用户继续;否则按纯流式中断处理。
// 数据源:state.messages[].toolCalls[].status,反向扫尾部提前退出(O(1) 均,同 findToolCall);
// 不复用 useAiEvents.findToolCall:本模块不依赖 useAiEvents(已下沉 nextMsgId 到 aiShared 破环),
// 且此处需遍历查找任一 completed 卡片而非按 id 定位,语义不同
// 一次性遍历同时做两件事(单次 O(n),不再分两遍扫):
// 1) 回滚 running 态工具调用 → rejected:approveToolCall 乐观置 running,后端 hang 时
// 看门狗触发本回调,若不回滚则卡片永久骨架屏(ToolCard:24 渲染 running 无审批按钮,无重审入口)。
// B-260615-07 残留。
// 2) 探测任一 completed 卡片以区分错误文案(工具已执行完后续回复中断 vs 纯流式中断)。
// 反向扫尾部提前退出(同 findToolCall 策略);不复用 useAiEvents.findToolCall:本模块
// 不依赖 useAiEvents(已下沉 nextMsgId 到 aiShared 破环),且此处需全量回滚 running 而非按 id 定位。
let hasCompletedTool = false
for (let i = state.messages.length - 1; i >= 0; i--) {
const toolCalls = state.messages[i].toolCalls
if (!toolCalls) continue
for (let j = 0; j < toolCalls.length; j++) {
if (toolCalls[j].status === 'completed') { hasCompletedTool = true; break }
const tc = toolCalls[j]
if (tc.status === 'running') {
// B-33:running 态无后续事件回流,置 rejected 释放骨架屏,恢复重审入口
tc.status = 'rejected'
} else if (tc.status === 'completed') {
hasCompletedTool = true
}
}
if (hasCompletedTool) break
}
const content = hasCompletedTool
? '⚠ 工具已执行完成,后续回复中断(长时间无数据流)。可点继续重试。'