@dvmkit/sdk 0.14.1 → 0.15.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.
- package/dist/{chunk-ITRSITFP.js → chunk-2I6NXIPG.js} +1 -1
- package/dist/{chunk-TKJBUFYY.js → chunk-33J3NQDZ.js} +1 -1
- package/dist/{chunk-7KQLPVV7.js → chunk-7NZRJPEP.js} +1 -1
- package/dist/{chunk-Z4FOPROB.js → chunk-BAHZK334.js} +2 -0
- package/dist/{chunk-D4FFS3IS.js → chunk-CRH6BMMG.js} +1 -1
- package/dist/{chunk-LDKV4UHI.js → chunk-CVT5LTAK.js} +1 -1
- package/dist/{chunk-EP7OVWI5.js → chunk-FOXGN4KQ.js} +1 -1
- package/dist/{chunk-VWIMQ5H5.js → chunk-G47UY3BK.js} +1 -1
- package/dist/{chunk-JLT2YIBS.js → chunk-GB2N4NG5.js} +2 -2
- package/dist/{chunk-E7ZBCT5U.js → chunk-I2H2XYQV.js} +1 -1
- package/dist/{chunk-3YFFZLW7.js → chunk-IUODXRQN.js} +2 -2
- package/dist/{chunk-566CFXGU.js → chunk-IVW5UMD3.js} +1 -1
- package/dist/{chunk-AFV77S4C.js → chunk-KVKJ7R5V.js} +81 -2
- package/dist/{chunk-A2YESDQM.js → chunk-LM6E4YHQ.js} +2 -2
- package/dist/{chunk-5JSPTVIK.js → chunk-LZFZGY7L.js} +1 -1
- package/dist/{chunk-FFRTAQUW.js → chunk-P5KQKLWL.js} +1 -1
- package/dist/{chunk-KJZVTWMX.js → chunk-PKLNMMKF.js} +52 -15
- package/dist/{chunk-M4NFYEEY.js → chunk-QRYGBKCR.js} +1 -1
- package/dist/{chunk-HNXMWHLU.js → chunk-R7FH7SHX.js} +1 -1
- package/dist/{chunk-NS2OMIU7.js → chunk-RQK7OFJO.js} +1 -1
- package/dist/{chunk-TWKQFIOW.js → chunk-YMMCRQ2H.js} +84 -14
- package/dist/{chunk-JFH34P3M.js → chunk-ZJRGORJ3.js} +359 -169
- package/dist/{chunk-HULU2UVM.js → chunk-ZO2ZAPG3.js} +1 -1
- package/dist/{content-retention-BcHNpP2F.d.ts → content-retention-BASsjgKw.d.ts} +1 -1
- package/dist/{credit-ledger-D3UUR2GW.js → credit-ledger-EZDZYLSK.js} +3 -3
- package/dist/{credit-menu-C-kmf61K.d.ts → credit-menu-B239nIY5.d.ts} +23 -9
- package/dist/{fx-hFlF4Cn8.d.ts → fx-BmvWGISH.d.ts} +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +12 -5
- package/dist/internal/caller.d.ts +11 -7
- package/dist/internal/caller.js +7 -7
- package/dist/internal/server.d.ts +7 -7
- package/dist/internal/server.js +21 -21
- package/dist/{job-store-DVCATwhU.d.ts → job-store-CA7u1nHV.d.ts} +22 -4
- package/dist/{lightning-backend-DXNVlVs9.d.ts → lightning-backend-BOcopzXk.d.ts} +1 -1
- package/dist/{memory-credit-ledger-SCXQO4HZ.js → memory-credit-ledger-REZGHPJP.js} +4 -4
- package/dist/{mpp-setup-JU3MT7QX.js → mpp-setup-RG5JDMIO.js} +3 -3
- package/dist/{payout-reporter-NXWQVKXW.js → payout-reporter-DDQ7FGRC.js} +2 -2
- package/dist/{postgres-consumed-credential-store-WXJLM6J7.js → postgres-consumed-credential-store-I4WTNVQZ.js} +1 -1
- package/dist/{postgres-job-store-ZVMAN6L2.js → postgres-job-store-IXTNWT4K.js} +3 -3
- package/dist/{postgres-replay-store-QJ4FG6AR.js → postgres-replay-store-3F5FCUXN.js} +2 -2
- package/dist/{pricing-KCLENT2C.js → pricing-HLYEGPXD.js} +3 -3
- package/dist/{revenue-reporter-HD4GMXGD.js → revenue-reporter-RIEHBKRA.js} +2 -2
- package/dist/server/index.d.ts +15 -12
- package/dist/server/index.js +28 -28
- package/dist/{step-cache-CoORQQ_Y.d.ts → step-cache-BCRHimiY.d.ts} +48 -9
- package/dist/{tempo-charge-store-7LTUAQHQ.js → tempo-charge-store-B5SYDCBK.js} +2 -2
- package/dist/{tempo-session-store-LYAIDR2B.js → tempo-session-store-FHW7N22I.js} +3 -3
- package/dist/testing/index.d.ts +7 -4
- package/dist/testing/index.js +7 -7
- package/dist/{usd-CeXGiyTC.d.ts → usd-B8_WMfHT.d.ts} +1 -1
- package/dist/{x402-RUJ4LUKF.js → x402-AY6P5KV4.js} +3 -3
- 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-
|
|
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-
|
|
17
|
+
} from "./chunk-I2H2XYQV.js";
|
|
18
18
|
import {
|
|
19
19
|
MemoryCreditLedger
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-33J3NQDZ.js";
|
|
21
21
|
import {
|
|
22
22
|
PostgresJobStore
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-YMMCRQ2H.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-
|
|
31
|
+
} from "./chunk-ZO2ZAPG3.js";
|
|
32
32
|
import {
|
|
33
33
|
MemoryJobStore,
|
|
34
34
|
UNCAPPABLE_ASK_TOTAL,
|
|
@@ -37,21 +37,26 @@ import {
|
|
|
37
37
|
isJobRetentionStore,
|
|
38
38
|
isReceiptIssuingStore,
|
|
39
39
|
isStreamableJobStore
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-PKLNMMKF.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,
|
|
50
|
+
holdJobHistory,
|
|
48
51
|
isContentHoldStore,
|
|
49
52
|
isTerminal,
|
|
50
53
|
isYieldMessage,
|
|
51
54
|
localMessage,
|
|
52
55
|
providerMessage,
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
releaseJobHistory,
|
|
57
|
+
toJobRecord,
|
|
58
|
+
visibleJobMessages
|
|
59
|
+
} from "./chunk-KVKJ7R5V.js";
|
|
55
60
|
import {
|
|
56
61
|
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
57
62
|
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
@@ -73,7 +78,7 @@ import {
|
|
|
73
78
|
receiptPubkeyFromSecret,
|
|
74
79
|
toLockPubkey,
|
|
75
80
|
verifyChallenge
|
|
76
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-LZFZGY7L.js";
|
|
77
82
|
import {
|
|
78
83
|
X402_BATCH_SETTLEMENT_SCHEME,
|
|
79
84
|
X402_DEFAULT_FACILITATOR,
|
|
@@ -100,7 +105,7 @@ import {
|
|
|
100
105
|
} from "./chunk-SXL44PL5.js";
|
|
101
106
|
import {
|
|
102
107
|
createConsoleLogger
|
|
103
|
-
} from "./chunk-
|
|
108
|
+
} from "./chunk-7NZRJPEP.js";
|
|
104
109
|
import {
|
|
105
110
|
StepCache
|
|
106
111
|
} from "./chunk-BQJIFRAZ.js";
|
|
@@ -117,7 +122,7 @@ import {
|
|
|
117
122
|
roundUsd,
|
|
118
123
|
satsToFiat,
|
|
119
124
|
validateCurrency
|
|
120
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-CVT5LTAK.js";
|
|
121
126
|
import {
|
|
122
127
|
CreditLedger,
|
|
123
128
|
CreditLedgerError,
|
|
@@ -133,7 +138,7 @@ import {
|
|
|
133
138
|
isNonChannelBitcoinRail,
|
|
134
139
|
netOwedSats,
|
|
135
140
|
x402SettlementPending
|
|
136
|
-
} from "./chunk-
|
|
141
|
+
} from "./chunk-LM6E4YHQ.js";
|
|
137
142
|
import {
|
|
138
143
|
parseStoreValue
|
|
139
144
|
} from "./chunk-YG7G4DPZ.js";
|
|
@@ -151,7 +156,7 @@ import {
|
|
|
151
156
|
} from "./chunk-5UC7TOQC.js";
|
|
152
157
|
import {
|
|
153
158
|
withPostgresRecoveryContext
|
|
154
|
-
} from "./chunk-
|
|
159
|
+
} from "./chunk-FOXGN4KQ.js";
|
|
155
160
|
import {
|
|
156
161
|
DvmError,
|
|
157
162
|
x402NetworkByCaip2
|
|
@@ -169,17 +174,17 @@ import {
|
|
|
169
174
|
installHostedTempoCloseGuard,
|
|
170
175
|
isTempoInsufficientFundsError,
|
|
171
176
|
resolveTempoNetwork
|
|
172
|
-
} from "./chunk-
|
|
177
|
+
} from "./chunk-GB2N4NG5.js";
|
|
173
178
|
import {
|
|
174
179
|
loggers,
|
|
175
180
|
pinDimension,
|
|
176
181
|
withSpan,
|
|
177
182
|
withTraceContext
|
|
178
|
-
} from "./chunk-
|
|
183
|
+
} from "./chunk-R7FH7SHX.js";
|
|
179
184
|
import {
|
|
180
185
|
createDiagnosticCapabilityAuthority,
|
|
181
186
|
diagnosticConsole
|
|
182
|
-
} from "./chunk-
|
|
187
|
+
} from "./chunk-BAHZK334.js";
|
|
183
188
|
|
|
184
189
|
// src/sdk/server/lock-pubkey-store.ts
|
|
185
190
|
async function initLockPubkeyTable(db) {
|
|
@@ -3181,6 +3186,26 @@ function isRecord(value) {
|
|
|
3181
3186
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3182
3187
|
}
|
|
3183
3188
|
|
|
3189
|
+
// src/sdk/server/upfront-payment.ts
|
|
3190
|
+
function upfrontPaymentFromDraw(draw) {
|
|
3191
|
+
const value = draw.railValue;
|
|
3192
|
+
const rail = value.rail;
|
|
3193
|
+
return {
|
|
3194
|
+
amount_msats: value.drawMsats,
|
|
3195
|
+
...value.creditCurrency !== null && {
|
|
3196
|
+
amount_micro: draw.amountMicro,
|
|
3197
|
+
currency: value.creditCurrency
|
|
3198
|
+
},
|
|
3199
|
+
...rail === "cashu" || rail === "x402" || rail === "tempo" || rail === "lightning" ? { rail } : {},
|
|
3200
|
+
...value.drawNative !== null && value.nativeAsset !== null && {
|
|
3201
|
+
native_amount: value.drawNative,
|
|
3202
|
+
native_asset: value.nativeAsset
|
|
3203
|
+
},
|
|
3204
|
+
credit_id: draw.creditId,
|
|
3205
|
+
draw_id: draw.drawId
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3184
3209
|
// src/sdk/server/payment-diagnostics.ts
|
|
3185
3210
|
import { Errors } from "mppx";
|
|
3186
3211
|
function paymentFailureFields(error) {
|
|
@@ -3560,7 +3585,18 @@ async function issueUpfrontChallenges(mpp, requiredMsats, resourcePath, btcUsdRa
|
|
|
3560
3585
|
);
|
|
3561
3586
|
}
|
|
3562
3587
|
async function verifyUpfrontPayment(opts) {
|
|
3563
|
-
|
|
3588
|
+
const result = await withPaymentDiagnostics(() => verifyUpfrontPaymentInScope(opts));
|
|
3589
|
+
if (!result.error && !result.replayedDraw && !result.upfrontPayment && !opts.fundOnly) {
|
|
3590
|
+
result.upfrontPayment = {
|
|
3591
|
+
amount_msats: result.paidMsats,
|
|
3592
|
+
...result.paymentRail && { rail: result.paymentRail },
|
|
3593
|
+
...result.nativeAmount !== void 0 && result.nativeAsset !== void 0 && {
|
|
3594
|
+
native_amount: result.nativeAmount,
|
|
3595
|
+
native_asset: result.nativeAsset
|
|
3596
|
+
}
|
|
3597
|
+
};
|
|
3598
|
+
}
|
|
3599
|
+
return result;
|
|
3564
3600
|
}
|
|
3565
3601
|
async function verifyUpfrontPaymentInScope(opts) {
|
|
3566
3602
|
const {
|
|
@@ -3892,7 +3928,7 @@ async function verifyUpfrontPaymentInScope(opts) {
|
|
|
3892
3928
|
x402Requirements
|
|
3893
3929
|
});
|
|
3894
3930
|
}
|
|
3895
|
-
const { verifyX402Payment } = await import("./x402-
|
|
3931
|
+
const { verifyX402Payment } = await import("./x402-AY6P5KV4.js");
|
|
3896
3932
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
3897
3933
|
x402Payment,
|
|
3898
3934
|
x402Config,
|
|
@@ -4328,7 +4364,8 @@ async function verifyUpfrontPaymentInScope(opts) {
|
|
|
4328
4364
|
creditId: draw.creditId,
|
|
4329
4365
|
drawId: draw.drawId,
|
|
4330
4366
|
drawAmountMicro: draw.amountMicro,
|
|
4331
|
-
creditCurrency: ledgerCtx.priceFiat.currency
|
|
4367
|
+
creditCurrency: ledgerCtx.priceFiat.currency,
|
|
4368
|
+
upfrontPayment: upfrontPaymentFromDraw(draw)
|
|
4332
4369
|
};
|
|
4333
4370
|
} catch (err) {
|
|
4334
4371
|
if (err instanceof CreditLedgerError) return creditLedgerErrorResult(err, x402Requirements);
|
|
@@ -4916,7 +4953,7 @@ async function verifyIncomingPaymentInScope(body, opts, snapshot) {
|
|
|
4916
4953
|
snapshot
|
|
4917
4954
|
});
|
|
4918
4955
|
}
|
|
4919
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4956
|
+
const { verifyX402Payment } = await import("./x402-AY6P5KV4.js");
|
|
4920
4957
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
4921
4958
|
body.content.x402_payment,
|
|
4922
4959
|
opts.x402Config,
|
|
@@ -5155,7 +5192,9 @@ async function verifyIncomingPaymentInScope(body, opts, snapshot) {
|
|
|
5155
5192
|
};
|
|
5156
5193
|
}
|
|
5157
5194
|
}
|
|
5158
|
-
return {
|
|
5195
|
+
return { error: { status: 402, body: paymentErrorBody("payment_invalid", {
|
|
5196
|
+
message: "Payment method is not configured"
|
|
5197
|
+
}) } };
|
|
5159
5198
|
}
|
|
5160
5199
|
function midJobFundedMsats(snapshot, railMsats) {
|
|
5161
5200
|
const pending = snapshot.pendingPaymentMsats ?? 0;
|
|
@@ -5365,7 +5404,7 @@ async function processIncomingPaymentInScope(job, body, opts) {
|
|
|
5365
5404
|
try {
|
|
5366
5405
|
const requiredMsats = job.pendingPaymentMsats ?? 0;
|
|
5367
5406
|
const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
|
|
5368
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5407
|
+
const { verifyX402Payment } = await import("./x402-AY6P5KV4.js");
|
|
5369
5408
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
5370
5409
|
body.content.x402_payment,
|
|
5371
5410
|
opts.x402Config,
|
|
@@ -5964,7 +6003,8 @@ function withDrawBasis(info, ctx, committed) {
|
|
|
5964
6003
|
const withDraw = {
|
|
5965
6004
|
...base2,
|
|
5966
6005
|
drawId,
|
|
5967
|
-
drawAmountMicro: draw.amountMicro
|
|
6006
|
+
drawAmountMicro: draw.amountMicro,
|
|
6007
|
+
...!ctx.midJob && !ctx.fundOnly && { upfrontPayment: upfrontPaymentFromDraw(draw) }
|
|
5968
6008
|
};
|
|
5969
6009
|
if (!ctx.drawBasis) return withDraw;
|
|
5970
6010
|
const { drawMsats, drawNative } = committed.added ?? draw.railValue;
|
|
@@ -12514,6 +12554,12 @@ function buildContext(job, opts) {
|
|
|
12514
12554
|
log.error("Terminal handling failed", { error: String(err) });
|
|
12515
12555
|
});
|
|
12516
12556
|
};
|
|
12557
|
+
let replayPrompts;
|
|
12558
|
+
const captureReplayPrompts = () => {
|
|
12559
|
+
if (replayPrompts === void 0 && job.replayHighSeq !== null) {
|
|
12560
|
+
replayPrompts = job.messages.filter((m) => m.from === "provider" && m.type === "prompt" && m.seq <= job.replayHighSeq);
|
|
12561
|
+
}
|
|
12562
|
+
};
|
|
12517
12563
|
let creditedToHandlerMsats = 0;
|
|
12518
12564
|
let creditedToHandlerMicro = 0;
|
|
12519
12565
|
let poolBasis = false;
|
|
@@ -12521,6 +12567,7 @@ function buildContext(job, opts) {
|
|
|
12521
12567
|
let declaredCostCurrency;
|
|
12522
12568
|
let declaredCostRevision = 0;
|
|
12523
12569
|
let costPoisoned = false;
|
|
12570
|
+
const stepWrites = /* @__PURE__ */ new Map();
|
|
12524
12571
|
const stepCostOwner = {};
|
|
12525
12572
|
const declareCost = (declared) => {
|
|
12526
12573
|
if (terminated("cost")) return;
|
|
@@ -12576,29 +12623,41 @@ function buildContext(job, opts) {
|
|
|
12576
12623
|
},
|
|
12577
12624
|
sendMessage: (type, content) => {
|
|
12578
12625
|
if (terminated("sendMessage")) return;
|
|
12626
|
+
if (type === "payment-request" && typeof content.amount_msats === "number") job.pendingPaymentMsats = content.amount_msats;
|
|
12579
12627
|
providerMessage(job, type, content);
|
|
12628
|
+
if ((type === "prompt" || type === "payment-request") && opts.onYield) {
|
|
12629
|
+
void opts.onYield().catch((err) => {
|
|
12630
|
+
log.error("Failed to persist event-driven wait", { error: String(err) });
|
|
12631
|
+
});
|
|
12632
|
+
}
|
|
12580
12633
|
},
|
|
12581
12634
|
prompt: (id, text, promptOpts) => {
|
|
12582
12635
|
if (terminated("prompt")) {
|
|
12583
12636
|
return Promise.reject(new JobCancelledError(`Job already ${job.status}`));
|
|
12584
12637
|
}
|
|
12585
12638
|
job.status = "awaiting-input";
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
});
|
|
12592
|
-
if (job.replayHighSeq !== null) {
|
|
12639
|
+
captureReplayPrompts();
|
|
12640
|
+
const originalIndex = replayPrompts?.findIndex((m) => m.type === "prompt" && m.content.id === id) ?? -1;
|
|
12641
|
+
const original = originalIndex < 0 ? void 0 : replayPrompts.splice(originalIndex, 1)[0];
|
|
12642
|
+
if (original) {
|
|
12643
|
+
if (job.replayProviderSkip > 0) job.replayProviderSkip--;
|
|
12593
12644
|
const response = job.messages.find(
|
|
12594
|
-
(m) => m.type === "response" && m.from === "requester" && m.content.prompt_id === id
|
|
12645
|
+
(m) => m.type === "response" && m.from === "requester" && m.content.prompt_id === id && m.seq > original.seq
|
|
12595
12646
|
);
|
|
12596
12647
|
if (response) {
|
|
12597
|
-
job.status = "processing";
|
|
12648
|
+
job.status = job.pendingPrompts.size > 0 || job.pendingPayment ? "awaiting-input" : "processing";
|
|
12649
|
+
opts.onCallerResume?.();
|
|
12598
12650
|
return Promise.resolve(response.content);
|
|
12599
12651
|
}
|
|
12652
|
+
} else {
|
|
12600
12653
|
job.replayHighSeq = null;
|
|
12601
12654
|
job.replayProviderSkip = 0;
|
|
12655
|
+
providerMessage(job, "prompt", {
|
|
12656
|
+
id,
|
|
12657
|
+
text,
|
|
12658
|
+
...promptOpts?.options && { options: promptOpts.options },
|
|
12659
|
+
...promptOpts?.schema && { schema: promptOpts.schema }
|
|
12660
|
+
});
|
|
12602
12661
|
}
|
|
12603
12662
|
const p = new Promise((resolve, reject) => {
|
|
12604
12663
|
job.pendingPrompts.set(id, { resolve, reject });
|
|
@@ -12611,6 +12670,7 @@ function buildContext(job, opts) {
|
|
|
12611
12670
|
return p;
|
|
12612
12671
|
},
|
|
12613
12672
|
requestPayment: async (amount, reason, payOpts) => {
|
|
12673
|
+
captureReplayPrompts();
|
|
12614
12674
|
if (terminated("requestPayment")) {
|
|
12615
12675
|
throw new JobCancelledError(`Job already ${job.status}`);
|
|
12616
12676
|
}
|
|
@@ -12719,135 +12779,153 @@ function buildContext(job, opts) {
|
|
|
12719
12779
|
poolRemainingMicro: pool.amountMicro - creditedToHandlerMicro
|
|
12720
12780
|
} : { poolRemaining: job.paidMsats - creditedToHandlerMsats }
|
|
12721
12781
|
});
|
|
12782
|
+
if (job.replayHighSeq !== null) {
|
|
12783
|
+
job.status = job.pendingPrompts.size > 0 ? "awaiting-input" : "processing";
|
|
12784
|
+
opts.onCallerResume?.();
|
|
12785
|
+
}
|
|
12722
12786
|
advanceReplayPaymentIndex(job);
|
|
12723
12787
|
return { amount_msats: amountMsats };
|
|
12724
12788
|
}
|
|
12725
|
-
const
|
|
12789
|
+
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;
|
|
12790
|
+
const originalAsk = replayedPaymentRequest?.type === "payment-request" ? replayedPaymentRequest.content : void 0;
|
|
12791
|
+
if (originalAsk) amountMsats = originalAsk.amount_msats;
|
|
12792
|
+
const pendingFiat = originalAsk ? originalAsk.credit ? { amountMicro: originalAsk.credit.amount_micro, currency: originalAsk.credit.currency } : void 0 : await pinAsk();
|
|
12726
12793
|
job.pendingPaymentMsats = amountMsats;
|
|
12727
12794
|
job.pendingPaymentFiatMicro = pendingFiat?.amountMicro;
|
|
12728
12795
|
job.pendingPaymentFiatCurrency = pendingFiat?.currency;
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
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 } : {}
|
|
12796
|
+
let paymentRequestContent;
|
|
12797
|
+
if (originalAsk) {
|
|
12798
|
+
paymentRequestContent = { ...originalAsk };
|
|
12799
|
+
job.pendingX402Nonce = originalAsk.x402?.nonce;
|
|
12800
|
+
job.pendingX402AmountUsdcMicro = originalAsk.x402?.required_usdc_micro;
|
|
12801
|
+
job.pendingMppChallengeIds = originalAsk.tempo?.challenges.map((challenge) => challenge.id);
|
|
12802
|
+
} else {
|
|
12803
|
+
paymentRequestContent = {
|
|
12804
|
+
amount_msats: amountMsats,
|
|
12805
|
+
reason,
|
|
12806
|
+
...pendingFiat && { credit: { amount_micro: pendingFiat.amountMicro, currency: pendingFiat.currency } },
|
|
12807
|
+
...fiatAmount && { fiat_amount: fiatAmount },
|
|
12808
|
+
...payOpts?.mints && { mints: payOpts.mints },
|
|
12809
|
+
...payOpts?.minLocktimeSeconds && { min_locktime_seconds: payOpts.minLocktimeSeconds },
|
|
12810
|
+
...!payOpts?.mints && opts.mints && { mints: opts.mints }
|
|
12764
12811
|
};
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
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);
|
|
12812
|
+
let paymentRate;
|
|
12813
|
+
let x402Nonce;
|
|
12814
|
+
let x402AmountUsdcMicro;
|
|
12815
|
+
const hasExplicitX402 = Boolean(payOpts?.x402);
|
|
12816
|
+
if ((opts.pricingCurrency ?? "usd") === "usd") {
|
|
12817
|
+
if (payOpts?.x402) {
|
|
12818
|
+
paymentRequestContent.x402 = payOpts.x402;
|
|
12819
|
+
} else if (opts.paymentMethods?.includes("x402") && opts.x402 && (opts.x402FacilitatorHealth?.support().available ?? true)) {
|
|
12820
|
+
paymentRequestContent.x402 = {
|
|
12821
|
+
pay_to: opts.x402.payTo,
|
|
12822
|
+
network: opts.x402.network,
|
|
12823
|
+
asset: opts.x402.asset ?? "USDC",
|
|
12824
|
+
...opts.x402.facilitator && { facilitator: opts.x402.facilitator }
|
|
12825
|
+
};
|
|
12826
|
+
}
|
|
12786
12827
|
}
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
const
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12828
|
+
if (paymentRequestContent.x402) {
|
|
12829
|
+
paymentRate = (await resolveServerBtcUsdRate(opts.fxFetcher)).rate;
|
|
12830
|
+
x402Nonce = `0x${randomBytes3(32).toString("hex")}`;
|
|
12831
|
+
x402AmountUsdcMicro = msatsToUsdc(amountMsats, paymentRate).toString();
|
|
12832
|
+
const x402Request = paymentRequestContent.x402;
|
|
12833
|
+
const challengeConfig = {
|
|
12834
|
+
payTo: x402Request.pay_to,
|
|
12835
|
+
network: x402Request.network,
|
|
12836
|
+
...x402Request.asset ? { asset: x402Request.asset } : {},
|
|
12837
|
+
...x402Request.facilitator ? { facilitator: x402Request.facilitator } : {}
|
|
12838
|
+
};
|
|
12839
|
+
Object.assign(x402Request, {
|
|
12840
|
+
nonce: x402Nonce,
|
|
12841
|
+
required_usdc_micro: x402AmountUsdcMicro,
|
|
12842
|
+
...hasExplicitX402 || (opts.x402FacilitatorHealth?.support().exact.v2 ?? true) ? {
|
|
12843
|
+
payment_required: buildPaymentRequiredV2({
|
|
12844
|
+
x402Config: challengeConfig,
|
|
12845
|
+
requiredUsdcMicro: BigInt(x402AmountUsdcMicro),
|
|
12846
|
+
resource: `/v1/job/${job.id}/messages`,
|
|
12847
|
+
description: reason
|
|
12848
|
+
})
|
|
12849
|
+
} : {}
|
|
12797
12850
|
});
|
|
12798
|
-
challenges.push(ch);
|
|
12799
12851
|
}
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12852
|
+
job.pendingX402Nonce = x402Nonce;
|
|
12853
|
+
job.pendingX402AmountUsdcMicro = x402AmountUsdcMicro;
|
|
12854
|
+
let mppChallengeIds;
|
|
12855
|
+
if (payOpts?.mpp) {
|
|
12856
|
+
const challenges = payOpts.mpp.challenges.filter((c) => c.intent !== "session");
|
|
12857
|
+
if (challenges.length > 0) {
|
|
12858
|
+
paymentRequestContent.tempo = { challenges };
|
|
12859
|
+
mppChallengeIds = challenges.map((c) => c.id);
|
|
12860
|
+
}
|
|
12861
|
+
} else if (opts.paymentMethods?.includes("tempo") && opts.mpp) {
|
|
12862
|
+
const rate = paymentRate ?? (await resolveServerBtcUsdRate(opts.fxFetcher)).rate;
|
|
12863
|
+
const challenges = [];
|
|
12864
|
+
for (const m of opts.mpp.methods) {
|
|
12865
|
+
const meta = m;
|
|
12866
|
+
if (meta.intent === "session") continue;
|
|
12867
|
+
const amount2 = pendingFiat?.currency === "usd" ? fiatMicroToMethodAmount(pendingFiat.amountMicro, meta.name) : msatsToMethodAmount(amountMsats, meta.name, rate);
|
|
12868
|
+
const ch = await opts.mpp.issueChallenge(meta.name, meta.intent, {
|
|
12869
|
+
amount: amount2,
|
|
12870
|
+
description: reason
|
|
12871
|
+
});
|
|
12872
|
+
challenges.push(ch);
|
|
12873
|
+
}
|
|
12874
|
+
if (challenges.length > 0) {
|
|
12875
|
+
paymentRequestContent.tempo = { challenges };
|
|
12876
|
+
mppChallengeIds = challenges.map((c) => c.id);
|
|
12877
|
+
}
|
|
12803
12878
|
}
|
|
12879
|
+
job.pendingMppChallengeIds = mppChallengeIds;
|
|
12804
12880
|
}
|
|
12805
|
-
job.pendingMppChallengeIds = mppChallengeIds;
|
|
12806
12881
|
if (job.replayHighSeq !== null) {
|
|
12807
|
-
const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester");
|
|
12882
|
+
const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester" && m.seq <= job.replayHighSeq);
|
|
12808
12883
|
if (job.replayPaymentIndex < payments.length) {
|
|
12809
12884
|
const payment = payments[job.replayPaymentIndex];
|
|
12810
12885
|
job.replayPaymentIndex++;
|
|
12811
12886
|
const balance = payment.content.credit;
|
|
12812
12887
|
const accepted = balance ? await opts.acceptedBalancePayment?.(payment.seq, pendingFiat?.amountMicro, pendingFiat?.currency) : job.reactivationPaymentApplied === true;
|
|
12813
12888
|
if (!accepted) throw new Error("[internal_error] payment replay reached without credit");
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
job.status = "processing";
|
|
12889
|
+
job.pendingPaymentMsats = void 0;
|
|
12890
|
+
job.pendingPaymentFiatMicro = void 0;
|
|
12891
|
+
job.pendingPaymentFiatCurrency = void 0;
|
|
12892
|
+
job.pendingMppChallengeIds = void 0;
|
|
12893
|
+
job.pendingX402Nonce = void 0;
|
|
12894
|
+
job.pendingX402AmountUsdcMicro = void 0;
|
|
12895
|
+
job.status = job.pendingPrompts.size > 0 ? "awaiting-input" : "processing";
|
|
12896
|
+
opts.onCallerResume?.();
|
|
12823
12897
|
creditedToHandlerMsats += amountMsats;
|
|
12824
12898
|
creditedToHandlerMicro += pendingFiat?.amountMicro ?? 0;
|
|
12825
12899
|
return payment.content;
|
|
12826
12900
|
}
|
|
12827
|
-
|
|
12828
|
-
|
|
12901
|
+
if (!originalAsk) {
|
|
12902
|
+
job.replayHighSeq = null;
|
|
12903
|
+
job.replayProviderSkip = 0;
|
|
12904
|
+
}
|
|
12829
12905
|
}
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
job.topUpCapUnenforcedReason
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12906
|
+
if (!originalAsk) {
|
|
12907
|
+
const askedTotal = accumulateAskedTopUp(
|
|
12908
|
+
{
|
|
12909
|
+
micro: job.askedTopUpMicro,
|
|
12910
|
+
currency: job.askedTopUpCurrency,
|
|
12911
|
+
reason: job.topUpCapUnenforcedReason
|
|
12912
|
+
},
|
|
12913
|
+
{ micro: pendingFiat?.amountMicro, currency: pendingFiat?.currency }
|
|
12914
|
+
);
|
|
12915
|
+
job.askedTopUpMicro = askedTotal.micro;
|
|
12916
|
+
job.askedTopUpCurrency = askedTotal.currency;
|
|
12917
|
+
if (askedTotal.reason && !job.topUpCapUnenforcedReason) {
|
|
12918
|
+
job.topUpCapUnenforcedReason = askedTotal.reason;
|
|
12919
|
+
loggers.isolate.warn("dvm.topup_cap_unenforceable", {
|
|
12920
|
+
job_id: job.id,
|
|
12921
|
+
reason: askedTotal.reason,
|
|
12922
|
+
ask_currency: askCurrency,
|
|
12923
|
+
ask_msats: amountMsats
|
|
12924
|
+
});
|
|
12925
|
+
}
|
|
12848
12926
|
}
|
|
12849
12927
|
job.status = "awaiting-input";
|
|
12850
|
-
providerMessage(job, "payment-request", paymentRequestContent);
|
|
12928
|
+
if (!originalAsk) providerMessage(job, "payment-request", paymentRequestContent);
|
|
12851
12929
|
const paidMsatsAtYield = job.paidMsats;
|
|
12852
12930
|
const p = new Promise((resolve, reject) => {
|
|
12853
12931
|
let resolved = false;
|
|
@@ -12865,6 +12943,9 @@ function buildContext(job, opts) {
|
|
|
12865
12943
|
if (resolved) return;
|
|
12866
12944
|
job.pendingPayment = null;
|
|
12867
12945
|
}
|
|
12946
|
+
job.replayHighSeq = null;
|
|
12947
|
+
job.replayProviderSkip = 0;
|
|
12948
|
+
opts.onCallerResume?.();
|
|
12868
12949
|
resolved = true;
|
|
12869
12950
|
cleanup();
|
|
12870
12951
|
creditedToHandlerMsats += amountMsats;
|
|
@@ -12970,6 +13051,7 @@ function buildContext(job, opts) {
|
|
|
12970
13051
|
cost: declareCost,
|
|
12971
13052
|
step: async (id, fn) => {
|
|
12972
13053
|
if (job.stepCache.has(id)) {
|
|
13054
|
+
await stepWrites.get(id);
|
|
12973
13055
|
const record = job.stepCache.getRecord(id);
|
|
12974
13056
|
for (const declared of record.costs ?? []) declareCost(declared);
|
|
12975
13057
|
return record.value;
|
|
@@ -12982,6 +13064,15 @@ function buildContext(job, opts) {
|
|
|
12982
13064
|
fn
|
|
12983
13065
|
);
|
|
12984
13066
|
job.stepCache.set(id, result, costs);
|
|
13067
|
+
if (opts.onStep) {
|
|
13068
|
+
const persistence = opts.onStep(job.stepCache.getRecord(id));
|
|
13069
|
+
stepWrites.set(id, persistence);
|
|
13070
|
+
try {
|
|
13071
|
+
await persistence;
|
|
13072
|
+
} finally {
|
|
13073
|
+
stepWrites.delete(id);
|
|
13074
|
+
}
|
|
13075
|
+
}
|
|
12985
13076
|
return result;
|
|
12986
13077
|
},
|
|
12987
13078
|
state: job.state,
|
|
@@ -13008,7 +13099,7 @@ function warnPinLost(jobId, currency, amountMsats, reason, detail) {
|
|
|
13008
13099
|
}
|
|
13009
13100
|
function advanceReplayPaymentIndex(job) {
|
|
13010
13101
|
if (job.replayHighSeq === null) return;
|
|
13011
|
-
const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester");
|
|
13102
|
+
const payments = job.messages.filter((m) => m.type === "payment" && m.from === "requester" && m.seq <= job.replayHighSeq);
|
|
13012
13103
|
if (job.replayPaymentIndex < payments.length) {
|
|
13013
13104
|
job.replayPaymentIndex++;
|
|
13014
13105
|
}
|
|
@@ -13421,6 +13512,7 @@ var JobManager = class {
|
|
|
13421
13512
|
// Bind live resolvers and recovery scaffolds to the store-allocated ask identity.
|
|
13422
13513
|
paymentAskSeqs = /* @__PURE__ */ new WeakMap();
|
|
13423
13514
|
reactivationAskSeqs = /* @__PURE__ */ new WeakMap();
|
|
13515
|
+
pendingPaymentMessages = /* @__PURE__ */ new WeakMap();
|
|
13424
13516
|
// Every local input resume holds this queue through its durable wait update.
|
|
13425
13517
|
// A prompt response and a payment NOTIFY must not mistake each other for a
|
|
13426
13518
|
// different machine's successful claim.
|
|
@@ -13746,6 +13838,7 @@ var JobManager = class {
|
|
|
13746
13838
|
status: "processing",
|
|
13747
13839
|
messages: [],
|
|
13748
13840
|
seq: 0,
|
|
13841
|
+
upfrontPayment: payment.upfrontPayment ? structuredClone(payment.upfrontPayment) : void 0,
|
|
13749
13842
|
paidMsats: payment.paidMsats,
|
|
13750
13843
|
paymentMint: payment.paymentMint,
|
|
13751
13844
|
paymentRail: payment.paymentRail,
|
|
@@ -13929,8 +14022,10 @@ var JobManager = class {
|
|
|
13929
14022
|
job.pendingPrompts.delete(rc.prompt_id);
|
|
13930
14023
|
job.status = job.pendingPrompts.size > 0 || job.pendingPayment ? "awaiting-input" : "processing";
|
|
13931
14024
|
if (job.status === "awaiting-input") await this.persistJob(job);
|
|
14025
|
+
this.startIdleTimer(job);
|
|
13932
14026
|
pending.resolve(rc);
|
|
13933
14027
|
} else if (cap?.onResponse && ctx) {
|
|
14028
|
+
this.startIdleTimer(job);
|
|
13934
14029
|
await cap.onResponse(ctx, rc);
|
|
13935
14030
|
}
|
|
13936
14031
|
} else if (type === "payment") {
|
|
@@ -13950,6 +14045,7 @@ var JobManager = class {
|
|
|
13950
14045
|
await pp.resolve(pc);
|
|
13951
14046
|
});
|
|
13952
14047
|
} else if (cap?.onPayment && ctx) {
|
|
14048
|
+
this.startIdleTimer(job);
|
|
13953
14049
|
await cap.onPayment(ctx, pc);
|
|
13954
14050
|
}
|
|
13955
14051
|
} else if (type === "approval" && cap?.onApproval && ctx) {
|
|
@@ -13994,7 +14090,7 @@ var JobManager = class {
|
|
|
13994
14090
|
if (await this.adoptDurableTerminal(job)) return;
|
|
13995
14091
|
const record = toJobRecord(job);
|
|
13996
14092
|
if (isTerminal(job.status) && isStreamableJobStore(this.jobStore)) {
|
|
13997
|
-
const durable = await this.jobStore.getMessages(job.id, 0);
|
|
14093
|
+
const durable = await this.jobStore.getMessages(job.id, 0, { includeHeld: true });
|
|
13998
14094
|
if (durable.length >= record.messages.length) {
|
|
13999
14095
|
record.messages = durable;
|
|
14000
14096
|
const highSeq = durable.length ? durable[durable.length - 1].seq : record.seq;
|
|
@@ -14298,9 +14394,10 @@ var JobManager = class {
|
|
|
14298
14394
|
job.messageAppenderTail = next;
|
|
14299
14395
|
};
|
|
14300
14396
|
}
|
|
14301
|
-
startIdleTimer(job) {
|
|
14397
|
+
startIdleTimer(job, deadline = callerDeadline(job, this.descriptor.idleTimeout * 1e3)) {
|
|
14302
14398
|
this.clearIdleTimer(job.id);
|
|
14303
|
-
|
|
14399
|
+
if (deadline === void 0) return;
|
|
14400
|
+
const timeoutMs = Math.min(2147483647, Math.max(0, deadline - Date.now()));
|
|
14304
14401
|
const timer = setTimeout(() => {
|
|
14305
14402
|
void this.cancelJobIdle(job).catch(() => void 0);
|
|
14306
14403
|
}, timeoutMs);
|
|
@@ -14316,7 +14413,7 @@ var JobManager = class {
|
|
|
14316
14413
|
/** Cancel a job due to idle timeout. Exposed for the reactivation path's idle-expiry guard. */
|
|
14317
14414
|
async cancelJobIdle(job) {
|
|
14318
14415
|
if (isTerminal(job.status)) return;
|
|
14319
|
-
const current = await this.jobStore.get(job.id)
|
|
14416
|
+
const current = await this.jobStore.get(job.id);
|
|
14320
14417
|
if (current && isTerminal(current.status)) {
|
|
14321
14418
|
const reason = `Job reached terminal status "${current.status}" elsewhere`;
|
|
14322
14419
|
for (const [, p] of job.pendingPrompts) p.reject(new Error(reason));
|
|
@@ -14328,11 +14425,32 @@ var JobManager = class {
|
|
|
14328
14425
|
this.scheduleCleanup(job);
|
|
14329
14426
|
return;
|
|
14330
14427
|
}
|
|
14428
|
+
const waiting = current ?? job;
|
|
14429
|
+
if (current && isStreamableJobStore(this.jobStore)) {
|
|
14430
|
+
const messages = await this.jobStore.getMessages(job.id, 0);
|
|
14431
|
+
if (messages.length) waiting.messages = messages;
|
|
14432
|
+
}
|
|
14433
|
+
const deadline = callerDeadline(waiting, this.descriptor.idleTimeout * 1e3);
|
|
14434
|
+
if (deadline === void 0) {
|
|
14435
|
+
this.clearIdleTimer(job.id);
|
|
14436
|
+
return;
|
|
14437
|
+
}
|
|
14438
|
+
if (Date.now() < deadline) {
|
|
14439
|
+
this.startIdleTimer(job, deadline);
|
|
14440
|
+
return;
|
|
14441
|
+
}
|
|
14442
|
+
if (current && isStreamableJobStore(this.jobStore)) {
|
|
14443
|
+
const claimed = await this.jobStore.cancelStaleJob(job.id, current.lastActivityAt, "Job timed out (idle)", "cancelled");
|
|
14444
|
+
if (claimed) await this.finalizeStoredTerminal(job.id);
|
|
14445
|
+
await this.adoptDurableTerminal(job);
|
|
14446
|
+
if (!claimed && !isTerminal(job.status)) this.startIdleTimer(job, Date.now() + 1);
|
|
14447
|
+
return;
|
|
14448
|
+
}
|
|
14331
14449
|
for (const [, p] of job.pendingPrompts) p.reject(new Error("Job timed out (idle)"));
|
|
14332
14450
|
if (job.pendingPayment) job.pendingPayment.reject(new Error("Job timed out (idle)"));
|
|
14333
14451
|
job.pendingPrompts.clear();
|
|
14334
14452
|
job.pendingPayment = null;
|
|
14335
|
-
job.endedBy =
|
|
14453
|
+
job.endedBy = "caller";
|
|
14336
14454
|
job.status = "cancelled";
|
|
14337
14455
|
abortJob(job, "Job timed out (idle)");
|
|
14338
14456
|
providerMessage(job, "cancel", { reason: "Job timed out (idle)" });
|
|
@@ -14345,8 +14463,11 @@ var JobManager = class {
|
|
|
14345
14463
|
* • `awaiting-input` past `staleJobTimeoutMs` → `cancelled`
|
|
14346
14464
|
* (`stale_no_terminal_status`) — the caller never paid / responded.
|
|
14347
14465
|
* • `processing`/`working` past `processingWatchdogMs` → `failed`
|
|
14348
|
-
* (`worker_died_mid_job`) — the worker died mid-job.
|
|
14349
|
-
*
|
|
14466
|
+
* (`worker_died_mid_job`) — the worker died mid-job. Before claiming a
|
|
14467
|
+
* processing row, skip and heartbeat it if this manager still holds a live
|
|
14468
|
+
* `processing`/`working` job. After a process pause, the sweep may run before
|
|
14469
|
+
* the heartbeat timer; stale activity alone does not prove a local worker
|
|
14470
|
+
* died. Superseded handlers leave `_activeJobs` and receive no protection.
|
|
14350
14471
|
*
|
|
14351
14472
|
* Fires on a timer (wired in the constructor) and also callable on demand
|
|
14352
14473
|
* (tests, ops). The CAS in `cancelStaleJob` makes this safe to run
|
|
@@ -14354,6 +14475,8 @@ var JobManager = class {
|
|
|
14354
14475
|
* JobManager owns so multi-mount hosts don't steal stuck rows from sibling
|
|
14355
14476
|
* descriptors — the local activeJobs teardown only makes sense on the
|
|
14356
14477
|
* JobManager that actually hosted the zombie handler.
|
|
14478
|
+
* Local liveness prevents this manager's claim; the activity CAS continues
|
|
14479
|
+
* to protect against other machines claiming a row refreshed after find.
|
|
14357
14480
|
*
|
|
14358
14481
|
* **Both cutoffs float off the wall clock.** Each is one
|
|
14359
14482
|
* `Date.now()` sample compared against `last_activity_at`, stamped from a
|
|
@@ -14418,10 +14541,25 @@ var JobManager = class {
|
|
|
14418
14541
|
);
|
|
14419
14542
|
let swept = 0;
|
|
14420
14543
|
for (const record of stale) {
|
|
14421
|
-
const
|
|
14544
|
+
const messages = await this.jobStore.getMessages(record.id, 0);
|
|
14545
|
+
const history = messages.length ? messages : record.messages;
|
|
14546
|
+
const deadline = callerDeadline({ ...record, messages: history });
|
|
14547
|
+
if (deadline !== void 0 && now < deadline) continue;
|
|
14548
|
+
const orphanedInput = record.status === "awaiting-input" && deadline === void 0 && hasCallerDeadline(history);
|
|
14549
|
+
const awaiting = deadline !== void 0 || record.status === "awaiting-input" && !orphanedInput;
|
|
14422
14550
|
const terminalStatus = awaiting ? "cancelled" : "failed";
|
|
14423
14551
|
const reason = awaiting ? "stale_no_terminal_status" : "worker_died_mid_job";
|
|
14424
|
-
const expectedActivityBefore = awaiting ? awaitingThreshold : processingThreshold;
|
|
14552
|
+
const expectedActivityBefore = awaiting && record.status === "awaiting-input" ? awaitingThreshold : processingThreshold;
|
|
14553
|
+
if (expectedActivityBefore < 0 || record.lastActivityAt > expectedActivityBefore) continue;
|
|
14554
|
+
const local = this._activeJobs.get(record.id);
|
|
14555
|
+
if ((record.status === "processing" || record.status === "working") && local && (local.status === "processing" || local.status === "working")) {
|
|
14556
|
+
await this.jobStore.heartbeatActiveJobs([record.id], Date.now());
|
|
14557
|
+
diagnosticConsole.warn({
|
|
14558
|
+
level: "stale_job_local_live_skipped",
|
|
14559
|
+
ageMs: monoNow - record.lastActivityAt
|
|
14560
|
+
});
|
|
14561
|
+
continue;
|
|
14562
|
+
}
|
|
14425
14563
|
const claimed = await this.jobStore.cancelStaleJob(
|
|
14426
14564
|
record.id,
|
|
14427
14565
|
expectedActivityBefore,
|
|
@@ -15041,8 +15179,9 @@ var JobManager = class {
|
|
|
15041
15179
|
}
|
|
15042
15180
|
}
|
|
15043
15181
|
/**
|
|
15044
|
-
* Record an inbound client message durably via Tx B
|
|
15045
|
-
*
|
|
15182
|
+
* Record an inbound client message durably via Tx B. Payments stay outside
|
|
15183
|
+
* visible history until processPayment accepts them; other messages append
|
|
15184
|
+
* to `job.messages` immediately. Returns the DB-allocated seq
|
|
15046
15185
|
* (`null` when the store isn't streamable — non-Postgres test fallback).
|
|
15047
15186
|
*
|
|
15048
15187
|
* Status starts as `pending-verification`; a follow-up Tx C call
|
|
@@ -15061,13 +15200,24 @@ var JobManager = class {
|
|
|
15061
15200
|
});
|
|
15062
15201
|
}
|
|
15063
15202
|
job.seq++;
|
|
15064
|
-
|
|
15203
|
+
const message = {
|
|
15065
15204
|
seq: job.seq,
|
|
15066
15205
|
from: "requester",
|
|
15067
15206
|
timestamp,
|
|
15068
15207
|
type: msgType,
|
|
15069
15208
|
content: msgContent
|
|
15070
|
-
}
|
|
15209
|
+
};
|
|
15210
|
+
if (msgType === "payment") {
|
|
15211
|
+
let pending = this.pendingPaymentMessages.get(job);
|
|
15212
|
+
if (!pending) {
|
|
15213
|
+
pending = /* @__PURE__ */ new Map();
|
|
15214
|
+
this.pendingPaymentMessages.set(job, pending);
|
|
15215
|
+
}
|
|
15216
|
+
pending.set(txSeq ?? msgContent, message);
|
|
15217
|
+
holdJobHistory(job, message.seq);
|
|
15218
|
+
} else {
|
|
15219
|
+
job.messages.push(message);
|
|
15220
|
+
}
|
|
15071
15221
|
job.lastActivityAt = Date.now();
|
|
15072
15222
|
return txSeq;
|
|
15073
15223
|
}
|
|
@@ -15176,6 +15326,22 @@ var JobManager = class {
|
|
|
15176
15326
|
* 500. See `creditInbound`.
|
|
15177
15327
|
*/
|
|
15178
15328
|
async processPayment(job, body, inboundSeq, extra) {
|
|
15329
|
+
const pending = this.pendingPaymentMessages.get(job);
|
|
15330
|
+
const key = inboundSeq ?? body.content;
|
|
15331
|
+
const message = pending?.get(key);
|
|
15332
|
+
let accepted;
|
|
15333
|
+
try {
|
|
15334
|
+
const outcome = await this.processPaymentAndCredit(job, body, inboundSeq, extra);
|
|
15335
|
+
if (!outcome.error && message && pending?.get(key) === message) {
|
|
15336
|
+
accepted = message;
|
|
15337
|
+
}
|
|
15338
|
+
return outcome;
|
|
15339
|
+
} finally {
|
|
15340
|
+
pending?.delete(key);
|
|
15341
|
+
if (message) releaseJobHistory(job, message.seq, accepted);
|
|
15342
|
+
}
|
|
15343
|
+
}
|
|
15344
|
+
async processPaymentAndCredit(job, body, inboundSeq, extra) {
|
|
15179
15345
|
if (body.type !== "payment") return {};
|
|
15180
15346
|
const devAuto = this.opts.devMode === true && body.content.dev_auto === true && !hasPaymentProof(body.content);
|
|
15181
15347
|
if (devAuto || devModeSkipsPaymentVerification(this.opts)) {
|
|
@@ -15260,7 +15426,7 @@ var JobManager = class {
|
|
|
15260
15426
|
};
|
|
15261
15427
|
}
|
|
15262
15428
|
const info = verifyResult.verified;
|
|
15263
|
-
if (!info) return {};
|
|
15429
|
+
if (!info) return { error: { status: 402, body: { error: "payment_invalid", message: "Payment was not accepted" } } };
|
|
15264
15430
|
const drawFundingReceipt = await this.drawFundingReceiptFor(info.drawFunding, info.funding?.currency);
|
|
15265
15431
|
let unappliedCredit;
|
|
15266
15432
|
const fullyUnapplied = info.unappliedMicro !== void 0 && info.funding !== void 0 && info.unappliedMicro >= info.funding.amountMicro;
|
|
@@ -15634,6 +15800,7 @@ var JobManager = class {
|
|
|
15634
15800
|
*/
|
|
15635
15801
|
buildContextOpts(job) {
|
|
15636
15802
|
const jobStore = this.jobStore;
|
|
15803
|
+
job.idleTimeoutMs = this.descriptor.idleTimeout * 1e3;
|
|
15637
15804
|
return {
|
|
15638
15805
|
mints: this.opts.mints,
|
|
15639
15806
|
store: this.opts.store,
|
|
@@ -15644,7 +15811,25 @@ var JobManager = class {
|
|
|
15644
15811
|
mpp: this.opts.mpp,
|
|
15645
15812
|
fxFetcher: this.opts.fxFetcher,
|
|
15646
15813
|
onTerminal: () => this.handleJobTerminal(job),
|
|
15647
|
-
onYield: () =>
|
|
15814
|
+
onYield: () => {
|
|
15815
|
+
this.startIdleTimer(job);
|
|
15816
|
+
return this.persistJob(job);
|
|
15817
|
+
},
|
|
15818
|
+
onCallerResume: () => this.startIdleTimer(job),
|
|
15819
|
+
onStep: jobStore.saveStep ? async (record) => {
|
|
15820
|
+
for (let attempt = 0; ; attempt++) {
|
|
15821
|
+
try {
|
|
15822
|
+
await jobStore.saveStep(job.id, record);
|
|
15823
|
+
return;
|
|
15824
|
+
} catch {
|
|
15825
|
+
if (attempt >= 2) {
|
|
15826
|
+
diagnosticConsole.warn({ event: "step_persist_failed" });
|
|
15827
|
+
return;
|
|
15828
|
+
}
|
|
15829
|
+
await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
|
|
15830
|
+
}
|
|
15831
|
+
}
|
|
15832
|
+
} : void 0,
|
|
15648
15833
|
onPaymentResume: () => retryJobSnapshot(() => this.persistJob(job)),
|
|
15649
15834
|
subscribeNotifications: isStreamableJobStore(jobStore) ? (cb) => jobStore.subscribeNotifications(job.id, cb) : void 0,
|
|
15650
15835
|
acceptedBalancePayment: this.opts.db && jobStore instanceof PostgresJobStore ? async (seq, amountMicro, currency) => {
|
|
@@ -20829,7 +21014,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
20829
21014
|
}
|
|
20830
21015
|
}
|
|
20831
21016
|
if (!opts.onJobCompleted && platformToken && platformUrl && dvmId && opts.db) {
|
|
20832
|
-
const { RevenueReporter } = await import("./revenue-reporter-
|
|
21017
|
+
const { RevenueReporter } = await import("./revenue-reporter-RIEHBKRA.js");
|
|
20833
21018
|
const reporter = new RevenueReporter(
|
|
20834
21019
|
opts.db,
|
|
20835
21020
|
platformUrl,
|
|
@@ -20839,7 +21024,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
20839
21024
|
revenueReporter = reporter;
|
|
20840
21025
|
await reporter.init();
|
|
20841
21026
|
reporter.startRetryLoop();
|
|
20842
|
-
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-
|
|
21027
|
+
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-DDQ7FGRC.js");
|
|
20843
21028
|
payoutReporter = new PayoutReporter({
|
|
20844
21029
|
dvmId,
|
|
20845
21030
|
db: opts.db,
|
|
@@ -23150,7 +23335,6 @@ var DVMServer = class {
|
|
|
23150
23335
|
);
|
|
23151
23336
|
}
|
|
23152
23337
|
const sdkCtx = jm.buildAndAttachContext(job, parsedInput);
|
|
23153
|
-
jm.startIdleTimer(job);
|
|
23154
23338
|
jm.runHandler(job, sdkCtx);
|
|
23155
23339
|
await new Promise((resolve) => {
|
|
23156
23340
|
setTimeout(resolve, 0);
|
|
@@ -23494,14 +23678,21 @@ var DVMServer = class {
|
|
|
23494
23678
|
const job = this.jobManager.activeJobs.get(id);
|
|
23495
23679
|
const after = Number(c.req.query("after") ?? "0");
|
|
23496
23680
|
const accept = c.req.header("Accept") ?? "";
|
|
23497
|
-
|
|
23498
|
-
|
|
23681
|
+
const store = this.jobManager.store;
|
|
23682
|
+
if (job && !isJobOwner(c, job)) return c.json({ error: "Job not found" }, 404);
|
|
23683
|
+
if (!job || isStreamableJobStore(store)) {
|
|
23684
|
+
if (job) await job.messageAppenderTail;
|
|
23499
23685
|
const record = await store.get(id);
|
|
23500
23686
|
if (!record) return c.json({ error: "Job not found" }, 404);
|
|
23501
23687
|
if (!isJobOwner(c, record)) return c.json({ error: "Job not found" }, 404);
|
|
23502
|
-
const
|
|
23688
|
+
const terminal = isTerminal(record.status) || job !== void 0 && isTerminal(job.status);
|
|
23689
|
+
let filtered = record.messages.filter((m) => m.seq > after);
|
|
23690
|
+
if (isStreamableJobStore(store)) {
|
|
23691
|
+
const durable = await store.getMessages(id, after, { includeHeld: terminal });
|
|
23692
|
+
if (durable.length > 0 || !terminal) filtered = durable;
|
|
23693
|
+
}
|
|
23503
23694
|
if (accept.includes("text/event-stream")) {
|
|
23504
|
-
if (isStreamableJobStore(store) && !
|
|
23695
|
+
if (isStreamableJobStore(store) && !terminal) {
|
|
23505
23696
|
return streamSSE(c, async (stream) => {
|
|
23506
23697
|
let unsubFn;
|
|
23507
23698
|
await new Promise((resolve) => {
|
|
@@ -23564,10 +23755,6 @@ var DVMServer = class {
|
|
|
23564
23755
|
}
|
|
23565
23756
|
});
|
|
23566
23757
|
}
|
|
23567
|
-
if (isStreamableJobStore(store) && !isTerminal(record.status)) {
|
|
23568
|
-
const msgs = await store.getMessages(id, after);
|
|
23569
|
-
if (msgs.length > 0) return c.json(await this.messageFields(record, msgs));
|
|
23570
|
-
}
|
|
23571
23758
|
return c.json(await this.messageFields(record, filtered));
|
|
23572
23759
|
}
|
|
23573
23760
|
if (!isJobOwner(c, job)) return c.json({ error: "Job not found" }, 404);
|
|
@@ -23575,7 +23762,7 @@ var DVMServer = class {
|
|
|
23575
23762
|
const fields = await this.contentFields(job);
|
|
23576
23763
|
return streamSSE(c, async (stream) => {
|
|
23577
23764
|
if (fields.content_available === false) return;
|
|
23578
|
-
const existing = job.
|
|
23765
|
+
const existing = visibleJobMessages(job).filter((m) => m.seq > after);
|
|
23579
23766
|
let yielded = false;
|
|
23580
23767
|
for (const msg of existing) {
|
|
23581
23768
|
await stream.writeSSE({ data: JSON.stringify(msg), event: "message" });
|
|
@@ -23603,7 +23790,7 @@ var DVMServer = class {
|
|
|
23603
23790
|
});
|
|
23604
23791
|
});
|
|
23605
23792
|
}
|
|
23606
|
-
return c.json(await this.messageFields(job, job.
|
|
23793
|
+
return c.json(await this.messageFields(job, visibleJobMessages(job).filter((m) => m.seq > after)));
|
|
23607
23794
|
}
|
|
23608
23795
|
jobNotActiveResponse(c) {
|
|
23609
23796
|
return c.json(
|
|
@@ -23697,7 +23884,6 @@ var DVMServer = class {
|
|
|
23697
23884
|
} else {
|
|
23698
23885
|
await jm.markInboundVerified(activeJob.id, inboundSeq2);
|
|
23699
23886
|
}
|
|
23700
|
-
jm.startIdleTimer(activeJob);
|
|
23701
23887
|
try {
|
|
23702
23888
|
await jm.dispatchMessage(activeJob, body2.type, body2.content);
|
|
23703
23889
|
await this.persistMessageSnapshot(jm, activeJob, body2.type);
|
|
@@ -23794,11 +23980,13 @@ var DVMServer = class {
|
|
|
23794
23980
|
...unappliedCredit && { credit: unappliedCredit },
|
|
23795
23981
|
...drawFundingReceipt && { draw_funding_receipt: drawFundingReceipt }
|
|
23796
23982
|
};
|
|
23797
|
-
const
|
|
23798
|
-
if (
|
|
23983
|
+
const deadline = callerDeadline(record, this.descriptor.idleTimeout * 1e3);
|
|
23984
|
+
if (deadline !== void 0 && Date.now() >= deadline) {
|
|
23799
23985
|
await jm.cancelJobIdle(job);
|
|
23800
|
-
|
|
23801
|
-
|
|
23986
|
+
if (isTerminal(job.status)) {
|
|
23987
|
+
const lastMsg2 = job.messages[job.messages.length - 1];
|
|
23988
|
+
return c.json({ seq: lastMsg2.seq, ...creditBlock }, 201);
|
|
23989
|
+
}
|
|
23802
23990
|
}
|
|
23803
23991
|
jm.startIdleTimer(job);
|
|
23804
23992
|
try {
|
|
@@ -23978,6 +24166,7 @@ function recordedDrawPayment(draw, opts) {
|
|
|
23978
24166
|
...rail === "cashu" ? { cashuFlow: "p2pk_accumulator" } : {},
|
|
23979
24167
|
creditId: draw.creditId,
|
|
23980
24168
|
drawId: draw.drawId,
|
|
24169
|
+
upfrontPayment: upfrontPaymentFromDraw(draw),
|
|
23981
24170
|
drawAmountMicro: draw.amountMicro,
|
|
23982
24171
|
...draw.railValue.creditCurrency ? { creditCurrency: draw.railValue.creditCurrency } : {}
|
|
23983
24172
|
};
|
|
@@ -24063,6 +24252,7 @@ function removeUnavailableContent(fields) {
|
|
|
24063
24252
|
}
|
|
24064
24253
|
function terminalResponseFields(job) {
|
|
24065
24254
|
const fields = {};
|
|
24255
|
+
if (job.upfrontPayment !== void 0) fields.upfront_payment = job.upfrontPayment;
|
|
24066
24256
|
if (job.status === "completed") {
|
|
24067
24257
|
if (job.summary !== void 0) fields.summary = job.summary;
|
|
24068
24258
|
const artifactCount = job.messages.filter((m) => m.type === "artifact").length;
|