@agent-score/commerce 1.8.0 → 2.0.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 (90) hide show
  1. package/README.md +73 -9
  2. package/dist/{_response-BMt2y4Or.d.mts → _response-BFYN3b6i.d.mts} +19 -22
  3. package/dist/{_response-DyJ3mWI3.d.ts → _response-_iPD5AIj.d.ts} +19 -22
  4. package/dist/challenge/index.d.mts +106 -198
  5. package/dist/challenge/index.d.ts +106 -198
  6. package/dist/challenge/index.js +238 -111
  7. package/dist/challenge/index.js.map +1 -1
  8. package/dist/challenge/index.mjs +238 -111
  9. package/dist/challenge/index.mjs.map +1 -1
  10. package/dist/checkout-BoFwnVsj.d.ts +931 -0
  11. package/dist/checkout-DRbQ0Fsh.d.mts +931 -0
  12. package/dist/core.d.mts +2 -2
  13. package/dist/core.d.ts +2 -2
  14. package/dist/core.js +1 -1
  15. package/dist/core.js.map +1 -1
  16. package/dist/core.mjs +1 -1
  17. package/dist/core.mjs.map +1 -1
  18. package/dist/discovery/index.d.mts +453 -51
  19. package/dist/discovery/index.d.ts +453 -51
  20. package/dist/discovery/index.js +1092 -58
  21. package/dist/discovery/index.js.map +1 -1
  22. package/dist/discovery/index.mjs +1060 -57
  23. package/dist/discovery/index.mjs.map +1 -1
  24. package/dist/identity/express.d.mts +3 -3
  25. package/dist/identity/express.d.ts +3 -3
  26. package/dist/identity/express.js +30 -19
  27. package/dist/identity/express.js.map +1 -1
  28. package/dist/identity/express.mjs +30 -19
  29. package/dist/identity/express.mjs.map +1 -1
  30. package/dist/identity/fastify.d.mts +4 -4
  31. package/dist/identity/fastify.d.ts +4 -4
  32. package/dist/identity/fastify.js +30 -19
  33. package/dist/identity/fastify.js.map +1 -1
  34. package/dist/identity/fastify.mjs +30 -19
  35. package/dist/identity/fastify.mjs.map +1 -1
  36. package/dist/identity/hono.d.mts +3 -3
  37. package/dist/identity/hono.d.ts +3 -3
  38. package/dist/identity/hono.js +30 -19
  39. package/dist/identity/hono.js.map +1 -1
  40. package/dist/identity/hono.mjs +30 -19
  41. package/dist/identity/hono.mjs.map +1 -1
  42. package/dist/identity/nextjs.d.mts +6 -7
  43. package/dist/identity/nextjs.d.ts +6 -7
  44. package/dist/identity/nextjs.js +30 -19
  45. package/dist/identity/nextjs.js.map +1 -1
  46. package/dist/identity/nextjs.mjs +30 -19
  47. package/dist/identity/nextjs.mjs.map +1 -1
  48. package/dist/identity/policy.d.mts +41 -4
  49. package/dist/identity/policy.d.ts +41 -4
  50. package/dist/identity/policy.js +3662 -18
  51. package/dist/identity/policy.js.map +1 -1
  52. package/dist/identity/policy.mjs +3648 -3
  53. package/dist/identity/policy.mjs.map +1 -1
  54. package/dist/identity/web.d.mts +3 -3
  55. package/dist/identity/web.d.ts +3 -3
  56. package/dist/identity/web.js +30 -19
  57. package/dist/identity/web.js.map +1 -1
  58. package/dist/identity/web.mjs +30 -19
  59. package/dist/identity/web.mjs.map +1 -1
  60. package/dist/index.d.mts +72 -329
  61. package/dist/index.d.ts +72 -329
  62. package/dist/index.js +3651 -373
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +3628 -361
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/payment/index.d.mts +257 -266
  67. package/dist/payment/index.d.ts +257 -266
  68. package/dist/payment/index.js +586 -149
  69. package/dist/payment/index.js.map +1 -1
  70. package/dist/payment/index.mjs +573 -148
  71. package/dist/payment/index.mjs.map +1 -1
  72. package/dist/{agent_instructions-DiMSGkdm.d.mts → pricing-CQ9DIFaw.d.ts} +109 -56
  73. package/dist/{agent_instructions-DiMSGkdm.d.ts → pricing-CxzwyiO6.d.mts} +109 -56
  74. package/dist/rail_spec-XP0wKgJV.d.mts +132 -0
  75. package/dist/rail_spec-XP0wKgJV.d.ts +132 -0
  76. package/dist/{signer-CFVQsWjL.d.mts → signer-3FAit11j.d.mts} +27 -1
  77. package/dist/{signer-CFVQsWjL.d.ts → signer-3FAit11j.d.ts} +27 -1
  78. package/dist/solana-Cds87OTu.d.mts +67 -0
  79. package/dist/solana-Cds87OTu.d.ts +67 -0
  80. package/dist/stripe-multichain/index.d.mts +56 -67
  81. package/dist/stripe-multichain/index.d.ts +56 -67
  82. package/dist/stripe-multichain/index.js +68 -42
  83. package/dist/stripe-multichain/index.js.map +1 -1
  84. package/dist/stripe-multichain/index.mjs +68 -41
  85. package/dist/stripe-multichain/index.mjs.map +1 -1
  86. package/dist/{wwwauthenticate-CU1eNvMQ.d.mts → wwwauthenticate-D_FMnPgU.d.mts} +9 -10
  87. package/dist/{wwwauthenticate-CU1eNvMQ.d.ts → wwwauthenticate-D_FMnPgU.d.ts} +9 -10
  88. package/dist/x402_server-hgQzWQwB.d.mts +81 -0
  89. package/dist/x402_server-hgQzWQwB.d.ts +81 -0
  90. package/package.json +13 -9
package/dist/index.js CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -15,22 +18,1162 @@ var __copyProps = (to, from, except, desc) => {
15
18
  if (!__hasOwnProp.call(to, key) && key !== except)
16
19
  __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
20
  }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/identity/ucp.ts
