@agentcash/router 1.7.1 → 1.9.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.d.ts CHANGED
@@ -58,13 +58,13 @@ interface PluginContext {
58
58
  setVerifiedWallet(address: string): void;
59
59
  }
60
60
  interface PaymentEvent {
61
- protocol: 'x402' | 'mpp';
61
+ protocol: ProtocolType;
62
62
  payer: string;
63
63
  amount: string;
64
64
  network: string;
65
65
  }
66
66
  interface SettlementEvent {
67
- protocol: 'x402' | 'mpp';
67
+ protocol: ProtocolType;
68
68
  payer: string;
69
69
  transaction: string;
70
70
  network: string;
@@ -188,7 +188,7 @@ interface X402AcceptBase {
188
188
  extra?: Record<string, unknown>;
189
189
  }
190
190
  interface X402AcceptConfig extends X402AcceptBase {
191
- /** `'exact'` for fixed-price one-shot payments; `'upto'` for settle-≤-cap (required for `.paid({ dynamic: true })` on x402). @default 'exact' */
191
+ /** `'exact'` for fixed-price one-shot payments; `'upto'` for settle-≤-cap (required for `.upTo()` routes). @default 'exact' */
192
192
  scheme?: string;
193
193
  /** Per-accept payee override. Function form receives the request and parsed body for dynamic recipient routing. Falls back to `RouterConfig.payeeAddress`. */
194
194
  payTo?: PayToConfig;
@@ -212,14 +212,29 @@ interface PaidOptions {
212
212
  protocols?: ProtocolType[];
213
213
  maxPrice?: string;
214
214
  minPrice?: string;
215
- /** Override the payment recipient. String for static, function for dynamic (receives the Request). */
215
+ /** Override the payment recipient. String for static, function for body-derived (receives the Request). */
216
216
  payTo?: PayToConfig;
217
217
  /** Override MPP protocol metadata in x-payment-info discovery. */
218
218
  mpp?: MppProtocolInfo;
219
- /** Handler-driven dynamic pricing: handler calls `charge()` per tick, total billed is `tickCost × calls` capped at `maxPrice`. Requires `maxPrice`. Incompatible with tiered pricing. On x402 needs an `upto` accept; on MPP needs `RouterConfig.mpp.session`. */
220
- dynamic?: boolean;
221
- /** Per-tick cost (positive decimal-dollar string). Required for `.paid({ dynamic: true })`. Also the voucher-headroom granularity for MPP sessions. */
222
- tickCost?: string;
219
+ }
220
+ type PaidArg = (PaidOptions & {
221
+ price: string;
222
+ }) | (PaidOptions & {
223
+ field: string;
224
+ tiers: Record<string, TierConfig>;
225
+ default?: string;
226
+ });
227
+ interface UpToOptions extends Omit<PaidOptions, 'maxPrice'> {
228
+ /** Cap on total billed amount; handler-accumulated `charge(amount)` calls cannot exceed this. */
229
+ maxPrice: string;
230
+ /** Cosmetic unit label for 402 challenges / UIs. Does not affect billing. */
231
+ unitType?: string;
232
+ }
233
+ interface MeteredOptions extends Omit<PaidOptions, 'maxPrice'> {
234
+ /** Per-tick cost (positive decimal-dollar string). On `.handler()` bills exactly this per request; on `.stream()` is the voucher-headroom granularity. */
235
+ tickCost: string;
236
+ /** Cap on total billed amount (streaming only — request-mode bills exactly `tickCost`). */
237
+ maxPrice: string;
223
238
  /** Cosmetic unit label for 402 challenges / UIs (e.g. `'token'`, `'byte'`). Does not affect billing. */
224
239
  unitType?: string;
225
240
  }
@@ -267,6 +282,7 @@ interface SettlementLifecycle<TBody = unknown> {
267
282
  onSettlementError?: (ctx: SettlementErrorContext<TBody>) => void | Promise<void>;
268
283
  }
269
284
  type ChargeFn = () => Promise<void>;
285
+ type UptoChargeFn = (amount: string) => Promise<void>;
270
286
  interface HandlerContext<TBody = undefined, TQuery = undefined> {
271
287
  body: TBody;
272
288
  query: TQuery;
@@ -279,10 +295,14 @@ interface HandlerContext<TBody = undefined, TQuery = undefined> {
279
295
  alert: AlertFn;
280
296
  setVerifiedWallet: (addr: string) => void;
281
297
  }
282
- /** Handler context for streaming `.paid({ dynamic: true })` handlers (async generators). Call `charge()` once per billable unit. */
298
+ /** Handler context for streaming `.metered()` handlers (async generators). Call `charge()` once per billable unit. */
283
299
  interface StreamingHandlerContext<TBody = undefined, TQuery = undefined> extends HandlerContext<TBody, TQuery> {
284
300
  charge: ChargeFn;
285
301
  }
302
+ /** Handler context for `.upTo()` routes (x402-only). Call `charge(amount)` one or more times; the request settles for the accumulated total capped at `maxPrice`. */
303
+ interface UptoHandlerContext<TBody = undefined, TQuery = undefined> extends HandlerContext<TBody, TQuery> {
304
+ charge: UptoChargeFn;
305
+ }
286
306
  type OveragePolicy = 'same-rate' | 'increased-rate' | 'hard-stop';
287
307
  type QuotaLevel = 'healthy' | 'warn' | 'critical';
288
308
  interface QuotaInfo {
@@ -316,9 +336,9 @@ interface RouteEntry {
316
336
  */
317
337
  siwxEnabled?: boolean;
318
338
  pricing?: PricingConfig;
319
- /** When true the route is dynamic-priced; bills `tickCost` per request (request-mode) or per `charge()` call (streaming). */
320
- dynamicPrice?: boolean;
321
- /** True iff handler is an async generator. Streaming handlers settle per-tick over SSE; non-streaming dynamic handlers bill exactly `tickCost` per request. Set by the builder at `.handler(fn)` time. */
339
+ /** `'exact'` settles a fixed price once; `'upto'` (x402-only) settles the handler-accumulated `charge(amount)` total capped at `maxPrice`; `'metered'` (MPP-only) bills per `tickCost`. */
340
+ billing: 'exact' | 'upto' | 'metered';
341
+ /** True iff handler is an async generator. Streaming handlers settle per-tick over SSE; non-streaming metered handlers bill exactly `tickCost` per request. Set by the builder at `.handler(fn)` time. */
322
342
  streaming?: boolean;
323
343
  protocols: ProtocolType[];
324
344
  bodySchema?: ZodType;
@@ -340,7 +360,7 @@ interface RouteEntry {
340
360
  validateFn?: (body: unknown) => void | Promise<void>;
341
361
  settlement?: SettlementLifecycle;
342
362
  mppInfo?: MppProtocolInfo;
343
- /** Per-tick cost (decimal-dollar). Required when `dynamicPrice` is true. */
363
+ /** Per-tick cost (decimal-dollar). Required when `metered` is true. */
344
364
  tickCost?: string;
345
365
  /** Cosmetic unit label for 402 challenges and client UIs. */
346
366
  unitType?: string;
@@ -361,7 +381,7 @@ interface DiscoveryConfig {
361
381
  serverUrl?: string;
362
382
  }
363
383
  interface RouterConfig {
364
- /** Default payee for paid routes — populates `payTo` on the auto-generated x402 `exact` accept and acts as the MPP `recipient` fallback. Override per-protocol via `x402.accepts[i].payTo` / `mpp.recipient`, or per-route via `.paid({ payTo })`. */
384
+ /** Default payee for paid routes — populates `payTo` on the auto-generated x402 `exact` accept and acts as the MPP `recipient` fallback. Override per-protocol via `x402.accepts[i].payTo` / `mpp.recipient`, or per-route via the `payTo` option on `.paid()` / `.upTo()` / `.metered()`. */
365
385
  payeeAddress?: string;
366
386
  /** Origin URL (required). Used as 402 realm, discovery base, OpenAPI server, and MPP memo prefix — must match the public domain or payment matching breaks. */
367
387
  baseUrl: string;
@@ -369,7 +389,7 @@ interface RouterConfig {
369
389
  network?: string;
370
390
  /** x402 protocol settings. Omit to default to a single `exact`/USDC accept on `network` paid to `payeeAddress`, verified via the Coinbase default facilitator (requires `CDP_API_KEY_ID`/`CDP_API_KEY_SECRET`). */
371
391
  x402?: {
372
- /** Explicit accepts list (scheme + network + asset). Overrides the auto-generated default. Add an `upto` accept here to enable `.paid({ dynamic: true })` on x402. */
392
+ /** Explicit accepts list (scheme + network + asset). Overrides the auto-generated default. Add an `upto` accept here to enable `.upTo()` routes. */
373
393
  accepts?: X402AcceptConfig[];
374
394
  /** Per-chain facilitator overrides (`evm`/`solana`). Defaults to the Coinbase facilitator on EVM; set `solana` to accept Solana payments. */
375
395
  facilitators?: X402FacilitatorsConfig;
@@ -397,7 +417,7 @@ interface RouterConfig {
397
417
  operatorKey?: string;
398
418
  /** Hex private key. Sponsors gas for client channel open/topUp. MUST resolve to a different address than `operatorKey` — Tempo rejects sender===feePayer. Validated at init. Omit to make clients pay their own gas. */
399
419
  feePayerKey?: string;
400
- /** Enables MPP payment-channel sessions for `.paid({ dynamic: true })` routes (registers both request and SSE session middleware). Also requires `mpp.operatorKey`. */
420
+ /** Enables MPP payment-channel sessions for `.metered()` routes (registers both request and SSE session middleware). Also requires `mpp.operatorKey`. */
401
421
  session?: {
402
422
  /** Suggested deposit on the 402 challenge = `tickCost × depositMultiplier` USDC. Route `maxPrice` overrides. @default 10 */
403
423
  depositMultiplier?: number;
@@ -473,98 +493,99 @@ interface RouterDeps {
473
493
  tempoClient?: viem.Client | null;
474
494
  }
475
495
 
476
- /** @deprecated alias kept for downstream consumers; use `RouterDeps`. */
477
- type OrchestrateDeps = RouterDeps;
478
-
479
496
  type True = true;
480
497
  type False = false;
498
+ declare const ROUTE_ERROR: unique symbol;
499
+ interface RouteError<M extends string> {
500
+ readonly [ROUTE_ERROR]: M;
501
+ }
481
502
  type InputTypeFor<TBody, TQuery> = [TBody] extends [undefined] ? [TQuery] extends [undefined] ? never : TQuery : TBody;
482
503
  type RequestHandlerFn<TBody, TQuery> = (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown>;
504
+ type UptoHandlerFn<TBody, TQuery> = (ctx: UptoHandlerContext<TBody, TQuery>) => Promise<unknown>;
483
505
  type StreamingHandlerFn<TBody, TQuery> = (ctx: StreamingHandlerContext<TBody, TQuery>) => AsyncIterable<unknown>;
484
- type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? {
485
- __missing: 'Call .body(schema) dynamic/tiered pricing requires a body schema to resolve the price against';
486
- } : RequestHandlerFn<TBody, TQuery> : {
487
- __missing: 'Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()';
488
- };
489
- type StreamArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean, IsDynamic extends boolean> = HasAuth extends true ? IsDynamic extends true ? [NeedsBody, HasBody] extends [true, false] ? {
490
- __missing: 'Call .body(schema) — dynamic pricing requires a body schema to resolve the price against';
491
- } : StreamingHandlerFn<TBody, TQuery> : {
492
- __missing: 'Streaming handlers require .paid({ dynamic: true, tickCost, unitType, maxPrice }) — static/free routes cannot meter per-chunk billing';
493
- } : {
494
- __missing: 'Select an auth mode: .paid({ dynamic: true, ... }) — streaming requires handler-driven dynamic pricing';
495
- };
506
+ /** Discriminator threaded through the builder so `.handler()` / `.stream()` can pick the right handler shape. */
507
+ type BillingMode = 'none' | 'upto' | 'metered';
508
+ type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean, Bill extends BillingMode> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? RouteError<'Call .body(schema) — body-derived/tiered pricing reads the parsed body'> : Bill extends 'upto' ? UptoHandlerFn<TBody, TQuery> : RequestHandlerFn<TBody, TQuery> : RouteError<'Pick an auth mode first: .paid(...), .upTo(...), .metered(...), .siwx(), .apiKey(...), or .unprotected()'>;
509
+ type StreamArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean, Bill extends BillingMode> = HasAuth extends true ? Bill extends 'metered' ? [NeedsBody, HasBody] extends [true, false] ? RouteError<'Call .body(schema) — metered pricing reads the parsed body'> : StreamingHandlerFn<TBody, TQuery> : Bill extends 'upto' ? RouteError<'Streaming is not supported on .upTo() — use .metered() on MPP for per-yield billing'> : RouteError<'Streaming requires .metered({ tickCost, maxPrice }) — static/free routes cannot meter per-chunk billing'> : RouteError<'Pick an auth mode first: .metered({ ... }) — streaming requires metered pricing'>;
496
510
  interface RouteBuilderDefaults {
497
511
  protocols?: ProtocolType[];
498
512
  }
499
- declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false, IsDynamic extends boolean = false> {
513
+ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false, Bill extends BillingMode = 'none'> {
500
514
  #private;
501
- constructor(key: string, registry: RouteRegistry, deps: OrchestrateDeps, defaults?: RouteBuilderDefaults);
515
+ constructor(key: string, registry: RouteRegistry, deps: RouterDeps, defaults?: RouteBuilderDefaults);
502
516
  private fork;
503
517
  /**
504
- * Charge a fixed price per request, denominated in USDC as a decimal string.
518
+ * Fixed-price string sugar: `paid('0.01')` charges 0.01 USDC per request.
505
519
  *
506
520
  * @example
507
521
  * ```ts
508
522
  * router.route('search').paid('0.01').handler(handler);
509
523
  * ```
510
524
  */
511
- paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, IsDynamic>;
525
+ paid(price: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, 'none'>;
512
526
  /**
513
- * Configure handler-driven dynamic pricing each tick costs `tickCost` USDC,
514
- * capped at `maxPrice`. Pair with `.handler()` for one-tick-per-request
515
- * billing, or with `.stream()` for per-yield metering.
527
+ * Compute the price from the parsed body before issuing the 402 challenge.
528
+ * Throw an `HttpError` from the pricing function to reject the request
529
+ * before payment is requested. Requires `.body(schema)`.
516
530
  *
517
531
  * @example
518
532
  * ```ts
519
- * router
520
- * .route('llm/stream')
521
- * .paid({ dynamic: true, tickCost: '0.0001', unitType: 'token', maxPrice: '0.05' })
522
- * .stream(async function* ({ charge }) { await charge(); yield 'hi'; });
533
+ * router.route('llm')
534
+ * .paid((body) => `${body.tokens * 0.0001}`, { maxPrice: '5.00' })
535
+ * .body(schema)
536
+ * .handler(handler);
523
537
  * ```
524
538
  */
525
- paid(options: PaidOptions & {
526
- dynamic: true;
527
- maxPrice: string;
528
- }): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, True>;
539
+ paid<TBodyIn>(fn: (body: TBodyIn) => string | Promise<string>, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, 'none'>;
529
540
  /**
530
- * Compute the price from the parsed body before issuing the 402 challenge.
531
- * Throw an `HttpError` from the pricing function to reject the request before
532
- * payment is requested.
541
+ * Options-object form of fixed or body-derived pricing. Pass exactly one of:
542
+ *
543
+ * - `{ price }` — fixed price (object form of the string sugar).
544
+ * - `{ field, tiers, default? }` — pick a tier from `body[field]`.
545
+ *
546
+ * Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`) live
547
+ * alongside the pricing shape. For handler-computed billing use `.upTo()`;
548
+ * for per-tick billing use `.metered()`.
533
549
  *
534
550
  * @example
535
551
  * ```ts
536
- * router
537
- * .route('llm')
538
- * .paid((body) => `${body.tokens * 0.0001}`, { maxPrice: '5.00' })
539
- * .body(schema)
540
- * .handler(handler);
552
+ * router.route('upload')
553
+ * .paid({ field: 'size', tiers: { sm: { price: '0.01' }, lg: { price: '0.10' } } })
554
+ * .body(schema).handler(handler);
541
555
  * ```
542
556
  */
543
- paid<TBodyIn>(pricing: (body: TBodyIn) => string | Promise<string>, options?: PaidOptions & {
544
- maxPrice?: string;
545
- }): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, IsDynamic>;
557
+ paid<T extends PaidArg>(arg: T): RouteBuilder<TBody, TQuery, TOutput, True, T extends {
558
+ tiers: Record<string, TierConfig>;
559
+ } ? True : False, HasBody, 'none'>;
546
560
  /**
547
- * Select a price tier from `body[field]`, optionally falling back to the
548
- * `default` tier when the value is missing. The 402 challenge advertises the
549
- * highest tier price.
561
+ * x402-only handler-computed billing. The handler receives `charge(amount)`
562
+ * and the request settles once for the accumulated total, capped at
563
+ * `maxPrice`. Requires an `'upto'` accept on at least one configured network.
564
+ * Pass a bare string as sugar for `{ maxPrice }`.
550
565
  *
551
566
  * @example
552
567
  * ```ts
553
- * router
554
- * .route('upload')
555
- * .paid({ field: 'size', tiers: { sm: { price: '0.01' }, lg: { price: '0.10' } } })
568
+ * router.route('llm')
569
+ * .upTo('0.05')
556
570
  * .body(schema)
557
- * .handler(handler);
571
+ * .handler(async ({ body, charge }) => { await charge('0.001'); ... });
572
+ * ```
573
+ */
574
+ upTo(arg: string | UpToOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, 'upto'>;
575
+ /**
576
+ * MPP-only per-tick billing. `.handler()` bills exactly `tickCost`;
577
+ * `.stream()` calls `charge()` (no-arg) per yield, settling per tick up to
578
+ * `maxPrice`. Requires `RouterConfig.mpp.session`.
579
+ *
580
+ * @example
581
+ * ```ts
582
+ * router.route('llm/stream')
583
+ * .metered({ tickCost: '0.0001', maxPrice: '0.05', unitType: 'token' })
584
+ * .stream(async function* ({ charge }) { await charge(); yield 'hi'; });
558
585
  * ```
559
586
  */
560
- paid(pricing: {
561
- field: string;
562
- tiers: Record<string, {
563
- price: string;
564
- label?: string;
565
- }>;
566
- default?: string;
567
- }, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, IsDynamic>;
587
+ metered(options: MeteredOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, 'metered'>;
588
+ private applyPaid;
568
589
  /**
569
590
  * Require Sign-In-with-X wallet identity on this route — clients prove
570
591
  * control of a wallet via a signed challenge. Combine with `.paid()` to gate
@@ -575,7 +596,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
575
596
  * router.route('profile').siwx().handler(async ({ wallet }) => getProfile(wallet));
576
597
  * ```
577
598
  */
578
- siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, IsDynamic>;
599
+ siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, Bill>;
579
600
  /**
580
601
  * Require an `X-API-Key` header (or `Authorization: Bearer <key>`); the
581
602
  * resolver returns the account record, or `null` for 401. Composes with
@@ -589,7 +610,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
589
610
  * .handler(async ({ account }) => db.user.list(account.orgId));
590
611
  * ```
591
612
  */
592
- apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody, IsDynamic>;
613
+ apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody, Bill>;
593
614
  /**
594
615
  * Mark the route as public — no auth, no payment, no SIWX. The handler
595
616
  * receives `null` for `wallet`, `payment`, and `account`.
@@ -599,7 +620,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
599
620
  * router.route('health').unprotected().handler(async () => ({ status: 'ok' }));
600
621
  * ```
601
622
  */
602
- unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, IsDynamic>;
623
+ unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, Bill>;
603
624
  /**
604
625
  * Tag the route with an upstream provider for discovery and provider-side
605
626
  * monitoring. The provider name and config surface in `well-known` and
@@ -625,7 +646,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
625
646
  * .handler(async ({ body }) => search(body.query));
626
647
  * ```
627
648
  */
628
- body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True, IsDynamic>;
649
+ body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True, Bill>;
629
650
  /**
630
651
  * Declare a query-string Zod schema and switch the route to `GET`. Parsed
631
652
  * query is typed as `ctx.query` in the handler. Use `.inputExample()` to
@@ -637,7 +658,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
637
658
  * .handler(async ({ query }) => getById(query.id));
638
659
  * ```
639
660
  */
640
- query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody, IsDynamic>;
661
+ query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
641
662
  /**
642
663
  * Declare the response output's Zod schema for OpenAPI generation. The
643
664
  * runtime does not validate handler return values — use Zod's `.parse()`
@@ -650,7 +671,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
650
671
  * .handler(async () => ({ result: 'ok' }));
651
672
  * ```
652
673
  */
653
- output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody, IsDynamic>;
674
+ output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody, Bill>;
654
675
  /**
655
676
  * Attach an example of the request body or query for discovery output,
656
677
  * validated against the registered schema at registration.
@@ -660,7 +681,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
660
681
  * .body(searchSchema).inputExample({ query: 'cats' });
661
682
  * ```
662
683
  */
663
- inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, IsDynamic>;
684
+ inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
664
685
  /**
665
686
  * Attach an example response for discovery output, validated against the
666
687
  * registered output schema at registration.
@@ -670,7 +691,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
670
691
  * .output(resultSchema).outputExample({ result: 'ok' });
671
692
  * ```
672
693
  */
673
- outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, IsDynamic>;
694
+ outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
674
695
  /**
675
696
  * Set a human-readable summary of the route. Surfaces in OpenAPI,
676
697
  * `well-known`, and `llms.txt` discovery output.
@@ -715,7 +736,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
715
736
  * });
716
737
  * ```
717
738
  */
718
- validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, IsDynamic>;
739
+ validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
719
740
  /**
720
741
  * Hook into the settlement lifecycle. `beforeSettle` runs after the handler
721
742
  * succeeds but before on-chain settlement and can cancel the charge;
@@ -729,7 +750,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
729
750
  * });
730
751
  * ```
731
752
  */
732
- settlement(lifecycle: SettlementLifecycle<TBody>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, IsDynamic>;
753
+ settlement(lifecycle: SettlementLifecycle<TBody>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
733
754
  /**
734
755
  * Register the request handler and return the Next.js route function. The
735
756
  * handler receives a typed context and may return a value (serialized to
@@ -744,17 +765,17 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
744
765
  * .handler(async ({ body, wallet }) => searchService(body, wallet));
745
766
  * ```
746
767
  */
747
- handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody>): (request: NextRequest) => Promise<Response>;
768
+ handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody, Bill>): (request: NextRequest) => Promise<Response>;
748
769
  /**
749
770
  * Register a streaming handler (`async function*`) and return the Next.js
750
771
  * route function. Each `charge()` call bills one tick (`tickCost` USDC) up
751
- * to `maxPrice`; requires `.paid({ dynamic: true, ... })` and MPP session mode.
772
+ * to `maxPrice`; requires `.metered({ ... })` and MPP session mode.
752
773
  *
753
774
  * @example
754
775
  * ```ts
755
776
  * export const POST = router
756
777
  * .route('llm/stream')
757
- * .paid({ dynamic: true, tickCost: '0.0001', unitType: 'token', maxPrice: '0.05' })
778
+ * .metered({ tickCost: '0.0001', maxPrice: '0.05', unitType: 'token' })
758
779
  * .body(schema)
759
780
  * .stream(async function* ({ body, charge }) {
760
781
  * for await (const token of streamLLM(body.prompt)) {
@@ -764,11 +785,11 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
764
785
  * });
765
786
  * ```
766
787
  */
767
- stream(fn: StreamArg<TBody, TQuery, HasAuth, NeedsBody, HasBody, IsDynamic>): (request: NextRequest) => Promise<Response>;
788
+ stream(fn: StreamArg<TBody, TQuery, HasAuth, NeedsBody, HasBody, Bill>): (request: NextRequest) => Promise<Response>;
768
789
  private register;
769
790
  }
770
791
 
771
- type RouterConfigIssueCode = 'missing_base_url' | 'invalid_base_url' | 'empty_protocols' | 'missing_x402_accepts' | 'missing_x402_network' | 'unsupported_x402_network' | 'missing_x402_asset' | 'invalid_x402_decimals' | 'missing_x402_payee' | 'invalid_x402_payee' | 'invalid_solana_payee' | 'invalid_solana_facilitator_url' | 'missing_cdp_keys' | 'placeholder_payee' | 'missing_mpp_config' | 'missing_mpp_secret_key' | 'missing_mpp_currency' | 'invalid_mpp_currency' | 'missing_mpp_recipient' | 'invalid_mpp_recipient' | 'missing_mpp_rpc_url' | 'invalid_mpp_rpc_url' | 'invalid_mpp_fee_payer_key' | 'invalid_mpp_operator_key' | 'mpp_operator_equals_fee_payer' | 'missing_discovery_title' | 'missing_discovery_description' | 'missing_discovery_guidance' | 'invalid_server_url' | 'kv_url_without_token' | 'kv_token_without_url' | 'invalid_kv_url' | 'missing_kv_in_production';
792
+ type RouterConfigIssueCode = 'missing_base_url' | 'invalid_base_url' | 'empty_protocols' | 'missing_x402_accepts' | 'missing_x402_network' | 'unsupported_x402_network' | 'missing_x402_asset' | 'invalid_x402_decimals' | 'missing_x402_payee' | 'invalid_x402_payee' | 'invalid_solana_payee' | 'invalid_solana_facilitator_url' | 'missing_cdp_keys' | 'placeholder_payee' | 'missing_mpp_config' | 'missing_mpp_secret_key' | 'missing_mpp_currency' | 'invalid_mpp_currency' | 'missing_mpp_recipient' | 'invalid_mpp_recipient' | 'missing_mpp_rpc_url' | 'invalid_mpp_rpc_url' | 'invalid_mpp_fee_payer_key' | 'invalid_mpp_operator_key' | 'mpp_operator_equals_fee_payer' | 'mpp_operator_recipient_mismatch' | 'missing_discovery_title' | 'missing_discovery_description' | 'missing_discovery_guidance' | 'invalid_server_url' | 'kv_url_without_token' | 'kv_token_without_url' | 'invalid_kv_url' | 'missing_kv_in_production';
772
793
  type RouterConfigIssueSeverity = 'error' | 'warning';
773
794
  interface RouterConfigIssue {
774
795
  code: RouterConfigIssueCode;