@dvmkit/sdk 0.5.0-rc.1 → 0.7.0-rc.1

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 (54) hide show
  1. package/README.md +100 -0
  2. package/dist/{chunk-JH3SX3LY.js → chunk-6XF3RVVX.js} +1 -1
  3. package/dist/chunk-AFV77S4C.js +258 -0
  4. package/dist/{chunk-RLTSCKRI.js → chunk-BPYMEMQX.js} +50 -3
  5. package/dist/chunk-BQJIFRAZ.js +36 -0
  6. package/dist/{chunk-IWQHUSMO.js → chunk-CXCQLOOK.js} +143 -2
  7. package/dist/{chunk-PB4FYZET.js → chunk-EQLA4FFY.js} +54 -4
  8. package/dist/{chunk-LE74UEV7.js → chunk-EZXAJ6EI.js} +64 -53
  9. package/dist/{chunk-FI4CST2D.js → chunk-FWGWLPXF.js} +4 -6
  10. package/dist/{chunk-EYTHFFFU.js → chunk-GY66THYI.js} +179 -56
  11. package/dist/{chunk-TZ2WKCDD.js → chunk-HCLVYZUZ.js} +2 -2
  12. package/dist/{chunk-O5AT6S5T.js → chunk-IV6Q7NRW.js} +25 -195
  13. package/dist/{chunk-UOCZXO2Z.js → chunk-JEVLNAO3.js} +10 -5
  14. package/dist/{chunk-TUA7PYH7.js → chunk-KMED3H3A.js} +134 -130
  15. package/dist/{chunk-Z4KLECAC.js → chunk-N5RZ5HVB.js} +1 -1
  16. package/dist/{chunk-PAIM66YA.js → chunk-NFMOPYBA.js} +47 -37
  17. package/dist/{chunk-B2IROPQF.js → chunk-O2OTZ5GP.js} +40 -15
  18. package/dist/{chunk-FYRR2UEZ.js → chunk-OML32F6I.js} +1 -1
  19. package/dist/{chunk-6DOGTAHA.js → chunk-QR2EF2NI.js} +9 -43
  20. package/dist/{chunk-7ZUKL4IV.js → chunk-RA2WIX7G.js} +5 -5
  21. package/dist/{chunk-A4YFWZ7R.js → chunk-RCM3P2HE.js} +1 -1
  22. package/dist/{chunk-PLG4XER6.js → chunk-S3OY533W.js} +1 -1
  23. package/dist/{chunk-GFY5FRX3.js → chunk-SZVXOQ75.js} +5 -12
  24. package/dist/{chunk-MZRJR2CW.js → chunk-WBOKPC37.js} +22 -3
  25. package/dist/content-retention-DHyLdA6b.d.ts +78 -0
  26. package/dist/{credit-ledger-MI2H7NEC.js → credit-ledger-MCUXYKSJ.js} +2 -3
  27. package/dist/{credit-menu-CAfqe8IY.d.ts → credit-menu-DjnkeEUU.d.ts} +98 -5
  28. package/dist/{fx-D9nsN3n4.d.ts → fx-zMWFzfrU.d.ts} +1 -1
  29. package/dist/index.d.ts +5 -5
  30. package/dist/index.js +3 -3
  31. package/dist/internal/caller.d.ts +1369 -860
  32. package/dist/internal/caller.js +1079 -991
  33. package/dist/internal/index.js +1 -1
  34. package/dist/internal/server.d.ts +8 -99
  35. package/dist/internal/server.js +19 -18
  36. package/dist/{job-store-Bx-VFRjg.d.ts → job-store-DdLXBpol.d.ts} +1 -1
  37. package/dist/{lightning-backend-BAx2Y8pP.d.ts → lightning-backend-Bw-qtAbt.d.ts} +4 -2
  38. package/dist/{memory-credit-ledger-W2BAUYJP.js → memory-credit-ledger-SBSMGMDX.js} +3 -4
  39. package/dist/{postgres-consumed-credential-store-CUQJKRSL.js → postgres-consumed-credential-store-BUVCAPLS.js} +13 -0
  40. package/dist/{postgres-job-store-VLXLWXUQ.js → postgres-job-store-VHLQBJZC.js} +3 -1
  41. package/dist/server/index.d.ts +28 -10
  42. package/dist/server/index.js +34 -24
  43. package/dist/{step-cache-QG_h5-3G.d.ts → step-cache-2MkvenJn.d.ts} +3 -3
  44. package/dist/tempo-lifecycle-GJ77YKVY.js +15 -0
  45. package/dist/{tempo-wallet-B5XXV3YH.js → tempo-wallet-X2GGAZ6X.js} +4 -4
  46. package/dist/testing/index.d.ts +9 -2
  47. package/dist/testing/index.js +6 -3
  48. package/dist/{usd-EwRqCPF-.d.ts → usd-DI3d3gTK.d.ts} +1 -1
  49. package/dist/{x402-R6HPBY5D.js → x402-FT4S4JS4.js} +8 -10
  50. package/package.json +5 -3
  51. package/recipes/postgres/financial-export-v1.sql +137 -0
  52. package/dist/chunk-CZULM42S.js +0 -143
  53. package/dist/chunk-LIRAHP7B.js +0 -53
  54. package/dist/tempo-lifecycle-ZBBLFBMH.js +0 -15
