@@ -6,6 +6,7 @@ import * as path from 'path'
66import * as vscode from 'vscode'
77import { Event as VSCodeEvent , Uri , workspace , window , ViewColumn , Position , Selection } from 'vscode'
88import { EditorContextExtractor } from '../../editor/context/extractor'
9+ import { ConversationTracker } from '../../storages/conversationTracker'
910import { ChatSessionStorage } from '../../storages/chatSession'
1011import { Messenger , MessengerResponseType , StaticTextResponseType } from './messenger/messenger'
1112import {
@@ -101,6 +102,7 @@ import { FsWriteParams } from '../../tools/fsWrite'
101102import { tempDirPath } from '../../../shared/filesystemUtilities'
102103import { Database } from '../../../shared/db/chatDb/chatDb'
103104import { TabBarController } from './tabBarController'
105+ import { sleep } from '../../../shared'
104106
105107export interface ChatControllerMessagePublishers {
106108 readonly processPromptChatMessage : MessagePublisher < PromptMessage >
@@ -407,9 +409,25 @@ export class ChatController {
407409
408410 private async processStopResponseMessage ( message : StopResponseMessage ) {
409411 const session = this . sessionStorage . getSession ( message . tabID )
412+ const wasInAgenticLoop = session . agenticLoopInProgress
410413 session . tokenSource . cancel ( )
414+ console . log ( 'process stop has been triggered' )
415+ session . setAgenticLoopInProgress ( false )
416+ session . setToolUseWithError ( undefined )
417+
418+ // Mark any active triggers as completed when stopping the response
419+ const triggerEvents = this . triggerEventsStorage . getTriggerEventsByTabID ( message . tabID )
420+ if ( triggerEvents && triggerEvents . length > 0 ) {
421+ const conversationTracker = ConversationTracker . getInstance ( )
422+ triggerEvents . forEach ( ( event ) => {
423+ conversationTracker . markTriggerCompleted ( event . id )
424+ } )
425+ }
426+
427+ wasInAgenticLoop && ( await sleep ( 1000 ) )
428+
411429 this . messenger . sendEmptyMessage ( message . tabID , '' , undefined )
412- this . chatHistoryStorage . getTabHistory ( message . tabID ) . clearRecentHistory ( )
430+ // this.chatHistoryStorage.getTabHistory(message.tabID).clearRecentHistory()
413431 }
414432
415433 private async processTriggerTabIDReceived ( message : TriggerTabIDReceived ) {
@@ -465,6 +483,13 @@ export class ChatController {
465483 }
466484
467485 private async processTabCloseMessage ( message : TabClosedMessage ) {
486+ // First cancel any active triggers to stop ongoing operations
487+ const conversationTracker = ConversationTracker . getInstance ( )
488+ conversationTracker . cancelTabTriggers ( message . tabID )
489+
490+ // Then clear all triggers to release resources
491+ conversationTracker . clearTabTriggers ( message . tabID )
492+
468493 this . sessionStorage . deleteSession ( message . tabID )
469494 this . chatHistoryStorage . deleteHistory ( message . tabID )
470495 this . triggerEventsStorage . removeTabEvents ( message . tabID )
@@ -680,7 +705,7 @@ export class ChatController {
680705 this . editorContextExtractor
681706 . extractContextForTrigger ( 'ChatMessage' )
682707 . then ( async ( context ) => {
683- const triggerID = randomUUID ( )
708+ const triggerID = message . triggerId ?? randomUUID ( )
684709 this . triggerEventsStorage . addTriggerEvent ( {
685710 id : triggerID ,
686711 tabID : message . tabID ,
@@ -690,10 +715,16 @@ export class ChatController {
690715 } )
691716 this . messenger . sendAsyncEventProgress ( tabID , true , '' )
692717 const session = this . sessionStorage . getSession ( tabID )
718+
719+ // Check if the session has been cancelled before proceeding
720+ if ( session . tokenSource . token . isCancellationRequested ) {
721+ getLogger ( ) . debug ( `Tool execution cancelled for tabID: ${ tabID } ` )
722+ return
723+ }
724+
693725 const toolUseWithError = session . toolUseWithError
694726 if ( ! toolUseWithError || ! toolUseWithError . toolUse || ! toolUseWithError . toolUse . input ) {
695727 // Turn off AgentLoop flag if there's no tool use
696- this . sessionStorage . setAgentLoopInProgress ( tabID , false )
697728 return
698729 }
699730 session . setToolUseWithError ( undefined )
@@ -716,14 +747,33 @@ export class ChatController {
716747 try {
717748 await ToolUtils . validate ( tool )
718749
719- const chatStream = new ChatStream ( this . messenger , tabID , triggerID , toolUse , {
720- requiresAcceptance : false ,
721- } )
750+ // Get the cancellation token from the session
751+ const cancellationToken = session . tokenSource . token
752+
753+ // Pass the cancellation token to ChatStream
754+ const chatStream = new ChatStream (
755+ this . messenger ,
756+ tabID ,
757+ triggerID ,
758+ toolUse ,
759+ { requiresAcceptance : false } ,
760+ undefined ,
761+ undefined ,
762+ cancellationToken
763+ )
764+
722765 if ( tool . type === ToolType . FsWrite && toolUse . toolUseId ) {
723766 const backup = await tool . tool . getBackup ( )
724767 session . setFsWriteBackup ( toolUse . toolUseId , backup )
725768 }
726- const output = await ToolUtils . invoke ( tool , chatStream )
769+
770+ // Check again if cancelled before invoking the tool
771+ if ( cancellationToken . isCancellationRequested ) {
772+ getLogger ( ) . debug ( `Tool execution cancelled before invoke for tabID: ${ tabID } ` )
773+ return
774+ }
775+
776+ const output = await ToolUtils . invoke ( tool , chatStream , cancellationToken )
727777 ToolUtils . validateOutput ( output )
728778
729779 toolResults . push ( {
@@ -985,7 +1035,17 @@ export class ChatController {
9851035
9861036 // Turn off AgentLoop flag in case of exception
9871037 if ( tabID ) {
988- this . sessionStorage . setAgentLoopInProgress ( tabID , false )
1038+ const session = this . sessionStorage . getSession ( tabID )
1039+ session . setAgenticLoopInProgress ( false )
1040+
1041+ // Mark any active triggers as completed when there's an exception
1042+ const triggerEvents = this . triggerEventsStorage . getTriggerEventsByTabID ( tabID )
1043+ if ( triggerEvents && triggerEvents . length > 0 ) {
1044+ const conversationTracker = ConversationTracker . getInstance ( )
1045+ triggerEvents . forEach ( ( event ) => {
1046+ conversationTracker . markTriggerCompleted ( event . id )
1047+ } )
1048+ }
9891049 }
9901050
9911051 this . messenger . sendErrorMessage ( errorMessage , tabID , requestID )
@@ -1171,22 +1231,33 @@ export class ChatController {
11711231
11721232 private async processPromptMessageAsNewThread ( message : PromptMessage ) {
11731233 const session = this . sessionStorage . getSession ( message . tabID )
1234+ // If there's an existing conversation, ensure we dispose the previous token
1235+ if ( session . agenticLoopInProgress ) {
1236+ session . disposeTokenSource ( )
1237+ }
1238+
1239+ // Create a fresh token for this new conversation
1240+ session . createNewTokenSource ( )
1241+ session . setAgenticLoopInProgress ( true )
11741242 session . clearListOfReadFiles ( )
11751243 session . setShowDiffOnFileWrite ( false )
11761244 this . editorContextExtractor
11771245 . extractContextForTrigger ( 'ChatMessage' )
11781246 . then ( async ( context ) => {
11791247 const triggerID = randomUUID ( )
1248+
1249+ // Register the trigger ID with the token for cancellation tracking
1250+ const conversationTracker = ConversationTracker . getInstance ( )
1251+ conversationTracker . registerTrigger ( triggerID , session . tokenSource , message . tabID )
1252+ console . log ( 'conversation tracker:' , conversationTracker . getTokenForTrigger ( triggerID ) )
1253+
11801254 this . triggerEventsStorage . addTriggerEvent ( {
11811255 id : triggerID ,
11821256 tabID : message . tabID ,
11831257 message : message . message ,
11841258 type : 'chat_message' ,
11851259 context,
11861260 } )
1187-
1188- this . messenger . sendAsyncEventProgress ( message . tabID , true , '' )
1189-
11901261 await this . generateResponse (
11911262 {
11921263 message : message . message ?? '' ,
@@ -1381,16 +1452,6 @@ export class ChatController {
13811452 }
13821453
13831454 const tabID = triggerEvent . tabID
1384- if ( this . sessionStorage . isAgentLoopInProgress ( tabID ) ) {
1385- // If a response is already in progress, stop it first
1386- const stopResponseMessage : StopResponseMessage = {
1387- tabID : tabID ,
1388- }
1389- await this . processStopResponseMessage ( stopResponseMessage )
1390- }
1391-
1392- // Ensure AgentLoop flag is set to true during response generation
1393- this . sessionStorage . setAgentLoopInProgress ( tabID , true )
13941455
13951456 const credentialsState = await AuthUtil . instance . getChatAuthState ( )
13961457
@@ -1493,6 +1554,7 @@ export class ChatController {
14931554 session . setContext ( triggerPayload . context )
14941555 }
14951556 this . messenger . sendInitalStream ( tabID , triggerID )
1557+ this . messenger . sendAsyncEventProgress ( tabID , true , '' )
14961558 this . telemetryHelper . setConversationStreamStartTime ( tabID )
14971559 if ( isSsoConnection ( AuthUtil . instance . conn ) ) {
14981560 const { $metadata, generateAssistantResponseResponse } = await session . chatSso ( request )
@@ -1527,13 +1589,8 @@ export class ChatController {
15271589 } metadata: ${ inspect ( response . $metadata , { depth : 12 } ) } `
15281590 )
15291591 await this . messenger . sendAIResponse ( response , session , tabID , triggerID , triggerPayload , chatHistory )
1530-
1531- // Turn off AgentLoop flag after sending the AI response
1532- this . sessionStorage . setAgentLoopInProgress ( tabID , false )
15331592 } catch ( e : any ) {
15341593 this . telemetryHelper . recordMessageResponseError ( triggerPayload , tabID , getHttpStatusCode ( e ) ?? 0 )
1535- // Turn off AgentLoop flag in case of exception
1536- this . sessionStorage . setAgentLoopInProgress ( tabID , false )
15371594 // clears session, record telemetry before this call
15381595 this . processException ( e , tabID )
15391596 }
0 commit comments