@edictum/core 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,1201 @@
1
+ /** Exception classes for Edictum. */
2
+ /** Raised when guard.run() denies a tool call in enforce mode. */
3
+ declare class EdictumDenied extends Error {
4
+ readonly reason: string;
5
+ readonly decisionSource: string | null;
6
+ readonly decisionName: string | null;
7
+ constructor(reason: string, decisionSource?: string | null, decisionName?: string | null);
8
+ }
9
+ /** Raised for configuration/load-time errors (invalid YAML, schema failures, etc.). */
10
+ declare class EdictumConfigError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ /** Raised when the governed tool itself fails. */
14
+ declare class EdictumToolError extends Error {
15
+ constructor(message: string);
16
+ }
17
+
18
+ /** Tool Invocation Envelope — immutable snapshot of a tool call. */
19
+ /**
20
+ * Classification of tool side effects.
21
+ *
22
+ * Determines postcondition behavior and retry safety.
23
+ *
24
+ * DEFAULTS:
25
+ * - Unregistered tools -> IRREVERSIBLE (conservative)
26
+ * - Bash -> IRREVERSIBLE unless strict allowlist match
27
+ * - Classification errors always err toward MORE restrictive
28
+ */
29
+ declare const SideEffect: {
30
+ readonly PURE: "pure";
31
+ readonly READ: "read";
32
+ readonly WRITE: "write";
33
+ readonly IRREVERSIBLE: "irreversible";
34
+ };
35
+ type SideEffect = (typeof SideEffect)[keyof typeof SideEffect];
36
+ /**
37
+ * Identity context for audit attribution.
38
+ *
39
+ * NOTE: `claims` is a plain object. The Principal itself is frozen via
40
+ * `Object.freeze()`, making the reference immutable. Callers should treat
41
+ * claims as read-only after construction.
42
+ */
43
+ interface Principal {
44
+ readonly userId: string | null;
45
+ readonly serviceId: string | null;
46
+ readonly orgId: string | null;
47
+ readonly role: string | null;
48
+ readonly ticketRef: string | null;
49
+ readonly claims: Readonly<Record<string, unknown>>;
50
+ }
51
+ /** Create a frozen Principal with defaults for omitted fields. */
52
+ declare function createPrincipal(partial?: Partial<Principal>): Readonly<Principal>;
53
+ /**
54
+ * Validate tool_name: reject empty, control chars, path separators.
55
+ *
56
+ * Throws EdictumConfigError for:
57
+ * - Empty string
58
+ * - Any ASCII control character (code < 0x20 or code === 0x7f)
59
+ * - Forward slash `/`
60
+ * - Backslash `\`
61
+ */
62
+ declare function _validateToolName(toolName: string): void;
63
+ /**
64
+ * Immutable snapshot of a tool invocation.
65
+ *
66
+ * Prefer `createEnvelope()` factory for deep-copy guarantees.
67
+ * Direct construction validates tool_name but does NOT deep-copy args.
68
+ */
69
+ interface ToolEnvelope {
70
+ readonly toolName: string;
71
+ readonly args: Readonly<Record<string, unknown>>;
72
+ readonly callId: string;
73
+ readonly runId: string;
74
+ readonly callIndex: number;
75
+ readonly parentCallId: string | null;
76
+ readonly sideEffect: SideEffect;
77
+ readonly idempotent: boolean;
78
+ readonly environment: string;
79
+ readonly timestamp: Date;
80
+ readonly caller: string;
81
+ readonly toolUseId: string | null;
82
+ readonly principal: Readonly<Principal> | null;
83
+ readonly bashCommand: string | null;
84
+ readonly filePath: string | null;
85
+ readonly metadata: Readonly<Record<string, unknown>>;
86
+ }
87
+ /**
88
+ * Recursively freeze an object and all nested objects.
89
+ *
90
+ * Date objects are skipped — Object.freeze() cannot prevent mutation
91
+ * via Date prototype methods (setFullYear, setTime, etc.) because Date
92
+ * stores state in internal slots, not own properties.
93
+ */
94
+ declare function deepFreeze<T>(obj: T): T;
95
+ /** Maps tool names to governance properties. Unregistered tools default to IRREVERSIBLE. */
96
+ declare class ToolRegistry {
97
+ private readonly _tools;
98
+ register(name: string, sideEffect?: SideEffect, idempotent?: boolean): void;
99
+ classify(toolName: string, _args: Record<string, unknown>): [SideEffect, boolean];
100
+ }
101
+ /**
102
+ * Classify bash commands by side-effect level.
103
+ *
104
+ * Default is IRREVERSIBLE. Only downgraded to READ via strict
105
+ * allowlist AND absence of shell operators.
106
+ *
107
+ * This is a heuristic, not a security boundary.
108
+ */
109
+ declare const BashClassifier: {
110
+ readonly READ_ALLOWLIST: readonly ["ls", "cat", "head", "tail", "wc", "find", "grep", "rg", "git status", "git log", "git diff", "git show", "git branch", "git remote", "git tag", "echo", "pwd", "whoami", "date", "which", "file", "stat", "du", "df", "tree", "less", "more"];
111
+ readonly SHELL_OPERATORS: readonly ["\n", "\r", "<(", "<<", "$", "${", ">", ">>", "|", ";", "&&", "||", "$(", "`", "#{"];
112
+ readonly classify: (command: string) => SideEffect;
113
+ };
114
+ /** Options for `createEnvelope()` beyond the required positional args. */
115
+ interface CreateEnvelopeOptions {
116
+ readonly runId?: string;
117
+ readonly callIndex?: number;
118
+ readonly callId?: string;
119
+ readonly parentCallId?: string | null;
120
+ readonly sideEffect?: SideEffect;
121
+ readonly idempotent?: boolean;
122
+ readonly environment?: string;
123
+ readonly timestamp?: Date;
124
+ readonly caller?: string;
125
+ readonly toolUseId?: string | null;
126
+ readonly principal?: Principal | null;
127
+ readonly metadata?: Record<string, unknown>;
128
+ readonly registry?: ToolRegistry | null;
129
+ }
130
+ /**
131
+ * Factory that enforces immutability guarantees.
132
+ *
133
+ * Prefer this factory over direct construction — it deep-copies args
134
+ * and metadata to ensure the envelope is a true immutable snapshot.
135
+ */
136
+ declare function createEnvelope(toolName: string, toolInput: Record<string, unknown>, options?: CreateEnvelopeOptions): Readonly<ToolEnvelope>;
137
+
138
+ /** StorageBackend interface + MemoryBackend implementation. */
139
+ /**
140
+ * Protocol for persistent state storage.
141
+ *
142
+ * Requirements:
143
+ * - increment() MUST be atomic
144
+ * - get/set for simple key-value
145
+ *
146
+ * v0.1.0: No append() method (counters only, no list ops).
147
+ */
148
+ interface StorageBackend {
149
+ get(key: string): Promise<string | null>;
150
+ set(key: string, value: string): Promise<void>;
151
+ delete(key: string): Promise<void>;
152
+ increment(key: string, amount?: number): Promise<number>;
153
+ }
154
+ /**
155
+ * In-memory storage for development and testing.
156
+ *
157
+ * WARNING: State lost on restart. Session contracts reset.
158
+ * Suitable for: local dev, tests, single-process scripts.
159
+ *
160
+ * Node.js is single-threaded — Map operations are atomic.
161
+ * No lock needed (unlike Python's asyncio.Lock).
162
+ */
163
+ declare class MemoryBackend implements StorageBackend {
164
+ private readonly _data;
165
+ private readonly _counters;
166
+ get(key: string): Promise<string | null>;
167
+ set(key: string, value: string): Promise<void>;
168
+ delete(key: string): Promise<void>;
169
+ increment(key: string, amount?: number): Promise<number>;
170
+ /**
171
+ * Retrieve multiple values in a single operation.
172
+ *
173
+ * In-memory implementation: multiple Map lookups, no network overhead.
174
+ */
175
+ batchGet(keys: readonly string[]): Promise<Record<string, string | null>>;
176
+ }
177
+
178
+ /** Session -- atomic counters backed by StorageBackend. */
179
+
180
+ /**
181
+ * Tracks execution state via atomic counters in StorageBackend.
182
+ *
183
+ * All methods are ASYNC because StorageBackend is async.
184
+ *
185
+ * Counter semantics:
186
+ * - attempts: every PreToolUse, including denied (pre-execution)
187
+ * - execs: every PostToolUse (tool actually ran)
188
+ * - tool:{name}: per-tool execution count
189
+ * - consec_fail: resets on success, increments on failure
190
+ */
191
+ declare class Session {
192
+ private readonly _sid;
193
+ private readonly _backend;
194
+ constructor(sessionId: string, backend: StorageBackend);
195
+ get sessionId(): string;
196
+ /** Increment attempt counter. Called in PreToolUse (before governance). */
197
+ incrementAttempts(): Promise<number>;
198
+ attemptCount(): Promise<number>;
199
+ /** Record a tool execution. Called in PostToolUse. */
200
+ recordExecution(toolName: string, success: boolean): Promise<void>;
201
+ executionCount(): Promise<number>;
202
+ toolExecutionCount(tool: string): Promise<number>;
203
+ consecutiveFailures(): Promise<number>;
204
+ /**
205
+ * Pre-fetch multiple session counters in a single backend call.
206
+ *
207
+ * Returns a record with keys: "attempts", "execs", and optionally
208
+ * "tool:{name}" if includeTool is provided.
209
+ *
210
+ * Uses batchGet() on the backend when available (single HTTP round
211
+ * trip for ServerBackend). Falls back to individual get() calls for
212
+ * backends without batchGet support.
213
+ */
214
+ batchGetCounters(options?: {
215
+ includeTool?: string;
216
+ }): Promise<Record<string, number>>;
217
+ }
218
+
219
+ /** Pre/Post Conditions — contract types for tool governance. */
220
+
221
+ /** Outcome of a single contract check. */
222
+ interface Verdict {
223
+ readonly passed: boolean;
224
+ readonly message: string | null;
225
+ readonly metadata: Readonly<Record<string, unknown>>;
226
+ }
227
+ /** Factory methods for Verdict. */
228
+ declare const Verdict: {
229
+ /**
230
+ * Contract passed — tool call is acceptable.
231
+ */
232
+ pass_(): Verdict;
233
+ /**
234
+ * Contract failed with an actionable message (truncated to 500 chars).
235
+ *
236
+ * Make it SPECIFIC and INSTRUCTIVE — the agent uses it to self-correct.
237
+ */
238
+ fail(message: string, metadata?: Record<string, unknown>): Verdict;
239
+ };
240
+ /** Before execution. Safe to deny — tool hasn't run yet. */
241
+ interface Precondition {
242
+ readonly contractType?: "pre";
243
+ readonly tool: string;
244
+ readonly check: (envelope: ToolEnvelope) => Verdict | Promise<Verdict>;
245
+ readonly when?: ((envelope: ToolEnvelope) => boolean) | null;
246
+ }
247
+ /**
248
+ * After execution. Observe-and-log.
249
+ *
250
+ * On failure for pure/read: inject context suggesting retry.
251
+ * On failure for write/irreversible: warn only, NO retry coaching.
252
+ */
253
+ interface Postcondition {
254
+ readonly contractType: "post";
255
+ readonly tool: string;
256
+ readonly check: (envelope: ToolEnvelope, response: unknown) => Verdict | Promise<Verdict>;
257
+ readonly when?: ((envelope: ToolEnvelope) => boolean) | null;
258
+ }
259
+ /**
260
+ * Cross-turn governance using persisted atomic counters.
261
+ *
262
+ * Session methods are ASYNC. Session contract checks must be async.
263
+ *
264
+ * Example:
265
+ * ```typescript
266
+ * const maxOperations: SessionContract = {
267
+ * check: async (session) => {
268
+ * const count = await session.executionCount();
269
+ * if (count >= 200) {
270
+ * return Verdict.fail("Session limit reached.");
271
+ * }
272
+ * return Verdict.pass_();
273
+ * },
274
+ * };
275
+ * ```
276
+ */
277
+ interface SessionContract {
278
+ readonly check: (session: Session) => Verdict | Promise<Verdict>;
279
+ }
280
+
281
+ /** Hook interception — before/after tool execution. */
282
+ declare const HookResult: {
283
+ readonly ALLOW: "allow";
284
+ readonly DENY: "deny";
285
+ };
286
+ type HookResult = (typeof HookResult)[keyof typeof HookResult];
287
+ interface HookDecision {
288
+ readonly result: HookResult;
289
+ readonly reason: string | null;
290
+ }
291
+ declare const HookDecision: {
292
+ allow(): HookDecision;
293
+ deny(reason: string): HookDecision;
294
+ };
295
+
296
+ /** Operation Limits — tool call and attempt caps. */
297
+ /**
298
+ * Operation limits for an agent session.
299
+ *
300
+ * Two counter types:
301
+ * - maxAttempts: caps ALL PreToolUse events (including denied)
302
+ * - maxToolCalls: caps EXECUTIONS only (PostToolUse)
303
+ *
304
+ * Both are checked. Whichever fires first wins.
305
+ */
306
+ interface OperationLimits {
307
+ readonly maxAttempts: number;
308
+ readonly maxToolCalls: number;
309
+ readonly maxCallsPerTool: Readonly<Record<string, number>>;
310
+ }
311
+ declare const DEFAULT_LIMITS: OperationLimits;
312
+
313
+ /** Shared types for Edictum internals. */
314
+ type AnyFunction = (...args: any[]) => any;
315
+ /** Registration for a hook callback. */
316
+ interface HookRegistration {
317
+ readonly phase: "before" | "after";
318
+ readonly tool: string;
319
+ readonly callback: AnyFunction;
320
+ readonly when?: AnyFunction | null;
321
+ }
322
+ /** Internal tool configuration. */
323
+ interface ToolConfig {
324
+ readonly name: string;
325
+ readonly sideEffect: string;
326
+ readonly idempotent: boolean;
327
+ }
328
+
329
+ /** Approval protocol for human-in-the-loop tool call authorization. */
330
+ declare const ApprovalStatus: {
331
+ readonly PENDING: "pending";
332
+ readonly APPROVED: "approved";
333
+ readonly DENIED: "denied";
334
+ readonly TIMEOUT: "timeout";
335
+ };
336
+ type ApprovalStatus = (typeof ApprovalStatus)[keyof typeof ApprovalStatus];
337
+ /** A request for human approval of a tool call. */
338
+ interface ApprovalRequest {
339
+ readonly approvalId: string;
340
+ readonly toolName: string;
341
+ readonly toolArgs: Readonly<Record<string, unknown>>;
342
+ readonly message: string;
343
+ readonly timeout: number;
344
+ readonly timeoutEffect: string;
345
+ readonly principal: Readonly<Record<string, unknown>> | null;
346
+ readonly metadata: Readonly<Record<string, unknown>>;
347
+ readonly createdAt: Date;
348
+ }
349
+ /** The result of a human approval decision. */
350
+ interface ApprovalDecision {
351
+ readonly approved: boolean;
352
+ readonly approver: string | null;
353
+ readonly reason: string | null;
354
+ readonly status: ApprovalStatus;
355
+ readonly timestamp: Date;
356
+ }
357
+ /** Protocol for human-in-the-loop approval providers. */
358
+ interface ApprovalBackend {
359
+ requestApproval(toolName: string, toolArgs: Record<string, unknown>, message: string, options?: {
360
+ timeout?: number;
361
+ timeoutEffect?: string;
362
+ principal?: Record<string, unknown> | null;
363
+ metadata?: Record<string, unknown> | null;
364
+ }): Promise<ApprovalRequest>;
365
+ waitForDecision(approvalId: string, timeout?: number | null): Promise<ApprovalDecision>;
366
+ }
367
+ /**
368
+ * CLI-based approval backend for local testing.
369
+ *
370
+ * Prompts on stdout, reads from stdin. Blocks until response or timeout.
371
+ */
372
+ declare class LocalApprovalBackend implements ApprovalBackend {
373
+ private readonly _pending;
374
+ requestApproval(toolName: string, toolArgs: Record<string, unknown>, message: string, options?: {
375
+ timeout?: number;
376
+ timeoutEffect?: string;
377
+ principal?: Record<string, unknown> | null;
378
+ metadata?: Record<string, unknown> | null;
379
+ }): Promise<ApprovalRequest>;
380
+ waitForDecision(approvalId: string, timeout?: number | null): Promise<ApprovalDecision>;
381
+ /** Read a single line from stdin with a timeout. */
382
+ private _readStdin;
383
+ }
384
+
385
+ /** Redaction policy for sensitive data in audit events. */
386
+ /**
387
+ * Redact sensitive data from audit events.
388
+ *
389
+ * Recurses into dicts AND lists. Normalizes keys to lowercase.
390
+ * Caps total payload size. Detects common secret patterns in values.
391
+ */
392
+ declare class RedactionPolicy {
393
+ static readonly DEFAULT_SENSITIVE_KEYS: ReadonlySet<string>;
394
+ static readonly BASH_REDACTION_PATTERNS: ReadonlyArray<readonly [string, string]>;
395
+ static readonly SECRET_VALUE_PATTERNS: ReadonlyArray<string>;
396
+ static readonly MAX_PAYLOAD_SIZE = 32768;
397
+ static readonly MAX_REGEX_INPUT = 10000;
398
+ static readonly MAX_PATTERN_LENGTH = 10000;
399
+ private readonly _keys;
400
+ private readonly _patterns;
401
+ private readonly _compiledPatterns;
402
+ private readonly _compiledSecretPatterns;
403
+ private readonly _detectValues;
404
+ constructor(sensitiveKeys?: ReadonlySet<string> | null, customPatterns?: ReadonlyArray<readonly [string, string]> | null, detectSecretValues?: boolean);
405
+ /** Recursively redact sensitive data from tool arguments. */
406
+ redactArgs(args: unknown): unknown;
407
+ /** Check if a key name indicates sensitive data. */
408
+ _isSensitiveKey(key: string): boolean;
409
+ /** Check if a string value looks like a known secret format. */
410
+ _looksLikeSecret(value: string): boolean;
411
+ /** Apply redaction patterns to a bash command string. */
412
+ redactBashCommand(command: string): string;
413
+ /** Apply redaction patterns and truncate a result string. */
414
+ redactResult(result: string, maxLength?: number): string;
415
+ /** Cap total serialized size of audit payload. Returns a new object if truncated. */
416
+ capPayload(data: Record<string, unknown>): Record<string, unknown>;
417
+ }
418
+
419
+ /** Structured Event Log with Redaction. */
420
+
421
+ declare const AuditAction: {
422
+ readonly CALL_DENIED: "call_denied";
423
+ readonly CALL_WOULD_DENY: "call_would_deny";
424
+ readonly CALL_ALLOWED: "call_allowed";
425
+ readonly CALL_EXECUTED: "call_executed";
426
+ readonly CALL_FAILED: "call_failed";
427
+ readonly POSTCONDITION_WARNING: "postcondition_warning";
428
+ readonly CALL_APPROVAL_REQUESTED: "call_approval_requested";
429
+ readonly CALL_APPROVAL_GRANTED: "call_approval_granted";
430
+ readonly CALL_APPROVAL_DENIED: "call_approval_denied";
431
+ readonly CALL_APPROVAL_TIMEOUT: "call_approval_timeout";
432
+ };
433
+ type AuditAction = (typeof AuditAction)[keyof typeof AuditAction];
434
+ interface AuditEvent {
435
+ schemaVersion: string;
436
+ timestamp: Date;
437
+ runId: string;
438
+ callId: string;
439
+ callIndex: number;
440
+ parentCallId: string | null;
441
+ toolName: string;
442
+ toolArgs: Record<string, unknown>;
443
+ sideEffect: string;
444
+ environment: string;
445
+ principal: Record<string, unknown> | null;
446
+ action: AuditAction;
447
+ decisionSource: string | null;
448
+ decisionName: string | null;
449
+ reason: string | null;
450
+ hooksEvaluated: Record<string, unknown>[];
451
+ contractsEvaluated: Record<string, unknown>[];
452
+ toolSuccess: boolean | null;
453
+ postconditionsPassed: boolean | null;
454
+ durationMs: number;
455
+ error: string | null;
456
+ resultSummary: string | null;
457
+ sessionAttemptCount: number;
458
+ sessionExecutionCount: number;
459
+ mode: string;
460
+ policyVersion: string | null;
461
+ policyError: boolean;
462
+ }
463
+ /** Factory with defaults matching the Python dataclass. */
464
+ declare function createAuditEvent(f?: Partial<AuditEvent>): AuditEvent;
465
+ interface AuditSink {
466
+ emit(event: AuditEvent): Promise<void>;
467
+ }
468
+ /** Raised when a mark references events evicted from the buffer. */
469
+ declare class MarkEvictedError extends Error {
470
+ constructor(message: string);
471
+ }
472
+ /** Fan-out sink: emits to all sinks, raises AggregateError on failures. */
473
+ declare class CompositeSink implements AuditSink {
474
+ private readonly _sinks;
475
+ constructor(sinks: AuditSink[]);
476
+ get sinks(): AuditSink[];
477
+ emit(event: AuditEvent): Promise<void>;
478
+ }
479
+ declare class StdoutAuditSink implements AuditSink {
480
+ private readonly _redaction;
481
+ constructor(redaction?: RedactionPolicy | null);
482
+ emit(event: AuditEvent): Promise<void>;
483
+ }
484
+ declare class FileAuditSink implements AuditSink {
485
+ private readonly _path;
486
+ private readonly _redaction;
487
+ constructor(path: string, redaction?: RedactionPolicy | null);
488
+ emit(event: AuditEvent): Promise<void>;
489
+ }
490
+ /** In-memory ring buffer sink for programmatic inspection. */
491
+ declare class CollectingAuditSink implements AuditSink {
492
+ private _events;
493
+ private readonly _maxEvents;
494
+ private _totalEmitted;
495
+ constructor(maxEvents?: number);
496
+ emit(event: AuditEvent): Promise<void>;
497
+ get events(): AuditEvent[];
498
+ mark(): number;
499
+ sinceMark(m: number): AuditEvent[];
500
+ last(): AuditEvent;
501
+ filter(action: AuditAction): AuditEvent[];
502
+ clear(): void;
503
+ }
504
+
505
+ /** Evaluation result types for dry-run contract evaluation. */
506
+ /** Result of evaluating a single contract. */
507
+ interface ContractResult {
508
+ readonly contractId: string;
509
+ readonly contractType: string;
510
+ readonly passed: boolean;
511
+ readonly message: string | null;
512
+ readonly tags: readonly string[];
513
+ readonly observed: boolean;
514
+ readonly effect: string;
515
+ readonly policyError: boolean;
516
+ }
517
+ /** Create a frozen ContractResult with defaults matching the Python dataclass. */
518
+ declare function createContractResult(fields: Pick<ContractResult, "contractId" | "contractType" | "passed"> & Partial<Omit<ContractResult, "contractId" | "contractType" | "passed">>): ContractResult;
519
+ /** Result of dry-run evaluation of a tool call against contracts. */
520
+ interface EvaluationResult {
521
+ readonly verdict: string;
522
+ readonly toolName: string;
523
+ readonly contracts: readonly ContractResult[];
524
+ readonly denyReasons: readonly string[];
525
+ readonly warnReasons: readonly string[];
526
+ readonly contractsEvaluated: number;
527
+ readonly policyError: boolean;
528
+ }
529
+ /** Create a frozen EvaluationResult with defaults matching the Python dataclass. */
530
+ declare function createEvaluationResult(fields: Pick<EvaluationResult, "verdict" | "toolName"> & Partial<Omit<EvaluationResult, "verdict" | "toolName">>): EvaluationResult;
531
+
532
+ /** Structured postcondition findings. */
533
+ /**
534
+ * A structured finding from a postcondition evaluation.
535
+ *
536
+ * Produced when a postcondition warns or detects an issue.
537
+ * Returned to the caller via PostCallResult so they can
538
+ * decide how to remediate.
539
+ */
540
+ interface Finding {
541
+ readonly type: string;
542
+ readonly contractId: string;
543
+ readonly field: string;
544
+ readonly message: string;
545
+ readonly metadata: Readonly<Record<string, unknown>>;
546
+ }
547
+ /** Create a frozen Finding with defaults for metadata. */
548
+ declare function createFinding(fields: Pick<Finding, "type" | "contractId" | "field" | "message"> & Partial<Pick<Finding, "metadata">>): Finding;
549
+ /**
550
+ * Result from a governed tool call, including postcondition findings.
551
+ *
552
+ * Returned by adapter's postToolCall and available via asToolWrapper.
553
+ *
554
+ * When postconditionsPassed is false, the findings list contains
555
+ * structured Finding objects describing what was detected. The caller
556
+ * can then decide how to remediate (redact, replace, log, etc.).
557
+ */
558
+ interface PostCallResult {
559
+ readonly result: unknown;
560
+ readonly postconditionsPassed: boolean;
561
+ readonly findings: readonly Finding[];
562
+ readonly outputSuppressed: boolean;
563
+ }
564
+ /** Create a PostCallResult with defaults. */
565
+ declare function createPostCallResult(fields: Pick<PostCallResult, "result"> & Partial<Omit<PostCallResult, "result">>): PostCallResult;
566
+ /**
567
+ * Classify a postcondition finding type from contract ID and message.
568
+ *
569
+ * Returns a standard finding type string.
570
+ */
571
+ declare function classifyFinding(contractId: string, verdictMessage: string): string;
572
+ /**
573
+ * Structural type for the PostDecision fields consumed by buildFindings.
574
+ *
575
+ * The full PostDecision lives in pipeline.ts. This captures only the
576
+ * subset needed here to avoid a circular import.
577
+ */
578
+ interface PostDecisionLike {
579
+ readonly contractsEvaluated: ReadonlyArray<{
580
+ readonly passed?: boolean;
581
+ readonly name: string;
582
+ readonly message?: string;
583
+ readonly metadata?: Record<string, unknown>;
584
+ }>;
585
+ }
586
+ /**
587
+ * Build Finding objects from a PostDecision's failed postconditions.
588
+ *
589
+ * The `field` value is extracted from `metadata.field` if the
590
+ * contract provides it (e.g. `Verdict.fail("msg", { field: "output.text" })`),
591
+ * otherwise defaults to `"output"` for postconditions.
592
+ */
593
+ declare function buildFindings(postDecision: PostDecisionLike): Finding[];
594
+
595
+ /**
596
+ * Internal contract representations used by GuardLike and GovernancePipeline.
597
+ *
598
+ * User-facing types (Precondition, Postcondition, SessionContract) are plain
599
+ * objects optimized for DX. These internal types carry the metadata the
600
+ * pipeline needs (name, mode, source, effect, timeout) that Python stores
601
+ * as _edictum_* function attributes.
602
+ *
603
+ * The Guard class converts user contracts → internal contracts at construction.
604
+ * The YAML compiler produces internal contracts directly.
605
+ */
606
+
607
+ interface InternalContractBase {
608
+ readonly name: string;
609
+ readonly mode?: "enforce" | "observe";
610
+ readonly source?: string;
611
+ }
612
+ /** Internal precondition — enriched with pipeline metadata. */
613
+ interface InternalPrecondition extends InternalContractBase {
614
+ readonly type: "precondition";
615
+ readonly tool: string;
616
+ readonly check: (envelope: ToolEnvelope) => Verdict | Promise<Verdict>;
617
+ readonly when?: ((envelope: ToolEnvelope) => boolean) | null;
618
+ readonly effect?: "deny" | "approve";
619
+ readonly timeout?: number;
620
+ readonly timeoutEffect?: "deny" | "allow";
621
+ }
622
+ /** Internal postcondition — enriched with effect and redaction info. */
623
+ interface InternalPostcondition extends InternalContractBase {
624
+ readonly type: "postcondition";
625
+ readonly tool: string;
626
+ readonly check: (envelope: ToolEnvelope, response: unknown) => Verdict | Promise<Verdict>;
627
+ readonly when?: ((envelope: ToolEnvelope) => boolean) | null;
628
+ readonly effect?: "warn" | "redact" | "deny";
629
+ readonly redactPatterns?: readonly RegExp[];
630
+ }
631
+ /** Internal session contract. */
632
+ interface InternalSessionContract extends InternalContractBase {
633
+ readonly type: "session_contract";
634
+ readonly check: (session: Session) => Verdict | Promise<Verdict>;
635
+ }
636
+ /** Internal sandbox contract — tool matching uses tools[] not tool. */
637
+ interface InternalSandboxContract extends InternalContractBase {
638
+ readonly type: "sandbox";
639
+ readonly tools: readonly string[];
640
+ readonly check: (envelope: ToolEnvelope) => Verdict | Promise<Verdict>;
641
+ readonly effect?: "deny" | "approve";
642
+ readonly timeout?: number;
643
+ readonly timeoutEffect?: "deny" | "allow";
644
+ }
645
+ /** Union of all internal contract types. */
646
+ type InternalContract = InternalPrecondition | InternalPostcondition | InternalSessionContract | InternalSandboxContract;
647
+ /**
648
+ * Interface representing what the GovernancePipeline needs from the Guard.
649
+ *
650
+ * Decouples pipeline from concrete Guard class for testability.
651
+ * The real Edictum class implements this.
652
+ */
653
+ interface GuardLike {
654
+ readonly limits: OperationLimits;
655
+ getHooks(phase: "before" | "after", envelope: ToolEnvelope): HookRegistration[];
656
+ getPreconditions(envelope: ToolEnvelope): InternalPrecondition[];
657
+ getPostconditions(envelope: ToolEnvelope): InternalPostcondition[];
658
+ getSessionContracts(): InternalSessionContract[];
659
+ getSandboxContracts(envelope: ToolEnvelope): InternalSandboxContract[];
660
+ getObservePreconditions(envelope: ToolEnvelope): InternalPrecondition[];
661
+ getObservePostconditions(envelope: ToolEnvelope): InternalPostcondition[];
662
+ getObserveSandboxContracts(envelope: ToolEnvelope): InternalSandboxContract[];
663
+ getObserveSessionContracts(): InternalSessionContract[];
664
+ }
665
+
666
+ /**
667
+ * _CompiledState -- frozen snapshot of compiled contracts.
668
+ *
669
+ * All contract lists are readonly arrays (frozen). The entire state is
670
+ * replaced atomically via a single reference assignment in reload(),
671
+ * ensuring concurrent evaluations never see a mix of old and new
672
+ * contracts.
673
+ */
674
+
675
+ interface CompiledState {
676
+ readonly preconditions: readonly InternalPrecondition[];
677
+ readonly postconditions: readonly InternalPostcondition[];
678
+ readonly sessionContracts: readonly InternalSessionContract[];
679
+ readonly sandboxContracts: readonly InternalSandboxContract[];
680
+ readonly observePreconditions: readonly InternalPrecondition[];
681
+ readonly observePostconditions: readonly InternalPostcondition[];
682
+ readonly observeSessionContracts: readonly InternalSessionContract[];
683
+ readonly observeSandboxContracts: readonly InternalSandboxContract[];
684
+ readonly limits: OperationLimits;
685
+ readonly policyVersion: string | null;
686
+ }
687
+ declare function createCompiledState(partial?: Partial<CompiledState>): CompiledState;
688
+
689
+ /**
690
+ * GovernancePipeline -- single source of governance logic.
691
+ *
692
+ * SIZE APPROVAL: This file exceeds 200 lines. It mirrors Python's pipeline.py
693
+ * (485 LOC). PreDecision + PostDecision types + the 5-stage pre/post engine
694
+ * form a single cohesive evaluation flow that would be harder to follow if split.
695
+ */
696
+
697
+ /** Result of pre-execution governance evaluation. */
698
+ interface PreDecision {
699
+ readonly action: "allow" | "deny" | "pending_approval";
700
+ readonly reason: string | null;
701
+ readonly decisionSource: string | null;
702
+ readonly decisionName: string | null;
703
+ readonly hooksEvaluated: Record<string, unknown>[];
704
+ readonly contractsEvaluated: Record<string, unknown>[];
705
+ readonly observed: boolean;
706
+ readonly policyError: boolean;
707
+ readonly observeResults: Record<string, unknown>[];
708
+ readonly approvalTimeout: number;
709
+ readonly approvalTimeoutEffect: string;
710
+ readonly approvalMessage: string | null;
711
+ }
712
+ /** Create a PreDecision with defaults for omitted fields. */
713
+ declare function createPreDecision(partial: Partial<PreDecision> & Pick<PreDecision, "action">): PreDecision;
714
+ /** Result of post-execution governance evaluation. */
715
+ interface PostDecision {
716
+ readonly toolSuccess: boolean;
717
+ readonly postconditionsPassed: boolean;
718
+ readonly warnings: string[];
719
+ readonly contractsEvaluated: Record<string, unknown>[];
720
+ readonly policyError: boolean;
721
+ readonly redactedResponse: unknown;
722
+ readonly outputSuppressed: boolean;
723
+ }
724
+ /** Create a PostDecision with defaults for omitted fields. */
725
+ declare function createPostDecision(partial: Partial<PostDecision> & Pick<PostDecision, "toolSuccess">): PostDecision;
726
+ /**
727
+ * Orchestrates all governance checks.
728
+ *
729
+ * This is the single source of truth for governance logic.
730
+ * Adapters call preExecute() and postExecute(), then translate
731
+ * the structured results into framework-specific formats.
732
+ */
733
+ declare class GovernancePipeline {
734
+ private readonly _guard;
735
+ constructor(guard: GuardLike);
736
+ preExecute(envelope: ToolEnvelope, session: Session): Promise<PreDecision>;
737
+ postExecute(envelope: ToolEnvelope, toolResponse: unknown, toolSuccess: boolean): Promise<PostDecision>;
738
+ /**
739
+ * Evaluate observe-mode contracts without affecting the real decision.
740
+ *
741
+ * Observe-mode contracts are identified by mode === "observe" on the
742
+ * internal contract. Results are returned as dicts for audit emission
743
+ * but never block calls.
744
+ */
745
+ private _evaluateObserveContracts;
746
+ }
747
+
748
+ /** Bundle Composer — merge multiple parsed YAML bundles into one. */
749
+ /** Records a contract that was replaced during composition. */
750
+ interface CompositionOverride {
751
+ readonly contractId: string;
752
+ readonly overriddenBy: string;
753
+ readonly originalSource: string;
754
+ }
755
+ /** Records a contract added as an observe-mode copy (observe_alongside). */
756
+ interface ObserveContract {
757
+ readonly contractId: string;
758
+ readonly enforcedSource: string;
759
+ readonly observedSource: string;
760
+ }
761
+ /** Report of what happened during composition. */
762
+ interface CompositionReport {
763
+ readonly overriddenContracts: readonly CompositionOverride[];
764
+ readonly observeContracts: readonly ObserveContract[];
765
+ }
766
+ /** Result of composing multiple bundles. */
767
+ interface ComposedBundle {
768
+ readonly bundle: Record<string, unknown>;
769
+ readonly report: CompositionReport;
770
+ }
771
+ /**
772
+ * Merge multiple parsed bundle dicts left to right.
773
+ *
774
+ * Each entry is a tuple of [bundleData, sourceLabel]. Later layers
775
+ * have higher priority.
776
+ */
777
+ declare function composeBundles(...bundles: [Record<string, unknown>, string][]): ComposedBundle;
778
+
779
+ /** Built-in operators for YAML condition evaluation. */
780
+ /** Cap regex input to prevent catastrophic backtracking DoS. */
781
+ declare const MAX_REGEX_INPUT = 10000;
782
+ /** All built-in operator names (including "exists" which is special-cased). */
783
+ declare const BUILTIN_OPERATOR_NAMES: ReadonlySet<string>;
784
+
785
+ /** Selector resolution — map YAML selector paths to ToolEnvelope values. */
786
+
787
+ declare const BUILTIN_SELECTOR_PREFIXES: ReadonlySet<string>;
788
+
789
+ /** Condition Evaluator — resolve selectors and apply operators against ToolEnvelope. */
790
+
791
+ /**
792
+ * Sentinel indicating a type mismatch or evaluation error.
793
+ *
794
+ * Converts to `true` conceptually — errors trigger the contract (fail-closed).
795
+ * Callers should treat PolicyError as "condition matched" and apply
796
+ * deny/warn + policyError flag.
797
+ */
798
+ declare class PolicyError {
799
+ readonly message: string;
800
+ constructor(message: string);
801
+ }
802
+ type CustomOperator = (fieldValue: unknown, opValue: unknown) => boolean;
803
+ type CustomSelector = (envelope: ToolEnvelope) => Record<string, unknown>;
804
+ interface EvaluateOptions$1 {
805
+ readonly customOperators?: Readonly<Record<string, CustomOperator>> | null;
806
+ readonly customSelectors?: Readonly<Record<string, CustomSelector>> | null;
807
+ }
808
+ /**
809
+ * Evaluate a boolean expression tree against an envelope.
810
+ *
811
+ * Returns `true` if the expression matches, `false` if not.
812
+ * Returns a `PolicyError` if a type mismatch or evaluation error occurs
813
+ * (caller should treat as deny/warn + policyError).
814
+ *
815
+ * Missing fields always evaluate to `false` (contract doesn't fire).
816
+ */
817
+ declare function evaluateExpression(expr: Record<string, unknown>, envelope: ToolEnvelope, outputText?: string | null, options?: EvaluateOptions$1): boolean | PolicyError;
818
+
819
+ /**
820
+ * Factory functions for creating Edictum instances from YAML bundles.
821
+ *
822
+ * Ports Python's _factory.py: fromYaml, fromYamlString, reload.
823
+ *
824
+ * SIZE APPROVAL: This file exceeds 200 lines. It mirrors Python's _factory.py
825
+ * (384 LOC). The three factory functions share option types and helper logic
826
+ * that would create unnecessary coupling if split.
827
+ *
828
+ * Guard.ts delegates to these via dynamic import to avoid circular deps.
829
+ */
830
+
831
+ /** Options shared by fromYaml and fromYamlString. */
832
+ interface YamlFactoryOptions {
833
+ readonly mode?: "enforce" | "observe";
834
+ readonly tools?: Record<string, {
835
+ side_effect?: string;
836
+ idempotent?: boolean;
837
+ }>;
838
+ readonly auditSink?: AuditSink | AuditSink[];
839
+ readonly redaction?: RedactionPolicy;
840
+ readonly backend?: StorageBackend;
841
+ readonly environment?: string;
842
+ readonly onDeny?: (envelope: ToolEnvelope, reason: string, source: string | null) => void;
843
+ readonly onAllow?: (envelope: ToolEnvelope) => void;
844
+ readonly customOperators?: Record<string, CustomOperator>;
845
+ readonly customSelectors?: Record<string, CustomSelector>;
846
+ readonly successCheck?: (toolName: string, result: unknown) => boolean;
847
+ readonly principal?: Principal;
848
+ readonly principalResolver?: (toolName: string, toolInput: Record<string, unknown>) => Principal;
849
+ readonly approvalBackend?: ApprovalBackend;
850
+ }
851
+ /** Options for fromYaml, extending base with returnReport. */
852
+ interface FromYamlOptions extends YamlFactoryOptions {
853
+ readonly returnReport?: boolean;
854
+ }
855
+ /**
856
+ * Create an Edictum instance from one or more YAML contract bundle paths.
857
+ *
858
+ * When multiple paths are given, bundles are composed left-to-right
859
+ * (later layers override earlier ones).
860
+ *
861
+ * EXCEPTION TO "ALL ASYNC" RULE: This factory is intentionally synchronous,
862
+ * matching Python's from_yaml(). File I/O uses Node's readFileSync and YAML
863
+ * parsing is CPU-bound — neither benefits from async. The returned Edictum
864
+ * instance's run()/evaluate()/evaluateBatch() methods are fully async.
865
+ * Making this async would force all callers to await at module init time
866
+ * for no concurrency benefit.
867
+ */
868
+ declare function fromYaml(...args: [...string[], FromYamlOptions] | string[]): Edictum | [Edictum, CompositionReport];
869
+ /**
870
+ * Create an Edictum instance from a YAML string or Uint8Array.
871
+ *
872
+ * Like fromYaml but accepts YAML content directly instead of a file path.
873
+ *
874
+ * EXCEPTION TO "ALL ASYNC" RULE: Synchronous by design — see fromYaml docs.
875
+ */
876
+ declare function fromYamlString(content: string | Uint8Array, options?: YamlFactoryOptions): Edictum;
877
+ /** Options for reload(). */
878
+ interface ReloadOptions {
879
+ readonly customOperators?: Record<string, CustomOperator>;
880
+ readonly customSelectors?: Record<string, CustomSelector>;
881
+ }
882
+ /**
883
+ * Atomically replace a guard's contracts from a YAML string.
884
+ *
885
+ * Builds a new CompiledState from the YAML content and swaps the
886
+ * guard's internal state reference. Concurrent evaluations that
887
+ * started before reload() see the old state; evaluations after
888
+ * see the new state.
889
+ */
890
+ declare function reload(guard: Edictum, yamlContent: string, options?: ReloadOptions): void;
891
+
892
+ /**
893
+ * Core Edictum class -- construction, contract registry, and accessor methods.
894
+ *
895
+ * SIZE APPROVAL: This file exceeds 200 lines. It mirrors Python's _guard.py
896
+ * (314 LOC) which is already the decomposed version of the original god class.
897
+ * The contract classification + accessor methods form a cohesive unit.
898
+ *
899
+ * Minimum viable guard: constructor + contract classification + accessors.
900
+ * run(), from_yaml(), from_server() are delegated methods added later.
901
+ */
902
+
903
+ /** Constructor options for the Edictum guard. */
904
+ interface EdictumOptions {
905
+ readonly environment?: string;
906
+ readonly mode?: "enforce" | "observe";
907
+ readonly limits?: OperationLimits;
908
+ readonly tools?: Record<string, {
909
+ side_effect?: string;
910
+ idempotent?: boolean;
911
+ }>;
912
+ readonly contracts?: ReadonlyArray<Precondition | Postcondition | SessionContract>;
913
+ readonly hooks?: ReadonlyArray<HookRegistration>;
914
+ readonly auditSink?: AuditSink | AuditSink[];
915
+ readonly redaction?: RedactionPolicy;
916
+ readonly backend?: StorageBackend;
917
+ readonly policyVersion?: string;
918
+ readonly onDeny?: (envelope: ToolEnvelope, reason: string, source: string | null) => void;
919
+ readonly onAllow?: (envelope: ToolEnvelope) => void;
920
+ readonly successCheck?: (toolName: string, result: unknown) => boolean;
921
+ readonly principal?: Principal;
922
+ readonly principalResolver?: (toolName: string, toolInput: Record<string, unknown>) => Principal;
923
+ readonly approvalBackend?: ApprovalBackend;
924
+ }
925
+ /**
926
+ * Main configuration and entrypoint.
927
+ *
928
+ * Two usage modes:
929
+ * 1. With framework adapter: use the appropriate adapter
930
+ * 2. Framework-agnostic: use guard.run() directly
931
+ */
932
+ declare class Edictum implements GuardLike {
933
+ readonly environment: string;
934
+ readonly mode: "enforce" | "observe";
935
+ readonly backend: StorageBackend;
936
+ readonly redaction: RedactionPolicy;
937
+ readonly toolRegistry: ToolRegistry;
938
+ readonly auditSink: AuditSink;
939
+ private readonly _localSink;
940
+ private _state;
941
+ private readonly _beforeHooks;
942
+ private readonly _afterHooks;
943
+ private readonly _sessionId;
944
+ /** @internal */ readonly _onDeny: ((envelope: ToolEnvelope, reason: string, source: string | null) => void) | null;
945
+ /** @internal */ readonly _onAllow: ((envelope: ToolEnvelope) => void) | null;
946
+ /** @internal */ readonly _successCheck: ((toolName: string, result: unknown) => boolean) | null;
947
+ private _principal;
948
+ private readonly _principalResolver;
949
+ /** @internal */ readonly _approvalBackend: ApprovalBackend | null;
950
+ constructor(options?: EdictumOptions);
951
+ /** The local in-memory audit event collector. Always present. */
952
+ get localSink(): CollectingAuditSink;
953
+ /** Operation limits for the current contract set. */
954
+ get limits(): OperationLimits;
955
+ /** Update operation limits (replaces compiled state atomically). */
956
+ set limits(value: OperationLimits);
957
+ /** SHA256 hash identifying the active contract bundle. */
958
+ get policyVersion(): string | null;
959
+ /**
960
+ * Replace the compiled state atomically.
961
+ *
962
+ * @internal — used by factory.ts reload(). Not part of the public API.
963
+ */
964
+ _replaceState(newState: CompiledState): void;
965
+ /**
966
+ * Read the current compiled state.
967
+ *
968
+ * @internal — used by factory.ts reload(). Not part of the public API.
969
+ */
970
+ _getState(): CompiledState;
971
+ /** Update policy version (replaces compiled state atomically). */
972
+ set policyVersion(value: string | null);
973
+ /** The persistent session ID for this guard instance. */
974
+ get sessionId(): string;
975
+ /** Update the principal used for subsequent tool calls. */
976
+ setPrincipal(principal: Principal): void;
977
+ /** Resolve the principal for a tool call. */
978
+ _resolvePrincipal(toolName: string, toolInput: Record<string, unknown>): Principal | null;
979
+ private _registerHook;
980
+ getHooks(phase: "before" | "after", envelope: ToolEnvelope): HookRegistration[];
981
+ getPreconditions(envelope: ToolEnvelope): InternalPrecondition[];
982
+ getPostconditions(envelope: ToolEnvelope): InternalPostcondition[];
983
+ getSessionContracts(): InternalSessionContract[];
984
+ getSandboxContracts(envelope: ToolEnvelope): InternalSandboxContract[];
985
+ getObservePreconditions(envelope: ToolEnvelope): InternalPrecondition[];
986
+ getObservePostconditions(envelope: ToolEnvelope): InternalPostcondition[];
987
+ getObserveSessionContracts(): InternalSessionContract[];
988
+ getObserveSandboxContracts(envelope: ToolEnvelope): InternalSandboxContract[];
989
+ /**
990
+ * Classify user-facing and internal contracts into enforce/observe lists.
991
+ *
992
+ * User-facing contracts (Precondition, Postcondition, SessionContract)
993
+ * are converted to internal representations. Internal contracts (from
994
+ * YAML compiler) carry _edictum_* metadata and are classified by their
995
+ * _edictum_observe flag (Python uses _edictum_shadow — wire-format parity).
996
+ */
997
+ private static _classifyContracts;
998
+ /** Route an internal contract to the appropriate enforce/observe list. */
999
+ private static _classifyInternal;
1000
+ /** Filter contracts by tool pattern and optional `when` guard. */
1001
+ private static _filterByTool;
1002
+ /** Filter sandbox contracts by tool patterns array. */
1003
+ private static _filterSandbox;
1004
+ /** Execute a tool call with full governance pipeline. */
1005
+ run(toolName: string, args: Record<string, unknown>, toolCallable: (args: Record<string, unknown>) => unknown | Promise<unknown>, options?: {
1006
+ sessionId?: string;
1007
+ environment?: string;
1008
+ principal?: Principal;
1009
+ }): Promise<unknown>;
1010
+ /**
1011
+ * Dry-run evaluation of a tool call against all matching contracts.
1012
+ *
1013
+ * Never executes the tool. Evaluates exhaustively (no short-circuit).
1014
+ * Session contracts are skipped.
1015
+ */
1016
+ evaluate(toolName: string, args: Record<string, unknown>, options?: {
1017
+ principal?: Principal;
1018
+ output?: string;
1019
+ environment?: string;
1020
+ }): Promise<EvaluationResult>;
1021
+ /** Evaluate a batch of tool calls. Thin wrapper over evaluate(). */
1022
+ evaluateBatch(calls: Array<{
1023
+ tool: string;
1024
+ args?: Record<string, unknown>;
1025
+ principal?: Record<string, unknown>;
1026
+ output?: string | Record<string, unknown>;
1027
+ environment?: string;
1028
+ }>): Promise<EvaluationResult[]>;
1029
+ /**
1030
+ * Create an Edictum instance from one or more YAML contract bundle paths.
1031
+ *
1032
+ * When multiple paths are given, bundles are composed left-to-right
1033
+ * (later layers override earlier ones).
1034
+ *
1035
+ * When the trailing options object has `returnReport: true`, returns
1036
+ * a tuple of [Edictum, CompositionReport].
1037
+ */
1038
+ static fromYaml(...args: [...string[], FromYamlOptions & {
1039
+ returnReport: true;
1040
+ }]): [Edictum, CompositionReport];
1041
+ static fromYaml(...args: [...string[], FromYamlOptions] | string[]): Edictum;
1042
+ /**
1043
+ * Create an Edictum instance from a YAML string or Uint8Array.
1044
+ */
1045
+ static fromYamlString(content: string | Uint8Array, options?: YamlFactoryOptions): Edictum;
1046
+ /**
1047
+ * Atomically replace this guard's contracts from a YAML string.
1048
+ *
1049
+ * Pass customOperators/customSelectors if the new YAML uses custom
1050
+ * operators or selectors that were passed to fromYaml/fromYamlString.
1051
+ */
1052
+ reload(yamlContent: string, options?: ReloadOptions): void;
1053
+ }
1054
+
1055
+ /**
1056
+ * Execution logic for Edictum.run() -- governance pipeline with tool execution.
1057
+ *
1058
+ * Ports Python's _runner.py. Governance pipeline + tool callable execution.
1059
+ *
1060
+ * SIZE APPROVAL: This file exceeds 200 lines. It mirrors Python's _runner.py
1061
+ * (350 LOC). The full run() flow (pre-execute → approval → execute → post-execute
1062
+ * → audit) is a single cohesive transaction that would be harder to follow if split.
1063
+ */
1064
+
1065
+ /**
1066
+ * Default heuristic for tool success detection.
1067
+ *
1068
+ * Matches the heuristic used by all framework adapters:
1069
+ * - null/undefined is success
1070
+ * - object with is_error truthy is failure
1071
+ * - string starting with "error:" or "fatal:" (case-insensitive) is failure
1072
+ * - everything else is success
1073
+ */
1074
+ declare function defaultSuccessCheck(_toolName: string, result: unknown): boolean;
1075
+ /** Options for the run() function beyond the required positional args. */
1076
+ interface RunOptions {
1077
+ readonly sessionId?: string;
1078
+ readonly environment?: string;
1079
+ readonly principal?: Principal;
1080
+ }
1081
+ /**
1082
+ * Framework-agnostic entrypoint for governed tool execution.
1083
+ *
1084
+ * Creates session, pipeline, envelope. Runs pre-execute governance,
1085
+ * handles approval flow, executes the tool, runs post-execute governance,
1086
+ * emits audit events, and returns the (potentially redacted) result.
1087
+ */
1088
+ declare function run(guard: Edictum, toolName: string, args: Record<string, unknown>, toolCallable: (args: Record<string, unknown>) => unknown | Promise<unknown>, options?: RunOptions): Promise<unknown>;
1089
+
1090
+ /**
1091
+ * Dry-run evaluation logic for Edictum.evaluate() and evaluateBatch().
1092
+ *
1093
+ * Ports Python's _dry_run.py. Exhaustive contract evaluation without
1094
+ * tool execution. Session contracts are skipped (no session state).
1095
+ *
1096
+ * SIZE APPROVAL: This file exceeds 200 lines. It mirrors Python's
1097
+ * _dry_run.py (205 LOC). evaluate() + evaluateBatch() are a cohesive unit.
1098
+ */
1099
+
1100
+ /** Options for the evaluate() function. */
1101
+ interface EvaluateOptions {
1102
+ readonly principal?: Principal;
1103
+ readonly output?: string;
1104
+ readonly environment?: string;
1105
+ }
1106
+ /** A single call in an evaluateBatch() batch. */
1107
+ interface BatchCall {
1108
+ readonly tool: string;
1109
+ readonly args?: Record<string, unknown>;
1110
+ readonly principal?: Record<string, unknown>;
1111
+ readonly output?: string | Record<string, unknown>;
1112
+ readonly environment?: string;
1113
+ }
1114
+
1115
+ /**
1116
+ * Minimal Python fnmatch.fnmatch() port for glob pattern matching.
1117
+ *
1118
+ * Used by Edictum for contract tool filtering and hook registration.
1119
+ */
1120
+ /**
1121
+ * Match a name against a glob pattern (fnmatch-style).
1122
+ *
1123
+ * Supports: `*` (any sequence), `?` (any single char), literal match.
1124
+ * Does NOT support `[...]` character classes (not used by edictum contracts).
1125
+ *
1126
+ * Input capped at 10,000 characters to prevent regex DoS.
1127
+ */
1128
+ declare function fnmatch(name: string, pattern: string): boolean;
1129
+
1130
+ /** Compiler — convert parsed YAML contracts into contract objects and OperationLimits. */
1131
+
1132
+ /** Result of compiling a YAML contract bundle. */
1133
+ interface CompiledBundle {
1134
+ readonly preconditions: readonly unknown[];
1135
+ readonly postconditions: readonly unknown[];
1136
+ readonly sessionContracts: readonly unknown[];
1137
+ readonly sandboxContracts: readonly unknown[];
1138
+ readonly limits: OperationLimits;
1139
+ readonly defaultMode: string;
1140
+ readonly tools: Readonly<Record<string, Record<string, unknown>>>;
1141
+ }
1142
+ interface CompileOptions {
1143
+ readonly customOperators?: Readonly<Record<string, CustomOperator>> | null;
1144
+ readonly customSelectors?: Readonly<Record<string, CustomSelector>> | null;
1145
+ }
1146
+ /**
1147
+ * Compile a validated YAML bundle into contract objects.
1148
+ *
1149
+ * @param bundle - A validated bundle dict (output of loadBundle).
1150
+ * @param options - Optional custom operators and selectors.
1151
+ * @returns CompiledBundle with preconditions, postconditions, sessionContracts,
1152
+ * and merged OperationLimits.
1153
+ */
1154
+ declare function compileContracts(bundle: Record<string, unknown>, options?: CompileOptions): CompiledBundle;
1155
+
1156
+ /** Compiler utilities — validation, regex precompilation, message expansion. */
1157
+
1158
+ /**
1159
+ * Expand {placeholder} tokens in a message template.
1160
+ *
1161
+ * Missing placeholders are kept as-is. Each expansion is capped at 200 chars.
1162
+ * Values that look like secrets are redacted.
1163
+ */
1164
+ declare function expandMessage(template: string, envelope: ToolEnvelope, outputText?: string | null, customSelectors?: Readonly<Record<string, CustomSelector>> | null): string;
1165
+ /** Validate that all operators used in the bundle are known (built-in or custom). */
1166
+ declare function validateOperators(bundle: Record<string, unknown>, customOperators: Readonly<Record<string, unknown>> | null): void;
1167
+
1168
+ /** YAML Bundle Loader — parse, validate, compute bundle hash. */
1169
+
1170
+ /** Maximum bundle file size in bytes (1 MB). */
1171
+ declare const MAX_BUNDLE_SIZE = 1048576;
1172
+ /** SHA256 hash of raw YAML bytes, used as policy_version. */
1173
+ interface BundleHash {
1174
+ readonly hex: string;
1175
+ }
1176
+ /** Compute SHA256 hash of raw YAML bytes. */
1177
+ declare function computeHash(rawBytes: Uint8Array): BundleHash;
1178
+ /**
1179
+ * Load and validate a YAML contract bundle from a file path.
1180
+ *
1181
+ * @returns Tuple of [parsed bundle dict, bundle hash].
1182
+ * @throws EdictumConfigError on validation failure.
1183
+ * @throws Error if the file does not exist.
1184
+ */
1185
+ declare function loadBundle(source: string): [Record<string, unknown>, BundleHash];
1186
+ /**
1187
+ * Load and validate a YAML contract bundle from a string or bytes.
1188
+ *
1189
+ * Like {@link loadBundle} but accepts YAML content directly instead of
1190
+ * a file path. Useful when YAML is generated programmatically or fetched
1191
+ * from an API.
1192
+ *
1193
+ * @returns Tuple of [parsed bundle dict, bundle hash].
1194
+ * @throws EdictumConfigError on validation failure.
1195
+ */
1196
+ declare function loadBundleString(content: string | Uint8Array): [Record<string, unknown>, BundleHash];
1197
+
1198
+ /** Edictum — Runtime contract enforcement for AI agent tool calls. */
1199
+ declare const VERSION = "0.1.0";
1200
+
1201
+ export { type ApprovalBackend, type ApprovalDecision, type ApprovalRequest, ApprovalStatus, AuditAction, type AuditEvent, type AuditSink, BUILTIN_OPERATOR_NAMES, BUILTIN_SELECTOR_PREFIXES, BashClassifier, type BatchCall, type BundleHash, CollectingAuditSink, type CompileOptions, type CompiledBundle, type CompiledState, type ComposedBundle, CompositeSink, type CompositionReport, type ContractResult, type CreateEnvelopeOptions, type CustomOperator, type CustomSelector, DEFAULT_LIMITS, Edictum, EdictumConfigError, EdictumDenied, type EdictumOptions, EdictumToolError, type EvaluateOptions, type EvaluationResult, FileAuditSink, type Finding, type FromYamlOptions, GovernancePipeline, type GuardLike, HookDecision, type HookRegistration, HookResult, type InternalContract, type InternalPostcondition, type InternalPrecondition, type InternalSandboxContract, type InternalSessionContract, LocalApprovalBackend, MAX_BUNDLE_SIZE, MAX_REGEX_INPUT, MarkEvictedError, MemoryBackend, type OperationLimits, PolicyError, type PostCallResult, type PostDecision, type PostDecisionLike, type Postcondition, type PreDecision, type Precondition, type Principal, RedactionPolicy, type ReloadOptions, type RunOptions, Session, type SessionContract, SideEffect, StdoutAuditSink, type StorageBackend, type ToolConfig, type ToolEnvelope, ToolRegistry, VERSION, Verdict, type YamlFactoryOptions, _validateToolName, buildFindings, classifyFinding, compileContracts, composeBundles, computeHash, createAuditEvent, createCompiledState, createContractResult, createEnvelope, createEvaluationResult, createFinding, createPostCallResult, createPostDecision, createPreDecision, createPrincipal, deepFreeze, defaultSuccessCheck, evaluateExpression, expandMessage, fnmatch, fromYaml, fromYamlString, loadBundle, loadBundleString, reload, run, validateOperators };