@astrasyncai/verification-gateway 5.19.0 → 5.21.0

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 (111) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +53 -4
  3. package/dist/adapter-interface/interface.d.mts +2 -2
  4. package/dist/adapter-interface/interface.d.ts +2 -2
  5. package/dist/adapters/express.d.mts +1 -1
  6. package/dist/adapters/express.d.ts +1 -1
  7. package/dist/adapters/express.js +65 -16
  8. package/dist/adapters/express.js.map +1 -1
  9. package/dist/adapters/express.mjs +65 -16
  10. package/dist/adapters/express.mjs.map +1 -1
  11. package/dist/adapters/mcp.d.mts +23 -1
  12. package/dist/adapters/mcp.d.ts +23 -1
  13. package/dist/adapters/mcp.js +91 -16
  14. package/dist/adapters/mcp.js.map +1 -1
  15. package/dist/adapters/mcp.mjs +91 -16
  16. package/dist/adapters/mcp.mjs.map +1 -1
  17. package/dist/adapters/nextjs.d.mts +1 -1
  18. package/dist/adapters/nextjs.d.ts +1 -1
  19. package/dist/adapters/nextjs.js +57 -11
  20. package/dist/adapters/nextjs.js.map +1 -1
  21. package/dist/adapters/nextjs.mjs +57 -11
  22. package/dist/adapters/nextjs.mjs.map +1 -1
  23. package/dist/adapters/sdk.d.mts +35 -2
  24. package/dist/adapters/sdk.d.ts +35 -2
  25. package/dist/adapters/sdk.js +110 -11
  26. package/dist/adapters/sdk.js.map +1 -1
  27. package/dist/adapters/sdk.mjs +110 -11
  28. package/dist/adapters/sdk.mjs.map +1 -1
  29. package/dist/agent/index.d.mts +1 -1
  30. package/dist/agent/index.d.ts +1 -1
  31. package/dist/agent/index.js +1 -1
  32. package/dist/agent/index.js.map +1 -1
  33. package/dist/agent/index.mjs +1 -1
  34. package/dist/agent/index.mjs.map +1 -1
  35. package/dist/bin/astrasync-claude-hook.js +50 -12
  36. package/dist/bin/astrasync-codex-hook.js +50 -12
  37. package/dist/bin/astrasync-guard.js +50 -12
  38. package/dist/bin/astrasync.js +65 -13
  39. package/dist/browser/background.js +50 -12
  40. package/dist/browser/background.js.map +1 -1
  41. package/dist/browser/background.mjs +50 -12
  42. package/dist/browser/background.mjs.map +1 -1
  43. package/dist/cli/index.js +1 -1
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/cli/index.mjs +1 -1
  46. package/dist/cli/index.mjs.map +1 -1
  47. package/dist/codex/index.js +50 -12
  48. package/dist/codex/index.js.map +1 -1
  49. package/dist/codex/index.mjs +50 -12
  50. package/dist/codex/index.mjs.map +1 -1
  51. package/dist/cursor/extension.js +50 -12
  52. package/dist/cursor/extension.js.map +1 -1
  53. package/dist/cursor/extension.mjs +50 -12
  54. package/dist/cursor/extension.mjs.map +1 -1
  55. package/dist/edge-config.d.mts +1 -1
  56. package/dist/edge-config.d.ts +1 -1
  57. package/dist/edge-config.js +1 -1
  58. package/dist/edge-config.js.map +1 -1
  59. package/dist/edge-config.mjs +1 -1
  60. package/dist/edge-config.mjs.map +1 -1
  61. package/dist/edge-core/index.d.mts +1 -1
  62. package/dist/edge-core/index.d.ts +1 -1
  63. package/dist/edge-core/index.js +49 -11
  64. package/dist/edge-core/index.js.map +1 -1
  65. package/dist/edge-core/index.mjs +49 -11
  66. package/dist/edge-core/index.mjs.map +1 -1
  67. package/dist/gateway/gateway.d.mts +2 -2
  68. package/dist/gateway/gateway.d.ts +2 -2
  69. package/dist/gateway/gateway.js +50 -12
  70. package/dist/gateway/gateway.js.map +1 -1
  71. package/dist/gateway/gateway.mjs +50 -12
  72. package/dist/gateway/gateway.mjs.map +1 -1
  73. package/dist/git-trigger/git-hooks.d.mts +1 -1
  74. package/dist/git-trigger/git-hooks.d.ts +1 -1
  75. package/dist/index.d.mts +286 -8
  76. package/dist/index.d.ts +286 -8
  77. package/dist/index.js +532 -49
  78. package/dist/index.js.map +1 -1
  79. package/dist/index.mjs +522 -49
  80. package/dist/index.mjs.map +1 -1
  81. package/dist/local-evaluator/evaluator.d.mts +2 -2
  82. package/dist/local-evaluator/evaluator.d.ts +2 -2
  83. package/dist/registration/index.d.mts +11 -2
  84. package/dist/registration/index.d.ts +11 -2
  85. package/dist/registration/index.js +1 -1
  86. package/dist/registration/index.js.map +1 -1
  87. package/dist/registration/index.mjs +1 -1
  88. package/dist/registration/index.mjs.map +1 -1
  89. package/dist/transport/index.d.mts +1 -1
  90. package/dist/transport/index.d.ts +1 -1
  91. package/dist/transport/index.js +1 -1
  92. package/dist/transport/index.js.map +1 -1
  93. package/dist/transport/index.mjs +1 -1
  94. package/dist/transport/index.mjs.map +1 -1
  95. package/dist/{types-BoGjyon0.d.mts → types-B4zhOAlq.d.mts} +30 -1
  96. package/dist/{types-BRz2U0Pn.d.ts → types-B_HLhZ9n.d.ts} +1 -1
  97. package/dist/{types-BfILnheI.d.mts → types-CU17R4Nr.d.mts} +11 -0
  98. package/dist/{types-BfILnheI.d.ts → types-CU17R4Nr.d.ts} +11 -0
  99. package/dist/{types-eWyZuz-3.d.ts → types-CtBXnWuV.d.ts} +30 -1
  100. package/dist/{types-DqWPkiYl.d.mts → types-DHHyXtuZ.d.mts} +30 -1
  101. package/dist/{types-DqWPkiYl.d.ts → types-DHHyXtuZ.d.ts} +30 -1
  102. package/dist/{types-CfBpm3w5.d.mts → types-Djpz-L0w.d.mts} +1 -1
  103. package/dist/ui/index.d.mts +1 -1
  104. package/dist/ui/index.d.ts +1 -1
  105. package/dist/verify.d.mts +28 -3
  106. package/dist/verify.d.ts +28 -3
  107. package/dist/verify.js +71 -11
  108. package/dist/verify.js.map +1 -1
  109. package/dist/verify.mjs +69 -11
  110. package/dist/verify.mjs.map +1 -1
  111. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ __export(src_exports, {
44
44
  DECLARED_SOURCE_MAX: () => DECLARED_SOURCE_MAX,
45
45
  DEFAULT_EDGE_CONFIG: () => DEFAULT_EDGE_CONFIG,
46
46
  DEFAULT_IDEMPOTENCY_TTL_MS: () => DEFAULT_IDEMPOTENCY_TTL_MS,
47
+ ISSUER_PROGRAM_HEADER: () => ISSUER_PROGRAM_HEADER,
47
48
  KYDRequiredError: () => KYDRequiredError,
48
49
  MAX_HEADERS: () => MAX_HEADERS,
49
50
  MAX_HEADER_VALUE_BYTES: () => MAX_HEADER_VALUE_BYTES,
@@ -102,6 +103,7 @@ __export(src_exports, {
102
103
  closedPaymentMandate: () => closedPaymentMandate,
103
104
  contentDigest: () => contentDigest,
104
105
  createAcpCheckoutKit: () => createAcpCheckoutKit,
106
+ createAgentCommerce: () => createAgentCommerce,
105
107
  createKbHop: () => createKbHop,
106
108
  createMcpMiddleware: () => createMcpMiddleware,
107
109
  createUcpCheckoutKit: () => createUcpCheckoutKit,
@@ -123,6 +125,7 @@ __export(src_exports, {
123
125
  fetchRoutes: () => fetchRoutes,
124
126
  fetchUcpProfile: () => fetchUcpProfile,
125
127
  finishIdempotent: () => finishIdempotent,
128
+ fromFeed: () => fromFeed,
126
129
  getCachedWellKnownUrls: () => getCachedWellKnownUrls,
127
130
  getEdgeConfig: () => getEdgeConfig,
128
131
  getTrustLevel: () => getTrustLevel,
@@ -133,10 +136,12 @@ __export(src_exports, {
133
136
  isEdgeConfig: () => isEdgeConfig,
134
137
  isRejectedIdentityClaim: () => isRejectedIdentityClaim,
135
138
  isRetryableSettlementError: () => isRetryableSettlementError,
139
+ isSharedVerdict: () => isSharedVerdict,
136
140
  isSpecialUseIp: () => isSpecialUseIp,
137
141
  isVerificationUnavailable: () => isVerificationUnavailable,
138
142
  isVerifiedHopValidFor: () => isVerifiedHopValidFor,
139
143
  issueDelegateSdJwt: () => issueDelegateSdJwt,
144
+ issuerOfferPrice: () => issuerOfferPrice,
140
145
  matchEdgePathRule: () => matchEdgePathRule,
141
146
  matchPlatformSignature: () => matchPlatformSignature,
142
147
  matchRoutePattern: () => matchRoutePattern,
@@ -144,8 +149,10 @@ __export(src_exports, {
144
149
  negotiateUcp: () => negotiateUcp,
145
150
  nextjs: () => nextjs_exports,
146
151
  normalizeDeclaredSource: () => normalizeDeclaredSource,
152
+ onVerificationResult: () => onVerificationResult,
147
153
  openCheckoutMandate: () => openCheckoutMandate,
148
154
  openPaymentMandate: () => openPaymentMandate,
155
+ parseFeed: () => parseFeed,
149
156
  parseLocalPolicyYaml: () => parseYaml,
150
157
  parseMppCredential: () => parseMppCredential,
151
158
  parseUcpAgentHeader: () => parseUcpAgentHeader,
@@ -154,6 +161,7 @@ __export(src_exports, {
154
161
  parseX402Payment: () => parseX402Payment,
155
162
  pollStepUpStatus: () => pollStepUpStatus,
156
163
  prefetchWellKnown: () => prefetchWellKnown,
164
+ priceForAgent: () => priceForAgent,
157
165
  quickVerify: () => quickVerify,
158
166
  recordDecision: () => recordDecision2,
159
167
  reportAttempt: () => reportAttempt,
@@ -167,6 +175,7 @@ __export(src_exports, {
167
175
  sdkFetch: () => sdkFetch,
168
176
  sendUcpWebhook: () => sendUcpWebhook,
169
177
  serializeVerifiedHop: () => serializeVerifiedHop,
178
+ serveAgentCatalog: () => serveAgentCatalog,
170
179
  setMcpMeta: () => setMcpMeta,
171
180
  signHttpRequest: () => signHttpRequest,
172
181
  signRootMandate: () => signRootMandate,
@@ -177,6 +186,7 @@ __export(src_exports, {
177
186
  ucpEscalation: () => ucpEscalation,
178
187
  ucpFailure: () => ucpFailure,
179
188
  ucpProfileHandler: () => ucpProfileHandler,
189
+ ucpProfileKeys: () => ucpProfileKeys,
180
190
  ucpRequiredComponents: () => ucpRequiredComponents,
181
191
  unavailableRetryAfterSeconds: () => unavailableRetryAfterSeconds,
182
192
  validateLocalPolicy: () => validatePolicy,
@@ -218,7 +228,7 @@ function getTrustLevel(score) {
218
228
  }
219
229
 
220
230
  // src/version.ts
221
- var SDK_VERSION = "5.19.0";
231
+ var SDK_VERSION = "5.21.0";
222
232
 
223
233
  // src/http.ts
224
234
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -500,6 +510,7 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
500
510
  }
501
511
  function clearCache() {
502
512
  verificationCache.clear();
513
+ inflightVerifies.clear();
503
514
  }
504
515
  function extractCredentials(headers, query) {
505
516
  const credentials = {};
@@ -673,6 +684,9 @@ async function callVerifyAccessAPI(config, request) {
673
684
  if (requestData.enableRuntimeChallenge)
674
685
  body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
675
686
  if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
687
+ if (config.actingProof?.connectorToken) {
688
+ body.actingProof = { connectorToken: config.actingProof.connectorToken };
689
+ }
676
690
  if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
677
691
  if (requestData.createSession) body.createSession = requestData.createSession;
678
692
  if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
@@ -840,22 +854,46 @@ async function verify(config, request, options) {
840
854
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
841
855
  );
842
856
  }
843
- const uncacheable = Boolean(request.commerceArtifacts);
857
+ const uncacheable = Boolean(request.commerceArtifacts) || Boolean(mergedConfig.actingProof);
844
858
  if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
845
859
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
846
860
  if (cached) {
847
861
  if (mergedConfig.debug) {
848
862
  console.log("[VerificationGateway] Returning cached result");
849
863
  }
850
- const hit = { ...cached };
851
- if (request.attemptId) {
852
- hit.attemptId = request.attemptId;
853
- } else {
854
- delete hit.attemptId;
855
- }
856
- return hit;
864
+ return sharedVerdict(cached, request);
865
+ }
866
+ }
867
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
868
+ const key = getCacheKey(request, mergedConfig.counterpartyId);
869
+ const pending = inflightVerifies.get(key);
870
+ if (pending) return sharedVerdict(await pending, request);
871
+ const flight = verifyViaApi(mergedConfig, request, urls, uncacheable);
872
+ inflightVerifies.set(key, flight);
873
+ try {
874
+ return await flight;
875
+ } finally {
876
+ inflightVerifies.delete(key);
857
877
  }
858
878
  }
879
+ return verifyViaApi(mergedConfig, request, urls, uncacheable);
880
+ }
881
+ var inflightVerifies = /* @__PURE__ */ new Map();
882
+ var sharedVerdicts = /* @__PURE__ */ new WeakSet();
883
+ function isSharedVerdict(result) {
884
+ return sharedVerdicts.has(result);
885
+ }
886
+ function sharedVerdict(source, request) {
887
+ const hit = { ...source };
888
+ if (request.attemptId) {
889
+ hit.attemptId = request.attemptId;
890
+ } else {
891
+ delete hit.attemptId;
892
+ }
893
+ sharedVerdicts.add(hit);
894
+ return hit;
895
+ }
896
+ async function verifyViaApi(mergedConfig, request, urls, uncacheable) {
859
897
  const enrichedRequest = { ...request };
860
898
  if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
861
899
  enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
@@ -934,7 +972,9 @@ async function verify(config, request, options) {
934
972
  fulfilment: apiResponse.fulfilment,
935
973
  mandates: apiResponse.mandates,
936
974
  kyaToken: apiResponse.kyaToken,
937
- aep: apiResponse.aep
975
+ aep: apiResponse.aep,
976
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
977
+ warnings: apiResponse.warnings
938
978
  };
939
979
  return result2;
940
980
  }
@@ -1002,7 +1042,9 @@ async function verify(config, request, options) {
1002
1042
  // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
1003
1043
  mandates: apiResponse.mandates,
1004
1044
  kyaToken: apiResponse.kyaToken,
1005
- aep: apiResponse.aep
1045
+ aep: apiResponse.aep,
1046
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
1047
+ warnings: apiResponse.warnings
1006
1048
  };
1007
1049
  if (result.recommendation === "deny") {
1008
1050
  result.policyAllowed = false;
@@ -1028,8 +1070,24 @@ async function verify(config, request, options) {
1028
1070
  cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
1029
1071
  }
1030
1072
  }
1073
+ notifyVerificationListeners(request, result);
1031
1074
  return result;
1032
1075
  }
1076
+ var verificationListeners = /* @__PURE__ */ new Set();
1077
+ function onVerificationResult(listener) {
1078
+ verificationListeners.add(listener);
1079
+ return () => {
1080
+ verificationListeners.delete(listener);
1081
+ };
1082
+ }
1083
+ function notifyVerificationListeners(request, result) {
1084
+ for (const listener of verificationListeners) {
1085
+ try {
1086
+ listener(result, request);
1087
+ } catch {
1088
+ }
1089
+ }
1090
+ }
1033
1091
  async function recordDecision(config, sessionId, decision, reason) {
1034
1092
  const headers = { "Content-Type": "application/json" };
1035
1093
  if (config.apiKey) {
@@ -1094,6 +1152,7 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
1094
1152
  headers["X-API-Key"] = config.apiKey;
1095
1153
  }
1096
1154
  try {
1155
+ headers["X-AstraSync-SDK-Version"] = SDK_VERSION;
1097
1156
  const response = await sdkFetch(
1098
1157
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes` + (options.codeGates?.length ? `?codeGates=${options.codeGates.map(encodeURIComponent).join(",")}` : ""),
1099
1158
  { method: "GET", headers }
@@ -1108,6 +1167,13 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
1108
1167
  return null;
1109
1168
  }
1110
1169
  const body = await response.json();
1170
+ const type2 = body.data?.counterpartyType;
1171
+ if (typeof type2 === "string" && options.onEndpointType) {
1172
+ try {
1173
+ options.onEndpointType(type2);
1174
+ } catch {
1175
+ }
1176
+ }
1111
1177
  return body.data?.routes ?? [];
1112
1178
  } catch {
1113
1179
  return null;
@@ -3031,14 +3097,14 @@ function createMiddleware(options) {
3031
3097
  res.setHeader("X-Astra-Gateway-Mode", "enforced");
3032
3098
  res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
3033
3099
  }
3034
- if (shouldRecordDecisions && sessionId) {
3100
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
3035
3101
  recordDecision(config, sessionId, "granted").catch(() => {
3036
3102
  });
3037
3103
  }
3038
3104
  return next();
3039
3105
  }
3040
3106
  if (!result.policyAllowed) {
3041
- if (shouldRecordDecisions && sessionId) {
3107
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
3042
3108
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
3043
3109
  });
3044
3110
  }
@@ -3051,7 +3117,7 @@ function createMiddleware(options) {
3051
3117
  }
3052
3118
  if (requiresHumanApproval(result)) {
3053
3119
  annotateApprovalRequired(result);
3054
- if (shouldRecordDecisions && sessionId) {
3120
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
3055
3121
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
3056
3122
  });
3057
3123
  }
@@ -3076,7 +3142,7 @@ function createMiddleware(options) {
3076
3142
  documentationUrl: wellKnownUrls.documentationUrl
3077
3143
  };
3078
3144
  }
3079
- if (shouldRecordDecisions && sessionId) {
3145
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
3080
3146
  recordDecision(config, sessionId, "denied", trustFailure.message).catch(() => {
3081
3147
  });
3082
3148
  }
@@ -3085,7 +3151,7 @@ function createMiddleware(options) {
3085
3151
  return;
3086
3152
  }
3087
3153
  }
3088
- if (shouldRecordDecisions && sessionId) {
3154
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
3089
3155
  recordDecision(config, sessionId, "granted").catch(() => {
3090
3156
  });
3091
3157
  }
@@ -3933,6 +3999,66 @@ var VerificationGatewayClient = class {
3933
3999
  }
3934
4000
  return json4.data;
3935
4001
  }
4002
+ /**
4003
+ * 5.20.0 (round 29.1): ask AstraSync to send ONE signed step-up event to the
4004
+ * endpoint's Events URL now — `POST /api/endpoints/:id/events/test`.
4005
+ * Delivered synchronously, outside the event outbox (random `eventId`, no
4006
+ * retries), signed with the endpoint's webhook secret like a real event.
4007
+ * The payload is synthetic (`approvalId: "test_…"`); a kit acknowledges and
4008
+ * ignores it.
4009
+ *
4010
+ * 5.21.0 (shops' item 48): synthetic only. Sending a real held approval's
4011
+ * payload (which releases or re-drives that checkout) is dashboard-only, so
4012
+ * a leaked API key can't do it.
4013
+ *
4014
+ * Resolves even when your endpoint refused the event (`delivered: false`,
4015
+ * with `responseCode` / `error`).
4016
+ */
4017
+ async sendTestEvent(endpointId, options) {
4018
+ return this.endpointCall("POST", endpointId, "/events/test", { type: options.type });
4019
+ }
4020
+ async endpointCall(method, endpointId, suffix, body) {
4021
+ const id = endpointId ?? this.config.counterpartyId;
4022
+ if (!id) {
4023
+ throw new AstraSyncError(
4024
+ "An endpoint id (ASTRAE-\u2026) is required: pass it, or set counterpartyId on the client",
4025
+ 400,
4026
+ "VALIDATION"
4027
+ );
4028
+ }
4029
+ if (!this.config.apiKey) {
4030
+ throw new AstraSyncError("This call needs the client apiKey", 401, "AUTH_REQUIRED");
4031
+ }
4032
+ const base = this.config.apiBaseUrl.replace(/\/+$/, "");
4033
+ const url = `${base}/endpoints/${encodeURIComponent(id)}${suffix}`;
4034
+ let res;
4035
+ try {
4036
+ res = await sdkFetch(url, {
4037
+ method,
4038
+ headers: {
4039
+ "Content-Type": "application/json",
4040
+ Authorization: `Bearer ${this.config.apiKey}`,
4041
+ "X-API-Key": this.config.apiKey
4042
+ },
4043
+ body: JSON.stringify(body),
4044
+ signal: AbortSignal.timeout(Math.max(this.timeout, 15e3))
4045
+ });
4046
+ } catch (err) {
4047
+ throw new AstraSyncError(
4048
+ `${method} ${suffix || "/endpoints/:id"} failed: ${err instanceof Error ? err.message : String(err)}`,
4049
+ 0,
4050
+ "network_error"
4051
+ );
4052
+ }
4053
+ const json4 = await res.json().catch(() => ({}));
4054
+ if (!res.ok || json4.success === false || json4.data === void 0) {
4055
+ const errObj = typeof json4.error === "object" ? json4.error : void 0;
4056
+ const code = json4.code ?? errObj?.code ?? (typeof json4.error === "string" ? json4.error : "unknown_error");
4057
+ const message = json4.message ?? errObj?.message ?? `HTTP ${res.status}`;
4058
+ throw new AstraSyncError(`${message} (${code})`, res.status, code);
4059
+ }
4060
+ return json4.data;
4061
+ }
3936
4062
  /**
3937
4063
  * Quick verification — checks credentials and policy in one call.
3938
4064
  *
@@ -6894,16 +7020,30 @@ function createMcpMiddleware(options) {
6894
7020
  failOnError = "closed",
6895
7021
  toolGatePrecedence = "code",
6896
7022
  routesRefreshMs,
7023
+ skipEvaluation,
6897
7024
  ...config
6898
7025
  } = options;
7026
+ const skipTools = new Set(skipEvaluation?.tools ?? []);
7027
+ const skipMethods = new Set(skipEvaluation?.methods ?? []);
6899
7028
  if (config.apiBaseUrl) {
6900
7029
  prefetchWellKnown(config.apiBaseUrl).catch(() => {
6901
7030
  });
6902
7031
  }
6903
7032
  const codeGatedTools = Object.entries(toolGates ?? {}).filter(([, g]) => g !== "observe").map(([name]) => name);
7033
+ let warnedEndpointType = false;
7034
+ const onEndpointType = (counterpartyType) => {
7035
+ if (counterpartyType === "mcp_server" || warnedEndpointType) return;
7036
+ warnedEndpointType = true;
7037
+ console.warn(
7038
+ `[VerificationGateway/MCP] counterpartyId ${config.counterpartyId} is a '${counterpartyType}' endpoint, not an 'mcp_server' endpoint. Dashboard tool rules are read from an mcp_server endpoint only: register a separate MCP endpoint for this server (same owner) and use its id here. See https://astrasync.ai/docs/mcp-integration#tool-rules.`
7039
+ );
7040
+ };
6904
7041
  const rulesCache = config.apiKey && config.counterpartyId ? createRoutesCache(config, {
6905
7042
  ...routesRefreshMs !== void 0 && { refreshMs: routesRefreshMs },
6906
- ...codeGatedTools.length && { fetchOptions: { codeGates: codeGatedTools } }
7043
+ fetchOptions: {
7044
+ ...codeGatedTools.length && { codeGates: codeGatedTools },
7045
+ onEndpointType
7046
+ }
6907
7047
  }) : void 0;
6908
7048
  return async (req, res, next) => {
6909
7049
  try {
@@ -6953,6 +7093,9 @@ function createMcpMiddleware(options) {
6953
7093
  return next();
6954
7094
  }
6955
7095
  }
7096
+ if (!parsed.toolName && skipMethods.has(parsed.method) && methodGates?.[parsed.method] === void 0) {
7097
+ return next();
7098
+ }
6956
7099
  const dashboardRules = rulesCache ? dashboardToolRules(await rulesCache.current()) : void 0;
6957
7100
  const gateDecision = resolveGate(parsed, {
6958
7101
  toolGates,
@@ -6960,6 +7103,9 @@ function createMcpMiddleware(options) {
6960
7103
  dashboardRules,
6961
7104
  toolGatePrecedence
6962
7105
  });
7106
+ if (!gateDecision.enforce && (parsed.toolName && skipTools.has(parsed.toolName) || !parsed.toolName && skipMethods.has(parsed.method))) {
7107
+ return next();
7108
+ }
6963
7109
  const credentials = extractCredentials(
6964
7110
  req.headers,
6965
7111
  req.query
@@ -7016,8 +7162,14 @@ function createMcpMiddleware(options) {
7016
7162
  const forwardedFor = req.headers["x-forwarded-for"];
7017
7163
  const forwardedForStr = Array.isArray(forwardedFor) ? forwardedFor.join(", ") : forwardedFor;
7018
7164
  const sourceIp = req.ip ?? (forwardedForStr ? forwardedForStr.split(",")[0].trim() : void 0);
7165
+ const attemptRaw = readSingleHeader(req.headers["x-astra-attempt-id"]);
7166
+ const metaAttempt = req.body?.params?._meta?.astrasync?.attemptId;
7167
+ const attemptId = [attemptRaw, metaAttempt].find(
7168
+ (v) => typeof v === "string" && /^att_[0-9a-f]{32}$/.test(v)
7169
+ );
7019
7170
  const verificationRequest = {
7020
7171
  credentials,
7172
+ ...attemptId && { attemptId },
7021
7173
  purpose: pdlss.purpose,
7022
7174
  action: pdlss.action,
7023
7175
  resource: pdlss.resource,
@@ -7079,14 +7231,14 @@ function createMcpMiddleware(options) {
7079
7231
  res.setHeader("X-Astra-Gateway-Mode", "enforced");
7080
7232
  res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
7081
7233
  }
7082
- if (shouldRecordDecisions && sessionId) {
7234
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
7083
7235
  recordDecision(config, sessionId, "granted").catch(() => {
7084
7236
  });
7085
7237
  }
7086
7238
  return next();
7087
7239
  }
7088
7240
  if (!result.policyAllowed) {
7089
- if (shouldRecordDecisions && sessionId) {
7241
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
7090
7242
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
7091
7243
  });
7092
7244
  }
@@ -7096,7 +7248,7 @@ function createMcpMiddleware(options) {
7096
7248
  }
7097
7249
  if (requiresHumanApproval(result)) {
7098
7250
  annotateApprovalRequired(result);
7099
- if (shouldRecordDecisions && sessionId) {
7251
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
7100
7252
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
7101
7253
  });
7102
7254
  }
@@ -7114,7 +7266,7 @@ function createMcpMiddleware(options) {
7114
7266
  })
7115
7267
  );
7116
7268
  }
7117
- if (shouldRecordDecisions && sessionId) {
7269
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
7118
7270
  recordDecision(config, sessionId, "granted").catch(() => {
7119
7271
  });
7120
7272
  }
@@ -9054,13 +9206,13 @@ function requireLoader() {
9054
9206
  throwError(state, "ill-formed argument of the YAML directive");
9055
9207
  }
9056
9208
  const major = parseInt(match[1], 10);
9057
- const minor = parseInt(match[2], 10);
9209
+ const minor2 = parseInt(match[2], 10);
9058
9210
  if (major !== 1) {
9059
9211
  throwError(state, "unacceptable YAML version of the document");
9060
9212
  }
9061
9213
  state.version = args[0];
9062
- state.checkLineBreaks = minor < 2;
9063
- if (minor !== 1 && minor !== 2) {
9214
+ state.checkLineBreaks = minor2 < 2;
9215
+ if (minor2 !== 1 && minor2 !== 2) {
9064
9216
  throwWarning(state, "unsupported YAML version of the document");
9065
9217
  }
9066
9218
  },
@@ -11123,6 +11275,16 @@ function checkUcpAgentBinding(header, identities) {
11123
11275
 
11124
11276
  // src/ucp/profile.ts
11125
11277
  var UCP_VERSION = "2026-08-25";
11278
+ function ucpProfileKeys(profile) {
11279
+ const out = [];
11280
+ for (const list of [profile?.keys, profile?.signing_keys]) {
11281
+ if (!Array.isArray(list)) continue;
11282
+ for (const k of list) {
11283
+ if (k && typeof k === "object" && k.use !== "enc") out.push(k);
11284
+ }
11285
+ }
11286
+ return out;
11287
+ }
11126
11288
  var SPEC = `https://ucp.dev/${UCP_VERSION}/specification`;
11127
11289
  var SCHEMAS = `https://ucp.dev/${UCP_VERSION}/schemas`;
11128
11290
  function astrasyncPaymentHandlers(handlers, specBase = "https://astrasync.ai/ucp/handlers") {
@@ -11546,6 +11708,228 @@ async function sendUcpWebhook(opts) {
11546
11708
  return { ok: false, status: last, attempts };
11547
11709
  }
11548
11710
 
11711
+ // src/ucp/agent-commerce.ts
11712
+ var ISSUER_PROGRAM_HEADER = "X-Astra-Issuer-Program";
11713
+ function issuerOfferPrice(priceMinor, percentOff) {
11714
+ if (!percentOff) return priceMinor;
11715
+ const pct = Math.min(100, Math.max(0, percentOff));
11716
+ return Math.max(0, Math.round(priceMinor * (1 - pct / 100)));
11717
+ }
11718
+ function priceForAgent(rules, sku, basePriceMinor, ctx) {
11719
+ const rule = rules?.products?.[sku];
11720
+ let price = rule?.priceMinor ?? basePriceMinor;
11721
+ if (ctx.verified && rules?.verifiedPercentOff) {
11722
+ price = issuerOfferPrice(price, rules.verifiedPercentOff);
11723
+ }
11724
+ const offer = ctx.verified && ctx.issuerProgram && rule?.issuerEligible !== false ? rules?.offers?.find((o) => o.program === ctx.issuerProgram) : void 0;
11725
+ if (offer?.pricePercentOff) {
11726
+ return {
11727
+ priceMinor: issuerOfferPrice(price, offer.pricePercentOff),
11728
+ regularMinor: price,
11729
+ issuerProgram: offer.program
11730
+ };
11731
+ }
11732
+ return { priceMinor: price };
11733
+ }
11734
+ function visibleToAgent(rules, sku, ctx) {
11735
+ if (!ctx.verified && rules?.hideFromUnverified) return false;
11736
+ return rules?.products?.[sku]?.visible !== false;
11737
+ }
11738
+ function createAgentCommerce(opts) {
11739
+ const f = opts.fetch ?? sdkFetch;
11740
+ let current = null;
11741
+ let kitReport;
11742
+ const refreshMs = opts.refreshMs ?? 5 * 6e4;
11743
+ async function refresh() {
11744
+ try {
11745
+ const headers = {
11746
+ Accept: "application/json",
11747
+ Authorization: `Bearer ${opts.apiKey}`,
11748
+ "X-AstraSync-SDK-Version": SDK_VERSION
11749
+ };
11750
+ if (kitReport) {
11751
+ headers["X-AstraSync-Kit-Report"] = Buffer.from(JSON.stringify(kitReport)).toString(
11752
+ "base64url"
11753
+ );
11754
+ }
11755
+ const res = await f(
11756
+ `${opts.apiBaseUrl.replace(/\/+$/, "")}/endpoints/${encodeURIComponent(
11757
+ opts.counterpartyId
11758
+ )}/agent-commerce`,
11759
+ { method: "GET", headers }
11760
+ );
11761
+ if (!res.ok) return;
11762
+ const body = await res.json();
11763
+ if (body.data && typeof body.data.version === "number") current = body.data;
11764
+ } catch {
11765
+ }
11766
+ }
11767
+ const timer = refreshMs > 0 ? setInterval(() => {
11768
+ void refresh();
11769
+ }, refreshMs) : null;
11770
+ timer?.unref?.();
11771
+ void refresh();
11772
+ return {
11773
+ rules: () => current,
11774
+ refresh,
11775
+ price: (sku, base, ctx) => priceForAgent(current, sku, base, ctx),
11776
+ visible: (sku, ctx) => visibleToAgent(current, sku, ctx),
11777
+ report: (r) => {
11778
+ kitReport = { ...kitReport, ...r };
11779
+ },
11780
+ stop: () => {
11781
+ if (timer) clearInterval(timer);
11782
+ }
11783
+ };
11784
+ }
11785
+ function pricingContextFrom(headers) {
11786
+ const astraId = headers.get("x-astra-id");
11787
+ const hop = parseVerifiedHop(headers.get(MCP_VERIFIED_HOP_HEADER.toLowerCase()));
11788
+ const verified = !!astraId && (!hop || hop.astraId === astraId && Date.now() - hop.checkedAt <= MCP_VERIFIED_HOP_MAX_AGE_MS);
11789
+ const program = headers.get(ISSUER_PROGRAM_HEADER.toLowerCase());
11790
+ return {
11791
+ verified,
11792
+ ...verified && program && /^[a-z0-9-]{2,40}$/.test(program) && { issuerProgram: program }
11793
+ };
11794
+ }
11795
+ function minor(value) {
11796
+ const n = typeof value === "number" ? value : Number(String(value ?? "").replace(/[^\d.]/g, ""));
11797
+ return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) : null;
11798
+ }
11799
+ function fromFeed(url, options = {}) {
11800
+ const f = options.fetch ?? fetch;
11801
+ let cache4 = null;
11802
+ return async () => {
11803
+ if (cache4 && Date.now() - cache4.at < 6e4) return cache4.products;
11804
+ const res = await f(url, {
11805
+ headers: { Accept: "application/json, application/xml, text/csv" }
11806
+ });
11807
+ if (!res.ok) throw new Error(`Product feed ${url} answered ${res.status}`);
11808
+ const text = await res.text();
11809
+ const products = parseFeed(text, options.currency ?? "AUD");
11810
+ cache4 = { at: Date.now(), products };
11811
+ return products;
11812
+ };
11813
+ }
11814
+ function parseFeed(text, currency) {
11815
+ const trimmed = text.trimStart();
11816
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
11817
+ const json4 = JSON.parse(trimmed);
11818
+ const list = Array.isArray(json4) ? json4 : json4.products ?? json4.items ?? [];
11819
+ return list.flatMap((p) => {
11820
+ const variant = p.variants?.[0];
11821
+ const price = minor(variant?.price ?? p.price);
11822
+ const sku = String(variant?.sku || p.sku || p.id || "");
11823
+ if (!sku || price === null) return [];
11824
+ const images = Array.isArray(p.images) ? p.images.map(
11825
+ (i) => typeof i === "string" ? i : i.src ?? ""
11826
+ ) : [p.image_url, p.image].filter((x) => typeof x === "string");
11827
+ return [
11828
+ {
11829
+ sku,
11830
+ name: String(p.title ?? p.name ?? sku),
11831
+ priceMinor: price,
11832
+ currency: String(p.currency ?? currency),
11833
+ ...typeof p.description === "string" && { description: p.description },
11834
+ imageUrls: images.filter((u) => /^https:\/\//.test(u)),
11835
+ inStock: variant ? variant.available !== false : p.in_stock !== false
11836
+ }
11837
+ ];
11838
+ });
11839
+ }
11840
+ if (trimmed.startsWith("<")) {
11841
+ const items = trimmed.split(/<item[\s>]|<entry[\s>]/).slice(1);
11842
+ return items.flatMap((chunk) => {
11843
+ const tag = (name) => {
11844
+ const m = new RegExp(`<(?:g:)?${name}[^>]*>([\\s\\S]*?)</(?:g:)?${name}>`).exec(chunk);
11845
+ return m ? m[1].replace(/<!\[CDATA\[|\]\]>/g, "").trim() : void 0;
11846
+ };
11847
+ const sku = tag("id");
11848
+ const price = minor(tag("price"));
11849
+ if (!sku || price === null) return [];
11850
+ const extra = [...chunk.matchAll(/<g:additional_image_link>([^<]+)</g)].map(
11851
+ (m) => m[1].trim()
11852
+ );
11853
+ return [
11854
+ {
11855
+ sku,
11856
+ name: tag("title") ?? sku,
11857
+ priceMinor: price,
11858
+ currency: /[A-Z]{3}/.exec(tag("price") ?? "")?.[0] ?? currency,
11859
+ imageUrls: [tag("image_link"), ...extra].filter(
11860
+ (u) => !!u && /^https:\/\//.test(u)
11861
+ ),
11862
+ inStock: (tag("availability") ?? "in stock").replace(/_/g, " ") !== "out of stock"
11863
+ }
11864
+ ];
11865
+ });
11866
+ }
11867
+ const [head, ...rows] = trimmed.split(/\r?\n/).filter(Boolean);
11868
+ const cols = head.split(",").map((c) => c.trim().toLowerCase());
11869
+ const at = (r, ...names) => {
11870
+ const i = cols.findIndex((c) => names.includes(c));
11871
+ return i >= 0 ? r[i]?.trim() : void 0;
11872
+ };
11873
+ return rows.flatMap((line) => {
11874
+ const r = line.split(",");
11875
+ const sku = at(r, "id", "sku");
11876
+ const price = minor(at(r, "price"));
11877
+ if (!sku || price === null) return [];
11878
+ const image = at(r, "image_link", "image_url", "image");
11879
+ return [
11880
+ {
11881
+ sku,
11882
+ name: at(r, "title", "name") ?? sku,
11883
+ priceMinor: price,
11884
+ currency,
11885
+ imageUrls: image && /^https:\/\//.test(image) ? [image] : [],
11886
+ inStock: (at(r, "availability", "in_stock") ?? "in stock") !== "out of stock"
11887
+ }
11888
+ ];
11889
+ });
11890
+ }
11891
+ function serveAgentCatalog(opts) {
11892
+ return async (req, res) => {
11893
+ const ctx = pricingContextFrom({
11894
+ get: (name) => {
11895
+ const v = req.headers[name.toLowerCase()];
11896
+ return Array.isArray(v) ? v[0] ?? null : v ?? null;
11897
+ }
11898
+ });
11899
+ let products;
11900
+ try {
11901
+ products = await opts.products();
11902
+ } catch {
11903
+ res.status(503).json({ error: "catalog_unavailable" });
11904
+ return;
11905
+ }
11906
+ const rules = opts.commerce.rules();
11907
+ res.setHeader?.("Cache-Control", ctx.verified ? "private, no-store" : "public, max-age=60");
11908
+ res.json({
11909
+ products: products.filter((p) => opts.commerce.visible(p.sku, ctx)).map((p) => {
11910
+ const priced = opts.commerce.price(p.sku, p.priceMinor, ctx);
11911
+ const image = rules?.products?.[p.sku]?.imageUrl ?? p.imageUrls?.[0];
11912
+ return {
11913
+ sku: p.sku,
11914
+ name: p.name,
11915
+ ...p.description && { description: p.description },
11916
+ price: priced.priceMinor / 100,
11917
+ currency: p.currency,
11918
+ ...image && { image_url: image },
11919
+ in_stock: p.inStock !== false,
11920
+ ...p.url && { url: p.url },
11921
+ ...priced.issuerProgram && {
11922
+ issuer_offer: {
11923
+ program: priced.issuerProgram,
11924
+ regular_price: (priced.regularMinor ?? p.priceMinor) / 100
11925
+ }
11926
+ }
11927
+ };
11928
+ })
11929
+ });
11930
+ };
11931
+ }
11932
+
11549
11933
  // src/ucp/checkout-kit.ts
11550
11934
  var import_crypto8 = require("crypto");
11551
11935
  var import_jose3 = require("jose");
@@ -12282,6 +12666,19 @@ var resumeResult = (outcome, session) => ({
12282
12666
  });
12283
12667
 
12284
12668
  // src/ucp/checkout-kit.ts
12669
+ function withValidImageUrl(item) {
12670
+ if (item.image_url === void 0) return item;
12671
+ let ok = false;
12672
+ try {
12673
+ const u = new URL(item.image_url);
12674
+ ok = u.protocol === "https:" || u.protocol === "http:";
12675
+ } catch {
12676
+ ok = false;
12677
+ }
12678
+ if (ok) return item;
12679
+ const { image_url: _drop, ...rest } = item;
12680
+ return rest;
12681
+ }
12285
12682
  var MemoryCheckoutStore = class {
12286
12683
  constructor() {
12287
12684
  this.m = /* @__PURE__ */ new Map();
@@ -12351,6 +12748,7 @@ var clone = (v) => JSON.parse(JSON.stringify(v));
12351
12748
  var inFlight = (st) => st === "complete_in_progress" || st === "finishing";
12352
12749
  function createUcpCheckoutKit(opts) {
12353
12750
  const profile = buildUcpProfile(opts.profile);
12751
+ opts.agentCommerce?.report({ checkoutUrl: opts.profile.endpoint, protocols: ["ucp"] });
12354
12752
  const store = opts.store ?? new MemoryCheckoutStore();
12355
12753
  const idem = {
12356
12754
  store: opts.idempotencyStore ?? new MemoryIdempotencyStore(),
@@ -12524,9 +12922,7 @@ function createUcpCheckoutKit(opts) {
12524
12922
  delete s._approvalId;
12525
12923
  if (settled.status === "failed") {
12526
12924
  s.status = "ready_for_complete";
12527
- s.messages = [
12528
- ucpError("payment_failed", "The payment did not go through.", "recoverable")
12529
- ];
12925
+ s.messages = [ucpError("payment_failed", "The payment did not go through.", "recoverable")];
12530
12926
  await save(s);
12531
12927
  return;
12532
12928
  }
@@ -12560,12 +12956,14 @@ function createUcpCheckoutKit(opts) {
12560
12956
  const b = checkUcpAgentBinding(req.headers.get("ucp-agent"), identities);
12561
12957
  return b.ok ? null : b;
12562
12958
  }
12563
- async function priceLines(lines) {
12959
+ async function priceLines(lines, pricing = { verified: false }) {
12564
12960
  const out = [];
12565
12961
  const messages = [];
12962
+ let issuerProgram;
12566
12963
  for (const [i, l] of lines.entries()) {
12567
12964
  const q = Math.max(1, Math.floor(l.quantity ?? 1));
12568
- const item = l.item?.id ? await opts.catalog(l.item.id, q) : null;
12965
+ const found = l.item?.id ? await opts.catalog(l.item.id, q) : null;
12966
+ const item = found && withValidImageUrl(found);
12569
12967
  if (!item) {
12570
12968
  messages.push(
12571
12969
  ucpError(
@@ -12577,6 +12975,22 @@ function createUcpCheckoutKit(opts) {
12577
12975
  );
12578
12976
  continue;
12579
12977
  }
12978
+ if (opts.agentCommerce) {
12979
+ if (!opts.agentCommerce.visible(item.id, pricing)) {
12980
+ messages.push(
12981
+ ucpError(
12982
+ "item_unavailable",
12983
+ `Item ${item.id} is unavailable.`,
12984
+ "recoverable",
12985
+ `$.line_items[${i}]`
12986
+ )
12987
+ );
12988
+ continue;
12989
+ }
12990
+ const priced = opts.agentCommerce.price(item.id, item.price, pricing);
12991
+ item.price = priced.priceMinor;
12992
+ if (priced.issuerProgram) issuerProgram = priced.issuerProgram;
12993
+ }
12580
12994
  const amount = item.price * q;
12581
12995
  out.push({
12582
12996
  id: `li_${i + 1}`,
@@ -12592,6 +13006,7 @@ function createUcpCheckoutKit(opts) {
12592
13006
  return {
12593
13007
  lines: out,
12594
13008
  messages,
13009
+ issuerProgram,
12595
13010
  totals: [
12596
13011
  { type: "subtotal", amount: sum },
12597
13012
  { type: "total", amount: sum }
@@ -12654,7 +13069,7 @@ function createUcpCheckoutKit(opts) {
12654
13069
  { method: req.method, url: req.url, headers, body: body || void 0 },
12655
13070
  async (fresh) => {
12656
13071
  const pp = fresh ? await fetchProfile(profileUrl, true) : plat;
12657
- return "keys" in pp && pp.keys ? pp.keys : [];
13072
+ return "ucp" in pp ? ucpProfileKeys(pp) : [];
12658
13073
  }
12659
13074
  );
12660
13075
  if (!sig.ok) return { error: json2(sig.status, { code: sig.code, content: sig.content }) };
@@ -12761,7 +13176,11 @@ function createUcpCheckoutKit(opts) {
12761
13176
  ...checkoutJwt && { checkoutJwt },
12762
13177
  valueMajor: total(s) / 100,
12763
13178
  agentArtifacts,
12764
- metadata: { kitSessionId: s.id, protocol: "ucp" }
13179
+ metadata: {
13180
+ kitSessionId: s.id,
13181
+ protocol: "ucp",
13182
+ ...s._issuerProgram && { issuerProgram: s._issuerProgram }
13183
+ }
12765
13184
  });
12766
13185
  const binding2 = await bindAgent(req, result);
12767
13186
  if (binding2) {
@@ -12900,7 +13319,11 @@ function createUcpCheckoutKit(opts) {
12900
13319
  attemptId: s._attemptId,
12901
13320
  ...checkoutJwt && { checkoutJwt },
12902
13321
  valueMajor: total(s) / 100,
12903
- metadata: { kitSessionId: s.id, protocol: "ucp" },
13322
+ metadata: {
13323
+ kitSessionId: s.id,
13324
+ protocol: "ucp",
13325
+ ...s._issuerProgram && { issuerProgram: s._issuerProgram }
13326
+ },
12904
13327
  resumed: { ...s._approvalId && { approvalId: s._approvalId } }
12905
13328
  });
12906
13329
  if (isHeld(result)) {
@@ -12950,7 +13373,7 @@ function createUcpCheckoutKit(opts) {
12950
13373
  ucpError(
12951
13374
  `approval_${reason}`,
12952
13375
  reason === "denied" ? "The buyer declined this purchase." : "The approval for this purchase is no longer valid. Complete the checkout again to request a new one.",
12953
- "requires_buyer_review"
13376
+ "recoverable"
12954
13377
  )
12955
13378
  ];
12956
13379
  await store.put(s);
@@ -12985,7 +13408,7 @@ function createUcpCheckoutKit(opts) {
12985
13408
  });
12986
13409
  if (status === "approved" || status === "consumed") {
12987
13410
  out.push({ sessionId: s.id, outcome: (await resumeApproved(s.id)).outcome });
12988
- } else if (status === "denied" || status === "expired") {
13411
+ } else if (status === "denied" || status === "expired" || status === "lapsed") {
12989
13412
  out.push({ sessionId: s.id, outcome: (await resumeDeclined(s.id, status)).outcome });
12990
13413
  } else {
12991
13414
  out.push({ sessionId: s.id, outcome: "pending" });
@@ -13015,7 +13438,10 @@ function createUcpCheckoutKit(opts) {
13015
13438
  const caller = await resolveCallerScope(req, reqCtx, opts.resolveCallerId) ?? fallbackScope("ucp-profile", ctx.profileUrl);
13016
13439
  if (req.method === "POST" && path === "/checkout-sessions") {
13017
13440
  return withIdempotency2(req, body, path, caller, async () => {
13018
- const priced = await priceLines(parsed.line_items ?? []);
13441
+ const priced = await priceLines(
13442
+ parsed.line_items ?? [],
13443
+ pricingContextFrom(req.headers)
13444
+ );
13019
13445
  const webhookUrl = ctx.plat.ucp.capabilities?.[UCP_ORDER_CAPABILITY]?.[0]?.config?.webhook_url;
13020
13446
  const s2 = {
13021
13447
  id: `chk_${(0, import_crypto8.randomUUID)().replace(/-/g, "")}`,
@@ -13028,11 +13454,12 @@ function createUcpCheckoutKit(opts) {
13028
13454
  _caller: caller,
13029
13455
  _platformProfile: ctx.profileUrl,
13030
13456
  _ap2: ctx.ap2,
13031
- _platformKeys: ctx.plat.keys ?? [],
13457
+ _platformKeys: ucpProfileKeys(ctx.plat),
13032
13458
  ...typeof webhookUrl === "string" && /^https:\/\//.test(webhookUrl) && { _webhookUrl: webhookUrl },
13033
13459
  ...req.headers.get("x-astra-checkout-session") && {
13034
13460
  _astraCheckoutSessionId: req.headers.get("x-astra-checkout-session").slice(0, 64)
13035
- }
13461
+ },
13462
+ ...priced.issuerProgram && { _issuerProgram: priced.issuerProgram }
13036
13463
  };
13037
13464
  if (!priced.lines.length) {
13038
13465
  return json2(
@@ -13077,8 +13504,13 @@ function createUcpCheckoutKit(opts) {
13077
13504
  if (inFlight(s.status)) return busy(s);
13078
13505
  if (s.status === "requires_escalation") return heldNoEdit(s);
13079
13506
  if (parsed.line_items) {
13080
- const priced = await priceLines(parsed.line_items);
13507
+ const priced = await priceLines(
13508
+ parsed.line_items,
13509
+ pricingContextFrom(req.headers)
13510
+ );
13081
13511
  s.line_items = priced.lines;
13512
+ if (priced.issuerProgram) s._issuerProgram = priced.issuerProgram;
13513
+ else delete s._issuerProgram;
13082
13514
  s.totals = priced.totals;
13083
13515
  s.messages = priced.messages;
13084
13516
  s.status = priced.lines.length && !priced.messages.length ? "ready_for_complete" : "incomplete";
@@ -13709,7 +14141,12 @@ var ACP_CODES = /* @__PURE__ */ new Set([
13709
14141
  "out_of_stock",
13710
14142
  "payment_declined",
13711
14143
  "requires_sign_in",
13712
- "requires_3ds"
14144
+ "requires_3ds",
14145
+ // 5.20.0: the owner-approval codes the kit itself emits.
14146
+ "approval_required",
14147
+ "approval_denied",
14148
+ "approval_expired",
14149
+ "approval_lapsed"
13713
14150
  ]);
13714
14151
  function heldBody2(text) {
13715
14152
  try {
@@ -13719,6 +14156,7 @@ function heldBody2(text) {
13719
14156
  }
13720
14157
  }
13721
14158
  function createAcpCheckoutKit(opts) {
14159
+ opts.agentCommerce?.report({ protocols: ["acp"] });
13722
14160
  const mem = /* @__PURE__ */ new Map();
13723
14161
  const store = opts.store ?? {
13724
14162
  get: async (id) => mem.get(id) ?? null,
@@ -13771,12 +14209,14 @@ function createAcpCheckoutKit(opts) {
13771
14209
  ...s.order && { order: s.order }
13772
14210
  };
13773
14211
  }
13774
- async function price(items) {
14212
+ async function price(items, pricing = { verified: false }) {
13775
14213
  const lines = [];
13776
14214
  const messages = [];
14215
+ let issuerProgram;
13777
14216
  for (const [i, it] of items.entries()) {
13778
14217
  const q = Math.max(1, Math.floor(it.quantity ?? 1));
13779
- const c = it.id ? await opts.catalog(it.id, q) : null;
14218
+ const found = it.id ? await opts.catalog(it.id, q) : null;
14219
+ const c = found && withValidImageUrl(found);
13780
14220
  if (!c) {
13781
14221
  messages.push({
13782
14222
  type: "error",
@@ -13787,10 +14227,31 @@ function createAcpCheckoutKit(opts) {
13787
14227
  });
13788
14228
  continue;
13789
14229
  }
14230
+ if (opts.agentCommerce) {
14231
+ if (!opts.agentCommerce.visible(c.id, pricing)) {
14232
+ messages.push({
14233
+ type: "error",
14234
+ code: "not_found",
14235
+ content: `Item ${c.id} not found.`,
14236
+ resolution: "recoverable",
14237
+ param: `$.line_items[${i}]`
14238
+ });
14239
+ continue;
14240
+ }
14241
+ const priced = opts.agentCommerce.price(c.id, c.price, pricing);
14242
+ c.price = priced.priceMinor;
14243
+ if (priced.issuerProgram) issuerProgram = priced.issuerProgram;
14244
+ }
13790
14245
  const amount = c.price * q;
13791
14246
  lines.push({
13792
14247
  id: `li_${i + 1}`,
13793
- item: { id: c.id, name: c.title, unit_amount: c.price },
14248
+ item: {
14249
+ id: c.id,
14250
+ name: c.title,
14251
+ unit_amount: c.price,
14252
+ ...c.image_url && { image_url: c.image_url },
14253
+ ...typeof c.description === "string" && { description: c.description }
14254
+ },
13794
14255
  quantity: q,
13795
14256
  name: c.title,
13796
14257
  unit_amount: c.price,
@@ -13804,6 +14265,7 @@ function createAcpCheckoutKit(opts) {
13804
14265
  return {
13805
14266
  lines,
13806
14267
  messages,
14268
+ issuerProgram,
13807
14269
  totals: [
13808
14270
  { type: "subtotal", amount: sum },
13809
14271
  { type: "total", amount: sum }
@@ -13955,7 +14417,11 @@ function createAcpCheckoutKit(opts) {
13955
14417
  attemptId,
13956
14418
  valueMajor: total2(s) / 100,
13957
14419
  agentArtifacts,
13958
- metadata: { kitSessionId: s.id, protocol: "acp" }
14420
+ metadata: {
14421
+ kitSessionId: s.id,
14422
+ protocol: "acp",
14423
+ ...s._issuerProgram && { issuerProgram: s._issuerProgram }
14424
+ }
13959
14425
  });
13960
14426
  if (isHeld(result)) {
13961
14427
  s.status = "pending_approval";
@@ -14092,7 +14558,11 @@ function createAcpCheckoutKit(opts) {
14092
14558
  astraId: s._astraId,
14093
14559
  attemptId: s._attemptId,
14094
14560
  valueMajor: total2(s) / 100,
14095
- metadata: { kitSessionId: s.id, protocol: "acp" },
14561
+ metadata: {
14562
+ kitSessionId: s.id,
14563
+ protocol: "acp",
14564
+ ...s._issuerProgram && { issuerProgram: s._issuerProgram }
14565
+ },
14096
14566
  resumed: { ...s._approvalId && { approvalId: s._approvalId } }
14097
14567
  });
14098
14568
  if (isHeld(result)) {
@@ -14137,7 +14607,7 @@ function createAcpCheckoutKit(opts) {
14137
14607
  code: `approval_${reason}`,
14138
14608
  content: reason === "denied" ? "The buyer declined this purchase." : "The approval for this purchase is no longer valid. Complete the session again to request a new one.",
14139
14609
  severity: "medium",
14140
- resolution: "requires_buyer_review"
14610
+ resolution: "recoverable"
14141
14611
  }
14142
14612
  ];
14143
14613
  await store.put(s);
@@ -14172,7 +14642,7 @@ function createAcpCheckoutKit(opts) {
14172
14642
  });
14173
14643
  if (status === "approved" || status === "consumed") {
14174
14644
  out.push({ sessionId: s.id, outcome: (await resumeApproved(s.id)).outcome });
14175
- } else if (status === "denied" || status === "expired") {
14645
+ } else if (status === "denied" || status === "expired" || status === "lapsed") {
14176
14646
  out.push({ sessionId: s.id, outcome: (await resumeDeclined(s.id, status)).outcome });
14177
14647
  } else {
14178
14648
  out.push({ sessionId: s.id, outcome: "pending" });
@@ -14210,7 +14680,7 @@ function createAcpCheckoutKit(opts) {
14210
14680
  const items = parsed.line_items ?? parsed.items;
14211
14681
  if (!items?.length)
14212
14682
  return apiError(400, "missing", "line_items is required.", "$.line_items");
14213
- const p = await price(items);
14683
+ const p = await price(items, pricingContextFrom(req.headers));
14214
14684
  const s2 = {
14215
14685
  id: `cs_${(0, import_crypto12.randomUUID)().replace(/-/g, "")}`,
14216
14686
  status: p.lines.length && !p.messages.length ? "ready_for_payment" : "not_ready_for_payment",
@@ -14221,7 +14691,8 @@ function createAcpCheckoutKit(opts) {
14221
14691
  _caller: caller,
14222
14692
  ...req.headers.get("x-astra-checkout-session") && {
14223
14693
  _astraCheckoutSessionId: req.headers.get("x-astra-checkout-session").slice(0, 64)
14224
- }
14694
+ },
14695
+ ...p.issuerProgram && { _issuerProgram: p.issuerProgram }
14225
14696
  };
14226
14697
  if (s2.status === "ready_for_payment") {
14227
14698
  const v = await validate(s2);
@@ -14250,7 +14721,9 @@ function createAcpCheckoutKit(opts) {
14250
14721
  );
14251
14722
  const items = parsed.line_items ?? parsed.items;
14252
14723
  if (items) {
14253
- const p = await price(items);
14724
+ const p = await price(items, pricingContextFrom(req.headers));
14725
+ if (p.issuerProgram) s._issuerProgram = p.issuerProgram;
14726
+ else delete s._issuerProgram;
14254
14727
  Object.assign(s, {
14255
14728
  line_items: p.lines,
14256
14729
  totals: p.totals,
@@ -14401,6 +14874,7 @@ function expressHandler(kit, options = {}) {
14401
14874
  DECLARED_SOURCE_MAX,
14402
14875
  DEFAULT_EDGE_CONFIG,
14403
14876
  DEFAULT_IDEMPOTENCY_TTL_MS,
14877
+ ISSUER_PROGRAM_HEADER,
14404
14878
  KYDRequiredError,
14405
14879
  MAX_HEADERS,
14406
14880
  MAX_HEADER_VALUE_BYTES,
@@ -14459,6 +14933,7 @@ function expressHandler(kit, options = {}) {
14459
14933
  closedPaymentMandate,
14460
14934
  contentDigest,
14461
14935
  createAcpCheckoutKit,
14936
+ createAgentCommerce,
14462
14937
  createKbHop,
14463
14938
  createMcpMiddleware,
14464
14939
  createUcpCheckoutKit,
@@ -14480,6 +14955,7 @@ function expressHandler(kit, options = {}) {
14480
14955
  fetchRoutes,
14481
14956
  fetchUcpProfile,
14482
14957
  finishIdempotent,
14958
+ fromFeed,
14483
14959
  getCachedWellKnownUrls,
14484
14960
  getEdgeConfig,
14485
14961
  getTrustLevel,
@@ -14490,10 +14966,12 @@ function expressHandler(kit, options = {}) {
14490
14966
  isEdgeConfig,
14491
14967
  isRejectedIdentityClaim,
14492
14968
  isRetryableSettlementError,
14969
+ isSharedVerdict,
14493
14970
  isSpecialUseIp,
14494
14971
  isVerificationUnavailable,
14495
14972
  isVerifiedHopValidFor,
14496
14973
  issueDelegateSdJwt,
14974
+ issuerOfferPrice,
14497
14975
  matchEdgePathRule,
14498
14976
  matchPlatformSignature,
14499
14977
  matchRoutePattern,
@@ -14501,8 +14979,10 @@ function expressHandler(kit, options = {}) {
14501
14979
  negotiateUcp,
14502
14980
  nextjs,
14503
14981
  normalizeDeclaredSource,
14982
+ onVerificationResult,
14504
14983
  openCheckoutMandate,
14505
14984
  openPaymentMandate,
14985
+ parseFeed,
14506
14986
  parseLocalPolicyYaml,
14507
14987
  parseMppCredential,
14508
14988
  parseUcpAgentHeader,
@@ -14511,6 +14991,7 @@ function expressHandler(kit, options = {}) {
14511
14991
  parseX402Payment,
14512
14992
  pollStepUpStatus,
14513
14993
  prefetchWellKnown,
14994
+ priceForAgent,
14514
14995
  quickVerify,
14515
14996
  recordDecision,
14516
14997
  reportAttempt,
@@ -14524,6 +15005,7 @@ function expressHandler(kit, options = {}) {
14524
15005
  sdkFetch,
14525
15006
  sendUcpWebhook,
14526
15007
  serializeVerifiedHop,
15008
+ serveAgentCatalog,
14527
15009
  setMcpMeta,
14528
15010
  signHttpRequest,
14529
15011
  signRootMandate,
@@ -14534,6 +15016,7 @@ function expressHandler(kit, options = {}) {
14534
15016
  ucpEscalation,
14535
15017
  ucpFailure,
14536
15018
  ucpProfileHandler,
15019
+ ucpProfileKeys,
14537
15020
  ucpRequiredComponents,
14538
15021
  unavailableRetryAfterSeconds,
14539
15022
  validateLocalPolicy,