@@ -343,6 +343,7 @@ export class AgentServer {
343343 private rtkSavingsAttempted = false ;
344344 private questionRelayedToSlack = false ;
345345 private adapterEmittedTurnComplete = false ;
346+ private suppressAdapterTurnComplete = false ;
346347 private runUsage = new RunUsageAccumulator ( ) ;
347348 private detectedPrUrl : string | null = null ;
348349 // Reset per session. `evaluatedPrUrls` dedupes per URL; `prAttributionChain` serializes
@@ -368,6 +369,7 @@ export class AgentServer {
368369 private initializationPromise : Promise < void > | null = null ;
369370 private pendingEvents : Record < string , unknown > [ ] = [ ] ;
370371 private deliveredMessageIds = new Set < string > ( ) ;
372+ private pendingCompactContinuationMessageIds = new Set < string > ( ) ;
371373 private pendingPermissions = new Map <
372374 string ,
373375 {
@@ -913,18 +915,28 @@ export class AgentServer {
913915 typeof params . messageId === "string" && params . messageId
914916 ? params . messageId
915917 : undefined ;
918+ let retryCompactContinuation = false ;
916919 if ( messageId ) {
917920 if ( this . deliveredMessageIds . has ( messageId ) ) {
918- this . logger . info ( "Duplicate user_message delivery ignored" , {
919- messageId,
920- } ) ;
921- return { stopReason : "duplicate_delivery" , duplicate : true } ;
921+ if ( this . pendingCompactContinuationMessageIds . has ( messageId ) ) {
922+ retryCompactContinuation = true ;
923+ this . logger . info ( "Retrying pending compact continuation" , {
924+ messageId,
925+ } ) ;
926+ } else {
927+ this . logger . info ( "Duplicate user_message delivery ignored" , {
928+ messageId,
929+ } ) ;
930+ return { stopReason : "duplicate_delivery" , duplicate : true } ;
931+ }
932+ } else {
933+ this . deliveredMessageIds . add ( messageId ) ;
922934 }
923- this . deliveredMessageIds . add ( messageId ) ;
924935 if ( this . deliveredMessageIds . size > 500 ) {
925936 const oldest = this . deliveredMessageIds . values ( ) . next ( ) . value ;
926937 if ( oldest !== undefined ) {
927938 this . deliveredMessageIds . delete ( oldest ) ;
939+ this . pendingCompactContinuationMessageIds . delete ( oldest ) ;
928940 }
929941 }
930942 }
@@ -955,34 +967,53 @@ export class AgentServer {
955967 : { } ) ,
956968 } ;
957969
958- let result : PromptResponse ;
959- try {
960- result = await this . session . clientConnection . prompt ( {
961- sessionId : this . session . acpSessionId ,
962- prompt,
963- ...( Object . keys ( promptMeta ) . length > 0
964- ? { _meta : promptMeta }
965- : { } ) ,
970+ const manualCompactPrompt = isManualCompactPrompt ( prompt ) ;
971+ const acpSessionId = this . session . acpSessionId ;
972+ const continueAfterCompaction = ( ) : Promise < PromptResponse > =>
973+ this . promptWithUpstreamRetry ( {
974+ sessionId : acpSessionId ,
975+ prompt : [
976+ hiddenTextBlock (
977+ "Compaction is complete. Continue working on the task from the compacted context, following the user's instructions from the /compact command." ,
978+ ) ,
979+ ] ,
966980 } ) ;
967981
968- if (
969- result . stopReason === "end_turn" &&
970- isManualCompactPrompt ( prompt )
971- ) {
972- // `/compact` is an SDK-local command, so without a follow-up the
973- // cloud run reports completion before the model resumes the task.
974- this . recordTurnUsage ( result . usage ) ;
975- result = await this . promptWithUpstreamRetry ( {
982+ let compactCommandCompleted = retryCompactContinuation ;
983+ let result : PromptResponse ;
984+ this . suppressAdapterTurnComplete =
985+ manualCompactPrompt || retryCompactContinuation ;
986+ try {
987+ if ( retryCompactContinuation ) {
988+ result = await continueAfterCompaction ( ) ;
989+ if ( messageId ) {
990+ this . pendingCompactContinuationMessageIds . delete ( messageId ) ;
991+ }
992+ } else {
993+ result = await this . session . clientConnection . prompt ( {
976994 sessionId : this . session . acpSessionId ,
977- prompt : [
978- hiddenTextBlock (
979- "Compaction is complete. Continue working on the task from the compacted context, following the user's instructions from the /compact command." ,
980- ) ,
981- ] ,
995+ prompt,
996+ ...( Object . keys ( promptMeta ) . length > 0
997+ ? { _meta : promptMeta }
998+ : { } ) ,
982999 } ) ;
1000+
1001+ if ( result . stopReason === "end_turn" && manualCompactPrompt ) {
1002+ compactCommandCompleted = true ;
1003+ if ( messageId ) {
1004+ this . pendingCompactContinuationMessageIds . add ( messageId ) ;
1005+ }
1006+ // `/compact` is an SDK-local command, so without a follow-up the
1007+ // cloud run reports completion before the model resumes the task.
1008+ this . recordTurnUsage ( result . usage ) ;
1009+ result = await continueAfterCompaction ( ) ;
1010+ if ( messageId ) {
1011+ this . pendingCompactContinuationMessageIds . delete ( messageId ) ;
1012+ }
1013+ }
9831014 }
9841015 } catch ( error ) {
985- if ( messageId ) {
1016+ if ( messageId && ! compactCommandCompleted ) {
9861017 this . deliveredMessageIds . delete ( messageId ) ;
9871018 }
9881019 await this . session . logWriter . flushAll ( ) ;
@@ -995,6 +1026,8 @@ export class AgentServer {
9951026 throw error ;
9961027 }
9971028 return { stopReason : "error_recoverable" } ;
1029+ } finally {
1030+ this . suppressAdapterTurnComplete = false ;
9981031 }
9991032
10001033 this . logger . debug ( "User message completed" , {
@@ -1320,16 +1353,8 @@ export class AgentServer {
13201353
13211354 // Tap both streams to broadcast all ACP messages via SSE (mimics local transport)
13221355 this . adapterEmittedTurnComplete = false ;
1323- const onAcpMessage = ( message : unknown ) => {
1324- if ( isTurnCompleteNotification ( message ) ) {
1325- this . adapterEmittedTurnComplete = true ;
1326- }
1327- this . broadcastEvent ( {
1328- type : "notification" ,
1329- timestamp : new Date ( ) . toISOString ( ) ,
1330- notification : message ,
1331- } ) ;
1332- } ;
1356+ const onAcpMessage = ( message : unknown ) =>
1357+ this . handleAcpTransportMessage ( message ) ;
13331358
13341359 const tappedReadable = createTappedReadableStream (
13351360 acpConnection . clientStreams . readable as ReadableStream < Uint8Array > ,
@@ -4066,6 +4091,20 @@ ${signedCommitInstructions}${prLinkInstructions}${shellEfficiencyInstructions}
40664091 } ) ;
40674092 }
40684093
4094+ private handleAcpTransportMessage ( message : unknown ) : void {
4095+ if ( isTurnCompleteNotification ( message ) ) {
4096+ if ( this . suppressAdapterTurnComplete ) {
4097+ return ;
4098+ }
4099+ this . adapterEmittedTurnComplete = true ;
4100+ }
4101+ this . broadcastEvent ( {
4102+ type : "notification" ,
4103+ timestamp : new Date ( ) . toISOString ( ) ,
4104+ notification : message ,
4105+ } ) ;
4106+ }
4107+
40694108 private broadcastTurnComplete ( stopReason : string ) : void {
40704109 if ( ! this . session ) return ;
40714110 if ( this . adapterEmittedTurnComplete ) {
0 commit comments