@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,256 @@
1
+ /**
2
+ * AgentGuard — the types.
3
+ *
4
+ * Protect informs a person, who then decides. AgentGuard sits in front of
5
+ * something with no judgement that will do exactly what it was told, at
6
+ * machine speed, as many times as its loop runs. Three consequences shape
7
+ * everything in this file:
8
+ *
9
+ * 1. **A warning is not a control.** Protect deliberately warns and never
10
+ * blocks, because the person holding the keys should decide. An agent
11
+ * cannot heed a warning — a warning it ignores is an allow with extra
12
+ * words. So AgentGuard's outcomes are `allow`, `require_human_approval`
13
+ * and `deny`, and the middle one means *stop and hand this to a person*.
14
+ *
15
+ * 2. **The operator is not the agent.** Limits are set by a human ahead of
16
+ * time. The agent must not be able to widen them — not by argument, not by
17
+ * mutating the object it was handed, not by passing an override. That is
18
+ * enforced structurally in `policy.ts` (`sealPolicy`, `narrowPolicy`),
19
+ * not by convention.
20
+ *
21
+ * 3. **Every decision must survive being questioned later.** "The agent spent
22
+ * the treasury" is answered by an audit record that names each rule, the
23
+ * limit it enforced and the value it observed. An allow that cannot be
24
+ * explained is as bad as a wrong one, so a passing rule is recorded just
25
+ * as loudly as a failing one.
26
+ *
27
+ * The risk model is *not* reimplemented here. Classification, the seven risk
28
+ * levels and `applyPolicy` come from `@decentrys/protect` unchanged; this
29
+ * package adds the limits an autonomous actor needs and a human does not.
30
+ */
31
+ import type { Assessment, Policy, RiskLevel } from '@decentrys/protect';
32
+ export declare const AGENT_MODEL_VERSION = "agent-1.0.0";
33
+ /**
34
+ * The kinds of action a policy can talk about.
35
+ *
36
+ * These are coarse on purpose. "May swap, may not approve" is a sentence an
37
+ * operator can write and check; "may call selector 0x095ea7b3" is not. An
38
+ * action whose type the integrator cannot determine is `unknown`, which is a
39
+ * first-class value rather than a default — see `deniedActions` below.
40
+ */
41
+ export declare const AGENT_ACTION_TYPES: readonly ["transfer", "swap", "approve", "contract_call", "bridge", "stake", "unstake", "deploy", "sign_message", "unknown"];
42
+ export type AgentActionType = (typeof AGENT_ACTION_TYPES)[number];
43
+ export interface AgentAction {
44
+ /** Which agent proposes it. Limits are held per agent, not per process. */
45
+ agentId: string;
46
+ chain: string;
47
+ type: AgentActionType;
48
+ /** The wallet the agent signs with. */
49
+ from: string;
50
+ /** Counterparty or contract. Absent for a deploy or a bare message signature. */
51
+ to?: string;
52
+ /** Token contract, for a transfer, swap or approval. */
53
+ token?: string;
54
+ /** Base units, as a string. Numbers lose precision at these magnitudes. */
55
+ value?: string;
56
+ /**
57
+ * The action's value in USD, as the integrator prices it.
58
+ *
59
+ * **`undefined` is not zero.** An action with no value at all (a message
60
+ * signature, a zero-value call) should say `0`; leaving this out means "I do
61
+ * not know what this is worth", and a value cap cannot be enforced against
62
+ * an unknown. See `VALUE_UNKNOWN` in `policy.ts` for what happens then, and
63
+ * why it is the only defensible answer for an autonomous signer.
64
+ *
65
+ * AgentGuard never prices anything itself. Pricing belongs to whoever runs
66
+ * the agent, with the feed and the staleness rules they are willing to be
67
+ * accountable for.
68
+ */
69
+ valueUsd?: number;
70
+ /** EVM calldata. */
71
+ data?: string;
72
+ /** Non-EVM families carry their serialized payload here. */
73
+ raw?: string;
74
+ /** The site or service that asked for this, when there is one. */
75
+ origin?: string;
76
+ /** Base units, or `unlimited`. Only meaningful for `type: 'approve'`. */
77
+ approvalAmount?: string;
78
+ /** What the agent believes it is doing, in its own words. Recorded, never trusted. */
79
+ intent?: string;
80
+ /**
81
+ * Set by the caller so a retried action is not counted twice against a
82
+ * cumulative cap. Two calls carrying the same key reuse one reservation.
83
+ */
84
+ idempotencyKey?: string;
85
+ proposedAt?: string;
86
+ }
87
+ /** A cumulative limit: how much may be committed inside a rolling window. */
88
+ export interface SpendWindow {
89
+ /** A name an operator recognises in an audit record — 'daily', 'per-hour'. */
90
+ label: string;
91
+ windowMs: number;
92
+ maxValueUsd: number;
93
+ }
94
+ /** A rate ceiling: how many actions may be admitted inside a rolling window. */
95
+ export interface RateWindow {
96
+ label: string;
97
+ windowMs: number;
98
+ maxActions: number;
99
+ }
100
+ /**
101
+ * What an operator can express.
102
+ *
103
+ * Every field is optional and every omitted field means *unconstrained on that
104
+ * axis* — with one deliberate exception, `allowUnlimitedApprovals`, which
105
+ * defaults to refusing. An unlimited approval is the single action that turns
106
+ * a bounded mistake into an unbounded one, and defaulting it open would make
107
+ * the safe configuration the one you have to remember to write.
108
+ */
109
+ export interface AgentPolicy {
110
+ /** Free text for the operator's own change control. Recorded in every decision. */
111
+ version?: string;
112
+ /**
113
+ * Bind this policy to one agent. When set, an action from a different
114
+ * `agentId` is denied rather than evaluated — a policy leaking across agents
115
+ * is how one agent's generous limits become another's.
116
+ */
117
+ agentId?: string;
118
+ /**
119
+ * Risk level → action, exactly the `Policy` type Protect uses. Merged over
120
+ * `DEFAULT_AGENT_RISK_POLICY`; see that constant for why the agent default
121
+ * differs from the consumer one.
122
+ */
123
+ risk?: Policy;
124
+ /** Shorthand: deny anything strictly above this level. Applied on top of `risk`. */
125
+ maxRiskLevel?: RiskLevel;
126
+ maxValuePerActionUsd?: number;
127
+ /** Rolling cumulative caps. Several may run at once — per-hour and per-day. */
128
+ spendWindows?: SpendWindow[];
129
+ /**
130
+ * When present, an allowlist: any counterparty not in it is denied. Absent
131
+ * means unconstrained, which is a real choice an operator may make and not
132
+ * one AgentGuard makes for them.
133
+ */
134
+ allowedCounterparties?: string[];
135
+ /** Always denied, even if also allowlisted. Denial wins. */
136
+ blockedCounterparties?: string[];
137
+ /** When present, an allowlist over the contract the action calls. */
138
+ allowedContracts?: string[];
139
+ /** When present, an allowlist over `token`. */
140
+ allowedTokens?: string[];
141
+ allowedActions?: AgentActionType[];
142
+ deniedActions?: AgentActionType[];
143
+ allowedChains?: string[];
144
+ /** Default `false`. Set true only if an unbounded allowance is genuinely intended. */
145
+ allowUnlimitedApprovals?: boolean;
146
+ /** An agent stuck in a loop is a real failure mode, not a hypothetical one. */
147
+ rateWindows?: RateWindow[];
148
+ /** Above this value, a human decides even when every other rule passes. */
149
+ humanApprovalAboveUsd?: number;
150
+ /** At or above this level, a human decides even when the risk policy allows. */
151
+ humanApprovalAtRiskLevel?: RiskLevel;
152
+ /** What to do when Decentrys could not be reached. Default `escalate`. */
153
+ failMode?: AgentFailMode;
154
+ }
155
+ /**
156
+ * What AgentGuard does when the risk check could not run.
157
+ *
158
+ * Protect offers `open | warn | closed` and defaults to `warn`, which is right
159
+ * for a wallet: tell the person, let them decide. **`warn` is not offered
160
+ * here, because there is nobody to warn.** A warning delivered to an
161
+ * autonomous process is an allow that logs.
162
+ *
163
+ * - `open` — proceed on operator-set limits alone.
164
+ * - `escalate` — the local policy still applies and still binds; only the
165
+ * external risk check is missing, so a human decides. **Default.**
166
+ * - `closed` — stop.
167
+ *
168
+ * `escalate` is the default because it is the only one of the three that
169
+ * loses nothing. `open` signs unscreened transactions during exactly the
170
+ * window an attacker would choose to create. `closed` halts the agent
171
+ * completely, which for a liquidation or a market-making agent is its own
172
+ * loss and pushes operators to configure `open` to avoid it. `escalate`
173
+ * spends a human's attention instead of the treasury, and where an integrator
174
+ * has no approval path it degrades to a stop — the safe direction.
175
+ *
176
+ * Note that a local rule that *denies* still denies during an outage. An
177
+ * unreachable Decentrys removes one input; it does not suspend the operator's
178
+ * limits.
179
+ */
180
+ export type AgentFailMode = 'open' | 'escalate' | 'closed';
181
+ /**
182
+ * Three outcomes, never two.
183
+ *
184
+ * Collapsing `require_human_approval` into `deny` loses the difference between
185
+ * "this must not happen" and "a person needs to look at this", and an operator
186
+ * who cannot see that difference in the log cannot tune the policy.
187
+ */
188
+ export type AgentVerdict = 'allow' | 'require_human_approval' | 'deny';
189
+ /** The rules AgentGuard can apply. Stable identifiers — they end up in logs. */
190
+ export declare const AGENT_RULES: readonly ["AGENT_BINDING", "ACTION_TYPE", "CHAIN", "COUNTERPARTY", "CONTRACT", "TOKEN", "APPROVAL_ALLOWANCE", "VALUE_PER_ACTION", "CUMULATIVE_SPEND", "RATE_LIMIT", "RISK_LEVEL", "ASSESSMENT_AVAILABILITY", "HUMAN_APPROVAL_THRESHOLD"];
191
+ export type AgentRuleId = (typeof AGENT_RULES)[number];
192
+ /**
193
+ * `not_configured` and `not_applicable` are distinct and both are recorded.
194
+ *
195
+ * "There was no counterparty allowlist" and "there was one and this
196
+ * counterparty was on it" produce the same allow and mean completely
197
+ * different things to whoever reads the record afterwards.
198
+ */
199
+ export type RuleOutcome = 'pass' | 'fail' | 'escalate' | 'not_applicable' | 'not_configured';
200
+ export interface RuleEvaluation {
201
+ rule: AgentRuleId;
202
+ outcome: RuleOutcome;
203
+ /** One sentence naming the limit and what was seen against it. */
204
+ statement: string;
205
+ /** The configured bound, when there was one. */
206
+ limit?: string | number;
207
+ /** What this action presented against that bound. */
208
+ observed?: string | number;
209
+ }
210
+ export interface AgentDecision {
211
+ /** Stable id for this decision, carried into the reservation and the log. */
212
+ decisionId: string;
213
+ agentId: string;
214
+ verdict: AgentVerdict;
215
+ /** The failing or escalating rules, in evaluation order. Empty on a clean allow. */
216
+ reasons: string[];
217
+ /**
218
+ * Every rule that was considered, passing ones included.
219
+ *
220
+ * This is what makes an *allow* explainable. A record that lists only what
221
+ * went wrong cannot answer "why was this permitted", which is the question
222
+ * asked after the money is gone.
223
+ */
224
+ evaluations: RuleEvaluation[];
225
+ /** The Protect assessment this decision used, when one was obtained. */
226
+ assessment?: Assessment;
227
+ /** Why no assessment was available, when none was. */
228
+ assessmentUnavailable?: string;
229
+ /** Fingerprint of the sealed policy in force, so the record is reproducible. */
230
+ policyFingerprint: string;
231
+ policyVersion?: string;
232
+ /**
233
+ * Present on `allow` and `require_human_approval`. Value and rate budget is
234
+ * held against this id until it is confirmed or released; see `ledger.ts`.
235
+ */
236
+ reservationId?: string;
237
+ /** A digest of the action, so a log entry can be tied to what was proposed. */
238
+ actionDigest: string;
239
+ modelVersion: string;
240
+ decidedAt: string;
241
+ }
242
+ /**
243
+ * What actually happened to a reserved action.
244
+ *
245
+ * Recorded separately from the decision, because "AgentGuard allowed it" and
246
+ * "the agent then executed it" are different facts and an audit trail that
247
+ * conflates them cannot show an allow that was never used.
248
+ */
249
+ export interface AgentOutcomeRecord {
250
+ decisionId: string;
251
+ state: 'confirmed' | 'released' | 'expired';
252
+ txHash?: string;
253
+ note?: string;
254
+ at: string;
255
+ }
256
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
package/dist/model.js ADDED
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * AgentGuard — the types.
4
+ *
5
+ * Protect informs a person, who then decides. AgentGuard sits in front of
6
+ * something with no judgement that will do exactly what it was told, at
7
+ * machine speed, as many times as its loop runs. Three consequences shape
8
+ * everything in this file:
9
+ *
10
+ * 1. **A warning is not a control.** Protect deliberately warns and never
11
+ * blocks, because the person holding the keys should decide. An agent
12
+ * cannot heed a warning — a warning it ignores is an allow with extra
13
+ * words. So AgentGuard's outcomes are `allow`, `require_human_approval`
14
+ * and `deny`, and the middle one means *stop and hand this to a person*.
15
+ *
16
+ * 2. **The operator is not the agent.** Limits are set by a human ahead of
17
+ * time. The agent must not be able to widen them — not by argument, not by
18
+ * mutating the object it was handed, not by passing an override. That is
19
+ * enforced structurally in `policy.ts` (`sealPolicy`, `narrowPolicy`),
20
+ * not by convention.
21
+ *
22
+ * 3. **Every decision must survive being questioned later.** "The agent spent
23
+ * the treasury" is answered by an audit record that names each rule, the
24
+ * limit it enforced and the value it observed. An allow that cannot be
25
+ * explained is as bad as a wrong one, so a passing rule is recorded just
26
+ * as loudly as a failing one.
27
+ *
28
+ * The risk model is *not* reimplemented here. Classification, the seven risk
29
+ * levels and `applyPolicy` come from `@decentrys/protect` unchanged; this
30
+ * package adds the limits an autonomous actor needs and a human does not.
31
+ */
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.AGENT_RULES = exports.AGENT_ACTION_TYPES = exports.AGENT_MODEL_VERSION = void 0;
34
+ exports.AGENT_MODEL_VERSION = 'agent-1.0.0';
35
+ // ---------------------------------------------------------------------------
36
+ // What an agent proposes to do
37
+ // ---------------------------------------------------------------------------
38
+ /**
39
+ * The kinds of action a policy can talk about.
40
+ *
41
+ * These are coarse on purpose. "May swap, may not approve" is a sentence an
42
+ * operator can write and check; "may call selector 0x095ea7b3" is not. An
43
+ * action whose type the integrator cannot determine is `unknown`, which is a
44
+ * first-class value rather than a default — see `deniedActions` below.
45
+ */
46
+ exports.AGENT_ACTION_TYPES = [
47
+ 'transfer',
48
+ 'swap',
49
+ 'approve',
50
+ 'contract_call',
51
+ 'bridge',
52
+ 'stake',
53
+ 'unstake',
54
+ 'deploy',
55
+ 'sign_message',
56
+ 'unknown',
57
+ ];
58
+ /** The rules AgentGuard can apply. Stable identifiers — they end up in logs. */
59
+ exports.AGENT_RULES = [
60
+ 'AGENT_BINDING',
61
+ 'ACTION_TYPE',
62
+ 'CHAIN',
63
+ 'COUNTERPARTY',
64
+ 'CONTRACT',
65
+ 'TOKEN',
66
+ 'APPROVAL_ALLOWANCE',
67
+ 'VALUE_PER_ACTION',
68
+ 'CUMULATIVE_SPEND',
69
+ 'RATE_LIMIT',
70
+ 'RISK_LEVEL',
71
+ 'ASSESSMENT_AVAILABILITY',
72
+ 'HUMAN_APPROVAL_THRESHOLD',
73
+ ];
74
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;AAIU,QAAA,mBAAmB,GAAG,aAAa,CAAC;AAEjD,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E;;;;;;;GAOG;AACU,QAAA,kBAAkB,GAAG;IAChC,UAAU;IACV,MAAM;IACN,SAAS;IACT,eAAe;IACf,QAAQ;IACR,OAAO;IACP,SAAS;IACT,QAAQ;IACR,cAAc;IACd,SAAS;CACD,CAAC;AAqLX,gFAAgF;AACnE,QAAA,WAAW,GAAG;IACzB,eAAe;IACf,aAAa;IACb,OAAO;IACP,cAAc;IACd,UAAU;IACV,OAAO;IACP,oBAAoB;IACpB,kBAAkB;IAClB,kBAAkB;IAClB,YAAY;IACZ,YAAY;IACZ,yBAAyB;IACzB,0BAA0B;CAClB,CAAC"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The policy engine.
3
+ *
4
+ * Two things live here, and keeping them together is deliberate.
5
+ *
6
+ * **Sealing** is how "the agent cannot widen its own limits" stops being a
7
+ * convention. An operator writes an `AgentPolicy` and calls `sealPolicy()`.
8
+ * What comes back is deep-frozen, cloned away from the caller's own arrays,
9
+ * and carries a fingerprint of its contents. `AgentGuard` accepts nothing
10
+ * else, exposes no setter, and records the fingerprint on every decision. An
11
+ * agent handed a reference to that object can read it and cannot change it,
12
+ * and a policy that changed between two decisions cannot pretend it did not.
13
+ *
14
+ * The only way to derive a new policy is `narrowPolicy()`, which takes the
15
+ * stricter of every field. There is no widening path in this file at all —
16
+ * not a guarded one, not an admin one. A capability that does not exist
17
+ * cannot be reached by a sufficiently persuasive prompt.
18
+ *
19
+ * **Evaluation** is a pure function. `evaluatePolicy()` performs no I/O and
20
+ * reads no clock it was not given, so the same action, policy, assessment and
21
+ * usage always produce the same decision. That is what makes an audit record
22
+ * checkable rather than merely readable: an investigator can re-run it.
23
+ */
24
+ import { type Assessment, type Policy } from '@decentrys/protect';
25
+ import { type AgentAction, type AgentDecision, type AgentPolicy } from './model';
26
+ import type { AgentUsage } from './ledger';
27
+ /**
28
+ * How risk levels map to behaviour when nobody is watching.
29
+ *
30
+ * Protect's consumer default warns from CAUTION upward and blocks only
31
+ * `KNOWN_MALICIOUS`, because a person is reading the warning. Neither half of
32
+ * that transfers:
33
+ *
34
+ * - **`warn` is not available to an agent.** It is mapped out entirely; see
35
+ * `sealPolicy`, which refuses a `warn` in an agent risk policy rather than
36
+ * quietly reinterpreting it.
37
+ * - **CAUTION stays `allow`, and that is not laziness.** CAUTION is reached
38
+ * by a single SIGNIFICANT *capability* — a pausable token, an upgradeable
39
+ * router. Most serious protocols have one. Escalating there would send a
40
+ * human hundreds of approvals a day, and a human who approves 500
41
+ * escalations approves the 501st unread. The Sentinel stage recorded the
42
+ * same failure for alerts; it is worse here, because the 501st is the one
43
+ * that empties the treasury. The capability is still recorded on the
44
+ * decision, so it is visible without being an interrupt.
45
+ * - **ELEVATED_RISK upward escalates**, because at that point actual threat
46
+ * signals with evidence exist and a person can weigh them.
47
+ * - **`KNOWN_MALICIOUS` denies, and a denial is not escalatable.** It is the
48
+ * one level that requires analyst-verified evidence, and there is no value
49
+ * at which signing to confirmed malicious infrastructure is the right call.
50
+ *
51
+ * `CRITICAL_THREAT` escalates rather than denies for the reason Protect draws
52
+ * the same line: severe evidence that no analyst has confirmed is exactly the
53
+ * case where a person should look at the evidence.
54
+ */
55
+ export declare const DEFAULT_AGENT_RISK_POLICY: Required<Policy>;
56
+ /**
57
+ * The brand, as a real runtime symbol rather than a `declare`d type-only one.
58
+ *
59
+ * A type-only brand is erased at compile time, so an object cast to
60
+ * `SealedPolicy` would satisfy every check and carry no mark at all. This
61
+ * symbol is module-private and never exported, so the only way to obtain an
62
+ * object bearing it is to call `sealPolicy()` — which clones and freezes.
63
+ */
64
+ declare const SEALED_POLICY: unique symbol;
65
+ export interface SealedPolicy {
66
+ /**
67
+ * Structural, not decorative. A plain object literal is not assignable to
68
+ * `SealedPolicy`, so there is no way to hand `AgentGuard` a policy that did
69
+ * not go through `sealPolicy()` — including from inside a tool an agent
70
+ * controls.
71
+ */
72
+ readonly [SEALED_POLICY]: true;
73
+ readonly policy: Readonly<AgentPolicy>;
74
+ /**
75
+ * A content fingerprint. Not a cryptographic commitment — it exists so two
76
+ * decisions can be shown to have run under the same rules, and so a policy
77
+ * that changed mid-incident cannot be described afterwards as unchanged.
78
+ */
79
+ readonly fingerprint: string;
80
+ /**
81
+ * Configurations that are legal but deserve to be said out loud — a policy
82
+ * that permits confirmed-malicious counterparties, or one with no limits at
83
+ * all. Surfaced rather than refused: policy belongs to the integrator.
84
+ */
85
+ readonly warnings: readonly string[];
86
+ }
87
+ export declare function sealPolicy(policy: AgentPolicy): SealedPolicy;
88
+ /**
89
+ * Derive a stricter policy. There is no counterpart that loosens one.
90
+ *
91
+ * Every field is combined by taking the tighter side: caps become the minimum,
92
+ * allowlists intersect, blocklists union, windows accumulate, and
93
+ * `allowUnlimitedApprovals` is true only if both sides say so. So a
94
+ * restriction supplied at call time — including one an agent composed itself —
95
+ * can only ever reduce what is permitted.
96
+ */
97
+ export declare function narrowPolicy(base: SealedPolicy, restriction: AgentPolicy): SealedPolicy;
98
+ export interface EvaluatePolicyInput {
99
+ action: AgentAction;
100
+ policy: SealedPolicy;
101
+ /** The Protect assessment, when one was obtained. */
102
+ assessment?: Assessment | null;
103
+ /** Why no assessment was obtained. Drives the `failMode` rule. */
104
+ assessmentUnavailable?: string;
105
+ /** Committed spend and admitted actions so far. Absent means none recorded. */
106
+ usage?: AgentUsage;
107
+ now?: Date;
108
+ /** Supplied by the caller so a decision and its reservation share an id. */
109
+ decisionId?: string;
110
+ }
111
+ /**
112
+ * Evaluate an action against a sealed policy.
113
+ *
114
+ * Every rule is evaluated, including after one has already failed. Stopping at
115
+ * the first failure would produce a record saying an action exceeded its value
116
+ * cap while omitting that its counterparty was also confirmed malicious — and
117
+ * an operator reading that would tune the cap and ship the same hole.
118
+ *
119
+ * The verdict is the worst outcome any rule reached: one `fail` denies, one
120
+ * `escalate` sends it to a person, and everything else allows.
121
+ */
122
+ export declare function evaluatePolicy(input: EvaluatePolicyInput): AgentDecision;
123
+ /** `unlimited`, or a number large enough that no real supply could exhaust it. */
124
+ export declare function isUnlimitedAllowance(amount: string): boolean;
125
+ export declare function describeDuration(ms: number): string;
126
+ /**
127
+ * FNV-1a over canonical JSON.
128
+ *
129
+ * A change detector, not a commitment. It answers "were these two decisions
130
+ * made under the same rules" without a dependency and without a claim it
131
+ * cannot support; anyone needing tamper evidence should sign the record.
132
+ */
133
+ export declare function fingerprint(value: unknown): string;
134
+ /** `crypto.randomUUID` where it exists; a monotonic fallback everywhere else. */
135
+ export declare function newDecisionId(): string;
136
+ export {};
137
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAEL,KAAK,UAAU,EAAE,KAAK,MAAM,EAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,KAAK,WAAW,EAAwB,KAAK,aAAa,EAC1D,KAAK,WAAW,EAEjB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAM3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAQtD,CAAC;AAqBF;;;;;;;GAOG;AACH,QAAA,MAAM,aAAa,EAAE,OAAO,MAA+C,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAwC5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,GAAG,YAAY,CAgCvF;AAMD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,qDAAqD;IACrD,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,aAAa,CA2ExE;AA4LD,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAY5D;AAoPD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMnD;AAiJD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQlD;AAaD,iFAAiF;AACjF,wBAAgB,aAAa,IAAI,MAAM,CAKtC"}