@dvmkit/sdk 0.1.0-rc.1 → 0.1.0-rc.3

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 (42) hide show
  1. package/README.md +10 -2
  2. package/dist/{revenue-reporter-M35KP6V7.js → chunk-2ABMGUDS.js} +78 -2
  3. package/dist/chunk-5SO7ZVOH.js +375 -0
  4. package/dist/chunk-66HGCPBU.js +25 -0
  5. package/dist/{chunk-KQAJVVZT.js → chunk-AZBXSXQT.js} +27 -287
  6. package/dist/chunk-C2DC4FKR.js +21424 -0
  7. package/dist/{chunk-H25M54MI.js → chunk-C3MTFLC6.js} +16 -0
  8. package/dist/{chunk-NTK5DJ6R.js → chunk-EXHBXA4U.js} +11 -1
  9. package/dist/chunk-FT7IM66W.js +1557 -0
  10. package/dist/{chunk-KXWROQGK.js → chunk-FUJ36YDV.js} +1 -24
  11. package/dist/{tempo-charge-store-6GJEMNUU.js → chunk-JZWELPFH.js} +1 -0
  12. package/dist/{chunk-RPXHKMYE.js → chunk-LWUR4CGG.js} +348 -34
  13. package/dist/chunk-RHP3BRTH.js +1090 -0
  14. package/dist/{tempo-session-store-FTEEGZXA.js → chunk-RU7SXHLO.js} +2 -1
  15. package/dist/{chunk-7IH5SG2A.js → chunk-TKA6ZP4M.js} +62 -41
  16. package/dist/chunk-TVI4V7GF.js +283 -0
  17. package/dist/chunk-X3IKFWJA.js +754 -0
  18. package/dist/{chunk-DCNT4PJS.js → chunk-XY5Y5REG.js} +6 -258
  19. package/dist/chunk-XYTSDAPH.js +232 -0
  20. package/dist/chunk-YD3TZNXV.js +1042 -0
  21. package/dist/{credit-ledger-EDMEZSA2.js → credit-ledger-ED6JXKVD.js} +2 -2
  22. package/dist/credit-menu-DONAtGVf.d.ts +5076 -0
  23. package/dist/{ssrf-BdHsrrIb.d.ts → fx-Bq4cvn16.d.ts} +37 -119
  24. package/dist/index.d.ts +8 -66
  25. package/dist/index.js +11 -219
  26. package/dist/internal/index.d.ts +5745 -0
  27. package/dist/internal/index.js +6379 -0
  28. package/dist/{job-store-C5n6bhap.d.ts → job-store-m2pYmvbr.d.ts} +1772 -31
  29. package/dist/{memory-credit-ledger-7TTZDSRS.js → memory-credit-ledger-XJ5VQEVP.js} +3 -3
  30. package/dist/payout-reporter-3UB5WRCV.js +13 -0
  31. package/dist/revenue-reporter-JIKUPXOK.js +7 -0
  32. package/dist/server/index.d.ts +14 -3530
  33. package/dist/server/index.js +327 -20636
  34. package/dist/ssrf-DbFkpDv0.d.ts +118 -0
  35. package/dist/tempo-charge-store-RIFTALZK.js +8 -0
  36. package/dist/tempo-session-store-DALMRIWN.js +11 -0
  37. package/dist/testing/index.d.ts +3 -2
  38. package/dist/testing/index.js +3 -2
  39. package/dist/usd-DjVAPMlf.d.ts +97 -0
  40. package/dist/x402-5EVIUSEP.js +81 -0
  41. package/package.json +6 -2
  42. package/dist/x402-35VLYFKZ.js +0 -1272
@@ -1,10 +1,11 @@
1
1
  import { ProofLike } from '@cashu/cashu-ts';
2
- import { Challenge, Credential, Method, Receipt } from 'mppx';
3
- import { MiddlewareHandler, Context, Hono } from 'hono';
4
- import { FacilitatorClient } from '@x402/core/server';
2
+ import { Challenge, Credential, Store, Method, Receipt } from 'mppx';
3
+ import { FacilitatorClient, FacilitatorConfig } from '@x402/core/server';
5
4
  import { AuthorizerSigner, ChannelStorage, AutoSettlementConfig, Channel, ChannelUpdateResult } from '@x402/evm/batch-settlement/server';
5
+ import { MiddlewareHandler, Context, Hono } from 'hono';
6
+ import { Account, Client, Hex } from 'viem';
6
7
  import { Pool, PoolClient } from 'pg';
7
- import { PaymentPayload, PaymentRequirements, SettleResponse } from '@x402/core/types';
8
+ import { PaymentPayload as PaymentPayload$1, PaymentRequirements as PaymentRequirements$1, SettleResponse as SettleResponse$1 } from '@x402/core/types';
8
9
  import { z } from 'zod';
9
10
 
10
11
  /**
@@ -78,6 +79,8 @@ interface X402SelfRelayConfig {
78
79
  /** Advanced RPC-health hook; platform-hosted DVMs receive an automatic reporter. */
79
80
  onRpcHealth?: (observation: X402SelfRelayRpcHealthObservation) => void | Promise<void>;
80
81
  }
82
+ /** Caller-visible reasons an ambiguous x402 settlement could not be reconciled. */
83
+ type X402SettlementReconciliationReason = "settlement_not_on_chain" | "chain_unreachable" | "settlement_unbookmarked" | "facilitator_channel_state_unavailable";
81
84
  /** Builder-facing configuration for reusable x402 batch-settlement channels. */
