@dvmkit/sdk 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/{chunk-RPUFWIT4.js → chunk-5JSPTVIK.js} +52 -2
  2. package/dist/{chunk-5H7P3ORL.js → chunk-5UC7TOQC.js} +22 -0
  3. package/dist/{chunk-2VGMZ7VF.js → chunk-A2YESDQM.js} +47 -11
  4. package/dist/{chunk-WFCVS6QB.js → chunk-HULU2UVM.js} +1 -1
  5. package/dist/{chunk-S7PIFHE4.js → chunk-JFH34P3M.js} +258 -31
  6. package/dist/{chunk-HW2UCOOC.js → chunk-SXL44PL5.js} +1 -1
  7. package/dist/{chunk-POYUX2NN.js → chunk-TKJBUFYY.js} +11 -3
  8. package/dist/{content-retention-a1yRdmhI.d.ts → content-retention-BcHNpP2F.d.ts} +1 -1
  9. package/dist/{credit-ledger-MF23UKRJ.js → credit-ledger-D3UUR2GW.js} +2 -2
  10. package/dist/{credit-menu-B2T-O9oY.d.ts → credit-menu-C-kmf61K.d.ts} +57 -5
  11. package/dist/{fx-DLhscRUh.d.ts → fx-hFlF4Cn8.d.ts} +1 -1
  12. package/dist/index.d.ts +5 -5
  13. package/dist/index.js +2 -2
  14. package/dist/internal/caller.d.ts +160 -9
  15. package/dist/internal/caller.js +141 -10
  16. package/dist/internal/index.js +1 -1
  17. package/dist/internal/server.d.ts +7 -7
  18. package/dist/internal/server.js +7 -7
  19. package/dist/{job-store-B8Ijr5V6.d.ts → job-store-DVCATwhU.d.ts} +1 -1
  20. package/dist/{lightning-backend-DmwMSgaM.d.ts → lightning-backend-DXNVlVs9.d.ts} +1 -1
  21. package/dist/{memory-credit-ledger-ERD764PS.js → memory-credit-ledger-SCXQO4HZ.js} +3 -3
  22. package/dist/server/index.d.ts +17 -13
  23. package/dist/server/index.js +9 -9
  24. package/dist/{step-cache-DQqpwXa8.d.ts → step-cache-CoORQQ_Y.d.ts} +108 -11
  25. package/dist/testing/index.d.ts +3 -3
  26. package/dist/{usd-D5kk2nIT.d.ts → usd-CeXGiyTC.d.ts} +1 -1
  27. package/package.json +1 -1
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-E7ZBCT5U.js";
18
18
  import {
19
19
  MemoryCreditLedger
20
- } from "./chunk-POYUX2NN.js";
20
+ } from "./chunk-TKJBUFYY.js";
21
21
  import {
22
22
  PostgresJobStore
23
23
  } from "./chunk-TWKQFIOW.js";
@@ -28,7 +28,7 @@ import {
28
28
  humanizeZodIssues,
29
29
  signedRequestInput,
30
30
  zodIssuesOf
31
- } from "./chunk-WFCVS6QB.js";
31
+ } from "./chunk-HULU2UVM.js";
32
32
  import {
33
33
  MemoryJobStore,
34
34
  UNCAPPABLE_ASK_TOTAL,
@@ -73,7 +73,7 @@ import {
73
73
  receiptPubkeyFromSecret,
74
74
  toLockPubkey,
75
75
  verifyChallenge
76
- } from "./chunk-RPUFWIT4.js";
76
+ } from "./chunk-5JSPTVIK.js";
77
77
  import {
78
78
  X402_BATCH_SETTLEMENT_SCHEME,
79
79
  X402_DEFAULT_FACILITATOR,
@@ -97,7 +97,7 @@ import {
97
97
  RAIL_REFUNDABLE,
98
98
  canonicalRequestPath,
99
99
  isZodSchema
100
- } from "./chunk-HW2UCOOC.js";
100
+ } from "./chunk-SXL44PL5.js";
101
101
  import {
102
102
  createConsoleLogger
103
103
  } from "./chunk-7KQLPVV7.js";
@@ -133,7 +133,7 @@ import {
133
133
  isNonChannelBitcoinRail,
134
134
  netOwedSats,
135
135
  x402SettlementPending
136
- } from "./chunk-2VGMZ7VF.js";
136
+ } from "./chunk-A2YESDQM.js";
137
137
  import {
138
138
  parseStoreValue
139
139
  } from "./chunk-YG7G4DPZ.js";
@@ -142,10 +142,13 @@ import {
142
142
  } from "./chunk-COYKJRMM.js";
