@dvmkit/sdk 0.1.5-rc.9 → 0.2.1-rc.9

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 (67) hide show
  1. package/README.md +14 -0
  2. package/dist/{chunk-MLRCSJYX.js → chunk-2D75MNNG.js} +178 -550
  3. package/dist/{chunk-BIFLRKMO.js → chunk-2KGG5ORW.js} +1 -1
  4. package/dist/{chunk-27V2ILSR.js → chunk-4A2RAKCW.js} +2 -2
  5. package/dist/{chunk-5URG56JJ.js → chunk-A4CM2BD3.js} +18 -2
  6. package/dist/{chunk-L4OYF4DQ.js → chunk-AJNFXR7J.js} +2 -2
  7. package/dist/{chunk-S3XAHZQY.js → chunk-COYKJRMM.js} +24 -3
  8. package/dist/{chunk-UP2F5RRT.js → chunk-D7YQYLLM.js} +1 -4
  9. package/dist/{chunk-CEOAHV2I.js → chunk-FDKRXOZO.js} +0 -5
  10. package/dist/{chunk-TQWGQCNV.js → chunk-FPOLJRBF.js} +5 -5
  11. package/dist/{chunk-BTZY7VPH.js → chunk-GAIPXGM3.js} +1 -1
  12. package/dist/{chunk-SSSZUVWM.js → chunk-JAHRF5X3.js} +89 -39
  13. package/dist/{chunk-5PBOA25N.js → chunk-JNKQZVZX.js} +472 -617
  14. package/dist/{chunk-U6M3ATSG.js → chunk-MD3H3BF7.js} +40 -6
  15. package/dist/{chunk-CGKZDODG.js → chunk-MG67KXU7.js} +0 -5
  16. package/dist/{chunk-KSQEFVFJ.js → chunk-MMCE7XSR.js} +1 -1
  17. package/dist/{chunk-JZWELPFH.js → chunk-N5B66UOS.js} +4 -16
  18. package/dist/{chunk-BIP6G74V.js → chunk-O6D72KYA.js} +10 -10
  19. package/dist/{chunk-KXCKFIJI.js → chunk-QX52F6IB.js} +1 -1
  20. package/dist/{chunk-RW5LP57K.js → chunk-SDK6KDJN.js} +0 -1
  21. package/dist/{chunk-KVEHHC7W.js → chunk-U5VNEALI.js} +24 -13
  22. package/dist/{chunk-FROTD5XQ.js → chunk-V5ZRATJH.js} +2 -3
  23. package/dist/{chunk-EVBK675R.js → chunk-VA7GT6C6.js} +31 -37
  24. package/dist/{chunk-2UUXIIOC.js → chunk-VIVXB4CD.js} +3 -3
  25. package/dist/{chunk-6BQM7TOW.js → chunk-XMDQOXHO.js} +11 -38
  26. package/dist/{chunk-DMNLFNTW.js → chunk-XRLKGCAW.js} +2 -2
  27. package/dist/{chunk-M7LHFJ5K.js → chunk-XZ7LW4S5.js} +2 -3
  28. package/dist/{chunk-E4EVGPDX.js → chunk-YO56VCTQ.js} +0 -6
  29. package/dist/{credit-ledger-2DFQHNLB.js → credit-ledger-MHA652TV.js} +2 -2
  30. package/dist/{credit-menu-enwMbn55.d.ts → credit-menu-kDA9RVzD.d.ts} +524 -1077
  31. package/dist/{fx-D860pZvP.d.ts → fx-DhyT1Cjh.d.ts} +38 -82
  32. package/dist/index.d.ts +11 -26
  33. package/dist/index.js +2 -2
  34. package/dist/internal/caller.d.ts +644 -2140
  35. package/dist/internal/caller.js +32 -66
  36. package/dist/internal/server.d.ts +40 -90
  37. package/dist/internal/server.js +16 -16
  38. package/dist/{job-store-BUGqvCfL.d.ts → job-store-D3CcZp_g.d.ts} +73 -66
  39. package/dist/{lightning-backend-BozcevPZ.d.ts → lightning-backend-BhsyrBLa.d.ts} +19 -41
  40. package/dist/{memory-credit-ledger-MNUOTQO5.js → memory-credit-ledger-BYBATRN3.js} +3 -3
  41. package/dist/{mpp-secret-state-WNAQQ6K4.js → mpp-secret-state-B5GCILVH.js} +1 -1
  42. package/dist/{mpp-setup-4FJD6ZHV.js → mpp-setup-MPP3ST5X.js} +1 -1
  43. package/dist/{payout-reporter-RG6XNGPI.js → payout-reporter-KEMUXEUO.js} +2 -2
  44. package/dist/{postgres-consumed-credential-store-VHBT4KEA.js → postgres-consumed-credential-store-6N2V5MJN.js} +2 -2
  45. package/dist/postgres-job-store-MZZZMS2B.js +7 -0
  46. package/dist/postgres-kv-store-DXGTKXLU.js +7 -0
  47. package/dist/{postgres-replay-store-UJXRT6VO.js → postgres-replay-store-ZF4JNZOW.js} +2 -2
  48. package/dist/{pricing-4CEB34RM.js → pricing-MU5GNUJZ.js} +1 -1
  49. package/dist/{processed-payment-store-HAA4SFNK.js → processed-payment-store-GKMAWOM3.js} +2 -2
  50. package/dist/revenue-reporter-O75BGYFK.js +7 -0
  51. package/dist/server/index.d.ts +47 -159
  52. package/dist/server/index.js +67 -138
  53. package/dist/{ssrf-DbFkpDv0.d.ts → ssrf-dMooihtY.d.ts} +1 -2
  54. package/dist/{step-cache-5dljDqrQ.d.ts → step-cache-v2yhyhSZ.d.ts} +617 -1532
  55. package/dist/{tempo-charge-store-RIFTALZK.js → tempo-charge-store-SDTUNTKA.js} +2 -2
  56. package/dist/{tempo-lifecycle-DFIXQ54Q.js → tempo-lifecycle-G5SB4HFC.js} +5 -5
  57. package/dist/{tempo-session-store-2JNOKJGX.js → tempo-session-store-WF3VZXNF.js} +2 -2
  58. package/dist/{tempo-wallet-4QKSV65O.js → tempo-wallet-ZFNUDFVO.js} +2 -2
  59. package/dist/testing/index.d.ts +5 -15
  60. package/dist/testing/index.js +4 -11
  61. package/dist/{usd-DoRuAckA.d.ts → usd-Cu9G6xv2.d.ts} +14 -16
  62. package/dist/{wallet-CJC8lwxx.d.ts → wallet-Dwjs5n_M.d.ts} +1 -1
  63. package/dist/{x402-5H27DCBE.js → x402-CI6PSHNO.js} +5 -5
  64. package/package.json +3 -2
  65. package/dist/postgres-job-store-3RAXMNSY.js +0 -7
  66. package/dist/postgres-kv-store-JFBDP5IP.js +0 -7
  67. package/dist/revenue-reporter-ASZ7SHHH.js +0 -7
@@ -25,11 +25,8 @@ declare class InvalidCurrencyError extends Error {
25
25
  constructor(raw: unknown);
26
26
  }
27
27
  /**
28
- * Thrown by the SDK's mid-job fiat-form `requestPayment` when the operator
29
- * passes a currency the runtime can't live-convert to sats. v1 supports only
30
- * "usd" (the bundled fx fetcher is USD/BTC); EUR/GBP/JPY land when a
31
- * multi-currency fx fetcher does. The message names the supported set so
32
- * operators see exactly what's missing.
28
+ * A valid currency code that the configured FX snapshot cannot price.
29
+ * The error message names the supported set so the builder can correct it.
33
30
  */
