@agentguard-run/spend 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ /**
3
+ * AgentGuard(TM) Spend — Core SDK entry point
4
+ *
5
+ * `withSpendGuard()` wraps a provider SDK client (OpenAI, Anthropic, Bedrock)
6
+ * and enforces a SpendPolicy locally before each call is sent to the provider.
7
+ *
8
+ * Design rule: ZERO DATA PLANE INVOLVEMENT.
9
+ * - Prompts never leave the customer process.
10
+ * - Provider API keys never leave the customer process.
11
+ * - The signing key never leaves the customer process.
12
+ * - All decisions and the entire signed log live in the customer's storage.
13
+ *
14
+ * Patent notice: Protected by U.S. patent-pending technology
15
+ * (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
16
+ * and an additional application filed May 2026).
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.AgentGuardBlockedError = exports.SpendGuard = void 0;
20
+ exports.withSpendGuard = withSpendGuard;
21
+ const policy_1 = require("./policy");
22
+ const decision_log_1 = require("./decision-log");
23
+ const store_memory_1 = require("./store-memory");
24
+ const decision_log_2 = require("./decision-log");
25
+ const cost_table_1 = require("./cost-table");
26
+ class SpendGuard {
27
+ config;
28
+ spendStore;
29
+ logStore;
30
+ nextSequence = 0;
31
+ previousHash = decision_log_1.GENESIS_PREVIOUS_HASH;
32
+ constructor(config) {
33
+ this.config = config;
34
+ this.spendStore = config.spendStore ?? new store_memory_1.InMemorySpendStore();
35
+ this.logStore = config.logStore ?? new decision_log_2.InMemoryDecisionLogStore();
36
+ }
37
+ /**
38
+ * Hydrate the chain pointer from the log store. Call this once at startup
39
+ * if the log store is persistent and may already contain entries from a
40
+ * previous run.
41
+ */
42
+ async hydrate() {
43
+ const latest = await this.logStore.getLatest();
44
+ if (latest) {
45
+ this.nextSequence = latest.sequence + 1;
46
+ this.previousHash = latest.entryHash;
47
+ }
48
+ }
49
+ /**
50
+ * Evaluate the policy for a call and append to the decision log.
51
+ * Returns the decision plus the signed log entry (or null if unsigned).
52
+ */
53
+ async decide(call) {
54
+ const decision = await (0, policy_1.evaluatePolicy)(this.config.policy, call, this.spendStore);
55
+ let signed = null;
56
+ if (this.config.signingKeys) {
57
+ signed = await (0, decision_log_1.signDecision)({
58
+ sequence: this.nextSequence,
59
+ decision,
60
+ previousHash: this.previousHash,
61
+ privateKey: this.config.signingKeys.privateKey,
62
+ publicKey: this.config.signingKeys.publicKey,
63
+ });
64
+ await this.logStore.append(signed);
65
+ this.nextSequence += 1;
66
+ this.previousHash = signed.entryHash;
67
+ }
68
+ if (this.config.onDecision) {
69
+ await this.config.onDecision(decision, signed);
70
+ }
71
+ return { decision, signed };
72
+ }
73
+ /** Default token estimator: 1 token ≈ 4 characters of English. */
74
+ defaultTokenEstimator(text) {
75
+ return Math.ceil(text.length / 4);
76
+ }
77
+ /** Estimate tokens for a string using the configured estimator. */
78
+ estimateTokens(text) {
79
+ const fn = this.config.tokenEstimator ?? this.defaultTokenEstimator;
80
+ return fn(text);
81
+ }
82
+ /** Expose the spend store for tests and dashboards. */
83
+ getSpendStore() {
84
+ return this.spendStore;
85
+ }
86
+ /** Expose the log store for tests and dashboards. */
87
+ getLogStore() {
88
+ return this.logStore;
89
+ }
90
+ }
91
+ exports.SpendGuard = SpendGuard;
92
+ class AgentGuardBlockedError extends Error {
93
+ decision;
94
+ constructor(decision) {
95
+ super(`AgentGuard Spend blocked call: policy='${decision.policyId}' ` +
96
+ `action='${decision.action}' reasons=${decision.reasons.join('; ')}`);
97
+ this.name = 'AgentGuardBlockedError';
98
+ this.decision = decision;
99
+ }
100
+ }
101
+ exports.AgentGuardBlockedError = AgentGuardBlockedError;
102
+ /**
103
+ * Wrap an OpenAI client. Returns a proxy whose chat.completions.create
104
+ * enforces the spend policy. Other endpoints pass through untouched in this
105
+ * alpha; subsequent releases will cover responses, embeddings, and images.
106
+ */
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ function withSpendGuard(client, opts) {
109
+ const guard = new SpendGuard({ policy: opts.policy, ...(opts.config ?? {}) });
110
+ const originalCreate = client?.chat?.completions?.create?.bind(client.chat.completions);
111
+ if (!originalCreate) {
112
+ throw new Error('withSpendGuard: expected an OpenAI client with chat.completions.create. ' +
113
+ 'For Anthropic or Bedrock, use withAnthropicSpendGuard or withBedrockSpendGuard.');
114
+ }
115
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
+ client.chat.completions.create = async (params) => {
117
+ const inputText = serializeMessages(params?.messages ?? []);
118
+ const inputTokens = guard.estimateTokens(inputText);
119
+ const outputTokens = typeof params?.max_tokens === 'number' ? params.max_tokens : 512;
120
+ const provider = (0, cost_table_1.inferProvider)(params?.model ?? '');
121
+ const call = {
122
+ provider,
123
+ model: params?.model ?? 'unknown',
124
+ inputTokens,
125
+ outputTokens,
126
+ scope: opts.scope,
127
+ capabilityClaim: opts.capabilityClaim,
128
+ label: params?.metadata?.label,
129
+ };
130
+ const { decision } = await guard.decide(call);
131
+ if (decision.action === 'block') {
132
+ throw new AgentGuardBlockedError(decision);
133
+ }
134
+ if (decision.action === 'downgrade' && decision.modelResolved !== decision.modelRequested) {
135
+ params = { ...params, model: decision.modelResolved };
136
+ }
137
+ // shadow + allow + downgrade all proceed to the provider.
138
+ return originalCreate(params);
139
+ };
140
+ // Attach the guard so callers can introspect or await hydration.
141
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
+ client.__agentguard = guard;
143
+ return client;
144
+ }
145
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
+ function serializeMessages(messages) {
147
+ if (!Array.isArray(messages))
148
+ return '';
149
+ return messages
150
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
+ .map((m) => (typeof m?.content === 'string' ? m.content : JSON.stringify(m?.content ?? '')))
152
+ .join('\n');
153
+ }
154
+ //# sourceMappingURL=spend-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spend-guard.js","sourceRoot":"","sources":["../src/spend-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * AgentGuard(TM) Spend — In-memory spend store
3
+ *
4
+ * Reference implementation of the SpendStore interface. Suitable for tests,
5
+ * examples, and single-process shadow deployments. Production callers with
6
+ * multi-process or distributed workloads should supply their own store
7
+ * (typically Redis with INCR, or Postgres with SELECT FOR UPDATE).
8
+ *
9
+ * Licensed under the alpha evaluation license; see LICENSE in the package
10
+ * root. Patent notice: Protected by U.S. patent-pending technology
11
+ * (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
12
+ * and an additional application filed May 2026).
13
+ */
14
+ import type { SpendStore, SpendWindow } from './types';
15
+ /**
16
+ * In-memory spend store. Thread-safe in single-process Node (we rely on the
17
+ * event loop's atomic execution between awaits; this store does no I/O).
18
+ */
19
+ export declare class InMemorySpendStore implements SpendStore {
20
+ private state;
21
+ private key;
22
+ private rollIfStale;
23
+ getWindowSpend(scopeKey: string, window: SpendWindow): Promise<number>;
24
+ incrementWindowSpend(scopeKey: string, window: SpendWindow, cents: number): Promise<number>;
25
+ /** Clear all state. Test helper. */
26
+ reset(): void;
27
+ }
28
+ //# sourceMappingURL=store-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-memory.d.ts","sourceRoot":"","sources":["../src/store-memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA4BvD;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IACnD,OAAO,CAAC,KAAK,CAAkC;IAE/C,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,WAAW;IAYb,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAKtE,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;IAMlB,oCAAoC;IACpC,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * AgentGuard(TM) Spend — In-memory spend store
4
+ *
5
+ * Reference implementation of the SpendStore interface. Suitable for tests,
6
+ * examples, and single-process shadow deployments. Production callers with
7
+ * multi-process or distributed workloads should supply their own store
8
+ * (typically Redis with INCR, or Postgres with SELECT FOR UPDATE).
9
+ *
10
+ * Licensed under the alpha evaluation license; see LICENSE in the package
11
+ * root. Patent notice: Protected by U.S. patent-pending technology
12
+ * (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
13
+ * and an additional application filed May 2026).
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.InMemorySpendStore = void 0;
17
+ const WINDOW_DURATION_MS = {
18
+ per_call: 0, // per_call windows reset on every read
19
+ per_minute: 60_000,
20
+ per_hour: 3_600_000,
21
+ per_day: 86_400_000,
22
+ per_month: 30 * 86_400_000, // approximate; production stores should use calendar-month boundaries
23
+ };
24
+ /**
25
+ * Returns the millisecond timestamp at which a window of the given duration
26
+ * containing `nowMs` started. For fixed-grid windows we align to UTC epoch
27
+ * (so all processes agree on window boundaries without coordination).
28
+ */
29
+ function windowStart(window, nowMs) {
30
+ const dur = WINDOW_DURATION_MS[window];
31
+ if (dur === 0)
32
+ return nowMs; // per_call is trivial
33
+ return Math.floor(nowMs / dur) * dur;
34
+ }
35
+ /**
36
+ * In-memory spend store. Thread-safe in single-process Node (we rely on the
37
+ * event loop's atomic execution between awaits; this store does no I/O).
38
+ */
39
+ class InMemorySpendStore {
40
+ state = new Map();
41
+ key(scopeKey, window) {
42
+ return `${scopeKey}::${window}`;
43
+ }
44
+ rollIfStale(scopeKey, window, nowMs) {
45
+ const k = this.key(scopeKey, window);
46
+ const expectedStart = windowStart(window, nowMs);
47
+ const existing = this.state.get(k);
48
+ if (!existing || existing.windowStartMs !== expectedStart) {
49
+ const fresh = { cents: 0, windowStartMs: expectedStart };
50
+ this.state.set(k, fresh);
51
+ return fresh;
52
+ }
53
+ return existing;
54
+ }
55
+ async getWindowSpend(scopeKey, window) {
56
+ const state = this.rollIfStale(scopeKey, window, Date.now());
57
+ return state.cents;
58
+ }
59
+ async incrementWindowSpend(scopeKey, window, cents) {
60
+ const state = this.rollIfStale(scopeKey, window, Date.now());
61
+ state.cents += cents;
62
+ return state.cents;
63
+ }
64
+ /** Clear all state. Test helper. */
65
+ reset() {
66
+ this.state.clear();
67
+ }
68
+ }
69
+ exports.InMemorySpendStore = InMemorySpendStore;
70
+ //# sourceMappingURL=store-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-memory.js","sourceRoot":"","sources":["../src/store-memory.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAWH,MAAM,kBAAkB,GAAgC;IACtD,QAAQ,EAAE,CAAC,EAAE,uCAAuC;IACpD,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,UAAU;IACnB,SAAS,EAAE,EAAE,GAAG,UAAU,EAAE,sEAAsE;CACnG,CAAC;AAEF;;;;GAIG;AACH,SAAS,WAAW,CAAC,MAAmB,EAAE,KAAa;IACrD,MAAM,GAAG,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,sBAAsB;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAa,kBAAkB;IACrB,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEvC,GAAG,CAAC,QAAgB,EAAE,MAAmB;QAC/C,OAAO,GAAG,QAAQ,KAAK,MAAM,EAAE,CAAC;IAClC,CAAC;IAEO,WAAW,CAAC,QAAgB,EAAE,MAAmB,EAAE,KAAa;QACtE,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,aAAa,KAAK,aAAa,EAAE,CAAC;YAC1D,MAAM,KAAK,GAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;YACtE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,QAAgB,EAAE,MAAmB;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7D,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,oBAAoB,CACxB,QAAgB,EAChB,MAAmB,EACnB,KAAa;QAEb,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7D,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC;QACrB,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,oCAAoC;IACpC,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAtCD,gDAsCC"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * AgentGuard(TM) Spend — Core type definitions
3
+ *
4
+ * Patent notice: Protected by U.S. patent-pending technology
5
+ * (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
6
+ * and an additional application filed May 2026).
7
+ *
8
+ * Design rule: NO DATA PLANE INVOLVEMENT.
9
+ * All policy decisions are made locally in the customer runtime.
10
+ * Prompts, completions, and provider API keys never leave the customer process.
11
+ */
12
+ export type SpendAction = 'block' | 'downgrade' | 'shadow' | 'allow';
13
+ export type SpendWindow = 'per_call' | 'per_minute' | 'per_hour' | 'per_day' | 'per_month';
14
+ export type Provider = 'openai' | 'anthropic' | 'bedrock' | 'gemini' | 'unknown';
15
+ export type EnforcementMode = 'shadow' | 'canary' | 'enforce';
16
+ export type CapabilityTier = 'read_only' | 'data_write' | 'payment_initiate' | 'payment_execute';
17
+ /**
18
+ * A spend cap with one or more thresholds across configurable windows.
19
+ * All amounts are USD cents (integer math, no floating-point cost drift).
20
+ */
21
+ export interface SpendCap {
22
+ /** Maximum cents spent in the given window before the action fires */
23
+ amountCents: number;
24
+ /** Time window over which the cap applies */
25
+ window: SpendWindow;
26
+ /** What to do when the cap is reached or projected to be reached */
27
+ action: SpendAction;
28
+ /** Optional model to downgrade to when action='downgrade' */
29
+ downgradeTo?: string;
30
+ /** Optional reason string included in decision log */
31
+ reason?: string;
32
+ }
33
+ /**
34
+ * Identity scope a cap applies to. Caps are matched on EVERY matching scope.
35
+ * If any scope's cap is exceeded, the most restrictive action wins.
36
+ */
37
+ export interface SpendScope {
38
+ /** Tenant or organization ID. Required at top level. */
39
+ tenantId: string;
40
+ /** Optional user ID for per-user limits */
41
+ userId?: string;
42
+ /** Optional team ID */
43
+ teamId?: string;
44
+ /** Optional project / agent ID */
45
+ agentId?: string;
46
+ /** Optional task / job ID */
47
+ taskId?: string;
48
+ /** Optional provider scope (e.g., only enforce on OpenAI) */
49
+ provider?: Provider;
50
+ }
51
+ /**
52
+ * A full spend policy: name, scope, and ordered list of caps.
53
+ * The first cap whose threshold is reached determines the action.
54
+ */
55
+ export interface SpendPolicy {
56
+ /** Stable identifier, e.g., 'finance-ops-v1' */
57
+ id: string;
58
+ /** Human-readable name */
59
+ name: string;
60
+ /** Scope this policy binds to */
61
+ scope: SpendScope;
62
+ /** Ordered list of caps (evaluated in order, first match wins) */
63
+ caps: SpendCap[];
64
+ /** Enforcement mode for graduated rollout */
65
+ mode: EnforcementMode;
66
+ /** Optional capability tier requirement.
67
+ *
68
+ * The tier set is informed by the capability-tier framework of Patent D
69
+ * §7.3 (App. No. 63/984,626). The SDK enforces a flat ordered comparison
70
+ * between the call's capability claim and this requirement. Sourcing the
71
+ * capability claim from a DAG-TAT verifier, an enterprise identity
72
+ * provider, an ERC-8004 credential, or any other attestation mechanism is
73
+ * out of scope of this package and is performed by the integrating code.
74
+ */
75
+ requiredCapability?: CapabilityTier;
76
+ /** Policy version, monotonically increasing */
77
+ version: number;
78
+ /** ISO 8601 effective-from timestamp */
79
+ effectiveFrom: string;
80
+ }
81
+ /**
82
+ * A single LLM call attempted under a policy.
83
+ */
84
+ export interface CallContext {
85
+ /** Provider being called */
86
+ provider: Provider;
87
+ /** Model name as passed to the provider */
88
+ model: string;
89
+ /** Input token count (estimated or measured) */
90
+ inputTokens: number;
91
+ /** Output token count (estimated or measured) */
92
+ outputTokens: number;
93
+ /** Identity scope of the caller (must include tenantId) */
94
+ scope: SpendScope;
95
+ /** Optional human-readable label for the call */
96
+ label?: string;
97
+ /** Optional capability tier the caller claims */
98
+ capabilityClaim?: CapabilityTier;
99
+ }
100
+ /**
101
+ * The policy decision for a given call.
102
+ */
103
+ export interface SpendDecision {
104
+ /** Unique decision ID (UUID v4) */
105
+ decisionId: string;
106
+ /** ISO 8601 timestamp */
107
+ timestamp: string;
108
+ /** The action taken */
109
+ action: SpendAction;
110
+ /** The cap that triggered the action, if any */
111
+ triggeredCap: SpendCap | null;
112
+ /** The scope key that the cap matched on */
113
+ triggeredScopeKey: string | null;
114
+ /** Cents the call would have cost */
115
+ projectedCents: number;
116
+ /** Cents spent in the relevant window before this call */
117
+ windowSpendBefore: number;
118
+ /** Cents that would be spent in the window after allowing this call */
119
+ windowSpendAfter: number;
120
+ /** Provider being called */
121
+ provider: Provider;
122
+ /** Model originally requested */
123
+ modelRequested: string;
124
+ /** Model actually used (differs from modelRequested on downgrade) */
125
+ modelResolved: string;
126
+ /** Policy ID and version this decision was made under */
127
+ policyId: string;
128
+ policyVersion: number;
129
+ /** Human-readable reasons */
130
+ reasons: string[];
131
+ }
132
+ /**
133
+ * A signed, hash-chained decision log entry.
134
+ *
135
+ * The chain enables tamper-evident audit: each entry binds the previous
136
+ * entry's hash, and the entire chain is verifiable with a public key.
137
+ *
138
+ * The capability-tier framework used here is informed by Patent D §7.3
139
+ * (App. No. 63/984,626). The flat tier comparison implemented in this SDK
140
+ * does not by itself implement the full DAG-TAT cryptographic gating of
141
+ * §7.3; integration with a DAG-TAT verifier is performed separately and
142
+ * is not required for the spend-governance functions of this package.
143
+ */
144
+ export interface SignedDecisionLogEntry {
145
+ /** Monotonic chain sequence number (starts at 0) */
146
+ sequence: number;
147
+ /** The decision itself */
148
+ decision: SpendDecision;
149
+ /** SHA-256 hash of the previous entry, or 64 zeros for sequence=0 */
150
+ previousHash: string;
151
+ /** SHA-256 hash of this entry's canonical JSON (without signature) */
152
+ entryHash: string;
153
+ /** Ed25519 signature over entryHash, hex-encoded */
154
+ signature: string;
155
+ /** Hex-encoded public key fingerprint (first 8 bytes of SHA-256(pubkey)) */
156
+ signerFingerprint: string;
157
+ }
158
+ /**
159
+ * Storage backend for spend state. Implementations may use Redis, Postgres,
160
+ * SQLite, in-memory, or anything else. Spend state never leaves the customer
161
+ * runtime in the no-data-plane configuration.
162
+ */
163
+ export interface SpendStore {
164
+ /**
165
+ * Get the total cents spent for a given scope key + window since the window start.
166
+ */
167
+ getWindowSpend(scopeKey: string, window: SpendWindow): Promise<number>;
168
+ /**
169
+ * Atomically increment the cents spent for a given scope key + window.
170
+ * Returns the new total. Implementations MUST be atomic to prevent races.
171
+ */
172
+ incrementWindowSpend(scopeKey: string, window: SpendWindow, cents: number): Promise<number>;
173
+ }
174
+ /**
175
+ * Storage backend for the signed decision log. Implementations may append to a
176
+ * local file, a database table, or any other append-only sink.
177
+ */
178
+ export interface DecisionLogStore {
179
+ /** Append a new signed entry to the chain. */
180
+ append(entry: SignedDecisionLogEntry): Promise<void>;
181
+ /** Get the most recent entry, or null if empty. */
182
+ getLatest(): Promise<SignedDecisionLogEntry | null>;
183
+ /** Iterate from sequence onward (used by verification tools). */
184
+ read(fromSequence: number, limit: number): Promise<SignedDecisionLogEntry[]>;
185
+ }
186
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * AgentGuard(TM) Spend — Core type definitions
4
+ *
5
+ * Patent notice: Protected by U.S. patent-pending technology
6
+ * (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
7
+ * and an additional application filed May 2026).
8
+ *
9
+ * Design rule: NO DATA PLANE INVOLVEMENT.
10
+ * All policy decisions are made locally in the customer runtime.
11
+ * Prompts, completions, and provider API keys never leave the customer process.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@agentguard-run/spend",
3
+ "version": "0.1.0",
4
+ "description": "Local-runtime spend caps and capability-gated model routing for AI agents. Prompts, API keys, and signing keys stay inside the customer runtime. Zero data plane involvement.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "require": "./dist/index.js",
12
+ "import": "./dist/index.mjs"
13
+ },
14
+ "./policy": {
15
+ "types": "./dist/policy.d.ts",
16
+ "require": "./dist/policy.js",
17
+ "import": "./dist/policy.mjs"
18
+ },
19
+ "./decision-log": {
20
+ "types": "./dist/decision-log.d.ts",
21
+ "require": "./dist/decision-log.js",
22
+ "import": "./dist/decision-log.mjs"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE",
29
+ "PATENTS.md"
30
+ ],
31
+ "keywords": [
32
+ "ai-agent-security",
33
+ "spend-control",
34
+ "model-routing",
35
+ "local-first",
36
+ "no-proxy",
37
+ "tamper-evident",
38
+ "cryptographic-attestation",
39
+ "audit-log",
40
+ "agent-governance",
41
+ "ed25519",
42
+ "llm",
43
+ "ai-agents",
44
+ "policy-enforcement",
45
+ "openai",
46
+ "anthropic",
47
+ "bedrock"
48
+ ],
49
+ "author": "Dunecrest Ventures Inc. <hello@agentguard.run>",
50
+ "license": "SEE LICENSE IN LICENSE",
51
+ "homepage": "https://agentguard.run",
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "https://github.com/MerchantGuardOps/agentguard-site"
55
+ },
56
+ "bugs": {
57
+ "url": "https://agentguard.run/contact"
58
+ },
59
+ "dependencies": {
60
+ "@noble/ed25519": "^2.3.0"
61
+ },
62
+ "peerDependencies": {
63
+ "openai": ">=5.0.0",
64
+ "@anthropic-ai/sdk": ">=0.30.0",
65
+ "@aws-sdk/client-bedrock-runtime": ">=3.600.0"
66
+ },
67
+ "peerDependenciesMeta": {
68
+ "openai": { "optional": true },
69
+ "@anthropic-ai/sdk": { "optional": true },
70
+ "@aws-sdk/client-bedrock-runtime": { "optional": true }
71
+ },
72
+ "engines": {
73
+ "node": ">=20.0.0"
74
+ },
75
+ "publishConfig": {
76
+ "access": "public"
77
+ }
78
+ }