@dvmkit/sdk 0.14.1 → 0.14.2

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 (53) hide show
  1. package/dist/{chunk-ITRSITFP.js → chunk-2I6NXIPG.js} +1 -1
  2. package/dist/{chunk-TKJBUFYY.js → chunk-33J3NQDZ.js} +1 -1
  3. package/dist/{chunk-7KQLPVV7.js → chunk-7NZRJPEP.js} +1 -1
  4. package/dist/{chunk-Z4FOPROB.js → chunk-BAHZK334.js} +2 -0
  5. package/dist/{chunk-D4FFS3IS.js → chunk-CRH6BMMG.js} +1 -1
  6. package/dist/{chunk-LDKV4UHI.js → chunk-CVT5LTAK.js} +1 -1
  7. package/dist/{chunk-AFV77S4C.js → chunk-FL6Q3JNJ.js} +25 -0
  8. package/dist/{chunk-EP7OVWI5.js → chunk-FOXGN4KQ.js} +1 -1
  9. package/dist/{chunk-VWIMQ5H5.js → chunk-G47UY3BK.js} +1 -1
  10. package/dist/{chunk-JLT2YIBS.js → chunk-GB2N4NG5.js} +2 -2
  11. package/dist/{chunk-E7ZBCT5U.js → chunk-I2H2XYQV.js} +1 -1
  12. package/dist/{chunk-3YFFZLW7.js → chunk-IUODXRQN.js} +2 -2
  13. package/dist/{chunk-566CFXGU.js → chunk-IVW5UMD3.js} +1 -1
  14. package/dist/{chunk-A2YESDQM.js → chunk-LM6E4YHQ.js} +2 -2
  15. package/dist/{chunk-5JSPTVIK.js → chunk-LZFZGY7L.js} +1 -1
  16. package/dist/{chunk-FFRTAQUW.js → chunk-P5KQKLWL.js} +1 -1
  17. package/dist/{chunk-TWKQFIOW.js → chunk-PJAKJD2U.js} +55 -7
  18. package/dist/{chunk-M4NFYEEY.js → chunk-QRYGBKCR.js} +1 -1
  19. package/dist/{chunk-KJZVTWMX.js → chunk-R3FSW66G.js} +15 -4
  20. package/dist/{chunk-HNXMWHLU.js → chunk-R7FH7SHX.js} +1 -1
  21. package/dist/{chunk-NS2OMIU7.js → chunk-RQK7OFJO.js} +1 -1
  22. package/dist/{chunk-JFH34P3M.js → chunk-VG74TVFF.js} +265 -148
  23. package/dist/{chunk-HULU2UVM.js → chunk-ZO2ZAPG3.js} +1 -1
  24. package/dist/{content-retention-BcHNpP2F.d.ts → content-retention-DA2mlW31.d.ts} +1 -1
  25. package/dist/{credit-ledger-D3UUR2GW.js → credit-ledger-EZDZYLSK.js} +3 -3
  26. package/dist/{credit-menu-C-kmf61K.d.ts → credit-menu-BXHL3jHA.d.ts} +14 -7
  27. package/dist/{fx-hFlF4Cn8.d.ts → fx-UnO-P6Cj.d.ts} +1 -1
  28. package/dist/index.d.ts +6 -5
  29. package/dist/index.js +12 -5
  30. package/dist/internal/caller.d.ts +6 -6
  31. package/dist/internal/caller.js +7 -7
  32. package/dist/internal/server.d.ts +7 -7
  33. package/dist/internal/server.js +21 -21
  34. package/dist/{job-store-DVCATwhU.d.ts → job-store-AxjHh5Gp.d.ts} +12 -2
  35. package/dist/{lightning-backend-DXNVlVs9.d.ts → lightning-backend-C5_GKiKR.d.ts} +1 -1
  36. package/dist/{memory-credit-ledger-SCXQO4HZ.js → memory-credit-ledger-REZGHPJP.js} +4 -4
  37. package/dist/{mpp-setup-JU3MT7QX.js → mpp-setup-RG5JDMIO.js} +3 -3
  38. package/dist/{payout-reporter-NXWQVKXW.js → payout-reporter-DDQ7FGRC.js} +2 -2
  39. package/dist/{postgres-consumed-credential-store-WXJLM6J7.js → postgres-consumed-credential-store-I4WTNVQZ.js} +1 -1
  40. package/dist/{postgres-job-store-ZVMAN6L2.js → postgres-job-store-VDSDXCHR.js} +3 -3
  41. package/dist/{postgres-replay-store-QJ4FG6AR.js → postgres-replay-store-3F5FCUXN.js} +2 -2
  42. package/dist/{pricing-KCLENT2C.js → pricing-HLYEGPXD.js} +3 -3
  43. package/dist/{revenue-reporter-HD4GMXGD.js → revenue-reporter-RIEHBKRA.js} +2 -2
  44. package/dist/server/index.d.ts +11 -10
  45. package/dist/server/index.js +28 -28
  46. package/dist/{step-cache-CoORQQ_Y.d.ts → step-cache-MA3hpa7F.d.ts} +25 -8
  47. package/dist/{tempo-charge-store-7LTUAQHQ.js → tempo-charge-store-B5SYDCBK.js} +2 -2
  48. package/dist/{tempo-session-store-LYAIDR2B.js → tempo-session-store-FHW7N22I.js} +3 -3
  49. package/dist/testing/index.d.ts +3 -3
  50. package/dist/testing/index.js +7 -7
  51. package/dist/{usd-CeXGiyTC.d.ts → usd-CLGrKw9p.d.ts} +1 -1
  52. package/dist/{x402-RUJ4LUKF.js → x402-AY6P5KV4.js} +3 -3
  53. package/package.json +1 -1
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-XRLKGCAW.js";
6
6
  import {
7
7
  x402EarnedCeiling
8
- } from "./chunk-ITRSITFP.js";
8
+ } from "./chunk-2I6NXIPG.js";
9
9
  import {
10
10
  createX402FacilitatorAuthHeaders,
11
11
  createX402FacilitatorClient,
@@ -14,13 +14,13 @@ import {
14
14
  verifyWithFacilitator,
15
15
  withPaymentDiagnostics,
16
16
  withPaymentStage
17
- } from "./chunk-E7ZBCT5U.js";
17
+ } from "./chunk-I2H2XYQV.js";
18
18
  import {
19
19
  MemoryCreditLedger
20
- } from "./chunk-TKJBUFYY.js";
20
+ } from "./chunk-33J3NQDZ.js";
21
21
  import {
22
22
  PostgresJobStore
23
- } from "./chunk-TWKQFIOW.js";
23
+ } from "./chunk-PJAKJD2U.js";
24
24
  import {
25
25
  SIGNED_ENVELOPE_FIELDS,
26
26
  SignedRequestError,
@@ -28,7 +28,7 @@ import {
28
28
  humanizeZodIssues,
29
29
  signedRequestInput,
30
30
  zodIssuesOf
31
- } from "./chunk-HULU2UVM.js";
31
+ } from "./chunk-ZO2ZAPG3.js";
32
32
  import {
33
33
  MemoryJobStore,
34
34
  UNCAPPABLE_ASK_TOTAL,
@@ -37,21 +37,23 @@ import {
37
37
  isJobRetentionStore,
38
38
  isReceiptIssuingStore,
39
39
  isStreamableJobStore
40
- } from "./chunk-KJZVTWMX.js";
40
+ } from "./chunk-R3FSW66G.js";
41
41
  import {
42
42
  ContentHoldError,
43
43
  JobCancelledError,
44
44
  abortJob,
45
45
  baselineContentExpiry,
46
+ callerDeadline,
46
47
  contentMetadata,
47
48
  fromJobRecord,
49
+ hasCallerDeadline,
48
50
  isContentHoldStore,
49
51
  isTerminal,
50
52
  isYieldMessage,
51
53
  localMessage,
52
54
  providerMessage,
53
55
  toJobRecord
54
- } from "./chunk-AFV77S4C.js";
56
+ } from "./chunk-FL6Q3JNJ.js";
55
57
  import {
56
58
  ADMIN_AUTH_CLOCK_SKEW_SECONDS,
57
59
  ADMIN_AUTH_NONCE_TTL_SECONDS,
@@ -73,7 +75,7 @@ import {
73
75
  receiptPubkeyFromSecret,
74
76
  toLockPubkey,
75
77
  verifyChallenge
76
- } from "./chunk-5JSPTVIK.js";
78
+ } from "./chunk-LZFZGY7L.js";
77
79
  import {
78
80
  X402_BATCH_SETTLEMENT_SCHEME,
79
81
  X402_DEFAULT_FACILITATOR,
@@ -100,7 +102,7 @@ import {
100
102
  } from "./chunk-SXL44PL5.js";
101
103
  import {
102
104
  createConsoleLogger
103
- } from "./chunk-7KQLPVV7.js";
105
+ } from "./chunk-7NZRJPEP.js";
104
106
  import {
105
107
  StepCache
106
108
  } from "./chunk-BQJIFRAZ.js";
