@agent-score/commerce 2.1.1 → 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 (53) hide show
  1. package/dist/challenge/index.d.mts +2 -2
  2. package/dist/challenge/index.d.ts +2 -2
  3. package/dist/challenge/index.js +7 -5
  4. package/dist/challenge/index.js.map +1 -1
  5. package/dist/challenge/index.mjs +7 -5
  6. package/dist/challenge/index.mjs.map +1 -1
  7. package/dist/{checkout-BRw_caGr.d.mts → checkout-CKQE2QpJ.d.mts} +37 -1
  8. package/dist/{checkout-CuSNUJFX.d.ts → checkout-CfgxgPZL.d.ts} +37 -1
  9. package/dist/core.js +4 -3
  10. package/dist/core.js.map +1 -1
  11. package/dist/core.mjs +4 -3
  12. package/dist/core.mjs.map +1 -1
  13. package/dist/discovery/index.d.mts +3 -3
  14. package/dist/discovery/index.d.ts +3 -3
  15. package/dist/identity/express.d.mts +4 -3
  16. package/dist/identity/express.d.ts +4 -3
  17. package/dist/identity/express.js +4 -3
  18. package/dist/identity/express.js.map +1 -1
  19. package/dist/identity/express.mjs +4 -3
  20. package/dist/identity/express.mjs.map +1 -1
  21. package/dist/identity/fastify.d.mts +4 -3
  22. package/dist/identity/fastify.d.ts +4 -3
  23. package/dist/identity/fastify.js +4 -3
  24. package/dist/identity/fastify.js.map +1 -1
  25. package/dist/identity/fastify.mjs +4 -3
  26. package/dist/identity/fastify.mjs.map +1 -1
  27. package/dist/identity/hono.d.mts +4 -3
  28. package/dist/identity/hono.d.ts +4 -3
  29. package/dist/identity/hono.js +4 -3
  30. package/dist/identity/hono.js.map +1 -1
  31. package/dist/identity/hono.mjs +4 -3
  32. package/dist/identity/hono.mjs.map +1 -1
  33. package/dist/identity/nextjs.js +4 -3
  34. package/dist/identity/nextjs.js.map +1 -1
  35. package/dist/identity/nextjs.mjs +4 -3
  36. package/dist/identity/nextjs.mjs.map +1 -1
  37. package/dist/identity/web.js +4 -3
  38. package/dist/identity/web.js.map +1 -1
  39. package/dist/identity/web.mjs +4 -3
  40. package/dist/identity/web.mjs.map +1 -1
  41. package/dist/index.d.mts +3 -3
  42. package/dist/index.d.ts +3 -3
  43. package/dist/index.js +137 -13
  44. package/dist/index.js.map +1 -1
  45. package/dist/index.mjs +137 -13
  46. package/dist/index.mjs.map +1 -1
  47. package/dist/{pricing-4n5Ota0D.d.mts → pricing-dSI3ePmE.d.mts} +4 -2
  48. package/dist/{pricing-DHfH3ogG.d.ts → pricing-uFGRNoGl.d.ts} +4 -2
  49. package/dist/stripe-multichain/index.js +2 -1
  50. package/dist/stripe-multichain/index.js.map +1 -1
  51. package/dist/stripe-multichain/index.mjs +2 -1
  52. package/dist/stripe-multichain/index.mjs.map +1 -1
  53. package/package.json +6 -6
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,
@@ -21161,7 +21162,7 @@ function createAgentScoreCore(options) {
21161
21162
  } = options;
21162
21163
  const baseUrl = stripTrailingSlashes(rawBaseUrl);
21163
21164
  const agentMemoryHint = buildAgentMemoryHint();
21164
- const defaultUa = `@agent-score/commerce@${"2.1.1"}`;
21165
+ const defaultUa = `@agent-score/commerce@${"2.2.0"}`;
21165
21166
  const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
21166
21167
  const sdk = new AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
21167
21168
  const sessionSdkCache = /* @__PURE__ */ new Map();
@@ -21298,8 +21299,9 @@ function createAgentScoreCore(options) {
21298
21299
  ...Object.keys(policy).length > 0 ? { policy } : {},
21299
21300
  // Pre-extracted payment signer (by the adapter middleware). When present, the API
21300
21301
  // composes BOTH signer_match (wallet-binding) and signer_sanctions (OFAC SDN wallet
21301
- // check) verdicts on the response in one round trip. Under
21302
- // 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).
21303
21305
  ...signer && { signer: { address: signer.address, network: signer.network } }
21304
21306
  };
21305
21307
  const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
@@ -21781,6 +21783,16 @@ async function deriveMppxReceiptMethod(raw) {
21781
21783
  return extractMppxReceiptMethod(header);
21782
21784
  }
21783
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
+
21784
21796
  // src/payment/rail_spec.ts
21785
21797
  init_usdc();
