@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.
- package/CHANGELOG.md +13 -0
- package/dist/dashboard/aggregate.d.ts +90 -0
- package/dist/dashboard/aggregate.d.ts.map +1 -0
- package/dist/dashboard/aggregate.js +147 -0
- package/dist/dashboard/aggregate.js.map +1 -0
- package/dist/frameworks/hermes-kanban.d.ts +10 -2
- package/dist/frameworks/hermes-kanban.d.ts.map +1 -1
- package/dist/frameworks/hermes-kanban.js +55 -9
- package/dist/frameworks/hermes-kanban.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/receipts/portability.d.ts +76 -0
- package/dist/receipts/portability.d.ts.map +1 -0
- package/dist/receipts/portability.js +88 -0
- package/dist/receipts/portability.js.map +1 -0
- package/dist/telemetry.js +1 -1
- package/package.json +8 -4
- package/src/dashboard/aggregate.test.ts +103 -0
- package/src/dashboard/aggregate.ts +201 -0
- package/src/frameworks/hermes-kanban.ts +67 -8
- package/src/receipts/portability.test.ts +68 -0
- package/src/receipts/portability.ts +156 -0
package/dist/index.d.ts.map
CHANGED
|
@@ -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.
|
|
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":"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"}
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
158
|
-
"
|
|
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)}`;
|