@dvmkit/sdk 0.13.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.
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-E7ZBCT5U.js";
18
18
  import {
19
19
  MemoryCreditLedger
20
- } from "./chunk-EQYHHYW4.js";
20
+ } from "./chunk-TKJBUFYY.js";
21
21
  import {
22
22
  PostgresJobStore
23
23
  } from "./chunk-TWKQFIOW.js";
@@ -73,7 +73,7 @@ import {
73
73
  receiptPubkeyFromSecret,
74
74
  toLockPubkey,
75
75
  verifyChallenge
76
- } from "./chunk-V3WFX65W.js";
76
+ } from "./chunk-5JSPTVIK.js";
77
77
  import {
78
78
  X402_BATCH_SETTLEMENT_SCHEME,
79
79
  X402_DEFAULT_FACILITATOR,
@@ -133,7 +133,7 @@ import {
133
133
  isNonChannelBitcoinRail,
134
134
  netOwedSats,
135
135
  x402SettlementPending
136
- } from "./chunk-Q2X4QYOB.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;
@@ -6037,6 +6063,19 @@ async function fundAndDrawInScope(ctx, funding, tx) {
6037
6063
  }
6038
6064
  if (ctx.topUp) {
6039
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
+ });
6040
6079
  const credit = await ctx.ledger.fund({
6041
6080
  creditId: creditId2,
6042
6081
  callerPubkey: ctx.callerId,
@@ -6049,10 +6088,23 @@ async function fundAndDrawInScope(ctx, funding, tx) {
6049
6088
  basis,
6050
6089
  tx
6051
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
+ });
6052
6100
  if (ctx.askSatisfied) {
6053
- 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);
6054
6107
  }
6055
- if (shortMidJob) return commitDeposit({ creditId: creditId2 }, credit);
6056
6108
  const grown = await ctx.ledger.growDraw({
6057
6109
  creditId: creditId2,
6058
6110
  drawId: drawId2,
@@ -6073,14 +6125,17 @@ async function fundAndDrawInScope(ctx, funding, tx) {
6073
6125
  tx
6074
6126
  });
6075
6127
  if (grown.addedAmountMicro <= 0) {
6076
- 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);
6077
6130
  }
6131
+ const drawFunding = await completeDrawFunding(grown.balanceAfterMicro, grown.ledgerSeq);
6078
6132
  return commitDeposit(
6079
6133
  {
6080
6134
  creditId: creditId2,
6081
6135
  draw: grown,
6082
6136
  added: { amountMicro: grown.addedAmountMicro, ...grown.addedRailValue },
6083
- ...grown.cappedMicro > 0 && { unappliedMicro: grown.cappedMicro }
6137
+ ...grown.cappedMicro > 0 && { unappliedMicro: grown.cappedMicro },
6138
+ drawFunding
6084
6139
  },
6085
6140
  credit
6086
6141
  );
@@ -6927,8 +6982,8 @@ function drainedAt(drain) {
6927
6982
  // src/sdk/server/funding-receipt.ts
6928
6983
  async function fundingReceiptFor(ledger, issuer, funding) {
6929
6984
  if (!issuer) return void 0;
6930
- if (funding.receipt) return funding.receipt;
6931
- 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)) {
6932
6987
  return void 0;
6933
6988
  }
6934
6989
  const receipt = issuer.issueFunding({
@@ -6940,11 +6995,43 @@ async function fundingReceiptFor(ledger, issuer, funding) {
6940
6995
  callerPubkey: funding.callerPubkey,
6941
6996
  issuedAt: Math.floor(funding.createdAt / 1e3)
6942
6997
  });
6943
- return ledger.saveFundingReceipt({
6998
+ const saved = await ledger.saveFundingReceipt({
6944
6999
  creditId: funding.creditId,
6945
7000
  fundId: funding.fundId,
6946
7001
  receipt
6947
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;
6948
7035
  }
6949
7036
 
6950
7037
  // src/sdk/server/tempo-drain-leg.ts
@@ -13027,6 +13114,34 @@ var ReceiptIssuer = class {
13027
13114
  this.secret
13028
13115
  );
13029
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
+ }
13030
13145
  };
13031
13146
  function isReceiptableStatus(status) {
13032
13147
  return status === "completed" || status === "failed" || status === "cancelled";
@@ -15074,14 +15189,14 @@ var JobManager = class {
15074
15189
  if (hadPendingPayment2 && delta > 0 && (counters.pendingPaymentMsats === void 0 || counters.pendingPaymentMsats === 0)) {
15075
15190
  job.reactivationPaymentApplied = true;
15076
15191
  }
15077
- if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input") {
15192
+ if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input" && job.pendingPrompts.size === 0) {
15078
15193
  job.status = "processing";
15079
15194
  }
15080
15195
  } else if (delta) {
15081
15196
  job.paidMsats += delta;
15082
15197
  job.pendingPaymentMsats = void 0;
15083
15198
  if (hadPendingPayment2) job.reactivationPaymentApplied = true;
15084
- if (job.status === "awaiting-input") job.status = "processing";
15199
+ if (job.status === "awaiting-input" && job.pendingPrompts.size === 0) job.status = "processing";
15085
15200
  }
15086
15201
  return {};
15087
15202
  }
@@ -15136,10 +15251,17 @@ var JobManager = class {
15136
15251
  await this.jobStore.markInboundFailed(job.id, inboundSeq, code).catch(() => void 0);
15137
15252
  }
15138
15253
  this.reportCreditDeposit(verifyResult.error.funding);
15139
- 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
+ };
15140
15261
  }