package/README.md CHANGED
@@ -96,6 +96,12 @@ For Bitcoin credit monitoring, `GET /admin/credit/bitcoin-liability` reports exa
96
96
 
97
97
  The public [SDK guide](https://dvmkit.com/docs/sdk) covers signed requests, custom routes, rail configuration, credit reclaim, persistence, and deployment in depth. The [protocol guide](https://dvmkit.com/docs/protocol) specifies the HTTP contract.
98
98
 
99
+ ## Diagnostic retention
100
+
101
+ The SDK advertises 730 days of retention for sanitized diagnostics in every existing span dataset: `platform`, `cashu`, `lightning`, `stripe`, `x402`, `tempo`, `isolate`, `audit`, and `cron`. Dataset names, producer permissions, and query semantics remain unchanged; only operator services may write to `audit`.
102
+
103
+ This metadata describes the retention schedule. The SDK does not automatically delete diagnostics or override platform destruction or case controls. The platform must enforce the schedule and those controls.
104
+
99
105
  ## Repository development
100
106
 
101
107
  Use the Node.js release in `.nvmrc` (Node 22). Install with `npm ci`, then run the same gate CI runs with `npm run verify`. The gate checks repository metadata in the checkout, copies only tracked and unignored source into an isolated directory, installs its locked dependencies, and runs lint, build, typecheck (including the release scripts under `checkJs`), unit and database tests, API, package, consumer, packed-artifact safety, and release checks there. The clean source tree owns the complete build and test pass, so verification never depends on the checkout's `node_modules` or pre-existing `dist` and does not repeat the full suite; after every check passes, its verified `dist` replaces the checkout's build output for release packaging.
@@ -182,3 +188,97 @@ None of this claims exclusive rights in the phrase "digital vending machine" or
182
188
  ## License
183
189
 
184
190
  Apache-2.0. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).
