@suveren/gateway 0.6.3 → 0.6.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.
@@ -80,7 +80,10 @@
80
80
  "action_type": "release"
81
81
  },
82
82
  "contentField": "deployment_url",
83
- "actionLabel": "Deployment released"
83
+ "actionLabel": "Deployment released",
84
+ "argNormalization": {
85
+ "deployment_url": "url"
86
+ }
84
87
  }
85
88
  }
86
89
  },
@@ -1270,6 +1270,49 @@ function encodeOutgoingArgs(tool, args) {
1270
1270
  return out ?? args;
1271
1271
  }
1272
1272
 
1273
+ // src/lib/arg-normalization.ts
1274
+ function normalizeUrl(value) {
1275
+ const trimmed = value.trim();
1276
+ if (!trimmed) return value;
1277
+ const withScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed) ? trimmed : `https://${trimmed}`;
1278
+ let url;
1279
+ try {
1280
+ url = new URL(withScheme);
1281
+ } catch {
1282
+ return value;
1283
+ }
1284
+ if (!url.hostname) return value;
1285
+ const scheme = url.protocol.replace(/:$/, "").toLowerCase();
1286
+ const host = url.hostname.toLowerCase();
1287
+ const defaultPort = scheme === "https" && url.port === "443" || scheme === "http" && url.port === "80";
1288
+ const port2 = url.port && !defaultPort ? `:${url.port}` : "";
1289
+ return `${scheme}://${host}${port2}`;
1290
+ }
1291
+ var NORMALIZERS = {
1292
+ url: normalizeUrl
1293
+ };
1294
+ function normalizeIncomingArgs(tool, args) {
1295
+ const declared = tool?.gating?.argNormalization;
1296
+ if (!declared || !args) return args;
1297
+ let out = null;
1298
+ for (const [field, form] of Object.entries(declared)) {
1299
+ const normalize = NORMALIZERS[form];
1300
+ if (!normalize) {
1301
+ console.error(
1302
+ `[Suveren MCP] Warning: ${tool?.namespacedName} declares argNormalization "${form}" for "${field}", which this gateway does not implement. Using the value as supplied.`
1303
+ );
1304
+ continue;
1305
+ }
1306
+ const value = args[field];
1307
+ if (typeof value !== "string") continue;
1308
+ const normalized = normalize(value);
1309
+ if (normalized === value) continue;
1310
+ out ??= { ...args };
1311
+ out[field] = normalized;
1312
+ }
1313
+ return out ?? args;
1314
+ }
1315
+
1273
1316
  // src/lib/scope-specificity.ts