15141
15262
  const info = verifyResult.verified;
15142
15263
  if (!info) return {};
15264
+ const drawFundingReceipt = await this.drawFundingReceiptFor(info.drawFunding, info.funding?.currency);
15143
15265
  let unappliedCredit;
15144
15266
  const fullyUnapplied = info.unappliedMicro !== void 0 && info.funding !== void 0 && info.unappliedMicro >= info.funding.amountMicro;
15145
15267
  const hadPendingPayment = job.pendingPaymentMsats !== void 0;
@@ -15165,7 +15287,13 @@ var JobManager = class {
15165
15287
  }
15166
15288
  );
15167
15289
  } catch (err) {
15168
- 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
+ };
15169
15297
  }
15170
15298
  const { counters } = result;
15171
15299
  job.paidMsats = counters.paidMsats;
@@ -15209,7 +15337,7 @@ var JobManager = class {
15209
15337
  job.pendingX402AmountUsdcMicro = void 0;
15210
15338
  }
15211
15339
  }
15212
- if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input") {
15340
+ if (counters.pendingPaymentMsats === void 0 && job.status === "awaiting-input" && job.pendingPrompts.size === 0) {
15213
15341
  job.status = "processing";
15214
15342
  }
15215
15343
  } else {
@@ -15235,7 +15363,81 @@ var JobManager = class {
15235
15363
  );
15236
15364
  }
15237
15365
  this.reportCreditDeposit(info.funding);
15238
- 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
+ }
15239
15441
  }
