@suveren/gateway 0.29.0 → 0.29.1

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.
@@ -2190,21 +2190,168 @@ function nowSec() {
2190
2190
  return Math.floor(Date.now() / 1e3);
2191
2191
  }
2192
2192
 
2193
- // src/lib/proposal-submission-store.ts
2194
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync9, renameSync as renameSync2 } from "fs";
2193
+ // src/lib/execution-result-store.ts
2194
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync9, unlinkSync as unlinkSync5 } from "fs";
2195
2195
  import { homedir as homedir6 } from "os";
2196
2196
  import { dirname as dirname7, join as join9 } from "path";
2197
+ import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes5 } from "crypto";
2197
2198
  var DEFAULT_DIR6 = process.env.SUVEREN_DATA_DIR ?? join9(homedir6(), ".suveren");
2198
- var FILE_NAME2 = "proposal-submissions.json";
2199
2199
  var MAX_AGE_SECONDS3 = 90 * 24 * 60 * 60;
2200
+ var MAX_BYTES = 16 * 1024;
2201
+ function capResult(text, structuredContent) {
2202
+ const structJson = structuredContent ? JSON.stringify(structuredContent) : void 0;
2203
+ const textBytes = Buffer.byteLength(text, "utf8");
2204
+ const structBytes = structJson ? Buffer.byteLength(structJson, "utf8") : 0;
2205
+ if (textBytes + structBytes <= MAX_BYTES) {
2206
+ return { text, ...structuredContent ? { structuredContent } : {}, truncated: false };
2207
+ }
2208
+ if (textBytes <= MAX_BYTES) {
2209
+ return { text, truncated: true };
2210
+ }
2211
+ const cappedText = Buffer.from(text, "utf8").subarray(0, MAX_BYTES).toString("utf8");
2212
+ return { text: cappedText, truncated: true };
2213
+ }
2214
+ var ExecutionResultStore = class {
2215
+ entries = [];
2216
+ baseDir;
2217
+ vaultKey = null;
2218
+ constructor(filePath) {
2219
+ this.baseDir = filePath && filePath.endsWith(".json") ? dirname7(filePath) : filePath ?? DEFAULT_DIR6;
2220
+ this.loadPlaintext();
2221
+ }
2222
+ /** Mirrors OutcomeOutbox.setVaultKey — migrates plaintext -> encrypted once, in place. */
2223
+ setVaultKey(key) {
2224
+ this.vaultKey = key;
2225
+ if (existsSync9(this.encryptedFilePath)) {
2226
+ this.loadEncrypted();
2227
+ } else if (this.entries.length > 0) {
2228
+ this.persistEncrypted();
2229
+ if (existsSync9(this.plaintextFilePath)) {
2230
+ try {
2231
+ unlinkSync5(this.plaintextFilePath);
2232
+ } catch {
2233
+ }
2234
+ }
2235
+ }
2236
+ }
2237
+ /**
2238
+ * True when encrypted evidence exists on disk that this process cannot
2239
+ * read yet (no vault key). Callers should not report "nothing retained" in
2240
+ * this state — that would claim a hollow absence over unreadable content.
2241
+ */
2242
+ isLocked() {
2243
+ return this.vaultKey === null && existsSync9(this.encryptedFilePath);
2244
+ }
2245
+ /** Record the outcome of one execution. Replaces any prior entry for the same proposal. */
2246
+ record(input, now = nowSec2()) {
2247
+ const capped = capResult(input.text, input.structuredContent);
2248
+ this.entries = this.entries.filter((e) => e.proposalId !== input.proposalId);
2249
+ this.entries.push({
2250
+ proposalId: input.proposalId,
2251
+ ticketId: input.ticketId,
2252
+ ok: input.ok,
2253
+ text: capped.text,
2254
+ ...capped.structuredContent ? { structuredContent: capped.structuredContent } : {},
2255
+ ...capped.truncated ? { truncated: true } : {},
2256
+ recordedAt: now
2257
+ });
2258
+ this.prune(now);
2259
+ this.persist();
2260
+ }
2261
+ /** The retained outcome for a proposal, or undefined if never recorded (or pruned). */
2262
+ get(proposalId) {
2263
+ return this.entries.find((e) => e.proposalId === proposalId);
2264
+ }
2265
+ /** Diagnostic / tests. */
2266
+ get size() {
2267
+ return this.entries.length;
2268
+ }
2269
+ // ─── Retention ────────────────────────────────────────────────────────────
2270
+ prune(now) {
2271
+ const cutoff = now - MAX_AGE_SECONDS3;
2272
+ this.entries = this.entries.filter((e) => e.recordedAt >= cutoff);
2273
+ }
2274
+ // ─── Encryption (mirrors OutcomeOutbox / DenialLog) ────────────────────────
2275
+ encrypt(plaintext) {
2276
+ if (!this.vaultKey) throw new Error("No vault key");
2277
+ const iv = randomBytes5(12);
2278
+ const cipher = createCipheriv5("aes-256-gcm", this.vaultKey, iv);
2279
+ const enc = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
2280
+ return { iv: iv.toString("hex"), ciphertext: enc.toString("hex"), tag: cipher.getAuthTag().toString("hex") };
2281
+ }
2282
+ decrypt(blob) {
2283
+ if (!this.vaultKey) throw new Error("No vault key");
2284
+ const decipher = createDecipheriv5("aes-256-gcm", this.vaultKey, Buffer.from(blob.iv, "hex"));
2285
+ decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
2286
+ return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "hex")), decipher.final()]).toString("utf8");
2287
+ }
2288
+ get plaintextFilePath() {
2289
+ return join9(this.baseDir, "execution-results.json");
2290
+ }
2291
+ get encryptedFilePath() {
2292
+ return join9(this.baseDir, "execution-results.enc.json");
2293
+ }
2294
+ loadPlaintext() {
2295
+ if (!existsSync9(this.plaintextFilePath)) {
2296
+ mkdirSync7(this.baseDir, { recursive: true });
2297
+ return;
2298
+ }
2299
+ try {
2300
+ const data = JSON.parse(readFileSync10(this.plaintextFilePath, "utf-8"));
2301
+ this.entries = data.entries ?? [];
2302
+ this.prune(nowSec2());
2303
+ } catch {
2304
+ console.error(`[ExecutionResultStore] Could not parse ${this.plaintextFilePath}, starting fresh`);
2305
+ this.entries = [];
2306
+ }
2307
+ }
2308
+ loadEncrypted() {
2309
+ if (!existsSync9(this.encryptedFilePath)) return;
2310
+ try {
2311
+ const data = JSON.parse(readFileSync10(this.encryptedFilePath, "utf-8"));
2312
+ const storeFile = JSON.parse(this.decrypt(data.blob));
2313
+ this.entries = storeFile.entries ?? [];
2314
+ this.prune(nowSec2());
2315
+ } catch (err) {
2316
+ console.error(`[ExecutionResultStore] Could not decrypt ${this.encryptedFilePath}:`, err);
2317
+ this.entries = [];
2318
+ }
2319
+ }
2320
+ persist() {
2321
+ if (this.vaultKey) this.persistEncrypted();
2322
+ else this.persistPlaintext();
2323
+ }
2324
+ persistPlaintext() {
2325
+ const data = { version: 1, entries: this.entries };
2326
+ mkdirSync7(this.baseDir, { recursive: true, mode: 448 });
2327
+ writeFileSync7(this.plaintextFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
2328
+ }
2329
+ persistEncrypted() {
2330
+ const storeFile = { version: 1, entries: this.entries };
2331
+ const data = { version: 1, blob: this.encrypt(JSON.stringify(storeFile)) };
2332
+ mkdirSync7(this.baseDir, { recursive: true, mode: 448 });
2333
+ writeFileSync7(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
2334
+ }
2335
+ };
2200
2336
  function nowSec2() {
2201
2337
  return Math.floor(Date.now() / 1e3);
2202
2338
  }
2339
+
2340
+ // src/lib/proposal-submission-store.ts
2341
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync10, renameSync as renameSync2 } from "fs";
2342
+ import { homedir as homedir7 } from "os";
2343
+ import { dirname as dirname8, join as join10 } from "path";
2344
+ var DEFAULT_DIR7 = process.env.SUVEREN_DATA_DIR ?? join10(homedir7(), ".suveren");
2345
+ var FILE_NAME2 = "proposal-submissions.json";
2346
+ var MAX_AGE_SECONDS4 = 90 * 24 * 60 * 60;
2347
+ function nowSec3() {
2348
+ return Math.floor(Date.now() / 1e3);
2349
+ }
2203
2350
  var ProposalSubmissionStore = class {
2204
2351
  filePath;
2205
2352
  constructor(filePath) {
2206
- const baseDir = filePath && filePath.endsWith(".json") ? dirname7(filePath) : filePath ?? DEFAULT_DIR6;
2207
- this.filePath = join9(baseDir, FILE_NAME2);
2353
+ const baseDir = filePath && filePath.endsWith(".json") ? dirname8(filePath) : filePath ?? DEFAULT_DIR7;
2354
+ this.filePath = join10(baseDir, FILE_NAME2);
2208
2355
  }
2209
2356
  /** Record what was submitted, right after a successful submitProposal(). */
2210
2357
  record(entry) {
@@ -2217,7 +2364,7 @@ var ProposalSubmissionStore = class {
2217
2364
  executionContextHash: hashToolArgs(entry.executionContext),
2218
2365
  authorizationId: entry.authorizationId,
2219
2366
  profileId: entry.profileId,
2220
- submittedAt: nowSec2(),
2367
+ submittedAt: nowSec3(),
2221
2368
  ...entry.previewHash ? { previewHash: entry.previewHash } : {}
2222
2369
  });
2223
2370
  this.persist(file);
@@ -2228,10 +2375,10 @@ var ProposalSubmissionStore = class {
2228
2375
  return this.load().entries.find((e) => e.proposalId === proposalId);
2229
2376
  }
2230
2377
  load() {
2231
- if (!existsSync9(this.filePath)) return { version: 1, entries: [] };
2378
+ if (!existsSync10(this.filePath)) return { version: 1, entries: [] };
2232
2379
  try {
2233
- const parsed = JSON.parse(readFileSync10(this.filePath, "utf-8"));
2234
- const cutoff = nowSec2() - MAX_AGE_SECONDS3;
2380
+ const parsed = JSON.parse(readFileSync11(this.filePath, "utf-8"));
2381
+ const cutoff = nowSec3() - MAX_AGE_SECONDS4;
2235
2382
  parsed.entries = (parsed.entries ?? []).filter((e) => e.submittedAt >= cutoff);
2236
2383
  return parsed;
2237
2384
  } catch {
@@ -2239,26 +2386,26 @@ var ProposalSubmissionStore = class {
2239
2386
  }
2240
2387
  }
2241
2388
  persist(file) {
2242
- mkdirSync7(dirname7(this.filePath), { recursive: true, mode: 448 });
2389
+ mkdirSync8(dirname8(this.filePath), { recursive: true, mode: 448 });
2243
2390
  const tmpPath = `${this.filePath}.tmp`;
2244
- writeFileSync7(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
2391
+ writeFileSync8(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
2245
2392
  renameSync2(tmpPath, this.filePath);
2246
2393
  }
2247
2394
  };
2248
2395
 
2249
2396
  // src/lib/preview-log.ts
2250
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync10, renameSync as renameSync3 } from "fs";
2251
- import { homedir as homedir7 } from "os";
2252
- import { dirname as dirname8, join as join10 } from "path";
2253
- var DEFAULT_DIR7 = process.env.SUVEREN_DATA_DIR ?? join10(homedir7(), ".suveren");
2397
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync11, renameSync as renameSync3 } from "fs";
2398
+ import { homedir as homedir8 } from "os";
2399
+ import { dirname as dirname9, join as join11 } from "path";
2400
+ var DEFAULT_DIR8 = process.env.SUVEREN_DATA_DIR ?? join11(homedir8(), ".suveren");
2254
2401
  var FILE_NAME3 = "preview-log.json";
2255
2402
  var MAX_RECORDS2 = 500;
2256
2403
  var MAX_AGE_MS2 = 30 * 24 * 60 * 60 * 1e3;
2257
2404
  var PreviewLog = class {
2258
2405
  filePath;
2259
2406
  constructor(filePath) {
2260
- const baseDir = filePath && filePath.endsWith(".json") ? dirname8(filePath) : filePath ?? DEFAULT_DIR7;
2261
- this.filePath = join10(baseDir, FILE_NAME3);
2407
+ const baseDir = filePath && filePath.endsWith(".json") ? dirname9(filePath) : filePath ?? DEFAULT_DIR8;
2408
+ this.filePath = join11(baseDir, FILE_NAME3);
2262
2409
  }
2263
2410
  /** Record one preview read. Never throws — recording must not break the read. */
2264
2411
  record(entry) {
@@ -2280,28 +2427,28 @@ var PreviewLog = class {
2280
2427
  return [...this.load().entries];
2281
2428
  }
2282
2429
  load() {
2283
- if (!existsSync10(this.filePath)) return { version: 1, entries: [] };
2430
+ if (!existsSync11(this.filePath)) return { version: 1, entries: [] };
2284
2431
  try {
2285
- const parsed = JSON.parse(readFileSync11(this.filePath, "utf-8"));
2432
+ const parsed = JSON.parse(readFileSync12(this.filePath, "utf-8"));
2286
2433
  return { version: 1, entries: parsed.entries ?? [] };
2287
2434
  } catch {
2288
2435
  return { version: 1, entries: [] };
2289
2436
  }
2290
2437
  }
2291
2438
  persist(file) {
2292
- mkdirSync8(dirname8(this.filePath), { recursive: true, mode: 448 });
2439
+ mkdirSync9(dirname9(this.filePath), { recursive: true, mode: 448 });
2293
2440
  const tmp = `${this.filePath}.tmp`;
2294
- writeFileSync8(tmp, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
2441
+ writeFileSync9(tmp, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
2295
2442
  renameSync3(tmp, this.filePath);
2296
2443
  }
2297
2444
  };
2298
2445
 
2299
2446
  // src/lib/outcome-outbox.ts
2300
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync11, unlinkSync as unlinkSync5 } from "fs";
2301
- import { homedir as homedir8 } from "os";
2302
- import { dirname as dirname9, join as join11 } from "path";
2303
- import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes5 } from "crypto";
2304
- var DEFAULT_DIR8 = process.env.SUVEREN_DATA_DIR ?? join11(homedir8(), ".suveren");
2447
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync12, unlinkSync as unlinkSync6 } from "fs";
2448
+ import { homedir as homedir9 } from "os";
2449
+ import { dirname as dirname10, join as join12 } from "path";
2450
+ import { createCipheriv as createCipheriv6, createDecipheriv as createDecipheriv6, randomBytes as randomBytes6 } from "crypto";
2451
+ var DEFAULT_DIR9 = process.env.SUVEREN_DATA_DIR ?? join12(homedir9(), ".suveren");
2305
2452
  var MAX_AGE_MS3 = 7 * 24 * 60 * 60 * 1e3;
2306
2453
  function outcomeEntryFor(proposal, submittedHere, who, nowMs) {
2307
2454
  if (!submittedHere) return null;
@@ -2316,19 +2463,19 @@ var OutcomeOutbox = class {
2316
2463
  baseDir;
2317
2464
  vaultKey = null;
2318
2465
  constructor(filePath) {
2319
- this.baseDir = filePath && filePath.endsWith(".json") ? dirname9(filePath) : filePath ?? DEFAULT_DIR8;
2466
+ this.baseDir = filePath && filePath.endsWith(".json") ? dirname10(filePath) : filePath ?? DEFAULT_DIR9;
2320
2467
  this.loadPlaintext();
2321
2468
  }
2322
2469
  /** Mirrors DenialLog.setVaultKey — migrates plaintext -> encrypted once, in place. */
2323
2470
  setVaultKey(key) {
2324
2471
  this.vaultKey = key;
2325
- if (existsSync11(this.encryptedFilePath)) {
2472
+ if (existsSync12(this.encryptedFilePath)) {
2326
2473
  this.loadEncrypted();
2327
2474
  } else if (this.entries.length > 0) {
2328
2475
  this.persistEncrypted();
2329
- if (existsSync11(this.plaintextFilePath)) {
2476
+ if (existsSync12(this.plaintextFilePath)) {
2330
2477
  try {
2331
- unlinkSync5(this.plaintextFilePath);
2478
+ unlinkSync6(this.plaintextFilePath);
2332
2479
  } catch {
2333
2480
  }
2334
2481
  }
@@ -2377,30 +2524,30 @@ var OutcomeOutbox = class {
2377
2524
  // ─── Encryption (mirrors DenialLog / ExecutionLog) ─────────────────────────
2378
2525
  encrypt(plaintext) {
2379
2526
  if (!this.vaultKey) throw new Error("No vault key");
2380
- const iv = randomBytes5(12);
2381
- const cipher = createCipheriv5("aes-256-gcm", this.vaultKey, iv);
2527
+ const iv = randomBytes6(12);
2528
+ const cipher = createCipheriv6("aes-256-gcm", this.vaultKey, iv);
2382
2529
  const enc = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
2383
2530
  return { iv: iv.toString("hex"), ciphertext: enc.toString("hex"), tag: cipher.getAuthTag().toString("hex") };
2384
2531
  }
2385
2532
  decrypt(blob) {
2386
2533
  if (!this.vaultKey) throw new Error("No vault key");
2387
- const decipher = createDecipheriv5("aes-256-gcm", this.vaultKey, Buffer.from(blob.iv, "hex"));
2534
+ const decipher = createDecipheriv6("aes-256-gcm", this.vaultKey, Buffer.from(blob.iv, "hex"));
2388
2535
  decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
2389
2536
  return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "hex")), decipher.final()]).toString("utf8");
2390
2537
  }
2391
2538
  get plaintextFilePath() {
2392
- return join11(this.baseDir, "outcome-outbox.json");
2539
+ return join12(this.baseDir, "outcome-outbox.json");
2393
2540
  }
2394
2541
  get encryptedFilePath() {
2395
- return join11(this.baseDir, "outcome-outbox.enc.json");
2542
+ return join12(this.baseDir, "outcome-outbox.enc.json");
2396
2543
  }
2397
2544
  loadPlaintext() {
2398
- if (!existsSync11(this.plaintextFilePath)) {
2399
- mkdirSync9(this.baseDir, { recursive: true });
2545
+ if (!existsSync12(this.plaintextFilePath)) {
2546
+ mkdirSync10(this.baseDir, { recursive: true });
2400
2547
  return;
2401
2548
  }
2402
2549
  try {
2403
- const data = JSON.parse(readFileSync12(this.plaintextFilePath, "utf-8"));
2550
+ const data = JSON.parse(readFileSync13(this.plaintextFilePath, "utf-8"));
2404
2551
  this.entries = data.entries ?? [];
2405
2552
  this.prune(Date.now());
2406
2553
  } catch {
@@ -2409,9 +2556,9 @@ var OutcomeOutbox = class {
2409
2556
  }
2410
2557
  }
2411
2558
  loadEncrypted() {
2412
- if (!existsSync11(this.encryptedFilePath)) return;
2559
+ if (!existsSync12(this.encryptedFilePath)) return;
2413
2560
  try {
2414
- const data = JSON.parse(readFileSync12(this.encryptedFilePath, "utf-8"));
2561
+ const data = JSON.parse(readFileSync13(this.encryptedFilePath, "utf-8"));
2415
2562
  const storeFile = JSON.parse(this.decrypt(data.blob));
2416
2563
  this.entries = storeFile.entries ?? [];
2417
2564
  this.prune(Date.now());
@@ -2426,14 +2573,14 @@ var OutcomeOutbox = class {
2426
2573
  }
2427
2574
  persistPlaintext() {
2428
2575
  const data = { version: 1, entries: this.entries };
2429
- mkdirSync9(this.baseDir, { recursive: true, mode: 448 });
2430
- writeFileSync9(this.plaintextFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
2576
+ mkdirSync10(this.baseDir, { recursive: true, mode: 448 });
2577
+ writeFileSync10(this.plaintextFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
2431
2578
  }
2432
2579
  persistEncrypted() {
2433
2580
  const storeFile = { version: 1, entries: this.entries };
2434
2581
  const data = { version: 1, blob: this.encrypt(JSON.stringify(storeFile)) };
2435
- mkdirSync9(this.baseDir, { recursive: true, mode: 448 });
2436
- writeFileSync9(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
2582
+ mkdirSync10(this.baseDir, { recursive: true, mode: 448 });
2583
+ writeFileSync10(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
2437
2584
  }
2438
2585
  };
2439
2586
  function formatOutcomeLine(e) {
@@ -2545,10 +2692,10 @@ var MCPGatekeeper = class {
2545
2692
  };
2546
2693
 
2547
2694
  // src/lib/report/report-store.ts
2548
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync12, unlinkSync as unlinkSync6 } from "fs";
2549
- import { homedir as homedir9 } from "os";
2550
- import { dirname as dirname10, join as join12 } from "path";
2551
- import { createCipheriv as createCipheriv6, createDecipheriv as createDecipheriv6, randomBytes as randomBytes6 } from "crypto";
2695
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync13, unlinkSync as unlinkSync7 } from "fs";
2696
+ import { homedir as homedir10 } from "os";
2697
+ import { dirname as dirname11, join as join13 } from "path";
2698
+ import { createCipheriv as createCipheriv7, createDecipheriv as createDecipheriv7, randomBytes as randomBytes7 } from "crypto";
2552
2699
 
2553
2700
  // src/lib/report/record-resolvers.ts
2554
2701
  function findById(rows, id) {
@@ -2856,29 +3003,29 @@ async function buildCoverage(caseElements, getExport, archive, referenced, windo
2856
3003
  }
2857
3004
 
2858
3005
  // src/lib/report/report-store.ts
2859
- var DEFAULT_DIR9 = process.env.SUVEREN_DATA_DIR ?? join12(homedir9(), ".suveren");
3006
+ var DEFAULT_DIR10 = process.env.SUVEREN_DATA_DIR ?? join13(homedir10(), ".suveren");
2860
3007
  var ReportStore = class {
2861
3008
  report = null;
2862
3009
  baseDir;
2863
3010
  vaultKey = null;
2864
3011
  constructor(filePath) {
2865
3012
  if (filePath && filePath.endsWith(".json")) {
2866
- this.baseDir = dirname10(filePath);
3013
+ this.baseDir = dirname11(filePath);
2867
3014
  } else {
2868
- this.baseDir = filePath ?? DEFAULT_DIR9;
3015
+ this.baseDir = filePath ?? DEFAULT_DIR10;
2869
3016
  }
2870
3017
  this.loadPlaintext();
2871
3018
  }
2872
3019
  /** Set the vault key for encryption. Triggers migration from plaintext if needed. */
2873
3020
  setVaultKey(key) {
2874
3021
  this.vaultKey = key;
2875
- if (existsSync12(this.encryptedFilePath)) {
3022
+ if (existsSync13(this.encryptedFilePath)) {
2876
3023
  this.loadEncrypted();
2877
3024
  } else if (this.report !== null) {
2878
3025
  this.persistEncrypted();
2879
- if (existsSync12(this.plaintextFilePath)) {
3026
+ if (existsSync13(this.plaintextFilePath)) {
2880
3027
  try {
2881
- unlinkSync6(this.plaintextFilePath);
3028
+ unlinkSync7(this.plaintextFilePath);
2882
3029
  } catch {
2883
3030
  }
2884
3031
  }
@@ -2891,7 +3038,7 @@ var ReportStore = class {
2891
3038
  * "the report exists but is locked".
2892
3039
  */
2893
3040
  isLocked() {
2894
- return this.vaultKey === null && existsSync12(this.encryptedFilePath);
3041
+ return this.vaultKey === null && existsSync13(this.encryptedFilePath);
2895
3042
  }
2896
3043
  /** The stored, already-verified report — or null if none has been saved
2897
3044
  * (and the store is not locked; check `isLocked()` first). */
@@ -2925,49 +3072,49 @@ var ReportStore = class {
2925
3072
  // ─── Encryption helpers ─────────────────────────────────────────────────
2926
3073
  encrypt(plaintext) {
2927
3074
  if (!this.vaultKey) throw new Error("No vault key");
2928
- const iv = randomBytes6(12);
2929
- const cipher = createCipheriv6("aes-256-gcm", this.vaultKey, iv);
3075
+ const iv = randomBytes7(12);
3076
+ const cipher = createCipheriv7("aes-256-gcm", this.vaultKey, iv);
2930
3077
  const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
2931
3078
  const tag = cipher.getAuthTag();
2932
3079
  return { iv: iv.toString("hex"), ciphertext: encrypted.toString("hex"), tag: tag.toString("hex") };
2933
3080
  }
2934
3081
  decrypt(blob) {
2935
3082
  if (!this.vaultKey) throw new Error("No vault key");
2936
- const decipher = createDecipheriv6("aes-256-gcm", this.vaultKey, Buffer.from(blob.iv, "hex"));
3083
+ const decipher = createDecipheriv7("aes-256-gcm", this.vaultKey, Buffer.from(blob.iv, "hex"));
2937
3084
  decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
2938
3085
  const decrypted = Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "hex")), decipher.final()]);
2939
3086
  return decrypted.toString("utf8");
2940
3087
  }
2941
3088
  // ─── File paths ─────────────────────────────────────────────────────────
2942
3089
  get plaintextFilePath() {
2943
- return join12(this.baseDir, "report.json");
3090
+ return join13(this.baseDir, "report.json");
2944
3091
  }
2945
3092
  get encryptedFilePath() {
2946
- return join12(this.baseDir, "report.enc.json");
3093
+ return join13(this.baseDir, "report.enc.json");
2947
3094
  }
2948
3095
  // ─── Load / Persist ─────────────────────────────────────────────────────
2949
3096
  loadPlaintext() {
2950
- if (!existsSync12(this.plaintextFilePath)) {
2951
- mkdirSync10(this.baseDir, { recursive: true });
3097
+ if (!existsSync13(this.plaintextFilePath)) {
3098
+ mkdirSync11(this.baseDir, { recursive: true });
2952
3099
  return;
2953
3100
  }
2954
3101
  try {
2955
- const raw = readFileSync13(this.plaintextFilePath, "utf-8");
3102
+ const raw = readFileSync14(this.plaintextFilePath, "utf-8");
2956
3103
  const parsed = JSON.parse(raw);
2957
3104
  this.report = parsed.report ?? null;
2958
3105
  } catch {
2959
3106
  console.error(`[ReportStore] Could not parse ${this.plaintextFilePath} \u2014 preserving as .corrupt`);
2960
3107
  try {
2961
- writeFileSync10(`${this.plaintextFilePath}.corrupt-${Date.now()}`, readFileSync13(this.plaintextFilePath));
3108
+ writeFileSync11(`${this.plaintextFilePath}.corrupt-${Date.now()}`, readFileSync14(this.plaintextFilePath));
2962
3109
  } catch {
2963
3110
  }
2964
3111
  this.report = null;
2965
3112
  }
2966
3113
  }
2967
3114
  loadEncrypted() {
2968
- if (!existsSync12(this.encryptedFilePath)) return;
3115
+ if (!existsSync13(this.encryptedFilePath)) return;
2969
3116
  try {
2970
- const raw = readFileSync13(this.encryptedFilePath, "utf-8");
3117
+ const raw = readFileSync14(this.encryptedFilePath, "utf-8");
2971
3118
  const data = JSON.parse(raw);
2972
3119
  const decrypted = this.decrypt(data.blob);
2973
3120
  const parsed = JSON.parse(decrypted);
@@ -2975,7 +3122,7 @@ var ReportStore = class {
2975
3122
  } catch (err) {
2976
3123
  console.error(`[ReportStore] Could not decrypt ${this.encryptedFilePath} \u2014 preserving as .corrupt:`, err);
2977
3124
  try {
2978
- writeFileSync10(`${this.encryptedFilePath}.corrupt-${Date.now()}`, readFileSync13(this.encryptedFilePath));
3125
+ writeFileSync11(`${this.encryptedFilePath}.corrupt-${Date.now()}`, readFileSync14(this.encryptedFilePath));
2979
3126
  } catch {
2980
3127
  }
2981
3128
  this.report = null;
@@ -2990,16 +3137,16 @@ var ReportStore = class {
2990
3137
  }
2991
3138
  persistPlaintext() {
2992
3139
  const data = { version: 1, report: this.report };
2993
- mkdirSync10(this.baseDir, { recursive: true, mode: 448 });
2994
- writeFileSync10(this.plaintextFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
3140
+ mkdirSync11(this.baseDir, { recursive: true, mode: 448 });
3141
+ writeFileSync11(this.plaintextFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
2995
3142
  }
2996
3143
  persistEncrypted() {
2997
3144
  const data = {
2998
3145
  version: 1,
2999
3146
  blob: this.encrypt(JSON.stringify({ version: 1, report: this.report }))
3000
3147
  };
3001
- mkdirSync10(this.baseDir, { recursive: true, mode: 448 });
3002
- writeFileSync10(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
3148
+ mkdirSync11(this.baseDir, { recursive: true, mode: 448 });
3149
+ writeFileSync11(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
3003
3150
  }
3004
3151
  };
3005
3152
 
@@ -3017,6 +3164,8 @@ var SharedState = class {
3017
3164
  reportStore;
3018
3165
  /** Which tickets this Gatekeeper has executed — the "one execution per ticket" half of exactly-once. */
3019
3166
  executionJournal;
3167
+ /** The content half of what executionJournal records — see execution-result-store.ts. */
3168
+ executionResults;
3020
3169
  /** What THIS gateway submitted for each review-mode proposal it created —
3021
3170
  * see ticket-verify.ts / commitments.ts. */
3022
3171
  proposalSubmissions;
@@ -3057,6 +3206,7 @@ var SharedState = class {
3057
3206
  this.receiptArchive = new ReceiptArchive(gateStorePath);
3058
3207
  this.reportStore = new ReportStore(gateStorePath);
3059
3208
  this.executionJournal = new ExecutionJournal(gateStorePath);
3209
+ this.executionResults = new ExecutionResultStore(gateStorePath);
3060
3210
  this.proposalSubmissions = new ProposalSubmissionStore(gateStorePath);
3061
3211
  this.previewLog = new PreviewLog(gateStorePath);
3062
3212
  this.outcomeOutbox = new OutcomeOutbox(gateStorePath);
@@ -3180,7 +3330,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3180
3330
  import { z as z2 } from "zod";
3181
3331
 
3182
3332
  // src/lib/mandate-brief.ts
3183
- import { getProfile as getProfile4 } from "@hap/core";
3333
+ import { getProfile as getProfile5 } from "@hap/core";
3184
3334
 
3185
3335
  // src/lib/consumption.ts
3186
3336
  import { boundActionTypes } from "@hap/core";
@@ -3248,29 +3398,29 @@ function formatConsumptionFull(entries) {
3248
3398
  }
3249
3399
 
3250
3400
  // src/lib/context-loader.ts
3251
- import { readFileSync as readFileSync14, existsSync as existsSync13, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11, renameSync as renameSync4 } from "fs";
3252
- import { homedir as homedir10 } from "os";
3253
- import { join as join13, dirname as dirname11 } from "path";
3254
- var DEFAULT_DIR10 = process.env.SUVEREN_DATA_DIR ?? join13(homedir10(), ".suveren");
3401
+ import { readFileSync as readFileSync15, existsSync as existsSync14, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12, renameSync as renameSync4 } from "fs";
3402
+ import { homedir as homedir11 } from "os";
3403
+ import { join as join14, dirname as dirname12 } from "path";
3404
+ var DEFAULT_DIR11 = process.env.SUVEREN_DATA_DIR ?? join14(homedir11(), ".suveren");
3255
3405
  var CONTEXT_MAX_BYTES = 16 * 1024;
3256
3406
  function contextFilePath(dataDir2) {
3257
- return join13(dataDir2 ?? DEFAULT_DIR10, "context.md");
3407
+ return join14(dataDir2 ?? DEFAULT_DIR11, "context.md");
3258
3408
  }
3259
3409
  function writeContextFile(content, dataDir2) {
3260
3410
  const bytes = Buffer.byteLength(content, "utf-8");
3261
3411
  if (bytes > CONTEXT_MAX_BYTES) throw new Error(`the brief is ${bytes} bytes; the limit is ${CONTEXT_MAX_BYTES}.`);
3262
3412
  const filePath = contextFilePath(dataDir2);
3263
- mkdirSync11(dirname11(filePath), { recursive: true });
3413
+ mkdirSync12(dirname12(filePath), { recursive: true });
3264
3414
  const tmpPath = `${filePath}.tmp`;
3265
- writeFileSync11(tmpPath, content, "utf-8");
3415
+ writeFileSync12(tmpPath, content, "utf-8");
3266
3416
  renameSync4(tmpPath, filePath);
3267
3417
  }
3268
3418
  var BRIEF_MAX_CHARS = 16 * 1024;
3269
3419
  function readContextFile(dataDir2) {
3270
3420
  const filePath = contextFilePath(dataDir2);
3271
- if (!existsSync13(filePath)) return null;
3421
+ if (!existsSync14(filePath)) return null;
3272
3422
  try {
3273
- const content = readFileSync14(filePath, "utf-8").trim();
3423
+ const content = readFileSync15(filePath, "utf-8").trim();
3274
3424
  return content || null;
3275
3425
  } catch {
3276
3426
  return null;
@@ -3457,23 +3607,244 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
3457
3607
  boundContent
3458
3608
  };
3459
3609
  }
3460
- function attachTicketId(tool, args, ticketId) {
3461
- const schema = tool.inputSchema;
3462
- if (!schema?.properties || !("ticket_id" in schema.properties)) return args;
3463
- return { ...args, ticket_id: ticketId };
3610
+ function attachTicketId(tool, args, ticketId) {
3611
+ const schema = tool.inputSchema;
3612
+ if (!schema?.properties || !("ticket_id" in schema.properties)) return args;
3613
+ return { ...args, ticket_id: ticketId };
3614
+ }
3615
+
3616
+ // src/lib/preview.ts
3617
+ import { computeContentHash as computeContentHash2 } from "@hap/core";
3618
+
3619
+ // src/lib/manifest-loader.ts
3620
+ import { readFileSync as readFileSync16, existsSync as existsSync15 } from "fs";
3621
+ import { join as join15, resolve } from "path";
3622
+ import { getProfile as getProfile2 } from "@hap/core";
3623
+
3624
+ // src/lib/tool-gating-types.ts
3625
+ var MAX_PREVIEW_READS = 10;
3626
+ var MAX_FOREACH_ELEMENTS = 50;
3627
+ var MAX_RECORD_KEY_STEPS = 2;
3628
+
3629
+ // src/lib/scope-specificity.ts
3630
+ function tokenSet(value) {
3631
+ if (value === void 0 || value === null) return /* @__PURE__ */ new Set();
3632
+ return new Set(
3633
+ String(value).split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)
3634
+ );
3635
+ }
3636
+ function subsetOrEqual(a, b) {
3637
+ if (b.size === 0) return true;
3638
+ if (a.size === 0) return false;
3639
+ for (const x of a) if (!b.has(x)) return false;
3640
+ return true;
3641
+ }
3642
+ function atLeastAsSpecific(keys, a, b) {
3643
+ for (const k of keys) {
3644
+ if (!subsetOrEqual(tokenSet(a[k]), tokenSet(b[k]))) return false;
3645
+ }
3646
+ return true;
3647
+ }
3648
+ function strictlyMoreSpecific(keys, a, b) {
3649
+ return atLeastAsSpecific(keys, a, b) && !atLeastAsSpecific(keys, b, a);
3650
+ }
3651
+ function selectAuthorization(contextKeys, passers) {
3652
+ if (passers.length === 1) {
3653
+ return { chosen: passers[0], superseded: [], reason: "sole" };
3654
+ }
3655
+ const dominators = passers.filter(
3656
+ (a) => passers.every((b) => b === a || strictlyMoreSpecific(contextKeys, a.context, b.context))
3657
+ );
3658
+ if (dominators.length === 1) {
3659
+ const chosen2 = dominators[0];
3660
+ return { chosen: chosen2, superseded: passers.filter((p) => p !== chosen2), reason: "most-specific" };
3661
+ }
3662
+ const approvers = passers.filter((p) => p.requiresApproval);
3663
+ const pool = approvers.length > 0 ? approvers : passers;
3664
+ const chosen = pool.slice().sort((a, b) => a.id.localeCompare(b.id))[0];
3665
+ return {
3666
+ chosen,
3667
+ superseded: passers.filter((p) => p !== chosen),
3668
+ reason: approvers.length > 0 ? "fail-safe-approval" : "fail-safe-tiebreak"
3669
+ };
3670
+ }
3671
+
3672
+ // src/lib/read-gate.ts
3673
+ function boundsSatisfyReadGate(bounds, gate) {
3674
+ if (!gate.boundField) return true;
3675
+ const actual = bounds?.[gate.boundField];
3676
+ return actual !== void 0 && String(actual) === gate.requiredValue;
3677
+ }
3678
+ function readToolIsGoverned(g) {
3679
+ if (!g) return false;
3680
+ return Boolean(g.boundField) || g.read != null || g.readGovernance === "none";
3681
+ }
3682
+ function allowedResources(contexts, boundField) {
3683
+ const out = /* @__PURE__ */ new Set();
3684
+ for (const ctx of contexts) {
3685
+ for (const v of tokenSet(ctx?.[boundField])) out.add(v);
3686
+ }
3687
+ return out;
3688
+ }
3689
+ function deniedResources(requested, allowed) {
3690
+ return requested.filter((r) => !allowed.has(r));
3691
+ }
3692
+ function filterItemsByResource(items, idPath, allowed) {
3693
+ return items.filter((it) => {
3694
+ const id = getByDottedPath(it, idPath);
3695
+ return typeof id === "string" && allowed.has(id);
3696
+ });
3697
+ }
3698
+ function hasBlockedValue(item, valuesPath, blocked) {
3699
+ const v = getByDottedPath(item, valuesPath);
3700
+ const arr = Array.isArray(v) ? v : v != null ? [v] : [];
3701
+ return arr.some((x) => typeof x === "string" && blocked.has(x));
3702
+ }
3703
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
3704
+ function parseMessageTimestamp(value) {
3705
+ if (typeof value === "number" && Number.isFinite(value)) return value;
3706
+ if (typeof value === "string" && value.trim() !== "") {
3707
+ const n = Number(value);
3708
+ if (Number.isFinite(n)) return n;
3709
+ const iso = Date.parse(value);
3710
+ if (Number.isFinite(iso)) return iso;
3711
+ }
3712
+ return null;
3713
+ }
3714
+ function ageInDays(timestampMs, nowMs) {
3715
+ return Math.floor((nowMs - timestampMs) / MS_PER_DAY);
3716
+ }
3717
+ function maxReadAgeDays(boundsList, field) {
3718
+ let max = null;
3719
+ for (const b of boundsList) {
3720
+ const raw = b?.[field];
3721
+ const n = typeof raw === "number" ? raw : raw !== void 0 ? Number(raw) : NaN;
3722
+ if (Number.isFinite(n)) max = max === null ? n : Math.max(max, n);
3723
+ }
3724
+ return max;
3725
+ }
3726
+ function isOlderThanMaxAge(timestampMs, maxAgeDays, nowMs) {
3727
+ if (timestampMs === null) return true;
3728
+ return ageInDays(timestampMs, nowMs) > maxAgeDays;
3729
+ }
3730
+ var TRAILING_OPERATOR = /(?:^|\s)(?:OR|AND|NOT)$|[-+|&:]$/i;
3731
+ function parensBalanced(s) {
3732
+ let depth = 0;
3733
+ let inQuote = false;
3734
+ for (const c of s) {
3735
+ if (c === '"') {
3736
+ inQuote = !inQuote;
3737
+ continue;
3738
+ }
3739
+ if (inQuote) continue;
3740
+ if (c === "(") depth++;
3741
+ else if (c === ")" && --depth < 0) return false;
3742
+ }
3743
+ return !inQuote && depth === 0;
3744
+ }
3745
+ function composeReadQuery(agentQuery, clauses) {
3746
+ const injected = clauses.filter((c) => typeof c === "string" && c.trim() !== "").map((c) => c.trim());
3747
+ const base = (agentQuery ?? "").trim();
3748
+ if (base === "") return { ok: true, query: injected.join(" ") };
3749
+ if (!parensBalanced(base)) {
3750
+ return { ok: false, reason: "the requested search query has unbalanced parentheses" };
3751
+ }
3752
+ if (TRAILING_OPERATOR.test(base)) {
3753
+ return { ok: false, reason: "the requested search query ends in a dangling operator" };
3754
+ }
3755
+ if (injected.length === 0) return { ok: true, query: base };
3756
+ return { ok: true, query: `(${base}) ${injected.join(" ")}` };
3757
+ }
3758
+ function detectAgeConflict(agentQuery, conflictPattern, maxAgeDays) {
3759
+ if (!agentQuery || !conflictPattern || maxAgeDays === null) return null;
3760
+ let re;
3761
+ try {
3762
+ re = new RegExp(conflictPattern, "gi");
3763
+ } catch {
3764
+ return null;
3765
+ }
3766
+ let worst = null;
3767
+ for (const match of agentQuery.matchAll(re)) {
3768
+ const days = Number(match[1]);
3769
+ if (!Number.isFinite(days)) continue;
3770
+ if (days >= maxAgeDays && (worst === null || days > worst)) worst = days;
3771
+ }
3772
+ return worst;
3773
+ }
3774
+ function resolveAgeBoundField(boundsSchema, ageExecField) {
3775
+ const fields = boundsSchema?.fields;
3776
+ if (!fields) return null;
3777
+ for (const [name, def] of Object.entries(fields)) {
3778
+ if (def?.boundType?.kind === "per_transaction" && def.boundType.of === ageExecField) return name;
3779
+ }
3780
+ return null;
3781
+ }
3782
+ function classifyEnumSwitches(boundsSchema) {
3783
+ const readSide = [];
3784
+ const writeSide = [];
3785
+ for (const [name, def] of Object.entries(boundsSchema?.fields ?? {})) {
3786
+ if (def?.boundType?.kind !== "enum") continue;
3787
+ const values = new Set(def.boundType.values ?? []);
3788
+ if (!values.has("none")) continue;
3789
+ if (values.size === 2 && values.has("unlimited")) readSide.push(name);
3790
+ else writeSide.push(name);
3791
+ }
3792
+ return { readSide, writeSide };
3793
+ }
3794
+ function uncoveredReadSwitchReason(overrides, boundsSchema) {
3795
+ const { readSide, writeSide } = classifyEnumSwitches(boundsSchema);
3796
+ const anySwitch = [...readSide, ...writeSide];
3797
+ if (anySwitch.length === 0) return null;
3798
+ const valuesOf = (field) => boundsSchema?.fields?.[field]?.boundType?.values ?? [];
3799
+ for (const [toolName, entry] of Object.entries(overrides)) {
3800
+ if (entry !== null && typeof entry !== "object") continue;
3801
+ const e = entry ?? {};
3802
+ if (entry !== null && e.category !== "read") continue;
3803
+ if (e.readGovernance === "none") {
3804
+ if (!e.readGovernanceReason) {
3805
+ return `override "${toolName}" declares readGovernance:"none" with no readGovernanceReason \u2014 an exemption from this profile's switch(es) [${anySwitch.join(", ")}] needs a written reason`;
3806
+ }
3807
+ continue;
3808
+ }
3809
+ if (!e.boundField || !anySwitch.includes(e.boundField)) {
3810
+ return `override "${toolName}" is category:"read" under a profile that declares enum switch(es) [${anySwitch.join(", ")}] (read-side: [${readSide.join(", ")}], write-side: [${writeSide.join(", ")}]), but does not reference one via boundField (and carries no readGovernance exemption) \u2014 it would silently ignore e.g. "${anySwitch[0]}: none"`;
3811
+ }
3812
+ const positiveValues = valuesOf(e.boundField).filter((v) => v !== "none");
3813
+ if (!e.requiredValue || !positiveValues.includes(e.requiredValue)) {
3814
+ return `override "${toolName}".requiredValue for boundField "${e.boundField}" must be ${positiveValues.map((v) => `"${v}"`).join(" or ")}, not ${JSON.stringify(e.requiredValue)} \u2014 requiring "none" (or an unlisted value) would invert the gate`;
3815
+ }
3816
+ }
3817
+ return null;
3818
+ }
3819
+ function getByDottedPath(obj, path) {
3820
+ return path.split(".").reduce(
3821
+ (o, k) => o == null ? void 0 : o[k],
3822
+ obj
3823
+ );
3824
+ }
3825
+ function firstParsableDate(obj, paths) {
3826
+ for (const path of Array.isArray(paths) ? paths : [paths]) {
3827
+ const parsed = parseMessageTimestamp(getByDottedPath(obj, path));
3828
+ if (parsed !== null) return parsed;
3829
+ }
3830
+ return null;
3831
+ }
3832
+ function renderAgeConstraint(template, maxAgeDays, nowMs) {
3833
+ const floorIso = new Date(nowMs - maxAgeDays * MS_PER_DAY).toISOString();
3834
+ return template.replace(/\{days\}/g, String(maxAgeDays)).replace(/\{date\}/g, floorIso.slice(0, 10));
3835
+ }
3836
+ function ageFloorValue(maxAgeDays, nowMs, format = "iso") {
3837
+ const floorMs = nowMs - maxAgeDays * MS_PER_DAY;
3838
+ if (format === "epoch_ms") return floorMs;
3839
+ if (format === "epoch_s") return String(Math.floor(floorMs / 1e3));
3840
+ return new Date(floorMs).toISOString();
3841
+ }
3842
+ function clampAgeFloor(requested, maxAgeDays, nowMs, format = "iso") {
3843
+ const floorMs = nowMs - maxAgeDays * MS_PER_DAY;
3844
+ const asked = parseMessageTimestamp(requested);
3845
+ if (asked !== null && asked >= floorMs) return requested;
3846
+ return ageFloorValue(maxAgeDays, nowMs, format);
3464
3847
  }
3465
-
3466
- // src/lib/preview.ts
3467
- import { computeContentHash as computeContentHash2 } from "@hap/core";
3468
-
3469
- // src/lib/manifest-loader.ts
3470
- import { readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
3471
- import { join as join14, resolve } from "path";
3472
-
3473
- // src/lib/tool-gating-types.ts
3474
- var MAX_PREVIEW_READS = 10;
3475
- var MAX_FOREACH_ELEMENTS = 50;
3476
- var MAX_RECORD_KEY_STEPS = 2;
3477
3848
 
3478
3849
  // src/lib/manifest-loader.ts
3479
3850
  var EXACT_SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
@@ -3823,28 +4194,34 @@ function invalidManifestContextReason(manifest) {
3823
4194
  }
3824
4195
  return null;
3825
4196
  }
4197
+ function invalidManifestReadGateReason(manifest) {
4198
+ const profileDef = getProfile2(manifest.profile);
4199
+ if (!profileDef) return null;
4200
+ const overrides = manifest.toolGating?.overrides ?? {};
4201
+ return uncoveredReadSwitchReason(overrides, profileDef.boundsSchema);
4202
+ }
3826
4203
  var manifests = /* @__PURE__ */ new Map();
3827
4204
  function loadManifests(integrationsDir) {
3828
4205
  const dir = resolve(
3829
- integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join14(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
4206
+ integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join15(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
3830
4207
  );
3831
- const indexPath = join14(dir, "index.json");
3832
- if (!existsSync14(indexPath)) {
4208
+ const indexPath = join15(dir, "index.json");
4209
+ if (!existsSync15(indexPath)) {
3833
4210
  console.error(`[ManifestLoader] No index.json found at ${indexPath}, skipping manifest loading`);
3834
4211
  return 0;
3835
4212
  }
3836
4213
  let index;
3837
4214
  try {
3838
- index = JSON.parse(readFileSync15(indexPath, "utf-8"));
4215
+ index = JSON.parse(readFileSync16(indexPath, "utf-8"));
3839
4216
  } catch (err) {
3840
4217
  console.error(`[ManifestLoader] Failed to parse ${indexPath}:`, err);
3841
4218
  return 0;
3842
4219
  }
3843
4220
  let loaded = 0;
3844
4221
  for (const [id, relativePath] of Object.entries(index.integrations)) {
3845
- const manifestPath = join14(dir, relativePath);
4222
+ const manifestPath = join15(dir, relativePath);
3846
4223
  try {
3847
- const manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
4224
+ const manifest = JSON.parse(readFileSync16(manifestPath, "utf-8"));
3848
4225
  const pinReason = invalidNpmPinReason(manifest);
3849
4226
  if (pinReason) {
3850
4227
  console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${pinReason}`);
@@ -3860,6 +4237,11 @@ function loadManifests(integrationsDir) {
3860
4237
  console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${contextReason}`);
3861
4238
  continue;
3862
4239
  }
4240
+ const readGateReason = invalidManifestReadGateReason(manifest);
4241
+ if (readGateReason) {
4242
+ console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${readGateReason}`);
4243
+ continue;
4244
+ }
3863
4245
  manifests.set(id, manifest);
3864
4246
  loaded++;
3865
4247
  } catch (err) {
@@ -4144,189 +4526,6 @@ async function buildInternalPreview(integrationManager2, integrationId, toolName
4144
4526
  });
4145
4527
  }
4146
4528
 
4147
- // src/lib/scope-specificity.ts
4148
- function tokenSet(value) {
4149
- if (value === void 0 || value === null) return /* @__PURE__ */ new Set();
4150
- return new Set(
4151
- String(value).split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)
4152
- );
4153
- }
4154
- function subsetOrEqual(a, b) {
4155
- if (b.size === 0) return true;
4156
- if (a.size === 0) return false;
4157
- for (const x of a) if (!b.has(x)) return false;
4158
- return true;
4159
- }
4160
- function atLeastAsSpecific(keys, a, b) {
4161
- for (const k of keys) {
4162
- if (!subsetOrEqual(tokenSet(a[k]), tokenSet(b[k]))) return false;
4163
- }
4164
- return true;
4165
- }
4166
- function strictlyMoreSpecific(keys, a, b) {
4167
- return atLeastAsSpecific(keys, a, b) && !atLeastAsSpecific(keys, b, a);
4168
- }
4169
- function selectAuthorization(contextKeys, passers) {
4170
- if (passers.length === 1) {
4171
- return { chosen: passers[0], superseded: [], reason: "sole" };
4172
- }
4173
- const dominators = passers.filter(
4174
- (a) => passers.every((b) => b === a || strictlyMoreSpecific(contextKeys, a.context, b.context))
4175
- );
4176
- if (dominators.length === 1) {
4177
- const chosen2 = dominators[0];
4178
- return { chosen: chosen2, superseded: passers.filter((p) => p !== chosen2), reason: "most-specific" };
4179
- }
4180
- const approvers = passers.filter((p) => p.requiresApproval);
4181
- const pool = approvers.length > 0 ? approvers : passers;
4182
- const chosen = pool.slice().sort((a, b) => a.id.localeCompare(b.id))[0];
4183
- return {
4184
- chosen,
4185
- superseded: passers.filter((p) => p !== chosen),
4186
- reason: approvers.length > 0 ? "fail-safe-approval" : "fail-safe-tiebreak"
4187
- };
4188
- }
4189
-
4190
- // src/lib/read-gate.ts
4191
- function boundsSatisfyReadGate(bounds, gate) {
4192
- if (!gate.boundField) return true;
4193
- const actual = bounds?.[gate.boundField];
4194
- return actual !== void 0 && String(actual) === gate.requiredValue;
4195
- }
4196
- function readToolIsGoverned(g) {
4197
- if (!g) return false;
4198
- return Boolean(g.boundField) || g.read != null || g.readGovernance === "none";
4199
- }
4200
- function allowedResources(contexts, boundField) {
4201
- const out = /* @__PURE__ */ new Set();
4202
- for (const ctx of contexts) {
4203
- for (const v of tokenSet(ctx?.[boundField])) out.add(v);
4204
- }
4205
- return out;
4206
- }
4207
- function deniedResources(requested, allowed) {
4208
- return requested.filter((r) => !allowed.has(r));
4209
- }
4210
- function filterItemsByResource(items, idPath, allowed) {
4211
- return items.filter((it) => {
4212
- const id = getByDottedPath(it, idPath);
4213
- return typeof id === "string" && allowed.has(id);
4214
- });
4215
- }
4216
- function hasBlockedValue(item, valuesPath, blocked) {
4217
- const v = getByDottedPath(item, valuesPath);
4218
- const arr = Array.isArray(v) ? v : v != null ? [v] : [];
4219
- return arr.some((x) => typeof x === "string" && blocked.has(x));
4220
- }
4221
- var MS_PER_DAY = 24 * 60 * 60 * 1e3;
4222
- function parseMessageTimestamp(value) {
4223
- if (typeof value === "number" && Number.isFinite(value)) return value;
4224
- if (typeof value === "string" && value.trim() !== "") {
4225
- const n = Number(value);
4226
- if (Number.isFinite(n)) return n;
4227
- const iso = Date.parse(value);
4228
- if (Number.isFinite(iso)) return iso;
4229
- }
4230
- return null;
4231
- }
4232
- function ageInDays(timestampMs, nowMs) {
4233
- return Math.floor((nowMs - timestampMs) / MS_PER_DAY);
4234
- }
4235
- function maxReadAgeDays(boundsList, field) {
4236
- let max = null;
4237
- for (const b of boundsList) {
4238
- const raw = b?.[field];
4239
- const n = typeof raw === "number" ? raw : raw !== void 0 ? Number(raw) : NaN;
4240
- if (Number.isFinite(n)) max = max === null ? n : Math.max(max, n);
4241
- }
4242
- return max;
4243
- }
4244
- function isOlderThanMaxAge(timestampMs, maxAgeDays, nowMs) {
4245
- if (timestampMs === null) return true;
4246
- return ageInDays(timestampMs, nowMs) > maxAgeDays;
4247
- }
4248
- var TRAILING_OPERATOR = /(?:^|\s)(?:OR|AND|NOT)$|[-+|&:]$/i;
4249
- function parensBalanced(s) {
4250
- let depth = 0;
4251
- let inQuote = false;
4252
- for (const c of s) {
4253
- if (c === '"') {
4254
- inQuote = !inQuote;
4255
- continue;
4256
- }
4257
- if (inQuote) continue;
4258
- if (c === "(") depth++;
4259
- else if (c === ")" && --depth < 0) return false;
4260
- }
4261
- return !inQuote && depth === 0;
4262
- }
4263
- function composeReadQuery(agentQuery, clauses) {
4264
- const injected = clauses.filter((c) => typeof c === "string" && c.trim() !== "").map((c) => c.trim());
4265
- const base = (agentQuery ?? "").trim();
4266
- if (base === "") return { ok: true, query: injected.join(" ") };
4267
- if (!parensBalanced(base)) {
4268
- return { ok: false, reason: "the requested search query has unbalanced parentheses" };
4269
- }
4270
- if (TRAILING_OPERATOR.test(base)) {
4271
- return { ok: false, reason: "the requested search query ends in a dangling operator" };
4272
- }
4273
- if (injected.length === 0) return { ok: true, query: base };
4274
- return { ok: true, query: `(${base}) ${injected.join(" ")}` };
4275
- }
4276
- function detectAgeConflict(agentQuery, conflictPattern, maxAgeDays) {
4277
- if (!agentQuery || !conflictPattern || maxAgeDays === null) return null;
4278
- let re;
4279
- try {
4280
- re = new RegExp(conflictPattern, "gi");
4281
- } catch {
4282
- return null;
4283
- }
4284
- let worst = null;
4285
- for (const match of agentQuery.matchAll(re)) {
4286
- const days = Number(match[1]);
4287
- if (!Number.isFinite(days)) continue;
4288
- if (days >= maxAgeDays && (worst === null || days > worst)) worst = days;
4289
- }
4290
- return worst;
4291
- }
4292
- function resolveAgeBoundField(boundsSchema, ageExecField) {
4293
- const fields = boundsSchema?.fields;
4294
- if (!fields) return null;
4295
- for (const [name, def] of Object.entries(fields)) {
4296
- if (def?.boundType?.kind === "per_transaction" && def.boundType.of === ageExecField) return name;
4297
- }
4298
- return null;
4299
- }
4300
- function getByDottedPath(obj, path) {
4301
- return path.split(".").reduce(
4302
- (o, k) => o == null ? void 0 : o[k],
4303
- obj
4304
- );
4305
- }
4306
- function firstParsableDate(obj, paths) {
4307
- for (const path of Array.isArray(paths) ? paths : [paths]) {
4308
- const parsed = parseMessageTimestamp(getByDottedPath(obj, path));
4309
- if (parsed !== null) return parsed;
4310
- }
4311
- return null;
4312
- }
4313
- function renderAgeConstraint(template, maxAgeDays, nowMs) {
4314
- const floorIso = new Date(nowMs - maxAgeDays * MS_PER_DAY).toISOString();
4315
- return template.replace(/\{days\}/g, String(maxAgeDays)).replace(/\{date\}/g, floorIso.slice(0, 10));
4316
- }
4317
- function ageFloorValue(maxAgeDays, nowMs, format = "iso") {
4318
- const floorMs = nowMs - maxAgeDays * MS_PER_DAY;
4319
- if (format === "epoch_ms") return floorMs;
4320
- if (format === "epoch_s") return String(Math.floor(floorMs / 1e3));
4321
- return new Date(floorMs).toISOString();
4322
- }
4323
- function clampAgeFloor(requested, maxAgeDays, nowMs, format = "iso") {
4324
- const floorMs = nowMs - maxAgeDays * MS_PER_DAY;
4325
- const asked = parseMessageTimestamp(requested);
4326
- if (asked !== null && asked >= floorMs) return requested;
4327
- return ageFloorValue(maxAgeDays, nowMs, format);
4328
- }
4329
-
4330
4529
  // src/lib/field-path.ts
4331
4530
  function matchesWhere(element, where) {
4332
4531
  if (element === null || typeof element !== "object" || Array.isArray(element)) return false;
@@ -4811,8 +5010,8 @@ async function verifyTicket(cache, receipt, expected) {
4811
5010
  if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) {
4812
5011
  throw new TicketBindingMismatchError("Ticket carries no valid timestamp \u2014 refusing to execute.");
4813
5012
  }
4814
- const nowSec3 = Math.floor(Date.now() / 1e3);
4815
- const ageSec = nowSec3 - timestamp;
5013
+ const nowSec4 = Math.floor(Date.now() / 1e3);
5014
+ const ageSec = nowSec4 - timestamp;
4816
5015
  if (ageSec > TICKET_MAX_AGE_SECONDS) {
4817
5016
  throw new TicketBindingMismatchError(
4818
5017
  `Ticket is ${ageSec}s old, older than the ${TICKET_MAX_AGE_SECONDS}s freshness window \u2014 refusing to execute.`
@@ -4947,7 +5146,7 @@ function normalizeIncomingArgs(tool, args) {
4947
5146
  }
4948
5147
 
4949
5148
  // src/lib/numeric-execution.ts
4950
- import { getProfile as getProfile2 } from "@hap/core";
5149
+ import { getProfile as getProfile3 } from "@hap/core";
4951
5150
  function parseNumericString(value) {
4952
5151
  if (!/^-?\d+(\.\d+)?$/.test(value)) return void 0;
4953
5152
  const parsed = Number(value);
@@ -4963,7 +5162,7 @@ function declaredNumericFields(profile) {
4963
5162
  return out;
4964
5163
  }
4965
5164
  function coerceDeclaredNumericFields(profileId, execution) {
4966
- const fields = declaredNumericFields(getProfile2(profileId));
5165
+ const fields = declaredNumericFields(getProfile3(profileId));
4967
5166
  for (const field of fields) {
4968
5167
  const value = execution[field];
4969
5168
  if (typeof value !== "string") continue;
@@ -4973,7 +5172,7 @@ function coerceDeclaredNumericFields(profileId, execution) {
4973
5172
  }
4974
5173
 
4975
5174
  // src/lib/tool-proxy.ts
4976
- import { getProfile as getProfile3, ContentBindingError, boundActionTypes as boundActionTypes2 } from "@hap/core";
5175
+ import { getProfile as getProfile4, ContentBindingError, boundActionTypes as boundActionTypes2 } from "@hap/core";
4977
5176
  import { readFile } from "fs/promises";
4978
5177
  import { extname } from "path";
4979
5178
  import { randomUUID } from "crypto";
@@ -5045,7 +5244,7 @@ function profileMatches(profileId, shortName2) {
5045
5244
  return profileId === shortName2 || profileId.includes("/" + shortName2 + "@") || profileId.endsWith("/" + shortName2);
5046
5245
  }
5047
5246
  function wireExecutionContext(profileId, execution) {
5048
- const profile = getProfile3(profileId);
5247
+ const profile = getProfile4(profileId);
5049
5248
  const allowed = /* @__PURE__ */ new Set(["action_type"]);
5050
5249
  const fields = profile?.boundsSchema?.fields;
5051
5250
  if (fields) {
@@ -5063,7 +5262,7 @@ function wireExecutionContext(profileId, execution) {
5063
5262
  return out;
5064
5263
  }
5065
5264
  function countBoundsFor(profileId, actionType) {
5066
- const fields = getProfile3(profileId)?.boundsSchema?.fields;
5265
+ const fields = getProfile4(profileId)?.boundsSchema?.fields;
5067
5266
  if (!fields) return null;
5068
5267
  return Object.entries(fields).filter(([, def]) => def.boundType?.kind === "cumulative_count").filter(([name, def]) => {
5069
5268
  const applies = boundActionTypes2(name, def);
@@ -5138,7 +5337,7 @@ function recordBlocked(state2, tool, rec) {
5138
5337
  function recordGatekeeperRefusal(state2, tool, refusals) {
5139
5338
  if (refusals.length === 0) return;
5140
5339
  const pick = refusals.find((r) => r.bound !== void 0 && r.actual !== void 0) ?? refusals[0];
5141
- const scoped = pick.field ? Boolean(getProfile3(pick.profileId)?.scopeSchema?.fields?.[pick.field]) : false;
5340
+ const scoped = pick.field ? Boolean(getProfile4(pick.profileId)?.scopeSchema?.fields?.[pick.field]) : false;
5142
5341
  const asValueOrString = (v) => typeof v === "number" || typeof v === "string" ? v : void 0;
5143
5342
  recordBlocked(state2, tool, {
5144
5343
  kind: scoped ? "scope" : "bound",
@@ -5238,7 +5437,7 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
5238
5437
  };
5239
5438
  };
5240
5439
  }
5241
- const { profile, executionMapping, staticExecution, category } = tool.gating;
5440
+ const { profile, executionMapping, staticExecution, category, boundField: toolBoundField, requiredValue: toolRequiredValue } = tool.gating;
5242
5441
  const eligibleMandates = () => {
5243
5442
  const refusals = [];
5244
5443
  const matching = state2.getEnrichedAuthorizations().filter((a) => a.complete && profileMatches(a.profileId, profile)).filter((a) => {
@@ -5317,7 +5516,7 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
5317
5516
  );
5318
5517
  }
5319
5518
  }
5320
- const readProfile = readAdapter ? getProfile3(matchingAuths[0].profileId) : void 0;
5519
+ const readProfile = readAdapter ? getProfile4(matchingAuths[0].profileId) : void 0;
5321
5520
  const boundsSchema = readProfile?.boundsSchema;
5322
5521
  const ageBoundField = readAdapter?.ageField ? resolveAgeBoundField(boundsSchema, readAdapter.ageField) : null;
5323
5522
  const hasAgeDimension = Boolean(readAdapter?.ageField);
@@ -5481,7 +5680,7 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
5481
5680
  };
5482
5681
  }
5483
5682
  {
5484
- const profileDef = getProfile3(profile);
5683
+ const profileDef = getProfile4(profile);
5485
5684
  const registry = profileDef?.boundsSchema?.actionTypes;
5486
5685
  if (Array.isArray(registry) && registry.length > 0 && !registry.includes(declaredActionType)) {
5487
5686
  recordBlocked(state2, tool, {
@@ -5519,6 +5718,25 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
5519
5718
  const passers = [];
5520
5719
  const structuredRefusals = [];
5521
5720
  for (const candidate of matchingAuths) {
5721
+ if (toolBoundField) {
5722
+ const gateOk = boundsSatisfyReadGate(
5723
+ candidate.bounds ?? candidate.frame,
5724
+ { boundField: toolBoundField, requiredValue: toolRequiredValue }
5725
+ );
5726
+ if (!gateOk) {
5727
+ errors.push(
5728
+ `${candidate.path}: requires "${toolBoundField}: ${toolRequiredValue}", but this authorization does not grant it`
5729
+ );
5730
+ structuredRefusals.push({
5731
+ authorizationId: candidate.authorizationId,
5732
+ profileId: candidate.profileId,
5733
+ code: "BOUND_EXCEEDED",
5734
+ field: toolBoundField,
5735
+ bound: toolRequiredValue
5736
+ });
5737
+ continue;
5738
+ }
5739
+ }
5522
5740
  const { result } = await state2.gatekeeper.verifyExecution(candidate.authorizationId, execution, {
5523
5741
  bounds: candidate.bounds,
5524
5742
  context: candidate.context
@@ -5566,7 +5784,7 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
5566
5784
  isError: true
5567
5785
  };
5568
5786
  }
5569
- const contextKeys = getProfile3(passers[0].profileId)?.scopeSchema?.keyOrder ?? [];
5787
+ const contextKeys = getProfile4(passers[0].profileId)?.scopeSchema?.keyOrder ?? [];
5570
5788
  return attemptWithCandidates(passers);
5571
5789
  async function attemptWithCandidates(candidates) {
5572
5790
  const selection = selectAuthorization(
@@ -6026,7 +6244,7 @@ function buildMandateBrief(opts) {
6026
6244
  const parts = [];
6027
6245
  if (boundsDesc) parts.push(boundsDesc);
6028
6246
  if (executionLog) {
6029
- const profile = getProfile4(auth.profileId) ?? getProfile4(shortName2);
6247
+ const profile = getProfile5(auth.profileId) ?? getProfile5(shortName2);
6030
6248
  const consumption = getConsumptionState(auth, executionLog, profile);
6031
6249
  const compact = formatConsumptionCompact(consumption);
6032
6250
  if (compact) parts.push(compact);
@@ -6064,7 +6282,7 @@ function buildMandateBrief(opts) {
6064
6282
  }
6065
6283
 
6066
6284
  // src/tools/authorizations.ts
6067
- import { getProfile as getProfile5 } from "@hap/core";
6285
+ import { getProfile as getProfile6 } from "@hap/core";
6068
6286
  function shortProfileName2(profileId) {
6069
6287
  const withoutVersion = profileId.replace(/@.*$/, "");
6070
6288
  const parts = withoutVersion.split("/");
@@ -6182,7 +6400,7 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
6182
6400
  } catch {
6183
6401
  }
6184
6402
  const shortName2 = shortProfileName2(auth.profileId);
6185
- const profile = getProfile5(auth.profileId) ?? getProfile5(shortName2);
6403
+ const profile = getProfile6(auth.profileId) ?? getProfile6(shortName2);
6186
6404
  if (profile) {
6187
6405
  const consumption = getConsumptionState(auth, state2.executionLog, profile);
6188
6406
  const consumptionText = formatConsumptionFull(consumption);
@@ -6242,7 +6460,7 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
6242
6460
  lines.push(" Mode: review (each action requires your approval)");
6243
6461
  }
6244
6462
  const shortName2 = shortProfileName2(auth.profileId);
6245
- const profile = getProfile5(auth.profileId) ?? getProfile5(shortName2);
6463
+ const profile = getProfile6(auth.profileId) ?? getProfile6(shortName2);
6246
6464
  if (profile) {
6247
6465
  const consumption = getConsumptionState(auth, state2.executionLog, profile);
6248
6466
  const compact = formatConsumptionCompact(consumption);
@@ -6435,6 +6653,18 @@ async function decryptRejectionNote(by) {
6435
6653
  }
6436
6654
 
6437
6655
  // src/tools/commitments.ts
6656
+ function recordExecutionOutcome(state2, proposalId, ticketId, ok, text, structuredContent) {
6657
+ try {
6658
+ state2.executionResults?.record({ proposalId, ticketId, ok, text, structuredContent });
6659
+ } catch (err) {
6660
+ console.error(`[Suveren MCP] Could not record the execution result for proposal ${proposalId}:`, err);
6661
+ }
6662
+ }
6663
+ function logExecutionOutcome(proposalId, ok, shortMessage) {
6664
+ console.error(
6665
+ `[Suveren MCP] Proposal ${proposalId}: execution outcome=${ok ? "ok" : `error (${shortMessage ?? "unknown"})`}`
6666
+ );
6667
+ }
6438
6668
  function buildSkippedProposalNote(proposalId) {
6439
6669
  return `Proposal ${proposalId} was approved, but was submitted from another device/installation of this operator (or one whose local record is gone) \u2014 this gateway will not execute it. Re-run the same request here to execute it from this gateway.`;
6440
6670
  }
@@ -6701,12 +6931,19 @@ async function executeCommitted(proposal, state2, integrationManager2) {
6701
6931
  try {
6702
6932
  result = await integrationManager2.callTool(integrationId, toolName, outgoingArgs);
6703
6933
  } catch (err) {
6934
+ const msg = err instanceof Error ? err.message : String(err);
6704
6935
  state2.executionJournal.complete(receiptId, "failed");
6936
+ recordExecutionOutcome(state2, proposal.id, receiptId, false, `The tool call threw: ${msg}`);
6937
+ logExecutionOutcome(proposal.id, false, err instanceof Error ? err.name : "thrown error");
6938
+ void notifyControlPlane("execution-failed");
6705
6939
  throw err;
6706
6940
  }
6707
6941
  const resultText = result.content?.[0]?.text ?? JSON.stringify(result);
6708
6942
  if (result.isError) {
6709
6943
  state2.executionJournal.complete(receiptId, "failed", "refused", resultText);
6944
+ recordExecutionOutcome(state2, proposal.id, receiptId, false, resultText);
6945
+ logExecutionOutcome(proposal.id, false, "the connector returned an error");
6946
+ void notifyControlPlane("execution-failed");
6710
6947
  return {
6711
6948
  text: `Proposal ${proposal.id} was approved, but ${integrationId} refused to run it \u2014 nothing was done.
6712
6949
  Reason: ${resultText}`,
@@ -6714,6 +6951,8 @@ Reason: ${resultText}`,
6714
6951
  };
6715
6952
  }
6716
6953
  state2.executionJournal.complete(receiptId, "done");
6954
+ recordExecutionOutcome(state2, proposal.id, receiptId, true, resultText, result.structuredContent);
6955
+ logExecutionOutcome(proposal.id, true);
6717
6956
  state2.executionLog.record({
6718
6957
  profileId: proposal.profileId,
6719
6958
  path: proposal.path,
@@ -6724,6 +6963,12 @@ Reason: ${resultText}`,
6724
6963
  Result: ${resultText}` };
6725
6964
  } catch (err) {
6726
6965
  const msg = err instanceof Error ? err.message : String(err);
6966
+ if (state2.executionJournal.get(receiptId)?.state === "intent") {
6967
+ state2.executionJournal.complete(receiptId, "failed");
6968
+ recordExecutionOutcome(state2, proposal.id, receiptId, false, `Receipt issued but tool execution failed: ${msg}`);
6969
+ logExecutionOutcome(proposal.id, false, err instanceof Error ? err.name : "error");
6970
+ void notifyControlPlane("execution-failed");
6971
+ }
6727
6972
  return {
6728
6973
  text: `Proposal ${proposal.id} receipt issued but tool execution failed: ${msg}`,
6729
6974
  isError: true
@@ -6732,11 +6977,22 @@ Result: ${resultText}` };
6732
6977
  }
6733
6978
  function localOutcomeOfExecuted(proposal, state2) {
6734
6979
  const entry = state2.executionJournal?.findByProposal?.(proposal.id);
6735
- if (!entry || entry.state === "done") return null;
6980
+ if (!entry) return null;
6736
6981
  const integration = proposal.tool.split("__")[0];
6982
+ if (entry.state === "done") {
6983
+ const stored = state2.executionResults?.get(proposal.id);
6984
+ if (!stored || !stored.ok) return null;
6985
+ return {
6986
+ text: `Proposal ${proposal.id} was approved and executed.
6987
+ Result: ${stored.text}`,
6988
+ isError: false
6989
+ };
6990
+ }
6737
6991
  if (entry.state === "failed") {
6738
- const reasonSuffix = entry.detail ? `
6739
- Reason: ${entry.detail}` : "";
6992
+ const stored = state2.executionResults?.get(proposal.id);
6993
+ const detail = (stored && !stored.ok ? stored.text : void 0) ?? entry.detail;
6994
+ const reasonSuffix = detail ? `
6995
+ Reason: ${detail}` : "";
6740
6996
  return {
6741
6997
  text: entry.outcome === "as_refused" ? `Proposal ${proposal.id} was approved, but the Authority Server refused its ticket \u2014 nothing ran. Do not retry the same values; ask the person (e.g. for a higher limit).${reasonSuffix}` : entry.outcome === "refused" ? `Proposal ${proposal.id} was approved, but ${integration} refused to run it \u2014 nothing was done. Do not call the tool again with the same values; a new attempt needs a new request and approval.${reasonSuffix}` : entry.outcome === "changed" ? `Proposal ${proposal.id} was approved, but the record changed in ${integration} since it was requested \u2014 nothing was done. A new attempt needs a new request and approval.${reasonSuffix}` : `Proposal ${proposal.id} was approved, but running it failed \u2014 whether anything happened in ${integration} is unknown. Check ${integration} before trying again; a new attempt needs a new request and approval.${reasonSuffix}`,
6742
6998
  isError: true
@@ -7083,21 +7339,21 @@ function createMcpServer(state2, integrationManager2) {
7083
7339
  }
7084
7340
 
7085
7341
  // src/lib/agent-contact.ts
7086
- import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync16, renameSync as renameSync5, writeFileSync as writeFileSync12 } from "fs";
7087
- import { dirname as dirname12, join as join15 } from "path";
7342
+ import { existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync17, renameSync as renameSync5, writeFileSync as writeFileSync13 } from "fs";
7343
+ import { dirname as dirname13, join as join16 } from "path";
7088
7344
  var FILE = "agent-contact.json";
7089
7345
  var REWRITE_AFTER_MS = 6e4;
7090
7346
  var AgentContactStore = class {
7091
7347
  path;
7092
7348
  current;
7093
7349
  constructor(dataDir2) {
7094
- this.path = join15(dataDir2, FILE);
7350
+ this.path = join16(dataDir2, FILE);
7095
7351
  this.current = this.load();
7096
7352
  }
7097
7353
  load() {
7098
- if (!existsSync15(this.path)) return null;
7354
+ if (!existsSync16(this.path)) return null;
7099
7355
  try {
7100
- const v = JSON.parse(readFileSync16(this.path, "utf8"));
7356
+ const v = JSON.parse(readFileSync17(this.path, "utf8"));
7101
7357
  if (typeof v.clientName !== "string" || typeof v.firstSeenAt !== "string" || typeof v.lastSeenAt !== "string") return null;
7102
7358
  return { clientName: v.clientName, clientVersion: v.clientVersion, firstSeenAt: v.firstSeenAt, lastSeenAt: v.lastSeenAt };
7103
7359
  } catch {
@@ -7120,9 +7376,9 @@ var AgentContactStore = class {
7120
7376
  };
7121
7377
  if (!due) return;
7122
7378
  try {
7123
- mkdirSync12(dirname12(this.path), { recursive: true });
7379
+ mkdirSync13(dirname13(this.path), { recursive: true });
7124
7380
  const tmp = `${this.path}.tmp`;
7125
- writeFileSync12(tmp, JSON.stringify(this.current, null, 2));
7381
+ writeFileSync13(tmp, JSON.stringify(this.current, null, 2));
7126
7382
  renameSync5(tmp, this.path);
7127
7383
  } catch (err) {
7128
7384
  console.error("[Suveren MCP] Could not save agent contact:", err);
@@ -7157,16 +7413,16 @@ function verifyGateContentHashes(content, auth) {
7157
7413
  }
7158
7414
 
7159
7415
  // src/lib/integration-registry.ts
7160
- import { mkdirSync as mkdirSync13, readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync16 } from "fs";
7161
- import { homedir as homedir11 } from "os";
7162
- import { dirname as dirname13, join as join16 } from "path";
7163
- var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join16(homedir11(), ".suveren");
7416
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync17 } from "fs";
7417
+ import { homedir as homedir12 } from "os";
7418
+ import { dirname as dirname14, join as join17 } from "path";
7419
+ var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join17(homedir12(), ".suveren");
7164
7420
  var IntegrationRegistry = class {
7165
7421
  integrations = /* @__PURE__ */ new Map();
7166
7422
  filePath;
7167
7423
  constructor(dataDir2) {
7168
7424
  const dir = dataDir2 ?? DEFAULT_DATA_DIR;
7169
- this.filePath = join16(dir, "integrations.json");
7425
+ this.filePath = join17(dir, "integrations.json");
7170
7426
  this.load();
7171
7427
  }
7172
7428
  // ─── CRUD ───────────────────────────────────────────────────────────────
@@ -7197,12 +7453,12 @@ var IntegrationRegistry = class {
7197
7453
  }
7198
7454
  // ─── Load / Save ────────────────────────────────────────────────────────
7199
7455
  load() {
7200
- if (!existsSync16(this.filePath)) {
7201
- mkdirSync13(dirname13(this.filePath), { recursive: true });
7456
+ if (!existsSync17(this.filePath)) {
7457
+ mkdirSync14(dirname14(this.filePath), { recursive: true });
7202
7458
  return;
7203
7459
  }
7204
7460
  try {
7205
- const raw = readFileSync17(this.filePath, "utf-8");
7461
+ const raw = readFileSync18(this.filePath, "utf-8");
7206
7462
  const data = JSON.parse(raw);
7207
7463
  const migrated = data.integrations.map((i) => {
7208
7464
  if ("toolGating" in i && !("profile" in i)) {
@@ -7231,15 +7487,15 @@ var IntegrationRegistry = class {
7231
7487
  version: 1,
7232
7488
  integrations: Array.from(this.integrations.values())
7233
7489
  };
7234
- mkdirSync13(dirname13(this.filePath), { recursive: true });
7235
- writeFileSync13(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
7490
+ mkdirSync14(dirname14(this.filePath), { recursive: true });
7491
+ writeFileSync14(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
7236
7492
  }
7237
7493
  };
7238
7494
 
7239
7495
  // src/lib/integration-manager.ts
7240
- import { homedir as homedir12 } from "os";
7241
- import { join as join17, delimiter } from "path";
7242
- import { existsSync as existsSync17, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14, readFileSync as readFileSync18, rmSync } from "fs";
7496
+ import { homedir as homedir13 } from "os";
7497
+ import { join as join18, delimiter } from "path";
7498
+ import { existsSync as existsSync18, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15, readFileSync as readFileSync19, rmSync } from "fs";
7243
7499
  import { execFile } from "child_process";
7244
7500
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7245
7501
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
@@ -7369,8 +7625,8 @@ function inheritedConnectorEnv(parent, passEnv = []) {
7369
7625
  }
7370
7626
 
7371
7627
  // src/lib/integration-manager.ts
7372
- var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join17(homedir12(), ".suveren");
7373
- var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join17(DEFAULT_DATA_DIR2, "integrations");
7628
+ var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join18(homedir13(), ".suveren");
7629
+ var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join18(DEFAULT_DATA_DIR2, "integrations");
7374
7630
  var OFFLINE_MODE = process.env.SUVEREN_OFFLINE === "1";
7375
7631
  var installLock = Promise.resolve();
7376
7632
  function withInstallLock(task) {
@@ -7378,7 +7634,7 @@ function withInstallLock(task) {
7378
7634
  installLock = run.then(() => void 0, () => void 0);
7379
7635
  return run;
7380
7636
  }
7381
- var INTEGRATIONS_BIN = join17(INTEGRATIONS_DIR, "node_modules", ".bin");
7637
+ var INTEGRATIONS_BIN = join18(INTEGRATIONS_DIR, "node_modules", ".bin");
7382
7638
  function getIntegrationsBinDir() {
7383
7639
  return INTEGRATIONS_BIN;
7384
7640
  }
@@ -7387,12 +7643,12 @@ function buildPath() {
7387
7643
  return [INTEGRATIONS_BIN, base].join(delimiter);
7388
7644
  }
7389
7645
  function ensureIntegrationsDir() {
7390
- if (!existsSync17(INTEGRATIONS_DIR)) {
7391
- mkdirSync14(INTEGRATIONS_DIR, { recursive: true });
7646
+ if (!existsSync18(INTEGRATIONS_DIR)) {
7647
+ mkdirSync15(INTEGRATIONS_DIR, { recursive: true });
7392
7648
  }
7393
- const pkgPath = join17(INTEGRATIONS_DIR, "package.json");
7394
- if (!existsSync17(pkgPath)) {
7395
- writeFileSync14(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
7649
+ const pkgPath = join18(INTEGRATIONS_DIR, "package.json");
7650
+ if (!existsSync18(pkgPath)) {
7651
+ writeFileSync15(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
7396
7652
  }
7397
7653
  }
7398
7654
  function readAgeOf(config) {
@@ -7481,19 +7737,19 @@ var IntegrationManager = class {
7481
7737
  * exist, and treat anything else as not-installed (and reinstallable).
7482
7738
  */
7483
7739
  isUsableInstall(npmPackage) {
7484
- const pkgDir = join17(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
7485
- const pkgJson = join17(pkgDir, "package.json");
7486
- if (!existsSync17(pkgDir) || !existsSync17(pkgJson)) return false;
7740
+ const pkgDir = join18(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
7741
+ const pkgJson = join18(pkgDir, "package.json");
7742
+ if (!existsSync18(pkgDir) || !existsSync18(pkgJson)) return false;
7487
7743
  try {
7488
- const pkg = JSON.parse(readFileSync18(pkgJson, "utf8"));
7744
+ const pkg = JSON.parse(readFileSync19(pkgJson, "utf8"));
7489
7745
  const defaultBinName = (pkg.name ?? npmPackage).split("/").pop() ?? "";
7490
7746
  const binMap = typeof pkg.bin === "string" ? { [defaultBinName]: pkg.bin } : pkg.bin ?? {};
7491
7747
  const binEntries = Object.entries(binMap);
7492
7748
  for (const [binName, target] of binEntries) {
7493
- if (!existsSync17(join17(pkgDir, target))) return false;
7749
+ if (!existsSync18(join18(pkgDir, target))) return false;
7494
7750
  if (!this.binShimExists(binName)) return false;
7495
7751
  }
7496
- if (binEntries.length === 0 && pkg.main && !existsSync17(join17(pkgDir, pkg.main))) return false;
7752
+ if (binEntries.length === 0 && pkg.main && !existsSync18(join18(pkgDir, pkg.main))) return false;
7497
7753
  return true;
7498
7754
  } catch {
7499
7755
  return false;
@@ -7508,9 +7764,9 @@ var IntegrationManager = class {
7508
7764
  */
7509
7765
  readInstalledVersion(npmPackage) {
7510
7766
  if (!this.isUsableInstall(npmPackage)) return null;
7511
- const pkgJson = join17(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"), "package.json");
7767
+ const pkgJson = join18(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"), "package.json");
7512
7768
  try {
7513
- const pkg = JSON.parse(readFileSync18(pkgJson, "utf8"));
7769
+ const pkg = JSON.parse(readFileSync19(pkgJson, "utf8"));
7514
7770
  return typeof pkg.version === "string" ? pkg.version : null;
7515
7771
  } catch {
7516
7772
  return null;
@@ -7524,7 +7780,7 @@ var IntegrationManager = class {
7524
7780
  binShimExists(binName) {
7525
7781
  if (!binName) return false;
7526
7782
  const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
7527
- return candidates.some((c) => existsSync17(join17(INTEGRATIONS_BIN, c)));
7783
+ return candidates.some((c) => existsSync18(join18(INTEGRATIONS_BIN, c)));
7528
7784
  }
7529
7785
  /**
7530
7786
  * The exact version THIS gateway's CURRENTLY LOADED manifest pins
@@ -7614,8 +7870,8 @@ var IntegrationManager = class {
7614
7870
  * nothing at all.
7615
7871
  */
7616
7872
  async installNow(npmPackage, exactVersion, cleanFirst = true) {
7617
- const pkgDir = join17(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
7618
- if (cleanFirst && existsSync17(pkgDir)) {
7873
+ const pkgDir = join18(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
7874
+ if (cleanFirst && existsSync18(pkgDir)) {
7619
7875
  console.error(`[IntegrationManager] Removing ${exactVersion ? "outdated" : "unusable"} install of ${npmPackage}`);
7620
7876
  try {
7621
7877
  rmSync(pkgDir, { recursive: true, force: true });
@@ -8204,7 +8460,18 @@ var IntegrationManager = class {
8204
8460
  simulationOnly: ext.simulationOnly,
8205
8461
  approvalView: ext.approvalView,
8206
8462
  contextKeys: ext.contextKeys,
8207
- sourceFromArg: ext.sourceFromArg
8463
+ sourceFromArg: ext.sourceFromArg,
8464
+ // A WRITE override may ALSO declare a capability-gate (enum boundType,
8465
+ // e.g. records.json's archive_access/delete_access) — the same
8466
+ // mechanism the read branch uses, now generalized: hap-core's
8467
+ // Gatekeeper deliberately never compares an `enum` bound against
8468
+ // execution (gatekeeper.ts's 'enum' case: "the gateway tool-proxy
8469
+ // gates tool calls ... before they reach the Gatekeeper"), so if this
8470
+ // branch dropped it, nothing anywhere would ever enforce it. Before
8471
+ // this, boundField/requiredValue on a non-"read" override was silently
8472
+ // discarded here — the config in the manifest existed but did nothing.
8473
+ boundField: ext.boundField,
8474
+ requiredValue: ext.requiredValue
8208
8475
  };
8209
8476
  }
8210
8477
  return {
@@ -8335,7 +8602,7 @@ async function buildTicketDetails(archive, ticketIds) {
8335
8602
  }
8336
8603
 
8337
8604
  // src/lib/report/window.ts
8338
- import { getProfile as getProfile6 } from "@hap/core";
8605
+ import { getProfile as getProfile7 } from "@hap/core";
8339
8606
  var REPORT_AGE_FIELD = "read_age_days";
8340
8607
  var REPORTING_PROFILE = "reporting";
8341
8608
  var END_SKEW_SECONDS = 120;
@@ -8349,7 +8616,7 @@ function windowLabel(start, days, loadedAt) {
8349
8616
  if (loadedAt !== null && loadedAt === start) return `${since} (when the test data was loaded)`;
8350
8617
  return `${since} (the last ${days} ${days === 1 ? "day" : "days"})`;
8351
8618
  }
8352
- var defaultLookup = (id) => getProfile6(id);
8619
+ var defaultLookup = (id) => getProfile7(id);
8353
8620
  function reportingMandateRefusal(m, lookup = defaultLookup) {
8354
8621
  const schema = lookup(m.profileId)?.boundsSchema;
8355
8622
  const field = resolveAgeBoundField(schema, REPORT_AGE_FIELD);
@@ -8559,28 +8826,28 @@ function agentAccess(token, opensSession) {
8559
8826
  }
8560
8827
 
8561
8828
  // src/lib/profile-loader.ts
8562
- import { readFileSync as readFileSync19, existsSync as existsSync18 } from "fs";
8563
- import { join as join18, resolve as resolve2 } from "path";
8829
+ import { readFileSync as readFileSync20, existsSync as existsSync19 } from "fs";
8830
+ import { join as join19, resolve as resolve2 } from "path";
8564
8831
  import { registerProfile } from "@hap/core";
8565
8832
  function loadProfiles(profilesDir) {
8566
- const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join18(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
8567
- const indexPath = join18(dir, "index.json");
8568
- if (!existsSync18(indexPath)) {
8833
+ const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join19(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
8834
+ const indexPath = join19(dir, "index.json");
8835
+ if (!existsSync19(indexPath)) {
8569
8836
  warnNoProfiles(`No index.json at ${indexPath}`);
8570
8837
  return 0;
8571
8838
  }
8572
8839
  let index;
8573
8840
  try {
8574
- index = JSON.parse(readFileSync19(indexPath, "utf-8"));
8841
+ index = JSON.parse(readFileSync20(indexPath, "utf-8"));
8575
8842
  } catch (err) {
8576
8843
  console.error(`[ProfileLoader] Failed to parse ${indexPath}:`, err);
8577
8844
  return 0;
8578
8845
  }
8579
8846
  let loaded = 0;
8580
8847
  for (const [profileId, relativePath] of Object.entries(index.profiles)) {
8581
- const profilePath = join18(dir, relativePath);
8848
+ const profilePath = join19(dir, relativePath);
8582
8849
  try {
8583
- const profile = JSON.parse(readFileSync19(profilePath, "utf-8"));
8850
+ const profile = JSON.parse(readFileSync20(profilePath, "utf-8"));
8584
8851
  registerProfile(profileId, profile);
8585
8852
  const shortName2 = profileId.split("/").pop()?.replace(/@.*$/, "");
8586
8853
  if (shortName2 && shortName2 !== profileId) {
@@ -8921,20 +9188,20 @@ async function controlPlaneMandate(dryRun, request) {
8921
9188
  }
8922
9189
 
8923
9190
  // src/lib/guides.ts
8924
- import { existsSync as existsSync19, readdirSync, readFileSync as readFileSync20 } from "fs";
8925
- import { dirname as dirname14, join as join19 } from "path";
9191
+ import { existsSync as existsSync20, readdirSync, readFileSync as readFileSync21 } from "fs";
9192
+ import { dirname as dirname15, join as join20 } from "path";
8926
9193
  var FILE_RE = /^(?:(\d+)-)?([a-z0-9][a-z0-9-]*)\.md$/;
8927
9194
  function builtinGuidesDir() {
8928
9195
  if (process.env.SUVEREN_BUILTIN_GUIDES_DIR) return process.env.SUVEREN_BUILTIN_GUIDES_DIR;
8929
9196
  let dir = import.meta.dirname ?? __dirname;
8930
9197
  for (let i = 0; i < 8; i++) {
8931
- const candidate = join19(dir, "content", "guides");
8932
- if (existsSync19(candidate)) return candidate;
8933
- const parent = dirname14(dir);
9198
+ const candidate = join20(dir, "content", "guides");
9199
+ if (existsSync20(candidate)) return candidate;
9200
+ const parent = dirname15(dir);
8934
9201
  if (parent === dir) break;
8935
9202
  dir = parent;
8936
9203
  }
8937
- return join19(import.meta.dirname ?? __dirname, "content", "guides");
9204
+ return join20(import.meta.dirname ?? __dirname, "content", "guides");
8938
9205
  }
8939
9206
  function parse(body) {
8940
9207
  const lines = body.split(/\r?\n/);
@@ -8945,14 +9212,14 @@ function parse(body) {
8945
9212
  return { title, summary };
8946
9213
  }
8947
9214
  function readDir(dir, source) {
8948
- if (!dir || !existsSync19(dir)) return [];
9215
+ if (!dir || !existsSync20(dir)) return [];
8949
9216
  const out = [];
8950
9217
  for (const file of readdirSync(dir)) {
8951
9218
  const m = FILE_RE.exec(file);
8952
9219
  if (!m) continue;
8953
9220
  let body;
8954
9221
  try {
8955
- body = readFileSync20(join19(dir, file), "utf8");
9222
+ body = readFileSync21(join20(dir, file), "utf8");
8956
9223
  } catch {
8957
9224
  continue;
8958
9225
  }
@@ -9087,7 +9354,7 @@ function unifiedLineDiff(before, after) {
9087
9354
  }
9088
9355
 
9089
9356
  // src/lib/builtins/setup.ts
9090
- import { getProfile as getProfile7 } from "@hap/core";
9357
+ import { getProfile as getProfile8 } from "@hap/core";
9091
9358
  var DELEGATION_PROFILE = "delegation";
9092
9359
  var DELEGATION_INTENT_HINT = [
9093
9360
  "Why \u2014 With test data only, I want to find out which routine parts of my work an AI could take over, so people gain time \u2014 before anything touches real systems.",
@@ -9139,7 +9406,7 @@ function delegationItselfRefusal(args) {
9139
9406
  }
9140
9407
  function delegationMandateRefusal(auth) {
9141
9408
  if (isSimulationMode()) return void 0;
9142
- const profile = getProfile7(auth.profileId);
9409
+ const profile = getProfile8(auth.profileId);
9143
9410
  if (profile?.scopeSchema?.fields?.allowed_profiles) return void 0;
9144
9411
  return `your Delegation mandate (${shortName(auth.profileId)} ${versionOf(auth.profileId)}) does not declare which profiles or modes you may propose \u2014 only a Delegation mandate on delegation 0.4 or later works outside simulation mode. Ask the person to give a current Delegation mandate.`;
9145
9412
  }
@@ -9197,7 +9464,7 @@ function delegationChecks(deps, args, targetShortName, targetDisplayName) {
9197
9464
  );
9198
9465
  const first = governing[0];
9199
9466
  if (first) {
9200
- const delegationProfile = getProfile7(first.profileId);
9467
+ const delegationProfile = getProfile8(first.profileId);
9201
9468
  const entry = getConsumptionState(first, deps.state.executionLog, delegationProfile).find((e) => e.field === "mandate_daily_max");
9202
9469
  if (entry && entry.limit !== null) {
9203
9470
  checks.push(`Proposals today: ${entry.current} of ${entry.limit}`);
@@ -9207,7 +9474,7 @@ function delegationChecks(deps, args, targetShortName, targetDisplayName) {
9207
9474
  }
9208
9475
  function resolveTargetProfile(input) {
9209
9476
  if (typeof input !== "string" || !input.trim()) return void 0;
9210
- const profile = getProfile7(input.trim());
9477
+ const profile = getProfile8(input.trim());
9211
9478
  if (!profile) return void 0;
9212
9479
  return { profile, shortName: shortName(profile.id) };
9213
9480
  }
@@ -9358,7 +9625,7 @@ Read each topic with get_guide(topic) before doing that step.`);
9358
9625
  return { ...builtinText(`Unknown topic "${topic}". Topics: ${guides.map((g) => g.topic).join(", ") || "none installed"}.`), isError: true };
9359
9626
  }
9360
9627
  const body = fillGenerated(guide.body.trim(), {
9361
- limits: () => limitsSection(systems.map((s) => ({ id: s.id, profile: getProfile7(s.profile) }))),
9628
+ limits: () => limitsSection(systems.map((s) => ({ id: s.id, profile: getProfile8(s.profile) }))),
9362
9629
  package: () => packageSection(
9363
9630
  deps.integrationManager.getAllTools().find((t) => !deps.integrationManager.isBuiltin(t.integrationId) && t.originalName === "load_simulation")
9364
9631
  )
@@ -9500,8 +9767,8 @@ function registerBuiltins(deps, factories = BUILTIN_FACTORIES) {
9500
9767
  import { decodeMandateBlob as decodeMandateBlob3 } from "@hap/core";
9501
9768
 
9502
9769
  // src/lib/committed-executor.ts
9503
- import { existsSync as existsSync20, mkdirSync as mkdirSync15, readFileSync as readFileSync21, unlinkSync as unlinkSync7, writeFileSync as writeFileSync15 } from "fs";
9504
- import { dirname as dirname15, join as join20 } from "path";
9770
+ import { existsSync as existsSync21, mkdirSync as mkdirSync16, readFileSync as readFileSync22, unlinkSync as unlinkSync8, writeFileSync as writeFileSync16 } from "fs";
9771
+ import { dirname as dirname16, join as join21 } from "path";
9505
9772
  function describeAutoExecutionLog(proposalId, tool, result) {
9506
9773
  if (result.skipped) return null;
9507
9774
  return result.isError ? `[Suveren MCP] Auto-exec proposal ${proposalId}: ${result.text}` : `[Suveren MCP] Auto-executed proposal ${proposalId}: ${tool}`;
@@ -9533,7 +9800,7 @@ var ExecutorLock = class {
9533
9800
  path;
9534
9801
  held = false;
9535
9802
  constructor(dataDir2) {
9536
- this.path = join20(dataDir2, "executor.lock");
9803
+ this.path = join21(dataDir2, "executor.lock");
9537
9804
  }
9538
9805
  /** Try to take the lock. Returns the holder's pid when someone else has it. */
9539
9806
  acquire() {
@@ -9541,9 +9808,9 @@ var ExecutorLock = class {
9541
9808
  if (current && current.pid !== process.pid && isAlive(current.pid)) {
9542
9809
  return { held: false, holderPid: current.pid };
9543
9810
  }
9544
- mkdirSync15(dirname15(this.path), { recursive: true, mode: 448 });
9811
+ mkdirSync16(dirname16(this.path), { recursive: true, mode: 448 });
9545
9812
  const lock = { pid: process.pid, startedAt: Math.floor(Date.now() / 1e3) };
9546
- writeFileSync15(this.path, JSON.stringify(lock), { mode: 384 });
9813
+ writeFileSync16(this.path, JSON.stringify(lock), { mode: 384 });
9547
9814
  this.held = true;
9548
9815
  return { held: true };
9549
9816
  }
@@ -9559,15 +9826,15 @@ var ExecutorLock = class {
9559
9826
  const current = this.read();
9560
9827
  if (current?.pid === process.pid) {
9561
9828
  try {
9562
- unlinkSync7(this.path);
9829
+ unlinkSync8(this.path);
9563
9830
  } catch {
9564
9831
  }
9565
9832
  }
9566
9833
  }
9567
9834
  read() {
9568
- if (!existsSync20(this.path)) return null;
9835
+ if (!existsSync21(this.path)) return null;
9569
9836
  try {
9570
- const parsed = JSON.parse(readFileSync21(this.path, "utf8"));
9837
+ const parsed = JSON.parse(readFileSync22(this.path, "utf8"));
9571
9838
  return typeof parsed?.pid === "number" ? parsed : null;
9572
9839
  } catch {
9573
9840
  return null;
@@ -9584,20 +9851,20 @@ function isAlive(pid) {
9584
9851
  }
9585
9852
 
9586
9853
  // bin/http.ts
9587
- import { homedir as homedir13 } from "os";
9588
- import { join as join22 } from "path";
9854
+ import { homedir as homedir14 } from "os";
9855
+ import { join as join23 } from "path";
9589
9856
 
9590
9857
  // src/lib/mcp-as-tracker.ts
9591
- import { existsSync as existsSync21, mkdirSync as mkdirSync16, readFileSync as readFileSync22, writeFileSync as writeFileSync16 } from "fs";
9592
- import { dirname as dirname16, join as join21 } from "path";
9858
+ import { existsSync as existsSync22, mkdirSync as mkdirSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "fs";
9859
+ import { dirname as dirname17, join as join22 } from "path";
9593
9860
  function trackerPath(dataDir2) {
9594
- return join21(dataDir2, "mcp-as-pairing.json");
9861
+ return join22(dataDir2, "mcp-as-pairing.json");
9595
9862
  }
9596
9863
  function readMcpPairedAsUrl(dataDir2) {
9597
9864
  const path = trackerPath(dataDir2);
9598
- if (!existsSync21(path)) return null;
9865
+ if (!existsSync22(path)) return null;
9599
9866
  try {
9600
- const data = JSON.parse(readFileSync22(path, "utf-8"));
9867
+ const data = JSON.parse(readFileSync23(path, "utf-8"));
9601
9868
  return typeof data.asUrl === "string" ? data.asUrl : null;
9602
9869
  } catch {
9603
9870
  return null;
@@ -9605,12 +9872,12 @@ function readMcpPairedAsUrl(dataDir2) {
9605
9872
  }
9606
9873
  function writeMcpPairedAsUrl(dataDir2, asUrl) {
9607
9874
  const path = trackerPath(dataDir2);
9608
- mkdirSync16(dirname16(path), { recursive: true, mode: 448 });
9609
- writeFileSync16(path, JSON.stringify({ asUrl }, null, 2), { encoding: "utf-8", mode: 384 });
9875
+ mkdirSync17(dirname17(path), { recursive: true, mode: 448 });
9876
+ writeFileSync17(path, JSON.stringify({ asUrl }, null, 2), { encoding: "utf-8", mode: 384 });
9610
9877
  }
9611
9878
 
9612
9879
  // bin/http.ts
9613
- var dataDir = process.env.SUVEREN_DATA_DIR ?? join22(homedir13(), ".suveren");
9880
+ var dataDir = process.env.SUVEREN_DATA_DIR ?? join23(homedir14(), ".suveren");
9614
9881
  var spUrl = resolveAsUrl(dataDir);
9615
9882
  validatePinTlsForUrl(resolvePinTls(dataDir), spUrl);
9616
9883
  var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
@@ -9696,7 +9963,8 @@ app.post("/internal/configure", internalOnly, (req, res) => {
9696
9963
  state.receiptArchive.setVaultKey(key);
9697
9964
  state.reportStore.setVaultKey(key);
9698
9965
  state.outcomeOutbox.setVaultKey(key);
9699
- console.error("[Suveren MCP] Vault key configured \u2014 gate store + denial log + receipt archive + report store + outcome outbox encryption active");
9966
+ state.executionResults.setVaultKey(key);
9967
+ console.error("[Suveren MCP] Vault key configured \u2014 gate store + denial log + receipt archive + report store + outcome outbox + execution results encryption active");
9700
9968
  }
9701
9969
  if (apiKey) {
9702
9970
  state.spClient.setApiKey(apiKey);
@@ -10364,7 +10632,7 @@ app.listen(port, bindHost, () => {
10364
10632
  (proposal) => executeCommitted(proposal, state, integrationManager)
10365
10633
  );
10366
10634
  installCommittedExecutor(committedExecutor);
10367
- const dataDir2 = process.env.SUVEREN_DATA_DIR ?? join22(homedir13(), ".suveren");
10635
+ const dataDir2 = process.env.SUVEREN_DATA_DIR ?? join23(homedir14(), ".suveren");
10368
10636
  const executorLock = new ExecutorLock(dataDir2);
10369
10637
  const lock = executorLock.acquire();
10370
10638
  if (!lock.held) {