| ... | ... | @@ -17,6 +17,8 @@ import { |
| 17 | 17 | type SDKResultMessage, |
| 18 | 18 | type SettingSource, |
| 19 | 19 | type SDKUserMessage, |
| 20 | ModelUsage, |
| 21 | NonNullableUsage, |
| 20 | 22 | } from "@anthropic-ai/claude-agent-sdk"; |
| 21 | 23 | import { |
| 22 | 24 | ApprovalRequestId, |
| ... | ... | @@ -272,24 +274,14 @@ function asRuntimeItemId(value: string): RuntimeItemId { |
| 272 | 274 | return RuntimeItemId.make(value); |
| 273 | 275 | } |
| 274 | 276 | |
| 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; |
| 279 | 281 | |
| 280 | 282 | 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; |
| 293 | 285 | maxContextWindow = Math.max(maxContextWindow ?? 0, contextWindow); |
| 294 | 286 | } |
| 295 | 287 | |
| ... | ... | @@ -297,53 +289,58 @@ function maxClaudeContextWindowFromModelUsage(modelUsage: unknown): number | und |
| 297 | 289 | } |
| 298 | 290 | |
| 299 | 291 | function normalizeClaudeTokenUsage( |
| 300 | | usage: unknown, |
| 292 | value: NonNullableUsage | undefined, |
| 301 | 293 | contextWindow?: number, |
| 302 | 294 | ): ThreadTokenUsageSnapshot | undefined { |
| 303 | | if (!usage || typeof usage !== "object") { |
| 295 | if (!value || typeof value !== "object") { |
| 304 | 296 | return undefined; |
| 305 | 297 | } |
| 306 | 298 | |
| 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>; |
| 312 | 300 | 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 |
| 315 | 303 | : 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 |
| 319 | 307 | : 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 |
| 323 | 311 | : 0); |
| 324 | 312 | 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 |
| 327 | 315 | : 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) { |
| 331 | 322 | return undefined; |
| 332 | 323 | } |
| 333 | 324 | |
| 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 | |
| 334 | 332 | return { |
| 335 | 333 | usedTokens, |
| 336 | 334 | lastUsedTokens: usedTokens, |
| 335 | ...(totalProcessedTokens > usedTokens ? { totalProcessedTokens } : {}), |
| 337 | 336 | ...(inputTokens > 0 ? { inputTokens } : {}), |
| 338 | 337 | ...(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 } |
| 344 | 341 | : {}), |
| 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 } |
| 347 | 344 | : {}), |
| 348 | 345 | }; |
| 349 | 346 | } |
| ... | ... | @@ -1328,8 +1325,6 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( |
| 1328 | 1325 | errorMessage?: string, |
| 1329 | 1326 | result?: SDKResultMessage, |
| 1330 | 1327 | ) { |
| 1331 | | const resultUsage = |
| 1332 | | result?.usage && typeof result.usage === "object" ? { ...result.usage } : undefined; |
| 1333 | 1328 | const resultContextWindow = maxClaudeContextWindowFromModelUsage(result?.modelUsage); |
| 1334 | 1329 | if (resultContextWindow !== undefined) { |
| 1335 | 1330 | context.lastKnownContextWindow = resultContextWindow; |
| ... | ... | @@ -1341,9 +1336,11 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( |
| 1341 | 1336 | // Instead, use the last known context-window-accurate usage from task_progress |
| 1342 | 1337 | // events and treat the accumulated total as totalProcessedTokens. |
| 1343 | 1338 | const accumulatedSnapshot = normalizeClaudeTokenUsage( |
| 1344 | | resultUsage, |
| 1339 | result?.usage, |
| 1345 | 1340 | resultContextWindow ?? context.lastKnownContextWindow, |
| 1346 | 1341 | ); |
| 1342 | const accumulatedTotalProcessedTokens = |
| 1343 | accumulatedSnapshot?.totalProcessedTokens ?? accumulatedSnapshot?.usedTokens; |
| 1347 | 1344 | const lastGoodUsage = context.lastKnownTokenUsage; |
| 1348 | 1345 | const maxTokens = resultContextWindow ?? context.lastKnownContextWindow; |
| 1349 | 1346 | const usageSnapshot: ThreadTokenUsageSnapshot | undefined = lastGoodUsage |
| ... | ... | @@ -1352,8 +1349,10 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( |
| 1352 | 1349 | ...(typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0 |
| 1353 | 1350 | ? { maxTokens } |
| 1354 | 1351 | : {}), |
| 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 } |
| 1357 | 1356 | : {}), |
| 1358 | 1357 | } |
| 1359 | 1358 | : accumulatedSnapshot; |