1274
1317
  function tokenSet(value) {
1275
1318
  if (value === void 0 || value === null) return /* @__PURE__ */ new Set();
@@ -1542,7 +1585,8 @@ function denyRead(state2, tool, reason, detail, target) {
1542
1585
  return { content: [{ type: "text", text: `Read blocked by Gatekeeper: ${detail}` }], isError: true };
1543
1586
  }
1544
1587
  function createGatedToolHandler(tool, integrationManager2, state2) {
1545
- const inner = createGatedToolHandlerInner(tool, integrationManager2, state2);
1588
+ const gated = createGatedToolHandlerInner(tool, integrationManager2, state2);
1589
+ const inner = async (args) => gated(normalizeIncomingArgs(tool, args));
1546
1590
  const blocked = tool.gating?.blockedArgs ?? [];
1547
1591
  if (blocked.length === 0) return inner;
1548
1592
  return async (args) => {
@@ -3204,7 +3248,8 @@ var IntegrationManager = class {
3204
3248
  // simply carries no binding, which only surfaces when a verifier asks.
3205
3249
  contentField: ext.contentField,
3206
3250
  blockedArgs: ext.blockedArgs,
3207
- argEncoding: ext.argEncoding
3251
+ argEncoding: ext.argEncoding,
3252
+ argNormalization: ext.argNormalization
3208
3253
  };
3209
3254
  }
3210
3255
  return {
@@ -415,6 +415,28 @@ interface AgentProfile {
415
415
  * know why 0.5 exists. Absent → surfaces show the version alone.
416
416
  */
417
417
  whatsNew?: string;
418
+ /**
419
+ * Whether receipts under this profile may be looked up BY THEIR CONTENT — a
420
+ * verifier holding the content supplies its hash and learns which receipts
421
+ * bind it, without needing a receipt id.
422
+ *
423
+ * OFF unless declared, and that default is the point. The lookup is a
424
+ * confirmation oracle: given a guess at the content it says whether that
425
+ * content was authorized. Where the bound content has low entropy this is
426
+ * disclosure, not verification — guessing a message body is hopeless,
427
+ * guessing `production` takes a second. It is the same enumeration hazard
428
+ * recorded for per-field commitments, arriving from the other direction.
429
+ *
430
+ * Enable only when the bound content is unguessable enough that producing it
431
+ * is equivalent to already having it: prose, an artifact URL, a whole record
432
+ * payload. Never for a binding over a short value drawn from a small set.
433
+ *
434
+ * Why it must exist at all: most consequential actions cannot carry their
435
+ * receipt id. A released build was built before the receipt existed, a
436
+ * content-addressed artifact would change identity if the id were added, and
437
+ * a forwarded message has usually lost the footer that carried it.
438
+ */
439
+ receipt_lookup?: boolean;
418
440
  /**
419
441
  * v0.3 frame schema (deprecated, kept for backward compat).
420
442
  * Used when boundsSchema is not present.
@@ -415,6 +415,28 @@ interface AgentProfile {
415
415
  * know why 0.5 exists. Absent → surfaces show the version alone.
416
416
  */
417
417
  whatsNew?: string;
418
+ /**
419
+ * Whether receipts under this profile may be looked up BY THEIR CONTENT — a
420
+ * verifier holding the content supplies its hash and learns which receipts
421
+ * bind it, without needing a receipt id.
422
+ *
423
+ * OFF unless declared, and that default is the point. The lookup is a
424
+ * confirmation oracle: given a guess at the content it says whether that
425
+ * content was authorized. Where the bound content has low entropy this is
426
+ * disclosure, not verification — guessing a message body is hopeless,
427
+ * guessing `production` takes a second. It is the same enumeration hazard
428
+ * recorded for per-field commitments, arriving from the other direction.
429
+ *
430
+ * Enable only when the bound content is unguessable enough that producing it
431
+ * is equivalent to already having it: prose, an artifact URL, a whole record
432
+ * payload. Never for a binding over a short value drawn from a small set.
433
+ *
434
+ * Why it must exist at all: most consequential actions cannot carry their
435
+ * receipt id. A released build was built before the receipt existed, a
436
+ * content-addressed artifact would change identity if the id were added, and
437
+ * a forwarded message has usually lost the footer that carried it.
438
+ */
439
+ receipt_lookup?: boolean;
418
440
  /**
419
441
  * v0.3 frame schema (deprecated, kept for backward compat).
420
442
  * Used when boundsSchema is not present.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanagencyp/hap-core",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Core types, cryptographic primitives, and verification logic for the Human Agency Protocol",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -427,6 +427,29 @@ export interface AgentProfile {
427
427
  */
428
428
  whatsNew?: string;
429
429
 
430
+ /**
431
+ * Whether receipts under this profile may be looked up BY THEIR CONTENT — a
432
+ * verifier holding the content supplies its hash and learns which receipts
433
+ * bind it, without needing a receipt id.
434
+ *
435
+ * OFF unless declared, and that default is the point. The lookup is a
436
+ * confirmation oracle: given a guess at the content it says whether that
437
+ * content was authorized. Where the bound content has low entropy this is
438
+ * disclosure, not verification — guessing a message body is hopeless,
439
+ * guessing `production` takes a second. It is the same enumeration hazard
440
+ * recorded for per-field commitments, arriving from the other direction.
441
+ *
442
+ * Enable only when the bound content is unguessable enough that producing it
443
+ * is equivalent to already having it: prose, an artifact URL, a whole record
444
+ * payload. Never for a binding over a short value drawn from a small set.
445
+ *
446
+ * Why it must exist at all: most consequential actions cannot carry their
447
+ * receipt id. A released build was built before the receipt existed, a
448
+ * content-addressed artifact would change identity if the id were added, and
449
+ * a forwarded message has usually lost the footer that carried it.
450
+ */
451
+ receipt_lookup?: boolean;
452
+
430
453
  /**
431
454
  * v0.3 frame schema (deprecated, kept for backward compat).
432
455
  * Used when boundsSchema is not present.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suveren/gateway",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Suveren gateway — local agent gateway built in compliance with the Human Agency Protocol (HAP). Runs the UI, control plane, and MCP server in one Node process.",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -164,5 +164,6 @@
164
164
  "content_binding": {
165
165
  "version": "1",
166
166
  "kind": "text"
167
- }
167
+ },
168
+ "receipt_lookup": true
168
169
  }