143
143
  import {
144
144
  computeResultHash,
145
+ isDrawFundingReceipt,
146
+ isFundingReceipt,
145
147
  signDrainReceipt,
148
+ signDrawFundingReceipt,
146
149
  signFundingReceipt,
147
150
  signReceipt
148
- } from "./chunk-5H7P3ORL.js";
151
+ } from "./chunk-5UC7TOQC.js";
149
152
  import {
150
153
  withPostgresRecoveryContext
151
154
  } from "./chunk-EP7OVWI5.js";
@@ -4971,7 +4974,13 @@ async function verifyIncomingPaymentInScope(body, opts, snapshot) {
4971
4974
  const mapped = mapLedgerCommitError(err, void 0, true);
4972
4975
  if (mapped?.error) {
4973
4976
  logCredentialRejected("x402", "mid-job", err);
4974
- return { error: { body: mapped.error.body, status: mapped.error.status } };
4977
+ return {
4978
+ error: {
4979
+ body: mapped.error.body,
4980
+ status: mapped.error.status,
4981
+ ...mapped.error.replayPaymentId && { replayPaymentId: mapped.error.replayPaymentId }
4982
+ }
4983
+ };
4975
4984
  }
4976
4985
  throw err;
4977
4986
  }
@@ -5095,7 +5104,13 @@ async function verifyIncomingPaymentInScope(body, opts, snapshot) {
5095
5104
  const mapped = mapLedgerCommitError(err, void 0, true);
5096
5105
  if (mapped?.error) {
5097
5106
  logCredentialRejected("tempo", "mid-job", err);
5098
- return { error: { body: mapped.error.body, status: mapped.error.status } };
5107
+ return {
5108
+ error: {
5109
+ body: mapped.error.body,
5110
+ status: mapped.error.status,
5111
+ ...mapped.error.replayPaymentId && { replayPaymentId: mapped.error.replayPaymentId }
5112
+ }
5113
+ };
5099
5114
  }
5100
5115
  throw err;
5101
5116
  }
@@ -5234,8 +5249,13 @@ async function acceptDurableExactIncoming(args) {
5234
5249
  drawId: replayedDrawId
5235
5250
  }) !== void 0;
5236
5251
  }
5252
+ const replayedDrawFunding = accepted.replayed && ledgerCtx?.topUp && accepted.receipt.txHash ? await ledgerCtx.ledger.getFunding({
5253
+ creditId: ledgerCtx.topUp.creditId,
5254
+ fundId: accepted.receipt.txHash
5255
+ }) : void 0;
5237
5256
  const info = withDrawBasis(
5238
5257
  {
5258
+ ...replayedDrawFunding && { drawFunding: replayedDrawFunding },
5239
5259
  paidMsats: args.snapshot.pendingPaymentMsats ?? accepted.receipt.amountMsats,
5240
5260
  paymentRail: "x402",
5241
5261
  receivedProofs: [],
@@ -5264,7 +5284,8 @@ function topUpBinding(info, snapshot) {
5264
5284
  return {
5265
5285
  ...fresh && { creditId: info.creditId, drawId: info.drawId },
5266
5286
  ...info.funding && { funding: info.funding },
5267
- ...info.unappliedMicro !== void 0 && { unappliedMicro: info.unappliedMicro }
5287
+ ...info.unappliedMicro !== void 0 && { unappliedMicro: info.unappliedMicro },
5288
+ ...info.drawFunding && { drawFunding: info.drawFunding }
5268
5289
  };
5269
5290
  }
5270
5291
  function applyPaymentInfoToJob(job, info) {
@@ -5584,7 +5605,11 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
5584
5605
  receivedProofs: [],
5585
5606
  error: {
5586
5607
  body: paymentErrorBody(result.code, { message: result.message, extra: { mints } }),
5587
- status
5608
+ status,
5609
+ // The accumulator keys replay on the request id, which is also the
5610
+ // funding id a mid-job top-up committed under, so the route can hand
5611
+ // back the original draw funding evidence beside the 409.
5612
+ ...status === 409 && requestId && { replayPaymentId: requestId }
5588
5613
  }
5589
5614
  };
5590
5615
  }
@@ -5780,7 +5805,7 @@ async function resolveTopUpLedgerContext(opts, snapshot, binding) {
5780
5805
  });
5781
5806
  return void 0;
5782
5807
  }
5783
- topUp = { creditId: snapshot.creditId, drawId: snapshot.drawId };
5808
+ topUp = { creditId: snapshot.creditId, drawId: snapshot.drawId, ledgerSeq: draw.ledgerSeq };
5784
5809
  callerId = credit.callerPubkey;
5785
5810
  currency = credit.currency;
5786
5811
  drawBasis = snapshot.paymentTxHash === drawSettlementRef(dvmId, snapshot.creditId, snapshot.drawId);
