@piprail/sdk 1.13.1 → 1.15.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 (30) hide show
  1. package/CHAINS.md +3 -2
  2. package/CHANGELOG.md +69 -0
  3. package/ERRORS.md +17 -2
  4. package/README.md +57 -4
  5. package/STANDARDS.md +4 -0
  6. package/dist/{algorand-MXUSKX46.cjs → algorand-EJ3S2V7E.cjs} +17 -17
  7. package/dist/{algorand-WGVF4KTU.js → algorand-F3OYB534.js} +1 -1
  8. package/dist/{aptos-YT7SXWPF.cjs → aptos-GJGIZHNI.cjs} +16 -16
  9. package/dist/{aptos-LPBLSEIQ.js → aptos-SUXOVP7B.js} +1 -1
  10. package/dist/{chunk-SVMGHASK.js → chunk-ILPABTI2.js} +18 -1
  11. package/dist/{chunk-MDLZJGLY.cjs → chunk-PA6YD3HL.cjs} +35 -18
  12. package/dist/index.cjs +800 -163
  13. package/dist/index.d.cts +472 -42
  14. package/dist/index.d.ts +472 -42
  15. package/dist/index.js +695 -58
  16. package/dist/{near-K6BDBABG.js → near-LM7S3WUD.js} +1 -1
  17. package/dist/{near-7ZDNISUX.cjs → near-ZJLZE26R.cjs} +19 -19
  18. package/dist/{solana-PU7N2M64.cjs → solana-MPPE6K24.cjs} +14 -14
  19. package/dist/{solana-S3UFI3FE.js → solana-WDKWWF33.js} +1 -1
  20. package/dist/{stellar-Q5PO23SC.js → stellar-FIJPQZVW.js} +1 -1
  21. package/dist/{stellar-VDQOFQEO.cjs → stellar-XHLLNHQP.cjs} +21 -21
  22. package/dist/{sui-FKSMLKRF.cjs → sui-6CVLEXLA.cjs} +17 -17
  23. package/dist/{sui-WOXRKJXS.js → sui-B7AVN7NK.js} +1 -1
  24. package/dist/{ton-WPTXGLVK.js → ton-CHJ26BVA.js} +1 -1
  25. package/dist/{ton-VK6KRJHP.cjs → ton-RNEFN25G.cjs} +14 -14
  26. package/dist/{tron-6GXBXTR4.js → tron-DD3JDROV.js} +1 -1
  27. package/dist/{tron-WLOF5OUV.cjs → tron-TKJHNFGM.cjs} +24 -24
  28. package/dist/{xrpl-HEAPEXAM.js → xrpl-GTUPP6SK.js} +1 -1
  29. package/dist/{xrpl-CMNI25BV.cjs → xrpl-XN2NBNGI.cjs} +21 -21
  30. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -84,10 +84,13 @@ interface X402ExactAcceptEntry {
84
84
  extra: {
85
85
  /** The exact-EVM transfer method. PipRail self-settles EIP-3009 today. */
86
86
  assetTransferMethod: 'eip3009';
87
- /** EIP-712 domain name of the token (USDC: "USD Coin"; EURC: "EURC"). Read on-chain. */
88
- name: string;
89
- /** EIP-712 domain version of the token (USDC/EURC: "2"). Read on-chain. */
90
- version: string;
87
+ /** EIP-712 domain name of the token. OPTIONAL per the exact-EVM scheme (only
88
+ * `assetTransferMethod` is required) — a foreign rail may omit it. NEVER assumed
89
+ * from the symbol (USDC's on-chain name() is "USD Coin", not "USDC"); a PipRail gate
90
+ * READS it on-chain, and the PipRail buyer RE-DERIVES it on-chain and ignores this. */
91
+ name?: string;
92
+ /** EIP-712 domain version of the token (USDC: "2"). OPTIONAL (see `name`); read/re-derived on-chain. */
93
+ version?: string;
91
94
  /** Confirmations the gate waits for before granting access — mirrors the gate's
92
95
  * `minConfirmations`, so the exact rail honours the same reorg safety as onchain-proof.
93
96
  * A PipRail convenience (standard clients ignore unknown keys). */
@@ -221,13 +224,54 @@ declare const HEADER_RESPONSE_V1 = "x-payment-response";
221
224
  declare function buildChallengeHeader(challenge: X402Challenge): string;
222
225
  declare function buildReceiptHeader(receipt: X402Receipt): string;
223
226
  declare function buildSignatureHeader(signature: X402PaymentSignature): string;
227
+ /**
228
+ * Build the v2 PAYMENT-SIGNATURE header value for a standard x402 `exact` payment:
229
+ * base64 of `{ x402Version: 2, accepted, payload }`. `accepted` is the chosen rail
230
+ * echoed back VERBATIM from the challenge's `accepts[]` (preserving any extra keys a
231
+ * facilitator needs); `payload` is the EIP-3009 `{ signature, authorization }` the
232
+ * buyer's EVM driver produced. Chain-agnostic (pure JSON/base64) — the driver owns
233
+ * the signing, this only frames it for the wire. Round-trips through
234
+ * {@link parseExactPaymentHeader}. (The `onchain-proof` counterpart is
235
+ * {@link buildSignatureHeader}; the v1 flat-shape utility is `encodeXPaymentHeader`.)
236
+ */
237
+ declare function buildExactSignatureHeader(input: {
238
+ accepted: X402ExactAcceptEntry;
239
+ payload: ExactPaymentPayload;
240
+ }): string;
224
241
  /**
225
242
  * Parse the PAYMENT-REQUIRED challenge from a 402 response. Prefers the
226
243
  * `payment-required` header, falls back to the JSON body.
227
244
  */
228
245
  declare function parseChallenge(response: Response): Promise<X402Challenge | null>;
229
- /** Parse the PAYMENT-RESPONSE receipt header on a 200 settlement. */
246
+ /** Parse the PAYMENT-RESPONSE receipt header on a 200 settlement. Reads the v2
247
+ * `payment-response` header, falling back to the v1 `x-payment-response` a foreign
248
+ * server may set. Returns a fully-formed {@link X402Receipt} only (a bare foreign
249
+ * exact SettleResponse without a `payer` is read by {@link parseSettleResponse}). */
230
250
  declare function parseReceipt(response: Response): X402Receipt | null;
251
+ /**
252
+ * A standard x402 SettleResponse as the BUYER reads it off a settled (non-402)
253
+ * response. The `success` flag is authoritative: `false` is an EXPLICIT facilitator/
254
+ * server REJECTION (the buyer must NOT record a spend), `true` is an affirmative
255
+ * settlement. `transaction` is the on-chain settle tx the facilitator broadcast.
256
+ */
257
+ interface SettleOutcome {
258
+ success: boolean;
259
+ transaction?: string;
260
+ network?: string;
261
+ payer?: string;
262
+ errorReason?: string;
263
+ }
264
+ /**
265
+ * Read a standard x402 SettleResponse for the BUYER, from the v2 `payment-response`
266
+ * header (or the v1 `x-payment-response` fallback). Returns `null` when neither
267
+ * header is present, unparseable, or carries no boolean `success` — i.e. when the
268
+ * server served the resource WITHOUT echoing a settle result, which the exact buyer
269
+ * treats as an affirmative 2xx settlement (receipt-less). When a body IS present with
270
+ * a boolean `success`, that flag is returned verbatim: ONLY an explicit
271
+ * `success: false` is a rejection. Used by the exact pay path to tell a real
272
+ * settlement from a phantom one (never record a spend on `success:false`).
273
+ */
274
+ declare function parseSettleResponse(response: Response): SettleOutcome | null;
231
275
  /** Parse a PAYMENT-SIGNATURE header value (server side). */
232
276
  declare function parseSignatureHeader(value: string): X402PaymentSignature | null;
233
277
  /**
@@ -347,6 +391,11 @@ declare const CHAINS: {
347
391
  decimals: number;
348
392
  symbol: string;
349
393
  };
394
+ EURC: {
395
+ address: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c";
396
+ decimals: number;
397
+ symbol: string;
398
+ };
350
399
  };
351
400
  };
352
401
  base: {
@@ -561,7 +610,7 @@ declare const CHAINS: {
561
610
  value: bigint;
562
611
  yParity: number;
563
612
  accessList: viem.AccessList;
564
- authorizationList?: undefined | undefined;
613
+ authorizationList? /** EVM chain id, e.g. 5000 for Mantle. */: undefined | undefined;
565
614
  blobVersionedHashes?: undefined | undefined;
566
615
  chainId: number;
567
616
  type: "eip1559";
@@ -570,7 +619,7 @@ declare const CHAINS: {
570
619
  maxFeePerGas: bigint;
571
620
  maxPriorityFeePerGas: bigint;
572
621
  isSystemTx?: undefined | undefined;
573
- mint?: undefined | undefined;
622
+ mint? /** Known tokens on this chain (empty for unknown custom chains). */: undefined | undefined;
574
623
  sourceHash?: undefined | undefined;
575
624
  } | {
576
625
  blockHash: `0x${string}` | null;
@@ -682,6 +731,11 @@ declare const CHAINS: {
682
731
  decimals: number;
683
732
  symbol: string;
684
733
  };
734
+ EURC: {
735
+ address: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42";
736
+ decimals: number;
737
+ symbol: string;
738
+ };
685
739
  };
686
740
  };
687
741
  arbitrum: {
@@ -965,7 +1019,7 @@ declare const CHAINS: {
965
1019
  maxPriorityFeePerGas: bigint;
966
1020
  isSystemTx?: undefined | undefined;
967
1021
  mint?: undefined | undefined;
968
- sourceHash?: undefined | undefined;
1022
+ sourceHash? /** Known tokens on this chain (empty for unknown custom chains). */: undefined | undefined;
969
1023
  } | {
970
1024
  blockHash: `0x${string}` | null;
971
1025
  blockNumber: bigint | null;
@@ -1264,6 +1318,11 @@ declare const CHAINS: {
1264
1318
  decimals: number;
1265
1319
  symbol: string;
1266
1320
  };
1321
+ EURC: {
1322
+ address: "0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD";
1323
+ decimals: number;
1324
+ symbol: string;
1325
+ };
1267
1326
  };
1268
1327
  };
1269
1328
  mantle: {
@@ -4118,9 +4177,11 @@ interface ResolvedNetwork {
4118
4177
  * never throws for a transient RPC issue — it falls back to a 'heuristic'
4119
4178
  * constant. `opts.from` (the payer's address) sharpens chains whose fee
4120
4179
  * depends on the sender (notably Tron energy); omit it for a typical estimate.
4121
- * Payer-side + informational — the gate never calls this.
4180
+ * Payer-side + informational — the gate never calls this. Accepts either rail
4181
+ * shape ({@link X402AnyAccept}): a standard `exact` rail estimates the BUYER's gas
4182
+ * as ~0 (the server/facilitator broadcasts the signed authorization).
4122
4183
  */
4123
- estimateCost(accept: X402AcceptEntry, opts?: {
4184
+ estimateCost(accept: X402AnyAccept, opts?: {
4124
4185
  from?: string;
4125
4186
  }): Promise<CostEstimate>;
4126
4187
  /**
@@ -4148,6 +4209,32 @@ interface ResolvedNetwork {
4148
4209
  ready: boolean | 'n/a' | 'unknown';
4149
4210
  reason?: RecipientReason;
4150
4211
  }>;
4212
+ /**
4213
+ * OPTIONAL (EVM + EIP-3009 only) — the BUYER counterpart to {@link settleExactSelf}.
4214
+ * Build + EIP-712-sign an EIP-3009 `transferWithAuthorization` for a standard x402
4215
+ * `exact` rail, so a PipRail agent can PAY any standard x402 server (not just PipRail's
4216
+ * own `onchain-proof` gates). The client frames the returned `payload` + `accepted` echo
4217
+ * into the `PAYMENT-SIGNATURE` header and re-requests; the server / merchant-chosen
4218
+ * facilitator BROADCASTS the authorization (the buyer never broadcasts and spends ~0 gas).
4219
+ *
4220
+ * Re-derives the token's EIP-712 domain ON-CHAIN (never trusts the server-supplied
4221
+ * `extra.{name,version}`), generates a CSPRNG 32-byte nonce + current unix time
4222
+ * internally, and signs through the wallet client (bring-your-own JsonRpcAccount safe).
4223
+ * THROWS a typed `PipRailError` (`UnsupportedSchemeError`) when the asset isn't EIP-3009
4224
+ * (USDT/native/plain ERC-20) or the signer is a contract / EIP-1271 / EIP-7702 account.
4225
+ *
4226
+ * The third optional `exact` method (after {@link exactDomain}/{@link settleExactSelf});
4227
+ * optional `?` is the gather gate — non-EVM families omit it, so an `exact` rail is never
4228
+ * gathered/paid on those chains. Returns the signed payload, the chosen-rail echo (the
4229
+ * server's RAW rail, verbatim, so a facilitator's extra keys survive), the payer address,
4230
+ * and the nonce (for the client's spend record + a re-present-the-same-auth retry).
4231
+ */
4232
+ payExact?(wallet: WalletHandle, accept: X402ExactAcceptEntry): Promise<{
4233
+ payload: ExactPaymentPayload;
4234
+ accepted: X402ExactAcceptEntry;
4235
+ payerFrom: string;
4236
+ nonce: string;
4237
+ }>;
4151
4238
  /**
4152
4239
  * OPTIONAL — a DISCOVERY signer for the bound wallet: its public address plus a
4153
4240
  * message signer, used only for ownership proofs + SIWX index registration,
@@ -4169,7 +4256,8 @@ interface ResolvedNetwork {
4169
4256
  * `null` when the asset is NOT an EIP-3009 token (no `transferWithAuthorization` —
4170
4257
  * e.g. USDT, native coin, or a plain ERC-20), so the gate can refuse to advertise a
4171
4258
  * standard `exact` rail for it. Never derived from the symbol (USDC's domain name is
4172
- * "USD Coin", not "USDC"; EURC's is "EURC"). Called once at exact-rail resolution
4259
+ * "USD Coin", not "USDC"; EURC's is "Euro Coin" on Ethereum/Avalanche but "EURC" on Base —
4260
+ * which is exactly why it must be READ, never assumed). Called once at exact-rail resolution
4173
4261
  * (cached by the gate). RPC-read; may throw on a transient read (the gate surfaces a
4174
4262
  * clear config error). The first of two optional server methods for the `exact` rail.
4175
4263
  */
@@ -4439,6 +4527,31 @@ interface PaymentPolicy {
4439
4527
  * declined, because its true decimals can't be verified. When true, the
4440
4528
  * server-stated decimals are trusted (the explicit, opt-in risk). */
4441
4529
  allowUnknownTokens?: boolean;
4530
+ /**
4531
+ * Session time-to-live in SECONDS, relative to session start (client
4532
+ * construction). After the deadline EVERY payment is refused
4533
+ * (`SESSION_EXPIRED`), regardless of amount — a headless agent's time leash.
4534
+ * When both `ttlSeconds` and `expiresAt` are set, the EARLIER deadline wins.
4535
+ * Opt-in; omit for no time limit (default). PROCESS-SCOPED — resets on restart.
4536
+ */
4537
+ ttlSeconds?: number;
4538
+ /**
4539
+ * Absolute session deadline as epoch MILLISECONDS (matches `Date.now()`).
4540
+ * SDK-only — there is no MCP env knob (the MCP exposes only the relative-seconds
4541
+ * `PIPRAIL_TTL`). A small value expires immediately BY DESIGN; it is NOT
4542
+ * auto-corrected from seconds, so pass milliseconds. Opt-in; omit for no limit.
4543
+ */
4544
+ expiresAt?: number;
4545
+ /**
4546
+ * Rolling-window spend cap per (network, asset), in human units (e.g. '1.00').
4547
+ * Refuses a payment that would push spend within the last {@link windowSeconds}
4548
+ * past this cap (`OUTSIDE_WINDOW`) — a rate limit on top of the lifetime
4549
+ * `maxTotal`. REQUIRED together with `windowSeconds`: setting one without the
4550
+ * other is a config error (a half-armed leash is forbidden). Opt-in; heavier.
4551
+ */
4552
+ windowTotal?: string;
4553
+ /** Rolling-window width in seconds for {@link windowTotal}. REQUIRED together with it. */
4554
+ windowSeconds?: number;
4442
4555
  }
4443
4556
  /** What the policy reasons over — built by the client from the chosen accept. */
4444
4557
  interface PaymentIntent {
@@ -4457,19 +4570,50 @@ interface PaymentIntent {
4457
4570
  /** Did the driver's `describeAsset` recognise this asset? */
4458
4571
  recognized: boolean;
4459
4572
  }
4573
+ /**
4574
+ * A typed, machine-readable code for WHICH guard refused a payment — set by
4575
+ * `deny()` alongside the human `reason`, so the client routes a denial to the
4576
+ * right {@link PayBlocker}/`reasonCode` WITHOUT substring-matching the prose
4577
+ * (which would silently break on a wording tweak).
4578
+ */
4579
+ type PolicyDenyCode = 'CHAIN' | 'HOST' | 'UNKNOWN_TOKEN' | 'TOKEN' | 'MAX_AMOUNT' | 'MAX_TOTAL' | 'SESSION_EXPIRED' | 'WINDOW_TOTAL';
4460
4580
  interface PolicyDecision {
4461
4581
  allowed: boolean;
4462
4582
  /** Why it was refused (only when `allowed === false`). */
4463
4583
  reason?: string;
4584
+ /** Which guard fired, as a typed enum (only when `allowed === false`). */
4585
+ code?: PolicyDenyCode;
4586
+ }
4587
+ /**
4588
+ * INTERNAL — the injected clock + the pre-sliced per-asset window total the
4589
+ * client builds for the otherwise-pure {@link evaluatePolicy}. NOT exported: the
4590
+ * client is the only producer, and keeping it private de-risks a future
4591
+ * fold-`spentForAssetBase`-into-ctx refactor. All time state is process-scoped.
4592
+ */
4593
+ interface PolicyContext {
4594
+ /** A single `Date.now()` captured per quote — the expiry check and the window edge share it. */
4595
+ now: number;
4596
+ /** Session clock origin (epoch-ms) = client construction. */
4597
+ sessionStart: number;
4598
+ /** Base units spent on THIS (network, asset) within the rolling window; `0n` when no window cap. */
4599
+ spentInWindowBase: bigint;
4464
4600
  }
4465
4601
  /**
4466
4602
  * Evaluate a payment against the policy. `spentForAssetBase` is the running
4467
4603
  * total already spent on THIS (network, asset) — supplied by the client's
4468
4604
  * ledger — and powers the per-asset `maxTotal` cap.
4469
4605
  *
4470
- * Checks run cheapest-first; the first failure wins so the reason is specific.
4606
+ * The optional `ctx` carries the injected clock + the pre-sliced window total so
4607
+ * this function stays PURE (no `Date.now()` inside). Omit `ctx` and no time check
4608
+ * runs — behaviour is byte-identical to a time-free policy.
4609
+ *
4610
+ * Checks run in a pinned, deterministic order, first-failure-wins so the reason
4611
+ * is specific: **session expiry → chains → hosts → unknown-token → tokens →
4612
+ * maxAmount → maxTotal → windowTotal**. Expiry is first because it's
4613
+ * session-global (not asset-scoped) — an expired session must always report
4614
+ * expiry, not whichever other gate happens to also fail.
4471
4615
  */
4472
- declare function evaluatePolicy(intent: PaymentIntent, policy: PaymentPolicy | undefined, spentForAssetBase: bigint): PolicyDecision;
4616
+ declare function evaluatePolicy(intent: PaymentIntent, policy: PaymentPolicy | undefined, spentForAssetBase: bigint, ctx?: PolicyContext): PolicyDecision;
4473
4617
 
4474
4618
  interface SpendRecord {
4475
4619
  url: string;
@@ -4504,11 +4648,15 @@ interface SpendSummary {
4504
4648
  records: SpendRecord[];
4505
4649
  }
4506
4650
 
4651
+ /** The payment schemes a client can settle: PipRail's native `onchain-proof` (the
4652
+ * default) and the standard x402 `exact` rail (EVM + EIP-3009 only, opt-in). */
4653
+ type PaymentScheme = 'onchain-proof' | 'exact';
4654
+
4507
4655
  /** Observability events. `ref` is the proof — a chain-specific id (EVM tx hash, Solana signature, TON locator, Stellar tx hash). */
4508
4656
  type PipRailEvent = {
4509
4657
  kind: 'payment-required';
4510
4658
  challenge: X402Challenge;
4511
- accept: X402AcceptEntry;
4659
+ accept: X402AnyAccept;
4512
4660
  } | {
4513
4661
  kind: 'payment-broadcast';
4514
4662
  ref: string;
@@ -4528,9 +4676,18 @@ type PipRailEvent = {
4528
4676
  kind: 'payment-unconfirmed';
4529
4677
  ref: string;
4530
4678
  reason: string;
4531
- } | {
4679
+ }
4680
+ /**
4681
+ * The payment settled. `receipt` is PipRail's rich {@link X402Receipt} when the
4682
+ * server returns one (its own gate, or a facilitator that echoes the full shape);
4683
+ * `settle` is the standard x402 SettleResponse (`{ success, transaction, … }`) on
4684
+ * conformant third-party-facilitator interop, where the lean SettleResponse has no
4685
+ * rich receipt — read `settle.transaction` for the on-chain settle tx there.
4686
+ */
4687
+ | {
4532
4688
  kind: 'payment-settled';
4533
4689
  receipt: X402Receipt | null;
4690
+ settle?: SettleOutcome;
4534
4691
  } | {
4535
4692
  kind: 'payment-failed';
4536
4693
  reason: string;
@@ -4607,6 +4764,9 @@ interface PipRailQuote {
4607
4764
  withinPolicy: boolean;
4608
4765
  /** Why the policy would refuse it (only when `withinPolicy === false`). */
4609
4766
  policyReason?: string;
4767
+ /** The TYPED reason the policy refused it (only when `withinPolicy === false`) —
4768
+ * routes the denial to the right blocker/`reasonCode` without parsing prose. */
4769
+ policyCode?: PolicyDenyCode;
4610
4770
  }
4611
4771
  /**
4612
4772
  * What `client.estimateCost(url)` returns: the priced payment requirement plus
@@ -4622,13 +4782,14 @@ interface PipRailCostQuote {
4622
4782
  cost: CostEstimate;
4623
4783
  }
4624
4784
  /** A hard reason a rail can't be settled right now — each maps to a concrete fix. */
4625
- type PayBlocker = 'INSUFFICIENT_TOKEN' | 'INSUFFICIENT_GAS' | 'RECIPIENT_NOT_READY' | 'OUTSIDE_POLICY';
4785
+ type PayBlocker = 'INSUFFICIENT_TOKEN' | 'INSUFFICIENT_GAS' | 'RECIPIENT_NOT_READY' | 'OUTSIDE_POLICY' | 'OUTSIDE_WINDOW';
4626
4786
  /** A soft flag — never blocks, always worth surfacing to the agent. */
4627
4787
  type PayWarning = 'SYMBOL_MISMATCH' | 'BALANCE_UNREADABLE' | 'RECIPIENT_READINESS_UNKNOWN' | 'GAS_HEURISTIC' | 'THIN_GAS_MARGIN';
4628
4788
  /** One offered rail, fully analysed against the bound wallet's own holdings. */
4629
4789
  interface PayOption {
4630
- /** The rail this analyses (one entry from the 402's accepts[]). */
4631
- accept: X402AcceptEntry;
4790
+ /** The rail this analyses (one entry from the 402's accepts[]) — an
4791
+ * `onchain-proof` rail or, when `schemes` enables it, a standard `exact` rail. */
4792
+ accept: X402AnyAccept;
4632
4793
  /** The priced requirement — TRUE decimals/symbol + the policy verdict. */
4633
4794
  quote: PipRailQuote;
4634
4795
  /** Estimated native-coin gas to send it (cost.basis surfaced). */
@@ -4676,6 +4837,61 @@ interface PaymentPlan {
4676
4837
  options: PayOption[];
4677
4838
  /** When NOT payable: one human, actionable sentence on exactly what to do. */
4678
4839
  fundingHint: string | null;
4840
+ /**
4841
+ * Read-only TIME envelope — present ONLY when the policy configures one
4842
+ * (`ttlSeconds`/`expiresAt`). Lets a headless (Mode A) agent SEE its remaining
4843
+ * time leash before paying, rather than discovering it by hitting a decline.
4844
+ *
4845
+ * PROCESS-SCOPED: resets to a fresh window on restart; for crash-loop-resistant
4846
+ * limits supply a pluggable durable store (the `isUsed`/`markUsed` analogue).
4847
+ * `secondsRemaining` is a best-effort host wall-clock estimate, clamped ≥ 0.
4848
+ */
4849
+ session?: {
4850
+ expiresAt: number | null;
4851
+ secondsRemaining: number | null;
4852
+ };
4853
+ }
4854
+ /**
4855
+ * A read-only view of the spend leash for a Mode-A agent — `client.budget()`.
4856
+ * Composes the in-memory ledger + the configured policy WITHOUT coupling them.
4857
+ *
4858
+ * PROCESS-SCOPED: every figure resets on restart — the session IS the process.
4859
+ * For crash-loop-resistant limits supply a pluggable durable store (the
4860
+ * `isUsed`/`markUsed` analogue). `secondsRemaining` is clamped ≥ 0.
4861
+ */
4862
+ interface SessionBudget {
4863
+ /** The session's time envelope (null fields when no `ttlSeconds`/`expiresAt`). */
4864
+ session: {
4865
+ /** Session start, ISO. */
4866
+ start: string;
4867
+ /** Deadline as ISO, or null when no time limit is configured. */
4868
+ expiresAt: string | null;
4869
+ /** Seconds until expiry (clamped ≥ 0), or null when no time limit. */
4870
+ secondsRemaining: number | null;
4871
+ };
4872
+ /** Per-(network, asset) money leash — ONE row per pair the ledger has seen. */
4873
+ byAsset: SpendRemaining[];
4874
+ }
4875
+ /**
4876
+ * Per-(network, asset) remaining budget — the money half of the leash. One row
4877
+ * per pair the LEDGER already holds (decimals are known only after the first
4878
+ * spend), so a never-spent pair simply isn't a row. `cap`/`remaining` are
4879
+ * `undefined` when no `policy.maxTotal` is set (unbounded). Never a cross-token
4880
+ * sum — there is no price oracle.
4881
+ */
4882
+ interface SpendRemaining {
4883
+ network: Caip2;
4884
+ asset: string;
4885
+ symbol?: string;
4886
+ decimals: number;
4887
+ /** Base units spent so far on this pair. */
4888
+ spentBase: string;
4889
+ /** The `maxTotal` cap in base units; undefined when unbounded. */
4890
+ capBase?: string;
4891
+ /** `max(0, cap − spent)` in base units; undefined when unbounded. */
4892
+ remainingBase?: string;
4893
+ /** `remainingBase` in human units; undefined when unbounded. */
4894
+ remainingFormatted?: string;
4679
4895
  }
4680
4896
  interface PipRailClientOptions {
4681
4897
  /** Wallet for the chosen chain family. */
@@ -4725,6 +4941,24 @@ interface PipRailClientOptions {
4725
4941
  * per call with `fetch(url, { autoRoute: true })`.
4726
4942
  */
4727
4943
  autoRoute?: boolean;
4944
+ /**
4945
+ * Which payment SCHEMES this client may settle. Default **`['onchain-proof']`**
4946
+ * (defaults never change): the zero-config client pays only PipRail's native
4947
+ * backendless rail, exactly as before. Add `'exact'` to ALSO pay standard x402
4948
+ * `exact` rails — letting the agent pay ANY standard x402 server (the dominant
4949
+ * `exact`-on-Base-via-CDP web), not just PipRail's own gates:
4950
+ *
4951
+ * new PipRailClient({ chain: 'base', wallet, schemes: ['onchain-proof', 'exact'] })
4952
+ *
4953
+ * `exact` is **EVM + EIP-3009 only** (USDC/EURC); it's silently ignored on a
4954
+ * non-EVM chain, for USDT/native, or for a token the SDK can't price — those keep
4955
+ * `onchain-proof`. The agent signs an EIP-3009 authorization with its OWN wallet
4956
+ * and the server / merchant-chosen facilitator broadcasts it (the buyer pays ~0
4957
+ * gas; PipRail hosts/settles nothing). The same `policy` + `onBeforePay` gate it
4958
+ * BEFORE signing. **Verify against your target facilitator before production.**
4959
+ * Override per call with `fetch(url, { schemes })`.
4960
+ */
4961
+ schemes?: PaymentScheme[];
4728
4962
  /** Logger hook. Default no-op. */
4729
4963
  onEvent?: (event: PipRailEvent) => void;
4730
4964
  }
@@ -4786,11 +5020,24 @@ declare class PipRailClient {
4786
5020
  private readonly ledger;
4787
5021
  private bound?;
4788
5022
  constructor(opts: PipRailClientOptions);
5023
+ /**
5024
+ * Fail LOUDLY at construction on a misconfigured time policy — a security
5025
+ * boundary must never silently half-arm. Two invariants (a misconfiguration is
5026
+ * a programmer error → `TypeError`, no new SDK error code):
5027
+ * - the rolling window needs BOTH `windowTotal` and `windowSeconds`, or NEITHER
5028
+ * (one alone is a leash that silently doesn't bite);
5029
+ * - `ttlSeconds` must be a positive, safe integer whose `*1000` deadline stays
5030
+ * within `Number.MAX_SAFE_INTEGER` (else the arithmetic would lose precision).
5031
+ */
5032
+ private assertPolicyTimeOptions;
4789
5033
  /** Emit an observability event, never letting a throwing handler break the
4790
5034
  * payment flow (mirrors the server gate's `onPaid` isolation). */
4791
5035
  private safeEmit;
4792
5036
  /** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */
4793
5037
  private ensure;
5038
+ /** Resolve the effective scheme set: a per-call override, else the constructor's
5039
+ * `schemes`, else the `onchain-proof`-only default. */
5040
+ private resolveSchemes;
4794
5041
  /** GET that auto-handles 402. Pass a full URL to any x402-gated endpoint. */
4795
5042
  get(url: string, init?: RequestInit): Promise<Response>;
4796
5043
  /**
@@ -4827,6 +5074,25 @@ declare class PipRailClient {
4827
5074
  /** Aggregated snapshot of every payment this client has settled — total
4828
5075
  * count, cumulative spend per token, and the individual records. */
4829
5076
  spent(): SpendSummary;
5077
+ /**
5078
+ * Read-only budget + time leash for a Mode-A (headless) agent — the policy IS
5079
+ * the consent, and this is how the agent SEES what's left of it before paying.
5080
+ * Composes the in-memory ledger with the configured policy; never throws, moves
5081
+ * no funds. PROCESS-SCOPED — every figure resets on restart (see {@link SessionBudget}).
5082
+ */
5083
+ budget(): SessionBudget;
5084
+ /**
5085
+ * Per-(network, asset) remaining budget — ONE row per pair the ledger already
5086
+ * holds (decimals are known only after the first spend), so a fresh client with
5087
+ * a `maxTotal` set returns `[]` until its first payment. `cap`/`remaining` are
5088
+ * `undefined` when no `maxTotal` is configured (unbounded). Pure + in-memory;
5089
+ * never throws, never sums across tokens (no price oracle). PROCESS-SCOPED.
5090
+ */
5091
+ remaining(): SpendRemaining[];
5092
+ /** The read-only TIME envelope for the plan/budget surfaces, or `undefined`
5093
+ * when no session deadline (`ttlSeconds`/`expiresAt`) is set. `secondsRemaining`
5094
+ * is clamped ≥ 0 — a best-effort host wall-clock estimate. */
5095
+ private sessionView;
4830
5096
  /**
4831
5097
  * Plan a payment for a gated URL — WITHOUT paying. The read-only completion of
4832
5098
  * the `quote()` → `estimateCost()` → **`planPayment()`** trio: it surveys every
@@ -4869,8 +5135,8 @@ declare class PipRailClient {
4869
5135
  * - A resource just listed via {@link register} may not appear yet — 402 Index reviews
4870
5136
  * before publishing, so retry with a brief backoff if a fresh listing is missing.
4871
5137
  * - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays
4872
- * only `onchain-proof` rails directly (pay `exact` resources with the experimental
4873
- * `drivers/evm/exact.ts`).
5138
+ * `onchain-proof` rails by default, and standard `exact` rails too once you opt in
5139
+ * with `schemes: ['onchain-proof', 'exact']` (EVM + EIP-3009 — USDC/EURC).
4874
5140
  */
4875
5141
  discover(opts?: DiscoverOptions): Promise<DiscoveredResource[]>;
4876
5142
  /**
@@ -4937,17 +5203,21 @@ declare class PipRailClient {
4937
5203
  * twice (once to fetch the 402, once with the proof attached). One-shot
4938
5204
  * streams throw `NonReplayableBodyError`.
4939
5205
  */
4940
- fetch(url: string, init?: RequestInit): Promise<Response>;
5206
+ fetch(url: string, init?: RequestInit & {
5207
+ autoRoute?: boolean;
5208
+ schemes?: PaymentScheme[];
5209
+ }): Promise<Response>;
4941
5210
  /**
4942
5211
  * From a confirmed-402 response: parse the challenge, mount + bind the
4943
5212
  * network, pick the accept the client can pay, and build its quote. Shared by
4944
5213
  * `quote()` (read-only) and `fetch()` (which then authorises + pays).
4945
5214
  */
4946
5215
  private resolveChallenge;
4947
- /** The candidate accepts this client could pay: our scheme, on the bound network.
4948
- * A dual-advertised challenge may also carry standard `exact` rails the PipRail
4949
- * client ignores those (it pays the backendless `onchain-proof` rail); the type
4950
- * predicate narrows the `X402AnyAccept` union to the rails we settle. */
5216
+ /** The candidate accepts this client could pay, on the bound network. Always the
5217
+ * backendless `onchain-proof` rails; PLUS standard `exact` rails when `schemes`
5218
+ * enables them AND the driver can settle them (EVM `payExact` + a recognised
5219
+ * EIP-3009 token). `onchain-proof` is gathered FIRST so default selection is
5220
+ * unchanged when `exact` is off. */
4951
5221
  private gatherCandidates;
4952
5222
  /** Build the full {@link PaymentPlan} from an already-parsed challenge + bound
4953
5223
  * net/wallet. Shared by `planPayment` (read-only) and `fetch`'s autoRoute. */
@@ -4959,12 +5229,32 @@ declare class PipRailClient {
4959
5229
  * driver's describeAsset) + the policy verdict + a symbol-mismatch flag. */
4960
5230
  private buildQuote;
4961
5231
  /** Enforce the spend policy and the onBeforePay hook — both refuse by
4962
- * throwing PaymentDeclinedError, before any funds move. */
5232
+ * throwing PaymentDeclinedError, before any funds move. Every refusal carries
5233
+ * a typed `reasonCode` so an agent can branch on the cause (and spot a
5234
+ * TERMINAL expiry/approval decline it must not retry) without parsing prose. */
4963
5235
  private authorize;
4964
5236
  /** Record a settled payment in the ledger (true decimals for the running total). */
4965
5237
  private recordSpend;
4966
5238
  private payAndConfirm;
4967
5239
  private retryWithProof;
5240
+ /**
5241
+ * Pay a standard x402 `exact` rail — a SEPARATE, fundamentally more conservative
5242
+ * path than {@link retryWithProof}. The buyer SIGNS an EIP-3009 authorization ONCE
5243
+ * (the driver's `payExact`) and the server / merchant-chosen facilitator BROADCASTS
5244
+ * it synchronously, so a blind re-POST of a still-in-flight authorization could
5245
+ * double-BROADCAST it. Hence, unlike the onchain-proof loop:
5246
+ *
5247
+ * • sign exactly once — reuse the SAME header on every retry, never re-sign;
5248
+ * • retry ONLY an explicit 402 (a definitive pre-broadcast rejection), bounded
5249
+ * well under `maxTimeoutSeconds` so the loop can't outlive the authorization;
5250
+ * • a post-POST transport error/timeout → {@link PaymentTimeoutError} carrying the
5251
+ * nonce (the facilitator MAY have settled — verify on-chain, NEVER re-pay);
5252
+ * • a 5xx → return as-is (server settle failure; the authorization stays valid +
5253
+ * its nonce unused) — no settled event, no spend;
5254
+ * • a 200 whose SettleResponse says `success:false` → a rejection, NEVER a spend;
5255
+ * • the spend is recorded EXACTLY ONCE, on an affirmative settlement only.
5256
+ */
5257
+ private payExactRail;
4968
5258
  }
4969
5259
  /**
4970
5260
  * Plan a payment ACROSS several single-chain clients — the cross-chain brain.
@@ -5006,11 +5296,15 @@ interface AgentTool {
5006
5296
  parameters: Record<string, unknown>;
5007
5297
  /** Advisory MCP-style hints about the tool's nature (read-only, value-moving, …). */
5008
5298
  annotations?: ToolAnnotations;
5299
+ /** Optional JSON Schema (draft-07 object) for the tool's RESULT — declared only
5300
+ * on stable read-only tools so a strict client can validate `structuredContent`.
5301
+ * Kept OPEN (no `additionalProperties:false`) so additive fields never break it. */
5302
+ outputSchema?: Record<string, unknown>;
5009
5303
  /** Execute the tool. Returns a JSON-serialisable result. */
5010
5304
  invoke: (args: Record<string, unknown>) => Promise<unknown>;
5011
5305
  }
5012
5306
  /**
5013
- * Five tools wrapping a configured {@link PipRailClient}:
5307
+ * Seven tools wrapping a configured {@link PipRailClient}:
5014
5308
  * - `piprail_discover(query?)` — FIND payable resources on the open x402
5015
5309
  * indexes, WITHOUT paying (the phone book — solves "what can I buy?").
5016
5310
  * - `piprail_quote_payment(url)` — price a gated URL WITHOUT paying.
@@ -5019,12 +5313,89 @@ interface AgentTool {
5019
5313
  * - `piprail_pay_request(url, method?, body?)` — pay if needed and return the result.
5020
5314
  * - `piprail_register(url, …)` — LIST a resource you run on the open indexes so
5021
5315
  * other agents can find it (402 Index, no signature).
5316
+ * - `piprail_budget()` — read the remaining spend budget + time leash (Mode A self-check).
5317
+ * - `piprail_guide()` — read the agent contract (how to quote/plan/pay + read a refusal).
5022
5318
  *
5023
- * A policy/approval refusal comes back as a structured `{ declined: true, reason }`
5024
- * (not a thrown error), so the model can reason about it instead of crashing.
5319
+ * The first five are byte-identical in name + order to before; the two read-only
5320
+ * tools are appended LAST. EVERY failure the pay tool sees comes back as a
5321
+ * STRUCTURED object (`{ ok:false, code, reason, explain, ref?, reasonCode?,
5322
+ * declined? }`) — never a thrown error — so the model reasons about it (and never
5323
+ * re-pays a broadcast-but-unconfirmed payment) instead of crashing.
5025
5324
  */
5026
5325
  declare function paymentTools(client: PipRailClient): AgentTool[];
5027
5326
 
5327
+ /**
5328
+ * One line summarising a {@link PaymentPlan} for a model: what's payable, on which
5329
+ * chain, the gas, and how many other rails aren't settleable. `null` (the URL
5330
+ * isn't gated) → "no payment required".
5331
+ */
5332
+ declare function summarizePlan(plan: PaymentPlan | null): string;
5333
+ /**
5334
+ * One line a model can act on for any failure. For a {@link PipRailError} it
5335
+ * switches on the stable `.code`; the broadcast-but-unconfirmed codes
5336
+ * (`PAYMENT_TIMEOUT`, `MAX_RETRIES_EXCEEDED`, `CONFIRMATION_TIMEOUT`) carry the
5337
+ * load-bearing recovery rule — **recover via `.ref`, never re-pay** (a fresh
5338
+ * payment would double-spend). A non-PipRailError → `'Payment failed: <message>'`.
5339
+ */
5340
+ declare function explainDecline(err: unknown): string;
5341
+ /**
5342
+ * One line summarising spend so far, per (network, asset) — NEVER a single
5343
+ * cross-token figure (there is no price oracle). Count 0 → "no payments yet".
5344
+ *
5345
+ * NOTE: spend totals are in-memory for THIS process and reset on restart — a
5346
+ * convenience, not a durable ledger.
5347
+ */
5348
+ declare function formatSpendReport(summary: SpendSummary): string;
5349
+
5350
+ /**
5351
+ * The PipRail agent contract, distilled into one string an LLM can read once and
5352
+ * use the tools correctly with near-zero other docs. PURE — a static constant, no
5353
+ * imports, no I/O. Exposed to MCP clients as a prompt + resource, and reachable
5354
+ * from the tool layer so a headless (non-MCP) agent can prepend it to its system
5355
+ * prompt.
5356
+ *
5357
+ * Keep it tight, concrete, and tool-name-accurate — an agent will trust it
5358
+ * literally, so a wrong name or order actively misleads. A test pins the load-
5359
+ * bearing phrases.
5360
+ */
5361
+ declare const PIPRAIL_AGENT_GUIDE = "# Paying with PipRail \u2014 the agent contract\n\nYou can pay for x402 \"402 Payment Required\" resources autonomously. Money moves\nstraight from your wallet to the server; PipRail custodies nothing. Follow this.\n\n## The loop: quote \u2192 plan \u2192 pay\n1. piprail_quote_payment(url) \u2014 PRICE it. Returns the amount, token, chain, and\n whether it is within your spend policy. No funds move. Use it to decide if a\n resource is worth buying.\n2. piprail_plan_payment(url) \u2014 can I afford it NOW? Reads your balance, native gas,\n and recipient-readiness across every rail, and returns { payable, best,\n fundingHint, session? }. If payable is false, do NOT attempt the payment \u2014\n fundingHint says exactly what to fix.\n3. piprail_pay_request(url, method?, body?) \u2014 PAY (only if the plan was payable)\n and return the result.\nAlways plan before you pay so you never commit to a payment you cannot finish.\n\n## Reading a refusal \u2014 never crash, never double-spend\nA failed pay returns a STRUCTURED object, never a thrown error you must catch:\n { ok:false, code, reason, explain, ref?, reasonCode?, declined? }\nBranch on `code` (always reliable). Key cases:\n- declined:true with reasonCode:'SESSION_EXPIRED' \u2014 your time budget is over. This\n is TERMINAL: STOP. Do not retry ANY payment this process; it cannot be undone\n without a restart / a longer TTL.\n- declined:true with reasonCode:'APPROVAL' \u2014 a human (or hook) declined this\n payment. Terminal for this pay: do NOT auto-retry \u2014 they said no, or no one\n answered.\n- declined:true with reasonCode:'OUTSIDE_WINDOW' \u2014 your rolling rate-limit is\n exhausted. Wait for it to free, then retry; do not raise the amount.\n- declined:true with reasonCode:'POLICY' or 'BUDGET' \u2014 a spend cap or allowlist\n refused it. Don't retry the same payment; pick a cheaper/allowed one.\n- code:'INSUFFICIENT_FUNDS' \u2014 top up the wallet (token and/or native gas), retry.\n- code:'PAYMENT_TIMEOUT' / 'MAX_RETRIES_EXCEEDED' / 'CONFIRMATION_TIMEOUT' \u2014 the\n payment may ALREADY be on-chain. Recover using the proof on `.ref` (re-verify\n or re-submit it); never re-pay \u2014 a fresh payment would double-spend.\n- code:'NO_COMPATIBLE_ACCEPT' / 'UNSUPPORTED_SCHEME' \u2014 the 402 isn't payable on\n your chain/scheme; `explain` says whether it's the wrong chain or a scheme to enable.\n\n## Knowing your leash \u2014 call piprail_budget\npiprail_budget tells you how much budget and time you have left, per\n(network, asset), plus your spend so far. Read-only; moves no funds. Use it in\nMode A to self-check before paying.\n\n## Two modes\n- Mode A (headless, default): you run FREE inside a pre-set budget + time\n envelope. The policy IS the consent \u2014 there is no per-payment prompt. Stay\n inside it; piprail_budget shows what's left.\n- Mode B (supervised): the host may ask a human to approve each payment. A\n decline/cancel/timeout comes back as declined:true (reasonCode:'APPROVAL') \u2014\n do NOT retry it as if it were a transient error.\n\n## Hard facts\n- Spend caps are PER (network, asset). There is no single cross-token dollar cap \u2014\n budgets aren't summed across tokens (no price oracle).\n- Spend totals and the time envelope live IN-MEMORY for THIS process; they reset on restart\n (a convenience, not a durable ledger).\n";
5362
+ /** Returns {@link PIPRAIL_AGENT_GUIDE} (a parity accessor for callers that prefer a function). */
5363
+ declare function agentGuide(): string;
5364
+
5365
+ /**
5366
+ * Scheme/chain triage for an x402 challenge — a pure, never-throwing read that
5367
+ * tells an agent WHY a 402 might be unpayable: "wrong chain" vs "the scheme isn't
5368
+ * enabled" vs "no rail at all". Stops those three very different fixes from being
5369
+ * conflated into one opaque `NO_COMPATIBLE_ACCEPT`.
5370
+ *
5371
+ * PURE: imports only types (`./x402.js`, `./client.js`) — zero chain libraries,
5372
+ * zero I/O. Feed it a challenge you already parsed (`parseChallenge`) plus your
5373
+ * client's bound network + enabled schemes.
5374
+ */
5375
+
5376
+ /** The verdict of a challenge triage — what's standing between you and paying. */
5377
+ type ChallengeVerdict = 'PAYABLE_RAIL' | 'UNPAYABLE_SCHEME' | 'WRONG_CHAIN' | 'NO_RAIL';
5378
+ interface ChallengeTriage {
5379
+ /** Does any offered rail sit on the client's bound network? */
5380
+ onClientChain: boolean;
5381
+ /** Does any rail on the client's network use an enabled scheme? */
5382
+ payableScheme: boolean;
5383
+ /** The distinct schemes the challenge offers. */
5384
+ offeredSchemes: PaymentScheme[];
5385
+ /** The distinct networks the challenge offers. */
5386
+ offeredNetworks: Caip2[];
5387
+ /** The one-word verdict. */
5388
+ verdict: ChallengeVerdict;
5389
+ }
5390
+ /**
5391
+ * Bucket `challenge.accepts[]` by (network === `opts.network`) and
5392
+ * (scheme ∈ `opts.schemes`) and return the verdict. Never throws.
5393
+ */
5394
+ declare function classifyChallenge(challenge: X402Challenge, opts: {
5395
+ network: Caip2;
5396
+ schemes: readonly PaymentScheme[];
5397
+ }): ChallengeTriage;
5398
+
5028
5399
  /**
5029
5400
  * Discovery — make a gated resource FINDABLE, by emitting the open-standard
5030
5401
  * artifacts a crawler/index reads. PURE: this file turns the config a gate
@@ -5614,21 +5985,54 @@ declare class PaymentTimeoutError extends PipRailError {
5614
5985
  */
5615
5986
  declare class MaxRetriesExceededError extends PipRailError {
5616
5987
  readonly code = "MAX_RETRIES_EXCEEDED";
5617
- /** The already-broadcast proof ref — recover with it, don't re-pay. */
5988
+ /**
5989
+ * The proof ref — recover with it, don't re-pay. Its meaning depends on the
5990
+ * scheme: for `onchain-proof` it's the already-broadcast transaction ref
5991
+ * (re-verify or re-submit it). For a standard `exact` rail it's the EIP-3009
5992
+ * authorization NONCE (a `0x…` 32-byte value, NOT a tx hash) — re-PRESENT the
5993
+ * same signed authorization, never re-sign a fresh nonce; check the token's
5994
+ * `authorizationState(from, nonce)` before assuming it didn't settle.
5995
+ */
5618
5996
  readonly ref?: string;
5619
5997
  constructor(message: string, options?: ErrorOptions & {
5620
5998
  ref?: string;
5621
5999
  });
5622
6000
  }
6001
+ /**
6002
+ * A typed, machine-readable discriminator on a {@link PaymentDeclinedError} so an
6003
+ * agent can branch on WHY a payment was refused WITHOUT regexing the human
6004
+ * message. It's a HINT layered on top of the always-reliable `.code`
6005
+ * (`'PAYMENT_DECLINED'`) — the two-channel error model is unchanged; this adds NO
6006
+ * new `.code`. Values:
6007
+ * - `'POLICY'` — a chain/host/token/per-payment cap refused it.
6008
+ * - `'BUDGET'` — the per-(network,asset) lifetime `maxTotal` cap.
6009
+ * - `'OUTSIDE_WINDOW'` — the rolling `windowTotal` cap (wait for it to slide).
6010
+ * - `'SESSION_EXPIRED'`— the session TTL elapsed. **TERMINAL** — every payment
6011
+ * this process is now refused; do NOT retry, restart/extend the TTL.
6012
+ * - `'APPROVAL'` — an `onBeforePay` approval hook said no (e.g. an MCP
6013
+ * human-in-the-loop decline). Terminal for this pay — do NOT auto-retry.
6014
+ */
6015
+ type DeclineReasonCode = 'POLICY' | 'BUDGET' | 'OUTSIDE_WINDOW' | 'SESSION_EXPIRED' | 'APPROVAL';
5623
6016
  /**
5624
6017
  * The client refused to pay BEFORE any on-chain send — the quoted payment
5625
- * exceeded the configured {@link PaymentPolicy} (amount/total ceiling, or a
5626
- * chain/token/host outside the allowlist), or an `onBeforePay` hook returned
5627
- * `false`. No funds moved. The message names which guard fired; inspect the
5628
- * `quote` via `client.quote(url)` to see the full breakdown.
6018
+ * exceeded the configured {@link PaymentPolicy} (amount/total ceiling, a
6019
+ * chain/token/host outside the allowlist, or the session's time envelope), or an
6020
+ * `onBeforePay` hook returned `false`. No funds moved. The message names which
6021
+ * guard fired; inspect the `quote` via `client.quote(url)` to see the full
6022
+ * breakdown.
6023
+ *
6024
+ * `.reasonCode` is an optional, typed {@link DeclineReasonCode} the client stamps
6025
+ * so an agent can branch on the cause (and spot a TERMINAL `'SESSION_EXPIRED'` /
6026
+ * `'APPROVAL'` it must not retry) without parsing the prose. `.code` stays
6027
+ * `'PAYMENT_DECLINED'`.
5629
6028
  */
5630
6029
  declare class PaymentDeclinedError extends PipRailError {
5631
6030
  readonly code = "PAYMENT_DECLINED";
6031
+ /** Why it was declined, as a typed enum (a hint; `.code` is the reliable channel). */
6032
+ readonly reasonCode?: DeclineReasonCode;
6033
+ constructor(message: string, options?: ErrorOptions & {
6034
+ reasonCode?: DeclineReasonCode;
6035
+ });
5632
6036
  }
5633
6037
  /**
5634
6038
  * The payment broadcast but didn't confirm within the driver's polling window
@@ -5667,6 +6071,22 @@ declare class InvalidEnvelopeError extends PipRailError {
5667
6071
  declare class NoCompatibleAcceptError extends PipRailError {
5668
6072
  readonly code = "NO_COMPATIBLE_ACCEPT";
5669
6073
  }
6074
+ /**
6075
+ * The client was asked to pay a SCHEME the bound chain family/asset/signer can't
6076
+ * settle, and no fallback rail was offered. Specifically: a standard `exact` rail
6077
+ * on a non-EVM family (only EVM ships the buyer `payExact`); an `exact` rail for a
6078
+ * token that isn't EIP-3009 (USDT needs Permit2, native isn't exact-payable, a
6079
+ * plain ERC-20 has no `transferWithAuthorization`); or an `exact` rail whose signer
6080
+ * is a contract / EIP-1271 / EIP-7702-delegated account (no recoverable ECDSA sig).
6081
+ *
6082
+ * Distinct from {@link NoCompatibleAcceptError} (no rail for the network at all)
6083
+ * and {@link WrongFamilyError} (the wallet/payTo/token was given in another
6084
+ * family's shape). The fix is usually "enable/keep an `onchain-proof` rail" or
6085
+ * "pay with a supported chain/token". Thrown by the client / the EVM driver.
6086
+ */
6087
+ declare class UnsupportedSchemeError extends PipRailError {
6088
+ readonly code = "UNSUPPORTED_SCHEME";
6089
+ }
5670
6090
  /** init.body was provided but isn't replayable (e.g. a one-shot ReadableStream). */
5671
6091
  declare class NonReplayableBodyError extends PipRailError {
5672
6092
  readonly code = "NON_REPLAYABLE_BODY";
@@ -5717,10 +6137,12 @@ declare class UnsupportedNetworkError extends PipRailError {
5717
6137
  * x402 `exact` interop, in BOTH directions, EVM + EIP-3009 only, on the existing
5718
6138
  * `viem` peer (no new dep):
5719
6139
  *
5720
- * • BUYER side (client) — parse an `exact` requirement, build + EIP-712-sign the
5721
- * EIP-3009 authorization, encode the `X-PAYMENT` header. Deterministic,
5722
- * unit-testable; NOT wired into `PipRailClient.fetch`'s default (see
5723
- * `.claude/plans/agent-readiness/04-universal-exact.md`).
6140
+ * • BUYER side (client) — {@link payExactEvm} re-derives the token's EIP-712 domain
6141
+ * ON-CHAIN, signs the EIP-3009 authorization, and is wired into `PipRailClient`'s
6142
+ * pay path via the EVM driver's `payExact` SPI (OPT-IN through `schemes: ['exact']`;
6143
+ * the default is unchanged). The lower-level `buildExactAuthorization` /
6144
+ * `encodeXPaymentHeader` codecs remain for hand-rolled/v1 clients. See
6145
+ * `.claude/plans/exact-client/IMPLEMENTATION.md`.
5724
6146
  *
5725
6147
  * • SELLER side (gate) — {@link readExactDomain} (read the token's true EIP-712
5726
6148
  * domain so the gate can advertise + verify it) and {@link verifyAndSettleExactEvm}
@@ -5812,6 +6234,13 @@ interface BuildExactParams {
5812
6234
  * Build + EIP-712-sign an EIP-3009 `transferWithAuthorization` for an `exact`
5813
6235
  * requirement. Returns the authorization and its signature; pass both to
5814
6236
  * {@link encodeXPaymentHeader} to produce the `X-PAYMENT` header value.
6237
+ *
6238
+ * @deprecated Low-level primitive — prefer {@link payExactEvm}, which the client's
6239
+ * `payExact` SPI uses. This helper TRUSTS the server-supplied `accept.extra.{name,
6240
+ * version}` for the EIP-712 domain (a lying/absent value yields a silently-invalid
6241
+ * signature) and calls `account.signTypedData` directly (which is `undefined` on a
6242
+ * bring-your-own JsonRpcAccount). `payExactEvm` re-derives the domain on-chain and
6243
+ * signs via the wallet client. Kept exported as a deterministic test/codec building block.
5815
6244
  */
5816
6245
  declare function buildExactAuthorization(params: BuildExactParams): Promise<{
5817
6246
  authorization: ExactAuthorization;
@@ -5938,13 +6367,14 @@ declare const eip3009Abi: readonly [{
5938
6367
  * plain ERC-20), so the gate refuses to advertise a standard `exact` rail for it.
5939
6368
  *
5940
6369
  * The name is NEVER assumed from the symbol: canonical USDC's domain name is
5941
- * "USD Coin" (not "USDC"), EURC's is "EURC". `eip712Domain()` (EIP-5267) reverts
5942
- * on these proxies, so we read `name()`+`version()` and probe `authorizationState`
5943
- * to confirm EIP-3009 support.
6370
+ * "USD Coin" (not "USDC"), and EURC's is "Euro Coin" on Ethereum/Avalanche but "EURC"
6371
+ * on Base proof that only the on-chain read is authoritative. `eip712Domain()`
6372
+ * (EIP-5267) reverts on these proxies, so we read `name()`+`version()` and probe
6373
+ * `authorizationState` to confirm EIP-3009 support.
5944
6374
  */
5945
6375
  declare function readExactDomain(publicClient: PublicClient, asset: string): Promise<{
5946
6376
  name: string;
5947
6377
  version: string;
5948
6378
  } | null>;
5949
6379
 
5950
- export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BazaarExtension, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, buildBazaarExtension, buildChallengeHeader, buildExactAuthorization, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, createPaymentGate, decorateOutcome, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, getDirectoryInfo, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, toInsufficientFundsError, toInvalidBody, verify402IndexDomain };
6380
+ export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BazaarExtension, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ChallengeTriage, type ChallengeVerdict, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DeclineReasonCode, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, PIPRAIL_AGENT_GUIDE, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, type PaymentScheme, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, type PolicyDenyCode, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SessionBudget, type SettleOutcome, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendRemaining, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, UnsupportedSchemeError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, agentGuide, buildBazaarExtension, buildChallengeHeader, buildExactAuthorization, buildExactSignatureHeader, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, classifyChallenge, createPaymentGate, decorateOutcome, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, explainDecline, formatSpendReport, getDirectoryInfo, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSettleResponse, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, summarizePlan, toInsufficientFundsError, toInvalidBody, verify402IndexDomain };