@@ -17,6 +17,8 @@ import {
1717 type SDKResultMessage ,
1818 type SettingSource ,
1919 type SDKUserMessage ,
20+ ModelUsage ,
21+ NonNullableUsage ,
2022} from "@anthropic-ai/claude-agent-sdk" ;
2123import {
2224 ApprovalRequestId ,
@@ -272,78 +274,73 @@ function asRuntimeItemId(value: string): RuntimeItemId {
272274 return RuntimeItemId . make ( value ) ;
273275}
274276
275- function maxClaudeContextWindowFromModelUsage ( modelUsage : unknown ) : number | undefined {
276- if ( ! modelUsage || typeof modelUsage !== "object" ) {
277- return undefined ;
278- }
277+ function maxClaudeContextWindowFromModelUsage (
278+ modelUsage : Record < string , ModelUsage > | undefined ,
279+ ) : number | undefined {
280+ if ( ! modelUsage ) return undefined ;
279281
280282 let maxContextWindow : number | undefined ;
281- for ( const value of Object . values ( modelUsage as Record < string , unknown > ) ) {
282- if ( ! value || typeof value !== "object" ) {
283- continue ;
284- }
285- const contextWindow = ( value as { contextWindow ?: unknown } ) . contextWindow ;
286- if (
287- typeof contextWindow !== "number" ||
288- ! Number . isFinite ( contextWindow ) ||
289- contextWindow <= 0
290- ) {
291- continue ;
292- }
283+ for ( const value of Object . values ( modelUsage ) ) {
284+ const contextWindow = value . contextWindow ;
293285 maxContextWindow = Math . max ( maxContextWindow ?? 0 , contextWindow ) ;
294286 }
295287
296288 return maxContextWindow ;
297289}
298290
299291function normalizeClaudeTokenUsage (
300- usage : unknown ,
292+ value : NonNullableUsage | undefined ,
301293 contextWindow ?: number ,
302294) : ThreadTokenUsageSnapshot | undefined {
303- if ( ! usage || typeof usage !== "object" ) {
295+ if ( ! value || typeof value !== "object" ) {
304296 return undefined ;
305297 }
306298
307- const record = usage as Record < string , unknown > ;
308- const directUsedTokens =
309- typeof record . total_tokens === "number" && Number . isFinite ( record . total_tokens )
310- ? record . total_tokens
311- : undefined ;
299+ const usage = value as Record < string , unknown > ;
312300 const inputTokens =
313- ( typeof record . input_tokens === "number" && Number . isFinite ( record . input_tokens )
314- ? record . input_tokens
301+ ( typeof usage . input_tokens === "number" && Number . isFinite ( usage . input_tokens )
302+ ? usage . input_tokens
315303 : 0 ) +
316- ( typeof record . cache_creation_input_tokens === "number" &&
317- Number . isFinite ( record . cache_creation_input_tokens )
318- ? record . cache_creation_input_tokens
304+ ( typeof usage . cache_creation_input_tokens === "number" &&
305+ Number . isFinite ( usage . cache_creation_input_tokens )
306+ ? usage . cache_creation_input_tokens
319307 : 0 ) +
320- ( typeof record . cache_read_input_tokens === "number" &&
321- Number . isFinite ( record . cache_read_input_tokens )
322- ? record . cache_read_input_tokens
308+ ( typeof usage . cache_read_input_tokens === "number" &&
309+ Number . isFinite ( usage . cache_read_input_tokens )
310+ ? usage . cache_read_input_tokens
323311 : 0 ) ;
324312 const outputTokens =
325- typeof record . output_tokens === "number" && Number . isFinite ( record . output_tokens )
326- ? record . output_tokens
313+ typeof usage . output_tokens === "number" && Number . isFinite ( usage . output_tokens )
314+ ? usage . output_tokens
327315 : 0 ;
328- const derivedUsedTokens = inputTokens + outputTokens ;
329- const usedTokens = directUsedTokens ?? ( derivedUsedTokens > 0 ? derivedUsedTokens : undefined ) ;
330- if ( usedTokens === undefined || usedTokens <= 0 ) {
316+ const derivedTotalProcessedTokens = inputTokens + outputTokens ;
317+ const totalProcessedTokens =
318+ ( typeof usage . total_tokens === "number" && Number . isFinite ( usage . total_tokens )
319+ ? usage . total_tokens
320+ : undefined ) ?? ( derivedTotalProcessedTokens > 0 ? derivedTotalProcessedTokens : undefined ) ;
321+ if ( totalProcessedTokens === undefined || totalProcessedTokens <= 0 ) {
331322 return undefined ;
332323 }
333324
325+ const maxTokens =
326+ typeof contextWindow === "number" && Number . isFinite ( contextWindow ) && contextWindow > 0
327+ ? contextWindow
328+ : undefined ;
329+ const usedTokens =
330+ maxTokens !== undefined ? Math . min ( totalProcessedTokens , maxTokens ) : totalProcessedTokens ;
331+
334332 return {
335333 usedTokens,
336334 lastUsedTokens : usedTokens ,
335+ ...( totalProcessedTokens > usedTokens ? { totalProcessedTokens } : { } ) ,
337336 ...( inputTokens > 0 ? { inputTokens } : { } ) ,
338337 ...( outputTokens > 0 ? { outputTokens } : { } ) ,
339- ...( typeof contextWindow === "number" && Number . isFinite ( contextWindow ) && contextWindow > 0
340- ? { maxTokens : contextWindow }
341- : { } ) ,
342- ...( typeof record . tool_uses === "number" && Number . isFinite ( record . tool_uses )
343- ? { toolUses : record . tool_uses }
338+ ...( maxTokens !== undefined ? { maxTokens } : { } ) ,
339+ ...( typeof usage . tool_uses === "number" && Number . isFinite ( usage . tool_uses )
340+ ? { toolUses : usage . tool_uses }
344341 : { } ) ,
345- ...( typeof record . duration_ms === "number" && Number . isFinite ( record . duration_ms )
346- ? { durationMs : record . duration_ms }
342+ ...( typeof usage . duration_ms === "number" && Number . isFinite ( usage . duration_ms )
343+ ? { durationMs : usage . duration_ms }
347344 : { } ) ,
348345 } ;
349346}
@@ -1328,8 +1325,6 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
13281325 errorMessage ?: string ,
13291326 result ?: SDKResultMessage ,
13301327 ) {
1331- const resultUsage =
1332- result ?. usage && typeof result . usage === "object" ? { ...result . usage } : undefined ;
13331328 const resultContextWindow = maxClaudeContextWindowFromModelUsage ( result ?. modelUsage ) ;
13341329 if ( resultContextWindow !== undefined ) {
13351330 context . lastKnownContextWindow = resultContextWindow ;
@@ -1341,9 +1336,11 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
13411336 // Instead, use the last known context-window-accurate usage from task_progress
13421337 // events and treat the accumulated total as totalProcessedTokens.
13431338 const accumulatedSnapshot = normalizeClaudeTokenUsage (
1344- resultUsage ,
1339+ result ?. usage ,
13451340 resultContextWindow ?? context . lastKnownContextWindow ,
13461341 ) ;
1342+ const accumulatedTotalProcessedTokens =
1343+ accumulatedSnapshot ?. totalProcessedTokens ?? accumulatedSnapshot ?. usedTokens ;
13471344 const lastGoodUsage = context . lastKnownTokenUsage ;
13481345 const maxTokens = resultContextWindow ?? context . lastKnownContextWindow ;
13491346 const usageSnapshot : ThreadTokenUsageSnapshot | undefined = lastGoodUsage
@@ -1352,8 +1349,10 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
13521349 ...( typeof maxTokens === "number" && Number . isFinite ( maxTokens ) && maxTokens > 0
13531350 ? { maxTokens }
13541351 : { } ) ,
1355- ...( accumulatedSnapshot && accumulatedSnapshot . usedTokens > lastGoodUsage . usedTokens
1356- ? { totalProcessedTokens : accumulatedSnapshot . usedTokens }
1352+ ...( typeof accumulatedTotalProcessedTokens === "number" &&
1353+ Number . isFinite ( accumulatedTotalProcessedTokens ) &&
1354+ accumulatedTotalProcessedTokens > lastGoodUsage . usedTokens
1355+ ? { totalProcessedTokens : accumulatedTotalProcessedTokens }
13571356 : { } ) ,
13581357 }
13591358 : accumulatedSnapshot ;
0 commit comments