@@ -5930,7 +5955,8 @@ function withDrawBasis(info, ctx, committed) {
5930
5955
  ...committed.fundingCredit ? { fundingCredit: committed.fundingCredit } : {},
5931
5956
  ...committed.unappliedMicro !== void 0 && {
5932
5957
  unappliedMicro: committed.unappliedMicro
5933
- }
5958
+ },
5959
+ ...committed.drawFunding && { drawFunding: committed.drawFunding }
5934
5960
  };
5935
5961
  const draw = committed.draw;
5936
5962
  if (!draw) return base2;
@@ -5994,6 +6020,7 @@ async function fundAndDrawInScope(ctx, funding, tx) {
5994
6020
  const instrument = credit.rail ? storedCreditBinding(credit).instrument : void 0;
5995
6021
  const report = {
5996
6022
  ...deposit,
6023
+ expiryMs: credit.expiryMs,
5997
6024
  callerPubkey: credit.callerPubkey,
5998
6025
  ...instrument && { instrument }
5999
6026
  };
@@ -6036,6 +6063,19 @@ async function fundAndDrawInScope(ctx, funding, tx) {
6036
6063
  }
6037
6064
  if (ctx.topUp) {
6038
6065
  const { creditId: creditId2, drawId: drawId2 } = ctx.topUp;
6066
+ await ctx.ledger.recordFunding({
6067
+ creditId: creditId2,
6068
+ fundId: funding.paymentId,
6069
+ amountMicro: fundedMicro,
6070
+ rail: funding.rail,
6071
+ hostingAdmissionId: ctx.hostingAdmissionId,
6072
+ hostingJobId: ctx.jobId,
6073
+ hostingPaymentSeq: ctx.hostingPaymentSeq,
6074
+ jobId: ctx.jobId,
6075
+ drawId: drawId2,
6076
+ paymentSeq: ctx.hostingPaymentSeq,
6077
+ tx
6078
+ });
6039
6079
  const credit = await ctx.ledger.fund({
6040
6080
  creditId: creditId2,
6041
6081
  callerPubkey: ctx.callerId,
@@ -6048,10 +6088,23 @@ async function fundAndDrawInScope(ctx, funding, tx) {
6048
6088
  basis,
6049
6089
  tx
6050
6090
  });
6091
+ const drawLedgerSeq = async () => ctx.topUp?.ledgerSeq ?? (await ctx.ledger.getDraw({ creditId: creditId2, drawId: drawId2 }))?.ledgerSeq;
6092
+ const completeDrawFunding = async (balanceAfterMicro, ledgerSeq) => ledgerSeq === void 0 ? void 0 : ctx.ledger.completeFunding({
6093
+ creditId: creditId2,
6094
+ fundId: funding.paymentId,
6095
+ callerPubkey: credit.callerPubkey,
6096
+ balanceAfterMicro,
6097
+ ledgerSeq,
6098
+ tx
6099
+ });
6051
6100
  if (ctx.askSatisfied) {
6052
- return commitDeposit({ creditId: creditId2, unappliedMicro: fundedMicro }, credit);
6101
+ const drawFunding2 = await completeDrawFunding(credit.availableMicro, await drawLedgerSeq());
6102
+ return commitDeposit({ creditId: creditId2, unappliedMicro: fundedMicro, drawFunding: drawFunding2 }, credit);
6103
+ }
6104
+ if (shortMidJob) {
6105
+ const drawFunding2 = await completeDrawFunding(credit.availableMicro, await drawLedgerSeq());
6106
+ return commitDeposit({ creditId: creditId2, drawFunding: drawFunding2 }, credit);
6053
6107
  }
6054
- if (shortMidJob) return commitDeposit({ creditId: creditId2 }, credit);
6055
6108
  const grown = await ctx.ledger.growDraw({
6056
6109
  creditId: creditId2,
6057
6110
  drawId: drawId2,
@@ -6072,14 +6125,17 @@ async function fundAndDrawInScope(ctx, funding, tx) {
6072
6125
  tx
6073
6126
  });
6074
6127
  if (grown.addedAmountMicro <= 0) {
6075
- return commitDeposit({ creditId: creditId2, unappliedMicro: grown.cappedMicro }, credit);
6128
+ const drawFunding2 = await completeDrawFunding(credit.availableMicro, grown.ledgerSeq);
6129
+ return commitDeposit({ creditId: creditId2, unappliedMicro: grown.cappedMicro, drawFunding: drawFunding2 }, credit);
6076
6130
  }
6131
+ const drawFunding = await completeDrawFunding(grown.balanceAfterMicro, grown.ledgerSeq);
6077
6132
  return commitDeposit(
6078
6133
  {
6079
6134
  creditId: creditId2,
6080
6135
  draw: grown,
6081
6136
  added: { amountMicro: grown.addedAmountMicro, ...grown.addedRailValue },
6082
- ...grown.cappedMicro > 0 && { unappliedMicro: grown.cappedMicro }
6137
+ ...grown.cappedMicro > 0 && { unappliedMicro: grown.cappedMicro },
6138
+ drawFunding
6083
6139
  },
6084
6140
  credit
6085
6141
  );
@@ -6926,8 +6982,8 @@ function drainedAt(drain) {
6926
6982
  // src/sdk/server/funding-receipt.ts
6927
6983
  async function fundingReceiptFor(ledger, issuer, funding) {
6928
6984
  if (!issuer) return void 0;
6929
- if (funding.receipt) return funding.receipt;
6930
- if (funding.callerPubkey === null || funding.balanceAfterMicro === null || funding.ledgerSeq === null) {
6985
+ if (funding.receipt) return isFundingReceipt(funding.receipt) ? funding.receipt : void 0;
6986
+ if (funding.callerPubkey === null || funding.balanceAfterMicro === null || funding.ledgerSeq === null || isDrawBound(funding)) {
6931
6987
  return void 0;
6932
6988
  }
6933
6989
  const receipt = issuer.issueFunding({
@@ -6939,11 +6995,43 @@ async function fundingReceiptFor(ledger, issuer, funding) {
6939
6995
  callerPubkey: funding.callerPubkey,
6940
6996
  issuedAt: Math.floor(funding.createdAt / 1e3)
6941
6997
  });
6942
- return ledger.saveFundingReceipt({
6998
+ const saved = await ledger.saveFundingReceipt({
6943
6999
  creditId: funding.creditId,
6944
7000
  fundId: funding.fundId,
6945
7001
  receipt
6946
7002
  });
7003
+ return isFundingReceipt(saved) ? saved : void 0;
7004
+ }
7005
+ function isDrawBound(funding) {
7006
+ return Boolean(funding.jobId) || Boolean(funding.drawId);
7007
+ }
7008
+ async function drawFundingReceiptFor(ledger, issuer, funding, currency) {
7009
+ if (!issuer) return void 0;
7010
+ if (funding.receipt) return isDrawFundingReceipt(funding.receipt) ? funding.receipt : void 0;
7011
+ if (funding.callerPubkey === null || funding.balanceAfterMicro === null || funding.ledgerSeq === null || !funding.jobId || !funding.drawId) {
7012
+ return void 0;
7013
+ }
7014
+ const receipt = issuer.issueDrawFunding({
7015
+ creditId: funding.creditId,
7016
+ jobId: funding.jobId,
7017
+ drawId: funding.drawId,
7018
+ fundId: funding.fundId,
7019
+ ...funding.paymentSeq !== null && funding.paymentSeq !== void 0 && {
7020
+ paymentSeq: funding.paymentSeq
7021
+ },
7022
+ currency,
7023
+ fundedMicro: funding.amountMicro,
7024
+ balanceAfterMicro: funding.balanceAfterMicro,
7025
+ ledgerSeq: funding.ledgerSeq,
7026
+ callerPubkey: funding.callerPubkey,
7027
+ issuedAt: Math.floor(funding.createdAt / 1e3)
7028
+ });
7029
+ const saved = await ledger.saveFundingReceipt({
7030
+ creditId: funding.creditId,
7031
+ fundId: funding.fundId,
7032
+ receipt
7033
+ });
7034
+ return isDrawFundingReceipt(saved) ? saved : void 0;
6947
7035
  }
6948
7036
 
6949
7037
  // src/sdk/server/tempo-drain-leg.ts
@@ -13026,6 +13114,34 @@ var ReceiptIssuer = class {
13026
13114
  this.secret
13027
13115
  );
13028
13116
  }
13117
+ /**
13118
+ * Countersign the immutable tuple captured when a mid-job payment funded a
13119
+ * job's credit against its pending draw. `ledgerSeq` is the draw's own
13120
+ * sequence so the artifact lines up with the terminal receipt's credit block.
13121
+ */
13122
+ issueDrawFunding(args) {
13123
+ return signDrawFundingReceipt(
13124
+ {
13125
+ v: 1,
13126
+ kind: "credit_draw_funding",
13127
+ credit_id: args.creditId,
13128
+ job_id: args.jobId,
13129
+ draw_id: args.drawId,
13130
+ fund_id: args.fundId,
13131
+ ...args.paymentSeq !== void 0 && { payment_seq: args.paymentSeq },
13132
+ currency: args.currency,
13133
+ funded_micro: args.fundedMicro,
13134
+ balance_after_micro: args.balanceAfterMicro,
13135
+ ledger_seq: args.ledgerSeq,
13136
+ issued_at: args.issuedAt ?? Math.floor(Date.now() / 1e3),
13137
+ caller_pubkey: args.callerPubkey,
13138
+ dvm_id: this.dvmId,
13139
+ dvm: this.dvm,
13140
+ receipt_pubkey: this.pubkey
13141
+ },
13142
+ this.secret
13143
+ );
13144
+ }
13029
13145
  };
13030
13146
  function isReceiptableStatus(status) {
13031
13147
  return status === "completed" || status === "failed" || status === "cancelled";
@@ -15073,14 +15189,14 @@ var JobManager = class {
15073
15189
  if (hadPendingPayment2 && delta > 0 && (counters.pendingPaymentMsats === void 0 || counters.pendingPaymentMsats === 0)) {
15074
15190
  job.reactivationPaymentApplied = true;
15075
15191
  }
15076
- if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input") {
15192
+ if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input" && job.pendingPrompts.size === 0) {
15077
15193
  job.status = "processing";
15078
15194
  }
15079
15195
  } else if (delta) {
15080
15196
  job.paidMsats += delta;
15081
15197
  job.pendingPaymentMsats = void 0;
15082
15198
  if (hadPendingPayment2) job.reactivationPaymentApplied = true;
15083
- if (job.status === "awaiting-input") job.status = "processing";
15199
+ if (job.status === "awaiting-input" && job.pendingPrompts.size === 0) job.status = "processing";
15084
15200
  }
15085
15201
  return {};
15086
15202
  }
@@ -15135,10 +15251,17 @@ var JobManager = class {
15135
15251
  await this.jobStore.markInboundFailed(job.id, inboundSeq, code).catch(() => void 0);
15136
15252
  }
15137
15253
  this.reportCreditDeposit(verifyResult.error.funding);
15138
- return { error: { body: verifyResult.error.body, status: verifyResult.error.status } };
15254
+ const replayed = await this.replayedDrawFundingReceipt(job, verifyResult.error.replayPaymentId);
15255
+ return {
15256
+ error: {
15257
+ body: { ...verifyResult.error.body, ...replayed && { draw_funding_receipt: replayed } },
15258
+ status: verifyResult.error.status
15259
+ }
15260
+ };
15139
15261
  }
15140
15262
  const info = verifyResult.verified;
15141
15263
  if (!info) return {};
15264
+ const drawFundingReceipt = await this.drawFundingReceiptFor(info.drawFunding, info.funding?.currency);
15142
15265
  let unappliedCredit;
15143
15266
  const fullyUnapplied = info.unappliedMicro !== void 0 && info.funding !== void 0 && info.unappliedMicro >= info.funding.amountMicro;
15144
15267
  const hadPendingPayment = job.pendingPaymentMsats !== void 0;
@@ -15164,7 +15287,13 @@ var JobManager = class {
15164
15287
  }
15165
15288
  );
15166
15289
  } catch (err) {
15167
- return { error: await this.reportUnappliedPayment(job, inboundSeq, info, err) };
15290
+ const unapplied = await this.reportUnappliedPayment(job, inboundSeq, info, err);
15291
+ return {
15292
+ error: {
15293
+ body: { ...unapplied.body, ...drawFundingReceipt && { draw_funding_receipt: drawFundingReceipt } },
15294
+ status: unapplied.status
15295
+ }
15296
+ };
15168
15297
  }
15169
15298
  const { counters } = result;
15170
15299
  job.paidMsats = counters.paidMsats;
@@ -15208,7 +15337,7 @@ var JobManager = class {
15208
15337
  job.pendingX402AmountUsdcMicro = void 0;
15209
15338
  }
15210
15339
  }
15211
- if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input") {
15340
+ if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input" && job.pendingPrompts.size === 0) {
15212
15341
  job.status = "processing";
15213
15342
  }
15214
15343
  } else {
@@ -15234,7 +15363,81 @@ var JobManager = class {
15234
15363
  );
15235
15364
  }
