@odingard/guardian-sdk 0.0.0-stage → 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.
package/dist/index.js ADDED
@@ -0,0 +1,3248 @@
1
+ // src/client.ts
2
+ import { randomBytes } from "node:crypto";
3
+
4
+ // src/proposal.ts
5
+ import { z as z16 } from "zod";
6
+
7
+ // src/contracts/action.ts
8
+ import { z as z3 } from "zod";
9
+
10
+ // src/contracts/authority.ts
11
+ import { z as z2 } from "zod";
12
+
13
+ // src/contracts/identifiers.ts
14
+ import { z } from "zod";
15
+ function identifier(prefix, brand) {
16
+ return z.string().regex(
17
+ new RegExp(`^${prefix}_[A-Za-z0-9][A-Za-z0-9_-]{7,119}$`),
18
+ `${brand} must use the ${prefix}_ prefix and contain 8-120 safe characters`
19
+ ).brand();
20
+ }
21
+ var TenantIdSchema = identifier("ten", "TenantId");
22
+ var PrincipalIdSchema = identifier("pri", "PrincipalId");
23
+ var AgentIdSchema = identifier("agt", "AgentId");
24
+ var GrantIdSchema = identifier("grt", "GrantId");
25
+ var PolicyIdSchema = identifier("pol", "PolicyId");
26
+ var RequestIdSchema = identifier("req", "RequestId");
27
+ var ApprovalIdSchema = identifier("apr", "ApprovalId");
28
+ var CapabilityIdSchema = identifier("cap", "CapabilityId");
29
+ var CorrelationIdSchema = identifier("cor", "CorrelationId");
30
+ var EvidenceIdSchema = identifier("evd", "EvidenceId");
31
+ var AdapterIdSchema = identifier("adp", "AdapterId");
32
+ var ConnectionIdSchema = identifier("pcn", "ConnectionId");
33
+ var AttemptIdSchema = identifier("att", "AttemptId");
34
+ var BudgetIdSchema = identifier("bdg", "BudgetId");
35
+ var TaskIdSchema = identifier("tsk", "TaskId");
36
+ var NonceSchema = z.string().regex(
37
+ /^nce_[A-Za-z0-9_-]{22,116}$/,
38
+ "Nonce must use the nce_ prefix and contain 22-116 base64url-safe characters"
39
+ ).brand();
40
+
41
+ // src/contracts/authority.ts
42
+ var uniqueStrings = (values) => new Set(values).size === values.length;
43
+ var sorted = (values) => [...values].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
44
+ var CentsSchema = z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
45
+ var FactLabelSchema = z2.string().trim().min(1).max(256);
46
+ var RecurrencePolicySchema = z2.enum(["never", "allowed"]);
47
+ var BudgetConsumptionSchema = z2.object({
48
+ budgetId: BudgetIdSchema,
49
+ amountCents: CentsSchema,
50
+ transactions: z2.number().int().positive().max(Number.MAX_SAFE_INTEGER)
51
+ }).strict();
52
+ var ActionTypeSchema = z2.string().trim().min(1).max(128).regex(/^[a-z][a-z0-9]*(?:[._:-][a-z0-9]+)*$/).brand();
53
+ var ResourceRefSchema = z2.string().trim().min(1).max(512).refine(
54
+ (value) => value.normalize("NFC") === value,
55
+ "resource references must already use NFC normalization"
56
+ ).brand();
57
+ var EpochMillisecondsSchema = z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
58
+ var AgentInterfaceSchema = z2.enum(["internal", "mcp", "rest"]);
59
+ var PrincipalSchema = z2.object({
60
+ id: PrincipalIdSchema,
61
+ tenantId: TenantIdSchema,
62
+ status: z2.enum(["active", "disabled"])
63
+ }).strict();
64
+ var BudgetStateSchema = z2.object({
65
+ id: BudgetIdSchema,
66
+ scope: z2.enum(["agent", "task"]),
67
+ taskId: TaskIdSchema.optional(),
68
+ limitCents: CentsSchema,
69
+ consumedCents: CentsSchema,
70
+ transactionLimit: z2.number().int().positive().max(Number.MAX_SAFE_INTEGER),
71
+ consumedTransactions: z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
72
+ expiresAtEpochMs: EpochMillisecondsSchema.optional(),
73
+ status: z2.enum(["active", "exhausted", "revoked"])
74
+ }).strict().superRefine((budget, context) => {
75
+ if (budget.scope === "task" && budget.taskId === void 0) {
76
+ context.addIssue({
77
+ code: "custom",
78
+ message: "task-scoped budgets must carry a taskId",
79
+ path: ["taskId"]
80
+ });
81
+ }
82
+ });
83
+ var AgentIdentitySchema = z2.object({
84
+ id: AgentIdSchema,
85
+ tenantId: TenantIdSchema,
86
+ ownerPrincipalId: PrincipalIdSchema,
87
+ status: z2.enum(["active", "disabled", "revoked"]),
88
+ allowedInterfaces: z2.array(AgentInterfaceSchema).min(1).refine(uniqueStrings, "allowedInterfaces must not contain duplicates").transform(sorted)
89
+ }).strict();
90
+ var AuthorityConstraintsSchema = z2.object({
91
+ actions: z2.array(ActionTypeSchema).min(1).max(64).refine(uniqueStrings, "actions must not contain duplicates").transform(sorted),
92
+ resources: z2.array(ResourceRefSchema).min(1).max(256).refine(uniqueStrings, "resources must not contain duplicates").transform(sorted),
93
+ adapterIds: z2.array(AdapterIdSchema).min(1).max(64).refine(uniqueStrings, "adapterIds must not contain duplicates").transform(sorted),
94
+ maxValueCents: z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
95
+ maxUsesPerRequest: z2.number().int().positive().max(Number.MAX_SAFE_INTEGER),
96
+ notBeforeEpochMs: EpochMillisecondsSchema.optional(),
97
+ expiresAtEpochMs: EpochMillisecondsSchema,
98
+ approvalRequired: z2.boolean(),
99
+ askAboveValueCents: CentsSchema.optional(),
100
+ merchantAllowlist: z2.array(FactLabelSchema).min(1).max(256).refine(uniqueStrings, "merchantAllowlist must not contain duplicates").transform(sorted).optional(),
101
+ categoryAllowlist: z2.array(FactLabelSchema).min(1).max(256).refine(uniqueStrings, "categoryAllowlist must not contain duplicates").transform(sorted).optional(),
102
+ recurrence: RecurrencePolicySchema.optional(),
103
+ budgetIds: z2.array(BudgetIdSchema).min(1).max(64).refine(uniqueStrings, "budgetIds must not contain duplicates").transform(sorted).optional()
104
+ }).strict().superRefine((constraints, context) => {
105
+ if (constraints.askAboveValueCents !== void 0 && constraints.maxValueCents !== void 0 && constraints.askAboveValueCents >= constraints.maxValueCents) {
106
+ context.addIssue({
107
+ code: "custom",
108
+ message: "askAboveValueCents must be below maxValueCents",
109
+ path: ["askAboveValueCents"]
110
+ });
111
+ }
112
+ }).superRefine((constraints, context) => {
113
+ if (constraints.notBeforeEpochMs !== void 0 && constraints.expiresAtEpochMs !== void 0 && constraints.notBeforeEpochMs >= constraints.expiresAtEpochMs) {
114
+ context.addIssue({
115
+ code: "custom",
116
+ message: "notBeforeEpochMs must be earlier than expiresAtEpochMs",
117
+ path: ["expiresAtEpochMs"]
118
+ });
119
+ }
120
+ });
121
+ var AuthorityGrantSchema = z2.object({
122
+ id: GrantIdSchema,
123
+ tenantId: TenantIdSchema,
124
+ principalId: PrincipalIdSchema,
125
+ agentId: AgentIdSchema,
126
+ parentGrantId: GrantIdSchema.optional(),
127
+ issuedBy: z2.object({
128
+ type: z2.literal("principal"),
129
+ principalId: PrincipalIdSchema
130
+ }).strict(),
131
+ status: z2.enum(["active", "revoked", "disabled"]),
132
+ version: z2.number().int().positive(),
133
+ delegable: z2.boolean().default(false),
134
+ constraints: AuthorityConstraintsSchema
135
+ }).strict().superRefine((grant, context) => {
136
+ if (grant.issuedBy.principalId !== grant.principalId) {
137
+ context.addIssue({
138
+ code: "custom",
139
+ message: "issuedBy principal must match the grant principal",
140
+ path: ["issuedBy", "principalId"]
141
+ });
142
+ }
143
+ if (grant.parentGrantId === grant.id) {
144
+ context.addIssue({
145
+ code: "custom",
146
+ message: "a grant cannot be its own parent",
147
+ path: ["parentGrantId"]
148
+ });
149
+ }
150
+ });
151
+ var AuthorityRequestSchema = z2.object({
152
+ id: RequestIdSchema,
153
+ tenantId: TenantIdSchema,
154
+ principalId: PrincipalIdSchema,
155
+ agentId: AgentIdSchema,
156
+ action: ActionTypeSchema,
157
+ resource: ResourceRefSchema,
158
+ interface: AgentInterfaceSchema,
159
+ requestedAdapterId: AdapterIdSchema,
160
+ valueCents: z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
161
+ uses: z2.number().int().positive().max(Number.MAX_SAFE_INTEGER),
162
+ materialActionDigest: z2.string().regex(/^[a-f0-9]{64}$/).optional(),
163
+ descriptiveContext: z2.string().max(8192).optional(),
164
+ merchantId: FactLabelSchema.optional(),
165
+ merchantCategory: FactLabelSchema.optional(),
166
+ recurring: z2.boolean().optional(),
167
+ taskId: TaskIdSchema.optional()
168
+ }).strict();
169
+ var DenyReasonCodeSchema = z2.enum([
170
+ "MALFORMED_INPUT",
171
+ "TENANT_MISMATCH",
172
+ "PRINCIPAL_MISMATCH",
173
+ "PRINCIPAL_NOT_ACTIVE",
174
+ "AGENT_TENANT_MISMATCH",
175
+ "AGENT_OWNER_MISMATCH",
176
+ "AGENT_MISMATCH",
177
+ "AGENT_NOT_ACTIVE",
178
+ "INTERFACE_NOT_ALLOWED",
179
+ "AUTHORITY_CHAIN_MISSING",
180
+ "DUPLICATE_GRANT_ID",
181
+ "GRANT_TENANT_MISMATCH",
182
+ "GRANT_PRINCIPAL_MISMATCH",
183
+ "GRANT_AGENT_MISMATCH",
184
+ "GRANT_NOT_ACTIVE",
185
+ "GRANT_REVOKED",
186
+ "GRANT_NOT_YET_VALID",
187
+ "GRANT_EXPIRED",
188
+ "INVALID_PARENT_LINK",
189
+ "DELEGATION_NOT_ALLOWED",
190
+ "AUTHORITY_AMPLIFICATION",
191
+ "ACTION_NOT_ALLOWED",
192
+ "RESOURCE_NOT_ALLOWED",
193
+ "ADAPTER_NOT_ALLOWED",
194
+ "VALUE_LIMIT_EXCEEDED",
195
+ "USE_LIMIT_EXCEEDED",
196
+ "MERCHANT_NOT_ALLOWED",
197
+ "CATEGORY_NOT_ALLOWED",
198
+ "RECURRENCE_NOT_ALLOWED",
199
+ "BUDGET_NOT_FOUND",
200
+ "BUDGET_SCOPE_MISMATCH",
201
+ "BUDGET_NOT_ACTIVE",
202
+ "BUDGET_EXPIRED",
203
+ "BUDGET_EXCEEDED",
204
+ "TRANSACTION_LIMIT_EXCEEDED"
205
+ ]);
206
+ var PolicyReferenceSchema = z2.object({
207
+ id: PolicyIdSchema,
208
+ version: z2.number().int().positive()
209
+ }).strict();
210
+ var DecisionBindingSchema = z2.object({
211
+ requestId: RequestIdSchema.nullable(),
212
+ policy: PolicyReferenceSchema.nullable(),
213
+ evaluatedGrants: z2.array(
214
+ z2.object({
215
+ id: GrantIdSchema,
216
+ version: z2.number().int().positive()
217
+ }).strict()
218
+ ).max(32),
219
+ inputDigest: z2.string().regex(/^[a-f0-9]{64}$/).nullable(),
220
+ budgetConsumption: z2.array(BudgetConsumptionSchema).max(64).optional()
221
+ }).strict();
222
+ var AuthorityDecisionSchema = z2.discriminatedUnion("decision", [
223
+ z2.object({
224
+ decision: z2.literal("ALLOW"),
225
+ reasonCode: z2.literal("AUTHORIZED"),
226
+ binding: DecisionBindingSchema
227
+ }).strict(),
228
+ z2.object({
229
+ decision: z2.literal("ASK"),
230
+ reasonCode: z2.literal("APPROVAL_REQUIRED"),
231
+ binding: DecisionBindingSchema
232
+ }).strict(),
233
+ z2.object({
234
+ decision: z2.literal("DENY"),
235
+ reasonCode: DenyReasonCodeSchema,
236
+ binding: DecisionBindingSchema
237
+ }).strict()
238
+ ]);
239
+ var RevocationSchema = z2.object({
240
+ grantId: GrantIdSchema,
241
+ revokedAtEpochMs: EpochMillisecondsSchema,
242
+ reasonCode: z2.string().trim().min(1).max(128)
243
+ }).strict();
244
+
245
+ // src/contracts/canonical-json.ts
246
+ var MAX_CANONICAL_NODES = 1e5;
247
+ function serialize(value, ancestors, depth = 0, budget = { remainingNodes: MAX_CANONICAL_NODES }) {
248
+ budget.remainingNodes -= 1;
249
+ if (budget.remainingNodes < 0) {
250
+ throw new TypeError("Canonical JSON exceeds maximum node count");
251
+ }
252
+ if (depth > 64) throw new TypeError("Canonical JSON exceeds maximum depth");
253
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
254
+ return JSON.stringify(value);
255
+ }
256
+ if (typeof value === "number") {
257
+ if (!Number.isFinite(value)) {
258
+ throw new TypeError("Canonical JSON does not permit non-finite numbers");
259
+ }
260
+ return JSON.stringify(Object.is(value, -0) ? 0 : value);
261
+ }
262
+ if (Array.isArray(value)) {
263
+ if (ancestors.has(value))
264
+ throw new TypeError("Canonical JSON rejects cycles");
265
+ ancestors.add(value);
266
+ const keys2 = Reflect.ownKeys(value);
267
+ const expectedKeys = /* @__PURE__ */ new Set([
268
+ "length",
269
+ ...Array.from({ length: value.length }, (_, index) => String(index))
270
+ ]);
271
+ if (keys2.some((key) => typeof key !== "string" || !expectedKeys.has(key)) || keys2.length !== expectedKeys.size) {
272
+ ancestors.delete(value);
273
+ throw new TypeError(
274
+ "Canonical JSON rejects sparse arrays and extra properties"
275
+ );
276
+ }
277
+ const entries2 = Array.from({ length: value.length }, (_, index) => {
278
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
279
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
280
+ throw new TypeError(
281
+ "Canonical JSON rejects accessors and hidden values"
282
+ );
283
+ }
284
+ return serialize(descriptor.value, ancestors, depth + 1, budget);
285
+ });
286
+ const result2 = `[${entries2.join(",")}]`;
287
+ ancestors.delete(value);
288
+ return result2;
289
+ }
290
+ if (typeof value !== "object" || value === void 0) {
291
+ throw new TypeError(`Canonical JSON rejects ${typeof value}`);
292
+ }
293
+ const prototype = Object.getPrototypeOf(value);
294
+ if (prototype !== Object.prototype && prototype !== null) {
295
+ throw new TypeError("Canonical JSON accepts only plain objects and arrays");
296
+ }
297
+ if (ancestors.has(value))
298
+ throw new TypeError("Canonical JSON rejects cycles");
299
+ ancestors.add(value);
300
+ const keys = Reflect.ownKeys(value);
301
+ if (keys.some((key) => typeof key !== "string")) {
302
+ ancestors.delete(value);
303
+ throw new TypeError("Canonical JSON rejects symbol keys");
304
+ }
305
+ const entries = keys.map((key) => {
306
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
307
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
308
+ throw new TypeError(
309
+ "Canonical JSON rejects accessors and hidden values"
310
+ );
311
+ }
312
+ return [key, descriptor.value];
313
+ }).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
314
+ const result = `{${entries.map(
315
+ ([key, entry]) => `${JSON.stringify(key)}:${serialize(entry, ancestors, depth + 1, budget)}`
316
+ ).join(",")}}`;
317
+ ancestors.delete(value);
318
+ return result;
319
+ }
320
+ function canonicalJson(value) {
321
+ return serialize(value, /* @__PURE__ */ new WeakSet());
322
+ }
323
+
324
+ // src/contracts/digest.ts
325
+ import { createHash } from "node:crypto";
326
+ function canonicalSha256(value) {
327
+ return createHash("sha256").update(canonicalJson(value), "utf8").digest("hex");
328
+ }
329
+ function sha256Text(value) {
330
+ return createHash("sha256").update(value, "utf8").digest("hex");
331
+ }
332
+
333
+ // src/contracts/action.ts
334
+ var SHA256Schema = z3.string().regex(/^[a-f0-9]{64}$/);
335
+ var MAX_MATERIAL_BYTES = 32768;
336
+ var MAX_MATERIAL_NODES = 4096;
337
+ function normalizeCanonicalJson(value, depth = 0, ancestors = /* @__PURE__ */ new WeakSet(), budget = { remainingNodes: MAX_MATERIAL_NODES }) {
338
+ budget.remainingNodes -= 1;
339
+ if (budget.remainingNodes < 0) {
340
+ throw new TypeError("material parameters exceed maximum node count");
341
+ }
342
+ if (depth > 8)
343
+ throw new TypeError("material parameters exceed maximum depth");
344
+ if (value === null || typeof value === "boolean") return value;
345
+ if (typeof value === "string") {
346
+ if (value.length > 8192 || value.normalize("NFC") !== value) {
347
+ throw new TypeError("material string is too long or not NFC-normalized");
348
+ }
349
+ return value;
350
+ }
351
+ if (typeof value === "number") {
352
+ if (!Number.isFinite(value) || Math.abs(value) > Number.MAX_SAFE_INTEGER || Object.is(value, -0)) {
353
+ throw new TypeError("material number is not canonical");
354
+ }
355
+ return value;
356
+ }
357
+ if (typeof value !== "object" || value === void 0 || ancestors.has(value)) {
358
+ throw new TypeError(
359
+ "material parameters contain an unsupported value or cycle"
360
+ );
361
+ }
362
+ ancestors.add(value);
363
+ try {
364
+ if (Array.isArray(value)) {
365
+ if (value.length > 256)
366
+ throw new TypeError("material array is too large");
367
+ const keys2 = Reflect.ownKeys(value);
368
+ const expectedKeys = /* @__PURE__ */ new Set([
369
+ "length",
370
+ ...Array.from({ length: value.length }, (_, index) => String(index))
371
+ ]);
372
+ if (keys2.some((key) => typeof key !== "string" || !expectedKeys.has(key)) || keys2.length !== expectedKeys.size) {
373
+ throw new TypeError(
374
+ "material arrays must be dense and have no extra properties"
375
+ );
376
+ }
377
+ const normalized2 = Array.from({ length: value.length }, (_, index) => {
378
+ const descriptor = Object.getOwnPropertyDescriptor(
379
+ value,
380
+ String(index)
381
+ );
382
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
383
+ throw new TypeError(
384
+ "material arrays may contain only enumerable data values"
385
+ );
386
+ }
387
+ return normalizeCanonicalJson(
388
+ descriptor.value,
389
+ depth + 1,
390
+ ancestors,
391
+ budget
392
+ );
393
+ });
394
+ Object.freeze(normalized2);
395
+ return normalized2;
396
+ }
397
+ const prototype = Object.getPrototypeOf(value);
398
+ if (prototype !== Object.prototype && prototype !== null) {
399
+ throw new TypeError(
400
+ "material parameters accept only plain objects and arrays"
401
+ );
402
+ }
403
+ const keys = Reflect.ownKeys(value);
404
+ if (keys.length > 128 || keys.some((key) => typeof key !== "string")) {
405
+ throw new TypeError(
406
+ "material objects are too large or contain symbol keys"
407
+ );
408
+ }
409
+ const normalized = /* @__PURE__ */ Object.create(
410
+ null
411
+ );
412
+ for (const key of keys) {
413
+ if (key.length === 0 || key.length > 128 || key.normalize("NFC") !== key || ["__proto__", "constructor", "prototype"].includes(key)) {
414
+ throw new TypeError("material object key is invalid");
415
+ }
416
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
417
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
418
+ throw new TypeError(
419
+ "material objects may contain only enumerable data properties"
420
+ );
421
+ }
422
+ Object.defineProperty(normalized, key, {
423
+ value: normalizeCanonicalJson(
424
+ descriptor.value,
425
+ depth + 1,
426
+ ancestors,
427
+ budget
428
+ ),
429
+ enumerable: true,
430
+ writable: false,
431
+ configurable: false
432
+ });
433
+ }
434
+ return Object.freeze(normalized);
435
+ } finally {
436
+ ancestors.delete(value);
437
+ }
438
+ }
439
+ function normalizeMaterialParameters(value) {
440
+ const normalized = normalizeCanonicalJson(value);
441
+ if (normalized === null || Array.isArray(normalized) || typeof normalized !== "object") {
442
+ throw new TypeError("material parameters must be an object");
443
+ }
444
+ if (new TextEncoder().encode(canonicalJson(normalized)).length > MAX_MATERIAL_BYTES) {
445
+ throw new TypeError("material parameters exceed maximum encoded size");
446
+ }
447
+ return normalized;
448
+ }
449
+ var MaterialParametersSchema = z3.unknown().transform((value, context) => {
450
+ try {
451
+ return normalizeMaterialParameters(value);
452
+ } catch {
453
+ context.addIssue({
454
+ code: "custom",
455
+ message: "material parameters must be a bounded canonical JSON object"
456
+ });
457
+ return z3.NEVER;
458
+ }
459
+ });
460
+ var AuthorityReferenceSchema = z3.object({
461
+ policy: PolicyReferenceSchema,
462
+ evaluatedGrants: z3.array(
463
+ z3.object({
464
+ id: GrantIdSchema,
465
+ version: z3.number().int().positive()
466
+ }).strict()
467
+ ).min(1).max(32).refine(
468
+ (grants) => new Set(grants.map((grant) => grant.id)).size === grants.length,
469
+ "evaluated grant identifiers must be unique"
470
+ )
471
+ }).strict();
472
+ var MaterialActionSchema = z3.object({
473
+ schemaVersion: z3.literal(1),
474
+ requestId: RequestIdSchema,
475
+ correlationId: CorrelationIdSchema,
476
+ tenantId: TenantIdSchema,
477
+ principalId: PrincipalIdSchema,
478
+ agentId: AgentIdSchema,
479
+ action: ActionTypeSchema,
480
+ resource: ResourceRefSchema,
481
+ interface: AgentInterfaceSchema,
482
+ valueCents: z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
483
+ merchantId: z3.string().trim().min(1).max(256).optional(),
484
+ merchantCategory: z3.string().trim().min(1).max(256).optional(),
485
+ recurring: z3.boolean().optional(),
486
+ taskId: TaskIdSchema.optional(),
487
+ uses: z3.number().int().positive().max(Number.MAX_SAFE_INTEGER),
488
+ materialParameters: MaterialParametersSchema,
489
+ initiatingContext: z3.enum(["user", "scheduled", "agent", "external-event"]),
490
+ authority: AuthorityReferenceSchema,
491
+ issuedAtEpochMs: EpochMillisecondsSchema,
492
+ expiresAtEpochMs: EpochMillisecondsSchema,
493
+ nonce: NonceSchema,
494
+ requestedAdapterId: AdapterIdSchema
495
+ }).strict().superRefine((value, context) => {
496
+ if (!Object.prototype.hasOwnProperty.call(value, "materialParameters")) {
497
+ context.addIssue({
498
+ code: "custom",
499
+ path: ["materialParameters"],
500
+ message: "materialParameters must be explicitly present"
501
+ });
502
+ }
503
+ if (value.issuedAtEpochMs >= value.expiresAtEpochMs) {
504
+ context.addIssue({
505
+ code: "custom",
506
+ path: ["expiresAtEpochMs"],
507
+ message: "expiresAtEpochMs must be later than issuedAtEpochMs"
508
+ });
509
+ }
510
+ });
511
+ var CanonicalActionEnvelopeSchema = MaterialActionSchema.extend({
512
+ purpose: z3.string().trim().min(1).max(1024).refine((value) => value.normalize("NFC") === value)
513
+ }).strict();
514
+ var CanonicalActionSchema = CanonicalActionEnvelopeSchema.extend({
515
+ materialDigest: SHA256Schema
516
+ }).strict().superRefine((value, context) => {
517
+ try {
518
+ const material = MaterialActionSchema.strip().parse(value);
519
+ if (materialActionDigest(material) === value.materialDigest) return;
520
+ context.addIssue({
521
+ code: "custom",
522
+ path: ["materialDigest"],
523
+ message: "materialDigest does not match the canonical action"
524
+ });
525
+ } catch {
526
+ context.addIssue({
527
+ code: "custom",
528
+ path: ["materialDigest"],
529
+ message: "canonical action cannot be safely digested"
530
+ });
531
+ }
532
+ });
533
+ var RecordedAuthorityEvaluationInputSchema = z3.object({
534
+ nowEpochMs: EpochMillisecondsSchema,
535
+ principal: PrincipalSchema,
536
+ agent: AgentIdentitySchema,
537
+ request: AuthorityRequestSchema,
538
+ policy: PolicyReferenceSchema,
539
+ authorityChain: z3.array(AuthorityGrantSchema).max(32),
540
+ revocations: z3.array(RevocationSchema).max(256).transform(
541
+ (values) => [...values].sort(
542
+ (left, right) => left.grantId < right.grantId ? -1 : left.grantId > right.grantId ? 1 : left.revokedAtEpochMs - right.revokedAtEpochMs
543
+ )
544
+ ),
545
+ budgets: z3.array(BudgetStateSchema).max(64).default([]).transform(
546
+ (values) => [...values].sort((left, right) => left.id < right.id ? -1 : 1)
547
+ )
548
+ }).strict();
549
+ var ActionDecisionRecordCoreSchema = z3.object({
550
+ schemaVersion: z3.literal(1),
551
+ tenantId: TenantIdSchema,
552
+ principalId: PrincipalIdSchema,
553
+ agentId: AgentIdSchema,
554
+ requestId: RequestIdSchema,
555
+ correlationId: CorrelationIdSchema,
556
+ actionDigest: SHA256Schema,
557
+ policy: PolicyReferenceSchema,
558
+ evaluatedGrants: AuthorityReferenceSchema.shape.evaluatedGrants,
559
+ action: CanonicalActionSchema,
560
+ authorityRequest: AuthorityRequestSchema,
561
+ authorityEvaluationInput: RecordedAuthorityEvaluationInputSchema,
562
+ authorityDecision: AuthorityDecisionSchema,
563
+ decidedAtEpochMs: EpochMillisecondsSchema
564
+ }).strict();
565
+ var ActionDecisionRecordSchema = ActionDecisionRecordCoreSchema.extend(
566
+ {
567
+ recordDigest: SHA256Schema
568
+ }
569
+ ).strict().superRefine((value, context) => {
570
+ try {
571
+ const binding = value.authorityDecision.binding;
572
+ const expectedRequest = toAuthorityRequest(value.action);
573
+ if (value.tenantId !== value.action.tenantId || value.principalId !== value.action.principalId || value.agentId !== value.action.agentId || value.requestId !== value.action.requestId || value.correlationId !== value.action.correlationId || value.actionDigest !== value.action.materialDigest) {
574
+ context.addIssue({
575
+ code: "custom",
576
+ path: ["actionDigest"],
577
+ message: "action decision context does not match the canonical action"
578
+ });
579
+ }
580
+ if (value.policy.id !== value.action.authority.policy.id || value.policy.version !== value.action.authority.policy.version || canonicalSha256(value.evaluatedGrants) !== canonicalSha256(value.action.authority.evaluatedGrants)) {
581
+ context.addIssue({
582
+ code: "custom",
583
+ path: ["policy"],
584
+ message: "action decision authority references do not match the action"
585
+ });
586
+ }
587
+ if (canonicalSha256(value.authorityRequest) !== canonicalSha256(expectedRequest)) {
588
+ context.addIssue({
589
+ code: "custom",
590
+ path: ["authorityRequest"],
591
+ message: "authority request does not match the canonical action"
592
+ });
593
+ }
594
+ if (canonicalSha256(value.authorityEvaluationInput.request) !== canonicalSha256(expectedRequest) || canonicalSha256(value.authorityEvaluationInput.policy) !== canonicalSha256(value.action.authority.policy) || value.authorityEvaluationInput.principal.id !== value.action.principalId || value.authorityEvaluationInput.principal.tenantId !== value.action.tenantId || value.authorityEvaluationInput.agent.id !== value.action.agentId || value.authorityEvaluationInput.agent.tenantId !== value.action.tenantId || value.authorityEvaluationInput.agent.ownerPrincipalId !== value.action.principalId || value.authorityEvaluationInput.principal.status !== "active" || value.authorityEvaluationInput.agent.status !== "active" || !value.authorityEvaluationInput.agent.allowedInterfaces.includes(
595
+ value.action.interface
596
+ ) || value.authorityEvaluationInput.authorityChain.some(
597
+ (grant) => grant.tenantId !== value.action.tenantId || grant.principalId !== value.action.principalId || grant.agentId !== value.action.agentId || grant.status !== "active"
598
+ )) {
599
+ context.addIssue({
600
+ code: "custom",
601
+ path: ["authorityEvaluationInput"],
602
+ message: "evaluated authority input does not match the canonical action"
603
+ });
604
+ }
605
+ if (binding.requestId !== value.action.requestId) {
606
+ context.addIssue({
607
+ code: "custom",
608
+ path: ["authorityDecision", "binding", "requestId"],
609
+ message: "decision request binding does not match the action"
610
+ });
611
+ }
612
+ if (binding.policy === null || binding.policy.id !== value.action.authority.policy.id || binding.policy.version !== value.action.authority.policy.version) {
613
+ context.addIssue({
614
+ code: "custom",
615
+ path: ["authorityDecision", "binding", "policy"],
616
+ message: "decision policy binding does not match the action"
617
+ });
618
+ }
619
+ if (canonicalSha256(binding.evaluatedGrants) !== canonicalSha256(value.action.authority.evaluatedGrants)) {
620
+ context.addIssue({
621
+ code: "custom",
622
+ path: ["authorityDecision", "binding", "evaluatedGrants"],
623
+ message: "decision grant bindings do not match the action"
624
+ });
625
+ }
626
+ if (binding.inputDigest === null || binding.inputDigest !== canonicalSha256(value.authorityEvaluationInput)) {
627
+ context.addIssue({
628
+ code: "custom",
629
+ path: ["authorityDecision", "binding", "inputDigest"],
630
+ message: "decision input digest does not match the evaluated authority input"
631
+ });
632
+ }
633
+ const grantNotBeforeEpochMs = Math.max(
634
+ 0,
635
+ ...value.authorityEvaluationInput.authorityChain.map(
636
+ (grant) => grant.constraints.notBeforeEpochMs ?? 0
637
+ )
638
+ );
639
+ const grantExpiresAtEpochMs = Math.min(
640
+ ...value.authorityEvaluationInput.authorityChain.map(
641
+ (grant) => grant.constraints.expiresAtEpochMs
642
+ )
643
+ );
644
+ if (value.authorityEvaluationInput.nowEpochMs !== value.decidedAtEpochMs || value.decidedAtEpochMs < value.action.issuedAtEpochMs || value.decidedAtEpochMs >= value.action.expiresAtEpochMs) {
645
+ context.addIssue({
646
+ code: "custom",
647
+ path: ["decidedAtEpochMs"],
648
+ message: "decision time must fall within the action validity window"
649
+ });
650
+ }
651
+ if (value.authorityEvaluationInput.authorityChain.length === 0 || value.action.issuedAtEpochMs < grantNotBeforeEpochMs || value.action.expiresAtEpochMs > grantExpiresAtEpochMs) {
652
+ context.addIssue({
653
+ code: "custom",
654
+ path: ["action", "expiresAtEpochMs"],
655
+ message: "action validity window must be contained by evaluated grants"
656
+ });
657
+ }
658
+ const evaluatedGrantReferences = value.authorityEvaluationInput.authorityChain.map(
659
+ ({ id, version }) => ({
660
+ id,
661
+ version
662
+ })
663
+ );
664
+ if (canonicalSha256(evaluatedGrantReferences) !== canonicalSha256(value.action.authority.evaluatedGrants)) {
665
+ context.addIssue({
666
+ code: "custom",
667
+ path: ["authorityEvaluationInput", "authorityChain"],
668
+ message: "evaluated authority chain does not match declared grants"
669
+ });
670
+ }
671
+ const core = ActionDecisionRecordCoreSchema.strip().parse(value);
672
+ if (actionDecisionRecordDigest(core) !== value.recordDigest) {
673
+ context.addIssue({
674
+ code: "custom",
675
+ path: ["recordDigest"],
676
+ message: "recordDigest does not match the action decision record"
677
+ });
678
+ }
679
+ } catch {
680
+ context.addIssue({
681
+ code: "custom",
682
+ path: ["recordDigest"],
683
+ message: "action decision record cannot be safely verified"
684
+ });
685
+ }
686
+ });
687
+ var MATERIAL_ACTION_KEYS = Object.keys(
688
+ MaterialActionSchema.shape
689
+ );
690
+ function materialActionDigest(material) {
691
+ return canonicalSha256({
692
+ domain: "odingard.guardian.action.v1",
693
+ materialAction: material
694
+ });
695
+ }
696
+ function actionDecisionRecordDigest(record) {
697
+ return canonicalSha256({
698
+ domain: "odingard.guardian.action-decision.v1",
699
+ record
700
+ });
701
+ }
702
+ function toAuthorityRequest(action) {
703
+ return AuthorityRequestSchema.parse({
704
+ id: action.requestId,
705
+ tenantId: action.tenantId,
706
+ principalId: action.principalId,
707
+ agentId: action.agentId,
708
+ action: action.action,
709
+ resource: action.resource,
710
+ interface: action.interface,
711
+ requestedAdapterId: action.requestedAdapterId,
712
+ ...action.valueCents === void 0 ? {} : { valueCents: action.valueCents },
713
+ uses: action.uses,
714
+ materialActionDigest: action.materialDigest,
715
+ descriptiveContext: action.purpose,
716
+ ...action.merchantId === void 0 ? {} : { merchantId: action.merchantId },
717
+ ...action.merchantCategory === void 0 ? {} : { merchantCategory: action.merchantCategory },
718
+ ...action.recurring === void 0 ? {} : { recurring: action.recurring },
719
+ ...action.taskId === void 0 ? {} : { taskId: action.taskId }
720
+ });
721
+ }
722
+
723
+ // src/contracts/email.ts
724
+ import { z as z4 } from "zod";
725
+ var LOCAL_ATEXT = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~.-]+$/;
726
+ var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
727
+ function isNoncharacter(codePoint) {
728
+ return codePoint >= 64976 && codePoint <= 65007 || (codePoint & 65535) === 65534 || (codePoint & 65535) === 65535;
729
+ }
730
+ function hasNoncharacter(value) {
731
+ return [...value].some(
732
+ (character) => isNoncharacter(character.codePointAt(0) ?? 0)
733
+ );
734
+ }
735
+ function isCanonicalEmailAddress(value) {
736
+ if (![...value].every((character) => (character.codePointAt(0) ?? 128) < 128) || value.length > 254 || value.normalize("NFC") !== value) {
737
+ return false;
738
+ }
739
+ const at = value.indexOf("@");
740
+ if (at <= 0 || at !== value.lastIndexOf("@")) return false;
741
+ const local = value.slice(0, at);
742
+ const domain = value.slice(at + 1);
743
+ if (local.length > 64 || !LOCAL_ATEXT.test(local) || local.startsWith(".") || local.endsWith(".") || local.includes("..") || domain.length > 253 || domain !== domain.toLowerCase()) {
744
+ return false;
745
+ }
746
+ const labels = domain.split(".");
747
+ if (labels.length < 2 || labels.some((label) => !DOMAIN_LABEL.test(label))) {
748
+ return false;
749
+ }
750
+ const topLevel = labels.at(-1) ?? "";
751
+ return /^[a-z]{2,63}$/.test(topLevel) || /^xn--[a-z0-9](?:[a-z0-9-]{0,57}[a-z0-9])?$/.test(topLevel);
752
+ }
753
+ function isSafeSubject(value) {
754
+ return value === value.trim() && value.normalize("NFC") === value && [...value].length >= 1 && [...value].length <= 200 && !/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}\p{Co}\p{Cs}\p{Cn}]/u.test(value) && !hasNoncharacter(value);
755
+ }
756
+ function isSafeBody(value) {
757
+ if (value.length < 1 || value.length > 8e3 || value.normalize("NFC") !== value || !/\S/u.test(value) || /\r/u.test(value) || /[\p{Co}\p{Cs}\p{Cn}]/u.test(value) || hasNoncharacter(value)) {
758
+ return false;
759
+ }
760
+ for (const character of value) {
761
+ const codePoint = character.codePointAt(0) ?? 0;
762
+ if (codePoint < 32 && codePoint !== 9 && codePoint !== 10 || codePoint === 127 || codePoint === 1564 || codePoint === 8206 || codePoint === 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8288 && codePoint <= 8292 || codePoint >= 8294 && codePoint <= 8297 || codePoint === 8203 || codePoint === 65279 || codePoint >= 917504 && codePoint <= 917631) {
763
+ return false;
764
+ }
765
+ }
766
+ return true;
767
+ }
768
+ var EmailAddressSchema = z4.string().refine(isCanonicalEmailAddress, "email address must be canonical ASCII");
769
+ var EmailSubjectSchema = z4.string().refine(isSafeSubject, "email subject contains unsafe or noncanonical text");
770
+ var EmailBodyTextSchema = z4.string().refine(isSafeBody, "email body contains unsafe or noncanonical text");
771
+ var EmailSendParametersV1Schema = z4.object({
772
+ schema: z4.literal("odingard.guardian.email.send.v1"),
773
+ connectionId: ConnectionIdSchema,
774
+ from: EmailAddressSchema,
775
+ to: z4.array(EmailAddressSchema).min(1).max(20),
776
+ cc: z4.array(EmailAddressSchema).max(20),
777
+ bcc: z4.array(EmailAddressSchema).max(20),
778
+ replyTo: z4.array(z4.never()).max(0),
779
+ subject: EmailSubjectSchema,
780
+ body: z4.object({ format: z4.literal("text/plain"), text: EmailBodyTextSchema }).strict(),
781
+ attachments: z4.array(z4.never()).max(0),
782
+ inReplyTo: z4.null()
783
+ }).strict().superRefine((value, context) => {
784
+ const recipients = [...value.to, ...value.cc, ...value.bcc].map(
785
+ (address) => address.toLowerCase()
786
+ );
787
+ if (recipients.length > 20) {
788
+ context.addIssue({
789
+ code: "custom",
790
+ path: ["to"],
791
+ message: "at most 20 total recipients are allowed"
792
+ });
793
+ }
794
+ if (new Set(recipients).size !== recipients.length) {
795
+ context.addIssue({
796
+ code: "custom",
797
+ path: ["to"],
798
+ message: "recipient addresses must be unique across all lists"
799
+ });
800
+ }
801
+ });
802
+ function canonicalBodyForComparison(text) {
803
+ return text.replace(/\r\n?/gu, "\n").replace(/[ \t\n]*$/u, "");
804
+ }
805
+ function canonicalRecipients(addresses) {
806
+ return [...new Set(addresses.map((address) => address.toLowerCase()))].sort();
807
+ }
808
+ function emailVerificationProjection(input) {
809
+ const params = EmailSendParametersV1Schema.parse(input);
810
+ return Object.freeze({
811
+ domain: "odingard.guardian.email-projection.v1",
812
+ sender: params.from.toLowerCase(),
813
+ to: canonicalRecipients(params.to),
814
+ cc: canonicalRecipients(params.cc),
815
+ bcc: canonicalRecipients(params.bcc),
816
+ replyTo: [],
817
+ subject: params.subject,
818
+ bodyDigest: sha256Text(canonicalBodyForComparison(params.body.text)),
819
+ attachmentCount: 0,
820
+ inReplyTo: null
821
+ });
822
+ }
823
+ function emailEnvelopeProjection(input) {
824
+ const projection = emailVerificationProjection(input);
825
+ return Object.freeze({
826
+ domain: projection.domain,
827
+ sender: projection.sender,
828
+ to: projection.to,
829
+ cc: projection.cc,
830
+ bcc: projection.bcc,
831
+ replyTo: projection.replyTo,
832
+ subject: projection.subject,
833
+ attachmentCount: projection.attachmentCount,
834
+ inReplyTo: projection.inReplyTo
835
+ });
836
+ }
837
+
838
+ // src/contracts/api.ts
839
+ import { z as z5 } from "zod";
840
+ var ApiRequestMethodSchema = z5.enum([
841
+ "GET",
842
+ "HEAD",
843
+ "POST",
844
+ "PUT",
845
+ "PATCH",
846
+ "DELETE"
847
+ ]);
848
+ var API_REQUEST_BODY_MAX_LENGTH = 16384;
849
+ var API_REQUEST_MAX_HEADERS = 32;
850
+ var API_RESPONSE_BODY_CAPTURE_MAX_BYTES = 16 * 1024;
851
+ var HEADER_NAME = /^[a-z0-9][a-z0-9-]{0,126}$/;
852
+ var FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
853
+ "authorization",
854
+ "proxy-authorization",
855
+ "proxy-authenticate",
856
+ "www-authenticate",
857
+ "host",
858
+ "content-length",
859
+ "connection",
860
+ "keep-alive",
861
+ "transfer-encoding",
862
+ "te",
863
+ "trailer",
864
+ "trailers",
865
+ "upgrade",
866
+ "expect",
867
+ "cookie",
868
+ "set-cookie"
869
+ ]);
870
+ function isSafeApiHeaderValue(value) {
871
+ if (value.length === 0 || value.length > 8192) return false;
872
+ for (const character of value) {
873
+ const codePoint = character.codePointAt(0) ?? 0;
874
+ if (codePoint < 32 && codePoint !== 9 || codePoint === 127 || codePoint > 255) {
875
+ return false;
876
+ }
877
+ }
878
+ return true;
879
+ }
880
+ function canonicalizeApiRequestUrl(value) {
881
+ let url;
882
+ try {
883
+ url = new URL(value);
884
+ } catch {
885
+ return null;
886
+ }
887
+ if (url.protocol !== "https:") return null;
888
+ const urlMap = url;
889
+ if (urlMap.username !== "" || urlMap["password"] !== "") return null;
890
+ if (url.hash !== "") return null;
891
+ if (url.hostname.length === 0) return null;
892
+ url.hostname = url.hostname.toLowerCase();
893
+ if (url.port === "443") url.port = "";
894
+ url.searchParams.sort();
895
+ return url.toString();
896
+ }
897
+ var ApiRequestUrlSchema = z5.string().min(8).max(2048).refine(
898
+ (value) => canonicalizeApiRequestUrl(value) === value,
899
+ "api request url must be an already-canonical https URL"
900
+ );
901
+ var ApiRequestHeaderSchema = z5.object({ name: z5.string().regex(HEADER_NAME), value: z5.string() }).strict();
902
+ var ApiRequestParametersV1Schema = z5.object({
903
+ schema: z5.literal("odingard.guardian.api.request.v1"),
904
+ connectionId: ConnectionIdSchema,
905
+ method: ApiRequestMethodSchema,
906
+ url: ApiRequestUrlSchema,
907
+ headers: z5.array(ApiRequestHeaderSchema).max(API_REQUEST_MAX_HEADERS).refine(
908
+ (headers) => {
909
+ const seen = /* @__PURE__ */ new Set();
910
+ for (const header of headers) {
911
+ if (seen.has(header.name) || FORBIDDEN_HEADERS.has(header.name)) {
912
+ return false;
913
+ }
914
+ seen.add(header.name);
915
+ }
916
+ return true;
917
+ },
918
+ {
919
+ message: "header names must be unique lowercase tokens and must not set auth, routing, or hop-by-hop headers"
920
+ }
921
+ ).refine(
922
+ (headers) => headers.every((h) => isSafeApiHeaderValue(h.value)),
923
+ {
924
+ message: "header values must be printable latin-1 without controls"
925
+ }
926
+ ),
927
+ body: z5.string().min(1).max(API_REQUEST_BODY_MAX_LENGTH).refine(
928
+ (value) => value.normalize("NFC") === value,
929
+ "request body must already use NFC normalization"
930
+ ).optional(),
931
+ idempotencyKey: z5.string().min(8).max(128).regex(/^[A-Za-z0-9._-]+$/u).optional()
932
+ }).strict().superRefine((value, context) => {
933
+ if ((value.method === "GET" || value.method === "HEAD") && value.body !== void 0) {
934
+ context.addIssue({
935
+ code: "custom",
936
+ path: ["body"],
937
+ message: "GET and HEAD requests may not carry a body"
938
+ });
939
+ }
940
+ });
941
+ function canonicalHeaders(headers) {
942
+ return [...headers].map((header) => ({ name: header.name, value: header.value.trim() })).sort((a, b) => a.name.localeCompare(b.name));
943
+ }
944
+ function apiVerificationProjection(input) {
945
+ const params = ApiRequestParametersV1Schema.parse(input);
946
+ return Object.freeze({
947
+ domain: "odingard.guardian.api-projection.v1",
948
+ connectionId: params.connectionId,
949
+ method: params.method,
950
+ url: params.url,
951
+ headersDigest: canonicalSha256({
952
+ domain: "odingard.guardian.api-headers.v1",
953
+ headers: canonicalHeaders(params.headers)
954
+ }),
955
+ bodyDigest: params.body === void 0 ? null : sha256Text(params.body),
956
+ idempotencyKey: params.idempotencyKey ?? null
957
+ });
958
+ }
959
+ function apiEnvelopeProjection(input) {
960
+ return apiVerificationProjection(input);
961
+ }
962
+ function apiEndpointWithinAllowlist(canonicalUrl, allowlist) {
963
+ for (const entry of allowlist) {
964
+ if (canonicalUrl === entry) return true;
965
+ if (entry.endsWith("/") && canonicalUrl.startsWith(entry)) return true;
966
+ if (!entry.endsWith("/") && (canonicalUrl.startsWith(`${entry}/`) || canonicalUrl.startsWith(`${entry}?`))) {
967
+ return true;
968
+ }
969
+ }
970
+ return false;
971
+ }
972
+
973
+ // src/contracts/commerce.ts
974
+ import { z as z6 } from "zod";
975
+ var FactLabelSchema2 = z6.string().trim().min(1).max(256);
976
+ var CentsSchema2 = z6.number().int().positive().max(Number.MAX_SAFE_INTEGER);
977
+ var IdempotencyKeySchema = z6.string().trim().min(8).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9_-]*$/);
978
+ var NfcText = (max) => z6.string().trim().min(1).max(max).refine((value) => value.normalize("NFC") === value);
979
+ var CommercePurchaseParametersV1Schema = z6.object({
980
+ schema: z6.literal("commerce.purchase.v1"),
981
+ connectionId: ConnectionIdSchema,
982
+ merchantId: FactLabelSchema2,
983
+ merchantCategory: FactLabelSchema2,
984
+ amountCents: CentsSchema2,
985
+ currency: z6.literal("USD"),
986
+ orderDescription: NfcText(512),
987
+ idempotencyKey: IdempotencyKeySchema,
988
+ taskId: TaskIdSchema.optional()
989
+ }).strict();
990
+ var CommerceRefundParametersV1Schema = z6.object({
991
+ schema: z6.literal("commerce.refund.v1"),
992
+ connectionId: ConnectionIdSchema,
993
+ chargeId: z6.string().trim().min(1).max(128),
994
+ amountCents: CentsSchema2.optional(),
995
+ reason: NfcText(256)
996
+ }).strict();
997
+ var CommerceReturnParametersV1Schema = z6.object({
998
+ schema: z6.literal("commerce.return.v1"),
999
+ connectionId: ConnectionIdSchema,
1000
+ chargeId: z6.string().trim().min(1).max(128),
1001
+ reason: NfcText(256)
1002
+ }).strict();
1003
+ var SubscriptionCreateParametersV1Schema = z6.object({
1004
+ schema: z6.literal("subscription.create.v1"),
1005
+ connectionId: ConnectionIdSchema,
1006
+ merchantId: FactLabelSchema2,
1007
+ planId: z6.string().trim().min(1).max(128),
1008
+ amountCents: CentsSchema2,
1009
+ currency: z6.literal("USD"),
1010
+ interval: z6.enum(["week", "month", "year"]),
1011
+ idempotencyKey: IdempotencyKeySchema
1012
+ }).strict();
1013
+ function commercePurchaseVerificationProjection(input) {
1014
+ const params = CommercePurchaseParametersV1Schema.parse(input);
1015
+ return Object.freeze({
1016
+ domain: "odingard.guardian.commerce-projection.v1",
1017
+ action: "commerce.purchase",
1018
+ connectionId: params.connectionId,
1019
+ merchantId: params.merchantId,
1020
+ merchantCategory: params.merchantCategory,
1021
+ amountCents: params.amountCents,
1022
+ currency: params.currency,
1023
+ descriptionDigest: sha256Text(params.orderDescription),
1024
+ idempotencyKey: params.idempotencyKey,
1025
+ taskId: params.taskId ?? null
1026
+ });
1027
+ }
1028
+ function commercePurchaseEnvelopeProjection(input) {
1029
+ return commercePurchaseVerificationProjection(input);
1030
+ }
1031
+ function commerceRefundVerificationProjection(input) {
1032
+ const params = CommerceRefundParametersV1Schema.parse(input);
1033
+ return Object.freeze({
1034
+ domain: "odingard.guardian.commerce-projection.v1",
1035
+ action: "commerce.refund",
1036
+ connectionId: params.connectionId,
1037
+ chargeId: params.chargeId,
1038
+ amountCents: params.amountCents ?? null,
1039
+ reasonDigest: sha256Text(params.reason)
1040
+ });
1041
+ }
1042
+ function commerceRefundEnvelopeProjection(input) {
1043
+ return commerceRefundVerificationProjection(input);
1044
+ }
1045
+ function commerceReturnVerificationProjection(input) {
1046
+ const params = CommerceReturnParametersV1Schema.parse(input);
1047
+ return Object.freeze({
1048
+ domain: "odingard.guardian.commerce-projection.v1",
1049
+ action: "commerce.return",
1050
+ connectionId: params.connectionId,
1051
+ chargeId: params.chargeId,
1052
+ reasonDigest: sha256Text(params.reason)
1053
+ });
1054
+ }
1055
+ function commerceReturnEnvelopeProjection(input) {
1056
+ return commerceReturnVerificationProjection(input);
1057
+ }
1058
+ function subscriptionCreateVerificationProjection(input) {
1059
+ const params = SubscriptionCreateParametersV1Schema.parse(input);
1060
+ return Object.freeze({
1061
+ domain: "odingard.guardian.commerce-projection.v1",
1062
+ action: "subscription.create",
1063
+ connectionId: params.connectionId,
1064
+ merchantId: params.merchantId,
1065
+ planId: params.planId,
1066
+ amountCents: params.amountCents,
1067
+ currency: params.currency,
1068
+ interval: params.interval,
1069
+ idempotencyKey: params.idempotencyKey
1070
+ });
1071
+ }
1072
+ function subscriptionCreateEnvelopeProjection(input) {
1073
+ return subscriptionCreateVerificationProjection(input);
1074
+ }
1075
+
1076
+ // src/contracts/calendar.ts
1077
+ import { z as z7 } from "zod";
1078
+ var CalendarIdSchema = z7.string().trim().min(1).max(256).regex(/^[A-Za-z0-9_][A-Za-z0-9_.@%+-]*$/, "invalid calendar id");
1079
+ var EventIdSchema = z7.string().trim().min(1).max(512).regex(/^[A-Za-z0-9_-]+$/, "invalid event id");
1080
+ var EventTimeSchema = z7.object({
1081
+ // RFC 3339 timestamp (what the Google Calendar API accepts).
1082
+ dateTime: z7.string().regex(
1083
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/,
1084
+ "dateTime must be an RFC 3339 timestamp"
1085
+ ),
1086
+ timeZone: z7.string().trim().min(1).max(64).optional()
1087
+ }).strict();
1088
+ var SendUpdatesSchema = z7.enum(["all", "externalOnly", "none"]).optional();
1089
+ var AttendeeSchema = z7.object({
1090
+ email: z7.string().trim().min(3).max(320).regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, "invalid attendee email"),
1091
+ optional: z7.boolean().optional()
1092
+ }).strict();
1093
+ var CalendarBaseSchema = z7.object({
1094
+ connectionId: ConnectionIdSchema,
1095
+ calendarId: CalendarIdSchema
1096
+ });
1097
+ var CalendarCreateParametersV1Schema = CalendarBaseSchema.extend({
1098
+ schema: z7.literal("odingard.guardian.calendar.create.v1"),
1099
+ summary: z7.string().trim().min(1).max(1024),
1100
+ description: z7.string().max(8192).optional(),
1101
+ location: z7.string().max(1024).optional(),
1102
+ start: EventTimeSchema,
1103
+ end: EventTimeSchema,
1104
+ idempotencyKey: z7.string().min(8).max(128).optional()
1105
+ }).strict();
1106
+ var CalendarUpdateParametersV1Schema = CalendarBaseSchema.extend({
1107
+ schema: z7.literal("odingard.guardian.calendar.update.v1"),
1108
+ eventId: EventIdSchema,
1109
+ summary: z7.string().trim().min(1).max(1024).optional(),
1110
+ description: z7.string().max(8192).optional(),
1111
+ location: z7.string().max(1024).optional(),
1112
+ start: EventTimeSchema.optional(),
1113
+ end: EventTimeSchema.optional(),
1114
+ sendUpdates: SendUpdatesSchema
1115
+ }).strict().refine(
1116
+ (params) => params.summary !== void 0 || params.description !== void 0 || params.location !== void 0 || params.start !== void 0 || params.end !== void 0,
1117
+ "calendar.update must carry at least one changed field"
1118
+ );
1119
+ var CalendarCancelParametersV1Schema = CalendarBaseSchema.extend({
1120
+ schema: z7.literal("odingard.guardian.calendar.cancel.v1"),
1121
+ eventId: EventIdSchema,
1122
+ sendUpdates: SendUpdatesSchema
1123
+ }).strict();
1124
+ var CalendarInviteParametersV1Schema = CalendarBaseSchema.extend({
1125
+ schema: z7.literal("odingard.guardian.calendar.invite.v1"),
1126
+ eventId: EventIdSchema,
1127
+ attendees: z7.array(AttendeeSchema).min(1).max(50),
1128
+ sendUpdates: SendUpdatesSchema
1129
+ }).strict();
1130
+ var CalendarParametersV1Schema = z7.discriminatedUnion("schema", [
1131
+ CalendarCreateParametersV1Schema,
1132
+ CalendarUpdateParametersV1Schema,
1133
+ CalendarCancelParametersV1Schema,
1134
+ CalendarInviteParametersV1Schema
1135
+ ]);
1136
+ function attendeeEmails(attendees) {
1137
+ return [...new Set(attendees.map((a) => a.email.toLowerCase()))].sort();
1138
+ }
1139
+ function calendarVerificationProjection(params) {
1140
+ switch (params.schema) {
1141
+ case "odingard.guardian.calendar.create.v1":
1142
+ return {
1143
+ kind: "calendar.create",
1144
+ calendarId: params.calendarId,
1145
+ summary: params.summary,
1146
+ ...params.description === void 0 ? {} : { description: params.description },
1147
+ ...params.location === void 0 ? {} : { location: params.location },
1148
+ start: params.start,
1149
+ end: params.end
1150
+ };
1151
+ case "odingard.guardian.calendar.update.v1":
1152
+ return {
1153
+ kind: "calendar.update",
1154
+ calendarId: params.calendarId,
1155
+ eventId: params.eventId,
1156
+ ...params.summary === void 0 ? {} : { summary: params.summary },
1157
+ ...params.description === void 0 ? {} : { description: params.description },
1158
+ ...params.location === void 0 ? {} : { location: params.location },
1159
+ ...params.start === void 0 ? {} : { start: params.start },
1160
+ ...params.end === void 0 ? {} : { end: params.end },
1161
+ sendUpdates: params.sendUpdates ?? "none"
1162
+ };
1163
+ case "odingard.guardian.calendar.cancel.v1":
1164
+ return {
1165
+ kind: "calendar.cancel",
1166
+ calendarId: params.calendarId,
1167
+ eventId: params.eventId,
1168
+ outcome: "cancelled",
1169
+ sendUpdates: params.sendUpdates ?? "none"
1170
+ };
1171
+ case "odingard.guardian.calendar.invite.v1":
1172
+ return {
1173
+ kind: "calendar.invite",
1174
+ calendarId: params.calendarId,
1175
+ eventId: params.eventId,
1176
+ attendees: attendeeEmails(params.attendees),
1177
+ sendUpdates: params.sendUpdates ?? "none"
1178
+ };
1179
+ }
1180
+ }
1181
+ function calendarEnvelopeProjection(params) {
1182
+ const base = {
1183
+ calendarId: params.calendarId,
1184
+ ..."eventId" in params && params.eventId !== void 0 ? { eventId: params.eventId } : {}
1185
+ };
1186
+ switch (params.schema) {
1187
+ case "odingard.guardian.calendar.create.v1":
1188
+ return { kind: "calendar.create", ...base };
1189
+ case "odingard.guardian.calendar.update.v1":
1190
+ return { kind: "calendar.update", ...base };
1191
+ case "odingard.guardian.calendar.cancel.v1":
1192
+ return { kind: "calendar.cancel", ...base };
1193
+ case "odingard.guardian.calendar.invite.v1":
1194
+ return { kind: "calendar.invite", ...base };
1195
+ }
1196
+ }
1197
+
1198
+ // src/contracts/files.ts
1199
+ import { z as z8 } from "zod";
1200
+ var FILE_PATH_MAX_LENGTH = 512;
1201
+ var FILE_CONTENT_MAX_LENGTH = 64e3;
1202
+ var FILE_GRANTEE_MAX_LENGTH = 256;
1203
+ var FILE_PATH_SEGMENT = /^[A-Za-z0-9._~-]+$/;
1204
+ function isCanonicalFilePath(value) {
1205
+ if (value.length < 2 || value.length > FILE_PATH_MAX_LENGTH) return false;
1206
+ if (!value.startsWith("/") || value.endsWith("/")) return false;
1207
+ if (value.includes("//")) return false;
1208
+ for (const segment of value.slice(1).split("/")) {
1209
+ if (segment === "." || segment === "..") return false;
1210
+ if (!FILE_PATH_SEGMENT.test(segment)) return false;
1211
+ }
1212
+ return true;
1213
+ }
1214
+ var FilePathSchema = z8.string().min(2).max(FILE_PATH_MAX_LENGTH).refine(isCanonicalFilePath, "file path must be canonical posix");
1215
+ var CounterpartyTokenSchema = z8.string().min(1).max(FILE_GRANTEE_MAX_LENGTH).regex(/^[A-Za-z0-9._~+@-]+$/u, "counterparty token charset");
1216
+ var FILE_READ_SCHEMA = "odingard.guardian.file.read.v1";
1217
+ var FILE_WRITE_SCHEMA = "odingard.guardian.file.write.v1";
1218
+ var FILE_SHARE_SCHEMA = "odingard.guardian.file.share.v1";
1219
+ var FILE_DELETE_SCHEMA = "odingard.guardian.file.delete.v1";
1220
+ var FileReadParametersV1Schema = z8.object({
1221
+ schema: z8.literal(FILE_READ_SCHEMA),
1222
+ connectionId: ConnectionIdSchema,
1223
+ path: FilePathSchema
1224
+ }).strict();
1225
+ var FileWriteParametersV1Schema = z8.object({
1226
+ schema: z8.literal(FILE_WRITE_SCHEMA),
1227
+ connectionId: ConnectionIdSchema,
1228
+ path: FilePathSchema,
1229
+ content: z8.string().max(FILE_CONTENT_MAX_LENGTH).refine(
1230
+ (value) => value.normalize("NFC") === value,
1231
+ "file content must already use NFC normalization"
1232
+ )
1233
+ }).strict();
1234
+ var FileShareParametersV1Schema = z8.object({
1235
+ schema: z8.literal(FILE_SHARE_SCHEMA),
1236
+ connectionId: ConnectionIdSchema,
1237
+ path: FilePathSchema,
1238
+ grantee: CounterpartyTokenSchema,
1239
+ expiresAtEpochMs: z8.number().int().positive().safe().optional()
1240
+ }).strict();
1241
+ var FileDeleteParametersV1Schema = z8.object({
1242
+ schema: z8.literal(FILE_DELETE_SCHEMA),
1243
+ connectionId: ConnectionIdSchema,
1244
+ path: FilePathSchema
1245
+ }).strict();
1246
+ function filePathWithinAllowlist(path, allowlist) {
1247
+ for (const entry of allowlist) {
1248
+ if (!isCanonicalFilePath(entry) && !entry.endsWith("/")) continue;
1249
+ if (path === entry) return true;
1250
+ if (entry.endsWith("/") && path.startsWith(entry)) return true;
1251
+ if (path.startsWith(`${entry}/`)) return true;
1252
+ }
1253
+ return false;
1254
+ }
1255
+ function fileReadVerificationProjection(input) {
1256
+ const params = FileReadParametersV1Schema.parse(input);
1257
+ return Object.freeze({
1258
+ domain: "odingard.guardian.file-read-projection.v1",
1259
+ connectionId: params.connectionId,
1260
+ path: params.path
1261
+ });
1262
+ }
1263
+ function fileWriteVerificationProjection(input) {
1264
+ const params = FileWriteParametersV1Schema.parse(input);
1265
+ return Object.freeze({
1266
+ domain: "odingard.guardian.file-write-projection.v1",
1267
+ connectionId: params.connectionId,
1268
+ path: params.path,
1269
+ contentDigest: sha256Text(params.content),
1270
+ bytes: params.content.length
1271
+ });
1272
+ }
1273
+ function fileShareVerificationProjection(input) {
1274
+ const params = FileShareParametersV1Schema.parse(input);
1275
+ return Object.freeze({
1276
+ domain: "odingard.guardian.file-share-projection.v1",
1277
+ connectionId: params.connectionId,
1278
+ path: params.path,
1279
+ grantee: params.grantee,
1280
+ expiresAtEpochMs: params.expiresAtEpochMs ?? null
1281
+ });
1282
+ }
1283
+ function fileDeleteVerificationProjection(input) {
1284
+ const params = FileDeleteParametersV1Schema.parse(input);
1285
+ return Object.freeze({
1286
+ domain: "odingard.guardian.file-delete-projection.v1",
1287
+ connectionId: params.connectionId,
1288
+ path: params.path
1289
+ });
1290
+ }
1291
+
1292
+ // src/contracts/protected-data.ts
1293
+ import { z as z9 } from "zod";
1294
+ var PROTECTED_FIELD_REF_MAX_LENGTH = 64;
1295
+ var PROTECTED_VALUE_MAX_LENGTH = 4096;
1296
+ var PROTECTED_CATALOG_MAX_ENTRIES = 128;
1297
+ var PROTECTED_USE_SINKS_MAX_ENTRIES = 16;
1298
+ var PROTECTED_DISCLOSE_TARGETS_MAX_ENTRIES = 32;
1299
+ var ProtectedFieldRefSchema = z9.string().min(1).max(PROTECTED_FIELD_REF_MAX_LENGTH).regex(
1300
+ /^[a-z0-9][a-z0-9._-]*$/u,
1301
+ "field refs are lowercase tokens (no traversal, no globs)"
1302
+ );
1303
+ var ProtectedUseSinkSchema = z9.object({
1304
+ /** The file connection whose store receives the injected value. */
1305
+ connectionId: ConnectionIdSchema,
1306
+ /** Canonical path prefix the agent's sink path must sit under. */
1307
+ pathPrefix: z9.string().min(1).max(512).refine(
1308
+ (value) => isCanonicalFilePath(value) || value.startsWith("/") && !value.endsWith("/") && value.length > 1 && isCanonicalFilePath(value.slice(0, -1)),
1309
+ "useSink pathPrefix must be a canonical path or directory prefix"
1310
+ )
1311
+ }).strict();
1312
+ var ProtectedCatalogEntrySchema = z9.object({
1313
+ fieldRef: ProtectedFieldRefSchema,
1314
+ label: z9.string().min(1).max(200),
1315
+ /** Safe preview for slips/receipts — never the plaintext. */
1316
+ maskedPreview: z9.string().max(120),
1317
+ allowUse: z9.boolean(),
1318
+ allowView: z9.boolean(),
1319
+ allowPersist: z9.boolean(),
1320
+ /** Declared sinks `protected_data.use` may inject into. */
1321
+ useSinks: z9.array(ProtectedUseSinkSchema).max(PROTECTED_USE_SINKS_MAX_ENTRIES),
1322
+ /** Declared counterparties `protected_data.disclose` may send to. */
1323
+ discloseTargets: z9.array(CounterpartyTokenSchema).max(PROTECTED_DISCLOSE_TARGETS_MAX_ENTRIES)
1324
+ }).strict();
1325
+ var ProtectedCatalogSchema = z9.array(ProtectedCatalogEntrySchema).max(PROTECTED_CATALOG_MAX_ENTRIES).refine(
1326
+ (entries) => new Set(entries.map((entry) => entry.fieldRef)).size === entries.length,
1327
+ "catalog field refs must be unique"
1328
+ );
1329
+ var PROTECTED_USE_SCHEMA = "odingard.guardian.protected-data.use.v1";
1330
+ var PROTECTED_VIEW_SCHEMA = "odingard.guardian.protected-data.view.v1";
1331
+ var PROTECTED_DISCLOSE_SCHEMA = "odingard.guardian.protected-data.disclose.v1";
1332
+ var PROTECTED_PERSIST_SCHEMA = "odingard.guardian.protected-data.persist.v1";
1333
+ var ProtectedUseParametersV1Schema = z9.object({
1334
+ schema: z9.literal(PROTECTED_USE_SCHEMA),
1335
+ connectionId: ConnectionIdSchema,
1336
+ fieldRef: ProtectedFieldRefSchema,
1337
+ /** Where the adapter injects the resolved value. Agent sees no plaintext. */
1338
+ sink: z9.object({
1339
+ connectionId: ConnectionIdSchema,
1340
+ path: FilePathSchema
1341
+ }).strict()
1342
+ }).strict();
1343
+ var ProtectedViewParametersV1Schema = z9.object({
1344
+ schema: z9.literal(PROTECTED_VIEW_SCHEMA),
1345
+ connectionId: ConnectionIdSchema,
1346
+ fieldRef: ProtectedFieldRefSchema
1347
+ }).strict();
1348
+ var ProtectedDiscloseParametersV1Schema = z9.object({
1349
+ schema: z9.literal(PROTECTED_DISCLOSE_SCHEMA),
1350
+ connectionId: ConnectionIdSchema,
1351
+ fieldRef: ProtectedFieldRefSchema,
1352
+ counterparty: CounterpartyTokenSchema
1353
+ }).strict();
1354
+ var ProtectedPersistParametersV1Schema = z9.object({
1355
+ schema: z9.literal(PROTECTED_PERSIST_SCHEMA),
1356
+ connectionId: ConnectionIdSchema,
1357
+ fieldRef: ProtectedFieldRefSchema,
1358
+ value: z9.string().min(1).max(PROTECTED_VALUE_MAX_LENGTH).refine(
1359
+ (value) => value.normalize("NFC") === value,
1360
+ "protected value must already use NFC normalization"
1361
+ )
1362
+ }).strict();
1363
+ function protectedCatalogField(catalog, fieldRef) {
1364
+ return catalog?.find((entry) => entry.fieldRef === fieldRef) ?? null;
1365
+ }
1366
+ function protectedSinkWithinUseSinks(sink, useSinks) {
1367
+ for (const entry of useSinks) {
1368
+ if (entry.connectionId !== sink.connectionId) continue;
1369
+ if (entry.pathPrefix.endsWith("/")) {
1370
+ if (sink.path.startsWith(entry.pathPrefix)) return true;
1371
+ } else if (sink.path === entry.pathPrefix || sink.path.startsWith(`${entry.pathPrefix}/`)) {
1372
+ return true;
1373
+ }
1374
+ }
1375
+ return false;
1376
+ }
1377
+ function protectedUseVerificationProjection(input) {
1378
+ const params = ProtectedUseParametersV1Schema.parse(input);
1379
+ return Object.freeze({
1380
+ domain: "odingard.guardian.protected-use-projection.v1",
1381
+ connectionId: params.connectionId,
1382
+ fieldRef: params.fieldRef,
1383
+ sinkConnectionId: params.sink.connectionId,
1384
+ sinkPath: params.sink.path
1385
+ });
1386
+ }
1387
+ function protectedViewVerificationProjection(input) {
1388
+ const params = ProtectedViewParametersV1Schema.parse(input);
1389
+ return Object.freeze({
1390
+ domain: "odingard.guardian.protected-view-projection.v1",
1391
+ connectionId: params.connectionId,
1392
+ fieldRef: params.fieldRef
1393
+ });
1394
+ }
1395
+ function protectedDiscloseVerificationProjection(input) {
1396
+ const params = ProtectedDiscloseParametersV1Schema.parse(input);
1397
+ return Object.freeze({
1398
+ domain: "odingard.guardian.protected-disclose-projection.v1",
1399
+ connectionId: params.connectionId,
1400
+ fieldRef: params.fieldRef,
1401
+ counterparty: params.counterparty
1402
+ });
1403
+ }
1404
+ function protectedPersistVerificationProjection(input) {
1405
+ const params = ProtectedPersistParametersV1Schema.parse(input);
1406
+ return Object.freeze({
1407
+ domain: "odingard.guardian.protected-persist-projection.v1",
1408
+ connectionId: params.connectionId,
1409
+ fieldRef: params.fieldRef,
1410
+ valueDigest: sha256Text(params.value)
1411
+ });
1412
+ }
1413
+
1414
+ // src/contracts/tools.ts
1415
+ import { z as z10 } from "zod";
1416
+ var TOOL_NAME_MAX_LENGTH = 64;
1417
+ var TOOL_ARGUMENTS_MAX_BYTES = 16384;
1418
+ var TOOL_CATALOG_MAX_TOOLS = 64;
1419
+ var TOOL_SERVER_NAME_MAX_LENGTH = 128;
1420
+ var TOOL_SERVER_VERSION_MAX_LENGTH = 64;
1421
+ var TOOL_DESCRIPTION_MAX_LENGTH = 512;
1422
+ var ToolNameSchema = z10.string().min(1).max(TOOL_NAME_MAX_LENGTH).regex(
1423
+ /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/,
1424
+ "tool names are lowercase dotted/dashed identifiers"
1425
+ );
1426
+ var ToolDescriptorSchema = z10.object({
1427
+ name: ToolNameSchema,
1428
+ description: z10.string().max(TOOL_DESCRIPTION_MAX_LENGTH).optional(),
1429
+ method: ApiRequestMethodSchema,
1430
+ url: ApiRequestUrlSchema
1431
+ }).strict();
1432
+ var ToolServerCatalogSchema = z10.object({
1433
+ serverName: z10.string().min(1).max(TOOL_SERVER_NAME_MAX_LENGTH),
1434
+ serverVersion: z10.string().min(1).max(TOOL_SERVER_VERSION_MAX_LENGTH),
1435
+ tools: z10.array(ToolDescriptorSchema).max(TOOL_CATALOG_MAX_TOOLS).refine(
1436
+ (tools) => new Set(tools.map((tool) => tool.name)).size === tools.length,
1437
+ "tool names must be unique within a catalog"
1438
+ )
1439
+ }).strict();
1440
+ var ToolArgumentsSchema = z10.record(z10.string(), z10.unknown()).refine(
1441
+ (value) => {
1442
+ try {
1443
+ return canonicalJson(value).length <= TOOL_ARGUMENTS_MAX_BYTES;
1444
+ } catch {
1445
+ return false;
1446
+ }
1447
+ },
1448
+ {
1449
+ message: "arguments must be a bounded canonical JSON object (no cycles, accessors, or exotic prototypes)"
1450
+ }
1451
+ );
1452
+ var ToolInvokeParametersV1Schema = z10.object({
1453
+ schema: z10.literal("odingard.guardian.tool.invoke.v1"),
1454
+ connectionId: ConnectionIdSchema,
1455
+ toolName: ToolNameSchema,
1456
+ arguments: ToolArgumentsSchema
1457
+ }).strict();
1458
+ var McpInstallParametersV1Schema = z10.object({
1459
+ schema: z10.literal("odingard.guardian.mcp.install.v1"),
1460
+ connectionId: ConnectionIdSchema,
1461
+ tool: ToolDescriptorSchema
1462
+ }).strict();
1463
+ var McpEnableParametersV1Schema = z10.object({
1464
+ schema: z10.literal("odingard.guardian.mcp.enable.v1"),
1465
+ connectionId: ConnectionIdSchema,
1466
+ toolName: ToolNameSchema
1467
+ }).strict();
1468
+ function toolVerificationProjection(input) {
1469
+ const params = ToolInvokeParametersV1Schema.parse(input);
1470
+ return Object.freeze({
1471
+ domain: "odingard.guardian.tool-invoke-projection.v1",
1472
+ connectionId: params.connectionId,
1473
+ toolName: params.toolName,
1474
+ argumentsDigest: sha256Text(canonicalJson(params.arguments))
1475
+ });
1476
+ }
1477
+ function toolEnvelopeProjection(input) {
1478
+ return toolVerificationProjection(input);
1479
+ }
1480
+ function mcpInstallProjection(input) {
1481
+ const params = McpInstallParametersV1Schema.parse(input);
1482
+ return Object.freeze({
1483
+ domain: "odingard.guardian.mcp-projection.v1",
1484
+ operation: "install",
1485
+ connectionId: params.connectionId,
1486
+ toolName: params.tool.name,
1487
+ descriptorDigest: sha256Text(canonicalJson(params.tool))
1488
+ });
1489
+ }
1490
+ function mcpEnableProjection(input) {
1491
+ const params = McpEnableParametersV1Schema.parse(input);
1492
+ return Object.freeze({
1493
+ domain: "odingard.guardian.mcp-projection.v1",
1494
+ operation: "enable",
1495
+ connectionId: params.connectionId,
1496
+ toolName: params.toolName,
1497
+ descriptorDigest: null
1498
+ });
1499
+ }
1500
+ function findCatalogTool(connection, toolName) {
1501
+ return connection.toolServer?.tools.find((tool) => tool.name === toolName);
1502
+ }
1503
+ function validateToolInvokeBinding(input) {
1504
+ const tool = findCatalogTool(input.connection, input.params.toolName);
1505
+ if (tool === void 0) {
1506
+ return { ok: false, reasonCode: "TOOL_NOT_IN_CATALOG" };
1507
+ }
1508
+ if (input.connection.enabledToolNames !== void 0 && !input.connection.enabledToolNames.includes(tool.name)) {
1509
+ return { ok: false, reasonCode: "TOOL_NOT_GRANTED" };
1510
+ }
1511
+ const rendered = ToolDescriptorSchema.safeParse(tool);
1512
+ if (!rendered.success || !apiEndpointWithinAllowlist(
1513
+ rendered.data.url,
1514
+ input.connection.endpointAllowlist ?? []
1515
+ )) {
1516
+ return { ok: false, reasonCode: "ENDPOINT_OUTSIDE_ALLOWLIST" };
1517
+ }
1518
+ return { ok: true };
1519
+ }
1520
+ function validateMcpInstallBinding(input) {
1521
+ const tools = input.connection.toolServer?.tools;
1522
+ if (tools === void 0) {
1523
+ return { ok: false, reasonCode: "CONNECTION_UNAVAILABLE" };
1524
+ }
1525
+ if (tools.length >= TOOL_CATALOG_MAX_TOOLS) {
1526
+ return { ok: false, reasonCode: "TOOL_CATALOG_FULL" };
1527
+ }
1528
+ if (tools.some((tool) => tool.name === input.params.tool.name)) {
1529
+ return { ok: false, reasonCode: "TOOL_ALREADY_INSTALLED" };
1530
+ }
1531
+ if (!apiEndpointWithinAllowlist(
1532
+ input.params.tool.url,
1533
+ input.connection.endpointAllowlist ?? []
1534
+ )) {
1535
+ return { ok: false, reasonCode: "ENDPOINT_OUTSIDE_ALLOWLIST" };
1536
+ }
1537
+ return { ok: true };
1538
+ }
1539
+ function validateMcpEnableBinding(input) {
1540
+ const tool = findCatalogTool(input.connection, input.params.toolName);
1541
+ if (tool === void 0) {
1542
+ return { ok: false, reasonCode: "TOOL_NOT_IN_CATALOG" };
1543
+ }
1544
+ if (input.connection.enabledToolNames?.includes(tool.name)) {
1545
+ return { ok: false, reasonCode: "TOOL_ALREADY_ENABLED" };
1546
+ }
1547
+ return { ok: true };
1548
+ }
1549
+
1550
+ // src/contracts/action-registry.ts
1551
+ function createActionRegistry(definitions) {
1552
+ const byAction = /* @__PURE__ */ new Map();
1553
+ for (const definition of definitions) {
1554
+ if (byAction.has(definition.actionId)) {
1555
+ throw new TypeError(
1556
+ `duplicate action definition: ${definition.actionId}`
1557
+ );
1558
+ }
1559
+ byAction.set(definition.actionId, definition);
1560
+ }
1561
+ const entries = Object.freeze([...definitions]);
1562
+ return Object.freeze({
1563
+ get: (actionId) => byAction.get(actionId),
1564
+ entries: () => entries
1565
+ });
1566
+ }
1567
+ var emailSendDefinition = {
1568
+ actionId: "email.send",
1569
+ schemaVersion: 1,
1570
+ actionFamily: "communication",
1571
+ humanLabel: "Send email",
1572
+ description: "Send an email message from a leased mailbox connection.",
1573
+ parametersKind: "email.send.v1",
1574
+ requiredParameters: ["connectionId", "from", "to", "subject", "body"],
1575
+ materialParameters: [
1576
+ "connectionId",
1577
+ "from",
1578
+ "to",
1579
+ "cc",
1580
+ "bcc",
1581
+ "replyTo",
1582
+ "subject",
1583
+ "body",
1584
+ "attachments",
1585
+ "inReplyTo"
1586
+ ],
1587
+ resourceType: "email:mailbox",
1588
+ providers: ["gmail", "graph"],
1589
+ counterpartyRules: "Recipients are external counterparties; every recipient is material.",
1590
+ dataEffects: "Discloses message content to external recipients.",
1591
+ reversibility: "limited",
1592
+ riskClass: "R3",
1593
+ defaultDecisionPosture: "deny",
1594
+ approvalPresentationSchema: [
1595
+ "from",
1596
+ "to",
1597
+ "subject",
1598
+ "bodyPreview",
1599
+ "attachments"
1600
+ ],
1601
+ capabilityRequirements: { uses: 1 },
1602
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected",
1603
+ verificationContract: {
1604
+ level: "pre-commit-content+post-commit-envelope"
1605
+ },
1606
+ evidenceRequirements: [
1607
+ "effect.prepared",
1608
+ "commit.authorized",
1609
+ "effect.verified | effect.unknown | commit.rejected"
1610
+ ],
1611
+ version: 1,
1612
+ status: "active",
1613
+ validateParameters(materialParameters) {
1614
+ const parsed = EmailSendParametersV1Schema.safeParse(materialParameters);
1615
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
1616
+ },
1617
+ validateBinding({ params, connection }) {
1618
+ return connection.mailboxAddress !== void 0 && params.from === connection.mailboxAddress ? { ok: true } : { ok: false, reasonCode: "CONNECTION_UNAVAILABLE" };
1619
+ },
1620
+ verificationProjection: (params) => emailVerificationProjection(params),
1621
+ envelopeProjection: (params) => emailEnvelopeProjection(params)
1622
+ };
1623
+ var apiRequestDefinition = {
1624
+ actionId: "api.request",
1625
+ schemaVersion: 1,
1626
+ actionFamily: "networking",
1627
+ humanLabel: "Call external API",
1628
+ description: "Issue an outbound https request within a connection's endpoint allowlist; the adapter injects the leased bearer token at commit.",
1629
+ parametersKind: "api.request.v1",
1630
+ requiredParameters: ["connectionId", "method", "url", "headers"],
1631
+ materialParameters: [
1632
+ "connectionId",
1633
+ "method",
1634
+ "url",
1635
+ "headers",
1636
+ "body",
1637
+ "idempotencyKey"
1638
+ ],
1639
+ resourceType: "api:endpoint",
1640
+ providers: ["api"],
1641
+ counterpartyRules: "Host+path must stay inside the connection endpoint allowlist; https only; agent may not set credential or hop-by-hop headers.",
1642
+ dataEffects: "May mutate or read external state; response metadata (status, byte count, headers digest, bounded body digest) is evidenced.",
1643
+ reversibility: "difficult",
1644
+ riskClass: "R3",
1645
+ defaultDecisionPosture: "deny",
1646
+ approvalPresentationSchema: ["method", "url", "headers", "bodyPreview"],
1647
+ capabilityRequirements: { uses: 1 },
1648
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected; observe never re-issues the request.",
1649
+ verificationContract: { level: "post-commit-content" },
1650
+ evidenceRequirements: [
1651
+ "effect.prepared",
1652
+ "commit.authorized",
1653
+ "effect.verified | effect.unknown | commit.rejected"
1654
+ ],
1655
+ version: 1,
1656
+ status: "active",
1657
+ validateParameters(materialParameters) {
1658
+ const parsed = ApiRequestParametersV1Schema.safeParse(materialParameters);
1659
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
1660
+ },
1661
+ validateBinding({ params, connection }) {
1662
+ return apiEndpointWithinAllowlist(
1663
+ params.url,
1664
+ connection.endpointAllowlist ?? []
1665
+ ) ? { ok: true } : { ok: false, reasonCode: "ENDPOINT_OUTSIDE_ALLOWLIST" };
1666
+ },
1667
+ verificationProjection: (params) => apiVerificationProjection(params),
1668
+ envelopeProjection: (params) => apiEnvelopeProjection(params)
1669
+ };
1670
+ var toolInvokeDefinition = {
1671
+ actionId: "tool.invoke",
1672
+ schemaVersion: 1,
1673
+ actionFamily: "tooling",
1674
+ humanLabel: "Invoke tool",
1675
+ description: "Invoke a catalog tool enabled for the agent on a leased tool-server connection; renders to a bounded https call inside the connection endpoint allowlist.",
1676
+ parametersKind: "tool.invoke.v1",
1677
+ requiredParameters: ["connectionId", "toolName", "arguments"],
1678
+ materialParameters: ["connectionId", "toolName", "arguments"],
1679
+ resourceType: "tool:server",
1680
+ providers: ["tool"],
1681
+ counterpartyRules: "Tool name must exist in the connection catalog and be enabled for the requesting agent; the rendered endpoint stays inside the allowlist.",
1682
+ dataEffects: "May read or mutate external state through the tool; bounded invocation and result metadata is evidenced.",
1683
+ reversibility: "limited",
1684
+ riskClass: "R3",
1685
+ defaultDecisionPosture: "deny",
1686
+ approvalPresentationSchema: ["toolName", "serverName", "argumentsPreview"],
1687
+ capabilityRequirements: { uses: 1 },
1688
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected; observe never re-issues the invocation.",
1689
+ verificationContract: { level: "post-commit-content" },
1690
+ evidenceRequirements: [
1691
+ "effect.prepared",
1692
+ "commit.authorized",
1693
+ "effect.verified | effect.unknown | commit.rejected"
1694
+ ],
1695
+ version: 1,
1696
+ status: "active",
1697
+ validateParameters(materialParameters) {
1698
+ const parsed = ToolInvokeParametersV1Schema.safeParse(materialParameters);
1699
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
1700
+ },
1701
+ validateBinding({ params, connection }) {
1702
+ return validateToolInvokeBinding({ params, connection });
1703
+ },
1704
+ verificationProjection: (params) => toolVerificationProjection(params),
1705
+ envelopeProjection: (params) => toolEnvelopeProjection(params)
1706
+ };
1707
+ var mcpInstallDefinition = {
1708
+ actionId: "mcp.install",
1709
+ schemaVersion: 1,
1710
+ actionFamily: "capability-governance",
1711
+ humanLabel: "Install MCP tool",
1712
+ description: "Register an additional tool on a human-installed tool-server connection; capability-expanding \u2014 requires an explicit human grant, never auto-derived.",
1713
+ parametersKind: "mcp.install.v1",
1714
+ requiredParameters: ["connectionId", "tool"],
1715
+ materialParameters: ["connectionId", "tool"],
1716
+ resourceType: "mcp:server",
1717
+ providers: ["tool"],
1718
+ counterpartyRules: "Tool descriptor must be new to the catalog and its url must stay inside the existing endpoint allowlist; installs never create connections or widen scopes.",
1719
+ dataEffects: "Mutates the connection's tool catalog (adds one callable tool).",
1720
+ reversibility: "limited",
1721
+ riskClass: "R3",
1722
+ defaultDecisionPosture: "ask",
1723
+ approvalPresentationSchema: ["tool", "serverName"],
1724
+ capabilityRequirements: { uses: 1 },
1725
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected",
1726
+ verificationContract: { level: "post-commit-content" },
1727
+ evidenceRequirements: [
1728
+ "effect.prepared",
1729
+ "commit.authorized",
1730
+ "effect.verified | effect.unknown | commit.rejected"
1731
+ ],
1732
+ version: 1,
1733
+ status: "active",
1734
+ requiresExplicitGrant: true,
1735
+ validateParameters(materialParameters) {
1736
+ const parsed = McpInstallParametersV1Schema.safeParse(materialParameters);
1737
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
1738
+ },
1739
+ validateBinding({ params, connection }) {
1740
+ return validateMcpInstallBinding({ params, connection });
1741
+ },
1742
+ verificationProjection: (params) => mcpInstallProjection(params),
1743
+ envelopeProjection: (params) => mcpInstallProjection(params)
1744
+ };
1745
+ var mcpEnableDefinition = {
1746
+ actionId: "mcp.enable",
1747
+ schemaVersion: 1,
1748
+ actionFamily: "capability-governance",
1749
+ humanLabel: "Enable MCP tool",
1750
+ description: "Enable a catalog tool for the requesting agent on a tool-server connection; capability-expanding \u2014 requires an explicit human grant, never auto-derived.",
1751
+ parametersKind: "mcp.enable.v1",
1752
+ requiredParameters: ["connectionId", "toolName"],
1753
+ materialParameters: ["connectionId", "toolName"],
1754
+ resourceType: "mcp:server",
1755
+ providers: ["tool"],
1756
+ counterpartyRules: "Tool name must exist in the catalog; enabling writes to the per-agent tool registry with the granting evidence.",
1757
+ dataEffects: "Mutates the agent's tool registry (enables one catalog tool).",
1758
+ reversibility: "reversible",
1759
+ riskClass: "R2",
1760
+ defaultDecisionPosture: "ask",
1761
+ approvalPresentationSchema: ["toolName", "serverName"],
1762
+ capabilityRequirements: { uses: 1 },
1763
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected",
1764
+ verificationContract: { level: "post-commit-content" },
1765
+ evidenceRequirements: [
1766
+ "effect.prepared",
1767
+ "commit.authorized",
1768
+ "effect.verified | effect.unknown | commit.rejected"
1769
+ ],
1770
+ version: 1,
1771
+ status: "active",
1772
+ requiresExplicitGrant: true,
1773
+ validateParameters(materialParameters) {
1774
+ const parsed = McpEnableParametersV1Schema.safeParse(materialParameters);
1775
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
1776
+ },
1777
+ validateBinding({ params, connection }) {
1778
+ return validateMcpEnableBinding({ params, connection });
1779
+ },
1780
+ verificationProjection: (params) => mcpEnableProjection(params),
1781
+ envelopeProjection: (params) => mcpEnableProjection(params)
1782
+ };
1783
+ var GCAL_ADAPTER_ID = "adp_00000003";
1784
+ var calendarBinding = (params, connection) => connection.provider === "gcalendar" && connection.calendarId !== void 0 && params.calendarId === connection.calendarId ? { ok: true } : { ok: false, reasonCode: "CONNECTION_UNAVAILABLE" };
1785
+ var calendarValidate = (schema) => (materialParameters) => {
1786
+ const parsed = schema.safeParse(materialParameters);
1787
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
1788
+ };
1789
+ var calendarDefinitionBase = {
1790
+ schemaVersion: 1,
1791
+ actionFamily: "scheduling",
1792
+ resourceType: "calendar:calendar",
1793
+ providers: ["gcalendar"],
1794
+ adapterIds: [GCAL_ADAPTER_ID],
1795
+ counterpartyRules: "Events live on the connection's authorized calendar only; external invitees are counterparties and are material.",
1796
+ capabilityRequirements: { uses: 1 },
1797
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected; observe never re-issues the request.",
1798
+ verificationContract: { level: "post-commit-content" },
1799
+ evidenceRequirements: [
1800
+ "effect.prepared",
1801
+ "commit.authorized",
1802
+ "effect.verified | effect.unknown | commit.rejected"
1803
+ ],
1804
+ version: 1,
1805
+ status: "active"
1806
+ };
1807
+ var calendarCreateDefinition = {
1808
+ ...calendarDefinitionBase,
1809
+ actionId: "calendar.create",
1810
+ humanLabel: "Create calendar event",
1811
+ description: "Create an event on the connection's authorized calendar; attendees are never settable here \u2014 inviting people is a separate material commitment.",
1812
+ parametersKind: "calendar.create.v1",
1813
+ requiredParameters: ["connectionId", "calendarId", "summary", "start", "end"],
1814
+ materialParameters: [
1815
+ "connectionId",
1816
+ "calendarId",
1817
+ "summary",
1818
+ "description",
1819
+ "location",
1820
+ "start",
1821
+ "end",
1822
+ "idempotencyKey"
1823
+ ],
1824
+ dataEffects: "Creates an event record on the authorized calendar.",
1825
+ reversibility: "reversible",
1826
+ riskClass: "R2",
1827
+ defaultDecisionPosture: "allow",
1828
+ approvalPresentationSchema: ["summary", "start", "end", "location"],
1829
+ validateParameters: calendarValidate(CalendarCreateParametersV1Schema),
1830
+ validateBinding: ({ params, connection }) => calendarBinding(params, connection),
1831
+ verificationProjection: (params) => calendarVerificationProjection(params),
1832
+ envelopeProjection: (params) => calendarEnvelopeProjection(params)
1833
+ };
1834
+ var calendarUpdateDefinition = {
1835
+ ...calendarDefinitionBase,
1836
+ actionId: "calendar.update",
1837
+ humanLabel: "Update calendar event",
1838
+ description: "Change fields of an existing event \u2014 including its committed start/end time \u2014 on the authorized calendar; a material external commitment.",
1839
+ parametersKind: "calendar.update.v1",
1840
+ requiredParameters: ["connectionId", "calendarId", "eventId"],
1841
+ materialParameters: [
1842
+ "connectionId",
1843
+ "calendarId",
1844
+ "eventId",
1845
+ "summary",
1846
+ "description",
1847
+ "location",
1848
+ "start",
1849
+ "end",
1850
+ "sendUpdates"
1851
+ ],
1852
+ dataEffects: "Mutates a live commitment; a changed time moves every attendee's schedule.",
1853
+ reversibility: "limited",
1854
+ riskClass: "R3",
1855
+ defaultDecisionPosture: "ask",
1856
+ approvalPresentationSchema: [
1857
+ "eventId",
1858
+ "summary",
1859
+ "start",
1860
+ "end",
1861
+ "location"
1862
+ ],
1863
+ validateParameters: calendarValidate(CalendarUpdateParametersV1Schema),
1864
+ validateBinding: ({ params, connection }) => calendarBinding(params, connection),
1865
+ verificationProjection: (params) => calendarVerificationProjection(params),
1866
+ envelopeProjection: (params) => calendarEnvelopeProjection(params)
1867
+ };
1868
+ var calendarCancelDefinition = {
1869
+ ...calendarDefinitionBase,
1870
+ actionId: "calendar.cancel",
1871
+ humanLabel: "Cancel calendar event",
1872
+ description: "Cancel (delete) an existing event on the authorized calendar; destroys a commitment.",
1873
+ parametersKind: "calendar.cancel.v1",
1874
+ requiredParameters: ["connectionId", "calendarId", "eventId"],
1875
+ materialParameters: ["connectionId", "calendarId", "eventId", "sendUpdates"],
1876
+ dataEffects: "Removes a commitment; may send cancellation notices to attendees.",
1877
+ reversibility: "difficult",
1878
+ riskClass: "R3",
1879
+ defaultDecisionPosture: "ask",
1880
+ approvalPresentationSchema: ["eventId", "sendUpdates"],
1881
+ validateParameters: calendarValidate(CalendarCancelParametersV1Schema),
1882
+ validateBinding: ({ params, connection }) => calendarBinding(params, connection),
1883
+ verificationProjection: (params) => calendarVerificationProjection(params),
1884
+ envelopeProjection: (params) => calendarEnvelopeProjection(params)
1885
+ };
1886
+ var calendarInviteDefinition = {
1887
+ ...calendarDefinitionBase,
1888
+ actionId: "calendar.invite",
1889
+ humanLabel: "Invite attendees to event",
1890
+ description: "Add external attendees to an existing event on the authorized calendar; creates a material external commitment to each invitee.",
1891
+ parametersKind: "calendar.invite.v1",
1892
+ requiredParameters: ["connectionId", "calendarId", "eventId", "attendees"],
1893
+ materialParameters: [
1894
+ "connectionId",
1895
+ "calendarId",
1896
+ "eventId",
1897
+ "attendees",
1898
+ "sendUpdates"
1899
+ ],
1900
+ dataEffects: "Sends invitations to external addresses; the attendee set is material and approval-bound.",
1901
+ reversibility: "limited",
1902
+ riskClass: "R3",
1903
+ defaultDecisionPosture: "ask",
1904
+ approvalPresentationSchema: ["eventId", "attendees", "sendUpdates"],
1905
+ validateParameters: calendarValidate(CalendarInviteParametersV1Schema),
1906
+ validateBinding: ({ params, connection }) => calendarBinding(params, connection),
1907
+ verificationProjection: (params) => calendarVerificationProjection(params),
1908
+ envelopeProjection: (params) => calendarEnvelopeProjection(params)
1909
+ };
1910
+ var commercePurchaseDefinition = {
1911
+ actionId: "commerce.purchase",
1912
+ schemaVersion: 1,
1913
+ actionFamily: "commerce",
1914
+ humanLabel: "Purchase",
1915
+ description: "Charge a leased payment connection for a one-shot purchase; the adapter charges adapter-side and no reusable payment credential reaches the agent.",
1916
+ parametersKind: "commerce.purchase.v1",
1917
+ requiredParameters: [
1918
+ "connectionId",
1919
+ "merchantId",
1920
+ "merchantCategory",
1921
+ "amountCents",
1922
+ "currency",
1923
+ "orderDescription",
1924
+ "idempotencyKey"
1925
+ ],
1926
+ materialParameters: [
1927
+ "connectionId",
1928
+ "merchantId",
1929
+ "merchantCategory",
1930
+ "amountCents",
1931
+ "currency",
1932
+ "orderDescription",
1933
+ "idempotencyKey",
1934
+ "taskId"
1935
+ ],
1936
+ resourceType: "commerce:account",
1937
+ providers: ["payment-sandbox"],
1938
+ counterpartyRules: "merchantId is the external counterparty; merchant/category facts are re-derived from parameters and bound into the material digest.",
1939
+ dataEffects: "Moves money within sandbox/test scope only; charge metadata is evidenced.",
1940
+ reversibility: "limited",
1941
+ riskClass: "R4",
1942
+ defaultDecisionPosture: "allow",
1943
+ approvalPresentationSchema: [
1944
+ "merchantId",
1945
+ "merchantCategory",
1946
+ "amountCents",
1947
+ "currency",
1948
+ "orderDescription",
1949
+ "taskId"
1950
+ ],
1951
+ capabilityRequirements: { uses: 1 },
1952
+ executionContract: "prepare \u2192 readback \u2192 commit (charge) \u2192 observe \u2192 verified/unknown/rejected",
1953
+ verificationContract: { level: "post-commit-content" },
1954
+ evidenceRequirements: [
1955
+ "effect.prepared",
1956
+ "commit.authorized",
1957
+ "effect.verified | effect.unknown | commit.rejected"
1958
+ ],
1959
+ version: 1,
1960
+ status: "active",
1961
+ validateParameters(materialParameters) {
1962
+ const parsed = CommercePurchaseParametersV1Schema.safeParse(materialParameters);
1963
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
1964
+ },
1965
+ validateBinding() {
1966
+ return { ok: true };
1967
+ },
1968
+ verificationProjection: (params) => commercePurchaseVerificationProjection(params),
1969
+ envelopeProjection: (params) => commercePurchaseEnvelopeProjection(params),
1970
+ authorityFacts: (params) => ({
1971
+ valueCents: params.amountCents,
1972
+ merchantId: params.merchantId,
1973
+ merchantCategory: params.merchantCategory,
1974
+ recurring: false,
1975
+ ...params.taskId === void 0 ? {} : { taskId: params.taskId }
1976
+ })
1977
+ };
1978
+ var commerceRefundDefinition = {
1979
+ actionId: "commerce.refund",
1980
+ schemaVersion: 1,
1981
+ actionFamily: "commerce",
1982
+ humanLabel: "Refund",
1983
+ description: "Refund a prior sandbox charge by chargeId; adapter-side only, full refund when amountCents is omitted.",
1984
+ parametersKind: "commerce.refund.v1",
1985
+ requiredParameters: ["connectionId", "chargeId", "reason"],
1986
+ materialParameters: ["connectionId", "chargeId", "amountCents", "reason"],
1987
+ resourceType: "commerce:account",
1988
+ providers: ["payment-sandbox"],
1989
+ counterpartyRules: "Refunds settle to the original charge's counterparty.",
1990
+ dataEffects: "Returns money within sandbox/test scope only.",
1991
+ reversibility: "difficult",
1992
+ riskClass: "R3",
1993
+ defaultDecisionPosture: "allow",
1994
+ approvalPresentationSchema: ["chargeId", "amountCents", "reason"],
1995
+ capabilityRequirements: { uses: 1 },
1996
+ executionContract: "prepare \u2192 readback \u2192 commit (refund) \u2192 observe \u2192 verified/unknown/rejected",
1997
+ verificationContract: { level: "post-commit-content" },
1998
+ evidenceRequirements: [
1999
+ "effect.prepared",
2000
+ "commit.authorized",
2001
+ "effect.verified | effect.unknown | commit.rejected"
2002
+ ],
2003
+ version: 1,
2004
+ status: "active",
2005
+ validateParameters(materialParameters) {
2006
+ const parsed = CommerceRefundParametersV1Schema.safeParse(materialParameters);
2007
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2008
+ },
2009
+ validateBinding() {
2010
+ return { ok: true };
2011
+ },
2012
+ verificationProjection: (params) => commerceRefundVerificationProjection(params),
2013
+ envelopeProjection: (params) => commerceRefundEnvelopeProjection(params),
2014
+ authorityFacts: (params) => params.amountCents === void 0 ? {} : { valueCents: params.amountCents }
2015
+ };
2016
+ var commerceReturnDefinition = {
2017
+ actionId: "commerce.return",
2018
+ schemaVersion: 1,
2019
+ actionFamily: "commerce",
2020
+ humanLabel: "Return",
2021
+ description: "Record a return against a prior sandbox charge by chargeId; adapter-side only.",
2022
+ parametersKind: "commerce.return.v1",
2023
+ requiredParameters: ["connectionId", "chargeId", "reason"],
2024
+ materialParameters: ["connectionId", "chargeId", "reason"],
2025
+ resourceType: "commerce:account",
2026
+ providers: ["payment-sandbox"],
2027
+ counterpartyRules: "Returns settle to the original charge's counterparty.",
2028
+ dataEffects: "Records a goods return within sandbox/test scope only.",
2029
+ reversibility: "reversible",
2030
+ riskClass: "R2",
2031
+ defaultDecisionPosture: "allow",
2032
+ approvalPresentationSchema: ["chargeId", "reason"],
2033
+ capabilityRequirements: { uses: 1 },
2034
+ executionContract: "prepare \u2192 readback \u2192 commit (return) \u2192 observe \u2192 verified/unknown/rejected",
2035
+ verificationContract: { level: "post-commit-content" },
2036
+ evidenceRequirements: [
2037
+ "effect.prepared",
2038
+ "commit.authorized",
2039
+ "effect.verified | effect.unknown | commit.rejected"
2040
+ ],
2041
+ version: 1,
2042
+ status: "active",
2043
+ validateParameters(materialParameters) {
2044
+ const parsed = CommerceReturnParametersV1Schema.safeParse(materialParameters);
2045
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2046
+ },
2047
+ validateBinding() {
2048
+ return { ok: true };
2049
+ },
2050
+ verificationProjection: (params) => commerceReturnVerificationProjection(params),
2051
+ envelopeProjection: (params) => commerceReturnEnvelopeProjection(params),
2052
+ authorityFacts: () => ({})
2053
+ };
2054
+ var subscriptionCreateDefinition = {
2055
+ actionId: "subscription.create",
2056
+ schemaVersion: 1,
2057
+ actionFamily: "commerce",
2058
+ humanLabel: "Create subscription",
2059
+ description: "Start a recurring subscription charge on a leased payment connection; recurrence is a derived authority fact the kernel can deny.",
2060
+ parametersKind: "subscription.create.v1",
2061
+ requiredParameters: [
2062
+ "connectionId",
2063
+ "merchantId",
2064
+ "planId",
2065
+ "amountCents",
2066
+ "currency",
2067
+ "interval",
2068
+ "idempotencyKey"
2069
+ ],
2070
+ materialParameters: [
2071
+ "connectionId",
2072
+ "merchantId",
2073
+ "planId",
2074
+ "amountCents",
2075
+ "currency",
2076
+ "interval",
2077
+ "idempotencyKey"
2078
+ ],
2079
+ resourceType: "commerce:account",
2080
+ providers: ["payment-sandbox"],
2081
+ counterpartyRules: "merchantId is the external counterparty; recurring is derived as true so recurrence control applies.",
2082
+ dataEffects: "Creates a recurring payment obligation within sandbox/test scope only.",
2083
+ reversibility: "limited",
2084
+ riskClass: "R4",
2085
+ defaultDecisionPosture: "deny",
2086
+ approvalPresentationSchema: [
2087
+ "merchantId",
2088
+ "planId",
2089
+ "amountCents",
2090
+ "currency",
2091
+ "interval"
2092
+ ],
2093
+ capabilityRequirements: { uses: 1 },
2094
+ executionContract: "prepare \u2192 readback \u2192 commit (subscribe) \u2192 observe \u2192 verified/unknown/rejected",
2095
+ verificationContract: { level: "post-commit-content" },
2096
+ evidenceRequirements: [
2097
+ "effect.prepared",
2098
+ "commit.authorized",
2099
+ "effect.verified | effect.unknown | commit.rejected"
2100
+ ],
2101
+ version: 1,
2102
+ status: "active",
2103
+ validateParameters(materialParameters) {
2104
+ const parsed = SubscriptionCreateParametersV1Schema.safeParse(materialParameters);
2105
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2106
+ },
2107
+ validateBinding() {
2108
+ return { ok: true };
2109
+ },
2110
+ verificationProjection: (params) => subscriptionCreateVerificationProjection(params),
2111
+ envelopeProjection: (params) => subscriptionCreateEnvelopeProjection(params),
2112
+ authorityFacts: (params) => ({
2113
+ valueCents: params.amountCents,
2114
+ merchantId: params.merchantId,
2115
+ recurring: true
2116
+ })
2117
+ };
2118
+ function fileDefinition(input) {
2119
+ return {
2120
+ actionId: input.actionId,
2121
+ schemaVersion: 1,
2122
+ actionFamily: "files",
2123
+ humanLabel: input.humanLabel,
2124
+ description: input.description,
2125
+ parametersKind: input.parametersKind,
2126
+ requiredParameters: input.requiredParameters,
2127
+ materialParameters: input.materialParameters,
2128
+ resourceType: "file:store",
2129
+ providers: ["file"],
2130
+ counterpartyRules: "Paths must be canonical and inside the connection path allowlist; shares additionally require the declared share allowlist.",
2131
+ dataEffects: input.dataEffects,
2132
+ reversibility: input.reversibility,
2133
+ riskClass: input.riskClass,
2134
+ defaultDecisionPosture: "deny",
2135
+ approvalPresentationSchema: ["path", "grantee", "bytes"],
2136
+ capabilityRequirements: { uses: 1 },
2137
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected",
2138
+ verificationContract: { level: "post-commit-content" },
2139
+ evidenceRequirements: [
2140
+ "effect.prepared",
2141
+ "commit.authorized",
2142
+ "effect.verified | effect.unknown | commit.rejected"
2143
+ ],
2144
+ version: 1,
2145
+ status: "active",
2146
+ validateParameters: input.parse,
2147
+ validateBinding: input.bind,
2148
+ verificationProjection: input.projection,
2149
+ envelopeProjection: input.projection
2150
+ };
2151
+ }
2152
+ var fileReadDefinition = fileDefinition({
2153
+ actionId: "file.read",
2154
+ humanLabel: "Read file",
2155
+ description: "Read a file from the connection's file store; content is delivered to the agent in the execution receipt, digest-correlated with the evidence.",
2156
+ parametersKind: "file.read.v1",
2157
+ requiredParameters: ["connectionId", "path"],
2158
+ materialParameters: ["connectionId", "path"],
2159
+ dataEffects: "Returns stored content to the agent (a SEE-equivalent on files); sealed files are rejected before effect.",
2160
+ reversibility: "reversible",
2161
+ riskClass: "R2",
2162
+ parse: (materialParameters) => {
2163
+ const parsed = FileReadParametersV1Schema.safeParse(materialParameters);
2164
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2165
+ },
2166
+ bind: ({ params, connection }) => filePathWithinAllowlist(params.path, connection.pathAllowlist ?? []) ? { ok: true } : { ok: false, reasonCode: "PATH_OUTSIDE_ALLOWLIST" },
2167
+ projection: (params) => fileReadVerificationProjection(params)
2168
+ });
2169
+ var fileWriteDefinition = fileDefinition({
2170
+ actionId: "file.write",
2171
+ humanLabel: "Write file",
2172
+ description: "Write agent-supplied content to a path inside the connection path allowlist; the written bytes are verified post-commit.",
2173
+ parametersKind: "file.write.v1",
2174
+ requiredParameters: ["connectionId", "path", "content"],
2175
+ materialParameters: ["connectionId", "path", "content"],
2176
+ dataEffects: "Creates or replaces a stored file.",
2177
+ reversibility: "limited",
2178
+ riskClass: "R2",
2179
+ parse: (materialParameters) => {
2180
+ const parsed = FileWriteParametersV1Schema.safeParse(materialParameters);
2181
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2182
+ },
2183
+ bind: ({ params, connection }) => filePathWithinAllowlist(params.path, connection.pathAllowlist ?? []) ? { ok: true } : { ok: false, reasonCode: "PATH_OUTSIDE_ALLOWLIST" },
2184
+ projection: (params) => fileWriteVerificationProjection(params)
2185
+ });
2186
+ var fileShareDefinition = fileDefinition({
2187
+ actionId: "file.share",
2188
+ humanLabel: "Share file",
2189
+ description: "Create a share grant for a file inside the connection's declared share allowlist; sealed (protected-materialized) files cannot be shared.",
2190
+ parametersKind: "file.share.v1",
2191
+ requiredParameters: ["connectionId", "path", "grantee"],
2192
+ materialParameters: ["connectionId", "path", "grantee", "expiresAtEpochMs"],
2193
+ dataEffects: "Grants the named grantee read access to the file (a DISCLOSE-equivalent on files); shares outside the declared scope deny before effect.",
2194
+ reversibility: "limited",
2195
+ riskClass: "R3",
2196
+ parse: (materialParameters) => {
2197
+ const parsed = FileShareParametersV1Schema.safeParse(materialParameters);
2198
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2199
+ },
2200
+ bind: ({ params, connection }) => filePathWithinAllowlist(params.path, connection.shareAllowlist ?? []) ? { ok: true } : { ok: false, reasonCode: "SHARE_OUTSIDE_ALLOWLIST" },
2201
+ projection: (params) => fileShareVerificationProjection(params)
2202
+ });
2203
+ var fileDeleteDefinition = fileDefinition({
2204
+ actionId: "file.delete",
2205
+ humanLabel: "Delete file",
2206
+ description: "Remove a file inside the connection path allowlist; absence is verified post-commit.",
2207
+ parametersKind: "file.delete.v1",
2208
+ requiredParameters: ["connectionId", "path"],
2209
+ materialParameters: ["connectionId", "path"],
2210
+ dataEffects: "Irreversibly removes the stored file.",
2211
+ reversibility: "destructive",
2212
+ riskClass: "R3",
2213
+ parse: (materialParameters) => {
2214
+ const parsed = FileDeleteParametersV1Schema.safeParse(materialParameters);
2215
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2216
+ },
2217
+ bind: ({ params, connection }) => filePathWithinAllowlist(params.path, connection.pathAllowlist ?? []) ? { ok: true } : { ok: false, reasonCode: "PATH_OUTSIDE_ALLOWLIST" },
2218
+ projection: (params) => fileDeleteVerificationProjection(params)
2219
+ });
2220
+ function protectedDefinition(input) {
2221
+ return {
2222
+ actionId: input.actionId,
2223
+ schemaVersion: 1,
2224
+ actionFamily: "protected-data",
2225
+ humanLabel: input.humanLabel,
2226
+ description: input.description,
2227
+ parametersKind: input.parametersKind,
2228
+ requiredParameters: input.requiredParameters,
2229
+ materialParameters: input.materialParameters,
2230
+ resourceType: "protected:data",
2231
+ providers: ["protected_data"],
2232
+ counterpartyRules: "USE/SEE/DISCLOSE are independent policy operations; the connection's declared catalog binds each one at field level (K12).",
2233
+ dataEffects: input.dataEffects,
2234
+ reversibility: input.reversibility,
2235
+ riskClass: input.riskClass,
2236
+ defaultDecisionPosture: "deny",
2237
+ approvalPresentationSchema: [
2238
+ "fieldRef",
2239
+ "counterparty",
2240
+ "sink",
2241
+ "maskedPreview"
2242
+ ],
2243
+ capabilityRequirements: { uses: 1 },
2244
+ executionContract: "prepare \u2192 readback \u2192 commit \u2192 observe \u2192 verified/unknown/rejected; the adapter resolves and injects the protected value at execution.",
2245
+ verificationContract: { level: "post-commit-content" },
2246
+ evidenceRequirements: [
2247
+ "effect.prepared",
2248
+ "commit.authorized",
2249
+ "effect.verified | effect.unknown | commit.rejected"
2250
+ ],
2251
+ version: 1,
2252
+ status: "active",
2253
+ validateParameters: input.parse,
2254
+ validateBinding: ({ params, connection }) => {
2255
+ const field = protectedCatalogField(
2256
+ connection.protectedCatalog,
2257
+ params.fieldRef
2258
+ );
2259
+ if (!field) {
2260
+ return { ok: false, reasonCode: "FIELD_NOT_IN_CATALOG" };
2261
+ }
2262
+ return input.bind({ params, field });
2263
+ },
2264
+ verificationProjection: input.projection,
2265
+ envelopeProjection: input.projection
2266
+ };
2267
+ }
2268
+ var protectedUseDefinition = protectedDefinition({
2269
+ actionId: "protected_data.use",
2270
+ humanLabel: "Use protected data",
2271
+ description: "Inject a catalog-declared protected field into a declared file sink at execution time; the agent never receives the plaintext.",
2272
+ parametersKind: "protected-data.use.v1",
2273
+ requiredParameters: ["connectionId", "fieldRef", "sink"],
2274
+ materialParameters: ["connectionId", "fieldRef", "sink"],
2275
+ dataEffects: "Materializes the protected value inside the declared sink file (sealed); the agent receives only path + digest evidence.",
2276
+ reversibility: "limited",
2277
+ riskClass: "R3",
2278
+ parse: (materialParameters) => {
2279
+ const parsed = ProtectedUseParametersV1Schema.safeParse(materialParameters);
2280
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2281
+ },
2282
+ bind: ({ params, field }) => field?.allowUse !== true ? { ok: false, reasonCode: "USE_NOT_ALLOWED" } : protectedSinkWithinUseSinks(params.sink, field.useSinks) ? { ok: true } : { ok: false, reasonCode: "SINK_OUTSIDE_DECLARED" },
2283
+ projection: (params) => protectedUseVerificationProjection(params)
2284
+ });
2285
+ var protectedViewDefinition = protectedDefinition({
2286
+ actionId: "protected_data.view",
2287
+ humanLabel: "View protected data",
2288
+ description: "Deliver a catalog-declared protected field to the agent once, via the execution receipt's delivered channel; requires allowView.",
2289
+ parametersKind: "protected-data.view.v1",
2290
+ requiredParameters: ["connectionId", "fieldRef"],
2291
+ materialParameters: ["connectionId", "fieldRef"],
2292
+ dataEffects: "Discloses the protected value to the agent (SEE); not persisted in the ledger.",
2293
+ reversibility: "limited",
2294
+ riskClass: "R3",
2295
+ parse: (materialParameters) => {
2296
+ const parsed = ProtectedViewParametersV1Schema.safeParse(materialParameters);
2297
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2298
+ },
2299
+ bind: ({ field }) => field?.allowView === true ? { ok: true } : { ok: false, reasonCode: "VIEW_NOT_ALLOWED" },
2300
+ projection: (params) => protectedViewVerificationProjection(params)
2301
+ });
2302
+ var protectedDiscloseDefinition = protectedDefinition({
2303
+ actionId: "protected_data.disclose",
2304
+ humanLabel: "Disclose protected data",
2305
+ description: "Send a catalog-declared protected field to a catalog-declared counterparty; without explicit authority the action denies before execution.",
2306
+ parametersKind: "protected-data.disclose.v1",
2307
+ requiredParameters: ["connectionId", "fieldRef", "counterparty"],
2308
+ materialParameters: ["connectionId", "fieldRef", "counterparty"],
2309
+ dataEffects: "Delivers the protected value to the declared counterparty only (DISCLOSE); the agent never receives it.",
2310
+ reversibility: "difficult",
2311
+ riskClass: "R4",
2312
+ parse: (materialParameters) => {
2313
+ const parsed = ProtectedDiscloseParametersV1Schema.safeParse(materialParameters);
2314
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2315
+ },
2316
+ bind: ({ params, field }) => field?.discloseTargets.includes(params.counterparty) === true ? { ok: true } : { ok: false, reasonCode: "DISCLOSE_TARGET_NOT_DECLARED" },
2317
+ projection: (params) => protectedDiscloseVerificationProjection(params)
2318
+ });
2319
+ var protectedPersistDefinition = protectedDefinition({
2320
+ actionId: "protected_data.persist",
2321
+ humanLabel: "Persist protected data",
2322
+ description: "Store an agent-supplied value into a catalog-declared protected field; requires allowPersist on the declared field.",
2323
+ parametersKind: "protected-data.persist.v1",
2324
+ requiredParameters: ["connectionId", "fieldRef", "value"],
2325
+ materialParameters: ["connectionId", "fieldRef", "value"],
2326
+ dataEffects: "Writes a new protected value into the declared field slot.",
2327
+ reversibility: "limited",
2328
+ riskClass: "R3",
2329
+ parse: (materialParameters) => {
2330
+ const parsed = ProtectedPersistParametersV1Schema.safeParse(materialParameters);
2331
+ return parsed.success ? { ok: true, params: parsed.data } : { ok: false };
2332
+ },
2333
+ bind: ({ field }) => field?.allowPersist === true ? { ok: true } : { ok: false, reasonCode: "PERSIST_NOT_ALLOWED" },
2334
+ projection: (params) => protectedPersistVerificationProjection(params)
2335
+ });
2336
+ var universalActionRegistry = createActionRegistry([
2337
+ emailSendDefinition,
2338
+ apiRequestDefinition,
2339
+ toolInvokeDefinition,
2340
+ mcpInstallDefinition,
2341
+ mcpEnableDefinition,
2342
+ calendarCreateDefinition,
2343
+ calendarUpdateDefinition,
2344
+ calendarCancelDefinition,
2345
+ calendarInviteDefinition,
2346
+ commercePurchaseDefinition,
2347
+ commerceRefundDefinition,
2348
+ commerceReturnDefinition,
2349
+ subscriptionCreateDefinition,
2350
+ fileReadDefinition,
2351
+ fileWriteDefinition,
2352
+ fileShareDefinition,
2353
+ fileDeleteDefinition,
2354
+ protectedUseDefinition,
2355
+ protectedViewDefinition,
2356
+ protectedDiscloseDefinition,
2357
+ protectedPersistDefinition
2358
+ ]);
2359
+
2360
+ // src/contracts/approval.ts
2361
+ import { z as z12 } from "zod";
2362
+
2363
+ // src/contracts/evidence.ts
2364
+ import { z as z11 } from "zod";
2365
+ var Sha256DigestSchema = z11.string().regex(/^[a-f0-9]{64}$/);
2366
+ var AttemptReferenceSchema = z11.string().trim().min(1).max(256).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
2367
+ var FailureCodeSchema = z11.string().trim().min(1).max(128).regex(/^[A-Z][A-Z0-9_]*$/);
2368
+ var payload = (eventType, shape) => z11.object({
2369
+ eventType: z11.literal(eventType),
2370
+ payload: z11.object(shape).strict()
2371
+ }).strict();
2372
+ var EvidenceEventSchema = z11.discriminatedUnion("eventType", [
2373
+ payload("AUTHENTICATION_RECORDED", {
2374
+ interface: AgentInterfaceSchema,
2375
+ outcome: z11.enum(["authenticated", "rejected"]),
2376
+ credentialReference: z11.string().trim().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/)
2377
+ }),
2378
+ payload("ADAPTER_AUTHENTICATION_RECORDED", {
2379
+ adapterId: AdapterIdSchema,
2380
+ outcome: z11.literal("authenticated"),
2381
+ credentialReference: AttemptReferenceSchema
2382
+ }),
2383
+ payload("REQUEST_RECEIVED", { actionDigest: Sha256DigestSchema }),
2384
+ payload("ACTION_NORMALIZED", { actionDigest: Sha256DigestSchema }),
2385
+ z11.object({
2386
+ eventType: z11.literal("DECISION_RECORDED"),
2387
+ payload: z11.discriminatedUnion("decision", [
2388
+ z11.object({
2389
+ actionDigest: Sha256DigestSchema,
2390
+ actionDecisionDigest: Sha256DigestSchema,
2391
+ decision: z11.literal("ALLOW"),
2392
+ reasonCode: z11.literal("AUTHORIZED")
2393
+ }).strict(),
2394
+ z11.object({
2395
+ actionDigest: Sha256DigestSchema,
2396
+ actionDecisionDigest: Sha256DigestSchema,
2397
+ decision: z11.literal("ASK"),
2398
+ reasonCode: z11.literal("APPROVAL_REQUIRED")
2399
+ }).strict(),
2400
+ z11.object({
2401
+ actionDigest: Sha256DigestSchema,
2402
+ actionDecisionDigest: Sha256DigestSchema,
2403
+ decision: z11.literal("DENY"),
2404
+ reasonCode: DenyReasonCodeSchema
2405
+ }).strict()
2406
+ ])
2407
+ }).strict(),
2408
+ payload("APPROVAL_STATE_REFERENCED", {
2409
+ approvalId: ApprovalIdSchema,
2410
+ /** Approval record version this state evidence describes; versions are contiguous per chain. */
2411
+ version: z11.number().int().positive().safe(),
2412
+ state: z11.enum([
2413
+ "pending",
2414
+ "approved",
2415
+ "denied",
2416
+ "expired",
2417
+ "revoked",
2418
+ "superseded",
2419
+ "consumed"
2420
+ ]),
2421
+ approvalDigest: Sha256DigestSchema
2422
+ }),
2423
+ payload("APPROVAL_PRESENTED", {
2424
+ approvalId: ApprovalIdSchema,
2425
+ presentationDigest: Sha256DigestSchema,
2426
+ viewerReference: AttemptReferenceSchema
2427
+ }),
2428
+ payload("APPROVAL_DECISION_RECORDED", {
2429
+ approvalId: ApprovalIdSchema,
2430
+ /** Approval record version the human decision produced. */
2431
+ version: z11.number().int().positive().safe(),
2432
+ decision: z11.enum(["approve", "deny", "revoke"]),
2433
+ actorPrincipalId: PrincipalIdSchema,
2434
+ actorReference: AttemptReferenceSchema,
2435
+ presentationDigest: Sha256DigestSchema
2436
+ }),
2437
+ payload("APPROVAL_REJECTED", {
2438
+ approvalId: ApprovalIdSchema,
2439
+ reasonCode: FailureCodeSchema
2440
+ }),
2441
+ payload("CAPABILITY_STATE_REFERENCED", {
2442
+ actionDigest: Sha256DigestSchema,
2443
+ capabilityId: CapabilityIdSchema,
2444
+ state: z11.enum(["issued", "used", "expired", "revoked"]),
2445
+ capabilityDigest: Sha256DigestSchema
2446
+ }),
2447
+ payload("CAPABILITY_REJECTED", {
2448
+ actionDigest: Sha256DigestSchema,
2449
+ reasonCode: FailureCodeSchema
2450
+ }),
2451
+ payload("EXECUTION_ATTEMPTED", {
2452
+ actionDigest: Sha256DigestSchema,
2453
+ adapterId: AdapterIdSchema,
2454
+ attemptReference: AttemptReferenceSchema,
2455
+ idempotencyDigest: Sha256DigestSchema
2456
+ }),
2457
+ payload("EXECUTION_ABORTED", {
2458
+ actionDigest: Sha256DigestSchema,
2459
+ adapterId: AdapterIdSchema,
2460
+ attemptReference: AttemptReferenceSchema,
2461
+ reasonCode: FailureCodeSchema
2462
+ }),
2463
+ payload("EFFECT_PREPARED", {
2464
+ actionDigest: Sha256DigestSchema,
2465
+ adapterId: AdapterIdSchema,
2466
+ attemptReference: AttemptReferenceSchema,
2467
+ preparedDigest: Sha256DigestSchema
2468
+ }),
2469
+ payload("EFFECT_COMMIT_AUTHORIZED", {
2470
+ actionDigest: Sha256DigestSchema,
2471
+ adapterId: AdapterIdSchema,
2472
+ attemptReference: AttemptReferenceSchema,
2473
+ actionDecisionDigest: Sha256DigestSchema
2474
+ }),
2475
+ payload("EFFECT_OUTCOME_UNKNOWN", {
2476
+ actionDigest: Sha256DigestSchema,
2477
+ adapterId: AdapterIdSchema,
2478
+ attemptReference: AttemptReferenceSchema,
2479
+ reasonCode: FailureCodeSchema
2480
+ }),
2481
+ payload("EFFECT_UNRESOLVED", {
2482
+ actionDigest: Sha256DigestSchema,
2483
+ adapterId: AdapterIdSchema,
2484
+ attemptReference: AttemptReferenceSchema
2485
+ }),
2486
+ payload("EFFECT_VERIFIED", {
2487
+ actionDigest: Sha256DigestSchema,
2488
+ adapterId: AdapterIdSchema,
2489
+ attemptReference: AttemptReferenceSchema,
2490
+ effectDigest: Sha256DigestSchema
2491
+ }),
2492
+ payload("EXECUTION_FAILED", {
2493
+ actionDigest: Sha256DigestSchema,
2494
+ adapterId: AdapterIdSchema,
2495
+ attemptReference: AttemptReferenceSchema,
2496
+ failureCode: FailureCodeSchema,
2497
+ failureDigest: Sha256DigestSchema.optional()
2498
+ }),
2499
+ payload("AUTHORITY_REVOKED", {
2500
+ grantId: GrantIdSchema,
2501
+ grantVersion: z11.number().int().positive(),
2502
+ revocationDigest: Sha256DigestSchema
2503
+ })
2504
+ ]);
2505
+ var EvidenceContextShape = {
2506
+ schemaVersion: z11.literal(1),
2507
+ id: EvidenceIdSchema,
2508
+ tenantId: TenantIdSchema,
2509
+ principalId: PrincipalIdSchema,
2510
+ agentId: AgentIdSchema,
2511
+ requestId: RequestIdSchema,
2512
+ correlationId: CorrelationIdSchema,
2513
+ eventTimeEpochMs: EpochMillisecondsSchema
2514
+ };
2515
+ var EvidenceChainContextSchema = z11.object({
2516
+ tenantId: TenantIdSchema,
2517
+ principalId: PrincipalIdSchema,
2518
+ agentId: AgentIdSchema,
2519
+ requestId: RequestIdSchema,
2520
+ correlationId: CorrelationIdSchema
2521
+ }).strict();
2522
+ var EvidenceEventDraftSchema = z11.object({ ...EvidenceContextShape, event: EvidenceEventSchema }).strict();
2523
+ var EvidenceRecordCoreSchema = z11.object({
2524
+ ...EvidenceContextShape,
2525
+ event: EvidenceEventSchema,
2526
+ sequence: z11.number().int().positive().max(Number.MAX_SAFE_INTEGER),
2527
+ previousRecordHash: Sha256DigestSchema.nullable(),
2528
+ payloadDigest: Sha256DigestSchema
2529
+ }).strict();
2530
+ var EvidenceRecordSchema = z11.object({
2531
+ ...EvidenceRecordCoreSchema.shape,
2532
+ recordHash: Sha256DigestSchema
2533
+ }).strict();
2534
+ var HumanSessionRejectionSchema = z11.enum([
2535
+ "UNAUTHENTICATED",
2536
+ "NOT_HUMAN",
2537
+ "UNSAFE_SESSION_REFERENCE"
2538
+ ]);
2539
+ var PreIdentitySecurityEventSchema = z11.discriminatedUnion(
2540
+ "eventType",
2541
+ [
2542
+ z11.object({
2543
+ eventType: z11.literal("AUTHENTICATION_REJECTED"),
2544
+ payload: z11.object({
2545
+ interface: z11.enum(["mcp", "rest"]),
2546
+ reasonCode: z11.literal("UNAUTHENTICATED")
2547
+ }).strict()
2548
+ }).strict(),
2549
+ z11.object({
2550
+ eventType: z11.literal("HUMAN_AUTHENTICATION_REJECTED"),
2551
+ payload: z11.object({
2552
+ surface: z11.literal("approval-service"),
2553
+ reasonCode: HumanSessionRejectionSchema
2554
+ }).strict()
2555
+ }).strict(),
2556
+ z11.object({
2557
+ eventType: z11.literal("ADAPTER_AUTHENTICATION_REJECTED"),
2558
+ payload: z11.object({
2559
+ surface: z11.literal("redemption"),
2560
+ reasonCode: z11.literal("UNAUTHENTICATED")
2561
+ }).strict()
2562
+ }).strict()
2563
+ ]
2564
+ );
2565
+ var PreIdentitySecurityRecordCoreSchema = z11.object({
2566
+ schemaVersion: z11.literal(1),
2567
+ id: EvidenceIdSchema,
2568
+ eventTimeEpochMs: EpochMillisecondsSchema,
2569
+ event: PreIdentitySecurityEventSchema
2570
+ }).strict();
2571
+ var PreIdentitySecurityRecordSchema = PreIdentitySecurityRecordCoreSchema.extend({
2572
+ recordHash: Sha256DigestSchema
2573
+ }).strict();
2574
+
2575
+ // src/contracts/approval.ts
2576
+ var GrantVersionReferenceSchema = z12.object({ id: GrantIdSchema, version: z12.number().int().positive() }).strict();
2577
+ var AgentStatementSchema = z12.object({
2578
+ source: z12.literal("agent"),
2579
+ authoritative: z12.literal(false),
2580
+ text: z12.string().trim().min(1).max(1024)
2581
+ }).strict();
2582
+ var ApprovalReasonSchema = z12.object({
2583
+ source: z12.literal("guardian"),
2584
+ code: z12.literal("APPROVAL_REQUIRED"),
2585
+ authority: AuthorityReferenceSchema
2586
+ }).strict();
2587
+ var ApprovalPresentationSchema = z12.object({
2588
+ schemaVersion: z12.literal(1),
2589
+ approvalId: ApprovalIdSchema,
2590
+ tenantId: TenantIdSchema,
2591
+ principalId: PrincipalIdSchema,
2592
+ agentId: AgentIdSchema,
2593
+ requestId: RequestIdSchema,
2594
+ correlationId: CorrelationIdSchema,
2595
+ action: ActionTypeSchema,
2596
+ resource: ResourceRefSchema,
2597
+ adapterId: AdapterIdSchema,
2598
+ interface: AgentInterfaceSchema,
2599
+ initiatingContext: z12.enum(["user", "scheduled", "agent", "external-event"]),
2600
+ materialParameters: MaterialParametersSchema,
2601
+ valueCents: z12.number().int().nonnegative().safe().optional(),
2602
+ uses: z12.number().int().positive().safe(),
2603
+ agentStatement: AgentStatementSchema,
2604
+ reason: ApprovalReasonSchema,
2605
+ actionDigest: Sha256DigestSchema,
2606
+ expiresAtEpochMs: EpochMillisecondsSchema
2607
+ }).strict();
2608
+ var ApprovalBindingSchema = z12.object({
2609
+ schemaVersion: z12.literal(1),
2610
+ approvalId: ApprovalIdSchema,
2611
+ tenantId: TenantIdSchema,
2612
+ principalId: PrincipalIdSchema,
2613
+ agentId: AgentIdSchema,
2614
+ requestId: RequestIdSchema,
2615
+ correlationId: CorrelationIdSchema,
2616
+ actionDigest: Sha256DigestSchema,
2617
+ /**
2618
+ * Evidence link to the exact ASK decision record that created the approval.
2619
+ * It covers evaluation time, so it is never re-derived; askDecisionDigest is
2620
+ * the decision binding that issuance and redemption verify.
2621
+ */
2622
+ actionDecisionRecordDigest: Sha256DigestSchema,
2623
+ /**
2624
+ * Digest of the time-independent projection of that ASK decision: the exact
2625
+ * canonical action, the ASK outcome, and the principal, agent, and complete
2626
+ * grant chain it was evaluated under. Every later use recomputes it from the
2627
+ * fresh decision or the current trusted authority and requires it to match.
2628
+ */
2629
+ askDecisionDigest: Sha256DigestSchema,
2630
+ adapterId: AdapterIdSchema,
2631
+ policy: PolicyReferenceSchema,
2632
+ evaluatedGrants: z12.array(GrantVersionReferenceSchema).min(1).max(32),
2633
+ presentationDigest: Sha256DigestSchema,
2634
+ requestedAtEpochMs: EpochMillisecondsSchema,
2635
+ expiresAtEpochMs: EpochMillisecondsSchema
2636
+ }).strict().superRefine((binding, context) => {
2637
+ if (binding.requestedAtEpochMs >= binding.expiresAtEpochMs) {
2638
+ context.addIssue({
2639
+ code: "custom",
2640
+ message: "approval expiry must follow request time",
2641
+ path: ["expiresAtEpochMs"]
2642
+ });
2643
+ }
2644
+ });
2645
+ var ApprovalStateSchema = z12.enum([
2646
+ "pending",
2647
+ "approved",
2648
+ "denied",
2649
+ "expired",
2650
+ "revoked",
2651
+ "superseded",
2652
+ "consumed"
2653
+ ]);
2654
+ var ApprovalRecordSchema = z12.object({
2655
+ schemaVersion: z12.literal(1),
2656
+ binding: ApprovalBindingSchema,
2657
+ state: ApprovalStateSchema,
2658
+ version: z12.number().int().positive(),
2659
+ updatedAtEpochMs: EpochMillisecondsSchema,
2660
+ decidedByPrincipalId: PrincipalIdSchema.nullable()
2661
+ }).strict().superRefine((record, context) => {
2662
+ const humanDecision = ["approved", "denied", "revoked"].includes(
2663
+ record.state
2664
+ );
2665
+ if (humanDecision !== (record.decidedByPrincipalId !== null)) {
2666
+ context.addIssue({
2667
+ code: "custom",
2668
+ message: "human decisions require the deciding principal",
2669
+ path: ["decidedByPrincipalId"]
2670
+ });
2671
+ }
2672
+ if (record.updatedAtEpochMs < record.binding.requestedAtEpochMs || record.updatedAtEpochMs > record.binding.expiresAtEpochMs) {
2673
+ context.addIssue({
2674
+ code: "custom",
2675
+ message: "approval update time must stay inside its validity window",
2676
+ path: ["updatedAtEpochMs"]
2677
+ });
2678
+ }
2679
+ });
2680
+ var ApprovalDecisionRequestSchema = z12.object({
2681
+ schemaVersion: z12.literal(1),
2682
+ decision: z12.enum(["approve", "deny"]),
2683
+ expectedVersion: z12.number().int().positive().safe(),
2684
+ presentationDigest: Sha256DigestSchema
2685
+ }).strict();
2686
+ var ApprovalRevocationRequestSchema = z12.object({
2687
+ schemaVersion: z12.literal(1),
2688
+ expectedVersion: z12.number().int().positive().safe()
2689
+ }).strict();
2690
+ var ApprovalChallengeSchema = z12.object({
2691
+ approvalId: ApprovalIdSchema,
2692
+ version: z12.number().int().positive().safe(),
2693
+ presentationDigest: Sha256DigestSchema,
2694
+ expiresAtEpochMs: EpochMillisecondsSchema
2695
+ }).strict();
2696
+ var ApprovalReceiptSchema = z12.object({
2697
+ schemaVersion: z12.literal(1),
2698
+ approvalId: ApprovalIdSchema,
2699
+ state: ApprovalStateSchema,
2700
+ version: z12.number().int().positive().safe(),
2701
+ updatedAtEpochMs: EpochMillisecondsSchema,
2702
+ approvalDigest: Sha256DigestSchema
2703
+ }).strict();
2704
+
2705
+ // src/contracts/authority-state.ts
2706
+ import { z as z13 } from "zod";
2707
+ var AuthorityStateSnapshotSchema = z13.object({
2708
+ principal: PrincipalSchema,
2709
+ agent: AgentIdentitySchema,
2710
+ policy: PolicyReferenceSchema,
2711
+ authorityChain: z13.array(AuthorityGrantSchema).max(32),
2712
+ revocations: z13.array(RevocationSchema).max(256),
2713
+ allowedAdapterIds: z13.array(AdapterIdSchema).max(64),
2714
+ budgets: z13.array(BudgetStateSchema).max(64).default([])
2715
+ }).strict();
2716
+
2717
+ // src/contracts/capability.ts
2718
+ import { z as z14 } from "zod";
2719
+ var CapabilityKeyIdSchema = z14.string().regex(/^key_[A-Za-z0-9][A-Za-z0-9_-]{7,119}$/).brand();
2720
+ var CapabilityClaimsSchema = z14.object({
2721
+ schemaVersion: z14.literal(1),
2722
+ capabilityId: CapabilityIdSchema,
2723
+ tenantId: TenantIdSchema,
2724
+ principalId: PrincipalIdSchema,
2725
+ agentId: AgentIdSchema,
2726
+ requestId: RequestIdSchema,
2727
+ actionDigest: z14.string().regex(/^[a-f0-9]{64}$/),
2728
+ actionNonce: NonceSchema,
2729
+ audience: AdapterIdSchema,
2730
+ policy: PolicyReferenceSchema,
2731
+ evaluatedGrants: z14.array(
2732
+ z14.object({
2733
+ id: GrantIdSchema,
2734
+ version: z14.number().int().positive()
2735
+ }).strict()
2736
+ ).min(1).max(32),
2737
+ issuedAtEpochMs: z14.number().int().nonnegative(),
2738
+ expiresAtEpochMs: z14.number().int().positive(),
2739
+ maxUses: z14.literal(1),
2740
+ keyId: CapabilityKeyIdSchema,
2741
+ algorithm: z14.literal("HS256")
2742
+ }).strict().superRefine((value, context) => {
2743
+ if (value.issuedAtEpochMs >= value.expiresAtEpochMs) {
2744
+ context.addIssue({
2745
+ code: "custom",
2746
+ path: ["expiresAtEpochMs"],
2747
+ message: "capability expiry must be later than issuance"
2748
+ });
2749
+ }
2750
+ if (new Set(value.evaluatedGrants.map(({ id }) => id)).size !== value.evaluatedGrants.length) {
2751
+ context.addIssue({
2752
+ code: "custom",
2753
+ path: ["evaluatedGrants"],
2754
+ message: "evaluated grant identifiers must be unique"
2755
+ });
2756
+ }
2757
+ });
2758
+
2759
+ // src/contracts/execution.ts
2760
+ import { z as z15 } from "zod";
2761
+ var ProviderSchema = z15.enum([
2762
+ "gmail",
2763
+ "graph",
2764
+ "api",
2765
+ "tool",
2766
+ "gcalendar",
2767
+ "payment-sandbox",
2768
+ "file",
2769
+ "protected_data"
2770
+ ]);
2771
+ var ConnectionProfileSchema = z15.enum([
2772
+ "gmail-split-compose-metadata",
2773
+ "gmail-compose-readonly",
2774
+ "graph-readwrite-send",
2775
+ "api-bearer-request",
2776
+ "tool-mcp-server",
2777
+ "gcalendar-events-v1",
2778
+ "payment-sandbox-charge",
2779
+ "file-local-store",
2780
+ "protected-data-store"
2781
+ ]);
2782
+ var VerificationLevelSchema = z15.enum([
2783
+ "post-commit-content",
2784
+ "pre-commit-content+post-commit-envelope"
2785
+ ]);
2786
+ var LeasePurposeSchema = z15.enum([
2787
+ "account",
2788
+ "prepare",
2789
+ "readback",
2790
+ "commit",
2791
+ "observe",
2792
+ "cleanup"
2793
+ ]);
2794
+ var ExecutionStateSchema = z15.enum([
2795
+ "reserved",
2796
+ "redeemed",
2797
+ "preparing",
2798
+ "prepared",
2799
+ "committing",
2800
+ "unknown",
2801
+ "unresolved",
2802
+ "verified",
2803
+ "mismatch",
2804
+ "commit-rejected",
2805
+ "aborted"
2806
+ ]);
2807
+ var ExecutionFailureCodeSchema = z15.enum([
2808
+ "SENDER_ACCOUNT_MISMATCH",
2809
+ "CREDENTIAL_UNAVAILABLE",
2810
+ "PREPARE_REJECTED",
2811
+ "PREPARE_AMBIGUOUS",
2812
+ "READBACK_UNAVAILABLE",
2813
+ "PREPARED_CONTENT_MISMATCH",
2814
+ "COMMIT_NOT_AUTHORIZED",
2815
+ "AUTHORITY_REVOKED_BEFORE_COMMIT",
2816
+ "COMMIT_DEADLINE_EXPIRED",
2817
+ "ABORTED_BEFORE_COMMIT",
2818
+ "COMMIT_NOT_ISSUED",
2819
+ "COMMIT_REJECTED",
2820
+ "EFFECT_MISMATCH",
2821
+ "DUPLICATE_EFFECT_DETECTED"
2822
+ ]);
2823
+ var ExecutionAbortReasonSchema = z15.literal(
2824
+ "INTERRUPTED_BEFORE_ATTEMPT"
2825
+ );
2826
+ var ExecutionUnknownReasonSchema = z15.enum([
2827
+ "COMMIT_RESPONSE_LOST",
2828
+ "COMMIT_TIMEOUT",
2829
+ "COMMIT_SERVER_ERROR",
2830
+ "COMMIT_RESPONSE_INVALID",
2831
+ "COMMIT_REDIRECT_REFUSED",
2832
+ "COMMIT_TARGET_NOT_FOUND",
2833
+ "REJECTION_UNCONFIRMED",
2834
+ "AWAITING_PROVIDER_RECORD",
2835
+ "VERIFICATION_UNAVAILABLE"
2836
+ ]);
2837
+ var ProviderIdSchema = z15.string().regex(/^[A-Za-z0-9_=+/-]{1,512}$/);
2838
+ var InternetMessageIdSchema = z15.string().regex(/^<[\x21-\x3b\x3d\x3f-\x7e]{1,995}>$/);
2839
+ var ProviderObjectRefSchema = z15.discriminatedUnion("kind", [
2840
+ z15.object({
2841
+ kind: z15.literal("gmail-draft"),
2842
+ draftId: ProviderIdSchema,
2843
+ messageId: ProviderIdSchema,
2844
+ threadId: ProviderIdSchema
2845
+ }).strict(),
2846
+ z15.object({
2847
+ kind: z15.literal("graph-message"),
2848
+ immutableId: ProviderIdSchema,
2849
+ internetMessageId: InternetMessageIdSchema
2850
+ }).strict(),
2851
+ z15.object({
2852
+ kind: z15.literal("api-request"),
2853
+ requestDigest: ProviderIdSchema
2854
+ }).strict(),
2855
+ z15.object({
2856
+ kind: z15.literal("tool-call"),
2857
+ callDigest: Sha256DigestSchema
2858
+ }).strict(),
2859
+ z15.object({
2860
+ kind: z15.literal("gcalendar-plan"),
2861
+ requestDigest: ProviderIdSchema
2862
+ }).strict(),
2863
+ z15.object({
2864
+ kind: z15.literal("payment-order"),
2865
+ orderId: ProviderIdSchema,
2866
+ intentId: ProviderIdSchema
2867
+ }).strict(),
2868
+ z15.object({
2869
+ kind: z15.literal("file-request"),
2870
+ requestDigest: ProviderIdSchema
2871
+ }).strict(),
2872
+ z15.object({
2873
+ kind: z15.literal("protected_data-op"),
2874
+ operationDigest: ProviderIdSchema
2875
+ }).strict()
2876
+ ]);
2877
+ var ProviderSentRefSchema = z15.discriminatedUnion("kind", [
2878
+ z15.object({
2879
+ kind: z15.literal("gmail-message"),
2880
+ messageId: ProviderIdSchema,
2881
+ threadId: ProviderIdSchema
2882
+ }).strict(),
2883
+ z15.object({
2884
+ kind: z15.literal("graph-message"),
2885
+ immutableId: ProviderIdSchema
2886
+ }).strict(),
2887
+ z15.object({
2888
+ kind: z15.literal("api-response"),
2889
+ status: z15.number().int().min(0).max(999),
2890
+ bodyBytes: z15.number().int().min(0),
2891
+ headersDigest: Sha256DigestSchema,
2892
+ bodyDigest: Sha256DigestSchema,
2893
+ bodyPreview: z15.string().max(512),
2894
+ truncated: z15.boolean()
2895
+ }).strict(),
2896
+ z15.object({
2897
+ kind: z15.literal("tool-response"),
2898
+ operation: z15.enum(["invoke", "install", "enable"]),
2899
+ toolName: z15.string().min(1).max(64),
2900
+ status: z15.number().int().min(0).max(999).nullable(),
2901
+ resultDigest: Sha256DigestSchema,
2902
+ detail: z15.string().max(256)
2903
+ }).strict(),
2904
+ z15.object({
2905
+ kind: z15.literal("gcalendar-event"),
2906
+ eventId: ProviderIdSchema,
2907
+ status: z15.enum(["confirmed", "cancelled"])
2908
+ }).strict(),
2909
+ z15.object({
2910
+ kind: z15.literal("payment-charge"),
2911
+ chargeId: ProviderIdSchema,
2912
+ amountCents: z15.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
2913
+ currency: z15.string().trim().min(3).max(3),
2914
+ status: z15.enum(["succeeded", "refunded", "returned", "voided"])
2915
+ }).strict(),
2916
+ z15.object({
2917
+ kind: z15.literal("file-effect"),
2918
+ operation: z15.enum(["read", "write", "share", "delete"]),
2919
+ path: z15.string().min(1).max(512),
2920
+ contentDigest: Sha256DigestSchema.nullable(),
2921
+ bytes: z15.number().int().min(0),
2922
+ shareId: ProviderIdSchema.nullable(),
2923
+ grantee: z15.string().max(256).nullable()
2924
+ }).strict(),
2925
+ z15.object({
2926
+ kind: z15.literal("protected_data-effect"),
2927
+ operation: z15.enum(["use", "view", "disclose", "persist"]),
2928
+ fieldRef: z15.string().min(1).max(64),
2929
+ valueDigest: Sha256DigestSchema,
2930
+ counterparty: z15.string().max(256).nullable(),
2931
+ maskedPreview: z15.string().max(120).nullable()
2932
+ }).strict()
2933
+ ]);
2934
+ var VerifiedOutcomeSchema = z15.object({
2935
+ kind: z15.literal("verified"),
2936
+ sentRef: ProviderSentRefSchema,
2937
+ observedDigest: Sha256DigestSchema,
2938
+ effectDigest: Sha256DigestSchema,
2939
+ level: VerificationLevelSchema,
2940
+ observedAtEpochMs: EpochMillisecondsSchema
2941
+ }).strict();
2942
+ var MismatchOutcomeSchema = z15.object({
2943
+ kind: z15.literal("mismatch"),
2944
+ code: z15.enum(["EFFECT_MISMATCH", "DUPLICATE_EFFECT_DETECTED"]),
2945
+ observedDigest: Sha256DigestSchema.nullable()
2946
+ }).strict();
2947
+ var CommitRejectedOutcomeSchema = z15.object({
2948
+ kind: z15.literal("commit-rejected"),
2949
+ providerStatus: z15.union([
2950
+ z15.literal(0),
2951
+ z15.literal(400),
2952
+ z15.literal(401),
2953
+ z15.literal(403),
2954
+ z15.literal(404),
2955
+ z15.literal(409),
2956
+ z15.literal(413),
2957
+ z15.literal(422),
2958
+ z15.literal(429)
2959
+ ]),
2960
+ confirmedAtEpochMs: EpochMillisecondsSchema
2961
+ }).strict();
2962
+ var UnknownOutcomeSchema = z15.object({
2963
+ kind: z15.literal("unknown"),
2964
+ reason: ExecutionUnknownReasonSchema
2965
+ }).strict();
2966
+ var AbortedOutcomeSchema = z15.object({
2967
+ kind: z15.literal("aborted"),
2968
+ code: ExecutionFailureCodeSchema,
2969
+ orphanDraftPossible: z15.boolean()
2970
+ }).strict();
2971
+ var UnresolvedOutcomeSchema = z15.object({
2972
+ kind: z15.literal("unresolved"),
2973
+ reason: ExecutionUnknownReasonSchema
2974
+ }).strict();
2975
+ var OutcomeSchema = z15.discriminatedUnion("kind", [
2976
+ VerifiedOutcomeSchema,
2977
+ MismatchOutcomeSchema,
2978
+ CommitRejectedOutcomeSchema,
2979
+ UnknownOutcomeSchema,
2980
+ AbortedOutcomeSchema,
2981
+ UnresolvedOutcomeSchema
2982
+ ]);
2983
+ var PendingOutcomeSchema = OutcomeSchema;
2984
+ var ExecutionRecordSchema = z15.object({
2985
+ schemaVersion: z15.literal(1),
2986
+ attemptId: AttemptIdSchema,
2987
+ tenantId: TenantIdSchema,
2988
+ principalId: PrincipalIdSchema,
2989
+ agentId: AgentIdSchema,
2990
+ requestId: RequestIdSchema,
2991
+ correlationId: CorrelationIdSchema,
2992
+ capabilityId: CapabilityIdSchema,
2993
+ actionDigest: Sha256DigestSchema,
2994
+ adapterId: AdapterIdSchema,
2995
+ connectionId: ConnectionIdSchema,
2996
+ provider: ProviderSchema,
2997
+ profile: ConnectionProfileSchema,
2998
+ effectKey: Sha256DigestSchema,
2999
+ expectedProjectionDigest: Sha256DigestSchema,
3000
+ expectedEnvelopeDigest: Sha256DigestSchema,
3001
+ state: ExecutionStateSchema,
3002
+ version: z15.number().int().positive().safe(),
3003
+ reservedAtEpochMs: EpochMillisecondsSchema,
3004
+ updatedAtEpochMs: EpochMillisecondsSchema,
3005
+ commitDeadlineEpochMs: EpochMillisecondsSchema,
3006
+ commitLeaseDeadlineEpochMs: EpochMillisecondsSchema.nullable(),
3007
+ reconcileDeadlineEpochMs: EpochMillisecondsSchema.nullable(),
3008
+ lateReconcileDeadlineEpochMs: EpochMillisecondsSchema.nullable(),
3009
+ commitLeaseIssued: z15.boolean(),
3010
+ providerObjectRef: ProviderObjectRefSchema.nullable(),
3011
+ sentRef: ProviderSentRefSchema.nullable(),
3012
+ orphanDraftPossible: z15.boolean(),
3013
+ pendingOutcome: PendingOutcomeSchema.nullable(),
3014
+ outcome: OutcomeSchema.nullable()
3015
+ }).strict();
3016
+ var AdapterRedemptionRequestSchema = z15.object({
3017
+ schemaVersion: z15.literal(1),
3018
+ capabilityToken: z15.string().min(1).max(512),
3019
+ requestId: RequestIdSchema,
3020
+ presentedBy: z15.object({
3021
+ tenantId: TenantIdSchema,
3022
+ principalId: PrincipalIdSchema,
3023
+ agentId: AgentIdSchema
3024
+ }).strict()
3025
+ }).strict();
3026
+ var ExecutionGrantSchema = z15.object({
3027
+ schemaVersion: z15.literal(1),
3028
+ attemptId: AttemptIdSchema,
3029
+ version: z15.number().int().positive().safe(),
3030
+ connectionId: ConnectionIdSchema,
3031
+ provider: ProviderSchema,
3032
+ profile: ConnectionProfileSchema,
3033
+ action: CanonicalActionSchema,
3034
+ expectedProjectionDigest: Sha256DigestSchema,
3035
+ expectedEnvelopeDigest: Sha256DigestSchema,
3036
+ commitDeadlineEpochMs: EpochMillisecondsSchema
3037
+ }).strict();
3038
+ var expectedVersion = z15.number().int().positive().safe();
3039
+ var CommandOutcomeSchema = z15.discriminatedUnion("kind", [
3040
+ VerifiedOutcomeSchema.omit({ effectDigest: true }),
3041
+ MismatchOutcomeSchema,
3042
+ CommitRejectedOutcomeSchema,
3043
+ UnknownOutcomeSchema
3044
+ ]);
3045
+ var ExecutionStageCommandSchema = z15.discriminatedUnion("command", [
3046
+ z15.object({ command: z15.literal("begin-prepare"), expectedVersion }).strict(),
3047
+ z15.object({
3048
+ command: z15.literal("prepared"),
3049
+ expectedVersion,
3050
+ providerObjectRef: ProviderObjectRefSchema,
3051
+ preparedProjectionDigest: Sha256DigestSchema
3052
+ }).strict(),
3053
+ z15.object({ command: z15.literal("commit-request"), expectedVersion }).strict(),
3054
+ z15.object({
3055
+ command: z15.literal("outcome"),
3056
+ expectedVersion,
3057
+ outcome: CommandOutcomeSchema
3058
+ }).strict(),
3059
+ z15.object({
3060
+ command: z15.literal("abort"),
3061
+ expectedVersion,
3062
+ code: ExecutionFailureCodeSchema,
3063
+ orphanDraftPossible: z15.boolean()
3064
+ }).strict()
3065
+ ]);
3066
+ var AgentExecutionRequestSchema = z15.object({
3067
+ schemaVersion: z15.literal(1),
3068
+ requestId: RequestIdSchema,
3069
+ capabilityToken: z15.string().min(1).max(512)
3070
+ }).strict();
3071
+ var AgentExecutionReceiptSchema = z15.object({
3072
+ schemaVersion: z15.literal(1),
3073
+ requestId: RequestIdSchema,
3074
+ receiptReference: AttemptIdSchema,
3075
+ status: z15.enum([
3076
+ "sent-verified",
3077
+ "not-sent",
3078
+ "outcome-pending",
3079
+ "outcome-unconfirmed"
3080
+ ]),
3081
+ /**
3082
+ * Inline-only delivery channel (WO-013). Carries the value the action was
3083
+ * authorized to hand the agent — a file's content for `file.read` or the
3084
+ * field value for `protected_data.view` — digest-correlated with the
3085
+ * sentRef. Never written to the ledger; only `protected_data.view` and
3086
+ * `file.read` produce it and only on a `sent-verified` receipt.
3087
+ */
3088
+ delivered: z15.object({
3089
+ kind: z15.enum(["file-content", "protected-value"]),
3090
+ fieldRef: z15.string().min(1).max(64).nullable(),
3091
+ path: z15.string().min(1).max(512).nullable(),
3092
+ value: z15.string().max(64e3),
3093
+ valueDigest: Sha256DigestSchema
3094
+ }).strict().optional()
3095
+ }).strict();
3096
+
3097
+ // src/proposal.ts
3098
+ var AgentActionProposalSchema = z16.object({
3099
+ schemaVersion: z16.literal(1),
3100
+ requestId: RequestIdSchema,
3101
+ correlationId: CorrelationIdSchema,
3102
+ action: ActionTypeSchema,
3103
+ resource: ResourceRefSchema,
3104
+ valueCents: z16.number().int().nonnegative().safe().optional(),
3105
+ merchantId: z16.string().trim().min(1).max(256).optional(),
3106
+ merchantCategory: z16.string().trim().min(1).max(256).optional(),
3107
+ recurring: z16.boolean().optional(),
3108
+ taskId: TaskIdSchema.optional(),
3109
+ uses: z16.literal(1),
3110
+ materialParameters: MaterialParametersSchema,
3111
+ purpose: z16.string().trim().min(1).max(1024),
3112
+ initiatingContext: z16.enum(["user", "scheduled", "agent", "external-event"]),
3113
+ requestedAdapterId: AdapterIdSchema,
3114
+ requestedTtlMs: z16.number().int().positive().max(3e5)
3115
+ }).strict();
3116
+
3117
+ // src/client.ts
3118
+ function randomId(prefix) {
3119
+ return `${prefix}_${randomBytes(16).toString("hex")}`;
3120
+ }
3121
+ function materialParametersFor(invocation) {
3122
+ if (invocation.action === "tool.invoke") {
3123
+ const params2 = ToolInvokeParametersV1Schema.parse({
3124
+ schema: "odingard.guardian.tool.invoke.v1",
3125
+ connectionId: invocation.connectionId,
3126
+ toolName: invocation.toolName,
3127
+ arguments: invocation.arguments
3128
+ });
3129
+ return { ...params2 };
3130
+ }
3131
+ if (invocation.action === "mcp.install") {
3132
+ const params2 = McpInstallParametersV1Schema.parse({
3133
+ schema: "odingard.guardian.mcp.install.v1",
3134
+ connectionId: invocation.connectionId,
3135
+ tool: invocation.tool
3136
+ });
3137
+ return { ...params2 };
3138
+ }
3139
+ const params = McpEnableParametersV1Schema.parse({
3140
+ schema: "odingard.guardian.mcp.enable.v1",
3141
+ connectionId: invocation.connectionId,
3142
+ toolName: invocation.toolName
3143
+ });
3144
+ return { ...params };
3145
+ }
3146
+ var RESOURCE_TYPE = {
3147
+ "tool.invoke": "tool:server",
3148
+ "mcp.install": "mcp:server",
3149
+ "mcp.enable": "mcp:server"
3150
+ };
3151
+ function createGuardianClient(config) {
3152
+ const transport = config.fetch ?? fetch;
3153
+ const base = config.baseUrl.replace(/\/+$/, "");
3154
+ const call = async (path, body, method = "POST") => {
3155
+ const init = {
3156
+ method,
3157
+ headers: {
3158
+ authorization: `Bearer ${config.credential}`,
3159
+ ...body === void 0 ? {} : { "content-type": "application/json" }
3160
+ },
3161
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
3162
+ };
3163
+ const response = await transport(`${base}${path}`, init);
3164
+ const parsed = await response.json().catch(() => null);
3165
+ return { status: response.status, body: parsed };
3166
+ };
3167
+ const requestCapability = async (path, body) => {
3168
+ const response = await call(path, body);
3169
+ const data = response.body;
3170
+ if (!data || data.ok !== true) {
3171
+ return {
3172
+ ok: false,
3173
+ reasonCode: typeof data?.reasonCode === "string" ? data.reasonCode : `HTTP_${response.status}`
3174
+ };
3175
+ }
3176
+ return {
3177
+ ok: true,
3178
+ requestId: typeof data.requestId === "string" ? data.requestId : body.requestId ?? "",
3179
+ decision: data.decision === "ASK" || data.decision === "DENY" ? data.decision : "ALLOW",
3180
+ ...typeof data.capabilityToken === "string" ? { capabilityToken: data.capabilityToken } : typeof data.token === "string" ? { capabilityToken: data.token } : {},
3181
+ ...typeof data.reasonCode === "string" ? { reasonCode: data.reasonCode } : {}
3182
+ };
3183
+ };
3184
+ return {
3185
+ request(invocation) {
3186
+ const requestId = randomId("req");
3187
+ const proposal = AgentActionProposalSchema.parse({
3188
+ schemaVersion: 1,
3189
+ requestId,
3190
+ correlationId: randomId("cor"),
3191
+ action: invocation.action,
3192
+ resource: `${RESOURCE_TYPE[invocation.action]}:${invocation.connectionId}`,
3193
+ uses: 1,
3194
+ materialParameters: materialParametersFor(invocation),
3195
+ purpose: invocation.purpose ?? `Guardian ${invocation.action}`,
3196
+ initiatingContext: "agent",
3197
+ requestedAdapterId: invocation.adapterId ?? "adp_00000006",
3198
+ requestedTtlMs: 12e4
3199
+ });
3200
+ return requestCapability("/v1/agent/requests", proposal);
3201
+ },
3202
+ claim(requestId) {
3203
+ return requestCapability("/v1/agent/claims", {
3204
+ schemaVersion: 1,
3205
+ requestId
3206
+ });
3207
+ },
3208
+ async execute({ requestId, capabilityToken }) {
3209
+ const request = AgentExecutionRequestSchema.parse({
3210
+ schemaVersion: 1,
3211
+ requestId,
3212
+ capabilityToken
3213
+ });
3214
+ const response = await call("/v1/agent/execute", request);
3215
+ const data = response.body;
3216
+ const receipt = AgentExecutionReceiptSchema.safeParse(data?.receipt);
3217
+ if (!data || data.ok !== true || !receipt.success) {
3218
+ return {
3219
+ ok: false,
3220
+ reasonCode: typeof data?.reasonCode === "string" ? data.reasonCode : `HTTP_${response.status}`
3221
+ };
3222
+ }
3223
+ return { ok: true, receipt: receipt.data };
3224
+ },
3225
+ async receipts(requestId) {
3226
+ const response = await call(
3227
+ `/v1/agent/receipts/${encodeURIComponent(requestId)}`,
3228
+ void 0,
3229
+ "GET"
3230
+ );
3231
+ const data = response.body;
3232
+ if (!data || data.ok !== true || !Array.isArray(data.receipts)) {
3233
+ return {
3234
+ ok: false,
3235
+ reasonCode: typeof data?.code === "string" ? data.code : `HTTP_${response.status}`
3236
+ };
3237
+ }
3238
+ return {
3239
+ ok: true,
3240
+ requestId: data.requestId,
3241
+ receipts: data.receipts
3242
+ };
3243
+ }
3244
+ };
3245
+ }
3246
+ export {
3247
+ createGuardianClient
3248
+ };