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