@vtxmacro/cli 2026.9.55 → 2026.9.57

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.57",
20
20
  codex_package_name: "@openai/codex",
21
21
  codex_version: "0.155.0",
22
22
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -203,6 +203,8 @@ var runtime_policy_generated_default = {
203
203
  },
204
204
  server_runtime: {
205
205
  prompt_rescue: {
206
+ snapshot_max_age_seconds: 5,
207
+ snapshot_component_max_skew_seconds: 2,
206
208
  client_long_poll_seconds: 20,
207
209
  client_claim_http_timeout_milliseconds: 25e3,
208
210
  client_presence_heartbeat_interval_milliseconds: 3e4,
@@ -296,6 +298,28 @@ var runtime_policy_generated_default = {
296
298
  apiWallets: 20
297
299
  },
298
300
  exchange_action_batch_size: 40
301
+ },
302
+ public_recovery: {
303
+ fallback_initial_seconds: 2,
304
+ fallback_max_seconds: 60,
305
+ permit_lease_seconds: 60,
306
+ recovery_initial_concurrency: 1,
307
+ recovery_max_concurrency: 8,
308
+ recovery_successes_per_step: 4,
309
+ recovery_min_spacing_ms: 100,
310
+ queue_poll_ms: 50,
311
+ recovery_queue_wait_seconds: 10,
312
+ recovery_quiet_seconds: 30
313
+ },
314
+ http: {
315
+ exchange: {
316
+ request_timeout_seconds: 10,
317
+ pool_timeout_seconds: 10
318
+ },
319
+ public_critical: {
320
+ request_timeout_seconds: 10,
321
+ pool_timeout_seconds: 5
322
+ }
299
323
  }
300
324
  }
301
325
  }
@@ -86,6 +86,8 @@ var runtime_policy_generated_default = {
86
86
  },
87
87
  server_runtime: {
88
88
  prompt_rescue: {
89
+ snapshot_max_age_seconds: 5,
90
+ snapshot_component_max_skew_seconds: 2,
89
91
  client_long_poll_seconds: 20,
90
92
  client_claim_http_timeout_milliseconds: 25e3,
91
93
  client_presence_heartbeat_interval_milliseconds: 3e4,
@@ -179,6 +181,28 @@ var runtime_policy_generated_default = {
179
181
  apiWallets: 20
180
182
  },
181
183
  exchange_action_batch_size: 40
184
+ },
185
+ public_recovery: {
186
+ fallback_initial_seconds: 2,
187
+ fallback_max_seconds: 60,
188
+ permit_lease_seconds: 60,
189
+ recovery_initial_concurrency: 1,
190
+ recovery_max_concurrency: 8,
191
+ recovery_successes_per_step: 4,
192
+ recovery_min_spacing_ms: 100,
193
+ queue_poll_ms: 50,
194
+ recovery_queue_wait_seconds: 10,
195
+ recovery_quiet_seconds: 30
196
+ },
197
+ http: {
198
+ exchange: {
199
+ request_timeout_seconds: 10,
200
+ pool_timeout_seconds: 10
201
+ },
202
+ public_critical: {
203
+ request_timeout_seconds: 10,
204
+ pool_timeout_seconds: 5
205
+ }
182
206
  }
183
207
  }
184
208
  }
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.57",
81
81
  codex_package_name: "@openai/codex",
82
82
  codex_version: "0.155.0",
