@vapi-network/core 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +14 -1
  2. package/dist/agent-link.d.ts.map +1 -1
  3. package/dist/agent-link.js +6 -1
  4. package/dist/agent-link.js.map +1 -1
  5. package/dist/agent-profile.d.ts +4 -0
  6. package/dist/agent-profile.d.ts.map +1 -1
  7. package/dist/api-key.js +47 -0
  8. package/dist/api-key.js.map +1 -1
  9. package/dist/backup.d.ts +2 -0
  10. package/dist/backup.d.ts.map +1 -1
  11. package/dist/backup.js +65 -0
  12. package/dist/backup.js.map +1 -1
  13. package/dist/cloud-backup.js +71 -1
  14. package/dist/cloud-backup.js.map +1 -1
  15. package/dist/config.d.ts +9 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/docs.d.ts +40 -0
  18. package/dist/docs.d.ts.map +1 -0
  19. package/dist/funding-policy.d.ts +29 -0
  20. package/dist/funding-policy.d.ts.map +1 -0
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +718 -35
  24. package/dist/index.js.map +3 -3
  25. package/dist/router-client.js +230 -33
  26. package/dist/router-client.js.map +2 -2
  27. package/dist/secrets.js +29 -1
  28. package/dist/secrets.js.map +1 -1
  29. package/dist/spend-policy.d.ts +41 -7
  30. package/dist/spend-policy.d.ts.map +1 -1
  31. package/dist/support-report.d.ts +1 -1
  32. package/dist/tasks/actions.d.ts +152 -0
  33. package/dist/tasks/actions.d.ts.map +1 -0
  34. package/dist/tasks/canonical-json.d.ts +6 -0
  35. package/dist/tasks/canonical-json.d.ts.map +1 -0
  36. package/dist/tasks/chain-error.d.ts +6 -0
  37. package/dist/tasks/chain-error.d.ts.map +1 -0
  38. package/dist/tasks/chain-port.d.ts +88 -0
  39. package/dist/tasks/chain-port.d.ts.map +1 -0
  40. package/dist/tasks/chain-verification.d.ts +36 -0
  41. package/dist/tasks/chain-verification.d.ts.map +1 -0
  42. package/dist/tasks/chain.d.ts +22 -0
  43. package/dist/tasks/chain.d.ts.map +1 -0
  44. package/dist/tasks/client.d.ts +76 -0
  45. package/dist/tasks/client.d.ts.map +1 -0
  46. package/dist/tasks/delivery-manifest.d.ts +24 -0
  47. package/dist/tasks/delivery-manifest.d.ts.map +1 -0
  48. package/dist/tasks/dispute-fee.d.ts +4 -0
  49. package/dist/tasks/dispute-fee.d.ts.map +1 -0
  50. package/dist/tasks/index.d.ts +22 -0
  51. package/dist/tasks/index.d.ts.map +1 -0
  52. package/dist/tasks/money.d.ts +17 -0
  53. package/dist/tasks/money.d.ts.map +1 -0
  54. package/dist/tasks/operation-support.d.ts +127 -0
  55. package/dist/tasks/operation-support.d.ts.map +1 -0
  56. package/dist/tasks/operations.d.ts +722 -0
  57. package/dist/tasks/operations.d.ts.map +1 -0
  58. package/dist/tasks/pending-transactions.d.ts +52 -0
  59. package/dist/tasks/pending-transactions.d.ts.map +1 -0
  60. package/dist/tasks/plan-guard.d.ts +326 -0
  61. package/dist/tasks/plan-guard.d.ts.map +1 -0
  62. package/dist/tasks/rpc.d.ts +8 -0
  63. package/dist/tasks/rpc.d.ts.map +1 -0
  64. package/dist/tasks/scope-bindings.d.ts +20 -0
  65. package/dist/tasks/scope-bindings.d.ts.map +1 -0
  66. package/dist/tasks/scope-terms.d.ts +75 -0
  67. package/dist/tasks/scope-terms.d.ts.map +1 -0
  68. package/dist/tasks/trusted-deployments.d.ts +14 -0
  69. package/dist/tasks/trusted-deployments.d.ts.map +1 -0
  70. package/dist/tasks/types.d.ts +4439 -0
  71. package/dist/tasks/types.d.ts.map +1 -0
  72. package/dist/tasks/verbs.d.ts +4 -0
  73. package/dist/tasks/verbs.d.ts.map +1 -0
  74. package/dist/tasks.js +43906 -0
  75. package/dist/tasks.js.map +6 -0
  76. package/dist/transfer.d.ts.map +1 -1
  77. package/package.json +5 -1
