@@ -95,6 +95,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
9595 subscribeToConversationContextRequest ( )
9696 subscribeToWatchedFilesHandler ( )
9797 subscribeToClientToolInvokeEvent ( )
98+ subscribeToClientToolConfirmationEvent ( )
9899 }
99100
100101 private func subscribeToNotifications( ) {
@@ -136,6 +137,27 @@ public final class ChatService: ChatServiceType, ObservableObject {
136137 } ) . store ( in: & cancellables)
137138 }
138139
140+ private func subscribeToClientToolConfirmationEvent( ) {
141+ ClientToolHandlerImpl . shared. onClientToolConfirmationEvent. sink ( receiveValue: { [ weak self] ( request, completion) in
142+ guard let params = request. params, params. conversationId == self ? . conversationId else { return }
143+ let editAgentRounds : [ AgentRound ] = [
144+ AgentRound ( roundId: params. roundId,
145+ reply: " " ,
146+ toolCalls: [
147+ AgentToolCall ( id: params. toolCallId, name: params. name, status: . waitForConfirmation, invokeParams: params)
148+ ]
149+ )
150+ ]
151+ self ? . appendToolCallHistory ( turnId: params. turnId, editAgentRounds: editAgentRounds)
152+ self ? . pendingToolCallRequests [ params. toolCallId] = ToolCallRequest (
153+ requestId: request. id,
154+ turnId: params. turnId,
155+ roundId: params. roundId,
156+ toolCallId: params. toolCallId,
157+ completion: completion)
158+ } ) . store ( in: & cancellables)
159+ }
160+
139161 private func subscribeToClientToolInvokeEvent( ) {
140162 ClientToolHandlerImpl . shared. onClientToolInvokeEvent. sink ( receiveValue: { [ weak self] ( request, completion) in
141163 guard let params = request. params, params. conversationId == self ? . conversationId else { return }
@@ -154,15 +176,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
154176 return
155177 }
156178
157- let completed = copilotTool. invokeTool ( request, completion: completion, chatHistoryUpdater: self ? . appendToolCallHistory, contextProvider: self )
158- if !completed {
159- self ? . pendingToolCallRequests [ params. toolCallId] = ToolCallRequest (
160- requestId: request. id,
161- turnId: params. turnId,
162- roundId: params. roundId,
163- toolCallId: params. toolCallId,
164- completion: completion)
165- }
179+ copilotTool. invokeTool ( request, completion: completion, chatHistoryUpdater: self ? . appendToolCallHistory, contextProvider: self )
166180 } ) . store ( in: & cancellables)
167181 }
168182
@@ -225,11 +239,9 @@ public final class ChatService: ChatServiceType, ObservableObject {
225239 }
226240
227241 // Send the tool call result back to the server
228- if let toolCallRequest = self . pendingToolCallRequests [ toolCallId] , status == . completed , let result = payload {
242+ if let toolCallRequest = self . pendingToolCallRequests [ toolCallId] , status == . accepted {
229243 self . pendingToolCallRequests. removeValue ( forKey: toolCallId)
230- let toolResult = LanguageModelToolResult ( content: [
231- . init( value: result)
232- ] )
244+ let toolResult = LanguageModelToolConfirmationResult ( result: . Accept)
233245 let jsonResult = try ? JSONEncoder ( ) . encode ( toolResult)
234246 let jsonValue = ( try ? JSONDecoder ( ) . decode ( JSONValue . self, from: jsonResult ?? Data ( ) ) ) ?? JSONValue . null
235247 toolCallRequest. completion (
@@ -505,12 +517,27 @@ public final class ChatService: ChatServiceType, ObservableObject {
505517 private func handleProgressBegin( token: String , progress: ConversationProgressBegin ) {
506518 guard let workDoneToken = activeRequestId, workDoneToken == token else { return }
507519 conversationId = progress. conversationId
520+ let turnId = progress. turnId
508521
509522 Task {
510523 if var lastUserMessage = await memory. history. last ( where: { $0. role == . user } ) {
511524 lastUserMessage. clsTurnID = progress. turnId
512525 saveChatMessageToStorage ( lastUserMessage)
513526 }
527+
528+ /// Display an initial assistant message immediately after the user sends a message.
529+ /// This improves perceived responsiveness, especially in Agent Mode where the first
530+ /// ProgressReport may take long time.
531+ let message = ChatMessage (
532+ id: turnId,
533+ chatTabID: self . chatTabInfo. id,
534+ clsTurnID: turnId,
535+ role: . assistant,
536+ content: " "
537+ )
538+
539+ // will persist in resetOngoingRequest()
540+ await memory. appendMessage ( message)
514541 }
515542 }
516543
@@ -642,17 +669,18 @@ public final class ChatService: ChatServiceType, ObservableObject {
642669 // cancel all pending tool call requests
643670 for (_, request) in pendingToolCallRequests {
644671 pendingToolCallRequests. removeValue ( forKey: request. toolCallId)
645- request. completion ( AnyJSONRPCResponse ( id: request. requestId,
646- result: JSONValue . array ( [
647- JSONValue . null,
648- JSONValue . hash (
649- [
650- " code " : . number( - 32800 ) , // client cancelled
651- " message " : . string( " The client cancelled the tool call request \( request. toolCallId) " )
652- ] )
653- ] )
654- )
655- )
672+ let toolResult = LanguageModelToolConfirmationResult ( result: . Dismiss)
673+ let jsonResult = try ? JSONEncoder ( ) . encode ( toolResult)
674+ let jsonValue = ( try ? JSONDecoder ( ) . decode ( JSONValue . self, from: jsonResult ?? Data ( ) ) ) ?? JSONValue . null
675+ request. completion (
676+ AnyJSONRPCResponse (
677+ id: request. requestId,
678+ result: JSONValue . array ( [
679+ jsonValue,
680+ JSONValue . null
681+ ] )
682+ )
683+ )
656684 }
657685
658686 Task {
@@ -901,7 +929,7 @@ extension [ChatMessage] {
901929 if index + 1 < count {
902930 let nextMessage = self [ index + 1 ]
903931 if nextMessage. role == . assistant {
904- turn. response = nextMessage. content
932+ turn. response = nextMessage. content + extractContentFromEditAgentRounds ( nextMessage . editAgentRounds )
905933 index += 1
906934 }
907935 }
@@ -912,5 +940,14 @@ extension [ChatMessage] {
912940
913941 return turns
914942 }
943+
944+ private func extractContentFromEditAgentRounds( _ editAgentRounds: [ AgentRound ] ) -> String {
945+ var content = " "
946+ for round in editAgentRounds {
947+ if !round. reply. isEmpty {
948+ content += round. reply
949+ }
950+ }
951+ return content
952+ }
915953}
916-
0 commit comments