@agent-finops/core 0.5.8 → 0.6.0
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/agentInventory.d.ts +11 -14
- package/dist/agentInventory.js +180 -46
- package/dist/analyze.js +165 -113
- package/dist/contextHealth.d.ts +10 -1
- package/dist/contextHealth.js +216 -44
- package/dist/cutList.d.ts +9 -3
- package/dist/cutList.js +99 -41
- package/dist/deadContext.d.ts +8 -4
- package/dist/deadContext.js +83 -26
- package/dist/discovery.d.ts +6 -2
- package/dist/discovery.js +36 -14
- package/dist/glance.d.ts +9 -2
- package/dist/glance.js +107 -24
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/insights.js +53 -30
- package/dist/localAgentLogs.d.ts +80 -2
- package/dist/localAgentLogs.js +480 -88
- package/dist/modelPricing.js +0 -1
- package/dist/providerConnectors.d.ts +3 -2
- package/dist/providerConnectors.js +673 -89
- package/dist/sampleData.js +32 -4
- package/dist/schema.d.ts +105 -27
- package/dist/schema.js +110 -2
- 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/dist/toolInvocations.d.ts +47 -18
- package/dist/toolInvocations.js +200 -48
- package/package.json +1 -1
- package/samples/anthropic-usage.csv +4 -4
- package/samples/openai-usage.csv +7 -7
package/dist/sampleData.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import {
|
|
4
|
+
import { downgradeSampleUsageEvidence, parseUsageRecord } from "./schema.js";
|
|
5
5
|
// One level up from dist/ = the package root, where samples/ ships (see
|
|
6
6
|
// "files" in package.json). Must survive npm installation — never resolve
|
|
7
7
|
// relative to the repo.
|
|
@@ -12,7 +12,8 @@ export const sampleFiles = [
|
|
|
12
12
|
];
|
|
13
13
|
export async function loadSampleUsageData(rootDir = packageRoot) {
|
|
14
14
|
const records = await Promise.all(sampleFiles.map((file) => loadUsageCsv(resolve(rootDir, file))));
|
|
15
|
-
return records.flat()
|
|
15
|
+
return downgradeSampleUsageEvidence(records.flat())
|
|
16
|
+
.sort((left, right) => left.timestamp.localeCompare(right.timestamp));
|
|
16
17
|
}
|
|
17
18
|
async function loadUsageCsv(path) {
|
|
18
19
|
const contents = await readFile(path, "utf8");
|
|
@@ -28,7 +29,7 @@ export function parseUsageCsv(contents) {
|
|
|
28
29
|
return recordLines.map((line) => {
|
|
29
30
|
const values = line.split(",");
|
|
30
31
|
const row = Object.fromEntries(headers.map((header, index) => [header, values[index]?.trim() ?? ""]));
|
|
31
|
-
return
|
|
32
|
+
return parseUsageRecord({
|
|
32
33
|
id: row.id,
|
|
33
34
|
timestamp: row.timestamp,
|
|
34
35
|
source: {
|
|
@@ -49,11 +50,38 @@ export function parseUsageCsv(contents) {
|
|
|
49
50
|
userId: optionalValue(row.user_id),
|
|
50
51
|
workspaceId: optionalValue(row.workspace_id),
|
|
51
52
|
apiKeyId: optionalValue(row.api_key_id),
|
|
52
|
-
|
|
53
|
+
providerCostType: optionalValue(row.provider_cost_type),
|
|
54
|
+
operation: optionalValue(row.operation),
|
|
55
|
+
usageGranularity: optionalValue(row.usage_granularity),
|
|
56
|
+
workloadSemantics: workloadSemantics(row)
|
|
53
57
|
});
|
|
54
58
|
});
|
|
55
59
|
}
|
|
56
60
|
function optionalValue(value) {
|
|
57
61
|
return value === "" ? undefined : value;
|
|
58
62
|
}
|
|
63
|
+
function workloadSemantics(row) {
|
|
64
|
+
const stableInputFingerprint = optionalValue(row.stable_input_fingerprint);
|
|
65
|
+
const batchEligible = optionalBoolean(row.batch_eligible);
|
|
66
|
+
const downgradeSafe = optionalBoolean(row.downgrade_safe);
|
|
67
|
+
if (stableInputFingerprint === undefined &&
|
|
68
|
+
batchEligible === undefined &&
|
|
69
|
+
downgradeSafe === undefined) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
...(stableInputFingerprint ? { stableInputFingerprint } : {}),
|
|
74
|
+
...(batchEligible !== undefined ? { batchEligible } : {}),
|
|
75
|
+
...(downgradeSafe !== undefined ? { downgradeSafe } : {})
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function optionalBoolean(value) {
|
|
79
|
+
if (value === undefined || value === "")
|
|
80
|
+
return undefined;
|
|
81
|
+
if (value === "true")
|
|
82
|
+
return true;
|
|
83
|
+
if (value === "false")
|
|
84
|
+
return false;
|
|
85
|
+
throw new Error(`Expected true/false CSV value, received ${JSON.stringify(value)}.`);
|
|
86
|
+
}
|
|
59
87
|
//# sourceMappingURL=sampleData.js.map
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const costConfidenceValues: readonly ["verified", "estimated", "detected_unverified", "missing"];
|
|
3
3
|
export declare const costConfidenceSchema: z.ZodEnum<{
|
|
4
|
-
verified: "verified";
|
|
5
4
|
estimated: "estimated";
|
|
5
|
+
verified: "verified";
|
|
6
6
|
detected_unverified: "detected_unverified";
|
|
7
7
|
missing: "missing";
|
|
8
8
|
}>;
|
|
@@ -12,14 +12,45 @@ export declare const spendSourceSchema: z.ZodObject<{
|
|
|
12
12
|
name: z.ZodString;
|
|
13
13
|
provider: z.ZodString;
|
|
14
14
|
confidence: z.ZodEnum<{
|
|
15
|
-
verified: "verified";
|
|
16
15
|
estimated: "estimated";
|
|
16
|
+
verified: "verified";
|
|
17
17
|
detected_unverified: "detected_unverified";
|
|
18
18
|
missing: "missing";
|
|
19
19
|
}>;
|
|
20
20
|
observedFrom: z.ZodString;
|
|
21
21
|
}, z.core.$strip>;
|
|
22
22
|
export type SpendSource = z.infer<typeof spendSourceSchema>;
|
|
23
|
+
/**
|
|
24
|
+
* What one normalized usage record represents.
|
|
25
|
+
*
|
|
26
|
+
* Only `call` and `invocation` are precise enough to support per-workload
|
|
27
|
+
* counterfactuals such as model routing, result caching, Batch API moves, or
|
|
28
|
+
* prompt trimming. The remaining values are still useful financial evidence,
|
|
29
|
+
* but must never be silently treated as individual calls.
|
|
30
|
+
*/
|
|
31
|
+
export declare const usageGranularityValues: readonly ["call", "invocation", "session", "daily_aggregate", "usage_bucket", "billing_bucket", "seat", "user_aggregate"];
|
|
32
|
+
export declare const usageGranularitySchema: z.ZodEnum<{
|
|
33
|
+
call: "call";
|
|
34
|
+
invocation: "invocation";
|
|
35
|
+
session: "session";
|
|
36
|
+
daily_aggregate: "daily_aggregate";
|
|
37
|
+
usage_bucket: "usage_bucket";
|
|
38
|
+
billing_bucket: "billing_bucket";
|
|
39
|
+
seat: "seat";
|
|
40
|
+
user_aggregate: "user_aggregate";
|
|
41
|
+
}>;
|
|
42
|
+
export type UsageGranularity = z.infer<typeof usageGranularitySchema>;
|
|
43
|
+
/**
|
|
44
|
+
* Explicit adapter attestations for workload-specific optimization advice.
|
|
45
|
+
* These fields are intentionally absent by default: an operation label alone
|
|
46
|
+
* does not prove identical inputs, latency tolerance, or downgrade safety.
|
|
47
|
+
*/
|
|
48
|
+
export declare const workloadSemanticsSchema: z.ZodObject<{
|
|
49
|
+
stableInputFingerprint: z.ZodOptional<z.ZodString>;
|
|
50
|
+
batchEligible: z.ZodOptional<z.ZodBoolean>;
|
|
51
|
+
downgradeSafe: z.ZodOptional<z.ZodBoolean>;
|
|
52
|
+
}, z.core.$strict>;
|
|
53
|
+
export type WorkloadSemantics = z.infer<typeof workloadSemanticsSchema>;
|
|
23
54
|
export declare const usageRecordSchema: z.ZodObject<{
|
|
24
55
|
id: z.ZodString;
|
|
25
56
|
timestamp: z.ZodString;
|
|
@@ -28,8 +59,8 @@ export declare const usageRecordSchema: z.ZodObject<{
|
|
|
28
59
|
name: z.ZodString;
|
|
29
60
|
provider: z.ZodString;
|
|
30
61
|
confidence: z.ZodEnum<{
|
|
31
|
-
verified: "verified";
|
|
32
62
|
estimated: "estimated";
|
|
63
|
+
verified: "verified";
|
|
33
64
|
detected_unverified: "detected_unverified";
|
|
34
65
|
missing: "missing";
|
|
35
66
|
}>;
|
|
@@ -40,8 +71,8 @@ export declare const usageRecordSchema: z.ZodObject<{
|
|
|
40
71
|
outputTokens: z.ZodNumber;
|
|
41
72
|
amountUsd: z.ZodNullable<z.ZodNumber>;
|
|
42
73
|
costConfidence: z.ZodEnum<{
|
|
43
|
-
verified: "verified";
|
|
44
74
|
estimated: "estimated";
|
|
75
|
+
verified: "verified";
|
|
45
76
|
detected_unverified: "detected_unverified";
|
|
46
77
|
missing: "missing";
|
|
47
78
|
}>;
|
|
@@ -54,14 +85,59 @@ export declare const usageRecordSchema: z.ZodObject<{
|
|
|
54
85
|
quantity: z.ZodOptional<z.ZodNumber>;
|
|
55
86
|
agentId: z.ZodOptional<z.ZodString>;
|
|
56
87
|
operation: z.ZodOptional<z.ZodString>;
|
|
88
|
+
usageGranularity: z.ZodOptional<z.ZodEnum<{
|
|
89
|
+
call: "call";
|
|
90
|
+
invocation: "invocation";
|
|
91
|
+
session: "session";
|
|
92
|
+
daily_aggregate: "daily_aggregate";
|
|
93
|
+
usage_bucket: "usage_bucket";
|
|
94
|
+
billing_bucket: "billing_bucket";
|
|
95
|
+
seat: "seat";
|
|
96
|
+
user_aggregate: "user_aggregate";
|
|
97
|
+
}>>;
|
|
98
|
+
workloadSemantics: z.ZodOptional<z.ZodObject<{
|
|
99
|
+
stableInputFingerprint: z.ZodOptional<z.ZodString>;
|
|
100
|
+
batchEligible: z.ZodOptional<z.ZodBoolean>;
|
|
101
|
+
downgradeSafe: z.ZodOptional<z.ZodBoolean>;
|
|
102
|
+
}, z.core.$strict>>;
|
|
57
103
|
}, z.core.$strip>;
|
|
58
104
|
export type UsageRecord = z.infer<typeof usageRecordSchema>;
|
|
105
|
+
/**
|
|
106
|
+
* True only when a record can honestly ground a modeled workload change.
|
|
107
|
+
*
|
|
108
|
+
* A provider billing row, usage bucket, seat, user total, or unlabelled legacy
|
|
109
|
+
* row may describe real spend, but it does not prove that one row was one
|
|
110
|
+
* optimizable call. Requiring both explicit call/invocation provenance and a
|
|
111
|
+
* named operation keeps aggregate connector data in accounting views without
|
|
112
|
+
* manufacturing per-call savings advice from it.
|
|
113
|
+
*/
|
|
114
|
+
export declare function hasModeledWorkloadEvidence(record: UsageRecord): boolean;
|
|
115
|
+
/** Explicit one-call / one-model-invocation provenance, never inferred. */
|
|
116
|
+
export declare function hasCallLevelProvenance(record: UsageRecord): boolean;
|
|
117
|
+
/** A positive priced observation from which a dollar counterfactual can be modeled. */
|
|
118
|
+
export declare function hasPricedEvidence(record: UsageRecord): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Recognize the bundled demo records written by releases that predate the
|
|
121
|
+
* persisted `mode` field. The marker is deliberately narrow: every record
|
|
122
|
+
* must come from the shipped sample CSV and use a sample source identifier.
|
|
123
|
+
* This lets newer clients recover the demo boundary without guessing that an
|
|
124
|
+
* arbitrary unlabeled state is real local or connected evidence.
|
|
125
|
+
*/
|
|
126
|
+
export declare function isBundledSampleUsage(records: UsageRecord[]): boolean;
|
|
127
|
+
/** A declared demo/sample mode can never carry proof-level financial labels. */
|
|
128
|
+
export declare function downgradeSampleUsageEvidence(records: UsageRecord[]): UsageRecord[];
|
|
129
|
+
/**
|
|
130
|
+
* Stable financial cohort for period-over-period comparisons. Missing shape
|
|
131
|
+
* provenance returns `undefined`; unknown rows must not be pooled into a fake
|
|
132
|
+
* comparable series.
|
|
133
|
+
*/
|
|
134
|
+
export declare function spendComparisonKey(record: UsageRecord): string | undefined;
|
|
59
135
|
export declare const attributionCandidateSchema: z.ZodObject<{
|
|
60
136
|
entityType: z.ZodEnum<{
|
|
61
137
|
user: "user";
|
|
62
138
|
project: "project";
|
|
63
|
-
client: "client";
|
|
64
139
|
agent: "agent";
|
|
140
|
+
client: "client";
|
|
65
141
|
workspace: "workspace";
|
|
66
142
|
api_key: "api_key";
|
|
67
143
|
}>;
|
|
@@ -76,8 +152,8 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
76
152
|
entityType: z.ZodEnum<{
|
|
77
153
|
user: "user";
|
|
78
154
|
project: "project";
|
|
79
|
-
client: "client";
|
|
80
155
|
agent: "agent";
|
|
156
|
+
client: "client";
|
|
81
157
|
workspace: "workspace";
|
|
82
158
|
api_key: "api_key";
|
|
83
159
|
}>;
|
|
@@ -89,8 +165,8 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
89
165
|
entityType: z.ZodEnum<{
|
|
90
166
|
user: "user";
|
|
91
167
|
project: "project";
|
|
92
|
-
client: "client";
|
|
93
168
|
agent: "agent";
|
|
169
|
+
client: "client";
|
|
94
170
|
workspace: "workspace";
|
|
95
171
|
api_key: "api_key";
|
|
96
172
|
}>;
|
|
@@ -99,10 +175,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
99
175
|
evidence: z.ZodArray<z.ZodString>;
|
|
100
176
|
}, z.core.$strip>>;
|
|
101
177
|
status: z.ZodEnum<{
|
|
102
|
-
unmapped: "unmapped";
|
|
103
178
|
auto_mapped: "auto_mapped";
|
|
104
179
|
needs_confirmation: "needs_confirmation";
|
|
105
180
|
needs_question: "needs_question";
|
|
181
|
+
unmapped: "unmapped";
|
|
106
182
|
}>;
|
|
107
183
|
evidence: z.ZodArray<z.ZodString>;
|
|
108
184
|
}, z.core.$strip>;
|
|
@@ -112,8 +188,8 @@ export declare const spendBreakdownEntrySchema: z.ZodObject<{
|
|
|
112
188
|
amountUsd: z.ZodNumber;
|
|
113
189
|
recordCount: z.ZodNumber;
|
|
114
190
|
confidence: z.ZodEnum<{
|
|
115
|
-
verified: "verified";
|
|
116
191
|
estimated: "estimated";
|
|
192
|
+
verified: "verified";
|
|
117
193
|
detected_unverified: "detected_unverified";
|
|
118
194
|
missing: "missing";
|
|
119
195
|
}>;
|
|
@@ -125,12 +201,13 @@ export declare const spendAnomalySchema: z.ZodObject<{
|
|
|
125
201
|
week_over_week_spike: "week_over_week_spike";
|
|
126
202
|
}>;
|
|
127
203
|
key: z.ZodString;
|
|
204
|
+
comparisonKey: z.ZodOptional<z.ZodString>;
|
|
128
205
|
previousAmountUsd: z.ZodNumber;
|
|
129
206
|
currentAmountUsd: z.ZodNumber;
|
|
130
207
|
multiplier: z.ZodNumber;
|
|
131
208
|
confidence: z.ZodEnum<{
|
|
132
|
-
verified: "verified";
|
|
133
209
|
estimated: "estimated";
|
|
210
|
+
verified: "verified";
|
|
134
211
|
detected_unverified: "detected_unverified";
|
|
135
212
|
missing: "missing";
|
|
136
213
|
}>;
|
|
@@ -146,8 +223,8 @@ export declare const workflowWatchEntrySchema: z.ZodObject<{
|
|
|
146
223
|
shareOfSpend: z.ZodNumber;
|
|
147
224
|
recordCount: z.ZodNumber;
|
|
148
225
|
confidence: z.ZodEnum<{
|
|
149
|
-
verified: "verified";
|
|
150
226
|
estimated: "estimated";
|
|
227
|
+
verified: "verified";
|
|
151
228
|
detected_unverified: "detected_unverified";
|
|
152
229
|
missing: "missing";
|
|
153
230
|
}>;
|
|
@@ -171,8 +248,8 @@ export declare const recommendationSchema: z.ZodObject<{
|
|
|
171
248
|
}>;
|
|
172
249
|
estimatedImpactUsd: z.ZodNumber;
|
|
173
250
|
confidence: z.ZodEnum<{
|
|
174
|
-
verified: "verified";
|
|
175
251
|
estimated: "estimated";
|
|
252
|
+
verified: "verified";
|
|
176
253
|
detected_unverified: "detected_unverified";
|
|
177
254
|
missing: "missing";
|
|
178
255
|
}>;
|
|
@@ -216,8 +293,8 @@ export declare const spendInsightSchema: z.ZodObject<{
|
|
|
216
293
|
affectedModels: z.ZodArray<z.ZodString>;
|
|
217
294
|
estimatedImpactUsd: z.ZodNumber;
|
|
218
295
|
confidence: z.ZodEnum<{
|
|
219
|
-
verified: "verified";
|
|
220
296
|
estimated: "estimated";
|
|
297
|
+
verified: "verified";
|
|
221
298
|
detected_unverified: "detected_unverified";
|
|
222
299
|
missing: "missing";
|
|
223
300
|
}>;
|
|
@@ -229,14 +306,14 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
229
306
|
totalUsd: z.ZodNumber;
|
|
230
307
|
recordCount: z.ZodNumber;
|
|
231
308
|
confidence: z.ZodEnum<{
|
|
232
|
-
verified: "verified";
|
|
233
309
|
estimated: "estimated";
|
|
310
|
+
verified: "verified";
|
|
234
311
|
detected_unverified: "detected_unverified";
|
|
235
312
|
missing: "missing";
|
|
236
313
|
}>;
|
|
237
314
|
confidenceBreakdown: z.ZodRecord<z.ZodEnum<{
|
|
238
|
-
verified: "verified";
|
|
239
315
|
estimated: "estimated";
|
|
316
|
+
verified: "verified";
|
|
240
317
|
detected_unverified: "detected_unverified";
|
|
241
318
|
missing: "missing";
|
|
242
319
|
}>, z.ZodNumber>;
|
|
@@ -245,8 +322,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
245
322
|
amountUsd: z.ZodNumber;
|
|
246
323
|
recordCount: z.ZodNumber;
|
|
247
324
|
confidence: z.ZodEnum<{
|
|
248
|
-
verified: "verified";
|
|
249
325
|
estimated: "estimated";
|
|
326
|
+
verified: "verified";
|
|
250
327
|
detected_unverified: "detected_unverified";
|
|
251
328
|
missing: "missing";
|
|
252
329
|
}>;
|
|
@@ -256,8 +333,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
256
333
|
amountUsd: z.ZodNumber;
|
|
257
334
|
recordCount: z.ZodNumber;
|
|
258
335
|
confidence: z.ZodEnum<{
|
|
259
|
-
verified: "verified";
|
|
260
336
|
estimated: "estimated";
|
|
337
|
+
verified: "verified";
|
|
261
338
|
detected_unverified: "detected_unverified";
|
|
262
339
|
missing: "missing";
|
|
263
340
|
}>;
|
|
@@ -267,8 +344,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
267
344
|
amountUsd: z.ZodNumber;
|
|
268
345
|
recordCount: z.ZodNumber;
|
|
269
346
|
confidence: z.ZodEnum<{
|
|
270
|
-
verified: "verified";
|
|
271
347
|
estimated: "estimated";
|
|
348
|
+
verified: "verified";
|
|
272
349
|
detected_unverified: "detected_unverified";
|
|
273
350
|
missing: "missing";
|
|
274
351
|
}>;
|
|
@@ -278,8 +355,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
278
355
|
amountUsd: z.ZodNumber;
|
|
279
356
|
recordCount: z.ZodNumber;
|
|
280
357
|
confidence: z.ZodEnum<{
|
|
281
|
-
verified: "verified";
|
|
282
358
|
estimated: "estimated";
|
|
359
|
+
verified: "verified";
|
|
283
360
|
detected_unverified: "detected_unverified";
|
|
284
361
|
missing: "missing";
|
|
285
362
|
}>;
|
|
@@ -289,8 +366,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
289
366
|
amountUsd: z.ZodNumber;
|
|
290
367
|
recordCount: z.ZodNumber;
|
|
291
368
|
confidence: z.ZodEnum<{
|
|
292
|
-
verified: "verified";
|
|
293
369
|
estimated: "estimated";
|
|
370
|
+
verified: "verified";
|
|
294
371
|
detected_unverified: "detected_unverified";
|
|
295
372
|
missing: "missing";
|
|
296
373
|
}>;
|
|
@@ -300,8 +377,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
300
377
|
amountUsd: z.ZodNumber;
|
|
301
378
|
recordCount: z.ZodNumber;
|
|
302
379
|
confidence: z.ZodEnum<{
|
|
303
|
-
verified: "verified";
|
|
304
380
|
estimated: "estimated";
|
|
381
|
+
verified: "verified";
|
|
305
382
|
detected_unverified: "detected_unverified";
|
|
306
383
|
missing: "missing";
|
|
307
384
|
}>;
|
|
@@ -311,8 +388,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
311
388
|
amountUsd: z.ZodNumber;
|
|
312
389
|
recordCount: z.ZodNumber;
|
|
313
390
|
confidence: z.ZodEnum<{
|
|
314
|
-
verified: "verified";
|
|
315
391
|
estimated: "estimated";
|
|
392
|
+
verified: "verified";
|
|
316
393
|
detected_unverified: "detected_unverified";
|
|
317
394
|
missing: "missing";
|
|
318
395
|
}>;
|
|
@@ -322,8 +399,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
322
399
|
amountUsd: z.ZodNumber;
|
|
323
400
|
recordCount: z.ZodNumber;
|
|
324
401
|
confidence: z.ZodEnum<{
|
|
325
|
-
verified: "verified";
|
|
326
402
|
estimated: "estimated";
|
|
403
|
+
verified: "verified";
|
|
327
404
|
detected_unverified: "detected_unverified";
|
|
328
405
|
missing: "missing";
|
|
329
406
|
}>;
|
|
@@ -338,8 +415,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
338
415
|
shareOfSpend: z.ZodNumber;
|
|
339
416
|
recordCount: z.ZodNumber;
|
|
340
417
|
confidence: z.ZodEnum<{
|
|
341
|
-
verified: "verified";
|
|
342
418
|
estimated: "estimated";
|
|
419
|
+
verified: "verified";
|
|
343
420
|
detected_unverified: "detected_unverified";
|
|
344
421
|
missing: "missing";
|
|
345
422
|
}>;
|
|
@@ -355,12 +432,13 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
355
432
|
week_over_week_spike: "week_over_week_spike";
|
|
356
433
|
}>;
|
|
357
434
|
key: z.ZodString;
|
|
435
|
+
comparisonKey: z.ZodOptional<z.ZodString>;
|
|
358
436
|
previousAmountUsd: z.ZodNumber;
|
|
359
437
|
currentAmountUsd: z.ZodNumber;
|
|
360
438
|
multiplier: z.ZodNumber;
|
|
361
439
|
confidence: z.ZodEnum<{
|
|
362
|
-
verified: "verified";
|
|
363
440
|
estimated: "estimated";
|
|
441
|
+
verified: "verified";
|
|
364
442
|
detected_unverified: "detected_unverified";
|
|
365
443
|
missing: "missing";
|
|
366
444
|
}>;
|
|
@@ -378,8 +456,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
378
456
|
}>;
|
|
379
457
|
estimatedImpactUsd: z.ZodNumber;
|
|
380
458
|
confidence: z.ZodEnum<{
|
|
381
|
-
verified: "verified";
|
|
382
459
|
estimated: "estimated";
|
|
460
|
+
verified: "verified";
|
|
383
461
|
detected_unverified: "detected_unverified";
|
|
384
462
|
missing: "missing";
|
|
385
463
|
}>;
|
|
@@ -416,8 +494,8 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
416
494
|
affectedModels: z.ZodArray<z.ZodString>;
|
|
417
495
|
estimatedImpactUsd: z.ZodNumber;
|
|
418
496
|
confidence: z.ZodEnum<{
|
|
419
|
-
verified: "verified";
|
|
420
497
|
estimated: "estimated";
|
|
498
|
+
verified: "verified";
|
|
421
499
|
detected_unverified: "detected_unverified";
|
|
422
500
|
missing: "missing";
|
|
423
501
|
}>;
|
package/dist/schema.js
CHANGED
|
@@ -13,6 +13,35 @@ export const spendSourceSchema = z.object({
|
|
|
13
13
|
confidence: costConfidenceSchema,
|
|
14
14
|
observedFrom: z.string().min(1)
|
|
15
15
|
});
|
|
16
|
+
/**
|
|
17
|
+
* What one normalized usage record represents.
|
|
18
|
+
*
|
|
19
|
+
* Only `call` and `invocation` are precise enough to support per-workload
|
|
20
|
+
* counterfactuals such as model routing, result caching, Batch API moves, or
|
|
21
|
+
* prompt trimming. The remaining values are still useful financial evidence,
|
|
22
|
+
* but must never be silently treated as individual calls.
|
|
23
|
+
*/
|
|
24
|
+
export const usageGranularityValues = [
|
|
25
|
+
"call",
|
|
26
|
+
"invocation",
|
|
27
|
+
"session",
|
|
28
|
+
"daily_aggregate",
|
|
29
|
+
"usage_bucket",
|
|
30
|
+
"billing_bucket",
|
|
31
|
+
"seat",
|
|
32
|
+
"user_aggregate"
|
|
33
|
+
];
|
|
34
|
+
export const usageGranularitySchema = z.enum(usageGranularityValues);
|
|
35
|
+
/**
|
|
36
|
+
* Explicit adapter attestations for workload-specific optimization advice.
|
|
37
|
+
* These fields are intentionally absent by default: an operation label alone
|
|
38
|
+
* does not prove identical inputs, latency tolerance, or downgrade safety.
|
|
39
|
+
*/
|
|
40
|
+
export const workloadSemanticsSchema = z.object({
|
|
41
|
+
stableInputFingerprint: z.string().min(8).max(128).regex(/^[a-zA-Z0-9:_-]+$/).optional(),
|
|
42
|
+
batchEligible: z.boolean().optional(),
|
|
43
|
+
downgradeSafe: z.boolean().optional()
|
|
44
|
+
}).strict();
|
|
16
45
|
export const usageRecordSchema = z.object({
|
|
17
46
|
id: z.string().min(1),
|
|
18
47
|
timestamp: z.string().datetime({ offset: true }),
|
|
@@ -30,7 +59,9 @@ export const usageRecordSchema = z.object({
|
|
|
30
59
|
providerCostType: z.string().min(1).optional(),
|
|
31
60
|
quantity: z.number().nonnegative().optional(),
|
|
32
61
|
agentId: z.string().min(1).optional(),
|
|
33
|
-
operation: z.string().min(1).optional()
|
|
62
|
+
operation: z.string().min(1).optional(),
|
|
63
|
+
usageGranularity: usageGranularitySchema.optional(),
|
|
64
|
+
workloadSemantics: workloadSemanticsSchema.optional()
|
|
34
65
|
}).superRefine((record, context) => {
|
|
35
66
|
if (record.costConfidence === "missing" && record.amountUsd !== null) {
|
|
36
67
|
context.addIssue({
|
|
@@ -47,6 +78,79 @@ export const usageRecordSchema = z.object({
|
|
|
47
78
|
});
|
|
48
79
|
}
|
|
49
80
|
});
|
|
81
|
+
/**
|
|
82
|
+
* True only when a record can honestly ground a modeled workload change.
|
|
83
|
+
*
|
|
84
|
+
* A provider billing row, usage bucket, seat, user total, or unlabelled legacy
|
|
85
|
+
* row may describe real spend, but it does not prove that one row was one
|
|
86
|
+
* optimizable call. Requiring both explicit call/invocation provenance and a
|
|
87
|
+
* named operation keeps aggregate connector data in accounting views without
|
|
88
|
+
* manufacturing per-call savings advice from it.
|
|
89
|
+
*/
|
|
90
|
+
export function hasModeledWorkloadEvidence(record) {
|
|
91
|
+
return (hasCallLevelProvenance(record) &&
|
|
92
|
+
typeof record.operation === "string" &&
|
|
93
|
+
record.operation.trim().length > 0 &&
|
|
94
|
+
hasPricedEvidence(record));
|
|
95
|
+
}
|
|
96
|
+
/** Explicit one-call / one-model-invocation provenance, never inferred. */
|
|
97
|
+
export function hasCallLevelProvenance(record) {
|
|
98
|
+
return record.usageGranularity === "call" || record.usageGranularity === "invocation";
|
|
99
|
+
}
|
|
100
|
+
/** A positive priced observation from which a dollar counterfactual can be modeled. */
|
|
101
|
+
export function hasPricedEvidence(record) {
|
|
102
|
+
return (typeof record.amountUsd === "number" &&
|
|
103
|
+
record.amountUsd > 0 &&
|
|
104
|
+
record.costConfidence !== "missing");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Recognize the bundled demo records written by releases that predate the
|
|
108
|
+
* persisted `mode` field. The marker is deliberately narrow: every record
|
|
109
|
+
* must come from the shipped sample CSV and use a sample source identifier.
|
|
110
|
+
* This lets newer clients recover the demo boundary without guessing that an
|
|
111
|
+
* arbitrary unlabeled state is real local or connected evidence.
|
|
112
|
+
*/
|
|
113
|
+
export function isBundledSampleUsage(records) {
|
|
114
|
+
return records.length > 0 && records.every(isBundledSampleRecord);
|
|
115
|
+
}
|
|
116
|
+
function isBundledSampleRecord(record) {
|
|
117
|
+
return record.source.observedFrom === "sample_csv" &&
|
|
118
|
+
/(?:^|-)sample$/i.test(record.source.id);
|
|
119
|
+
}
|
|
120
|
+
/** A declared demo/sample mode can never carry proof-level financial labels. */
|
|
121
|
+
export function downgradeSampleUsageEvidence(records) {
|
|
122
|
+
return records.map(downgradeSampleRecordEvidence);
|
|
123
|
+
}
|
|
124
|
+
function downgradeSampleRecordEvidence(record) {
|
|
125
|
+
return {
|
|
126
|
+
...record,
|
|
127
|
+
source: {
|
|
128
|
+
...record.source,
|
|
129
|
+
confidence: record.source.confidence === "verified"
|
|
130
|
+
? "estimated"
|
|
131
|
+
: record.source.confidence
|
|
132
|
+
},
|
|
133
|
+
costConfidence: record.costConfidence === "verified"
|
|
134
|
+
? "estimated"
|
|
135
|
+
: record.costConfidence
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Stable financial cohort for period-over-period comparisons. Missing shape
|
|
140
|
+
* provenance returns `undefined`; unknown rows must not be pooled into a fake
|
|
141
|
+
* comparable series.
|
|
142
|
+
*/
|
|
143
|
+
export function spendComparisonKey(record) {
|
|
144
|
+
if (!record.providerCostType || !record.usageGranularity) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return [
|
|
148
|
+
record.source.id,
|
|
149
|
+
record.source.provider,
|
|
150
|
+
record.providerCostType,
|
|
151
|
+
record.usageGranularity
|
|
152
|
+
].map((part) => encodeURIComponent(part)).join("|");
|
|
153
|
+
}
|
|
50
154
|
export const attributionCandidateSchema = z.object({
|
|
51
155
|
entityType: z.enum(["client", "project", "agent", "user", "workspace", "api_key"]),
|
|
52
156
|
entityId: z.string().min(1),
|
|
@@ -69,6 +173,7 @@ export const spendBreakdownEntrySchema = z.object({
|
|
|
69
173
|
export const spendAnomalySchema = z.object({
|
|
70
174
|
kind: z.enum(["day_over_day_spike", "week_over_week_spike"]),
|
|
71
175
|
key: z.string().min(1),
|
|
176
|
+
comparisonKey: z.string().min(1).optional(),
|
|
72
177
|
previousAmountUsd: z.number().nonnegative(),
|
|
73
178
|
currentAmountUsd: z.number().nonnegative(),
|
|
74
179
|
multiplier: z.number().nonnegative(),
|
|
@@ -150,7 +255,10 @@ export const spendSummarySchema = z.object({
|
|
|
150
255
|
insights: z.array(spendInsightSchema).default([])
|
|
151
256
|
});
|
|
152
257
|
export function parseUsageRecord(value) {
|
|
153
|
-
|
|
258
|
+
const record = usageRecordSchema.parse(value);
|
|
259
|
+
if (!isBundledSampleRecord(record))
|
|
260
|
+
return record;
|
|
261
|
+
return downgradeSampleRecordEvidence(record);
|
|
154
262
|
}
|
|
155
263
|
export function parseSpendSummary(value) {
|
|
156
264
|
return spendSummarySchema.parse(value);
|
package/dist/sourceRegistry.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { UsageSignal } from "./discovery.js";
|
|
2
|
+
import type { FinancialEvidenceStatus, SourceValidationCoverage } from "./sourceStatus.js";
|
|
2
3
|
export type SourceType = "local_folder" | "provider_export" | "provider_api" | "browser_account" | "local_tool_detection" | "mcp_tool" | "internal_system";
|
|
3
4
|
export type SourceAccessMethod = "file" | "api" | "browser" | "cli_detection" | "mcp" | "internal" | "manual";
|
|
4
5
|
export type ConnectorAuthMode = "oauth" | "api_token_ref" | "browser_session" | "mcp_auth" | "manual_export" | "none";
|
|
5
6
|
export type ConnectorTokenStorage = "local_reference_only" | "keychain_reference" | "none";
|
|
6
|
-
|
|
7
|
+
/** @deprecated Use FinancialEvidenceStatus. Kept only for persisted v1 migration. */
|
|
8
|
+
export type SourceVerificationStatus = FinancialEvidenceStatus;
|
|
9
|
+
export type SourceBoundaryApproval = "approved";
|
|
7
10
|
export type IngestionLaneId = "local_files_exports" | "provider_apis" | "browser_account_ui" | "local_cli_tool_detection" | "mcp_internal_systems";
|
|
8
11
|
export type IngestionLane = {
|
|
9
12
|
id: IngestionLaneId;
|
|
10
13
|
label: string;
|
|
11
14
|
sourceTypes: SourceType[];
|
|
12
|
-
|
|
15
|
+
defaultFinancialEvidence: FinancialEvidenceStatus;
|
|
13
16
|
};
|
|
14
17
|
export type ApprovedSource = {
|
|
15
18
|
id: string;
|
|
@@ -22,7 +25,12 @@ export type ApprovedSource = {
|
|
|
22
25
|
scope: string;
|
|
23
26
|
lane: IngestionLaneId;
|
|
24
27
|
accessMethod: SourceAccessMethod;
|
|
25
|
-
|
|
28
|
+
/** Permission to read this exact boundary. This is never financial proof. */
|
|
29
|
+
boundaryApproval: SourceBoundaryApproval;
|
|
30
|
+
/** How thoroughly the connector/parser itself has been exercised. */
|
|
31
|
+
validationCoverage: SourceValidationCoverage;
|
|
32
|
+
/** Quality of the financial numbers currently emitted by this source. */
|
|
33
|
+
financialEvidence: FinancialEvidenceStatus;
|
|
26
34
|
fieldsVerified: string[];
|
|
27
35
|
fieldsEstimated: string[];
|
|
28
36
|
fieldsMissing: string[];
|
|
@@ -73,7 +81,7 @@ export type ProviderConnectorCatalogEntry = {
|
|
|
73
81
|
};
|
|
74
82
|
export type MissingSourcePrompt = {
|
|
75
83
|
provider: string;
|
|
76
|
-
status: Extract<
|
|
84
|
+
status: Extract<FinancialEvidenceStatus, "detected_unverified" | "missing">;
|
|
77
85
|
reason: string;
|
|
78
86
|
detectedEvidence: string[];
|
|
79
87
|
suggestedConnector: string;
|
|
@@ -100,8 +108,25 @@ export declare const providerCatalog: ProviderCatalogEntry[];
|
|
|
100
108
|
export declare const providerConnectorCatalog: ProviderConnectorCatalogEntry[];
|
|
101
109
|
export declare const defaultDeniedGlobs: string[];
|
|
102
110
|
export declare function createLocalFolderSourceRegistry(rootPath: string, now?: Date): SourceRegistry;
|
|
103
|
-
export declare function addApprovedSource(registry: SourceRegistry, source: Omit<ApprovedSource, "approvedAt" | "readOnly" | "scope" | "lane" | "accessMethod" | "
|
|
111
|
+
export declare function addApprovedSource(registry: SourceRegistry, source: Omit<ApprovedSource, "approvedAt" | "readOnly" | "scope" | "lane" | "accessMethod" | "boundaryApproval" | "validationCoverage" | "financialEvidence" | "fieldsVerified" | "fieldsEstimated" | "fieldsMissing"> & Partial<Pick<ApprovedSource, "readOnly" | "scope" | "lane" | "accessMethod" | "boundaryApproval" | "validationCoverage" | "financialEvidence" | "fieldsVerified" | "fieldsEstimated" | "fieldsMissing" | "authMode" | "authScopes" | "tokenStorage" | "authReference">>, now?: Date): SourceRegistry;
|
|
104
112
|
export declare function createProviderConnectorStub(provider: string, type?: SourceType, now?: Date): ApprovedSource;
|
|
113
|
+
/**
|
|
114
|
+
* Read a persisted source registry into the canonical three-axis contract.
|
|
115
|
+
*
|
|
116
|
+
* Version 1 registries used `verification` for several unrelated meanings.
|
|
117
|
+
* It is accepted here only as a migration input for financial evidence and is
|
|
118
|
+
* deliberately omitted from the returned object. A local-folder approval is
|
|
119
|
+
* permission metadata, so even a legacy `verification: "verified"` migrates
|
|
120
|
+
* to `financialEvidence: "missing"`.
|
|
121
|
+
*/
|
|
122
|
+
export declare function normalizeSourceRegistry(value: unknown): SourceRegistry;
|
|
123
|
+
/**
|
|
124
|
+
* Persisted source registries are repository-controlled configuration. Until
|
|
125
|
+
* an external provider-sync receipt binds their exact bytes, keep only the
|
|
126
|
+
* approved read-only boundary and remove any self-asserted validation or
|
|
127
|
+
* financial-evidence claims.
|
|
128
|
+
*/
|
|
129
|
+
export declare function downgradeUntrustedSourceRegistryClaims(registry: SourceRegistry): SourceRegistry;
|
|
105
130
|
export declare function buildMissingSourcePrompts(signals: UsageSignal[], registry: SourceRegistry): MissingSourcePrompt[];
|
|
106
131
|
export declare function confirmMapping(input: Omit<ConfirmedMapping, "id" | "status" | "confirmedAt">, now?: Date): ConfirmedMapping;
|
|
107
132
|
export declare function createScanAuditLog(events?: ScanAuditEvent[]): ScanAuditLog;
|