@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
package/dist/index.js CHANGED
@@ -39074,7 +39074,9 @@ function http(url2, config2 = {}) {
39074
39074
  // ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/index.js
39075
39075
  init_getAddress();
39076
39076
  init_isAddress();
39077
+ init_toBytes();
39077
39078
  init_toHex();
39079
+ init_keccak256();
39078
39080
 
39079
39081
  // src/x402-networks.ts
39080
39082
  var BASE_MAINNET_CAIP2 = "eip155:8453";
@@ -46438,6 +46440,8 @@ var DEFAULT_SPEND_CAPS = {
46438
46440
  perCallAtomic: "100000",
46439
46441
  perDayAtomic: "1000000"
46440
46442
  };
46443
+ var DEFAULT_MAX_PER_TASK_USD = 100;
46444
+ var DEFAULT_AUTO_RELEASE_BELOW_USD = 25;
46441
46445
  var DEFAULT_CEILING_ATOMIC = 5000000n;
46442
46446
  var atomicString = external_exports.string().regex(/^\d+$/, "Expected non-negative atomic units.");
46443
46447
  var spendCapsSchema = external_exports.object({
@@ -46454,6 +46458,14 @@ var configSchema = external_exports.object({
46454
46458
  })
46455
46459
  ).optional(),
46456
46460
  allowPrivateNetwork: external_exports.boolean().default(false).optional(),
46461
+ tasksEscrowDurationOverrides: external_exports.record(
46462
+ external_exports.string().regex(/^\d+$/),
46463
+ external_exports.object({
46464
+ workDurationSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
46465
+ reviewWindowSeconds: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional()
46466
+ }).strict()
46467
+ ).optional(),
46468
+ tasksEscrowFactoryOverrides: external_exports.record(external_exports.string().regex(/^\d+$/), external_exports.string()).optional(),
46457
46469
  networks: external_exports.record(
46458
46470
  external_exports.string().refine((network) => isSupportedPaymentNetwork(network), {
46459
46471
  message: "Expected a supported eip155:<chainId> or Solana network identifier."
@@ -46472,6 +46484,17 @@ var configSchema = external_exports.object({
46472
46484
  spendCaps: spendCapsSchema,
46473
46485
  device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
46474
46486
  }).superRefine((config2, context) => {
46487
+ for (const [chainId, factory] of Object.entries(config2.tasksEscrowFactoryOverrides ?? {})) {
46488
+ try {
46489
+ getAddress(factory);
46490
+ } catch {
46491
+ context.addIssue({
46492
+ code: "custom",
46493
+ path: ["tasksEscrowFactoryOverrides", chainId],
46494
+ message: "Expected a valid EVM escrow factory address."
46495
+ });
46496
+ }
46497
+ }
46475
46498
  for (const [network, configured] of Object.entries(config2.networks)) {
46476
46499
  if (isSolanaNetwork(network)) {
46477
46500
  if (configured.usdc !== NETWORKS[SOLANA_MAINNET_CAIP2].usdc) {
@@ -46634,6 +46657,7 @@ function getDefaultConfig(source = process.env, options = {}) {
46634
46657
  const registry2 = registryEndpoints(source.VAPI_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL);
46635
46658
  const requested = new Set(options.networks ?? ["base"]);
46636
46659
  const networks = {};
46660
+ const tasksEscrowFactoryOverrides = tasksFactoryOverridesFromEnv(source);
46637
46661
  if (requested.has("base") || requested.has(BASE_MAINNET_CAIP2)) {
46638
46662
  networks[BASE_MAINNET_CAIP2] = {
46639
46663
  rpcUrl: source.BASE_RPC_URL?.trim() || NETWORKS[BASE_MAINNET_CAIP2].publicRpcUrl,
@@ -46671,6 +46695,8 @@ function getDefaultConfig(source = process.env, options = {}) {
46671
46695
  marketplaceDiscoveryUrl: source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim() || registry2.marketplaceDiscoveryUrl,
46672
46696
  registryFallbacks: DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback })),
46673
46697
  allowPrivateNetwork: false,
46698
+ tasksEscrowFactoryOverrides,
46699
+ tasksEscrowDurationOverrides: tasksDurationOverridesFromEnv(source),
46674
46700
  networks,
46675
46701
  spendCaps: { ...DEFAULT_SPEND_CAPS }
46676
46702
  };
@@ -46688,8 +46714,10 @@ async function loadConfig(path = getVapiPaths().config, source = process.env, op
46688
46714
  }
46689
46715
  const parsed = configSchema.parse(JSON.parse(raw));
46690
46716
  parsed.registryFallbacks ??= DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback }));
46717
+ parsed.tasksEscrowFactoryOverrides ??= {};
46691
46718
  const migrated = rewriteLegacyRegistryUrls(parsed);
46692
46719
  const config2 = migrated.config;
46720
+ config2.tasksEscrowFactoryOverrides ??= {};
46693
46721
  if (migrated.changes.length > 0 && options.notice) {
46694
46722
  options.notice(
46695
46723
  `${formatLegacyRegistryRewrites(migrated.changes)}
@@ -46703,6 +46731,14 @@ Run vapi init to write the new URLs to ${path}.`
46703
46731
  const discoveryUrl = source.VAPI_DISCOVERY_URL?.trim();
46704
46732
  const marketplaceDiscoveryUrl = source.VAPI_MARKETPLACE_DISCOVERY_URL?.trim();
46705
46733
  const registryUrl = source.VAPI_REGISTRY_URL?.trim();
46734
+ Object.assign(config2.tasksEscrowFactoryOverrides, tasksFactoryOverridesFromEnv(source));
46735
+ config2.tasksEscrowDurationOverrides ??= {};
46736
+ for (const [chainId, overrides] of Object.entries(tasksDurationOverridesFromEnv(source))) {
46737
+ config2.tasksEscrowDurationOverrides[chainId] = {
46738
+ ...config2.tasksEscrowDurationOverrides[chainId],
46739
+ ...overrides
46740
+ };
46741
+ }
46706
46742
  if (config2.networks[BASE_MAINNET_CAIP2] && baseRpcUrl) {
46707
46743
  config2.networks[BASE_MAINNET_CAIP2].rpcUrl = baseRpcUrl;
46708
46744
  }
@@ -46743,6 +46779,31 @@ Run vapi init to write the new URLs to ${path}.`
46743
46779
  }
46744
46780
  return config2;
46745
46781
  }
46782
+ function tasksDurationOverridesFromEnv(source) {
46783
+ const result = {};
46784
+ for (const [name, raw] of Object.entries(source)) {
46785
+ const match = /^VAPI_TASKS_(WORK_DURATION|REVIEW_WINDOW)_SECONDS_(\d+)$/.exec(name);
46786
+ const value = raw?.trim();
46787
+ if (!match || !value) continue;
46788
+ const seconds = Number(value);
46789
+ if (!/^\d+$/.test(value) || !Number.isSafeInteger(seconds) || seconds <= 0)
46790
+ throw new Error(`Invalid trusted Tasks duration ${name}.`);
46791
+ const entry = result[match[2]] ??= {};
46792
+ if (match[1] === "WORK_DURATION") entry.workDurationSeconds = seconds;
46793
+ else entry.reviewWindowSeconds = seconds;
46794
+ }
46795
+ return result;
46796
+ }
46797
+ function tasksFactoryOverridesFromEnv(source) {
46798
+ const overrides = {};
46799
+ for (const [name, raw] of Object.entries(source)) {
46800
+ const match = /^VAPI_TASKS_ESCROW_FACTORY_(\d+)$/.exec(name);
46801
+ const value = raw?.trim();
46802
+ if (!match || !value) continue;
46803
+ overrides[match[1]] = getAddress(value);
46804
+ }
46805
+ return overrides;
46806
+ }
46746
46807
  function registryEndpoints(baseUrl) {
46747
46808
  const base = new URL(baseUrl);
46748
46809
  base.search = "";
@@ -46999,7 +47060,12 @@ var AgentLinkError = class extends Error {
46999
47060
  code;
47000
47061
  };
47001
47062
  var DEFAULT_AGENT_SCOPES = ["mcp:call", "router.use"];
47002
- var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([...DEFAULT_AGENT_SCOPES, "call.publish"]);
47063
+ var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([
47064
+ ...DEFAULT_AGENT_SCOPES,
47065
+ "call.publish",
47066
+ "tasks:read",
47067
+ "tasks:write"
47068
+ ]);
47003
47069
  var publicFetch = createPublicFetch({ allowPrivateNetwork: false });
47004
47070
  async function startDeviceLink(args) {
47005
47071
  if (args.device !== void 0 && !DEVICE_NAME_PATTERN.test(args.device)) {
@@ -72953,6 +73019,8 @@ var agentProfileShape = {
72953
73019
  instructions: external_exports.string().max(2e4),
72954
73020
  verifiedOnly: external_exports.boolean().default(true),
72955
73021
  approveAboveUsd: external_exports.number().min(0).default(0.5),
73022
+ maxPerTaskUsd: external_exports.number().min(0).default(DEFAULT_MAX_PER_TASK_USD),
73023
+ autoReleaseBelowUsd: external_exports.number().min(0).default(DEFAULT_AUTO_RELEASE_BELOW_USD),
72956
73024
  maxSteps: external_exports.number().int().min(1).max(50).default(12),
72957
73025
  paused: external_exports.boolean().default(false),
72958
73026
  grants: external_exports.array(external_exports.enum(AGENT_GRANTS)).default([]),
@@ -73113,7 +73181,9 @@ var ledgerSchema = external_exports.object({
73113
73181
  });
73114
73182
  var spendReservationSchema = external_exports.object({
73115
73183
  id: external_exports.string().min(1),
73116
- amountAtomic: external_exports.string().regex(/^\d+$/)
73184
+ amountAtomic: external_exports.string().regex(/^\d+$/),
73185
+ kind: external_exports.enum(["payment", "escrow-funding", "dispute-fee"]).optional(),
73186
+ taskKey: external_exports.string().optional()
73117
73187
  });
73118
73188
  var ledgerRowSchema = ledgerSchema.extend({
73119
73189
  wallet: walletNameSchema.optional(),
@@ -73139,6 +73209,29 @@ async function readSpendLedger(path = getAgentCashPaths().ledger, now = /* @__PU
73139
73209
  const row = rows.find((candidate) => candidate.wallet === wallet);
73140
73210
  return { date: utcDateKey(now), spentAtomic: row?.spentAtomic ?? "0" };
73141
73211
  }
73212
+ async function escrowFundingDayRemainingAtomic(input2) {
73213
+ const perDayAtomic = parseAtomicCap(input2.caps.perDayAtomic, "per-day");
73214
+ const rows = await readStoredSpendLedgerRows(input2.ledgerPath, input2.now);
73215
+ const row = rows.find((candidate) => candidate.wallet === input2.wallet);
73216
+ const reserved = input2.reservationId ? row?.reservations?.find((candidate) => candidate.id === input2.reservationId) : void 0;
73217
+ const remaining = perDayAtomic - BigInt(row?.spentAtomic ?? "0") + BigInt(reserved?.amountAtomic ?? "0");
73218
+ return remaining > 0n ? remaining : 0n;
73219
+ }
73220
+ async function taskReservedAtomic(input2) {
73221
+ const rows = await readStoredSpendLedgerRows(input2.ledgerPath, input2.now, true);
73222
+ return sumTaskReservations(rows, input2.wallet, input2.taskKey, input2.excludeReservationId);
73223
+ }
73224
+ function sumTaskReservations(rows, wallet, taskKey, excludeReservationId) {
73225
+ let total = 0n;
73226
+ for (const row of rows) {
73227
+ if (row.wallet !== wallet) continue;
73228
+ for (const reservation of row.reservations ?? []) {
73229
+ if (reservation.taskKey === taskKey && reservation.id !== excludeReservationId)
73230
+ total += BigInt(reservation.amountAtomic);
73231
+ }
73232
+ }
73233
+ return total;
73234
+ }
73142
73235
  async function readSpendLedgerRows(path = getAgentCashPaths().ledger, now = /* @__PURE__ */ new Date()) {
73143
73236
  return (await readStoredSpendLedgerRows(path, now)).map(({ date: date5, spentAtomic, wallet }) => ({
73144
73237
  date: date5,
@@ -73146,7 +73239,7 @@ async function readSpendLedgerRows(path = getAgentCashPaths().ledger, now = /* @
73146
73239
  wallet
73147
73240
  }));
73148
73241
  }
73149
- async function readStoredSpendLedgerRows(path, now) {
73242
+ async function readStoredSpendLedgerRows(path, now, allDays = false) {
73150
73243
  let raw;
73151
73244
  try {
73152
73245
  raw = await readFile13(path, "utf8");
@@ -73157,7 +73250,7 @@ async function readStoredSpendLedgerRows(path, now) {
73157
73250
  const parsed = ledgerFileSchema.parse(JSON.parse(raw));
73158
73251
  const rows = "rows" in parsed ? parsed.rows : [parsed];
73159
73252
  const today = utcDateKey(now);
73160
- return rows.filter((row) => row.date === today).map((row) => ({
73253
+ return rows.filter((row) => allDays || row.date === today).map((row) => ({
73161
73254
  date: row.date,
73162
73255
  spentAtomic: row.spentAtomic,
73163
73256
  wallet: row.wallet ?? DEFAULT_WALLET_NAME,
@@ -73168,49 +73261,164 @@ async function reserveSpend(amountAtomic, caps, options) {
73168
73261
  if (amountAtomic < 0n) {
73169
73262
  throw new Error("Spend amount cannot be negative.");
73170
73263
  }
73171
- const perCallAtomic = parseAtomicCap(caps.perCallAtomic, "per-call");
73172
- const perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
73173
- if (amountAtomic > perCallAtomic) {
73174
- throw new SpendCapError(
73175
- "per_call_cap_exceeded",
73176
- `Payment quote ${amountAtomic} atomic USDC exceeds the per-call cap ${perCallAtomic}. Refusing to sign.`
73177
- );
73264
+ const kind = options?.kind ?? "payment";
73265
+ let perDayAtomic;
73266
+ if (kind === "escrow-funding" || kind === "dispute-fee") {
73267
+ const maxPerTaskAtomic = options?.maxPerTaskAtomic;
73268
+ if (maxPerTaskAtomic === void 0 || maxPerTaskAtomic < 0n) {
73269
+ throw new Error("Escrow funding requires a non-negative per-task cap.");
73270
+ }
73271
+ if (amountAtomic > maxPerTaskAtomic) {
73272
+ throw new SpendCapError(
73273
+ "per_task_cap_exceeded",
73274
+ `Escrow funding ${amountAtomic} atomic USDC exceeds the per-task cap ${maxPerTaskAtomic}. Refusing to sign.`
73275
+ );
73276
+ }
73277
+ perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
73278
+ } else {
73279
+ const perCallAtomic = parseAtomicCap(caps.perCallAtomic, "per-call");
73280
+ perDayAtomic = parseAtomicCap(caps.perDayAtomic, "per-day");
73281
+ if (amountAtomic > perCallAtomic) {
73282
+ throw new SpendCapError(
73283
+ "per_call_cap_exceeded",
73284
+ `Payment quote ${amountAtomic} atomic USDC exceeds the per-call cap ${perCallAtomic}. Refusing to sign.`
73285
+ );
73286
+ }
73178
73287
  }
73179
73288
  const ledgerPath = options?.ledgerPath ?? getAgentCashPaths().ledger;
73180
73289
  const now = options?.now ?? /* @__PURE__ */ new Date();
73181
73290
  const wallet = options?.wallet ?? DEFAULT_WALLET_NAME;
73182
73291
  return await withLedgerLock(ledgerPath, async () => {
73183
- const rows = await readStoredSpendLedgerRows(ledgerPath, now);
73184
- const current = rows.find((row) => row.wallet === wallet);
73292
+ const rows = await readStoredSpendLedgerRows(ledgerPath, now, true);
73293
+ const today = utcDateKey(now);
73294
+ const current = rows.find((row) => row.wallet === wallet && row.date === today);
73295
+ if ((kind === "escrow-funding" || kind === "dispute-fee") && options?.taskKey !== void 0) {
73296
+ const taskTotal = sumTaskReservations(
73297
+ rows,
73298
+ wallet,
73299
+ options.taskKey,
73300
+ options.reuseExistingEscrowReservation || options.resumeEscrowReservation ? options.reservationId : void 0
73301
+ ) + amountAtomic;
73302
+ if (taskTotal > options.maxPerTaskAtomic)
73303
+ throw new SpendCapError(
73304
+ "per_task_cap_exceeded",
73305
+ `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.`
73306
+ );
73307
+ }
73185
73308
  if (options?.reservationId !== void 0) {
73186
- if (current?.reservations?.some((reservation) => reservation.id === options.reservationId)) {
73309
+ const existing = current?.reservations?.find(
73310
+ (reservation) => reservation.id === options.reservationId
73311
+ );
73312
+ if (existing && options.reuseExistingEscrowReservation) {
73313
+ if (kind !== "escrow-funding" && kind !== "dispute-fee" || existing.kind !== kind || existing.amountAtomic !== amountAtomic.toString() || existing.taskKey !== void 0 && existing.taskKey !== options.taskKey)
73314
+ throw new Error(
73315
+ `Spend reservation ${options.reservationId} does not match this funding.`
73316
+ );
73317
+ if (existing.taskKey === void 0 && options.taskKey !== void 0) {
73318
+ existing.taskKey = options.taskKey;
73319
+ await writeLedgerAtomically(ledgerPath, rows);
73320
+ }
73321
+ return {
73322
+ date: current.date,
73323
+ spentAtomic: current.spentAtomic,
73324
+ reservationReused: true
73325
+ };
73326
+ }
73327
+ if (existing) {
73187
73328
  throw new Error(`Spend reservation ${options.reservationId} already exists.`);
73188
73329
  }
73330
+ if (options.resumeEscrowReservation) {
73331
+ const resumed = options.resumeEscrowReservation;
73332
+ const historical = rows.find((row) => row.wallet === wallet && row.date === resumed.date);
73333
+ const stored = historical?.reservations?.find(
73334
+ (reservation) => reservation.id === resumed.id
73335
+ );
73336
+ 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))
73337
+ throw new Error(
73338
+ `Spend reservation ${options.reservationId} does not match this funding.`
73339
+ );
73340
+ if (stored?.taskKey === void 0 && options.taskKey !== void 0) {
73341
+ if (stored) stored.taskKey = options.taskKey;
73342
+ else {
73343
+ const reservation = {
73344
+ id: resumed.id,
73345
+ amountAtomic: resumed.amountAtomic,
73346
+ kind,
73347
+ taskKey: options.taskKey
73348
+ };
73349
+ if (historical)
73350
+ historical.reservations = [...historical.reservations ?? [], reservation];
73351
+ else
73352
+ rows.push({
73353
+ date: resumed.date,
73354
+ spentAtomic: resumed.amountAtomic,
73355
+ wallet,
73356
+ reservations: [reservation]
73357
+ });
73358
+ }
73359
+ await writeLedgerAtomically(ledgerPath, rows);
73360
+ }
73361
+ return {
73362
+ date: resumed.date,
73363
+ spentAtomic: current?.spentAtomic ?? "0",
73364
+ reservationReused: true
73365
+ };
73366
+ }
73189
73367
  }
73190
73368
  const spentAtomic = current?.spentAtomic ?? "0";
73191
73369
  const nextSpent = BigInt(spentAtomic) + amountAtomic;
73192
73370
  if (nextSpent > perDayAtomic) {
73193
73371
  throw new SpendCapError(
73194
73372
  "per_day_cap_exceeded",
73195
- `Payment quote ${amountAtomic} atomic USDC would raise today's spend to ${nextSpent} for wallet ${wallet}, above the per-day cap ${perDayAtomic}. Refusing to sign.`
73373
+ `${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.`
73196
73374
  );
73197
73375
  }
73376
+ const reservationId = options?.reservationId ?? (options?.taskKey === void 0 ? void 0 : randomBytes10(16).toString("hex"));
73198
73377
  const nextRow = {
73199
73378
  date: utcDateKey(now),
73200
73379
  spentAtomic: nextSpent.toString(),
73201
73380
  wallet,
73202
- ...(current?.reservations?.length ?? 0) === 0 && options?.reservationId === void 0 ? {} : {
73381
+ ...(current?.reservations?.length ?? 0) === 0 && reservationId === void 0 ? {} : {
73203
73382
  reservations: [
73204
73383
  ...current?.reservations ?? [],
73205
- ...options?.reservationId === void 0 ? [] : [{ id: options.reservationId, amountAtomic: amountAtomic.toString() }]
73384
+ ...reservationId === void 0 ? [] : [
73385
+ {
73386
+ id: reservationId,
73387
+ amountAtomic: amountAtomic.toString(),
73388
+ ...kind !== "payment" ? { kind } : {},
73389
+ ...options?.taskKey === void 0 ? {} : { taskKey: options.taskKey }
73390
+ }
73391
+ ]
73206
73392
  ]
73207
73393
  }
73208
73394
  };
73209
- const nextRows = [...rows.filter((row) => row.wallet !== wallet), nextRow].sort(
73210
- (a, b) => a.wallet.localeCompare(b.wallet)
73395
+ const otherRows = rows.filter((row) => row.wallet !== wallet || row.date !== today).map((row) => {
73396
+ if (row.wallet !== wallet || !options?.reuseExistingEscrowReservation || options.reservationId === void 0)
73397
+ return row;
73398
+ const previous = row.reservations?.find(
73399
+ (reservation) => reservation.id === options.reservationId
73400
+ );
73401
+ if (!previous) return row;
73402
+ if (kind !== "escrow-funding" && kind !== "dispute-fee" || previous.kind !== kind || previous.amountAtomic !== amountAtomic.toString() || previous.taskKey !== void 0 && previous.taskKey !== options.taskKey)
73403
+ throw new Error(
73404
+ `Spend reservation ${options.reservationId} does not match this funding.`
73405
+ );
73406
+ return {
73407
+ ...row,
73408
+ reservations: row.reservations.filter(
73409
+ (reservation) => reservation.id !== options.reservationId
73410
+ )
73411
+ };
73412
+ });
73413
+ const nextRows = [...otherRows, nextRow].sort(
73414
+ (a, b) => a.wallet.localeCompare(b.wallet) || a.date.localeCompare(b.date)
73211
73415
  );
73212
73416
  await writeLedgerAtomically(ledgerPath, nextRows);
73213
- return { date: nextRow.date, spentAtomic: nextRow.spentAtomic };
73417
+ return {
73418
+ date: nextRow.date,
73419
+ spentAtomic: nextRow.spentAtomic,
73420
+ ...options?.reservationId === void 0 && reservationId !== void 0 ? { reservationId } : {}
73421
+ };
73214
73422
  });
73215
73423
  }
73216
73424
  async function releaseSpend(amountAtomic, options) {
@@ -73222,35 +73430,40 @@ async function releaseSpend(amountAtomic, options) {
73222
73430
  const wallet = options.wallet ?? DEFAULT_WALLET_NAME;
73223
73431
  return await withLedgerLock(ledgerPath, async () => {
73224
73432
  const today = utcDateKey(now);
73225
- const rows = await readStoredSpendLedgerRows(ledgerPath, now);
73226
- const current = rows.find((row) => row.wallet === wallet);
73227
- if (options.reservedOn !== today) {
73433
+ const rows = await readStoredSpendLedgerRows(ledgerPath, now, true);
73434
+ const current = rows.find((row) => row.wallet === wallet && row.date === today);
73435
+ if (options.reservedOn !== today && options.reservationId === void 0) {
73228
73436
  return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
73229
73437
  }
73230
- const reservation = options.reservationId === void 0 ? void 0 : current?.reservations?.find((candidate) => candidate.id === options.reservationId);
73438
+ const reserved = rows.find((row) => row.wallet === wallet && row.date === options.reservedOn);
73439
+ const reservation = options.reservationId === void 0 ? void 0 : reserved?.reservations?.find((candidate) => candidate.id === options.reservationId);
73231
73440
  if (options.reservationId !== void 0 && reservation === void 0) {
73232
73441
  return { date: today, spentAtomic: current?.spentAtomic ?? "0" };
73233
73442
  }
73234
73443
  if (reservation !== void 0 && reservation.amountAtomic !== amountAtomic.toString()) {
73235
73444
  throw new Error(`Spend reservation ${options.reservationId} has a different amount.`);
73236
73445
  }
73237
- const spentAtomic = BigInt(current?.spentAtomic ?? "0");
73446
+ const spentAtomic = BigInt(reserved?.spentAtomic ?? "0");
73238
73447
  const releasedAmount = reservation === void 0 ? amountAtomic : BigInt(reservation.amountAtomic);
73239
73448
  const nextSpent = spentAtomic > releasedAmount ? spentAtomic - releasedAmount : 0n;
73240
- const remainingReservations = current?.reservations?.filter(
73449
+ const remainingReservations = reserved?.reservations?.filter(
73241
73450
  (candidate) => candidate.id !== options.reservationId
73242
73451
  );
73243
73452
  const nextRow = {
73244
- date: today,
73453
+ date: options.reservedOn,
73245
73454
  spentAtomic: nextSpent.toString(),
73246
73455
  wallet,
73247
73456
  ...remainingReservations === void 0 || remainingReservations.length === 0 ? {} : { reservations: remainingReservations }
73248
73457
  };
73249
- const nextRows = [...rows.filter((row) => row.wallet !== wallet), nextRow].sort(
73250
- (a, b) => a.wallet.localeCompare(b.wallet)
73251
- );
73458
+ const nextRows = [
73459
+ ...rows.filter((row) => row.wallet !== wallet || row.date !== options.reservedOn),
73460
+ nextRow
73461
+ ].sort((a, b) => a.wallet.localeCompare(b.wallet) || a.date.localeCompare(b.date));
73252
73462
  await writeLedgerAtomically(ledgerPath, nextRows);
73253
- return { date: nextRow.date, spentAtomic: nextRow.spentAtomic };
73463
+ return {
73464
+ date: today,
73465
+ spentAtomic: options.reservedOn === today ? nextRow.spentAtomic : current?.spentAtomic ?? "0"
73466
+ };
73254
73467
  });
73255
73468
  }
73256
73469
  function parseAtomicCap(value, label) {
@@ -73547,7 +73760,9 @@ async function transferBetweenAccounts(args) {
73547
73760
  );
73548
73761
  reservedOn = reservation.date;
73549
73762
  } catch (error61) {
73550
- if (error61 instanceof SpendCapError) throw transferError(error61.code);
73763
+ if (error61 instanceof SpendCapError && isTransferErrorCode(error61.code)) {
73764
+ throw transferError(error61.code);
73765
+ }
73551
73766
  throw transferError("network_error");
73552
73767
  }
73553
73768
  }
@@ -76685,7 +76900,7 @@ function createRunBudget(args) {
76685
76900
  import { mkdir as mkdir15, writeFile as writeFile8 } from "node:fs/promises";
76686
76901
  import { arch, platform, release } from "node:os";
76687
76902
  import { join as join13 } from "node:path";
76688
- var VAPI_CLIENT_VERSION = "0.8.0";
76903
+ var VAPI_CLIENT_VERSION = "0.9.0";
76689
76904
  async function createSupportReport(options) {
76690
76905
  const message = options.message.trim();
76691
76906
  if (!message) throw new Error("Support report message must not be empty.");
@@ -77687,6 +77902,115 @@ async function responseJson(response) {
77687
77902
  }
77688
77903
  }
77689
77904
 
77905
+ // src/tasks/canonical-json.ts
77906
+ function canonicalizeJson(value) {
77907
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
77908
+ return value;
77909
+ }
77910
+ if (typeof value === "number") {
77911
+ if (!Number.isFinite(value)) {
77912
+ throw new Error("Value is not JSON serializable");
77913
+ }
77914
+ return value;
77915
+ }
77916
+ if (Array.isArray(value)) {
77917
+ return value.map(canonicalizeJson);
77918
+ }
77919
+ if (typeof value === "object") {
77920
+ const prototype = Object.getPrototypeOf(value);
77921
+ if (prototype !== Object.prototype && prototype !== null) {
77922
+ throw new Error("Value is not JSON serializable");
77923
+ }
77924
+ return Object.fromEntries(
77925
+ Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, item]) => [key, canonicalizeJson(item)])
77926
+ );
77927
+ }
77928
+ throw new Error("Value is not JSON serializable");
77929
+ }
77930
+ function canonicalJson(value) {
77931
+ return JSON.stringify(canonicalizeJson(value));
77932
+ }
77933
+
77934
+ // src/tasks/delivery-manifest.ts
77935
+ import { createHash as createHash2 } from "node:crypto";
77936
+ var sha256Schema = external_exports.string().regex(/^[0-9a-fA-F]{64}$/).transform((digest) => digest.toLowerCase());
77937
+ var deliveryManifestSchema = external_exports.object({
77938
+ files: external_exports.array(
77939
+ external_exports.object({
77940
+ fileId: external_exports.uuid(),
77941
+ fileName: external_exports.string().min(1).max(255),
77942
+ sha256: sha256Schema,
77943
+ sizeBytes: external_exports.number().int().positive()
77944
+ }).strict()
77945
+ ).min(1).max(20),
77946
+ noteSha256: sha256Schema
77947
+ }).strict().superRefine((manifest, context) => {
77948
+ if (new Set(manifest.files.map((file2) => file2.fileId)).size !== manifest.files.length) {
77949
+ context.addIssue({ code: "custom", path: ["files"], message: "File ids must be unique" });
77950
+ }
77951
+ });
77952
+ function freezeDeliveryManifest(input2) {
77953
+ const parsed = deliveryManifestSchema.parse(input2);
77954
+ const manifest = deliveryManifestSchema.parse({
77955
+ ...parsed,
77956
+ files: [...parsed.files].sort((left, right) => left.fileId.localeCompare(right.fileId))
77957
+ });
77958
+ return {
77959
+ manifest,
77960
+ manifestHash: keccak256(toBytes(canonicalJson(manifest)))
77961
+ };
77962
+ }
77963
+ async function sha256Hex(bytes) {
77964
+ return createHash2("sha256").update(bytes).digest("hex");
77965
+ }
77966
+ async function prepareDeliveryManifest(files, note) {
77967
+ if (files.length === 0) {
77968
+ throw new Error("Upload at least one delivery file.");
77969
+ }
77970
+ const noteSha256 = await sha256Hex(new TextEncoder().encode(note));
77971
+ return freezeDeliveryManifest({ files, noteSha256 });
77972
+ }
77973
+
77974
+ // src/tasks/verbs.ts
77975
+ var TASKS_CLIENT_VERBS = [
77976
+ "search",
77977
+ "show",
77978
+ "post",
77979
+ "propose",
77980
+ "submit",
77981
+ "award",
77982
+ "sign",
77983
+ "fund",
77984
+ "deliver",
77985
+ "release",
77986
+ "refund",
77987
+ "dispute",
77988
+ "counter-evidence",
77989
+ "resolve-unmatched",
77990
+ "message",
77991
+ "thread",
77992
+ "watch",
77993
+ "status"
77994
+ ];
77995
+
77996
+ // src/funding-policy.ts
77997
+ function decideFunding(input2) {
77998
+ validateAmount(input2.amountUsd);
77999
+ if (input2.amountUsd > input2.maxPerTaskUsd) return { ok: false, reason: "policy.perTask" };
78000
+ if (input2.amountUsd > input2.dayRemainingUsd) return { ok: false, reason: "policy.perDay" };
78001
+ if (input2.amountUsd > input2.approveAboveUsd) return { approval: true };
78002
+ return { ok: true };
78003
+ }
78004
+ function decideRelease(input2) {
78005
+ validateAmount(input2.amountUsd);
78006
+ return input2.amountUsd < input2.autoReleaseBelowUsd ? { auto: true } : { approval: true };
78007
+ }
78008
+ function validateAmount(amountUsd) {
78009
+ if (!Number.isFinite(amountUsd) || amountUsd < 0) {
78010
+ throw new Error("Task amount must be a finite, non-negative USD number.");
78011
+ }
78012
+ }
78013
+
77690
78014
  // src/searches.ts
77691
78015
  import { mkdir as mkdir16, open as open11, readFile as readFile16 } from "node:fs/promises";
77692
78016
  import { dirname as dirname10 } from "node:path";
@@ -77867,6 +78191,351 @@ function dedupeProvenance(values) {
77867
78191
  });
77868
78192
  }
77869
78193
 
78194
+ // src/docs.ts
78195
+ var DEFAULT_DOCS_ORIGIN = "https://docs.vapinetwork.ai";
78196
+ var DOCS_URL_ENV = "VAPI_DOCS_URL";
78197
+ var DOCS_RESPONSE_MAX_BYTES = 2 * 1024 * 1024;
78198
+ var SEARCH_TIMEOUT_MS = 15e3;
78199
+ var READ_TIMEOUT_MS = 15e3;
78200
+ var ASK_TIMEOUT_MS = 6e4;
78201
+ var MAX_REDIRECTS = 5;
78202
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
78203
+ var DocsError = class extends Error {
78204
+ constructor(code, message, options) {
78205
+ super(message, options);
78206
+ this.code = code;
78207
+ }
78208
+ code;
78209
+ name = "DocsError";
78210
+ };
78211
+ var publicFetch3 = createPublicFetch({ allowPrivateNetwork: false });
78212
+ function resolveDocsOrigin(env = process.env) {
78213
+ const configured = env[DOCS_URL_ENV]?.trim() || DEFAULT_DOCS_ORIGIN;
78214
+ let url2;
78215
+ try {
78216
+ url2 = new URL(configured);
78217
+ } catch (cause) {
78218
+ throw new DocsError("host_not_allowed", `The configured docs origin is invalid.`, { cause });
78219
+ }
78220
+ if (url2.protocol !== "https:" || url2.username || url2.password || url2.pathname !== "/" || url2.search || url2.hash) {
78221
+ throw new DocsError(
78222
+ "host_not_allowed",
78223
+ "The configured docs origin must be an HTTPS origin without credentials, a path, a query, or a fragment."
78224
+ );
78225
+ }
78226
+ return url2.origin;
78227
+ }
78228
+ async function searchDocs(query, options = {}) {
78229
+ const normalized = nonEmpty(query, "Search query");
78230
+ if (options.limit !== void 0 && (!Number.isSafeInteger(options.limit) || options.limit < 1)) {
78231
+ throw new DocsError("invalid_input", "Search limit must be a positive integer.");
78232
+ }
78233
+ const origin = resolveDocsOrigin(options.env);
78234
+ const { text: body } = await requestText(
78235
+ new URL("/~gitbook/mcp", origin),
78236
+ {
78237
+ method: "POST",
78238
+ headers: {
78239
+ "content-type": "application/json",
78240
+ accept: "application/json, text/event-stream"
78241
+ },
78242
+ body: JSON.stringify({
78243
+ jsonrpc: "2.0",
78244
+ id: 1,
78245
+ method: "tools/call",
78246
+ params: { name: "searchDocumentation", arguments: { query: normalized } }
78247
+ })
78248
+ },
78249
+ {
78250
+ operation: "search",
78251
+ origin,
78252
+ timeoutMs: options.timeoutMs ?? SEARCH_TIMEOUT_MS,
78253
+ fetchImpl: options.fetchImpl
78254
+ }
78255
+ );
78256
+ const payload = parseSearchEnvelope(body);
78257
+ const blocks = searchTextBlocks(payload);
78258
+ const results = blocks.flatMap((block) => {
78259
+ const parsed = parseSearchBlock(block);
78260
+ return parsed === void 0 ? [] : [parsed];
78261
+ });
78262
+ return { results: options.limit === void 0 ? results : results.slice(0, options.limit) };
78263
+ }
78264
+ async function readDocs(pageUrlOrPath, options = {}) {
78265
+ const input2 = nonEmpty(pageUrlOrPath, "Documentation page");
78266
+ const origin = resolveDocsOrigin(options.env);
78267
+ const page = docsPagePath(input2, origin);
78268
+ const requestOptions = {
78269
+ operation: "read",
78270
+ origin,
78271
+ timeoutMs: options.timeoutMs ?? READ_TIMEOUT_MS,
78272
+ fetchImpl: options.fetchImpl
78273
+ };
78274
+ const found = await readFirstMarkdown(page, requestOptions);
78275
+ if (found !== void 0) return found;
78276
+ const { url: canonical } = await requestText(
78277
+ page,
78278
+ { method: "HEAD" },
78279
+ { ...requestOptions, notFoundOn404: true }
78280
+ );
78281
+ if (canonical.pathname !== page.pathname) {
78282
+ const moved = await readFirstMarkdown(canonical, requestOptions);
78283
+ if (moved !== void 0) return moved;
78284
+ }
78285
+ throw new DocsError("not_found", `Documentation page not found: ${page.href}`);
78286
+ }
78287
+ async function readFirstMarkdown(page, options) {
78288
+ const path = page.pathname.replace(/\/+$/u, "");
78289
+ const candidates = path ? [`${path}.md`, `${path}/readme.md`] : ["/readme.md"];
78290
+ for (const pathname of candidates) {
78291
+ const candidate = new URL(page);
78292
+ candidate.pathname = pathname;
78293
+ try {
78294
+ const { text: markdown, url: url2 } = await requestText(
78295
+ candidate,
78296
+ { method: "GET", headers: { accept: "text/markdown" } },
78297
+ { ...options, notFoundOn404: true }
78298
+ );
78299
+ if (!markdown.trimStart().startsWith("# Page Not Found")) {
78300
+ return { url: url2.href, markdown };
78301
+ }
78302
+ } catch (error61) {
78303
+ if (!(error61 instanceof DocsError && error61.code === "not_found")) throw error61;
78304
+ }
78305
+ }
78306
+ return void 0;
78307
+ }
78308
+ async function askDocs(question, options = {}) {
78309
+ const normalized = nonEmpty(question, "Question");
78310
+ const origin = resolveDocsOrigin(options.env);
78311
+ const url2 = new URL("/readme.md", origin);
78312
+ url2.searchParams.set("ask", normalized);
78313
+ const { text: markdown } = await requestText(
78314
+ url2,
78315
+ { method: "GET", headers: { accept: "text/markdown" } },
78316
+ {
78317
+ operation: "ask",
78318
+ origin,
78319
+ timeoutMs: options.timeoutMs ?? ASK_TIMEOUT_MS,
78320
+ fetchImpl: options.fetchImpl
78321
+ }
78322
+ );
78323
+ return parseAnswer(markdown);
78324
+ }
78325
+ function nonEmpty(value, label) {
78326
+ const normalized = value.trim();
78327
+ if (!normalized) throw new DocsError("invalid_input", `${label} must not be empty.`);
78328
+ return normalized;
78329
+ }
78330
+ function docsPagePath(value, origin) {
78331
+ let url2;
78332
+ try {
78333
+ url2 = new URL(value, `${origin}/`);
78334
+ } catch (cause) {
78335
+ throw new DocsError("invalid_input", "Documentation page must be a docs URL or path.", {
78336
+ cause
78337
+ });
78338
+ }
78339
+ if (url2.origin !== origin || url2.username || url2.password) {
78340
+ throw new DocsError(
78341
+ "host_not_allowed",
78342
+ `Documentation pages must use the configured docs origin ${origin}.`
78343
+ );
78344
+ }
78345
+ url2.search = "";
78346
+ url2.hash = "";
78347
+ url2.pathname = url2.pathname.replace(/\/+$/u, "").replace(/(?:\/readme)?\.md$/iu, "") || "/";
78348
+ return url2;
78349
+ }
78350
+ async function requestText(requestUrl, init, options) {
78351
+ if (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1) {
78352
+ throw new DocsError("invalid_input", "Documentation request timeout must be positive.");
78353
+ }
78354
+ assertAllowedUrl(requestUrl, options.origin);
78355
+ const controller = new AbortController();
78356
+ const timeoutError = new DocsError(
78357
+ "timeout",
78358
+ `The documentation ${options.operation} request timed out.`
78359
+ );
78360
+ let timer;
78361
+ const timeout = new Promise((_resolve, reject) => {
78362
+ timer = setTimeout(() => {
78363
+ controller.abort(timeoutError);
78364
+ reject(timeoutError);
78365
+ }, options.timeoutMs);
78366
+ });
78367
+ let url2 = requestUrl;
78368
+ try {
78369
+ let response;
78370
+ for (let hops = 0; ; hops += 1) {
78371
+ response = await Promise.race([
78372
+ (options.fetchImpl ?? publicFetch3)(url2, {
78373
+ ...init,
78374
+ redirect: "manual",
78375
+ signal: controller.signal
78376
+ }),
78377
+ timeout
78378
+ ]);
78379
+ if (response.url) assertAllowedUrl(new URL(response.url), options.origin);
78380
+ const location = response.headers.get("location");
78381
+ if (init.method !== "GET" && init.method !== "HEAD" || !REDIRECT_STATUSES.has(response.status) || !location) {
78382
+ break;
78383
+ }
78384
+ if (hops === MAX_REDIRECTS) {
78385
+ throw new DocsError(
78386
+ "bad_response",
78387
+ `The documentation ${options.operation} request redirected too many times.`
78388
+ );
78389
+ }
78390
+ await response.body?.cancel();
78391
+ const next = new URL(location, url2);
78392
+ assertAllowedUrl(next, options.origin);
78393
+ url2 = next;
78394
+ }
78395
+ if (options.notFoundOn404 && response.status === 404) {
78396
+ throw new DocsError("not_found", `Documentation page not found: ${url2.href}`);
78397
+ }
78398
+ if (!response.ok) {
78399
+ throw new DocsError(
78400
+ "bad_response",
78401
+ `The documentation ${options.operation} request returned HTTP ${response.status}.`
78402
+ );
78403
+ }
78404
+ return { text: await Promise.race([readCappedText(response), timeout]), url: url2 };
78405
+ } catch (error61) {
78406
+ if (error61 instanceof DocsError) throw error61;
78407
+ if (controller.signal.aborted) throw timeoutError;
78408
+ throw new DocsError("bad_response", `The documentation ${options.operation} request failed.`, {
78409
+ cause: error61
78410
+ });
78411
+ } finally {
78412
+ if (timer !== void 0) clearTimeout(timer);
78413
+ }
78414
+ }
78415
+ function assertAllowedUrl(url2, origin) {
78416
+ if (url2.origin !== origin || url2.username || url2.password) {
78417
+ throw new DocsError(
78418
+ "host_not_allowed",
78419
+ `Documentation requests may only contact the configured docs origin ${origin}.`
78420
+ );
78421
+ }
78422
+ }
78423
+ async function readCappedText(response) {
78424
+ const contentLength = response.headers.get("content-length");
78425
+ if (contentLength !== null && Number(contentLength) > DOCS_RESPONSE_MAX_BYTES) {
78426
+ throw responseTooLarge();
78427
+ }
78428
+ if (response.body === null) return "";
78429
+ const reader = response.body.getReader();
78430
+ const chunks = [];
78431
+ let size5 = 0;
78432
+ try {
78433
+ while (true) {
78434
+ const { done, value } = await reader.read();
78435
+ if (done) break;
78436
+ size5 += value.byteLength;
78437
+ if (size5 > DOCS_RESPONSE_MAX_BYTES) {
78438
+ await reader.cancel();
78439
+ throw responseTooLarge();
78440
+ }
78441
+ chunks.push(value);
78442
+ }
78443
+ } finally {
78444
+ reader.releaseLock();
78445
+ }
78446
+ const bytes = new Uint8Array(size5);
78447
+ let offset = 0;
78448
+ for (const chunk of chunks) {
78449
+ bytes.set(chunk, offset);
78450
+ offset += chunk.byteLength;
78451
+ }
78452
+ return new TextDecoder().decode(bytes);
78453
+ }
78454
+ function responseTooLarge() {
78455
+ return new DocsError(
78456
+ "bad_response",
78457
+ `The documentation response exceeded ${DOCS_RESPONSE_MAX_BYTES} bytes.`
78458
+ );
78459
+ }
78460
+ function parseSearchEnvelope(body) {
78461
+ const trimmed = body.trim();
78462
+ if (!trimmed) throw invalidSearchResponse();
78463
+ if (trimmed.startsWith("{")) return parseJson2(trimmed);
78464
+ const records = [];
78465
+ let data = [];
78466
+ for (const line of body.split(/\r?\n/u)) {
78467
+ if (line === "") {
78468
+ if (data.length > 0) records.push(data.join("\n"));
78469
+ data = [];
78470
+ } else if (line.startsWith("data:")) {
78471
+ data.push(line.slice(5).trimStart());
78472
+ }
78473
+ }
78474
+ if (data.length > 0) records.push(data.join("\n"));
78475
+ for (const record2 of records) {
78476
+ try {
78477
+ return JSON.parse(record2);
78478
+ } catch {
78479
+ }
78480
+ }
78481
+ throw invalidSearchResponse();
78482
+ }
78483
+ function parseJson2(value) {
78484
+ try {
78485
+ return JSON.parse(value);
78486
+ } catch (cause) {
78487
+ throw new DocsError("bad_response", "The documentation search response was invalid JSON.", {
78488
+ cause
78489
+ });
78490
+ }
78491
+ }
78492
+ function searchTextBlocks(payload) {
78493
+ const envelope = asRecord(payload);
78494
+ const rpcError = asRecord(envelope?.error);
78495
+ if (rpcError !== void 0) {
78496
+ const message = typeof rpcError.message === "string" ? `: ${rpcError.message}` : "";
78497
+ throw new DocsError("bad_response", `The documentation search returned an error${message}`);
78498
+ }
78499
+ const result = asRecord(envelope?.result);
78500
+ if (!Array.isArray(result?.content)) throw invalidSearchResponse();
78501
+ return result.content.flatMap((item) => {
78502
+ const block = asRecord(item);
78503
+ return block?.type === "text" && typeof block.text === "string" ? [block.text] : [];
78504
+ });
78505
+ }
78506
+ function parseSearchBlock(block) {
78507
+ const match = /^Title:\s*([^\r\n]+)\r?\nLink:\s*([^\r\n]+)\r?\nContent:\s*([\s\S]*)$/u.exec(
78508
+ block.trim()
78509
+ );
78510
+ if (!match) return void 0;
78511
+ const title = match[1].trim();
78512
+ const url2 = match[2].trim();
78513
+ const excerpt = match[3].trim();
78514
+ return title && url2 && excerpt ? { title, url: url2, excerpt } : void 0;
78515
+ }
78516
+ function invalidSearchResponse() {
78517
+ return new DocsError("bad_response", "The documentation search response was invalid.");
78518
+ }
78519
+ function parseAnswer(markdown) {
78520
+ const heading = /^# Sources:\s*$/gmu;
78521
+ const matches = [...markdown.matchAll(heading)];
78522
+ const last = matches.at(-1);
78523
+ if (last?.index === void 0) return { answer: markdown.trimEnd(), sources: [] };
78524
+ const answer = markdown.slice(0, last.index).trimEnd();
78525
+ const sourceText = markdown.slice(last.index + last[0].length).trim();
78526
+ const sources = sourceText.split(/\r?\n/u).flatMap((line) => {
78527
+ const linked = /^\s*(?:[-*]|\d+\.)\s+\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)\s*$/u.exec(line);
78528
+ if (linked) return [{ title: linked[1].trim(), url: linked[2] }];
78529
+ const bare = /^\s*(?:[-*]|\d+\.)\s+(https?:\/\/\S+)\s*$/u.exec(line);
78530
+ if (!bare) return [];
78531
+ return [{ title: bare[1], url: bare[1] }];
78532
+ });
78533
+ return { answer, sources };
78534
+ }
78535
+ function asRecord(value) {
78536
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
78537
+ }
78538
+
77870
78539
  // src/distribute.ts
77871
78540
  var DistributeError = class extends Error {
77872
78541
  constructor(code, message, movementId) {
@@ -78425,7 +79094,7 @@ function csvCell(value) {
78425
79094
  }
78426
79095
 
78427
79096
  // src/swarm-capital.ts
78428
- import { createHash as createHash2, randomBytes as randomBytes15 } from "node:crypto";
79097
+ import { createHash as createHash3, randomBytes as randomBytes15 } from "node:crypto";
78429
79098
  import { readFile as readFile17, unlink as unlink12 } from "node:fs/promises";
78430
79099
  import { join as join14 } from "node:path";
78431
79100
  var movementIdSchema3 = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/u);
@@ -78497,7 +79166,7 @@ var resumableSwarmReasons = /* @__PURE__ */ new Set([
78497
79166
  "sweep"
78498
79167
  ]);
78499
79168
  function treasuryRequestMovementId(swarm, requestId) {
78500
- const digest = createHash2("sha256").update(`${swarm}
79169
+ const digest = createHash3("sha256").update(`${swarm}
78501
79170
  ${requestId}`).digest("hex").slice(0, 24);
78502
79171
  return movementIdSchema3.parse(`mv_${digest}`);
78503
79172
  }
@@ -80556,9 +81225,12 @@ export {
80556
81225
  CEILING_SWEEP_TIMEOUT_MS,
80557
81226
  DEFAULT_AGENT_TOOLS,
80558
81227
  DEFAULT_ARC_GAS_HEADROOM_ATOMIC,
81228
+ DEFAULT_AUTO_RELEASE_BELOW_USD,
80559
81229
  DEFAULT_CEILING_ATOMIC,
80560
81230
  DEFAULT_DISCOVERY_URL,
81231
+ DEFAULT_DOCS_ORIGIN,
80561
81232
  DEFAULT_MARKETPLACE_DISCOVERY_URL,
81233
+ DEFAULT_MAX_PER_TASK_USD,
80562
81234
  DEFAULT_REGISTRY_FALLBACKS,
80563
81235
  DEFAULT_REGISTRY_URL,
80564
81236
  DEFAULT_SPEND_CAPS,
@@ -80568,6 +81240,7 @@ export {
80568
81240
  DISCOVERY_CATEGORIES,
80569
81241
  DiscoveryCatalogError,
80570
81242
  DistributeError,
81243
+ DocsError,
80571
81244
  EIP3009_AUTHORIZATION_STATE_ABI,
80572
81245
  EIP3009_AUTHORIZATION_TYPES,
80573
81246
  ERC20_ABI,
@@ -80611,6 +81284,7 @@ export {
80611
81284
  SpendPolicy,
80612
81285
  SwarmError,
80613
81286
  SweepGasError,
81287
+ TASKS_CLIENT_VERBS,
80614
81288
  TransferError,
80615
81289
  USDC_DECIMALS,
80616
81290
  VAPI_BUILDER_CODE,
@@ -80637,6 +81311,7 @@ export {
80637
81311
  appendReceipt,
80638
81312
  appendSearchEvent,
80639
81313
  areSamePaymentNetwork,
81314
+ askDocs,
80640
81315
  assertMaxPrice,
80641
81316
  assertPublicUrl,
80642
81317
  assertSIWxChallengeBoundToOrigin,
@@ -80674,6 +81349,8 @@ export {
80674
81349
  createRunId,
80675
81350
  createSIWxMessage,
80676
81351
  createSupportReport,
81352
+ decideFunding,
81353
+ decideRelease,
80677
81354
  decodePaymentRequiredHeader,
80678
81355
  deleteSwarmFile,
80679
81356
  deriveEvmPrivateKey,
@@ -80694,6 +81371,7 @@ export {
80694
81371
  encryptPrivateKey,
80695
81372
  ensureDeviceName,
80696
81373
  entropyToPhrase,
81374
+ escrowFundingDayRemainingAtomic,
80697
81375
  executeMovement,
80698
81376
  executionModeSchema,
80699
81377
  explorerAddressUrl,
@@ -80778,12 +81456,14 @@ export {
80778
81456
  phraseToSeed,
80779
81457
  pinnedLookup,
80780
81458
  planAllocation,
81459
+ prepareDeliveryManifest,
80781
81460
  promptForSecret,
80782
81461
  protectVault,
80783
81462
  provenanceSchema,
80784
81463
  readAgentProfile,
80785
81464
  readAuditLog,
80786
81465
  readDelegationRecord,
81466
+ readDocs,
80787
81467
  readKeystoreAddress,
80788
81468
  readKeystoreVersion,
80789
81469
  readMovement,
@@ -80813,6 +81493,7 @@ export {
80813
81493
  roleGrants,
80814
81494
  runRefSchema,
80815
81495
  scheduleCeilingSweep,
81496
+ searchDocs,
80816
81497
  searchServicesResponseSchema,
80817
81498
  secretStore,
80818
81499
  secretsAllowed,
@@ -80820,6 +81501,7 @@ export {
80820
81501
  serviceSummarySchema,
80821
81502
  serviceTierSchema,
80822
81503
  setupSwarm,
81504
+ sha256Hex,
80823
81505
  slugifyDeviceName,
80824
81506
  spendCapsForWallet,
80825
81507
  spendCapsSchema,
@@ -80838,6 +81520,7 @@ export {
80838
81520
  sweepBack,
80839
81521
  sweepSolanaUsdc,
80840
81522
  sweepTarget,
81523
+ taskReservedAtomic,
80841
81524
  transferBetweenAccounts,
80842
81525
  treasuryRequestMovementId,
80843
81526
  unlockKeystore,