1- import type {
2- ChatHistoryItem ,
3- Session ,
4- ToolCallState ,
5- ToolStatus ,
6- } from "core/index.js" ;
1+ import type { Session , ToolCallState , ToolStatus } from "core/index.js" ;
72
83import { streamChatResponse } from "../stream/streamChatResponse.js" ;
94import { StreamCallbacks } from "../stream/streamChatResponse.types.js" ;
@@ -27,85 +22,45 @@ export async function streamChatResponseWithInterruption(
2722 // Set up periodic interruption checks
2823 const interruptionChecker = setInterval ( checkInterruption , 100 ) ;
2924
30- let currentStreamingItem : ChatHistoryItem | null = null ;
31-
3225 // Create callbacks to capture tool events
3326 const callbacks : StreamCallbacks = {
34- onContent : ( content : string ) => {
35- if ( ! currentStreamingItem ) {
36- currentStreamingItem = {
37- message : { role : "assistant" , content : "" } ,
38- contextItems : [ ] ,
39- } ;
40- state . session . history . push ( currentStreamingItem ) ;
41- }
42- currentStreamingItem . message . content =
43- ( currentStreamingItem . message . content as string ) + content ;
27+ onContent : ( _ : string ) => {
28+ // onContent is empty - doesn't update history during streaming
29+ // This is just for real-time display purposes
4430 } ,
45- onContentComplete : ( content : string ) => {
46- if ( currentStreamingItem ) {
47- currentStreamingItem . message . content = content ;
48- currentStreamingItem = null ;
49- } else {
50- // Add complete assistant message
51- state . session . history . push ( {
52- message : { role : "assistant" , content : content } ,
53- contextItems : [ ] ,
54- } ) ;
55- }
31+ onContentComplete : ( _ : string ) => {
32+ // Note: streamChatResponse already adds messages to history via handleToolCalls
33+ // so we don't need to add them here - this callback is just for notification
34+ // that content streaming is complete
5635 } ,
57- onToolStart : ( toolName : string , toolArgs ?: any ) => {
58- // If there was streaming content, finalize it first
59- if ( currentStreamingItem && currentStreamingItem . message . content ) {
60- currentStreamingItem = null ;
61- }
62-
63- // Always create a new assistant message for each tool call
64- const toolCallId = `tool_${ Date . now ( ) } _${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) } ` ;
65- const toolCallState : ToolCallState = {
66- toolCallId : toolCallId ,
67- toolCall : {
68- id : toolCallId ,
69- type : "function" ,
70- function : {
71- name : toolName ,
72- arguments : JSON . stringify ( toolArgs || { } ) ,
73- } ,
74- } ,
75- status : "calling" ,
76- parsedArgs : toolArgs ,
77- } ;
78-
79- state . session . history . push ( {
80- message : { role : "assistant" , content : "" } ,
81- contextItems : [ ] ,
82- toolCallStates : [ toolCallState ] ,
83- } ) ;
36+ onToolStart : ( __ : string , _ ?: any ) => {
37+ // Note: handleToolCalls already adds the tool call message to history
38+ // This callback is just for notification/UI updates
39+ // The tool call state is already created and added by handleToolCalls
8440 } ,
8541 onToolResult : ( result : string , toolName : string , status : ToolStatus ) => {
86- // Find and update the corresponding tool call state
42+ // Update only the tool call state status
43+ // The actual result is already added as a separate message by handleToolCalls
8744 for ( let i = state . session . history . length - 1 ; i >= 0 ; i -- ) {
8845 const item = state . session . history [ i ] ;
8946 if ( item . toolCallStates ) {
9047 const toolState = item . toolCallStates . find (
9148 ( ts : ToolCallState ) =>
92- ts . toolCall . function . name === toolName && ts . status === "calling" ,
49+ ts . toolCall . function . name === toolName &&
50+ ( ts . status === "calling" || ts . status === "generated" ) ,
9351 ) ;
9452 if ( toolState ) {
53+ // Only update the status, not the output
9554 toolState . status = status ;
96- toolState . output = [
97- {
98- content : result ,
99- name : `Tool Result: ${ toolName } ` ,
100- description : "Tool execution result" ,
101- } ,
102- ] ;
103- return ;
55+ break ;
10456 }
10557 }
10658 }
10759 } ,
10860 onToolError : ( error : string , toolName ?: string ) => {
61+ // Only update the tool call state to errored status when tool name is provided
62+ // The error message is already added as a separate tool result message
63+ // by handleToolCalls/preprocessStreamedToolCalls/executeStreamedToolCalls
10964 if ( toolName ) {
11065 // Find and update the corresponding tool call state
11166 for ( let i = state . session . history . length - 1 ; i >= 0 ; i -- ) {
@@ -114,39 +69,30 @@ export async function streamChatResponseWithInterruption(
11469 const toolState = item . toolCallStates . find (
11570 ( ts : ToolCallState ) =>
11671 ts . toolCall . function . name === toolName &&
117- ts . status === "calling" ,
72+ ( ts . status === "calling" || ts . status === "generated" ) ,
11873 ) ;
11974 if ( toolState ) {
75+ // Only update the status, not the output
12076 toolState . status = "errored" ;
121- toolState . output = [
122- {
123- content : error ,
124- name : `Tool Error: ${ toolName } ` ,
125- description : "Tool execution error" ,
126- } ,
127- ] ;
128- return ;
77+ break ;
12978 }
13079 }
13180 }
13281 }
133- // Generic error if tool not found
134- state . session . history . push ( {
135- message : { role : "system" , content : error } ,
136- contextItems : [ ] ,
137- } ) ;
13882 } ,
13983 onToolPermissionRequest : (
14084 toolName : string ,
14185 toolArgs : any ,
14286 requestId : string ,
87+ toolCallPreview ?: any [ ] ,
14388 ) => {
14489 // Set pending permission state
14590 state . pendingPermission = {
14691 toolName,
14792 toolArgs,
14893 requestId,
14994 timestamp : Date . now ( ) ,
95+ toolCallPreview,
15096 } ;
15197
15298 // Add a system message indicating permission is needed
@@ -160,6 +106,15 @@ export async function streamChatResponseWithInterruption(
160106
161107 // Don't wait here - the streamChatResponse will handle waiting
162108 } ,
109+ onSystemMessage : ( message : string ) => {
110+ state . session . history . push ( {
111+ message : {
112+ role : "system" ,
113+ content : message ,
114+ } ,
115+ contextItems : [ ] ,
116+ } ) ;
117+ } ,
163118 } ;
164119
165120 try {
@@ -173,10 +128,6 @@ export async function streamChatResponseWithInterruption(
173128 return response || "" ;
174129 } finally {
175130 clearInterval ( interruptionChecker ) ;
176- // Ensure any streaming message is finalized
177- if ( currentStreamingItem !== null ) {
178- currentStreamingItem = null ;
179- }
180131 }
181132}
182133
@@ -185,6 +136,7 @@ export interface PendingPermission {
185136 toolArgs : any ;
186137 requestId : string ;
187138 timestamp : number ;
139+ toolCallPreview ?: any [ ] ;
188140}
189141
190142export interface ServerState {
0 commit comments