15240
15442
  /**
15241
15443
  * Tx C with a bounded retry.
@@ -23262,6 +23464,7 @@ var DVMServer = class {
23262
23464
  await this.jobManager.ensureReceipt(job);
23263
23465
  const res2 = { job_id: job.id, status: job.status };
23264
23466
  Object.assign(res2, terminalResponseFields(job), await this.contentFields(job));
23467
+ Object.assign(res2, await this.drawFundingFields(job));
23265
23468
  removeUnavailableContent(res2);
23266
23469
  return c.json(res2);
23267
23470
  }
@@ -23271,9 +23474,20 @@ var DVMServer = class {
23271
23474
  await this.jobManager.ensureReceipt(record);
23272
23475
  const res = { job_id: record.id, status: record.status };
23273
23476
  Object.assign(res, terminalResponseFields(record), await this.contentFields(record));
23477
+ Object.assign(res, await this.drawFundingFields(record));
23274
23478
  removeUnavailableContent(res);
23275
23479
  return c.json(res);
23276
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
+ }
23277
23491
  async handleGetMessages(c) {
23278
23492
  if (!jobCredential.presents(c.req)) return c.json(jobCredential.body(), 401);
23279
23493
  const id = c.req.param("id") ?? "";
@@ -23470,6 +23684,7 @@ var DVMServer = class {
23470
23684
  const inboundSeq2 = await jm.recordInboundPending(activeJob, body2.type, body2.content);
23471
23685
  const lockPubkeys2 = await this.resolveLockPubkeysForReceive();
23472
23686
  let unappliedCredit2;
23687
+ let drawFundingReceipt2;
23473
23688
  if (body2.type === "payment") {
23474
23689
  const outcome = await jm.processPayment(activeJob, body2, inboundSeq2, {
23475
23690
  lockPubkeys: lockPubkeys2,
@@ -23478,6 +23693,7 @@ var DVMServer = class {
23478
23693
  if (outcome.error)
23479
23694
  return c.json(outcome.error.body, outcome.error.status);
23480
23695
  unappliedCredit2 = outcome.unappliedCredit;
23696
+ drawFundingReceipt2 = outcome.drawFundingReceipt;
23481
23697
  } else {
23482
23698
  await jm.markInboundVerified(activeJob.id, inboundSeq2);
23483
23699
  }
@@ -23486,12 +23702,16 @@ var DVMServer = class {
23486
23702
  await jm.dispatchMessage(activeJob, body2.type, body2.content);
23487
23703
  await this.persistMessageSnapshot(jm, activeJob, body2.type);
23488
23704
  } catch (error) {
23489
- const response = this.postPaymentSnapshotFailure(c, error, activeJob, body2.type, inboundSeq2, unappliedCredit2);
23705
+ const response = this.postPaymentSnapshotFailure(c, error, activeJob, body2.type, inboundSeq2, unappliedCredit2, drawFundingReceipt2);
23490
23706
  if (response) return response;
23491
23707
  throw error;
23492
23708
  }
23493
23709
  const lastMsg2 = activeJob.messages[activeJob.messages.length - 1];
23494
- 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);
23495
23715
  }
23496
23716
  const record = await jm.store.get(id);
23497
23717
  if (!record) return c.json({ error: "Job not found" }, 404);
@@ -23555,6 +23775,7 @@ var DVMServer = class {
23555
23775
  const inboundSeq = await jm.recordInboundPending(job, body.type, body.content);
23556
23776
  const lockPubkeys = await this.resolveLockPubkeysForReceive();
23557
23777
  let unappliedCredit;
23778
+ let drawFundingReceipt;
23558
23779
  if (body.type === "payment") {
23559
23780
  const outcome = await jm.processPayment(job, body, inboundSeq, {
23560
23781
  lockPubkeys,
@@ -23565,10 +23786,14 @@ var DVMServer = class {
23565
23786
  return c.json(outcome.error.body, outcome.error.status);
23566
23787
  }
23567
23788
  unappliedCredit = outcome.unappliedCredit;
23789
+ drawFundingReceipt = outcome.drawFundingReceipt;
23568
23790
  } else {
23569
23791
  await jm.markInboundVerified(job.id, inboundSeq);
23570
23792
  }
23571
- const creditBlock = unappliedCredit ? { credit: unappliedCredit } : {};
23793
+ const creditBlock = {
23794
+ ...unappliedCredit && { credit: unappliedCredit },
23795
+ ...drawFundingReceipt && { draw_funding_receipt: drawFundingReceipt }
23796
+ };
23572
23797
  const elapsed = Date.now() - record.lastActivityAt;
23573
23798
  if (elapsed >= this.descriptor.idleTimeout * 1e3) {
23574
23799
  await jm.cancelJobIdle(job);
@@ -23589,7 +23814,7 @@ var DVMServer = class {
23589
23814
  await this.runReactivation(jm, job, body, record.status);
23590
23815
  }
23591
23816
  } catch (error) {
23592
- const response = this.postPaymentSnapshotFailure(c, error, job, body.type, inboundSeq, unappliedCredit);
23817
+ const response = this.postPaymentSnapshotFailure(c, error, job, body.type, inboundSeq, unappliedCredit, drawFundingReceipt);
23593
23818
  if (response) return response;
23594
23819
  throw error;
23595
23820
  }
@@ -23603,7 +23828,7 @@ var DVMServer = class {
23603
23828
  await jm.persistJob(job);
23604
23829
  }
23605
23830
  }
23606
- postPaymentSnapshotFailure(c, error, job, type, inboundSeq, unappliedCredit) {
23831
+ postPaymentSnapshotFailure(c, error, job, type, inboundSeq, unappliedCredit, drawFundingReceipt) {
23607
23832
  if (!(error instanceof JobSnapshotPersistenceError) || type !== "payment" || inboundSeq === null || !isStreamableJobStore(this.jobManager.store)) return void 0;
23608
23833
  diagnosticConsole.error({ level: "job_snapshot_unavailable", job_id: job.id, seq: inboundSeq });
23609
23834
  return c.json({
@@ -23615,7 +23840,8 @@ var DVMServer = class {
23615
23840
  payment_status: "recorded",
23616
23841
  job_id: job.id,
23617
23842
  seq: inboundSeq,
23618
- ...unappliedCredit && { credit: unappliedCredit }
23843
+ ...unappliedCredit && { credit: unappliedCredit },
23844
+ ...drawFundingReceipt && { draw_funding_receipt: drawFundingReceipt }
23619
23845
  }, 503);
23620
23846
  }
23621
23847
  /**
@@ -18,7 +18,7 @@ import {
18
18
  refundAssetDelta,
19
19
  refundRequest,
20
20
  x402SettlementPending
21
- } from "./chunk-Q2X4QYOB.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);
@@ -1,4 +1,4 @@
1
- import { b as JobStore, J as JobRecord } from './job-store-BVGvAaBG.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-Q2X4QYOB.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";