@agentguard-run/spend 0.15.1 → 0.15.3

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 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.computeDagEntryHash = exports.compressReceiptDag = exports.receiptSchemaVersion = exports.isReceiptV3 = exports.assertReceiptV3 = exports.isOpenRouterHost = exports.clearManagedOpenRouterKeyCache = exports.fetchManagedOpenRouterKey = exports.modelCostFromOpenRouter = exports.persistOverrides = exports.syncPricingIntoCostTable = exports.getCachedCatalog = exports.fetchCatalog = exports.recordTelemetryEvent = exports.resetTelemetryInstallId = exports.disableTelemetry = exports.enableTelemetry = exports.telemetryStatus = exports.licenseKeyFingerprint = exports.clearConfiguredLicenseKey = exports.writeConfiguredLicenseKey = exports.readConfiguredLicenseKey = exports.validateAndRegisterLicense = exports.validateLicenseKey = exports.AgentGuardLicenseRequiredError = exports.InMemorySpendStore = exports.InMemoryDecisionLogStore = exports.GENESIS_PREVIOUS_HASH = exports.verifyChain = exports.verifyEntry = exports.signDecision = exports.computeSignerFingerprint = exports.computeEntryHash = exports.sha256Hex = exports.canonicalJson = exports.BUILT_IN_KEYWORD_WATCHLISTS = exports.findCustomKeywordWatchlists = exports.enforcePolicyLicenseGates = exports.adjustPolicyWindowSpend = exports.buildScopeKey = exports.evaluatePolicy = exports.knownModelCosts = exports.persistCostOverrides = exports.loadPersistedCostOverrides = exports.listCostOverrides = exports.inferProvider = exports.computeCallCents = exports.clearCostOverrides = exports.setCostOverride = exports.getModelCost = void 0;
25
25
  exports.formatBlockedTrace = exports.t = exports.resolveLocale = exports.TRANSLATIONS = exports.SUPPORTED_LOCALES = exports.DEFAULT_LOCALE = exports.defaultKey = exports.recommend = exports.withSpendGuardBedrock = exports.withSpendGuardAnthropic = exports.AgentGuardBlockedError = exports.withSpendGuard = exports.SpendGuard = exports.writeAdvisorOutputs = exports.createAdvisorSessionLogger = exports.resolveAdvisorApiKey = exports.createAdvisorClient = exports.projectedSavings = exports.buildPolicyFromProfile = exports.buildBusinessProfile = exports.AdvisorConversation = exports.AgentGuardWorkflowStateError = exports.AgentGuardDurationCapError = exports.AgentGuardChainCorruptError = exports.AgentGuardBudgetCapError = exports.validateReceiptChain = exports.computeChainHash = exports.WorkflowContext = exports.workflow = exports.verifyConsentReceipt = exports.enforceCompliance = exports.AgentGuardConsentRequiredError = exports.AgentGuardComplianceError = exports.isForeignOriginModel = exports.inferCompliance = exports.inferHosting = exports.inferModelIdentity = exports.inferProviderRoute = exports.PROVIDER_REGISTRY = exports.captureProvenance = exports.verifyReceiptDag = exports.verifyInclusion = exports.signDagNode = exports.msgHashForDecision = exports.merkleRoot = exports.merkleInclusionProof = exports.gateCapability = exports.deriveTrustScore = exports.deriveCapability = exports.dagEnvelopeFromEntries = void 0;
26
- exports.PATENT_NOTICE = exports.agentguard = exports.AGENTGUARD_SPEND_VERSION = void 0;
26
+ exports.formatLineage = exports.workloadProvenance = exports.centsToUsd = exports.decisionCents = exports.classifyOrigin = exports.aggregateLedger = exports.PATENT_NOTICE = exports.agentguard = exports.AGENTGUARD_SPEND_VERSION = void 0;
27
27
  const context_1 = require("./workflow/context");
28
28
  // Cost table
29
29
  var cost_table_1 = require("./cost-table");
