/** * @internal x402 paying transport — the SDK's "pay on 402" wall. * * Wraps whichever authenticated transport the client selected (direct bearer * or broker) so that a 402 carrying an x402 V2 `PaymentRequired` challenge is * answered with exactly one signed retry: * * bare request ──► 402 + challenge ──► select under policy ──► sign * ──► the SAME request + PAYMENT-SIGNATURE ──► response * * Doctrine (Wave 2 Phase C, changed deliberately): the SDK CAN sign and pay, * but only when the caller opted in at construction with a signer AND a * spend policy. Never by default. This module is never reached unless the * `x402` option was passed to `AgentTool`. * * The walls this wrapper adds on top of `x402.ts`: * * 1. Exactly two fetches, ever. A second 402 after the signed retry is a * typed error (`x402_payment_not_accepted`), never another signature. * Signing again would be a second, independent payment. * 2. The retry is the same request: same method, same URL, same body, same * caller headers (including `Idempotency-Key`), same authenticating * transport — so the same bearer. Only `PAYMENT-SIGNATURE` is added. * 3. A request that already carries a caller-supplied `PAYMENT-SIGNATURE` * is never paid for again; its 402 surfaces untouched. * 4. A 402 without a parseable challenge (fail-closed admission with * `Retry-After`, a replay-suppressed 402 echoing `PAYMENT-RESPONSE`) is * not payable and surfaces untouched, headers intact. * 5. A refusal by the spend policy is a typed error whose `code` is the * refusal reason. Nothing was signed; one fetch happened. * 6. A body that cannot be re-sent (a stream) is refused BEFORE anything is * signed: a signature that cannot be delivered is a liability. * * The leading underscore keeps the parity scanner off this file: it is * plumbing, not the public surface. The public surface is `x402.ts`. */ import { type AgentToolTransport } from "./_http.js"; import { type X402Signer, type X402SpendPolicy } from "./x402.js"; /** Environment variable read ONLY when the `x402` option is present without * a signer. Its absence with no `x402` option changes nothing. */ export declare const X402_PRIVATE_KEY_ENV = "AT_X402_PRIVATE_KEY"; /** What `onPayment` receives after the signed retry was attempted. */ export interface X402PaymentEvent { /** Client identity of the signed authorization (the six EIP-3009 fields, * `authorizationHash` in x402.ts). Persist it: recovery is a lookup on * what was emitted, never a fresh signature. */ authorizationHash: string; /** Unix seconds after which the signed bytes are dead. */ validBefore: number; /** HTTP status of the signed retry. Absent when the retry itself threw * (network failure) — the authorization may still have been received. */ status?: number; /** Raw `PAYMENT-RESPONSE` settlement receipt from the retry, if any. */ paymentResponse?: string; /** Raw `Link` header from the retry (rel="payment-status"), if any. */ paymentStatusLink?: string; /** The server's LEDGER identity for this payment, parsed from the * rel="payment-status" Link when present. This — not `authorizationHash` * — is what `at.x402.payment(id)` resolves. */ paymentId?: string; /** Raw `X-Credits-Balance` from the retry, if any. */ creditsBalance?: string; } export type X402PaymentCallback = (event: X402PaymentEvent) => void | Promise; /** The `x402` option on `AgentToolOptions`. Presence is the opt-in. */ export interface AgentToolX402Options { /** Whoever holds the key. Omit to read `AT_X402_PRIVATE_KEY` into a * `localEvmSigner` — honoured ONLY because this option object exists. */ signer?: X402Signer; /** Mandatory, no defaults: `maxAmountAtomic` and `allowedPayTo` above all. * A policy without a hard cap and a recipient allow-list is refused at * construction, before any request is made. */ policy: X402SpendPolicy; /** Called once per signed retry with the identity of what was emitted. */ onPayment?: X402PaymentCallback; /** Clock for the authorization window (unix seconds). Defaults to * `Date.now()`; injectable for tests and trusted time sources. */ nowSeconds?: () => number; } /** @internal Validated form handed to the transport. */ export interface ResolvedX402Options { signer: X402Signer; policy: X402SpendPolicy; onPayment: X402PaymentCallback | undefined; nowSeconds: () => number; } /** * @internal Validate the `x402` option at construction. * * Throws typed `AgentToolError`s: `x402_option_invalid` (not an object, bad * callback), `x402_spend_policy_invalid` (missing/invalid policy — the cap * and the payTo allow-list are named first because they are the two walls * that matter most), `x402_signer_invalid`, `x402_private_key_invalid`, * `x402_signer_missing`. Nothing here touches the network. */ export declare function resolveX402Options(option: unknown, env: Readonly>): ResolvedX402Options; /** Parse the ledger id out of a rel="payment-status" Link header. */ export declare function paymentIdFromStatusLink(link: string | undefined): string | undefined; /** * @internal Wrap `inner` so a challenged 402 is paid once, under `resolved`. * * `inner` performs authentication; the retry goes through it again, so the * bearer (or broker grant) on the retry is the same one the bare request * carried. This wrapper never sees a credential. */ export declare function x402PayingTransport(inner: AgentToolTransport, resolved: ResolvedX402Options): AgentToolTransport;