82
85
  interface X402BatchSettlementConfig {
83
86
  /** Receiver-authorizer signer used when the facilitator advertises none. */
@@ -142,6 +145,13 @@ interface X402ExactVersionSupport {
142
145
  v1: boolean;
143
146
  v2: boolean;
144
147
  }
148
+ /** Client-side x402 wallet — EVM key pair for signing payments. */
149
+ interface X402Wallet {
150
+ privateKey: string;
151
+ address: string;
152
+ /** CAIP-2 chain id this wallet pays on. */
153
+ network: string;
154
+ }
145
155
  /** Server-side receipt from verifying an x402 payment. */
146
156
  interface X402Receipt {
147
157
  verified: boolean;
@@ -181,6 +191,50 @@ interface X402Receipt {
181
191
 
182
192
  /** Current x402 protocol version preferred by dvmkit clients and servers. */
183
193
  declare const X402_VERSION = 2;
194
+ /** Legacy x402 protocol version retained during the dual-serve window. */
195
+ declare const X402_V1_VERSION = 1;
196
+ /** x402 protocol versions supported by dvmkit's hand-rolled exact codec. */
197
+ type X402Version = typeof X402_V1_VERSION | typeof X402_VERSION;
198
+ /**
199
+ * Chain a caller's x402 wallet reads when it was connected without an explicit
200
+ * `--network`. Single source of truth for the default: the wallet loader, the
201
+ * connect output and the asset resolver all read it from here (internal-review).
202
+ */
203
+ declare const X402_DEFAULT_NETWORK = "eip155:8453";
204
+ /**
205
+ * Scheme name of the per-call rail — one signed authorization settling one
206
+ * resource. Shared with the credit funding menu's `x402.schemes` block, which
207
+ * has to name the same string the requirement carries (internal-review).
208
+ */
209
+ declare const X402_EXACT_SCHEME = "exact";
210
+ /**
211
+ * Scheme name of the reusable v2 channel rail. Single source of truth: the
212
+ * facilitator probe, the requirement the server builds, the header dispatch,
213
+ * the caller's requirement selection, and the credit menu's `x402.schemes`
214
+ * block all read it from here — the menu advertises exactly what the challenge
215
+ * will carry, so a rename cannot leave one of them advertising a flavour the
216
+ * others no longer speak (internal-review).
217
+ */
218
+ declare const X402_BATCH_SETTLEMENT_SCHEME = "batch-settlement";
219
+
220
+ /**
221
+ * x402-spec PaymentRequirements — the shape advertised on a 402 response's
222
+ * `accepts` array, mirrored from the facilitator's `PaymentRequirementsSchema`
223
+ * (see `x402/types/index.d.ts` in the published `x402` package).
224
+ */
225
+ interface PaymentRequirementsV1 {
226
+ scheme: string;
227
+ network: string;
228
+ maxAmountRequired: string;
229
+ resource: string;
230
+ description: string;
231
+ mimeType: string;
232
+ payTo: string;
233
+ asset: string;
234
+ maxTimeoutSeconds: number;
235
+ outputSchema?: Record<string, unknown>;
236
+ extra?: Record<string, unknown>;
237
+ }
184
238
  /** x402 v2 resource metadata echoed by a paying client. */
185
239
  interface ResourceInfo {
186
240
  url: string;
@@ -204,6 +258,73 @@ interface PaymentRequirementsV2 {
204
258
  */
205
259
  extra?: Record<string, unknown> | null;
206
260
  }
261
+ /** Legacy alias retained for server internals that carry the v1 body requirements. */
262
+ type PaymentRequirements = PaymentRequirementsV1;
263
+ /**
264
+ * EIP-3009 `TransferWithAuthorization` parameters that the client signs and
265
+ * the facilitator submits on settlement.
266
+ */
267
+ interface ExactEvmPayloadAuthorization {
268
+ from: string;
269
+ to: string;
270
+ value: string;
271
+ validAfter: string;
272
+ validBefore: string;
273
+ nonce: string;
274
+ }
275
+ /** Signed EIP-3009 transfer authorization (the `payload` of a PaymentPayload). */
276
+ interface ExactEvmPayload {
277
+ signature: string;
278
+ authorization: ExactEvmPayloadAuthorization;
279
+ }
280
+ /**
281
+ * x402-spec PaymentPayload — the JSON shape carried inside the `X-PAYMENT`
282
+ * header (base64-encoded). `payload` is scheme-specific; alpha only supports
283
+ * `scheme: "exact"` against an EVM network.
284
+ */
285
+ interface PaymentPayloadV1 {
286
+ x402Version: typeof X402_V1_VERSION;
287
+ scheme: "exact";
288
+ network: string;
289
+ payload: ExactEvmPayload;
290
+ }
291
+ /** x402 v2 payment payload carried in `PAYMENT-SIGNATURE`. */
292
+ interface PaymentPayloadV2 {
293
+ x402Version: typeof X402_VERSION;
294
+ resource?: ResourceInfo;
295
+ accepted: PaymentRequirementsV2;
296
+ payload: ExactEvmPayload;
297
+ extensions?: Record<string, unknown>;
298
+ }
299
+ /** Exact EVM payment payload accepted by the dual-version codec. */
300
+ type PaymentPayload = PaymentPayloadV1 | PaymentPayloadV2;
301
+ /** Facilitator `/verify` response. */
302
+ interface VerifyResponse {
303
+ isValid: boolean;
304
+ invalidReason?: string;
305
+ payer?: string;
306
+ }
307
+ /** Facilitator `/settle` response — what we base64 into `X-PAYMENT-RESPONSE`. */
308
+ interface SettleResponse {
309
+ success: boolean;
310
+ errorReason?: string;
311
+ payer?: string;
312
+ transaction?: string;
313
+ network?: string;
314
+ amount?: string;
315
+ extensions?: Record<string, unknown>;
316
+ }
317
+ /**
318
+ * Body envelope used to carry x402 PaymentRequirements on a 402 response.
319
+ * The x402 spec puts requirements in the JSON body under `accepts`; the
320
+ * `WWW-Authenticate: Payment` header carries the MPP method advertisements
321
+ * separately, so the two protocols don't fight for the same header slot.
322
+ */
323
+ interface X402ResponseBody {
324
+ x402Version: typeof X402_V1_VERSION;
325
+ accepts: PaymentRequirementsV1[];
326
+ error?: string;
327
+ }
207
328
  /** x402 v2 payment-required declaration encoded into `PAYMENT-REQUIRED`. */
208
329
  interface PaymentRequiredV2 {
209
330
  x402Version: typeof X402_VERSION;
@@ -212,6 +333,101 @@ interface PaymentRequiredV2 {
212
333
  accepts: PaymentRequirementsV2[];
213
334
  extensions?: Record<string, unknown>;
214
335
  }
336
+ /** Either challenge shape understood by dvmkit callers. */
337
+ type PaymentRequired = X402ResponseBody | PaymentRequiredV2;
338
+ /** Inputs for `buildPaymentRequirements`. */
339
+ interface BuildPaymentRequirementsOpts {
340
+ x402Config: X402Config;
341
+ requiredUsdcMicro: bigint;
342
+ resource: string;
343
+ description?: string;
344
+ mimeType?: string;
345
+ version?: X402Version;
346
+ }
347
+ /**
348
+ * Build a single x402 `PaymentRequirements` entry from the DVM's `X402Config`,
349
+ * the resolved per-call USDC amount, and the live request's resource URL.
350
+ * The resource string is bound into the EIP-3009 `extra` field on signing in
351
+ * the reference clients, so it must match exactly between the 402 response
352
+ * and the retry payload — pass the request URL the client will retry against.
353
+ */
354
+ declare function buildPaymentRequirements(opts: BuildPaymentRequirementsOpts & {
355
+ version: typeof X402_V1_VERSION;
356
+ }): PaymentRequirementsV1;
357
+ declare function buildPaymentRequirements(opts: (BuildPaymentRequirementsOpts & {
358
+ version: typeof X402_VERSION;
359
+ }) | (BuildPaymentRequirementsOpts & {
360
+ version?: undefined;
361
+ })): PaymentRequirementsV2;
362
+ /** Build the canonical x402 v2 declaration for a `PAYMENT-REQUIRED` header. */
363
+ declare function buildPaymentRequiredV2(opts: Omit<BuildPaymentRequirementsOpts, "version"> & {
364
+ error?: string;
365
+ }): PaymentRequiredV2;
366
+ /** Convert the server's legacy body requirements into the parallel v2 declaration. */
367
+ declare function paymentRequiredV2FromV1(requirements: PaymentRequirementsV1[], error?: string): PaymentRequiredV2;
368
+ /**
369
+ * Translate a CAIP-2 chain id (`eip155:8453`) to the x402-spec network slug
370
+ * (`base`). Operators configure DVMs in CAIP-2 because it's the lingua-franca
371
+ * across our other rails; the x402 facilitator API only accepts named slugs.
372
+ * Unknown CAIP-2 ids fall through to the original string so non-EVM rails
373
+ * (Solana mainnet, etc.) can also be plumbed once supported.
374
+ */
375
+ declare function caip2ToX402Network(network: string): string;
376
+ /** Translate a legacy x402 network slug back to its CAIP-2 chain id. */
377
+ declare function x402NetworkToCaip2(network: string): string;
378
+ /** EIP-712 chain id derived from a CAIP-2 chain id. */
379
+ declare function chainIdFromCaip2(network: string): number;
380
+ /** USDC contract address for a network slug (post-translation). */
381
+ declare function usdcContractFor(networkSlug: string): `0x${string}` | undefined;
382
+ /**
383
+ * USDC contract address for a CAIP-2 chain id (e.g. `eip155:8453`). Resolves
384
+ * via {@link caip2ToX402Network} so the one network table is the source of
385
+ * truth — keeps callers that hold the CAIP-2 form (CLI wallet) in lockstep
386
+ * with the SDK 402-builder which already speaks x402 slugs.
387
+ */
388
+ declare function usdcContractByCaip2(caip2: string): `0x${string}` | undefined;
389
+ /** USDC EIP-712 domain `name` for a network slug. */
390
+ declare function usdcDomainNameFor(networkSlug: string): string | undefined;
391
+ /** USDC EIP-712 domain `version` for a network slug. */
392
+ declare function usdcDomainVersionFor(networkSlug: string): string | undefined;
393
+ /**
394
+ * Decode a base64-encoded `X-PAYMENT` header into a structured `PaymentPayload`.
395
+ * Throws on malformed input rather than silently returning a partial — the
396
+ * server treats decode failures as `payment_invalid` per the spec.
397
+ */
398
+ declare function decodePayment(header: string): PaymentPayload;
399
+ /**
400
+ * Encode a `PaymentPayload` for transport in the `X-PAYMENT` header. JSON
401
+ * serialization uses string-typed integer fields (the spec encodes uint256
402
+ * values as decimal strings, so `bigint` must already be stringified by the
403
+ * caller).
404
+ */
405
+ declare function encodePayment(payload: PaymentPayload): string;
406
+ /** Encode a v2 `PaymentRequired` declaration for the HTTP response header. */
407
+ declare function encodePaymentRequiredHeader(required: PaymentRequiredV2): string;
408
+ /** Decode and structurally validate a v2 `PAYMENT-REQUIRED` header. */
409
+ declare function decodePaymentRequiredHeader(header: string): PaymentRequiredV2;
410
+ /** Read the exact EIP-3009 authorization carried by either protocol version. */
411
+ declare function exactEvmAuthorization(payload: PaymentPayload): ExactEvmPayloadAuthorization;
412
+ /**
413
+ * Encode a `SettleResponse` for the `X-PAYMENT-RESPONSE` header that the
414
+ * server emits on a 2xx after a successful settlement (internal-review, per the
415
+ * x402 spec).
416
+ */
417
+ declare function encodeSettleResponseHeader(response: SettleResponse): string;
418
+ /**
419
+ * POST a verify request to the facilitator. Returns the parsed `VerifyResponse`
420
+ * or a structured-failure shape when the facilitator rejects the call (network
421
+ * error, non-2xx, or `isValid: false`).
422
+ */
423
+ declare function verifyWithFacilitator(payload: PaymentPayload, requirements: PaymentRequirementsV1 | PaymentRequirementsV2, config?: Pick<X402Config, "facilitator" | "facilitatorAuth">, createAuthHeaders?: FacilitatorConfig["createAuthHeaders"] | undefined): Promise<VerifyResponse>;
424
+ /**
425
+ * POST a settle request to the facilitator. The facilitator broadcasts the
426
+ * EIP-3009 `transferWithAuthorization` and returns the on-chain tx hash on
427
+ * success; the dvmkit DVM mirrors that into `X-PAYMENT-RESPONSE` for the
428
+ * client and into `revenue_events.tx_hash` for the platform ledger.
429
+ */
430
+ declare function settleWithFacilitator(payload: PaymentPayload, requirements: PaymentRequirementsV1 | PaymentRequirementsV2, config?: Pick<X402Config, "facilitator" | "facilitatorAuth">, createAuthHeaders?: FacilitatorConfig["createAuthHeaders"] | undefined): Promise<SettleResponse>;
215
431
 
216
432
  /** Identifies which side of the conversation sent a message. */
217
433
  type MessageFrom = "requester" | "provider";
@@ -419,6 +635,16 @@ type Message = (MessageBase & {
419
635
  type: "progress";
420
636
  content: ProgressContent;
421
637
  });
638
+ /** Type guard: message contains an artifact. */
639
+ declare function isArtifactMessage(msg: Message): msg is MessageBase & {
640
+ type: "artifact";
641
+ content: ArtifactContent;
642
+ };
643
+ /** Type guard: message is a payment request. */
644
+ declare function isPaymentRequestMessage(msg: Message): msg is MessageBase & {
645
+ type: "payment-request";
646
+ content: PaymentRequestContent;
647
+ };
422
648
 
423
649
  /** Auth identifier advertised by DVMs using audience-bound caller proofs. */
424
650
  declare const SIGNED_REQUEST_AUTH_ID: "secp256k1-schnorr-v2";
@@ -514,6 +740,42 @@ declare const requireClientCompatibility: ClientCompatibilityGate;
514
740
  /** Render the normal structured HTTP 426 compatibility error. */
515
741
  declare function clientUpgradeRequired(c: Context, compatibility: ClientCompatibility, requirement: ClientCompatibilityRequirement): Response;
516
742
 
743
+ /**
744
+ * Canonical JSON serialisation used for secp256k1+BIP-340 Schnorr request
745
+ * signing in cast (internal-review).
746
+ *
747
+ * Single source of truth for both the cast DVM's verifier (internal-review) and the
748
+ * client signing helper (internal-review). The same function on both sides guarantees
749
+ * that what the client signs is byte-identical to what the server hashes.
750
+ *
751
+ * Algorithm: recursively sort object keys lexicographically (UTF-16 code-unit
752
+ * order — JavaScript's default), then JSON.stringify with no whitespace.
753
+ * Arrays preserve order. Numbers, strings, booleans, null pass through as
754
+ * `JSON.stringify` formats them. Values of `undefined` or functions are
755
+ * forbidden (we throw rather than silently drop, since both sides must agree).
756
+ */
757
+ /** Any JSON-serialisable value accepted by `canonicalize`. */
758
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
759
+ [key: string]: JsonValue;
760
+ };
761
+ /**
762
+ * Serialise `value` to its canonical JSON form (sorted keys, no whitespace).
763
+ *
764
+ * `undefined` properties on objects are silently omitted (mirroring
765
+ * `JSON.stringify`), so callers don't need to explicitly delete optional fields
766
+ * that weren't set. Throws `TypeError` for unsupported scalar types (functions,
767
+ * symbols, non-finite numbers). The caller is responsible for stripping fields
768
+ * that shouldn't appear in the signed payload (e.g. `signature` itself).
769
+ */
770
+ declare function canonicalize(value: JsonValue): string;
771
+ /**
772
+ * Canonicalise `value` and return the UTF-8 bytes the signer should produce a
773
+ * signature over. Both the cast client signer (`signAddEpisode`) and the
774
+ * server verifier (`verifyCanonicalSignedRequest`) call this — sharing the function
775
+ * guarantees byte-identical input on both sides.
776
+ */
777
+ declare function canonicaliseForSigning(value: JsonValue): Uint8Array;
778
+
517
779
  /** Terminal outcome a receipt attests. Mirrors the SDK's terminal job statuses. */
518
780
  type ReceiptOutcome = "completed" | "failed" | "cancelled";
519
781
  /** What the caller paid for the job, as persisted on the job record. */
@@ -611,6 +873,8 @@ interface JobReceipt {
611
873
  */
612
874
  credit?: ReceiptCredit;
613
875
  }
876
+ /** A receipt before signing — every field but the signature itself. */
877
+ type UnsignedJobReceipt = Omit<JobReceipt, "signature">;
614
878
  /** Reclaim lifecycle event a {@link DrainReceipt} attests (internal-review, spec §5). */
615
879
  type DrainReceiptEvent = "requested" | "parked" | "picked_up" | "sent" | "released";
616
880
  /**
@@ -665,6 +929,8 @@ interface DrainReceipt {
665
929
  /** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
666
930
  signature: string;
667
931
  }
932
+ /** A drain receipt before signing — every field but the signature itself. */
933
+ type UnsignedDrainReceipt = Omit<DrainReceipt, "signature">;
668
934
  /**
669
935
  * A DVM-signed proof that one payment funded a prepaid credit (internal-review).
670
936
  * The balance and sequence are the values fixed when the funding committed;
@@ -698,6 +964,94 @@ interface FundingReceipt {
698
964
  /** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
699
965
  signature: string;
700
966
  }
967
+ /** A funding receipt before signing. */
968
+ type UnsignedFundingReceipt = Omit<FundingReceipt, "signature">;
969
+ /**
970
+ * Hash the delivered result so a receipt binds to *what* was returned, not
971
+ * just that something was.
972
+ *
973
+ * `sha256(canonicalize({ summary, artifact_hashes }))`, where each artifact
974
+ * hash is `sha256(canonicalize(artifactContent))` in emission order (`[]`
975
+ * when the job emitted none). Hashing the artifact *content object* rather
976
+ * than trusting `content.sha256` — which is optional and which `ctx.artifact`
977
+ * never fills in — keeps the hash recomputable by any consumer from exactly
978
+ * the messages it received.
979
+ *
980
+ * **Pass `summary` only for a `completed` job.** On `failed`/`cancelled` the
981
+ * SDK's durable terminal writes the terminal *reason* into the job's summary
982
+ * column, so a verifier recomputing a non-completed receipt must pass
983
+ * `undefined` here and read the reason off `receipt.reason` instead. The
984
+ * issuer does the same (`resultHashFor` in `sdk/server/receipt-issuer.ts`).
985
+ */
986
+ declare function computeResultHash(summary: string | undefined, artifactContents: JsonValue[]): string;
987
+ /**
988
+ * Sign an unsigned receipt with the DVM's receipt secret, returning the
989
+ * complete receipt. Signs `canonicaliseForSigning(receipt)` — the same
990
+ * sorted-key canonical JSON the caller-auth envelope and the builder
991
+ * attestation use, so {@link verifyReceipt} is symmetric with
992
+ * `verifyCanonicalSignedRequest`. There is no second canonicalisation.
993
+ */
994
+ declare function signReceipt(unsigned: UnsignedJobReceipt, secretHex: string): JobReceipt;
995
+ /**
996
+ * Verify a receipt's self-consistency: the BIP-340 signature over the
997
+ * canonical payload (minus `signature`) under the receipt's own
998
+ * `receipt_pubkey`. Returns `false` rather than throwing on malformed input.
999
+ *
1000
+ * This is only the inner link. A consumer that wants provenance must also
1001
+ * check `receipt_pubkey` against `/v1/info#builder`'s attested
1002
+ * `receipt_pubkey` and verify that attestation under the builder pubkey
1003
+ * (`verifyAttestation` in `builder-identity.ts`).
1004
+ */
1005
+ declare function verifyReceipt(receipt: JobReceipt): boolean;
1006
+ /**
1007
+ * Structural guard on a receipt that arrived from outside this process — off a
1008
+ * `/v1/job` response, or back out of the hand-editable `~/.dvm/receipts.jsonl`.
1009
+ *
1010
+ * Checks only that the load-bearing fields are the right *kind* of thing; it
1011
+ * deliberately does not judge the receipt. A well-formed receipt whose
1012
+ * signature doesn't hold is `invalid` and the caller must see it said so. A
1013
+ * body missing one of these fields is a different animal: consumers dereference
1014
+ * them unconditionally (`receiptDisplay`'s `paid.msats`, the attestation link's
1015
+ * `receipt_pubkey`), so an unguarded one throws `TypeError` and surfaces as an
1016
+ * `internal` exit — and it could never have verified anyway, since the
1017
+ * signature covers the whole canonical body. Refusing it costs no proof.
1018
+ *
1019
+ * One guard for both directions on purpose: what can't come in over the wire
1020
+ * must not be able to come back out of the file.
1021
+ *
1022
+ * Release coupling: adding or requiring a field here changes both the SDK's
1023
+ * emitted wire shape and the caller CLI's accepted shape. Name the first-party
1024
+ * fleet redeploy as a prerequisite in the release note before publishing the
1025
+ * stricter caller; see `public compatibility guide`.
1026
+ */
1027
+ declare function isSignedJobReceipt(value: unknown): value is JobReceipt;
1028
+ /** Sign an unsigned drain receipt — same canonicalisation as {@link signReceipt}. */
1029
+ declare function signDrainReceipt(unsigned: UnsignedDrainReceipt, secretHex: string): DrainReceipt;
1030
+ /** Sign an unsigned funding receipt using the common receipt canonicalisation. */
1031
+ declare function signFundingReceipt(unsigned: UnsignedFundingReceipt, secretHex: string): FundingReceipt;
1032
+ /** Structural guard for a funding receipt received over the wire or from disk. */
1033
+ declare function isFundingReceipt(value: unknown): value is FundingReceipt;
1034
+ /** Verify a funding receipt's inner BIP-340 signature. */
1035
+ declare function verifyFundingReceipt(receipt: FundingReceipt): boolean;
1036
+ /**
1037
+ * Structural guard on a drain receipt from outside this process — off a
1038
+ * `/v1/credit` drain response, or back out of `~/.dvm/credits.json`.
1039
+ *
1040
+ * Same contract and same reasoning as {@link isSignedJobReceipt}: both homes
1041
+ * type these `unknown[]` because neither the wire nor a hand-editable file is
1042
+ * evidence of shape, and the chain walk dereferences `receipt_pubkey`
1043
+ * unconditionally. A body missing one of these fields could never have carried
1044
+ * a valid signature anyway, so refusing it costs no proof.
1045
+ * Required-field changes follow the fleet-before-caller release note linked
1046
+ * from {@link isSignedJobReceipt}.
1047
+ */
1048
+ declare function isDrainReceipt(value: unknown): value is DrainReceipt;
1049
+ /**
1050
+ * Verify a drain receipt's self-consistency, mirroring {@link verifyReceipt}
1051
+ * — the same provenance caveat applies: check `receipt_pubkey` against the
1052
+ * DVM's attested `receipt_pubkey` for the outer link.
1053
+ */
1054
+ declare function verifyDrainReceipt(receipt: DrainReceipt): boolean;
701
1055
 
702
1056
  /**
703
1057
  * Funding lots: the in-kind basis of a non-channel Bitcoin credit (internal-review).
@@ -734,12 +1088,19 @@ interface FundingReceipt {
734
1088
  declare const NON_CHANNEL_BITCOIN_RAILS: readonly ["cashu", "lightning"];
735
1089
  /** A rail whose unused credit is a sats deposit the provider owes back. */
736
1090
  type NonChannelBitcoinRail = (typeof NON_CHANNEL_BITCOIN_RAILS)[number];
1091
+ /** True when `rail` funds a credit with sats the provider holds and owes back. */
1092
+ declare function isNonChannelBitcoinRail(rail: string | null | undefined): rail is NonChannelBitcoinRail;
737
1093
  /**
738
1094
  * One funding event's deposit: the sats that arrived and the credit micro they
739
1095
  * bought, with however much of that micro is still unspent.
740
1096
  *
741
1097
  * `remainingMicro` is depleted FIFO by settled draws and by reclaims, so the
742
- * sum of a credit's lots tracks `credits.balance_micro` exactly.
1098
+ * sum of a credit's lots tracks `credits.balance_micro` exactly — with one
1099
+ * deliberate exception: an expiry release (internal-review) zeroes the balance and
1100
+ * leaves the lots standing, because a revival restores that balance and its
1101
+ * in-kind basis has to still be there. A released credit is excluded from the
1102
+ * sweep floor by `BITCOIN_CREDIT_PREDICATE`'s own `balance_micro > 0`, so the
1103
+ * over-coverage is never counted as sats the builder owes back.
743
1104
  */
744
1105
  interface FundingLot {
745
1106
  lotId: string;
@@ -755,6 +1116,71 @@ interface FundingLot {
755
1116
  fundingRef: string | null;
756
1117
  createdAt: number;
757
1118
  }
1119
+ /** One lot's share of a depletion, and what that share is worth in kind. */
1120
+ interface LotDebit {
1121
+ lotId: string;
1122
+ /** Credit micro taken out of this lot. */
1123
+ micro: number;
1124
+ /** Sats that micro is worth at this lot's own funding rate, floored. */
1125
+ sats: number;
1126
+ }
1127
+ /** What a FIFO depletion took, and what it could not cover. */
1128
+ interface LotDepletion {
1129
+ debits: LotDebit[];
1130
+ /** In-kind value of everything taken — the reclaim obligation. */
1131
+ satsOwed: number;
1132
+ /** Micro the lots could not cover. Non-zero means the credit's lots are short. */
1133
+ uncoveredMicro: number;
1134
+ /** Of the micro taken, how much came out of a lot carrying a real sats basis. */
1135
+ backedMicro: number;
1136
+ }
1137
+ /**
1138
+ * Take `amountMicro` out of `lots`, oldest first, and price what was taken at
1139
+ * each lot's own rate.
1140
+ *
1141
+ * FIFO rather than pro rata across the pool, so a top-up taken at a different
1142
+ * rate is reclaimed at *that* rate once the earlier deposit is spent. A
1143
+ * fraction of a satoshi cannot be handed back, so the conversion floors and
1144
+ * the dust stays with the provider rather than being rounded into a payout the
1145
+ * deposit does not cover.
1146
+ *
1147
+ * A slice is priced as the **decrement in the lot's own obligation** —
1148
+ * `lotSats(before) - lotSats(after)` — rather than by flooring the slice on
1149
+ * its own. The two agree whenever a lot is taken whole, which is every
1150
+ * ordinary reclaim; they diverge once a lot is drawn down in pieces, and there
1151
+ * the independent floor loses up to a satoshi *per piece*, permanently. That
1152
+ * matters since internal-review, where each settling draw is priced through here: the
1153
+ * slices have to telescope, or a lot spent over many jobs pays out less than
1154
+ * it took in and the residue is exactly the unattributable one this was meant
1155
+ * to remove. It also makes {@link lotOwedSats} exact rather than conservative
1156
+ * — the remaining obligation is precisely what future depletions will pay.
1157
+ *
1158
+ * `lots` must already be in FIFO order ({@link fifoOrder}).
1159
+ */
1160
+ declare function depleteLots(lots: FundingLot[], amountMicro: number): LotDepletion;
1161
+ /**
1162
+ * What a settling draw's fiat debit was worth in kind, in millisatoshis
1163
+ * (internal-review) — `null` where the lots cannot price it.
1164
+ *
1165
+ * This is the single sats authority for a non-channel Bitcoin credit. The
1166
+ * settle already depletes the lots the draw consumed, so the figure costs no
1167
+ * extra read: it is `depletion.satsOwed` in the ledger's own msat unit, and
1168
+ * stamping it on `credit_draws.draw_msats` is what makes
1169
+ * `Σ settled draw sats + reclaim sats == Σ sats funded` close per credit.
1170
+ *
1171
+ * `null` follows {@link isInKindDepletion} exactly, so a settle and a reclaim
1172
+ * fall back together: lots short of the balance, or covering lots with no sats
1173
+ * basis, keep the pooled pro-rata figure rather than blending a lot rate with
1174
+ * a blended one. Mixed coverage counts as unpriceable for the same reason it
1175
+ * does on the reclaim.
1176
+ */
1177
+ declare function inKindDrawMsats(depletion: LotDepletion, amountMicro: number): number | null;
1178
+ /**
1179
+ * What these lots owe in kind if every remaining micro were reclaimed now —
1180
+ * the deposit-liability figure, and the floor the hub must never be swept
1181
+ * below.
1182
+ */
1183
+ declare function lotOwedSats(lots: FundingLot[]): number;
758
1184
  /**
759
1185
  * Sats held back from every non-channel Bitcoin reclaim to pay for handing it
760
1186
  * over (internal-review).
@@ -777,7 +1203,502 @@ interface FundingLot {
777
1203
  * rule the per-lot flooring already follows.
778
1204
  */
779
1205
  declare const DRAIN_DELIVERY_RESERVE_SATS = 8;
1206
+ /**
1207
+ * The figure a reclaim publishes: its in-kind gross less
1208
+ * {@link DRAIN_DELIVERY_RESERVE_SATS}, floored at zero.
1209
+ *
1210
+ * Zero is a refusal rather than a payout — a balance worth no more than what
1211
+ * it costs to send is `drain_below_dust`, since parking a zero-value token
1212
+ * would hold the servicer's queue open forever.
1213
+ */
1214
+ declare function netOwedSats(grossSats: number): number;
1215
+ /**
1216
+ * The order lots deplete in: oldest first, `lotId` breaking a tie.
1217
+ *
1218
+ * A tiebreak is not decoration — lot ids are surrogates rather than a
1219
+ * per-credit sequence (`fund` takes no credit lock, so two concurrent
1220
+ * fundings cannot cooperate on a counter), and two fundings can land on the
1221
+ * same millisecond. Without it the FIFO order is whatever the planner
1222
+ * returned, and a reclaim's figure would depend on it.
1223
+ */
1224
+ declare function fifoOrder(left: FundingLot, right: FundingLot): number;
1225
+ /**
1226
+ * Whether a depletion may be settled in kind, or has to fall back to pricing
1227
+ * the fiat balance at a live rate.
1228
+ *
1229
+ * Both failure modes are real and neither is the caller's fault: a credit's
1230
+ * lots can be short (a coverage hole the boot backfill is meant to close), and
1231
+ * a credit funded before internal-review recorded a rail basis at all has lots whose
1232
+ * `satsFunded` is 0 — pricing *that* in kind would answer "we owe you nothing"
1233
+ * for a deposit we plainly hold. Mixed coverage is treated as unbacked too:
1234
+ * blending a lot rate with a live rate produces a figure neither basis
1235
+ * supports.
1236
+ */
1237
+ declare function isInKindDepletion(depletion: LotDepletion, amountMicro: number): boolean;
1238
+
1239
+ /** The rails a payout lands on. Lightning never pays out: credits fund straight into the receive wallet. */
1240
+ type PayoutRail = "cashu" | "x402" | "tempo";
1241
+ /**
1242
+ * What gathered the payments into one transfer: a cashu accumulator melt, an
1243
+ * x402 batch settle to `pay_to`, a Tempo channel's cooperative close, or a
1244
+ * Tempo channel's scheduled / manual settlement to the recipient.
1245
+ */
1246
+ type PayoutKind = "melt" | "batch" | "close" | "settle";
1247
+ /** Which repair verb completed a stuck movement. Only `tempo_close_reconciled` is produced here today. */
1248
+ type PayoutRepairKind = "x402_settlement_reconciled" | "lightning_invoice_reconciled" | "tempo_close_reconciled";
1249
+ /**
1250
+ * JSON wire shape POSTed to the platform's `/_internal/payout` endpoint —
1251
+ * one landed movement into the builder's custody. Idempotent platform-side
1252
+ * on `(dvmId, payoutId)`, so the durable retry loop can redeliver freely; a
1253
+ * `repaired` redelivery of an id that already landed upgrades that row rather
1254
+ * than adding a second.
1255
+ */
1256
+ interface PayoutReportPayload {
1257
+ /** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1258
+ dvmId: string;
1259
+ /** Required. The DVM's own id for the movement — `cashu:melt:<quote>`, `x402:batch:<uuid>`, `tempo:<tx>`. */
1260
+ payoutId: string;
1261
+ rail: PayoutRail;
1262
+ kind: PayoutKind;
1263
+ /** Required. Rail-native atomic units: sats, or USDC micro. Always > 0. */
1264
+ nativeAmount: number;
1265
+ /** Required. `sats` for cashu, `usdc` for the stablecoin rails. */
1266
+ nativeAsset: "sats" | "usdc";
1267
+ /** Where it landed; `null` for a melt, whose Lightning destination the platform never records. */
1268
+ recipient: string | null;
1269
+ /** Mechanism references — mint, melt quote, tx hash, batch id, voucher count, channel id. Description material, never arithmetic. */
1270
+ refs: Record<string, unknown>;
1271
+ /** Required. When the money landed (epoch ms). */
1272
+ landedAt: number;
1273
+ /** Set when a repair verb completed this movement. */
1274
+ repaired?: boolean;
1275
+ /** Required iff `repaired`. */
1276
+ repairKind?: PayoutRepairKind;
1277
+ }
1278
+ /** A stuck movement inside a pending snapshot. */
1279
+ interface PayoutWedge {
1280
+ id: string;
1281
+ /**
1282
+ * `settle`: an x402 batch whose transfer keeps failing (auto-retried).
1283
+ * `refund`: an x402 cooperative refund wedged between chain and ledger —
1284
+ * caller money, not in the pool. `close`: a Tempo close wedged the same way.
1285
+ * `melt`: cashu rows whose builder-side melt failed.
1286
+ */
1287
+ kind: "settle" | "refund" | "close" | "melt";
1288
+ /** Epoch ms the wedge was first observed. */
1289
+ since: number;
1290
+ /** Retry count where the retry is automatic, else null. */
1291
+ attempts: number | null;
1292
+ /** Epoch ms of the next automatic retry, else null. */
1293
+ nextRetry: number | null;
1294
+ lastError: string | null;
1295
+ /** Rail-native amount held up, when known. */
1296
+ native?: number;
1297
+ }
1298
+ /** One pool inside a rail's pending figure — a mint, a batch, a channel. */
1299
+ interface PayoutPool {
1300
+ id: string;
1301
+ native: number;
1302
+ label?: string;
1303
+ }
1304
+ /**
1305
+ * JSON wire shape POSTed to the platform's `/_internal/payout-pending`
1306
+ * endpoint — one rail's snapshot of money still moving toward the builder,
1307
+ * replaced on each report. Best-effort: a lost one is superseded by the next
1308
+ * tick, so it rides no durable queue.
1309
+ */
1310
+ interface PayoutPendingPayload {
1311
+ dvmId: string;
1312
+ rail: PayoutRail;
1313
+ /** Everything on this rail still moving toward the builder, in atomic units. */
1314
+ poolNative: number;
1315
+ nativeAsset: "sats" | "usdc";
1316
+ /** How many pools the figure is made of. */
1317
+ items: number;
1318
+ pools: PayoutPool[];
1319
+ wedged: PayoutWedge[];
1320
+ /** This DVM's clock when the snapshot was taken (epoch ms). */
1321
+ snapshotAt: number;
1322
+ }
1323
+ /** The delivery seam — implemented by `RevenueReporter`, stubbed in tests. */
1324
+ interface PayoutTransport {
1325
+ /**
1326
+ * Queue a payout through the caller's open transaction, so the row and the
1327
+ * fact it reports commit or roll back together; without `tx` it is queued on
1328
+ * its own. Durable once committed — the retry loop delivers it, or
1329
+ * {@link PayoutTransport.drainPending} when a caller wants it now.
1330
+ */
1331
+ enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
1332
+ /** Queue a payout on its own and try to deliver it at once. */
1333
+ reportPayout(payload: PayoutReportPayload): Promise<void>;
1334
+ /** Best-effort: one POST, replaced by the next snapshot. */
1335
+ reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
1336
+ /** Deliver what is queued now rather than on the next retry tick. */
1337
+ drainPending?(): Promise<void>;
1338
+ }
1339
+ /**
1340
+ * A money-path hook whose report must commit with the fact it describes.
1341
+ * `enqueue` runs inside the caller's transaction, and a throw rolls the fact
1342
+ * back with it — the drain report's own discipline (internal-review): a call the
1343
+ * caller has to repeat costs a round trip, where a landed movement with no
1344
+ * row understates paid-out forever. `committed` runs once that transaction
1345
+ * has committed, for the delivery and the snapshot that must not read
1346
+ * uncommitted state.
1347
+ */
1348
+ interface TransactionalPayoutHook<E> {
1349
+ enqueue(event: E, tx: RevenueReporterQuerier): Promise<void>;
1350
+ committed(): void;
1351
+ }
1352
+ /** What `/admin/cashu/mark-melted` knows once the rows are marked. */
1353
+ interface CashuMeltCompleted {
1354
+ rows: {
1355
+ id: string;
1356
+ mintUrl: string;
1357
+ proofAmount: number;
1358
+ }[];
1359
+ meltQuoteId: string;
1360
+ paymentPreimage: string;
1361
+ }
1362
+ /** What mppx reports after a Tempo channel settlement or close confirmed on chain. */
1363
+ interface TempoSessionSettled {
1364
+ txHash: string;
1365
+ channelId: string;
1366
+ trigger: "settle" | "close" | "scheduled";
1367
+ /** Cumulative amount settled to the payee on this channel, atomic units. */
1368
+ amount: bigint;
1369
+ /** Newly settled to the payee by this transaction, atomic units. */
1370
+ delta: bigint;
1371
+ }
1372
+ /** What `reconcile-tempo-drain` re-read from the close receipt it booked against. */
1373
+ interface TempoCloseReconciled {
1374
+ channelId: string;
1375
+ txHash: string;
1376
+ /** Captured by the payee — this DVM — per the receipt, atomic units as a decimal string. */
1377
+ settledToPayee: string;
1378
+ }
1379
+ /** The channel view the tracker sums claims over. */
1380
+ interface X402TrackedChannel {
1381
+ channelId: string;
1382
+ chargedCumulativeAmount: string;
1383
+ totalClaimed: string;
1384
+ }
1385
+ /** A refund settlement wedged between chain and ledger (internal-review), for the wedge list. */
1386
+ interface X402WedgedRefund {
1387
+ settlementId: string;
1388
+ /** Epoch ms the settlement was first prepared. */
1389
+ createdAt: number;
1390
+ native?: number;
1391
+ }
1392
+ /** What the batch-settlement server hands the tracker once it knows its scope. */
1393
+ interface X402PayoutContext {
1394
+ /** `${network}|${payTo}|${token}` — the scope the settle-pending marker is keyed on. */
1395
+ scope: string;
1396
+ payTo: string;
1397
+ network: string;
1398
+ storage: {
1399
+ list(): Promise<X402TrackedChannel[]>;
1400
+ };
1401
+ /** The scheduler's settle cadence — what a wedge's `nextRetry` is derived from. */
1402
+ settleIntervalMs: number;
1403
+ listWedgedRefunds?: () => Promise<X402WedgedRefund[]>;
1404
+ }
1405
+ /** The two manager verbs the tracker wraps. */
1406
+ interface X402TrackedManager {
1407
+ claim(...args: never[]): Promise<{
1408
+ vouchers: number;
1409
+ transaction: string;
1410
+ }[]>;
1411
+ settle(): Promise<{
1412
+ transaction: string;
1413
+ }>;
1414
+ }
1415
+ /**
1416
+ * The batch-settlement server's view of the reporter: attach once with the
1417
+ * scope, then route every claim and settle through the tracked manager so
1418
+ * the open batch is kept and the settle emits the payout.
1419
+ */
1420
+ interface X402PayoutObserver {
1421
+ attach(ctx: X402PayoutContext): void;
1422
+ trackManager<M extends X402TrackedManager>(manager: M): M;
1423
+ /** A settle that landed outside the tracked manager — the manual claim-and-settle's own retry loop. */
1424
+ recordSettle(transaction: string): Promise<void>;
1425
+ /** A settle attempt that failed outside the tracked manager. One call per attempt a builder would count as one. */
1426
+ recordSettleFailure(error: unknown): Promise<void>;
1427
+ }
1428
+ /** The Tempo readers the host attaches after mounting. */
1429
+ interface TempoPayoutReader {
1430
+ /** The recipient address every Tempo payout names. */
1431
+ recipient?: string;
1432
+ /** CAIP-2 network for the refs, when known. */
1433
+ network?: string;
1434
+ listActive?: (limit: number, cursor?: {
1435
+ updatedAt: number;
1436
+ key: string;
1437
+ }) => Promise<{
1438
+ key: string;
1439
+ state: Record<string, unknown>;
1440
+ updatedAt: number;
1441
+ }[]>;
1442
+ listChannelDrains?: (args: {
1443
+ limit: number;
1444
+ after?: ChannelDrainCursor;
1445
+ createdBeforeMs: number;
1446
+ rail: "tempo";
1447
+ }) => Promise<CreditDrainRecord[]>;
1448
+ }
1449
+ /**
1450
+ * The claims gathered since the last settle — the batch the next settle pays
1451
+ * out. Durable across machines because claims run under the fleet lock on
1452
+ * whichever machine won the tick, and the settle that finally moves the money
1453
+ * may run on another: an in-process figure would report a partial amount
1454
+ * after any restart in between.
1455
+ */
1456
+ interface X402OpenBatch {
1457
+ scope: string;
1458
+ batchId: string;
1459
+ claimedNative: bigint;
1460
+ voucherCount: number;
1461
+ claims: number;
1462
+ openedAt: number;
1463
+ attempts: number;
1464
+ firstFailedAt: number | null;
1465
+ lastError: string | null;
1466
+ nextRetryAt: number | null;
1467
+ }
1468
+ /** Where the open batch lives — Postgres beside the settlement rows, or memory in dev. */
1469
+ interface X402BatchStore {
1470
+ get(scope: string): Promise<X402OpenBatch | undefined>;
1471
+ put(batch: X402OpenBatch): Promise<void>;
1472
+ delete(scope: string): Promise<void>;
1473
+ /**
1474
+ * Close the open batch and run `enqueue` in the same transaction, so the
1475
+ * payout row is queued exactly when the batch is gone and never otherwise:
1476
+ * a settle can never lose its payout, because a failed enqueue leaves the
1477
+ * batch open for the next settle to report in full. `false` when the batch
1478
+ * under `scope` is no longer `batchId` — a sibling settled it first — in
1479
+ * which case nothing is queued.
1480
+ */
1481
+ settle(scope: string, batchId: string, enqueue: (tx?: RevenueReporterQuerier) => Promise<void>): Promise<boolean>;
1482
+ }
1483
+ /** Construction options for {@link PayoutReporter}. */
1484
+ interface PayoutReporterOpts {
1485
+ /** Platform DVM record ID, bound into every report. */
1486
+ dvmId: string;
1487
+ transport: PayoutTransport;
1488
+ /**
1489
+ * The SDK's Postgres. Reads the cashu accumulator for the pending pool and
1490
+ * holds the open x402 batch; without it the batch lives in memory (dev) and
1491
+ * no cashu snapshot is taken.
1492
+ */
1493
+ db?: Pool;
1494
+ /**
1495
+ * Whether this DVM runs the cashu accumulator. The cashu snapshot reads
1496
+ * `wallet_accumulator`, which only that mode creates — on any other DVM the
1497
+ * read would fail every tick and warn about a rail it does not carry.
1498
+ */
1499
+ cashu?: boolean;
1500
+ /** Override the batch store (tests); defaults to Postgres on `db`, memory without. */
1501
+ batchStore?: X402BatchStore;
1502
+ /** Snapshot cadence, ms. Default 5 minutes. */
1503
+ snapshotIntervalMs?: number;
1504
+ /** Override for the current time (tests). */
1505
+ now?: () => number;
1506
+ }
1507
+ /**
1508
+ * Builds the payout and pending reports from the SDK's money paths and hands
1509
+ * them to the transport.
1510
+ *
1511
+ * Two entry points are transactional: `cashuMelted` and `tempoCloseReconciled`
1512
+ * queue their row inside the caller's transaction and throw when they cannot,
1513
+ * so the melt or the reconciled close rolls back with its missing report rather
1514
+ * than landing without one. The x402 settle and the pending snapshots are
1515
+ * fail-safe instead: a report that cannot be built or queued there is logged and
1516
+ * picked up on the next tick, never thrown into the settle that produced it.
1517
+ */
1518
+ declare class PayoutReporter implements PayoutTransport {
1519
+ private readonly dvmId;
1520
+ private readonly transport;
1521
+ private readonly db?;
1522
+ private readonly cashu;
1523
+ private readonly batches;
1524
+ private readonly ownsBatchStore;
1525
+ private readonly intervalMs;
1526
+ private readonly now;
1527
+ private x402?;
1528
+ private tempo?;
1529
+ private timer;
1530
+ private warmup;
1531
+ constructor(opts: PayoutReporterOpts);
1532
+ /** Boot DDL for the durable batch store, when this reporter owns one. */
1533
+ init(): Promise<void>;
1534
+ /**
1535
+ * Start the snapshot loop. Unref'd, so it never keeps a scale-to-zero
1536
+ * machine awake; a first snapshot runs shortly after boot so a fresh process
1537
+ * reports without waiting a whole interval.
1538
+ */
1539
+ start(): void;
1540
+ stop(): void;
1541
+ enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
1542
+ reportPayout(payload: PayoutReportPayload): Promise<void>;
1543
+ reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
1544
+ /** Deliver what is queued now; a transport without the seam waits for its retry loop. */
1545
+ flush(): Promise<void>;
1546
+ /** The hook `mark-melted` runs inside its transaction: the mark and its payout commit together. */
1547
+ meltHook(): TransactionalPayoutHook<CashuMeltCompleted>;
1548
+ /**
1549
+ * A melt completed: the accumulator rows are SPENT at the mint and the
1550
+ * Lightning payment reached the builder's destination. One payout per mint
1551
+ * in the call — a `mark-melted` normally names one, since a melt quote
1552
+ * belongs to one mint. The amount is the face value melted; the mint's fee
1553
+ * and the Lightning amount received are builder-machine facts this DVM does
1554
+ * not see.
1555
+ *
1556
+ * Queued through `tx` when the caller is inside the transaction that marks
1557
+ * the rows, and a failure is thrown rather than swallowed so that mark rolls
1558
+ * back with it — see {@link TransactionalPayoutHook}. Without `tx` the rows
1559
+ * are queued on their own and delivered at once.
1560
+ */
1561
+ cashuMelted(event: CashuMeltCompleted, tx?: RevenueReporterQuerier): Promise<void>;
1562
+ /** The host attaches the session store and ledger readers once they exist. */
1563
+ attachTempo(reader: TempoPayoutReader): void;
1564
+ /**
1565
+ * A Tempo channel settled or closed on chain. `delta` is what this
1566
+ * transaction newly paid the recipient; a transaction that paid nothing new
1567
+ * (a close of a fully settled channel) is no payout.
1568
+ */
1569
+ tempoSettled(event: TempoSessionSettled): Promise<void>;
1570
+ /** The hook `reconcile-tempo-drain` runs inside its transaction: the booking and its payout commit together. */
1571
+ tempoRepairHook(): TransactionalPayoutHook<TempoCloseReconciled>;
1572
+ /**
1573
+ * The operator repaired a wedged cooperative close (internal-review). The close's
1574
+ * payee side landed on chain when the close did; if the live hook reported
1575
+ * it, this upgrades that row to `repair`, otherwise it is the row. The
1576
+ * figure is the receipt's payee total for the channel.
1577
+ *
1578
+ * Queued through `tx` when the caller is inside the transaction that books
1579
+ * the drain, and thrown rather than swallowed so that booking rolls back
1580
+ * with it — see {@link TransactionalPayoutHook}.
1581
+ */
1582
+ tempoCloseReconciled(event: TempoCloseReconciled, tx?: RevenueReporterQuerier): Promise<void>;
1583
+ /** The observer the batch-settlement server attaches to and routes its manager through. */
1584
+ x402Observer(): X402PayoutObserver;
1585
+ /**
1586
+ * Wrap the upstream channel manager so every claim grows the open batch and
1587
+ * every settle closes it. The claim delta is read off storage rather than
1588
+ * off upstream's result, which carries only a voucher count; under the
1589
+ * fleet lock the before/after read is consistent. A channel the claim
1590
+ * removed on its way through — a refund's claim-then-delete (internal-review) —
1591
+ * moved whatever it still owed before it went, and that value reaches
1592
+ * `pay_to` in this batch too, so it counts at its pre-claim figure.
1593
+ */
1594
+ private trackX402Manager;
1595
+ private recordX402Claim;
1596
+ private recordX402Settle;
1597
+ private recordX402SettleFailure;
1598
+ /** Every rail this reporter can read, each on its own failure boundary. */
1599
+ snapshot(): Promise<void>;
1600
+ /**
1601
+ * Cashu: every unmelted proof in the accumulator, per mint. Rows whose
1602
+ * builder-side melt failed are still in the pool (they are still at the
1603
+ * mint) and listed as wedged until `restart-failed` clears them.
1604
+ */
1605
+ snapshotCashu(): Promise<void>;
1606
+ /**
1607
+ * x402: the open batch (claimed, not yet transferred) plus every channel's
1608
+ * unclaimed voucher value; wedged when the settle keeps failing, and the
1609
+ * refund settlements stuck between chain and ledger beside it.
1610
+ */
1611
+ snapshotX402(): Promise<void>;
1612
+ /**
1613
+ * Tempo: each active channel's spent-but-unsettled balance — earned, not
1614
+ * yet paid to the recipient — and the cooperative closes wedged between
1615
+ * chain and ledger (internal-review), aged past the same floor the repair queue
1616
+ * uses so an in-flight close is not reported as stuck.
1617
+ */
1618
+ snapshotTempo(): Promise<void>;
1619
+ }
780
1620
 
1621
+ /**
1622
+ * JSON wire shape POSTed to the platform's `/_internal/job-revenue` endpoint
1623
+ * and stored in `pending_revenue_reports.payload` for durable retry.
1624
+ *
1625
+ * The platform-side consumer is `OnJobCompleted`, implemented by
1626
+ * `createRevenueCallback`. Keep the two shapes in sync: the SDK and platform
1627
+ * deploy in lockstep, and any
1628
+ * backwards-incompatible field change requires a coordinated release. No `version`
1629
+ * field is included pre-launch because there are no external container-runtime
1630
+ * consumers yet; add one once the first third-party builder ships a container-runtime
1631
+ * DVM (trigger condition: external builder onboarded via `dvmctl deploy --container`).
1632
+ */
1633
+ interface RevenueReportPayload {
1634
+ /** Required. Platform DVM record ID (`dvms.id`). Identifies which DVM earned the revenue. */
1635
+ dvmId: string;
1636
+ /** Required. Job identifier used by the platform to de-duplicate revenue rows and link to the job record. */
1637
+ jobId: string;
1638
+ /** Required. Total amount paid by the caller in millisatoshis, across all credits that satisfied the job. */
1639
+ paidMsats: number;
1640
+ /** Optional. Cashu mint URL from which the payment tokens were issued. Set only for the `cashu` rail; omitted for all other rails. */
1641
+ paymentMint?: string;
1642
+ /**
1643
+ * Required. Payment rail identifier (`"cashu"`, `"tempo"`, `"x402"`, `"stripe"`).
1644
+ * Mirrors `PaymentMethod` on the platform; carried as a plain string over the wire
1645
+ * so the SDK has no platform-type dependency.
1646
+ */
1647
+ rail: string;
1648
+ /**
1649
+ * Optional. Settlement reference for the credit: EVM tx hash for `x402`, credential
1650
+ * challenge ID for `tempo`, `X-Cashu-Request-Id` UUID for `cashu`. Required by the
1651
+ * platform revenue ledger for `tempo`/`x402` rails (recordJobRevenue throws without it);
1652
+ * nullable for `cashu`.
1653
+ */
1654
+ paymentTxHash?: string;
1655
+ /**
1656
+ * Optional. Rail-native payment amount in atomic units: satoshis for `tempo`, USDC
1657
+ * microunits for `x402`, USD cents for `stripe`. Omitted for `cashu` (platform derives
1658
+ * `paidMsats / 1000`). Must be set together with `nativeAsset`.
1659
+ */
1660
+ nativeAmount?: number;
1661
+ /**
1662
+ * Optional. Asset tag paired with `nativeAmount`: `"sats"`, `"usdc"`, `"usdc.e"`, or
1663
+ * `"usd-cents"`. Both fields must be present for the platform to write a non-cashu
1664
+ * revenue row.
1665
+ */
1666
+ nativeAsset?: string;
1667
+ /**
1668
+ * Optional. Cashu flow discriminator. Set to `"p2pk_accumulator"` when the cashu
1669
+ * accumulator path (internal-review) satisfied the job; omitted for legacy cashu. Written into
1670
+ * `revenue_events.metadata.cashu_flow` by the platform so ops can split per-call cashu
1671
+ * rows by source.
1672
+ */
1673
+ cashuFlow?: string;
1674
+ /**
1675
+ * Optional. Credit this job's payment drew against (internal-review). Present on
1676
+ * every job a ledger-backed payment satisfied; its presence is what tells
1677
+ * the platform the row is a **draw-keyed** revenue event rather than a
1678
+ * pre-credits per-call one.
1679
+ */
1680
+ creditId?: string;
1681
+ /** Optional. The draw this job settled — one draw, one revenue row. */
1682
+ drawId?: string;
1683
+ /** Optional. The draw's fiat amount, 1e-6 of `creditCurrency`. The exact liability offset. */
1684
+ drawAmountMicro?: number;
1685
+ /** Optional. Currency the credit is denominated in (lowercase ISO, e.g. `usd`). */
1686
+ creditCurrency?: string;
1687
+ /**
1688
+ * Optional. Settlement instant (epoch ms) — draws are revenue *when they
1689
+ * settle*, so this dates the row. Omitted for pre-credits rows, which the
1690
+ * platform dates at record time as before.
1691
+ */
1692
+ settledAt?: number;
1693
+ /** Optional. The funding's own rail reference, for reconciling a draw back to its deposit. */
1694
+ fundingRef?: string;
1695
+ /**
1696
+ * Optional. Revenue class when this row is not ordinary service revenue.
1697
+ * `short_pay_forfeit` marks money kept from an underpayment that bought no
1698
+ * job (internal-review operator ruling) so analytics can exclude it.
1699
+ */
1700
+ kind?: string;
1701
+ }
781
1702
  /**
782
1703
  * JSON wire shape POSTed to the platform's `/_internal/credit-deposit`
783
1704
  * endpoint (internal-review). A funding event is a **deposit** — a liability until
@@ -855,6 +1776,14 @@ interface CreditDepositPayload {
855
1776
  /** 32-byte channel id, `0x`-prefixed and lowercased. */
856
1777
  channelId: string;
857
1778
  };
1779
+ /**
1780
+ * Set by the blocked-invoice repair (internal-review): this deposit is a paid
1781
+ * Lightning invoice the builder's agent reconciled onto a credit. The money
1782
+ * landed in the receive wallet at payment time and this deposit is the one
1783
+ * row it gets, so the settlements table types it `repair` off this flag
1784
+ * rather than off a second row.
1785
+ */
1786
+ repaired?: boolean;
858
1787
  }
859
1788
  /**
860
1789
  * JSON wire shape POSTed to the platform's `/_internal/credit-draw-release`
@@ -944,6 +1873,94 @@ type CreditDrawReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditDraw
944
1873
  * it before committing the status CAS that moved the money.
945
1874
  */
946
1875
  type CreditDrainEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrainPayload) => Promise<void>;
1876
+ /**
1877
+ * JSON wire shape POSTed to the platform's `/_internal/credit-expiry-release`
1878
+ * endpoint (internal-review). A prepaid credit that reaches its TTL with an undrawn
1879
+ * remainder RELEASES that remainder to the builder: on cashu, Lightning and
1880
+ * x402 the funding value reached builder custody at funding time or on the
1881
+ * settlement cycle, so expiry extinguishes the caller's remaining claim.
1882
+ * Reported so committed value nets to
1883
+ * `deposited − drawn − drained − released`; without it an expired credit is
1884
+ * owed forever on paper, exactly like an unreported drain.
1885
+ *
1886
+ * Never sent for a Tempo credit. Tempo balances are channel-backed and undrawn
1887
+ * channel value returns to the *caller* at exit, so no release exists on that
1888
+ * rail — the sweep skips them and the platform refuses one outright.
1889
+ *
1890
+ * Two reports share this shape, distinguished only by `reversedAt`:
1891
+ *
1892
+ * - **The release**, written by the expiry sweep with `reversedAt` absent.
1893
+ * - **The reversal**, written when a later funding revives the credit
1894
+ * (`CreditLedger.fund` overwrites `expiry_ms`, so the recorded remainder is
1895
+ * no longer final). It repeats the whole payload rather than sending a
1896
+ * reference, because the retry loop can deliver the two out of order and the
1897
+ * platform's upsert must be able to land either one first.
1898
+ *
1899
+ * Idempotent on `(dvmId, creditId, releaseId)`, so the shared retry loop can
1900
+ * redeliver freely.
1901
+ */
1902
+ interface CreditExpiryReleasePayload {
1903
+ /** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1904
+ dvmId: string;
1905
+ /** Required. Credit whose remainder expired. */
1906
+ creditId: string;
1907
+ /** Required. Per-expiry-episode key; the platform idempotency key with DVM and credit. */
1908
+ releaseId: string;
1909
+ /** Required. Caller whose claim on the remainder ended. */
1910
+ callerPubkey: string;
1911
+ /** Funding rail. `null` on a credit funded before the basis became mandatory. */
1912
+ rail: string | null;
1913
+ /** Required. Fiat micro released — the liability this expiry extinguishes. */
1914
+ amountMicro: number;
1915
+ /** Required. Currency `amountMicro` is 1e-6 of. */
1916
+ creditCurrency: string;
1917
+ /** Required. When the credit's TTL ran out (epoch ms) — when the liability ended. */
1918
+ expiryMs: number;
1919
+ /** Required. When the sweep observed the expiry (epoch ms). */
1920
+ releasedAt: number;
1921
+ /** Set only on the reversal report: when the revival funding landed (epoch ms). */
1922
+ reversedAt?: number | null;
1923
+ }
1924
+ /**
1925
+ * Transactional expiry-release outbox seam, threaded through the sweep's insert
1926
+ * and through the funding that reverses a release (internal-review).
1927
+ *
1928
+ * Implemented by {@link RevenueReporter.enqueueCreditExpiryRelease}; callers
1929
+ * must await it before committing the write that made the release (or its
1930
+ * reversal) true, for the same reason the drain seam does — a report lost
1931
+ * between the two overstates or understates committed value forever.
1932
+ */
1933
+ type CreditExpiryReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload) => Promise<void>;
1934
+ /**
1935
+ * JSON wire shape POSTed to the platform's `/_internal/paid-job-death` endpoint
1936
+ * (internal-review) when the stale-job reaper force-fails a *paid* job — its pending
1937
+ * credit draw is released rather than settled, so the caller's payment is back
1938
+ * on their credit balance and reclaimable by drain. The platform resolves fiat
1939
+ * + owner and sends an ambient notice via the shared alert sink. Unlike
1940
+ * {@link RevenueReportPayload} this carries no durable-retry semantics: the
1941
+ * reaper's `stale_job_failed` structured log is the durable backstop, so a lost
1942
+ * report degrades to log-only rather than blocking the sweep.
1943
+ */
1944
+ interface PaidJobDeathPayload {
1945
+ /** Required. Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
1946
+ dvmId: string;
1947
+ /** Required. Job identifier the reaper force-failed. */
1948
+ jobId: string;
1949
+ /** Required. Capability the job dispatched to (e.g. `"add-episode"`). */
1950
+ capability: string;
1951
+ /** Required. Amount of the released credit draw in millisatoshis. */
1952
+ paidMsats: number;
1953
+ /** Required. Payment rail of the credit draw (`"cashu"` / `"tempo"` / `"x402"`). */
1954
+ rail: string;
1955
+ /** Required. Terminal reason the reaper set: dead worker vs never-terminal idle. */
1956
+ reason: string;
1957
+ /** Optional. Rail-native released amount, paired with `nativeAsset`. */
1958
+ nativeAmount?: number;
1959
+ /** Optional. Asset tag for `nativeAmount` (`"sats"` / `"usdc"` / …). */
1960
+ nativeAsset?: string;
1961
+ /** Optional. Cashu mint URL the credit draw's tokens were issued from. */
1962
+ paymentMint?: string;
1963
+ }
947
1964
  /** Distinct causes for a settled credit draw that could not book revenue. */
948
1965
  type RevenueSkippedNoRailReason = "credit_rail_null" | "credit_rail_unrecognized" | "job_rail_missing" | "ledger_not_configured";
949
1966
  /**
@@ -973,6 +1990,210 @@ interface RevenueSkippedNoRailPayload {
973
1990
  /** Required classification of why the revenue rail was unavailable. */
974
1991
  reason: RevenueSkippedNoRailReason;
975
1992
  }
