@vtxmacro/cli 2026.9.55 → 2026.9.56

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.
@@ -16,7 +16,7 @@ import { fileURLToPath } from "node:url";
16
16
  // agent-cli-release.json
17
17
  var agent_cli_release_default = {
18
18
  package_name: "@vtxmacro/cli",
19
- package_version: "2026.9.55",
19
+ package_version: "2026.9.56",
20
20
  codex_package_name: "@openai/codex",
21
21
  codex_version: "0.155.0",
22
22
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -296,6 +296,28 @@ var runtime_policy_generated_default = {
296
296
  apiWallets: 20
297
297
  },
298
298
  exchange_action_batch_size: 40
299
+ },
300
+ public_recovery: {
301
+ fallback_initial_seconds: 2,
302
+ fallback_max_seconds: 60,
303
+ permit_lease_seconds: 60,
304
+ recovery_initial_concurrency: 1,
305
+ recovery_max_concurrency: 8,
306
+ recovery_successes_per_step: 4,
307
+ recovery_min_spacing_ms: 100,
308
+ queue_poll_ms: 50,
309
+ recovery_queue_wait_seconds: 10,
310
+ recovery_quiet_seconds: 30
311
+ },
312
+ http: {
313
+ exchange: {
314
+ request_timeout_seconds: 10,
315
+ pool_timeout_seconds: 10
316
+ },
317
+ public_critical: {
318
+ request_timeout_seconds: 10,
319
+ pool_timeout_seconds: 5
320
+ }
299
321
  }
300
322
  }
301
323
  }
@@ -179,6 +179,28 @@ var runtime_policy_generated_default = {
179
179
  apiWallets: 20
180
180
  },
181
181
  exchange_action_batch_size: 40
182
+ },
183
+ public_recovery: {
184
+ fallback_initial_seconds: 2,
185
+ fallback_max_seconds: 60,
186
+ permit_lease_seconds: 60,
187
+ recovery_initial_concurrency: 1,
188
+ recovery_max_concurrency: 8,
189
+ recovery_successes_per_step: 4,
190
+ recovery_min_spacing_ms: 100,
191
+ queue_poll_ms: 50,
192
+ recovery_queue_wait_seconds: 10,
193
+ recovery_quiet_seconds: 30
194
+ },
195
+ http: {
196
+ exchange: {
197
+ request_timeout_seconds: 10,
198
+ pool_timeout_seconds: 10
199
+ },
200
+ public_critical: {
201
+ request_timeout_seconds: 10,
202
+ pool_timeout_seconds: 5
203
+ }
182
204
  }
183
205
  }
184
206
  }
package/bin/vtx.js CHANGED
@@ -77,7 +77,7 @@ var init_agent_cli_release = __esm({
77
77
  "agent-cli-release.json"() {
78
78
  agent_cli_release_default = {
79
79
  package_name: "@vtxmacro/cli",
80
- package_version: "2026.9.55",
80
+ package_version: "2026.9.56",
81
81
  codex_package_name: "@openai/codex",
82
82
  codex_version: "0.155.0",
83
83
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -20637,6 +20637,28 @@ var init_runtime_policy_generated = __esm({
20637
20637
  apiWallets: 20
20638
20638
  },
20639
20639
  exchange_action_batch_size: 40
20640
+ },
20641
+ public_recovery: {
20642
+ fallback_initial_seconds: 2,
20643
+ fallback_max_seconds: 60,
20644
+ permit_lease_seconds: 60,
20645
+ recovery_initial_concurrency: 1,
20646
+ recovery_max_concurrency: 8,
20647
+ recovery_successes_per_step: 4,
20648
+ recovery_min_spacing_ms: 100,
20649
+ queue_poll_ms: 50,
20650
+ recovery_queue_wait_seconds: 10,
20651
+ recovery_quiet_seconds: 30
20652
+ },
20653
+ http: {
20654
+ exchange: {
20655
+ request_timeout_seconds: 10,
20656
+ pool_timeout_seconds: 10
20657
+ },
20658
+ public_critical: {
20659
+ request_timeout_seconds: 10,
20660
+ pool_timeout_seconds: 5
20661
+ }
20640
20662
  }
20641
20663
  }
20642
20664
  }
@@ -42675,6 +42697,28 @@ var init_runtime_policy_generated2 = __esm({
42675
42697
  apiWallets: 20
42676
42698
  },
42677
42699
  exchange_action_batch_size: 40
42700
+ },
42701
+ public_recovery: {
42702
+ fallback_initial_seconds: 2,
42703
+ fallback_max_seconds: 60,
42704
+ permit_lease_seconds: 60,
42705
+ recovery_initial_concurrency: 1,
42706
+ recovery_max_concurrency: 8,
42707
+ recovery_successes_per_step: 4,
42708
+ recovery_min_spacing_ms: 100,
42709
+ queue_poll_ms: 50,
42710
+ recovery_queue_wait_seconds: 10,
42711
+ recovery_quiet_seconds: 30
42712
+ },
42713
+ http: {
42714
+ exchange: {
42715
+ request_timeout_seconds: 10,
42716
+ pool_timeout_seconds: 10
42717
+ },
42718
+ public_critical: {
42719
+ request_timeout_seconds: 10,
42720
+ pool_timeout_seconds: 5
42721
+ }
42678
42722
  }
42679
42723
  }
42680
42724
  }
@@ -48616,7 +48660,7 @@ var init_runtime_redaction = __esm({
48616
48660
  });
48617
48661
 
48618
48662
  // lib/runtime/hyperliquid-public-read.ts