83
83
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -20544,6 +20544,8 @@ var init_runtime_policy_generated = __esm({
20544
20544
  },
20545
20545
  server_runtime: {
20546
20546
  prompt_rescue: {
20547
+ snapshot_max_age_seconds: 5,
20548
+ snapshot_component_max_skew_seconds: 2,
20547
20549
  client_long_poll_seconds: 20,
20548
20550
  client_claim_http_timeout_milliseconds: 25e3,
20549
20551
  client_presence_heartbeat_interval_milliseconds: 3e4,
@@ -20637,6 +20639,28 @@ var init_runtime_policy_generated = __esm({
20637
20639
  apiWallets: 20
20638
20640
  },
20639
20641
  exchange_action_batch_size: 40
20642
+ },
20643
+ public_recovery: {
20644
+ fallback_initial_seconds: 2,
20645
+ fallback_max_seconds: 60,
20646
+ permit_lease_seconds: 60,
20647
+ recovery_initial_concurrency: 1,
20648
+ recovery_max_concurrency: 8,
20649
+ recovery_successes_per_step: 4,
20650
+ recovery_min_spacing_ms: 100,
20651
+ queue_poll_ms: 50,
20652
+ recovery_queue_wait_seconds: 10,
20653
+ recovery_quiet_seconds: 30
20654
+ },
20655
+ http: {
20656
+ exchange: {
20657
+ request_timeout_seconds: 10,
20658
+ pool_timeout_seconds: 10
20659
+ },
20660
+ public_critical: {
20661
+ request_timeout_seconds: 10,
20662
+ pool_timeout_seconds: 5
20663
+ }
20640
20664
  }
20641
20665
  }
20642
20666
  }
@@ -42582,6 +42606,8 @@ var init_runtime_policy_generated2 = __esm({
42582
42606
  },
42583
42607
  server_runtime: {
42584
42608
  prompt_rescue: {
42609
+ snapshot_max_age_seconds: 5,
42610
+ snapshot_component_max_skew_seconds: 2,
42585
42611
  client_long_poll_seconds: 20,
42586
42612
  client_claim_http_timeout_milliseconds: 25e3,
42587
42613
  client_presence_heartbeat_interval_milliseconds: 3e4,
@@ -42675,6 +42701,28 @@ var init_runtime_policy_generated2 = __esm({
42675
42701
  apiWallets: 20
42676
42702
  },
42677
42703
  exchange_action_batch_size: 40
42704
+ },
42705
+ public_recovery: {
42706
+ fallback_initial_seconds: 2,
42707
+ fallback_max_seconds: 60,
42708
+ permit_lease_seconds: 60,
42709
+ recovery_initial_concurrency: 1,
42710
+ recovery_max_concurrency: 8,
42711
+ recovery_successes_per_step: 4,
42712
+ recovery_min_spacing_ms: 100,
42713
+ queue_poll_ms: 50,
42714
+ recovery_queue_wait_seconds: 10,
42715
+ recovery_quiet_seconds: 30
42716
+ },
42717
+ http: {
42718
+ exchange: {
42719
+ request_timeout_seconds: 10,
42720
+ pool_timeout_seconds: 10
42721
+ },
42722
+ public_critical: {
42723
+ request_timeout_seconds: 10,
42724
+ pool_timeout_seconds: 5
42725
+ }
42678
42726
  }
42679
42727
  }
42680
42728
  }
@@ -48616,7 +48664,7 @@ var init_runtime_redaction = __esm({
48616
48664
  });
48617
48665
 
48618
48666
  // 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;
48667
+ 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
48668
  var init_hyperliquid_public_read = __esm({
48621
48669
  "lib/runtime/hyperliquid-public-read.ts"() {
48622
48670
  "use strict";
@@ -48627,6 +48675,7 @@ var init_hyperliquid_public_read = __esm({
48627
48675
  init_runtime_policy_generated();
48628
48676
  policy = runtime_policy_generated_default.exchange.hyperliquid.client_public_read_budget;
48629
48677
  weights = runtime_policy_generated_default.exchange.hyperliquid.read_budget.weights;
48678
+ recoveryPolicy = runtime_policy_generated_default.exchange.hyperliquid.public_recovery;
48630
48679
  budgets = /* @__PURE__ */ new Map();
48631
48680
  unpersistedBudgets = /* @__PURE__ */ new Set();
48632
48681
  cache = /* @__PURE__ */ new Map();
@@ -48684,7 +48733,15 @@ var init_hyperliquid_public_read = __esm({
48684
48733
  withBudgetLock = async (key, signal, work) => {
48685
48734
  checkAbort(signal);
48686
48735
  if (typeof navigator !== "undefined" && navigator.locks?.request) {
48687
- return navigator.locks.request(key, { ...signal ? { signal } : {} }, work);
48736
+ const result2 = await navigator.locks.request(key, { ...signal ? { signal } : {} }, () => {
48737
+ try {
48738
+ return { ok: true, value: work() };
48739
+ } catch (error48) {
48740
+ return { ok: false, error: error48 };
48741
+ }
48742
+ });
48743
+ if (!result2.ok) throw result2.error;
48744
+ return result2.value;
48688
48745
  }
48689
48746
  return work();
48690
48747
  };
@@ -48701,6 +48758,15 @@ var init_hyperliquid_public_read = __esm({
48701
48758
  this.name = "HyperliquidPublicReadError";
48702
48759
  }
48703
48760
  };
48761
+ getHyperliquidPublicReadBudget = (endpoint) => {
48762
+ const budget = loadBudget(`${PREFIX}${endpoint.replace(/\/$/, "")}`);
48763
+ return {
48764
+ usedWeight: budget.reservations.filter((r) => r.at > Date.now() - policy.window_seconds * 1e3).reduce((sum, r) => sum + r.weight, 0),
48765
+ maxWeight: policy.max_weight,
48766
+ windowSeconds: policy.window_seconds,
48767
+ cooldownUntilMs: budget.cooldownUntil
48768
+ };
48769
+ };
48704
48770
  cooldownError = (requestType, until) => new HyperliquidPublicReadError(
48705
48771
  "public_cooldown_active",
48706
48772
  "Hyperliquid public request cooldown is active.",
@@ -48708,48 +48774,129 @@ var init_hyperliquid_public_read = __esm({
48708
48774
  void 0,
48709
48775
  Math.max(0, until - Date.now())
48710
48776
  );
48711
- admit = async (apiUrl, requestType, signal, bundleWeight, bundleId, deadlineMs) => {
48777
+ recoveryWaitError = (requestType) => new HyperliquidPublicReadError(
48778
+ "public_recovery_wait",
48779
+ "Hyperliquid provider recovery could not dispatch before the request deadline.",
48780
+ requestType
48781
+ );
48782
+ fallbackDelayMs = (failures) => Math.min(
48783
+ recoveryPolicy.fallback_max_seconds * 1e3,
48784
+ recoveryPolicy.fallback_initial_seconds * 1e3 * 2 ** Math.min(30, Math.max(0, failures - 1)) * (0.5 + Math.random())
48785
+ );
48786
+ admit = async (apiUrl, requestType, signal, weightOverride, deadlineMs) => {
48712
48787
  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 } : {} });
48788
+ const weight = weightOverride ?? weights[requestType] ?? policy.default_request_weight;
48789
+ const ticket = crypto.randomUUID();
48790
+ const deadline = Math.min(deadlineMs ?? Infinity, Date.now() + recoveryPolicy.recovery_queue_wait_seconds * 1e3);
48791
+ let admitted = false;
48792
+ try {
48793
+ for (; ; ) {
48794
+ checkAbort(signal);
48795
+ if (deadlineMs != null && Date.now() >= deadlineMs) throw recoveryWaitError(requestType);
48796
+ const result2 = await withBudgetLock(key, signal, () => {
48797
+ const now = Date.now();
48798
+ const budget = loadBudget(key);
48799
+ if (budget.cooldownUntil > now) throw cooldownError(requestType, budget.cooldownUntil);
48800
+ const recovery = budget.recovery;
48801
+ let permit = null;
48802
+ if (recovery) {
48803
+ recovery.active = recovery.active.filter((entry) => entry.expiresAt > now);
48804
+ recovery.queue = recovery.queue.filter((entry) => entry.expiresAt > now);
48805
+ if (now >= deadline) throw recoveryWaitError(requestType);
48806
+ if (!recovery.queue.some((entry) => entry.id === ticket)) recovery.queue.push({ id: ticket, expiresAt: deadline });
48807
+ if (recovery.queue[0]?.id !== ticket || recovery.active.length >= recovery.concurrency || recovery.nextAt > now) {
48808
+ saveBudget(key, budget);
48809
+ return { ready: false, permit: null };
48810
+ }
48811
+ recovery.queue.shift();
48812
+ const expiresAt = now + recoveryPolicy.permit_lease_seconds * 1e3;
48813
+ recovery.active.push({ id: ticket, expiresAt });
48814
+ recovery.nextAt = now + recoveryPolicy.recovery_min_spacing_ms;
48815
+ permit = { endpoint: apiUrl, id: ticket, generation: recovery.generation, expiresAt };
48816
+ }
48817
+ budget.reservations = budget.reservations.filter((entry) => entry.at > now - policy.window_seconds * 1e3);
48818
+ budget.reservations.push({ at: now, weight });
48726
48819
  saveBudget(key, budget);
48727
- return { delay: 0, usedWeight: usedWeight2 };
48820
+ return { ready: true, permit };
48821
+ });
48822
+ if (result2.ready) {
48823
+ admitted = true;
48824
+ return result2.permit;
48728
48825
  }
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 };
48826
+ await wait(Math.min(recoveryPolicy.queue_poll_ms, Math.max(1, deadline - Date.now())), signal);
48827
+ }
48828
+ } finally {
48829
+ if (!admitted) await withBudgetLock(key, void 0, () => {
48830
+ const budget = loadBudget(key);
48831
+ if (budget.recovery) {
48832
+ budget.recovery.queue = budget.recovery.queue.filter((entry) => entry.id !== ticket);
48833
+ saveBudget(key, budget);
48732
48834
  }
48733
- throw new Error("Invalid Hyperliquid public read budget.");
48734
48835
  });
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
48836
  }
48741
48837
  };
48742
48838
  cooldown = async (apiUrl, retryAfter) => {
48743
48839
  const key = `${PREFIX}${apiUrl}`;
48744
48840
  await withBudgetLock(key, void 0, () => {
48745
48841
  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
48842
  const budget = loadBudget(key);
48749
- budget.cooldownUntil = Math.max(budget.cooldownUntil, now + policy.cooldown_seconds * 1e3, Number.isFinite(providerUntil) ? providerUntil : 0);
48843
+ if (!budget.recovery && now - (budget.lastFailure ?? 0) >= recoveryPolicy.recovery_quiet_seconds * 1e3) budget.failures = 0;
48844
+ budget.failures = (budget.failures ?? 0) + 1;
48845
+ const trimmed = retryAfter?.trim() ?? "";
48846
+ const seconds = trimmed !== "" ? Number(trimmed) : NaN;
48847
+ const providerUntil = Number.isFinite(seconds) && seconds >= 0 ? now + seconds * 1e3 : trimmed && !Number.isFinite(seconds) ? Date.parse(trimmed) : NaN;
48848
+ const until = Number.isFinite(providerUntil) ? Math.max(now, Number(providerUntil)) : now + fallbackDelayMs(budget.failures);
48849
+ budget.cooldownUntil = Math.max(budget.cooldownUntil, until);
48850
+ budget.lastFailure = now;
48851
+ budget.recovery = {
48852
+ generation: crypto.randomUUID(),
48853
+ concurrency: recoveryPolicy.recovery_initial_concurrency,
48854
+ successes: 0,
48855
+ nextAt: budget.cooldownUntil,
48856
+ queue: budget.recovery?.queue ?? [],
48857
+ active: []
48858
+ };
48750
48859
  saveBudget(key, budget);
48751
48860
  });
48752
48861
  };
48862
+ finishHyperliquidRecoveryRequest = async (permit, outcome) => {
48863
+ if (!permit) return;
48864
+ const key = `${PREFIX}${permit.endpoint}`;
48865
+ await withBudgetLock(key, void 0, () => {
48866
+ const budget = loadBudget(key);
48867
+ const recovery = budget.recovery;
48868
+ if (!recovery || recovery.generation !== permit.generation) return;
48869
+ const now = Date.now();
48870
+ const member = recovery.active.find((entry) => entry.id === permit.id);
48871
+ recovery.active = recovery.active.filter((entry) => entry.id !== permit.id);
48872
+ if (member && member.expiresAt > now) {
48873
+ if (outcome === "success") {
48874
+ recovery.successes += 1;
48875
+ if (recovery.successes >= recoveryPolicy.recovery_successes_per_step) {
48876
+ if (recovery.concurrency >= recoveryPolicy.recovery_max_concurrency) {
48877
+ if (recovery.queue.length === 0 && recovery.active.length === 0) delete budget.recovery;
48878
+ } else {
48879
+ recovery.concurrency = Math.min(recoveryPolicy.recovery_max_concurrency, recovery.concurrency * 2);
48880
+ recovery.successes = 0;
48881
+ }
48882
+ }
48883
+ } else if (outcome === "failure") {
48884
+ recovery.successes = 0;
48885
+ recovery.concurrency = recoveryPolicy.recovery_initial_concurrency;
48886
+ recovery.nextAt = Math.max(recovery.nextAt, now + fallbackDelayMs(budget.failures ?? 1));
48887
+ }
48888
+ }
48889
+ saveBudget(key, budget);
48890
+ });
48891
+ };
48892
+ assertHyperliquidRecoveryDispatch = (endpoint, permit, requestType) => {
48893
+ const budget = loadBudget(`${PREFIX}${endpoint.replace(/\/$/, "")}`);
48894
+ if (budget.cooldownUntil > Date.now()) throw cooldownError(requestType, budget.cooldownUntil);
48895
+ 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())))) {
48896
+ throw recoveryWaitError(requestType);
48897
+ }
48898
+ if (!permit && budget.recovery) throw recoveryWaitError(requestType);
48899
+ };
48753
48900
  cacheTtlMs = (body, options) => {
48754
48901
  const requestType = String(body.type);
48755
48902
  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 +48908,26 @@ var init_hyperliquid_public_read = __esm({
48761
48908
  return ttl > 0 && existing && Date.now() >= existing.fetchedAtMs && horizon - existing.fetchedAtMs < ttl ? existing : void 0;
48762
48909
  };
48763
48910
  reserveHyperliquidPublicReads = async (endpoint, bodies, signal, options = {}) => {
48911
+ checkAbort(signal);
48764
48912
  const apiUrl = endpoint.replace(/\/$/, "");
48765
48913
  const cached2 = /* @__PURE__ */ new Map();
48766
- const remaining = /* @__PURE__ */ new Map();
48767
- let weight = 0;
48768
48914
  for (const body of bodies) {
48769
- const key = JSON.stringify(body);
48770
48915
  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;
48916
+ if (hit) cached2.set(JSON.stringify(body), hit);
48777
48917
  }
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 };
48918
+ return { closed: false, endpoint: apiUrl, cached: cached2, cacheValidUntilMs: options.cacheValidUntilMs };
48782
48919
  };
48783
48920
  releaseHyperliquidPublicReadReservation = async (reservation) => {
48784
48921
  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
48922
  };
48798
48923
  admitHyperliquidExchangeRequest = async (endpoint, action, signal) => {
48799
48924
  const batchLength = Math.max(0, ...[action.orders, action.cancels, action.modifies].filter(Array.isArray).map((value) => value.length));
48800
48925
  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);
48926
+ return admit(endpoint.replace(/\/$/, ""), "exchange", signal, weight);
48802
48927
  };
48803
- recordHyperliquidExchangeResponse = async (endpoint, response) => {
48928
+ recordHyperliquidExchangeResponse = async (endpoint, response, permit) => {
48804
48929
  if (response.status === 429) await cooldown(endpoint.replace(/\/$/, ""), response.headers.get("Retry-After"));
48930
+ await finishHyperliquidRecoveryRequest(permit, response.status < 400 ? "success" : "failure");
48805
48931
  };
48806
48932
  readHyperliquidPublicInfo = async (endpoint, body, options = {}) => {
48807
48933
  const apiUrl = endpoint.replace(/\/$/, "");
@@ -48810,71 +48936,101 @@ var init_hyperliquid_public_read = __esm({
48810
48936
  const ttlMs = cacheTtlMs(body, cacheOptions);
48811
48937
  const key = `${apiUrl}:${JSON.stringify(body)}`;
48812
48938
  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
- }
48939
+ const transportPolicy = runtime_policy_generated_default.exchange.hyperliquid.http.public_critical;
48940
+ const deadline = Math.min(
48941
+ Date.now() + (transportPolicy.request_timeout_seconds + transportPolicy.pool_timeout_seconds) * 1e3,
48942
+ options.deadlineMs ?? Infinity,
48943
+ options.reservation?.cacheValidUntilMs ?? Infinity
48944
+ );
48945
+ const controller = new AbortController();
48946
+ const signal = controller.signal;
48947
+ const callerAborted = () => controller.abort(abortError2(options.signal));
48948
+ options.signal?.addEventListener("abort", callerAborted, { once: true });
48949
+ let timer;
48950
+ const armDeadline = (until) => {
48951
+ clearTimeout(timer);
48952
+ const timeout = () => controller.abort(new DOMException("Hyperliquid public request timed out.", "TimeoutError"));
48953
+ if (until <= Date.now()) timeout();
48954
+ else timer = setTimeout(timeout, until - Date.now());
48955
+ };
48956
+ armDeadline(deadline);
48957
+ let permit = null;
48958
+ let outcome = "cancelled";
48833
48959
  let settled;
48834
- if (ttlMs) pending2.set(key, new Promise((resolve13) => {
48835
- settled = resolve13;
48836
- }));
48960
+ let acquiring = false;
48837
48961
  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);
48962
+ checkAbort(signal);
48963
+ if (ttlMs) {
48964
+ for (; ; ) {
48965
+ const existing = options.reservation?.cached.get(JSON.stringify(body)) ?? eligibleCache(apiUrl, body, cacheOptions);
48966
+ if (existing) return existing;
48967
+ const running = pending2.get(key);
48968
+ if (!running) break;
48969
+ await new Promise((resolve13, reject) => {
48970
+ const aborted2 = () => {
48971
+ signal.removeEventListener("abort", aborted2);
48972
+ reject(abortError2(signal));
48973
+ };
48974
+ signal.addEventListener("abort", aborted2, { once: true });
48975
+ running.then(() => {
48976
+ signal.removeEventListener("abort", aborted2);
48977
+ resolve13();
48978
+ });
48979
+ });
48980
+ checkAbort(signal);
48981
+ }
48982
+ pending2.set(key, new Promise((resolve13) => {
48983
+ settled = resolve13;
48984
+ }));
48985
+ }
48986
+ if (options.reservation?.closed) throw new DOMException("Hyperliquid account read was cancelled.", "AbortError");
48987
+ acquiring = true;
48988
+ permit = await admit(apiUrl, requestType, signal, void 0, deadline);
48989
+ acquiring = false;
48990
+ armDeadline(Math.min(deadline, permit?.expiresAt ?? Infinity));
48991
+ checkAbort(signal);
48992
+ if (options.reservation?.closed) throw new DOMException("Hyperliquid account read was cancelled.", "AbortError");
48993
+ assertHyperliquidRecoveryDispatch(apiUrl, permit, requestType);
48994
+ outcome = "failure";
48995
+ let transportAborted;
48996
+ let response;
48997
+ let payload;
48998
+ try {
48999
+ const aborted2 = new Promise((_resolve, reject) => {
49000
+ transportAborted = () => reject(abortError2(signal));
49001
+ signal.addEventListener("abort", transportAborted, { once: true });
48852
49002
  });
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
- });
49003
+ const transport = (async () => {
49004
+ const received = await fetch(`${apiUrl}/info`, {
49005
+ method: "POST",
49006
+ headers: { "Content-Type": "application/json" },
49007
+ body: JSON.stringify(body),
49008
+ signal
49009
+ });
49010
+ checkAbort(signal);
49011
+ if (received.status === 429) {
49012
+ await cooldown(apiUrl, received.headers.get("Retry-After"));
49013
+ throw new HyperliquidPublicReadError("public_rate_limited", "Hyperliquid public API returned HTTP 429.", requestType, 429);
49014
+ }
49015
+ const receivedPayload = await received.json().catch((error48) => {
49016
+ if (error48 instanceof SyntaxError) return {};
49017
+ throw error48;
49018
+ });
49019
+ checkAbort(signal);
49020
+ return { response: received, payload: receivedPayload };
49021
+ })();
49022
+ ({ response, payload } = await Promise.race([transport, aborted2]));
49023
+ } finally {
49024
+ if (transportAborted) signal.removeEventListener("abort", transportAborted);
49025
+ }
48870
49026
  if (!response.ok) {
48871
49027
  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
49028
  throw Object.assign(new Error(`Hyperliquid info request failed (${response.status}) request_type=${requestType}.${detail ? ` ${detail}` : ""}`), {
48874
49029
  status: response.status,
48875
49030
  requestType
48876
49031
  });
48877
49032
  }
49033
+ outcome = "success";
48878
49034
  const result2 = { payload, fetchedAtMs: Date.now() };
48879
49035
  if (ttlMs) {
48880
49036
  cache.set(key, result2);
@@ -48882,14 +49038,26 @@ var init_hyperliquid_public_read = __esm({
48882
49038
  }
48883
49039
  return result2;
48884
49040
  } catch (error48) {
48885
- if (options.signal?.aborted || error48 instanceof HyperliquidPublicReadError) throw error48;
49041
+ if (options.signal?.aborted) {
49042
+ outcome = "cancelled";
49043
+ throw abortError2(options.signal);
49044
+ }
49045
+ if (acquiring && signal.aborted && signal.reason?.name === "TimeoutError") throw recoveryWaitError(requestType);
49046
+ if (error48 instanceof HyperliquidPublicReadError) throw error48;
48886
49047
  if (error48 instanceof TypeError) throw new HyperliquidPublicReadError("public_network_error", "Hyperliquid public request network failure.", requestType);
48887
49048
  if (error48 instanceof Error && (error48.name === "TimeoutError" || error48.name === "AbortError")) throw new HyperliquidPublicReadError("public_timeout", "Hyperliquid public request timed out.", requestType);
48888
49049
  throw error48;
48889
49050
  } finally {
48890
- if (ttlMs) {
48891
- pending2.delete(key);
48892
- settled?.();
49051
+ clearTimeout(timer);
49052
+ options.signal?.removeEventListener("abort", callerAborted);
49053
+ controller.abort();
49054
+ try {
49055
+ await finishHyperliquidRecoveryRequest(permit, outcome);
49056
+ } finally {
49057
+ if (settled) {
49058
+ pending2.delete(key);
49059
+ settled();
49060
+ }
48893
49061
  }
48894
49062
  }
48895
49063
  };
@@ -49987,7 +50155,7 @@ var init_network_debug = __esm({
49987
50155
  });
49988
50156
 
49989
50157
  // 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;
50158
+ 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, fetchHyperliquidInfoRead, fetchHyperliquidInfoPayload, requireHyperliquidApiUrl, requireWalletAddress, parseInfoPositionSnapshot, fetchBrowserAccountState, fetchBrowserAccountStateUncached, fetchBrowserUserFills, fetchBrowserPositionSnapshot;
49991
50159
  var init_hyperliquid_account_state_adapter = __esm({
49992
50160
  "lib/runtime/hyperliquid-account-state-adapter.ts"() {
49993
50161
  "use strict";
@@ -50003,20 +50171,17 @@ var init_hyperliquid_account_state_adapter = __esm({
50003
50171
  init_hyperliquid_fee_contract();
50004
50172
  init_hyperliquid_market_symbol();
50005
50173
  init_network_debug();
50174
+ init_runtime_policy_generated();
50006
50175
  unsupportedUserActiveAssetCache = /* @__PURE__ */ new Set();
50007
50176
  MAX_UNSUPPORTED_USER_ACTIVE_ASSET_CACHE_KEYS = 512;
50008
50177
  UNSUPPORTED_USER_ACTIVE_ASSET_STORAGE_KEY = "vtx:hyperliquid:unsupported-user-active-asset:v1";
50009
50178
  USER_FILLS_CACHE_STORAGE_PREFIX = "vtx:hyperliquid:user-fills:v1:";
50010
50179
  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
50180
  browserUserFillsCache = /* @__PURE__ */ new Map();
50015
50181
  browserUserFillsInFlight = /* @__PURE__ */ new Map();
50016
50182
  HYPERLIQUID_USER_FILLS_PROVIDER_PAGE_CAP = 2e3;
50017
50183
  browserAccountStateCache = /* @__PURE__ */ new Map();
50018
50184
  browserAccountStateInFlight = /* @__PURE__ */ new Map();
50019
- browserInfoRateLimitCooldowns = /* @__PURE__ */ new Map();
50020
50185
  UNSUPPORTED_USER_ACTIVE_ASSET_DETAIL_PATTERNS = [
50021
50186
  /unsupported/i,
50022
50187
  /not\s+supported/i,
@@ -50134,93 +50299,7 @@ var init_hyperliquid_account_state_adapter = __esm({
50134
50299
  buildAccountStateStorageKey = (cacheKey) => {
50135
50300
  return `${ACCOUNT_STATE_CACHE_STORAGE_PREFIX}${cacheKey}`;
50136
50301
  };
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
- };
50302
+ hasActiveInfoRateLimitCooldown = (apiUrl, _requestTypes, nowMs = Date.now()) => getHyperliquidPublicReadBudget(apiUrl).cooldownUntilMs > nowMs;
50224
50303
  requireNonNegativeConfigSeconds = (value, path) => {
50225
50304
  const numeric2 = Number(value);
50226
50305
  if (!Number.isFinite(numeric2) || numeric2 < 0) {
@@ -50744,17 +50823,31 @@ var init_hyperliquid_account_state_adapter = __esm({
50744
50823
  }
50745
50824
  return true;
50746
50825
  };
50747
- fetchHyperliquidInfoPayload = async (apiUrl, body, signal, reservation) => {
50826
+ awaitAccountReads = async (reads) => {
50827
+ try {
50828
+ return await Promise.all(reads);
50829
+ } catch (error48) {
50830
+ if (error48 instanceof HyperliquidPublicReadError && error48.code === "public_cooldown_active") {
50831
+ const settled = await Promise.allSettled(reads);
50832
+ for (const result2 of settled) {
50833
+ if (result2.status === "rejected" && result2.reason instanceof HyperliquidPublicReadError && result2.reason.code === "public_rate_limited") throw result2.reason;
50834
+ }
50835
+ }
50836
+ throw error48;
50837
+ }
50838
+ };
50839
+ fetchHyperliquidInfoRead = async (apiUrl, body, signal, reservation) => {
50748
50840
  const trace = startRuntimeNetworkDebugTrace({ kind: "exchange", label: `hyperliquid_info:${String(body.type)}`, method: "POST", url: `${apiUrl.replace(/\/$/, "")}/info` });
50749
50841
  try {
50750
50842
  const result2 = await readHyperliquidPublicInfo(apiUrl, body, { signal, reservation });
50751
50843
  trace.completeSuccess(200);
50752
- return result2.payload;
50844
+ return result2;
50753
50845
  } catch (error48) {
50754
50846
  trace.completeError(error48, error48?.status ?? null);
50755
50847
  throw error48;
50756
50848
  }
50757
50849
  };
50850
+ fetchHyperliquidInfoPayload = async (apiUrl, body, signal, reservation) => (await fetchHyperliquidInfoRead(apiUrl, body, signal, reservation)).payload;
50758
50851
  requireHyperliquidApiUrl = (config2) => {
50759
50852
  const apiUrl = String(config2.client_runtime_exchange_endpoints?.hyperliquid_api_url || "").trim();
50760
50853
  if (!apiUrl) {
@@ -50867,7 +50960,6 @@ var init_hyperliquid_account_state_adapter = __esm({
50867
50960
  "Browser account state request aborted."
50868
50961
  ));
50869
50962
  } catch (error48) {
50870
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
50871
50963
  const staleCache = readBrowserAccountStateCache(cacheKey);
50872
50964
  if (!input.forceRefresh && isRateLimitError(error48) && staleCache && Date.now() - staleCache.fetchedAtMs <= staleIf429MaxAgeMs) {
50873
50965
  return withAccountStateEvidence(staleCache.result, "stale_if_429", staleCache.fetchedAtMs, staleIf429MaxAgeMs);
@@ -50927,10 +51019,10 @@ var init_hyperliquid_account_state_adapter = __esm({
50927
51019
  capturedAt: new Date(capturedAtMs).toISOString()
50928
51020
  };
50929
51021
  })() : Promise.resolve(null);
50930
- await Promise.all([userFeesPromise, exactAccountModePromise]);
51022
+ await awaitAccountReads([userFeesPromise, exactAccountModePromise]);
50931
51023
  const metadataByDex = /* @__PURE__ */ new Map();
50932
51024
  if (input.includeEffectiveTakerRate === true) {
50933
- await Promise.all(["", ...dexNames].map(async (dex) => {
51025
+ await awaitAccountReads(["", ...dexNames].map(async (dex) => {
50934
51026
  try {
50935
51027
  metadataByDex.set(dex, await fetchHyperliquidInfoPayload(apiUrl, { type: "meta", ...dex ? { dex } : {} }, input.signal, reservation));
50936
51028
  } catch (error48) {
@@ -50939,68 +51031,56 @@ var init_hyperliquid_account_state_adapter = __esm({
50939
51031
  }
50940
51032
  }));
50941
51033
  }
50942
- const perpsPromise = (async () => {
50943
- const defaultResponse = await fetchHyperliquidInfoPayload(
50944
- apiUrl,
50945
- { type: "clearinghouseState", user: walletAddress },
50946
- input.signal,
50947
- reservation
50948
- );
50949
- if (input.includeEffectiveTakerRate === true) {
50950
- requirePromptClearinghouseState(defaultResponse, "default");
50951
- }
50952
- const dexResponses = await Promise.all(
50953
- dexNames.map(async (dexName) => {
50954
- const response = await fetchHyperliquidInfoPayload(
50955
- apiUrl,
50956
- { type: "clearinghouseState", user: walletAddress, dex: dexName },
50957
- input.signal,
50958
- reservation
50959
- );
51034
+ const dynamicPromise = (async () => {
51035
+ const perpsDexes = ["", ...dexNames];
51036
+ const components = [
51037
+ ...perpsDexes.map((dex) => ({ dex, body: { type: "clearinghouseState", user: walletAddress, ...dex ? { dex } : {} } })),
51038
+ { body: { type: "spotClearinghouseState", user: walletAddress } }
51039
+ ];
51040
+ const capture = async (component) => {
51041
+ try {
51042
+ const read = await fetchHyperliquidInfoRead(apiUrl, component.body, input.signal, reservation);
50960
51043
  if (input.includeEffectiveTakerRate === true) {
50961
- requirePromptClearinghouseState(response, dexName);
51044
+ if (component.dex == null) requirePromptSpotState(read.payload);
51045
+ else requirePromptClearinghouseState(read.payload, component.dex || "default");
50962
51046
  }
50963
- return {
50964
- dex: dexName,
50965
- payload: prefixHip3PositionCoins(response, dexName),
50966
- rawPayload: response
50967
- };
50968
- })
50969
- );
50970
- const selectedPayload = selectedDex ? dexResponses.find((entry) => entry.dex === selectedDex)?.payload : defaultResponse;
50971
- if (!selectedPayload) {
50972
- throw new Error(`Missing selected Hyperliquid clearinghouse state for DEX ${selectedDex}.`);
50973
- }
50974
- return {
50975
- payload: aggregateClearinghouseResponses([
50976
- defaultResponse,
50977
- ...dexResponses.map((entry) => entry.payload)
50978
- ]),
50979
- selectedPayload,
50980
- dexPayloads: [
50981
- { dex: "", payload: defaultResponse },
50982
- ...dexResponses.map(({ dex, rawPayload }) => ({ dex, payload: rawPayload }))
50983
- ],
50984
- capturedAt: (/* @__PURE__ */ new Date()).toISOString()
51047
+ component.read = read;
51048
+ } catch (error48) {
51049
+ if (component.dex != null || input.forceRefresh || input.includeEffectiveTakerRate === true) throw error48;
51050
+ component.read = { payload: {}, fetchedAtMs: Date.now() };
51051
+ }
50985
51052
  };
50986
- })();
50987
- const spotPromise = (async () => {
50988
- let payload;
50989
- try {
50990
- payload = await fetchHyperliquidInfoPayload(
50991
- apiUrl,
50992
- { type: "spotClearinghouseState", user: walletAddress },
50993
- input.signal,
50994
- reservation
50995
- );
50996
- } catch (error48) {
50997
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
50998
- if (input.forceRefresh || input.includeEffectiveTakerRate === true) {
50999
- throw error48;
51053
+ await awaitAccountReads(components.map(capture));
51054
+ if (input.publicReadDeadlineMs != null && input.includeEffectiveTakerRate === true && input.includeExactAccountMode === true) {
51055
+ const freshness = runtime_policy_generated_default.trading.engine.server_runtime.prompt_rescue;
51056
+ const horizonMs = Math.min(freshness.snapshot_max_age_seconds, freshness.snapshot_component_max_skew_seconds) * 1e3;
51057
+ for (; ; ) {
51058
+ const now = Date.now();
51059
+ const stale = components.filter(({ read }) => now - read.fetchedAtMs > horizonMs);
51060
+ if (!stale.length) break;
51061
+ if (now >= input.publicReadDeadlineMs) {
51062
+ throw new HyperliquidPublicReadError("public_timeout", "Hyperliquid public request timed out.", "clearinghouseState");
51063
+ }
51064
+ await awaitAccountReads(stale.map(capture));
51000
51065
  }
51001
- payload = {};
51002
51066
  }
51003
- return { payload, capturedAt: (/* @__PURE__ */ new Date()).toISOString() };
51067
+ const perpsReads = components.slice(0, perpsDexes.length).map((component) => ({
51068
+ dex: component.dex,
51069
+ payload: component.read.payload,
51070
+ fetchedAtMs: component.read.fetchedAtMs
51071
+ }));
51072
+ const selectedRead = perpsReads.find(({ dex }) => dex === selectedDex);
51073
+ if (!selectedRead) throw new Error(`Missing selected Hyperliquid clearinghouse state for DEX ${selectedDex}.`);
51074
+ const spotRead = components[perpsDexes.length].read;
51075
+ return {
51076
+ perpsResult: {
51077
+ payload: aggregateClearinghouseResponses(perpsReads.map(({ dex, payload }) => dex ? prefixHip3PositionCoins(payload, dex) : payload)),
51078
+ selectedPayload: selectedDex ? prefixHip3PositionCoins(selectedRead.payload, selectedDex) : selectedRead.payload,
51079
+ dexPayloads: perpsReads.map(({ dex, payload }) => ({ dex, payload })),
51080
+ capturedAt: new Date(Math.min(...perpsReads.map(({ fetchedAtMs }) => fetchedAtMs))).toISOString()
51081
+ },
51082
+ spotResult: { payload: spotRead.payload, capturedAt: new Date(spotRead.fetchedAtMs).toISOString() }
51083
+ };
51004
51084
  })();
51005
51085
  const activeAssetPromise = input.includeActiveAssetData === false ? Promise.resolve(null) : (async () => {
51006
51086
  let lastError = null;
@@ -51015,7 +51095,6 @@ var init_hyperliquid_account_state_adapter = __esm({
51015
51095
  return { payload, type: typeName };
51016
51096
  } catch (error48) {
51017
51097
  lastError = error48;
51018
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
51019
51098
  if (isRateLimitError(error48)) throw error48;
51020
51099
  if (typeName === "userActiveAssetData" && isUnsupportedUserActiveAssetError(error48)) {
51021
51100
  rememberUnsupportedUserActiveAssetKey(activeAssetCacheKey);
@@ -51024,9 +51103,8 @@ var init_hyperliquid_account_state_adapter = __esm({
51024
51103
  }
51025
51104
  throw lastError instanceof Error ? lastError : new Error("Failed to fetch browser active asset data.");
51026
51105
  })();
51027
- const [perpsResult, spotResult, activeAssetResult, userFeesResult, exactAccountMode] = await Promise.all([
51028
- perpsPromise,
51029
- spotPromise,
51106
+ const [{ perpsResult, spotResult }, activeAssetResult, userFeesResult, exactAccountMode] = await awaitAccountReads([
51107
+ dynamicPromise,
51030
51108
  activeAssetPromise,
51031
51109
  userFeesPromise,
51032
51110
  exactAccountModePromise
@@ -51050,7 +51128,7 @@ var init_hyperliquid_account_state_adapter = __esm({
51050
51128
  ...parseInfoPositions(perpsResponse).filter((position) => Number(position.size) !== 0).map((position) => position.symbol)
51051
51129
  ].map(normalizeHyperliquidMarketSymbol).filter(Boolean))].sort();
51052
51130
  const feeDexes = [...new Set(feeSymbols.map(getHyperliquidMarketDex))];
51053
- const metadata = await Promise.all(feeDexes.map(async (dex) => {
51131
+ const metadata = await awaitAccountReads(feeDexes.map(async (dex) => {
51054
51132
  try {
51055
51133
  const payload = metadataByDex.get(dex) ?? null;
51056
51134
  return [dex, payload];
@@ -51151,7 +51229,7 @@ var init_hyperliquid_account_state_adapter = __esm({
51151
51229
  ...effectiveTakerRates == null ? {} : { effectiveTakerRates },
51152
51230
  ...exactAccountMode == null || userFeesResult == null ? {} : {
51153
51231
  promptRescueEvidence: {
51154
- capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
51232
+ capturedAt: new Date(Math.min(Date.parse(perpsResult.capturedAt), Date.parse(spotResult.capturedAt))).toISOString(),
51155
51233
  perpsCapturedAt: perpsResult.capturedAt,
51156
51234
  spotCapturedAt: spotResult.capturedAt,
51157
51235
  feesCapturedAt: userFeesResult.capturedAt,
@@ -51245,7 +51323,6 @@ var init_hyperliquid_account_state_adapter = __esm({
51245
51323
  providerRowCount = liveResult.providerRowCount;
51246
51324
  fetchedAtMs = liveResult.fetchedAtMs;
51247
51325
  } catch (error48) {
51248
- recordInfoRateLimitCooldownFromError(apiUrl, error48, staleIf429MaxAgeMs);
51249
51326
  const staleCache = readBrowserUserFillsCache(cacheKey);
51250
51327
  if (isRateLimitError(error48) && !input.forceRefresh) {
51251
51328
  evidenceSource = "stale_if_429";
@@ -83844,6 +83921,7 @@ var init_hyperliquid_client = __esm({
83844
83921
  init_define_VTX_EXO_POLICY();
83845
83922
  init_define_VTX_GROK_POLICY();
83846
83923
  init_define_VTX_PI_MODEL_POLICY();
83924
+ init_runtime_policy_generated();
83847
83925
  init_hyperliquid_public_read();
83848
83926
  init_api2();
83849
83927
  init_abort();
@@ -85514,6 +85592,7 @@ var init_hyperliquid_client = __esm({
85514
85592
  const serializedPayload = JSON.stringify(input.payload);
85515
85593
  const durableMutation = input.durableMutation;
85516
85594
  let exchangeDispatched = false;
85595
+ let recoveryPermit = null;
85517
85596
  let settlementAttempted = false;
85518
85597
  assertFreshPreparedExecutionContext(input.preparedContext);
85519
85598
  const captureTimeoutMs = Number(
@@ -85670,8 +85749,9 @@ var init_hyperliquid_client = __esm({
85670
85749
  }
85671
85750
  }
85672
85751
  try {
85673
- await admitHyperliquidExchangeRequest(apiUrl, input.payload.action, input.signal);
85752
+ recoveryPermit = await admitHyperliquidExchangeRequest(apiUrl, input.payload.action, input.signal);
85674
85753
  assertFreshPreparedExecutionContext(input.preparedContext);
85754
+ assertHyperliquidRecoveryDispatch(apiUrl, recoveryPermit, "exchange");
85675
85755
  } catch (preparedContextError) {
85676
85756
  await settleDurableMutation("rejected");
85677
85757
  if (isAbortLikeError(preparedContextError) || preparedContextError instanceof HyperliquidPublicReadError) throw preparedContextError;
@@ -85699,16 +85779,44 @@ var init_hyperliquid_client = __esm({
85699
85779
  }
85700
85780
  }
85701
85781
  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(() => ({}));
85782
+ const transportController = new AbortController();
85783
+ const exchangeTimeout = runtime_policy_generated_default.exchange.hyperliquid.http.exchange;
85784
+ const transportTimeoutMs = Math.min(
85785
+ (exchangeTimeout.request_timeout_seconds + exchangeTimeout.pool_timeout_seconds) * 1e3,
85786
+ recoveryPermit ? Math.max(0, recoveryPermit.expiresAt - Date.now()) : Infinity
85787
+ );
85788
+ let transportTimer;
85789
+ let response;
85790
+ let payload;
85791
+ try {
85792
+ const transport = (async () => {
85793
+ const received = await fetch(requestUrl, {
85794
+ method: "POST",
85795
+ headers: { "Content-Type": "application/json" },
85796
+ body: serializedPayload,
85797
+ signal: transportController.signal
85798
+ });
85799
+ if (transportController.signal.aborted) throw transportController.signal.reason;
85800
+ responseStatus = received.status;
85801
+ await recordHyperliquidExchangeResponse(apiUrl, received);
85802
+ const body = await received.json().catch(() => ({}));
85803
+ if (transportController.signal.aborted) throw transportController.signal.reason;
85804
+ return { response: received, payload: body };
85805
+ })();
85806
+ const timeout = new Promise((_resolve, reject) => {
85807
+ transportTimer = globalThis.setTimeout(() => {
85808
+ const error48 = new DOMException("Hyperliquid exchange transport timed out.", "TimeoutError");
85809
+ transportController.abort(error48);
85810
+ reject(error48);
85811
+ }, transportTimeoutMs);
85812
+ });
85813
+ ({ response, payload } = await Promise.race([transport, timeout]));
85814
+ } finally {
85815
+ globalThis.clearTimeout(transportTimer);
85816
+ transportController.abort();
85817
+ }
85818
+ await finishHyperliquidRecoveryRequest(recoveryPermit, response.status < 400 ? "success" : "failure").catch(() => void 0);
85819
+ recoveryPermit = null;
85712
85820
  if (!response.ok) {
85713
85821
  await settleDurableMutation("transport_ambiguous");
85714
85822
  throw new RuntimeExecutionError(
@@ -85773,6 +85881,8 @@ var init_hyperliquid_client = __esm({
85773
85881
  );
85774
85882
  }
85775
85883
  throw error48;
85884
+ } finally {
85885
+ await finishHyperliquidRecoveryRequest(recoveryPermit, exchangeDispatched ? "failure" : "cancelled").catch(() => void 0);
85776
85886
  }
85777
85887
  });
85778
85888
  } 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.57",
4
4
  "description": "VTX Macro CLI, MCP server, and durable external inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",