@@ -191,6 +191,8 @@ import {
191191    MAX_OVERALL_CHARACTERS , 
192192    FSREAD_MEMORY_BANK_MAX_PER_FILE , 
193193    FSREAD_MEMORY_BANK_MAX_TOTAL , 
194+     MID_LOOP_COMPACTION_HANDOFF_PROMPT , 
195+     COMPACTION_PROMPT , 
194196}  from  './constants/constants' 
195197import  { 
196198    AgenticChatError , 
@@ -943,6 +945,11 @@ export class AgenticChatController implements ChatHandlers {
943945
944946        const  compactIds  =  session . getAllDeferredCompactMessageIds ( ) 
945947        await  this . #invalidateCompactCommand( params . tabId ,  compactIds ) 
948+         // Set compactionDeclined flag if there were pending compaction requests 
949+         // This prevents endless compaction warning loops when user declines compaction once 
950+         if  ( compactIds . length  >  0 )  { 
951+             session . compactionDeclined  =  true 
952+         } 
946953        session . rejectAllDeferredToolExecutions ( new  ToolApprovalException ( 'Command ignored: new prompt' ,  false ) ) 
947954        await  this . #invalidateAllShellCommands( params . tabId ,  session ) 
948955
@@ -978,6 +985,10 @@ export class AgenticChatController implements ChatHandlers {
978985                session . abortRequest ( ) 
979986                const  compactIds  =  session . getAllDeferredCompactMessageIds ( ) 
980987                await  this . #invalidateCompactCommand( params . tabId ,  compactIds ) 
988+                 // Set compactionDeclined flag if there were pending compaction requests 
989+                 if  ( compactIds . length  >  0 )  { 
990+                     session . compactionDeclined  =  true 
991+                 } 
981992                void  this . #invalidateAllShellCommands( params . tabId ,  session ) 
982993                session . rejectAllDeferredToolExecutions ( new  CancellationError ( 'user' ) ) 
983994
@@ -1087,7 +1098,7 @@ export class AgenticChatController implements ChatHandlers {
10871098            } 
10881099
10891100            // Result Handling - This happens only once 
1090-             return  await  this . #handleFinalResult( 
1101+             const   result   =  await  this . #handleFinalResult( 
10911102                finalResult , 
10921103                session , 
10931104                params . tabId , 
@@ -1096,6 +1107,13 @@ export class AgenticChatController implements ChatHandlers {
10961107                isNewConversation , 
10971108                chatResultStream 
10981109            ) 
1110+ 
1111+             // Reset compactionDeclined flag after successful completion 
1112+             if  ( session . compactionDeclined )  { 
1113+                 session . compactionDeclined  =  false 
1114+             } 
1115+ 
1116+             return  result 
10991117        }  catch  ( err )  { 
11001118            // HACK: the chat-client needs to have a partial event with the associated messageId sent before it can accept the final result. 
11011119            // Without this, the `working` indicator never goes away. 
@@ -1167,25 +1185,29 @@ export class AgenticChatController implements ChatHandlers {
11671185    /** 
11681186     * Prepares the initial request input for the chat prompt 
11691187     */ 
1170-     #getCompactionRequestInput( session : ChatSessionService ) : ChatCommandInput  { 
1188+     #getCompactionRequestInput( session : ChatSessionService ,   toolResults ?:  any [ ] ) : ChatCommandInput  { 
11711189        this . #debug( 'Preparing compaction request input' ) 
11721190        // Get profileArn from the service manager if available 
11731191        const  profileArn  =  this . #serviceManager?. getActiveProfileArn ( ) 
11741192        const  requestInput  =  this . #triggerContext. getCompactionChatCommandInput ( 
11751193            profileArn , 
11761194            this . #getTools( session ) , 
11771195            session . modelId , 
1178-             this . #origin
1196+             this . #origin, 
1197+             toolResults 
11791198        ) 
11801199        return  requestInput 
11811200    } 
11821201
11831202    /** 
11841203     * Runs the compaction, making requests and processing tool uses until completion 
11851204     */ 
1186-     #shouldCompact( currentRequestCount : number ) : boolean  { 
1187-         if  ( currentRequestCount  >  COMPACTION_CHARACTER_THRESHOLD )  { 
1188-             this . #debug( `Current request total character count is: ${ currentRequestCount }  , prompting user to compact` ) 
1205+     #shouldCompact( currentRequestCount : number ,  session : ChatSessionService ) : boolean  { 
1206+         const  EFFECTIVE_COMPACTION_THRESHOLD  =  COMPACTION_CHARACTER_THRESHOLD  -  COMPACTION_PROMPT . length 
1207+         if  ( currentRequestCount  >  EFFECTIVE_COMPACTION_THRESHOLD  &&  ! session . compactionDeclined )  { 
1208+             this . #debug( 
1209+                 `Current request total character count is: ${ currentRequestCount }  , prompting user to compact (threshold: ${ EFFECTIVE_COMPACTION_THRESHOLD }  )` 
1210+             ) 
11891211            return  true 
11901212        }  else  { 
11911213            return  false 
@@ -1368,6 +1390,10 @@ export class AgenticChatController implements ChatHandlers {
13681390        let  currentRequestCount  =  0 
13691391        const  pinnedContext  =  additionalContext ?. filter ( item  =>  item . pinned ) 
13701392
1393+         // Store initial non-empty prompt for compaction handoff 
1394+         const  initialPrompt  = 
1395+             initialRequestInput . conversationState ?. currentMessage ?. userInputMessage ?. content ?. trim ( )  ||  '' 
1396+ 
13711397        metric . recordStart ( ) 
13721398        this . logSystemInformation ( ) 
13731399        while  ( true )  { 
@@ -1432,6 +1458,63 @@ export class AgenticChatController implements ChatHandlers {
14321458            this . #llmRequestStartTime =  Date . now ( ) 
14331459            // Phase 3: Request Execution 
14341460            currentRequestInput  =  sanitizeRequestInput ( currentRequestInput ) 
1461+ 
1462+             if  ( this . #shouldCompact( currentRequestCount ,  session ) )  { 
1463+                 this . #features. logging . info ( 
1464+                     `Entering mid-loop compaction at iteration ${ iterationCount }   with ${ currentRequestCount }   characters` 
1465+                 ) 
1466+                 this . #telemetryController. emitMidLoopCompaction ( 
1467+                     currentRequestCount , 
1468+                     iterationCount , 
1469+                     this . #features. runtime . serverInfo . version  ??  '' 
1470+                 ) 
1471+                 const  messageId  =  this . #getMessageIdForCompact( uuid ( ) ) 
1472+                 const  confirmationResult  =  this . #processCompactConfirmation( messageId ,  currentRequestCount ) 
1473+                 const  cachedButtonBlockId  =  await  chatResultStream . writeResultBlock ( confirmationResult ) 
1474+                 await  this . waitForCompactApproval ( messageId ,  chatResultStream ,  cachedButtonBlockId ,  session ) 
1475+ 
1476+                 // Run compaction 
1477+                 const  toolResults  = 
1478+                     currentRequestInput . conversationState ?. currentMessage ?. userInputMessage ?. userInputMessageContext 
1479+                         ?. toolResults  ||  [ ] 
1480+                 const  compactionRequestInput  =  this . #getCompactionRequestInput( session ,  toolResults ) 
1481+                 const  compactionResult  =  await  this . #runCompaction( 
1482+                     compactionRequestInput , 
1483+                     session , 
1484+                     metric , 
1485+                     chatResultStream , 
1486+                     tabId , 
1487+                     promptId , 
1488+                     CompactHistoryActionType . Nudge , 
1489+                     session . conversationId , 
1490+                     token , 
1491+                     documentReference 
1492+                 ) 
1493+ 
1494+                 if  ( ! compactionResult . success )  { 
1495+                     this . #features. logging . error ( `Compaction failed: ${ compactionResult . error }  ` ) 
1496+                     return  compactionResult 
1497+                 } 
1498+ 
1499+                 // Show compaction summary to user before continuing 
1500+                 await  chatResultStream . writeResultBlock ( { 
1501+                     type : 'answer' , 
1502+                     body :
1503+                         ( compactionResult . data ?. chatResult . body  ||  '' )  + 
1504+                         '\n\nConversation history has been compacted successfully!' , 
1505+                     messageId : uuid ( ) , 
1506+                 } ) 
1507+ 
1508+                 currentRequestInput  =  this . #updateRequestInputWithToolResults( 
1509+                     currentRequestInput , 
1510+                     [ ] , 
1511+                     MID_LOOP_COMPACTION_HANDOFF_PROMPT  +  initialPrompt 
1512+                 ) 
1513+                 shouldDisplayMessage  =  false 
1514+                 this . #features. logging . info ( `Completed mid-loop compaction, restarting loop with handoff prompt` ) 
1515+                 continue 
1516+             } 
1517+ 
14351518            // Note: these logs are very noisy, but contain information redacted on the backend. 
14361519            this . #debug( 
14371520                `generateAssistantResponse/SendMessage Request: ${ JSON . stringify ( currentRequestInput ,  this . #imageReplacer,  2 ) }  ` 
@@ -1660,7 +1743,7 @@ export class AgenticChatController implements ChatHandlers {
16601743            currentRequestInput  =  this . #updateRequestInputWithToolResults( currentRequestInput ,  toolResults ,  content ) 
16611744        } 
16621745
1663-         if  ( this . #shouldCompact( currentRequestCount ) )  { 
1746+         if  ( this . #shouldCompact( currentRequestCount ,   session ) )  { 
16641747            this . #telemetryController. emitCompactNudge ( 
16651748                currentRequestCount , 
16661749                this . #features. runtime . serverInfo . version  ??  '' 
0 commit comments