15236
15365
  this.reportCreditDeposit(info.funding);
15237
- return { ...unappliedCredit && { unappliedCredit } };
15366
+ return {
15367
+ ...unappliedCredit && { unappliedCredit },
15368
+ ...drawFundingReceipt && { drawFundingReceipt }
15369
+ };
15370
+ }
15371
+ /** Issue (or reuse) the signed draw funding receipt for a committed funding row; never throws. */
15372
+ async drawFundingReceiptFor(funding, currency) {
15373
+ const ledger = this.opts.creditLedger;
15374
+ if (!funding || !ledger || currency === void 0) return void 0;
15375
+ try {
15376
+ return await drawFundingReceiptFor(ledger, this.opts.receiptIssuer, funding, currency);
15377
+ } catch (err) {
15378
+ diagnosticConsole.error({
15379
+ level: "error",
15380
+ event: "draw_funding_receipt_unavailable",
15381
+ credit_id: funding.creditId,
15382
+ fund_id: funding.fundId,
15383
+ error: errText(err)
15384
+ });
15385
+ return void 0;
15386
+ }
15387
+ }
15388
+ /**
15389
+ * Every signed draw funding receipt for a job's draw, oldest first, reissuing
15390
+ * any the acknowledgement never carried. Empty when the ledger cannot list
15391
+ * draw fundings or the job holds no draw; a lookup failure is logged and
15392
+ * answered as empty rather than failing the read.
15393
+ */
15394
+ async drawFundingReceipts(job) {
15395
+ const ledger = this.opts.creditLedger;
15396
+ if (!ledger?.listDrawFundings || !this.opts.receiptIssuer || !job.creditId || !job.drawId) {
15397
+ return [];
15398
+ }
15399
+ try {
15400
+ const fundings = (await ledger.listDrawFundings({ creditId: job.creditId, drawId: job.drawId })).filter((funding) => funding.jobId === job.id);
15401
+ if (fundings.length === 0) return [];
15402
+ const credit = await ledger.get(job.creditId);
15403
+ if (!credit) return [];
15404
+ const receipts = [];
15405
+ for (const funding of fundings) {
15406
+ const receipt = await drawFundingReceiptFor(ledger, this.opts.receiptIssuer, funding, credit.currency);
15407
+ if (receipt) receipts.push(receipt);
15408
+ }
15409
+ return receipts;
15410
+ } catch (err) {
15411
+ diagnosticConsole.error({
15412
+ level: "error",
15413
+ event: "draw_funding_receipts_unavailable",
15414
+ job_id: job.id,
15415
+ error: errText(err)
15416
+ });
15417
+ return [];
15418
+ }
15419
+ }
15420
+ /** The stored evidence for a replayed mid-job payment, looked up by the payment id the marker collided on. */
15421
+ async replayedDrawFundingReceipt(job, replayPaymentId) {
15422
+ const ledger = this.opts.creditLedger;
15423
+ if (!replayPaymentId || !ledger || !job.creditId) return void 0;
15424
+ try {
15425
+ const [credit, funding] = await Promise.all([
15426
+ ledger.get(job.creditId),
15427
+ ledger.getFunding({ creditId: job.creditId, fundId: replayPaymentId })
15428
+ ]);
15429
+ if (!credit || !funding || funding.jobId !== job.id) return void 0;
15430
+ return await drawFundingReceiptFor(ledger, this.opts.receiptIssuer, funding, credit.currency);
15431
+ } catch (err) {
15432
+ diagnosticConsole.error({
15433
+ level: "error",
15434
+ event: "draw_funding_receipt_unavailable",
15435
+ job_id: job.id,
15436
+ fund_id: replayPaymentId,
15437
+ error: errText(err)
15438
+ });
15439
+ return void 0;
15440
+ }
15238
15441
  }
