@agent-finops/core 0.5.9 → 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.
@@ -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 { usageRecordSchema } from "./schema.js";
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().sort((left, right) => left.timestamp.localeCompare(right.timestamp));
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 usageRecordSchema.parse({
32
+ return parseUsageRecord({
32
33
  id: row.id,
33
34
  timestamp: row.timestamp,
34
35
  source: {
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,8 +12,8 @@ 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
  }>;
@@ -59,8 +59,8 @@ export declare const usageRecordSchema: z.ZodObject<{
59
59
  name: z.ZodString;
60
60
  provider: z.ZodString;
61
61
  confidence: z.ZodEnum<{
62
- verified: "verified";
63
62
  estimated: "estimated";
63
+ verified: "verified";
64
64
  detected_unverified: "detected_unverified";
65
65
  missing: "missing";
66
66
  }>;
@@ -71,8 +71,8 @@ export declare const usageRecordSchema: z.ZodObject<{
71
71
  outputTokens: z.ZodNumber;
72
72
  amountUsd: z.ZodNullable<z.ZodNumber>;
73
73
  costConfidence: z.ZodEnum<{
74
- verified: "verified";
75
74
  estimated: "estimated";
75
+ verified: "verified";
76
76
  detected_unverified: "detected_unverified";
77
77
  missing: "missing";
78
78
  }>;
@@ -124,6 +124,8 @@ export declare function hasPricedEvidence(record: UsageRecord): boolean;
124
124
  * arbitrary unlabeled state is real local or connected evidence.
125
125
  */
126
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[];
127
129
  /**
128
130
  * Stable financial cohort for period-over-period comparisons. Missing shape
129
131
  * provenance returns `undefined`; unknown rows must not be pooled into a fake
@@ -132,10 +134,10 @@ export declare function isBundledSampleUsage(records: UsageRecord[]): boolean;
132
134
  export declare function spendComparisonKey(record: UsageRecord): string | undefined;
133
135
  export declare const attributionCandidateSchema: z.ZodObject<{
134
136
  entityType: z.ZodEnum<{
135
- client: "client";
137
+ user: "user";
136
138
  project: "project";
137
139
  agent: "agent";
138
- user: "user";
140
+ client: "client";
139
141
  workspace: "workspace";
140
142
  api_key: "api_key";
141
143
  }>;
@@ -148,10 +150,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
148
150
  usageRecordId: z.ZodString;
149
151
  candidates: z.ZodArray<z.ZodObject<{
150
152
  entityType: z.ZodEnum<{
151
- client: "client";
153
+ user: "user";
152
154
  project: "project";
153
155
  agent: "agent";
154
- user: "user";
156
+ client: "client";
155
157
  workspace: "workspace";
156
158
  api_key: "api_key";
157
159
  }>;
@@ -161,10 +163,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
161
163
  }, z.core.$strip>>;
162
164
  selected: z.ZodOptional<z.ZodObject<{
163
165
  entityType: z.ZodEnum<{
164
- client: "client";
166
+ user: "user";
165
167
  project: "project";
166
168
  agent: "agent";
167
- user: "user";
169
+ client: "client";
168
170
  workspace: "workspace";
169
171
  api_key: "api_key";
170
172
  }>;
@@ -186,8 +188,8 @@ export declare const spendBreakdownEntrySchema: z.ZodObject<{
186
188
  amountUsd: z.ZodNumber;
187
189
  recordCount: z.ZodNumber;
188
190
  confidence: z.ZodEnum<{
189
- verified: "verified";
190
191
  estimated: "estimated";
192
+ verified: "verified";
191
193
  detected_unverified: "detected_unverified";
192
194
  missing: "missing";
193
195
  }>;
@@ -204,8 +206,8 @@ export declare const spendAnomalySchema: z.ZodObject<{
204
206
  currentAmountUsd: z.ZodNumber;
205
207
  multiplier: z.ZodNumber;
206
208
  confidence: z.ZodEnum<{
207
- verified: "verified";
208
209
  estimated: "estimated";
210
+ verified: "verified";
209
211
  detected_unverified: "detected_unverified";
210
212
  missing: "missing";
211
213
  }>;
@@ -221,8 +223,8 @@ export declare const workflowWatchEntrySchema: z.ZodObject<{
221
223
  shareOfSpend: z.ZodNumber;
222
224
  recordCount: z.ZodNumber;
223
225
  confidence: z.ZodEnum<{
224
- verified: "verified";
225
226
  estimated: "estimated";
227
+ verified: "verified";
226
228
  detected_unverified: "detected_unverified";
227
229
  missing: "missing";
228
230
  }>;
@@ -246,8 +248,8 @@ export declare const recommendationSchema: z.ZodObject<{
246
248
  }>;
247
249
  estimatedImpactUsd: z.ZodNumber;
248
250
  confidence: z.ZodEnum<{
249
- verified: "verified";
250
251
  estimated: "estimated";
252
+ verified: "verified";
251
253
  detected_unverified: "detected_unverified";
252
254
  missing: "missing";
253
255
  }>;
@@ -291,8 +293,8 @@ export declare const spendInsightSchema: z.ZodObject<{
291
293
  affectedModels: z.ZodArray<z.ZodString>;
292
294
  estimatedImpactUsd: z.ZodNumber;
293
295
  confidence: z.ZodEnum<{
294
- verified: "verified";
295
296
  estimated: "estimated";
297
+ verified: "verified";
296
298
  detected_unverified: "detected_unverified";
297
299
  missing: "missing";
298
300
  }>;
@@ -304,14 +306,14 @@ export declare const spendSummarySchema: z.ZodObject<{
304
306
  totalUsd: z.ZodNumber;
305
307
  recordCount: z.ZodNumber;
306
308
  confidence: z.ZodEnum<{
307
- verified: "verified";
308
309
  estimated: "estimated";
310
+ verified: "verified";
309
311
  detected_unverified: "detected_unverified";
310
312
  missing: "missing";
311
313
  }>;
312
314
  confidenceBreakdown: z.ZodRecord<z.ZodEnum<{
313
- verified: "verified";
314
315
  estimated: "estimated";
316
+ verified: "verified";
315
317
  detected_unverified: "detected_unverified";
316
318
  missing: "missing";
317
319
  }>, z.ZodNumber>;
@@ -320,8 +322,8 @@ export declare const spendSummarySchema: z.ZodObject<{
320
322
  amountUsd: z.ZodNumber;
321
323
  recordCount: z.ZodNumber;
322
324
  confidence: z.ZodEnum<{
323
- verified: "verified";
324
325
  estimated: "estimated";
326
+ verified: "verified";
325
327
  detected_unverified: "detected_unverified";
326
328
  missing: "missing";
327
329
  }>;
@@ -331,8 +333,8 @@ export declare const spendSummarySchema: z.ZodObject<{
331
333
  amountUsd: z.ZodNumber;
332
334
  recordCount: z.ZodNumber;
333
335
  confidence: z.ZodEnum<{
334
- verified: "verified";
335
336
  estimated: "estimated";
337
+ verified: "verified";
336
338
  detected_unverified: "detected_unverified";
337
339
  missing: "missing";
338
340
  }>;
@@ -342,8 +344,8 @@ export declare const spendSummarySchema: z.ZodObject<{
342
344
  amountUsd: z.ZodNumber;
343
345
  recordCount: z.ZodNumber;
344
346
  confidence: z.ZodEnum<{
345
- verified: "verified";
346
347
  estimated: "estimated";
348
+ verified: "verified";
347
349
  detected_unverified: "detected_unverified";
348
350
  missing: "missing";
349
351
  }>;
@@ -353,8 +355,8 @@ export declare const spendSummarySchema: z.ZodObject<{
353
355
  amountUsd: z.ZodNumber;
354
356
  recordCount: z.ZodNumber;
355
357
  confidence: z.ZodEnum<{
356
- verified: "verified";
357
358
  estimated: "estimated";
359
+ verified: "verified";
358
360
  detected_unverified: "detected_unverified";
359
361
  missing: "missing";
360
362
  }>;
@@ -364,8 +366,8 @@ export declare const spendSummarySchema: z.ZodObject<{
364
366
  amountUsd: z.ZodNumber;
365
367
  recordCount: z.ZodNumber;
366
368
  confidence: z.ZodEnum<{
367
- verified: "verified";
368
369
  estimated: "estimated";
370
+ verified: "verified";
369
371
  detected_unverified: "detected_unverified";
370
372
  missing: "missing";
371
373
  }>;
@@ -375,8 +377,8 @@ export declare const spendSummarySchema: z.ZodObject<{
375
377
  amountUsd: z.ZodNumber;
376
378
  recordCount: z.ZodNumber;
377
379
  confidence: z.ZodEnum<{
378
- verified: "verified";
379
380
  estimated: "estimated";
381
+ verified: "verified";
380
382
  detected_unverified: "detected_unverified";
381
383
  missing: "missing";
382
384
  }>;
@@ -386,8 +388,8 @@ export declare const spendSummarySchema: z.ZodObject<{
386
388
  amountUsd: z.ZodNumber;
387
389
  recordCount: z.ZodNumber;
388
390
  confidence: z.ZodEnum<{
389
- verified: "verified";
390
391
  estimated: "estimated";
392
+ verified: "verified";
391
393
  detected_unverified: "detected_unverified";
392
394
  missing: "missing";
393
395
  }>;
@@ -397,8 +399,8 @@ export declare const spendSummarySchema: z.ZodObject<{
397
399
  amountUsd: z.ZodNumber;
398
400
  recordCount: z.ZodNumber;
399
401
  confidence: z.ZodEnum<{
400
- verified: "verified";
401
402
  estimated: "estimated";
403
+ verified: "verified";
402
404
  detected_unverified: "detected_unverified";
403
405
  missing: "missing";
404
406
  }>;
@@ -413,8 +415,8 @@ export declare const spendSummarySchema: z.ZodObject<{
413
415
  shareOfSpend: z.ZodNumber;
414
416
  recordCount: z.ZodNumber;
415
417
  confidence: z.ZodEnum<{
416
- verified: "verified";
417
418
  estimated: "estimated";
419
+ verified: "verified";
418
420
  detected_unverified: "detected_unverified";
419
421
  missing: "missing";
420
422
  }>;
@@ -435,8 +437,8 @@ export declare const spendSummarySchema: z.ZodObject<{
435
437
  currentAmountUsd: z.ZodNumber;
436
438
  multiplier: z.ZodNumber;
437
439
  confidence: z.ZodEnum<{
438
- verified: "verified";
439
440
  estimated: "estimated";
441
+ verified: "verified";
440
442
  detected_unverified: "detected_unverified";
441
443
  missing: "missing";
442
444
  }>;
@@ -454,8 +456,8 @@ export declare const spendSummarySchema: z.ZodObject<{
454
456
  }>;
455
457
  estimatedImpactUsd: z.ZodNumber;
456
458
  confidence: z.ZodEnum<{
457
- verified: "verified";
458
459
  estimated: "estimated";
460
+ verified: "verified";
459
461
  detected_unverified: "detected_unverified";
460
462
  missing: "missing";
461
463
  }>;
@@ -492,8 +494,8 @@ export declare const spendSummarySchema: z.ZodObject<{
492
494
  affectedModels: z.ZodArray<z.ZodString>;
493
495
  estimatedImpactUsd: z.ZodNumber;
494
496
  confidence: z.ZodEnum<{
495
- verified: "verified";
496
497
  estimated: "estimated";
498
+ verified: "verified";
497
499
  detected_unverified: "detected_unverified";
498
500
  missing: "missing";
499
501
  }>;
package/dist/schema.js CHANGED
@@ -111,8 +111,29 @@ export function hasPricedEvidence(record) {
111
111
  * arbitrary unlabeled state is real local or connected evidence.
112
112
  */
113
113
  export function isBundledSampleUsage(records) {
114
- return records.length > 0 && records.every((record) => record.source.observedFrom === "sample_csv" &&
115
- /(?:^|-)sample$/i.test(record.source.id));
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
+ };
116
137
  }
117
138
  /**
118
139
  * Stable financial cohort for period-over-period comparisons. Missing shape
@@ -234,7 +255,10 @@ export const spendSummarySchema = z.object({
234
255
  insights: z.array(spendInsightSchema).default([])
235
256
  });
236
257
  export function parseUsageRecord(value) {
237
- return usageRecordSchema.parse(value);
258
+ const record = usageRecordSchema.parse(value);
259
+ if (!isBundledSampleRecord(record))
260
+ return record;
261
+ return downgradeSampleRecordEvidence(record);
238
262
  }
239
263
  export function parseSpendSummary(value) {
240
264
  return spendSummarySchema.parse(value);
@@ -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
- export type SourceVerificationStatus = "verified" | "estimated" | "detected_unverified" | "missing";
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
- defaultVerification: SourceVerificationStatus;
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
- verification: SourceVerificationStatus;
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<SourceVerificationStatus, "detected_unverified" | "missing">;
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" | "verification" | "fieldsVerified" | "fieldsEstimated" | "fieldsMissing"> & Partial<Pick<ApprovedSource, "readOnly" | "scope" | "lane" | "accessMethod" | "verification" | "fieldsVerified" | "fieldsEstimated" | "fieldsMissing" | "authMode" | "authScopes" | "tokenStorage" | "authReference">>, now?: Date): SourceRegistry;
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;