@devlab.group/agent-commerce 1.6.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 (41) hide show
  1. package/LICENSE +187 -2
  2. package/README.md +6 -6
  3. package/dist/ap2.d.ts +2 -2
  4. package/dist/ap2.js +94 -28
  5. package/dist/ap2.js.map +1 -1
  6. package/dist/{chunk-V2V2E7HX.js → chunk-3LUJHVKX.js} +4 -4
  7. package/dist/{chunk-V2V2E7HX.js.map → chunk-3LUJHVKX.js.map} +1 -1
  8. package/dist/{chunk-IVJC5YLE.js → chunk-53WXXIPR.js} +3 -3
  9. package/dist/{chunk-IVJC5YLE.js.map → chunk-53WXXIPR.js.map} +1 -1
  10. package/dist/chunk-CKJRITWY.js +113 -0
  11. package/dist/chunk-CKJRITWY.js.map +1 -0
  12. package/dist/chunk-HOZA3IWP.js +115 -0
  13. package/dist/chunk-HOZA3IWP.js.map +1 -0
  14. package/dist/{chunk-ATTWSFQP.js → chunk-RKBGCPCK.js} +78 -25
  15. package/dist/chunk-RKBGCPCK.js.map +1 -0
  16. package/dist/{chunk-RAJGPGJG.js → chunk-SYEGCNJZ.js} +3 -3
  17. package/dist/{chunk-RAJGPGJG.js.map → chunk-SYEGCNJZ.js.map} +1 -1
  18. package/dist/{chunk-74BSEXB5.js → chunk-TWRUNWDR.js} +11 -4
  19. package/dist/chunk-TWRUNWDR.js.map +1 -0
  20. package/dist/cli/index.js +143 -34
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/{constants-Bkq04FRZ.d.ts → constants-BSBRlKRR.d.ts} +2 -2
  23. package/dist/index.d.ts +42 -22
  24. package/dist/index.js +1254 -377
  25. package/dist/index.js.map +1 -1
  26. package/dist/mcp.d.ts +1 -1
  27. package/dist/mcp.js +247 -94
  28. package/dist/mcp.js.map +1 -1
  29. package/dist/mpp.d.ts +6 -2
  30. package/dist/mpp.js +65 -28
  31. package/dist/mpp.js.map +1 -1
  32. package/dist/{guardrails-71XwtXlT.d.ts → payment-flow-DCS882tR.d.ts} +9 -1
  33. package/dist/{protocol-adapter-DTkrXTev.d.ts → protocol-adapter-DonxfAwa.d.ts} +2 -0
  34. package/dist/{types-O6lg3Q5V.d.ts → types-CVw6Sf46.d.ts} +11 -0
  35. package/dist/x402.d.ts +11 -3
  36. package/dist/x402.js +4 -4
  37. package/package.json +22 -17
  38. package/dist/chunk-74BSEXB5.js.map +0 -1
  39. package/dist/chunk-AFI2CEZS.js +0 -10
  40. package/dist/chunk-AFI2CEZS.js.map +0 -1
  41. package/dist/chunk-ATTWSFQP.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -10,7 +10,7 @@ import picocolors from 'picocolors';
10
10
  import { randomUUID } from 'crypto';
11
11
  import 'module';
12
12
  import Database from 'better-sqlite3';
13
- import { writeFile, stat, rename, rm, open } from 'fs/promises';
13
+ import { writeFile, stat, mkdir, rename, rm, open } from 'fs/promises';
14
14
  import { validate } from '@scalar/openapi-parser';
15
15
  import * as clack from '@clack/prompts';
16
16
 
@@ -279,6 +279,12 @@ var init_backend_http = __esm({
279
279
  PATH_PARAM_PATTERN = /\{([a-zA-Z0-9_.-]+)\}/g;
280
280
  }
281
281
  });