@@ -117,7 +119,7 @@ import {
117
119
  roundUsd,
118
120
  satsToFiat,
119
121
  validateCurrency
120
- } from "./chunk-LDKV4UHI.js";
122
+ } from "./chunk-CVT5LTAK.js";
121
123
  import {
122
124
  CreditLedger,
123
125
  CreditLedgerError,
@@ -133,7 +135,7 @@ import {
133
135
  isNonChannelBitcoinRail,
134
136
  netOwedSats,
135
137
  x402SettlementPending
136
- } from "./chunk-A2YESDQM.js";
138
+ } from "./chunk-LM6E4YHQ.js";
137
139
  import {
138
140
  parseStoreValue
139
141
  } from "./chunk-YG7G4DPZ.js";
@@ -151,7 +153,7 @@ import {
151
153
  } from "./chunk-5UC7TOQC.js";
152
154
  import {
153
155
  withPostgresRecoveryContext
154
- } from "./chunk-EP7OVWI5.js";
156
+ } from "./chunk-FOXGN4KQ.js";
155
157
  import {
156
158
  DvmError,
157
159
  x402NetworkByCaip2
@@ -169,17 +171,17 @@ import {
169
171
  installHostedTempoCloseGuard,
170
172
  isTempoInsufficientFundsError,
171
173
  resolveTempoNetwork
172
- } from "./chunk-JLT2YIBS.js";
174
+ } from "./chunk-GB2N4NG5.js";
173
175
  import {
174
176
  loggers,
175
177
  pinDimension,
176
178
  withSpan,
177
179
  withTraceContext
178
- } from "./chunk-HNXMWHLU.js";
180
+ } from "./chunk-R7FH7SHX.js";
179
181
  import {
180
182
  createDiagnosticCapabilityAuthority,
181
183
  diagnosticConsole
182
- } from "./chunk-Z4FOPROB.js";
184
+ } from "./chunk-BAHZK334.js";
183
185
 
184
186
  // src/sdk/server/lock-pubkey-store.ts
185
187
  async function initLockPubkeyTable(db) {
@@ -3892,7 +3894,7 @@ async function verifyUpfrontPaymentInScope(opts) {
3892
3894
  x402Requirements
3893
3895
  });
3894
3896
  }
3895
- const { verifyX402Payment } = await import("./x402-RUJ4LUKF.js");
3897
+ const { verifyX402Payment } = await import("./x402-AY6P5KV4.js");
3896
3898
  const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
3897
3899
  x402Payment,
3898
3900
  x402Config,
@@ -4916,7 +4918,7 @@ async function verifyIncomingPaymentInScope(body, opts, snapshot) {
4916
4918
  snapshot
4917
4919
  });
4918
4920
  }
