fix(chat): filter internal messages (NO_REPLY) in SSE final handler (#904) (#915)

This commit is contained in:
Kagura
2026-04-25 15:51:09 +08:00
committed by GitHub
parent 04e234bbde
commit ceb537f7bd
3 changed files with 68 additions and 0 deletions

View File

@@ -2082,6 +2082,24 @@ export const useChatStore = create<ChatState>((set, get) => ({
if (finalMsg) {
const normalizedFinalMessage = normalizeStreamingMessage(finalMsg) as RawMessage;
const updates = collectToolUpdates(normalizedFinalMessage, resolvedState);
// Filter out internal-only final responses (NO_REPLY, HEARTBEAT_OK, etc.)
// before adding to messages. Without this guard, the internal token appears
// briefly in the UI until loadHistory replaces the message list — and if the
// quiet-mode reload is debounced away, the token can stay visible permanently.
if (isInternalMessage(normalizedFinalMessage)) {
set({
streamingText: '',
streamingMessage: null,
sending: false,
activeRunId: null,
pendingFinal: false,
streamingTools: [],
pendingToolImages: [],
});
clearHistoryPoll();
void get().loadHistory(true);
break;
}
if (isToolResultRole(normalizedFinalMessage.role)) {
// Resolve file path from the streaming assistant message's matching tool call
const currentStreamForPath = get().streamingMessage as RawMessage | null;

View File

@@ -9,6 +9,7 @@ import {
getToolCallFilePath,
hasErrorRecoveryTimer,
hasNonToolAssistantContent,
isInternalMessage,
isToolOnlyMessage,
isToolResultRole,
makeAttachedFile,
@@ -81,6 +82,24 @@ export function handleRuntimeEventState(
if (finalMsg) {
const normalizedFinalMessage = normalizeStreamingMessage(finalMsg) as RawMessage;
const updates = collectToolUpdates(normalizedFinalMessage, resolvedState);
// Filter out internal-only final responses (NO_REPLY, HEARTBEAT_OK, etc.)
// before adding to messages. Without this guard, the internal token appears
// briefly in the UI until loadHistory replaces the message list — and if the
// quiet-mode reload is debounced away, the token can stay visible permanently.
if (isInternalMessage(normalizedFinalMessage)) {
set({
streamingText: '',
streamingMessage: null,
sending: false,
activeRunId: null,
pendingFinal: false,
streamingTools: [],
pendingToolImages: [],
});
clearHistoryPoll();
void get().loadHistory(true);
break;
}
if (isToolResultRole(normalizedFinalMessage.role)) {
// Resolve file path from the streaming assistant message's matching tool call
const currentStreamForPath = get().streamingMessage as RawMessage | null;