1993
+ /** Gas-balance observation POSTed by a platform-hosted x402 self-relay. */
1994
+ interface X402SelfRelayGasBalancePayload {
1995
+ /** Platform DVM record ID, bound to the authenticating platform token. */
1996
+ dvmId: string;
1997
+ /** Dedicated relay EOA whose Base ETH balance was read. */
1998
+ address: `0x${string}`;
1999
+ /** CAIP-2 Base network identifier. */
2000
+ network: string;
2001
+ /** Current native balance as a decimal wei string. */
2002
+ balanceWei: string;
2003
+ /** Configured paging floor as a decimal wei string. */
2004
+ lowBalanceWei: string;
2005
+ /** Millisecond timestamp at which the DVM read the balance. */
2006
+ checkedAt: number;
2007
+ }
2008
+ /** RPC-health transition POSTed by a platform-hosted x402 self-relay. */
2009
+ type X402SelfRelayRpcHealthPayload = {
2010
+ /** Platform DVM record ID, bound to the authenticating platform token. */
2011
+ dvmId: string;
2012
+ /** Dedicated relay EOA identifying the affected self-relay. */
2013
+ address: `0x${string}`;
2014
+ /** CAIP-2 Base network identifier. */
2015
+ network: string;
2016
+ /** Healthy transition clears an open RPC page. */
2017
+ status: "healthy";
2018
+ /** Millisecond timestamp at which the DVM observed the transition. */
2019
+ checkedAt: number;
2020
+ } | {
2021
+ /** Platform DVM record ID, bound to the authenticating platform token. */
2022
+ dvmId: string;
2023
+ /** Dedicated relay EOA identifying the affected self-relay. */
2024
+ address: `0x${string}`;
2025
+ /** CAIP-2 Base network identifier. */
2026
+ network: string;
2027
+ /** Failed transition opens or refreshes an RPC page. */
2028
+ status: "failed";
2029
+ /** Redacted failure class; never the provider error or endpoint URL. */
2030
+ reason: X402SelfRelayRpcFailureReason;
2031
+ /** Millisecond timestamp at which the DVM observed the transition. */
2032
+ checkedAt: number;
2033
+ };
2034
+ /** Configured Tempo operator fee-token balance POSTed by a hosted DVM. */
2035
+ interface TempoSettlementBalancePayload {
2036
+ dvmId: string;
2037
+ address: `0x${string}`;
2038
+ network: string;
2039
+ token: `0x${string}`;
2040
+ balanceMicro: string;
2041
+ lowBalanceMicro: string;
2042
+ checkedAt: number;
2043
+ ready: boolean;
2044
+ }
2045
+ /** Redacted Tempo operator fee-funding rejection POSTed by a hosted DVM. */
2046
+ interface TempoSettlementFailurePayload {
2047
+ dvmId: string;
2048
+ address: `0x${string}`;
2049
+ network: string;
2050
+ token: `0x${string}`;
2051
+ operation: "settle" | "close";
2052
+ errorClass: "InsufficientFundsError";
2053
+ checkedAt: number;
2054
+ balanceMicro?: string;
2055
+ channelId?: string;
2056
+ creditId?: string;
2057
+ drainId?: string;
2058
+ }
2059
+ /**
2060
+ * Durable revenue reporter for container-runtime DVMs (internal-review).
2061
+ *
2062
+ * On job completion the SDK calls `report()`, which persists the payload to
2063
+ * a local Postgres table and attempts an immediate POST to the platform.
2064
+ * A background retry loop picks up un-acked rows with exponential backoff.
2065
+ */
2066
+ declare class RevenueReporter {
2067
+ private readonly db;
2068
+ private readonly platformUrl;
2069
+ private readonly platformToken;
2070
+ private retryTimer;
2071
+ private static readonly RETRY_INTERVAL_MS;
2072
+ private static readonly MAX_BACKOFF_MS;
2073
+ private static readonly ABANDON_AFTER_MS;
2074
+ constructor(db: Pool, platformUrl: string, platformToken: string);
2075
+ /** Create the pending-reports table (idempotent). */
2076
+ init(): Promise<void>;
2077
+ /** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
2078
+ private createTables;
2079
+ /** Persist a revenue report and attempt immediate delivery. */
2080
+ report(payload: RevenueReportPayload): Promise<void>;
2081
+ /**
2082
+ * Join a credit deposit to the caller-owned rail transaction.
2083
+ *
2084
+ * This method performs no network I/O: it only writes the reporter-owned
2085
+ * durable queue row through `tx`. The retry drain delivers the row after the
2086
+ * rail transaction commits, including after a process restart.
2087
+ */
2088
+ enqueueDeposit(tx: RevenueReporterQuerier, payload: CreditDepositPayload): Promise<void>;
2089
+ /** Join a released draw to the transaction that made the hold terminal. */
2090
+ enqueueCreditDrawRelease(tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload): Promise<void>;
2091
+ /**
2092
+ * Join a credit drain to the transaction that made it terminal (internal-review).
2093
+ *
2094
+ * Same discipline as {@link enqueueDeposit} and the same reason: no network
2095
+ * I/O here, only the durable queue row, written through `tx` so the report
2096
+ * and the status CAS commit or roll back together. A drain whose row was
2097
+ * lost between the two would overstate outstanding liability forever —
2098
+ * the mirror image of a lost deposit.
2099
+ *
2100
+ * Named for the payload rather than the verb because `drain` already means
2101
+ * "flush the queue" in this class (see {@link drainPending}).
2102
+ */
2103
+ enqueueCreditDrain(tx: RevenueReporterQuerier, payload: CreditDrainPayload): Promise<void>;
2104
+ /**
2105
+ * Join a payout to the transaction that commits the movement it reports
2106
+ * (internal-review) — the batch close, the accumulator mark, the drain booking.
2107
+ * No network I/O, only the durable queue row through `tx`, the discipline
2108
+ * {@link enqueueCreditDrain} set and for the same reason: the money has
2109
+ * already moved on a rail this ledger does not own, so the platform row is
2110
+ * the only record the builder's dashboard has of it, and a row lost between
2111
+ * the fact and the queue would understate "paid out" forever. Without `tx`
2112
+ * the row is queued on the pool, for callers with no fact of their own to
2113
+ * commit. Idempotent on `(dvmId, payoutId)` platform-side, so the retry
2114
+ * loop redelivers freely.
2115
+ */
2116
+ enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
2117
+ /**
2118
+ * Report one landed payout on its own and try to deliver it at once — the
2119
+ * live Tempo settlement, which has no dvmkit transaction to join. Durable
2120
+ * like {@link report}.
2121
+ */
2122
+ reportPayout(payload: PayoutReportPayload): Promise<void>;
2123
+ /**
2124
+ * Report one rail's pending-pool snapshot (internal-review). Best-effort and
2125
+ * fire-and-forget, deliberately unlike {@link reportPayout}: a snapshot is
2126
+ * replaced by the next tick, so queueing a stale one behind an outage would
2127
+ * only deliver figures the platform already has newer ones for.
2128
+ */
2129
+ reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
2130
+ /**
2131
+ * Join a credit-expiry release — or the revival that reverses one — to the
2132
+ * transaction that made it true (internal-review).
2133
+ *
2134
+ * Same discipline as {@link enqueueCreditDrain}: no network I/O, only the
2135
+ * durable queue row written through `tx`. A release whose row was lost after
2136
+ * the sweep committed would understate committed value forever; a lost
2137
+ * reversal would overstate it, which is the worse direction — the builder
2138
+ * would see money as theirs that a revived credit can still buy work with.
2139
+ */
2140
+ enqueueCreditExpiryRelease(tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload): Promise<void>;
2141
+ /**
2142
+ * Report a reaper-force-failed paid job to the platform (internal-review) so the
2143
+ * operator receives an ambient notice that a DVM died or wedged mid-job.
2144
+ * Best-effort and fire-and-forget: unlike {@link report} there is no local
2145
+ * durable queue — a non-2xx or transport failure is structured-logged
2146
+ * (`paid_job_death_report_failed`) and dropped, since the reaper's own
2147
+ * `stale_job_failed` log is the durable record and a failed *alert* must never
2148
+ * wedge the sweep.
2149
+ */
2150
+ reportPaidJobDeath(payload: PaidJobDeathPayload): Promise<void>;
2151
+ /**
2152
+ * Report a settled draw whose revenue event was skipped (internal-review).
2153
+ * Best-effort and fire-and-forget: the SDK's structured
2154
+ * `revenue_skipped_no_rail` log is the durable backstop, so a failed notice
2155
+ * must never wedge terminal handling or the orphan-draw reconciler.
2156
+ */
2157
+ reportRevenueSkippedNoRail(payload: RevenueSkippedNoRailPayload): Promise<void>;
2158
+ /** Report the self-relay's Base gas gauge for platform-side paging and recovery. */
2159
+ reportX402SelfRelayGasBalance(payload: X402SelfRelayGasBalancePayload): Promise<void>;
2160
+ /** Report redacted self-relay RPC health for platform-side paging and recovery. */
2161
+ reportX402SelfRelayRpcHealth(payload: X402SelfRelayRpcHealthPayload): Promise<void>;
2162
+ /** Report the Tempo operator's fee-token gauge for paging and recovery. */
2163
+ reportTempoSettlementBalance(payload: TempoSettlementBalancePayload): Promise<void>;
2164
+ /** Report a redacted operator fee rejection immediately. */
2165
+ reportTempoSettlementFailure(payload: TempoSettlementFailurePayload): Promise<void>;
2166
+ private postTempoSettlementReadiness;
2167
+ /** Start the background retry loop. */
2168
+ startRetryLoop(): void;
2169
+ /** Stop the background retry loop. */
2170
+ stop(): void;
2171
+ /**
2172
+ * Operator-facing queue snapshot: distinguishes the live retry queue from
2173
+ * terminal (stale) rows and breaks the latter down by classification reason
2174
+ * (`token_rotated`, `client_error:<status>`, `abandoned`). Lets monitoring tell
2175
+ * transient backlog apart from permanently-failed rows.
2176
+ */
2177
+ getQueueStats(): Promise<{
2178
+ liveRetry: number;
2179
+ stale: number;
2180
+ byReason: Record<string, number>;
2181
+ }>;
2182
+ /**
2183
+ * Deliver one bounded batch from the durable queue.
2184
+ *
2185
+ * Public so startup/recovery tests and operator tooling can drive the same
2186
+ * drain the background timer uses without reaching into private state.
2187
+ */
2188
+ drainPending(): Promise<void>;
2189
+ /** Durably queue one report of either kind, then try it once immediately. */
2190
+ private enqueue;
2191
+ /** Insert one pending report through either the pool or an open transaction. */
2192
+ private insertPending;
2193
+ private attemptDelivery;
2194
+ /** Mark a pending row terminal (stale) with a classification reason. */
2195
+ private markStale;
2196
+ }
976
2197
 
