@suveren/gateway 0.8.4 → 0.8.6

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.
Files changed (97) hide show
  1. package/dist/control-plane/index.mjs +170 -83
  2. package/dist/mcp-server/http.mjs +75 -0
  3. package/dist/ui/assets/index-BJqF5rmx.css +1 -0
  4. package/dist/ui/assets/index-DWpjjPIf.js +110 -0
  5. package/dist/ui/index.html +2 -2
  6. package/node_modules/@types/node/README.md +2 -2
  7. package/node_modules/@types/node/buffer.d.ts +1 -0
  8. package/node_modules/@types/node/child_process.d.ts +1 -1
  9. package/node_modules/@types/node/crypto.d.ts +20 -8
  10. package/node_modules/@types/node/diagnostics_channel.d.ts +4 -4
  11. package/node_modules/@types/node/ffi.d.ts +14 -0
  12. package/node_modules/@types/node/http.d.ts +1 -1
  13. package/node_modules/@types/node/http2.d.ts +3 -2
  14. package/node_modules/@types/node/net.d.ts +16 -4
  15. package/node_modules/@types/node/package.json +3 -18
  16. package/node_modules/@types/node/perf_hooks.d.ts +31 -19
  17. package/node_modules/@types/node/process.d.ts +1 -0
  18. package/node_modules/@types/node/quic.d.ts +45 -5
  19. package/node_modules/@types/node/stream/iter.d.ts +9 -12
  20. package/node_modules/@types/node/stream/web.d.ts +20 -0
  21. package/node_modules/@types/node/test/reporters.d.ts +1 -0
  22. package/node_modules/@types/node/test.d.ts +72 -0
  23. package/node_modules/@types/node/tls.d.ts +40 -25
  24. package/node_modules/@types/node/vfs.d.ts +7 -6
  25. package/node_modules/@types/node/vm.d.ts +2 -1
  26. package/node_modules/@types/node/web-globals/streams.d.ts +4 -0
  27. package/node_modules/@types/node/worker_threads.d.ts +4 -1
  28. package/node_modules/@types/node/zlib.d.ts +17 -0
  29. package/node_modules/body-parser/HISTORY.md +156 -145
  30. package/node_modules/body-parser/package.json +2 -2
  31. package/node_modules/express/History.md +583 -576
  32. package/node_modules/express/lib/request.js +2 -2
  33. package/node_modules/express/package.json +3 -3
  34. package/node_modules/fast-uri/index.js +10 -6
  35. package/node_modules/fast-uri/package.json +1 -1
  36. package/node_modules/fast-uri/test/security-normalization.test.js +38 -0
  37. package/node_modules/hono/README.md +1 -1
  38. package/node_modules/hono/dist/adapter/aws-lambda/handler.js +5 -4
  39. package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +5 -4
  40. package/node_modules/hono/dist/cjs/context.js +1 -1
  41. package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +1 -1
  42. package/node_modules/hono/dist/cjs/jsx/dom/render.js +44 -8
  43. package/node_modules/hono/dist/cjs/middleware/language/language.js +4 -1
  44. package/node_modules/hono/dist/cjs/request.js +4 -1
  45. package/node_modules/hono/dist/cjs/utils/accept.js +4 -7
  46. package/node_modules/hono/dist/context.js +1 -1
  47. package/node_modules/hono/dist/helper/accepts/accepts.js +1 -1
  48. package/node_modules/hono/dist/jsx/dom/render.js +44 -8
  49. package/node_modules/hono/dist/middleware/language/language.js +4 -1
  50. package/node_modules/hono/dist/request.js +4 -1
  51. package/node_modules/hono/dist/types/helper/proxy/index.d.ts +1 -1
  52. package/node_modules/hono/dist/types/helper/route/index.d.ts +2 -2
  53. package/node_modules/hono/dist/types/middleware/combine/index.d.ts +3 -1
  54. package/node_modules/hono/dist/types/utils/types.d.ts +1 -1
  55. package/node_modules/hono/dist/utils/accept.js +4 -7
  56. package/node_modules/hono/package.json +1 -1
  57. package/node_modules/ip-address/README.md +114 -114
  58. package/node_modules/ip-address/dist/common.d.ts +5 -0
  59. package/node_modules/ip-address/dist/common.js +12 -0
  60. package/node_modules/ip-address/dist/common.js.map +1 -1
  61. package/node_modules/ip-address/dist/ipv4.d.ts +10 -3
  62. package/node_modules/ip-address/dist/ipv4.js +20 -5
  63. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  64. package/node_modules/ip-address/dist/ipv6.d.ts +5 -1
  65. package/node_modules/ip-address/dist/ipv6.js +18 -3
  66. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  67. package/node_modules/ip-address/package.json +1 -1
  68. package/node_modules/proxy-addr/HISTORY.md +60 -54
  69. package/node_modules/proxy-addr/README.md +1 -1
  70. package/node_modules/proxy-addr/index.js +32 -0
  71. package/node_modules/proxy-addr/package.json +8 -4
  72. package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
  73. package/node_modules/qs/CHANGELOG.md +15 -0
  74. package/node_modules/qs/README.md +19 -1
  75. package/node_modules/qs/dist/qs.js +21 -21
  76. package/node_modules/qs/eslint.config.mjs +1 -1
  77. package/node_modules/qs/lib/parse.js +3 -4
  78. package/node_modules/qs/lib/stringify.js +23 -8
  79. package/node_modules/qs/lib/utils.js +10 -3
  80. package/node_modules/qs/package.json +4 -4
  81. package/node_modules/qs/test/parse.js +192 -3
  82. package/node_modules/qs/test/stringify.js +317 -0
  83. package/node_modules/qs/test/utils.js +12 -0
  84. package/node_modules/undici-types/client.d.ts +8 -3
  85. package/node_modules/undici-types/connector.d.ts +1 -0
  86. package/node_modules/undici-types/cookies.d.ts +1 -1
  87. package/node_modules/undici-types/dispatcher.d.ts +3 -3
  88. package/node_modules/undici-types/errors.d.ts +10 -0
  89. package/node_modules/undici-types/fetch.d.ts +5 -1
  90. package/node_modules/undici-types/handlers.d.ts +2 -0
  91. package/node_modules/undici-types/interceptors.d.ts +1 -1
  92. package/node_modules/undici-types/mock-interceptor.d.ts +5 -1
  93. package/node_modules/undici-types/package.json +1 -1
  94. package/node_modules/undici-types/proxy-agent.d.ts +7 -0
  95. package/package.json +1 -1
  96. package/dist/ui/assets/index-BYVb_0oG.css +0 -1
  97. package/dist/ui/assets/index-Z0OQ-dd1.js +0 -110
