@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,194 @@
1
+ /**
2
+ * AgentGuard — the client an operator wires in front of an agent.
3
+ *
4
+ * The contract differs from both of its neighbours, and the difference is the
5
+ * product.
6
+ *
7
+ * - **Protect never throws and never blocks.** It sits between a person and a
8
+ * signing screen; a security service having a bad minute must not cost
9
+ * someone their transaction, and the person decides.
10
+ * - **Sentinel throws.** It is management tooling, and an operator who
11
+ * believes they are monitored when they are not is worse off than one who
12
+ * saw an error.
13
+ * - **AgentGuard returns a verdict, and the verdict can be `deny`.** Nothing
14
+ * here throws on a call path either — an exception thrown at an agent is a
15
+ * condition it will handle by retrying, or by taking the branch that skips
16
+ * the guard. A decision object it must read is harder to route around.
17
+ *
18
+ * The one place this does throw is the constructor, on a missing credential or
19
+ * an unsealed policy. That is a wiring error made by a human at deploy time,
20
+ * not an event during an agent's run.
21
+ */
22
+ import { Decentrys, type Assessment, type CallOptions, type FetchLike, type Transport } from '@decentrys/protect';
23
+ import type { AgentAction, AgentDecision, AgentOutcomeRecord, AgentPolicy } from './model';
24
+ import { type SealedPolicy } from './policy';
25
+ import { type AgentUsage, type ReservationLedger } from './ledger';
26
+ import { type AgentActionExplanation } from './explain';
27
+ export declare const AGENT_SDK_VERSION = "0.1.0";
28
+ export interface AgentGuardConfig {
29
+ /**
30
+ * The operator's policy, already sealed.
31
+ *
32
+ * Typed as `SealedPolicy`, so a plain object cannot be passed here — an
33
+ * agent that constructs its own limits and hands them over does not
34
+ * typecheck and would not survive `sealPolicy`'s cloning if it did.
35
+ */
36
+ policy: SealedPolicy;
37
+ /** An existing Protect client. Supply this or `apiKey`. */
38
+ protect?: Decentrys;
39
+ apiKey?: string;
40
+ baseUrl?: string;
41
+ timeoutMs?: number;
42
+ retries?: number;
43
+ fetch?: FetchLike;
44
+ /** For tests, and for operators routing through their own gateway. */
45
+ transport?: Transport;
46
+ /**
47
+ * Where cumulative spend and rate state lives. Defaults to an in-process
48
+ * `SpendLedger` — read its file comment before running more than one
49
+ * instance, because a per-process cap on N processes is an N-times cap.
50
+ *
51
+ * A source that can only be read (see `ReservationLedger`) disables holds;
52
+ * `reservesBudget` reports which of the two this guard got.
53
+ */
54
+ ledger?: AgentUsage | ReservationLedger;
55
+ /** How many decisions to keep in memory for `auditLog()` / `explain()`. */
56
+ auditLogEntries?: number;
57
+ /**
58
+ * Called with every decision, before it is returned.
59
+ *
60
+ * The in-memory log is a convenience, not an audit trail: it dies with the
61
+ * process. This hook is where a real one is written. It is invoked
62
+ * synchronously and its exceptions are swallowed — a logging sink must not
63
+ * be able to change a verdict, in either direction.
64
+ */
65
+ onDecision?: (decision: AgentDecision) => void;
66
+ }
67
+ export interface AssessAgentTransactionOptions extends CallOptions {
68
+ /**
69
+ * A per-call restriction. It can only *narrow* — see `narrowPolicy`. There
70
+ * is no parameter anywhere in this class that widens a policy, which is what
71
+ * makes "the agent cannot raise its own limits" a property of the code
72
+ * rather than a rule someone has to remember.
73
+ */
74
+ restrict?: AgentPolicy;
75
+ /** An assessment the caller already holds, to avoid a second round trip. */
76
+ assessment?: Assessment;
77
+ now?: Date;
78
+ }
79
+ interface AuditEntry {
80
+ decision: AgentDecision;
81
+ outcome?: AgentOutcomeRecord;
82
+ }
83
+ export declare class AgentGuard {
84
+ private readonly protect;
85
+ private readonly sealed;
86
+ private readonly ledgerImpl;
87
+ private readonly reserving;
88
+ private readonly auditEntries;
89
+ private readonly auditLimit;
90
+ private readonly onDecision;
91
+ constructor(config: AgentGuardConfig);
92
+ /** The policy in force. Frozen — reading it is safe, changing it is impossible. */
93
+ get policy(): SealedPolicy;
94
+ /** Configurations that are legal but worth an operator's attention. */
95
+ get policyWarnings(): readonly string[];
96
+ /**
97
+ * Whether admitted actions hold budget before they execute.
98
+ *
99
+ * False when the guard was given a read-only usage source. Cumulative caps
100
+ * then count settled history only, so two actions that each fit under the
101
+ * cap can both be admitted and together exceed it. Exposed rather than
102
+ * assumed, because the difference is invisible until it costs something.
103
+ */
104
+ get reservesBudget(): boolean;
105
+ /**
106
+ * Assess a proposed action and decide whether the agent may sign it.
107
+ *
108
+ * Screening happens first and always — including when a local rule has
109
+ * already failed. A record saying an action exceeded its value cap, while
110
+ * omitting that its counterparty was confirmed malicious, would send an
111
+ * operator to raise the cap and ship the same hole.
112
+ *
113
+ * On `allow` or `require_human_approval` the action's value and its slot in
114
+ * the rate window are held immediately; call `confirm()` when it lands or
115
+ * `release()` when it does not. The hold is taken synchronously in the same
116
+ * turn as the evaluation, so two concurrent calls in one process cannot both
117
+ * pass a cap that only one of them fits under. Across processes they can —
118
+ * see `ledger.ts`, which says so plainly.
119
+ */
120
+ assessAgentTransaction(action: AgentAction, options?: AssessAgentTransactionOptions): Promise<AgentDecision>;
121
+ /**
122
+ * Decide without going to the network.
123
+ *
124
+ * For an operator replaying a decision against a different policy, and for
125
+ * an agent runtime that already holds an assessment. It applies the same
126
+ * rules; what it cannot do is discover that a counterparty was labelled
127
+ * since the assessment was taken.
128
+ */
129
+ evaluate(action: AgentAction, options?: {
130
+ assessment?: Assessment;
131
+ assessmentUnavailable?: string;
132
+ restrict?: AgentPolicy;
133
+ now?: Date;
134
+ }): AgentDecision;
135
+ /** The agent signed and broadcast it. The held value becomes committed. */
136
+ confirm(decisionId: string, options?: {
137
+ txHash?: string;
138
+ now?: Date;
139
+ }): AgentOutcomeRecord | null;
140
+ /** The agent did not proceed. The held value returns to the budget. */
141
+ release(decisionId: string, options?: {
142
+ note?: string;
143
+ now?: Date;
144
+ }): AgentOutcomeRecord | null;
145
+ /**
146
+ * Explain a decision, by id or by value.
147
+ *
148
+ * Returns `null` for an id this process never made or no longer holds —
149
+ * stated rather than reconstructed, because an explanation assembled from a
150
+ * decision that is no longer in hand is a guess wearing a record's clothes.
151
+ */
152
+ explain(decision: AgentDecision | string): AgentActionExplanation | null;
153
+ /**
154
+ * Recent decisions, newest last.
155
+ *
156
+ * In memory and bounded; it does not survive a restart. Use `onDecision` for
157
+ * a trail that does. Saying which of the two this is matters — an operator
158
+ * who believes this is the audit log will discover otherwise at the worst
159
+ * possible moment.
160
+ */
161
+ auditLog(filter?: {
162
+ agentId?: string;
163
+ verdict?: AgentDecision['verdict'];
164
+ }): AuditEntry[];
165
+ /**
166
+ * A guard for a narrower task, sharing this one's ledger and client.
167
+ *
168
+ * The shared ledger is the point: a sub-task that spends against a tighter
169
+ * per-task cap still spends against the agent's daily one.
170
+ */
171
+ withPolicy(restriction: AgentPolicy): AgentGuard;
172
+ /**
173
+ * Route the action to the right Protect endpoint.
174
+ *
175
+ * An approval is assessed on its spender *and its allowance together*,
176
+ * which is a different question from "is this transaction dangerous" and
177
+ * the one an unlimited approval turns on.
178
+ */
179
+ private screen;
180
+ private record;
181
+ private attachOutcome;
182
+ }
183
+ /**
184
+ * Distinguish "Decentrys answered, and found nothing" from "Decentrys did not
185
+ * answer".
186
+ *
187
+ * Protect never throws: an unreachable service produces an assessment whose
188
+ * `unknowns` say so. Both of those are `NO_CRITICAL_RISK_DETECTED` on the
189
+ * surface, and for an autonomous signer treating the second as the first is
190
+ * the whole failure — it is a clean bill of health issued by an outage.
191
+ */
192
+ export declare function unavailableReason(assessment: Assessment | undefined): string | undefined;
193
+ export {};
194
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ /**
3
+ * AgentGuard — the client an operator wires in front of an agent.
4
+ *
5
+ * The contract differs from both of its neighbours, and the difference is the
6
+ * product.
7
+ *
8
+ * - **Protect never throws and never blocks.** It sits between a person and a
9
+ * signing screen; a security service having a bad minute must not cost
10
+ * someone their transaction, and the person decides.
11
+ * - **Sentinel throws.** It is management tooling, and an operator who
12
+ * believes they are monitored when they are not is worse off than one who
13
+ * saw an error.
14
+ * - **AgentGuard returns a verdict, and the verdict can be `deny`.** Nothing
15
+ * here throws on a call path either — an exception thrown at an agent is a
16
+ * condition it will handle by retrying, or by taking the branch that skips
17
+ * the guard. A decision object it must read is harder to route around.
18
+ *
19
+ * The one place this does throw is the constructor, on a missing credential or
20
+ * an unsealed policy. That is a wiring error made by a human at deploy time,
21
+ * not an event during an agent's run.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.AgentGuard = exports.AGENT_SDK_VERSION = void 0;
25
+ exports.unavailableReason = unavailableReason;
26
+ const protect_1 = require("@decentrys/protect");
27
+ const policy_1 = require("./policy");
28
+ const ledger_1 = require("./ledger");
29
+ const explain_1 = require("./explain");
30
+ exports.AGENT_SDK_VERSION = '0.1.0';
31
+ const DEFAULT_AUDIT_LOG_ENTRIES = 1_000;
32
+ class AgentGuard {
33
+ protect;
34
+ sealed;
35
+ ledgerImpl;
36
+ reserving;
37
+ auditEntries = [];
38
+ auditLimit;
39
+ onDecision;
40
+ constructor(config) {
41
+ if (!config.policy || typeof config.policy !== 'object' || typeof config.policy.fingerprint !== 'string') {
42
+ throw new Error('AgentGuard: a sealed policy is required. Build one with sealPolicy({ ... }) at wiring time, '
43
+ + 'from a human-owned configuration — not from anything the agent produces.');
44
+ }
45
+ if (config.protect) {
46
+ this.protect = config.protect;
47
+ }
48
+ else if (config.apiKey || config.transport) {
49
+ const protectConfig = {
50
+ apiKey: config.apiKey ?? 'transport-provided',
51
+ // Protect's own failMode only shapes the wording of an unavailable
52
+ // assessment; AgentGuard's `failMode` decides what actually happens.
53
+ // Pinning it to `open` keeps the two from being applied twice.
54
+ failMode: 'open',
55
+ ...(config.baseUrl === undefined ? {} : { baseUrl: config.baseUrl }),
56
+ ...(config.timeoutMs === undefined ? {} : { timeoutMs: config.timeoutMs }),
57
+ ...(config.retries === undefined ? {} : { retries: config.retries }),
58
+ ...(config.fetch === undefined ? {} : { fetch: config.fetch }),
59
+ ...(config.transport === undefined ? {} : { transport: config.transport }),
60
+ };
61
+ this.protect = new protect_1.Decentrys(protectConfig);
62
+ }
63
+ else {
64
+ throw new Error('AgentGuard: pass an apiKey or an existing Decentrys client. Screening a counterparty is the '
65
+ + 'part of this that no local policy can do — an allowlist cannot tell you the address on it '
66
+ + 'was labelled malicious yesterday.');
67
+ }
68
+ this.sealed = config.policy;
69
+ this.ledgerImpl = config.ledger ?? new ledger_1.SpendLedger();
70
+ this.reserving = (0, ledger_1.isReservationLedger)(this.ledgerImpl) ? this.ledgerImpl : null;
71
+ this.auditLimit = config.auditLogEntries ?? DEFAULT_AUDIT_LOG_ENTRIES;
72
+ this.onDecision = config.onDecision;
73
+ }
74
+ /** The policy in force. Frozen — reading it is safe, changing it is impossible. */
75
+ get policy() {
76
+ return this.sealed;
77
+ }
78
+ /** Configurations that are legal but worth an operator's attention. */
79
+ get policyWarnings() {
80
+ return this.sealed.warnings;
81
+ }
82
+ /**
83
+ * Whether admitted actions hold budget before they execute.
84
+ *
85
+ * False when the guard was given a read-only usage source. Cumulative caps
86
+ * then count settled history only, so two actions that each fit under the
87
+ * cap can both be admitted and together exceed it. Exposed rather than
88
+ * assumed, because the difference is invisible until it costs something.
89
+ */
90
+ get reservesBudget() {
91
+ return this.reserving !== null;
92
+ }
93
+ // -------------------------------------------------------------------------
94
+ // The main path
95
+ // -------------------------------------------------------------------------
96
+ /**
97
+ * Assess a proposed action and decide whether the agent may sign it.
98
+ *
99
+ * Screening happens first and always — including when a local rule has
100
+ * already failed. A record saying an action exceeded its value cap, while
101
+ * omitting that its counterparty was confirmed malicious, would send an
102
+ * operator to raise the cap and ship the same hole.
103
+ *
104
+ * On `allow` or `require_human_approval` the action's value and its slot in
105
+ * the rate window are held immediately; call `confirm()` when it lands or
106
+ * `release()` when it does not. The hold is taken synchronously in the same
107
+ * turn as the evaluation, so two concurrent calls in one process cannot both
108
+ * pass a cap that only one of them fits under. Across processes they can —
109
+ * see `ledger.ts`, which says so plainly.
110
+ */
111
+ async assessAgentTransaction(action, options = {}) {
112
+ const policy = options.restrict ? (0, policy_1.narrowPolicy)(this.sealed, options.restrict) : this.sealed;
113
+ const decisionId = (0, policy_1.newDecisionId)();
114
+ let assessment = options.assessment;
115
+ if (!assessment) {
116
+ assessment = await this.screen(action, options);
117
+ }
118
+ const unavailable = unavailableReason(assessment);
119
+ // Everything from here down is synchronous. That is deliberate: an `await`
120
+ // between the evaluation and the reservation is the gap through which an
121
+ // agent's parallel calls each see the same untouched budget.
122
+ const decision = (0, policy_1.evaluatePolicy)({
123
+ action,
124
+ policy,
125
+ assessment: unavailable ? null : assessment,
126
+ ...(unavailable === undefined ? {} : { assessmentUnavailable: unavailable }),
127
+ usage: this.ledgerImpl,
128
+ decisionId,
129
+ ...(options.now === undefined ? {} : { now: options.now }),
130
+ });
131
+ if (decision.verdict !== 'deny' && this.reserving) {
132
+ const reservation = this.reserving.reserve({
133
+ decisionId,
134
+ agentId: action.agentId,
135
+ // Reaching here with no stated value means no value-based rule was
136
+ // configured; if one had been, this would already be a denial.
137
+ valueUsd: action.valueUsd ?? 0,
138
+ ...(action.idempotencyKey === undefined ? {} : { idempotencyKey: action.idempotencyKey }),
139
+ ...(options.now === undefined ? {} : { now: options.now }),
140
+ });
141
+ decision.reservationId = reservation.decisionId;
142
+ }
143
+ this.record(decision);
144
+ return decision;
145
+ }
146
+ /**
147
+ * Decide without going to the network.
148
+ *
149
+ * For an operator replaying a decision against a different policy, and for
150
+ * an agent runtime that already holds an assessment. It applies the same
151
+ * rules; what it cannot do is discover that a counterparty was labelled
152
+ * since the assessment was taken.
153
+ */
154
+ evaluate(action, options = {}) {
155
+ const policy = options.restrict ? (0, policy_1.narrowPolicy)(this.sealed, options.restrict) : this.sealed;
156
+ const decision = (0, policy_1.evaluatePolicy)({
157
+ action,
158
+ policy,
159
+ assessment: options.assessment ?? null,
160
+ ...(options.assessment
161
+ ? {}
162
+ : { assessmentUnavailable: options.assessmentUnavailable ?? 'no assessment was supplied to evaluate()' }),
163
+ usage: this.ledgerImpl,
164
+ ...(options.now === undefined ? {} : { now: options.now }),
165
+ });
166
+ this.record(decision);
167
+ return decision;
168
+ }
169
+ // -------------------------------------------------------------------------
170
+ // Settling
171
+ // -------------------------------------------------------------------------
172
+ /** The agent signed and broadcast it. The held value becomes committed. */
173
+ confirm(decisionId, options = {}) {
174
+ const outcome = this.reserving?.confirm(decisionId, options) ?? null;
175
+ if (outcome)
176
+ this.attachOutcome(outcome);
177
+ return outcome;
178
+ }
179
+ /** The agent did not proceed. The held value returns to the budget. */
180
+ release(decisionId, options = {}) {
181
+ const outcome = this.reserving?.release(decisionId, options) ?? null;
182
+ if (outcome)
183
+ this.attachOutcome(outcome);
184
+ return outcome;
185
+ }
186
+ // -------------------------------------------------------------------------
187
+ // The trail
188
+ // -------------------------------------------------------------------------
189
+ /**
190
+ * Explain a decision, by id or by value.
191
+ *
192
+ * Returns `null` for an id this process never made or no longer holds —
193
+ * stated rather than reconstructed, because an explanation assembled from a
194
+ * decision that is no longer in hand is a guess wearing a record's clothes.
195
+ */
196
+ explain(decision) {
197
+ if (typeof decision !== 'string')
198
+ return (0, explain_1.explainAgentAction)(decision);
199
+ const entry = this.auditEntries.find((e) => e.decision.decisionId === decision);
200
+ return entry ? (0, explain_1.explainAgentAction)(entry.decision) : null;
201
+ }
202
+ /**
203
+ * Recent decisions, newest last.
204
+ *
205
+ * In memory and bounded; it does not survive a restart. Use `onDecision` for
206
+ * a trail that does. Saying which of the two this is matters — an operator
207
+ * who believes this is the audit log will discover otherwise at the worst
208
+ * possible moment.
209
+ */
210
+ auditLog(filter = {}) {
211
+ return this.auditEntries
212
+ .filter((e) => (!filter.agentId || e.decision.agentId === filter.agentId))
213
+ .filter((e) => (!filter.verdict || e.decision.verdict === filter.verdict))
214
+ .map((e) => ({ decision: e.decision, ...(e.outcome ? { outcome: e.outcome } : {}) }));
215
+ }
216
+ /**
217
+ * A guard for a narrower task, sharing this one's ledger and client.
218
+ *
219
+ * The shared ledger is the point: a sub-task that spends against a tighter
220
+ * per-task cap still spends against the agent's daily one.
221
+ */
222
+ withPolicy(restriction) {
223
+ return new AgentGuard({
224
+ policy: (0, policy_1.narrowPolicy)(this.sealed, restriction),
225
+ protect: this.protect,
226
+ ledger: this.ledgerImpl,
227
+ auditLogEntries: this.auditLimit,
228
+ ...(this.onDecision === undefined ? {} : { onDecision: this.onDecision }),
229
+ });
230
+ }
231
+ // -------------------------------------------------------------------------
232
+ // Internals
233
+ // -------------------------------------------------------------------------
234
+ /**
235
+ * Route the action to the right Protect endpoint.
236
+ *
237
+ * An approval is assessed on its spender *and its allowance together*,
238
+ * which is a different question from "is this transaction dangerous" and
239
+ * the one an unlimited approval turns on.
240
+ */
241
+ async screen(action, options) {
242
+ const call = {
243
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
244
+ ...(options.skipCache === undefined ? {} : { skipCache: options.skipCache }),
245
+ };
246
+ if (action.type === 'approve' && action.token && action.to) {
247
+ const result = await this.protect.screenApproval({
248
+ chain: action.chain,
249
+ owner: action.from,
250
+ spender: action.to,
251
+ token: action.token,
252
+ ...(action.approvalAmount === undefined ? {} : { amount: action.approvalAmount }),
253
+ }, call);
254
+ return result.assessment;
255
+ }
256
+ const result = await this.protect.assessTransaction({
257
+ chain: action.chain,
258
+ from: action.from,
259
+ ...(action.to === undefined ? {} : { to: action.to }),
260
+ ...(action.value === undefined ? {} : { value: action.value }),
261
+ ...(action.data === undefined ? {} : { data: action.data }),
262
+ ...(action.raw === undefined ? {} : { raw: action.raw }),
263
+ ...(action.origin === undefined ? {} : { origin: action.origin }),
264
+ }, call);
265
+ return result.assessment;
266
+ }
267
+ record(decision) {
268
+ this.auditEntries.push({ decision });
269
+ while (this.auditEntries.length > this.auditLimit)
270
+ this.auditEntries.shift();
271
+ if (!this.onDecision)
272
+ return;
273
+ try {
274
+ this.onDecision(decision);
275
+ }
276
+ catch {
277
+ // A sink that throws must not turn a deny into an exception the agent
278
+ // handles by retrying, nor an allow into a failure. The decision stands.
279
+ }
280
+ }
281
+ attachOutcome(outcome) {
282
+ const entry = this.auditEntries.find((e) => e.decision.decisionId === outcome.decisionId);
283
+ if (entry)
284
+ entry.outcome = outcome;
285
+ }
286
+ }
287
+ exports.AgentGuard = AgentGuard;
288
+ /**
289
+ * Distinguish "Decentrys answered, and found nothing" from "Decentrys did not
290
+ * answer".
291
+ *
292
+ * Protect never throws: an unreachable service produces an assessment whose
293
+ * `unknowns` say so. Both of those are `NO_CRITICAL_RISK_DETECTED` on the
294
+ * surface, and for an autonomous signer treating the second as the first is
295
+ * the whole failure — it is a clean bill of health issued by an outage.
296
+ */
297
+ function unavailableReason(assessment) {
298
+ if (!assessment)
299
+ return 'no assessment was obtained';
300
+ const unknown = assessment.unknowns.find((u) => u.reason === 'PROVIDER_UNAVAILABLE');
301
+ return unknown ? unknown.statement : undefined;
302
+ }
303
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Turning a decision into something a person can be held to.
3
+ *
4
+ * The question this file exists to answer is not "why was that blocked" — a
5
+ * block explains itself, loudly, at the moment it happens. It is **"why was
6
+ * that allowed"**, asked weeks later by someone looking at a drained account.
7
+ * A record that lists only what went wrong cannot answer it, so
8
+ * `explainAgentAction` renders every rule that was applied, the bound it
9
+ * carried and the value it saw, alongside the axes on which the policy set no
10
+ * bound at all. An allow with nothing behind it reads, correctly, as an allow
11
+ * with nothing behind it.
12
+ *
13
+ * Nothing here recomputes anything. The explanation is a rendering of the
14
+ * decision record and cannot disagree with it — an explanation that is
15
+ * generated separately from the decision eventually explains a decision that
16
+ * was not made.
17
+ */
18
+ import type { AgentDecision, AgentVerdict, RuleEvaluation } from './model';
19
+ export interface AgentActionExplanation {
20
+ decisionId: string;
21
+ agentId: string;
22
+ verdict: AgentVerdict;
23
+ /** One sentence. What happened and, at the top level, why. */
24
+ headline: string;
25
+ /** The failing or escalating rules, in the words they recorded. */
26
+ because: string[];
27
+ /**
28
+ * Every rule that ran, passes included. This is the part that makes an
29
+ * allow explainable rather than merely unobjected-to.
30
+ */
31
+ rulesApplied: RuleEvaluation[];
32
+ /**
33
+ * Axes this policy left unbounded.
34
+ *
35
+ * An operator reading an allow deserves to know the difference between "the
36
+ * rate ceiling was checked and this was the third action this minute" and
37
+ * "there is no rate ceiling". Both produce a pass in the eyes of a summary;
38
+ * only one of them is a control.
39
+ */
40
+ unbounded: string[];
41
+ /**
42
+ * What would have to change for this to be permitted.
43
+ *
44
+ * Addressed to the operator, who is the only party that can change a sealed
45
+ * policy. The agent can read this and still cannot act on it, which is the
46
+ * point of `sealPolicy`.
47
+ */
48
+ toProceed: string[];
49
+ /** Enough to reproduce the decision against the same inputs. */
50
+ provenance: {
51
+ policyFingerprint: string;
52
+ policyVersion?: string;
53
+ actionDigest: string;
54
+ modelVersion: string;
55
+ riskModelVersion?: string;
56
+ riskLevel?: string;
57
+ decidedAt: string;
58
+ };
59
+ /** A plain-text rendering, for a log line, a ticket or a human review queue. */
60
+ text: string;
61
+ }
62
+ export declare function explainAgentAction(decision: AgentDecision): AgentActionExplanation;
63
+ //# sourceMappingURL=explain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAe,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAExF,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gEAAgE;IAChE,UAAU,EAAE;QACV,iBAAiB,EAAE,MAAM,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CACd;AA8BD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,sBAAsB,CAyClF"}