@agentcash/router 1.12.0 → 1.14.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.
package/dist/index.cjs CHANGED
@@ -42,7 +42,7 @@ var init_constants = __esm({
42
42
  BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
43
43
  BASE_USDC_DECIMALS = 6;
44
44
  ZERO_EVM_ADDRESS = "0x0000000000000000000000000000000000000000";
45
- DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.corbits.dev";
45
+ DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.payai.network";
46
46
  DEFAULT_TEMPO_RPC_URL = "https://rpc.tempo.xyz";
47
47
  }
48
48
  });
@@ -151,31 +151,64 @@ function buildSolanaExactOptions(accepts, price) {
151
151
  function hasSolanaAccepts(accepts) {
152
152
  return accepts.some((accept) => isSolanaNetwork(accept.network));
153
153
  }
154
- async function enrichRequirementsWithFacilitatorAccepts(facilitator, resource, requirements) {
154
+ function facilitatorBaseUrl(facilitator) {
155
155
  if (!facilitator.url) {
156
- throw new Error(`Facilitator for ${facilitator.network} is missing a URL for /accepts`);
156
+ throw new Error(`Facilitator for ${facilitator.network} is missing a URL`);
157
157
  }
158
- const authHeaders = await getAcceptsHeadersForFacilitator(facilitator);
159
- const response = await fetch(`${facilitator.url.replace(/\/+$/, "")}/accepts`, {
160
- method: "POST",
161
- headers: {
162
- ...authHeaders,
163
- "content-type": "application/json"
164
- },
165
- body: JSON.stringify({
166
- x402Version: 2,
167
- resource,
168
- accepts: requirements
169
- })
158
+ return facilitator.url.replace(/\/+$/, "");
159
+ }
160
+ function matchesSupportedKind(requirement, kind) {
161
+ const scheme = requirement.scheme ?? "exact";
162
+ return kind.network === requirement.network && (kind.scheme ?? "exact") === scheme;
163
+ }
164
+ async function fetchFacilitatorSupported(facilitator) {
165
+ const authHeaders = await getSupportedHeadersForFacilitator(facilitator);
166
+ const response = await fetch(`${facilitatorBaseUrl(facilitator)}/supported`, {
167
+ headers: authHeaders
170
168
  });
171
169
  if (!response.ok) {
172
- throw new Error(`Facilitator /accepts failed with status ${response.status}`);
170
+ throw new Error(`Facilitator /supported failed with status ${response.status}`);
173
171
  }
174
172
  const body = await response.json();
175
- if (!Array.isArray(body.accepts)) {
176
- throw new Error("Facilitator /accepts response did not include accepts");
173
+ if (!Array.isArray(body.kinds)) {
174
+ throw new Error("Facilitator /supported response did not include kinds");
175
+ }
176
+ return body.kinds;
177
+ }
178
+ function enrichRequirementFromKind(requirement, kinds) {
179
+ const kind = kinds.find((candidate) => matchesSupportedKind(requirement, candidate));
180
+ if (!kind) {
181
+ throw new Error(
182
+ `Facilitator /supported has no kind for ${requirement.scheme} on ${requirement.network}`
183
+ );
184
+ }
185
+ const feePayer = kind.extra?.feePayer;
186
+ if (!feePayer) {
187
+ throw new Error(
188
+ `Facilitator /supported kind for ${requirement.network} is missing extra.feePayer`
189
+ );
177
190
  }
178
- return body.accepts;
191
+ const requirementExtra = requirement.extra ?? {};
192
+ const kindExtra = kind.extra ?? {};
193
+ const requirementFeatures = requirementExtra.features ?? {};
194
+ const kindFeatures = kindExtra.features ?? {};
195
+ return {
196
+ ...requirement,
197
+ ...kind.asset && !requirement.asset ? { asset: kind.asset } : {},
198
+ extra: {
199
+ ...requirementExtra,
200
+ ...kindExtra,
201
+ features: {
202
+ ...requirementFeatures,
203
+ ...kindFeatures,
204
+ xSettlementAccountSupported: true
205
+ }
206
+ }
207
+ };
208
+ }
209
+ async function enrichRequirementsFromFacilitatorSupported(facilitator, requirements) {
210
+ const kinds = await fetchFacilitatorSupported(facilitator);
211
+ return requirements.map((requirement) => enrichRequirementFromKind(requirement, kinds));
179
212
  }
180
213
  function isSolanaRequirement(requirement) {
181
214
  return isSolanaNetwork(requirement.network);
@@ -232,10 +265,7 @@ function getFacilitatorForRequirement(facilitatorsByNetwork, requirement) {
232
265
  function sameResolvedX402Facilitator(a, b) {
233
266
  return sameFacilitatorConfig(a.config, b.config);
234
267
  }
235
- async function getAcceptsHeadersForFacilitator(facilitator) {
236
- if (facilitator.config.createAcceptsHeaders) {
237
- return facilitator.config.createAcceptsHeaders();
238
- }
268
+ async function getSupportedHeadersForFacilitator(facilitator) {
239
269
  if (facilitator.config.createAuthHeaders) {
240
270
  const headers = await facilitator.config.createAuthHeaders();
241
271
  return headers.supported;
@@ -257,7 +287,7 @@ function getNetworkFamily(network) {
257
287
  return null;
258
288
  }
259
289
  function sameFacilitatorConfig(a, b) {
260
- return a.url === b.url && a.createAuthHeaders === b.createAuthHeaders && a.createAcceptsHeaders === b.createAcceptsHeaders;
290
+ return a.url === b.url && a.createAuthHeaders === b.createAuthHeaders;
261
291
  }
262
292
  var init_facilitators = __esm({
263
293
  "src/protocols/x402/facilitators.ts"() {
@@ -473,6 +503,7 @@ __export(index_exports, {
473
503
  DEFAULT_SOLANA_FACILITATOR_URL: () => DEFAULT_SOLANA_FACILITATOR_URL,
474
504
  DEFAULT_TEMPO_RPC_URL: () => DEFAULT_TEMPO_RPC_URL,
475
505
  HttpError: () => HttpError,
506
+ RouteDefinitionError: () => RouteDefinitionError,
476
507
  RouterConfigError: () => RouterConfigError,
477
508
  SOLANA_MAINNET_NETWORK: () => SOLANA_MAINNET_NETWORK,
478
509
  TEMPO_USDC_ADDRESS: () => TEMPO_USDC_ADDRESS,
@@ -873,6 +904,13 @@ var HttpError = class extends Error {
873
904
  this.name = "HttpError";
874
905
  }
875
906
  };
907
+ var RouteDefinitionError = class extends Error {
908
+ constructor(route, detail) {
909
+ super(`route '${route}': ${detail}`);
910
+ this.route = route;
911
+ this.name = "RouteDefinitionError";
912
+ }
913
+ };
876
914
 
877
915
  // src/auth/agent-identity.ts
878
916
  var PROTOCOL_VERSION = 1;
@@ -1400,6 +1438,23 @@ function multiplyDecimal(decimal, factor) {
1400
1438
  return fracPart ? `${intPart}.${fracPart}` : intPart;
1401
1439
  }
1402
1440
 
1441
+ // src/protocols/detect.ts
1442
+ function hasX402Payment(request) {
1443
+ return Boolean(
1444
+ request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)
1445
+ );
1446
+ }
1447
+ function hasMppPayment(request) {
1448
+ const auth = request.headers.get(HEADERS.AUTHORIZATION);
1449
+ return Boolean(auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT));
1450
+ }
1451
+ function detectProtocol(request) {
1452
+ if (hasX402Payment(request)) return "x402";
1453
+ if (hasMppPayment(request)) return "mpp";
1454
+ if (request.headers.get(HEADERS.SIWX)) return "siwx";
1455
+ return null;
1456
+ }
1457
+
1403
1458
  // src/protocols/mpp/credential.ts
1404
1459
  var import_mppx = require("mppx");
1405
1460
  var import_viem = require("viem");
@@ -1766,10 +1821,7 @@ function settleHashMode(args) {
1766
1821
  // src/protocols/mpp/strategy.ts
1767
1822
  var mppStrategy = {
1768
1823
  protocol: "mpp",
1769
- detects(request) {
1770
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
1771
- return Boolean(auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT));
1772
- },
1824
+ detects: hasMppPayment,
1773
1825
  preflight(request, _routeEntry) {
1774
1826
  const info = readMppCredential(request);
1775
1827
  if (!info?.sessionAction) return null;
@@ -1974,15 +2026,14 @@ async function buildChallengeRequirements(server, request, price, accepts, resou
1974
2026
  function needsFacilitatorEnrichment(accepts) {
1975
2027
  return hasSolanaAccepts(accepts);
1976
2028
  }
1977
- async function enrichGroup(group, resource) {
1978
- const accepted = await enrichRequirementsWithFacilitatorAccepts(
2029
+ async function enrichGroup(group) {
2030
+ const accepted = await enrichRequirementsFromFacilitatorSupported(
1979
2031
  group.facilitator,
1980
- resource,
1981
2032
  group.items.map(({ requirement }) => requirement)
1982
2033
  );
1983
2034
  if (accepted.length !== group.items.length) {
1984
2035
  throw new Error(
1985
- `Facilitator /accepts returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
2036
+ `Facilitator /supported enrichment returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
1986
2037
  );
1987
2038
  }
1988
2039
  return accepted;
@@ -1993,13 +2044,13 @@ async function enrichChallengeRequirements(requirements, resource, facilitatorsB
1993
2044
  const results = await Promise.all(
1994
2045
  groups.map(async (group) => {
1995
2046
  try {
1996
- return { success: true, group, accepted: await enrichGroup(group, resource) };
2047
+ return { success: true, group, accepted: await enrichGroup(group) };
1997
2048
  } catch (err) {
1998
2049
  const label = group.facilitator.url ?? group.facilitator.network;
1999
2050
  const reason = err instanceof Error ? err.message : String(err);
2000
2051
  report?.(
2001
2052
  "warn",
2002
- `${label} /accepts failed, dropping ${group.items.length} requirement(s): ${reason}`
2053
+ `${label} /supported enrichment failed, dropping ${group.items.length} requirement(s): ${reason}`
2003
2054
  );
2004
2055
  return { success: false, group };
2005
2056
  }
@@ -2204,11 +2255,7 @@ function formatVerifyFailureMessage(failure) {
2204
2255
  }
2205
2256
  var x402Strategy = {
2206
2257
  protocol: "x402",
2207
- detects(request) {
2208
- return Boolean(
2209
- request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)
2210
- );
2211
- },
2258
+ detects: hasX402Payment,
2212
2259
  verify: (args) => verifyX402(args),
2213
2260
  settle: (args) => settleX402(args),
2214
2261
  buildChallenge: (args) => buildX402ChallengeContribution(args)
@@ -2355,21 +2402,6 @@ function getAllowedStrategies(allowed) {
2355
2402
  return allowed.map((name) => STRATEGIES[name]);
2356
2403
  }
2357
2404
 
2358
- // src/protocols/detect.ts
2359
- function detectProtocol(request) {
2360
- if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
2361
- return "x402";
2362
- }
2363
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
2364
- if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
2365
- return "mpp";
2366
- }
2367
- if (request.headers.get(HEADERS.SIWX)) {
2368
- return "siwx";
2369
- }
2370
- return null;
2371
- }
2372
-
2373
2405
  // src/pipeline/flows/api-key-only.ts
2374
2406
  async function runApiKeyOnlyFlow(ctx) {
2375
2407
  if (!ctx.routeEntry.apiKeyResolver) {
@@ -2692,6 +2724,61 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
2692
2724
  return response;
2693
2725
  }
2694
2726
 
2727
+ // src/pipeline/flows/paid-preamble.ts
2728
+ async function runPaidPreamble(ctx) {
2729
+ const { request, routeEntry, deps, report } = ctx;
2730
+ const apiKeyGate = await runApiKeyGate(ctx);
2731
+ if (!apiKeyGate.ok) return { done: true, response: apiKeyGate.response };
2732
+ const { account } = apiKeyGate;
2733
+ const pricing = selectPricing(routeEntry.pricing, {
2734
+ alert: report,
2735
+ maxPrice: routeEntry.maxPrice,
2736
+ minPrice: routeEntry.minPrice,
2737
+ route: routeEntry.key
2738
+ });
2739
+ const incomingStrategy = selectIncomingStrategy(request, routeEntry.protocols);
2740
+ const earlyResolution = await resolveEarlyBody({ ctx, pricing, incomingStrategy });
2741
+ if (!earlyResolution.ok) return { done: true, response: earlyResolution.response };
2742
+ const { earlyBody } = earlyResolution;
2743
+ const siwxFastPath = await trySiwxFastPath(ctx, account);
2744
+ if (siwxFastPath) return { done: true, response: siwxFastPath };
2745
+ if (!incomingStrategy) {
2746
+ const initError = protocolInitError(routeEntry, deps);
2747
+ if (initError) return { done: true, response: fail(ctx, 500, initError) };
2748
+ return { done: true, response: await buildChallengeResponse(ctx, pricing, earlyBody) };
2749
+ }
2750
+ return { done: false, account, pricing, incomingStrategy, earlyBody };
2751
+ }
2752
+ async function runPaidVerify(args) {
2753
+ const { ctx, strategy, pricing, parsedBody, price } = args;
2754
+ const { request, routeEntry, deps, report } = ctx;
2755
+ const verifyOutcome = await strategy.verify({
2756
+ request,
2757
+ body: parsedBody,
2758
+ price,
2759
+ routeEntry,
2760
+ deps,
2761
+ report
2762
+ });
2763
+ if (verifyOutcome.ok === false) {
2764
+ if (verifyOutcome.kind === "config") {
2765
+ return { ok: false, response: fail(ctx, 500, verifyOutcome.message, parsedBody) };
2766
+ }
2767
+ return {
2768
+ ok: false,
2769
+ response: await buildChallengeResponse(ctx, pricing, parsedBody, verifyOutcome.failure)
2770
+ };
2771
+ }
2772
+ ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2773
+ firePaymentVerified(ctx, {
2774
+ protocol: strategy.protocol,
2775
+ payer: verifyOutcome.wallet,
2776
+ amount: price,
2777
+ network: verifyOutcome.payment.network
2778
+ });
2779
+ return { ok: true, verifyOutcome };
2780
+ }
2781
+
2695
2782
  // src/pipeline/flows/dynamic/dynamic-body-and-price.ts
2696
2783
  async function resolveDynamicBodyAndPrice(args) {
2697
2784
  const { ctx, pricing, skipBody } = args;
@@ -3058,27 +3145,10 @@ async function runDynamicStreamFlow(args) {
3058
3145
 
3059
3146
  // src/pipeline/flows/dynamic/dynamic-paid.ts
3060
3147
  async function runDynamicPaidFlow(ctx) {
3061
- const { request, routeEntry, deps, report } = ctx;
3062
- const apiKeyGate = await runApiKeyGate(ctx);
3063
- if (!apiKeyGate.ok) return apiKeyGate.response;
3064
- const { account } = apiKeyGate;
3065
- const pricing = selectPricing(routeEntry.pricing, {
3066
- alert: report,
3067
- maxPrice: routeEntry.maxPrice,
3068
- minPrice: routeEntry.minPrice,
3069
- route: routeEntry.key
3070
- });
3071
- const incomingStrategy = selectIncomingStrategy(request, routeEntry.protocols);
3072
- const earlyResolution = await resolveEarlyBody({ ctx, pricing, incomingStrategy });
3073
- if (!earlyResolution.ok) return earlyResolution.response;
3074
- const { earlyBody } = earlyResolution;
3075
- const siwxFastPath = await trySiwxFastPath(ctx, account);
3076
- if (siwxFastPath) return siwxFastPath;
3077
- if (!incomingStrategy) {
3078
- const initError = protocolInitError(routeEntry, deps);
3079
- if (initError) return fail(ctx, 500, initError);
3080
- return buildChallengeResponse(ctx, pricing, earlyBody);
3081
- }
3148
+ const { request, routeEntry } = ctx;
3149
+ const preamble = await runPaidPreamble(ctx);
3150
+ if (preamble.done) return preamble.response;
3151
+ const { account, pricing, incomingStrategy } = preamble;
3082
3152
  const { skipBody, skipHandler } = resolveDynamicPreflight(incomingStrategy, request, routeEntry);
3083
3153
  if (skipHandler) {
3084
3154
  return runDynamicChannelMgmtFlow({
@@ -3092,27 +3162,15 @@ async function runDynamicPaidFlow(ctx) {
3092
3162
  const bodyAndPrice = await resolveDynamicBodyAndPrice({ ctx, pricing, skipBody });
3093
3163
  if (!bodyAndPrice.ok) return bodyAndPrice.response;
3094
3164
  const { parsedBody, price } = bodyAndPrice;
3095
- const verifyOutcome = await incomingStrategy.verify({
3096
- request,
3097
- body: parsedBody,
3098
- price,
3099
- routeEntry,
3100
- deps,
3101
- report
3102
- });
3103
- if (verifyOutcome.ok === false) {
3104
- if (verifyOutcome.kind === "config") {
3105
- return fail(ctx, 500, verifyOutcome.message, parsedBody);
3106
- }
3107
- return buildChallengeResponse(ctx, pricing, parsedBody, verifyOutcome.failure);
3108
- }
3109
- ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
3110
- firePaymentVerified(ctx, {
3111
- protocol: incomingStrategy.protocol,
3112
- payer: verifyOutcome.wallet,
3113
- amount: price,
3114
- network: verifyOutcome.payment.network
3165
+ const verify = await runPaidVerify({
3166
+ ctx,
3167
+ strategy: incomingStrategy,
3168
+ pricing,
3169
+ parsedBody,
3170
+ price
3115
3171
  });
3172
+ if (!verify.ok) return verify.response;
3173
+ const { verifyOutcome } = verify;
3116
3174
  const result = await invokeDynamic(ctx, verifyOutcome, account, parsedBody);
3117
3175
  switch (result.kind) {
3118
3176
  case "stream":
@@ -3244,51 +3302,21 @@ function failureFromCause2(cause) {
3244
3302
 
3245
3303
  // src/pipeline/flows/static/static-paid.ts
3246
3304
  async function runStaticPaidFlow(ctx) {
3247
- const { request, routeEntry, deps, report } = ctx;
3248
- const apiKeyGate = await runApiKeyGate(ctx);
3249
- if (!apiKeyGate.ok) return apiKeyGate.response;
3250
- const { account } = apiKeyGate;
3251
- const pricing = selectPricing(routeEntry.pricing, {
3252
- alert: report,
3253
- maxPrice: routeEntry.maxPrice,
3254
- minPrice: routeEntry.minPrice,
3255
- route: routeEntry.key
3256
- });
3257
- const incomingStrategy = selectIncomingStrategy(request, routeEntry.protocols);
3258
- const earlyResolution = await resolveEarlyBody({ ctx, pricing, incomingStrategy });
3259
- if (!earlyResolution.ok) return earlyResolution.response;
3260
- const { earlyBody } = earlyResolution;
3261
- const siwxFastPath = await trySiwxFastPath(ctx, account);
3262
- if (siwxFastPath) return siwxFastPath;
3263
- if (!incomingStrategy) {
3264
- const initError = protocolInitError(routeEntry, deps);
3265
- if (initError) return fail(ctx, 500, initError);
3266
- return buildChallengeResponse(ctx, pricing, earlyBody);
3267
- }
3305
+ const preamble = await runPaidPreamble(ctx);
3306
+ if (preamble.done) return preamble.response;
3307
+ const { account, pricing, incomingStrategy } = preamble;
3268
3308
  const bodyAndPrice = await resolveStaticBodyAndPrice({ ctx, pricing });
3269
3309
  if (!bodyAndPrice.ok) return bodyAndPrice.response;
3270
3310
  const { parsedBody, price } = bodyAndPrice;
3271
- const verifyOutcome = await incomingStrategy.verify({
3272
- request,
3273
- body: parsedBody,
3274
- price,
3275
- routeEntry,
3276
- deps,
3277
- report
3278
- });
3279
- if (verifyOutcome.ok === false) {
3280
- if (verifyOutcome.kind === "config") {
3281
- return fail(ctx, 500, verifyOutcome.message, parsedBody);
3282
- }
3283
- return buildChallengeResponse(ctx, pricing, parsedBody, verifyOutcome.failure);
3284
- }
3285
- ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
3286
- firePaymentVerified(ctx, {
3287
- protocol: incomingStrategy.protocol,
3288
- payer: verifyOutcome.wallet,
3289
- amount: price,
3290
- network: verifyOutcome.payment.network
3311
+ const verify = await runPaidVerify({
3312
+ ctx,
3313
+ strategy: incomingStrategy,
3314
+ pricing,
3315
+ parsedBody,
3316
+ price
3291
3317
  });
3318
+ if (!verify.ok) return verify.response;
3319
+ const { verifyOutcome } = verify;
3292
3320
  const result = await invokePaidStatic(
3293
3321
  ctx,
3294
3322
  verifyOutcome.wallet,
@@ -3780,55 +3808,35 @@ var RouteBuilder = class _RouteBuilder {
3780
3808
  return next;
3781
3809
  }
3782
3810
  paid(arg, options) {
3783
- return this.applyPaid(normalizePaidArg(this.#s.key, arg, options), "paid");
3811
+ const self = this;
3812
+ return self.applyPaid(normalizePaidArg(self.#s.key, arg, options), "paid");
3784
3813
  }
3785
- /**
3786
- * x402-only handler-computed billing. The handler receives `charge(amount)`
3787
- * and the request settles once for the accumulated total, capped at
3788
- * `maxPrice`. Requires an `'upto'` accept on at least one configured network.
3789
- * Pass a bare string as sugar for `{ maxPrice }`.
3790
- *
3791
- * @example
3792
- * ```ts
3793
- * router.route('llm')
3794
- * .upTo('0.05')
3795
- * .body(schema)
3796
- * .handler(async ({ body, charge }) => { await charge('0.001'); ... });
3797
- * ```
3798
- */
3799
3814
  upTo(arg) {
3800
- return this.applyPaid(normalizeUpToArg(this.#s.key, arg), "upTo");
3815
+ const self = this;
3816
+ return self.applyPaid(normalizeUpToArg(self.#s.key, arg), "upTo");
3801
3817
  }
3802
- /**
3803
- * MPP-only per-tick billing. `.handler()` bills exactly `tickCost`;
3804
- * `.stream()` calls `charge()` (no-arg) per yield, settling per tick up to
3805
- * `maxPrice`. Requires `RouterConfig.mpp.session`.
3806
- *
3807
- * @example
3808
- * ```ts
3809
- * router.route('llm/stream')
3810
- * .metered({ tickCost: '0.0001', maxPrice: '0.05', unitType: 'token' })
3811
- * .stream(async function* ({ charge }) { await charge(); yield 'hi'; });
3812
- * ```
3813
- */
3814
3818
  metered(options) {
3815
- return this.applyPaid(normalizeMeteredArg(this.#s.key, options), "metered");
3819
+ const self = this;
3820
+ return self.applyPaid(normalizeMeteredArg(self.#s.key, options), "metered");
3816
3821
  }
3817
3822
  applyPaid(normalized, method) {
3818
3823
  const { pricing, resolvedOptions, billing, tickCost, unitType, maxPrice } = normalized;
3819
3824
  if (this.#s.authMode === "unprotected") {
3820
- throw new Error(
3821
- `route '${this.#s.key}': Cannot combine .unprotected() and .${method}() on the same route.`
3825
+ throw new RouteDefinitionError(
3826
+ this.#s.key,
3827
+ `Cannot combine .unprotected() and .${method}() on the same route.`
3822
3828
  );
3823
3829
  }
3824
3830
  if (this.#s.pricing !== void 0) {
3825
- throw new Error(
3826
- `route '${this.#s.key}': Cannot combine .paid(), .upTo(), and .metered() \u2014 pick one pricing mode.`
3831
+ throw new RouteDefinitionError(
3832
+ this.#s.key,
3833
+ `Cannot combine .paid(), .upTo(), and .metered() \u2014 pick one pricing mode.`
3827
3834
  );
3828
3835
  }
3829
3836
  if (this.#s.siwxEnabled && billing === "metered") {
3830
- throw new Error(
3831
- `route '${this.#s.key}': Cannot combine .siwx() and .metered() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3837
+ throw new RouteDefinitionError(
3838
+ this.#s.key,
3839
+ `Cannot combine .siwx() and .metered() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3832
3840
  );
3833
3841
  }
3834
3842
  const next = this.fork();
@@ -3836,15 +3844,17 @@ var RouteBuilder = class _RouteBuilder {
3836
3844
  next.#s.pricing = pricing;
3837
3845
  if (billing === "upto") {
3838
3846
  if (resolvedOptions.protocols?.some((p) => p !== "x402")) {
3839
- throw new Error(
3840
- `route '${this.#s.key}': .upTo() is x402-only \u2014 remove the conflicting protocols override.`
3847
+ throw new RouteDefinitionError(
3848
+ this.#s.key,
3849
+ `.upTo() is x402-only \u2014 remove the conflicting protocols override.`
3841
3850
  );
3842
3851
  }
3843
3852
  next.#s.protocols = ["x402"];
3844
3853
  } else if (billing === "metered") {
3845
3854
  if (resolvedOptions.protocols?.some((p) => p !== "mpp")) {
3846
- throw new Error(
3847
- `route '${this.#s.key}': .metered() is MPP-only \u2014 remove the conflicting protocols override.`
3855
+ throw new RouteDefinitionError(
3856
+ this.#s.key,
3857
+ `.metered() is MPP-only \u2014 remove the conflicting protocols override.`
3848
3858
  );
3849
3859
  }
3850
3860
  next.#s.protocols = ["mpp"];
@@ -3868,73 +3878,69 @@ var RouteBuilder = class _RouteBuilder {
3868
3878
  if (typeof pricing === "object" && "tiers" in pricing) {
3869
3879
  for (const [tierKey, tierConfig] of Object.entries(pricing.tiers)) {
3870
3880
  if (!tierKey) {
3871
- throw new Error(`route '${this.#s.key}': tier key cannot be empty`);
3881
+ throw new RouteDefinitionError(this.#s.key, `tier key cannot be empty`);
3872
3882
  }
3873
3883
  if (!isPositiveDecimal(tierConfig.price)) {
3874
- throw new Error(
3875
- `route '${this.#s.key}': tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3884
+ throw new RouteDefinitionError(
3885
+ this.#s.key,
3886
+ `tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3876
3887
  );
3877
3888
  }
3878
3889
  }
3879
3890
  }
3880
3891
  if (billing === "exact" && typeof pricing === "string" && !isPositiveDecimal(pricing)) {
3881
- throw new Error(
3882
- `route '${this.#s.key}': price '${pricing}' must be a positive decimal string`
3892
+ throw new RouteDefinitionError(
3893
+ this.#s.key,
3894
+ `price '${pricing}' must be a positive decimal string`
3883
3895
  );
3884
3896
  }
3885
3897
  if (typeof pricing === "function" && next.#s.maxPrice === void 0) {
3886
- throw new Error(
3887
- `route '${this.#s.key}': dynamic pricing requires maxPrice \u2014 without it, bare probes would advertise a $0 challenge`
3898
+ throw new RouteDefinitionError(
3899
+ this.#s.key,
3900
+ `dynamic pricing requires maxPrice \u2014 without it, bare probes would advertise a $0 challenge`
3888
3901
  );
3889
3902
  }
3890
3903
  if (next.#s.maxPrice !== void 0 && !isPositiveDecimal(next.#s.maxPrice)) {
3891
- throw new Error(
3892
- `route '${this.#s.key}': maxPrice '${next.#s.maxPrice}' must be a positive decimal string`
3904
+ throw new RouteDefinitionError(
3905
+ this.#s.key,
3906
+ `maxPrice '${next.#s.maxPrice}' must be a positive decimal string`
3893
3907
  );
3894
3908
  }
3895
3909
  if (next.#s.minPrice !== void 0 && !isPositiveDecimal(next.#s.minPrice)) {
3896
- throw new Error(
3897
- `route '${this.#s.key}': minPrice '${next.#s.minPrice}' must be a positive decimal string`
3910
+ throw new RouteDefinitionError(
3911
+ this.#s.key,
3912
+ `minPrice '${next.#s.minPrice}' must be a positive decimal string`
3898
3913
  );
3899
3914
  }
3900
3915
  if (next.#s.tickCost !== void 0 && !isPositiveDecimal(next.#s.tickCost)) {
3901
- throw new Error(
3902
- `route '${this.#s.key}': tickCost '${next.#s.tickCost}' must be a positive decimal string`
3916
+ throw new RouteDefinitionError(
3917
+ this.#s.key,
3918
+ `tickCost '${next.#s.tickCost}' must be a positive decimal string`
3903
3919
  );
3904
3920
  }
3905
3921
  return next;
3906
3922
  }
3907
- /**
3908
- * Require Sign-In-with-X wallet identity on this route — clients prove
3909
- * control of a wallet via a signed challenge. Composes with `.paid()` and
3910
- * `.upTo()` for pay-once-then-replay: the first request settles normally,
3911
- * subsequent requests with a valid SIWX signature for the same wallet skip
3912
- * payment (on `.upTo()`, `charge(amount)` becomes a no-op on the replay).
3913
- * Mutually exclusive with `.metered()`.
3914
- *
3915
- * @example
3916
- * ```ts
3917
- * router.route('profile').siwx().handler(async ({ wallet }) => getProfile(wallet));
3918
- * router.route('inbox').paid('0.01').siwx().handler(async ({ wallet }) => getInbox(wallet));
3919
- * ```
3920
- */
3921
3923
  siwx() {
3922
- if (this.#s.authMode === "unprotected") {
3923
- throw new Error(
3924
- `route '${this.#s.key}': Cannot combine .unprotected() and .siwx() on the same route.`
3924
+ const self = this;
3925
+ if (self.#s.authMode === "unprotected") {
3926
+ throw new RouteDefinitionError(
3927
+ self.#s.key,
3928
+ `Cannot combine .unprotected() and .siwx() on the same route.`
3925
3929
  );
3926
3930
  }
3927
- if (this.#s.apiKeyResolver) {
3928
- throw new Error(
3929
- `route '${this.#s.key}': Combining .siwx() and .apiKey() is not supported on the same route.`
3931
+ if (self.#s.apiKeyResolver) {
3932
+ throw new RouteDefinitionError(
3933
+ self.#s.key,
3934
+ `Combining .siwx() and .apiKey() is not supported on the same route.`
3930
3935
  );
3931
3936
  }
3932
- if (this.#s.billing === "metered") {
3933
- throw new Error(
3934
- `route '${this.#s.key}': Cannot combine .metered() and .siwx() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3937
+ if (self.#s.billing === "metered") {
3938
+ throw new RouteDefinitionError(
3939
+ self.#s.key,
3940
+ `Cannot combine .metered() and .siwx() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3935
3941
  );
3936
3942
  }
3937
- const next = this.fork();
3943
+ const next = self.fork();
3938
3944
  next.#s.siwxEnabled = true;
3939
3945
  if (next.#s.authMode === "paid" || next.#s.pricing) {
3940
3946
  next.#s.authMode = "paid";
@@ -3945,59 +3951,48 @@ var RouteBuilder = class _RouteBuilder {
3945
3951
  next.#s.protocols = [];
3946
3952
  return next;
3947
3953
  }
3948
- /**
3949
- * Require an `X-API-Key` header (or `Authorization: Bearer <key>`); the
3950
- * resolver returns the account record, or `null` for 401. Composes with
3951
- * `.paid()` — key is checked first, payment second.
3952
- *
3953
- * @example
3954
- * ```ts
3955
- * router
3956
- * .route('admin/users')
3957
- * .apiKey(async (key) => db.admin.findByKey(key))
3958
- * .handler(async ({ account }) => db.user.list(account.orgId));
3959
- * ```
3960
- */
3961
3954
  apiKey(resolver) {
3962
- if (this.#s.siwxEnabled) {
3963
- throw new Error(
3964
- `route '${this.#s.key}': Combining .apiKey() and .siwx() is not supported on the same route.`
3955
+ const self = this;
3956
+ if (self.#s.authMode === "unprotected") {
3957
+ throw new RouteDefinitionError(
3958
+ self.#s.key,
3959
+ `Cannot combine .unprotected() and .apiKey() on the same route.`
3965
3960
  );
3966
3961
  }
3967
- const next = this.fork();
3962
+ if (self.#s.siwxEnabled) {
3963
+ throw new RouteDefinitionError(
3964
+ self.#s.key,
3965
+ `Combining .apiKey() and .siwx() is not supported on the same route.`
3966
+ );
3967
+ }
3968
+ const next = self.fork();
3968
3969
  next.#s.authMode = "apiKey";
3969
3970
  next.#s.apiKeyResolver = resolver;
3970
3971
  return next;
3971
3972
  }
3972
- /**
3973
- * Mark the route as public — no auth, no payment, no SIWX. The handler
3974
- * receives `null` for `wallet`, `payment`, and `account`.
3975
- *
3976
- * @example
3977
- * ```ts
3978
- * router.route('health').unprotected().handler(async () => ({ status: 'ok' }));
3979
- * ```
3980
- */
3981
3973
  unprotected() {
3982
- if (this.#s.authMode && this.#s.authMode !== "unprotected") {
3983
- throw new Error(
3984
- `route '${this.#s.key}': Cannot combine .unprotected() and .${this.#s.authMode}() on the same route.`
3974
+ const self = this;
3975
+ if (self.#s.authMode && self.#s.authMode !== "unprotected") {
3976
+ throw new RouteDefinitionError(
3977
+ self.#s.key,
3978
+ `Cannot combine .unprotected() and .${self.#s.authMode}() on the same route.`
3985
3979
  );
3986
3980
  }
3987
- if (this.#s.pricing) {
3988
- throw new Error(
3989
- `route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
3981
+ if (self.#s.pricing) {
3982
+ throw new RouteDefinitionError(
3983
+ self.#s.key,
3984
+ `Cannot combine .unprotected() and .paid() on the same route.`
3990
3985
  );
3991
3986
  }
3992
- const next = this.fork();
3987
+ const next = self.fork();
3993
3988
  next.#s.authMode = "unprotected";
3994
3989
  next.#s.protocols = [];
3995
3990
  return next;
3996
3991
  }
3997
3992
  /**
3998
3993
  * Tag the route with an upstream provider for discovery and provider-side
3999
- * monitoring. The provider name and config surface in `well-known` and
4000
- * OpenAPI output.
3994
+ * monitoring. The provider name and config surface in OpenAPI discovery
3995
+ * output.
4001
3996
  *
4002
3997
  * @example
4003
3998
  * ```ts
@@ -4094,8 +4089,8 @@ var RouteBuilder = class _RouteBuilder {
4094
4089
  return next;
4095
4090
  }
4096
4091
  /**
4097
- * Set a human-readable summary of the route. Surfaces in OpenAPI,
4098
- * `well-known`, and `llms.txt` discovery output.
4092
+ * Set a human-readable summary of the route. Surfaces in OpenAPI and
4093
+ * `llms.txt` discovery output.
4099
4094
  *
4100
4095
  * @example
4101
4096
  * ```ts
@@ -4231,38 +4226,46 @@ var RouteBuilder = class _RouteBuilder {
4231
4226
  }
4232
4227
  register(handlerFn, streaming) {
4233
4228
  if (!this.#s.authMode) {
4234
- throw new Error(
4235
- `route '${this.#s.key}': Select an auth mode: .paid(pricing), .upTo(maxPrice), .metered(options), .siwx(), .apiKey(resolver), or .unprotected()`
4229
+ throw new RouteDefinitionError(
4230
+ this.#s.key,
4231
+ `Select an auth mode: .paid(pricing), .upTo(maxPrice), .metered(options), .siwx(), .apiKey(resolver), or .unprotected()`
4236
4232
  );
4237
4233
  }
4238
4234
  if (this.#s.validateFn && !this.#s.bodySchema) {
4239
- throw new Error(
4240
- `route '${this.#s.key}': .validate() requires .body() \u2014 validation runs on parsed body`
4235
+ throw new RouteDefinitionError(
4236
+ this.#s.key,
4237
+ `.validate() requires .body() \u2014 validation runs on parsed body`
4241
4238
  );
4242
4239
  }
4243
4240
  if (this.#s.settlement && !this.#s.pricing) {
4244
- throw new Error(`route '${this.#s.key}': .settlement() requires a paid route`);
4241
+ throw new RouteDefinitionError(this.#s.key, `.settlement() requires a paid route`);
4245
4242
  }
4246
4243
  if (this.#s.mppInfo?.settleBeforeHandler) {
4247
4244
  if (!this.#s.pricing) {
4248
- throw new Error(`route '${this.#s.key}': mpp.settleBeforeHandler requires a paid route`);
4245
+ throw new RouteDefinitionError(
4246
+ this.#s.key,
4247
+ `mpp.settleBeforeHandler requires a paid route`
4248
+ );
4249
4249
  }
4250
4250
  if (this.#s.billing !== "exact") {
4251
- throw new Error(
4252
- `route '${this.#s.key}': mpp.settleBeforeHandler is only supported on .paid() routes`
4251
+ throw new RouteDefinitionError(
4252
+ this.#s.key,
4253
+ `mpp.settleBeforeHandler is only supported on .paid() routes`
4253
4254
  );
4254
4255
  }
4255
4256
  if (this.#s.settlement?.beforeSettle) {
4256
- throw new Error(
4257
- `route '${this.#s.key}': mpp.settleBeforeHandler is incompatible with .settlement({ beforeSettle }) \u2014 MPP payment is already broadcast before the handler runs`
4257
+ throw new RouteDefinitionError(
4258
+ this.#s.key,
4259
+ `mpp.settleBeforeHandler is incompatible with .settlement({ beforeSettle }) \u2014 MPP payment is already broadcast before the handler runs`
4258
4260
  );
4259
4261
  }
4260
4262
  }
4261
4263
  if (this.#s.billing === "upto") {
4262
4264
  const hasUpto = this.#s.deps.x402Accepts.some((accept) => accept.scheme === "upto");
4263
4265
  if (!hasUpto) {
4264
- throw new Error(
4265
- `route '${this.#s.key}': .upTo() requires an 'upto' accept on at least one configured network. Add { scheme: 'upto', network, asset } to RouterConfig.x402.accepts.`
4266
+ throw new RouteDefinitionError(
4267
+ this.#s.key,
4268
+ `.upTo() requires an 'upto' accept on at least one configured network. Add { scheme: 'upto', network, asset } to RouterConfig.x402.accepts.`
4266
4269
  );
4267
4270
  }
4268
4271
  }
@@ -4271,26 +4274,30 @@ var RouteBuilder = class _RouteBuilder {
4271
4274
  (accept) => (accept.scheme ?? "exact") !== "upto"
4272
4275
  );
4273
4276
  if (!hasExact) {
4274
- throw new Error(
4275
- `route '${this.#s.key}': .paid() needs a non-'upto' x402 accept \u2014 an 'upto'-only accept list cannot serve a fixed-price route. Add { scheme: 'exact', network } to RouterConfig.x402.accepts, or use .upTo() for handler-computed billing.`
4277
+ throw new RouteDefinitionError(
4278
+ this.#s.key,
4279
+ `.paid() needs a non-'upto' x402 accept \u2014 an 'upto'-only accept list cannot serve a fixed-price route. Add { scheme: 'exact', network } to RouterConfig.x402.accepts, or use .upTo() for handler-computed billing.`
4276
4280
  );
4277
4281
  }
4278
4282
  }
4279
4283
  if (this.#s.billing === "metered") {
4280
4284
  if (!this.#s.deps.mppSessionConfig) {
4281
- throw new Error(
4282
- `route '${this.#s.key}': .metered() requires MPP session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
4285
+ throw new RouteDefinitionError(
4286
+ this.#s.key,
4287
+ `.metered() requires MPP session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
4283
4288
  );
4284
4289
  }
4285
4290
  }
4286
4291
  if (streaming && this.#s.billing !== "metered") {
4287
- throw new Error(
4288
- `route '${this.#s.key}': .stream() requires .metered() \u2014 static/free/upto routes can't meter per-chunk billing.`
4292
+ throw new RouteDefinitionError(
4293
+ this.#s.key,
4294
+ `.stream() requires .metered() \u2014 static/free/upto routes can't meter per-chunk billing.`
4289
4295
  );
4290
4296
  }
4291
4297
  if (this.#s.description !== void 0 && this.#s.description.length > MAX_X402_DESCRIPTION_LENGTH && this.#s.pricing !== void 0 && this.#s.protocols.includes("x402")) {
4292
- throw new Error(
4293
- `route '${this.#s.key}': .description() is ${this.#s.description.length} chars; must be \u2264 ${MAX_X402_DESCRIPTION_LENGTH} chars \u2014 the CDP x402 facilitator rejects payments whose 402 challenge resource.description exceeds ~500 chars.`
4298
+ throw new RouteDefinitionError(
4299
+ this.#s.key,
4300
+ `.description() is ${this.#s.description.length} chars; must be \u2264 ${MAX_X402_DESCRIPTION_LENGTH} chars \u2014 the CDP x402 facilitator rejects payments whose 402 challenge resource.description exceeds ~500 chars.`
4294
4301
  );
4295
4302
  }
4296
4303
  validateExamples(
@@ -4358,14 +4365,15 @@ function normalizePaidArg(routeKey, arg, options) {
4358
4365
  if ("price" in arg && typeof arg.price === "string") {
4359
4366
  return { pricing: arg.price, resolvedOptions: arg, billing: "exact" };
4360
4367
  }
4361
- throw new Error(
4362
- `route '${routeKey}': .paid() requires one of: a price string, a (body) => string function, { price }, or { field, tiers }. For handler-computed billing use .upTo(); for per-tick billing use .metered().`
4368
+ throw new RouteDefinitionError(
4369
+ routeKey,
4370
+ `.paid() requires one of: a price string, a (body) => string function, { price }, or { field, tiers }. For handler-computed billing use .upTo(); for per-tick billing use .metered().`
4363
4371
  );
4364
4372
  }
4365
4373
  function normalizeUpToArg(routeKey, arg) {
4366
4374
  const options = typeof arg === "string" ? { maxPrice: arg } : arg;
4367
4375
  if (!options.maxPrice) {
4368
- throw new Error(`route '${routeKey}': .upTo() requires maxPrice`);
4376
+ throw new RouteDefinitionError(routeKey, `.upTo() requires maxPrice`);
4369
4377
  }
4370
4378
  return {
4371
4379
  pricing: options.maxPrice,
@@ -4377,10 +4385,10 @@ function normalizeUpToArg(routeKey, arg) {
4377
4385
  }
4378
4386
  function normalizeMeteredArg(routeKey, options) {
4379
4387
  if (!options.maxPrice) {
4380
- throw new Error(`route '${routeKey}': .metered() requires maxPrice`);
4388
+ throw new RouteDefinitionError(routeKey, `.metered() requires maxPrice`);
4381
4389
  }
4382
4390
  if (!options.tickCost) {
4383
- throw new Error(`route '${routeKey}': .metered() requires tickCost`);
4391
+ throw new RouteDefinitionError(routeKey, `.metered() requires tickCost`);
4384
4392
  }
4385
4393
  return {
4386
4394
  pricing: options.maxPrice,
@@ -4687,9 +4695,9 @@ function createNotFoundHandler(baseUrl) {
4687
4695
  code: "route_not_found",
4688
4696
  error: "Route not found. Rediscover this origin and retry with the current discovery document.",
4689
4697
  requestedUrl: request.url,
4698
+ // The deprecated /.well-known/x402 surface is intentionally not advertised.
4690
4699
  discovery: {
4691
4700
  openapi: `${normalizedBase}/openapi.json`,
4692
- wellKnown: `${normalizedBase}/.well-known/x402`,
4693
4701
  llmsTxt: `${normalizedBase}/llms.txt`
4694
4702
  }
4695
4703
  },
@@ -4723,6 +4731,7 @@ function formatRouterConfigIssues(issues) {
4723
4731
 
4724
4732
  // src/config/schema.ts
4725
4733
  var import_zod = require("zod");
4734
+ init_accepts();
4726
4735
  init_constants();
4727
4736
 
4728
4737
  // src/config/utils.ts
@@ -4911,18 +4920,8 @@ function deriveBaseUrlEnv(env) {
4911
4920
  BASE_URL: vercelBaseUrl
4912
4921
  };
4913
4922
  }
4914
- function getConfiguredX402Accepts2(config) {
4915
- if (config.x402?.accepts?.length) return [...config.x402.accepts];
4916
- return [
4917
- {
4918
- scheme: "exact",
4919
- network: config.network ?? BASE_MAINNET_NETWORK,
4920
- payTo: config.payeeAddress
4921
- }
4922
- ];
4923
- }
4924
4923
  function validateX402Config(config, env) {
4925
- const accepts = getConfiguredX402Accepts2(config);
4924
+ const accepts = getConfiguredX402Accepts(config);
4926
4925
  const issues = [];
4927
4926
  const push = (code, message) => issues.push({ code, protocol: "x402", message });
4928
4927
  if (accepts.length === 0) {
@@ -5223,7 +5222,8 @@ function getMppxRequestContext(args) {
5223
5222
  recipient: mppConfig.recipient ?? payeeAddress,
5224
5223
  getClient,
5225
5224
  ...feePayerAccount ? { feePayer: feePayerAccount } : {},
5226
- ...resolvedStore ? { store: resolvedStore } : {}
5225
+ ...resolvedStore ? { store: resolvedStore } : {},
5226
+ ...mppConfig.feePayerPolicy ? { feePayerPolicy: mppConfig.feePayerPolicy } : {}
5227
5227
  }),
5228
5228
  ...sessionEnabled ? [
5229
5229
  tempo.session({
@@ -5261,7 +5261,7 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5261
5261
  try {
5262
5262
  const { Mppx, tempo } = await import("mppx/server");
5263
5263
  const { createClient, http } = await import("viem");
5264
- const { tempo: tempoChain } = await import("viem/chains");
5264
+ const { tempo: tempoChain } = await import("viem/tempo/chains");
5265
5265
  const { privateKeyToAccount: privateKeyToAccount2 } = await import("viem/accounts");
5266
5266
  const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL ?? DEFAULT_TEMPO_RPC_URL;
5267
5267
  const tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
@@ -5279,7 +5279,9 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5279
5279
  getClient,
5280
5280
  ...operatorAccount ? { account: operatorAccount } : {},
5281
5281
  ...feePayerAccount ? { feePayer: feePayerAccount } : {},
5282
- ...resolvedStore ? { store: resolvedStore } : {}
5282
+ ...resolvedStore ? { store: resolvedStore } : {},
5283
+ ...mppConfig.feePayerPolicy ? { feePayerPolicy: mppConfig.feePayerPolicy } : {},
5284
+ ...mppConfig.session?.settlementSchedule ? { settlementSchedule: mppConfig.session.settlementSchedule } : {}
5283
5285
  };
5284
5286
  const mppxArgs = {
5285
5287
  Mppx,
@@ -5295,6 +5297,7 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5295
5297
  };
5296
5298
  const primary = getMppxRequestContext(mppxArgs);
5297
5299
  const streaming = getMppxStreamingContext(mppxArgs);
5300
+ subscribePaymentFailedAlerts(config.plugin, [primary, streaming]);
5298
5301
  const mppx = {
5299
5302
  charge: primary.charge,
5300
5303
  ...primary.session ? { sessionRequest: primary.session } : {},
@@ -5305,6 +5308,41 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5305
5308
  return { initError: err instanceof Error ? err.message : String(err) };
5306
5309
  }
5307
5310
  }
5311
+ function subscribePaymentFailedAlerts(plugin, instances) {
5312
+ if (!plugin?.onAlert) return;
5313
+ const ctx = {
5314
+ requestId: "mppx",
5315
+ route: "mpp",
5316
+ walletAddress: null,
5317
+ clientId: null,
5318
+ sessionId: null,
5319
+ verifiedWallet: null,
5320
+ setVerifiedWallet() {
5321
+ }
5322
+ };
5323
+ for (const instance of instances) {
5324
+ const events = instance;
5325
+ if (typeof events?.onPaymentFailed !== "function") continue;
5326
+ events.onPaymentFailed((payload) => {
5327
+ try {
5328
+ const error = payload?.error ?? {};
5329
+ firePluginHook(plugin, "onAlert", ctx, {
5330
+ level: (error.status ?? 402) >= 500 ? "error" : "warn",
5331
+ message: `MPP payment failed: ${error.message ?? error.name ?? "unknown error"}`,
5332
+ route: "mpp",
5333
+ meta: {
5334
+ ...payload?.method ? { method: `${payload.method.name}/${payload.method.intent}` } : {},
5335
+ ...error.type ? { errorType: error.type } : {},
5336
+ ...error.status !== void 0 ? { status: error.status } : {},
5337
+ ...error.hint ? { hint: error.hint } : {},
5338
+ ...payload?.credential?.source ? { payer: payload.credential.source } : {}
5339
+ }
5340
+ });
5341
+ } catch {
5342
+ }
5343
+ });
5344
+ }
5345
+ }
5308
5346
 
5309
5347
  // src/index.ts
5310
5348
  init_constants();
@@ -5448,6 +5486,7 @@ function createRouterFromEnv(options) {
5448
5486
  DEFAULT_SOLANA_FACILITATOR_URL,
5449
5487
  DEFAULT_TEMPO_RPC_URL,
5450
5488
  HttpError,
5489
+ RouteDefinitionError,
5451
5490
  RouterConfigError,
5452
5491
  SOLANA_MAINNET_NETWORK,
5453
5492
  TEMPO_USDC_ADDRESS,