@agent-finops/core 0.7.3 → 0.8.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 +35 -2
- package/dist/activitySnapshot.d.ts +57 -57
- package/dist/activitySnapshot.js +20 -9
- package/dist/agentEconomicsReceipt.d.ts +44 -44
- package/dist/analyze.js +3 -1
- package/dist/cutList.js +10 -2
- package/dist/glance.d.ts +3 -1
- package/dist/glance.js +117 -73
- package/dist/insights.js +3 -1
- package/dist/localAgentFormats/gemini.d.ts +55 -0
- package/dist/localAgentFormats/gemini.js +443 -0
- package/dist/localAgentFormats/registry.d.ts +2 -1
- package/dist/localAgentFormats/registry.js +125 -9
- package/dist/localAgentFormats/runtimeRegistry.js +25 -2
- package/dist/localAgentFormats/types.d.ts +13 -4
- package/dist/localAgentLogs.d.ts +25 -4
- package/dist/localAgentLogs.js +215 -17
- package/dist/modelPricing.d.ts +33 -1
- package/dist/modelPricing.js +120 -13
- package/dist/planMath.js +12 -7
- package/dist/providerConnectors.d.ts +16 -0
- package/dist/providerConnectors.js +320 -33
- package/dist/providerContractStates.generated.d.ts +8 -0
- package/dist/providerContractStates.generated.js +9 -0
- package/dist/schema.d.ts +18 -0
- package/dist/schema.js +26 -0
- package/dist/sourceRegistry.js +90 -52
- package/dist/sourceStatus.d.ts +23 -1
- package/dist/sourceStatus.js +104 -9
- package/dist/stateTrust.js +2 -2
- package/package.json +8 -1
package/dist/sourceRegistry.js
CHANGED
|
@@ -13,9 +13,9 @@ export const ingestionLanes = [
|
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
id: "browser_account_ui",
|
|
16
|
-
label: "Browser Account UI",
|
|
16
|
+
label: "Browser Account UI (schema scaffold; ingestion unavailable)",
|
|
17
17
|
sourceTypes: ["browser_account"],
|
|
18
|
-
defaultFinancialEvidence: "
|
|
18
|
+
defaultFinancialEvidence: "missing"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
id: "local_cli_tool_detection",
|
|
@@ -31,58 +31,79 @@ export const ingestionLanes = [
|
|
|
31
31
|
}
|
|
32
32
|
];
|
|
33
33
|
export const supportedSourceTypes = ingestionLanes.flatMap((lane) => lane.sourceTypes);
|
|
34
|
+
const browserAccountScaffoldMissingField = "browser-account ingestion is a schema scaffold and is not implemented";
|
|
34
35
|
export const providerCatalog = [
|
|
35
36
|
{
|
|
36
37
|
id: "openai",
|
|
37
38
|
label: "OpenAI / Codex account usage",
|
|
38
39
|
preferredSourceType: "provider_api",
|
|
39
40
|
preferredAccessMethod: "api",
|
|
40
|
-
verifiedFields: ["organization costs", "
|
|
41
|
-
missingFields: [
|
|
41
|
+
verifiedFields: ["organization costs", "completions usage by project, user, model, API key, and service tier"],
|
|
42
|
+
missingFields: [
|
|
43
|
+
"OpenAI Admin API key reference",
|
|
44
|
+
"non-completions usage families",
|
|
45
|
+
"ChatGPT/Codex workspace seats, credits, and final invoice settlement"
|
|
46
|
+
]
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
49
|
id: "anthropic",
|
|
45
50
|
label: "Anthropic / Claude / Claude Code",
|
|
46
51
|
preferredSourceType: "provider_api",
|
|
47
52
|
preferredAccessMethod: "api",
|
|
48
|
-
verifiedFields: ["organization cost report", "Claude Code
|
|
49
|
-
missingFields: [
|
|
50
|
-
|
|
53
|
+
verifiedFields: ["organization cost report", "Claude Code analytics by workspace and user"],
|
|
54
|
+
missingFields: [
|
|
55
|
+
"Anthropic Admin API key reference",
|
|
56
|
+
"Messages Usage API detail",
|
|
57
|
+
"Claude Enterprise Analytics and final invoice settlement"
|
|
58
|
+
]
|
|
51
59
|
},
|
|
52
60
|
{
|
|
53
61
|
id: "github-copilot",
|
|
54
62
|
label: "GitHub Copilot",
|
|
55
63
|
preferredSourceType: "provider_api",
|
|
56
64
|
preferredAccessMethod: "api",
|
|
57
|
-
verifiedFields: ["Copilot usage metrics", "seat
|
|
58
|
-
missingFields: [
|
|
59
|
-
|
|
65
|
+
verifiedFields: ["Copilot usage metrics", "seat assignments and reported plan types"],
|
|
66
|
+
missingFields: [
|
|
67
|
+
"GitHub admin token reference and organization or enterprise slug",
|
|
68
|
+
"AI-credit gross, discount, and net billing",
|
|
69
|
+
"license invoice settlement"
|
|
70
|
+
]
|
|
60
71
|
},
|
|
61
72
|
{
|
|
62
73
|
id: "codex",
|
|
63
74
|
label: "Codex / OpenAI coding tools",
|
|
64
75
|
preferredSourceType: "provider_api",
|
|
65
76
|
preferredAccessMethod: "api",
|
|
66
|
-
verifiedFields: ["OpenAI
|
|
67
|
-
missingFields: [
|
|
68
|
-
|
|
77
|
+
verifiedFields: ["OpenAI organization costs", "OpenAI completions usage"],
|
|
78
|
+
missingFields: [
|
|
79
|
+
"OpenAI Admin API key reference",
|
|
80
|
+
"ChatGPT/Codex workspace seats, credits, and subscription billing",
|
|
81
|
+
"workspace-to-local-project mapping"
|
|
82
|
+
]
|
|
69
83
|
},
|
|
70
84
|
{
|
|
71
85
|
id: "cursor",
|
|
72
86
|
label: "Cursor",
|
|
73
87
|
preferredSourceType: "provider_api",
|
|
74
88
|
preferredAccessMethod: "api",
|
|
75
|
-
verifiedFields: ["Cursor Admin API
|
|
76
|
-
missingFields: [
|
|
77
|
-
|
|
89
|
+
verifiedFields: ["Cursor Admin API team-member spend aggregate"],
|
|
90
|
+
missingFields: [
|
|
91
|
+
"Cursor team Admin API key reference",
|
|
92
|
+
"filtered usage-event detail and aggregate reconciliation",
|
|
93
|
+
"seat contract and final invoice settlement"
|
|
94
|
+
]
|
|
78
95
|
},
|
|
79
96
|
{
|
|
80
97
|
id: "gemini",
|
|
81
98
|
label: "Google Gemini",
|
|
82
99
|
preferredSourceType: "provider_api",
|
|
83
100
|
preferredAccessMethod: "api",
|
|
84
|
-
verifiedFields: [
|
|
85
|
-
missingFields: [
|
|
101
|
+
verifiedFields: [],
|
|
102
|
+
missingFields: [
|
|
103
|
+
"Google Cloud Billing export or approved billing API source",
|
|
104
|
+
"Gemini CLI authentication and billing mode",
|
|
105
|
+
"provider-reported billed money"
|
|
106
|
+
]
|
|
86
107
|
},
|
|
87
108
|
{
|
|
88
109
|
id: "langfuse",
|
|
@@ -136,35 +157,35 @@ export const providerCatalog = [
|
|
|
136
157
|
export const providerConnectorCatalog = [
|
|
137
158
|
{
|
|
138
159
|
provider: "openai",
|
|
139
|
-
preferredAuthMode: "
|
|
140
|
-
fallbackAuthModes: [
|
|
141
|
-
scopes: ["
|
|
160
|
+
preferredAuthMode: "api_token_ref",
|
|
161
|
+
fallbackAuthModes: [],
|
|
162
|
+
scopes: ["Organization Admin API usage read", "Organization Admin API costs read"],
|
|
142
163
|
tokenStorage: "local_reference_only",
|
|
143
|
-
setupHint: "
|
|
164
|
+
setupHint: "Use a local env reference to an OpenAI Organization Admin API key with usage and costs access; raw keys are never accepted on the command line."
|
|
144
165
|
},
|
|
145
166
|
{
|
|
146
167
|
provider: "anthropic",
|
|
147
|
-
preferredAuthMode: "
|
|
148
|
-
fallbackAuthModes: [
|
|
149
|
-
scopes: ["
|
|
168
|
+
preferredAuthMode: "api_token_ref",
|
|
169
|
+
fallbackAuthModes: [],
|
|
170
|
+
scopes: ["Claude Console Admin cost report read", "Claude Code analytics read"],
|
|
150
171
|
tokenStorage: "local_reference_only",
|
|
151
|
-
setupHint: "
|
|
172
|
+
setupHint: "Use a local env reference to a Claude Console organization Admin API key; Claude Enterprise Analytics requires a different key and is not implemented."
|
|
152
173
|
},
|
|
153
174
|
{
|
|
154
175
|
provider: "github-copilot",
|
|
155
|
-
preferredAuthMode: "
|
|
156
|
-
fallbackAuthModes: [
|
|
157
|
-
scopes: ["
|
|
176
|
+
preferredAuthMode: "api_token_ref",
|
|
177
|
+
fallbackAuthModes: [],
|
|
178
|
+
scopes: ["fine-grained Administration: read", "organization or enterprise billing access"],
|
|
158
179
|
tokenStorage: "local_reference_only",
|
|
159
|
-
setupHint: "
|
|
180
|
+
setupHint: "Use a local env reference to a GitHub token with read-only organization or enterprise Copilot metrics and seat access; AI-credit billing is not implemented."
|
|
160
181
|
},
|
|
161
182
|
{
|
|
162
183
|
provider: "cursor",
|
|
163
184
|
preferredAuthMode: "api_token_ref",
|
|
164
|
-
fallbackAuthModes: [
|
|
165
|
-
scopes: ["
|
|
185
|
+
fallbackAuthModes: [],
|
|
186
|
+
scopes: ["Cursor team Admin API spend read"],
|
|
166
187
|
tokenStorage: "local_reference_only",
|
|
167
|
-
setupHint: "Use Cursor Admin API key
|
|
188
|
+
setupHint: "Use a local env reference to a Cursor team Admin API key for the team spend aggregate; filtered usage-event and invoice reconciliation are not implemented."
|
|
168
189
|
}
|
|
169
190
|
];
|
|
170
191
|
export const defaultDeniedGlobs = [
|
|
@@ -215,6 +236,7 @@ export function addApprovedSource(registry, source, now = new Date()) {
|
|
|
215
236
|
if (source.readOnly === false) {
|
|
216
237
|
throw new Error("Approved source boundaries must remain read-only.");
|
|
217
238
|
}
|
|
239
|
+
const browserScaffold = source.type === "browser_account";
|
|
218
240
|
const nextSource = {
|
|
219
241
|
id: source.id,
|
|
220
242
|
type: source.type,
|
|
@@ -223,15 +245,17 @@ export function addApprovedSource(registry, source, now = new Date()) {
|
|
|
223
245
|
...(source.provider ? { provider: source.provider } : {}),
|
|
224
246
|
readOnly: true,
|
|
225
247
|
approvedAt: timestamp,
|
|
226
|
-
scope: source.scope ?? defaultScopeForSource(source.type),
|
|
248
|
+
scope: browserScaffold ? defaultScopeForSource(source.type) : (source.scope ?? defaultScopeForSource(source.type)),
|
|
227
249
|
lane: source.lane ?? laneForSourceType(source.type),
|
|
228
250
|
accessMethod: source.accessMethod ?? accessMethodForSourceType(source.type),
|
|
229
251
|
boundaryApproval: source.boundaryApproval ?? "approved",
|
|
230
|
-
validationCoverage: source.validationCoverage ?? "untested",
|
|
231
|
-
financialEvidence: source.type === "local_folder" ? "missing" : (source.financialEvidence ?? "missing"),
|
|
232
|
-
fieldsVerified: source.fieldsVerified ?? [],
|
|
233
|
-
fieldsEstimated: source.fieldsEstimated ?? [],
|
|
234
|
-
fieldsMissing:
|
|
252
|
+
validationCoverage: browserScaffold ? "untested" : (source.validationCoverage ?? "untested"),
|
|
253
|
+
financialEvidence: source.type === "local_folder" || browserScaffold ? "missing" : (source.financialEvidence ?? "missing"),
|
|
254
|
+
fieldsVerified: browserScaffold ? [] : (source.fieldsVerified ?? []),
|
|
255
|
+
fieldsEstimated: browserScaffold ? [] : (source.fieldsEstimated ?? []),
|
|
256
|
+
fieldsMissing: browserScaffold
|
|
257
|
+
? Array.from(new Set([...(source.fieldsMissing ?? []), browserAccountScaffoldMissingField]))
|
|
258
|
+
: (source.fieldsMissing ?? []),
|
|
235
259
|
authMode: source.authMode,
|
|
236
260
|
authScopes: source.authScopes,
|
|
237
261
|
tokenStorage: source.tokenStorage,
|
|
@@ -250,6 +274,7 @@ export function createProviderConnectorStub(provider, type = providerCatalog.fin
|
|
|
250
274
|
const catalogEntry = providerCatalog.find((entry) => entry.id === provider);
|
|
251
275
|
const connectorEntry = providerConnectorCatalog.find((entry) => entry.provider === provider);
|
|
252
276
|
const id = slugifySourceId(`${provider}-${type}`);
|
|
277
|
+
const browserScaffold = type === "browser_account";
|
|
253
278
|
return {
|
|
254
279
|
id,
|
|
255
280
|
type,
|
|
@@ -265,9 +290,14 @@ export function createProviderConnectorStub(provider, type = providerCatalog.fin
|
|
|
265
290
|
// A successful provider result promotes this axis explicitly.
|
|
266
291
|
validationCoverage: "untested",
|
|
267
292
|
financialEvidence: "missing",
|
|
268
|
-
fieldsVerified: catalogEntry?.verifiedFields ?? [],
|
|
293
|
+
fieldsVerified: browserScaffold ? [] : (catalogEntry?.verifiedFields ?? []),
|
|
269
294
|
fieldsEstimated: [],
|
|
270
|
-
fieldsMissing:
|
|
295
|
+
fieldsMissing: browserScaffold
|
|
296
|
+
? Array.from(new Set([
|
|
297
|
+
...(catalogEntry?.missingFields ?? []),
|
|
298
|
+
browserAccountScaffoldMissingField
|
|
299
|
+
]))
|
|
300
|
+
: (catalogEntry?.missingFields ?? ["approved account/API/export source"]),
|
|
271
301
|
authMode: authModeForConnectorType(type, connectorEntry),
|
|
272
302
|
authScopes: connectorEntry?.scopes ?? [],
|
|
273
303
|
tokenStorage: tokenStorageForConnectorType(type, connectorEntry)
|
|
@@ -377,6 +407,7 @@ function normalizeApprovedSource(value) {
|
|
|
377
407
|
: isFinancialEvidence(value.verification)
|
|
378
408
|
? value.verification
|
|
379
409
|
: "missing";
|
|
410
|
+
const browserScaffold = value.type === "browser_account";
|
|
380
411
|
return {
|
|
381
412
|
id: value.id,
|
|
382
413
|
type: value.type,
|
|
@@ -385,17 +416,21 @@ function normalizeApprovedSource(value) {
|
|
|
385
416
|
...(provider ? { provider } : {}),
|
|
386
417
|
readOnly: true,
|
|
387
418
|
approvedAt: value.approvedAt,
|
|
388
|
-
scope: value.scope,
|
|
419
|
+
scope: browserScaffold ? defaultScopeForSource(value.type) : value.scope,
|
|
389
420
|
lane: value.lane,
|
|
390
421
|
accessMethod: value.accessMethod,
|
|
391
422
|
boundaryApproval: "approved",
|
|
392
|
-
validationCoverage:
|
|
393
|
-
?
|
|
394
|
-
:
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
423
|
+
validationCoverage: browserScaffold
|
|
424
|
+
? "untested"
|
|
425
|
+
: isValidationCoverage(value.validationCoverage)
|
|
426
|
+
? value.validationCoverage
|
|
427
|
+
: "untested",
|
|
428
|
+
financialEvidence: value.type === "local_folder" || browserScaffold ? "missing" : migratedEvidence,
|
|
429
|
+
fieldsVerified: browserScaffold ? [] : [...value.fieldsVerified],
|
|
430
|
+
fieldsEstimated: browserScaffold ? [] : [...value.fieldsEstimated],
|
|
431
|
+
fieldsMissing: browserScaffold
|
|
432
|
+
? Array.from(new Set([...value.fieldsMissing, browserAccountScaffoldMissingField]))
|
|
433
|
+
: [...value.fieldsMissing],
|
|
399
434
|
...(authMode ? { authMode } : {}),
|
|
400
435
|
...(value.authScopes ? { authScopes: [...value.authScopes] } : {}),
|
|
401
436
|
...(tokenStorage ? { tokenStorage } : {}),
|
|
@@ -443,7 +478,7 @@ export function buildMissingSourcePrompts(signals, registry) {
|
|
|
443
478
|
prompts.push({
|
|
444
479
|
provider,
|
|
445
480
|
status: "detected_unverified",
|
|
446
|
-
reason: `${provider} was detected locally, but no approved
|
|
481
|
+
reason: `${provider} was detected locally, but no approved implemented source has current financial evidence. Connector validation is reported separately.`,
|
|
447
482
|
detectedEvidence: detectedSignals.map((signal) => signal.evidence),
|
|
448
483
|
suggestedConnector: `connect ${provider} --type ${preferredType}`,
|
|
449
484
|
suggestedSourceTypes
|
|
@@ -490,7 +525,10 @@ function hasCurrentProviderFinancialEvidence(registry, provider) {
|
|
|
490
525
|
return source.boundaryApproval === "approved" &&
|
|
491
526
|
source.validationCoverage !== "failed" &&
|
|
492
527
|
source.financialEvidence !== "missing" &&
|
|
493
|
-
source.type !== "local_tool_detection"
|
|
528
|
+
source.type !== "local_tool_detection" &&
|
|
529
|
+
// browser_account is reserved in the schema but has no ingestion
|
|
530
|
+
// implementation. Repository state cannot promote the scaffold.
|
|
531
|
+
source.type !== "browser_account";
|
|
494
532
|
});
|
|
495
533
|
}
|
|
496
534
|
function validationCoverageForSource(provider, type) {
|
|
@@ -523,7 +561,7 @@ function defaultScopeForSource(type) {
|
|
|
523
561
|
return "Read-only provider API/account usage source. Store token references only; no raw secrets. No billing changes. No cloud upload.";
|
|
524
562
|
}
|
|
525
563
|
if (type === "browser_account") {
|
|
526
|
-
return "
|
|
564
|
+
return "Schema scaffold only. Browser-account ingestion is not implemented and cannot produce financial evidence or satisfy a missing-source prompt.";
|
|
527
565
|
}
|
|
528
566
|
if (type === "local_tool_detection") {
|
|
529
567
|
return "Read-only local CLI/tool detection path. Detection is not official provider-reported cost; connect an account API or export to add that evidence.";
|
package/dist/sourceStatus.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CostConfidence, UsageRecord } from "./schema.js";
|
|
2
|
+
import type { SourceRegistry } from "./sourceRegistry.js";
|
|
2
3
|
import type { LocalAgentFormatId } from "./localAgentFormats/types.js";
|
|
3
4
|
/**
|
|
4
5
|
* How thoroughly an ingestion path itself has been exercised.
|
|
@@ -12,6 +13,8 @@ export type SourceValidationCoverage = typeof sourceValidationCoverageValues[num
|
|
|
12
13
|
export type FinancialEvidenceStatus = CostConfidence;
|
|
13
14
|
export declare const sourceFreshnessStatusValues: readonly ["fresh", "stale", "not_checked"];
|
|
14
15
|
export type SourceFreshnessStatus = typeof sourceFreshnessStatusValues[number];
|
|
16
|
+
export declare const providerContractStateValues: readonly ["current", "stale_contract"];
|
|
17
|
+
export type ProviderContractState = typeof providerContractStateValues[number];
|
|
15
18
|
export type SourceStatusId = LocalAgentFormatId | "openai" | "anthropic" | "cursor" | "github-copilot";
|
|
16
19
|
export type SourceStatusDefinition = {
|
|
17
20
|
id: SourceStatusId;
|
|
@@ -19,6 +22,8 @@ export type SourceStatusDefinition = {
|
|
|
19
22
|
validationCoverage: SourceValidationCoverage;
|
|
20
23
|
validationNote: string;
|
|
21
24
|
staleAfterHours: number;
|
|
25
|
+
/** Review state of the provider/parser financial semantics used by this source. */
|
|
26
|
+
contractState?: ProviderContractState;
|
|
22
27
|
};
|
|
23
28
|
export type SourceStatusObservation = {
|
|
24
29
|
id: SourceStatusId;
|
|
@@ -32,6 +37,8 @@ export type SourceStatusObservation = {
|
|
|
32
37
|
lastError?: string;
|
|
33
38
|
/** Runtime failures may override the shipped validation baseline. */
|
|
34
39
|
validationCoverage?: SourceValidationCoverage;
|
|
40
|
+
/** A drift monitor or reviewed contract update may fail this source closed. */
|
|
41
|
+
contractState?: ProviderContractState;
|
|
35
42
|
};
|
|
36
43
|
export type SourceStatusFreshness = {
|
|
37
44
|
status: SourceFreshnessStatus;
|
|
@@ -46,6 +53,7 @@ export type SourceStatus = {
|
|
|
46
53
|
validationNote: string;
|
|
47
54
|
financialEvidence: FinancialEvidenceStatus;
|
|
48
55
|
financialEvidenceNote: string;
|
|
56
|
+
contractState?: ProviderContractState;
|
|
49
57
|
freshness: SourceStatusFreshness;
|
|
50
58
|
lastError?: string;
|
|
51
59
|
};
|
|
@@ -55,12 +63,26 @@ export type SourceStatus = {
|
|
|
55
63
|
* reconciliation.
|
|
56
64
|
*/
|
|
57
65
|
export declare const sourceStatusDefinitions: readonly SourceStatusDefinition[];
|
|
58
|
-
export declare function buildSourceStatuses(observations?: readonly SourceStatusObservation[], now?: Date): SourceStatus[];
|
|
66
|
+
export declare function buildSourceStatuses(observations?: readonly SourceStatusObservation[], now?: Date, definitions?: readonly SourceStatusDefinition[]): SourceStatus[];
|
|
59
67
|
/**
|
|
60
68
|
* Reduce one source's current rows to the evidence label used for its headline.
|
|
61
69
|
* Verified billed cost wins; otherwise estimates win over an unpriced signal.
|
|
62
70
|
*/
|
|
63
71
|
export declare function financialEvidenceForRecords(records: readonly UsageRecord[]): FinancialEvidenceStatus;
|
|
72
|
+
/**
|
|
73
|
+
* Apply the reviewed provider-contract gate before any connected financial
|
|
74
|
+
* calculation. Local transcript evidence is unchanged. When a provider's
|
|
75
|
+
* financial semantics are stale, its priced rows remain present for audit and
|
|
76
|
+
* attribution but cannot carry a dollar amount or proof-level confidence.
|
|
77
|
+
*/
|
|
78
|
+
export declare function applyProviderContractGate(records: readonly UsageRecord[], definitions?: readonly SourceStatusDefinition[]): UsageRecord[];
|
|
79
|
+
/**
|
|
80
|
+
* Project persisted source metadata through the same fail-closed contract
|
|
81
|
+
* gate as records. This is read-time only: the signed local receipt remains
|
|
82
|
+
* byte-exact, while an upgraded release cannot repeat an obsolete verified
|
|
83
|
+
* claim from an older sources.json.
|
|
84
|
+
*/
|
|
85
|
+
export declare function applyProviderContractGateToSourceRegistry(registry: SourceRegistry, definitions?: readonly SourceStatusDefinition[]): SourceRegistry;
|
|
64
86
|
/** Stable, plain-text formatter shared by terminal surfaces. */
|
|
65
87
|
export declare function formatSourceStatuses(statuses: readonly SourceStatus[]): string;
|
|
66
88
|
//# sourceMappingURL=sourceStatus.d.ts.map
|
package/dist/sourceStatus.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { localAgentFormatDescriptors } from "./localAgentFormats/registry.js";
|
|
2
|
+
import { generatedProviderContractStates } from "./providerContractStates.generated.js";
|
|
2
3
|
/**
|
|
3
4
|
* How thoroughly an ingestion path itself has been exercised.
|
|
4
5
|
*
|
|
@@ -12,6 +13,7 @@ export const sourceValidationCoverageValues = [
|
|
|
12
13
|
"failed"
|
|
13
14
|
];
|
|
14
15
|
export const sourceFreshnessStatusValues = ["fresh", "stale", "not_checked"];
|
|
16
|
+
export const providerContractStateValues = ["current", "stale_contract"];
|
|
15
17
|
/**
|
|
16
18
|
* Shipped validation matrix. Keep these claims proof-conservative: a live
|
|
17
19
|
* authentication check is not the same as a non-empty end-to-end billing
|
|
@@ -23,48 +25,67 @@ export const sourceStatusDefinitions = [
|
|
|
23
25
|
label: `${descriptor.label} local logs`,
|
|
24
26
|
validationCoverage: descriptor.confidenceDefaults.validationCoverage,
|
|
25
27
|
validationNote: descriptor.validationNote,
|
|
26
|
-
staleAfterHours: 72
|
|
28
|
+
staleAfterHours: 72,
|
|
29
|
+
...(descriptor.id === "gemini-cli"
|
|
30
|
+
? { contractState: generatedProviderContractStates["gemini-cli"] }
|
|
31
|
+
: {})
|
|
27
32
|
})),
|
|
28
33
|
{
|
|
29
34
|
id: "openai",
|
|
30
35
|
label: "OpenAI Costs and Usage API",
|
|
31
36
|
validationCoverage: "live_verified",
|
|
32
37
|
validationNote: "Product connector QA exercised non-empty Admin cost and usage API paths; the tested Costs total reconciled to invoiced API credits less the provider-UI balance with $0.00 variance. This does not reconcile the current user's account; final invoices, tax, discounts, and later adjustments remain separate.",
|
|
33
|
-
staleAfterHours: 48
|
|
38
|
+
staleAfterHours: 48,
|
|
39
|
+
contractState: generatedProviderContractStates.openai
|
|
34
40
|
},
|
|
35
41
|
{
|
|
36
42
|
id: "anthropic",
|
|
37
43
|
label: "Anthropic Cost Report and Claude Code Analytics",
|
|
38
44
|
validationCoverage: "live_verified",
|
|
39
45
|
validationNote: "Admin cost and Claude Code usage paths are exercised with non-empty live records.",
|
|
40
|
-
staleAfterHours: 48
|
|
46
|
+
staleAfterHours: 48,
|
|
47
|
+
contractState: generatedProviderContractStates.anthropic
|
|
41
48
|
},
|
|
42
49
|
{
|
|
43
50
|
id: "cursor",
|
|
44
51
|
label: "Cursor Admin API",
|
|
45
52
|
validationCoverage: "fixture_verified",
|
|
46
53
|
validationNote: "Canonical teamMemberSpend envelopes, totalPages pagination, completeness failures, and malformed responses pass recorded fixtures; live account QA is pending.",
|
|
47
|
-
staleAfterHours: 48
|
|
54
|
+
staleAfterHours: 48,
|
|
55
|
+
contractState: generatedProviderContractStates.cursor
|
|
48
56
|
},
|
|
49
57
|
{
|
|
50
58
|
id: "github-copilot",
|
|
51
59
|
label: "GitHub Copilot organization APIs",
|
|
52
60
|
validationCoverage: "fixture_verified",
|
|
53
61
|
validationNote: "The 2026-03-10 signed-NDJSON metrics workflow, seat pagination, per-seat plan types, and failure paths pass recorded fixtures; live account QA is pending.",
|
|
54
|
-
staleAfterHours: 48
|
|
62
|
+
staleAfterHours: 48,
|
|
63
|
+
contractState: generatedProviderContractStates["github-copilot"]
|
|
55
64
|
}
|
|
56
65
|
];
|
|
57
|
-
export function buildSourceStatuses(observations = [], now = new Date()) {
|
|
66
|
+
export function buildSourceStatuses(observations = [], now = new Date(), definitions = sourceStatusDefinitions) {
|
|
58
67
|
const byId = new Map(observations.map((observation) => [observation.id, observation]));
|
|
59
|
-
return
|
|
68
|
+
return definitions.map((definition) => {
|
|
60
69
|
const observation = byId.get(definition.id);
|
|
70
|
+
const observedContractState = observation?.contractState;
|
|
71
|
+
const contractState = definition.contractState === "stale_contract" || observedContractState === "stale_contract"
|
|
72
|
+
? "stale_contract"
|
|
73
|
+
: definition.contractState;
|
|
74
|
+
const observedFinancialEvidence = observation?.financialEvidence ?? "missing";
|
|
75
|
+
const financialEvidence = contractState === "stale_contract" && observedFinancialEvidence === "verified"
|
|
76
|
+
? "missing"
|
|
77
|
+
: observedFinancialEvidence;
|
|
78
|
+
const financialEvidenceNote = contractState === "stale_contract" && observedFinancialEvidence === "verified"
|
|
79
|
+
? "Provider contract drift is unresolved; the verified financial headline is withheld pending human review."
|
|
80
|
+
: observation?.financialEvidenceNote ?? "No current financial evidence was observed on this machine.";
|
|
61
81
|
return {
|
|
62
82
|
id: definition.id,
|
|
63
83
|
label: definition.label,
|
|
64
84
|
validationCoverage: observation?.validationCoverage ?? definition.validationCoverage,
|
|
65
85
|
validationNote: definition.validationNote,
|
|
66
|
-
financialEvidence
|
|
67
|
-
financialEvidenceNote
|
|
86
|
+
financialEvidence,
|
|
87
|
+
financialEvidenceNote,
|
|
88
|
+
...(contractState ? { contractState } : {}),
|
|
68
89
|
freshness: sourceFreshness(definition, observation, now),
|
|
69
90
|
...(observation?.lastError ? { lastError: observation.lastError } : {})
|
|
70
91
|
};
|
|
@@ -86,6 +107,79 @@ export function financialEvidenceForRecords(records) {
|
|
|
86
107
|
}
|
|
87
108
|
return "missing";
|
|
88
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Apply the reviewed provider-contract gate before any connected financial
|
|
112
|
+
* calculation. Local transcript evidence is unchanged. When a provider's
|
|
113
|
+
* financial semantics are stale, its priced rows remain present for audit and
|
|
114
|
+
* attribution but cannot carry a dollar amount or proof-level confidence.
|
|
115
|
+
*/
|
|
116
|
+
export function applyProviderContractGate(records, definitions = sourceStatusDefinitions) {
|
|
117
|
+
const contractStates = new Map(definitions
|
|
118
|
+
.filter((definition) => definition.contractState !== undefined)
|
|
119
|
+
.map((definition) => [definition.id, definition.contractState]));
|
|
120
|
+
return records.map((record) => {
|
|
121
|
+
if (record.providerCostType === "local_agent_logs")
|
|
122
|
+
return record;
|
|
123
|
+
const sourceId = providerStatusIdForRecord(record);
|
|
124
|
+
if (!sourceId || contractStates.get(sourceId) !== "stale_contract")
|
|
125
|
+
return record;
|
|
126
|
+
return {
|
|
127
|
+
...record,
|
|
128
|
+
source: { ...record.source, confidence: "missing" },
|
|
129
|
+
amountUsd: null,
|
|
130
|
+
costConfidence: "missing"
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Project persisted source metadata through the same fail-closed contract
|
|
136
|
+
* gate as records. This is read-time only: the signed local receipt remains
|
|
137
|
+
* byte-exact, while an upgraded release cannot repeat an obsolete verified
|
|
138
|
+
* claim from an older sources.json.
|
|
139
|
+
*/
|
|
140
|
+
export function applyProviderContractGateToSourceRegistry(registry, definitions = sourceStatusDefinitions) {
|
|
141
|
+
const states = new Map(definitions
|
|
142
|
+
.filter((definition) => definition.contractState !== undefined)
|
|
143
|
+
.map((definition) => [definition.id, definition.contractState]));
|
|
144
|
+
return {
|
|
145
|
+
...registry,
|
|
146
|
+
approvedSources: registry.approvedSources.map((source) => {
|
|
147
|
+
if (source.type !== "provider_api")
|
|
148
|
+
return source;
|
|
149
|
+
const statusId = providerStatusIdForProvider(source.provider);
|
|
150
|
+
if (!statusId || states.get(statusId) !== "stale_contract")
|
|
151
|
+
return source;
|
|
152
|
+
const baseScope = source.scope.split(" Last successful pull produced ")[0]?.trim() || source.scope;
|
|
153
|
+
return {
|
|
154
|
+
...source,
|
|
155
|
+
financialEvidence: "missing",
|
|
156
|
+
fieldsMissing: Array.from(new Set([
|
|
157
|
+
...source.fieldsMissing,
|
|
158
|
+
"provider financial headline (contract review required)"
|
|
159
|
+
])),
|
|
160
|
+
scope: `${baseScope} Provider contract drift is unresolved; prior financial evidence and headline are withheld.`
|
|
161
|
+
};
|
|
162
|
+
})
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function providerStatusIdForRecord(record) {
|
|
166
|
+
if (record.source.provider === "google" && record.agentId === "gemini-cli")
|
|
167
|
+
return "gemini-cli";
|
|
168
|
+
return providerStatusIdForProvider(record.source.provider);
|
|
169
|
+
}
|
|
170
|
+
function providerStatusIdForProvider(provider) {
|
|
171
|
+
if (provider === "openai")
|
|
172
|
+
return "openai";
|
|
173
|
+
if (provider === "anthropic")
|
|
174
|
+
return "anthropic";
|
|
175
|
+
if (provider === "cursor")
|
|
176
|
+
return "cursor";
|
|
177
|
+
if (provider === "github" || provider === "github-copilot" || provider === "copilot")
|
|
178
|
+
return "github-copilot";
|
|
179
|
+
if (provider === "google" || provider === "gemini")
|
|
180
|
+
return "gemini-cli";
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
89
183
|
/** Stable, plain-text formatter shared by terminal surfaces. */
|
|
90
184
|
export function formatSourceStatuses(statuses) {
|
|
91
185
|
return statuses.map((status) => {
|
|
@@ -93,6 +187,7 @@ export function formatSourceStatuses(statuses) {
|
|
|
93
187
|
return [
|
|
94
188
|
`${status.label} (${status.id})`,
|
|
95
189
|
` validation coverage: ${status.validationCoverage}`,
|
|
190
|
+
...(status.contractState ? [` provider contract: ${status.contractState}`] : []),
|
|
96
191
|
` financial evidence: ${status.financialEvidence}`,
|
|
97
192
|
` freshness: ${freshness}`,
|
|
98
193
|
` last error: ${status.lastError ? singleLineStatusText(status.lastError) : "none recorded"}`,
|
package/dist/stateTrust.js
CHANGED
|
@@ -39,7 +39,7 @@ export async function verifyConnectedSpendTrustReceipt(rootPath, exactSpendConte
|
|
|
39
39
|
if (isNodeError(error, "ENOENT")) {
|
|
40
40
|
return missingTrust();
|
|
41
41
|
}
|
|
42
|
-
return invalidTrust(
|
|
42
|
+
return invalidTrust("the external trust directory failed safety validation");
|
|
43
43
|
}
|
|
44
44
|
let rawReceipt;
|
|
45
45
|
try {
|
|
@@ -48,7 +48,7 @@ export async function verifyConnectedSpendTrustReceipt(rootPath, exactSpendConte
|
|
|
48
48
|
catch (error) {
|
|
49
49
|
if (isNodeError(error, "ENOENT"))
|
|
50
50
|
return missingTrust();
|
|
51
|
-
return invalidTrust(
|
|
51
|
+
return invalidTrust("the external provider-sync receipt failed safety validation");
|
|
52
52
|
}
|
|
53
53
|
let receipt;
|
|
54
54
|
try {
|
package/package.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-finops/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
7
14
|
"scripts": {
|
|
8
15
|
"build": "tsc -b",
|
|
9
16
|
"typecheck": "tsc -b --pretty false",
|