@@ -5,9 +5,9 @@ import {
5
5
 
6
6
  // src/index.ts
7
7
  import { randomBytes as randomBytes2 } from "crypto";
8
- import { existsSync as existsSync6, readFileSync as readFileSync5, renameSync } from "fs";
9
- import { dirname as dirname5, join as join6 } from "path";
10
- import { homedir as homedir5 } from "os";
8
+ import { existsSync as existsSync7, readFileSync as readFileSync7, renameSync as renameSync2 } from "fs";
9
+ import { dirname as dirname6, join as join8 } from "path";
10
+ import { homedir as homedir7 } from "os";
11
11
  import express from "express";
12
12
  import { createProxyMiddleware } from "http-proxy-middleware";
13
13
 
@@ -1042,6 +1042,8 @@ A good Intent can describe:
1042
1042
  - What the agent should try to achieve (the goal).
1043
1043
  - Soft rules or watch-outs the agent must honor even inside bounds (e.g. "never publish on weekends", "only reply in German").
1044
1044
 
1045
+ If an Agent Brief is provided, it is the user's standing orders that every agent already receives before any Intent. Use it to answer the user's questions and to keep the Intent consistent with it; do not repeat rules the brief already states \u2014 the Intent should add what is specific to this authorization.
1046
+
1045
1047
  Your role is to coach: ask clarifying questions, reflect the user's words back, surface edge cases they haven't thought about. You may propose concrete phrasings on request.
1046
1048
 
1047
1049
  CRITICAL \u2014 ask before you draft.
@@ -1062,23 +1064,34 @@ Once they have answered, produce the draft inside a single fenced block:
1062
1064
 
1063
1065
  Only use the fence for full-document drafts the user can click "Apply". For partial suggestions, questions, comments, or reflections, write prose without the fence.`
1064
1066
  };
1067
+ function buildChatGrounding(target, currentText, agentBrief) {
1068
+ const parts = [];
1069
+ if (target.kind === "intent") {
1070
+ if (target.profileId) parts.push(`Profile: ${target.profileId}`);
1071
+ if (target.path) parts.push(`Path: ${target.path}`);
1072
+ if (target.bounds) parts.push(`Bounds: ${target.bounds}`);
1073
+ if (agentBrief?.trim()) {
1074
+ parts.push(
1075
+ `Agent Brief (standing orders every agent already receives; the Intent builds on this, it does not repeat it):
1076
+ """
1077
+ ${agentBrief.trim()}
1078
+ """`
1079
+ );
1080
+ }
1081
+ }
1082
+ parts.push(
1083
+ currentText.trim() ? `Current draft:
1084
+ """
1085
+ ${currentText}
1086
+ """` : "Current draft is empty."
1087
+ );
1088
+ return parts.join("\n");
1089
+ }
1065
1090
  async function getAIChatResponse(config, request) {
1066
1091
  const { getPromptOverride } = await import("./ai-prompts-store-5JISFV4Z.mjs");
1067
1092
  const override = await getPromptOverride(request.target.kind);
1068
1093
  const systemPrompt = override ?? CHAT_SYSTEM_PROMPTS[request.target.kind];
1069
- const groundingParts = [];
1070
- if (request.target.kind === "intent") {
1071
- if (request.target.profileId) groundingParts.push(`Profile: ${request.target.profileId}`);
1072
- if (request.target.path) groundingParts.push(`Path: ${request.target.path}`);
1073
- if (request.target.bounds) groundingParts.push(`Bounds: ${request.target.bounds}`);
1074
- }
1075
- groundingParts.push(
1076
- request.currentText.trim() ? `Current draft:
1077
- """
1078
- ${request.currentText}
1079
- """` : "Current draft is empty."
1080
- );
1081
- const grounding = groundingParts.join("\n");
1094
+ const grounding = buildChatGrounding(request.target, request.currentText, request.agentBrief);
1082
1095
  const messages = [
1083
1096
  { role: "system", content: systemPrompt },
1084
1097
  { role: "user", content: grounding },
@@ -1280,6 +1293,21 @@ async function testAIConnectivity(config) {
1280
1293
  }
1281
1294
  }
1282
1295
 
1296
+ // src/lib/agent-brief-store.ts
1297
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1298
+ import { homedir as homedir2 } from "os";
1299
+ import { join as join2 } from "path";
1300
+ var AGENT_CONTEXT_MAX_BYTES = 16 * 1024;
1301
+ function agentBriefPath() {
1302
+ const dataDir = process.env.SUVEREN_DATA_DIR ?? join2(homedir2(), ".suveren");
1303
+ return join2(dataDir, "context.md");
1304
+ }
1305
+ function readAgentBrief() {
1306
+ const filePath = agentBriefPath();
1307
+ if (!existsSync2(filePath)) return "";
1308
+ return readFileSync2(filePath, "utf-8");
1309
+ }
1310
+
1283
1311
  // src/routes/ai.ts
1284
1312
  var describeScope = (c = {}) => {
1285
1313
  const vals = Object.values(c).map((v) => String(v).trim()).filter(Boolean);
@@ -1326,7 +1354,7 @@ function createAIRouter(vault2) {
1326
1354
  res.status(400).json({ error: "Missing messages array" });
1327
1355
  return;
1328
1356
  }
1329
- const result = await getAIChatResponse(config, request);
1357
+ const result = await getAIChatResponse(config, { ...request, agentBrief: readAgentBrief() });
1330
1358
  res.json(result);
1331
1359
  });
1332
1360
  router.post("/intent-review", async (req, res) => {
@@ -1641,28 +1669,28 @@ function createMCPRouter() {
1641
1669
  // src/routes/autostart.ts
1642
1670
  import { Router as Router6 } from "express";
1643
1671
  import { execFile } from "child_process";
1644
- import { existsSync as existsSync2 } from "fs";
1672
+ import { existsSync as existsSync3 } from "fs";
1645
1673
  import { dirname as pathDirname, resolve } from "path";
1646
1674
  import { fileURLToPath } from "url";
1647
1675
  import { promisify } from "util";
1648
1676
  var run = promisify(execFile);
1649
1677
  var SUPPORTED = /* @__PURE__ */ new Set(["darwin", "win32", "linux"]);
1650
1678
  var MODULE_DIR = pathDirname(fileURLToPath(import.meta.url));
1651
- function findCli(dirname6 = MODULE_DIR) {
1679
+ function findCli(dirname7 = MODULE_DIR) {
1652
1680
  const candidates = [
1653
1681
  // Bundled (the shipped layout): dist/control-plane → dist → root → bin
1654
- resolve(dirname6, "..", "..", "bin", "suveren-gateway.js"),
1682
+ resolve(dirname7, "..", "..", "bin", "suveren-gateway.js"),
1655
1683
  // Bundled, were the routes/ directory ever preserved.
1656
- resolve(dirname6, "..", "..", "..", "bin", "suveren-gateway.js"),
1684
+ resolve(dirname7, "..", "..", "..", "bin", "suveren-gateway.js"),
1657
1685
  // Dev, running from source: the repo's own bundle directory.
1658
- resolve(dirname6, "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js"),
1659
- resolve(dirname6, "..", "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js")
1686
+ resolve(dirname7, "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js"),
1687
+ resolve(dirname7, "..", "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js")
1660
1688
  // Deliberately NO process.cwd() fallback: it makes the result depend on
1661
1689
  // where the process happened to be launched from, so the same install
1662
1690
  // resolves differently between a shell and a login service — and it would
1663
1691
  // mask a genuinely missing CLI whenever the cwd happened to be the repo.
1664
1692
  ];
1665
- return candidates.find(existsSync2) ?? null;
1693
+ return candidates.find(existsSync3) ?? null;
1666
1694
  }
1667
1695
  async function cli(args) {
1668
1696
  const bin = findCli();
@@ -1743,15 +1771,15 @@ function createAutostartRouter() {
1743
1771
  import { spawn, spawnSync } from "child_process";
1744
1772
 
1745
1773
  // src/lib/notifier-icon.ts
1746
- import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
1747
- import { homedir as homedir2 } from "os";
1748
- import { join as join2, dirname as dirname2 } from "path";
1774
+ import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
1775
+ import { homedir as homedir3 } from "os";
1776
+ import { join as join3, dirname as dirname2 } from "path";
1749
1777
  var ICON_BASE64 = "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";
1750
1778
  function ensureNotifierIcon(dataDir) {
1751
1779
  try {
1752
- const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join2(homedir2(), ".suveren");
1753
- const path = join2(dir, "notifier-icon.png");
1754
- if (!existsSync3(path)) {
1780
+ const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join3(homedir3(), ".suveren");
1781
+ const path = join3(dir, "notifier-icon.png");
1782
+ if (!existsSync4(path)) {
1755
1783
  mkdirSync2(dirname2(path), { recursive: true });
1756
1784
  writeFileSync2(path, Buffer.from(ICON_BASE64, "base64"));
1757
1785
  }
@@ -1947,17 +1975,17 @@ import { Router as Router9 } from "express";
1947
1975
 
1948
1976
  // src/routes/approved-intents.ts
1949
1977
  import { Router as Router8 } from "express";
1950
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
1951
- import { homedir as homedir3 } from "os";
1952
- import { join as join3 } from "path";
1953
- var SUVEREN_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join3(homedir3(), ".suveren");
1954
- var FILE_PATH = join3(SUVEREN_DATA_DIR, "approved-intents.enc.json");
1978
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
1979
+ import { homedir as homedir4 } from "os";
1980
+ import { join as join4 } from "path";
1981
+ var SUVEREN_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join4(homedir4(), ".suveren");
1982
+ var FILE_PATH = join4(SUVEREN_DATA_DIR, "approved-intents.enc.json");
1955
1983
  function readFile() {
1956
- if (!existsSync4(FILE_PATH)) {
1984
+ if (!existsSync5(FILE_PATH)) {
1957
1985
  return { version: 1, entries: {} };
1958
1986
  }
1959
1987
  try {
1960
- return JSON.parse(readFileSync2(FILE_PATH, "utf-8"));
1988
+ return JSON.parse(readFileSync3(FILE_PATH, "utf-8"));
1961
1989
  } catch {
1962
1990
  return { version: 1, entries: {} };
1963
1991
  }
@@ -2366,19 +2394,19 @@ data: null
2366
2394
  import { Router as Router12 } from "express";
2367
2395
 
2368
2396
  // src/lib/gateway-settings.ts
2369
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
2370
- import { homedir as homedir4 } from "os";
2371
- import { join as join4, dirname as dirname4 } from "path";
2397
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
2398
+ import { homedir as homedir5 } from "os";
2399
+ import { join as join5, dirname as dirname4 } from "path";
2372
2400
  var DEFAULT_SETTINGS = {
2373
2401
  desktopNotifications: true
2374
2402
  };
2375
2403
  function settingsPath(dataDir) {
2376
- const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join4(homedir4(), ".suveren");
2377
- return join4(dir, "settings.json");
2404
+ const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join5(homedir5(), ".suveren");
2405
+ return join5(dir, "settings.json");
2378
2406
  }
2379
2407
  function readSettings(dataDir) {
2380
2408
  try {
2381
- const raw = JSON.parse(readFileSync3(settingsPath(dataDir), "utf8"));
2409
+ const raw = JSON.parse(readFileSync4(settingsPath(dataDir), "utf8"));
2382
2410
  return {
2383
2411
  desktopNotifications: typeof raw.desktopNotifications === "boolean" ? raw.desktopNotifications : DEFAULT_SETTINGS.desktopNotifications
2384
2412
  };
@@ -2421,8 +2449,73 @@ function createGatewaySettingsRouter() {
2421
2449
  return router;
2422
2450
  }
2423
2451
 
2424
- // src/routes/internal-events.ts
2452
+ // src/routes/archived-mandates.ts
2425
2453
  import { Router as Router13 } from "express";
2454
+
2455
+ // src/lib/archived-mandates-store.ts
2456
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, renameSync } from "fs";
2457
+ import { homedir as homedir6 } from "os";
2458
+ import { join as join6, dirname as dirname5 } from "path";
2459
+ var ID_PATTERN = /^[A-Za-z0-9_.:-]{1,200}$/;
2460
+ function isValidMandateId(id) {
2461
+ return typeof id === "string" && ID_PATTERN.test(id);
2462
+ }
2463
+ function storePath(dataDir) {
2464
+ const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join6(homedir6(), ".suveren");
2465
+ return join6(dir, "archived-mandates.json");
2466
+ }
2467
+ function readArchived(dataDir) {
2468
+ try {
2469
+ const raw = JSON.parse(readFileSync5(storePath(dataDir), "utf8"));
2470
+ if (!Array.isArray(raw.archived)) return [];
2471
+ return [...new Set(raw.archived.filter(isValidMandateId))];
2472
+ } catch {
2473
+ return [];
2474
+ }
2475
+ }
2476
+ function writeArchived(ids, dataDir) {
2477
+ const next = [...new Set(ids.filter(isValidMandateId))].sort();
2478
+ const path = storePath(dataDir);
2479
+ mkdirSync5(dirname5(path), { recursive: true });
2480
+ const tmp = `${path}.tmp`;
2481
+ writeFileSync5(tmp, JSON.stringify({ archived: next }, null, 2) + "\n", "utf8");
2482
+ renameSync(tmp, path);
2483
+ return next;
2484
+ }
2485
+ function archiveMandate(id, dataDir) {
2486
+ if (!isValidMandateId(id)) throw new Error("invalid mandate id");
2487
+ return writeArchived([...readArchived(dataDir), id], dataDir);
2488
+ }
2489
+ function unarchiveMandate(id, dataDir) {
2490
+ if (!isValidMandateId(id)) throw new Error("invalid mandate id");
2491
+ return writeArchived(readArchived(dataDir).filter((x) => x !== id), dataDir);
2492
+ }
2493
+
2494
+ // src/routes/archived-mandates.ts
2495
+ function createArchivedMandatesRouter(dataDir) {
2496
+ const router = Router13();
2497
+ router.get("/", (_req, res) => {
2498
+ res.json({ archived: readArchived(dataDir) });
2499
+ });
2500
+ const change = (fn) => (req, res) => {
2501
+ const id = req.params.id;
2502
+ if (!isValidMandateId(id)) {
2503
+ res.status(400).json({ error: "invalid mandate id" });
2504
+ return;
2505
+ }
2506
+ try {
2507
+ res.json({ archived: fn(id, dataDir) });
2508
+ } catch (err) {
2509
+ res.status(500).json({ error: `Could not save: ${err.message}` });
2510
+ }
2511
+ };
2512
+ router.put("/:id", change(archiveMandate));
2513
+ router.delete("/:id", change(unarchiveMandate));
2514
+ return router;
2515
+ }
2516
+
2517
+ // src/routes/internal-events.ts
2518
+ import { Router as Router14 } from "express";
2426
2519
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
2427
2520
  var ALLOWED = ["proposal-added", "action-approval-needed"];
2428
2521
  function secretMatches(provided, expected) {
@@ -2434,7 +2527,7 @@ function secretMatches(provided, expected) {
2434
2527
  return timingSafeEqual2(a, b);
2435
2528
  }
2436
2529
  function createInternalEventsRouter(getSecret) {
2437
- const router = Router13();
2530
+ const router = Router14();
2438
2531
  router.post("/event", (req, res) => {
2439
2532
  if (!secretMatches(req.headers["x-internal-secret"], getSecret())) {
2440
2533
  res.status(401).json({ error: "Unauthorized" });
@@ -2538,22 +2631,24 @@ function startNotificationDispatcher(opts = {}) {
2538
2631
  }
2539
2632
 
2540
2633
  // src/lib/denials-reader.ts
2541
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
2542
- import { join as join5 } from "path";
2634
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
2635
+ import { join as join7 } from "path";
2543
2636
  function loadDenials(dataDir, decrypt) {
2544
- const encPath = join5(dataDir, "denials.enc.json");
2545
- const plainPath = join5(dataDir, "denials.json");
2546
- if (existsSync5(encPath)) {
2547
- const { blob } = JSON.parse(readFileSync4(encPath, "utf-8"));
2637
+ const encPath = join7(dataDir, "denials.enc.json");
2638
+ const plainPath = join7(dataDir, "denials.json");
2639
+ if (existsSync6(encPath)) {
2640
+ const { blob } = JSON.parse(readFileSync6(encPath, "utf-8"));
2548
2641
  return JSON.parse(decrypt(blob)).records ?? [];
2549
2642
  }
2550
- if (existsSync5(plainPath)) {
2551
- return JSON.parse(readFileSync4(plainPath, "utf-8")).records ?? [];
2643
+ if (existsSync6(plainPath)) {
2644
+ return JSON.parse(readFileSync6(plainPath, "utf-8")).records ?? [];
2552
2645
  }
2553
2646
  return [];
2554
2647
  }
2648
+ var DENIAL_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
2555
2649
  function selectDenials(records, opts = {}) {
2556
- let out = [...records].sort((a, b) => b.ts - a.ts);
2650
+ const cutoff = (opts.now ?? Date.now()) - DENIAL_MAX_AGE_MS;
2651
+ let out = records.filter((r) => r.ts >= cutoff).sort((a, b) => b.ts - a.ts);
2557
2652
  if (opts.since !== void 0 && Number.isFinite(opts.since)) out = out.filter((r) => r.ts >= opts.since);
2558
2653
  const count = out.length;
2559
2654
  if (opts.limit !== void 0 && Number.isFinite(opts.limit) && opts.limit > 0) out = out.slice(0, opts.limit);
@@ -2563,11 +2658,11 @@ function selectDenials(records, opts = {}) {
2563
2658
  // src/index.ts
2564
2659
  (function migrateDataDir() {
2565
2660
  if (process.env.SUVEREN_DATA_DIR) return;
2566
- const oldDir = join6(homedir5(), ".hap");
2567
- const newDir = join6(homedir5(), ".suveren");
2568
- if (!existsSync6(newDir) && existsSync6(oldDir)) {
2661
+ const oldDir = join8(homedir7(), ".hap");
2662
+ const newDir = join8(homedir7(), ".suveren");
2663
+ if (!existsSync7(newDir) && existsSync7(oldDir)) {
2569
2664
  try {
2570
- renameSync(oldDir, newDir);
2665
+ renameSync2(oldDir, newDir);
2571
2666
  console.error(`[Control Plane] Migrated data directory: ${oldDir} \u2192 ${newDir}`);
2572
2667
  } catch (err) {
2573
2668
  console.error(`[Control Plane] FATAL: could not migrate data directory ${oldDir} \u2192 ${newDir}:`, err);
@@ -2578,7 +2673,7 @@ function selectDenials(records, opts = {}) {
2578
2673
  var SP_URL2 = process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai";
2579
2674
  var port = parseInt(process.env.SUVEREN_CP_PORT ?? "3402", 10);
2580
2675
  var HAP_MODE = process.env.HAP_MODE ?? "personal";
2581
- var UI_DIST = process.env.HAP_UI_DIST ?? join6(import.meta.dirname ?? __dirname, "../../ui/dist");
2676
+ var UI_DIST = process.env.HAP_UI_DIST ?? join8(import.meta.dirname ?? __dirname, "../../ui/dist");
2582
2677
  var vault = new Vault();
2583
2678
  var internalSecret2 = process.env.SUVEREN_INTERNAL_SECRET ?? randomBytes2(32).toString("hex");
2584
2679
  setInternalSecret(internalSecret2);
@@ -2815,6 +2910,7 @@ app.use("/ai-prompts", jsonParser, authGuard, createAIPromptsRouter());
2815
2910
  app.use("/mcp", jsonParser, authGuard, createMCPRouter());
2816
2911
  app.use("/autostart", jsonParser, authGuard, createAutostartRouter());
2817
2912
  app.use("/api/gateway-settings", jsonParser, authGuard, createGatewaySettingsRouter());
2913
+ app.use("/api/archived-mandates", jsonParser, authGuard, createArchivedMandatesRouter());
2818
2914
  app.use("/api/encrypt-intent", jsonParser, authGuard, createEncryptIntentRouter());
2819
2915
  app.use("/api/evidence-export", authGuard, createEvidenceExportRouter(SP_URL2, vault));
2820
2916
  app.use("/api/evidence", authGuard, createEvidenceRouter());
@@ -2948,7 +3044,7 @@ app.get("/active-authorizations", authGuard, async (_req, res) => {
2948
3044
  });
2949
3045
  app.get("/denials", authGuard, (req, res) => {
2950
3046
  try {
2951
- const dataDir = process.env.SUVEREN_DATA_DIR ?? join6(homedir5(), ".suveren");
3047
+ const dataDir = process.env.SUVEREN_DATA_DIR ?? join8(homedir7(), ".suveren");
2952
3048
  const all = loadDenials(dataDir, (blob) => vault.decrypt(blob));
2953
3049
  const since = Number(req.query.since);
2954
3050
  const limit = Number(req.query.limit);
@@ -2980,18 +3076,9 @@ app.post("/resync-gates", authGuard, async (_req, res) => {
2980
3076
  res.status(500).json({ error: "Failed to resync gates with MCP server" });
2981
3077
  }
2982
3078
  });
2983
- var AGENT_CONTEXT_MAX_BYTES = 16 * 1024;
2984
- var SUVEREN_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join6(homedir5(), ".suveren");
2985
3079
  app.get("/agent-brief/context", authGuard, async (_req, res) => {
2986
3080
  try {
2987
- const { readFileSync: readFileSync6, existsSync: fileExists } = await import("fs");
2988
- const filePath = join6(SUVEREN_DATA_DIR2, "context.md");
2989
- if (!fileExists(filePath)) {
2990
- res.json({ content: "" });
2991
- return;
2992
- }
2993
- const content = readFileSync6(filePath, "utf-8");
2994
- res.json({ content });
3081
+ res.json({ content: readAgentBrief() });
2995
3082
  } catch (err) {
2996
3083
  console.error("[Control Plane] agent-brief/context GET failed:", err);
2997
3084
  res.status(500).json({ error: err instanceof Error ? err.message : "Failed to read context" });
@@ -3008,12 +3095,12 @@ app.put("/agent-brief/context", jsonParser, authGuard, async (req, res) => {
3008
3095
  res.status(413).json({ error: `Context exceeds ${AGENT_CONTEXT_MAX_BYTES}-byte cap` });
3009
3096
  return;
3010
3097
  }
3011
- const { mkdirSync: mkdirSync5, writeFileSync: writeFileSync5, renameSync: renameSync2 } = await import("fs");
3012
- mkdirSync5(SUVEREN_DATA_DIR2, { recursive: true });
3013
- const filePath = join6(SUVEREN_DATA_DIR2, "context.md");
3098
+ const { mkdirSync: mkdirSync6, writeFileSync: writeFileSync6, renameSync: renameSync3 } = await import("fs");
3099
+ const filePath = agentBriefPath();
3100
+ mkdirSync6(dirname6(filePath), { recursive: true });
3014
3101
  const tmpPath = `${filePath}.tmp`;
3015
- writeFileSync5(tmpPath, content, "utf-8");
3016
- renameSync2(tmpPath, filePath);
3102
+ writeFileSync6(tmpPath, content, "utf-8");
3103
+ renameSync3(tmpPath, filePath);
3017
3104
  res.json({ ok: true });
3018
3105
  } catch (err) {
3019
3106
  console.error("[Control Plane] agent-brief/context PUT failed:", err);
@@ -3062,13 +3149,13 @@ app.use(
3062
3149
  })
3063
3150
  );
3064
3151
  function detectInstallMethod() {
3065
- if (existsSync6("/.dockerenv")) return "docker";
3152
+ if (existsSync7("/.dockerenv")) return "docker";
3066
3153
  const dir = import.meta.dirname ?? __dirname;
3067
3154
  if (dir.includes("/node_modules/@suveren/gateway/")) return "npm";
3068
3155
  let cursor = dir;
3069
3156
  for (let i = 0; i < 8; i++) {
3070
- if (existsSync6(join6(cursor, ".git"))) return "dev";
3071
- const parent = dirname5(cursor);
3157
+ if (existsSync7(join8(cursor, ".git"))) return "dev";
3158
+ const parent = dirname6(cursor);
3072
3159
  if (parent === cursor) break;
3073
3160
  cursor = parent;
3074
3161
  }
@@ -3080,10 +3167,10 @@ function detectRunningVersion() {
3080
3167
  return process.env.HAP_BUILD_SHA ?? "dev";
3081
3168
  }
3082
3169
  const dir = import.meta.dirname ?? __dirname;
3083
- const bundlePkg = join6(dir, "..", "..", "package.json");
3084
- if (existsSync6(bundlePkg)) {
3170
+ const bundlePkg = join8(dir, "..", "..", "package.json");
3171
+ if (existsSync7(bundlePkg)) {
3085
3172
  try {
3086
- const pkg = JSON.parse(readFileSync5(bundlePkg, "utf8"));
3173
+ const pkg = JSON.parse(readFileSync7(bundlePkg, "utf8"));
3087
3174
  if (pkg.name === "@suveren/gateway" && typeof pkg.version === "string") {
3088
3175
  return pkg.version;
3089
3176
  }
@@ -3111,7 +3198,7 @@ app.get("/health", async (req, res) => {
3111
3198
  }
3112
3199
  });
3113
3200
  });
3114
- if (existsSync6(UI_DIST)) {
3201
+ if (existsSync7(UI_DIST)) {
3115
3202
  app.use(express.static(UI_DIST, {
3116
3203
  setHeaders: (res, filePath) => {
3117
3204
  if (filePath.endsWith("index.html")) {
@@ -3121,7 +3208,7 @@ if (existsSync6(UI_DIST)) {
3121
3208
  }));
3122
3209
  app.get("*", (_req, res) => {
3123
3210
  res.setHeader("Cache-Control", "no-store, must-revalidate");
3124
- res.sendFile(join6(UI_DIST, "index.html"));
3211
+ res.sendFile(join8(UI_DIST, "index.html"));
3125
3212
  });
3126
3213
  } else {
3127
3214
  app.get("/", (_req, res) => {
@@ -3464,6 +3464,76 @@ function getAllManifests() {
3464
3464
  return Array.from(manifests.values());
3465
3465
  }
3466
3466
 
3467
+ // src/lib/remote-auth-preflight.ts
3468
+ var AUTH_HEADER_RE = /^authorization:\s*(.+)$/i;
3469
+ function remotePreflightTarget(command, args) {
3470
+ const bin = command.split(/[\\/]/).pop() ?? command;
3471
+ if (bin !== "mcp-remote") return null;
3472
+ const url = args.find((a) => /^https?:\/\//i.test(a));
3473
+ if (!url) return null;
3474
+ for (let i = 0; i < args.length - 1; i++) {
3475
+ if (args[i] !== "--header") continue;
3476
+ const m = AUTH_HEADER_RE.exec(args[i + 1]);
3477
+ if (m) return { url, authorization: m[1].trim() };
3478
+ }
3479
+ return null;
3480
+ }
3481
+ function isBlankCredential(authorization) {
3482
+ return authorization.split(/\s+/).filter(Boolean).length < 2;
3483
+ }
3484
+ async function preflightRemoteAuth(target, fetchImpl = fetch, timeoutMs = 8e3) {
3485
+ const host = safeHost(target.url);
3486
+ if (isBlankCredential(target.authorization)) {
3487
+ return {
3488
+ ok: false,
3489
+ reason: `no credential configured for ${host} \u2014 the saved token is empty. Not started: the remote would open a browser login instead.`
3490
+ };
3491
+ }
3492
+ const ctrl = new AbortController();
3493
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
3494
+ try {
3495
+ const res = await fetchImpl(target.url, {
3496
+ method: "POST",
3497
+ headers: {
3498
+ Authorization: target.authorization,
3499
+ "Content-Type": "application/json",
3500
+ Accept: "application/json, text/event-stream"
3501
+ },
3502
+ body: JSON.stringify({
3503
+ jsonrpc: "2.0",
3504
+ id: 0,
3505
+ method: "initialize",
3506
+ params: {
3507
+ protocolVersion: "2025-03-26",
3508
+ capabilities: {},
3509
+ clientInfo: { name: "suveren-gateway-preflight", version: "0" }
3510
+ }
3511
+ }),
3512
+ signal: ctrl.signal
3513
+ });
3514
+ res.body?.cancel().catch(() => {
3515
+ });
3516
+ if (res.status === 401 || res.status === 403) {
3517
+ return {
3518
+ ok: false,
3519
+ reason: `${host} rejected the configured credential (HTTP ${res.status}). Not started: the remote would open a browser login instead. Re-enter the token under Integrations.`
3520
+ };
3521
+ }
3522
+ return { ok: true };
3523
+ } catch {
3524
+ return { ok: true };
3525
+ } finally {
3526
+ clearTimeout(timer);
3527
+ }
3528
+ }
3529
+ function safeHost(url) {
3530
+ try {
3531
+ return new URL(url).host;
3532
+ } catch {
3533
+ return url;
3534
+ }
3535
+ }
3536
+
3467
3537
  // src/lib/integration-manager.ts
3468
3538
  var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join10(homedir8(), ".suveren");
3469
3539
  var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join10(DEFAULT_DATA_DIR2, "integrations");
@@ -3707,6 +3777,11 @@ var IntegrationManager = class {
3707
3777
  const env = this.resolveEnvKeys(config);
3708
3778
  const interpolate = (s) => s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? process.env[name] ?? "");
3709
3779
  const args = config.args.map(interpolate);
3780
+ const remote = remotePreflightTarget(config.command, args);
3781
+ if (remote) {
3782
+ const verdict = await preflightRemoteAuth(remote);
3783
+ if (!verdict.ok) throw new Error(`${config.id}: ${verdict.reason}`);
3784
+ }
3710
3785
  const transport = new StdioClientTransport({
3711
3786
  command: config.command,
3712
3787
  args,