15239
15442
  /**
15240
15443
  * Tx C with a bounded retry.
@@ -23261,6 +23464,7 @@ var DVMServer = class {
23261
23464
  await this.jobManager.ensureReceipt(job);
23262
23465
  const res2 = { job_id: job.id, status: job.status };
23263
23466
  Object.assign(res2, terminalResponseFields(job), await this.contentFields(job));
23467
+ Object.assign(res2, await this.drawFundingFields(job));
23264
23468
  removeUnavailableContent(res2);
23265
23469
  return c.json(res2);
23266
23470
  }
@@ -23270,9 +23474,20 @@ var DVMServer = class {
23270
23474
  await this.jobManager.ensureReceipt(record);
23271
23475
  const res = { job_id: record.id, status: record.status };
23272
23476
  Object.assign(res, terminalResponseFields(record), await this.contentFields(record));
23477
+ Object.assign(res, await this.drawFundingFields(record));
23273
23478
  removeUnavailableContent(res);
23274
23479
  return c.json(res);
23275
23480
  }
23481
+ /**
23482
+ * Recoverable mid-job funding evidence for an authenticated job read: every
23483
+ * signed draw funding receipt the ledger holds for the job's draw, reissued
23484
+ * from the committed rows when an acknowledgement was lost or the process
23485
+ * restarted. Omitted entirely when the job holds no draw or none exist.
23486
+ */
23487
+ async drawFundingFields(job) {
23488
+ const receipts = await this.jobManager.drawFundingReceipts(job);
23489
+ return receipts.length > 0 ? { draw_funding_receipts: receipts } : {};
23490
+ }
23276
23491
  async handleGetMessages(c) {
23277
23492
  if (!jobCredential.presents(c.req)) return c.json(jobCredential.body(), 401);
23278
23493
  const id = c.req.param("id") ?? "";
@@ -23469,6 +23684,7 @@ var DVMServer = class {
23469
23684
  const inboundSeq2 = await jm.recordInboundPending(activeJob, body2.type, body2.content);
23470
23685
  const lockPubkeys2 = await this.resolveLockPubkeysForReceive();
23471
23686
  let unappliedCredit2;
23687
+ let drawFundingReceipt2;
23472
23688
  if (body2.type === "payment") {
23473
23689
  const outcome = await jm.processPayment(activeJob, body2, inboundSeq2, {
23474
23690
  lockPubkeys: lockPubkeys2,
@@ -23477,6 +23693,7 @@ var DVMServer = class {
23477
23693
  if (outcome.error)
23478
23694
  return c.json(outcome.error.body, outcome.error.status);
23479
23695
  unappliedCredit2 = outcome.unappliedCredit;
23696
+ drawFundingReceipt2 = outcome.drawFundingReceipt;
23480
23697
  } else {
23481
23698
  await jm.markInboundVerified(activeJob.id, inboundSeq2);
23482
23699
  }
@@ -23485,12 +23702,16 @@ var DVMServer = class {
23485
23702
  await jm.dispatchMessage(activeJob, body2.type, body2.content);
23486
23703
  await this.persistMessageSnapshot(jm, activeJob, body2.type);
23487
23704
  } catch (error) {
23488
- const response = this.postPaymentSnapshotFailure(c, error, activeJob, body2.type, inboundSeq2, unappliedCredit2);
23705
+ const response = this.postPaymentSnapshotFailure(c, error, activeJob, body2.type, inboundSeq2, unappliedCredit2, drawFundingReceipt2);
23489
23706
  if (response) return response;
23490
23707
  throw error;
23491
23708
  }
23492
23709
  const lastMsg2 = activeJob.messages[activeJob.messages.length - 1];
23493
- return c.json({ seq: lastMsg2.seq, ...unappliedCredit2 && { credit: unappliedCredit2 } }, 201);
23710
+ return c.json({
23711
+ seq: lastMsg2.seq,
23712
+ ...unappliedCredit2 && { credit: unappliedCredit2 },
23713
+ ...drawFundingReceipt2 && { draw_funding_receipt: drawFundingReceipt2 }
23714
+ }, 201);
23494
23715
  }
23495
23716
  const record = await jm.store.get(id);
23496
23717
  if (!record) return c.json({ error: "Job not found" }, 404);
@@ -23554,6 +23775,7 @@ var DVMServer = class {
23554
23775
  const inboundSeq = await jm.recordInboundPending(job, body.type, body.content);
23555
23776
  const lockPubkeys = await this.resolveLockPubkeysForReceive();
23556
23777
  let unappliedCredit;
23778
+ let drawFundingReceipt;
23557
23779
  if (body.type === "payment") {
23558
23780
  const outcome = await jm.processPayment(job, body, inboundSeq, {
23559
23781
  lockPubkeys,
@@ -23564,10 +23786,14 @@ var DVMServer = class {
23564
23786
  return c.json(outcome.error.body, outcome.error.status);
23565
23787
  }
23566
23788
  unappliedCredit = outcome.unappliedCredit;
23789
+ drawFundingReceipt = outcome.drawFundingReceipt;
23567
23790
  } else {
23568
23791
  await jm.markInboundVerified(job.id, inboundSeq);
23569
23792
  }
23570
- const creditBlock = unappliedCredit ? { credit: unappliedCredit } : {};
23793
+ const creditBlock = {
23794
+ ...unappliedCredit && { credit: unappliedCredit },
23795
+ ...drawFundingReceipt && { draw_funding_receipt: drawFundingReceipt }
23796
+ };
23571
23797
  const elapsed = Date.now() - record.lastActivityAt;
23572
23798
  if (elapsed >= this.descriptor.idleTimeout * 1e3) {
23573
23799
  await jm.cancelJobIdle(job);
@@ -23588,7 +23814,7 @@ var DVMServer = class {
23588
23814
  await this.runReactivation(jm, job, body, record.status);
23589
23815
  }
23590
23816
  } catch (error) {
23591
- const response = this.postPaymentSnapshotFailure(c, error, job, body.type, inboundSeq, unappliedCredit);
23817
+ const response = this.postPaymentSnapshotFailure(c, error, job, body.type, inboundSeq, unappliedCredit, drawFundingReceipt);
23592
23818
  if (response) return response;
23593
23819
  throw error;
23594
23820
  }
@@ -23602,7 +23828,7 @@ var DVMServer = class {
23602
23828
  await jm.persistJob(job);
23603
23829
  }
23604
23830
  }
23605
- postPaymentSnapshotFailure(c, error, job, type, inboundSeq, unappliedCredit) {
23831
+ postPaymentSnapshotFailure(c, error, job, type, inboundSeq, unappliedCredit, drawFundingReceipt) {
23606
23832
  if (!(error instanceof JobSnapshotPersistenceError) || type !== "payment" || inboundSeq === null || !isStreamableJobStore(this.jobManager.store)) return void 0;
23607
23833
  diagnosticConsole.error({ level: "job_snapshot_unavailable", job_id: job.id, seq: inboundSeq });
23608
23834
  return c.json({
@@ -23614,7 +23840,8 @@ var DVMServer = class {
23614
23840
  payment_status: "recorded",
23615
23841
  job_id: job.id,
23616
23842
  seq: inboundSeq,
23617
- ...unappliedCredit && { credit: unappliedCredit }
23843
+ ...unappliedCredit && { credit: unappliedCredit },
23844
+ ...drawFundingReceipt && { draw_funding_receipt: drawFundingReceipt }
23618
23845
  }, 503);
23619
23846
  }
23620
23847
  /**
@@ -13,7 +13,7 @@ var RAIL_REFUNDABLE = {
13
13
  };
14
14
  var DEFAULT_CREDIT_MIN = "$0.10";
15
15
  var DEFAULT_CREDIT_MAX = "$5.00";
16
- var DEFAULT_CREDIT_TTL_SECONDS = 30 * 24 * 60 * 60;
16
+ var DEFAULT_CREDIT_TTL_SECONDS = 90 * 24 * 60 * 60;
17
17
  var DEFAULT_JOB_RETENTION_DAYS = 730;
18
18
 
19
19
  // src/lib/signed-request-statement.ts
@@ -18,7 +18,7 @@ import {
18
18
  refundAssetDelta,
19
19
  refundRequest,
20
20
  x402SettlementPending
21
- } from "./chunk-2VGMZ7VF.js";
21
+ } from "./chunk-A2YESDQM.js";
22
22
 
23
23
  // src/sdk/server/memory-credit-ledger.ts
24
24
  import { randomUUID } from "crypto";
@@ -191,7 +191,10 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
191
191
  callerPubkey: null,
192
192
  balanceAfterMicro: null,
193
193
  ledgerSeq: null,
194
- receipt: null
194
+ receipt: null,
195
+ jobId: args.jobId ?? null,
196
+ drawId: args.drawId ?? null,
197
+ paymentSeq: args.paymentSeq ?? null
195
198
  };
196
199
  this.fundings.set(key, record);
197
200
  return record;
@@ -200,6 +203,11 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
200
203
  getFunding(args) {
201
204
  return run(() => this.fundings.get(fundingKey(args.creditId, args.fundId)));
202
205
  }
206
+ listDrawFundings(args) {
207
+ return run(
208
+ () => [...this.fundings.values()].filter((funding) => funding.creditId === args.creditId && funding.drawId === args.drawId).sort((a, b) => a.createdAt - b.createdAt || a.fundId.localeCompare(b.fundId))
209
+ );
210
+ }
203
211
  completeFunding(args) {
204
212
  return run(() => {
205
213
  const key = fundingKey(args.creditId, args.fundId);
@@ -519,7 +527,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
519
527
  }
520
528
  existing.lastFundingLedgerSeq = existing.lastLedgerSeq;
521
529
  existing.balanceMicro += args.amountMicro;
522
- existing.expiryMs = args.expiryMs;
530
+ existing.expiryMs = Math.max(existing.expiryMs, args.expiryMs);
523
531
  applyBasis(existing, b);
524
532
  recordLot(existing, args.amountMicro, b, nowMs);
525
533
  return this.snapshot(existing, nowMs);
@@ -1,4 +1,4 @@
1
- import { b as JobStore, J as JobRecord } from './job-store-B8Ijr5V6.js';
1
+ import { b as JobStore, J as JobRecord } from './job-store-DVCATwhU.js';
2
2
 
3
3
  /** Authenticated caller metadata; omitted fields on older services mean unknown. */
4
4
  interface JobContentMetadata {
@@ -10,10 +10,10 @@ import {
10
10
  isDrainMethod,
11
11
  isFundingRail,
12
12
  x402SettlementPending
13
- } from "./chunk-2VGMZ7VF.js";
13
+ } from "./chunk-A2YESDQM.js";
14
14
  import "./chunk-YG7G4DPZ.js";
15
15
  import "./chunk-COYKJRMM.js";
16
- import "./chunk-5H7P3ORL.js";
16
+ import "./chunk-5UC7TOQC.js";
17
17
  import "./chunk-EP7OVWI5.js";
18
18
  import "./chunk-BPYMEMQX.js";
19
19
  import "./chunk-H3GGFEE5.js";