@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,238 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/errors.ts
9
+ var EdictumDenied = class extends Error {
10
+ reason;
11
+ decisionSource;
12
+ decisionName;
13
+ constructor(reason, decisionSource = null, decisionName = null) {
14
+ super(reason);
15
+ this.name = "EdictumDenied";
16
+ this.reason = reason;
17
+ this.decisionSource = decisionSource;
18
+ this.decisionName = decisionName;
19
+ }
20
+ };
21
+ var EdictumConfigError = class extends Error {
22
+ constructor(message) {
23
+ super(message);
24
+ this.name = "EdictumConfigError";
25
+ }
26
+ };
27
+ var EdictumToolError = class extends Error {
28
+ constructor(message) {
29
+ super(message);
30
+ this.name = "EdictumToolError";
31
+ }
32
+ };
33
+
34
+ // src/envelope.ts
35
+ import { randomUUID } from "crypto";
36
+ var SideEffect = {
37
+ PURE: "pure",
38
+ READ: "read",
39
+ WRITE: "write",
40
+ IRREVERSIBLE: "irreversible"
41
+ };
42
+ function createPrincipal(partial = {}) {
43
+ const p = {
44
+ userId: partial.userId ?? null,
45
+ serviceId: partial.serviceId ?? null,
46
+ orgId: partial.orgId ?? null,
47
+ role: partial.role ?? null,
48
+ ticketRef: partial.ticketRef ?? null,
49
+ claims: partial.claims ?? {}
50
+ };
51
+ return deepFreeze(p);
52
+ }
53
+ function _validateToolName(toolName) {
54
+ if (!toolName) {
55
+ throw new EdictumConfigError(`Invalid tool_name: ${JSON.stringify(toolName)}`);
56
+ }
57
+ for (let i = 0; i < toolName.length; i++) {
58
+ const code = toolName.charCodeAt(i);
59
+ const ch = toolName[i];
60
+ if (code < 32 || code === 127 || ch === "/" || ch === "\\") {
61
+ throw new EdictumConfigError(`Invalid tool_name: ${JSON.stringify(toolName)}`);
62
+ }
63
+ }
64
+ }
65
+ function deepFreeze(obj) {
66
+ if (obj === null || obj === void 0 || typeof obj !== "object") {
67
+ return obj;
68
+ }
69
+ if (obj instanceof Date) {
70
+ return obj;
71
+ }
72
+ if (obj instanceof RegExp) {
73
+ return obj;
74
+ }
75
+ Object.freeze(obj);
76
+ for (const value of Object.values(obj)) {
77
+ if (value !== null && value !== void 0 && typeof value === "object" && !Object.isFrozen(value)) {
78
+ deepFreeze(value);
79
+ }
80
+ }
81
+ return obj;
82
+ }
83
+ var ToolRegistry = class {
84
+ _tools = /* @__PURE__ */ new Map();
85
+ register(name, sideEffect = SideEffect.WRITE, idempotent = false) {
86
+ this._tools.set(name, { name, sideEffect, idempotent });
87
+ }
88
+ classify(toolName, _args) {
89
+ const cfg = this._tools.get(toolName);
90
+ if (cfg) {
91
+ return [cfg.sideEffect, cfg.idempotent];
92
+ }
93
+ return [SideEffect.IRREVERSIBLE, false];
94
+ }
95
+ };
96
+ var BashClassifier = {
97
+ READ_ALLOWLIST: [
98
+ "ls",
99
+ "cat",
100
+ "head",
101
+ "tail",
102
+ "wc",
103
+ "find",
104
+ "grep",
105
+ "rg",
106
+ "git status",
107
+ "git log",
108
+ "git diff",
109
+ "git show",
110
+ "git branch",
111
+ "git remote",
112
+ "git tag",
113
+ "echo",
114
+ "pwd",
115
+ "whoami",
116
+ "date",
117
+ "which",
118
+ "file",
119
+ "stat",
120
+ "du",
121
+ "df",
122
+ "tree",
123
+ "less",
124
+ "more"
125
+ ],
126
+ SHELL_OPERATORS: [
127
+ "\n",
128
+ "\r",
129
+ "<(",
130
+ "<<",
131
+ "$",
132
+ "${",
133
+ ">",
134
+ ">>",
135
+ "|",
136
+ ";",
137
+ "&&",
138
+ "||",
139
+ "$(",
140
+ "`",
141
+ "#{"
142
+ ],
143
+ classify(command) {
144
+ const stripped = command.trim();
145
+ if (!stripped) {
146
+ return SideEffect.READ;
147
+ }
148
+ for (const op of BashClassifier.SHELL_OPERATORS) {
149
+ if (stripped.includes(op)) {
150
+ return SideEffect.IRREVERSIBLE;
151
+ }
152
+ }
153
+ for (const allowed of BashClassifier.READ_ALLOWLIST) {
154
+ if (stripped === allowed || stripped.startsWith(allowed + " ")) {
155
+ return SideEffect.READ;
156
+ }
157
+ }
158
+ return SideEffect.IRREVERSIBLE;
159
+ }
160
+ };
161
+ function safeDeepCopy(value) {
162
+ try {
163
+ return structuredClone(value);
164
+ } catch {
165
+ return JSON.parse(JSON.stringify(value));
166
+ }
167
+ }
168
+ function createEnvelope(toolName, toolInput, options = {}) {
169
+ _validateToolName(toolName);
170
+ const safeArgs = safeDeepCopy(toolInput);
171
+ const safeMetadata = options.metadata ? safeDeepCopy(options.metadata) : {};
172
+ let safePrincipal = null;
173
+ if (options.principal != null) {
174
+ const p = options.principal;
175
+ safePrincipal = createPrincipal({
176
+ userId: p.userId,
177
+ serviceId: p.serviceId,
178
+ orgId: p.orgId,
179
+ role: p.role,
180
+ ticketRef: p.ticketRef,
181
+ claims: p.claims ? safeDeepCopy(p.claims) : {}
182
+ });
183
+ }
184
+ const registry = options.registry ?? null;
185
+ let sideEffect = options.sideEffect ?? SideEffect.IRREVERSIBLE;
186
+ let idempotent = options.idempotent ?? false;
187
+ let bashCommand = null;
188
+ let filePath = null;
189
+ if (registry) {
190
+ const [regEffect, regIdempotent] = registry.classify(toolName, safeArgs);
191
+ if (options.sideEffect == null) sideEffect = regEffect;
192
+ if (options.idempotent == null) idempotent = regIdempotent;
193
+ }
194
+ if (toolName === "Bash") {
195
+ bashCommand = safeArgs.command ?? "";
196
+ if (options.sideEffect == null) {
197
+ sideEffect = BashClassifier.classify(bashCommand);
198
+ }
199
+ } else if (toolName === "Read" || toolName === "Glob" || toolName === "Grep") {
200
+ filePath = safeArgs.file_path ?? safeArgs.filePath ?? safeArgs.path ?? null;
201
+ } else if (toolName === "Write" || toolName === "Edit") {
202
+ filePath = safeArgs.file_path ?? safeArgs.filePath ?? safeArgs.path ?? null;
203
+ }
204
+ const envelope = {
205
+ toolName,
206
+ args: safeArgs,
207
+ callId: options.callId ?? randomUUID(),
208
+ runId: options.runId ?? "",
209
+ callIndex: options.callIndex ?? 0,
210
+ parentCallId: options.parentCallId ?? null,
211
+ sideEffect,
212
+ idempotent,
213
+ environment: options.environment ?? "production",
214
+ timestamp: options.timestamp ?? /* @__PURE__ */ new Date(),
215
+ caller: options.caller ?? "",
216
+ toolUseId: options.toolUseId ?? null,
217
+ principal: safePrincipal,
218
+ bashCommand,
219
+ filePath,
220
+ metadata: safeMetadata
221
+ };
222
+ return deepFreeze(envelope);
223
+ }
224
+
225
+ export {
226
+ __require,
227
+ EdictumDenied,
228
+ EdictumConfigError,
229
+ EdictumToolError,
230
+ SideEffect,
231
+ createPrincipal,
232
+ _validateToolName,
233
+ deepFreeze,
234
+ ToolRegistry,
235
+ BashClassifier,
236
+ createEnvelope
237
+ };
238
+ //# sourceMappingURL=chunk-CRPQFRYJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/envelope.ts"],"sourcesContent":["/** Exception classes for Edictum. */\n\n/** Raised when guard.run() denies a tool call in enforce mode. */\nexport class EdictumDenied extends Error {\n readonly reason: string;\n readonly decisionSource: string | null;\n readonly decisionName: string | null;\n\n constructor(\n reason: string,\n decisionSource: string | null = null,\n decisionName: string | null = null,\n ) {\n super(reason);\n this.name = \"EdictumDenied\";\n this.reason = reason;\n this.decisionSource = decisionSource;\n this.decisionName = decisionName;\n }\n}\n\n/** Raised for configuration/load-time errors (invalid YAML, schema failures, etc.). */\nexport class EdictumConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EdictumConfigError\";\n }\n}\n\n/** Raised when the governed tool itself fails. */\nexport class EdictumToolError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EdictumToolError\";\n }\n}\n","/** Tool Invocation Envelope — immutable snapshot of a tool call. */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport { EdictumConfigError } from \"./errors.js\";\nimport type { ToolConfig } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// SideEffect\n// ---------------------------------------------------------------------------\n\n/**\n * Classification of tool side effects.\n *\n * Determines postcondition behavior and retry safety.\n *\n * DEFAULTS:\n * - Unregistered tools -> IRREVERSIBLE (conservative)\n * - Bash -> IRREVERSIBLE unless strict allowlist match\n * - Classification errors always err toward MORE restrictive\n */\nexport const SideEffect = {\n PURE: \"pure\",\n READ: \"read\",\n WRITE: \"write\",\n IRREVERSIBLE: \"irreversible\",\n} as const;\n\nexport type SideEffect = (typeof SideEffect)[keyof typeof SideEffect];\n\n// ---------------------------------------------------------------------------\n// Principal\n// ---------------------------------------------------------------------------\n\n/**\n * Identity context for audit attribution.\n *\n * NOTE: `claims` is a plain object. The Principal itself is frozen via\n * `Object.freeze()`, making the reference immutable. Callers should treat\n * claims as read-only after construction.\n */\nexport interface Principal {\n readonly userId: string | null;\n readonly serviceId: string | null;\n readonly orgId: string | null;\n readonly role: string | null;\n readonly ticketRef: string | null;\n readonly claims: Readonly<Record<string, unknown>>;\n}\n\n/** Create a frozen Principal with defaults for omitted fields. */\nexport function createPrincipal(\n partial: Partial<Principal> = {},\n): Readonly<Principal> {\n const p: Principal = {\n userId: partial.userId ?? null,\n serviceId: partial.serviceId ?? null,\n orgId: partial.orgId ?? null,\n role: partial.role ?? null,\n ticketRef: partial.ticketRef ?? null,\n claims: partial.claims ?? {},\n };\n return deepFreeze(p);\n}\n\n// ---------------------------------------------------------------------------\n// _validateToolName\n// ---------------------------------------------------------------------------\n\n/**\n * Validate tool_name: reject empty, control chars, path separators.\n *\n * Throws EdictumConfigError for:\n * - Empty string\n * - Any ASCII control character (code < 0x20 or code === 0x7f)\n * - Forward slash `/`\n * - Backslash `\\`\n */\nexport function _validateToolName(toolName: string): void {\n if (!toolName) {\n throw new EdictumConfigError(`Invalid tool_name: ${JSON.stringify(toolName)}`);\n }\n for (let i = 0; i < toolName.length; i++) {\n const code = toolName.charCodeAt(i);\n const ch = toolName[i];\n if (code < 0x20 || code === 0x7f || ch === \"/\" || ch === \"\\\\\") {\n throw new EdictumConfigError(`Invalid tool_name: ${JSON.stringify(toolName)}`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// ToolEnvelope\n// ---------------------------------------------------------------------------\n\n/**\n * Immutable snapshot of a tool invocation.\n *\n * Prefer `createEnvelope()` factory for deep-copy guarantees.\n * Direct construction validates tool_name but does NOT deep-copy args.\n */\nexport interface ToolEnvelope {\n // Identity\n readonly toolName: string;\n readonly args: Readonly<Record<string, unknown>>;\n readonly callId: string;\n readonly runId: string;\n readonly callIndex: number;\n readonly parentCallId: string | null;\n\n // Classification\n readonly sideEffect: SideEffect;\n readonly idempotent: boolean;\n\n // Context\n readonly environment: string;\n readonly timestamp: Date;\n readonly caller: string;\n readonly toolUseId: string | null;\n\n // Principal\n readonly principal: Readonly<Principal> | null;\n\n // Extracted convenience fields\n readonly bashCommand: string | null;\n readonly filePath: string | null;\n\n // Extensible\n readonly metadata: Readonly<Record<string, unknown>>;\n}\n\n// ---------------------------------------------------------------------------\n// deepFreeze\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively freeze an object and all nested objects.\n *\n * Date objects are skipped — Object.freeze() cannot prevent mutation\n * via Date prototype methods (setFullYear, setTime, etc.) because Date\n * stores state in internal slots, not own properties.\n */\nexport function deepFreeze<T>(obj: T): T {\n if (obj === null || obj === undefined || typeof obj !== \"object\") {\n return obj;\n }\n // Date internal slots are not freezable — skip to avoid false sense of safety\n if (obj instanceof Date) {\n return obj;\n }\n // RegExp with global/sticky flags stores mutable lastIndex in internal state.\n // Freezing prevents String.replace() from updating lastIndex → TypeError.\n if (obj instanceof RegExp) {\n return obj;\n }\n Object.freeze(obj);\n for (const value of Object.values(obj as Record<string, unknown>)) {\n if (value !== null && value !== undefined && typeof value === \"object\" && !Object.isFrozen(value)) {\n deepFreeze(value);\n }\n }\n return obj;\n}\n\n// ---------------------------------------------------------------------------\n// ToolRegistry\n// ---------------------------------------------------------------------------\n\n/** Maps tool names to governance properties. Unregistered tools default to IRREVERSIBLE. */\nexport class ToolRegistry {\n private readonly _tools: Map<string, ToolConfig> = new Map();\n\n register(\n name: string,\n sideEffect: SideEffect = SideEffect.WRITE,\n idempotent: boolean = false,\n ): void {\n this._tools.set(name, { name, sideEffect, idempotent });\n }\n\n classify(\n toolName: string,\n _args: Record<string, unknown>,\n ): [SideEffect, boolean] {\n const cfg = this._tools.get(toolName);\n if (cfg) {\n return [cfg.sideEffect as SideEffect, cfg.idempotent];\n }\n return [SideEffect.IRREVERSIBLE, false];\n }\n}\n\n// ---------------------------------------------------------------------------\n// BashClassifier\n// ---------------------------------------------------------------------------\n\n/**\n * Classify bash commands by side-effect level.\n *\n * Default is IRREVERSIBLE. Only downgraded to READ via strict\n * allowlist AND absence of shell operators.\n *\n * This is a heuristic, not a security boundary.\n */\nexport const BashClassifier = {\n READ_ALLOWLIST: [\n \"ls\",\n \"cat\",\n \"head\",\n \"tail\",\n \"wc\",\n \"find\",\n \"grep\",\n \"rg\",\n \"git status\",\n \"git log\",\n \"git diff\",\n \"git show\",\n \"git branch\",\n \"git remote\",\n \"git tag\",\n \"echo\",\n \"pwd\",\n \"whoami\",\n \"date\",\n \"which\",\n \"file\",\n \"stat\",\n \"du\",\n \"df\",\n \"tree\",\n \"less\",\n \"more\",\n ] as const,\n\n SHELL_OPERATORS: [\n \"\\n\",\n \"\\r\",\n \"<(\",\n \"<<\",\n \"$\",\n \"${\",\n \">\",\n \">>\",\n \"|\",\n \";\",\n \"&&\",\n \"||\",\n \"$(\",\n \"`\",\n \"#{\",\n ] as const,\n\n classify(command: string): SideEffect {\n const stripped = command.trim();\n if (!stripped) {\n return SideEffect.READ;\n }\n\n for (const op of BashClassifier.SHELL_OPERATORS) {\n if (stripped.includes(op)) {\n return SideEffect.IRREVERSIBLE;\n }\n }\n\n for (const allowed of BashClassifier.READ_ALLOWLIST) {\n if (stripped === allowed || stripped.startsWith(allowed + \" \")) {\n return SideEffect.READ;\n }\n }\n\n return SideEffect.IRREVERSIBLE;\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// safeDeepCopy — structuredClone with JSON roundtrip fallback\n// ---------------------------------------------------------------------------\n\nfunction safeDeepCopy<T>(value: T): T {\n try {\n return structuredClone(value);\n } catch {\n return JSON.parse(JSON.stringify(value)) as T;\n }\n}\n\n// ---------------------------------------------------------------------------\n// createEnvelope\n// ---------------------------------------------------------------------------\n\n/** Options for `createEnvelope()` beyond the required positional args. */\nexport interface CreateEnvelopeOptions {\n readonly runId?: string;\n readonly callIndex?: number;\n readonly callId?: string;\n readonly parentCallId?: string | null;\n readonly sideEffect?: SideEffect;\n readonly idempotent?: boolean;\n readonly environment?: string;\n readonly timestamp?: Date;\n readonly caller?: string;\n readonly toolUseId?: string | null;\n readonly principal?: Principal | null;\n readonly metadata?: Record<string, unknown>;\n readonly registry?: ToolRegistry | null;\n}\n\n/**\n * Factory that enforces immutability guarantees.\n *\n * Prefer this factory over direct construction — it deep-copies args\n * and metadata to ensure the envelope is a true immutable snapshot.\n */\nexport function createEnvelope(\n toolName: string,\n toolInput: Record<string, unknown>,\n options: CreateEnvelopeOptions = {},\n): Readonly<ToolEnvelope> {\n _validateToolName(toolName);\n\n // Deep-copy for immutability\n const safeArgs = safeDeepCopy(toolInput);\n\n // Deep-copy metadata\n const safeMetadata = options.metadata ? safeDeepCopy(options.metadata) : {};\n\n // Deep-copy Principal to protect claims dict\n let safePrincipal: Readonly<Principal> | null = null;\n if (options.principal != null) {\n const p = options.principal;\n safePrincipal = createPrincipal({\n userId: p.userId,\n serviceId: p.serviceId,\n orgId: p.orgId,\n role: p.role,\n ticketRef: p.ticketRef,\n claims: p.claims ? safeDeepCopy(p.claims as Record<string, unknown>) : {},\n });\n }\n\n // Classification: explicit options > registry > defaults\n const registry = options.registry ?? null;\n let sideEffect: SideEffect = options.sideEffect ?? SideEffect.IRREVERSIBLE;\n let idempotent = options.idempotent ?? false;\n let bashCommand: string | null = null;\n let filePath: string | null = null;\n\n // Registry overrides defaults but not explicit options\n if (registry) {\n const [regEffect, regIdempotent] = registry.classify(toolName, safeArgs);\n if (options.sideEffect == null) sideEffect = regEffect;\n if (options.idempotent == null) idempotent = regIdempotent;\n }\n\n // Extract convenience fields (handle both snake_case and camelCase keys)\n if (toolName === \"Bash\") {\n bashCommand = (safeArgs.command as string) ?? \"\";\n // BashClassifier wins over registry but NOT over explicit caller options\n if (options.sideEffect == null) {\n sideEffect = BashClassifier.classify(bashCommand);\n }\n } else if (\n toolName === \"Read\" ||\n toolName === \"Glob\" ||\n toolName === \"Grep\"\n ) {\n filePath =\n (safeArgs.file_path as string) ??\n (safeArgs.filePath as string) ??\n (safeArgs.path as string) ??\n null;\n } else if (toolName === \"Write\" || toolName === \"Edit\") {\n filePath =\n (safeArgs.file_path as string) ??\n (safeArgs.filePath as string) ??\n (safeArgs.path as string) ??\n null;\n }\n\n const envelope: ToolEnvelope = {\n toolName,\n args: safeArgs,\n callId: options.callId ?? randomUUID(),\n runId: options.runId ?? \"\",\n callIndex: options.callIndex ?? 0,\n parentCallId: options.parentCallId ?? null,\n sideEffect,\n idempotent,\n environment: options.environment ?? \"production\",\n timestamp: options.timestamp ?? new Date(),\n caller: options.caller ?? \"\",\n toolUseId: options.toolUseId ?? null,\n principal: safePrincipal,\n bashCommand,\n filePath,\n metadata: safeMetadata,\n };\n\n return deepFreeze(envelope);\n}\n"],"mappings":";;;;;;;;AAGO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,QACA,iBAAgC,MAChC,eAA8B,MAC9B;AACA,UAAM,MAAM;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,iBAAiB;AACtB,SAAK,eAAe;AAAA,EACtB;AACF;AAGO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACjCA,SAAS,kBAAkB;AAmBpB,IAAM,aAAa;AAAA,EACxB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,cAAc;AAChB;AAyBO,SAAS,gBACd,UAA8B,CAAC,GACV;AACrB,QAAM,IAAe;AAAA,IACnB,QAAQ,QAAQ,UAAU;AAAA,IAC1B,WAAW,QAAQ,aAAa;AAAA,IAChC,OAAO,QAAQ,SAAS;AAAA,IACxB,MAAM,QAAQ,QAAQ;AAAA,IACtB,WAAW,QAAQ,aAAa;AAAA,IAChC,QAAQ,QAAQ,UAAU,CAAC;AAAA,EAC7B;AACA,SAAO,WAAW,CAAC;AACrB;AAeO,SAAS,kBAAkB,UAAwB;AACxD,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,mBAAmB,sBAAsB,KAAK,UAAU,QAAQ,CAAC,EAAE;AAAA,EAC/E;AACA,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,OAAO,SAAS,WAAW,CAAC;AAClC,UAAM,KAAK,SAAS,CAAC;AACrB,QAAI,OAAO,MAAQ,SAAS,OAAQ,OAAO,OAAO,OAAO,MAAM;AAC7D,YAAM,IAAI,mBAAmB,sBAAsB,KAAK,UAAU,QAAQ,CAAC,EAAE;AAAA,IAC/E;AAAA,EACF;AACF;AAqDO,SAAS,WAAc,KAAW;AACvC,MAAI,QAAQ,QAAQ,QAAQ,UAAa,OAAO,QAAQ,UAAU;AAChE,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,MAAM;AACvB,WAAO;AAAA,EACT;AAGA,MAAI,eAAe,QAAQ;AACzB,WAAO;AAAA,EACT;AACA,SAAO,OAAO,GAAG;AACjB,aAAW,SAAS,OAAO,OAAO,GAA8B,GAAG;AACjE,QAAI,UAAU,QAAQ,UAAU,UAAa,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;AACjG,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAOO,IAAM,eAAN,MAAmB;AAAA,EACP,SAAkC,oBAAI,IAAI;AAAA,EAE3D,SACE,MACA,aAAyB,WAAW,OACpC,aAAsB,OAChB;AACN,SAAK,OAAO,IAAI,MAAM,EAAE,MAAM,YAAY,WAAW,CAAC;AAAA,EACxD;AAAA,EAEA,SACE,UACA,OACuB;AACvB,UAAM,MAAM,KAAK,OAAO,IAAI,QAAQ;AACpC,QAAI,KAAK;AACP,aAAO,CAAC,IAAI,YAA0B,IAAI,UAAU;AAAA,IACtD;AACA,WAAO,CAAC,WAAW,cAAc,KAAK;AAAA,EACxC;AACF;AAcO,IAAM,iBAAiB;AAAA,EAC5B,gBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EAEA,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EAEA,SAAS,SAA6B;AACpC,UAAM,WAAW,QAAQ,KAAK;AAC9B,QAAI,CAAC,UAAU;AACb,aAAO,WAAW;AAAA,IACpB;AAEA,eAAW,MAAM,eAAe,iBAAiB;AAC/C,UAAI,SAAS,SAAS,EAAE,GAAG;AACzB,eAAO,WAAW;AAAA,MACpB;AAAA,IACF;AAEA,eAAW,WAAW,eAAe,gBAAgB;AACnD,UAAI,aAAa,WAAW,SAAS,WAAW,UAAU,GAAG,GAAG;AAC9D,eAAO,WAAW;AAAA,MACpB;AAAA,IACF;AAEA,WAAO,WAAW;AAAA,EACpB;AACF;AAMA,SAAS,aAAgB,OAAa;AACpC,MAAI;AACF,WAAO,gBAAgB,KAAK;AAAA,EAC9B,QAAQ;AACN,WAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAAA,EACzC;AACF;AA6BO,SAAS,eACd,UACA,WACA,UAAiC,CAAC,GACV;AACxB,oBAAkB,QAAQ;AAG1B,QAAM,WAAW,aAAa,SAAS;AAGvC,QAAM,eAAe,QAAQ,WAAW,aAAa,QAAQ,QAAQ,IAAI,CAAC;AAG1E,MAAI,gBAA4C;AAChD,MAAI,QAAQ,aAAa,MAAM;AAC7B,UAAM,IAAI,QAAQ;AAClB,oBAAgB,gBAAgB;AAAA,MAC9B,QAAQ,EAAE;AAAA,MACV,WAAW,EAAE;AAAA,MACb,OAAO,EAAE;AAAA,MACT,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,QAAQ,EAAE,SAAS,aAAa,EAAE,MAAiC,IAAI,CAAC;AAAA,IAC1E,CAAC;AAAA,EACH;AAGA,QAAM,WAAW,QAAQ,YAAY;AACrC,MAAI,aAAyB,QAAQ,cAAc,WAAW;AAC9D,MAAI,aAAa,QAAQ,cAAc;AACvC,MAAI,cAA6B;AACjC,MAAI,WAA0B;AAG9B,MAAI,UAAU;AACZ,UAAM,CAAC,WAAW,aAAa,IAAI,SAAS,SAAS,UAAU,QAAQ;AACvE,QAAI,QAAQ,cAAc,KAAM,cAAa;AAC7C,QAAI,QAAQ,cAAc,KAAM,cAAa;AAAA,EAC/C;AAGA,MAAI,aAAa,QAAQ;AACvB,kBAAe,SAAS,WAAsB;AAE9C,QAAI,QAAQ,cAAc,MAAM;AAC9B,mBAAa,eAAe,SAAS,WAAW;AAAA,IAClD;AAAA,EACF,WACE,aAAa,UACb,aAAa,UACb,aAAa,QACb;AACA,eACG,SAAS,aACT,SAAS,YACT,SAAS,QACV;AAAA,EACJ,WAAW,aAAa,WAAW,aAAa,QAAQ;AACtD,eACG,SAAS,aACT,SAAS,YACT,SAAS,QACV;AAAA,EACJ;AAEA,QAAM,WAAyB;AAAA,IAC7B;AAAA,IACA,MAAM;AAAA,IACN,QAAQ,QAAQ,UAAU,WAAW;AAAA,IACrC,OAAO,QAAQ,SAAS;AAAA,IACxB,WAAW,QAAQ,aAAa;AAAA,IAChC,cAAc,QAAQ,gBAAgB;AAAA,IACtC;AAAA,IACA;AAAA,IACA,aAAa,QAAQ,eAAe;AAAA,IACpC,WAAW,QAAQ,aAAa,oBAAI,KAAK;AAAA,IACzC,QAAQ,QAAQ,UAAU;AAAA,IAC1B,WAAW,QAAQ,aAAa;AAAA,IAChC,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,UAAU;AAAA,EACZ;AAEA,SAAO,WAAW,QAAQ;AAC5B;","names":[]}
@@ -0,0 +1,30 @@
1
+ // src/evaluation.ts
2
+ function createContractResult(fields) {
3
+ return Object.freeze({
4
+ contractId: fields.contractId,
5
+ contractType: fields.contractType,
6
+ passed: fields.passed,
7
+ message: fields.message ?? null,
8
+ tags: Object.freeze([...fields.tags ?? []]),
9
+ observed: fields.observed ?? false,
10
+ effect: fields.effect ?? "warn",
11
+ policyError: fields.policyError ?? false
12
+ });
13
+ }
14
+ function createEvaluationResult(fields) {
15
+ return Object.freeze({
16
+ verdict: fields.verdict,
17
+ toolName: fields.toolName,
18
+ contracts: Object.freeze([...fields.contracts ?? []]),
19
+ denyReasons: Object.freeze([...fields.denyReasons ?? []]),
20
+ warnReasons: Object.freeze([...fields.warnReasons ?? []]),
21
+ contractsEvaluated: fields.contractsEvaluated ?? 0,
22
+ policyError: fields.policyError ?? false
23
+ });
24
+ }
25
+
26
+ export {
27
+ createContractResult,
28
+ createEvaluationResult
29
+ };
30
+ //# sourceMappingURL=chunk-IXMXZGJG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/evaluation.ts"],"sourcesContent":["/** Evaluation result types for dry-run contract evaluation. */\n\n// ---------------------------------------------------------------------------\n// ContractResult\n// ---------------------------------------------------------------------------\n\n/** Result of evaluating a single contract. */\nexport interface ContractResult {\n readonly contractId: string;\n readonly contractType: string; // \"precondition\" | \"postcondition\" | \"sandbox\"\n readonly passed: boolean;\n readonly message: string | null;\n readonly tags: readonly string[];\n readonly observed: boolean;\n readonly effect: string;\n readonly policyError: boolean;\n}\n\n/** Create a frozen ContractResult with defaults matching the Python dataclass. */\nexport function createContractResult(\n fields: Pick<ContractResult, \"contractId\" | \"contractType\" | \"passed\"> &\n Partial<Omit<ContractResult, \"contractId\" | \"contractType\" | \"passed\">>,\n): ContractResult {\n return Object.freeze({\n contractId: fields.contractId,\n contractType: fields.contractType,\n passed: fields.passed,\n message: fields.message ?? null,\n tags: Object.freeze([...(fields.tags ?? [])]),\n observed: fields.observed ?? false,\n effect: fields.effect ?? \"warn\",\n policyError: fields.policyError ?? false,\n });\n}\n\n// ---------------------------------------------------------------------------\n// EvaluationResult\n// ---------------------------------------------------------------------------\n\n/** Result of dry-run evaluation of a tool call against contracts. */\nexport interface EvaluationResult {\n readonly verdict: string; // \"allow\" | \"deny\" | \"warn\"\n readonly toolName: string;\n readonly contracts: readonly ContractResult[];\n readonly denyReasons: readonly string[];\n readonly warnReasons: readonly string[];\n readonly contractsEvaluated: number;\n readonly policyError: boolean;\n}\n\n/** Create a frozen EvaluationResult with defaults matching the Python dataclass. */\nexport function createEvaluationResult(\n fields: Pick<EvaluationResult, \"verdict\" | \"toolName\"> &\n Partial<Omit<EvaluationResult, \"verdict\" | \"toolName\">>,\n): EvaluationResult {\n return Object.freeze({\n verdict: fields.verdict,\n toolName: fields.toolName,\n contracts: Object.freeze([...(fields.contracts ?? [])]),\n denyReasons: Object.freeze([...(fields.denyReasons ?? [])]),\n warnReasons: Object.freeze([...(fields.warnReasons ?? [])]),\n contractsEvaluated: fields.contractsEvaluated ?? 0,\n policyError: fields.policyError ?? false,\n });\n}\n"],"mappings":";AAmBO,SAAS,qBACd,QAEgB;AAChB,SAAO,OAAO,OAAO;AAAA,IACnB,YAAY,OAAO;AAAA,IACnB,cAAc,OAAO;AAAA,IACrB,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO,WAAW;AAAA,IAC3B,MAAM,OAAO,OAAO,CAAC,GAAI,OAAO,QAAQ,CAAC,CAAE,CAAC;AAAA,IAC5C,UAAU,OAAO,YAAY;AAAA,IAC7B,QAAQ,OAAO,UAAU;AAAA,IACzB,aAAa,OAAO,eAAe;AAAA,EACrC,CAAC;AACH;AAkBO,SAAS,uBACd,QAEkB;AAClB,SAAO,OAAO,OAAO;AAAA,IACnB,SAAS,OAAO;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,WAAW,OAAO,OAAO,CAAC,GAAI,OAAO,aAAa,CAAC,CAAE,CAAC;AAAA,IACtD,aAAa,OAAO,OAAO,CAAC,GAAI,OAAO,eAAe,CAAC,CAAE,CAAC;AAAA,IAC1D,aAAa,OAAO,OAAO,CAAC,GAAI,OAAO,eAAe,CAAC,CAAE,CAAC;AAAA,IAC1D,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,aAAa,OAAO,eAAe;AAAA,EACrC,CAAC;AACH;","names":[]}