@@ -134,12 +134,20 @@ export interface Logger {
134134 error : ( ...args : any [ ] ) => void ;
135135}
136136
137+ type AccumulatedUsage = {
138+ inputTokens : number ;
139+ outputTokens : number ;
140+ cachedReadTokens : number ;
141+ cachedWriteTokens : number ;
142+ } ;
143+
137144type Session = {
138145 query : Query ;
139146 input : Pushable < SDKUserMessage > ;
140147 cancelled : boolean ;
141148 permissionMode : PermissionMode ;
142149 settingsManager : SettingsManager ;
150+ accumulatedUsage : AccumulatedUsage ;
143151 configOptions : SessionConfigOption [ ] ;
144152} ;
145153
@@ -407,6 +415,14 @@ export class ClaudeAcpAgent implements Agent {
407415 }
408416
409417 this . sessions [ params . sessionId ] . cancelled = false ;
418+ this . sessions [ params . sessionId ] . accumulatedUsage = {
419+ inputTokens : 0 ,
420+ outputTokens : 0 ,
421+ cachedReadTokens : 0 ,
422+ cachedWriteTokens : 0 ,
423+ } ;
424+
425+ let lastAssistantTotalUsage : number | null = null ;
410426
411427 const { query, input } = this . sessions [ params . sessionId ] ;
412428
@@ -423,6 +439,11 @@ export class ClaudeAcpAgent implements Agent {
423439
424440 switch ( message . type ) {
425441 case "system" :
442+ if ( message . subtype === "compact_boundary" ) {
443+ // We don't know the exact size, but since we compacted,
444+ // we set it to zero. The client gets the exact size on the next message.
445+ lastAssistantTotalUsage = 0 ;
446+ }
426447 switch ( message . subtype ) {
427448 case "init" :
428449 break ;
@@ -447,6 +468,47 @@ export class ClaudeAcpAgent implements Agent {
447468 return { stopReason : "cancelled" } ;
448469 }
449470
471+ // Accumulate usage from this result
472+ const session = this . sessions [ params . sessionId ] ;
473+ session . accumulatedUsage . inputTokens += message . usage . input_tokens ;
474+ session . accumulatedUsage . outputTokens += message . usage . output_tokens ;
475+ session . accumulatedUsage . cachedReadTokens += message . usage . cache_read_input_tokens ;
476+ session . accumulatedUsage . cachedWriteTokens += message . usage . cache_creation_input_tokens ;
477+
478+ // Calculate context window size from modelUsage (minimum across all models used)
479+ const contextWindows = Object . values ( message . modelUsage ) . map ( ( m ) => m . contextWindow ) ;
480+ const contextWindowSize =
481+ contextWindows . length > 0 ? Math . min ( ...contextWindows ) : 200000 ;
482+
483+ // Send usage_update notification
484+ if ( lastAssistantTotalUsage !== null ) {
485+ await this . client . sessionUpdate ( {
486+ sessionId : params . sessionId ,
487+ update : {
488+ sessionUpdate : "usage_update" ,
489+ used : lastAssistantTotalUsage ,
490+ size : contextWindowSize ,
491+ cost : {
492+ amount : message . total_cost_usd ,
493+ currency : "USD" ,
494+ } ,
495+ } ,
496+ } ) ;
497+ }
498+
499+ // Build the usage response
500+ const usage : PromptResponse [ "usage" ] = {
501+ inputTokens : session . accumulatedUsage . inputTokens ,
502+ outputTokens : session . accumulatedUsage . outputTokens ,
503+ cachedReadTokens : session . accumulatedUsage . cachedReadTokens ,
504+ cachedWriteTokens : session . accumulatedUsage . cachedWriteTokens ,
505+ totalTokens :
506+ session . accumulatedUsage . inputTokens +
507+ session . accumulatedUsage . outputTokens +
508+ session . accumulatedUsage . cachedReadTokens +
509+ session . accumulatedUsage . cachedWriteTokens ,
510+ } ;
511+
450512 switch ( message . subtype ) {
451513 case "success" : {
452514 if ( message . result . includes ( "Please run /login" ) ) {
@@ -455,7 +517,7 @@ export class ClaudeAcpAgent implements Agent {
455517 if ( message . is_error ) {
456518 throw RequestError . internalError ( undefined , message . result ) ;
457519 }
458- return { stopReason : "end_turn" } ;
520+ return { stopReason : "end_turn" , usage } ;
459521 }
460522 case "error_during_execution" :
461523 if ( message . is_error ) {
@@ -464,7 +526,7 @@ export class ClaudeAcpAgent implements Agent {
464526 message . errors . join ( ", " ) || message . subtype ,
465527 ) ;
466528 }
467- return { stopReason : "end_turn" } ;
529+ return { stopReason : "end_turn" , usage } ;
468530 case "error_max_budget_usd" :
469531 case "error_max_turns" :
470532 case "error_max_structured_output_retries" :
@@ -474,7 +536,7 @@ export class ClaudeAcpAgent implements Agent {
474536 message . errors . join ( ", " ) || message . subtype ,
475537 ) ;
476538 }
477- return { stopReason : "max_turn_requests" } ;
539+ return { stopReason : "max_turn_requests" , usage } ;
478540 default :
479541 unreachable ( message , this . logger ) ;
480542 break ;
@@ -500,6 +562,16 @@ export class ClaudeAcpAgent implements Agent {
500562 break ;
501563 }
502564
565+ // Store latest assistant usage (excluding subagents)
566+ if ( ( message . message as any ) . usage && message . parent_tool_use_id === null ) {
567+ const messageWithUsage = message . message as unknown as SDKResultMessage ;
568+ lastAssistantTotalUsage =
569+ messageWithUsage . usage . input_tokens +
570+ messageWithUsage . usage . output_tokens +
571+ messageWithUsage . usage . cache_read_input_tokens +
572+ messageWithUsage . usage . cache_creation_input_tokens ;
573+ }
574+
503575 // Slash commands like /compact can generate invalid output... doesn't match
504576 // their own docs: https://docs.anthropic.com/en/docs/claude-code/sdk/sdk-slash-commands#%2Fcompact-compact-conversation-history
505577 if (
@@ -978,7 +1050,6 @@ export class ClaudeAcpAgent implements Agent {
9781050 const options : Options = {
9791051 systemPrompt,
9801052 settingSources : [ "user" , "project" , "local" ] ,
981- stderr : ( err ) => this . logger . error ( err ) ,
9821053 ...( maxThinkingTokens !== undefined && { maxThinkingTokens } ) ,
9831054 ...userProvidedOptions ,
9841055 // Override certain fields that must be controlled by ACP
@@ -1052,6 +1123,12 @@ export class ClaudeAcpAgent implements Agent {
10521123 cancelled : false ,
10531124 permissionMode,
10541125 settingsManager,
1126+ accumulatedUsage : {
1127+ inputTokens : 0 ,
1128+ outputTokens : 0 ,
1129+ cachedReadTokens : 0 ,
1130+ cachedWriteTokens : 0 ,
1131+ } ,
10551132 configOptions : [ ] ,
10561133 } ;
10571134
0 commit comments