@agent-score/commerce 2.1.0 → 2.2.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 (76) hide show
  1. package/README.md +1 -1
  2. package/dist/challenge/index.d.mts +2 -2
  3. package/dist/challenge/index.d.ts +2 -2
  4. package/dist/challenge/index.js +7 -5
  5. package/dist/challenge/index.js.map +1 -1
  6. package/dist/challenge/index.mjs +7 -5
  7. package/dist/challenge/index.mjs.map +1 -1
  8. package/dist/{checkout-Bd_4aQ6c.d.mts → checkout-CKQE2QpJ.d.mts} +38 -23
  9. package/dist/{checkout-BH-I_Ns8.d.ts → checkout-CfgxgPZL.d.ts} +38 -23
  10. package/dist/core.js +4 -3
  11. package/dist/core.js.map +1 -1
  12. package/dist/core.mjs +4 -3
  13. package/dist/core.mjs.map +1 -1
  14. package/dist/{default_rails-BxBzcCA1.d.ts → default_rails-C5gKZJMI.d.ts} +11 -1
  15. package/dist/{default_rails-BWAquZeu.d.mts → default_rails-XFCuRddA.d.mts} +11 -1
  16. package/dist/discovery/index.d.mts +3 -3
  17. package/dist/discovery/index.d.ts +3 -3
  18. package/dist/identity/express.d.mts +4 -3
  19. package/dist/identity/express.d.ts +4 -3
  20. package/dist/identity/express.js +4 -3
  21. package/dist/identity/express.js.map +1 -1
  22. package/dist/identity/express.mjs +4 -3
  23. package/dist/identity/express.mjs.map +1 -1
  24. package/dist/identity/fastify.d.mts +4 -3
  25. package/dist/identity/fastify.d.ts +4 -3
  26. package/dist/identity/fastify.js +4 -3
  27. package/dist/identity/fastify.js.map +1 -1
  28. package/dist/identity/fastify.mjs +4 -3
  29. package/dist/identity/fastify.mjs.map +1 -1
  30. package/dist/identity/hono.d.mts +4 -3
  31. package/dist/identity/hono.d.ts +4 -3
  32. package/dist/identity/hono.js +4 -3
  33. package/dist/identity/hono.js.map +1 -1
  34. package/dist/identity/hono.mjs +4 -3
  35. package/dist/identity/hono.mjs.map +1 -1
  36. package/dist/identity/nextjs.js +4 -3
  37. package/dist/identity/nextjs.js.map +1 -1
  38. package/dist/identity/nextjs.mjs +4 -3
  39. package/dist/identity/nextjs.mjs.map +1 -1
  40. package/dist/identity/policy.js +22 -23363
  41. package/dist/identity/policy.js.map +1 -1
  42. package/dist/identity/policy.mjs +1 -23366
  43. package/dist/identity/policy.mjs.map +1 -1
  44. package/dist/identity/web.js +4 -3
  45. package/dist/identity/web.js.map +1 -1
  46. package/dist/identity/web.mjs +4 -3
  47. package/dist/identity/web.mjs.map +1 -1
  48. package/dist/index.d.mts +73 -6
  49. package/dist/index.d.ts +73 -6
  50. package/dist/index.js +392 -148
  51. package/dist/index.js.map +1 -1
  52. package/dist/index.mjs +389 -146
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/middleware/express.js.map +1 -1
  55. package/dist/middleware/express.mjs.map +1 -1
  56. package/dist/middleware/fastify.js.map +1 -1
  57. package/dist/middleware/fastify.mjs.map +1 -1
  58. package/dist/middleware/hono.js.map +1 -1
  59. package/dist/middleware/hono.mjs.map +1 -1
  60. package/dist/middleware/nextjs.js.map +1 -1
  61. package/dist/middleware/nextjs.mjs.map +1 -1
  62. package/dist/middleware/web.js.map +1 -1
  63. package/dist/middleware/web.mjs.map +1 -1
  64. package/dist/payment/index.d.mts +13 -17
  65. package/dist/payment/index.d.ts +13 -17
  66. package/dist/payment/index.js +80 -4
  67. package/dist/payment/index.js.map +1 -1
  68. package/dist/payment/index.mjs +79 -4
  69. package/dist/payment/index.mjs.map +1 -1
  70. package/dist/{pricing-4n5Ota0D.d.mts → pricing-dSI3ePmE.d.mts} +4 -2
  71. package/dist/{pricing-DHfH3ogG.d.ts → pricing-uFGRNoGl.d.ts} +4 -2
  72. package/dist/stripe-multichain/index.js +55 -5
  73. package/dist/stripe-multichain/index.js.map +1 -1
  74. package/dist/stripe-multichain/index.mjs +55 -5
  75. package/dist/stripe-multichain/index.mjs.map +1 -1
  76. package/package.json +8 -8
package/dist/index.mjs CHANGED
@@ -20059,11 +20059,12 @@ function toClient(method, options) {
20059
20059
  };
20060
20060
  }
