@suveren/gateway 0.2.7 → 0.2.8

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.
@@ -904,6 +904,54 @@ function buildMandateBrief(opts) {
904
904
  // src/tools/authorizations.ts
905
905
  import { getProfile as getProfile2 } from "@hap/core";
906
906
 
907
+ // src/lib/receipt-footer.ts
908
+ var AS_BASE = (process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai").replace(/\/$/, "");
909
+ var FOOTER_PROFILES = /* @__PURE__ */ new Set(["email", "calendar", "publish"]);
910
+ var CONTENT_FIELD_CANDIDATES = ["body", "text", "description"];
911
+ var FOOTER_MARKER = "\u2014 Sent by an AI agent via Suveren";
912
+ function shouldAttachFooter() {
913
+ return true;
914
+ }
915
+ function verifyUrl(receiptId) {
916
+ return `${AS_BASE}/r/${receiptId}`;
917
+ }
918
+ function footerText(receiptId) {
919
+ return `
920
+
921
+ ${FOOTER_MARKER}. Verify: ${verifyUrl(receiptId)}`;
922
+ }
923
+ function isStringType(t) {
924
+ return t === "string" || Array.isArray(t) && t.includes("string");
925
+ }
926
+ function detectContentField(tool) {
927
+ const schema = tool.inputSchema;
928
+ const props = schema?.properties ?? {};
929
+ for (const candidate of CONTENT_FIELD_CANDIDATES) {
930
+ const prop = props[candidate];
931
+ if (prop && isStringType(prop.type)) return candidate;
932
+ }
933
+ return null;
934
+ }
935
+ function stripFooter(value) {
936
+ const i = value.indexOf(FOOTER_MARKER);
937
+ if (i === -1) return value;
938
+ return value.slice(0, i).replace(/\n+$/, "");
939
+ }
940
+ function appendVerificationFooter(tool, args, receiptId) {
941
+ const profile = tool.gating?.profile;
942
+ if (!profile || !FOOTER_PROFILES.has(profile)) return args;
943
+ if (typeof args.raw === "string" && args.raw.trim().length > 0) {
944
+ console.error(
945
+ `[Suveren MCP] ${tool.namespacedName}: 'raw' message present \u2014 skipping verification footer.`
946
+ );
947
+ return args;
948
+ }
949
+ const field = detectContentField(tool);
950
+ if (!field) return args;
951
+ const current = typeof args[field] === "string" ? args[field] : "";
952
+ return { ...args, [field]: stripFooter(current) + footerText(receiptId) };
953
+ }
954
+
907
955
  // src/lib/tool-proxy.ts
908
956
  import { readFile } from "fs/promises";
909
957
  import { extname } from "path";
@@ -1064,6 +1112,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1064
1112
  };
1065
1113
  }
1066
1114
  }
1115
+ let receiptId;
1067
1116
  try {
1068
1117
  const actionType = typeof execution.action_type === "string" ? execution.action_type : void 0;
1069
1118
  if (!actionType) {
@@ -1071,7 +1120,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1071
1120
  `[Suveren MCP] Warning: tool ${tool.namespacedName} has no action_type in staticExecution. Bounds check may be skipped. Fix the integration manifest.`
1072
1121
  );
1073
1122
  }
1074
- await state2.spClient.postReceipt({
1123
+ const { receipt } = await state2.spClient.postReceipt({
1075
1124
  // v0.5: send the bare content address; the AS reconstructs the
1076
1125
  // per-user storage key. Fall back to frameHash only for legacy
1077
1126
  // (pre-v0.4) records that predate bounds_hash.
@@ -1083,6 +1132,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1083
1132
  amount: typeof execution.amount === "number" ? execution.amount : void 0,
1084
1133
  idempotencyKey: randomUUID()
1085
1134
  });
1135
+ receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
1086
1136
  } catch (err) {
1087
1137
  if (err instanceof SPReceiptError && err.statusCode === 409) {
1088
1138
  const spBody = err.body;
@@ -1160,7 +1210,8 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
1160
1210
  execution: { ...execution },
1161
1211
  timestamp: Math.floor(Date.now() / 1e3)
1162
1212
  });
1163
- return integrationManager2.callTool(tool.integrationId, tool.originalName, args);
1213
+ const outgoingArgs = shouldAttachFooter() && receiptId ? appendVerificationFooter(tool, args, receiptId) : args;
1214
+ return integrationManager2.callTool(tool.integrationId, tool.originalName, outgoingArgs);
1164
1215
  }
1165
1216
  const reasons = result.errors.map((e) => {
1166
1217
  if (e.code === "BOUND_EXCEEDED") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suveren/gateway",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
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",