@@ -104,7 +104,7 @@ import { buildHostLocalEventsPath } from '../../copilotCliEventsUri.js';
104104import { toolDataToDefinition } from './agentHostToolUtils.js' ;
105105import { IAgentHostUntitledProvisionalSessionService } from './agentHostUntitledProvisionalSessionService.js' ;
106106import { IAgentHostImportConversationStore } from './agentHostImportConversationStore.js' ;
107- import { activeTurnToProgress , BOOLEAN_TRUE_OPTION_ID , completedToolCallToEditParts , completedToolCallToSerialized , containsAutomaticReplyAnswer , convertProtocolAnswers , convertProtocolPlanReviewResult , createInputRequestCarousel , createInputRequestPlanReview , finalizeToolInvocation , formatTurnResponseDetails , getTerminalContent , getUrlInputRequestPresentation , isSubagentTool , makeAhpTerminalToolSessionId , messageAttachmentsToVariableData , messageToVariableData , parseAhpTerminalToolSessionId , rewriteAgentHostLinkTarget , stringOrMarkdownToString , systemNotificationToChatPart , toolCallAuthenticationServer , toolCallStateToInvocation , toolCallStateToPreparedInvocation , toolCallStateToStreamingInvocation , turnsToHistory , updateRunningToolSpecificData , updateStreamingToolInvocation , usageInfoToAutoModeResolution , usageInfoToChatUsage , usageInfoToQuotas , type IAgentHostToolInvocationOptions , type IToolCallFileEdit , type TurnModelLookup } from './stateToProgressAdapter.js' ;
107+ import { activeTurnToProgress , BOOLEAN_TRUE_OPTION_ID , completedToolCallToEditParts , completedToolCallToSerialized , containsAutomaticReplyAnswer , convertProtocolAnswers , convertProtocolPlanReviewResult , createInputRequestCarousel , createInputRequestPlanReview , finalizeToolInvocation , formatTurnResponseDetails , getTerminalContent , getUrlInputRequestPresentation , isSubagentTool , makeAhpTerminalToolSessionId , messageAttachmentsToVariableData , messageToVariableData , parseAhpTerminalToolSessionId , rewriteAgentHostLinkTarget , shouldObserveSubagentChat , stringOrMarkdownToString , systemNotificationToChatPart , toolCallAuthenticationServer , toolCallStateToInvocation , toolCallStateToPreparedInvocation , toolCallStateToStreamingInvocation , turnsToHistory , updateRunningToolSpecificData , updateStreamingToolInvocation , usageInfoToAutoModeResolution , usageInfoToChatUsage , usageInfoToQuotas , type IAgentHostToolInvocationOptions , type IToolCallFileEdit , type TurnModelLookup } from './stateToProgressAdapter.js' ;
108108import { resolveMcpServerAuthentication , agentHostMcpServerId , modelRequiresAgentAuthentication } from './agentHostAuth.js' ;
109109export { toolDataToDefinition } ;
110110
@@ -231,8 +231,8 @@ interface IObserveTurnOptions {
231231 * subagent tool calls already have observers so they aren't double-subscribed.
232232 */
233233interface ISubagentContext {
234- /** Tool call IDs already subscribed — prevents duplicate observers . */
235- readonly observedToolIds : Set < string > ;
234+ /** Active child-chat observers keyed by their spawning tool call . */
235+ readonly observations : DisposableMap < string > ;
236236}
237237
238238interface IOutputTerminalAttachment {
@@ -2967,7 +2967,7 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC
29672967 // Subagent observation context: dedups subagent tool calls so each is
29682968 // observed once.
29692969 const subagentContext : ISubagentContext = {
2970- observedToolIds : new Set < string > ( ) ,
2970+ observations : store . add ( new DisposableMap ( ) ) ,
29712971 } ;
29722972
29732973 // Per response part. Markdown / reasoning / tool calls each get a
@@ -3774,7 +3774,7 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC
37743774 return ;
37753775 }
37763776
3777- const isObserved = subagentContext . observedToolIds . has ( toolCallId ) ;
3777+ const isObserved = subagentContext . observations . has ( toolCallId ) ;
37783778 const currentData = invocation . toolSpecificData ?. kind === 'subagent' ? invocation . toolSpecificData : undefined ;
37793779 const prepared = toolCallStateToPreparedInvocation ( toolCall , opts . backendSession , this . _config . connectionAuthority , opts . sessionResource . authority ) ;
37803780 const protocolData = prepared . toolSpecificData ?. kind === 'subagent' ? prepared . toolSpecificData : undefined ;
@@ -3799,23 +3799,28 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC
37993799 invocation . notifyToolSpecificDataChanged ( ) ;
38003800 }
38013801
3802+ if ( isObserved && ! shouldObserveSubagentChat ( toolCall ) ) {
3803+ subagentContext . observations . deleteAndDispose ( toolCallId ) ;
3804+ return ;
3805+ }
38023806 if ( isObserved ) {
38033807 return ;
38043808 }
3805- if ( toolCall . status !== ToolCallStatus . Running && toolCall . status !== ToolCallStatus . Completed ) {
3809+ if ( ! shouldObserveSubagentChat ( toolCall ) ) {
38063810 return ;
38073811 }
38083812
38093813 const subagentData = invocation . toolSpecificData ;
38103814 if ( subagentData ?. kind !== 'subagent' ) {
38113815 return ;
38123816 }
3813- subagentContext . observedToolIds . add ( toolCallId ) ;
3817+ const observationStore = new DisposableStore ( ) ;
3818+ subagentContext . observations . set ( toolCallId , observationStore ) ;
38143819 subagentData . isActive = true ;
38153820 invocation . notifyToolSpecificDataChanged ( ) ;
38163821
38173822 const perInvocationCredits = observableValue < number > ( 'subagentInvocationCredits' , 0 ) ;
3818- store . add ( autorun ( reader => {
3823+ observationStore . add ( autorun ( reader => {
38193824 const total = perInvocationCredits . read ( reader ) ;
38203825 if ( total > 0 && invocation . toolSpecificData ?. kind === 'subagent' && invocation . toolSpecificData . credits !== total ) {
38213826 invocation . toolSpecificData . credits = total ;
@@ -3824,7 +3829,7 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC
38243829 } ) ) ;
38253830
38263831 const perInvocationModel = observableValue < string | undefined > ( 'subagentInvocationModel' , undefined ) ;
3827- store . add ( autorun ( reader => {
3832+ observationStore . add ( autorun ( reader => {
38283833 const modelName = perInvocationModel . read ( reader ) ;
38293834 if ( modelName && invocation . toolSpecificData ?. kind === 'subagent' && invocation . toolSpecificData . modelName !== modelName ) {
38303835 invocation . toolSpecificData . modelName = modelName ;
@@ -3835,7 +3840,7 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC
38353840 const rootInvocationId = opts . subAgentInvocationId ?? toolCallId ;
38363841 const childChatUri = subagentData . chatResource
38373842 || buildSubagentChatUri ( opts . backendSession . toString ( ) , toolCallId ) ;
3838- this . _observeSubagentSession ( opts . sessionResource , opts . backendSession , toolCallId , childChatUri , rootInvocationId , invocation , opts . sink , store , subagentContext , perInvocationCredits , perInvocationModel ) ;
3843+ this . _observeSubagentSession ( opts . sessionResource , opts . backendSession , toolCallId , childChatUri , rootInvocationId , invocation , opts . sink , observationStore , subagentContext , perInvocationCredits , perInvocationModel ) ;
38393844 }
38403845
38413846 /**
@@ -4692,7 +4697,7 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC
46924697 ) ) ;
46934698 } catch ( err ) {
46944699 // Remove from observed set so a later state change can retry
4695- subagentContext . observedToolIds . delete ( parentToolCallId ) ;
4700+ subagentContext . observations . deleteAndDispose ( parentToolCallId ) ;
46964701 this . _logService . warn ( `[AgentHost] Failed to subscribe to subagent chat: ${ childChatUri } ` , err ) ;
46974702 }
46984703 }
0 commit comments