@@ -37290,6 +37290,14 @@ var configSchema = external_exports.object({
37290
37290
  })
37291
37291
  ).optional(),
37292
37292
  allowPrivateNetwork: external_exports.boolean().default(false).optional(),
37293
+ tasksEscrowDurationOverrides: external_exports.record(
37294
+ external_exports.string().regex(/^\d+$/),
37295
+ external_exports.object({
37296
+ workDurationSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
37297
+ reviewWindowSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional()
37298
+ }).strict()
37299
+ ).optional(),
37300
+ tasksEscrowFactoryOverrides: external_exports.record(external_exports.string().regex(/^\d+$/), external_exports.string()).optional(),
37293
37301
  networks: external_exports.record(
37294
37302
  external_exports.string().refine((network) => isSupportedPaymentNetwork(network), {
37295
37303
  message: "Expected a supported eip155:<chainId> or Solana network identifier."
@@ -37308,6 +37316,17 @@ var configSchema = external_exports.object({
37308
37316
  spendCaps: spendCapsSchema,
37309
37317
  device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
37310
37318
  }).superRefine((config2, context) => {
37319
+ for (const [chainId, factory] of Object.entries(config2.tasksEscrowFactoryOverrides ?? {})) {
37320
+ try {
37321
+ getAddress(factory);
37322
+ } catch {
37323
+ context.addIssue({
37324
+ code: "custom",
37325
+ path: ["tasksEscrowFactoryOverrides", chainId],
37326
+ message: "Expected a valid EVM escrow factory address."
37327
+ });
37328
+ }
37329
+ }
37311
37330
  for (const [network, configured] of Object.entries(config2.networks)) {
37312
37331
  if (isSolanaNetwork(network)) {
37313
37332
  if (configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
@@ -37454,6 +37473,7 @@ function getDefaultConfig(source = process.env, options = {}) {
37454
37473
  const registry2 = registryEndpoints(source.VAPI_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL);
37455
37474
  const requested = new Set(options.networks ?? ["base"]);
37456
37475
  const networks = {};
37476
+ const tasksEscrowFactoryOverrides = tasksFactoryOverridesFromEnv(source);
37457
37477
  if (requested.has("base") || requested.has(BASE_MAINNET_CAIP2)) {
37458
37478
  networks[BASE_MAINNET_CAIP2] = {
37459
37479
  rpcUrl: source.BASE_RPC_URL?.trim() || NETWORKS[BASE_MAINNET_CAIP2].publicRpcUrl,
@@ -37491,6 +37511,8 @@ function getDefaultConfig(source = process.env, options = {}) {
37491
37511
  marketplaceDiscoveryUrl: source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim() || registry2.marketplaceDiscoveryUrl,
37492
37512
  registryFallbacks: DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback })),
37493
37513
  allowPrivateNetwork: false,
37514
+ tasksEscrowFactoryOverrides,
37515
+ tasksEscrowDurationOverrides: tasksDurationOverridesFromEnv(source),
37494
37516
  networks,
37495
37517
  spendCaps: { ...DEFAULT_SPEND_CAPS }
37496
37518
  };
@@ -37508,8 +37530,10 @@ async function loadConfig(path = getVapiPaths().config, source = process.env, op
37508
37530
  }
37509
37531
  const parsed = configSchema.parse(JSON.parse(raw));
37510
37532
  parsed.registryFallbacks ??= DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback }));
37533
+ parsed.tasksEscrowFactoryOverrides ??= {};
37511
37534
  const migrated = rewriteLegacyRegistryUrls(parsed);
37512
37535
  const config2 = migrated.config;
37536
+ config2.tasksEscrowFactoryOverrides ??= {};
37513
37537
  if (migrated.changes.length > 0 && options.notice) {
37514
37538
  options.notice(
37515
37539
  `${formatLegacyRegistryRewrites(migrated.changes)}
@@ -37523,6 +37547,14 @@ Run vapi init to write the new URLs to ${path}.`
37523
37547
  const discoveryUrl = source.VAPI_DISCOVERY_URL?.trim();
37524
37548
  const marketplaceDiscoveryUrl = source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim();
37525
37549
  const registryUrl = source.VAPI_REGISTRY_URL?.trim();
37550
+ Object.assign(config2.tasksEscrowFactoryOverrides, tasksFactoryOverridesFromEnv(source));
37551
+ config2.tasksEscrowDurationOverrides ??= {};
37552
+ for (const [chainId, overrides] of Object.entries(tasksDurationOverridesFromEnv(source))) {
37553
+ config2.tasksEscrowDurationOverrides[chainId] = {
37554
+ ...config2.tasksEscrowDurationOverrides[chainId],
37555
+ ...overrides
37556
+ };
37557
+ }
37526
37558
  if (config2.networks[BASE_MAINNET_CAIP2] && baseRpcUrl) {
37527
37559
  config2.networks[BASE_MAINNET_CAIP2].rpcUrl = baseRpcUrl;
37528
37560
  }
@@ -37563,6 +37595,31 @@ Run vapi init to write the new URLs to ${path}.`
37563
37595
  }
37564
37596
  return config2;
37565
37597
  }
37598
+ function tasksDurationOverridesFromEnv(source) {
37599
+ const result = {};
37600
+ for (const [name, raw] of Object.entries(source)) {
37601
+ const match = /^VAPI_TASKS_(WORK_DURATION|REVIEW_WINDOW)_SECONDS_(\d+)$/.exec(name);
37602
+ const value = raw?.trim();
37603
+ if (!match || !value) continue;
37604
+ const seconds = Number(value);
37605
+ if (!/^\d+$/.test(value) || !Number.isSafeInteger(seconds) || seconds <= 0)
37606
+ throw new Error(`Invalid trusted Tasks duration ${name}.`);
37607
+ const entry = result[match[2]] ??= {};
37608
+ if (match[1] === "WORK_DURATION") entry.workDurationSeconds = seconds;
37609
+ else entry.reviewWindowSeconds = seconds;
37610
+ }
37611
+ return result;
37612
+ }
37613
+ function tasksFactoryOverridesFromEnv(source) {
37614
+ const overrides = {};
37615
+ for (const [name, raw] of Object.entries(source)) {
37616
+ const match = /^VAPI_TASKS_ESCROW_FACTORY_(\d+)$/.exec(name);
37617
+ const value = raw?.trim();
37618
+ if (!match || !value) continue;
37619
+ overrides[match[1]] = getAddress(value);
37620
+ }
37621
+ return overrides;
37622
+ }
37566
37623
  function registryEndpoints(baseUrl) {
37567
37624
  const base = new URL(baseUrl);
37568
37625
  base.search = "";
@@ -37629,7 +37686,12 @@ var AgentLinkError = class extends Error {
37629
37686
  code;
37630
37687
  };
37631
37688
  var DEFAULT_AGENT_SCOPES = ["mcp:call", "router.use"];
37632
- var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([...DEFAULT_AGENT_SCOPES, "call.publish"]);
37689
+ var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([
37690
+ ...DEFAULT_AGENT_SCOPES,
37691
+ "call.publish",
37692
+ "tasks:read",
37693
+ "tasks:write"
37694
+ ]);
37633
37695
  var publicFetch = createPublicFetch({ allowPrivateNetwork: false });
37634
37696
  var agentSecretAccounts = (wallet) => ({
37635
37697
  tokens: `vapi.agent.${wallet}.tokens`,
@@ -38048,7 +38110,9 @@ var ledgerSchema = external_exports.object({
38048
38110
  });
38049
38111
  var spendReservationSchema = external_exports.object({
38050
38112
  id: external_exports.string().min(1),
38051
- amountAtomic: external_exports.string().regex(/^\d+$/)
38113
+ amountAtomic: external_exports.string().regex(/^\d+$/),
38114
+ kind: external_exports.enum(["payment", "escrow-funding", "dispute-fee"]).optional(),
38115
+ taskKey: external_exports.string().optional()
38052
38116
  });
38053
38117
  var ledgerRowSchema = ledgerSchema.extend({
38054
38118
  wallet: walletNameSchema.optional(),
@@ -38069,7 +38133,18 @@ var SpendCapError = class extends Error {
38069
38133
  function utcDateKey(now = /* @__PURE__ */ new Date()) {
38070
38134
  return now.toISOString().slice(0, 10);
38071
38135
  }
38072
- async function readStoredSpendLedgerRows(path, now) {
38136
+ function sumTaskReservations(rows, wallet, taskKey, excludeReservationId) {
38137
+ let total = 0n;
38138
+ for (const row of rows) {
38139
+ if (row.wallet !== wallet) continue;
38140
+ for (const reservation of row.reservations ?? []) {
38141
+ if (reservation.taskKey === taskKey && reservation.id !== excludeReservationId)
38142
+ total += BigInt(reservation.amountAtomic);
38143
+ }
38144
+ }
38145
+ return total;
38146
+ }
38147
+ async function readStoredSpendLedgerRows(path, now, allDays = false) {
38073
38148
  let raw;
38074
38149
  try {
38075
38150
  raw = await readFile5(path, "utf8");
@@ -38080,7 +38155,7 @@ async function readStoredSpendLedgerRows(path, now) {
38080
38155
  const parsed = ledgerFileSchema.parse(JSON.parse(raw));
38081
38156
  const rows = "rows" in parsed ? parsed.rows : [parsed];
38082
38157
  const today = utcDateKey(now);
38083
- return rows.filter((row) => row.date === today).map((row) => ({
38158
+ return rows.filter((row) => allDays || row.date === today).map((row) => ({
38084
38159
  date: row.date,
38085
38160
  spentAtomic: row.spentAtomic,
38086
38161
  wallet: row.wallet ?? DEFAULT_WALLET_NAME,
@@ -38091,49 +38166,164 @@ async function reserveSpend(amountAtomic, caps, options) {
38091
38166
  if (amountAtomic < 0n) {
38092
38167
  throw new Error("Spend amount cannot be negative.");
38093
38168
  }
38094
- const perCallAtomic = parseAtomicCap(caps.perCallAtomic, "per-call");
38095
- const perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
38096
- if (amountAtomic > perCallAtomic) {
38097
- throw new SpendCapError(
38098
- "per_call_cap_exceeded",
38099
- `Payment quote ${amountAtomic} atomic USDC exceeds the per-call cap ${perCallAtomic}. Refusing to sign.`
38100
- );
38169
+ const kind = options?.kind ?? "payment";
38170
+ let perDayAtomic;
38171
+ if (kind === "escrow-funding" || kind === "dispute-fee") {
38172
+ const maxPerTaskAtomic = options?.maxPerTaskAtomic;
38173
+ if (maxPerTaskAtomic === void 0 || maxPerTaskAtomic < 0n) {
38174
+ throw new Error("Escrow funding requires a non-negative per-task cap.");
38175
+ }
38176
+ if (amountAtomic > maxPerTaskAtomic) {
38177
+ throw new SpendCapError(
38178
+ "per_task_cap_exceeded",
38179
+ `Escrow funding ${amountAtomic} atomic USDC exceeds the per-task cap ${maxPerTaskAtomic}. Refusing to sign.`
38180
+ );
38181
+ }
38182
+ perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
38183
+ } else {
38184
+ const perCallAtomic = parseAtomicCap(caps.perCallAtomic, "per-call");
38185
+ perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
38186
+ if (amountAtomic > perCallAtomic) {
38187
+ throw new SpendCapError(
38188
+ "per_call_cap_exceeded",
38189
+ `Payment quote ${amountAtomic} atomic USDC exceeds the per-call cap ${perCallAtomic}. Refusing to sign.`
38190
+ );
38191
+ }
38101
38192
  }
38102
38193
  const ledgerPath = options?.ledgerPath ?? getAgentCashPaths().ledger;
38103
38194
  const now = options?.now ?? /* @__PURE__ */ new Date();
38104
38195
  const wallet = options?.wallet ?? DEFAULT_WALLET_NAME;
38105
38196
  return await withLedgerLock(ledgerPath, async () => {
38106
- const rows = await readStoredSpendLedgerRows(ledgerPath, now);
38107
- const current = rows.find((row) => row.wallet === wallet);
38197
+ const rows = await readStoredSpendLedgerRows(ledgerPath, now, true);
38198
+ const today = utcDateKey(now);
38199
+ const current = rows.find((row) => row.wallet === wallet && row.date === today);
38200
+ if ((kind === "escrow-funding" || kind === "dispute-fee") && options?.taskKey !== void 0) {
38201
+ const taskTotal = sumTaskReservations(
38202
+ rows,
38203
+ wallet,
38204
+ options.taskKey,
38205
+ options.reuseExistingEscrowReservation || options.resumeEscrowReservation ? options.reservationId : void 0
38206
+ ) + amountAtomic;
38207
+ if (taskTotal > options.maxPerTaskAtomic)
38208
+ throw new SpendCapError(
38209
+ "per_task_cap_exceeded",
38210
+ `New spend ${amountAtomic} atomic USDC would raise the running total for task ${options.taskKey} and wallet ${wallet} to ${taskTotal}, above the per-task cap ${options.maxPerTaskAtomic}. Refusing to sign.`
38211
+ );
38212
+ }
38108
38213
  if (options?.reservationId !== void 0) {
38109
- if (current?.reservations?.some((reservation) => reservation.id === options.reservationId)) {
38214
+ const existing = current?.reservations?.find(
38215
+ (reservation) => reservation.id === options.reservationId
38216
+ );
38217
+ if (existing && options.reuseExistingEscrowReservation) {
38218
+ if (kind !== "escrow-funding" && kind !== "dispute-fee" || existing.kind !== kind || existing.amountAtomic !== amountAtomic.toString() || existing.taskKey !== void 0 && existing.taskKey !== options.taskKey)
38219
+ throw new Error(
38220
+ `Spend reservation ${options.reservationId} does not match this funding.`
38221
+ );
38222
+ if (existing.taskKey === void 0 && options.taskKey !== void 0) {
38223
+ existing.taskKey = options.taskKey;
38224
+ await writeLedgerAtomically(ledgerPath, rows);
38225
+ }
38226
+ return {
38227
+ date: current.date,
38228
+ spentAtomic: current.spentAtomic,
38229
+ reservationReused: true
38230
+ };
38231
+ }
38232
+ if (existing) {
38110
38233
  throw new Error(`Spend reservation ${options.reservationId} already exists.`);
38111
38234
  }
38235
+ if (options.resumeEscrowReservation) {
38236
+ const resumed = options.resumeEscrowReservation;
38237
+ const historical = rows.find((row) => row.wallet === wallet && row.date === resumed.date);
38238
+ const stored = historical?.reservations?.find(
38239
+ (reservation) => reservation.id === resumed.id
38240
+ );
38241
+ if (kind !== "escrow-funding" && kind !== "dispute-fee" || resumed.id !== options.reservationId || resumed.wallet !== wallet || resumed.amountAtomic !== amountAtomic.toString() || resumed.date >= utcDateKey(now) || resumed.exposed !== true || resumed.invalidated === true || stored !== void 0 && (stored.kind !== kind || stored.amountAtomic !== amountAtomic.toString() || stored.taskKey !== void 0 && stored.taskKey !== options.taskKey))
38242
+ throw new Error(
38243
+ `Spend reservation ${options.reservationId} does not match this funding.`
38244
+ );
38245
+ if (stored?.taskKey === void 0 && options.taskKey !== void 0) {
38246
+ if (stored) stored.taskKey = options.taskKey;
38247
+ else {
38248
+ const reservation = {
38249
+ id: resumed.id,
38250
+ amountAtomic: resumed.amountAtomic,
38251
+ kind,
38252
+ taskKey: options.taskKey
38253
+ };
38254
+ if (historical)
38255
+ historical.reservations = [...historical.reservations ?? [], reservation];
38256
+ else
38257
+ rows.push({
38258
+ date: resumed.date,
38259
+ spentAtomic: resumed.amountAtomic,
38260
+ wallet,
38261
+ reservations: [reservation]
38262
+ });
38263
+ }
38264
+ await writeLedgerAtomically(ledgerPath, rows);
38265
+ }
38266
+ return {
38267
+ date: resumed.date,
38268
+ spentAtomic: current?.spentAtomic ?? "0",
38269
+ reservationReused: true
38270
+ };
38271
+ }
38112
38272
  }
38113
38273
  const spentAtomic = current?.spentAtomic ?? "0";
38114
38274
  const nextSpent = BigInt(spentAtomic) + amountAtomic;
38115
38275
  if (nextSpent > perDayAtomic) {
38116
38276
  throw new SpendCapError(
38117
38277
  "per_day_cap_exceeded",
38118
- `Payment quote ${amountAtomic} atomic USDC would raise today's spend to ${nextSpent} for wallet ${wallet}, above the per-day cap ${perDayAtomic}. Refusing to sign.`
38278
+ `${kind === "escrow-funding" ? "Escrow funding" : "Payment quote"} ${amountAtomic} atomic USDC would raise today's spend to ${nextSpent} for wallet ${wallet}, above the per-day cap ${perDayAtomic}. Refusing to sign.`
38119
38279
  );
38120
38280
  }
38281
+ const reservationId = options?.reservationId ?? (options?.taskKey === void 0 ? void 0 : randomBytes4(16).toString("hex"));
38121
38282
  const nextRow = {
38122
38283
  date: utcDateKey(now),
38123
38284
  spentAtomic: nextSpent.toString(),
38124
38285
  wallet,
38125
- ...(current?.reservations?.length ?? 0) === 0 && options?.reservationId === void 0 ? {} : {
38286
+ ...(current?.reservations?.length ?? 0) === 0 && reservationId === void 0 ? {} : {
38126
38287
  reservations: [
38127
38288
  ...current?.reservations ?? [],
38128
- ...options?.reservationId === void 0 ? [] : [{ id: options.reservationId, amountAtomic: amountAtomic.toString() }]
38289
+ ...reservationId === void 0 ? [] : [
38290
+ {
38291
+ id: reservationId,
38292
+ amountAtomic: amountAtomic.toString(),
38293
+ ...kind !== "payment" ? { kind } : {},
38294
+ ...options?.taskKey === void 0 ? {} : { taskKey: options.taskKey }
38295
+ }
38296
+ ]
38129
38297
  ]
38130
38298
  }
38131
38299
  };
38132
- const nextRows = [...rows.filter((row) => row.wallet !== wallet), nextRow].sort(
38133
- (a, b) => a.wallet.localeCompare(b.wallet)
38300
+ const otherRows = rows.filter((row) => row.wallet !== wallet || row.date !== today).map((row) => {
38301
+ if (row.wallet !== wallet || !options?.reuseExistingEscrowReservation || options.reservationId === void 0)
38302
+ return row;
38303
+ const previous = row.reservations?.find(
38304
+ (reservation) => reservation.id === options.reservationId
38305
+ );
38306
+ if (!previous) return row;
38307
+ if (kind !== "escrow-funding" && kind !== "dispute-fee" || previous.kind !== kind || previous.amountAtomic !== amountAtomic.toString() || previous.taskKey !== void 0 && previous.taskKey !== options.taskKey)
38308
+ throw new Error(
38309
+ `Spend reservation ${options.reservationId} does not match this funding.`
38310
+ );
38311
+ return {
38312
+ ...row,
38313
+ reservations: row.reservations.filter(
38314
+ (reservation) => reservation.id !== options.reservationId
38315
+ )
38316
+ };
38317
+ });
38318
+ const nextRows = [...otherRows, nextRow].sort(
38319
+ (a, b) => a.wallet.localeCompare(b.wallet) || a.date.localeCompare(b.date)
38134
38320
  );
38135
38321
  await writeLedgerAtomically(ledgerPath, nextRows);
38136
- return { date: nextRow.date, spentAtomic: nextRow.spentAtomic };
38322
+ return {
38323
+ date: nextRow.date,
38324
+ spentAtomic: nextRow.spentAtomic,
38325
+ ...options?.reservationId === void 0 && reservationId !== void 0 ? { reservationId } : {}
38326
+ };
38137
38327
  });
38138
38328
  }
38139
38329
  async function releaseSpend(amountAtomic, options) {
@@ -38145,35 +38335,40 @@ async function releaseSpend(amountAtomic, options) {
38145
38335
  const wallet = options.wallet ?? DEFAULT_WALLET_NAME;
38146
38336
  return await withLedgerLock(ledgerPath, async () => {
38147
38337
  const today = utcDateKey(now);
38148
- const rows = await readStoredSpendLedgerRows(ledgerPath, now);
38149
- const current = rows.find((row) => row.wallet === wallet);
38150
- if (options.reservedOn !== today) {
38338
+ const rows = await readStoredSpendLedgerRows(ledgerPath, now, true);
38339
+ const current = rows.find((row) => row.wallet === wallet && row.date === today);
38340
+ if (options.reservedOn !== today && options.reservationId === void 0) {
38151
38341
  return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
38152
38342
  }
38153
- const reservation = options.reservationId === void 0 ? void 0 : current?.reservations?.find((candidate) => candidate.id === options.reservationId);
38343
+ const reserved = rows.find((row) => row.wallet === wallet && row.date === options.reservedOn);
38344
+ const reservation = options.reservationId === void 0 ? void 0 : reserved?.reservations?.find((candidate) => candidate.id === options.reservationId);
38154
38345
  if (options.reservationId !== void 0 && reservation === void 0) {
38155
38346
  return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
38156
38347
  }
38157
38348
  if (reservation !== void 0 && reservation.amountAtomic !== amountAtomic.toString()) {
38158
38349
  throw new Error(`Spend reservation ${options.reservationId} has a different amount.`);
38159
38350
  }
38160
- const spentAtomic = BigInt(current?.spentAtomic ?? "0");
38351
+ const spentAtomic = BigInt(reserved?.spentAtomic ?? "0");
38161
38352
  const releasedAmount = reservation === void 0 ? amountAtomic : BigInt(reservation.amountAtomic);
38162
38353
  const nextSpent = spentAtomic > releasedAmount ? spentAtomic - releasedAmount : 0n;
38163
- const remainingReservations = current?.reservations?.filter(
38354
+ const remainingReservations = reserved?.reservations?.filter(
38164
38355
  (candidate) => candidate.id !== options.reservationId
38165
38356
  );
38166
38357
  const nextRow = {
38167
- date: today,
38358
+ date: options.reservedOn,
38168
38359
  spentAtomic: nextSpent.toString(),
38169
38360
  wallet,
38170
38361
  ...remainingReservations === void 0 || remainingReservations.length === 0 ? {} : { reservations: remainingReservations }
38171
38362
  };
38172
- const nextRows = [...rows.filter((row) => row.wallet !== wallet), nextRow].sort(
38173
- (a, b) => a.wallet.localeCompare(b.wallet)
38174
- );
38363
+ const nextRows = [
38364
+ ...rows.filter((row) => row.wallet !== wallet || row.date !== options.reservedOn),
38365
+ nextRow
38366
+ ].sort((a, b) => a.wallet.localeCompare(b.wallet) || a.date.localeCompare(b.date));
38175
38367
  await writeLedgerAtomically(ledgerPath, nextRows);
38176
- return { date: nextRow.date, spentAtomic: nextRow.spentAtomic };
38368
+ return {
38369
+ date: today,
38370
+ spentAtomic: options.reservedOn === today ? nextRow.spentAtomic : current?.spentAtomic ?? "0"
38371
+ };
38177
38372
  });
38178
38373
  }
38179
38374
  function parseAtomicCap(value, label) {
@@ -44435,7 +44630,9 @@ async function transferBetweenAccounts(args) {
44435
44630
  );
44436
44631
  reservedOn = reservation.date;
44437
44632
  } catch (error61) {
44438
- if (error61 instanceof SpendCapError) throw transferError(error61.code);
44633
+ if (error61 instanceof SpendCapError && isTransferErrorCode(error61.code)) {
44634
+ throw transferError(error61.code);
44635
+ }
44439
44636
  throw transferError("network_error");
44440
44637
  }
44441
44638
  }
@@ -46300,7 +46497,7 @@ var RunBudgetError = class extends Error {
46300
46497
  };
46301
46498
 
46302
46499
  // src/support-report.ts
46303
- var VAPI_CLIENT_VERSION = "0.8.0";
46500
+ var VAPI_CLIENT_VERSION = "0.9.0";
46304
46501
 
46305
46502
  // src/x402-pay.ts
46306
46503
  var X402PaymentError = class extends Error {