@suveren/gateway 0.3.2 → 0.3.4

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 (42) hide show
  1. package/dist/control-plane/index.mjs +2 -2
  2. package/dist/mcp-server/http.mjs +73 -68
  3. package/dist/ui/assets/index-mg8gMTzG.js +105 -0
  4. package/dist/ui/index.html +1 -1
  5. package/node_modules/@hap/core/dist/index.d.mts +7 -2
  6. package/node_modules/@hap/core/dist/index.d.ts +7 -2
  7. package/node_modules/@hap/core/dist/index.js +2 -2
  8. package/node_modules/@hap/core/dist/index.mjs +2 -2
  9. package/node_modules/@hap/core/package.json +1 -1
  10. package/node_modules/@hap/core/src/identity.ts +9 -4
  11. package/node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite/encodings/sbcs-data.js +2 -0
  12. package/node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite/encodings/utf32.js +10 -3
  13. package/node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite/package.json +2 -2
  14. package/node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite/types/encodings.d.ts +2 -0
  15. package/node_modules/@types/node/README.md +1 -1
  16. package/node_modules/@types/node/buffer.d.ts +64 -25
  17. package/node_modules/@types/node/crypto.d.ts +18 -5
  18. package/node_modules/@types/node/diagnostics_channel.d.ts +237 -3
  19. package/node_modules/@types/node/dns.d.ts +1 -1
  20. package/node_modules/@types/node/ffi.d.ts +486 -0
  21. package/node_modules/@types/node/fs/promises.d.ts +3 -0
  22. package/node_modules/@types/node/fs.d.ts +21 -6
  23. package/node_modules/@types/node/http.d.ts +25 -0
  24. package/node_modules/@types/node/index.d.ts +1 -0
  25. package/node_modules/@types/node/package.json +2 -2
  26. package/node_modules/@types/node/process.d.ts +14 -1
  27. package/node_modules/@types/node/quic.d.ts +92 -11
  28. package/node_modules/@types/node/sqlite.d.ts +55 -0
  29. package/node_modules/@types/node/stream/iter.d.ts +150 -0
  30. package/node_modules/@types/node/stream.d.ts +32 -0
  31. package/node_modules/@types/node/test.d.ts +112 -2
  32. package/node_modules/@types/node/ts5.6/index.d.ts +1 -0
  33. package/node_modules/@types/node/ts5.7/index.d.ts +1 -0
  34. package/node_modules/@types/node/util.d.ts +19 -2
  35. package/node_modules/@types/node/v8.d.ts +84 -2
  36. package/node_modules/@types/node/worker_threads.d.ts +8 -7
  37. package/node_modules/raw-body/node_modules/iconv-lite/encodings/sbcs-data.js +2 -0
  38. package/node_modules/raw-body/node_modules/iconv-lite/encodings/utf32.js +10 -3
  39. package/node_modules/raw-body/node_modules/iconv-lite/package.json +2 -2
  40. package/node_modules/raw-body/node_modules/iconv-lite/types/encodings.d.ts +2 -0
  41. package/package.json +2 -2
  42. package/dist/ui/assets/index-RkZBhIxU.js +0 -105
@@ -2009,8 +2009,8 @@ app.get("/active-authorizations", authGuard, async (_req, res) => {
2009
2009
  });
