@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
@@ -1,16 +1,15 @@
1
- import { ah as FundingMethod, o as PaymentMethod, bM as CreditLedgerLike, a1 as Message, X as FundingReceipt, bO as CreditSnapshot, Y as JobReceipt, S as SDKJobContext, aw as StepCache, u as ResponseContent, P as PaymentContent, a2 as MessageType, bj as X402Receipt, aE as X402Config, bi as X402ExactVersionSupport, cS as X402SettlementIntent, aq as PaymentRequirementsV2, c1 as CreditLedgerQuerier, cT as X402SettlementCursor, bQ as X402SettlementStatus, cU as X402SettlementWriteOff, bN as X402RefundSettlementGate, cV as X402FacilitatorAuth, cW as X402BatchSettlementConfig, cy as PostgresX402ChannelStorage, cX as X402PayoutObserver, br as MppxServer, ab as CashuMode, cY as CreditDepositEnqueue, cZ as X402SettlementReconciliationReason, ao as PaymentRequirements, a0 as MppxCredential, bm as CreditDepositPayload, bP as DrawResult, cn as CreditLedgerError, a6 as ReceiptCredit, ae as DrainReceiptEvent, ad as DrainReceipt, K as KVStore, y as SignedRequestAudience, c_ as CreditDrawReleaseEnqueue, cx as JobCostReportPayload, cA as RevenueSkippedNoRailPayload, Z as ZodLike, a as DVMDescriptor, bV as CreditInvoiceRecord, bW as InvoiceSettlement, ch as ClientCompatibilityGate, cf as ClientCompatibility, bu as PayoutReporter, R as ResolvedCreditConfig, g as CreditView } from './step-cache-5dljDqrQ.js';
1
+ import { ah as FundingMethod, o as PaymentMethod, bM as CreditLedgerLike, a1 as Message, X as FundingReceipt, bO as CreditSnapshot, Y as JobReceipt, S as SDKJobContext, aw as StepCache, u as ResponseContent, P as PaymentContent, a2 as MessageType, bj as X402Receipt, aE as X402Config, bi as X402ExactVersionSupport, cT as X402SettlementIntent, aq as PaymentRequirementsV2, c1 as CreditLedgerQuerier, cU as X402SettlementCursor, bQ as X402SettlementStatus, cV as X402SettlementWriteOff, bN as X402RefundSettlementGate, cW as X402FacilitatorAuth, cX as X402BatchSettlementConfig, cz as PostgresX402ChannelStorage, cY as X402PayoutObserver, br as MppxServer, ab as CashuMode, cZ as CreditDepositEnqueue, c_ as X402SettlementReconciliationReason, ao as PaymentRequirements, a0 as MppxCredential, bm as CreditDepositPayload, bP as DrawResult, cn as CreditLedgerError, a6 as ReceiptCredit, ae as DrainReceiptEvent, ad as DrainReceipt, K as KVStore, y as SignedRequestAudience, c$ as CreditDrawReleaseEnqueue, cx as JobCostReportPayload, cy as JobTerminalReportPayload, cB as RevenueSkippedNoRailPayload, Z as ZodLike, a as DVMDescriptor, bV as CreditInvoiceRecord, bW as InvoiceSettlement, ch as ClientCompatibilityGate, cf as ClientCompatibility, bu as PayoutReporter, R as ResolvedCreditConfig, g as CreditView } from './step-cache-v2yhyhSZ.js';
2
2
  import { ProofLike, SerializedDLEQ, Proof } from '@cashu/cashu-ts';
3
3
  import { Hono, Context } from 'hono';
4
4
  import { Pool } from 'pg';
5
- import { b as FxRateSnapshot, F as FxFetcher } from './fx-D860pZvP.js';
6
- import { g as LockPubkey, f as CheckMintHealthOptions, b as LightningBackend, A as AttestationPayload } from './lightning-backend-BozcevPZ.js';
7
- import { T as TopUpCapUnenforcedReason, A as AppendOutgoingOptions, J as JobRecord, b as JobStore } from './job-store-BUGqvCfL.js';
5
+ import { b as FxRateSnapshot, F as FxFetcher } from './fx-DhyT1Cjh.js';
6
+ import { g as LockPubkey, f as CheckMintHealthOptions, b as LightningBackend, A as AttestationPayload } from './lightning-backend-BhsyrBLa.js';
7
+ import { T as TopUpCapUnenforcedReason, A as AppendOutgoingOptions, J as JobRecord, b as JobStore } from './job-store-D3CcZp_g.js';
8
8
  import { Challenge } from 'mppx';
9
9
  import { SettleResponse, SupportedResponse } from '@x402/core/types';
10
10
  import { Channel, AutoSettlementConfig } from '@x402/evm/batch-settlement/server';
11
11
  import { FacilitatorClient } from '@x402/core/server';
12
12
 
