@suveren/gateway 0.31.1 → 0.31.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.
@@ -216,6 +216,7 @@
216
216
  },
217
217
  "log_activity": {
218
218
  "summary": "Log {type}: {summary|short}",
219
+ "actionLabel": "Activity logged",
219
220
  "executionMapping": {
220
221
  "contact_type": "contact_type"
221
222
  },
@@ -236,6 +237,7 @@
236
237
  },
237
238
  "create_deal": {
238
239
  "summary": "Create deal: {title|short}[ — {value|money}]",
240
+ "actionLabel": "Deal created",
239
241
  "executionMapping": {
240
242
  "contact_type": "contact_type"
241
243
  },
@@ -256,6 +258,7 @@
256
258
  },
257
259
  "update_deal": {
258
260
  "summary": "Update deal[: {title|short}][ — {value|money}]",
261
+ "actionLabel": "Deal updated",
259
262
  "executionMapping": {
260
263
  "contact_type": "contact_type"
261
264
  },
@@ -285,6 +288,7 @@
285
288
  },
286
289
  "create_task": {
287
290
  "summary": "Create task: {title|short}[ (due {due_date|date})]",
291
+ "actionLabel": "Task created",
288
292
  "executionMapping": {
289
293
  "contact_type": "contact_type"
290
294
  },
@@ -305,6 +309,7 @@
305
309
  },