977
2198
  /** Query surface shared by the pool and an open Postgres transaction. */
978
2199
  type X402ChannelQuerier = Pick<Pool, "query">;
@@ -992,8 +2213,8 @@ interface X402SettlementIntent {
992
2213
  operation: "fund" | "refund";
993
2214
  effectId: string;
994
2215
  paymentId: string;
995
- payload: PaymentPayload;
996
- requirements: PaymentRequirements;
2216
+ payload: PaymentPayload$1;
2217
+ requirements: PaymentRequirements$1;
997
2218
  channelBefore: Channel;
998
2219
  /**
999
2220
  * The channel as it stood when the settlement was handed to the facilitator —
@@ -1012,7 +2233,7 @@ interface X402SettlementIntent {
1012
2233
  submissionBlock?: string;
1013
2234
  pendingId?: string;
1014
2235
  status: X402SettlementStatus;
1015
- response?: SettleResponse;
2236
+ response?: SettleResponse$1;
1016
2237
  /** Epoch ms the row was first prepared. */
1017
2238
  createdAt: number;
1018
2239
  /** Epoch ms of the last status write — what the repair queue ages against. */
@@ -1279,7 +2500,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
1279
2500
  * reversed. On the live path the columns are already `NULL` and this is a
1280
2501
  * no-op.
1281
2502
  */
1282
- recordSettlementResponse(settlementId: string, response: SettleResponse): Promise<void>;
2503
+ recordSettlementResponse(settlementId: string, response: SettleResponse$1): Promise<void>;
1283
2504
  /**
1284
2505
  * Mark the external settlement and ledger effect complete in the active transaction.
1285
2506
  *
@@ -1366,15 +2587,15 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
1366
2587
 
1367
2588
  /** Durable channel seam used to compose a settled draw with consumed Tempo value. */
1368
2589
  interface TempoSessionSettlementStore {
1369
- withDrawPlacement<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle) => Promise<value>, tx?: CreditLedgerQuerier): Promise<value>;
1370
- withTerminalConsumption<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle) => Promise<{
2590
+ withDrawPlacement<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle$1) => Promise<value>, tx?: CreditLedgerQuerier): Promise<value>;
2591
+ withTerminalConsumption<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle$1) => Promise<{
1371
2592
  value: value;
1372
2593
  amount: bigint;
1373
2594
  consume: boolean;
1374
2595
  }>): Promise<value>;