191
+
192
+ ## Job content retention and case holds
193
+
194
+ `configureDVM` defaults `jobRetentionDays` to 730. Set a shorter or longer whole-day
195
+ window explicitly, or `0` to disable automatic age expiry. Only completed, failed,
196
+ and cancelled jobs are eligible. The terminal `lastActivityAt` is frozen: reads,
197
+ receipt delivery, late messages, and re-saving an outcome do not renew it. The
198
+ sweep removes input, params, summary, messages, state, caches, and bearer proofs;
199
+ it preserves durable status, signed receipts, payment amounts, and recovery linkage.
200
+
201
+ Authenticated job submit, request-result, status and JSON message responses expose
202
+ `content_expires_at` (epoch milliseconds or `null`) and `content_available` (boolean).
203
+ A null deadline means the job is active, expiry is disabled, or an active hold
204
+ suppresses it. A past deadline can still have available content until the sweep
205
+ runs. Missing fields on an older service or unsupported custom store mean unknown.
206
+ SSE messages retain their existing event format; read job status for expiry.
207
+
208
+ With durable service-admin configuration, use signed `Authorization: P2PK`
209
+ requests on the service-local routes:
210
+
211
+ | Method | Path | JSON body |
212
+ | --- | --- | --- |
213
+ | GET | `/admin/jobs/:jobId/content-holds` | none |
214
+ | POST | `/admin/jobs/:jobId/content-holds` | `purpose`, `reason`, `review_due_at` |
215
+ | POST | `/admin/jobs/:jobId/content-holds/:holdId/renew` | `reason`, `review_due_at` |
216
+ | POST | `/admin/jobs/:jobId/content-holds/:holdId/clear` | `reason` |
217
+
218
+ Purpose is `support` or `dispute`; reason is 1–1000 characters and review dates
219
+ are future epoch millisecond integers. Unknown body fields are rejected. Hold IDs,
220
+ actor public keys, and creation/clear times are server-derived. Authentication uses
221
+ the existing method/path/service-bound admin challenge, current and grace-period
222
+ keys, and atomic durable nonce consumption. A hold request does not grant access
223
+ to job content. Authentication failure returns 401 with a reason; an unsupported
224
+ store returns `content_holds_unavailable` (503), missing jobs return 404, and a
225
+ redacted job or inactive hold returns 409. Holds cannot restore redacted content.
226
+
227
+ Responses include `job_id`, caller content metadata, `baseline_content_expires_at`,
228
+ `holds`, and append-only `events`. Each hold contains `id`, `job_id`, `purpose`,
229
+ `reason`, original `actor`, `created_at`, `review_due_at`, `cleared_at`, and computed
230
+ `review_overdue`. Each event contains `id`, `hold_id`, `job_id`, `action`, `actor`,
231
+ `reason`, `at`, `review_due_at`. Renew and clear preserve the original actor and
232
+ creation date; their authenticated actors are in history. Any active hold suppresses
233
+ the effective deadline. Overdue review flags work; it does not release the hold.
234
+ The baseline remains visible for review. Clearing the last hold restores the
235
+ baseline and can make content immediately eligible for the next sweep.
236
+
237
+ `PostgresJobStore.init()` adds `job_content_holds` and `job_content_hold_events`
238
+ with job-scoped indexes; existing jobs need no expiry column or backfill. Placement
239
+ and sweep lock the same job row and check redaction/terminal activity/active holds
240
+ inside the transaction. Admin operations refuse jobs bound to another service;
241
+ legacy unbound rows belong to the local service database. A committed hold wins any later competing sweep; if
242
+ redaction wins first, placement fails. `MemoryJobStore` supplies the same contract
243
+ for development. Custom stores must implement `ContentHoldStore` and atomic
244
+ redaction fencing to support holds and truthful metadata.
245
+
246
+ Before explicitly destroying a service database, use your existing database
247
+ connection/proxy and the packaged `recipes/postgres/financial-export-v1.sql`:
248
+
249
+ ```sh
250
+ psql "$DATABASE_URL" -X -q -t -A -v ON_ERROR_STOP=1 \
251
+ -f node_modules/@dvmkit/sdk/recipes/postgres/financial-export-v1.sql \
252
+ > financial-evidence.json
253
+ ```
254
+
255
+ The versioned JSON export takes a consistent read-only snapshot and includes signed
256
+ receipt tuples, ledger balances/amounts/currencies, job/payment links, and recovery
257
+ outcomes. Funding lots preserve every deposit's original sats and credit basis,
258
+ including depleted lots. Draws retain both the original allocation and settled
259
+ amount. Refunds retain fulfilment costs and funding-lot linkage. Optional channel
260
+ projections preserve amounts and settlement links while excluding authorization
261
+ signatures and vouchers; Tempo bigint amounts are decimal strings.
262
+ Unsigned JSON blocks use typed financial projections: unsupported fields and
263
+ objects or arrays in scalar positions are excluded. Optional ledger tables absent from a service become empty arrays. It
264
+ excludes raw input/output, bearer tokens/proofs, payout descriptors, and operational
265
+ credentials. Signed receipts may retain their signed terminal reason and result
266
+ hash; supported version-1 job, funding, and drain receipts are returned unchanged.
267
+ Unsupported receipt versions, kinds, fields, or nested extensions fail the entire
268
+ export before any JSON is emitted. Update the recipe for that schema before
269
+ destruction; a failed export is not retained financial evidence. Verify receipt
270
+ signatures and check the file before destruction, then keep it securely with this recipe
271
+ and the service's public receipt-key attestation. The builder owns this export;
272
+ the platform retains its separate financial history. An export does not settle
273
+ caller balances or refunds: resolve liabilities through their existing lifecycle.
274
+ Case holds affect age expiry, not explicit erasure or contract/lifecycle destruction.
275
+ The active-service window does not extend any contractual maximum for copies after
276
+ explicit destruction, including a 90-day post-destruction limit where applicable.
277
+
278
+ The wire fields and routes are additive within protocol version 1. Existing explicit
279
+ retention settings retain their value. The new default also applies to existing
280
+ unredacted terminal rows; previously removed content stays unavailable. Upgrade
281
+ all replicas before relying on holds: older SDKs do not recognize the hold fence.
282
+ Do not downgrade or mix old replicas with active holds unless automatic sweeping
283
+ is disabled on those replicas. The SQL export format is versioned independently
284
+ of the package and is not a raw database backup or a content archive.
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-P6QON7ER.js";
7
7
  import {
8
8
  x402NetworkByCaip2
9
- } from "./chunk-RLTSCKRI.js";
9
+ } from "./chunk-BPYMEMQX.js";
10
10
  import {
11
11
  diagnosticConsole
12
12
  } from "./chunk-ETXDTUJO.js";
