@@ -95,6 +95,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
95
95
subscribeToConversationContextRequest ( )
96
96
subscribeToWatchedFilesHandler ( )
97
97
subscribeToClientToolInvokeEvent ( )
98
+ subscribeToClientToolConfirmationEvent ( )
98
99
}
99
100
100
101
private func subscribeToNotifications( ) {
@@ -136,6 +137,27 @@ public final class ChatService: ChatServiceType, ObservableObject {
136
137
} ) . store ( in: & cancellables)
137
138
}
138
139
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
+
139
161
private func subscribeToClientToolInvokeEvent( ) {
140
162
ClientToolHandlerImpl . shared. onClientToolInvokeEvent. sink ( receiveValue: { [ weak self] ( request, completion) in
141
163
guard let params = request. params, params. conversationId == self ? . conversationId else { return }
@@ -154,15 +176,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
154
176
return
155
177
}
156
178
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 )
166
180
} ) . store ( in: & cancellables)
167
181
}
168
182
@@ -225,11 +239,9 @@ public final class ChatService: ChatServiceType, ObservableObject {
225
239
}
226
240
227
241
// 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 {
229
243
self . pendingToolCallRequests. removeValue ( forKey: toolCallId)
230
- let toolResult = LanguageModelToolResult ( content: [
231
- . init( value: result)
232
- ] )
244
+ let toolResult = LanguageModelToolConfirmationResult ( result: . Accept)
233
245
let jsonResult = try ? JSONEncoder ( ) . encode ( toolResult)
234
246
let jsonValue = ( try ? JSONDecoder ( ) . decode ( JSONValue . self, from: jsonResult ?? Data ( ) ) ) ?? JSONValue . null
235
247
toolCallRequest. completion (
@@ -505,12 +517,27 @@ public final class ChatService: ChatServiceType, ObservableObject {
505
517
private func handleProgressBegin( token: String , progress: ConversationProgressBegin ) {
506
518
guard let workDoneToken = activeRequestId, workDoneToken == token else { return }
507
519
conversationId = progress. conversationId
520
+ let turnId = progress. turnId
508
521
509
522
Task {
510
523
if var lastUserMessage = await memory. history. last ( where: { $0. role == . user } ) {
511
524
lastUserMessage. clsTurnID = progress. turnId
512
525
saveChatMessageToStorage ( lastUserMessage)
513
526
}
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)
514
541
}
515
542
}
516
543
@@ -642,17 +669,18 @@ public final class ChatService: ChatServiceType, ObservableObject {
642
669
// cancel all pending tool call requests
643
670
for (_, request) in pendingToolCallRequests {
644
671
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
+ )
656
684
}
657
685
658
686
Task {
@@ -901,7 +929,7 @@ extension [ChatMessage] {
901
929
if index + 1 < count {
902
930
let nextMessage = self [ index + 1 ]
903
931
if nextMessage. role == . assistant {
904
- turn. response = nextMessage. content
932
+ turn. response = nextMessage. content + extractContentFromEditAgentRounds ( nextMessage . editAgentRounds )
905
933
index += 1
906
934
}
907
935
}
@@ -912,5 +940,14 @@ extension [ChatMessage] {
912
940
913
941
return turns
914
942
}
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
+ }
915
953
}
916
-
0 commit comments