@@ -31,6 +31,7 @@ public protocol ChatServiceType {
3131 model: String ? ,
3232 modelProviderName: String ? ,
3333 agentMode: Bool ,
34+ customChatModeId: String ? ,
3435 userLanguage: String ? ,
3536 turnId: String ?
3637 ) async throws
@@ -318,6 +319,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
318319 model: String ? = nil ,
319320 modelProviderName: String ? = nil ,
320321 agentMode: Bool = false ,
322+ customChatModeId: String ? = nil ,
321323 userLanguage: String ? = nil ,
322324 turnId: String ? = nil
323325 ) async throws {
@@ -423,14 +425,17 @@ public final class ChatService: ChatServiceType, ObservableObject {
423425 model: model,
424426 modelProviderName: modelProviderName,
425427 agentMode: agentMode,
428+ customChatModeId: customChatModeId,
426429 userLanguage: userLanguage,
427430 turnId: currentTurnId,
428431 skillSet: validSkillSet
429432 )
430433
431434 self . lastUserRequest = request
432435 self . skillSet = validSkillSet
433- try await sendConversationRequest ( request)
436+ if let response = try await sendConversationRequest ( request) {
437+ await handleConversationCreateResponse ( response)
438+ }
434439 }
435440
436441 private func createConversationRequest(
@@ -442,6 +447,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
442447 model: String ? = nil ,
443448 modelProviderName: String ? = nil ,
444449 agentMode: Bool = false ,
450+ customChatModeId: String ? = nil ,
445451 userLanguage: String ? = nil ,
446452 turnId: String ? = nil ,
447453 skillSet: [ ConversationSkill ]
@@ -467,10 +473,22 @@ public final class ChatService: ChatServiceType, ObservableObject {
467473 model: model,
468474 modelProviderName: modelProviderName,
469475 agentMode: agentMode,
476+ customChatModeId: customChatModeId,
470477 userLanguage: userLanguage,
471478 turnId: turnId
472479 )
473480 }
481+
482+ private func handleConversationCreateResponse( _ response: ConversationCreateResponse ) async {
483+ await memory. mutateHistory { history in
484+ if let index = history. firstIndex ( where: { $0. id == response. turnId && $0. role. isAssistant } ) {
485+ history [ index] . modelName = response. modelName
486+ history [ index] . billingMultiplier = response. billingMultiplier
487+
488+ self . saveChatMessageToStorage ( history [ index] )
489+ }
490+ }
491+ }
474492
475493 public func sendAndWait( _ id: String , content: String ) async throws -> String {
476494 try await send ( id, content: content, skillSet: [ ] , references: [ ] )
@@ -535,6 +553,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
535553 model: model != nil ? model : lastUserRequest. model,
536554 modelProviderName: modelProviderName,
537555 agentMode: lastUserRequest. agentMode,
556+ customChatModeId: lastUserRequest. customChatModeId,
538557 userLanguage: lastUserRequest. userLanguage,
539558 turnId: id
540559 )
@@ -652,8 +671,13 @@ public final class ChatService: ChatServiceType, ObservableObject {
652671
653672 private func handleProgressBegin( token: String , progress: ConversationProgressBegin ) {
654673 guard let workDoneToken = activeRequestId, workDoneToken == token else { return }
655- conversationId = progress. conversationId
674+ // Only update conversationId for main turns, not subagent turns
675+ // Subagent turns have their own conversation ID which should not replace the parent
676+ if progress. parentTurnId == nil {
677+ conversationId = progress. conversationId
678+ }
656679 let turnId = progress. turnId
680+ let parentTurnId = progress. parentTurnId
657681
658682 Task {
659683 if var lastUserMessage = await memory. history. last ( where: { $0. role == . user } ) {
@@ -677,10 +701,17 @@ public final class ChatService: ChatServiceType, ObservableObject {
677701 /// Display an initial assistant message immediately after the user sends a message.
678702 /// This improves perceived responsiveness, especially in Agent Mode where the first
679703 /// ProgressReport may take long time.
680- let message = ChatMessage ( assistantMessageWithId: turnId, chatTabID: chatTabInfo. id, turnStatus: . inProgress)
704+ /// Skip creating a new message for subturns - they will be merged into the parent turn
705+ if parentTurnId == nil {
706+ let message = ChatMessage (
707+ assistantMessageWithId: turnId,
708+ chatTabID: chatTabInfo. id,
709+ turnStatus: . inProgress
710+ )
681711
682- // will persist in resetOngoingRequest()
683- await memory. appendMessage ( message)
712+ // will persist in resetOngoingRequest()
713+ await memory. appendMessage ( message)
714+ }
684715 }
685716 }
686717
@@ -694,6 +725,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
694725 var references : [ ConversationReference ] = [ ]
695726 var steps : [ ConversationProgressStep ] = [ ]
696727 var editAgentRounds : [ AgentRound ] = [ ]
728+ let parentTurnId = progress. parentTurnId
697729
698730 if let reply = progress. reply {
699731 content = reply
@@ -711,15 +743,15 @@ public final class ChatService: ChatServiceType, ObservableObject {
711743 editAgentRounds = progressAgentRounds
712744 }
713745
714- if content. isEmpty && references. isEmpty && steps. isEmpty && editAgentRounds. isEmpty {
746+ if content. isEmpty && references. isEmpty && steps. isEmpty && editAgentRounds. isEmpty && parentTurnId == nil {
715747 return
716748 }
717749
718- // create immutable copies
719750 let messageContent = content
720751 let messageReferences = references
721752 let messageSteps = steps
722753 let messageAgentRounds = editAgentRounds
754+ let messageParentTurnId = parentTurnId
723755
724756 Task {
725757 let message = ChatMessage (
@@ -729,10 +761,10 @@ public final class ChatService: ChatServiceType, ObservableObject {
729761 references: messageReferences,
730762 steps: messageSteps,
731763 editAgentRounds: messageAgentRounds,
764+ parentTurnId: messageParentTurnId,
732765 turnStatus: . inProgress
733766 )
734767
735- // will persist in resetOngoingRequest()
736768 await memory. appendMessage ( message)
737769 }
738770 }
@@ -801,10 +833,16 @@ public final class ChatService: ChatServiceType, ObservableObject {
801833 }
802834 } else {
803835 Task {
836+ var clsErrorMessage = CLSError . message
837+ if CLSError . code == ConversationErrorCode . toolRoundExceedError. rawValue {
838+ // TODO: Remove this after `Continue` is supported.
839+ clsErrorMessage = HardCodedToolRoundExceedErrorMessage
840+ }
841+
804842 let errorMessage = ChatMessage (
805843 errorMessageWithId: progress. turnId,
806844 chatTabID: chatTabInfo. id,
807- errorMessages: [ CLSError . message ]
845+ errorMessages: [ clsErrorMessage ]
808846 )
809847 // will persist in resetOngoingRequest()
810848 await memory. appendMessage ( errorMessage)
@@ -874,6 +912,20 @@ public final class ChatService: ChatServiceType, ObservableObject {
874912 history [ lastIndex] . editAgentRounds [ i] . toolCalls![ j] . status = . cancelled
875913 }
876914 }
915+
916+ // Cancel tool calls in subagent rounds
917+ if let subAgentRounds = history [ lastIndex] . editAgentRounds [ i] . subAgentRounds {
918+ for k in 0 ..< subAgentRounds. count {
919+ if let toolCalls = subAgentRounds [ k] . toolCalls {
920+ for l in 0 ..< toolCalls. count {
921+ if toolCalls [ l] . status == . running
922+ || toolCalls [ l] . status == . waitForConfirmation {
923+ history [ lastIndex] . editAgentRounds [ i] . subAgentRounds![ k] . toolCalls![ l] . status = . cancelled
924+ }
925+ }
926+ }
927+ }
928+ }
877929 }
878930
879931 if history [ lastIndex] . codeReviewRound != nil ,
@@ -897,14 +949,14 @@ public final class ChatService: ChatServiceType, ObservableObject {
897949 }
898950 }
899951
900- private func sendConversationRequest( _ request: ConversationRequest ) async throws {
952+ private func sendConversationRequest( _ request: ConversationRequest ) async throws -> ConversationCreateResponse ? {
901953 guard !isReceivingMessage else { throw CancellationError ( ) }
902954 isReceivingMessage = true
903955 requestType = . conversation
904956
905957 do {
906958 if let conversationId = conversationId {
907- try await conversationProvider?
959+ return try await conversationProvider?
908960 . createTurn (
909961 with: conversationId,
910962 request: request,
@@ -922,7 +974,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
922974 requestWithTurns. turns = turns
923975 }
924976
925- try await conversationProvider? . createConversation ( requestWithTurns, workspaceURL: getWorkspaceURL ( ) )
977+ return try await conversationProvider? . createConversation ( requestWithTurns, workspaceURL: getWorkspaceURL ( ) )
926978 }
927979 } catch {
928980 resetOngoingRequest ( with: . error)
@@ -952,6 +1004,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
9521004public final class SharedChatService {
9531005 public var chatTemplates : [ ChatTemplate ] ? = nil
9541006 public var chatAgents : [ ChatAgent ] ? = nil
1007+ public var conversationModes : [ ConversationMode ] ? = nil
9551008 private let conversationProvider : ConversationServiceProvider ?
9561009
9571010 public static let shared = SharedChatService . service ( )
@@ -980,6 +1033,19 @@ public final class SharedChatService {
9801033 return nil
9811034 }
9821035
1036+ public func loadConversationModes( ) async -> [ ConversationMode ] ? {
1037+ do {
1038+ if let modes = ( try await conversationProvider? . modes ( ) ) {
1039+ self . conversationModes = modes
1040+ return modes
1041+ }
1042+ } catch {
1043+ // handle error if desired
1044+ }
1045+
1046+ return nil
1047+ }
1048+
9831049 public func copilotModels( ) async -> [ CopilotModel ] {
9841050 guard let models = try ? await conversationProvider? . models ( ) else { return [ ] }
9851051 return models
0 commit comments