13
- /** A row in the per-DVM `wallet_accumulator` table (internal-review). */
14
13
  interface WalletAccumulatorRow {
15
14
  id: string;
16
15
  dvmId: string;
@@ -36,11 +35,8 @@ interface WalletAccumulatorRow {
36
35
  * rows created before the column existed.
37
36
  */
38
37
  meltQuoteId: string | null;
39
- /**
40
- * NUT-11 locktime captured at accept time (ms since epoch). Null when the
41
- * accepted proof has no `locktime` tag (internal-review). Drives oldest-first
42
- * ordering and the future `wallet_accumulator_batch_expiring_soon` monitor.
43
- */
38
+ /** NUT-11 locktime at acceptance, in epoch ms; null without a tag.
39
+ * Drives oldest-first ordering and expiring-soon monitoring. */
44
40
  tExpire: number | null;
45
41
  }
46
42
  /**
@@ -71,7 +67,7 @@ declare function initWalletAccumulatorTable(db: AccumulatorPool): Promise<void>;
71
67
  *
72
68
  * With `tx` (a client inside a caller-owned `BEGIN`), issues **no**
73
69
  * transaction control: the inserts join the caller's transaction so the rail
74
- * commit and the `CreditLedger.fund` + `draw` land atomically (internal-review, spec
70
+ * commit and the `CreditLedger.fund` + `draw` land atomically (spec
75
71
  * §2 condition 3). A `unique_violation` still surfaces as
76
72
  * `AccumulatorReplayError`, but the caller owns the ROLLBACK — note the
77
73
  * transaction is poisoned after the violation (Postgres aborts it), so the
@@ -82,7 +78,7 @@ declare function insertAccumulatorRows(db: AccumulatorPool, args: {
82
78
  /**
83
79
  * Per-proof matched lock pubkey, parallel to `proofs`. Recording the
84
80
  * matched value per row lets the monitor surface retired-batch alerts
85
- * (internal-review) and lets `dvmctl melt-pending`'s rotation walker pick the
81
+ * and lets `dvmctl melt-pending`'s rotation walker pick the
86
82
  * right privkey per batch. Branded `LockPubkey` so the lowercase invariant
87
83
  * is enforced at the type boundary, not by every call site.
88
84
  */
@@ -97,7 +93,7 @@ declare function insertAccumulatorRows(db: AccumulatorPool, args: {
97
93
  */
98
94
  tExpireMsByIndex?: (number | null)[];
99
95
  receivedAt?: number;
100
- /** External transaction handle — caller owns BEGIN/COMMIT/ROLLBACK (internal-review). */
96
+ /** External transaction handle — caller owns BEGIN/COMMIT/ROLLBACK. */
101
97
  tx?: AccumulatorQuerier;
102
98
  }): Promise<WalletAccumulatorRow[]>;
103
99
  /** Delete every accumulator row for a DVM. Used by `dvmadmin wallet-accumulator clear`. */
@@ -117,8 +113,8 @@ declare function hashLockKey(input: string): bigint;
117
113
  *
118
114
  * Used by `verifyUpfrontPayment` to reject replay of an MPP credential whose
119
115
  * `(realm, challenge.id)` was already accepted within the current TTL window
120
- * (internal-review). Mid-job replay is handled separately via per-job
121
- * `pendingMppChallengeIds` (internal-review).
116
+ * Mid-job replay is handled separately via per-job
117
+ * `pendingMppChallengeIds`.
122
118
  */
123
119
  interface ConsumedCredentialStore {
124
120
  /** True iff `(realm, challengeId)` was marked and the entry has not expired. */
@@ -126,7 +122,6 @@ interface ConsumedCredentialStore {
126
122
  /** Mark `(realm, challengeId)` as consumed for `ttlSeconds`. */
127
123
  mark(realm: string, challengeId: string, ttlSeconds: number): Promise<void>;
128
124
  }
129
- /** Options for `MemoryConsumedCredentialStore`. */
130
125
  interface MemoryConsumedCredentialStoreOpts {
131
126
  /** FIFO eviction kicks in when entry count would exceed this. Default: 10_000. */
132
127
  maxEntries?: number;
@@ -138,8 +133,8 @@ interface MemoryConsumedCredentialStoreOpts {
138
133
  *
139
134
  * Per-process and not multi-instance-safe — each replica tracks its own
140
135
  * consumed set, so a credential redirected to a different replica still works
141
- * once. Acceptable for first-party single-instance Fly DVMs (internal-review open
142
- * question); swap to a shared backend before deploying multiple replicas.
136
+ * once. Use only for a single-instance DVM; swap to a shared backend before
137
+ * deploying multiple replicas.
143
138
  */
144
139
  declare class MemoryConsumedCredentialStore implements ConsumedCredentialStore {
145
140
  private readonly maxEntries;
@@ -175,7 +170,6 @@ interface DisposableCashuCommit {
175
170
  commit(requestId: string, proofs: Proof[], account: (ledger: CreditLedgerLike) => Promise<void>): Promise<void>;
176
171
  }
177
172
 
178
- /** How often the sweep runs, and how long after boot its first pass fires. */
179
173
  interface CreditExpirySweepOpts {
180
174
  /** Sweep period. Defaults to {@link DEFAULT_SWEEP_INTERVAL_MS}; `0` disables the timer. */
181
175
  intervalMs?: number;
@@ -184,26 +178,22 @@ interface CreditExpirySweepOpts {
184
178
  * disables the boot pass, leaving only the interval.
185
179
  */
186
180
  bootDelayMs?: number;
187
- /** Credits released per pass. Passed straight to the ledger. */
188
181
  limit?: number;
189
182
  }
190
183
 
191
- /** Promise resolver for a pending client prompt response. */
192
184
  interface PromptResolver {
193
185
  resolve: (value: ResponseContent) => void;
194
186
  reject: (reason: Error) => void;
195
187
  }
196
- /** Promise resolver for a pending client payment. */
197
188
  interface PaymentResolver {
198
189
  resolve: (value: PaymentContent) => void;
199
190
  reject: (reason: Error) => void;
200
191
  }
201
- /** In-memory representation of a running job. */
202
192
  interface ServerJob {
203
193
  id: string;
204
194
  tags: string[];
205
195
  /**
206
- * Capability name this job belongs to (internal-review). Set at submission from
196
+ * Capability name this job belongs to. Set at submission from
207
197
  * `POST /v1/job` body's `capability` field; routes the SDK runtime to the
208
198
  * right `onJob` / `onResponse` / `onPayment` handler and the right input
209
199
  * schema. Required — the wire layer rejects requests that omit it.
@@ -214,12 +204,12 @@ interface ServerJob {
214
204
  requesterId: string;
215
205
  /**
216
206
  * SHA-256 hex of the per-job opaque `job_token` minted at creation for
217
- * anonymous callers (internal-review). Mirror of `JobRecord.requesterTokenHash`;
207
+ * anonymous callers. Mirror of `JobRecord.requesterTokenHash`;
218
208
  * see that field for the wire-level gating semantics.
219
209
  */
220
210
  requesterTokenHash?: string;
221
211
  /**
222
- * Submission provenance (internal-review) — mirrors `JobRecord.requesterToken` /
212
+ * Submission provenance — mirrors `JobRecord.requesterToken` /
223
213
  * `requestFingerprint` / `requesterPubkey`. Carried on the job purely so
224
214
  * `toJobRecord` persists it; nothing in the runtime reads it. It's the
225
215
  * replay gate: a retried paid submit that reproduces the fingerprint (and,
@@ -232,6 +222,16 @@ interface ServerJob {
232
222
  /** HTTP path independently recorded when the caller proof was accepted. */
233
223
  authRequestPath?: string;
234
224
  status: "processing" | "completed" | "failed" | "awaiting-input" | "cancelled" | "working";
225
+ /** Mirrors the durable completeness marker; false for legacy accepted jobs. */
226
+ timingKnown?: boolean;
227
+ /** SDK terminal attribution; never accepted from a handler. */
228
+ endedBy?: "caller" | "provider";
229
+ /** Unix ms when the terminal status committed; immutable afterwards. */
230
+ terminalAtMs?: number;
231
+ /** Accumulated awaiting-input time, excluding the currently open stretch. */
232
+ callerWaitMs?: number;
233
+ /** Unix ms at which the current awaiting-input stretch began. */
234
+ awaitingInputAtMs?: number;
235
235
  summary?: string;
236
236
  messages: Message[];
237
237
  seq: number;
@@ -243,9 +243,9 @@ interface ServerJob {
243
243
  * Settlement reference for the most-recent successful credit. Threaded
244
244
  * into `onJobCompleted` so the platform revenue ledger can populate
245
245
  * `revenue_events.tx_hash`. mpp/x402 carry the credential's `challenge.id`
246
- * / EVM tx hash from the facilitator (internal-review); cashu accumulator carries
247
- * the `X-Cashu-Request-Id` UUID (internal-review). Updated on every credit
248
- * (upfront or mid-job via `processIncomingPayment`) — internal-review.
246
+ * / EVM tx hash from the facilitator; cashu accumulator carries
247
+ * the `X-Cashu-Request-Id` UUID. Updated on every credit
248
+ * (upfront or mid-job via `processIncomingPayment`).
249
249
  * Single-row-per-job accounting keeps the most recent credit's hash,
250
250
  * mirroring `paymentRail`.
251
251
  */
@@ -253,22 +253,18 @@ interface ServerJob {
253
253
  /** Chain transaction returned to callers recovering x402 or Tempo acceptance. */
254
254
  paymentTransactionHash?: string;
255
255
  /**
256
- * Rail-native amount accumulated across all successful credits on this
257
- * job (sats for mpp, USDC microunits for x402). The single
258
- * `revenue_events` row written at completion uses this as `gross_native`,
259
- * so multi-credit jobs sum the per-credit native amounts (internal-review).
260
- * internal-review wired the upfront credit; mid-job credits accumulate same-rail
261
- * and reset on a cross-rail switch (see `processIncomingPayment`).
256
+ * Rail-native atomic units accumulated across same-rail credits: Tempo and
257
+ * x402 use stablecoin micro-units. A rail switch resets this amount; Cashu
258
+ * revenue is derived from paidMsats instead.
262
259
  */
263
260
  nativeAmount?: number;
264
- /** Native asset tag — paired with `nativeAmount`. internal-review. */
261
+ /** Native asset tag paired with `nativeAmount`. */
265
262
  nativeAsset?: "sats" | "usdc" | "usdc.e" | "usd-cents";
266
- /** Cashu flow discriminator written into `revenue_events.metadata.cashu_flow` (internal-review). */
267
263
  cashuFlow?: "p2pk_accumulator";
268
264
  /**
269
265
  * What this job cost the **builder** to serve, in 1e-6 of
270
266
  * {@link ServerJob.costCurrency} — the running total the handler declared
271
- * through `ctx.cost()` (internal-review). Absent until a handler declares
267
+ * through `ctx.cost()`. Absent until a handler declares
272
268
  * something; absent is "unreported", which is not the same fact as a
273
269
  * declared zero and is reported differently.
274
270
  *
@@ -284,9 +280,9 @@ interface ServerJob {
284
280
  costCurrency?: string;
285
281
  /** Number of `ctx.cost()` declarations incorporated into the current cost state. */
286
282
  costRevision?: number;
287
- /** Credit the upfront payment funded/drew (internal-review). See `JobRecord.creditId`. */
283
+ /** Credit the upfront payment funded/drew. See `JobRecord.creditId`. */
288
284
  creditId?: string;
289
- /** The draw placed for this job on `creditId` (internal-review). */
285
+ /** The draw placed for this job on `creditId`. */
290
286
  drawId?: string;
291
287
  /** Original funding artifact returned by an explicit fund-and-draw. */
292
288
  fundingReceipt?: FundingReceipt;
@@ -299,16 +295,16 @@ interface ServerJob {
299
295
  * binds challenges to realm/method/amount/expiry, not to the dvmkit job-id;
300
296
  * verifying the incoming credential's `challenge.id` is in this set blocks
301
297
  * a credential lifted from a sibling job that requested the same fields.
302
- * Cleared on successful credit (internal-review).
298
+ * Cleared on successful credit.
303
299
  */
304
300
  pendingMppChallengeIds?: string[];
305
301
  /**
306
- * Per-job binding for x402 mid-job credentials (internal-review). 32-byte 0x-hex
302
+ * Per-job binding for x402 mid-job credentials. 32-byte 0x-hex
307
303
  * nonce issued by `requestPayment` and stamped onto the outbound x402
308
304
  * envelope; the caller signs `TransferWithAuthorization` against this exact
309
305
  * `bytes32`. Verification rejects an incoming `x402_payment` whose decoded
310
- * authorization nonce differs, blocking a cross-job replay before the
311
- * facilitator settles. Cleared on successful credit.
306
+ * authorization nonce differs before crediting the job. Cleared on
307
+ * successful credit.
312
308
  */
313
309
  pendingX402Nonce?: string;
314
310
  /**
@@ -318,30 +314,30 @@ interface ServerJob {
318
314
  pendingX402AmountUsdcMicro?: string;
319
315
  /**
320
316
  * Fiat micro-units the outstanding `requestPayment` ask is worth, pinned when
321
- * the payment-request was emitted (internal-review). See `JobRecord`.
317
+ * the payment-request was emitted. See `JobRecord`.
322
318
  */
323
319
  pendingPaymentFiatMicro?: number;
324
320
  /** Currency of {@link pendingPaymentFiatMicro} (lowercase ISO-4217). */
325
321
  pendingPaymentFiatCurrency?: string;
326
322
  /**
327
323
  * Cumulative fiat micro this job has asked for mid-job, the ceiling on its
328
- * draw growth (internal-review). See `JobRecord.askedTopUpMicro` for the sentinel.
324
+ * draw growth. See `JobRecord.askedTopUpMicro` for the sentinel.
329
325
  */
330
326
  askedTopUpMicro?: number;
331
327
  /**
332
328
  * Currency of {@link askedTopUpMicro} — and this job's sticky ask
333
- * denomination (internal-review). See `JobRecord.askedTopUpCurrency`.
329
+ * denomination. See `JobRecord.askedTopUpCurrency`.
334
330
  */
335
331
  askedTopUpCurrency?: string;
336
332
  /**
337
- * Why this job's draw growth is not capped, when it isn't (internal-review). Mirror
333
+ * Why this job's draw growth is not capped, when it isn't. Mirror
338
334
  * of `JobRecord.topUpCapUnenforcedReason`.
339
335
  */
340
336
  topUpCapUnenforcedReason?: TopUpCapUnenforcedReason;
341
337
  /** Price the job was charged at (msats). Used to detect overpayment for change/melt gating. */
342
338
  requiredMsats?: number;
343
339
  /**
344
- * The signed receipt for this job's terminal outcome (internal-review). Mirror of
340
+ * The signed receipt for this job's terminal outcome. Mirror of
345
341
  * `JobRecord.receipt`, set once the terminal funnel has signed and persisted
346
342
  * it so the local read paths (which serve from `activeJobs` during the
347
343
  * cleanup window) hand back the same bytes as a cross-machine re-read.
@@ -349,7 +345,7 @@ interface ServerJob {
349
345
  receipt?: JobReceipt;
350
346
  /**
351
347
  * The in-flight terminal chain — persist, then sign + store the receipt
352
- * (internal-review). Assigned synchronously by `ctx.complete()` / `ctx.fail()` at
348
+ * Assigned synchronously by `ctx.complete()` / `ctx.fail()` at
353
349
  * the instant the job goes terminal, so the `POST /v1/job` fast path can
354
350
  * await it (bounded) and put the receipt on the synchronous 200 rather than
355
351
  * making the caller poll for it. Never rejects — the chain swallows its own
@@ -358,7 +354,7 @@ interface ServerJob {
358
354
  terminalWork?: Promise<void>;
359
355
  listeners: Set<(msg: Message) => void>;
360
356
  /**
361
- * Cancellation signal for the running handler (internal-review). Aborted whenever
357
+ * Cancellation signal for the running handler. Aborted whenever
362
358
  * the job is cancelled on this process — caller cancel, idle timeout,
363
359
  * stale-sweep teardown, supersession. Surfaced to builders as `ctx.signal`
364
360
  * and auto-threaded into `ctx.fetch`, so an in-flight provider call
@@ -368,18 +364,18 @@ interface ServerJob {
368
364
  */
369
365
  abort: AbortController;
370
366
  /**
371
- * Hook for cross-machine message persistence (Tx A, internal-review). Set by
367
+ * Hook for cross-machine message persistence (Tx A). Set by
372
368
  * `JobManager` when the store is streamable. The appender runs the
373
369
  * outgoing-message tx atomically with any payment-request counter bump so
374
370
  * `(message, pending_payment_msats)` land together — never separately.
375
371
  *
376
- * Per-job serialised (internal-review): each call chains onto `messageAppenderTail`
372
+ * Per-job serialised: each call chains onto `messageAppenderTail`
377
373
  * so two synchronous `providerMessage` calls (e.g. `artifact` followed by
378
374
  * `complete`) can't race for the DB-side seq allocation.
379
375
  */
380
376
  messageAppender?: (msg: Message, opts?: AppendOutgoingOptions) => void;
381
377
  /**
382
- * Tail of the per-job appender chain (internal-review). `wireMessageAppender` updates
378
+ * Tail of the per-job appender chain. `wireMessageAppender` updates
383
379
  * this on every call so `persistJob` can await it before the terminal snapshot
384
380
  * save runs `DELETE FROM job_messages`.
385
381
  */
@@ -406,12 +402,11 @@ interface ServerJob {
406
402
  /** Unix ms timestamp of last activity (message, yield, terminal). */
407
403
  lastActivityAt: number;
408
404
  }
409
- /** Extract the persistent fields from a ServerJob into a JobRecord. */
410
405
  declare function toJobRecord(job: ServerJob): JobRecord;
411
- /** Reconstitute a ServerJob from a persisted JobRecord with fresh runtime fields. */
406
+ /** Restored snapshots receive fresh process-local listeners, cancellation and prompt state. */
412
407
  declare function fromJobRecord(record: JobRecord): ServerJob;
413
408
  /**
414
- * Signal cancellation to a running handler (internal-review). Idempotent — aborting an
409
+ * Signal cancellation to a running handler. Idempotent — aborting an
415
410
  * already-aborted controller is a no-op, so the overlapping teardown paths
416
411
  * (caller cancel then stale sweep, say) can each call it unconditionally.
417
412
  */
@@ -424,41 +419,14 @@ declare function abortJob(job: ServerJob, reason: string): void;
424
419
  declare class JobCancelledError extends Error {
425
420
  constructor(message?: string);
426
421
  }
427
- /** Append a provider-sent message to the job and notify SSE listeners. */
428
422
  declare function providerMessage(job: ServerJob, type: MessageType, content: Record<string, unknown> | object): void;
429
- /** Check if a job status is terminal (completed, failed, cancelled). */
430
423
  declare function isTerminal(status: ServerJob["status"]): boolean;
431
- /** Check if a message is a yield point (ends the provider's turn). */
432
424
  declare function isYieldMessage(msg: Message): boolean;
433
425
 
434
- /**
435
- * Per-DVM runtime mint-health tracker (internal-review). Alongside the platform's
436
- * durable mint-health cron, probes every configured
437
- * mint on a timer, tracks consecutive failures, and flips a mint to `sick`
438
- * once the threshold is crossed. The SDK uses the resulting view to:
439
- *
440
- * 1. Filter `/v1/info`'s advertised `mints` to currently-healthy ones.
441
- * 2. Reject new accumulator receives at sick mints before they touch the
442
- * DB (`cashu_mint_sick` 503 from `verifyAccumulatorReceipt`).
443
- *
444
- * Since internal-review this tracker is the sole owner of mint health: boot no longer
445
- * runs a fail-fast check before binding the port. To preserve what boot used
446
- * to guarantee it (a) treats a mint as healthy only when it's reachable AND
447
- * NUT-compliant (reproducing `assertNutSupport` via `nutCompliant`), and (b)
448
- * exposes `initialProbeComplete()` so the paid path holds calls at
449
- * `mint_health_pending` until the first probe validates ≥1 mint.
450
- *
451
- * Existing accumulator entries are not touched — they're P2PK-locked to the
452
- * original mint and stay put until `dvmctl melt-pending` drains them. The
453
- * accumulator monitor's `wallet_accumulator_batch_expiring_soon` already
454
- * pages the builder when a stuck batch nears `t_expire`.
455
- *
456
- * In-memory only — no DB persistence. The platform cron keeps the durable
457
- * record + email path (internal-review); the SDK only needs fresh in-process state
458
- * to make routing decisions.
459
- */
426
+ /** In-process mint health gates advertising and new receives. Paid calls wait for
427
+ * the first probe; NUT incompatibility fails immediately, transient failures
428
+ * use a consecutive threshold. Existing accumulator proofs remain at their mint. */
460
429
 
461
- /** Per-mint runtime view exposed by `snapshot()`. */
462
430
  interface MintHealthSnapshot {
463
431
  /** Last-observed wall-clock latency of the probe in ms. */
464
432
  latencyMs?: number;
@@ -467,8 +435,8 @@ interface MintHealthSnapshot {
467
435
  /**
468
436
  * True once the mint is considered unhealthy for routing: either
469
437
  * `consecutiveFailures` crossed the threshold, or the last probe reached the
470
- * mint but it fails the NUT gate (a deterministic, permanent config error
471
- * that flips `sick` immediately — internal-review).
438
+ * mint but it fails the NUT gate, a deterministic, permanent config error
439
+ * that flips `sick` immediately.
472
440
  */
473
441
  sick: boolean;
474
442
  /**
@@ -478,15 +446,14 @@ interface MintHealthSnapshot {
478
446
  * never resolves without a config/mint change.
479
447
  */
480
448
  nutIncompatible?: boolean;
481
- /** Mint version reported by the last successful probe. */
482
449
  version?: string;
483
- /** Error category from the most recent failed probe. */
484
450
  errorCode?: string;
485
451
  }
486
- /** Tunables for {@link MintHealthTracker}. */
487
452
  interface MintHealthTrackerOptions {
488
453
  /** Cashu mint URLs the DVM accepts. The tracker probes every entry. */
489
454
  mints: string[];
455
+ /** Host environment used for default timing and failure settings. */
456
+ env?: Record<string, string | undefined>;
490
457
  /**
491
458
  * Tick cadence in ms. Defaults to {@link DEFAULT_TICK_INTERVAL_MS} or the
492
459
  * `DVMKIT_MINT_HEALTH_TICK_MS` env var when set.
@@ -497,26 +464,11 @@ interface MintHealthTrackerOptions {
497
464
  * {@link DEFAULT_FAILURES_TO_SICK} or `DVMKIT_MINT_FAILURES_TO_SICK` env.
498
465
  */
499
466
  consecutiveFailuresToSick?: number;
500
- /** Injected `fetch` for tests. */
501
467
  fetchImpl?: typeof fetch;
502
- /** Injected `Date.now` for tests. */
503
468
  now?: () => number;
504
- /**
505
- * Per-probe timeout / retry / backoff, forwarded to {@link checkMintHealth}.
506
- * Defaults to that function's own defaults — 3 attempts with `[200, 500]`
507
- * backoff, which is what a real tick should do.
508
- *
509
- * Tests that drive several failing ticks set `backoffMs: [0]`: a failing
510
- * probe otherwise costs 700ms of real `setTimeout`, and four of them put a
511
- * unit test within a loaded CI runner's reach of the 5s default timeout.
512
- * `fetchImpl` is set on the tracker, not here.
513
- */
469
+ /** Forwarded to checkMintHealth; fetchImpl is supplied on the tracker. */
514
470
  probe?: Omit<CheckMintHealthOptions, "fetchImpl">;
515
471
  }
516
- /**
517
- * Runtime mint-health tracker. Owns its own timer; `start()` runs an initial
518
- * probe immediately so the first received call doesn't see a stale snapshot.
519
- */
520
472
  declare class MintHealthTracker {
521
473
  private readonly mints;
522
474
  private readonly tickIntervalMs;
@@ -542,7 +494,7 @@ declare class MintHealthTracker {
542
494
  /**
543
495
  * True once the first full probe tick has landed. Before this, paid calls
544
496
  * are held at `503 mint_health_pending` rather than accepted against
545
- * unvalidated mints (internal-review).
497
+ * unvalidated mints.
546
498
  */
547
499
  initialProbeComplete(): boolean;
548
500
  /**
@@ -559,63 +511,34 @@ declare class MintHealthTracker {
559
511
  * gate — a deterministic, permanent config error, unlike a transient outage.
560
512
  * Unknown mints return false. `advertisedMints()` uses this to keep a
561
513
  * permanently-unsettleable mint off `/v1/info` even in the all-sick fallback,
562
- * where a merely-flapping mint is still published (internal-review).
514
+ * where a merely-flapping mint is still published.
563
515
  */
564
516
  isNutIncompatible(mintUrl: string): boolean;
565
- /** Plain-object snapshot keyed by mint URL — for tests + observability. */
566
517
  snapshot(): Record<string, MintHealthSnapshot>;
567
518
  private applyResult;
568
- /**
569
- * One-shot structured summary emitted after the first probe tick. Replaces
570
- * the boot check's per-mint `cashu_mint_startup_health` lines and surfaces an
571
- * all-mints-unhealthy warning — the operator alert that the Fly restart-loop
572
- * used to be before boot stopped refusing to start (internal-review).
573
- */
519
+ /** One boot summary, including an all-unhealthy warning. */
574
520
  private logBootHealth;
575
521
  }
576
522
 
577
523
  /**
578
- * Credit fields riding the signed request body (internal-review, spec §2).
579
- *
580
- * An explicit draw references the credit INSIDE the secp256k1/Schnorr-signed
581
- * `body.data` — no new caller crypto. Wire field names (snake_case, matching
582
- * the rest of the wire surface):
583
- *
584
- * - `credit_id` — the credit to draw against.
585
- * - `draw_id` — client-generated draw id; the ledger's idempotency key.
586
- * - `fund` — optional `{ amount_micro, commitment }` funding commitment:
587
- * present iff a funding artifact (X-Cashu / X-PAYMENT / mpp credential)
588
- * rides the same request. `commitment` is the SHA-256 hex over the raw
589
- * artifact bytes, binding the header-borne proof — which sits OUTSIDE the
590
- * signed body — to the credit its sender intended (spec §2 condition 3).
524
+ * Credit fields belong to the raw signed body. The funding commitment hashes the header
525
+ * artifact, binding money outside that body to its intended credit.
591
526
  *
592
- * These are envelope-level fields, not capability input: **the SDK** removes
593
- * them before any builder-owned schema parses ({@link stripCreditEnvelope} in
594
- * `JobManager.parseInput` and on the quote path; {@link
595
- * creditEnvelopeIgnoreFields} feeding the auth verifier's own schema check), so
596
- * handlers never see them and a top-level `.strict()` capability schema is
597
- * fine (internal-review). Relying on Zod's default strip mode instead was the trap:
598
- * `.strict()` rejects unknown keys, so an explicit draw 400'd at parse before
599
- * the envelope was ever extracted.
600
- *
601
- * The strip never touches the canonical signing bytes. Every auth call site
602
- * verifies the raw wire body (`signedRequestInput`, internal-review), so the credit
603
- * fields are in the verified bytes because they were never taken out of them —
604
- * the strip is only ever applied to what a schema is about to parse.
527
+ * Schema projection strips undeclared protocol keys before builder input parsing, without
528
+ * changing canonical signing bytes. Signature verification must use the original wire body; a
529
+ * parsed projection may omit credit fields.
605
530
  */
606
531
  interface CreditEnvelope {
607
532
  creditId: string;
608
533
  drawId: string;
609
534
  fund?: CreditFundCommitment;
610
535
  }
611
- /** The signed funding commitment for a fund-and-draw request. */
612
536
  interface CreditFundCommitment {
613
537
  /** Funding amount in 1e-6 units of the credit's currency. */
614
538
  amountMicro: number;
615
539
  /** SHA-256 hex over the raw funding artifact bytes. */
616
540
  commitment: string;
617
541
  }
618
- /** Wire keys reserved for the credit envelope inside `body.data`. */
619
542
  declare const CREDIT_ENVELOPE_KEYS: readonly ["credit_id", "draw_id", "fund"];
620
543
  /**
621
544
  * Thrown by {@link extractCreditEnvelope} on a structurally invalid credit
@@ -633,36 +556,24 @@ declare class CreditEnvelopeError extends Error {
633
556
  */
634
557
  declare function extractCreditEnvelope(data: unknown): CreditEnvelope | undefined;
635
558
  /**
636
- * The reserved keys `schema` does not itself declare — the ones the SDK is
637
- * free to remove before handing it an input (internal-review).
638
- *
639
- * A capability schema declares none of them, so all three are removable. The
640
- * one exception this exists for is `/v1/credit`'s own `CREDIT_REQUEST_SCHEMA`,
641
- * where `credit_id` and `fund` are real fields on a different wire shape and
642
- * removing them would empty the request. It is not an escape hatch for a
643
- * capability: the three names stay reserved on `body.data`, and declaring one
644
- * doesn't win a builder their own value — `app.ts` runs
645
- * {@link extractCreditEnvelope} over the raw body on every submit, so a
646
- * capability that declares `fund` answers `invalid_credit_fields` instead.
559
+ * Return protocol keys not declared by the schema. Schema-declared keys remain available,
560
+ * including /v1/credit’s differently shaped credit_id and fund fields. Submit separately
561
+ * extracts and validates the credit envelope from raw body.data; declaring a key does not
562
+ * bypass that validation.
647
563
  */
648
564
  declare function creditEnvelopeIgnoreFields(schema: unknown): readonly string[];
649
565
  /**
650
- * Remove the reserved credit keys from `data` before `schema` parses it, so a
651
- * strict schema doesn't reject the protocol's own envelope (internal-review). Keys
652
- * `schema` declares are left alone — see {@link creditEnvelopeIgnoreFields}.
653
- *
654
- * Returns `data` unchanged when it isn't a plain object or carries none of the
655
- * removable keys, so the ordinary no-credit request allocates nothing. Never
656
- * use the result to derive canonical signing bytes: those cover the wire form,
657
- * credit fields included (`signedRequestInput`).
566
+ * Remove undeclared credit keys before schema parsing. Preserve nonobject/array inputs and
567
+ * objects with no removable keys. Never derive signing bytes from this projection; signatures
568
+ * cover the raw wire body.
658
569
  */
659
570
  declare function stripCreditEnvelope(schema: unknown, data: unknown): unknown;
660
571
 
661
572
  /**
662
- * Fiat denomination of the job price, pinned per request (internal-review).
573
+ * Fiat denomination of the job price, pinned per request.
663
574
  *
664
- * The credit ledger is fiat-micro denominated (internal-review baked decision:
665
- * 1e-6 of the DVM's pricing currency, never msats). The conversion pins at
575
+ * The credit ledger is fiat-micro denominated: 1e-6 of the DVM's pricing
576
+ * currency, never msats. The conversion pins at
666
577
  * quote time: the verify path already fixes both the fiat price and its msat
667
578
  * equivalent, so the implicit N=1 fund credits the QUOTED fiat amount when
668
579
  * the attached token satisfies the quoted msats — no fresh BTC/USD lookup in
@@ -677,36 +588,13 @@ interface PriceFiat {
677
588
  }
678
589
 
679
590
  /**
680
- * Resolve the fiat denomination for a request's price (internal-review).
681
- *
682
- * Both sources are already fiat-exact, so this never consults an fx rate —
683
- * which is the point: since internal-review removed numeric-msats prices there is no
684
- * price form that needs one, and the ledger can be denominated for every paid
685
- * request rather than only the ones a rate provider happened to be up for.
686
- *
687
- * - `dynamicUpfront` — the validated `QuoteResult.upfront` fiat envelope a
688
- * dynamic-priced capability returned for THIS request.
689
- * - a static `"$X.XX"` price — parsed directly.
690
- *
691
- * Returns `undefined` for free jobs (`priceMsats` unset or 0). For a paid job
692
- * it always returns a denomination: `validateQuoteResult` rejects a dynamic
693
- * amount too small or too large to express in micro-units, and `configureDVM`
694
- * rejects a static price with the same defect at boot.
695
- *
696
- * internal-review: whichever branch answers, the currency is the DVM's declared
697
- * pricing currency, so a job draws in the denomination `/v1/credit` funded in.
698
- * Held there by two guards outside this function, which is why neither branch
699
- * needs to know the DVM's currency to return it — `validateQuoteResult`
700
- * refuses a quote that disagrees with the declaration, and `configureDVM`
701
- * refuses a static (USD-literal) price on a DVM that declares anything but
702
- * `"usd"`. Both refuse rather than convert, deliberately: an fx lookup on this
703
- * path is the internal-review shape, where a cold-start rate outage took money and
704
- * recorded no draw.
591
+ * Paid requests pin the validated fiat price without an FX lookup. Free jobs
592
+ * return undefined. configureDVM and validateQuoteResult enforce micro-unit
593
+ * range and the DVM's declared currency before this helper is called; neither
594
+ * guard converts a mismatched currency.
705
595
  */
706
596
  declare function resolvePriceFiat(args: {
707
- /** The capability's static `price` value, if any. */
708
597
  price: string | undefined;
709
- /** The resolved required price in msats (static or dynamic). */
710
598
  priceMsats: number | undefined;
711
599
  /** Fiat envelope from a dynamic quote, when the price came from `onQuote`. */
712
600
  dynamicUpfront?: {
@@ -715,28 +603,11 @@ declare function resolvePriceFiat(args: {
715
603
  };
716
604
  }): PriceFiat | undefined;
717
605
  /**
718
- * The micro-unit half of {@link resolvePriceFiat}, without its "is this job
719
- * paid at all" gate — the one derivation every surface that states an exact
720
- * fiat price reads (internal-review).
721
- *
722
- * A caller doing an explicit fund-and-draw has to sign the provider's own
723
- * valuation of the artifact it attaches (`price_micro × paid_msats /
724
- * required_msats`), and `price_micro` is `PriceFiat.amountMicro`. Reconstructing
725
- * that integer from a rendered decimal is lossy the moment any surface between
726
- * the two rounds — the isolate tier advertises at four decimals, and the CLI's
727
- * synthetic quote used to re-round a 402 the same way — so `/v1/quote`'s
728
- * `upfront.amount_micro` and the upfront 402's `price_micro` state the integer
729
- * itself. Both **must** derive through here rather than recomputing: two
730
- * independent derivations of one figure is the shape that produced the mismatch
731
- * these fields exist to remove.
732
- *
733
- * The `priceMsats` gate stays on `resolvePriceFiat` alone. An advertisement
734
- * surface has no msat figure in hand — the quote route states a price before
735
- * anything has converted it — and a free capability is already excluded there
736
- * by having no price to pass.
606
+ * Shared exact integer for quote, 402 and ledger valuation. Callers must sign
607
+ * this micro-unit figure rather than reconstruct it from a rounded display.
608
+ * Unlike resolvePriceFiat, advertisement callers need no msat price in hand.
737
609
  */
738
610
  declare function priceFiatMicro(args: {
739
- /** The capability's static `price` value, if any. */
740
611
  price?: string | undefined;
741
612
  /** Fiat envelope from a dynamic quote, when the price came from `onQuote`. */
742
613
  dynamicUpfront?: {
@@ -744,17 +615,7 @@ declare function priceFiatMicro(args: {
744
615
  currency: string;
745
616
  };
746
617
  }): PriceFiat | undefined;
747
- /**
748
- * Why a denomination could not be produced (internal-review). Carried out of
749
- * {@link msatsToFiatMicro} and {@link resolveFxSnapshot} so the skip that
750
- * follows can name its cause.
751
- *
752
- * Every one of these used to collapse into a bare `undefined`, which is how a
753
- * `credit_topup_skipped` / `denomination_unavailable` line came to be
754
- * undiagnosable: a rate outage, a currency the snapshot never carried, and an
755
- * ask too small to express in micro-units all read identically in the log, and
756
- * the only way to tell them apart was to re-derive the whole path by hand.
757
- */
618
+ /** Reasons are kept distinct so a skipped ledger leg identifies its cause. */
758
619
  type FiatDenominationFailure =
759
620
  /** Nothing to convert — a zero-msat ask. */
760
621
  "no_msats"
@@ -771,7 +632,7 @@ type FiatDenominationFailure =
771
632
  | "rate_stale_refused";
772
633
  /**
773
634
  * An fx snapshot plus how fresh it is, or why there is none. `stale` means the
774
- * live fetch failed and this is the last snapshot the fetcher saw (internal-review);
635
+ * live fetch failed and this is the last snapshot the fetcher saw;
775
636
  * `cachedAt` is the Unix ms it landed.
776
637
  */
777
638
  type ResolvedFx = {
@@ -790,7 +651,7 @@ type ResolvedFx = {
790
651
  * `stale` covers both degradation rungs and `carriedForward` distinguishes
791
652
  * them: either the whole snapshot came from `lastKnown()` because the live
792
653
  * fetch failed, or the fetch succeeded and only *this* currency was carried
793
- * over from an earlier one because the body omitted it (internal-review). They read
654
+ * over from an earlier one because the body omitted it. They read
794
655
  * differently to an operator — the rate source down, versus the rate source
795
656
  * having quietly narrowed — so the log says which.
796
657
  */
@@ -806,80 +667,51 @@ type FiatDenomination = {
806
667
  detail?: string;
807
668
  };
808
669
  /**
809
- * Resolve an fx snapshot for a denomination hop, degrading to the fetcher's
810
- * last-known one when the live fetch fails (internal-review).
811
- *
812
- * The same ladder `resolveServerBtcUsdRate` walks for the x402/Tempo rails
813
- * (`btc-rate.ts`), and it exists here because the mid-job top-up's two fx legs
814
- * — the ask-time pin in `context.ts` and the verify-time hop in
815
- * {@link msatsToFiatMicro} — were the only ones in the payment layer that
816
- * called `fetch()` raw and swallowed the throw. `FxFetcher.lastKnown` is
817
- * documented as "the offline degradation source for the payment rail", so a
818
- * process holding a perfectly good snapshot was nonetheless dropping the
819
- * receipt's whole `credit` block over one blip at the rate provider.
820
- *
821
- * **A known-stale refusal fails closed and never degrades** (internal-review): the
822
- * platform decided the snapshot is too old to price against, so serving the
823
- * last-known one prices off exactly what it refused. Every other failure
824
- * degrades, which is the internal-review posture.
825
- *
826
- * No cool-down latch, unlike `resolveServerBtcUsdRate`. That one guards a
827
- * per-request hot path; this runs at most twice per mid-job ask, and a latch
828
- * shared across every DVM in the process would suppress the recovery probe a
829
- * long-lived host needs.
670
+ * A live fetch failure may use lastKnown(), but a known-stale refusal never
671
+ * does: that would use the very snapshot the platform refused. No shared
672
+ * cooldown suppresses the recovery probe on a later mid-job ask.
830
673
  */
831
674
  declare function resolveFxSnapshot(fetcher: FxFetcher): Promise<ResolvedFx>;
832
675
  /**
833
- * Convert an msat amount into a target currency's micro-units through the
834
- * shared fx snapshot (internal-review) — the denomination for a mid-job
835
- * `requestPayment` ask that carries no fiat envelope of its own.
836
- *
837
- * internal-review removed the numeric-msats *price*, so {@link resolvePriceFiat} needs
838
- * no rate; the mid-job ask is the one surface where a bare msat amount survives
839
- * (`ctx.requestPayment(50_000, …)`), and the credit it tops up may be
840
- * denominated in a currency the ask never named. An fx hop is the only way to
841
- * put either on the ledger.
842
- *
843
- * Answers a *reason* rather than `undefined` when it can't denominate — the
844
- * caller still accepts the money and skips the ledger leg rather than refusing
845
- * over a rate outage (internal-review), but it can now say which failure it degraded
846
- * on (internal-review). That fail-soft is why this stays separate from
847
- * `resolvePriceFiat`, which is now total for every paid request.
676
+ * Bare-msat mid-job asks need an FX conversion into the credit's currency.
677
+ * Failure carries a reason: callers accept the payment and skip its ledger
678
+ * leg rather than refusing over an outage. Upfront prices use resolvePriceFiat
679
+ * and require no rate, so they do not share this fail-soft behavior.
848
680
  */
849
681
  declare function msatsToFiatMicro(args: {
850
682
  msats: number;
851
683
  currency: string;
852
684
  fxFetcher: FxFetcher;
853
685
  /**
854
- * A snapshot already in hand (internal-review). The mid-job ask path fetches one to
686
+ * A snapshot already in hand. The mid-job ask path fetches one to
855
687
  * size a fiat-form ask's msats; converting on that same snapshot costs no
856
688
  * second fetch and keeps the two figures arithmetically consistent.
857
689
  */
858
690
  snapshot?: FxRateSnapshot;
859
691
  }): Promise<FiatDenomination>;
860
692
  /**
861
- * The mid-job ask's fiat pin, in the target currency (internal-review) — what the cap
693
+ * The mid-job ask's fiat pin, in the target currency — what the cap
862
694
  * on this job's cumulative draw growth is summed from, and what the top-up
863
695
  * funds against.
864
696
  *
865
697
  * Two differences from {@link msatsToFiatMicro}, both scoped to the ask:
866
698
  *
867
699
  * - **A fiat-form ask already in the target currency is taken verbatim**, so a
868
- * `ctx.requestPayment({ amount, currency })` keeps the exactness it has and
869
- * never round-trips through a rate. Any other form converts `msats` on the
870
- * caller's snapshot — including a fiat-form ask in some *other* currency,
871
- * which converts on the very snapshot that sized its msats, so the pin and
872
- * the wire ask can't disagree about the rate.
700
+ * `ctx.requestPayment({ amount, currency })` keeps the exactness it has and
701
+ * never round-trips through a rate. Any other form converts `msats` on the
702
+ * caller's snapshot — including a fiat-form ask in some *other* currency,
703
+ * which converts on the very snapshot that sized its msats, so the pin and
704
+ * the wire ask can't disagree about the rate.
873
705
  * - **Floors at one micro-unit rather than returning `undefined`.**
874
- * `ctx.requestPayment(500)` is half a micro at $100k/BTC — a legitimate, if
875
- * tiny, ask, and refusing to denominate it poisons the job's whole ask total
876
- * and with it the ceiling. One micro is provably conservative for a ceiling:
877
- * the ask is worth less than that. `resolvePriceFiat` and `msatsToFiatMicro`
878
- * keep their sub-micro refusals, which are load-bearing on the upfront path.
706
+ * `ctx.requestPayment(500)` is half a micro at $100k/BTC — a legitimate, if
707
+ * tiny, ask, and refusing to denominate it poisons the job's whole ask total
708
+ * and with it the ceiling. One micro is provably conservative for a ceiling:
709
+ * the ask is worth less than that. `resolvePriceFiat` and `msatsToFiatMicro`
710
+ * keep their sub-micro refusals, which are load-bearing on the upfront path.
879
711
  *
880
712
  * Throws only what `fxRateFor` throws — an unsupported target currency, or an
881
713
  * `fx_rate_unavailable` for one the snapshot never carried. The ask path catches
882
- * both and degrades to an unpinnable ask (internal-review), naming which it was.
714
+ * both and degrades to an unpinnable ask, naming which it was.
883
715
  */
884
716
  declare function pinAskFiat(args: {
885
717
  msats: number;
@@ -892,65 +724,29 @@ declare function pinAskFiat(args: {
892
724
  }): PriceFiat | undefined;
893
725
  /**
894
726
  * Fiat micro-units actually funded for a rail payment of `paidMsats` against
895
- * a job quoted at `requiredMsats` ↔ `priceFiat` (operator ruling: overpay
896
- * funds and draws the FULL paid amount — ledger truth equals money truth).
727
+ * a job quoted at `requiredMsats` ↔ `priceFiat`. An overpayment funds and draws
728
+ * the full paid amount so ledger value matches money received.
897
729
  * Pro-rata at the quote-pinned ratio, so the exact-payment case — what every
898
730
  * conformant client sends — is `priceFiat.amountMicro` with zero drift.
899
731
  */
900
732
  declare function fundedMicroFor(priceFiat: PriceFiat, paidMsats: number, requiredMsats: number): number;
901
733
  /**
902
- * The µUSDC figure an x402 402 advertises as `maxAmountRequired`, and the same
903
- * figure the settled authorization is valued against at submit (internal-review).
904
- *
905
- * **A USD-priced job states its price verbatim.** USDC is a six-decimal USD
906
- * stablecoin, and `priceFiat.amountMicro` is already an exact USD micro figure,
907
- * so the advertisement is the price — no rate in the derivation, and therefore
908
- * no way for the 402 and the submit to disagree about it.
909
- *
910
- * The BTC hop this replaces was not merely lossy, it was *unpinned*. The 402 is
911
- * stateless, so the figure was recomputed at submit from whatever the fx cache
912
- * held by then, through two rate-dependent roundings: `resolveStaticPriceMsats`
913
- * ceils the price to a whole **satoshi**, and `msatsToUsdc` ceils that back to
914
- * µUSDC. A rate refresh between the 402 and the payment therefore moved the
915
- * reconstruction by up to a satoshi's worth (~$0.0013 at $100k/BTC) — and when
916
- * it moved down, the caller's exact payment read as an overpayment, pro-rated
917
- * above `price_micro`, and was refused `funding_commitment_mismatch` *after*
918
- * the facilitator had settled. Money moved, no job, no credit. The sat-ceil
919
- * also overcharged every x402 caller outright: at $0.01 and $97,432/BTC the
920
- * quote is 11 sats and the old advertisement demanded 10,718 µUSDC — 7% above
921
- * the price the caller was quoted and the ledger recorded.
922
- *
923
- * Every caller gates x402 on a USD-priced DVM before reaching this helper.
924
- * Keeping the helper USD-only makes a future advertisement call site fail
925
- * closed instead of quietly restoring the rate-derived non-USD path.
734
+ * USD micro-units are already USDC's six-decimal units. Advertise them verbatim
735
+ * and value settlement against the same figure; a BTC conversion would add
736
+ * rounding and an unpinned rate between challenge and settlement.
737
+ * Non-USD callers fail closed rather than implicitly introducing that FX hop.
926
738
  */
927
739
  declare function x402RequiredUsdcMicro(priceFiat: PriceFiat): number;
928
740
  /**
929
- * A settled x402 authorization's share of a quoted figure, taken against the
930
- * `maxAmountRequired` the 402 advertised (internal-review) — `quoted` unmodified when
931
- * the authorization paid exactly that, pro-rata otherwise. Unit-agnostic like
932
- * {@link fundedMicroFor}: the same ratio restates the job's fiat price and its
933
- * msat quote, so the two frames can never disagree about what landed.
934
- *
935
- * x402 must NOT reach {@link fundedMicroFor} through `usdcToMsats`. The
936
- * advertised figure is stated in µUSDC by {@link x402RequiredUsdcMicro} — the
937
- * price verbatim for a USD job — and the
938
- * receipt converts back with `usdcToMsats`, a `Math.round`, which is the
939
- * inverse of neither. The round trip lands at or above `requiredMsats`, which
940
- * took the pro-rata branch and valued a payment of exactly the advertised
941
- * amount above `price_micro`. That mismatch used to be refused only after the
942
- * facilitator had broadcast the transfer, so a conformant caller signing the
943
- * quoted price lost the payment outright (the gate now also runs pre-settle —
944
- * internal-review).
945
- *
946
- * Valuing in the rail's own units removes the manufactured disagreement and
947
- * nothing else: a genuine overpayment still pro-rates, and both inputs are on
948
- * the 402, so the caller can derive the figure it must sign from that alone.
741
+ * Value a settled authorization against the advertised USDC amount, preserving
742
+ * the quote at exact payment and prorating genuine over/underpayment. Use this
743
+ * ratio for both fiat and msat frames: a USDC-to-msat round trip can round an
744
+ * exact payment into a false overpayment and invalidate its signed commitment.
949
745
  */
950
746
  declare function x402SettledShare(quoted: number, requiredUsdcMicro: number, settledUsdcMicro: number): number;
951
747
  /**
952
748
  * SHA-256 hex over the raw funding artifact — the value an explicit
953
- * fund-and-draw request must sign as `fund.commitment` (spec §2 condition 3).
749
+ * fund-and-draw request must sign as `fund.commitment` (the funding-commitment rule).
954
750
  * The artifact is the exact header-borne string: the `X-Cashu` header value,
955
751
  * the `X-PAYMENT` header value, or the extracted `Payment <…>` mpp scheme
956
752
  * string. Hashing the verbatim wire string keeps the commitment computable by
@@ -964,14 +760,14 @@ declare function fundingCommitment(artifact: string): string;
964
760
  declare function implicitCreditId(rail: "cashu" | "x402" | "tempo", paymentId: string): string;
965
761
  /**
966
762
  * Whether a credit id sits in a namespace the server derives — which a caller
967
- * may **reference** but must never **create** (internal-review).
763
+ * may **reference** but must never **create**.
968
764
  *
969
765
  * Both namespaces are computable by a third party: `imp:` from the rail's
970
766
  * payment id, `fnd:` from `(pubkey, fund_id)`. `CreditLedger.fund` INSERTs at
971
767
  * whatever id it is handed, so without this rule anyone could pre-open a
972
768
  * credit at an id the server would later derive for someone else. The victim's
973
769
  * top-up then settles on the rail before `fund` refuses with `caller_mismatch`
974
- * — they pay and hold nothing. Keeping creation server-side means a derived id
770
+ * they pay and hold nothing. Keeping creation server-side means a derived id
975
771
  * is only ever owned by the caller it was derived for.
976
772
  *
977
773
  * Referencing stays open, and has to: the id of a credit opened this way is
@@ -981,7 +777,7 @@ declare function implicitCreditId(rail: "cashu" | "x402" | "tempo", paymentId: s
981
777
  declare function isDerivedCreditId(creditId: string): boolean;
982
778
  /**
983
779
  * Deterministic credit id for a `/v1/credit` top-up that names no credit —
984
- * the documented "omit `credit_id` to open a new one" flow (internal-review).
780
+ * the documented "omit `credit_id` to open a new one" flow.
985
781
  *
986
782
  * Minting a fresh random id per attempt would make that flow the one top-up
987
783
  * shape that is *not* retry-safe: the second attempt looks up
@@ -998,8 +794,8 @@ declare function isDerivedCreditId(creditId: string): boolean;
998
794
  */
999
795
  declare function derivedFundCreditId(callerPubkey: string, fundId: string): string;
1000
796
  /**
1001
- * Settlement reference stamped on the revenue event of an **explicit** draw
1002
- * (internal-review). One funding backs N draws, so the funding's own rail reference
797
+ * Settlement reference stamped on the revenue event of an **explicit** draw;
798
+ * one funding backs N draws, so the funding's own rail reference
1003
799
  * cannot key them: `revenue_events` carries a global `UNIQUE (rail, tx_hash)`
1004
800
  * that deliberately drops cross-job reuse of a settlement reference, and
1005
801
  * reusing it would silently discard every draw but the first.
@@ -1012,18 +808,8 @@ declare function derivedFundCreditId(callerPubkey: string, fundId: string): stri
1012
808
  declare function drawSettlementRef(dvmId: string, creditId: string, drawId: string): string;
1013
809
 
1014
810
  /**
1015
- * Durable marker for a rail payment that already funded the credit ledger
1016
- * (internal-review, spec §2 condition 3). The x402 and mpp rails have no dvmkit-side
1017
- * commit point of their own — the money moves at the facilitator / the payer's
1018
- * mpp server — so the marker row, inserted in the SAME transaction as
1019
- * `CreditLedger.fund` + `draw`, is what makes a crash-replayed credential
1020
- * unable to fund twice: the second attempt collides on the primary key and
1021
- * surfaces as {@link ProcessedPaymentReplayError}, which the verifier routes
1022
- * into the internal-review recovery gates instead of a second fund.
1023
- *
1024
- * The Cashu rail does not use this store — its accumulator insert is already
1025
- * the durable commit point, and its replay key (the `X-Cashu-Request-Id`
1026
- * regime) must stay exactly as-is (dual-idempotency rule, spec §2).
811
+ * x402/Tempo marker, funding and draw commit in one transaction so a retry
812
+ * cannot fund twice. Cashu retains its separate accumulator replay regime.
1027
813
  */
1028
814
  interface ProcessedPaymentStore {
1029
815
  /**
@@ -1049,11 +835,8 @@ interface ProcessedPaymentStore {
1049
835
  paymentId: string;
1050
836
  }): Promise<ProcessedPaymentRecord | undefined>;
1051
837
  /**
1052
- * Run `fn` inside one transaction (Postgres: BEGIN → fn → COMMIT, rollback
1053
- * on throw). The verifier funds and draws the ledger inside `fn`, passing
1054
- * the handle through to `record` / `CreditLedger.fund` / `draw` so all
1055
- * three commit atomically. The memory implementation invokes `fn` with no
1056
- * handle — its operations are synchronous-atomic in-process.
838
+ * Postgres supplies a transaction for marker, fund and draw; failure rolls it
839
+ * back. Memory invokes fn without a transactional handle or rollback.
1057
840
  */
1058
841
  withTx<T>(fn: (tx?: ProcessedPaymentQuerier) => Promise<T>): Promise<T>;
1059
842
  }
@@ -1086,7 +869,7 @@ declare class PostgresProcessedPaymentStore implements ProcessedPaymentStore {
1086
869
  withTx<T>(fn: (tx?: ProcessedPaymentQuerier) => Promise<T>): Promise<T>;
1087
870
  /** Create the `processed_payments` table if absent. Call once at SDK boot. */
1088
871
  init(): Promise<void>;
1089
- /** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
872
+ /** The boot DDL itself — always runs under {@link withSdkInitLock}. */
1090
873
  private createTables;
1091
874
  record(args: {
1092
875
  dvmId: string;
@@ -1128,11 +911,10 @@ declare class MemoryProcessedPaymentStore implements ProcessedPaymentStore {
1128
911
  /**
1129
912
  * Machine codes an accumulator-path receive can reject with. Named and exported
1130
913
  * so the payment-error builder in `payment.ts` folds them into `PaymentErrorCode`
1131
- * and keeps display/hint/retryable copy in lockstep with this union (internal-review).
914
+ * and keeps display/hint/retryable copy in lockstep with this union.
1132
915
  */
1133
916
  type AccumulatorRejectionCode = "missing_request_id" | "missing_lock_pubkey" | "not_p2pk_locked" | "wrong_lock_pubkey" | "proof_not_unspent" | "proof_pending" | "replay_rejected" | "mint_unreachable" | "invalid_token";
1134
917
 
1135
- /** Durable state of one exact x402 authorization. */
1136
918
  type X402ExactSettlementStatus = "prepared" | "submitting" | "settled" | "local_committed" | "ambiguous" | "rejected" | "complete";
1137
919
  /** Serializable local economic effect that follows an exact settlement. */
1138
920
  interface X402ExactSettlementEffect {
@@ -1181,7 +963,6 @@ interface X402ExactAuthorizationFacts {
1181
963
  nonce: string;
1182
964
  fingerprint: string;
1183
965
  }
1184
- /** One durable exact-x402 settlement intent. */
1185
966
  interface X402ExactSettlementIntent extends X402ExactAuthorizationFacts {
1186
967
  id: string;
1187
968
  dvmId: string;
@@ -1236,11 +1017,9 @@ interface X402ExactSettlementStore {
1236
1017
  }): Promise<X402ExactSettlementIntent[]>;
1237
1018
  attempts(dvmId: string, intentId: string): Promise<X402ExactSettlementAttempt[]>;
1238
1019
  }
1239
- /** Postgres-backed exact settlement intent store. */
1240
1020
  declare class PostgresX402ExactSettlementStore implements X402ExactSettlementStore {
1241
1021
  private readonly pool;
1242
1022
  constructor(pool: Pick<Pool, "query" | "connect">);
1243
- /** Create the exact settlement tables. */
1244
1023
  init(): Promise<void>;
1245
1024
  prepare(intent: Omit<X402ExactSettlementIntent, "status" | "createdAt" | "updatedAt" | "resolvedAt">): Promise<X402ExactSettlementIntent>;
1246
1025
  get(dvmId: string, intentId: string): Promise<X402ExactSettlementIntent | undefined>;
@@ -1349,7 +1128,6 @@ type X402ExactAcceptance<T> = {
1349
1128
  intentId: string;
1350
1129
  reason: string;
1351
1130
  };
1352
- /** Dependencies for the durable exact x402 settlement coordinator. */
1353
1131
  interface X402ExactSettlementServerOpts {
1354
1132
  store: X402ExactSettlementStore;
1355
1133
  evidence?: X402ExactSettlementEvidenceReader;
@@ -1372,13 +1150,11 @@ declare class X402ExactSettlementServer {
1372
1150
  effect?: X402ExactSettlementEffect;
1373
1151
  commit: (receipt: X402Receipt, tx?: ProcessedPaymentQuerier, effect?: X402ExactSettlementEffect) => Promise<T>;
1374
1152
  }): Promise<X402ExactAcceptance<T>>;
1375
- /** Read the operator queue. */
1376
1153
  list(args: {
1377
1154
  dvmId: string;
1378
1155
  limit: number;
1379
1156
  includeResolved?: boolean;
1380
1157
  }): Promise<X402ExactSettlementIntent[]>;
1381
- /** Read durable attempt evidence for one intent. */
1382
1158
  attempts(dvmId: string, intentId: string): Promise<X402ExactSettlementAttempt[]>;
1383
1159
  /** Reconcile and finish one intent from chain evidence. */
1384
1160
  reconcile(dvmId: string, intentId: string): Promise<{
@@ -1437,7 +1213,7 @@ interface X402BatchChannelObservation {
1437
1213
  refundNonce: number;
1438
1214
  }
1439
1215
  /**
1440
- * The chain submission behind an accepted voucher did not return success (internal-review).
1216
+ * The chain submission behind an accepted voucher did not return success.
1441
1217
  *
1442
1218
  * Typed and `retryable` because it is a service-availability fault, not a fault
1443
1219
  * in the credential: the voucher verified, the ledger leg rolled back, and the
@@ -1479,16 +1255,16 @@ interface X402BatchRefusal {
1479
1255
  * `false` when re-signing cannot fix this: the caller must be answered with a
1480
1256
  * terminal error rather than a 402 carrying fresh requirements, because an
1481
1257
  * agent reads a corrective 402 as "sign this and resubmit" and lands straight
1482
- * back on the same refusal (internal-review). Omitted means corrective.
1258
+ * back on the same refusal. Omitted means corrective.
1483
1259
  */
1484
1260
  corrective?: boolean;
1485
1261
  }
1486
1262
  /**
1487
- * What the chain says about one wedged settlement (internal-review).
1263
+ * What the chain says about one wedged settlement.
1488
1264
  *
1489
1265
  * The four answers are kept apart because their repairs differ and because an
1490
1266
  * operator re-pointing an RPC needs a definitive answer to read as definitive
1491
- * — the same split `LightningReceive.lookupSettlement` draws between
1267
+ * the same split `LightningReceive.lookupSettlement` draws between
1492
1268
  * `invoice_unknown_to_wallet` and `wallet_unreachable`. `findAppliedSettlement`
1493
1269
  * collapses all of them (`undefined` for absent *and* unbookmarked, a throw for
1494
1270
  * transport), so this is where they are separated.
@@ -1563,7 +1339,7 @@ interface X402SettlementRepaired<T> {
1563
1339
  }
1564
1340
  /**
1565
1341
  * The operator's exit from a settlement stuck between "chain paid" and "ledger
1566
- * booked" (internal-review).
1342
+ * booked".
1567
1343
  *
1568
1344
  * `reconcile` is the ordinary recovery path with the chain's figure substituted
1569
1345
  * for the facilitator's: the amount is re-derived from
@@ -1617,7 +1393,7 @@ interface X402BatchSettlementServer {
1617
1393
  * Required on `refund`: the credit's unspent balance in the channel token's
1618
1394
  * own units. It is the ceiling the on-chain payout is sized to, so that a
1619
1395
  * drain returns what the caller is owed and nothing the DVM has earned
1620
- * (internal-review). `commit` must re-assert it against the authoritative ledger
1396
+ * `commit` must re-assert it against the authoritative ledger
1621
1397
  * figure, since this one is read before the channel lock is taken.
1622
1398
  */
1623
1399
  refundBoundNative?: number;
@@ -1633,7 +1409,7 @@ interface X402BatchSettlementServer {
1633
1409
  * required in the *type*, because the payload the payer presents is
1634
1410
  * what revokes, settles and pays: a refund voucher naming another
1635
1411
  * channel would extinguish this credit's balance out of that channel's
1636
- * escrow (internal-review). The fund path binds in the ledger
1412
+ * escrow. The fund path binds in the ledger
1637
1413
  * (`credit-ledger.ts`'s `rail_mismatch`) and the Tempo close binds in
1638
1414
  * its credential verifier; this is x402's.
1639
1415
  */
@@ -1648,20 +1424,19 @@ interface X402BatchSettlementServer {
1648
1424
  claimAndSettle(): Promise<void>;
1649
1425
  /**
1650
1426
  * Operator repair for a settlement wedged between chain and ledger
1651
- * (internal-review). Absent without durable storage: with no settlement rows there
1427
+ * Absent without durable storage: with no settlement rows there
1652
1428
  * is no queue and nothing a repair could act on.
1653
1429
  */
1654
1430
  repair?: X402SettlementRepair;
1655
1431
  /**
1656
1432
  * The durable settlement read the credit ledger gates spending on
1657
- * (internal-review). Absent without durable storage, on the same reasoning as
1433
+ * Absent without durable storage, on the same reasoning as
1658
1434
  * {@link repair}: with no settlement rows nothing can be wedged, so there is
1659
1435
  * nothing to gate.
1660
1436
  */
1661
1437
  settlementGate?: X402RefundSettlementGate;
1662
1438
  stop(): Promise<void>;
1663
1439
  }
1664
- /** Options for constructing one DVM's batch-settlement server. */
1665
1440
  interface CreateX402BatchSettlementServerOpts {
1666
1441
  network: string;
1667
1442
  payTo: string;
@@ -1677,12 +1452,12 @@ interface CreateX402BatchSettlementServerOpts {
1677
1452
  rpcUrl?: string;
1678
1453
  db?: Pool;
1679
1454
  /**
1680
- * Durable store to adopt instead of building one from {@link db} (internal-review).
1455
+ * Durable store to adopt instead of building one from {@link db}.
1681
1456
  *
1682
1457
  * `createDVMServer` builds it at ledger construction, because the credit
1683
1458
  * ledger's spend gate has to exist whether or not this server is ever
1684
1459
  * constructed. Adopting it here keeps one `PostgresX402ChannelStorage` — and
1685
- * so one settlement pool — per mount, which is what the internal-review reasoning on
1460
+ * so one settlement pool — per mount, which is what the reasoning on
1686
1461
  * {@link PostgresX402ChannelStorage} rests on. Ignored when the config
1687
1462
  * supplies its own {@link X402BatchSettlementConfig.storage}.
1688
1463
  */
@@ -1694,7 +1469,7 @@ interface CreateX402BatchSettlementServerOpts {
1694
1469
  * `undefined` means the channel's credit could not be resolved.
1695
1470
  *
1696
1471
  * Required, not optional: it is the ceiling every claim is capped at
1697
- * (internal-review), and a server that cannot derive one must claim nothing rather
1472
+ * and a server that cannot derive one must claim nothing rather
1698
1473
  * than fall back to the deposit.
1699
1474
  */
1700
1475
  earnedNative: (channelId: string) => Promise<number | undefined>;
@@ -1705,13 +1480,13 @@ interface CreateX402BatchSettlementServerOpts {
1705
1480
  /** Authoritative channel-view override for deterministic tests. */
1706
1481
  channelStateReader?: (channelId: string) => Promise<X402BatchChannelObservation>;
1707
1482
  /**
1708
- * The payout tracker (internal-review). Attached with this server's settle scope
1483
+ * The payout tracker. Attached with this server's settle scope
1709
1484
  * and routed every claim and settle, so the batch that lands at `pay_to` is
1710
1485
  * reported with the value claimed into it, and the pending snapshot can say
1711
1486
  * what is still on the channels and whether the settle is wedged.
1712
1487
  *
1713
1488
  * @internal Wired by `createDVMServer` from the platform reporter; not a
1714
- * builder surface, and not part of the extractable SDK contract (internal-review).
1489
+ * builder surface, and not part of the extractable SDK contract.
1715
1490
  */
1716
1491
  payout?: X402PayoutObserver;
1717
1492
  }
@@ -1723,12 +1498,11 @@ interface CreateX402BatchSettlementServerOpts {
1723
1498
  */
1724
1499
  declare function createX402BatchSettlementServer(opts: CreateX402BatchSettlementServerOpts): Promise<X402BatchSettlementServer>;
1725
1500
 
1726
- /** Payment state from upfront token verification. */
1727
1501
  interface PaymentInfo {
1728
1502
  /**
1729
1503
  * Rail value this payment contributes to the job, in millisatoshis.
1730
1504
  *
1731
- * For a credit-drawn job (internal-review) this is the **draw's** allocated share of
1505
+ * For a credit-drawn job this is the **draw's** allocated share of
1732
1506
  * the credit's rail value, not the amount the caller handed over on this
1733
1507
  * request — under an explicit fund-and-draw the caller may have funded twenty
1734
1508
  * jobs' worth. That makes it the revenue basis for this job: mid-job top-ups
@@ -1739,38 +1513,36 @@ interface PaymentInfo {
1739
1513
  /**
1740
1514
  * Rail that satisfied the upfront payment, if any. A {@link FundingMethod}
1741
1515
  * rather than a `PaymentMethod`: a draw against a Lightning-funded credit
1742
- * reports `"lightning"` here (internal-review), which is what lets the revenue
1516
+ * reports `"lightning"` here, which is what lets the revenue
1743
1517
  * ledger book it instead of silently dropping the row.
1744
1518
  */
1745
1519
  paymentRail?: FundingMethod;
1746
1520
  receivedProofs: ProofLike[];
1747
1521
  /**
1748
1522
  * Settlement reference for the credit. x402 carries the EVM tx hash from
1749
- * the facilitator (internal-review); mpp carries the credential's `challenge.id`,
1750
- * HMAC-bound to the realm so cross-DVM collisions are impossible (internal-review);
1751
- * cashu accumulator (internal-review) sets it to the `X-Cashu-Request-Id` UUID
1752
- * (internal-review). Required by the platform revenue ledger for `tempo`/`x402`
1523
+ * the facilitator; mpp carries the credential's `challenge.id`,
1524
+ * HMAC-bound to the realm so cross-DVM collisions are impossible;
1525
+ * cashu accumulator sets it to the `X-Cashu-Request-Id` UUID
1526
+ * Required by the platform revenue ledger for `tempo`/`x402`
1753
1527
  * rails; nullable for cashu's `tx_hash` column.
1754
1528
  */
1755
1529
  paymentTxHash?: string;
1756
1530
  /** Chain transaction returned to a caller recovering this accepted request. */
1757
1531
  paymentTransactionHash?: string;
1758
1532
  /**
1759
- * Rail-native payment amount in atomic units (internal-review). Sats for mpp,
1760
- * USDC microunits for x402. Cashu leaves it unset — the existing
1761
- * `paidMsats / 1000` derivation in the platform callback covers it.
1533
+ * Stablecoin micro-units for Tempo and x402. Cashu leaves this unset; its revenue is derived
1534
+ * from paidMsats.
1762
1535
  */
1763
1536
  nativeAmount?: number;
1764
- /** Native asset tag matching `revenue_events.native_asset` (internal-review). */
1765
1537
  nativeAsset?: "sats" | "usdc" | "usdc.e" | "usd-cents";
1766
1538
  /**
1767
1539
  * Discriminator written into `revenue_events.metadata.cashu_flow` so ops
1768
- * queries can split per-call cashu rows by source path (internal-review).
1540
+ * queries can split per-call cashu rows by source path.
1769
1541
  */
1770
1542
  cashuFlow?: "p2pk_accumulator";
1771
1543
  /**
1772
1544
  * Base64-encoded x402 `SettleResponse` for the `X-PAYMENT-RESPONSE`
1773
- * response header (internal-review). Set on the upfront-flow x402 success path;
1545
+ * response header. Set on the upfront-flow x402 success path;
1774
1546
  * `app.ts` attaches it to the 2xx response per x402 spec.
1775
1547
  */
1776
1548
  x402SettleResponseHeader?: string;
@@ -1781,21 +1553,21 @@ interface PaymentInfo {
1781
1553
  replayed: boolean;
1782
1554
  };
1783
1555
  /**
1784
- * Credit-ledger linkage (internal-review). Set whenever the payment funded and/or
1556
+ * Credit-ledger linkage. Set whenever the payment funded and/or
1785
1557
  * drew the ledger — every paid job with a ledger wired. Persisted on the
1786
1558
  * job so the terminal funnel can settle/release the draw and the receipt
1787
1559
  * can countersign the `ReceiptCredit` block.
1788
1560
  */
1789
1561
  creditId?: string;
1790
- /** The draw placed for this job on `creditId` (internal-review). */
1562
+ /** The draw placed for this job on `creditId`. */
1791
1563
  drawId?: string;
1792
- /** The draw's fiat amount, 1e-6 of {@link creditCurrency} (internal-review). */
1564
+ /** The draw's fiat amount, 1e-6 of {@link creditCurrency}. */
1793
1565
  drawAmountMicro?: number;
1794
- /** Currency the credit — and therefore the draw — is denominated in (internal-review). */
1566
+ /** Currency the credit — and therefore the draw — is denominated in. */
1795
1567
  creditCurrency?: string;
1796
1568
  /**
1797
1569
  * The funding leg, when this request actually moved money onto a credit
1798
- * (internal-review). Reported to the platform as a **deposit**: a liability until
1570
+ * Reported to the platform as a **deposit**: a liability until
1799
1571
  * drawn, never summed into revenue. Absent on a pure draw against an
1800
1572
  * existing balance.
1801
1573
  */
@@ -1805,7 +1577,7 @@ interface PaymentInfo {
1805
1577
  fundingCredit?: CreditSnapshot;
1806
1578
  /**
1807
1579
  * Fiat micro of {@link creditCurrency} this payment funded that bought the
1808
- * job **nothing** (internal-review) — a second payment for an ask the job's draw
1580
+ * job **nothing** — a second payment for an ask the job's draw
1809
1581
  * already covers, or the overpaid tail of an oversized one. It is available
1810
1582
  * balance on {@link creditId}, drainable at once, and the caller has to be
1811
1583
  * told or they will re-send. Absent on every ordinary payment.
@@ -1813,7 +1585,7 @@ interface PaymentInfo {
1813
1585
  unappliedMicro?: number;
1814
1586
  }
1815
1587
  /**
1816
- * A funding event as the platform books it — one deposit row (internal-review, spec
1588
+ * A funding event as the platform books it — one deposit row (spec
1817
1589
  * §10). Keyed on `(dvmId, creditId, fundingId)`, so a retried report is a
1818
1590
  * no-op rather than a double-count.
1819
1591
  */
@@ -1845,12 +1617,12 @@ interface CreditFundingReport {
1845
1617
  channelId: string;
1846
1618
  cumulativeAmount: string;
1847
1619
  };
1848
- /** TIP-1034 chain binding, reported so the platform can route a close (internal-review). */
1620
+ /** TIP-1034 chain binding, reported so the platform can route a close. */
1849
1621
  tempoChannel?: TempoChannelReport;
1850
1622
  }
1851
1623
  /**
1852
1624
  * The chain identity of a TIP-1034 channel, as reported to the platform
1853
- * alongside its deposit (internal-review). Read from the DVM's own durable channel
1625
+ * alongside its deposit. Read from the DVM's own durable channel
1854
1626
  * store, never from the caller's credential.
1855
1627
  */
1856
1628
  interface TempoChannelReport {
@@ -1858,7 +1630,6 @@ interface TempoChannelReport {
1858
1630
  escrow: string;
1859
1631
  payee?: string;
1860
1632
  }
1861
- /** Options for verifying an upfront payment on job submission. */
1862
1633
  interface UpfrontPaymentOpts {
1863
1634
  cashuToken?: string;
1864
1635
  requiredMsats?: number;
@@ -1875,7 +1646,7 @@ interface UpfrontPaymentOpts {
1875
1646
  /**
1876
1647
  * Resource URL the client retried against. Bound into the rebuilt
1877
1648
  * `PaymentRequirements` when verifying x402, and surfaced on the 402 body
1878
- * `accepts` array for x402 dual-serve (internal-review).
1649
+ * `accepts` array for x402 dual-serve.
1879
1650
  */
1880
1651
  x402Resource?: string;
1881
1652
  /** Either the structured credential object or its serialized `Authorization: Payment <…>` value. */
@@ -1884,17 +1655,17 @@ interface UpfrontPaymentOpts {
1884
1655
  /**
1885
1656
  * Request path of the resource being paid for (e.g. `/v1/job`). Bound into
1886
1657
  * each issued mppx challenge's `meta.path` so a credential can't be replayed
1887
- * against a different paid endpoint of the same DVM (internal-review).
1658
+ * against a different paid endpoint of the same DVM.
1888
1659
  */
1889
1660
  resourcePath?: string;
1890
1661
  /**
1891
1662
  * Whether `tempo/session` may be issued and newly accepted right now — the
1892
- * platform close-observer and hosted settlement-readiness gates (internal-review,
1893
- * internal-review), resolved per request by `DVMServer`. Absent means offered: a
1663
+ * platform close-observer and hosted settlement-readiness gates, resolved per
1664
+ * request by `DVMServer`. Absent means offered: a
1894
1665
  * self-hosted DVM operates these responsibilities itself.
1895
1666
  *
1896
1667
  * `false` refuses a session credential that would open a channel the platform
1897
- * cannot safely protect or settle (internal-review, internal-review), and withholds session
1668
+ * cannot safely protect or settle, and withholds session
1898
1669
  * challenges from every 402 this call emits, *except* on a credit already
1899
1670
  * bound to a TIP-1034 channel. That deposit is already exposed, so refusing
1900
1671
  * its vouchers only strands it; and since a channel-bound credit can be funded
@@ -1907,11 +1678,10 @@ interface UpfrontPaymentOpts {
1907
1678
  /**
1908
1679
  * Tracks consumed `(realm, challenge.id)` pairs so a credential that was
1909
1680
  * already accepted on the upfront path can't be replayed within its
1910
- * `expires` window (internal-review). Optional: when absent, the upfront flow has
1681
+ * `expires` window. Optional: when absent, the upfront flow has
1911
1682
  * no per-process replay protection beyond mppx's own expiry check.
1912
1683
  */
1913
1684
  consumedCredentialStore?: ConsumedCredentialStore;
1914
- /** Cashu receive mode (internal-review / internal-review). Routes the receive path. */
1915
1685
  cashuMode?: CashuMode;
1916
1686
  /** Per-DVM Postgres pool — required for the `p2pk-accumulator` path. */
1917
1687
  accumulatorDb?: AccumulatorPool;
@@ -1921,7 +1691,7 @@ interface UpfrontPaymentOpts {
1921
1691
  /**
1922
1692
  * Active NUT-11 P2PK lock pubkeys — required for the `p2pk-accumulator`
1923
1693
  * path. Includes the current pubkey followed by retired pubkeys still
1924
- * within the rotation grace window (internal-review). Caller resolves via
1694
+ * within the rotation grace window. Caller resolves via
1925
1695
  * `loadLockPubkeyState` per-request so rotations propagate without a
1926
1696
  * server restart.
1927
1697
  */
@@ -1929,7 +1699,7 @@ interface UpfrontPaymentOpts {
1929
1699
  /** UUIDv4 from `X-Cashu-Request-Id` header. */
1930
1700
  requestId?: string;
1931
1701
  /**
1932
- * Runtime mint-health tracker (internal-review). Receive path short-circuits with
1702
+ * Runtime mint-health tracker. Receive path short-circuits with
1933
1703
  * `cashu_mint_sick` when the token's mint is currently flagged sick, so
1934
1704
  * agents retry against a healthy alternative without waiting for the
1935
1705
  * per-call mint timeout to fire inside `acceptAccumulatorPayment`.
@@ -1937,37 +1707,34 @@ interface UpfrontPaymentOpts {
1937
1707
  mintHealthTracker?: MintHealthTracker;
1938
1708
  /**
1939
1709
  * Host fx fetcher used to price the non-sat rails (x402 USDC, mppx fiat)
1940
- * through the same BTC/USD source the quote path uses (internal-review). On a hosted
1710
+ * through the same BTC/USD source the quote path uses. On a hosted
1941
1711
  * DVM this is the platform-served snapshot; self-hosted defaults to
1942
1712
  * CoinGecko-direct. Optional: a lazy default fetcher stands in when unset, so
1943
1713
  * the payment path never reaches `fetchBtcUsdRate`.
1944
1714
  */
1945
1715
  fxFetcher?: FxFetcher;
1946
1716
  /**
1947
- * Credit ledger every payment funds and draws through (internal-review, spec §1):
1948
- * an attached rail payment is an implicit N=1 funding — `fund` the paid
1949
- * amount + `draw` it, atomically with the rail commit. When absent (or
1950
- * when the price can't be fiat-denominated), the payment is accepted with
1951
- * legacy semantics and a structured warning — money is never refused
1952
- * because the ledger is unavailable.
1717
+ * Attached payments fund and draw an implicit per-job credit atomically with
1718
+ * rail accounting. Omitting the ledger uses the legacy ledgerless path;
1719
+ * a configured ledger requires a fiat-denominated price before acceptance.
1953
1720
  */
1954
1721
  creditLedger?: CreditLedgerLike;
1955
1722
  /**
1956
- * Durable processed-payment markers for the x402/mpp rails (internal-review).
1723
+ * Durable processed-payment markers for the x402/mpp rails.
1957
1724
  * Those rails commit money outside dvmkit, so the marker row — written in
1958
1725
  * the same transaction as the ledger fund — is what makes a crash-replayed
1959
1726
  * credential unable to fund twice.
1960
1727
  */
1961
1728
  processedPayments?: ProcessedPaymentStore;
1962
1729
  /**
1963
- * Reporter-owned transactional outbox enqueue (internal-review). When present,
1730
+ * Reporter-owned transactional outbox enqueue. When present,
1964
1731
  * every funding awaits this inside the rail's commit transaction, so money
1965
1732
  * and its durable deposit report land together.
1966
1733
  */
1967
1734
  enqueueCreditDeposit?: CreditDepositEnqueue;
1968
1735
  /**
1969
1736
  * The job price's fiat denomination, pinned by the caller from the same
1970
- * quote surface that produced `requiredMsats` (internal-review). The ledger is
1737
+ * quote surface that produced `requiredMsats`. The ledger is
1971
1738
  * fiat-micro denominated; fund = draw = this amount in the exact-payment
1972
1739
  * case, so no fresh fx conversion happens on the verify hot path.
1973
1740
  */
@@ -1983,14 +1750,14 @@ interface UpfrontPaymentOpts {
1983
1750
  /** Requester id — funder identity fallback for DVMs without descriptor auth. */
1984
1751
  requesterId?: string;
1985
1752
  /**
1986
- * Explicit credit fields extracted from the signed body (spec §2). Their
1753
+ * Explicit credit fields extracted from the signed body (the signed credit contract). Their
1987
1754
  * signature coverage and structural validation happen in `app.ts` before
1988
1755
  * payment verification; this layer enforces ownership, the funding
1989
1756
  * commitment, and the draw itself.
1990
1757
  */
1991
1758
  creditEnvelope?: CreditEnvelope;
1992
1759
  /**
1993
- * Turn this verification into a **fund-only top-up** (internal-review): the rails
1760
+ * Turn this verification into a **fund-only top-up**: the rails
1994
1761
  * verify and commit exactly as they do for a job, but the ledger leg funds
1995
1762
  * without drawing — there is no job to pay for. Set by `POST /v1/credit`;
1996
1763
  * `requiredMsats` / `priceFiat` carry the requested top-up amount rather
@@ -2021,26 +1788,25 @@ interface FundOnlyRequest {
2021
1788
  /** Client-generated idempotency key for this deposit (`credit_fundings` PK). */
2022
1789
  fundId: string;
2023
1790
  /**
2024
- * SHA-256 hex over the raw funding artifact, signed by the caller (spec §2
1791
+ * SHA-256 hex over the raw funding artifact, signed by the caller (the signed credit contract
2025
1792
  * condition 3). Checked before any rail work: it is what binds the artifact
2026
1793
  * presented on the header to the deposit named in the signed body.
2027
1794
  */
2028
1795
  commitment: string;
2029
1796
  }
2030
- /** Structured payment validation error detail carried through `PaymentValidationError`. */
2031
1797
  interface PaymentErrorDetail {
2032
1798
  body: Record<string, unknown>;
2033
1799
  status: number;
2034
1800
  /**
2035
1801
  * mppx challenges to advertise via `WWW-Authenticate` headers on the 402
2036
- * response (internal-review). Populated whenever the DVM has an mppx handle and
1802
+ * response. Populated whenever the DVM has an mppx handle and
2037
1803
  * the request is short on payment — including on payment_invalid, since
2038
1804
  * draft-ryan-httpauth-payment-01 requires a fresh challenge on rejection.
2039
1805
  */
2040
1806
  mppChallenges?: Challenge.Challenge[];
2041
1807
  /**
2042
1808
  * x402 `PaymentRequirements` to merge into the 402 body's `accepts`
2043
- * array under the `x402Version` envelope (internal-review). Populated when the
1809
+ * array under the `x402Version` envelope. Populated when the
2044
1810
  * DVM has x402 wired and dual-serve is on.
2045
1811
  */
2046
1812
  x402Requirements?: PaymentRequirements[];
@@ -2049,30 +1815,30 @@ interface PaymentErrorDetail {
2049
1815
  /**
2050
1816
  * Scheme-level error code for the x402 v2 envelope, kept apart from the
2051
1817
  * body's human `message` because a scheme client matches on it verbatim to
2052
- * decide whether a 402 is correctable (internal-review).
1818
+ * decide whether a 402 is correctable.
2053
1819
  */
2054
1820
  x402ErrorCode?: string;
2055
1821
  /**
2056
1822
  * On a `replay_rejected` from the x402/mpp processed-payment marker
2057
- * (internal-review): the payment id the original funding committed under. The
2058
- * route feeds it to the internal-review recovery gates (jobs persist it as
1823
+ * the payment id the original funding committed under. The
1824
+ * route feeds it to the recovery gates (jobs persist it as
2059
1825
  * `payment_tx_hash`) so a crash-replayed credential can recover its
2060
1826
  * original job instead of a bare 409.
2061
1827
  */
2062
1828
  replayPaymentId?: string;
2063
1829
  /**
2064
1830
  * A short payment that was kept under the fail-closed posture and settled
2065
- * on the spot (internal-review). The route books it as revenue under a synthetic
1831
+ * on the spot. The route books it as revenue under a synthetic
2066
1832
  * job id — no job exists to carry it — tagged so it stays separable from
2067
1833
  * per-job service revenue.
2068
1834
  */
2069
1835
  forfeit?: ShortPayForfeit;
2070
1836
  /**
2071
- * Money that landed on a credit even though this request failed (internal-review,
2072
- * internal-review). Set when a short mid-job pay funds without completing its ask,
1837
+ * Money that landed on a credit even though this request failed. Set when a
1838
+ * short mid-job pay funds without completing its ask,
2073
1839
  * or an explicit upfront fund-and-draw commits the deposit but the combined
2074
1840
  * balance cannot cover the draw. In both cases the caller keeps an available
2075
- * balance they can reclaim under the internal-review drain. That is a liability, so
1841
+ * balance they can reclaim under the drain. That is a liability, so
2076
1842
  * the deposit has to reach the platform on the 402 path exactly as it does on
2077
1843
  * the success path, or the outstanding-liability view is short by precisely
2078
1844
  * the money we still owe.
@@ -2085,7 +1851,7 @@ interface PaymentErrorDetail {
2085
1851
  */
2086
1852
  funding?: CreditFundingReport;
2087
1853
  }
2088
- /** A kept-but-undelivered short payment, ready to be booked as tagged revenue (internal-review). */
1854
+ /** A kept-but-undelivered short payment, ready to be booked as tagged revenue. */
2089
1855
  interface ShortPayForfeit {
2090
1856
  /** Cashu request id — the rail settlement reference. */
2091
1857
  paymentId: string;
@@ -2101,9 +1867,9 @@ type PaymentResult = PaymentInfo & {
2101
1867
  error?: PaymentErrorDetail;
2102
1868
  /**
2103
1869
  * The signed body's `(credit_id, draw_id)` matches an already-recorded
2104
- * draw (internal-review, spec §2 condition 1 — the explicit idempotency regime).
1870
+ * draw (the draw-idempotency rule — the explicit idempotency regime).
2105
1871
  * No rail work ran and no second hold was placed; the route applies the
2106
- * internal-review proof-of-origin gates and re-issues the original job's response.
1872
+ * proof-of-origin gates and re-issues the original job's response.
2107
1873
  */
2108
1874
  replayedDraw?: {
2109
1875
  creditId: string;
@@ -2112,7 +1878,7 @@ type PaymentResult = PaymentInfo & {
2112
1878
  };
2113
1879
  };
2114
1880
  /**
2115
- * Whether a dev-mode server skips payment verification outright (internal-review).
1881
+ * Whether a dev-mode server skips payment verification outright.
2116
1882
  *
2117
1883
  * Both upfront and mid-job acceptance require verification whenever any Cashu,
2118
1884
  * x402, or Tempo rail is configured. An unusable configured rail fails closed;
@@ -2134,7 +1900,7 @@ declare function devModeSkipsPaymentVerification(opts: {
2134
1900
  */
2135
1901
  declare const PAYMENT_PROOF_KEYS: readonly ["cashu_token", "x402_payment", "tempo_credential"];
2136
1902
  /**
2137
- * Whether a `payment` message carries a proof on any mid-job rail (internal-review).
1903
+ * Whether a `payment` message carries a proof on any mid-job rail.
2138
1904
  *
2139
1905
  * Deliberately colocated with the rail dispatch it mirrors, because two call
2140
1906
  * sites decide on it: `validateMessageContent` (`server/app.ts`) admits a
@@ -2146,26 +1912,26 @@ declare const PAYMENT_PROOF_KEYS: readonly ["cashu_token", "x402_payment", "temp
2146
1912
  declare function hasPaymentProof(content: Record<string, unknown>): boolean;
2147
1913
  /**
2148
1914
  * Issue one mppx Challenge per registered method for the upfront HTTP-transport
2149
- * 402 path (internal-review). Mirrors the message-based issuance loop in
1915
+ * 402 path. Mirrors the message-based issuance loop in
2150
1916
  * `context.ts` but binds challenges via `meta` (resource path + per-request
2151
- * nonce) instead of the per-job `pendingMppChallengeIds` binding (internal-review) —
1917
+ * nonce) instead of the per-job `pendingMppChallengeIds` binding —
2152
1918
  * there's no job at upfront challenge-issue time, so the binding has to live
2153
1919
  * inside the challenge itself.
2154
1920
  *
2155
1921
  * `meta.nonce` is unique-per-issue and HMAC-bound; consumed-id tracking on
2156
- * the verification side is wired via `ConsumedCredentialStore` (internal-review), so
1922
+ * the verification side is wired via `ConsumedCredentialStore`, so
2157
1923
  * a successfully-accepted credential can't be replayed within its `expires`
2158
1924
  * window even if it lands on the same paid endpoint.
2159
1925
  *
2160
- * `btcUsdRate` is resolved once per request by the caller (internal-review) rather
1926
+ * `btcUsdRate` is resolved once per request by the caller rather
2161
1927
  * than fetched here, so every rail this request advertises or verifies prices
2162
1928
  * off the same number and a rate-provider outage is handled in one place.
2163
1929
  *
2164
- * `sessionIssuable` is the platform close-observer gate (internal-review), threaded in
1930
+ * `sessionIssuable` is the platform close-observer gate, threaded in
2165
1931
  * by `DVMServer` and defaulting to issuable for a self-hosted DVM that watches
2166
1932
  * its own closes. It used to gate advertisement only, so a `/v1/credit` 402
2167
1933
  * kept issuing `tempo/session` challenges the DVM had just stopped advertising
2168
- * — and its own interface doc said "advertised **and** issued" (internal-review). The
1934
+ * and its own interface doc said "advertised **and** issued". The
2169
1935
  * caller resolves it as "the gate is open, **or** this credit is already bound
2170
1936
  * to a channel", so a top-up on an existing channel keeps its challenge while
2171
1937
  * the gate is closed; `verifyUpfrontPayment` still refuses any *other* channel
@@ -2175,11 +1941,9 @@ declare function hasPaymentProof(content: Record<string, unknown>): boolean;
2175
1941
  * surfaces always keep charge.
2176
1942
  */
2177
1943
  declare function issueUpfrontChallenges(mpp: MppxServer, requiredMsats: number, resourcePath: string, btcUsdRate: number, sessionIssuable?: boolean, allowOneShotStablecoin?: unknown, priceFiat?: PriceFiat): Promise<Challenge.Challenge[]>;
2178
- /** Verify an upfront payment on job submission (Cashu, x402, or MPP). */
2179
1944
  declare function verifyUpfrontPayment(opts: UpfrontPaymentOpts): Promise<PaymentResult>;
2180
1945
  /**
2181
1946
  * A management credential signed for a channel other than the one required
2182
- * (internal-review).
2183
1947
  *
2184
1948
  * Typed rather than a bare `Error` because the drain route has to tell it apart
2185
1949
  * from the transport and chain failures its catch ladder reconciles: this one is
@@ -2206,7 +1970,7 @@ declare function verifyTempoSessionManagementCredential(args: {
2206
1970
  *
2207
1971
  * The drain route re-asserts its close intent here rather than a line above
2208
1972
  * the call so that a credential signed for another channel is refused before
2209
- * the reservation is touched (internal-review). Only this function knows where that
1973
+ * the reservation is touched. Only this function knows where that
2210
1974
  * seam is, so putting the hook here makes the ordering structural instead of a
2211
1975
  * comment the next edit can reorder past. A throw propagates to the caller
2212
1976
  * with nothing broadcast.
@@ -2218,7 +1982,7 @@ declare function verifyTempoSessionManagementCredential(args: {
2218
1982
  cumulativeAmount?: string;
2219
1983
  }>;
2220
1984
  /**
2221
- * Every machine code a flat payment-error body can carry (internal-review) — the six
1985
+ * Every machine code a flat payment-error body can carry — the six
2222
1986
  * codes constructed directly in this file plus the agent-wallet cashu rejection
2223
1987
  * codes ({@link AccumulatorRejectionCode}), which surface verbatim through the
2224
1988
  * accumulator branch of {@link verifyAccumulatorReceipt}. `fx_rate_unavailable`
@@ -2228,25 +1992,9 @@ declare function verifyTempoSessionManagementCredential(args: {
2228
1992
  */
2229
1993
  type PaymentErrorCode = "payment_invalid" | "x402_not_offered" | "x402_settlement_ambiguous" | "cashu_unavailable" | "cashu_not_p2pk_locked" | "mint_health_pending" | "cashu_mint_sick" | "payment_insufficient" | "payment_unapplied" | "insufficient_credit" | "credit_expired" | "credit_unbacked" | "credit_not_found" | "tempo_channel_closing" | "settlement_pending" | "draw_conflict" | "funding_commitment_mismatch" | "credit_not_supported" | "one_shot_credit_not_supported" | "credit_below_min" | "below_rail_minimum" | "credit_over_max" | "credit_id_unavailable" | "nothing_to_drain" | "drain_below_dust" | "drain_not_found" | "drain_conflict" | "drain_method_unsupported" | "tempo_channel_exit_required" | "x402_settlement_submission_failed" | X402SettlementReconciliationReason | "lightning_settlement_pending" | "lightning_invoice_expired" | "lightning_unavailable" | AccumulatorRejectionCode;
2230
1994
  /**
2231
- * Build a flat payment-error body carrying the four agent-facing fields every
2232
- * payment error must have (internal-review): the machine `code`, the operator-facing
2233
- * `message`, and the `display`/`hint`/`retryable` copy an agent relays to a
2234
- * human and branches retries on. `display`, `hint`, and `retryable` default from
2235
- * {@link PAYMENT_ERROR_COPY} keyed on `code`; `message` is the per-site operator
2236
- * log line. The prose fields can be overridden for dynamic cases (e.g.
2237
- * `cashu_mint_sick` naming the currently-healthy mints, or a retained deposit
2238
- * distinguishing "not spent" from "not paid"). `extra` carries a code's ad-hoc
2239
- * fields (`mints`, `required_msats`, `received_msats`) and is merged last so
2240
- * those are preserved.
2241
- *
2242
- * `retryable` overrides on the same terms, and for the same reason: a code that
2243
- * covers more than one refusal can be terminal for one of them and clear on its
2244
- * own for another — `drain_conflict` is both (internal-review). Override it only
2245
- * alongside the `hint`, since the two are one answer: a hint that says retry
2246
- * under `retryable: false` is the disagreement this exists to prevent.
2247
- *
2248
- * Upstream of {@link buildPaymentErrorResponse}, which serializes the body onto
2249
- * a `Response` — this shapes it.
1995
+ * Per-code display, hint and retryability may be overridden for a particular
1996
+ * refusal. Keep the hint and retry decision consistent. Extra fields merge
1997
+ * last, so callers can supply code-specific amounts and alternatives.
2250
1998
  */
2251
1999
  declare function paymentErrorBody(code: PaymentErrorCode, opts: {
2252
2000
  message: string;
@@ -2258,14 +2006,14 @@ declare function paymentErrorBody(code: PaymentErrorCode, opts: {
2258
2006
  /**
2259
2007
  * Build a payment-error response carrying a JSON error body plus one
2260
2008
  * `WWW-Authenticate: Payment <serialized challenge>` header per mppx challenge
2261
- * (internal-review, per draft-ryan-httpauth-payment-01). When `challenges` is empty,
2009
+ * (per draft-ryan-httpauth-payment-01). When `challenges` is empty,
2262
2010
  * returns a plain JSON response (Cashu/x402-only DVMs).
2263
2011
  *
2264
2012
  * Status defaults to 402 (Payment Required) but callers can override — e.g.
2265
- * the internal-review sick-mint short-circuit returns 503 because the issue is a
2013
+ * the sick-mint short-circuit returns 503 because the failure is a
2266
2014
  * mint-side outage, not a payment problem.
2267
2015
  *
2268
- * When `x402Requirements` is present (internal-review, x402 dual-serve), merge the
2016
+ * When `x402Requirements` is present (x402 dual-serve), merge the
2269
2017
  * x402-spec envelope `{ x402Version, accepts: [...] }` into the body next to
2270
2018
  * the existing dvmkit error fields so x402 clients can find their retry
2271
2019
  * shape on the same response. The MPP `WWW-Authenticate` header and the
@@ -2275,7 +2023,6 @@ declare function paymentErrorBody(code: PaymentErrorCode, opts: {
2275
2023
  * are single-use and shared caches must not return them to other clients.
2276
2024
  */
2277
2025
  declare function buildPaymentErrorResponse(body: Record<string, unknown>, challenges?: Challenge.Challenge[], x402Requirements?: PaymentRequirements[], status?: number, x402V2Requirements?: PaymentRequirementsV2[], x402V2Resource?: string, x402ExactVersions?: X402ExactVersionSupport, x402ErrorCode?: string): Response;
2278
- /** Options for processing an incoming mid-job payment message. */
2279
2026
  interface IncomingPaymentOpts {
2280
2027
  mints?: string[];
2281
2028
  x402Config?: X402Config;
@@ -2288,7 +2035,7 @@ interface IncomingPaymentOpts {
2288
2035
  mpp?: MppxServer;
2289
2036
  /** Resource URL bound into the rebuilt x402 `PaymentRequirements`. */
2290
2037
  resourcePath?: string;
2291
- /** Cashu receive mode (internal-review). Mirrors {@link UpfrontPaymentOpts.cashuMode}. */
2038
+ /** Cashu receive mode. Mirrors {@link UpfrontPaymentOpts.cashuMode}. */
2292
2039
  cashuMode?: CashuMode;
2293
2040
  /** Per-DVM Postgres pool — required for the `p2pk-accumulator` path. */
2294
2041
  accumulatorDb?: AccumulatorPool;
@@ -2297,28 +2044,28 @@ interface IncomingPaymentOpts {
2297
2044
  dvmId?: string;
2298
2045
  /** Active NUT-11 P2PK lock pubkeys (current + retired-in-grace). */
2299
2046
  lockPubkeys?: LockPubkey[];
2300
- /** Runtime mint-health tracker (internal-review). */
2047
+ /** Runtime mint-health tracker. */
2301
2048
  mintHealthTracker?: MintHealthTracker;
2302
2049
  /**
2303
- * Host fx fetcher for the non-sat rails (internal-review). See
2050
+ * Host fx fetcher for the non-sat rails. See
2304
2051
  * {@link UpfrontPaymentOpts.fxFetcher}.
2305
2052
  */
2306
2053
  fxFetcher?: FxFetcher;
2307
2054
  /**
2308
- * Credit ledger the mid-job top-up funds and grows (internal-review, spec §1:
2055
+ * Credit ledger the mid-job top-up funds and grows (the credit lifecycle contract:
2309
2056
  * "mid-job `requestPayment` becomes a top-up"). Absent ⇒ the payment is
2310
2057
  * accepted with pre-credits semantics, exactly as before.
2311
2058
  */
2312
2059
  creditLedger?: CreditLedgerLike;
2313
2060
  /**
2314
- * Durable processed-payment markers (internal-review). x402/mpp money moves outside
2061
+ * Durable processed-payment markers. x402/mpp money moves outside
2315
2062
  * dvmkit, so the marker + `fund` + `growDraw` in one transaction is what
2316
2063
  * makes the mid-job ledger leg atomic and crash-replay safe — the cashu path
2317
2064
  * gets the same property from the accumulator's own commit transaction.
2318
2065
  */
2319
2066
  processedPayments?: ProcessedPaymentStore;
2320
2067
  /**
2321
- * Reporter-owned transactional outbox enqueue (internal-review). Runs inside the
2068
+ * Reporter-owned transactional outbox enqueue. Runs inside the
2322
2069
  * mid-job rail transaction after the ledger top-up/growth and before commit.
2323
2070
  */
2324
2071
  enqueueCreditDeposit?: CreditDepositEnqueue;
@@ -2328,20 +2075,15 @@ interface IncomingPaymentOpts {
2328
2075
  creditTtlMs?: number;
2329
2076
  }
2330
2077
  /**
2331
- * Pure verification of an incoming mid-job payment (internal-review). Runs the
2332
- * rail-specific verify path and returns the credit shape; does not mutate
2333
- * the job. The caller (`JobManager.processPayment`) applies the result
2334
- * inside Tx C so verify+credit land atomically.
2335
- *
2336
- * `snapshot` carries the read-only job state the verification path needs
2337
- * (`pendingPaymentMsats` for amount checks, the MPP/x402 per-job bindings,
2338
- * `paymentRail` for the same-rail accumulator check).
2078
+ * Job state read by incoming-payment verification; the job itself is not mutated. Rail
2079
+ * acceptance and ledger funding can still occur before the caller applies the returned credit in
2080
+ * Tx C.
2339
2081
  */
2340
2082
  interface VerifyIncomingSnapshot {
2341
2083
  pendingPaymentMsats?: number;
2342
2084
  pendingMppChallengeIds?: string[];
2343
2085
  /**
2344
- * Per-job x402 binding (internal-review). When set, the incoming `x402_payment`'s
2086
+ * Per-job x402 binding. When set, the incoming `x402_payment`'s
2345
2087
  * decoded `authorization.nonce` must match this value or the credential is
2346
2088
  * rejected — closes the pre-settlement cross-job replay window. Pre-fix
2347
2089
  * DVMs leave this `undefined`; the verify-side check no-ops for them.
@@ -2350,9 +2092,9 @@ interface VerifyIncomingSnapshot {
2350
2092
  /** Exact advertised USDC microunits paired with `pendingX402Nonce`. */
2351
2093
  pendingX402AmountUsdcMicro?: string;
2352
2094
  paymentRail?: FundingMethod;
2353
- /** The credit the upfront leg funded and drew, when it had one (internal-review). */
2095
+ /** The credit the upfront leg funded and drew, when it had one. */
2354
2096
  creditId?: string;
2355
- /** The draw this job holds on {@link creditId}; the top-up grows it (internal-review). */
2097
+ /** The draw this job holds on {@link creditId}; the top-up grows it. */
2356
2098
  drawId?: string;
2357
2099
  /**
2358
2100
  * The job's current settlement reference. Used to tell an explicit
@@ -2361,17 +2103,17 @@ interface VerifyIncomingSnapshot {
2361
2103
  * upfront leg established.
2362
2104
  */
2363
2105
  paymentTxHash?: string;
2364
- /** Fiat envelope pinned when the ask was emitted (internal-review). See `JobRecord`. */
2106
+ /** Fiat envelope pinned when the ask was emitted. See `JobRecord`. */
2365
2107
  pendingPaymentFiatMicro?: number;
2366
2108
  pendingPaymentFiatCurrency?: string;
2367
2109
  /**
2368
2110
  * Everything this job has asked for mid-job, summed and never decremented
2369
- * (internal-review) — the ceiling on its draw growth. See `JobRecord`, including
2111
+ * the ceiling on its draw growth. See `JobRecord`, including
2370
2112
  * why it can't be derived from the outstanding figure above.
2371
2113
  */
2372
2114
  askedTopUpMicro?: number;
2373
2115
  askedTopUpCurrency?: string;
2374
- /** Why the total above can't express a ceiling, when it can't (internal-review). */
2116
+ /** Why the total above can't express a ceiling, when it can't. */
2375
2117
  topUpCapUnenforcedReason?: TopUpCapUnenforcedReason;
2376
2118
  /**
2377
2119
  * Caller identity for a top-up that has to mint a *fresh* implicit credit
@@ -2382,7 +2124,6 @@ interface VerifyIncomingSnapshot {
2382
2124
  requesterPubkey?: string;
2383
2125
  requesterId?: string;
2384
2126
  }
2385
- /** Result of `verifyIncomingPayment`. */
2386
2127
  interface VerifiedIncomingPayment {
2387
2128
  paidMsats: number;
2388
2129
  paymentMint?: string;
@@ -2391,21 +2132,21 @@ interface VerifiedIncomingPayment {
2391
2132
  nativeAmount?: number;
2392
2133
  nativeAsset?: "sats" | "usdc" | "usdc.e" | "usd-cents";
2393
2134
  cashuFlow?: "p2pk_accumulator";
2394
- /** True when this credit should accumulate onto an existing same-rail native amount (internal-review). */
2135
+ /** True when this credit should accumulate onto an existing same-rail native amount. */
2395
2136
  sameRail: boolean;
2396
2137
  /**
2397
- * Credit this top-up opened (internal-review). Only set when the job had none — a
2138
+ * Credit this top-up opened. Only set when the job had none — a
2398
2139
  * free-then-paid job — so Tx C can bind it; a job that already holds a credit
2399
2140
  * keeps its binding, because the top-up grew that draw.
2400
2141
  */
2401
2142
  creditId?: string;
2402
2143
  /** The draw opened alongside {@link creditId}. */
2403
2144
  drawId?: string;
2404
- /** The funding leg, reported to the platform as a deposit (internal-review). */
2145
+ /** The funding leg, reported to the platform as a deposit. */
2405
2146
  funding?: CreditFundingReport;
2406
2147
  /**
2407
2148
  * Fiat micro this payment funded that the job's draw did not take
2408
- * (internal-review). See `PaymentInfo.unappliedMicro`. When it equals the whole
2149
+ * See `PaymentInfo.unappliedMicro`. When it equals the whole
2409
2150
  * funding, the payment moved no job counters at all and Tx C is handed a
2410
2151
  * `null` delta.
2411
2152
  */
@@ -2413,7 +2154,7 @@ interface VerifiedIncomingPayment {
2413
2154
  }
2414
2155
  /**
2415
2156
  * Verify an incoming payment without mutating the job. Returns the credit
2416
- * shape on success or an error envelope on failure. internal-review.
2157
+ * shape on success or an error envelope on failure.
2417
2158
  *
2418
2159
  * The rails dispatched on below are the list {@link PAYMENT_PROOF_KEYS}
2419
2160
  * carries — add one here and it must be added there in the same commit.
@@ -2428,27 +2169,19 @@ declare function verifyIncomingPayment(body: {
2428
2169
  status: number;
2429
2170
  /**
2430
2171
  * A short mid-job pay that funded a credit before this request failed
2431
- * (internal-review) — still a deposit, still reported. See `PaymentErrorDetail`.
2172
+ * still a deposit, still reported. See `PaymentErrorDetail`.
2432
2173
  */
2433
2174
  funding?: CreditFundingReport;
2434
2175
  };
2435
2176
  }>;
2436
2177
  /**
2437
2178
  * Apply a verified payment result to the in-memory job. Used by callers that
2438
- * don't run Tx C (e.g., the legacy isolate-job-manager path). internal-review.
2179
+ * don't run Tx C (e.g., the legacy isolate-job-manager path).
2439
2180
  */
2440
2181
  declare function applyPaymentInfoToJob(job: ServerJob, info: VerifiedIncomingPayment): void;
2441
2182
  /**
2442
- * Process an incoming payment message. Returns an error body on failure.
2443
- *
2444
- * **The isolate-runtime path, and deliberately ledger-less** (spec §3). Its
2445
- * only caller is `platform/isolate-job-manager.ts`; isolate DVMs have no
2446
- * Postgres of their own, so they run implicit N=1 with today's semantics
2447
- * preserved and never advertise credit. The credits-carrying mid-job path is
2448
- * {@link verifyIncomingPayment} + `JobManager.processPayment` (Tx C), where
2449
- * internal-review wired `fund` + `growDraw`. Wiring a ledger here would need a
2450
- * platform-side ledger leg with its own funding-atomicity and receipt-key
2451
- * story — the trust-surface expansion internal-review exists to decide.
2183
+ * Legacy ledgerless payment path. Mutates the job on acceptance; durable credit hosts use
2184
+ * verifyIncomingPayment and apply its result in Tx C.
2452
2185
  */
2453
2186
  declare function processIncomingPayment(job: ServerJob, body: {
2454
2187
  type: MessageType;
@@ -2463,22 +2196,22 @@ interface CommittedFunding {
2463
2196
  /** Snapshot fixed immediately after this request's funding leg, before any draw. */
2464
2197
  fundingCredit?: CreditSnapshot;
2465
2198
  /**
2466
- * Absent for a `/v1/credit` fund-only top-up (internal-review) — money landed on the
2199
+ * Absent for a `/v1/credit` fund-only top-up — money landed on the
2467
2200
  * credit with no job to pay for — for a short mid-job pay, which funds
2468
- * without drawing or growing (internal-review), and for an explicit short deposit
2469
- * whose combined balance cannot cover the draw (internal-review). On a mid-job
2201
+ * without drawing or growing, and for an explicit short deposit
2202
+ * whose combined balance cannot cover the draw. On a mid-job
2470
2203
  * top-up this is the **grown** draw, so `amountMicro` is the job's running
2471
2204
  * total, not this leg's contribution.
2472
2205
  */
2473
2206
  draw?: DrawResult;
2474
2207
  /**
2475
2208
  * An explicit short deposit committed, but the combined available balance
2476
- * could not cover the full-price draw (internal-review). Preserved instead of
2209
+ * could not cover the full-price draw. Preserved instead of
2477
2210
  * thrown so the rail receive and deposit commit atomically with no draw.
2478
2211
  */
2479
2212
  drawRefusal?: CreditLedgerError;
2480
2213
  /**
2481
- * What this leg added to an already-placed draw (internal-review). Set only on a
2214
+ * What this leg added to an already-placed draw. Set only on a
2482
2215
  * mid-job top-up; the upfront legs contribute the whole draw, so they leave
2483
2216
  * it unset and `draw.railValue` is the same thing.
2484
2217
  */
@@ -2488,7 +2221,7 @@ interface CommittedFunding {
2488
2221
  drawNative: number | null;
2489
2222
  };
2490
2223
  /**
2491
- * Fiat micro that landed on the credit but grew no draw (internal-review) — a
2224
+ * Fiat micro that landed on the credit but grew no draw — a
2492
2225
  * duplicate payment for an ask already satisfied, the clipped tail of an
2493
2226
  * oversized one, or a payment that arrived after the ask was met. It stays
2494
2227
  * the caller's available balance; `UnappliedCredit` on the 201 is how they
@@ -2503,9 +2236,9 @@ interface CommittedFunding {
2503
2236
  *
2504
2237
  * Exported because the builder-supplied `onCreditFunded` override is a second
2505
2238
  * door onto the same report, and both must say the same thing: hand-rolling the
2506
- * mapping there once dropped the internal-review `tempoChannel` block, so a DVM
2239
+ * mapping there once dropped the `tempoChannel` block, so a DVM
2507
2240
  * reporting through the override registered no channel with the close observer
2508
- * (internal-review). One builder, no drift.
2241
+ * One builder, no drift.
2509
2242
  */
2510
2243
  declare function creditDepositPayload(dvmId: string, funding: CreditFundingReport): CreditDepositPayload;
2511
2244
  /** Finish the local ledger effect retained by a durable exact-settlement intent. */
@@ -2518,7 +2251,6 @@ declare function repairX402ExactSettlementEffect(args: {
2518
2251
  tx?: CreditLedgerQuerier;
2519
2252
  }): Promise<CommittedFunding | undefined>;
2520
2253
 
2521
- /** Everything the issuer needs to sign for one DVM. */
2522
2254
  interface ReceiptIssuerOpts {
2523
2255
  /** 32-byte hex receipt secret — `DVMKIT_RECEIPT_KEY`, or an ephemeral dev key. */
2524
2256
  secret: string;
@@ -2528,14 +2260,8 @@ interface ReceiptIssuerOpts {
2528
2260
  dvmId: string;
2529
2261
  }
2530
2262
  /**
2531
- * Signs job receipts for one DVM (internal-review).
2532
- *
2533
- * Constructed only when a receipt key is wired, so the presence of an issuer
2534
- * *is* the "this DVM emits receipts" signal that `/v1/info` advertises. Pure:
2535
- * it holds the key and the slug and turns a terminal `JobRecord` plus an
2536
- * allocated sequence number into signed bytes — sequence allocation and
2537
- * persistence belong to the store, which is what makes issuance idempotent
2538
- * across machines.
2263
+ * An issuer indicates receipt support. Sequence allocation and write-once
2264
+ * persistence belong to the store; signing alone does not provide idempotency.
2539
2265
  */
2540
2266
  declare class ReceiptIssuer {
2541
2267
  /** x-only pubkey this issuer signs under; the attested `receipt_pubkey`. */
@@ -2545,20 +2271,14 @@ declare class ReceiptIssuer {
2545
2271
  private readonly dvmId;
2546
2272
  constructor(opts: ReceiptIssuerOpts);
2547
2273
  /**
2548
- * Build and sign the receipt for a terminal job. `seq` comes from
2549
- * `JobStore.claimReceiptSeq`, `issuedAt` defaults to now (tests pin it).
2550
- *
2551
- * Free jobs get receipts too, with `paid.msats: 0` — they still consume a
2552
- * sequence number and still attest an outcome. Whether feedback is
2553
- * paid-only is the feedback layer's call, not this one's.
2554
- *
2555
- * `credit` is the resolved draw block (internal-review) — present on every job
2556
- * whose payment funded/drew the ledger, absent otherwise. Signature-
2557
- * compatible either way: `canonicalize` omits absent keys.
2274
+ * seq is store-allocated; issuedAt is epoch seconds. Free jobs also consume a
2275
+ * sequence and attest their outcome. Timing/attribution comes from the committed
2276
+ * terminal row; legacy rows omit unknown historical fields. credit is present
2277
+ * only for a resolved ledger draw.
2558
2278
  */
2559
2279
  issue(record: JobRecord, seq: number, issuedAt?: number, credit?: ReceiptCredit): JobReceipt;
2560
2280
  /**
2561
- * Countersign one reclaim event (internal-review). Unlike job receipts there is
2281
+ * Countersign one reclaim event. Unlike job receipts there is
2562
2282
  * no store-allocated sequence — the drain's own `ledger_seq` (taken under
2563
2283
  * the credit row lock, shared with draws) already orders it in the
2564
2284
  * per-credit evidence chain.
@@ -2650,38 +2370,31 @@ declare class X402FacilitatorHealth implements X402FacilitatorHealthLike {
2650
2370
  private refreshOnce;
2651
2371
  }
2652
2372
 
2653
- /** Options for creating a JobManager. */
2654
2373
  interface JobManagerOpts {
2655
- /** Environment variables for ctx.env. */
2656
2374
  env: Record<string, string>;
2657
- /** KV store for ctx.store. */
2658
2375
  store: KVStore;
2659
- /** Cashu mints accepted for payment. */
2660
2376
  mints?: string[];
2661
- /** Persistent backing store for job records. Default: in-memory. */
2377
+ /** Durable in production; defaults to memory only in development/test mode. */
2662
2378
  jobStore?: JobStore;
2663
- /** When true, payment messages auto-credit without verification. */
2379
+ /** Skips payment only when no payment rail is configured. Wired rails still verify. */
2664
2380
  devMode?: boolean;
2665
2381
  /** Payment methods this DVM accepts. Default: derived by the server from configured rails. */
2666
2382
  paymentMethods?: PaymentMethod[];
2667
- /** x402 stablecoin payment configuration. */
2668
2383
  x402?: X402Config;
2669
2384
  /** Durable exact-settlement coordinator shared with upfront verification. */
2670
2385
  x402ExactSettlement?: X402ExactSettlementServer;
2671
2386
  /** Live facilitator capabilities for version-specific mid-job payment handling. */
2672
2387
  x402FacilitatorHealth?: X402FacilitatorHealthLike;
2673
- /** MPP multi-rail payment handle (built via `createMppFromOpts`). */
2674
2388
  mpp?: MppxServer;
2675
- /** Cashu receive mode (internal-review). */
2676
2389
  cashuMode?: CashuMode;
2677
- /** Builder's NUT-11 P2PK lock pubkey (internal-review). Required for accumulator mode. */
2390
+ /** Builder's NUT-11 P2PK lock pubkey. Required for accumulator mode. */
2678
2391
  lockPubkey?: string;
2679
2392
  /** Test-only atomic Cashu backend, supplied by the development host. */
2680
2393
  disposableCashu?: DisposableCashuCommit;
2681
2394
  /** Postgres pool for wallet accumulator persistence. */
2682
2395
  db?: Pool;
2683
2396
  /**
2684
- * Agent-wallet (internal-review): per-DVM identifier used for the scheduler advisory
2397
+ * Agent-wallet: per-DVM identifier used for the scheduler advisory
2685
2398
  * lock and the `(dvm_id, request_id)` replay key. Defaults to `DVMKIT_DVM_ID`
2686
2399
  * env at boot in `createDVMHost()`.
2687
2400
  */
@@ -2690,18 +2403,18 @@ interface JobManagerOpts {
2690
2403
  authAudience?: SignedRequestAudience;
2691
2404
  /**
2692
2405
  * Shared fx fetcher used to convert USD-string descriptor prices and
2693
- * fiat-form `requestPayment` calls into sats (internal-review). The SDK server owns
2406
+ * fiat-form `requestPayment` calls into sats. The SDK server owns
2694
2407
  * one per process so concurrent quotes share the 60s cache window.
2695
2408
  */
2696
2409
  fxFetcher: FxFetcher;
2697
2410
  /**
2698
- * Signs a receipt at every terminal transition (internal-review). Absent when the
2411
+ * Signs a receipt at every terminal transition. Absent when the
2699
2412
  * DVM has no receipt key wired — jobs then terminate exactly as before and
2700
2413
  * `/v1/info` doesn't advertise `receipts`.
2701
2414
  */
2702
2415
  receiptIssuer?: ReceiptIssuer;
2703
2416
  /**
2704
- * Credit ledger for the terminal funnel (internal-review): a job carrying a
2417
+ * Credit ledger for the terminal funnel: a job carrying a
2705
2418
  * `creditId`/`drawId` settles its draw on success and releases it on
2706
2419
  * failure/cancel — this is how "no debit on job failure" is mechanically
2707
2420
  * real. The resolution runs before receipt issuance so the receipt's
@@ -2709,31 +2422,31 @@ interface JobManagerOpts {
2709
2422
  */
2710
2423
  creditLedger?: CreditLedgerLike;
2711
2424
  /**
2712
- * Durable processed-payment markers (internal-review), threaded through to the
2713
- * mid-job top-up path (internal-review) so an x402/mpp payment's marker, `fund`, and
2425
+ * Durable processed-payment markers, threaded through to the
2426
+ * mid-job top-up path so an x402/mpp payment's marker, `fund`, and
2714
2427
  * `growDraw` commit together.
2715
2428
  */
2716
2429
  processedPayments?: ProcessedPaymentStore;
2717
2430
  /**
2718
2431
  * Reporter-owned transactional deposit enqueue threaded into mid-job rail
2719
- * commits (internal-review).
2432
+ * commits.
2720
2433
  */
2721
2434
  enqueueCreditDeposit?: CreditDepositEnqueue;
2722
- /** Reporter outbox inserted atomically with a terminal draw release (internal-review). */
2435
+ /** Reporter outbox inserted atomically with a terminal draw release. */
2723
2436
  enqueueCreditDrawRelease?: CreditDrawReleaseEnqueue;
2724
2437
  /** Credit lifetime a top-up mints with, from the DVM's advertised `credit.ttl`. */
2725
2438
  creditTtlMs?: number;
2726
2439
  /**
2727
- * The DVM's declared pricing currency (internal-review), which is the currency of
2440
+ * The DVM's declared pricing currency, which is the currency of
2728
2441
  * every credit it opens. Threaded into the job context so a mid-job ask on a
2729
2442
  * job that holds no credit yet is still pinned in the denomination its first
2730
- * payment will open the credit in (internal-review). `DVMServer` passes its resolved
2443
+ * payment will open the credit in. `DVMServer` passes its resolved
2731
2444
  * `pricingCurrency`; a host building a `JobManager` directly falls back to the
2732
2445
  * descriptor, and then to `"usd"`.
2733
2446
  */
2734
2447
  pricingCurrency?: string;
2735
2448
  /**
2736
- * Callback fired when a mid-job top-up moves money onto a credit (internal-review).
2449
+ * Callback fired when a mid-job top-up moves money onto a credit.
2737
2450
  * The platform books it as a **deposit** — a liability until drawn, never
2738
2451
  * summed into revenue — so a top-up that skipped this would leave the
2739
2452
  * outstanding-liability view short. Mirrors `DVMServer.reportCreditDeposit`,
@@ -2741,7 +2454,7 @@ interface JobManagerOpts {
2741
2454
  */
2742
2455
  onCreditFunded?: (info: CreditDepositPayload) => void | Promise<void>;
2743
2456
  /**
2744
- * Callback fired when a paid job completes (internal-review). Container-runtime DVMs
2457
+ * Callback fired when a paid job completes. Container-runtime DVMs
2745
2458
  * use this to report revenue to the platform via `RevenueReporter`. Mirrors
2746
2459
  * the isolate path's `IsolateJobManager.opts.onJobCompleted`.
2747
2460
  */
@@ -2773,16 +2486,18 @@ interface JobManagerOpts {
2773
2486
  }) => void | Promise<void>;
2774
2487
  /**
2775
2488
  * Callback fired when a terminal job declared a builder cost but emitted no
2776
- * revenue report (internal-review). Container-runtime DVMs use the reporter's
2489
+ * revenue report. Container-runtime DVMs use the reporter's
2777
2490
  * durable `job-cost` outbox path. The receiver de-duplicates by DVM + job,
2778
2491
  * independently of the rail-keyed revenue ledger.
2779
2492
  */
2780
2493
  onJobCost?: (info: JobCostReportPayload) => void | Promise<void>;
2494
+ /** SDK-owned hosted terminal outbox callback. */
2495
+ onJobTerminal?: (info: JobTerminalReportPayload) => Promise<void>;
2781
2496
  /**
2782
2497
  * Callback fired when the stale-job reaper force-fails a *paid* job — its
2783
2498
  * pending credit draw is released rather than settled, because only a
2784
- * completed job settles a draw (internal-review). Container-runtime DVMs wire this
2785
- * to a paid-job-death report → operator `notice` (internal-review). Never fires for
2499
+ * completed job settles a draw. Container-runtime DVMs wire this
2500
+ * to a paid-job-death report → operator `notice`. Never fires for
2786
2501
  * free jobs (`paidMsats === 0`). Fired fire-and-forget so a throw or a slow
2787
2502
  * report can't wedge the sweep.
2788
2503
  */
@@ -2799,23 +2514,17 @@ interface JobManagerOpts {
2799
2514
  }) => void | Promise<void>;
2800
2515
  /**
2801
2516
  * Callback fired when a settled credit draw cannot book a revenue event
2802
- * because no usable payment rail is available (internal-review). Container-runtime
2517
+ * because no usable payment rail is available. Container-runtime
2803
2518
  * DVMs auto-wire this to the platform's deduped operator notice. Fired
2804
2519
  * fire-and-forget so alert delivery cannot block terminal handling or draw
2805
2520
  * reconciliation.
2806
2521
  */
2807
2522
  onRevenueSkippedNoRail?: (info: RevenueSkippedNoRailPayload) => void | Promise<void>;
2808
2523
  /**
2809
- * Stale-job sweep threshold in ms (internal-review). Jobs in a non-terminal status
2810
- * (`processing` / `working` / `awaiting-input`) whose `lastActivityAt` is
2811
- * older than this are force-cancelled with reason `stale_no_terminal_status`,
2812
- * so callers always reach a terminal status instead of polling forever after
2813
- * a worker crash, OOM, or silent `messageAppender` failure. Default:
2814
- * `2 × descriptor.idleTimeout + STALE_JOB_WATCHDOG_HEADROOM_MS`, matching
2815
- * the internal-review acceptance criterion against scrape's `JOB_TIMEOUT_MS`.
2816
- * Builders whose per-handler wall-clock bound exceeds 90s must override
2817
- * this — otherwise the watchdog fires on a legitimate in-flight handler.
2818
- * Set to `0` to disable (test seam).
2524
+ * Awaiting-input age in milliseconds before cancellation. Defaults to
2525
+ * twice descriptor.idleTimeout (seconds), converted to milliseconds, plus
2526
+ * STALE_JOB_WATCHDOG_HEADROOM_MS. Set to 0 to disable this arm; processing
2527
+ * jobs use processingWatchdogMs.
2819
2528
  */
2820
2529
  staleJobTimeoutMs?: number;
2821
2530
  /**
@@ -2824,7 +2533,7 @@ interface JobManagerOpts {
2824
2533
  */
2825
2534
  staleJobSweepIntervalMs?: number;
2826
2535
  /**
2827
- * Worker-liveness watchdog in ms (internal-review). Jobs in `processing`/`working`
2536
+ * Worker-liveness watchdog in ms. Jobs in `processing`/`working`
2828
2537
  * whose `lastActivityAt` is older than this are marked `failed` (dead
2829
2538
  * worker), separately from the longer `awaiting-input` idle timeout above.
2830
2539
  * Default: `descriptor.processingWatchdog * 1000` or
@@ -2834,15 +2543,15 @@ interface JobManagerOpts {
2834
2543
  /**
2835
2544
  * Interval at which locally-active `processing`/`working` jobs have their
2836
2545
  * `lastActivityAt` bumped so the processing watchdog only fires on a truly
2837
- * dead worker (internal-review). Default: `DEFAULT_HEARTBEAT_INTERVAL_MS`. Set to
2838
- * `0` to disable (test seam). Also floors how far the internal-review clock guard
2546
+ * dead worker. Default: `DEFAULT_HEARTBEAT_INTERVAL_MS`. Set to
2547
+ * `0` to disable (test seam). Also floors how far the clock guard
2839
2548
  * may compensate a backwards step on the processing arm — a cadence close to
2840
2549
  * `processingWatchdogMs` leaves it no room and it compensates nothing.
2841
2550
  */
2842
2551
  heartbeatIntervalMs?: number;
2843
2552
  /**
2844
2553
  * Age in ms at which a `pending` credit draw whose `job_id` matches no row in
2845
- * the job store is released as orphaned (internal-review). The draw commits before
2554
+ * the job store is released as orphaned. The draw commits before
2846
2555
  * the job row is persisted, so a crash or a fail-closed refusal in that
2847
2556
  * window strands a hold no terminal path can ever reach. Must stay
2848
2557
  * comfortably above the submit path's draw→persist latency. Set to `0` to
@@ -2853,7 +2562,7 @@ interface JobManagerOpts {
2853
2562
  orphanDrawAgeMs?: number;
2854
2563
  /**
2855
2564
  * Age in ms at which a `pending` credit draw whose job row **is** terminal is
2856
- * reconciled against that row's outcome (internal-review) — settled for a
2565
+ * reconciled against that row's outcome — settled for a
2857
2566
  * `completed` job, released for a `failed`/`cancelled` one. Covers the hole
2858
2567
  * `orphanDrawAgeMs` cannot: `resolveCreditDraw` throwing on a ledger error
2859
2568
  * leaves a terminal job whose hold no later path retries.
@@ -2861,7 +2570,7 @@ interface JobManagerOpts {
2861
2570
  * Rides the same scan as the orphan arm, which has two consequences for the
2862
2571
  * value you set here. Setting `orphanDrawAgeMs` to `0` disables this arm too.
2863
2572
  * And the effective gate is `max(orphanDrawAgeMs, terminalDrawReconcileAgeMs)`
2864
- * — the scan never returns a row younger than its own cutoff, so anything
2573
+ * the scan never returns a row younger than its own cutoff, so anything
2865
2574
  * below `orphanDrawAgeMs` is silently clamped up to it. That floor is
2866
2575
  * deliberate rather than a wart: honouring a narrower value would mean
2867
2576
  * widening the scan, which hands the orphan arm rows younger than
@@ -2900,7 +2609,7 @@ interface JobManagerOpts {
2900
2609
  /**
2901
2610
  * Grace window in ms a reactivating machine waits for a live original handler
2902
2611
  * to win the single-execution claim before replaying an `awaiting-input` job
2903
- * itself (internal-review). Default: `DEFAULT_REACTIVATION_CLAIM_GRACE_MS`. Widen if
2612
+ * itself. Default: `DEFAULT_REACTIVATION_CLAIM_GRACE_MS`. Widen if
2904
2613
  * cross-machine NOTIFY latency causes spurious double executions; lower in
2905
2614
  * tests for speed.
2906
2615
  */
@@ -2908,7 +2617,7 @@ interface JobManagerOpts {
2908
2617
  }
2909
2618
  type ResolvedInput<I extends ZodLike | undefined> = I extends ZodLike<infer O> ? O : string;
2910
2619
  /**
2911
- * A deposit this payment funded that the job row did **not** take (internal-review).
2620
+ * A deposit this payment funded that the job row did **not** take.
2912
2621
  *
2913
2622
  * Reached when two mid-job top-ups race on a free-then-paid job: each opens its
2914
2623
  * own implicit credit, and `verifyAndCredit`'s set-if-null bind keeps one. The
@@ -2958,12 +2667,13 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
2958
2667
  private readonly cleanupTimers;
2959
2668
  /** Cost revisions held only by a terminal handler owner until its durable merge succeeds. */
2960
2669
  private readonly terminalCostHandoffRetryTimers;
2961
- /** Per-job NOTIFY unsubscribe handles for the durable-status watch (internal-review). */
2670
+ /** Per-job NOTIFY unsubscribe handles for the durable-status watch. */
2962
2671
  private readonly statusWatchers;
2963
- /** Job ids whose durable-status re-read is in flight — coalesces notify storms (internal-review). */
2672
+ /** Job ids whose durable-status re-read is in flight — coalesces notify storms. */
2964
2673
  private readonly statusChecksInFlight;
2965
- /** Job ids that were notified mid-re-read and must be re-checked (internal-review). */
2674
+ /** Job ids that were notified mid-re-read and must be re-checked. */
2966
2675
  private readonly statusChecksQueued;
2676
+ private readonly inputResumeTails;
2967
2677
  private readonly sessionId;
2968
2678
  private nextJobId;
2969
2679
  private accumulatorMonitor?;
@@ -2971,7 +2681,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
2971
2681
  private readonly processingWatchdogMs;
2972
2682
  private readonly reactivationClaimGraceMs;
2973
2683
  /**
2974
- * Worker-heartbeat cadence (internal-review). Also floors the internal-review clock
2684
+ * Worker-heartbeat cadence. Also floors the clock
2975
2685
  * guard's compensation on the processing arm — see
2976
2686
  * {@link staleSweepCompensationCapMs}.
2977
2687
  */
@@ -2991,7 +2701,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
2991
2701
  private jobRetentionResumeFrom?;
2992
2702
  /**
2993
2703
  * A `Date.now()` reading that cannot regress within this manager's lifetime
2994
- * (internal-review). Anchored at construction, so a manager already running when
2704
+ * Anchored at construction, so a manager already running when
2995
2705
  * the clock stepped is covered. The stale-job sweep's two cutoffs, the
2996
2706
  * orphan-draw age gate and the reactivation claim grace all read it; see
2997
2707
  * {@link createMonotonicClock} for what it costs and where it is clamped.
@@ -3004,29 +2714,25 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3004
2714
  private jobRetentionSweepInFlight;
3005
2715
  private heartbeatInFlight;
3006
2716
  /**
3007
- * The denomination of every credit this DVM opens (internal-review). Resolved once,
2717
+ * The denomination of every credit this DVM opens. Resolved once,
3008
2718
  * with the same boundary fallback `DVMServer` applies for direct JavaScript
3009
2719
  * callers that force an incomplete value past the descriptor contract.
3010
2720
  */
3011
2721
  private readonly pricingCurrency;
3012
2722
  constructor(descriptor: DVMDescriptor<State, InputSchema>, opts: JobManagerOpts);
3013
- /** Active in-memory jobs. */
3014
2723
  get activeJobs(): Map<string, ServerJob>;
3015
- /** The backing job store. */
3016
2724
  get store(): JobStore;
3017
2725
  /**
3018
- * True when this DVM will actually issue receipts (internal-review) — a key *and*
2726
+ * True when this DVM will actually issue receipts — a key *and*
3019
2727
  * a store that can allocate sequence numbers and persist bytes write-once.
3020
2728
  * `/v1/info#receipts` reads this rather than the key alone, so the flag can
3021
2729
  * never promise something `issueReceipt` silently declines to do.
3022
2730
  */
3023
2731
  get receiptsEnabled(): boolean;
3024
- /** True when the named capability exists on this descriptor. */
3025
2732
  hasCapability(name: string): boolean;
3026
- /** Names of every capability this descriptor exposes — used for error envelopes. */
3027
2733
  capabilityNames(): string[];
3028
2734
  /**
3029
- * Resolve a capability's static `price` to msats (internal-review).
2735
+ * Resolve a capability's static `price` to msats.
3030
2736
  *
3031
2737
  * `"$X.XX"` is parsed as USD and converted via the SDK's shared fx fetcher.
3032
2738
  * Returns `undefined` for dynamic-priced capabilities (those that declare
@@ -3035,7 +2741,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3035
2741
  currentPriceMsats(capability: string): Promise<number | undefined>;
3036
2742
  /**
3037
2743
  * Translate a capability's static `price` into the fiat envelope used by
3038
- * the per-capability `pricing.max` advertised on `/v1/info` (internal-review).
2744
+ * the per-capability `pricing.max` advertised on `/v1/info`.
3039
2745
  * Returns `undefined` for dynamic-priced capabilities and for unknown
3040
2746
  * names (the route handler validates the name before invoking this).
3041
2747
  */
@@ -3044,24 +2750,24 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3044
2750
  currency: string;
3045
2751
  } | undefined;
3046
2752
  /**
3047
- * Parse a request body through a capability's input schema (internal-review).
2753
+ * Parse a request body through a capability's input schema.
3048
2754
  *
3049
2755
  * Resolution order when a capability declares an `input` schema:
3050
2756
  * 1. `body.data` is preferred — agents pass structured fields directly
3051
- * (CLI builds this from `--param k=v`).
2757
+ * (CLI builds this from `--param k=v`).
3052
2758
  * 2. Fallback: `JSON.parse(body.input)` for clients still on the legacy
3053
- * JSON-string contract.
2759
+ * JSON-string contract.
3054
2760
  * 3. Neither usable → throw `MissingStructuredInputError` so the route can
3055
- * return `invalid_input` with a hint pointing at `/v1/info`.
2761
+ * return `invalid_input` with a hint pointing at `/v1/info`.
3056
2762
  *
3057
2763
  * When the capability has no `input` schema, returns `body.input` raw
3058
2764
  * (primitive path).
3059
2765
  *
3060
- * The internal-review credit envelope is removed before the schema runs, on both
3061
- * branches (internal-review) — it rides the signed body but is protocol-level, so a
2766
+ * The credit envelope is removed before the schema runs, on both
2767
+ * branches — it rides the signed body but is protocol-level, so a
3062
2768
  * top-level `.strict()` capability schema would otherwise reject every
3063
2769
  * explicit draw as an unknown key, before the envelope was even extracted.
3064
- * The persisted `record.input` the internal-review reactivation path re-reads is the
2770
+ * The persisted `record.input` the reactivation path re-reads is the
3065
2771
  * pre-Zod wire form, so it comes through the second branch carrying them too.
3066
2772
  */
3067
2773
  parseInput(body: {
@@ -3069,7 +2775,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3069
2775
  data?: unknown;
3070
2776
  }, capability: string): unknown;
3071
2777
  /**
3072
- * Look up the per-capability descriptor by name (internal-review). Throws when the
2778
+ * Look up the per-capability descriptor by name. Throws when the
3073
2779
  * name doesn't exist — the route layer validates body.capability first, so
3074
2780
  * reaching this with an unknown name is a programmer error.
3075
2781
  */
@@ -3083,7 +2789,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3083
2789
  */
3084
2790
  private getCapability;
3085
2791
  /**
3086
- * Allocate the id the next job will be created under (internal-review). The
2792
+ * Allocate the id the next job will be created under. The
3087
2793
  * submit path calls this BEFORE payment verification so the ledger draw
3088
2794
  * commits with its job linkage, then passes the id back through
3089
2795
  * `createJob`'s provenance. Ids allocated for requests whose payment is
@@ -3092,7 +2798,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3092
2798
  */
3093
2799
  allocateJobId(): string;
3094
2800
  /**
3095
- * Create a new job from a request body. `provenance` (internal-review) is what the
2801
+ * Create a new job from a request body. `provenance` is what the
3096
2802
  * idempotent-replay path on `POST /v1/job` reads back: the raw `job_token`
3097
2803
  * to re-issue, and the fingerprint + caller pubkey a retry must reproduce
3098
2804
  * to be given it.
@@ -3107,32 +2813,32 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3107
2813
  requestId?: string;
3108
2814
  authRequestPath?: string;
3109
2815
  /**
3110
- * Pre-allocated id from {@link allocateJobId} (internal-review) — the submit
2816
+ * Pre-allocated id from {@link allocateJobId} — the submit
3111
2817
  * path allocates before payment verification so the ledger draw can
3112
2818
  * record the job it pays for.
3113
2819
  */
3114
2820
  jobId?: string;
3115
2821
  }): ServerJob;
3116
- /** Build an SDKJobContext and attach it to the job. */
3117
2822
  buildAndAttachContext(job: ServerJob, parsedInput: unknown): SDKJobContext<State, ResolvedInput<InputSchema>>;
3118
- /** Start the handler for a job. */
3119
2823
  runHandler(job: ServerJob, sdkCtx: SDKJobContext<unknown, unknown>): void;
3120
- /** Dispatch a validated incoming message to the appropriate handler or pending resolver. */
3121
2824
  dispatchMessage(job: ServerJob, type: MessageType, content: unknown): Promise<void>;
2825
+ private queueInputResume;
2826
+ private claimLocalInput;
2827
+ private dispatchMessageNow;
3122
2828
  /**
3123
2829
  * Persist a job to the backing store.
3124
2830
  *
3125
- * Awaits the per-job appender tail (internal-review) so that:
3126
- * 1. The snapshot's `messages` is consistent with the `job_messages` table
3127
- * — no row is still in flight at snapshot time.
3128
- * 2. For terminal saves, the `DELETE FROM job_messages` inside `save()`
3129
- * can't race a still-pending `appendOutgoing` for the final yield
3130
- * message (which would otherwise wipe the row before an in-flight SSE
3131
- * subscriber's NOTIFY-driven fetch can see it).
2831
+ * Awaits the per-job appender tail so that:
2832
+ * 1. The snapshot's `messages` is consistent with the `job_messages` table
2833
+ * no row is still in flight at snapshot time.
2834
+ * 2. For terminal saves, the `DELETE FROM job_messages` inside `save()`
2835
+ * can't race a still-pending `appendOutgoing` for the final yield
2836
+ * message (which would otherwise wipe the row before an in-flight SSE
2837
+ * subscriber's NOTIFY-driven fetch can see it).
3132
2838
  */
3133
2839
  persistJob(job: ServerJob): Promise<void>;
3134
2840
  /**
3135
- * Cross-machine terminal guard (internal-review). `buildContext`'s guard reads the
2841
+ * Cross-machine terminal guard. `buildContext`'s guard reads the
3136
2842
  * in-memory `job.status`, so on its own it only protects the machine running
3137
2843
  * the handler. On a multi-machine DVM a `DELETE /v1/job/:id` routinely lands
3138
2844
  * on a machine that isn't running the job — `app.ts` cancels it in the store
@@ -3144,7 +2850,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3144
2850
  * fires `job.abort` so in-flight `ctx.fetch` calls tear down. Returns true when
3145
2851
  * the durable state won and the caller must skip its save.
3146
2852
  *
3147
- * Two triggers: `watchDurableStatus`'s NOTIFY subscription (internal-review) fires
2853
+ * Two triggers: `watchDurableStatus`'s NOTIFY subscription fires
3148
2854
  * this within a round-trip of the remote cancel committing, which is what
3149
2855
  * actually stops the provider spend; `persistJob` calls it again on every save
3150
2856
  * as the backstop for the window where a cancel commits between a handler's
@@ -3163,7 +2869,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3163
2869
  /** Durably merge a terminal handler owner's latest cost before reporting it. */
3164
2870
  private persistAdoptedTerminalCost;
3165
2871
  /**
3166
- * Watch the durable status of a locally-active job (internal-review).
2872
+ * Watch the durable status of a locally-active job.
3167
2873
  *
3168
2874
  * `ctx.signal` is raised by `dispatchMessage`, which only runs on the machine
3169
2875
  * holding the job in `activeJobs`. On a multi-machine DVM the `DELETE` usually
@@ -3181,30 +2887,30 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3181
2887
  */
3182
2888
  private watchDurableStatus;
3183
2889
  /**
3184
- * Tear down a job's durable-status subscription (internal-review). Must be called
2890
+ * Tear down a job's durable-status subscription. Must be called
3185
2891
  * wherever a job leaves `activeJobs` — a leaked subscriber outlives the job on
3186
2892
  * the store's shared LISTEN connection. Idempotent.
3187
2893
  */
3188
2894
  unwatchDurableStatus(jobId: string): void;
3189
2895
  /**
3190
- * NOTIFY-driven durable-status re-read, coalesced per job (internal-review).
2896
+ * NOTIFY-driven durable-status re-read, coalesced per job.
3191
2897
  *
3192
2898
  * A notify that arrives while a re-read is in flight is queued rather than
3193
2899
  * dropped: the in-flight read may have observed the row a moment *before* the
3194
- * cancel committed, and dropping its notify would put the abort back where
3195
- * this issue found it — waiting for the handler's next store write.
2900
+ * cancel committed, and dropping its notify would leave the local handler
2901
+ * running until its next store write instead of aborting promptly.
3196
2902
  */
3197
2903
  private checkDurableTerminal;
3198
2904
  /**
3199
2905
  * Wire the Tx A appender when the store supports streaming.
3200
2906
  *
3201
- * Tx A (internal-review): outgoing message + `pending_payment_msats` bump land in
2907
+ * Tx A: outgoing message + `pending_payment_msats` bump land in
3202
2908
  * the same transaction. The DB allocates the row's seq via
3203
2909
  * `UPDATE jobs SET next_seq = next_seq + 1 RETURNING` so concurrent inbound
3204
2910
  * traffic on different machines can never collide on the `(job_id, seq)` PK.
3205
2911
  * In-memory `job.seq` keeps its own counter for same-machine SSE listeners.
3206
2912
  *
3207
- * Per-job serialisation (internal-review): chained through `job.messageAppenderTail`
2913
+ * Per-job serialisation: chained through `job.messageAppenderTail`
3208
2914
  * so two synchronous `providerMessage` calls (e.g. `artifact` followed by
3209
2915
  * `complete`) commit their `appendOutgoing` transactions in call order.
3210
2916
  * Without this, the two transactions race for the `jobs` row lock and the
@@ -3215,19 +2921,17 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3215
2921
  * `artifact`'s NOTIFY silently drops the artifact.
3216
2922
  */
3217
2923
  private wireMessageAppender;
3218
- /** Start (or restart) the idle timer for a job. */
3219
2924
  startIdleTimer(job: ServerJob): void;
3220
- /** Clear the idle timer for a job. */
3221
2925
  clearIdleTimer(id: string): void;
3222
- /** Cancel a job due to idle timeout. Exposed for the reactivation path's idle-expiry guard (internal-review). */
2926
+ /** Cancel a job due to idle timeout. Exposed for the reactivation path's idle-expiry guard. */
3223
2927
  cancelJobIdle(job: ServerJob): Promise<void>;
3224
2928
  /**
3225
2929
  * Force-terminate stale non-terminal jobs. Two status-aware arms:
3226
- * • `awaiting-input` past `staleJobTimeoutMs` → `cancelled`
3227
- * (`stale_no_terminal_status`) — the caller never paid / responded.
3228
- * • `processing`/`working` past `processingWatchdogMs` → `failed`
3229
- * (`worker_died_mid_job`) — the worker died mid-job. The heartbeat keeps
3230
- * live workers fresh, so a stale row here means a dead process.
2930
+ * • `awaiting-input` past `staleJobTimeoutMs` → `cancelled`
2931
+ * (`stale_no_terminal_status`) — the caller never paid / responded.
2932
+ * • `processing`/`working` past `processingWatchdogMs` → `failed`
2933
+ * (`worker_died_mid_job`) — the worker died mid-job. The heartbeat keeps
2934
+ * live workers fresh, so a stale row here means a dead process.
3231
2935
  *
3232
2936
  * Fires on a timer (wired in the constructor) and also callable on demand
3233
2937
  * (tests, ops). The CAS in `cancelStaleJob` makes this safe to run
@@ -3236,17 +2940,17 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3236
2940
  * descriptors — the local activeJobs teardown only makes sense on the
3237
2941
  * JobManager that actually hosted the zombie handler.
3238
2942
  *
3239
- * **Both cutoffs float off the wall clock (internal-review).** Each is one
2943
+ * **Both cutoffs float off the wall clock.** Each is one
3240
2944
  * `Date.now()` sample compared against `last_activity_at`, stamped from a
3241
2945
  * different sample at a different moment, so a backwards step between the
3242
2946
  * two makes every row look more recently active than it is: the query
3243
2947
  * matches nothing and both arms go silent for the length of the skew. The
3244
2948
  * dead worker's job keeps its `processing` status, its credit hold stays
3245
- * `pending`, and the internal-review paid-job-death alert — wired to this reaper —
3246
- * never fires. Same defect, same host class, as internal-review's orphan sweep.
2949
+ * `pending`, and the paid-job-death alert — wired to this reaper —
2950
+ * never fires. Same defect, same host class, as orphan sweep.
3247
2951
  *
3248
2952
  * **The compensation is capped** — see {@link staleSweepCompensationCapMs}
3249
- * for where each arm's cap comes from — which internal-review did not need to do. Its eagerness costs at worst an early release
2953
+ * for where each arm's cap comes from — which did not need to do. Its eagerness costs at worst an early release
3250
2954
  * of an unclaimed hold, refused outright for any live job row; ours
3251
2955
  * force-*fails* a running job. `cancelStaleJob`'s CAS does not cover that:
3252
2956
  * `expectedActivityBefore` is the same compensated threshold the query used,
@@ -3278,7 +2982,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3278
2982
  }>;
3279
2983
  /**
3280
2984
  * How far one watchdog arm may lean on {@link monotonicNowMs} past the wall
3281
- * clock to cover a backwards step (internal-review). The compensation is capped,
2985
+ * clock to cover a backwards step. The compensation is capped,
3282
2986
  * not free: a claimed row must still have been silent for
3283
2987
  * `windowMs - cap`, and unlike the orphan sweep, claiming eagerly here
3284
2988
  * force-fails a job that may well be alive.
@@ -3298,7 +3002,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3298
3002
  * clock stepped. That pair is marginal already; the guard must not make it
3299
3003
  * deterministic. The cap collapses to zero there, which is today's
3300
3004
  * behaviour: late, never wrong. The `awaiting-input` arm takes no such term
3301
- * — nothing heartbeats it by design, and its refresh is an inbound caller
3005
+ * nothing heartbeats it by design, and its refresh is an inbound caller
3302
3006
  * message on no cadence at all.
3303
3007
  *
3304
3008
  * **Why a magnitude and not a predicate.** The tempting sharper rule is to
@@ -3335,7 +3039,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3335
3039
  * Resolve `pending` credit draws the terminal funnel can no longer reach.
3336
3040
  * Two arms over one scan, distinguished by whether the draw's job row exists.
3337
3041
  *
3338
- * **No job row (internal-review) — release.** internal-review allocates the job id and
3042
+ * **No job row — release.** allocates the job id and
3339
3043
  * commits the ledger draw, with its `job_id` linkage, before `recordReplay`
3340
3044
  * and `persistJob` write the job row. A crash, a `draw_conflict` 409, or a
3341
3045
  * fail-closed `replay_detected` 401 in that window leaves a committed hold
@@ -3345,12 +3049,12 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3345
3049
  * available balance stays reduced forever. Cosmetic for an implicit N=1
3346
3050
  * credit, a silent balance shrink for an explicit N>1 one.
3347
3051
  *
3348
- * **Terminal job row (internal-review) — reconcile to its outcome.** The funnel
3052
+ * **Terminal job row — reconcile to its outcome.** The funnel
3349
3053
  * demonstrably fails: `resolveCreditDraw` throws on a ledger/DB error,
3350
3054
  * `issueReceipt` catches it, logs `credit_resolve_failed` and returns
3351
3055
  * nothing, and until now nothing retried. Same stranded hold, reached through
3352
3056
  * a different door — and for a `completed` job it strands the platform's
3353
- * books too, since draws *are* the revenue events (internal-review): the caller is
3057
+ * books too, since draws *are* the revenue events: the caller is
3354
3058
  * charged nothing, the balance never moves, and outstanding liability
3355
3059
  * (`deposits − draw revenue`) overstates forever. So a late settle books, off
3356
3060
  * the committed draw row, through the ordinary reporter path.
@@ -3377,8 +3081,8 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3377
3081
  reconciled: number;
3378
3082
  }>;
3379
3083
  /**
3380
- * The end-of-scan line for a tick that resolved nothing (internal-review, extended
3381
- * by internal-review). Two shapes are worth a warning, and neither is the ordinary
3084
+ * The end-of-scan line for a tick that resolved nothing. Two shapes are
3085
+ * worth a warning, and neither is the ordinary
3382
3086
  * one — the timer runs every 5 minutes per ledger in production.
3383
3087
  *
3384
3088
  * `examined > 0`: aged candidates were looked at and none moved. Usually
@@ -3387,7 +3091,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3387
3091
  *
3388
3092
  * `examined === 0`: the query returned nothing. Ordinarily that means there
3389
3093
  * is nothing to do — but it is equally what a blinded scan looks like, which
3390
- * is the case the internal-review gate could not reach. Probe for the oldest
3094
+ * is the case the gate could not reach. Probe for the oldest
3391
3095
  * `pending` hold at any age and speak up when one exists the scan should
3392
3096
  * have seen and didn't: already past the age gate outright, stamped ahead of
3393
3097
  * our clock (some other process's clock is fast), or hidden while our own
@@ -3400,7 +3104,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3400
3104
  */
3401
3105
  private reportSweepOutcome;
3402
3106
  /**
3403
- * Resolve one aged `pending` draw whose job row reached terminal (internal-review),
3107
+ * Resolve one aged `pending` draw whose job row reached terminal,
3404
3108
  * mirroring what `resolveCreditDraw` would have done in line. Returns true
3405
3109
  * when this call is the one that moved the draw — the caller counts it, and
3406
3110
  * only it books.
@@ -3434,7 +3138,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3434
3138
  /**
3435
3139
  * Refresh `lastActivityAt` for jobs this process is actively running
3436
3140
  * (`processing`/`working`) so the processing watchdog only fires once the
3437
- * worker is genuinely dead (internal-review). `awaiting-input` jobs are
3141
+ * worker is genuinely dead. `awaiting-input` jobs are
3438
3142
  * deliberately excluded — their idle timeout must still elapse. Fires on the
3439
3143
  * heartbeat timer; also callable on demand for tests.
3440
3144
  */
@@ -3442,15 +3146,15 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3442
3146
  beat: number;
3443
3147
  }>;
3444
3148
  /**
3445
- * Single-execution claim for a reactivating machine (internal-review). Before
3149
+ * Single-execution claim for a reactivating machine. Before
3446
3150
  * replaying an `awaiting-input` job, wait a bounded grace window for a live
3447
3151
  * original handler to win the `awaiting-input → processing` CAS via its
3448
3152
  * NOTIFY wake. Resolves:
3449
- * • `false` — the row left `awaiting-input` during the window (the original
3450
- * handler claimed it / drove it terminal). Stand down; do not replay.
3451
- * • `true` — the window elapsed still `awaiting-input` (the original worker
3452
- * is gone or has no live resolver) AND this machine won the claim CAS.
3453
- * Replay for dead-worker recovery.
3153
+ * • `false` — the row left `awaiting-input` during the window (the original
3154
+ * handler claimed it / drove it terminal). Stand down; do not replay.
3155
+ * • `true` — the window elapsed still `awaiting-input` (the original worker
3156
+ * is gone or has no live resolver) AND this machine won the claim CAS.
3157
+ * Replay for dead-worker recovery.
3454
3158
  *
3455
3159
  * Biasing the original handler to win kills the double execution (double
3456
3160
  * substrate spend, clobbered artifact) without heartbeating `awaiting-input`
@@ -3459,7 +3163,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3459
3163
  */
3460
3164
  claimAwaitingInputForReactivation(jobId: string): Promise<boolean>;
3461
3165
  /**
3462
- * Record an inbound client message durably via Tx B (internal-review) and append
3166
+ * Record an inbound client message durably via Tx B and append
3463
3167
  * it to the in-memory `job.messages` array. Returns the DB-allocated seq
3464
3168
  * (`null` when the store isn't streamable — non-Postgres test fallback).
3465
3169
  *
@@ -3478,13 +3182,13 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3478
3182
  * in-memory `ServerJob`, wires the appender, builds the context, starts
3479
3183
  * the idle timer. The caller has already recorded the inbound via Tx B
3480
3184
  * and run Tx C (verify + credit); reactivation is now decoupled from
3481
- * credit (internal-review, internal-review). The caller decides whether to run the
3185
+ * credit. The caller decides whether to run the
3482
3186
  * handler (Pattern A: status was `awaiting-input`) or to dispatch the
3483
3187
  * message to a custom handler (Pattern B).
3484
3188
  *
3485
3189
  * For DVMs that declare descriptor-level auth, the persisted
3486
3190
  * `record.input` carries the original signed envelope. We re-verify the
3487
- * signature here (internal-review) so a DB-layer tamper — compromised admin, SQL
3191
+ * signature here so a DB-layer tamper — compromised admin, SQL
3488
3192
  * injection, malicious operator with DB access — can't silently feed an
3489
3193
  * attacker-supplied pubkey/envelope into the handler. The drift window
3490
3194
  * and replay store are deliberately skipped: the persisted timestamp is
@@ -3498,7 +3202,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3498
3202
  * What gets re-verified is the persisted row read back through
3499
3203
  * {@link signedRequestInput} — `app.ts` stores the pre-Zod wire form, so
3500
3204
  * those are the caller's own signed bytes, the same ones `/v1/quote` and the
3501
- * submit checked (internal-review). That makes the tamper check strictly stronger
3205
+ * submit checked. That makes the tamper check strictly stronger
3502
3206
  * than the parsed form it replaced: an injected key the capability schema
3503
3207
  * doesn't declare used to be stripped before the signature was checked, so
3504
3208
  * it re-verified clean.
@@ -3508,7 +3212,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3508
3212
  sdkCtx: SDKJobContext<unknown, unknown>;
3509
3213
  };
3510
3214
  /**
3511
- * Verify an inbound payment and apply the credit via Tx C (internal-review).
3215
+ * Verify an inbound payment and apply the credit via Tx C.
3512
3216
  * The inbound message must have been recorded via Tx B already
3513
3217
  * (`recordInboundPending` returned `inboundSeq`). For non-streamable
3514
3218
  * stores (`inboundSeq === null`), falls back to in-memory mutation via
@@ -3518,12 +3222,12 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3518
3222
  * `pendingPaymentMsats` without external verification. Gated on
3519
3223
  * `devModeSkipsPaymentVerification` — the same predicate the upfront path
3520
3224
  * reads, so a dev server wired to a mint verifies mid-job payments for real
3521
- * (internal-review) — plus the explicit `dev_auto` opt-out the dev console uses,
3225
+ * plus the explicit `dev_auto` opt-out the dev console uses,
3522
3226
  * which is the one caller that has no wallet to pay from. `dev_auto` loses to
3523
3227
  * any proof riding the same message: money on the wire always takes the rail,
3524
3228
  * so the flag can never leave a real token unspent against a credited job.
3525
3229
  *
3526
- * Tx C is not atomic with the rail commit (internal-review). `verifyIncomingPayment`
3230
+ * Tx C is not atomic with the rail commit. `verifyIncomingPayment`
3527
3231
  * commits the proofs *and* the ledger leg in one transaction and returns;
3528
3232
  * only then does `verifyAndCredit` write the job row, on its own connection.
3529
3233
  * Everything after that call therefore runs with the caller's money already
@@ -3539,7 +3243,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3539
3243
  mintHealthTracker?: MintHealthTracker;
3540
3244
  }): Promise<ProcessPaymentOutcome>;
3541
3245
  /**
3542
- * Tx C with a bounded retry (internal-review).
3246
+ * Tx C with a bounded retry.
3543
3247
  *
3544
3248
  * By the time this runs on the verified path, the rail commit and the ledger
3545
3249
  * leg have already committed in a transaction this call is not part of — so a
@@ -3573,7 +3277,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3573
3277
  private creditInbound;
3574
3278
  /**
3575
3279
  * Answer a payment whose rail and ledger legs committed but whose job row
3576
- * could not be written (internal-review) — every retry spent, the money real.
3280
+ * could not be written — every retry spent, the money real.
3577
3281
  *
3578
3282
  * The refusal names the credit the money landed on so the caller can act on
3579
3283
  * it directly, mirroring the short mid-job pay's 402 (`payment.ts`). It is a
@@ -3582,7 +3286,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3582
3286
  * fault an operator should see in their 5xx rate. `retryable` is true because
3583
3287
  * the ask is genuinely still outstanding.
3584
3288
  *
3585
- * Nothing is released here. The hold is left to the internal-review reconciler,
3289
+ * Nothing is released here. The hold is left to the reconciler,
3586
3290
  * which makes the same decision — unbound draw ⇒ release, never settle — but
3587
3291
  * under the credit row's `FOR UPDATE`, after the job is terminal, where it
3588
3292
  * cannot race an in-flight COMMIT. Releasing from here would also be
@@ -3605,23 +3309,23 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3605
3309
  private reportUnappliedPayment;
3606
3310
  /**
3607
3311
  * Describe a deposit the job row never took, in the shape both the 201 and
3608
- * the `payment_unapplied` 500 carry (internal-review). `undefined` when the payment
3312
+ * the `payment_unapplied` 500 carry. `undefined` when the payment
3609
3313
  * ran ledger-less (no credit ledger wired, or the top-up preflight skipped) —
3610
3314
  * there is no credit to name, so the 201 carries no `credit` block and the
3611
3315
  * 500 takes its words from `unappliedCopy` directly.
3612
3316
  */
3613
3317
  private unappliedCredit;
3614
- /** Book a mid-job top-up's funding as a deposit (internal-review, spec §10). */
3318
+ /** Book a mid-job top-up's funding as a deposit (the deposit-accounting rule). */
3615
3319
  private reportCreditDeposit;
3616
3320
  /**
3617
3321
  * Internal: build the BuildContextOpts shared between `createJob` and
3618
3322
  * `reactivateJob`. Centralised so persistence/terminal callbacks and the
3619
- * internal-review NOTIFY-driven cross-machine wake stay in one place.
3323
+ * NOTIFY-driven cross-machine wake stay in one place.
3620
3324
  */
3621
3325
  private buildContextOpts;
3622
3326
  /**
3623
3327
  * Internal: what unit `ctx.requestPayment`'s auto-credit gate may measure this
3624
- * job's remaining pool in (internal-review).
3328
+ * job's remaining pool in.
3625
3329
  *
3626
3330
  * The regime is read off the DRAW, never off the job. Implicit N=1 is exactly
3627
3331
  * `credit_id === imp:<rail>:<draw_id>`, because `fundAndDraw` derives both
@@ -3639,15 +3343,14 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3639
3343
  * drew and whose pool this can't read or denominate answers `unpriced`, which
3640
3344
  * the gate declines to auto-credit from: the msat figure it would otherwise
3641
3345
  * fall back on is a slice of the credit's rail value at a ratio pinned
3642
- * whenever that credit was funded, which is the exact figure this issue exists
3643
- * to stop spending against.
3346
+ * whenever that credit was funded, so it cannot safely fund a new ask.
3644
3347
  */
3645
3348
  private resolveDrawPool;
3646
3349
  /** Internal: structured warn for the one path that can't price a drawn job's pool. */
3647
3350
  private warnDrawPool;
3648
3351
  /**
3649
3352
  * Issue the signed receipt for a job that has reached a terminal status
3650
- * (internal-review). Idempotent and safe to call from every terminal path — the
3353
+ * Idempotent and safe to call from every terminal path — the
3651
3354
  * store owns both the sequence allocation and the write-once persist, so
3652
3355
  * two machines racing on the same job converge on identical bytes and burn
3653
3356
  * exactly one sequence number.
@@ -3663,10 +3366,10 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3663
3366
  /** Build the common settle/release args, adding the hosted release outbox when wired. */
3664
3367
  private drawResolutionArgs;
3665
3368
  /**
3666
- * Settle or release a terminal job's credit draw (internal-review): `completed`
3369
+ * Settle or release a terminal job's credit draw: `completed`
3667
3370
  * settles (the hold becomes a real debit); `failed`/`cancelled` releases
3668
3371
  * (the hold evaporates — "no debit on job failure", mechanically
3669
- * superseding the internal-review/974 no-op refund for credit-paid jobs, including
3372
+ * superseding the 974 no-op refund for credit-paid jobs, including
3670
3373
  * the `ctx.fail` path and the stale-sweeper reap). Idempotent — a replayed
3671
3374
  * resolution returns the recorded state. Returns the `ReceiptCredit` block
3672
3375
  * for the receipt: `balance_after` is the recorded draw trajectory for a
@@ -3674,7 +3377,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3674
3377
  */
3675
3378
  private resolveCreditDraw;
3676
3379
  /**
3677
- * The one place a terminal job's status becomes a ledger verb (internal-review):
3380
+ * The one place a terminal job's status becomes a ledger verb:
3678
3381
  * `completed` settles the hold into a real debit, `failed`/`cancelled`
3679
3382
  * release it. Read by the in-line funnel (`resolveCreditDraw`) and by the
3680
3383
  * late reconciler (`reconcileTerminalDraw`) — two paths that must never
@@ -3688,32 +3391,26 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3688
3391
  */
3689
3392
  private attachReceipt;
3690
3393
  /**
3691
- * Read-path repair for a terminal job carrying no receipt (internal-review), for
3394
+ * Read-path repair for a terminal job carrying no receipt, for
3692
3395
  * either an in-memory job or a store record. Free in the steady state — it
3693
3396
  * returns on the `receipt` check without touching the store — so it costs
3694
3397
  * only on the cases it exists for:
3695
3398
  *
3696
3399
  * - **A crash between the two store calls.** `claimReceiptSeq` commits the
3697
- * sequence number to the job row before `saveReceipt` writes the bytes; a
3698
- * process death in that window would otherwise strand that number
3699
- * forever, and a permanent gap is indistinguishable from the deliberate
3700
- * suppression `seq` exists to expose. Re-issuing here reuses the already
3701
- * committed number (the claim is idempotent per job) rather than
3702
- * allocating a second one.
3400
+ * sequence number to the job row before `saveReceipt` writes the bytes; a
3401
+ * process death in that window would otherwise strand that number
3402
+ * forever, and a permanent gap is indistinguishable from the deliberate
3403
+ * suppression `seq` exists to expose. Re-issuing here reuses the already
3404
+ * committed number (the claim is idempotent per job) rather than
3405
+ * allocating a second one.
3703
3406
  * - **A transient store failure** at the terminal: `issueReceipt` logs
3704
- * `receipt_issue_failed` and returns nothing rather than failing the job,
3705
- * so the next read retries.
3407
+ * `receipt_issue_failed` and returns nothing rather than failing the job,
3408
+ * so the next read retries.
3706
3409
  * - **Jobs that terminated before the DVM had a receipt key.** They pick one
3707
- * up on first read, with `issued_at` reflecting when it was signed.
3410
+ * up on first read, with `issued_at` reflecting when it was signed.
3708
3411
  */
3709
3412
  ensureReceipt(target: ServerJob | JobRecord): Promise<void>;
3710
- /**
3711
- * Close out a terminal job whose caller hasn't already persisted it: sign
3712
- * the receipt, then save the snapshot. The snapshot save never writes the
3713
- * receipt column, so ordering only affects how soon a reader sees the
3714
- * receipt — this way a caller polling immediately after the terminal
3715
- * already finds it.
3716
- */
3413
+ /** Commit the terminal snapshot before resolving money and signing its durable evidence. */
3717
3414
  private finalizeTerminal;
3718
3415
  /**
3719
3416
  * Issue a receipt for a job this process doesn't hold in `activeJobs` — a
@@ -3724,10 +3421,12 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3724
3421
  issueReceiptForStoredJob(jobId: string): Promise<JobReceipt | undefined>;
3725
3422
  /**
3726
3423
  * Finish accounting for a terminal written directly to the store — the
3727
- * stale reaper and a cancel handled on a different machine (internal-review).
3424
+ * stale reaper and a cancel handled on a different machine.
3728
3425
  */
3729
3426
  finalizeStoredTerminal(jobId: string): Promise<JobReceipt | undefined>;
3730
- /** Handle job reaching terminal state (completed, failed, cancelled). */
3427
+ private reportTerminalJob;
3428
+ private terminalReportRecoveryCursor?;
3429
+ private recoverTerminalReports;
3731
3430
  private handleJobTerminal;
3732
3431
  /**
3733
3432
  * Re-enqueue terminal cost-only rows left between the job commit and outbox
@@ -3744,11 +3443,11 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3744
3443
  /** Report the latest declared-cost state only when no revenue event carried it. */
3745
3444
  private reportJobCost;
3746
3445
  /**
3747
- * Report a completed paid job's revenue (internal-review, re-keyed by internal-review).
3446
+ * Report a completed paid job's revenue under its durable job identity.
3748
3447
  * Fire-and-forget — the `RevenueReporter` owns persistence and retry.
3749
3448
  *
3750
3449
  * For a **credit-backed** job the revenue event is the *settled draw* (spec
3751
- * §10), and since internal-review the sats figure corrects the job's mirror of that
3450
+ * §10), and since the behavior was introduced the sats figure corrects the job's mirror of that
3752
3451
  * draw against the draw itself: `withDrawBasis` stamps the job when the
3753
3452
  * payment lands, but a Bitcoin credit's settle re-prices the draw against
3754
3453
  * the funding lots it consumed. The correction is a delta, because a job's
@@ -3763,7 +3462,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3763
3462
  * whose price couldn't be fiat-denominated) nothing changes: the pre-credits
3764
3463
  * payload is reported verbatim.
3765
3464
  *
3766
- * Takes the fields rather than a `ServerJob` so the internal-review reconciler — a
3465
+ * Takes the fields rather than a `ServerJob` so the reconciler — a
3767
3466
  * background sweep that holds no in-process job — books through this exact
3768
3467
  * path off the row it read. `paymentTxHash` is passed **verbatim**, never
3769
3468
  * recomputed as a `drawSettlementRef`: it is the same column the in-line
@@ -3780,7 +3479,7 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3780
3479
  * DVM with no reporter). Every drop that costs a booking logs first.
3781
3480
  *
3782
3481
  * The rail check sits **below** the draw re-read, not in the entry guard
3783
- * (internal-review): a settled draw the terminal can't report under is a real debit
3482
+ * a settled draw the terminal can't report under is a real debit
3784
3483
  * with no revenue event, permanently overstating outstanding liability
3785
3484
  * (`deposits − draw revenue`), and it used to return here in silence. Placed
3786
3485
  * after the re-read, `revenue_skipped_no_rail` can name the draw and its
@@ -3789,19 +3488,16 @@ declare class JobManager<State, InputSchema extends ZodLike | undefined> {
3789
3488
  private bookRevenue;
3790
3489
  /** Schedule job removal from activeJobs after a delay so clients can still poll final status. */
3791
3490
  scheduleCleanup(job: ServerJob): void;
3792
- /** Clear all timers. Called on server shutdown to allow clean exit. */
3793
3491
  shutdown(): void;
3794
3492
  }
3795
3493
 
3796
3494
  /** Cached liveness signal for a platform-hosted DVM's Lightning receive rail. */
3797
3495
  interface LightningRailHealth {
3798
- /** Whether the receive rail may be advertised at this instant. */
3799
3496
  available(): boolean;
3800
3497
  /** Prime the cached signal at boot when the source supports it. */
3801
3498
  refresh?(): Promise<void>;
3802
3499
  }
3803
3500
 
3804
- /** Builder-facing wiring for the Lightning receive leg (internal-review). */
3805
3501
  interface LightningReceiveConfig {
3806
3502
  /**
3807
3503
  * Receive-only NWC connection URI (`DVMKIT_NWC_RECEIVE_URI`). Validated at
@@ -3835,31 +3531,19 @@ interface LightningReceiveConfig {
3835
3531
  declare const DEFAULT_INVOICE_TTL_SECONDS = 900;
3836
3532
  /**
3837
3533
  * Floor on the invoice lifetime: twice the signed-request drift window, so the
3838
- * bolt11 always outlives the funding negotiation that produced it (the issue's
3839
- * "invoice expiry ≥ the funding-negotiation window"). A shorter one would
3534
+ * bolt11 always outlives the funding negotiation that produced it. A shorter one would
3840
3535
  * expire inside the caller's own retry budget and strand them mid-top-up.
3841
3536
  */
3842
3537
  declare const MIN_INVOICE_TTL_SECONDS = 600;
3843
3538
  /**
3844
- * The builder-side Lightning receive leg (internal-review, credits spec §4).
3845
- *
3846
- * Issues a bolt11 over a **receive-only** NWC connection while the DVM is
3847
- * awake serving the 402, and credits the ledger when a later request observes
3848
- * settlement. Two properties are load-bearing:
3539
+ * Prepaid Lightning funding over a receive-safe NWC connection. URI construction validates
3540
+ * the advertised method allowlist, refusing spending or unknown authority; the injected
3541
+ * backend seam bypasses that probe. Receive-safe methods include invoice, read and
3542
+ * hold-invoice operations.
3849
3543
  *
3850
- * - **It never holds a send credential.** `pay_invoice` on this connection is
3851
- * refused at construction, so a compromised DVM can mint invoices and
3852
- * nothing else. The drain/refund sender (internal-review) is a separate, budgeted
3853
- * connection by rule.
3854
- * - **Crediting is pull-based — there is no settlement watcher.** The caller's
3855
- * next request drives `lookup_invoice`, which is what makes this work on a
3856
- * suspend-to-zero fleet: a machine that is asleep has nothing to miss.
3857
- * Wallet downtime at that moment delays crediting; it never loses money,
3858
- * because the invoice→credit binding is a durable row.
3859
- *
3860
- * Reachability is **cached**, never probed per request: the funding menu is
3861
- * assembled on every quote and every 402, so a live NIP-47 round trip there
3862
- * would put a nostr relay in the latency path of every priced call.
3544
+ * Crediting is pull-based: later requests observe settlement against the durable invoice
3545
+ * binding, so sleeping hosts need no watcher. Reachability is cached and refreshed in the
3546
+ * background for funding menus.
3863
3547
  */
3864
3548
  declare class LightningReceive {
3865
3549
  private readonly source;
@@ -3872,36 +3556,21 @@ declare class LightningReceive {
3872
3556
  private constructor();
3873
3557
  private readonly railHealth;
3874
3558
  /**
3875
- * Validate the configured connection and build the receive leg, or return
3876
- * `undefined` when this DVM didn't configure one.
3877
- *
3878
- * **Throws on a credential that is wrong, degrades on one that is merely
3879
- * unreachable.** A wallet outage at boot must not stop a DVM whose other
3880
- * rails are fine — the menu simply omits `lightning` until a later probe
3881
- * succeeds. A connection that can spend, or one that can't state what it can
3882
- * do, is a different thing entirely: it is a standing money risk that no
3883
- * amount of retrying fixes, so it fails the boot loudly with the fix in the
3884
- * message.
3559
+ * Return undefined when unconfigured. Permission/URI failures refuse construction with a
3560
+ * corrective message; wallet transport failure constructs an unavailable receive leg so
3561
+ * other rails can still boot. Recovery must validate method scope before advertising
3562
+ * Lightning.
3885
3563
  */
3886
3564
  static create(config: LightningReceiveConfig): Promise<LightningReceive | undefined>;
3887
3565
  /**
3888
- * Whether `lightning` belongs on the funding menu right now.
3889
- *
3890
- * Reads the cached observation and never blocks; a stale one kicks off a
3891
- * background refresh and answers with what we last knew. Advertising a rail
3892
- * whose wallet is down would hand the caller an option that 402s on use —
3893
- * the §4 posture is to drop it and let the sale survive on the others.
3566
+ * Answer from cached wallet and optional rail-health observations without blocking. A stale
3567
+ * wallet observation starts a background refresh.
3894
3568
  */
3895
3569
  available(): boolean;
3896
3570
  /**
3897
- * Issue (or re-issue) the bolt11 funding `(creditId, fundId)`.
3898
- *
3899
- * The invoice is minted **for** the credit and amount named in the caller's
3900
- * signed body — that binding is the internal-review condition-3 commitment on this
3901
- * rail, and it is why no artifact hash rides the request: there is no
3902
- * caller-supplied artifact to hash. A re-poll returns the stored row rather
3903
- * than minting again; a second bolt11 for one `fund_id` would leave two
3904
- * payable invoices against a funding that can only be credited once.
3571
+ * Return the stored invoice on retry: two payable bolt11s for one fund ID could charge
3572
+ * twice while only one credits. Caller and amount are bound to the recorded credit/funding
3573
+ * pair, without a caller-supplied artifact hash.
3905
3574
  */
3906
3575
  issue(ledger: CreditLedgerLike, args: {
3907
3576
  creditId: string;
@@ -3913,24 +3582,9 @@ declare class LightningReceive {
3913
3582
  description?: string;
3914
3583
  }): Promise<CreditInvoiceRecord>;
3915
3584
  /**
3916
- * Ask the wallet whether one payment hash is paid, and when (internal-review).
3917
- *
3918
- * The operator's reconcile verb runs this before it credits anything. A
3919
- * `blocked` row implies payment — {@link applyOne} returns above the block
3920
- * classification when the lookup says unpaid — but that is *this fleet's
3921
- * belief, recorded possibly weeks ago*, and the verb it gates mints balance
3922
- * against it. One round trip turns the belief into a fact and recovers the
3923
- * true settlement instant for `settled_at`, which a blocked row never got to
3924
- * write.
3925
- *
3926
- * Unlike {@link settlePending} this **throws**: there is no request whose
3927
- * latency it protects, and crediting on an unverifiable wallet is exactly
3928
- * what it exists to prevent. `NOT_FOUND` is the one exception, and it is not
3929
- * an outage — it is the wallet saying it has never seen this hash, the same
3930
- * reading {@link applyOne} and the caller-side reconcile in `src/lib/nwc.ts`
3931
- * take. It comes back as `known: false` because the operator's repair for it
3932
- * ("you are pointed at a different wallet than the one that minted this")
3933
- * is not the repair for an unpaid invoice, and certainly not for a retry.
3585
+ * Re-observe the wallet payment before an operator credits a blocked invoice and recover
3586
+ * its settlement instant. Unlike settlePending, lookup failures throw. NOT_FOUND returns
3587
+ * known:false, distinguishing the wrong wallet/unknown hash from an unpaid known invoice.
3934
3588
  */
3935
3589
  lookupSettlement(paymentHash: string): Promise<{
3936
3590
  settled: boolean;
@@ -3938,23 +3592,12 @@ declare class LightningReceive {
3938
3592
  known: boolean;
3939
3593
  }>;
3940
3594
  /**
3941
- * Consult the wallet about this caller's outstanding invoices and credit the
3942
- * ones that settled — the pull half of pull-based crediting.
3943
- *
3944
- * The pending rows are read locally first, so the common case (nothing
3945
- * outstanding) costs one indexed SELECT and no network at all. Bounded to
3946
- * {@link MAX_SETTLE_CHECKS} invoices on a short deadline, and **never throws
3947
- * into the request**: a wallet failure here means the caller's balance is
3948
- * merely not updated yet, which the ordinary `insufficient_credit` path
3949
- * already states honestly.
3595
+ * Read pending invoices locally, then check at most MAX_SETTLE_CHECKS on bounded wallet
3596
+ * deadlines. Log failures without throwing into the request.
3950
3597
  *
3951
- * **Each invoice gets its own failure boundary.** The sweep window is a few
3952
- * rows wide, so a row that fails on its own terms — a `fund_id` the caller
3953
- * reused on another rail, a credit someone else opened first — must not take
3954
- * the rest of the pass down with it: the next invoice in the window may be
3955
- * the paid one, and it would never be looked up. Only a transport failure
3956
- * ends the pass early, because there the wallet itself is gone and the
3957
- * remaining lookups would just spend the caller's latency confirming it.
3598
+ * An invoice-specific failure must not suppress payable siblings; wallet transport failure
3599
+ * ends the pass. Deterministic refusals retire that row as blocked for operator repair,
3600
+ * while transient failures remain pending.
3958
3601
  */
3959
3602
  settlePending(ledger: CreditLedgerLike, args: {
3960
3603
  callerPubkey: string;
@@ -3964,49 +3607,21 @@ declare class LightningReceive {
3964
3607
  enqueueCreditDeposit?: CreditDepositEnqueue;
3965
3608
  nowMs?: number;
3966
3609
  }): Promise<InvoiceSettlement[]>;
3967
- /**
3968
- * One invoice's settlement check. Returns the applied settlement, or
3969
- * `undefined` when nothing changed (still unpaid, or retired unpaid).
3970
- *
3971
- * Throws whatever the wallet or the ledger threw — classifying that is
3972
- * {@link retireOrLog}'s job, so this stays one invoice's happy path.
3973
- */
3974
3610
  private applyOne;
3975
3611
  /**
3976
- * Decide what one invoice's failure means, and retire the invoice when the
3977
- * answer is "this can never succeed".
3978
- *
3979
- * The split that matters is permanent-versus-transient, because it decides
3980
- * whether the row keeps a slot in the sweep window. A transient failure — the
3981
- * database blinked, the wallet answered oddly — leaves it `pending` and the
3982
- * caller's next request retries it. A **permanent** one means the ledger will
3983
- * refuse this invoice identically forever, so leaving it pending costs a
3984
- * `lookup_invoice` on every subsequent request and, at
3985
- * {@link MAX_SETTLE_CHECKS} of them, fills the window so a genuinely payable
3986
- * invoice behind them is never even looked up. Those get `blocked`, which
3987
- * drops them out of the sweep and hands the operator the payment hash.
3612
+ * Retire a deterministic uncreditable payment as blocked so it no longer occupies the
3613
+ * bounded sweep window. Transient failures stay pending for a later request; report the
3614
+ * payment hash for operator repair.
3988
3615
  */
3989
3616
  private retireOrLog;
3990
3617
  /**
3991
- * The reason this invoice can never be credited, or `undefined` if the
3992
- * failure was transient.
3993
- *
3994
- * Every code here is decided by state a retry cannot move — a row the ledger
3995
- * has already committed (a `credit_fundings` entry at this
3996
- * `(credit_id, fund_id)`, or a `credits` row whose owner, denomination or
3997
- * rail disagrees with the invoice), or the basis this module rebuilds
3998
- * identically from the invoice row on every pass. None of them is a race.
3999
- *
4000
- * `funding_replayed` is the one that needs a second read to classify.
4001
- * Usually it *is* benign — a concurrent check won the race and the money is
4002
- * credited either way — but the key is `(credit_id, fund_id)` and `fund_id`
4003
- * is **caller-chosen**: reuse it on cashu or x402 after this bolt11 was
4004
- * minted and the row that collided is a different payment entirely, so the
4005
- * sats this invoice received have nowhere to land.
3618
+ * Classify funding_replayed by reading the existing funding’s rail. A Lightning row can
3619
+ * represent a concurrent applied check; another rail at this caller-chosen key is a
3620
+ * different payment, leaving the invoice’s sats uncredited. A failed diagnostic read
3621
+ * remains transient, never a guessed permanent refusal.
4006
3622
  */
4007
3623
  private blockingReason;
4008
3624
  private withBackend;
4009
- /** Record activity without letting it promote an unvalidated connection. */
4010
3625
  private noteOperationOk;
4011
3626
  private notePermissionsOk;
4012
3627
  private notePermissionRefused;
@@ -4019,7 +3634,7 @@ declare class LightningReceive {
4019
3634
  }
4020
3635
 
4021
3636
  /**
4022
- * Owner display identity surfaced on `/v1/info#owner` (internal-review). Personal orgs
3637
+ * Owner display identity surfaced on `/v1/info#owner`. Personal orgs
4023
3638
  * resolve to the owner builder's profile; shared orgs resolve to the org's own
4024
3639
  * profile. Container DVMs read from env vars; isolate DVMs resolve from Postgres.
4025
3640
  */
@@ -4029,22 +3644,22 @@ interface OwnerDisplay {
4029
3644
  avatarUrl?: string | null;
4030
3645
  type: "builder" | "org";
4031
3646
  }
4032
- /** Optional builder identity surfaced on `/v1/info` (forward-compatible stub). */
3647
+ /** Builder identity advertised on /v1/info. */
4033
3648
  interface BuilderIdentity {
4034
3649
  id?: string;
4035
3650
  name?: string;
4036
3651
  url?: string;
4037
3652
  /**
4038
- * Builder identity x-only secp256k1 pubkey (internal-review). When populated, the
3653
+ * Builder identity x-only secp256k1 pubkey. When populated, the
4039
3654
  * `/v1/info#builder` block also carries `attestation` + `signature` so
4040
3655
  * consumers can verify the deploy was signed by the holder of this key.
4041
3656
  * Populated at deploy time from `DVMKIT_BUILDER_PUBKEY` (Fly secret); the
4042
3657
  * SDK never re-signs at runtime.
4043
3658
  */
4044
3659
  pubkey?: string;
4045
- /** Canonical deploy-time attestation payload (internal-review). Served verbatim. */
3660
+ /** Canonical deploy-time attestation payload. Served verbatim. */
4046
3661
  attestation?: AttestationPayload;
4047
- /** Schnorr signature over `canonicaliseForSigning(attestation)` (internal-review). */
3662
+ /** Schnorr signature over `canonicaliseForSigning(attestation)`. */
4048
3663
  signature?: string;
4049
3664
  }
4050
3665
  /** Platform reporter overrides — the host falls back to env when omitted. */
@@ -4054,28 +3669,29 @@ interface PlatformReporterOpts {
4054
3669
  /** Platform internal URL. Defaults to `DVMKIT_PLATFORM_URL` env. */
4055
3670
  url?: string;
4056
3671
  }
4057
- /** Running host handle returned by serve(). */
4058
3672
  interface DVMServeResult {
4059
3673
  url: string;
4060
3674
  close: () => Promise<void>;
4061
3675
  /** Boot snapshot for each mount; configured but unusable rails remain payment-required. */
4062
3676
  paymentModes: PaymentMode[];
4063
3677
  }
4064
- /** Options for {@link createDVMHost}. */
4065
3678
  interface DVMHostOpts {
4066
3679
  /**
4067
3680
  * Postgres connection string. Defaults to `DATABASE_URL` env.
4068
- * Required at runtime (internal-review). Boot fails when missing unless `jobStore`
3681
+ * Required at runtime. Boot fails when missing unless `jobStore`
4069
3682
  * is explicitly supplied or `devMode` is true (test/dev escape hatches).
4070
3683
  */
4071
3684
  database?: string;
4072
3685
  /** Listen port. Defaults to `PORT` env or 8080. */
4073
3686
  port?: number;
4074
- /** Environment variables. Defaults to `process.env`. */
3687
+ /**
3688
+ * Environment variables. Defaults to `process.env` when omitted. A supplied
3689
+ * object is the complete host configuration: absent fields do not fall back
3690
+ * to ambient process variables.
3691
+ */
4075
3692
  env?: Record<string, string>;
4076
- /** Builder identity for `/v1/info` (forward-compatible). */
4077
3693
  builder?: BuilderIdentity;
4078
- /** Owner display identity for `/v1/info#owner` (internal-review). Falls back to env vars. */
3694
+ /** Owner display identity for `/v1/info#owner`. Falls back to env vars. */
4079
3695
  owner?: OwnerDisplay;
4080
3696
  /** Platform reporter overrides. Defaults to env-derived values. */
4081
3697
  platformReporter?: PlatformReporterOpts;
@@ -4093,7 +3709,7 @@ interface DVMHostOpts {
4093
3709
  jobStore?: JobStore;
4094
3710
  /**
4095
3711
  * Existing Postgres pool to reuse instead of opening one from `database`
4096
- * (internal-review test seam). When set, the host builds its JobStore / KVStore /
3712
+ * (also useful as a test seam). When set, the host builds its JobStore / KVStore /
4097
3713
  * cashu accumulator on this pool and leaves it open at shutdown — the caller
4098
3714
  * owns it. Lets the e2e harness run DVMs in `p2pk-accumulator` mode against
4099
3715
  * its single shared pool rather than spawning a pool per DVM.
@@ -4101,7 +3717,6 @@ interface DVMHostOpts {
4101
3717
  pool?: Pool;
4102
3718
  /** Cashu mints accepted. Defaults to env `DVMKIT_CASHU_MINTS`. */
4103
3719
  mints?: string[];
4104
- /** Cashu receive mode override. */
4105
3720
  cashuMode?: CashuMode;
4106
3721
  /** MPP handle override. Defaults to env-resolved via `createMppFromOpts`. */
4107
3722
  mpp?: MppxServer;
@@ -4110,7 +3725,7 @@ interface DVMHostOpts {
4110
3725
  /** Payment methods override. Defaults to derived from configured rails. */
4111
3726
  paymentMethods?: PaymentMethod[];
4112
3727
  /**
4113
- * Lightning receive leg for credit funding (internal-review). Defaults to the
3728
+ * Lightning receive leg for credit funding. Defaults to the
4114
3729
  * env-resolved `DVMKIT_NWC_RECEIVE_URI` /
4115
3730
  * `DVMKIT_NWC_RECEIVE_INVOICE_TTL_SECONDS`. Pass `backend` to inject a
4116
3731
  * wallet directly — a test seam that skips the connect-time probe, so
@@ -4124,9 +3739,7 @@ interface DVMHostOpts {
4124
3739
  onPaidJobCompleted?: (event: PaidJobCompletion) => void | Promise<void>;
4125
3740
  /** When true, payment is skipped only when no payment rail is configured. */
4126
3741
  devMode?: boolean;
4127
- /** Consumed-credential store override (test seam). */
4128
3742
  consumedCredentialStore?: ConsumedCredentialStore;
4129
- /** Mint-health tracker override (test seam). */
4130
3743
  mintHealthTracker?: MintHealthTracker;
4131
3744
  /** Operator-alert boundary for failed terminal-job retention passes. */
4132
3745
  onJobRetentionSweepFailed?: JobManagerOpts["onJobRetentionSweepFailed"];
@@ -4139,12 +3752,10 @@ interface DVMHostOpts {
4139
3752
  */
4140
3753
  wrapRouteClientCompatibility?: (sdkGate: ClientCompatibilityGate) => ClientCompatibilityGate;
4141
3754
  }
4142
- /** Per-mount options. Currently only `prefix` is supported. */
4143
3755
  interface MountOpts {
4144
3756
  /** Path prefix for this DVM's protocol routes (e.g. `/delete-feed`). */
4145
3757
  prefix?: string;
4146
3758
  }
4147
- /** Live DVM host. Single wiring locus for the SDK (internal-review). */
4148
3759
  interface DVMHost {
4149
3760
  /**
4150
3761
  * Underlying Hono app for custom non-protocol routes that belong to the
@@ -4155,15 +3766,15 @@ interface DVMHost {
4155
3766
  /**
4156
3767
  * Resolved Postgres pool — undefined before `host.serve()` completes
4157
3768
  * database resolution, set thereafter. Exposed so DVM-local Postgres
4158
- * consumers (e.g. scrape's `ScrapeDb` per-fetch event store, internal-review)
3769
+ * consumers (e.g. scrape's `ScrapeDb` per-fetch event store)
4159
3770
  * can reuse the host's pool rather than constructing their own. Wire
4160
3771
  * inside descriptor `onBoot` (fires after pool init, before listener opens).
4161
3772
  */
4162
3773
  readonly pool: Pool | undefined;
4163
3774
  /**
4164
- * Money-safe credit ledger (internal-review) — undefined before `host.serve()`
3775
+ * Money-safe credit ledger — undefined before `host.serve()`
4165
3776
  * resolves stores, set thereafter. Postgres-backed when a pool exists;
4166
- * pool-less hosts get a shared in-memory ledger so the internal-review fund+draw
3777
+ * pool-less hosts get a shared in-memory ledger so the fund+draw
4167
3778
  * semantics hold everywhere. Same wiring window as `pool` (descriptor
4168
3779
  * `onBoot` fires after init, before the listener opens).
4169
3780
  */
@@ -4173,7 +3784,6 @@ interface DVMHost {
4173
3784
  * a single mount with no prefix attaches at root.
4174
3785
  */
4175
3786
  mount<S, I extends ZodLike | undefined>(descriptor: DVMDescriptor<S, I>, opts?: MountOpts): void;
4176
- /** Start listening. Returns the live server handle. */
4177
3787
  serve(opts?: {
4178
3788
  port?: number;
4179
3789
  }): Promise<DVMServeResult>;
@@ -4181,7 +3791,7 @@ interface DVMHost {
4181
3791
  shutdown(): Promise<void>;
4182
3792
  }
4183
3793
  /**
4184
- * Single wiring locus for SDK-side construction (internal-review). Replaces both the
3794
+ * Single wiring locus for SDK-side construction. Replaces both the
4185
3795
  * `serve()` wrapper and the `createDVMServer` direct path. Reads env once,
4186
3796
  * resolves payment rails, owns the Postgres pool, and mounts each DVM
4187
3797
  * descriptor as a Hono sub-app on `host.app`.
@@ -4190,13 +3800,12 @@ declare function createDVMHost(opts?: DVMHostOpts): DVMHost;
4190
3800
 
4191
3801
  /**
4192
3802
  * Cached liveness signal for the platform's central Tempo close observer
4193
- * (internal-review).
4194
3803
  *
4195
3804
  * A TIP-1034 session channel is only safe to accept while something is
4196
3805
  * watching the chain for its close: without that, a caller can consume value,
4197
3806
  * request a close, wait out the 900-second grace period and withdraw. So this
4198
3807
  * gate is **fail-closed**, which is the opposite of `lightning-rail-health.ts`
4199
- * — there, withholding a rail over an outage costs a sale; here, advertising
3808
+ * there, withholding a rail over an outage costs a sale; here, advertising
4200
3809
  * one costs the money. `tempo/charge` is untouched either way: a charge is an
4201
3810
  * ordinary payment with no channel and no close to watch.
4202
3811
  */
@@ -4218,22 +3827,10 @@ interface TempoObserverHealth {
4218
3827
  /** Prime the cached signal at boot. */
4219
3828
  refresh?(): Promise<void>;
4220
3829
  /**
4221
- * Register a mount's `tempo/session` **capability** so the poll can carry it
4222
- * back to the platform (internal-review).
4223
- *
4224
- * The platform's `dvms.tempo_session_advertised_at` — the only filter on the
4225
- * close observer's payee-fallback rung 2 — was written once per container
4226
- * deploy and never refreshed, so a builder flipping sessions on with
4227
- * `dvmctl env --set` stayed invisible to the fallback indefinitely. That is a
4228
- * `no_route` on the first close of that DVM's first channel, which is exactly
4229
- * the race the rung exists to cover.
4230
- *
4231
- * The reader must answer from **configuration**, never from
4232
- * {@link TempoObserverHealth.available} — a fail-closed gate blip must not
4233
- * de-stamp the DVM, which is the internal-review failure through a new door.
4234
- *
4235
- * Returns a disposer the mount calls on shutdown, so a torn-down capability
4236
- * stops voting in the OR below.
3830
+ * Report the OR of mounted session capabilities to keep the platform's
3831
+ * observer watch signal current. Readers must use configuration, not current
3832
+ * availability: an observer outage must not erase the capability it watches.
3833
+ * The returned disposer removes a mount's contribution on shutdown.
4237
3834
  */
4238
3835
  declareSessionCapability?(read: () => boolean): () => void;
4239
3836
  }
@@ -4281,7 +3878,6 @@ interface TempoSettlementReadinessStateStore {
4281
3878
  clearFailureIfRefilled(version: string, balanceMicro: bigint, lowBalanceMicro: bigint): Promise<boolean>;
4282
3879
  clearFailureAfterSuccessfulTransaction(version: string): Promise<boolean>;
4283
3880
  }
4284
- /** Construction inputs for {@link TempoSettlementReadiness}. */
4285
3881
  interface TempoSettlementReadinessOptions {
4286
3882
  address: `0x${string}`;
4287
3883
  network: string;
@@ -4328,7 +3924,6 @@ declare class TempoSettlementReadiness {
4328
3924
  start(): void;
4329
3925
  /** Stop the cadence without affecting any in-flight reading. */
4330
3926
  stop(): void;
4331
- /** Read and report the configured fee-token balance now. */
4332
3927
  checkBalance(opts?: {
4333
3928
  successfulTransaction?: boolean;
4334
3929
  recoveryVersion?: string;
@@ -4351,24 +3946,11 @@ declare class TempoSettlementReadiness {
4351
3946
  private notify;
4352
3947
  }
4353
3948
 
4354
- /**
4355
- * Options for the revenue reporter readiness assertion (internal-review).
4356
- *
4357
- * The check branches on `platformUrl` presence — that's the unambiguous
4358
- * "platform-hosted" signal. `failFast` stays on the opts as a general SDK
4359
- * strict-mode flag but is not consulted by this assertion.
4360
- */
4361
3949
  interface RevenueBootCheckOpts {
4362
3950
  /**
4363
- * General SDK strict-mode flag, sourced from `DVMKIT_FAIL_FAST` at the host
4364
- * boundary (internal-review). Reserved for future warning-vs-fatal SDK checks; the
4365
- * reporter check itself ignores it (internal-review), hence optional.
3951
+ * Reporter readiness ignores this host strict-mode flag.
4366
3952
  */
4367
3953
  failFast?: boolean;
4368
- /**
4369
- * The SDK's canonical "this is a dev/test run" signal. Exempts the boot from
4370
- * the platform-hosted wiring requirement (internal-review) — see below.
4371
- */
4372
3954
  devMode?: boolean;
4373
3955
  platformToken: string | undefined;
4374
3956
  platformUrl: string | undefined;
@@ -4376,63 +3958,38 @@ interface RevenueBootCheckOpts {
4376
3958
  hasPgPool: boolean;
4377
3959
  }
4378
3960
  /**
4379
- * Assert that revenue reporting is correctly configured at boot (internal-review).
4380
- *
4381
- * Platform-hosted DVMs (signalled by `DVMKIT_PLATFORM_URL`) unconditionally
4382
- * require `DVMKIT_PLATFORM_TOKEN`, `DVMKIT_DVM_ID`, and a database connection.
4383
- * Partial wiring is always fatal — there's no legitimate "platform set my URL
4384
- * but didn't finish wiring" case, and `DVMKIT_FAIL_FAST` has no effect here.
4385
- *
4386
- * Self-hosted DVMs (no `DVMKIT_PLATFORM_URL`) have no platform reporter
4387
- * contract to enforce — the SDK skips validation entirely.
4388
- *
4389
- * `devMode` boots are exempt (internal-review), mirroring the test-mint guard's
4390
- * exemption in `createDVMHost` (internal-review, `host.ts`). `devMode` is set by
4391
- * `dvmctl dev`, `standalone development tooling`, the money-loop, and the e2e/smoke
4392
- * harnesses — all of which spread `...process.env`, so a builder whose shell
4393
- * exports `DVMKIT_PLATFORM_URL` for their `dvmctl` platform commands would
4394
- * otherwise hit this FATAL on a dev server that reports no revenue anyway (no
4395
- * payment, no auth, no DB). Production boots never set it: first-party
4396
- * container entrypoints and `dvmctl serve` (`buildServeOpts`) both leave it
4397
- * unset — asserted in
4398
- * `__tests__/revenue-reporter-boot.test.ts`, not just claimed here.
3961
+ * Hosted boots (a platform URL is set) require a token, DVM ID and database;
3962
+ * partial wiring is fatal regardless of `failFast`. Self-hosted and `devMode`
3963
+ * boots skip this assertion. The exemption does not disable an already wired
3964
+ * reporter; the banner separately describes whether reporting is active.
4399
3965
  */
4400
3966
  declare function assertRevenueReporterReady(opts: RevenueBootCheckOpts): void;
4401
3967
  /** What the host's boot banner knows about the revenue reporter's wiring. */
4402
3968
  interface ReporterBannerOpts {
4403
3969
  /** `DVMKIT_PLATFORM_URL` — the platform-hosted signal. */
4404
3970
  platformUrl: string | undefined;
4405
- /** Whether `DVMKIT_PLATFORM_TOKEN` resolved — never the token itself (internal-review). */
3971
+ /** Whether `DVMKIT_PLATFORM_TOKEN` resolved — never the token itself. */
4406
3972
  hasPlatformToken: boolean;
4407
3973
  /** Whether `DVMKIT_DVM_ID` resolved. */
4408
3974
  hasDvmId: boolean;
4409
- /** Whether a Postgres pool is wired. */
4410
3975
  databaseConnected: boolean;
4411
- /** Dev/test boot — exempt from the wiring assertion above (internal-review). */
4412
3976
  devMode: boolean;
4413
3977
  }
4414
3978
  /**
4415
- * Render the boot banner's `Platform rep.` line (internal-review).
3979
+ * Render the boot banner's `Platform rep.` line.
4416
3980
  *
4417
3981
  * Lives here, next to the assertion, because `active` has to mirror the
4418
3982
  * reporter's own wiring gate in `createDVMServer` below — token **and** dvmId
4419
3983
  * **and** database. Miss one and the banner claims a reporter that was never
4420
3984
  * constructed.
4421
3985
  *
4422
- * On a production boot the assertion throws before the banner prints, so only
4423
- * `(self-hosted)` and `active` are reachable there. A `devMode` boot skips the
4424
- * assertion (internal-review), making it the one path where partial wiring reaches
4425
- * this line — say so plainly rather than shouting `misconfigured` at a builder
4426
- * whose shell merely exports the platform URL.
3986
+ * Partial dev wiring reaches the banner because the boot assertion is exempt;
3987
+ * it reports a dev status rather than a production configuration failure.
4427
3988
  */
4428
3989
  declare function revenueReporterBannerState(s: ReporterBannerOpts): string;
4429
- /** Options for creating the SDK server app. */
4430
3990
  interface SDKServerOpts {
4431
- /** Environment variables for ctx.env. */
4432
3991
  env: Record<string, string>;
4433
- /** KV store for ctx.store. */
4434
3992
  store: KVStore;
4435
- /** Cashu mints accepted for payment. */
4436
3993
  mints?: string[];
4437
3994
  /** Persistent backing store for job records. Default: in-memory. */
4438
3995
  jobStore?: JobStore;
@@ -4440,7 +3997,6 @@ interface SDKServerOpts {
4440
3997
  devMode?: boolean;
4441
3998
  /** Payment methods this DVM accepts. Default: derived from configured rails — adds "cashu" when `mints` is set, "x402" when `x402` is set, "tempo" when `tempo` is set. */
4442
3999
  paymentMethods?: PaymentMethod[];
4443
- /** x402 stablecoin payment configuration. */
4444
4000
  x402?: X402Config;
4445
4001
  /** Live facilitator capability gate. Hosts wire this after probing `/supported`. */
4446
4002
  x402FacilitatorHealth?: X402FacilitatorHealthLike;
@@ -4449,13 +4005,13 @@ interface SDKServerOpts {
4449
4005
  /**
4450
4006
  * Tracks already-consumed upfront-flow MPP credentials so the same
4451
4007
  * credential can't satisfy `POST /v1/job` twice within its `expires`
4452
- * window (internal-review). Default: in-memory, bounded, per-process. Swap for a
4008
+ * window. Default: in-memory, bounded, per-process. Swap for a
4453
4009
  * shared backend before deploying multiple replicas.
4454
4010
  */
4455
4011
  consumedCredentialStore?: ConsumedCredentialStore;
4456
4012
  /**
4457
4013
  * Tracks already-consumed admin-endpoint nonces so a signed P2PK challenge
4458
- * can't be replayed within the 5-minute sliding window (internal-review). Default:
4014
+ * can't be replayed within the 5-minute sliding window. Default:
4459
4015
  * in-memory, bounded, per-process. Swap for a shared backend before
4460
4016
  * deploying multiple replicas.
4461
4017
  */
@@ -4469,14 +4025,14 @@ interface SDKServerOpts {
4469
4025
  * registration order.
4470
4026
  */
4471
4027
  healthHandler?: (c: Context<AppEnv>) => Response | Promise<Response>;
4472
- /** Cashu receive mode (internal-review). */
4028
+ /** Cashu receive mode. */
4473
4029
  cashuMode?: CashuMode;
4474
4030
  /**
4475
- * Builder's NUT-11 P2PK lock pubkey (internal-review). Used as the seed pubkey for
4031
+ * Builder's NUT-11 P2PK lock pubkey. Used as the seed pubkey for
4476
4032
  * `seedLockPubkeyState` on first boot, as the admin-auth identity, and as
4477
4033
  * part of the gate for whether `/v1/info` advertises `cashu`. After first
4478
4034
  * boot, the authoritative `[current, ...retired]` state lives in the DVM's
4479
- * Postgres via `dvm_lock_pubkeys`; rotations (internal-review) mutate that store
4035
+ * Postgres via `dvm_lock_pubkeys`; rotations mutate that store
4480
4036
  * directly.
4481
4037
  */
4482
4038
  lockPubkey?: string;
@@ -4485,12 +4041,11 @@ interface SDKServerOpts {
4485
4041
  /**
4486
4042
  * Rotation grace window (seconds) — retired pubkeys remain advertised on
4487
4043
  * `cashu.lock_pubkeys` and accepted by the receive path until they age out.
4488
- * Default 24h (`DEFAULT_LOCK_PUBKEY_GRACE_SECONDS`); operators tune via
4489
- * `BUILDER_LOCK_PUBKEY_GRACE_SECONDS` env (internal-review).
4044
+ * Defaults to 24h.
4490
4045
  */
4491
4046
  lockPubkeyGraceSeconds?: number;
4492
4047
  /**
4493
- * Per-DVM canonical identifier (internal-review). Advertised on `/v1/info` as
4048
+ * Per-DVM canonical identifier. Advertised on `/v1/info` as
4494
4049
  * `cashu.canonical_id` so agents can derive the matching per-DVM refund
4495
4050
  * subkey at `m/1789'/2'/hashed(canonical_dvm_id)'`. Defaults to `dvmId`
4496
4051
  * (the platform's DVM UUID) when unset — keeps `/v1/info` self-consistent
@@ -4500,30 +4055,23 @@ interface SDKServerOpts {
4500
4055
  /** Postgres pool for builder admin state and wallet accumulator persistence. */
4501
4056
  db?: Pool;
4502
4057
  /**
4503
- * General SDK strict-mode flag (internal-review), sourced from `DVMKIT_FAIL_FAST` at
4504
- * the host boundary. Reserved for future warning-vs-fatal SDK checks; the
4505
- * revenue-reporter boot check is gated on platform context, not on this
4506
- * flag (internal-review).
4058
+ * Host strict-mode flag. Reporter readiness follows hosted context regardless
4059
+ * of this value.
4507
4060
  */
4508
4061
  failFast?: boolean;
4509
- /** Agent-wallet (internal-review): per-DVM identifier — drives the monitor advisory lock + replay key. */
4062
+ /** Agent-wallet: per-DVM identifier — drives the monitor advisory lock + replay key. */
4510
4063
  dvmId?: string;
4511
4064
  /**
4512
- * Runtime mint-health tracker (internal-review). When omitted, the server builds a
4513
- * fresh one whenever `mints?.length > 0`. Sick mints are dropped from
4514
- * `/v1/info`'s advertised list and rejected at receive time. Tests pass an
4515
- * instance with `runOnce`-driven state instead of relying on the timer.
4065
+ * Runtime mint-health tracker. When omitted, the server builds a
4066
+ * fresh one whenever `mints?.length > 0`. Healthy mints are preferred in
4067
+ * `/v1/info`; when none are healthy, transiently sick mints remain advertised.
4068
+ * NUT-incompatible mints are excluded even from that fallback.
4516
4069
  */
4517
4070
  mintHealthTracker?: MintHealthTracker;
4518
- /**
4519
- * Shared fx fetcher used to translate USD descriptor prices and fiat-form
4520
- * `ctx.requestPayment` calls into sats (internal-review). When omitted, the server
4521
- * builds one from `env.DVMKIT_FX_SOURCE` (or the bundled CoinGecko default).
4522
- * Tests pass a deterministic stub.
4523
- */
4071
+ /** Shared upfront/mid-job fiat conversion fetcher. Defaults to `DVMKIT_FX_SOURCE` or CoinGecko. */
4524
4072
  fxFetcher?: FxFetcher;
4525
4073
  /**
4526
- * Platform API token for revenue reporting (internal-review). Read from
4074
+ * Platform API token for revenue reporting. Read from
4527
4075
  * `opts.env.DVMKIT_PLATFORM_TOKEN` when not set explicitly. When both
4528
4076
  * `platformToken` and `platformUrl` are present alongside `db` and `dvmId`,
4529
4077
  * the SDK auto-constructs a `RevenueReporter` and wires it into the
@@ -4531,35 +4079,32 @@ interface SDKServerOpts {
4531
4079
  */
4532
4080
  platformToken?: string;
4533
4081
  /**
4534
- * Platform API URL for revenue reporting (internal-review). Read from
4082
+ * Platform API URL for revenue reporting. Read from
4535
4083
  * `opts.env.DVMKIT_PLATFORM_URL` when not set explicitly.
4536
4084
  */
4537
4085
  platformUrl?: string;
4538
- /**
4539
- * Advanced override: callback fired when a paid job completes (internal-review).
4540
- * When set, bypasses the SDK's automatic `RevenueReporter` wiring — the
4541
- * caller assumes full responsibility for revenue tracking. Intended for
4542
- * tests and advanced integrations only.
4543
- */
4086
+ /** Observes completed jobs with a positive settled draw; failures are logged and swallowed. */
4544
4087
  onPaidJobCompleted?: (event: PaidJobCompletion) => void | Promise<void>;
4545
4088
  paymentMode?: PaymentMode;
4089
+ /** Overrides automatic RevenueReporter wiring; the caller owns revenue delivery. */
4546
4090
  onJobCompleted?: JobManagerOpts["onJobCompleted"];
4547
4091
  /**
4548
4092
  * Advanced override for a declared cost on a terminal job that emitted no
4549
- * revenue report (internal-review). Auto-wired to the reporter's durable
4093
+ * revenue report. Auto-wired to the reporter's durable
4550
4094
  * job-cost path; intended for tests and advanced integrations only.
4551
4095
  */
4552
4096
  onJobCost?: JobManagerOpts["onJobCost"];
4097
+ onJobTerminal?: JobManagerOpts["onJobTerminal"];
4553
4098
  /**
4554
4099
  * Advanced override: callback fired when the stale-job reaper force-fails a
4555
- * *paid* job (internal-review). Auto-wired to the `RevenueReporter`'s paid-job-death
4100
+ * *paid* job. Auto-wired to the `RevenueReporter`'s paid-job-death
4556
4101
  * report alongside `onJobCompleted`; set explicitly only for tests / advanced
4557
4102
  * integrations that own their own reporting.
4558
4103
  */
4559
4104
  onPaidJobDeath?: JobManagerOpts["onPaidJobDeath"];
4560
4105
  /**
4561
4106
  * Advanced override: callback fired when a settled credit draw could not
4562
- * book revenue because its rail was unavailable (internal-review). Auto-wired to
4107
+ * book revenue because its rail was unavailable. Auto-wired to
4563
4108
  * the platform reporter alongside the completion and paid-job-death paths.
4564
4109
  */
4565
4110
  onRevenueSkippedNoRail?: (info: RevenueSkippedNoRailPayload) => void | Promise<void>;
@@ -4568,22 +4113,22 @@ interface SDKServerOpts {
4568
4113
  /** Advanced clear-on-recovery callback for clean job-retention passes. */
4569
4114
  onJobRetentionSweepRecovered?: JobManagerOpts["onJobRetentionSweepRecovered"];
4570
4115
  /**
4571
- * Advanced override: callback fired when a payment funds a credit (internal-review).
4116
+ * Advanced override: callback fired when a payment funds a credit.
4572
4117
  * Auto-wired to the `RevenueReporter`'s deposit report alongside
4573
4118
  * `onJobCompleted`; set explicitly only for tests / advanced integrations
4574
4119
  * that own their own reporting. Fundings are deposits — a liability until
4575
- * drawn — reported separately from the draws that become revenue (spec §10).
4120
+ * drawn — reported separately from the draws that become revenue (the deposit-accounting rule).
4576
4121
  */
4577
4122
  onCreditFunded?: (info: CreditDepositPayload) => void | Promise<void>;
4578
4123
  /**
4579
4124
  * Grace window (ms) a reactivating machine waits for a live original handler
4580
4125
  * to win the single-execution claim before replaying an `awaiting-input` job
4581
- * (internal-review). Defaults to the JobManager's built-in value; exposed as an ops
4126
+ * Defaults to the JobManager's built-in value; exposed as an ops
4582
4127
  * override and a test seam.
4583
4128
  */
4584
4129
  reactivationClaimGraceMs?: JobManagerOpts["reactivationClaimGraceMs"];
4585
4130
  /**
4586
- * Builder identity for `/v1/info#builder` (internal-review). Inline alternative
4131
+ * Builder identity for `/v1/info#builder`. Inline alternative
4587
4132
  * to the `DVMKIT_BUILDER_PUBKEY` / `_ATTESTATION` / `_SIGNATURE` env vars
4588
4133
  * the platform's deploy pipeline injects as Fly secrets. When set,
4589
4134
  * `builder.{pubkey, attestation, signature}` take per-field precedence
@@ -4591,10 +4136,10 @@ interface SDKServerOpts {
4591
4136
  * `/v1/info#builder` block to be emitted.
4592
4137
  */
4593
4138
  builder?: BuilderIdentity;
4594
- /** Owner display identity for `/v1/info#owner` (internal-review). Falls back to env vars. */
4139
+ /** Owner display identity for `/v1/info#owner`. Falls back to env vars. */
4595
4140
  owner?: OwnerDisplay;
4596
4141
  /**
4597
- * Per-DVM receipt-signing secret, 32 bytes hex (internal-review). Resolved at the
4142
+ * Per-DVM receipt-signing secret, 32 bytes hex. Resolved at the
4598
4143
  * host boundary from `DVMKIT_RECEIPT_KEY` — the deploy pipeline provisions
4599
4144
  * it as a Fly secret, derived one-way from the builder identity key and
4600
4145
  * bound into the deploy attestation's `receipt_pubkey`.
@@ -4605,25 +4150,25 @@ interface SDKServerOpts {
4605
4150
  */
4606
4151
  receiptKey?: string;
4607
4152
  /**
4608
- * Credit ledger every payment funds and draws through (internal-review). The host
4153
+ * Credit ledger every payment funds and draws through. The host
4609
4154
  * passes the Postgres `CreditLedger` when a pool exists; standalone /
4610
4155
  * pool-less setups default to a per-server `MemoryCreditLedger` so the
4611
4156
  * fund+draw semantics (and receipts' credit block) hold everywhere.
4612
4157
  */
4613
4158
  creditLedger?: CreditLedgerLike;
4614
4159
  /**
4615
- * Durable x402/mpp processed-payment markers (internal-review). Defaults to the
4160
+ * Durable x402/mpp processed-payment markers. Defaults to the
4616
4161
  * in-memory store; the host passes the Postgres one alongside the ledger.
4617
4162
  */
4618
4163
  processedPayments?: ProcessedPaymentStore;
4619
4164
  /**
4620
- * Lightning credit-funding receive leg (internal-review), when the host resolved a
4165
+ * Lightning credit-funding receive leg, when the host resolved a
4621
4166
  * receive-only NWC connection. Absent ⇒ `lightning` never appears on the
4622
4167
  * funding menu and `POST /v1/credit` refuses the method.
4623
4168
  */
4624
4169
  lightningReceive?: LightningReceive;
4625
4170
  /**
4626
- * Liveness of the platform's Tempo close observer (internal-review). Absent ⇒ this
4171
+ * Liveness of the platform's Tempo close observer. Absent ⇒ this
4627
4172
  * DVM is self-hosted and answers for its own close watching, so nothing is
4628
4173
  * withheld. Present and unhealthy ⇒ `tempo/session` is withheld while
4629
4174
  * `tempo/charge` stays available.
@@ -4632,35 +4177,25 @@ interface SDKServerOpts {
4632
4177
  /** Platform-hosted operator fee-token readiness; absent outside hosted sessions. */
4633
4178
  tempoSettlementReadiness?: TempoSettlementReadiness;
4634
4179
  /**
4635
- * Tuning for the credit-expiry sweep (internal-review). Omitted, the sweep runs on
4180
+ * Tuning for the credit-expiry sweep. Omitted, the sweep runs on
4636
4181
  * its own defaults whenever the ledger supports it; tests pass `intervalMs: 0`
4637
4182
  * and `bootDelayMs: 0` to hold the clock still and drive `runOnce` by hand.
4638
4183
  */
4639
4184
  creditExpirySweep?: CreditExpirySweepOpts;
4640
4185
  }
4641
- /**
4642
- * Per-descriptor server construction — internal to the SDK after host
4643
- * consolidation (internal-review). Public callers use `createDVMHost` instead; this
4644
- * function is what the host invokes per `host.mount(dvm)`.
4645
- *
4646
- * Builds a Hono sub-app for one DVM descriptor, wires payment verification,
4647
- * auto-wires `RevenueReporter` when platform env + db + dvmId are present, and
4648
- * runs the platform-hosted partial-wiring assertion (internal-review) — gated on
4649
- * `DVMKIT_PLATFORM_URL` presence, independent of `DVMKIT_FAIL_FAST`.
4650
- */
4651
4186
  declare function createDVMServer<State, InputSchema extends ZodLike | undefined>(descriptor: DVMDescriptor<State, InputSchema>, opts: SDKServerOpts): Promise<{
4652
4187
  app: Hono<AppEnv>;
4653
4188
  authAudience?: SignedRequestAudience;
4654
4189
  shutdown: () => Promise<void>;
4655
4190
  jobManager: JobManager<State, InputSchema>;
4656
4191
  revenueReporterActive: boolean;
4657
- /** The payout reporter (internal-review), for the host to attach its Tempo readers to. */
4192
+ /** The payout reporter, for the host to attach its Tempo readers to. */
4658
4193
  payoutReporter?: PayoutReporter;
4659
4194
  }>;
4660
4195
  /**
4661
4196
  * Hono env for the SDK's DVM app. `unknownRoute` is set by
4662
4197
  * {@link unknownRouteNotFound} so the `dvm.request` span can classify a 404 as
4663
- * scanner-probe noise vs a registered route's own 404 (internal-review).
4198
+ * scanner-probe noise vs a registered route's own 404.
4664
4199
  *
4665
4200
  * Exported as a type only, for route modules split out of this file
4666
4201
  * (`credit-routes.ts`) — a type-only import erases at compile time, so it
@@ -4676,7 +4211,7 @@ interface AppEnv {
4676
4211
  /**
4677
4212
  * `notFound` handler for paths that match no registered route — scanner probes
4678
4213
  * (`GET /robots.txt`, `/.env`, …). Tags the request context so the `dvm.request`
4679
- * span downgrades the 404 to `warn`-level probe noise (internal-review) instead of
4214
+ * span downgrades the 404 to `warn`-level probe noise instead of
4680
4215
  * crowding out real errors in a `--level error` sweep, and returns a
4681
4216
  * machine-parseable JSON 404 (Hono's default is plain text). Registered on both
4682
4217
  * the DVM app (standalone `createDVMServer`) and the host app (mounted path):
@@ -4689,13 +4224,8 @@ interface AppEnv {
4689
4224
  declare function unknownRouteNotFound(c: Context): Response;
4690
4225
 
4691
4226
  /**
4692
- * The public prepaid-credit terms a DVM advertises before a caller identifies
4693
- * itself. Snake_case: this is the wire shape.
4694
- *
4695
- * This type deliberately excludes the per-caller echo. `/v1/info` is public
4696
- * and cacheable, so its type must make a caller credit ID or balance
4697
- * unrepresentable rather than relying on the current handler not to ask for
4698
- * one.
4227
+ * Public, cacheable prepaid terms exclude caller IDs and balances. toCreditTerms explicitly
4228
+ * whitelists this shape so later caller-specific menu fields cannot leak into /v1/info.
4699
4229
  */
4700
4230
  interface CreditTerms {
4701
4231
  /** Smallest funding accepted, in 1e-6 units of `currency`. */
@@ -4708,7 +4238,10 @@ interface CreditTerms {
4708
4238
  currency: string;
4709
4239
  /** Rails a top-up may be funded over. */
4710
4240
  funding: FundingMethod[];
4711
- /** MPP instruments offered or temporarily withheld for credit funding. */
4241
+ /**
4242
+ * Tempo methods are actionable. withheld names configured but temporarily unavailable
4243
+ * instruments, preserving capability discovery without advertising them for funding.
4244
+ */
4712
4245
  tempo?: {
4713
4246
  methods: {
4714
4247
  method: string;
@@ -4720,63 +4253,32 @@ interface CreditTerms {
4720
4253
  reason: string;
4721
4254
  }[];
4722
4255
  };
4723
- /** x402 flavours accepted for credit funding. */
4256
+ /**
4257
+ * Present with offered x402 funding. Reusable batch settlement is enabled by default; exact
4258
+ * requires explicit acceptance of its manual refund obligation.
4259
+ */
4724
4260
  x402?: {
4725
4261
  schemes: {
4726
4262
  scheme: string;
4727
4263
  network: string;
4728
4264
  }[];
4729
4265
  };
4730
- /** Smallest Lightning funding accepted, in 1e-6 units of `currency`. */
4266
+ /**
4267
+ * Configured Lightning receive floor in micro-units of currency, including the advertised
4268
+ * FX margin. Issuance checks the raw sats floor at current rates; arbitrary drift can still
4269
+ * make the advertised amount insufficient.
4270
+ */
4731
4271
  lightning_min_micro?: number;
4732
4272
  }
4733
4273
  /**
4734
- * The **funding menu** a DVM advertises on `/v1/quote` and in every 402
4735
- * (internal-review, credits spec §4/§6). Snake_case: this is the wire shape.
4736
- *
4737
- * `min_micro` / `max_micro` / `ttl_ms` are the DVM's sizing terms — `max_micro`
4738
- * binds the **residual balance**, not a funding amount, so a job priced above
4739
- * it still clears as fund-and-immediately-draw. `funding` is the rail list a
4740
- * top-up may arrive on — a {@link FundingMethod}, not a `PaymentMethod`, since
4741
- * `lightning` funds a credit without ever being an attached-proof wire method
4742
- * (internal-review). It degrades per spec §4: a rail whose backing wallet is
4743
- * unreachable drops out and the sale survives on the others.
4274
+ * Funding options and sizing in the wire denomination. max_micro limits residual balance,
4275
+ * not a job’s price; fund-and-draw can spend a larger payment immediately. Lightning funds
4276
+ * credit but is not an attached-proof payment method.
4744
4277
  *
4745
- * The `credit_id` / `balance_micro` / `remaining_micro` / `expiry_ms` echo
4746
- * appears only for an authenticated caller who already holds a live credit —
4747
- * so one round trip answers "what does this cost, and what do I have left?"
4748
- * (spec §7).
4278
+ * An authenticated caller’s live credit may add an echo with funded and spendable balance;
4279
+ * pending holds are included only in the funded figure.
4749
4280
  */
4750
4281
  interface CreditMenu extends CreditTerms {
4751
- /**
4752
- * MPP instruments accepted for this credit, reusable sessions first when
4753
- * available.
4754
- *
4755
- * `withheld` names an instrument this DVM is configured for and is not
4756
- * offering at this instant, with the reason (internal-review). Two audiences read
4757
- * it. An agent gets to relay *why* `tempo/session` vanished instead of
4758
- * inferring the DVM never had it. And the platform's deploy probe reads
4759
- * capability from `methods ∪ withheld`, so a deploy landing during an
4760
- * observer blip no longer stamps `tempo_session_advertised_at` NULL and
4761
- * drops the DVM out of the close observer's payee fallback permanently.
4762
- * `methods` stays the list a caller acts on — never widen it to include a
4763
- * withheld instrument.
4764
- */
4765
- /**
4766
- * Which x402 flavours a top-up may arrive on, and the chain each rides
4767
- * (internal-review). Present exactly when `funding` lists `x402`. Reusable
4768
- * `batch-settlement` is admitted by default; one-payment `exact` appears
4769
- * only when the builder explicitly accepts its manual refund obligation.
4770
- */
4771
- /**
4772
- * Smallest funding the `lightning` rail accepts, in 1e-6 units of
4773
- * `currency`. Present only when `funding` lists `lightning`. This is the
4774
- * deployment's sats-physical receive floor (internal-review) rendered into fiat
4775
- * with a ×{@link LIGHTNING_FLOOR_FX_MARGIN} margin absorbing rate drift
4776
- * between menu fetch and funding — issuance enforces the *raw* floor at
4777
- * request-time rates, so funding exactly this figure always clears. Rails
4778
- * without an entry (cashu) have no floor beyond `min_micro`.
4779
- */
4780
4282
  /** The caller's credit this echo describes. Present only with the echo. */
4781
4283
  credit_id?: string;
4782
4284
  /** Funded balance, pending holds NOT subtracted. */
@@ -4786,7 +4288,6 @@ interface CreditMenu extends CreditTerms {
4786
4288
  /** Absolute expiry of the echoed credit, ms since epoch. */
4787
4289
  expiry_ms?: number;
4788
4290
  }
4789
- /** Everything {@link buildCreditMenu} needs, assembled per request. */
4790
4291
  interface BuildCreditMenuArgs {
4791
4292
  /**
4792
4293
  * Which runtime is answering. `"isolate"` is refused unconditionally — see
@@ -4796,13 +4297,8 @@ interface BuildCreditMenuArgs {
4796
4297
  /** The DVM's resolved `credit` block; `undefined` means it didn't opt in. */
4797
4298
  config: ResolvedCreditConfig | undefined;
4798
4299
  /**
4799
- * Currency the menu — and the credit it funds — denominates in: the DVM's
4800
- * declared `DVMConfig.currency`, which every call site reads off the single
4801
- * `DVMServer.creditCurrency()` (internal-review). Deliberately *not* the currency of
4802
- * the response this menu rides on: a quote priced elsewhere is refused before
4803
- * it reaches here, so the two agree, and sourcing it from the response is
4804
- * what left `POST /v1/credit` — which has no response to read — funding in
4805
- * hardwired USD at a DVM whose jobs draw in something else.
4300
+ * Use the DVM’s declared denomination, including /v1/credit where no quote response supplies
4301
+ * it. Quote validation refuses a different denomination before menu construction.
4806
4302
  */
4807
4303
  currency: string;
4808
4304
  /** Rails this DVM settles over — the menu's funding options. */
@@ -4818,31 +4314,19 @@ interface BuildCreditMenuArgs {
4818
4314
  intent: "charge" | "session";
4819
4315
  reason: string;
4820
4316
  }[];
4821
- /**
4822
- * x402 flavours this DVM can actually accept a top-up on (internal-review) — one
4823
- * server read, so what the sub-block advertises is what `/v1/credit` will
4824
- * answer a 402 with.
4825
- */
4826
4317
  x402Schemes?: {
4827
4318
  scheme: string;
4828
4319
  network: string;
4829
4320
  }[];
4830
4321
  /**
4831
- * The deployment's Lightning receive floor in sats, when `lightning` is on
4832
- * the menu — rendered into `lightning_min_micro`. An fx failure during that
4833
- * rendering drops `lightning` from `funding` rather than suppressing the
4834
- * menu: a rail whose minimum we can't state honestly is a rail we must not
4835
- * advertise, but the other rails' sale survives.
4322
+ * Configured raw receive floor in sats, rendered with an FX margin for the menu. Failure
4323
+ * omits Lightning while allowing the rest of the menu; issuance checks the raw floor at
4324
+ * current request-time rates.
4836
4325
  */
4837
4326
  lightningFundingMinSats?: number;
4838
4327
  /**
4839
- * Whether this DVM has at least one accepted Cashu mint configured
4840
- * (internal-review). Without one, no Bitcoin funding rail may be advertised — see
4841
- * the filter in {@link buildCreditMenu}.
4842
- *
4843
- * Read off the **configured** mint list, never the health-filtered
4844
- * advertised one: a mint blip must not make a funding rail appear and
4845
- * disappear between quotes.
4328
+ * Read the configured accepted mint list, not its health-filtered advertisement. Non-channel
4329
+ * Bitcoin refunds need a mint, but configuration alone does not establish refund solvency.
4846
4330
  */
4847
4331
  hasAcceptedMint?: boolean;
4848
4332
  fxFetcher: FxFetcher;
@@ -4855,50 +4339,18 @@ interface BuildCreditMenuArgs {
4855
4339
  callerPubkey?: string;
4856
4340
  }
4857
4341
  /**
4858
- * Assemble the funding menu, or return `undefined` when this DVM must not
4859
- * advertise credit. **The refusal ladder is the feature**; each rung exists
4860
- * because advertising anyway would misadvertise:
4861
- *
4862
- * 1. **Isolate runtime** (credits spec §3) — checked first and unconditionally,
4863
- * before config is even read. Isolate DVMs have no Postgres of their own;
4864
- * their only DVM-side persistence is the last-write-wins callback KV, which
4865
- * spec §2 condition 2 prohibits for credit state. Until [internal-review] resolves
4866
- * the gate they run implicit N=1 only, and their quote simply never offers
4867
- * credit so nothing is misadvertised.
4868
- * 2. **Not opted in** — no `credit` block on `configureDVM`.
4869
- * 3. **No descriptor auth** — a credit belongs to a verified secp256k1 pubkey.
4870
- * Without auth the ledger's funder identity degrades to the requester id or
4871
- * `"anonymous"` (see `resolveLedgerContext`), which would pool every
4872
- * anonymous caller's money into one balance. Advertising a per-caller
4873
- * balance on such a DVM would be a lie at best and a disclosure at worst.
4874
- * 4. **Non-durable ledger outside devMode** — a `MemoryCreditLedger` is
4875
- * per-process, so a caller funding on machine A and drawing on machine B
4876
- * gets `credit_not_found`. Advertise only what survives the fleet.
4877
- * 5. **Bounds not expressible in `currency`** — an fx outage, or a currency the
4878
- * snapshot doesn't carry. Omit rather than quote a number we can't state.
4342
+ * Return no menu for static ineligibility or unresolvable denomination bounds. Isolates
4343
+ * currently lack durable credit storage; anonymous balances must not be advertised.
4879
4344
  *
4880
- * The balance echo is added only when a verified `callerPubkey` holds a live,
4881
- * same-currency credit.
4882
- *
4883
- * Rungs 1–4 live in {@link creditMenuConfigured}, which the capability refresh
4884
- * reads too.
4345
+ * Select the caller echo only from live, same-currency credits with spendable value.
4885
4346
  */
4886
4347
  declare function buildCreditMenu(args: BuildCreditMenuArgs): Promise<CreditMenu | undefined>;
4887
4348
  /**
4888
- * The one credit a caller's quote echoes, from the several they may hold —
4889
- * every implicit N=1 payment mints its own `imp:<rail>:<paymentId>` credit, so
4890
- * this list grows with each paid job.
4891
- *
4892
- * Live, same-currency credits with something left in them; largest spendable
4893
- * balance wins, newest breaks a tie. That is the credit a caller would in fact
4894
- * draw against next. A failed job's released hold leaves a positive implicit
4895
- * balance, which is real caller money (spec §1, no debit on failure) and is
4896
- * echoed like any other.
4349
+ * Choose the largest live, active, same-currency spendable balance; newest creation breaks
4350
+ * a tie. Released implicit payments can hold reclaimable value and are eligible too.
4897
4351
  *
4898
- * **Requires a verified pubkey.** Never call this with a requester id or
4899
- * `"anonymous"`: on an auth-less DVM the ledger pools every anonymous caller
4900
- * under one `caller_pubkey`, so echoing it would show one caller another's
4901
- * balance.
4352
+ * The caller supplies a verified pubkey. Anonymous/missing identity returns no credit; the
4353
+ * ledger’s anonymous pool must not be exposed to an individual caller.
4902
4354
  */
4903
4355
  declare function selectPrimaryCredit(args: {
4904
4356
  ledger: CreditLedgerLike;
@@ -4906,20 +4358,15 @@ declare function selectPrimaryCredit(args: {
4906
4358
  currency: string;
4907
4359
  nowMs?: number;
4908
4360
  }): Promise<CreditSnapshot | undefined>;
4909
- /** Narrow a ledger snapshot to the read-only shape a quote handler receives. */
4910
4361
  declare function toCreditView(snapshot: CreditSnapshot): CreditView;
4911
4362
  /**
4912
- * Attach a menu to a response body when there is one. Keeps the "omit the key
4913
- * entirely rather than emit `credit: null`" rule in one place — every consumer
4914
- * checks presence, and the isolate tier depends on the key never appearing.
4363
+ * Omit the credit key entirely when there is no menu; consumers use presence to determine
4364
+ * whether credit is offered.
4915
4365
  */
4916
4366
  declare function attachCreditMenu(body: Record<string, unknown>, menu: CreditMenu | undefined): Record<string, unknown>;
4917
4367
  /**
4918
- * Project a quote/402 menu onto the public, cache-safe `/v1/info` terms.
4919
- *
4920
- * This is an explicit whitelist rather than object rest syntax: a later
4921
- * caller-specific field added to {@link CreditMenu} cannot cross this boundary
4922
- * until someone deliberately adds it to {@link CreditTerms} and this copy.
4368
+ * Explicitly whitelist public /v1/info terms. A future caller-specific menu field cannot
4369
+ * cross this cacheable boundary merely by being added to CreditMenu.
4923
4370
  */
4924
4371
  declare function toCreditTerms(menu: CreditMenu): CreditTerms;
4925
4372