fix(chat): thinking execution graph (#880)
This commit is contained in:
@@ -2,12 +2,10 @@ import { invokeIpc } from '@/lib/api-client';
|
||||
import { hostApiFetch } from '@/lib/host-api';
|
||||
import { useGatewayStore } from '@/stores/gateway';
|
||||
import {
|
||||
clearHistoryPoll,
|
||||
enrichWithCachedImages,
|
||||
enrichWithToolResultFiles,
|
||||
getLatestOptimisticUserMessage,
|
||||
getMessageText,
|
||||
hasNonToolAssistantContent,
|
||||
isInternalMessage,
|
||||
isToolResultRole,
|
||||
loadMissingPreviews,
|
||||
@@ -160,6 +158,18 @@ export function createHistoryActions(
|
||||
return toMs(msg.timestamp) >= userMsTs;
|
||||
};
|
||||
|
||||
// If we're sending but haven't received streaming events, check
|
||||
// whether the loaded history reveals assistant activity (tool calls,
|
||||
// narration, etc.). Setting pendingFinal surfaces the execution
|
||||
// graph / activity indicator in the UI.
|
||||
//
|
||||
// Note: we intentionally do NOT set sending=false here. Run
|
||||
// completion is exclusively signalled by the Gateway's phase
|
||||
// 'completed' event (handled in gateway.ts) or by receiving a
|
||||
// 'final' streaming event (handled in runtime-event-handlers.ts).
|
||||
// Attempting to infer completion from message history is fragile
|
||||
// and leads to premature sending=false during server-side tool
|
||||
// execution.
|
||||
if (isSendingNow && !pendingFinal) {
|
||||
const hasRecentAssistantActivity = [...filteredMessages].reverse().some((msg) => {
|
||||
if (msg.role !== 'assistant') return false;
|
||||
@@ -169,25 +179,6 @@ export function createHistoryActions(
|
||||
set({ pendingFinal: true });
|
||||
}
|
||||
}
|
||||
|
||||
// If pendingFinal, check whether the AI produced a final text response.
|
||||
// Only finalize when the candidate is the very last message in the
|
||||
// history — intermediate assistant messages (narration + tool_use) are
|
||||
// followed by tool-result messages and must NOT be treated as the
|
||||
// completed response, otherwise `pendingFinal` is cleared too early
|
||||
// and the streaming reply bubble never renders.
|
||||
if (pendingFinal || get().pendingFinal) {
|
||||
const recentAssistant = [...filteredMessages].reverse().find((msg) => {
|
||||
if (msg.role !== 'assistant') return false;
|
||||
if (!hasNonToolAssistantContent(msg)) return false;
|
||||
return isAfterUserMsg(msg);
|
||||
});
|
||||
const lastMsg = filteredMessages[filteredMessages.length - 1];
|
||||
if (recentAssistant && lastMsg === recentAssistant) {
|
||||
clearHistoryPoll();
|
||||
set({ sending: false, activeRunId: null, pendingFinal: false });
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user