@decentrys/agent 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,144 @@
1
+ "use strict";
2
+ /**
3
+ * Turning a decision into something a person can be held to.
4
+ *
5
+ * The question this file exists to answer is not "why was that blocked" — a
6
+ * block explains itself, loudly, at the moment it happens. It is **"why was
7
+ * that allowed"**, asked weeks later by someone looking at a drained account.
8
+ * A record that lists only what went wrong cannot answer it, so
9
+ * `explainAgentAction` renders every rule that was applied, the bound it
10
+ * carried and the value it saw, alongside the axes on which the policy set no
11
+ * bound at all. An allow with nothing behind it reads, correctly, as an allow
12
+ * with nothing behind it.
13
+ *
14
+ * Nothing here recomputes anything. The explanation is a rendering of the
15
+ * decision record and cannot disagree with it — an explanation that is
16
+ * generated separately from the decision eventually explains a decision that
17
+ * was not made.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.explainAgentAction = explainAgentAction;
21
+ const protect_1 = require("@decentrys/protect");
22
+ const VERDICT_HEADLINE = {
23
+ allow: 'Permitted',
24
+ require_human_approval: 'Held for human approval',
25
+ deny: 'Denied',
26
+ };
27
+ /** What an operator would change, per rule, to permit the action. */
28
+ const REMEDIATION = {
29
+ AGENT_BINDING: 'Use the policy sealed for this agent, or bind this policy to it.',
30
+ ACTION_TYPE: 'Add this action type to `allowedActions`, or remove it from `deniedActions`.',
31
+ CHAIN: 'Add this chain to `allowedChains`.',
32
+ COUNTERPARTY: 'Add this counterparty to `allowedCounterparties`, or remove it from `blockedCounterparties`.',
33
+ CONTRACT: 'Add this contract to `allowedContracts`.',
34
+ TOKEN: 'Add this token to `allowedTokens`.',
35
+ APPROVAL_ALLOWANCE: 'Have the agent request a bounded allowance, or set `allowUnlimitedApprovals` — '
36
+ + 'which makes any later compromise of the spender unbounded.',
37
+ VALUE_PER_ACTION: 'Raise `maxValuePerActionUsd`, or state the action\'s `valueUsd`.',
38
+ CUMULATIVE_SPEND: 'Wait for the window to roll over, raise the window\'s `maxValueUsd`, or state the '
39
+ + 'action\'s `valueUsd`.',
40
+ RATE_LIMIT: 'Wait for the window to roll over, or raise the window\'s `maxActions`. A rate ceiling being '
41
+ + 'hit repeatedly is usually the agent looping, not the ceiling being wrong.',
42
+ RISK_LEVEL: 'A person should review the evidence on the decision\'s assessment before this is permitted.',
43
+ ASSESSMENT_AVAILABILITY: 'Restore connectivity to Decentrys, or accept unscreened actions by setting '
44
+ + '`failMode: \'open\'` — which signs during precisely the window an attacker would choose.',
45
+ HUMAN_APPROVAL_THRESHOLD: 'A person must approve this action. Raise the threshold only if the operator '
46
+ + 'intends actions of this size to proceed unattended.',
47
+ };
48
+ function explainAgentAction(decision) {
49
+ const failing = decision.evaluations.filter((e) => e.outcome === 'fail');
50
+ const escalating = decision.evaluations.filter((e) => e.outcome === 'escalate');
51
+ const blocking = [...failing, ...escalating];
52
+ const headline = buildHeadline(decision, failing, escalating);
53
+ const because = blocking.map((e) => e.statement);
54
+ const unbounded = decision.evaluations
55
+ .filter((e) => e.outcome === 'not_configured')
56
+ .map((e) => e.statement);
57
+ // Deduplicated: two rules can share a remedy, and telling an operator the
58
+ // same thing twice trains them to skim the list.
59
+ const toProceed = [...new Set(blocking.map((e) => REMEDIATION[e.rule]))];
60
+ const explanation = {
61
+ decisionId: decision.decisionId,
62
+ agentId: decision.agentId,
63
+ verdict: decision.verdict,
64
+ headline,
65
+ because,
66
+ rulesApplied: decision.evaluations,
67
+ unbounded,
68
+ toProceed,
69
+ provenance: {
70
+ policyFingerprint: decision.policyFingerprint,
71
+ ...(decision.policyVersion === undefined ? {} : { policyVersion: decision.policyVersion }),
72
+ actionDigest: decision.actionDigest,
73
+ modelVersion: decision.modelVersion,
74
+ ...(decision.assessment === undefined ? {} : {
75
+ riskModelVersion: decision.assessment.modelVersion,
76
+ riskLevel: decision.assessment.riskLevel,
77
+ }),
78
+ decidedAt: decision.decidedAt,
79
+ },
80
+ text: '',
81
+ };
82
+ explanation.text = render(decision, explanation);
83
+ return explanation;
84
+ }
85
+ function buildHeadline(decision, failing, escalating) {
86
+ const prefix = VERDICT_HEADLINE[decision.verdict];
87
+ if (decision.verdict === 'allow') {
88
+ const checked = decision.evaluations.filter((e) => e.outcome === 'pass').length;
89
+ const unconfigured = decision.evaluations.filter((e) => e.outcome === 'not_configured').length;
90
+ return `${prefix}: ${checked} ${checked === 1 ? 'rule' : 'rules'} were applied and passed`
91
+ + (unconfigured > 0
92
+ ? `, and ${unconfigured} ${unconfigured === 1 ? 'axis was' : 'axes were'} left unbounded by this policy.`
93
+ : '.');
94
+ }
95
+ const driving = failing[0] ?? escalating[0];
96
+ return driving
97
+ ? `${prefix}: ${driving.statement}`
98
+ : `${prefix}.`;
99
+ }
100
+ function render(decision, explanation) {
101
+ const lines = [
102
+ `${explanation.headline}`,
103
+ '',
104
+ `Agent: ${decision.agentId}`,
105
+ `Decision: ${decision.decisionId} at ${decision.decidedAt}`,
106
+ `Policy: ${decision.policyVersion ? `${decision.policyVersion} ` : ''}#${decision.policyFingerprint}`,
107
+ `Action: #${decision.actionDigest}`,
108
+ ];
109
+ if (decision.assessment) {
110
+ lines.push(`Risk: ${decision.assessment.riskLevel} `
111
+ + `(confidence ${decision.assessment.confidence}) — `
112
+ + `${protect_1.RISK_LEVEL_MEANING[decision.assessment.riskLevel]}`);
113
+ }
114
+ else if (decision.assessmentUnavailable) {
115
+ lines.push(`Risk: not assessed — ${decision.assessmentUnavailable}`);
116
+ }
117
+ lines.push('', 'Rules applied:');
118
+ for (const rule of decision.evaluations) {
119
+ const bound = rule.limit === undefined ? '' : ` [limit ${rule.limit}]`;
120
+ const seen = rule.observed === undefined ? '' : ` [observed ${rule.observed}]`;
121
+ lines.push(` ${symbolFor(rule.outcome)} ${rule.rule}: ${rule.statement}${bound}${seen}`);
122
+ }
123
+ if (explanation.unbounded.length > 0) {
124
+ lines.push('', 'Left unbounded by this policy:');
125
+ for (const line of explanation.unbounded)
126
+ lines.push(` - ${line}`);
127
+ }
128
+ if (explanation.toProceed.length > 0) {
129
+ lines.push('', 'For this to proceed, the operator would have to:');
130
+ for (const line of explanation.toProceed)
131
+ lines.push(` - ${line}`);
132
+ }
133
+ return lines.join('\n');
134
+ }
135
+ function symbolFor(outcome) {
136
+ switch (outcome) {
137
+ case 'pass': return 'PASS ';
138
+ case 'fail': return 'FAIL ';
139
+ case 'escalate': return 'HOLD ';
140
+ case 'not_applicable': return 'n/a ';
141
+ case 'not_configured': return 'unset';
142
+ }
143
+ }
144
+ //# sourceMappingURL=explain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explain.js","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from './model';
2
+ export * from './policy';
3
+ export * from './ledger';
4
+ export * from './explain';
5
+ export * from './client';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./model"), exports);
18
+ __exportStar(require("./policy"), exports);
19
+ __exportStar(require("./ledger"), exports);
20
+ __exportStar(require("./explain"), exports);
21
+ __exportStar(require("./client"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,2CAAyB;AACzB,2CAAyB;AACzB,4CAA0B;AAC1B,2CAAyB"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Cumulative spend and rate accounting.
3
+ *
4
+ * ## What this is, honestly
5
+ *
6
+ * **This ledger is in-process and in-memory.** It counts what one instance of
7
+ * one process admitted. It is not shared, not durable, and not correct across
8
+ * a restart or a horizontal scale-out: two replicas each enforcing a $10,000
9
+ * daily cap enforce $20,000 between them, and a restart resets the day to
10
+ * zero.
11
+ *
12
+ * That is stated rather than papered over, and the shape of the API is chosen
13
+ * so it can be fixed without a rewrite: `evaluatePolicy` depends only on the
14
+ * `AgentUsage` interface below, so a Redis- or Postgres-backed implementation
15
+ * of two methods replaces this class entirely. A distributed limit needs
16
+ * shared storage with atomic reservation, which is a later concern and a real
17
+ * one — do not deploy a fleet of agents behind this class and describe the cap
18
+ * as enforced.
19
+ *
20
+ * ## Why reservations, rather than counting on the way out
21
+ *
22
+ * An agent asks "may I", gets an allow, and then signs. If budget were only
23
+ * counted when the transaction is confirmed, an agent could ask a hundred
24
+ * times before the first answer was recorded and get a hundred allows against
25
+ * a cap of one. So an admitted decision holds its value immediately, and the
26
+ * hold is released when the caller says the action did not happen or when it
27
+ * expires unclaimed. The failure direction is a briefly over-tight budget,
28
+ * which is the right way for this to be wrong.
29
+ */
30
+ import type { AgentOutcomeRecord } from './model';
31
+ /**
32
+ * What `evaluatePolicy` needs to know about the past.
33
+ *
34
+ * Deliberately two read methods and nothing else. Anything satisfying this can
35
+ * be handed to the evaluator — including a shared store, which is how this
36
+ * becomes correct across processes.
37
+ */
38
+ export interface AgentUsage {
39
+ /** Value held or committed by this agent at or after `sinceMs`. */
40
+ spentUsdSince(agentId: string, sinceMs: number): number;
41
+ /** Decisions this guard admitted for the agent at or after `sinceMs`. */
42
+ actionsSince(agentId: string, sinceMs: number): number;
43
+ }
44
+ /**
45
+ * A usage source that can also hold budget.
46
+ *
47
+ * Kept separate from `AgentUsage` because the two are genuinely different
48
+ * capabilities: a read-only source (a warehouse query, a metrics store) can
49
+ * answer "how much has this agent spent today" without being able to take a
50
+ * hold. `AgentGuard` accepts either, and says which it got — a guard running
51
+ * on a read-only source enforces cumulative caps against *settled* history
52
+ * only, which cannot stop two concurrent actions that each fit alone.
53
+ */
54
+ export interface ReservationLedger extends AgentUsage {
55
+ reserve(input: {
56
+ decisionId: string;
57
+ agentId: string;
58
+ valueUsd: number;
59
+ idempotencyKey?: string;
60
+ now?: Date;
61
+ }): Reservation;
62
+ confirm(decisionId: string, options?: {
63
+ txHash?: string;
64
+ now?: Date;
65
+ }): AgentOutcomeRecord | null;
66
+ release(decisionId: string, options?: {
67
+ note?: string;
68
+ now?: Date;
69
+ }): AgentOutcomeRecord | null;
70
+ }
71
+ export declare function isReservationLedger(usage: AgentUsage): usage is ReservationLedger;
72
+ export type ReservationState = 'held' | 'confirmed' | 'released' | 'expired';
73
+ export interface Reservation {
74
+ decisionId: string;
75
+ agentId: string;
76
+ valueUsd: number;
77
+ idempotencyKey?: string;
78
+ state: ReservationState;
79
+ createdAtMs: number;
80
+ expiresAtMs: number;
81
+ settledAtMs?: number;
82
+ txHash?: string;
83
+ note?: string;
84
+ }
85
+ export interface SpendLedgerOptions {
86
+ /**
87
+ * How long an unsettled hold survives. Default 5 minutes — long enough for a
88
+ * signature and a broadcast, short enough that an agent that crashed
89
+ * mid-action does not hold its budget until the window rolls over.
90
+ */
91
+ reservationTtlMs?: number;
92
+ /**
93
+ * How long settled entries are kept. Must exceed the longest policy window,
94
+ * or a cumulative cap silently stops being cumulative. Default 8 days.
95
+ */
96
+ retentionMs?: number;
97
+ /** A hard ceiling on entries, so a runaway agent cannot exhaust memory. */
98
+ maxEntries?: number;
99
+ /**
100
+ * The clock, injected for the same reason Protect injects `fetch`: expiry
101
+ * and retention are time-dependent behaviour, and behaviour that can only be
102
+ * exercised by waiting is behaviour that does not get tested.
103
+ */
104
+ clock?: () => number;
105
+ }
106
+ export declare class SpendLedger implements ReservationLedger {
107
+ private readonly reservationTtlMs;
108
+ private readonly retentionMs;
109
+ private readonly maxEntries;
110
+ private readonly clock;
111
+ private readonly entries;
112
+ private readonly byDecision;
113
+ private readonly byIdempotencyKey;
114
+ constructor(options?: SpendLedgerOptions);
115
+ /**
116
+ * Hold budget for an admitted decision.
117
+ *
118
+ * A repeated `idempotencyKey` returns the existing hold instead of taking a
119
+ * second one. An agent retrying a call after a timeout is one action, and
120
+ * counting it twice would tighten its own cap against it for no reason.
121
+ */
122
+ reserve(input: {
123
+ decisionId: string;
124
+ agentId: string;
125
+ valueUsd: number;
126
+ idempotencyKey?: string;
127
+ now?: Date;
128
+ }): Reservation;
129
+ /** The action happened. The hold becomes a permanent commitment. */
130
+ confirm(decisionId: string, options?: {
131
+ txHash?: string;
132
+ now?: Date;
133
+ }): AgentOutcomeRecord | null;
134
+ /** The action did not happen. The hold is returned to the budget. */
135
+ release(decisionId: string, options?: {
136
+ note?: string;
137
+ now?: Date;
138
+ }): AgentOutcomeRecord | null;
139
+ /**
140
+ * Value held or committed in the window.
141
+ *
142
+ * Released and expired holds do not count. They are actions that did not
143
+ * happen, and charging an agent for them would shrink a cap the operator
144
+ * set for real spending.
145
+ */
146
+ spentUsdSince(agentId: string, sinceMs: number): number;
147
+ /**
148
+ * Decisions admitted in the window — regardless of how they settled.
149
+ *
150
+ * A rate ceiling counts what the guard admitted, not what landed on chain.
151
+ * An agent that spins through assess-and-abandon at machine speed is exactly
152
+ * the loop the ceiling exists to catch, and it would be invisible to a
153
+ * counter that only saw confirmed transactions.
154
+ */
155
+ actionsSince(agentId: string, sinceMs: number): number;
156
+ reservation(decisionId: string): Reservation | undefined;
157
+ /** Snapshot for inspection. Copies, so a caller cannot edit the ledger. */
158
+ list(agentId?: string): Reservation[];
159
+ clear(): void;
160
+ private settle;
161
+ private prune;
162
+ private enforceCeiling;
163
+ /** Idempotency keys are the caller's, so they are namespaced per agent. */
164
+ private keyFor;
165
+ }
166
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
package/dist/ledger.js ADDED
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ /**
3
+ * Cumulative spend and rate accounting.
4
+ *
5
+ * ## What this is, honestly
6
+ *
7
+ * **This ledger is in-process and in-memory.** It counts what one instance of
8
+ * one process admitted. It is not shared, not durable, and not correct across
9
+ * a restart or a horizontal scale-out: two replicas each enforcing a $10,000
10
+ * daily cap enforce $20,000 between them, and a restart resets the day to
11
+ * zero.
12
+ *
13
+ * That is stated rather than papered over, and the shape of the API is chosen
14
+ * so it can be fixed without a rewrite: `evaluatePolicy` depends only on the
15
+ * `AgentUsage` interface below, so a Redis- or Postgres-backed implementation
16
+ * of two methods replaces this class entirely. A distributed limit needs
17
+ * shared storage with atomic reservation, which is a later concern and a real
18
+ * one — do not deploy a fleet of agents behind this class and describe the cap
19
+ * as enforced.
20
+ *
21
+ * ## Why reservations, rather than counting on the way out
22
+ *
23
+ * An agent asks "may I", gets an allow, and then signs. If budget were only
24
+ * counted when the transaction is confirmed, an agent could ask a hundred
25
+ * times before the first answer was recorded and get a hundred allows against
26
+ * a cap of one. So an admitted decision holds its value immediately, and the
27
+ * hold is released when the caller says the action did not happen or when it
28
+ * expires unclaimed. The failure direction is a briefly over-tight budget,
29
+ * which is the right way for this to be wrong.
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.SpendLedger = void 0;
33
+ exports.isReservationLedger = isReservationLedger;
34
+ function isReservationLedger(usage) {
35
+ const candidate = usage;
36
+ return typeof candidate.reserve === 'function'
37
+ && typeof candidate.confirm === 'function'
38
+ && typeof candidate.release === 'function';
39
+ }
40
+ const DEFAULT_RESERVATION_TTL_MS = 5 * 60_000;
41
+ const DEFAULT_RETENTION_MS = 8 * 24 * 60 * 60_000;
42
+ const DEFAULT_MAX_ENTRIES = 50_000;
43
+ class SpendLedger {
44
+ reservationTtlMs;
45
+ retentionMs;
46
+ maxEntries;
47
+ clock;
48
+ entries = [];
49
+ byDecision = new Map();
50
+ byIdempotencyKey = new Map();
51
+ constructor(options = {}) {
52
+ this.reservationTtlMs = options.reservationTtlMs ?? DEFAULT_RESERVATION_TTL_MS;
53
+ this.retentionMs = options.retentionMs ?? DEFAULT_RETENTION_MS;
54
+ this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
55
+ this.clock = options.clock ?? (() => Date.now());
56
+ }
57
+ /**
58
+ * Hold budget for an admitted decision.
59
+ *
60
+ * A repeated `idempotencyKey` returns the existing hold instead of taking a
61
+ * second one. An agent retrying a call after a timeout is one action, and
62
+ * counting it twice would tighten its own cap against it for no reason.
63
+ */
64
+ reserve(input) {
65
+ const nowMs = input.now ? input.now.getTime() : this.clock();
66
+ this.prune(nowMs);
67
+ if (input.idempotencyKey) {
68
+ const existing = this.byIdempotencyKey.get(this.keyFor(input.agentId, input.idempotencyKey));
69
+ if (existing && existing.state !== 'released' && existing.state !== 'expired')
70
+ return existing;
71
+ }
72
+ const reservation = {
73
+ decisionId: input.decisionId,
74
+ agentId: input.agentId,
75
+ // A negative value would be a credit against the agent's own cap, which
76
+ // is a way to spend more than the cap allows. Clamped, not trusted.
77
+ valueUsd: Number.isFinite(input.valueUsd) ? Math.max(0, input.valueUsd) : 0,
78
+ ...(input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}),
79
+ state: 'held',
80
+ createdAtMs: nowMs,
81
+ expiresAtMs: nowMs + this.reservationTtlMs,
82
+ };
83
+ this.entries.push(reservation);
84
+ this.byDecision.set(reservation.decisionId, reservation);
85
+ if (input.idempotencyKey) {
86
+ this.byIdempotencyKey.set(this.keyFor(input.agentId, input.idempotencyKey), reservation);
87
+ }
88
+ this.enforceCeiling();
89
+ return reservation;
90
+ }
91
+ /** The action happened. The hold becomes a permanent commitment. */
92
+ confirm(decisionId, options = {}) {
93
+ return this.settle(decisionId, 'confirmed', {
94
+ ...(options.txHash === undefined ? {} : { txHash: options.txHash }),
95
+ ...(options.now === undefined ? {} : { now: options.now }),
96
+ });
97
+ }
98
+ /** The action did not happen. The hold is returned to the budget. */
99
+ release(decisionId, options = {}) {
100
+ return this.settle(decisionId, 'released', {
101
+ ...(options.note === undefined ? {} : { note: options.note }),
102
+ ...(options.now === undefined ? {} : { now: options.now }),
103
+ });
104
+ }
105
+ /**
106
+ * Value held or committed in the window.
107
+ *
108
+ * Released and expired holds do not count. They are actions that did not
109
+ * happen, and charging an agent for them would shrink a cap the operator
110
+ * set for real spending.
111
+ */
112
+ spentUsdSince(agentId, sinceMs) {
113
+ this.prune(this.clock());
114
+ let total = 0;
115
+ for (const entry of this.entries) {
116
+ if (entry.agentId !== agentId)
117
+ continue;
118
+ if (entry.state === 'released' || entry.state === 'expired')
119
+ continue;
120
+ if (entry.createdAtMs < sinceMs)
121
+ continue;
122
+ total += entry.valueUsd;
123
+ }
124
+ return total;
125
+ }
126
+ /**
127
+ * Decisions admitted in the window — regardless of how they settled.
128
+ *
129
+ * A rate ceiling counts what the guard admitted, not what landed on chain.
130
+ * An agent that spins through assess-and-abandon at machine speed is exactly
131
+ * the loop the ceiling exists to catch, and it would be invisible to a
132
+ * counter that only saw confirmed transactions.
133
+ */
134
+ actionsSince(agentId, sinceMs) {
135
+ this.prune(this.clock());
136
+ let count = 0;
137
+ for (const entry of this.entries) {
138
+ if (entry.agentId !== agentId)
139
+ continue;
140
+ if (entry.createdAtMs < sinceMs)
141
+ continue;
142
+ count += 1;
143
+ }
144
+ return count;
145
+ }
146
+ reservation(decisionId) {
147
+ return this.byDecision.get(decisionId);
148
+ }
149
+ /** Snapshot for inspection. Copies, so a caller cannot edit the ledger. */
150
+ list(agentId) {
151
+ return this.entries
152
+ .filter((entry) => !agentId || entry.agentId === agentId)
153
+ .map((entry) => ({ ...entry }));
154
+ }
155
+ clear() {
156
+ this.entries.length = 0;
157
+ this.byDecision.clear();
158
+ this.byIdempotencyKey.clear();
159
+ }
160
+ // -------------------------------------------------------------------------
161
+ settle(decisionId, state, options) {
162
+ const now = options.now ?? new Date(this.clock());
163
+ const entry = this.byDecision.get(decisionId);
164
+ if (!entry)
165
+ return null;
166
+ // An expired hold that later confirms is a real and dangerous case: the
167
+ // budget was already returned, so the spend happened outside the cap. It
168
+ // is recorded as confirmed so the total is right, and the note says the
169
+ // hold had lapsed, because that is a tuning signal for the TTL.
170
+ const lapsed = entry.state === 'expired';
171
+ entry.state = state;
172
+ entry.settledAtMs = now.getTime();
173
+ if (options.txHash !== undefined)
174
+ entry.txHash = options.txHash;
175
+ if (options.note !== undefined)
176
+ entry.note = options.note;
177
+ return {
178
+ decisionId,
179
+ state,
180
+ ...(entry.txHash === undefined ? {} : { txHash: entry.txHash }),
181
+ ...(lapsed
182
+ ? { note: 'The hold had already expired when this was settled; the reservation TTL may be too short.' }
183
+ : entry.note === undefined ? {} : { note: entry.note }),
184
+ at: now.toISOString(),
185
+ };
186
+ }
187
+ prune(nowMs) {
188
+ for (const entry of this.entries) {
189
+ if (entry.state === 'held' && entry.expiresAtMs <= nowMs)
190
+ entry.state = 'expired';
191
+ }
192
+ for (let i = this.entries.length - 1; i >= 0; i -= 1) {
193
+ const entry = this.entries[i];
194
+ if (nowMs - entry.createdAtMs <= this.retentionMs)
195
+ continue;
196
+ this.entries.splice(i, 1);
197
+ this.byDecision.delete(entry.decisionId);
198
+ if (entry.idempotencyKey)
199
+ this.byIdempotencyKey.delete(this.keyFor(entry.agentId, entry.idempotencyKey));
200
+ }
201
+ }
202
+ enforceCeiling() {
203
+ while (this.entries.length > this.maxEntries) {
204
+ const entry = this.entries.shift();
205
+ if (!entry)
206
+ break;
207
+ this.byDecision.delete(entry.decisionId);
208
+ if (entry.idempotencyKey)
209
+ this.byIdempotencyKey.delete(this.keyFor(entry.agentId, entry.idempotencyKey));
210
+ }
211
+ }
212
+ /** Idempotency keys are the caller's, so they are namespaced per agent. */
213
+ keyFor(agentId, idempotencyKey) {
214
+ return `${agentId}${idempotencyKey}`;
215
+ }
216
+ }
217
+ exports.SpendLedger = SpendLedger;
218
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}