1375
2596
  }
1376
2597
  /** Tempo channel state read while holding its durable row lock. */
1377
- interface TempoSessionLifecycle {
2598
+ interface TempoSessionLifecycle$1 {
1378
2599
  finalized: boolean;
1379
2600
  closeRequestedAt: bigint;
1380
2601
  spent: bigint;
@@ -1451,6 +2672,7 @@ declare class CreditLedger {
1451
2672
  */
1452
2673
  readonly durable = true;
1453
2674
  private x402Settlements?;
2675
+ private expiryReleaseOutbox?;
1454
2676
  constructor(pool: CreditLedgerPool, tempoSessionStore?: TempoSessionSettlementStore | undefined);
1455
2677
  /**
1456
2678
  * Bind the durable x402 settlement state this ledger gates spending on
@@ -1462,6 +2684,22 @@ declare class CreditLedger {
1462
2684
  * channel storage, where no settlement row can exist to wedge.
1463
2685
  */
1464
2686
  useX402SettlementGate(gate: X402RefundSettlementGate): void;
2687
+ /**
2688
+ * Bind the durable outbox that reports credit-expiry releases and their
2689
+ * revivals to the platform (internal-review).
2690
+ *
2691
+ * Held on the ledger rather than threaded through {@link fund} for the same
2692
+ * reason as the gate above: the revival half fires from *every* funding path
2693
+ * — Cashu commit, x402 exact and channel, Tempo, the Lightning invoice
2694
+ * settle, the implicit N=1 per-call payment — and a seam each of those has to
2695
+ * remember to pass is a seam one of them will eventually forget.
2696
+ *
2697
+ * Unbound (a `dvmctl dev` server, a self-hosted builder, a test host) the
2698
+ * sweep and the reversal still run and still write the ledger's own rows;
2699
+ * only the platform report is skipped. Reporting is bookkeeping and must
2700
+ * never gate the ledger.
2701
+ */
2702
+ useCreditExpiryReleaseOutbox(outbox: CreditExpiryReleaseOutbox): void;
1465
2703
  /** Create the `credits` / `credit_draws` tables if absent. Call once at SDK boot. */
1466
2704
  init(): Promise<void>;
1467
2705
  /** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
@@ -1499,9 +2737,15 @@ declare class CreditLedger {
1499
2737
  *
1500
2738
  * Pass `tx` (a client inside a caller-owned `BEGIN`) to commit the rail
1501
2739
  * receive and the ledger credit atomically (spec condition 3 — the internal-review
1502
- * verifier does this). The ledger issues **no** transaction control on `tx`;
1503
- * the upsert is a single statement, so without `tx` it is equally atomic on
1504
- * the pool.
2740
+ * verifier does this). The ledger issues **no** transaction control on `tx`.
2741
+ *
2742
+ * Without `tx` it opens one of its own, because a funding is no longer a
2743
+ * single statement: it upserts the credit, records its funding lot
2744
+ * (internal-review), and reverses any standing expiry release (internal-review) — and that
2745
+ * last leg restores balance and queues a report. A crash between the upsert
2746
+ * and the reversal would leave a revived credit whose release still stands,
2747
+ * which the sweep's own exclusion then makes permanent: `balance_micro > 0`
2748
+ * but a standing release means it is neither drainable nor re-releasable.
1505
2749
  *
1506
2750
  * `basis` records the rail value behind the fiat (internal-review) so each draw can
1507
2751
  * be allocated its share of the rail-native amount actually received. A
@@ -1538,21 +2782,110 @@ declare class CreditLedger {
1538
2782
  * below. This method is the authority on that rule, as it is on the binding
1539
2783
  * rules above; every door preflights it where a refusal is still free.
1540
2784
  */
1541
- fund(args: {
1542
- /** Omit to mint a fresh credit id; provide to top up (or idempotently create). */
1543
- creditId?: string;
1544
- callerPubkey: string;
1545
- /** The DVM's pricing currency; `amountMicro` is 1e-6 of it. */
1546
- currency: string;
1547
- amountMicro: number;
1548
- /** Absolute expiry, ms since epoch. Overwrites the previous value on top-up. */
1549
- expiryMs: number;
1550
- /** Rail-value basis this funding contributes (internal-review, required by internal-review). */
1551
- basis: CreditFundingBasis;
1552
- /** External transaction handle — caller owns BEGIN/COMMIT (spec condition 3). */
1553
- tx?: CreditLedgerQuerier;
2785
+ fund(args: CreditFundArgs): Promise<CreditSnapshot>;
2786
+ /**
2787
+ * The funding itself, on whichever handle {@link fund} chose. Every refusal
2788
+ * it raises is the caller's to see unchanged; a self-opened transaction rolls
2789
+ * back around it.
2790
+ */
2791
+ private fundOn;
2792
+ /**
2793
+ * Undo any expiry release this credit still carries, because a funding just
2794
+ * landed on it (internal-review — the revival rule), restoring the balance the
2795
+ * release took.
2796
+ *
2797
+ * A release records the credit's **final** undrawn remainder and zeroes it
2798
+ * (see {@link releaseExpiredCreditLocked}). Funding adds to the balance and
2799
+ * overwrites `expiry_ms` (spec §5 carry-forward: the credit is a rolling
2800
+ * buffer), so the moment money arrives the recorded remainder is no longer
2801
+ * final: the release stops counting and its micro come back.
2802
+ *
2803
+ * Deliberately unconditional on the incoming expiry rather than gated on
2804
+ * `expiryMs > nowMs`. A release exists only for a credit that had already
2805
+ * lapsed, so any funding that reaches one is money the release did not
2806
+ * account for — and a funding that arrives with an already-past expiry leaves
2807
+ * a credit the very next sweep re-releases at its new, larger remainder. One
2808
+ * rule, self-healing in both directions, and it never needs to read the
2809
+ * pre-funding expiry the upsert has already overwritten.
2810
+ *
2811
+ * Runs on the funding's own handle — which {@link fund} now guarantees is a
2812
+ * transaction — so the reversal, the restored balance and the report commit
2813
+ * with the money or not at all. A reversal that committed without its report
2814
+ * would be unrecoverable: `WHERE reversed_at IS NULL` means no later pass
2815
+ * re-derives it.
2816
+ *
2817
+ * @returns the credit row as the restore left it, or undefined when there was
2818
+ * nothing to reverse. The caller reads its snapshot off this rather than off
2819
+ * the funding upsert's `RETURNING`, which predates the restore.
2820
+ */
2821
+ private reverseExpiryReleases;
2822
+ /**
2823
+ * Release the undrawn remainder of every credit whose TTL has run out
2824
+ * (internal-review). Returns the releases this pass recorded.
2825
+ *
2826
+ * **What is released.** The credit's whole `balance_micro`. Expiry ends
2827
+ * spending but never ownership of the record, so the balance stays readable;
2828
+ * what ends is the caller's claim on it, and on the prepaid rails the value
2829
+ * behind it is already in builder custody.
2830
+ *
2831
+ * **Tempo is excluded**, at both of its markers (`tempo_channel_id` and a
2832
+ * `tempo` rail). A Tempo credit is channel-backed and its undrawn value
2833
+ * returns to the *caller* at channel exit, so releasing it would book the
2834
+ * builder money the chain is about to hand back.
2835
+ *
2836
+ * **A credit with a pending hold is skipped, not partially released.** A draw
2837
+ * placed before expiry stays settleable afterwards (`draw`'s replay lookup
2838
+ * runs above the expiry check, so a lost response is still recoverable), so
2839
+ * the remainder is not final while a hold is outstanding. Skipping costs one
2840
+ * sweep interval and keeps the released figure exactly "what nothing bought";
2841
+ * holds do resolve — the orphan-draw watchdog (internal-review) is what guarantees
2842
+ * a stranded one still reaches a terminal state.
2843
+ *
2844
+ * **Idempotent** two ways. `release_id` is derived from the expiry instant,
2845
+ * so a re-sweep after a crash between the insert and its report collides on
2846
+ * the same primary key instead of writing a second row; and the candidate
2847
+ * scan excludes any credit that already has an un-reversed release.
2848
+ *
2849
+ * Each candidate is re-checked under the credit row lock every
2850
+ * `credit_draws` write is taken under, so a draw racing the sweep either
2851
+ * loses the race (its credit is already released and it would have been
2852
+ * refused `credit_expired` anyway) or wins it and leaves a pending hold the
2853
+ * locked re-check sees.
2854
+ */
2855
+ sweepExpiredCredits(args?: {
1554
2856
  nowMs?: number;
1555
- }): Promise<CreditSnapshot>;
2857
+ /** Credits released per pass. Bounds the work a long-idle DVM does at boot. */
2858
+ limit?: number;
2859
+ }): Promise<{
2860
+ released: CreditExpiryRelease[];
2861
+ }>;
2862
+ /**
2863
+ * Write one credit's expiry release under its row lock, zero the balance it
2864
+ * released, and re-assert every condition the unlocked candidate scan tested.
2865
+ * Returns undefined when the credit no longer qualifies — funded, drawn
2866
+ * against, or already released between the scan and the lock.
2867
+ *
2868
+ * **Zeroing the balance is what makes the release real**, and it is the whole
2869
+ * reason this runs under the lock rather than as a bare INSERT. The row alone
2870
+ * records that the caller's claim ended; it does not *end* it. `requestDrain`
2871
+ * has no expiry check by design (expiry gates new draws, not reclaims), so a
2872
+ * credit whose remainder had been released and reported was still fully
2873
+ * drainable: the DVM would pay out money the platform had already booked as
2874
+ * the builder's, and `deposited - drawn - drained - released` would go
2875
+ * negative on the same micro. With the balance at zero, `requestDrain`'s
2876
+ * existing `availableMicro <= 0` guard refuses with `nothing_to_drain` and no
2877
+ * new check is needed anywhere. {@link reverseExpiryReleases} puts the
2878
+ * balance back when a funding revives the credit.
2879
+ *
2880
+ * `status` deliberately stays `active`: a released credit must remain
2881
+ * fundable, or the revival rule has nothing to revive.
2882
+ *
2883
+ * The zeroed balance also takes the credit out of `BITCOIN_CREDIT_PREDICATE`
2884
+ * (`balance_micro > 0`), so the in-kind sweep floor stops counting sats the
2885
+ * builder no longer owes back — and counts them again after a revival, with
2886
+ * the credit's funding lots untouched throughout.
2887
+ */
2888
+ private releaseExpiredCreditLocked;
1556
2889
  /**
1557
2890
  * Record this funding's in-kind basis as a lot (internal-review).
1558
2891
  *
@@ -2054,7 +3387,15 @@ declare class CreditLedger {
2054
3387
  listTempoCreditLosses(args?: {
2055
3388
  limit?: number;
2056
3389
  }): Promise<TempoCreditLoss[]>;
2057
- /** Retire the active credit bound to a chain-proven empty x402 channel. */
3390
+ /**
3391
+ * Reconcile the credit bound to a chain-proven empty x402 channel.
3392
+ *
3393
+ * A channel with no unclaimed backing makes every remaining customer
3394
+ * liability unsafe, so those credits become terminal. A fully settled credit
3395
+ * has neither a balance nor an unfinished drain and is safe to retain for a
3396
+ * later deposit. This also repairs a zero-liability row an older build
3397
+ * terminalized from the same observation.
3398
+ */
2058
3399
  terminalizeX402Credit(evidence: X402CreditLossEvidence, tx?: CreditLedgerQuerier): Promise<X402CreditLoss[]>;
2059
3400
  private terminalizeX402CreditLocked;
2060
3401
  /** List terminal x402 credit losses, newest observation first. */
@@ -2386,6 +3727,27 @@ interface CreditLedgerLike {
2386
3727
  * {@link CreditLedger.useX402SettlementGate}.
2387
3728
  */
2388
3729
  useX402SettlementGate?(gate: X402RefundSettlementGate): void;
3730
+ /**
3731
+ * Bind the outbox that reports credit-expiry releases and their revivals
3732
+ * (internal-review).
3733
+ *
3734
+ * Optional, and absent on `MemoryCreditLedger`: an expiry release is a
3735
+ * durable liability fact, and a ledger whose balances do not survive a
3736
+ * restart has none to report. A host on a non-durable ledger simply never
3737
+ * sweeps. See {@link CreditLedger.useCreditExpiryReleaseOutbox}.
3738
+ */
3739
+ useCreditExpiryReleaseOutbox?(outbox: CreditExpiryReleaseOutbox): void;
3740
+ /**
3741
+ * Release the undrawn remainder of every credit past its TTL (internal-review).
3742
+ * Optional on the same terms as the binder above. See
3743
+ * {@link CreditLedger.sweepExpiredCredits}.
3744
+ */
3745
+ sweepExpiredCredits?(args?: {
3746
+ nowMs?: number;
3747
+ limit?: number;
3748
+ }): Promise<{
3749
+ released: CreditExpiryRelease[];
3750
+ }>;
2389
3751
  /**
2390
3752
  * Ask that gate directly, so a route can refuse a wedged channel **before**
2391
3753
  * the rail moves anything (internal-review). Optional on the same terms as the
@@ -2641,6 +4003,38 @@ interface CreditDrawReleaseOutbox {
2641
4003
  /** Reporter-owned insert executed through the ledger transaction. */
2642
4004
  enqueue: CreditDrawReleaseEnqueue;
2643
4005
  }
4006
+ /** Reporter seam for credit-expiry releases and the revivals that reverse them (internal-review). */
4007
+ interface CreditExpiryReleaseOutbox {
4008
+ /** Platform DVM record ID bound into the report. */
4009
+ dvmId: string;
4010
+ /** Reporter-owned insert executed through the ledger transaction. */
4011
+ enqueue: CreditExpiryReleaseEnqueue;
4012
+ }
4013
+ /**
4014
+ * One recorded expiry episode: a credit's undrawn remainder passing to the
4015
+ * builder when its TTL ran out (internal-review).
4016
+ *
4017
+ * `reversedAt` is set when a later funding revived the credit, at which point
4018
+ * the release stops counting against committed value — the row stays as the
4019
+ * record that it once did.
4020
+ */
4021
+ interface CreditExpiryRelease {
4022
+ creditId: string;
4023
+ /** Per-expiry-episode key, derived from {@link CreditExpiryRelease.expiryMs}. */
4024
+ releaseId: string;
4025
+ callerPubkey: string;
4026
+ currency: string;
4027
+ /** Funding rail, or `null` on a credit funded before the basis was mandatory. */
4028
+ rail: string | null;
4029
+ /** Fiat micro released, 1e-6 of `currency`. */
4030
+ amountMicro: number;
4031
+ /** When the credit's TTL ran out — when the liability ended. */
4032
+ expiryMs: number;
4033
+ /** When the sweep observed the expiry. */
4034
+ releasedAt: number;
4035
+ /** When a funding revived the credit and undid this release. */
4036
+ reversedAt: number | null;
4037
+ }
2644
4038
  /** Pool-shaped handle (`query` + `connect`). Required for the locked write paths. */
2645
4039
  type CreditLedgerPool = Pick<Pool, "query" | "connect">;
2646
4040
  /**
@@ -3059,6 +4453,22 @@ interface CreditFundingBasis {
3059
4453
  maxClaimableAmount: string;
3060
4454
  };
3061
4455
  }
4456
+ /** What {@link CreditLedger.fund} takes. Named so the private body can share the shape. */
4457
+ interface CreditFundArgs {
4458
+ /** Omit to mint a fresh credit id; provide to top up (or idempotently create). */
4459
+ creditId?: string;
4460
+ callerPubkey: string;
4461
+ /** The DVM's pricing currency; `amountMicro` is 1e-6 of it. */
4462
+ currency: string;
4463
+ amountMicro: number;
4464
+ /** Absolute expiry, ms since epoch. Overwrites the previous value on top-up. */
4465
+ expiryMs: number;
4466
+ /** Rail-value basis this funding contributes (internal-review, required by internal-review). */
4467
+ basis: CreditFundingBasis;
4468
+ /** External transaction handle — caller owns BEGIN/COMMIT (spec condition 3). */
4469
+ tx?: CreditLedgerQuerier;
4470
+ nowMs?: number;
4471
+ }
3062
4472
  /**
3063
4473
  * Allocate a draw's share of a credit's unspent rail value (internal-review). Pure —
3064
4474
  * shared by the Postgres and in-memory ledgers so the two can't drift.
@@ -3244,6 +4654,17 @@ declare class CreditLedgerError extends Error {
3244
4654
  constructor(code: CreditLedgerErrorCode, message: string, details?: CreditLedgerErrorDetails, opts?: ErrorOptions);
3245
4655
  }
3246
4656
 
4657
+ /** One durable Tempo session-channel row exposed to reconciliation workers. */
4658
+ interface TempoSessionRow {
4659
+ key: string;
4660
+ state: Record<string, unknown>;
4661
+ updatedAt: number;
4662
+ }
4663
+ /** Cursor for stable pagination over active Tempo session channels. */
4664
+ interface TempoSessionCursor {
4665
+ updatedAt: number;
4666
+ key: string;
4667
+ }
3247
4668
  /** Durable state joining a credit drain to its cooperative channel close. */
3248
4669
  interface TempoSessionCloseRecord {
3249
4670
  channelId: string;
@@ -3261,7 +4682,212 @@ type TempoSessionCloseForecastResolution<value> = {
3261
4682
  } | {
3262
4683
  outcome: "missing";
3263
4684
  };
4685
+ /** The voucher increment observed while holding the channel row lock. */
4686
+ interface TempoVoucherDelta {
4687
+ key: string;
4688
+ cumulativeAmount: bigint;
4689
+ priorCumulativeAmount: bigint;
4690
+ delta: bigint;
4691
+ }
4692
+ /** Result of a ledger resolution composed with terminal channel consumption. */
4693
+ interface TempoSessionConsumption<value> {
4694
+ value: value;
4695
+ amount: bigint;
4696
+ consume: boolean;
4697
+ }
4698
+ /** Channel lifecycle fields exposed while its row lock is held. */
4699
+ interface TempoSessionLifecycle {
4700
+ finalized: boolean;
4701
+ closeRequestedAt: bigint;
4702
+ spent: bigint;
4703
+ settledOnChain: bigint;
4704
+ highestVoucherAmount: bigint;
4705
+ }
4706
+ /**
4707
+ * Postgres implementation of mppx's atomic channel store.
4708
+ *
4709
+ * Every mutation takes an advisory transaction lock keyed by the channel
4710
+ * before locking its row with `FOR UPDATE`, applies mppx's synchronous
4711
+ * transition exactly once, and commits before returning. The advisory lock
4712
+ * covers a channel's first write, when there is no row for `FOR UPDATE` to
4713
+ * lock. Together these are the cross-machine linearization point for voucher,
4714
+ * top-up, spend, and close state; mppx's process-local fallback is
4715
+ * intentionally never used in a durable SDK host.
4716
+ *
4717
+ * Lock order remains channel before credit: mutation paths take the advisory
4718
+ * lock and then the row lock, while terminal consumption requires an existing
4719
+ * row and starts there. Both own the channel lock before the ledger takes its
4720
+ * parent-credit lock through the same client.
4721
+ *
4722
+ * {@link withVoucherAcceptance} acquires its pooled client **lazily**, on the
4723
+ * first store mutation rather than up front (internal-review). Its `operation` is
4724
+ * mppx's credential verification, which does the chain work before it touches
4725
+ * the store: an `open` submits the escrow transaction and awaits its receipt,
4726
+ * a `voucher` reads channel state over public RPC. Holding a connection across
4727
+ * that starves everything else on the shared host pool — `/v1/job`, the job
4728
+ * store, KV, the credit ledger, the replay store — and exposes the open
4729
+ * transaction to `idle_in_transaction_session_timeout`. This is the same shape
4730
+ * internal-review removed from the x402 settlement path. Laziness costs nothing in
4731
+ * atomicity: the `FOR UPDATE` row lock was always taken at the first mutation,
4732
+ * never at `BEGIN`, so the critical section is unchanged — only the
4733
+ * connection-holding window shrinks to the database work it actually covers.
4734
+ */
4735
+ declare class PostgresTempoSessionStore implements Store.AtomicStore {
4736
+ private readonly pool;
4737
+ private readonly acceptance;
4738
+ constructor(pool: Pool);
4739
+ /** Create the channel table under the SDK-wide migration lock. */
4740
+ init(): Promise<void>;
4741
+ /** Read one channel snapshot. */
4742
+ get(key: string): Promise<unknown>;
4743
+ /** Replace one channel snapshot. Used by mppx only for non-RMW maintenance. */
4744
+ put(key: string, value: unknown): Promise<void>;
4745
+ /** Delete one channel snapshot. */
4746
+ delete(key: string): Promise<void>;
4747
+ /** Atomic read-modify-write using a row lock shared by every SDK replica. */
4748
+ update<result>(key: string, fn: (current: unknown) => Store.Change<unknown, result>): Promise<result>;
4749
+ /** Compose voucher acceptance and its ledger mutation under the channel-row lock. */
4750
+ withVoucherAcceptance<value, result>(voucher: {
4751
+ channelId: string;
4752
+ cumulativeAmount: bigint;
4753
+ }, commit: (tx: PoolClient, voucher: TempoVoucherDelta) => Promise<result>, operation: () => Promise<value>): Promise<{
4754
+ value: value;
4755
+ result: result | undefined;
4756
+ }>;
4757
+ /**
4758
+ * Check out the acceptance transaction's client, opening it on first demand.
4759
+ *
4760
+ * An operation that never mutates the store therefore never takes a
4761
+ * connection and never opens an empty transaction — the caller sees the same
4762
+ * `result: undefined` it saw before, because nothing committed either way.
4763
+ *
4764
+ * The in-flight checkout is memoized, not just its result: mppx awaits each
4765
+ * store call today, but two concurrent first mutations would otherwise open
4766
+ * two transactions and leak the one nobody keeps a handle to.
4767
+ */
4768
+ private acceptanceClient;
4769
+ /** Serialize a credit draw with the channel lifecycle that authorizes it. */
4770
+ withDrawPlacement<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle) => Promise<value>, tx?: CreditLedgerQuerier): Promise<value>;
4771
+ private withLockedLifecycle;
4772
+ /**
4773
+ * Compose a terminal ledger resolution with the channel's consumed value.
4774
+ *
4775
+ * Voucher acceptance records spending authority; only a successful job
4776
+ * consumes it. Holding the channel row while the callback locks and settles
4777
+ * the credit keeps that distinction atomic across machines and preserves the
4778
+ * shared channel -> credit lock order.
4779
+ */
4780
+ withTerminalConsumption<value>(channelId: string, operation: (tx: PoolClient, lifecycle: TempoSessionLifecycle) => Promise<TempoSessionConsumption<value>>): Promise<value>;
4781
+ /** Retire lost backing while holding the channel row ahead of every credit lock. */
4782
+ withFinalizedCreditLoss<value>(channelId: string, operation: (tx: PoolClient, lifecycle: TempoSessionLifecycle) => Promise<value>): Promise<value>;
4783
+ /** Page active channel snapshots for the close/settlement watcher. */
4784
+ listActive(limit?: number, cursor?: TempoSessionCursor): Promise<TempoSessionRow[]>;
4785
+ /** Persist a drain's close intent before its credential is broadcast. */
4786
+ beginClose(channelId: string, drainId: string): Promise<TempoSessionCloseRecord>;
4787
+ /** Reserve a close intent and run its ledger debit in the same transaction. */
4788
+ withCloseIntent<value>(channelId: string, drainId: string, operation: (tx: PoolClient) => Promise<value>): Promise<value>;
4789
+ /**
4790
+ * Resolve a reserved close's forecast under the intent row lock.
4791
+ *
4792
+ * `proceed` opens the broadcast gate. `release` deletes the intent and runs
4793
+ * the ledger restore in the same transaction, but only while the gate is
4794
+ * still pending. A concurrent proceed wins by returning `proceed` to the
4795
+ * releaser, which must not restore value that may already be broadcasting.
4796
+ */
4797
+ resolveCloseForecast<value>(channelId: string, drainId: string, decision: TempoSessionCloseForecastDecision, operation?: (tx: PoolClient) => Promise<value>): Promise<TempoSessionCloseForecastResolution<value>>;
4798
+ /** Delete a matching close intent and release its ledger debit atomically. */
4799
+ releaseCloseIntent<value>(channelId: string, drainId: string, operation: (tx: PoolClient) => Promise<value>): Promise<value>;
4800
+ /** Read a persisted cooperative-close intent or confirmed result. */
4801
+ getClose(channelId: string): Promise<TempoSessionCloseRecord | null>;
4802
+ /** Persist proof that a cooperative close reached terminal chain state. */
4803
+ confirmClose(channelId: string, drainId: string, reference: string): Promise<TempoSessionCloseRecord>;
4804
+ private runCloseIntentTransaction;
4805
+ private transaction;
4806
+ private writeCloseIntent;
4807
+ private updateInTransaction;
4808
+ }
3264
4809
 
4810
+ /**
4811
+ * Options for assembling an Mppx server handle from explicit recipient strings
4812
+ * and the surrounding environment. The SDK boundary owns env reads so DVM
4813
+ * authors only think in terms of "set DVMKIT_TEMPO_RECIPIENT /
4814
+ * DVMKIT_TEMPO_SECRET_KEY in your DVM env" — same pattern as the existing
4815
+ * payment-rail inputs.
4816
+ */
4817
+ interface MppOpts {
4818
+ /**
4819
+ * Tempo recipient (0x-prefixed 40-char hex address). When set, registers
4820
+ * `tempo/charge`. Currency defaults to USDC on Tempo mainnet; override via
4821
+ * the `DVMKIT_TEMPO_CURRENCY` env var for testnet/devnet deployments.
4822
+ */
4823
+ tempoRecipient?: string;
4824
+ /**
4825
+ * Server realm for advertising on `/v1/info`. Default resolution lives in
4826
+ * mppx (env vars `MPP_REALM`, `FLY_APP_NAME`, `VERCEL_URL`, request URL,
4827
+ * `"MPP Payment"`).
4828
+ */
4829
+ realm?: string;
4830
+ /**
4831
+ * When set, only register MPP methods whose name appears in this list, even
4832
+ * if the per-rail env var (`DVMKIT_TEMPO_RECIPIENT`) is configured
4833
+ * (internal-review). Lets operators temporarily disable a rail without unsetting
4834
+ * other knobs. Empty/undefined falls back to "register everything that has
4835
+ * a configured recipient".
4836
+ */
4837
+ methodsAllowlist?: string[];
4838
+ /**
4839
+ * Durable replay protection for the one-shot `tempo/charge` method
4840
+ * (internal-review). Omit and mppx falls back to `Store.memory()`, whose consumed
4841
+ * transaction hashes are per-process — invisible to sibling machines and lost
4842
+ * on restart. Pass a cross-machine store (`PostgresTempoChargeStore`) on any
4843
+ * multi-instance deploy.
4844
+ *
4845
+ * The same store also backs mppx's fee-sponsor budget and, because mppx
4846
+ * enables proof-credential replay protection only when a store is supplied,
4847
+ * turns that guard on as a side effect.
4848
+ */
4849
+ tempoCharge?: {
4850
+ store: Store.AtomicStore;
4851
+ };
4852
+ /**
4853
+ * Durable TIP-1034 session support. Omit to keep the existing one-shot
4854
+ * `tempo/charge` method only. The account must be able to submit settlement
4855
+ * transactions as the channel payee/operator; an address-only client is not
4856
+ * sufficient.
4857
+ */
4858
+ tempoSession?: {
4859
+ store: Store.AtomicStore;
4860
+ account: Account;
4861
+ getClient?: (parameters: {
4862
+ chainId?: number;
4863
+ }) => Client | Promise<Client>;
4864
+ chainId?: number;
4865
+ settlementSchedule?: {
4866
+ units?: number;
4867
+ amount?: string | bigint;
4868
+ intervalMs?: number;
4869
+ /**
4870
+ * How long a settlement holds its in-flight lease (internal-review). Defaults to
4871
+ * {@link TEMPO_SETTLEMENT_LEASE_MS}, which documents the receipt-timeout
4872
+ * floor an override has to stay above.
4873
+ */
4874
+ leaseMs?: number;
4875
+ };
4876
+ beforeSessionSettlement?: () => string | undefined | Promise<string | undefined>;
4877
+ onSessionSettlement?: (context: {
4878
+ txHash: `0x${string}`;
4879
+ channelId: `0x${string}`;
4880
+ trigger: "settle" | "close" | "scheduled";
4881
+ amount: bigint;
4882
+ delta: bigint;
4883
+ recoveryVersion?: string;
4884
+ }) => void | Promise<void>;
4885
+ onInsufficientFunds?: (context: {
4886
+ operation: "settle";
4887
+ channelId: string;
4888
+ }) => void | Promise<void>;
4889
+ };
4890
+ }
3265
4891
  /**
3266
4892
  * Loose runtime view of an `Mppx.create(...)` handle. mppx's full generic
3267
4893
  * typing is precise but propagates badly through layers — the SDK boundary
@@ -3464,6 +5090,36 @@ interface TempoSessionDrainState {
3464
5090
  deposit: bigint;
3465
5091
  finalized: boolean;
3466
5092
  }
5093
+ /**
5094
+ * Attach the typed `issueChallenge` dispatcher to a freshly-created mppx
5095
+ * handle. Production builds via `createMppFromOpts`; tests build via
5096
+ * `Mppx.create(...)` directly and use `wrapMppx` to satisfy the SDK boundary.
5097
+ *
5098
+ * Accepts `unknown` because mppx's `Mppx<methods, transport>` generic doesn't
5099
+ * collapse cleanly through `ReturnType<typeof Mppx.create>` — the default
5100
+ * pins `methods: readonly []` and rejects any concrete method tuple. We cast
5101
+ * inside the wrapper instead of leaking the generic onto every caller.
5102
+ */
5103
+ declare function wrapMppx(mppx: unknown): MppxServer;
5104
+ interface TempoSessionChainOps {
5105
+ getChannelStatesBatch(client: Client, channelIds: readonly Hex[], escrowContract: `0x${string}`): Promise<{
5106
+ settled: bigint;
5107
+ deposit: bigint;
5108
+ closeRequestedAt: number;
5109
+ }[]>;
5110
+ settle(store: Store.AtomicStore, client: Client, channelId: Hex, options: {
5111
+ account: Account;
5112
+ escrowContract: `0x${string}`;
5113
+ feeToken: `0x${string}`;
5114
+ onSessionSettlement?: NonNullable<MppOpts["tempoSession"]>["onSessionSettlement"];
5115
+ }): Promise<Hex>;
5116
+ readChannelClose(client: Client, channelId: Hex, txHash: Hex): Promise<TempoChannelCloseAmounts>;
5117
+ }
5118
+ declare function attachTempoSessionRuntime(mppx: MppxServer, config: MppOpts["tempoSession"], chainOpOverrides?: Partial<TempoSessionChainOps>): MppxServer;
5119
+ /** Test-only seams for deterministic watcher coverage without a live Tempo RPC. */
5120
+ declare const _testing: {
5121
+ attachTempoSessionRuntime: typeof attachTempoSessionRuntime;
5122
+ };
3467
5123
 
3468
5124
  /** Envelope fields the verifier validates around the signed payload. */
3469
5125
  interface CanonicalEnvelope {
@@ -3575,6 +5231,28 @@ interface SignedRequestVerifier<T> {
3575
5231
  }
3576
5232
  /** Failure mode taxonomy returned from `verify()`. */
3577
5233
  type SignedRequestFailure = "schema_invalid" | "timestamp_drift" | "signature_invalid" | "replay_detected";
5234
+ /**
5235
+ * The four fields every signed-request envelope carries around the payload,
5236
+ * with the runtime type each must have once the schema has parsed.
5237
+ *
5238
+ * The single definition of "which part of a schema failure is the envelope's":
5239
+ * the post-parse shape check in `parse()` iterates it, and `authErrorBody`'s
5240
+ * internal-review classifier reads its keys to decide whether a `schema_invalid` throw
5241
+ * is the caller's *input* being wrong (disclosable: the schema is public on
5242
+ * `/v1/info`) or their *signing* being wrong (folded into `signature_invalid`,
5243
+ * one answer for every key and signature failure). `satisfies` pins it to
5244
+ * {@link CanonicalEnvelope}, so a fifth envelope field can't be added to the
5245
+ * type without both readers picking it up.
5246
+ */
5247
+ declare const SIGNED_ENVELOPE_TYPES: {
5248
+ readonly pubkey: "string";
5249
+ readonly signature: "string";
5250
+ readonly timestamp: "number";
5251
+ readonly nonce: "string";
5252
+ readonly auth_statement: "object";
5253
+ };
5254
+ /** The envelope field names — {@link SIGNED_ENVELOPE_TYPES}' keys. */
5255
+ declare const SIGNED_ENVELOPE_FIELDS: readonly (keyof CanonicalEnvelope)[];
3578
5256
  /** Structured error from `verify()` — `sub_reason` is the failure kind. */
3579
5257
  declare class SignedRequestError extends Error {
3580
5258
  readonly sub_reason: SignedRequestFailure;
@@ -3640,6 +5318,23 @@ declare function signedRequestInput(body: {
3640
5318
  input?: string;
3641
5319
  data?: unknown;
3642
5320
  }): unknown;
5321
+ /**
5322
+ * Build the bounded in-memory FIFO replay store used as the default when no
5323
+ * cross-machine `SignedRequestReplayStore` is supplied. 10-minute retention
5324
+ * window, 100k entry cap, per-process. Exported so `secp256k1Auth(...)` can
5325
+ * pre-build one and share it across every per-schema verifier plus its own
5326
+ * `recordReplay` path (internal-review).
5327
+ *
5328
+ * When the cap is hit after the time-expired sweep, fresh inserts are
5329
+ * **rejected** (return `true` — treated as replay at the wire) rather than
5330
+ * silently evicting an in-window entry. Silent eviction would flush a
5331
+ * still-valid nonce back to "unseen" and let an attacker who can drive cap
5332
+ * pressure replay it (internal-review). A rate-limited structured warning fires so
5333
+ * operators see saturation in `fly logs`; reaching cap means in-memory has
5334
+ * been pushed past where it's safe and the deploy should move to
5335
+ * `PostgresReplayStore`.
5336
+ */
5337
+ declare function createDefaultReplayStore(): SignedRequestReplayStore;
3643
5338
 
3644
5339
  /**
3645
5340
  * A DVM-level auth scheme, declared on `DVMDescriptor.auth`. Authentication
@@ -4123,6 +5818,35 @@ type PaymentMethod = "cashu" | "x402" | "tempo";
4123
5818
  * stops that dead rail from reappearing every time the funding menu grows.
4124
5819
  */
4125
5820
  type FundingMethod = PaymentMethod | "lightning";
5821
+ /**
5822
+ * Whether a failed job on a given rail can return the caller's funds (internal-review).
5823
+ *
5824
+ * **No rail refunds on `ctx.fail`, and no code behind the idea any more.** Under
5825
+ * the P2PK-accumulator Cashu path — the only Cashu path post-internal-review/internal-review —
5826
+ * `verifyAccumulatorReceipt` commits the caller's proofs straight into
5827
+ * `wallet_accumulator` and leaves `job.receivedProofs` empty; every producer in
5828
+ * `payment.ts` returns `[]`. The `ctx.fail(err, { refund: true })` branch that
5829
+ * used to send proofs back gated on those held proofs, so it had been an
5830
+ * unreachable no-op for as long as the accumulator has been the commit boundary,
5831
+ * and internal-review deleted it. This fail-closed posture was locked by internal-review
5832
+ * (Decision C) and pinned by internal-review. mpp (Tempo) and x402 credentials are
5833
+ * single-use, final-settlement — no SDK-managed reversal path either.
5834
+ *
5835
+ * **This is a rail-level question, and since internal-review it is no longer the whole
5836
+ * story.** A failed job never debits: the draw is a hold the terminal funnel
5837
+ * releases (`JobManager.resolveCreditDraw`), so the value stays on the caller's
5838
+ * credit. What this flag reports is narrower and still true — no rail hands
5839
+ * value backwards — and reclaiming a released balance runs through the
5840
+ * internal-review `drain` op, which exists only on a DVM that advertises credit.
5841
+ *
5842
+ * `{ refund: true }` survives as a **caller-fault annotation only** — first-party
5843
+ * handlers tag which errors were the caller's doing, and `sdk/testing`'s
5844
+ * `createTestContext` exposes it — and it is not a hook waiting to be re-wired.
5845
+ * internal-review, the accumulator-debit rewrite that would have flipped `cashu` back to
5846
+ * `true`, was cancelled as superseded: the ledger is where reclaim lives now.
5847
+ * x402 refund parity remains a deferred internal-review exploration.
5848
+ */
5849
+ declare const RAIL_REFUNDABLE: Record<PaymentMethod, boolean>;
4126
5850
  /**
4127
5851
  * Cashu receive mode for builder DVMs (internal-review).
4128
5852
  *
@@ -5086,5 +6810,22 @@ interface OutgoingMessage {
5086
6810
  * that calls the member it's missing.
5087
6811
  */
5088
6812
  declare function isStreamableJobStore(store: JobStore): store is StreamableJobStore;
6813
+ /**
6814
+ * The stale-job reaper surface (internal-review) — the two methods a sweeper needs to
6815
+ * find and atomically reap worker-stranded jobs. A strict subset of
6816
+ * `StreamableJobStore`: `MemoryJobStore` and `PostgresJobStore` satisfy it via
6817
+ * the full streamable interface, and the platform's `IsolateJobStore` (a plain
6818
+ * single-machine `JobStore`, not streamable) implements just these two so the
6819
+ * `IsolateJobManager` reaper can sweep the `isolate_jobs` table without taking
6820
+ * on the streaming/LISTEN machinery it doesn't need.
6821
+ */
6822
+ interface StaleJobReapable {
6823
+ /** See {@link StreamableJobStore.findStaleJobs}. */
6824
+ findStaleJobs(processingThresholdMs: number, awaitingThresholdMs: number, limit: number, capabilities: string[] | null): Promise<JobRecord[]>;
6825
+ /** See {@link StreamableJobStore.cancelStaleJob}. */
6826
+ cancelStaleJob(jobId: string, expectedActivityBefore: number, reason: string, terminalStatus: "failed" | "cancelled"): Promise<boolean>;
6827
+ }
6828
+ /** Runtime type guard for StaleJobReapable. */
6829
+ declare function isStaleJobReapable(store: JobStore): store is JobStore & StaleJobReapable;
5089
6830
 
5090
- export { type CreditSnapshot as $, type ApprovalContent as A, type SignedRequestDomain as B, type Currency as C, type DVMConfig as D, SignedRequestError as E, type SignedRequestFailure as F, type SignedRequestReplayStore as G, type SignedRequestSignOpts as H, type IncomingMessage as I, type JobRecord as J, type KVStore as K, type Logger as L, type SignedRequestStatementHeader as M, type SignedRequestVerifier as N, createSignedRequestVerifier as O, type PaymentContent as P, type QuoteConfig as Q, type ResolvedCreditConfig as R, type SDKJobContext as S, isZodSchema as T, UnsupportedCurrencyError as U, signedRequestStatementHeader as V, validateCurrency as W, type CashuMode as X, type FundingMethod as Y, type ZodLike as Z, type CreditLedgerLike as _, type DVMDescriptor as a, type OutgoingMessage as a$, type CreditInvoiceRecord as a0, type CreditDepositEnqueue as a1, type InvoiceSettlement as a2, type MppxServer as a3, type X402Config as a4, type ClientCompatibilityGate as a5, type Message as a6, type FundingReceipt as a7, type TopUpCapUnenforcedReason as a8, type JobReceipt as a9, type X402UnresolvedRefund as aA, type GrownDrawResult as aB, type DrawResolution as aC, type FundingRecord as aD, type BlockedInvoiceCursor as aE, type InvoiceReconciliation as aF, type InvoiceWriteOff as aG, type DrawRecord as aH, type StalePendingDrawCursor as aI, type TempoCreditLossEvidence as aJ, type TempoCreditLoss as aK, type X402CreditLossEvidence as aL, type X402CreditLoss as aM, type DrainMethod as aN, type DrainRequestResult as aO, type BitcoinDepositLiability as aP, type FundingLot as aQ, type CreditDrainRecord as aR, type ChannelDrainCursor as aS, type DrainWriteOff as aT, type DrainReleaseResult as aU, type DrainFulfilment as aV, type DrainTransitionResult as aW, type StreamableJobStore as aX, type ReceiptIssuingStore as aY, type RequestIdClaim as aZ, type RequestIdClaimResult as a_, type AppendOutgoingOptions as aa, StepCache as ab, type X402Receipt as ac, type X402ExactVersionSupport as ad, type X402SettlementIntent as ae, type X402FacilitatorAuth as af, type X402BatchSettlementConfig as ag, PostgresX402ChannelStorage as ah, type PaymentRequirementsV2 as ai, type CreditLedgerQuerier as aj, type X402SettlementCursor as ak, type X402SettlementStatus as al, type X402SettlementWriteOff as am, type X402RefundSettlementGate as an, type MppxCredential as ao, type ReceiptCredit as ap, type DrainReceiptEvent as aq, type DrainReceipt as ar, type CreditDrawReleaseEnqueue as as, type CreditDepositPayload as at, type RevenueSkippedNoRailPayload as au, type MessageType as av, type ClientCompatibility as aw, type CreditDrainEnqueue as ax, type DVMAuthScheme as ay, type DrawResult as az, type ArtifactContent as b, type PaymentCreditDelta as b0, type VerifyAndCreditResult as b1, type JobCounters as b2, CLIENT_COMPATIBILITY_HEADERS as b3, type ClientCompatibilityEnv as b4, type ClientCompatibilityRequirement as b5, type ClientSemVer as b6, type CreditFundingBasis as b7, type CreditInvoiceStatus as b8, CreditLedger as b9, requireClientCompatibility as bA, secp256k1Auth as bB, signedRequestInput as bC, CreditLedgerError as ba, type CreditLedgerErrorCode as bb, type CreditLedgerErrorDetails as bc, type CreditLedgerPool as bd, type CreditStatus as be, DRAIN_DELIVERY_RESERVE_SATS as bf, DVM_PROTOCOL_VERSION as bg, type DrainConflictReason as bh, type DrawRailValue as bi, type DrawStatus as bj, type ReplayStoreBackend as bk, type RevenueSkippedNoRailReason as bl, type Secp256k1AuthOpts as bm, type X402ChannelStorageOpts as bn, type X402RelayLockHolder as bo, type X402RelaySubmissionLock as bp, X402RelaySubmissionLockError as bq, allocateDrawValue as br, clientCompatibilityAttributes as bs, clientCompatibilityMiddleware as bt, clientUpgradeRequired as bu, isStreamableJobStore as bv, parseClientCapabilities as bw, parseClientCompatibility as bx, parseDvmClient as by, parseProtocolVersion as bz, type CancelContent as c, type CanonicalEnvelope as d, type CreateSignedRequestVerifierOpts as e, type CreditConfig as f, type CreditView as g, DEFAULT_CREDIT_MAX as h, DEFAULT_CREDIT_MIN as i, DEFAULT_CREDIT_TTL_SECONDS as j, type DVMRouteContext as k, type InputType as l, InvalidCurrencyError as m, type JobStatus as n, type JobStore as o, type PaymentMethod as p, type PriceValue as q, type ProgressContent as r, type PromptOpts as s, type QuoteContext as t, type QuoteResult as u, type ResponseContent as v, type SDKPaymentRequestOpts as w, SIGNED_REQUEST_AUTH_ID as x, SIGNED_REQUEST_STATEMENT_VERSION as y, type SignedRequestAudience as z };
6831
+ export { type PaymentRequired as $, type ApprovalContent as A, type SignedRequestDomain as B, type Currency as C, type DVMConfig as D, SignedRequestError as E, type SignedRequestFailure as F, type SignedRequestReplayStore as G, type SignedRequestSignOpts as H, type IncomingMessage as I, type JobRecord as J, type KVStore as K, type Logger as L, type SignedRequestStatementHeader as M, type SignedRequestVerifier as N, createSignedRequestVerifier as O, type PaymentContent as P, type QuoteConfig as Q, type ResolvedCreditConfig as R, type SDKJobContext as S, isZodSchema as T, UnsupportedCurrencyError as U, signedRequestStatementHeader as V, validateCurrency as W, type JsonValue as X, type FundingReceipt as Y, type ZodLike as Z, type JobReceipt as _, type DVMDescriptor as a, caip2ToX402Network as a$, type X402Version as a0, type MppxCredential as a1, type Message as a2, type MessageType as a3, type MppxChallenge as a4, X402_BATCH_SETTLEMENT_SCHEME as a5, X402_EXACT_SCHEME as a6, type X402Wallet as a7, type X402Config as a8, type X402ExactVersionSupport as a9, PayoutReporter as aA, PostgresTempoSessionStore as aB, RAIL_REFUNDABLE as aC, type ReceiptCredit as aD, type ReceiptOutcome as aE, type ReceiptPayment as aF, type ResourceInfo as aG, RevenueReporter as aH, SIGNED_ENVELOPE_FIELDS as aI, SIGNED_ENVELOPE_TYPES as aJ, type SettleResponse as aK, type StaleJobReapable as aL, StepCache as aM, type StepRecord as aN, type UnsignedDrainReceipt as aO, type UnsignedFundingReceipt as aP, type UnsignedJobReceipt as aQ, type VerifyResponse as aR, type X402ResponseBody as aS, type X402SelfRelayRpcFailureReason as aT, type X402TrackedChannel as aU, X402_DEFAULT_NETWORK as aV, X402_V1_VERSION as aW, X402_VERSION as aX, _testing as aY, buildPaymentRequiredV2 as aZ, buildPaymentRequirements as a_, type X402Receipt as aa, type TransactionalPayoutHook as ab, type CashuMeltCompleted as ac, type BuildPaymentRequirementsOpts as ad, type CapabilityDescriptor as ae, type CashuMode as af, type CreditDepositPayload as ag, type DrainReceipt as ah, type DrainReceiptEvent as ai, type ExactEvmPayload as aj, type ExactEvmPayloadAuthorization as ak, type FundingLot as al, type FundingMethod as am, type LotDebit as an, type LotDepletion as ao, type MppxServer as ap, NON_CHANNEL_BITCOIN_RAILS as aq, type NonChannelBitcoinRail as ar, type OutgoingMessage as as, type PaymentPayload as at, type PaymentPayloadV1 as au, type PaymentPayloadV2 as av, type PaymentRequiredV2 as aw, type PaymentRequirements as ax, type PaymentRequirementsV1 as ay, type PaymentRequirementsV2 as az, type ArtifactContent as b, type DrainRequestResult as b$, canonicaliseForSigning as b0, canonicalize as b1, chainIdFromCaip2 as b2, computeResultHash as b3, createDefaultReplayStore as b4, decodePayment as b5, decodePaymentRequiredHeader as b6, depleteLots as b7, encodePayment as b8, encodePaymentRequiredHeader as b9, verifyWithFacilitator as bA, wrapMppx as bB, x402NetworkToCaip2 as bC, type CreditDrainEnqueue as bD, type DVMAuthScheme as bE, type CreditLedgerLike as bF, type X402RefundSettlementGate as bG, type CreditSnapshot as bH, type DrawResult as bI, type X402SettlementStatus as bJ, type X402UnresolvedRefund as bK, type GrownDrawResult as bL, type DrawResolution as bM, type FundingRecord as bN, type CreditInvoiceRecord as bO, type InvoiceSettlement as bP, type BlockedInvoiceCursor as bQ, type InvoiceReconciliation as bR, type InvoiceWriteOff as bS, type DrawRecord as bT, type StalePendingDrawCursor as bU, type TempoCreditLossEvidence as bV, type CreditLedgerQuerier as bW, type TempoCreditLoss as bX, type X402CreditLossEvidence as bY, type X402CreditLoss as bZ, type DrainMethod as b_, encodeSettleResponseHeader as ba, exactEvmAuthorization as bb, fifoOrder as bc, inKindDrawMsats as bd, isArtifactMessage as be, isDrainReceipt as bf, isFundingReceipt as bg, isInKindDepletion as bh, isNonChannelBitcoinRail as bi, isPaymentRequestMessage as bj, isSignedJobReceipt as bk, isStaleJobReapable as bl, lotOwedSats as bm, netOwedSats as bn, paymentRequiredV2FromV1 as bo, settleWithFacilitator as bp, signDrainReceipt as bq, signFundingReceipt as br, signReceipt as bs, usdcContractByCaip2 as bt, usdcContractFor as bu, usdcDomainNameFor as bv, usdcDomainVersionFor as bw, verifyDrainReceipt as bx, verifyFundingReceipt as by, verifyReceipt as bz, type CancelContent as c, type X402SettlementReconciliationReason as c$, type BitcoinDepositLiability as c0, type CreditDrainRecord as c1, type ChannelDrainCursor as c2, type DrainWriteOff as c3, type DrainReleaseResult as c4, type DrainFulfilment as c5, type DrainTransitionResult as c6, type StreamableJobStore as c7, type ReceiptIssuingStore as c8, type RequestIdClaim as c9, type RevenueSkippedNoRailPayload as cA, type RevenueSkippedNoRailReason as cB, type Secp256k1AuthOpts as cC, type X402ChannelStorageOpts as cD, type X402RelayLockHolder as cE, type X402RelaySubmissionLock as cF, X402RelaySubmissionLockError as cG, allocateDrawValue as cH, clientCompatibilityAttributes as cI, clientCompatibilityMiddleware as cJ, clientUpgradeRequired as cK, isStreamableJobStore as cL, parseClientCapabilities as cM, parseClientCompatibility as cN, parseDvmClient as cO, parseProtocolVersion as cP, requireClientCompatibility as cQ, secp256k1Auth as cR, signedRequestInput as cS, type CreditDepositEnqueue as cT, type TopUpCapUnenforcedReason as cU, type X402SettlementIntent as cV, type X402SettlementCursor as cW, type X402SettlementWriteOff as cX, type X402FacilitatorAuth as cY, type X402BatchSettlementConfig as cZ, type X402PayoutObserver as c_, type RequestIdClaimResult as ca, type AppendOutgoingOptions as cb, type PaymentCreditDelta as cc, type VerifyAndCreditResult as cd, type JobCounters as ce, CLIENT_COMPATIBILITY_HEADERS as cf, type ClientCompatibility as cg, type ClientCompatibilityEnv as ch, type ClientCompatibilityGate as ci, type ClientCompatibilityRequirement as cj, type ClientSemVer as ck, type CreditFundingBasis as cl, type CreditInvoiceStatus as cm, CreditLedger as cn, CreditLedgerError as co, type CreditLedgerErrorCode as cp, type CreditLedgerErrorDetails as cq, type CreditLedgerPool as cr, type CreditStatus as cs, DRAIN_DELIVERY_RESERVE_SATS as ct, DVM_PROTOCOL_VERSION as cu, type DrainConflictReason as cv, type DrawRailValue as cw, type DrawStatus as cx, PostgresX402ChannelStorage as cy, type ReplayStoreBackend as cz, type CanonicalEnvelope as d, type CreditDrawReleaseEnqueue as d0, type CreateSignedRequestVerifierOpts as e, type CreditConfig as f, type CreditView as g, DEFAULT_CREDIT_MAX as h, DEFAULT_CREDIT_MIN as i, DEFAULT_CREDIT_TTL_SECONDS as j, type DVMRouteContext as k, type InputType as l, InvalidCurrencyError as m, type JobStatus as n, type JobStore as o, type PaymentMethod as p, type PriceValue as q, type ProgressContent as r, type PromptOpts as s, type QuoteContext as t, type QuoteResult as u, type ResponseContent as v, type SDKPaymentRequestOpts as w, SIGNED_REQUEST_AUTH_ID as x, SIGNED_REQUEST_STATEMENT_VERSION as y, type SignedRequestAudience as z };