4919
- const { verifyX402Payment } = await import("./x402-RUJ4LUKF.js");
4921
+ const { verifyX402Payment } = await import("./x402-AY6P5KV4.js");
4920
4922
  const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
4921
4923
  body.content.x402_payment,
4922
4924
  opts.x402Config,
@@ -5365,7 +5367,7 @@ async function processIncomingPaymentInScope(job, body, opts) {
5365
5367
  try {
5366
5368
  const requiredMsats = job.pendingPaymentMsats ?? 0;
5367
5369
  const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
5368
- const { verifyX402Payment } = await import("./x402-RUJ4LUKF.js");
5370
+ const { verifyX402Payment } = await import("./x402-AY6P5KV4.js");
5369
5371
  const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
5370
5372
  body.content.x402_payment,
5371
5373
  opts.x402Config,
@@ -12514,6 +12516,12 @@ function buildContext(job, opts) {
12514
12516
  log.error("Terminal handling failed", { error: String(err) });
12515
12517
  });
12516
12518
  };
12519
+ let replayPrompts;
12520
+ const captureReplayPrompts = () => {
12521
+ if (replayPrompts === void 0 && job.replayHighSeq !== null) {
12522
+ replayPrompts = job.messages.filter((m) => m.from === "provider" && m.type === "prompt" && m.seq <= job.replayHighSeq);
12523
+ }
12524
+ };
12517
12525
  let creditedToHandlerMsats = 0;
12518
12526
  let creditedToHandlerMicro = 0;
12519
12527
  let poolBasis = false;