2010
2010
  app.post("/gate-content", jsonParser, authGuard, async (req, res) => {
2011
2011
  try {
2012
- const { frameHash, boundsHash, contextHash, context, path, gateContent } = req.body;
2013
- await pushGateContent({ frameHash, boundsHash, contextHash, context, path, gateContent });
2012
+ const { authorizationId, boundsHash, contextHash, context, path, gateContent } = req.body;
2013
+ await pushGateContent({ authorizationId, boundsHash, contextHash, context, path, gateContent });
2014
2014
  res.json({ ok: true });
2015
2015
  } catch (err) {
2016
2016
  console.error("[Control Plane] Gate content forward error:", err);
@@ -63,8 +63,8 @@ var SPClient = class {
63
63
  /**
64
64
  * Get all attestations for a frame hash.
65
65
  */
66
- async getAttestations(frameHash) {
67
- const res = await this.fetch(`/api/attestations?frame_hash=${encodeURIComponent(frameHash)}`);
66
+ async getAttestations(authorizationId) {
67
+ const res = await this.fetch(`/api/attestations?authorization_id=${encodeURIComponent(authorizationId)}`);
68
68
  if (!res.ok) throw new Error(`SP attestations request failed: ${res.status}`);
69
69
  return res.json();
70
70
  }
@@ -138,7 +138,7 @@ var SPClient = class {
138
138
  const res = await this.fetch("/api/proposals", {
139
139
  method: "POST",
140
140
  body: JSON.stringify({
141
- frame_hash: data.frameHash,
141
+ authorization_id: data.authorizationId,
142
142
  profile_id: data.profileId,
143
143
  path: data.path,
144
144
  pending_domains: data.pendingDomains,
@@ -157,11 +157,11 @@ var SPClient = class {
157
157
  return res.json();
158
158
  }
159
159
  /**
160
- * Phase 6: Fetch frame metadata for an authority by its boundsHash / frameHash.
160
+ * Phase 6: Fetch the authorization summary by its per-ceremony id.
161
161
  * Used to read aboveCap and approversFrozen at action time.
162
162
  */
163
- async getFrameMetadata(frameHash) {
164
- const res = await this.fetch(`/api/as/frame/${encodeURIComponent(frameHash)}`);
163
+ async getAuthorizationSummary(authorizationId) {
164
+ const res = await this.fetch(`/api/authorizations/${encodeURIComponent(authorizationId)}`);
165
165
  if (res.status === 404) return null;
166
166
  if (!res.ok) return null;
167
167
  return res.json();
@@ -228,16 +228,19 @@ var AttestationCache = class {
228
228
  /**
229
229
  * Fetch attestation data from SP for a frame hash and cache it.
230
230
  */
231
- async syncAuthorization(frameHash) {
232
- const result = await this.spClient.getAttestations(frameHash);
233
- if (!result.profile_id || !result.frame) return null;
231
+ async syncAuthorization(authorizationId) {
232
+ const result = await this.spClient.getAttestations(authorizationId);
233
+ if (!result.profile_id) return null;
234
234
  if (result.revoked) {
235
- this.authorizations.delete(frameHash);
236
- if (result.frame_hash) this.authorizations.delete(result.frame_hash);
235
+ this.authorizations.delete(authorizationId);
237
236
  return null;
238
237
  }
239
- const storageHash = result.frame_hash ?? result.bounds_hash;
240
- const bounds = result.bounds ?? result.frame;
238
+ if (!result.authorization_id) {
239
+ throw new Error(
240
+ "Authority Server response lacks authorization_id \u2014 the AS predates per-ceremony identity. Update the Authority Server (lockstep deploy)."
241
+ );
242
+ }
243
+ const bounds = result.bounds ?? result.frame ?? {};
241
244
  let signedCommitmentMode;
242
245
  let subjects;
243
246
  const firstBlob = result.attestations[0]?.blob;
@@ -251,12 +254,12 @@ var AttestationCache = class {
251
254
  }
252
255
  }
253
256
  const auth = {
254
- frameHash: storageHash,
257
+ authorizationId: result.authorization_id,
255
258
  boundsHash: result.bounds_hash,
256
259
  // content fingerprint (undefined for pre-v0.4 records)
257
260
  contextHash: result.context_hash,
258
261
  profileId: result.profile_id,
259
- path: result.path ?? result.profile_id,
262
+ path: result.profile_id,
260
263
  frame: bounds,
261
264
  // compat alias
262
265
  bounds: result.bounds,
@@ -273,7 +276,7 @@ var AttestationCache = class {
273
276
  subjects,
274
277
  complete: result.complete
275
278
  };
276
- this.authorizations.set(auth.frameHash ?? auth.path, auth);
279
+ this.authorizations.set(auth.authorizationId, auth);
277
280
  return auth;
278
281
  }
279
282
  /**
@@ -302,7 +305,7 @@ var AttestationCache = class {
302
305
  * Cache an authorization directly (e.g., from SP response after creation).
303
306
  */
304
307
  cacheAuthorization(auth) {
305
- this.authorizations.set(auth.frameHash ?? auth.path, auth);
308
+ this.authorizations.set(auth.authorizationId, auth);
306
309
  }
307
310
  /**
308
311
  * Remove a cached authorization by path. Called when the SP reports the
@@ -717,9 +720,9 @@ var SharedState = class {
717
720
  this.executionLog = new ExecutionLog(gateStorePath);
718
721
  this.gatekeeper = new MCPGatekeeper(this.cache, this.executionLog);
719
722
  }
720
- setGateContent(path, frameHash, profileId, content, opts) {
721
- this.gateStore.set(path, {
722
- frameHash,
723
+ setGateContent(path, authorizationId, profileId, content, opts) {
724
+ this.gateStore.set(authorizationId, {
725
+ authorizationId,
723
726
  boundsHash: opts?.boundsHash,
724
727
  contextHash: opts?.contextHash,
725
728
  path,
@@ -739,7 +742,7 @@ var SharedState = class {
739
742
  getEnrichedAuthorizations() {
740
743
  const authorizations = this.cache.getAllAuthorizations();
741
744
  return authorizations.map((auth) => {
742
- const gateEntry = (auth.frameHash ? this.gateStore.get(auth.frameHash) : null) ?? (auth.boundsHash ? this.gateStore.get(auth.boundsHash) : null) ?? this.gateStore.get(auth.path) ?? null;
745
+ const gateEntry = this.gateStore.get(auth.authorizationId) ?? null;
743
746
  return {
744
747
  ...auth,
745
748
  gateContent: gateEntry?.gateContent ?? null,
@@ -932,20 +935,29 @@ import { getProfile as getProfile3 } from "@hap/core";
932
935
  import { deriveIdentityLine } from "@hap/core";
933
936
  var AS_BASE = (process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai").replace(/\/$/, "");
934
937
  var OPERATOR_NAME = process.env.SUVEREN_OPERATOR_NAME ?? "Suveren";
938
+ var HAP_URL = "https://www.humanagencyprotocol.org/";
935
939
  var FOOTER_PROFILES = /* @__PURE__ */ new Set(["email", "calendar", "publish"]);
936
940
  var CONTENT_FIELD_CANDIDATES = ["body", "text", "description", "content"];
937
- var FOOTER_MARKER = "\u2014 Sent by an AI agent";
941
+ var FOOTER_RE = /\n*(?:—|--) (?:Sent|Published) by an AI agent[\s\S]*$/;
938
942
  function shouldAttachFooter() {
939
943
  return true;
940
944
  }
941
- function verifyUrl(receiptId) {
945
+ function receiptUrl(receiptId) {
942
946
  return `${AS_BASE}/r/${receiptId}`;
943
947
  }
944
- function footerText(receiptId, subject) {
945
- const line = deriveIdentityLine(subject, { operatorName: OPERATOR_NAME });
948
+ function verbForProfile(profile) {
949
+ return profile === "publish" ? "Published" : "Sent";
950
+ }
951
+ function hapLine() {
952
+ return `-- ${OPERATOR_NAME} is an implementation of the Human Agency Protocol (HAP), an open protocol to delegate execution to AI agents under human authority. ${HAP_URL}`;
953
+ }
954
+ function footerText(receiptId, verb, subject) {
955
+ const identity = deriveIdentityLine(subject, { operatorName: OPERATOR_NAME }).replace(/^Sent\b/, verb);
946
956
  return `
947
957
 
948
- \u2014 ${line}. Verify: ${verifyUrl(receiptId)}`;
958
+ -- ${identity}. Receipt: ${receiptUrl(receiptId)}
959
+
960
+ ${hapLine()}`;
949
961
  }
950
962
  function isStringType(t) {
951
963
  return t === "string" || Array.isArray(t) && t.includes("string");
@@ -960,9 +972,7 @@ function detectContentField(tool) {
960
972
  return null;
961
973
  }
962
974
  function stripFooter(value) {
963
- const i = value.indexOf(FOOTER_MARKER);
964
- if (i === -1) return value;
965
- return value.slice(0, i).replace(/\n+$/, "");
975
+ return value.replace(FOOTER_RE, "").replace(/\n+$/, "");
966
976
  }
967
977
  function appendVerificationFooter(tool, args, receiptId, subject) {
968
978
  const profile = tool.gating?.profile;
@@ -970,7 +980,7 @@ function appendVerificationFooter(tool, args, receiptId, subject) {
970
980
  const field = detectContentField(tool);
971
981
  if (!field) return args;
972
982
  const current = typeof args[field] === "string" ? args[field] : "";
973
- return { ...args, [field]: stripFooter(current) + footerText(receiptId, subject) };
983
+ return { ...args, [field]: stripFooter(current) + footerText(receiptId, verbForProfile(profile), subject) };
974
984
  }
975
985
 
976
986
  // src/lib/content-binding.ts
@@ -1127,14 +1137,14 @@ function createGatedToolHandler(tool, integrationManager2, state2) {
1127
1137
  }
1128
1138
  const errors = [];
1129
1139
  for (const auth of matchingAuths) {
1130
- const { result } = await state2.gatekeeper.verifyExecution(auth.frameHash ?? auth.path, execution, {
1140
+ const { result } = await state2.gatekeeper.verifyExecution(auth.authorizationId, execution, {
1131
1141
  bounds: auth.bounds,
1132
1142
  context: auth.context
1133
1143
  });
1134
1144
  if (!result.approved) {
1135
1145
  }
1136
1146
  if (result.approved) {
1137
- const authHash = auth.frameHash ?? auth.boundsHash;
1147
+ const authzId = auth.authorizationId;
1138
1148
  if (isCommitmentDowngrade(auth)) {
1139
1149
  return {
1140
1150
  content: [{
@@ -1148,7 +1158,7 @@ function createGatedToolHandler(tool, integrationManager2, state2) {
1148
1158
  try {
1149
1159
  const enrichedArgs = await attachImagePreview(args);
1150
1160
  const { proposal } = await state2.spClient.submitProposal({
1151
- frameHash: authHash,
1161
+ authorizationId: authzId,
1152
1162
  profileId: auth.profileId,
1153
1163
  path: auth.path,
1154
1164
  pendingDomains: auth.deferredCommitmentDomains,
@@ -1180,10 +1190,9 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1180
1190
  }
1181
1191
  const binding = computeContentBinding(auth.profileId, tool, args);
1182
1192
  const { receipt } = await state2.spClient.postReceipt({
1183
- // v0.5: send the bare content address; the AS reconstructs the
1184
- // per-user storage key. Fall back to frameHash only for legacy
1185
- // (pre-v0.4) records that predate bounds_hash.
1186
- boundsHash: auth.boundsHash ?? authHash,
1193
+ authorizationId: authzId,
1194
+ // Optional cross-check the AS fails closed on a mismatch.
1195
+ boundsHash: auth.boundsHash,
1187
1196
  profileId: auth.profileId,
1188
1197
  action: tool.namespacedName,
1189
1198
  actionType,
@@ -1199,20 +1208,20 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1199
1208
  let pendingApprovers = spBody.approvers ?? [];
1200
1209
  if (pendingApprovers.length === 0) {
1201
1210
  try {
1202
- const frameMeta = await state2.spClient.getFrameMetadata(authHash);
1203
- if (frameMeta?.approversFrozen) {
1204
- pendingApprovers = frameMeta.approversFrozen;
1211
+ const summary = await state2.spClient.getAuthorizationSummary(authzId);
1212
+ if (summary?.approvers_frozen) {
1213
+ pendingApprovers = summary.approvers_frozen;
1205
1214
  }
1206
- if (frameMeta?.createdBy) {
1207
- pendingApprovers = [frameMeta.createdBy, ...pendingApprovers];
1215
+ if (summary?.created_by) {
1216
+ pendingApprovers = [summary.created_by, ...pendingApprovers];
1208
1217
  }
1209
1218
  } catch {
1210
1219
  }
1211
1220
  } else {
1212
1221
  try {
1213
- const frameMeta = await state2.spClient.getFrameMetadata(authHash);
1214
- if (frameMeta?.createdBy) {
1215
- pendingApprovers = [frameMeta.createdBy, ...pendingApprovers];
1222
+ const summary = await state2.spClient.getAuthorizationSummary(authzId);
1223
+ if (summary?.created_by) {
1224
+ pendingApprovers = [summary.created_by, ...pendingApprovers];
1216
1225
  }
1217
1226
  } catch {
1218
1227
  }
@@ -1221,7 +1230,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1221
1230
  try {
1222
1231
  const enrichedArgs = await attachImagePreview(args);
1223
1232
  const { proposal } = await state2.spClient.submitProposal({
1224
- frameHash: authHash,
1233
+ authorizationId: authzId,
1225
1234
  profileId: auth.profileId,
1226
1235
  path: auth.path,
1227
1236
  pendingDomains: [],
@@ -1252,7 +1261,7 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
1252
1261
  }
1253
1262
  if (err instanceof SPReceiptError && err.statusCode === 403) {
1254
1263
  if (/revoked/i.test(err.message)) {
1255
- state2.cache.invalidate(auth.frameHash ?? auth.path);
1264
+ state2.cache.invalidate(auth.authorizationId);
1256
1265
  }
1257
1266
  return {
1258
1267
  content: [{ type: "text", text: `Blocked by SP: ${err.message}` }],
@@ -1418,8 +1427,8 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
1418
1427
  const reviewDomains = auth.deferredCommitmentDomains ?? [];
1419
1428
  output2.push(reviewDomains.length > 0 ? " Mode: review \u2014 each action requires your approval before it runs (a proposal is created, not executed)" : " Mode: automatic \u2014 actions run immediately within bounds");
1420
1429
  try {
1421
- const meta = await state2.spClient.getFrameMetadata(auth.frameHash ?? auth.boundsHash ?? auth.path);
1422
- if (meta?.aboveCap) {
1430
+ const meta = await state2.spClient.getAuthorizationSummary(auth.authorizationId);
1431
+ if (meta?.above_cap) {
1423
1432
  output2.push(" \u26A0 Above team cap \u2014 actions here require approval even within these bounds.");
1424
1433
  }
1425
1434
  } catch {
@@ -1631,10 +1640,9 @@ async function executeCommitted(proposal, state2, integrationManager2) {
1631
1640
  }
1632
1641
  let receiptId;
1633
1642
  try {
1634
- const boundsHash = proposal.frameHash.split(":").slice(0, 2).join(":");
1635
1643
  const binding = computeContentBinding(proposal.profileId, discovered, proposal.toolArgs);
1636
1644
  const { receipt } = await state2.spClient.postReceipt({
1637
- boundsHash,
1645
+ authorizationId: proposal.authorizationId,
1638
1646
  profileId: proposal.profileId,
1639
1647
  action: proposal.tool,
1640
1648
  actionType: proposalActionType,
@@ -2494,18 +2502,17 @@ app.post("/internal/configure", internalOnly, (req, res) => {
2494
2502
  });
2495
2503
  app.post("/internal/gate-content", internalOnly, async (req, res) => {
2496
2504
  try {
2497
- const { frameHash, boundsHash, contextHash, context, path: rawPath, gateContent } = req.body;
2498
- const storageHash = frameHash ?? boundsHash;
2505
+ const { authorizationId, boundsHash, contextHash, context, path: rawPath, gateContent } = req.body;
2499
2506
  const hasIntent = !!gateContent?.intent;
2500
2507
  const legacy = gateContent;
2501
2508
  const hasLegacy = !!legacy?.problem && !!legacy?.objective && !!legacy?.tradeoffs;
2502
- if (!storageHash || !hasIntent && !hasLegacy) {
2503
- res.status(400).json({ error: "Missing required fields: frameHash (or boundsHash), gateContent.{intent} or gateContent.{problem,objective,tradeoffs}" });
2509
+ if (!authorizationId || !hasIntent && !hasLegacy) {
2510
+ res.status(400).json({ error: "Missing required fields: authorizationId, gateContent.{intent} or gateContent.{problem,objective,tradeoffs}" });
2504
2511
  return;
2505
2512
  }
2506
- const auth = await state.cache.syncAuthorization(storageHash);
2513
+ const auth = await state.cache.syncAuthorization(authorizationId);
2507
2514
  if (!auth) {
2508
- res.status(404).json({ error: `No attestation found for frame hash ${storageHash}` });
2515
+ res.status(404).json({ error: `No authorization found for ${authorizationId}` });
2509
2516
  return;
2510
2517
  }
2511
2518
  const verification = verifyGateContentHashes(gateContent, auth);
@@ -2513,8 +2520,8 @@ app.post("/internal/gate-content", internalOnly, async (req, res) => {
2513
2520
  res.status(400).json({ error: "Gate content hash mismatch", details: verification.errors });
2514
2521
  return;
2515
2522
  }
2516
- const path = rawPath || storageHash;
2517
- state.setGateContent(path, storageHash, auth.profileId, gateContent, {
2523
+ const path = rawPath || authorizationId;
2524
+ state.setGateContent(path, authorizationId, auth.profileId, gateContent, {
2518
2525
  boundsHash,
2519
2526
  contextHash,
2520
2527
  context
@@ -2570,10 +2577,10 @@ app.post("/internal/resync-gates", internalOnly, async (_req, res) => {
2570
2577
  let orphaned = 0;
2571
2578
  for (const gate of gates) {
2572
2579
  try {
2573
- const syncHash = gate.frameHash ?? gate.boundsHash;
2574
- const auth = await state.cache.syncAuthorization(syncHash);
2580
+ const authzId = gate.authorizationId;
2581
+ const auth = await state.cache.syncAuthorization(authzId);
2575
2582
  if (auth) {
2576
- state.setGateContent(syncHash, syncHash, auth.profileId, gate.gateContent, {
2583
+ state.setGateContent(auth.path, authzId, auth.profileId, gate.gateContent, {
2577
2584
  boundsHash: gate.boundsHash,
2578
2585
  contextHash: gate.contextHash,
2579
2586
  context: gate.context
@@ -2581,8 +2588,8 @@ app.post("/internal/resync-gates", internalOnly, async (_req, res) => {
2581
2588
  synced++;
2582
2589
  console.error(`[Suveren MCP] Re-synced gate: ${gate.path}`);
2583
2590
  } else {
2584
- state.cache.invalidate(syncHash);
2585
- state.gateStore.delete(syncHash);
2591
+ state.cache.invalidate(authzId);
2592
+ state.gateStore.delete(authzId);
2586
2593
  orphaned++;
2587
2594
  console.error(`[Suveren MCP] Orphan gate purged (SP attestation deleted): ${gate.path}`);
2588
2595
  }
@@ -2737,9 +2744,7 @@ app.get("/internal/gate-content", internalOnly, (req, res) => {
2737
2744
  const path = req.query.path;
2738
2745
  const gates = state.gateStore.getAll();
2739
2746
  if (path) {
2740
- const entry = gates.find(
2741
- (g) => g.path === path || g.profileId === path || g.boundsHash === path || g.frameHash === path
2742
- );
2747
+ const entry = gates.find((g) => g.authorizationId === path);
2743
2748
  res.json({ entry: entry ?? null });
2744
2749
  } else {
2745
2750
  res.json({ entries: gates });
@@ -2748,7 +2753,7 @@ app.get("/internal/gate-content", internalOnly, (req, res) => {
2748
2753
  app.get("/internal/authorizations", internalOnly, (_req, res) => {
2749
2754
  const authorizations = state.getEnrichedAuthorizations().map((a) => ({
2750
2755
  profileId: a.profileId,
2751
- frameHash: a.frameHash,
2756
+ authorizationId: a.authorizationId,
2752
2757
  bounds: a.frame,
2753
2758
  context: a.context ?? {},
2754
2759
  intent: a.gateContent?.intent ?? null,