21786
21798
  async function resolveRecipient(r) {
@@ -22012,14 +22024,16 @@ function buildHowToPay({
22012
22024
  const d = decimals ?? 2;
22013
22025
  const defaultMaxSpend = totalNum >= 1 ? (Math.ceil(totalNum) + 1).toFixed(d) : totalNum.toFixed(d);
22014
22026
  const maxSpendStr = String(maxSpend ?? defaultMaxSpend);
22027
+ const gateless = opTokenPlaceholder === null;
22015
22028
  const opToken = opTokenPlaceholder ?? "<your_opc_token>";
22029
+ const opTokenHeaderFlag = gateless ? "" : `-H 'X-Operator-Token: ${opToken}' `;
22016
22030
  const block = {};
22017
22031
  if (rails2.tempo) {
22018
22032
  const networkName = rails2.tempo.testnet ? "tempo-testnet" : rails2.tempo.network ?? RAIL_SPEC_DEFAULTS.tempo.network;
22019
22033
  const chainId = rails2.tempo.chainId ?? RAIL_SPEC_DEFAULTS.tempo.chainId;
22020
22034
  const recommend = rails2.tempo.recommend ?? RAIL_SPEC_DEFAULTS.tempo.recommend;
22021
- const tempoCommand = `tempo request -X POST -H 'X-Operator-Token: ${opToken}' -H 'Content-Type: application/json' --json '${retryBodyJson}' --max-spend ${maxSpendStr} ${url2}`;
22022
- 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}`;
22023
22037
  block.tempo = {
22024
22038
  setup: TEMPO_SETUP,
22025
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.`,
@@ -22033,7 +22047,7 @@ function buildHowToPay({
22033
22047
  block.x402_base = {
22034
22048
  setup: PAY_SETUP_BASE,
22035
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.`,
22036
- 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}`,
22037
22051
  what_it_does: "Pays via USDC on Base."
22038
22052
  };
22039
22053
  }
@@ -22042,7 +22056,7 @@ function buildHowToPay({
22042
22056
  block.solana_mpp = {
22043
22057
  setup: PAY_SETUP_SOLANA,
22044
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.`,
22045
- 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}`,
22046
22060
  what_it_does: "Pays via USDC on Solana."
22047
22061
  };
22048
22062
  }
@@ -22064,7 +22078,7 @@ function buildHowToPay({
22064
22078
  ];
22065
22079
  stripe.command_link_cli = [
22066
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)`,
22067
- `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`
22068
22082
  ];
22069
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.";
22070
22084
  } else if (linkCliBlocked) {
@@ -22981,6 +22995,23 @@ var Checkout = class {
22981
22995
  discoveryExtensions;
22982
22996
  discoveryProbe;
22983
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
+ }
22984
23015
  constructor(opts) {
22985
23016
  const x402Server = opts.x402Server;
22986
23017
  let x402ServerGetter;
@@ -23178,8 +23209,8 @@ var Checkout = class {
23178
23209
  }
23179
23210
  }
23180
23211
  const hasPaymentHeader2 = hasX402Header(request.headers) || hasMppxHeader(request.headers);
23181
- if (this.gate !== void 0 && hasPaymentHeader2) {
23182
- const denial = await this.runGate(ctx);
23212
+ if (hasPaymentHeader2) {
23213
+ const denial = this.gate !== void 0 ? await this.runGate(ctx) : await this.runWalletSanctionsOnly(ctx);
23183
23214
  if (denial !== null) {
23184
23215
  return {
23185
23216
  status: denial.status,
@@ -23251,7 +23282,9 @@ var Checkout = class {
23251
23282
  }
23252
23283
  return result;
23253
23284
  }
23254
- if (gate.apiKey === void 0) return null;
23285
+ if (gate.apiKey === void 0) {
23286
+ return this.runWalletSanctionsOnly(ctx);
23287
+ }
23255
23288
  let policyOverride;
23256
23289
  if (gate.perRequestPolicy !== void 0) {
23257
23290
  policyOverride = await gate.perRequestPolicy(ctx);
@@ -23341,6 +23374,58 @@ var Checkout = class {
23341
23374
  const status = reason.code === "token_expired" || reason.code === "invalid_credential" ? 401 : reason.code === "api_error" ? 503 : 403;
23342
23375
  return { status, body };
23343
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
+ }
23344
23429
  async handleZeroSettle(ctx, rail) {
23345
23430
  if (!this.zeroSettleCarveOut || ctx.pricing === null) return null;
23346
23431
  const cents = Math.round(ctx.pricing.amountUsd * 100);
@@ -23556,6 +23641,10 @@ var Checkout = class {
23556
23641
  retryBodyJson: JSON.stringify(ctx.request.body),
23557
23642
  totalUsd: ctx.pricing.amountUsd.toFixed(pricingDecimals),
23558
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 },
23559
23648
  ...ctx.pricing.decimals !== void 0 && { decimals: ctx.pricing.decimals }
23560
23649
  });
23561
23650
  const pricingBlock = ctx.pricing.block ?? buildPricingBlock({
@@ -23592,7 +23681,10 @@ var Checkout = class {
23592
23681
  pricing: pricingBlock,
23593
23682
  amountUsd: ctx.pricing.amountUsd.toFixed(pricingDecimals),
23594
23683
  retryBody: ctx.request.body,
23595
- 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() }),
23596
23688
  ...ctx.pricing.product ? { product: ctx.pricing.product } : {},
23597
23689
  ...ctx.pricing.bodyExtras ? { extra: ctx.pricing.bodyExtras } : {},
23598
23690
  ...x402Accepts.length > 0 ? {
@@ -24320,6 +24412,36 @@ function computeFirstCheckout(opts) {
24320
24412
  );
24321
24413
  return new Response(JSON.stringify(body402), { status: 402, headers });
24322
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
+ }
24323
24445
  async function handleX402Settle(req, referenceId, cachedBody, priceCents, recipients) {
24324
24446
  const verified = await verifyX402Request({
24325
24447
  request: req,
@@ -24518,6 +24640,8 @@ function computeFirstCheckout(opts) {
24518
24640
  { status: 400, headers: { "Content-Type": "application/json" } }
24519
24641
  );
24520
24642
  }
24643
+ const ofacDenial = await enforceWalletSanctions(req, referenceId);
24644
+ if (ofacDenial !== null) return ofacDenial;
24521
24645
  if (hasX402Header(req.headers)) return handleX402Settle(req, referenceId, quote2.body, quote2.priceCents, quote2.recipients);
24522
24646
  return handleMppSettle(req, referenceId, quote2.body, quote2.priceCents, quote2.recipients);
24523
24647
  }