@@ -160,7 +160,7 @@ Object.defineProperty(exports, "TRANSLATIONS", { enumerable: true, get: function
160
160
  Object.defineProperty(exports, "resolveLocale", { enumerable: true, get: function () { return i18n_1.resolveLocale; } });
161
161
  Object.defineProperty(exports, "t", { enumerable: true, get: function () { return i18n_1.t; } });
162
162
  Object.defineProperty(exports, "formatBlockedTrace", { enumerable: true, get: function () { return i18n_1.formatBlockedTrace; } });
163
- exports.AGENTGUARD_SPEND_VERSION = '0.15.1';
163
+ exports.AGENTGUARD_SPEND_VERSION = '0.15.2';
164
164
  exports.agentguard = {
165
165
  workflow: context_1.workflow,
166
166
  };
@@ -174,4 +174,14 @@ __exportStar(require("./governance"), exports);
174
174
  __exportStar(require("./byo"), exports);
175
175
  __exportStar(require("./frameworks"), exports);
176
176
  __exportStar(require("./operating-pack"), exports);
177
+ // CFO cost dashboard (read side)
178
+ var aggregate_1 = require("./dashboard/aggregate");
179
+ Object.defineProperty(exports, "aggregateLedger", { enumerable: true, get: function () { return aggregate_1.aggregateLedger; } });
180
+ Object.defineProperty(exports, "classifyOrigin", { enumerable: true, get: function () { return aggregate_1.classifyOrigin; } });
181
+ Object.defineProperty(exports, "decisionCents", { enumerable: true, get: function () { return aggregate_1.decisionCents; } });
182
+ Object.defineProperty(exports, "centsToUsd", { enumerable: true, get: function () { return aggregate_1.centsToUsd; } });
183
+ // Model-agnostic provenance that survives hot-swap
184
+ var portability_1 = require("./receipts/portability");
185
+ Object.defineProperty(exports, "workloadProvenance", { enumerable: true, get: function () { return portability_1.workloadProvenance; } });
186
+ Object.defineProperty(exports, "formatLineage", { enumerable: true, get: function () { return portability_1.formatLineage; } });
177
187
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;;;;;;;;;;;;;;;;AAEH,gDAA6D;AAmB7D,aAAa;AACb,2CAWsB;AAVpB,0GAAA,YAAY,OAAA;AACZ,6GAAA,eAAe,OAAA;AACf,gHAAA,kBAAkB,OAAA;AAClB,8GAAA,gBAAgB,OAAA;AAChB,2GAAA,aAAa,OAAA;AACb,+GAAA,iBAAiB,OAAA;AACjB,wHAAA,0BAA0B,OAAA;AAC1B,kHAAA,oBAAoB,OAAA;AACpB,6GAAA,eAAe,OAAA;AAIjB,gBAAgB;AAChB,mCAAuK;AAA9J,wGAAA,cAAc,OAAA;AAAE,uGAAA,aAAa,OAAA;AAAE,iHAAA,uBAAuB,OAAA;AAAE,mHAAA,yBAAyB,OAAA;AAAE,qHAAA,2BAA2B,OAAA;AAAE,qHAAA,2BAA2B,OAAA;AAEpJ,eAAe;AACf,+CAUwB;AATtB,6GAAA,aAAa,OAAA;AACb,yGAAA,SAAS,OAAA;AACT,gHAAA,gBAAgB,OAAA;AAChB,wHAAA,wBAAwB,OAAA;AACxB,4GAAA,YAAY,OAAA;AACZ,2GAAA,WAAW,OAAA;AACX,2GAAA,WAAW,OAAA;AACX,qHAAA,qBAAqB,OAAA;AACrB,wHAAA,wBAAwB,OAAA;AAG1B,wBAAwB;AACxB,+CAAoD;AAA3C,kHAAA,kBAAkB,OAAA;AAE3B,mBAAmB;AACnB,qCAWmB;AAVjB,yHAAA,8BAA8B,OAAA;AAC9B,6GAAA,kBAAkB,OAAA;AAClB,qHAAA,0BAA0B,OAAA;AAC1B,mHAAA,wBAAwB,OAAA;AACxB,oHAAA,yBAAyB,OAAA;AACzB,oHAAA,yBAAyB,OAAA;AACzB,gHAAA,qBAAqB,OAAA;AAMvB,qBAAqB;AACrB,yCAOqB;AANnB,4GAAA,eAAe,OAAA;AACf,4GAAA,eAAe,OAAA;AACf,6GAAA,gBAAgB,OAAA;AAChB,oHAAA,uBAAuB,OAAA;AACvB,iHAAA,oBAAoB,OAAA;AAItB,qBAAqB;AACrB,2DAS8B;AAR5B,kHAAA,YAAY,OAAA;AACZ,sHAAA,gBAAgB,OAAA;AAChB,8HAAA,wBAAwB,OAAA;AACxB,sHAAA,gBAAgB,OAAA;AAChB,6HAAA,uBAAuB,OAAA;AAMzB,oDAMgC;AAL9B,sHAAA,yBAAyB,OAAA;AACzB,2HAAA,8BAA8B,OAAA;AAC9B,6GAAA,gBAAgB,OAAA;AAoBlB,4CAAuF;AAA9E,yGAAA,eAAe,OAAA;AAAE,qGAAA,WAAW,OAAA;AAAE,8GAAA,oBAAoB,OAAA;AAC3D,sCAuBwB;AAtBtB,yGAAA,kBAAkB,OAAA;AAClB,0GAAA,mBAAmB,OAAA;AACnB,6GAAA,sBAAsB,OAAA;AACtB,uGAAA,gBAAgB,OAAA;AAChB,uGAAA,gBAAgB,OAAA;AAChB,qGAAA,cAAc,OAAA;AACd,2GAAA,oBAAoB,OAAA;AACpB,iGAAA,UAAU,OAAA;AACV,yGAAA,kBAAkB,OAAA;AAClB,kGAAA,WAAW,OAAA;AACX,sGAAA,eAAe,OAAA;AACf,uGAAA,gBAAgB,OAAA;AAYlB,sDAAuG;AAA9F,+GAAA,iBAAiB,OAAA;AAC1B,oEAAgK;AAAvJ,sHAAA,iBAAiB,OAAA;AAAE,uHAAA,kBAAkB,OAAA;AAAE,uHAAA,kBAAkB,OAAA;AAAE,iHAAA,YAAY,OAAA;AAAE,oHAAA,eAAe,OAAA;AAAE,yHAAA,oBAAoB,OAAA;AACvH,6CAA+J;AAAtJ,oHAAA,yBAAyB,OAAA;AAAE,yHAAA,8BAA8B,OAAA;AAAE,4GAAA,iBAAiB,OAAA;AAAE,+GAAA,oBAAoB,OAAA;AAE3G,qBAAqB;AACrB,8CAA+D;AAAtD,mGAAA,QAAQ,OAAA;AAAE,0GAAA,eAAe,OAAA;AAClC,8DAAoF;AAA3E,mHAAA,gBAAgB,OAAA;AAAE,uHAAA,oBAAoB,OAAA;AAC/C,4CAK2B;AAJzB,kHAAA,wBAAwB,OAAA;AACxB,qHAAA,2BAA2B,OAAA;AAC3B,oHAAA,0BAA0B,OAAA;AAC1B,sHAAA,4BAA4B,OAAA;AAY9B,sBAAsB;AACtB,uDAOgC;AAN9B,mHAAA,mBAAmB,OAAA;AACnB,oHAAA,oBAAoB,OAAA;AACpB,sHAAA,sBAAsB,OAAA;AACtB,gHAAA,gBAAgB,OAAA;AAIlB,mDAA2H;AAAlH,iHAAA,mBAAmB,OAAA;AAAE,kHAAA,oBAAoB,OAAA;AAClD,2CAAwG;AAA/F,oHAAA,0BAA0B,OAAA;AAAE,6GAAA,mBAAmB,OAAA;AAExD,eAAe;AACf,6CAQuB;AAPrB,yGAAA,UAAU,OAAA;AACV,6GAAA,cAAc,OAAA;AACd,qHAAA,sBAAsB,OAAA;AAOxB,kDAA6F;AAApF,oHAAA,uBAAuB,OAAA;AAChC,8CAAuF;AAA9E,gHAAA,qBAAqB,OAAA;AAC9B,mCAAuG;AAA9F,mGAAA,SAAS,OAAA;AAAE,oGAAA,UAAU,OAAA;AAE9B,sCAAsC;AACtC,+BASgB;AARd,sGAAA,cAAc,OAAA;AACd,yGAAA,iBAAiB,OAAA;AACjB,oGAAA,YAAY,OAAA;AAEZ,qGAAA,aAAa,OAAA;AACb,yFAAA,CAAC,OAAA;AACD,0GAAA,kBAAkB,OAAA;AAIP,QAAA,wBAAwB,GAAG,QAAQ,CAAC;AAGpC,QAAA,UAAU,GAAG;IACxB,QAAQ,EAAE,kBAAW;CACtB,CAAC;AAEF,2DAA2D;AAC9C,QAAA,aAAa,GACxB,8CAA8C;IAC9C,6DAA6D;IAC7D,sDAAsD,CAAC;AACzD,6CAA2B;AAC3B,8CAA4B;AAC5B,+CAA6B;AAC7B,wCAAsB;AAEtB,+CAA6B;AAC7B,mDAAiC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * AgentGuard® — model-agnostic provenance that survives hot-swap.
3
+ *
4
+ * The "headless" / model-fungibility problem (raised on All-In E280): teams want
5
+ * to hot-swap models — frontier → open-weight → self-hosted — per task to cut
6
+ * cost, but "no one has figured out how to abstract memory/context/AUDIT away
7
+ * from the model." A provider console can't help: it only sees its own tokens,
8
+ * and the trail breaks the moment you route to a different provider.
9
+ *
10
+ * AgentGuard's signed decision log already records, per call, which model served
11
+ * it (provenance) inside one continuous hash chain. This module reads that log
12
+ * and produces a PORTABLE, model-agnostic provenance manifest per workload: the
13
+ * ordered model lineage, every swap, the frontier/dark origin mix, and a single
14
+ * tamper-evidence verdict that spans the whole swap sequence. The audit is keyed
15
+ * to the workload, not the model — so it survives the swap.
16
+ *
17
+ * Honest scope note (same as the dashboard): the signed SpendDecision does not
18
+ * carry a first-class workload/agent id (only `triggeredScopeKey`). Callers pass
19
+ * a `keyOf` selector to group; the default uses `triggeredScopeKey`. We never
20
+ * invent a grouping the ledger does not support.
21
+ *
22
+ * Patent notice: signed hash-chained decision log (U.S. application filed May
23
+ * 2026); composes with DAG Trust Attestation, Patent D §7.3 (App. No.
24
+ * 63/984,626). AgentGuard® is a U.S. registered trademark (Reg. No. 8281464) of
25
+ * Dunecrest Ventures Inc.
26
+ */
27
+ import type { SignedDecisionLogEntry, SpendDecision } from '../types';
28
+ import { type TokenOrigin } from '../dashboard/aggregate';
29
+ export interface ModelUse {
30
+ model: string;
31
+ origin: TokenOrigin;
32
+ provider: string;
33
+ calls: number;
34
+ cents: number;
35
+ }
36
+ export interface ModelSwap {
37
+ atSequence: number;
38
+ fromModel: string;
39
+ toModel: string;
40
+ fromOrigin: TokenOrigin;
41
+ toOrigin: TokenOrigin;
42
+ /** True when the swap crossed the frontier↔dark boundary (the audit-risky kind). */
43
+ crossesBoundary: boolean;
44
+ }
45
+ export interface WorkloadProvenance {
46
+ key: string;
47
+ calls: number;
48
+ spentCents: number;
49
+ frontierCents: number;
50
+ darkCents: number;
51
+ firstAt: string | null;
52
+ lastAt: string | null;
53
+ models: ModelUse[];
54
+ /** Ordered model lineage, e.g. ['claude-5','gpt-5-mini','glm-5.2']. */
55
+ lineage: string[];
56
+ swaps: ModelSwap[];
57
+ }
58
+ export interface WorkloadProvenanceReport {
59
+ workloads: WorkloadProvenance[];
60
+ /** Chain-level tamper evidence spans ALL swaps — the point of the manifest. */
61
+ continuity: {
62
+ verified: boolean;
63
+ entries: number;
64
+ reason?: string;
65
+ };
66
+ }
67
+ /**
68
+ * Build the portable per-workload provenance manifest from signed ledger
69
+ * entries. Pass the async `verifyChain` result as `continuity` so this stays a
70
+ * pure, synchronous function (never null-continuity in production — a manifest
71
+ * without tamper evidence is not the product).
72
+ */
73
+ export declare function workloadProvenance(entries: SignedDecisionLogEntry[], continuity?: WorkloadProvenanceReport['continuity'], keyOf?: (d: SpendDecision) => string): WorkloadProvenanceReport;
74
+ /** Human-readable lineage, e.g. "claude-5 → gpt-5-mini → glm-5.2". */
75
+ export declare const formatLineage: (w: WorkloadProvenance) => string;
76
+ //# sourceMappingURL=portability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portability.d.ts","sourceRoot":"","sources":["../../src/receipts/portability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACtE,OAAO,EAAiC,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEzF,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,WAAW,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,oFAAoF;IACpF,eAAe,EAAE,OAAO,CAAC;CAC1B;AACD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,uEAAuE;IACvE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AACD,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,kBAAkB,EAAE,CAAC;IAChC,+EAA+E;IAC/E,UAAU,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,sBAAsB,EAAE,EACjC,UAAU,GAAE,wBAAwB,CAAC,YAAY,CAA0D,EAC3G,KAAK,GAAE,CAAC,CAAC,EAAE,aAAa,KAAK,MAAqB,GACjD,wBAAwB,CA4E1B;AAED,sEAAsE;AACtE,eAAO,MAAM,aAAa,GAAI,GAAG,kBAAkB,KAAG,MAA+B,CAAC"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatLineage = void 0;
4
+ exports.workloadProvenance = workloadProvenance;
5
+ const aggregate_1 = require("../dashboard/aggregate");
6
+ const defaultKeyOf = (d) => d.triggeredScopeKey ?? 'default';
7
+ /**
8
+ * Build the portable per-workload provenance manifest from signed ledger
9
+ * entries. Pass the async `verifyChain` result as `continuity` so this stays a
10
+ * pure, synchronous function (never null-continuity in production — a manifest
11
+ * without tamper evidence is not the product).
12
+ */
13
+ function workloadProvenance(entries, continuity = { verified: false, entries: 0, reason: 'NOT_CHECKED' }, keyOf = defaultKeyOf) {
14
+ const groups = new Map();
15
+ for (const entry of entries) {
16
+ const d = entry.decision;
17
+ const kind = d.entryType ?? 'decision';
18
+ if (kind !== 'decision')
19
+ continue; // settlements/outcomes don't add model steps
20
+ const key = keyOf(d);
21
+ const grp = groups.get(key) ?? {
22
+ calls: 0, spent: 0, frontier: 0, dark: 0, first: null, last: null,
23
+ models: new Map(), ordered: [],
24
+ };
25
+ const model = d.modelResolved || d.modelRequested || 'unknown';
26
+ const { origin } = (0, aggregate_1.classifyOrigin)(d);
27
+ const cents = (0, aggregate_1.decisionCents)(d);
28
+ grp.calls += 1;
29
+ grp.spent += cents;
30
+ if (origin === 'frontier')
31
+ grp.frontier += cents;
32
+ else if (origin === 'dark')
33
+ grp.dark += cents;
34
+ if (d.timestamp) {
35
+ if (!grp.first || d.timestamp < grp.first)
36
+ grp.first = d.timestamp;
37
+ if (!grp.last || d.timestamp > grp.last)
38
+ grp.last = d.timestamp;
39
+ }
40
+ const mu = grp.models.get(model) ?? { model, origin, provider: d.provider ?? 'unknown', calls: 0, cents: 0 };
41
+ mu.calls += 1;
42
+ mu.cents += cents;
43
+ grp.models.set(model, mu);
44
+ grp.ordered.push({ seq: entry.sequence, model, origin });
45
+ groups.set(key, grp);
46
+ }
47
+ const workloads = [];
48
+ for (const [key, grp] of groups) {
49
+ grp.ordered.sort((a, b) => a.seq - b.seq);
50
+ const swaps = [];
51
+ for (let i = 1; i < grp.ordered.length; i++) {
52
+ const prev = grp.ordered[i - 1];
53
+ const cur = grp.ordered[i];
54
+ if (cur.model !== prev.model) {
55
+ swaps.push({
56
+ atSequence: cur.seq,
57
+ fromModel: prev.model, toModel: cur.model,
58
+ fromOrigin: prev.origin, toOrigin: cur.origin,
59
+ crossesBoundary: prev.origin !== cur.origin
60
+ && prev.origin !== 'unknown' && cur.origin !== 'unknown',
61
+ });
62
+ }
63
+ }
64
+ // lineage = models in first-appearance order (collapse consecutive repeats)
65
+ const lineage = [];
66
+ for (const step of grp.ordered)
67
+ if (lineage[lineage.length - 1] !== step.model)
68
+ lineage.push(step.model);
69
+ workloads.push({
70
+ key,
71
+ calls: grp.calls,
72
+ spentCents: grp.spent,
73
+ frontierCents: grp.frontier,
74
+ darkCents: grp.dark,
75
+ firstAt: grp.first,
76
+ lastAt: grp.last,
77
+ models: [...grp.models.values()].sort((a, b) => b.cents - a.cents),
78
+ lineage,
79
+ swaps,
80
+ });
81
+ }
82
+ workloads.sort((a, b) => b.spentCents - a.spentCents);
83
+ return { workloads, continuity };
84
+ }
85
+ /** Human-readable lineage, e.g. "claude-5 → gpt-5-mini → glm-5.2". */
86
+ const formatLineage = (w) => w.lineage.join(' → ');
87
+ exports.formatLineage = formatLineage;
88
+ //# sourceMappingURL=portability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portability.js","sourceRoot":"","sources":["../../src/receipts/portability.ts"],"names":[],"mappings":"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"}
package/dist/telemetry.js CHANGED
@@ -43,7 +43,7 @@ const https = __importStar(require("https"));
43
43
  const os = __importStar(require("os"));
44
44
  const path = __importStar(require("path"));
45
45
  const crypto_1 = require("crypto");
46
- const AGENTGUARD_SPEND_VERSION = '0.15.1';
46
+ const AGENTGUARD_SPEND_VERSION = '0.15.2';
47
47
  const STATE_DIR = path.join(os.homedir(), '.agentguard');
48
48
  const INSTALL_PATH = path.join(STATE_DIR, 'install.json');
49
49
  const BEACON_URL = 'https://agentguard.run/api/beacon';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentguard-run/spend",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "All terminology and labels used in AgentGuard materials are descriptive of software functionality only, not legal definitions or guarantees of compliance. Terms like receipt, audit log, evidence, audit trail, and attestation refer solely to cryptographically-signed records produced by the software. Full functional-use disclaimer in README.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -148,14 +148,18 @@
148
148
  "src/cli/byo.ts",
149
149
  "docs/patents/CLAIM_MAPPING.md",
150
150
  "src/frameworks",
151
- "src/operating-pack.ts"
151
+ "src/operating-pack.ts",
152
+ "src/dashboard"
152
153
  ],
153
154
  "scripts": {
154
155
  "build": "tsc",
155
156
  "prepublishOnly": "npm run build",
156
157
  "clean": "rm -rf dist dist-test",
157
- "test": "AGENTGUARD_HOME=./dist-test/home tsc -p tsconfig.test.json && rm -rf dist-test/home && AGENTGUARD_HOME=./dist-test/home node --test dist-test/**/*.test.js",
158
- "verify": "node dist/verify-test-vector.js"
158
+ "demo:attestation": "npx --yes tsx examples/dag-attestation-demo.ts",
159
+ "test": "AGENTGUARD_HOME=./dist-test/home tsc -p tsconfig.test.json && rm -rf dist-test/home && AGENTGUARD_HOME=./dist-test/home AGENTGUARD_LICENSE_ENDPOINT=http://127.0.0.1:9 node --test dist-test/**/*.test.js",
160
+ "verify": "node dist/verify-test-vector.js",
161
+ "demo:dashboard": "npx --yes tsx examples/cfo-dashboard-demo.ts",
162
+ "demo:hotswap": "npx --yes tsx examples/hot-swap-provenance-demo.ts"
159
163
  },
160
164
  "keywords": [
161
165
  "ai-agent-security",
@@ -0,0 +1,103 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { aggregateLedger, classifyOrigin, decisionCents } from './aggregate';
4
+ import type { SignedDecisionLogEntry, SpendDecision, Provider } from '../types';
5
+ import type { ProvenanceProvider } from '../receipts/schema';
6
+
7
+ let seq = 0;
8
+ function entry(d: Partial<SpendDecision>): SignedDecisionLogEntry {
9
+ const decision: SpendDecision = {
10
+ decisionId: `d${seq}`,
11
+ timestamp: '2026-07-10T14:00:00.000Z',
12
+ action: 'allow',
13
+ triggeredCap: null,
14
+ triggeredScopeKey: null,
15
+ projectedCents: 0,
16
+ windowSpendBefore: 0,
17
+ windowSpendAfter: 0,
18
+ provider: 'openai',
19
+ modelRequested: 'gpt-5',
20
+ modelResolved: 'gpt-5',
21
+ policyId: 'p',
22
+ policyVersion: 1,
23
+ enforcementMode: 'enforce',
24
+ reasons: [],
25
+ ...d,
26
+ };
27
+ return {
28
+ sequence: seq++, decision, previousHash: '0'.repeat(64),
29
+ entryHash: 'x', signature: 'y', signerFingerprint: 'f',
30
+ };
31
+ }
32
+ function withProvenance(fam: ProvenanceProvider, route = 'api'): SpendDecision['provenance'] {
33
+ return {
34
+ model_identity: { provider: fam, model_id: 'm', model_version: '1', model_family: 'x', weights_origin_country: 'US' },
35
+ hosting: { provider_route: route, jurisdiction_country: 'US', jurisdiction_region: 'us' },
36
+ compliance: {
37
+ baa_in_force: false, baa_vendor: null, hipaa_eligible: false, data_retention_days: null,
38
+ data_residency_attested: false, foreign_origin_weight_flag: false,
39
+ foreign_origin_consent_receipt_id: null, inference_billing: 'customer_managed',
40
+ },
41
+ captured_at: '2026-07-10T14:00:00.000Z',
42
+ };
43
+ }
44
+
45
+ test('classifyOrigin: self-hosted provenance = dark, not inferred', () => {
46
+ const d = entry({ provenance: withProvenance('self_hosted') }).decision;
47
+ assert.deepEqual(classifyOrigin(d), { origin: 'dark', inferred: false });
48
+ });
49
+
50
+ test('classifyOrigin: open-weight family = dark; frontier family = frontier', () => {
51
+ assert.equal(classifyOrigin(entry({ provenance: withProvenance('deepseek') }).decision).origin, 'dark');
52
+ assert.equal(classifyOrigin(entry({ provenance: withProvenance('anthropic') }).decision).origin, 'frontier');
53
+ });
54
+
55
+ test('classifyOrigin: no provenance falls back to provider and is flagged inferred', () => {
56
+ assert.deepEqual(classifyOrigin(entry({ provider: 'anthropic' }).decision), { origin: 'frontier', inferred: true });
57
+ assert.deepEqual(classifyOrigin(entry({ provider: 'unknown' as Provider }).decision), { origin: 'dark', inferred: true });
58
+ });
59
+
60
+ test('decisionCents: blocked = 0, settled actual beats projection', () => {
61
+ assert.equal(decisionCents(entry({ action: 'block', projectedCents: 500 }).decision), 0);
62
+ assert.equal(decisionCents(entry({ projectedCents: 500, actualCents: 420 }).decision), 420);
63
+ assert.equal(decisionCents(entry({ projectedCents: 500 }).decision), 500);
64
+ });
65
+
66
+ test('aggregate: dark share, savings, true-up, and buckets', () => {
67
+ seq = 0;
68
+ const entries = [
69
+ entry({ action: 'allow', provider: 'anthropic', modelResolved: 'claude-5', projectedCents: 100, actualCents: 100, provenance: withProvenance('anthropic') }),
70
+ entry({ action: 'allow', modelResolved: 'glm-5.2', projectedCents: 4, actualCents: 4, provider: 'unknown' as Provider, provenance: withProvenance('self_hosted', 'self-hosted-gpu') }),
71
+ entry({ action: 'downgrade', provider: 'openai', modelResolved: 'gpt-5-mini', projectedCents: 200, actualCents: 20 }),
72
+ entry({ action: 'block', provider: 'openai', modelResolved: 'gpt-5', projectedCents: 300 }),
73
+ entry({ entryType: 'settlement', actualCents: 98, projectedCents: 0 }),
74
+ ];
75
+ const dash = aggregateLedger(entries, { verified: true, entries: entries.length });
76
+
77
+ // 4 enforcement decisions (settlement not counted as a call)
78
+ assert.equal(dash.totals.calls, 4);
79
+ // spend = 100 + 4 + 20 + 0(blocked) = 124
80
+ assert.equal(dash.totals.spentCents, 124);
81
+ assert.equal(dash.totals.darkCents, 4);
82
+ assert.equal(dash.totals.frontierCents, 120); // 100 anthropic + 20 downgraded openai(inferred frontier)
83
+ assert.equal(dash.totals.darkSharePct, Math.round((4 / 124) * 1000) / 10);
84
+ // savings = downgrade delta (200-20=180) + blocked projection (300) = 480
85
+ assert.equal(dash.enforcement.estimatedSavedCents, 480);
86
+ assert.equal(dash.enforcement.blocked, 1);
87
+ assert.equal(dash.enforcement.downgraded, 1);
88
+ // true-up: settled actual 98 vs reserved (100+4+200+300=604)
89
+ assert.equal(dash.trueUp.settledCents, 98);
90
+ assert.equal(dash.trueUp.reservedCents, 604);
91
+ assert.equal(dash.trueUp.driftCents, 98 - 604);
92
+ // integrity passthrough
93
+ assert.equal(dash.integrity.verified, true);
94
+ // model bucket present
95
+ assert.ok(dash.byModel.find(b => b.key === 'claude-5' && b.cents === 100));
96
+ });
97
+
98
+ test('aggregate: empty ledger is safe', () => {
99
+ const dash = aggregateLedger([]);
100
+ assert.equal(dash.totals.calls, 0);
101
+ assert.equal(dash.totals.darkSharePct, 0);
102
+ assert.equal(dash.integrity.verified, false);
103
+ });
@@ -0,0 +1,201 @@
1
+ /**
2
+ * AgentGuard® — CFO cost dashboard, read side.
3
+ *
4
+ * Aggregates the signed, hash-chained decision log into a CFO-legible view:
5
+ * spend over time, by model, by provider, frontier-vs-"dark" (local /
6
+ * open-weight / self-hosted) tokens, reserved-vs-settled true-up, an
7
+ * enforcement/savings summary, and a tamper-evidence check. Everything is
8
+ * derived IN-PROCESS from the customer-held ledger — no proxy, no data plane,
9
+ * no provider billing API required.
10
+ *
11
+ * The "dark token" view is the differentiator: open-source / self-hosted usage
12
+ * never appears on any provider's bill, so a provider console structurally
13
+ * cannot show it. The signed ledger can, because the receipt is written where
14
+ * the call is made.
15
+ *
16
+ * Honest scope note: the signed SpendDecision carries time, provider, model,
17
+ * cents, action, builderCode, entryType and a provenance block — but NOT full
18
+ * tenant/agent identity (only the scope key a cap matched on). Per-agent /
19
+ * per-tenant breakdown therefore requires recording a metadata-only scope
20
+ * digest on the decision; see `scopeKeyBreakdown` (best-effort from
21
+ * triggeredScopeKey) and DASHBOARD.md. We do not invent attribution the ledger
22
+ * does not carry.
23
+ *
24
+ * Patent notice: signed hash-chained decision log (U.S. application filed May
25
+ * 2026); composes with DAG Trust Attestation, Patent D §7.3 (App. No.
26
+ * 63/984,626). AgentGuard® is a U.S. registered trademark (Reg. No. 8281464)
27
+ * of Dunecrest Ventures Inc.
28
+ */
29
+ import type { Provider, SignedDecisionLogEntry, SpendDecision } from '../types';
30
+ import type { ProvenanceProvider } from '../receipts/schema';
31
+
32
+ /** Model families that are open-weight / self-hostable → "dark" when self-hosted. */
33
+ const OPEN_WEIGHT_FAMILIES = new Set<ProvenanceProvider>([
34
+ 'meta', 'mistral', 'cohere', 'fireworks', 'baseten', 'together',
35
+ 'deepseek', 'moonshot', 'alibaba', 'self_hosted',
36
+ ]);
37
+ const FRONTIER_PROVIDERS = new Set<Provider>(['openai', 'anthropic', 'gemini', 'bedrock']);
38
+
39
+ export type TokenOrigin = 'frontier' | 'dark' | 'unknown';
40
+
41
+ /** Classify a decision as frontier vs dark (local/open-weight) spend.
42
+ * Prefers the signed provenance block; falls back to the coarse provider
43
+ * field and labels the result `inferred` so the UI never overstates. */
44
+ export function classifyOrigin(decision: SpendDecision): { origin: TokenOrigin; inferred: boolean } {
45
+ const prov = decision.provenance;
46
+ if (prov?.model_identity) {
47
+ const fam = prov.model_identity.provider;
48
+ if (fam === 'self_hosted') return { origin: 'dark', inferred: false };
49
+ const selfHosted = /self|local|on[-_]?prem|in[-_]?house/i.test(prov.hosting?.provider_route ?? '');
50
+ if (selfHosted) return { origin: 'dark', inferred: false };
51
+ if (OPEN_WEIGHT_FAMILIES.has(fam)) return { origin: 'dark', inferred: false };
52
+ if (fam === 'anthropic' || fam === 'openai' || fam === 'google') return { origin: 'frontier', inferred: false };
53
+ }
54
+ if (FRONTIER_PROVIDERS.has(decision.provider)) return { origin: 'frontier', inferred: true };
55
+ if (decision.provider === 'unknown') return { origin: 'dark', inferred: true };
56
+ return { origin: 'unknown', inferred: true };
57
+ }
58
+
59
+ /** Cents this decision actually cost the org: settled actuals win over the
60
+ * pre-dispatch projection; blocked calls cost zero. */
61
+ export function decisionCents(decision: SpendDecision): number {
62
+ if (decision.action === 'block') return 0;
63
+ if (typeof decision.actualCents === 'number') return decision.actualCents;
64
+ return decision.projectedCents ?? 0;
65
+ }
66
+
67
+ export interface BucketSpend {
68
+ key: string;
69
+ cents: number;
70
+ calls: number;
71
+ }
72
+ export interface CfoDashboard {
73
+ generatedAt: string;
74
+ windowStart: string | null;
75
+ windowEnd: string | null;
76
+ totals: {
77
+ calls: number;
78
+ spentCents: number;
79
+ frontierCents: number;
80
+ darkCents: number;
81
+ darkSharePct: number; // dark / total spend, 0..100
82
+ inferredCents: number; // spend whose origin was inferred, not from provenance
83
+ };
84
+ enforcement: {
85
+ allowed: number;
86
+ downgraded: number;
87
+ blocked: number;
88
+ shadow: number;
89
+ /** Cents NOT spent because a call was blocked or downgraded — the ROI line. */
90
+ estimatedSavedCents: number;
91
+ };
92
+ trueUp: {
93
+ reservedCents: number; // sum of projections on decision entries
94
+ settledCents: number; // sum of actuals on settlement entries
95
+ driftCents: number; // settled - reserved (over/under-estimate)
96
+ };
97
+ byHour: BucketSpend[]; // ISO hour → spend
98
+ byModel: BucketSpend[]; // modelResolved → spend, desc
99
+ byProvider: BucketSpend[];
100
+ byBuilderCode: BucketSpend[];
101
+ scopeKeyBreakdown: BucketSpend[]; // best-effort from triggeredScopeKey (may be sparse)
102
+ integrity: { verified: boolean; entries: number; reason?: string; sequence?: number };
103
+ }
104
+
105
+ function topBuckets(map: Map<string, BucketSpend>, limit = 50): BucketSpend[] {
106
+ return [...map.values()].sort((a, b) => b.cents - a.cents).slice(0, limit);
107
+ }
108
+
109
+ function bump(map: Map<string, BucketSpend>, key: string, cents: number) {
110
+ const cur = map.get(key) ?? { key, cents: 0, calls: 0 };
111
+ cur.cents += cents;
112
+ cur.calls += 1;
113
+ map.set(key, cur);
114
+ }
115
+
116
+ /**
117
+ * Build the CFO dashboard from signed ledger entries. Pure + synchronous.
118
+ * `integrity` is passed in (compute via verifyChain, which is async) so this
119
+ * stays a pure function; pass null to skip.
120
+ */
121
+ export function aggregateLedger(
122
+ entries: SignedDecisionLogEntry[],
123
+ integrity: CfoDashboard['integrity'] | null = null,
124
+ now: string = new Date().toISOString(),
125
+ ): CfoDashboard {
126
+ const byHour = new Map<string, BucketSpend>();
127
+ const byModel = new Map<string, BucketSpend>();
128
+ const byProvider = new Map<string, BucketSpend>();
129
+ const byBuilder = new Map<string, BucketSpend>();
130
+ const byScopeKey = new Map<string, BucketSpend>();
131
+
132
+ let calls = 0, spent = 0, frontier = 0, dark = 0, inferred = 0;
133
+ let allowed = 0, downgraded = 0, blocked = 0, shadow = 0, saved = 0;
134
+ let reserved = 0, settled = 0;
135
+ let minTs: string | null = null, maxTs: string | null = null;
136
+
137
+ for (const entry of entries) {
138
+ const d = entry.decision;
139
+ const kind = d.entryType ?? 'decision';
140
+
141
+ if (kind === 'settlement') {
142
+ if (typeof d.actualCents === 'number') settled += d.actualCents;
143
+ // settlement entries adjust true-up only; not double-counted as calls
144
+ continue;
145
+ }
146
+ if (kind === 'outcome' || kind === 'governance') continue;
147
+
148
+ // provider enforcement decision
149
+ calls += 1;
150
+ const cents = decisionCents(d);
151
+ spent += cents;
152
+ reserved += d.projectedCents ?? 0;
153
+
154
+ const { origin, inferred: wasInferred } = classifyOrigin(d);
155
+ if (origin === 'frontier') frontier += cents;
156
+ else if (origin === 'dark') dark += cents;
157
+ if (wasInferred) inferred += cents;
158
+
159
+ switch (d.action) {
160
+ case 'allow': allowed += 1; break;
161
+ case 'downgrade': downgraded += 1; saved += Math.max(0, (d.projectedCents ?? 0) - cents); break;
162
+ case 'block': blocked += 1; saved += d.projectedCents ?? 0; break;
163
+ case 'shadow': shadow += 1; break;
164
+ }
165
+
166
+ const ts = d.timestamp;
167
+ if (ts) {
168
+ if (!minTs || ts < minTs) minTs = ts;
169
+ if (!maxTs || ts > maxTs) maxTs = ts;
170
+ bump(byHour, ts.slice(0, 13) + ':00', cents); // ISO hour bucket
171
+ }
172
+ bump(byModel, d.modelResolved || d.modelRequested || 'unknown', cents);
173
+ bump(byProvider, d.provider || 'unknown', cents);
174
+ if (d.builderCode) bump(byBuilder, d.builderCode, cents);
175
+ if (d.triggeredScopeKey) bump(byScopeKey, d.triggeredScopeKey, cents);
176
+ }
177
+
178
+ return {
179
+ generatedAt: now,
180
+ windowStart: minTs,
181
+ windowEnd: maxTs,
182
+ totals: {
183
+ calls,
184
+ spentCents: spent,
185
+ frontierCents: frontier,
186
+ darkCents: dark,
187
+ darkSharePct: spent > 0 ? Math.round((dark / spent) * 1000) / 10 : 0,
188
+ inferredCents: inferred,
189
+ },
190
+ enforcement: { allowed, downgraded, blocked, shadow, estimatedSavedCents: saved },
191
+ trueUp: { reservedCents: reserved, settledCents: settled, driftCents: settled - reserved },
192
+ byHour: [...byHour.values()].sort((a, b) => a.key.localeCompare(b.key)),
193
+ byModel: topBuckets(byModel),
194
+ byProvider: topBuckets(byProvider),
195
+ byBuilderCode: topBuckets(byBuilder),
196
+ scopeKeyBreakdown: topBuckets(byScopeKey),
197
+ integrity: integrity ?? { verified: false, entries: entries.length, reason: 'NOT_CHECKED' },
198
+ };
199
+ }
200
+
201
+ export const centsToUsd = (c: number): string => `$${(c / 100).toFixed(2)}`;