@suveren/gateway 0.19.2 → 0.19.3

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.
@@ -599,6 +599,13 @@ async function exportReport(gatewayVersion) {
599
599
  const filename = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? "suveren-report.html";
600
600
  return { html, filename };
601
601
  }
602
+ async function getAgentContact() {
603
+ const res = await fetch(`${MCP_BASE}/internal/agent-contact`, {
604
+ headers: internalHeaders()
605
+ });
606
+ if (!res.ok) throw new Error("Failed to fetch agent contact");
607
+ return res.json();
608
+ }
602
609
  async function getMcpHealth() {
603
610
  const res = await fetch(`${MCP_BASE}/health`);
604
611
  if (!res.ok) throw new Error("MCP server unreachable");
@@ -2870,6 +2877,13 @@ function createMCPRouter() {
2870
2877
  res.status(502).json({ error: "MCP server unreachable" });
2871
2878
  }
2872
2879
  });
2880
+ router.get("/agent-contact", async (_req, res) => {
2881
+ try {
2882
+ res.json(await getAgentContact());
2883
+ } catch {
2884
+ res.status(502).json({ error: "MCP server unreachable" });
2885
+ }
2886
+ });
2873
2887
  router.get("/integrations", async (_req, res) => {
2874
2888
  try {
2875
2889
  const data = await getIntegrations();
@@ -4158,6 +4158,13 @@ function buildMandateBrief(opts) {
4158
4158
  }
4159
4159
  lines.push("");
4160
4160
  }
4161
+ if (isSimulationMode()) {
4162
+ const canSetUp = active.some((a) => shortProfileName(a.profileId) === "delegation");
4163
+ lines.push("=== GETTING STARTED ===");
4164
+ lines.push("");
4165
+ lines.push(canSetUp ? "If the person asks how to start with Suveren or how to set up their work with it: call setup__get_guide first and follow its steps in order." : `If the person asks how to start with Suveren: they first create a "Delegation" mandate in the Suveren Gateway at ${uiUrl()}. With it you can read the setup guide and lead them through the setup.`);
4166
+ lines.push("");
4167
+ }
4161
4168
  lines.push("When you receive a task, call list-authorizations(domain) to load full details for the relevant domain.");
4162
4169
  return lines.join("\n");
4163
4170
  }
@@ -4947,6 +4954,54 @@ function createMcpServer(state2, integrationManager2) {
4947
4954
  return { server, gatekeeper: state2.gatekeeper, refreshTools, registerProxiedTools };
4948
4955
  }
4949
4956
 
4957
+ // src/lib/agent-contact.ts
4958
+ import { existsSync as existsSync12, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync4, writeFileSync as writeFileSync10 } from "fs";
4959
+ import { dirname as dirname10, join as join12 } from "path";
4960
+ var FILE = "agent-contact.json";
4961
+ var REWRITE_AFTER_MS = 6e4;
4962
+ var AgentContactStore = class {
4963
+ path;
4964
+ current;
4965
+ constructor(dataDir2) {
4966
+ this.path = join12(dataDir2, FILE);
4967
+ this.current = this.load();
4968
+ }
4969
+ load() {
4970
+ if (!existsSync12(this.path)) return null;
4971
+ try {
4972
+ const v = JSON.parse(readFileSync13(this.path, "utf8"));
4973
+ if (typeof v.clientName !== "string" || typeof v.firstSeenAt !== "string" || typeof v.lastSeenAt !== "string") return null;
4974
+ return { clientName: v.clientName, clientVersion: v.clientVersion, firstSeenAt: v.firstSeenAt, lastSeenAt: v.lastSeenAt };
4975
+ } catch {
4976
+ return null;
4977
+ }
4978
+ }
4979
+ read() {
4980
+ return this.current;
4981
+ }
4982
+ /** Record a completed MCP initialize. `now` is injectable for tests. */
4983
+ record(client, now = /* @__PURE__ */ new Date()) {
4984
+ const clientName = client?.name?.trim() || "Unknown AI client";
4985
+ const prev = this.current;
4986
+ const due = !prev || prev.clientName !== clientName || now.getTime() - Date.parse(prev.lastSeenAt) >= REWRITE_AFTER_MS;
4987
+ this.current = {
4988
+ clientName,
4989
+ clientVersion: client?.version,
4990
+ firstSeenAt: prev?.firstSeenAt ?? now.toISOString(),
4991
+ lastSeenAt: now.toISOString()
4992
+ };
4993
+ if (!due) return;
4994
+ try {
4995
+ mkdirSync10(dirname10(this.path), { recursive: true });
4996
+ const tmp = `${this.path}.tmp`;
4997
+ writeFileSync10(tmp, JSON.stringify(this.current, null, 2));
4998
+ renameSync4(tmp, this.path);
4999
+ } catch (err) {
5000
+ console.error("[Suveren MCP] Could not save agent contact:", err);
5001
+ }
5002
+ }
5003
+ };
5004
+
4950
5005
  // src/lib/gate-content.ts
4951
5006
  import { computeIntentHash } from "@hap/core";
4952
5007
  import { decodeAttestationBlob as decodeAttestationBlob2 } from "@hap/core";
@@ -4974,16 +5029,16 @@ function verifyGateContentHashes(content, auth) {
4974
5029
  }
4975
5030
 
4976
5031
  // src/lib/integration-registry.ts
4977
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync12 } from "fs";
5032
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync13 } from "fs";
4978
5033
  import { homedir as homedir9 } from "os";