@@ -12521,6 +12529,7 @@ function buildContext(job, opts) {
12521
12529
  let declaredCostCurrency;
12522
12530
  let declaredCostRevision = 0;
12523
12531
  let costPoisoned = false;
12532
+ const stepWrites = /* @__PURE__ */ new Map();
12524
12533
  const stepCostOwner = {};
12525
12534
  const declareCost = (declared) => {
12526
12535
  if (terminated("cost")) return;
@@ -12576,29 +12585,41 @@ function buildContext(job, opts) {
12576
12585
  },
12577
12586
  sendMessage: (type, content) => {
12578
12587
  if (terminated("sendMessage")) return;
12588
+ if (type === "payment-request" && typeof content.amount_msats === "number") job.pendingPaymentMsats = content.amount_msats;
12579
12589
  providerMessage(job, type, content);
12590
+ if ((type === "prompt" || type === "payment-request") && opts.onYield) {
12591
+ void opts.onYield().catch((err) => {
12592
+ log.error("Failed to persist event-driven wait", { error: String(err) });
12593
+ });
12594
+ }
12580
12595
  },
12581
12596
  prompt: (id, text, promptOpts) => {
12582
12597
  if (terminated("prompt")) {
12583
12598
  return Promise.reject(new JobCancelledError(`Job already ${job.status}`));
12584
12599
  }
12585
12600
  job.status = "awaiting-input";
12586
- providerMessage(job, "prompt", {
12587
- id,
12588
- text,
12589
- ...promptOpts?.options && { options: promptOpts.options },
12590
- ...promptOpts?.schema && { schema: promptOpts.schema }
12591
- });
12592
- if (job.replayHighSeq !== null) {
12601
+ captureReplayPrompts();
12602
+ const originalIndex = replayPrompts?.findIndex((m) => m.type === "prompt" && m.content.id === id) ?? -1;
12603
+ const original = originalIndex < 0 ? void 0 : replayPrompts.splice(originalIndex, 1)[0];
12604
+ if (original) {
12605
+ if (job.replayProviderSkip > 0) job.replayProviderSkip--;
12593
12606
  const response = job.messages.find(
12594
- (m) => m.type === "response" && m.from === "requester" && m.content.prompt_id === id
12607
+ (m) => m.type === "response" && m.from === "requester" && m.content.prompt_id === id && m.seq > original.seq
12595
12608
  );
12596
12609
  if (response) {
12597
- job.status = "processing";
12610
+ job.status = job.pendingPrompts.size > 0 || job.pendingPayment ? "awaiting-input" : "processing";
12611
+ opts.onCallerResume?.();
12598
12612
  return Promise.resolve(response.content);
12599
12613
  }
12614
+ } else {
12600
12615
  job.replayHighSeq = null;
12601
12616
  job.replayProviderSkip = 0;
12617
+ providerMessage(job, "prompt", {
12618
+ id,
12619
+ text,
12620
+ ...promptOpts?.options && { options: promptOpts.options },
12621
+ ...promptOpts?.schema && { schema: promptOpts.schema }
12622
+ });
12602
12623
  }
12603
12624
  const p = new Promise((resolve, reject) => {
12604
12625
  job.pendingPrompts.set(id, { resolve, reject });
@@ -12611,6 +12632,7 @@ function buildContext(job, opts) {
12611
12632
  return p;
12612
12633
  },
12613
12634
  requestPayment: async (amount, reason, payOpts) => {
12635
+ captureReplayPrompts();
12614
12636
  if (terminated("requestPayment")) {
12615
12637
  throw new JobCancelledError(`Job already ${job.status}`);
12616
12638
  }
@@ -12719,135 +12741,153 @@ function buildContext(job, opts) {
12719
12741
  poolRemainingMicro: pool.amountMicro - creditedToHandlerMicro
12720
12742
  } : { poolRemaining: job.paidMsats - creditedToHandlerMsats }
12721
12743
  });
12744
+ if (job.replayHighSeq !== null) {
12745
+ job.status = job.pendingPrompts.size > 0 ? "awaiting-input" : "processing";
12746
+ opts.onCallerResume?.();
12747
+ }
12722
12748
  advanceReplayPaymentIndex(job);
12723
12749
  return { amount_msats: amountMsats };
12724
12750
  }
12725
- const pendingFiat = await pinAsk();
12751
+ const replayedPaymentRequest = job.replayHighSeq !== null && job.replayPaymentIndex >= job.messages.filter((m) => m.from === "requester" && m.type === "payment").length ? job.messages.findLast((m) => m.from === "provider" && m.type === "payment-request" && m.seq <= job.replayHighSeq && m.seq > (job.messages.findLast((message) => message.from === "requester" && message.type === "payment")?.seq ?? -1)) : void 0;
12752
+ const originalAsk = replayedPaymentRequest?.type === "payment-request" ? replayedPaymentRequest.content : void 0;
12753
+ if (originalAsk) amountMsats = originalAsk.amount_msats;
12754
+ const pendingFiat = originalAsk ? originalAsk.credit ? { amountMicro: originalAsk.credit.amount_micro, currency: originalAsk.credit.currency } : void 0 : await pinAsk();
12726
12755
  job.pendingPaymentMsats = amountMsats;
12727
12756
  job.pendingPaymentFiatMicro = pendingFiat?.amountMicro;
12728
12757
  job.pendingPaymentFiatCurrency = pendingFiat?.currency;
12729
- const paymentRequestContent = {
12730
- amount_msats: amountMsats,
12731
- reason,
12732
- ...pendingFiat && { credit: { amount_micro: pendingFiat.amountMicro, currency: pendingFiat.currency } },
12733
- ...fiatAmount && { fiat_amount: fiatAmount },
12734
- ...payOpts?.mints && { mints: payOpts.mints },
12735
- ...payOpts?.minLocktimeSeconds && { min_locktime_seconds: payOpts.minLocktimeSeconds },
12736
- ...!payOpts?.mints && opts.mints && { mints: opts.mints }
12737
- };
12738
- let paymentRate;
12739
- let x402Nonce;
12740
- let x402AmountUsdcMicro;
12741
- const hasExplicitX402 = Boolean(payOpts?.x402);
12742
- if ((opts.pricingCurrency ?? "usd") === "usd") {
12743
- if (payOpts?.x402) {
12744
- paymentRequestContent.x402 = payOpts.x402;
12745
- } else if (opts.paymentMethods?.includes("x402") && opts.x402 && (opts.x402FacilitatorHealth?.support().available ?? true)) {
12746
- paymentRequestContent.x402 = {
12747
- pay_to: opts.x402.payTo,
12748
- network: opts.x402.network,
12749
- asset: opts.x402.asset ?? "USDC",
12750
- ...opts.x402.facilitator && { facilitator: opts.x402.facilitator }
12751
- };
12752
- }
12753
- }
12754
- if (paymentRequestContent.x402) {
12755
- paymentRate = (await resolveServerBtcUsdRate(opts.fxFetcher)).rate;
12756
- x402Nonce = `0x${randomBytes3(32).toString("hex")}`;
12757
- x402AmountUsdcMicro = msatsToUsdc(amountMsats, paymentRate).toString();
12758
- const x402Request = paymentRequestContent.x402;
12759
- const challengeConfig = {
12760
- payTo: x402Request.pay_to,
12761
- network: x402Request.network,
12762
- ...x402Request.asset ? { asset: x402Request.asset } : {},
12763
- ...x402Request.facilitator ? { facilitator: x402Request.facilitator } : {}
12758
+ let paymentRequestContent;
12759
+ if (originalAsk) {
12760
+ paymentRequestContent = { ...originalAsk };
12761
+ job.pendingX402Nonce = originalAsk.x402?.nonce;
12762
+ job.pendingX402AmountUsdcMicro = originalAsk.x402?.required_usdc_micro;
12763
+ job.pendingMppChallengeIds = originalAsk.tempo?.challenges.map((challenge) => challenge.id);
12764
+ } else {
12765
+ paymentRequestContent = {
12766
+ amount_msats: amountMsats,
12767
+ reason,
12768
+ ...pendingFiat && { credit: { amount_micro: pendingFiat.amountMicro, currency: pendingFiat.currency } },
12769
+ ...fiatAmount && { fiat_amount: fiatAmount },
12770
+ ...payOpts?.mints && { mints: payOpts.mints },
12771
+ ...payOpts?.minLocktimeSeconds && { min_locktime_seconds: payOpts.minLocktimeSeconds },
12772
+ ...!payOpts?.mints && opts.mints && { mints: opts.mints }
12764
12773
  };
12765
- Object.assign(x402Request, {
12766
- nonce: x402Nonce,
12767
- required_usdc_micro: x402AmountUsdcMicro,
12768
- ...hasExplicitX402 || (opts.x402FacilitatorHealth?.support().exact.v2 ?? true) ? {
12769
- payment_required: buildPaymentRequiredV2({
12770
- x402Config: challengeConfig,
12771
- requiredUsdcMicro: BigInt(x402AmountUsdcMicro),
12772
- resource: `/v1/job/${job.id}/messages`,
12773
- description: reason
12774
- })
12775
- } : {}
12776
- });
12777
- }
12778
- job.pendingX402Nonce = x402Nonce;
12779
- job.pendingX402AmountUsdcMicro = x402AmountUsdcMicro;
12780
- let mppChallengeIds;
12781
- if (payOpts?.mpp) {
12782
- const challenges = payOpts.mpp.challenges.filter((c) => c.intent !== "session");
12783
- if (challenges.length > 0) {
12784
- paymentRequestContent.tempo = { challenges };
12785
- mppChallengeIds = challenges.map((c) => c.id);
12774
+ let paymentRate;
12775
+ let x402Nonce;
12776
+ let x402AmountUsdcMicro;
12777
+ const hasExplicitX402 = Boolean(payOpts?.x402);
12778
+ if ((opts.pricingCurrency ?? "usd") === "usd") {
12779
+ if (payOpts?.x402) {
12780
+ paymentRequestContent.x402 = payOpts.x402;
12781
+ } else if (opts.paymentMethods?.includes("x402") && opts.x402 && (opts.x402FacilitatorHealth?.support().available ?? true)) {
12782
+ paymentRequestContent.x402 = {
12783
+ pay_to: opts.x402.payTo,
12784
+ network: opts.x402.network,
12785
+ asset: opts.x402.asset ?? "USDC",
12786
+ ...opts.x402.facilitator && { facilitator: opts.x402.facilitator }
12787
+ };
12788
+ }
12786
12789
  }
12787
- } else if (opts.paymentMethods?.includes("tempo") && opts.mpp) {
12788
- const rate = paymentRate ?? (await resolveServerBtcUsdRate(opts.fxFetcher)).rate;
12789
- const challenges = [];
12790
- for (const m of opts.mpp.methods) {
12791
- const meta = m;
12792
- if (meta.intent === "session") continue;
12793
- const amount2 = pendingFiat?.currency === "usd" ? fiatMicroToMethodAmount(pendingFiat.amountMicro, meta.name) : msatsToMethodAmount(amountMsats, meta.name, rate);
12794
- const ch = await opts.mpp.issueChallenge(meta.name, meta.intent, {
12795
- amount: amount2,
12796
- description: reason
12790
+ if (paymentRequestContent.x402) {
12791
+ paymentRate = (await resolveServerBtcUsdRate(opts.fxFetcher)).rate;
12792
+ x402Nonce = `0x${randomBytes3(32).toString("hex")}`;
12793
+ x402AmountUsdcMicro = msatsToUsdc(amountMsats, paymentRate).toString();
12794
+ const x402Request = paymentRequestContent.x402;
12795
+ const challengeConfig = {
12796
+ payTo: x402Request.pay_to,
12797
+ network: x402Request.network,
12798
+ ...x402Request.asset ? { asset: x402Request.asset } : {},
12799
+ ...x402Request.facilitator ? { facilitator: x402Request.facilitator } : {}
12800
+ };
12801
+ Object.assign(x402Request, {
12802
+ nonce: x402Nonce,
12803
+ required_usdc_micro: x402AmountUsdcMicro,
12804
+ ...hasExplicitX402 || (opts.x402FacilitatorHealth?.support().exact.v2 ?? true) ? {
12805
+ payment_required: buildPaymentRequiredV2({
12806
+ x402Config: challengeConfig,
12807
+ requiredUsdcMicro: BigInt(x402AmountUsdcMicro),
12808
+ resource: `/v1/job/${job.id}/messages`,
12809
+ description: reason
12810
+ })
12811
+ } : {}
12797
12812
  });
12798
- challenges.push(ch);
12799
12813
  }
12800
- if (challenges.length > 0) {
12801
- paymentRequestContent.tempo = { challenges };
12802
- mppChallengeIds = challenges.map((c) => c.id);
12814
+ job.pendingX402Nonce = x402Nonce;
12815
+ job.pendingX402AmountUsdcMicro = x402AmountUsdcMicro;
12816
+ let mppChallengeIds;
12817
+ if (payOpts?.mpp) {
12818
+ const challenges = payOpts.mpp.challenges.filter((c) => c.intent !== "session");
12819
+ if (challenges.length > 0) {
12820
+ paymentRequestContent.tempo = { challenges };
12821
+ mppChallengeIds = challenges.map((c) => c.id);
12822
+ }
12823
+ } else if (opts.paymentMethods?.includes("tempo") && opts.mpp) {
12824
+ const rate = paymentRate ?? (await resolveServerBtcUsdRate(opts.fxFetcher)).rate;
12825
+ const challenges = [];
12826
+ for (const m of opts.mpp.methods) {
12827
+ const meta = m;
12828
+ if (meta.intent === "session") continue;
12829
+ const amount2 = pendingFiat?.currency === "usd" ? fiatMicroToMethodAmount(pendingFiat.amountMicro, meta.name) : msatsToMethodAmount(amountMsats, meta.name, rate);
12830
+ const ch = await opts.mpp.issueChallenge(meta.name, meta.intent, {
12831
+ amount: amount2,
12832
+ description: reason
12833
+ });
12834
+ challenges.push(ch);
12835
+ }
12836
+ if (challenges.length > 0) {
12837
+ paymentRequestContent.tempo = { challenges };
12838
+ mppChallengeIds = challenges.map((c) => c.id);
12839
+ }
12803
12840
  }
12841
+ job.pendingMppChallengeIds = mppChallengeIds;
12804
12842
  }
12805
- job.pendingMppChallengeIds = mppChallengeIds;
12806
12843
  if (job.replayHighSeq !== null) {
12807
- const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester");
12844
+ const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester" && m.seq <= job.replayHighSeq);
12808
12845
  if (job.replayPaymentIndex < payments.length) {
12809
12846
  const payment = payments[job.replayPaymentIndex];
12810
12847
  job.replayPaymentIndex++;
12811
12848
  const balance = payment.content.credit;
12812
12849
  const accepted = balance ? await opts.acceptedBalancePayment?.(payment.seq, pendingFiat?.amountMicro, pendingFiat?.currency) : job.reactivationPaymentApplied === true;
12813
12850
  if (!accepted) throw new Error("[internal_error] payment replay reached without credit");
12814
- if (balance) {
12815
- job.pendingPaymentMsats = void 0;
12816
- job.pendingPaymentFiatMicro = void 0;
12817
- job.pendingPaymentFiatCurrency = void 0;
12818
- job.pendingMppChallengeIds = void 0;
12819
- job.pendingX402Nonce = void 0;
12820
- job.pendingX402AmountUsdcMicro = void 0;
12821
- }
12822
- job.status = "processing";
12851
+ job.pendingPaymentMsats = void 0;
12852
+ job.pendingPaymentFiatMicro = void 0;
12853
+ job.pendingPaymentFiatCurrency = void 0;
12854
+ job.pendingMppChallengeIds = void 0;
12855
+ job.pendingX402Nonce = void 0;
12856
+ job.pendingX402AmountUsdcMicro = void 0;
12857
+ job.status = job.pendingPrompts.size > 0 ? "awaiting-input" : "processing";
12858
+ opts.onCallerResume?.();
12823
12859
  creditedToHandlerMsats += amountMsats;
12824
12860
  creditedToHandlerMicro += pendingFiat?.amountMicro ?? 0;
12825
12861
  return payment.content;
12826
12862
  }
12827
- job.replayHighSeq = null;
12828
- job.replayProviderSkip = 0;
12863
+ if (!originalAsk) {
12864
+ job.replayHighSeq = null;
12865
+ job.replayProviderSkip = 0;
12866
+ }
12829
12867
  }
12830
- const askedTotal = accumulateAskedTopUp(
12831
- {
12832
- micro: job.askedTopUpMicro,
12833
- currency: job.askedTopUpCurrency,
12834
- reason: job.topUpCapUnenforcedReason
12835
- },
12836
- { micro: pendingFiat?.amountMicro, currency: pendingFiat?.currency }
12837
- );
12838
- job.askedTopUpMicro = askedTotal.micro;
12839
- job.askedTopUpCurrency = askedTotal.currency;
12840
- if (askedTotal.reason && !job.topUpCapUnenforcedReason) {
12841
- job.topUpCapUnenforcedReason = askedTotal.reason;
12842
- loggers.isolate.warn("dvm.topup_cap_unenforceable", {
12843
- job_id: job.id,
12844
- reason: askedTotal.reason,
12845
- ask_currency: askCurrency,
12846
- ask_msats: amountMsats
12847
- });
12868
+ if (!originalAsk) {
12869
+ const askedTotal = accumulateAskedTopUp(
12870
+ {
12871
+ micro: job.askedTopUpMicro,
12872
+ currency: job.askedTopUpCurrency,
12873
+ reason: job.topUpCapUnenforcedReason
12874
+ },
12875
+ { micro: pendingFiat?.amountMicro, currency: pendingFiat?.currency }
12876
+ );
12877
+ job.askedTopUpMicro = askedTotal.micro;
12878
+ job.askedTopUpCurrency = askedTotal.currency;
12879
+ if (askedTotal.reason && !job.topUpCapUnenforcedReason) {
12880
+ job.topUpCapUnenforcedReason = askedTotal.reason;
12881
+ loggers.isolate.warn("dvm.topup_cap_unenforceable", {
12882
+ job_id: job.id,
12883
+ reason: askedTotal.reason,
12884
+ ask_currency: askCurrency,
12885
+ ask_msats: amountMsats
12886
+ });
12887
+ }
12848
12888
  }
12849
12889
  job.status = "awaiting-input";
12850
- providerMessage(job, "payment-request", paymentRequestContent);
12890
+ if (!originalAsk) providerMessage(job, "payment-request", paymentRequestContent);
12851
12891
  const paidMsatsAtYield = job.paidMsats;
12852
12892
  const p = new Promise((resolve, reject) => {
12853
12893
  let resolved = false;
@@ -12865,6 +12905,9 @@ function buildContext(job, opts) {
12865
12905
  if (resolved) return;
12866
12906
  job.pendingPayment = null;
12867
12907
  }
12908
+ job.replayHighSeq = null;
12909
+ job.replayProviderSkip = 0;
12910
+ opts.onCallerResume?.();
12868
12911
  resolved = true;
12869
12912
  cleanup();
12870
12913
  creditedToHandlerMsats += amountMsats;
@@ -12970,6 +13013,7 @@ function buildContext(job, opts) {
12970
13013
  cost: declareCost,
12971
13014
  step: async (id, fn) => {
12972
13015
  if (job.stepCache.has(id)) {
13016
+ await stepWrites.get(id);
12973
13017
  const record = job.stepCache.getRecord(id);
12974
13018
  for (const declared of record.costs ?? []) declareCost(declared);
12975
13019
  return record.value;
@@ -12982,6 +13026,15 @@ function buildContext(job, opts) {
12982
13026
  fn
12983
13027
  );
12984
13028
  job.stepCache.set(id, result, costs);
13029
+ if (opts.onStep) {
13030
+ const persistence = opts.onStep(job.stepCache.getRecord(id));
13031
+ stepWrites.set(id, persistence);
13032
+ try {
13033
+ await persistence;
13034
+ } finally {
13035
+ stepWrites.delete(id);
13036
+ }
13037
+ }
12985
13038
  return result;
12986
13039
  },
12987
13040
  state: job.state,
@@ -13008,7 +13061,7 @@ function warnPinLost(jobId, currency, amountMsats, reason, detail) {
13008
13061
  }
13009
13062
  function advanceReplayPaymentIndex(job) {
13010
13063
  if (job.replayHighSeq === null) return;
13011
- const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester");
13064
+ const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester" && m.seq <= job.replayHighSeq);
13012
13065
  if (job.replayPaymentIndex < payments.length) {
13013
13066
  job.replayPaymentIndex++;
13014
13067
  }
@@ -13929,8 +13982,10 @@ var JobManager = class {
13929
13982
  job.pendingPrompts.delete(rc.prompt_id);
13930
13983
  job.status = job.pendingPrompts.size > 0 || job.pendingPayment ? "awaiting-input" : "processing";
13931
13984
  if (job.status === "awaiting-input") await this.persistJob(job);
13985
+ this.startIdleTimer(job);
13932
13986
  pending.resolve(rc);
13933
13987
  } else if (cap?.onResponse && ctx) {
13988
+ this.startIdleTimer(job);
13934
13989
  await cap.onResponse(ctx, rc);
13935
13990
  }
13936
13991
  } else if (type === "payment") {
@@ -13950,6 +14005,7 @@ var JobManager = class {
13950
14005
  await pp.resolve(pc);
13951
14006
  });
13952
14007
  } else if (cap?.onPayment && ctx) {
14008
+ this.startIdleTimer(job);
13953
14009
  await cap.onPayment(ctx, pc);
13954
14010
  }
13955
14011
  } else if (type === "approval" && cap?.onApproval && ctx) {
@@ -14298,9 +14354,10 @@ var JobManager = class {
14298
14354
  job.messageAppenderTail = next;
14299
14355
  };
14300
14356
  }
14301
- startIdleTimer(job) {
14357
+ startIdleTimer(job, deadline = callerDeadline(job, this.descriptor.idleTimeout * 1e3)) {
14302
14358
  this.clearIdleTimer(job.id);
14303
- const timeoutMs = this.descriptor.idleTimeout * 1e3;
14359
+ if (deadline === void 0) return;
14360
+ const timeoutMs = Math.min(2147483647, Math.max(0, deadline - Date.now()));
14304
14361
  const timer = setTimeout(() => {
14305
14362
  void this.cancelJobIdle(job).catch(() => void 0);
14306
14363
  }, timeoutMs);
@@ -14316,7 +14373,7 @@ var JobManager = class {
14316
14373
  /** Cancel a job due to idle timeout. Exposed for the reactivation path's idle-expiry guard. */
14317
14374
  async cancelJobIdle(job) {
14318
14375
  if (isTerminal(job.status)) return;
14319
- const current = await this.jobStore.get(job.id).catch(() => void 0);
14376
+ const current = await this.jobStore.get(job.id);
14320
14377
  if (current && isTerminal(current.status)) {
14321
14378
  const reason = `Job reached terminal status "${current.status}" elsewhere`;
14322
14379
  for (const [, p] of job.pendingPrompts) p.reject(new Error(reason));
@@ -14328,11 +14385,32 @@ var JobManager = class {
14328
14385
  this.scheduleCleanup(job);
14329
14386
  return;
14330
14387
  }
14388
+ const waiting = current ?? job;
14389
+ if (current && isStreamableJobStore(this.jobStore)) {
14390
+ const messages = await this.jobStore.getMessages(job.id, 0);
14391
+ if (messages.length) waiting.messages = messages;
14392
+ }
14393
+ const deadline = callerDeadline(waiting, this.descriptor.idleTimeout * 1e3);
14394
+ if (deadline === void 0) {
14395
+ this.clearIdleTimer(job.id);
14396
+ return;
14397
+ }
14398
+ if (Date.now() < deadline) {
14399
+ this.startIdleTimer(job, deadline);
14400
+ return;
14401
+ }
14402
+ if (current && isStreamableJobStore(this.jobStore)) {
14403
+ const claimed = await this.jobStore.cancelStaleJob(job.id, current.lastActivityAt, "Job timed out (idle)", "cancelled");
14404
+ if (claimed) await this.finalizeStoredTerminal(job.id);
14405
+ await this.adoptDurableTerminal(job);
14406
+ if (!claimed && !isTerminal(job.status)) this.startIdleTimer(job, Date.now() + 1);
14407
+ return;
14408
+ }
14331
14409
  for (const [, p] of job.pendingPrompts) p.reject(new Error("Job timed out (idle)"));
14332
14410
  if (job.pendingPayment) job.pendingPayment.reject(new Error("Job timed out (idle)"));
14333
14411
  job.pendingPrompts.clear();
14334
14412
  job.pendingPayment = null;
14335
- job.endedBy = (current?.status ?? job.status) === "awaiting-input" ? "caller" : "provider";
14413
+ job.endedBy = "caller";
14336
14414
  job.status = "cancelled";
14337
14415
  abortJob(job, "Job timed out (idle)");
14338
14416
  providerMessage(job, "cancel", { reason: "Job timed out (idle)" });
@@ -14345,8 +14423,11 @@ var JobManager = class {
14345
14423
  * • `awaiting-input` past `staleJobTimeoutMs` → `cancelled`
14346
14424
  * (`stale_no_terminal_status`) — the caller never paid / responded.
14347
14425
  * • `processing`/`working` past `processingWatchdogMs` → `failed`
14348
- * (`worker_died_mid_job`) — the worker died mid-job. The heartbeat keeps
14349
- * live workers fresh, so a stale row here means a dead process.
14426
+ * (`worker_died_mid_job`) — the worker died mid-job. Before claiming a
14427
+ * processing row, skip and heartbeat it if this manager still holds a live
14428
+ * `processing`/`working` job. After a process pause, the sweep may run before
14429
+ * the heartbeat timer; stale activity alone does not prove a local worker
14430
+ * died. Superseded handlers leave `_activeJobs` and receive no protection.
14350
14431
  *
14351
14432
  * Fires on a timer (wired in the constructor) and also callable on demand
14352
14433
  * (tests, ops). The CAS in `cancelStaleJob` makes this safe to run
@@ -14354,6 +14435,8 @@ var JobManager = class {
14354
14435
  * JobManager owns so multi-mount hosts don't steal stuck rows from sibling
14355
14436
  * descriptors — the local activeJobs teardown only makes sense on the
14356
14437
  * JobManager that actually hosted the zombie handler.
14438
+ * Local liveness prevents this manager's claim; the activity CAS continues
14439
+ * to protect against other machines claiming a row refreshed after find.
14357
14440
  *
14358
14441
  * **Both cutoffs float off the wall clock.** Each is one
14359
14442
  * `Date.now()` sample compared against `last_activity_at`, stamped from a
@@ -14418,10 +14501,25 @@ var JobManager = class {
14418
14501
  );
14419
14502
  let swept = 0;
14420
14503
  for (const record of stale) {
14421
- const awaiting = record.status === "awaiting-input";
14504
+ const messages = await this.jobStore.getMessages(record.id, 0);
14505
+ const history = messages.length ? messages : record.messages;
14506
+ const deadline = callerDeadline({ ...record, messages: history });
14507
+ if (deadline !== void 0 && now < deadline) continue;
14508
+ const orphanedInput = record.status === "awaiting-input" && deadline === void 0 && hasCallerDeadline(history);
14509
+ const awaiting = deadline !== void 0 || record.status === "awaiting-input" && !orphanedInput;
14422
14510
  const terminalStatus = awaiting ? "cancelled" : "failed";
14423
14511
  const reason = awaiting ? "stale_no_terminal_status" : "worker_died_mid_job";
14424
- const expectedActivityBefore = awaiting ? awaitingThreshold : processingThreshold;
14512
+ const expectedActivityBefore = awaiting && record.status === "awaiting-input" ? awaitingThreshold : processingThreshold;
14513
+ if (expectedActivityBefore < 0 || record.lastActivityAt > expectedActivityBefore) continue;
14514
+ const local = this._activeJobs.get(record.id);
14515
+ if ((record.status === "processing" || record.status === "working") && local && (local.status === "processing" || local.status === "working")) {
14516
+ await this.jobStore.heartbeatActiveJobs([record.id], Date.now());
14517
+ diagnosticConsole.warn({
14518
+ level: "stale_job_local_live_skipped",
14519
+ ageMs: monoNow - record.lastActivityAt
14520
+ });
14521
+ continue;
14522
+ }
14425
14523
  const claimed = await this.jobStore.cancelStaleJob(
14426
14524
  record.id,
14427
14525
  expectedActivityBefore,
@@ -15634,6 +15732,7 @@ var JobManager = class {
15634
15732
  */
15635
15733
  buildContextOpts(job) {
15636
15734
  const jobStore = this.jobStore;
15735
+ job.idleTimeoutMs = this.descriptor.idleTimeout * 1e3;
15637
15736
  return {
15638
15737
  mints: this.opts.mints,
15639
15738
  store: this.opts.store,
@@ -15644,7 +15743,25 @@ var JobManager = class {
15644
15743
  mpp: this.opts.mpp,
15645
15744
  fxFetcher: this.opts.fxFetcher,
15646
15745
  onTerminal: () => this.handleJobTerminal(job),
15647
- onYield: () => this.persistJob(job),
15746
+ onYield: () => {
15747
+ this.startIdleTimer(job);
15748
+ return this.persistJob(job);
15749
+ },
15750
+ onCallerResume: () => this.startIdleTimer(job),
15751
+ onStep: jobStore.saveStep ? async (record) => {
15752
+ for (let attempt = 0; ; attempt++) {
15753
+ try {
15754
+ await jobStore.saveStep(job.id, record);
15755
+ return;
15756
+ } catch {
15757
+ if (attempt >= 2) {
15758
+ diagnosticConsole.warn({ event: "step_persist_failed" });
15759
+ return;
15760
+ }
15761
+ await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
15762
+ }
15763
+ }
15764
+ } : void 0,
15648
15765
  onPaymentResume: () => retryJobSnapshot(() => this.persistJob(job)),
15649
15766
  subscribeNotifications: isStreamableJobStore(jobStore) ? (cb) => jobStore.subscribeNotifications(job.id, cb) : void 0,
15650
15767
  acceptedBalancePayment: this.opts.db && jobStore instanceof PostgresJobStore ? async (seq, amountMicro, currency) => {
@@ -20829,7 +20946,7 @@ async function createDVMServer(descriptor, opts) {
20829
20946
  }
20830
20947
  }
20831
20948
  if (!opts.onJobCompleted && platformToken && platformUrl && dvmId && opts.db) {
20832
- const { RevenueReporter } = await import("./revenue-reporter-HD4GMXGD.js");
20949
+ const { RevenueReporter } = await import("./revenue-reporter-RIEHBKRA.js");
20833
20950
  const reporter = new RevenueReporter(
20834
20951
  opts.db,
20835
20952
  platformUrl,
@@ -20839,7 +20956,7 @@ async function createDVMServer(descriptor, opts) {
20839
20956
  revenueReporter = reporter;
20840
20957
  await reporter.init();
20841
20958
  reporter.startRetryLoop();
20842
- const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-NXWQVKXW.js");
20959
+ const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-DDQ7FGRC.js");
20843
20960
  payoutReporter = new PayoutReporter({
20844
20961
  dvmId,
20845
20962
  db: opts.db,
@@ -23150,7 +23267,6 @@ var DVMServer = class {
23150
23267
  );
23151
23268
  }
23152
23269
  const sdkCtx = jm.buildAndAttachContext(job, parsedInput);
23153
- jm.startIdleTimer(job);
23154
23270
  jm.runHandler(job, sdkCtx);
23155
23271
  await new Promise((resolve) => {
23156
23272
  setTimeout(resolve, 0);
@@ -23697,7 +23813,6 @@ var DVMServer = class {
23697
23813
  } else {
23698
23814
  await jm.markInboundVerified(activeJob.id, inboundSeq2);
23699
23815
  }
23700
- jm.startIdleTimer(activeJob);
23701
23816
  try {
23702
23817
  await jm.dispatchMessage(activeJob, body2.type, body2.content);
23703
23818
  await this.persistMessageSnapshot(jm, activeJob, body2.type);
@@ -23794,11 +23909,13 @@ var DVMServer = class {
23794
23909
  ...unappliedCredit && { credit: unappliedCredit },
23795
23910
  ...drawFundingReceipt && { draw_funding_receipt: drawFundingReceipt }
23796
23911
  };
23797
- const elapsed = Date.now() - record.lastActivityAt;
23798
- if (elapsed >= this.descriptor.idleTimeout * 1e3) {
23912
+ const deadline = callerDeadline(record, this.descriptor.idleTimeout * 1e3);
23913
+ if (deadline !== void 0 && Date.now() >= deadline) {
23799
23914
  await jm.cancelJobIdle(job);
23800
- const lastMsg2 = job.messages[job.messages.length - 1];
23801
- return c.json({ seq: lastMsg2.seq, ...creditBlock }, 201);
23915
+ if (isTerminal(job.status)) {
23916
+ const lastMsg2 = job.messages[job.messages.length - 1];
23917
+ return c.json({ seq: lastMsg2.seq, ...creditBlock }, 201);
23918
+ }
23802
23919
  }
23803
23920
  jm.startIdleTimer(job);
23804
23921
  try {