306
310
  "complete_task": {
307
311
  "summary": "Complete a task",
312
+ "actionLabel": "Task completed",
308
313
  "executionMapping": {
309
314
  "contact_type": "contact_type"
310
315
  },
@@ -39,6 +39,7 @@
39
39
  "overrides": {
40
40
  "CreatePayment": {
41
41
  "summary": "Payment of {amount|money}: {description|short}",
42
+ "actionLabel": "Payment created",
42
43
  "executionMapping": {
43
44
  "amount": "amount",
44
45
  "currency": "currency"
@@ -58,6 +59,7 @@
58
59
  },
59
60
  "CreatePaymentLink": {
60
61
  "summary": "Payment link[ of {amount|money}]: {description|short}",
62
+ "actionLabel": "Payment link created",
61
63
  "executionMapping": {
62
64
  "amount": "amount",
63
65
  "currency": "currency"
@@ -92,6 +94,7 @@
92
94
  },
93
95
  "CancelSubscription": {
94
96
  "summary": "Cancel a subscription",
97
+ "actionLabel": "Subscription cancelled",
95
98
  "executionMapping": {},
96
99
  "staticExecution": {
97
100
  "action_type": "subscribe"
@@ -99,6 +102,7 @@
99
102
  },
100
103
  "CreateCustomer": {
101
104
  "summary": "Create payment customer[ {name}]",
105
+ "actionLabel": "Payment customer created",
102
106
  "executionMapping": {},
103
107
  "staticExecution": {
104
108
  "action_type": "charge"
@@ -118,6 +122,7 @@
118
122
  },
119
123
  "RevokeMandate": {
120
124
  "summary": "Revoke a payment mandate",
125
+ "actionLabel": "Payment mandate revoked",
121
126
  "executionMapping": {},
122
127
  "staticExecution": {
123
128
  "action_type": "charge"
@@ -218,7 +223,7 @@
218
223
  ]
219
224
  }
220
225
  ],
221
- "setupHint": "Use a test token (test_...) for development.",
226
+ "setupHint": "Use an organization access token (access_org_...), not an API key (test_... or live_...): Mollie's connector needs it to list your profiles. Test payments still work with it.",
222
227
  "setupGuide": [
223
228
  {
224
229
  "title": "Create a Mollie account",
@@ -227,12 +232,12 @@
227
232
  },
228
233
  {
229
234
  "title": "Get your access token",
230
- "description": "Go to Settings > Developer > Access tokens in your Mollie dashboard. Create an Organization access token.",
235
+ "description": "Go to Settings > Developer > Access tokens in your Mollie dashboard. Create an Organization access token with read and write access to payments, payment links, profiles, customers and organizations.",
231
236
  "link": "https://my.mollie.com/dashboard/developers/api-keys"
232
237
  },
233
238
  {
234
239
  "title": "Enter your token below",
235
- "description": "Paste your Access Token in the field below. Use a test token (test_...) for development."
240
+ "description": "Paste the organization access token (access_org_...) in the field below. An API key (test_... or live_...) does not work here."
236
241
  }
237
242
  ]
238
243
  }
@@ -5,9 +5,9 @@ import {
5
5
 
6
6
  // src/index.ts
7
7
  import { randomBytes as randomBytes3 } from "crypto";
8
- import { existsSync as existsSync13, readFileSync as readFileSync17, renameSync as renameSync5 } from "fs";
9
- import { dirname as dirname12, join as join17 } from "path";
10
- import { homedir as homedir10 } from "os";
8
+ import { existsSync as existsSync13, readFileSync as readFileSync18, renameSync as renameSync6 } from "fs";
9
+ import { dirname as dirname13, join as join18 } from "path";
10
+ import { homedir as homedir11 } from "os";
11
11
  import express from "express";
12
12
  import { createProxyMiddleware } from "http-proxy-middleware";
13
13
 
@@ -3384,15 +3384,15 @@ import { promisify } from "util";
3384
3384
  var run = promisify(execFile);
3385
3385
  var SUPPORTED = /* @__PURE__ */ new Set(["darwin", "win32", "linux"]);
3386
3386
  var MODULE_DIR = pathDirname(fileURLToPath(import.meta.url));
3387
- function findCli(dirname13 = MODULE_DIR) {
3387
+ function findCli(dirname14 = MODULE_DIR) {
3388
3388
  const candidates = [
3389
3389
  // Bundled (the shipped layout): dist/control-plane → dist → root → bin
3390
- resolve(dirname13, "..", "..", "bin", "suveren-gateway.js"),
3390
+ resolve(dirname14, "..", "..", "bin", "suveren-gateway.js"),
3391
3391
  // Bundled, were the routes/ directory ever preserved.
3392
- resolve(dirname13, "..", "..", "..", "bin", "suveren-gateway.js"),
3392
+ resolve(dirname14, "..", "..", "..", "bin", "suveren-gateway.js"),
3393
3393
  // Dev, running from source: the repo's own bundle directory.
3394
- resolve(dirname13, "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js"),
3395
- resolve(dirname13, "..", "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js")
3394
+ resolve(dirname14, "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js"),
3395
+ resolve(dirname14, "..", "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js")
3396
3396
  // Deliberately NO process.cwd() fallback: it makes the result depend on
3397
3397
  // where the process happened to be launched from, so the same install
3398
3398
  // resolves differently between a shell and a login service — and it would
@@ -4724,15 +4724,63 @@ function createTicketsSeenRouter(dataDir) {
4724
4724
  return router;
4725
4725
  }
4726
4726
 
4727
- // src/routes/internal-events.ts
4727
+ // src/routes/report-seen.ts
4728
4728
  import { Router as Router21 } from "express";
4729
+
4730
+ // src/lib/report-seen-store.ts
4731
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, renameSync as renameSync5 } from "fs";
4732
+ import { homedir as homedir10 } from "os";
4733
+ import { join as join16, dirname as dirname12 } from "path";
4734
+ function storePath4(dataDir) {
4735
+ const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join16(homedir10(), ".suveren");
4736
+ return join16(dir, "report-seen.json");
4737
+ }
4738
+ function readLastSeenReportAt(dataDir) {
4739
+ try {
4740
+ const raw = JSON.parse(readFileSync16(storePath4(dataDir), "utf8"));
4741
+ return typeof raw.lastSeenReportAt === "number" && Number.isFinite(raw.lastSeenReportAt) ? raw.lastSeenReportAt : 0;
4742
+ } catch {
4743
+ return 0;
4744
+ }
4745
+ }
4746
+ function markReportSeen(ts, dataDir) {
4747
+ const safeTs = typeof ts === "number" && Number.isFinite(ts) ? ts : Date.now();
4748
+ const path = storePath4(dataDir);
4749
+ mkdirSync11(dirname12(path), { recursive: true });
4750
+ const tmp = `${path}.tmp`;
4751
+ writeFileSync11(tmp, JSON.stringify({ lastSeenReportAt: safeTs }, null, 2) + "\n", "utf8");
4752
+ renameSync5(tmp, path);
4753
+ return safeTs;
4754
+ }
4755
+
4756
+ // src/routes/report-seen.ts
4757
+ function createReportSeenRouter(dataDir) {
4758
+ const router = Router21();
4759
+ router.get("/", (_req, res) => {
4760
+ res.json({ lastSeenReportAt: readLastSeenReportAt(dataDir) });
4761
+ });
4762
+ router.post("/", (req, res) => {
4763
+ const body = req.body;
4764
+ const ts = typeof body?.ts === "number" && Number.isFinite(body.ts) ? body.ts : Date.now();
4765
+ try {
4766
+ res.json({ lastSeenReportAt: markReportSeen(ts, dataDir) });
4767
+ } catch (err) {
4768
+ res.status(500).json({ error: `Could not save: ${err.message}` });
4769
+ }
4770
+ });
4771
+ return router;
4772
+ }
4773
+
4774
+ // src/routes/internal-events.ts
4775
+ import { Router as Router22 } from "express";
4729
4776
  import { timingSafeEqual as timingSafeEqual3 } from "crypto";
4730
4777
  var ALLOWED = [
4731
4778
  "proposal-added",
4732
4779
  "action-approval-needed",
4733
4780
  "execution-failed",
4734
4781
  "denial-recorded",
4735
- "ticket-recorded"
4782
+ "ticket-recorded",
4783
+ "report-written"
4736
4784
  ];
4737
4785
  var SESSION_EXPIRED = "session-expired";
4738
4786
  var AS_KEY_MISMATCH = "as-key-mismatch";
@@ -4746,7 +4794,7 @@ function secretMatches2(provided, expected) {
4746
4794
  return timingSafeEqual3(a, b);
4747
4795
  }
4748
4796
  function createInternalEventsRouter(getSecret, onSessionExpired, onAsKeyMismatch, onAsTlsMismatch) {
4749
- const router = Router21();
4797
+ const router = Router22();
4750
4798
  router.post("/event", (req, res) => {
4751
4799
  if (!secretMatches2(req.headers["x-internal-secret"], getSecret())) {
4752
4800
  res.status(401).json({ error: "Unauthorized" });
@@ -4780,7 +4828,7 @@ function createInternalEventsRouter(getSecret, onSessionExpired, onAsKeyMismatch
4780
4828
  }
4781
4829
 
4782
4830
  // src/routes/internal-mandate.ts
4783
- import { Router as Router22 } from "express";
4831
+ import { Router as Router23 } from "express";
4784
4832
  import { timingSafeEqual as timingSafeEqual4, randomUUID } from "crypto";
4785
4833
 
4786
4834
  // src/lib/mandate-ceremony.ts
@@ -4962,7 +5010,7 @@ function secretMatches3(provided, expected) {
4962
5010
  return a.length === b.length && timingSafeEqual4(a, b);
4963
5011
  }
4964
5012
  function createInternalMandateRouter(getSecret, deps) {
4965
- const router = Router22();
5013
+ const router = Router23();
4966
5014
  router.post("/mandate", async (req, res) => {
4967
5015
  if (!secretMatches3(req.header("X-Internal-Secret") ?? void 0, getSecret())) {
4968
5016
  res.status(401).json({ error: "unauthorized" });
@@ -5165,17 +5213,17 @@ function buildSessionHealth(vault2) {
5165
5213
  }
5166
5214
 
5167
5215
  // src/lib/denials-reader.ts
5168
- import { existsSync as existsSync12, readFileSync as readFileSync16 } from "fs";
5169
- import { join as join16 } from "path";
5216
+ import { existsSync as existsSync12, readFileSync as readFileSync17 } from "fs";
5217
+ import { join as join17 } from "path";
5170
5218
  function loadDenials(dataDir, decrypt) {
5171
- const encPath = join16(dataDir, "denials.enc.json");
5172
- const plainPath = join16(dataDir, "denials.json");
5219
+ const encPath = join17(dataDir, "denials.enc.json");
5220
+ const plainPath = join17(dataDir, "denials.json");
5173
5221
  if (existsSync12(encPath)) {
5174
- const { blob } = JSON.parse(readFileSync16(encPath, "utf-8"));
5222
+ const { blob } = JSON.parse(readFileSync17(encPath, "utf-8"));
5175
5223
  return JSON.parse(decrypt(blob)).records ?? [];
5176
5224
  }
5177
5225
  if (existsSync12(plainPath)) {
5178
- return JSON.parse(readFileSync16(plainPath, "utf-8")).records ?? [];
5226
+ return JSON.parse(readFileSync17(plainPath, "utf-8")).records ?? [];
5179
5227
  }
5180
5228
  return [];
5181
5229
  }
@@ -5199,11 +5247,11 @@ function isSimulationMode() {
5199
5247
  // src/index.ts
5200
5248
  (function migrateDataDir() {
5201
5249
  if (process.env.SUVEREN_DATA_DIR) return;
5202
- const oldDir = join17(homedir10(), ".hap");
5203
- const newDir = join17(homedir10(), ".suveren");
5250
+ const oldDir = join18(homedir11(), ".hap");
5251
+ const newDir = join18(homedir11(), ".suveren");
5204
5252
  if (!existsSync13(newDir) && existsSync13(oldDir)) {
5205
5253
  try {
5206
- renameSync5(oldDir, newDir);
5254
+ renameSync6(oldDir, newDir);
5207
5255
  console.error(`[Control Plane] Migrated data directory: ${oldDir} \u2192 ${newDir}`);
5208
5256
  } catch (err) {
5209
5257
  console.error(`[Control Plane] FATAL: could not migrate data directory ${oldDir} \u2192 ${newDir}:`, err);
@@ -5211,7 +5259,7 @@ function isSimulationMode() {
5211
5259
  }
5212
5260
  }
5213
5261
  })();
5214
- var DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join17(homedir10(), ".suveren");
5262
+ var DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join18(homedir11(), ".suveren");
5215
5263
  var SP_URL = resolveAsUrl(DATA_DIR);
5216
5264
  validatePinTlsForUrl(resolvePinTls(DATA_DIR), SP_URL);
5217
5265
  var POLICY_LOCKED = [...readPolicy().locked];
@@ -5219,7 +5267,7 @@ var POLICY_LOCKED_INSTALL_METHOD = readPolicy().policy.installMethod;
5219
5267
  var POLICY_LOCKED_TEAM = readPolicy().policy.team;
5220
5268
  var port = parseInt(process.env.SUVEREN_CP_PORT ?? "3402", 10);
5221
5269
  var HAP_MODE = process.env.HAP_MODE ?? "personal";
5222
- var UI_DIST = process.env.HAP_UI_DIST ?? join17(import.meta.dirname ?? __dirname, "../../ui/dist");
5270
+ var UI_DIST = process.env.HAP_UI_DIST ?? join18(import.meta.dirname ?? __dirname, "../../ui/dist");
5223
5271
  var vault = new Vault(DATA_DIR);
5224
5272
  var shownSnapshotStore = new ShownSnapshotStore(DATA_DIR);
5225
5273
  (function repairIfAsUrlChanged() {
@@ -5530,6 +5578,7 @@ app.use("/api/gateway-settings", jsonParser, authGuard, createGatewaySettingsRou
5530
5578
  app.use("/api/archived-mandates", jsonParser, authGuard, createArchivedMandatesRouter());
5531
5579
  app.use("/api/blocked-seen", jsonParser, authGuard, createBlockedSeenRouter());
5532
5580
  app.use("/api/tickets-seen", jsonParser, authGuard, createTicketsSeenRouter());
5581
+ app.use("/api/report-seen", jsonParser, authGuard, createReportSeenRouter());
5533
5582
  app.use("/api/encrypt-intent", jsonParser, authGuard, createEncryptIntentRouter());
5534
5583
  app.use("/api/evidence-export", authGuard, createEvidenceExportRouter(SP_URL, vault, DATA_DIR, lockAsTlsMismatch));
5535
5584
  app.use("/proposal-status", jsonParser, authGuard, createProposalStatusRouter(SP_URL, vault, DATA_DIR, lockAsTlsMismatch, shownSnapshotStore));
@@ -5706,7 +5755,7 @@ app.get("/as-pairing", authGuard, (_req, res) => {
5706
5755
  });
5707
5756
  app.get("/denials", authGuard, (req, res) => {
5708
5757
  try {
5709
- const dataDir = process.env.SUVEREN_DATA_DIR ?? join17(homedir10(), ".suveren");
5758
+ const dataDir = process.env.SUVEREN_DATA_DIR ?? join18(homedir11(), ".suveren");
5710
5759
  const all = loadDenials(dataDir, (blob) => vault.decrypt(blob));
5711
5760
  const since = Number(req.query.since);
5712
5761
  const limit = Number(req.query.limit);
@@ -5765,12 +5814,12 @@ app.put("/agent-brief/context", jsonParser, authGuard, async (req, res) => {
5765
5814
  res.status(413).json({ error: `Context exceeds ${AGENT_CONTEXT_MAX_BYTES}-byte cap` });
5766
5815
  return;
5767
5816
  }
5768
- const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync11, renameSync: renameSync6 } = await import("fs");
5817
+ const { mkdirSync: mkdirSync12, writeFileSync: writeFileSync12, renameSync: renameSync7 } = await import("fs");
5769
5818
  const filePath = agentBriefPath();
5770
- mkdirSync11(dirname12(filePath), { recursive: true });
5819
+ mkdirSync12(dirname13(filePath), { recursive: true });
5771
5820
  const tmpPath = `${filePath}.tmp`;
5772
- writeFileSync11(tmpPath, content, "utf-8");
5773
- renameSync6(tmpPath, filePath);
5821
+ writeFileSync12(tmpPath, content, "utf-8");
5822
+ renameSync7(tmpPath, filePath);
5774
5823
  res.json({ ok: true });
5775
5824
  } catch (err) {
5776
5825
  console.error("[Control Plane] agent-brief/context PUT failed:", err);
@@ -5862,10 +5911,10 @@ function detectRunningVersion() {
5862
5911
  return process.env.HAP_BUILD_SHA ?? "dev";
5863
5912
  }
5864
5913
  const dir = import.meta.dirname ?? __dirname;
5865
- const bundlePkg = join17(dir, "..", "..", "package.json");
5914
+ const bundlePkg = join18(dir, "..", "..", "package.json");
5866
5915
  if (existsSync13(bundlePkg)) {
5867
5916
  try {
5868
- const pkg = JSON.parse(readFileSync17(bundlePkg, "utf8"));
5917
+ const pkg = JSON.parse(readFileSync18(bundlePkg, "utf8"));
5869
5918
  if (pkg.name === "@suveren/gateway" && typeof pkg.version === "string") {
5870
5919
  return pkg.version;
5871
5920
  }
@@ -5917,7 +5966,7 @@ if (existsSync13(UI_DIST)) {
5917
5966
  }));
5918
5967
  app.get("*", (_req, res) => {
5919
5968
  res.setHeader("Cache-Control", "no-store, must-revalidate");
5920
- res.sendFile(join17(UI_DIST, "index.html"));
5969
+ res.sendFile(join18(UI_DIST, "index.html"));
5921
5970
  });
5922
5971
  } else {
5923
5972
  app.get("/", (_req, res) => {
@@ -3113,6 +3113,7 @@ var ReportStore = class {
3113
3113
  const result = await verifyReport(html, sources);
3114
3114
  this.report = { html: result.html, savedAt: now, checkedAt: now, result };
3115
3115
  this.persist();
3116
+ void notifyControlPlane("report-written");
3116
3117
  return this.report;
3117
3118
  }
3118
3119
  /**
@@ -7403,7 +7404,11 @@ function createMcpServer(state2, integrationManager2) {
7403
7404
  tool.namespacedName,
7404
7405
  {
7405
7406
  description,
7406
- ...Object.keys(zodShape).length > 0 ? { inputSchema: zodShape } : {}
7407
+ // Always a schema, even an empty one: without it the SDK calls the
7408
+ // handler as (extra) instead of (args, extra), and the request's own
7409
+ // data (session id, headers, _meta) was forwarded to the connector as
7410
+ // the tool's arguments (Mollie GetOrganizationDetails, 2026-10-16).
7411
+ inputSchema: zodShape
7407
7412
  },
7408
7413
  // Built-in tools (reports, setup) never carry the outcome line: a
7409
7414
  // report's output is evidence and must not mix in a notice (nor the
@@ -8270,6 +8275,21 @@ var IntegrationManager = class {
8270
8275
  isBuiltin(id) {
8271
8276
  return this.builtins.has(id);
8272
8277
  }
8278
+ /**
8279
+ * Names only, for the UI's labels ("Report written", system "Reports"):
8280
+ * id, display name and per-tool action labels. Kept apart from the
8281
+ * connector manifests so a built-in never shows up as a connector card.
8282
+ */
8283
+ getBuiltinLabels() {
8284
+ return [...this.builtins.values()].map(({ def }) => {
8285
+ const overrides = {};
8286
+ for (const [tool, o] of Object.entries(def.toolGating?.overrides ?? {})) {
8287
+ const label = o?.actionLabel;
8288
+ if (typeof label === "string") overrides[tool] = { actionLabel: label };
8289
+ }
8290
+ return { id: def.id, name: def.name, toolGating: { overrides } };
8291
+ });
8292
+ }
8273
8293
  /** A built-in that declares `alwaysListed` (see builtin-integration.ts). */
8274
8294
  builtinAlwaysListed(id) {
8275
8295
  return this.builtins.get(id)?.def.alwaysListed === true;
@@ -9197,6 +9217,14 @@ function writeReportRefusal(args) {
9197
9217
  if (bytes > MAX_REPORT_HTML_BYTES) {
9198
9218
  return `the report is ${bytes} bytes, over the ${MAX_REPORT_HTML_BYTES}-byte limit. Shorten it (the gateway draws the verifiable elements for you \u2014 you do not need to inline their data) and write again.`;
9199
9219
  }
9220
+ const { html: kept, notes } = sanitizeReport(html);
9221
+ if (!/<sv-(ai|ticket|approval|mandate|record|case|metric|row)\b/i.test(kept)) {
9222
+ const lost = [];
9223
+ if (notes.droppedBlocks > 0) lost.push(`${notes.droppedBlocks} block(s) outside sv-ai`);
9224
+ if (notes.droppedSvInsideAi > 0) lost.push(`${notes.droppedSvInsideAi} sv-* element(s) inside sv-ai`);
9225
+ if (notes.droppedStyles > 0) lost.push(`${notes.droppedStyles} <style> block(s)`);
9226
+ return `nothing in this report would be shown${lost.length ? ` (dropped: ${lost.join(", ")})` : ""}. A report is a flat sequence of top-level blocks: your own content inside <sv-ai>\u2026</sv-ai>, gateway elements (sv-ticket, sv-approval, sv-mandate, sv-record, sv-case, sv-metric, optionally in sv-row) and at most one sv-glossary. Write it again in that form; nothing was stored and no ticket was used.`;
9227
+ }
9200
9228
  return void 0;
9201
9229
  }
9202
9230
  function writeReportTool(deps) {
@@ -9266,7 +9294,7 @@ var reportBuiltin = (deps) => ({
9266
9294
  get_ticket: READ_GATE,
9267
9295
  list_cases: READ_GATE,
9268
9296
  get_records: READ_GATE,
9269
- write_report: { executionMapping: {}, staticExecution: { action_type: "report" } }
9297
+ write_report: { executionMapping: {}, staticExecution: { action_type: "report" }, actionLabel: "Report written" }
9270
9298
  }
9271
9299
  },
9272
9300
  tools: [
@@ -9665,6 +9693,7 @@ function setupBuiltin(deps) {
9665
9693
  set_agent_brief: {
9666
9694
  executionMapping: {},
9667
9695
  staticExecution: { action_type: "brief" },
9696
+ actionLabel: "Agent brief set",
9668
9697
  hideUnlessAuthorized: true,
9669
9698
  approvalView: { content: { label: "New agent brief", kind: "markdown" } },
9670
9699
  // AU3 — the approval card's preview of this pending proposal (DL1
@@ -9679,6 +9708,7 @@ function setupBuiltin(deps) {
9679
9708
  create_mandate: {
9680
9709
  executionMapping: { profile: "allowed_profiles", mode: "allowed_modes" },
9681
9710
  staticExecution: { action_type: "mandate" },
9711
+ actionLabel: "Mandate proposed",
9682
9712
  hideUnlessAuthorized: true,
9683
9713
  // Shown like the mandate screen: limits and scope by the profile's own names.
9684
9714
  approvalView: {
@@ -10448,7 +10478,7 @@ app.get("/internal/authorizations", internalOnly, (_req, res) => {
10448
10478
  res.json({ authorizations });
10449
10479
  });
10450
10480
  app.get("/internal/manifests", internalOnly, (_req, res) => {
10451
- res.json({ manifests: getAllManifests() });
10481
+ res.json({ manifests: getAllManifests(), builtins: integrationManager?.getBuiltinLabels?.() ?? [] });
10452
10482
  });
10453
10483
  app.get("/internal/skipped-commitments", internalOnly, async (_req, res) => {
10454
10484
  try {