4979
- import { dirname as dirname10, join as join12 } from "path";
4980
- var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join12(homedir9(), ".suveren");
5034
+ import { dirname as dirname11, join as join13 } from "path";
5035
+ var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join13(homedir9(), ".suveren");
4981
5036
  var IntegrationRegistry = class {
4982
5037
  integrations = /* @__PURE__ */ new Map();
4983
5038
  filePath;
4984
5039
  constructor(dataDir2) {
4985
5040
  const dir = dataDir2 ?? DEFAULT_DATA_DIR;
4986
- this.filePath = join12(dir, "integrations.json");
5041
+ this.filePath = join13(dir, "integrations.json");
4987
5042
  this.load();
4988
5043
  }
4989
5044
  // ─── CRUD ───────────────────────────────────────────────────────────────
@@ -5014,12 +5069,12 @@ var IntegrationRegistry = class {
5014
5069
  }
5015
5070
  // ─── Load / Save ────────────────────────────────────────────────────────
5016
5071
  load() {
5017
- if (!existsSync12(this.filePath)) {
5018
- mkdirSync10(dirname10(this.filePath), { recursive: true });
5072
+ if (!existsSync13(this.filePath)) {
5073
+ mkdirSync11(dirname11(this.filePath), { recursive: true });
5019
5074
  return;
5020
5075
  }
5021
5076
  try {
5022
- const raw = readFileSync13(this.filePath, "utf-8");
5077
+ const raw = readFileSync14(this.filePath, "utf-8");
5023
5078
  const data = JSON.parse(raw);
5024
5079
  const migrated = data.integrations.map((i) => {
5025
5080
  if ("toolGating" in i && !("profile" in i)) {
@@ -5048,23 +5103,23 @@ var IntegrationRegistry = class {
5048
5103
  version: 1,
5049
5104
  integrations: Array.from(this.integrations.values())
5050
5105
  };
5051
- mkdirSync10(dirname10(this.filePath), { recursive: true });
5052
- writeFileSync10(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
5106
+ mkdirSync11(dirname11(this.filePath), { recursive: true });
5107
+ writeFileSync11(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
5053
5108
  }
5054
5109
  };
5055
5110
 
5056
5111
  // src/lib/integration-manager.ts
5057
5112
  import { homedir as homedir10 } from "os";
5058
- import { join as join14, delimiter } from "path";
5059
- import { existsSync as existsSync14, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync15, rmSync } from "fs";
5113
+ import { join as join15, delimiter } from "path";
5114
+ import { existsSync as existsSync15, writeFileSync as writeFileSync12, mkdirSync as mkdirSync12, readFileSync as readFileSync16, rmSync } from "fs";
5060
5115
  import { execFile } from "child_process";
5061
5116
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5062
5117
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5063
5118
  import { getProfile as getProfile5 } from "@hap/core";
5064
5119
 
5065
5120
  // src/lib/manifest-loader.ts
5066
- import { readFileSync as readFileSync14, existsSync as existsSync13 } from "fs";
5067
- import { join as join13, resolve } from "path";
5121
+ import { readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
5122
+ import { join as join14, resolve } from "path";
5068
5123
  var EXACT_SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
5069
5124
  function isExactSemver(value) {
5070
5125
  return typeof value === "string" && EXACT_SEMVER_RE.test(value);
@@ -5082,25 +5137,25 @@ function invalidNpmPinReason(manifest) {
5082
5137
  var manifests = /* @__PURE__ */ new Map();
5083
5138
  function loadManifests(integrationsDir) {
5084
5139
  const dir = resolve(
5085
- integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join13(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
5140
+ integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join14(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
5086
5141
  );
5087
- const indexPath = join13(dir, "index.json");
5088
- if (!existsSync13(indexPath)) {
5142
+ const indexPath = join14(dir, "index.json");
5143
+ if (!existsSync14(indexPath)) {
5089
5144
  console.error(`[ManifestLoader] No index.json found at ${indexPath}, skipping manifest loading`);
5090
5145
  return 0;
5091
5146
  }
5092
5147
  let index;
5093
5148
  try {
5094
- index = JSON.parse(readFileSync14(indexPath, "utf-8"));
5149
+ index = JSON.parse(readFileSync15(indexPath, "utf-8"));
5095
5150
  } catch (err) {
5096
5151
  console.error(`[ManifestLoader] Failed to parse ${indexPath}:`, err);
5097
5152
  return 0;
5098
5153
  }
5099
5154
  let loaded = 0;
5100
5155
  for (const [id, relativePath] of Object.entries(index.integrations)) {
5101
- const manifestPath = join13(dir, relativePath);
5156
+ const manifestPath = join14(dir, relativePath);
5102
5157
  try {
5103
- const manifest = JSON.parse(readFileSync14(manifestPath, "utf-8"));
5158
+ const manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
5104
5159
  const pinReason = invalidNpmPinReason(manifest);
5105
5160
  if (pinReason) {
5106
5161
  console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${pinReason}`);
@@ -5247,8 +5302,8 @@ function inheritedConnectorEnv(parent, passEnv = []) {
5247
5302
  }
5248
5303
 
5249
5304
  // src/lib/integration-manager.ts
5250
- var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join14(homedir10(), ".suveren");
5251
- var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join14(DEFAULT_DATA_DIR2, "integrations");
5305
+ var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join15(homedir10(), ".suveren");
5306
+ var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join15(DEFAULT_DATA_DIR2, "integrations");
5252
5307
  var OFFLINE_MODE = process.env.SUVEREN_OFFLINE === "1";
5253
5308
  var installLock = Promise.resolve();
5254
5309
  function withInstallLock(task) {
@@ -5256,7 +5311,7 @@ function withInstallLock(task) {
5256
5311
  installLock = run.then(() => void 0, () => void 0);
5257
5312
  return run;
5258
5313
  }
5259
- var INTEGRATIONS_BIN = join14(INTEGRATIONS_DIR, "node_modules", ".bin");
5314
+ var INTEGRATIONS_BIN = join15(INTEGRATIONS_DIR, "node_modules", ".bin");
5260
5315
  function getIntegrationsBinDir() {
5261
5316
  return INTEGRATIONS_BIN;
5262
5317
  }
@@ -5265,12 +5320,12 @@ function buildPath() {
5265
5320
  return [INTEGRATIONS_BIN, base].join(delimiter);
5266
5321
  }
5267
5322
  function ensureIntegrationsDir() {
5268
- if (!existsSync14(INTEGRATIONS_DIR)) {
5269
- mkdirSync11(INTEGRATIONS_DIR, { recursive: true });
5323
+ if (!existsSync15(INTEGRATIONS_DIR)) {
5324
+ mkdirSync12(INTEGRATIONS_DIR, { recursive: true });
5270
5325
  }
5271
- const pkgPath = join14(INTEGRATIONS_DIR, "package.json");
5272
- if (!existsSync14(pkgPath)) {
5273
- writeFileSync11(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
5326
+ const pkgPath = join15(INTEGRATIONS_DIR, "package.json");
5327
+ if (!existsSync15(pkgPath)) {
5328
+ writeFileSync12(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
5274
5329
  }
5275
5330
  }
5276
5331
  function readAgeOf(config) {
@@ -5359,19 +5414,19 @@ var IntegrationManager = class {
5359
5414
  * exist, and treat anything else as not-installed (and reinstallable).
5360
5415
  */
5361
5416
  isUsableInstall(npmPackage) {
5362
- const pkgDir = join14(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
5363
- const pkgJson = join14(pkgDir, "package.json");
5364
- if (!existsSync14(pkgDir) || !existsSync14(pkgJson)) return false;
5417
+ const pkgDir = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
5418
+ const pkgJson = join15(pkgDir, "package.json");
5419
+ if (!existsSync15(pkgDir) || !existsSync15(pkgJson)) return false;
5365
5420
  try {
5366
- const pkg = JSON.parse(readFileSync15(pkgJson, "utf8"));
5421
+ const pkg = JSON.parse(readFileSync16(pkgJson, "utf8"));
5367
5422
  const defaultBinName = (pkg.name ?? npmPackage).split("/").pop() ?? "";
5368
5423
  const binMap = typeof pkg.bin === "string" ? { [defaultBinName]: pkg.bin } : pkg.bin ?? {};
5369
5424
  const binEntries = Object.entries(binMap);
5370
5425
  for (const [binName, target] of binEntries) {
5371
- if (!existsSync14(join14(pkgDir, target))) return false;
5426
+ if (!existsSync15(join15(pkgDir, target))) return false;
5372
5427
  if (!this.binShimExists(binName)) return false;
5373
5428
  }
5374
- if (binEntries.length === 0 && pkg.main && !existsSync14(join14(pkgDir, pkg.main))) return false;
5429
+ if (binEntries.length === 0 && pkg.main && !existsSync15(join15(pkgDir, pkg.main))) return false;
5375
5430
  return true;
5376
5431
  } catch {
5377
5432
  return false;
@@ -5386,9 +5441,9 @@ var IntegrationManager = class {
5386
5441
  */
5387
5442
  readInstalledVersion(npmPackage) {
5388
5443
  if (!this.isUsableInstall(npmPackage)) return null;
5389
- const pkgJson = join14(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"), "package.json");
5444
+ const pkgJson = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"), "package.json");
5390
5445
  try {
5391
- const pkg = JSON.parse(readFileSync15(pkgJson, "utf8"));
5446
+ const pkg = JSON.parse(readFileSync16(pkgJson, "utf8"));
5392
5447
  return typeof pkg.version === "string" ? pkg.version : null;
5393
5448
  } catch {
5394
5449
  return null;
@@ -5402,7 +5457,7 @@ var IntegrationManager = class {
5402
5457
  binShimExists(binName) {
5403
5458
  if (!binName) return false;
5404
5459
  const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
5405
- return candidates.some((c) => existsSync14(join14(INTEGRATIONS_BIN, c)));
5460
+ return candidates.some((c) => existsSync15(join15(INTEGRATIONS_BIN, c)));
5406
5461
  }
5407
5462
  /**
5408
5463
  * The exact version THIS gateway's CURRENTLY LOADED manifest pins
@@ -5492,8 +5547,8 @@ var IntegrationManager = class {
5492
5547
  * nothing at all.
5493
5548
  */
5494
5549
  async installNow(npmPackage, exactVersion, cleanFirst = true) {
5495
- const pkgDir = join14(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
5496
- if (cleanFirst && existsSync14(pkgDir)) {
5550
+ const pkgDir = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
5551
+ if (cleanFirst && existsSync15(pkgDir)) {
5497
5552
  console.error(`[IntegrationManager] Removing ${exactVersion ? "outdated" : "unusable"} install of ${npmPackage}`);
5498
5553
  try {
5499
5554
  rmSync(pkgDir, { recursive: true, force: true });
@@ -6398,28 +6453,28 @@ function agentAccess(token, opensSession) {
6398
6453
  }
6399
6454
 
6400
6455
  // src/lib/profile-loader.ts
6401
- import { readFileSync as readFileSync16, existsSync as existsSync15 } from "fs";
6402
- import { join as join15, resolve as resolve2 } from "path";
6456
+ import { readFileSync as readFileSync17, existsSync as existsSync16 } from "fs";
6457
+ import { join as join16, resolve as resolve2 } from "path";
6403
6458
  import { registerProfile } from "@hap/core";
6404
6459
  function loadProfiles(profilesDir) {
6405
- const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join15(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
6406
- const indexPath = join15(dir, "index.json");
6407
- if (!existsSync15(indexPath)) {
6460
+ const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join16(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
6461
+ const indexPath = join16(dir, "index.json");
6462
+ if (!existsSync16(indexPath)) {
6408
6463
  warnNoProfiles(`No index.json at ${indexPath}`);
6409
6464
  return 0;
6410
6465
  }
6411
6466
  let index;
6412
6467
  try {
6413
- index = JSON.parse(readFileSync16(indexPath, "utf-8"));
6468
+ index = JSON.parse(readFileSync17(indexPath, "utf-8"));
6414
6469
  } catch (err) {
6415
6470
  console.error(`[ProfileLoader] Failed to parse ${indexPath}:`, err);
6416
6471
  return 0;
6417
6472
  }
6418
6473
  let loaded = 0;
6419
6474
  for (const [profileId, relativePath] of Object.entries(index.profiles)) {
6420
- const profilePath = join15(dir, relativePath);
6475
+ const profilePath = join16(dir, relativePath);
6421
6476
  try {
6422
- const profile = JSON.parse(readFileSync16(profilePath, "utf-8"));
6477
+ const profile = JSON.parse(readFileSync17(profilePath, "utf-8"));
6423
6478
  registerProfile(profileId, profile);
6424
6479
  const shortName = profileId.split("/").pop()?.replace(/@.*$/, "");
6425
6480
  if (shortName && shortName !== profileId) {
@@ -6757,20 +6812,20 @@ async function controlPlaneMandate(dryRun, request) {
6757
6812
  }
6758
6813
 
6759
6814
  // src/lib/guides.ts
6760
- import { existsSync as existsSync16, readdirSync, readFileSync as readFileSync17 } from "fs";
6761
- import { dirname as dirname11, join as join16 } from "path";
6815
+ import { existsSync as existsSync17, readdirSync, readFileSync as readFileSync18 } from "fs";
6816
+ import { dirname as dirname12, join as join17 } from "path";
6762
6817
  var FILE_RE = /^(?:(\d+)-)?([a-z0-9][a-z0-9-]*)\.md$/;
6763
6818
  function builtinGuidesDir() {
6764
6819
  if (process.env.SUVEREN_BUILTIN_GUIDES_DIR) return process.env.SUVEREN_BUILTIN_GUIDES_DIR;
6765
6820
  let dir = import.meta.dirname ?? __dirname;
6766
6821
  for (let i = 0; i < 8; i++) {
6767
- const candidate = join16(dir, "content", "guides");
6768
- if (existsSync16(candidate)) return candidate;
6769
- const parent = dirname11(dir);
6822
+ const candidate = join17(dir, "content", "guides");
6823
+ if (existsSync17(candidate)) return candidate;
6824
+ const parent = dirname12(dir);
6770
6825
  if (parent === dir) break;
6771
6826
  dir = parent;
6772
6827
  }
6773
- return join16(import.meta.dirname ?? __dirname, "content", "guides");
6828
+ return join17(import.meta.dirname ?? __dirname, "content", "guides");
6774
6829
  }
6775
6830
  function parse(body) {
6776
6831
  const lines = body.split(/\r?\n/);
@@ -6781,14 +6836,14 @@ function parse(body) {
6781
6836
  return { title, summary };
6782
6837
  }
6783
6838
  function readDir(dir, source) {
6784
- if (!dir || !existsSync16(dir)) return [];
6839
+ if (!dir || !existsSync17(dir)) return [];
6785
6840
  const out = [];
6786
6841
  for (const file of readdirSync(dir)) {
6787
6842
  const m = FILE_RE.exec(file);
6788
6843
  if (!m) continue;
6789
6844
  let body;
6790
6845
  try {
6791
- body = readFileSync17(join16(dir, file), "utf8");
6846
+ body = readFileSync18(join17(dir, file), "utf8");
6792
6847
  } catch {
6793
6848
  continue;
6794
6849
  }
@@ -6824,6 +6879,8 @@ function guideHeader(systems) {
6824
6879
  lines.push(`- **${s.id}** \u2014 ${parts.join("; ")}`);
6825
6880
  }
6826
6881
  }
6882
+ lines.push("");
6883
+ lines.push(`**Approvals:** every mandate and brief you propose waits until the person approves it in the Suveren Gateway at ${uiUrl()}/approvals. Tell them each time you propose one.`);
6827
6884
  return lines.join("\n");
6828
6885
  }
6829
6886
 
@@ -6854,7 +6911,11 @@ function connectedSystems(im) {
6854
6911
  function setupBuiltin(deps) {
6855
6912
  return {
6856
6913
  id: "setup",
6857
- name: "Test setup",
6914
+ // Shown as the picker card title (apps/ui AuthorizePicker) — the person
6915
+ // reads this as the mandate they're giving, so it must match the profile
6916
+ // name they'll see everywhere else (Mandates page, dashboard first-run
6917
+ // card), not the internal "test setup" framing. Description is unchanged.
6918
+ name: "Delegation",
6858
6919
  description: "Your AI sets up a test of itself: it reads the setup guides and proposes its agent brief and its mandates. Every proposal waits for your approval. Simulation mode only.",
6859
6920
  profile: DELEGATION_PROFILE,
6860
6921
  simulation: true,
@@ -6893,7 +6954,7 @@ function setupBuiltin(deps) {
6893
6954
  tools: [
6894
6955
  {
6895
6956
  name: "get_guide",
6896
- description: "Simulation mode only: the setup guides \u2014 how to interview the person, build the test data, propose mandates and the agent brief, set up the working AI's process, and what can make a test misleading. Call without `topic` for the list and the order to follow; then read each topic before doing that step.",
6957
+ description: "Start here when the person asks how to begin with Suveren or how to set up their work with it. Simulation mode only: the setup guides \u2014 how to interview the person, build the test data, propose mandates and the agent brief, set up the working AI's process, and what can make a test misleading. Call without `topic` for the list and the order to follow; then read each topic before doing that step.",
6897
6958
  inputSchema: {
6898
6959
  type: "object",
6899
6960
  properties: {
@@ -7020,8 +7081,8 @@ function registerBuiltins(deps, factories = BUILTIN_FACTORIES) {
7020
7081
  import { decodeAttestationBlob as decodeAttestationBlob3 } from "@hap/core";
7021
7082
 
7022
7083
  // src/lib/committed-executor.ts
7023
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, readFileSync as readFileSync18, unlinkSync as unlinkSync6, writeFileSync as writeFileSync12 } from "fs";
7024
- import { dirname as dirname12, join as join17 } from "path";
7084
+ import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync13 } from "fs";
7085
+ import { dirname as dirname13, join as join18 } from "path";
7025
7086
  var CommittedExecutor = class {
7026
7087
  constructor(run) {
7027
7088
  this.run = run;
@@ -7049,7 +7110,7 @@ var ExecutorLock = class {
7049
7110
  path;
7050
7111
  held = false;
7051
7112
  constructor(dataDir2) {
7052
- this.path = join17(dataDir2, "executor.lock");
7113
+ this.path = join18(dataDir2, "executor.lock");
7053
7114
  }
7054
7115
  /** Try to take the lock. Returns the holder's pid when someone else has it. */
7055
7116
  acquire() {
@@ -7057,9 +7118,9 @@ var ExecutorLock = class {
7057
7118
  if (current && current.pid !== process.pid && isAlive(current.pid)) {
7058
7119
  return { held: false, holderPid: current.pid };
7059
7120
  }
7060
- mkdirSync12(dirname12(this.path), { recursive: true, mode: 448 });
7121
+ mkdirSync13(dirname13(this.path), { recursive: true, mode: 448 });
7061
7122
  const lock = { pid: process.pid, startedAt: Math.floor(Date.now() / 1e3) };
7062
- writeFileSync12(this.path, JSON.stringify(lock), { mode: 384 });
7123
+ writeFileSync13(this.path, JSON.stringify(lock), { mode: 384 });
7063
7124
  this.held = true;
7064
7125
  return { held: true };
7065
7126
  }
@@ -7081,9 +7142,9 @@ var ExecutorLock = class {
7081
7142
  }
7082
7143
  }
7083
7144
  read() {
7084
- if (!existsSync17(this.path)) return null;
7145
+ if (!existsSync18(this.path)) return null;
7085
7146
  try {
7086
- const parsed = JSON.parse(readFileSync18(this.path, "utf8"));
7147
+ const parsed = JSON.parse(readFileSync19(this.path, "utf8"));
7087
7148
  return typeof parsed?.pid === "number" ? parsed : null;
7088
7149
  } catch {
7089
7150
  return null;
@@ -7101,19 +7162,19 @@ function isAlive(pid) {
7101
7162
 
7102
7163
  // bin/http.ts
7103
7164
  import { homedir as homedir11 } from "os";
7104
- import { join as join19 } from "path";
7165
+ import { join as join20 } from "path";
7105
7166
 
7106
7167
  // src/lib/mcp-as-tracker.ts
7107
- import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
7108
- import { dirname as dirname13, join as join18 } from "path";
7168
+ import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
7169
+ import { dirname as dirname14, join as join19 } from "path";
7109
7170
  function trackerPath(dataDir2) {
7110
- return join18(dataDir2, "mcp-as-pairing.json");
7171
+ return join19(dataDir2, "mcp-as-pairing.json");
7111
7172
  }
7112
7173
  function readMcpPairedAsUrl(dataDir2) {
7113
7174
  const path = trackerPath(dataDir2);
7114
- if (!existsSync18(path)) return null;
7175
+ if (!existsSync19(path)) return null;
7115
7176
  try {
7116
- const data = JSON.parse(readFileSync19(path, "utf-8"));
7177
+ const data = JSON.parse(readFileSync20(path, "utf-8"));
7117
7178
  return typeof data.asUrl === "string" ? data.asUrl : null;
7118
7179
  } catch {
7119
7180
  return null;
@@ -7121,12 +7182,12 @@ function readMcpPairedAsUrl(dataDir2) {
7121
7182
  }
7122
7183
  function writeMcpPairedAsUrl(dataDir2, asUrl) {
7123
7184
  const path = trackerPath(dataDir2);
7124
- mkdirSync13(dirname13(path), { recursive: true, mode: 448 });
7125
- writeFileSync13(path, JSON.stringify({ asUrl }, null, 2), { encoding: "utf-8", mode: 384 });
7185
+ mkdirSync14(dirname14(path), { recursive: true, mode: 448 });
7186
+ writeFileSync14(path, JSON.stringify({ asUrl }, null, 2), { encoding: "utf-8", mode: 384 });
7126
7187
  }
7127
7188
 
7128
7189
  // bin/http.ts
7129
- var dataDir = process.env.SUVEREN_DATA_DIR ?? join19(homedir11(), ".suveren");
7190
+ var dataDir = process.env.SUVEREN_DATA_DIR ?? join20(homedir11(), ".suveren");
7130
7191
  var spUrl = resolveAsUrl(dataDir);
7131
7192
  validatePinTlsForUrl(resolvePinTls(dataDir), spUrl);
7132
7193
  var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
@@ -7139,6 +7200,7 @@ if (bindProblem) {
7139
7200
  }
7140
7201
  setAsBaseUrl(spUrl);
7141
7202
  var state = new SharedState(spUrl, void 0, dataDir);
7203
+ var agentContact = new AgentContactStore(dataDir);
7142
7204
  var reportSources = {
7143
7205
  archive: state.receiptArchive,
7144
7206
  runExport: createConnectorExportRunner({ integrationsBinDir: getIntegrationsBinDir(), dataDir })
@@ -7422,6 +7484,7 @@ var guardAgent = agentAccess(
7422
7484
  app.get("/sse", guardAgent, async (_req, res) => {
7423
7485
  const transport = new SSEServerTransport("/messages", res);
7424
7486
  const { server, refreshTools, registerProxiedTools } = createMcpServer(state, integrationManager);
7487
+ server.server.oninitialized = () => agentContact.record(server.server.getClientVersion());
7425
7488
  const sessionId = transport.sessionId;
7426
7489
  sseSessions.set(sessionId, transport);
7427
7490
  activeSessions.set(sessionId, { refreshTools, registerProxiedTools });
@@ -7463,6 +7526,7 @@ app.all("/mcp", guardAgent, async (req, res) => {
7463
7526
  }
7464
7527
  };
7465
7528
  const { server, refreshTools, registerProxiedTools } = createMcpServer(state, integrationManager);
7529
+ server.server.oninitialized = () => agentContact.record(server.server.getClientVersion());
7466
7530
  await server.connect(transport);
7467
7531
  await transport.handleRequest(req, res, req.body);
7468
7532
  if (transport.sessionId && !streamableSessions.has(transport.sessionId)) {
@@ -7493,6 +7557,9 @@ app.get("/health", (_req, res) => {
7493
7557
  builtins: integrationManager.getBuiltins()
7494
7558
  });
7495
7559
  });
7560
+ app.get("/internal/agent-contact", internalOnly, (_req, res) => {
7561
+ res.json({ contact: agentContact.read() });
7562
+ });
7496
7563
  app.get("/internal/tool-display", internalOnly, (_req, res) => {
7497
7564
  res.json({ tools: integrationManager.getToolDisplay() });
7498
7565
  });
@@ -7813,7 +7880,7 @@ app.listen(port, bindHost, () => {
7813
7880
  (proposal) => executeCommitted(proposal, state, integrationManager)
7814
7881
  );
7815
7882
  installCommittedExecutor(committedExecutor);
7816
- const dataDir2 = process.env.SUVEREN_DATA_DIR ?? join19(homedir11(), ".suveren");
7883
+ const dataDir2 = process.env.SUVEREN_DATA_DIR ?? join20(homedir11(), ".suveren");
7817
7884
  const executorLock = new ExecutorLock(dataDir2);
7818
7885
  const lock = executorLock.acquire();
7819
7886
  if (!lock.held) {