@@ -0,0 +1,258 @@
1
+ import {
2
+ StepCache
3
+ } from "./chunk-BQJIFRAZ.js";
4
+
5
+ // src/sdk/server/job.ts
6
+ function toJobRecord(job) {
7
+ return {
8
+ id: job.id,
9
+ tags: job.tags,
10
+ capability: job.capability,
11
+ input: job.input,
12
+ params: job.params,
13
+ requesterId: job.requesterId,
14
+ requesterTokenHash: job.requesterTokenHash,
15
+ requesterToken: job.requesterToken,
16
+ requestFingerprint: job.requestFingerprint,
17
+ requesterPubkey: job.requesterPubkey,
18
+ requestId: job.requestId,
19
+ authRequestPath: job.authRequestPath,
20
+ status: job.status,
21
+ summary: job.summary,
22
+ timingKnown: job.timingKnown,
23
+ endedBy: job.endedBy,
24
+ terminalAtMs: job.terminalAtMs,
25
+ callerWaitMs: job.callerWaitMs,
26
+ awaitingInputAtMs: job.awaitingInputAtMs,
27
+ messages: job.messages,
28
+ seq: job.seq,
29
+ paidMsats: job.paidMsats,
30
+ paymentMint: job.paymentMint,
31
+ paymentRail: job.paymentRail,
32
+ paymentTxHash: job.paymentTxHash,
33
+ paymentTransactionHash: job.paymentTransactionHash,
34
+ nativeAmount: job.nativeAmount,
35
+ nativeAsset: job.nativeAsset,
36
+ cashuFlow: job.cashuFlow,
37
+ costAmountMicro: job.costAmountMicro,
38
+ costCurrency: job.costCurrency,
39
+ costRevision: job.costRevision,
40
+ creditId: job.creditId,
41
+ drawId: job.drawId,
42
+ fundingReceipt: job.fundingReceipt,
43
+ fundingCredit: job.fundingCredit,
44
+ receivedProofs: job.receivedProofs,
45
+ pendingPaymentMsats: job.pendingPaymentMsats,
46
+ pendingMppChallengeIds: job.pendingMppChallengeIds,
47
+ pendingX402Nonce: job.pendingX402Nonce,
48
+ pendingX402AmountUsdcMicro: job.pendingX402AmountUsdcMicro,
49
+ pendingPaymentFiatMicro: job.pendingPaymentFiatMicro,
50
+ pendingPaymentFiatCurrency: job.pendingPaymentFiatCurrency,
51
+ askedTopUpMicro: job.askedTopUpMicro,
52
+ askedTopUpCurrency: job.askedTopUpCurrency,
53
+ topUpCapUnenforcedReason: job.topUpCapUnenforcedReason,
54
+ requiredMsats: job.requiredMsats,
55
+ receipt: job.receipt,
56
+ stepCache: job.stepCache.serialize(),
57
+ state: job.state,
58
+ createdAt: job.createdAt,
59
+ lastActivityAt: job.lastActivityAt
60
+ };
61
+ }
62
+ function fromJobRecord(record) {
63
+ return {
64
+ id: record.id,
65
+ tags: record.tags,
66
+ capability: record.capability,
67
+ input: record.input,
68
+ params: record.params,
69
+ requesterId: record.requesterId,
70
+ requesterTokenHash: record.requesterTokenHash,
71
+ status: record.status,
72
+ summary: record.summary,
73
+ timingKnown: record.timingKnown,
74
+ endedBy: record.endedBy,
75
+ terminalAtMs: record.terminalAtMs,
76
+ callerWaitMs: record.callerWaitMs,
77
+ awaitingInputAtMs: record.awaitingInputAtMs,
78
+ messages: [...record.messages],
79
+ seq: record.seq,
80
+ paidMsats: record.paidMsats,
81
+ paymentMint: record.paymentMint,
82
+ paymentRail: record.paymentRail,
83
+ paymentTxHash: record.paymentTxHash,
84
+ paymentTransactionHash: record.paymentTransactionHash,
85
+ nativeAmount: record.nativeAmount,
86
+ nativeAsset: record.nativeAsset,
87
+ cashuFlow: record.cashuFlow,
88
+ costAmountMicro: record.costAmountMicro,
89
+ costCurrency: record.costCurrency,
90
+ costRevision: record.costRevision,
91
+ creditId: record.creditId,
92
+ drawId: record.drawId,
93
+ fundingReceipt: record.fundingReceipt,
94
+ fundingCredit: record.fundingCredit,
95
+ receivedProofs: [...record.receivedProofs],
96
+ pendingPaymentMsats: record.pendingPaymentMsats,
97
+ pendingMppChallengeIds: record.pendingMppChallengeIds ? [...record.pendingMppChallengeIds] : void 0,
98
+ requesterToken: record.requesterToken,
99
+ requestFingerprint: record.requestFingerprint,
100
+ requesterPubkey: record.requesterPubkey,
101
+ requestId: record.requestId,
102
+ authRequestPath: record.authRequestPath,
103
+ pendingX402Nonce: record.pendingX402Nonce,
104
+ pendingX402AmountUsdcMicro: record.pendingX402AmountUsdcMicro,
105
+ pendingPaymentFiatMicro: record.pendingPaymentFiatMicro,
106
+ pendingPaymentFiatCurrency: record.pendingPaymentFiatCurrency,
107
+ askedTopUpMicro: record.askedTopUpMicro,
108
+ askedTopUpCurrency: record.askedTopUpCurrency,
109
+ topUpCapUnenforcedReason: record.topUpCapUnenforcedReason,
110
+ requiredMsats: record.requiredMsats,
111
+ receipt: record.receipt,
112
+ listeners: /* @__PURE__ */ new Set(),
113
+ abort: new AbortController(),
114
+ messageAppender: void 0,
115
+ messageAppenderTail: void 0,
116
+ stepCache: StepCache.deserialize(record.stepCache),
117
+ state: record.state,
118
+ pendingPrompts: /* @__PURE__ */ new Map(),
119
+ pendingPayment: null,
120
+ replayHighSeq: null,
121
+ replayProviderSkip: 0,
122
+ replayPaymentIndex: 0,
123
+ createdAt: record.createdAt,
124
+ lastActivityAt: record.lastActivityAt
125
+ };
126
+ }
127
+ function abortJob(job, reason) {
128
+ job.abort.abort(new JobCancelledError(reason));
129
+ }
130
+ var JobCancelledError = class extends Error {
131
+ constructor(message = "Job cancelled") {
132
+ super(message);
133
+ this.name = "JobCancelledError";
134
+ }
135
+ };
136
+ function providerMessage(job, type, content) {
137
+ if (job.replayProviderSkip > 0) {
138
+ job.replayProviderSkip--;
139
+ return;
140
+ }
141
+ const msg = localMessage(job, "provider", type, content);
142
+ if (type === "payment-request") {
143
+ const paymentRequest = content;
144
+ const pendingPaymentDelta = typeof paymentRequest.amount_msats === "number" ? paymentRequest.amount_msats : void 0;
145
+ const pendingMppChallengeIds = paymentRequest.tempo?.challenges?.map((challenge) => challenge.id).filter((id) => typeof id === "string");
146
+ job.messageAppender?.(msg, {
147
+ awaitingInput: job.status === "awaiting-input",
148
+ pendingPaymentDelta,
149
+ pendingMppChallengeIds,
150
+ pendingX402Nonce: typeof paymentRequest.x402?.nonce === "string" ? paymentRequest.x402.nonce : void 0,
151
+ pendingX402AmountUsdcMicro: typeof paymentRequest.x402?.required_usdc_micro === "string" ? paymentRequest.x402.required_usdc_micro : void 0,
152
+ // read off the job rather than the content — the fiat envelope
153
+ // is ledger state, not part of the wire shape, and `requestPayment` has
154
+ // already pinned it on the job by the time this fires (same ordering the
155
+ // `pendingPaymentMsats` comment above describes).
156
+ pendingPaymentFiatMicro: job.pendingPaymentFiatMicro,
157
+ pendingPaymentFiatCurrency: job.pendingPaymentFiatCurrency
158
+ });
159
+ return;
160
+ }
161
+ job.messageAppender?.(msg, type === "prompt" ? { awaitingInput: job.status === "awaiting-input" } : void 0);
162
+ }
163
+ function localMessage(job, from, type, content) {
164
+ job.seq++;
165
+ const msg = {
166
+ seq: job.seq,
167
+ from,
168
+ timestamp: Math.floor(Date.now() / 1e3),
169
+ type,
170
+ content
171
+ };
172
+ job.messages.push(msg);
173
+ for (const listener of job.listeners) {
174
+ listener(msg);
175
+ }
176
+ return msg;
177
+ }
178
+ function isTerminal(status) {
179
+ return status === "completed" || status === "failed" || status === "cancelled";
180
+ }
181
+ function isYieldMessage(msg) {
182
+ return msg.type === "prompt" || msg.type === "payment-request" || msg.type === "working" || msg.type === "complete" || msg.type === "cancel";
183
+ }
184
+
185
+ // src/sdk/server/content-retention.ts
186
+ import { randomUUID } from "crypto";
187
+ var ContentHoldError = class extends Error {
188
+ constructor(code) {
189
+ super(code);
190
+ this.code = code;
191
+ }
192
+ code;
193
+ };
194
+ function isContentHoldStore(store) {
195
+ return "getContentSnapshot" in store && "mutateContentHold" in store;
196
+ }
197
+ function contentMetadata(job, retentionDays, holds = []) {
198
+ const baseline = baselineContentExpiry(job, retentionDays);
199
+ return {
200
+ content_expires_at: holds.some((hold) => hold.cleared_at === null) ? null : baseline,
201
+ content_available: job.redactedAt === void 0
202
+ };
203
+ }
204
+ function baselineContentExpiry(job, retentionDays) {
205
+ return retentionDays > 0 && isTerminal(job.status) ? job.lastActivityAt + retentionDays * 864e5 : null;
206
+ }
207
+ function applyContentHoldMutation(job, history, mutation) {
208
+ if (job.redactedAt !== void 0) throw new ContentHoldError("content_unavailable");
209
+ const result = structuredClone(history);
210
+ let hold;
211
+ if (mutation.action === "placed") {
212
+ hold = {
213
+ id: randomUUID(),
214
+ job_id: job.id,
215
+ purpose: mutation.purpose,
216
+ reason: mutation.reason,
217
+ actor: mutation.actor,
218
+ created_at: mutation.at,
219
+ review_due_at: mutation.review_due_at,
220
+ cleared_at: null
221
+ };
222
+ result.holds.push(hold);
223
+ } else {
224
+ const existing = result.holds.find((item) => item.id === mutation.hold_id && item.cleared_at === null);
225
+ if (!existing) throw new ContentHoldError("hold_not_active");
226
+ hold = existing;
227
+ if (mutation.action === "cleared") hold.cleared_at = mutation.at;
228
+ else hold.review_due_at = mutation.review_due_at;
229
+ hold.reason = mutation.reason;
230
+ }
231
+ result.events.push({
232
+ id: randomUUID(),
233
+ hold_id: hold.id,
234
+ job_id: job.id,
235
+ action: mutation.action,
236
+ actor: mutation.actor,
237
+ reason: mutation.reason,
238
+ at: mutation.at,
239
+ review_due_at: hold.review_due_at
240
+ });
241
+ return result;
242
+ }
243
+
244
+ export {
245
+ toJobRecord,
246
+ fromJobRecord,
247
+ abortJob,
248
+ JobCancelledError,
249
+ providerMessage,
250
+ localMessage,
251
+ isTerminal,
252
+ isYieldMessage,
253
+ ContentHoldError,
254
+ isContentHoldStore,
255
+ contentMetadata,
256
+ baselineContentExpiry,
257
+ applyContentHoldMutation
258
+ };
@@ -1,6 +1,49 @@
1
- import {
2
- DvmError
3
- } from "./chunk-LIRAHP7B.js";
1
+ // src/lib/errors.ts
2
+ var DvmError = class extends Error {
3
+ constructor(code, message, hint, data, display, proseSources) {
4
+ super(message);
5
+ this.code = code;
6
+ this.hint = hint;
7
+ this.data = data;
8
+ this.display = display;
9
+ this.proseSources = proseSources;
10
+ this.name = "DvmError";
11
+ }
12
+ code;
13
+ hint;
14
+ data;
15
+ display;
16
+ proseSources;
17
+ };
18
+ function providerProseSources(...sources) {
19
+ const provenance = {};
20
+ for (const source of sources) {
21
+ if (!source) continue;
22
+ for (const field of ["message", "hint", "display"]) {
23
+ if (typeof source[field] === "string") provenance[field] = "provider";
24
+ }
25
+ }
26
+ return Object.keys(provenance).length > 0 ? provenance : void 0;
27
+ }
28
+ function isAbortError(err) {
29
+ return err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError");
30
+ }
31
+ function transportErrorCode(err) {
32
+ let current = err;
33
+ const seen = /* @__PURE__ */ new Set();
34
+ for (let depth = 0; depth < 5 && isErrorLike(current) && !seen.has(current); depth++) {
35
+ seen.add(current);
36
+ const code = current.code;
37
+ if (typeof code === "string" && /^(?:E[A-Z0-9_]{1,63}|UND_ERR_[A-Z0-9_]{1,63})$/.test(code)) {
38
+ return code;
39
+ }
40
+ current = current.cause;
41
+ }
42
+ return void 0;
43
+ }
44
+ function isErrorLike(value) {
45
+ return (typeof value === "object" || typeof value === "function") && value !== null;
46
+ }
4
47
 
