Skip to content

Commit 885e8c3

Browse files
committed
refactor: centralize tool persistence logic in processToolResult
Improved separation of concerns by moving tool-specific behavior into processToolResult: - Added 'context' parameter to processToolResult (streaming vs historical) - loadHistoricalMessages now just passes context, doesn't know about specific tools - processToolResult decides what to do based on tool type and context - Cleaner abstraction: tool behavior is centralized in one place
1 parent ee69918 commit 885e8c3

File tree

1 file changed

+22
-13
lines changed

1 file changed

+22
-13
lines changed

src/utils/messages/StreamingMessageAggregator.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,9 @@ export class StreamingMessageAggregator {
219219
}
220220

221221
// Then, reconstruct derived state from the most recent assistant message
222-
// TODOs: only if there's an active stream (stream-scoped, only during reconnection)
223-
// agentStatus: always (persists across sessions)
222+
// Use "streaming" context if there's an active stream (reconnection), otherwise "historical"
223+
const context = hasActiveStream ? "streaming" : "historical";
224+
224225
const sortedMessages = [...messages].sort(
225226
(a, b) => (b.metadata?.historySequence ?? 0) - (a.metadata?.historySequence ?? 0)
226227
);
@@ -229,16 +230,11 @@ export class StreamingMessageAggregator {
229230
const lastAssistantMessage = sortedMessages.find((msg) => msg.role === "assistant");
230231

231232
if (lastAssistantMessage) {
232-
// Only process tool results from the most recent assistant message
233+
// Process all tool results from the most recent assistant message
234+
// processToolResult will decide what to do based on tool type and context
233235
for (const part of lastAssistantMessage.parts) {
234236
if (isDynamicToolPart(part) && part.state === "output-available") {
235-
// Reconstruct based on tool type and stream state
236-
const shouldReconstructTodos = part.toolName === "todo_write" && hasActiveStream;
237-
const shouldReconstructStatus = part.toolName === "status_set";
238-
239-
if (shouldReconstructTodos || shouldReconstructStatus) {
240-
this.processToolResult(part.toolName, part.input, part.output);
241-
}
237+
this.processToolResult(part.toolName, part.input, part.output, context);
242238
}
243239
}
244240
}
@@ -539,10 +535,21 @@ export class StreamingMessageAggregator {
539535
*
540536
* This is the single source of truth for updating state from tool results,
541537
* ensuring consistency whether processing live events or historical messages.
538+
*
539+
* @param toolName - Name of the tool that was called
540+
* @param input - Tool input arguments
541+
* @param output - Tool output result
542+
* @param context - Whether this is from live streaming or historical reload
542543
*/
543-
private processToolResult(toolName: string, input: unknown, output: unknown): void {
544+
private processToolResult(
545+
toolName: string,
546+
input: unknown,
547+
output: unknown,
548+
context: "streaming" | "historical"
549+
): void {
544550
// Update TODO state if this was a successful todo_write
545-
if (toolName === "todo_write" && hasSuccessResult(output)) {
551+
// TODOs are stream-scoped: only update during live streaming, not on historical reload
552+
if (toolName === "todo_write" && hasSuccessResult(output) && context === "streaming") {
546553
const args = input as { todos: TodoItem[] };
547554
// Only update if todos actually changed (prevents flickering from reference changes)
548555
if (!this.todosEqual(this.currentTodos, args.todos)) {
@@ -551,6 +558,7 @@ export class StreamingMessageAggregator {
551558
}
552559

553560
// Update agent status if this was a successful status_set
561+
// agentStatus persists: update both during streaming and on historical reload
554562
// Use output instead of input to get the truncated message
555563
if (toolName === "status_set" && hasSuccessResult(output)) {
556564
const result = output as Extract<StatusSetToolResult, { success: true }>;
@@ -584,7 +592,8 @@ export class StreamingMessageAggregator {
584592
(toolPart as DynamicToolPartAvailable).output = data.result;
585593

586594
// Process tool result to update derived state (todos, agentStatus, etc.)
587-
this.processToolResult(data.toolName, toolPart.input, data.result);
595+
// This is from a live stream, so use "streaming" context
596+
this.processToolResult(data.toolName, toolPart.input, data.result, "streaming");
588597
}
589598
this.invalidateCache();
590599
}

0 commit comments

Comments
 (0)