34
31
  declare class UnsupportedCurrencyError extends Error {
35
32
  readonly raw: string;
@@ -128,7 +125,7 @@ interface X402Config {
128
125
  * When true (default), the SDK includes x402 `PaymentRequirements` on the
129
126
  * 402 response body alongside MPP's `WWW-Authenticate: Payment` header so
130
127
  * x402-speaking agents (Coinbase reference client, thirdweb-ecosystem)
131
- * can pay. Flip to `false` for MPP-only DVMs (internal-review).
128
+ * can pay. Flip to `false` for MPP-only DVMs.
132
129
  */
133
130
  dualServe?: boolean;
134
131
  /** Human-readable description carried on the `PaymentRequirements`. */
@@ -156,7 +153,7 @@ interface X402Receipt {
156
153
  verified: boolean;
157
154
  amountMsats: number;
158
155
  /**
159
- * Native USDC amount in 6-decimal microunits (internal-review). Captured from the
156
+ * Native USDC amount in 6-decimal microunits. Captured from the
160
157
  * payment payload's `value` field so the platform revenue ledger records
161
158
  * the rail-native value without re-deriving it from `amountMsats` + a
162
159
  * fresh BTC rate (lossy and racy across the boundary).
@@ -166,27 +163,14 @@ interface X402Receipt {
166
163
  txHash?: string;
167
164
  /**
168
165
  * Base64-encoded `SettleResponse` for the `X-PAYMENT-RESPONSE` header the
169
- * server attaches to the 2xx response per x402 spec (internal-review).
166
+ * server attaches to the 2xx response per x402 spec.
170
167
  */
171
168
  settleResponseHeader?: string;
172
169
  /** When `verified === false`, a short stable code surfacing the reason. */
173
170
  invalidReason?: string;
174
171
  }
175
172
 
176
- /**
177
- * dvmkit's x402 wire-format implementation — the v1/v2 `PaymentRequirements` /
178
- * `PaymentPayload` envelope shapes, payment-header encode/decode, and the
179
- * facilitator verify/settle calls that a deployed DVM's 402 response and
180
- * payment parsing are built from. A wire-format change lands here.
181
- *
182
- * **On a change here, or an x402 reference-client upgrade, run the conformance
183
- * check** — `public compatibility guide`. Our own SDK round-trips
184
- * against itself (the vitest tiers, `money-loop:x402`), so only a client
185
- * that isn't dvmkit can catch a *mutual* misreading of the spec. The runbook
186
- * names the out-of-tree v1 and v2 clients plus the funded Base Sepolia key
187
- * (`public compatibility guide`); the runbook carries a run log of
188
- * what it last ran against.
189
- */
173
+ /** Wire compatibility requires independent v1 and v2 reference-client checks. */
190
174
 
191
175
  /** Current x402 protocol version preferred by dvmkit clients and servers. */
192
176
  declare const X402_VERSION = 2;
@@ -197,13 +181,13 @@ type X402Version = typeof X402_V1_VERSION | typeof X402_VERSION;
197
181
  /**
198
182
  * Chain a caller's x402 wallet reads when it was connected without an explicit
199
183
  * `--network`. Single source of truth for the default: the wallet loader, the
200
- * connect output and the asset resolver all read it from here (internal-review).
184
+ * connect output and the asset resolver all read it from here.
201
185
  */
202
186
  declare const X402_DEFAULT_NETWORK = "eip155:8453";
203
187
  /**
204
188
  * Scheme name of the per-call rail — one signed authorization settling one
205
189
  * resource. Shared with the credit funding menu's `x402.schemes` block, which
206
- * has to name the same string the requirement carries (internal-review).
190
+ * has to name the same string the requirement carries.
207
191
  */
208
192
  declare const X402_EXACT_SCHEME = "exact";
209
193
  /**
@@ -212,7 +196,7 @@ declare const X402_EXACT_SCHEME = "exact";
212
196
  * the caller's requirement selection, and the credit menu's `x402.schemes`
213
197
  * block all read it from here — the menu advertises exactly what the challenge
214
198
  * will carry, so a rename cannot leave one of them advertising a flavour the
215
- * others no longer speak (internal-review).
199
+ * others no longer speak.
216
200
  */
217
201
  declare const X402_BATCH_SETTLEMENT_SCHEME = "batch-settlement";
218
202
 
@@ -344,11 +328,8 @@ interface BuildPaymentRequirementsOpts {
344
328
  version?: X402Version;
345
329
  }
346
330
  /**
347
- * Build a single x402 `PaymentRequirements` entry from the DVM's `X402Config`,
348
- * the resolved per-call USDC amount, and the live request's resource URL.
349
- * The resource string is bound into the EIP-3009 `extra` field on signing in
350
- * the reference clients, so it must match exactly between the 402 response
351
- * and the retry payload — pass the request URL the client will retry against.
331
+ * V1 carries the resource URL in each requirement; v2 carries it in the outer
332
+ * response. This metadata is not part of the EIP-3009 authorization signature.
352
333
  */
353
334
  declare function buildPaymentRequirements(opts: BuildPaymentRequirementsOpts & {
354
335
  version: typeof X402_V1_VERSION;
@@ -364,13 +345,7 @@ declare function buildPaymentRequiredV2(opts: Omit<BuildPaymentRequirementsOpts,
364
345
  }): PaymentRequiredV2;
365
346
  /** Convert the server's legacy body requirements into the parallel v2 declaration. */
366
347
  declare function paymentRequiredV2FromV1(requirements: PaymentRequirementsV1[], error?: string): PaymentRequiredV2;
367
- /**
368
- * Translate a CAIP-2 chain id (`eip155:8453`) to the x402-spec network slug
369
- * (`base`). Operators configure DVMs in CAIP-2 because it's the lingua-franca
370
- * across our other rails; the x402 facilitator API only accepts named slugs.
371
- * Unknown CAIP-2 ids fall through to the original string so non-EVM rails
372
- * (Solana mainnet, etc.) can also be plumbed once supported.
373
- */
348
+ /** Legacy v1 network slugs; unknown identifiers pass through unchanged. */
374
349
  declare function caip2ToX402Network(network: string): string;
375
350
  /** Translate a legacy x402 network slug back to its CAIP-2 chain id. */
376
351
  declare function x402NetworkToCaip2(network: string): string;
@@ -378,12 +353,6 @@ declare function x402NetworkToCaip2(network: string): string;
378
353
  declare function chainIdFromCaip2(network: string): number;
379
354
  /** USDC contract address for a network slug (post-translation). */
380
355
  declare function usdcContractFor(networkSlug: string): `0x${string}` | undefined;
381
- /**
382
- * USDC contract address for a CAIP-2 chain id (e.g. `eip155:8453`). Resolves
383
- * via {@link caip2ToX402Network} so the one network table is the source of
384
- * truth — keeps callers that hold the CAIP-2 form (CLI wallet) in lockstep
385
- * with the SDK 402-builder which already speaks x402 slugs.
386
- */
387
356
  declare function usdcContractByCaip2(caip2: string): `0x${string}` | undefined;
388
357
  /** USDC EIP-712 domain `name` for a network slug. */
389
358
  declare function usdcDomainNameFor(networkSlug: string): string | undefined;
@@ -410,29 +379,24 @@ declare function decodePaymentRequiredHeader(header: string): PaymentRequiredV2;
410
379
  declare function exactEvmAuthorization(payload: PaymentPayload): ExactEvmPayloadAuthorization;
411
380
  /**
412
381
  * Encode a `SettleResponse` for the `X-PAYMENT-RESPONSE` header that the
413
- * server emits on a 2xx after a successful settlement (internal-review, per the
382
+ * server emits on a 2xx after a successful settlement (per the
414
383
  * x402 spec).
415
384
  */
416
385
  declare function encodeSettleResponseHeader(response: SettleResponse): string;
417
386
 
418
- /** Identifies which side of the conversation sent a message. */
419
387
  type MessageFrom = "requester" | "provider";
420
388
  /** Single mppx-shaped MPP challenge carried in `payment-request` messages. */
421
389
  type MppxChallenge = Challenge.Challenge;
422
390
  /** Structured MPP credential (`{ challenge, payload, source? }`) carried in `payment` messages. */
423
391
  type MppxCredential = Credential.Credential;
424
- /** All supported message types in the conversation protocol. */
425
392
  type MessageType = "text" | "prompt" | "response" | "artifact" | "payment-request" | "payment" | "approval" | "cancel" | "complete" | "working" | "progress";
426
- /** Common fields shared by all message variants. */
427
393
  interface MessageBase {
428
394
  /** Monotonic sequence number, assigned by provider/server. */
429
395
  seq: number;
430
- /** Which side sent this message. */
431
396
  from: MessageFrom;
432
397
  /** Unix timestamp (seconds). */
433
398
  timestamp: number;
434
399
  }
435
- /** Free-form text message content. */
436
400
  interface TextContent {
437
401
  text: string;
438
402
  }
@@ -453,15 +417,12 @@ interface ResponseContent {
453
417
  }
454
418
  /** Job output — inline data or a URL reference. */
455
419
  interface ArtifactContent {
456
- /** Human-readable name for the artifact. */
457
420
  name?: string;
458
- /** MIME type of the artifact. */
459
421
  mime_type?: string;
460
422
  /** Inline data (string or base64 for binary). */
461
423
  data?: string;
462
424
  /** Encoding of inline data: "utf-8" for text, "base64" for binary. */
463
425
  encoding?: "utf-8" | "base64";
464
- /** URL to fetch the artifact from. */
465
426
  url?: string;
466
427
  /** Size in bytes (for URL-referenced artifacts). */
467
428
  size_bytes?: number;
@@ -487,7 +448,7 @@ interface PaymentRequestContent {
487
448
  mints?: string[];
488
449
  min_locktime_seconds?: number;
489
450
  /**
490
- * NUT-11 P2PK lock pubkey the requester should lock cashu outputs to (internal-review).
451
+ * NUT-11 P2PK lock pubkey the requester should lock cashu outputs to.
491
452
  * When absent, the requester falls back to `/v1/info`'s `cashu.lock_pubkeys[0]`.
492
453
  */
493
454
  lock_pubkey?: string;
@@ -506,7 +467,7 @@ interface PaymentRequestContent {
506
467
  * caller MUST use when signing `TransferWithAuthorization` for this
507
468
  * payment-request. Binds the credential to this job so a payload signed
508
469
  * for job A cannot satisfy job B within the pre-settlement window
509
- * (internal-review). Optional on the wire for backwards-compat — pre-fix DVMs
470
+ * Optional on the wire for backwards-compat — pre-fix DVMs
510
471
  * never set it; verify-side gate keys on `pendingX402Nonce !== undefined`.
511
472
  */
512
473
  nonce?: string;
@@ -527,7 +488,7 @@ interface PaymentContent {
527
488
  mint?: string;
528
489
  amount_msats?: number;
529
490
  /**
530
- * UUIDv4 per-call identifier for the agent-wallet path (internal-review).
491
+ * UUIDv4 per-call identifier for the agent-wallet path.
531
492
  * Mid-job equivalent of the upfront flow's `X-Cashu-Request-Id` header —
532
493
  * threaded into the SDK accumulator's `(dvm_id, request_id)` replay key.
533
494
  */
@@ -545,7 +506,7 @@ interface PaymentContent {
545
506
  */
546
507
  tempo_credential?: MppxCredential;
547
508
  /**
548
- * Dev-only: satisfy the ask with no proof at all (internal-review). The `/_dev`
509
+ * Dev-only: satisfy the ask with no proof at all. The `/_dev`
549
510
  * console has no wallet, so its auto-approve button sends this instead of a
550
511
  * rail credential. Admitted only by a `devMode` server, and only on a
551
512
  * message that carries none of the proofs above — money on the wire always
@@ -586,7 +547,6 @@ interface ProgressContent {
586
547
  /** Optional human-readable hint about what's happening right now. */
587
548
  hint?: string;
588
549
  }
589
- /** A single message in a job conversation — discriminated union on `type`. */
590
550
  type Message = (MessageBase & {
591
551
  type: "text";
592
552
  content: TextContent;
@@ -621,37 +581,30 @@ type Message = (MessageBase & {
621
581
  type: "progress";
622
582
  content: ProgressContent;
623
583
  });
624
- /** Type guard: message is a prompt from the provider. */
625
584
  declare function isPromptMessage(msg: Message): msg is MessageBase & {
626
585
  type: "prompt";
627
586
  content: PromptContent;
628
587
  };
629
- /** Type guard: message contains an artifact. */
630
588
  declare function isArtifactMessage(msg: Message): msg is MessageBase & {
631
589
  type: "artifact";
632
590
  content: ArtifactContent;
633
591
  };
634
- /** Type guard: message is a payment request. */
635
592
  declare function isPaymentRequestMessage(msg: Message): msg is MessageBase & {
636
593
  type: "payment-request";
637
594
  content: PaymentRequestContent;
638
595
  };
639
- /** Type guard: message is a text message. */
640
596
  declare function isTextMessage(msg: Message): msg is MessageBase & {
641
597
  type: "text";
642
598
  content: TextContent;
643
599
  };
644
- /** Type guard: message signals completion. */
645
600
  declare function isCompleteMessage(msg: Message): msg is MessageBase & {
646
601
  type: "complete";
647
602
  content: CompleteContent;
648
603
  };
649
- /** Type guard: message is a cancellation. */
650
604
  declare function isCancelMessage(msg: Message): msg is MessageBase & {
651
605
  type: "cancel";
652
606
  content: CancelContent;
653
607
  };
654
- /** Type guard: message is a working signal. */
655
608
  declare function isWorkingMessage(msg: Message): msg is MessageBase & {
656
609
  type: "working";
657
610
  content: WorkingContent;
@@ -697,7 +650,6 @@ declare const CLIENT_COMPATIBILITY_HEADERS: {
697
650
  readonly protocolVersion: "DVM-Protocol-Version";
698
651
  readonly capabilities: "DVM-Client-Capabilities";
699
652
  };
700
- /** A parsed SemVer value carried by `DVM-Client`. */
701
653
  interface ClientSemVer {
702
654
  readonly version: string;
703
655
  readonly major: number;
@@ -705,7 +657,6 @@ interface ClientSemVer {
705
657
  readonly patch: number;
706
658
  readonly prerelease: readonly (string | number)[];
707
659
  }
708
- /** Validated compatibility metadata from one HTTP request. */
709
660
  interface ClientCompatibility {
710
661
  readonly clientVersion?: ClientSemVer;
711
662
  readonly protocolVersion?: number;
@@ -721,16 +672,13 @@ interface ClientCompatibility {
721
672
  readonly capabilities: boolean;
722
673
  };
723
674
  }
724
- /** Compatibility requirement attached to one endpoint. */
725
675
  interface ClientCompatibilityRequirement {
726
676
  /** Additive capability tokens the caller must advertise. */
727
677
  readonly requiredCapabilities?: readonly string[];
728
678
  /** Lowest `dvm` SemVer accepted by this endpoint. */
729
679
  readonly minimumClientVersion?: string;
730
680
  }
731
- /** Factory for compatibility middleware attached to one custom route. */
732
681
  type ClientCompatibilityGate = (requirement: ClientCompatibilityRequirement) => MiddlewareHandler<ClientCompatibilityEnv>;
733
- /** Hono variables installed by the SDK compatibility middleware. */
734
682
  interface ClientCompatibilityEnv {
735
683
  Variables: {
736
684
  clientCompatibility?: ClientCompatibility;
@@ -748,62 +696,35 @@ declare function parseClientCapabilities(value: string): readonly string[] | und
748
696
  declare function clientCompatibilityAttributes(compatibility: ClientCompatibility): Record<string, unknown>;
749
697
  /** Parse request metadata and advertise the server protocol version on every response. */
750
698
  declare function clientCompatibilityMiddleware(): MiddlewareHandler<ClientCompatibilityEnv>;
751
- /** Build pre-handler enforcement middleware for one endpoint declaration. */
752
699
  declare const requireClientCompatibility: ClientCompatibilityGate;
753
- /** Render the normal structured HTTP 426 compatibility error. */
754
700
  declare function clientUpgradeRequired(c: Context, compatibility: ClientCompatibility, requirement: ClientCompatibilityRequirement): Response;
755
701
 
756
- /**
757
- * Canonical JSON serialisation used for secp256k1+BIP-340 Schnorr request
758
- * signing in cast (internal-review).
759
- *
760
- * Single source of truth for both the cast DVM's verifier (internal-review) and the
761
- * client signing helper (internal-review). The same function on both sides guarantees
762
- * that what the client signs is byte-identical to what the server hashes.
763
- *
764
- * Algorithm: recursively sort object keys lexicographically (UTF-16 code-unit
765
- * order — JavaScript's default), then JSON.stringify with no whitespace.
766
- * Arrays preserve order. Numbers, strings, booleans, null pass through as
767
- * `JSON.stringify` formats them. Values of `undefined` or functions are
768
- * forbidden (we throw rather than silently drop, since both sides must agree).
769
- */
770
- /** Any JSON-serialisable value accepted by `canonicalize`. */
771
702
  type JsonValue = string | number | boolean | null | JsonValue[] | {
772
703
  [key: string]: JsonValue;
773
704
  };
774
705
  /**
775
- * Serialise `value` to its canonical JSON form (sorted keys, no whitespace).
776
- *
777
- * `undefined` properties on objects are silently omitted (mirroring
778
- * `JSON.stringify`), so callers don't need to explicitly delete optional fields
779
- * that weren't set. Throws `TypeError` for unsupported scalar types (functions,
780
- * symbols, non-finite numbers). The caller is responsible for stripping fields
781
- * that shouldn't appear in the signed payload (e.g. `signature` itself).
706
+ * Object keys sort recursively in UTF-16 code-unit order; arrays preserve order.
707
+ * Undefined object properties are omitted. Unsupported scalars, including
708
+ * non-finite numbers, throw TypeError. Callers strip unsigned fields themselves.
782
709
  */
783
710
  declare function canonicalize(value: JsonValue): string;
784
711
  /**
785
- * Canonicalise `value` and return the UTF-8 bytes the signer should produce a
786
- * signature over. Both the cast client signer (`signAddEpisode`) and the
787
- * server verifier (`verifyCanonicalSignedRequest`) call this — sharing the function
788
- * guarantees byte-identical input on both sides.
712
+ * UTF-8 encoding of the canonical JSON form.
789
713
  */
790
714
  declare function canonicaliseForSigning(value: JsonValue): Uint8Array;
791
715
 
792
- /** Terminal outcome a receipt attests. Mirrors the SDK's terminal job statuses. */
793
716
  type ReceiptOutcome = "completed" | "failed" | "cancelled";
794
- /** What the caller paid for the job, as persisted on the job record. */
795
717
  interface ReceiptPayment {
796
718
  /** Total credited millisats. `0` on free jobs — they still get receipts. */
797
719
  msats: number;
798
720
  /**
799
721
  * Rail of the most recent successful credit; `null` when nothing was paid.
800
722
  * `lightning` appears on a draw against a credit the caller funded by
801
- * invoice (internal-review) — it is a funding rail, never an attached-proof one.
723
+ * invoice — it is a funding rail, never an attached-proof one.
802
724
  */
803
725
  rail: "cashu" | "x402" | "tempo" | "lightning" | null;
804
726
  /** Rail-native upfront amount (sats / USDC microunits), `null` when absent. */
805
727
  native_amount: number | null;
806
- /** Native asset tag paired with `native_amount`. */
807
728
  native_asset: string | null;
808
729
  /** Settlement reference: EVM tx hash (x402), mppx challenge id, or the Cashu request id. */
809
730
  tx_hash: string | null;
@@ -811,8 +732,8 @@ interface ReceiptPayment {
811
732
  mint: string | null;
812
733
  }
813
734
  /**
814
- * Credit-draw countersignature block (internal-review / internal-review; emitted since
815
- * internal-review). Present on every job whose payment funded/drew the credit
735
+ * Credit-draw countersignature block. Present on every job whose payment funded
736
+ * or drew the credit
816
737
  * ledger — including implicit N=1 per-job payments, which fund and draw a
817
738
  * single-use credit in the same request. A caller holding a DVM-signed
818
739
  * balance trajectory plus its own caller-signed draw intents can prove
@@ -820,23 +741,21 @@ interface ReceiptPayment {
820
741
  * because the countersignature rides the receipt.
821
742
  *
822
743
  * Absence is signature-compatible: `canonicalize` omits absent keys, so a
823
- * ledger-less (or free) job's receipt signs identically to a pre-internal-review
744
+ * ledger-less (or free) job's receipt signs identically to a legacy
824
745
  * receipt.
825
746
  */
826
747
  interface ReceiptCredit {
827
- /** Credit the draw was taken against. */
828
748
  credit_id: string;
829
749
  /** This draw's identifier — matches the caller-signed draw intent. */
830
750
  draw_id: string;
831
751
  /** Amount drawn, in the credit's denomination. */
832
752
  amount: number;
833
- /** Credit balance after this draw. */
834
753
  balance_after: number;
835
754
  /** Per-credit monotonic draw counter. */
836
755
  ledger_seq: number;
837
756
  }
838
757
  /**
839
- * A DVM-signed proof of what a job did and what it cost (internal-review).
758
+ * A DVM-signed proof of what a job did and what it cost.
840
759
  *
841
760
  * Signed once at the terminal transition by the per-DVM receipt key, whose
842
761
  * authority chains to the builder identity key through the deploy-time
@@ -845,18 +764,22 @@ interface ReceiptCredit {
845
764
  * and on every later re-read — the bytes are persisted, not recomputed.
846
765
  */
847
766
  interface JobReceipt {
848
- /** Receipt schema version. Currently always `1`. */
849
767
  v: 1;
850
- /** The job this receipt attests. */
851
768
  job_id: string;
852
769
  /** Immutable DVM identity bound into the builder attestation. */
853
770
  dvm_id: string;
854
771
  /** DVM slug, as bound in the builder attestation. */
855
772
  dvm: string;
856
- /** Capability the job dispatched to. */
857
773
  capability: string;
858
- /** Terminal outcome. */
859
774
  outcome: ReceiptOutcome;
775
+ /** Signed Unix millisecond lifecycle times; absent on legacy receipts. */
776
+ timing?: {
777
+ accepted_at_ms: number;
778
+ terminal_at_ms: number;
779
+ caller_wait_ms: number;
780
+ };
781
+ /** SDK-derived attribution on failure/cancellation; absent on completion and legacy receipts. */
782
+ ended_by?: "caller" | "provider";
860
783
  /** Terminal reason on `failed`/`cancelled` (e.g. `worker_died_mid_job`); `null` on success. */
861
784
  reason: string | null;
862
785
  /** Per-DVM monotonic terminal counter. Gaps expose receipt suppression. */
@@ -865,7 +788,6 @@ interface JobReceipt {
865
788
  issued_at: number;
866
789
  /** Caller's x-only pubkey when signed-request auth was used; `null` otherwise. */
867
790
  requester_pubkey: string | null;
868
- /** What the caller paid. */
869
791
  paid: ReceiptPayment;
870
792
  /**
871
793
  * {@link computeResultHash} over the delivered result; `null` when nothing
@@ -878,7 +800,7 @@ interface JobReceipt {
878
800
  /** BIP-340 Schnorr signature over the canonical receipt minus `signature` (128 hex chars). */
879
801
  signature: string;
880
802
  /**
881
- * Credit-draw countersignature (internal-review) — present when the job's payment
803
+ * Credit-draw countersignature — present when the job's payment
882
804
  * funded/drew the credit ledger. See {@link ReceiptCredit}. `balance_after`
883
805
  * reflects the terminal resolution: the recorded draw trajectory on a
884
806
  * settled (completed) job, the restored available balance on a released
@@ -886,12 +808,10 @@ interface JobReceipt {
886
808
  */
887
809
  credit?: ReceiptCredit;
888
810
  }
889
- /** A receipt before signing — every field but the signature itself. */
890
811
  type UnsignedJobReceipt = Omit<JobReceipt, "signature">;
891
- /** Reclaim lifecycle event a {@link DrainReceipt} attests (internal-review, spec §5). */
892
812
  type DrainReceiptEvent = "requested" | "parked" | "picked_up" | "sent" | "released";
893
813
  /**
894
- * A DVM-signed proof of one reclaim event (internal-review). One receipt per
814
+ * A DVM-signed proof of one reclaim event. One receipt per
895
815
  * lifecycle transition — `requested` (the balance debited into the drain
896
816
  * liability), `parked` (Cashu notes locked to the caller's refund key,
897
817
  * parked for pickup), `picked_up` (the caller collected them), `sent` (a
@@ -907,24 +827,20 @@ type DrainReceiptEvent = "requested" | "parked" | "picked_up" | "sent" | "releas
907
827
  * same canonicalisation, so one attestation chain covers both.
908
828
  */
909
829
  interface DrainReceipt {
910
- /** Receipt schema version. Currently always `1`. */
911
830
  v: 1;
912
831
  /** Discriminator separating this from a `JobReceipt` in mixed stores. */
913
832
  kind: "credit_drain";
914
- /** Credit the drain was taken against. */
915
833
  credit_id: string;
916
834
  /** This drain's identifier — matches the caller-signed drain request. */
917
835
  drain_id: string;
918
- /** The lifecycle event this receipt attests. */
919
836
  event: DrainReceiptEvent;
920
837
  /** Payout rail: `cashu` | `x402` | `tempo`. */
921
838
  method: string;
922
839
  /** Amount reclaimed, micro-units of the credit's currency. */
923
840
  amount_micro: number;
924
841
  /**
925
- * Credit balance immediately after the drain debit (micro-units) — the
926
- * pending job holds at request time, fixed when the drain was recorded.
927
- * Every event's receipt attests this same figure.
842
+ * Credit balance left after the drain debit, in micro-units. It includes
843
+ * pending job holds at request time and is fixed across this drain's event receipts.
928
844
  */
929
845
  balance_after_micro: number;
930
846
  /** Per-credit monotonic sequence the drain consumed, shared with draws. */
@@ -942,19 +858,16 @@ interface DrainReceipt {
942
858
  /** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
943
859
  signature: string;
944
860
  }
945
- /** A drain receipt before signing — every field but the signature itself. */
946
861
  type UnsignedDrainReceipt = Omit<DrainReceipt, "signature">;
947
862
  /**
948
- * A DVM-signed proof that one payment funded a prepaid credit (internal-review).
863
+ * A DVM-signed proof that one payment funded a prepaid credit.
949
864
  * The balance and sequence are the values fixed when the funding committed;
950
865
  * retries return this original artifact even when the credit moved later.
951
866
  */
952
867
  interface FundingReceipt {
953
- /** Receipt schema version. Currently always `1`. */
954
868
  v: 1;
955
869
  /** Discriminator separating funding evidence from job and drain receipts. */
956
870
  kind: "credit_funding";
957
- /** Credit that received the value. */
958
871
  credit_id: string;
959
872
  /** Caller fund id, or the attached rail payment id for fund-and-draw. */
960
873
  fund_id: string;
@@ -972,12 +885,10 @@ interface FundingReceipt {
972
885
  dvm_id: string;
973
886
  /** DVM slug, as bound in the builder attestation. */
974
887
  dvm: string;
975
- /** x-only secp256k1 pubkey that signed this receipt. */
976
888
  receipt_pubkey: string;
977
889
  /** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
978
890
  signature: string;
979
891
  }
980
- /** A funding receipt before signing. */
981
892
  type UnsignedFundingReceipt = Omit<FundingReceipt, "signature">;
982
893
  /**
983
894
  * Hash the delivered result so a receipt binds to *what* was returned, not
@@ -997,13 +908,6 @@ type UnsignedFundingReceipt = Omit<FundingReceipt, "signature">;
997
908
  * issuer does the same (`resultHashFor` in `sdk/server/receipt-issuer.ts`).
998
909
  */
999
910
  declare function computeResultHash(summary: string | undefined, artifactContents: JsonValue[]): string;
1000
- /**
1001
- * Sign an unsigned receipt with the DVM's receipt secret, returning the
1002
- * complete receipt. Signs `canonicaliseForSigning(receipt)` — the same
1003
- * sorted-key canonical JSON the caller-auth envelope and the builder
1004
- * attestation use, so {@link verifyReceipt} is symmetric with
1005
- * `verifyCanonicalSignedRequest`. There is no second canonicalisation.
1006
- */
1007
911
  declare function signReceipt(unsigned: UnsignedJobReceipt, secretHex: string): JobReceipt;
1008
912
  /**
1009
913
  * Verify a receipt's self-consistency: the BIP-340 signature over the
@@ -1042,9 +946,7 @@ declare function isSignedJobReceipt(value: unknown): value is JobReceipt;
1042
946
  declare function signDrainReceipt(unsigned: UnsignedDrainReceipt, secretHex: string): DrainReceipt;
1043
947
  /** Sign an unsigned funding receipt using the common receipt canonicalisation. */
1044
948
  declare function signFundingReceipt(unsigned: UnsignedFundingReceipt, secretHex: string): FundingReceipt;
1045
- /** Structural guard for a funding receipt received over the wire or from disk. */
1046
949
  declare function isFundingReceipt(value: unknown): value is FundingReceipt;
1047
- /** Verify a funding receipt's inner BIP-340 signature. */
1048
950
  declare function verifyFundingReceipt(receipt: FundingReceipt): boolean;
1049
951
  /**
1050
952
  * Structural guard on a drain receipt from outside this process — off a
@@ -1061,33 +963,16 @@ declare function verifyFundingReceipt(receipt: FundingReceipt): boolean;
1061
963
  declare function isDrainReceipt(value: unknown): value is DrainReceipt;
1062
964
  /**
1063
965
  * Verify a drain receipt's self-consistency, mirroring {@link verifyReceipt}
1064
- * — the same provenance caveat applies: check `receipt_pubkey` against the
966
+ * the same provenance caveat applies: check `receipt_pubkey` against the
1065
967
  * DVM's attested `receipt_pubkey` for the outer link.
1066
968
  */
1067
969
  declare function verifyDrainReceipt(receipt: DrainReceipt): boolean;
1068
970
 
1069
971
  /**
1070
- * Funding lots: the in-kind basis of a non-channel Bitcoin credit (internal-review).
1071
- *
1072
- * A reusable Bitcoin credit is a deposit, and internal-review decided the provider
1073
- * owes back the **asset it received** rather than a dollar figure re-priced at
1074
- * reclaim time: fund 1,151 sats, draw 1.2% of the credit's value, reclaim
1075
- * 98.8% of 1,151 sats. That obligation is only expressible if each funding
1076
- * event's `{sats_funded, credited_micro}` pair survives — the ledger's pooled
1077
- * `credits.msats_remaining` blends every funding into one ratio, which answers
1078
- * a different question (what share of the credit's rail value does *this draw*
1079
- * consume) and cannot say what a top-up taken at a different rate is worth.
1080
- *
1081
- * Everything here is pure integer arithmetic over that pair, shared by the
1082
- * Postgres and in-memory ledgers so the two cannot drift — the arrangement
1083
- * `allocateDrawValue` already holds for the pooled basis.
1084
- *
1085
- * Since internal-review these lots are also what a *settling* draw is priced at on
1086
- * these rails, not only a reclaim: `inKindDrawMsats` turns the depletion a
1087
- * settle already performs into the draw's booked rail value, so one credit has
1088
- * one answer to "what were these sats" and the pooled remainder stops being a
1089
- * second authority: settled draw sats plus reclaim sats must equal the sats
1090
- * that credit's funding lots received.
972
+ * Cashu and Lightning credits owe unused sats back at each deposit's funding
973
+ * rate. Keep the sats/micro basis per funding lot: a pooled ratio loses the
974
+ * basis of top-ups made at different rates. Settled draws and reclaims share
975
+ * these lots so their total equals the sats funded.
1091
976
  */
1092
977
  /**
1093
978
  * The rails whose credits carry a Bitcoin deposit this ledger owes back in
@@ -1100,9 +985,7 @@ declare function verifyDrainReceipt(receipt: DrainReceipt): boolean;
1100
985
  * {@link FundingMethod} must be a decision here rather than a default.
1101
986
  */
1102
987
  declare const NON_CHANNEL_BITCOIN_RAILS: readonly ["cashu", "lightning"];
1103
- /** A rail whose unused credit is a sats deposit the provider owes back. */
1104
988
  type NonChannelBitcoinRail = (typeof NON_CHANNEL_BITCOIN_RAILS)[number];
1105
- /** True when `rail` funds a credit with sats the provider holds and owes back. */
1106
989
  declare function isNonChannelBitcoinRail(rail: string | null | undefined): rail is NonChannelBitcoinRail;
1107
990
  /**
1108
991
  * One funding event's deposit: the sats that arrived and the credit micro they
@@ -1110,7 +993,7 @@ declare function isNonChannelBitcoinRail(rail: string | null | undefined): rail
1110
993
  *
1111
994
  * `remainingMicro` is depleted FIFO by settled draws and by reclaims, so the
1112
995
  * sum of a credit's lots tracks `credits.balance_micro` exactly — with one
1113
- * deliberate exception: an expiry release (internal-review) zeroes the balance and
996
+ * deliberate exception: an expiry release zeroes the balance and
1114
997
  * leaves the lots standing, because a revival restores that balance and its
1115
998
  * in-kind basis has to still be there. A released credit is excluded from the
1116
999
  * sweep floor by `BITCOIN_CREDIT_PREDICATE`'s own `balance_micro > 0`, so the
@@ -1124,24 +1007,19 @@ interface FundingLot {
1124
1007
  satsFunded: number;
1125
1008
  /** Credit micro this funding bought — the lot's denominator, never depleted. */
1126
1009
  creditedMicro: number;
1127
- /** Credit micro of this lot not yet drawn or reclaimed. */
1128
1010
  remainingMicro: number;
1129
1011
  /** Rail settlement reference, provenance only. */
1130
1012
  fundingRef: string | null;
1131
1013
  createdAt: number;
1132
1014
  }
1133
- /** One lot's share of a depletion, and what that share is worth in kind. */
1134
1015
  interface LotDebit {
1135
1016
  lotId: string;
1136
- /** Credit micro taken out of this lot. */
1137
1017
  micro: number;
1138
- /** Sats that micro is worth at this lot's own funding rate, floored. */
1018
+ /** Decrease in this lot's floored sats obligation after taking the micro debit. */
1139
1019
  sats: number;
1140
1020
  }
1141
- /** What a FIFO depletion took, and what it could not cover. */
1142
1021
  interface LotDepletion {
1143
1022
  debits: LotDebit[];
1144
- /** In-kind value of everything taken — the reclaim obligation. */
1145
1023
  satsOwed: number;
1146
1024
  /** Micro the lots could not cover. Non-zero means the credit's lots are short. */
1147
1025
  uncoveredMicro: number;
@@ -1149,32 +1027,15 @@ interface LotDepletion {
1149
1027
  backedMicro: number;
1150
1028
  }
1151
1029
  /**
1152
- * Take `amountMicro` out of `lots`, oldest first, and price what was taken at
1153
- * each lot's own rate.
1154
- *
1155
- * FIFO rather than pro rata across the pool, so a top-up taken at a different
1156
- * rate is reclaimed at *that* rate once the earlier deposit is spent. A
1157
- * fraction of a satoshi cannot be handed back, so the conversion floors and
1158
- * the dust stays with the provider rather than being rounded into a payout the
1159
- * deposit does not cover.
1160
- *
1161
- * A slice is priced as the **decrement in the lot's own obligation** —
1162
- * `lotSats(before) - lotSats(after)` — rather than by flooring the slice on
1163
- * its own. The two agree whenever a lot is taken whole, which is every
1164
- * ordinary reclaim; they diverge once a lot is drawn down in pieces, and there
1165
- * the independent floor loses up to a satoshi *per piece*, permanently. That
1166
- * matters since internal-review, where each settling draw is priced through here: the
1167
- * slices have to telescope, or a lot spent over many jobs pays out less than
1168
- * it took in and the residue is exactly the unattributable one this was meant
1169
- * to remove. It also makes {@link lotOwedSats} exact rather than conservative
1170
- * — the remaining obligation is precisely what future depletions will pay.
1171
- *
1172
- * `lots` must already be in FIFO order ({@link fifoOrder}).
1030
+ * Deplete lots in FIFO order, supplied by fifoOrder. Price each debit as
1031
+ * lotSats(before) - lotSats(after), not the independently floored slice: small
1032
+ * draws then telescope to the full deposit instead of losing up to one sat
1033
+ * per draw. Fractional remaining sats stay with the provider.
1173
1034
  */
1174
1035
  declare function depleteLots(lots: FundingLot[], amountMicro: number): LotDepletion;
1175
1036
  /**
1176
1037
  * What a settling draw's fiat debit was worth in kind, in millisatoshis
1177
- * (internal-review) — `null` where the lots cannot price it.
1038
+ * `null` where the lots cannot price it.
1178
1039
  *
1179
1040
  * This is the single sats authority for a non-channel Bitcoin credit. The
1180
1041
  * settle already depletes the lots the draw consumed, so the figure costs no
@@ -1196,25 +1057,9 @@ declare function inKindDrawMsats(depletion: LotDepletion, amountMicro: number):
1196
1057
  */
1197
1058
  declare function lotOwedSats(lots: FundingLot[]): number;
1198
1059
  /**
1199
- * Sats held back from every non-channel Bitcoin reclaim to pay for handing it
1200
- * over (internal-review).
1201
- *
1202
- * Delivering a refund costs money whichever way it is fulfilled: an
1203
- * accumulator swap pays the mint's per-input fee, and a just-in-time mint pays
1204
- * the mint's quote plus whatever the Lightning hop routes for. internal-review puts
1205
- * that cost on the caller — but charging *actual* fees would mean re-publishing
1206
- * a smaller figure after the reclaim was already promised, and internal-review fixed
1207
- * `owed_sats` at request time precisely so it cannot move between the promise
1208
- * and the payout. A flat reserve keeps both: the caller is quoted one final
1209
- * number, net, and the provider spends the reserve on delivery.
1210
- *
1211
- * It is deliberately a constant rather than a policy knob. The figure only has
1212
- * to cover a small mint fee and one Lightning hop — the live hub's last hop
1213
- * measured 1.1 sats on a 2,000-sat payment (internal-review) — so there is headroom
1214
- * without a number anyone has to tune, and a knob nobody sets is a knob that
1215
- * drifts. What the reserve does not cover on some future expensive route is
1216
- * absorbed, and what it over-covers stays with the provider as dust, the same
1217
- * rule the per-lot flooring already follows.
1060
+ * Refund delivery uses a fixed eight-sat reserve so owed_sats cannot shrink
1061
+ * after it is promised. Extra delivery cost is absorbed by the provider;
1062
+ * unused reserve stays with the provider, like fractional-sat dust.
1218
1063
  */
1219
1064
  declare const DRAIN_DELIVERY_RESERVE_SATS = 8;
1220
1065
  /**
@@ -1237,59 +1082,50 @@ declare function netOwedSats(grossSats: number): number;
1237
1082
  */
1238
1083
  declare function fifoOrder(left: FundingLot, right: FundingLot): number;
1239
1084
  /**
1240
- * Whether a depletion may be settled in kind, or has to fall back to pricing
1241
- * the fiat balance at a live rate.
1242
- *
1243
- * Both failure modes are real and neither is the caller's fault: a credit's
1244
- * lots can be short (a coverage hole the boot backfill is meant to close), and
1245
- * a credit funded before internal-review recorded a rail basis at all has lots whose
1246
- * `satsFunded` is 0 — pricing *that* in kind would answer "we owe you nothing"
1247
- * for a deposit we plainly hold. Mixed coverage is treated as unbacked too:
1248
- * blending a lot rate with a live rate produces a figure neither basis
1249
- * supports.
1085
+ * Missing lot coverage or a zero legacy sats basis requires pooled pricing.
1086
+ * Mixed backed/unbacked lots also fall back: combining lot and live rates
1087
+ * would claim a value neither basis supports.
1250
1088
  */
1251
1089
  declare function isInKindDepletion(depletion: LotDepletion, amountMicro: number): boolean;
1252
1090
 
1253
1091
  /** The rails a payout lands on. Lightning never pays out: credits fund straight into the receive wallet. */
1254
1092
  type PayoutRail = "cashu" | "x402" | "tempo";
1255
1093
  /**
1256
- * What gathered the payments into one transfer: a cashu accumulator melt, an
1257
- * x402 batch settle to `pay_to`, a Tempo channel's cooperative close, or a
1258
- * Tempo channel's scheduled / manual settlement to the recipient.
1094
+ * Transfers aggregate payments: a Cashu melt, x402 batch to pay_to, or Tempo recipient
1095
+ * settlement/close.
1259
1096
  */
1260
1097
  type PayoutKind = "melt" | "batch" | "close" | "settle";
1261
- /** Which repair verb completed a stuck movement. Only `tempo_close_reconciled` is produced here today. */
1262
1098
  type PayoutRepairKind = "x402_settlement_reconciled" | "lightning_invoice_reconciled" | "tempo_close_reconciled";
1263
1099
  /**
1264
- * JSON wire shape POSTed to the platform's `/_internal/payout` endpoint —
1265
- * one landed movement into the builder's custody. Idempotent platform-side
1266
- * on `(dvmId, payoutId)`, so the durable retry loop can redeliver freely; a
1267
- * `repaired` redelivery of an id that already landed upgrades that row rather
1268
- * than adding a second.
1100
+ * One landed movement into builder custody. Platform retries deduplicate by (dvmId,
1101
+ * payoutId); repaired redelivery upgrades that movement rather than adding another.
1269
1102
  */
1270
1103
  interface PayoutReportPayload {
1271
- /** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1104
+ /**
1105
+ * Platform record ID; must match the bearer token’s DVM.
1106
+ */
1272
1107
  dvmId: string;
1273
1108
  /** Required. The DVM's own id for the movement — `cashu:melt:<quote>`, `x402:batch:<uuid>`, `tempo:<tx>`. */
1274
1109
  payoutId: string;
1275
1110
  rail: PayoutRail;
1276
1111
  kind: PayoutKind;
1277
- /** Required. Rail-native atomic units: sats, or USDC micro. Always > 0. */
1112
+ /**
1113
+ * Positive rail-native atomic units: sats or USDC micro.
1114
+ */
1278
1115
  nativeAmount: number;
1279
- /** Required. `sats` for cashu, `usdc` for the stablecoin rails. */
1280
1116
  nativeAsset: "sats" | "usdc";
1281
1117
  /** Where it landed; `null` for a melt, whose Lightning destination the platform never records. */
1282
1118
  recipient: string | null;
1283
- /** Mechanism references — mint, melt quote, tx hash, batch id, voucher count, channel id. Description material, never arithmetic. */
1119
+ /**
1120
+ * Descriptive mechanism references, never an arithmetic basis.
1121
+ */
1284
1122
  refs: Record<string, unknown>;
1285
1123
  /** Required. When the money landed (epoch ms). */
1286
1124
  landedAt: number;
1287
- /** Set when a repair verb completed this movement. */
1288
1125
  repaired?: boolean;
1289
1126
  /** Required iff `repaired`. */
1290
1127
  repairKind?: PayoutRepairKind;
1291
1128
  }
1292
- /** A stuck movement inside a pending snapshot. */
1293
1129
  interface PayoutWedge {
1294
1130
  id: string;
1295
1131
  /**
@@ -1309,17 +1145,14 @@ interface PayoutWedge {
1309
1145
  /** Rail-native amount held up, when known. */
1310
1146
  native?: number;
1311
1147
  }
1312
- /** One pool inside a rail's pending figure — a mint, a batch, a channel. */
1313
1148
  interface PayoutPool {
1314
1149
  id: string;
1315
1150
  native: number;
1316
1151
  label?: string;
1317
1152
  }
1318
1153
  /**
1319
- * JSON wire shape POSTed to the platform's `/_internal/payout-pending`
1320
- * endpoint — one rail's snapshot of money still moving toward the builder,
1321
- * replaced on each report. Best-effort: a lost one is superseded by the next
1322
- * tick, so it rides no durable queue.
1154
+ * A replaceable snapshot of one rail’s pending builder funds. Best-effort delivery uses no
1155
+ * durable queue; a later tick supersedes a lost report.
1323
1156
  */
1324
1157
  interface PayoutPendingPayload {
1325
1158
  dvmId: string;
@@ -1327,14 +1160,12 @@ interface PayoutPendingPayload {
1327
1160
  /** Everything on this rail still moving toward the builder, in atomic units. */
1328
1161
  poolNative: number;
1329
1162
  nativeAsset: "sats" | "usdc";
1330
- /** How many pools the figure is made of. */
1331
1163
  items: number;
1332
1164
  pools: PayoutPool[];
1333
1165
  wedged: PayoutWedge[];
1334
1166
  /** This DVM's clock when the snapshot was taken (epoch ms). */
1335
1167
  snapshotAt: number;
1336
1168
  }
1337
- /** The delivery seam — implemented by `RevenueReporter`, stubbed in tests. */
1338
1169
  interface PayoutTransport {
1339
1170
  /**
1340
1171
  * Queue a payout through the caller's open transaction, so the row and the
@@ -1351,19 +1182,13 @@ interface PayoutTransport {
1351
1182
  drainPending?(): Promise<void>;
1352
1183
  }
1353
1184
  /**
1354
- * A money-path hook whose report must commit with the fact it describes.
1355
- * `enqueue` runs inside the caller's transaction, and a throw rolls the fact
1356
- * back with it — the drain report's own discipline (internal-review): a call the
1357
- * caller has to repeat costs a round trip, where a landed movement with no
1358
- * row understates paid-out forever. `committed` runs once that transaction
1359
- * has committed, for the delivery and the snapshot that must not read
1360
- * uncommitted state.
1185
+ * enqueue joins the money transaction and must throw on failure so its fact rolls back.
1186
+ * committed runs after commit, when delivery and snapshots can safely read the new state.
1361
1187
  */
1362
1188
  interface TransactionalPayoutHook<E> {
1363
1189
  enqueue(event: E, tx: RevenueReporterQuerier): Promise<void>;
1364
1190
  committed(): void;
1365
1191
  }
1366
- /** What `/admin/cashu/mark-melted` knows once the rows are marked. */
1367
1192
  interface CashuMeltCompleted {
1368
1193
  rows: {
1369
1194
  id: string;
@@ -1373,7 +1198,6 @@ interface CashuMeltCompleted {
1373
1198
  meltQuoteId: string;
1374
1199
  paymentPreimage: string;
1375
1200
  }
1376
- /** What mppx reports after a Tempo channel settlement or close confirmed on chain. */
1377
1201
  interface TempoSessionSettled {
1378
1202
  txHash: string;
1379
1203
  channelId: string;
@@ -1383,27 +1207,23 @@ interface TempoSessionSettled {
1383
1207
  /** Newly settled to the payee by this transaction, atomic units. */
1384
1208
  delta: bigint;
1385
1209
  }
1386
- /** What `reconcile-tempo-drain` re-read from the close receipt it booked against. */
1387
1210
  interface TempoCloseReconciled {
1388
1211
  channelId: string;
1389
1212
  txHash: string;
1390
1213
  /** Captured by the payee — this DVM — per the receipt, atomic units as a decimal string. */
1391
1214
  settledToPayee: string;
1392
1215
  }
1393
- /** The channel view the tracker sums claims over. */
1394
1216
  interface X402TrackedChannel {
1395
1217
  channelId: string;
1396
1218
  chargedCumulativeAmount: string;
1397
1219
  totalClaimed: string;
1398
1220
  }
1399
- /** A refund settlement wedged between chain and ledger (internal-review), for the wedge list. */
1400
1221
  interface X402WedgedRefund {
1401
1222
  settlementId: string;
1402
1223
  /** Epoch ms the settlement was first prepared. */
1403
1224
  createdAt: number;
1404
1225
  native?: number;
1405
1226
  }
1406
- /** What the batch-settlement server hands the tracker once it knows its scope. */
1407
1227
  interface X402PayoutContext {
1408
1228
  /** `${network}|${payTo}|${token}` — the scope the settle-pending marker is keyed on. */
1409
1229
  scope: string;
@@ -1416,7 +1236,6 @@ interface X402PayoutContext {
1416
1236
  settleIntervalMs: number;
1417
1237
  listWedgedRefunds?: () => Promise<X402WedgedRefund[]>;
1418
1238
  }
1419
- /** The two manager verbs the tracker wraps. */
1420
1239
  interface X402TrackedManager {
1421
1240
  claim(...args: never[]): Promise<{
1422
1241
  vouchers: number;
@@ -1439,9 +1258,7 @@ interface X402PayoutObserver {
1439
1258
  /** A settle attempt that failed outside the tracked manager. One call per attempt a builder would count as one. */
1440
1259
  recordSettleFailure(error: unknown): Promise<void>;
1441
1260
  }
1442
- /** The Tempo readers the host attaches after mounting. */
1443
1261
  interface TempoPayoutReader {
1444
- /** The recipient address every Tempo payout names. */
1445
1262
  recipient?: string;
1446
1263
  /** CAIP-2 network for the refs, when known. */
1447
1264
  network?: string;
@@ -1461,11 +1278,8 @@ interface TempoPayoutReader {
1461
1278
  }) => Promise<CreditDrainRecord[]>;
1462
1279
  }
1463
1280
  /**
1464
- * The claims gathered since the last settle — the batch the next settle pays
1465
- * out. Durable across machines because claims run under the fleet lock on
1466
- * whichever machine won the tick, and the settle that finally moves the money
1467
- * may run on another: an in-process figure would report a partial amount
1468
- * after any restart in between.
1281
+ * Claims accumulated since the last settle. The batch is durable across machines: claims and
1282
+ * eventual settlement can run on different hosts under the fleet lock.
1469
1283
  */
1470
1284
  interface X402OpenBatch {
1471
1285
  scope: string;
@@ -1479,55 +1293,39 @@ interface X402OpenBatch {
1479
1293
  lastError: string | null;
1480
1294
  nextRetryAt: number | null;
1481
1295
  }
1482
- /** Where the open batch lives — Postgres beside the settlement rows, or memory in dev. */
1483
1296
  interface X402BatchStore {
1484
1297
  get(scope: string): Promise<X402OpenBatch | undefined>;
1485
1298
  put(batch: X402OpenBatch): Promise<void>;
1486
1299
  delete(scope: string): Promise<void>;
1487
1300
  /**
1488
- * Close the open batch and run `enqueue` in the same transaction, so the
1489
- * payout row is queued exactly when the batch is gone and never otherwise:
1490
- * a settle can never lose its payout, because a failed enqueue leaves the
1491
- * batch open for the next settle to report in full. `false` when the batch
1492
- * under `scope` is no longer `batchId` — a sibling settled it first — in
1493
- * which case nothing is queued.
1301
+ * Atomically delete the matching batch and enqueue its payout. Failed enqueue preserves the
1302
+ * batch for retry; false means the scope no longer holds batchId and nothing was queued.
1494
1303
  */
1495
1304
  settle(scope: string, batchId: string, enqueue: (tx?: RevenueReporterQuerier) => Promise<void>): Promise<boolean>;
1496
1305
  }
1497
- /** Construction options for {@link PayoutReporter}. */
1498
1306
  interface PayoutReporterOpts {
1499
- /** Platform DVM record ID, bound into every report. */
1500
1307
  dvmId: string;
1501
1308
  transport: PayoutTransport;
1502
1309
  /**
1503
- * The SDK's Postgres. Reads the cashu accumulator for the pending pool and
1504
- * holds the open x402 batch; without it the batch lives in memory (dev) and
1505
- * no cashu snapshot is taken.
1310
+ * Postgres holds durable x402 batches and supplies Cashu snapshots. Without it, the default
1311
+ * batch store is memory and Cashu snapshots are omitted.
1506
1312
  */
1507
1313
  db?: Pool;
1508
1314
  /**
1509
- * Whether this DVM runs the cashu accumulator. The cashu snapshot reads
1510
- * `wallet_accumulator`, which only that mode creates — on any other DVM the
1511
- * read would fail every tick and warn about a rail it does not carry.
1315
+ * Only accumulator hosts create wallet_accumulator; enabling this on another host makes
1316
+ * each snapshot query fail.
1512
1317
  */
1513
1318
  cashu?: boolean;
1514
1319
  /** Override the batch store (tests); defaults to Postgres on `db`, memory without. */
1515
1320
  batchStore?: X402BatchStore;
1516
1321
  /** Snapshot cadence, ms. Default 5 minutes. */
1517
1322
  snapshotIntervalMs?: number;
1518
- /** Override for the current time (tests). */
1519
1323
  now?: () => number;
1520
1324
  }
1521
1325
  /**
1522
- * Builds the payout and pending reports from the SDK's money paths and hands
1523
- * them to the transport.
1524
- *
1525
- * Two entry points are transactional: `cashuMelted` and `tempoCloseReconciled`
1526
- * queue their row inside the caller's transaction and throw when they cannot,
1527
- * so the melt or the reconciled close rolls back with its missing report rather
1528
- * than landing without one. The x402 settle and the pending snapshots are
1529
- * fail-safe instead: a report that cannot be built or queued there is logged and
1530
- * picked up on the next tick, never thrown into the settle that produced it.
1326
+ * Cashu melts and repaired Tempo closes enqueue inside their caller’s transaction and
1327
+ * propagate failures. x402 reporting and pending snapshots instead log failures; failed x402
1328
+ * enqueue preserves the open batch for retry.
1531
1329
  */
1532
1330
  declare class PayoutReporter implements PayoutTransport {
1533
1331
  private readonly dvmId;
@@ -1544,7 +1342,6 @@ declare class PayoutReporter implements PayoutTransport {
1544
1342
  private timer;
1545
1343
  private warmup;
1546
1344
  constructor(opts: PayoutReporterOpts);
1547
- /** Boot DDL for the durable batch store, when this reporter owns one. */
1548
1345
  init(): Promise<void>;
1549
1346
  /**
1550
1347
  * Start the snapshot loop. Unref'd, so it never keeps a scale-to-zero
@@ -1558,23 +1355,13 @@ declare class PayoutReporter implements PayoutTransport {
1558
1355
  reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
1559
1356
  /** Deliver what is queued now; a transport without the seam waits for its retry loop. */
1560
1357
  flush(): Promise<void>;
1561
- /** The hook `mark-melted` runs inside its transaction: the mark and its payout commit together. */
1562
1358
  meltHook(): TransactionalPayoutHook<CashuMeltCompleted>;
1563
1359
  /**
1564
- * A melt completed: the accumulator rows are SPENT at the mint and the
1565
- * Lightning payment reached the builder's destination. One payout per mint
1566
- * in the call — a `mark-melted` normally names one, since a melt quote
1567
- * belongs to one mint. The amount is the face value melted; the mint's fee
1568
- * and the Lightning amount received are builder-machine facts this DVM does
1569
- * not see.
1570
- *
1571
- * Queued through `tx` when the caller is inside the transaction that marks
1572
- * the rows, and a failure is thrown rather than swallowed so that mark rolls
1573
- * back with it — see {@link TransactionalPayoutHook}. Without `tx` the rows
1574
- * are queued on their own and delivered at once.
1360
+ * Report melted face value per mint, not the Lightning amount received after fees. Hash the
1361
+ * payment preimage; do not publish it. With tx, propagate enqueue failures and defer
1362
+ * delivery until commit. Without tx, enqueue independently and start delivery.
1575
1363
  */
1576
1364
  cashuMelted(event: CashuMeltCompleted, tx?: RevenueReporterQuerier): Promise<void>;
1577
- /** The host attaches the session store and ledger readers once they exist. */
1578
1365
  attachTempo(reader: TempoPayoutReader): void;
1579
1366
  /**
1580
1367
  * A Tempo channel settled or closed on chain. `delta` is what this
@@ -1582,29 +1369,18 @@ declare class PayoutReporter implements PayoutTransport {
1582
1369
  * (a close of a fully settled channel) is no payout.
1583
1370
  */
1584
1371
  tempoSettled(event: TempoSessionSettled): Promise<void>;
1585
- /** The hook `reconcile-tempo-drain` runs inside its transaction: the booking and its payout commit together. */
1586
1372
  tempoRepairHook(): TransactionalPayoutHook<TempoCloseReconciled>;
1587
1373
  /**
1588
- * The operator repaired a wedged cooperative close (internal-review). The close's
1589
- * payee side landed on chain when the close did; if the live hook reported
1590
- * it, this upgrades that row to `repair`, otherwise it is the row. The
1591
- * figure is the receipt's payee total for the channel.
1592
- *
1593
- * Queued through `tx` when the caller is inside the transaction that books
1594
- * the drain, and thrown rather than swallowed so that booking rolls back
1595
- * with it — see {@link TransactionalPayoutHook}.
1374
+ * Report the receipt’s payee total under the live close’s transaction ID, flagged repaired.
1375
+ * The platform upgrades a live row if one exists. Join tx and propagate enqueue failures;
1376
+ * this does not report the caller refund.
1596
1377
  */
1597
1378
  tempoCloseReconciled(event: TempoCloseReconciled, tx?: RevenueReporterQuerier): Promise<void>;
1598
- /** The observer the batch-settlement server attaches to and routes its manager through. */
1599
1379
  x402Observer(): X402PayoutObserver;
1600
1380
  /**
1601
- * Wrap the upstream channel manager so every claim grows the open batch and
1602
- * every settle closes it. The claim delta is read off storage rather than
1603
- * off upstream's result, which carries only a voucher count; under the
1604
- * fleet lock the before/after read is consistent. A channel the claim
1605
- * removed on its way through — a refund's claim-then-delete (internal-review) —
1606
- * moved whatever it still owed before it went, and that value reaches
1607
- * `pay_to` in this batch too, so it counts at its pre-claim figure.
1381
+ * Read claim deltas from storage because upstream returns voucher counts. Under the fleet
1382
+ * lock, before/after reads are consistent. A deleted channel contributes its pre-claim
1383
+ * unclaimed value, including refund claim-then-delete.
1608
1384
  */
1609
1385
  private trackX402Manager;
1610
1386
  private recordX402Claim;
@@ -1627,7 +1403,7 @@ declare class PayoutReporter implements PayoutTransport {
1627
1403
  /**
1628
1404
  * Tempo: each active channel's spent-but-unsettled balance — earned, not
1629
1405
  * yet paid to the recipient — and the cooperative closes wedged between
1630
- * chain and ledger (internal-review), aged past the same floor the repair queue
1406
+ * chain and ledger, aged past the same floor the repair queue
1631
1407
  * uses so an in-flight close is not reported as stuck.
1632
1408
  */
1633
1409
  snapshotTempo(): Promise<void>;
@@ -1636,65 +1412,57 @@ declare class PayoutReporter implements PayoutTransport {
1636
1412
  /**
1637
1413
  * JSON wire shape POSTed to the platform's `/_internal/job-revenue` endpoint
1638
1414
  * and stored in `pending_dvm_reports.payload` for durable retry.
1639
- *
1640
- * The platform-side consumer is `OnJobCompleted`, implemented by
1641
- * `createRevenueCallback`. Keep the two shapes in sync: the SDK and platform
1642
- * deploy in lockstep, and any
1643
- * backwards-incompatible field change requires a coordinated release. No `version`
1644
- * field is included pre-launch because there are no external container-runtime
1645
- * consumers yet; add one once the first third-party builder ships a container-runtime
1646
- * DVM (trigger condition: external builder onboarded via `dvmctl deploy --container`).
1647
1415
  */
1648
1416
  interface RevenueReportPayload {
1649
- /** Required. Platform DVM record ID (`dvms.id`). Identifies which DVM earned the revenue. */
1417
+ /** Platform DVM record ID (`dvms.id`). Identifies which DVM earned the revenue. */
1650
1418
  dvmId: string;
1651
- /** Required. Job identifier used by the platform to de-duplicate revenue rows and link to the job record. */
1419
+ /** Job identifier used by the platform to de-duplicate revenue rows and link to the job record. */
1652
1420
  jobId: string;
1653
1421
  /**
1654
- * Optional. Capability selected by dispatch for this job. Omitted for
1422
+ * Capability selected by dispatch for this job. Omitted for
1655
1423
  * reports from older SDKs and legacy persisted jobs that predate capability
1656
1424
  * dispatch; absence must not be rewritten as a capability name.
1657
1425
  */
1658
1426
  capability?: string;
1659
- /** Required. Total amount paid by the caller in millisatoshis, across all credits that satisfied the job. */
1427
+ /** Total amount paid by the caller in millisatoshis, across all credits that satisfied the job. */
1660
1428
  paidMsats: number;
1661
- /** Optional. Cashu mint URL from which the payment tokens were issued. Set only for the `cashu` rail; omitted for all other rails. */
1429
+ /** Cashu mint URL from which the payment tokens were issued. Set only for the `cashu` rail; omitted for all other rails. */
1662
1430
  paymentMint?: string;
1663
1431
  /**
1664
- * Required. Payment rail identifier (`"cashu"`, `"tempo"`, `"x402"`, `"stripe"`).
1432
+ * Payment rail identifier (`"cashu"`, `"tempo"`, `"x402"`, `"stripe"`).
1665
1433
  * Mirrors `PaymentMethod` on the platform; carried as a plain string over the wire
1666
1434
  * so the SDK has no platform-type dependency.
1667
1435
  */
1668
1436
  rail: string;
1669
1437
  /**
1670
- * Optional. Settlement reference for the credit: EVM tx hash for `x402`, credential
1438
+ * Settlement reference for the credit: EVM tx hash for `x402`, credential
1671
1439
  * challenge ID for `tempo`, `X-Cashu-Request-Id` UUID for `cashu`. Required by the
1672
1440
  * platform revenue ledger for `tempo`/`x402` rails (recordJobRevenue throws without it);
1673
1441
  * nullable for `cashu`.
1674
1442
  */
1675
1443
  paymentTxHash?: string;
1676
1444
  /**
1677
- * Optional. Rail-native payment amount in atomic units: satoshis for `tempo`, USDC
1678
- * microunits for `x402`, USD cents for `stripe`. Omitted for `cashu` (platform derives
1445
+ * Rail-native payment amount in atomic units: USDC microunits for `tempo` and
1446
+ * `x402`, USD cents for `stripe`. Omitted for `cashu` (platform derives
1679
1447
  * `paidMsats / 1000`). Must be set together with `nativeAsset`.
1680
1448
  */
1681
1449
  nativeAmount?: number;
1682
1450
  /**
1683
- * Optional. Asset tag paired with `nativeAmount`: `"sats"`, `"usdc"`, `"usdc.e"`, or
1451
+ * Asset tag paired with `nativeAmount`: `"sats"`, `"usdc"`, `"usdc.e"`, or
1684
1452
  * `"usd-cents"`. Both fields must be present for the platform to write a non-cashu
1685
1453
  * revenue row.
1686
1454
  */
1687
1455
  nativeAsset?: string;
1688
1456
  /**
1689
- * Optional. Cashu flow discriminator. Set to `"p2pk_accumulator"` when the cashu
1690
- * accumulator path (internal-review) satisfied the job; omitted for legacy cashu. Written into
1457
+ * Cashu flow discriminator. Set to `"p2pk_accumulator"` when the cashu
1458
+ * accumulator path satisfied the job; omitted for legacy cashu. Written into
1691
1459
  * `revenue_events.metadata.cashu_flow` by the platform so ops can split per-call cashu
1692
1460
  * rows by source.
1693
1461
  */
1694
1462
  cashuFlow?: string;
1695
1463
  /**
1696
- * Optional. What the job cost the **builder** to serve, as declared by the
1697
- * handler through `ctx.cost()` (internal-review) — the other half of the
1464
+ * What the job cost the **builder** to serve, as declared by the
1465
+ * handler through `ctx.cost()` — the other half of the
1698
1466
  * margin the dashboard states. `amountMicro` is 1e-6 of `currency`, which
1699
1467
  * is the currency the builder incurred the cost in and need not be the
1700
1468
  * credit's.
@@ -1715,37 +1483,50 @@ interface RevenueReportPayload {
1715
1483
  currency: string;
1716
1484
  };
1717
1485
  /**
1718
- * Optional. Credit this job's payment drew against (internal-review). Present on
1486
+ * Credit this job's payment drew against. Present on
1719
1487
  * every job a ledger-backed payment satisfied; its presence is what tells
1720
1488
  * the platform the row is a **draw-keyed** revenue event rather than a
1721
1489
  * pre-credits per-call one.
1722
1490
  */
1723
1491
  creditId?: string;
1724
- /** Optional. The draw this job settled — one draw, one revenue row. */
1492
+ /** The draw this job settled — one draw, one revenue row. */
1725
1493
  drawId?: string;
1726
- /** Optional. The draw's fiat amount, 1e-6 of `creditCurrency`. The exact liability offset. */
1494
+ /** The draw's fiat amount, 1e-6 of `creditCurrency`. The exact liability offset. */
1727
1495
  drawAmountMicro?: number;
1728
- /** Optional. Currency the credit is denominated in (lowercase ISO, e.g. `usd`). */
1496
+ /** Currency the credit is denominated in (lowercase ISO, e.g. `usd`). */
1729
1497
  creditCurrency?: string;
1730
1498
  /**
1731
- * Optional. Settlement instant (epoch ms) — draws are revenue *when they
1499
+ * Settlement instant (epoch ms) — draws are revenue *when they
1732
1500
  * settle*, so this dates the row. Omitted for pre-credits rows, which the
1733
1501
  * platform dates at record time as before.
1734
1502
  */
1735
1503
  settledAt?: number;
1736
- /** Optional. The funding's own rail reference, for reconciling a draw back to its deposit. */
1504
+ /** The funding's own rail reference, for reconciling a draw back to its deposit. */
1737
1505
  fundingRef?: string;
1738
1506
  /**
1739
- * Optional. Revenue class when this row is not ordinary service revenue.
1507
+ * Revenue class when this row is not ordinary service revenue.
1740
1508
  * `short_pay_forfeit` marks money kept from an underpayment that bought no
1741
- * job (internal-review operator ruling) so analytics can exclude it.
1509
+ * job so analytics can exclude it.
1742
1510
  */
1743
1511
  kind?: string;
1744
1512
  }
1513
+ /** Privacy-preserving terminal evidence; contains no caller or payment references. */
1514
+ interface JobTerminalReportPayload {
1515
+ dvmId: string;
1516
+ jobId: string;
1517
+ capability: string;
1518
+ outcome: "completed" | "failed" | "cancelled";
1519
+ endedBy: "caller" | "provider" | null;
1520
+ paid: boolean;
1521
+ acceptedAtMs: number;
1522
+ terminalAtMs: number;
1523
+ callerWaitMs: number;
1524
+ receiptSeq: number | null;
1525
+ }
1745
1526
  /**
1746
1527
  * JSON wire shape POSTed to the platform's `/_internal/job-cost` endpoint
1747
1528
  * when a terminal job incurred a declared builder cost but produced no
1748
- * revenue report (internal-review).
1529
+ * revenue report.
1749
1530
  *
1750
1531
  * Unlike {@link RevenueReportPayload}, this event has no payment rail or
1751
1532
  * revenue amount: a failed, cancelled, or free job is not a sale. The
@@ -1756,16 +1537,16 @@ interface RevenueReportPayload {
1756
1537
  * delivery order.
1757
1538
  */
1758
1539
  interface JobCostReportPayload {
1759
- /** Required. Platform DVM record ID (`dvms.id`); bound to the authenticating token. */
1540
+ /** Platform DVM record ID (`dvms.id`); bound to the authenticating token. */
1760
1541
  dvmId: string;
1761
- /** Required. Job identifier and the report's platform-side idempotency key. */
1542
+ /** Job identifier and the report's platform-side idempotency key. */
1762
1543
  jobId: string;
1763
- /** Optional. Capability selected by dispatch; absent only on legacy persisted jobs. */
1544
+ /** Capability selected by dispatch; absent only on legacy persisted jobs. */
1764
1545
  capability?: string;
1765
- /** Required. Terminal outcome that explains why this event carries no revenue. */
1546
+ /** Terminal outcome that explains why this event carries no revenue. */
1766
1547
  outcome: "completed" | "failed" | "cancelled";
1767
1548
  /**
1768
- * Required. Number of declarations incorporated into `cost`; the
1549
+ * Number of declarations incorporated into `cost`; the
1769
1550
  * platform keeps the payload with the highest revision for this job.
1770
1551
  */
1771
1552
  costRevision: number;
@@ -1781,45 +1562,44 @@ interface JobCostReportPayload {
1781
1562
  }
1782
1563
  /**
1783
1564
  * JSON wire shape POSTed to the platform's `/_internal/credit-deposit`
1784
- * endpoint (internal-review). A funding event is a **deposit** — a liability until
1785
- * drawn — reported separately and never summed into revenue (spec §10).
1565
+ * endpoint. A funding event is a **deposit** — a liability until
1566
+ * drawn — reported separately and never summed into revenue (the deposit-accounting rule).
1786
1567
  *
1787
1568
  * Idempotent on `(dvmId, creditId, fundingId)`, so the shared retry loop can
1788
1569
  * redeliver freely.
1789
1570
  */
1790
1571
  interface CreditDepositPayload {
1791
- /** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1572
+ /** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1792
1573
  dvmId: string;
1793
- /** Required. Credit the funding landed on. */
1794
1574
  creditId: string;
1795
- /** Required. Canonical owner of the funded credit. */
1575
+ /** Canonical owner of the funded credit. */
1796
1576
  callerPubkey: string;
1797
- /** Required. The rail's settlement reference — the deposit's idempotency key. */
1577
+ /** The rail's settlement reference — the deposit's idempotency key. */
1798
1578
  fundingId: string;
1799
- /** Required. Payment rail (`"cashu"`, `"tempo"`, `"x402"`). */
1579
+ /** Payment rail (`"cashu"`, `"tempo"`, `"x402"`). */
1800
1580
  rail: string;
1801
- /** Required. Rail value received, in millisatoshis. */
1581
+ /** Rail value received, in millisatoshis. */
1802
1582
  paidMsats: number;
1803
1583
  /** Cashu only. Full received proof face before its receiver input fee. */
1804
1584
  grossPaidMsats?: number;
1805
1585
  /** Cashu only. Standalone NUT-02 fee reserved by the received proofs. */
1806
1586
  cashuInputFeeMsats?: number;
1807
- /** Optional. Rail-native atomic units; omitted for cashu (derived from `paidMsats`). */
1587
+ /** Rail-native atomic units; omitted for cashu (derived from `paidMsats`). */
1808
1588
  nativeAmount?: number;
1809
- /** Optional. Asset tag paired with `nativeAmount`. */
1589
+ /** Asset tag paired with `nativeAmount`. */
1810
1590
  nativeAsset?: string;
1811
- /** Optional. Cashu mint URL the funding proofs were issued from. */
1591
+ /** Cashu mint URL the funding proofs were issued from. */
1812
1592
  paymentMint?: string;
1813
- /** Required. Fiat micro credited to the ledger — the liability this deposit creates. */
1593
+ /** Fiat micro credited to the ledger — the liability this deposit creates. */
1814
1594
  amountMicro: number;
1815
- /** Required. Currency `amountMicro` is 1e-6 of. */
1595
+ /** Currency `amountMicro` is 1e-6 of. */
1816
1596
  creditCurrency: string;
1817
- /** Required. When the funding committed (epoch ms). */
1597
+ /** When the funding committed (epoch ms). */
1818
1598
  fundedAt: number;
1819
- /** Required. Current credit expiry after this funding committed (epoch ms). */
1599
+ /** Current credit expiry after this funding committed (epoch ms). */
1820
1600
  expiryMs: number;
1821
1601
  /**
1822
- * Tempo session funding only (internal-review): the TIP-1034 channel this deposit
1602
+ * Tempo session funding only: the TIP-1034 channel this deposit
1823
1603
  * arrived on, registered so the platform's close observer can route a
1824
1604
  * `CloseRequested` event back to the DVM that owns the channel.
1825
1605
  *
@@ -1841,14 +1621,14 @@ interface CreditDepositPayload {
1841
1621
  payee?: string;
1842
1622
  };
1843
1623
  /**
1844
- * Tempo session funding only (internal-review): which channel this deposit's voucher
1624
+ * Tempo session funding only: which channel this deposit's voucher
1845
1625
  * was accepted on, emitted **whenever the funding was session-based** —
1846
1626
  * independently of whether {@link CreditDepositPayload.tempoChannel} could be
1847
1627
  * read. That independence is the entire point: `rail: "tempo"` covers both
1848
1628
  * `tempo/session` and the `tempo/charge` one-shot, and a charge legitimately
1849
1629
  * carries no channel at all, so without this the platform cannot tell a
1850
1630
  * routine charge deposit from a session whose chain binding was lost — the
1851
- * internal-review shape, where the close observer silently never watches the escrow.
1631
+ * shape, where the close observer silently never watches the escrow.
1852
1632
  *
1853
1633
  * Carries only the channel id, not the chain binding: a DVM that could read
1854
1634
  * the binding sends `tempoChannel` too, and one that could not still names
@@ -1859,7 +1639,7 @@ interface CreditDepositPayload {
1859
1639
  channelId: string;
1860
1640
  };
1861
1641
  /**
1862
- * Set by the blocked-invoice repair (internal-review): this deposit is a paid
1642
+ * Set by the blocked-invoice repair: this deposit is a paid
1863
1643
  * Lightning invoice the builder's agent reconciled onto a credit. The money
1864
1644
  * landed in the receive wallet at payment time and this deposit is the one
1865
1645
  * row it gets, so the settlements table types it `repair` off this flag
@@ -1869,7 +1649,7 @@ interface CreditDepositPayload {
1869
1649
  }
1870
1650
  /**
1871
1651
  * JSON wire shape POSTed to the platform's `/_internal/credit-draw-release`
1872
- * endpoint (internal-review). A released hold leaves the credit balance intact, but
1652
+ * endpoint. A released hold leaves the credit balance intact, but
1873
1653
  * marks that slice of the outstanding liability as failure churn rather than
1874
1654
  * untouched prefunding.
1875
1655
  *
@@ -1877,62 +1657,45 @@ interface CreditDepositPayload {
1877
1657
  * redeliver freely.
1878
1658
  */
1879
1659
  interface CreditDrawReleasePayload {
1880
- /** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1660
+ /** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1881
1661
  dvmId: string;
1882
- /** Required. Credit whose hold was released. */
1883
1662
  creditId: string;
1884
- /** Required. Released draw; the platform idempotency key with DVM and credit. */
1663
+ /** Released draw; the platform idempotency key with DVM and credit. */
1885
1664
  drawId: string;
1886
- /** Required. Fiat micro restored to availability. */
1665
+ /** Fiat micro restored to availability. */
1887
1666
  amountMicro: number;
1888
- /** Required. Currency `amountMicro` is 1e-6 of. */
1667
+ /** Currency `amountMicro` is 1e-6 of. */
1889
1668
  creditCurrency: string;
1890
- /** Required. Per-credit gap-free sequence assigned when the draw was placed. */
1669
+ /** Per-credit gap-free sequence assigned when the draw was placed. */
1891
1670
  ledgerSeq: number;
1892
- /** Required. When the hold reached `released` (epoch ms). */
1671
+ /** When the hold reached `released` (epoch ms). */
1893
1672
  releasedAt: number;
1894
1673
  }
1895
- /** Repair verbs that can finish a caller-facing credit reclaim. */
1896
1674
  type CreditDrainRepairKind = "x402_settlement_reconciled" | "tempo_close_reconciled";
1897
1675
  /**
1898
- * JSON wire shape POSTed to the platform's `/_internal/credit-drain`
1899
- * endpoint (internal-review). A drain is a **reclaim**: deposited money paid back to
1900
- * the caller, which extinguishes the liability the funding created. Reported
1901
- * so outstanding liability nets out to `deposited − drawn − drained`; never
1902
- * summed into revenue, and never a revenue row — no job was sold.
1903
- *
1904
- * Reported only on the drain's **terminal** transition (`picked_up` for cashu,
1905
- * `sent` for the other rails), because that is where the money has verifiably
1906
- * left. `requested` and `parked` have not paid the caller yet.
1907
- *
1908
- * Idempotent on `(dvmId, creditId, drainId)`, so the shared retry loop can
1909
- * redeliver freely.
1910
- *
1911
- * Fiat micro only, with no `paidMsats` sibling: `credit_drains` holds no rail
1912
- * value (the sats figure is derived at read time from a live rate), so there
1913
- * would be nothing truthful to put in one — and the liability arithmetic this
1914
- * feeds is exact fiat micro on all three of its terms.
1676
+ * Terminal picked_up/sent drains report reclaimed fiat micro-units, reducing
1677
+ * outstanding liability rather than adding revenue. Requested or parked drains
1678
+ * have not paid the caller. Idempotency key: (dvmId, creditId, drainId).
1915
1679
  */
1916
1680
  interface CreditDrainPayload {
1917
- /** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1681
+ /** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1918
1682
  dvmId: string;
1919
- /** Required. Credit the reclaim came off. */
1920
1683
  creditId: string;
1921
- /** Required. Canonical owner of the reclaimed credit. */
1684
+ /** Canonical owner of the reclaimed credit. */
1922
1685
  callerPubkey: string;
1923
- /** Required. The drain's own idempotency key. */
1686
+ /** The drain's own idempotency key. */
1924
1687
  drainId: string;
1925
- /** Required. Payout rail the reclaim shipped on (`"cashu"`, `"x402"`, `"tempo"`). */
1688
+ /** Payout rail (`"cashu"`, `"x402"`, `"tempo"`). */
1926
1689
  method: string;
1927
- /** Required. Fiat micro reclaimed — the liability this drain extinguishes. */
1690
+ /** Fiat micro reclaimed — the liability this drain extinguishes. */
1928
1691
  amountMicro: number;
1929
- /** Required. Currency `amountMicro` is 1e-6 of. */
1692
+ /** Currency `amountMicro` is 1e-6 of. */
1930
1693
  creditCurrency: string;
1931
- /** Required. Terminal state reached: `"picked_up"` (cashu) or `"sent"` (other rails). */
1694
+ /** Terminal state: `"picked_up"` for cashu, `"sent"` for other rails. */
1932
1695
  status: string;
1933
- /** Required. When the money left (epoch ms). */
1696
+ /** When the money left (epoch ms). */
1934
1697
  drainedAt: number;
1935
- /** Optional. Per-credit gap-free ledger sequence, for reconciling against the DVM's ledger. */
1698
+ /** Per-credit gap-free ledger sequence, for reconciling against the DVM's ledger. */
1936
1699
  ledgerSeq?: number;
1937
1700
  /** Set when an operator repair verb completed this reclaim. */
1938
1701
  repaired?: boolean;
@@ -1957,7 +1720,7 @@ type CreditDepositEnqueue = (tx: RevenueReporterQuerier, payload: CreditDepositP
1957
1720
  type CreditDrawReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload) => Promise<void>;
1958
1721
  /**
1959
1722
  * Transactional drain-outbox seam threaded through each terminal drain
1960
- * transition (internal-review).
1723
+ * transition.
1961
1724
  *
1962
1725
  * Implemented by {@link RevenueReporter.enqueueCreditDrain}; callers must await
1963
1726
  * it before committing the status CAS that moved the money.
@@ -1965,7 +1728,7 @@ type CreditDrawReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditDraw
1965
1728
  type CreditDrainEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrainPayload) => Promise<void>;
1966
1729
  /**
1967
1730
  * JSON wire shape POSTed to the platform's `/_internal/credit-expiry-release`
1968
- * endpoint (internal-review). A prepaid credit that reaches its TTL with an undrawn
1731
+ * endpoint. A prepaid credit that reaches its TTL with an undrawn
1969
1732
  * remainder RELEASES that remainder to the builder: on cashu, Lightning and
1970
1733
  * x402 the funding value reached builder custody at funding time or on the
1971
1734
  * settlement cycle, so expiry extinguishes the caller's remaining claim.
@@ -1981,39 +1744,37 @@ type CreditDrainEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrainPaylo
1981
1744
  *
1982
1745
  * - **The release**, written by the expiry sweep with `reversedAt` absent.
1983
1746
  * - **The reversal**, written when a later funding revives the credit
1984
- * (`CreditLedger.fund` overwrites `expiry_ms`, so the recorded remainder is
1985
- * no longer final). It repeats the whole payload rather than sending a
1986
- * reference, because the retry loop can deliver the two out of order and the
1987
- * platform's upsert must be able to land either one first.
1747
+ * (`CreditLedger.fund` overwrites `expiry_ms`, so the recorded remainder is
1748
+ * no longer final). It repeats the whole payload rather than sending a
1749
+ * reference, because the retry loop can deliver the two out of order and the
1750
+ * platform's upsert must be able to land either one first.
1988
1751
  *
1989
1752
  * Idempotent on `(dvmId, creditId, releaseId)`, so the shared retry loop can
1990
1753
  * redeliver freely.
1991
1754
  */
1992
1755
  interface CreditExpiryReleasePayload {
1993
- /** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1756
+ /** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
1994
1757
  dvmId: string;
1995
- /** Required. Credit whose remainder expired. */
1996
1758
  creditId: string;
1997
- /** Required. Per-expiry-episode key; the platform idempotency key with DVM and credit. */
1759
+ /** Per-expiry-episode key; the platform idempotency key with DVM and credit. */
1998
1760
  releaseId: string;
1999
- /** Required. Caller whose claim on the remainder ended. */
2000
1761
  callerPubkey: string;
2001
1762
  /** Funding rail. `null` on a credit funded before the basis became mandatory. */
2002
1763
  rail: string | null;
2003
- /** Required. Fiat micro released — the liability this expiry extinguishes. */
1764
+ /** Fiat micro released — the liability this expiry extinguishes. */
2004
1765
  amountMicro: number;
2005
- /** Required. Currency `amountMicro` is 1e-6 of. */
1766
+ /** Currency `amountMicro` is 1e-6 of. */
2006
1767
  creditCurrency: string;
2007
- /** Required. When the credit's TTL ran out (epoch ms) — when the liability ended. */
1768
+ /** When the credit's TTL ran out (epoch ms) — when the liability ended. */
2008
1769
  expiryMs: number;
2009
- /** Required. When the sweep observed the expiry (epoch ms). */
1770
+ /** When the sweep observed the expiry (epoch ms). */
2010
1771
  releasedAt: number;
2011
1772
  /** Set only on the reversal report: when the revival funding landed (epoch ms). */
2012
1773
  reversedAt?: number | null;
2013
1774
  }
2014
1775
  /**
2015
1776
  * Transactional expiry-release outbox seam, threaded through the sweep's insert
2016
- * and through the funding that reverses a release (internal-review).
1777
+ * and through the funding that reverses a release.
2017
1778
  *
2018
1779
  * Implemented by {@link RevenueReporter.enqueueCreditExpiryRelease}; callers
2019
1780
  * must await it before committing the write that made the release (or its
@@ -2023,7 +1784,7 @@ interface CreditExpiryReleasePayload {
2023
1784
  type CreditExpiryReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload) => Promise<void>;
2024
1785
  /**
2025
1786
  * JSON wire shape POSTed to the platform's `/_internal/paid-job-death` endpoint
2026
- * (internal-review) when the stale-job reaper force-fails a *paid* job — its pending
1787
+ * when the stale-job reaper force-fails a *paid* job — its pending
2027
1788
  * credit draw is released rather than settled, so the caller's payment is back
2028
1789
  * on their credit balance and reclaimable by drain. The platform resolves fiat
2029
1790
  * + owner and sends an ambient notice via the shared alert sink. Unlike
@@ -2032,52 +1793,45 @@ type CreditExpiryReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditEx
2032
1793
  * report degrades to log-only rather than blocking the sweep.
2033
1794
  */
2034
1795
  interface PaidJobDeathPayload {
2035
- /** Required. Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
1796
+ /** Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
2036
1797
  dvmId: string;
2037
- /** Required. Job identifier the reaper force-failed. */
2038
1798
  jobId: string;
2039
- /** Required. Capability the job dispatched to (e.g. `"add-episode"`). */
2040
1799
  capability: string;
2041
- /** Required. Amount of the released credit draw in millisatoshis. */
1800
+ /** Amount of the released credit draw in millisatoshis. */
2042
1801
  paidMsats: number;
2043
- /** Required. Payment rail of the credit draw (`"cashu"` / `"tempo"` / `"x402"`). */
1802
+ /** Payment rail of the released draw (`"cashu"`, `"tempo"`, `"x402"`). */
2044
1803
  rail: string;
2045
- /** Required. Terminal reason the reaper set: dead worker vs never-terminal idle. */
1804
+ /** Reaper reason: dead worker or never-terminal idle job. */
2046
1805
  reason: string;
2047
- /** Optional. Rail-native released amount, paired with `nativeAsset`. */
1806
+ /** Rail-native released amount, paired with `nativeAsset`. */
2048
1807
  nativeAmount?: number;
2049
- /** Optional. Asset tag for `nativeAmount` (`"sats"` / `"usdc"` / …). */
1808
+ /** Asset tag for `nativeAmount` (`"sats"` / `"usdc"` / …). */
2050
1809
  nativeAsset?: string;
2051
- /** Optional. Cashu mint URL the credit draw's tokens were issued from. */
1810
+ /** Cashu mint URL the credit draw's tokens were issued from. */
2052
1811
  paymentMint?: string;
2053
1812
  }
2054
- /** Distinct causes for a settled credit draw that could not book revenue. */
2055
1813
  type RevenueSkippedNoRailReason = "credit_rail_null" | "credit_rail_unrecognized" | "job_rail_missing" | "ledger_not_configured";
2056
1814
  /**
2057
1815
  * JSON wire shape POSTed to the platform's `/_internal/revenue-skipped-no-rail`
2058
- * endpoint (internal-review) when a settled credit draw has no usable payment rail
1816
+ * endpoint when a settled credit draw has no usable payment rail
2059
1817
  * for its revenue event. This is an operator notice rather than a durable
2060
1818
  * accounting report: the SDK's structured log remains the durable backstop,
2061
1819
  * and a failed alert must never block terminal handling or reconciliation.
2062
1820
  */
2063
1821
  interface RevenueSkippedNoRailPayload {
2064
- /** Required. Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
1822
+ /** Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
2065
1823
  dvmId: string;
2066
- /** Required. Job whose settled draw could not be booked. */
2067
1824
  jobId: string;
2068
- /** Required. Credit carrying the settled draw. */
2069
1825
  creditId: string;
2070
- /** Required. Settled draw that has no revenue event. */
2071
1826
  drawId: string;
2072
- /** Required. Caller payment captured for the job, in millisatoshis. */
1827
+ /** Caller payment captured for the job, in millisatoshis. */
2073
1828
  paidMsats: number;
2074
- /** Optional. Settled draw amount in 1e-6 of `creditCurrency`. */
1829
+ /** Settled draw amount in 1e-6 of `creditCurrency`. */
2075
1830
  drawAmountMicro?: number;
2076
- /** Optional. Currency the draw amount is denominated in. */
1831
+ /** Currency the draw amount is denominated in. */
2077
1832
  creditCurrency?: string;
2078
1833
  /** Credit funding rail as stored on the draw, or null for legacy rows. */
2079
1834
  creditRail: string | null;
2080
- /** Required classification of why the revenue rail was unavailable. */
2081
1835
  reason: RevenueSkippedNoRailReason;
2082
1836
  }
2083
1837
  /** Gas-balance observation POSTed by a platform-hosted x402 self-relay. */
@@ -2147,11 +1901,13 @@ interface TempoSettlementFailurePayload {
2147
1901
  drainId?: string;
2148
1902
  }
2149
1903
  /**
2150
- * Durable revenue reporter for container-runtime DVMs (internal-review).
1904
+ * Durable revenue reporter for container-runtime DVMs.
2151
1905
  *
2152
1906
  * On job completion the SDK calls `report()`, which persists the payload to
2153
1907
  * a local Postgres table and attempts an immediate POST to the platform.
2154
1908
  * A background retry loop picks up un-acked rows with exponential backoff.
1909
+ * Gauge and health notifications are best-effort: failed delivery is logged
1910
+ * and dropped rather than queued for retry.
2155
1911
  */
2156
1912
  declare class RevenueReporter {
2157
1913
  private readonly db;
@@ -2164,16 +1920,17 @@ declare class RevenueReporter {
2164
1920
  constructor(db: Pool, platformUrl: string, platformToken: string);
2165
1921
  /** Create the pending-reports table and migrate the legacy SDK outbox (idempotent). */
2166
1922
  init(): Promise<void>;
2167
- /** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
2168
1923
  private createTables;
2169
- /** Copy rows from the old SDK-owned table without touching the platform's table of that name. */
2170
1924
  private migrateLegacySdkOutbox;
2171
- /** Refuse a pre-existing relation whose columns cannot safely serve as the SDK outbox. */
2172
1925
  private assertSdkTableShape;
2173
1926
  private assertSdkColumns;
2174
1927
  private tableColumns;
2175
1928
  /** Persist a revenue report and attempt immediate delivery. */
2176
1929
  report(payload: RevenueReportPayload): Promise<void>;
1930
+ /** The permanent deduplication claim and outbox insert commit together.
1931
+ * The claim survives successful delivery, including for custom job stores.
1932
+ */
1933
+ reportTerminal(payload: JobTerminalReportPayload): Promise<void>;
2177
1934
  /** Persist a zero-revenue job cost and attempt immediate delivery. */
2178
1935
  reportCost(payload: JobCostReportPayload): Promise<void>;
2179
1936
  /**
@@ -2187,21 +1944,13 @@ declare class RevenueReporter {
2187
1944
  /** Join a released draw to the transaction that made the hold terminal. */
2188
1945
  enqueueCreditDrawRelease(tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload): Promise<void>;
2189
1946
  /**
2190
- * Join a credit drain to the transaction that made it terminal (internal-review).
2191
- *
2192
- * Same discipline as {@link enqueueDeposit} and the same reason: no network
2193
- * I/O here, only the durable queue row, written through `tx` so the report
2194
- * and the status CAS commit or roll back together. A drain whose row was
2195
- * lost between the two would overstate outstanding liability forever —
2196
- * the mirror image of a lost deposit.
2197
- *
2198
- * Named for the payload rather than the verb because `drain` already means
2199
- * "flush the queue" in this class (see {@link drainPending}).
1947
+ * Await durable queue insertion through tx so the terminal drain and its
1948
+ * report commit or roll back together; no network I/O here.
2200
1949
  */
2201
1950
  enqueueCreditDrain(tx: RevenueReporterQuerier, payload: CreditDrainPayload): Promise<void>;
2202
1951
  /**
2203
1952
  * Join a payout to the transaction that commits the movement it reports
2204
- * (internal-review) — the batch close, the accumulator mark, the drain booking.
1953
+ * the batch close, the accumulator mark, the drain booking.
2205
1954
  * No network I/O, only the durable queue row through `tx`, the discipline
2206
1955
  * {@link enqueueCreditDrain} set and for the same reason: the money has
2207
1956
  * already moved on a rail this ledger does not own, so the platform row is
@@ -2219,25 +1968,19 @@ declare class RevenueReporter {
2219
1968
  */
2220
1969
  reportPayout(payload: PayoutReportPayload): Promise<void>;
2221
1970
  /**
2222
- * Report one rail's pending-pool snapshot (internal-review). Best-effort and
1971
+ * Report one rail's pending-pool snapshot. Best-effort and
2223
1972
  * fire-and-forget, deliberately unlike {@link reportPayout}: a snapshot is
2224
1973
  * replaced by the next tick, so queueing a stale one behind an outage would
2225
1974
  * only deliver figures the platform already has newer ones for.
2226
1975
  */
2227
1976
  reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
2228
1977
  /**
2229
- * Join a credit-expiry release — or the revival that reverses one — to the
2230
- * transaction that made it true (internal-review).
2231
- *
2232
- * Same discipline as {@link enqueueCreditDrain}: no network I/O, only the
2233
- * durable queue row written through `tx`. A release whose row was lost after
2234
- * the sweep committed would understate committed value forever; a lost
2235
- * reversal would overstate it, which is the worse direction — the builder
2236
- * would see money as theirs that a revived credit can still buy work with.
1978
+ * Await durable queue insertion through tx so expiry release or reversal and
1979
+ * its report commit together. A missing reversal would overstate builder value.
2237
1980
  */
2238
1981
  enqueueCreditExpiryRelease(tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload): Promise<void>;
2239
1982
  /**
2240
- * Report a reaper-force-failed paid job to the platform (internal-review) so the
1983
+ * Report a reaper-force-failed paid job to the platform so the
2241
1984
  * operator receives an ambient notice that a DVM died or wedged mid-job.
2242
1985
  * Best-effort and fire-and-forget: unlike {@link report} there is no local
2243
1986
  * durable queue — a non-2xx or transport failure is structured-logged
@@ -2247,24 +1990,18 @@ declare class RevenueReporter {
2247
1990
  */
2248
1991
  reportPaidJobDeath(payload: PaidJobDeathPayload): Promise<void>;
2249
1992
  /**
2250
- * Report a settled draw whose revenue event was skipped (internal-review).
1993
+ * Report a settled draw whose revenue event was skipped.
2251
1994
  * Best-effort and fire-and-forget: the SDK's structured
2252
1995
  * `revenue_skipped_no_rail` log is the durable backstop, so a failed notice
2253
1996
  * must never wedge terminal handling or the orphan-draw reconciler.
2254
1997
  */
2255
1998
  reportRevenueSkippedNoRail(payload: RevenueSkippedNoRailPayload): Promise<void>;
2256
- /** Report the self-relay's Base gas gauge for platform-side paging and recovery. */
2257
1999
  reportX402SelfRelayGasBalance(payload: X402SelfRelayGasBalancePayload): Promise<void>;
2258
- /** Report redacted self-relay RPC health for platform-side paging and recovery. */
2259
2000
  reportX402SelfRelayRpcHealth(payload: X402SelfRelayRpcHealthPayload): Promise<void>;
2260
- /** Report the Tempo operator's fee-token gauge for paging and recovery. */
2261
2001
  reportTempoSettlementBalance(payload: TempoSettlementBalancePayload): Promise<void>;
2262
- /** Report a redacted operator fee rejection immediately. */
2263
2002
  reportTempoSettlementFailure(payload: TempoSettlementFailurePayload): Promise<void>;
2264
2003
  private postTempoSettlementReadiness;
2265
- /** Start the background retry loop. */
2266
2004
  startRetryLoop(): void;
2267
- /** Stop the background retry loop. */
2268
2005
  stop(): void;
2269
2006
  /**
2270
2007
  * Operator-facing queue snapshot: distinguishes the live retry queue from
@@ -2280,16 +2017,13 @@ declare class RevenueReporter {
2280
2017
  /**
2281
2018
  * Deliver one bounded batch from the durable queue.
2282
2019
  *
2283
- * Public so startup/recovery tests and operator tooling can drive the same
2284
- * drain the background timer uses without reaching into private state.
2020
+ * Processes due, non-stale rows; failures back off up to five minutes and
2021
+ * become terminal after 24 hours. Successful deliveries remove their rows.
2285
2022
  */
2286
2023
  drainPending(): Promise<void>;
2287
- /** Durably queue one report of either kind, then try it once immediately. */
2288
2024
  private enqueue;
2289
- /** Insert one pending report through either the pool or an open transaction. */
2290
2025
  private insertPending;
2291
2026
  private attemptDelivery;
2292
- /** Mark a pending row terminal (stale) with a classification reason. */
2293
2027
  private markStale;
2294
2028
  }
2295
2029
 
@@ -2298,7 +2032,7 @@ type X402ChannelQuerier = Pick<Pool, "query">;
2298
2032
  /**
2299
2033
  * Durable phase of an irreversible x402 facilitator settlement.
2300
2034
  *
2301
- * `written_off` is the operator's terminal (internal-review): a settlement whose
2035
+ * `written_off` is the operator's terminal: a settlement whose
2302
2036
  * ledger leg cannot be completed, reviewed and closed so it leaves the repair
2303
2037
  * queue without pretending it booked. It is deliberately reversible — a later
2304
2038
  * reconcile still accepts it, because it moved nothing.
@@ -2317,7 +2051,7 @@ interface X402SettlementIntent {
2317
2051
  /**
2318
2052
  * The channel as it stood when the settlement was handed to the facilitator —
2319
2053
  * after the refund's mandatory on-chain refresh, which {@link channelBefore}
2320
- * precedes (internal-review).
2054
+ * precedes.
2321
2055
  *
2322
2056
  * The facilitator sizes a cooperative refund from *live* state
2323
2057
  * (`balance − chargedCumulativeAmount`) and signs it against the *live*
@@ -2363,7 +2097,7 @@ interface X402SettlementPage {
2363
2097
  nextAfter?: X402SettlementCursor;
2364
2098
  }
2365
2099
  /**
2366
- * Fleet-wide serialisation of the self-relay's chain submissions (internal-review).
2100
+ * Fleet-wide serialisation of the self-relay's chain submissions.
2367
2101
  *
2368
2102
  * Narrow on purpose, and implemented by {@link PostgresX402ChannelStorage}: the
2369
2103
  * relay asks for one thing — exclusive use of its gas EOA for the length of one
@@ -2374,7 +2108,6 @@ interface X402SettlementPage {
2374
2108
  interface X402RelaySubmissionLock {
2375
2109
  withRelaySubmissionLock<T>(key: string, operation: () => Promise<T>): Promise<T>;
2376
2110
  }
2377
- /** Construction options for {@link PostgresX402ChannelStorage}. */
2378
2111
  interface X402ChannelStorageOpts {
2379
2112
  /**
2380
2113
  * Override the relay submission lock's wait ceiling, in milliseconds.
@@ -2411,7 +2144,7 @@ interface X402RelayLockHolder {
2411
2144
  * fault in the payment: a fleet-mate whose RPC is retrying holds the lock, and
2412
2145
  * the same request works once it lets go. It refuses rather than proceeding —
2413
2146
  * two machines submitting from one EOA collide on the nonce — and it refuses
2414
- * rather than blocking, which is the internal-review defect: the old acquire had no
2147
+ * rather than blocking, which is the defect: the old acquire had no
2415
2148
  * `lock_timeout`, so a wedged holder pinned a host-pool client and a channel
2416
2149
  * advisory lock for as long as it took.
2417
2150
  */
@@ -2428,7 +2161,7 @@ interface X402SettlementWriteOff {
2428
2161
  intent: X402SettlementIntent;
2429
2162
  written: boolean;
2430
2163
  }
2431
- /** The refund standing between a channel and the credit it backs (internal-review). */
2164
+ /** The refund standing between a channel and the credit it backs. */
2432
2165
  interface X402UnresolvedRefund {
2433
2166
  settlementId: string;
2434
2167
  /** `${creditId}:${drainId}` — the drain this settlement belongs to. */
@@ -2436,7 +2169,7 @@ interface X402UnresolvedRefund {
2436
2169
  status: X402SettlementStatus;
2437
2170
  }
2438
2171
  /**
2439
- * The credit ledger's read into durable x402 settlement state (internal-review).
2172
+ * The credit ledger's read into durable x402 settlement state.
2440
2173
  *
2441
2174
  * Narrow on purpose: the ledger asks one question — is this channel's refund
2442
2175
  * still unresolved — and must not acquire the ability to move a settlement.
@@ -2454,7 +2187,7 @@ interface X402RefundSettlementGate {
2454
2187
  * transaction to the credit ledger: the fixed lock order is channel first,
2455
2188
  * credit second, and no credit-ledger path calls back into this store.
2456
2189
  *
2457
- * Two pools, and the split is load-bearing (internal-review). The settlement markers
2190
+ * Two pools, and the split is load-bearing. The settlement markers
2458
2191
  * must write *outside* the ledger transaction, which means a second connection
2459
2192
  * taken while the transaction's client is still checked out — on the shared
2460
2193
  * host pool that is a self-deadlock the moment concurrent deposits reach its
@@ -2475,7 +2208,6 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2475
2208
  private readonly relayLockTimeoutMs;
2476
2209
  private readonly terminalizeUnbackedCredit?;
2477
2210
  constructor(pool: Pool, opts?: X402ChannelStorageOpts);
2478
- /** Create the durable channel table under the SDK-wide DDL lock. */
2479
2211
  init(): Promise<void>;
2480
2212
  /**
2481
2213
  * Read the fleet's durable "claimed funds are awaiting transfer" marker.
@@ -2487,7 +2219,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2487
2219
  *
2488
2220
  * Both accessors run on the settlement pool: every caller reads or writes the
2489
2221
  * marker while {@link withFleetSettlementLock} holds a host-pool client, which
2490
- * is the nesting the internal-review split exists to keep off a non-terminal pool.
2222
+ * is the nesting the split exists to keep off a non-terminal pool.
2491
2223
  */
2492
2224
  isSettlePending(scope: string): Promise<boolean>;
2493
2225
  /** Record whether a claim's funds still need a `settle(receiver, token)` transfer. */
@@ -2497,16 +2229,16 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2497
2229
  *
2498
2230
  * On the dedicated settlement pool like every other marker, and for a sharper
2499
2231
  * reason than they had: this and {@link prepareSettlement} now run *inside*
2500
- * the channel transaction (internal-review), which holds a host-pool client for its
2232
+ * the channel transaction, which holds a host-pool client for its
2501
2233
  * whole duration. Taking a second client from that same pool is the
2502
- * self-deadlock internal-review fixed — at pool max every client is a holder and a
2234
+ * self-deadlock fixed — at pool max every client is a holder and a
2503
2235
  * waiter — and moving these calls under the transaction is exactly what would
2504
2236
  * reintroduce it.
2505
2237
  */
2506
2238
  getSettlement(operation: "fund" | "refund", effectId: string): Promise<X402SettlementIntent | undefined>;
2507
2239
  /**
2508
2240
  * The refund on this channel that is neither finished nor abandoned, if there
2509
- * is one — the credit ledger's spend gate (internal-review).
2241
+ * is one — the credit ledger's spend gate.
2510
2242
  *
2511
2243
  * `statuses` is the caller's, because the two gates disagree by design: a
2512
2244
  * draw is blocked by {@link X402_SPEND_BLOCKING_SETTLEMENT_STATUSES}, a fresh
@@ -2514,12 +2246,12 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2514
2246
  *
2515
2247
  * On the dedicated settlement pool, and that is what makes the gate callable
2516
2248
  * at all: a draw routinely runs inside an open channel transaction holding a
2517
- * host-pool client, and taking a second one from that pool is the internal-review
2249
+ * host-pool client, and taking a second one from that pool is the
2518
2250
  * self-deadlock. The oldest blocking row wins, so a channel that somehow
2519
2251
  * accumulated two names the one an operator should reach for first.
2520
2252
  *
2521
2253
  * Projects three columns rather than decoding a whole {@link
2522
- * X402SettlementIntent}: the decoder refuses a pre-internal-review row that carries
2254
+ * X402SettlementIntent}: the decoder refuses a legacy row that carries
2523
2255
  * no reconciliation snapshot, and a gate that throws on one would take every
2524
2256
  * draw on that channel down with it — when the row is exactly the kind that
2525
2257
  * ought to block them.
@@ -2527,7 +2259,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2527
2259
  pendingRefundSettlement(channelId: string, statuses: X402SettlementStatus[]): Promise<X402UnresolvedRefund | undefined>;
2528
2260
  /**
2529
2261
  * Read one settlement by its own identifier — the handle an operator holds
2530
- * (internal-review). Same dedicated pool as {@link getSettlement}, for the same
2262
+ * Same dedicated pool as {@link getSettlement}, for the same
2531
2263
  * reason: the repair reads this while a channel transaction may be open.
2532
2264
  */
2533
2265
  getSettlementById(settlementId: string): Promise<X402SettlementIntent | undefined>;
@@ -2535,7 +2267,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2535
2267
  normalizeLegacyPaymentId(settlementId: string, operation: "fund" | "refund", legacyPaymentId: string, paymentId: string): Promise<X402SettlementIntent>;
2536
2268
  /**
2537
2269
  * The operator's repair queue: settlements stuck between "chain paid" and
2538
- * "ledger booked", oldest write first (internal-review).
2270
+ * "ledger booked", oldest write first.
2539
2271
  *
2540
2272
  * Keyset-paginated on `(created_at, settlement_id)` for the reason the
2541
2273
  * blocked-invoice queue is: a wedged row never self-clears, so a fixed first
@@ -2560,7 +2292,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2560
2292
  after?: X402SettlementCursor;
2561
2293
  }): Promise<X402SettlementPage>;
2562
2294
  /**
2563
- * Close a wedged settlement without booking anything (internal-review).
2295
+ * Close a wedged settlement without booking anything.
2564
2296
  *
2565
2297
  * A single-statement CAS on the wedged statuses, on the settlement pool —
2566
2298
  * there is no ledger effect to be atomic with, which is the whole point:
@@ -2580,7 +2312,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2580
2312
  * Mark that the next operation is the ambiguous external-call boundary.
2581
2313
  *
2582
2314
  * `channelAtSubmission` is the live channel the facilitator is about to size
2583
- * and sign the settlement against (internal-review) — bookmarked here, with the
2315
+ * and sign the settlement against — bookmarked here, with the
2584
2316
  * block height, because these two are the only description of that instant
2585
2317
  * that survives the call.
2586
2318
  */
@@ -2602,12 +2334,11 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2602
2334
  /**
2603
2335
  * Mark the external settlement and ledger effect complete in the active transaction.
2604
2336
  *
2605
- * `resolvedAtMs` is set only by the operator repair (internal-review), so an audit
2337
+ * `resolvedAtMs` is set only by the operator repair, so an audit
2606
2338
  * read can tell a settlement that completed on its own from one a human had
2607
2339
  * to finish.
2608
2340
  */
2609
2341
  completeSettlement(settlementId: string, resolvedAtMs?: number): Promise<void>;
2610
- /** Load one channel snapshot. */
2611
2342
  get(channelId: string): Promise<Channel | undefined>;
2612
2343
  /** List all channel snapshots in stable channel-id order. */
2613
2344
  list(): Promise<Channel[]>;
@@ -2629,7 +2360,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2629
2360
  * this is how the settlement layer says "this exact decrease is the one the
2630
2361
  * payer's verified refund voucher authorises". Outside this scope a decrease
2631
2362
  * of `chargedCumulativeAmount` is refused flat, which is what keeps the
2632
- * arbitrary-decrease guard meaningful (internal-review).
2363
+ * arbitrary-decrease guard meaningful.
2633
2364
  */
2634
2365
  withSanctionedRefundBase<T>(base: string, operation: () => Promise<T>): Promise<T>;
2635
2366
  withChannelTransaction<T>(channelId: string, operation: (tx: PoolClient) => Promise<T>): Promise<T>;
@@ -2639,7 +2370,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2639
2370
  * The lock is session-scoped, so its client stays checked out for the whole
2640
2371
  * operation — and `operation()` (upstream's claim/settle) re-enters the pool
2641
2372
  * through {@link updateChannel}. Two bounds keep that from wedging the pool
2642
- * (internal-review): local callers are serialized in-process, so this holds at most
2373
+ * local callers are serialized in-process, so this holds at most
2643
2374
  * one client per process no matter how many requests arrive, and a waiting
2644
2375
  * acquire carries a `lock_timeout` so a wedged fleet-mate can't pin that
2645
2376
  * client — and the caller's request — indefinitely. A timeout reports the
@@ -2652,7 +2383,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2652
2383
  */
2653
2384
  withFleetSettlementLock(operation: () => Promise<unknown>, wait?: boolean): Promise<boolean>;
2654
2385
  /**
2655
- * Hold the fleet-wide self-relay submission lock for one chain submission (internal-review).
2386
+ * Hold the fleet-wide self-relay submission lock for one chain submission.
2656
2387
  *
2657
2388
  * The session lock keeps one per-DVM gas EOA to one in-flight submission
2658
2389
  * across the whole fleet — two machines submitting at once collide on the
@@ -2683,7 +2414,6 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
2683
2414
  private updateInTransaction;
2684
2415
  }
2685
2416
 
2686
- /** Durable channel seam used to compose a settled draw with consumed Tempo value. */
2687
2417
  interface TempoSessionSettlementStore {
2688
2418
  withDrawPlacement<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle$1) => Promise<value>, tx?: CreditLedgerQuerier): Promise<value>;
2689
2419
  withTerminalConsumption<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle$1) => Promise<{
@@ -2692,7 +2422,6 @@ interface TempoSessionSettlementStore {
2692
2422
  consume: boolean;
2693
2423
  }>): Promise<value>;
2694
2424
  }
2695
- /** Tempo channel state read while holding its durable row lock. */
2696
2425
  interface TempoSessionLifecycle$1 {
2697
2426
  finalized: boolean;
2698
2427
  closeRequestedAt: bigint;
@@ -2708,7 +2437,6 @@ interface TempoCreditLossEvidence {
2708
2437
  consumedNative: bigint;
2709
2438
  observedAt?: number;
2710
2439
  }
2711
- /** Immutable audit record for one terminal, unbacked Tempo credit. */
2712
2440
  interface TempoCreditLoss {
2713
2441
  creditId: string;
2714
2442
  channelId: string;
@@ -2728,7 +2456,6 @@ interface X402CreditLossEvidence {
2728
2456
  totalClaimedNative: bigint;
2729
2457
  observedAt?: number;
2730
2458
  }
2731
- /** Immutable audit record for one terminal, unbacked x402 credit. */
2732
2459
  interface X402CreditLoss {
2733
2460
  creditId: string;
2734
2461
  channelId: string;
@@ -2741,300 +2468,115 @@ interface X402CreditLoss {
2741
2468
  observedAt: number;
2742
2469
  }
2743
2470
  /**
2744
- * Money-safe credit ledger (internal-review) — the SDK-owned settlement primitive the
2745
- * credits spec (§2 condition 2) requires every DVM to use for prepaid balances.
2746
- * Two tables in the DVM's own Postgres (`credits`, `credit_draws`), two-phase
2747
- * draws (`pending` at job-accept → `settled` on success / `released` on
2748
- * failure), and a per-credit gap-free `ledger_seq` for the receipt evidence
2749
- * chain (`ReceiptCredit` in `lib/receipt.ts`).
2750
- *
2751
- * Concurrency contract: **every write to `credit_draws` happens inside a
2752
- * transaction that first takes `SELECT … FOR UPDATE` on the parent `credits`
2753
- * row.** The pending-hold SUM that defines available balance is only stable
2754
- * because of that lock — never add a `credit_draws` write path that skips it.
2755
- * `ctx.store` / KV is prohibited for credit state: last-write-wins KV on a
2756
- * multi-machine fleet loses money under concurrency (spec condition 2).
2471
+ * Every write to credit_draws takes SELECT … FOR UPDATE on its parent credit within the same
2472
+ * transaction. That lock keeps the pending-hold sum and available balance stable across
2473
+ * machines; KV storage cannot provide this money boundary.
2757
2474
  *
2758
- * Scope note: the ledger is host-wide, keyed only by `credit_id` /
2759
- * `caller_pubkey`. A multi-mount host shares one ledger across its mounts —
2760
- * same operator, same money destination, so not a money hazard; a `dvm_id`
2761
- * column can be added by idempotent migration if per-mount ledgers are ever
2762
- * needed.
2475
+ * The ledger is host-wide: credit_id and caller_pubkey identify rows, without a per-mount
2476
+ * dvm_id. Draws hold funds until settlement or release, and share a gap-free per-credit
2477
+ * ledger_seq with drains.
2763
2478
  */
2764
2479
  declare class CreditLedger {
2765
2480
  private readonly pool;
2766
2481
  private readonly tempoSessionStore?;
2767
2482
  /**
2768
2483
  * Postgres-backed: balances survive a restart and are shared across the
2769
- * fleet, so this DVM may advertise credit (internal-review).
2484
+ * fleet, so this DVM may advertise credit.
2770
2485
  */
2771
2486
  readonly durable = true;
2772
2487
  private x402Settlements?;
2773
2488
  private expiryReleaseOutbox?;
2774
2489
  constructor(pool: CreditLedgerPool, tempoSessionStore?: TempoSessionSettlementStore | undefined);
2775
2490
  /**
2776
- * Bind the durable x402 settlement state this ledger gates spending on
2777
- * (internal-review).
2778
- *
2779
- * Late, because the settlement store is built with the batch-settlement
2780
- * server, which is built *after* the ledger it reads earned draws from. An
2781
- * unbound ledger simply does not gate — correct for a DVM with no x402
2782
- * channel storage, where no settlement row can exist to wedge.
2491
+ * Bound after construction because the settlement store shares this ledger and is
2492
+ * initialized separately. Ledger effects and route preflights use the same refund gate.
2783
2493
  */
2784
2494
  useX402SettlementGate(gate: X402RefundSettlementGate): void;
2785
2495
  /**
2786
- * Bind the durable outbox that reports credit-expiry releases and their
2787
- * revivals to the platform (internal-review).
2788
- *
2789
- * Held on the ledger rather than threaded through {@link fund} for the same
2790
- * reason as the gate above: the revival half fires from *every* funding path
2791
- * — Cashu commit, x402 exact and channel, Tempo, the Lightning invoice
2792
- * settle, the implicit N=1 per-call payment — and a seam each of those has to
2793
- * remember to pass is a seam one of them will eventually forget.
2794
- *
2795
- * Unbound (a `dvmctl dev` server, a self-hosted builder, a test host) the
2796
- * sweep and the reversal still run and still write the ledger's own rows;
2797
- * only the platform report is skipped. Reporting is bookkeeping and must
2798
- * never gate the ledger.
2496
+ * Expiry release and revival reports join the ledger transaction. A caller-owned funding
2497
+ * transaction must not commit a reversal independently of its outbox row. Without this
2498
+ * wiring, ledger transitions still run.
2799
2499
  */
2800
2500
  useCreditExpiryReleaseOutbox(outbox: CreditExpiryReleaseOutbox): void;
2801
- /** Create the `credits` / `credit_draws` tables if absent. Call once at SDK boot. */
2802
2501
  init(): Promise<void>;
2803
- /** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
2804
2502
  private createTables;
2805
2503
  /**
2806
- * Give every open non-channel Bitcoin credit a funding lot (internal-review).
2807
- *
2808
- * Runs inside the boot DDL, is guarded per credit by `NOT EXISTS`, and is
2809
- * therefore both the one-time migration and a standing self-heal for a
2810
- * credit that somehow reaches a positive balance with no lot behind it.
2811
- *
2812
- * The lot is derived from `credits.msats_remaining`, not replayed out of
2813
- * `credit_fundings`. That is not a shortcut: `credit_fundings` records only
2814
- * `{amount_micro, rail}` — no instrument amount at all — and is not written
2815
- * on every funding path, whereas `msats_remaining` **is** the credit's
2816
- * current unspent rail value, maintained pro rata on every draw since
2817
- * internal-review. One synthetic lot pairing it with `balance_micro` therefore
2818
- * reproduces today's in-kind position exactly, and FIFO over a single lot is
2819
- * trivially correct. Historic per-funding rates are unrecoverable and would
2820
- * change nothing: only the remaining position is owed.
2821
- *
2822
- * The semantics change is deliberately retroactive (internal-review) — every
2823
- * current holder is first-party, so there is one regime and no legacy
2824
- * branch.
2504
+ * Backfill an open non-channel Bitcoin credit only when it has no funding lot. Pair
2505
+ * balance_micro with current msats_remaining; credit_fundings lacks historical instrument
2506
+ * amounts and does not cover every funding path. Historical per-funding rates cannot be
2507
+ * recovered. Existing lots are left untouched, so this does not repair partial coverage
2508
+ * gaps.
2825
2509
  */
2826
2510
  private backfillFundingLots;
2827
2511
  /**
2828
- * Create a credit, or top up an existing one (same `creditId`). Top-ups add
2829
- * to the balance and overwrite `expiry_ms` with the provided value — funding
2830
- * an expired credit revives it (spec §5 carry-forward: the credit is a
2831
- * rolling buffer, not per-job escrow). The existing row's `caller_pubkey`
2832
- * and `currency` must match or the fund is refused with a typed error —
2833
- * without that check a top-up against someone else's `credit_id` would
2834
- * silently merge two callers' money.
2512
+ * Funding adds balance and overwrites expiry, reviving an expired credit. It is additive,
2513
+ * not idempotent: the caller’s rail marker or funding record must prevent replay. Owner and
2514
+ * currency must match; rail and reusable-channel bindings cannot be mixed with other
2515
+ * instruments. A legacy NULL rail adopts its first typed top-up.
2835
2516
  *
2836
- * Pass `tx` (a client inside a caller-owned `BEGIN`) to commit the rail
2837
- * receive and the ledger credit atomically (spec condition 3 — the internal-review
2838
- * verifier does this). The ledger issues **no** transaction control on `tx`.
2517
+ * With tx, all writes join the caller’s transaction without transaction control. Otherwise
2518
+ * the ledger owns a transaction covering balance, funding lot, reversal of standing expiry
2519
+ * releases and their reports. A partial revival would leave restored balance with a release
2520
+ * still counted.
2839
2521
  *
2840
- * Without `tx` it opens one of its own, because a funding is no longer a
2841
- * single statement: it upserts the credit, records its funding lot
2842
- * (internal-review), and reverses any standing expiry release (internal-review) — and that
2843
- * last leg restores balance and queues a report. A crash between the upsert
2844
- * and the reversal would leave a revived credit whose release still stands,
2845
- * which the sweep's own exclusion then makes permanent: `balance_micro > 0`
2846
- * but a standing release means it is neither drainable nor re-releasable.
2847
- *
2848
- * `basis` records the rail value behind the fiat (internal-review) so each draw can
2849
- * be allocated its share of the rail-native amount actually received. A
2850
- * top-up on a different rail than the credit's is refused with
2851
- * `rail_mismatch`: sats and USDC microunits aren't summable, so a blended
2852
- * credit would have no coherent native basis to allocate from.
2853
- *
2854
- * It is **required** (internal-review), in the type and again at runtime via
2855
- * {@link assertFundingBasis}. A basis-less fund wrote `rail = NULL`, and a
2856
- * draw against such a credit settles — a real debit — while
2857
- * `railAsFundingMethod` correctly declines to guess a rail, so `bookRevenue`
2858
- * books nothing and outstanding liability (`deposits − draw revenue`) stays
2859
- * overstated by the drawn amount forever. `credits.rail` stays nullable
2860
- * because rows written before this change still exist in live databases:
2861
- * **write is closed, read is not.** A legacy row adopts a rail from its first
2862
- * typed top-up via the `COALESCE` below.
2863
- *
2864
- * The rail-native **instrument** is pinned at first funding too, and both
2865
- * channel guards are symmetric for the same reason (internal-review). A reusable
2866
- * channel — Tempo session or x402 batch-settlement — may only ever top up
2867
- * the credit it opened, and a credit opened by a one-shot (a Tempo charge, an
2868
- * x402 exact authorization) may never adopt one. The x402 half used to admit
2869
- * `credits.x402_channel_id IS NULL`, so a channel deposit bound itself to a
2870
- * credit already holding exact-funded value: `earnedNativeForX402Channel`
2871
- * then read that credit's whole settled-draw history as the channel's
2872
- * earnings, and the first claim tick swept a deposit nobody had consumed,
2873
- * while the unspent balance lost every exit (the drain's refund base goes
2874
- * below `totalClaimed`, and a channel-bound credit refuses every non-x402
2875
- * drain). `credits_x402_channel_uidx` does not cover this: it guards
2876
- * one-credit-per-channel, not one-source-per-credit.
2877
- *
2878
- * An x402 channel deposit is additionally refused `settlement_pending` while
2879
- * that channel carries an unresolved refund (internal-review) — see the gate read
2880
- * below. This method is the authority on that rule, as it is on the binding
2881
- * rules above; every door preflights it where a refusal is still free.
2522
+ * An incoming x402 channel with an unresolved refund is refused before the upsert. That
2523
+ * preflight is not atomic with a concurrent cooperative drain; see the incoming-channel
2524
+ * gate below.
2882
2525
  */
2883
2526
  fund(args: CreditFundArgs): Promise<CreditSnapshot>;
2884
- /**
2885
- * The funding itself, on whichever handle {@link fund} chose. Every refusal
2886
- * it raises is the caller's to see unchanged; a self-opened transaction rolls
2887
- * back around it.
2888
- */
2889
2527
  private fundOn;
2890
2528
  /**
2891
- * Undo any expiry release this credit still carries, because a funding just
2892
- * landed on it (internal-review — the revival rule), restoring the balance the
2893
- * release took.
2529
+ * Funding reverses standing expiry releases, restores their released micro and queues
2530
+ * reversal reports on the funding transaction. Do this even when the new expiry is already
2531
+ * past: the next sweep must release the enlarged remainder as a new episode.
2894
2532
  *
2895
- * A release records the credit's **final** undrawn remainder and zeroes it
2896
- * (see {@link releaseExpiredCreditLocked}). Funding adds to the balance and
2897
- * overwrites `expiry_ms` (spec §5 carry-forward: the credit is a rolling
2898
- * buffer), so the moment money arrives the recorded remainder is no longer
2899
- * final: the release stops counting and its micro come back.
2900
- *
2901
- * Deliberately unconditional on the incoming expiry rather than gated on
2902
- * `expiryMs > nowMs`. A release exists only for a credit that had already
2903
- * lapsed, so any funding that reaches one is money the release did not
2904
- * account for — and a funding that arrives with an already-past expiry leaves
2905
- * a credit the very next sweep re-releases at its new, larger remainder. One
2906
- * rule, self-healing in both directions, and it never needs to read the
2907
- * pre-funding expiry the upsert has already overwritten.
2908
- *
2909
- * Runs on the funding's own handle — which {@link fund} now guarantees is a
2910
- * transaction — so the reversal, the restored balance and the report commit
2911
- * with the money or not at all. A reversal that committed without its report
2912
- * would be unrecoverable: `WHERE reversed_at IS NULL` means no later pass
2913
- * re-derives it.
2914
- *
2915
- * @returns the credit row as the restore left it, or undefined when there was
2916
- * nothing to reverse. The caller reads its snapshot off this rather than off
2917
- * the funding upsert's `RETURNING`, which predates the restore.
2533
+ * Returns the row after restoration, or undefined when no release stood. The upsert’s
2534
+ * RETURNING row predates the restoration and is not the final balance.
2918
2535
  */
2919
2536
  private reverseExpiryReleases;
2920
2537
  /**
2921
- * Release the undrawn remainder of every credit whose TTL has run out
2922
- * (internal-review). Returns the releases this pass recorded.
2923
- *
2924
- * **What is released.** The credit's whole `balance_micro`. Expiry ends
2925
- * spending but never ownership of the record, so the balance stays readable;
2926
- * what ends is the caller's claim on it, and on the prepaid rails the value
2927
- * behind it is already in builder custody.
2928
- *
2929
- * **Tempo is excluded**, at both of its markers (`tempo_channel_id` and a
2930
- * `tempo` rail). A Tempo credit is channel-backed and its undrawn value
2931
- * returns to the *caller* at channel exit, so releasing it would book the
2932
- * builder money the chain is about to hand back.
2538
+ * Release expired undrawn balances, retaining readable credit records. Tempo rail or
2539
+ * channel-backed credits are excluded because their collateral returns to the caller
2540
+ * through the channel lifecycle.
2933
2541
  *
2934
- * **A credit with a pending hold is skipped, not partially released.** A draw
2935
- * placed before expiry stays settleable afterwards (`draw`'s replay lookup
2936
- * runs above the expiry check, so a lost response is still recoverable), so
2937
- * the remainder is not final while a hold is outstanding. Skipping costs one
2938
- * sweep interval and keeps the released figure exactly "what nothing bought";
2939
- * holds do resolve — the orphan-draw watchdog (internal-review) is what guarantees
2940
- * a stranded one still reaches a terminal state.
2941
- *
2942
- * **Idempotent** two ways. `release_id` is derived from the expiry instant,
2943
- * so a re-sweep after a crash between the insert and its report collides on
2944
- * the same primary key instead of writing a second row; and the candidate
2945
- * scan excludes any credit that already has an un-reversed release.
2946
- *
2947
- * Each candidate is re-checked under the credit row lock every
2948
- * `credit_draws` write is taken under, so a draw racing the sweep either
2949
- * loses the race (its credit is already released and it would have been
2950
- * refused `credit_expired` anyway) or wins it and leaves a pending hold the
2951
- * locked re-check sees.
2542
+ * Pending holds prevent release: jobs accepted before expiry may still settle or release.
2543
+ * Candidates are rechecked under the same parent-credit lock used by draw writes. The
2544
+ * balance debit, episode record and report commit together; standing releases exclude
2545
+ * repeat work. A later revival may reuse the expiry instant but receives a new release
2546
+ * sequence.
2952
2547
  */
2953
2548
  sweepExpiredCredits(args?: {
2954
2549
  nowMs?: number;
2955
- /** Credits released per pass. Bounds the work a long-idle DVM does at boot. */
2956
2550
  limit?: number;
2957
2551
  }): Promise<{
2958
2552
  released: CreditExpiryRelease[];
2959
2553
  }>;
2960
2554
  /**
2961
- * Write one credit's expiry release under its row lock, zero the balance it
2962
- * released, and re-assert every condition the unlocked candidate scan tested.
2963
- * Returns undefined when the credit no longer qualifies — funded, drawn
2964
- * against, or already released between the scan and the lock.
2965
- *
2966
- * **Zeroing the balance is what makes the release real**, and it is the whole
2967
- * reason this runs under the lock rather than as a bare INSERT. The row alone
2968
- * records that the caller's claim ended; it does not *end* it. `requestDrain`
2969
- * has no expiry check by design (expiry gates new draws, not reclaims), so a
2970
- * credit whose remainder had been released and reported was still fully
2971
- * drainable: the DVM would pay out money the platform had already booked as
2972
- * the builder's, and `deposited - drawn - drained - released` would go
2973
- * negative on the same micro. With the balance at zero, `requestDrain`'s
2974
- * existing `availableMicro <= 0` guard refuses with `nothing_to_drain` and no
2975
- * new check is needed anywhere. {@link reverseExpiryReleases} puts the
2976
- * balance back when a funding revives the credit.
2555
+ * Recheck eligibility under the credit lock and zero the released balance in the same
2556
+ * transaction as its episode/report. Otherwise requestDrain, which intentionally ignores
2557
+ * expiry, could reclaim value already released to the builder.
2977
2558
  *
2978
- * `status` deliberately stays `active`: a released credit must remain
2979
- * fundable, or the revival rule has nothing to revive.
2980
- *
2981
- * The zeroed balance also takes the credit out of `BITCOIN_CREDIT_PREDICATE`
2982
- * (`balance_micro > 0`), so the in-kind sweep floor stops counting sats the
2983
- * builder no longer owes back — and counts them again after a revival, with
2984
- * the credit's funding lots untouched throughout.
2559
+ * Keep status active so future funding can revive it. Funding lots remain intact: balance
2560
+ * zero excludes this credit from Bitcoin liability, and reversal restores that liability.
2985
2561
  */
2986
2562
  private releaseExpiredCreditLocked;
2987
2563
  /**
2988
- * Record this funding's in-kind basis as a lot (internal-review).
2989
- *
2990
- * Called from inside {@link fund}, on the same handle, so the lot shares
2991
- * whatever transaction the rail opened — `withCommitTx` for Cashu,
2992
- * `ProcessedPaymentStore.withTx` for x402/Tempo, `settleInvoice`'s own
2993
- * `BEGIN` for Lightning. The funding-atomicity invariant therefore extends
2994
- * to it structurally rather than by every call site remembering: a lot
2995
- * cannot exist without its ledger credit, or the credit without its lot.
2996
- *
2997
- * Only a **channel-backed** credit writes no lot: it is in-kind already —
2998
- * the escrow itself retains the unused value — so a lot on one would be
2999
- * counted as Bitcoin deposit liability the hub does not hold.
2564
+ * Record non-channel Bitcoin funding lots on the funding transaction. An implicit payment
2565
+ * also needs a lot: its draw is initially only a hold, and failure releases the whole
2566
+ * balance for reclaim. Channel collateral is excluded from Bitcoin lots.
3000
2567
  *
3001
- * Every other non-channel Bitcoin funding writes one, the implicit N=1
3002
- * per-call payment included. That one funds and draws the same amount in
3003
- * this transaction, so it looks like a lot born depleted — but the draw is a
3004
- * *pending hold*, not a debit, and only a `completed` job settles it
3005
- * (`JobManager.settlesDraw`). A failed, cancelled or stale-swept job
3006
- * releases the hold and the whole funded amount becomes reclaimable balance,
3007
- * so suppressing the lot here would leave routine traffic reclaiming off a
3008
- * live rate and missing from the sweep floor. One small insert on the hot
3009
- * path buys the invariant the liability read depends on: open lots cover
3010
- * `balance_micro` for every Bitcoin credit, always.
3011
- *
3012
- * A zero-sats basis still writes a lot. It is honest — the credit exists and
3013
- * we know its rail — and it is what makes "covered but unbacked" a
3014
- * distinguishable state from "no lot at all" for the reclaim and the
3015
- * liability rollup, instead of both reading as a gap.
2568
+ * A zero-sats basis still records a lot, distinguishing covered but basisless credit from a
2569
+ * missing-lot coverage gap.
3016
2570
  */
3017
2571
  private recordFundingLot;
3018
2572
  /**
3019
- * Place a two-phase `pending` hold of `amountMicro` against the credit and
3020
- * assign the next `ledger_seq`. Idempotent on `(creditId, drawId)`: a replay
3021
- * returns the original `{balanceAfterMicro, ledgerSeq}` with
3022
- * `replayed: true` and places no second hold — this is how a caller recovers
3023
- * a lost response (spec condition 1). A replay whose `amountMicro` or
3024
- * `jobId` differs from the recorded draw is not a replay but a money bug in
3025
- * the caller, refused with `draw_conflict`.
3026
- *
3027
- * The replay lookup runs **before** the expiry check, deliberately: a draw
3028
- * placed pre-expiry must stay recoverable after the credit expires, or the
3029
- * DVM holds a debit the caller can never reconcile. Fresh draws on an
3030
- * expired credit are refused with `credit_expired`; the balance stays
3031
- * intact and readable (spec §5: expiry ends spending, never ownership).
2573
+ * Place a pending hold with the next shared per-credit sequence. A matching (creditId,
2574
+ * drawId) replay returns its recorded tuple without another hold; conflicting amount or
2575
+ * jobId is draw_conflict.
3032
2576
  *
3033
- * Pass `tx` (a client inside a caller-owned `BEGIN`) to place the hold in
3034
- * the same transaction as the rail commit and the `fund` upsert (spec
3035
- * condition 3 — the internal-review implicit N=1 path). The ledger issues no
3036
- * transaction control on `tx`; the `SELECT … FOR UPDATE` row lock is still
3037
- * taken on the caller's transaction, so the locking invariant holds.
2577
+ * Replay precedes expiry and settlement gates so a lost response stays recoverable. Fresh
2578
+ * draws require spendable credit. With tx, take the parent-credit lock on the caller’s
2579
+ * transaction without issuing transaction control.
3038
2580
  */
3039
2581
  draw(args: {
3040
2582
  creditId: string;
@@ -3047,63 +2589,23 @@ declare class CreditLedger {
3047
2589
  nowMs?: number;
3048
2590
  }): Promise<DrawResult>;
3049
2591
  /**
3050
- * The unresolved refund on `channelId` that must stop this ledger effect, if
3051
- * any (internal-review).
3052
- *
3053
- * `undefined` whenever there is nothing to ask — an unbound credit, or a DVM
3054
- * with no durable settlement store, where a settlement row cannot exist.
3055
- *
3056
- * Public since internal-review, so `/v1/credit`'s pre-payment preflight can ask the
3057
- * same question `fund` will ask from inside the rail transaction — one read,
3058
- * one answer, rather than a second copy of the rule in the routes.
2592
+ * Return the unresolved refund that blocks an effect on this channel, or undefined. Route
2593
+ * preflights use the same rule before moving money; draw and drain also read it under the
2594
+ * credit lock.
3059
2595
  */
3060
2596
  blockingX402Refund(channelId: string | null | undefined, statuses: X402SettlementStatus[]): Promise<X402UnresolvedRefund | undefined>;
3061
2597
  /**
3062
- * Grow a pending draw by `addAmountMicro` (internal-review) — the ledger half of a
3063
- * mid-job `requestPayment` top-up. One job keeps **one** draw: the mid-job
3064
- * money funds the same credit and enlarges the hold the upfront leg placed,
3065
- * so the receipt's `ReceiptCredit` block countersigns the job's full cost
3066
- * and `revenue_events` still books exactly one row per job.
3067
- *
3068
- * `draw()` cannot express this: a same-`draw_id` call with a different
3069
- * amount is `draw_conflict` by design (a replayed draw must never re-price).
2598
+ * Grow an accepted job’s pending hold while preserving its original ledger_seq. The balance
2599
+ * tuple is updated at that sequence, so interleaved draws need not form a monotonic balance
2600
+ * trajectory. No expiry check applies to an already accepted job.
3070
2601
  *
3071
- * The arithmetic is `draw()`'s, unchanged: `addAmountMicro` is checked
3072
- * against — and `balance_after_micro` recomputed from — the balance net of
3073
- * **every** pending hold, this draw's own included. That is deliberately the
3074
- * conservative reading (a fresh draw of `addAmountMicro` would see exactly
3075
- * the same figure), and it is what keeps the total held after growth inside
3076
- * the credit's balance.
2602
+ * Available balance excludes every pending hold, including this one. An earmark allocates
2603
+ * the incoming funding’s rail value; absent one, growth uses the pool.
3077
2604
  *
3078
- * The increment's rail value comes from {@link growthRailValue}: earmarked
3079
- * to the funding that backs it when the caller names one (the mid-job case),
3080
- * pro-rata otherwise. Either way the draws of a credit keep summing to
3081
- * precisely what the rails paid (internal-review).
3082
- *
3083
- * **`ledger_seq` is not re-taken.** The per-credit sequence is gap-free
3084
- * (`credit_draws_seq_uidx`), so moving this draw forward would strand its
3085
- * original number. The consequence is deliberate and worth knowing: the
3086
- * recorded `balance_after_micro` is the post-growth figure stamped at the
3087
- * *original* seq, so a credit with interleaved sibling activity has a
3088
- * trajectory that is truthful per draw rather than monotonic across seqs.
3089
- *
3090
- * **No expiry check**, matching `settle`/`release`: the job was accepted
3091
- * pre-expiry and is still running. Refusing here would strand the handler
3092
- * mid-flight over a clock the caller can't influence.
3093
- *
3094
- * **Not idempotent by itself.** Growth carries no key of its own — it
3095
- * inherits the rail's (the accumulator's `(dvm_id, request_id)` UNIQUE rolls
3096
- * the whole transaction back on replay; x402/mpp collide on the
3097
- * `processed_payments` marker). Never call it outside a rail commit.
3098
- *
3099
- * **`capMicro` bounds cumulative growth at what the job cumulatively asked
3100
- * (internal-review)**, and it is the only ceiling that can refuse a top-up: the
3101
- * available-balance check cannot, because the `fund` a moment earlier in
3102
- * this same transaction raised the balance by exactly the amount being
3103
- * drawn. Excess is **granted partially or not at all rather than thrown** —
3104
- * this runs inside the rail commit, so a throw would roll back cashu proofs
3105
- * the caller already sent. The refused remainder stays funded balance the
3106
- * caller owns and can drain.
2605
+ * Growth has no independent replay key: compose it with the rail’s replay protection in one
2606
+ * transaction. capMicro bounds cumulative growth at the job’s cumulative ask. Excess
2607
+ * remains caller-owned funded balance; partial or zero growth returns that excess rather
2608
+ * than throwing and rolling back accepted proofs.
3107
2609
  */
3108
2610
  growDraw(args: {
3109
2611
  creditId: string;
@@ -3111,7 +2613,7 @@ declare class CreditLedger {
3111
2613
  addAmountMicro: number;
3112
2614
  /**
3113
2615
  * Rail value that funded this growth, earmarked to the increment instead
3114
- * of allocated pro-rata (internal-review). Pass it whenever `addAmountMicro` is
2616
+ * of allocated pro-rata. Pass it whenever `addAmountMicro` is
3115
2617
  * money that landed in this same transaction; omit it when the growth
3116
2618
  * spends balance the credit already held.
3117
2619
  */
@@ -3121,7 +2623,7 @@ declare class CreditLedger {
3121
2623
  };
3122
2624
  /**
3123
2625
  * Ceiling on this draw's **cumulative** growth, in fiat micro of the
3124
- * credit's currency (internal-review) — the job's total mid-job ask. Growth is
2626
+ * credit's currency — the job's total mid-job ask. Growth is
3125
2627
  * clipped to `capMicro - grown_micro`; a second payment for an ask the
3126
2628
  * first already satisfied gets zero. Omit it only when the caller genuinely
3127
2629
  * has no ask total to bound against; growth is then unbounded, as before.
@@ -3155,7 +2657,7 @@ declare class CreditLedger {
3155
2657
  }): Promise<DrawResolution>;
3156
2658
  /**
3157
2659
  * Release a pending draw: the hold evaporates, the balance is untouched —
3158
- * this is how "no debit on job failure" is mechanically real (spec §1).
2660
+ * this is how "no debit on job failure" is mechanically real (the credit lifecycle contract).
3159
2661
  * Idempotent: releasing a released draw returns the original tuple. A
3160
2662
  * settled draw cannot be released (`invalid_draw_state`) — un-settling
3161
2663
  * booked revenue is a reconciliation problem, not a ledger verb.
@@ -3167,18 +2669,9 @@ declare class CreditLedger {
3167
2669
  nowMs?: number;
3168
2670
  }): Promise<DrawResolution>;
3169
2671
  /**
3170
- * Record a fund-only top-up under its client-generated `fundId` (internal-review).
3171
- * The deposit half of the evidence chain, and the top-up path's idempotency
3172
- * key: `PRIMARY KEY (credit_id, fund_id)` means a concurrent duplicate
3173
- * loses with a typed `funding_replayed` rather than crediting twice.
3174
- *
3175
- * Call it inside the same `tx` as {@link fund} — the rail commit, the
3176
- * funding record, and the balance increment must land together or not at
3177
- * all (spec §2 condition 3). Like `fund`, this issues no transaction
3178
- * control on `tx`; it is a single statement.
3179
- *
3180
- * This does **not** move money on its own. `fund` still does the crediting;
3181
- * this row is what lets a retry be answered with the original outcome.
2672
+ * Record the fund-only replay key without moving money. A duplicate (credit_id, fund_id)
2673
+ * raises funding_replayed. Compose this insert, fund and rail acceptance on the same
2674
+ * caller-owned transaction; a retry reads the recorded funding outcome.
3182
2675
  */
3183
2676
  recordFunding(args: {
3184
2677
  creditId: string;
@@ -3216,7 +2709,7 @@ declare class CreditLedger {
3216
2709
  receipt: FundingReceipt;
3217
2710
  }): Promise<FundingReceipt>;
3218
2711
  /**
3219
- * Record the bolt11 issued for a `(creditId, fundId)` top-up (internal-review), or
2712
+ * Record the bolt11 issued for a `(creditId, fundId)` top-up, or
3220
2713
  * return the one already issued for it.
3221
2714
  *
3222
2715
  * **Returning the existing row is the point.** A caller re-polling an unpaid
@@ -3237,7 +2730,6 @@ declare class CreditLedger {
3237
2730
  expiresAt: number;
3238
2731
  nowMs?: number;
3239
2732
  }): Promise<CreditInvoiceRecord>;
3240
- /** Read one invoice (no lock). */
3241
2733
  getInvoice(args: {
3242
2734
  creditId: string;
3243
2735
  fundId: string;
@@ -3253,35 +2745,22 @@ declare class CreditLedger {
3253
2745
  limit: number;
3254
2746
  }): Promise<CreditInvoiceRecord[]>;
3255
2747
  /**
3256
- * Apply an observed Lightning settlement to the ledger — **the exactly-once
3257
- * boundary** (internal-review; spec §2 condition 3 for a rail whose commit happens
3258
- * off-box).
3259
- *
3260
- * One transaction covers the funding record, the balance, and the invoice's
3261
- * own status flip, so the three can never disagree. A crash anywhere inside
3262
- * leaves the invoice `pending` and the balance untouched: the next
3263
- * `lookup_invoice` observes the same settled payment and credits it, once.
3264
- * A replayed check after the commit sees `settled` and moves nothing.
3265
- *
3266
- * The lock is on the **invoice** row, not the credit row: the first
3267
- * Lightning top-up is what opens the credit, so there is frequently no
3268
- * `credits` row to take `FOR UPDATE` yet. That does not weaken the ledger's
3269
- * concurrency contract — no `credit_draws` write happens here, and `fund`'s
3270
- * upsert is a single statement — while still serialising two settlement
3271
- * checks racing on the same invoice.
2748
+ * Serialize settlement on the invoice row, which exists before the first credit does.
2749
+ * Funding record, balance, invoice status and optional deposit outbox commit together. An
2750
+ * interrupted transaction leaves the prior invoice status intact for retry; a settled replay
2751
+ * moves no money.
3272
2752
  */
3273
2753
  settleInvoice(args: {
3274
2754
  creditId: string;
3275
2755
  fundId: string;
3276
2756
  expiryMs: number;
3277
2757
  /**
3278
- * Rail-value basis, pinned when the invoice was issued (internal-review). Required
3279
- * by internal-review — it is what `fund` writes to `credits.rail`.
2758
+ * Funding rail value pinned when this invoice was issued.
3280
2759
  */
3281
2760
  basis: CreditFundingBasis;
3282
2761
  /**
3283
2762
  * Reporter-owned deposit outbox write. Runs on the invoice transaction
3284
- * after the ledger/status writes and before COMMIT (internal-review).
2763
+ * after the ledger/status writes and before COMMIT.
3285
2764
  */
3286
2765
  depositOutbox?: {
3287
2766
  enqueue: CreditDepositEnqueue;
@@ -3300,39 +2779,20 @@ declare class CreditLedger {
3300
2779
  fundId: string;
3301
2780
  }): Promise<CreditInvoiceRecord | undefined>;
3302
2781
  /**
3303
- * Retire an invoice that **was paid** and can never be credited (internal-review).
3304
- *
3305
- * Distinct from `expired` because the money is the opposite way round: an
3306
- * expired invoice was never paid and owes nobody anything, whereas a blocked
3307
- * one has sats sitting in the builder's wallet with no ledger row to put them
3308
- * on. Only an operator can resolve that, so the row records `reason` and
3309
- * stops being swept — leaving it `pending` would spend one of the sweep's few
3310
- * slots re-deriving the same verdict on every request the caller makes, and
3311
- * three of them would wedge that caller's sweep so a genuinely payable
3312
- * invoice behind them never credits.
3313
- *
3314
- * A CAS on `pending`, like the expiry retirement: a settlement observed
3315
- * concurrently must win.
2782
+ * Mark a paid invoice blocked when its payment cannot be credited. This removes it from the
2783
+ * bounded pending sweep and leaves its reason for operator repair. The pending-only CAS
2784
+ * cannot overwrite a committed settlement.
3316
2785
  */
3317
2786
  markInvoiceBlocked(args: {
3318
2787
  creditId: string;
3319
2788
  fundId: string;
3320
2789
  reason: string;
3321
2790
  }): Promise<CreditInvoiceRecord | undefined>;
3322
- /** Read one invoice by the wallet's own identifier for the payment (no lock). */
3323
2791
  getInvoiceByPaymentHash(paymentHash: string): Promise<CreditInvoiceRecord | undefined>;
3324
2792
  /**
3325
- * The operator's queue: invoices the sweep gave up on (internal-review).
3326
- *
3327
- * **Keyset-paginated, not offset.** Blocked rows are terminal and never
3328
- * self-clear, so a row the operator declines to act on sits at the head of
3329
- * the age ordering forever; a fixed first page would starve everything
3330
- * behind it on every run, which is the internal-review shape one page up.
3331
- *
3332
- * `includeResolved` widens to the operator-resolved statuses so a run can be
3333
- * audited after the fact — the acceptance criterion this verb exists for.
3334
- * Host-wide, like `listPendingDrains`: the ledger has no `dvm_id`, so on a
3335
- * multi-mount host one builder's admin credential reads every mount's rows.
2793
+ * Host-wide operator queue, keyset-paginated so an untouched blocked row does not starve
2794
+ * later rows. includeResolved also exposes reconciled and written-off history; the ledger
2795
+ * has no per-mount dvm_id.
3336
2796
  */
3337
2797
  listBlockedInvoices(args: {
3338
2798
  limit: number;
@@ -3340,44 +2800,22 @@ declare class CreditLedger {
3340
2800
  includeResolved?: boolean;
3341
2801
  }): Promise<CreditInvoiceRecord[]>;
3342
2802
  /**
3343
- * Apply a blocked invoice's payment to a credit an operator named — the
3344
- * repair for the one Lightning outcome the DVM cannot fix itself (internal-review).
3345
- *
3346
- * Shaped statement-for-statement on {@link settleInvoice}, because it is the
3347
- * same money doing the same thing a different way: one transaction covering
3348
- * the funding record, the balance, the invoice's status flip and the deposit
3349
- * outbox row, so the four can never disagree. Three deliberate differences:
2803
+ * Apply a blocked or written-off invoice to an operator-named credit. Lock the invoice by
2804
+ * its unique payment_hash before funding the credit, matching settlement’s
2805
+ * invoice-to-credit lock order. Funding, balance, resolution and optional outbox share one
2806
+ * transaction.
3350
2807
  *
3351
- * - **The lock is taken on `payment_hash`**, which is UNIQUE and is the
3352
- * identifier the operator actually holds (it is what the
3353
- * `lightning_settlement_blocked` log carries and what names the payment in
3354
- * their wallet). Invoice row first, credit row second via `fund`'s upsert —
3355
- * the same order `settleInvoice` takes, which is what keeps a reconcile and
3356
- * a concurrent settlement check from deadlocking against each other.
3357
- * - **The funding lands at `(targetCreditId, paymentHash)`, not the invoice's
3358
- * own `(credit_id, fund_id)`.** That key is frequently the reason the row
3359
- * is blocked at all — `funding_replayed_on_cashu` means something else
3360
- * already holds it — and the operator's most natural target is that very
3361
- * credit. The payment hash cannot collide with it, and it makes all three
3362
- * references to this payment agree: `basis.fundingRef`, the deposit's
3363
- * `funding_id`, and the funding row's `fund_id`.
3364
- * - **`written_off` is an accepted input status.** A write-off unwound
3365
- * nothing, so an operator who closed a row by mistake must not need raw SQL
3366
- * against a money table to reopen it.
3367
- *
3368
- * The invoice row's status — never the funding row — is the idempotency
3369
- * source of truth. `fund_id` is caller-chosen and the payment hash is
3370
- * disclosed to them, so a caller *can* squat `(theirCredit, thatHash)` with a
3371
- * top-up on another rail; when they have, `recordFunding` throws
3372
- * `funding_replayed` and that surfaces as its own loud failure rather than
3373
- * being absorbed as "already reconciled".
2808
+ * The repair funding key is (targetCreditId, paymentHash), distinct from the original
2809
+ * caller-chosen fundId. Invoice status determines replay. A caller can still occupy the
2810
+ * repair key with another payment; funding_replayed must remain a refusal, not be absorbed
2811
+ * as already reconciled. Preserve blocked_reason and the wallet’s settlement instant.
3374
2812
  */
3375
2813
  reconcileBlockedInvoice(args: {
3376
2814
  paymentHash: string;
3377
2815
  /** Credit the money lands on. The caller identity comes off the invoice row. */
3378
2816
  targetCreditId: string;
3379
2817
  expiryMs: number;
3380
- /** Rail-value basis, rebuilt from the invoice row by the caller (internal-review). */
2818
+ /** Rail-value basis, rebuilt from the invoice row by the caller. */
3381
2819
  basis: CreditFundingBasis;
3382
2820
  /** The wallet's settlement instant, re-observed by the operator's verb. */
3383
2821
  settledAtMs?: number;
@@ -3388,17 +2826,9 @@ declare class CreditLedger {
3388
2826
  nowMs?: number;
3389
2827
  }): Promise<InvoiceReconciliation>;
3390
2828
  /**
3391
- * Record that an operator reviewed a blocked invoice and chose not to credit
3392
- * it (internal-review) — no ledger effect, purely a queue transition.
3393
- *
3394
- * A CAS on `blocked`, and idempotent: re-running returns the recorded row
3395
- * rather than overwriting the first note. It cannot capture a `reconciled`
3396
- * row, because that one moved money.
3397
- *
3398
- * `written` reports whether *this* call was the transition. The caller has
3399
- * no other way to tell — the row reads identically either way — and the
3400
- * write-off's audit log is the only record of the amount, so a re-emitted
3401
- * one reads as a second decision about the same sats.
2829
+ * Write off an invoice without crediting or moving money. Only the first transition records
2830
+ * the note; its written bit controls the audit event. An operator can later reconcile a
2831
+ * written-off invoice, because write-off did not return its payment.
3402
2832
  */
3403
2833
  writeOffBlockedInvoice(args: {
3404
2834
  paymentHash: string;
@@ -3413,38 +2843,24 @@ declare class CreditLedger {
3413
2843
  getForCaller(callerPubkey: string, opts?: {
3414
2844
  nowMs?: number;
3415
2845
  }): Promise<CreditSnapshot[]>;
3416
- /** Read one draw (no lock), with its credit's rail basis joined in. */
3417
2846
  getDraw(args: {
3418
2847
  creditId: string;
3419
2848
  drawId: string;
3420
2849
  }): Promise<DrawRecord | undefined>;
3421
2850
  /** Read the sole draw bound to a pre-allocated job id; ambiguity fails closed. */
3422
2851
  getDrawByJobId(jobId: string): Promise<DrawRecord | undefined>;
3423
- /** Read every draw linked to a job, including already-resolved rows. */
3424
2852
  listDrawsByJobId(jobId: string): Promise<DrawRecord[]>;
3425
2853
  /**
3426
2854
  * All pending holds on a credit, in `ledger_seq` order (no lock). Read
3427
- * surface for the internal-review sweeper wiring — a worker that dies mid-job
2855
+ * surface for the sweeper wiring — a worker that dies mid-job
3428
2856
  * leaves its hold `pending` until something releases it.
3429
2857
  */
3430
2858
  listPendingDraws(creditId: string): Promise<DrawRecord[]>;
3431
2859
  /**
3432
- * One page of pending holds host-wide placed before `createdBeforeMs`,
3433
- * oldest first (no lock). The orphan sweeper's read surface (internal-review): a
3434
- * draw commits with its `job_id` before the job row is persisted, so a crash
3435
- * or a fail-closed refusal in that window strands a hold nothing can ever
3436
- * resolve — the only release path keys off the `credit_id`/`draw_id` written
3437
- * on the job row.
3438
- *
3439
- * Host-wide like {@link listPendingDrains}, not per-credit: the sweeper has
3440
- * no candidate credit to start from. Age-bounded so it never sees a draw
3441
- * whose job row is merely still in flight.
3442
- *
3443
- * `limit` is required and `after` keyset-paginates: an aged hold whose job
3444
- * row *does* exist is legitimately un-sweepable and stays `pending`
3445
- * indefinitely, so a caller that re-issues the same first page would never
3446
- * see past a `limit`-sized wall of them. Pass the last row's
3447
- * {@link StalePendingDrawCursor} to advance.
2860
+ * Page host-wide pending holds older than createdBeforeMs by (created_at, credit_id,
2861
+ * ledger_seq). Use the same compound key for ordering and continuation, preserving
2862
+ * same-millisecond rows. Terminal resolution still rechecks state under the parent-credit
2863
+ * lock.
3448
2864
  */
3449
2865
  listStalePendingDraws(args: {
3450
2866
  createdBeforeMs: number;
@@ -3452,38 +2868,18 @@ declare class CreditLedger {
3452
2868
  after?: StalePendingDrawCursor;
3453
2869
  }): Promise<DrawRecord[]>;
3454
2870
  /**
3455
- * Rail-native value the credit an x402 settlement channel funded has
3456
- * actually earned — the sum of its **settled** draws, in the credit's native
3457
- * atomic units (USDC micro on this rail). The batch-settlement claim job's
3458
- * ceiling (internal-review): a channel is claimable up to what its credit's draws
3459
- * have earned, never up to the deposit that funded it.
3460
- *
3461
- * `undefined` when no credit is bound to the channel, which the claim job
3462
- * treats as "cannot be resolved" and claims nothing for — distinct from a
3463
- * resolved credit that has earned `0`.
3464
- *
3465
- * Deliberately a sum over settled draws rather than `funded − remaining`:
3466
- * a drain lowers `native_remaining` without earning anything, so the
3467
- * subtraction would read the caller's own reclaim as revenue. Pending holds
3468
- * are excluded for the mirror reason — a hold is work in flight, and
3469
- * claiming against it would take money a release still owes back.
2871
+ * Sum settled draws’ native value for the credit bound to this x402 channel.
2872
+ * Pending/released holds and drains earn nothing. Return undefined for an unbound channel,
2873
+ * distinct from earned zero, so settlement cannot guess a claim limit.
3470
2874
  */
3471
2875
  earnedNativeForX402Channel(channelId: string): Promise<number | undefined>;
3472
2876
  /**
3473
- * The credit an x402 settlement channel funded, if one is bound to it — the
3474
- * binding is UNIQUE, so at most one row can answer (internal-review).
3475
- *
3476
- * The operator repair's entry point: a wedged settlement row carries the
3477
- * channel and an `effect_id`, and this is what turns them back into the
3478
- * credit id, the caller pubkey, and the drain id the effect encodes. Deriving
3479
- * the credit id by splitting `effect_id` instead would be wrong — credit ids
3480
- * carry their own colons (`imp:`, `fnd:` and the rail segment inside them).
2877
+ * Resolve the unique credit bound to the channel. The ID is opaque and can contain colons;
2878
+ * do not reconstruct it by splitting settlement effect IDs.
3481
2879
  */
3482
2880
  getCreditByX402Channel(channelId: string): Promise<CreditSnapshot | undefined>;
3483
- /** Retire every active credit backed by a chain-proven lost Tempo channel. */
3484
2881
  terminalizeTempoCredits(evidence: TempoCreditLossEvidence, tx?: CreditLedgerQuerier): Promise<TempoCreditLoss[]>;
3485
2882
  private terminalizeTempoCreditsLocked;
3486
- /** List terminal Tempo credit losses, newest observation first. */
3487
2883
  listTempoCreditLosses(args?: {
3488
2884
  limit?: number;
3489
2885
  }): Promise<TempoCreditLoss[]>;
@@ -3498,36 +2894,21 @@ declare class CreditLedger {
3498
2894
  */
3499
2895
  terminalizeX402Credit(evidence: X402CreditLossEvidence, tx?: CreditLedgerQuerier): Promise<X402CreditLoss[]>;
3500
2896
  private terminalizeX402CreditLocked;
3501
- /** List terminal x402 credit losses, newest observation first. */
3502
2897
  listX402CreditLosses(args?: {
3503
2898
  limit?: number;
3504
2899
  }): Promise<X402CreditLoss[]>;
3505
2900
  /**
3506
- * Debit the caller's entire available balance into a drain liability
3507
- * (spec §5: expiry ends spending, never ownership — this is the
3508
- * builder-honored reclaim floor). Runs under the credit row lock: the
3509
- * amount is `balance − pending holds` read under `FOR UPDATE`, the balance
3510
- * is decremented in the same transaction, and the drain takes the next
3511
- * gap-free `ledger_seq` — so the balance zeroes exactly once however many
3512
- * machines race the request.
2901
+ * Move all available balance to a drain liability under the credit lock; pending holds stay
2902
+ * reserved. Expiry alone does not prevent reclaim before a sweep releases the balance.
2903
+ * Repeating (creditId, drainId) returns the recorded liability; owner, method and payout
2904
+ * conflicts refuse.
3513
2905
  *
3514
- * Idempotent on `(creditId, drainId)`: a replay returns the recorded drain
3515
- * (this is also the poll/pickup read). A replay whose `method` or `payout`
3516
- * differs from the recorded drain is refused with `drain_conflict` —
3517
- * silently honouring a changed payout would ship the caller's money to a
3518
- * destination they never signed alongside this `drain_id`.
3519
- *
3520
- * Pending holds are NOT drained — in-flight jobs keep their money until
3521
- * they settle or release. A release after a drain restores available
3522
- * balance the caller reclaims with a **new** `drain_id`; drains are ledger
3523
- * arithmetic, not a terminal credit state, so `status` stays `active`.
3524
- *
3525
- * Deliberately **no expiry check**: draining works before and after
3526
- * expiry. The one thing expiry gates is new draws.
2906
+ * An unresolved channel refund blocks fresh drain IDs but admits its own effect ID for
2907
+ * repair. Caller-supplied payout validation belongs to the route. With tx, join the channel
2908
+ * transaction without controlling it.
3527
2909
  */
3528
2910
  requestDrain(args: {
3529
2911
  creditId: string;
3530
- /** Client-generated drain id from the signed request body. */
3531
2912
  drainId: string;
3532
2913
  /** Verified caller pubkey — a drain may only be requested by the owner. */
3533
2914
  callerPubkey: string;
@@ -3540,37 +2921,22 @@ declare class CreditLedger {
3540
2921
  tx?: CreditLedgerQuerier;
3541
2922
  nowMs?: number;
3542
2923
  }): Promise<DrainRequestResult>;
3543
- /** Read one drain (no lock), joined with its credit for currency + owner. */
3544
2924
  getDrain(args: {
3545
2925
  creditId: string;
3546
2926
  drainId: string;
3547
2927
  }): Promise<CreditDrainRecord | undefined>;
3548
2928
  /**
3549
- * One credit's undepleted funding lots, oldest first (internal-review) — the
2929
+ * One credit's undepleted funding lots, oldest first — the
3550
2930
  * public read behind the reclaim's own arithmetic, for anything that needs
3551
2931
  * to show its work.
3552
2932
  */
3553
2933
  listFundingLots(creditId: string): Promise<FundingLot[]>;
3554
2934
  /**
3555
- * What this DVM owes back in satoshis if every open non-channel Bitcoin
3556
- * credit reclaimed right now (internal-review) — the deposit half of the hub
3557
- * balance, and the floor a payout sweep must never go below.
3558
- *
3559
- * Read entirely off the funding lots, at their own funding rates, with no
3560
- * exchange-rate lookup anywhere: the obligation is in kind, so a rate has
3561
- * nothing to say about it. Pending job holds are deliberately **not**
3562
- * subtracted — a hold either settles into revenue or releases back to the
3563
- * caller, and until it does, treating it as already earned would understate
3564
- * the floor. The figure is conservative by exactly that amount.
3565
- *
3566
- * The two coverage holes ride along rather than being folded in silently, so
3567
- * a gap reads as a gap: `uncovered` is a credit whose lots fall short of its
3568
- * balance, `unbacked` is one holding any open micro with no sats basis. A
3569
- * reclaim on either prices the **whole** balance off a live rate — the
3570
- * depletion refuses a mixture rather than blending two bases — so both are
3571
- * reported as the whole balance, and neither contributes to `depositSats`.
3572
- * A figure that counted the backed half of a mixed credit would be a floor
3573
- * the payout never matches.
2935
+ * Host-wide in-kind Bitcoin deposit liability, excluding channel collateral. Credits with
2936
+ * missing coverage or any basisless lot are reported as gaps and use the live-rate fallback
2937
+ * rather than a guessed sats obligation. Group fiat micro by currency; never sum
2938
+ * denominations.
2939
+ * Pending job holds remain liabilities until settled because they may release to the caller.
3574
2940
  */
3575
2941
  bitcoinDepositLiability(): Promise<BitcoinDepositLiability>;
3576
2942
  /**
@@ -3583,46 +2949,21 @@ declare class CreditLedger {
3583
2949
  */
3584
2950
  listPendingDrains(): Promise<CreditDrainRecord[]>;
3585
2951
  /**
3586
- * One page of still-`pending` drains on a channel-backed credit, oldest
3587
- * first (no lock) — the operator's repair queue (internal-review).
3588
- *
3589
- * Keyset-paginated for the x402 settlement queue's reason: a wedged row
3590
- * never self-clears, so a caller that re-issues the same first page would
3591
- * never see past a `limit`-sized wall of them. The keyset is
3592
- * `(created_at, credit_id, drain_id)` — `created_at` is immutable, so no
3593
- * concurrent write can push an unexamined row past the cursor. It is a
3594
- * BIGINT of epoch milliseconds rather than a `TIMESTAMPTZ`, so the
3595
- * microsecond-vs-millisecond cursor trap that bit `listSettlements` cannot
3596
- * arise here: the value round-trips as the integer it already is.
3597
- *
3598
- * `createdBeforeMs` is the staleness floor. A healthy cooperative close
3599
- * moves the drain to `sent` inside the request that opened it, so anything
3600
- * younger is in flight rather than stuck.
3601
- *
3602
- * The method filter is `'tempo'`, and it is the one line here worth a second
3603
- * look: `init()` migrates the pre-internal-review `'mpp'` spelling away and
3604
- * `DrainMethod` no longer carries it, so a query naming it matches nothing a
3605
- * DVM has ever written. It named it anyway until internal-review's rename sweep and
3606
- * internal-review's live rung caught it independently — the whole operator queue
3607
- * read empty in production while every route test stayed green, because
3608
- * those run on `MemoryCreditLedger`, which filters on the typed value. The
3609
- * two implementations of this one predicate must be read together.
3610
- *
3611
- * A written-off row is excluded unless `includeResolved`: the operator has
3612
- * already decided about it, and leaving it on the live queue would re-serve
3613
- * a closed decision on every walk.
2952
+ * Page pending Tempo-channel drains by (created_at, credit_id, drain_id), optionally
2953
+ * age-bounded, excluding written-off rows. Accept the legacy mpp stored method as well as
2954
+ * tempo so pre-migration drains remain repairable. Use the compound key for both ordering
2955
+ * and continuation.
3614
2956
  */
3615
2957
  listChannelDrains(args: {
3616
2958
  limit: number;
3617
2959
  after?: ChannelDrainCursor;
3618
2960
  createdBeforeMs: number;
3619
- /** Which channel binding to walk. Today only Tempo has a repair. */
3620
2961
  rail: "tempo";
3621
2962
  includeResolved?: boolean;
3622
2963
  }): Promise<CreditDrainRecord[]>;
3623
2964
  /**
3624
2965
  * Record that an operator reviewed a floor-refused channel drain and is not
3625
- * booking it (internal-review). Books nothing anywhere — no status change, no
2966
+ * booking it. Books nothing anywhere — no status change, no
3626
2967
  * balance movement, no platform report.
3627
2968
  *
3628
2969
  * A single-statement CAS on `written_off_at IS NULL`, so the first note on
@@ -3680,7 +3021,7 @@ declare class CreditLedger {
3680
3021
  creditId: string;
3681
3022
  drainId: string;
3682
3023
  token: string;
3683
- /** Cost evidence for a just-in-time mint (internal-review); absent on an accumulator park. */
3024
+ /** Cost evidence for a just-in-time mint; absent on an accumulator park. */
3684
3025
  fulfilment?: DrainFulfilment;
3685
3026
  tx?: CreditLedgerQuerier;
3686
3027
  nowMs?: number;
@@ -3691,7 +3032,7 @@ declare class CreditLedger {
3691
3032
  * loss, so the caller's re-poll keeps returning the same token either way.
3692
3033
  *
3693
3034
  * Returns `replayed` because this is a **terminal** transition and the money
3694
- * has left at exactly one of these calls (internal-review): the platform drain
3035
+ * has left at exactly one of these calls: the platform drain
3695
3036
  * report must be emitted by that caller and no other. Pass `tx` to write the
3696
3037
  * report through the same transaction as the CAS.
3697
3038
  */
@@ -3722,11 +3063,9 @@ declare class CreditLedger {
3722
3063
  nowMs?: number;
3723
3064
  }): Promise<DrainTransitionResult>;
3724
3065
  /**
3725
- * Append a countersigned reclaim-event receipt to the drain's evidence
3726
- * trail. A single-statement JSONB append — receipts are emitted after the
3727
- * state transition they attest, and losing one never loses money (the
3728
- * transition is the source of truth; the receipt is the proof the caller
3729
- * can carry away).
3066
+ * Append an opaque supplied receipt observation to the drain’s JSONB evidence array.
3067
+ * Issuance and authentication belong to callers/consumers; this store does not verify
3068
+ * signatures. Preserve append order on replay/poll reads.
3730
3069
  */
3731
3070
  appendDrainReceipt(args: {
3732
3071
  creditId: string;
@@ -3735,35 +3074,17 @@ declare class CreditLedger {
3735
3074
  tx?: CreditLedgerQuerier;
3736
3075
  }): Promise<void>;
3737
3076
  private readDrain;
3738
- /** Re-read the credit join fields for a freshly-updated drain row. */
3739
3077
  private rejoin;
3740
- /** Shared refusal tail for the CAS transitions: idempotent replay or typed error. */
3741
3078
  private refuseTransition;
3742
3079
  /**
3743
- * Price a reclaim in kind and say which lots it takes (internal-review).
3744
- *
3745
- * The debits are returned whether or not the reclaim can be priced, and the
3746
- * caller applies them either way: the money is leaving the credit, so the
3747
- * lots have to shrink with it or the deposit-liability rollup would keep
3748
- * counting a balance that is gone.
3749
- *
3750
- * `owedSats` is `null` where {@link isInKindDepletion} refuses — the lots
3751
- * are short of the balance, or the covering lots carry no sats basis (a
3752
- * credit funded before internal-review). That row is priced off a live rate by the
3753
- * admin surface instead, which is what *every* row did before this change,
3754
- * so the fallback is the old behaviour rather than a new failure mode.
3755
- *
3756
- * What it publishes is **net of the delivery reserve** (internal-review): handing a
3757
- * refund over costs a mint fee and, when the notes have to be minted just in
3758
- * time, a Lightning hop. The caller carries that cost per internal-review, and a
3759
- * flat reserve is how they carry it without the figure moving — an
3760
- * actual-fee true-up could only be applied after the promise was made. The
3761
- * debits stay gross because they are denominated in micro and are what
3762
- * `releaseDrain` restores; only the sats figure is netted.
3080
+ * Deplete FIFO lots even when they cannot price the reclaim; stale open lots must not
3081
+ * remain behind a debited balance. Publish owedSats only with complete positive sats
3082
+ * coverage, net of one delivery reserve. Otherwise preserve null for a live-rate fallback.
3083
+ * Returned debits allow an unpaid drain release to restore the exact lots.
3763
3084
  */
3764
3085
  private reclaimInKind;
3765
3086
  /**
3766
- * This credit's undepleted funding lots, oldest first (internal-review).
3087
+ * This credit's undepleted funding lots, oldest first.
3767
3088
  *
3768
3089
  * Ordered in SQL on the same `(created_at, lot_id)` key the partial index
3769
3090
  * carries, so FIFO is the index's own order rather than something a reader
@@ -3779,7 +3100,6 @@ declare class CreditLedger {
3779
3100
  * as every other balance move is.
3780
3101
  */
3781
3102
  private applyLotDebits;
3782
- /** Shared settle/release state machine — see the public JSDoc on each verb. */
3783
3103
  private resolveDraw;
3784
3104
  private resolveDrawLocked;
3785
3105
  /**
@@ -3788,13 +3108,12 @@ declare class CreditLedger {
3788
3108
  * success (harmless for read-only replay paths), rolls back on throw.
3789
3109
  */
3790
3110
  private withCreditLock;
3791
- /** `SELECT … FOR UPDATE` the credit row on `q` (which must be inside a transaction). */
3792
3111
  private lockCreditRow;
3793
3112
  private readDraw;
3794
3113
  /**
3795
3114
  * Held-but-unresolved totals for a credit, in all three units at once — the
3796
3115
  * fiat micro that defines available balance plus the rail-value remainders
3797
- * a new draw allocates against (internal-review). One round-trip, since every
3116
+ * a new draw allocates against. One round-trip, since every
3798
3117
  * caller needs the micro sum anyway.
3799
3118
  */
3800
3119
  private pendingSums;
@@ -3803,24 +3122,20 @@ declare class CreditLedger {
3803
3122
  private readX402CreditLosses;
3804
3123
  }
3805
3124
  /**
3806
- * The ledger surface the SDK server codes against (internal-review). Implemented by
3807
- * the Postgres {@link CreditLedger} (production — spec condition 2 requires
3808
- * the DVM's own Postgres) and by `MemoryCreditLedger` (devMode / in-process
3809
- * tests / pool-less `createDVMServer` setups, mirroring the `MemoryJobStore`
3810
- * precedent). The `tx` handles on `fund`/`draw` are meaningful only on the
3811
- * Postgres implementation; the memory ledger ignores them — its operations
3812
- * are synchronous-atomic in-process, so there is no rail transaction to join.
3125
+ * Implemented by durable Postgres and per-process memory ledgers. Production funding menus
3126
+ * require durable storage; memory is for explicit development and tests. Mutations accepting
3127
+ * tx join the caller’s transaction; memory implementations ignore that handle.
3813
3128
  */
3814
3129
  interface CreditLedgerLike {
3815
3130
  /**
3816
3131
  * True when balances survive a restart and are visible fleet-wide
3817
- * (internal-review). The funding menu is suppressed on a non-durable ledger
3132
+ * The funding menu is suppressed on a non-durable ledger
3818
3133
  * outside devMode: a caller who funds on machine A and draws on machine B
3819
3134
  * would get `credit_not_found`, so advertising credit there misadvertises.
3820
3135
  */
3821
3136
  readonly durable: boolean;
3822
3137
  /**
3823
- * Bind the durable x402 settlement state spending is gated on (internal-review).
3138
+ * Bind the durable x402 settlement state spending is gated on.
3824
3139
  *
3825
3140
  * Optional because a host may wire a ledger implementation that has no such
3826
3141
  * gate; the SDK's own two both do. See
@@ -3829,7 +3144,6 @@ interface CreditLedgerLike {
3829
3144
  useX402SettlementGate?(gate: X402RefundSettlementGate): void;
3830
3145
  /**
3831
3146
  * Bind the outbox that reports credit-expiry releases and their revivals
3832
- * (internal-review).
3833
3147
  *
3834
3148
  * Optional, and absent on `MemoryCreditLedger`: an expiry release is a
3835
3149
  * durable liability fact, and a ledger whose balances do not survive a
@@ -3838,7 +3152,7 @@ interface CreditLedgerLike {
3838
3152
  */
3839
3153
  useCreditExpiryReleaseOutbox?(outbox: CreditExpiryReleaseOutbox): void;
3840
3154
  /**
3841
- * Release the undrawn remainder of every credit past its TTL (internal-review).
3155
+ * Release the undrawn remainder of every credit past its TTL.
3842
3156
  * Optional on the same terms as the binder above. See
3843
3157
  * {@link CreditLedger.sweepExpiredCredits}.
3844
3158
  */
@@ -3850,7 +3164,7 @@ interface CreditLedgerLike {
3850
3164
  }>;
3851
3165
  /**
3852
3166
  * Ask that gate directly, so a route can refuse a wedged channel **before**
3853
- * the rail moves anything (internal-review). Optional on the same terms as the
3167
+ * the rail moves anything. Optional on the same terms as the
3854
3168
  * binder above; an implementation without it simply gets no preflight, and
3855
3169
  * the ledger effect stays the authority. See
3856
3170
  * {@link CreditLedger.blockingX402Refund}.
@@ -3999,9 +3313,7 @@ interface CreditLedgerLike {
3999
3313
  creditId: string;
4000
3314
  drawId: string;
4001
3315
  }): Promise<DrawRecord | undefined>;
4002
- /** Read the sole draw linked to a stable pre-payment job id. */
4003
3316
  getDrawByJobId(jobId: string): Promise<DrawRecord | undefined>;
4004
- /** Read every draw linked to a job so retention can resolve all holds before redaction. */
4005
3317
  listDrawsByJobId(jobId: string): Promise<DrawRecord[]>;
4006
3318
  listPendingDraws(creditId: string): Promise<DrawRecord[]>;
4007
3319
  listStalePendingDraws(args: {
@@ -4034,9 +3346,8 @@ interface CreditLedgerLike {
4034
3346
  drainId: string;
4035
3347
  }): Promise<CreditDrainRecord | undefined>;
4036
3348
  listPendingDrains(): Promise<CreditDrainRecord[]>;
4037
- /** See {@link CreditLedger.bitcoinDepositLiability} — the in-kind deposit floor (internal-review). */
3349
+ /** See {@link CreditLedger.bitcoinDepositLiability} — the in-kind deposit floor. */
4038
3350
  bitcoinDepositLiability(): Promise<BitcoinDepositLiability>;
4039
- /** One credit's undepleted funding lots, oldest first (internal-review). */
4040
3351
  listFundingLots(creditId: string): Promise<FundingLot[]>;
4041
3352
  listChannelDrains(args: {
4042
3353
  limit: number;
@@ -4098,14 +3409,12 @@ interface CreditLedgerLike {
4098
3409
  interface CreditLedgerQuerier {
4099
3410
  query: Pool["query"];
4100
3411
  }
4101
- /** Reporter outbox joined to a draw release's own transaction (internal-review). */
4102
3412
  interface CreditDrawReleaseOutbox {
4103
3413
  /** Platform DVM record ID bound into the report. */
4104
3414
  dvmId: string;
4105
3415
  /** Reporter-owned insert executed through the ledger transaction. */
4106
3416
  enqueue: CreditDrawReleaseEnqueue;
4107
3417
  }
4108
- /** Reporter seam for credit-expiry releases and the revivals that reverse them (internal-review). */
4109
3418
  interface CreditExpiryReleaseOutbox {
4110
3419
  /** Platform DVM record ID bound into the report. */
4111
3420
  dvmId: string;
@@ -4113,16 +3422,15 @@ interface CreditExpiryReleaseOutbox {
4113
3422
  enqueue: CreditExpiryReleaseEnqueue;
4114
3423
  }
4115
3424
  /**
4116
- * One recorded expiry episode: a credit's undrawn remainder passing to the
4117
- * builder when its TTL ran out (internal-review).
4118
- *
4119
- * `reversedAt` is set when a later funding revived the credit, at which point
4120
- * the release stops counting against committed value — the row stays as the
4121
- * record that it once did.
3425
+ * One committed expiry episode. The sweep releases the undrawn remainder; later funding sets
3426
+ * reversedAt and restores that release on the same transaction. Multiple episodes may share
3427
+ * an expiry instant.
4122
3428
  */
4123
3429
  interface CreditExpiryRelease {
4124
3430
  creditId: string;
4125
- /** Per-expiry-episode key, derived from {@link CreditExpiryRelease.expiryMs}. */
3431
+ /**
3432
+ * Key derived from expiry instant and the release sequence counted under the credit lock.
3433
+ */
4126
3434
  releaseId: string;
4127
3435
  callerPubkey: string;
4128
3436
  currency: string;
@@ -4130,31 +3438,29 @@ interface CreditExpiryRelease {
4130
3438
  rail: string | null;
4131
3439
  /** Fiat micro released, 1e-6 of `currency`. */
4132
3440
  amountMicro: number;
4133
- /** When the credit's TTL ran out — when the liability ended. */
3441
+ /**
3442
+ * Nominal credit expiry, ms since epoch; the claim is released only when the sweep commits.
3443
+ */
4134
3444
  expiryMs: number;
4135
- /** When the sweep observed the expiry. */
3445
+ /**
3446
+ * When the sweep recorded this release, ms since epoch.
3447
+ */
4136
3448
  releasedAt: number;
4137
3449
  /** When a funding revived the credit and undid this release. */
4138
3450
  reversedAt: number | null;
4139
3451
  }
4140
- /** Pool-shaped handle (`query` + `connect`). Required for the locked write paths. */
4141
3452
  type CreditLedgerPool = Pick<Pool, "query" | "connect">;
4142
3453
  /**
4143
- * Credit lifecycle status. `unbacked` is the terminal audit state for a Tempo
4144
- * channel that finalized below its accepted voucher; its nominal balance is
4145
- * preserved, but it can never be drawn, funded, grown, or drained.
3454
+ * unbacked credit is unavailable for spending or reclaim while channel collateral is gone. An
3455
+ * x402 credit with no remaining liability can be restored to active by channel
3456
+ * reconciliation.
4146
3457
  */
4147
3458
  type CreditStatus = "active" | "unbacked";
4148
3459
  /** Draw lifecycle: two-phase hold → terminal settle/release. */
4149
3460
  type DrawStatus = "pending" | "settled" | "released";
4150
3461
  /**
4151
- * Rails a drain can pay out on (internal-review, spec §5 tier 3).
4152
- *
4153
- * Direct Lightning payout is deliberately absent (internal-review / internal-review): a
4154
- * first-party deployment holds no send-capable Lightning credential by design
4155
- * (the funding NWC is receive-only, internal-review), so P2PK-locked Cashu notes are
4156
- * the only non-channel Bitcoin reclaim path. Adding `lightning` back means
4157
- * re-deciding that, not just widening a union.
3462
+ * Drain payout rails. Direct Lightning send is excluded: deployments reclaiming Bitcoin can
3463
+ * use Cashu notes without putting a spending wallet credential in the DVM.
4158
3464
  */
4159
3465
  type DrainMethod = "cashu" | "x402" | "tempo";
4160
3466
  /**
@@ -4167,7 +3473,7 @@ type DrainMethod = "cashu" | "x402" | "tempo";
4167
3473
  */
4168
3474
  type DrainStatus = "pending" | "parked" | "picked_up" | "sent" | "released";
4169
3475
  /**
4170
- * The sats a DVM owes back on undrawn non-channel Bitcoin credit (internal-review) —
3476
+ * The sats a DVM owes back on undrawn non-channel Bitcoin credit —
4171
3477
  * the deposit pot inside the hub's balance, which the operator's sweep may
4172
3478
  * never bank below.
4173
3479
  */
@@ -4193,16 +3499,24 @@ interface BitcoinDepositLiability {
4193
3499
  }[];
4194
3500
  /** Credits holding balance no funding lot backs — a defect, not a liability figure. */
4195
3501
  uncoveredCredits: number;
4196
- /** Their whole balance: a short-covered credit reclaims off a live rate entirely. */
4197
- uncoveredMicro: number;
3502
+ /** Whole balances of short-covered credits, by ledger currency; reclaim uses a live rate. */
3503
+ uncoveredByCurrency: {
3504
+ currency: string;
3505
+ micro: number;
3506
+ credits: number;
3507
+ }[];
4198
3508
  /** Credits holding any open micro with no sats basis, so their reclaim prices off a live rate. */
4199
3509
  unbackedCredits: number;
4200
- /** Their whole balance, for the same all-or-nothing reason. */
4201
- unbackedMicro: number;
3510
+ /** Whole balances of basisless credits, by ledger currency. */
3511
+ unbackedByCurrency: {
3512
+ currency: string;
3513
+ micro: number;
3514
+ credits: number;
3515
+ }[];
4202
3516
  }
4203
3517
  /**
4204
3518
  * What it cost to put a just-in-time-minted refund in the caller's hands
4205
- * (internal-review), itemized on the drain row so the spend is auditable against the
3519
+ * itemized on the drain row so the spend is auditable against the
4206
3520
  * hub's own transaction history long after the worker's log has rotated.
4207
3521
  *
4208
3522
  * `faceSats` is what the caller receives and equals the drain's published
@@ -4236,16 +3550,9 @@ interface CreditDrainRecord {
4236
3550
  currency: string;
4237
3551
  method: DrainMethod;
4238
3552
  /**
4239
- * The channel this drain's credit is bound to (Tempo session or x402
4240
- * settlement channel), `null` on a one-payment or Cashu/Lightning credit.
4241
- *
4242
- * The method alone does not say who settles a reclaim: an `mpp` or `x402`
4243
- * drain on a channel-bound credit is closed by the channel itself, and a
4244
- * `pending` one is routinely mid-reconciliation rather than owed by anybody
4245
- * — `completeTempoDrain` leaves every ambiguous close pending on purpose for
4246
- * `recoverTempoSessionClose` to finish on the caller's next poll. Carrying
4247
- * the binding on the record is what lets a reader tell that apart from a
4248
- * one-payment reclaim only a person can settle (internal-review).
3553
+ * Reusable Tempo or x402 channel backing this drain, or null for one-payment/non-channel
3554
+ * credit. The channel’s cooperative lifecycle owns fulfillment; the operator’s manual sent
3555
+ * route refuses channel-bound drains.
4249
3556
  */
4250
3557
  channelId: string | null;
4251
3558
  /** Method-specific payout target as signed by the caller. */
@@ -4257,16 +3564,14 @@ interface CreditDrainRecord {
4257
3564
  * `null` on a rail that reports no native basis. This — not `amountMicro` —
4258
3565
  * is what an on-chain payout must be sized against, because the fiat figure
4259
3566
  * and the rail figure drift with the rate between funding and drain
4260
- * (internal-review).
4261
3567
  */
4262
3568
  drainedNative: number | null;
4263
3569
  /**
4264
- * What this reclaim owes in satoshis, computed from the credit's funding
4265
- * lots when it was requested and never re-priced (internal-review): the deposited
4266
- * sats, pro rata to the share of the credit being reclaimed, floored per
4267
- * lot. `null` on a channel rail (in-kind structurally, through the escrow)
4268
- * and on a Bitcoin credit whose lots cannot price it — that one falls back
4269
- * to a live rate on the admin surface.
3570
+ * In-kind sats obligation fixed at request time: the decrease in FIFO lots’ remaining
3571
+ * floored obligations, net of one delivery reserve. A non-null obligation is not repriced.
3572
+ * Null on unpriceable non-channel Bitcoin refunds requires the live-rate Cashu fallback.
3573
+ * Channel and stablecoin refunds also use null: they have no sats obligation and follow
3574
+ * their native/channel payout lifecycle, using `drainedNative` where applicable.
4270
3575
  */
4271
3576
  owedSats: number | null;
4272
3577
  /**
@@ -4283,7 +3588,7 @@ interface CreditDrainRecord {
4283
3588
  /** Settlement evidence, present on `sent`. */
4284
3589
  sentRef: Record<string, unknown> | null;
4285
3590
  /**
4286
- * How a parked refund was sourced and what delivering it cost (internal-review).
3591
+ * How a parked refund was sourced and what delivering it cost.
4287
3592
  *
4288
3593
  * `null` on an accumulator park — there the consumed rows *are* the record —
4289
3594
  * and on every non-cashu rail. Set only where the notes were minted just in
@@ -4292,7 +3597,10 @@ interface CreditDrainRecord {
4292
3597
  * already holding.
4293
3598
  */
4294
3599
  fulfilment: DrainFulfilment | null;
4295
- /** Countersigned reclaim-event receipts, in emission order. */
3600
+ /**
3601
+ * Opaque supplied reclaim-event observations in append order; consumers shape-check and
3602
+ * authenticate them.
3603
+ */
4296
3604
  receipts: Record<string, unknown>[];
4297
3605
  createdAt: number;
4298
3606
  parkedAt: number | null;
@@ -4301,7 +3609,7 @@ interface CreditDrainRecord {
4301
3609
  /** When an unfulfilled Tempo drain was restored to the credit balance. */
4302
3610
  releasedAt: number | null;
4303
3611
  /**
4304
- * Why an operator closed this drain out without booking it (internal-review), and
3612
+ * Why an operator closed this drain out without booking it, and
4305
3613
  * when. Set only on a channel drain the payout floor refused: the chain paid
4306
3614
  * the caller less than this ledger was extinguishing, the operator reviewed
4307
3615
  * the two figures and is not booking the difference.
@@ -4336,7 +3644,6 @@ interface DrainWriteOff {
4336
3644
  drain: CreditDrainRecord;
4337
3645
  written: boolean;
4338
3646
  }
4339
- /** Result of restoring an unfulfilled Tempo drain to its credit. */
4340
3647
  interface DrainReleaseResult {
4341
3648
  drain: CreditDrainRecord;
4342
3649
  credit: CreditSnapshot;
@@ -4344,7 +3651,7 @@ interface DrainReleaseResult {
4344
3651
  }
4345
3652
  /**
4346
3653
  * Result of a terminal drain transition — {@link CreditLedger.markDrainPickedUp}
4347
- * and {@link CreditLedger.markDrainSent} (internal-review).
3654
+ * and {@link CreditLedger.markDrainSent}.
4348
3655
  *
4349
3656
  * The record alone can't answer "did I move this money", because the CAS
4350
3657
  * winner and an idempotent replay return the same row. Anything that must
@@ -4356,7 +3663,6 @@ interface DrainTransitionResult {
4356
3663
  /** True when the drain was already terminal and this call changed nothing. */
4357
3664
  replayed: boolean;
4358
3665
  }
4359
- /** Result of {@link CreditLedger.requestDrain}. */
4360
3666
  interface DrainRequestResult {
4361
3667
  drain: CreditDrainRecord;
4362
3668
  /** The credit as of this call — balance already debited on a fresh drain. */
@@ -4365,9 +3671,8 @@ interface DrainRequestResult {
4365
3671
  replayed: boolean;
4366
3672
  }
4367
3673
  /**
4368
- * Read-model of one credit. `availableMicro` = `balanceMicro` − pending
4369
- * holds; `expired` is computed from `expiryMs` at read time (expiry ends
4370
- * spending, never ownership — the balance stays readable).
3674
+ * Read model with funded balance and pending holds. Expiry is computed at read time and does
3675
+ * not erase the record; unbacked credit has no available balance.
4371
3676
  */
4372
3677
  interface CreditSnapshot {
4373
3678
  creditId: string;
@@ -4375,23 +3680,26 @@ interface CreditSnapshot {
4375
3680
  currency: string;
4376
3681
  /** Funded balance, 1e-6 currency units. Pending holds are NOT subtracted. */
4377
3682
  balanceMicro: number;
4378
- /** Spendable balance: `balanceMicro` minus pending holds. */
3683
+ /**
3684
+ * Balance minus pending holds, or zero while backing is unbacked.
3685
+ */
4379
3686
  availableMicro: number;
4380
3687
  /**
4381
3688
  * `availableMicro` in the funding rail's own units — the ceiling any
4382
3689
  * rail-denominated payout must respect. `null` marks a rail that reports no
4383
3690
  * native basis, which is why a channel drain refuses rather than guessing
4384
- * (internal-review).
4385
3691
  */
4386
3692
  nativeAvailable: number | null;
4387
3693
  expiryMs: number;
4388
3694
  expired: boolean;
4389
3695
  status: CreditStatus;
4390
- /** High-water `ledger_seq` — the seq of the most recent draw. */
3696
+ /**
3697
+ * High-water sequence shared by draw placements and drains.
3698
+ */
4391
3699
  lastLedgerSeq: number;
4392
3700
  createdAt: number;
4393
3701
  /**
4394
- * The rail this balance was funded on (internal-review); `null` on a credit opened
3702
+ * The rail this balance was funded on; `null` on a credit opened
4395
3703
  * before rail basis was recorded, which conflicts with nothing. Exposed so a
4396
3704
  * route can refuse a cross-rail top-up **before** taking the payment `fund`
4397
3705
  * would then refuse `rail_mismatch`.
@@ -4402,37 +3710,30 @@ interface CreditSnapshot {
4402
3710
  /** Reusable x402 channel bound to this credit, if one funded it. */
4403
3711
  x402ChannelId: string | null;
4404
3712
  }
4405
- /**
4406
- * Result of a draw — carries the receipt tuple the internal-review countersignature
4407
- * consumes (`ReceiptCredit`: `draw_id`, `amount`, `balance_after`,
4408
- * `ledger_seq`). `balanceAfterMicro` and `ledgerSeq` are the values recorded
4409
- * when the draw was first placed; a replay returns them unchanged.
4410
- */
4411
3713
  interface DrawResult {
4412
3714
  creditId: string;
4413
3715
  drawId: string;
4414
3716
  amountMicro: number;
4415
- /** Available balance immediately after this draw's hold, as first recorded. */
3717
+ /**
3718
+ * Recorded balance after placement or latest growth, at the draw’s original sequence.
3719
+ */
4416
3720
  balanceAfterMicro: number;
4417
- /** Per-credit monotonic, gap-free draw counter. */
3721
+ /**
3722
+ * Per-credit gap-free sequence shared with drains; growth keeps the original value.
3723
+ */
4418
3724
  ledgerSeq: number;
4419
3725
  status: DrawStatus;
4420
3726
  /** True when this call was an idempotent replay of an already-recorded state. */
4421
3727
  replayed: boolean;
4422
- /** Rail value allocated to this draw, plus the credit's rail identity (internal-review). */
3728
+ /** Rail value allocated to this draw, plus the credit's rail identity. */
4423
3729
  railValue: DrawRailValue;
4424
3730
  /** When the draw reached its terminal state; `null` while pending. */
4425
3731
  resolvedAt: number | null;
4426
3732
  }
4427
3733
  /**
4428
- * The rail-native slice of a credit that one draw carries (internal-review) — what
4429
- * makes a settled draw bookable as a revenue event on the funding's rail.
4430
- *
4431
- * Allocated at draw time as `remaining × amountMicro / availableMicro`, where
4432
- * both remainders are net of pending holds. The draw that consumes a credit's
4433
- * last micro therefore takes ratio 1 and the exact remainder, so the draws of
4434
- * a credit sum to precisely the value funded — no rounding residue, and no
4435
- * last-draw special case.
3734
+ * A draw’s rail value and funding identity. Placement records a pooled forecast; settling
3735
+ * non-channel Bitcoin can replace it with FIFO lot pricing while retaining the original as
3736
+ * allocatedMsats. Native null means no native amount was reported, distinct from zero.
4436
3737
  */
4437
3738
  interface DrawRailValue {
4438
3739
  /** Millisatoshis of rail value allocated to this draw. */
@@ -4441,7 +3742,7 @@ interface DrawRailValue {
4441
3742
  drawNative: number | null;
4442
3743
  /**
4443
3744
  * Rail that funded the credit — a {@link FundingMethod} on any row `fund`
4444
- * wrote, `null` on one it opened before internal-review recorded the column. Stays
3745
+ * wrote, `null` on one it opened before recorded the column. Stays
4445
3746
  * `string` rather than the union: this is a database read, and a fleet
4446
3747
  * mid-rollout can hold a rail a newer build wrote ({@link isFundingRail}).
4447
3748
  */
@@ -4456,7 +3757,7 @@ interface DrawRailValue {
4456
3757
  creditCurrency: string | null;
4457
3758
  }
4458
3759
  /**
4459
- * Result of `growDraw` (internal-review) — the draw's post-growth tuple, plus the
3760
+ * Result of `growDraw` — the draw's post-growth tuple, plus the
4460
3761
  * slice this top-up added. The added slice is what the mid-job rail result
4461
3762
  * reports as its `paidMsats` delta: the job's counters accumulate increments,
4462
3763
  * not the running total the draw now holds.
@@ -4470,7 +3771,7 @@ interface GrownDrawResult extends DrawResult {
4470
3771
  /** Fiat micro this growth added — `amountMicro` minus the pre-growth amount. */
4471
3772
  addedAmountMicro: number;
4472
3773
  /**
4473
- * Fiat micro the internal-review cap refused, because growing by it would have
3774
+ * Fiat micro the cap refused, because growing by it would have
4474
3775
  * pushed the draw past what the job cumulatively asked. Zero on every
4475
3776
  * ordinary top-up. Non-zero means money landed on the credit that this job
4476
3777
  * did not buy anything with — it stays the caller's available balance, and
@@ -4485,13 +3786,12 @@ interface DrawResolution extends DrawResult {
4485
3786
  /** Settlement instant — the timestamp a draw-keyed revenue event is dated by. */
4486
3787
  resolvedAt: number;
4487
3788
  }
4488
- /** Full persisted draw row, camel-cased. */
4489
3789
  interface DrawRecord extends Omit<DrawResult, "replayed"> {
4490
3790
  jobId: string | null;
4491
3791
  createdAt: number;
4492
3792
  /**
4493
3793
  * What `allocateDrawValue` allocated this draw when the payment landed
4494
- * (internal-review) — the pro-rata slice of the credit's pooled rail value that
3794
+ * the pro-rata slice of the credit's pooled rail value that
4495
3795
  * `withDrawBasis` mirrored onto the job's `paidMsats` counter.
4496
3796
  *
4497
3797
  * Equal to `railValue.drawMsats` on every draw the settle did not re-price,
@@ -4504,8 +3804,8 @@ interface DrawRecord extends Omit<DrawResult, "replayed"> {
4504
3804
  allocatedMsats: number;
4505
3805
  }
4506
3806
  /**
4507
- * Keyset cursor into {@link CreditLedgerLike.listStalePendingDraws} (internal-review)
4508
- * — the sort key of the last row a caller examined. Offset paging would not
3807
+ * Keyset cursor into {@link CreditLedgerLike.listStalePendingDraws}, holding the
3808
+ * sort key of the last row a caller examined; offset paging would not
4509
3809
  * do: the scan skips holds it can't resolve (their job row exists), so those
4510
3810
  * rows stay `pending` and reappear at the head of every subsequent page. The
4511
3811
  * tuple is `(createdAt, creditId, ledgerSeq)`, matching the read's `ORDER BY`;
@@ -4518,7 +3818,7 @@ interface StalePendingDrawCursor {
4518
3818
  ledgerSeq: number;
4519
3819
  }
4520
3820
  /**
4521
- * Rail value one funding contributes to a credit (internal-review). Recorded so a
3821
+ * Rail value one funding contributes to a credit. Recorded so a
4522
3822
  * later draw can be allocated its share of what the rail actually paid, and so
4523
3823
  * the platform can book the funding as a deposit.
4524
3824
  */
@@ -4527,11 +3827,11 @@ interface CreditFundingBasis {
4527
3827
  * The rail whose value this funding carries. A top-up on another rail is
4528
3828
  * refused (`rail_mismatch`).
4529
3829
  *
4530
- * Narrowed from `string` to {@link FundingMethod} by internal-review — the same type
3830
+ * Narrowed from `string` to {@link FundingMethod} by — the same type
4531
3831
  * {@link FUNDING_RAILS} is keyed by — so a rail cannot enter `credits.rail`
4532
3832
  * that the terminal has no way to book revenue under. That silently costs
4533
3833
  * every draw the credit backs its booking, which is what happened when
4534
- * `lightning` reached the ledger before the reader's allow-list (internal-review).
3834
+ * `lightning` reached the ledger before the reader's allow-list.
4535
3835
  */
4536
3836
  rail: FundingMethod;
4537
3837
  /** Rail payment value in millisatoshis. */
@@ -4555,9 +3855,11 @@ interface CreditFundingBasis {
4555
3855
  maxClaimableAmount: string;
4556
3856
  };
4557
3857
  }
4558
- /** What {@link CreditLedger.fund} takes. Named so the private body can share the shape. */
4559
3858
  interface CreditFundArgs {
4560
- /** Omit to mint a fresh credit id; provide to top up (or idempotently create). */
3859
+ /**
3860
+ * Omit to mint a credit ID; provide to add funding to that credit. Replay prevention is the
3861
+ * caller’s responsibility.
3862
+ */
4561
3863
  creditId?: string;
4562
3864
  callerPubkey: string;
4563
3865
  /** The DVM's pricing currency; `amountMicro` is 1e-6 of it. */
@@ -4565,14 +3867,18 @@ interface CreditFundArgs {
4565
3867
  amountMicro: number;
4566
3868
  /** Absolute expiry, ms since epoch. Overwrites the previous value on top-up. */
4567
3869
  expiryMs: number;
4568
- /** Rail-value basis this funding contributes (internal-review, required by internal-review). */
3870
+ /**
3871
+ * Rail value contributed by this funding; required on writes.
3872
+ */
4569
3873
  basis: CreditFundingBasis;
4570
- /** External transaction handle — caller owns BEGIN/COMMIT (spec condition 3). */
3874
+ /**
3875
+ * External transaction handle; caller owns BEGIN, COMMIT and ROLLBACK.
3876
+ */
4571
3877
  tx?: CreditLedgerQuerier;
4572
3878
  nowMs?: number;
4573
3879
  }
4574
3880
  /**
4575
- * Allocate a draw's share of a credit's unspent rail value (internal-review). Pure —
3881
+ * Allocate a draw's share of a credit's unspent rail value. Pure —
4576
3882
  * shared by the Postgres and in-memory ledgers so the two can't drift.
4577
3883
  *
4578
3884
  * `availableMicro` and both remainders must be net of pending holds; the
@@ -4587,7 +3893,6 @@ declare function allocateDrawValue(args: {
4587
3893
  drawMsats: number;
4588
3894
  drawNative: number | null;
4589
3895
  };
4590
- /** Full persisted `credit_fundings` row, camel-cased (internal-review). */
4591
3896
  interface FundingRecord {
4592
3897
  creditId: string;
4593
3898
  fundId: string;
@@ -4603,7 +3908,7 @@ interface FundingRecord {
4603
3908
  receipt: FundingReceipt | null;
4604
3909
  }
4605
3910
  /**
4606
- * Lifecycle of a Lightning funding invoice (internal-review).
3911
+ * Lifecycle of a Lightning funding invoice.
4607
3912
  *
4608
3913
  * Only `pending` is live; everything else is terminal. The two that carry no
4609
3914
  * balance carry opposite money: `expired` was never paid and owes nobody
@@ -4611,7 +3916,7 @@ interface FundingRecord {
4611
3916
  * operator. Both exist so that a row the sweep can never advance stops being
4612
3917
  * swept.
4613
3918
  *
4614
- * `reconciled` and `written_off` (internal-review) are where a `blocked` row goes once
3919
+ * `reconciled` and `written_off` are where a `blocked` row goes once
4615
3920
  * that operator acts — respectively, the money was credited onto a credit they
4616
3921
  * named, or they decided it would not be. Neither is `settled`: that one means
4617
3922
  * the ledger applied the payment to the top-up the caller asked for, and
@@ -4619,7 +3924,6 @@ interface FundingRecord {
4619
3924
  * human had to intervene.
4620
3925
  */
4621
3926
  type CreditInvoiceStatus = "pending" | "settled" | "expired" | "blocked" | "reconciled" | "written_off";
4622
- /** Full persisted `credit_invoices` row, camel-cased (internal-review). */
4623
3927
  interface CreditInvoiceRecord {
4624
3928
  creditId: string;
4625
3929
  fundId: string;
@@ -4638,7 +3942,7 @@ interface CreditInvoiceRecord {
4638
3942
  * trail for why a human had to touch this row at all.
4639
3943
  */
4640
3944
  blockedReason: string | null;
4641
- /** Credit an operator sent a blocked invoice's money to (internal-review). */
3945
+ /** Credit an operator sent a blocked invoice's money to. */
4642
3946
  reconciledCreditId: string | null;
4643
3947
  /** Funding key on that credit — the payment hash. See `reconcileBlockedInvoice`. */
4644
3948
  reconciledFundId: string | null;
@@ -4652,20 +3956,17 @@ interface CreditInvoiceRecord {
4652
3956
  /** When the payment landed at the wallet — never the instant an operator acted on it. */
4653
3957
  settledAt: number | null;
4654
3958
  }
4655
- /** Outcome of applying an observed settlement to the ledger (internal-review). */
4656
3959
  interface InvoiceSettlement {
4657
3960
  invoice: CreditInvoiceRecord;
4658
- /** The `credit_fundings` row; absent only if a pre-internal-review row somehow lacks one. */
3961
+ /** The `credit_fundings` row; absent only if a legacy row somehow lacks one. */
4659
3962
  funding: FundingRecord | undefined;
4660
3963
  credit: CreditSnapshot | undefined;
4661
3964
  /** True when this settlement had already been applied — nothing moved. */
4662
3965
  replayed: boolean;
4663
3966
  }
4664
3967
  /**
4665
- * Outcome of an operator crediting a blocked invoice onto a credit they named
4666
- * (internal-review). Same shape as {@link InvoiceSettlement}, but `funding` and
4667
- * `credit` describe the **target** credit, not the one the invoice was minted
4668
- * against.
3968
+ * Reconciliation keeps the original invoice identity; funding and credit describe the
3969
+ * operator-selected target credit, which may differ from the invoice’s original credit.
4669
3970
  */
4670
3971
  interface InvoiceReconciliation {
4671
3972
  invoice: CreditInvoiceRecord;
@@ -4674,7 +3975,6 @@ interface InvoiceReconciliation {
4674
3975
  /** True when this reconcile had already been applied — nothing moved. */
4675
3976
  replayed: boolean;
4676
3977
  }
4677
- /** Outcome of `writeOffBlockedInvoice` — the recorded row, plus whether this call wrote it. */
4678
3978
  interface InvoiceWriteOff {
4679
3979
  invoice: CreditInvoiceRecord;
4680
3980
  /** False when the invoice was already written off, so nothing changed. */
@@ -4686,31 +3986,13 @@ interface BlockedInvoiceCursor {
4686
3986
  creditId: string;
4687
3987
  fundId: string;
4688
3988
  }
4689
- /** Typed failure codes for {@link CreditLedgerError}. */
4690
3989
  type CreditLedgerErrorCode = "credit_not_found" | "credit_expired" | "credit_unbacked" | "tempo_channel_closing" | "tempo_settlement_pending" | "settlement_pending" | "insufficient_credit" | "draw_conflict" | "draw_not_found" | "invalid_draw_state" | "funding_replayed" | "currency_mismatch" | "caller_mismatch" | "rail_mismatch" | "invalid_amount" | "invalid_expiry" | "invalid_basis" | "nothing_to_drain" | "drain_below_dust" | "drain_not_found" | "drain_conflict" | "invalid_drain_state" | "invoice_not_found" | "invoice_conflict" | "invoice_not_blocked" | "below_rail_minimum";
4691
3990
  /**
4692
- * Which conflict a `drain_conflict` is, for the routes that answer one (internal-review).
4693
- *
4694
- * One code covers refusals whose only common property is that no drain was
4695
- * booked; the copy a caller relays to a human differs entirely between them.
4696
- * A tag rather than a message substring because that is what the copy used to
4697
- * key on, and both of the x402 commit leg's conflicts missed it by a word —
4698
- * silently, since the fallback is a well-formed answer about a different
4699
- * refusal.
4700
- *
4701
- * The three are not one shape with three causes. `pending_draws` and
4702
- * `balance_moved` roll back with nothing on any chain and clear on their own;
4703
- * `refund_floor` is the opposite — the cooperative refund reached the chain and
4704
- * paid short, so the caller's money moved and only the operator repair resolves
4705
- * it (internal-review). A route's `retryable` and `drain_registered` follow from which
4706
- * one it is, so the tag has to reach it.
4707
- *
4708
- * Absent means the duplicate-payout conflict `PAYMENT_ERROR_COPY.drain_conflict`
4709
- * was written for, which is also the conservative default for a conflict nobody
4710
- * has classified yet.
3991
+ * Structured drain_conflict reasons select caller instructions without matching prose. A
3992
+ * refund_floor shortfall needs operator resolution, unlike duplicate payout; foreign_credit
3993
+ * discloses no liability to the wrong owner; drain_id_retired requires a fresh ID.
4711
3994
  */
4712
3995
  type DrainConflictReason = "pending_draws" | "balance_moved" | "refund_floor";
4713
- /** Structured context on a {@link CreditLedgerError}, per code. */
4714
3996
  interface CreditLedgerErrorDetails {
4715
3997
  /** On `drain_conflict`: which conflict it is, so a route can answer it without matching on prose. */
4716
3998
  drainConflict?: DrainConflictReason;
@@ -4740,13 +4022,13 @@ interface CreditLedgerErrorDetails {
4740
4022
  settlementId?: string;
4741
4023
  /** On `settlement_pending`: that settlement's status, so an operator sees which repair applies. */
4742
4024
  settlementStatus?: string;
4743
- /** On `settlement_pending`: the drain id that settlement is under — the one id this gate admits (internal-review). */
4025
+ /** On `settlement_pending`: the drain id that settlement is under — the one id this gate admits. */
4744
4026
  blockingDrainId?: string;
4745
4027
  }
4746
4028
  /**
4747
4029
  * Thrown by every {@link CreditLedger} method on refusal. Carries a `code`
4748
4030
  * discriminator (plus structured `details`) so callers branch on a field
4749
- * instead of string-matching messages — the internal-review quote surface maps
4031
+ * instead of string-matching messages — the quote surface maps
4750
4032
  * `insufficient_credit` + `details.availableMicro` onto the structured
4751
4033
  * 402-class wire error.
4752
4034
  */
@@ -4810,7 +4092,8 @@ interface TempoSessionLifecycle {
4810
4092
  *
4811
4093
  * Every mutation takes an advisory transaction lock keyed by the channel
4812
4094
  * before locking its row with `FOR UPDATE`, applies mppx's synchronous
4813
- * transition exactly once, and commits before returning. The advisory lock
4095
+ * transition and commits before returning. Transaction recovery may retry a callback
4096
+ * attempt; a rolled-back attempt is not a second committed transition. The advisory lock
4814
4097
  * covers a channel's first write, when there is no row for `FOR UPDATE` to
4815
4098
  * lock. Together these are the cross-machine linearization point for voucher,
4816
4099
  * top-up, spend, and close state; mppx's process-local fallback is
@@ -4822,14 +4105,14 @@ interface TempoSessionLifecycle {
4822
4105
  * parent-credit lock through the same client.
4823
4106
  *
4824
4107
  * {@link withVoucherAcceptance} acquires its pooled client **lazily**, on the
4825
- * first store mutation rather than up front (internal-review). Its `operation` is
4108
+ * first store mutation rather than up front. Its `operation` is
4826
4109
  * mppx's credential verification, which does the chain work before it touches
4827
4110
  * the store: an `open` submits the escrow transaction and awaits its receipt,
4828
4111
  * a `voucher` reads channel state over public RPC. Holding a connection across
4829
4112
  * that starves everything else on the shared host pool — `/v1/job`, the job
4830
4113
  * store, KV, the credit ledger, the replay store — and exposes the open
4831
4114
  * transaction to `idle_in_transaction_session_timeout`. This is the same shape
4832
- * internal-review removed from the x402 settlement path. Laziness costs nothing in
4115
+ * removed from the x402 settlement path. Laziness costs nothing in
4833
4116
  * atomicity: the `FOR UPDATE` row lock was always taken at the first mutation,
4834
4117
  * never at `BEGIN`, so the critical section is unchanged — only the
4835
4118
  * connection-holding window shrinks to the database work it actually covers.
@@ -4838,13 +4121,10 @@ declare class PostgresTempoSessionStore implements Store.AtomicStore {
4838
4121
  private readonly pool;
4839
4122
  private readonly acceptance;
4840
4123
  constructor(pool: Pool);
4841
- /** Create the channel table under the SDK-wide migration lock. */
4842
4124
  init(): Promise<void>;
4843
- /** Read one channel snapshot. */
4844
4125
  get(key: string): Promise<unknown>;
4845
4126
  /** Replace one channel snapshot. Used by mppx only for non-RMW maintenance. */
4846
4127
  put(key: string, value: unknown): Promise<void>;
4847
- /** Delete one channel snapshot. */
4848
4128
  delete(key: string): Promise<void>;
4849
4129
  /** Atomic read-modify-write using a row lock shared by every SDK replica. */
4850
4130
  update<result>(key: string, fn: (current: unknown) => Store.Change<unknown, result>): Promise<result>;
@@ -4933,14 +4213,14 @@ interface MppOpts {
4933
4213
  /**
4934
4214
  * When set, only register MPP methods whose name appears in this list, even
4935
4215
  * if the per-rail env var (`DVMKIT_TEMPO_RECIPIENT`) is configured
4936
- * (internal-review). Lets operators temporarily disable a rail without unsetting
4216
+ * Lets operators temporarily disable a rail without unsetting
4937
4217
  * other knobs. Empty/undefined falls back to "register everything that has
4938
4218
  * a configured recipient".
4939
4219
  */
4940
4220
  methodsAllowlist?: string[];
4941
4221
  /**
4942
4222
  * Durable replay protection for the one-shot `tempo/charge` method
4943
- * (internal-review). Omit and mppx falls back to `Store.memory()`, whose consumed
4223
+ * Omit and mppx falls back to `Store.memory()`, whose consumed
4944
4224
  * transaction hashes are per-process — invisible to sibling machines and lost
4945
4225
  * on restart. Pass a cross-machine store (`PostgresTempoChargeStore`) on any
4946
4226
  * multi-instance deploy.
@@ -4970,7 +4250,7 @@ interface MppOpts {
4970
4250
  amount?: string | bigint;
4971
4251
  intervalMs?: number;
4972
4252
  /**
4973
- * How long a settlement holds its in-flight lease (internal-review). Defaults to
4253
+ * How long a settlement holds its in-flight lease. Defaults to
4974
4254
  * {@link TEMPO_SETTLEMENT_LEASE_MS}, which documents the receipt-timeout
4975
4255
  * floor an override has to stay above.
4976
4256
  */
@@ -5031,7 +4311,7 @@ interface MppxServer {
5031
4311
  * Reconcile active TIP-1034 channels; present only on a durable session
5032
4312
  * handle. `inFlight` counts channels the sweep found due but declined to
5033
4313
  * settle because another path already holds their settlement lease
5034
- * (internal-review) — a deferral, not a failure.
4314
+ * a deferral, not a failure.
5035
4315
  */
5036
4316
  sweepTempoSessions?: () => Promise<{
5037
4317
  scanned: number;
@@ -5041,7 +4321,7 @@ interface MppxServer {
5041
4321
  }>;
5042
4322
  /**
5043
4323
  * Reconcile and, if the channel is at risk, settle exactly one TIP-1034
5044
- * channel (internal-review). The forced-close protection path: a verified on-chain
4324
+ * channel. The forced-close protection path: a verified on-chain
5045
4325
  * `CloseRequested` event authorises this and nothing else.
5046
4326
  *
5047
4327
  * Needs no `listActive`, unlike {@link sweepTempoSessions} — a store that
@@ -5074,7 +4354,7 @@ interface MppxServer {
5074
4354
  /** Recover a close result after a process failure between chain and ledger commits. */
5075
4355
  recoverTempoSessionClose?: (channelId: string, drainId: string) => Promise<string | null>;
5076
4356
  /**
5077
- * Read a channel's close intent without touching it (internal-review).
4357
+ * Read a channel's close intent without touching it.
5078
4358
  *
5079
4359
  * The operator repair needs the reference a confirmed close already carries,
5080
4360
  * and it must not be `recoverTempoSessionClose`: that one *writes* — an
@@ -5086,14 +4366,14 @@ interface MppxServer {
5086
4366
  /** Read terminally consumed value for an exact cooperative-close challenge. */
5087
4367
  getTempoSessionSpent?: (channelId: string) => Promise<bigint>;
5088
4368
  /**
5089
- * Read the immutable chain binding of a stored channel (internal-review), or `null`
4369
+ * Read the immutable chain binding of a stored channel, or `null`
5090
4370
  * when this DVM holds no such channel. What a close event must match before
5091
4371
  * it may authorise anything.
5092
4372
  */
5093
4373
  getTempoSessionBinding?: (channelId: string) => Promise<TempoChannelBinding | null>;
5094
4374
  /**
5095
4375
  * Fetch a transaction receipt from this DVM's **own** configured Tempo RPC
5096
- * (internal-review) — the independent read that makes a close-event locator
4376
+ * the independent read that makes a close-event locator
5097
4377
  * self-verifying rather than something the platform asserts.
5098
4378
  */
5099
4379
  getTempoReceipt?: (args: {
@@ -5107,7 +4387,7 @@ interface MppxServer {
5107
4387
  }[];
5108
4388
  } | null>;
5109
4389
  /**
5110
- * Read what a landed cooperative close actually paid out (internal-review).
4390
+ * Read what a landed cooperative close actually paid out.
5111
4391
  *
5112
4392
  * mppx decodes `ChannelClosed` while verifying the close credential and then
5113
4393
  * drops both amounts — its session receipt carries only the channel, the
@@ -5132,7 +4412,7 @@ interface TempoChannelCloseAmounts {
5132
4412
  refundedToPayer: bigint;
5133
4413
  }
5134
4414
  /**
5135
- * What a targeted single-channel settlement did (internal-review).
4415
+ * What a targeted single-channel settlement did.
5136
4416
  *
5137
4417
  * The platform's close observer acknowledges its durable event on a terminal
5138
4418
  * outcome and retries otherwise, so the distinctions here are a delivery
@@ -5140,16 +4420,16 @@ interface TempoChannelCloseAmounts {
5140
4420
  *
5141
4421
  * - `settled` — a settlement transaction landed for this channel.
5142
4422
  * - `already_safe` — the on-chain watermark already covers every accepted
5143
- * voucher, so there is nothing to protect. A replay lands here.
4423
+ * voucher, so there is nothing to protect. A replay lands here.
5144
4424
  * - `unknown_channel` — not in this DVM's durable store. Terminal: a DVM must
5145
- * never settle a channel it does not own.
4425
+ * never settle a channel it does not own.
5146
4426
  * - `finalized_before_protection` — the channel finalized while it still owed
5147
- * us value. A **money incident**, not a success: the grace period elapsed
5148
- * without settlement and the deposit is gone. Preserved and paged, never
5149
- * quietly acknowledged.
4427
+ * us value. A **money incident**, not a success: the grace period elapsed
4428
+ * without settlement and the deposit is gone. Preserved and paged, never
4429
+ * quietly acknowledged.
5150
4430
  * - `retryable_failure` — a chain read or settlement attempt failed, or another
5151
- * path is already broadcasting a settlement for this channel (internal-review). The
5152
- * event stays pending and is retried inside the grace period.
4431
+ * path is already broadcasting a settlement for this channel. The
4432
+ * event stays pending and is retried inside the grace period.
5153
4433
  */
5154
4434
  type TempoChannelSettlementOutcome = {
5155
4435
  outcome: "settled";
@@ -5174,7 +4454,6 @@ type TempoChannelSettlementOutcome = {
5174
4454
  };
5175
4455
  /**
5176
4456
  * The immutable chain identity a TIP-1034 channel was opened against
5177
- * (internal-review).
5178
4457
  *
5179
4458
  * Doubles as the close-event authorisation record and as what the DVM reports
5180
4459
  * to the platform so a close can be routed back to it. `payee` is the DVM's
@@ -5225,7 +4504,6 @@ declare const _testing: {
5225
4504
  attachTempoSessionRuntime: typeof attachTempoSessionRuntime;
5226
4505
  };
5227
4506
 
5228
- /** Envelope fields the verifier validates around the signed payload. */
5229
4507
  interface CanonicalEnvelope {
5230
4508
  pubkey: string;
5231
4509
  signature: string;
@@ -5233,26 +4511,24 @@ interface CanonicalEnvelope {
5233
4511
  nonce: string;
5234
4512
  auth_statement?: SignedRequestStatementHeader;
5235
4513
  }
5236
- /** Options for `createSignedRequestVerifier`. */
5237
4514
  interface CreateSignedRequestVerifierOpts {
5238
4515
  /** Fixed audience/operation for a custom signed route or standalone verifier. */
5239
4516
  domain?: SignedRequestDomain;
5240
4517
  /**
5241
- * Permitted clock drift in seconds. Defaults to 300 (5 minutes — matches
5242
- * cast's `SIGNATURE_DRIFT_SECONDS`).
4518
+ * Permitted clock drift in seconds; default 300.
5243
4519
  */
5244
4520
  driftSeconds?: number;
5245
4521
  /**
5246
- * Field names to strip from the parsed input when computing the canonical
5247
- * bytes the signature covers. Defaults to `["signature"]`. The signature
5248
- * itself is always stripped (callers don't need to repeat it).
4522
+ * Fields stripped from raw input when forming signing bytes; default signature,
4523
+ * which is always stripped even when additional fields are configured.
5249
4524
  */
5250
4525
  envelopeFields?: string[];
5251
4526
  /**
5252
4527
  * Field names removed from the input before `schema` validates it — and
5253
4528
  * **kept** in the canonical bytes the signature covers. For protocol-reserved
5254
- * keys the caller signs but the schema never declares: the internal-review credit
5255
- * envelope riding a capability schema (internal-review). Defaults to none.
4529
+ * keys the caller signs but the schema never declares: the credit
4530
+ * envelope riding a capability schema. No additional fields are ignored by default;
4531
+ * auth_statement is always excluded from schema validation.
5256
4532
  *
5257
4533
  * This is the one place where "what is validated" and "what is signed"
5258
4534
  * legitimately differ. Don't reach for it to paper over a schema mismatch —
@@ -5267,7 +4543,6 @@ interface CreateSignedRequestVerifierOpts {
5267
4543
  /** Current Unix-seconds source. Defaults to `() => Math.floor(Date.now() / 1000)`. */
5268
4544
  now?: () => number;
5269
4545
  }
5270
- /** Minimal replay-store contract — same shape as cast's `ReplayStore`. */
5271
4546
  interface SignedRequestReplayStore {
5272
4547
  /**
5273
4548
  * True iff (pubkey, timestamp, nonce) was already seen within the retention
@@ -5277,7 +4552,6 @@ interface SignedRequestReplayStore {
5277
4552
  */
5278
4553
  checkAndRecord(pubkey: string, timestamp: number, nonce: string, now: number): Promise<boolean>;
5279
4554
  }
5280
- /** Options for the signer half. */
5281
4555
  interface SignedRequestSignOpts {
5282
4556
  /** Unix timestamp in seconds. Defaults to `now()`. */
5283
4557
  timestamp?: number;
@@ -5285,21 +4559,9 @@ interface SignedRequestSignOpts {
5285
4559
  nonce?: string;
5286
4560
  }
5287
4561
  /**
5288
- * Round-trip verifier + signer for secp256k1+BIP-340 Schnorr-signed protocol
5289
- * requests (internal-review, internal-review, internal-review).
5290
- *
5291
- * Authentication is two-step so the replay nonce is committed iff a job is
5292
- * created, not on every request entry:
5293
- *
5294
- * 1. {@link checkAuth} runs schema → drift → signature at request entry on
5295
- * both `/v1/quote` and `/v1/job`. Sync; no replay-store touch.
5296
- * 2. {@link recordReplay} commits `(pubkey, timestamp, nonce)` to the replay
5297
- * store. Called from `/v1/job` only, after upfront payment is verified
5298
- * and immediately before `jobManager.createJob`.
5299
- *
5300
- * The unpaid `/v1/job` warmup probe (price discovery → 402) thus no longer
5301
- * burns the nonce — clients can re-submit the same envelope with a real
5302
- * payment.
4562
+ * Authentication does not touch replay state. Job submission commits replay
4563
+ * only after upfront payment verification, so unpaid discovery cannot burn
4564
+ * the nonce. Reactivation rechecks the persisted signature without drift/replay.
5303
4565
  */
5304
4566
  interface SignedRequestVerifier<T> {
5305
4567
  /**
@@ -5310,7 +4572,7 @@ interface SignedRequestVerifier<T> {
5310
4572
  checkAuth(input: unknown, domain?: SignedRequestDomain): T & CanonicalEnvelope;
5311
4573
  /**
5312
4574
  * Schema + signature check against `input`, without the drift window check
5313
- * or replay-store touch (internal-review). For the reactivation path where the
4575
+ * or replay-store touch. For the reactivation path where the
5314
4576
  * persisted timestamp is from the original signing instant (could be hours
5315
4577
  * or days ago) and the replay nonce was already committed at submission.
5316
4578
  * Returns the verified envelope; throws {@link SignedRequestError} with
@@ -5333,20 +4595,11 @@ interface SignedRequestVerifier<T> {
5333
4595
  */
5334
4596
  signRequest(privkey: Uint8Array, body: Record<string, unknown>, opts?: SignedRequestSignOpts, domain?: SignedRequestDomain): T & CanonicalEnvelope;
5335
4597
  }
5336
- /** Failure mode taxonomy returned from `verify()`. */
5337
4598
  type SignedRequestFailure = "schema_invalid" | "timestamp_drift" | "signature_invalid" | "replay_detected";
5338
4599
  /**
5339
- * The four fields every signed-request envelope carries around the payload,
5340
- * with the runtime type each must have once the schema has parsed.
5341
- *
5342
- * The single definition of "which part of a schema failure is the envelope's":
5343
- * the post-parse shape check in `parse()` iterates it, and `authErrorBody`'s
5344
- * internal-review classifier reads its keys to decide whether a `schema_invalid` throw
5345
- * is the caller's *input* being wrong (disclosable: the schema is public on
5346
- * `/v1/info`) or their *signing* being wrong (folded into `signature_invalid`,
5347
- * one answer for every key and signature failure). `satisfies` pins it to
5348
- * {@link CanonicalEnvelope}, so a fifth envelope field can't be added to the
5349
- * type without both readers picking it up.
4600
+ * One definition of envelope field types shared by the runtime shape guard
4601
+ * and error disclosure classifier. Envelope failures stay private; violations
4602
+ * of the published builder input schema can be disclosed.
5350
4603
  */
5351
4604
  declare const SIGNED_ENVELOPE_TYPES: {
5352
4605
  readonly pubkey: "string";
@@ -5355,14 +4608,12 @@ declare const SIGNED_ENVELOPE_TYPES: {
5355
4608
  readonly nonce: "string";
5356
4609
  readonly auth_statement: "object";
5357
4610
  };
5358
- /** The envelope field names — {@link SIGNED_ENVELOPE_TYPES}' keys. */
5359
4611
  declare const SIGNED_ENVELOPE_FIELDS: readonly (keyof CanonicalEnvelope)[];
5360
- /** Structured error from `verify()` — `sub_reason` is the failure kind. */
5361
4612
  declare class SignedRequestError extends Error {
5362
4613
  readonly sub_reason: SignedRequestFailure;
5363
4614
  /**
5364
4615
  * The Zod issues behind a `schema_invalid`, when the failure came from a
5365
- * `safeParse` that produced any (internal-review). `authErrorBody` reads their
4616
+ * `safeParse` that produced any. `authErrorBody` reads their
5366
4617
  * paths to tell an input violation from an envelope one, and renders the
5367
4618
  * input half into the caller-facing `display`. Absent on every other
5368
4619
  * `sub_reason`, and on the `schema_invalid` throws that aren't a parse
@@ -5374,7 +4625,7 @@ declare class SignedRequestError extends Error {
5374
4625
  constructor(message: string, sub_reason: SignedRequestFailure,
5375
4626
  /**
5376
4627
  * The Zod issues behind a `schema_invalid`, when the failure came from a
5377
- * `safeParse` that produced any (internal-review). `authErrorBody` reads their
4628
+ * `safeParse` that produced any. `authErrorBody` reads their
5378
4629
  * paths to tell an input violation from an envelope one, and renders the
5379
4630
  * input half into the caller-facing `display`. Absent on every other
5380
4631
  * `sub_reason`, and on the `schema_invalid` throws that aren't a parse
@@ -5384,39 +4635,15 @@ declare class SignedRequestError extends Error {
5384
4635
  issues?: readonly z.core.$ZodIssue[] | undefined);
5385
4636
  }
5386
4637
  /**
5387
- * Build a secp256k1+BIP-340 Schnorr-over-canonical-JSON request verifier
5388
- * (internal-review, internal-review, internal-review).
5389
- *
5390
- * Generalises cast's `canonicalSignedPayload` + `verifyCanonicalSignedRequest`
5391
- * into a reusable primitive — `(schema, opts) -> { verify, canonicalBytes, signRequest }`.
5392
- * Both halves derive their canonical bytes through the same function so any
5393
- * Zod schema change automatically propagates to both signer and verifier.
4638
+ * Signatures cover the raw input, not Zod-parsed output. Schema validation
4639
+ * may apply defaults or transformations without changing the signed bytes.
5394
4640
  */
5395
4641
  declare function createSignedRequestVerifier<T = unknown>(schema: ZodLike<T>, opts?: CreateSignedRequestVerifierOpts): SignedRequestVerifier<T>;
5396
4642
  /**
5397
- * The wire object the caller signed, taken out of a request body (internal-review).
5398
- *
5399
- * **The single definition of which bytes get verified, and every auth call site
5400
- * reads it**: `/v1/quote`, the `/v1/job` submit, and the internal-review reactivation
5401
- * re-verify. They used to disagree — quote checked the raw `body.data`, submit
5402
- * checked `JobManager.parseInput`'s Zod output — so the two agreed only where
5403
- * the parse happened to be an identity on the caller's payload. A nested
5404
- * `.default()`, a `.transform()`, a `z.coerce.*`, or any key the caller sent
5405
- * that the schema doesn't declare made the quote pass and the paid submit 401
5406
- * `signature_invalid`, blaming the signature for a schema shape the caller
5407
- * never saw.
5408
- *
5409
- * Raw is what `public SDK documentation` specifies ("take the body, remove the
5410
- * `signature` field, and canonicalise"), what the CLI signs, and what the SDK
5411
- * persists as `record.input` — so the parsed form has no claim here. The
5412
- * capability schema still validates what the handler receives; it just doesn't
5413
- * get to rewrite the bytes the signature covers. It also means the credit
5414
- * envelope needs no re-merge: it never left.
5415
- *
5416
- * `body.data` wins when present; otherwise the JSON-encoded `body.input` form
5417
- * is parsed. Anything else — no input at all, a non-JSON legacy string — yields
5418
- * `{}`, which carries no envelope and so fails the auth check, exactly as
5419
- * before.
4643
+ * Raw structured input for authentication and durable replay. data wins;
4644
+ * otherwise input is parsed as JSON, with absent/legacy text becoming {}.
4645
+ * Builder validation may transform the handler input but cannot rewrite these
4646
+ * signing bytes, including protocol-owned credit fields.
5420
4647
  */
5421
4648
  declare function signedRequestInput(body: {
5422
4649
  input?: string;
@@ -5427,13 +4654,13 @@ declare function signedRequestInput(body: {
5427
4654
  * cross-machine `SignedRequestReplayStore` is supplied. 10-minute retention
5428
4655
  * window, 100k entry cap, per-process. Exported so `secp256k1Auth(...)` can
5429
4656
  * pre-build one and share it across every per-schema verifier plus its own
5430
- * `recordReplay` path (internal-review).
4657
+ * `recordReplay` path.
5431
4658
  *
5432
4659
  * When the cap is hit after the time-expired sweep, fresh inserts are
5433
4660
  * **rejected** (return `true` — treated as replay at the wire) rather than
5434
4661
  * silently evicting an in-window entry. Silent eviction would flush a
5435
4662
  * still-valid nonce back to "unseen" and let an attacker who can drive cap
5436
- * pressure replay it (internal-review). A rate-limited structured warning fires so
4663
+ * pressure replay it. A rate-limited structured warning fires so
5437
4664
  * operators see saturation in `fly logs`; reaching cap means in-memory has
5438
4665
  * been pushed past where it's safe and the deploy should move to
5439
4666
  * `PostgresReplayStore`.
@@ -5441,55 +4668,15 @@ declare function signedRequestInput(body: {
5441
4668
  declare function createDefaultReplayStore(): SignedRequestReplayStore;
5442
4669
 
5443
4670
  /**
5444
- * A DVM-level auth scheme, declared on `DVMDescriptor.auth`. Authentication
5445
- * is two-step (internal-review):
5446
- *
5447
- * 1. {@link checkAuth} fires at request entry on `/v1/quote` and `/v1/job`,
5448
- * before payment verification. Schema / drift / signature checks only —
5449
- * no replay-store touch. Unsigned / malformed / stale / wrong-sig
5450
- * envelopes reject with HTTP 401 with no Cashu round-trip and no pending
5451
- * revenue row (internal-review).
5452
- * 2. {@link recordReplay} fires on `/v1/job` only, after upfront payment is
5453
- * verified and immediately before `jobManager.createJob`. Commits the
5454
- * `(pubkey, timestamp, nonce)` tuple to the replay store; throws 401
5455
- * `replay_detected` on duplicate. The unpaid 402-discovery hop does not
5456
- * burn the nonce, so clients can re-submit with payment.
5457
- *
5458
- * On `replay_detected` after payment was already accepted, the SDK responds
5459
- * 401 fail-closed uniformly across cashu / mppx / x402 — no rail-specific
5460
- * refund or compensation logic (internal-review / internal-review). The Cashu accept path
5461
- * commits proofs into the accumulator wallet inside `verifyUpfrontPayment`
5462
- * before `recordReplay` runs (internal-review rejects unlocked Cashu upstream, so
5463
- * the accumulator is the only production path), so no proofs are held to
5464
- * refund; mppx / x402 credentials are single-use and already consumed by
5465
- * the facilitator.
5466
- *
5467
- * The SDK picks the active capability's `input` schema and dispatches the
5468
- * `checkAuth` call against it — schemes themselves take no schema parameter,
5469
- * so one declaration covers every capability under a multi-capability DVM.
5470
- *
5471
- * A scheme validates the input it is handed, and on `/v1/job` that input
5472
- * carries the credit envelope (`credit_id` / `draw_id` / `fund`) because the
5473
- * signature covers it. Those keys are the protocol's, not the capability's, so
5474
- * a scheme must not fail a schema that doesn't declare them — `secp256k1Auth`
5475
- * removes them for the schema check only, keeping the signing bytes verbatim
5476
- * (internal-review). A custom scheme owes callers the same tolerance, or every strict
5477
- * schema 401s on an explicit draw.
4671
+ * Boot-summary backend: postgres is shared across machines; memory is per-process.
4672
+ * Explicit user-supplied stores and null (disabled) are never overridden.
5478
4673
  */
4674
+ type ReplayStoreBackend = "postgres" | "memory" | "user-supplied" | "disabled";
5479
4675
  /**
5480
- * The replay-store backend resolved at host-boot time (internal-review). Surfaced in
5481
- * the boot summary so operators see which protection is active.
5482
- *
5483
- * - `postgres` — SDK auto-wired a `PostgresReplayStore` against the host's
5484
- * pool. Cross-machine safe.
5485
- * - `memory` — SDK fell back to the in-memory FIFO. Per-process; unsafe for
5486
- * multi-machine deploys. `createDVMHost` emits a warning (or throws under
5487
- * `DVMKIT_FAIL_FAST=true`) when this kicks in outside `devMode`.
5488
- * - `user-supplied` — user passed an explicit `replayStore` at factory time.
5489
- * SDK doesn't override.
5490
- * - `disabled` — user passed `replayStore: null`. No replay tracking.
4676
+ * Auth runs before payment; replay commits after accepted upfront payment.
4677
+ * Custom schemes must validate builder input while tolerating signed protocol
4678
+ * fields (credit, recovery and auth statement) outside the builder schema.
5491
4679
  */
5492
- type ReplayStoreBackend = "postgres" | "memory" | "user-supplied" | "disabled";
5493
4680
  interface DVMAuthScheme {
5494
4681
  /** Wire-level identifier surfaced on `/v1/info#auth`. */
5495
4682
  readonly id: typeof SIGNED_REQUEST_AUTH_ID;
@@ -5499,19 +4686,15 @@ interface DVMAuthScheme {
5499
4686
  envelope: T & CanonicalEnvelope;
5500
4687
  };
5501
4688
  /**
5502
- * Re-verify the signature on a persisted envelope at reactivation time
5503
- * (internal-review). Schema + signature only — no drift check, no replay-store
5504
- * touch. Catches DB-layer tampering: persisted `record.input` would
5505
- * otherwise be trusted as authentic, and a compromised row could feed an
5506
- * attacker-supplied pubkey/envelope into the handler. Throws
5507
- * {@link SignedRequestError} with `sub_reason: "signature_invalid"` or
5508
- * `"schema_invalid"` on failure.
4689
+ * Reactivation validates schema and signature without timestamp drift or replay
4690
+ * checks: the original timestamp and already-committed nonce must remain usable.
4691
+ * Throws signature_invalid or schema_invalid on failed validation.
5509
4692
  */
5510
4693
  recheckSignature<T>(schema: ZodLike<T>, input: unknown, domain?: SignedRequestDomain): void;
5511
4694
  /** Commit the envelope's `(pubkey, timestamp, nonce)` to the replay store. Throws on duplicate. */
5512
4695
  recordReplay(envelope: CanonicalEnvelope): Promise<void>;
5513
4696
  /**
5514
- * Called by `createDVMHost` after the Postgres pool is resolved (internal-review).
4697
+ * Called by `createDVMHost` after the Postgres pool is resolved.
5515
4698
  * The SDK passes a Postgres store when one is available; the scheme adopts
5516
4699
  * it iff the user did not supply `replayStore` explicitly at factory time.
5517
4700
  * Returns the resolved backend for boot-summary logging. User code should
@@ -5521,20 +4704,11 @@ interface DVMAuthScheme {
5521
4704
  postgresStore?: SignedRequestReplayStore;
5522
4705
  }): ReplayStoreBackend;
5523
4706
  }
5524
- /** Options for `secp256k1Auth()`. */
5525
4707
  interface Secp256k1AuthOpts {
5526
4708
  /**
5527
- * Override the replay-window store. Most callers should omit this opt:
5528
- * `createDVMHost` auto-wires a cross-machine `PostgresReplayStore` when a
5529
- * pool is available, and falls back to a bounded in-memory FIFO (10-min
5530
- * window, 100k cap) only when no pool exists — emitting a warning (or
5531
- * throwing under `DVMKIT_FAIL_FAST=true`) outside `devMode` so multi-machine
5532
- * deploys can't silently inherit the per-process default (internal-review).
5533
- *
5534
- * Pass a custom `SignedRequestReplayStore` here only to override that
5535
- * default (e.g. a different cross-machine backend), or pass `null` to
5536
- * disable replay tracking entirely. Any explicit value — including `null` —
5537
- * locks the choice: the host-boot wiring never overrides it.
4709
+ * Omitting this lets the host adopt Postgres, otherwise falling back to a
4710
+ * bounded per-process store with a warning or fail-fast refusal outside dev mode.
4711
+ * Any explicit store, including null to disable replay tracking, is locked.
5538
4712
  */
5539
4713
  replayStore?: SignedRequestReplayStore | null;
5540
4714
  /** Permitted clock drift in seconds. Defaults to 300 (5 minutes). */
@@ -5543,31 +4717,16 @@ interface Secp256k1AuthOpts {
5543
4717
  now?: () => number;
5544
4718
  }
5545
4719
  /**
5546
- * Build a descriptor-level secp256k1+BIP-340 Schnorr auth scheme (internal-review,
5547
- * internal-review).
5548
- *
5549
- * Declare on `configureDVM({ auth: secp256k1Auth({...}) })` to gate every
5550
- * capability's `/v1/job` and `/v1/quote` on a signed request envelope
5551
- * (`pubkey` / `signature` / `timestamp` / `nonce`) over the canonical JSON
5552
- * form of the capability's input. The SDK calls {@link DVMAuthScheme.checkAuth}
5553
- * at request entry on both endpoints, and {@link DVMAuthScheme.recordReplay}
5554
- * on `/v1/job` only — after upfront payment is verified, just before the job
5555
- * is created. The 402-discovery hop on `/v1/job` therefore does not consume
5556
- * the replay nonce; the same signed envelope is accepted on the paid resubmit.
5557
- *
5558
- * One scheme instance covers every capability under the DVM — internally
5559
- * the SDK caches a per-schema verifier so each capability's Zod schema is
5560
- * compiled once. The replay store is shared across all capabilities.
4720
+ * One scheme covers all capabilities and shares their replay store. Per-schema
4721
+ * verifiers validate without touching replay state; the host commits replay
4722
+ * only after upfront payment verification, immediately before job creation.
5561
4723
  */
5562
4724
  declare function secp256k1Auth(opts?: Secp256k1AuthOpts): DVMAuthScheme;
5563
4725
 
5564
4726
  /** Structural type matching any Zod schema. Avoids a hard dependency on zod. */
5565
4727
  interface ZodLike<T = unknown> {
5566
- /** Internal Zod metadata (used for duck-type detection). */
5567
4728
  _zod: unknown;
5568
- /** Parse and validate data, throwing on failure. */
5569
4729
  parse(data: unknown): T;
5570
- /** Parse and validate data, returning a result object instead of throwing. */
5571
4730
  safeParse(data: unknown): {
5572
4731
  success: true;
5573
4732
  data: T;
@@ -5576,28 +4735,13 @@ interface ZodLike<T = unknown> {
5576
4735
  error: unknown;
5577
4736
  };
5578
4737
  }
5579
- /** Extract the output type from a ZodLike schema, or default to string. */
5580
4738
  type InputType<T> = T extends ZodLike<infer O> ? O : string;
5581
4739
  /**
5582
- * Narrowed type for capability `example` payloads (internal-review).
5583
- *
5584
- * Surfaces the Zod schema's output type so a drifted example produces a
5585
- * TypeScript error at the `configureDVM` call site. Falls back to
5586
- * `Record<string, unknown>` when `InputSchema` is undefined or a plain JSON
5587
- * Schema object (which can't validate at type-level; the runtime
5588
- * `validateExampleAgainstInput` in `configure.ts` covers schema-less paths).
5589
- *
5590
- * Approximation: `ZodLike<T>` exposes only one type param. True `z.input`
5591
- * distinguishes pre-coercion input from post-coercion output for schemas
5592
- * that use `.transform()` or `z.coerce.*`. None of the first-party DVMs do
5593
- * that on their input schemas today. Revisit — extend `ZodLike` to a
5594
- * two-param shape `ZodLike<Output, Input = Output>` — if a builder ever
5595
- * hits a real divergence on `example`.
4740
+ * Zod examples use the schema output type, including transforms. Plain JSON Schema examples have
4741
+ * no equivalent static validation.
5596
4742
  */
5597
4743
  type ExampleType<InputSchema> = [InputSchema] extends [ZodLike<infer O>] ? O : Record<string, unknown>;
5598
- /** Runtime check for whether a value is a Zod schema. */
5599
4744
  declare function isZodSchema(value: unknown): value is ZodLike;
5600
- /** Structured logging interface injected via ctx.log. */
5601
4745
  interface Logger {
5602
4746
  debug(message: string, data?: Record<string, unknown>): void;
5603
4747
  info(message: string, data?: Record<string, unknown>): void;
@@ -5612,21 +4756,17 @@ interface KVStore {
5612
4756
  set(key: string, value: unknown, opts?: {
5613
4757
  ttl?: number;
5614
4758
  }): Promise<void>;
5615
- /** Delete a key. */
5616
4759
  delete(key: string): Promise<void>;
5617
4760
  /** List keys, optionally filtered by prefix. */
5618
4761
  list(prefix?: string): Promise<string[]>;
5619
4762
  }
5620
- /** Options for ctx.prompt(). */
5621
4763
  interface PromptOpts {
5622
4764
  /** Preset choices the client can select from. */
5623
4765
  options?: string[];
5624
4766
  /** JSON Schema describing the expected structured response. */
5625
4767
  schema?: Record<string, unknown>;
5626
4768
  }
5627
- /** Options for ctx.requestPayment(). */
5628
4769
  interface SDKPaymentRequestOpts {
5629
- /** Cashu mints accepted for this payment. */
5630
4770
  mints?: string[];
5631
4771
  /** Minimum P2PK locktime in seconds. */
5632
4772
  minLocktimeSeconds?: number;
@@ -5642,11 +4782,11 @@ interface SDKPaymentRequestOpts {
5642
4782
  * pre-issued mppx challenge per accepted method/intent. Overrides the
5643
4783
  * SDK's auto-issued challenges; if omitted, and the SDK is configured for
5644
4784
  * MPP, challenges are issued automatically. Each challenge's `id` becomes
5645
- * part of this job's per-job replay binding (internal-review).
4785
+ * part of this job's per-job replay binding.
5646
4786
  *
5647
4787
  * `session`-intent challenges are dropped from this list, whoever issued
5648
4788
  * them: reusable channels fund credits on `POST /v1/credit` and are refused
5649
- * everywhere on the job surface (internal-review).
4789
+ * everywhere on the job surface.
5650
4790
  */
5651
4791
  mpp?: {
5652
4792
  challenges: MppxChallenge[];
@@ -5679,22 +4819,18 @@ interface JobCost {
5679
4819
  /** ISO 4217 lowercase, e.g. `"usd"`. */
5680
4820
  currency: Currency;
5681
4821
  }
5682
- /** Extended job context provided by the SDK runtime to DVM handlers. */
5683
4822
  interface SDKJobContext<State = Record<string, unknown>, Input = string> {
5684
- /** Server-assigned job ID. */
5685
4823
  readonly jobId: string;
5686
- /** Tags from the DVM descriptor (what this DVM handles). */
5687
4824
  readonly tags: string[];
5688
4825
  /** Job input — raw string or parsed/validated object when an input schema is defined. */
5689
4826
  readonly input: Input;
5690
- /** Job parameters as key-value pairs. */
5691
4827
  readonly params: Record<string, string>;
5692
4828
  /** Opaque requester identifier (derived from auth token or "anonymous"). */
5693
4829
  readonly requesterId: string;
5694
4830
  /** Total msats paid so far for this job. */
5695
4831
  readonly paidMsats: number;
5696
4832
  /**
5697
- * Verified caller identity from the descriptor's `auth` scheme (internal-review).
4833
+ * Verified caller identity from the descriptor's `auth` scheme.
5698
4834
  * Present when the DVM declares `auth: secp256k1Auth(...)`; undefined for
5699
4835
  * DVMs without descriptor-level auth. The envelope is the same `T &
5700
4836
  * CanonicalEnvelope` the auth scheme produced — handlers can read
@@ -5704,24 +4840,17 @@ interface SDKJobContext<State = Record<string, unknown>, Input = string> {
5704
4840
  pubkey: string;
5705
4841
  envelope: CanonicalEnvelope & Record<string, unknown>;
5706
4842
  };
5707
- /** Send a progress text message to the client. */
5708
4843
  text(message: string): void;
5709
- /** Send an artifact (output data) to the client. */
5710
4844
  artifact(content: ArtifactContent): void;
5711
4845
  /** Send a raw typed message (escape hatch). */
5712
4846
  sendMessage(type: MessageType, content: Record<string, unknown>): void;
5713
4847
  /** Ask the client a question. Resolves when the client responds. */
5714
4848
  prompt(id: string, text: string, opts?: PromptOpts): Promise<ResponseContent>;
5715
4849
  /**
5716
- * Request payment from the client. Resolves when payment is received.
5717
- *
5718
- * - `number` = msats raw (sats-native operators).
5719
- * - `{ amount, currency }` = fiat envelope; the SDK converts to sats at
5720
- * send-time via its centralised fx fetcher. v1 supports `currency: "usd"`
5721
- * only; other currencies throw `UnsupportedCurrencyError`.
5722
- *
5723
- * The legacy `string` form (`"$0.05"`) was removed in internal-review under the
5724
- * project's no-shims convention. Migrate callers to the fiat envelope.
4850
+ * Request payment; resolves when the ask is satisfied by the upfront pool or a payment.
4851
+ * Numbers are millisatoshis. Fiat envelopes use major units in the supplied
4852
+ * supported currency; the ledger pin is converted into the job's denomination.
4853
+ * x402 is offered only for USD; other configured rails can use supported FX currencies.
5725
4854
  */
5726
4855
  requestPayment(amount: number | {
5727
4856
  amount: number;
@@ -5742,14 +4871,14 @@ interface SDKJobContext<State = Record<string, unknown>, Input = string> {
5742
4871
  /**
5743
4872
  * Fail the job with an error message. No debit happens either way — the
5744
4873
  * terminal funnel releases the credit draw ({@link RAIL_REFUNDABLE}).
5745
- * `{ refund: true }` annotates the failure as caller-fault (internal-review); it
4874
+ * `{ refund: true }` annotates the failure as caller-fault; it
5746
4875
  * moves no money on any rail and never has.
5747
4876
  */
5748
4877
  fail(error: string, opts?: {
5749
4878
  refund?: boolean;
5750
4879
  }): void;
5751
4880
  /**
5752
- * Declare what this job cost **you**, the builder, to serve (internal-review).
4881
+ * Declare what this job cost **you**, the builder, to serve.
5753
4882
  *
5754
4883
  * The SDK reports it to the platform alongside the job's revenue, so the
5755
4884
  * dashboard can state a margin. Without it the margin is unknowable, and the
@@ -5777,9 +4906,9 @@ interface SDKJobContext<State = Record<string, unknown>, Input = string> {
5777
4906
  *
5778
4907
  * ```ts
5779
4908
  * const r = await ctx.step("transcribe", async () => {
5780
- * const result = await whisper(audio);
5781
- * ctx.cost({ amount: result.seconds * 0.0001, currency: "usd" });
5782
- * return result;
4909
+ * const result = await whisper(audio);
4910
+ * ctx.cost({ amount: result.seconds * 0.0001, currency: "usd" });
4911
+ * return result;
5783
4912
  * });
5784
4913
  * ```
5785
4914
  *
@@ -5805,7 +4934,7 @@ interface SDKJobContext<State = Record<string, unknown>, Input = string> {
5805
4934
  step<T>(id: string, fn: () => Promise<T>): Promise<T>;
5806
4935
  /** Per-job typed state, auto-loaded and auto-persisted. */
5807
4936
  state: State;
5808
- /** Persistent cross-job KV storage. */
4937
+ /** Persistent storage shared across jobs. */
5809
4938
  store: KVStore;
5810
4939
  /**
5811
4940
  * Instrumented fetch — emits `dvm.fetch` spans with redacted URLs, and
@@ -5814,7 +4943,7 @@ interface SDKJobContext<State = Record<string, unknown>, Input = string> {
5814
4943
  */
5815
4944
  fetch: typeof globalThis.fetch;
5816
4945
  /**
5817
- * Cancellation signal for this job (internal-review). Aborts when the caller
4946
+ * Cancellation signal for this job. Aborts when the caller
5818
4947
  * cancels, the job idles out, or the handler is superseded. `ctx.fetch`
5819
4948
  * already carries it; pass it into anything else that can be torn down —
5820
4949
  * a provider SDK's `signal` option, a `spawn`, your own sleeps — so a
@@ -5822,24 +4951,20 @@ interface SDKJobContext<State = Record<string, unknown>, Input = string> {
5822
4951
  * / `ctx.fail` are no-ops: the job stays `cancelled`.
5823
4952
  */
5824
4953
  readonly signal: AbortSignal;
5825
- /** Environment variables configured by the operator. */
5826
4954
  env: Record<string, string>;
5827
- /** Structured logging. */
5828
4955
  log: Logger;
5829
4956
  }
5830
- /** Context provided to the onQuote handler. */
5831
4957
  interface QuoteContext<QuoteData = Record<string, unknown>> {
5832
- /** Parsed quote data — typed when a Zod schema is provided. */
4958
+ /** Quote data, parsed by the configured Zod schema when present. */
5833
4959
  data: QuoteData;
5834
- /** Raw input string from the client, if provided. */
4960
+ /** The client's raw input string, when supplied. */
5835
4961
  input?: string;
5836
- /** Job parameters from the client, if provided. */
5837
4962
  params?: Record<string, string>;
5838
4963
  /**
5839
4964
  * Operator environment — same object threaded into `JobContext.env`.
5840
4965
  * Quote handlers read it for pre-payment checks (API-key presence, feature
5841
4966
  * flags, model overrides) so failures surface as a structured 400 rather
5842
- * than after the upfront fee has been debited. internal-review.
4967
+ * than after the upfront fee has been debited.
5843
4968
  */
5844
4969
  env: Record<string, string>;
5845
4970
  /**
@@ -5849,16 +4974,16 @@ interface QuoteContext<QuoteData = Record<string, unknown>> {
5849
4974
  */
5850
4975
  fetch: typeof globalThis.fetch;
5851
4976
  /**
5852
- * Verified caller pubkey from the signed-request envelope (internal-review, credits
5853
- * spec §7). Present only when the descriptor declares an auth scheme and the
4977
+ * Verified caller pubkey from the signed-request envelope. Present only when
4978
+ * the descriptor declares an auth scheme and the
5854
4979
  * envelope verified — `/v1/quote` runs `checkAuth` before the handler, so by
5855
4980
  * the time this is set the signature is good. Undefined on unauthenticated
5856
4981
  * DVMs; quote handlers that price per-caller must handle that.
5857
4982
  */
5858
4983
  callerPubkey?: string;
5859
4984
  /**
5860
- * The authenticated caller's standing prepaid credit at this DVM, if any
5861
- * (internal-review, spec §7) — so one round trip answers "cost + my remaining
4985
+ * The authenticated caller's standing prepaid credit at this DVM, if any,
4986
+ * so one round trip answers "cost + my remaining
5862
4987
  * credit". Undefined when the DVM doesn't offer credit, the caller is
5863
4988
  * unauthenticated, or they hold no live credit in this DVM's currency.
5864
4989
  *
@@ -5869,8 +4994,8 @@ interface QuoteContext<QuoteData = Record<string, unknown>> {
5869
4994
  credit?: CreditView;
5870
4995
  }
5871
4996
  /**
5872
- * Read-only snapshot of a caller's credit, surfaced on {@link QuoteContext}
5873
- * (internal-review). Mirrors the ledger's `CreditSnapshot` minus the write surface
4997
+ * Read-only snapshot of a caller's credit, surfaced on {@link QuoteContext};
4998
+ * it mirrors the ledger's `CreditSnapshot` minus the write surface
5874
4999
  * and the fields a quote handler has no business branching on.
5875
5000
  */
5876
5001
  interface CreditView {
@@ -5888,7 +5013,7 @@ interface CreditView {
5888
5013
  expired: boolean;
5889
5014
  }
5890
5015
  /**
5891
- * Result returned by the onQuote handler — the fiat-first envelope (internal-review).
5016
+ * Result returned by the onQuote handler — the fiat-first envelope.
5892
5017
  *
5893
5018
  * The protocol enforces exactly one hard invariant — `upfront` — and pushes
5894
5019
  * every other pricing detail into prose. LLM-agent intermediaries read
@@ -5955,35 +5080,29 @@ interface QuoteResult {
5955
5080
  max_units?: number;
5956
5081
  };
5957
5082
  }
5958
- /** Quote handler configuration with a Zod schema for structured quote data. */
5959
5083
  interface QuoteConfig<QuoteData = Record<string, unknown>> {
5960
- /** Schema describing the data clients must provide for a quote. */
5961
5084
  schema: ZodLike<QuoteData> | Record<string, unknown>;
5962
- /** Compute a price based on the structured quote data. */
5963
5085
  handler(ctx: QuoteContext<QuoteData>): Promise<QuoteResult>;
5964
5086
  }
5965
- /** Incoming message shape for the onMessage catch-all handler. */
5966
5087
  interface IncomingMessage {
5967
5088
  /** Message type (e.g. "response", "payment", "approval", "cancel"). */
5968
5089
  type: MessageType;
5969
- /** Raw message content. */
5970
5090
  content: Record<string, unknown>;
5971
5091
  }
5972
5092
  /**
5973
5093
  * A builder-declared price — a USD literal, always (`"$0.05"`).
5974
5094
  *
5975
- * Fiat is the only static price form (internal-review). The credit ledger is
5095
+ * Fiat is the only static price form. The credit ledger is
5976
5096
  * fiat-micro denominated, so a raw-msats price could only reach the ledger
5977
5097
  * through a BTC/USD lookup that may be unavailable — which is how a
5978
5098
  * numeric-priced DVM under a cold-start rate outage used to take money and
5979
- * record no draw (internal-review). Rails are unaffected: callers still pay sats,
5099
+ * record no draw. Rails are unaffected: callers still pay sats,
5980
5100
  * the SDK converts at request time.
5981
5101
  */
5982
5102
  type PriceValue = string;
5983
- /** Accepted payment rail identifier. */
5984
5103
  type PaymentMethod = "cashu" | "x402" | "tempo";
5985
5104
  /**
5986
- * Rails a prepaid **credit** may be funded over (internal-review, credits spec §4).
5105
+ * Rails that may fund a prepaid **credit**.
5987
5106
  *
5988
5107
  * A deliberate superset of {@link PaymentMethod}, not a widening of it.
5989
5108
  * `lightning` funds a credit through the interactive invoice leg on
@@ -5991,59 +5110,40 @@ type PaymentMethod = "cashu" | "x402" | "tempo";
5991
5110
  * connection and credits when settlement is observed — but it is **not** an
5992
5111
  * attached-proof wire method: there is no header a caller can hang a bolt11
5993
5112
  * on, nothing to verify inline, and no `lightning` entry in `/v1/info` or
5994
- * `/v1/quote`'s `payment.methods` (internal-review stands; internal-review deleted the last
5995
- * remnants of the per-call surface). Keeping the two types distinct is what
5113
+ * `/v1/quote`'s `payment.methods`. Keeping the two types distinct is what
5996
5114
  * stops that dead rail from reappearing every time the funding menu grows.
5997
5115
  */
5998
5116
  type FundingMethod = PaymentMethod | "lightning";
5999
5117
  /**
6000
- * Whether a failed job on a given rail can return the caller's funds (internal-review).
6001
- *
6002
- * **No rail refunds on `ctx.fail`, and no code behind the idea any more.** Under
6003
- * the P2PK-accumulator Cashu path — the only Cashu path post-internal-review/internal-review —
6004
- * `verifyAccumulatorReceipt` commits the caller's proofs straight into
6005
- * `wallet_accumulator` and leaves `job.receivedProofs` empty; every producer in
6006
- * `payment.ts` returns `[]`. The `ctx.fail(err, { refund: true })` branch that
6007
- * used to send proofs back gated on those held proofs, so it had been an
6008
- * unreachable no-op for as long as the accumulator has been the commit boundary,
6009
- * and internal-review deleted it. This fail-closed posture was locked by internal-review
6010
- * (Decision C) and pinned by internal-review. mpp (Tempo) and x402 credentials are
6011
- * single-use, final-settlement — no SDK-managed reversal path either.
6012
- *
6013
- * **This is a rail-level question, and since internal-review it is no longer the whole
6014
- * story.** A failed job never debits: the draw is a hold the terminal funnel
6015
- * releases (`JobManager.resolveCreditDraw`), so the value stays on the caller's
6016
- * credit. What this flag reports is narrower and still true — no rail hands
6017
- * value backwards — and reclaiming a released balance runs through the
6018
- * internal-review `drain` op, which exists only on a DVM that advertises credit.
5118
+ * Whether the payment rail returns value when a job fails. No rail reverses
5119
+ * `ctx.fail`: Cashu proofs are committed to the accumulator; Tempo and x402
5120
+ * credentials settle without an SDK reversal.
6019
5121
  *
6020
- * `{ refund: true }` survives as a **caller-fault annotation only** — first-party
6021
- * handlers tag which errors were the caller's doing, and `sdk/testing`'s
6022
- * `createTestContext` exposes it — and it is not a hook waiting to be re-wired.
6023
- * internal-review, the accumulator-debit rewrite that would have flipped `cashu` back to
6024
- * `true`, was cancelled as superseded: the ledger is where reclaim lives now.
6025
- * x402 refund parity remains a deferred internal-review exploration.
5122
+ * Failed credit jobs release their draw hold back to the caller's balance.
5123
+ * Reclaiming that balance uses `drain`; this flag describes rail reversal,
5124
+ * independently of the credit ledger. `{ refund: true }` records caller fault
5125
+ * and does not move funds.
6026
5126
  */
6027
5127
  declare const RAIL_REFUNDABLE: Record<PaymentMethod, boolean>;
6028
5128
  /**
6029
- * Cashu receive mode for builder DVMs (internal-review).
5129
+ * Cashu receive mode for builder DVMs.
6030
5130
  *
6031
5131
  * `"p2pk-accumulator"` — accept P2PK-locked proofs from agent wallets and persist
6032
5132
  * them in `wallet_accumulator`. Melting runs builder-local via `dvmctl melt-pending`
6033
- * (internal-review / internal-review); the SDK only monitors batches for `t_expire` proximity.
5133
+ * the SDK only monitors batches for `t_expire` proximity.
6034
5134
  */
6035
5135
  type CashuMode = "p2pk-accumulator";
6036
5136
  /**
6037
- * Prepaid-credit sizing this DVM advertises (internal-review, credits spec §6).
5137
+ * Prepaid-credit sizing this DVM advertises.
6038
5138
  *
6039
5139
  * Declaring the block is the opt-in: a DVM without `credit` never offers the
6040
5140
  * funding menu on `/v1/quote` or the 402, and `/v1/credit` refuses with
6041
5141
  * `credit_not_supported`. Every payment still funds and draws the ledger
6042
- * internally (the implicit N=1 case, internal-review) — this block is only about
5142
+ * internally (the implicit N=1 case) — this block is only about
6043
5143
  * whether callers may hold a balance across jobs.
6044
5144
  *
6045
5145
  * Isolate-runtime DVMs never advertise credit regardless of this config
6046
- * (spec §3): they have no Postgres of their own, so the money-safety
5146
+ * (the isolate-runtime rule): they have no Postgres of their own, so the money-safety
6047
5147
  * conditions the ledger relies on can't hold there.
6048
5148
  */
6049
5149
  interface CreditConfig {
@@ -6058,14 +5158,14 @@ interface CreditConfig {
6058
5158
  min?: PriceValue;
6059
5159
  /**
6060
5160
  * Largest **residual balance** a caller may hold, as a `PriceValue`.
6061
- * Binds the balance, not the funding amount (spec §6): a job priced above
5161
+ * Binds the balance, not the funding amount (the balance-sizing contract): a job priced above
6062
5162
  * `max` still clears as fund-and-immediately-draw. Bounds the DVM's
6063
5163
  * prepaid-deposit liability. Default `"$5.00"`.
6064
5164
  */
6065
5165
  max?: PriceValue;
6066
5166
  /**
6067
5167
  * Credit lifetime in **seconds**, from the most recent funding. Post-TTL
6068
- * the credit refuses new draws; the balance stays the caller's (spec §5).
5168
+ * the credit refuses new draws; the balance stays the caller's (the caller-ownership contract).
6069
5169
  * Default 30 days.
6070
5170
  */
6071
5171
  ttl?: number;
@@ -6099,17 +5199,17 @@ interface ResolvedCreditConfig {
6099
5199
  declare const DEFAULT_CREDIT_MIN: PriceValue;
6100
5200
  /**
6101
5201
  * Platform default for {@link CreditConfig.max} — the largest residual balance
6102
- * a caller may hold. Pocket-money sizing (spec §11: prepaid exposure is
5202
+ * a caller may hold. Pocket-money sizing (the exposure limit: prepaid exposure is
6103
5203
  * capped by policy at pocket-money size), set above the caller-side tier-1
6104
5204
  * cap of $2 so an established caller can actually reach the builder ceiling.
6105
5205
  */
6106
5206
  declare const DEFAULT_CREDIT_MAX: PriceValue;
6107
- /** Platform default for {@link CreditConfig.ttl} — 30 days (credits spec §15, ratified). */
5207
+ /** Platform default for {@link CreditConfig.ttl}: 30 days. */
6108
5208
  declare const DEFAULT_CREDIT_TTL_SECONDS: number;
6109
5209
  /** Default lifetime of caller-controlled job content before it is redacted. */
6110
5210
  declare const DEFAULT_JOB_RETENTION_DAYS = 90;
6111
5211
  /**
6112
- * Per-capability configuration for the multi-capability shape (internal-review).
5212
+ * Per-capability configuration for the multi-capability shape.
6113
5213
  * Each capability is an independent unit a caller pays for: its own input
6114
5214
  * schema, pricing, handler, and (optional) message handlers. DVM-level
6115
5215
  * concerns (auth, payment rails, tags, builder) stay on the parent
@@ -6121,13 +5221,13 @@ interface CapabilityConfig<State = Record<string, unknown>, InputSchema extends
6121
5221
  /** Zod schema for structured job input. When set, ctx.input is parsed and typed. */
6122
5222
  input?: InputSchema;
6123
5223
  /**
6124
- * Example input payload surfaced on `/v1/info` (internal-review). Lets the CLI render
5224
+ * Example input payload surfaced on `/v1/info`. Lets the CLI render
6125
5225
  * a runnable `dvm quote --data '<payload>'` line when the JSON Schema alone
6126
5226
  * is ambiguous — e.g. cross-field Zod refinements that don't survive
6127
5227
  * serialization (narrate's `text` XOR `dialogue`). Must satisfy the schema
6128
5228
  * including any `.refine()` checks.
6129
5229
  *
6130
- * Typed via `ExampleType<InputSchema>` (internal-review): narrows to the schema's
5230
+ * Typed via `ExampleType<InputSchema>`: narrows to the schema's
6131
5231
  * output type when `input` is a Zod schema, falls back to
6132
5232
  * `Record<string, unknown>` otherwise.
6133
5233
  */
@@ -6136,7 +5236,6 @@ interface CapabilityConfig<State = Record<string, unknown>, InputSchema extends
6136
5236
  state?: State;
6137
5237
  /** Static price — a USD literal ("$0.05"). Free capabilities omit this and `onQuote`. */
6138
5238
  price?: PriceValue;
6139
- /** Quote handler for dynamic pricing. */
6140
5239
  onQuote?: QuoteConfig;
6141
5240
  /** Handle a newly submitted job for this capability (required). */
6142
5241
  onJob: (ctx: SDKJobContext<State, InputType<InputSchema>>) => void | Promise<void>;
@@ -6152,7 +5251,7 @@ interface CapabilityConfig<State = Record<string, unknown>, InputSchema extends
6152
5251
  onMessage?: (ctx: SDKJobContext<State, InputType<InputSchema>>, msg: IncomingMessage) => void | Promise<void>;
6153
5252
  }
6154
5253
  /**
6155
- * Desugared per-capability descriptor (internal-review). `configureDVM` produces one
5254
+ * Desugared per-capability descriptor. `configureDVM` produces one
6156
5255
  * of these per capability — the SDK runtime always operates on this shape.
6157
5256
  */
6158
5257
  interface CapabilityDescriptor<State = unknown, InputSchema extends ZodLike | undefined = undefined> {
@@ -6162,15 +5261,13 @@ interface CapabilityDescriptor<State = unknown, InputSchema extends ZodLike | un
6162
5261
  readonly description: string;
6163
5262
  /** Zod schema for structured job input. */
6164
5263
  readonly input?: InputSchema;
6165
- /** Example input payload surfaced on `/v1/info` (internal-review). See `CapabilityConfig.example`. */
5264
+ /** Example input payload surfaced on `/v1/info`. See `CapabilityConfig.example`. */
6166
5265
  readonly example?: ExampleType<InputSchema>;
6167
5266
  /** Default per-job state for jobs of this capability. */
6168
5267
  readonly state: State;
6169
5268
  /** Static price — a USD literal ("$0.05"). Undefined for dynamic-priced or free. */
6170
5269
  readonly price?: PriceValue;
6171
- /** Quote handler for dynamic pricing. */
6172
5270
  readonly onQuote?: QuoteConfig;
6173
- /** Handle a newly submitted job. */
6174
5271
  readonly onJob: (ctx: SDKJobContext<State, InputType<InputSchema>>) => void | Promise<void>;
6175
5272
  /** Handle a client response to a prompt. */
6176
5273
  readonly onResponse?: (ctx: SDKJobContext<State, InputType<InputSchema>>, content: ResponseContent) => void | Promise<void>;
@@ -6186,30 +5283,28 @@ interface CapabilityDescriptor<State = unknown, InputSchema extends ZodLike | un
6186
5283
  /**
6187
5284
  * Configuration object passed to configureDVM().
6188
5285
  *
6189
- * Two equivalent shapes (internal-review):
5286
+ * Two equivalent shapes:
6190
5287
  *
6191
5288
  * - **Flat single-capability shape** — declare `capability: "<name>"` plus
6192
- * the per-capability fields (`input`, `state`, `price`/`onQuote`, `onJob`,
6193
- * `onResponse`, …) at the top level. Desugars internally to a one-entry
6194
- * `capabilities` map. Ergonomic for the common case (narrate, scribe,
6195
- * scrape).
5289
+ * the per-capability fields (`input`, `state`, `price`/`onQuote`, `onJob`,
5290
+ * `onResponse`, …) at the top level. Desugars internally to a one-entry
5291
+ * `capabilities` map. Ergonomic for the common case (narrate, scribe,
5292
+ * scrape).
6196
5293
  *
6197
5294
  * - **Explicit multi-capability shape** — declare `capabilities: { "<name>":
6198
- * { input, onJob, … } }`. Used by services that expose several operations
6199
- * on a shared resource model (cast's add-episode / update-feed /
6200
- * delete-episode / delete-feed).
5295
+ * { input, onJob, … } }`. Used by services that expose several operations
5296
+ * on a shared resource model (cast's add-episode / update-feed /
5297
+ * delete-episode / delete-feed).
6201
5298
  *
6202
5299
  * The two are mutually exclusive: providing both throws at `configureDVM`.
6203
5300
  * DVM-level concerns (tags, payment rails, idle timeout, lifecycle,
6204
5301
  * descriptor routes) live on `DVMConfig` regardless of shape.
6205
5302
  */
6206
5303
  interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike | undefined = undefined> {
6207
- /** Display name for the DVM. */
6208
5304
  name: string;
6209
- /** Human-readable description. */
6210
5305
  description?: string;
6211
5306
  /**
6212
- * Single-capability name for the flat shape (internal-review). Mutually exclusive
5307
+ * Single-capability name for the flat shape. Mutually exclusive
6213
5308
  * with `capabilities`. Single-capability DVMs declare it here and put the
6214
5309
  * per-capability fields (`input`, `state`, `price`/`onQuote`, `onJob`,
6215
5310
  * `onResponse`, …) at the top level; `configureDVM` desugars to a
@@ -6217,7 +5312,7 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
6217
5312
  */
6218
5313
  capability?: string;
6219
5314
  /**
6220
- * Explicit multi-capability block (internal-review). Mutually exclusive with the
5315
+ * Explicit multi-capability block. Mutually exclusive with the
6221
5316
  * flat shape's `capability` + top-level handler fields. Each key is a
6222
5317
  * capability name (tag-shape: lowercase letters/digits/hyphens, no
6223
5318
  * slashes); each value carries its own `input`, `state`, `price`/`onQuote`,
@@ -6230,7 +5325,7 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
6230
5325
  tags?: string[];
6231
5326
  /**
6232
5327
  * The currency this DVM prices in — and therefore the one its credit ledger
6233
- * is denominated in (internal-review). ISO 4217 lowercase; default `"usd"`.
5328
+ * is denominated in. ISO 4217 lowercase; default `"usd"`.
6234
5329
  *
6235
5330
  * The single authoritative answer to "what denomination does this DVM deal
6236
5331
  * in", read by the funding menu, `POST /v1/credit`, and the quote balance
@@ -6241,11 +5336,11 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
6241
5336
  * Two consequences, both enforced rather than documented:
6242
5337
  *
6243
5338
  * - **`onQuote` must return it.** A quote denominated in anything else is a
6244
- * builder config error, refused as `quote_currency_mismatch` — never
6245
- * coerced, never converted.
5339
+ * builder config error, refused as `quote_currency_mismatch` — never
5340
+ * coerced, never converted.
6246
5341
  * - **Static `price` literals are USD-only** (`"$0.05"` — that is the whole
6247
- * grammar), so declaring a non-USD currency alongside one is a
6248
- * `configureDVM` error. Price a non-USD DVM dynamically with `onQuote`.
5342
+ * grammar), so declaring a non-USD currency alongside one is a
5343
+ * `configureDVM` error. Price a non-USD DVM dynamically with `onQuote`.
6249
5344
  *
6250
5345
  * The rails are unaffected: callers still pay sats, and the SDK converts at
6251
5346
  * request time through whichever rate the fx fetcher carries for this
@@ -6256,7 +5351,7 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
6256
5351
  price?: PriceValue;
6257
5352
  /** Zod schema for structured job input. Flat shape only. */
6258
5353
  input?: InputSchema;
6259
- /** Example input payload surfaced on `/v1/info` (internal-review). Flat shape only — see `CapabilityConfig.example`. */
5354
+ /** Example input payload surfaced on `/v1/info`. Flat shape only — see `CapabilityConfig.example`. */
6260
5355
  example?: ExampleType<InputSchema>;
6261
5356
  /** Per-job state shape and defaults. Flat shape only. */
6262
5357
  state?: State;
@@ -6265,7 +5360,7 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
6265
5360
  /**
6266
5361
  * Seconds a job may sit in `processing`/`working` with no SDK liveness
6267
5362
  * heartbeat before the stale-job watchdog declares its worker dead and marks
6268
- * it `failed` (internal-review). The SDK heartbeats locally-active jobs every ~30s,
5363
+ * it `failed`. The SDK heartbeats locally-active jobs every ~30s,
6269
5364
  * so this bounds *crashed/OOM-killed-worker* detection — not legitimate
6270
5365
  * handler runtime, which the heartbeat keeps fresh regardless of how long it
6271
5366
  * runs. Distinct from `idleTimeout`, which governs the `awaiting-input`
@@ -6281,7 +5376,7 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
6281
5376
  */
6282
5377
  jobRetentionDays?: number;
6283
5378
  /**
6284
- * Descriptor-level auth scheme (internal-review). Fires on `/v1/job` and
5379
+ * Descriptor-level auth scheme. Fires on `/v1/job` and
6285
5380
  * `/v1/quote` before payment verification and handler dispatch — unsigned
6286
5381
  * or malformed envelopes reject with HTTP 401, no payment is attempted.
6287
5382
  *
@@ -6319,14 +5414,14 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
6319
5414
  /** MPP multi-rail payment handle (provider-side, built via `createMppFromOpts`). */
6320
5415
  mpp?: MppxServer;
6321
5416
  /**
6322
- * Prepaid-credit sizing (internal-review). Declaring the block opts this DVM into
5417
+ * Prepaid-credit sizing. Declaring the block opts this DVM into
6323
5418
  * the funding menu on `/v1/quote` / the 402 and into `POST /v1/credit`;
6324
5419
  * omitting it leaves callers on per-call payment. See {@link CreditConfig}.
6325
5420
  */
6326
5421
  credit?: CreditConfig;
6327
5422
  /**
6328
5423
  * Register DVM-scoped non-protocol routes on the descriptor's Hono sub-app
6329
- * (internal-review). Invoked once per `host.mount(dvm)` against the sub-app that
5424
+ * Invoked once per `host.mount(dvm)` against the sub-app that
6330
5425
  * receives `/v1/info`, `/v1/job`, etc. — any prefix passed to `host.mount`
6331
5426
  * applies automatically, so a route registered as `/feeds/:owner/:slug` on
6332
5427
  * a DVM mounted under `/cast` becomes `/cast/feeds/:owner/:slug` externally.
@@ -6349,14 +5444,12 @@ interface DVMRouteContext {
6349
5444
  }
6350
5445
  /** Immutable descriptor returned by configureDVM(). */
6351
5446
  interface DVMDescriptor<State = Record<string, unknown>, InputSchema extends ZodLike | undefined = undefined> {
6352
- /** Display name for the DVM. */
6353
5447
  readonly name: string;
6354
5448
  /** Human-readable description (empty string if not provided). */
6355
5449
  readonly description: string;
6356
- /** Freeform tags this DVM handles. */
6357
5450
  readonly tags: string[];
6358
5451
  /**
6359
- * Resolved pricing currency (internal-review) — `config.currency` after validation,
5452
+ * Resolved pricing currency — `config.currency` after validation,
6360
5453
  * or `"usd"` when the builder declared none. Always present, so no consumer
6361
5454
  * re-derives the default, and always the exact code the ledger compares
6362
5455
  * against (`"EUR"` is a `configureDVM` error, not a case to normalise: the
@@ -6385,14 +5478,14 @@ interface DVMDescriptor<State = Record<string, unknown>, InputSchema extends Zod
6385
5478
  readonly state: State;
6386
5479
  /** Seconds before an idle job is auto-cancelled. */
6387
5480
  readonly idleTimeout: number;
6388
- /** Seconds before a worker-stranded `processing`/`working` job is marked `failed` (internal-review). Undefined → SDK default (5 min). */
5481
+ /** Seconds before a worker-stranded `processing`/`working` job is marked `failed`. Undefined → SDK default (5 min). */
6389
5482
  readonly processingWatchdog?: number;
6390
5483
  /** Days terminal job content is retained; `0` disables automatic redaction. */
6391
5484
  readonly jobRetentionDays: number;
6392
5485
  /** Reference to the original DVMConfig (handler functions, quote config, etc.). */
6393
5486
  readonly config: DVMConfig<State, InputSchema>;
6394
5487
  /**
6395
- * Desugared per-capability descriptors (internal-review). The SDK runtime always
5488
+ * Desugared per-capability descriptors. The SDK runtime always
6396
5489
  * operates on this map: single-capability DVMs have one entry keyed by
6397
5490
  * `config.capability`; multi-capability DVMs have N entries with the
6398
5491
  * names declared in `config.capabilities`.
@@ -6410,24 +5503,22 @@ interface DVMDescriptor<State = Record<string, unknown>, InputSchema extends Zod
6410
5503
  /** MPP multi-rail payment handle. */
6411
5504
  readonly mpp?: MppxServer;
6412
5505
  /**
6413
- * Prepaid-credit sizing (internal-review), with every optional field resolved to
5506
+ * Prepaid-credit sizing, with every optional field resolved to
6414
5507
  * its platform default. Present iff `configureDVM` was called with a
6415
5508
  * `credit` block — the presence check IS the opt-in gate.
6416
5509
  */
6417
5510
  readonly credit?: ResolvedCreditConfig;
6418
5511
  /**
6419
- * Descriptor-level auth scheme (internal-review). Set when `configureDVM` was
5512
+ * Descriptor-level auth scheme. Set when `configureDVM` was
6420
5513
  * called with `auth: secp256k1Auth(...)` — the SDK fires this on
6421
5514
  * `/v1/job` and `/v1/quote` before payment verification.
6422
5515
  */
6423
5516
  readonly auth?: DVMAuthScheme;
6424
5517
  }
6425
5518
 
6426
- /** Serializable record of a completed step. */
6427
5519
  interface StepRecord {
6428
5520
  /** Step identifier (must be unique within a job). */
6429
5521
  id: string;
6430
- /** Cached return value of the step function. */
6431
5522
  value: unknown;
6432
5523
  /** Cost declarations made while the step body ran, replayed on cache hits. */
6433
5524
  costs?: JobCost[];
@@ -6442,18 +5533,12 @@ interface StepRecord {
6442
5533
  */
6443
5534
  declare class StepCache {
6444
5535
  private cache;
6445
- /** Check if a step result is cached. */
6446
5536
  has(id: string): boolean;
6447
- /** Get a cached step result. Throws if not present. */
6448
5537
  get(id: string): unknown;
6449
- /** Get a cached step record, including replay metadata. Throws if not present. */
6450
5538
  getRecord(id: string): StepRecord;
6451
- /** Cache a step result. */
6452
5539
  set(id: string, value: unknown, costs?: readonly JobCost[]): void;
6453
- /** Export all cached steps for persistence. */
6454
5540
  serialize(): StepRecord[];
6455
- /** Restore a StepCache from persisted records. */
6456
5541
  static deserialize(records: StepRecord[]): StepCache;
6457
5542
  }
6458
5543
 
6459
- export { type X402Version as $, type ApprovalContent as A, SignedRequestError as B, type Currency as C, type DVMConfig as D, type SignedRequestFailure as E, type SignedRequestReplayStore as F, type SignedRequestSignOpts as G, type SignedRequestStatementHeader as H, type IncomingMessage as I, type JobCost as J, type KVStore as K, type Logger as L, type SignedRequestVerifier as M, createSignedRequestVerifier as N, isZodSchema as O, type PaymentContent as P, type QuoteConfig as Q, type ResolvedCreditConfig as R, type SDKJobContext as S, signedRequestStatementHeader as T, UnsupportedCurrencyError as U, validateCurrency as V, type JsonValue as W, type FundingReceipt as X, type JobReceipt as Y, type ZodLike as Z, type PaymentRequired as _, type DVMDescriptor as a, isDrainReceipt as a$, type MppxCredential as a0, type Message as a1, type MessageType as a2, type MppxChallenge as a3, X402_BATCH_SETTLEMENT_SCHEME as a4, X402_EXACT_SCHEME as a5, type ReceiptCredit as a6, type X402Wallet as a7, type ResourceInfo as a8, type BuildPaymentRequirementsOpts as a9, type UnsignedFundingReceipt as aA, type UnsignedJobReceipt as aB, type VerifyResponse as aC, type WorkingContent as aD, type X402Config as aE, type X402ResponseBody as aF, type X402SelfRelayRpcFailureReason as aG, X402_DEFAULT_NETWORK as aH, X402_V1_VERSION as aI, X402_VERSION as aJ, buildPaymentRequiredV2 as aK, buildPaymentRequirements as aL, caip2ToX402Network as aM, canonicalRequestPath as aN, canonicaliseForSigning as aO, canonicalize as aP, chainIdFromCaip2 as aQ, computeResultHash as aR, decodePayment as aS, decodePaymentRequiredHeader as aT, encodePayment as aU, encodePaymentRequiredHeader as aV, encodeSettleResponseHeader as aW, exactEvmAuthorization as aX, isArtifactMessage as aY, isCancelMessage as aZ, isCompleteMessage as a_, type CapabilityDescriptor as aa, type CashuMode as ab, type CompleteContent as ac, type DrainReceipt as ad, type DrainReceiptEvent as ae, type ExactEvmPayload as af, type ExactEvmPayloadAuthorization as ag, type FundingMethod as ah, type MessageFrom as ai, type PaymentPayload as aj, type PaymentPayloadV1 as ak, type PaymentPayloadV2 as al, type PaymentRequestContent as am, type PaymentRequiredV2 as an, type PaymentRequirements as ao, type PaymentRequirementsV1 as ap, type PaymentRequirementsV2 as aq, type PromptContent as ar, RAIL_REFUNDABLE as as, type ReceiptOutcome as at, type ReceiptPayment as au, type SettleResponse as av, StepCache as aw, type StepRecord as ax, type TextContent as ay, type UnsignedDrainReceipt as az, type ArtifactContent as b, type StalePendingDrawCursor as b$, isFundingReceipt as b0, isPaymentRequestMessage as b1, isPromptMessage as b2, isSignedJobReceipt as b3, isTextMessage as b4, isWorkingMessage as b5, paymentRequiredV2FromV1 as b6, signDrainReceipt as b7, signFundingReceipt as b8, signReceipt as b9, _testing as bA, createDefaultReplayStore as bB, depleteLots as bC, fifoOrder as bD, inKindDrawMsats as bE, isInKindDepletion as bF, isNonChannelBitcoinRail as bG, lotOwedSats as bH, netOwedSats as bI, wrapMppx as bJ, type CreditDrainEnqueue as bK, type DVMAuthScheme as bL, type CreditLedgerLike as bM, type X402RefundSettlementGate as bN, type CreditSnapshot as bO, type DrawResult as bP, type X402SettlementStatus as bQ, type X402UnresolvedRefund as bR, type GrownDrawResult as bS, type DrawResolution as bT, type FundingRecord as bU, type CreditInvoiceRecord as bV, type InvoiceSettlement as bW, type BlockedInvoiceCursor as bX, type InvoiceReconciliation as bY, type InvoiceWriteOff as bZ, type DrawRecord as b_, usdcContractByCaip2 as ba, usdcContractFor as bb, usdcDomainNameFor as bc, usdcDomainVersionFor as bd, verifyDrainReceipt as be, verifyFundingReceipt as bf, verifyReceipt as bg, x402NetworkToCaip2 as bh, type X402ExactVersionSupport as bi, type X402Receipt as bj, type TransactionalPayoutHook as bk, type CashuMeltCompleted as bl, type CreditDepositPayload as bm, type CreditDrainPayload as bn, type FundingLot as bo, type LotDebit as bp, type LotDepletion as bq, type MppxServer as br, NON_CHANNEL_BITCOIN_RAILS as bs, type NonChannelBitcoinRail as bt, PayoutReporter as bu, PostgresTempoSessionStore as bv, RevenueReporter as bw, SIGNED_ENVELOPE_FIELDS as bx, SIGNED_ENVELOPE_TYPES as by, type X402TrackedChannel as bz, type CancelContent as c, type TempoCreditLossEvidence as c0, type CreditLedgerQuerier as c1, type TempoCreditLoss as c2, type X402CreditLossEvidence as c3, type X402CreditLoss as c4, type DrainMethod as c5, type DrainRequestResult as c6, type BitcoinDepositLiability as c7, type CreditDrainRecord as c8, type ChannelDrainCursor 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, parseClientCapabilities as cL, parseClientCompatibility as cM, parseDvmClient as cN, parseProtocolVersion as cO, requireClientCompatibility as cP, secp256k1Auth as cQ, signedRequestInput as cR, type X402SettlementIntent as cS, type X402SettlementCursor as cT, type X402SettlementWriteOff as cU, type X402FacilitatorAuth as cV, type X402BatchSettlementConfig as cW, type X402PayoutObserver as cX, type CreditDepositEnqueue as cY, type X402SettlementReconciliationReason as cZ, type CreditDrawReleaseEnqueue as c_, type DrainWriteOff as ca, type DrainReleaseResult as cb, type DrainFulfilment as cc, type DrainTransitionResult as cd, CLIENT_COMPATIBILITY_HEADERS as ce, type ClientCompatibility as cf, type ClientCompatibilityEnv as cg, type ClientCompatibilityGate as ch, type ClientCompatibilityRequirement as ci, type ClientSemVer as cj, type CreditFundingBasis as ck, type CreditInvoiceStatus as cl, CreditLedger as cm, CreditLedgerError as cn, type CreditLedgerErrorCode as co, type CreditLedgerErrorDetails as cp, type CreditLedgerPool as cq, type CreditStatus as cr, DRAIN_DELIVERY_RESERVE_SATS as cs, DVM_PROTOCOL_VERSION as ct, type DrainConflictReason as cu, type DrawRailValue as cv, type DrawStatus as cw, type JobCostReportPayload as cx, PostgresX402ChannelStorage as cy, type ReplayStoreBackend as cz, 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, DEFAULT_JOB_RETENTION_DAYS as k, type DVMRouteContext as l, type InputType as m, InvalidCurrencyError as n, type PaymentMethod as o, type PriceValue as p, type ProgressContent as q, type PromptOpts as r, type QuoteContext as s, type QuoteResult as t, type ResponseContent as u, type SDKPaymentRequestOpts as v, SIGNED_REQUEST_AUTH_ID as w, SIGNED_REQUEST_STATEMENT_VERSION as x, type SignedRequestAudience as y, type SignedRequestDomain as z };
5544
+ export { type X402Version as $, type ApprovalContent as A, SignedRequestError as B, type Currency as C, type DVMConfig as D, type SignedRequestFailure as E, type SignedRequestReplayStore as F, type SignedRequestSignOpts as G, type SignedRequestStatementHeader as H, type IncomingMessage as I, type JobCost as J, type KVStore as K, type Logger as L, type SignedRequestVerifier as M, createSignedRequestVerifier as N, isZodSchema as O, type PaymentContent as P, type QuoteConfig as Q, type ResolvedCreditConfig as R, type SDKJobContext as S, signedRequestStatementHeader as T, UnsupportedCurrencyError as U, validateCurrency as V, type JsonValue as W, type FundingReceipt as X, type JobReceipt as Y, type ZodLike as Z, type PaymentRequired as _, type DVMDescriptor as a, isDrainReceipt as a$, type MppxCredential as a0, type Message as a1, type MessageType as a2, type MppxChallenge as a3, X402_BATCH_SETTLEMENT_SCHEME as a4, X402_EXACT_SCHEME as a5, type ReceiptCredit as a6, type X402Wallet as a7, type ResourceInfo as a8, type BuildPaymentRequirementsOpts as a9, type UnsignedFundingReceipt as aA, type UnsignedJobReceipt as aB, type VerifyResponse as aC, type WorkingContent as aD, type X402Config as aE, type X402ResponseBody as aF, type X402SelfRelayRpcFailureReason as aG, X402_DEFAULT_NETWORK as aH, X402_V1_VERSION as aI, X402_VERSION as aJ, buildPaymentRequiredV2 as aK, buildPaymentRequirements as aL, caip2ToX402Network as aM, canonicalRequestPath as aN, canonicaliseForSigning as aO, canonicalize as aP, chainIdFromCaip2 as aQ, computeResultHash as aR, decodePayment as aS, decodePaymentRequiredHeader as aT, encodePayment as aU, encodePaymentRequiredHeader as aV, encodeSettleResponseHeader as aW, exactEvmAuthorization as aX, isArtifactMessage as aY, isCancelMessage as aZ, isCompleteMessage as a_, type CapabilityDescriptor as aa, type CashuMode as ab, type CompleteContent as ac, type DrainReceipt as ad, type DrainReceiptEvent as ae, type ExactEvmPayload as af, type ExactEvmPayloadAuthorization as ag, type FundingMethod as ah, type MessageFrom as ai, type PaymentPayload as aj, type PaymentPayloadV1 as ak, type PaymentPayloadV2 as al, type PaymentRequestContent as am, type PaymentRequiredV2 as an, type PaymentRequirements as ao, type PaymentRequirementsV1 as ap, type PaymentRequirementsV2 as aq, type PromptContent as ar, RAIL_REFUNDABLE as as, type ReceiptOutcome as at, type ReceiptPayment as au, type SettleResponse as av, StepCache as aw, type StepRecord as ax, type TextContent as ay, type UnsignedDrainReceipt as az, type ArtifactContent as b, type StalePendingDrawCursor as b$, isFundingReceipt as b0, isPaymentRequestMessage as b1, isPromptMessage as b2, isSignedJobReceipt as b3, isTextMessage as b4, isWorkingMessage as b5, paymentRequiredV2FromV1 as b6, signDrainReceipt as b7, signFundingReceipt as b8, signReceipt as b9, _testing as bA, createDefaultReplayStore as bB, depleteLots as bC, fifoOrder as bD, inKindDrawMsats as bE, isInKindDepletion as bF, isNonChannelBitcoinRail as bG, lotOwedSats as bH, netOwedSats as bI, wrapMppx as bJ, type CreditDrainEnqueue as bK, type DVMAuthScheme as bL, type CreditLedgerLike as bM, type X402RefundSettlementGate as bN, type CreditSnapshot as bO, type DrawResult as bP, type X402SettlementStatus as bQ, type X402UnresolvedRefund as bR, type GrownDrawResult as bS, type DrawResolution as bT, type FundingRecord as bU, type CreditInvoiceRecord as bV, type InvoiceSettlement as bW, type BlockedInvoiceCursor as bX, type InvoiceReconciliation as bY, type InvoiceWriteOff as bZ, type DrawRecord as b_, usdcContractByCaip2 as ba, usdcContractFor as bb, usdcDomainNameFor as bc, usdcDomainVersionFor as bd, verifyDrainReceipt as be, verifyFundingReceipt as bf, verifyReceipt as bg, x402NetworkToCaip2 as bh, type X402ExactVersionSupport as bi, type X402Receipt as bj, type TransactionalPayoutHook as bk, type CashuMeltCompleted as bl, type CreditDepositPayload as bm, type CreditDrainPayload as bn, type FundingLot as bo, type LotDebit as bp, type LotDepletion as bq, type MppxServer as br, NON_CHANNEL_BITCOIN_RAILS as bs, type NonChannelBitcoinRail as bt, PayoutReporter as bu, PostgresTempoSessionStore as bv, RevenueReporter as bw, SIGNED_ENVELOPE_FIELDS as bx, SIGNED_ENVELOPE_TYPES as by, type X402TrackedChannel as bz, type CancelContent as c, type CreditDrawReleaseEnqueue as c$, type TempoCreditLossEvidence as c0, type CreditLedgerQuerier as c1, type TempoCreditLoss as c2, type X402CreditLossEvidence as c3, type X402CreditLoss as c4, type DrainMethod as c5, type DrainRequestResult as c6, type BitcoinDepositLiability as c7, type CreditDrainRecord as c8, type ChannelDrainCursor as c9, type ReplayStoreBackend as cA, type RevenueSkippedNoRailPayload as cB, type RevenueSkippedNoRailReason as cC, type Secp256k1AuthOpts as cD, type X402ChannelStorageOpts as cE, type X402RelayLockHolder as cF, type X402RelaySubmissionLock as cG, X402RelaySubmissionLockError as cH, allocateDrawValue as cI, clientCompatibilityAttributes as cJ, clientCompatibilityMiddleware as cK, clientUpgradeRequired as cL, parseClientCapabilities as cM, parseClientCompatibility as cN, parseDvmClient as cO, parseProtocolVersion as cP, requireClientCompatibility as cQ, secp256k1Auth as cR, signedRequestInput as cS, type X402SettlementIntent as cT, type X402SettlementCursor as cU, type X402SettlementWriteOff as cV, type X402FacilitatorAuth as cW, type X402BatchSettlementConfig as cX, type X402PayoutObserver as cY, type CreditDepositEnqueue as cZ, type X402SettlementReconciliationReason as c_, type DrainWriteOff as ca, type DrainReleaseResult as cb, type DrainFulfilment as cc, type DrainTransitionResult as cd, CLIENT_COMPATIBILITY_HEADERS as ce, type ClientCompatibility as cf, type ClientCompatibilityEnv as cg, type ClientCompatibilityGate as ch, type ClientCompatibilityRequirement as ci, type ClientSemVer as cj, type CreditFundingBasis as ck, type CreditInvoiceStatus as cl, CreditLedger as cm, CreditLedgerError as cn, type CreditLedgerErrorCode as co, type CreditLedgerErrorDetails as cp, type CreditLedgerPool as cq, type CreditStatus as cr, DRAIN_DELIVERY_RESERVE_SATS as cs, DVM_PROTOCOL_VERSION as ct, type DrainConflictReason as cu, type DrawRailValue as cv, type DrawStatus as cw, type JobCostReportPayload as cx, type JobTerminalReportPayload as cy, PostgresX402ChannelStorage as cz, 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, DEFAULT_JOB_RETENTION_DAYS as k, type DVMRouteContext as l, type InputType as m, InvalidCurrencyError as n, type PaymentMethod as o, type PriceValue as p, type ProgressContent as q, type PromptOpts as r, type QuoteContext as s, type QuoteResult as t, type ResponseContent as u, type SDKPaymentRequestOpts as v, SIGNED_REQUEST_AUTH_ID as w, SIGNED_REQUEST_STATEMENT_VERSION as x, type SignedRequestAudience as y, type SignedRequestDomain as z };