@intentgate-app/intentgate 0.3.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.cjs ADDED
@@ -0,0 +1,661 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AttenuationError: () => AttenuationError,
24
+ BudgetError: () => BudgetError,
25
+ CapabilityError: () => CapabilityError,
26
+ CaveatType: () => CaveatType,
27
+ Gateway: () => Gateway,
28
+ GatewayError: () => GatewayError,
29
+ IntentError: () => IntentError,
30
+ IntentGateError: () => IntentGateError,
31
+ MemoryProvenanceError: () => MemoryProvenanceError,
32
+ MemoryStore: () => MemoryStore,
33
+ PolicyError: () => PolicyError,
34
+ ProtocolError: () => ProtocolError,
35
+ ProvenanceError: () => ProvenanceError,
36
+ SESSION_KEY_SIZE: () => SESSION_KEY_SIZE,
37
+ ZERO_HASH: () => ZERO_HASH,
38
+ attenuate: () => attenuate,
39
+ canonical: () => canonical,
40
+ decodeToken: () => decodeToken,
41
+ deriveSessionKey: () => deriveSessionKey,
42
+ forCode: () => forCode,
43
+ sign: () => sign,
44
+ verify: () => verify,
45
+ verifyChain: () => verifyChain
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // src/errors.ts
50
+ var IntentGateError = class extends Error {
51
+ /** JSON-RPC error code from the gateway, or 0 if client-side. */
52
+ code;
53
+ /** Optional structured payload from the gateway's `error.data`. */
54
+ data;
55
+ constructor(message, opts = {}) {
56
+ super(message, { cause: opts.cause });
57
+ this.name = "IntentGateError";
58
+ this.code = opts.code ?? 0;
59
+ this.data = opts.data;
60
+ Object.setPrototypeOf(this, new.target.prototype);
61
+ }
62
+ /**
63
+ * Human-friendly string. When `data` is a string, it usually carries
64
+ * the operator-facing reason (e.g. the Rego rule's explanation), so
65
+ * we surface it on `toString`.
66
+ */
67
+ toString() {
68
+ if (typeof this.data === "string" && this.data.length > 0) {
69
+ return `${this.message}: ${this.data}`;
70
+ }
71
+ return this.message;
72
+ }
73
+ };
74
+ var GatewayError = class extends IntentGateError {
75
+ constructor(message, opts) {
76
+ super(message, opts);
77
+ this.name = "GatewayError";
78
+ }
79
+ };
80
+ var ProtocolError = class extends IntentGateError {
81
+ constructor(message, opts) {
82
+ super(message, opts);
83
+ this.name = "ProtocolError";
84
+ }
85
+ };
86
+ var CapabilityError = class extends IntentGateError {
87
+ constructor(message, opts) {
88
+ super(message, opts);
89
+ this.name = "CapabilityError";
90
+ }
91
+ };
92
+ var IntentError = class extends IntentGateError {
93
+ constructor(message, opts) {
94
+ super(message, opts);
95
+ this.name = "IntentError";
96
+ }
97
+ };
98
+ var PolicyError = class extends IntentGateError {
99
+ constructor(message, opts) {
100
+ super(message, opts);
101
+ this.name = "PolicyError";
102
+ }
103
+ };
104
+ var BudgetError = class extends IntentGateError {
105
+ constructor(message, opts) {
106
+ super(message, opts);
107
+ this.name = "BudgetError";
108
+ }
109
+ };
110
+ var ProvenanceError = class extends IntentGateError {
111
+ constructor(message, opts) {
112
+ super(message, opts);
113
+ this.name = "ProvenanceError";
114
+ }
115
+ };
116
+ var CODE_TO_CLASS = {
117
+ [-32010]: CapabilityError,
118
+ [-32011]: IntentError,
119
+ [-32012]: PolicyError,
120
+ [-32013]: BudgetError,
121
+ [-32014]: ProvenanceError
122
+ };
123
+ function forCode(code) {
124
+ return CODE_TO_CLASS[code] ?? ProtocolError;
125
+ }
126
+
127
+ // src/client.ts
128
+ var TOOLS_CALL_METHOD = "tools/call";
129
+ var DEFAULT_TIMEOUT_MS = 1e4;
130
+ var Gateway = class {
131
+ url;
132
+ token;
133
+ timeoutMs;
134
+ fetchImpl;
135
+ nextId = 1;
136
+ constructor(url, opts = {}) {
137
+ if (!url) {
138
+ throw new Error("Gateway: url is required");
139
+ }
140
+ let cleaned = url;
141
+ while (cleaned.endsWith("/")) cleaned = cleaned.slice(0, -1);
142
+ this.url = cleaned;
143
+ this.token = opts.token;
144
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
145
+ this.fetchImpl = opts.fetch ?? globalThis.fetch;
146
+ if (!this.fetchImpl) {
147
+ throw new Error(
148
+ "Gateway: no fetch available; pass `fetch` in options or run on Node 18+"
149
+ );
150
+ }
151
+ }
152
+ /**
153
+ * Invoke a tool through the gateway.
154
+ *
155
+ * Resolves with a {@link ToolCallResult} for an allowed call. Throws
156
+ * one of the typed errors (CapabilityError / IntentError /
157
+ * PolicyError / BudgetError / ProtocolError / GatewayError) when
158
+ * the gateway denies, the request fails to reach the gateway, or
159
+ * the response isn't well-formed JSON-RPC.
160
+ */
161
+ async toolCall(tool, opts = {}) {
162
+ if (!tool) {
163
+ throw new Error("toolCall: tool is required");
164
+ }
165
+ const id = opts.requestId ?? this.nextId++;
166
+ const body = JSON.stringify({
167
+ jsonrpc: "2.0",
168
+ id,
169
+ method: TOOLS_CALL_METHOD,
170
+ params: {
171
+ name: tool,
172
+ arguments: opts.arguments ?? {}
173
+ }
174
+ });
175
+ const headers = {
176
+ "Content-Type": "application/json"
177
+ };
178
+ if (this.token) {
179
+ headers["Authorization"] = `Bearer ${this.token}`;
180
+ }
181
+ if (opts.intentPrompt) {
182
+ headers["X-Intent-Prompt"] = opts.intentPrompt;
183
+ }
184
+ if (opts.memoryProvenance && opts.memoryProvenance.length > 0) {
185
+ if (!opts.memoryStore) {
186
+ throw new Error(
187
+ "toolCall: memoryProvenance is non-empty but memoryStore is undefined; supply a MemoryStore so the SDK can look up the envelopes"
188
+ );
189
+ }
190
+ const wireEntries = opts.memoryStore.provenanceFor(opts.memoryProvenance);
191
+ headers["X-Intent-Memory-Provenance"] = Buffer.from(JSON.stringify(wireEntries)).toString(
192
+ "base64url"
193
+ );
194
+ }
195
+ const controller = new AbortController();
196
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
197
+ let resp;
198
+ try {
199
+ resp = await this.fetchImpl(`${this.url}/v1/mcp`, {
200
+ method: "POST",
201
+ body,
202
+ headers,
203
+ signal: controller.signal
204
+ });
205
+ } catch (cause) {
206
+ const isAbort = cause instanceof Error && cause.name === "AbortError";
207
+ const msg = isAbort ? `gateway timed out after ${this.timeoutMs}ms` : `transport error reaching gateway: ${stringifyCause(cause)}`;
208
+ throw new GatewayError(msg, { cause });
209
+ } finally {
210
+ clearTimeout(timer);
211
+ }
212
+ if (!resp.ok) {
213
+ const text = await safeText(resp);
214
+ throw new GatewayError(`gateway returned HTTP ${resp.status}`, {
215
+ data: text || resp.statusText
216
+ });
217
+ }
218
+ let payload;
219
+ try {
220
+ payload = await resp.json();
221
+ } catch (cause) {
222
+ throw new GatewayError("non-JSON response from gateway", { cause });
223
+ }
224
+ return parseResponse(payload);
225
+ }
226
+ };
227
+ function stringifyCause(cause) {
228
+ if (cause instanceof Error) return cause.message;
229
+ return String(cause);
230
+ }
231
+ async function safeText(resp) {
232
+ try {
233
+ const t = await resp.text();
234
+ return t.slice(0, 500);
235
+ } catch {
236
+ return "";
237
+ }
238
+ }
239
+ function parseResponse(payload) {
240
+ if (!isObject(payload)) {
241
+ throw new ProtocolError("response is not a JSON object");
242
+ }
243
+ const err = payload["error"];
244
+ if (err != null) {
245
+ if (!isObject(err)) {
246
+ throw new ProtocolError("error field is not an object");
247
+ }
248
+ const code = typeof err["code"] === "number" ? err["code"] : 0;
249
+ const message = typeof err["message"] === "string" ? err["message"] : "gateway error";
250
+ const data = err["data"];
251
+ const Cls = forCode(code);
252
+ throw new Cls(message, { code, data });
253
+ }
254
+ const result = payload["result"];
255
+ if (!isObject(result)) {
256
+ throw new ProtocolError("response missing 'result' object", { data: payload });
257
+ }
258
+ const rawContent = Array.isArray(result["content"]) ? result["content"] : [];
259
+ const content = [];
260
+ for (const b of rawContent) {
261
+ if (!isObject(b)) continue;
262
+ content.push({
263
+ type: typeof b["type"] === "string" ? b["type"] : "",
264
+ text: typeof b["text"] === "string" ? b["text"] : void 0
265
+ });
266
+ }
267
+ let intentgate = null;
268
+ const ig = result["_intentgate"];
269
+ if (isObject(ig)) {
270
+ intentgate = {
271
+ decision: typeof ig["decision"] === "string" ? ig["decision"] : "",
272
+ reason: typeof ig["reason"] === "string" ? ig["reason"] : "",
273
+ check: typeof ig["check"] === "string" ? ig["check"] : "",
274
+ latencyMs: typeof ig["latency_ms"] === "number" ? ig["latency_ms"] : 0
275
+ };
276
+ }
277
+ return {
278
+ content,
279
+ isError: result["isError"] === true,
280
+ intentgate
281
+ };
282
+ }
283
+ function isObject(v) {
284
+ return v !== null && typeof v === "object" && !Array.isArray(v);
285
+ }
286
+
287
+ // src/capability.ts
288
+ var import_node_crypto = require("crypto");
289
+ var CaveatType = {
290
+ EXPIRY: "exp",
291
+ TOOL_ALLOW: "tool_allow",
292
+ TOOL_DENY: "tool_deny",
293
+ AGENT_LOCK: "agent_lock",
294
+ MAX_CALLS: "max_calls"
295
+ };
296
+ var AttenuationError = class extends Error {
297
+ constructor(message, opts) {
298
+ super(message, { cause: opts?.cause });
299
+ this.name = "AttenuationError";
300
+ Object.setPrototypeOf(this, new.target.prototype);
301
+ }
302
+ };
303
+ function b64urlDecode(s) {
304
+ const standard = s.replace(/-/g, "+").replace(/_/g, "/");
305
+ const pad = standard.length % 4 === 0 ? "" : "=".repeat(4 - standard.length % 4);
306
+ try {
307
+ return new Uint8Array(Buffer.from(standard + pad, "base64"));
308
+ } catch (cause) {
309
+ throw new AttenuationError(`invalid base64url: ${stringifyCause2(cause)}`, { cause });
310
+ }
311
+ }
312
+ function b64urlEncode(b) {
313
+ return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
314
+ }
315
+ function canonicalCaveatBytes(c) {
316
+ const obj = {};
317
+ obj["t"] = c.type;
318
+ if (c.tools && c.tools.length > 0) {
319
+ obj["tools"] = [...c.tools];
320
+ }
321
+ if (c.agent) {
322
+ obj["agent"] = c.agent;
323
+ }
324
+ if (c.expiry) {
325
+ obj["exp"] = Math.trunc(c.expiry);
326
+ }
327
+ if (c.maxCalls) {
328
+ obj["max_calls"] = Math.trunc(c.maxCalls);
329
+ }
330
+ const json = JSON.stringify(obj);
331
+ return new TextEncoder().encode(json);
332
+ }
333
+ function decodeToken(token) {
334
+ const raw = b64urlDecode(token);
335
+ let parsed;
336
+ try {
337
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(raw));
338
+ } catch (cause) {
339
+ throw new AttenuationError(`token JSON is malformed: ${stringifyCause2(cause)}`, {
340
+ cause
341
+ });
342
+ }
343
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
344
+ throw new AttenuationError("token JSON is not an object");
345
+ }
346
+ return parsed;
347
+ }
348
+ function attenuate(token, opts = {}) {
349
+ const parsed = decodeToken(token);
350
+ if (typeof parsed["sig"] !== "string") {
351
+ throw new AttenuationError("token is missing 'sig' field");
352
+ }
353
+ if (!Array.isArray(parsed["cav"])) {
354
+ throw new AttenuationError("token is missing 'cav' field");
355
+ }
356
+ if (typeof parsed["root_jti"] !== "string" || parsed["root_jti"] === "") {
357
+ throw new AttenuationError(
358
+ "token has no root_jti (was it minted by gateway < v0.7?)"
359
+ );
360
+ }
361
+ if (typeof parsed["tenant"] !== "string" || parsed["tenant"] === "") {
362
+ throw new AttenuationError(
363
+ "token has no tenant (was it minted by gateway < v0.9?)"
364
+ );
365
+ }
366
+ const cavs = [...parsed["cav"]];
367
+ let sig = b64urlDecode(parsed["sig"]);
368
+ const newCaveats = [];
369
+ if (opts.addTools && opts.addTools.length > 0) {
370
+ newCaveats.push({ type: CaveatType.TOOL_ALLOW, tools: [...opts.addTools] });
371
+ }
372
+ if (opts.denyTools && opts.denyTools.length > 0) {
373
+ newCaveats.push({ type: CaveatType.TOOL_DENY, tools: [...opts.denyTools] });
374
+ }
375
+ if (opts.maxCalls !== void 0) {
376
+ if (opts.maxCalls < 0) {
377
+ throw new AttenuationError("maxCalls must be >= 0");
378
+ }
379
+ newCaveats.push({ type: CaveatType.MAX_CALLS, maxCalls: Math.trunc(opts.maxCalls) });
380
+ }
381
+ if (opts.expiresAt !== void 0 || opts.expiresInSeconds !== void 0) {
382
+ const exp = opts.expiresAt ?? Math.floor(Date.now() / 1e3) + (opts.expiresInSeconds ?? 0);
383
+ newCaveats.push({ type: CaveatType.EXPIRY, expiry: Math.trunc(exp) });
384
+ }
385
+ if (opts.extra && opts.extra.length > 0) {
386
+ newCaveats.push(...opts.extra);
387
+ }
388
+ if (newCaveats.length === 0) {
389
+ throw new AttenuationError(
390
+ "attenuate() requires at least one narrowing argument (addTools, denyTools, maxCalls, expiresInSeconds, expiresAt, or extra)"
391
+ );
392
+ }
393
+ for (const c of newCaveats) {
394
+ const cb = canonicalCaveatBytes(c);
395
+ sig = new Uint8Array((0, import_node_crypto.createHmac)("sha256", sig).update(cb).digest());
396
+ cavs.push(JSON.parse(new TextDecoder().decode(cb)));
397
+ }
398
+ const child = { ...parsed };
399
+ child["cav"] = cavs;
400
+ child["sig"] = b64urlEncode(sig);
401
+ return b64urlEncode(new TextEncoder().encode(JSON.stringify(child)));
402
+ }
403
+ function stringifyCause2(cause) {
404
+ if (cause instanceof Error) return cause.message;
405
+ return String(cause);
406
+ }
407
+
408
+ // src/memory.ts
409
+ var import_node_crypto2 = require("crypto");
410
+ var DERIVATION_INFO = Buffer.from("intentgate-memory-v1");
411
+ var SESSION_KEY_SIZE = 32;
412
+ var HASH_SIZE = 32;
413
+ var ZERO_HASH = Buffer.alloc(HASH_SIZE);
414
+ var MemoryProvenanceError = class extends Error {
415
+ constructor(message) {
416
+ super(message);
417
+ this.name = "MemoryProvenanceError";
418
+ Object.setPrototypeOf(this, new.target.prototype);
419
+ }
420
+ };
421
+ function deriveSessionKey(masterKey, sessionId) {
422
+ if (!masterKey || masterKey.length === 0) {
423
+ throw new Error("deriveSessionKey: masterKey is empty");
424
+ }
425
+ if (!sessionId) {
426
+ throw new Error("deriveSessionKey: sessionId is empty");
427
+ }
428
+ const out = (0, import_node_crypto2.hkdfSync)(
429
+ "sha256",
430
+ masterKey,
431
+ Buffer.from(sessionId, "utf8"),
432
+ DERIVATION_INFO,
433
+ SESSION_KEY_SIZE
434
+ );
435
+ return Buffer.from(out);
436
+ }
437
+ function canonical(env) {
438
+ const sid = Buffer.from(env.sessionId, "utf8");
439
+ const eid = Buffer.from(env.id, "utf8");
440
+ const total = 4 + sid.length + 4 + eid.length + 8 + 4 + env.prevHash.length + 4 + env.data.length;
441
+ const out = Buffer.alloc(total);
442
+ let off = 0;
443
+ out.writeUInt32BE(sid.length, off);
444
+ off += 4;
445
+ sid.copy(out, off);
446
+ off += sid.length;
447
+ out.writeUInt32BE(eid.length, off);
448
+ off += 4;
449
+ eid.copy(out, off);
450
+ off += eid.length;
451
+ out.writeBigUInt64BE(BigInt(env.timestamp) & 0xffffffffffffffffn, off);
452
+ off += 8;
453
+ out.writeUInt32BE(env.prevHash.length, off);
454
+ off += 4;
455
+ env.prevHash.copy(out, off);
456
+ off += env.prevHash.length;
457
+ out.writeUInt32BE(env.data.length, off);
458
+ off += 4;
459
+ env.data.copy(out, off);
460
+ return out;
461
+ }
462
+ function sign(sessionKey, env) {
463
+ if (!sessionKey || sessionKey.length === 0) {
464
+ throw new Error("sign: sessionKey is empty");
465
+ }
466
+ const mac = (0, import_node_crypto2.createHmac)("sha256", sessionKey);
467
+ mac.update(canonical(env));
468
+ return { ...env, hmac: mac.digest() };
469
+ }
470
+ function verify(sessionKey, env) {
471
+ if (!sessionKey || sessionKey.length === 0) {
472
+ throw new Error("verify: sessionKey is empty");
473
+ }
474
+ if (env.hmac.length !== HASH_SIZE) {
475
+ throw new MemoryProvenanceError(
476
+ `hmac field is ${env.hmac.length} bytes; expected ${HASH_SIZE}`
477
+ );
478
+ }
479
+ const expected = (0, import_node_crypto2.createHmac)("sha256", sessionKey).update(canonical(env)).digest();
480
+ if (!(0, import_node_crypto2.timingSafeEqual)(expected, env.hmac)) {
481
+ throw new MemoryProvenanceError("hmac mismatch");
482
+ }
483
+ }
484
+ function verifyChain(sessionKey, chain) {
485
+ if (chain.length === 0) {
486
+ return;
487
+ }
488
+ for (let i = 0; i < chain.length; i++) {
489
+ const env = chain[i];
490
+ if (!env) continue;
491
+ try {
492
+ verify(sessionKey, env);
493
+ } catch (e) {
494
+ if (e instanceof MemoryProvenanceError) {
495
+ throw new MemoryProvenanceError(`entry ${i}: ${e.message}`);
496
+ }
497
+ throw e;
498
+ }
499
+ let expectedPrev;
500
+ if (i === 0) {
501
+ expectedPrev = ZERO_HASH;
502
+ } else {
503
+ const prev = chain[i - 1];
504
+ if (!prev) continue;
505
+ expectedPrev = (0, import_node_crypto2.createHash)("sha256").update(canonical(prev)).digest();
506
+ }
507
+ if (!(0, import_node_crypto2.timingSafeEqual)(expectedPrev, env.prevHash)) {
508
+ throw new MemoryProvenanceError(
509
+ `entry ${i}: prev_hash does not match previous entry's canonical hash`
510
+ );
511
+ }
512
+ }
513
+ }
514
+ var MemoryStore = class {
515
+ sessionId;
516
+ key;
517
+ fallback = /* @__PURE__ */ new Map();
518
+ writeHook;
519
+ readHook;
520
+ chainHead = ZERO_HASH;
521
+ /**
522
+ * @param sessionId The `jti` of the capability token this store is bound to.
523
+ * Used as the HKDF salt to derive the signing key.
524
+ * @param memorySigningKey The 32-byte signing key returned by
525
+ * `POST /v1/admin/mint` when `with_memory_signing_key: true`.
526
+ * @param options Optional `writeHook` / `readHook` callables; when
527
+ * absent the wrapper uses an in-memory Map fallback.
528
+ */
529
+ constructor(sessionId, memorySigningKey, options = {}) {
530
+ if (!sessionId) {
531
+ throw new Error("MemoryStore: sessionId is required");
532
+ }
533
+ if (memorySigningKey.length !== SESSION_KEY_SIZE) {
534
+ throw new Error(
535
+ `MemoryStore: memorySigningKey must be ${SESSION_KEY_SIZE} bytes, got ${memorySigningKey.length}`
536
+ );
537
+ }
538
+ this.sessionId = sessionId;
539
+ this.key = Buffer.from(memorySigningKey);
540
+ this.writeHook = options.writeHook;
541
+ this.readHook = options.readHook;
542
+ }
543
+ /**
544
+ * Sign `data` into a new envelope and store it. Returns the entry
545
+ * ID, which the caller passes to `Gateway.toolCall` via the
546
+ * `memoryProvenance` list.
547
+ *
548
+ * `data` may be a Buffer, a string (utf-8 encoded), or any JSON-
549
+ * serializable value (encoded with sorted keys + no whitespace so
550
+ * equivalent inputs produce identical envelope bytes).
551
+ */
552
+ write(data) {
553
+ let payload;
554
+ if (Buffer.isBuffer(data)) {
555
+ payload = data;
556
+ } else if (typeof data === "string") {
557
+ payload = Buffer.from(data, "utf8");
558
+ } else {
559
+ payload = Buffer.from(stableStringify(data), "utf8");
560
+ }
561
+ const entryId = (0, import_node_crypto2.randomUUID)().replaceAll("-", "");
562
+ const env = sign(this.key, {
563
+ id: entryId,
564
+ sessionId: this.sessionId,
565
+ timestamp: Date.now(),
566
+ data: payload,
567
+ prevHash: this.chainHead
568
+ });
569
+ if (this.writeHook !== void 0) {
570
+ this.writeHook(entryId, env);
571
+ } else {
572
+ this.fallback.set(entryId, env);
573
+ }
574
+ this.chainHead = (0, import_node_crypto2.createHash)("sha256").update(canonical(env)).digest();
575
+ return entryId;
576
+ }
577
+ /**
578
+ * Fetch and verify the envelope identified by `entryId`.
579
+ *
580
+ * @throws if the entry is missing (`Error`) or if the HMAC fails
581
+ * ({@link MemoryProvenanceError}, indicating the entry was
582
+ * tampered with after writing).
583
+ */
584
+ read(entryId) {
585
+ let env;
586
+ if (this.readHook !== void 0) {
587
+ env = this.readHook(entryId);
588
+ } else {
589
+ env = this.fallback.get(entryId);
590
+ if (env === void 0) {
591
+ throw new Error(`MemoryStore: entry ${entryId} not found`);
592
+ }
593
+ }
594
+ verify(this.key, env);
595
+ return env;
596
+ }
597
+ /**
598
+ * Build the wire-format provenance entries for a tool call. Each
599
+ * entry is verified before inclusion — if any envelope was tampered
600
+ * with at the storage layer, {@link MemoryProvenanceError} is
601
+ * raised here rather than at the gateway.
602
+ *
603
+ * The returned objects use base64url (no padding) encoding for byte
604
+ * fields — same shape the Go gateway parses.
605
+ */
606
+ provenanceFor(entryIds) {
607
+ return entryIds.map((eid) => {
608
+ const env = this.read(eid);
609
+ return {
610
+ id: env.id,
611
+ session_id: env.sessionId,
612
+ ts: env.timestamp,
613
+ data: env.data.toString("base64url"),
614
+ prev_hash: env.prevHash.toString("base64url"),
615
+ hmac: env.hmac.toString("base64url")
616
+ };
617
+ });
618
+ }
619
+ /** Number of entries in the fallback in-memory store. */
620
+ get size() {
621
+ return this.fallback.size;
622
+ }
623
+ };
624
+ function stableStringify(value) {
625
+ if (value === null || typeof value !== "object") {
626
+ return JSON.stringify(value);
627
+ }
628
+ if (Array.isArray(value)) {
629
+ return "[" + value.map(stableStringify).join(",") + "]";
630
+ }
631
+ const obj = value;
632
+ const keys = Object.keys(obj).sort();
633
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
634
+ }
635
+ // Annotate the CommonJS export names for ESM import in node:
636
+ 0 && (module.exports = {
637
+ AttenuationError,
638
+ BudgetError,
639
+ CapabilityError,
640
+ CaveatType,
641
+ Gateway,
642
+ GatewayError,
643
+ IntentError,
644
+ IntentGateError,
645
+ MemoryProvenanceError,
646
+ MemoryStore,
647
+ PolicyError,
648
+ ProtocolError,
649
+ ProvenanceError,
650
+ SESSION_KEY_SIZE,
651
+ ZERO_HASH,
652
+ attenuate,
653
+ canonical,
654
+ decodeToken,
655
+ deriveSessionKey,
656
+ forCode,
657
+ sign,
658
+ verify,
659
+ verifyChain
660
+ });
661
+ //# sourceMappingURL=index.cjs.map