282
+
283
+ // src/core/execution/settlement-order.ts
284
+ var init_settlement_order = __esm({
285
+ "src/core/execution/settlement-order.ts"() {
286
+ }
287
+ });
282
288
  function typeList(raw) {
283
289
  if (typeof raw === "string") return [raw];
284
290
  if (Array.isArray(raw) && raw.every((entry) => typeof entry === "string")) {
@@ -302,6 +308,7 @@ var init_pipeline = __esm({
302
308
  init_errors();
303
309
  init_is_record();
304
310
  init_backend_http();
311
+ init_settlement_order();
305
312
  init_validation();
306
313
  [...RESERVED_INPUT_FIELDS, "__proto__"];
307
314
  }
@@ -341,12 +348,12 @@ var init_constants2 = __esm({
341
348
  "src/payments/mpp/constants.ts"() {
342
349
  MPP_SPEC_COMMIT = "806fdb8b8c92cda5c84b8660558921b6bbeef7e0";
343
350
  MPP_SPEC_DRAFTS = {
344
- core: "draft-httpauth-payment-00",
351
+ core: "draft-httpauth-payment-01",
345
352
  intent: "draft-payment-intent-charge-00",
346
353
  method: "draft-evm-charge-00"
347
354
  };
348
355
  MPP_MIN_CHALLENGE_SECRET_LENGTH = 32;
349
- MPPX_VERSION = "0.10.1";
356
+ MPPX_VERSION = "0.13.1";
350
357
  MPP_PROFILE = {
351
358
  intent: "charge",
352
359
  method: "evm",
@@ -419,7 +426,7 @@ var init_networks = __esm({
419
426
  });
420
427
 
421
428
  // src/protocols/acp/constants.ts
422
- var ACP_SPEC_VERSION, ACP_API_VERSION, ACP_WELL_KNOWN_PATH, ACP_CHECKOUT_OPERATIONS, ACP_OPERATION_INPUT_KEYS, ACP_OPERATION_OPTIONAL_INPUT_KEYS;
429
+ var ACP_SPEC_VERSION, ACP_API_VERSION, ACP_WELL_KNOWN_PATH, ACP_CHECKOUT_OPERATIONS, ACP_OPERATION_INPUT_KEYS, ACP_OPERATION_OPTIONAL_INPUT_KEYS, ACP_SESSION_ID_INPUT_KEY;
423
430
  var init_constants3 = __esm({
424
431
  "src/protocols/acp/constants.ts"() {
425
432
  ACP_SPEC_VERSION = "2026-04-17";
@@ -442,6 +449,7 @@ var init_constants3 = __esm({
442
449
  ACP_OPERATION_OPTIONAL_INPUT_KEYS = {
443
450
  cancelCheckoutSession: ["body"]
444
451
  };
452
+ ACP_SESSION_ID_INPUT_KEY = "checkout_session_id";
445
453
  }
446
454
  });
447
455
 
@@ -694,6 +702,15 @@ var init_guardrails = __esm({
694
702
  }
695
703
  });
696
704
 
705
+ // src/payments/x402/payment-flow.ts
706
+ var X402_PAYMENT_FLOWS, X402_DEFAULT_PAYMENT_FLOW;
707
+ var init_payment_flow = __esm({
708
+ "src/payments/x402/payment-flow.ts"() {
709
+ X402_PAYMENT_FLOWS = ["authorization", "upfront"];
710
+ X402_DEFAULT_PAYMENT_FLOW = "authorization";
711
+ }
712
+ });
713
+
697
714
  // src/protocols/mcp/constants.ts
698
715
  var MCP_TOOL_NAME_CHARS, MCP_TOOL_NAME_MAX_LENGTH, MCP_TOOL_NAME_PATTERN;
699
716
  var init_constants4 = __esm({
@@ -836,6 +853,7 @@ function normalize(raw) {
836
853
  min: 1
837
854
  }
838
855
  ),
856
+ paymentFlow: x402Raw.paymentFlow ?? X402_DEFAULT_PAYMENT_FLOW,
839
857
  facilitator: normalizeFacilitator(x402Raw.facilitator),
840
858
  ...x402Raw.allowMainnet !== void 0 ? { allowMainnet: toBoolean(x402Raw.allowMainnet, "payments.x402.allowMainnet") } : {},
841
859
  ...x402Raw.allowUnauthenticatedFacilitator !== void 0 ? {
@@ -867,7 +885,22 @@ function normalize(raw) {
867
885
  const resources = Object.entries(raw.resources).map(
868
886
  ([id, entry]) => normalizeResource(id, entry, protocols, x402, mpp, ap2)
869
887
  );
888
+ const resourcePaymentFlows = Object.fromEntries(
889
+ Object.entries(raw.resources).flatMap(
890
+ ([id, entry]) => entry.paymentFlow !== void 0 ? [[id, entry.paymentFlow]] : []
891
+ )
892
+ );
893
+ const hasFlowOverrides = Object.keys(resourcePaymentFlows).length > 0;
894
+ const x402WithFlows = x402 !== void 0 && hasFlowOverrides ? { ...x402, resourcePaymentFlows } : x402;
895
+ const mppWithFlows = mpp !== void 0 && hasFlowOverrides ? { ...mpp, resourcePaymentFlows } : mpp;
870
896
  if (protocols.acp.enabled) validateAcpCheckoutMapping(protocols.acp, resources);
897
+ if (protocols.a2a.enabled && !resources.some((r) => r.exposedVia.includes("a2a"))) {
898
+ throw new CommerceError(
899
+ "CONFIG_INVALID",
900
+ 'protocols.a2a.enabled is true but no resource lists "a2a" in its expose',
901
+ { details: { path: "protocols.a2a.enabled" } }
902
+ );
903
+ }
871
904
  return {
872
905
  version: SUPPORTED_CONFIG_VERSION,
873
906
  merchant: {
@@ -886,8 +919,8 @@ function normalize(raw) {
886
919
  protocols,
887
920
  resources,
888
921
  payments: {
889
- ...x402 !== void 0 ? { x402 } : {},
890
- ...mpp !== void 0 ? { mpp } : {}
922
+ ...x402WithFlows !== void 0 ? { x402: x402WithFlows } : {},
923
+ ...mppWithFlows !== void 0 ? { mpp: mppWithFlows } : {}
891
924
  },
892
925
  ...ap2 !== void 0 ? { authorization: { ap2 } } : {}
893
926
  };
@@ -956,6 +989,14 @@ function normalizeAp2(raw) {
956
989
  "authorization.ap2.clockSkewSeconds",
957
990
  { min: 0, max: AP2_MAX_CLOCK_SKEW_SECONDS }
958
991
  ),
992
+ requireMandateAudience: toBoolean(
993
+ raw.requireMandateAudience ?? false,
994
+ "authorization.ap2.requireMandateAudience"
995
+ ),
996
+ requireMandateExpiry: toBoolean(
997
+ raw.requireMandateExpiry ?? false,
998
+ "authorization.ap2.requireMandateExpiry"
999
+ ),
959
1000
  replay: { path: raw.replay.path }
960
1001
  };
961
1002
  }
@@ -1131,7 +1172,8 @@ function normalizeAcp(raw) {
1131
1172
  raw.idempotency.retentionHours ?? ACP_RETENTION_HOURS,
1132
1173
  "protocols.acp.idempotency.retentionHours",
1133
1174
  { min: ACP_RETENTION_HOURS }
1134
- )
1175
+ ),
1176
+ merchantIdempotent: raw.idempotency.merchantIdempotent ?? false
1135
1177
  },
1136
1178
  checkout: { operations: normalizeAcpOperations(raw.checkout) },
1137
1179
  // Built key by key so absent metadata stays absent rather than `undefined`
@@ -1214,10 +1256,11 @@ function validateAcpOperationInput(path2, operation, resource) {
1214
1256
  if (schema === void 0) return;
1215
1257
  const keys = ACP_OPERATION_INPUT_KEYS[operation];
1216
1258
  const optionalKeys = ACP_OPERATION_OPTIONAL_INPUT_KEYS[operation] ?? [];
1217
- const properties = schema["properties"];
1218
- if (schema["additionalProperties"] === false && isRecord(properties)) {
1219
- for (const key of [...keys, ...optionalKeys]) {
1220
- if (!Object.hasOwn(properties, key)) {
1259
+ const sent = [...keys, ...optionalKeys];
1260
+ const topLevel = closedObjectProperties(schema);
1261
+ if (topLevel !== void 0) {
1262
+ for (const key of sent) {
1263
+ if (!Object.hasOwn(topLevel, key)) {
1221
1264
  const when = keys.includes(key) ? "always sends" : "sends when the caller supplies it";
1222
1265
  throw acpInvalid(
1223
1266
  path2,
@@ -1227,6 +1270,23 @@ function validateAcpOperationInput(path2, operation, resource) {
1227
1270
  }
1228
1271
  }
1229
1272
  }
1273
+ const declared = isRecord(schema["properties"]) ? schema["properties"] : {};
1274
+ const pathKeys = closedObjectProperties(declared["path"]);
1275
+ if (sent.includes("path") && pathKeys !== void 0 && !Object.hasOwn(pathKeys, ACP_SESSION_ID_INPUT_KEY)) {
1276
+ throw acpInvalid(
1277
+ path2,
1278
+ `Resource "${resource.id}" implements ACP operation "${operation}" but its "path" schema sets additionalProperties: false without declaring "${ACP_SESSION_ID_INPUT_KEY}", which the adapter always sends inside "path"`,
1279
+ { resourceId: resource.id }
1280
+ );
1281
+ }
1282
+ const bodyKeys = closedObjectProperties(declared["body"]);
1283
+ if (sent.includes("body") && bodyKeys !== void 0 && Object.keys(bodyKeys).length === 0) {
1284
+ throw acpInvalid(
1285
+ path2,
1286
+ `Resource "${resource.id}" implements ACP operation "${operation}" but its "body" schema accepts no keys, so it refuses every ACP document. Config load closes an object schema that omits additionalProperties: declare body as { type: object, additionalProperties: true }`,
1287
+ { resourceId: resource.id }
1288
+ );
1289
+ }
1230
1290
  const required = schema["required"];
1231
1291
  if (Array.isArray(required)) {
1232
1292
  for (const entry of required) {
@@ -1240,6 +1300,10 @@ function validateAcpOperationInput(path2, operation, resource) {
1240
1300
  }
1241
1301
  }
1242
1302
  }
1303
+ function closedObjectProperties(schema) {
1304
+ if (!isRecord(schema) || schema["additionalProperties"] !== false) return void 0;
1305
+ return isRecord(schema["properties"]) ? schema["properties"] : {};
1306
+ }
1243
1307
  function normalizeFacilitator(raw) {
1244
1308
  return raw.mode === "local" ? { mode: "local", signerPrivateKey: raw.signerPrivateKey } : { mode: "remote", url: raw.url, auth: raw.auth ?? { type: "none" } };
1245
1309
  }
@@ -1270,6 +1334,7 @@ function normalizeMpp(raw) {
1270
1334
  { min: 1 }
1271
1335
  )
1272
1336
  } : {},
1337
+ paymentFlow: raw.paymentFlow ?? X402_DEFAULT_PAYMENT_FLOW,
1273
1338
  facilitator: normalizeFacilitator(raw.facilitator),
1274
1339
  ...raw.allowMainnet !== void 0 ? { allowMainnet: toBoolean(raw.allowMainnet, "payments.mpp.allowMainnet") } : {},
1275
1340
  ...raw.allowUnauthenticatedFacilitator !== void 0 ? {
@@ -1423,6 +1488,13 @@ function normalizeResource(id, entry, protocols, x402, mpp, ap2) {
1423
1488
  validatePricingAmount(id, entry.pricing.amount, MPP_PROFILE.assetDecimals);
1424
1489
  }
1425
1490
  }
1491
+ if (entry.paymentFlow !== void 0 && (entry.pricing.type !== "fixed" || !paymentMethods.some((method) => method === "x402" || method === "mpp"))) {
1492
+ throw new CommerceError(
1493
+ "CONFIG_INVALID",
1494
+ `Resource "${id}" sets paymentFlow, which requires fixed pricing and "x402" or "mpp" in payments`,
1495
+ { details: { path: `resources.${id}.paymentFlow`, resourceId: id } }
1496
+ );
1497
+ }
1426
1498
  const pricing = entry.pricing.type === "free" ? { type: "free" } : { type: "fixed", amount: entry.pricing.amount, currency: entry.pricing.currency };
1427
1499
  const authorization = normalizeResourceAuthorization(id, entry, pricing, ap2);
1428
1500
  return {
@@ -1745,6 +1817,7 @@ var init_schema = __esm({
1745
1817
  init_is_record();
1746
1818
  init_constants2();
1747
1819
  init_guardrails();
1820
+ init_payment_flow();
1748
1821
  init_constants3();
1749
1822
  init_constants4();
1750
1823
  init_env();
@@ -1812,7 +1885,11 @@ var init_schema = __esm({
1812
1885
  }
1813
1886
  );
1814
1887
  AcpAuthSchema = z.object({ type: z.literal("bearer"), token: z.string().min(1) }).strict();
1815
- AcpIdempotencySchema = z.object({ path: z.string().min(1), retentionHours: NumberOrString.optional() }).strict();
1888
+ AcpIdempotencySchema = z.object({
1889
+ path: z.string().min(1),
1890
+ retentionHours: NumberOrString.optional(),
1891
+ merchantIdempotent: z.boolean().optional()
1892
+ }).strict();
1816
1893
  AcpCheckoutSchema = z.object({ operations: z.record(z.string().min(1)) }).strict();
1817
1894
  AcpDiscoverySchema = z.object({
1818
1895
  documentationUrl: z.string().min(1).optional(),
@@ -1882,6 +1959,7 @@ var init_schema = __esm({
1882
1959
  pricing: PricingSchema,
1883
1960
  expose: z.array(z.string().min(1)).min(1),
1884
1961
  payments: z.array(z.string().min(1)).optional(),
1962
+ paymentFlow: z.enum(X402_PAYMENT_FLOWS).optional(),
1885
1963
  authorization: z.object({ required: z.array(z.string().min(1)).min(1) }).strict().optional()
1886
1964
  }).strict();
1887
1965
  ResourcesMapSchema = z.record(z.string().min(1), ResourceEntrySchema);
@@ -1919,6 +1997,8 @@ var init_schema = __esm({
1919
1997
  assetDecimals: NumberOrString,
1920
1998
  payTo: z.string().min(1),
1921
1999
  maxTimeoutSeconds: NumberOrString,
2000
+ // `authorization` (the x402 default) settles after the backend call
2001
+ paymentFlow: z.enum(X402_PAYMENT_FLOWS).optional(),
1922
2002
  facilitator: FacilitatorSchema,
1923
2003
  // Real funds. Never defaulted; see src/payments/x402/guardrails.ts
1924
2004
  allowMainnet: BooleanOrString.optional(),
@@ -1936,6 +2016,8 @@ var init_schema = __esm({
1936
2016
  realm: z.string().min(1),
1937
2017
  challengeSecret: z.string().min(1),
1938
2018
  challengeTtlSeconds: NumberOrString.optional(),
2019
+ // Same values and default as `payments.x402.paymentFlow`
2020
+ paymentFlow: z.enum(X402_PAYMENT_FLOWS).optional(),
1939
2021
  facilitator: FacilitatorSchema,
1940
2022
  // Real funds. Never defaulted; see src/payments/x402/guardrails.ts
1941
2023
  allowMainnet: BooleanOrString.optional(),
@@ -1966,6 +2048,8 @@ var init_schema = __esm({
1966
2048
  checkoutIssuers: z.array(Ap2IssuerSchema).optional()
1967
2049
  }).strict().optional(),
1968
2050
  clockSkewSeconds: NumberOrString.optional(),
2051
+ requireMandateAudience: BooleanOrString.optional(),
2052
+ requireMandateExpiry: BooleanOrString.optional(),
1969
2053
  replay: z.object({ path: z.string().min(1) }).strict().optional()
1970
2054
  }).strict();
1971
2055
  AuthorizationSchema = z.object({ ap2: Ap2Schema.optional() }).strict();
@@ -2166,10 +2250,12 @@ var AP2_UNSUPPORTED = [
2166
2250
  // Mandate kinds. Open mandates are refused: see AP2_CHECKOUT_MANDATE_VCT
2167
2251
  "autonomous mode",
2168
2252
  "open checkout mandates (mandate.checkout.open.1)",
2169
- "intent mandates",
2170
- "cart mandates",
2253
+ "payment mandates (mandate.payment.1)",
2171
2254
  "spending constraint evaluation",
2172
2255
  "cnf-bound agent keys",
2256
+ // Delegation models other than a trusted agent provider
2257
+ "user credential delegation (OpenID4VP)",
2258
+ "delegation chains and key-binding JWTs",
2173
2259
  // Key handling. Every key is written into config by an operator
2174
2260
  "JWKS and any key discovery by URL (jku, x5u)",
2175
2261
  "issuer metadata fetching",
@@ -2179,6 +2265,7 @@ var AP2_UNSUPPORTED = [
2179
2265
  "digest algorithms other than sha-256",
2180
2266
  // Roles this gateway does not play
2181
2267
  "mandate issuance",
2268
+ "checkout receipts",
2182
2269
  "AP2 over the ACP checkout adapter"
2183
2270
  ];
2184
2271
 
@@ -2189,20 +2276,23 @@ init_constants2();
2189
2276
  init_networks();
2190
2277
 
2191
2278
  // src/protocols/a2a/constants.ts
2192
- var A2A_SPEC_VERSION = "1.0.0";
2279
+ var A2A_SPEC_VERSION = "1.0.1";
2193
2280
  var A2A_PROTOCOL_VERSION = "1.0";
2194
2281
  var A2A_PROTOCOL_BINDING = "JSONRPC";
2195
2282
  var A2A_AGENT_CARD_PATH = "/.well-known/agent-card.json";
2283
+ var A2A_PUSH_CONFIG_METHODS = [
2284
+ "CreateTaskPushNotificationConfig",
2285
+ "GetTaskPushNotificationConfig",
2286
+ "ListTaskPushNotificationConfigs",
2287
+ "DeleteTaskPushNotificationConfig"
2288
+ ];
2196
2289
  var A2A_UNSUPPORTED_METHODS = [
2197
2290
  "SendStreamingMessage",
2198
2291
  "GetTask",
2199
2292
  "ListTasks",
2200
2293
  "CancelTask",
2201
2294
  "SubscribeToTask",
2202
- "CreateTaskPushNotificationConfig",
2203
- "GetTaskPushNotificationConfig",
2204
- "ListTaskPushNotificationConfigs",
2205
- "DeleteTaskPushNotificationConfig",
2295
+ ...A2A_PUSH_CONFIG_METHODS,
2206
2296
  "GetExtendedAgentCard"
2207
2297
  ];
2208
2298
 
@@ -2216,12 +2306,14 @@ var A2A_UNSUPPORTED = [
2216
2306
  // Behaviors
2217
2307
  "SSE",
2218
2308
  "long-running task persistence",
2219
- "task resumption",
2309
+ "task resumption other than an x402 extension payment",
2220
2310
  "push notifications",
2221
2311
  "multi-turn conversational continuation",
2222
2312
  "authenticated extended agent cards",
2223
2313
  "A2A authentication schemes",
2224
- "artifact types beyond Agent Commerce outcome data"
2314
+ "artifact types beyond Agent Commerce outcome data",
2315
+ // Pending x402 payments live in process memory
2316
+ "x402 extension payments across gateway restarts or instances"
2225
2317
  ];
2226
2318
 
2227
2319
  // src/cli/commands/doctor.ts
@@ -2233,7 +2325,9 @@ init_constants3();
2233
2325
  "rest transport",
2234
2326
  "well-known discovery",
2235
2327
  "bearer authentication",
2236
- "API-Version negotiation",
2328
+ "API-Version enforcement",
2329
+ // Forwarded as received: the gateway verifies neither signature header
2330
+ "request header forwarding to the merchant",
2237
2331
  "checkout service",
2238
2332
  ...ACP_CHECKOUT_OPERATIONS
2239
2333
  ];
@@ -2262,7 +2356,7 @@ var ACP_UNSUPPORTED = [
2262
2356
 
2263
2357
  // src/storage/receipts/store.ts
2264
2358
  init_core();
2265
- var PACKAGE_VERSION = "1.6.0" ;
2359
+ var PACKAGE_VERSION = "2.0.0" ;
2266
2360
 
2267
2361
  // src/storage/sqlite.ts
2268
2362
  init_core();
@@ -2558,6 +2652,9 @@ function createSqliteReceiptStore(options) {
2558
2652
  const listPaymentAttemptsByRequestStmt = db.prepare(
2559
2653
  "SELECT * FROM payment_attempts WHERE request_id = ? ORDER BY created_at DESC, seq DESC LIMIT ?"
2560
2654
  );
2655
+ const paymentAttemptStatusStmt = db.prepare(
2656
+ "SELECT status FROM payment_attempts WHERE replay_key = ?"
2657
+ );
2561
2658
  let closed = false;
2562
2659
  const descriptor = {
2563
2660
  name: "sqlite-receipt-store",
@@ -2614,10 +2711,15 @@ function createSqliteReceiptStore(options) {
2614
2711
  });
2615
2712
  } catch (err) {
2616
2713
  if (isUniqueConstraintOn(err, "payment_attempts.replay_key")) {
2714
+ const existing = paymentAttemptStatusStmt.get(reservation.replayKey);
2617
2715
  throw new CommerceError(
2618
2716
  "PAYMENT_REPLAYED",
2619
2717
  `Payment authorization has already been used (replayKey=${reservation.replayKey})`,
2620
- { requestId: reservation.requestId, resourceId: reservation.resourceId }
2718
+ {
2719
+ requestId: reservation.requestId,
2720
+ resourceId: reservation.resourceId,
2721
+ ...existing !== void 0 ? { details: { attemptStatus: existing.status } } : {}
2722
+ }
2621
2723
  );
2622
2724
  }
2623
2725
  throw toCommerceError(err, "STORAGE_ERROR", "Failed to reserve payment attempt");
@@ -3003,7 +3105,7 @@ var package_default = {
3003
3105
  "ajv-formats": "3.0.1",
3004
3106
  "better-sqlite3": "13.0.3",
3005
3107
  commander: "15.0.0",
3006
- fastify: "5.12.0",
3108
+ fastify: "5.12.5",
3007
3109
  picocolors: "1.1.1",
3008
3110
  pino: "10.3.1",
3009
3111
  yaml: "2.9.0",
@@ -3011,19 +3113,19 @@ var package_default = {
3011
3113
  },
3012
3114
  peerDependencies: {
3013
3115
  "@coinbase/x402": "2.1.0",
3014
- "@modelcontextprotocol/sdk": "1.30.0",
3116
+ "@modelcontextprotocol/server": "2.3.0",
3015
3117
  "@sd-jwt/core": "0.20.1",
3016
- "@x402/core": "2.23.0",
3017
- "@x402/evm": "2.23.0",
3118
+ "@x402/core": "2.25.0",
3119
+ "@x402/evm": "2.25.0",
3018
3120
  canonicalize: "5.0.0",
3019
3121
  jose: "6.2.12",
3020
- mppx: "0.10.1",
3021
- viem: "2.55.18"
3122
+ mppx: "0.13.1",
3123
+ viem: "2.57.1"
3022
3124
  }};
3023
3125
 
3024
3126
  // src/cli/lib/versions.ts
3025
3127
  var PINS = [
3026
- ["@modelcontextprotocol/sdk", "protocols/mcp"],
3128
+ ["@modelcontextprotocol/server", "protocols/mcp"],
3027
3129
  ["ajv", "protocols/acp"],
3028
3130
  ["ajv-formats", "protocols/acp"],
3029
3131
  ["@x402/core", "payments/x402"],
@@ -3071,6 +3173,11 @@ function substitutePathParams(url) {
3071
3173
  }
3072
3174
  return filled;
3073
3175
  }
3176
+ function describePaymentFlow(rail) {
3177
+ const overrides = Object.keys(rail.resourcePaymentFlows ?? {}).length;
3178
+ const overrideCount = overrides > 0 ? ` (${overrides} resource ${overrides === 1 ? "override" : "overrides"})` : "";
3179
+ return `paymentFlow=${rail.paymentFlow ?? "authorization"}${overrideCount}`;
3180
+ }
3074
3181
  function extractWellKnownX402(body, rail = "x402") {
3075
3182
  const payments = body?.["payments"];
3076
3183
  if (typeof payments !== "object" || payments === null) return void 0;
@@ -3327,7 +3434,7 @@ async function runDoctor(options, deps = {}) {
3327
3434
  checks.push({
3328
3435
  name: "AP2",
3329
3436
  status: "PASS",
3330
- detail: `experimental \xB7 spec ${AP2_SPEC_VERSION} \xB7 mode ${ap2.mode} \xB7 ${AP2_CHECKOUT_MANDATE_VCT} \xB7 profile ${AP2_CHECKOUT_PROFILE} \xB7 clock skew ${ap2.clockSkewSeconds}s`
3437
+ detail: `experimental \xB7 spec ${AP2_SPEC_VERSION} \xB7 mode ${ap2.mode} \xB7 ${AP2_CHECKOUT_MANDATE_VCT} \xB7 profile ${AP2_CHECKOUT_PROFILE} \xB7 clock skew ${ap2.clockSkewSeconds}s \xB7 mandate aud ${ap2.requireMandateAudience === true ? "required" : "optional"} \xB7 mandate exp ${ap2.requireMandateExpiry === true ? "required" : "optional"}`
3331
3438
  });
3332
3439
  checks.push({
3333
3440
  name: "AP2 trust",
@@ -3359,7 +3466,8 @@ async function runDoctor(options, deps = {}) {
3359
3466
  checks.push({ name: "Payments", status: "INFO", detail: "x402 not configured" });
3360
3467
  } else {
3361
3468
  const { mode, where, via } = describeSettlement(x402.network, x402.facilitator);
3362
- const summary = `x402 v2 (scheme=exact) enabled - ${where}, destination=${maskMiddle(x402.payTo)}, facilitator=${via}`;
3469
+ const flow = describePaymentFlow(x402);
3470
+ const summary = `x402 v2 (scheme=exact) enabled - ${where}, destination=${maskMiddle(x402.payTo)}, facilitator=${via}, ${flow}`;
3363
3471
  const live = wellKnown?.ok ? extractWellKnownX402(wellKnown.body) : void 0;
3364
3472
  if (sameAddress2(x402.asset, PLACEHOLDER_ASSET_ADDRESS)) {
3365
3473
  checks.push({
@@ -3394,7 +3502,7 @@ async function runDoctor(options, deps = {}) {
3394
3502
  checks.push({ name: "Payments (MPP)", status: "INFO", detail: "MPP not configured" });
3395
3503
  } else {
3396
3504
  const { mode, where, via } = describeSettlement(mpp.network, mpp.facilitator);
3397
- const summary = `MPP ${MPP_PROFILE.intent}/${MPP_PROFILE.method}/${MPP_PROFILE.credentialType} enabled - ${where}, asset=${MPP_PROFILE.assetSymbol} ${maskMiddle(mpp.asset)}, recipient=${maskMiddle(mpp.recipient)}, facilitator=${via}, spec ${MPP_SPEC_DRAFTS.core}@${MPP_SPEC_COMMIT.slice(0, 7)}, mppx ${MPPX_VERSION}`;
3505
+ const summary = `MPP ${MPP_PROFILE.intent}/${MPP_PROFILE.method}/${MPP_PROFILE.credentialType} enabled - ${where}, asset=${MPP_PROFILE.assetSymbol} ${maskMiddle(mpp.asset)}, recipient=${maskMiddle(mpp.recipient)}, facilitator=${via}, ${describePaymentFlow(mpp)}, spec ${MPP_SPEC_DRAFTS.core}@${MPP_SPEC_COMMIT.slice(0, 7)}, mppx ${MPPX_VERSION}`;
3398
3506
  const configured = { asset: mpp.asset, network: mpp.network, payTo: mpp.recipient };
3399
3507
  const live = wellKnown?.ok ? extractWellKnownX402(wellKnown.body, "mpp") : void 0;
3400
3508
  if (live === "disabled") {
@@ -4662,6 +4770,7 @@ async function writeAtomically(outputPath, content, deps) {
4662
4770
  const target = resolve(outputPath);
4663
4771
  const temporary = `${target}.${process.pid}.tmp`;
4664
4772
  try {
4773
+ await mkdir(dirname(target), { recursive: true });
4665
4774
  await writeFile(temporary, content, "utf8");
4666
4775
  await rename(temporary, target);
4667
4776
  } catch (error) {