20061
20061
  function toServer(method, options) {
20062
- const { authorize, defaults, html, request, respond, stableBinding, transport, verify: verify3 } = options;
20062
+ const { authorize, defaults, extensions, html, request, respond, stableBinding, transport, verify: verify3 } = options;
20063
20063
  return {
20064
20064
  ...method,
20065
20065
  authorize,
20066
20066
  defaults,
20067
+ extensions,
20067
20068
  html,
20068
20069
  request,
20069
20070
  respond,
@@ -20990,6 +20991,20 @@ var RESERVED_FIELDS = /* @__PURE__ */ new Set([
20990
20991
  "actual_signer",
20991
20992
  "linked_wallets"
20992
20993
  ]);
20994
+ function buildVerificationRequiredBody(reason, opts = {}) {
20995
+ const body = denialReasonToBody(reason);
20996
+ body.error = {
20997
+ code: "operator_verification_required",
20998
+ message: opts.message ?? "Identity verification is required."
20999
+ };
21000
+ if (opts.agentInstructions !== void 0) {
21001
+ body.agent_instructions = opts.agentInstructions;
21002
+ }
21003
+ if (opts.extra) {
21004
+ for (const [k, v] of Object.entries(opts.extra)) body[k] = v;
21005
+ }
21006
+ return body;
21007
+ }
20993
21008
  function denialReasonToBody(reason) {
20994
21009
  const message = reason.message ?? DEFAULT_MESSAGES[reason.code];
20995
21010
  const body = { error: { code: reason.code, message } };
@@ -21147,7 +21162,7 @@ function createAgentScoreCore(options) {
21147
21162
  } = options;
21148
21163
  const baseUrl = stripTrailingSlashes(rawBaseUrl);
21149
21164
  const agentMemoryHint = buildAgentMemoryHint();
21150
- const defaultUa = `@agent-score/commerce@${"2.1.0"}`;
21165
+ const defaultUa = `@agent-score/commerce@${"2.2.0"}`;
21151
21166
  const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
21152
21167
  const sdk = new AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
21153
21168
  const sessionSdkCache = /* @__PURE__ */ new Map();
@@ -21284,8 +21299,9 @@ function createAgentScoreCore(options) {
21284
21299
  ...Object.keys(policy).length > 0 ? { policy } : {},
21285
21300
  // Pre-extracted payment signer (by the adapter middleware). When present, the API
21286
21301
  // composes BOTH signer_match (wallet-binding) and signer_sanctions (OFAC SDN wallet
21287
- // check) verdicts on the response in one round trip. Under
21288
- // policy.require_sanctions_clear, a signer_sanctions hit flips decision -> deny inline.
21302
+ // check) verdicts on the response in one round trip. Wallet-OFAC enforcement on the
21303
+ // signer block is unconditional — a signer_sanctions hit flips decision -> deny
21304
+ // regardless of policy.require_sanctions_clear (which gates the separate NAME screen).
21289
21305
  ...signer && { signer: { address: signer.address, network: signer.network } }
21290
21306
  };
21291
21307
  const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
@@ -21605,6 +21621,137 @@ var A2A_DEFAULT_TRANSPORT = DEFAULT_TRANSPORT;
21605
21621
  init_ucp();
21606
21622
  init_ucp_jwks();
21607
21623
 
21624
+ // src/errors.ts
21625
+ var CheckoutValidationError = class extends Error {
21626
+ code;
21627
+ action;
21628
+ status;
21629
+ extra;
21630
+ constructor(opts) {
21631
+ super(opts.message);
21632
+ this.name = "CheckoutValidationError";
21633
+ this.code = opts.code;
21634
+ this.action = opts.action ?? "fix_request";
21635
+ this.status = opts.status ?? 400;
21636
+ this.extra = opts.extra;
21637
+ }
21638
+ };
21639
+
21640
+ // src/identity/policy.ts
21641
+ function buildGateFromPolicy(policy, base) {
21642
+ if (!policy || !policy.enforcement) return null;
21643
+ return {
21644
+ apiKey: base.apiKey,
21645
+ ...base.baseUrl !== void 0 && { baseUrl: base.baseUrl },
21646
+ ...policy.requireKyc !== void 0 && { requireKyc: policy.requireKyc },
21647
+ ...policy.requireSanctionsClear !== void 0 && {
21648
+ requireSanctionsClear: policy.requireSanctionsClear
21649
+ },
21650
+ ...policy.minAge !== void 0 && { minAge: policy.minAge },
21651
+ ...policy.allowedJurisdictions !== void 0 && {
21652
+ allowedJurisdictions: [...policy.allowedJurisdictions]
21653
+ }
21654
+ };
21655
+ }
21656
+ async function runGateWithEnforcement(enforcement, runGate) {
21657
+ if (!runGate || !enforcement) return { status: "anonymous" };
21658
+ const outcome = await runGate();
21659
+ if (outcome.ok) return { status: "verified" };
21660
+ if (enforcement === "hard") {
21661
+ return {
21662
+ status: "denied",
21663
+ denialStatus: outcome.status,
21664
+ denialBody: outcome.body,
21665
+ ...outcome.reason !== void 0 && { denialReason: outcome.reason }
21666
+ };
21667
+ }
21668
+ return {
21669
+ status: "unverified",
21670
+ denialStatus: outcome.status,
21671
+ denialBody: outcome.body,
21672
+ ...outcome.reason !== void 0 && { denialReason: outcome.reason }
21673
+ };
21674
+ }
21675
+ function shippingCountryAllowed(country, policy) {
21676
+ if (!policy?.allowedShippingCountries || policy.allowedShippingCountries.length === 0) return true;
21677
+ const allowed = new Set(policy.allowedShippingCountries.map((c) => c.toUpperCase()));
21678
+ return allowed.has(country.toUpperCase());
21679
+ }
21680
+ function shippingStateAllowed(state, country, policy) {
21681
+ if (!policy?.allowedShippingStates || policy.allowedShippingStates.length === 0) return true;
21682
+ if (country.toUpperCase() !== "US") return true;
21683
+ const allowed = new Set(policy.allowedShippingStates.map((s) => s.toUpperCase()));
21684
+ return allowed.has(state.toUpperCase());
21685
+ }
21686
+ function validateShippingAgainstPolicy(opts) {
21687
+ const code = opts.errorCode ?? "unsupported_jurisdiction";
21688
+ const action = opts.errorAction ?? "change_shipping_state";
21689
+ const item = opts.productName ? `'${opts.productName}'` : "this item";
21690
+ if (!shippingCountryAllowed(opts.country, opts.policy)) {
21691
+ throw new CheckoutValidationError({
21692
+ code,
21693
+ message: opts.countryMessage ?? `We can't ship ${item} to ${opts.country.toUpperCase() || "<unset>"}.`,
21694
+ action
21695
+ });
21696
+ }
21697
+ if (!shippingStateAllowed(opts.state, opts.country, opts.policy)) {
21698
+ throw new CheckoutValidationError({
21699
+ code,
21700
+ message: opts.stateMessage ?? `We can't ship ${item} to ${opts.state.toUpperCase() || "<unset>"}.`,
21701
+ action
21702
+ });
21703
+ }
21704
+ }
21705
+
21706
+ // src/identity/tokens.ts
21707
+ import { createHash } from "crypto";
21708
+
21709
+ // src/payment/payment_header.ts
21710
+ function toTitleCase(name) {
21711
+ return name.replace(/(^|-)([a-z])/g, (_m, sep, c) => sep + c.toUpperCase());
21712
+ }
21713
+ function readHeader(headers, name) {
21714
+ if (typeof headers.get === "function") {
21715
+ return headers.get(name);
21716
+ }
21717
+ const rec = headers;
21718
+ const v = rec[name] ?? rec[name.toLowerCase()] ?? rec[toTitleCase(name)];
21719
+ if (typeof v === "string") return v;
21720
+ if (Array.isArray(v) && typeof v[0] === "string") return v[0];
21721
+ return null;
21722
+ }
21723
+ function asHeaders(input) {
21724
+ return typeof input.headers === "object" && input instanceof Request ? input.headers : input;
21725
+ }
21726
+ function hasPaymentHeader(input) {
21727
+ const headers = asHeaders(input);
21728
+ return Boolean(
21729
+ readHeader(headers, "payment-signature") || readHeader(headers, "x-payment") || readHeader(headers, "authorization")?.startsWith("Payment ")
21730
+ );
21731
+ }
21732
+ function hasX402Header(input) {
21733
+ const headers = asHeaders(input);
21734
+ return Boolean(readHeader(headers, "payment-signature") || readHeader(headers, "x-payment"));
21735
+ }
21736
+ function hasMppxHeader(input) {
21737
+ const headers = asHeaders(input);
21738
+ return Boolean(readHeader(headers, "authorization")?.startsWith("Payment "));
21739
+ }
21740
+
21741
+ // src/identity/tokens.ts
21742
+ function hashOperatorToken(plaintext) {
21743
+ return createHash("sha256").update(plaintext, "utf8").digest("hex");
21744
+ }
21745
+ function extractOwnerScope(input) {
21746
+ const headers = asHeaders(input);
21747
+ const walletAddress = readHeader(headers, "x-wallet-address");
21748
+ const operatorToken = readHeader(headers, "x-operator-token");
21749
+ return {
21750
+ ...walletAddress ? { walletAddress } : {},
21751
+ ...operatorToken ? { operatorTokenHash: hashOperatorToken(operatorToken) } : {}
21752
+ };
21753
+ }
21754
+
21608
21755
  // src/checkout.ts
21609
21756
  import { randomUUID } from "crypto";
21610
21757
 
@@ -21636,6 +21783,16 @@ async function deriveMppxReceiptMethod(raw) {
21636
21783
  return extractMppxReceiptMethod(header);
21637
21784
  }
21638
21785
 
21786
+ // src/_warnings.ts
21787
+ var warnedNoApiKey = false;
21788
+ function warnMissingApiKeyOnce(label) {
21789
+ if (warnedNoApiKey) return;
21790
+ warnedNoApiKey = true;
21791
+ console.warn(
21792
+ `[${label}] AGENTSCORE_API_KEY is not set \u2014 wallet OFAC SDN sanctions are NOT being enforced. Set the env var to enable strict-liability protection on settle.`
21793
+ );
21794
+ }
21795
+
21639
21796
  // src/payment/rail_spec.ts
21640
21797
  init_usdc();
21641
21798
  async function resolveRecipient(r) {
@@ -21867,14 +22024,16 @@ function buildHowToPay({
21867
22024
  const d = decimals ?? 2;
21868
22025
  const defaultMaxSpend = totalNum >= 1 ? (Math.ceil(totalNum) + 1).toFixed(d) : totalNum.toFixed(d);
21869
22026
  const maxSpendStr = String(maxSpend ?? defaultMaxSpend);
22027
+ const gateless = opTokenPlaceholder === null;
21870
22028
  const opToken = opTokenPlaceholder ?? "<your_opc_token>";
22029
+ const opTokenHeaderFlag = gateless ? "" : `-H 'X-Operator-Token: ${opToken}' `;
21871
22030
  const block = {};
21872
22031
  if (rails2.tempo) {
21873
22032
  const networkName = rails2.tempo.testnet ? "tempo-testnet" : rails2.tempo.network ?? RAIL_SPEC_DEFAULTS.tempo.network;
21874
22033
  const chainId = rails2.tempo.chainId ?? RAIL_SPEC_DEFAULTS.tempo.chainId;
21875
22034
  const recommend = rails2.tempo.recommend ?? RAIL_SPEC_DEFAULTS.tempo.recommend;
21876
- const tempoCommand = `tempo request -X POST -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' --json '${retryBodyJson}' --max-spend ${maxSpendStr} ${url2}`;
21877
- const payCommand = `agentscore-pay pay POST ${url2} --chain tempo -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`;
22035
+ const tempoCommand = `tempo request -X POST ${opTokenHeaderFlag}-H 'Content-Type: application/json' --json '${retryBodyJson}' --max-spend ${maxSpendStr} ${url2}`;
22036
+ const payCommand = `agentscore-pay pay POST ${url2} --chain tempo ${opTokenHeaderFlag}-H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`;
21878
22037
  block.tempo = {
21879
22038
  setup: TEMPO_SETUP,
21880
22039
  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.`,
@@ -21888,7 +22047,7 @@ function buildHowToPay({
21888
22047
  block.x402_base = {
21889
22048
  setup: PAY_SETUP_BASE,
21890
22049
  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.`,
21891
- command: `agentscore-pay pay POST ${url2} --chain base -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`,
22050
+ command: `agentscore-pay pay POST ${url2} --chain base ${opTokenHeaderFlag}-H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`,
21892
22051
  what_it_does: "Pays via USDC on Base."
21893
22052
  };
21894
22053
  }
@@ -21897,7 +22056,7 @@ function buildHowToPay({
21897
22056
  block.solana_mpp = {
21898
22057
  setup: PAY_SETUP_SOLANA,
21899
22058
  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.`,
21900
- command: `agentscore-pay pay POST ${url2} --chain solana -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`,
22059
+ command: `agentscore-pay pay POST ${url2} --chain solana ${opTokenHeaderFlag}-H 'Content-Type: application/json' -d '${retryBodyJson}' --max-spend ${maxSpendStr}`,
21901
22060
  what_it_does: "Pays via USDC on Solana."
21902
22061
  };
21903
22062
  }
@@ -21919,7 +22078,7 @@ function buildHowToPay({
21919
22078
  ];
21920
22079
  stripe.command_link_cli = [
21921
22080
  `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)`,
21922
- `link-cli mpp pay ${url2} --spend-request-id $SPEND_ID --method POST --data '${retryBodyJson}' --header 'X-Operator-Token: ${opToken}' --output-json`
22081
+ gateless ? `link-cli mpp pay ${url2} --spend-request-id $SPEND_ID --method POST --data '${retryBodyJson}' --output-json` : `link-cli mpp pay ${url2} --spend-request-id $SPEND_ID --method POST --data '${retryBodyJson}' --header 'X-Operator-Token: ${opToken}' --output-json`
21923
22082
  ];
21924
22083
  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.";
21925
22084
  } else if (linkCliBlocked) {
@@ -22014,6 +22173,12 @@ function buildValidationError({
22014
22173
  return body;
22015
22174
  }
22016
22175
 
22176
+ // src/payment/constants.ts
22177
+ var STRIPE_MIN_CHARGE_USD = 0.5;
22178
+
22179
+ // src/payment/mppx_server.ts
22180
+ import { AsyncLocalStorage } from "async_hooks";
22181
+
22017
22182
  // src/stripe-multichain/mppx_stripe.ts
22018
22183
  async function createMppxStripe({
22019
22184
  profileId,
@@ -22194,6 +22359,31 @@ function wrapSolanaChargeWithFinalizedBlockhash(baseMethod, rpcUrl) {
22194
22359
  }
22195
22360
  };
22196
22361
  }
22362
+ var mppxCapture = new AsyncLocalStorage();
22363
+ var consoleErrorPatched = false;
22364
+ function ensureConsoleErrorPatch() {
22365
+ if (consoleErrorPatched) return;
22366
+ consoleErrorPatched = true;
22367
+ const original = console.error.bind(console);
22368
+ console.error = function captureMppxInternal(...args) {
22369
+ if (args[0] === "mppx: internal verification error" && args[1] !== void 0) {
22370
+ const ctx = mppxCapture.getStore();
22371
+ if (ctx) {
22372
+ const e = args[1];
22373
+ const reason = typeof e?.shortMessage === "string" ? e.shortMessage : typeof e?.message === "string" ? e.message : String(args[1]);
22374
+ const details = e?.details;
22375
+ ctx.reason = typeof details === "string" && details.length > 0 ? `${reason} (${details})` : reason;
22376
+ }
22377
+ }
22378
+ return original(...args);
22379
+ };
22380
+ }
22381
+ async function runWithMppxFailureCapture(fn) {
22382
+ ensureConsoleErrorPatch();
22383
+ const ctx = { reason: null };
22384
+ const result = await mppxCapture.run(ctx, fn);
22385
+ return { result, failureReason: ctx.reason };
22386
+ }
22197
22387
 
22198
22388
  // src/payment/x402_server.ts
22199
22389
  init_networks();
@@ -22335,40 +22525,28 @@ function lazyMppxServer(opts) {
22335
22525
  };
22336
22526
  }
22337
22527
 
22338
- // src/checkout.ts
22339
- init_network_kind();
22340
-
22341
- // src/payment/payment_header.ts
22342
- function toTitleCase(name) {
22343
- return name.replace(/(^|-)([a-z])/g, (_m, sep, c) => sep + c.toUpperCase());
22344
- }
22345
- function readHeader(headers, name) {
22346
- if (typeof headers.get === "function") {
22347
- return headers.get(name);
22528
+ // src/payment/mppx_failures.ts
22529
+ var TEMPO_KEY_NOT_REGISTERED = {
22530
+ code: "tempo_key_not_registered",
22531
+ status: 401,
22532
+ message: "Tempo rejected the transaction: signer wallet is not registered with Tempo's keychain.",
22533
+ nextSteps: {
22534
+ action: "register_tempo_key",
22535
+ user_message: "Your wallet is not enrolled with Tempo. Run `tempo wallet login` to complete the one-time WebAuthn enrollment (or use `tempo request` directly), then retry. To skip enrollment, switch to the Base or Solana rail on this 402."
22536
+ },
22537
+ extra: { upstream_error: "KeyNotFound", chain: "tempo" }
22538
+ };
22539
+ function classifyMppxFailure(reason) {
22540
+ if (!reason) return null;
22541
+ const lower = reason.toLowerCase();
22542
+ if (lower.includes("keychain validation failed") || lower.includes("keynotfound")) {
22543
+ return TEMPO_KEY_NOT_REGISTERED;
22348
22544
  }
22349
- const rec = headers;
22350
- const v = rec[name] ?? rec[name.toLowerCase()] ?? rec[toTitleCase(name)];
22351
- if (typeof v === "string") return v;
22352
- if (Array.isArray(v) && typeof v[0] === "string") return v[0];
22353
22545
  return null;
22354
22546
  }
22355
- function asHeaders(input) {
22356
- return typeof input.headers === "object" && input instanceof Request ? input.headers : input;
22357
- }
22358
- function hasPaymentHeader(input) {
22359
- const headers = asHeaders(input);
22360
- return Boolean(
22361
- readHeader(headers, "payment-signature") || readHeader(headers, "x-payment") || readHeader(headers, "authorization")?.startsWith("Payment ")
22362
- );
22363
- }
22364
- function hasX402Header(input) {
22365
- const headers = asHeaders(input);
22366
- return Boolean(readHeader(headers, "payment-signature") || readHeader(headers, "x-payment"));
22367
- }
22368
- function hasMppxHeader(input) {
22369
- const headers = asHeaders(input);
22370
- return Boolean(readHeader(headers, "authorization")?.startsWith("Payment "));
22371
- }
22547
+
22548
+ // src/checkout.ts
22549
+ init_network_kind();
22372
22550
 
22373
22551
  // src/payment/x402_settle.ts
22374
22552
  function classifyX402SettleResult(result) {
@@ -22691,20 +22869,6 @@ function getIdentityStatus(ctx) {
22691
22869
  if (decision === "allow") return "verified";
22692
22870
  return "unverified";
22693
22871
  }
22694
- var CheckoutValidationError = class extends Error {
22695
- code;
22696
- action;
22697
- status;
22698
- extra;
22699
- constructor(opts) {
22700
- super(opts.message);
22701
- this.name = "CheckoutValidationError";
22702
- this.code = opts.code;
22703
- this.action = opts.action ?? "fix_request";
22704
- this.status = opts.status ?? 400;
22705
- this.extra = opts.extra;
22706
- }
22707
- };
22708
22872
  function resolveIdentityMetadata(ctx) {
22709
22873
  const h = normalizeHeadersToLowercase(ctx.request.headers);
22710
22874
  const wallet = h["x-wallet-address"];
@@ -22831,6 +22995,23 @@ var Checkout = class {
22831
22995
  discoveryExtensions;
22832
22996
  discoveryProbe;
22833
22997
  _x402ServerGetter;
22998
+ /**
22999
+ * True when the merchant has configured an identity-bearing policy flag —
23000
+ * `require_kyc`, `require_sanctions_clear` (name screening on the KYC
23001
+ * identity), `min_age`, or jurisdiction lists. Wallet OFAC SDN enforcement
23002
+ * (the always-on default) does NOT count as an identity gate; agents don't
23003
+ * need an AgentScore credential to satisfy it.
23004
+ *
23005
+ * Used to conditionally emit AgentScore identity boilerplate in 402 bodies
23006
+ * (`agent_memory`, `X-Operator-Token` references in per-rail commands).
23007
+ */
23008
+ hasIdentityGate() {
23009
+ const g = this.gate;
23010
+ if (!g) return false;
23011
+ return Boolean(
23012
+ g.requireKyc || g.requireSanctionsClear || g.minAge !== void 0 || g.allowedJurisdictions && g.allowedJurisdictions.length > 0 || g.blockedJurisdictions && g.blockedJurisdictions.length > 0
23013
+ );
23014
+ }
22834
23015
  constructor(opts) {
22835
23016
  const x402Server = opts.x402Server;
22836
23017
  let x402ServerGetter;
@@ -23028,8 +23209,8 @@ var Checkout = class {
23028
23209
  }
23029
23210
  }
23030
23211
  const hasPaymentHeader2 = hasX402Header(request.headers) || hasMppxHeader(request.headers);
23031
- if (this.gate !== void 0 && hasPaymentHeader2) {
23032
- const denial = await this.runGate(ctx);
23212
+ if (hasPaymentHeader2) {
23213
+ const denial = this.gate !== void 0 ? await this.runGate(ctx) : await this.runWalletSanctionsOnly(ctx);
23033
23214
  if (denial !== null) {
23034
23215
  return {
23035
23216
  status: denial.status,
@@ -23041,7 +23222,14 @@ var Checkout = class {
23041
23222
  }
23042
23223
  }
23043
23224
  ctx.pricing = await this.computePricing(ctx);
23044
- await this.resolveRecipientsForCtx(ctx);
23225
+ try {
23226
+ await this.resolveRecipientsForCtx(ctx);
23227
+ } catch (err) {
23228
+ if (err instanceof Error && err.name === "CheckoutValidationError") {
23229
+ return this.validationErrorResult(ctx, err);
23230
+ }
23231
+ throw err;
23232
+ }
23045
23233
  const x402ServerOk = this.x402ServerAvailable() && this.x402BaseNetwork !== null;
23046
23234
  if (hasX402Header(request.headers) && x402ServerOk) {
23047
23235
  const zero = await this.handleZeroSettle(ctx, "x402-base");
@@ -23094,7 +23282,9 @@ var Checkout = class {
23094
23282
  }
23095
23283
  return result;
23096
23284
  }
23097
- if (gate.apiKey === void 0) return null;
23285
+ if (gate.apiKey === void 0) {
23286
+ return this.runWalletSanctionsOnly(ctx);
23287
+ }
23098
23288
  let policyOverride;
23099
23289
  if (gate.perRequestPolicy !== void 0) {
23100
23290
  policyOverride = await gate.perRequestPolicy(ctx);
@@ -23112,8 +23302,16 @@ var Checkout = class {
23112
23302
  ...gate.failOpen !== void 0 && { failOpen: gate.failOpen },
23113
23303
  ...gate.cacheSeconds !== void 0 && { cacheSeconds: gate.cacheSeconds },
23114
23304
  ...gate.chain !== void 0 && { chain: gate.chain },
23115
- ...gate.createSessionOnMissing !== void 0 && {
23116
- createSessionOnMissing: gate.createSessionOnMissing
23305
+ // Auto-default `createSessionOnMissing` from gate config when the merchant
23306
+ // didn't supply one. Matches python-commerce's behavior — every gated route
23307
+ // gets the bootstrap session-mint UX out of the box. Merchants who need
23308
+ // custom session context or onBeforeSession side effects (goods merchants
23309
+ // pre-minting an order_id) supply their own config instead.
23310
+ createSessionOnMissing: gate.createSessionOnMissing ?? {
23311
+ apiKey: gate.apiKey,
23312
+ ...gate.baseUrl !== void 0 && { baseUrl: gate.baseUrl },
23313
+ ...gate.context !== void 0 && { context: gate.context },
23314
+ ...gate.merchantName !== void 0 && { productName: gate.merchantName }
23117
23315
  },
23118
23316
  ...policyOverride ?? {}
23119
23317
  };
@@ -23176,6 +23374,58 @@ var Checkout = class {
23176
23374
  const status = reason.code === "token_expired" || reason.code === "invalid_credential" ? 401 : reason.code === "api_error" ? 503 : 403;
23177
23375
  return { status, body };
23178
23376
  }
23377
+ /**
23378
+ * Wallet OFAC SDN enforcement.
23379
+ *
23380
+ * Runs on settle (payment header present) when either `this.gate` is
23381
+ * undefined OR a gate is configured but has no `apiKey` to reach
23382
+ * `/v1/assess` for full policy enforcement (fallback to the
23383
+ * strict-liability default).
23384
+ *
23385
+ * Env knobs:
23386
+ * - `AGENTSCORE_API_KEY` — required. No key → one-time warning + skip
23387
+ * (dev/testnet pattern; production should always configure a key).
23388
+ * - `AGENTSCORE_BASE_URL` — optional override for staging/dev API
23389
+ * (e.g. `https://api-dev.agentscore.sh` or `http://localhost:3002`).
23390
+ *
23391
+ * Stripe SPT (no extractable wallet signer) → skip silently; Stripe runs
23392
+ * its own OFAC screen on the buyer's Stripe account at customer creation.
23393
+ *
23394
+ * Calls `/v1/assess` with the signer wallet as both the primary address
23395
+ * and the signer block. The API enforces signer-sanctions unconditionally
23396
+ * when a signer is present (no policy flag needed). Denies on OFAC SDN
23397
+ * hit; fail-closed on unavailable lookup (strict liability — falsely
23398
+ * allowing a sanctioned settle is an OFAC violation, falsely denying a
23399
+ * clean buyer is just bad UX).
23400
+ */
23401
+ async runWalletSanctionsOnly(ctx) {
23402
+ const apiKey = process.env.AGENTSCORE_API_KEY;
23403
+ if (!apiKey) {
23404
+ warnMissingApiKeyOnce("checkout");
23405
+ return null;
23406
+ }
23407
+ const headers = normalizeHeadersToLowercase(ctx.request.headers);
23408
+ const x402Header = headers["payment-signature"] ?? headers["x-payment"];
23409
+ const signer = await extractPaymentSignerFromAuth(headers["authorization"], x402Header);
23410
+ if (!signer) {
23411
+ return null;
23412
+ }
23413
+ const baseUrl = process.env.AGENTSCORE_BASE_URL;
23414
+ const core = createAgentScoreCore({
23415
+ apiKey,
23416
+ ...baseUrl !== void 0 && { baseUrl }
23417
+ });
23418
+ const outcome = await core.evaluate(
23419
+ { address: signer.address },
23420
+ ctx,
23421
+ signer
23422
+ );
23423
+ if (outcome.kind === "allow") return null;
23424
+ const reason = outcome.reason;
23425
+ const body = denialReasonToBody(reason);
23426
+ const status = reason.code === "token_expired" || reason.code === "invalid_credential" ? 401 : reason.code === "api_error" ? 503 : 403;
23427
+ return { status, body };
23428
+ }
23179
23429
  async handleZeroSettle(ctx, rail) {
23180
23430
  if (!this.zeroSettleCarveOut || ctx.pricing === null) return null;
23181
23431
  const cents = Math.round(ctx.pricing.amountUsd * 100);
@@ -23313,7 +23563,9 @@ var Checkout = class {
23313
23563
  if (this.composeMppx === void 0) {
23314
23564
  throw new Error("Checkout.handleMppx: composeMppx hook not configured");
23315
23565
  }
23316
- const composed = await this.composeMppx(ctx);
23566
+ const { result: composed, failureReason } = await runWithMppxFailureCapture(
23567
+ async () => this.composeMppx(ctx)
23568
+ );
23317
23569
  if (composed.status === 200) {
23318
23570
  const paymentReceiptHeader = composed.paymentReceiptHeader ?? extractMppxReceiptHeaderFromRaw(composed.raw);
23319
23571
  const directMethod = composed.raw?.receipt?.method;
@@ -23332,6 +23584,22 @@ var Checkout = class {
23332
23584
  };
23333
23585
  return await this.buildSuccess(ctx, outcome);
23334
23586
  }
23587
+ const classified = classifyMppxFailure(failureReason);
23588
+ if (classified !== null) {
23589
+ return {
23590
+ status: classified.status,
23591
+ body: buildValidationError({
23592
+ code: classified.code,
23593
+ message: classified.message,
23594
+ nextSteps: classified.nextSteps,
23595
+ ...classified.extra && { extra: classified.extra }
23596
+ }),
23597
+ headers: { ...composed.headers ?? {} },
23598
+ referenceId: ctx.referenceId,
23599
+ settled: false,
23600
+ settlePhase: "verify_failed"
23601
+ };
23602
+ }
23335
23603
  return {
23336
23604
  status: 400,
23337
23605
  body: buildValidationError({
@@ -23350,7 +23618,11 @@ var Checkout = class {
23350
23618
  throw new Error("Checkout.emit402: pricing not computed");
23351
23619
  }
23352
23620
  await this.resolveRecipientsForCtx(ctx);
23353
- const emitRails = applyRecipientOverrides(this.rails, ctx.recipients);
23621
+ let emitRails = applyRecipientOverrides(this.rails, ctx.recipients);
23622
+ if (ctx.pricing.amountUsd < STRIPE_MIN_CHARGE_USD && emitRails.stripe !== void 0) {
23623
+ const { stripe: _stripe, ...rest } = emitRails;
23624
+ emitRails = rest;
23625
+ }
23354
23626
  const accepted = await buildAcceptedMethods({
23355
23627
  tempo: pickRail(emitRails, "tempo"),
23356
23628
  x402_base: pickRail(emitRails, "x402_base"),
@@ -23369,6 +23641,10 @@ var Checkout = class {
23369
23641
  retryBodyJson: JSON.stringify(ctx.request.body),
23370
23642
  totalUsd: ctx.pricing.amountUsd.toFixed(pricingDecimals),
23371
23643
  rails: howToPayRails,
23644
+ // Merchants without an identity-bearing policy flag get clean commands
23645
+ // without an X-Operator-Token header — agents don't need one to satisfy
23646
+ // wallet OFAC enforcement (the always-on default).
23647
+ ...this.hasIdentityGate() ? {} : { opTokenPlaceholder: null },
23372
23648
  ...ctx.pricing.decimals !== void 0 && { decimals: ctx.pricing.decimals }
23373
23649
  });
23374
23650
  const pricingBlock = ctx.pricing.block ?? buildPricingBlock({
@@ -23405,7 +23681,10 @@ var Checkout = class {
23405
23681
  pricing: pricingBlock,
23406
23682
  amountUsd: ctx.pricing.amountUsd.toFixed(pricingDecimals),
23407
23683
  retryBody: ctx.request.body,
23408
- agentMemory: firstEncounterAgentMemory({ firstEncounter: true }),
23684
+ // Merchants without an identity-bearing gate get a clean 402: no
23685
+ // AgentScore-identity bootstrap describing a verification flow they
23686
+ // don't run. Wallet OFAC (the always-on default) doesn't need it.
23687
+ agentMemory: firstEncounterAgentMemory({ firstEncounter: this.hasIdentityGate() }),
23409
23688
  ...ctx.pricing.product ? { product: ctx.pricing.product } : {},
23410
23689
  ...ctx.pricing.bodyExtras ? { extra: ctx.pricing.bodyExtras } : {},
23411
23690
  ...x402Accepts.length > 0 ? {
@@ -23753,87 +24032,6 @@ Checkout.prototype.mountUcpRoutesFastify = function(app, opts) {
23753
24032
  app.options(jwksPath, preflight);
23754
24033
  };
23755
24034
 
23756
- // src/identity/policy.ts
23757
- function buildGateFromPolicy(policy, base) {
23758
- if (!policy || !policy.enforcement) return null;
23759
- return {
23760
- apiKey: base.apiKey,
23761
- ...base.baseUrl !== void 0 && { baseUrl: base.baseUrl },
23762
- ...policy.requireKyc !== void 0 && { requireKyc: policy.requireKyc },
23763
- ...policy.requireSanctionsClear !== void 0 && {
23764
- requireSanctionsClear: policy.requireSanctionsClear
23765
- },
23766
- ...policy.minAge !== void 0 && { minAge: policy.minAge },
23767
- ...policy.allowedJurisdictions !== void 0 && {
23768
- allowedJurisdictions: [...policy.allowedJurisdictions]
23769
- }
23770
- };
23771
- }
23772
- async function runGateWithEnforcement(enforcement, runGate) {
23773
- if (!runGate || !enforcement) return { status: "anonymous" };
23774
- const outcome = await runGate();
23775
- if (outcome.ok) return { status: "verified" };
23776
- if (enforcement === "hard") {
23777
- return {
23778
- status: "denied",
23779
- denialStatus: outcome.status,
23780
- denialBody: outcome.body,
23781
- ...outcome.reason !== void 0 && { denialReason: outcome.reason }
23782
- };
23783
- }
23784
- return {
23785
- status: "unverified",
23786
- denialStatus: outcome.status,
23787
- denialBody: outcome.body,
23788
- ...outcome.reason !== void 0 && { denialReason: outcome.reason }
23789
- };
23790
- }
23791
- function shippingCountryAllowed(country, policy) {
23792
- if (!policy?.allowedShippingCountries || policy.allowedShippingCountries.length === 0) return true;
23793
- const allowed = new Set(policy.allowedShippingCountries.map((c) => c.toUpperCase()));
23794
- return allowed.has(country.toUpperCase());
23795
- }
23796
- function shippingStateAllowed(state, country, policy) {
23797
- if (!policy?.allowedShippingStates || policy.allowedShippingStates.length === 0) return true;
23798
- if (country.toUpperCase() !== "US") return true;
23799
- const allowed = new Set(policy.allowedShippingStates.map((s) => s.toUpperCase()));
23800
- return allowed.has(state.toUpperCase());
23801
- }
23802
- function validateShippingAgainstPolicy(opts) {
23803
- const code = opts.errorCode ?? "unsupported_jurisdiction";
23804
- const action = opts.errorAction ?? "change_shipping_state";
23805
- const item = opts.productName ? `'${opts.productName}'` : "this item";
23806
- if (!shippingCountryAllowed(opts.country, opts.policy)) {
23807
- throw new CheckoutValidationError({
23808
- code,
23809
- message: opts.countryMessage ?? `We can't ship ${item} to ${opts.country.toUpperCase() || "<unset>"}.`,
23810
- action
23811
- });
23812
- }
23813
- if (!shippingStateAllowed(opts.state, opts.country, opts.policy)) {
23814
- throw new CheckoutValidationError({
23815
- code,
23816
- message: opts.stateMessage ?? `We can't ship ${item} to ${opts.state.toUpperCase() || "<unset>"}.`,
23817
- action
23818
- });
23819
- }
23820
- }
23821
-
23822
- // src/identity/tokens.ts
23823
- import { createHash } from "crypto";
23824
- function hashOperatorToken(plaintext) {
23825
- return createHash("sha256").update(plaintext, "utf8").digest("hex");
23826
- }
23827
- function extractOwnerScope(input) {
23828
- const headers = asHeaders(input);
23829
- const walletAddress = readHeader(headers, "x-wallet-address");
23830
- const operatorToken = readHeader(headers, "x-operator-token");
23831
- return {
23832
- ...walletAddress ? { walletAddress } : {},
23833
- ...operatorToken ? { operatorTokenHash: hashOperatorToken(operatorToken) } : {}
23834
- };
23835
- }
23836
-
23837
24035
  // src/payment/index.ts
23838
24036
  init_directive();
23839
24037
  init_networks();
@@ -23910,6 +24108,7 @@ async function loadSolanaFeePayer(opts) {
23910
24108
 
23911
24109
  // src/payment/compose_rails.ts
23912
24110
  init_usdc();
24111
+ var warnedStripeBelowMinimum = false;
23913
24112
  function buildMppxComposeRails(opts) {
23914
24113
  const rails2 = [];
23915
24114
  if (opts.tempoRecipient) {
@@ -23931,7 +24130,17 @@ function buildMppxComposeRails(opts) {
23931
24130
  }]);
23932
24131
  }
23933
24132
  if (opts.includeStripe !== false) {
23934
- rails2.push(["stripe/charge", { amount: opts.amountUsd, currency: "usd", decimals: 2 }]);
24133
+ const amountUsdNumeric = Number(opts.amountUsd);
24134
+ if (Number.isFinite(amountUsdNumeric) && amountUsdNumeric < STRIPE_MIN_CHARGE_USD) {
24135
+ if (!warnedStripeBelowMinimum) {
24136
+ warnedStripeBelowMinimum = true;
24137
+ console.warn(
24138
+ `[buildMppxComposeRails] Dropping stripe/charge rail: amountUsd=${opts.amountUsd} is below Stripe's $${STRIPE_MIN_CHARGE_USD.toFixed(2)} USD minimum. Stripe's fixed ~$0.30 fee makes sub-50-cent charges unprofitable (and many accounts reject PI creation with amount_too_small below this floor). Pass includeStripe: false to suppress this warning.`
24139
+ );
24140
+ }
24141
+ } else {
24142
+ rails2.push(["stripe/charge", { amount: opts.amountUsd, currency: "usd", decimals: 2 }]);
24143
+ }
23935
24144
  }
23936
24145
  return rails2;
23937
24146
  }
@@ -24122,10 +24331,11 @@ function computeFirstCheckout(opts) {
24122
24331
  const tempoRecipient = recipients.tempo;
24123
24332
  const x402BaseRecipient = recipients.x402_base;
24124
24333
  const solanaRecipient = recipients.solana_mpp;
24334
+ const includeStripeInDiscovery = opts.rails.stripe !== void 0 && Number(totalUsd) >= STRIPE_MIN_CHARGE_USD;
24125
24335
  const accepted = await buildAcceptedMethods({
24126
24336
  ...tempoRecipient && opts.rails.tempo && { tempo: { ...opts.rails.tempo, recipient: tempoRecipient } },
24127
24337
  ...solanaRecipient && opts.rails.solana_mpp && { solana_mpp: { ...opts.rails.solana_mpp, recipient: solanaRecipient } },
24128
- ...opts.rails.stripe && { stripe: opts.rails.stripe }
24338
+ ...includeStripeInDiscovery && { stripe: opts.rails.stripe }
24129
24339
  });
24130
24340
  if (x402BaseRecipient && opts.rails.x402_base) {
24131
24341
  try {
@@ -24202,6 +24412,36 @@ function computeFirstCheckout(opts) {
24202
24412
  );
24203
24413
  return new Response(JSON.stringify(body402), { status: 402, headers });
24204
24414
  }
24415
+ async function enforceWalletSanctions(req, referenceId) {
24416
+ const apiKey = process.env.AGENTSCORE_API_KEY;
24417
+ if (!apiKey) {
24418
+ warnMissingApiKeyOnce(`${opts.name}.computeFirst`);
24419
+ return null;
24420
+ }
24421
+ const x402Header = readX402PaymentHeader(req);
24422
+ const signer = await extractPaymentSigner(req, x402Header);
24423
+ if (!signer) return null;
24424
+ const baseUrl = process.env.AGENTSCORE_BASE_URL;
24425
+ const core = createAgentScoreCore({
24426
+ apiKey,
24427
+ ...baseUrl !== void 0 && { baseUrl }
24428
+ });
24429
+ const outcome = await core.evaluate({ address: signer.address }, void 0, signer);
24430
+ if (outcome.kind === "allow") return null;
24431
+ const reason = outcome.reason;
24432
+ const body = denialReasonToBody(reason);
24433
+ const status = reason.code === "token_expired" || reason.code === "invalid_credential" ? 401 : reason.code === "api_error" ? 503 : 403;
24434
+ return new Response(
24435
+ JSON.stringify({
24436
+ id: referenceId,
24437
+ endpoint: opts.name,
24438
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
24439
+ payment_status: "failed",
24440
+ ...body
24441
+ }),
24442
+ { status, headers: { "Content-Type": "application/json" } }
24443
+ );
24444
+ }
24205
24445
  async function handleX402Settle(req, referenceId, cachedBody, priceCents, recipients) {
24206
24446
  const verified = await verifyX402Request({
24207
24447
  request: req,
@@ -24400,6 +24640,8 @@ function computeFirstCheckout(opts) {
24400
24640
  { status: 400, headers: { "Content-Type": "application/json" } }
24401
24641
  );
24402
24642
  }
24643
+ const ofacDenial = await enforceWalletSanctions(req, referenceId);
24644
+ if (ofacDenial !== null) return ofacDenial;
24403
24645
  if (hasX402Header(req.headers)) return handleX402Settle(req, referenceId, quote2.body, quote2.priceCents, quote2.recipients);
24404
24646
  return handleMppSettle(req, referenceId, quote2.body, quote2.priceCents, quote2.recipients);
24405
24647
  }
@@ -24548,6 +24790,7 @@ export {
24548
24790
  buildMppxComposeRails,
24549
24791
  buildSignerMismatchBody,
24550
24792
  buildUCPProfile,
24793
+ buildVerificationRequiredBody,
24551
24794
  computeFirstCheckout,
24552
24795
  createDefaultOnDenied,
24553
24796
  createQuoteCache,