34
+ function ucpSigningKeyFromJWKImpl(jwk) {
35
+ if (!jwk || typeof jwk !== "object") {
36
+ throw new Error(`UCPSigningKey.fromJWK expected a non-null object; got ${typeof jwk}.`);
37
+ }
38
+ if (typeof jwk.kid !== "string" || !jwk.kid) {
39
+ throw new Error("UCPSigningKey.fromJWK: JWK missing required field `kid` (or non-string).");
40
+ }
41
+ if (typeof jwk.kty !== "string" || !jwk.kty) {
42
+ throw new Error("UCPSigningKey.fromJWK: JWK missing required field `kty` (or non-string).");
43
+ }
44
+ if (jwk.kty !== "OKP" && jwk.kty !== "EC" && jwk.kty !== "RSA") {
45
+ throw new Error(
46
+ `UCPSigningKey.fromJWK: kty=${JSON.stringify(jwk.kty)} is not a supported asymmetric key type (expected OKP, EC, or RSA). Symmetric \`oct\` keys are rejected because they cannot publicly verify a JWS in the trust-mode UCP flow.`
47
+ );
48
+ }
49
+ if ((jwk.kty === "EC" || jwk.kty === "OKP") && (typeof jwk.crv !== "string" || !jwk.crv)) {
50
+ throw new Error(`UCPSigningKey.fromJWK: kty=${jwk.kty} requires a non-empty \`crv\` field (e.g., "P-256" for EC, "Ed25519" for OKP).`);
51
+ }
52
+ return jwk;
53
+ }
54
+ function buildUCPProfile(input) {
55
+ for (const [name, bindings] of Object.entries(input.services ?? {})) {
56
+ for (const binding of bindings) {
57
+ if ((binding.transport === "rest" || binding.transport === "mcp" || binding.transport === "a2a") && (binding.endpoint === void 0 || binding.endpoint === null || binding.endpoint === "")) {
58
+ throw new Error(
59
+ `buildUCPProfile: service "${name}" transport=${binding.transport} requires \`endpoint\`. Per UCP spec service.json business_schema, rest/mcp/a2a bindings MUST carry an endpoint URL.`
60
+ );
61
+ }
62
+ }
63
+ }
64
+ const paymentHandlers = {};
65
+ for (const [name, bindings] of Object.entries(input.payment_handlers ?? {})) {
66
+ paymentHandlers[name] = bindings.map((binding) => {
67
+ if (Array.isArray(binding.available_instruments) && binding.available_instruments.length === 0) {
68
+ const { available_instruments: _drop, ...rest } = binding;
69
+ return rest;
70
+ }
71
+ return binding;
72
+ });
73
+ }
74
+ const capabilities = {};
75
+ for (const [name, bindings] of Object.entries(input.capabilities ?? {})) {
76
+ capabilities[name] = [...bindings];
77
+ }
78
+ if (input.agentscore_gate) {
79
+ const gateConfig = { ...input.agentscore_gate };
80
+ const agentscoreBinding = {
81
+ version: AGENTSCORE_CAPABILITY_VERSION,
82
+ spec: input.agentscore_spec_url ?? AGENTSCORE_DEFAULT_SPEC_URL,
83
+ schema: input.agentscore_schema_url ?? AGENTSCORE_DEFAULT_SCHEMA_URL,
84
+ extends: AGENTSCORE_EXTENDS
85
+ };
86
+ if (Object.keys(gateConfig).length > 0) agentscoreBinding.config = gateConfig;
87
+ const existing = capabilities[AGENTSCORE_CAPABILITY_NAME];
88
+ if (existing) existing.push(agentscoreBinding);
89
+ else capabilities[AGENTSCORE_CAPABILITY_NAME] = [agentscoreBinding];
90
+ }
91
+ const ucp = {
92
+ version: input.version ?? DEFAULT_VERSION,
93
+ services: input.services ?? {},
94
+ capabilities,
95
+ payment_handlers: paymentHandlers
96
+ };
97
+ if (input.name !== void 0) ucp.name = input.name;
98
+ if (input.supported_versions !== void 0) ucp.supported_versions = input.supported_versions;
99
+ if (input.ucp_extras) {
100
+ for (const k of Object.keys(input.ucp_extras)) {
101
+ if (RESERVED_UCP_FIELDS.has(k)) {
102
+ throw new Error(`buildUCPProfile: ucp_extras key "${k}" collides with a reserved \`ucp\` field; rejected.`);
103
+ }
104
+ }
105
+ Object.assign(ucp, input.ucp_extras);
106
+ }
107
+ const profile = {
108
+ ucp,
109
+ signing_keys: input.signing_keys
110
+ };
111
+ if (input.extras) {
112
+ for (const k of Object.keys(input.extras)) {
113
+ if (RESERVED_TOP_LEVEL.has(k)) {
114
+ throw new Error(`buildUCPProfile: extras key "${k}" collides with a reserved profile field; rejected.`);
115
+ }
116
+ }
117
+ Object.assign(profile, input.extras);
118
+ }
119
+ return profile;
120
+ }
121
+ function ucpNetworkName(caip2OrUcp, fallback) {
122
+ if (caip2OrUcp === void 0) return fallback;
123
+ return CAIP2_TO_UCP_NETWORK[caip2OrUcp] ?? caip2OrUcp;
124
+ }
125
+ function staticRecipient(r) {
126
+ return typeof r === "string" && r.length > 0 ? r : void 0;
127
+ }
128
+ function tempoToNetworkEntry(spec) {
129
+ const entry = {
130
+ network: spec.testnet ? "tempo-testnet" : spec.network ?? "tempo-mainnet",
131
+ chain_id: spec.chainId ?? 4217
132
+ };
133
+ const recipient = staticRecipient(spec.recipient);
134
+ if (recipient !== void 0) entry.recipient = recipient;
135
+ return entry;
136
+ }
137
+ function solanaMppToNetworkEntry(spec) {
138
+ const entry = {
139
+ network: ucpNetworkName(spec.network, "solana-mainnet-beta")
140
+ };
141
+ const recipient = staticRecipient(spec.recipient);
142
+ if (recipient !== void 0) entry.recipient = recipient;
143
+ return entry;
144
+ }
145
+ function tempoSessionToNetworkEntry(spec) {
146
+ const entry = {
147
+ network: spec.testnet ? "tempo-testnet" : "tempo-mainnet",
148
+ escrow_contract: spec.escrowContract
149
+ };
150
+ const recipient = staticRecipient(spec.recipient);
151
+ if (recipient !== void 0) entry.recipient = recipient;
152
+ return entry;
153
+ }
154
+ function isTempoRailSpec(s) {
155
+ return !("escrowContract" in s) && !("rpcUrl" in s) && !("tokenProgram" in s);
156
+ }
157
+ function isTempoSessionRailSpec(s) {
158
+ return "escrowContract" in s && "store" in s;
159
+ }
160
+ function mppRailToNetworkEntry(spec) {
161
+ if (isTempoSessionRailSpec(spec)) return tempoSessionToNetworkEntry(spec);
162
+ if ("rpcUrl" in spec || "tokenProgram" in spec || (spec.network?.startsWith("solana:") ?? false)) {
163
+ return solanaMppToNetworkEntry(spec);
164
+ }
165
+ if (isTempoRailSpec(spec)) return tempoToNetworkEntry(spec);
166
+ return tempoToNetworkEntry(spec);
167
+ }
168
+ function mppPaymentHandler({
169
+ networks: networks2
170
+ }) {
171
+ return {
172
+ "sh.agentscore.payment.mpp": [{
173
+ id: "mpp",
174
+ version: HANDLER_VERSION,
175
+ spec: `${SPEC_BASE}/mpp`,
176
+ schema: `${SCHEMA_BASE}/mpp.json`,
177
+ config: { networks: networks2.map(mppRailToNetworkEntry) }
178
+ }]
179
+ };
180
+ }
181
+ function x402RailToNetworkEntry(spec) {
182
+ const entry = {
183
+ network: ucpNetworkName(spec.network, "base-8453")
184
+ };
185
+ const recipient = staticRecipient(spec.recipient);
186
+ if (recipient !== void 0) entry.recipient = recipient;
187
+ return entry;
188
+ }
189
+ function x402PaymentHandler({
190
+ networks: networks2
191
+ }) {
192
+ return {
193
+ "sh.agentscore.payment.x402": [{
194
+ id: "x402",
195
+ version: HANDLER_VERSION,
196
+ spec: `${SPEC_BASE}/x402`,
197
+ schema: `${SCHEMA_BASE}/x402.json`,
198
+ config: { networks: networks2.map(x402RailToNetworkEntry) }
199
+ }]
200
+ };
201
+ }
202
+ function stripeSptPaymentHandler({
203
+ spec
204
+ }) {
205
+ return {
206
+ "sh.agentscore.payment.stripe_spt": [{
207
+ id: "stripe-spt",
208
+ version: HANDLER_VERSION,
209
+ spec: `${SPEC_BASE}/stripe_spt`,
210
+ schema: `${SCHEMA_BASE}/stripe_spt.json`,
211
+ config: { rail: "stripe-spt", profile_id: spec.profileId ?? null }
212
+ }]
213
+ };
214
+ }
215
+ var UCPSigningKey, DEFAULT_VERSION, AGENTSCORE_CAPABILITY_NAME, AGENTSCORE_CAPABILITY_VERSION, AGENTSCORE_DEFAULT_SPEC_URL, AGENTSCORE_DEFAULT_SCHEMA_URL, AGENTSCORE_EXTENDS, RESERVED_TOP_LEVEL, RESERVED_UCP_FIELDS, AGENTSCORE_UCP_CAPABILITY, HANDLER_VERSION, SPEC_BASE, SCHEMA_BASE, CAIP2_TO_UCP_NETWORK;
216
+ var init_ucp = __esm({
217
+ "src/identity/ucp.ts"() {
218
+ "use strict";
219
+ UCPSigningKey = {
220
+ fromJWK: ucpSigningKeyFromJWKImpl
221
+ };
222
+ DEFAULT_VERSION = "2026-04-08";
223
+ AGENTSCORE_CAPABILITY_NAME = "sh.agentscore.identity";
224
+ AGENTSCORE_CAPABILITY_VERSION = "2026-04-08";
225
+ AGENTSCORE_DEFAULT_SPEC_URL = "https://agentscore.sh/specification/identity";
226
+ AGENTSCORE_DEFAULT_SCHEMA_URL = "https://agentscore.sh/schemas/ucp/sh-agentscore-identity-v1.json";
227
+ AGENTSCORE_EXTENDS = ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"];
228
+ RESERVED_TOP_LEVEL = /* @__PURE__ */ new Set([
229
+ "ucp",
230
+ "signing_keys",
231
+ "signature",
232
+ "__proto__",
233
+ "constructor",
234
+ "prototype"
235
+ ]);
236
+ RESERVED_UCP_FIELDS = /* @__PURE__ */ new Set([
237
+ "version",
238
+ "name",
239
+ "services",
240
+ "capabilities",
241
+ "payment_handlers",
242
+ "supported_versions",
243
+ "__proto__",
244
+ "constructor",
245
+ "prototype"
246
+ ]);
247
+ AGENTSCORE_UCP_CAPABILITY = AGENTSCORE_CAPABILITY_NAME;
248
+ HANDLER_VERSION = "2026-04-08";
249
+ SPEC_BASE = "https://agentscore.sh/specification/payment-handlers";
250
+ SCHEMA_BASE = "https://agentscore.sh/schemas/payment-handlers";
251
+ CAIP2_TO_UCP_NETWORK = {
252
+ "eip155:8453": "base-8453",
253
+ "eip155:84532": "base-84532",
254
+ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "solana-mainnet-beta",
255
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": "solana-devnet"
256
+ };
257
+ }
258
+ });
259
+
260
+ // src/identity/ucp-jwks.ts
261
+ async function loadJose() {
262
+ try {
263
+ return await import("jose");
264
+ } catch (err) {
265
+ throw new Error(
266
+ `UCP signing requires the \`jose\` library, which is an optional peer dependency. ${JOSE_INSTALL_HINT}
267
+ Original error: ${err instanceof Error ? err.message : String(err)}`
268
+ );
269
+ }
270
+ }
271
+ function canonicalizeProfile(profile) {
272
+ const stripped = { ...profile };
273
+ delete stripped.signature;
274
+ return stableStringify(stripped);
275
+ }
276
+ function stableStringify(value) {
277
+ if (value === void 0) {
278
+ throw new Error(
279
+ "stableStringify: undefined values are not allowed in canonicalized JSON. Object fields with no value must be omitted."
280
+ );
281
+ }
282
+ if (typeof value === "function" || typeof value === "symbol") {
283
+ throw new Error(`stableStringify: ${typeof value} values are not allowed in canonicalized JSON.`);
284
+ }
285
+ if (typeof value === "bigint") {
286
+ throw new Error("stableStringify: BigInt values are not allowed; use a decimal string.");
287
+ }
288
+ if (value instanceof Date) {
289
+ throw new Error(
290
+ "stableStringify: Date instances are not allowed; serialize to an ISO string before passing."
291
+ );
292
+ }
293
+ if (value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
294
+ throw new Error(
295
+ `stableStringify: ${value.constructor.name} values are not allowed; convert to a plain object/array first.`
296
+ );
297
+ }
298
+ if (ArrayBuffer.isView(value)) {
299
+ throw new Error("stableStringify: typed arrays are not allowed; convert to a plain array first.");
300
+ }
301
+ if (typeof value === "number") {
302
+ if (!Number.isFinite(value)) {
303
+ throw new Error(
304
+ `UCP profile canonicalization rejects non-finite Number ${value}. Use a decimal string for any value that may be NaN/Infinity.`
305
+ );
306
+ }
307
+ if (!Number.isInteger(value)) {
308
+ throw new Error(
309
+ `UCP profile canonicalization rejects non-integer Number ${value}. Use a decimal string (e.g. "9.99") for monetary or fractional fields to preserve cross-language byte-parity.`
310
+ );
311
+ }
312
+ if (!Number.isSafeInteger(value)) {
313
+ throw new Error(
314
+ `stableStringify: integer ${value} exceeds Number.MAX_SAFE_INTEGER. For values >2^53, use a decimal string to preserve cross-language byte parity.`
315
+ );
316
+ }
317
+ }
318
+ if (typeof value === "string") {
319
+ if (value.includes("\u2028") || value.includes("\u2029")) {
320
+ throw new Error(
321
+ "stableStringify: strings containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; cross-language byte parity requires neither be present (Node JSON.stringify on older V8 escapes them; Python json.dumps with ensure_ascii=False does not)."
322
+ );
323
+ }
324
+ return JSON.stringify(value);
325
+ }
326
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
327
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
328
+ const obj = value;
329
+ const keys = Object.keys(obj).sort((a, b) => {
330
+ const aPoints = [...a].map((c) => c.codePointAt(0));
331
+ const bPoints = [...b].map((c) => c.codePointAt(0));
332
+ const len = Math.min(aPoints.length, bPoints.length);
333
+ for (let i = 0; i < len; i += 1) {
334
+ if (aPoints[i] !== bPoints[i]) return aPoints[i] - bPoints[i];
335
+ }
336
+ return aPoints.length - bPoints.length;
337
+ });
338
+ for (const k of keys) {
339
+ if (k.includes("\u2028") || k.includes("\u2029")) {
340
+ throw new Error(
341
+ "stableStringify: object keys containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; cross-language byte parity (Node JSON.stringify on older V8 escapes them; Python json.dumps with ensure_ascii=False does not)."
342
+ );
343
+ }
344
+ }
345
+ const pairs = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
346
+ return `{${pairs.join(",")}}`;
347
+ }
348
+ async function generateUCPSigningKey(opts) {
349
+ const jose = await loadJose();
350
+ const alg = opts.alg ?? "EdDSA";
351
+ const { privateKey, publicKey } = await jose.generateKeyPair(alg, { extractable: true });
352
+ const exportedJwk = await jose.exportJWK(publicKey);
353
+ const publicJWK = {
354
+ kid: opts.kid,
355
+ alg,
356
+ use: "sig",
357
+ ...exportedJwk
358
+ };
359
+ return { privateKey, publicJWK };
360
+ }
361
+ async function signUCPProfile(profile, {
362
+ signingKey,
363
+ kid,
364
+ alg = "EdDSA"
365
+ }) {
366
+ const jose = await loadJose();
367
+ if (!ALLOWED_ALGS.includes(alg)) {
368
+ throw new Error(
369
+ `signUCPProfile: alg ${JSON.stringify(alg)} is not in the supported set [${ALLOWED_ALGS.join(", ")}].`
370
+ );
371
+ }
372
+ if (typeof kid !== "string" || !kid) {
373
+ throw new Error("signUCPProfile: kid must be a non-empty string.");
374
+ }
375
+ const kids = (profile.signing_keys ?? []).map((k) => k.kid);
376
+ if (!kids.includes(kid)) {
377
+ throw new Error(
378
+ `signUCPProfile: kid ${JSON.stringify(kid)} is not present in profile.signing_keys[] (declared kids: ${JSON.stringify(kids)}). Verifiers will not find the key.`
379
+ );
380
+ }
381
+ const canonicalBody = canonicalizeProfile(profile);
382
+ const payloadBytes = new TextEncoder().encode(canonicalBody);
383
+ const signature = await new jose.CompactSign(payloadBytes).setProtectedHeader({ alg, kid, typ: PROFILE_TYP }).sign(signingKey);
384
+ return { ...profile, signature };
385
+ }
386
+ async function verifyUCPProfile(profile, jwks) {
387
+ if (profile === null || typeof profile !== "object" || Array.isArray(profile)) {
388
+ throw new UCPVerificationError(
389
+ "no_signature",
390
+ `UCP profile must be a JSON object; got ${profile === null ? "null" : Array.isArray(profile) ? "array" : typeof profile}.`
391
+ );
392
+ }
393
+ const jose = await loadJose();
394
+ if (!jwks || typeof jwks !== "object" || !Array.isArray(jwks.keys)) {
395
+ throw new UCPVerificationError(
396
+ "malformed_jwks",
397
+ `UCP verifier expected JWKS shape { keys: [...] }; got ${jwks === null ? "null" : typeof jwks === "object" ? "object without keys[] array" : typeof jwks}.`
398
+ );
399
+ }
400
+ const stripped = { ...profile };
401
+ const sig = stripped.signature;
402
+ delete stripped.signature;
403
+ if (typeof sig !== "string" || !sig) {
404
+ throw new UCPVerificationError(
405
+ "no_signature",
406
+ `UCP profile signature must be a non-empty string; got ${sig === void 0 ? "undefined" : typeof sig}.`
407
+ );
408
+ }
409
+ let header;
410
+ try {
411
+ const protectedB64 = sig.split(".")[0];
412
+ if (!protectedB64) throw new Error("JWS protected header segment is empty.");
413
+ const headerJson = new TextDecoder().decode(jose.base64url.decode(protectedB64));
414
+ const parsed = JSON.parse(headerJson);
415
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
416
+ throw new Error("JWS protected header is not a JSON object.");
417
+ }
418
+ header = parsed;
419
+ } catch (err) {
420
+ throw new UCPVerificationError(
421
+ "malformed_jws",
422
+ `JWS protected header is not valid base64url-encoded JSON: ${err instanceof Error ? err.message : String(err)}`
423
+ );
424
+ }
425
+ if (header.typ !== PROFILE_TYP) {
426
+ throw new UCPVerificationError("wrong_typ", `UCP signature typ must be "${PROFILE_TYP}"; got ${String(header.typ)}.`);
427
+ }
428
+ if (!ALLOWED_ALGS.includes(header.alg)) {
429
+ throw new UCPVerificationError("unsupported_alg", `UCP signing alg must be one of ${ALLOWED_ALGS.join(", ")}; got ${String(header.alg)}.`);
430
+ }
431
+ if (typeof header.kid !== "string" || !header.kid) {
432
+ throw new UCPVerificationError(
433
+ "missing_kid",
434
+ `UCP signature header kid must be a non-empty string; got ${header.kid === void 0 ? "undefined" : typeof header.kid}.`
435
+ );
436
+ }
437
+ if ("crit" in header) {
438
+ const crit = header.crit;
439
+ if (!Array.isArray(crit) || crit.length === 0 || !crit.every((c) => typeof c === "string")) {
440
+ throw new UCPVerificationError(
441
+ "malformed_jws",
442
+ `JWS protected header crit must be a non-empty array of strings; got ${JSON.stringify(crit)}.`
443
+ );
444
+ }
445
+ throw new UCPVerificationError(
446
+ "unrecognized_critical_header",
447
+ `JWS protected header advertises unrecognized crit headers: ${JSON.stringify(crit)}.`
448
+ );
449
+ }
450
+ let signedPayload;
451
+ try {
452
+ const verified = await jose.compactVerify(
453
+ sig,
454
+ async (h) => {
455
+ const kid = h.kid;
456
+ if (typeof kid !== "string" || !kid) {
457
+ throw new UCPVerificationError(
458
+ "missing_kid",
459
+ `UCP signature header kid must be a non-empty string; got ${kid === void 0 ? "undefined" : typeof kid}.`
460
+ );
461
+ }
462
+ const matches = jwks.keys.filter(
463
+ (k) => k != null && typeof k === "object" && k.kid === kid
464
+ );
465
+ if (matches.length === 0) throw new UCPVerificationError("kid_not_found", `No JWK in JWKS matching kid=${JSON.stringify(kid)}.`);
466
+ if (matches.length > 1) throw new UCPVerificationError("duplicate_kid", `JWKS contains ${matches.length} keys with kid=${JSON.stringify(kid)}; expected exactly one.`);
467
+ const matchedKey = matches[0];
468
+ if (matchedKey.use != null && matchedKey.use !== "sig") {
469
+ throw new UCPVerificationError("unusable_key", `JWK with kid=${kid} has use=${JSON.stringify(matchedKey.use)}; expected "sig".`);
470
+ }
471
+ if (matchedKey.alg != null && matchedKey.alg !== h.alg) {
472
+ throw new UCPVerificationError(
473
+ "unusable_key",
474
+ `JWK alg ${JSON.stringify(matchedKey.alg)} does not match JWS header alg ${JSON.stringify(h.alg)}.`
475
+ );
476
+ }
477
+ return jose.importJWK(matches[0], h.alg);
478
+ }
479
+ );
480
+ signedPayload = verified.payload;
481
+ } catch (err) {
482
+ if (err instanceof UCPVerificationError) throw err;
483
+ if (err instanceof Error && err.name === "JOSEAlgNotAllowed") {
484
+ throw new UCPVerificationError("unsupported_alg", `UCP signing alg not allowed: ${err.message}`);
485
+ }
486
+ if (err instanceof Error && err.name === "JWSSignatureVerificationFailed") {
487
+ throw new UCPVerificationError("signature_invalid", `UCP signature verification failed: ${err.message}`);
488
+ }
489
+ if (err instanceof Error && err.name === "JWSInvalid") {
490
+ throw new UCPVerificationError("malformed_jws", `Malformed JWS: ${err.message}`);
491
+ }
492
+ if (err instanceof Error && err.name === "JOSENotSupported") {
493
+ throw new UCPVerificationError("unrecognized_critical_header", `UCP signing rejected unrecognized critical header: ${err.message}`);
494
+ }
495
+ throw err;
496
+ }
497
+ let canonicalBody;
498
+ try {
499
+ canonicalBody = canonicalizeProfile(stripped);
500
+ } catch (err) {
501
+ throw new UCPVerificationError(
502
+ "body_mismatch",
503
+ `Failed to canonicalize received profile for verification: ${err instanceof Error ? err.message : String(err)}`
504
+ );
505
+ }
506
+ const expectedPayload = new TextEncoder().encode(canonicalBody);
507
+ if (!constantTimeEqual(signedPayload, expectedPayload)) {
508
+ throw new UCPVerificationError("body_mismatch", "UCP profile body does not match the signed payload (tampered or non-canonical).");
509
+ }
510
+ return true;
511
+ }
512
+ function constantTimeEqual(a, b) {
513
+ if (a.length !== b.length) return false;
514
+ let diff = 0;
515
+ for (let i = 0; i < a.length; i += 1) {
516
+ diff |= a[i] ^ b[i];
517
+ }
518
+ return diff === 0;
519
+ }
520
+ function buildJWKSResponse(keys) {
521
+ return { keys };
522
+ }
523
+ function readEnvTrimmed(name) {
524
+ const raw = process.env[name];
525
+ if (raw === void 0) return void 0;
526
+ const trimmed = raw.trim();
527
+ return trimmed === "" ? void 0 : trimmed;
528
+ }
529
+ function detectAlgFromJwk(jwk) {
530
+ if (jwk.kty === "OKP" && jwk.crv === "Ed25519") return "EdDSA";
531
+ if (jwk.kty === "EC" && jwk.crv === "P-256") return "ES256";
532
+ return null;
533
+ }
534
+ function cacheKey(opts) {
535
+ return `${opts.envJwkVar}|${opts.envKidVar}|${opts.envAlgVar}|${opts.defaultKid}|${opts.defaultAlg}`;
536
+ }
537
+ async function buildEnvSigningKey(opts) {
538
+ const kidDefault = readEnvTrimmed(opts.envKidVar) ?? opts.defaultKid;
539
+ const rawAlg = (readEnvTrimmed(opts.envAlgVar) ?? "").toUpperCase();
540
+ const algFallback = rawAlg === "ES256" ? "ES256" : opts.defaultAlg;
541
+ const envJwk = readEnvTrimmed(opts.envJwkVar);
542
+ if (envJwk) {
543
+ let jwkDict;
544
+ try {
545
+ jwkDict = JSON.parse(envJwk);
546
+ } catch (err) {
547
+ throw new Error(
548
+ `${opts.envJwkVar} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
549
+ );
550
+ }
551
+ if (!jwkDict || typeof jwkDict !== "object" || Array.isArray(jwkDict) || Object.keys(jwkDict).length === 0) {
552
+ throw new Error(`${opts.envJwkVar} must be a non-empty JWK object.`);
553
+ }
554
+ const detectedAlg = detectAlgFromJwk(jwkDict);
555
+ if (!detectedAlg) {
556
+ throw new Error(
557
+ `${opts.envJwkVar} has unsupported kty/crv (got kty=${String(jwkDict.kty)} crv=${String(jwkDict.crv)}); expected OKP+Ed25519 or EC+P-256.`
558
+ );
559
+ }
560
+ const canonicalPrivateJwk = detectedAlg === "EdDSA" ? { kty: jwkDict.kty, crv: jwkDict.crv, x: jwkDict.x, d: jwkDict.d } : { kty: jwkDict.kty, crv: jwkDict.crv, x: jwkDict.x, y: jwkDict.y, d: jwkDict.d };
561
+ const { importJWK } = await import("jose");
562
+ const { createPublicKey } = await import("crypto");
563
+ let privateKey;
564
+ let publicNodeKey;
565
+ try {
566
+ privateKey = await importJWK(
567
+ canonicalPrivateJwk,
568
+ detectedAlg
569
+ );
570
+ publicNodeKey = createPublicKey({ key: canonicalPrivateJwk, format: "jwk" });
571
+ } catch (err) {
572
+ const className = err instanceof Error ? err.constructor.name : typeof err;
573
+ const code = err && typeof err === "object" && "code" in err ? String(err.code) : null;
574
+ const codeSuffix = code ? ` [${code}]` : "";
575
+ throw new Error(
576
+ `${opts.envJwkVar} has malformed key material (${className}${codeSuffix}). Verify the JWK is well-formed and matches the declared kty/crv. Underlying details suppressed to avoid leaking key bytes.`
577
+ );
578
+ }
579
+ const publicJWK = publicNodeKey.export({ format: "jwk" });
580
+ publicJWK.kid = jwkDict.kid || kidDefault;
581
+ publicJWK.alg = detectedAlg;
582
+ publicJWK.use = "sig";
583
+ return { privateKey, publicJWK };
584
+ }
585
+ return generateUCPSigningKey({ kid: kidDefault, alg: algFallback });
586
+ }
587
+ async function loadUCPSigningKeyFromEnv({
588
+ envJwkVar,
589
+ envKidVar,
590
+ envAlgVar,
591
+ defaultKid,
592
+ defaultAlg
593
+ } = {}) {
594
+ const resolved = {
595
+ ...DEFAULT_LOAD_OPTS,
596
+ ...envJwkVar !== void 0 && { envJwkVar },
597
+ ...envKidVar !== void 0 && { envKidVar },
598
+ ...envAlgVar !== void 0 && { envAlgVar },
599
+ ...defaultKid !== void 0 && { defaultKid },
600
+ ...defaultAlg !== void 0 && { defaultAlg }
601
+ };
602
+ const key = cacheKey(resolved);
603
+ let cached = envLoaderCache.get(key);
604
+ if (cached) return cached;
605
+ cached = buildEnvSigningKey(resolved).catch((err) => {
606
+ envLoaderCache.delete(key);
607
+ throw err;
608
+ });
609
+ envLoaderCache.set(key, cached);
610
+ return cached;
611
+ }
612
+ var JOSE_INSTALL_HINT, ALLOWED_ALGS, PROFILE_TYP, UCPVerificationError, DEFAULT_LOAD_OPTS, envLoaderCache;
613
+ var init_ucp_jwks = __esm({
614
+ "src/identity/ucp-jwks.ts"() {
615
+ "use strict";
616
+ JOSE_INSTALL_HINT = "Install the optional peer dependency: `npm install jose@^6` (or `bun add jose`). Tested against jose v6.x.";
617
+ ALLOWED_ALGS = ["EdDSA", "ES256"];
618
+ PROFILE_TYP = "agentscore-profile+jws";
619
+ UCPVerificationError = class extends Error {
620
+ constructor(code, message) {
621
+ super(message);
622
+ this.code = code;
623
+ this.name = "UCPVerificationError";
624
+ }
625
+ code;
626
+ };
627
+ DEFAULT_LOAD_OPTS = {
628
+ envJwkVar: "UCP_SIGNING_KEY_JWK_PRIVATE",
629
+ envKidVar: "UCP_SIGNING_KEY_KID",
630
+ envAlgVar: "UCP_SIGNING_KEY_ALG",
631
+ defaultKid: "merchant-default",
632
+ defaultAlg: "EdDSA"
633
+ };
634
+ envLoaderCache = /* @__PURE__ */ new Map();
635
+ }
636
+ });
637
+
638
+ // src/payment/usdc.ts
639
+ var USDC;
640
+ var init_usdc = __esm({
641
+ "src/payment/usdc.ts"() {
642
+ "use strict";
643
+ USDC = {
644
+ base: {
645
+ mainnet: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6 },
646
+ sepolia: { address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", decimals: 6 }
647
+ },
648
+ solana: {
649
+ mainnet: { mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", decimals: 6 },
650
+ devnet: { mint: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", decimals: 6 }
651
+ },
652
+ tempo: {
653
+ mainnet: { address: "0x20C000000000000000000000b9537d11c60E8b50", decimals: 6 },
654
+ testnet: { address: "0x20c0000000000000000000000000000000000000", decimals: 6 }
655
+ }
656
+ };
657
+ }
658
+ });
659
+
660
+ // src/payment/wwwauthenticate.ts
661
+ function paymentRequiredHeader({
662
+ x402Version,
663
+ accepts,
664
+ resource
665
+ }) {
666
+ return Buffer.from(JSON.stringify({ x402Version, accepts, ...resource ? { resource } : {} })).toString("base64");
667
+ }
668
+ var init_wwwauthenticate = __esm({
669
+ "src/payment/wwwauthenticate.ts"() {
670
+ "use strict";
671
+ }
672
+ });
673
+
674
+ // src/payment/networks.ts
675
+ var networks;
676
+ var init_networks = __esm({
677
+ "src/payment/networks.ts"() {
678
+ "use strict";
679
+ networks = {
680
+ base: {
681
+ mainnet: { caip2: "eip155:8453", chainId: 8453 },
682
+ sepolia: { caip2: "eip155:84532", chainId: 84532 }
683
+ },
684
+ solana: {
685
+ mainnet: { caip2: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" },
686
+ devnet: { caip2: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" }
687
+ },
688
+ tempo: {
689
+ mainnet: { caip2: "eip155:4217", chainId: 4217 },
690
+ testnet: { caip2: "eip155:42431", chainId: 42431 }
691
+ }
692
+ };
693
+ }
694
+ });
695
+
696
+ // src/payment/rails.ts
697
+ function lookupRail(name) {
698
+ return rails[name];
699
+ }
700
+ var rails;
701
+ var init_rails = __esm({
702
+ "src/payment/rails.ts"() {
703
+ "use strict";
704
+ init_networks();
705
+ init_usdc();
706
+ rails = {
707
+ "tempo-mainnet": {
708
+ method: "tempo",
709
+ network: networks.tempo.mainnet.caip2,
710
+ chainId: networks.tempo.mainnet.chainId,
711
+ currency: USDC.tempo.mainnet.address,
712
+ decimals: USDC.tempo.mainnet.decimals,
713
+ asset: USDC.tempo.mainnet.address
714
+ },
715
+ "tempo-testnet": {
716
+ method: "tempo",
717
+ network: networks.tempo.testnet.caip2,
718
+ chainId: networks.tempo.testnet.chainId,
719
+ currency: USDC.tempo.testnet.address,
720
+ decimals: USDC.tempo.testnet.decimals,
721
+ asset: USDC.tempo.testnet.address
722
+ },
723
+ "x402-base-mainnet": {
724
+ method: "x402",
725
+ network: networks.base.mainnet.caip2,
726
+ chainId: networks.base.mainnet.chainId,
727
+ currency: USDC.base.mainnet.address,
728
+ decimals: USDC.base.mainnet.decimals,
729
+ asset: USDC.base.mainnet.address
730
+ },
731
+ "x402-base-sepolia": {
732
+ method: "x402",
733
+ network: networks.base.sepolia.caip2,
734
+ chainId: networks.base.sepolia.chainId,
735
+ currency: USDC.base.sepolia.address,
736
+ decimals: USDC.base.sepolia.decimals,
737
+ asset: USDC.base.sepolia.address
738
+ },
739
+ // Upto rails — pay UP TO a max amount (Permit2-based, vs EIP-3009 for exact). Use for
740
+ // variable-cost APIs where the actual cost depends on output (LLM tokens, bandwidth, etc.).
741
+ // Only available on EVM networks; Solana svm doesn't ship an upto scheme yet.
742
+ "x402-base-mainnet-upto": {
743
+ method: "x402-upto",
744
+ network: networks.base.mainnet.caip2,
745
+ chainId: networks.base.mainnet.chainId,
746
+ currency: USDC.base.mainnet.address,
747
+ decimals: USDC.base.mainnet.decimals,
748
+ asset: USDC.base.mainnet.address
749
+ },
750
+ "x402-base-sepolia-upto": {
751
+ method: "x402-upto",
752
+ network: networks.base.sepolia.caip2,
753
+ chainId: networks.base.sepolia.chainId,
754
+ currency: USDC.base.sepolia.address,
755
+ decimals: USDC.base.sepolia.decimals,
756
+ asset: USDC.base.sepolia.address
757
+ },
758
+ "mpp-solana-mainnet": {
759
+ method: "solana",
760
+ network: networks.solana.mainnet.caip2,
761
+ currency: USDC.solana.mainnet.mint,
762
+ decimals: USDC.solana.mainnet.decimals,
763
+ asset: USDC.solana.mainnet.mint
764
+ },
765
+ "mpp-solana-devnet": {
766
+ method: "solana",
767
+ network: networks.solana.devnet.caip2,
768
+ currency: USDC.solana.devnet.mint,
769
+ decimals: USDC.solana.devnet.decimals,
770
+ asset: USDC.solana.devnet.mint
771
+ },
772
+ "stripe-spt": {
773
+ method: "stripe",
774
+ currency: "usd",
775
+ decimals: 2
776
+ }
777
+ };
778
+ }
779
+ });
780
+
781
+ // src/payment/directive.ts
782
+ function buildPaymentRequestBlob({
783
+ rail,
784
+ amountUsd,
785
+ currency,
786
+ decimals,
787
+ recipient,
788
+ chainId,
789
+ networkId
790
+ }) {
791
+ const railDef = rail ? lookupRail(rail) : void 0;
792
+ const decimalsResolved = decimals ?? railDef?.decimals ?? 6;
793
+ const currencyResolved = currency ?? railDef?.currency ?? "usd";
794
+ const chainIdResolved = chainId ?? railDef?.chainId;
795
+ const amountNum = typeof amountUsd === "string" ? Number(amountUsd) : amountUsd;
796
+ const amountRaw = BigInt(Math.round(amountNum * 10 ** decimalsResolved)).toString();
797
+ const blob = { amount: amountRaw, currency: currencyResolved, decimals: decimalsResolved };
798
+ if (recipient) blob.recipient = recipient;
799
+ const methodDetails = {};
800
+ if (chainIdResolved !== void 0) methodDetails.chainId = chainIdResolved;
801
+ if (networkId) methodDetails.networkId = networkId;
802
+ if (Object.keys(methodDetails).length > 0) blob.methodDetails = methodDetails;
803
+ return Buffer.from(JSON.stringify(blob)).toString("base64url");
804
+ }
805
+ function paymentDirective({
806
+ rail,
807
+ id,
808
+ realm,
809
+ method,
810
+ intent,
811
+ expires,
812
+ request
813
+ }) {
814
+ const railDef = rail ? lookupRail(rail) : void 0;
815
+ const methodResolved = method ?? railDef?.method ?? "unknown";
816
+ const intentResolved = intent ?? "charge";
817
+ const expiresResolved = expires ?? new Date(Date.now() + 5 * 60 * 1e3).toISOString();
818
+ return `Payment id="${id}", realm="${realm}", method="${methodResolved}", intent="${intentResolved}", expires="${expiresResolved}", request="${request}"`;
819
+ }
820
+ var init_directive = __esm({
821
+ "src/payment/directive.ts"() {
822
+ "use strict";
823
+ init_rails();
824
+ }
825
+ });
826
+
827
+ // src/discovery/probe.ts
828
+ var probe_exports = {};
829
+ __export(probe_exports, {
830
+ buildDiscoveryProbeResponse: () => buildDiscoveryProbeResponse,
831
+ isDiscoveryProbeRequest: () => isDiscoveryProbeRequest,
832
+ sampleX402AcceptForNetwork: () => sampleX402AcceptForNetwork
833
+ });
834
+ function sampleX402AcceptForNetwork(caip2, amountAtomic = "1000000") {
835
+ if (caip2 === networks.base.mainnet.caip2) {
836
+ return {
837
+ scheme: "exact",
838
+ network: caip2,
839
+ amount: amountAtomic,
840
+ asset: USDC.base.mainnet.address,
841
+ payTo: ZERO_EVM_PAYTO,
842
+ maxTimeoutSeconds: 300,
843
+ // ``extra.name`` mirrors the on-chain USDC contract's ``name()`` because
844
+ // EIP-712 domain hashes include this string. Wrong name → every signed
845
+ // payload fails facilitator verify with ``invalid_exact_evm_payload_signature``.
846
+ // Base mainnet USDC returns "USD Coin"; base sepolia USDC returns "USDC".
847
+ extra: { name: "USD Coin", version: "2" }
848
+ };
849
+ }
850
+ if (caip2 === networks.base.sepolia.caip2) {
851
+ return {
852
+ scheme: "exact",
853
+ network: caip2,
854
+ amount: amountAtomic,
855
+ asset: USDC.base.sepolia.address,
856
+ payTo: ZERO_EVM_PAYTO,
857
+ maxTimeoutSeconds: 300,
858
+ extra: { name: "USDC", version: "2" }
859
+ };
860
+ }
861
+ if (caip2 === networks.solana.mainnet.caip2) {
862
+ return {
863
+ scheme: "exact",
864
+ network: caip2,
865
+ amount: amountAtomic,
866
+ asset: USDC.solana.mainnet.mint,
867
+ payTo: ZERO_SOLANA_PAYTO,
868
+ maxTimeoutSeconds: 300
869
+ };
870
+ }
871
+ if (caip2 === networks.solana.devnet.caip2) {
872
+ return {
873
+ scheme: "exact",
874
+ network: caip2,
875
+ amount: amountAtomic,
876
+ asset: USDC.solana.devnet.mint,
877
+ payTo: ZERO_SOLANA_PAYTO,
878
+ maxTimeoutSeconds: 300
879
+ };
880
+ }
881
+ return null;
882
+ }
883
+ function buildDiscoveryProbeResponse(opts) {
884
+ const probeId = `probe_${Date.now()}`;
885
+ const expires = new Date(Date.now() + (opts.ttlSeconds ?? 300) * 1e3).toISOString();
886
+ const request = buildPaymentRequestBlob({
887
+ rail: opts.sampleRail,
888
+ amountUsd: opts.sampleAmountUsd,
889
+ recipient: opts.sampleRecipient
890
+ });
891
+ const directive = paymentDirective({
892
+ rail: opts.sampleRail,
893
+ id: probeId,
894
+ realm: opts.realm,
895
+ intent: opts.intent,
896
+ expires,
897
+ request
898
+ });
899
+ const bodyObj = {
900
+ error: {
901
+ code: "payment_required",
902
+ message: opts.message ?? "This endpoint requires payment. Send a valid request body to receive a full challenge."
903
+ },
904
+ discovery: true,
905
+ ...opts.docsUrl ? { docs: opts.docsUrl } : {}
906
+ };
907
+ const headers = {
908
+ "content-type": "application/json",
909
+ "www-authenticate": directive
910
+ };
911
+ if (opts.x402Sample) {
912
+ const x402Version = opts.x402Sample.version ?? 2;
913
+ const sampleAccepts = opts.x402Sample.accepts ?? (opts.x402Sample.networks ?? []).map((n) => sampleX402AcceptForNetwork(n, opts.x402Sample.amountAtomic ?? "1000000")).filter((e) => e !== null);
914
+ headers["payment-required"] = paymentRequiredHeader({
915
+ x402Version,
916
+ accepts: sampleAccepts,
917
+ ...opts.x402Sample.resourceUrl ? { resource: { url: opts.x402Sample.resourceUrl, mimeType: "application/json" } } : {}
918
+ });
919
+ bodyObj.x402Version = x402Version;
920
+ const headerJson = JSON.parse(Buffer.from(headers["payment-required"], "base64").toString("utf-8"));
921
+ bodyObj.accepts = headerJson.accepts;
922
+ }
923
+ return {
924
+ status: 402,
925
+ headers,
926
+ body: JSON.stringify(bodyObj)
927
+ };
928
+ }
929
+ async function isDiscoveryProbeRequest(req) {
930
+ if (req.method !== "POST") return false;
931
+ const auth = req.headers.get("authorization");
932
+ if (auth?.startsWith("Payment ")) return false;
933
+ const body = await req.clone().text();
934
+ return !body || body === "{}";
935
+ }
936
+ var ZERO_EVM_PAYTO, ZERO_SOLANA_PAYTO;
937
+ var init_probe = __esm({
938
+ "src/discovery/probe.ts"() {
939
+ "use strict";
940
+ init_directive();
941
+ init_networks();
942
+ init_usdc();
943
+ init_wwwauthenticate();
944
+ ZERO_EVM_PAYTO = "0x0000000000000000000000000000000000000000";
945
+ ZERO_SOLANA_PAYTO = "11111111111111111111111111111111";
946
+ }
947
+ });
948
+
949
+ // src/discovery/well_known.ts
950
+ var well_known_exports = {};
951
+ __export(well_known_exports, {
952
+ bootstrapUcpSigningKey: () => bootstrapUcpSigningKey,
953
+ buildSignedJwksResponse: () => buildSignedJwksResponse,
954
+ buildSignedUcpResponse: () => buildSignedUcpResponse,
955
+ defaultA2aServices: () => defaultA2aServices,
956
+ signedResponseExpress: () => signedResponseExpress,
957
+ signedResponseFastify: () => signedResponseFastify,
958
+ signedResponseHono: () => signedResponseHono,
959
+ signedResponseNextjs: () => signedResponseNextjs,
960
+ signedResponseWeb: () => signedResponseWeb,
961
+ wellKnownCorsPreflightHeaders: () => wellKnownCorsPreflightHeaders,
962
+ wellKnownPreflightResponse: () => wellKnownPreflightResponse
963
+ });
964
+ function requestId(headers) {
965
+ if (headers === void 0) return void 0;
966
+ if (headers instanceof Headers) return headers.get("x-request-id") ?? void 0;
967
+ for (const [k, v] of Object.entries(headers)) {
968
+ if (k.toLowerCase() === "x-request-id") return v;
969
+ }
970
+ return void 0;
971
+ }
972
+ function attachRequestId(headers, requestHeaders) {
973
+ const rid = requestId(requestHeaders);
974
+ if (rid !== void 0) headers["X-Request-ID"] = rid;
975
+ }
976
+ function isTempoSession(s) {
977
+ return "escrowContract" in s && "store" in s;
978
+ }
979
+ function isStripe(s) {
980
+ return !("recipient" in s);
981
+ }
982
+ function railHasRecipientField(spec) {
983
+ return Object.hasOwn(spec, "recipient");
984
+ }
985
+ function composeHandlers(checkout) {
986
+ const handlers = {};
987
+ const mpp = [];
988
+ const x402 = [];
989
+ const stripe = [];
990
+ for (const spec of Object.values(checkout.rails)) {
991
+ if (isStripe(spec)) {
992
+ stripe.push(spec);
993
+ continue;
994
+ }
995
+ if (isTempoSession(spec)) {
996
+ if (railHasRecipientField(spec)) mpp.push(spec);
997
+ continue;
998
+ }
999
+ const network = spec.network ?? "";
1000
+ if (network.startsWith("eip155:") || "mode" in spec) {
1001
+ if (railHasRecipientField(spec)) x402.push(spec);
1002
+ } else if (network.startsWith("solana:") || "rpcUrl" in spec) {
1003
+ if (railHasRecipientField(spec)) mpp.push(spec);
1004
+ } else {
1005
+ if (railHasRecipientField(spec)) mpp.push(spec);
1006
+ }
1007
+ }
1008
+ if (mpp.length > 0) Object.assign(handlers, mppPaymentHandler({ networks: mpp }));
1009
+ if (x402.length > 0) Object.assign(handlers, x402PaymentHandler({ networks: x402 }));
1010
+ for (const spec of stripe) Object.assign(handlers, stripeSptPaymentHandler({ spec }));
1011
+ return handlers;
1012
+ }
1013
+ function misconfiguredResponse(requestHeaders) {
1014
+ const body = {
1015
+ error: {
1016
+ code: "ucp_misconfigured",
1017
+ message: "Merchant has no configured payment handlers."
1018
+ },
1019
+ next_steps: {
1020
+ action: "contact_merchant",
1021
+ user_message: "This merchant is temporarily unable to accept agent payments."
1022
+ },
1023
+ agent_instructions: {
1024
+ action: "contact_merchant",
1025
+ steps: [
1026
+ "Surface a transient error to the user.",
1027
+ "Retry later; the merchant operator will repair the configuration."
1028
+ ],
1029
+ user_message: "Merchant temporarily offline for agent payments."
1030
+ }
1031
+ };
1032
+ const headers = {
1033
+ "Access-Control-Allow-Origin": "*",
1034
+ "Cache-Control": `public, max-age=${UCP_CACHE_SECONDS}`
1035
+ };
1036
+ attachRequestId(headers, requestHeaders);
1037
+ return {
1038
+ body: JSON.stringify(body),
1039
+ mediaType: "application/json",
1040
+ headers,
1041
+ status: 503
1042
+ };
1043
+ }
1044
+ async function buildSignedUcpResponse(opts) {
1045
+ const {
1046
+ checkout,
1047
+ name,
1048
+ wellKnownUcpUrl,
1049
+ services,
1050
+ requestHeaders,
1051
+ signingKid = "merchant-default",
1052
+ agentscoreGate
1053
+ } = opts;
1054
+ const handlers = composeHandlers(checkout);
1055
+ if (Object.keys(handlers).length === 0) {
1056
+ return misconfiguredResponse(requestHeaders);
1057
+ }
1058
+ const key = await loadUCPSigningKeyFromEnv({ defaultKid: signingKid });
1059
+ const signingKeyEntry = UCPSigningKey.fromJWK(key.publicJWK);
1060
+ const profile = buildUCPProfile({
1061
+ name,
1062
+ supported_versions: { "2026-04-08": wellKnownUcpUrl },
1063
+ agentscore_gate: agentscoreGate,
1064
+ services,
1065
+ payment_handlers: handlers,
1066
+ signing_keys: [signingKeyEntry]
1067
+ });
1068
+ const signed = await signUCPProfile(profile, {
1069
+ signingKey: key.privateKey,
1070
+ kid: key.publicJWK.kid,
1071
+ alg: key.publicJWK.alg ?? "EdDSA"
1072
+ });
1073
+ const headers = {
1074
+ "Cache-Control": `public, max-age=${UCP_CACHE_SECONDS}`,
1075
+ "Access-Control-Allow-Origin": "*"
1076
+ };
1077
+ attachRequestId(headers, requestHeaders);
1078
+ return {
1079
+ body: JSON.stringify(signed),
1080
+ mediaType: "application/json",
1081
+ headers,
1082
+ status: 200
1083
+ };
1084
+ }
1085
+ async function buildSignedJwksResponse(opts) {
1086
+ const { requestHeaders, signingKid = "merchant-default" } = opts ?? {};
1087
+ const key = await loadUCPSigningKeyFromEnv({ defaultKid: signingKid });
1088
+ const jwks = buildJWKSResponse([UCPSigningKey.fromJWK(key.publicJWK)]);
1089
+ const headers = {
1090
+ "Cache-Control": `public, max-age=${JWKS_CACHE_SECONDS}`,
1091
+ "Access-Control-Allow-Origin": "*"
1092
+ };
1093
+ attachRequestId(headers, requestHeaders);
1094
+ return {
1095
+ body: JSON.stringify(jwks),
1096
+ mediaType: "application/jwk-set+json",
1097
+ headers,
1098
+ status: 200
1099
+ };
1100
+ }
1101
+ function wellKnownCorsPreflightHeaders(requestHeaders) {
1102
+ const headers = {
1103
+ "Access-Control-Allow-Origin": "*",
1104
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
1105
+ "Access-Control-Max-Age": "86400",
1106
+ Vary: "Access-Control-Request-Headers"
1107
+ };
1108
+ if (requestHeaders === void 0) return headers;
1109
+ const acrh = requestHeaders instanceof Headers ? requestHeaders.get("access-control-request-headers") : Object.entries(requestHeaders).find(([k]) => k.toLowerCase() === "access-control-request-headers")?.[1];
1110
+ if (acrh) headers["Access-Control-Allow-Headers"] = acrh;
1111
+ return headers;
1112
+ }
1113
+ function wellKnownPreflightResponse(requestHeaders) {
1114
+ return new Response(null, {
1115
+ status: 204,
1116
+ headers: wellKnownCorsPreflightHeaders(requestHeaders)
1117
+ });
1118
+ }
1119
+ function defaultA2aServices(opts) {
1120
+ return {
1121
+ "dev.ucp.shopping": [
1122
+ {
1123
+ version: "2026-04-08",
1124
+ spec: UCP_SHOPPING_SPEC_2026_04_08,
1125
+ transport: "a2a",
1126
+ endpoint: opts.agentCardUrl
1127
+ }
1128
+ ]
1129
+ };
1130
+ }
1131
+ async function bootstrapUcpSigningKey(opts) {
1132
+ const defaultKid = opts?.defaultKid ?? "merchant-default";
1133
+ await loadUCPSigningKeyFromEnv({ defaultKid });
1134
+ }
1135
+ function signedResponseHono(resp) {
1136
+ return new Response(resp.body, {
1137
+ status: resp.status,
1138
+ headers: { ...resp.headers, "Content-Type": resp.mediaType }
1139
+ });
1140
+ }
1141
+ function signedResponseNextjs(resp) {
1142
+ return signedResponseHono(resp);
1143
+ }
1144
+ function signedResponseWeb(resp) {
1145
+ return signedResponseHono(resp);
1146
+ }
1147
+ function signedResponseExpress(res, resp) {
1148
+ res.status(resp.status);
1149
+ res.set(resp.headers);
1150
+ res.type(resp.mediaType);
1151
+ res.send(resp.body);
1152
+ }
1153
+ function signedResponseFastify(reply, resp) {
1154
+ reply.code(resp.status);
1155
+ for (const [k, v] of Object.entries(resp.headers)) reply.header(k, v);
1156
+ reply.type(resp.mediaType);
1157
+ return reply.send(resp.body);
1158
+ }
1159
+ var UCP_CACHE_SECONDS, JWKS_CACHE_SECONDS, UCP_SHOPPING_SPEC_2026_04_08;
1160
+ var init_well_known = __esm({
1161
+ "src/discovery/well_known.ts"() {
1162
+ "use strict";
1163
+ init_ucp();
1164
+ init_ucp_jwks();
1165
+ UCP_CACHE_SECONDS = 60;
1166
+ JWKS_CACHE_SECONDS = 300;
1167
+ UCP_SHOPPING_SPEC_2026_04_08 = "https://ucp.dev/2026-04-08/specification/overview";
1168
+ }
1169
+ });
29
1170
 
30
1171
  // src/index.ts
31
1172
  var index_exports = {};
32
1173
  __export(index_exports, {
33
1174
  AGENTSCORE_UCP_CAPABILITY: () => AGENTSCORE_UCP_CAPABILITY,
1175
+ Checkout: () => Checkout,
1176
+ CheckoutValidationError: () => CheckoutValidationError,
34
1177
  FIXABLE_DENIAL_REASONS: () => FIXABLE_DENIAL_REASONS,
35
1178
  UCPSigningKey: () => UCPSigningKey,
36
1179
  UCPVerificationError: () => UCPVerificationError,
@@ -38,16 +1181,25 @@ __export(index_exports, {
38
1181
  buildA2AAgentCard: () => buildA2AAgentCard,
39
1182
  buildAgentMemoryHint: () => buildAgentMemoryHint,
40
1183
  buildContactSupportNextSteps: () => buildContactSupportNextSteps,
41
- buildGateOptionsFromPolicy: () => buildGateOptionsFromPolicy,
1184
+ buildGateFromPolicy: () => buildGateFromPolicy,
42
1185
  buildJWKSResponse: () => buildJWKSResponse,
43
1186
  buildSignerMismatchBody: () => buildSignerMismatchBody,
44
1187
  buildUCPProfile: () => buildUCPProfile,
45
1188
  denialReasonStatus: () => denialReasonStatus,
46
1189
  denialReasonToBody: () => denialReasonToBody,
47
1190
  extractPaymentSigner: () => extractPaymentSigner,
1191
+ extractPaymentSignerFromAuth: () => extractPaymentSignerFromAuth,
1192
+ extractSignerForPrecheck: () => extractSignerForPrecheck,
1193
+ formatUsdCents: () => formatUsdCents,
48
1194
  generateUCPSigningKey: () => generateUCPSigningKey,
1195
+ getIdentityStatus: () => getIdentityStatus,
1196
+ hashOperatorToken: () => hashOperatorToken,
49
1197
  isFixableDenial: () => isFixableDenial,
1198
+ loadSolanaFeePayer: () => loadSolanaFeePayer,
1199
+ loadUCPSigningKeyFromEnv: () => loadUCPSigningKeyFromEnv,
1200
+ makeMppxComposeHook: () => makeMppxComposeHook,
50
1201
  mppPaymentHandler: () => mppPaymentHandler,
1202
+ pricingResult: () => pricingResult,
51
1203
  readX402PaymentHeader: () => readX402PaymentHeader,
52
1204
  runGateWithEnforcement: () => runGateWithEnforcement,
53
1205
  shippingCountryAllowed: () => shippingCountryAllowed,
@@ -55,6 +1207,13 @@ __export(index_exports, {
55
1207
  signUCPProfile: () => signUCPProfile,
56
1208
  stripeSptPaymentHandler: () => stripeSptPaymentHandler,
57
1209
  ucpA2AExtension: () => ucpA2AExtension,
1210
+ validateShippingAgainstPolicy: () => validateShippingAgainstPolicy,
1211
+ validationEnvelope: () => validationEnvelope,
1212
+ validationResponseExpress: () => validationResponseExpress,
1213
+ validationResponseFastify: () => validationResponseFastify,
1214
+ validationResponseHono: () => validationResponseHono,
1215
+ validationResponseNextjs: () => validationResponseNextjs,
1216
+ validationResponseWeb: () => validationResponseWeb,
58
1217
  verificationAgentInstructions: () => verificationAgentInstructions,
59
1218
  verifyUCPProfile: () => verifyUCPProfile,
60
1219
  x402PaymentHandler: () => x402PaymentHandler
@@ -79,13 +1238,16 @@ function denialReasonStatus(reason) {
79
1238
  if (reason.code === "api_error") return 503;
80
1239
  return 403;
81
1240
  }
82
- function buildSignerMismatchBody(input) {
83
- const { result } = input;
1241
+ function buildSignerMismatchBody({
1242
+ result,
1243
+ userMessage,
1244
+ learnMoreUrl
1245
+ }) {
84
1246
  if (result.kind === "pass") return null;
85
- const learnMoreUrl = input.learnMoreUrl ?? "https://docs.agentscore.sh/guides/agent-identity";
1247
+ const learnMoreUrlResolved = learnMoreUrl ?? "https://docs.agentscore.sh/guides/agent-identity";
86
1248
  if (result.kind === "wallet_signer_mismatch") {
87
1249
  const linkedWallets = result.linkedWallets ?? [];
88
- const userMessage = input.userMessage ?? (linkedWallets.length > 0 ? `Sign the payment with one of the wallets linked to this operator: ${linkedWallets.join(", ")}. Then retry.` : "Sign the payment with the same wallet you claimed via X-Wallet-Address, or switch to X-Operator-Token for rail-independent identity.");
1250
+ const userMessageResolved = userMessage ?? (linkedWallets.length > 0 ? `Sign the payment with one of the wallets linked to this operator: ${linkedWallets.join(", ")}. Then retry.` : "Sign the payment with the same wallet you claimed via X-Wallet-Address, or switch to X-Operator-Token for rail-independent identity.");
89
1251
  return {
90
1252
  error: {
91
1253
  code: "wallet_signer_mismatch",
@@ -98,8 +1260,8 @@ function buildSignerMismatchBody(input) {
98
1260
  linked_wallets: linkedWallets,
99
1261
  next_steps: {
100
1262
  action: "regenerate_payment_from_linked_wallet",
101
- user_message: userMessage,
102
- learn_more_url: learnMoreUrl
1263
+ user_message: userMessageResolved,
1264
+ learn_more_url: learnMoreUrlResolved
103
1265
  }
104
1266
  };
105
1267
  }
@@ -110,8 +1272,8 @@ function buildSignerMismatchBody(input) {
110
1272
  },
111
1273
  next_steps: {
112
1274
  action: "switch_to_operator_token",
113
- user_message: input.userMessage ?? "Drop the X-Wallet-Address header and retry with X-Operator-Token (works on every payment rail).",
114
- learn_more_url: learnMoreUrl
1275
+ user_message: userMessage ?? "Drop the X-Wallet-Address header and retry with X-Operator-Token (works on every payment rail).",
1276
+ learn_more_url: learnMoreUrlResolved
115
1277
  }
116
1278
  };
117
1279
  }
@@ -119,27 +1281,35 @@ function buildContactSupportNextSteps(supportEmail, message) {
119
1281
  return {
120
1282
  action: "contact_support",
121
1283
  support_email: supportEmail,
122
- user_message: message ?? `If you believe this denial is in error, contact support at ${supportEmail} with your order details.`
1284
+ user_message: message ?? `If you believe this denial is in error, contact support at ${supportEmail} with the details of your request.`
123
1285
  };
124
1286
  }
125
- function verificationAgentInstructions(input = {}) {
1287
+ function verificationAgentInstructions({
1288
+ userAction,
1289
+ retryStep,
1290
+ extraSteps,
1291
+ pollIntervalSeconds = 5,
1292
+ timeoutSeconds = 3600,
1293
+ orderTtl,
1294
+ extra
1295
+ } = {}) {
126
1296
  const baseSteps = [
127
1297
  "Present the verify_url directly to the user \u2014 it is a complete, ready-to-open URL with the session token already embedded (e.g. https://agentscore.sh/verify?session=sess_...). Do NOT modify or construct the URL yourself.",
128
- `Immediately begin polling poll_url every ${input.pollIntervalSeconds ?? 5} seconds with header X-Poll-Secret set to poll_secret. The user will complete verification in their browser while you poll in the background.`,
1298
+ `Immediately begin polling poll_url every ${pollIntervalSeconds} seconds with header X-Poll-Secret set to poll_secret. The user will complete verification in their browser while you poll in the background.`,
129
1299
  "The user visits the URL, signs in, completes identity verification (photo ID + selfie via Stripe Identity), and closes the tab. They do NOT need to copy or paste anything back to you.",
130
1300
  'When your poll returns status "verified", extract operator_token from the response. This is a one-time value \u2014 save it immediately. Subsequent polls return status "consumed" without the token.',
131
- input.retryStep ?? "Retry the original merchant request with header X-Operator-Token set to the operator_token value."
1301
+ retryStep ?? "Retry the original merchant request with header X-Operator-Token set to the operator_token value."
132
1302
  ];
133
1303
  return {
134
1304
  action: "poll_for_credential",
135
- user_action: input.userAction ?? "The user must visit verify_url to complete identity verification before this request can proceed",
136
- steps: input.extraSteps ? [...baseSteps, ...input.extraSteps] : baseSteps,
137
- poll_interval_seconds: input.pollIntervalSeconds ?? 5,
1305
+ user_action: userAction ?? "The user must visit verify_url to complete identity verification before this request can proceed",
1306
+ steps: extraSteps ? [...baseSteps, ...extraSteps] : baseSteps,
1307
+ poll_interval_seconds: pollIntervalSeconds,
138
1308
  poll_secret_header: "X-Poll-Secret",
139
1309
  retry_token_header: "X-Operator-Token",
140
- timeout_seconds: input.timeoutSeconds ?? 3600,
141
- ...input.orderTtl ? { order_ttl: input.orderTtl } : {},
142
- ...input.extra ?? {}
1310
+ timeout_seconds: timeoutSeconds,
1311
+ ...orderTtl ? { order_ttl: orderTtl } : {},
1312
+ ...extra ?? {}
143
1313
  };
144
1314
  }
145
1315
 
@@ -260,7 +1430,72 @@ function denialReasonToBody(reason) {
260
1430
  return body;
261
1431
  }
262
1432
 
1433
+ // src/address.ts
1434
+ var SOLANA_BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
1435
+ var isSolanaAddress = (address) => SOLANA_BASE58_RE.test(address) && !address.startsWith("0x");
1436
+ var normalizeAddress = (address) => {
1437
+ if (isSolanaAddress(address)) {
1438
+ return address;
1439
+ }
1440
+ return address.toLowerCase();
1441
+ };
1442
+
1443
+ // src/cache.ts
1444
+ var TTLCache = class {
1445
+ constructor(defaultTtlMs, maxSize = 1e4) {
1446
+ this.defaultTtlMs = defaultTtlMs;
1447
+ this.maxSize = maxSize;
1448
+ }
1449
+ defaultTtlMs;
1450
+ store = /* @__PURE__ */ new Map();
1451
+ maxSize;
1452
+ get(key) {
1453
+ const entry = this.store.get(key);
1454
+ if (!entry) return void 0;
1455
+ if (Date.now() > entry.expiresAt) {
1456
+ this.store.delete(key);
1457
+ return void 0;
1458
+ }
1459
+ return entry.value;
1460
+ }
1461
+ set(key, value, ttlMs) {
1462
+ if (this.store.size >= this.maxSize) {
1463
+ this.sweep();
1464
+ }
1465
+ if (this.store.size >= this.maxSize) {
1466
+ this.evictOldest(this.store.size - this.maxSize + 1);
1467
+ }
1468
+ this.store.set(key, {
1469
+ value,
1470
+ expiresAt: Date.now() + (ttlMs ?? this.defaultTtlMs)
1471
+ });
1472
+ }
1473
+ /** Remove all expired entries. */
1474
+ sweep() {
1475
+ const now = Date.now();
1476
+ for (const [k, v] of this.store) {
1477
+ if (now > v.expiresAt) {
1478
+ this.store.delete(k);
1479
+ }
1480
+ }
1481
+ }
1482
+ /** Evict the oldest `count` entries by insertion order. */
1483
+ evictOldest(count) {
1484
+ let removed = 0;
1485
+ for (const key of this.store.keys()) {
1486
+ if (removed >= count) break;
1487
+ this.store.delete(key);
1488
+ removed++;
1489
+ }
1490
+ }
1491
+ };
1492
+
263
1493
  // src/core.ts
1494
+ function stripTrailingSlashes(s) {
1495
+ let end = s.length;
1496
+ while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
1497
+ return end === s.length ? s : s.slice(0, end);
1498
+ }
264
1499
  var CANONICAL_AGENTSCORE_API = "https://api.agentscore.sh";
265
1500
  var WALLET_SIGNER_MISMATCH_INSTRUCTIONS = JSON.stringify({
266
1501
  action: "resign_or_switch_to_operator_token",
@@ -303,6 +1538,317 @@ function buildAgentMemoryHint() {
303
1538
  persist_in_credential_store: ["operator_token"]
304
1539
  };
305
1540
  }
1541
+ function createAgentScoreCore(options) {
1542
+ if (!options.apiKey) {
1543
+ throw new Error("AgentScore API key is required. Get one at https://agentscore.sh/sign-up");
1544
+ }
1545
+ const {
1546
+ apiKey,
1547
+ requireKyc,
1548
+ requireSanctionsClear,
1549
+ minAge,
1550
+ blockedJurisdictions,
1551
+ allowedJurisdictions,
1552
+ failOpen = false,
1553
+ cacheSeconds = 300,
1554
+ baseUrl: rawBaseUrl = "https://api.agentscore.sh",
1555
+ chain: gateChain,
1556
+ userAgent,
1557
+ createSessionOnMissing
1558
+ } = options;
1559
+ const baseUrl = stripTrailingSlashes(rawBaseUrl);
1560
+ const agentMemoryHint = buildAgentMemoryHint();
1561
+ const defaultUa = `@agent-score/commerce@${"2.0.0"}`;
1562
+ const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
1563
+ const sdk = new import_sdk.AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
1564
+ const sessionSdkCache = /* @__PURE__ */ new Map();
1565
+ function getSessionSdk(sessionApiKey, sessionBaseUrl) {
1566
+ const key = `${sessionApiKey}|${sessionBaseUrl ?? ""}`;
1567
+ let s = sessionSdkCache.get(key);
1568
+ if (!s) {
1569
+ s = new import_sdk.AgentScore({
1570
+ apiKey: sessionApiKey,
1571
+ baseUrl: sessionBaseUrl ?? baseUrl,
1572
+ userAgent: userAgentHeader
1573
+ });
1574
+ sessionSdkCache.set(key, s);
1575
+ }
1576
+ return s;
1577
+ }
1578
+ const cache = new TTLCache(cacheSeconds * 1e3);
1579
+ async function tryMintSessionDenial(ctx) {
1580
+ if (!createSessionOnMissing) return void 0;
1581
+ try {
1582
+ const sessionBody = {};
1583
+ if (createSessionOnMissing.context != null) sessionBody.context = createSessionOnMissing.context;
1584
+ if (createSessionOnMissing.productName != null) sessionBody.product_name = createSessionOnMissing.productName;
1585
+ if (createSessionOnMissing.getSessionOptions && ctx !== void 0) {
1586
+ try {
1587
+ const dynamic = await createSessionOnMissing.getSessionOptions(ctx);
1588
+ if (dynamic?.context != null) sessionBody.context = dynamic.context;
1589
+ if (dynamic?.productName != null) sessionBody.product_name = dynamic.productName;
1590
+ } catch (err) {
1591
+ console.warn("[gate] createSessionOnMissing.getSessionOptions hook failed:", err instanceof Error ? err.message : err);
1592
+ }
1593
+ }
1594
+ const sessionSdk = getSessionSdk(createSessionOnMissing.apiKey, createSessionOnMissing.baseUrl);
1595
+ const data = await sessionSdk.createSession({
1596
+ ...sessionBody.context !== void 0 ? { context: sessionBody.context } : {},
1597
+ ...sessionBody.product_name !== void 0 ? { product_name: sessionBody.product_name } : {}
1598
+ });
1599
+ if (typeof data.session_id !== "string" || typeof data.poll_secret !== "string" || typeof data.verify_url !== "string") {
1600
+ console.warn("[gate] /v1/sessions returned 200 without required fields \u2014 falling back to bare denial");
1601
+ return void 0;
1602
+ }
1603
+ let extra;
1604
+ if (createSessionOnMissing.onBeforeSession && ctx !== void 0) {
1605
+ try {
1606
+ const sessionMeta = {
1607
+ session_id: data.session_id,
1608
+ verify_url: data.verify_url,
1609
+ poll_secret: data.poll_secret,
1610
+ poll_url: data.poll_url,
1611
+ expires_at: data.expires_at
1612
+ };
1613
+ const result = await createSessionOnMissing.onBeforeSession(ctx, sessionMeta);
1614
+ if (result && typeof result === "object") extra = result;
1615
+ } catch (err) {
1616
+ console.warn("[gate] createSessionOnMissing.onBeforeSession hook failed:", err instanceof Error ? err.message : err);
1617
+ }
1618
+ }
1619
+ const apiNextSteps = data.next_steps;
1620
+ return {
1621
+ code: "identity_verification_required",
1622
+ verify_url: data.verify_url,
1623
+ session_id: data.session_id,
1624
+ poll_secret: data.poll_secret,
1625
+ poll_url: data.poll_url,
1626
+ agent_instructions: apiNextSteps ? JSON.stringify(apiNextSteps) : void 0,
1627
+ agent_memory: agentMemoryHint,
1628
+ ...extra && { extra }
1629
+ };
1630
+ } catch (err) {
1631
+ console.warn("[gate] createSessionOnMissing path failed \u2014 falling back to bare denial:", err instanceof Error ? err.message : err);
1632
+ return void 0;
1633
+ }
1634
+ }
1635
+ async function evaluate(identity, ctx, signer) {
1636
+ if (!identity || !identity.address && !identity.operatorToken) {
1637
+ if (failOpen) return { kind: "allow" };
1638
+ const sessionReason = await tryMintSessionDenial(ctx);
1639
+ if (sessionReason) return { kind: "deny", reason: sessionReason };
1640
+ const missingIdentityInstructions = JSON.stringify({
1641
+ action: "probe_identity_then_session",
1642
+ steps: [
1643
+ "If you have a wallet AND this merchant accepts a wallet-signing rail (Tempo MPP or x402 \u2014 see openapi.json x-payment-info): send X-Wallet-Address and retry. If the wallet is already linked to an AgentScore operator (via prior SIWE claim or prior captureWallet), this passes in one round trip. If the wallet is unlinked or the account has no KYC, the 403 will include a verify_url \u2014 share it with the user to claim the wallet + complete KYC, then retry.",
1644
+ "If step 1 is denied or you already have a stored operator_token (valid + not expired): send X-Operator-Token: opc_... and retry.",
1645
+ "If neither applies: retry with NO identity header. Merchants that auto-create verification sessions (most AgentScore merchants do) return verify_url + session_id + poll_secret in the 403 body \u2014 share verify_url with the user, then poll poll_url every 5s with the X-Poll-Secret header until status=verified (the poll returns a one-time operator_token). If the retry returns the same bare 403, this merchant does not support self-service session bootstrapping \u2014 direct the user to https://agentscore.sh/sign-up to create an AgentScore identity and mint an operator_token from their dashboard (https://agentscore.sh/dashboard/verify). The user hands the opc_... to you, and you retry with X-Operator-Token."
1646
+ ],
1647
+ user_message: "Try X-Wallet-Address first if you have a wallet and the merchant accepts Tempo/x402; fall back to a stored X-Operator-Token, then to the session/verify flow described in agent_memory.bootstrap."
1648
+ });
1649
+ return {
1650
+ kind: "deny",
1651
+ reason: {
1652
+ code: "missing_identity",
1653
+ agent_instructions: missingIdentityInstructions,
1654
+ agent_memory: agentMemoryHint
1655
+ }
1656
+ };
1657
+ }
1658
+ const cacheKey2 = identity.operatorToken?.toLowerCase() ?? (identity.address ? normalizeAddress(identity.address) : "");
1659
+ const cached = cache.get(cacheKey2);
1660
+ if (cached) {
1661
+ if (cached.allow) {
1662
+ const cachedRaw = cached.raw;
1663
+ const cachedQuota = cachedRaw?.quota;
1664
+ return {
1665
+ kind: "allow",
1666
+ data: cachedRaw,
1667
+ ...cachedQuota !== void 0 && { quota: cachedQuota }
1668
+ };
1669
+ }
1670
+ if (isFixableDenial(cached.reasons)) {
1671
+ const sessionReason = await tryMintSessionDenial(ctx);
1672
+ if (sessionReason) return { kind: "deny", reason: sessionReason };
1673
+ }
1674
+ return {
1675
+ kind: "deny",
1676
+ reason: {
1677
+ code: "wallet_not_trusted",
1678
+ decision: cached.decision,
1679
+ reasons: cached.reasons,
1680
+ verify_url: cached.raw?.verify_url,
1681
+ data: cached.raw
1682
+ }
1683
+ };
1684
+ }
1685
+ const policy = {};
1686
+ if (requireKyc != null) policy.require_kyc = requireKyc;
1687
+ if (requireSanctionsClear != null) policy.require_sanctions_clear = requireSanctionsClear;
1688
+ if (minAge != null) policy.min_age = minAge;
1689
+ if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
1690
+ if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
1691
+ let data;
1692
+ try {
1693
+ const opts = {
1694
+ chain: gateChain,
1695
+ ...Object.keys(policy).length > 0 ? { policy } : {},
1696
+ // Pre-extracted payment signer (by the adapter middleware). When present, the API
1697
+ // composes BOTH signer_match (wallet-binding) and signer_sanctions (OFAC SDN wallet
1698
+ // check) verdicts on the response in one round trip. Under
1699
+ // policy.require_sanctions_clear, a signer_sanctions hit flips decision -> deny inline.
1700
+ ...signer && { signer: { address: signer.address, network: signer.network } }
1701
+ };
1702
+ const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
1703
+ data = result;
1704
+ } catch (err) {
1705
+ if (err instanceof import_sdk.PaymentRequiredError) {
1706
+ if (failOpen) return { kind: "allow" };
1707
+ return { kind: "deny", reason: { code: "payment_required" } };
1708
+ }
1709
+ if (err instanceof import_sdk.TokenExpiredError) {
1710
+ return {
1711
+ kind: "deny",
1712
+ reason: {
1713
+ code: "token_expired",
1714
+ data: err.details,
1715
+ ...err.verifyUrl ? { verify_url: err.verifyUrl } : {},
1716
+ ...err.sessionId ? { session_id: err.sessionId } : {},
1717
+ ...err.pollSecret ? { poll_secret: err.pollSecret } : {},
1718
+ ...err.pollUrl ? { poll_url: err.pollUrl } : {},
1719
+ ...err.nextSteps ? { agent_instructions: JSON.stringify(err.nextSteps) } : {},
1720
+ ...err.agentMemory ? { agent_memory: err.agentMemory } : {}
1721
+ }
1722
+ };
1723
+ }
1724
+ if (err instanceof import_sdk.InvalidCredentialError) {
1725
+ return {
1726
+ kind: "deny",
1727
+ reason: {
1728
+ code: "invalid_credential",
1729
+ agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
1730
+ agent_memory: agentMemoryHint
1731
+ }
1732
+ };
1733
+ }
1734
+ if (err instanceof import_sdk.QuotaExceededError) {
1735
+ console.warn("[gate] /v1/assess returned 429 quota_exceeded");
1736
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
1737
+ return {
1738
+ kind: "deny",
1739
+ reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
1740
+ };
1741
+ }
1742
+ if (err instanceof import_sdk.TimeoutError) {
1743
+ console.warn("[gate] /v1/assess timed out:", err.message);
1744
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
1745
+ return { kind: "deny", reason: { code: "api_error" } };
1746
+ }
1747
+ const status = err?.status;
1748
+ const errName = err instanceof Error ? err.name : "";
1749
+ if (status === 429) {
1750
+ console.warn("[gate] /v1/assess returned 429 (untyped \u2014 defensive)");
1751
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
1752
+ return {
1753
+ kind: "deny",
1754
+ reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
1755
+ };
1756
+ }
1757
+ if (errName === "TimeoutError" || errName === "AbortError") {
1758
+ console.warn("[gate] /v1/assess timed out (by Error.name):", err instanceof Error ? err.message : err);
1759
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
1760
+ return { kind: "deny", reason: { code: "api_error" } };
1761
+ }
1762
+ const errCode = err?.code;
1763
+ const msg = err instanceof Error ? err.message : String(err);
1764
+ const detail = errCode ? `${errCode}: ${msg}` : msg;
1765
+ console.warn(`[gate] /v1/assess call failed \u2014 surfacing as api_error: ${detail}`);
1766
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "api_error" };
1767
+ return { kind: "deny", reason: { code: "api_error" } };
1768
+ }
1769
+ const decision = data.decision;
1770
+ const decisionReasons = data.decision_reasons ?? [];
1771
+ const allow = decision === "allow" || decision == null;
1772
+ cache.set(cacheKey2, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
1773
+ if (allow) {
1774
+ const quota = data.quota;
1775
+ return {
1776
+ kind: "allow",
1777
+ data,
1778
+ ...quota !== void 0 && { quota }
1779
+ };
1780
+ }
1781
+ if (isFixableDenial(decisionReasons)) {
1782
+ const sessionReason = await tryMintSessionDenial(ctx);
1783
+ if (sessionReason) return { kind: "deny", reason: sessionReason };
1784
+ }
1785
+ return {
1786
+ kind: "deny",
1787
+ reason: {
1788
+ code: "wallet_not_trusted",
1789
+ decision: decision ?? void 0,
1790
+ reasons: decisionReasons,
1791
+ verify_url: data.verify_url,
1792
+ data
1793
+ }
1794
+ };
1795
+ }
1796
+ async function captureWallet(options2) {
1797
+ try {
1798
+ await sdk.associateWallet({
1799
+ operatorToken: options2.operatorToken,
1800
+ walletAddress: options2.walletAddress,
1801
+ network: options2.network,
1802
+ ...options2.idempotencyKey ? { idempotencyKey: options2.idempotencyKey } : {}
1803
+ });
1804
+ } catch (err) {
1805
+ console.warn("[agentscore-commerce] captureWallet failed:", err instanceof Error ? err.message : err);
1806
+ }
1807
+ }
1808
+ function projectSignerMatch(sm, claimedNorm, signerNorm) {
1809
+ const kind = sm.kind;
1810
+ if (kind === "pass") {
1811
+ return {
1812
+ kind: "pass",
1813
+ claimedOperator: sm.claimed_operator ?? null,
1814
+ signerOperator: sm.signer_operator ?? null
1815
+ };
1816
+ }
1817
+ if (kind === "wallet_auth_requires_wallet_signing") {
1818
+ return {
1819
+ kind: "wallet_auth_requires_wallet_signing",
1820
+ claimedWallet: sm.claimed_wallet ?? claimedNorm,
1821
+ agentInstructions: sm.agent_instructions ?? WALLET_AUTH_REQUIRES_WALLET_SIGNING_INSTRUCTIONS
1822
+ };
1823
+ }
1824
+ const linked = sm.linked_wallets;
1825
+ return {
1826
+ kind: "wallet_signer_mismatch",
1827
+ claimedOperator: sm.claimed_operator ?? null,
1828
+ actualSignerOperator: sm.signer_operator ?? null,
1829
+ expectedSigner: sm.expected_signer ?? claimedNorm,
1830
+ actualSigner: sm.actual_signer ?? signerNorm,
1831
+ linkedWallets: Array.isArray(linked) ? linked.filter((w) => typeof w === "string") : [],
1832
+ agentInstructions: sm.agent_instructions ?? WALLET_SIGNER_MISMATCH_INSTRUCTIONS
1833
+ };
1834
+ }
1835
+ function getSignerVerdict(claimedAddress) {
1836
+ const claimedNorm = normalizeAddress(claimedAddress);
1837
+ const cached = cache.get(claimedNorm);
1838
+ if (!cached) return void 0;
1839
+ const raw = cached.raw;
1840
+ if (!raw) return void 0;
1841
+ const rawMatch = raw.signer_match;
1842
+ const rawSanctions = raw.signer_sanctions;
1843
+ if (!rawMatch && !rawSanctions) return void 0;
1844
+ const signerNorm = rawMatch?.actual_signer ?? claimedNorm;
1845
+ return {
1846
+ signer_match: rawMatch ? projectSignerMatch(rawMatch, claimedNorm, signerNorm) : null,
1847
+ signer_sanctions: rawSanctions ?? null
1848
+ };
1849
+ }
1850
+ return { evaluate, captureWallet, getSignerVerdict };
1851
+ }
306
1852
 
307
1853
  // src/signer.ts
308
1854
  var TOKEN_PROGRAM = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
@@ -377,9 +1923,33 @@ async function extractPaymentSigner(request, x402PaymentHeader) {
377
1923
  }
378
1924
  return null;
379
1925
  }
1926
+ async function extractPaymentSignerFromAuth(authHeader, x402PaymentHeader) {
1927
+ const request = new Request("http://internal.gate/", {
1928
+ headers: authHeader ? { authorization: authHeader } : {}
1929
+ });
1930
+ return extractPaymentSigner(request, x402PaymentHeader);
1931
+ }
380
1932
  function readX402PaymentHeader(request) {
381
1933
  return request.headers.get("payment-signature") ?? request.headers.get("x-payment") ?? void 0;
382
1934
  }
1935
+ function lowerHeaders(headers) {
1936
+ const out = {};
1937
+ for (const [k, v] of Object.entries(headers)) out[k.toLowerCase()] = v;
1938
+ return out;
1939
+ }
1940
+ async function extractSignerForPrecheck(headers) {
1941
+ const lower = lowerHeaders(headers);
1942
+ const x402 = lower["payment-signature"] ?? lower["x-payment"];
1943
+ if (x402) {
1944
+ const signer = await extractPaymentSignerFromAuth(void 0, x402);
1945
+ if (signer !== null) return signer;
1946
+ }
1947
+ const authorization = lower["authorization"];
1948
+ if (authorization && authorization.toLowerCase().startsWith("payment ")) {
1949
+ return await extractPaymentSignerFromAuth(authorization);
1950
+ }
1951
+ return null;
1952
+ }
383
1953
 
384
1954
  // src/identity/a2a.ts
385
1955
  var PROTOCOL_VERSION = "1.0";
@@ -435,434 +2005,2062 @@ function buildA2AAgentCard(input) {
435
2005
  return card;
436
2006
  }
437
2007
 
438
- // src/identity/ucp.ts
439
- function ucpSigningKeyFromJWKImpl(jwk) {
440
- if (!jwk || typeof jwk !== "object") {
441
- throw new Error(`UCPSigningKey.fromJWK expected a non-null object; got ${typeof jwk}.`);
2008
+ // src/index.ts
2009
+ init_ucp();
2010
+ init_ucp_jwks();
2011
+
2012
+ // src/checkout.ts
2013
+ var import_node_crypto = require("crypto");
2014
+
2015
+ // src/payment/rail_spec.ts
2016
+ init_usdc();
2017
+ async function resolveRecipient(r) {
2018
+ if (typeof r === "string") return r;
2019
+ return Promise.resolve(r());
2020
+ }
2021
+ var RAIL_SPEC_DEFAULTS = {
2022
+ tempo: {
2023
+ network: "tempo-mainnet",
2024
+ chainId: 4217,
2025
+ token: USDC.tempo.mainnet.address,
2026
+ symbol: "USDC.e",
2027
+ decimals: 6,
2028
+ testnet: false,
2029
+ recommend: "both"
2030
+ },
2031
+ x402Base: {
2032
+ network: "eip155:8453",
2033
+ chainId: 8453,
2034
+ token: USDC.base.mainnet.address,
2035
+ symbol: "USDC",
2036
+ decimals: 6,
2037
+ mode: "exact"
2038
+ },
2039
+ solanaMpp: {
2040
+ network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
2041
+ token: USDC.solana.mainnet.mint,
2042
+ symbol: "USDC",
2043
+ decimals: 6
2044
+ },
2045
+ stripe: {
2046
+ rails: ["card", "link", "shared_payment_token"]
2047
+ },
2048
+ tempoSession: {
2049
+ currency: USDC.tempo.mainnet.address,
2050
+ testnet: false
442
2051
  }
443
- if (typeof jwk.kid !== "string" || !jwk.kid) {
444
- throw new Error("UCPSigningKey.fromJWK: JWK missing required field `kid` (or non-string).");
2052
+ };
2053
+
2054
+ // src/challenge/accepted_methods.ts
2055
+ async function buildAcceptedMethods({
2056
+ tempo,
2057
+ x402_base,
2058
+ solana_mpp,
2059
+ stripe
2060
+ }) {
2061
+ const out = [];
2062
+ if (tempo) {
2063
+ out.push({
2064
+ method: "tempo/charge",
2065
+ network: tempo.network ?? RAIL_SPEC_DEFAULTS.tempo.network,
2066
+ chain_id: tempo.chainId ?? RAIL_SPEC_DEFAULTS.tempo.chainId,
2067
+ token: tempo.token ?? RAIL_SPEC_DEFAULTS.tempo.token,
2068
+ symbol: tempo.symbol ?? RAIL_SPEC_DEFAULTS.tempo.symbol,
2069
+ decimals: tempo.decimals ?? RAIL_SPEC_DEFAULTS.tempo.decimals,
2070
+ pay_to: await resolveRecipient(tempo.recipient)
2071
+ });
445
2072
  }
446
- if (typeof jwk.kty !== "string" || !jwk.kty) {
447
- throw new Error("UCPSigningKey.fromJWK: JWK missing required field `kty` (or non-string).");
2073
+ if (x402_base) {
2074
+ out.push({
2075
+ method: "x402/exact",
2076
+ network: x402_base.network ?? RAIL_SPEC_DEFAULTS.x402Base.network,
2077
+ chain_id: x402_base.chainId ?? RAIL_SPEC_DEFAULTS.x402Base.chainId,
2078
+ token: x402_base.token ?? RAIL_SPEC_DEFAULTS.x402Base.token,
2079
+ symbol: x402_base.symbol ?? RAIL_SPEC_DEFAULTS.x402Base.symbol,
2080
+ decimals: x402_base.decimals ?? RAIL_SPEC_DEFAULTS.x402Base.decimals,
2081
+ pay_to: await resolveRecipient(x402_base.recipient)
2082
+ });
448
2083
  }
449
- if (jwk.kty !== "OKP" && jwk.kty !== "EC" && jwk.kty !== "RSA") {
450
- throw new Error(
451
- `UCPSigningKey.fromJWK: kty=${JSON.stringify(jwk.kty)} is not a supported asymmetric key type (expected OKP, EC, or RSA). Symmetric \`oct\` keys are rejected because they cannot publicly verify a JWS in the trust-mode UCP flow.`
452
- );
2084
+ if (solana_mpp) {
2085
+ out.push({
2086
+ method: "solana/charge",
2087
+ network: solana_mpp.network ?? RAIL_SPEC_DEFAULTS.solanaMpp.network,
2088
+ token: solana_mpp.token ?? RAIL_SPEC_DEFAULTS.solanaMpp.token,
2089
+ symbol: solana_mpp.symbol ?? RAIL_SPEC_DEFAULTS.solanaMpp.symbol,
2090
+ decimals: solana_mpp.decimals ?? RAIL_SPEC_DEFAULTS.solanaMpp.decimals,
2091
+ pay_to: await resolveRecipient(solana_mpp.recipient)
2092
+ });
453
2093
  }
454
- if ((jwk.kty === "EC" || jwk.kty === "OKP") && (typeof jwk.crv !== "string" || !jwk.crv)) {
455
- throw new Error(`UCPSigningKey.fromJWK: kty=${jwk.kty} requires a non-empty \`crv\` field (e.g., "P-256" for EC, "Ed25519" for OKP).`);
2094
+ if (stripe) {
2095
+ out.push({
2096
+ method: "stripe/charge",
2097
+ rails: stripe.rails ?? [...RAIL_SPEC_DEFAULTS.stripe.rails],
2098
+ profile_id: stripe.profileId ?? null
2099
+ });
456
2100
  }
457
- return jwk;
2101
+ return out;
458
2102
  }
459
- var UCPSigningKey = {
460
- fromJWK: ucpSigningKeyFromJWKImpl
2103
+
2104
+ // src/challenge/agent_instructions.ts
2105
+ var TEMPO_WARNING = "Do NOT use `tempo wallet transfer`. That moves USDC on-chain without completing the protocol handshake, so the request will not complete. Use `tempo request` instead.";
2106
+ var X402_WARNING = "Do NOT send USDC manually to the deposit addresses. Use `agentscore-pay pay` so the credential is signed and submitted; otherwise the request will not complete even though the deposit lands.";
2107
+ var TEMPO_TOOL = "`tempo request` for Tempo USDC";
2108
+ var AGENTSCORE_PAY_TOOL = "`agentscore-pay` \u2014 Base + Solana + Tempo from one CLI";
2109
+ var DEFAULT_WALLET_COMPATIBILITY = "Any client that can produce a valid MPP credential (Authorization: Payment) or x402 X-Payment header. Use the CLI commands above; sign-it-yourself is also fine.";
2110
+ function defaultRecommendedTools(howToPay) {
2111
+ const tools = [];
2112
+ if (howToPay.tempo) tools.push(TEMPO_TOOL);
2113
+ if (howToPay.tempo || howToPay.x402_base || howToPay.solana_mpp) tools.push(AGENTSCORE_PAY_TOOL);
2114
+ return tools;
2115
+ }
2116
+ function defaultWarnings(howToPay) {
2117
+ const w = [];
2118
+ if (howToPay.tempo) w.push(TEMPO_WARNING);
2119
+ if (howToPay.x402_base) w.push(X402_WARNING);
2120
+ return w;
2121
+ }
2122
+ var RAIL_CLIENTS = {
2123
+ tempo_mpp: ["agentscore-pay", "tempo request", "x402-proxy"],
2124
+ x402_base: ["agentscore-pay", "x402-proxy", "purl (omit --network flag)"],
2125
+ solana_mpp: ["agentscore-pay"],
2126
+ stripe: ["link-cli"]
461
2127
  };
462
- var DEFAULT_VERSION = "2026-04-08";
463
- var AGENTSCORE_CAPABILITY_NAME = "sh.agentscore.identity";
464
- var AGENTSCORE_CAPABILITY_VERSION = "2026-04-08";
465
- var AGENTSCORE_DEFAULT_SPEC_URL = "https://agentscore.sh/specification/identity";
466
- var AGENTSCORE_DEFAULT_SCHEMA_URL = "https://agentscore.sh/schemas/ucp/sh-agentscore-identity-v1.json";
467
- var AGENTSCORE_EXTENDS = ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"];
468
- var RESERVED_TOP_LEVEL = /* @__PURE__ */ new Set([
469
- "ucp",
470
- "signing_keys",
471
- "signature",
472
- "__proto__",
473
- "constructor",
474
- "prototype"
475
- ]);
476
- var RESERVED_UCP_FIELDS = /* @__PURE__ */ new Set([
477
- "version",
478
- "name",
479
- "services",
480
- "capabilities",
481
- "payment_handlers",
482
- "supported_versions",
483
- "__proto__",
484
- "constructor",
485
- "prototype"
486
- ]);
487
- function buildUCPProfile(input) {
488
- for (const [name, bindings] of Object.entries(input.services ?? {})) {
489
- for (const binding of bindings) {
490
- if ((binding.transport === "rest" || binding.transport === "mcp" || binding.transport === "a2a") && (binding.endpoint === void 0 || binding.endpoint === null || binding.endpoint === "")) {
491
- throw new Error(
492
- `buildUCPProfile: service "${name}" transport=${binding.transport} requires \`endpoint\`. Per UCP spec service.json business_schema, rest/mcp/a2a bindings MUST carry an endpoint URL.`
493
- );
494
- }
2128
+ function compatibleClientsByRails(rails2) {
2129
+ const out = {};
2130
+ for (const r of rails2) out[r] = [...RAIL_CLIENTS[r]];
2131
+ return Object.keys(out).length === 0 ? void 0 : out;
2132
+ }
2133
+ function defaultCompatibleClients(howToPay) {
2134
+ const rails2 = [];
2135
+ if (howToPay.tempo) rails2.push("tempo_mpp");
2136
+ if (howToPay.x402_base) rails2.push("x402_base");
2137
+ if (howToPay.solana_mpp) rails2.push("solana_mpp");
2138
+ if (howToPay.stripe) rails2.push("stripe");
2139
+ return compatibleClientsByRails(rails2);
2140
+ }
2141
+ function buildAgentInstructions({
2142
+ howToPay,
2143
+ recommendedTools,
2144
+ walletCompatibility,
2145
+ timeoutSeconds,
2146
+ warnings,
2147
+ extraWarnings,
2148
+ recommended,
2149
+ compatibleClients,
2150
+ extra
2151
+ }) {
2152
+ const compatibleClientsOut = compatibleClients ?? defaultCompatibleClients(howToPay);
2153
+ return {
2154
+ how_to_pay: howToPay,
2155
+ recommended_tools: recommendedTools ?? defaultRecommendedTools(howToPay),
2156
+ wallet_compatibility: walletCompatibility ?? DEFAULT_WALLET_COMPATIBILITY,
2157
+ timeout_seconds: timeoutSeconds ?? 300,
2158
+ warnings: warnings ?? [...defaultWarnings(howToPay), ...extraWarnings ?? []],
2159
+ ...recommended ? { recommended } : {},
2160
+ ...compatibleClientsOut ? { compatible_clients: compatibleClientsOut } : {},
2161
+ ...extra ?? {}
2162
+ };
2163
+ }
2164
+
2165
+ // src/challenge/agent_memory.ts
2166
+ function firstEncounterAgentMemory({
2167
+ firstEncounter
2168
+ }) {
2169
+ if (!firstEncounter) return void 0;
2170
+ return buildAgentMemoryHint();
2171
+ }
2172
+
2173
+ // src/challenge/body.ts
2174
+ function build402Body({
2175
+ acceptedMethods,
2176
+ agentInstructions,
2177
+ identityMetadata,
2178
+ agentMemory,
2179
+ pricing,
2180
+ amountUsd,
2181
+ currency,
2182
+ orderId,
2183
+ product,
2184
+ retryBody,
2185
+ recommended,
2186
+ x402,
2187
+ extra
2188
+ }) {
2189
+ const body = {
2190
+ payment_required: true,
2191
+ accepted_methods: acceptedMethods
2192
+ };
2193
+ if (x402) {
2194
+ body.x402Version = x402.version ?? 2;
2195
+ body.accepts = x402.accepts;
2196
+ if (x402.extensions !== void 0 && Object.keys(x402.extensions).length > 0) {
2197
+ body.extensions = x402.extensions;
495
2198
  }
496
2199
  }
497
- const paymentHandlers = {};
498
- for (const [name, bindings] of Object.entries(input.payment_handlers ?? {})) {
499
- paymentHandlers[name] = bindings.map((binding) => {
500
- if (Array.isArray(binding.available_instruments) && binding.available_instruments.length === 0) {
501
- const { available_instruments: _drop, ...rest } = binding;
502
- return rest;
503
- }
504
- return binding;
505
- });
2200
+ if (amountUsd !== void 0) body.amount_usd = amountUsd;
2201
+ if (currency) body.currency = currency;
2202
+ if (pricing) body.pricing = pricing;
2203
+ if (orderId !== void 0) body.order_id = orderId;
2204
+ if (product) body.product = product;
2205
+ if (recommended) body.recommended = recommended;
2206
+ if (retryBody !== void 0) body.retry_body = retryBody;
2207
+ if (identityMetadata) {
2208
+ Object.assign(body, identityMetadata);
506
2209
  }
507
- const capabilities = {};
508
- for (const [name, bindings] of Object.entries(input.capabilities ?? {})) {
509
- capabilities[name] = [...bindings];
2210
+ if (agentInstructions) body.agent_instructions = agentInstructions;
2211
+ if (agentMemory !== void 0) body.agent_memory = agentMemory;
2212
+ if (extra) Object.assign(body, extra);
2213
+ return body;
2214
+ }
2215
+
2216
+ // src/challenge/how_to_pay.ts
2217
+ var TEMPO_SETUP = [
2218
+ "curl -fsSL https://tempo.xyz/install | bash",
2219
+ "tempo wallet login",
2220
+ "tempo wallet whoami",
2221
+ "tempo wallet fund # if balance is zero"
2222
+ ];
2223
+ var PAY_SETUP_BASE = [
2224
+ "npm install -g @agent-score/pay # or: brew install agentscore/tap/agentscore-pay",
2225
+ "agentscore-pay wallet create --chain base",
2226
+ "agentscore-pay balance --chain base # fund the printed address with USDC on Base"
2227
+ ];
2228
+ var PAY_SETUP_SOLANA = [
2229
+ "npm install -g @agent-score/pay # or: brew install agentscore/tap/agentscore-pay",
2230
+ "agentscore-pay wallet create --chain solana",
2231
+ "agentscore-pay balance --chain solana # fund the printed address with USDC on Solana"
2232
+ ];
2233
+ function buildHowToPay({
2234
+ url,
2235
+ retryBodyJson,
2236
+ totalUsd,
2237
+ rails: rails2,
2238
+ opTokenPlaceholder,
2239
+ maxSpend
2240
+ }) {
2241
+ const totalNum = typeof totalUsd === "string" ? Number(totalUsd) : totalUsd;
2242
+ const maxSpendStr = String(maxSpend ?? (Math.ceil(totalNum) + 1).toFixed(2));
2243
+ const opToken = opTokenPlaceholder ?? "<your_opc_token>";
2244
+ const block = {};
2245
+ if (rails2.tempo) {
2246
+ const networkName = rails2.tempo.testnet ? "tempo-testnet" : rails2.tempo.network ?? RAIL_SPEC_DEFAULTS.tempo.network;
2247
+ const chainId = rails2.tempo.chainId ?? RAIL_SPEC_DEFAULTS.tempo.chainId;
2248
+ const recommend = rails2.tempo.recommend ?? RAIL_SPEC_DEFAULTS.tempo.recommend;
2249
+ const tempoCommand = `tempo request -X POST -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' --json '${retryBodyJson}' --max-spend ${maxSpendStr} ${url}`;
2250
+ const payCommand = `agentscore-pay pay POST ${url} --chain tempo -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`;
2251
+ block.tempo = {
2252
+ setup: TEMPO_SETUP,
2253
+ prerequisite: `Run \`tempo wallet whoami\` and confirm USDC.e balance on ${networkName} (chain ${chainId}) is at least $${maxSpendStr}. If the tempo CLI is not installed, run the setup commands above first.`,
2254
+ command: recommend === "agentscore-pay" ? payCommand : tempoCommand,
2255
+ ...recommend === "both" ? { alternative_command: payCommand } : recommend === "agentscore-pay" ? { alternative_command: tempoCommand } : {},
2256
+ what_it_does: `Pays via Tempo USDC on ${networkName}.`
2257
+ };
510
2258
  }
511
- if (input.agentscore_gate) {
512
- const gateConfig = { ...input.agentscore_gate };
513
- const agentscoreBinding = {
514
- version: AGENTSCORE_CAPABILITY_VERSION,
515
- spec: input.agentscore_spec_url ?? AGENTSCORE_DEFAULT_SPEC_URL,
516
- schema: input.agentscore_schema_url ?? AGENTSCORE_DEFAULT_SCHEMA_URL,
517
- extends: AGENTSCORE_EXTENDS
2259
+ if (rails2.x402_base) {
2260
+ const network = rails2.x402_base.network ?? RAIL_SPEC_DEFAULTS.x402Base.network;
2261
+ block.x402_base = {
2262
+ setup: PAY_SETUP_BASE,
2263
+ prerequisite: `Run \`agentscore-pay balance --chain base\` and confirm USDC balance on Base (${network}) is at least $${maxSpendStr}. If the CLI is not installed, run the setup commands above first.`,
2264
+ command: `agentscore-pay pay POST ${url} --chain base -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`,
2265
+ what_it_does: "Pays via USDC on Base."
518
2266
  };
519
- if (Object.keys(gateConfig).length > 0) agentscoreBinding.config = gateConfig;
520
- const existing = capabilities[AGENTSCORE_CAPABILITY_NAME];
521
- if (existing) existing.push(agentscoreBinding);
522
- else capabilities[AGENTSCORE_CAPABILITY_NAME] = [agentscoreBinding];
523
2267
  }
524
- const ucp = {
525
- version: input.version ?? DEFAULT_VERSION,
526
- services: input.services ?? {},
527
- capabilities,
528
- payment_handlers: paymentHandlers
529
- };
530
- if (input.name !== void 0) ucp.name = input.name;
531
- if (input.supported_versions !== void 0) ucp.supported_versions = input.supported_versions;
532
- if (input.ucp_extras) {
533
- for (const k of Object.keys(input.ucp_extras)) {
534
- if (RESERVED_UCP_FIELDS.has(k)) {
535
- throw new Error(`buildUCPProfile: ucp_extras key "${k}" collides with a reserved \`ucp\` field; rejected.`);
536
- }
537
- }
538
- Object.assign(ucp, input.ucp_extras);
2268
+ if (rails2.solana_mpp) {
2269
+ const network = rails2.solana_mpp.network ?? RAIL_SPEC_DEFAULTS.solanaMpp.network;
2270
+ block.solana_mpp = {
2271
+ setup: PAY_SETUP_SOLANA,
2272
+ prerequisite: `Run \`agentscore-pay balance --chain solana\` and confirm USDC balance on Solana (${network}) is at least $${maxSpendStr}. If the CLI is not installed, run the setup commands above first.`,
2273
+ command: `agentscore-pay pay POST ${url} --chain solana -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`,
2274
+ what_it_does: "Pays via USDC on Solana."
2275
+ };
539
2276
  }
540
- const profile = {
541
- ucp,
542
- signing_keys: input.signing_keys
543
- };
544
- if (input.extras) {
545
- for (const k of Object.keys(input.extras)) {
546
- if (RESERVED_TOP_LEVEL.has(k)) {
547
- throw new Error(`buildUCPProfile: extras key "${k}" collides with a reserved profile field; rejected.`);
548
- }
2277
+ if (rails2.stripe) {
2278
+ const stripeCfg = rails2.stripe;
2279
+ const amountCents = Math.round(totalNum * 100);
2280
+ const linkCliBlocked = amountCents > 5e4;
2281
+ const productName = stripeCfg.productName ?? "this purchase";
2282
+ const sptContext = `Purchasing "${productName}" via the agent commerce API. The user authorized this purchase through their AI agent for $${totalNum}; charge to be settled via shared payment token over the Machine Payments Protocol.`;
2283
+ const stripe = {
2284
+ prerequisite: "Either your own Stripe account with Shared Payment Token acceptance, OR a Stripe Link wallet (any user with link.com).",
2285
+ instructions: "Mint a SharedPaymentToken scoped to the profile_id advertised in accepted_methods, then submit via Authorization: Payment MPP header with method=stripe/charge."
2286
+ };
2287
+ if (stripeCfg.profileId && !linkCliBlocked) {
2288
+ stripe.setup_link_cli = [
2289
+ "npm install -g @stripe/link-cli # or use npx -y @stripe/link-cli for one-shot",
2290
+ "link-cli auth login # one-time, opens your Link wallet",
2291
+ "link-cli payment-methods list --output-json # copy a csmrpd_... id"
2292
+ ];
2293
+ stripe.command_link_cli = [
2294
+ `SPEND_ID=$(link-cli spend-request create --payment-method-id <csmrpd_id_from_payment_methods_list> --credential-type shared_payment_token --network-id ${stripeCfg.profileId} --amount ${amountCents} --context "${sptContext}" --request-approval --output-json | jq -r .id)`,
2295
+ `link-cli mpp pay ${url} --spend-request-id $SPEND_ID --method POST --data '${retryBodyJson}' --header 'X-Operator-Token: ${opToken}' --output-json`
2296
+ ];
2297
+ stripe.what_it_does_link_cli = "Mints a one-time-use SharedPaymentToken scoped to this purchase (user approves in Link wallet), then submits it as the payment credential.";
2298
+ } else if (linkCliBlocked) {
2299
+ stripe.note = `link-cli SPT path not available for this purchase \u2014 Stripe link-cli caps spend requests at $500.00 ($50000 cents); your total is $${totalNum}. Use your own Stripe account with the SharedPaymentToken API instead.`;
549
2300
  }
550
- Object.assign(profile, input.extras);
2301
+ block.stripe = stripe;
551
2302
  }
552
- return profile;
553
- }
554
- var AGENTSCORE_UCP_CAPABILITY = AGENTSCORE_CAPABILITY_NAME;
555
- var HANDLER_VERSION = "2026-04-08";
556
- var SPEC_BASE = "https://agentscore.sh/specification/payment-handlers";
557
- var SCHEMA_BASE = "https://agentscore.sh/schemas/payment-handlers";
558
- function mppPaymentHandler(input) {
559
- return {
560
- "sh.agentscore.payment.mpp": [{
561
- id: "mpp",
562
- version: HANDLER_VERSION,
563
- spec: `${SPEC_BASE}/mpp`,
564
- schema: `${SCHEMA_BASE}/mpp.json`,
565
- config: { networks: input.networks }
566
- }]
567
- };
2303
+ return block;
568
2304
  }
569
- function x402PaymentHandler(input) {
570
- return {
571
- "sh.agentscore.payment.x402": [{
572
- id: "x402",
573
- version: HANDLER_VERSION,
574
- spec: `${SPEC_BASE}/x402`,
575
- schema: `${SCHEMA_BASE}/x402.json`,
576
- config: { networks: input.networks }
577
- }]
578
- };
2305
+
2306
+ // src/challenge/identity.ts
2307
+ function buildIdentityMetadata({
2308
+ mode,
2309
+ wallet,
2310
+ signerMatchResult,
2311
+ linkedWallets,
2312
+ signerConstraint
2313
+ }) {
2314
+ const block = { identity_mode: mode };
2315
+ if (mode !== "wallet") return block;
2316
+ if (wallet) {
2317
+ block.required_signer = signerMatchResult?.expectedSigner ?? wallet;
2318
+ }
2319
+ if (linkedWallets && linkedWallets.length > 0) {
2320
+ block.linked_wallets = linkedWallets;
2321
+ }
2322
+ block.signer_constraint = signerConstraint ?? "Payment must be signed with the claimed wallet OR any same-operator linked wallet listed in linked_wallets.";
2323
+ return block;
579
2324
  }
580
- function stripeSptPaymentHandler(input) {
581
- return {
582
- "sh.agentscore.payment.stripe_spt": [{
583
- id: "stripe-spt",
584
- version: HANDLER_VERSION,
585
- spec: `${SPEC_BASE}/stripe_spt`,
586
- schema: `${SCHEMA_BASE}/stripe_spt.json`,
587
- config: { rail: "stripe-spt", profile_id: input.profile_id }
588
- }]
2325
+
2326
+ // src/challenge/pricing.ts
2327
+ function buildPricingBlock({
2328
+ subtotalCents,
2329
+ taxCents = 0,
2330
+ shippingCents,
2331
+ discountCents,
2332
+ totalCents,
2333
+ taxRate,
2334
+ taxState,
2335
+ currency
2336
+ }) {
2337
+ const shipping = shippingCents ?? 0;
2338
+ const discount = discountCents ?? 0;
2339
+ const total = totalCents ?? Math.max(0, subtotalCents + taxCents + shipping - discount);
2340
+ const block = {
2341
+ subtotal: formatCents(subtotalCents),
2342
+ tax: formatCents(taxCents),
2343
+ total: formatCents(total)
589
2344
  };
2345
+ if (shippingCents !== void 0) block.shipping = formatCents(shipping);
2346
+ if (discountCents !== void 0) block.discount = formatCents(discount);
2347
+ if (taxRate !== void 0) block.tax_rate = taxRate;
2348
+ if (taxState !== void 0) block.tax_state = taxState;
2349
+ if (currency !== void 0) block.currency = currency;
2350
+ return block;
2351
+ }
2352
+ function formatCents(cents) {
2353
+ return (cents / 100).toFixed(2);
590
2354
  }
591
2355
 
592
- // src/identity/ucp-jwks.ts
593
- var JOSE_INSTALL_HINT = "Install the optional peer dependency: `npm install jose@^6` (or `bun add jose`). Tested against jose v6.x.";
594
- var ALLOWED_ALGS = ["EdDSA", "ES256"];
595
- var PROFILE_TYP = "agentscore-profile+jws";
596
- var UCPVerificationError = class extends Error {
597
- constructor(code, message) {
598
- super(message);
599
- this.code = code;
600
- this.name = "UCPVerificationError";
2356
+ // src/challenge/respond_402.ts
2357
+ init_wwwauthenticate();
2358
+ function respond402({
2359
+ mppxChallengeHeaders,
2360
+ body,
2361
+ x402
2362
+ }) {
2363
+ const headers = {};
2364
+ for (const [k, v] of Object.entries(mppxChallengeHeaders)) {
2365
+ headers[k.toLowerCase()] = v;
601
2366
  }
602
- code;
603
- };
604
- async function loadJose() {
605
- try {
606
- return await import("jose");
607
- } catch (err) {
608
- throw new Error(
609
- `UCP signing requires the \`jose\` library, which is an optional peer dependency. ${JOSE_INSTALL_HINT}
610
- Original error: ${err instanceof Error ? err.message : String(err)}`
611
- );
2367
+ headers["content-type"] = "application/json";
2368
+ if (x402) {
2369
+ headers["payment-required"] = paymentRequiredHeader(x402);
612
2370
  }
2371
+ return { body, headers, status: 402 };
613
2372
  }
614
- function canonicalizeProfile(profile) {
615
- const stripped = { ...profile };
616
- delete stripped.signature;
617
- return stableStringify(stripped);
2373
+
2374
+ // src/challenge/validation_error.ts
2375
+ function buildValidationError({
2376
+ code,
2377
+ message,
2378
+ requiredFields,
2379
+ exampleBody,
2380
+ nextSteps,
2381
+ extra
2382
+ }) {
2383
+ const body = {
2384
+ error: { code, message }
2385
+ };
2386
+ if (requiredFields) body.required_fields = requiredFields;
2387
+ if (exampleBody !== void 0) body.example_body = exampleBody;
2388
+ if (nextSteps) body.next_steps = nextSteps;
2389
+ if (extra) Object.assign(body, extra);
2390
+ return body;
618
2391
  }
619
- function stableStringify(value) {
620
- if (value === void 0) {
2392
+
2393
+ // src/stripe-multichain/mppx_stripe.ts
2394
+ async function createMppxStripe({
2395
+ profileId,
2396
+ secretKey,
2397
+ paymentMethodTypes
2398
+ }) {
2399
+ const moduleName = "mppx/server";
2400
+ const mppx = await import(moduleName).catch(() => null);
2401
+ if (!mppx?.stripe?.charge) {
621
2402
  throw new Error(
622
- "stableStringify: undefined values are not allowed in canonicalized JSON. Object fields with no value must be omitted."
2403
+ "mppx not installed \u2014 install with `npm install mppx` to use createMppxStripe."
623
2404
  );
624
2405
  }
625
- if (typeof value === "function" || typeof value === "symbol") {
626
- throw new Error(`stableStringify: ${typeof value} values are not allowed in canonicalized JSON.`);
2406
+ return mppx.stripe.charge({
2407
+ networkId: profileId,
2408
+ paymentMethodTypes: paymentMethodTypes ?? ["card", "link"],
2409
+ secretKey
2410
+ });
2411
+ }
2412
+
2413
+ // src/payment/mppx_server.ts
2414
+ init_networks();
2415
+ init_usdc();
2416
+ function isStripeRailSpec(s) {
2417
+ return !("recipient" in s);
2418
+ }
2419
+ function isTempoSessionRailSpec2(s) {
2420
+ return "escrowContract" in s && "store" in s;
2421
+ }
2422
+ function isSolanaMppRailSpec(s) {
2423
+ if (!("recipient" in s)) return false;
2424
+ if ("escrowContract" in s) return false;
2425
+ if ("rpcUrl" in s || "tokenProgram" in s) return true;
2426
+ return s.network?.startsWith("solana:") ?? false;
2427
+ }
2428
+ function solanaNetworkFromCAIP2(caip2) {
2429
+ if (caip2 === networks.solana.devnet.caip2) return "devnet";
2430
+ return "mainnet-beta";
2431
+ }
2432
+ function solanaDefaultRpcUrl(network) {
2433
+ if (network === "mainnet-beta") return "https://api.mainnet-beta.solana.com";
2434
+ if (network === "devnet") return "https://api.devnet.solana.com";
2435
+ return "http://localhost:8899";
2436
+ }
2437
+ async function createMppxServer({
2438
+ rails: rails2,
2439
+ methods: extraMethods,
2440
+ secretKey
2441
+ }) {
2442
+ const mppx = await dynamicImport("mppx/server");
2443
+ if (!mppx?.Mppx?.create) {
2444
+ throw new Error("mppx not installed \u2014 `npm install mppx` to use createMppxServer.");
627
2445
  }
628
- if (typeof value === "bigint") {
629
- throw new Error("stableStringify: BigInt values are not allowed; use a decimal string.");
2446
+ const methods = [...extraMethods ?? []];
2447
+ for (const [name, spec] of Object.entries(rails2 ?? {})) {
2448
+ if (isStripeRailSpec(spec)) {
2449
+ methods.push(await registerStripe(spec));
2450
+ continue;
2451
+ }
2452
+ if (isTempoSessionRailSpec2(spec)) {
2453
+ methods.push(await registerTempoSession(mppx, spec));
2454
+ continue;
2455
+ }
2456
+ if (isSolanaMppRailSpec(spec)) {
2457
+ methods.push(await registerSolana(spec));
2458
+ continue;
2459
+ }
2460
+ methods.push(registerTempo(mppx, spec, name));
630
2461
  }
631
- if (value instanceof Date) {
2462
+ return mppx.Mppx.create({ methods, secretKey });
2463
+ }
2464
+ function registerTempo(mppx, spec, _name) {
2465
+ if (!mppx.tempo?.charge) {
2466
+ throw new Error("mppx.tempo.charge not available \u2014 check installed mppx version.");
2467
+ }
2468
+ const defaultCurrency = spec.testnet ? USDC.tempo.testnet.address : USDC.tempo.mainnet.address;
2469
+ if (typeof spec.recipient !== "string") {
2470
+ throw new TypeError(
2471
+ "createMppxServer: TempoRailSpec requires a string recipient (per-order factories not supported here)."
2472
+ );
2473
+ }
2474
+ return mppx.tempo.charge({
2475
+ currency: spec.token ?? defaultCurrency,
2476
+ recipient: spec.recipient,
2477
+ testnet: spec.testnet ?? false
2478
+ });
2479
+ }
2480
+ async function registerTempoSession(mppx, spec) {
2481
+ if (!mppx.tempo?.session) {
632
2482
  throw new Error(
633
- "stableStringify: Date instances are not allowed; serialize to an ISO string before passing."
2483
+ "mppx.tempo.session not available \u2014 your mppx version may not support sessions yet. Upgrade with `npm install mppx@latest`."
634
2484
  );
635
2485
  }
636
- if (value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
2486
+ const defaultCurrency = spec.testnet ? USDC.tempo.testnet.address : USDC.tempo.mainnet.address;
2487
+ return mppx.tempo.session({
2488
+ currency: spec.currency ?? defaultCurrency,
2489
+ recipient: await resolveRecipient(spec.recipient),
2490
+ escrowContract: spec.escrowContract,
2491
+ store: spec.store,
2492
+ testnet: spec.testnet ?? false,
2493
+ ...spec.chains ? { chains: spec.chains } : {}
2494
+ });
2495
+ }
2496
+ async function registerSolana(spec) {
2497
+ const solanaMpp = await dynamicImport("@solana/mpp/server");
2498
+ if (!solanaMpp?.charge) {
637
2499
  throw new Error(
638
- `stableStringify: ${value.constructor.name} values are not allowed; convert to a plain object/array first.`
2500
+ "@solana/mpp not installed \u2014 `npm install @solana/mpp @solana/kit` to use the solana rail."
639
2501
  );
640
2502
  }
641
- if (ArrayBuffer.isView(value)) {
642
- throw new Error("stableStringify: typed arrays are not allowed; convert to a plain array first.");
2503
+ const network = solanaNetworkFromCAIP2(spec.network);
2504
+ const defaultMint = network === "mainnet-beta" ? USDC.solana.mainnet.mint : USDC.solana.devnet.mint;
2505
+ const defaultDecimals = network === "mainnet-beta" ? USDC.solana.mainnet.decimals : USDC.solana.devnet.decimals;
2506
+ if (typeof spec.recipient !== "string") {
2507
+ throw new TypeError(
2508
+ "createMppxServer: SolanaMppRailSpec requires a string recipient (per-order factories not supported here)."
2509
+ );
643
2510
  }
644
- if (typeof value === "number") {
645
- if (!Number.isFinite(value)) {
646
- throw new Error(
647
- `UCP profile canonicalization rejects non-finite Number ${value}. Use a decimal string for any value that may be NaN/Infinity.`
648
- );
649
- }
650
- if (!Number.isInteger(value)) {
651
- throw new Error(
652
- `UCP profile canonicalization rejects non-integer Number ${value}. Use a decimal string (e.g. "9.99") for monetary or fractional fields to preserve cross-language byte-parity.`
653
- );
654
- }
655
- if (!Number.isSafeInteger(value)) {
656
- throw new Error(
657
- `stableStringify: integer ${value} exceeds Number.MAX_SAFE_INTEGER. For values >2^53, use a decimal string to preserve cross-language byte parity.`
658
- );
2511
+ const baseMethod = solanaMpp.charge({
2512
+ recipient: spec.recipient,
2513
+ currency: spec.token ?? defaultMint,
2514
+ decimals: spec.decimals ?? defaultDecimals,
2515
+ network,
2516
+ ...spec.rpcUrl ? { rpcUrl: spec.rpcUrl } : {},
2517
+ ...spec.signer ? { signer: spec.signer } : {},
2518
+ ...spec.tokenProgram ? { tokenProgram: spec.tokenProgram } : {}
2519
+ });
2520
+ return wrapSolanaChargeWithFinalizedBlockhash(baseMethod, spec.rpcUrl ?? solanaDefaultRpcUrl(network));
2521
+ }
2522
+ async function registerStripe(spec) {
2523
+ if (!spec.profileId || !spec.secretKey) {
2524
+ throw new Error(
2525
+ "createMppxServer: StripeRailSpec requires both profileId and secretKey."
2526
+ );
2527
+ }
2528
+ return createMppxStripe({
2529
+ profileId: spec.profileId,
2530
+ secretKey: spec.secretKey,
2531
+ paymentMethodTypes: spec.paymentMethodTypes
2532
+ });
2533
+ }
2534
+ async function dynamicImport(moduleName) {
2535
+ try {
2536
+ return await import(moduleName);
2537
+ } catch {
2538
+ return null;
2539
+ }
2540
+ }
2541
+ function wrapSolanaChargeWithFinalizedBlockhash(baseMethod, rpcUrl) {
2542
+ return {
2543
+ ...baseMethod,
2544
+ async request(args) {
2545
+ const orig = await baseMethod.request(args);
2546
+ if (args.credential || !orig || typeof orig !== "object") return orig;
2547
+ try {
2548
+ const res = await fetch(rpcUrl, {
2549
+ method: "POST",
2550
+ headers: { "Content-Type": "application/json" },
2551
+ body: JSON.stringify({
2552
+ id: 1,
2553
+ jsonrpc: "2.0",
2554
+ method: "getLatestBlockhash",
2555
+ params: [{ commitment: "finalized" }]
2556
+ })
2557
+ });
2558
+ const data = await res.json();
2559
+ const finalized = data?.result?.value?.blockhash;
2560
+ if (finalized) {
2561
+ return {
2562
+ ...orig,
2563
+ methodDetails: { ...orig.methodDetails ?? {}, recentBlockhash: finalized }
2564
+ };
2565
+ }
2566
+ } catch {
2567
+ }
2568
+ return orig;
659
2569
  }
2570
+ };
2571
+ }
2572
+
2573
+ // src/payment/x402_server.ts
2574
+ init_networks();
2575
+
2576
+ // src/payment/x402.ts
2577
+ function registerX402SchemesV1V2(server, network, scheme) {
2578
+ server.register(network, scheme);
2579
+ if (typeof server.registerV1 === "function") {
2580
+ server.registerV1(network, scheme);
660
2581
  }
661
- if (typeof value === "string") {
662
- if (value.includes("\u2028") || value.includes("\u2029")) {
2582
+ }
2583
+
2584
+ // src/payment/x402_server.ts
2585
+ async function createX402Server(opts = {}) {
2586
+ const x402Core = await dynamicImport2("@x402/core/server") ?? null;
2587
+ if (!x402Core) {
2588
+ throw new Error(
2589
+ "@x402/core not installed \u2014 `npm install @x402/core` to use createX402Server."
2590
+ );
2591
+ }
2592
+ let facilitator;
2593
+ const facilitatorChoice = opts.facilitator ?? (process.env.CDP_API_KEY_ID && process.env.CDP_API_KEY_SECRET ? "coinbase" : "http");
2594
+ if (facilitatorChoice === "coinbase") {
2595
+ const cb = await dynamicImport2("@coinbase/x402");
2596
+ if (!cb?.facilitator) {
663
2597
  throw new Error(
664
- "stableStringify: strings containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; cross-language byte parity requires neither be present (Node JSON.stringify on older V8 escapes them; Python json.dumps with ensure_ascii=False does not)."
2598
+ '@coinbase/x402 not installed \u2014 `npm install @coinbase/x402` for facilitator: "coinbase".'
665
2599
  );
666
2600
  }
667
- return JSON.stringify(value);
2601
+ facilitator = new x402Core.HTTPFacilitatorClient(cb.facilitator);
2602
+ } else if (facilitatorChoice === "http") {
2603
+ facilitator = new x402Core.HTTPFacilitatorClient();
2604
+ } else {
2605
+ facilitator = facilitatorChoice;
668
2606
  }
669
- if (value === null || typeof value !== "object") return JSON.stringify(value);
670
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
671
- const obj = value;
672
- const keys = Object.keys(obj).sort((a, b) => {
673
- const aPoints = [...a].map((c) => c.codePointAt(0));
674
- const bPoints = [...b].map((c) => c.codePointAt(0));
675
- const len = Math.min(aPoints.length, bPoints.length);
676
- for (let i = 0; i < len; i += 1) {
677
- if (aPoints[i] !== bPoints[i]) return aPoints[i] - bPoints[i];
2607
+ const server = new x402Core.x402ResourceServer(facilitator);
2608
+ let evmExactModule = null;
2609
+ let evmUptoModule = null;
2610
+ for (const rail of opts.rails ?? []) {
2611
+ const isUpto = rail.endsWith("-upto");
2612
+ if (rail.startsWith("x402-base")) {
2613
+ const baseRail = isUpto ? rail.slice(0, -5) : rail;
2614
+ const network = baseRail === "x402-base-mainnet" ? networks.base.mainnet.caip2 : networks.base.sepolia.caip2;
2615
+ if (isUpto) {
2616
+ evmUptoModule ??= await dynamicImport2("@x402/evm/upto/server");
2617
+ if (!evmUptoModule?.UptoEvmScheme) {
2618
+ throw new Error("@x402/evm not installed \u2014 `npm install @x402/evm` for x402 base upto rails.");
2619
+ }
2620
+ registerX402SchemesV1V2(server, network, new evmUptoModule.UptoEvmScheme());
2621
+ } else {
2622
+ evmExactModule ??= await dynamicImport2("@x402/evm/exact/server");
2623
+ if (!evmExactModule?.ExactEvmScheme) {
2624
+ throw new Error("@x402/evm not installed \u2014 `npm install @x402/evm` for x402 base rails.");
2625
+ }
2626
+ registerX402SchemesV1V2(server, network, new evmExactModule.ExactEvmScheme());
2627
+ }
678
2628
  }
679
- return aPoints.length - bPoints.length;
680
- });
681
- for (const k of keys) {
682
- if (k.includes("\u2028") || k.includes("\u2029")) {
2629
+ }
2630
+ for (const { network, scheme } of opts.schemes ?? []) {
2631
+ registerX402SchemesV1V2(server, network, scheme);
2632
+ }
2633
+ if (opts.bazaar) {
2634
+ const bazaar = await dynamicImport2("@x402/extensions/bazaar");
2635
+ if (!bazaar?.bazaarResourceServerExtension) {
683
2636
  throw new Error(
684
- "stableStringify: object keys containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; cross-language byte parity (Node JSON.stringify on older V8 escapes them; Python json.dumps with ensure_ascii=False does not)."
2637
+ "@x402/extensions not installed \u2014 `npm install @x402/extensions` for bazaar discovery."
685
2638
  );
686
2639
  }
2640
+ server.registerExtension(bazaar.bazaarResourceServerExtension);
687
2641
  }
688
- const pairs = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
689
- return `{${pairs.join(",")}}`;
2642
+ if (opts.initialize !== false) {
2643
+ await server.initialize();
2644
+ }
2645
+ return server;
690
2646
  }
691
- async function generateUCPSigningKey(opts) {
692
- const jose = await loadJose();
693
- const alg = opts.alg ?? "EdDSA";
694
- const { privateKey, publicKey } = await jose.generateKeyPair(alg, { extractable: true });
695
- const exportedJwk = await jose.exportJWK(publicKey);
696
- const publicJWK = {
697
- kid: opts.kid,
698
- alg,
699
- use: "sig",
700
- ...exportedJwk
2647
+ async function buildX402AcceptsFor402(server, opts) {
2648
+ const requirements = await server.buildPaymentRequirements(
2649
+ {
2650
+ scheme: opts.scheme ?? "exact",
2651
+ network: opts.network,
2652
+ price: opts.price,
2653
+ payTo: opts.payTo,
2654
+ maxTimeoutSeconds: opts.maxTimeoutSeconds ?? 300
2655
+ },
2656
+ opts.extensions
2657
+ );
2658
+ return Array.isArray(requirements) ? requirements : [];
2659
+ }
2660
+ async function dynamicImport2(moduleName) {
2661
+ try {
2662
+ return await import(moduleName);
2663
+ } catch {
2664
+ return null;
2665
+ }
2666
+ }
2667
+
2668
+ // src/payment/lazy.ts
2669
+ function x402RailName(spec) {
2670
+ const network = spec.network ?? "eip155:8453";
2671
+ if (network === "eip155:8453") return "x402-base-mainnet";
2672
+ if (network === "eip155:84532") return "x402-base-sepolia";
2673
+ throw new Error(
2674
+ `lazyX402Server: unsupported X402BaseRailSpec.network=${JSON.stringify(network)}`
2675
+ );
2676
+ }
2677
+ function lazyX402Server(opts) {
2678
+ const { spec, cdpApiKeyId, cdpApiKeySecret } = opts;
2679
+ const railName = x402RailName(spec);
2680
+ const useCdp = Boolean(cdpApiKeyId && cdpApiKeySecret);
2681
+ const facilitator = useCdp ? "coinbase" : "http";
2682
+ let cached;
2683
+ let pending;
2684
+ return async () => {
2685
+ if (cached !== void 0) return cached;
2686
+ if (pending !== void 0) return pending;
2687
+ pending = (async () => {
2688
+ const server = await createX402Server({ facilitator, rails: [railName] });
2689
+ cached = server;
2690
+ pending = void 0;
2691
+ return server;
2692
+ })();
2693
+ return pending;
701
2694
  };
702
- return { privateKey, publicJWK };
703
2695
  }
704
- async function signUCPProfile(profile, opts) {
705
- const jose = await loadJose();
706
- const alg = opts.alg ?? "EdDSA";
707
- if (!ALLOWED_ALGS.includes(alg)) {
708
- throw new Error(
709
- `signUCPProfile: alg ${JSON.stringify(opts.alg)} is not in the supported set [${ALLOWED_ALGS.join(", ")}].`
2696
+ function lazyMppxServer(opts) {
2697
+ const { rails: rails2, secretKey } = opts;
2698
+ let cached;
2699
+ let pending;
2700
+ return async () => {
2701
+ if (cached !== void 0) return cached;
2702
+ if (pending !== void 0) return pending;
2703
+ pending = (async () => {
2704
+ const server = await createMppxServer({ secretKey, rails: rails2 });
2705
+ cached = server;
2706
+ pending = void 0;
2707
+ return server;
2708
+ })();
2709
+ return pending;
2710
+ };
2711
+ }
2712
+
2713
+ // src/payment/x402_settle.ts
2714
+ function classifyX402SettleResult(result) {
2715
+ if (result.success) return null;
2716
+ switch (result.phase) {
2717
+ case "no_requirements":
2718
+ return {
2719
+ status: 500,
2720
+ code: "payment_internal_error",
2721
+ message: "Failed to build x402 payment requirements for this configuration",
2722
+ nextSteps: {
2723
+ action: "contact_support",
2724
+ user_message: "The merchant could not produce a payment challenge for this request. Try again later or contact support."
2725
+ }
2726
+ };
2727
+ case "verify_failed":
2728
+ return {
2729
+ status: 400,
2730
+ code: "payment_proof_invalid",
2731
+ message: "Payment credential failed verification; regenerate from a fresh 402 challenge",
2732
+ nextSteps: {
2733
+ action: "regenerate_payment_credential",
2734
+ user_message: "The payment credential was rejected at verify time. Discard it, fetch a fresh 402 challenge, and re-sign."
2735
+ }
2736
+ };
2737
+ case "facilitator_error":
2738
+ return {
2739
+ status: 503,
2740
+ code: "payment_provider_unavailable",
2741
+ message: "Payment provider could not process this network configuration",
2742
+ nextSteps: {
2743
+ action: "try_different_rail",
2744
+ user_message: "This rail is currently unavailable. Pick a different rail from the 402 challenge and retry."
2745
+ }
2746
+ };
2747
+ case "settle_failed":
2748
+ return {
2749
+ status: 503,
2750
+ code: "payment_provider_unavailable",
2751
+ message: "Payment credential verified but on-chain settlement failed",
2752
+ nextSteps: {
2753
+ action: "retry_or_swap_method",
2754
+ retry_after_seconds: 10,
2755
+ user_message: "Transient settlement error. Retry in a few seconds, or pick a different rail from the 402 challenge."
2756
+ }
2757
+ };
2758
+ }
2759
+ }
2760
+ async function processX402Settle({
2761
+ x402Server,
2762
+ payload,
2763
+ resourceConfig,
2764
+ resourceMeta,
2765
+ extension,
2766
+ transportContext
2767
+ }) {
2768
+ const server = x402Server;
2769
+ let builtRequirements;
2770
+ try {
2771
+ builtRequirements = await server.buildPaymentRequirements(resourceConfig);
2772
+ } catch (err) {
2773
+ console.warn("[x402_settle] build_requirements failed:", err instanceof Error ? err.message : err);
2774
+ return { success: false, phase: "facilitator_error", step: "build_requirements", error: err };
2775
+ }
2776
+ const matchedRequirement = builtRequirements[0];
2777
+ if (!matchedRequirement) {
2778
+ return { success: false, phase: "no_requirements", reason: "x402Server.buildPaymentRequirements returned empty" };
2779
+ }
2780
+ const resolvedTransportContext = transportContext ?? (() => {
2781
+ const path = new URL(resourceMeta.url).pathname;
2782
+ return { method: "POST", adapter: { getPath: () => path }, routePattern: path };
2783
+ })();
2784
+ let enrichedExt;
2785
+ try {
2786
+ enrichedExt = extension !== void 0 ? server.enrichExtensions(extension, resolvedTransportContext) : void 0;
2787
+ } catch (err) {
2788
+ console.warn("[x402_settle] enrich_extensions failed:", err instanceof Error ? err.message : err);
2789
+ return { success: false, phase: "facilitator_error", step: "enrich_extensions", error: err };
2790
+ }
2791
+ let verifyResult;
2792
+ try {
2793
+ verifyResult = await server.verifyPayment(
2794
+ payload,
2795
+ matchedRequirement,
2796
+ enrichedExt,
2797
+ resolvedTransportContext
710
2798
  );
2799
+ } catch (err) {
2800
+ console.warn("[x402_settle] verify_payment failed:", err instanceof Error ? err.message : err);
2801
+ return { success: false, phase: "facilitator_error", step: "verify_payment", error: err };
711
2802
  }
712
- if (typeof opts.kid !== "string" || !opts.kid) {
713
- throw new Error("signUCPProfile: opts.kid must be a non-empty string.");
2803
+ const verifyOk = verifyResult.isValid === true || verifyResult.success === true;
2804
+ if (!verifyOk) {
2805
+ return { success: false, phase: "verify_failed", verifyResult };
714
2806
  }
715
- const kids = (profile.signing_keys ?? []).map((k) => k.kid);
716
- if (!kids.includes(opts.kid)) {
717
- throw new Error(
718
- `signUCPProfile: kid ${JSON.stringify(opts.kid)} is not present in profile.signing_keys[] (declared kids: ${JSON.stringify(kids)}). Verifiers will not find the key.`
2807
+ try {
2808
+ const settleResult = await server.settlePayment(
2809
+ payload,
2810
+ matchedRequirement,
2811
+ enrichedExt,
2812
+ resolvedTransportContext
719
2813
  );
2814
+ const paymentResponseHeader = settleResult ? Buffer.from(JSON.stringify(settleResult)).toString("base64") : void 0;
2815
+ return {
2816
+ success: true,
2817
+ matchedRequirement,
2818
+ settleResult,
2819
+ paymentResponseHeader,
2820
+ verifyResult
2821
+ };
2822
+ } catch (err) {
2823
+ return { success: false, phase: "settle_failed", error: err, matchedRequirement };
720
2824
  }
721
- const canonicalBody = canonicalizeProfile(profile);
722
- const payloadBytes = new TextEncoder().encode(canonicalBody);
723
- const signature = await new jose.CompactSign(payloadBytes).setProtectedHeader({ alg, kid: opts.kid, typ: PROFILE_TYP }).sign(opts.signingKey);
724
- return { ...profile, signature };
725
2825
  }
726
- async function verifyUCPProfile(profile, jwks) {
727
- if (profile === null || typeof profile !== "object" || Array.isArray(profile)) {
728
- throw new UCPVerificationError(
729
- "no_signature",
730
- `UCP profile must be a JSON object; got ${profile === null ? "null" : Array.isArray(profile) ? "array" : typeof profile}.`
731
- );
2826
+
2827
+ // src/payment/x402_validation.ts
2828
+ init_networks();
2829
+ var X402_SUPPORTED_BASE_NETWORKS = /* @__PURE__ */ new Set([
2830
+ networks.base.mainnet.caip2,
2831
+ networks.base.sepolia.caip2
2832
+ ]);
2833
+ var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
2834
+ var REGENERATE_WARNING = "Use `agentscore-pay pay --chain base` (or `tempo request` for Tempo USDC) so the credential is signed and submitted via the protocol handshake. Do NOT use `tempo wallet transfer` \u2014 that sends USDC on-chain but does not complete the handshake.";
2835
+ function regenerateBody(message, userMessage) {
2836
+ return {
2837
+ error: { code: "payment_proof_invalid", message },
2838
+ next_steps: {
2839
+ action: "regenerate_payment_credential",
2840
+ user_message: userMessage,
2841
+ warning: REGENERATE_WARNING
2842
+ }
2843
+ };
2844
+ }
2845
+ async function verifyX402Request({
2846
+ request,
2847
+ isCachedAddress,
2848
+ acceptedNetwork
2849
+ }) {
2850
+ const headerValue = request.headers.get("payment-signature") ?? request.headers.get("x-payment");
2851
+ if (!headerValue) {
2852
+ return {
2853
+ ok: false,
2854
+ status: 400,
2855
+ body: regenerateBody(
2856
+ "X-Payment header missing",
2857
+ "No X-Payment header was sent. Generate the credential from the 402 challenge and resubmit on the same endpoint."
2858
+ )
2859
+ };
732
2860
  }
733
- const jose = await loadJose();
734
- if (!jwks || typeof jwks !== "object" || !Array.isArray(jwks.keys)) {
735
- throw new UCPVerificationError(
736
- "malformed_jwks",
737
- `UCP verifier expected JWKS shape { keys: [...] }; got ${jwks === null ? "null" : typeof jwks === "object" ? "object without keys[] array" : typeof jwks}.`
2861
+ let payload;
2862
+ try {
2863
+ payload = JSON.parse(Buffer.from(headerValue, "base64").toString());
2864
+ } catch {
2865
+ return {
2866
+ ok: false,
2867
+ status: 400,
2868
+ body: regenerateBody(
2869
+ "X-Payment header is not valid base64 JSON",
2870
+ "The payment credential could not be decoded. Reconstruct the credential from the 402 challenge and retry."
2871
+ )
2872
+ };
2873
+ }
2874
+ const signedNetwork = payload.accepted?.network;
2875
+ const signedPayTo = payload.accepted?.payTo;
2876
+ if (!signedNetwork || signedNetwork !== acceptedNetwork) {
2877
+ if (signedNetwork && signedNetwork.toLowerCase().startsWith("solana:")) {
2878
+ return {
2879
+ ok: false,
2880
+ status: 400,
2881
+ body: regenerateBody(
2882
+ `x402 on ${signedNetwork} is not accepted; Solana payments must use the \`solana/charge\` rail advertised in the 402 challenge. This server accepts x402 on ${acceptedNetwork} only.`,
2883
+ "Solana payments are not accepted over x402 at this merchant. Pick the `solana/charge` rail from the 402 challenge and re-sign."
2884
+ )
2885
+ };
2886
+ }
2887
+ return {
2888
+ ok: false,
2889
+ status: 400,
2890
+ body: regenerateBody(
2891
+ `Unsupported x402 network ${signedNetwork ?? "<missing>"}; this server accepts ${acceptedNetwork}.`,
2892
+ "The credential signed for an unsupported network. Pick the accepted network from the 402 challenge and re-sign."
2893
+ )
2894
+ };
2895
+ }
2896
+ const addressShapeOk = typeof signedPayTo === "string" && EVM_ADDRESS_RE.test(signedPayTo);
2897
+ if (!signedPayTo || !addressShapeOk) {
2898
+ return {
2899
+ ok: false,
2900
+ status: 400,
2901
+ body: regenerateBody(
2902
+ `Payment payload missing or malformed accepted.payTo address for network ${signedNetwork}`,
2903
+ "The credential payload is missing or malformed payTo for the signed network. Reconstruct the credential from the 402 challenge."
2904
+ )
2905
+ };
2906
+ }
2907
+ if (!await isCachedAddress(signedPayTo)) {
2908
+ return {
2909
+ ok: false,
2910
+ status: 400,
2911
+ body: regenerateBody(
2912
+ "payTo address not found in cache or expired. Request a fresh 402 challenge and retry.",
2913
+ "The deposit address is unknown or expired on this server. Request a fresh 402 challenge and re-sign against the new payTo."
2914
+ )
2915
+ };
2916
+ }
2917
+ return { ok: true, payload, signedNetwork, signedPayTo };
2918
+ }
2919
+
2920
+ // src/payment/zero-settle.ts
2921
+ var EVM_RE = /^0x[0-9a-fA-F]{40}$/;
2922
+ var SOLANA_BASE58_RE2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
2923
+ var NULL_RESULT = {
2924
+ signerAddress: null,
2925
+ signerNetwork: null,
2926
+ txHash: null
2927
+ };
2928
+ function zeroAmountCarveOut({
2929
+ rail,
2930
+ payload,
2931
+ authorizationHeader
2932
+ }) {
2933
+ if (rail === "x402-base") {
2934
+ return x402SignerFromPayload(payload);
2935
+ }
2936
+ if (rail === "tempo" || rail === "solana") {
2937
+ return mppSignerFromAuth(authorizationHeader);
2938
+ }
2939
+ return NULL_RESULT;
2940
+ }
2941
+ function x402SignerFromPayload(payload) {
2942
+ if (!payload || typeof payload !== "object") return NULL_RESULT;
2943
+ const inner = payload.payload;
2944
+ if (!inner || typeof inner !== "object") return NULL_RESULT;
2945
+ const authorization = inner.authorization;
2946
+ if (!authorization || typeof authorization !== "object") return NULL_RESULT;
2947
+ const fromAddr = authorization.from;
2948
+ if (typeof fromAddr !== "string" || !EVM_RE.test(fromAddr)) return NULL_RESULT;
2949
+ return {
2950
+ signerAddress: fromAddr.toLowerCase(),
2951
+ signerNetwork: "evm",
2952
+ txHash: null
2953
+ };
2954
+ }
2955
+ function mppSignerFromAuth(authorizationHeader) {
2956
+ if (typeof authorizationHeader !== "string") return NULL_RESULT;
2957
+ if (!authorizationHeader.toLowerCase().startsWith("payment ")) return NULL_RESULT;
2958
+ const token = authorizationHeader.slice("payment ".length).trim();
2959
+ if (!token) return NULL_RESULT;
2960
+ let credential;
2961
+ try {
2962
+ credential = JSON.parse(atob(token));
2963
+ } catch {
2964
+ return NULL_RESULT;
2965
+ }
2966
+ if (!credential || typeof credential !== "object") return NULL_RESULT;
2967
+ let source = credential.source;
2968
+ if (typeof source !== "string") {
2969
+ const challenge = credential.challenge;
2970
+ if (challenge && typeof challenge === "object") {
2971
+ source = challenge.source;
2972
+ }
2973
+ }
2974
+ if (typeof source !== "string") return NULL_RESULT;
2975
+ const parts = source.split(":");
2976
+ if (parts.length < 4 || parts[0] !== "did" || parts[1] !== "pkh") return NULL_RESULT;
2977
+ const family = parts[2];
2978
+ const addr = parts[parts.length - 1] ?? "";
2979
+ if (family === "eip155" && EVM_RE.test(addr)) {
2980
+ return { signerAddress: addr.toLowerCase(), signerNetwork: "evm", txHash: null };
2981
+ }
2982
+ if (family === "solana" && SOLANA_BASE58_RE2.test(addr)) {
2983
+ return { signerAddress: addr, signerNetwork: "solana", txHash: null };
2984
+ }
2985
+ return NULL_RESULT;
2986
+ }
2987
+
2988
+ // src/checkout.ts
2989
+ function pricingResult(opts) {
2990
+ const currency = opts.currency ?? "USD";
2991
+ if (opts.subtotalCents !== void 0) {
2992
+ const totalCents = Math.max(
2993
+ 0,
2994
+ opts.subtotalCents + (opts.taxCents ?? 0) + (opts.shippingCents ?? 0) - (opts.discountCents ?? 0)
738
2995
  );
2996
+ const derivedAmount = opts.amountUsd ?? totalCents / 100;
2997
+ const block = buildPricingBlock({
2998
+ subtotalCents: opts.subtotalCents,
2999
+ taxCents: opts.taxCents ?? 0,
3000
+ ...opts.shippingCents !== void 0 && { shippingCents: opts.shippingCents },
3001
+ ...opts.discountCents !== void 0 && { discountCents: opts.discountCents },
3002
+ ...opts.taxRate !== void 0 && { taxRate: opts.taxRate },
3003
+ ...opts.taxState !== void 0 && { taxState: opts.taxState },
3004
+ currency
3005
+ });
3006
+ return {
3007
+ amountUsd: derivedAmount,
3008
+ currency,
3009
+ block,
3010
+ ...opts.product !== void 0 && { product: opts.product },
3011
+ ...opts.bodyExtras !== void 0 && { bodyExtras: opts.bodyExtras }
3012
+ };
3013
+ }
3014
+ if (opts.amountUsd === void 0) {
3015
+ throw new Error("pricingResult requires either `subtotalCents` or `amountUsd`.");
3016
+ }
3017
+ return {
3018
+ amountUsd: opts.amountUsd,
3019
+ currency,
3020
+ ...opts.product !== void 0 && { product: opts.product },
3021
+ ...opts.bodyExtras !== void 0 && { bodyExtras: opts.bodyExtras }
3022
+ };
3023
+ }
3024
+ function getIdentityStatus(ctx) {
3025
+ const assess = ctx.request.assess;
3026
+ if (assess === null || assess === void 0) return "anonymous";
3027
+ const decision = assess.decision;
3028
+ if (decision === "allow") return "verified";
3029
+ return "unverified";
3030
+ }
3031
+ var CheckoutValidationError = class extends Error {
3032
+ code;
3033
+ action;
3034
+ status;
3035
+ extra;
3036
+ constructor(opts) {
3037
+ super(opts.message);
3038
+ this.name = "CheckoutValidationError";
3039
+ this.code = opts.code;
3040
+ this.action = opts.action ?? "fix_request";
3041
+ this.status = opts.status ?? 400;
3042
+ this.extra = opts.extra;
3043
+ }
3044
+ };
3045
+ function lowerHeaders2(headers) {
3046
+ const out = {};
3047
+ for (const [k, v] of Object.entries(headers)) out[k.toLowerCase()] = v;
3048
+ return out;
3049
+ }
3050
+ function hasX402Header(headers) {
3051
+ const h = lowerHeaders2(headers);
3052
+ return Boolean(h["payment-signature"] ?? h["x-payment"]);
3053
+ }
3054
+ function hasMppxHeader(headers) {
3055
+ const h = lowerHeaders2(headers);
3056
+ return (h["authorization"] ?? "").startsWith("Payment ");
3057
+ }
3058
+ function resolveIdentityMetadata(ctx) {
3059
+ const h = lowerHeaders2(ctx.request.headers);
3060
+ const wallet = h["x-wallet-address"];
3061
+ if (!wallet) return void 0;
3062
+ let linkedWallets;
3063
+ const assess = ctx.request.assess;
3064
+ if (assess && typeof assess === "object") {
3065
+ const identity = assess["identity"];
3066
+ if (identity && typeof identity === "object") {
3067
+ const lw = identity["linked_wallets"];
3068
+ if (Array.isArray(lw) && lw.every((x) => typeof x === "string")) {
3069
+ linkedWallets = lw;
3070
+ }
3071
+ }
3072
+ }
3073
+ return buildIdentityMetadata({
3074
+ mode: "wallet",
3075
+ wallet,
3076
+ ...linkedWallets !== void 0 ? { linkedWallets } : {}
3077
+ });
3078
+ }
3079
+ function isStripeRailSpec2(s) {
3080
+ return !("recipient" in s);
3081
+ }
3082
+ function isTempoSessionRailSpec3(s) {
3083
+ return "escrowContract" in s && "store" in s;
3084
+ }
3085
+ function specRailKey(spec) {
3086
+ if (isStripeRailSpec2(spec)) return "stripe";
3087
+ if (isTempoSessionRailSpec3(spec)) return "tempo_mpp";
3088
+ const network = spec.network ?? "";
3089
+ if (network.startsWith("eip155:")) return "x402_base";
3090
+ if (network.startsWith("solana:") || "rpcUrl" in spec) return "solana_mpp";
3091
+ return "tempo_mpp";
3092
+ }
3093
+ function specMethodName(spec) {
3094
+ if (isStripeRailSpec2(spec)) return "stripe/spt";
3095
+ if (isTempoSessionRailSpec3(spec)) return "tempo/charge";
3096
+ const network = spec.network ?? "";
3097
+ if (network.startsWith("eip155:")) return "x402/exact (base)";
3098
+ if (network.startsWith("solana:") || "rpcUrl" in spec) return "solana/charge";
3099
+ return "tempo/charge";
3100
+ }
3101
+ function makeMppxComposeHook(opts) {
3102
+ return async (ctx) => {
3103
+ if (ctx.pricing === null) return { status: 402 };
3104
+ const mpp = await opts.serverGetter();
3105
+ const lower = lowerHeaders2(ctx.request.headers);
3106
+ const authorization = lower["authorization"];
3107
+ const amountStr = ctx.pricing.amountUsd.toFixed(2);
3108
+ let result;
3109
+ try {
3110
+ result = await mpp.charge({ authorization, amount: amountStr });
3111
+ } catch {
3112
+ return { status: 402 };
3113
+ }
3114
+ if (!Array.isArray(result)) {
3115
+ const challenge = result;
3116
+ const realm = mpp.realm ?? "";
3117
+ const headers = typeof challenge.toWwwAuthenticate === "function" ? { "www-authenticate": challenge.toWwwAuthenticate(realm) } : {};
3118
+ return { status: 402, headers };
3119
+ }
3120
+ const [credential, receipt] = result;
3121
+ const txHash = receipt.reference ?? receipt.transaction ?? null;
3122
+ let signerAddress = null;
3123
+ let signerNetwork = null;
3124
+ const source = credential.source;
3125
+ if (typeof source === "string") {
3126
+ const parts = source.split(":");
3127
+ if (parts.length >= 4 && parts[0] === "did" && parts[1] === "pkh") {
3128
+ const family = parts[2];
3129
+ const addr = parts[parts.length - 1] ?? null;
3130
+ if (family === "eip155" && addr !== null) {
3131
+ signerAddress = addr.toLowerCase();
3132
+ signerNetwork = "evm";
3133
+ } else if (family === "solana" && addr !== null) {
3134
+ signerAddress = addr;
3135
+ signerNetwork = "solana";
3136
+ }
3137
+ }
3138
+ }
3139
+ return {
3140
+ status: 200,
3141
+ txHash,
3142
+ signerAddress,
3143
+ signerNetwork,
3144
+ raw: { credential, receipt }
3145
+ };
3146
+ };
3147
+ }
3148
+ function applyRecipientOverrides(rails2, overrides) {
3149
+ const out = {};
3150
+ for (const [key, spec] of Object.entries(rails2)) {
3151
+ if (isStripeRailSpec2(spec)) {
3152
+ out[key] = spec;
3153
+ continue;
3154
+ }
3155
+ const override = overrides[key];
3156
+ const finalRecipient = override ?? spec.recipient;
3157
+ if (finalRecipient === "" || finalRecipient === void 0) continue;
3158
+ out[key] = override !== void 0 ? { ...spec, recipient: override } : spec;
3159
+ }
3160
+ return out;
3161
+ }
3162
+ function pickRail(rails2, key) {
3163
+ const spec = rails2[key];
3164
+ return spec === void 0 ? void 0 : spec;
3165
+ }
3166
+ var Checkout = class {
3167
+ rails;
3168
+ url;
3169
+ merchantName;
3170
+ computePricing;
3171
+ preValidate;
3172
+ x402Server;
3173
+ composeMppx;
3174
+ mintRecipients;
3175
+ mintReferenceId;
3176
+ onSettled;
3177
+ isCachedAddress;
3178
+ zeroSettleCarveOut;
3179
+ gate;
3180
+ discoveryExtensions;
3181
+ discoveryProbe;
3182
+ _x402ServerGetter;
3183
+ constructor(opts) {
3184
+ const x402Server = opts.x402Server;
3185
+ let x402ServerGetter;
3186
+ if (x402Server === void 0) {
3187
+ const baseSpec = Object.values(opts.rails).find(
3188
+ (s) => !isTempoSessionRailSpec3(s) && !isStripeRailSpec2(s) && "recipient" in s && (s.network ?? "").startsWith("eip155:")
3189
+ );
3190
+ if (baseSpec !== void 0) {
3191
+ x402ServerGetter = lazyX402Server({
3192
+ spec: baseSpec,
3193
+ cdpApiKeyId: opts.cdpApiKeyId,
3194
+ cdpApiKeySecret: opts.cdpApiKeySecret
3195
+ });
3196
+ }
3197
+ } else {
3198
+ const baseSpec = opts.rails["x402_base"];
3199
+ if (baseSpec === void 0 || !("recipient" in baseSpec)) {
3200
+ throw new Error(
3201
+ "Checkout: x402Server requires an X402BaseRailSpec in rails['x402_base'] (the rail's `network` field supplies the CAIP-2)."
3202
+ );
3203
+ }
3204
+ }
3205
+ let composeMppx = opts.composeMppx;
3206
+ if (composeMppx === void 0 && opts.mppxSecretKey !== void 0) {
3207
+ const mppRails = {};
3208
+ for (const [k, v] of Object.entries(opts.rails)) {
3209
+ if (isStripeRailSpec2(v) || isTempoSessionRailSpec3(v) || "recipient" in v) {
3210
+ mppRails[k] = v;
3211
+ }
3212
+ }
3213
+ const getter = lazyMppxServer({
3214
+ rails: mppRails,
3215
+ secretKey: opts.mppxSecretKey
3216
+ });
3217
+ composeMppx = makeMppxComposeHook({ serverGetter: getter });
3218
+ }
3219
+ this.rails = opts.rails;
3220
+ this.url = opts.url;
3221
+ this.merchantName = opts.gate?.merchantName;
3222
+ this.computePricing = opts.computePricing;
3223
+ this.preValidate = opts.preValidate;
3224
+ this.x402Server = x402Server;
3225
+ this._x402ServerGetter = x402ServerGetter;
3226
+ this.composeMppx = composeMppx;
3227
+ this.mintRecipients = opts.mintRecipients;
3228
+ this.mintReferenceId = opts.mintReferenceId;
3229
+ this.onSettled = opts.onSettled;
3230
+ this.isCachedAddress = opts.isCachedAddress;
3231
+ this.zeroSettleCarveOut = opts.zeroSettleCarveOut ?? false;
3232
+ this.gate = opts.gate;
3233
+ this.discoveryExtensions = opts.discoveryExtensions;
3234
+ this.discoveryProbe = opts.discoveryProbe;
739
3235
  }
740
- const stripped = { ...profile };
741
- const sig = stripped.signature;
742
- delete stripped.signature;
743
- if (typeof sig !== "string" || !sig) {
744
- throw new UCPVerificationError(
745
- "no_signature",
746
- `UCP profile signature must be a non-empty string; got ${sig === void 0 ? "undefined" : typeof sig}.`
747
- );
3236
+ /** Canonical `RailKey` list derived from the configured rails dict. Each
3237
+ * `*RailSpec` type maps to one `RailKey` (Tempo and TempoSession both fold
3238
+ * to `"tempo_mpp"`). Dedupes so listing is per protocol, not per recipient.
3239
+ * Use in `.well-known/mpp.json`, skill.md / llms.txt discovery responses. */
3240
+ get acceptedRails() {
3241
+ const out = [];
3242
+ const seen = /* @__PURE__ */ new Set();
3243
+ for (const spec of Object.values(this.rails)) {
3244
+ const key = specRailKey(spec);
3245
+ if (seen.has(key)) continue;
3246
+ seen.add(key);
3247
+ out.push(key);
3248
+ }
3249
+ return out;
748
3250
  }
749
- let header;
750
- try {
751
- const protectedB64 = sig.split(".")[0];
752
- if (!protectedB64) throw new Error("JWS protected header segment is empty.");
753
- const headerJson = new TextDecoder().decode(jose.base64url.decode(protectedB64));
754
- const parsed = JSON.parse(headerJson);
755
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
756
- throw new Error("JWS protected header is not a JSON object.");
3251
+ /** Protocol-shaped method-name list (`"tempo/charge"`, `"x402/exact (base)"`).
3252
+ * Suitable for the `methods: [...]` array of `.well-known/mpp.json`. */
3253
+ get acceptedMethodNames() {
3254
+ const out = [];
3255
+ const seen = /* @__PURE__ */ new Set();
3256
+ for (const spec of Object.values(this.rails)) {
3257
+ const name = specMethodName(spec);
3258
+ if (seen.has(name)) continue;
3259
+ seen.add(name);
3260
+ out.push(name);
757
3261
  }
758
- header = parsed;
759
- } catch (err) {
760
- throw new UCPVerificationError(
761
- "malformed_jws",
762
- `JWS protected header is not valid base64url-encoded JSON: ${err instanceof Error ? err.message : String(err)}`
763
- );
3262
+ return out;
764
3263
  }
765
- if (header.typ !== PROFILE_TYP) {
766
- throw new UCPVerificationError("wrong_typ", `UCP signature typ must be "${PROFILE_TYP}"; got ${String(header.typ)}.`);
3264
+ /** Resolve the x402 server, awaiting the lazy getter on first use. */
3265
+ async getX402Server() {
3266
+ if (this.x402Server !== void 0) return this.x402Server;
3267
+ if (this._x402ServerGetter === void 0) return void 0;
3268
+ this.x402Server = await this._x402ServerGetter();
3269
+ return this.x402Server;
767
3270
  }
768
- if (!ALLOWED_ALGS.includes(header.alg)) {
769
- throw new UCPVerificationError("unsupported_alg", `UCP signing alg must be one of ${ALLOWED_ALGS.join(", ")}; got ${String(header.alg)}.`);
3271
+ x402ServerAvailable() {
3272
+ return this.x402Server !== void 0 || this._x402ServerGetter !== void 0;
770
3273
  }
771
- if (typeof header.kid !== "string" || !header.kid) {
772
- throw new UCPVerificationError(
773
- "missing_kid",
774
- `UCP signature header kid must be a non-empty string; got ${header.kid === void 0 ? "undefined" : typeof header.kid}.`
775
- );
3274
+ /** Return the rails-dict key for the X402BaseRailSpec entry. Defaults to
3275
+ * `"x402_base"` when no match found. */
3276
+ x402RailKey() {
3277
+ for (const [k, v] of Object.entries(this.rails)) {
3278
+ if (!isStripeRailSpec2(v) && !isTempoSessionRailSpec3(v) && (v.network ?? "").startsWith("eip155:")) {
3279
+ return k;
3280
+ }
3281
+ }
3282
+ return "x402_base";
776
3283
  }
777
- if ("crit" in header) {
778
- const crit = header.crit;
779
- if (!Array.isArray(crit) || crit.length === 0 || !crit.every((c) => typeof c === "string")) {
780
- throw new UCPVerificationError(
781
- "malformed_jws",
782
- `JWS protected header crit must be a non-empty array of strings; got ${JSON.stringify(crit)}.`
783
- );
3284
+ /** Return the rails-dict key for the primary MPP rail. */
3285
+ mppRailKey() {
3286
+ for (const [k, v] of Object.entries(this.rails)) {
3287
+ const network = v.network ?? "";
3288
+ if (!isStripeRailSpec2(v) && !network.startsWith("eip155:")) return k;
784
3289
  }
785
- throw new UCPVerificationError(
786
- "unrecognized_critical_header",
787
- `JWS protected header advertises unrecognized crit headers: ${JSON.stringify(crit)}.`
788
- );
3290
+ return "tempo";
789
3291
  }
790
- let signedPayload;
791
- try {
792
- const verified = await jose.compactVerify(
793
- sig,
794
- async (h) => {
795
- const kid = h.kid;
796
- if (typeof kid !== "string" || !kid) {
797
- throw new UCPVerificationError(
798
- "missing_kid",
799
- `UCP signature header kid must be a non-empty string; got ${kid === void 0 ? "undefined" : typeof kid}.`
800
- );
3292
+ /** CAIP-2 read from `rails['x402_base'].network` (or its default).
3293
+ * Defined only when an `X402BaseRailSpec` is present in rails AND a server
3294
+ * is configured (explicit or auto-derived); otherwise `null`. */
3295
+ get x402BaseNetwork() {
3296
+ if (!this.x402ServerAvailable()) return null;
3297
+ for (const spec of Object.values(this.rails)) {
3298
+ if (!isStripeRailSpec2(spec) && !isTempoSessionRailSpec3(spec) && (spec.network ?? "").startsWith("eip155:")) {
3299
+ return spec.network ?? "eip155:8453";
3300
+ }
3301
+ }
3302
+ return null;
3303
+ }
3304
+ async handle(request) {
3305
+ const referenceId = await this.mintRefId(request);
3306
+ const ctx = {
3307
+ request,
3308
+ referenceId,
3309
+ pricing: null,
3310
+ recipients: {},
3311
+ state: {}
3312
+ };
3313
+ if (this.discoveryProbe !== void 0 && request.method === "POST") {
3314
+ const auth = request.headers["authorization"] ?? request.headers["Authorization"];
3315
+ const isProbe = !auth?.startsWith("Payment ") && !hasX402Header(request.headers) && !hasMppxHeader(request.headers) && (request.body === void 0 || request.body === null || typeof request.body === "object" && Object.keys(request.body).length === 0);
3316
+ if (isProbe) {
3317
+ const { buildDiscoveryProbeResponse: buildDiscoveryProbeResponse2 } = await Promise.resolve().then(() => (init_probe(), probe_exports));
3318
+ const cfg = this.discoveryProbe;
3319
+ const probe = buildDiscoveryProbeResponse2({
3320
+ realm: cfg.realm,
3321
+ sampleRail: cfg.sampleRail,
3322
+ sampleAmountUsd: cfg.sampleAmountUsd,
3323
+ sampleRecipient: cfg.sampleRecipient,
3324
+ ...cfg.intent !== void 0 && { intent: cfg.intent },
3325
+ ...cfg.ttlSeconds !== void 0 && { ttlSeconds: cfg.ttlSeconds },
3326
+ ...cfg.docsUrl !== void 0 && { docsUrl: cfg.docsUrl },
3327
+ ...cfg.message !== void 0 && { message: cfg.message },
3328
+ ...cfg.x402Sample !== void 0 && { x402Sample: cfg.x402Sample }
3329
+ });
3330
+ return {
3331
+ status: probe.status,
3332
+ body: JSON.parse(probe.body),
3333
+ headers: probe.headers,
3334
+ referenceId: ctx.referenceId,
3335
+ settled: false
3336
+ };
3337
+ }
3338
+ }
3339
+ if (this.preValidate !== void 0) {
3340
+ try {
3341
+ const state = await this.preValidate(ctx);
3342
+ if (state && typeof state === "object") Object.assign(ctx.state, state);
3343
+ } catch (err) {
3344
+ if (err instanceof CheckoutValidationError) {
3345
+ return this.validationErrorResult(ctx, err);
801
3346
  }
802
- const matches = jwks.keys.filter(
803
- (k) => k != null && typeof k === "object" && k.kid === kid
3347
+ throw err;
3348
+ }
3349
+ }
3350
+ const hasPaymentHeader = hasX402Header(request.headers) || hasMppxHeader(request.headers);
3351
+ if (this.gate !== void 0 && hasPaymentHeader) {
3352
+ const denial = await this.runGate(ctx);
3353
+ if (denial !== null) {
3354
+ return {
3355
+ status: denial.status,
3356
+ body: denial.body,
3357
+ headers: { ...denial.headers ?? {} },
3358
+ referenceId: ctx.referenceId,
3359
+ settled: false
3360
+ };
3361
+ }
3362
+ }
3363
+ ctx.pricing = await this.computePricing(ctx);
3364
+ await this.resolveRecipientsForCtx(ctx);
3365
+ const x402ServerOk = this.x402ServerAvailable() && this.x402BaseNetwork !== null;
3366
+ if (hasX402Header(request.headers) && x402ServerOk) {
3367
+ const zero = await this.handleZeroSettle(ctx, "x402-base");
3368
+ if (zero !== null) return zero;
3369
+ return await this.handleX402(ctx);
3370
+ }
3371
+ if (hasMppxHeader(request.headers) && this.composeMppx !== void 0) {
3372
+ const zero = await this.handleZeroSettle(ctx, "tempo");
3373
+ if (zero !== null) return zero;
3374
+ return await this.handleMppx(ctx);
3375
+ }
3376
+ await this.resolveRecipientsForCtx(ctx);
3377
+ let mppxHeaders = {};
3378
+ if (this.composeMppx !== void 0) {
3379
+ try {
3380
+ const preComposed = await this.composeMppx(ctx);
3381
+ if (preComposed.status === 402) {
3382
+ mppxHeaders = { ...preComposed.headers ?? {} };
3383
+ }
3384
+ } catch {
3385
+ }
3386
+ }
3387
+ return await this.emit402(ctx, mppxHeaders);
3388
+ }
3389
+ validationErrorResult(ctx, err) {
3390
+ const body = buildValidationError({
3391
+ code: err.code,
3392
+ message: err.message,
3393
+ nextSteps: { action: err.action, user_message: err.message },
3394
+ extra: err.extra
3395
+ });
3396
+ return {
3397
+ status: err.status,
3398
+ body,
3399
+ headers: {},
3400
+ referenceId: ctx.referenceId,
3401
+ settled: false
3402
+ };
3403
+ }
3404
+ async runGate(ctx) {
3405
+ const gate = this.gate;
3406
+ if (gate === void 0) return null;
3407
+ if (gate.runGate !== void 0) {
3408
+ const result = await gate.runGate(ctx);
3409
+ if (result === void 0 || result === null) return null;
3410
+ if (typeof result !== "object" || typeof result.status !== "number") {
3411
+ throw new TypeError(
3412
+ "gate.runGate must return null/undefined (allow) or an object { status, body, headers? } (deny)"
804
3413
  );
805
- if (matches.length === 0) throw new UCPVerificationError("kid_not_found", `No JWK in JWKS matching kid=${JSON.stringify(kid)}.`);
806
- if (matches.length > 1) throw new UCPVerificationError("duplicate_kid", `JWKS contains ${matches.length} keys with kid=${JSON.stringify(kid)}; expected exactly one.`);
807
- const matchedKey = matches[0];
808
- if (matchedKey.use != null && matchedKey.use !== "sig") {
809
- throw new UCPVerificationError("unusable_key", `JWK with kid=${kid} has use=${JSON.stringify(matchedKey.use)}; expected "sig".`);
3414
+ }
3415
+ return result;
3416
+ }
3417
+ if (gate.apiKey === void 0) return null;
3418
+ let policyOverride;
3419
+ if (gate.perRequestPolicy !== void 0) {
3420
+ policyOverride = await gate.perRequestPolicy(ctx);
3421
+ if (policyOverride === null) return null;
3422
+ }
3423
+ const coreOpts = {
3424
+ apiKey: gate.apiKey,
3425
+ ...gate.baseUrl !== void 0 && { baseUrl: gate.baseUrl },
3426
+ ...gate.userAgent !== void 0 && { userAgent: gate.userAgent },
3427
+ ...gate.requireKyc !== void 0 && { requireKyc: gate.requireKyc },
3428
+ ...gate.requireSanctionsClear !== void 0 && { requireSanctionsClear: gate.requireSanctionsClear },
3429
+ ...gate.minAge !== void 0 && { minAge: gate.minAge },
3430
+ ...gate.blockedJurisdictions !== void 0 && { blockedJurisdictions: gate.blockedJurisdictions },
3431
+ ...gate.allowedJurisdictions !== void 0 && { allowedJurisdictions: gate.allowedJurisdictions },
3432
+ ...gate.failOpen !== void 0 && { failOpen: gate.failOpen },
3433
+ ...gate.cacheSeconds !== void 0 && { cacheSeconds: gate.cacheSeconds },
3434
+ ...gate.chain !== void 0 && { chain: gate.chain },
3435
+ ...gate.createSessionOnMissing !== void 0 && {
3436
+ createSessionOnMissing: gate.createSessionOnMissing
3437
+ },
3438
+ ...policyOverride ?? {}
3439
+ };
3440
+ const core = createAgentScoreCore(coreOpts);
3441
+ const headers = lowerHeaders2(ctx.request.headers);
3442
+ const walletAddress = headers["x-wallet-address"];
3443
+ const operatorToken = headers["x-operator-token"];
3444
+ const identity = walletAddress !== void 0 || operatorToken !== void 0 ? {
3445
+ ...walletAddress !== void 0 && { address: walletAddress },
3446
+ ...operatorToken !== void 0 && { operatorToken }
3447
+ } : void 0;
3448
+ const x402Header = headers["payment-signature"] ?? headers["x-payment"];
3449
+ const signer = await extractPaymentSignerFromAuth(headers["authorization"], x402Header);
3450
+ const outcome = await core.evaluate(identity, ctx, signer);
3451
+ if (outcome.kind === "allow") {
3452
+ if (operatorToken !== void 0) {
3453
+ const opToken = operatorToken;
3454
+ ctx.captureWallet = async (opts) => {
3455
+ await core.captureWallet({
3456
+ operatorToken: opToken,
3457
+ walletAddress: opts.walletAddress,
3458
+ network: opts.network,
3459
+ ...opts.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
3460
+ });
3461
+ };
3462
+ }
3463
+ if (walletAddress !== void 0) {
3464
+ const verdict = core.getSignerVerdict(walletAddress);
3465
+ const sm = verdict?.signer_match;
3466
+ if (sm && sm.kind !== "pass") {
3467
+ const reason2 = sm.kind === "wallet_auth_requires_wallet_signing" ? {
3468
+ code: "wallet_auth_requires_wallet_signing",
3469
+ expected_signer: sm.claimedWallet,
3470
+ agent_instructions: sm.agentInstructions
3471
+ } : {
3472
+ code: "wallet_signer_mismatch",
3473
+ ...sm.claimedOperator !== null && { claimed_operator: sm.claimedOperator },
3474
+ actual_signer_operator: sm.actualSignerOperator,
3475
+ expected_signer: sm.expectedSigner,
3476
+ actual_signer: sm.actualSigner,
3477
+ ...sm.linkedWallets.length > 0 && { linked_wallets: sm.linkedWallets },
3478
+ agent_instructions: sm.agentInstructions
3479
+ };
3480
+ if (gate.onDenied !== void 0) {
3481
+ const custom = await gate.onDenied(ctx, reason2);
3482
+ if (custom !== null) return custom;
3483
+ }
3484
+ const body2 = denialReasonToBody(reason2);
3485
+ return { status: 403, body: body2 };
810
3486
  }
811
- if (matchedKey.alg != null && matchedKey.alg !== h.alg) {
812
- throw new UCPVerificationError(
813
- "unusable_key",
814
- `JWK alg ${JSON.stringify(matchedKey.alg)} does not match JWS header alg ${JSON.stringify(h.alg)}.`
815
- );
3487
+ }
3488
+ return null;
3489
+ }
3490
+ const reason = outcome.reason;
3491
+ if (gate.onDenied !== void 0) {
3492
+ const custom = await gate.onDenied(ctx, reason);
3493
+ if (custom !== null) return custom;
3494
+ }
3495
+ const body = denialReasonToBody(reason);
3496
+ const status = reason.code === "token_expired" || reason.code === "invalid_credential" ? 401 : reason.code === "api_error" ? 503 : 403;
3497
+ return { status, body };
3498
+ }
3499
+ async handleZeroSettle(ctx, rail) {
3500
+ if (!this.zeroSettleCarveOut || ctx.pricing === null) return null;
3501
+ const cents = Math.round(ctx.pricing.amountUsd * 100);
3502
+ if (cents !== 0) return null;
3503
+ const headers = lowerHeaders2(ctx.request.headers);
3504
+ let zero;
3505
+ if (rail === "x402-base") {
3506
+ const x402Header = headers["payment-signature"] ?? headers["x-payment"];
3507
+ let payload = null;
3508
+ if (typeof x402Header === "string" && x402Header.length > 0) {
3509
+ try {
3510
+ payload = JSON.parse(atob(x402Header));
3511
+ } catch {
3512
+ payload = null;
816
3513
  }
817
- return jose.importJWK(matches[0], h.alg);
818
3514
  }
819
- );
820
- signedPayload = verified.payload;
821
- } catch (err) {
822
- if (err instanceof UCPVerificationError) throw err;
823
- if (err instanceof Error && err.name === "JOSEAlgNotAllowed") {
824
- throw new UCPVerificationError("unsupported_alg", `UCP signing alg not allowed: ${err.message}`);
3515
+ zero = zeroAmountCarveOut({ rail, payload });
3516
+ } else {
3517
+ zero = zeroAmountCarveOut({ rail, authorizationHeader: headers["authorization"] });
825
3518
  }
826
- if (err instanceof Error && err.name === "JWSSignatureVerificationFailed") {
827
- throw new UCPVerificationError("signature_invalid", `UCP signature verification failed: ${err.message}`);
3519
+ const railKey = rail === "x402-base" ? this.x402RailKey() : this.mppRailKey();
3520
+ const outcome = {
3521
+ rail: rail === "x402-base" ? "x402" : "mpp",
3522
+ paymentResponseHeader: null,
3523
+ raw: zero,
3524
+ txHash: null,
3525
+ signerAddress: zero.signerAddress,
3526
+ signerNetwork: zero.signerNetwork,
3527
+ railKey
3528
+ };
3529
+ return await this.buildSuccess(ctx, outcome);
3530
+ }
3531
+ async mintRefId(request) {
3532
+ if (this.mintReferenceId === void 0) return (0, import_node_crypto.randomUUID)();
3533
+ const seedCtx = { request, referenceId: "", pricing: null, recipients: {}, state: {} };
3534
+ return await this.mintReferenceId(seedCtx);
3535
+ }
3536
+ async resolveRecipientsForCtx(ctx) {
3537
+ if (this.mintRecipients === void 0) return ctx.recipients;
3538
+ if (Object.keys(ctx.recipients).length > 0) return ctx.recipients;
3539
+ ctx.recipients = { ...await this.mintRecipients(ctx) };
3540
+ return ctx.recipients;
3541
+ }
3542
+ async asyncIsCachedAddress(address) {
3543
+ if (this.isCachedAddress === void 0) return true;
3544
+ return Promise.resolve(this.isCachedAddress(address));
3545
+ }
3546
+ async handleX402(ctx) {
3547
+ const x402Server = await this.getX402Server();
3548
+ if (ctx.pricing === null || this.x402BaseNetwork === null || x402Server === void 0) {
3549
+ throw new Error("Checkout.handleX402: missing pricing or x402 rail config");
828
3550
  }
829
- if (err instanceof Error && err.name === "JWSInvalid") {
830
- throw new UCPVerificationError("malformed_jws", `Malformed JWS: ${err.message}`);
3551
+ const fakeRequest = new Request(ctx.request.url, {
3552
+ method: ctx.request.method,
3553
+ headers: ctx.request.headers
3554
+ });
3555
+ const verified = await verifyX402Request({
3556
+ request: fakeRequest,
3557
+ isCachedAddress: this.asyncIsCachedAddress.bind(this),
3558
+ acceptedNetwork: this.x402BaseNetwork
3559
+ });
3560
+ if (!verified.ok) {
3561
+ return {
3562
+ status: verified.status,
3563
+ body: verified.body,
3564
+ headers: {},
3565
+ referenceId: ctx.referenceId,
3566
+ settled: false,
3567
+ settlePhase: "verify_failed"
3568
+ };
831
3569
  }
832
- if (err instanceof Error && err.name === "JOSENotSupported") {
833
- throw new UCPVerificationError("unrecognized_critical_header", `UCP signing rejected unrecognized critical header: ${err.message}`);
3570
+ const settle = await processX402Settle({
3571
+ x402Server,
3572
+ payload: verified.payload,
3573
+ resourceConfig: {
3574
+ scheme: "exact",
3575
+ network: verified.signedNetwork,
3576
+ price: `$${ctx.pricing.amountUsd.toFixed(2)}`,
3577
+ payTo: verified.signedPayTo,
3578
+ maxTimeoutSeconds: 300
3579
+ },
3580
+ resourceMeta: {
3581
+ url: ctx.request.url,
3582
+ description: "Agent purchase via x402",
3583
+ mimeType: "application/json"
3584
+ }
3585
+ });
3586
+ if (!settle.success) {
3587
+ const classified = classifyX402SettleResult(settle);
3588
+ const responseHeaders = classified !== null && classified.status >= 500 ? { "Cache-Control": "no-store" } : {};
3589
+ if (classified !== null) {
3590
+ return {
3591
+ status: classified.status,
3592
+ body: {
3593
+ error: { code: classified.code, message: classified.message },
3594
+ next_steps: classified.nextSteps
3595
+ },
3596
+ headers: responseHeaders,
3597
+ referenceId: ctx.referenceId,
3598
+ settled: false,
3599
+ settlePhase: settle.phase ?? "settle_failed"
3600
+ };
3601
+ }
3602
+ return {
3603
+ status: 400,
3604
+ body: buildValidationError({
3605
+ code: "payment_proof_invalid",
3606
+ message: `Payment failed during settlement (phase: ${settle.phase ?? "unknown"}).`,
3607
+ nextSteps: { action: "regenerate_payment_credential" },
3608
+ extra: { phase: settle.phase }
3609
+ }),
3610
+ headers: {},
3611
+ referenceId: ctx.referenceId,
3612
+ settled: false,
3613
+ settlePhase: settle.phase ?? "settle_failed"
3614
+ };
834
3615
  }
835
- throw err;
3616
+ const settleRes = settle.settleResult ?? {};
3617
+ const verifiedFrom = verified.payload.payload?.authorization?.from ?? null;
3618
+ const signerAddress = verifiedFrom !== null ? verifiedFrom.toLowerCase() : settleRes.payer ?? null;
3619
+ const outcome = {
3620
+ rail: "x402",
3621
+ paymentResponseHeader: settle.paymentResponseHeader ?? null,
3622
+ raw: settle,
3623
+ txHash: settleRes.transaction ?? null,
3624
+ signerAddress,
3625
+ signerNetwork: signerAddress !== null ? "evm" : null,
3626
+ railKey: this.x402RailKey()
3627
+ };
3628
+ return await this.buildSuccess(ctx, outcome);
836
3629
  }
837
- let canonicalBody;
838
- try {
839
- canonicalBody = canonicalizeProfile(stripped);
840
- } catch (err) {
841
- throw new UCPVerificationError(
842
- "body_mismatch",
843
- `Failed to canonicalize received profile for verification: ${err instanceof Error ? err.message : String(err)}`
844
- );
3630
+ async handleMppx(ctx) {
3631
+ if (this.composeMppx === void 0) {
3632
+ throw new Error("Checkout.handleMppx: composeMppx hook not configured");
3633
+ }
3634
+ const composed = await this.composeMppx(ctx);
3635
+ if (composed.status === 200) {
3636
+ const outcome = {
3637
+ rail: "mpp",
3638
+ paymentResponseHeader: composed.paymentResponseHeader ?? null,
3639
+ raw: composed.raw,
3640
+ txHash: composed.txHash ?? null,
3641
+ signerAddress: composed.signerAddress ?? null,
3642
+ signerNetwork: composed.signerNetwork ?? null,
3643
+ railKey: composed.railKey ?? this.mppRailKey()
3644
+ };
3645
+ return await this.buildSuccess(ctx, outcome);
3646
+ }
3647
+ return {
3648
+ status: 400,
3649
+ body: buildValidationError({
3650
+ code: "payment_proof_invalid",
3651
+ message: "MPP credential rejected; regenerate from a fresh 402 challenge.",
3652
+ nextSteps: { action: "regenerate_payment_credential" }
3653
+ }),
3654
+ headers: { ...composed.headers ?? {} },
3655
+ referenceId: ctx.referenceId,
3656
+ settled: false,
3657
+ settlePhase: "verify_failed"
3658
+ };
845
3659
  }
846
- const expectedPayload = new TextEncoder().encode(canonicalBody);
847
- if (!constantTimeEqual(signedPayload, expectedPayload)) {
848
- throw new UCPVerificationError("body_mismatch", "UCP profile body does not match the signed payload (tampered or non-canonical).");
3660
+ async emit402(ctx, mppxHeaders = {}) {
3661
+ if (ctx.pricing === null) {
3662
+ throw new Error("Checkout.emit402: pricing not computed");
3663
+ }
3664
+ await this.resolveRecipientsForCtx(ctx);
3665
+ const emitRails = applyRecipientOverrides(this.rails, ctx.recipients);
3666
+ const accepted = await buildAcceptedMethods({
3667
+ tempo: pickRail(emitRails, "tempo"),
3668
+ x402_base: pickRail(emitRails, "x402_base"),
3669
+ solana_mpp: pickRail(emitRails, "solana_mpp"),
3670
+ stripe: pickRail(emitRails, "stripe")
3671
+ });
3672
+ const howToPayRails = {};
3673
+ for (const [k, v] of Object.entries(emitRails)) {
3674
+ if (!isTempoSessionRailSpec3(v)) {
3675
+ howToPayRails[k] = v;
3676
+ }
3677
+ }
3678
+ const howToPay = buildHowToPay({
3679
+ url: this.url,
3680
+ retryBodyJson: JSON.stringify(ctx.request.body),
3681
+ totalUsd: ctx.pricing.amountUsd.toFixed(2),
3682
+ rails: howToPayRails
3683
+ });
3684
+ const pricingBlock = ctx.pricing.block ?? buildPricingBlock({
3685
+ subtotalCents: Math.round(ctx.pricing.amountUsd * 100),
3686
+ currency: ctx.pricing.currency ?? "USD"
3687
+ });
3688
+ let x402Accepts = [];
3689
+ let x402Resource;
3690
+ const baseNetwork = this.x402BaseNetwork;
3691
+ const x402Server = await this.getX402Server();
3692
+ if (x402Server !== void 0 && baseNetwork !== null) {
3693
+ const baseSpec = emitRails["x402_base"];
3694
+ if (baseSpec !== void 0) {
3695
+ const recipient = await resolveRecipientValue(baseSpec.recipient);
3696
+ try {
3697
+ x402Accepts = await buildX402AcceptsFor402(x402Server, {
3698
+ network: baseNetwork,
3699
+ price: `$${ctx.pricing.amountUsd.toFixed(2)}`,
3700
+ payTo: recipient,
3701
+ maxTimeoutSeconds: 300
3702
+ });
3703
+ x402Resource = { url: ctx.request.url, mimeType: "application/json" };
3704
+ } catch {
3705
+ x402Accepts = [];
3706
+ }
3707
+ }
3708
+ }
3709
+ const identityMetadata = resolveIdentityMetadata(ctx);
3710
+ const body = build402Body({
3711
+ acceptedMethods: accepted,
3712
+ agentInstructions: buildAgentInstructions({ howToPay }),
3713
+ ...identityMetadata !== void 0 ? { identityMetadata } : {},
3714
+ pricing: pricingBlock,
3715
+ amountUsd: ctx.pricing.amountUsd.toFixed(2),
3716
+ retryBody: ctx.request.body,
3717
+ agentMemory: firstEncounterAgentMemory({ firstEncounter: true }),
3718
+ ...ctx.pricing.product ? { product: ctx.pricing.product } : {},
3719
+ ...ctx.pricing.bodyExtras ? { extra: ctx.pricing.bodyExtras } : {},
3720
+ ...x402Accepts.length > 0 ? {
3721
+ x402: {
3722
+ accepts: x402Accepts,
3723
+ ...this.discoveryExtensions !== void 0 && Object.keys(this.discoveryExtensions).length > 0 ? { extensions: this.discoveryExtensions } : {}
3724
+ }
3725
+ } : {}
3726
+ });
3727
+ let x402Block;
3728
+ if (x402Accepts.length > 0) {
3729
+ x402Block = {
3730
+ x402Version: 2,
3731
+ accepts: x402Accepts,
3732
+ ...x402Resource ? { resource: x402Resource } : {}
3733
+ };
3734
+ }
3735
+ const respond = respond402({
3736
+ mppxChallengeHeaders: mppxHeaders,
3737
+ body,
3738
+ x402: x402Block
3739
+ });
3740
+ return {
3741
+ status: respond.status,
3742
+ body: respond.body,
3743
+ headers: respond.headers,
3744
+ referenceId: ctx.referenceId,
3745
+ settled: false
3746
+ };
849
3747
  }
850
- return true;
3748
+ async buildSuccess(ctx, outcome) {
3749
+ let customBody = null;
3750
+ if (this.onSettled !== void 0) {
3751
+ const result = await this.onSettled(ctx, outcome);
3752
+ if (result !== null && typeof result === "object") customBody = result;
3753
+ }
3754
+ const body = customBody ?? { ok: true };
3755
+ if (!("reference_id" in body)) body.reference_id = ctx.referenceId;
3756
+ const headers = {};
3757
+ if (outcome.paymentResponseHeader) {
3758
+ headers["payment-response"] = outcome.paymentResponseHeader;
3759
+ }
3760
+ return {
3761
+ status: 200,
3762
+ body,
3763
+ headers,
3764
+ referenceId: ctx.referenceId,
3765
+ settled: true
3766
+ };
3767
+ }
3768
+ };
3769
+ async function resolveRecipientValue(r) {
3770
+ return await resolveRecipient(r);
851
3771
  }
852
- function constantTimeEqual(a, b) {
853
- if (a.length !== b.length) return false;
854
- let diff = 0;
855
- for (let i = 0; i < a.length; i += 1) {
856
- diff |= a[i] ^ b[i];
3772
+ function validationEnvelope(opts) {
3773
+ const action = opts.action ?? "fix_request";
3774
+ return buildValidationError({
3775
+ code: opts.code,
3776
+ message: opts.message,
3777
+ nextSteps: { action, user_message: opts.message },
3778
+ extra: opts.extra
3779
+ });
3780
+ }
3781
+ function validationResponseHono(input) {
3782
+ const status = input.status ?? 400;
3783
+ const body = validationEnvelope(input);
3784
+ return new Response(JSON.stringify(body), {
3785
+ status,
3786
+ headers: { "Content-Type": "application/json" }
3787
+ });
3788
+ }
3789
+ function validationResponseExpress(res, input) {
3790
+ const status = input.status ?? 400;
3791
+ const body = validationEnvelope(input);
3792
+ res.status(status);
3793
+ res.json(body);
3794
+ }
3795
+ function validationResponseFastify(reply, input) {
3796
+ const status = input.status ?? 400;
3797
+ const body = validationEnvelope(input);
3798
+ reply.code(status);
3799
+ return reply.send(body);
3800
+ }
3801
+ function validationResponseNextjs(input) {
3802
+ return validationResponseHono(input);
3803
+ }
3804
+ function validationResponseWeb(input) {
3805
+ return validationResponseHono(input);
3806
+ }
3807
+ function invalidBodyEnvelope() {
3808
+ return validationEnvelope({
3809
+ code: "invalid_body",
3810
+ message: "Request body must be valid JSON."
3811
+ });
3812
+ }
3813
+ function stripContentType(headers) {
3814
+ const out = {};
3815
+ for (const [k, v] of Object.entries(headers)) {
3816
+ if (k.toLowerCase() !== "content-type") out[k] = v;
857
3817
  }
858
- return diff === 0;
3818
+ return out;
859
3819
  }
860
- function buildJWKSResponse(keys) {
861
- return { keys };
3820
+ function headersToRecord(h) {
3821
+ if (h === void 0) return {};
3822
+ if (h instanceof Headers) {
3823
+ const out = {};
3824
+ h.forEach((v, k) => {
3825
+ out[k] = v;
3826
+ });
3827
+ return out;
3828
+ }
3829
+ return { ...h };
862
3830
  }
3831
+ Checkout.prototype.handleHono = async function(c, body) {
3832
+ let parsedBody;
3833
+ if (body !== void 0) {
3834
+ parsedBody = body;
3835
+ } else {
3836
+ try {
3837
+ parsedBody = await c.req.json();
3838
+ } catch {
3839
+ return new Response(JSON.stringify(invalidBodyEnvelope()), {
3840
+ status: 400,
3841
+ headers: { "Content-Type": "application/json" }
3842
+ });
3843
+ }
3844
+ }
3845
+ const rawHeaders = c.req.header();
3846
+ const headers = headersToRecord(rawHeaders);
3847
+ const result = await this.handle({
3848
+ method: c.req.method,
3849
+ url: c.req.url,
3850
+ headers,
3851
+ body: parsedBody,
3852
+ assess: null,
3853
+ raw: c.req.raw ?? c
3854
+ });
3855
+ return new Response(JSON.stringify(result.body), {
3856
+ status: result.status,
3857
+ headers: { "Content-Type": "application/json", ...stripContentType(result.headers) }
3858
+ });
3859
+ };
3860
+ Checkout.prototype.handleExpress = async function(req, res, body) {
3861
+ const parsedBody = body ?? (typeof req.body === "object" && req.body !== null ? req.body : null);
3862
+ if (parsedBody === null) {
3863
+ res.status(400);
3864
+ res.json(invalidBodyEnvelope());
3865
+ return;
3866
+ }
3867
+ const headers = {};
3868
+ for (const [k, v] of Object.entries(req.headers)) {
3869
+ if (typeof v === "string") headers[k] = v;
3870
+ else if (Array.isArray(v) && v[0] !== void 0) headers[k] = v[0];
3871
+ }
3872
+ const url = req.originalUrl ?? req.url ?? "/";
3873
+ const result = await this.handle({
3874
+ method: req.method,
3875
+ url,
3876
+ headers,
3877
+ body: parsedBody,
3878
+ assess: null,
3879
+ raw: req
3880
+ });
3881
+ for (const [k, v] of Object.entries(stripContentType(result.headers))) res.setHeader(k, v);
3882
+ res.status(result.status);
3883
+ res.json(result.body);
3884
+ };
3885
+ Checkout.prototype.handleFastify = async function(request, reply, body) {
3886
+ const parsedBody = body ?? (typeof request.body === "object" && request.body !== null ? request.body : null);
3887
+ if (parsedBody === null) {
3888
+ reply.code(400);
3889
+ return reply.send(invalidBodyEnvelope());
3890
+ }
3891
+ const headers = {};
3892
+ for (const [k, v] of Object.entries(request.headers)) {
3893
+ if (typeof v === "string") headers[k] = v;
3894
+ else if (Array.isArray(v) && v[0] !== void 0) headers[k] = v[0];
3895
+ }
3896
+ const result = await this.handle({
3897
+ method: request.method,
3898
+ url: request.url,
3899
+ headers,
3900
+ body: parsedBody,
3901
+ assess: null,
3902
+ raw: request
3903
+ });
3904
+ for (const [k, v] of Object.entries(stripContentType(result.headers))) reply.header(k, v);
3905
+ reply.code(result.status);
3906
+ return reply.send(result.body);
3907
+ };
3908
+ Checkout.prototype.handleNextjs = async function(request, body) {
3909
+ let parsedBody;
3910
+ if (body !== void 0) {
3911
+ parsedBody = body;
3912
+ } else {
3913
+ try {
3914
+ parsedBody = await request.json();
3915
+ } catch {
3916
+ return new Response(JSON.stringify(invalidBodyEnvelope()), {
3917
+ status: 400,
3918
+ headers: { "Content-Type": "application/json" }
3919
+ });
3920
+ }
3921
+ }
3922
+ const headers = {};
3923
+ request.headers.forEach((v, k) => {
3924
+ headers[k] = v;
3925
+ });
3926
+ const result = await this.handle({
3927
+ method: request.method,
3928
+ url: request.url,
3929
+ headers,
3930
+ body: parsedBody,
3931
+ assess: null,
3932
+ raw: request
3933
+ });
3934
+ return new Response(JSON.stringify(result.body), {
3935
+ status: result.status,
3936
+ headers: { "Content-Type": "application/json", ...stripContentType(result.headers) }
3937
+ });
3938
+ };
3939
+ Checkout.prototype.handleWeb = Checkout.prototype.handleNextjs;
3940
+ async function _ucpSignedResp(checkout, reqHeaders, opts) {
3941
+ const { buildSignedUcpResponse: buildSignedUcpResponse2 } = await Promise.resolve().then(() => (init_well_known(), well_known_exports));
3942
+ return await buildSignedUcpResponse2({
3943
+ checkout,
3944
+ name: opts.name,
3945
+ wellKnownUcpUrl: opts.wellKnownUcpUrl,
3946
+ services: opts.services,
3947
+ requestHeaders: reqHeaders,
3948
+ ...opts.signingKid !== void 0 && { signingKid: opts.signingKid },
3949
+ ...opts.agentscoreGate !== void 0 && {
3950
+ agentscoreGate: opts.agentscoreGate
3951
+ }
3952
+ });
3953
+ }
3954
+ async function _jwksSignedResp(reqHeaders, opts) {
3955
+ const { buildSignedJwksResponse: buildSignedJwksResponse2 } = await Promise.resolve().then(() => (init_well_known(), well_known_exports));
3956
+ return await buildSignedJwksResponse2({
3957
+ requestHeaders: reqHeaders,
3958
+ ...opts.signingKid !== void 0 && { signingKid: opts.signingKid }
3959
+ });
3960
+ }
3961
+ function _preflightResp(reqHeaders) {
3962
+ return new Response(null, {
3963
+ status: 204,
3964
+ headers: _preflightHeaders(reqHeaders)
3965
+ });
3966
+ }
3967
+ function _preflightHeaders(reqHeaders) {
3968
+ const headers = {
3969
+ "Access-Control-Allow-Origin": "*",
3970
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
3971
+ "Access-Control-Max-Age": "86400",
3972
+ Vary: "Access-Control-Request-Headers"
3973
+ };
3974
+ const acrh = reqHeaders.get("access-control-request-headers");
3975
+ if (acrh) headers["Access-Control-Allow-Headers"] = acrh;
3976
+ return headers;
3977
+ }
3978
+ Checkout.prototype.mountUcpRoutesHono = function(app, opts) {
3979
+ const ucpPath = opts.ucpPath ?? "/.well-known/ucp";
3980
+ const jwksPath = opts.jwksPath ?? "/.well-known/jwks.json";
3981
+ const checkout = this;
3982
+ app.get(ucpPath, async (c) => {
3983
+ const resp = await _ucpSignedResp(checkout, c.req.raw.headers, opts);
3984
+ return new Response(resp.body, {
3985
+ status: resp.status,
3986
+ headers: { ...resp.headers, "Content-Type": resp.mediaType }
3987
+ });
3988
+ });
3989
+ app.get(jwksPath, async (c) => {
3990
+ const resp = await _jwksSignedResp(c.req.raw.headers, opts);
3991
+ return new Response(resp.body, {
3992
+ status: resp.status,
3993
+ headers: { ...resp.headers, "Content-Type": resp.mediaType }
3994
+ });
3995
+ });
3996
+ app.options(ucpPath, (c) => _preflightResp(c.req.raw.headers));
3997
+ app.options(jwksPath, (c) => _preflightResp(c.req.raw.headers));
3998
+ };
3999
+ function _headersFromExpressLike(raw) {
4000
+ const out = new Headers();
4001
+ for (const [k, v] of Object.entries(raw)) {
4002
+ if (v === void 0) continue;
4003
+ out.set(k, Array.isArray(v) ? v.join(",") : v);
4004
+ }
4005
+ return out;
4006
+ }
4007
+ Checkout.prototype.mountUcpRoutesExpress = function(app, opts) {
4008
+ const ucpPath = opts.ucpPath ?? "/.well-known/ucp";
4009
+ const jwksPath = opts.jwksPath ?? "/.well-known/jwks.json";
4010
+ const checkout = this;
4011
+ app.get(ucpPath, async (req, res) => {
4012
+ const resp = await _ucpSignedResp(checkout, _headersFromExpressLike(req.headers), opts);
4013
+ res.status(resp.status);
4014
+ res.set(resp.headers);
4015
+ res.type(resp.mediaType);
4016
+ res.send(resp.body);
4017
+ });
4018
+ app.get(jwksPath, async (req, res) => {
4019
+ const resp = await _jwksSignedResp(_headersFromExpressLike(req.headers), opts);
4020
+ res.status(resp.status);
4021
+ res.set(resp.headers);
4022
+ res.type(resp.mediaType);
4023
+ res.send(resp.body);
4024
+ });
4025
+ const preflight = (req, res) => {
4026
+ const reqHeaders = _headersFromExpressLike(req.headers);
4027
+ res.status(204);
4028
+ res.set(_preflightHeaders(reqHeaders));
4029
+ res.send("");
4030
+ };
4031
+ app.options(ucpPath, preflight);
4032
+ app.options(jwksPath, preflight);
4033
+ };
4034
+ Checkout.prototype.mountUcpRoutesFastify = function(app, opts) {
4035
+ const ucpPath = opts.ucpPath ?? "/.well-known/ucp";
4036
+ const jwksPath = opts.jwksPath ?? "/.well-known/jwks.json";
4037
+ const checkout = this;
4038
+ app.get(ucpPath, async (request, reply) => {
4039
+ const resp = await _ucpSignedResp(checkout, _headersFromExpressLike(request.headers), opts);
4040
+ reply.code(resp.status);
4041
+ for (const [k, v] of Object.entries(resp.headers)) reply.header(k, v);
4042
+ reply.type(resp.mediaType);
4043
+ return reply.send(resp.body);
4044
+ });
4045
+ app.get(jwksPath, async (request, reply) => {
4046
+ const resp = await _jwksSignedResp(_headersFromExpressLike(request.headers), opts);
4047
+ reply.code(resp.status);
4048
+ for (const [k, v] of Object.entries(resp.headers)) reply.header(k, v);
4049
+ reply.type(resp.mediaType);
4050
+ return reply.send(resp.body);
4051
+ });
4052
+ const preflight = (request, reply) => {
4053
+ const reqHeaders = _headersFromExpressLike(request.headers);
4054
+ reply.code(204);
4055
+ for (const [k, v] of Object.entries(_preflightHeaders(reqHeaders))) reply.header(k, v);
4056
+ return reply.send("");
4057
+ };
4058
+ app.options(ucpPath, preflight);
4059
+ app.options(jwksPath, preflight);
4060
+ };
863
4061
 
864
4062
  // src/identity/policy.ts
865
- function buildGateOptionsFromPolicy(policy, base) {
4063
+ function buildGateFromPolicy(policy, base) {
866
4064
  if (!policy || !policy.enforcement) return null;
867
4065
  return {
868
4066
  apiKey: base.apiKey,
@@ -907,9 +4105,73 @@ function shippingStateAllowed(state, country, policy) {
907
4105
  const allowed = new Set(policy.allowedShippingStates.map((s) => s.toUpperCase()));
908
4106
  return allowed.has(state.toUpperCase());
909
4107
  }
4108
+ function validateShippingAgainstPolicy(opts) {
4109
+ const code = opts.errorCode ?? "unsupported_jurisdiction";
4110
+ const action = opts.errorAction ?? "change_shipping_state";
4111
+ const item = opts.productName ? `'${opts.productName}'` : "this item";
4112
+ if (!shippingCountryAllowed(opts.country, opts.policy)) {
4113
+ throw new CheckoutValidationError({
4114
+ code,
4115
+ message: opts.countryMessage ?? `We can't ship ${item} to ${opts.country.toUpperCase() || "<unset>"}.`,
4116
+ action
4117
+ });
4118
+ }
4119
+ if (!shippingStateAllowed(opts.state, opts.country, opts.policy)) {
4120
+ throw new CheckoutValidationError({
4121
+ code,
4122
+ message: opts.stateMessage ?? `We can't ship ${item} to ${opts.state.toUpperCase() || "<unset>"}.`,
4123
+ action
4124
+ });
4125
+ }
4126
+ }
4127
+
4128
+ // src/identity/tokens.ts
4129
+ var import_node_crypto2 = require("crypto");
4130
+ function hashOperatorToken(plaintext) {
4131
+ return (0, import_node_crypto2.createHash)("sha256").update(plaintext, "utf8").digest("hex");
4132
+ }
4133
+
4134
+ // src/payment/index.ts
4135
+ init_directive();
4136
+ init_networks();
4137
+ init_usdc();
4138
+ init_rails();
4139
+ init_wwwauthenticate();
4140
+
4141
+ // src/payment/headers.ts
4142
+ init_directive();
4143
+ init_wwwauthenticate();
4144
+
4145
+ // src/payment/amounts.ts
4146
+ function formatUsdCents(cents) {
4147
+ return (cents / 100).toFixed(2);
4148
+ }
4149
+
4150
+ // src/payment/solana.ts
4151
+ async function loadSolanaFeePayer(opts) {
4152
+ const raw = opts.privateKey;
4153
+ if (!raw) return void 0;
4154
+ const moduleName = "@solana/kit";
4155
+ const kit = await import(moduleName).catch(() => null);
4156
+ if (!kit?.createKeyPairSignerFromPrivateKeyBytes || !kit.getBase58Codec) {
4157
+ throw new Error(
4158
+ "@solana/kit not installed \u2014 `npm install @solana/kit` for loadSolanaFeePayer."
4159
+ );
4160
+ }
4161
+ let bytes;
4162
+ if (/^[0-9a-fA-F]{128}$/.test(raw)) {
4163
+ bytes = new Uint8Array(raw.match(/.{2}/g).map((h) => parseInt(h, 16))).slice(0, 32);
4164
+ } else {
4165
+ const decoded = new Uint8Array(kit.getBase58Codec().encode(raw));
4166
+ bytes = decoded.length === 64 ? decoded.slice(0, 32) : decoded;
4167
+ }
4168
+ return kit.createKeyPairSignerFromPrivateKeyBytes(bytes);
4169
+ }
910
4170
  // Annotate the CommonJS export names for ESM import in node:
911
4171
  0 && (module.exports = {
912
4172
  AGENTSCORE_UCP_CAPABILITY,
4173
+ Checkout,
4174
+ CheckoutValidationError,
913
4175
  FIXABLE_DENIAL_REASONS,
914
4176
  UCPSigningKey,
915
4177
  UCPVerificationError,
@@ -917,16 +4179,25 @@ function shippingStateAllowed(state, country, policy) {
917
4179
  buildA2AAgentCard,
918
4180
  buildAgentMemoryHint,
919
4181
  buildContactSupportNextSteps,
920
- buildGateOptionsFromPolicy,
4182
+ buildGateFromPolicy,
921
4183
  buildJWKSResponse,
922
4184
  buildSignerMismatchBody,
923
4185
  buildUCPProfile,
924
4186
  denialReasonStatus,
925
4187
  denialReasonToBody,
926
4188
  extractPaymentSigner,
4189
+ extractPaymentSignerFromAuth,
4190
+ extractSignerForPrecheck,
4191
+ formatUsdCents,
927
4192
  generateUCPSigningKey,
4193
+ getIdentityStatus,
4194
+ hashOperatorToken,
928
4195
  isFixableDenial,
4196
+ loadSolanaFeePayer,
4197
+ loadUCPSigningKeyFromEnv,
4198
+ makeMppxComposeHook,
929
4199
  mppPaymentHandler,
4200
+ pricingResult,
930
4201
  readX402PaymentHeader,
931
4202
  runGateWithEnforcement,
932
4203
  shippingCountryAllowed,
@@ -934,6 +4205,13 @@ function shippingStateAllowed(state, country, policy) {
934
4205
  signUCPProfile,
935
4206
  stripeSptPaymentHandler,
936
4207
  ucpA2AExtension,
4208
+ validateShippingAgainstPolicy,
4209
+ validationEnvelope,
4210
+ validationResponseExpress,
4211
+ validationResponseFastify,
4212
+ validationResponseHono,
4213
+ validationResponseNextjs,
4214
+ validationResponseWeb,
937
4215
  verificationAgentInstructions,
938
4216
  verifyUCPProfile,
939
4217
  x402PaymentHandler