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