@vtxmacro/cli 2026.10.6 → 2026.10.8

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.
package/bin/vtx.js CHANGED
@@ -113,7 +113,7 @@ var init_routing = __esm({
113
113
  var agent_cli_release_default;
114
114
  var init_agent_cli_release = __esm({
115
115
  "agent-cli-release.json"() {
116
- agent_cli_release_default = { package_name: "@vtxmacro/cli", package_version: "2026.10.6", codex_package_name: "@openai/codex", codex_version: "0.155.0", copilot_sdk_package_name: "@github/copilot-sdk", copilot_sdk_version: "1.0.0-beta.8", copilot_cli_package_name: "@github/copilot", copilot_cli_version: "1.0.80", deepseek_harness_version: "0.1.1-rc.2", deepseek_harness_packages: ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-agent-loop", "@deepseek-ai/dsh-agent-spine-demo", "@deepseek-ai/dsh-anonymous-user-id", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-attachment", "@deepseek-ai/dsh-brand", "@deepseek-ai/dsh-code-runtime", "@deepseek-ai/dsh-credentials", "@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-goal", "@deepseek-ai/dsh-goal-round-driver", "@deepseek-ai/dsh-home-paths", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-jobs", "@deepseek-ai/dsh-jobs-local", "@deepseek-ai/dsh-launch-environment", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-deepseek", "@deepseek-ai/dsh-llm-retry", "@deepseek-ai/dsh-output-retention", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-scope", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-session-checkpoint-policy", "@deepseek-ai/dsh-session-persistence", "@deepseek-ai/dsh-session-persistence-jsonl", "@deepseek-ai/dsh-session-projection", "@deepseek-ai/dsh-session-title", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-shell", "@deepseek-ai/dsh-shell-env", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-timeout", "@deepseek-ai/dsh-tool-bash", "@deepseek-ai/dsh-tool-goal", "@deepseek-ai/dsh-tool-jobs", "@deepseek-ai/dsh-tool-skill", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-user-approval"], deepseek_cordis_package_name: "@deepseek-ai/cordis", deepseek_cordis_version: "4.0.1", deepseek_support_packages: { "@deepseek-ai/cordis-plugin-timer": "1.1.3" }, platforms: { "linux-x64": { package_name: "@openai/codex-linux-x64", target: "x86_64-unknown-linux-musl", binary_name: "codex", sha256: "660e159a49e823ac8e5986cb238f73158ce4b957d40d9292f8de90862644b501" }, "win32-x64": { package_name: "@openai/codex-win32-x64", target: "x86_64-pc-windows-msvc", binary_name: "codex.exe", sha256: "e4c11374bd9de8ad5c3b7617fd4654bb7839901edb0863f9930666863c7a021b" } }, pi_coding_agent_package_name: "@earendil-works/pi-coding-agent", pi_ai_package_name: "@earendil-works/pi-ai", pi_version: "0.85.1", pi_support_packages: ["@earendil-works/pi-agent-core", "@earendil-works/pi-telemetry", "@earendil-works/pi-tui", "@earendil-works/chord"], grok_version: "1.0.30", grok_platforms: { "linux-x64": { package_name: "@xai-official/grok-linux-x64", binary_name: "grok", sha256: "504dd6546ab991b75d36698242875ce461489cd1f8cd84285873cb55bd5c7d54" }, "win32-x64": { package_name: "@xai-official/grok-win32-x64", binary_name: "grok.exe", sha256: "48736d1a4342a801ea26d0ecde4c516b653f791399065adaa55973f83abd8867" } } };
116
+ agent_cli_release_default = { package_name: "@vtxmacro/cli", package_version: "2026.10.8", codex_package_name: "@openai/codex", codex_version: "0.155.0", copilot_sdk_package_name: "@github/copilot-sdk", copilot_sdk_version: "1.0.0-beta.8", copilot_cli_package_name: "@github/copilot", copilot_cli_version: "1.0.80", deepseek_harness_version: "0.1.1-rc.2", deepseek_harness_packages: ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-agent-loop", "@deepseek-ai/dsh-agent-spine-demo", "@deepseek-ai/dsh-anonymous-user-id", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-attachment", "@deepseek-ai/dsh-brand", "@deepseek-ai/dsh-code-runtime", "@deepseek-ai/dsh-credentials", "@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-goal", "@deepseek-ai/dsh-goal-round-driver", "@deepseek-ai/dsh-home-paths", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-jobs", "@deepseek-ai/dsh-jobs-local", "@deepseek-ai/dsh-launch-environment", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-deepseek", "@deepseek-ai/dsh-llm-retry", "@deepseek-ai/dsh-output-retention", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-scope", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-session-checkpoint-policy", "@deepseek-ai/dsh-session-persistence", "@deepseek-ai/dsh-session-persistence-jsonl", "@deepseek-ai/dsh-session-projection", "@deepseek-ai/dsh-session-title", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-shell", "@deepseek-ai/dsh-shell-env", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-timeout", "@deepseek-ai/dsh-tool-bash", "@deepseek-ai/dsh-tool-goal", "@deepseek-ai/dsh-tool-jobs", "@deepseek-ai/dsh-tool-skill", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-user-approval"], deepseek_cordis_package_name: "@deepseek-ai/cordis", deepseek_cordis_version: "4.0.1", deepseek_support_packages: { "@deepseek-ai/cordis-plugin-timer": "1.1.3" }, platforms: { "linux-x64": { package_name: "@openai/codex-linux-x64", target: "x86_64-unknown-linux-musl", binary_name: "codex", sha256: "660e159a49e823ac8e5986cb238f73158ce4b957d40d9292f8de90862644b501" }, "win32-x64": { package_name: "@openai/codex-win32-x64", target: "x86_64-pc-windows-msvc", binary_name: "codex.exe", sha256: "e4c11374bd9de8ad5c3b7617fd4654bb7839901edb0863f9930666863c7a021b" } }, pi_coding_agent_package_name: "@earendil-works/pi-coding-agent", pi_ai_package_name: "@earendil-works/pi-ai", pi_version: "0.85.1", pi_support_packages: ["@earendil-works/pi-agent-core", "@earendil-works/pi-telemetry", "@earendil-works/pi-tui", "@earendil-works/chord"], grok_version: "1.0.30", grok_platforms: { "linux-x64": { package_name: "@xai-official/grok-linux-x64", binary_name: "grok", sha256: "504dd6546ab991b75d36698242875ce461489cd1f8cd84285873cb55bd5c7d54" }, "win32-x64": { package_name: "@xai-official/grok-win32-x64", binary_name: "grok.exe", sha256: "48736d1a4342a801ea26d0ecde4c516b653f791399065adaa55973f83abd8867" } } };
117
117
  }
118
118
  });
119
119
 
@@ -213,6 +213,9 @@ var init_runtime_policy_generated = __esm({
213
213
  evidence_max_items: 32,
214
214
  evidence_shared_read_seconds: 10
215
215
  },
216
+ protective_plan_set_lock: {
217
+ wait_seconds: 120
218
+ },
216
219
  history_evidence: {
217
220
  device_rounds_per_beat: 6,
218
221
  device_retry_seconds: 60
@@ -1623,7 +1626,7 @@ async function writeAtomicInferencePrivateFile(path, contents, dependencies = {}
1623
1626
  throw error48;
1624
1627
  }
1625
1628
  }
1626
- async function readInferencePrivateFile(path, label, dependencies = {}) {
1629
+ async function readInferencePrivateFile(path, label2, dependencies = {}) {
1627
1630
  const platform = dependencies.platform ?? process.platform;
1628
1631
  const runner = dependencies.runner ?? runWindowsPrivateAcl;
1629
1632
  const defaultRoot = dependencies.defaultRoot ?? resolve(homedir2(), ".vtx", "inference-host");
@@ -1635,7 +1638,7 @@ async function readInferencePrivateFile(path, label, dependencies = {}) {
1635
1638
  throw error48;
1636
1639
  }
1637
1640
  if (before.isSymbolicLink() || !before.isFile()) {
1638
- throw new Error(`${label} is unsafe.`);
1641
+ throw new Error(`${label2} is unsafe.`);
1639
1642
  }
1640
1643
  await secureExistingWindowsPath(
1641
1644
  dirname2(path),
@@ -1649,21 +1652,21 @@ async function readInferencePrivateFile(path, label, dependencies = {}) {
1649
1652
  try {
1650
1653
  const opened = await handle.stat();
1651
1654
  if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size > MAX_INFERENCE_PRIVATE_FILE_BYTES || typeof process.getuid === "function" && opened.uid !== process.getuid()) {
1652
- throw new Error(`${label} changed while it was opened.`);
1655
+ throw new Error(`${label2} changed while it was opened.`);
1653
1656
  }
1654
1657
  if (platform !== "win32" && (opened.mode & 63) !== 0) {
1655
- throw new Error(`${label} must have mode 0600.`);
1658
+ throw new Error(`${label2} must have mode 0600.`);
1656
1659
  }
1657
1660
  return await handle.readFile("utf8");
1658
1661
  } finally {
1659
1662
  await handle.close();
1660
1663
  }
1661
1664
  }
1662
- async function clearInferencePrivateFile(path, label) {
1665
+ async function clearInferencePrivateFile(path, label2) {
1663
1666
  try {
1664
1667
  const metadata = await lstat(path);
1665
1668
  if (metadata.isSymbolicLink() || !metadata.isFile()) {
1666
- throw new Error(`${label} is unsafe.`);
1669
+ throw new Error(`${label2} is unsafe.`);
1667
1670
  }
1668
1671
  await secureExistingWindowsPath(dirname2(path), "directory");
1669
1672
  await secureExistingWindowsPath(path, "file");
@@ -1785,7 +1788,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
1785
1788
  }
1786
1789
  };
1787
1790
  }
1788
- var loadInferenceReceiptDurabilityTimeoutMs, loadInferenceAgentDataRequestTimeoutMs, loadInferenceAgentCommandLockPolicy, InferenceHostProcessLockBusyError, INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS, WINDOWS_PRIVATE_ACL_BROKER_STARTUP_TIMEOUT_MS, SMALL_IDENTITY_COMMAND_TIMEOUT_MS, windowsPowerShellEnvironment, windowsPrivateAclBrokerInvocation, WindowsPrivateAclBroker, defaultWindowsPrivateAclBroker, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentityInvocation, windowsBootIdentityInvocation, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, cachedCurrentProcessIdentity, defaultCurrentProcessIdentity, readInferenceHostProcessLockStatus, processLockObservationCache, inferenceProcessIdentitiesMatch, resolveInferenceHostApiUrl, inferenceHostSupervisorServiceManifestPath, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity, inferenceHostCredentialContextPath, inferenceHostCredentialContextTransitionPath, credentialContextForConfig, WINDOWS_PRIVATE_FILE_REPLACE_ERROR_CODES, PROCESS_LOCK_PUBLICATION_GRACE_MS, PROCESS_LOCK_MAX_RECORD_BYTES, PROCESS_LOCK_GUARD_ELECTION_LEASE_MS, PROCESS_LOCK_GUARD_ELECTION_STALE_MS, PROCESS_LOCK_GUARD_ELECTION_ATTEMPTS, PROCESS_LOCK_GUARD_ELECTION_BACKOFF_MS, PROCESS_LOCK_LABEL, PROCESS_LOCK_GUARD_LABEL, sameProcessLockFile, openExclusiveProcessLockFile, inspectProcessLockFile, removeVerifiedStaleProcessLockFile, removeStaleProcessLockReclaimGuard, acquireProcessLockReclaimGuard;
1791
+ var loadInferenceReceiptDurabilityTimeoutMs, loadInferenceAgentDataRequestTimeoutMs, loadInferenceAgentCommandLockPolicy, InferenceHostProcessLockBusyError, INFERENCE_CREDENTIAL_NAMESPACE, MAX_INFERENCE_PRIVATE_FILE_BYTES, WINDOWS_PRIVATE_ACL_MODES, WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS, WINDOWS_PRIVATE_ACL_BROKER_STARTUP_TIMEOUT_MS, SMALL_IDENTITY_COMMAND_TIMEOUT_MS, windowsPowerShellEnvironment, windowsPrivateAclBrokerInvocation, WindowsPrivateAclBroker, defaultWindowsPrivateAclBroker, runWindowsPrivateAcl, windowsAclCache, windowsAclIdentity, aclCacheKey, secureWindowsPrivatePath, invalidateWindowsAclCache, rebindHardenedWindowsAclAfterRename, rebindHardenedWindowsDirectoryAfterOwnedMutation, isWindowsPrivateAclVerificationFailure, isDefaultWindowsPrivatePath, secureExistingWindowsPath, linuxProcessIdentity, windowsProcessIdentityInvocation, windowsBootIdentityInvocation, windowsProcessIdentity, darwinProcessIdentity, runSmallIdentityCommand, cachedSystemBootIdentity, readInferenceSystemBootIdentity, defaultProcessIdentity, cachedCurrentProcessIdentity, defaultCurrentProcessIdentity, readInferenceHostProcessLockStatus, processLockObservationCache, inferenceProcessIdentitiesMatch, resolveInferenceHostApiUrl, inferenceHostSupervisorServiceManifestPath, DEFAULT_INFERENCE_HOST_INSTANCE, SAFE_INFERENCE_HOST_INSTANCE, requireNamedInstancePath, credentialStoreIdentity, inferenceHostCredentialContextPath, inferenceHostCredentialContextTransitionPath, credentialContextForConfig, WINDOWS_PRIVATE_FILE_REPLACE_ERROR_CODES, PROCESS_LOCK_PUBLICATION_GRACE_MS, PROCESS_LOCK_MAX_RECORD_BYTES, PROCESS_LOCK_GUARD_ELECTION_LEASE_MS, PROCESS_LOCK_GUARD_ELECTION_STALE_MS, PROCESS_LOCK_GUARD_ELECTION_ATTEMPTS, PROCESS_LOCK_GUARD_ELECTION_BACKOFF_MS, PROCESS_LOCK_LABEL, PROCESS_LOCK_GUARD_LABEL, sameProcessLockFile, openExclusiveProcessLockFile, inspectProcessLockFile, removeVerifiedStaleProcessLockFile, removeStaleProcessLockReclaimGuard, acquireProcessLockReclaimGuard;
1789
1792
  var init_config2 = __esm({
1790
1793
  "lib/inference-host/config.ts"() {
1791
1794
  "use strict";
@@ -1847,6 +1850,7 @@ var init_config2 = __esm({
1847
1850
  };
1848
1851
  INFERENCE_CREDENTIAL_NAMESPACE = "vtxmacro-insights-inference";
1849
1852
  MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
1853
+ WINDOWS_PRIVATE_ACL_MODES = ["harden", "verify", "verify-owner", "verify-private"];
1850
1854
  WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
1851
1855
  $ProgressPreference='SilentlyContinue'
1852
1856
  while(($line=[Console]::In.ReadLine()) -ne $null) {
@@ -1863,7 +1867,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
1863
1867
  $kind=[string]$request.kind
1864
1868
  $mode=[string]$request.mode
1865
1869
  if($kind -ne 'directory' -and $kind -ne 'file') { throw 'invalid path kind' }
1866
- if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner') { throw 'invalid ACL mode' }
1870
+ if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner' -and $mode -ne 'verify-private') { throw 'invalid ACL mode' }
1867
1871
  $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([string]$request.path_b64))
1868
1872
  if([string]::IsNullOrWhiteSpace($path) -or $path.Length -gt 32767 -or $path.Contains([char]0)) { throw 'invalid path' }
1869
1873
  $failureStage='path_lookup'
@@ -1912,11 +1916,12 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
1912
1916
  $owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
1913
1917
  if($owner -ne $current.Value -and ($owner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
1914
1918
  if($mode -ne 'verify-owner') {
1915
- if(-not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
1919
+ $inheritedAllowed=$mode -eq 'verify-private'
1920
+ if(-not $inheritedAllowed -and -not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
1916
1921
  $userFull=$false
1917
1922
  foreach($rule in $actual.Access) {
1918
1923
  $sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
1919
- if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
1924
+ if(($rule.IsInherited -and -not $inheritedAllowed) -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
1920
1925
  throw 'private ACL contains an unauthorized access rule'
1921
1926
  }
1922
1927
  if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
@@ -2052,9 +2057,9 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
2052
2057
  error48.code = "ENOENT";
2053
2058
  pending3.reject(error48);
2054
2059
  } else {
2055
- pending3.reject(new Error(
2060
+ pending3.reject(Object.assign(new Error(
2056
2061
  `Windows private ACL ${pending3.mode} failed (${response.error_code}).`
2057
- ));
2062
+ ), { aclStage: response.error_code }));
2058
2063
  }
2059
2064
  this.scheduleIdleShutdown();
2060
2065
  }
@@ -2159,7 +2164,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
2159
2164
  };
2160
2165
  invalidateWindowsAclCache = (path) => {
2161
2166
  for (const kind of ["file", "directory"]) {
2162
- for (const mode of ["harden", "verify", "verify-owner"]) {
2167
+ for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
2163
2168
  windowsAclCache.delete(aclCacheKey(path, kind, mode));
2164
2169
  }
2165
2170
  }
@@ -2178,7 +2183,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
2178
2183
  }
2179
2184
  invalidateWindowsAclCache(source);
2180
2185
  invalidateWindowsAclCache(destination);
2181
- for (const mode of ["harden", "verify", "verify-owner"]) {
2186
+ for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
2182
2187
  windowsAclCache.set(aclCacheKey(destination, kind, mode), published);
2183
2188
  }
2184
2189
  };
@@ -2189,10 +2194,11 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
2189
2194
  throw new Error("Windows private directory identity changed during atomic publication.");
2190
2195
  }
2191
2196
  invalidateWindowsAclCache(path);
2192
- for (const mode of ["harden", "verify", "verify-owner"]) {
2197
+ for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
2193
2198
  windowsAclCache.set(aclCacheKey(path, "directory", mode), current);
2194
2199
  }
2195
2200
  };
2201
+ isWindowsPrivateAclVerificationFailure = (error48) => error48?.aclStage === "acl_verification";
2196
2202
  isDefaultWindowsPrivatePath = (path, root = resolve(homedir2(), ".vtx", "inference-host")) => {
2197
2203
  const child = relative(root, resolve(path));
2198
2204
  return child === "" || !child.startsWith("..") && !child.includes(":");
@@ -2421,11 +2427,11 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
2421
2427
  inferenceHostSupervisorServiceManifestPath = (env) => `${join2(inferenceBaseDir(env), "state.json")}.service.json`;
2422
2428
  DEFAULT_INFERENCE_HOST_INSTANCE = "default";
2423
2429
  SAFE_INFERENCE_HOST_INSTANCE = /^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u;
2424
- requireNamedInstancePath = (instanceName, instanceDir, rawPath, label) => {
2430
+ requireNamedInstancePath = (instanceName, instanceDir, rawPath, label2) => {
2425
2431
  if (!rawPath || instanceName === DEFAULT_INFERENCE_HOST_INSTANCE) return rawPath;
2426
2432
  const relativePath = relative(resolve(instanceDir), resolve(rawPath));
2427
2433
  if (relativePath === ".." || relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute2(relativePath)) {
2428
- throw new Error(`${label} for a named instance must stay inside its isolated instance directory.`);
2434
+ throw new Error(`${label2} for a named instance must stay inside its isolated instance directory.`);
2429
2435
  }
2430
2436
  return rawPath;
2431
2437
  };
@@ -2480,7 +2486,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
2480
2486
  throw error48;
2481
2487
  }
2482
2488
  };
2483
- inspectProcessLockFile = async (path, label, probes) => {
2489
+ inspectProcessLockFile = async (path, label2, probes) => {
2484
2490
  let before;
2485
2491
  try {
2486
2492
  before = await lstat(path);
@@ -2489,7 +2495,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
2489
2495
  throw error48;
2490
2496
  }
2491
2497
  if (before.isSymbolicLink() || !before.isFile() || process.platform !== "win32" && (before.mode & 63) !== 0 || typeof process.getuid === "function" && before.uid !== process.getuid()) {
2492
- throw new Error(`${label} is unsafe.`);
2498
+ throw new Error(`${label2} is unsafe.`);
2493
2499
  }
2494
2500
  let handle;
2495
2501
  try {
@@ -2736,7 +2742,7 @@ __export(updater_exports, {
2736
2742
  });
2737
2743
  import { spawn as spawn2 } from "node:child_process";
2738
2744
  import { createHash, randomUUID } from "node:crypto";
2739
- import { access, appendFile, chmod, lstat as lstat2, mkdir as mkdir3, readFile as readFile3, readdir as readdir3, realpath as realpath2, rename as rename2, rm as rm3 } from "node:fs/promises";
2745
+ import { access, appendFile, chmod, lstat as lstat2, readFile as readFile3, readdir as readdir3, realpath as realpath2, rename as rename2, rm as rm3 } from "node:fs/promises";
2740
2746
  import { constants as osConstants, homedir as homedir3 } from "node:os";
2741
2747
  import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3, relative as relative2, resolve as resolve2, win32 as windowsPath2 } from "node:path";
2742
2748
  var CLI_PACKAGE_NAME, OFFICIAL_NPM_REGISTRY, CLI_UPDATE_COMMAND, parseCliReleaseVersion, compareCliReleaseVersions, isPathWithin, defaultRunCliCommand, runCliReleaseNpmCommand, fetchLatestCliRelease, verifyCliReleaseIntegrity, verifyInstalledCliRelease, cleanupInterruptedReleaseStaging, ensureUpdaterPrivateDirectory, stageCliRelease, stageCliReleaseWithin, readCliReleaseRejection, writeCliReleaseRejection, CLI_BACKGROUND_STAGE_ARGUMENT, READY_MESSAGE_TYPE, CHILD_ENV, READY_PROTOCOL_ENV, EMPTY_STATE, resolveCliUpdatePaths, cliAutoUpdateDisabled, resolveRegistry, validSelection, readState, writeState, appendCliUpdateLog, isSkippedInvocation, isVersionQuery, policyApiUrl, fetchMinimumSupportedVersion, classifyCliInstall, refusal, inferenceHostServiceManifestPaths, prune, stageAndSelect, planCliEntryUpdate, spawnBackgroundStage, runCliBackgroundStage, installStopTracker, runReleaseProcess, reportCliEntryReady, markConfirmed, ROLLBACK_SKIPPED, rollBack, finishRollBack, INTERRUPTED_EXIT_CODE, superviseRuntime, releaseEntryScript, runCliEntry, RUNTIME_RESTART_FOR_UPDATE_EXIT_CODE, RUNTIME_SUPERVISED_ENV, RUNTIME_RESUME_ENV, RUNTIME_SUPERVISOR_PID_ENV, RUNTIME_RESUME_FILE_ENV, runtimeResumeFile, parseRuntimeResumeRecord, readRuntimeResume, writeRuntimeResume, clearRuntimeResume, isSupervisableRuntimeCommand, isRuntimeResume, runtimeRestartEligible, pollRuntimeRelease, createRuntimeRestartCheck;
@@ -2872,7 +2878,32 @@ var init_updater = __esm({
2872
2878
  };
2873
2879
  ensureUpdaterPrivateDirectory = async (path, root, dependencies = {}) => {
2874
2880
  const platform = dependencies.platform ?? process.platform;
2875
- if (platform !== "win32" && (resolve2(path) === resolve2(root) || isPathWithin(root, path))) {
2881
+ if (platform === "win32") {
2882
+ const existing = await lstat2(path).catch((error48) => {
2883
+ if (error48.code === "ENOENT") return null;
2884
+ throw error48;
2885
+ });
2886
+ if (!existing) {
2887
+ await ensureInferencePrivateDirectory(path, { ...dependencies, defaultRoot: root });
2888
+ return;
2889
+ }
2890
+ if (existing.isSymbolicLink() || !existing.isDirectory()) {
2891
+ throw new Error("Inference host state directory is unsafe.");
2892
+ }
2893
+ const runner = dependencies.runner ?? runWindowsPrivateAcl;
2894
+ try {
2895
+ await secureWindowsPrivatePath(path, "directory", "verify-private", platform, runner);
2896
+ } catch (error48) {
2897
+ if (!isWindowsPrivateAclVerificationFailure(error48)) throw error48;
2898
+ if ((await readdir3(path)).length > 0) {
2899
+ const recovery = /\.service-releases$/u.test(path) ? "run 'vtx inference-host service uninstall' then 'install'" : "delete this directory; it is recreated private";
2900
+ throw new Error(`updater_dir_not_private: ${recovery} (${path})`, { cause: error48 });
2901
+ }
2902
+ await secureWindowsPrivatePath(path, "directory", "harden", platform, runner);
2903
+ }
2904
+ return;
2905
+ }
2906
+ if (resolve2(path) === resolve2(root) || isPathWithin(root, path)) {
2876
2907
  const existing = await lstat2(path).catch(() => null);
2877
2908
  if (existing && existing.isDirectory() && !existing.isSymbolicLink() && (existing.mode & 63) !== 0 && (typeof process.getuid !== "function" || existing.uid === process.getuid())) {
2878
2909
  await chmod(path, 448);
@@ -2929,8 +2960,8 @@ var init_updater = __esm({
2929
2960
  } catch (error48) {
2930
2961
  if (error48.code !== "ENOENT") throw error48;
2931
2962
  }
2932
- await mkdir3(stagingPath, { recursive: true, mode: 448 });
2933
- await mkdir3(downloadPath, { recursive: true, mode: 448 });
2963
+ await ensureUpdaterPrivateDirectory(stagingPath, input.privateRoot, { platform });
2964
+ await ensureUpdaterPrivateDirectory(downloadPath, input.privateRoot, { platform });
2934
2965
  try {
2935
2966
  const packed = await runCliReleaseNpmCommand(executable, [
2936
2967
  "pack",
@@ -20407,25 +20438,25 @@ var init_external_inference_contract = __esm({
20407
20438
  // lib/inference-host/credential-store.ts
20408
20439
  import { createHash as createHash2 } from "node:crypto";
20409
20440
  import { spawn as spawn3 } from "node:child_process";
20410
- function canonicalUrl(raw, label) {
20441
+ function canonicalUrl(raw, label2) {
20411
20442
  let parsed;
20412
20443
  try {
20413
20444
  parsed = new URL(raw);
20414
20445
  } catch {
20415
- throw new Error(`${label} is invalid.`);
20446
+ throw new Error(`${label2} is invalid.`);
20416
20447
  }
20417
20448
  if (parsed.username || parsed.password || parsed.search || parsed.hash || !parsed.hostname || !["http:", "https:"].includes(parsed.protocol)) {
20418
- throw new Error(`${label} is invalid.`);
20449
+ throw new Error(`${label2} is invalid.`);
20419
20450
  }
20420
20451
  const isLoopback = ["localhost", "127.0.0.1", "[::1]"].includes(parsed.hostname);
20421
20452
  if (parsed.protocol !== "https:" && !isLoopback) {
20422
- throw new Error(`${label} must use HTTPS outside loopback.`);
20453
+ throw new Error(`${label2} must use HTTPS outside loopback.`);
20423
20454
  }
20424
20455
  return parsed.toString().replace(/\/$/u, "");
20425
20456
  }
20426
- function isoTimestamp(value, label) {
20457
+ function isoTimestamp(value, label2) {
20427
20458
  if (typeof value !== "string" || !value || !Number.isFinite(Date.parse(value))) {
20428
- throw new Error(`${label} is invalid.`);
20459
+ throw new Error(`${label2} is invalid.`);
20429
20460
  }
20430
20461
  return value;
20431
20462
  }
@@ -21396,33 +21427,33 @@ var init_crypto = __esm({
21396
21427
  // lib/inference-host/oauth.ts
21397
21428
  import { createHash as createHash4, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
21398
21429
  import { createServer } from "node:http";
21399
- function requireObject(value, label) {
21430
+ function requireObject(value, label2) {
21400
21431
  if (!value || typeof value !== "object" || Array.isArray(value)) {
21401
- throw new Error(`${label} is invalid.`);
21432
+ throw new Error(`${label2} is invalid.`);
21402
21433
  }
21403
21434
  return value;
21404
21435
  }
21405
- function exactUrl(raw, label) {
21406
- if (typeof raw !== "string" || !raw) throw new Error(`${label} is unavailable.`);
21436
+ function exactUrl(raw, label2) {
21437
+ if (typeof raw !== "string" || !raw) throw new Error(`${label2} is unavailable.`);
21407
21438
  let parsed;
21408
21439
  try {
21409
21440
  parsed = new URL(raw);
21410
21441
  } catch {
21411
- throw new Error(`${label} is invalid.`);
21442
+ throw new Error(`${label2} is invalid.`);
21412
21443
  }
21413
21444
  if (parsed.username || parsed.password || parsed.search || parsed.hash || !parsed.hostname) {
21414
- throw new Error(`${label} is invalid.`);
21445
+ throw new Error(`${label2} is invalid.`);
21415
21446
  }
21416
21447
  const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(parsed.hostname);
21417
21448
  if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && loopback)) {
21418
- throw new Error(`${label} must use HTTPS outside loopback.`);
21449
+ throw new Error(`${label2} must use HTTPS outside loopback.`);
21419
21450
  }
21420
21451
  return parsed.toString();
21421
21452
  }
21422
- function requireSameOrigin(endpoint, issuer, label) {
21423
- const parsed = new URL(exactUrl(endpoint, label));
21453
+ function requireSameOrigin(endpoint, issuer, label2) {
21454
+ const parsed = new URL(exactUrl(endpoint, label2));
21424
21455
  if (parsed.origin !== new URL(issuer).origin) {
21425
- throw new Error(`${label} has an unexpected origin.`);
21456
+ throw new Error(`${label2} has an unexpected origin.`);
21426
21457
  }
21427
21458
  return parsed.toString();
21428
21459
  }
@@ -22158,6 +22189,9 @@ var init_runtime_policy_generated2 = __esm({
22158
22189
  evidence_max_items: 32,
22159
22190
  evidence_shared_read_seconds: 10
22160
22191
  },
22192
+ protective_plan_set_lock: {
22193
+ wait_seconds: 120
22194
+ },
22161
22195
  history_evidence: {
22162
22196
  device_rounds_per_beat: 6,
22163
22197
  device_retry_seconds: 60
@@ -33244,10 +33278,10 @@ var init_runner = __esm({
33244
33278
  if (known && (!window2 || window2.windowDurationMins !== known.windowDurationMins)) return false;
33245
33279
  return window2 === null || window2.resetsAt !== null && window2.resetsAt * 1e3 > nowMs2 && (window2.usedPercent < 100 || codexCreditsAvailable(rateLimits.rateLimits));
33246
33280
  });
33247
- finitePositiveOption = (value, fallback, label) => {
33281
+ finitePositiveOption = (value, fallback, label2) => {
33248
33282
  const result = value ?? fallback;
33249
33283
  if (!Number.isSafeInteger(result) || result < 1) {
33250
- throw new InferenceHostRunnerError("invalid_configuration", `${label} must be a positive integer.`);
33284
+ throw new InferenceHostRunnerError("invalid_configuration", `${label2} must be a positive integer.`);
33251
33285
  }
33252
33286
  return result;
33253
33287
  };
@@ -46377,10 +46411,10 @@ Durable service:
46377
46411
  vtx inference-host service recover --instance codex-1 --force-recovery
46378
46412
  vtx inference-host service <start|stop|status|logs|recover|uninstall>
46379
46413
  `;
46380
- parseHostConcurrency = (raw, label) => {
46414
+ parseHostConcurrency = (raw, label2) => {
46381
46415
  const value = Number(raw);
46382
46416
  if (!Number.isSafeInteger(value) || value < 1) {
46383
- throw new Error(`${label} must be a positive safe integer.`);
46417
+ throw new Error(`${label2} must be a positive safe integer.`);
46384
46418
  }
46385
46419
  return value;
46386
46420
  };
@@ -46688,8 +46722,8 @@ Durable service:
46688
46722
  throw new Error("Inference host revocation checkpoint does not match credential recovery.");
46689
46723
  }
46690
46724
  };
46691
- fileExistsPrivately = async (path, label) => {
46692
- const raw = await readInferencePrivateFile(path, label);
46725
+ fileExistsPrivately = async (path, label2) => {
46726
+ const raw = await readInferencePrivateFile(path, label2);
46693
46727
  return raw !== null;
46694
46728
  };
46695
46729
  inspectCodexAuthentication = async (config2) => {
@@ -50092,7 +50126,7 @@ function getProfileHeaders(baseHeaders = {}, explicitProfileId) {
50092
50126
  if (profileId) headers["x-profile-id"] = profileId.toString();
50093
50127
  return headers;
50094
50128
  }
50095
- var configuredApiTransport, getApiUrl, apiFetch, sleep, inFlightClientGetRequests, recentClientGetResponses, clientGetRequestGenerations, initializedClientGetRequestKeys, CLIENT_GET_DEDUPE_TTL_MS, CLIENT_RUNTIME_STATUSES_DEDUPE_TTL_MS, CLIENT_LLM_CONFIG_PRIME_TTL_MS, AUTH_BOOTSTRAP_GET_MAX_ATTEMPTS, AUTH_BOOTSTRAP_RETRY_JITTER_MS, canUseClientGetRequestCache, invalidateClientGetRequestCache, raceWithAbortSignal, shareClientGetRequest, clientGetRequestKey, clientPreferencesRequestKey, invalidateClientPreferencesCache, parseRetryAfterMs2, resolveAuthBootstrapRetryDelayMs, fetchAuthBootstrapGetWithRetry, annotateMarketDataRequestError, buildMarketDataFetchError, buildMarketDataNetworkError, buildMarketDataResponseBodyError, isTransientNetworkFetchError, createIdempotencyKey, getErrorMessage, getErrorPayloadOrEmpty, getErrorPayloadOrFallbackDetail, getErrorMessageFromResponse, throwUnauthorizedIfNeeded, activeProfileId, PROFILE_STORAGE_KEY;
50129
+ var configuredApiTransport, getApiUrl, apiFetch, sleep, inFlightClientGetRequests, recentClientGetResponses, clientGetRequestGenerations, initializedClientGetRequestKeys, CLIENT_GET_DEDUPE_TTL_MS, CLIENT_LLM_CONFIG_PRIME_TTL_MS, AUTH_BOOTSTRAP_GET_MAX_ATTEMPTS, AUTH_BOOTSTRAP_RETRY_JITTER_MS, canUseClientGetRequestCache, invalidateClientGetRequestCache, raceWithAbortSignal, shareClientGetRequest, clientGetRequestKey, clientPreferencesRequestKey, invalidateClientPreferencesCache, parseRetryAfterMs2, resolveAuthBootstrapRetryDelayMs, fetchAuthBootstrapGetWithRetry, annotateMarketDataRequestError, buildMarketDataFetchError, buildMarketDataNetworkError, buildMarketDataResponseBodyError, isTransientNetworkFetchError, createIdempotencyKey, getErrorMessage, getErrorPayloadOrEmpty, getErrorPayloadOrFallbackDetail, getErrorMessageFromResponse, throwUnauthorizedIfNeeded, activeProfileId, PROFILE_STORAGE_KEY;
50096
50130
  var init_shared = __esm({
50097
50131
  "lib/api/shared.ts"() {
50098
50132
  "use strict";
@@ -50132,7 +50166,6 @@ var init_shared = __esm({
50132
50166
  clientGetRequestGenerations = /* @__PURE__ */ new Map();
50133
50167
  initializedClientGetRequestKeys = /* @__PURE__ */ new Set();
50134
50168
  CLIENT_GET_DEDUPE_TTL_MS = 2e3;
50135
- CLIENT_RUNTIME_STATUSES_DEDUPE_TTL_MS = 5e3;
50136
50169
  CLIENT_LLM_CONFIG_PRIME_TTL_MS = 1e4;
50137
50170
  AUTH_BOOTSTRAP_GET_MAX_ATTEMPTS = runtime_policy_generated_default2.ui.auth_bootstrap_retry.max_attempts;
50138
50171
  AUTH_BOOTSTRAP_RETRY_JITTER_MS = runtime_policy_generated_default2.ui.auth_bootstrap_retry.jitter_milliseconds;
@@ -50233,7 +50266,7 @@ var init_shared = __esm({
50233
50266
  runtimeRequestErrorKind: kind,
50234
50267
  runtimeRequestTransientNetwork: kind === "network" && isTransientNetworkFetchError(error48)
50235
50268
  });
50236
- buildMarketDataFetchError = async (response, label, path) => {
50269
+ buildMarketDataFetchError = async (response, label2, path) => {
50237
50270
  let responseText = "";
50238
50271
  try {
50239
50272
  responseText = (await response.clone().text()).trim();
@@ -50243,21 +50276,21 @@ var init_shared = __esm({
50243
50276
  const statusText = response.statusText ? ` ${response.statusText}` : "";
50244
50277
  const detail = responseText ? `: ${responseText.slice(0, 240)}` : "";
50245
50278
  return annotateMarketDataRequestError(
50246
- new Error(`Market data request failed: ${label} unavailable (HTTP ${response.status}${statusText})${detail}`),
50279
+ new Error(`Market data request failed: ${label2} unavailable (HTTP ${response.status}${statusText})${detail}`),
50247
50280
  path,
50248
50281
  response.status,
50249
50282
  "http"
50250
50283
  );
50251
50284
  };
50252
- buildMarketDataNetworkError = (error48, label, path) => {
50285
+ buildMarketDataNetworkError = (error48, label2, path) => {
50253
50286
  const message = error48 instanceof Error ? error48.message : String(error48 || "unknown error");
50254
50287
  return annotateMarketDataRequestError(Object.assign(
50255
- new Error(`Market data request failed: ${label} network error (${message})`),
50288
+ new Error(`Market data request failed: ${label2} network error (${message})`),
50256
50289
  { cause: error48, name: error48 instanceof Error ? error48.name : "Error" }
50257
50290
  ), path, null, "network");
50258
50291
  };
50259
- buildMarketDataResponseBodyError = (error48, label, response, path) => annotateMarketDataRequestError(Object.assign(
50260
- new Error(`Market data request failed: ${label} response body could not be read (HTTP ${response.status})`),
50292
+ buildMarketDataResponseBodyError = (error48, label2, response, path) => annotateMarketDataRequestError(Object.assign(
50293
+ new Error(`Market data request failed: ${label2} response body could not be read (HTTP ${response.status})`),
50261
50294
  { cause: error48, name: error48 instanceof Error ? error48.name : "Error" }
50262
50295
  ), path, response.status, "response_body");
50263
50296
  isTransientNetworkFetchError = (error48) => {
@@ -51172,10 +51205,10 @@ var init_network_debug = __esm({
51172
51205
  };
51173
51206
  getLatestRuntimeProviderFailureTelemetrySnapshot = (options = {}) => {
51174
51207
  const maxAgeMs = Number.isFinite(Number(options.maxAgeMs)) ? Math.max(0, Number(options.maxAgeMs)) : 5 * 60 * 1e3;
51175
- const label = typeof options.label === "string" && options.label.trim() ? options.label.trim().toLowerCase() : null;
51208
+ const label2 = typeof options.label === "string" && options.label.trim() ? options.label.trim().toLowerCase() : null;
51176
51209
  const traceId = typeof options.traceId === "string" && options.traceId.trim() ? options.traceId.trim() : null;
51177
51210
  const now = Date.now();
51178
- const candidates = getRuntimeProviderFailureDebugEntries().filter((entry) => entry.kind === "provider" && entry.outcome === "error").filter((entry) => !traceId || entry.id === traceId).filter((entry) => !label || entry.label.toLowerCase() === label).filter((entry) => Math.abs(now - (entry.completedAt ?? entry.startedAt)) <= maxAgeMs).sort((a, b2) => (b2.completedAt ?? b2.startedAt) - (a.completedAt ?? a.startedAt));
51211
+ const candidates = getRuntimeProviderFailureDebugEntries().filter((entry) => entry.kind === "provider" && entry.outcome === "error").filter((entry) => !traceId || entry.id === traceId).filter((entry) => !label2 || entry.label.toLowerCase() === label2).filter((entry) => Math.abs(now - (entry.completedAt ?? entry.startedAt)) <= maxAgeMs).sort((a, b2) => (b2.completedAt ?? b2.startedAt) - (a.completedAt ?? a.startedAt));
51179
51212
  const latest = candidates[0] ?? null;
51180
51213
  if (!latest) {
51181
51214
  return null;
@@ -52073,6 +52106,288 @@ var init_server_runtime_lease = __esm({
52073
52106
  }
52074
52107
  });
52075
52108
 
52109
+ // lib/runtime/hyperliquid-account-mode-contract.ts
52110
+ function resolveExactHyperliquidAccountMode(userAbstraction, ...dexEvidence) {
52111
+ const mode = extractCanonicalHyperliquidAccountMode(userAbstraction);
52112
+ if (!mode) {
52113
+ throw new Error("Exact Hyperliquid userAbstraction response is invalid.");
52114
+ }
52115
+ if (mode !== "default" && mode !== "disabled") return mode;
52116
+ if (dexEvidence.length < 1) {
52117
+ throw new Error("Exact Hyperliquid userDexAbstraction response is unavailable.");
52118
+ }
52119
+ return parseExactDexAbstractionEnabled(dexEvidence[0]) ? "dexAbstraction" : mode;
52120
+ }
52121
+ var UNIFIED_MODE_ALIASES, CANONICAL_ACCOUNT_MODE_ALIASES, EXACT_MODE_KEYS, EXACT_MODE_BOOLEAN_KEYS, isExactTruthyFlag, normalizeCanonicalHyperliquidAccountMode, extractCanonicalHyperliquidAccountMode, parseExactDexAbstractionEnabled, normalizeAccountMode, extractAccountModeFromPayload, extractAccountModeCandidateFromPayload, isUnifiedAccountMode;
52122
+ var init_hyperliquid_account_mode_contract = __esm({
52123
+ "lib/runtime/hyperliquid-account-mode-contract.ts"() {
52124
+ "use strict";
52125
+ init_define_VTX_EXO_POLICY();
52126
+ init_define_VTX_GROK_POLICY();
52127
+ init_define_VTX_PI_MODEL_POLICY();
52128
+ UNIFIED_MODE_ALIASES = /* @__PURE__ */ new Set([
52129
+ "unifiedaccount",
52130
+ "dexabstraction",
52131
+ "portfoliomargin",
52132
+ "unified",
52133
+ "pm"
52134
+ ]);
52135
+ CANONICAL_ACCOUNT_MODE_ALIASES = /* @__PURE__ */ new Map([
52136
+ ["default", "default"],
52137
+ ["standard", "default"],
52138
+ ["classic", "default"],
52139
+ ["disabled", "disabled"],
52140
+ ["unifiedaccount", "unifiedAccount"],
52141
+ ["unified", "unifiedAccount"],
52142
+ ["portfoliomargin", "portfolioMargin"],
52143
+ ["pm", "portfolioMargin"],
52144
+ ["dexabstraction", "dexAbstraction"]
52145
+ ]);
52146
+ EXACT_MODE_KEYS = [
52147
+ "accountMode",
52148
+ "account_mode",
52149
+ "accountType",
52150
+ "account_type",
52151
+ "accountUnificationMode",
52152
+ "account_unification_mode",
52153
+ "abstractionMode",
52154
+ "abstraction_mode",
52155
+ "abstraction",
52156
+ "abstractionState",
52157
+ "dexAbstractionState",
52158
+ "marginMode",
52159
+ "state",
52160
+ "mode",
52161
+ "value"
52162
+ ];
52163
+ EXACT_MODE_BOOLEAN_KEYS = [
52164
+ ["unifiedAccount", ["isUnifiedAccount", "unifiedAccount", "isUnified", "unified", "is_unified_account"]],
52165
+ ["portfolioMargin", ["isPortfolioMargin", "portfolioMargin", "isPm", "pm", "is_portfolio_margin"]],
52166
+ ["dexAbstraction", ["isDexAbstraction", "dexAbstraction", "dexAbstractionEnabled", "is_dex_abstraction"]],
52167
+ ["default", ["isClassic", "classic", "isStandard", "standard"]]
52168
+ ];
52169
+ isExactTruthyFlag = (value) => value === true || value === 1 || typeof value === "string" && value.trim().toLowerCase() === "true";
52170
+ normalizeCanonicalHyperliquidAccountMode = (value) => {
52171
+ if (typeof value !== "string") return null;
52172
+ const key = value.trim().replace(/[_\s-]/g, "").toLowerCase();
52173
+ return CANONICAL_ACCOUNT_MODE_ALIASES.get(key) ?? null;
52174
+ };
52175
+ extractCanonicalHyperliquidAccountMode = (payload, depth = 0) => {
52176
+ if (depth > 6) return null;
52177
+ const direct = normalizeCanonicalHyperliquidAccountMode(payload);
52178
+ if (direct) return direct;
52179
+ if (Array.isArray(payload)) {
52180
+ for (const item of payload) {
52181
+ const mode = extractCanonicalHyperliquidAccountMode(item, depth + 1);
52182
+ if (mode) return mode;
52183
+ }
52184
+ return null;
52185
+ }
52186
+ if (!payload || typeof payload !== "object") return null;
52187
+ const source = payload;
52188
+ for (const key of EXACT_MODE_KEYS) {
52189
+ if (!(key in source)) continue;
52190
+ const mode = normalizeCanonicalHyperliquidAccountMode(source[key]);
52191
+ if (mode) return mode;
52192
+ }
52193
+ for (const [mode, keys] of EXACT_MODE_BOOLEAN_KEYS) {
52194
+ for (const key of keys) {
52195
+ if (isExactTruthyFlag(source[key])) return mode;
52196
+ }
52197
+ }
52198
+ for (const value of Object.values(source)) {
52199
+ const mode = extractCanonicalHyperliquidAccountMode(value, depth + 1);
52200
+ if (mode) return mode;
52201
+ }
52202
+ return null;
52203
+ };
52204
+ parseExactDexAbstractionEnabled = (payload) => {
52205
+ if (payload == null || payload === false) return false;
52206
+ if (payload === true) return true;
52207
+ if (typeof payload === "string") {
52208
+ const normalized = payload.trim().toLowerCase();
52209
+ if (normalized === "true") return true;
52210
+ if (normalized === "false") return false;
52211
+ }
52212
+ if (typeof payload === "number" && Number.isFinite(payload)) {
52213
+ if (payload === 1) return true;
52214
+ if (payload === 0) return false;
52215
+ }
52216
+ throw new Error("Exact Hyperliquid userDexAbstraction response is invalid.");
52217
+ };
52218
+ normalizeAccountMode = (mode) => String(mode ?? "").trim().toLowerCase().replace(/[_\s-]/g, "");
52219
+ extractAccountModeFromPayload = (payload) => {
52220
+ const candidate = extractAccountModeCandidateFromPayload(payload);
52221
+ if (candidate && candidate.trim().length > 0) {
52222
+ return candidate;
52223
+ }
52224
+ return "default";
52225
+ };
52226
+ extractAccountModeCandidateFromPayload = (payload) => {
52227
+ if (!payload || typeof payload !== "object") {
52228
+ return null;
52229
+ }
52230
+ const source = payload;
52231
+ if ("accountMode" in source) {
52232
+ return String(source.accountMode ?? "");
52233
+ }
52234
+ if ("account_mode" in source) {
52235
+ return String(source.account_mode ?? "");
52236
+ }
52237
+ return null;
52238
+ };
52239
+ isUnifiedAccountMode = (mode) => UNIFIED_MODE_ALIASES.has(normalizeAccountMode(mode));
52240
+ }
52241
+ });
52242
+
52243
+ // lib/runtime/hyperliquid-account-contract.ts
52244
+ var toNumber, firstNumber, roundTo, canonicalAvailableMargin, STABLE_SPOT_SYMBOLS, spotBalanceValueUsd, normalizeSpotBalancesFromInfoResponse, sumBalanceHoldUsd, sumPositionUnrealizedPnl, resolveTotalUnrealizedPnl, approximatelyEqualAccountValues, buildAccountSummaryFromInfoResponses;
52245
+ var init_hyperliquid_account_contract = __esm({
52246
+ "lib/runtime/hyperliquid-account-contract.ts"() {
52247
+ "use strict";
52248
+ init_define_VTX_EXO_POLICY();
52249
+ init_define_VTX_GROK_POLICY();
52250
+ init_define_VTX_PI_MODEL_POLICY();
52251
+ init_hyperliquid_account_mode_contract();
52252
+ toNumber = (value, fallback = 0) => {
52253
+ const numeric2 = Number(value);
52254
+ return Number.isFinite(numeric2) ? numeric2 : fallback;
52255
+ };
52256
+ firstNumber = (values, fallback = 0) => {
52257
+ for (const value of values) {
52258
+ if (value == null || typeof value === "string" && value.trim() === "") {
52259
+ continue;
52260
+ }
52261
+ const numeric2 = Number(value);
52262
+ if (Number.isFinite(numeric2)) {
52263
+ return numeric2;
52264
+ }
52265
+ }
52266
+ return fallback;
52267
+ };
52268
+ roundTo = (value, digits = 12) => Number(value.toFixed(digits));
52269
+ canonicalAvailableMargin = (accountValue, totalMarginUsed) => Math.max(0, accountValue - totalMarginUsed);
52270
+ STABLE_SPOT_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USD", "USDT"]);
52271
+ spotBalanceValueUsd = (balance, coin, total) => {
52272
+ const explicit = firstNumber([balance?.value_usd, balance?.usdValue, balance?.usdcValue], Number.NaN);
52273
+ if (Number.isFinite(explicit)) {
52274
+ return explicit;
52275
+ }
52276
+ return STABLE_SPOT_SYMBOLS.has(coin) ? total : 0;
52277
+ };
52278
+ normalizeSpotBalancesFromInfoResponse = (spotResponse) => {
52279
+ const containers = [];
52280
+ if (spotResponse && typeof spotResponse === "object") {
52281
+ containers.push(spotResponse);
52282
+ const keys = ["spotState", "userState", "clearinghouseState", "state", "data"];
52283
+ for (const key of keys) {
52284
+ if (spotResponse[key] && typeof spotResponse[key] === "object") {
52285
+ containers.push(spotResponse[key]);
52286
+ }
52287
+ }
52288
+ }
52289
+ const firstWithBalances = containers.find((container) => Array.isArray(container?.balances));
52290
+ const balances = Array.isArray(firstWithBalances?.balances) ? firstWithBalances.balances : [];
52291
+ return balances.map((balance) => {
52292
+ const coin = String(balance?.coin ?? "").toUpperCase();
52293
+ const total = toNumber(balance?.total, 0);
52294
+ const hold = toNumber(balance?.hold, 0);
52295
+ const available = roundTo(Math.max(0, total - hold));
52296
+ const value_usd = spotBalanceValueUsd(balance, coin, total);
52297
+ return {
52298
+ coin,
52299
+ total,
52300
+ hold,
52301
+ available,
52302
+ value_usd
52303
+ };
52304
+ }).filter((balance) => balance.coin.length > 0);
52305
+ };
52306
+ sumBalanceHoldUsd = (balances) => balances.reduce((acc, balance) => {
52307
+ const hold = toNumber(balance.hold, 0);
52308
+ const total = toNumber(balance.total, 0);
52309
+ const valueUsd = toNumber(balance.value_usd, 0);
52310
+ if (hold <= 0 || total <= 0 || valueUsd <= 0) {
52311
+ return acc;
52312
+ }
52313
+ return acc + Math.max(0, valueUsd * (hold / total));
52314
+ }, 0);
52315
+ sumPositionUnrealizedPnl = (perpsResponse) => {
52316
+ const assetPositions = Array.isArray(perpsResponse?.assetPositions) ? perpsResponse.assetPositions : [];
52317
+ return assetPositions.reduce((total, entry) => {
52318
+ const unrealizedPnl = toNumber(entry?.position?.unrealizedPnl, 0);
52319
+ return total + unrealizedPnl;
52320
+ }, 0);
52321
+ };
52322
+ resolveTotalUnrealizedPnl = (perpsResponse) => {
52323
+ const topLevel = toNumber(perpsResponse?.totalUnrealizedPnl, 0);
52324
+ const positionTotal = sumPositionUnrealizedPnl(perpsResponse);
52325
+ if (Math.abs(topLevel) < 1e-12 && Math.abs(positionTotal) >= 1e-12) {
52326
+ return positionTotal;
52327
+ }
52328
+ return topLevel;
52329
+ };
52330
+ approximatelyEqualAccountValues = (left, right, absoluteToleranceUsd, relativeTolerance) => {
52331
+ const reference = Math.max(Math.abs(left), Math.abs(right), 1);
52332
+ return Math.abs(left - right) <= Math.max(
52333
+ absoluteToleranceUsd,
52334
+ reference * relativeTolerance
52335
+ );
52336
+ };
52337
+ buildAccountSummaryFromInfoResponses = (perpsResponse, spotResponse, options = {}) => {
52338
+ const marginSummary = perpsResponse?.marginSummary ?? {};
52339
+ const perpsAccountValue = toNumber(marginSummary?.accountValue, 0);
52340
+ const total_margin_used = toNumber(marginSummary?.totalMarginUsed, 0);
52341
+ const totalNtlPos = toNumber(marginSummary?.totalNtlPos, 0);
52342
+ const withdrawable = toNumber(perpsResponse?.withdrawable, 0);
52343
+ const maintenance_margin = firstNumber([
52344
+ perpsResponse?.crossMaintenanceMarginUsed,
52345
+ marginSummary?.crossMaintenanceMarginUsed,
52346
+ marginSummary?.totalMaintenanceMargin
52347
+ ], 0);
52348
+ const balances = normalizeSpotBalancesFromInfoResponse(spotResponse);
52349
+ const spotPortfolioValue = balances.reduce((acc, balance) => acc + toNumber(balance.value_usd, 0), 0);
52350
+ const balancesHoldUsd = sumBalanceHoldUsd(balances);
52351
+ const account_mode = extractAccountModeFromPayload(perpsResponse);
52352
+ const crossMarginSummary = perpsResponse?.crossMarginSummary && typeof perpsResponse.crossMarginSummary === "object" ? perpsResponse.crossMarginSummary : null;
52353
+ const crossMarginAccountValue = crossMarginSummary ? toNumber(crossMarginSummary.accountValue, 0) : 0;
52354
+ const normalizedMode = String(account_mode || "").trim().toLowerCase().replace(/[_\s-]/g, "");
52355
+ const isAmbiguousDefaultMode = normalizedMode.length === 0 || normalizedMode === "default";
52356
+ const spotDominatesMinTotalUsd = Number(options.spotDominatesMinTotalUsd ?? options.spot_dominates_min_total_usd);
52357
+ const spotDominatesPerpsMultiplier = Number(options.spotDominatesPerpsMultiplier ?? options.spot_dominates_perps_multiplier);
52358
+ const mirroredAccountValueAbsoluteToleranceUsd = Number(
52359
+ options.mirroredAccountValueAbsoluteToleranceUsd ?? options.mirrored_account_value_absolute_tolerance_usd
52360
+ );
52361
+ const mirroredAccountValueRelativeTolerance = Number(
52362
+ options.mirroredAccountValueRelativeTolerance ?? options.mirrored_account_value_relative_tolerance
52363
+ );
52364
+ const hasSpotDominanceThresholds = Number.isFinite(spotDominatesMinTotalUsd) && spotDominatesMinTotalUsd >= 0 && Number.isFinite(spotDominatesPerpsMultiplier) && spotDominatesPerpsMultiplier >= 0;
52365
+ const hasMirroredAccountValueTolerances = Number.isFinite(mirroredAccountValueAbsoluteToleranceUsd) && mirroredAccountValueAbsoluteToleranceUsd >= 0 && Number.isFinite(mirroredAccountValueRelativeTolerance) && mirroredAccountValueRelativeTolerance >= 0;
52366
+ const spotDominatesPerps = hasSpotDominanceThresholds && spotPortfolioValue >= spotDominatesMinTotalUsd && (perpsAccountValue <= 0 || spotPortfolioValue >= perpsAccountValue * spotDominatesPerpsMultiplier);
52367
+ const ambiguousDefaultLooksUnified = isAmbiguousDefaultMode && spotDominatesPerps;
52368
+ const ambiguousDefaultSpotAlreadyIncludesPerps = ambiguousDefaultLooksUnified && perpsAccountValue > 0 && balancesHoldUsd > 0 && hasMirroredAccountValueTolerances && approximatelyEqualAccountValues(
52369
+ balancesHoldUsd,
52370
+ perpsAccountValue,
52371
+ mirroredAccountValueAbsoluteToleranceUsd,
52372
+ mirroredAccountValueRelativeTolerance
52373
+ );
52374
+ const account_value = isUnifiedAccountMode(account_mode) && crossMarginSummary !== null && crossMarginAccountValue > 0 ? crossMarginAccountValue + spotPortfolioValue : ambiguousDefaultLooksUnified && !ambiguousDefaultSpotAlreadyIncludesPerps ? perpsAccountValue + spotPortfolioValue : Math.max(perpsAccountValue, spotPortfolioValue);
52375
+ return {
52376
+ account_value,
52377
+ total_margin_used,
52378
+ available_margin: roundTo(canonicalAvailableMargin(account_value, total_margin_used)),
52379
+ withdrawable,
52380
+ cross_margin_ratio: account_value > 0 ? maintenance_margin / account_value : 0,
52381
+ maintenance_margin,
52382
+ cross_account_leverage: account_value > 0 ? totalNtlPos / account_value : 0,
52383
+ total_unrealized_pnl: resolveTotalUnrealizedPnl(perpsResponse),
52384
+ account_mode,
52385
+ balances
52386
+ };
52387
+ };
52388
+ }
52389
+ });
52390
+
52076
52391
  // lib/runtime/order-identity.ts
52077
52392
  var clientOrderIdForMutation, clientDecisionMutationId;
52078
52393
  var init_order_identity = __esm({
@@ -52148,7 +52463,7 @@ var init_hyperliquid_order_status = __esm({
52148
52463
  });
52149
52464
 
52150
52465
  // lib/runtime/hyperliquid-public-read.ts
52151
- var policy, weights, recoveryPolicy, budgets, unpersistedBudgets, cache, pending2, PREFIX, priorityRank, createHyperliquidPublicPriorityScope, promoteHyperliquidPublicPriority, abortError2, checkAbort, wait, waitUnlessPromoted, storage, loadBudget, saveBudget, withBudgetLock, HyperliquidPublicReadError, getHyperliquidPublicReadBudget, cooldownError, recoveryWaitError, nonTradingWeightCap, nonTradingWaitMs, fallbackDelayMs, admit, cooldown, finishHyperliquidRecoveryRequest, assertHyperliquidRecoveryDispatch, cacheTtlMs, eligibleCache, reserveHyperliquidPublicReads, releaseHyperliquidPublicReadReservation, admitHyperliquidExchangeRequest, recordHyperliquidExchangeResponse, readHyperliquidPublicInfo;
52466
+ var policy, weights, recoveryPolicy, budgets, unpersistedBudgets, cache, pending2, PREFIX, priorityRank, createHyperliquidPublicPriorityScope, promoteHyperliquidPublicPriority, hostTransport, abortError2, checkAbort, wait, waitUnlessPromoted, storage, loadBudget, saveBudget, withBudgetLock, HyperliquidPublicReadError, getHyperliquidPublicReadBudget, cooldownError, recoveryWaitError, nonTradingWeightCap, nonTradingWaitMs, fallbackDelayMs, admit, cooldown, finishHyperliquidRecoveryRequest, assertHyperliquidRecoveryDispatch, validateHyperliquidRecoveryDispatch, cacheTtlMs, eligibleCache, reserveHyperliquidPublicReads, releaseHyperliquidPublicReadReservation, admitHyperliquidExchangeRequest, recordHyperliquidExchangeResponse, readHyperliquidPublicInfo;
52152
52467
  var init_hyperliquid_public_read = __esm({
52153
52468
  "lib/runtime/hyperliquid-public-read.ts"() {
52154
52469
  "use strict";
@@ -52172,6 +52487,7 @@ var init_hyperliquid_public_read = __esm({
52172
52487
  scope.value = priority;
52173
52488
  scope.followers?.forEach((promote) => promote(priority));
52174
52489
  };
52490
+ hostTransport = null;
52175
52491
  abortError2 = (signal) => getSanitizedRuntimeAbortReason(signal, "Aborted");
52176
52492
  checkAbort = (signal) => {
52177
52493
  if (signal?.aborted) throw abortError2(signal);
@@ -52309,6 +52625,10 @@ var init_hyperliquid_public_read = __esm({
52309
52625
  const weight = weightOverride ?? weights[requestType] ?? policy.default_request_weight;
52310
52626
  const ticket = crypto.randomUUID();
52311
52627
  const deadline = Math.min(deadlineMs ?? Infinity, Date.now() + recoveryPolicy.recovery_queue_wait_seconds * 1e3);
52628
+ if (hostTransport) {
52629
+ const permit = await hostTransport.admission.acquire({ requestType, weight, priority: priority.value, deadlineMs: deadline, signal });
52630
+ return { ...permit, endpoint: apiUrl, fleet: true };
52631
+ }
52312
52632
  let admitted = false;
52313
52633
  try {
52314
52634
  for (; ; ) {
@@ -52379,7 +52699,11 @@ var init_hyperliquid_public_read = __esm({
52379
52699
  });
52380
52700
  }
52381
52701
  };
52382
- cooldown = async (apiUrl, retryAfter) => {
52702
+ cooldown = async (apiUrl, retryAfter, requestType) => {
52703
+ if (hostTransport) {
52704
+ await hostTransport.admission.record429(retryAfter, requestType);
52705
+ return;
52706
+ }
52383
52707
  const key = `${PREFIX}${apiUrl}`;
52384
52708
  await withBudgetLock(key, void 0, () => {
52385
52709
  const now = Date.now();
@@ -52405,6 +52729,11 @@ var init_hyperliquid_public_read = __esm({
52405
52729
  };
52406
52730
  finishHyperliquidRecoveryRequest = async (permit, outcome) => {
52407
52731
  if (!permit) return;
52732
+ if (permit.fleet) {
52733
+ if (!hostTransport) throw new Error("A fleet Hyperliquid permit needs the host transport that issued it.");
52734
+ await hostTransport.admission.finish(permit, outcome);
52735
+ return;
52736
+ }
52408
52737
  const key = `${PREFIX}${permit.endpoint}`;
52409
52738
  await withBudgetLock(key, void 0, () => {
52410
52739
  const budget = loadBudget(key);
@@ -52441,6 +52770,15 @@ var init_hyperliquid_public_read = __esm({
52441
52770
  }
52442
52771
  if (!permit && budget.recovery) throw recoveryWaitError(requestType);
52443
52772
  };
52773
+ validateHyperliquidRecoveryDispatch = async (endpoint, permit, requestType) => {
52774
+ if (permit?.fleet) {
52775
+ if (!hostTransport) throw recoveryWaitError(requestType);
52776
+ await hostTransport.admission.validate(permit, requestType);
52777
+ return;
52778
+ }
52779
+ if (hostTransport) throw recoveryWaitError(requestType);
52780
+ assertHyperliquidRecoveryDispatch(endpoint, permit, requestType);
52781
+ };
52444
52782
  cacheTtlMs = (body, options) => {
52445
52783
  const requestType = String(body.type);
52446
52784
  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;
@@ -52470,7 +52808,7 @@ var init_hyperliquid_public_read = __esm({
52470
52808
  return admit(endpoint.replace(/\/$/, ""), "exchange", signal, weight, void 0, createHyperliquidPublicPriorityScope(priority));
52471
52809
  };
52472
52810
  recordHyperliquidExchangeResponse = async (endpoint, response, permit) => {
52473
- if (response.status === 429) await cooldown(endpoint.replace(/\/$/, ""), response.headers.get("Retry-After"));
52811
+ if (response.status === 429) await cooldown(endpoint.replace(/\/$/, ""), response.headers.get("Retry-After"), "exchange");
52474
52812
  await finishHyperliquidRecoveryRequest(permit, response.status < 400 ? "success" : "failure");
52475
52813
  };
52476
52814
  readHyperliquidPublicInfo = async (endpoint, body, options) => {
@@ -52504,6 +52842,7 @@ var init_hyperliquid_public_read = __esm({
52504
52842
  let settled;
52505
52843
  let acquiring = false;
52506
52844
  let observedRateLimit;
52845
+ let privateRouteUsed = false;
52507
52846
  let pendingPhase = null;
52508
52847
  const setPendingPhase = (next) => {
52509
52848
  if (next === pendingPhase) return;
@@ -52548,14 +52887,18 @@ var init_hyperliquid_public_read = __esm({
52548
52887
  }), priority });
52549
52888
  }
52550
52889
  if (options.reservation?.closed) throw new DOMException("Hyperliquid account read was cancelled.", "AbortError");
52551
- acquiring = true;
52552
- setPendingPhase("admission");
52553
- permit = await admit(apiUrl, requestType, signal, void 0, deadline, priority);
52554
- acquiring = false;
52555
- armDeadline(Math.min(deadline, permit?.expiresAt ?? Infinity));
52556
- checkAbort(signal);
52557
- if (options.reservation?.closed) throw new DOMException("Hyperliquid account read was cancelled.", "AbortError");
52558
- assertHyperliquidRecoveryDispatch(apiUrl, permit, requestType);
52890
+ const privateRoute = await hostTransport?.routeInfo?.(body, priority.value, deadline) ?? null;
52891
+ privateRouteUsed = privateRoute !== null;
52892
+ if (!privateRoute) {
52893
+ acquiring = true;
52894
+ setPendingPhase("admission");
52895
+ permit = await admit(apiUrl, requestType, signal, void 0, deadline, priority);
52896
+ acquiring = false;
52897
+ armDeadline(Math.min(deadline, permit?.expiresAt ?? Infinity));
52898
+ checkAbort(signal);
52899
+ if (options.reservation?.closed) throw new DOMException("Hyperliquid account read was cancelled.", "AbortError");
52900
+ await validateHyperliquidRecoveryDispatch(apiUrl, permit, requestType);
52901
+ }
52559
52902
  outcome = "failure";
52560
52903
  let transportAborted;
52561
52904
  let response;
@@ -52568,16 +52911,16 @@ var init_hyperliquid_public_read = __esm({
52568
52911
  });
52569
52912
  const transport = (async () => {
52570
52913
  setPendingPhase("transport");
52571
- const received = await fetch(`${apiUrl}/info`, {
52914
+ const received = await fetch(`${privateRoute?.url.replace(/\/$/, "") ?? apiUrl}/info`, {
52572
52915
  method: "POST",
52573
- headers: { "Content-Type": "application/json" },
52916
+ headers: { ...privateRoute?.headers, "Content-Type": "application/json" },
52574
52917
  body: JSON.stringify(body),
52575
52918
  signal
52576
52919
  });
52577
52920
  checkAbort(signal);
52578
52921
  if (received.status === 429) {
52579
52922
  observedRateLimit = new HyperliquidPublicReadError("public_rate_limited", "Hyperliquid public API returned HTTP 429.", requestType, 429);
52580
- await cooldown(apiUrl, received.headers.get("Retry-After"));
52923
+ if (!privateRoute) await cooldown(apiUrl, received.headers.get("Retry-After"), requestType);
52581
52924
  throw observedRateLimit;
52582
52925
  }
52583
52926
  let receivedText;
@@ -52624,6 +52967,13 @@ var init_hyperliquid_public_read = __esm({
52624
52967
  outcome = "cancelled";
52625
52968
  throw abortError2(options.signal);
52626
52969
  }
52970
+ const failedStatus = error48?.status;
52971
+ if (outcome === "failure" && privateRouteUsed && !(typeof failedStatus === "number" && failedStatus < 500)) {
52972
+ try {
52973
+ hostTransport?.onPrivateInfoFailure?.(body);
52974
+ } catch {
52975
+ }
52976
+ }
52627
52977
  if (acquiring && signal.aborted && signal.reason?.name === "TimeoutError") throw recoveryWaitError(requestType);
52628
52978
  if (error48 instanceof HyperliquidPublicReadError) throw error48;
52629
52979
  if (error48 instanceof TypeError) throw new HyperliquidPublicReadError("public_network_error", "Hyperliquid public request network failure.", requestType);
@@ -52647,6 +52997,32 @@ var init_hyperliquid_public_read = __esm({
52647
52997
  }
52648
52998
  });
52649
52999
 
53000
+ // lib/runtime/hyperliquid-exchange-rejections.ts
53001
+ var listeners, onHyperliquidExchangeRejection, emitHyperliquidExchangeRejection;
53002
+ var init_hyperliquid_exchange_rejections = __esm({
53003
+ "lib/runtime/hyperliquid-exchange-rejections.ts"() {
53004
+ "use strict";
53005
+ init_define_VTX_EXO_POLICY();
53006
+ init_define_VTX_GROK_POLICY();
53007
+ init_define_VTX_PI_MODEL_POLICY();
53008
+ listeners = /* @__PURE__ */ new Set();
53009
+ onHyperliquidExchangeRejection = (listener) => {
53010
+ listeners.add(listener);
53011
+ return () => {
53012
+ listeners.delete(listener);
53013
+ };
53014
+ };
53015
+ emitHyperliquidExchangeRejection = (message) => {
53016
+ for (const listener of listeners) {
53017
+ try {
53018
+ listener(message);
53019
+ } catch {
53020
+ }
53021
+ }
53022
+ };
53023
+ }
53024
+ });
53025
+
52650
53026
  // lib/runtime/hyperliquid-market-symbol.ts
52651
53027
  var configuredCaseSensitiveSymbols, isHyperliquidSpotSymbol, normalizeCaseSensitiveSymbols, configureHyperliquidMarketSymbolCasing, canonicalBaseSymbol, normalizeHyperliquidMarketSymbol, getHyperliquidMarketDex;
52652
53028
  var init_hyperliquid_market_symbol = __esm({
@@ -54092,7 +54468,7 @@ function getNumber(value, name) {
54092
54468
  }
54093
54469
  assertArgument(false, "invalid numeric value", name || "value", value);
54094
54470
  }
54095
- function toNumber(value) {
54471
+ function toNumber2(value) {
54096
54472
  return getNumber(toBigInt(value));
54097
54473
  }
54098
54474
  function toBeHex(_value, _width) {
@@ -56514,7 +56890,7 @@ var init_abstract_coder = __esm({
56514
56890
  return toBigInt(this.readBytes(WordSize));
56515
56891
  }
56516
56892
  readIndex() {
56517
- return toNumber(this.readBytes(WordSize));
56893
+ return toNumber2(this.readBytes(WordSize));
56518
56894
  }
56519
56895
  };
56520
56896
  }
@@ -63802,8 +64178,8 @@ function split2(name, nf, ef) {
63802
64178
  if (!name) return [];
63803
64179
  init();
63804
64180
  let offset = 0;
63805
- return name.split(STOP_CH).map((label) => {
63806
- let input = explode_cp(label);
64181
+ return name.split(STOP_CH).map((label2) => {
64182
+ let input = explode_cp(label2);
63807
64183
  let info = {
63808
64184
  input,
63809
64185
  offset
@@ -69478,8 +69854,8 @@ var init_contract = __esm({
69478
69854
  }
69479
69855
  const { subs } = getInternal(this);
69480
69856
  let total = 0;
69481
- for (const { listeners: listeners2 } of subs.values()) {
69482
- total += listeners2.length;
69857
+ for (const { listeners: listeners3 } of subs.values()) {
69858
+ total += listeners3.length;
69483
69859
  }
69484
69860
  return total;
69485
69861
  }
@@ -69497,8 +69873,8 @@ var init_contract = __esm({
69497
69873
  }
69498
69874
  const { subs } = getInternal(this);
69499
69875
  let result = [];
69500
- for (const { listeners: listeners2 } of subs.values()) {
69501
- result = result.concat(listeners2.map(({ listener }) => listener));
69876
+ for (const { listeners: listeners3 } of subs.values()) {
69877
+ result = result.concat(listeners3.map(({ listener }) => listener));
69502
69878
  }
69503
69879
  return result;
69504
69880
  }
@@ -72335,8 +72711,8 @@ var init_abstract_provider = __esm({
72335
72711
  return sub.listeners.length;
72336
72712
  }
72337
72713
  let total = 0;
72338
- for (const { listeners: listeners2 } of this.#subs.values()) {
72339
- total += listeners2.length;
72714
+ for (const { listeners: listeners3 } of this.#subs.values()) {
72715
+ total += listeners3.length;
72340
72716
  }
72341
72717
  return total;
72342
72718
  }
@@ -72349,8 +72725,8 @@ var init_abstract_provider = __esm({
72349
72725
  return sub.listeners.map(({ listener }) => listener);
72350
72726
  }
72351
72727
  let result = [];
72352
- for (const { listeners: listeners2 } of this.#subs.values()) {
72353
- result = result.concat(listeners2.map(({ listener }) => listener));
72728
+ for (const { listeners: listeners3 } of this.#subs.values()) {
72729
+ result = result.concat(listeners3.map(({ listener }) => listener));
72354
72730
  }
72355
72731
  return result;
72356
72732
  }
@@ -82951,7 +83327,7 @@ __export(ethers_exports, {
82951
83327
  toBeArray: () => toBeArray,
82952
83328
  toBeHex: () => toBeHex,
82953
83329
  toBigInt: () => toBigInt,
82954
- toNumber: () => toNumber,
83330
+ toNumber: () => toNumber2,
82955
83331
  toQuantity: () => toQuantity,
82956
83332
  toTwos: () => toTwos,
82957
83333
  toUtf8Bytes: () => toUtf8Bytes,
@@ -83169,7 +83545,7 @@ __export(lib_exports, {
83169
83545
  toBeArray: () => toBeArray,
83170
83546
  toBeHex: () => toBeHex,
83171
83547
  toBigInt: () => toBigInt,
83172
- toNumber: () => toNumber,
83548
+ toNumber: () => toNumber2,
83173
83549
  toQuantity: () => toQuantity,
83174
83550
  toTwos: () => toTwos,
83175
83551
  toUtf8Bytes: () => toUtf8Bytes,
@@ -83872,9 +84248,9 @@ var init_hyperliquid_signer = __esm({
83872
84248
  { name: "connectionId", type: "bytes32" }
83873
84249
  ]
83874
84250
  };
83875
- assertUint64 = (value, label) => {
84251
+ assertUint64 = (value, label2) => {
83876
84252
  if (!Number.isInteger(value) || value < 0) {
83877
- throw new Error(`${label} must be a non-negative integer.`);
84253
+ throw new Error(`${label2} must be a non-negative integer.`);
83878
84254
  }
83879
84255
  };
83880
84256
  concatBytes3 = (...parts) => {
@@ -83899,10 +84275,10 @@ var init_hyperliquid_signer = __esm({
83899
84275
  }
83900
84276
  return bytes3;
83901
84277
  };
83902
- hexToBytes2 = (value, label) => {
84278
+ hexToBytes2 = (value, label2) => {
83903
84279
  const normalized = value.trim().replace(/^0x/i, "");
83904
84280
  if (!normalized || normalized.length % 2 !== 0 || /[^0-9a-f]/i.test(normalized)) {
83905
- throw new Error(`Invalid ${label}.`);
84281
+ throw new Error(`Invalid ${label2}.`);
83906
84282
  }
83907
84283
  const bytes3 = new Uint8Array(normalized.length / 2);
83908
84284
  for (let index = 0; index < normalized.length; index += 2) {
@@ -84131,19 +84507,21 @@ var init_hyperliquid_signer = __esm({
84131
84507
  });
84132
84508
 
84133
84509
  // lib/runtime/hyperliquid-client.ts
84134
- var HyperliquidExchangeRejectionError, durableMutationAuthorityArgs, assertFreshPreparedExecutionContext, normalizeSymbol, HYPERLIQUID_NONCE_STORAGE_KEY, HYPERLIQUID_NONCE_LOCK_NAME, lastGeneratedNonce, readBrowserStoredHyperliquidNonce, writeBrowserStoredHyperliquidNonce, browserNonceCoordinator, allocateHyperliquidNonce, generateHyperliquidNonce, normalizeDecimalString, HYPERLIQUID_PRICE_SIGNIFICANT_FIGURES, HYPERLIQUID_INTEGER_PRICE_MINIMUM, shortestDecimalParts, formatHyperliquidPerpPriceGrid, formatHyperliquidTriggerPriceWire, hyperliquidOrderPriceRounding, formatHyperliquidOrderPriceWire, countSignificantDigits, countDecimalPlaces, validateHyperliquidPerpPriceWire, validateHyperliquidSizeWire, formatSizeWire, inferApiUrl, inferIsTestnet, postHyperliquidInfoRead, postHyperliquidInfo, fetchBrowserHyperliquidInfoPayload, fetchBrowserHyperliquidInfoText, toFiniteNumber, OPEN_ORDER_NUMERIC_STRING, OPEN_ORDER_ID_STRING, parseCanonicalOpenOrderId, parseOpenOrderFiniteNumber, parseOpenOrderPositiveNumber, parseOpenOrderPresentationNumber, readOpenOrderField, firstFiniteNumber, parseFundingUsdcFromInfoPosition, isExchangeIsolatedOnly, computeHip3AssetId, getPerpDexIndexMap, buildAssetInfosFromMetaResponse, requireWalletAddress, assertPositiveInteger, assertPositiveNumber, normalizeOrderSide, normalizeHyperliquidClientOrderId, resolveAssets, findResolvedAsset, normalizeExecutionAssetMetadata, resolveHyperliquidAsset, resolveHyperliquidAssetIndex, decimalUnits, quantizeHyperliquidOrderSizeValue, finitePositive, readDevicePrivateExecutionContext, quantizeHyperliquidOrderSize, fetchBrowserAvailableAssets, fetchBrowserAllMids, fetchBrowserSymbolMidPrice, fetchBrowserOpenOrders, parseCanonicalHyperliquidOrderStatusEvidence, fetchBrowserOrderStatusEvidence, fetchBrowserUserRole, fetchBrowserAccountState, fetchBrowserPosition, buildHyperliquidOrderWire, buildHyperliquidTriggerOrderWire, signHyperliquidPayload, signHyperliquidPreparedAction, buildHyperliquidCancelPayload, buildHyperliquidUpdateLeveragePayload, buildHyperliquidMarketOrderPayload, buildHyperliquidLimitOrderPayload, buildHyperliquidTriggerOrderPayload, buildHyperliquidOrderBatchPayload, buildHyperliquidModifyOrderPayload, extractHyperliquidExchangeError, resolveHyperliquidTriggerAcknowledgements, isDefinitiveHyperliquidExchangeResponse, extractDefinitiveHyperliquidOrderId, CANONICAL_POSITIVE_DECIMAL_PATTERN, extractCanonicalHyperliquidOrderStateAcknowledgement, extractCanonicalHyperliquidOrderStateAcknowledgements, extractCanonicalHyperliquidFilledOrderAcknowledgement, extractDefinitiveHyperliquidOrderMemberResults, buildHyperliquidOrderDiagnostics, loadClientExecutionReferenceEvidence, sha256Hex, extractSignedPayloadClientOrderId, extractSignedPayloadMutationCorrelation, assertDurableMutationMatchesSignedAction, exchangeRejectionReason, exchangeActionPriority, submitHyperliquidExchangePayload;
84510
+ var HyperliquidExchangeRejectionError, durableMutationAuthorityArgs, assertFreshPreparedExecutionContext, normalizeSymbol, HYPERLIQUID_NONCE_STORAGE_KEY, HYPERLIQUID_NONCE_LOCK_NAME, lastGeneratedNonce, readBrowserStoredHyperliquidNonce, writeBrowserStoredHyperliquidNonce, browserNonceCoordinator, allocateHyperliquidNonce, hostNonceAllocator, allocateActionNonce, generateHyperliquidNonce, normalizeDecimalString, HYPERLIQUID_PRICE_SIGNIFICANT_FIGURES, HYPERLIQUID_INTEGER_PRICE_MINIMUM, shortestDecimalParts, formatHyperliquidPerpPriceGrid, formatHyperliquidTriggerPriceWire, hyperliquidOrderPriceRounding, formatHyperliquidOrderPriceWire, countSignificantDigits, countDecimalPlaces, validateHyperliquidPerpPriceWire, validateHyperliquidSizeWire, formatSizeWire, inferApiUrl, inferIsTestnet, postHyperliquidInfoRead, postHyperliquidInfo, fetchBrowserHyperliquidInfoPayload, fetchBrowserHyperliquidInfoText, toFiniteNumber, OPEN_ORDER_NUMERIC_STRING, OPEN_ORDER_ID_STRING, parseCanonicalOpenOrderId, parseOpenOrderFiniteNumber, parseOpenOrderPositiveNumber, parseOpenOrderPresentationNumber, readOpenOrderField, firstFiniteNumber, parseFundingUsdcFromInfoPosition, isExchangeIsolatedOnly, computeHip3AssetId, getPerpDexIndexMap, buildAssetInfosFromMetaResponse, requireWalletAddress, assertPositiveInteger, assertPositiveNumber, normalizeOrderSide, normalizeHyperliquidClientOrderId, resolveAssets, findResolvedAsset, normalizeExecutionAssetMetadata, resolveHyperliquidAsset, resolveHyperliquidAssetIndex, decimalUnits, quantizeHyperliquidOrderSizeValue, hyperliquidSignedSizeMatchesRequestedSize, finitePositive, readDevicePrivateExecutionContext, quantizeHyperliquidOrderSize, fetchBrowserAvailableAssets, fetchBrowserAllMids, fetchBrowserSymbolMidPrice, fetchBrowserOpenOrders, parseCanonicalHyperliquidOrderStatusEvidence, fetchBrowserOrderStatusEvidence, fetchBrowserUserRole, fetchBrowserAccountState, fetchBrowserPosition, buildHyperliquidOrderWire, buildHyperliquidTriggerOrderWire, signHyperliquidPayload, signHyperliquidPreparedAction, buildHyperliquidCancelPayload, buildHyperliquidUpdateLeveragePayload, buildHyperliquidMarketOrderPayload, buildHyperliquidLimitOrderPayload, buildHyperliquidTriggerOrderPayload, buildHyperliquidOrderBatchPayload, buildHyperliquidModifyOrderPayload, extractHyperliquidExchangeError, resolveHyperliquidTriggerAcknowledgements, isDefinitiveHyperliquidExchangeResponse, extractDefinitiveHyperliquidOrderId, CANONICAL_POSITIVE_DECIMAL_PATTERN, extractCanonicalHyperliquidOrderStateAcknowledgement, extractCanonicalHyperliquidOrderStateAcknowledgements, extractCanonicalHyperliquidFilledOrderAcknowledgement, extractDefinitiveHyperliquidOrderMemberResults, buildHyperliquidOrderDiagnostics, loadClientExecutionReferenceEvidence, sha256Hex, extractSignedPayloadClientOrderId, extractSignedPayloadMutationCorrelation, assertDurableMutationMatchesSignedAction, exchangeRejectionReason, exchangeActionPriority, submitHyperliquidExchangePayload;
84135
84511
  var init_hyperliquid_client = __esm({
84136
84512
  "lib/runtime/hyperliquid-client.ts"() {
84137
84513
  "use strict";
84138
84514
  init_define_VTX_EXO_POLICY();
84139
84515
  init_define_VTX_GROK_POLICY();
84140
84516
  init_define_VTX_PI_MODEL_POLICY();
84517
+ init_hyperliquid_account_contract();
84141
84518
  init_order_identity();
84142
84519
  init_hyperliquid_order_status();
84143
84520
  init_runtime_policy_generated2();
84144
84521
  init_hyperliquid_public_read();
84145
84522
  init_api2();
84146
84523
  init_abort();
84524
+ init_hyperliquid_exchange_rejections();
84147
84525
  init_hyperliquid_market_symbol();
84148
84526
  init_network_debug();
84149
84527
  init_runtime_redaction();
@@ -84238,6 +84616,14 @@ var init_hyperliquid_client = __esm({
84238
84616
  coordinator.write?.(lastGeneratedNonce);
84239
84617
  return lastGeneratedNonce;
84240
84618
  };
84619
+ hostNonceAllocator = null;
84620
+ allocateActionNonce = async (input, apiUrl) => {
84621
+ if (hostNonceAllocator) {
84622
+ if (!input.signingKey) throw new Error("A fleet-allocated Hyperliquid nonce needs the action's signing key.");
84623
+ return hostNonceAllocator({ signingKey: input.signingKey, apiUrl });
84624
+ }
84625
+ return generateHyperliquidNonce(input.nonceCoordinator);
84626
+ };
84241
84627
  generateHyperliquidNonce = async (coordinator) => {
84242
84628
  const resolvedCoordinator = coordinator ?? browserNonceCoordinator();
84243
84629
  if (resolvedCoordinator.withLock) {
@@ -84363,12 +84749,12 @@ var init_hyperliquid_client = __esm({
84363
84749
  throw new Error("Missing client runtime Hyperliquid API URL.");
84364
84750
  };
84365
84751
  inferIsTestnet = (apiUrl) => apiUrl.toLowerCase().includes("testnet");
84366
- postHyperliquidInfoRead = async (input, body, label, includeBodyText = false) => {
84752
+ postHyperliquidInfoRead = async (input, body, label2, includeBodyText = false) => {
84367
84753
  const apiUrl = inferApiUrl(input);
84368
84754
  const requestUrl = `${apiUrl.replace(/\/$/, "")}/info`;
84369
84755
  const trace = startRuntimeNetworkDebugTrace({
84370
84756
  kind: "exchange",
84371
- label,
84757
+ label: label2,
84372
84758
  method: "POST",
84373
84759
  url: requestUrl
84374
84760
  });
@@ -84386,10 +84772,10 @@ var init_hyperliquid_client = __esm({
84386
84772
  throw sanitizedError;
84387
84773
  }
84388
84774
  };
84389
- postHyperliquidInfo = async (input, body, label) => (await postHyperliquidInfoRead(input, body, label)).payload;
84390
- fetchBrowserHyperliquidInfoPayload = (input, body, label) => postHyperliquidInfo(input, body, label);
84391
- fetchBrowserHyperliquidInfoText = async (input, body, label) => {
84392
- const { bodyText } = await postHyperliquidInfoRead(input, body, label, true);
84775
+ postHyperliquidInfo = async (input, body, label2) => (await postHyperliquidInfoRead(input, body, label2)).payload;
84776
+ fetchBrowserHyperliquidInfoPayload = (input, body, label2) => postHyperliquidInfo(input, body, label2);
84777
+ fetchBrowserHyperliquidInfoText = async (input, body, label2) => {
84778
+ const { bodyText } = await postHyperliquidInfoRead(input, body, label2, true);
84393
84779
  if (typeof bodyText !== "string") throw new Error("Hyperliquid returned no response text.");
84394
84780
  return bodyText;
84395
84781
  };
@@ -84559,14 +84945,14 @@ var init_hyperliquid_client = __esm({
84559
84945
  }
84560
84946
  return normalized;
84561
84947
  };
84562
- assertPositiveInteger = (value, label) => {
84948
+ assertPositiveInteger = (value, label2) => {
84563
84949
  if (!Number.isInteger(value) || value <= 0) {
84564
- throw new Error(`${label} must be a positive integer.`);
84950
+ throw new Error(`${label2} must be a positive integer.`);
84565
84951
  }
84566
84952
  };
84567
- assertPositiveNumber = (value, label) => {
84953
+ assertPositiveNumber = (value, label2) => {
84568
84954
  if (!Number.isFinite(value) || value <= 0) {
84569
- throw new Error(`${label} must be a positive number.`);
84955
+ throw new Error(`${label2} must be a positive number.`);
84570
84956
  }
84571
84957
  };
84572
84958
  normalizeOrderSide = (value) => {
@@ -84703,6 +85089,17 @@ var init_hyperliquid_client = __esm({
84703
85089
  const units = decimalUnits(value, decimals, roundingMode);
84704
85090
  return units / 10 ** decimals;
84705
85091
  };
85092
+ hyperliquidSignedSizeMatchesRequestedSize = (requestedSize, sizeDecimals, signedSize) => {
85093
+ if (!Number.isFinite(requestedSize) || requestedSize <= 0 || !Number.isFinite(signedSize) || signedSize <= 0 || !Number.isInteger(sizeDecimals) || sizeDecimals < 0) {
85094
+ return false;
85095
+ }
85096
+ const tolerance = 10 ** -(sizeDecimals + 4);
85097
+ const candidates = [
85098
+ quantizeHyperliquidOrderSizeValue(requestedSize, sizeDecimals, "nearest"),
85099
+ Number(requestedSize.toFixed(sizeDecimals))
85100
+ ];
85101
+ return candidates.some((candidate) => candidate > 0 && Math.abs(candidate - signedSize) <= tolerance);
85102
+ };
84706
85103
  finitePositive = (value) => {
84707
85104
  const parsed = Number(value);
84708
85105
  return typeof value !== "boolean" && value !== null && value !== "" && Number.isFinite(parsed) && parsed > 0 ? parsed : null;
@@ -85211,7 +85608,7 @@ var init_hyperliquid_client = __esm({
85211
85608
  accountSummary: {
85212
85609
  account_value: accountValue,
85213
85610
  total_margin_used: totalMarginUsed,
85214
- available_margin: Math.max(0, accountValue - totalMarginUsed),
85611
+ available_margin: canonicalAvailableMargin(accountValue, totalMarginUsed),
85215
85612
  withdrawable,
85216
85613
  cross_margin_ratio: accountValue > 0 ? maintenanceMargin / accountValue : 0,
85217
85614
  maintenance_margin: maintenanceMargin,
@@ -85232,7 +85629,8 @@ var init_hyperliquid_client = __esm({
85232
85629
  buildHyperliquidOrderWire = async (input, tif, price, priceKind) => {
85233
85630
  const { asset_index: assetIndex, sz_decimals: sizeDecimals } = await resolveHyperliquidAsset(input);
85234
85631
  const side = normalizeOrderSide(input.side);
85235
- const roundedSize = Number(input.size.toFixed(sizeDecimals));
85632
+ assertPositiveNumber(input.size, "Hyperliquid order size");
85633
+ const roundedSize = quantizeHyperliquidOrderSizeValue(input.size, sizeDecimals, "nearest");
85236
85634
  assertPositiveNumber(roundedSize, "Hyperliquid order size");
85237
85635
  const priceWire = formatHyperliquidOrderPriceWire(price, sizeDecimals, side, priceKind);
85238
85636
  const sizeWire = formatSizeWire(roundedSize, sizeDecimals);
@@ -85260,7 +85658,7 @@ var init_hyperliquid_client = __esm({
85260
85658
  assertPositiveNumber(input.limitPrice, "Hyperliquid trigger limit price");
85261
85659
  const { asset_index: assetIndex, sz_decimals: sizeDecimals } = await resolveHyperliquidAsset(input);
85262
85660
  const side = normalizeOrderSide(input.side);
85263
- const roundedSize = Number(input.size.toFixed(sizeDecimals));
85661
+ const roundedSize = quantizeHyperliquidOrderSizeValue(input.size, sizeDecimals, "nearest");
85264
85662
  assertPositiveNumber(roundedSize, "Hyperliquid trigger order size");
85265
85663
  const triggerPriceWire = formatHyperliquidTriggerPriceWire(input.triggerPrice, sizeDecimals);
85266
85664
  const limitPriceWire = input.limitPrice === input.triggerPrice ? triggerPriceWire : formatHyperliquidOrderPriceWire(input.limitPrice, sizeDecimals, side, "limit");
@@ -85296,7 +85694,7 @@ var init_hyperliquid_client = __esm({
85296
85694
  signHyperliquidPayload = async (input, action) => {
85297
85695
  const apiUrl = inferApiUrl(input);
85298
85696
  const { signHyperliquidAction: signHyperliquidAction2 } = await Promise.resolve().then(() => (init_hyperliquid_signer(), hyperliquid_signer_exports));
85299
- const nonce = input.nonce ?? await generateHyperliquidNonce(input.nonceCoordinator);
85697
+ const nonce = input.nonce ?? await allocateActionNonce(input, apiUrl);
85300
85698
  const expiresAfter = input.expiresAfterSeconds == null ? null : nonce + input.expiresAfterSeconds * 1e3;
85301
85699
  const signed2 = await signHyperliquidAction2({
85302
85700
  privateKey: input.signingKey,
@@ -85320,7 +85718,7 @@ var init_hyperliquid_client = __esm({
85320
85718
  }
85321
85719
  const apiUrl = inferApiUrl(input);
85322
85720
  const { signHyperliquidAction: signHyperliquidAction2 } = await Promise.resolve().then(() => (init_hyperliquid_signer(), hyperliquid_signer_exports));
85323
- const nonce = await generateHyperliquidNonce(input.nonceCoordinator);
85721
+ const nonce = await allocateActionNonce(input, apiUrl);
85324
85722
  const expiresAfter = nonce + input.expiresAfterSeconds * 1e3;
85325
85723
  const signed2 = await signHyperliquidAction2({
85326
85724
  privateKey: input.signingKey,
@@ -85348,7 +85746,7 @@ var init_hyperliquid_client = __esm({
85348
85746
  const apiUrl = inferApiUrl(input);
85349
85747
  const assetIndex = await resolveHyperliquidAssetIndex(input);
85350
85748
  const { signHyperliquidAction: signHyperliquidAction2 } = await Promise.resolve().then(() => (init_hyperliquid_signer(), hyperliquid_signer_exports));
85351
- const nonce = input.nonce ?? await generateHyperliquidNonce(input.nonceCoordinator);
85749
+ const nonce = input.nonce ?? await allocateActionNonce(input, apiUrl);
85352
85750
  const expiresAfter = input.expiresAfterSeconds == null ? null : nonce + input.expiresAfterSeconds * 1e3;
85353
85751
  const action = clientOrderId != null ? {
85354
85752
  type: "cancelByCloid",
@@ -85378,7 +85776,7 @@ var init_hyperliquid_client = __esm({
85378
85776
  const apiUrl = inferApiUrl(input);
85379
85777
  const asset = await resolveHyperliquidAsset(input);
85380
85778
  const { signHyperliquidAction: signHyperliquidAction2 } = await Promise.resolve().then(() => (init_hyperliquid_signer(), hyperliquid_signer_exports));
85381
- const nonce = input.nonce ?? await generateHyperliquidNonce(input.nonceCoordinator);
85779
+ const nonce = input.nonce ?? await allocateActionNonce(input, apiUrl);
85382
85780
  const expiresAfter = input.expiresAfterSeconds == null ? null : nonce + input.expiresAfterSeconds * 1e3;
85383
85781
  const leverage = Math.max(1, Math.min(asset.max_leverage, Math.trunc(input.leverage)));
85384
85782
  const isCross = isExchangeIsolatedOnly(asset) ? false : input.isCross !== false;
@@ -86205,7 +86603,7 @@ var init_hyperliquid_client = __esm({
86205
86603
  try {
86206
86604
  recoveryPermit = await admitHyperliquidExchangeRequest(apiUrl, input.payload.action, input.signal, input.publicRequestPriority);
86207
86605
  assertFreshPreparedExecutionContext(input.preparedContext);
86208
- assertHyperliquidRecoveryDispatch(apiUrl, recoveryPermit, "exchange");
86606
+ await validateHyperliquidRecoveryDispatch(apiUrl, recoveryPermit, "exchange");
86209
86607
  } catch (preparedContextError) {
86210
86608
  await settleDurableMutation("rejected");
86211
86609
  if (isAbortLikeError(preparedContextError) || preparedContextError instanceof HyperliquidPublicReadError) throw preparedContextError;
@@ -86296,6 +86694,7 @@ var init_hyperliquid_client = __esm({
86296
86694
  }
86297
86695
  const exchangeError = extractHyperliquidExchangeError(payload);
86298
86696
  if (exchangeError) {
86697
+ emitHyperliquidExchangeRejection(String(exchangeError.message ?? ""));
86299
86698
  await settleDurableMutation(
86300
86699
  exchangeError.partial ? "transport_ambiguous" : "rejected",
86301
86700
  null,
@@ -86914,7 +87313,8 @@ async function postClientRuntime(path, profileId, body, options) {
86914
87313
  const payload = await response.json();
86915
87314
  persistRuntimeLeaseFromPayload(payload);
86916
87315
  persistRuntimeLeaseStatusMetadata(numericProfileId, payload);
86917
- if (path === "/runtime/session/stop" && payload?.status !== "preserved") {
87316
+ const stopStatus = payload?.status;
87317
+ if (path === "/runtime/session/stop" && stopStatus !== "preserved" && stopStatus !== "not_owner") {
86918
87318
  clearPersistedClientRuntimeServerLease(numericProfileId);
86919
87319
  }
86920
87320
  trace.completeSuccess(response.status);
@@ -87268,6 +87668,81 @@ async function adoptClientProtectiveLegs(profileId, payload, requiredRuntimeSess
87268
87668
  { ...authority.requestOptions, signal: payload.signal }
87269
87669
  );
87270
87670
  }
87671
+ async function claimServerHostExecution(profileId, payload, signal, requiredRuntimeSessionId, explicitRuntimeLeaseToken, explicitRuntimeDeviceId, explicitSchedulerInstanceId) {
87672
+ const authority = resolveClientExchangeMutationAuthority(
87673
+ profileId,
87674
+ requiredRuntimeSessionId,
87675
+ explicitRuntimeLeaseToken,
87676
+ explicitRuntimeDeviceId,
87677
+ explicitSchedulerInstanceId
87678
+ );
87679
+ return postClientRuntime(
87680
+ "/runtime/execution-claim",
87681
+ authority.numericProfileId,
87682
+ JSON.stringify({
87683
+ ...clientExchangeMutationAuthorityBody(authority),
87684
+ cycle_id: payload.cycleId ?? null,
87685
+ symbol: payload.symbol,
87686
+ intent: payload.intent,
87687
+ side: payload.side,
87688
+ requested_units: payload.requestedUnits,
87689
+ reduce_only: payload.reduceOnly,
87690
+ margin_mode: payload.marginMode ?? null,
87691
+ last_price: payload.lastPrice
87692
+ }),
87693
+ { ...authority.requestOptions, signal }
87694
+ );
87695
+ }
87696
+ async function recordServerHostExecutionAudit(profileId, payload, signal, requiredRuntimeSessionId, explicitRuntimeLeaseToken, explicitRuntimeDeviceId, explicitSchedulerInstanceId) {
87697
+ const authority = resolveClientExchangeMutationAuthority(
87698
+ profileId,
87699
+ requiredRuntimeSessionId,
87700
+ explicitRuntimeLeaseToken,
87701
+ explicitRuntimeDeviceId,
87702
+ explicitSchedulerInstanceId
87703
+ );
87704
+ return postClientRuntime(
87705
+ "/runtime/execution-audit",
87706
+ authority.numericProfileId,
87707
+ JSON.stringify({
87708
+ ...clientExchangeMutationAuthorityBody(authority),
87709
+ audit_id: payload.auditId,
87710
+ cycle_id: payload.cycleId ?? null,
87711
+ symbol: payload.symbol,
87712
+ intent: payload.intent,
87713
+ side: payload.side,
87714
+ requested_units: payload.requestedUnits,
87715
+ reduce_only: payload.reduceOnly,
87716
+ last_price: payload.lastPrice,
87717
+ status: payload.status,
87718
+ reason_code: payload.reasonCode,
87719
+ order_id: payload.orderId ?? null
87720
+ }),
87721
+ { ...authority.requestOptions, signal }
87722
+ );
87723
+ }
87724
+ async function checkServerHostExecutionCapacity(profileId, plans, signal, requiredRuntimeSessionId, explicitRuntimeLeaseToken, explicitRuntimeDeviceId, explicitSchedulerInstanceId) {
87725
+ const authority = resolveClientExchangeMutationAuthority(
87726
+ profileId,
87727
+ requiredRuntimeSessionId,
87728
+ explicitRuntimeLeaseToken,
87729
+ explicitRuntimeDeviceId,
87730
+ explicitSchedulerInstanceId
87731
+ );
87732
+ const answer = await postClientRuntime(
87733
+ "/runtime/execution-capacity",
87734
+ authority.numericProfileId,
87735
+ JSON.stringify({
87736
+ ...clientExchangeMutationAuthorityBody(authority),
87737
+ plans
87738
+ }),
87739
+ { ...authority.requestOptions, signal }
87740
+ );
87741
+ if (!Array.isArray(answer.results) || answer.results.length !== plans.length) {
87742
+ throw new Error("Server execution capacity answered a different number of plans.");
87743
+ }
87744
+ return answer.results;
87745
+ }
87271
87746
  async function reportClientRuntimeExchangeAttemptTransition(profileId, mutationId, payload, signal, requiredRuntimeSessionId, explicitRuntimeLeaseToken, explicitRuntimeDeviceId, explicitSchedulerInstanceId) {
87272
87747
  const authority = resolveClientExchangeMutationAuthority(
87273
87748
  profileId,
@@ -87396,6 +87871,9 @@ async function sendClientRuntimeHeartbeat(profileId, payload, options) {
87396
87871
  if (payload.isExecuting !== void 0) {
87397
87872
  extras.is_executing = payload.isExecuting;
87398
87873
  }
87874
+ if (payload.analyzingObservedAtMs !== void 0) {
87875
+ extras.analyzing_observed_at_ms = payload.analyzingObservedAtMs;
87876
+ }
87399
87877
  return postClientRuntime(
87400
87878
  "/runtime/heartbeat",
87401
87879
  profileId,
@@ -87845,41 +88323,6 @@ async function fetchClientRuntimeKillswitchContext(profileId, payload, options)
87845
88323
  options
87846
88324
  );
87847
88325
  }
87848
- async function getClientRuntimeStatuses(profileIds, signal, options) {
87849
- const normalizedProfileIds = [...new Set(profileIds)].sort((left, right) => left - right);
87850
- const params = new URLSearchParams();
87851
- for (const profileId of normalizedProfileIds) {
87852
- params.append("profile_id", String(profileId));
87853
- }
87854
- const viewer = resolveClientRuntimeStatusViewerHeaders(options?.runtimeViewer);
87855
- const requestKey = `client-runtime-statuses:${normalizedProfileIds.join(",")}${viewer.requestKey}`;
87856
- return shareClientGetRequest(requestKey, async () => {
87857
- const headers = { ...viewer.headers };
87858
- const response = await apiFetch(
87859
- `${getApiUrl()}/trading/ai/runtime/statuses?${params.toString()}`,
87860
- {
87861
- method: "GET",
87862
- credentials: "include",
87863
- cache: "no-store",
87864
- headers,
87865
- // Without the browser's shared request a caller's abort ends the request too.
87866
- signal: typeof window === "undefined" ? signal : void 0
87867
- }
87868
- );
87869
- if (!response.ok) {
87870
- const error48 = await createRuntimeRequestError(response, "Failed to fetch client runtime statuses");
87871
- throw error48;
87872
- }
87873
- return response.json();
87874
- }, {
87875
- signal,
87876
- shareAbortScoped: true,
87877
- // A host-named read decides whether that host claims: it must see an
87878
- // intent change made a moment ago, so it shares only an in-flight read
87879
- // and never reuses a finished one.
87880
- ttlMs: viewer.requestKey ? 0 : CLIENT_RUNTIME_STATUSES_DEDUPE_TTL_MS
87881
- });
87882
- }
87883
88326
  var clientRuntimeSecretHeaders, preferencesWriteQueue, getCopyTradingRuntimeLeaseToken, getCopyTradingSchedulerInstanceId, resolveClientRuntimeStatusViewerHeaders, CLIENT_SUPPORTED_PROMPT_CONTRACT_VERSION, CLIENT_PROTECTION_CAPABILITY_VERSION, completedDeferredClientRuntimeStops, rememberCompletedDeferredClientRuntimeStop, consumeCompletedDeferredClientRuntimeStop, toNumericProfileId, buildClientRuntimeBody, shouldAttachRuntimeIdempotencyKey, attachRuntimeLeaseHeader, attachRuntimeSchedulerInstanceHeader, resolveRuntimeLeaseToken, resolveSchedulerBoundRuntimeLeaseToken, persistRuntimeLeaseFromPayload, persistRuntimeLeaseStatusMetadata, shouldClearPersistedRuntimeLeaseOnError, createRuntimeRequestError, parseClientRuntimeRequestContext, recordRuntimeAuthFailureBreadcrumb, classifyRuntimeRequestError, annotateRuntimeRequestError, parseRuntimeRetryAfterMs, isRetryableClientRuntimeStatus, shouldRetryClientRuntimeHttpStatus, isRuntimeIdempotencyInProgressError, isRetryableRuntimeConflictError, resolveClientRuntimeRetryDelayMs, reconcilePendingClientExchangeMutation, CLIENT_RUNTIME_CLAIM_EXCHANGE_MUTATION_WAIT_CODES, CLIENT_EXCHANGE_AUTHORITY_DEVICE_KEY, CLIENT_EXCHANGE_AUTHORITY_SESSION_KEY, getOrCreateClientExchangeAuthorityIdentity, resolveClientExchangeMutationAuthority, clientExchangeMutationAuthorityBody, isPendingClientExchangeMutationError, isMissingActiveClientExchangeMutationError, clientExchangeMutationReconciliationTimers, MAX_CLIENT_EXCHANGE_RECONCILIATION_DELAY_MS, CLIENT_EXCHANGE_RECONCILIATION_CLOCK_SKEW_MS, ClientExchangeMutationRebuildRequiredError, isTerminalClientExchangeMutation, clientExchangeMutationReconciliationKey, clearScheduledClientExchangeMutationReconciliation, snapshotClientExchangeMutationReconciliationAuthority, completeClientExchangeMutationReconciliation, protectiveBoundClientExchangeMutations, rememberProtectiveBoundClientExchangeMutation, isProtectiveBoundClientExchangeMutation, postClientExchangeMutationReconciliation, scheduleClientExchangeMutationReconciliation;
87884
88327
  var init_api2 = __esm({
87885
88328
  "lib/api.ts"() {
@@ -87990,6 +88433,9 @@ var init_api2 = __esm({
87990
88433
  if (payload.authoritativeIntent !== void 0) {
87991
88434
  body.authoritative_intent = payload.authoritativeIntent;
87992
88435
  }
88436
+ if (payload.requireCurrentOwner !== void 0) {
88437
+ body.require_current_owner = payload.requireCurrentOwner;
88438
+ }
87993
88439
  if (payload.analysisRunId !== void 0) {
87994
88440
  body.analysis_run_id = payload.analysisRunId ?? null;
87995
88441
  }
@@ -88291,14 +88737,14 @@ var init_api2 = __esm({
88291
88737
  ]);
88292
88738
  CLIENT_EXCHANGE_AUTHORITY_DEVICE_KEY = "vtx_client_exchange_authority_device_v1";
88293
88739
  CLIENT_EXCHANGE_AUTHORITY_SESSION_KEY = "vtx_client_exchange_authority_session_v1";
88294
- getOrCreateClientExchangeAuthorityIdentity = (storage3, key, label) => {
88740
+ getOrCreateClientExchangeAuthorityIdentity = (storage3, key, label2) => {
88295
88741
  const existing = String(storage3.getItem(key) || "").trim().toLowerCase();
88296
88742
  if (/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/.test(existing)) {
88297
88743
  return existing;
88298
88744
  }
88299
88745
  const generated = globalThis.crypto?.randomUUID?.().toLowerCase();
88300
88746
  if (!generated || !/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/.test(generated)) {
88301
- throw new Error(`Secure ${label} generation is unavailable.`);
88747
+ throw new Error(`Secure ${label2} generation is unavailable.`);
88302
88748
  }
88303
88749
  storage3.setItem(key, generated);
88304
88750
  return generated;
@@ -88517,7 +88963,7 @@ var init_api2 = __esm({
88517
88963
  });
88518
88964
 
88519
88965
  // lib/runtime/hyperliquid-signer-binding.ts
88520
- var DeviceSigningKeyError, DEVICE_SIGNING_KEY_FAILURES, getDeviceSigningKeyFailure, normalizeAddress, extractRoleAddress, assertBrowserHyperliquidSignerWalletBinding;
88966
+ var DeviceSigningKeyError, DEVICE_SIGNING_KEY_FAILURES, getDeviceSigningKeyFailure, normalizeAddress, extractRoleAddress, signerBindingCache, validatedSignerBindings, SIGNER_NOT_FOUND, invalidateHyperliquidSignerBindingAfterRejection, assertBrowserHyperliquidSignerWalletBinding;
88521
88967
  var init_hyperliquid_signer_binding = __esm({
88522
88968
  "lib/runtime/hyperliquid-signer-binding.ts"() {
88523
88969
  "use strict";
@@ -88525,6 +88971,7 @@ var init_hyperliquid_signer_binding = __esm({
88525
88971
  init_define_VTX_GROK_POLICY();
88526
88972
  init_define_VTX_PI_MODEL_POLICY();
88527
88973
  init_hyperliquid_client();
88974
+ init_hyperliquid_exchange_rejections();
88528
88975
  init_error_surface_contract();
88529
88976
  DeviceSigningKeyError = class extends RuntimeExecutionError {
88530
88977
  constructor(message, reasonCode, deviceSigningKeyFailure) {
@@ -88540,10 +88987,10 @@ var init_hyperliquid_signer_binding = __esm({
88540
88987
  const failure2 = error48?.deviceSigningKeyFailure;
88541
88988
  return typeof failure2 === "string" && DEVICE_SIGNING_KEY_FAILURES.has(failure2) ? failure2 : null;
88542
88989
  };
88543
- normalizeAddress = (value, label) => {
88990
+ normalizeAddress = (value, label2) => {
88544
88991
  const normalized = String(value || "").trim().toLowerCase();
88545
88992
  if (!/^0x[0-9a-f]{40}$/.test(normalized)) {
88546
- throw new Error(`Invalid Hyperliquid ${label}.`);
88993
+ throw new Error(`Invalid Hyperliquid ${label2}.`);
88547
88994
  }
88548
88995
  return normalized;
88549
88996
  };
@@ -88561,6 +89008,22 @@ var init_hyperliquid_signer_binding = __esm({
88561
89008
  }
88562
89009
  return null;
88563
89010
  };
89011
+ signerBindingCache = null;
89012
+ validatedSignerBindings = /* @__PURE__ */ new Map();
89013
+ SIGNER_NOT_FOUND = /^(?:L1 error:\s*)?User or API Wallet\s+(0x[0-9a-fA-F]{40})\s+does not exist\.?$/i;
89014
+ invalidateHyperliquidSignerBindingAfterRejection = async (message) => {
89015
+ const match = SIGNER_NOT_FOUND.exec(String(message ?? "").trim());
89016
+ if (!match || !signerBindingCache) return;
89017
+ const signerAddress = match[1].toLowerCase();
89018
+ for (const [key, entry] of [...validatedSignerBindings.entries()]) {
89019
+ if (entry.binding.signerAddress !== signerAddress) continue;
89020
+ validatedSignerBindings.delete(key);
89021
+ await signerBindingCache.invalidate(entry.binding.walletAddress, signerAddress).catch(() => void 0);
89022
+ }
89023
+ };
89024
+ onHyperliquidExchangeRejection((message) => {
89025
+ void invalidateHyperliquidSignerBindingAfterRejection(message);
89026
+ });
88564
89027
  assertBrowserHyperliquidSignerWalletBinding = async (input) => {
88565
89028
  let walletAddress;
88566
89029
  try {
@@ -88586,6 +89049,24 @@ var init_hyperliquid_signer_binding = __esm({
88586
89049
  "device_signing_key_invalid"
88587
89050
  );
88588
89051
  }
89052
+ const cacheKey = `${walletAddress}:${signerAddress}`;
89053
+ let generation = null;
89054
+ if (signerBindingCache) {
89055
+ try {
89056
+ generation = await signerBindingCache.generation(walletAddress, signerAddress);
89057
+ } catch {
89058
+ throw new RuntimeExecutionError(
89059
+ "VTX could not verify the cached signing-key authorization because its coordination state was temporarily unavailable. The signing key was not rejected.",
89060
+ "signer_validation_unavailable"
89061
+ );
89062
+ }
89063
+ const cached2 = validatedSignerBindings.get(cacheKey);
89064
+ if (cached2 && cached2.generation === generation) return cached2.binding;
89065
+ }
89066
+ const remember = (binding) => {
89067
+ if (signerBindingCache && generation !== null) validatedSignerBindings.set(cacheKey, { generation, binding });
89068
+ return binding;
89069
+ };
88589
89070
  let roleResponse;
88590
89071
  try {
88591
89072
  roleResponse = await fetchBrowserUserRole({
@@ -88603,11 +89084,11 @@ var init_hyperliquid_signer_binding = __esm({
88603
89084
  }
88604
89085
  const signerRole = String(roleResponse.role || "").trim().toLowerCase();
88605
89086
  if (signerRole === "user" && signerAddress === walletAddress) {
88606
- return {
89087
+ return remember({
88607
89088
  signerAddress,
88608
89089
  walletAddress,
88609
89090
  signerRole: "user"
88610
- };
89091
+ });
88611
89092
  }
88612
89093
  if (signerRole === "agent") {
88613
89094
  const masterWallet = extractRoleAddress(
@@ -88615,11 +89096,11 @@ var init_hyperliquid_signer_binding = __esm({
88615
89096
  ["user"]
88616
89097
  );
88617
89098
  if (masterWallet === walletAddress) {
88618
- return {
89099
+ return remember({
88619
89100
  signerAddress,
88620
89101
  walletAddress,
88621
89102
  signerRole: "agent"
88622
- };
89103
+ });
88623
89104
  }
88624
89105
  }
88625
89106
  throw new DeviceSigningKeyError(
@@ -89575,9 +90056,9 @@ var init_browser_trading = __esm({
89575
90056
  }
89576
90057
  return profileId;
89577
90058
  };
89578
- requirePositiveNumber = (value, label) => {
90059
+ requirePositiveNumber = (value, label2) => {
89579
90060
  if (!Number.isFinite(value) || value <= 0) {
89580
- throw new Error(`${label} must be a positive number.`);
90061
+ throw new Error(`${label2} must be a positive number.`);
89581
90062
  }
89582
90063
  };
89583
90064
  assertFreshPreparedContext = (preparedContext) => {
@@ -90050,287 +90531,6 @@ var init_browser_trading = __esm({
90050
90531
  }
90051
90532
  });
90052
90533
 
90053
- // lib/runtime/hyperliquid-account-mode-contract.ts
90054
- function resolveExactHyperliquidAccountMode(userAbstraction, ...dexEvidence) {
90055
- const mode = extractCanonicalHyperliquidAccountMode(userAbstraction);
90056
- if (!mode) {
90057
- throw new Error("Exact Hyperliquid userAbstraction response is invalid.");
90058
- }
90059
- if (mode !== "default" && mode !== "disabled") return mode;
90060
- if (dexEvidence.length < 1) {
90061
- throw new Error("Exact Hyperliquid userDexAbstraction response is unavailable.");
90062
- }
90063
- return parseExactDexAbstractionEnabled(dexEvidence[0]) ? "dexAbstraction" : mode;
90064
- }
90065
- var UNIFIED_MODE_ALIASES, CANONICAL_ACCOUNT_MODE_ALIASES, EXACT_MODE_KEYS, EXACT_MODE_BOOLEAN_KEYS, isExactTruthyFlag, normalizeCanonicalHyperliquidAccountMode, extractCanonicalHyperliquidAccountMode, parseExactDexAbstractionEnabled, normalizeAccountMode, extractAccountModeFromPayload, extractAccountModeCandidateFromPayload, isUnifiedAccountMode;
90066
- var init_hyperliquid_account_mode_contract = __esm({
90067
- "lib/runtime/hyperliquid-account-mode-contract.ts"() {
90068
- "use strict";
90069
- init_define_VTX_EXO_POLICY();
90070
- init_define_VTX_GROK_POLICY();
90071
- init_define_VTX_PI_MODEL_POLICY();
90072
- UNIFIED_MODE_ALIASES = /* @__PURE__ */ new Set([
90073
- "unifiedaccount",
90074
- "dexabstraction",
90075
- "portfoliomargin",
90076
- "unified",
90077
- "pm"
90078
- ]);
90079
- CANONICAL_ACCOUNT_MODE_ALIASES = /* @__PURE__ */ new Map([
90080
- ["default", "default"],
90081
- ["standard", "default"],
90082
- ["classic", "default"],
90083
- ["disabled", "disabled"],
90084
- ["unifiedaccount", "unifiedAccount"],
90085
- ["unified", "unifiedAccount"],
90086
- ["portfoliomargin", "portfolioMargin"],
90087
- ["pm", "portfolioMargin"],
90088
- ["dexabstraction", "dexAbstraction"]
90089
- ]);
90090
- EXACT_MODE_KEYS = [
90091
- "accountMode",
90092
- "account_mode",
90093
- "accountType",
90094
- "account_type",
90095
- "accountUnificationMode",
90096
- "account_unification_mode",
90097
- "abstractionMode",
90098
- "abstraction_mode",
90099
- "abstraction",
90100
- "abstractionState",
90101
- "dexAbstractionState",
90102
- "marginMode",
90103
- "state",
90104
- "mode",
90105
- "value"
90106
- ];
90107
- EXACT_MODE_BOOLEAN_KEYS = [
90108
- ["unifiedAccount", ["isUnifiedAccount", "unifiedAccount", "isUnified", "unified", "is_unified_account"]],
90109
- ["portfolioMargin", ["isPortfolioMargin", "portfolioMargin", "isPm", "pm", "is_portfolio_margin"]],
90110
- ["dexAbstraction", ["isDexAbstraction", "dexAbstraction", "dexAbstractionEnabled", "is_dex_abstraction"]],
90111
- ["default", ["isClassic", "classic", "isStandard", "standard"]]
90112
- ];
90113
- isExactTruthyFlag = (value) => value === true || value === 1 || typeof value === "string" && value.trim().toLowerCase() === "true";
90114
- normalizeCanonicalHyperliquidAccountMode = (value) => {
90115
- if (typeof value !== "string") return null;
90116
- const key = value.trim().replace(/[_\s-]/g, "").toLowerCase();
90117
- return CANONICAL_ACCOUNT_MODE_ALIASES.get(key) ?? null;
90118
- };
90119
- extractCanonicalHyperliquidAccountMode = (payload, depth = 0) => {
90120
- if (depth > 6) return null;
90121
- const direct = normalizeCanonicalHyperliquidAccountMode(payload);
90122
- if (direct) return direct;
90123
- if (Array.isArray(payload)) {
90124
- for (const item of payload) {
90125
- const mode = extractCanonicalHyperliquidAccountMode(item, depth + 1);
90126
- if (mode) return mode;
90127
- }
90128
- return null;
90129
- }
90130
- if (!payload || typeof payload !== "object") return null;
90131
- const source = payload;
90132
- for (const key of EXACT_MODE_KEYS) {
90133
- if (!(key in source)) continue;
90134
- const mode = normalizeCanonicalHyperliquidAccountMode(source[key]);
90135
- if (mode) return mode;
90136
- }
90137
- for (const [mode, keys] of EXACT_MODE_BOOLEAN_KEYS) {
90138
- for (const key of keys) {
90139
- if (isExactTruthyFlag(source[key])) return mode;
90140
- }
90141
- }
90142
- for (const value of Object.values(source)) {
90143
- const mode = extractCanonicalHyperliquidAccountMode(value, depth + 1);
90144
- if (mode) return mode;
90145
- }
90146
- return null;
90147
- };
90148
- parseExactDexAbstractionEnabled = (payload) => {
90149
- if (payload == null || payload === false) return false;
90150
- if (payload === true) return true;
90151
- if (typeof payload === "string") {
90152
- const normalized = payload.trim().toLowerCase();
90153
- if (normalized === "true") return true;
90154
- if (normalized === "false") return false;
90155
- }
90156
- if (typeof payload === "number" && Number.isFinite(payload)) {
90157
- if (payload === 1) return true;
90158
- if (payload === 0) return false;
90159
- }
90160
- throw new Error("Exact Hyperliquid userDexAbstraction response is invalid.");
90161
- };
90162
- normalizeAccountMode = (mode) => String(mode ?? "").trim().toLowerCase().replace(/[_\s-]/g, "");
90163
- extractAccountModeFromPayload = (payload) => {
90164
- const candidate = extractAccountModeCandidateFromPayload(payload);
90165
- if (candidate && candidate.trim().length > 0) {
90166
- return candidate;
90167
- }
90168
- return "default";
90169
- };
90170
- extractAccountModeCandidateFromPayload = (payload) => {
90171
- if (!payload || typeof payload !== "object") {
90172
- return null;
90173
- }
90174
- const source = payload;
90175
- if ("accountMode" in source) {
90176
- return String(source.accountMode ?? "");
90177
- }
90178
- if ("account_mode" in source) {
90179
- return String(source.account_mode ?? "");
90180
- }
90181
- return null;
90182
- };
90183
- isUnifiedAccountMode = (mode) => UNIFIED_MODE_ALIASES.has(normalizeAccountMode(mode));
90184
- }
90185
- });
90186
-
90187
- // lib/runtime/hyperliquid-account-contract.ts
90188
- var toNumber2, firstNumber, roundTo, STABLE_SPOT_SYMBOLS, spotBalanceValueUsd, normalizeSpotBalancesFromInfoResponse, sumBalanceHoldUsd, sumPositionUnrealizedPnl, resolveTotalUnrealizedPnl, approximatelyEqualAccountValues, buildAccountSummaryFromInfoResponses;
90189
- var init_hyperliquid_account_contract = __esm({
90190
- "lib/runtime/hyperliquid-account-contract.ts"() {
90191
- "use strict";
90192
- init_define_VTX_EXO_POLICY();
90193
- init_define_VTX_GROK_POLICY();
90194
- init_define_VTX_PI_MODEL_POLICY();
90195
- init_hyperliquid_account_mode_contract();
90196
- toNumber2 = (value, fallback = 0) => {
90197
- const numeric2 = Number(value);
90198
- return Number.isFinite(numeric2) ? numeric2 : fallback;
90199
- };
90200
- firstNumber = (values, fallback = 0) => {
90201
- for (const value of values) {
90202
- if (value == null || typeof value === "string" && value.trim() === "") {
90203
- continue;
90204
- }
90205
- const numeric2 = Number(value);
90206
- if (Number.isFinite(numeric2)) {
90207
- return numeric2;
90208
- }
90209
- }
90210
- return fallback;
90211
- };
90212
- roundTo = (value, digits = 12) => Number(value.toFixed(digits));
90213
- STABLE_SPOT_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USD", "USDT"]);
90214
- spotBalanceValueUsd = (balance, coin, total) => {
90215
- const explicit = firstNumber([balance?.value_usd, balance?.usdValue, balance?.usdcValue], Number.NaN);
90216
- if (Number.isFinite(explicit)) {
90217
- return explicit;
90218
- }
90219
- return STABLE_SPOT_SYMBOLS.has(coin) ? total : 0;
90220
- };
90221
- normalizeSpotBalancesFromInfoResponse = (spotResponse) => {
90222
- const containers = [];
90223
- if (spotResponse && typeof spotResponse === "object") {
90224
- containers.push(spotResponse);
90225
- const keys = ["spotState", "userState", "clearinghouseState", "state", "data"];
90226
- for (const key of keys) {
90227
- if (spotResponse[key] && typeof spotResponse[key] === "object") {
90228
- containers.push(spotResponse[key]);
90229
- }
90230
- }
90231
- }
90232
- const firstWithBalances = containers.find((container) => Array.isArray(container?.balances));
90233
- const balances = Array.isArray(firstWithBalances?.balances) ? firstWithBalances.balances : [];
90234
- return balances.map((balance) => {
90235
- const coin = String(balance?.coin ?? "").toUpperCase();
90236
- const total = toNumber2(balance?.total, 0);
90237
- const hold = toNumber2(balance?.hold, 0);
90238
- const available = roundTo(Math.max(0, total - hold));
90239
- const value_usd = spotBalanceValueUsd(balance, coin, total);
90240
- return {
90241
- coin,
90242
- total,
90243
- hold,
90244
- available,
90245
- value_usd
90246
- };
90247
- }).filter((balance) => balance.coin.length > 0);
90248
- };
90249
- sumBalanceHoldUsd = (balances) => balances.reduce((acc, balance) => {
90250
- const hold = toNumber2(balance.hold, 0);
90251
- const total = toNumber2(balance.total, 0);
90252
- const valueUsd = toNumber2(balance.value_usd, 0);
90253
- if (hold <= 0 || total <= 0 || valueUsd <= 0) {
90254
- return acc;
90255
- }
90256
- return acc + Math.max(0, valueUsd * (hold / total));
90257
- }, 0);
90258
- sumPositionUnrealizedPnl = (perpsResponse) => {
90259
- const assetPositions = Array.isArray(perpsResponse?.assetPositions) ? perpsResponse.assetPositions : [];
90260
- return assetPositions.reduce((total, entry) => {
90261
- const unrealizedPnl = toNumber2(entry?.position?.unrealizedPnl, 0);
90262
- return total + unrealizedPnl;
90263
- }, 0);
90264
- };
90265
- resolveTotalUnrealizedPnl = (perpsResponse) => {
90266
- const topLevel = toNumber2(perpsResponse?.totalUnrealizedPnl, 0);
90267
- const positionTotal = sumPositionUnrealizedPnl(perpsResponse);
90268
- if (Math.abs(topLevel) < 1e-12 && Math.abs(positionTotal) >= 1e-12) {
90269
- return positionTotal;
90270
- }
90271
- return topLevel;
90272
- };
90273
- approximatelyEqualAccountValues = (left, right, absoluteToleranceUsd, relativeTolerance) => {
90274
- const reference = Math.max(Math.abs(left), Math.abs(right), 1);
90275
- return Math.abs(left - right) <= Math.max(
90276
- absoluteToleranceUsd,
90277
- reference * relativeTolerance
90278
- );
90279
- };
90280
- buildAccountSummaryFromInfoResponses = (perpsResponse, spotResponse, options = {}) => {
90281
- const marginSummary = perpsResponse?.marginSummary ?? {};
90282
- const perpsAccountValue = toNumber2(marginSummary?.accountValue, 0);
90283
- const total_margin_used = toNumber2(marginSummary?.totalMarginUsed, 0);
90284
- const totalNtlPos = toNumber2(marginSummary?.totalNtlPos, 0);
90285
- const withdrawable = toNumber2(perpsResponse?.withdrawable, 0);
90286
- const maintenance_margin = firstNumber([
90287
- perpsResponse?.crossMaintenanceMarginUsed,
90288
- marginSummary?.crossMaintenanceMarginUsed,
90289
- marginSummary?.totalMaintenanceMargin
90290
- ], 0);
90291
- const balances = normalizeSpotBalancesFromInfoResponse(spotResponse);
90292
- const spotPortfolioValue = balances.reduce((acc, balance) => acc + toNumber2(balance.value_usd, 0), 0);
90293
- const balancesHoldUsd = sumBalanceHoldUsd(balances);
90294
- const account_mode = extractAccountModeFromPayload(perpsResponse);
90295
- const crossMarginSummary = perpsResponse?.crossMarginSummary && typeof perpsResponse.crossMarginSummary === "object" ? perpsResponse.crossMarginSummary : null;
90296
- const crossMarginAccountValue = crossMarginSummary ? toNumber2(crossMarginSummary.accountValue, 0) : 0;
90297
- const normalizedMode = String(account_mode || "").trim().toLowerCase().replace(/[_\s-]/g, "");
90298
- const isAmbiguousDefaultMode = normalizedMode.length === 0 || normalizedMode === "default";
90299
- const spotDominatesMinTotalUsd = Number(options.spotDominatesMinTotalUsd ?? options.spot_dominates_min_total_usd);
90300
- const spotDominatesPerpsMultiplier = Number(options.spotDominatesPerpsMultiplier ?? options.spot_dominates_perps_multiplier);
90301
- const mirroredAccountValueAbsoluteToleranceUsd = Number(
90302
- options.mirroredAccountValueAbsoluteToleranceUsd ?? options.mirrored_account_value_absolute_tolerance_usd
90303
- );
90304
- const mirroredAccountValueRelativeTolerance = Number(
90305
- options.mirroredAccountValueRelativeTolerance ?? options.mirrored_account_value_relative_tolerance
90306
- );
90307
- const hasSpotDominanceThresholds = Number.isFinite(spotDominatesMinTotalUsd) && spotDominatesMinTotalUsd >= 0 && Number.isFinite(spotDominatesPerpsMultiplier) && spotDominatesPerpsMultiplier >= 0;
90308
- const hasMirroredAccountValueTolerances = Number.isFinite(mirroredAccountValueAbsoluteToleranceUsd) && mirroredAccountValueAbsoluteToleranceUsd >= 0 && Number.isFinite(mirroredAccountValueRelativeTolerance) && mirroredAccountValueRelativeTolerance >= 0;
90309
- const spotDominatesPerps = hasSpotDominanceThresholds && spotPortfolioValue >= spotDominatesMinTotalUsd && (perpsAccountValue <= 0 || spotPortfolioValue >= perpsAccountValue * spotDominatesPerpsMultiplier);
90310
- const ambiguousDefaultLooksUnified = isAmbiguousDefaultMode && spotDominatesPerps;
90311
- const ambiguousDefaultSpotAlreadyIncludesPerps = ambiguousDefaultLooksUnified && perpsAccountValue > 0 && balancesHoldUsd > 0 && hasMirroredAccountValueTolerances && approximatelyEqualAccountValues(
90312
- balancesHoldUsd,
90313
- perpsAccountValue,
90314
- mirroredAccountValueAbsoluteToleranceUsd,
90315
- mirroredAccountValueRelativeTolerance
90316
- );
90317
- const account_value = isUnifiedAccountMode(account_mode) && crossMarginSummary !== null && crossMarginAccountValue > 0 ? crossMarginAccountValue + spotPortfolioValue : ambiguousDefaultLooksUnified && !ambiguousDefaultSpotAlreadyIncludesPerps ? perpsAccountValue + spotPortfolioValue : Math.max(perpsAccountValue, spotPortfolioValue);
90318
- return {
90319
- account_value,
90320
- total_margin_used,
90321
- available_margin: roundTo(Math.max(0, account_value - total_margin_used)),
90322
- withdrawable,
90323
- cross_margin_ratio: account_value > 0 ? maintenance_margin / account_value : 0,
90324
- maintenance_margin,
90325
- cross_account_leverage: account_value > 0 ? totalNtlPos / account_value : 0,
90326
- total_unrealized_pnl: resolveTotalUnrealizedPnl(perpsResponse),
90327
- account_mode,
90328
- balances
90329
- };
90330
- };
90331
- }
90332
- });
90333
-
90334
90534
  // lib/runtime/hyperliquid-active-asset-contract.ts
90335
90535
  var unwrapActiveAssetPayload, normalizePair, normalizeActiveAssetData;
90336
90536
  var init_hyperliquid_active_asset_contract = __esm({
@@ -90358,15 +90558,15 @@ var init_hyperliquid_active_asset_contract = __esm({
90358
90558
  };
90359
90559
  normalizePair = (raw) => {
90360
90560
  if (Array.isArray(raw) && raw.length >= 2) {
90361
- return [toNumber2(raw[0], 0), toNumber2(raw[1], 0)];
90561
+ return [toNumber(raw[0], 0), toNumber(raw[1], 0)];
90362
90562
  }
90363
90563
  if (raw && typeof raw === "object") {
90364
- const buy = toNumber2(raw.buy ?? raw.long ?? raw.b, 0);
90365
- const sell = toNumber2(raw.sell ?? raw.short ?? raw.s, 0);
90564
+ const buy = toNumber(raw.buy ?? raw.long ?? raw.b, 0);
90565
+ const sell = toNumber(raw.sell ?? raw.short ?? raw.s, 0);
90366
90566
  return [buy, sell];
90367
90567
  }
90368
90568
  if (typeof raw === "string" || typeof raw === "number") {
90369
- const value = toNumber2(raw, 0);
90569
+ const value = toNumber(raw, 0);
90370
90570
  return [value, value];
90371
90571
  }
90372
90572
  return [0, 0];
@@ -90377,21 +90577,21 @@ var init_hyperliquid_active_asset_contract = __esm({
90377
90577
  const directSell = source.available_to_trade_sell;
90378
90578
  const hasDirectAvailable = directBuy !== void 0 || directSell !== void 0;
90379
90579
  const [pairBuy, pairSell] = normalizePair(source.availableToTrade);
90380
- const available_to_trade_buy = hasDirectAvailable ? toNumber2(directBuy, pairBuy) : pairBuy;
90381
- const available_to_trade_sell = hasDirectAvailable ? toNumber2(directSell, pairSell) : pairSell;
90580
+ const available_to_trade_buy = hasDirectAvailable ? toNumber(directBuy, pairBuy) : pairBuy;
90581
+ const available_to_trade_sell = hasDirectAvailable ? toNumber(directSell, pairSell) : pairSell;
90382
90582
  const directMaxBuy = source.max_trade_size_buy;
90383
90583
  const directMaxSell = source.max_trade_size_sell;
90384
90584
  const hasDirectMax = directMaxBuy !== void 0 || directMaxSell !== void 0;
90385
90585
  const [pairMaxBuy, pairMaxSell] = normalizePair(source.maxTradeSzs);
90386
- const max_trade_size_buy = hasDirectMax ? toNumber2(directMaxBuy, pairMaxBuy) : pairMaxBuy;
90387
- const max_trade_size_sell = hasDirectMax ? toNumber2(directMaxSell, pairMaxSell) : pairMaxSell;
90586
+ const max_trade_size_buy = hasDirectMax ? toNumber(directMaxBuy, pairMaxBuy) : pairMaxBuy;
90587
+ const max_trade_size_sell = hasDirectMax ? toNumber(directMaxSell, pairMaxSell) : pairMaxSell;
90388
90588
  return {
90389
90589
  symbol: String(source.coin || symbol2),
90390
90590
  available_to_trade_buy,
90391
90591
  available_to_trade_sell,
90392
90592
  max_trade_size_buy,
90393
90593
  max_trade_size_sell,
90394
- mark_price: toNumber2(source.markPx ?? source.mark_price, 0)
90594
+ mark_price: toNumber(source.markPx ?? source.mark_price, 0)
90395
90595
  };
90396
90596
  };
90397
90597
  }
@@ -90583,12 +90783,7 @@ var init_server_prompt_rescue_account_contract = __esm({
90583
90783
  };
90584
90784
  const accountValue = computeEffectiveAccountValue(formulaInput);
90585
90785
  const maintenanceMargin = toNumber3(perps.crossMaintenanceMarginUsed);
90586
- const calculatedAvailable = accountValue - totalMarginUsed;
90587
- const availableMargin = maintenanceMargin > 0 ? Math.max(
90588
- calculatedAvailable,
90589
- Math.max(0, accountValue - 4 * maintenanceMargin),
90590
- withdrawable
90591
- ) : Math.max(calculatedAvailable, withdrawable);
90786
+ const availableMargin = canonicalAvailableMargin(accountValue, totalMarginUsed);
90592
90787
  let totalNotional = 0;
90593
90788
  let totalUnrealizedPnl = 0;
90594
90789
  const positions = Array.isArray(perps.assetPositions) ? perps.assetPositions : [];
@@ -91046,18 +91241,18 @@ var init_hyperliquid_account_state_adapter = __esm({
91046
91241
  }
91047
91242
  return parsed;
91048
91243
  };
91049
- requirePromptClearinghouseState = (response, label) => {
91244
+ requirePromptClearinghouseState = (response, label2) => {
91050
91245
  if (!response || typeof response !== "object" || Array.isArray(response)) {
91051
- throw new Error(`Malformed ${label} clearinghouseState response for client runtime prompt context.`);
91246
+ throw new Error(`Malformed ${label2} clearinghouseState response for client runtime prompt context.`);
91052
91247
  }
91053
91248
  const marginSummary = response.marginSummary;
91054
91249
  if (!marginSummary || typeof marginSummary !== "object" || Array.isArray(marginSummary)) {
91055
- throw new Error(`${label} clearinghouseState is missing marginSummary for client runtime prompt context.`);
91250
+ throw new Error(`${label2} clearinghouseState is missing marginSummary for client runtime prompt context.`);
91056
91251
  }
91057
- requireFinitePromptMetric(marginSummary.accountValue, `${label}.marginSummary.accountValue`);
91058
- requireFinitePromptMetric(marginSummary.totalMarginUsed, `${label}.marginSummary.totalMarginUsed`);
91059
- requireFinitePromptMetric(marginSummary.totalNtlPos, `${label}.marginSummary.totalNtlPos`);
91060
- requireFinitePromptMetric(response.withdrawable, `${label}.withdrawable`);
91252
+ requireFinitePromptMetric(marginSummary.accountValue, `${label2}.marginSummary.accountValue`);
91253
+ requireFinitePromptMetric(marginSummary.totalMarginUsed, `${label2}.marginSummary.totalMarginUsed`);
91254
+ requireFinitePromptMetric(marginSummary.totalNtlPos, `${label2}.marginSummary.totalNtlPos`);
91255
+ requireFinitePromptMetric(response.withdrawable, `${label2}.withdrawable`);
91061
91256
  const maintenanceCandidates = [
91062
91257
  response.crossMaintenanceMarginUsed,
91063
91258
  marginSummary.crossMaintenanceMarginUsed,
@@ -91066,33 +91261,33 @@ var init_hyperliquid_account_state_adapter = __esm({
91066
91261
  const maintenance = maintenanceCandidates.find(
91067
91262
  (value) => value != null && !(typeof value === "string" && value.trim() === "")
91068
91263
  );
91069
- requireFinitePromptMetric(maintenance, `${label}.maintenanceMargin`);
91264
+ requireFinitePromptMetric(maintenance, `${label2}.maintenanceMargin`);
91070
91265
  if (!Array.isArray(response.assetPositions)) {
91071
- throw new Error(`${label} clearinghouseState is missing assetPositions for client runtime prompt context.`);
91266
+ throw new Error(`${label2} clearinghouseState is missing assetPositions for client runtime prompt context.`);
91072
91267
  }
91073
91268
  response.assetPositions.forEach((entry, index) => {
91074
91269
  const position = entry?.position;
91075
91270
  if (!position || typeof position !== "object" || Array.isArray(position)) {
91076
- throw new Error(`${label}.assetPositions[${index}] is malformed for client runtime prompt context.`);
91271
+ throw new Error(`${label2}.assetPositions[${index}] is malformed for client runtime prompt context.`);
91077
91272
  }
91078
91273
  if (!String(entry?.coin || position.coin || "").trim()) {
91079
- throw new Error(`${label}.assetPositions[${index}] is missing its symbol for client runtime prompt context.`);
91274
+ throw new Error(`${label2}.assetPositions[${index}] is missing its symbol for client runtime prompt context.`);
91080
91275
  }
91081
91276
  const size = requireFinitePromptMetric(
91082
91277
  position.szi,
91083
- `${label}.assetPositions[${index}].position.szi`
91278
+ `${label2}.assetPositions[${index}].position.szi`
91084
91279
  );
91085
91280
  if (size !== 0) {
91086
91281
  const entryPrice = requireFinitePromptMetric(
91087
91282
  position.entryPx,
91088
- `${label}.assetPositions[${index}].position.entryPx`
91283
+ `${label2}.assetPositions[${index}].position.entryPx`
91089
91284
  );
91090
91285
  if (entryPrice <= 0) {
91091
- throw new Error(`${label}.assetPositions[${index}] has a nonpositive entry price for client runtime prompt context.`);
91286
+ throw new Error(`${label2}.assetPositions[${index}] has a nonpositive entry price for client runtime prompt context.`);
91092
91287
  }
91093
91288
  requireFinitePromptMetric(
91094
91289
  position.unrealizedPnl,
91095
- `${label}.assetPositions[${index}].position.unrealizedPnl`
91290
+ `${label2}.assetPositions[${index}].position.unrealizedPnl`
91096
91291
  );
91097
91292
  }
91098
91293
  });
@@ -91116,9 +91311,12 @@ var init_hyperliquid_account_state_adapter = __esm({
91116
91311
  perpDexConfig.allowed_dexes.map(normalizePerpDexName).filter((value) => value.length > 0)
91117
91312
  ));
91118
91313
  };
91119
- listPerpDexsForAccountState = (config2, selectedDex, aggregatePerpDexs) => {
91314
+ listPerpDexsForAccountState = (config2, selectedDex, aggregatePerpDexs, perpDexScope) => {
91120
91315
  if (aggregatePerpDexs) {
91121
- return listConfiguredPerpDexs(config2);
91316
+ const configured = listConfiguredPerpDexs(config2);
91317
+ if (!perpDexScope) return configured;
91318
+ const scope = new Set(perpDexScope.map(normalizePerpDexName));
91319
+ return configured.filter((dex) => scope.has(dex));
91122
91320
  }
91123
91321
  return selectedDex ? [selectedDex] : [];
91124
91322
  };
@@ -91326,7 +91524,7 @@ var init_hyperliquid_account_state_adapter = __esm({
91326
91524
  const includeEffectiveTakerRate = input.includeEffectiveTakerRate === true;
91327
91525
  const includeExactAccountMode = input.includeExactAccountMode === true;
91328
91526
  const includeActiveAssetData = input.includeActiveAssetData !== false;
91329
- const dexNames = listPerpDexsForAccountState(input.config, dex, aggregatePerpDexs);
91527
+ const dexNames = listPerpDexsForAccountState(input.config, dex, aggregatePerpDexs, input.perpDexScope);
91330
91528
  if (scopeAccountSummaryToSelectedDex && dex && !dexNames.includes(dex)) {
91331
91529
  dexNames.push(dex);
91332
91530
  }
@@ -91624,8 +91822,7 @@ var init_hyperliquid_account_state_adapter = __esm({
91624
91822
  ])
91625
91823
  }, {}, "disabled", accountSummaryOptions);
91626
91824
  const accountValue = Number(payload.marginSummary.accountValue);
91627
- const calculatedAvailable = accountValue - summary.total_margin_used;
91628
- const availableMargin = summary.maintenance_margin > 0 ? Math.max(calculatedAvailable, Math.max(0, accountValue - 4 * summary.maintenance_margin), summary.withdrawable) : Math.max(calculatedAvailable, summary.withdrawable);
91825
+ const availableMargin = canonicalAvailableMargin(accountValue, summary.total_margin_used);
91629
91826
  return {
91630
91827
  dex,
91631
91828
  account_value: accountValue,
@@ -91930,7 +92127,7 @@ var init_protective_evidence = __esm({
91930
92127
  const perDex = /* @__PURE__ */ new Map();
91931
92128
  const scope = `${String(input.apiUrl ?? "")}|${wallet.toLowerCase()}`;
91932
92129
  const shareable = batch.every((item) => item.kind !== "fills" && item.shareable);
91933
- const dexRead = (key, body, label) => {
92130
+ const dexRead = (key, body, label2) => {
91934
92131
  let read = perDex.get(key);
91935
92132
  if (read) return read;
91936
92133
  const sharedKey = `${scope}|${key}`;
@@ -91940,7 +92137,7 @@ var init_protective_evidence = __esm({
91940
92137
  startedAtMonotonicMs = Math.min(startedAtMonotonicMs, shared.startedAtMonotonicMs);
91941
92138
  read = shared.read;
91942
92139
  } else {
91943
- read = readOrNull(() => fetchBrowserHyperliquidInfoPayload(input, body, label));
92140
+ read = readOrNull(() => fetchBrowserHyperliquidInfoPayload(input, body, label2));
91944
92141
  const entry = { startedAtMonotonicMs: now, read };
91945
92142
  sharedAccountReads.set(sharedKey, entry);
91946
92143
  void read.then((value) => {
@@ -92117,19 +92314,19 @@ var init_protective_actions = __esm({
92117
92314
  "local_safety_block"
92118
92315
  );
92119
92316
  };
92120
- exactKeys = (value, keys, label) => {
92121
- if (!value || typeof value !== "object" || Array.isArray(value)) fail2(`${label} is not an object.`);
92317
+ exactKeys = (value, keys, label2) => {
92318
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail2(`${label2} is not an object.`);
92122
92319
  const record5 = value;
92123
92320
  const present = Object.keys(record5).sort();
92124
- if (present.join(",") !== [...keys].sort().join(",")) fail2(`${label} has unexpected fields.`);
92321
+ if (present.join(",") !== [...keys].sort().join(",")) fail2(`${label2} has unexpected fields.`);
92125
92322
  return record5;
92126
92323
  };
92127
- nonNegativeInteger = (value, label) => {
92128
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) fail2(`${label} is invalid.`);
92324
+ nonNegativeInteger = (value, label2) => {
92325
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) fail2(`${label2} is invalid.`);
92129
92326
  return value;
92130
92327
  };
92131
- decimalString = (value, label) => {
92132
- if (typeof value !== "string" || !DECIMAL.test(value) || Number(value) <= 0) fail2(`${label} is invalid.`);
92328
+ decimalString = (value, label2) => {
92329
+ if (typeof value !== "string" || !DECIMAL.test(value) || Number(value) <= 0) fail2(`${label2} is invalid.`);
92133
92330
  return value;
92134
92331
  };
92135
92332
  canonicalTriggerOrder = (value) => {
@@ -92439,6 +92636,119 @@ var init_protective_actions = __esm({
92439
92636
  }
92440
92637
  });
92441
92638
 
92639
+ // lib/runtime/bounded-lock.ts
92640
+ var webLocks, inProcessTails, abortReason, waitBounded, withInProcessLock, withBoundedExclusiveLock;
92641
+ var init_bounded_lock = __esm({
92642
+ "lib/runtime/bounded-lock.ts"() {
92643
+ "use strict";
92644
+ init_define_VTX_EXO_POLICY();
92645
+ init_define_VTX_GROK_POLICY();
92646
+ init_define_VTX_PI_MODEL_POLICY();
92647
+ webLocks = () => {
92648
+ const locks = typeof navigator === "undefined" ? void 0 : navigator.locks;
92649
+ return locks && typeof locks.request === "function" ? locks : null;
92650
+ };
92651
+ inProcessTails = /* @__PURE__ */ new Map();
92652
+ abortReason = (signal) => signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted.", "AbortError");
92653
+ waitBounded = (gate, waitMs, signal) => new Promise((resolve14, reject) => {
92654
+ if (signal?.aborted) {
92655
+ reject(abortReason(signal));
92656
+ return;
92657
+ }
92658
+ const finish = () => {
92659
+ clearTimeout(timer);
92660
+ signal?.removeEventListener("abort", onAbort);
92661
+ };
92662
+ const onAbort = () => {
92663
+ finish();
92664
+ reject(abortReason(signal));
92665
+ };
92666
+ const timer = setTimeout(() => {
92667
+ finish();
92668
+ resolve14();
92669
+ }, waitMs);
92670
+ signal?.addEventListener("abort", onAbort, { once: true });
92671
+ void gate.then(() => {
92672
+ finish();
92673
+ resolve14();
92674
+ });
92675
+ });
92676
+ withInProcessLock = async (name, waitMs, work, signal) => {
92677
+ const previous = inProcessTails.get(name) ?? Promise.resolve();
92678
+ let release;
92679
+ const released = new Promise((resolve14) => {
92680
+ release = resolve14;
92681
+ });
92682
+ const tail = previous.then(() => released);
92683
+ inProcessTails.set(name, tail);
92684
+ try {
92685
+ await waitBounded(previous, waitMs, signal);
92686
+ return await work();
92687
+ } finally {
92688
+ release();
92689
+ if (inProcessTails.get(name) === tail) inProcessTails.delete(name);
92690
+ }
92691
+ };
92692
+ withBoundedExclusiveLock = async (name, waitMs, work, signal) => {
92693
+ if (!Number.isFinite(waitMs) || waitMs <= 0) {
92694
+ throw new Error(`Invalid lock wait bound for ${name}.`);
92695
+ }
92696
+ const locks = webLocks();
92697
+ if (locks == null) return withInProcessLock(name, waitMs, work, signal);
92698
+ const waitTimeout = new AbortController();
92699
+ const timer = setTimeout(() => waitTimeout.abort(), waitMs);
92700
+ const relayAbort = () => waitTimeout.abort();
92701
+ signal?.addEventListener("abort", relayAbort, { once: true });
92702
+ let acquired = false;
92703
+ try {
92704
+ return await locks.request(name, { signal: waitTimeout.signal }, async () => {
92705
+ acquired = true;
92706
+ clearTimeout(timer);
92707
+ signal?.removeEventListener("abort", relayAbort);
92708
+ return work();
92709
+ });
92710
+ } catch (error48) {
92711
+ if (acquired) throw error48;
92712
+ if (signal?.aborted) throw abortReason(signal);
92713
+ if (!waitTimeout.signal.aborted) throw error48;
92714
+ return work();
92715
+ } finally {
92716
+ clearTimeout(timer);
92717
+ signal?.removeEventListener("abort", relayAbort);
92718
+ }
92719
+ };
92720
+ }
92721
+ });
92722
+
92723
+ // lib/api/client-exchange-mutations.ts
92724
+ async function getClientRuntimeExchangeMutation(profileId, mutationId, signal) {
92725
+ const numericProfileId = Number(profileId);
92726
+ const response = await apiFetch(
92727
+ `${getApiUrl()}/trading/ai/runtime/exchange-mutations/${encodeURIComponent(String(mutationId).trim().toLowerCase())}`,
92728
+ {
92729
+ method: "GET",
92730
+ credentials: "include",
92731
+ cache: "no-store",
92732
+ headers: getProfileHeaders({}, Number.isSafeInteger(numericProfileId) ? numericProfileId : void 0),
92733
+ signal
92734
+ }
92735
+ );
92736
+ if (response.status === 404) return null;
92737
+ if (!response.ok) {
92738
+ throw new Error(await getErrorMessageFromResponse(response, "Failed to read the exchange mutation"));
92739
+ }
92740
+ return response.json();
92741
+ }
92742
+ var init_client_exchange_mutations = __esm({
92743
+ "lib/api/client-exchange-mutations.ts"() {
92744
+ "use strict";
92745
+ init_define_VTX_EXO_POLICY();
92746
+ init_define_VTX_GROK_POLICY();
92747
+ init_define_VTX_PI_MODEL_POLICY();
92748
+ init_shared();
92749
+ }
92750
+ });
92751
+
92442
92752
  // lib/runtime/execution-guard-contract.ts
92443
92753
  var unitsFromPosition, signedUnitsFromPosition, signedUnitsFromEntryPosition, classifyExecutionIntent, resolveExecutionTarget;
92444
92754
  var init_execution_guard_contract = __esm({
@@ -92724,7 +93034,7 @@ var init_protective_trigger_price = __esm({
92724
93034
  });
92725
93035
 
92726
93036
  // lib/runtime/runtime-execution.ts
92727
- var clientProtectiveMutationAuthority, executionReadPriority, fetchClientRuntimeOpenOrders, _extractOrderResponseStates, createRuntimeId, _extractOrderId, _toExecutionMetadata, _extractClientMutationId, _stableMutationIdentity, trailingStopMemory, protectiveGroupMemory, protectiveGroupReadFailures, windowHandoffCleanupAttempts, clientRuntimeProtectionStorage, resolveClientRuntimeProtectionStorage, setClientRuntimeProtectionStorage, trailingStopStorageKey, readTrackedTrailingStops, writeTrackedTrailingStops, rememberTrailingStop, protectiveGroupStorageKey, readClientProtectiveGroups, writeClientProtectiveGroups, hasClientProtectiveGroupReadFailure, readFreshClientProtectiveGroups, stableProtectionSnapshot, exactClientProtectiveGroupSnapshot, exactClientProtectiveGroupSetSnapshot, replaceExactClientProtectiveGroup, replaceExactClientProtectiveGroupSet, isClientRuntimeLeaseOwnershipError, hasClientRuntimeProtectionState, clearClientRuntimeVetoedProtectiveIntent, rethrowWindowHandoffAfterProtectiveReplacementVeto, canPersistClientProtectiveGroups, removeTrackedTrailingStop, replaceTrackedTrailingStop, replaceProtectiveLeg, replacementJournalEntryOrderId, buildProtectiveReplacementJournalGroup, isProtectiveReplacementJournalGroup, reserveProtectiveLegReplacement, ensureProtectiveReplacementJournalCompanion, buildVetoedProtectiveReplacementCleanup, assertExactProtectiveReplacementDispatchAuthority, transitionProtectiveReplacementAcknowledgement, transitionTerminalProtectiveReplacement, pendingProtectiveReplacementReport, isConfiguredProtectionRequested, protectiveAction, isExactClientPositionTpslPair, deviceOwnsClientProtectiveLegKind, isAdoptionNarrowedPositionTpslLeg, hasAmbiguousClientPositionTpslAuthority, protectiveStableShapeMatches, protectiveLiveShapeMatches, protectiveTerminalShapeMatches, cancelClientProtectiveLeg, buildPendingProtectivePlan, persistPendingClientProtectiveGroup, attachClientProtectivePlan, resolveJournaledAttachmentContinuity, recoverJournaledClientProtectiveAttachments, recoverPendingClientProtectiveGroups, recoverPendingProtectiveReplacements, reconcileClientProtectiveGroups, cancelAllClientProtectiveGroups, tightenClientTrailingStops, throwIfAborted2, sleep2, roundAssetSize, resolvePositionSize, resolveCurrentPositionSize, buildPositionSnapshot, signedRemainingExposureAfterConfirmedReduction, runtimeProtectionAuthority, runtimeProtectionOwners, resolveRuntimeProtectionOwner, drainServerProtectionForRuntime, drainServerProtectionIfOwned, verifyNoLiveClientProtectiveTriggers, clientExecutionMutations, withClientExecutionMutation, withClientProtectiveAdoptionMutation, executeClientRuntimeDecision, maintainClientRuntimeTrailingStops, listPendingClientProtectiveSymbols, listStoredClientProtectiveSymbols, hasUnresolvedClientProtectiveIdentity, resolveClientProtectiveIdentitiesForAdoption, exportClientProtectiveGroupsForAdoption, adoptionGroupKey, applyClientProtectiveAdoptionOutcomes, hasStoredClientProtectiveGroupsForWallet, hasPendingClientProtectiveGroups, recoverManualClientProtectiveGroups, executeClientRuntimeDecisionUnlocked, executeClientCopyTradingIntent, executeClientCopyTradingOrderAction;
93037
+ var clientProtectiveMutationAuthority, executionReadPriority, serverEntryLeverageAlreadyHeld, fetchClientRuntimeOpenOrders, _extractOrderResponseStates, createRuntimeId, _extractOrderId, _toExecutionMetadata, _extractClientMutationId, _stableMutationIdentity, trailingStopMemory, protectiveGroupMemory, protectiveGroupReadFailures, windowHandoffCleanupAttempts, clientRuntimeProtectionStorage, resolveClientRuntimeProtectionStorage, setClientRuntimeProtectionStorage, trailingStopStorageKey, readTrackedTrailingStops, writeTrackedTrailingStops, rememberTrailingStop, protectiveGroupStorageKey, withClientProtectivePlanSetLock, readClientProtectiveGroups, writeClientProtectiveGroups, hasClientProtectiveGroupReadFailure, readFreshClientProtectiveGroups, stableProtectionSnapshot, exactClientProtectiveGroupSnapshot, exactClientProtectiveGroupSetSnapshot, replaceExactClientProtectiveGroup, replaceExactClientProtectiveGroupSet, isClientRuntimeLeaseOwnershipError, hasClientRuntimeProtectionState, clearClientRuntimeVetoedProtectiveIntent, rethrowWindowHandoffAfterProtectiveReplacementVeto, canPersistClientProtectiveGroups, removeTrackedTrailingStop, replaceTrackedTrailingStop, replaceProtectiveLeg, replacementJournalEntryOrderId, buildProtectiveReplacementJournalGroup, isProtectiveReplacementJournalGroup, reserveProtectiveLegReplacement, ensureProtectiveReplacementJournalCompanion, buildVetoedProtectiveReplacementCleanup, assertExactProtectiveReplacementDispatchAuthority, transitionProtectiveReplacementAcknowledgement, transitionTerminalProtectiveReplacement, pendingProtectiveReplacementReport, isConfiguredProtectionRequested, protectiveAction, isExactClientPositionTpslPair, deviceOwnsClientProtectiveLegKind, isAdoptionNarrowedPositionTpslLeg, hasAmbiguousClientPositionTpslAuthority, protectiveStableShapeMatches, protectiveLiveShapeMatches, protectiveTerminalShapeMatches, cancelClientProtectiveLeg, buildPendingProtectivePlan, persistPendingClientProtectiveGroup, attachClientProtectivePlan, resolveJournaledAttachmentContinuity, recoverJournaledClientProtectiveAttachments, readPendingEntryMutation, requireClientRuntimeConfigSeconds, replacePendingEntryRecovery, recoverPendingClientProtectiveGroups, recoverPendingProtectiveReplacements, reconcileClientProtectiveGroups, cancelAllClientProtectiveGroups, tightenClientTrailingStops, throwIfAborted2, sleep2, isDefinitiveEntryRejection, resolveExchangeSizeDecimals, roundAssetSize, resolvePositionSize, resolveCurrentPositionSize, buildPositionSnapshot, signedRemainingExposureAfterConfirmedReduction, runtimeProtectionAuthority, runtimeProtectionOwners, resolveRuntimeProtectionOwner, drainServerProtectionForRuntime, drainServerProtectionIfOwned, verifyNoLiveClientProtectiveTriggers, clientExecutionMutations, withClientExecutionMutation, allocateServerExecutionCapacity, withClientProtectiveAdoptionMutation, executeClientRuntimeDecisionLocked, SERVER_AUDIT_RECORDED_ELSEWHERE, serverExecutionAuditOutcome, executeClientRuntimeDecision, waitForFlipCloseFreshness, maintainClientRuntimeTrailingStops, listPendingClientProtectiveSymbols, listStoredClientProtectiveSymbols, hasUnresolvedClientProtectiveIdentity, resolveClientProtectiveIdentitiesForAdoption, exportClientProtectiveGroupsForAdoption, adoptionGroupKey, applyClientProtectiveAdoptionOutcomes, hasStoredClientProtectiveGroupsForWallet, hasPendingClientProtectiveGroups, recoverManualClientProtectiveGroups, clientRuntimeDecisionSizesOrders, executeClientRuntimeDecisionUnlocked, executeClientCopyTradingIntent, executeClientCopyTradingOrderAction;
92728
93038
  var init_runtime_execution = __esm({
92729
93039
  "lib/runtime/runtime-execution.ts"() {
92730
93040
  "use strict";
@@ -92732,8 +93042,11 @@ var init_runtime_execution = __esm({
92732
93042
  init_define_VTX_GROK_POLICY();
92733
93043
  init_define_VTX_PI_MODEL_POLICY();
92734
93044
  init_order_identity();
93045
+ init_runtime_policy_generated2();
93046
+ init_bounded_lock();
92735
93047
  init_client_runtime_ownership_errors();
92736
93048
  init_api2();
93049
+ init_client_exchange_mutations();
92737
93050
  init_hyperliquid_order_status();
92738
93051
  init_protective_actions();
92739
93052
  init_browser_trading();
@@ -92761,6 +93074,26 @@ var init_runtime_execution = __esm({
92761
93074
  );
92762
93075
  return classified.reduceOnly ? "trading_reduce" : "trading_execute";
92763
93076
  };
93077
+ serverEntryLeverageAlreadyHeld = async (input) => {
93078
+ if (input.executionMode !== "server") return false;
93079
+ const context = input.executionContext;
93080
+ const symbol2 = normalizeHyperliquidMarketSymbol(context.symbol);
93081
+ const wallet = String(context.walletAddress || "").trim();
93082
+ if (!symbol2 || !wallet || !Number.isInteger(context.leverage) || context.leverage <= 0 || !Number.isFinite(context.currentPositionSize) || context.currentPositionSize === 0) return false;
93083
+ let active;
93084
+ try {
93085
+ active = await fetchBrowserHyperliquidInfoPayload(
93086
+ { config: input.config ?? null, signal: input.signal, publicRequestPriority: "trading_reconcile" },
93087
+ { type: "activeAssetData", user: wallet, coin: symbol2 },
93088
+ "hyperliquid.activeAssetData"
93089
+ );
93090
+ } catch {
93091
+ throwIfAborted2(input.signal);
93092
+ return false;
93093
+ }
93094
+ const leverage = active?.leverage;
93095
+ return leverage?.value === context.leverage && leverage?.type === (context.marginMode === "isolated" ? "isolated" : "cross");
93096
+ };
92764
93097
  fetchClientRuntimeOpenOrders = async (request, publicRequestPriority = "trading_reconcile") => {
92765
93098
  const normalizedSymbol = normalizeHyperliquidMarketSymbol(
92766
93099
  request.executionContext.symbol
@@ -92975,6 +93308,12 @@ var init_runtime_execution = __esm({
92975
93308
  ]);
92976
93309
  };
92977
93310
  protectiveGroupStorageKey = (profileId, symbol2) => `vtx:auto-protective-group:${profileId}:${symbol2}`;
93311
+ withClientProtectivePlanSetLock = (profileId, symbol2, work, signal) => withBoundedExclusiveLock(
93312
+ `vtx:protective-plan-set:${protectiveGroupStorageKey(String(profileId).trim(), String(symbol2).trim())}`,
93313
+ runtime_policy_generated_default2.trading.engine.client_runtime.protective_plan_set_lock.wait_seconds * 1e3,
93314
+ work,
93315
+ signal
93316
+ );
92978
93317
  readClientProtectiveGroups = (profileId, symbol2) => {
92979
93318
  const key = protectiveGroupStorageKey(profileId, symbol2);
92980
93319
  const inMemory = protectiveGroupMemory.get(key);
@@ -93068,12 +93407,41 @@ var init_runtime_execution = __esm({
93068
93407
  const pending3 = pendingRecord;
93069
93408
  const stopLoss = pending3.stopLoss;
93070
93409
  const takeProfit = pending3.takeProfit;
93071
- if (typeof pending3.entryMutationId !== "string" || !/^[0-9a-f-]{36}$/.test(pending3.entryMutationId) || typeof pending3.entryClientOrderId !== "string" || !/^0x[0-9a-f]{32}$/.test(pending3.entryClientOrderId) || pending3.entrySide !== "buy" && pending3.entrySide !== "sell" || !Number.isFinite(Number(pending3.requestedSize)) || Number(pending3.requestedSize) <= 0 || stopLoss == null || typeof stopLoss !== "object" || Array.isArray(stopLoss) || takeProfit == null || typeof takeProfit !== "object" || Array.isArray(takeProfit)) {
93410
+ if (typeof pending3.entryMutationId !== "string" || !/^[0-9a-f-]{36}$/.test(pending3.entryMutationId) || typeof pending3.entryClientOrderId !== "string" || !/^0x[0-9a-f]{32}$/.test(pending3.entryClientOrderId) || pending3.entrySide !== "buy" && pending3.entrySide !== "sell" || !Number.isFinite(Number(pending3.requestedSize)) || Number(pending3.requestedSize) <= 0 || pending3.createdAtMs != null && (typeof pending3.createdAtMs !== "number" || !Number.isSafeInteger(pending3.createdAtMs) || pending3.createdAtMs <= 0) || stopLoss == null || typeof stopLoss !== "object" || Array.isArray(stopLoss) || takeProfit == null || typeof takeProfit !== "object" || Array.isArray(takeProfit)) {
93072
93411
  protectiveGroupReadFailures.add(key);
93073
93412
  return [];
93074
93413
  }
93075
93414
  const sl = stopLoss;
93076
93415
  const tp = takeProfit;
93416
+ let recovery;
93417
+ if (pending3.recovery != null) {
93418
+ const recoveryRecord = pending3.recovery;
93419
+ if (typeof recoveryRecord !== "object" || Array.isArray(recoveryRecord)) {
93420
+ protectiveGroupReadFailures.add(key);
93421
+ return [];
93422
+ }
93423
+ const {
93424
+ unknownOidObservedAtMs,
93425
+ unknownOidFirstSeenAtMs,
93426
+ attempts,
93427
+ nextAttemptAtMs,
93428
+ staleReported,
93429
+ entryFilled
93430
+ } = recoveryRecord;
93431
+ const invalidCount = (value, minimum) => value != null && (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum);
93432
+ if (invalidCount(unknownOidObservedAtMs, 1) || invalidCount(unknownOidFirstSeenAtMs, 1) || invalidCount(attempts, 0) || invalidCount(nextAttemptAtMs, 0) || staleReported != null && typeof staleReported !== "boolean" || entryFilled != null && typeof entryFilled !== "boolean") {
93433
+ protectiveGroupReadFailures.add(key);
93434
+ return [];
93435
+ }
93436
+ recovery = {
93437
+ ...typeof unknownOidObservedAtMs === "number" ? { unknownOidObservedAtMs } : {},
93438
+ ...typeof unknownOidFirstSeenAtMs === "number" ? { unknownOidFirstSeenAtMs } : {},
93439
+ ...typeof attempts === "number" ? { attempts } : {},
93440
+ ...typeof nextAttemptAtMs === "number" ? { nextAttemptAtMs } : {},
93441
+ ...typeof staleReported === "boolean" ? { staleReported } : {},
93442
+ ...typeof entryFilled === "boolean" ? { entryFilled } : {}
93443
+ };
93444
+ }
93077
93445
  if (typeof sl.enabled !== "boolean" || sl.mode !== "percent" && sl.mode !== "usdc" || !Number.isFinite(Number(sl.value)) || Number(sl.value) < 0 || typeof sl.isTrailing !== "boolean" || typeof tp.enabled !== "boolean" || tp.mode !== "percent" && tp.mode !== "usdc" || !Number.isFinite(Number(tp.value)) || Number(tp.value) < 0) {
93078
93446
  protectiveGroupReadFailures.add(key);
93079
93447
  return [];
@@ -93083,6 +93451,7 @@ var init_runtime_execution = __esm({
93083
93451
  entryClientOrderId: pending3.entryClientOrderId,
93084
93452
  entrySide: pending3.entrySide,
93085
93453
  requestedSize: Number(pending3.requestedSize),
93454
+ ...typeof pending3.createdAtMs === "number" ? { createdAtMs: pending3.createdAtMs } : {},
93086
93455
  stopLoss: {
93087
93456
  enabled: sl.enabled,
93088
93457
  mode: sl.mode,
@@ -93093,7 +93462,8 @@ var init_runtime_execution = __esm({
93093
93462
  enabled: tp.enabled,
93094
93463
  mode: tp.mode,
93095
93464
  value: Number(tp.value)
93096
- }
93465
+ },
93466
+ ...recovery ? { recovery } : {}
93097
93467
  };
93098
93468
  }
93099
93469
  const attachmentRecord = record5.attachmentJournal;
@@ -93837,6 +94207,7 @@ var init_runtime_execution = __esm({
93837
94207
  };
93838
94208
  buildPendingProtectivePlan = (settings, input) => ({
93839
94209
  ...input,
94210
+ createdAtMs: Date.now(),
93840
94211
  stopLoss: {
93841
94212
  enabled: settings.stopLossEnabled !== false,
93842
94213
  mode: settings.stopLossMode === "usdc" ? "usdc" : "percent",
@@ -94462,7 +94833,7 @@ var init_runtime_execution = __esm({
94462
94833
  }
94463
94834
  return reports;
94464
94835
  };
94465
- resolveJournaledAttachmentContinuity = async (request, group) => {
94836
+ resolveJournaledAttachmentContinuity = async (request, group, options = {}) => {
94466
94837
  const journal = group.attachmentJournal;
94467
94838
  const plan = group.pendingEntry;
94468
94839
  if (journal == null || plan == null) {
@@ -94559,7 +94930,7 @@ var init_runtime_execution = __esm({
94559
94930
  throw new Error("Journaled protective entry chronology is unresolved.");
94560
94931
  }
94561
94932
  const firstEntry = canonicalRows[firstEntryIndex];
94562
- if (expectedSign * firstEntry.startPosition < -positionTolerance(firstEntry.startPosition, 0)) {
94933
+ if (options.allowOppositeStart !== true && expectedSign * firstEntry.startPosition < -positionTolerance(firstEntry.startPosition, 0)) {
94563
94934
  throw new Error("Journaled protective entry start position is directionally unresolved.");
94564
94935
  }
94565
94936
  let expectedExposureEstablished = false;
@@ -94961,20 +95332,117 @@ var init_runtime_execution = __esm({
94961
95332
  }
94962
95333
  return reports;
94963
95334
  };
94964
- recoverPendingClientProtectiveGroups = async (request, pendingGroups) => {
95335
+ readPendingEntryMutation = async (request, plan) => {
95336
+ try {
95337
+ const mutation = await getClientRuntimeExchangeMutation(
95338
+ request.profileId,
95339
+ plan.entryMutationId,
95340
+ request.signal
95341
+ );
95342
+ if (mutation == null) return { kind: "absent" };
95343
+ return mutation.operation_kind === "order" && String(mutation.client_order_id ?? "").trim().toLowerCase() === plan.entryClientOrderId ? { kind: "found", mutation } : { kind: "unavailable" };
95344
+ } catch (error48) {
95345
+ if (request.signal?.aborted) throw error48;
95346
+ return { kind: "unavailable" };
95347
+ }
95348
+ };
95349
+ requireClientRuntimeConfigSeconds = (config2, key) => {
95350
+ const value = Number(config2?.[key]);
95351
+ if (!Number.isFinite(value) || value < 0) {
95352
+ throw new Error(`Missing required client runtime config: ${String(key)}`);
95353
+ }
95354
+ return value;
95355
+ };
95356
+ replacePendingEntryRecovery = (profileId, symbol2, group, recovery) => {
95357
+ const plan = group.pendingEntry;
95358
+ if (plan == null) throw new Error("Pending entry plan is unavailable.");
95359
+ const updated = { ...group, pendingEntry: { ...plan, recovery } };
95360
+ if (!replaceExactClientProtectiveGroup(profileId, symbol2, group, updated)) {
95361
+ throw new Error("Pending entry recovery state could not be saved durably.");
95362
+ }
95363
+ return updated;
95364
+ };
95365
+ recoverPendingClientProtectiveGroups = async (request, pendingGroups, retirements = [], reportsStale = false) => {
94965
95366
  const reports = [];
94966
95367
  const profileId = String(request.profileId).trim();
94967
95368
  const symbol2 = request.executionContext.symbol;
94968
- for (const group of pendingGroups) {
95369
+ const retryInitialMs = requireClientRuntimeConfigSeconds(
95370
+ request.config,
95371
+ "client_pending_entry_retry_initial_seconds"
95372
+ ) * 1e3;
95373
+ const retryMaxMs = Math.max(retryInitialMs, requireClientRuntimeConfigSeconds(
95374
+ request.config,
95375
+ "client_pending_entry_retry_max_seconds"
95376
+ ) * 1e3);
95377
+ const staleAfterAttempts = Math.max(1, Math.trunc(requireClientRuntimeConfigSeconds(
95378
+ request.config,
95379
+ "client_pending_entry_stale_after_attempts"
95380
+ )));
95381
+ for (const storedGroup of pendingGroups) {
95382
+ let group = storedGroup;
94969
95383
  const plan = group.pendingEntry;
94970
95384
  if (plan == null) continue;
95385
+ const waitMs = (plan.recovery?.nextAttemptAtMs ?? 0) - Date.now();
95386
+ if (waitMs > 0 && waitMs <= retryMaxMs) continue;
95387
+ let entryFilled = plan.recovery?.entryFilled === true || group.attachmentJournal != null;
95388
+ const recordUnresolved = (errorMessage) => {
95389
+ const currentRecovery = group.pendingEntry?.recovery ?? plan.recovery;
95390
+ const attempts = (currentRecovery?.attempts ?? 0) + 1;
95391
+ const reachedCap = attempts >= staleAfterAttempts;
95392
+ const reportStale = reachedCap && reportsStale && currentRecovery?.staleReported !== true;
95393
+ try {
95394
+ group = replacePendingEntryRecovery(profileId, symbol2, group, {
95395
+ ...currentRecovery,
95396
+ attempts,
95397
+ nextAttemptAtMs: entryFilled ? Date.now() : Date.now() + Math.min(retryMaxMs, retryInitialMs * 2 ** Math.min(30, attempts - 1)),
95398
+ ...entryFilled ? { entryFilled: true } : {},
95399
+ ...reportStale || currentRecovery?.staleReported === true ? { staleReported: true } : {}
95400
+ });
95401
+ } catch {
95402
+ reports.push({
95403
+ order_id: plan.entryClientOrderId,
95404
+ client_mutation_id: plan.entryMutationId,
95405
+ symbol: symbol2,
95406
+ action: "PROTECTIVE_RECONCILIATION",
95407
+ status: "failed",
95408
+ execution_metadata: { reason_code: "protective_lifecycle_persistence_failed" }
95409
+ });
95410
+ }
95411
+ if (errorMessage == null && !reportStale) return;
95412
+ if (reachedCap && !reportStale) return;
95413
+ reports.push({
95414
+ order_id: plan.entryClientOrderId,
95415
+ client_mutation_id: plan.entryMutationId,
95416
+ symbol: symbol2,
95417
+ action: "PROTECTIVE_RECONCILIATION",
95418
+ status: "failed",
95419
+ execution_metadata: {
95420
+ reason_code: reportStale ? "pending_protective_entry_stale" : "pending_protective_entry_unresolved",
95421
+ attempts,
95422
+ ...errorMessage ? { error: errorMessage } : {}
95423
+ }
95424
+ });
95425
+ };
95426
+ const recordIfStillPending = () => {
95427
+ const stored = readFreshClientProtectiveGroups(profileId, symbol2);
95428
+ const snapshot = exactClientProtectiveGroupSnapshot(group);
95429
+ if (stored.some((candidate) => exactClientProtectiveGroupSnapshot(candidate) === snapshot)) {
95430
+ recordUnresolved(null);
95431
+ }
95432
+ };
95433
+ const retire = (reasonCode) => {
95434
+ if (!replaceExactClientProtectiveGroup(profileId, symbol2, group, null)) {
95435
+ throw new Error("Pending entry plan could not be retired durably.");
95436
+ }
95437
+ retirements.push({ entryOrderId: plan.entryClientOrderId, reasonCode });
95438
+ };
94971
95439
  try {
94972
95440
  if (group.attachmentJournal != null) {
94973
- const { disposition } = await resolveJournaledAttachmentContinuity(
95441
+ const { disposition: disposition2 } = await resolveJournaledAttachmentContinuity(
94974
95442
  request,
94975
95443
  group
94976
95444
  );
94977
- if (disposition !== "matching") {
95445
+ if (disposition2 !== "matching") {
94978
95446
  if (!replaceExactClientProtectiveGroup(profileId, symbol2, group, null)) {
94979
95447
  reports.push({
94980
95448
  order_id: group.attachmentJournal.entryOrderId,
@@ -94997,6 +95465,13 @@ var init_runtime_execution = __esm({
94997
95465
  attachment2.handoffCleanup
94998
95466
  );
94999
95467
  }
95468
+ recordIfStillPending();
95469
+ continue;
95470
+ }
95471
+ const ledger = await readPendingEntryMutation(request, plan);
95472
+ const mutation = ledger.kind === "found" ? ledger.mutation : null;
95473
+ if (mutation != null && (mutation.status === "settled" || mutation.status === "reconciled") && mutation.outcome_code === "rejected") {
95474
+ retire("pending_protective_entry_rejected");
95000
95475
  continue;
95001
95476
  }
95002
95477
  const evidence = await fetchBrowserOrderStatusEvidence({
@@ -95006,12 +95481,102 @@ var init_runtime_execution = __esm({
95006
95481
  signal: request.signal,
95007
95482
  orderId: plan.entryClientOrderId
95008
95483
  });
95484
+ if (evidence.responseValid && evidence.responseStatus === "unknownOid") {
95485
+ const observedAtMs = Date.now();
95486
+ const marginMs = requireClientRuntimeConfigSeconds(
95487
+ request.config,
95488
+ "client_pending_entry_never_landed_margin_seconds"
95489
+ ) * 1e3;
95490
+ const firstSeenAtMs = plan.recovery?.unknownOidFirstSeenAtMs ?? observedAtMs;
95491
+ const recordFirstSeen = () => {
95492
+ if (plan.recovery?.unknownOidFirstSeenAtMs != null) return;
95493
+ group = replacePendingEntryRecovery(profileId, symbol2, group, {
95494
+ ...group.pendingEntry?.recovery ?? plan.recovery,
95495
+ unknownOidFirstSeenAtMs: firstSeenAtMs
95496
+ });
95497
+ };
95498
+ let provableAtMs;
95499
+ if (ledger.kind === "found") {
95500
+ const expiresAfterMs = Number(ledger.mutation.exchange_expires_after_ms);
95501
+ if (!Number.isSafeInteger(expiresAfterMs) || expiresAfterMs <= 0) {
95502
+ throw new Error("Pending entry is unknown to Hyperliquid and its signed expiry is unavailable.");
95503
+ }
95504
+ provableAtMs = expiresAfterMs + marginMs;
95505
+ } else if (ledger.kind === "absent") {
95506
+ const ageStartMs = plan.createdAtMs ?? firstSeenAtMs;
95507
+ const signedLifetimeSeconds = Number(
95508
+ request.config?.client_runtime_exchange_mutation?.expires_after_seconds
95509
+ );
95510
+ if (!Number.isInteger(signedLifetimeSeconds) || signedLifetimeSeconds <= 0) {
95511
+ throw new Error("Missing required client runtime config: client_runtime_exchange_mutation.expires_after_seconds");
95512
+ }
95513
+ provableAtMs = ageStartMs + signedLifetimeSeconds * 1e3 + marginMs;
95514
+ } else {
95515
+ recordFirstSeen();
95516
+ throw new Error("Pending entry is unknown to Hyperliquid and the ledger cannot answer.");
95517
+ }
95518
+ if (observedAtMs < provableAtMs) {
95519
+ recordFirstSeen();
95520
+ throw new Error("Pending entry is unknown to Hyperliquid while its signed action can still land.");
95521
+ }
95522
+ const confirmedAtMs = plan.recovery?.unknownOidObservedAtMs;
95523
+ if (confirmedAtMs == null || confirmedAtMs < provableAtMs) {
95524
+ group = replacePendingEntryRecovery(profileId, symbol2, group, {
95525
+ ...group.pendingEntry?.recovery ?? plan.recovery,
95526
+ unknownOidFirstSeenAtMs: firstSeenAtMs,
95527
+ unknownOidObservedAtMs: observedAtMs
95528
+ });
95529
+ throw new Error("Pending entry is unknown to Hyperliquid; a second read confirms it never landed.");
95530
+ }
95531
+ const { rawFills: unknownEntryFills, evidence: unknownEntryFillEvidence } = await fetchBrowserUserFills({
95532
+ publicRequestPriority: "trading_reconcile",
95533
+ config: request.config ?? {},
95534
+ walletAddress: String(request.executionContext.walletAddress || "").trim(),
95535
+ symbols: [symbol2],
95536
+ forceRefresh: true,
95537
+ signal: request.signal
95538
+ });
95539
+ if (unknownEntryFillEvidence?.exactTerminalFillEvidenceComplete !== true) {
95540
+ throw new Error(
95541
+ `Never-landed pending entry fills are incomplete (${unknownEntryFillEvidence?.source ?? "unknown"}; provider_rows=${unknownEntryFillEvidence?.providerRowCount ?? "unknown"}).`
95542
+ );
95543
+ }
95544
+ if (unknownEntryFills.some((fill) => String(fill.cloid ?? "").trim().toLowerCase() === plan.entryClientOrderId)) {
95545
+ throw new Error("Pending entry is unknown to Hyperliquid but has fills for its CLOID.");
95546
+ }
95547
+ if (ledger.kind === "absent" && (await readPendingEntryMutation(request, plan)).kind !== "absent") {
95548
+ throw new Error("Pending entry reached the ledger while its retirement was being proven.");
95549
+ }
95550
+ retire("pending_protective_entry_never_landed");
95551
+ continue;
95552
+ }
95009
95553
  const expectedSide = plan.entrySide === "buy" ? ["b", "buy"] : ["a", "sell"];
95010
- const sizeTolerance = Math.max(1e-10, plan.requestedSize * 1e-8);
95011
- const shapeMatches = evidence.responseValid && evidence.orderMatches && String(evidence.coin ?? "").trim().toUpperCase() === symbol2.trim().toUpperCase() && expectedSide.includes(String(evidence.side ?? "").trim().toLowerCase()) && evidence.reduceOnly === false && evidence.isTrigger === false && evidence.isPositionTpsl === false && evidence.originalSize != null && Math.abs(evidence.originalSize - plan.requestedSize) <= sizeTolerance;
95554
+ const signedSize = evidence.originalSize;
95555
+ const sizeTolerance = Math.max(1e-10, (signedSize ?? 0) * 1e-8);
95556
+ const shapeMatches = evidence.responseValid && evidence.orderMatches && String(evidence.coin ?? "").trim().toUpperCase() === symbol2.trim().toUpperCase() && expectedSide.includes(String(evidence.side ?? "").trim().toLowerCase()) && evidence.reduceOnly === false && evidence.isTrigger === false && evidence.isPositionTpsl === false && signedSize != null && hyperliquidSignedSizeMatchesRequestedSize(
95557
+ plan.requestedSize,
95558
+ request.executionContext.sizeDecimals,
95559
+ signedSize
95560
+ );
95561
+ if (!shapeMatches && evidence.responseValid && evidence.orderMatches && evidence.terminal) {
95562
+ retire("pending_protective_entry_shape_mismatch");
95563
+ reports.push({
95564
+ order_id: plan.entryClientOrderId,
95565
+ client_mutation_id: plan.entryMutationId,
95566
+ symbol: symbol2,
95567
+ action: "PROTECTIVE_RECONCILIATION",
95568
+ status: "failed",
95569
+ execution_metadata: {
95570
+ reason_code: "pending_protective_entry_shape_mismatch",
95571
+ order_status: evidence.orderStatus
95572
+ }
95573
+ });
95574
+ continue;
95575
+ }
95012
95576
  if (!shapeMatches || !evidence.terminal) {
95013
95577
  throw new Error("Pending entry status or shape is unresolved.");
95014
95578
  }
95579
+ if (evidence.fulfilled === true) entryFilled = true;
95015
95580
  const { rawFills, evidence: fillEvidence } = await fetchBrowserUserFills({
95016
95581
  publicRequestPriority: "trading_reconcile",
95017
95582
  config: request.config ?? {},
@@ -95052,14 +95617,37 @@ var init_runtime_execution = __esm({
95052
95617
  filledNotional += canonicalSize * canonicalPrice;
95053
95618
  }
95054
95619
  if (matchingFills.length === 0 && evidence.fulfilled === false) {
95055
- if (!replaceExactClientProtectiveGroup(profileId, symbol2, group, null)) {
95056
- throw new Error("Non-filled pending entry could not be cleared durably.");
95057
- }
95620
+ retire("pending_protective_entry_unfilled");
95058
95621
  continue;
95059
95622
  }
95060
- if (!fillRowsValid || !Number.isFinite(filledSize) || filledSize <= 0 || filledSize > plan.requestedSize + sizeTolerance || !Number.isFinite(filledNotional) || filledNotional <= 0) {
95623
+ if (!fillRowsValid || !Number.isFinite(filledSize) || filledSize <= 0 || filledSize > (signedSize ?? 0) + sizeTolerance || !Number.isFinite(filledNotional) || filledNotional <= 0) {
95061
95624
  throw new Error("Exact pending entry fills are unavailable.");
95062
95625
  }
95626
+ const { disposition } = await resolveJournaledAttachmentContinuity(request, {
95627
+ ...group,
95628
+ attachmentJournal: {
95629
+ phase: "awaiting_member_outcome",
95630
+ entryOrderId: confirmedEntryOrderId,
95631
+ filledSize,
95632
+ averagePrice: filledNotional / filledSize,
95633
+ members: []
95634
+ }
95635
+ }, { allowOppositeStart: true });
95636
+ if (disposition === "interrupted") {
95637
+ retire("protective_attachment_continuity_interrupted");
95638
+ reports.push({
95639
+ order_id: plan.entryClientOrderId,
95640
+ client_mutation_id: plan.entryMutationId,
95641
+ symbol: symbol2,
95642
+ action: "PROTECTIVE_RECONCILIATION",
95643
+ status: "failed",
95644
+ execution_metadata: {
95645
+ reason_code: "protective_attachment_continuity_interrupted",
95646
+ entry_order_id: plan.entryClientOrderId
95647
+ }
95648
+ });
95649
+ continue;
95650
+ }
95063
95651
  const attachment = await attachClientProtectivePlan(request, group, {
95064
95652
  orderId: String(evidence.orderId),
95065
95653
  filledSize,
@@ -95071,21 +95659,12 @@ var init_runtime_execution = __esm({
95071
95659
  attachment.handoffCleanup
95072
95660
  );
95073
95661
  }
95662
+ recordIfStillPending();
95074
95663
  } catch (error48) {
95075
95664
  if (isClientRuntimeWindowHandoffBeforePlanDispatch(error48) || isClientRuntimeLeaseOwnershipError(error48)) {
95076
95665
  throw error48;
95077
95666
  }
95078
- reports.push({
95079
- order_id: plan.entryClientOrderId,
95080
- client_mutation_id: plan.entryMutationId,
95081
- symbol: symbol2,
95082
- action: "PROTECTIVE_RECONCILIATION",
95083
- status: "failed",
95084
- execution_metadata: {
95085
- reason_code: "pending_protective_entry_unresolved",
95086
- error: getSanitizedRuntimeErrorMessage(error48) || "Pending entry protection is unresolved."
95087
- }
95088
- });
95667
+ recordUnresolved(getSanitizedRuntimeErrorMessage(error48) || "Pending entry protection is unresolved.");
95089
95668
  }
95090
95669
  }
95091
95670
  if (hasClientProtectiveGroupReadFailure(profileId, symbol2)) {
@@ -95649,11 +96228,10 @@ var init_runtime_execution = __esm({
95649
96228
  ...await recoverJournaledClientProtectiveAttachments(
95650
96229
  request,
95651
96230
  readClientProtectiveGroups(profileId, symbol2).filter((group) => group.pendingEntry != null && group.legs.length > 0)
95652
- ),
95653
- ...await recoverPendingClientProtectiveGroups(
95654
- request,
95655
- readClientProtectiveGroups(profileId, symbol2).filter((group) => group.pendingEntry != null && group.legs.length === 0)
95656
96231
  )
96232
+ // Pending entries (no legs yet) are polled by one owner, the runtime's
96233
+ // pending-protection sweep (or the app host without a runtime), never
96234
+ // again by each decision.
95657
96235
  ];
95658
96236
  const reconciliationGroupSet = readFreshClientProtectiveGroups(profileId, symbol2);
95659
96237
  if (hasClientProtectiveGroupReadFailure(profileId, symbol2)) {
@@ -96708,6 +97286,18 @@ var init_runtime_execution = __esm({
96708
97286
  signal?.addEventListener("abort", onAbort, { once: true });
96709
97287
  });
96710
97288
  };
97289
+ isDefinitiveEntryRejection = (error48, entryMutationId) => error48 instanceof HyperliquidExchangeRejectionError && Boolean(entryMutationId) && String(error48.clientMutationId ?? "").trim().toLowerCase() === String(entryMutationId).trim().toLowerCase();
97290
+ resolveExchangeSizeDecimals = async (symbol2, config2, publicRequestPriority) => {
97291
+ const { sz_decimals: sizeDecimals } = await resolveHyperliquidAsset({
97292
+ symbol: symbol2,
97293
+ config: config2,
97294
+ publicRequestPriority
97295
+ });
97296
+ if (!Number.isInteger(sizeDecimals) || sizeDecimals < 0) {
97297
+ throw new Error(`Hyperliquid size precision is unavailable for ${symbol2}.`);
97298
+ }
97299
+ return sizeDecimals;
97300
+ };
96711
97301
  roundAssetSize = (size, decimals) => {
96712
97302
  if (!Number.isFinite(size)) {
96713
97303
  return 0;
@@ -96825,6 +97415,7 @@ var init_runtime_execution = __esm({
96825
97415
  return pending3;
96826
97416
  };
96827
97417
  drainServerProtectionForRuntime = async (request, initialActions) => {
97418
+ if (request.executionMode === "server") return;
96828
97419
  try {
96829
97420
  await drainClientProtectiveActions({
96830
97421
  profileId: request.profileId,
@@ -96909,9 +97500,178 @@ var init_runtime_execution = __esm({
96909
97500
  if (clientExecutionMutations.get(key) === pending3) clientExecutionMutations.delete(key);
96910
97501
  }
96911
97502
  };
97503
+ allocateServerExecutionCapacity = async (authority, plans) => {
97504
+ if (plans.length === 0) return [];
97505
+ try {
97506
+ return await checkServerHostExecutionCapacity(
97507
+ authority.profileId,
97508
+ plans,
97509
+ authority.signal,
97510
+ authority.sessionId,
97511
+ authority.runtimeLeaseToken,
97512
+ authority.runtimeDeviceId,
97513
+ authority.schedulerInstanceId
97514
+ );
97515
+ } catch {
97516
+ throwIfAborted2(authority.signal);
97517
+ return plans.map(() => "unavailable");
97518
+ }
97519
+ };
96912
97520
  withClientProtectiveAdoptionMutation = (profileId, work, signal) => withClientExecutionMutation({ profileId: String(profileId).trim(), signal }, work);
96913
- executeClientRuntimeDecision = (input) => withClientExecutionMutation(input, () => executeClientRuntimeDecisionUnlocked(input));
96914
- maintainClientRuntimeTrailingStops = (request) => withClientExecutionMutation(request, async () => {
97521
+ executeClientRuntimeDecisionLocked = (input) => withClientExecutionMutation(input, () => withClientProtectivePlanSetLock(
97522
+ input.profileId,
97523
+ input.executionContext.symbol,
97524
+ () => executeClientRuntimeDecisionUnlocked(input),
97525
+ input.signal
97526
+ ));
97527
+ SERVER_AUDIT_RECORDED_ELSEWHERE = /* @__PURE__ */ new Set([
97528
+ "suppressed_duplicate_decision",
97529
+ "same_intent_cooldown",
97530
+ "redis_unavailable",
97531
+ "execution_capacity_refused",
97532
+ "execution_capacity_unavailable"
97533
+ ]);
97534
+ serverExecutionAuditOutcome = (result) => {
97535
+ if (result.status === "executed") {
97536
+ if (result.reasonCode === "close_fill_unresolved") return ["executed", "close_fill_unresolved"];
97537
+ if (result.reasonCode === "flip_close_fill_unresolved") return ["blocked", "flip_close_fill_unresolved"];
97538
+ if (result.reasonCode === "protective_reconciliation_unresolved") return ["blocked", "protective_reconciliation_unresolved"];
97539
+ return ["executed", "executed"];
97540
+ }
97541
+ if (result.status === "skipped") {
97542
+ if (result.reasonCode === "noop_already_at_target" || result.reasonCode === "stale_target_conflict") {
97543
+ return ["noop", result.reasonCode];
97544
+ }
97545
+ if (result.reasonCode === "protective_reconciliation_unresolved") return ["blocked", result.reasonCode];
97546
+ return ["blocked", "local_safety_block"];
97547
+ }
97548
+ const failure2 = result.executionReports.find((report) => report.status === "failed");
97549
+ const failureReason = String(failure2?.execution_metadata?.reason_code ?? "");
97550
+ if (failureReason === "outcome_unknown") return ["outcome_unknown", "outcome_unknown"];
97551
+ return ["error", failureReason === "exchange_rejected" ? "exchange_rejected" : "execution_error"];
97552
+ };
97553
+ executeClientRuntimeDecision = async (input) => {
97554
+ if (input.executionMode !== "server") return executeClientRuntimeDecisionLocked(input);
97555
+ const audit = input.serverExecutionAudit ?? { auditId: null };
97556
+ let entryDispatched = false;
97557
+ let result;
97558
+ try {
97559
+ result = await executeClientRuntimeDecisionLocked({
97560
+ ...input,
97561
+ serverExecutionAudit: audit,
97562
+ onExchangeOrderDispatched: (kind) => {
97563
+ input.onExchangeOrderDispatched?.(kind);
97564
+ if (kind === "entry") entryDispatched = true;
97565
+ }
97566
+ });
97567
+ } catch (error48) {
97568
+ if (audit.auditId !== null && !input.signal?.aborted && (input.decision === "BUY" || input.decision === "SELL")) {
97569
+ const reason = String(error48?.reasonCode ?? "");
97570
+ const [status2, reasonCode2] = reason === "outcome_unknown" || !reason && entryDispatched ? ["outcome_unknown", "outcome_unknown"] : ["error", reason === "exchange_rejected" ? "exchange_rejected" : "execution_error"];
97571
+ try {
97572
+ await recordServerHostExecutionAudit(
97573
+ input.profileId,
97574
+ {
97575
+ auditId: audit.auditId,
97576
+ cycleId: input.cycleId ?? null,
97577
+ symbol: input.executionContext.symbol,
97578
+ intent: "open",
97579
+ side: input.decision === "BUY" ? "buy" : "sell",
97580
+ requestedUnits: Math.max(0, Math.trunc(input.units)),
97581
+ reduceOnly: false,
97582
+ lastPrice: input.executionContext.lastPrice,
97583
+ status: status2,
97584
+ reasonCode: reasonCode2,
97585
+ orderId: null
97586
+ },
97587
+ input.signal,
97588
+ input.sessionId,
97589
+ input.runtimeLeaseToken,
97590
+ input.runtimeDeviceId,
97591
+ input.schedulerInstanceId
97592
+ );
97593
+ } catch (auditError) {
97594
+ appendRuntimeDebugEvent({
97595
+ label: "server_execution_audit_failed",
97596
+ note: getSanitizedRuntimeErrorMessage(auditError) || "unknown",
97597
+ outcome: "error"
97598
+ });
97599
+ }
97600
+ }
97601
+ throw error48;
97602
+ }
97603
+ if (result.intent === "hold" || !(input.decision === "BUY" || input.decision === "SELL") || SERVER_AUDIT_RECORDED_ELSEWHERE.has(String(result.reasonCode ?? ""))) {
97604
+ return result;
97605
+ }
97606
+ const [status, reasonCode] = serverExecutionAuditOutcome(result);
97607
+ const orderId = result.executionReports.find((report) => report.status === "executed" && report.order_id)?.order_id ?? null;
97608
+ try {
97609
+ const recorded = await recordServerHostExecutionAudit(
97610
+ input.profileId,
97611
+ {
97612
+ auditId: audit.auditId,
97613
+ cycleId: input.cycleId ?? null,
97614
+ symbol: input.executionContext.symbol,
97615
+ intent: result.intent,
97616
+ side: result.side ?? null,
97617
+ requestedUnits: Math.max(0, Math.trunc(result.requestedUnits)),
97618
+ reduceOnly: result.reduceOnly,
97619
+ lastPrice: input.executionContext.lastPrice,
97620
+ status,
97621
+ reasonCode,
97622
+ orderId: orderId === null ? null : String(orderId)
97623
+ },
97624
+ input.signal,
97625
+ input.sessionId,
97626
+ input.runtimeLeaseToken,
97627
+ input.runtimeDeviceId,
97628
+ input.schedulerInstanceId
97629
+ );
97630
+ audit.auditId = recorded.audit_id;
97631
+ } catch (error48) {
97632
+ appendRuntimeDebugEvent({
97633
+ label: "server_execution_audit_failed",
97634
+ note: getSanitizedRuntimeErrorMessage(error48) || "unknown",
97635
+ outcome: "error"
97636
+ });
97637
+ }
97638
+ if (audit.auditId === null) return result;
97639
+ const auditId = audit.auditId;
97640
+ return {
97641
+ ...result,
97642
+ executionReports: result.executionReports.map((report) => report.status === "executed" && ["BUY", "SELL", "FLIP_OPEN"].includes(String(report.action).toUpperCase()) ? { ...report, execution_metadata: { ...report.execution_metadata, execution_audit_id: auditId } } : report)
97643
+ };
97644
+ };
97645
+ waitForFlipCloseFreshness = async (input) => {
97646
+ const delayMs = Math.round(input.executionContext.closePositionDelaySeconds * 1e3);
97647
+ if (!(delayMs > 0)) return;
97648
+ const pollMs = Number(input.executionContext.closePositionFreshnessPollMs);
97649
+ const walletAddress = String(input.executionContext.walletAddress || "").trim();
97650
+ if (input.executionMode !== "server" || !(pollMs > 0) || !walletAddress) {
97651
+ await sleep2(delayMs, input.signal);
97652
+ return;
97653
+ }
97654
+ const deadline = Date.now() + delayMs;
97655
+ for (; ; ) {
97656
+ throwIfAborted2(input.signal);
97657
+ try {
97658
+ const snapshot = await fetchBrowserPositionSnapshot(
97659
+ input.config ?? {},
97660
+ walletAddress,
97661
+ input.executionContext.symbol,
97662
+ input.signal,
97663
+ executionReadPriority(input)
97664
+ );
97665
+ if (Number(snapshot.size) === 0) return;
97666
+ } catch {
97667
+ throwIfAborted2(input.signal);
97668
+ }
97669
+ const remaining = deadline - Date.now();
97670
+ if (remaining <= 0) return;
97671
+ await sleep2(Math.min(pollMs, remaining), input.signal);
97672
+ }
97673
+ };
97674
+ maintainClientRuntimeTrailingStops = (request) => withClientExecutionMutation(request, () => withClientProtectivePlanSetLock(request.profileId, request.executionContext.symbol, async () => {
96915
97675
  const context = request.executionContext;
96916
97676
  const capturedAt = Date.parse(context.decisionSnapshotCapturedAt ?? "");
96917
97677
  const maxAge = Number(context.executionFreshnessMaxAgeSeconds) * 1e3;
@@ -96920,15 +97680,11 @@ var init_runtime_execution = __esm({
96920
97680
  const groups = readFreshClientProtectiveGroups(String(request.profileId).trim(), context.symbol);
96921
97681
  if (hasClientProtectiveGroupReadFailure(String(request.profileId).trim(), context.symbol) || groups.some((group) => group.walletAddress !== String(context.walletAddress ?? "").trim().toLowerCase() || group.pendingEntry || group.attachmentJournal || group.cleanupRequired || group.legs.some((leg) => leg.replacement))) return [];
96922
97682
  if (!groups.some((group) => group.legs.some((leg) => leg.isTrailing && typeof leg.orderId === "number"))) return [];
96923
- const { sz_decimals: assetSizeDecimals } = await resolveHyperliquidAsset({
96924
- symbol: context.symbol,
96925
- config: request.config,
96926
- publicRequestPriority: "trading_reduce"
96927
- });
97683
+ const assetSizeDecimals = await resolveExchangeSizeDecimals(context.symbol, request.config ?? void 0, "trading_reduce");
96928
97684
  const isLong = context.currentPositionSize > 0;
96929
97685
  const improves = groups.some((group) => group.isLong === isLong && group.legs.some((leg) => {
96930
97686
  if (!leg.isTrailing || typeof leg.orderId !== "number") return false;
96931
- const size = roundAssetSize(Math.min(leg.size, Math.abs(context.currentPositionSize)), context.sizeDecimals);
97687
+ const size = roundAssetSize(Math.min(leg.size, Math.abs(context.currentPositionSize)), assetSizeDecimals);
96932
97688
  if (!(size > 0)) return false;
96933
97689
  return resolveTrailingStopTightening({
96934
97690
  currentPrice: context.lastPrice,
@@ -96943,12 +97699,16 @@ var init_runtime_execution = __esm({
96943
97699
  }));
96944
97700
  if (!improves) return [];
96945
97701
  await request.beforeTrailingMaintenance?.(request.signal);
96946
- const guardedRequest = { ...request, onExchangeOrderDispatched: (kind) => {
96947
- if (Date.now() - capturedAt > maxAge) throw new Error("Trailing observation expired before exchange dispatch.");
96948
- request.onExchangeOrderDispatched?.(kind);
96949
- } };
97702
+ const guardedRequest = {
97703
+ ...request,
97704
+ executionContext: { ...context, sizeDecimals: assetSizeDecimals },
97705
+ onExchangeOrderDispatched: (kind) => {
97706
+ if (Date.now() - capturedAt > maxAge) throw new Error("Trailing observation expired before exchange dispatch.");
97707
+ request.onExchangeOrderDispatched?.(kind);
97708
+ }
97709
+ };
96950
97710
  return tightenClientTrailingStops({ request: guardedRequest, currentPositionSize: context.currentPositionSize, currentPrice: context.lastPrice });
96951
- });
97711
+ }, request.signal));
96952
97712
  listPendingClientProtectiveSymbols = (profileId) => {
96953
97713
  const storage3 = resolveClientRuntimeProtectionStorage();
96954
97714
  if (!storage3) return [];
@@ -97002,11 +97762,7 @@ var init_runtime_execution = __esm({
97002
97762
  if (!hasUnresolvedClientProtectiveIdentity(groups)) continue;
97003
97763
  let symbolReports = [];
97004
97764
  try {
97005
- const { sz_decimals: sizeDecimals } = await resolveHyperliquidAsset({
97006
- symbol: symbol2,
97007
- config: input.config,
97008
- publicRequestPriority: "trading_reconcile"
97009
- });
97765
+ const sizeDecimals = await resolveExchangeSizeDecimals(symbol2, input.config, "trading_reconcile");
97010
97766
  const core = {
97011
97767
  profileId,
97012
97768
  config: input.config,
@@ -97218,7 +97974,8 @@ var init_runtime_execution = __esm({
97218
97974
  const results = [];
97219
97975
  for (const symbol2 of listPendingClientProtectiveSymbols(profileId)) {
97220
97976
  throwIfAborted2(input.signal);
97221
- const reports = await withClientExecutionMutation({ profileId, signal: input.signal }, async () => {
97977
+ const retired = [];
97978
+ const reports = await withClientExecutionMutation({ profileId, signal: input.signal }, () => withClientProtectivePlanSetLock(profileId, symbol2, async () => {
97222
97979
  const groups = readFreshClientProtectiveGroups(profileId, symbol2);
97223
97980
  if (hasClientProtectiveGroupReadFailure(profileId, symbol2)) {
97224
97981
  return [{
@@ -97231,14 +97988,7 @@ var init_runtime_execution = __esm({
97231
97988
  }
97232
97989
  const ownGroups = groups.filter((group) => group.pendingEntry != null && group.walletAddress === walletAddress.toLowerCase());
97233
97990
  if (ownGroups.length === 0) return [];
97234
- const { sz_decimals: sizeDecimals } = await resolveHyperliquidAsset({
97235
- symbol: symbol2,
97236
- config: input.config,
97237
- publicRequestPriority: "trading_reconcile"
97238
- });
97239
- if (!Number.isInteger(sizeDecimals) || sizeDecimals < 0) {
97240
- throw new Error(`Hyperliquid size precision is unavailable for ${symbol2}.`);
97241
- }
97991
+ const sizeDecimals = await resolveExchangeSizeDecimals(symbol2, input.config, "trading_reconcile");
97242
97992
  const request = {
97243
97993
  mutationAuthority: "manual_session",
97244
97994
  profileId,
@@ -97254,21 +98004,100 @@ var init_runtime_execution = __esm({
97254
98004
  ),
97255
98005
  ...await recoverPendingClientProtectiveGroups(
97256
98006
  request,
97257
- readClientProtectiveGroups(profileId, symbol2).filter((group) => group.pendingEntry != null && group.legs.length === 0 && group.walletAddress === walletAddress.toLowerCase())
98007
+ readClientProtectiveGroups(profileId, symbol2).filter((group) => group.pendingEntry != null && group.legs.length === 0 && group.walletAddress === walletAddress.toLowerCase()),
98008
+ retired,
98009
+ input.reportsStale !== false
97258
98010
  )
97259
98011
  ];
97260
- });
97261
- results.push({ symbol: symbol2, reports });
98012
+ }, input.signal));
98013
+ results.push({ symbol: symbol2, reports, ...retired.length > 0 ? { retired } : {} });
97262
98014
  }
97263
98015
  return results;
97264
98016
  };
97265
- executeClientRuntimeDecisionUnlocked = async (input) => {
97266
- throwIfAborted2(input.signal);
97267
- const requestedUnits = Math.max(0, Math.trunc(input.units));
98017
+ clientRuntimeDecisionSizesOrders = (input) => {
98018
+ const context = input.executionContext;
98019
+ const profileId = String(input.profileId).trim();
98020
+ if (input.decision !== "HOLD" && Math.max(0, Math.trunc(input.units)) > 0) return true;
98021
+ if (readClientProtectiveGroups(profileId, context.symbol).some((group) => group.legs.length > 0 || group.attachmentJournal != null) || hasClientProtectiveGroupReadFailure(profileId, context.symbol)) {
98022
+ return true;
98023
+ }
98024
+ const positionSize = Number.isFinite(context.currentPositionSize) ? Number(context.currentPositionSize) : 0;
98025
+ return !input.skipTrailingMaintenance && context.tpSlEnabled === true && context.stopLossEnabled !== false && context.stopLossType === "trailing" && positionSize !== 0;
98026
+ };
98027
+ executeClientRuntimeDecisionUnlocked = async (unsizedInput) => {
98028
+ throwIfAborted2(unsizedInput.signal);
98029
+ const requestedUnits = Math.max(0, Math.trunc(unsizedInput.units));
97268
98030
  appendRuntimeDebugEvent({
97269
98031
  label: "execution_start",
97270
- note: `decision=${input.decision} units=${requestedUnits} symbol=${input.executionContext.symbol}`
98032
+ note: `decision=${unsizedInput.decision} units=${requestedUnits} symbol=${unsizedInput.executionContext.symbol}`
97271
98033
  });
98034
+ if (!clientRuntimeDecisionSizesOrders(unsizedInput)) {
98035
+ return {
98036
+ status: "skipped",
98037
+ intent: "hold",
98038
+ side: null,
98039
+ reduceOnly: false,
98040
+ requestedUnits,
98041
+ targetSignedUnits: signedUnitsFromEntryPosition(
98042
+ Number.isFinite(unsizedInput.executionContext.currentPositionSize) ? Number(unsizedInput.executionContext.currentPositionSize) : 0,
98043
+ unsizedInput.executionContext.currentPositionEntryPrice,
98044
+ unsizedInput.executionContext.unitSizeUsdc
98045
+ ),
98046
+ executedSizeAsset: 0,
98047
+ executionReports: [],
98048
+ message: unsizedInput.decision === "HOLD" ? "Hold (No Action)" : "Unsupported decision",
98049
+ executionSnapshotId: createRuntimeId(),
98050
+ executionSnapshotCapturedAt: (/* @__PURE__ */ new Date()).toISOString()
98051
+ };
98052
+ }
98053
+ if (unsizedInput.executionMode === "server" && !unsizedInput.serverCapacityChecked && (unsizedInput.decision === "BUY" || unsizedInput.decision === "SELL") && requestedUnits > 0) {
98054
+ const skipped = (reasonCode, message) => ({
98055
+ status: "skipped",
98056
+ intent: "hold",
98057
+ side: null,
98058
+ reduceOnly: false,
98059
+ requestedUnits,
98060
+ targetSignedUnits: 0,
98061
+ executedSizeAsset: 0,
98062
+ executionReports: [],
98063
+ message,
98064
+ reasonCode,
98065
+ executionSnapshotId: createRuntimeId(),
98066
+ executionSnapshotCapturedAt: (/* @__PURE__ */ new Date()).toISOString()
98067
+ });
98068
+ const capacity = unsizedInput.serverCapacityAllocation ?? (await allocateServerExecutionCapacity(unsizedInput, [
98069
+ { symbol: unsizedInput.executionContext.symbol, decision: unsizedInput.decision, units: requestedUnits }
98070
+ ]))[0];
98071
+ if (capacity === "unavailable") {
98072
+ throwIfAborted2(unsizedInput.signal);
98073
+ return skipped("execution_capacity_unavailable", "Execution skipped: Failed to fetch positions before limit check.");
98074
+ }
98075
+ if (!capacity.accepted || !(capacity.units > 0)) {
98076
+ appendRuntimeDebugEvent({
98077
+ label: "execution_capacity_refused",
98078
+ note: `symbol=${unsizedInput.executionContext.symbol} reason=${capacity.reason_code ?? "unknown"}`,
98079
+ outcome: "ok"
98080
+ });
98081
+ return skipped("execution_capacity_refused", `Skipped: runtime constraint ${capacity.reason_code ?? "capacity"}.`);
98082
+ }
98083
+ return executeClientRuntimeDecisionUnlocked({
98084
+ ...unsizedInput,
98085
+ units: Math.min(requestedUnits, Math.trunc(capacity.units)),
98086
+ serverCapacityChecked: true
98087
+ });
98088
+ }
98089
+ const input = {
98090
+ ...unsizedInput,
98091
+ executionContext: {
98092
+ ...unsizedInput.executionContext,
98093
+ sizeDecimals: await resolveExchangeSizeDecimals(
98094
+ unsizedInput.executionContext.symbol,
98095
+ unsizedInput.config ?? void 0,
98096
+ unsizedInput.decision === "HOLD" || requestedUnits <= 0 ? "trading_reconcile" : executionReadPriority(unsizedInput)
98097
+ )
98098
+ }
98099
+ };
98100
+ throwIfAborted2(input.signal);
97272
98101
  const decisionSnapshotPositionSize = Number.isFinite(input.executionContext.currentPositionSize) ? Number(input.executionContext.currentPositionSize) : 0;
97273
98102
  const decisionSnapshotSignedUnits = signedUnitsFromEntryPosition(
97274
98103
  decisionSnapshotPositionSize,
@@ -97481,7 +98310,52 @@ var init_runtime_execution = __esm({
97481
98310
  executionSnapshotCapturedAt
97482
98311
  };
97483
98312
  }
97484
- if (!targetResolution.closeOpposingPosition && classified.reduceOnly !== true) {
98313
+ if (input.executionMode === "server") {
98314
+ throwIfAborted2(input.signal);
98315
+ const claim = await claimServerHostExecution(
98316
+ input.profileId,
98317
+ {
98318
+ cycleId: input.cycleId ?? null,
98319
+ symbol: input.executionContext.symbol,
98320
+ intent: classified.intent,
98321
+ side: classified.side,
98322
+ requestedUnits: Math.max(0, Math.trunc(classified.requestedUnits)),
98323
+ reduceOnly: classified.reduceOnly,
98324
+ marginMode: input.executionContext.marginMode === "auto" ? null : input.executionContext.marginMode,
98325
+ lastPrice: input.executionContext.lastPrice
98326
+ },
98327
+ input.signal,
98328
+ input.sessionId,
98329
+ input.runtimeLeaseToken,
98330
+ input.runtimeDeviceId,
98331
+ input.schedulerInstanceId
98332
+ );
98333
+ if (input.serverExecutionAudit && typeof claim.audit_id === "number") {
98334
+ input.serverExecutionAudit.auditId = claim.audit_id;
98335
+ }
98336
+ if (!claim.allowed) {
98337
+ appendRuntimeDebugEvent({
98338
+ label: "execution_guard_suppressed",
98339
+ note: `symbol=${input.executionContext.symbol} intent=${classified.intent} reason=${claim.reason_code}`,
98340
+ outcome: "ok"
98341
+ });
98342
+ return {
98343
+ status: "skipped",
98344
+ intent: classified.intent,
98345
+ side: classified.side,
98346
+ reduceOnly: classified.reduceOnly,
98347
+ requestedUnits: classified.requestedUnits,
98348
+ targetSignedUnits: classified.targetSignedUnits,
98349
+ executedSizeAsset: 0,
98350
+ executionReports: reconciliationReports,
98351
+ message: "The execution guard suppressed a duplicate or repeated decision.",
98352
+ reasonCode: claim.reason_code,
98353
+ executionSnapshotId,
98354
+ executionSnapshotCapturedAt
98355
+ };
98356
+ }
98357
+ }
98358
+ if (!targetResolution.closeOpposingPosition && classified.reduceOnly !== true && !await serverEntryLeverageAlreadyHeld(input)) {
97485
98359
  throwIfAborted2(input.signal);
97486
98360
  await executeClientLeverageUpdate({
97487
98361
  profileId: input.profileId,
@@ -97694,9 +98568,7 @@ var init_runtime_execution = __esm({
97694
98568
  input.executionContext.symbol,
97695
98569
  []
97696
98570
  );
97697
- if (input.executionContext.closePositionDelaySeconds > 0) {
97698
- await sleep2(Math.round(input.executionContext.closePositionDelaySeconds * 1e3), input.signal);
97699
- }
98571
+ await waitForFlipCloseFreshness(input);
97700
98572
  throwIfAborted2(input.signal);
97701
98573
  await executeClientLeverageUpdate({
97702
98574
  profileId: input.profileId,
@@ -97715,10 +98587,8 @@ var init_runtime_execution = __esm({
97715
98587
  throwIfAborted2(input.signal);
97716
98588
  }
97717
98589
  const actualUnits = targetResolution.orderUnits;
97718
- const sizeAsset = roundAssetSize(
97719
- input.executionContext.unitSizeUsdc * actualUnits / executionPrice,
97720
- input.executionContext.sizeDecimals
97721
- );
98590
+ const intendedSizeAsset = input.executionContext.unitSizeUsdc * actualUnits / executionPrice;
98591
+ const sizeAsset = Number.isFinite(intendedSizeAsset) && intendedSizeAsset > 0 ? quantizeHyperliquidOrderSizeValue(intendedSizeAsset, input.executionContext.sizeDecimals, "nearest") : 0;
97722
98592
  appendRuntimeDebugEvent({
97723
98593
  label: "execution_size_computed",
97724
98594
  note: `symbol=${input.executionContext.symbol} actualUnits=${actualUnits} sizeAsset=${sizeAsset} price=${executionPrice}`
@@ -97726,7 +98596,7 @@ var init_runtime_execution = __esm({
97726
98596
  if (sizeAsset <= 0) {
97727
98597
  appendRuntimeDebugEvent({
97728
98598
  label: "execution_skipped_zero_size",
97729
- note: `actualUnits=${actualUnits} unitSize=${input.executionContext.unitSizeUsdc} decimals=${input.executionContext.sizeDecimals}`,
98599
+ note: `reason=size_below_lot_precision actualUnits=${actualUnits} unitSize=${input.executionContext.unitSizeUsdc} intendedSize=${intendedSizeAsset} decimals=${input.executionContext.sizeDecimals}`,
97730
98600
  outcome: "error"
97731
98601
  });
97732
98602
  return {
@@ -97922,6 +98792,9 @@ var init_runtime_execution = __esm({
97922
98792
  const handoffBeforeDispatch = handoffVeto || !entryDispatchBoundaryPassed && input.shouldCancelBeforeExchangeOrderDispatch?.() === true;
97923
98793
  const hasCommittedReports = executionReports.some((report) => report.status === "executed" && String(report.order_id || "").trim().length > 0);
97924
98794
  let vetoedProtectionCleanupFailed = false;
98795
+ if (pendingProtectiveGroup != null && entryDispatchBoundaryPassed && pendingProtectiveCleanup != null && isDefinitiveEntryRejection(entryDispatchError, entryMutationId)) {
98796
+ clearClientRuntimeVetoedProtectiveIntent(pendingProtectiveCleanup);
98797
+ }
97925
98798
  if (pendingProtectiveGroup != null && !entryDispatchBoundaryPassed) {
97926
98799
  if (pendingProtectiveCleanup == null || !clearClientRuntimeVetoedProtectiveIntent(pendingProtectiveCleanup)) {
97927
98800
  vetoedProtectionCleanupFailed = true;
@@ -98875,7 +99748,7 @@ var init_runtime_execution = __esm({
98875
99748
  });
98876
99749
 
98877
99750
  // lib/runtime/protective-adoption-status.ts
98878
- var deferrals, listeners, notify, recordClientProtectiveAdoptionDeferral, clearClientProtectiveAdoptionDeferral;
99751
+ var deferrals, listeners2, notify, recordClientProtectiveAdoptionDeferral, clearClientProtectiveAdoptionDeferral;
98879
99752
  var init_protective_adoption_status = __esm({
98880
99753
  "lib/runtime/protective-adoption-status.ts"() {
98881
99754
  "use strict";
@@ -98883,9 +99756,9 @@ var init_protective_adoption_status = __esm({
98883
99756
  init_define_VTX_GROK_POLICY();
98884
99757
  init_define_VTX_PI_MODEL_POLICY();
98885
99758
  deferrals = /* @__PURE__ */ new Map();
98886
- listeners = /* @__PURE__ */ new Set();
99759
+ listeners2 = /* @__PURE__ */ new Set();
98887
99760
  notify = () => {
98888
- listeners.forEach((listener) => listener());
99761
+ listeners2.forEach((listener) => listener());
98889
99762
  };
98890
99763
  recordClientProtectiveAdoptionDeferral = (profileId, symbols) => {
98891
99764
  deferrals.set(String(profileId).trim(), { deferredAtMs: Date.now(), symbols: [...symbols] });
@@ -104055,7 +104928,7 @@ var init_inference_observability = __esm({
104055
104928
  });
104056
104929
 
104057
104930
  // lib/runtime/provider-client.ts
104058
- var mapOpenAiCompatibleNativeRequestPath, isRetryableProviderFailureForFallback, extractRuntimeProviderFailureStatusCode, isModelSpecificProviderFailure, isKeySpecificOrTransientProviderFailure, providerDispatchClockMs, beginProviderDispatch, providerDispatchLatencyMs, attachProviderDispatchFailure, attachProviderFailureTrace, toRuntimeExecutionMode, buildNoneReasoningPayload, normalizeLevelValue, buildRuntimeReasoningPayload, resolveProviderReasoningParameters, requestedReasoningParameters, usesReasoningCompletionTokens, shouldRequestReasoningTrace, normalizeProviderToken, runtimeProviderForByokProvider, findByokProviderForRuntime, findNativeRequestParametersForModel, modelHasNativeRequestApiSurface, setDottedRequestValue, normalizeProviderMaxTokens, applyRuntimeRequestContract, normalizeProvider2, requireProviderEndpoint, estimateTokenCount, normalizeUsageCount, buildOpenAiUsagePayload, buildAnthropicUsagePayload, buildGoogleUsagePayload, normalizeProviderUsageMetrics, providerResponseSignals, fetchProvider, readProviderPayload, emitRuntimeDelta, readProviderTextStream, extractOpenAiCompatibleDeltaText, readOpenAiCompatibleTextStream, rawProviderTextForDebug, extractProviderErrorDetail, buildCanonicalProviderFailureMessage, toProviderNetworkDebugError, REASONING_ONLY_CONTENT_TYPES, extractOpenAiContentPartText, extractOpenAiReasoningPartText, extractOpenAiMessageContent, extractOpenAiMessageReasoningContent, extractOpenAiCompatibleText, normalizeRuntimeChatHistory, calculateProviderCost, callOpenAiCompatible, extractOpenAiResponsesText, extractOpenAiResponsesReasoning, callOpenAiResponses, extractAnthropicMessageText, extractAnthropicReasoningContent, callAnthropic, isVertexExpressKey, googlePublicModelIdRegexes, shouldUseGooglePublicEndpointForModel, normalizeGoogleModelId, shouldRetryGoogleVertexRequestPublicly, GOOGLE_VERTEX_PUBLIC_RETRY_NOTE, buildGoogleRequest, isGoogleReasoningPart, extractGoogleMessageText, extractGoogleReasoningContent, extractGoogleSafetyMetadata, GOOGLE_BLOCKED_FINISH_REASONS, googleProviderBlockReason, callGoogle, resolveRuntimeProvider, defaultInvocationContext, defaultProviderTransport, invocationFailureCode, buildBrowserInvocation, resolveProviderSecretKey, resolveNativeProviderCredentialSlots, resolveProviderSecretKeySlots, requireOpenAiCompatibleRuntimeBaseUrl, runtimeSubscriptionPlan, runtimeKeySlotSupportsModel, invokeRuntimeProviderAttempt, invokeRuntimeProvider;
104931
+ var mapOpenAiCompatibleNativeRequestPath, isRetryableProviderFailureForFallback, extractRuntimeProviderFailureStatusCode, isModelSpecificProviderFailure, isKeySpecificOrTransientProviderFailure, providerDispatchClockMs, beginProviderDispatch, providerDispatchLatencyMs, attachProviderDispatchFailure, attachProviderFailureTrace, toRuntimeExecutionMode, buildNoneReasoningPayload, normalizeLevelValue, buildRuntimeReasoningPayload, resolveProviderReasoningParameters, requestedReasoningParameters, usesReasoningCompletionTokens, shouldRequestReasoningTrace, normalizeProviderToken, runtimeProviderForByokProvider, findByokProviderForRuntime, findNativeRequestParametersForModel, modelHasNativeRequestApiSurface, setDottedRequestValue, normalizeProviderMaxTokens, applyRuntimeRequestContract, normalizeProvider2, requireProviderEndpoint, estimateTokenCount, normalizeUsageCount, buildOpenAiUsagePayload, buildAnthropicUsagePayload, buildGoogleUsagePayload, normalizeProviderUsageMetrics, providerResponseSignals, fetchProvider, readProviderPayload, emitRuntimeDelta, readProviderTextStream, extractOpenAiCompatibleDeltaText, readOpenAiCompatibleTextStream, rawProviderTextForDebug, extractProviderErrorDetail, buildCanonicalProviderFailureMessage, toProviderNetworkDebugError, REASONING_ONLY_CONTENT_TYPES, extractOpenAiContentPartText, extractOpenAiReasoningPartText, extractOpenAiMessageContent, extractOpenAiMessageReasoningContent, extractOpenAiCompatibleText, normalizeRuntimeChatHistory, calculateProviderCost, callOpenAiCompatible, extractOpenAiResponsesText, extractOpenAiResponsesReasoning, callOpenAiResponses, extractAnthropicMessageText, extractAnthropicReasoningContent, callAnthropic, isVertexExpressKey, googlePublicModelIdRegexes, shouldUseGooglePublicEndpointForModel, normalizeGoogleModelId, shouldRetryGoogleVertexRequestPublicly, GOOGLE_VERTEX_PUBLIC_RETRY_NOTE, buildGoogleRequest, isGoogleReasoningPart, extractGoogleMessageText, extractGoogleReasoningContent, extractGoogleSafetyMetadata, GOOGLE_BLOCKED_FINISH_REASONS, googleProviderBlockReason, callGoogle, resolveRuntimeProvider, defaultInvocationContext, defaultProviderTransport, invocationFailureCode, buildBrowserInvocation, resolveProviderSecretKey, resolveNativeProviderCredentialSlots, resolveProviderSecretKeySlots, requireOpenAiCompatibleRuntimeBaseUrl, runtimeSubscriptionPlan, runtimeKeySlotSupportsModel, invokeRuntimeProviderAttempt, runtimeProviderInvoker, invokeRuntimeProvider;
104059
104932
  var init_provider_client = __esm({
104060
104933
  "lib/runtime/provider-client.ts"() {
104061
104934
  "use strict";
@@ -106770,8 +107643,10 @@ var init_provider_client = __esm({
106770
107643
  toolCalls: response.toolCalls
106771
107644
  };
106772
107645
  };
107646
+ runtimeProviderInvoker = null;
106773
107647
  invokeRuntimeProvider = async (input) => {
106774
107648
  if (input.signal?.aborted) throw input.signal.reason ?? new DOMException("Cancelled", "AbortError");
107649
+ if (runtimeProviderInvoker) return runtimeProviderInvoker(input);
106775
107650
  const policy2 = input.provider === "google" && Number(input.profileId) > 0 && !input.tools?.length && !input.onDelta && shouldUseGooglePublicEndpointForModel(input.config, input.model) ? googleEgressPolicyForModel(input.config.google_egress, input.model) : void 0;
106776
107651
  if (policy2) {
106777
107652
  const timeoutMs = Math.min(input.timeoutMs ?? policy2.deadline_seconds * 1e3, policy2.deadline_seconds * 1e3);
@@ -107762,7 +108637,7 @@ var init_reasoning_controls = __esm({
107762
108637
  return buildNoneReasoningControls();
107763
108638
  }
107764
108639
  seen.add(key);
107765
- const label = typeof record5.label === "string" && record5.label.trim() ? record5.label.trim() : void 0;
108640
+ const label2 = typeof record5.label === "string" && record5.label.trim() ? record5.label.trim() : void 0;
107766
108641
  const nested = normalizeReasoningControls({
107767
108642
  ...Object.fromEntries(Object.entries(record5).filter(([itemKey]) => !["key", "label"].includes(itemKey))),
107768
108643
  schema_version: 1
@@ -107772,7 +108647,7 @@ var init_reasoning_controls = __esm({
107772
108647
  }
107773
108648
  nestedControls.push({
107774
108649
  key,
107775
- ...label ? { label } : {},
108650
+ ...label2 ? { label: label2 } : {},
107776
108651
  ...nested
107777
108652
  });
107778
108653
  }
@@ -107937,24 +108812,170 @@ var init_model_controller_provenance = __esm({
107937
108812
  }
107938
108813
  });
107939
108814
 
108815
+ // lib/runtime/ai-schedule.ts
108816
+ function evaluateAiSchedule(schedule, defaults, nowMs2) {
108817
+ if (!isAiScheduleEnabled(schedule)) {
108818
+ return { shouldRun: true, nextChangeAtMs: null, message: "Schedule Disabled (Running 24/7)" };
108819
+ }
108820
+ try {
108821
+ const timeZone = schedule.timezone ?? defaults?.timezone;
108822
+ if (typeof timeZone !== "string" || !timeZone.trim()) throw new Error("Invalid schedule timezone");
108823
+ const nowWall = wallAt(timeZone, nowMs2);
108824
+ const windows = schedule.windows ?? defaults?.windows;
108825
+ if (!truthy(windows)) return { shouldRun: false, nextChangeAtMs: null, message: "No windows configured" };
108826
+ if (!Array.isArray(windows)) throw new Error("Invalid schedule windows");
108827
+ const nowDay = Math.floor(nowWall / DAY_MS) * DAY_MS;
108828
+ const candidates = [];
108829
+ for (let dayOffset = -6; dayOffset < 8; dayOffset += 1) {
108830
+ const dateMs = nowDay + dayOffset * DAY_MS;
108831
+ const weekday = (new Date(dateMs).getUTCDay() + 6) % 7;
108832
+ for (const window2 of windows) {
108833
+ if (!window2 || typeof window2 !== "object" || Array.isArray(window2)) {
108834
+ throw new Error("Invalid schedule window");
108835
+ }
108836
+ const startDay = dayNumber(window2.start_day);
108837
+ if (startDay === null || startDay !== weekday) continue;
108838
+ const start = parseClock(window2.start_time);
108839
+ const end = parseClock(window2.end_time);
108840
+ const endDay = dayNumber(window2.end_day);
108841
+ if (!start || !end || endDay === null) continue;
108842
+ const startWall = dateMs + (start[0] * 60 + start[1]) * MINUTE_MS;
108843
+ const daysDiff = ((endDay - startDay) % 7 + 7) % 7;
108844
+ const shifted = startWall + Math.round(daysDiff * DAY_MS);
108845
+ const endWall = Math.floor(shifted / DAY_MS) * DAY_MS + (end[0] * 60 + end[1]) * MINUTE_MS + shifted % MINUTE_MS;
108846
+ if (endWall <= startWall) continue;
108847
+ candidates.push([startWall, endWall]);
108848
+ }
108849
+ }
108850
+ candidates.sort((left, right) => left[0] - right[0]);
108851
+ const active = candidates.find(([start, end]) => start <= nowWall && nowWall < end);
108852
+ if (active) {
108853
+ const endsAt = instantOfWall(timeZone, active[1]);
108854
+ return { shouldRun: true, nextChangeAtMs: endsAt, message: `Active until ${label(timeZone, endsAt, active[1])}` };
108855
+ }
108856
+ const next = candidates.find(([start]) => start > nowWall);
108857
+ if (next) {
108858
+ const startsAt = instantOfWall(timeZone, next[0]);
108859
+ return { shouldRun: false, nextChangeAtMs: startsAt, message: `Paused until ${label(timeZone, startsAt, next[0])}` };
108860
+ }
108861
+ return { shouldRun: false, nextChangeAtMs: null, message: "No future schedule found" };
108862
+ } catch {
108863
+ return { shouldRun: true, nextChangeAtMs: null, message: "Schedule Error (Defaulting to Active)" };
108864
+ }
108865
+ }
108866
+ var MINUTE_MS, DAY_MS, truthy, isAiScheduleEnabled, formatters, formatterFor, wallAt, offsetAt, instantOfWall, digitValue, clockPart, dayNumber, parseClock, label;
108867
+ var init_ai_schedule = __esm({
108868
+ "lib/runtime/ai-schedule.ts"() {
108869
+ "use strict";
108870
+ init_define_VTX_EXO_POLICY();
108871
+ init_define_VTX_GROK_POLICY();
108872
+ init_define_VTX_PI_MODEL_POLICY();
108873
+ MINUTE_MS = 6e4;
108874
+ DAY_MS = 864e5;
108875
+ truthy = (value) => {
108876
+ if (Array.isArray(value)) return value.length > 0;
108877
+ if (value && typeof value === "object") return Object.keys(value).length > 0;
108878
+ return Boolean(value);
108879
+ };
108880
+ isAiScheduleEnabled = (schedule) => Boolean(schedule) && typeof schedule === "object" && truthy(schedule.enabled);
108881
+ formatters = /* @__PURE__ */ new Map();
108882
+ formatterFor = (timeZone) => {
108883
+ let formatter = formatters.get(timeZone);
108884
+ if (!formatter) {
108885
+ formatter = new Intl.DateTimeFormat("en-US", {
108886
+ timeZone,
108887
+ hourCycle: "h23",
108888
+ year: "numeric",
108889
+ month: "numeric",
108890
+ day: "numeric",
108891
+ hour: "numeric",
108892
+ minute: "numeric",
108893
+ second: "numeric"
108894
+ });
108895
+ const resolved = formatter.resolvedOptions().timeZone;
108896
+ if (resolved !== timeZone && resolved.toLowerCase() === timeZone.toLowerCase() || timeZone === timeZone.toLowerCase()) {
108897
+ throw new RangeError(`Invalid time zone: ${timeZone}`);
108898
+ }
108899
+ formatters.set(timeZone, formatter);
108900
+ }
108901
+ return formatter;
108902
+ };
108903
+ wallAt = (timeZone, instantMs) => {
108904
+ const parts = {};
108905
+ for (const part of formatterFor(timeZone).formatToParts(new Date(instantMs))) {
108906
+ if (part.type !== "literal") parts[part.type] = Number(part.value);
108907
+ }
108908
+ const wholeSecond = Math.floor(instantMs / 1e3) * 1e3;
108909
+ return Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second) + (instantMs - wholeSecond);
108910
+ };
108911
+ offsetAt = (timeZone, instantMs) => wallAt(timeZone, instantMs) - instantMs;
108912
+ instantOfWall = (timeZone, wallMs) => {
108913
+ const before = offsetAt(timeZone, wallMs - DAY_MS);
108914
+ if (offsetAt(timeZone, wallMs - before) === before) return wallMs - before;
108915
+ const after = offsetAt(timeZone, wallMs + DAY_MS);
108916
+ if (offsetAt(timeZone, wallMs - after) === after) return wallMs - after;
108917
+ return wallMs - before;
108918
+ };
108919
+ digitValue = (char) => {
108920
+ let start = char.codePointAt(0);
108921
+ while (start > 0 && new RegExp("\\p{Nd}", "u").test(String.fromCodePoint(start - 1))) start -= 1;
108922
+ return (char.codePointAt(0) - start) % 10;
108923
+ };
108924
+ clockPart = (text) => {
108925
+ const match = new RegExp("^\\s*([+-]?)(\\p{Nd}+(?:_\\p{Nd}+)*)\\s*$", "u").exec(text);
108926
+ if (!match) return null;
108927
+ const value = [...match[2].replace(/_/g, "")].reduce((total, char) => total * 10 + digitValue(char), 0);
108928
+ return match[1] === "-" ? -value : value;
108929
+ };
108930
+ dayNumber = (value) => {
108931
+ if (typeof value === "boolean") return Number(value);
108932
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
108933
+ };
108934
+ parseClock = (value) => {
108935
+ if (typeof value !== "string") return null;
108936
+ const parts = value.split(":");
108937
+ if (parts.length !== 2) return null;
108938
+ const hour = clockPart(parts[0]);
108939
+ const minute = clockPart(parts[1]);
108940
+ if (hour === null || minute === null || hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;
108941
+ return [hour, minute];
108942
+ };
108943
+ label = (timeZone, instantMs, wallMs) => {
108944
+ const wall = new Date(wallMs);
108945
+ const day = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][wall.getUTCDay()];
108946
+ const clock = `${String(wall.getUTCHours()).padStart(2, "0")}:${String(wall.getUTCMinutes()).padStart(2, "0")}`;
108947
+ const zone = new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "short" }).formatToParts(new Date(instantMs)).find((part) => part.type === "timeZoneName")?.value || timeZone;
108948
+ return `${day} ${clock} ${zone}`;
108949
+ };
108950
+ }
108951
+ });
108952
+
107940
108953
  // lib/runtime/local-analysis.ts
107941
108954
  var local_analysis_exports = {};
107942
108955
  __export(local_analysis_exports, {
107943
108956
  ClientRuntimeAgentInstructionEmpty: () => ClientRuntimeAgentInstructionEmpty,
108957
+ ClientRuntimeAiSchedulePaused: () => ClientRuntimeAiSchedulePaused,
108958
+ ClientRuntimeAiScheduleStillActive: () => ClientRuntimeAiScheduleStillActive,
108959
+ ServerDispatchAccountStateStale: () => ServerDispatchAccountStateStale,
108960
+ aiOrderSlippageFraction: () => aiOrderSlippageFraction,
107944
108961
  buildLocalRuntimePromptArtifacts: () => buildLocalRuntimePromptArtifacts,
107945
108962
  buildPromptBehaviorStateArtifacts: () => buildPromptBehaviorStateArtifacts,
107946
108963
  buildPromptBehaviorStateValue: () => buildPromptBehaviorStateValue,
107947
108964
  derivePromptVisibleSections: () => derivePromptVisibleSections,
107948
108965
  isClientRuntimeAgentInstructionEmpty: () => isClientRuntimeAgentInstructionEmpty,
108966
+ isClientRuntimeAiSchedulePaused: () => isClientRuntimeAiSchedulePaused,
108967
+ isClientRuntimeAiScheduleStillActive: () => isClientRuntimeAiScheduleStillActive,
108968
+ isServerDispatchAccountStateStale: () => isServerDispatchAccountStateStale,
107949
108969
  resolveEffectivePromptRuntimeProvenance: () => resolveEffectivePromptRuntimeProvenance,
107950
108970
  resolveExternalInferenceRuntimeTimeoutSeconds: () => resolveExternalInferenceRuntimeTimeoutSeconds,
107951
108971
  resolveLocalRuntimeSecretProvider: () => resolveLocalRuntimeSecretProvider,
107952
108972
  resolveLocalRuntimeSecretRequirements: () => resolveLocalRuntimeSecretRequirements,
107953
108973
  resolveNativeValuesForModel: () => resolveNativeValuesForModel,
107954
108974
  runLocalRuntimeCycle: () => runLocalRuntimeCycle,
108975
+ serverPerpDexScope: () => serverPerpDexScope,
107955
108976
  summarizeReviewFailureExplanation: () => summarizeReviewFailureExplanation
107956
108977
  });
107957
- var normalizeNativeValues, assertPromptModelController, resolveRuntimeReasoningControlsForModel, modelScopedKey, resolveNativeValuesForModel, observeRuntimeCycle, observeAccountState, ClientRuntimeAgentInstructionEmpty, isClientRuntimeAgentInstructionEmpty, buildClaimedAgentRuntimeCycleResult, resolveRuntimeMarginModePreference, resolveRuntimeLeveragePreference, resolveBooleanSetting2, CLIENT_RUNTIME_LIVE_CONTEXT_UNAVAILABLE_ERRORS, BEHAVIOR_STATE_UNAVAILABLE_ERROR, CriticalBehaviorStateError, clampNumber, resolveExternalInferenceRuntimeTimeoutSeconds, toPositiveFinite3, compactSnippet, hashRuntimeDebugText, EFFECTIVE_INPUT_MANIFEST_SCHEMA_VERSION, PROMPT_ASSEMBLY_DEFINITION_VERSION, RUNTIME_SOURCE_DEFINITION_VERSION, DECISION_INPUT_DEFINITION_VERSION, CONTEXT_ENVELOPE_DEFINITION_VERSION, CLIENT_RUNTIME_SOURCE_VERSION, CLIENT_MAIN_PROMPT_RENDERER, SERVER_MAIN_PROMPT_RENDERER, CLIENT_REVIEW_PROMPT_RENDERER, SERVER_REVIEW_PROMPT_RENDERER, resolveEffectivePromptRuntimeProvenance, canonicalManifestValue, canonicalManifestJson, sha256ManifestHex, effectiveInputComponent, buildEffectiveInputManifest, derivePromptVisibleSections, decodeDecisionTracePayload, extractDecisionSymbolTrace, formatDecisionSymbolTrace, formatActionableDecisionSymbols, normalizePromptSymbols, JSON_MODEL_OUTPUT_FENCE_PATTERN, STRICT_MODEL_OUTPUT_KEYS, currentModelOutputValidationErrors, currentModelOutputMatchesContract, projectCurrentModelDecisionContent, modelOutputValidationError, buildMissingDecisionHold, buildDecisionPlansFromResponse, buildReviewCandidatePlansFromResponse, forceUnavailableSymbolsToHold, extractTemplatePlaceholders, requireReviewPrimaryUserPromptTemplate, containsCompetingBehaviorStateMarkup, containsCompetingMainBehaviorStateMarkup, requireStrategyReviewTemplates, preflightStrategyReviewPromptContract, extractRuntimeReasoningSelection, isAbortError, isRuntimeTimeoutAbortReason, REVIEW_FAILURE_SENSITIVE_DB_TOKENS, REVIEW_FAILURE_SCHEMA_DISCLOSURE_TOKENS, REVIEW_FAILURE_SQL_STATEMENT_PATTERN, REVIEW_FAILURE_TRACEBACK_PATTERN, isSensitiveReviewFailureDetail, summarizeReviewFailureExplanation, buildReviewFailureHoldReason, createAbortError2, settleRuntimeCyclePromise, unwrapRuntimeCycleSettledResult, normalizeDecisionLabel, normalizeRuntimeSymbol, PromptContractFetchError, isLivePromptContextUnavailableError, buildFailClosedHoldResult, buildPromptConstructionFailureResult, buildLivePromptContextUnavailableFailureResult, pickFirstString, normalizeRuntimeStringList, requirePositiveRuntimeLimit, resolveRuntimeTradingScope, normalizeRuntimeProviderAlias, resolveCatalogModelEntry, createClientRuntimeProviderSetupError, createProviderSetupRejectionError, evaluateModelEligibility, extractReviewRuntimeReasoningSelection, isUsableRuntimeSettingsFallback, resolveRuntimeSettings, formatRuntimeDurationMs, normalizeRuntimeTimingNote, appendRuntimeTimingEvent, reportPromptParityTelemetry, resolveLocalRuntimeSecretRequirements, resolveLocalRuntimeSecretProvider, toEpochSeconds, REQUIRED_AI_INDICATOR_KEYS, TIMEFRAME_ORDER, sortTradingTimeframes, normalizePromptIdentityTimestamp, resolveCompletedCandleBoundary, normalizeAiIndicatorFlags, getPromptLookback, computeSma, computeEma, computeRsi, computeAtr, computeMacd, computeBollingerBands, getIndicatorValue, buildPromptCandlesComponent, buildPromptCandlesPayload, formatUtcTimestampWithWeekday, formatUtcNowWithWeekday, formatPromptPrice, computePromptUnitsUsedForPosition, buildPromptAccountStatusValue, buildPromptPositionsValue, sanitizePromptSymbol, fillTimeMs, fillSize, normalizeFillSide, normalizeFillOrderId, normalizeFillRiskOrderIdentity, fillStartPosition, fillSortValue, sortFillsAscending, sortFillsDescending, inferEntryFromStartPositionChain, inferEntryFromCurrentPositionSize, calculateCurrentPositionState, calculatePositionEntryTimes, countPositionAdds, latestPositionRiskIncreaseTimeMs, latestPositionRiskIncreaseEvidence, compactPromptRiskIncrease, roundPositiveDecimalHalfUp, compactPromptLastTrade, ENTRY_TRADE_FIELDS, PREVIOUS_DECISION_FIELDS, PREVIOUS_CAMPAIGN_FIELDS, hasExactKeys2, isNonnegativeInteger, isFiniteNumber, isExactEnumString, isNonemptyPromptText, isCampaignReasoningText, projectBackendDecisionMemoryItem, requireRenderedStrategyBehaviorState, canonicalFillSideForRiskEvidence, isValidRiskEvidenceFill, isValidCampaignBoundaryFill, normalizeFillHistoryCoverage, resolveFillHistoryCoverageForSymbol, buildPromptBehaviorStateArtifacts, buildPromptBehaviorStateValue, shouldCompareServerPromptParity, buildLocalRuntimePromptArtifacts, createRuntimeId2, asRecord, buildRuntimeConstraintRejectionMessage, hasDeviceProviderSecret, requireNonEmptyStringField, requirePositiveIntegerField, compareContractVersion, parsePromptContractTimestamp, assertPromptContractCompatibility, assertPromptContractTemporalBounds, sleepWithAbort, requireAgentPollRetryAfterMs, materializeClaimedClientRuntimeAgentInstruction, attachClientRuntimeErrorProvenance, isExternalRuntimeProviderResult, invokeClientExternalInference, assertPromptContractBindingTupleEqual, assertPromptContractScopeMatch, fetchBoundPromptContracts, formatTemplateWithNamedVariables, runLocalRuntimeCycle;
108978
+ var normalizeNativeValues, assertPromptModelController, resolveRuntimeReasoningControlsForModel, modelScopedKey, resolveNativeValuesForModel, observeRuntimeCycle, observeAccountState, aiOrderSlippageFraction, serverPerpDexScope, serverBehaviorStateArtifacts, ClientRuntimeAgentInstructionEmpty, isClientRuntimeAgentInstructionEmpty, ClientRuntimeAiSchedulePaused, ClientRuntimeAiScheduleStillActive, isClientRuntimeAiScheduleStillActive, ServerDispatchAccountStateStale, isServerDispatchAccountStateStale, isClientRuntimeAiSchedulePaused, buildClaimedAgentRuntimeCycleResult, resolveRuntimeMarginModePreference, resolveRuntimeLeveragePreference, resolveBooleanSetting2, CLIENT_RUNTIME_LIVE_CONTEXT_UNAVAILABLE_ERRORS, BEHAVIOR_STATE_UNAVAILABLE_ERROR, CriticalBehaviorStateError, clampNumber, resolveExternalInferenceRuntimeTimeoutSeconds, toPositiveFinite3, compactSnippet, hashRuntimeDebugText, EFFECTIVE_INPUT_MANIFEST_SCHEMA_VERSION, PROMPT_ASSEMBLY_DEFINITION_VERSION, RUNTIME_SOURCE_DEFINITION_VERSION, DECISION_INPUT_DEFINITION_VERSION, CONTEXT_ENVELOPE_DEFINITION_VERSION, CLIENT_RUNTIME_SOURCE_VERSION, CLIENT_MAIN_PROMPT_RENDERER, SERVER_MAIN_PROMPT_RENDERER, CLIENT_REVIEW_PROMPT_RENDERER, SERVER_REVIEW_PROMPT_RENDERER, resolveEffectivePromptRuntimeProvenance, canonicalManifestValue, canonicalManifestJson, sha256ManifestHex, effectiveInputComponent, buildEffectiveInputManifest, derivePromptVisibleSections, decodeDecisionTracePayload, extractDecisionSymbolTrace, formatDecisionSymbolTrace, formatActionableDecisionSymbols, normalizePromptSymbols, JSON_MODEL_OUTPUT_FENCE_PATTERN, STRICT_MODEL_OUTPUT_KEYS, currentModelOutputValidationErrors, currentModelOutputMatchesContract, projectCurrentModelDecisionContent, modelOutputValidationError, buildMissingDecisionHold, buildDecisionPlansFromResponse, buildReviewCandidatePlansFromResponse, forceUnavailableSymbolsToHold, extractTemplatePlaceholders, requireReviewPrimaryUserPromptTemplate, containsCompetingBehaviorStateMarkup, containsCompetingMainBehaviorStateMarkup, requireStrategyReviewTemplates, preflightStrategyReviewPromptContract, extractRuntimeReasoningSelection, isAbortError, isRuntimeTimeoutAbortReason, REVIEW_FAILURE_SENSITIVE_DB_TOKENS, REVIEW_FAILURE_SCHEMA_DISCLOSURE_TOKENS, REVIEW_FAILURE_SQL_STATEMENT_PATTERN, REVIEW_FAILURE_TRACEBACK_PATTERN, isSensitiveReviewFailureDetail, summarizeReviewFailureExplanation, buildReviewFailureHoldReason, createAbortError2, settleRuntimeCyclePromise, unwrapRuntimeCycleSettledResult, normalizeDecisionLabel, normalizeRuntimeSymbol, PromptContractFetchError, isLivePromptContextUnavailableError, buildFailClosedHoldResult, buildPromptConstructionFailureResult, buildLivePromptContextUnavailableFailureResult, pickFirstString, normalizeRuntimeStringList, requirePositiveRuntimeLimit, resolveRuntimeTradingScope, normalizeRuntimeProviderAlias, resolveCatalogModelEntry, createClientRuntimeProviderSetupError, createProviderSetupRejectionError, evaluateModelEligibility, extractReviewRuntimeReasoningSelection, isUsableRuntimeSettingsFallback, resolveRuntimeSettings, formatRuntimeDurationMs, normalizeRuntimeTimingNote, appendRuntimeTimingEvent, reportPromptParityTelemetry, resolveLocalRuntimeSecretRequirements, resolveLocalRuntimeSecretProvider, toEpochSeconds, REQUIRED_AI_INDICATOR_KEYS, TIMEFRAME_ORDER, sortTradingTimeframes, normalizePromptIdentityTimestamp, resolveCompletedCandleBoundary, normalizeAiIndicatorFlags, getPromptLookback, computeSma, computeEma, computeRsi, computeAtr, computeMacd, computeBollingerBands, getIndicatorValue, buildPromptCandlesComponent, buildPromptCandlesPayload, formatUtcTimestampWithWeekday, formatUtcNowWithWeekday, formatPromptPrice, computePromptUnitsUsedForPosition, buildPromptAccountStatusValue, buildPromptPositionsValue, sanitizePromptSymbol, fillTimeMs, fillSize, normalizeFillSide, normalizeFillOrderId, normalizeFillRiskOrderIdentity, fillStartPosition, fillSortValue, sortFillsAscending, sortFillsDescending, inferEntryFromStartPositionChain, inferEntryFromCurrentPositionSize, calculateCurrentPositionState, calculatePositionEntryTimes, countPositionAdds, latestPositionRiskIncreaseTimeMs, latestPositionRiskIncreaseEvidence, compactPromptRiskIncrease, roundPositiveDecimalHalfUp, compactPromptLastTrade, ENTRY_TRADE_FIELDS, PREVIOUS_DECISION_FIELDS, PREVIOUS_CAMPAIGN_FIELDS, hasExactKeys2, isNonnegativeInteger, isFiniteNumber, isExactEnumString, isNonemptyPromptText, isCampaignReasoningText, projectBackendDecisionMemoryItem, requireRenderedStrategyBehaviorState, canonicalFillSideForRiskEvidence, isValidRiskEvidenceFill, isValidCampaignBoundaryFill, normalizeFillHistoryCoverage, resolveFillHistoryCoverageForSymbol, buildPromptBehaviorStateArtifacts, buildPromptBehaviorStateValue, shouldCompareServerPromptParity, buildLocalRuntimePromptArtifacts, createRuntimeId2, asRecord, buildRuntimeConstraintRejectionMessage, hasDeviceProviderSecret, requireNonEmptyStringField, requirePositiveIntegerField, compareContractVersion, parsePromptContractTimestamp, assertPromptContractCompatibility, assertPromptContractTemporalBounds, sleepWithAbort, requireAgentPollRetryAfterMs, materializeClaimedClientRuntimeAgentInstruction, attachClientRuntimeErrorProvenance, isExternalRuntimeProviderResult, invokeClientExternalInference, assertPromptContractBindingTupleEqual, assertPromptContractScopeMatch, fetchBoundPromptContracts, formatTemplateWithNamedVariables, runLocalRuntimeCycle;
107958
108979
  var init_local_analysis = __esm({
107959
108980
  "lib/runtime/local-analysis.ts"() {
107960
108981
  "use strict";
@@ -107994,6 +109015,7 @@ var init_local_analysis = __esm({
107994
109015
  init_hyperliquid_market_symbol();
107995
109016
  init_external_inference_contract();
107996
109017
  init_model_controller_provenance();
109018
+ init_ai_schedule();
107997
109019
  normalizeNativeValues = (value) => {
107998
109020
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
107999
109021
  const entries = {};
@@ -108062,6 +109084,29 @@ var init_local_analysis = __esm({
108062
109084
  }
108063
109085
  });
108064
109086
  };
109087
+ aiOrderSlippageFraction = (config2, executionMode) => executionMode === "server" ? Number(config2.server_ai_max_slippage) / 100 : clampNumber(void 0, config2.default_max_slippage) / 100;
109088
+ serverPerpDexScope = (symbols, executionMode) => {
109089
+ if (executionMode !== "server") return void 0;
109090
+ const dexes = /* @__PURE__ */ new Set();
109091
+ for (const raw of symbols) {
109092
+ if (!String(raw ?? "").trim()) continue;
109093
+ if (!normalizeHyperliquidMarketSymbol(raw)) return void 0;
109094
+ dexes.add(getHyperliquidMarketDex(raw));
109095
+ }
109096
+ return dexes.size > 0 ? [...dexes].filter(Boolean) : void 0;
109097
+ };
109098
+ serverBehaviorStateArtifacts = (derived) => {
109099
+ const rendered = derived.behavior_state;
109100
+ if (derived.behavior_state_owner !== "server" || typeof rendered !== "string" || !rendered.trim()) {
109101
+ throw new Error("The server did not build this Server bot's behavior state.");
109102
+ }
109103
+ const preMemory = derived.behavior_state_pre_memory;
109104
+ return {
109105
+ rendered,
109106
+ preMemoryRendered: typeof preMemory === "string" && preMemory.trim() ? preMemory : rendered,
109107
+ policyState: asRecord(derived.behavior_policy_state)
109108
+ };
109109
+ };
108065
109110
  ClientRuntimeAgentInstructionEmpty = class extends Error {
108066
109111
  constructor(retryAfterMs) {
108067
109112
  super("No Agent instruction is currently queued for this Client runtime.");
@@ -108070,6 +109115,31 @@ var init_local_analysis = __esm({
108070
109115
  }
108071
109116
  };
108072
109117
  isClientRuntimeAgentInstructionEmpty = (value) => value instanceof ClientRuntimeAgentInstructionEmpty;
109118
+ ClientRuntimeAiSchedulePaused = class extends Error {
109119
+ constructor(input) {
109120
+ super(input.message);
109121
+ this.name = "ClientRuntimeAiSchedulePaused";
109122
+ this.nextChangeAtMs = input.nextChangeAtMs;
109123
+ this.closeOnStop = input.closeOnStop;
109124
+ this.settings = input.settings;
109125
+ this.config = input.config;
109126
+ }
109127
+ };
109128
+ ClientRuntimeAiScheduleStillActive = class extends Error {
109129
+ constructor() {
109130
+ super("The AI schedule still lets this bot run.");
109131
+ this.name = "ClientRuntimeAiScheduleStillActive";
109132
+ }
109133
+ };
109134
+ isClientRuntimeAiScheduleStillActive = (value) => value instanceof ClientRuntimeAiScheduleStillActive;
109135
+ ServerDispatchAccountStateStale = class extends Error {
109136
+ constructor() {
109137
+ super("Server account context could not be rebuilt inside the dispatch freshness window");
109138
+ this.name = "ServerDispatchAccountStateStale";
109139
+ }
109140
+ };
109141
+ isServerDispatchAccountStateStale = (value) => value instanceof Error && value.name === "ServerDispatchAccountStateStale";
109142
+ isClientRuntimeAiSchedulePaused = (value) => value instanceof ClientRuntimeAiSchedulePaused;
108073
109143
  buildClaimedAgentRuntimeCycleResult = async (input) => {
108074
109144
  const { config: config2, settings, instruction, runtimeInput, signal } = input;
108075
109145
  const binding = runtimeInput.runtimeContractBinding;
@@ -108106,6 +109176,7 @@ var init_local_analysis = __esm({
108106
109176
  tickerPrice: 0,
108107
109177
  leverage,
108108
109178
  aggregatePerpDexs: true,
109179
+ perpDexScope: serverPerpDexScope(symbols, runtimeInput.executionMode),
108109
109180
  feeSymbols: symbols,
108110
109181
  includeEffectiveTakerRate: true,
108111
109182
  sessionId: String(runtimeInput.sessionId || "").trim(),
@@ -108200,11 +109271,11 @@ var init_local_analysis = __esm({
108200
109271
  walletAddress,
108201
109272
  lastPrice: price,
108202
109273
  unitSizeUsdc: tradeSize,
108203
- sizeDecimals: Number(config2.default_size_decimals),
108204
109274
  leverage,
108205
109275
  marginMode,
108206
109276
  closePositionDelaySeconds: Number(config2.client_runtime_close_position_delay_seconds),
108207
- maxSlippageFraction: clampNumber(void 0, config2.default_max_slippage) / 100,
109277
+ closePositionFreshnessPollMs: Number(config2.close_position_freshness_poll_milliseconds),
109278
+ maxSlippageFraction: aiOrderSlippageFraction(config2, runtimeInput.executionMode),
108208
109279
  ...buildRuntimeSlTpExecutionSettings(settings, config2)
108209
109280
  };
108210
109281
  };
@@ -109554,14 +110625,14 @@ ${reviewOutputContract}`;
109554
110625
  const normalized = String(value || "").replace(/\s+/g, " ").trim();
109555
110626
  return normalized.length > 0 ? normalized : null;
109556
110627
  };
109557
- appendRuntimeTimingEvent = (label, startedAtMs, note, outcome) => {
110628
+ appendRuntimeTimingEvent = (label2, startedAtMs, note, outcome) => {
109558
110629
  const parts = [`duration=${formatRuntimeDurationMs(Date.now() - startedAtMs)}`];
109559
110630
  const normalizedNote = normalizeRuntimeTimingNote(note);
109560
110631
  if (normalizedNote) {
109561
110632
  parts.push(normalizedNote);
109562
110633
  }
109563
110634
  appendRuntimeDebugEvent({
109564
- label,
110635
+ label: label2,
109565
110636
  note: parts.join(" "),
109566
110637
  outcome
109567
110638
  });
@@ -111040,17 +112111,17 @@ ${behaviorJson}
111040
112111
  }
111041
112112
  return hasValue(secrets.providerKeys[provider]) || hasSlotValue(secrets.providerKeySlots?.[provider]);
111042
112113
  };
111043
- requireNonEmptyStringField = (value, label) => {
112114
+ requireNonEmptyStringField = (value, label2) => {
111044
112115
  const normalized = String(value ?? "").trim();
111045
112116
  if (!normalized) {
111046
- throw new Error(`Missing required prompt contract field: ${label}`);
112117
+ throw new Error(`Missing required prompt contract field: ${label2}`);
111047
112118
  }
111048
112119
  return normalized;
111049
112120
  };
111050
- requirePositiveIntegerField = (value, label) => {
112121
+ requirePositiveIntegerField = (value, label2) => {
111051
112122
  const parsed = Number(value);
111052
112123
  if (!Number.isFinite(parsed) || parsed <= 0) {
111053
- throw new Error(`Missing required positive prompt contract field: ${label}`);
112124
+ throw new Error(`Missing required positive prompt contract field: ${label2}`);
111054
112125
  }
111055
112126
  return Math.trunc(parsed);
111056
112127
  };
@@ -111800,6 +112871,25 @@ ${behaviorJson}
111800
112871
  }
111801
112872
  const config2 = configResult.value;
111802
112873
  const runtimeSettings = runtimeSettingsResult.value;
112874
+ const aiSchedule = runtimeSettings.settings.ai_schedule ?? config2.default_ai_schedule;
112875
+ if (!input.ignoreAiSchedule) {
112876
+ if (isAiScheduleEnabled(aiSchedule)) {
112877
+ const scheduleState = evaluateAiSchedule(aiSchedule, config2.default_ai_schedule, Date.now());
112878
+ if (!scheduleState.shouldRun) {
112879
+ throw new ClientRuntimeAiSchedulePaused({
112880
+ message: scheduleState.message,
112881
+ nextChangeAtMs: scheduleState.nextChangeAtMs,
112882
+ closeOnStop: isAiScheduleEnabled({ enabled: aiSchedule?.close_on_stop }),
112883
+ settings: runtimeSettings.settings,
112884
+ config: config2
112885
+ });
112886
+ }
112887
+ input.onAiScheduleWindowActive?.(scheduleState.nextChangeAtMs);
112888
+ } else {
112889
+ input.onAiScheduleOff?.();
112890
+ }
112891
+ if (input.aiScheduleCheckOnly) throw new ClientRuntimeAiScheduleStillActive();
112892
+ }
111803
112893
  input = {
111804
112894
  ...input,
111805
112895
  secrets: await ClientVault.refreshProviderSecretsForVersion(
@@ -111977,11 +113067,11 @@ ${behaviorJson}
111977
113067
  walletAddress: String(settings.hyperliquid_wallet_address || "").trim() || void 0,
111978
113068
  lastPrice: 0,
111979
113069
  unitSizeUsdc: clampNumber(promptKnobs2.trade_size, config2.default_ai_trade_amount),
111980
- sizeDecimals: Number(config2.default_size_decimals),
111981
113070
  leverage: clampNumber(promptKnobs2.leverage, config2.default_leverage),
111982
113071
  marginMode: resolveRuntimeMarginModePreference(settings, config2),
111983
113072
  closePositionDelaySeconds: Number(config2.client_runtime_close_position_delay_seconds),
111984
- maxSlippageFraction: clampNumber(void 0, config2.default_max_slippage) / 100,
113073
+ closePositionFreshnessPollMs: Number(config2.close_position_freshness_poll_milliseconds),
113074
+ maxSlippageFraction: aiOrderSlippageFraction(config2, input.executionMode),
111985
113075
  ...buildRuntimeSlTpExecutionSettings(settings, config2),
111986
113076
  executionFreshnessMaxAgeSeconds: requirePositiveIntegerField(
111987
113077
  promptKnobs2.execution_decision_max_age_seconds,
@@ -112002,6 +113092,8 @@ ${behaviorJson}
112002
113092
  let _latestPerpDexAccounts = null;
112003
113093
  let latestPositions = [];
112004
113094
  let _accountStateResolvedAtMs = null;
113095
+ let _accountStateFetchedAtMs = null;
113096
+ let _accountStateSource = null;
112005
113097
  const getLatestAccountEquity = () => _latestAccountEquity;
112006
113098
  const getLatestTotalUnrealizedPnl = () => _latestTotalUnrealizedPnl;
112007
113099
  const getAccountStateResolvedAtMs = () => _accountStateResolvedAtMs;
@@ -112015,8 +113107,12 @@ ${behaviorJson}
112015
113107
  tickerPrice: 0,
112016
113108
  leverage: resolveRuntimeLeveragePreference(settings, config2),
112017
113109
  aggregatePerpDexs: true,
113110
+ perpDexScope: serverPerpDexScope(symbols, input.executionMode),
112018
113111
  feeSymbols: symbols,
112019
113112
  includeEffectiveTakerRate: true,
113113
+ // A Server bot's cycle reads the account authoritatively, as the Python
113114
+ // Server loop does: never a cached or rate-limit-stale observation.
113115
+ forceRefresh: input.executionMode === "server",
112020
113116
  sessionId,
112021
113117
  signal: timeoutController.signal
112022
113118
  }).then((state2) => {
@@ -112024,6 +113120,8 @@ ${behaviorJson}
112024
113120
  latestPositions = state2.positions;
112025
113121
  const resolvedEquity = Number(state2.accountSummary?.account_value) || 0;
112026
113122
  _accountStateResolvedAtMs = Date.now();
113123
+ _accountStateFetchedAtMs = state2.evidence?.fetchedAtMs ?? _accountStateResolvedAtMs;
113124
+ _accountStateSource = state2.evidence?.source ?? null;
112027
113125
  if (resolvedEquity > 0) {
112028
113126
  _latestAccountEquity = resolvedEquity;
112029
113127
  _latestPerpDexAccounts = state2.accountSummary?.perp_dex_accounts ?? null;
@@ -112050,7 +113148,8 @@ ${behaviorJson}
112050
113148
  });
112051
113149
  const browserAccountStatePromise = settleRuntimeCyclePromise(browserAccountStateTask);
112052
113150
  const browserUserFillsStartedAtMs = Date.now();
112053
- const browserUserFillsTask = fetchBrowserUserFills({
113151
+ const serverOwnsBehaviorState = input.executionMode === "server";
113152
+ const browserUserFillsTask = serverOwnsBehaviorState ? Promise.resolve(null) : fetchBrowserUserFills({
112054
113153
  publicRequestPriority: "trading_analysis",
112055
113154
  config: config2,
112056
113155
  walletAddress,
@@ -112085,7 +113184,7 @@ ${behaviorJson}
112085
113184
  getLatestAccountEquity,
112086
113185
  getLatestTotalUnrealizedPnl,
112087
113186
  getAccountStateResolvedAtMs,
112088
- async () => {
113187
+ serverOwnsBehaviorState ? void 0 : async () => {
112089
113188
  const result = unwrapRuntimeCycleSettledResult(await browserUserFillsPromise);
112090
113189
  return { fills: result.rawFills, evidence: result.evidence };
112091
113190
  },
@@ -112113,12 +113212,14 @@ ${behaviorJson}
112113
113212
  sortedTimeframes.map(async (marketTimeframe) => [
112114
113213
  marketTimeframe,
112115
113214
  await getRecentCandles(
113215
+ // Indicators warm up on the same history as the Server loop's
113216
+ // (indicator_history.py); the prompt shows its lookback of it.
112116
113217
  Math.max(getPromptLookback(
112117
113218
  lookbackSettings,
112118
113219
  marketTimeframe,
112119
113220
  effectiveDefaultLookbacks,
112120
113221
  config2.max_analysis_lookback
112121
- ), 250),
113222
+ ), Number(config2.indicator_candle_count) || 250),
112122
113223
  "hyperliquid",
112123
113224
  marketSymbol,
112124
113225
  marketTimeframe,
@@ -112215,11 +113316,11 @@ ${behaviorJson}
112215
113316
  walletAddress: String(settings.hyperliquid_wallet_address || "").trim() || void 0,
112216
113317
  lastPrice: toPositiveFinite3(ticker.price) ?? 0,
112217
113318
  unitSizeUsdc: clampNumber(rejectedPromptKnobs.trade_size, config2.default_ai_trade_amount),
112218
- sizeDecimals: Number(config2.default_size_decimals),
112219
113319
  leverage: clampNumber(rejectedPromptKnobs.leverage, config2.default_leverage),
112220
113320
  marginMode: resolveRuntimeMarginModePreference(settings, config2),
112221
113321
  closePositionDelaySeconds: Number(config2.client_runtime_close_position_delay_seconds),
112222
- maxSlippageFraction: clampNumber(void 0, config2.default_max_slippage) / 100,
113322
+ closePositionFreshnessPollMs: Number(config2.close_position_freshness_poll_milliseconds),
113323
+ maxSlippageFraction: aiOrderSlippageFraction(config2, input.executionMode),
112223
113324
  ...buildRuntimeSlTpExecutionSettings(settings, config2),
112224
113325
  executionFreshnessMaxAgeSeconds: requirePositiveIntegerField(
112225
113326
  rejectedPromptKnobs.execution_decision_max_age_seconds,
@@ -112244,8 +113345,8 @@ ${behaviorJson}
112244
113345
  positions
112245
113346
  ) ?? browserAccountState.accountSummary.total_unrealized_pnl
112246
113347
  };
112247
- tradeHistory = browserUserFills.tradeHistory;
112248
- if (input.mode === "trader" && browserUserFills.evidence?.source !== "live") {
113348
+ tradeHistory = browserUserFills?.tradeHistory ?? [];
113349
+ if (input.mode === "trader" && !serverOwnsBehaviorState && browserUserFills?.evidence?.source !== "live") {
112249
113350
  throw new CriticalBehaviorStateError();
112250
113351
  }
112251
113352
  const metadataSymbol = requireNonEmptyStringField(promptMetadata.symbol, "symbol");
@@ -112540,11 +113641,11 @@ ${behaviorJson}
112540
113641
  if (input.mode === "trader" && !behaviorMemoryEnabled) {
112541
113642
  throw new CriticalBehaviorStateError();
112542
113643
  }
112543
- const browserFillEvidence = browserUserFills.evidence;
113644
+ const browserFillEvidence = browserUserFills?.evidence;
112544
113645
  const behaviorFillCoverage = browserFillEvidence?.source === "live" && browserFillEvidence.exactTerminalFillEvidenceComplete === true ? "complete" : "capped";
112545
113646
  let behaviorStateArtifacts;
112546
113647
  try {
112547
- behaviorStateArtifacts = buildPromptBehaviorStateArtifacts({
113648
+ behaviorStateArtifacts = serverOwnsBehaviorState ? serverBehaviorStateArtifacts(derivedContext) : buildPromptBehaviorStateArtifacts({
112548
113649
  fills: tradeHistory,
112549
113650
  positions,
112550
113651
  symbols: promptSymbols,
@@ -112632,11 +113733,11 @@ ${stringifyPromptJson(candlesPayload)}
112632
113733
  walletAddress: walletAddress || void 0,
112633
113734
  lastPrice: 0,
112634
113735
  unitSizeUsdc: metadataTradeSize,
112635
- sizeDecimals: Number(config2.default_size_decimals),
112636
113736
  leverage: metadataLeverage,
112637
113737
  marginMode: resolveRuntimeMarginModePreference(settings, config2),
112638
113738
  closePositionDelaySeconds: Number(config2.client_runtime_close_position_delay_seconds),
112639
- maxSlippageFraction: clampNumber(void 0, config2.default_max_slippage) / 100,
113739
+ closePositionFreshnessPollMs: Number(config2.close_position_freshness_poll_milliseconds),
113740
+ maxSlippageFraction: aiOrderSlippageFraction(config2, input.executionMode),
112640
113741
  ...buildRuntimeSlTpExecutionSettings(settings, config2),
112641
113742
  executionFreshnessMaxAgeSeconds
112642
113743
  }
@@ -112758,11 +113859,11 @@ ${stringifyPromptJson(candlesPayload)}
112758
113859
  walletAddress: String(settings.hyperliquid_wallet_address || "").trim() || void 0,
112759
113860
  lastPrice: executionPrice,
112760
113861
  unitSizeUsdc: metadataTradeSize,
112761
- sizeDecimals: Number(config2.default_size_decimals),
112762
113862
  leverage: metadataLeverage,
112763
113863
  marginMode: resolveRuntimeMarginModePreference(settings, config2),
112764
113864
  closePositionDelaySeconds: Number(config2.client_runtime_close_position_delay_seconds),
112765
- maxSlippageFraction: clampNumber(void 0, config2.default_max_slippage) / 100,
113865
+ closePositionFreshnessPollMs: Number(config2.close_position_freshness_poll_milliseconds),
113866
+ maxSlippageFraction: aiOrderSlippageFraction(config2, input.executionMode),
112766
113867
  ...buildRuntimeSlTpExecutionSettings(settings, config2),
112767
113868
  executionFreshnessMaxAgeSeconds
112768
113869
  }
@@ -112788,11 +113889,11 @@ ${stringifyPromptJson(candlesPayload)}
112788
113889
  walletAddress: String(settings.hyperliquid_wallet_address || "").trim() || void 0,
112789
113890
  lastPrice: planPrice,
112790
113891
  unitSizeUsdc: metadataTradeSize,
112791
- sizeDecimals: Number(config2.default_size_decimals),
112792
113892
  leverage: metadataLeverage,
112793
113893
  marginMode,
112794
113894
  closePositionDelaySeconds: Number(config2.client_runtime_close_position_delay_seconds),
112795
- maxSlippageFraction: clampNumber(void 0, config2.default_max_slippage) / 100,
113895
+ closePositionFreshnessPollMs: Number(config2.close_position_freshness_poll_milliseconds),
113896
+ maxSlippageFraction: aiOrderSlippageFraction(config2, input.executionMode),
112796
113897
  ...buildRuntimeSlTpExecutionSettings(settings, config2),
112797
113898
  executionFreshnessMaxAgeSeconds
112798
113899
  };
@@ -112945,6 +114046,17 @@ ${stringifyPromptJson(candlesPayload)}
112945
114046
  note: `lane=main seconds=${requestTimeoutSeconds}`
112946
114047
  });
112947
114048
  }
114049
+ const dispatchAccountStateAgeMs = _accountStateFetchedAtMs === null ? null : Date.now() - _accountStateFetchedAtMs;
114050
+ appendRuntimeDebugEvent({
114051
+ label: "account_state_dispatch_age",
114052
+ note: `account_state_age_ms=${dispatchAccountStateAgeMs ?? "unresolved"} source=${_accountStateSource ?? "unknown"}`
114053
+ });
114054
+ if (input.executionMode === "server" && !allPromptSymbolsUnavailable && (dispatchAccountStateAgeMs === null || dispatchAccountStateAgeMs > requirePositiveIntegerField(
114055
+ config2.server_dispatch_account_state_max_age_milliseconds,
114056
+ "server_dispatch_account_state_max_age_milliseconds"
114057
+ ))) {
114058
+ throw new ServerDispatchAccountStateStale();
114059
+ }
112948
114060
  const providerStartedAtMs = Date.now();
112949
114061
  observeRuntimeCycle(input, { stage: "model", reason_code: null });
112950
114062
  let selectedPrimaryModel = authoritativeModel;
@@ -113855,7 +114967,7 @@ ${reviewOutputContract}`;
113855
114967
  executionPlans: acceptedExecutionPlans
113856
114968
  });
113857
114969
  } catch (error48) {
113858
- if (isClientRuntimeAgentInstructionEmpty(error48)) {
114970
+ if (isClientRuntimeAgentInstructionEmpty(error48) || isClientRuntimeAiSchedulePaused(error48) || isClientRuntimeAiScheduleStillActive(error48)) {
113859
114971
  throw error48;
113860
114972
  }
113861
114973
  if (error48 instanceof PromptContractFetchError && error48.metadata) {
@@ -113886,8 +114998,8 @@ ${reviewOutputContract}`;
113886
114998
  const detail = error48 instanceof Error ? error48.message : String(error48);
113887
114999
  appendRuntimeTimingEvent("analysis_cycle_failed", cycleStartedAtMs, detail, "error");
113888
115000
  if (isAbortError(error48)) {
113889
- const abortReason = timeoutController.signal.reason;
113890
- if (isRuntimeTimeoutAbortReason(abortReason)) {
115001
+ const abortReason2 = timeoutController.signal.reason;
115002
+ if (isRuntimeTimeoutAbortReason(abortReason2)) {
113891
115003
  const timeoutError = Object.assign(
113892
115004
  new Error("Client runtime analysis timed out before the provider returned a result.", { cause: error48 }),
113893
115005
  error48,
@@ -113895,8 +115007,8 @@ ${reviewOutputContract}`;
113895
115007
  );
113896
115008
  throw cycleErrorProvenance ? attachClientRuntimeErrorProvenance(timeoutError, cycleErrorProvenance) : timeoutError;
113897
115009
  }
113898
- if (abortReason instanceof Error) {
113899
- throw cycleErrorProvenance ? attachClientRuntimeErrorProvenance(abortReason, cycleErrorProvenance) : abortReason;
115010
+ if (abortReason2 instanceof Error) {
115011
+ throw cycleErrorProvenance ? attachClientRuntimeErrorProvenance(abortReason2, cycleErrorProvenance) : abortReason2;
113900
115012
  }
113901
115013
  throw cycleErrorProvenance ? attachClientRuntimeErrorProvenance(error48, cycleErrorProvenance) : error48;
113902
115014
  }
@@ -113912,7 +115024,7 @@ ${reviewOutputContract}`;
113912
115024
  });
113913
115025
 
113914
115026
  // lib/runtime/engine.ts
113915
- var localAnalysis, getExchangeFailureDiagnostics, getRuntimeFallbackAttempts, isProviderFailureDiagnosticsCandidate, getEventBoundProviderLabel, getEventBoundProviderTraceId, getRecentProviderFailureDiagnostics, getClientRuntimeErrorProvenance, getRuntimeProviderSetupDiagnostics, buildProviderSetupErrorDiagnosticFields, buildClientRuntimeErrorProvenanceDiagnosticFields, buildRuntimeRequestErrorDiagnosticFields, hasEventBoundProviderFailureEvidence, classifyRuntimeErrorStage, buildHyperliquidPublicReadDiagnosticFields, buildRuntimeErrorDiagnostics, positiveNumber, resolveRuntimeEngineOptions, CLIENT_KILLSWITCH_BLOCK_STORAGE_PREFIX, getClientKillswitchBlockStorageKey, persistClientRuntimeKillswitchBlock, clearClientRuntimeKillswitchBlock, hashRuntimeDebugText2, createRuntimeUuid2, getRuntimeContractIdentityToken, getRuntimeStatusContractIdentityToken, getRuntimeContractBindingTokenFromCycleResult, getRuntimeContractBindingTupleToken, getRuntimeStatusContractBindingTupleToken, isTransientRuntimeConflictError, createRuntimeStartCancelledError, isRuntimeContractMismatchError, isClientExecutionModeMismatchError, isClientRuntimeSessionStoppedError, isClientRuntimeLeaseAuthError, isClientRuntimeReleasedError, deriveFailedStartCleanupOwnershipLossReason, isRuntimeAbortError, isExplicitClientExecutionMode, createStopAbortError, isRuntimeContractIdentityAdvancedAbort, CLIENT_RUNTIME_NO_DECISION_RECOVERY_REASON, sanitizeSettingsSnapshotForFallback, preflightClientRuntime, ClientExecutionEngine, createStoppedRuntimeSnapshot, ClientRuntimeRegistry, botEngine;
115027
+ var localAnalysis, getExchangeFailureDiagnostics, getRuntimeFallbackAttempts, isProviderFailureDiagnosticsCandidate, getEventBoundProviderLabel, getEventBoundProviderTraceId, getRecentProviderFailureDiagnostics, getClientRuntimeErrorProvenance, getRuntimeProviderSetupDiagnostics, buildProviderSetupErrorDiagnosticFields, buildClientRuntimeErrorProvenanceDiagnosticFields, buildRuntimeRequestErrorDiagnosticFields, hasEventBoundProviderFailureEvidence, classifyRuntimeErrorStage, buildHyperliquidPublicReadDiagnosticFields, buildRuntimeErrorDiagnostics, positiveNumber, resolveRuntimeEngineOptions, withServerPositionEvents, CLIENT_KILLSWITCH_BLOCK_STORAGE_PREFIX, getClientKillswitchBlockStorageKey, persistClientRuntimeKillswitchBlock, clearClientRuntimeKillswitchBlock, hashRuntimeDebugText2, createRuntimeUuid2, getRuntimeContractIdentityToken, getRuntimeStatusContractIdentityToken, getRuntimeContractBindingTokenFromCycleResult, getRuntimeContractBindingTupleToken, getRuntimeStatusContractBindingTupleToken, isTransientRuntimeConflictError, createRuntimeStartCancelledError, isRuntimeContractMismatchError, isClientExecutionModeMismatchError, isClientRuntimeSessionStoppedError, isClientRuntimeLeaseAuthError, isClientRuntimeReleasedError, deriveFailedStartCleanupOwnershipLossReason, isRuntimeAbortError, isExplicitClientExecutionMode, createStopAbortError, isRuntimeContractIdentityAdvancedAbort, CLIENT_RUNTIME_NO_DECISION_RECOVERY_REASON, sanitizeSettingsSnapshotForFallback, preflightClientRuntime, ClientExecutionEngine, createStoppedRuntimeSnapshot, ClientRuntimeRegistry, botEngine;
113916
115028
  var init_engine = __esm({
113917
115029
  "lib/runtime/engine.ts"() {
113918
115030
  "use strict";
@@ -114293,6 +115405,28 @@ var init_engine = __esm({
114293
115405
  }
114294
115406
  return options;
114295
115407
  };
115408
+ withServerPositionEvents = (result) => result.executionReports.map((report) => {
115409
+ const action = String(report.action ?? "").toUpperCase();
115410
+ if (report.status !== "executed" || !["BUY", "SELL", "FLIP_OPEN"].includes(action)) return report;
115411
+ const bySide = (side) => side === "BUY" ? "LONG" : "SHORT";
115412
+ let event = null;
115413
+ let direction = null;
115414
+ if (result.intent === "open" || result.intent === "add") {
115415
+ event = result.intent;
115416
+ direction = bySide(action);
115417
+ } else if (result.intent === "reduce") {
115418
+ event = "reduce";
115419
+ direction = action === "BUY" ? "SHORT" : "LONG";
115420
+ } else if (result.intent === "close" || result.intent === "flip" && action !== "FLIP_OPEN") {
115421
+ event = "close";
115422
+ direction = action === "SELL" ? "LONG" : "SHORT";
115423
+ } else if (result.intent === "flip" && (result.side === "buy" || result.side === "sell")) {
115424
+ event = "open";
115425
+ direction = result.side === "buy" ? "LONG" : "SHORT";
115426
+ }
115427
+ if (!event || !direction) return report;
115428
+ return { ...report, execution_metadata: { ...report.execution_metadata, position_event: event, position_direction: direction } };
115429
+ });
114296
115430
  CLIENT_KILLSWITCH_BLOCK_STORAGE_PREFIX = "vtx_client_runtime_killswitch_block_v1:";
114297
115431
  getClientKillswitchBlockStorageKey = (profileId) => `${CLIENT_KILLSWITCH_BLOCK_STORAGE_PREFIX}${profileId.trim()}`;
114298
115432
  persistClientRuntimeKillswitchBlock = (profileId) => {
@@ -114526,6 +115660,20 @@ var init_engine = __esm({
114526
115660
  this._startupPendingAnalysis = false;
114527
115661
  // True while the previous cycle ended in Agent-instruction polling.
114528
115662
  this._agentInstructionPollActive = false;
115663
+ // The AI schedule (lib/runtime/ai-schedule.ts): paused outside its windows,
115664
+ // and whether a window let a cycle run since (its end then may close
115665
+ // positions, as the Python Server loop's _was_scheduled_running).
115666
+ this._aiSchedulePaused = false;
115667
+ this._aiScheduleWindowActive = false;
115668
+ // When the window that let the last cycle run ends: the engine checks again
115669
+ // then, so a pause (and Close on Stop) starts at the window's end.
115670
+ this._aiScheduleWindowEndsAtMs = null;
115671
+ // Run Now's next cycle runs whatever the schedule says (triggerNow).
115672
+ this._manualRunRequested = false;
115673
+ // The cycle in flight only checks the AI schedule (a window-end or resume check).
115674
+ this._checkOnlyCycleInFlight = false;
115675
+ // The armed timer is a window-end wake: its cycle only applies the schedule.
115676
+ this._aiScheduleCheckPending = false;
114529
115677
  // Cycle whose Agent-instruction claim is currently held open by the server.
114530
115678
  // That idle wait is not analysis, so busy flags exclude it.
114531
115679
  this._agentInstructionClaimWaitRequestId = null;
@@ -114549,6 +115697,8 @@ var init_engine = __esm({
114549
115697
  this._primaryAnalysisDisabled = false;
114550
115698
  this._loopEarlyReturnRetryDelayMs = 5e3;
114551
115699
  this._executionModeMismatchConsecutiveCount = 0;
115700
+ this._expectedExecutionMode = "client";
115701
+ this._settleUnreportedModelCalls = null;
114552
115702
  this._activeRuntimeContractIdentity = null;
114553
115703
  this._contractRebindInFlight = false;
114554
115704
  this._contractRebindExhausted = false;
@@ -114716,6 +115866,57 @@ var init_engine = __esm({
114716
115866
  }
114717
115867
  return pending3.length === 0;
114718
115868
  }
115869
+ /**
115870
+ * Close on Stop: a Trader's schedule window ended with the option on, so
115871
+ * its open positions close, as the Python Server loop's
115872
+ * _handle_schedule_stop does. A failure is reported as a runtime error and
115873
+ * the bot stays paused.
115874
+ */
115875
+ async _closePositionsAtScheduleEnd(profileId, paused, cancelled) {
115876
+ try {
115877
+ if (cancelled()) return;
115878
+ const walletAddress = String(paused.settings.hyperliquid_wallet_address || "").trim();
115879
+ const signingKey = String(this._deviceSecrets?.hyperliquidSigningKey || "").trim();
115880
+ if (!walletAddress || !signingKey) {
115881
+ throw new Error("the wallet or its signing key is unavailable");
115882
+ }
115883
+ const timeoutSeconds = Number(paused.config.client_runtime_killswitch_close_timeout_seconds);
115884
+ if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
115885
+ throw new Error("Invalid Client Mode position close timeout configuration.");
115886
+ }
115887
+ await this._trailingMaintenancePromise;
115888
+ await this._pendingProtectionRecoveryPromise;
115889
+ await waitForBrowserExchangeMutationSettlement(profileId);
115890
+ const { positions } = await fetchBrowserAccountState2({
115891
+ publicRequestPriority: "trading_reduce",
115892
+ config: paused.config,
115893
+ walletAddress,
115894
+ symbol: firstConfiguredSymbol(paused.settings, paused.config),
115895
+ tickerPrice: 0,
115896
+ leverage: 1,
115897
+ aggregatePerpDexs: true,
115898
+ forceRefresh: true,
115899
+ sessionId: this._sessionId ?? void 0
115900
+ });
115901
+ if (cancelled()) return;
115902
+ await this._assertFreshClientWalletAssignment(profileId, walletAddress, void 0, "scheduled close on stop");
115903
+ if (cancelled()) return;
115904
+ const summary = await closeClientKillswitchPositions({
115905
+ profileId,
115906
+ walletAddress,
115907
+ positions,
115908
+ config: paused.config,
115909
+ signingKey,
115910
+ timeoutSeconds
115911
+ });
115912
+ if (summary.positionCloseFailures > 0) {
115913
+ throw new Error(`${summary.positionCloseFailures} position(s) could not be closed`);
115914
+ }
115915
+ } catch (error48) {
115916
+ const detail = error48 instanceof Error ? error48.message : String(error48);
115917
+ await this.reportError(`\u26A0\uFE0F Scheduled close-on-stop failed: ${detail}`, { fatal: false });
115918
+ }
115919
+ }
114719
115920
  async _assertFreshClientWalletAssignment(profileId, expectedWalletAddress, signal, stage) {
114720
115921
  const expectedWallet = String(expectedWalletAddress || "").trim().toLowerCase();
114721
115922
  if (!expectedWallet) {
@@ -114755,7 +115956,9 @@ var init_engine = __esm({
114755
115956
  const status = await getAiTradingStatus(void 0, Number(this._profileId), {
114756
115957
  runtimeViewer: this._runtimeStatusViewer()
114757
115958
  });
114758
- if (!isExplicitClientExecutionMode(status?.execution_mode)) {
115959
+ const statusMode = status?.execution_mode;
115960
+ const expectedMode = this._expectedExecutionMode === "server" ? String(statusMode ?? "").trim().toLowerCase() === "server" : isExplicitClientExecutionMode(statusMode);
115961
+ if (!expectedMode) {
114759
115962
  this._executionModeMismatchConsecutiveCount += 1;
114760
115963
  return this._executionModeMismatchConsecutiveCount >= 2;
114761
115964
  }
@@ -115297,6 +116500,7 @@ var init_engine = __esm({
115297
116500
  }
115298
116501
  _armExecutionLoopTimer(options) {
115299
116502
  this._clearExecutionLoopTimer();
116503
+ if (options?.reason !== "ai_schedule_window_end") this._aiScheduleCheckPending = false;
115300
116504
  if (this._state !== "running" || this._loopIntervalMs == null) {
115301
116505
  return;
115302
116506
  }
@@ -115318,7 +116522,7 @@ var init_engine = __esm({
115318
116522
  const timerReason = options?.reason || "scheduled";
115319
116523
  const armedAtMs = Date.now();
115320
116524
  const expectedFireAtMs = armedAtMs + delayMs;
115321
- const quietAgentPollTimer = timerReason === "agent_instruction_poll";
116525
+ const quietAgentPollTimer = timerReason === "agent_instruction_poll" || timerReason === "ai_schedule_paused";
115322
116526
  if (!quietAgentPollTimer) this._emitLoopBoundaryTelemetry("client_timer_armed", `reason=${options?.reason || "scheduled"} delay_ms=${delayMs}`, {
115323
116527
  reason: timerReason,
115324
116528
  delay_ms: delayMs,
@@ -115327,7 +116531,13 @@ var init_engine = __esm({
115327
116531
  timer_expected_fire_at_ms: expectedFireAtMs,
115328
116532
  // Terminal cycle evidence already records its next schedule. Retain the
115329
116533
  // initial/repaired schedule too, even if its first cycle never starts.
115330
- ...["cycle_finally", "main_model_disabled_copy_poll", "agent_instruction_poll"].includes(timerReason) ? {} : { cycle_evidence: {
116534
+ ...[
116535
+ "cycle_finally",
116536
+ "main_model_disabled_copy_poll",
116537
+ "agent_instruction_poll",
116538
+ "ai_schedule_paused",
116539
+ "ai_schedule_window_end"
116540
+ ].includes(timerReason) ? {} : { cycle_evidence: {
115331
116541
  version: 1,
115332
116542
  runtime_source: "client",
115333
116543
  mode: this._mode,
@@ -115570,6 +116780,24 @@ var init_engine = __esm({
115570
116780
  return this._instanceId;
115571
116781
  }
115572
116782
  /** Whether this engine runs, starts, or stops a runtime (never claim it again). */
116783
+ /**
116784
+ * Resolves once no cycle is in flight, or after the timeout: a Node runtime
116785
+ * host draining for a restart lets the current cycle finish, as a Python
116786
+ * Server worker drains its in-flight work. Whether it settled in time.
116787
+ */
116788
+ async waitForCycleToSettle(timeoutMs) {
116789
+ const loop = this._executionLoopPromise;
116790
+ if (!loop) return true;
116791
+ let timer;
116792
+ const settled = await Promise.race([
116793
+ loop.then(() => true, () => true),
116794
+ new Promise((resolve14) => {
116795
+ timer = setTimeout(() => resolve14(false), timeoutMs);
116796
+ })
116797
+ ]);
116798
+ clearTimeout(timer);
116799
+ return settled;
116800
+ }
115573
116801
  get active() {
115574
116802
  return this._startPromise !== null || this._isStopping || this._state === "running" || this._state === "stale" || this._state === "offline";
115575
116803
  }
@@ -115596,12 +116824,20 @@ var init_engine = __esm({
115596
116824
  await this.relinquishControl("window");
115597
116825
  }
115598
116826
  async _attachInternal(profileId, grant, options, startController) {
116827
+ this._aiSchedulePaused = false;
116828
+ this._aiScheduleWindowActive = false;
116829
+ this._aiScheduleWindowEndsAtMs = null;
115599
116830
  const lease = grant.lease;
115600
116831
  const grantSessionId = String(lease.runtime_session_id || "").trim();
115601
116832
  const grantDeviceId = String(lease.device_id || "").trim();
115602
116833
  if (String(lease.profile_id) !== String(profileId) || !grantSessionId || !grantDeviceId) {
115603
116834
  throw new Error("The runtime grant does not belong to this profile.");
115604
116835
  }
116836
+ this._expectedExecutionMode = options.executionMode === "server" ? "server" : "client";
116837
+ this._manualRunRequested = false;
116838
+ this._aiScheduleCheckPending = false;
116839
+ this._checkOnlyCycleInFlight = false;
116840
+ this._settleUnreportedModelCalls = options.settleUnreportedModelCalls ?? null;
115605
116841
  const leaseSchedulerInstanceId = String(lease.scheduler_instance_id ?? "").trim();
115606
116842
  if (leaseSchedulerInstanceId !== this._instanceId && !(leaseSchedulerInstanceId === "" && options.unboundSchedulerLease === true)) {
115607
116843
  throw createRuntimeStartCancelledError("scheduler_instance_binding_missing", {
@@ -115621,7 +116857,8 @@ var init_engine = __esm({
115621
116857
  this._sessionId = grantSessionId;
115622
116858
  this._deviceId = grantDeviceId;
115623
116859
  this._mode = lease.mode === "assistant" ? "assistant" : "trader";
115624
- this._lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
116860
+ const grantedLastRunAt = String(grant.runtime?.last_runtime_run_at ?? "").trim();
116861
+ this._lastRunAt = this._expectedExecutionMode === "server" ? Number.isFinite(Date.parse(grantedLastRunAt)) ? new Date(Date.parse(grantedLastRunAt)).toISOString() : null : (/* @__PURE__ */ new Date()).toISOString();
115625
116862
  this._lastAnalysisStartedAtMs = null;
115626
116863
  this._lastError = null;
115627
116864
  this._lastStopReason = null;
@@ -115747,7 +116984,10 @@ var init_engine = __esm({
115747
116984
  return;
115748
116985
  }
115749
116986
  this._assertStartAttemptActive(startController, "before_execution_loop");
115750
- if (waitsForAssignedStartPhase) {
116987
+ if (waitsForAssignedStartPhase && this._expectedExecutionMode === "server" && this._mode === "trader") {
116988
+ this._aiScheduleCheckPending = true;
116989
+ void this._runExecutionLoop();
116990
+ } else if (waitsForAssignedStartPhase) {
115751
116991
  this._armExecutionLoopTimer({ alignToNextRun: true, reason: "assigned_start_phase" });
115752
116992
  } else {
115753
116993
  this._armExecutionLoopTimer({ alignToNextRun: true });
@@ -116403,10 +117643,13 @@ var init_engine = __esm({
116403
117643
  }
116404
117644
  await this.relinquishControl(reason);
116405
117645
  }
116406
- async triggerNow() {
117646
+ async triggerNow(options) {
116407
117647
  if (this._state !== "running") {
116408
117648
  throw new Error("AI runtime is not running on this client.");
116409
117649
  }
117650
+ if ((options?.ignoreAiSchedule ?? true) && (!this._executionLoopPromise || this._aiSchedulePaused || this._checkOnlyCycleInFlight)) {
117651
+ this._manualRunRequested = true;
117652
+ }
116410
117653
  await this._runExecutionLoop();
116411
117654
  }
116412
117655
  _abortTrailingMaintenanceBeforeDispatch() {
@@ -116517,6 +117760,19 @@ var init_engine = __esm({
116517
117760
  this._pendingProtectionRecoveryDispatched = true;
116518
117761
  }
116519
117762
  });
117763
+ const retired = results.flatMap(({ retired: symbolRetired }) => symbolRetired ?? []);
117764
+ const stale = results.flatMap(({ symbol: symbol2, reports }) => reports.filter((report) => report.execution_metadata?.reason_code === "pending_protective_entry_stale").map(() => symbol2));
117765
+ if (retired.length > 0 || stale.length > 0) {
117766
+ const retiredByReason = retired.reduce((counts, { reasonCode }) => ({
117767
+ ...counts,
117768
+ [reasonCode]: (counts[reasonCode] ?? 0) + 1
117769
+ }), {});
117770
+ this.reportLifecycleTelemetry(
117771
+ "client_pending_protection_recovery",
117772
+ `retired=${retired.length} stale=${stale.length}`,
117773
+ { profileId, payload: { retired_by_reason: retiredByReason, stale_symbols: stale } }
117774
+ );
117775
+ }
116520
117776
  if (results.length) {
116521
117777
  appendRuntimeDebugEvent({
116522
117778
  label: "pending_protection_recovery",
@@ -116779,10 +118035,22 @@ var init_engine = __esm({
116779
118035
  const analysisAbortController = new AbortController();
116780
118036
  this._analysisAbortController = analysisAbortController;
116781
118037
  let decisionSubmitted = false;
118038
+ let cycleModelCalls = [];
118039
+ let cycleModelCallsReported = false;
116782
118040
  let executedExchangeOrderInCycle = false;
116783
118041
  let pendingExternalInferenceBindings = [];
116784
118042
  let agentPollDelayMs = null;
118043
+ let aiSchedulePauseMs = null;
118044
+ let aiSchedulePauseHasNext = false;
118045
+ let providerRetryDelayMs = null;
118046
+ const ignoreAiSchedule = this._manualRunRequested;
118047
+ this._manualRunRequested = false;
118048
+ const pausedBeforeSlot = this._aiSchedulePaused && this._nextRunAtMs != null && Date.now() < this._nextRunAtMs;
118049
+ const aiScheduleCheckOnly = (this._aiScheduleCheckPending || pausedBeforeSlot) && !ignoreAiSchedule;
118050
+ this._aiScheduleCheckPending = false;
118051
+ this._checkOnlyCycleInFlight = aiScheduleCheckOnly;
116785
118052
  const agentPollContinuation = this._agentInstructionPollActive;
118053
+ const aiSchedulePauseContinuation = this._aiSchedulePaused || aiScheduleCheckOnly;
116786
118054
  let cycleStartEmitted = false;
116787
118055
  let cycleErrorProvenance = null;
116788
118056
  let cycleWorkStarted = false;
@@ -116857,15 +118125,35 @@ var init_engine = __esm({
116857
118125
  this._notifyListeners();
116858
118126
  void this.sendHeartbeat();
116859
118127
  };
116860
- if (!agentPollContinuation) emitCycleStarted();
118128
+ if (!agentPollContinuation && !aiSchedulePauseContinuation) emitCycleStarted();
116861
118129
  try {
116862
- const savedSecrets = await ClientVault.getSecrets(profileId);
118130
+ let savedSecrets = await ClientVault.getSecrets(profileId);
118131
+ let serverSigningKey = null;
118132
+ if (this._expectedExecutionMode === "server") {
118133
+ try {
118134
+ const fresh = await ClientVault.hydrateFromServer(profileId, {
118135
+ includeAllProviderKeys: true,
118136
+ requireProviderSecret: false
118137
+ });
118138
+ if (fresh) {
118139
+ savedSecrets = fresh;
118140
+ serverSigningKey = String(fresh.hyperliquidSigningKey ?? "").trim() || null;
118141
+ }
118142
+ } catch (error48) {
118143
+ appendRuntimeDebugEvent({
118144
+ label: "server_secrets_refresh_failed",
118145
+ note: getSanitizedRuntimeErrorMessage(error48) || "unknown",
118146
+ outcome: "error"
118147
+ });
118148
+ }
118149
+ }
116863
118150
  if (analysisAbortController.signal.aborted || this._isStopping || this._analysisRequestId !== requestId) {
116864
118151
  throw new DOMException("The operation was aborted.", "AbortError");
116865
118152
  }
116866
118153
  if (savedSecrets) {
116867
118154
  deviceSecrets = {
116868
118155
  ...deviceSecrets,
118156
+ ...serverSigningKey ? { hyperliquidSigningKey: serverSigningKey } : {},
116869
118157
  providerKeys: savedSecrets.providerKeys ?? {},
116870
118158
  providerKeySlots: savedSecrets.providerKeySlots,
116871
118159
  providerBaseUrls: savedSecrets.providerBaseUrls,
@@ -116912,6 +118200,18 @@ var init_engine = __esm({
116912
118200
  }
116913
118201
  },
116914
118202
  onCycleObservation: observeCycle,
118203
+ onAiScheduleWindowActive: (windowEndsAtMs) => {
118204
+ this._aiSchedulePaused = false;
118205
+ this._aiScheduleWindowActive = true;
118206
+ this._aiScheduleWindowEndsAtMs = windowEndsAtMs;
118207
+ },
118208
+ onAiScheduleOff: () => {
118209
+ this._aiSchedulePaused = false;
118210
+ this._aiScheduleWindowEndsAtMs = null;
118211
+ },
118212
+ ignoreAiSchedule,
118213
+ aiScheduleCheckOnly,
118214
+ executionMode: this._expectedExecutionMode,
116915
118215
  onPreparedExecutionContexts: async ({ config: config2, executionContexts }) => {
116916
118216
  if (runtimeMode !== "trader") return;
116917
118217
  const cancelled = () => this._state !== "running" || this._isStopping || this._windowHandoffRequested || this._killswitchLatched || this._profileId !== profileId || this._sessionId !== sessionId || this._deviceId !== deviceId || (getRuntimeContractBindingTupleToken(this._activeRuntimeContractIdentity) ?? getRuntimeStatusContractBindingTupleToken(this._runtimeStatus)) !== loopContractBindingToken;
@@ -116940,10 +118240,74 @@ var init_engine = __esm({
116940
118240
  },
116941
118241
  signal: analysisAbortController.signal
116942
118242
  });
118243
+ const runCycleWithDispatchFreshness = (secrets) => this._expectedExecutionMode !== "server" ? runCycle(secrets) : runCycle(secrets).catch((error48) => {
118244
+ if (!localAnalysis.isServerDispatchAccountStateStale(error48) || analysisAbortController.signal.aborted || this._isStopping || this._analysisRequestId !== requestId) {
118245
+ throw error48;
118246
+ }
118247
+ appendRuntimeDebugEvent({ label: "server_dispatch_context_rebuilt", note: "attempt=2", outcome: "ok" });
118248
+ return runCycle(secrets);
118249
+ });
116943
118250
  let result;
116944
118251
  try {
116945
- result = await runCycle(deviceSecrets);
118252
+ result = await runCycleWithDispatchFreshness(deviceSecrets);
116946
118253
  } catch (error48) {
118254
+ if (localAnalysis.isClientRuntimeAiScheduleStillActive(error48)) {
118255
+ cycleOutcome = "skipped";
118256
+ cycleReason = "ai_schedule_check";
118257
+ this._lastRunAt = previousLastRunAt;
118258
+ this._nextRunAtMs = previousNextRunAtMs;
118259
+ this._lastError = null;
118260
+ this._notifyListeners();
118261
+ return;
118262
+ }
118263
+ if (localAnalysis.isClientRuntimeAiSchedulePaused(error48)) {
118264
+ cycleOutcome = "skipped";
118265
+ cycleReason = "ai_schedule_paused";
118266
+ this._lastRunAt = previousLastRunAt;
118267
+ this._lastError = null;
118268
+ const windowEnded = this._aiScheduleWindowActive;
118269
+ this._aiScheduleWindowActive = false;
118270
+ this._aiScheduleWindowEndsAtMs = null;
118271
+ this._aiSchedulePaused = true;
118272
+ const nowMs2 = Date.now();
118273
+ const windowOpensAtMs = error48.nextChangeAtMs != null ? this._periodicNextSlot(error48.nextChangeAtMs, true) ?? error48.nextChangeAtMs : null;
118274
+ aiSchedulePauseHasNext = windowOpensAtMs != null;
118275
+ aiSchedulePauseMs = windowOpensAtMs != null ? Math.max(1e3, Math.min(windowOpensAtMs - nowMs2, 36e5)) : 6e4;
118276
+ if (runtimeMode === "trader" && this._loopIntervalMs != null) {
118277
+ aiSchedulePauseMs = Math.min(aiSchedulePauseMs, Math.max(1e3, this._loopIntervalMs));
118278
+ }
118279
+ this._nextRunAtMs = windowOpensAtMs;
118280
+ if (!aiSchedulePauseContinuation) {
118281
+ this._emitLoopBoundaryTelemetry("client_ai_schedule_paused", `request_id=${requestId}`, {
118282
+ request_id: requestId,
118283
+ status: error48.message,
118284
+ next_change_ms: error48.nextChangeAtMs
118285
+ });
118286
+ }
118287
+ const cancelled = () => this._state !== "running" || this._isStopping || this._windowHandoffRequested || this._killswitchLatched || analysisAbortController.signal.aborted || this._profileId !== profileId || this._sessionId !== sessionId || this._deviceId !== deviceId;
118288
+ if (windowEnded && runtimeMode === "trader" && error48.closeOnStop) {
118289
+ await this._closePositionsAtScheduleEnd(profileId, error48, cancelled);
118290
+ }
118291
+ if (runtimeMode === "trader" && !cancelled()) {
118292
+ try {
118293
+ await this._processClientCopyTrading({
118294
+ profileId,
118295
+ runtimeSessionId: sessionId,
118296
+ runtimeDeviceId: deviceId,
118297
+ config: await this.getExecutionConfig(profileId),
118298
+ signal: analysisAbortController.signal
118299
+ });
118300
+ } catch (copyTradingError) {
118301
+ appendRuntimeDebugEvent({
118302
+ label: "ai_schedule_paused_copy_poll_failed",
118303
+ note: getSanitizedRuntimeErrorMessage(copyTradingError) || "Copy Trading poll failed.",
118304
+ outcome: "error"
118305
+ });
118306
+ }
118307
+ }
118308
+ this._notifyListeners();
118309
+ return;
118310
+ }
116947
118311
  if (localAnalysis.isClientRuntimeAgentInstructionEmpty(error48)) {
116948
118312
  cycleOutcome = "skipped";
116949
118313
  cycleReason = "agent_instruction_poll_empty";
@@ -116981,7 +118345,7 @@ var init_engine = __esm({
116981
118345
  throw error48;
116982
118346
  }
116983
118347
  try {
116984
- result = await runCycle(refresh.secrets);
118348
+ result = await runCycleWithDispatchFreshness(refresh.secrets);
116985
118349
  } catch (retryError) {
116986
118350
  const retryProviderSetup = getRuntimeProviderSetupDiagnostics(retryError);
116987
118351
  if (retryProviderSetup) {
@@ -117017,6 +118381,7 @@ var init_engine = __esm({
117017
118381
  }
117018
118382
  markCycleWorkStarted();
117019
118383
  decisionSnapshotId = result.decisionSnapshotId;
118384
+ cycleModelCalls = Array.isArray(result.invocations) ? result.invocations : [];
117020
118385
  if (result.runtimeError) cycleFailureStage = cycleObservation.stage;
117021
118386
  observeCycle({ stage: "decision" });
117022
118387
  cycleErrorProvenance = {
@@ -117173,7 +118538,10 @@ var init_engine = __esm({
117173
118538
  promptTokens: result.prompt_tokens,
117174
118539
  completionTokens: result.completion_tokens,
117175
118540
  totalTokens: result.total_tokens,
117176
- settingsSnapshot: result.settingsSnapshot,
118541
+ // A Server bot's row records what started its cycle, as the
118542
+ // Python Server loop's does (a Client row never has, and the key
118543
+ // is part of a row's Insights settings epoch).
118544
+ settingsSnapshot: this._expectedExecutionMode === "server" ? { ...result.settingsSnapshot, analysis_trigger_origin: ignoreAiSchedule ? "manual" : "scheduled" } : result.settingsSnapshot,
117177
118545
  clientExchangeSnapshot: result.clientExchangeSnapshot ?? null,
117178
118546
  invocations: result.invocations,
117179
118547
  externalInferenceBindings: result.externalInferenceBindings ?? [],
@@ -117186,6 +118554,7 @@ var init_engine = __esm({
117186
118554
  reconciliationTimeoutMs: result.decisionReconciliationTimeoutMs
117187
118555
  });
117188
118556
  decisionSubmitted = true;
118557
+ cycleModelCallsReported = true;
117189
118558
  } catch (decisionError) {
117190
118559
  if (isRuntimeContractMismatchError(decisionError)) {
117191
118560
  await this._discardCycleOnContractMismatch("decision", decisionError);
@@ -117258,11 +118627,28 @@ var init_engine = __esm({
117258
118627
  }
117259
118628
  const executionResults = [];
117260
118629
  const executionFailures = [];
117261
- for (let planIndex = 0; planIndex < executionPlans.length; planIndex += 1) {
117262
- if (planIndex > 0 && this._windowHandoffRequested) {
117263
- break;
117264
- }
117265
- const plan = executionPlans[planIndex];
118630
+ const executionLeaseToken = () => {
118631
+ const lease = getPersistedClientRuntimeServerLease(profileId);
118632
+ return lease?.runtimeSessionId === sessionId && lease.deviceId === deviceId && lease.mode === "trader" ? lease.runtimeLeaseToken : null;
118633
+ };
118634
+ const serverCapacityByPlan = /* @__PURE__ */ new Map();
118635
+ if (this._expectedExecutionMode === "server") {
118636
+ const capacityPlans = executionPlans.map((plan, planIndex) => ({ plan, planIndex, decision: String(plan.decision || "").toUpperCase() })).filter(({ plan, decision }) => !plan.executionBlock && (decision === "BUY" || decision === "SELL") && Math.trunc(Number(plan.units)) > 0);
118637
+ const allocations = capacityPlans.length === 0 ? [] : await allocateServerExecutionCapacity({
118638
+ profileId,
118639
+ sessionId,
118640
+ runtimeLeaseToken: executionLeaseToken(),
118641
+ runtimeDeviceId: deviceId,
118642
+ schedulerInstanceId: this._instanceId,
118643
+ signal: analysisAbortController.signal
118644
+ }, capacityPlans.map(({ plan, decision }) => ({
118645
+ symbol: plan.executionContext.symbol,
118646
+ decision,
118647
+ units: Math.trunc(Number(plan.units))
118648
+ })));
118649
+ capacityPlans.forEach(({ planIndex }, index) => serverCapacityByPlan.set(planIndex, allocations[index]));
118650
+ }
118651
+ const runPlan = async (plan, planIndex) => {
117266
118652
  let planEntryOrderDispatched = false;
117267
118653
  const decisionWallet = String(
117268
118654
  plan.executionContext.walletAddress || ""
@@ -117284,7 +118670,7 @@ var init_engine = __esm({
117284
118670
  actionablePlan ? "decision execution" : trailingHoldCanMutate ? "trailing stop maintenance" : "protective order reconciliation"
117285
118671
  );
117286
118672
  if (shouldAbortForContractInvalidation("post_execution_preferences_refresh")) {
117287
- return;
118673
+ return { control: "return" };
117288
118674
  }
117289
118675
  }
117290
118676
  appendRuntimeDebugEvent({
@@ -117336,17 +118722,16 @@ var init_engine = __esm({
117336
118722
  executionResult = await executeClientRuntimeDecision({
117337
118723
  profileId,
117338
118724
  sessionId,
117339
- runtimeLeaseToken: (() => {
117340
- const lease = getPersistedClientRuntimeServerLease(profileId);
117341
- return lease?.runtimeSessionId === sessionId && lease.deviceId === deviceId && lease.mode === "trader" ? lease.runtimeLeaseToken : null;
117342
- })(),
118725
+ runtimeLeaseToken: executionLeaseToken(),
117343
118726
  runtimeDeviceId: deviceId,
117344
118727
  schedulerInstanceId: this._instanceId,
117345
118728
  cycleId: result.cycleId,
118729
+ serverCapacityAllocation: serverCapacityByPlan.get(planIndex),
117346
118730
  signingKey: this._deviceSecrets?.hyperliquidSigningKey ?? null,
117347
118731
  config: config2,
117348
118732
  decision: plan.decision,
117349
118733
  units: plan.units,
118734
+ executionMode: this._expectedExecutionMode,
117350
118735
  executionContext: plan.executionContext,
117351
118736
  skipTrailingMaintenance: this._trailingObservations.some((request) => request.executionContext.decisionSnapshotId === plan.executionContext.decisionSnapshotId),
117352
118737
  signal: analysisAbortController.signal,
@@ -117365,7 +118750,8 @@ var init_engine = __esm({
117365
118750
  if (isClientRuntimeWindowHandoffBeforePlanDispatch(executionError)) {
117366
118751
  const handoffCleanup = getClientRuntimeWindowHandoffCleanup(executionError);
117367
118752
  if (handoffCleanup) {
117368
- executionResults.push({
118753
+ this._executionFinalizationRequired = true;
118754
+ return { control: "break", result: {
117369
118755
  status: "skipped",
117370
118756
  intent: "hold",
117371
118757
  side: null,
@@ -117378,10 +118764,9 @@ var init_engine = __esm({
117378
118764
  handoffCleanup,
117379
118765
  executionSnapshotId: `handoff-cleanup-${planIndex + 1}-${Date.now()}`,
117380
118766
  executionSnapshotCapturedAt: (/* @__PURE__ */ new Date()).toISOString()
117381
- });
117382
- this._executionFinalizationRequired = true;
118767
+ } };
117383
118768
  }
117384
- break;
118769
+ return { control: "break" };
117385
118770
  }
117386
118771
  if (analysisAbortController.signal.aborted || isRuntimeAbortError(executionError) || isRuntimeContractMismatchError(executionError) || isClientExecutionModeMismatchError(executionError) || isClientRuntimeSessionStoppedError(executionError) || isClientRuntimeLeaseAuthError(executionError) || isTransientRuntimeConflictError(executionError)) {
117387
118772
  throw executionError;
@@ -117440,6 +118825,9 @@ var init_engine = __esm({
117440
118825
  };
117441
118826
  }
117442
118827
  }
118828
+ return { control: "continue", result: executionResult };
118829
+ };
118830
+ const recordPlanResult = (executionResult) => {
117443
118831
  executionResults.push(executionResult);
117444
118832
  if (executionResult.handoffCleanup) {
117445
118833
  this._executionFinalizationRequired = true;
@@ -117448,11 +118836,26 @@ var init_engine = __esm({
117448
118836
  executedExchangeOrderInCycle = true;
117449
118837
  this._executionFinalizationRequired = true;
117450
118838
  }
117451
- if (!this._executionFinalizationRequired && (analysisAbortController.signal.aborted || this._isStopping || this._analysisRequestId !== requestId)) {
118839
+ };
118840
+ const cycleSupersededWithoutExecution = () => !this._executionFinalizationRequired && (analysisAbortController.signal.aborted || this._isStopping || this._analysisRequestId !== requestId);
118841
+ for (let planIndex = 0; planIndex < executionPlans.length; planIndex += 1) {
118842
+ if (planIndex > 0 && this._windowHandoffRequested) {
118843
+ break;
118844
+ }
118845
+ const outcome = await runPlan(executionPlans[planIndex], planIndex);
118846
+ if (outcome.control === "return") {
118847
+ return;
118848
+ }
118849
+ if (outcome.control === "break") {
118850
+ if (outcome.result) executionResults.push(outcome.result);
118851
+ break;
118852
+ }
118853
+ recordPlanResult(outcome.result);
118854
+ if (cycleSupersededWithoutExecution()) {
117452
118855
  return;
117453
118856
  }
117454
118857
  }
117455
- const executionReports = executionResults.flatMap((executionResult) => Array.isArray(executionResult.executionReports) ? executionResult.executionReports : []);
118858
+ const executionReports = executionResults.flatMap((executionResult) => Array.isArray(executionResult.executionReports) ? this._expectedExecutionMode === "server" ? withServerPositionEvents(executionResult) : executionResult.executionReports : []);
117456
118859
  const executedOrderReports = executionReports.filter((report) => report.status === "executed" && String(report.order_id || "").trim().length > 0);
117457
118860
  const handoffCleanups = executionResults.flatMap((executionResult) => executionResult.handoffCleanup ? [executionResult.handoffCleanup] : []);
117458
118861
  if (this._windowHandoffRequested && executedOrderReports.length === 0) {
@@ -117638,6 +119041,8 @@ var init_engine = __esm({
117638
119041
  this._notifyListeners();
117639
119042
  }
117640
119043
  } catch (error48) {
119044
+ const errorModelCalls = error48?.runtimeInvocationAttempts;
119045
+ if (Array.isArray(errorModelCalls) && errorModelCalls.length > 0) cycleModelCalls = errorModelCalls;
117641
119046
  const cycleElapsedSeconds = Math.max(0, (performance.now() - runStartedMonotonicMs) / 1e3);
117642
119047
  cycleOutcome = "failed";
117643
119048
  cycleReason = cycleObservation.reason_code ?? "runtime_failed";
@@ -117743,12 +119148,19 @@ var init_engine = __esm({
117743
119148
  model: cycleErrorProvenance?.model ?? null,
117744
119149
  modelSource: cycleErrorProvenance?.modelSource ?? null
117745
119150
  });
119151
+ if (reportedRuntime !== null && Array.isArray(errorModelCalls) && errorModelCalls.length > 0) {
119152
+ cycleModelCallsReported = true;
119153
+ }
117746
119154
  if (reportedRuntime?.runtime_recovery_blocked_source === "terminal_failure") {
117747
119155
  this._scheduleRecoverableSuspendAfterExecutionLoop({
117748
119156
  reason: "runtime_terminal_failure"
117749
119157
  });
117750
119158
  return;
117751
119159
  }
119160
+ if (hasEventBoundProviderFailureEvidence(error48) && this._expectedExecutionMode === "server") {
119161
+ const backoffSeconds = Number((await this.getExecutionConfig(profileId).catch(() => null))?.provider_failure_backoff_seconds);
119162
+ if (Number.isFinite(backoffSeconds) && backoffSeconds > 0) providerRetryDelayMs = backoffSeconds * 1e3;
119163
+ }
117752
119164
  if (hasEventBoundProviderFailureEvidence(error48)) {
117753
119165
  this.scheduleProviderFailureDiagnosticsTelemetry({
117754
119166
  profileId,
@@ -117762,6 +119174,18 @@ var init_engine = __esm({
117762
119174
  });
117763
119175
  }
117764
119176
  } finally {
119177
+ this._checkOnlyCycleInFlight = false;
119178
+ if (!cycleModelCallsReported && cycleModelCalls.length > 0 && this._settleUnreportedModelCalls) {
119179
+ try {
119180
+ await this._settleUnreportedModelCalls(cycleModelCalls);
119181
+ } catch (error48) {
119182
+ appendRuntimeDebugEvent({
119183
+ label: "unreported_model_calls_settle_failed",
119184
+ note: getSanitizedRuntimeErrorMessage(error48) || "unknown",
119185
+ outcome: "error"
119186
+ });
119187
+ }
119188
+ }
117765
119189
  if (!decisionSubmitted && pendingExternalInferenceBindings.length > 0) {
117766
119190
  await Promise.allSettled(
117767
119191
  pendingExternalInferenceBindings.map((binding) => cancelClientRuntimeInferenceJob(profileId, binding.job_id, {
@@ -117788,8 +119212,9 @@ var init_engine = __esm({
117788
119212
  if (this._analysisRequestId === requestId) {
117789
119213
  this._analysisRequestId = null;
117790
119214
  this._analysisStartedAtMs = null;
119215
+ if (this._expectedExecutionMode === "server" && this._state === "running") void this.sendHeartbeat();
117791
119216
  }
117792
- const silentEmptyAgentPoll = cycleOutcome === "skipped" && cycleReason === "agent_instruction_poll_empty" && !cycleStartEmitted;
119217
+ const silentEmptyAgentPoll = cycleOutcome === "skipped" && (cycleReason === "agent_instruction_poll_empty" || cycleReason === "ai_schedule_paused" || cycleReason === "ai_schedule_check") && !cycleStartEmitted;
117793
119218
  if (this._state === "running" && this._sessionId === sessionId) {
117794
119219
  this._agentInstructionPollActive = agentPollDelayMs !== null;
117795
119220
  }
@@ -117807,7 +119232,7 @@ var init_engine = __esm({
117807
119232
  });
117808
119233
  }
117809
119234
  this._notifyListeners();
117810
- if (this._periodicSchedule && agentPollDelayMs === null && !this._primaryAnalysisDisabled) {
119235
+ if (this._periodicSchedule && agentPollDelayMs === null && aiSchedulePauseMs === null && !this._primaryAnalysisDisabled) {
117811
119236
  this._advanceNextRunAnchor();
117812
119237
  }
117813
119238
  if (!silentEmptyAgentPoll) this._emitLoopBoundaryTelemetry("client_timer_rearmed", "reason=cycle_finally", {
@@ -117816,11 +119241,18 @@ var init_engine = __esm({
117816
119241
  primary_analysis_disabled: this._primaryAnalysisDisabled,
117817
119242
  agent_poll_retry_ms: agentPollDelayMs
117818
119243
  });
119244
+ const providerRetryWake = providerRetryDelayMs !== null && aiSchedulePauseMs === null && agentPollDelayMs === null && (this._nextRunAtMs == null || this._nextRunAtMs > Date.now() + providerRetryDelayMs) ? providerRetryDelayMs : null;
119245
+ if (providerRetryWake !== null) this._nextRunAtMs = Date.now() + providerRetryWake;
119246
+ const windowEndsAtMs = this._aiScheduleWindowEndsAtMs;
119247
+ const nextWorkAtMs = this._primaryAnalysisDisabled && this._loopIntervalMs != null ? Date.now() + this._loopIntervalMs : this._nextRunAtMs;
119248
+ const windowEndWake = aiSchedulePauseMs === null && agentPollDelayMs === null && providerRetryWake === null && windowEndsAtMs != null && windowEndsAtMs > Date.now() && (nextWorkAtMs == null || windowEndsAtMs < nextWorkAtMs) ? windowEndsAtMs - Date.now() : null;
119249
+ this._aiScheduleCheckPending = windowEndWake !== null;
119250
+ const manualRunWake = this._manualRunRequested ? 0 : null;
117819
119251
  this._armExecutionLoopTimer({
117820
119252
  alignToNextRun: agentPollDelayMs === null,
117821
- delayOverrideMs: agentPollDelayMs,
117822
- reason: agentPollDelayMs !== null ? "agent_instruction_poll" : this._primaryAnalysisDisabled ? "main_model_disabled_copy_poll" : "cycle_finally",
117823
- suppressNextRunProjection: agentPollDelayMs === null && this._primaryAnalysisDisabled
119253
+ delayOverrideMs: manualRunWake ?? aiSchedulePauseMs ?? agentPollDelayMs ?? providerRetryWake ?? windowEndWake,
119254
+ reason: manualRunWake !== null ? "manual_run_requested" : aiSchedulePauseMs !== null ? "ai_schedule_paused" : agentPollDelayMs !== null ? "agent_instruction_poll" : providerRetryWake !== null ? "provider_failure_retry" : windowEndWake !== null ? "ai_schedule_window_end" : this._primaryAnalysisDisabled ? "main_model_disabled_copy_poll" : "cycle_finally",
119255
+ suppressNextRunProjection: aiSchedulePauseMs !== null && !aiSchedulePauseHasNext || agentPollDelayMs === null && aiSchedulePauseMs === null && this._primaryAnalysisDisabled
117824
119256
  });
117825
119257
  }
117826
119258
  if (cycleOutcome === "unknown" && (analysisAbortController.signal.aborted || this._isStopping || this._state === "stopped")) {
@@ -118220,6 +119652,7 @@ var init_engine = __esm({
118220
119652
  const heartbeatAnalysisRunId = this._primaryAnalysisDisabled || !this._isAnalysisInFlight() ? null : this._analysisRequestId;
118221
119653
  const heartbeatIsAnalyzing = !this._primaryAnalysisDisabled && (this._isAnalysisInFlight() || this._startupPendingAnalysis && options?.confirmGrant !== true || this._isExecuting);
118222
119654
  const heartbeatIsExecuting = this._isExecuting;
119655
+ const heartbeatObservedAtMs = Date.now();
118223
119656
  const priorHeartbeatFailures = this._heartbeatFailures;
118224
119657
  try {
118225
119658
  const response = await sendClientRuntimeHeartbeat(heartbeatProfileId, {
@@ -118234,7 +119667,10 @@ var init_engine = __esm({
118234
119667
  schedulerPhase: heartbeatSchedulerPhase,
118235
119668
  analysisRunId: heartbeatAnalysisRunId,
118236
119669
  isAnalyzing: heartbeatIsAnalyzing,
118237
- isExecuting: heartbeatIsExecuting
119670
+ isExecuting: heartbeatIsExecuting,
119671
+ // Beats may arrive out of order: the server keeps a node bot's newest
119672
+ // analyzing observation, so a cycle's end is never undone by its start.
119673
+ ...this._expectedExecutionMode === "server" ? { analyzingObservedAtMs: heartbeatObservedAtMs } : {}
118238
119674
  });
118239
119675
  if (Number(response.protective_actions_pending ?? 0) > 0) {
118240
119676
  notifyClientProtectiveActionsPending(heartbeatProfileId);
@@ -118418,8 +119854,18 @@ var init_engine = __esm({
118418
119854
  );
118419
119855
  this._killswitchAbortController = null;
118420
119856
  }
119857
+ /**
119858
+ * A Server Mode bot (the Node runtime host, executionMode 'server') has one
119859
+ * killswitch, the Python worker's risk monitor: it reads the bot's account,
119860
+ * stops the bot through its runtime contract and closes its positions even
119861
+ * while this process is down, so the runtime evaluates none here and reads
119862
+ * no account for it.
119863
+ */
119864
+ _ownsKillswitch() {
119865
+ return this._expectedExecutionMode !== "server";
119866
+ }
118421
119867
  async _startClientKillswitchMonitor(startController) {
118422
- if (this._mode !== "trader" || !this._profileId) {
119868
+ if (this._mode !== "trader" || !this._profileId || !this._ownsKillswitch()) {
118423
119869
  return;
118424
119870
  }
118425
119871
  const configPromise = this.getExecutionConfig(this._profileId);
@@ -118446,7 +119892,7 @@ var init_engine = __esm({
118446
119892
  );
118447
119893
  }
118448
119894
  async _runClientKillswitchCheck() {
118449
- if (this._state !== "running" || this._mode !== "trader" || this._killswitchLatched || this._killswitchCheckInFlight || !this._profileId || !this._sessionId || !this._deviceId) {
119895
+ if (!this._ownsKillswitch() || this._state !== "running" || this._mode !== "trader" || this._killswitchLatched || this._killswitchCheckInFlight || !this._profileId || !this._sessionId || !this._deviceId) {
118450
119896
  return;
118451
119897
  }
118452
119898
  const profileId = this._profileId;
@@ -118762,6 +120208,9 @@ var init_engine = __esm({
118762
120208
  return config2;
118763
120209
  }
118764
120210
  async _processClientCopyTrading(input) {
120211
+ if (this._expectedExecutionMode === "server") {
120212
+ return;
120213
+ }
118765
120214
  try {
118766
120215
  const persistedLease = getPersistedClientRuntimeServerLease(input.profileId);
118767
120216
  const runtimeLeaseToken = persistedLease?.runtimeSessionId === input.runtimeSessionId && persistedLease.deviceId === input.runtimeDeviceId && persistedLease.mode === "trader" ? persistedLease.runtimeLeaseToken : null;
@@ -119232,8 +120681,8 @@ var init_engine = __esm({
119232
120681
  if (!normalized) return false;
119233
120682
  return this._getEngine(normalized, false)?.updateLoopInterval(options) ?? false;
119234
120683
  }
119235
- async triggerNow() {
119236
- await this._getSelectedEngine()?.triggerNow();
120684
+ async triggerNow(options) {
120685
+ await this._getSelectedEngine()?.triggerNow(options);
119237
120686
  }
119238
120687
  async triggerProfileNow(profileId) {
119239
120688
  const normalized = this._normalizeProfileId(profileId);
@@ -119517,7 +120966,8 @@ function startServerProtectionSweeper(options, identity2) {
119517
120966
  authority: {
119518
120967
  runtimeSessionId: identity2.sessionId,
119519
120968
  runtimeLeaseToken: lease.runtimeLeaseToken,
119520
- runtimeDeviceId: identity2.deviceId
120969
+ runtimeDeviceId: identity2.deviceId,
120970
+ schedulerInstanceId: identity2.schedulerInstanceId
119521
120971
  }
119522
120972
  }) : Date.now() + PROTECTION_IDLE_RECHECK_MS;
119523
120973
  } while (again && !controller.signal.aborted);
@@ -119593,36 +121043,24 @@ async function runHeadlessRuntime(options) {
119593
121043
  if (stored && stored.runtimeSessionId !== identity2.sessionId) return;
119594
121044
  await clearRuntimeState(options.statePath);
119595
121045
  };
119596
- const runtimeOwner = async () => {
119597
- try {
119598
- const { statuses } = await getClientRuntimeStatuses([options.profileId], AbortSignal.timeout(options.ownerReadTimeoutMs ?? OWNER_READ_TIMEOUT_MS));
119599
- const status = statuses[String(options.profileId)];
119600
- if (status) {
119601
- const ownerSession = String(status.runtime_session_id ?? "").trim();
119602
- const ownerDevice = String(status.runtime_device_id ?? "").trim();
119603
- return !ownerSession || ownerSession === identity2.sessionId && ownerDevice === identity2.deviceId ? "mine" : "other";
119604
- }
119605
- } catch {
119606
- }
119607
- return engine.snapshot.ownershipLossReason ? "other" : "unknown";
119608
- };
119609
121046
  const stopRuntime = async (reason) => {
119610
121047
  if (engine.active) await engine.stop({ skipRemote: true, reason });
119611
- const owner2 = await runtimeOwner();
119612
- if (owner2 === "other") {
119613
- await clearOwnState();
119614
- return;
119615
- }
119616
- if (owner2 === "unknown") {
119617
- throw new Error("VTX could not confirm who runs this profile, so its runtime was not stopped; it may still be set to run.");
121048
+ try {
121049
+ await stopClientRuntimeSession(options.profileId, {
121050
+ sessionId: identity2.sessionId,
121051
+ deviceId: identity2.deviceId,
121052
+ mode: identity2.mode,
121053
+ stopReason: reason,
121054
+ authoritativeIntent: true,
121055
+ requireCurrentOwner: true
121056
+ }, {
121057
+ omitRuntimeLeaseToken: true,
121058
+ requestTimeoutMs: options.stopRequestTimeoutMs ?? STOP_REQUEST_TIMEOUT_MS
121059
+ });
121060
+ } catch (error48) {
121061
+ const detail = error48 instanceof Error ? error48.message : String(error48);
121062
+ throw new Error(`VTX could not stop this profile's runtime on the server (${detail}); it may still be set to run.`);
119618
121063
  }
119619
- await stopClientRuntimeSession(options.profileId, {
119620
- sessionId: identity2.sessionId,
119621
- deviceId: identity2.deviceId,
119622
- mode: identity2.mode,
119623
- stopReason: reason,
119624
- authoritativeIntent: true
119625
- }, { omitRuntimeLeaseToken: true });
119626
121064
  await clearOwnState();
119627
121065
  };
119628
121066
  const releaseFailedStart = async (kind) => {
@@ -119661,9 +121099,9 @@ async function runHeadlessRuntime(options) {
119661
121099
  return true;
119662
121100
  };
119663
121101
  const firstAttached = () => {
119664
- const stopSweep = startServerProtectionSweeper(options, identity2);
121102
+ const stopSweep = startServerProtectionSweeper(options, { ...identity2, schedulerInstanceId: engine.schedulerInstanceId });
119665
121103
  if (options.once && engine.state === "running" && !engine.snapshot.isAnalyzing) {
119666
- void engine.triggerNow().catch(() => void 0);
121104
+ void engine.triggerNow({ ignoreAiSchedule: false }).catch(() => void 0);
119667
121105
  }
119668
121106
  return stopSweep;
119669
121107
  };
@@ -119782,7 +121220,7 @@ async function runHeadlessRuntime(options) {
119782
121220
  }
119783
121221
  }
119784
121222
  }
119785
- var HeadlessRuntimeTakeoverRequiredError, HEADLESS_RUNTIME_AGENT_SCOPES, DEFAULT_MAX_CLAIM_WAITS, OWNER_READ_TIMEOUT_MS, PROTECTION_IDLE_RECHECK_MS, PROTECTION_RETRY_MS, MAX_RESUMES_WITHOUT_A_CYCLE, RESUME_BACKOFF_MS;
121223
+ var HeadlessRuntimeTakeoverRequiredError, HEADLESS_RUNTIME_AGENT_SCOPES, DEFAULT_MAX_CLAIM_WAITS, STOP_REQUEST_TIMEOUT_MS, PROTECTION_IDLE_RECHECK_MS, PROTECTION_RETRY_MS, MAX_RESUMES_WITHOUT_A_CYCLE, RESUME_BACKOFF_MS;
119786
121224
  var init_headless_runtime = __esm({
119787
121225
  "lib/agent-core/headless-runtime.ts"() {
119788
121226
  "use strict";
@@ -119817,7 +121255,7 @@ var init_headless_runtime = __esm({
119817
121255
  };
119818
121256
  HEADLESS_RUNTIME_AGENT_SCOPES = ["read", "bot:control", "trading:execute", "secrets:hydrate"];
119819
121257
  DEFAULT_MAX_CLAIM_WAITS = 10;
119820
- OWNER_READ_TIMEOUT_MS = 1e4;
121258
+ STOP_REQUEST_TIMEOUT_MS = 1e4;
119821
121259
  PROTECTION_IDLE_RECHECK_MS = runtime_policy_generated_default2.trading.engine.client_runtime.protective_actions.idle_recheck_seconds * 1e3;
119822
121260
  PROTECTION_RETRY_MS = 15e3;
119823
121261
  MAX_RESUMES_WITHOUT_A_CYCLE = 5;