-
Notifications
You must be signed in to change notification settings - Fork 1
fix(run): preserve terminal usage provenance #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e0ee9bf
323e610
72145fb
69958f8
48036a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -124,6 +124,7 @@ export namespace SessionCompaction { | |
| root: Instance.worktree, | ||
| }, | ||
| cost: 0, | ||
| usageStatus: "missing", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚪ usageStatus field order diverges from Session.getUsage. 🤖 Fix with your agentWhy this matters
root: Instance.worktree,
},
cost: 0,
usageStatus: "missing",
tokens: {
output: 0,
input: 0, |
||
| tokens: { | ||
| output: 0, | ||
| input: 0, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -808,23 +808,32 @@ export namespace Session { | |
| metadata: z.custom<ProviderMetadata>().optional(), | ||
| }), | ||
| (input) => { | ||
| const safe = (value: number) => { | ||
| if (!Number.isFinite(value)) return 0 | ||
| const safe = (value: unknown) => { | ||
| if (typeof value !== "number" || !Number.isFinite(value)) return 0 | ||
| return value | ||
| } | ||
| const cacheWrite = | ||
| input.metadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? | ||
| // @ts-expect-error | ||
| input.metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ?? | ||
| // @ts-expect-error | ||
| input.metadata?.["venice"]?.["usage"]?.["cacheCreationInputTokens"] | ||
| const usageStatus = [ | ||
| input.usage.totalTokens, | ||
| input.usage.inputTokens, | ||
| input.usage.outputTokens, | ||
| input.usage.reasoningTokens, | ||
| input.usage.cachedInputTokens, | ||
| cacheWrite, | ||
| ].some(Number.isFinite) | ||
| ? ("reported" as const) | ||
| : ("missing" as const) | ||
| const inputTokens = safe(input.usage.inputTokens ?? 0) | ||
| const outputTokens = safe(input.usage.outputTokens ?? 0) | ||
| const reasoningTokens = safe(input.usage.reasoningTokens ?? 0) | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚪ Unsafe cast on untyped provider metadata. 🤖 Fix with your agentWhy this matters
const cacheReadInputTokens = safe(input.usage.cachedInputTokens ?? 0)
const cacheWriteInputTokens = safe(cacheWrite as number | undefined) |
||
| const cacheReadInputTokens = safe(input.usage.cachedInputTokens ?? 0) | ||
| const cacheWriteInputTokens = safe( | ||
| (input.metadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? | ||
| // @ts-expect-error | ||
| input.metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ?? | ||
| // @ts-expect-error | ||
| input.metadata?.["venice"]?.["usage"]?.["cacheCreationInputTokens"] ?? | ||
| 0) as number, | ||
| ) | ||
| const cacheWriteInputTokens = safe(cacheWrite) | ||
|
|
||
| // Providers where inputTokens EXCLUDES cached tokens | ||
| const excludesCachedTokens = [ | ||
|
|
@@ -850,9 +859,11 @@ export namespace Session { | |
| input.model.api.npm === "@ai-sdk/amazon-bedrock" || | ||
| input.model.api.npm === "@ai-sdk/google-vertex/anthropic" | ||
| ) { | ||
| if (!Number.isFinite(input.usage.inputTokens) || !Number.isFinite(input.usage.outputTokens)) return | ||
| return adjustedInputTokens + outputTokens + cacheReadInputTokens + cacheWriteInputTokens | ||
| } | ||
| return input.usage.totalTokens | ||
| if (Number.isFinite(input.usage.totalTokens)) return input.usage.totalTokens | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Partial-sum total labeled usageStatus "reported". 🤖 Fix with your agentWhy this mattersWhen if (!Number.isFinite(input.usage.inputTokens) || !Number.isFinite(input.usage.outputTokens)) return
return adjustedInputTokens + outputTokens + cacheReadInputTokens + cacheWriteInputTokens
}
if (Number.isFinite(input.usage.totalTokens)) return input.usage.totalTokens
if (!Number.isFinite(input.usage.inputTokens) || !Number.isFinite(input.usage.outputTokens)) return
return inputTokens + outputTokens
}) |
||
| return | ||
| }) | ||
|
|
||
| const tokens = { | ||
|
|
@@ -871,6 +882,7 @@ export namespace Session { | |
| ? input.model.cost.experimentalOver200K | ||
| : input.model.cost | ||
| return { | ||
| usageStatus, | ||
| cost: safe( | ||
| new Decimal(0) | ||
| .add(new Decimal(tokens.input).mul(costInfo?.input ?? 0).div(1_000_000)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Child-session filter silently drops message_complete events.
🤖 Fix with your agent
Why this matters
The emit gate changed from
if (info.finish)toif (info.sessionID === sessionID && info.time.completed !== undefined && !emitted.has(info.id)). The addedinfo.sessionID === sessionIDclause filters out assistant messages produced by child sessions (e.g. those spawned by the task tool). Previously, any assistant message with a finish reason — including child-session messages — produced amessage_completeevent. The new test explicitly assertsmsg_child(sessionID: 'ses_child') is filtered out, so the scoping is intentional, but EVENTS.md does not document it.Consumers that aggregate usage/cost across the primary and child sessions (e.g. for billing or CI telemetry) will silently stop receiving child-session
message_completeevents after this PR. EVENTS.md should state thatmessage_completeis scoped to the primary session only, or the filter should be broadened to trackedchildSessions.