5
48
  // src/lib/x402/networks.ts
6
49
  var X402_NETWORKS = [
@@ -99,6 +142,10 @@ var BY_CAIP2 = new Map(X402_NETWORKS.map((n) => [n.caip2, n]));
99
142
  var BY_SLUG = new Map(X402_NETWORKS.map((n) => [n.slug, n]));
100
143
 
101
144
  export {
145
+ DvmError,
146
+ providerProseSources,
147
+ isAbortError,
148
+ transportErrorCode,
102
149
  X402_NETWORKS,
103
150
  x402NetworkByCaip2,
104
151
  x402NetworkBySlug,
@@ -0,0 +1,36 @@
1
+ // src/sdk/step-cache.ts
2
+ var StepCache = class _StepCache {
3
+ cache = /* @__PURE__ */ new Map();
4
+ has(id) {
5
+ return this.cache.has(id);
6
+ }
7
+ get(id) {
8
+ return this.getRecord(id).value;
9
+ }
10
+ getRecord(id) {
11
+ const record = this.cache.get(id);
12
+ if (record === void 0) throw new Error(`StepCache: no cached result for step "${id}"`);
13
+ return record;
14
+ }
15
+ set(id, value, costs) {
16
+ this.cache.set(id, {
17
+ id,
18
+ value,
19
+ ...costs !== void 0 && costs.length > 0 ? { costs: costs.map(({ amount, currency }) => ({ amount, currency })) } : {}
20
+ });
21
+ }
22
+ serialize() {
23
+ return [...this.cache.values()];
24
+ }
25
+ static deserialize(records) {
26
+ const cache = new _StepCache();
27
+ for (const { id, value, costs } of records) {
28
+ cache.set(id, value, costs);
29
+ }
30
+ return cache;
31
+ }
32
+ };
33
+
34
+ export {
35
+ StepCache
36
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  x402NetworkByCaip2,
3
3
  x402NetworkBySlug
4
- } from "./chunk-RLTSCKRI.js";
4
+ } from "./chunk-BPYMEMQX.js";
5
5
 
6
6
  // src/lib/x402/constants.ts
7
7
  var X402_DEFAULT_FACILITATOR = "https://x402.org/facilitator";
@@ -204,6 +204,118 @@ function isOptionalRecord(value) {
204
204
  return value === void 0 || value === null || isRecord(value);
205
205
  }
206
206
 
207
+ // src/defaults.ts
208
+ import { homedir } from "os";
209
+ import { join } from "path";
210
+ var CONFIG_DIR = process.env.DVM_CONFIG_DIR ?? join(homedir(), ".dvm");
211
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
212
+ var CONFIG_BACKUP_FILE = join(CONFIG_DIR, "config.json.bak");
213
+ var CONFIG_AUDIT_FILE = join(CONFIG_DIR, "config-audit.jsonl");
214
+ var CONFIG_PINNED_FILE = join(CONFIG_DIR, "config.json.pinned");
215
+ var JOBS_FILE = join(CONFIG_DIR, "jobs.json");
216
+ var REQUEST_INTENTS_DIR = join(CONFIG_DIR, "request-intents");
217
+ var FAVORITES_FILE = join(CONFIG_DIR, "favorites.json");
218
+ var MESSAGES_DIR = join(CONFIG_DIR, "messages");
219
+ var RECEIPTS_FILE = join(CONFIG_DIR, "receipts.jsonl");
220
+ var CREDITS_FILE = join(CONFIG_DIR, "credits.json");
221
+ var X402_CHANNELS_FILE = join(CONFIG_DIR, "x402-channels.json");
222
+ var TEMPO_CHANNELS_FILE = join(CONFIG_DIR, "tempo-channels.json");
223
+ var FEEDBACK_NUDGES_FILE = join(CONFIG_DIR, "feedback-nudges.json");
224
+ var RECOVERY_MNEMONIC_FILE = join(CONFIG_DIR, "recovery.mnemonic");
225
+ var WALLET_FILE = join(CONFIG_DIR, "wallet.json");
226
+ var AGENT_MNEMONIC_FILE = join(CONFIG_DIR, "wallet.mnemonic");
227
+ var IDENTITIES_FILE = join(CONFIG_DIR, "identities.json");
228
+ var PAIRINGS_DIR = join(CONFIG_DIR, "pairings");
229
+ var BUDGET_FILE = join(CONFIG_DIR, "budget.json");
230
+ var BTC_RATE_FILE = join(CONFIG_DIR, "btc-rate.json");
231
+ var ADMIN_DIR = join(CONFIG_DIR, "admin");
232
+ var PROXY_STATE_FILE = join(ADMIN_DIR, "proxy.json");
233
+ var BUILDER_CONFIG_DIR = process.env.DVMKIT_CONFIG_DIR ?? join(homedir(), ".dvmkit");
234
+ var BUILDER_FILE = join(BUILDER_CONFIG_DIR, "builder.json");
235
+ var BUILDER_MNEMONIC_FILE = join(BUILDER_CONFIG_DIR, "recovery.mnemonic");
236
+ var BUILDER_KEY_FILE = join(BUILDER_CONFIG_DIR, "builder.key");
237
+ var MELT_PENDING_LOG_FILE = join(BUILDER_CONFIG_DIR, "melt-pending.log");
238
+ var DRAIN_PARK_LOG_FILE = join(BUILDER_CONFIG_DIR, "drain-park.log");
239
+ var DRAIN_HOLD_STATE_FILE = join(BUILDER_CONFIG_DIR, "drain-hold.json");
240
+ var MELT_SUCCESS_FILE = join(BUILDER_CONFIG_DIR, "melt-success.json");
241
+
242
+ // src/lib/config-dir.ts
243
+ import { chmodSync, mkdirSync, statSync } from "fs";
244
+ var CONFIG_DIR_MODE = 448;
245
+ function ensureConfigDir() {
246
+ ensureConfigSubdir(CONFIG_DIR);
247
+ }
248
+ function ensureConfigSubdir(path) {
249
+ mkdirSync(path, { recursive: true, mode: CONFIG_DIR_MODE });
250
+ }
251
+ function repairConfigDirMode() {
252
+ const base = {
253
+ path: CONFIG_DIR,
254
+ exists: false,
255
+ modeOctal: null,
256
+ repaired: false,
257
+ skippedNonPosix: process.platform === "win32"
258
+ };
259
+ if (base.skippedNonPosix) return base;
260
+ let mode;
261
+ try {
262
+ mode = statSync(CONFIG_DIR).mode & 511;
263
+ } catch (err) {
264
+ const message = err instanceof Error ? err.message : String(err);
265
+ const missing = err.code === "ENOENT";
266
+ return missing ? base : { ...base, error: message };
267
+ }
268
+ if ((mode & 63) === 0) {
269
+ return { ...base, exists: true, modeOctal: toOctal(mode) };
270
+ }
271
+ try {
272
+ chmodSync(CONFIG_DIR, CONFIG_DIR_MODE);
273
+ } catch (err) {
274
+ return {
275
+ ...base,
276
+ exists: true,
277
+ modeOctal: toOctal(mode),
278
+ error: err instanceof Error ? err.message : String(err)
279
+ };
280
+ }
281
+ return {
282
+ ...base,
283
+ exists: true,
284
+ modeOctal: toOctal(CONFIG_DIR_MODE),
285
+ previousModeOctal: toOctal(mode),
286
+ repaired: true
287
+ };
288
+ }
289
+ function toOctal(mode) {
290
+ return "0" + mode.toString(8).padStart(3, "0");
291
+ }
292
+
293
+ // src/lib/payment-rails/rpc.ts
294
+ function resolveRpcOverride(envValue, bundled) {
295
+ const override = envValue?.trim() ?? "";
296
+ return override.length > 0 ? { url: override, source: "override" } : { url: bundled, source: "bundled" };
297
+ }
298
+ function resolveRpcHttpTransportOptions(source, envValue) {
299
+ if (source !== "override" || !envValue?.trim()) return void 0;
300
+ let parsed;
301
+ try {
302
+ parsed = JSON.parse(envValue);
303
+ } catch {
304
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
305
+ }
306
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
307
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
308
+ const headers = {};
309
+ for (const [name, value] of Object.entries(parsed)) {
310
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || typeof value !== "string" || value.length === 0 || /[\r\n]/.test(value))
311
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
312
+ headers[name] = value;
313
+ }
314
+ if (Object.keys(headers).length === 0)
315
+ throw new Error("RPC headers must be a JSON object of non-empty string values.");
316
+ return { fetchOptions: { headers } };
317
+ }
318
+
207
319
  export {
208
320
  X402_DEFAULT_FACILITATOR,
209
321
  X402_VERSION,
@@ -226,5 +338,34 @@ export {
226
338
  encodePaymentRequiredHeader,
227
339
  decodePaymentRequiredHeader,
228
340
  exactEvmAuthorization,
229
- encodeSettleResponseHeader
341
+ encodeSettleResponseHeader,
342
+ CONFIG_DIR,
343
+ CONFIG_FILE,
344
+ CONFIG_BACKUP_FILE,
345
+ CONFIG_AUDIT_FILE,
346
+ CONFIG_PINNED_FILE,
347
+ JOBS_FILE,
348
+ REQUEST_INTENTS_DIR,
349
+ FAVORITES_FILE,
350
+ MESSAGES_DIR,
351
+ RECEIPTS_FILE,
352
+ CREDITS_FILE,
353
+ X402_CHANNELS_FILE,
354
+ TEMPO_CHANNELS_FILE,
355
+ FEEDBACK_NUDGES_FILE,
356
+ RECOVERY_MNEMONIC_FILE,
357
+ WALLET_FILE,
358
+ AGENT_MNEMONIC_FILE,
359
+ IDENTITIES_FILE,
360
+ PAIRINGS_DIR,
361
+ BUDGET_FILE,
362
+ BTC_RATE_FILE,
363
+ BUILDER_CONFIG_DIR,
364
+ BUILDER_FILE,
365
+ BUILDER_KEY_FILE,
366
+ ensureConfigDir,
367
+ ensureConfigSubdir,
368
+ repairConfigDirMode,
369
+ resolveRpcOverride,
370
+ resolveRpcHttpTransportOptions
230
371
  };