48619
- var policy, weights, budgets, unpersistedBudgets, cache, pending2, PREFIX, abortError2, checkAbort, wait, storage, loadBudget, saveBudget, withBudgetLock, HyperliquidPublicReadError, cooldownError, admit, cooldown, cacheTtlMs, eligibleCache, reserveHyperliquidPublicReads, releaseHyperliquidPublicReadReservation, admitHyperliquidExchangeRequest, recordHyperliquidExchangeResponse, readHyperliquidPublicInfo;
48663
+ var policy, weights, recoveryPolicy, budgets, unpersistedBudgets, cache, pending2, PREFIX, abortError2, checkAbort, wait, storage, loadBudget, saveBudget, withBudgetLock, HyperliquidPublicReadError, getHyperliquidPublicReadBudget, cooldownError, recoveryWaitError, fallbackDelayMs, admit, cooldown, finishHyperliquidRecoveryRequest, assertHyperliquidRecoveryDispatch, cacheTtlMs, eligibleCache, reserveHyperliquidPublicReads, releaseHyperliquidPublicReadReservation, admitHyperliquidExchangeRequest, recordHyperliquidExchangeResponse, readHyperliquidPublicInfo;
48620
48664
  var init_hyperliquid_public_read = __esm({
48621
48665
  "lib/runtime/hyperliquid-public-read.ts"() {
48622
48666
  "use strict";
@@ -48627,6 +48671,7 @@ var init_hyperliquid_public_read = __esm({
48627
48671
  init_runtime_policy_generated();
48628
48672
  policy = runtime_policy_generated_default.exchange.hyperliquid.client_public_read_budget;
48629
48673
  weights = runtime_policy_generated_default.exchange.hyperliquid.read_budget.weights;
48674
+ recoveryPolicy = runtime_policy_generated_default.exchange.hyperliquid.public_recovery;
48630
48675
  budgets = /* @__PURE__ */ new Map();
48631
48676
  unpersistedBudgets = /* @__PURE__ */ new Set();
48632
48677
  cache = /* @__PURE__ */ new Map();
@@ -48684,7 +48729,15 @@ var init_hyperliquid_public_read = __esm({
48684
48729
  withBudgetLock = async (key, signal, work) => {
48685
48730
  checkAbort(signal);
48686
48731
  if (typeof navigator !== "undefined" && navigator.locks?.request) {
48687
- return navigator.locks.request(key, { ...signal ? { signal } : {} }, work);
48732
+ const result2 = await navigator.locks.request(key, { ...signal ? { signal } : {} }, () => {
48733
+ try {
48734
+ return { ok: true, value: work() };
48735
+ } catch (error48) {
48736
+ return { ok: false, error: error48 };
48737
+ }
48738
+ });
48739
+ if (!result2.ok) throw result2.error;
48740
+ return result2.value;
48688
48741
  }
48689
48742
  return work();
48690
48743
  };
@@ -48701,6 +48754,15 @@ var init_hyperliquid_public_read = __esm({
48701
48754
  this.name = "HyperliquidPublicReadError";
48702
48755
  }
48703
48756
  };
48757
+ getHyperliquidPublicReadBudget = (endpoint) => {
48758
+ const budget = loadBudget(`${PREFIX}${endpoint.replace(/\/$/, "")}`);
48759
+ return {
48760
+ usedWeight: budget.reservations.filter((r) => r.at > Date.now() - policy.window_seconds * 1e3).reduce((sum, r) => sum + r.weight, 0),
48761
+ maxWeight: policy.max_weight,
48762
+ windowSeconds: policy.window_seconds,
48763
+ cooldownUntilMs: budget.cooldownUntil
48764
+ };
48765
+ };
48704
48766
  cooldownError = (requestType, until) => new HyperliquidPublicReadError(
48705
48767
  "public_cooldown_active",
48706
48768
  "Hyperliquid public request cooldown is active.",
@@ -48708,48 +48770,129 @@ var init_hyperliquid_public_read = __esm({
48708
48770
  void 0,
48709
48771
  Math.max(0, until - Date.now())
48710
48772
  );
48711
- admit = async (apiUrl, requestType, signal, bundleWeight, bundleId, deadlineMs) => {
48773
+ recoveryWaitError = (requestType) => new HyperliquidPublicReadError(
48774
+ "public_recovery_wait",
48775
+ "Hyperliquid provider recovery could not dispatch before the request deadline.",
48776
+ requestType
48777
+ );
48778
+ fallbackDelayMs = (failures) => Math.min(
48779
+ recoveryPolicy.fallback_max_seconds * 1e3,
48780
+ recoveryPolicy.fallback_initial_seconds * 1e3 * 2 ** Math.min(30, Math.max(0, failures - 1)) * (0.5 + Math.random())
48781
+ );
48782
+ admit = async (apiUrl, requestType, signal, weightOverride, deadlineMs) => {
48712
48783
  const key = `${PREFIX}${apiUrl}`;
48713
- const weight = bundleWeight ?? weights[requestType] ?? policy.default_request_weight;
48714
- if (!(weight > 0 && weight <= policy.max_weight)) throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid public request exceeds the available allowance.", requestType, void 0, void 0, weight, void 0, policy.max_weight);
48715
- for (; ; ) {
48716
- checkAbort(signal);
48717
- const { delay: delay2, usedWeight } = await withBudgetLock(key, signal, () => {
48718
- const now = Date.now();
48719
- const budget = loadBudget(key);
48720
- budget.reservations = budget.reservations.filter((r) => r.at > now - policy.window_seconds * 1e3);
48721
- if (budget.cooldownUntil > now) throw cooldownError(requestType, budget.cooldownUntil);
48722
- let used = budget.reservations.reduce((sum, r) => sum + r.weight, 0);
48723
- const usedWeight2 = used;
48724
- if (used + weight <= policy.max_weight) {
48725
- budget.reservations.push({ at: now, weight, ...bundleId ? { id: bundleId } : {} });
48784
+ const weight = weightOverride ?? weights[requestType] ?? policy.default_request_weight;
48785
+ const ticket = crypto.randomUUID();
48786
+ const deadline = Math.min(deadlineMs ?? Infinity, Date.now() + recoveryPolicy.recovery_queue_wait_seconds * 1e3);
48787
+ let admitted = false;
48788
+ try {
48789
+ for (; ; ) {
48790
+ checkAbort(signal);
48791
+ if (deadlineMs != null && Date.now() >= deadlineMs) throw recoveryWaitError(requestType);
48792
+ const result2 = await withBudgetLock(key, signal, () => {
48793
+ const now = Date.now();
48794
+ const budget = loadBudget(key);
48795
+ if (budget.cooldownUntil > now) throw cooldownError(requestType, budget.cooldownUntil);
48796
+ const recovery = budget.recovery;
48797
+ let permit = null;
48798
+ if (recovery) {
48799
+ recovery.active = recovery.active.filter((entry) => entry.expiresAt > now);
48800
+ recovery.queue = recovery.queue.filter((entry) => entry.expiresAt > now);
48801
+ if (now >= deadline) throw recoveryWaitError(requestType);
48802
+ if (!recovery.queue.some((entry) => entry.id === ticket)) recovery.queue.push({ id: ticket, expiresAt: deadline });
48803
+ if (recovery.queue[0]?.id !== ticket || recovery.active.length >= recovery.concurrency || recovery.nextAt > now) {
48804
+ saveBudget(key, budget);
48805
+ return { ready: false, permit: null };
48806
+ }
48807
+ recovery.queue.shift();
48808
+ const expiresAt = now + recoveryPolicy.permit_lease_seconds * 1e3;
48809
+ recovery.active.push({ id: ticket, expiresAt });
48810
+ recovery.nextAt = now + recoveryPolicy.recovery_min_spacing_ms;
48811
+ permit = { endpoint: apiUrl, id: ticket, generation: recovery.generation, expiresAt };
48812
+ }
48813
+ budget.reservations = budget.reservations.filter((entry) => entry.at > now - policy.window_seconds * 1e3);
48814
+ budget.reservations.push({ at: now, weight });
48726
48815
  saveBudget(key, budget);
48727
- return { delay: 0, usedWeight: usedWeight2 };
48816
+ return { ready: true, permit };
48817
+ });
48818
+ if (result2.ready) {
48819
+ admitted = true;
48820
+ return result2.permit;
48728
48821
  }
48729
- for (const reservation of budget.reservations) {
48730
- used -= reservation.weight;
48731
- if (used + weight <= policy.max_weight) return { delay: Math.max(1, reservation.at + policy.window_seconds * 1e3 - now), usedWeight: usedWeight2 };
48822
+ await wait(Math.min(recoveryPolicy.queue_poll_ms, Math.max(1, deadline - Date.now())), signal);
48823
+ }
48824
+ } finally {
48825
+ if (!admitted) await withBudgetLock(key, void 0, () => {
48826
+ const budget = loadBudget(key);
48827
+ if (budget.recovery) {
48828
+ budget.recovery.queue = budget.recovery.queue.filter((entry) => entry.id !== ticket);
48829
+ saveBudget(key, budget);
48732
48830
  }
48733
- throw new Error("Invalid Hyperliquid public read budget.");
48734
48831
  });
48735
- if (!delay2) return;
48736
- if (bundleWeight != null || deadlineMs != null && Date.now() + delay2 >= deadlineMs) {
48737
- throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid local public request allowance is exhausted.", requestType, void 0, delay2, weight, usedWeight, policy.max_weight);
48738
- }
48739
- await wait(delay2, signal);
48740
48832
  }
48741
48833
  };
48742
48834
  cooldown = async (apiUrl, retryAfter) => {
48743
48835
  const key = `${PREFIX}${apiUrl}`;
48744
48836
  await withBudgetLock(key, void 0, () => {
48745
48837
  const now = Date.now();
48746
- const seconds = retryAfter == null ? NaN : Number(retryAfter);
48747
- const providerUntil = Number.isFinite(seconds) ? now + seconds * 1e3 : Date.parse(retryAfter ?? "");
48748
48838
  const budget = loadBudget(key);
48749
- budget.cooldownUntil = Math.max(budget.cooldownUntil, now + policy.cooldown_seconds * 1e3, Number.isFinite(providerUntil) ? providerUntil : 0);
48839
+ if (!budget.recovery && now - (budget.lastFailure ?? 0) >= recoveryPolicy.recovery_quiet_seconds * 1e3) budget.failures = 0;
48840
+ budget.failures = (budget.failures ?? 0) + 1;
48841
+ const trimmed = retryAfter?.trim() ?? "";
48842
+ const seconds = trimmed !== "" ? Number(trimmed) : NaN;
48843
+ const providerUntil = Number.isFinite(seconds) && seconds >= 0 ? now + seconds * 1e3 : trimmed && !Number.isFinite(seconds) ? Date.parse(trimmed) : NaN;
48844
+ const until = Number.isFinite(providerUntil) ? Math.max(now, Number(providerUntil)) : now + fallbackDelayMs(budget.failures);
48845
+ budget.cooldownUntil = Math.max(budget.cooldownUntil, until);
48846
+ budget.lastFailure = now;
48847
+ budget.recovery = {
48848
+ generation: crypto.randomUUID(),
48849
+ concurrency: recoveryPolicy.recovery_initial_concurrency,
48850
+ successes: 0,
48851
+ nextAt: budget.cooldownUntil,
48852
+ queue: budget.recovery?.queue ?? [],
48853
+ active: []
48854
+ };
48855
+ saveBudget(key, budget);
48856
+ });
48857
+ };
48858
+ finishHyperliquidRecoveryRequest = async (permit, outcome) => {
48859
+ if (!permit) return;
48860
+ const key = `${PREFIX}${permit.endpoint}`;
48861
+ await withBudgetLock(key, void 0, () => {
48862
+ const budget = loadBudget(key);
48863
+ const recovery = budget.recovery;
48864
+ if (!recovery || recovery.generation !== permit.generation) return;
48865
+ const now = Date.now();
48866
+ const member = recovery.active.find((entry) => entry.id === permit.id);
48867
+ recovery.active = recovery.active.filter((entry) => entry.id !== permit.id);
48868
+ if (member && member.expiresAt > now) {
48869
+ if (outcome === "success") {
48870
+ recovery.successes += 1;
48871
+ if (recovery.successes >= recoveryPolicy.recovery_successes_per_step) {
48872
+ if (recovery.concurrency >= recoveryPolicy.recovery_max_concurrency) {
48873
+ if (recovery.queue.length === 0 && recovery.active.length === 0) delete budget.recovery;
48874
+ } else {
48875
+ recovery.concurrency = Math.min(recoveryPolicy.recovery_max_concurrency, recovery.concurrency * 2);
48876
+ recovery.successes = 0;
48877
+ }
48878
+ }
48879
+ } else if (outcome === "failure") {
48880
+ recovery.successes = 0;
48881
+ recovery.concurrency = recoveryPolicy.recovery_initial_concurrency;
48882
+ recovery.nextAt = Math.max(recovery.nextAt, now + fallbackDelayMs(budget.failures ?? 1));
48883
+ }
48884
+ }
48750
48885
  saveBudget(key, budget);
48751
48886
  });
48752
48887
  };
48888
+ assertHyperliquidRecoveryDispatch = (endpoint, permit, requestType) => {
48889
+ const budget = loadBudget(`${PREFIX}${endpoint.replace(/\/$/, "")}`);
48890
+ if (budget.cooldownUntil > Date.now()) throw cooldownError(requestType, budget.cooldownUntil);
48891
+ if (permit && (permit.expiresAt <= Date.now() || budget.recovery && (budget.recovery.generation !== permit.generation || !budget.recovery.active.some((entry) => entry.id === permit.id && entry.expiresAt > Date.now())))) {
48892
+ throw recoveryWaitError(requestType);
48893
+ }
48894
+ if (!permit && budget.recovery) throw recoveryWaitError(requestType);
48895
+ };
48753
48896
  cacheTtlMs = (body, options) => {
48754
48897
  const requestType = String(body.type);
48755
48898
  return (["meta", "spotMeta", "perpDexs"].includes(requestType) ? policy.metadata_ttl_seconds : requestType === "userFees" ? policy.user_fees_ttl_seconds : options.cacheAccountMode && ["userAbstraction", "userDexAbstraction"].includes(requestType) ? policy.account_mode_ttl_seconds : 0) * 1e3;
@@ -48761,47 +48904,26 @@ var init_hyperliquid_public_read = __esm({
48761
48904
  return ttl > 0 && existing && Date.now() >= existing.fetchedAtMs && horizon - existing.fetchedAtMs < ttl ? existing : void 0;
48762
48905
  };
48763
48906
  reserveHyperliquidPublicReads = async (endpoint, bodies, signal, options = {}) => {
48907
+ checkAbort(signal);
48764
48908
  const apiUrl = endpoint.replace(/\/$/, "");
48765
48909
  const cached2 = /* @__PURE__ */ new Map();
48766
- const remaining = /* @__PURE__ */ new Map();
48767
- let weight = 0;
48768
48910
  for (const body of bodies) {
48769
- const key = JSON.stringify(body);
48770
48911
  const hit = eligibleCache(apiUrl, body, options);
48771
- if (hit) {
48772
- cached2.set(key, hit);
48773
- continue;
48774
- }
48775
- remaining.set(key, (remaining.get(key) ?? 0) + 1);
48776
- weight += weights[String(body.type)] ?? policy.default_request_weight;
48912
+ if (hit) cached2.set(JSON.stringify(body), hit);
48777
48913
  }
48778
- const id2 = crypto.randomUUID();
48779
- const admittedAtMs = Date.now();
48780
- if (weight) await admit(apiUrl, "bundle", signal, weight, id2, options.cacheValidUntilMs);
48781
- return { id: id2, closed: false, endpoint: apiUrl, expiresAtMs: admittedAtMs + policy.window_seconds * 1e3, remaining, cached: cached2, cacheValidUntilMs: options.cacheValidUntilMs };
48914
+ return { closed: false, endpoint: apiUrl, cached: cached2, cacheValidUntilMs: options.cacheValidUntilMs };
48782
48915
  };
48783
48916
  releaseHyperliquidPublicReadReservation = async (reservation) => {
48784
48917
  reservation.closed = true;
48785
- const key = `${PREFIX}${reservation.endpoint}`;
48786
- await withBudgetLock(key, void 0, () => {
48787
- const budget = loadBudget(key);
48788
- const row = budget.reservations.find((entry) => entry.id === reservation.id);
48789
- if (row) {
48790
- const unused = [...reservation.remaining].reduce((sum, [body, count4]) => sum + (weights[String(JSON.parse(body).type)] ?? policy.default_request_weight) * count4, 0);
48791
- row.weight = Math.max(0, row.weight - unused);
48792
- budget.reservations = budget.reservations.filter((entry) => entry.weight > 0);
48793
- saveBudget(key, budget);
48794
- }
48795
- reservation.remaining.clear();
48796
- });
48797
48918
  };
48798
48919
  admitHyperliquidExchangeRequest = async (endpoint, action, signal) => {
48799
48920
  const batchLength = Math.max(0, ...[action.orders, action.cancels, action.modifies].filter(Array.isArray).map((value) => value.length));
48800
48921
  const weight = weights.exchange + Math.floor(batchLength / runtime_policy_generated_default.exchange.hyperliquid.read_budget.exchange_action_batch_size);
48801
- await admit(endpoint.replace(/\/$/, ""), "exchange", signal, weight);
48922
+ return admit(endpoint.replace(/\/$/, ""), "exchange", signal, weight);
48802
48923
  };
48803
- recordHyperliquidExchangeResponse = async (endpoint, response) => {
48924
+ recordHyperliquidExchangeResponse = async (endpoint, response, permit) => {
48804
48925
  if (response.status === 429) await cooldown(endpoint.replace(/\/$/, ""), response.headers.get("Retry-After"));
48926
+ await finishHyperliquidRecoveryRequest(permit, response.status < 400 ? "success" : "failure");
48805
48927
  };
48806
48928
  readHyperliquidPublicInfo = async (endpoint, body, options = {}) => {
48807
48929
  const apiUrl = endpoint.replace(/\/$/, "");
@@ -48810,71 +48932,101 @@ var init_hyperliquid_public_read = __esm({
48810
48932
  const ttlMs = cacheTtlMs(body, cacheOptions);
48811
48933
  const key = `${apiUrl}:${JSON.stringify(body)}`;
48812
48934
  checkAbort(options.signal);
48813
- if (ttlMs) {
48814
- for (; ; ) {
48815
- const existing = options.reservation?.cached.get(JSON.stringify(body)) ?? eligibleCache(apiUrl, body, cacheOptions);
48816
- if (existing) return existing;
48817
- const running = pending2.get(key);
48818
- if (!running) break;
48819
- await new Promise((resolve13, reject) => {
48820
- const aborted2 = () => {
48821
- options.signal?.removeEventListener("abort", aborted2);
48822
- reject(abortError2(options.signal));
48823
- };
48824
- options.signal?.addEventListener("abort", aborted2, { once: true });
48825
- running.then(() => {
48826
- options.signal?.removeEventListener("abort", aborted2);
48827
- resolve13();
48828
- });
48829
- });
48830
- checkAbort(options.signal);
48831
- }
48832
- }
48935
+ const transportPolicy = runtime_policy_generated_default.exchange.hyperliquid.http.public_critical;
48936
+ const deadline = Math.min(
48937
+ Date.now() + (transportPolicy.request_timeout_seconds + transportPolicy.pool_timeout_seconds) * 1e3,
48938
+ options.deadlineMs ?? Infinity,
48939
+ options.reservation?.cacheValidUntilMs ?? Infinity
48940
+ );
48941
+ const controller = new AbortController();
48942
+ const signal = controller.signal;
48943
+ const callerAborted = () => controller.abort(abortError2(options.signal));
48944
+ options.signal?.addEventListener("abort", callerAborted, { once: true });
48945
+ let timer;
48946
+ const armDeadline = (until) => {
48947
+ clearTimeout(timer);
48948
+ const timeout = () => controller.abort(new DOMException("Hyperliquid public request timed out.", "TimeoutError"));
48949
+ if (until <= Date.now()) timeout();
48950
+ else timer = setTimeout(timeout, until - Date.now());
48951
+ };
48952
+ armDeadline(deadline);
48953
+ let permit = null;
48954
+ let outcome = "cancelled";
48833
48955
  let settled;
48834
- if (ttlMs) pending2.set(key, new Promise((resolve13) => {
48835
- settled = resolve13;
48836
- }));
48956
+ let acquiring = false;
48837
48957
  try {
48838
- const reservedKey = JSON.stringify(body);
48839
- const remaining = options.reservation?.remaining.get(reservedKey) ?? 0;
48840
- if (options.reservation?.endpoint === apiUrl && remaining > 0) {
48841
- if (Date.now() >= options.reservation.expiresAtMs) throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid public read reservation expired.", requestType);
48842
- await withBudgetLock(`${PREFIX}${apiUrl}`, options.signal, () => {
48843
- if (options.reservation.closed) throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid public read bundle is closed.", requestType);
48844
- if ((options.reservation.remaining.get(reservedKey) ?? 0) <= 0) throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid request is outside its admitted bundle.", requestType);
48845
- const budget = loadBudget(`${PREFIX}${apiUrl}`);
48846
- const row = budget.reservations.find((entry) => entry.id === options.reservation.id);
48847
- if (!row || Date.now() >= options.reservation.expiresAtMs) throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid public read reservation expired.", requestType);
48848
- row.at = Date.now();
48849
- budget.reservations.sort((a, b2) => a.at - b2.at);
48850
- options.reservation.remaining.set(reservedKey, (options.reservation.remaining.get(reservedKey) ?? 0) - 1);
48851
- saveBudget(`${PREFIX}${apiUrl}`, budget);
48958
+ checkAbort(signal);
48959
+ if (ttlMs) {
48960
+ for (; ; ) {
48961
+ const existing = options.reservation?.cached.get(JSON.stringify(body)) ?? eligibleCache(apiUrl, body, cacheOptions);
48962
+ if (existing) return existing;
48963
+ const running = pending2.get(key);
48964
+ if (!running) break;
48965
+ await new Promise((resolve13, reject) => {
48966
+ const aborted2 = () => {
48967
+ signal.removeEventListener("abort", aborted2);
48968
+ reject(abortError2(signal));
48969
+ };
48970
+ signal.addEventListener("abort", aborted2, { once: true });
48971
+ running.then(() => {
48972
+ signal.removeEventListener("abort", aborted2);
48973
+ resolve13();
48974
+ });
48975
+ });
48976
+ checkAbort(signal);
48977
+ }
48978
+ pending2.set(key, new Promise((resolve13) => {
48979
+ settled = resolve13;
48980
+ }));
48981
+ }
48982
+ if (options.reservation?.closed) throw new DOMException("Hyperliquid account read was cancelled.", "AbortError");
48983
+ acquiring = true;
48984
+ permit = await admit(apiUrl, requestType, signal, void 0, deadline);
48985
+ acquiring = false;
48986
+ armDeadline(Math.min(deadline, permit?.expiresAt ?? Infinity));
48987
+ checkAbort(signal);
48988
+ if (options.reservation?.closed) throw new DOMException("Hyperliquid account read was cancelled.", "AbortError");
48989
+ assertHyperliquidRecoveryDispatch(apiUrl, permit, requestType);
48990
+ outcome = "failure";
48991
+ let transportAborted;
48992
+ let response;
48993
+ let payload;
48994
+ try {
48995
+ const aborted2 = new Promise((_resolve, reject) => {
48996
+ transportAborted = () => reject(abortError2(signal));
48997
+ signal.addEventListener("abort", transportAborted, { once: true });
48852
48998
  });
48853
- } else if (options.reservation) throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid request is outside its admitted bundle.", requestType);
48854
- else await admit(apiUrl, requestType, options.signal, void 0, void 0, options.deadlineMs);
48855
- checkAbort(options.signal);
48856
- if (options.reservation?.closed) throw new HyperliquidPublicReadError("public_budget_exhausted", "Hyperliquid public read bundle is closed.", requestType);
48857
- const cooldownUntil = loadBudget(`${PREFIX}${apiUrl}`).cooldownUntil;
48858
- if (cooldownUntil > Date.now()) throw cooldownError(requestType, cooldownUntil);
48859
- const response = await fetch(`${apiUrl}/info`, {
48860
- method: "POST",
48861
- headers: { "Content-Type": "application/json" },
48862
- body: JSON.stringify(body),
48863
- signal: options.signal
48864
- });
48865
- if (response.status === 429) await cooldown(apiUrl, response.headers.get("Retry-After"));
48866
- const payload = await response.json().catch((error48) => {
48867
- if (error48 instanceof SyntaxError) return {};
48868
- throw error48;
48869
- });
48999
+ const transport = (async () => {
49000
+ const received = await fetch(`${apiUrl}/info`, {
49001
+ method: "POST",
49002
+ headers: { "Content-Type": "application/json" },
49003
+ body: JSON.stringify(body),
49004
+ signal
49005
+ });
49006
+ checkAbort(signal);
49007
+ if (received.status === 429) {
49008
+ await cooldown(apiUrl, received.headers.get("Retry-After"));
49009
+ throw new HyperliquidPublicReadError("public_rate_limited", "Hyperliquid public API returned HTTP 429.", requestType, 429);
49010
+ }
49011
+ const receivedPayload = await received.json().catch((error48) => {
49012
+ if (error48 instanceof SyntaxError) return {};
49013
+ throw error48;
49014
+ });
49015
+ checkAbort(signal);
49016
+ return { response: received, payload: receivedPayload };
49017
+ })();
49018
+ ({ response, payload } = await Promise.race([transport, aborted2]));
49019
+ } finally {
49020
+ if (transportAborted) signal.removeEventListener("abort", transportAborted);
49021
+ }
48870
49022
  if (!response.ok) {
48871
49023
  const detail = sanitizeRuntimeDiagnosticValue(typeof payload?.response === "string" ? payload.response : typeof payload?.detail === "string" ? payload.detail : "");
48872
- if (response.status === 429) throw new HyperliquidPublicReadError("public_rate_limited", "Hyperliquid public API returned HTTP 429.", requestType, 429);
48873
49024
  throw Object.assign(new Error(`Hyperliquid info request failed (${response.status}) request_type=${requestType}.${detail ? ` ${detail}` : ""}`), {
48874
49025
  status: response.status,
48875
49026
  requestType
48876
49027
  });
48877
49028
  }
49029
+ outcome = "success";
48878
49030
  const result2 = { payload, fetchedAtMs: Date.now() };
48879
49031
  if (ttlMs) {
48880
49032
  cache.set(key, result2);
@@ -48882,14 +49034,26 @@ var init_hyperliquid_public_read = __esm({
48882
49034
  }
48883
49035
  return result2;
48884
49036
  } catch (error48) {
48885
- if (options.signal?.aborted || error48 instanceof HyperliquidPublicReadError) throw error48;
49037
+ if (options.signal?.aborted) {
49038
+ outcome = "cancelled";
49039
+ throw abortError2(options.signal);
49040
+ }
49041
+ if (acquiring && signal.aborted && signal.reason?.name === "TimeoutError") throw recoveryWaitError(requestType);
49042
+ if (error48 instanceof HyperliquidPublicReadError) throw error48;
48886
49043
  if (error48 instanceof TypeError) throw new HyperliquidPublicReadError("public_network_error", "Hyperliquid public request network failure.", requestType);
48887
49044
  if (error48 instanceof Error && (error48.name === "TimeoutError" || error48.name === "AbortError")) throw new HyperliquidPublicReadError("public_timeout", "Hyperliquid public request timed out.", requestType);
48888
49045
  throw error48;
48889
49046
  } finally {
48890
- if (ttlMs) {
48891
- pending2.delete(key);
48892
- settled?.();
49047
+ clearTimeout(timer);
49048
+ options.signal?.removeEventListener("abort", callerAborted);
49049
+ controller.abort();
49050
+ try {
49051
+ await finishHyperliquidRecoveryRequest(permit, outcome);
49052
+ } finally {
49053
+ if (settled) {
49054
+ pending2.delete(key);
49055
+ settled();
49056
+ }
48893
49057
  }
48894
49058
  }
48895
49059
  };
@@ -49987,7 +50151,7 @@ var init_network_debug = __esm({
49987
50151
  });
49988
50152
 
49989
50153
  // lib/runtime/hyperliquid-account-state-adapter.ts
49990
- var unsupportedUserActiveAssetCache, MAX_UNSUPPORTED_USER_ACTIVE_ASSET_CACHE_KEYS, UNSUPPORTED_USER_ACTIVE_ASSET_STORAGE_KEY, USER_FILLS_CACHE_STORAGE_PREFIX, ACCOUNT_STATE_CACHE_STORAGE_PREFIX, INFO_RATE_LIMIT_COOLDOWN_STORAGE_PREFIX, INFO_RATE_LIMIT_COOLDOWN_MIN_MS, INFO_RATE_LIMIT_COOLDOWN_MAX_MS, browserUserFillsCache, browserUserFillsInFlight, HYPERLIQUID_USER_FILLS_PROVIDER_PAGE_CAP, browserAccountStateCache, browserAccountStateInFlight, browserInfoRateLimitCooldowns, UNSUPPORTED_USER_ACTIVE_ASSET_DETAIL_PATTERNS, unsupportedUserActiveAssetCacheHydrated, hydrateUnsupportedUserActiveAssetCache, persistUnsupportedUserActiveAssetCache, toPositiveFinite, toFinite, firstFiniteNumber, parseFundingUsdcFromInfoPosition, buildActiveAssetCacheKey, buildUserFillsCacheKey, buildUserFillsStorageKey, buildAccountStateCacheKey, buildAccountStateStorageKey, normalizeInfoRateLimitApiUrl, normalizeInfoRequestType, buildInfoRateLimitCooldownKey, buildInfoRateLimitCooldownStorageKey, readInfoRateLimitCooldownUntilMs, hasActiveInfoRateLimitCooldown, writeInfoRateLimitCooldown, extractInfoRequestTypeFromRateLimitError, recordInfoRateLimitCooldownFromError, requireNonNegativeConfigSeconds, requireAccountStateCacheConfig, requireUserFillsCacheConfig, normalizeFillSymbol, buildSymbolFilter, filterFillsBySymbols, readBrowserUserFillsCache, writeBrowserUserFillsCache, cloneBrowserAccountStateResult, withAccountStateEvidence, readBrowserAccountStateCache, writeBrowserAccountStateCache, isRateLimitError, createAbortError, waitForSharedBrowserRequest, waitForSharedBrowserUserFills, getBrowserActiveAssetTypeCandidates, normalizeHyperliquidErrorDetail, isUnsupportedUserActiveAssetError, rememberUnsupportedUserActiveAssetKey, parseInfoPositions, requireFinitePromptMetric, requirePromptClearinghouseState, requirePromptSpotState, normalizePerpDexName, listConfiguredPerpDexs, listPerpDexsForAccountState, prefixHip3PositionCoins, aggregateClearinghouseResponses, hasCompletePerpDexAccountMetrics, normalizeBrowserFillToTradeHistory, isPerpFill, fetchHyperliquidInfoPayload, requireHyperliquidApiUrl, requireWalletAddress, parseInfoPositionSnapshot, fetchBrowserAccountState, fetchBrowserAccountStateUncached, fetchBrowserUserFills, fetchBrowserPositionSnapshot;
50154
+ var unsupportedUserActiveAssetCache, MAX_UNSUPPORTED_USER_ACTIVE_ASSET_CACHE_KEYS, UNSUPPORTED_USER_ACTIVE_ASSET_STORAGE_KEY, USER_FILLS_CACHE_STORAGE_PREFIX, ACCOUNT_STATE_CACHE_STORAGE_PREFIX, browserUserFillsCache, browserUserFillsInFlight, HYPERLIQUID_USER_FILLS_PROVIDER_PAGE_CAP, browserAccountStateCache, browserAccountStateInFlight, UNSUPPORTED_USER_ACTIVE_ASSET_DETAIL_PATTERNS, unsupportedUserActiveAssetCacheHydrated, hydrateUnsupportedUserActiveAssetCache, persistUnsupportedUserActiveAssetCache, toPositiveFinite, toFinite, firstFiniteNumber, parseFundingUsdcFromInfoPosition, buildActiveAssetCacheKey, buildUserFillsCacheKey, buildUserFillsStorageKey, buildAccountStateCacheKey, buildAccountStateStorageKey, hasActiveInfoRateLimitCooldown, requireNonNegativeConfigSeconds, requireAccountStateCacheConfig, requireUserFillsCacheConfig, normalizeFillSymbol, buildSymbolFilter, filterFillsBySymbols, readBrowserUserFillsCache, writeBrowserUserFillsCache, cloneBrowserAccountStateResult, withAccountStateEvidence, readBrowserAccountStateCache, writeBrowserAccountStateCache, isRateLimitError, createAbortError, waitForSharedBrowserRequest, waitForSharedBrowserUserFills, getBrowserActiveAssetTypeCandidates, normalizeHyperliquidErrorDetail, isUnsupportedUserActiveAssetError, rememberUnsupportedUserActiveAssetKey, parseInfoPositions, requireFinitePromptMetric, requirePromptClearinghouseState, requirePromptSpotState, normalizePerpDexName, listConfiguredPerpDexs, listPerpDexsForAccountState, prefixHip3PositionCoins, aggregateClearinghouseResponses, hasCompletePerpDexAccountMetrics, normalizeBrowserFillToTradeHistory, isPerpFill, awaitAccountReads, fetchHyperliquidInfoPayload, requireHyperliquidApiUrl, requireWalletAddress, parseInfoPositionSnapshot, fetchBrowserAccountState, fetchBrowserAccountStateUncached, fetchBrowserUserFills, fetchBrowserPositionSnapshot;
49991
50155
  var init_hyperliquid_account_state_adapter = __esm({
49992
50156
  "lib/runtime/hyperliquid-account-state-adapter.ts"() {
49993
50157
  "use strict";
@@ -50008,15 +50172,11 @@ var init_hyperliquid_account_state_adapter = __esm({
50008
50172
  UNSUPPORTED_USER_ACTIVE_ASSET_STORAGE_KEY = "vtx:hyperliquid:unsupported-user-active-asset:v1";
50009
50173
  USER_FILLS_CACHE_STORAGE_PREFIX = "vtx:hyperliquid:user-fills:v1:";
50010
50174
  ACCOUNT_STATE_CACHE_STORAGE_PREFIX = "vtx:hyperliquid:account-state:v1:";
50011
- INFO_RATE_LIMIT_COOLDOWN_STORAGE_PREFIX = "vtx:hyperliquid:info-rate-limit-cooldown:v1:";
50012
- INFO_RATE_LIMIT_COOLDOWN_MIN_MS = 5e3;
50013
- INFO_RATE_LIMIT_COOLDOWN_MAX_MS = 6e4;
50014
50175
  browserUserFillsCache = /* @__PURE__ */ new Map();
50015
50176
  browserUserFillsInFlight = /* @__PURE__ */ new Map();
50016
50177
  HYPERLIQUID_USER_FILLS_PROVIDER_PAGE_CAP = 2e3;
50017
50178
  browserAccountStateCache = /* @__PURE__ */ new Map();
50018
50179
  browserAccountStateInFlight = /* @__PURE__ */ new Map();
50019
- browserInfoRateLimitCooldowns = /* @__PURE__ */ new Map();
50020
50180
  UNSUPPORTED_USER_ACTIVE_ASSET_DETAIL_PATTERNS = [
50021
50181
  /unsupported/i,
50022
50182
  /not\s+supported/i,
@@ -50134,93 +50294,7 @@ var init_hyperliquid_account_state_adapter = __esm({
50134
50294
  buildAccountStateStorageKey = (cacheKey) => {
50135
50295
  return `${ACCOUNT_STATE_CACHE_STORAGE_PREFIX}${cacheKey}`;
50136
50296
  };
50137
- normalizeInfoRateLimitApiUrl = (apiUrl) => {
50138
- return String(apiUrl || "").trim().replace(/\/$/, "").toLowerCase();
50139
- };
50140
- normalizeInfoRequestType = (requestType) => {
50141
- return String(requestType || "").trim() || "info";
50142
- };
50143
- buildInfoRateLimitCooldownKey = (apiUrl, requestType) => {
50144
- return [
50145
- normalizeInfoRateLimitApiUrl(apiUrl),
50146
- normalizeInfoRequestType(requestType)
50147
- ].join("::");
50148
- };
50149
- buildInfoRateLimitCooldownStorageKey = (cooldownKey) => {
50150
- return `${INFO_RATE_LIMIT_COOLDOWN_STORAGE_PREFIX}${encodeURIComponent(cooldownKey)}`;
50151
- };
50152
- readInfoRateLimitCooldownUntilMs = (apiUrl, requestType, nowMs = Date.now()) => {
50153
- const cooldownKey = buildInfoRateLimitCooldownKey(apiUrl, requestType);
50154
- const memoryValue = browserInfoRateLimitCooldowns.get(cooldownKey);
50155
- if (Number.isFinite(memoryValue)) {
50156
- if (Number(memoryValue) > nowMs) {
50157
- return Number(memoryValue);
50158
- }
50159
- browserInfoRateLimitCooldowns.delete(cooldownKey);
50160
- }
50161
- if (typeof window === "undefined" || !window.localStorage) {
50162
- return 0;
50163
- }
50164
- try {
50165
- const storageKey = buildInfoRateLimitCooldownStorageKey(cooldownKey);
50166
- const rawValue = window.localStorage.getItem(storageKey);
50167
- if (!rawValue) {
50168
- return 0;
50169
- }
50170
- const parsed = JSON.parse(rawValue);
50171
- const untilMs = Number(parsed?.untilMs);
50172
- if (!Number.isFinite(untilMs) || untilMs <= nowMs) {
50173
- window.localStorage.removeItem(storageKey);
50174
- return 0;
50175
- }
50176
- browserInfoRateLimitCooldowns.set(cooldownKey, untilMs);
50177
- return untilMs;
50178
- } catch {
50179
- return 0;
50180
- }
50181
- };
50182
- hasActiveInfoRateLimitCooldown = (apiUrl, requestTypes, nowMs = Date.now()) => requestTypes.some((requestType) => readInfoRateLimitCooldownUntilMs(apiUrl, requestType, nowMs) > nowMs);
50183
- writeInfoRateLimitCooldown = (apiUrl, requestType, staleIf429MaxAgeMs, nowMs = Date.now()) => {
50184
- if (!Number.isFinite(staleIf429MaxAgeMs) || staleIf429MaxAgeMs <= 0) {
50185
- return;
50186
- }
50187
- const cooldownKey = buildInfoRateLimitCooldownKey(apiUrl, requestType);
50188
- const cooldownMs = Math.max(
50189
- INFO_RATE_LIMIT_COOLDOWN_MIN_MS,
50190
- Math.min(INFO_RATE_LIMIT_COOLDOWN_MAX_MS, staleIf429MaxAgeMs)
50191
- );
50192
- const untilMs = nowMs + cooldownMs;
50193
- browserInfoRateLimitCooldowns.set(cooldownKey, untilMs);
50194
- if (typeof window === "undefined" || !window.localStorage) {
50195
- return;
50196
- }
50197
- try {
50198
- window.localStorage.setItem(
50199
- buildInfoRateLimitCooldownStorageKey(cooldownKey),
50200
- JSON.stringify({ untilMs })
50201
- );
50202
- } catch {
50203
- }
50204
- };
50205
- extractInfoRequestTypeFromRateLimitError = (error48) => {
50206
- const explicitType = normalizeInfoRequestType(error48?.requestType);
50207
- if (explicitType !== "info") {
50208
- return explicitType;
50209
- }
50210
- const detail = normalizeHyperliquidErrorDetail(error48);
50211
- const match = /\brequest_type=([A-Za-z0-9_:-]+)/.exec(detail);
50212
- return match?.[1] ? normalizeInfoRequestType(match[1]) : null;
50213
- };
50214
- recordInfoRateLimitCooldownFromError = (apiUrl, error48, staleIf429MaxAgeMs, nowMs = Date.now()) => {
50215
- if (!isRateLimitError(error48)) {
50216
- return;
50217
- }
50218
- const requestType = extractInfoRequestTypeFromRateLimitError(error48);
50219
- if (!requestType) {
50220
- return;
50221
- }
50222
- writeInfoRateLimitCooldown(apiUrl, requestType, staleIf429MaxAgeMs, nowMs);
50223
- };
50297
+ hasActiveInfoRateLimitCooldown = (apiUrl, _requestTypes, nowMs = Date.now()) => getHyperliquidPublicReadBudget(apiUrl).cooldownUntilMs > nowMs;
50224
50298
  requireNonNegativeConfigSeconds = (value, path) => {
50225
50299
  const numeric2 = Number(value);
50226
50300
  if (!Number.isFinite(numeric2) || numeric2 < 0) {
@@ -50744,6 +50818,19 @@ var init_hyperliquid_account_state_adapter = __esm({
50744
50818
  }
50745
50819
  return true;
50746
50820
  };
50821
+ awaitAccountReads = async (reads) => {
50822
+ try {
50823
+ return await Promise.all(reads);
50824
+ } catch (error48) {
50825
+ if (error48 instanceof HyperliquidPublicReadError && error48.code === "public_cooldown_active") {
50826
+ const settled = await Promise.allSettled(reads);
50827
+ for (const result2 of settled) {
50828
+ if (result2.status === "rejected" && result2.reason instanceof HyperliquidPublicReadError && result2.reason.code === "public_rate_limited") throw result2.reason;
50829
+ }
50830
+ }
50831
+ throw error48;
50832
+ }
50833
+ };
50747
50834
  fetchHyperliquidInfoPayload = async (apiUrl, body, signal, reservation) => {
50748
50835
  const trace = startRuntimeNetworkDebugTrace({ kind: "exchange", label: `hyperliquid_info:${String(body.type)}`, method: "POST", url: `${apiUrl.replace(/\/$/, "")}/info` });
50749
50836
  try {
@@ -50867,7 +50954,6 @@ var init_hyperliquid_account_state_adapter = __esm({
50867
50954
  "Browser account state request aborted."
50868
50955
  ));
50869
50956
  } catch (error48) {
50870
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
50871
50957
  const staleCache = readBrowserAccountStateCache(cacheKey);
50872
50958
  if (!input.forceRefresh && isRateLimitError(error48) && staleCache && Date.now() - staleCache.fetchedAtMs <= staleIf429MaxAgeMs) {
50873
50959
  return withAccountStateEvidence(staleCache.result, "stale_if_429", staleCache.fetchedAtMs, staleIf429MaxAgeMs);
@@ -50927,10 +51013,10 @@ var init_hyperliquid_account_state_adapter = __esm({
50927
51013
  capturedAt: new Date(capturedAtMs).toISOString()
50928
51014
  };
50929
51015
  })() : Promise.resolve(null);
50930
- await Promise.all([userFeesPromise, exactAccountModePromise]);
51016
+ await awaitAccountReads([userFeesPromise, exactAccountModePromise]);
50931
51017
  const metadataByDex = /* @__PURE__ */ new Map();
50932
51018
  if (input.includeEffectiveTakerRate === true) {
50933
- await Promise.all(["", ...dexNames].map(async (dex) => {
51019
+ await awaitAccountReads(["", ...dexNames].map(async (dex) => {
50934
51020
  try {
50935
51021
  metadataByDex.set(dex, await fetchHyperliquidInfoPayload(apiUrl, { type: "meta", ...dex ? { dex } : {} }, input.signal, reservation));
50936
51022
  } catch (error48) {
@@ -50949,7 +51035,7 @@ var init_hyperliquid_account_state_adapter = __esm({
50949
51035
  if (input.includeEffectiveTakerRate === true) {
50950
51036
  requirePromptClearinghouseState(defaultResponse, "default");
50951
51037
  }
50952
- const dexResponses = await Promise.all(
51038
+ const dexResponses = await awaitAccountReads(
50953
51039
  dexNames.map(async (dexName) => {
50954
51040
  const response = await fetchHyperliquidInfoPayload(
50955
51041
  apiUrl,
@@ -50994,7 +51080,6 @@ var init_hyperliquid_account_state_adapter = __esm({
50994
51080
  reservation
50995
51081
  );
50996
51082
  } catch (error48) {
50997
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
50998
51083
  if (input.forceRefresh || input.includeEffectiveTakerRate === true) {
50999
51084
  throw error48;
51000
51085
  }
@@ -51015,7 +51100,6 @@ var init_hyperliquid_account_state_adapter = __esm({
51015
51100
  return { payload, type: typeName };
51016
51101
  } catch (error48) {
51017
51102
  lastError = error48;
51018
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
51019
51103
  if (isRateLimitError(error48)) throw error48;
51020
51104
  if (typeName === "userActiveAssetData" && isUnsupportedUserActiveAssetError(error48)) {
51021
51105
  rememberUnsupportedUserActiveAssetKey(activeAssetCacheKey);
@@ -51024,7 +51108,7 @@ var init_hyperliquid_account_state_adapter = __esm({
51024
51108
  }
51025
51109
  throw lastError instanceof Error ? lastError : new Error("Failed to fetch browser active asset data.");
51026
51110
  })();
51027
- const [perpsResult, spotResult, activeAssetResult, userFeesResult, exactAccountMode] = await Promise.all([
51111
+ const [perpsResult, spotResult, activeAssetResult, userFeesResult, exactAccountMode] = await awaitAccountReads([
51028
51112
  perpsPromise,
51029
51113
  spotPromise,
51030
51114
  activeAssetPromise,
@@ -51050,7 +51134,7 @@ var init_hyperliquid_account_state_adapter = __esm({
51050
51134
  ...parseInfoPositions(perpsResponse).filter((position) => Number(position.size) !== 0).map((position) => position.symbol)
51051
51135
  ].map(normalizeHyperliquidMarketSymbol).filter(Boolean))].sort();
51052
51136
  const feeDexes = [...new Set(feeSymbols.map(getHyperliquidMarketDex))];
51053
- const metadata = await Promise.all(feeDexes.map(async (dex) => {
51137
+ const metadata = await awaitAccountReads(feeDexes.map(async (dex) => {
51054
51138
  try {
51055
51139
  const payload = metadataByDex.get(dex) ?? null;
51056
51140
  return [dex, payload];
@@ -51245,7 +51329,6 @@ var init_hyperliquid_account_state_adapter = __esm({
51245
51329
  providerRowCount = liveResult.providerRowCount;
51246
51330
  fetchedAtMs = liveResult.fetchedAtMs;
51247
51331
  } catch (error48) {
51248
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
51249
51332
  const staleCache = readBrowserUserFillsCache(cacheKey);
51250
51333
  if (isRateLimitError(error48) && !input.forceRefresh) {
51251
51334
  evidenceSource = "stale_if_429";
@@ -83844,6 +83927,7 @@ var init_hyperliquid_client = __esm({
83844
83927
  init_define_VTX_EXO_POLICY();
83845
83928
  init_define_VTX_GROK_POLICY();
83846
83929
  init_define_VTX_PI_MODEL_POLICY();
83930
+ init_runtime_policy_generated();
83847
83931
  init_hyperliquid_public_read();
83848
83932
  init_api2();
83849
83933
  init_abort();
@@ -85514,6 +85598,7 @@ var init_hyperliquid_client = __esm({
85514
85598
  const serializedPayload = JSON.stringify(input.payload);
85515
85599
  const durableMutation = input.durableMutation;
85516
85600
  let exchangeDispatched = false;
85601
+ let recoveryPermit = null;
85517
85602
  let settlementAttempted = false;
85518
85603
  assertFreshPreparedExecutionContext(input.preparedContext);
85519
85604
  const captureTimeoutMs = Number(
@@ -85670,8 +85755,9 @@ var init_hyperliquid_client = __esm({
85670
85755
  }
85671
85756
  }
85672
85757
  try {
85673
- await admitHyperliquidExchangeRequest(apiUrl, input.payload.action, input.signal);
85758
+ recoveryPermit = await admitHyperliquidExchangeRequest(apiUrl, input.payload.action, input.signal);
85674
85759
  assertFreshPreparedExecutionContext(input.preparedContext);
85760
+ assertHyperliquidRecoveryDispatch(apiUrl, recoveryPermit, "exchange");
85675
85761
  } catch (preparedContextError) {
85676
85762
  await settleDurableMutation("rejected");
85677
85763
  if (isAbortLikeError(preparedContextError) || preparedContextError instanceof HyperliquidPublicReadError) throw preparedContextError;
@@ -85699,16 +85785,44 @@ var init_hyperliquid_client = __esm({
85699
85785
  }
85700
85786
  }
85701
85787
  exchangeDispatched = true;
85702
- const response = await fetch(requestUrl, {
85703
- method: "POST",
85704
- headers: {
85705
- "Content-Type": "application/json"
85706
- },
85707
- body: serializedPayload
85708
- });
85709
- responseStatus = response.status;
85710
- await recordHyperliquidExchangeResponse(apiUrl, response);
85711
- const payload = await response.json().catch(() => ({}));
85788
+ const transportController = new AbortController();
85789
+ const exchangeTimeout = runtime_policy_generated_default.exchange.hyperliquid.http.exchange;
85790
+ const transportTimeoutMs = Math.min(
85791
+ (exchangeTimeout.request_timeout_seconds + exchangeTimeout.pool_timeout_seconds) * 1e3,
85792
+ recoveryPermit ? Math.max(0, recoveryPermit.expiresAt - Date.now()) : Infinity
85793
+ );
85794
+ let transportTimer;
85795
+ let response;
85796
+ let payload;
85797
+ try {
85798
+ const transport = (async () => {
85799
+ const received = await fetch(requestUrl, {
85800
+ method: "POST",
85801
+ headers: { "Content-Type": "application/json" },
85802
+ body: serializedPayload,
85803
+ signal: transportController.signal
85804
+ });
85805
+ if (transportController.signal.aborted) throw transportController.signal.reason;
85806
+ responseStatus = received.status;
85807
+ await recordHyperliquidExchangeResponse(apiUrl, received);
85808
+ const body = await received.json().catch(() => ({}));
85809
+ if (transportController.signal.aborted) throw transportController.signal.reason;
85810
+ return { response: received, payload: body };
85811
+ })();
85812
+ const timeout = new Promise((_resolve, reject) => {
85813
+ transportTimer = globalThis.setTimeout(() => {
85814
+ const error48 = new DOMException("Hyperliquid exchange transport timed out.", "TimeoutError");
85815
+ transportController.abort(error48);
85816
+ reject(error48);
85817
+ }, transportTimeoutMs);
85818
+ });
85819
+ ({ response, payload } = await Promise.race([transport, timeout]));
85820
+ } finally {
85821
+ globalThis.clearTimeout(transportTimer);
85822
+ transportController.abort();
85823
+ }
85824
+ await finishHyperliquidRecoveryRequest(recoveryPermit, response.status < 400 ? "success" : "failure").catch(() => void 0);
85825
+ recoveryPermit = null;
85712
85826
  if (!response.ok) {
85713
85827
  await settleDurableMutation("transport_ambiguous");
85714
85828
  throw new RuntimeExecutionError(
@@ -85773,6 +85887,8 @@ var init_hyperliquid_client = __esm({
85773
85887
  );
85774
85888
  }
85775
85889
  throw error48;
85890
+ } finally {
85891
+ await finishHyperliquidRecoveryRequest(recoveryPermit, exchangeDispatched ? "failure" : "cancelled").catch(() => void 0);
85776
85892
  }
85777
85893
  });
85778
85894
  } catch (error48) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.9.55",
3
+ "version": "2026.9.56",
4
4
  "description": "VTX Macro CLI, MCP server, and durable external inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",