@@ -572,34 +572,17 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
572572 }
573573 let lastContextWindowSize = this . session . lastContextWindowSize ;
574574
575- const supportsTerminalOutput =
576- (
577- this . clientCapabilities ?. _meta as
578- | ClientCapabilities [ "_meta" ]
579- | undefined
580- ) ?. terminal_output === true ;
581-
582- const context = {
583- session : this . session ,
584- sessionId : params . sessionId ,
585- client : this . client ,
586- toolUseCache : this . toolUseCache ,
587- toolUseStreamCache : this . toolUseStreamCache ,
588- fileContentCache : this . fileContentCache ,
589- enrichedReadCache : this . enrichedReadCache ,
590- logger : this . logger ,
591- supportsTerminalOutput,
592- streamedAssistantBlocks : {
593- textIds : new Set < string > ( ) ,
594- thinkingIds : new Set < string > ( ) ,
595- } ,
596- } ;
575+ const context = this . buildMessageContext ( params . sessionId ) ;
597576
598577 // Wait for the idle drainer to release the query. Our user message was
599578 // already pushed above, so its echo is what unblocks the drainer's pending
600579 // `query.next()`; the drainer hands that message back to us here rather than
601- // consuming it, so `promptReplayed` still flips below.
602- let carriedMessage = await this . settleIdleDrainStop ( ) ;
580+ // consuming it, so `promptReplayed` still flips below. Skip the await on the
581+ // common path where no drainer is running so a plain prompt pays no
582+ // microtask hop.
583+ let carriedMessage = this . idleDrain
584+ ? await this . settleIdleDrainStop ( )
585+ : undefined ;
603586
604587 try {
605588 while ( true ) {
@@ -1106,22 +1089,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
11061089 }
11071090
11081091 case "tool_progress" : {
1109- await this . client . sessionUpdate ( {
1110- sessionId : params . sessionId ,
1111- update : {
1112- sessionUpdate : "tool_call_update" ,
1113- toolCallId : message . tool_use_id ,
1114- status : "in_progress" ,
1115- _meta : {
1116- claudeCode : {
1117- toolName : message . tool_name ,
1118- toolResponse : {
1119- elapsedTimeSeconds : message . elapsed_time_seconds ,
1120- } ,
1121- } ,
1122- } satisfies ToolUpdateMeta ,
1123- } ,
1124- } ) ;
1092+ await this . emitToolProgress ( params . sessionId , message ) ;
11251093 break ;
11261094 }
11271095 case "rate_limit_event" : {
@@ -1315,6 +1283,61 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
13151283 return state . handoff ;
13161284 }
13171285
1286+ /**
1287+ * Build the per-turn message-handler context shared by the prompt turn loop
1288+ * and the idle drainer. Fresh `streamedAssistantBlocks` Sets per call so each
1289+ * turn dedupes its own streamed blocks independently.
1290+ */
1291+ private buildMessageContext ( sessionId : string ) : MessageHandlerContext {
1292+ const supportsTerminalOutput =
1293+ (
1294+ this . clientCapabilities ?. _meta as
1295+ | ClientCapabilities [ "_meta" ]
1296+ | undefined
1297+ ) ?. terminal_output === true ;
1298+ return {
1299+ session : this . session ,
1300+ sessionId,
1301+ client : this . client ,
1302+ toolUseCache : this . toolUseCache ,
1303+ toolUseStreamCache : this . toolUseStreamCache ,
1304+ fileContentCache : this . fileContentCache ,
1305+ enrichedReadCache : this . enrichedReadCache ,
1306+ logger : this . logger ,
1307+ supportsTerminalOutput,
1308+ streamedAssistantBlocks : {
1309+ textIds : new Set < string > ( ) ,
1310+ thinkingIds : new Set < string > ( ) ,
1311+ } ,
1312+ } ;
1313+ }
1314+
1315+ /**
1316+ * Forward a `tool_progress` SDK message to the client as an in-progress
1317+ * `tool_call_update`. Shared by the prompt turn loop and the idle drainer.
1318+ */
1319+ private async emitToolProgress (
1320+ sessionId : string ,
1321+ message : Extract < SDKMessage , { type : "tool_progress" } > ,
1322+ ) : Promise < void > {
1323+ await this . client . sessionUpdate ( {
1324+ sessionId,
1325+ update : {
1326+ sessionUpdate : "tool_call_update" ,
1327+ toolCallId : message . tool_use_id ,
1328+ status : "in_progress" ,
1329+ _meta : {
1330+ claudeCode : {
1331+ toolName : message . tool_name ,
1332+ toolResponse : {
1333+ elapsedTimeSeconds : message . elapsed_time_seconds ,
1334+ } ,
1335+ } ,
1336+ } satisfies ToolUpdateMeta ,
1337+ } ,
1338+ } ) ;
1339+ }
1340+
13181341 /**
13191342 * Pump the SDK query stream between turns.
13201343 *
@@ -1340,27 +1363,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
13401363 // `this.session` out from under us; binding here keeps this loop scoped to
13411364 // the query it started on, which will end (iterator done) on that refresh.
13421365 const query = this . session . query ;
1343- const supportsTerminalOutput =
1344- (
1345- this . clientCapabilities ?. _meta as
1346- | ClientCapabilities [ "_meta" ]
1347- | undefined
1348- ) ?. terminal_output === true ;
1349- const context : MessageHandlerContext = {
1350- session : this . session ,
1351- sessionId,
1352- client : this . client ,
1353- toolUseCache : this . toolUseCache ,
1354- toolUseStreamCache : this . toolUseStreamCache ,
1355- fileContentCache : this . fileContentCache ,
1356- enrichedReadCache : this . enrichedReadCache ,
1357- logger : this . logger ,
1358- supportsTerminalOutput,
1359- streamedAssistantBlocks : {
1360- textIds : new Set < string > ( ) ,
1361- thinkingIds : new Set < string > ( ) ,
1362- } ,
1363- } ;
1366+ const context = this . buildMessageContext ( sessionId ) ;
13641367
13651368 const state : IdleDrainState = { stop : false , done : Promise . resolve ( ) } ;
13661369 this . idleDrain = state ;
@@ -1465,22 +1468,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
14651468 resetStreamDedupe ( ) ;
14661469 return ;
14671470 case "tool_progress" :
1468- await this . client . sessionUpdate ( {
1469- sessionId : context . sessionId ,
1470- update : {
1471- sessionUpdate : "tool_call_update" ,
1472- toolCallId : message . tool_use_id ,
1473- status : "in_progress" ,
1474- _meta : {
1475- claudeCode : {
1476- toolName : message . tool_name ,
1477- toolResponse : {
1478- elapsedTimeSeconds : message . elapsed_time_seconds ,
1479- } ,
1480- } ,
1481- } satisfies ToolUpdateMeta ,
1482- } ,
1483- } ) ;
1471+ await this . emitToolProgress ( context . sessionId , message ) ;
14841472 return ;
14851473 default :
14861474 // auth_status, rate_limit_event, prompt_suggestion, tool_use_summary,
0 commit comments