@agent-finops/core 0.5.9 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/activitySnapshot.d.ts +676 -0
- package/dist/activitySnapshot.js +1220 -0
- package/dist/activitySnapshotCache.d.ts +54 -0
- package/dist/activitySnapshotCache.js +489 -0
- package/dist/discovery.d.ts +6 -2
- package/dist/discovery.js +36 -14
- package/dist/glance.d.ts +6 -2
- package/dist/glance.js +62 -13
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/localAgentLogs.d.ts +68 -2
- package/dist/localAgentLogs.js +972 -59
- package/dist/modelPricing.js +0 -1
- package/dist/providerConnectors.d.ts +13 -2
- package/dist/providerConnectors.js +708 -95
- package/dist/sampleData.js +4 -3
- package/dist/schema.d.ts +31 -29
- package/dist/schema.js +27 -3
- package/dist/sourceRegistry.d.ts +30 -5
- package/dist/sourceRegistry.js +250 -21
- package/dist/sourceStatus.d.ts +65 -0
- package/dist/sourceStatus.js +147 -0
- package/dist/stateTrust.d.ts +37 -0
- package/dist/stateTrust.js +277 -0
- package/package.json +1 -1
- package/samples/openai-usage.csv +2 -2
|
@@ -0,0 +1,1220 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { aggregateCalls, dedupeCumulativeSessionCalls } from "./localAgentLogs.js";
|
|
3
|
+
import { estimateTokenCostUsd, PRICING_TABLE_AS_OF } from "./modelPricing.js";
|
|
4
|
+
import { isBundledSampleUsage } from "./schema.js";
|
|
5
|
+
import { sourceValidationCoverageValues } from "./sourceStatus.js";
|
|
6
|
+
const DAY_MS = 24 * 60 * 60 * 1_000;
|
|
7
|
+
export const activitySnapshotAgentValues = ["claude-code", "codex"];
|
|
8
|
+
export const activitySnapshotPlanIdValues = [
|
|
9
|
+
"claude-pro",
|
|
10
|
+
"claude-max-5x",
|
|
11
|
+
"claude-max-20x",
|
|
12
|
+
"chatgpt-plus",
|
|
13
|
+
"chatgpt-pro"
|
|
14
|
+
];
|
|
15
|
+
export const activitySnapshotProviderValues = [
|
|
16
|
+
"openai",
|
|
17
|
+
"anthropic",
|
|
18
|
+
"cursor",
|
|
19
|
+
"github-copilot",
|
|
20
|
+
"other"
|
|
21
|
+
];
|
|
22
|
+
export const activitySnapshotModeValues = [
|
|
23
|
+
"metered",
|
|
24
|
+
"subscription",
|
|
25
|
+
"mixed",
|
|
26
|
+
"unresolved",
|
|
27
|
+
"empty",
|
|
28
|
+
"error"
|
|
29
|
+
];
|
|
30
|
+
export const activitySnapshotRefreshErrorCodeValues = [
|
|
31
|
+
"scan_failed",
|
|
32
|
+
"source_unreadable",
|
|
33
|
+
"invalid_evidence",
|
|
34
|
+
"timeout",
|
|
35
|
+
"cache_write_failed",
|
|
36
|
+
"unknown"
|
|
37
|
+
];
|
|
38
|
+
export const activitySnapshotProviderCoverageStatusValues = [
|
|
39
|
+
"complete",
|
|
40
|
+
"partial",
|
|
41
|
+
"unavailable",
|
|
42
|
+
"error"
|
|
43
|
+
];
|
|
44
|
+
const isoTimestampSchema = z.string().datetime({ offset: true });
|
|
45
|
+
const usdSchema = z.number().finite().nonnegative();
|
|
46
|
+
const countSchema = z.number().int().nonnegative();
|
|
47
|
+
const windowCoverageSchema = z.enum(["complete", "partial", "missing"]);
|
|
48
|
+
const agentSchema = z.enum(activitySnapshotAgentValues);
|
|
49
|
+
const planIdSchema = z.enum(activitySnapshotPlanIdValues).nullable();
|
|
50
|
+
export const activitySnapshotApiEquivalentWindowSchema = z.object({
|
|
51
|
+
amountUsd: usdSchema.nullable(),
|
|
52
|
+
recordCount: countSchema,
|
|
53
|
+
basis: z.literal("api_equivalent"),
|
|
54
|
+
financialEvidence: z.enum(["estimated", "missing"]),
|
|
55
|
+
coverage: windowCoverageSchema
|
|
56
|
+
}).strict().superRefine((window, context) => {
|
|
57
|
+
if ((window.amountUsd === null) !== (window.financialEvidence === "missing")) {
|
|
58
|
+
context.addIssue({
|
|
59
|
+
code: "custom",
|
|
60
|
+
path: ["financialEvidence"],
|
|
61
|
+
message: "API-equivalent amount and financial evidence must agree."
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (window.coverage === "missing" && window.amountUsd !== null) {
|
|
65
|
+
context.addIssue({
|
|
66
|
+
code: "custom",
|
|
67
|
+
path: ["amountUsd"],
|
|
68
|
+
message: "Missing API-equivalent coverage cannot carry an amount."
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (window.recordCount === 0 && window.amountUsd !== null &&
|
|
72
|
+
(window.amountUsd !== 0 || window.coverage !== "complete")) {
|
|
73
|
+
context.addIssue({
|
|
74
|
+
code: "custom",
|
|
75
|
+
path: ["amountUsd"],
|
|
76
|
+
message: "An empty API-equivalent window may carry only a coverage-proved zero."
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
export const activitySnapshotBilledWindowSchema = z.object({
|
|
81
|
+
amountUsd: usdSchema.nullable(),
|
|
82
|
+
recordCount: countSchema,
|
|
83
|
+
basis: z.literal("provider_billed"),
|
|
84
|
+
financialEvidence: z.enum(["verified", "missing"]),
|
|
85
|
+
coverage: windowCoverageSchema
|
|
86
|
+
}).strict().superRefine((window, context) => {
|
|
87
|
+
if ((window.amountUsd === null) !== (window.financialEvidence === "missing")) {
|
|
88
|
+
context.addIssue({
|
|
89
|
+
code: "custom",
|
|
90
|
+
path: ["financialEvidence"],
|
|
91
|
+
message: "Provider-billed amount and financial evidence must agree."
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (window.coverage === "missing" && window.amountUsd !== null) {
|
|
95
|
+
context.addIssue({
|
|
96
|
+
code: "custom",
|
|
97
|
+
path: ["amountUsd"],
|
|
98
|
+
message: "Missing provider-billed coverage cannot carry an amount."
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (window.recordCount === 0 && window.amountUsd !== null &&
|
|
102
|
+
(window.amountUsd !== 0 || window.coverage !== "complete")) {
|
|
103
|
+
context.addIssue({
|
|
104
|
+
code: "custom",
|
|
105
|
+
path: ["amountUsd"],
|
|
106
|
+
message: "An empty provider-billed window may carry only a receipt-proved zero."
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
function rollingWindowsSchema(windowSchema) {
|
|
111
|
+
return z.object({
|
|
112
|
+
oneDay: windowSchema,
|
|
113
|
+
sevenDays: windowSchema,
|
|
114
|
+
thirtyDays: windowSchema
|
|
115
|
+
}).strict();
|
|
116
|
+
}
|
|
117
|
+
export const activitySnapshotApiEquivalentWindowsSchema = rollingWindowsSchema(activitySnapshotApiEquivalentWindowSchema);
|
|
118
|
+
export const activitySnapshotBilledWindowsSchema = rollingWindowsSchema(activitySnapshotBilledWindowSchema);
|
|
119
|
+
function rollingWindowsHaveEvidence(windows) {
|
|
120
|
+
return [windows.oneDay, windows.sevenDays, windows.thirtyDays]
|
|
121
|
+
.some((window) => window.recordCount > 0 || window.amountUsd !== null);
|
|
122
|
+
}
|
|
123
|
+
function uniqueAgentEntries(value, context) {
|
|
124
|
+
if (new Set(value.agents.map((agent) => agent.agent)).size !== value.agents.length) {
|
|
125
|
+
context.addIssue({
|
|
126
|
+
code: "custom",
|
|
127
|
+
path: ["agents"],
|
|
128
|
+
message: "A cohort may contain each agent only once."
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export const activitySnapshotLimitSchema = z.object({
|
|
133
|
+
kind: z.enum(["five-hour", "weekly"]),
|
|
134
|
+
usedPercent: z.number().finite().min(0).max(100),
|
|
135
|
+
remainingPercent: z.number().finite().min(0).max(100),
|
|
136
|
+
observedAt: isoTimestampSchema,
|
|
137
|
+
resetsAt: isoTimestampSchema,
|
|
138
|
+
source: z.literal("transcript_reported")
|
|
139
|
+
}).strict().superRefine((limit, context) => {
|
|
140
|
+
if (Math.abs(limit.usedPercent + limit.remainingPercent - 100) > 0.11) {
|
|
141
|
+
context.addIssue({
|
|
142
|
+
code: "custom",
|
|
143
|
+
path: ["remainingPercent"],
|
|
144
|
+
message: "Reported used and remaining percentages must sum to 100."
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (Date.parse(limit.observedAt) >= Date.parse(limit.resetsAt)) {
|
|
148
|
+
context.addIssue({
|
|
149
|
+
code: "custom",
|
|
150
|
+
path: ["resetsAt"],
|
|
151
|
+
message: "A reported limit reset must follow its observation."
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
export const activitySnapshotSubscriptionAgentSchema = z.object({
|
|
156
|
+
agent: agentSchema,
|
|
157
|
+
billing: z.literal("subscription"),
|
|
158
|
+
planId: planIdSchema,
|
|
159
|
+
apiEquivalent: activitySnapshotApiEquivalentWindowsSchema,
|
|
160
|
+
limits: z.array(activitySnapshotLimitSchema).max(2),
|
|
161
|
+
pressure: z.enum(["extra_usage_credits_exhausted"]).nullable()
|
|
162
|
+
}).strict().superRefine((value, context) => {
|
|
163
|
+
if (new Set(value.limits.map((limit) => limit.kind)).size !== value.limits.length) {
|
|
164
|
+
context.addIssue({
|
|
165
|
+
code: "custom",
|
|
166
|
+
path: ["limits"],
|
|
167
|
+
message: "An agent may contain at most one limit of each kind."
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
const activitySnapshotSubscriptionSchema = z.object({
|
|
172
|
+
agents: z.array(activitySnapshotSubscriptionAgentSchema).min(1).max(2)
|
|
173
|
+
}).strict().superRefine(uniqueAgentEntries);
|
|
174
|
+
const activitySnapshotMeteredSchema = z.object({
|
|
175
|
+
agents: z.array(z.object({
|
|
176
|
+
agent: agentSchema,
|
|
177
|
+
billing: z.literal("api_key"),
|
|
178
|
+
planId: planIdSchema
|
|
179
|
+
}).strict()).max(2),
|
|
180
|
+
apiEquivalent: activitySnapshotApiEquivalentWindowsSchema,
|
|
181
|
+
providerBilled: activitySnapshotBilledWindowsSchema
|
|
182
|
+
}).strict().superRefine((value, context) => {
|
|
183
|
+
uniqueAgentEntries(value, context);
|
|
184
|
+
const hasFinancialEvidence = rollingWindowsHaveEvidence(value.apiEquivalent) ||
|
|
185
|
+
rollingWindowsHaveEvidence(value.providerBilled);
|
|
186
|
+
if (value.agents.length === 0 && !hasFinancialEvidence) {
|
|
187
|
+
context.addIssue({
|
|
188
|
+
code: "custom",
|
|
189
|
+
path: ["agents"],
|
|
190
|
+
message: "A metered cohort requires an agent or bounded financial evidence."
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const activitySnapshotUnresolvedSchema = z.object({
|
|
195
|
+
agents: z.array(z.object({
|
|
196
|
+
agent: agentSchema,
|
|
197
|
+
billing: z.literal("unknown"),
|
|
198
|
+
planId: planIdSchema
|
|
199
|
+
}).strict()).max(2),
|
|
200
|
+
apiEquivalent: activitySnapshotApiEquivalentWindowsSchema
|
|
201
|
+
}).strict().superRefine((value, context) => {
|
|
202
|
+
uniqueAgentEntries(value, context);
|
|
203
|
+
if (value.agents.length === 0 && !rollingWindowsHaveEvidence(value.apiEquivalent)) {
|
|
204
|
+
context.addIssue({
|
|
205
|
+
code: "custom",
|
|
206
|
+
path: ["agents"],
|
|
207
|
+
message: "An unresolved cohort requires an agent or bounded financial evidence."
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
export const activitySnapshotOverageSchema = z.object({
|
|
212
|
+
amountUsd: z.number().finite().positive(),
|
|
213
|
+
currency: z.literal("USD"),
|
|
214
|
+
basis: z.literal("provider_billed"),
|
|
215
|
+
financialEvidence: z.literal("verified"),
|
|
216
|
+
alertEligible: z.literal(true),
|
|
217
|
+
recordCount: z.number().int().positive()
|
|
218
|
+
}).strict();
|
|
219
|
+
const activitySnapshotAgentCoverageSchema = z.object({
|
|
220
|
+
agent: agentSchema,
|
|
221
|
+
directoryStatus: z.enum(["readable", "missing", "unreadable"]),
|
|
222
|
+
filesDiscovered: countSchema,
|
|
223
|
+
filesParsed: countSchema,
|
|
224
|
+
malformedLines: countSchema,
|
|
225
|
+
unreadableFiles: countSchema,
|
|
226
|
+
unsupportedUsageSnapshots: countSchema,
|
|
227
|
+
filesSkippedBeforeWindow: countSchema,
|
|
228
|
+
filesReadFinancially: countSchema,
|
|
229
|
+
bytesSkippedAsNonFinancialHistory: countSchema,
|
|
230
|
+
nonFinancialLinesPrefiltered: countSchema,
|
|
231
|
+
nonFinancialBytesPrefiltered: countSchema,
|
|
232
|
+
jsonlValidationCoverage: z.enum(["complete", "financial_events_only", "not_reported"])
|
|
233
|
+
}).strict();
|
|
234
|
+
const activitySnapshotProviderCoverageSchema = z.object({
|
|
235
|
+
provider: z.enum(activitySnapshotProviderValues),
|
|
236
|
+
status: z.enum(activitySnapshotProviderCoverageStatusValues),
|
|
237
|
+
validationCoverage: z.enum(sourceValidationCoverageValues),
|
|
238
|
+
checkedAt: isoTimestampSchema.nullable(),
|
|
239
|
+
latestEvidenceAt: isoTimestampSchema.nullable(),
|
|
240
|
+
coverageStart: isoTimestampSchema.nullable(),
|
|
241
|
+
coverageEnd: isoTimestampSchema.nullable()
|
|
242
|
+
}).strict().superRefine((coverage, context) => {
|
|
243
|
+
if ((coverage.latestEvidenceAt || coverage.coverageStart || coverage.coverageEnd) &&
|
|
244
|
+
!coverage.checkedAt) {
|
|
245
|
+
context.addIssue({
|
|
246
|
+
code: "custom",
|
|
247
|
+
path: ["checkedAt"],
|
|
248
|
+
message: "Provider evidence timestamps require a receipt-bound check time."
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (coverage.latestEvidenceAt && coverage.checkedAt &&
|
|
252
|
+
Date.parse(coverage.latestEvidenceAt) > Date.parse(coverage.checkedAt)) {
|
|
253
|
+
context.addIssue({
|
|
254
|
+
code: "custom",
|
|
255
|
+
path: ["latestEvidenceAt"],
|
|
256
|
+
message: "Latest provider evidence cannot be newer than the receipt-bound check."
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if ((coverage.coverageStart === null) !== (coverage.coverageEnd === null)) {
|
|
260
|
+
context.addIssue({
|
|
261
|
+
code: "custom",
|
|
262
|
+
path: ["coverageEnd"],
|
|
263
|
+
message: "Provider coverage bounds must be supplied together."
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (coverage.coverageStart && coverage.coverageEnd &&
|
|
267
|
+
Date.parse(coverage.coverageStart) > Date.parse(coverage.coverageEnd)) {
|
|
268
|
+
context.addIssue({
|
|
269
|
+
code: "custom",
|
|
270
|
+
path: ["coverageEnd"],
|
|
271
|
+
message: "Provider coverage end must not precede its start."
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (coverage.coverageEnd && coverage.checkedAt &&
|
|
275
|
+
Date.parse(coverage.coverageEnd) > Date.parse(coverage.checkedAt)) {
|
|
276
|
+
context.addIssue({
|
|
277
|
+
code: "custom",
|
|
278
|
+
path: ["coverageEnd"],
|
|
279
|
+
message: "Provider coverage cannot extend beyond its receipt-bound check."
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (coverage.latestEvidenceAt && coverage.coverageStart && coverage.coverageEnd &&
|
|
283
|
+
(Date.parse(coverage.latestEvidenceAt) < Date.parse(coverage.coverageStart) ||
|
|
284
|
+
Date.parse(coverage.latestEvidenceAt) > Date.parse(coverage.coverageEnd))) {
|
|
285
|
+
context.addIssue({
|
|
286
|
+
code: "custom",
|
|
287
|
+
path: ["latestEvidenceAt"],
|
|
288
|
+
message: "Latest provider evidence must fall inside the receipt-bound coverage interval."
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
export const activitySnapshotCoverageSchema = z.object({
|
|
293
|
+
agents: z.array(activitySnapshotAgentCoverageSchema).max(2),
|
|
294
|
+
providers: z.array(activitySnapshotProviderCoverageSchema).max(5),
|
|
295
|
+
recordsParsed: countSchema,
|
|
296
|
+
recordsPriced: countSchema,
|
|
297
|
+
recordsUnpriced: countSchema,
|
|
298
|
+
validationStatus: z.enum(["complete", "partial", "failed", "not_checked"]),
|
|
299
|
+
pricingAsOf: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
300
|
+
networkUploaded: z.literal(false)
|
|
301
|
+
}).strict().superRefine((coverage, context) => {
|
|
302
|
+
if (coverage.recordsPriced + coverage.recordsUnpriced !== coverage.recordsParsed) {
|
|
303
|
+
context.addIssue({
|
|
304
|
+
code: "custom",
|
|
305
|
+
path: ["recordsParsed"],
|
|
306
|
+
message: "Priced and unpriced record counts must equal parsed records."
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
if (new Set(coverage.agents.map((agent) => agent.agent)).size !== coverage.agents.length) {
|
|
310
|
+
context.addIssue({
|
|
311
|
+
code: "custom",
|
|
312
|
+
path: ["agents"],
|
|
313
|
+
message: "Coverage may contain each agent only once."
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
if (new Set(coverage.providers.map((provider) => provider.provider)).size !== coverage.providers.length) {
|
|
317
|
+
context.addIssue({
|
|
318
|
+
code: "custom",
|
|
319
|
+
path: ["providers"],
|
|
320
|
+
message: "Coverage may contain each provider only once."
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
export const activitySnapshotSchema = z.object({
|
|
325
|
+
kind: z.literal("aibill.activity_snapshot"),
|
|
326
|
+
schemaVersion: z.literal(1),
|
|
327
|
+
currency: z.literal("USD"),
|
|
328
|
+
asOf: isoTimestampSchema,
|
|
329
|
+
generatedAt: isoTimestampSchema,
|
|
330
|
+
lastAttemptAt: isoTimestampSchema,
|
|
331
|
+
lastSuccessAt: isoTimestampSchema.nullable(),
|
|
332
|
+
refresh: z.discriminatedUnion("status", [
|
|
333
|
+
z.object({ status: z.literal("ok") }).strict(),
|
|
334
|
+
z.object({
|
|
335
|
+
status: z.literal("error"),
|
|
336
|
+
errorCode: z.enum(activitySnapshotRefreshErrorCodeValues)
|
|
337
|
+
}).strict()
|
|
338
|
+
]),
|
|
339
|
+
mode: z.enum(activitySnapshotModeValues),
|
|
340
|
+
subscription: activitySnapshotSubscriptionSchema.nullable(),
|
|
341
|
+
metered: activitySnapshotMeteredSchema.nullable(),
|
|
342
|
+
unresolved: activitySnapshotUnresolvedSchema.nullable(),
|
|
343
|
+
overage: activitySnapshotOverageSchema.nullable(),
|
|
344
|
+
coverage: activitySnapshotCoverageSchema,
|
|
345
|
+
networkUploaded: z.literal(false)
|
|
346
|
+
}).strict().superRefine((snapshot, context) => {
|
|
347
|
+
const invalid = (message, path) => context.addIssue({
|
|
348
|
+
code: "custom",
|
|
349
|
+
message,
|
|
350
|
+
path
|
|
351
|
+
});
|
|
352
|
+
if (snapshot.mode === "metered" && (!snapshot.metered || snapshot.subscription)) {
|
|
353
|
+
invalid("Metered mode requires a metered cohort and no subscription cohort.", ["mode"]);
|
|
354
|
+
}
|
|
355
|
+
if (snapshot.mode === "subscription" && (!snapshot.subscription || snapshot.metered)) {
|
|
356
|
+
invalid("Subscription mode requires a subscription cohort and no metered cohort.", ["mode"]);
|
|
357
|
+
}
|
|
358
|
+
if (snapshot.mode === "mixed" && (!snapshot.subscription || !snapshot.metered)) {
|
|
359
|
+
invalid("Mixed mode must keep subscription and metered cohorts separate.", ["mode"]);
|
|
360
|
+
}
|
|
361
|
+
if (snapshot.mode === "unresolved" && (!snapshot.unresolved || snapshot.subscription || snapshot.metered)) {
|
|
362
|
+
invalid("Unresolved mode must contain only unresolved API-equivalent evidence.", ["mode"]);
|
|
363
|
+
}
|
|
364
|
+
if ((snapshot.mode === "empty" || snapshot.mode === "error") &&
|
|
365
|
+
(snapshot.subscription || snapshot.metered || snapshot.unresolved || snapshot.overage)) {
|
|
366
|
+
invalid("Empty and error snapshots cannot carry financial cohorts.", ["mode"]);
|
|
367
|
+
}
|
|
368
|
+
if (snapshot.mode === "error" && snapshot.refresh.status !== "error") {
|
|
369
|
+
invalid("Error mode requires an error refresh state.", ["refresh"]);
|
|
370
|
+
}
|
|
371
|
+
if (snapshot.refresh.status === "ok" && snapshot.lastSuccessAt === null) {
|
|
372
|
+
invalid("Successful refreshes require lastSuccessAt.", ["lastSuccessAt"]);
|
|
373
|
+
}
|
|
374
|
+
if (snapshot.overage && !snapshot.metered) {
|
|
375
|
+
invalid("Verified billed overage belongs to the metered cohort.", ["overage"]);
|
|
376
|
+
}
|
|
377
|
+
const cohortAgents = [
|
|
378
|
+
...(snapshot.subscription?.agents ?? []),
|
|
379
|
+
...(snapshot.metered?.agents ?? []),
|
|
380
|
+
...(snapshot.unresolved?.agents ?? [])
|
|
381
|
+
].map((entry) => entry.agent);
|
|
382
|
+
if (new Set(cohortAgents).size !== cohortAgents.length) {
|
|
383
|
+
invalid("An agent cannot appear in more than one financial cohort.", ["mode"]);
|
|
384
|
+
}
|
|
385
|
+
const asOfMs = Date.parse(snapshot.asOf);
|
|
386
|
+
const generatedAtMs = Date.parse(snapshot.generatedAt);
|
|
387
|
+
const lastAttemptAtMs = Date.parse(snapshot.lastAttemptAt);
|
|
388
|
+
const lastSuccessAtMs = snapshot.lastSuccessAt === null
|
|
389
|
+
? null
|
|
390
|
+
: Date.parse(snapshot.lastSuccessAt);
|
|
391
|
+
if (asOfMs > generatedAtMs) {
|
|
392
|
+
invalid("Snapshot generation cannot precede its as-of time.", ["generatedAt"]);
|
|
393
|
+
}
|
|
394
|
+
for (let index = 0; index < snapshot.coverage.providers.length; index += 1) {
|
|
395
|
+
const provider = snapshot.coverage.providers[index];
|
|
396
|
+
for (const field of ["checkedAt", "latestEvidenceAt", "coverageEnd"]) {
|
|
397
|
+
const value = provider[field];
|
|
398
|
+
if (value !== null && Date.parse(value) > generatedAtMs) {
|
|
399
|
+
invalid("Provider evidence cannot be newer than snapshot generation.", ["coverage", "providers", String(index), field]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (snapshot.refresh.status === "ok") {
|
|
404
|
+
if (snapshot.mode === "error") {
|
|
405
|
+
invalid("A successful refresh cannot use error mode.", ["mode"]);
|
|
406
|
+
}
|
|
407
|
+
if (lastSuccessAtMs !== generatedAtMs) {
|
|
408
|
+
invalid("A successful refresh must bind lastSuccessAt to generatedAt.", ["lastSuccessAt"]);
|
|
409
|
+
}
|
|
410
|
+
if (lastAttemptAtMs !== asOfMs) {
|
|
411
|
+
invalid("A successful refresh must bind lastAttemptAt to asOf.", ["lastAttemptAt"]);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
else if (snapshot.lastSuccessAt === null) {
|
|
415
|
+
if (snapshot.mode !== "error" || lastAttemptAtMs !== asOfMs || generatedAtMs !== asOfMs) {
|
|
416
|
+
invalid("An initial failed refresh must be a single-time no-evidence error state.", ["refresh"]);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
if (snapshot.mode === "error") {
|
|
421
|
+
invalid("A retained last-good snapshot keeps its prior financial mode.", ["mode"]);
|
|
422
|
+
}
|
|
423
|
+
if (lastSuccessAtMs !== generatedAtMs || lastAttemptAtMs < generatedAtMs) {
|
|
424
|
+
invalid("A retained failure must follow the last successful generation.", ["lastAttemptAt"]);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
/**
|
|
429
|
+
* Build the privacy-bounded, plan-aware snapshot consumed by the status line.
|
|
430
|
+
* This function never trusts provider billing implicitly: the caller must pass
|
|
431
|
+
* IDs already validated by the external connected-state trust receipt.
|
|
432
|
+
*/
|
|
433
|
+
export function buildActivitySnapshot(input) {
|
|
434
|
+
if (input.sampleData === true ||
|
|
435
|
+
input.records.some((record) => isBundledSampleUsage([record]))) {
|
|
436
|
+
throw new Error("Sample data cannot create an activity snapshot.");
|
|
437
|
+
}
|
|
438
|
+
const asOfMs = parseTimestamp(input.asOf, "asOf");
|
|
439
|
+
const generatedAtMs = parseTimestamp(input.generatedAt, "generatedAt");
|
|
440
|
+
if (generatedAtMs < asOfMs) {
|
|
441
|
+
throw new Error("generatedAt must be at or after asOf.");
|
|
442
|
+
}
|
|
443
|
+
const generatedAt = new Date(generatedAtMs).toISOString();
|
|
444
|
+
const plans = safePlanMap(input.detectedPlans ?? []);
|
|
445
|
+
const scans = normalizeAgentScans(input.sourceScans ?? []);
|
|
446
|
+
const providers = normalizeProviderCoverage(input.providerCoverage ?? [], generatedAtMs);
|
|
447
|
+
const trustedIds = new Set(input.trustedProviderRecordIds ?? []);
|
|
448
|
+
const overageIds = new Set(input.billedOverageRecordIds ?? []);
|
|
449
|
+
for (const id of overageIds) {
|
|
450
|
+
if (!trustedIds.has(id)) {
|
|
451
|
+
throw new Error("Billed overage evidence must also be externally trusted provider evidence.");
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const horizonStartMs = asOfMs - 30 * DAY_MS;
|
|
455
|
+
// Horizon filtering must precede ID conflict detection: stale history must
|
|
456
|
+
// neither activate a cohort nor suppress a current row that reused an ID.
|
|
457
|
+
const horizonRecords = input.records.filter((record) => validRecordTimestampInHorizon(record, horizonStartMs, asOfMs));
|
|
458
|
+
const deduplicated = deduplicateRecords(horizonRecords);
|
|
459
|
+
const classified = deduplicated.records
|
|
460
|
+
.map((record) => ({
|
|
461
|
+
record,
|
|
462
|
+
cohort: classifyRecord(record, plans, trustedIds)
|
|
463
|
+
}));
|
|
464
|
+
// Keep later same-day calls available only for proportionally splitting a
|
|
465
|
+
// daily aggregate at asOf; activity and limit selection remain <= asOf.
|
|
466
|
+
const allCalls = dedupeCumulativeSessionCalls([...(input.calls ?? [])].filter((call) =>
|
|
467
|
+
// One preceding bucket is needed to preserve the denominator when a
|
|
468
|
+
// daily aggregate straddles the 30-day cutoff.
|
|
469
|
+
validCallAtOrAfter(call, horizonStartMs - DAY_MS)));
|
|
470
|
+
const calls = allCalls.filter((call) => validCallAtOrBefore(call, asOfMs));
|
|
471
|
+
const subscriptionAgents = activitySubscriptionAgents(classified, calls, allCalls, plans, scans, trustedIds, asOfMs);
|
|
472
|
+
const meteredApiRecords = classified
|
|
473
|
+
.filter((entry) => entry.cohort === "metered_api")
|
|
474
|
+
.map((entry) => entry.record);
|
|
475
|
+
const meteredBilledRecords = classified
|
|
476
|
+
.filter((entry) => entry.cohort === "metered_billed")
|
|
477
|
+
.map((entry) => entry.record);
|
|
478
|
+
const unresolvedRecords = classified
|
|
479
|
+
.filter((entry) => entry.cohort === "unresolved")
|
|
480
|
+
.map((entry) => entry.record);
|
|
481
|
+
const activeAgents = new Set();
|
|
482
|
+
for (const entry of classified) {
|
|
483
|
+
if (isSnapshotAgent(entry.record.agentId))
|
|
484
|
+
activeAgents.add(entry.record.agentId);
|
|
485
|
+
}
|
|
486
|
+
for (const call of calls)
|
|
487
|
+
activeAgents.add(call.agent);
|
|
488
|
+
const meteredAgents = [...activeAgents]
|
|
489
|
+
.filter((agent) => plans.get(agent)?.billing === "api_key")
|
|
490
|
+
.sort()
|
|
491
|
+
.map((agent) => {
|
|
492
|
+
const detected = plans.get(agent);
|
|
493
|
+
return {
|
|
494
|
+
agent,
|
|
495
|
+
billing: "api_key",
|
|
496
|
+
planId: isKnownPlanId(detected?.planId) ? detected.planId : null
|
|
497
|
+
};
|
|
498
|
+
});
|
|
499
|
+
const unresolvedAgents = [...activeAgents]
|
|
500
|
+
.filter((agent) => {
|
|
501
|
+
const billing = plans.get(agent)?.billing;
|
|
502
|
+
return billing !== "subscription" && billing !== "api_key";
|
|
503
|
+
})
|
|
504
|
+
.sort()
|
|
505
|
+
.map((agent) => {
|
|
506
|
+
const detected = plans.get(agent);
|
|
507
|
+
return {
|
|
508
|
+
agent,
|
|
509
|
+
billing: "unknown",
|
|
510
|
+
planId: isKnownPlanId(detected?.planId) ? detected.planId : null
|
|
511
|
+
};
|
|
512
|
+
});
|
|
513
|
+
const hasReceiptProvedMeteredWindow = providerIntervalCoverage(providers, asOfMs - DAY_MS, asOfMs) === "complete";
|
|
514
|
+
const hasMeteredActivity = meteredApiRecords.length > 0 || meteredBilledRecords.length > 0 ||
|
|
515
|
+
meteredAgents.length > 0 || hasReceiptProvedMeteredWindow;
|
|
516
|
+
const hasSubscriptionActivity = subscriptionAgents.length > 0;
|
|
517
|
+
const hasUnresolvedActivity = unresolvedRecords.length > 0 || unresolvedAgents.length > 0;
|
|
518
|
+
let mode;
|
|
519
|
+
if (hasSubscriptionActivity && hasMeteredActivity)
|
|
520
|
+
mode = "mixed";
|
|
521
|
+
else if (hasSubscriptionActivity)
|
|
522
|
+
mode = "subscription";
|
|
523
|
+
else if (hasMeteredActivity)
|
|
524
|
+
mode = "metered";
|
|
525
|
+
else if (hasUnresolvedActivity)
|
|
526
|
+
mode = "unresolved";
|
|
527
|
+
else
|
|
528
|
+
mode = "empty";
|
|
529
|
+
const subscription = hasSubscriptionActivity ? { agents: subscriptionAgents } : null;
|
|
530
|
+
const metered = hasMeteredActivity ? {
|
|
531
|
+
agents: meteredAgents,
|
|
532
|
+
apiEquivalent: buildApiWindows(meteredApiRecords, allCalls.filter((call) => plans.get(call.agent)?.billing === "api_key"), trustedIds, asOfMs, localCoverageForRecords(meteredApiRecords, scans, [...plans.entries()]
|
|
533
|
+
.filter(([, plan]) => plan.billing === "api_key")
|
|
534
|
+
.map(([agent]) => agent))),
|
|
535
|
+
providerBilled: buildBilledWindows(meteredBilledRecords, asOfMs, providers)
|
|
536
|
+
} : null;
|
|
537
|
+
const unresolved = hasUnresolvedActivity ? {
|
|
538
|
+
agents: unresolvedAgents,
|
|
539
|
+
apiEquivalent: buildApiWindows(unresolvedRecords, allCalls.filter((call) => {
|
|
540
|
+
const billing = plans.get(call.agent)?.billing;
|
|
541
|
+
return billing !== "subscription" && billing !== "api_key";
|
|
542
|
+
}), trustedIds, asOfMs, unresolvedCoverageForRecords(unresolvedRecords, scans, providers))
|
|
543
|
+
} : null;
|
|
544
|
+
const overage = buildOverage(meteredBilledRecords, overageIds, asOfMs);
|
|
545
|
+
const coverage = buildCoverage(classified.map((entry) => entry.record), scans, providers, input.pricingAsOf ?? PRICING_TABLE_AS_OF, deduplicated.conflictingIds);
|
|
546
|
+
return activitySnapshotSchema.parse({
|
|
547
|
+
kind: "aibill.activity_snapshot",
|
|
548
|
+
schemaVersion: 1,
|
|
549
|
+
currency: "USD",
|
|
550
|
+
asOf: new Date(asOfMs).toISOString(),
|
|
551
|
+
generatedAt,
|
|
552
|
+
lastAttemptAt: new Date(asOfMs).toISOString(),
|
|
553
|
+
lastSuccessAt: generatedAt,
|
|
554
|
+
refresh: { status: "ok" },
|
|
555
|
+
mode,
|
|
556
|
+
subscription: mode === "metered" || mode === "unresolved" || mode === "empty"
|
|
557
|
+
? null
|
|
558
|
+
: subscription,
|
|
559
|
+
metered: mode === "subscription" || mode === "unresolved" || mode === "empty"
|
|
560
|
+
? null
|
|
561
|
+
: metered,
|
|
562
|
+
unresolved,
|
|
563
|
+
overage: mode === "metered" || mode === "mixed" ? overage : null,
|
|
564
|
+
coverage,
|
|
565
|
+
networkUploaded: false
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
/** A bounded no-evidence state for an initial failed refresh. */
|
|
569
|
+
export function createActivitySnapshotError(attemptedAt, errorCode) {
|
|
570
|
+
const timestamp = new Date(parseTimestamp(attemptedAt, "attemptedAt")).toISOString();
|
|
571
|
+
return activitySnapshotSchema.parse({
|
|
572
|
+
kind: "aibill.activity_snapshot",
|
|
573
|
+
schemaVersion: 1,
|
|
574
|
+
currency: "USD",
|
|
575
|
+
asOf: timestamp,
|
|
576
|
+
generatedAt: timestamp,
|
|
577
|
+
lastAttemptAt: timestamp,
|
|
578
|
+
lastSuccessAt: null,
|
|
579
|
+
refresh: { status: "error", errorCode },
|
|
580
|
+
mode: "error",
|
|
581
|
+
subscription: null,
|
|
582
|
+
metered: null,
|
|
583
|
+
unresolved: null,
|
|
584
|
+
overage: null,
|
|
585
|
+
coverage: {
|
|
586
|
+
agents: [],
|
|
587
|
+
providers: [],
|
|
588
|
+
recordsParsed: 0,
|
|
589
|
+
recordsPriced: 0,
|
|
590
|
+
recordsUnpriced: 0,
|
|
591
|
+
validationStatus: "failed",
|
|
592
|
+
pricingAsOf: PRICING_TABLE_AS_OF,
|
|
593
|
+
networkUploaded: false
|
|
594
|
+
},
|
|
595
|
+
networkUploaded: false
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
function safePlanMap(plans) {
|
|
599
|
+
const byAgent = new Map();
|
|
600
|
+
for (const plan of plans) {
|
|
601
|
+
if (!isSnapshotAgent(plan.agent) || byAgent.has(plan.agent))
|
|
602
|
+
continue;
|
|
603
|
+
byAgent.set(plan.agent, plan);
|
|
604
|
+
}
|
|
605
|
+
return byAgent;
|
|
606
|
+
}
|
|
607
|
+
function classifyRecord(record, plans, trustedProviderIds) {
|
|
608
|
+
if (trustedProviderIds.has(record.id) &&
|
|
609
|
+
record.costConfidence === "verified" &&
|
|
610
|
+
typeof record.amountUsd === "number") {
|
|
611
|
+
return "metered_billed";
|
|
612
|
+
}
|
|
613
|
+
const agent = isSnapshotAgent(record.agentId) ? record.agentId : undefined;
|
|
614
|
+
const billing = agent ? plans.get(agent)?.billing : undefined;
|
|
615
|
+
if (billing === "subscription")
|
|
616
|
+
return "subscription";
|
|
617
|
+
if (billing === "api_key")
|
|
618
|
+
return "metered_api";
|
|
619
|
+
return "unresolved";
|
|
620
|
+
}
|
|
621
|
+
function activitySubscriptionAgents(records, calls, allCalls, plans, scans, trustedProviderIds, asOfMs) {
|
|
622
|
+
const result = [];
|
|
623
|
+
for (const agent of activitySnapshotAgentValues) {
|
|
624
|
+
const plan = plans.get(agent);
|
|
625
|
+
if (plan?.billing !== "subscription")
|
|
626
|
+
continue;
|
|
627
|
+
const agentRecords = records
|
|
628
|
+
.filter((entry) => entry.cohort === "subscription" && entry.record.agentId === agent)
|
|
629
|
+
.map((entry) => entry.record);
|
|
630
|
+
const agentCalls = calls.filter((call) => call.agent === agent);
|
|
631
|
+
if (agentRecords.length === 0 && agentCalls.length === 0)
|
|
632
|
+
continue;
|
|
633
|
+
result.push({
|
|
634
|
+
agent,
|
|
635
|
+
billing: "subscription",
|
|
636
|
+
planId: isKnownPlanId(plan.planId) ? plan.planId : null,
|
|
637
|
+
apiEquivalent: buildApiWindows(agentRecords, allCalls.filter((call) => call.agent === agent), trustedProviderIds, asOfMs, localCoverageForAgent(agent, scans)),
|
|
638
|
+
limits: latestReportedLimits(agentCalls, asOfMs),
|
|
639
|
+
pressure: plan.limitSignal === "extra-usage credits exhausted"
|
|
640
|
+
? "extra_usage_credits_exhausted"
|
|
641
|
+
: null
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return result;
|
|
645
|
+
}
|
|
646
|
+
function latestReportedLimits(calls, asOfMs) {
|
|
647
|
+
const selected = new Map();
|
|
648
|
+
for (const call of calls) {
|
|
649
|
+
if (!call.rateLimits)
|
|
650
|
+
continue;
|
|
651
|
+
const observedMs = Date.parse(call.rateLimits.observedAt);
|
|
652
|
+
if (!Number.isFinite(observedMs) || observedMs > asOfMs)
|
|
653
|
+
continue;
|
|
654
|
+
for (const window of call.rateLimits.windows) {
|
|
655
|
+
if (window.kind !== "five-hour" && window.kind !== "weekly")
|
|
656
|
+
continue;
|
|
657
|
+
const resetMs = Date.parse(window.resetsAt);
|
|
658
|
+
if (!Number.isFinite(resetMs) || resetMs <= asOfMs)
|
|
659
|
+
continue;
|
|
660
|
+
const prior = selected.get(window.kind);
|
|
661
|
+
if (!prior || Date.parse(prior.observedAt) < observedMs) {
|
|
662
|
+
selected.set(window.kind, { observedAt: call.rateLimits.observedAt, window });
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return ["five-hour", "weekly"].flatMap((kind) => {
|
|
667
|
+
const value = selected.get(kind);
|
|
668
|
+
if (!value)
|
|
669
|
+
return [];
|
|
670
|
+
const usedPercent = roundPercent(value.window.usedPercent);
|
|
671
|
+
return [{
|
|
672
|
+
kind,
|
|
673
|
+
usedPercent,
|
|
674
|
+
remainingPercent: roundPercent(100 - usedPercent),
|
|
675
|
+
observedAt: new Date(Date.parse(value.observedAt)).toISOString(),
|
|
676
|
+
resetsAt: new Date(Date.parse(value.window.resetsAt)).toISOString(),
|
|
677
|
+
source: "transcript_reported"
|
|
678
|
+
}];
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
function buildApiWindows(records, calls, trustedProviderIds, asOfMs, sourceCoverage) {
|
|
682
|
+
const observations = apiEquivalentObservations(records, calls, trustedProviderIds);
|
|
683
|
+
return {
|
|
684
|
+
oneDay: buildApiWindow(observations, asOfMs, 1, sourceCoverage),
|
|
685
|
+
sevenDays: buildApiWindow(observations, asOfMs, 7, sourceCoverage),
|
|
686
|
+
thirtyDays: buildApiWindow(observations, asOfMs, 30, sourceCoverage)
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
function buildApiWindow(observations, asOfMs, days, sourceCoverage) {
|
|
690
|
+
const inWindow = observationsForWindow(observations, asOfMs, days);
|
|
691
|
+
const basisAvailable = inWindow.filter((observation) => observation.apiEquivalentBasisAvailable);
|
|
692
|
+
const priced = basisAvailable.filter((observation) => typeof observation.amountUsd === "number");
|
|
693
|
+
const unpriced = basisAvailable.length - priced.length;
|
|
694
|
+
const basisUnavailable = inWindow.length - basisAvailable.length;
|
|
695
|
+
const boundaryLimited = inWindow.some((observation) => observation.precision !== "exact");
|
|
696
|
+
const coverage = basisAvailable.length === 0 && basisUnavailable > 0
|
|
697
|
+
? "missing"
|
|
698
|
+
: windowCoverage(sourceCoverage, unpriced + basisUnavailable, boundaryLimited);
|
|
699
|
+
const observedAmount = priced.length > 0
|
|
700
|
+
? roundUsd(priced.reduce((sum, observation) => sum + (observation.amountUsd ?? 0), 0))
|
|
701
|
+
: null;
|
|
702
|
+
const amountUsd = observedAmount !== null &&
|
|
703
|
+
sourceCoverage !== "missing" &&
|
|
704
|
+
(observedAmount > 0 || coverage === "complete")
|
|
705
|
+
? observedAmount
|
|
706
|
+
: coverage === "complete"
|
|
707
|
+
? 0
|
|
708
|
+
: null;
|
|
709
|
+
return {
|
|
710
|
+
amountUsd,
|
|
711
|
+
recordCount: inWindow.length,
|
|
712
|
+
basis: "api_equivalent",
|
|
713
|
+
financialEvidence: amountUsd === null ? "missing" : "estimated",
|
|
714
|
+
coverage
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function apiEquivalentObservations(records, calls, trustedProviderIds) {
|
|
718
|
+
const deduplicatedCalls = dedupeCumulativeSessionCalls([...calls]);
|
|
719
|
+
const callsByAggregateId = new Map();
|
|
720
|
+
for (const call of deduplicatedCalls) {
|
|
721
|
+
const aggregateId = aggregateCalls([call])[0]?.id;
|
|
722
|
+
if (!aggregateId)
|
|
723
|
+
continue;
|
|
724
|
+
callsByAggregateId.set(aggregateId, [...(callsByAggregateId.get(aggregateId) ?? []), call]);
|
|
725
|
+
}
|
|
726
|
+
const matchedCalls = new Set();
|
|
727
|
+
const observations = [];
|
|
728
|
+
for (const record of records) {
|
|
729
|
+
const apiEquivalentBasisAvailable = record.providerCostType === "local_agent_logs" ||
|
|
730
|
+
(record.providerCostType === "anthropic_claude_code_usage" &&
|
|
731
|
+
trustedProviderIds.has(record.id));
|
|
732
|
+
if (!apiEquivalentBasisAvailable) {
|
|
733
|
+
observations.push({
|
|
734
|
+
timestamp: record.timestamp,
|
|
735
|
+
amountUsd: null,
|
|
736
|
+
precision: bucketPrecision(record),
|
|
737
|
+
apiEquivalentBasisAvailable: false
|
|
738
|
+
});
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
const matchingCalls = callsByAggregateId.get(record.id) ?? [];
|
|
742
|
+
if (matchingCalls.length === 0) {
|
|
743
|
+
observations.push({
|
|
744
|
+
timestamp: record.timestamp,
|
|
745
|
+
amountUsd: record.amountUsd,
|
|
746
|
+
precision: record.usageGranularity === "daily_aggregate" ? "daily_bucket" : bucketPrecision(record),
|
|
747
|
+
apiEquivalentBasisAvailable: true
|
|
748
|
+
});
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
const allocated = allocateAggregateAmount(record.amountUsd, matchingCalls);
|
|
752
|
+
for (let index = 0; index < matchingCalls.length; index += 1) {
|
|
753
|
+
const call = matchingCalls[index];
|
|
754
|
+
matchedCalls.add(call);
|
|
755
|
+
observations.push({
|
|
756
|
+
timestamp: call.timestamp,
|
|
757
|
+
...(call.usageScope === "session_cumulative" && call.startedAt
|
|
758
|
+
? { intervalStart: call.startedAt }
|
|
759
|
+
: {}),
|
|
760
|
+
amountUsd: allocated[index] ?? null,
|
|
761
|
+
precision: call.usageScope === "session_cumulative"
|
|
762
|
+
? "session_interval"
|
|
763
|
+
: "exact",
|
|
764
|
+
apiEquivalentBasisAvailable: true
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
for (const call of deduplicatedCalls) {
|
|
769
|
+
if (matchedCalls.has(call))
|
|
770
|
+
continue;
|
|
771
|
+
observations.push({
|
|
772
|
+
timestamp: call.timestamp,
|
|
773
|
+
...(call.usageScope === "session_cumulative" && call.startedAt
|
|
774
|
+
? { intervalStart: call.startedAt }
|
|
775
|
+
: {}),
|
|
776
|
+
amountUsd: null,
|
|
777
|
+
precision: call.usageScope === "session_cumulative"
|
|
778
|
+
? "session_interval"
|
|
779
|
+
: "exact",
|
|
780
|
+
apiEquivalentBasisAvailable: true
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
return observations;
|
|
784
|
+
}
|
|
785
|
+
function allocateAggregateAmount(amountUsd, calls) {
|
|
786
|
+
if (amountUsd === null)
|
|
787
|
+
return calls.map(() => null);
|
|
788
|
+
const weights = calls.map((call) => estimateTokenCostUsd(call.model, call.usage) ?? 0);
|
|
789
|
+
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
790
|
+
if (totalWeight <= 0)
|
|
791
|
+
return calls.map(() => amountUsd / calls.length);
|
|
792
|
+
return weights.map((weight) => amountUsd * weight / totalWeight);
|
|
793
|
+
}
|
|
794
|
+
function observationsForWindow(observations, asOfMs, days) {
|
|
795
|
+
const boundaryMs = asOfMs - days * DAY_MS;
|
|
796
|
+
const selected = [];
|
|
797
|
+
for (const observation of observations) {
|
|
798
|
+
const timestampMs = Date.parse(observation.timestamp);
|
|
799
|
+
if (!Number.isFinite(timestampMs) || timestampMs > asOfMs)
|
|
800
|
+
continue;
|
|
801
|
+
if (observation.precision === "exact") {
|
|
802
|
+
if (timestampMs >= boundaryMs)
|
|
803
|
+
selected.push(observation);
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (observation.precision === "session_interval") {
|
|
807
|
+
if (timestampMs < boundaryMs)
|
|
808
|
+
continue;
|
|
809
|
+
const intervalStartMs = observation.intervalStart === undefined
|
|
810
|
+
? Number.NaN
|
|
811
|
+
: Date.parse(observation.intervalStart);
|
|
812
|
+
if (!Number.isFinite(intervalStartMs) || intervalStartMs > timestampMs ||
|
|
813
|
+
intervalStartMs < boundaryMs) {
|
|
814
|
+
selected.push({ ...observation, amountUsd: null });
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
// A cumulative total wholly observed inside this rolling window is
|
|
818
|
+
// exact for the window; only a cutoff-straddling interval is partial.
|
|
819
|
+
selected.push({ ...observation, precision: "exact" });
|
|
820
|
+
}
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
if (observation.precision === "daily_bucket") {
|
|
824
|
+
const intervalEndMs = Math.min(timestampMs + DAY_MS, asOfMs);
|
|
825
|
+
if (intervalEndMs <= boundaryMs || timestampMs > asOfMs)
|
|
826
|
+
continue;
|
|
827
|
+
if (timestampMs < boundaryMs) {
|
|
828
|
+
selected.push({ ...observation, amountUsd: null });
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
selected.push(observation);
|
|
832
|
+
}
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
if (timestampMs >= boundaryMs)
|
|
836
|
+
selected.push(observation);
|
|
837
|
+
}
|
|
838
|
+
return selected;
|
|
839
|
+
}
|
|
840
|
+
function bucketPrecision(record) {
|
|
841
|
+
if (record.usageGranularity === "call" || record.usageGranularity === "invocation") {
|
|
842
|
+
return "exact";
|
|
843
|
+
}
|
|
844
|
+
return record.usageGranularity === "daily_aggregate"
|
|
845
|
+
? "daily_bucket"
|
|
846
|
+
: "unbounded_bucket";
|
|
847
|
+
}
|
|
848
|
+
function buildBilledWindows(records, asOfMs, providers) {
|
|
849
|
+
return {
|
|
850
|
+
oneDay: buildBilledWindow(records, asOfMs, 1, providers),
|
|
851
|
+
sevenDays: buildBilledWindow(records, asOfMs, 7, providers),
|
|
852
|
+
thirtyDays: buildBilledWindow(records, asOfMs, 30, providers)
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
function buildBilledWindow(records, asOfMs, days, providers) {
|
|
856
|
+
const sourceCoverage = providerIntervalCoverage(providers, asOfMs - days * DAY_MS, asOfMs);
|
|
857
|
+
const boundaryMs = asOfMs - days * DAY_MS;
|
|
858
|
+
const fullyContained = [];
|
|
859
|
+
let overlappingRecordCount = 0;
|
|
860
|
+
let boundaryLimited = false;
|
|
861
|
+
for (const record of records) {
|
|
862
|
+
const timestampMs = Date.parse(record.timestamp);
|
|
863
|
+
if (!Number.isFinite(timestampMs) || timestampMs > asOfMs)
|
|
864
|
+
continue;
|
|
865
|
+
const precision = billedBucketPrecision(record);
|
|
866
|
+
if (precision === "exact") {
|
|
867
|
+
if (timestampMs >= boundaryMs) {
|
|
868
|
+
fullyContained.push(record);
|
|
869
|
+
overlappingRecordCount += 1;
|
|
870
|
+
}
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
if (precision === "daily_bucket") {
|
|
874
|
+
const intervalEndMs = billedBucketObservedEnd(record, providers, asOfMs);
|
|
875
|
+
if (intervalEndMs === null) {
|
|
876
|
+
if (timestampMs >= boundaryMs) {
|
|
877
|
+
overlappingRecordCount += 1;
|
|
878
|
+
boundaryLimited = true;
|
|
879
|
+
}
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (intervalEndMs <= boundaryMs || timestampMs > asOfMs)
|
|
883
|
+
continue;
|
|
884
|
+
overlappingRecordCount += 1;
|
|
885
|
+
if (timestampMs < boundaryMs || intervalEndMs > asOfMs) {
|
|
886
|
+
boundaryLimited = true;
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
fullyContained.push(record);
|
|
890
|
+
}
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (timestampMs >= boundaryMs) {
|
|
894
|
+
overlappingRecordCount += 1;
|
|
895
|
+
boundaryLimited = true;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
if (overlappingRecordCount === 0) {
|
|
899
|
+
const intervalProved = sourceCoverage === "complete";
|
|
900
|
+
return {
|
|
901
|
+
amountUsd: intervalProved ? 0 : null,
|
|
902
|
+
recordCount: 0,
|
|
903
|
+
basis: "provider_billed",
|
|
904
|
+
financialEvidence: intervalProved ? "verified" : "missing",
|
|
905
|
+
coverage: intervalProved ? "complete" : "missing"
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
const coverage = windowCoverage(sourceCoverage === "complete" ? "complete" : "partial", 0, boundaryLimited);
|
|
909
|
+
const amountUsd = boundaryLimited
|
|
910
|
+
? null
|
|
911
|
+
: roundUsd(fullyContained.reduce((sum, record) => sum + (record.amountUsd ?? 0), 0));
|
|
912
|
+
return {
|
|
913
|
+
amountUsd,
|
|
914
|
+
recordCount: overlappingRecordCount,
|
|
915
|
+
basis: "provider_billed",
|
|
916
|
+
financialEvidence: amountUsd === null ? "missing" : "verified",
|
|
917
|
+
coverage
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
function billedBucketPrecision(record) {
|
|
921
|
+
if (record.usageGranularity === "call" || record.usageGranularity === "invocation") {
|
|
922
|
+
return "exact";
|
|
923
|
+
}
|
|
924
|
+
if (record.usageGranularity === "billing_bucket" ||
|
|
925
|
+
record.usageGranularity === "daily_aggregate") {
|
|
926
|
+
return "daily_bucket";
|
|
927
|
+
}
|
|
928
|
+
return "unbounded_bucket";
|
|
929
|
+
}
|
|
930
|
+
function billedBucketObservedEnd(record, providers, asOfMs) {
|
|
931
|
+
const timestampMs = Date.parse(record.timestamp);
|
|
932
|
+
const provider = activitySnapshotProviderForRecord(record);
|
|
933
|
+
const checkedAt = providers
|
|
934
|
+
.filter((candidate) => candidate.provider === provider && candidate.checkedAt !== undefined)
|
|
935
|
+
.map((candidate) => Date.parse(candidate.checkedAt))
|
|
936
|
+
.filter(Number.isFinite)
|
|
937
|
+
.sort((left, right) => right - left)[0];
|
|
938
|
+
if (checkedAt === undefined)
|
|
939
|
+
return null;
|
|
940
|
+
return Math.min(timestampMs + DAY_MS, checkedAt, asOfMs);
|
|
941
|
+
}
|
|
942
|
+
function activitySnapshotProviderForRecord(record) {
|
|
943
|
+
const provider = record.source.provider.toLowerCase();
|
|
944
|
+
if (provider === "openai")
|
|
945
|
+
return "openai";
|
|
946
|
+
if (provider === "anthropic")
|
|
947
|
+
return "anthropic";
|
|
948
|
+
if (provider === "cursor")
|
|
949
|
+
return "cursor";
|
|
950
|
+
if (provider === "github-copilot" || provider === "github" || provider === "copilot") {
|
|
951
|
+
return "github-copilot";
|
|
952
|
+
}
|
|
953
|
+
return "other";
|
|
954
|
+
}
|
|
955
|
+
function providerIntervalCoverage(providers, windowStartMs, windowEndMs) {
|
|
956
|
+
if (providers.length === 0)
|
|
957
|
+
return "missing";
|
|
958
|
+
const spansWindow = (provider) => provider.status === "complete" &&
|
|
959
|
+
provider.checkedAt !== undefined &&
|
|
960
|
+
provider.coverageStart !== undefined &&
|
|
961
|
+
provider.coverageEnd !== undefined &&
|
|
962
|
+
Date.parse(provider.coverageStart) <= windowStartMs &&
|
|
963
|
+
Date.parse(provider.coverageEnd) >= windowEndMs &&
|
|
964
|
+
Date.parse(provider.checkedAt) >= Date.parse(provider.coverageEnd);
|
|
965
|
+
if (providers.every(spansWindow))
|
|
966
|
+
return "complete";
|
|
967
|
+
const overlapsWindow = providers.some((provider) => (provider.status === "complete" || provider.status === "partial") &&
|
|
968
|
+
provider.coverageStart !== undefined &&
|
|
969
|
+
provider.coverageEnd !== undefined &&
|
|
970
|
+
Date.parse(provider.coverageStart) < windowEndMs &&
|
|
971
|
+
Date.parse(provider.coverageEnd) > windowStartMs);
|
|
972
|
+
return overlapsWindow ? "partial" : "missing";
|
|
973
|
+
}
|
|
974
|
+
function buildOverage(billedRecords, overageIds, asOfMs) {
|
|
975
|
+
const records = billedRecords.filter((record) => overageIds.has(record.id) &&
|
|
976
|
+
typeof record.amountUsd === "number" &&
|
|
977
|
+
record.amountUsd > 0 &&
|
|
978
|
+
inRollingWindow(record.timestamp, asOfMs, 30));
|
|
979
|
+
if (records.length === 0)
|
|
980
|
+
return null;
|
|
981
|
+
return {
|
|
982
|
+
amountUsd: roundUsd(records.reduce((sum, record) => sum + (record.amountUsd ?? 0), 0)),
|
|
983
|
+
currency: "USD",
|
|
984
|
+
basis: "provider_billed",
|
|
985
|
+
financialEvidence: "verified",
|
|
986
|
+
alertEligible: true,
|
|
987
|
+
recordCount: records.length
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
function buildCoverage(records, scans, providers, pricingAsOf, conflictingIds) {
|
|
991
|
+
const priced = records.filter((record) => typeof record.amountUsd === "number").length;
|
|
992
|
+
const unpriced = records.length - priced;
|
|
993
|
+
const failed = scans.some((scan) => scan.directoryStatus === "unreadable") ||
|
|
994
|
+
providers.some((provider) => provider.status === "error" || provider.validationCoverage === "failed");
|
|
995
|
+
const partial = conflictingIds > 0 || unpriced > 0 ||
|
|
996
|
+
scans.some((scan) => scan.directoryStatus === "missing" ||
|
|
997
|
+
scan.malformedLines > 0 || scan.unreadableFiles > 0 ||
|
|
998
|
+
scan.unsupportedUsageSnapshots > 0 ||
|
|
999
|
+
scan.jsonlValidationCoverage !== "complete") ||
|
|
1000
|
+
providers.some((provider) => provider.status === "partial" || provider.status === "unavailable");
|
|
1001
|
+
const checked = scans.length > 0 || providers.length > 0;
|
|
1002
|
+
const validationStatus = failed
|
|
1003
|
+
? "failed"
|
|
1004
|
+
: partial
|
|
1005
|
+
? "partial"
|
|
1006
|
+
: checked
|
|
1007
|
+
? "complete"
|
|
1008
|
+
: "not_checked";
|
|
1009
|
+
return activitySnapshotCoverageSchema.parse({
|
|
1010
|
+
agents: scans.map((scan) => ({
|
|
1011
|
+
agent: scan.agent,
|
|
1012
|
+
directoryStatus: scan.directoryStatus,
|
|
1013
|
+
filesDiscovered: scan.filesDiscovered,
|
|
1014
|
+
filesParsed: scan.filesParsed,
|
|
1015
|
+
malformedLines: scan.malformedLines,
|
|
1016
|
+
unreadableFiles: scan.unreadableFiles,
|
|
1017
|
+
unsupportedUsageSnapshots: scan.unsupportedUsageSnapshots,
|
|
1018
|
+
filesSkippedBeforeWindow: scan.filesSkippedBeforeWindow ?? 0,
|
|
1019
|
+
filesReadFinancially: scan.filesReadFinancially ?? 0,
|
|
1020
|
+
bytesSkippedAsNonFinancialHistory: scan.bytesSkippedAsNonFinancialHistory ?? 0,
|
|
1021
|
+
nonFinancialLinesPrefiltered: scan.nonFinancialLinesPrefiltered ?? 0,
|
|
1022
|
+
nonFinancialBytesPrefiltered: scan.nonFinancialBytesPrefiltered ?? 0,
|
|
1023
|
+
jsonlValidationCoverage: scan.jsonlValidationCoverage ?? "not_reported"
|
|
1024
|
+
})),
|
|
1025
|
+
providers: providers.map((provider) => ({
|
|
1026
|
+
...provider,
|
|
1027
|
+
checkedAt: provider.checkedAt ?? null,
|
|
1028
|
+
latestEvidenceAt: provider.latestEvidenceAt ?? null,
|
|
1029
|
+
coverageStart: provider.coverageStart ?? null,
|
|
1030
|
+
coverageEnd: provider.coverageEnd ?? null
|
|
1031
|
+
})),
|
|
1032
|
+
recordsParsed: records.length,
|
|
1033
|
+
recordsPriced: priced,
|
|
1034
|
+
recordsUnpriced: unpriced,
|
|
1035
|
+
validationStatus,
|
|
1036
|
+
pricingAsOf,
|
|
1037
|
+
networkUploaded: false
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
function normalizeAgentScans(scans) {
|
|
1041
|
+
const latest = new Map();
|
|
1042
|
+
for (const scan of scans) {
|
|
1043
|
+
if (isSnapshotAgent(scan.agent))
|
|
1044
|
+
latest.set(scan.agent, scan);
|
|
1045
|
+
}
|
|
1046
|
+
return [...latest.values()].sort((left, right) => left.agent.localeCompare(right.agent));
|
|
1047
|
+
}
|
|
1048
|
+
function normalizeProviderCoverage(providers, generatedAtMs) {
|
|
1049
|
+
const latest = new Map();
|
|
1050
|
+
for (const provider of providers) {
|
|
1051
|
+
if (!activitySnapshotProviderValues.includes(provider.provider))
|
|
1052
|
+
continue;
|
|
1053
|
+
if (provider.checkedAt !== undefined)
|
|
1054
|
+
parseTimestamp(provider.checkedAt, "provider checkedAt");
|
|
1055
|
+
if (provider.latestEvidenceAt !== undefined) {
|
|
1056
|
+
parseTimestamp(provider.latestEvidenceAt, "provider latestEvidenceAt");
|
|
1057
|
+
}
|
|
1058
|
+
if (provider.coverageStart !== undefined) {
|
|
1059
|
+
parseTimestamp(provider.coverageStart, "provider coverageStart");
|
|
1060
|
+
}
|
|
1061
|
+
if (provider.coverageEnd !== undefined) {
|
|
1062
|
+
parseTimestamp(provider.coverageEnd, "provider coverageEnd");
|
|
1063
|
+
}
|
|
1064
|
+
if ((provider.coverageStart === undefined) !== (provider.coverageEnd === undefined)) {
|
|
1065
|
+
throw new Error("Provider coverageStart and coverageEnd must be supplied together.");
|
|
1066
|
+
}
|
|
1067
|
+
if ((provider.latestEvidenceAt !== undefined || provider.coverageStart !== undefined) &&
|
|
1068
|
+
provider.checkedAt === undefined) {
|
|
1069
|
+
throw new Error("Provider evidence timestamps require checkedAt.");
|
|
1070
|
+
}
|
|
1071
|
+
if (provider.checkedAt && provider.latestEvidenceAt &&
|
|
1072
|
+
Date.parse(provider.latestEvidenceAt) > Date.parse(provider.checkedAt)) {
|
|
1073
|
+
throw new Error("Provider latestEvidenceAt must not be newer than checkedAt.");
|
|
1074
|
+
}
|
|
1075
|
+
if (provider.coverageStart && provider.coverageEnd &&
|
|
1076
|
+
Date.parse(provider.coverageStart) > Date.parse(provider.coverageEnd)) {
|
|
1077
|
+
throw new Error("Provider coverageEnd must not precede coverageStart.");
|
|
1078
|
+
}
|
|
1079
|
+
if (provider.coverageEnd && provider.checkedAt &&
|
|
1080
|
+
Date.parse(provider.coverageEnd) > Date.parse(provider.checkedAt)) {
|
|
1081
|
+
throw new Error("Provider coverageEnd must not be newer than checkedAt.");
|
|
1082
|
+
}
|
|
1083
|
+
if (provider.checkedAt && Date.parse(provider.checkedAt) > generatedAtMs) {
|
|
1084
|
+
throw new Error("Provider checkedAt must not be newer than generatedAt.");
|
|
1085
|
+
}
|
|
1086
|
+
if (provider.latestEvidenceAt && provider.coverageStart && provider.coverageEnd &&
|
|
1087
|
+
(Date.parse(provider.latestEvidenceAt) < Date.parse(provider.coverageStart) ||
|
|
1088
|
+
Date.parse(provider.latestEvidenceAt) > Date.parse(provider.coverageEnd))) {
|
|
1089
|
+
throw new Error("Provider latestEvidenceAt must fall inside its coverage interval.");
|
|
1090
|
+
}
|
|
1091
|
+
latest.set(provider.provider, provider);
|
|
1092
|
+
}
|
|
1093
|
+
return [...latest.values()].sort((left, right) => left.provider.localeCompare(right.provider));
|
|
1094
|
+
}
|
|
1095
|
+
function localCoverageForAgent(agent, scans) {
|
|
1096
|
+
const scan = scans.find((candidate) => candidate.agent === agent);
|
|
1097
|
+
if (!scan)
|
|
1098
|
+
return "missing";
|
|
1099
|
+
if (scan.directoryStatus !== "readable")
|
|
1100
|
+
return "missing";
|
|
1101
|
+
return scan.malformedLines > 0 || scan.unreadableFiles > 0 || scan.unsupportedUsageSnapshots > 0
|
|
1102
|
+
|| scan.jsonlValidationCoverage !== "complete"
|
|
1103
|
+
? "partial"
|
|
1104
|
+
: "complete";
|
|
1105
|
+
}
|
|
1106
|
+
function localCoverageForRecords(records, scans, fallbackAgents = []) {
|
|
1107
|
+
const agents = new Set([
|
|
1108
|
+
...records.map((record) => record.agentId).filter(isSnapshotAgent),
|
|
1109
|
+
...fallbackAgents
|
|
1110
|
+
]);
|
|
1111
|
+
if (agents.size === 0) {
|
|
1112
|
+
return "missing";
|
|
1113
|
+
}
|
|
1114
|
+
const statuses = [...agents].map((agent) => localCoverageForAgent(agent, scans));
|
|
1115
|
+
if (statuses.every((status) => status === "complete"))
|
|
1116
|
+
return "complete";
|
|
1117
|
+
if (statuses.some((status) => status !== "missing"))
|
|
1118
|
+
return "partial";
|
|
1119
|
+
return "missing";
|
|
1120
|
+
}
|
|
1121
|
+
function unresolvedCoverageForRecords(records, scans, providers) {
|
|
1122
|
+
const localRecords = records.filter((record) => record.providerCostType === "local_agent_logs");
|
|
1123
|
+
const providerRecords = records.filter((record) => record.providerCostType !== "local_agent_logs");
|
|
1124
|
+
const statuses = [];
|
|
1125
|
+
if (localRecords.length > 0)
|
|
1126
|
+
statuses.push(localCoverageForRecords(localRecords, scans));
|
|
1127
|
+
if (providerRecords.length > 0)
|
|
1128
|
+
statuses.push(providerCoverageCompleteness(providers));
|
|
1129
|
+
if (statuses.length === 0)
|
|
1130
|
+
return "missing";
|
|
1131
|
+
if (statuses.every((status) => status === "complete"))
|
|
1132
|
+
return "complete";
|
|
1133
|
+
if (statuses.some((status) => status !== "missing"))
|
|
1134
|
+
return "partial";
|
|
1135
|
+
return "missing";
|
|
1136
|
+
}
|
|
1137
|
+
function providerCoverageCompleteness(providers) {
|
|
1138
|
+
if (providers.length === 0)
|
|
1139
|
+
return "missing";
|
|
1140
|
+
if (providers.every((provider) => provider.status === "complete"))
|
|
1141
|
+
return "complete";
|
|
1142
|
+
if (providers.some((provider) => provider.status === "complete" || provider.status === "partial"))
|
|
1143
|
+
return "partial";
|
|
1144
|
+
return "missing";
|
|
1145
|
+
}
|
|
1146
|
+
function windowCoverage(sourceCoverage, unpriced, boundaryLimited = false) {
|
|
1147
|
+
if (sourceCoverage === "missing")
|
|
1148
|
+
return "missing";
|
|
1149
|
+
return sourceCoverage === "partial" || unpriced > 0 || boundaryLimited
|
|
1150
|
+
? "partial"
|
|
1151
|
+
: "complete";
|
|
1152
|
+
}
|
|
1153
|
+
function deduplicateRecords(records) {
|
|
1154
|
+
const byId = new Map();
|
|
1155
|
+
for (const record of records)
|
|
1156
|
+
byId.set(record.id, [...(byId.get(record.id) ?? []), record]);
|
|
1157
|
+
const output = [];
|
|
1158
|
+
let conflictingIds = 0;
|
|
1159
|
+
for (const group of byId.values()) {
|
|
1160
|
+
const signatures = new Set(group.map(financialRecordSignature));
|
|
1161
|
+
if (signatures.size > 1) {
|
|
1162
|
+
conflictingIds += 1;
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
output.push(group[0]);
|
|
1166
|
+
}
|
|
1167
|
+
return { records: output, conflictingIds };
|
|
1168
|
+
}
|
|
1169
|
+
function financialRecordSignature(record) {
|
|
1170
|
+
return JSON.stringify([
|
|
1171
|
+
record.timestamp,
|
|
1172
|
+
record.agentId ?? null,
|
|
1173
|
+
record.amountUsd,
|
|
1174
|
+
record.costConfidence,
|
|
1175
|
+
record.providerCostType ?? null,
|
|
1176
|
+
record.inputTokens,
|
|
1177
|
+
record.outputTokens
|
|
1178
|
+
]);
|
|
1179
|
+
}
|
|
1180
|
+
function validRecordTimestampInHorizon(record, horizonStartMs, asOfMs) {
|
|
1181
|
+
const timestamp = Date.parse(record.timestamp);
|
|
1182
|
+
if (!Number.isFinite(timestamp) || timestamp > asOfMs)
|
|
1183
|
+
return false;
|
|
1184
|
+
if (record.usageGranularity === "daily_aggregate" ||
|
|
1185
|
+
record.usageGranularity === "billing_bucket") {
|
|
1186
|
+
return timestamp + DAY_MS > horizonStartMs;
|
|
1187
|
+
}
|
|
1188
|
+
return timestamp >= horizonStartMs;
|
|
1189
|
+
}
|
|
1190
|
+
function validCallAtOrBefore(call, asOfMs) {
|
|
1191
|
+
const timestamp = Date.parse(call.timestamp);
|
|
1192
|
+
return Number.isFinite(timestamp) && timestamp <= asOfMs;
|
|
1193
|
+
}
|
|
1194
|
+
function validCallAtOrAfter(call, horizonStartMs) {
|
|
1195
|
+
const timestamp = Date.parse(call.timestamp);
|
|
1196
|
+
return Number.isFinite(timestamp) && timestamp >= horizonStartMs;
|
|
1197
|
+
}
|
|
1198
|
+
function inRollingWindow(timestamp, asOfMs, days) {
|
|
1199
|
+
const value = Date.parse(timestamp);
|
|
1200
|
+
return Number.isFinite(value) && value >= asOfMs - days * DAY_MS && value <= asOfMs;
|
|
1201
|
+
}
|
|
1202
|
+
function isSnapshotAgent(value) {
|
|
1203
|
+
return typeof value === "string" && activitySnapshotAgentValues.includes(value);
|
|
1204
|
+
}
|
|
1205
|
+
function isKnownPlanId(value) {
|
|
1206
|
+
return typeof value === "string" && activitySnapshotPlanIdValues.includes(value);
|
|
1207
|
+
}
|
|
1208
|
+
function parseTimestamp(value, label) {
|
|
1209
|
+
const parsed = Date.parse(value);
|
|
1210
|
+
if (!Number.isFinite(parsed))
|
|
1211
|
+
throw new Error(`${label} must be a valid ISO timestamp.`);
|
|
1212
|
+
return parsed;
|
|
1213
|
+
}
|
|
1214
|
+
function roundUsd(value) {
|
|
1215
|
+
return Math.round((value + Number.EPSILON) * 1_000_000) / 1_000_000;
|
|
1216
|
+
}
|
|
1217
|
+
function roundPercent(value) {
|
|
1218
|
+
return Math.round(Math.max(0, Math.min(100, value)) * 10) / 10;
|
|
1219
|
+
}
|
|
1220
|
+
//# sourceMappingURL=activitySnapshot.js.map
|