@cortexkit/aft 0.61.0 → 0.63.4

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/dist/index.js CHANGED
@@ -110,12 +110,125 @@ var init_active_logger = __esm(() => {
110
110
  ACTIVE_LOGGER_SYMBOL = Symbol.for("aft-bridge-active-logger");
111
111
  });
112
112
 
113
+ // ../aft-bridge/dist/bash-env.js
114
+ var BLOCKED_BASH_ENV_NAMES, PROJECT_ONLY_BLOCKED_BASH_ENV_NAMES, PROJECT_ONLY_NETWORK_BLOCKED_BASH_ENV_NAMES, BLOCKED, PROJECT_ONLY;
115
+ var init_bash_env = __esm(() => {
116
+ BLOCKED_BASH_ENV_NAMES = [
117
+ "LD_PRELOAD",
118
+ "LD_LIBRARY_PATH",
119
+ "LD_AUDIT",
120
+ "DYLD_INSERT_LIBRARIES",
121
+ "DYLD_LIBRARY_PATH",
122
+ "DYLD_FALLBACK_LIBRARY_PATH",
123
+ "BASH_ENV",
124
+ "ENV",
125
+ "IFS",
126
+ "PATH",
127
+ "SHELLOPTS",
128
+ "BASHOPTS",
129
+ "PROMPT_COMMAND",
130
+ "PS4",
131
+ "PYTHONSTARTUP",
132
+ "PYTHONPATH",
133
+ "PYTHONHOME",
134
+ "PERL5OPT",
135
+ "PERL5LIB",
136
+ "PERLLIB",
137
+ "PERL5DB",
138
+ "RUBYOPT",
139
+ "RUBYLIB",
140
+ "NODE_OPTIONS",
141
+ "GCONV_PATH",
142
+ "LOCPATH",
143
+ "NLSPATH",
144
+ "HOSTALIASES",
145
+ "RESOLV_HOST_CONF",
146
+ "HOME",
147
+ "SHELL",
148
+ "TMPDIR",
149
+ "TMP",
150
+ "TEMP",
151
+ "XDG_CONFIG_HOME",
152
+ "XDG_DATA_HOME",
153
+ "XDG_CACHE_HOME",
154
+ "XDG_STATE_HOME",
155
+ "XDG_RUNTIME_DIR",
156
+ "CARGO_HOME",
157
+ "RUSTUP_HOME",
158
+ "USERPROFILE",
159
+ "APPDATA",
160
+ "LOCALAPPDATA",
161
+ "COMSPEC",
162
+ "PATHEXT",
163
+ "SYSTEMROOT",
164
+ "PSMODULEPATH",
165
+ "NODE_PATH",
166
+ "JAVA_TOOL_OPTIONS",
167
+ "_JAVA_OPTIONS",
168
+ "JDK_JAVA_OPTIONS",
169
+ "PYTHONUSERBASE",
170
+ "GIT_DIR",
171
+ "GIT_WORK_TREE",
172
+ "GIT_COMMON_DIR",
173
+ "GIT_INDEX_FILE",
174
+ "GIT_OBJECT_DIRECTORY",
175
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
176
+ "GIT_CEILING_DIRECTORIES",
177
+ "GIT_EXEC_PATH",
178
+ "GIT_TEMPLATE_DIR",
179
+ "GIT_SSH",
180
+ "GIT_SSH_COMMAND",
181
+ "GIT_ASKPASS",
182
+ "SSH_ASKPASS",
183
+ "GIT_EXTERNAL_DIFF",
184
+ "GIT_PROXY_COMMAND",
185
+ "RUSTC_WRAPPER",
186
+ "CARGO_BUILD_RUSTC_WRAPPER",
187
+ "RUSTC_WORKSPACE_WRAPPER",
188
+ "RUST_TEST_THREADS",
189
+ "NEXTEST_TEST_THREADS",
190
+ "DBUS_SESSION_BUS_ADDRESS"
191
+ ];
192
+ PROJECT_ONLY_BLOCKED_BASH_ENV_NAMES = [
193
+ "SSH_AUTH_SOCK",
194
+ "GOOGLE_APPLICATION_CREDENTIALS",
195
+ "KUBECONFIG",
196
+ "DOCKER_HOST",
197
+ "DOCKER_CONFIG"
198
+ ];
199
+ PROJECT_ONLY_NETWORK_BLOCKED_BASH_ENV_NAMES = [
200
+ "HTTP_PROXY",
201
+ "HTTPS_PROXY",
202
+ "ALL_PROXY",
203
+ "NO_PROXY",
204
+ "SSL_CERT_FILE",
205
+ "SSL_CERT_DIR",
206
+ "NODE_EXTRA_CA_CERTS",
207
+ "REQUESTS_CA_BUNDLE",
208
+ "CURL_CA_BUNDLE",
209
+ "GIT_SSL_NO_VERIFY",
210
+ "NODE_TLS_REJECT_UNAUTHORIZED"
211
+ ];
212
+ BLOCKED = new Set(BLOCKED_BASH_ENV_NAMES);
213
+ PROJECT_ONLY = new Set([
214
+ ...PROJECT_ONLY_BLOCKED_BASH_ENV_NAMES,
215
+ ...PROJECT_ONLY_NETWORK_BLOCKED_BASH_ENV_NAMES
216
+ ]);
217
+ });
218
+
113
219
  // ../aft-bridge/dist/protocol.js
114
220
  function isTerminalBashStatus(status, compatibilityAliases = false) {
115
221
  return typeof status === "string" && (TERMINAL_BASH_STATUSES.has(status) || compatibilityAliases && (status === "cancelled" || status === "timeout"));
116
222
  }
117
- var TERMINAL_BG_TASK_STATUSES, TERMINAL_BASH_STATUSES;
223
+ var LIVE_DESCENDANTS_UNAVAILABLE_REASONS, TERMINAL_BG_TASK_STATUSES, TERMINAL_BASH_STATUSES;
118
224
  var init_protocol = __esm(() => {
225
+ LIVE_DESCENDANTS_UNAVAILABLE_REASONS = Object.freeze([
226
+ "unsupported_platform",
227
+ "no_process_group",
228
+ "listing_failed",
229
+ "listing_incomplete",
230
+ "not_checked"
231
+ ]);
119
232
  TERMINAL_BG_TASK_STATUSES = Object.freeze([
120
233
  "completed",
121
234
  "failed",
@@ -392,7 +505,16 @@ var init_bash_host_fallback = __esm(() => {
392
505
  BASH_HOST_FALLBACK_MAX_OUTPUT_BYTES = 100 * 1024;
393
506
  BASH_HOST_FALLBACK_MAX_TIMEOUT_MS = 10 * 60 * 1000;
394
507
  });
508
+ // ../aft-bridge/dist/bash-permissions.js
509
+ var init_bash_permissions = __esm(() => {
510
+ init_active_logger();
511
+ });
395
512
 
513
+ // ../aft-bridge/dist/bash-watch-lines.js
514
+ var DEFAULT_MASKING_LINE_CAP_BYTES;
515
+ var init_bash_watch_lines = __esm(() => {
516
+ DEFAULT_MASKING_LINE_CAP_BYTES = 8 * 1024;
517
+ });
396
518
  // ../aft-bridge/dist/binary-identity.js
397
519
  import { createHash } from "node:crypto";
398
520
  import { closeSync, createReadStream, openSync, readFileSync, readSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
@@ -627,9 +749,22 @@ var init_binary_identity = __esm(() => {
627
749
  function isPassiveCommand(command) {
628
750
  return PASSIVE_COMMANDS.has(command);
629
751
  }
630
- var PASSIVE_COMMANDS, PASSIVE_COMMAND_TIMEOUT_MS = 5000;
752
+ function isReadOnlyRetryCommand(tool) {
753
+ return READ_ONLY_RETRY_COMMANDS.has(tool);
754
+ }
755
+ var PASSIVE_COMMANDS, PASSIVE_COMMAND_TIMEOUT_MS = 5000, READ_ONLY_RETRY_COMMANDS;
631
756
  var init_command_timeouts = __esm(() => {
632
757
  PASSIVE_COMMANDS = new Set(["status"]);
758
+ READ_ONLY_RETRY_COMMANDS = new Set([
759
+ "read",
760
+ "outline",
761
+ "zoom",
762
+ "grep",
763
+ "glob",
764
+ "search",
765
+ "callgraph",
766
+ "inspect"
767
+ ]);
633
768
  });
634
769
 
635
770
  // ../aft-bridge/dist/transport.js
@@ -639,6 +774,12 @@ var WORKER_SESSION_FIELD = "worker_session", HOST_TOOL_NAMES_FIELD = "host_tool_
639
774
  import { homedir as homedir4 } from "node:os";
640
775
  import { join as join3 } from "node:path";
641
776
  import { StringDecoder } from "node:string_decoder";
777
+ function hangWindowMs(budgetsMs, minWindowMs = DEFAULT_MIN_HANG_WINDOW_MS) {
778
+ let longest = 0;
779
+ for (const budget of budgetsMs)
780
+ longest = Math.max(longest, budget);
781
+ return Math.max(minWindowMs, 2 * longest);
782
+ }
642
783
  function bashTaskIdFrom(response) {
643
784
  const snakeCase = response.task_id;
644
785
  if (typeof snakeCase === "string" && snakeCase.length > 0)
@@ -728,7 +869,7 @@ function shellOutcomeFromLateReply(response) {
728
869
  }
729
870
  return { kind: "unknown" };
730
871
  }
731
- var DEFAULT_BRIDGE_TIMEOUT_MS = 30000, BRIDGE_HANG_TIMEOUT_THRESHOLD = 2, MAX_STDOUT_BUFFER, MALFORMED_STDOUT_LOG_CHARS = 512, HASHLINE_REGISTRATION_LOG_INTERVAL_MS = 60000, HASHLINE_REGISTRATION_LOG_STATE_LIMIT = 256, binaryFingerprintReader, binaryFingerprintAsyncReader, BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE = "failed to parse processor information from /proc/cpuinfo", BridgeReplacedDuringVersionCheck, BridgeTransportTimeoutError, SHELL_TASK_COMMANDS, DEFAULT_LATE_SHELL_REPLY_GRACE_MS = 1e4, BridgeTransportUnavailableError, BridgeTransportUnknownOutcomeError, BinaryBridge;
872
+ var DEFAULT_BRIDGE_TIMEOUT_MS = 30000, DEFAULT_MIN_HANG_WINDOW_MS = 60000, DEFAULT_HANG_PROBE_TIMEOUT_MS = 5000, MAX_STDOUT_BUFFER, MALFORMED_STDOUT_LOG_CHARS = 512, HASHLINE_REGISTRATION_LOG_INTERVAL_MS = 60000, HASHLINE_REGISTRATION_LOG_STATE_LIMIT = 256, binaryFingerprintReader, binaryFingerprintAsyncReader, BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE = "failed to parse processor information from /proc/cpuinfo", SYSTEM_CLOCK, BridgeReplacedDuringVersionCheck, BridgeTransportTimeoutError, SHELL_TASK_COMMANDS, DEFAULT_LATE_SHELL_REPLY_GRACE_MS = 1e4, BridgeTransportUnavailableError, BridgeTransportUnknownOutcomeError, BridgeRestartedError, BinaryBridge;
732
873
  var init_bridge = __esm(() => {
733
874
  init_active_logger();
734
875
  init_binary_identity();
@@ -738,6 +879,11 @@ var init_bridge = __esm(() => {
738
879
  MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
739
880
  binaryFingerprintReader = peekBinaryFingerprint;
740
881
  binaryFingerprintAsyncReader = binaryContentHash;
882
+ SYSTEM_CLOCK = {
883
+ now: () => Date.now(),
884
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
885
+ clearTimeout: (timer) => clearTimeout(timer)
886
+ };
741
887
  BridgeReplacedDuringVersionCheck = class BridgeReplacedDuringVersionCheck extends Error {
742
888
  newBinaryPath;
743
889
  constructor(newBinaryPath) {
@@ -773,6 +919,18 @@ var init_bridge = __esm(() => {
773
919
  this.name = "BridgeTransportUnknownOutcomeError";
774
920
  }
775
921
  };
922
+ BridgeRestartedError = class BridgeRestartedError extends BridgeTransportUnknownOutcomeError {
923
+ tool;
924
+ outcomeUnknown;
925
+ retryable = true;
926
+ restartReason = "hang_watchdog";
927
+ constructor(tool, outcomeUnknown, message) {
928
+ super(message);
929
+ this.tool = tool;
930
+ this.outcomeUnknown = outcomeUnknown;
931
+ this.name = "BridgeRestartedError";
932
+ }
933
+ };
776
934
  BinaryBridge = class BinaryBridge {
777
935
  static RESTART_RESET_MS = 5 * 60 * 1000;
778
936
  static STDERR_TAIL_MAX = 20;
@@ -799,7 +957,9 @@ var init_bridge = __esm(() => {
799
957
  _restartCount = 0;
800
958
  _shuttingDown = false;
801
959
  timeoutMs;
802
- hangThreshold;
960
+ hangWindowMinMs;
961
+ hangProbeTimeoutMs;
962
+ clock;
803
963
  lateShellReplyGraceMs;
804
964
  maxRestarts;
805
965
  configured = false;
@@ -819,8 +979,11 @@ var init_bridge = __esm(() => {
819
979
  configureWarningClients = new Map;
820
980
  restartTimer = null;
821
981
  restartResetTimer = null;
822
- lastChildActivityAt = 0;
823
- consecutiveRequestTimeouts = 0;
982
+ awaitingReply = new Map;
983
+ lastChildActivity = Number.NEGATIVE_INFINITY;
984
+ hangProbe = null;
985
+ hangWatchdogTimer = null;
986
+ hangSuppressionLogged = false;
824
987
  timeoutRestartAwaitingRecovery = false;
825
988
  errorPrefix;
826
989
  logger;
@@ -830,7 +993,9 @@ var init_bridge = __esm(() => {
830
993
  this.binaryPath = binaryPath;
831
994
  this.cwd = cwd;
832
995
  this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
833
- this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
996
+ this.hangWindowMinMs = options?.hangWindowMinMs ?? DEFAULT_MIN_HANG_WINDOW_MS;
997
+ this.hangProbeTimeoutMs = options?.hangProbeTimeoutMs ?? DEFAULT_HANG_PROBE_TIMEOUT_MS;
998
+ this.clock = options?.clock ?? SYSTEM_CLOCK;
834
999
  this.lateShellReplyGraceMs = options?.lateShellReplyGraceMs ?? DEFAULT_LATE_SHELL_REPLY_GRACE_MS;
835
1000
  this.maxRestarts = options?.maxRestarts ?? 3;
836
1001
  this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
@@ -871,6 +1036,19 @@ var init_bridge = __esm(() => {
871
1036
  log(message, meta);
872
1037
  }
873
1038
  }
1039
+ debugVia(message, meta) {
1040
+ const logger = this.logger ?? getActiveLogger();
1041
+ if (!logger?.debug) {
1042
+ this.logVia(message, meta);
1043
+ return;
1044
+ }
1045
+ try {
1046
+ logger.debug(message, meta);
1047
+ } catch (err) {
1048
+ console.error(`[aft-bridge] ERROR: logger debug threw: ${err instanceof Error ? err.message : String(err)}`);
1049
+ console.error(`[aft-bridge] ${message}`);
1050
+ }
1051
+ }
874
1052
  warnVia(message, meta) {
875
1053
  const logger = this.logger ?? getActiveLogger();
876
1054
  if (logger) {
@@ -1091,7 +1269,7 @@ var init_bridge = __esm(() => {
1091
1269
  params[HOST_TOOL_NAMES_FIELD] = hostToolNames;
1092
1270
  return await this.send("tool_call", params, Object.keys(sendOptions).length > 0 ? sendOptions : undefined);
1093
1271
  }
1094
- async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
1272
+ async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap, retriedAfterHangRestart = false) {
1095
1273
  try {
1096
1274
  if (this._retiringDueToBinaryChange) {
1097
1275
  throw new BridgeTransportUnavailableError(`${this.errorPrefix} Bridge is retiring after the on-disk binary changed; retry to respawn on the updated binary`);
@@ -1176,13 +1354,15 @@ var init_bridge = __esm(() => {
1176
1354
  } else {
1177
1355
  request = { id, command, ...params };
1178
1356
  }
1357
+ delete request.handles_progress;
1358
+ if (options?.onProgress)
1359
+ request.handles_progress = true;
1179
1360
  const line = `${JSON.stringify(request)}
1180
1361
  `;
1181
1362
  const keepBridgeOnTimeout = passive || command === "bash_drain_completions" || options?.keepBridgeOnTimeout === true;
1182
- let requestSentAt = Date.now();
1183
1363
  const child = this.process;
1184
1364
  const response = await new Promise((resolve, reject) => {
1185
- const timer = setTimeout(() => {
1365
+ const timer = this.clock.setTimeout(() => {
1186
1366
  const entry = this.pending.get(id);
1187
1367
  if (!entry)
1188
1368
  return;
@@ -1190,45 +1370,25 @@ var init_bridge = __esm(() => {
1190
1370
  return;
1191
1371
  }
1192
1372
  this.pending.delete(id);
1193
- clearTimeout(entry.timer);
1373
+ this.clock.clearTimeout(entry.timer);
1194
1374
  entry.onSettled?.();
1195
- if (keepBridgeOnTimeout) {
1196
- const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`;
1197
- if (requestSessionId) {
1198
- this.sessionWarnVia(requestSessionId, timeoutMsg);
1199
- } else {
1200
- this.warnVia(timeoutMsg);
1201
- }
1202
- entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
1203
- return;
1204
- }
1205
- const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
1206
- const consecutiveTimeouts = this.consecutiveRequestTimeouts + 1;
1207
- this.consecutiveRequestTimeouts = consecutiveTimeouts;
1208
- const restartSuppressed = this.timeoutRestartAwaitingRecovery;
1209
- const keepWarm = restartSuppressed || childActiveSinceRequest || consecutiveTimeouts < this.hangThreshold;
1210
- const restartSuffix = keepWarm ? " — bridge kept warm" : " — restarting bridge";
1211
- const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
1375
+ const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms — bridge kept warm`;
1212
1376
  if (requestSessionId) {
1213
1377
  this.sessionWarnVia(requestSessionId, timeoutMsg);
1214
1378
  } else {
1215
1379
  this.warnVia(timeoutMsg);
1216
1380
  }
1217
- if (keepWarm) {
1218
- entry.reject(new Error(restartSuppressed ? `${this.errorPrefix} request "${command}" timed out after ${effectiveTimeoutMs}ms; automatic timeout restart already attempted; bridge kept warm to finish startup. Retry later or restart the host explicitly if it remains unresponsive. A timed-out mutation may still complete; inspect before repeating it.` : `${this.errorPrefix} request "${command}" timed out after ${effectiveTimeoutMs}ms (bridge busy/under load); bridge kept warm — retry`));
1381
+ if (keepBridgeOnTimeout) {
1382
+ entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
1219
1383
  return;
1220
1384
  }
1221
- entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
1222
- this.handleTimeout({
1223
- requestId: id,
1224
- tool: entry.tool,
1225
- generation: entry.generation
1226
- }, requestSessionId);
1385
+ entry.reject(new Error(`${this.errorPrefix} request "${command}" timed out after ${effectiveTimeoutMs}ms (bridge busy/under load); bridge kept warm — retry. A timed-out mutation may still complete; inspect before repeating it.`));
1227
1386
  }, effectiveTimeoutMs);
1228
1387
  const entry = {
1229
1388
  resolve,
1230
1389
  reject,
1231
1390
  timer,
1391
+ budgetMs: effectiveTimeoutMs,
1232
1392
  onProgress: options?.onProgress,
1233
1393
  command,
1234
1394
  tool,
@@ -1237,21 +1397,21 @@ var init_bridge = __esm(() => {
1237
1397
  this.pending.set(id, entry);
1238
1398
  if (!child?.stdin?.writable) {
1239
1399
  this.pending.delete(id);
1240
- clearTimeout(timer);
1400
+ this.clock.clearTimeout(timer);
1241
1401
  const error = new BridgeTransportUnavailableError(`${this.errorPrefix} stdin not writable for command "${command}"`);
1242
1402
  reject(error);
1243
1403
  if (this.process === child)
1244
1404
  this.invalidateTransportProcess(error);
1245
1405
  return;
1246
1406
  }
1247
- requestSentAt = Date.now();
1407
+ this.recordAwaitingReply(id, effectiveTimeoutMs, tool, requestSessionId);
1248
1408
  const handleWriteFailure = (cause) => {
1249
1409
  const writeError = cause instanceof Error ? cause : new Error(String(cause));
1250
1410
  const error = new BridgeTransportUnknownOutcomeError(`${this.errorPrefix} Failed to write to stdin: ${writeError.message}`, { cause: writeError });
1251
1411
  const entry = this.pending.get(id);
1252
1412
  if (entry) {
1253
1413
  this.pending.delete(id);
1254
- clearTimeout(entry.timer);
1414
+ this.clock.clearTimeout(entry.timer);
1255
1415
  entry.onSettled?.();
1256
1416
  entry.reject(error);
1257
1417
  }
@@ -1277,7 +1437,12 @@ var init_bridge = __esm(() => {
1277
1437
  } catch (err) {
1278
1438
  if (err instanceof BridgeReplacedDuringVersionCheck && canRetryAfterVersionSwap && command !== "configure" && command !== "version") {
1279
1439
  this.logVia(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
1280
- return this.sendWithVersionMismatchRetry(command, params, options, false);
1440
+ return this.sendWithVersionMismatchRetry(command, params, options, false, retriedAfterHangRestart);
1441
+ }
1442
+ const tool = command === "tool_call" && typeof params.name === "string" ? params.name : command;
1443
+ if (err instanceof BridgeRestartedError && !retriedAfterHangRestart && isReadOnlyRetryCommand(tool) && !this._shuttingDown && options?.abortSignal?.aborted !== true) {
1444
+ this.logVia(`Retrying read-only request "${tool}" once after the hang watchdog restart`);
1445
+ return this.sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap, true);
1281
1446
  }
1282
1447
  throw err;
1283
1448
  }
@@ -1346,6 +1511,7 @@ var init_bridge = __esm(() => {
1346
1511
  async shutdown() {
1347
1512
  this._shuttingDown = true;
1348
1513
  this.spawnedBinaryFingerprint = null;
1514
+ this.resetHangWatchdog();
1349
1515
  this.clearRestartResetTimer();
1350
1516
  this.clearRestartTimer();
1351
1517
  this.configureWarningClients.clear();
@@ -1544,8 +1710,7 @@ var init_bridge = __esm(() => {
1544
1710
  this.stdoutPending = [];
1545
1711
  this.stdoutPendingLength = 0;
1546
1712
  this.stderrBuffer = "";
1547
- this.lastChildActivityAt = 0;
1548
- this.consecutiveRequestTimeouts = 0;
1713
+ this.resetHangWatchdog();
1549
1714
  this.stderrTail = [];
1550
1715
  this.processLoopReferenced = true;
1551
1716
  this.syncEventLoopHold();
@@ -1589,7 +1754,16 @@ var init_bridge = __esm(() => {
1589
1754
  if (!line || !shouldSurfaceStderrLine(line))
1590
1755
  return;
1591
1756
  const tagged = tagStderrLine(line);
1592
- this.logVia(tagged);
1757
+ const level = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z \[aft(?:-lsp)?\] (INFO|WARN|ERROR|DEBUG) /.exec(line)?.[1];
1758
+ const meta = level ? { engineRecord: true } : undefined;
1759
+ if (level === "WARN")
1760
+ this.warnVia(tagged, meta);
1761
+ else if (level === "ERROR")
1762
+ this.errorVia(tagged, meta);
1763
+ else if (level === "DEBUG")
1764
+ this.debugVia(tagged, meta);
1765
+ else
1766
+ this.logVia(tagged, meta);
1593
1767
  this.pushStderrLine(tagged);
1594
1768
  }
1595
1769
  formatStderrTail() {
@@ -1652,7 +1826,7 @@ var init_bridge = __esm(() => {
1652
1826
  processStdoutLine(line) {
1653
1827
  try {
1654
1828
  const response = JSON.parse(line);
1655
- this.lastChildActivityAt = Date.now();
1829
+ this.lastChildActivity = this.clock.now();
1656
1830
  if (response.type === "progress") {
1657
1831
  const requestId = response.request_id;
1658
1832
  const entry = requestId ? this.pending.get(requestId) : undefined;
@@ -1666,7 +1840,7 @@ var init_bridge = __esm(() => {
1666
1840
  const entry = requestId ? this.pending.get(requestId) : undefined;
1667
1841
  if (requestId && entry) {
1668
1842
  this.pending.delete(requestId);
1669
- clearTimeout(entry.timer);
1843
+ this.clock.clearTimeout(entry.timer);
1670
1844
  entry.onSettled?.();
1671
1845
  entry.resolve({
1672
1846
  success: false,
@@ -1675,6 +1849,8 @@ var init_bridge = __esm(() => {
1675
1849
  asks: response.asks
1676
1850
  });
1677
1851
  }
1852
+ if (requestId)
1853
+ this.settleHangEvidence(requestId);
1678
1854
  return;
1679
1855
  }
1680
1856
  if (response.type === "bash_completed") {
@@ -1704,14 +1880,18 @@ var init_bridge = __esm(() => {
1704
1880
  return;
1705
1881
  }
1706
1882
  const id = response.id;
1883
+ if (id && this.hangProbe?.id === id) {
1884
+ this.onHangProbeAnswered();
1885
+ return;
1886
+ }
1707
1887
  if (id && this.pending.has(id)) {
1708
1888
  const entry = this.pending.get(id);
1709
1889
  if (!entry)
1710
1890
  return;
1711
1891
  this.pending.delete(id);
1712
- clearTimeout(entry.timer);
1892
+ this.settleHangEvidence(id);
1893
+ this.clock.clearTimeout(entry.timer);
1713
1894
  entry.onSettled?.();
1714
- this.consecutiveRequestTimeouts = 0;
1715
1895
  if (response.success !== false && ![
1716
1896
  "configure",
1717
1897
  "version",
@@ -1727,6 +1907,7 @@ var init_bridge = __esm(() => {
1727
1907
  this.accountForBashTaskResponse(entry.command, response);
1728
1908
  entry.resolve(response);
1729
1909
  } else if (id && typeof response.type !== "string") {
1910
+ this.settleHangEvidence(id);
1730
1911
  const taskId = bashTaskIdFrom(response);
1731
1912
  if (taskId) {
1732
1913
  this.warnVia(`Dropped late reply for settled request id=${id} task_id=${taskId}`);
@@ -1757,8 +1938,8 @@ var init_bridge = __esm(() => {
1757
1938
  rejectWith(new BridgeTransportTimeoutError(entry.command, effectiveTimeoutMs, message, outcome));
1758
1939
  };
1759
1940
  entry.resolve = (response) => settle(shellOutcomeFromLateReply(response));
1760
- clearTimeout(entry.timer);
1761
- entry.timer = setTimeout(() => {
1941
+ this.clock.clearTimeout(entry.timer);
1942
+ entry.timer = this.clock.setTimeout(() => {
1762
1943
  if (this.pending.get(id) !== entry)
1763
1944
  return;
1764
1945
  this.pending.delete(id);
@@ -1780,17 +1961,150 @@ var init_bridge = __esm(() => {
1780
1961
  this.outstandingBackgroundTaskIds.add(taskId);
1781
1962
  }
1782
1963
  }
1783
- handleTimeout(trigger, triggeringSessionId) {
1784
- this.consecutiveRequestTimeouts = 0;
1964
+ recordAwaitingReply(id, budgetMs, tool, sessionId) {
1965
+ this.awaitingReply.set(id, { sentAt: this.clock.now(), budgetMs, tool, sessionId });
1966
+ this.armHangWatchdog();
1967
+ }
1968
+ settleHangEvidence(id) {
1969
+ if (!this.awaitingReply.delete(id))
1970
+ return;
1971
+ this.clearHangWatchdogTimer();
1972
+ this.armHangWatchdog();
1973
+ }
1974
+ hangDeadline() {
1975
+ if (this.awaitingReply.size === 0)
1976
+ return null;
1977
+ let oldestId = "";
1978
+ let oldestSentAt = Number.POSITIVE_INFINITY;
1979
+ const budgets = [];
1980
+ for (const [id, entry] of this.awaitingReply) {
1981
+ budgets.push(entry.budgetMs);
1982
+ if (entry.sentAt < oldestSentAt) {
1983
+ oldestSentAt = entry.sentAt;
1984
+ oldestId = id;
1985
+ }
1986
+ }
1987
+ for (const entry of this.pending.values())
1988
+ budgets.push(entry.budgetMs);
1989
+ const windowMs = hangWindowMs(budgets, this.hangWindowMinMs);
1990
+ const silentSince = Math.max(oldestSentAt, this.lastChildActivity);
1991
+ return { deadline: silentSince + windowMs, windowMs, silentSince, oldestId };
1992
+ }
1993
+ armHangWatchdog() {
1994
+ if (this.hangWatchdogTimer || this.hangProbe)
1995
+ return;
1996
+ const hang = this.hangDeadline();
1997
+ if (!hang)
1998
+ return;
1999
+ const timer = this.clock.setTimeout(() => {
2000
+ this.hangWatchdogTimer = null;
2001
+ this.checkForHang();
2002
+ }, Math.max(0, hang.deadline - this.clock.now()));
2003
+ timer.unref?.();
2004
+ this.hangWatchdogTimer = timer;
2005
+ }
2006
+ clearHangWatchdogTimer() {
2007
+ if (this.hangWatchdogTimer)
2008
+ this.clock.clearTimeout(this.hangWatchdogTimer);
2009
+ this.hangWatchdogTimer = null;
2010
+ }
2011
+ clearHangProbe() {
2012
+ if (this.hangProbe)
2013
+ this.clock.clearTimeout(this.hangProbe.timer);
2014
+ this.hangProbe = null;
2015
+ }
2016
+ resetHangWatchdog() {
2017
+ this.clearHangWatchdogTimer();
2018
+ this.clearHangProbe();
2019
+ this.awaitingReply.clear();
2020
+ this.lastChildActivity = Number.NEGATIVE_INFINITY;
2021
+ this.hangSuppressionLogged = false;
2022
+ }
2023
+ checkForHang() {
2024
+ if (!this.isAlive() || this.hangProbe)
2025
+ return;
2026
+ const hang = this.hangDeadline();
2027
+ if (!hang)
2028
+ return;
2029
+ const now = this.clock.now();
2030
+ if (now < hang.deadline) {
2031
+ this.armHangWatchdog();
2032
+ return;
2033
+ }
2034
+ if (this.timeoutRestartAwaitingRecovery) {
2035
+ if (!this.hangSuppressionLogged) {
2036
+ this.hangSuppressionLogged = true;
2037
+ this.warnVia(`Hang watchdog: child silent past its window, but it was already restarted once and no tool has succeeded since; not restarting again; ${this.hangAttribution(hang)}`);
2038
+ }
2039
+ return;
2040
+ }
2041
+ this.sendHangProbe(hang);
2042
+ }
2043
+ hangAttribution(hang) {
2044
+ return `silent_ms=${this.clock.now() - hang.silentSince} ` + `hang_window_ms=${hang.windowMs} ` + `oldest_unanswered_request_id=${hang.oldestId} ` + `oldest_unanswered_tool=${JSON.stringify(this.awaitingReply.get(hang.oldestId)?.tool)} ` + `bridge_generation=${this.processGeneration}`;
2045
+ }
2046
+ sendHangProbe(hang) {
2047
+ const id = `hang-probe-${this.nextId++}`;
2048
+ const timer = this.clock.setTimeout(() => this.onHangProbeTimeout(id), this.hangProbeTimeoutMs);
2049
+ timer.unref?.();
2050
+ this.hangProbe = { id, sentAt: this.clock.now(), timer };
2051
+ this.logVia(`Hang watchdog: child silent past its window; sending liveness probe id=${id} probe_timeout_ms=${this.hangProbeTimeoutMs}; ${this.hangAttribution(hang)}`);
2052
+ try {
2053
+ this.process?.stdin?.write(`${JSON.stringify({ id, command: "ping" })}
2054
+ `, () => {});
2055
+ } catch {}
2056
+ }
2057
+ onHangProbeAnswered() {
2058
+ const probeId = this.hangProbe?.id;
2059
+ this.clearHangProbe();
2060
+ let dropped = 0;
2061
+ for (const id of Array.from(this.awaitingReply.keys())) {
2062
+ if (this.pending.has(id))
2063
+ continue;
2064
+ this.awaitingReply.delete(id);
2065
+ dropped += 1;
2066
+ }
2067
+ this.logVia(`Hang watchdog: liveness probe id=${probeId} answered; child kept; dropped ${dropped} unanswered request(s) whose callers already settled, ${this.awaitingReply.size} still waiting`);
2068
+ this.clearHangWatchdogTimer();
2069
+ this.armHangWatchdog();
2070
+ }
2071
+ onHangProbeTimeout(probeId) {
2072
+ const probe = this.hangProbe;
2073
+ if (!probe || probe.id !== probeId)
2074
+ return;
2075
+ this.hangProbe = null;
2076
+ if (!this.isAlive())
2077
+ return;
2078
+ const hang = this.hangDeadline();
2079
+ if (!hang)
2080
+ return;
2081
+ if (this.lastChildActivity >= probe.sentAt) {
2082
+ this.armHangWatchdog();
2083
+ return;
2084
+ }
2085
+ const attribution = `${this.hangAttribution(hang)} probe_id=${probeId} probe_timeout_ms=${this.hangProbeTimeoutMs}`;
2086
+ this.killHungChild(attribution, this.awaitingReply.get(hang.oldestId)?.sessionId);
2087
+ }
2088
+ killHungChild(attribution, sessionId) {
1785
2089
  this.timeoutRestartAwaitingRecovery = true;
1786
2090
  this.spawnedBinaryFingerprint = null;
1787
- const abortedSiblings = Array.from(this.pending, ([requestId, entry]) => ({
2091
+ const aborted = Array.from(this.pending, ([requestId, entry]) => ({
1788
2092
  request_id: requestId,
1789
- tool: entry.tool
2093
+ tool: entry.tool,
2094
+ retry: isReadOnlyRetryCommand(entry.tool)
1790
2095
  }));
1791
- const attribution = `triggering_request_id=${trigger.requestId} ` + `triggering_tool=${JSON.stringify(trigger.tool)} ` + `bridge_generation=${trigger.generation} ` + `aborted_siblings=${JSON.stringify(abortedSiblings)}`;
1792
- this.rejectAllPending(new BridgeTransportUnknownOutcomeError(`${this.errorPrefix} bridge killed during sibling timeout — request aborted; ${attribution}`));
2096
+ const fullAttribution = `${attribution} aborted_requests=${JSON.stringify(aborted)}`;
2097
+ this.configureWarningClients.clear();
2098
+ const pending = Array.from(this.pending.values());
2099
+ this.pending.clear();
2100
+ for (const entry of pending) {
2101
+ this.clock.clearTimeout(entry.timer);
2102
+ entry.onSettled?.();
2103
+ const readOnly = isReadOnlyRetryCommand(entry.tool);
2104
+ entry.reject(new BridgeRestartedError(entry.tool, !readOnly, readOnly ? `${this.errorPrefix} bridge restarted: the child wrote nothing for longer than its hang window and did not answer a liveness probe; "${entry.tool}" is read-only and safe to retry; ${fullAttribution}` : `${this.errorPrefix} bridge restarted: the child wrote nothing for longer than its hang window and did not answer a liveness probe; the outcome of "${entry.tool}" is unknown — a mutation may already have been applied, inspect before repeating it; ${fullAttribution}`));
2105
+ }
1793
2106
  this.outstandingBackgroundTaskIds.clear();
2107
+ this.resetHangWatchdog();
1794
2108
  if (this.process) {
1795
2109
  this.process.kill("SIGKILL");
1796
2110
  this.process = null;
@@ -1799,9 +2113,9 @@ var init_bridge = __esm(() => {
1799
2113
  this.configured = false;
1800
2114
  const tail = this.formatStderrTail();
1801
2115
  this.stderrTail = [];
1802
- const killedMsg = tail ? `Bridge killed after timeout; ${attribution}.${tail}` : `Bridge killed after timeout; ${attribution} (see ${this.getLogFilePathVia()})`;
1803
- if (triggeringSessionId) {
1804
- this.sessionErrorVia(triggeringSessionId, killedMsg);
2116
+ const killedMsg = tail ? `Bridge killed after timeout of the hang watchdog; ${fullAttribution}.${tail}` : `Bridge killed after timeout of the hang watchdog; ${fullAttribution} (see ${this.getLogFilePathVia()})`;
2117
+ if (sessionId) {
2118
+ this.sessionErrorVia(sessionId, killedMsg);
1805
2119
  } else {
1806
2120
  this.errorVia(killedMsg);
1807
2121
  }
@@ -1850,7 +2164,7 @@ var init_bridge = __esm(() => {
1850
2164
  rejectAllPending(error) {
1851
2165
  this.configureWarningClients.clear();
1852
2166
  for (const [_id, entry] of this.pending) {
1853
- clearTimeout(entry.timer);
2167
+ this.clock.clearTimeout(entry.timer);
1854
2168
  entry.onSettled?.();
1855
2169
  entry.reject(error);
1856
2170
  }
@@ -3069,7 +3383,7 @@ var init_lifecycle_registry = __esm(() => {
3069
3383
  var init_project_identity = () => {};
3070
3384
 
3071
3385
  // ../aft-bridge/dist/subc-transport.js
3072
- var CALL_DEADLINE_EXPIRED, LOCALLY_SATISFIED_COMMANDS, dispatchProbes;
3386
+ var REQUEST_CONNECTION_LOSS_CODES, dispatchedRequestDeadlines, CALL_DEADLINE_EXPIRED, LOCALLY_SATISFIED_COMMANDS, dispatchProbes;
3073
3387
  var init_subc_transport = __esm(() => {
3074
3388
  init_dist();
3075
3389
  init_active_logger();
@@ -3077,17 +3391,20 @@ var init_subc_transport = __esm(() => {
3077
3391
  init_error_contract();
3078
3392
  init_lifecycle_registry();
3079
3393
  init_project_identity();
3394
+ REQUEST_CONNECTION_LOSS_CODES = new Set(["ECONNABORTED", "ECONNRESET", "EPIPE"]);
3395
+ dispatchedRequestDeadlines = new WeakSet;
3080
3396
  CALL_DEADLINE_EXPIRED = Symbol("call deadline expired");
3081
3397
  LOCALLY_SATISFIED_COMMANDS = new Set(["configure"]);
3082
3398
  dispatchProbes = new Map;
3083
3399
  });
3084
3400
 
3085
3401
  // ../aft-bridge/dist/error-contract.js
3086
- var SUBC_MODULE_DOWN_CODES;
3402
+ var CONNECTION_LOSS_ERROR_CODES, SUBC_MODULE_DOWN_CODES;
3087
3403
  var init_error_contract = __esm(() => {
3088
3404
  init_dist();
3089
3405
  init_bridge();
3090
3406
  init_subc_transport();
3407
+ CONNECTION_LOSS_ERROR_CODES = new Set(["ECONNABORTED", "ECONNRESET", "EPIPE"]);
3091
3408
  SUBC_MODULE_DOWN_CODES = new Set([
3092
3409
  "unknown_module",
3093
3410
  "module_reloading",
@@ -3214,6 +3531,14 @@ function translateInspectLspPaths(map, prefix, out) {
3214
3531
  recordRetired(out, prefix, `inspect.${leaf}`);
3215
3532
  }
3216
3533
  }
3534
+ function dropNoEffectPaths(map, prefix, out) {
3535
+ for (const path of REMOVED_NO_EFFECT_PATHS) {
3536
+ const [container, leaf] = path.split(".");
3537
+ if (container && leaf && takeNested(map, container, leaf).found) {
3538
+ recordRetired(out, prefix, path);
3539
+ }
3540
+ }
3541
+ }
3217
3542
  function translateExperimentalPaths(map, prefix, out) {
3218
3543
  for (const [oldKey, leaf] of [
3219
3544
  ["experimental_lsp_ty", "lsp_ty"],
@@ -3275,6 +3600,7 @@ function translateBlock(map, userBase, blockLabel, out) {
3275
3600
  const prefix = blockLabel === "base" ? "" : `${blockLabel}.`;
3276
3601
  translateGithubAliases(map, prefix, out);
3277
3602
  translateInspectLspPaths(map, prefix, out);
3603
+ dropNoEffectPaths(map, prefix, out);
3278
3604
  translateExperimentalPaths(map, prefix, out);
3279
3605
  if (isRecord(map.sandbox) && Object.hasOwn(map.sandbox, "enabled")) {
3280
3606
  const sandbox = map.sandbox;
@@ -3482,7 +3808,7 @@ function mergeIndexes(base, override, restricted) {
3482
3808
  }
3483
3809
  return merged;
3484
3810
  }
3485
- var CANONICAL_TOOLS, HOST_TOOL_NAMES, DEFAULT_DISABLED_TOOLS, LEGACY_TOOL_ALIASES, RETIRED_PATHS, INDEX_INPUTS, LSP_IDLE_MINUTES_DEFAULT = 60, LSP_IDLE_MINUTES_MIN = 5, LSP_IDLE_MINUTES_MAX = 1440, RETIRED_ENABLED_FALSE_INDEXES_NOTE = "enabled: false no longer turns AFT off: it is translated to disabling every tool, but the trigram, semantic and callgraph indexes still build. To keep AFT from indexing this repository, also set indexes.trigram, indexes.semantic and indexes.callgraph to false.", SEMANTIC_COST_NOTICE_DIGEST;
3811
+ var CANONICAL_TOOLS, HOST_TOOL_NAMES, DEFAULT_DISABLED_TOOLS, LEGACY_TOOL_ALIASES, RETIRED_PATHS, INDEX_INPUTS, LSP_IDLE_MINUTES_DEFAULT = 60, LSP_IDLE_MINUTES_MIN = 5, LSP_IDLE_MINUTES_MAX = 1440, REMOVED_NO_EFFECT_PATHS, RETIRED_ENABLED_FALSE_INDEXES_NOTE = "enabled: false no longer turns AFT off: it is translated to disabling every tool, but the trigram, semantic and callgraph indexes still build. To keep AFT from indexing this repository, also set indexes.trigram, indexes.semantic and indexes.callgraph to false.", SEMANTIC_COST_NOTICE_DIGEST, SECURITY_CONFIG_SECTIONS;
3486
3812
  var init_feature_config = __esm(() => {
3487
3813
  CANONICAL_TOOLS = [
3488
3814
  "aft_callgraph",
@@ -3550,7 +3876,26 @@ var init_feature_config = __esm(() => {
3550
3876
  ["semantic", "semantic_search", "experimental_semantic_search"],
3551
3877
  ["callgraph", "callgraph_store", undefined]
3552
3878
  ];
3879
+ REMOVED_NO_EFFECT_PATHS = ["bridge.hang_threshold"];
3553
3880
  SEMANTIC_COST_NOTICE_DIGEST = noticeDigest({ notice: "semantic_default_cost_v1" });
3881
+ SECURITY_CONFIG_SECTIONS = new Set([
3882
+ "restrict_to_project_root",
3883
+ "url_fetch_allow_private",
3884
+ "sandbox",
3885
+ "indexes",
3886
+ "index",
3887
+ "remote_exec",
3888
+ "disabled_tools",
3889
+ "bash",
3890
+ "lsp",
3891
+ "semantic",
3892
+ "search",
3893
+ "subc",
3894
+ "opencode",
3895
+ "github",
3896
+ "gh_shim",
3897
+ "auto_update"
3898
+ ]);
3554
3899
  });
3555
3900
  // ../aft-bridge/dist/pool.js
3556
3901
  var DEFAULT_IDLE_TIMEOUT_MS, CLEANUP_INTERVAL_MS;
@@ -3590,8 +3935,17 @@ var init_location_lifecycle = __esm(() => {
3590
3935
  });
3591
3936
 
3592
3937
  // ../aft-bridge/dist/lsp-github-probe.js
3938
+ var ALLOWED_GITHUB_HOSTS;
3593
3939
  var init_lsp_github_probe = __esm(() => {
3594
3940
  init_active_logger();
3941
+ ALLOWED_GITHUB_HOSTS = new Set([
3942
+ "github.com",
3943
+ "api.github.com",
3944
+ "objects.githubusercontent.com",
3945
+ "release-assets.githubusercontent.com",
3946
+ "raw.githubusercontent.com",
3947
+ "codeload.github.com"
3948
+ ]);
3595
3949
  });
3596
3950
 
3597
3951
  // ../aft-bridge/dist/lsp-github-table.js
@@ -3656,166 +4010,15 @@ var init_lsp_github_install = __esm(() => {
3656
4010
  init_tar_executable();
3657
4011
  });
3658
4012
 
3659
- // ../aft-bridge/dist/message-detach.js
3660
- var BASH_WAIT_DETACH_MAGIC_KEYWORD = "&detach", STANDALONE_DETACH_KEYWORD_SOURCE, STANDALONE_DETACH_KEYWORD_PATTERN, STANDALONE_DETACH_KEYWORDS_PATTERN;
3661
- var init_message_detach = __esm(() => {
3662
- STANDALONE_DETACH_KEYWORD_SOURCE = `(^|[^\\p{L}\\p{N}_])${BASH_WAIT_DETACH_MAGIC_KEYWORD}(?![\\p{L}\\p{N}_])`;
3663
- STANDALONE_DETACH_KEYWORD_PATTERN = new RegExp(STANDALONE_DETACH_KEYWORD_SOURCE, "u");
3664
- STANDALONE_DETACH_KEYWORDS_PATTERN = new RegExp(STANDALONE_DETACH_KEYWORD_SOURCE, "gu");
3665
- });
3666
-
3667
- // ../aft-bridge/dist/paths.js
3668
- import { homedir as homedir7 } from "node:os";
3669
- import { dirname as dirname2, isAbsolute as isAbsolute4, join as join9, resolve as resolve5 } from "node:path";
3670
- function homeDir3() {
3671
- if (process.platform === "win32")
3672
- return process.env.USERPROFILE || process.env.HOME || homedir7();
3673
- return process.env.HOME || homedir7();
3674
- }
3675
- function configHome() {
3676
- const xdg = process.env.XDG_CONFIG_HOME;
3677
- if (xdg && isAbsolute4(xdg))
3678
- return xdg;
3679
- return join9(homeDir3(), ".config");
3680
- }
3681
- function resolveCortexKitUserConfigPath() {
3682
- return join9(configHome(), "cortexkit", "aft.jsonc");
3683
- }
3684
- function resolveCortexKitProjectConfigPath(projectDirectory) {
3685
- return join9(projectDirectory, ".cortexkit", "aft.jsonc");
3686
- }
3687
- var init_paths = __esm(() => {
3688
- init_private_storage();
3689
- });
3690
-
3691
- // ../aft-bridge/dist/resolver.js
3692
- import { closeSync as closeSync4, existsSync as existsSync4, promises as fsPromises, openSync as openSync4, readFileSync as readFileSync4, readSync as readSync3 } from "node:fs";
3693
- function isNativeExecutable(binaryPath) {
3694
- let fd = null;
3695
- try {
3696
- fd = openSync4(binaryPath, "r");
3697
- const buf = Buffer.alloc(4);
3698
- const read = readSync3(fd, buf, 0, 4, 0);
3699
- if (read < 2)
3700
- return false;
3701
- const b0 = buf[0];
3702
- const b1 = buf[1];
3703
- if (b0 === 35 && b1 === 33)
3704
- return false;
3705
- const m32 = buf.readUInt32BE(0);
3706
- const machO = new Set([4277009102, 4277009103, 3472551422, 3489328638, 3405691582]);
3707
- if (read >= 4 && machO.has(m32))
3708
- return true;
3709
- if (read >= 4 && m32 === 2135247942)
3710
- return true;
3711
- if (b0 === 77 && b1 === 90)
3712
- return true;
3713
- return false;
3714
- } catch {
3715
- return false;
3716
- } finally {
3717
- if (fd !== null) {
3718
- try {
3719
- closeSync4(fd);
3720
- } catch {}
3721
- }
3722
- }
3723
- }
3724
- var cacheCopiesInFlight;
3725
- var init_resolver = __esm(() => {
3726
- init_active_logger();
3727
- init_binary_identity();
3728
- init_cache_paths();
3729
- init_downloader();
3730
- init_platform();
3731
- init_version_probe();
3732
- cacheCopiesInFlight = new Map;
3733
- });
3734
-
3735
- // ../aft-bridge/dist/migration.js
3736
- var DEFAULT_TIMEOUT_MS;
3737
- var init_migration = __esm(() => {
3738
- init_child_process();
3739
- init_paths();
3740
- init_resolver();
3741
- init_storage_paths();
3742
- init_storage_paths();
3743
- DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
3744
- });
3745
-
3746
- // ../aft-bridge/dist/migration-notices.js
3747
- import { readFileSync as readFileSync5, renameSync as renameSync3, rmdirSync, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
3748
- import { homedir as homedir8 } from "node:os";
3749
- import { dirname as dirname3, join as join10, resolve as resolve6 } from "node:path";
3750
- function migrationNoticeStorePath(env = process.env) {
3751
- const stateHome = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0 ? env.XDG_STATE_HOME : join10(homedir8(), ".local", "state");
3752
- return join10(stateHome, "cortexkit", "aft", "migration-notices.json");
3753
- }
3754
- function readRecords(storePath) {
3755
- try {
3756
- const parsed = JSON.parse(readFileSync5(storePath, "utf8"));
3757
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
3758
- } catch {
3759
- return {};
3760
- }
3761
- }
3762
- function withStoreLock(storePath, action) {
3763
- const lockPath = `${storePath}.lock`;
3764
- openPrivateStorageDir(dirname3(storePath));
3765
- const deadline = Date.now() + 2000;
3766
- for (;; ) {
3767
- try {
3768
- privateMkdirSync(lockPath, false);
3769
- break;
3770
- } catch (error) {
3771
- if (error.code !== "EEXIST")
3772
- throw error;
3773
- try {
3774
- if (Date.now() - statSync3(lockPath).mtimeMs > LOCK_STALE_MS)
3775
- rmdirSync(lockPath);
3776
- } catch {}
3777
- if (Date.now() > deadline)
3778
- throw error;
3779
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
3780
- }
3781
- }
3782
- try {
3783
- return action();
3784
- } finally {
3785
- try {
3786
- rmdirSync(lockPath);
3787
- } catch {}
3788
- }
3789
- }
3790
- function clearMigrationNotices(configPath, storePath = migrationNoticeStorePath()) {
3791
- const prefix = `${resolve6(configPath)}|`;
3792
- withStoreLock(storePath, () => {
3793
- const records = readRecords(storePath);
3794
- for (const key of Object.keys(records))
3795
- if (key.startsWith(prefix))
3796
- delete records[key];
3797
- writeFileSync2(storePath, `${JSON.stringify(records, null, 2)}
3798
- `, {
3799
- encoding: "utf8",
3800
- mode: PRIVATE_FILE_MODE
3801
- });
3802
- });
3803
- }
3804
- var LOCK_STALE_MS = 1e4;
3805
- var init_migration_notices = __esm(() => {
3806
- init_feature_config();
3807
- init_private_storage();
3808
- });
3809
-
3810
4013
  // ../aft-bridge/dist/npm-resolver.js
3811
- import { readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
3812
- import { homedir as homedir9 } from "node:os";
3813
- import { dirname as dirname4, isAbsolute as isAbsolute5, join as join11 } from "node:path";
4014
+ import { readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
4015
+ import { homedir as homedir7 } from "node:os";
4016
+ import { dirname as dirname2, isAbsolute as isAbsolute4, join as join9 } from "node:path";
3814
4017
  function defaultDeps() {
3815
4018
  return {
3816
4019
  platform: process.platform,
3817
4020
  env: process.env,
3818
- home: homedir9(),
4021
+ home: homedir7(),
3819
4022
  execPath: process.execPath
3820
4023
  };
3821
4024
  }
@@ -3824,7 +4027,7 @@ function npmBinaryName(platform) {
3824
4027
  }
3825
4028
  function isFile(p) {
3826
4029
  try {
3827
- return statSync4(p).isFile();
4030
+ return statSync3(p).isFile();
3828
4031
  } catch {
3829
4032
  return false;
3830
4033
  }
@@ -3837,16 +4040,16 @@ function npmFromPath(deps) {
3837
4040
  const separator = deps.platform === "win32" ? ";" : ":";
3838
4041
  for (const entry of raw.split(separator)) {
3839
4042
  const dir = entry.trim().replace(/^"|"$/g, "");
3840
- if (!dir || !isAbsolute5(dir))
4043
+ if (!dir || !isAbsolute4(dir))
3841
4044
  continue;
3842
- if (isFile(join11(dir, name)))
4045
+ if (isFile(join9(dir, name)))
3843
4046
  return dir;
3844
4047
  }
3845
4048
  return null;
3846
4049
  }
3847
4050
  function npmAdjacentToNode(deps) {
3848
- const dir = dirname4(deps.execPath);
3849
- return isFile(join11(dir, npmBinaryName(deps.platform))) ? dir : null;
4051
+ const dir = dirname2(deps.execPath);
4052
+ return isFile(join9(dir, npmBinaryName(deps.platform))) ? dir : null;
3850
4053
  }
3851
4054
  function highestVersionedNodeBin(installsDir, name) {
3852
4055
  let entries;
@@ -3855,8 +4058,8 @@ function highestVersionedNodeBin(installsDir, name) {
3855
4058
  } catch {
3856
4059
  return null;
3857
4060
  }
3858
- const candidates = entries.filter((v) => isFile(join11(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
3859
- return candidates.length > 0 ? join11(installsDir, candidates[0], "bin") : null;
4061
+ const candidates = entries.filter((v) => isFile(join9(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
4062
+ return candidates.length > 0 ? join9(installsDir, candidates[0], "bin") : null;
3860
4063
  }
3861
4064
  function compareVersionsDesc(a, b) {
3862
4065
  const pa = a.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10));
@@ -3881,22 +4084,22 @@ function wellKnownNpmDirs(deps) {
3881
4084
  const programFiles = env.ProgramFiles || "C:\\Program Files";
3882
4085
  const appData = env.APPDATA;
3883
4086
  const localAppData = env.LOCALAPPDATA;
3884
- push(join11(programFiles, "nodejs"));
4087
+ push(join9(programFiles, "nodejs"));
3885
4088
  if (appData)
3886
- push(join11(appData, "npm"));
4089
+ push(join9(appData, "npm"));
3887
4090
  if (localAppData)
3888
- push(join11(localAppData, "Volta", "bin"));
4091
+ push(join9(localAppData, "Volta", "bin"));
3889
4092
  if (env.NVM_SYMLINK)
3890
4093
  push(env.NVM_SYMLINK);
3891
4094
  } else {
3892
4095
  if (env.NVM_BIN)
3893
4096
  push(env.NVM_BIN);
3894
- push(highestVersionedNodeBin(join11(home, ".nvm", "versions", "node"), name));
3895
- push(highestVersionedNodeBin(join11(home, ".local", "share", "mise", "installs", "node"), name));
3896
- push(highestVersionedNodeBin(join11(home, ".asdf", "installs", "nodejs"), name));
3897
- push(join11(home, ".volta", "bin"));
3898
- push(join11(home, ".asdf", "shims"));
3899
- const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join11(home, ".local", "bin")]);
4097
+ push(highestVersionedNodeBin(join9(home, ".nvm", "versions", "node"), name));
4098
+ push(highestVersionedNodeBin(join9(home, ".local", "share", "mise", "installs", "node"), name));
4099
+ push(highestVersionedNodeBin(join9(home, ".asdf", "installs", "nodejs"), name));
4100
+ push(join9(home, ".volta", "bin"));
4101
+ push(join9(home, ".asdf", "shims"));
4102
+ const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join9(home, ".local", "bin")]);
3900
4103
  for (const dir of systemDirs)
3901
4104
  push(dir);
3902
4105
  }
@@ -3906,12 +4109,12 @@ function resolveNpm(deps = defaultDeps()) {
3906
4109
  const name = npmBinaryName(deps.platform);
3907
4110
  const onPath = npmFromPath(deps);
3908
4111
  if (onPath)
3909
- return { command: join11(onPath, name), binDir: onPath };
4112
+ return { command: join9(onPath, name), binDir: onPath };
3910
4113
  const adjacent = npmAdjacentToNode(deps);
3911
4114
  if (adjacent)
3912
- return { command: join11(adjacent, name), binDir: adjacent };
4115
+ return { command: join9(adjacent, name), binDir: adjacent };
3913
4116
  for (const dir of wellKnownNpmDirs(deps)) {
3914
- const candidate = join11(dir, name);
4117
+ const candidate = join9(dir, name);
3915
4118
  if (isFile(candidate))
3916
4119
  return { command: candidate, binDir: dir };
3917
4120
  }
@@ -4072,7 +4275,7 @@ function terminateNpmProcessTree(child, invocation, env = process.env, gracePeri
4072
4275
  };
4073
4276
  child.once("exit", onChildExit);
4074
4277
  const systemRoot = env.SystemRoot ?? env.SYSTEMROOT;
4075
- const taskkill = systemRoot ? join11(systemRoot, "System32", "taskkill.exe") : "taskkill.exe";
4278
+ const taskkill = systemRoot ? join9(systemRoot, "System32", "taskkill.exe") : "taskkill.exe";
4076
4279
  try {
4077
4280
  killer = spawn(taskkill, ["/pid", String(child.pid), "/t", "/f"], {
4078
4281
  stdio: "ignore",
@@ -4107,6 +4310,166 @@ var init_npm_resolver = __esm(() => {
4107
4310
  };
4108
4311
  });
4109
4312
 
4313
+ // ../aft-bridge/dist/lsp-npm-policy.js
4314
+ var npmCutoffSupport, NPM_PROBE_RETRY_MS;
4315
+ var init_lsp_npm_policy = __esm(() => {
4316
+ init_child_process();
4317
+ init_npm_resolver();
4318
+ npmCutoffSupport = new Map;
4319
+ NPM_PROBE_RETRY_MS = 5 * 60000;
4320
+ });
4321
+
4322
+ // ../aft-bridge/dist/message-detach.js
4323
+ var BASH_WAIT_DETACH_MAGIC_KEYWORD = "&detach", STANDALONE_DETACH_KEYWORD_SOURCE, STANDALONE_DETACH_KEYWORD_PATTERN, STANDALONE_DETACH_KEYWORDS_PATTERN;
4324
+ var init_message_detach = __esm(() => {
4325
+ STANDALONE_DETACH_KEYWORD_SOURCE = `(^|[^\\p{L}\\p{N}_])${BASH_WAIT_DETACH_MAGIC_KEYWORD}(?![\\p{L}\\p{N}_])`;
4326
+ STANDALONE_DETACH_KEYWORD_PATTERN = new RegExp(STANDALONE_DETACH_KEYWORD_SOURCE, "u");
4327
+ STANDALONE_DETACH_KEYWORDS_PATTERN = new RegExp(STANDALONE_DETACH_KEYWORD_SOURCE, "gu");
4328
+ });
4329
+
4330
+ // ../aft-bridge/dist/paths.js
4331
+ import { homedir as homedir8 } from "node:os";
4332
+ import { dirname as dirname3, isAbsolute as isAbsolute5, join as join10, resolve as resolve5 } from "node:path";
4333
+ function homeDir3() {
4334
+ if (process.platform === "win32")
4335
+ return process.env.USERPROFILE || process.env.HOME || homedir8();
4336
+ return process.env.HOME || homedir8();
4337
+ }
4338
+ function configHome() {
4339
+ const xdg = process.env.XDG_CONFIG_HOME;
4340
+ if (xdg && isAbsolute5(xdg))
4341
+ return xdg;
4342
+ return join10(homeDir3(), ".config");
4343
+ }
4344
+ function resolveCortexKitUserConfigPath() {
4345
+ return join10(configHome(), "cortexkit", "aft.jsonc");
4346
+ }
4347
+ function resolveCortexKitProjectConfigPath(projectDirectory) {
4348
+ return join10(projectDirectory, ".cortexkit", "aft.jsonc");
4349
+ }
4350
+ var init_paths = __esm(() => {
4351
+ init_private_storage();
4352
+ });
4353
+
4354
+ // ../aft-bridge/dist/resolver.js
4355
+ import { closeSync as closeSync4, existsSync as existsSync4, promises as fsPromises, openSync as openSync4, readFileSync as readFileSync4, readSync as readSync3 } from "node:fs";
4356
+ function isNativeExecutable(binaryPath) {
4357
+ let fd = null;
4358
+ try {
4359
+ fd = openSync4(binaryPath, "r");
4360
+ const buf = Buffer.alloc(4);
4361
+ const read = readSync3(fd, buf, 0, 4, 0);
4362
+ if (read < 2)
4363
+ return false;
4364
+ const b0 = buf[0];
4365
+ const b1 = buf[1];
4366
+ if (b0 === 35 && b1 === 33)
4367
+ return false;
4368
+ const m32 = buf.readUInt32BE(0);
4369
+ const machO = new Set([4277009102, 4277009103, 3472551422, 3489328638, 3405691582]);
4370
+ if (read >= 4 && machO.has(m32))
4371
+ return true;
4372
+ if (read >= 4 && m32 === 2135247942)
4373
+ return true;
4374
+ if (b0 === 77 && b1 === 90)
4375
+ return true;
4376
+ return false;
4377
+ } catch {
4378
+ return false;
4379
+ } finally {
4380
+ if (fd !== null) {
4381
+ try {
4382
+ closeSync4(fd);
4383
+ } catch {}
4384
+ }
4385
+ }
4386
+ }
4387
+ var cacheCopiesInFlight;
4388
+ var init_resolver = __esm(() => {
4389
+ init_active_logger();
4390
+ init_binary_identity();
4391
+ init_cache_paths();
4392
+ init_downloader();
4393
+ init_platform();
4394
+ init_version_probe();
4395
+ cacheCopiesInFlight = new Map;
4396
+ });
4397
+
4398
+ // ../aft-bridge/dist/migration.js
4399
+ var DEFAULT_TIMEOUT_MS;
4400
+ var init_migration = __esm(() => {
4401
+ init_child_process();
4402
+ init_paths();
4403
+ init_resolver();
4404
+ init_storage_paths();
4405
+ init_storage_paths();
4406
+ DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
4407
+ });
4408
+
4409
+ // ../aft-bridge/dist/migration-notices.js
4410
+ import { readFileSync as readFileSync5, renameSync as renameSync3, rmdirSync, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
4411
+ import { homedir as homedir9 } from "node:os";
4412
+ import { dirname as dirname4, join as join11, resolve as resolve6 } from "node:path";
4413
+ function migrationNoticeStorePath(env = process.env) {
4414
+ const stateHome = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0 ? env.XDG_STATE_HOME : join11(homedir9(), ".local", "state");
4415
+ return join11(stateHome, "cortexkit", "aft", "migration-notices.json");
4416
+ }
4417
+ function readRecords(storePath) {
4418
+ try {
4419
+ const parsed = JSON.parse(readFileSync5(storePath, "utf8"));
4420
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
4421
+ } catch {
4422
+ return {};
4423
+ }
4424
+ }
4425
+ function withStoreLock(storePath, action) {
4426
+ const lockPath = `${storePath}.lock`;
4427
+ openPrivateStorageDir(dirname4(storePath));
4428
+ const deadline = Date.now() + 2000;
4429
+ for (;; ) {
4430
+ try {
4431
+ privateMkdirSync(lockPath, false);
4432
+ break;
4433
+ } catch (error) {
4434
+ if (error.code !== "EEXIST")
4435
+ throw error;
4436
+ try {
4437
+ if (Date.now() - statSync4(lockPath).mtimeMs > LOCK_STALE_MS)
4438
+ rmdirSync(lockPath);
4439
+ } catch {}
4440
+ if (Date.now() > deadline)
4441
+ throw error;
4442
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
4443
+ }
4444
+ }
4445
+ try {
4446
+ return action();
4447
+ } finally {
4448
+ try {
4449
+ rmdirSync(lockPath);
4450
+ } catch {}
4451
+ }
4452
+ }
4453
+ function clearMigrationNotices(configPath, storePath = migrationNoticeStorePath()) {
4454
+ const prefix = `${resolve6(configPath)}|`;
4455
+ withStoreLock(storePath, () => {
4456
+ const records = readRecords(storePath);
4457
+ for (const key of Object.keys(records))
4458
+ if (key.startsWith(prefix))
4459
+ delete records[key];
4460
+ writeFileSync2(storePath, `${JSON.stringify(records, null, 2)}
4461
+ `, {
4462
+ encoding: "utf8",
4463
+ mode: PRIVATE_FILE_MODE
4464
+ });
4465
+ });
4466
+ }
4467
+ var LOCK_STALE_MS = 1e4;
4468
+ var init_migration_notices = __esm(() => {
4469
+ init_feature_config();
4470
+ init_private_storage();
4471
+ });
4472
+
4110
4473
  // ../aft-bridge/dist/onnx-probe.js
4111
4474
  import { closeSync as closeSync5, existsSync as existsSync5, openSync as openSync5, readSync as readSync4, realpathSync as realpathSync2 } from "node:fs";
4112
4475
  function readAt(fd, offset, length) {
@@ -5091,7 +5454,6 @@ function findExecutableOnPath(name, options = {}) {
5091
5454
  return findExecutablesOnPath(name, options)[0] ?? null;
5092
5455
  }
5093
5456
  var init_path_lookup = () => {};
5094
-
5095
5457
  // ../aft-bridge/dist/semantic-status.js
5096
5458
  var SEMANTIC_PROGRESS_STATUSES, SEMANTIC_READY_STATUSES, SEMANTIC_INACTIVE_STATUSES;
5097
5459
  var init_semantic_status = __esm(() => {
@@ -5102,9 +5464,12 @@ var init_semantic_status = __esm(() => {
5102
5464
  // ../aft-bridge/dist/index.js
5103
5465
  var init_dist2 = __esm(() => {
5104
5466
  init_active_logger();
5467
+ init_bash_env();
5105
5468
  init_bash_format();
5106
5469
  init_bash_hints();
5107
5470
  init_bash_host_fallback();
5471
+ init_bash_permissions();
5472
+ init_bash_watch_lines();
5108
5473
  init_binary_identity();
5109
5474
  init_bridge();
5110
5475
  init_cache_paths();
@@ -5123,6 +5488,7 @@ var init_dist2 = __esm(() => {
5123
5488
  init_lsp_github_install();
5124
5489
  init_lsp_github_probe();
5125
5490
  init_lsp_github_table();
5491
+ init_lsp_npm_policy();
5126
5492
  init_message_detach();
5127
5493
  init_migration();
5128
5494
  init_migration_notices();
@@ -5136,6 +5502,7 @@ var init_dist2 = __esm(() => {
5136
5502
  init_platform();
5137
5503
  init_pool();
5138
5504
  init_project_identity();
5505
+ init_protocol();
5139
5506
  init_resolver();
5140
5507
  init_revivable_transport();
5141
5508
  init_semantic_status();
@@ -5273,12 +5640,7 @@ function firstExisting(candidates) {
5273
5640
  return null;
5274
5641
  }
5275
5642
  function platformKey2(platform = process.platform, arch = process.arch) {
5276
- const table = {
5277
- darwin: { arm64: "darwin-arm64", x64: "darwin-x64" },
5278
- linux: { arm64: "linux-arm64", x64: "linux-x64" },
5279
- win32: { x64: "win32-x64" }
5280
- };
5281
- return table[platform]?.[arch] ?? null;
5643
+ return PLATFORM_ARCH_MAP[platform]?.[arch] ?? null;
5282
5644
  }
5283
5645
  function newestCachedBinary(cacheDir, binaryName) {
5284
5646
  let entries;
@@ -5409,13 +5771,13 @@ var init_binary_probe = __esm(async () => {
5409
5771
  });
5410
5772
 
5411
5773
  // src/lib/fs-util.ts
5412
- import { existsSync as existsSync8, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
5774
+ import { existsSync as existsSync8, lstatSync as lstatSync3, readdirSync as readdirSync5 } from "node:fs";
5413
5775
  import { join as join14 } from "node:path";
5414
5776
  function dirSize(path) {
5415
5777
  if (!existsSync8(path)) {
5416
5778
  return 0;
5417
5779
  }
5418
- const stat = statSync7(path);
5780
+ const stat = lstatSync3(path);
5419
5781
  if (stat.isFile()) {
5420
5782
  return stat.size;
5421
5783
  }
@@ -5429,10 +5791,10 @@ function dirSize(path) {
5429
5791
  return total;
5430
5792
  }
5431
5793
  function formatBytes(bytes) {
5432
- if (bytes === 0)
5794
+ if (!Number.isFinite(bytes) || bytes < 1)
5433
5795
  return "0 B";
5434
5796
  const units = ["B", "KB", "MB", "GB", "TB"];
5435
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
5797
+ const i = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
5436
5798
  return `${(bytes / 1024 ** i).toFixed(1)} ${units[i]}`;
5437
5799
  }
5438
5800
  function compareVersionLabels(a, b) {
@@ -5503,12 +5865,12 @@ function getOmpPackageDir() {
5503
5865
  var init_omp_paths = () => {};
5504
5866
 
5505
5867
  // src/lib/omp-helpers.ts
5506
- import { accessSync as accessSync2, constants as constants3, existsSync as existsSync10, readFileSync as readFileSync7, statSync as statSync8 } from "node:fs";
5868
+ import { accessSync as accessSync2, constants as constants3, existsSync as existsSync10, readFileSync as readFileSync7, statSync as statSync7 } from "node:fs";
5507
5869
  import { homedir as homedir12 } from "node:os";
5508
5870
  import { delimiter as delimiter2, extname, join as join16, resolve as resolve10 } from "node:path";
5509
5871
  function isExecutableFile(path, platform = process.platform) {
5510
5872
  try {
5511
- if (!existsSync10(path) || !statSync8(path).isFile())
5873
+ if (!existsSync10(path) || !statSync7(path).isFile())
5512
5874
  return false;
5513
5875
  accessSync2(path, platform === "win32" ? constants3.F_OK : constants3.X_OK);
5514
5876
  return true;
@@ -5834,7 +6196,7 @@ var init_omp = __esm(() => {
5834
6196
  });
5835
6197
 
5836
6198
  // src/lib/fs-errors.ts
5837
- import { existsSync as existsSync12, statSync as statSync9 } from "node:fs";
6199
+ import { existsSync as existsSync12, statSync as statSync8 } from "node:fs";
5838
6200
  import { homedir as homedir13 } from "node:os";
5839
6201
  import { dirname as dirname6, resolve as resolve11, sep as sep2 } from "node:path";
5840
6202
  function errorCode(error) {
@@ -5919,7 +6281,7 @@ var init_fs_errors = __esm(() => {
5919
6281
  defaultFacts = {
5920
6282
  ownerUid(path) {
5921
6283
  try {
5922
- return statSync9(path).uid;
6284
+ return statSync8(path).uid;
5923
6285
  } catch {
5924
6286
  return null;
5925
6287
  }
@@ -13707,7 +14069,19 @@ var require_src2 = __commonJS(function(exports, module) {
13707
14069
  });
13708
14070
 
13709
14071
  // src/lib/jsonc.ts
13710
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
14072
+ import { randomUUID as randomUUID2 } from "node:crypto";
14073
+ import {
14074
+ chmodSync as chmodSync3,
14075
+ existsSync as existsSync13,
14076
+ lstatSync as lstatSync4,
14077
+ mkdirSync as mkdirSync4,
14078
+ readFileSync as readFileSync9,
14079
+ realpathSync as realpathSync4,
14080
+ renameSync as renameSync5,
14081
+ rmSync as rmSync3,
14082
+ statSync as statSync9,
14083
+ writeFileSync as writeFileSync4
14084
+ } from "node:fs";
13711
14085
  import { dirname as dirname7 } from "node:path";
13712
14086
  function detectJsoncFile(configDir, baseName) {
13713
14087
  const jsoncPath = `${configDir}/${baseName}.jsonc`;
@@ -13735,11 +14109,28 @@ function readJsoncFile(path) {
13735
14109
  };
13736
14110
  }
13737
14111
  }
13738
- function writeJsoncFile(path, value, format = "json") {
14112
+ function writeJsoncFile(path, value, _format = "json") {
13739
14113
  mkdirSync4(dirname7(path), { recursive: true });
13740
- const serialized = format === "jsonc" ? import_comment_json.stringify(value, null, 2) : JSON.stringify(value, null, 2);
13741
- writeFileSync4(path, `${serialized}
13742
- `);
14114
+ const link = lstatSync4(path, { throwIfNoEntry: false });
14115
+ if (link?.isSymbolicLink() && !existsSync13(path)) {
14116
+ throw new Error(`Cannot write ${path}: config symlink target is missing or unreadable`);
14117
+ }
14118
+ const target = existsSync13(path) ? realpathSync4(path) : path;
14119
+ const mode = existsSync13(target) ? statSync9(target).mode & 511 : undefined;
14120
+ const temporary = `${target}.tmp-${randomUUID2()}`;
14121
+ try {
14122
+ writeFileSync4(temporary, `${import_comment_json.stringify(value, null, 2)}
14123
+ `, { flag: "wx", mode });
14124
+ if (mode !== undefined)
14125
+ chmodSync3(temporary, mode);
14126
+ renameSync5(temporary, target);
14127
+ } catch (error) {
14128
+ if (error && typeof error === "object" && "path" in error)
14129
+ error.path = path;
14130
+ throw error;
14131
+ } finally {
14132
+ rmSync3(temporary, { force: true });
14133
+ }
13743
14134
  }
13744
14135
  function ensureAftSchemaUrl(path, format) {
13745
14136
  const existed = existsSync13(path);
@@ -14018,8 +14409,8 @@ import {
14018
14409
  readdirSync as readdirSync6,
14019
14410
  readFileSync as readFileSync10,
14020
14411
  readSync as readSync5,
14021
- realpathSync as realpathSync4,
14022
- rmSync as rmSync3,
14412
+ realpathSync as realpathSync5,
14413
+ rmSync as rmSync4,
14023
14414
  statSync as statSync10
14024
14415
  } from "node:fs";
14025
14416
  import { homedir as homedir14, tmpdir as tmpdir3 } from "node:os";
@@ -14038,7 +14429,7 @@ function packageMetadataAt(path, expectedName) {
14038
14429
  function packageMetadataNearExecutable(executable, expectedName) {
14039
14430
  let current;
14040
14431
  try {
14041
- current = dirname8(realpathSync4(executable));
14432
+ current = dirname8(realpathSync5(executable));
14042
14433
  } catch {
14043
14434
  current = dirname8(executable);
14044
14435
  }
@@ -14203,7 +14594,7 @@ function probeOpenCodeV1Version(executable, options = {}) {
14203
14594
  }
14204
14595
  const after = snapshotOperatorState(operatorHome);
14205
14596
  const createdIsolatedState = probeCreatedIsolatedState(data, state);
14206
- rmSync3(root, { recursive: true, force: true });
14597
+ rmSync4(root, { recursive: true, force: true });
14207
14598
  if (after !== before && env.AFT_LOAD_MATRIX_ALLOW_LIVE_OPERATOR === "0") {
14208
14599
  throw new Error("OpenCode host probe changed the operator database or log directory");
14209
14600
  }
@@ -14241,7 +14632,7 @@ function isV2Version(version) {
14241
14632
  }
14242
14633
  function resolvedPath(executable) {
14243
14634
  try {
14244
- return realpathSync4(executable);
14635
+ return realpathSync5(executable);
14245
14636
  } catch {
14246
14637
  return executable;
14247
14638
  }
@@ -14322,7 +14713,7 @@ var init_host_generation = __esm(() => {
14322
14713
  });
14323
14714
 
14324
14715
  // src/adapters/opencode.ts
14325
- import { existsSync as existsSync15, readFileSync as readFileSync11, rmSync as rmSync4, statSync as statSync11 } from "node:fs";
14716
+ import { existsSync as existsSync15, readFileSync as readFileSync11, rmSync as rmSync5, statSync as statSync11 } from "node:fs";
14326
14717
  import { homedir as homedir15 } from "node:os";
14327
14718
  import { dirname as dirname9, join as join19, parse as parse3, resolve as resolve12 } from "node:path";
14328
14719
  import { fileURLToPath } from "node:url";
@@ -14344,7 +14735,7 @@ function clearLegacyOpenCodePluginCache(primaryPath) {
14344
14735
  if (!legacyPath || !existsSync15(legacyPath))
14345
14736
  return { clearedPath: null };
14346
14737
  try {
14347
- rmSync4(legacyPath, { recursive: true, force: true });
14738
+ rmSync5(legacyPath, { recursive: true, force: true });
14348
14739
  return { clearedPath: legacyPath };
14349
14740
  } catch (error) {
14350
14741
  return { clearedPath: null, error: error instanceof Error ? error.message : String(error) };
@@ -14664,18 +15055,20 @@ class OpenCodeAdapter {
14664
15055
  const configuredEntry = openCodePluginReadKeys(this.configGeneration()).flatMap((key) => Array.isArray(value?.[key]) ? value[key] : []).map(pluginEntryPackage).find(isAftNpmEntry);
14665
15056
  const path = join19(getOpenCodeCacheDir(), "packages", configuredEntry ?? PLUGIN_ENTRY);
14666
15057
  let cached;
15058
+ let readError;
14667
15059
  try {
14668
15060
  const installedPkgPath = join19(path, "node_modules", "@cortexkit", "aft-opencode", "package.json");
14669
- if (existsSync15(installedPkgPath)) {
14670
- const pkg = JSON.parse(readFileSync11(installedPkgPath, "utf-8"));
14671
- cached = typeof pkg.version === "string" ? pkg.version : undefined;
15061
+ const pkg = JSON.parse(readFileSync11(installedPkgPath, "utf-8"));
15062
+ cached = typeof pkg.version === "string" ? pkg.version : undefined;
15063
+ } catch (error) {
15064
+ if (error.code !== "ENOENT") {
15065
+ readError = error instanceof Error ? error.message : String(error);
14672
15066
  }
14673
- } catch {
14674
- cached = undefined;
14675
15067
  }
14676
15068
  return {
14677
15069
  path,
14678
15070
  cached,
15071
+ readError,
14679
15072
  latest: getSelfVersion(),
14680
15073
  exists: existsSync15(path),
14681
15074
  configuredEntry
@@ -14717,7 +15110,7 @@ class OpenCodeAdapter {
14717
15110
  };
14718
15111
  }
14719
15112
  try {
14720
- rmSync4(info.path, { recursive: true, force: true });
15113
+ rmSync5(info.path, { recursive: true, force: true });
14721
15114
  return {
14722
15115
  action: "cleared",
14723
15116
  path: info.path,
@@ -14768,7 +15161,8 @@ var init_opencode = __esm(() => {
14768
15161
  // src/adapters/pi.ts
14769
15162
  import { existsSync as existsSync16, readFileSync as readFileSync12 } from "node:fs";
14770
15163
  import { homedir as homedir16 } from "node:os";
14771
- import { join as join20, resolve as resolve13 } from "node:path";
15164
+ import { isAbsolute as isAbsolute7, join as join20, resolve as resolve13 } from "node:path";
15165
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
14772
15166
  function getPiAgentDir() {
14773
15167
  const configuredDir = process.env.PI_CODING_AGENT_DIR?.trim();
14774
15168
  if (configuredDir)
@@ -14830,12 +15224,11 @@ function piEntryMatchesAft(entry) {
14830
15224
  let resolved = null;
14831
15225
  if (entry.startsWith("file:")) {
14832
15226
  try {
14833
- const stripped = entry.slice("file:".length);
14834
- resolved = stripped.startsWith("//") ? stripped.slice(2) : stripped;
15227
+ resolved = fileURLToPath2(entry);
14835
15228
  } catch {
14836
15229
  return false;
14837
15230
  }
14838
- } else if (entry.startsWith("/")) {
15231
+ } else if (isAbsolute7(entry)) {
14839
15232
  resolved = entry;
14840
15233
  } else if (entry.length > 0) {
14841
15234
  resolved = join20(getPiAgentDir(), entry);
@@ -14885,7 +15278,7 @@ class PiAdapter {
14885
15278
  const stderr = (result.stderr ?? "").trim();
14886
15279
  const text = stdout || stderr;
14887
15280
  const semverLine = text.split(/\r?\n/).find((l) => /^\d+\.\d+\.\d+/.test(l.trim()));
14888
- return semverLine?.trim() ?? text ?? null;
15281
+ return semverLine?.trim() ?? (text || null);
14889
15282
  } catch {
14890
15283
  return null;
14891
15284
  }
@@ -14940,29 +15333,37 @@ class PiAdapter {
14940
15333
  };
14941
15334
  }
14942
15335
  }
15336
+ getConfiguredPluginEntry() {
15337
+ return readPiExtensionIndex().installed.find(piEntryMatchesAft);
15338
+ }
14943
15339
  getPluginCacheInfo() {
14944
15340
  const candidates = [
14945
15341
  join20(getPiAgentDir(), "npm", "node_modules", "@cortexkit", "aft-pi", "package.json"),
14946
15342
  join20(getPiAgentDir(), "node_modules", "@cortexkit", "aft-pi", "package.json"),
14947
15343
  join20(getPiAgentDir(), "extensions", "node_modules", "@cortexkit", "aft-pi", "package.json")
14948
15344
  ];
15345
+ let readError;
14949
15346
  for (const candidate of candidates) {
14950
- if (!existsSync16(candidate))
14951
- continue;
14952
15347
  try {
14953
15348
  const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
14954
15349
  const cached = typeof pkg.version === "string" ? pkg.version : undefined;
14955
15350
  return {
14956
15351
  path: candidate,
14957
15352
  cached,
15353
+ readError,
14958
15354
  latest: undefined,
14959
15355
  exists: true
14960
15356
  };
14961
- } catch {}
15357
+ } catch (error) {
15358
+ if (error.code !== "ENOENT") {
15359
+ readError ??= error instanceof Error ? error.message : String(error);
15360
+ }
15361
+ }
14962
15362
  }
14963
15363
  return {
14964
15364
  path: join20(getPiAgentDir(), "npm", "node_modules", "@cortexkit", "aft-pi", "package.json"),
14965
- exists: false
15365
+ exists: readError !== undefined,
15366
+ readError
14966
15367
  };
14967
15368
  }
14968
15369
  getStorageDir() {
@@ -16254,7 +16655,7 @@ var init_dist6 = __esm(() => {
16254
16655
  function handleCancel(value, message = "Cancelled.") {
16255
16656
  if (isCancel(value)) {
16256
16657
  log2.warn(message);
16257
- process.exit(0);
16658
+ process.exit(130);
16258
16659
  }
16259
16660
  }
16260
16661
  async function confirm2(message, defaultYes = true) {
@@ -16637,7 +17038,7 @@ function runConfigFix(run = runNative) {
16637
17038
  try {
16638
17039
  const report = JSON.parse(result.stdout);
16639
17040
  if (Array.isArray(report.files))
16640
- return { ok: true, files: report.files };
17041
+ return { ok: true, files: report.files, cacheCleanup: report.cache_cleanup };
16641
17042
  } catch {}
16642
17043
  return {
16643
17044
  ok: false,
@@ -17041,7 +17442,7 @@ var init_feature_wizard = __esm(async () => {
17041
17442
  clackIO = {
17042
17443
  selectRows: (message, options, initial, rules) => promptFeatureList(message, options, initial, () => {
17043
17444
  log2.warn("Cancelled.");
17044
- process.exit(0);
17445
+ process.exit(130);
17045
17446
  }, {}, rules),
17046
17447
  info: (message) => log2.info(message),
17047
17448
  warn: (message) => log2.warn(message),
@@ -17235,68 +17636,76 @@ var init_setup = __esm(async () => {
17235
17636
  ]);
17236
17637
  });
17237
17638
 
17238
- // src/commands/index.ts
17239
- function runIndex(argv) {
17240
- const binary = findAftBinary();
17639
+ // src/lib/native-forward.ts
17640
+ import { constants as constants4 } from "node:os";
17641
+ function forwardToNative(command, argv) {
17642
+ const version = getSelfVersion();
17643
+ const preferred = /^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version) ? version : undefined;
17644
+ const binary = findAftBinary(preferred);
17241
17645
  if (!binary) {
17242
- console.error(missingAftBinaryMessage(`${CLI} index`));
17646
+ console.error(missingAftBinaryMessage(`${CLI} ${command}`, preferred));
17243
17647
  return 1;
17244
17648
  }
17245
- const result = spawnSync(binary, ["index", ...argv], {
17246
- stdio: "inherit",
17247
- env: process.env
17248
- });
17649
+ const result = spawnSync(binary, [command, ...argv], { stdio: "inherit", env: process.env });
17249
17650
  if (result.error) {
17250
- console.error(`aft index failed to start ${binary}: ${result.error.message}`);
17651
+ console.error(`aft ${command} failed to start ${binary}: ${result.error.message}`);
17251
17652
  return 1;
17252
17653
  }
17253
- return result.status ?? 1;
17654
+ if (result.status !== null)
17655
+ return result.status;
17656
+ const signal = result.signal ? constants4.signals[result.signal] : undefined;
17657
+ return signal === undefined ? 1 : 128 + signal;
17254
17658
  }
17255
- var init_commands = __esm(async () => {
17659
+ var init_native_forward = __esm(async () => {
17256
17660
  init_dist2();
17661
+ init_self_version();
17257
17662
  await init_binary_probe();
17258
17663
  });
17259
17664
 
17665
+ // src/commands/index.ts
17666
+ function runIndex(argv) {
17667
+ return forwardToNative("index", argv);
17668
+ }
17669
+ var init_commands = __esm(async () => {
17670
+ await init_native_forward();
17671
+ });
17672
+
17260
17673
  // src/commands/backups.ts
17261
17674
  function runBackups(argv) {
17262
- const binary = findAftBinary();
17263
- if (!binary) {
17264
- console.error(missingAftBinaryMessage(`${CLI} backups`));
17265
- return 1;
17266
- }
17267
- const result = spawnSync(binary, ["backups", ...argv], {
17268
- stdio: "inherit",
17269
- env: process.env
17270
- });
17271
- if (result.error) {
17272
- console.error(`aft backups failed to start ${binary}: ${result.error.message}`);
17273
- return 1;
17274
- }
17275
- return result.status ?? 1;
17675
+ return forwardToNative("backups", argv);
17276
17676
  }
17277
17677
  var init_backups = __esm(async () => {
17278
- init_dist2();
17279
- await init_binary_probe();
17678
+ await init_native_forward();
17280
17679
  });
17281
17680
 
17282
17681
  // src/commands/cache.ts
17283
17682
  function runCache(argv) {
17683
+ return forwardToNative("cache", argv);
17684
+ }
17685
+ var init_cache = __esm(async () => {
17686
+ await init_native_forward();
17687
+ });
17688
+
17689
+ // src/commands/clean-old-caches.ts
17690
+ function runOldCacheCleanup(argv) {
17284
17691
  const binary = findAftBinary();
17285
17692
  if (!binary) {
17286
- console.error(missingAftBinaryMessage(`${CLI} cache`));
17693
+ console.error(missingAftBinaryMessage(`${CLI} doctor --clean-old-caches`));
17287
17694
  return 1;
17288
17695
  }
17289
- const result = spawnSync(binary, ["cache", ...argv], {
17696
+ if (argv.some((arg) => !["--clean-old-caches", "--yes", "--help", "-h"].includes(arg))) {
17697
+ console.error("usage: aft doctor --clean-old-caches [--yes]");
17698
+ return 2;
17699
+ }
17700
+ const result = spawnSync(binary, ["clean-old-caches", ...argv.filter((arg) => arg !== "--clean-old-caches")], {
17290
17701
  stdio: "inherit",
17291
17702
  env: process.env
17292
17703
  });
17293
- if (result.error) {
17294
- console.error(`aft cache failed to start ${binary}: ${result.error.message}`);
17295
- return 1;
17296
- }
17704
+ if (result.error)
17705
+ console.error(`Old cache cleanup failed to start: ${result.error.message}`);
17297
17706
  return result.status ?? 1;
17298
17707
  }
17299
- var init_cache = __esm(async () => {
17708
+ var init_clean_old_caches = __esm(async () => {
17300
17709
  init_dist2();
17301
17710
  await init_binary_probe();
17302
17711
  });
@@ -17411,7 +17820,7 @@ var init_features = __esm(() => {
17411
17820
  // src/doctor/opencode.ts
17412
17821
  import { existsSync as existsSync18, readFileSync as readFileSync13 } from "node:fs";
17413
17822
  import { dirname as dirname11, join as join23 } from "node:path";
17414
- import { fileURLToPath as fileURLToPath2 } from "node:url";
17823
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
17415
17824
  function isLoadPath(value) {
17416
17825
  return OPENCODE_LOAD_PATHS.includes(value);
17417
17826
  }
@@ -17458,7 +17867,7 @@ function localPluginRoot(entry) {
17458
17867
  return null;
17459
17868
  if (entry.startsWith("file://")) {
17460
17869
  try {
17461
- return fileURLToPath2(entry);
17870
+ return fileURLToPath3(entry);
17462
17871
  } catch {
17463
17872
  return null;
17464
17873
  }
@@ -17634,7 +18043,7 @@ async function sendAftRequest(binaryPath, request) {
17634
18043
  throw new Error("aft exited before responding");
17635
18044
  return response;
17636
18045
  }
17637
- async function sendAftRequests(binaryPath, requests) {
18046
+ async function sendAftRequests(binaryPath, requests, timeoutMs = 120000) {
17638
18047
  return new Promise((resolve, reject) => {
17639
18048
  const child = spawn(binaryPath, [], {
17640
18049
  stdio: ["pipe", "pipe", "pipe"]
@@ -17644,10 +18053,16 @@ async function sendAftRequests(binaryPath, requests) {
17644
18053
  let noiseLineCount = 0;
17645
18054
  let stderr = "";
17646
18055
  let settled = false;
18056
+ let timedOut = false;
18057
+ const deadline = setTimeout(() => {
18058
+ timedOut = true;
18059
+ child.kill("SIGKILL");
18060
+ }, timeoutMs);
17647
18061
  const finish = (fn) => {
17648
18062
  if (settled)
17649
18063
  return;
17650
18064
  settled = true;
18065
+ clearTimeout(deadline);
17651
18066
  child.kill();
17652
18067
  fn();
17653
18068
  };
@@ -17686,7 +18101,7 @@ async function sendAftRequests(binaryPath, requests) {
17686
18101
  });
17687
18102
  child.stdout.setEncoding("utf-8");
17688
18103
  child.stdout.on("data", (chunk) => {
17689
- if (settled)
18104
+ if (settled || timedOut)
17690
18105
  return;
17691
18106
  stdoutLines.push(chunk);
17692
18107
  });
@@ -17700,6 +18115,13 @@ async function sendAftRequests(binaryPath, requests) {
17700
18115
  child.on("close", (code) => {
17701
18116
  if (settled)
17702
18117
  return;
18118
+ if (timedOut) {
18119
+ const received = new Set(responses.map((response) => response.id));
18120
+ const pending = requests.filter((request) => !received.has(request.id)).map((request) => request.command);
18121
+ finish(() => reject(new Error(`aft request deadline exceeded; pending: ${pending.join(", ")}.
18122
+ ${stderr.trim()}`, { cause: { exitCode: code, signal: child.signalCode } })));
18123
+ return;
18124
+ }
17703
18125
  finish(() => reject(buildBridgeError({ binaryPath, code, stderr, noiseLines, noiseLineCount, responses })));
17704
18126
  });
17705
18127
  child.stdin.on("error", () => {});
@@ -17779,14 +18201,14 @@ var init_binary_cache = __esm(() => {
17779
18201
  });
17780
18202
 
17781
18203
  // src/lib/sanitize.ts
17782
- import { realpathSync as realpathSync5 } from "node:fs";
18204
+ import { realpathSync as realpathSync6 } from "node:fs";
17783
18205
  import { homedir as homedir17, userInfo } from "node:os";
17784
18206
  function escapeRegex(value) {
17785
18207
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17786
18208
  }
17787
18209
  function safeRealpath(p) {
17788
18210
  try {
17789
- return realpathSync5(p);
18211
+ return realpathSync6(p);
17790
18212
  } catch {
17791
18213
  return null;
17792
18214
  }
@@ -18315,7 +18737,7 @@ var init_legacy_storage = __esm(() => {
18315
18737
  });
18316
18738
 
18317
18739
  // src/lib/lsp-cache.ts
18318
- import { existsSync as existsSync24, readdirSync as readdirSync10, rmSync as rmSync5, statSync as statSync15 } from "node:fs";
18740
+ import { existsSync as existsSync24, readdirSync as readdirSync10, rmSync as rmSync6, statSync as statSync15 } from "node:fs";
18319
18741
  import { join as join27 } from "node:path";
18320
18742
  function inspectDir(path) {
18321
18743
  if (!existsSync24(path)) {
@@ -18335,8 +18757,12 @@ function inspectDir(path) {
18335
18757
  if (!statSync15(full).isDirectory())
18336
18758
  continue;
18337
18759
  const size = dirSize(full);
18760
+ let decodedName = name;
18761
+ try {
18762
+ decodedName = decodeURIComponent(name);
18763
+ } catch {}
18338
18764
  entries.push({
18339
- name: decodeURIComponent(name),
18765
+ name: decodedName,
18340
18766
  path: full,
18341
18767
  size
18342
18768
  });
@@ -18370,7 +18796,7 @@ function clearLspCaches() {
18370
18796
  const report = getLspCacheReport();
18371
18797
  for (const entry of [...report.npm.entries, ...report.github.entries]) {
18372
18798
  try {
18373
- rmSync5(entry.path, { recursive: true, force: true });
18799
+ rmSync6(entry.path, { recursive: true, force: true });
18374
18800
  result.cleared.push({ name: entry.name, path: entry.path, size: entry.size });
18375
18801
  result.totalBytes += entry.size;
18376
18802
  } catch (err) {
@@ -18388,8 +18814,8 @@ var init_lsp_cache = __esm(() => {
18388
18814
  });
18389
18815
 
18390
18816
  // src/lib/onnx.ts
18391
- import { existsSync as existsSync25, readdirSync as readdirSync11, readlinkSync as readlinkSync2, realpathSync as realpathSync6 } from "node:fs";
18392
- import { basename as basename2, isAbsolute as isAbsolute7, join as join28, resolve as resolve14, win32 as win323 } from "node:path";
18817
+ import { existsSync as existsSync25, readdirSync as readdirSync11, readlinkSync as readlinkSync2, realpathSync as realpathSync7 } from "node:fs";
18818
+ import { basename as basename2, isAbsolute as isAbsolute8, join as join28, resolve as resolve14, win32 as win323 } from "node:path";
18393
18819
  function getOnnxLibraryName() {
18394
18820
  if (process.platform === "darwin")
18395
18821
  return "libonnxruntime.dylib";
@@ -18425,7 +18851,7 @@ function pathEntriesForPlatform2() {
18425
18851
  return pathEnvValue2().split(delimiter).map((entry) => entry.trim().replace(/^"|"$/g, "")).filter((entry) => {
18426
18852
  if (!entry || entry === "." || entry.includes("\x00"))
18427
18853
  return false;
18428
- return isAbsolute7(entry) || win323.isAbsolute(entry);
18854
+ return isAbsolute8(entry) || win323.isAbsolute(entry);
18429
18855
  });
18430
18856
  }
18431
18857
  function isWindowsSystem32Directory2(dir) {
@@ -18564,7 +18990,7 @@ function detectOrtVersion(libDir) {
18564
18990
  return null;
18565
18991
  const libName = getOnnxLibraryName();
18566
18992
  try {
18567
- const resolved = parseOrtVersionFromPath(realpathSync6(join28(libDir, libName)));
18993
+ const resolved = parseOrtVersionFromPath(realpathSync7(join28(libDir, libName)));
18568
18994
  if (resolved && resolved !== INVALID_ORT_VERSION2)
18569
18995
  return resolved;
18570
18996
  } catch {}
@@ -18583,7 +19009,7 @@ function detectOrtVersion(libDir) {
18583
19009
  const base = join28(libDir, libName);
18584
19010
  if (existsSync25(base)) {
18585
19011
  try {
18586
- const real = realpathSync6(base);
19012
+ const real = realpathSync7(base);
18587
19013
  const version = parseOrtVersionFromPath(real) ?? parseOrtVersionFromDirectoryPath(real);
18588
19014
  if (version)
18589
19015
  return version;
@@ -18615,6 +19041,8 @@ var init_onnx = __esm(() => {
18615
19041
 
18616
19042
  // src/lib/plugin-load.ts
18617
19043
  import { existsSync as existsSync26, readFileSync as readFileSync15 } from "node:fs";
19044
+ import { homedir as homedir18 } from "node:os";
19045
+ import { isAbsolute as isAbsolute9, join as join29, normalize } from "node:path";
18618
19046
  function isRecord3(value) {
18619
19047
  return typeof value === "object" && value !== null && !Array.isArray(value);
18620
19048
  }
@@ -18637,7 +19065,7 @@ function failsEveryToolCall(toolCallError, scope = "") {
18637
19065
  }
18638
19066
  function withHarness(block, harness) {
18639
19067
  const override = isRecord3(block.harnesses) ? block.harnesses[harness] : undefined;
18640
- return isRecord3(override) ? { ...block, ...override } : block;
19068
+ return isRecord3(override) ? { ...block, ...override, storage: block.storage } : block;
18641
19069
  }
18642
19070
  function indexesOf(block) {
18643
19071
  return block && isRecord3(block.indexes) ? block.indexes : undefined;
@@ -18694,7 +19122,12 @@ function evaluatePluginLoad(input) {
18694
19122
  const semanticIndex = indexes?.semantic ?? true;
18695
19123
  const userSemantic = loaded.user && isRecord3(loaded.user.semantic) ? loaded.user.semantic : null;
18696
19124
  const semanticBackend = typeof userSemantic?.backend === "string" && userSemantic.backend.length > 0 ? userSemantic.backend : "fastembed";
19125
+ const storage = loaded.user && isRecord3(loaded.user.storage) ? loaded.user.storage : null;
19126
+ const requested = typeof storage?.cache_dir === "string" ? storage.cache_dir : "";
19127
+ const home = input.home ?? homedir18();
19128
+ const expanded = requested === "~" ? home : requested.startsWith("~/") || requested.startsWith("~\\") ? join29(home, requested.slice(2)) : requested;
18697
19129
  return {
19130
+ ...isAbsolute9(expanded) ? { cacheDir: normalize(expanded) } : {},
18698
19131
  blockers,
18699
19132
  semanticIndex,
18700
19133
  semanticBackend,
@@ -18711,12 +19144,13 @@ var init_plugin_load = __esm(() => {
18711
19144
  import {
18712
19145
  accessSync as accessSync3,
18713
19146
  closeSync as closeSync9,
18714
- constants as constants4,
19147
+ constants as constants5,
18715
19148
  existsSync as existsSync27,
18716
19149
  openSync as openSync9,
18717
19150
  readSync as readSync7,
18718
19151
  statSync as statSync16
18719
19152
  } from "node:fs";
19153
+ import { join as join30 } from "node:path";
18720
19154
  async function collectDiagnostics(adapters) {
18721
19155
  const cliVersion = getSelfVersion();
18722
19156
  const binaryVersion = probeBinaryVersion(cliVersion);
@@ -18754,7 +19188,7 @@ async function diagnoseHarness(adapter) {
18754
19188
  if (!existsSync27(storage))
18755
19189
  return false;
18756
19190
  try {
18757
- accessSync3(storage, constants4.R_OK | constants4.W_OK);
19191
+ accessSync3(storage, constants5.R_OK | constants5.W_OK);
18758
19192
  return true;
18759
19193
  } catch {
18760
19194
  return false;
@@ -18798,6 +19232,7 @@ async function diagnoseHarness(adapter) {
18798
19232
  exists: existsSync27(storage),
18799
19233
  accessible: storageAccessible,
18800
19234
  sizesByKey: describeStorage,
19235
+ ...pluginLoad.cacheDir ? { cacheRoot: join30(pluginLoad.cacheDir, "aft") } : {},
18801
19236
  ...legacyDuplication.totalPartitions > 0 ? { legacyDuplication } : {}
18802
19237
  },
18803
19238
  onnxRuntime: {
@@ -19249,14 +19684,14 @@ var init_issue_body = __esm(() => {
19249
19684
 
19250
19685
  // src/lib/onnx-fix.ts
19251
19686
  import { randomBytes as randomBytes2 } from "node:crypto";
19252
- import { existsSync as existsSync28, mkdirSync as mkdirSync7, renameSync as renameSync5, rmSync as rmSync6 } from "node:fs";
19253
- import { isAbsolute as isAbsolute8, join as join29, relative as relative4 } from "node:path";
19687
+ import { existsSync as existsSync28, mkdirSync as mkdirSync7, renameSync as renameSync6, rmSync as rmSync7 } from "node:fs";
19688
+ import { isAbsolute as isAbsolute10, join as join31, relative as relative4 } from "node:path";
19254
19689
  function findOnnxFixCandidates(report) {
19255
19690
  const candidates = [];
19256
19691
  for (const harness of report.harnesses) {
19257
19692
  if (!harness.onnxRuntime.required)
19258
19693
  continue;
19259
- const storageOnnxDir = join29(harness.storageDir.path, "onnxruntime");
19694
+ const storageOnnxDir = join31(harness.storageDir.path, "onnxruntime");
19260
19695
  const systemTooOld = harness.onnxRuntime.systemPath !== null && harness.onnxRuntime.systemCompatible === false;
19261
19696
  const cachedTooOld = harness.onnxRuntime.cachedPath !== null && harness.onnxRuntime.cachedCompatible === false;
19262
19697
  const hasCompatibleCached = harness.onnxRuntime.cachedCompatible === true;
@@ -19295,7 +19730,7 @@ function findOnnxFixCandidates(report) {
19295
19730
  async function installManagedReplacement(candidate, ensureFn, rmFn) {
19296
19731
  const suffix = `${process.pid}.${Date.now().toString(36)}.${randomBytes2(6).toString("hex")}`;
19297
19732
  const stagingStorageDir = `${candidate.harness.storageDir.path}.onnx-fix.tmp.${suffix}`;
19298
- const stagedOnnxDir = join29(stagingStorageDir, "onnxruntime");
19733
+ const stagedOnnxDir = join31(stagingStorageDir, "onnxruntime");
19299
19734
  const backupDir = `${candidate.storageOnnxDir}.backup.${suffix}`;
19300
19735
  try {
19301
19736
  const stagedInstalledPath = await ensureFn(stagingStorageDir);
@@ -19303,19 +19738,19 @@ async function installManagedReplacement(candidate, ensureFn, rmFn) {
19303
19738
  throw new Error("managed ONNX Runtime download was unavailable");
19304
19739
  }
19305
19740
  const installedRelativePath = relative4(stagedOnnxDir, stagedInstalledPath);
19306
- if (isAbsolute8(installedRelativePath) || installedRelativePath === ".." || installedRelativePath.startsWith("../") || installedRelativePath.startsWith("..\\")) {
19741
+ if (isAbsolute10(installedRelativePath) || installedRelativePath === ".." || installedRelativePath.startsWith("../") || installedRelativePath.startsWith("..\\")) {
19307
19742
  throw new Error(`managed ONNX Runtime was installed outside staging: ${stagedInstalledPath}`);
19308
19743
  }
19309
19744
  mkdirSync7(candidate.harness.storageDir.path, { recursive: true });
19310
19745
  const hadPrevious = existsSync28(candidate.storageOnnxDir);
19311
19746
  if (hadPrevious)
19312
- renameSync5(candidate.storageOnnxDir, backupDir);
19747
+ renameSync6(candidate.storageOnnxDir, backupDir);
19313
19748
  try {
19314
- renameSync5(stagedOnnxDir, candidate.storageOnnxDir);
19749
+ renameSync6(stagedOnnxDir, candidate.storageOnnxDir);
19315
19750
  } catch (installError) {
19316
19751
  if (hadPrevious) {
19317
19752
  try {
19318
- renameSync5(backupDir, candidate.storageOnnxDir);
19753
+ renameSync6(backupDir, candidate.storageOnnxDir);
19319
19754
  } catch (restoreError) {
19320
19755
  throw new Error(`managed ONNX Runtime replacement failed and the previous runtime could not be restored from ${backupDir}: ${restoreError}`, { cause: installError });
19321
19756
  }
@@ -19331,13 +19766,13 @@ async function installManagedReplacement(candidate, ensureFn, rmFn) {
19331
19766
  }
19332
19767
  }
19333
19768
  return {
19334
- installedPath: join29(candidate.storageOnnxDir, installedRelativePath),
19769
+ installedPath: join31(candidate.storageOnnxDir, installedRelativePath),
19335
19770
  clearedPrevious: hadPrevious && cleanupError === undefined,
19336
19771
  cleanupError
19337
19772
  };
19338
19773
  } finally {
19339
19774
  try {
19340
- rmSync6(stagingStorageDir, { recursive: true, force: true });
19775
+ rmSync7(stagingStorageDir, { recursive: true, force: true });
19341
19776
  } catch {}
19342
19777
  }
19343
19778
  }
@@ -19362,7 +19797,7 @@ async function runOnnxFix(adapters, report, options = {}) {
19362
19797
  return null;
19363
19798
  }
19364
19799
  const result = { cleared: 0, installed: 0, bytesReclaimed: 0, errors: [] };
19365
- const rmFn = options.rmFn ?? rmSync6;
19800
+ const rmFn = options.rmFn ?? rmSync7;
19366
19801
  const ensureFn = options.ensureFn ?? ensureOnnxRuntime;
19367
19802
  for (const candidate of candidates) {
19368
19803
  try {
@@ -19400,21 +19835,24 @@ var init_onnx_fix = __esm(() => {
19400
19835
  // src/lib/session-log.ts
19401
19836
  import { closeSync as closeSync10, openSync as openSync10, readSync as readSync8, statSync as statSync17 } from "node:fs";
19402
19837
  function readSessionLog(path, session, maxBytes = MAX_SCAN_BYTES) {
19838
+ return readLogHistory([path, `${path}.1`], session, maxBytes);
19839
+ }
19840
+ function readLogHistory(files, session, maxBytes = MAX_SCAN_BYTES) {
19403
19841
  const lines = [];
19404
19842
  const errors = [];
19405
19843
  let remaining = maxBytes;
19406
19844
  let boundHit = false;
19407
- const bareId = session.id.replace(/^ses_/, "");
19845
+ const bareId = session?.id.replace(/^ses_/, "");
19408
19846
  const selectedTags = [`[ses_${bareId}]`, `[${bareId}]`];
19409
19847
  const tagPattern = /\[ses_[^\]\s]+\]|\[[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}\]/;
19410
19848
  const accept = (line) => {
19411
19849
  if (!line)
19412
19850
  return;
19413
19851
  const tag = tagPattern.exec(line)?.[0];
19414
- if (tag && !selectedTags.includes(tag))
19852
+ if (session && tag && !selectedTags.includes(tag))
19415
19853
  return;
19416
- if (!tag) {
19417
- const stamp = /^\[([^\]]+)\]/.exec(line)?.[1];
19854
+ if (session && !tag) {
19855
+ const stamp = /^\[([^\]]+)\]/.exec(line)?.[1] ?? /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z) \[aft(?:-lsp)?\] /.exec(line)?.[1];
19418
19856
  const time = stamp ? Date.parse(stamp) : NaN;
19419
19857
  const inWindow = Number.isFinite(time) && (session.startedAt === undefined || time >= session.startedAt) && time <= session.lastActivity;
19420
19858
  if (session.projectRoot) {
@@ -19431,7 +19869,7 @@ function readSessionLog(path, session, maxBytes = MAX_SCAN_BYTES) {
19431
19869
  if (errors.length < 20 && isErrorLogLine(line))
19432
19870
  errors.push(line);
19433
19871
  };
19434
- for (const file of [path, `${path}.1`]) {
19872
+ for (const file of files) {
19435
19873
  if (remaining <= 0 || lines.length >= 200 && errors.length >= 20)
19436
19874
  break;
19437
19875
  let fd;
@@ -19479,8 +19917,8 @@ var init_session_log = __esm(() => {
19479
19917
  // src/lib/sessions.ts
19480
19918
  import { existsSync as existsSync29, readdirSync as readdirSync12, readFileSync as readFileSync16, statSync as statSync18 } from "node:fs";
19481
19919
  import { createRequire as createRequire4 } from "node:module";
19482
- import { homedir as homedir18 } from "node:os";
19483
- import { basename as basename3, join as join30 } from "node:path";
19920
+ import { homedir as homedir19 } from "node:os";
19921
+ import { basename as basename3, join as join32 } from "node:path";
19484
19922
  function listRecentSessions(adapter) {
19485
19923
  try {
19486
19924
  if (adapter.kind === "opencode")
@@ -19514,7 +19952,7 @@ function mapOpenCodeSessionRows(rows) {
19514
19952
  }).filter((session) => session !== null).sort((a, b) => b.lastActivity - a.lastActivity).slice(0, MAX_RECENT_SESSIONS);
19515
19953
  }
19516
19954
  function listRecentOpenCodeSessions() {
19517
- const dbPath = join30(getXdgDataHome(), "opencode", "opencode.db");
19955
+ const dbPath = join32(getXdgDataHome(), "opencode", "opencode.db");
19518
19956
  if (!existsSync29(dbPath))
19519
19957
  return [];
19520
19958
  let db = null;
@@ -19534,14 +19972,14 @@ function listRecentOpenCodeSessions() {
19534
19972
  }
19535
19973
  function getXdgDataHome() {
19536
19974
  const xdgDataHome = process.env.XDG_DATA_HOME;
19537
- return xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : join30(homedir18(), ".local", "share");
19975
+ return xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : join32(homedir19(), ".local", "share");
19538
19976
  }
19539
19977
  function listRecentPiSessions() {
19540
- return listPiSessionsFromDir(join30(getHomeDir(), ".pi", "agent", "sessions"));
19978
+ return listPiSessionsFromDir(join32(getHomeDir(), ".pi", "agent", "sessions"));
19541
19979
  }
19542
19980
  function getHomeDir() {
19543
19981
  const envHome = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
19544
- return envHome && envHome.length > 0 ? envHome : homedir18();
19982
+ return envHome && envHome.length > 0 ? envHome : homedir19();
19545
19983
  }
19546
19984
  function listPiSessionsFromDir(sessionsDir) {
19547
19985
  try {
@@ -19583,7 +20021,7 @@ function collectJsonlFiles(root) {
19583
20021
  continue;
19584
20022
  }
19585
20023
  for (const entry of entries) {
19586
- const path = join30(dir, entry.name);
20024
+ const path = join32(dir, entry.name);
19587
20025
  if (entry.isDirectory()) {
19588
20026
  stack.push(path);
19589
20027
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -19683,18 +20121,20 @@ var init_sessions = __esm(() => {
19683
20121
 
19684
20122
  // src/commands/doctor.ts
19685
20123
  import {
19686
- chmodSync as chmodSync3,
20124
+ chmodSync as chmodSync4,
19687
20125
  existsSync as existsSync30,
20126
+ lstatSync as lstatSync5,
19688
20127
  mkdirSync as mkdirSync8,
19689
20128
  mkdtempSync as mkdtempSync2,
20129
+ readdirSync as readdirSync13,
19690
20130
  readFileSync as readFileSync17,
19691
- realpathSync as realpathSync7,
19692
- rmSync as rmSync7,
20131
+ realpathSync as realpathSync8,
20132
+ rmSync as rmSync8,
19693
20133
  statSync as statSync19,
19694
20134
  writeFileSync as writeFileSync5
19695
20135
  } from "node:fs";
19696
20136
  import { tmpdir as tmpdir5 } from "node:os";
19697
- import { join as join31 } from "node:path";
20137
+ import { dirname as dirname12, join as join33 } from "node:path";
19698
20138
  function openCodeAdapter(adapters) {
19699
20139
  return adapters.find((adapter) => adapter.kind === "opencode");
19700
20140
  }
@@ -20095,8 +20535,8 @@ async function clearDoctorCaches(adapters, targets, options = {}) {
20095
20535
  }
20096
20536
  return summary;
20097
20537
  }
20098
- function clearOldBinaries() {
20099
- const cliVersion = getSelfVersion();
20538
+ function clearOldBinaries(cliVersion = getSelfVersion(), plugins = getAllAdapters()) {
20539
+ const knownVersion = /^(?:v)?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
20100
20540
  const keepTag = `v${cliVersion.replace(/^v/, "")}`;
20101
20541
  const info = getBinaryCacheInfo(cliVersion);
20102
20542
  const result = {
@@ -20109,13 +20549,34 @@ function clearOldBinaries() {
20109
20549
  log2.info(`Binary cache: nothing to clear at ${info.path}`);
20110
20550
  return result;
20111
20551
  }
20112
- const stale = info.versions.filter((v) => v !== keepTag);
20552
+ if (!knownVersion.test(cliVersion)) {
20553
+ result.keptVersion = null;
20554
+ return result;
20555
+ }
20556
+ const retained = new Set([keepTag]);
20557
+ for (const plugin of plugins) {
20558
+ try {
20559
+ const info = plugin.getPluginCacheInfo();
20560
+ if (info.readError !== undefined)
20561
+ return result;
20562
+ const entry = (info.configuredEntry ?? plugin.getConfiguredPluginEntry?.())?.replace(/^npm:/, "");
20563
+ const prefix = `${plugin.pluginPackageName}@`;
20564
+ const pinned = entry?.startsWith(prefix) ? entry.slice(prefix.length) : undefined;
20565
+ for (const version of [info.cached, pinned]) {
20566
+ if (version && knownVersion.test(version))
20567
+ retained.add(`v${version.replace(/^v/, "")}`);
20568
+ }
20569
+ } catch {
20570
+ return result;
20571
+ }
20572
+ }
20573
+ const stale = info.versions.filter((v) => !retained.has(v));
20113
20574
  if (stale.length === 0) {
20114
20575
  log2.info(`Binary cache: only the active version (${keepTag}) is present at ${info.path}; nothing to clear`);
20115
20576
  return result;
20116
20577
  }
20117
20578
  for (const version of stale) {
20118
- const dir = join31(info.path, version);
20579
+ const dir = join33(info.path, version);
20119
20580
  let bytes = 0;
20120
20581
  try {
20121
20582
  bytes = statSync19(dir).isDirectory() ? dirSize(dir) : 0;
@@ -20123,7 +20584,7 @@ function clearOldBinaries() {
20123
20584
  bytes = 0;
20124
20585
  }
20125
20586
  try {
20126
- rmSync7(dir, { recursive: true, force: true });
20587
+ rmSync8(dir, { recursive: true, force: true });
20127
20588
  result.cleared += 1;
20128
20589
  result.bytesReclaimed += bytes;
20129
20590
  log2.success(`Binary cache: cleared ${dir} (reclaimed ${formatBytes(bytes)})`);
@@ -20275,7 +20736,7 @@ async function runDoctorNpmInstall(npm, installDir) {
20275
20736
  });
20276
20737
  }
20277
20738
  function installedPluginVersion(installDir) {
20278
- const manifest = join31(installDir, "node_modules", ...AFT_OPENCODE_PACKAGE.split("/"), "package.json");
20739
+ const manifest = join33(installDir, "node_modules", ...AFT_OPENCODE_PACKAGE.split("/"), "package.json");
20279
20740
  try {
20280
20741
  const pkg = JSON.parse(readFileSync17(manifest, "utf-8"));
20281
20742
  return typeof pkg.version === "string" ? pkg.version : null;
@@ -20693,6 +21154,22 @@ function applyConfigMigration(run, binaryDownloadFailed = false, targets = []) {
20693
21154
  for (const line of file.notes)
20694
21155
  log2.warn(` ${line}`);
20695
21156
  }
21157
+ for (const path of result.cacheCleanup?.removed ?? []) {
21158
+ changed += 1;
21159
+ log2.success(`Removed inactive rebuildable cache ${path}`);
21160
+ }
21161
+ for (const skipped of result.cacheCleanup?.skipped ?? [])
21162
+ log2.warn(`Inactive cache retained: ${skipped}`);
21163
+ for (const partial of result.cacheCleanup?.partial ?? []) {
21164
+ changed += 1;
21165
+ log2.warn(`Partially cleared ${partial.path}: removed ${partial.removed_entries} file/directory entries before repair stopped`);
21166
+ }
21167
+ for (const warning of result.cacheCleanup?.warnings ?? [])
21168
+ log2.warn(warning);
21169
+ if (result.cacheCleanup?.error) {
21170
+ errors += 1;
21171
+ log2.error(`Inactive cache cleanup refused: ${result.cacheCleanup.error}`);
21172
+ }
20696
21173
  return { changed, errors };
20697
21174
  }
20698
21175
  function logDoctorIssues(report, heading) {
@@ -20717,7 +21194,28 @@ function logDoctorIssues(report, heading) {
20717
21194
  function formatDoctorStorageStatus(h) {
20718
21195
  const state = h.storageDir.exists ? h.storageDir.path : `${h.storageDir.path} (${h.pluginRegistered ? "not yet created (lazy — created on first tool call)" : "not created"})`;
20719
21196
  const legacyDuplication = formatLegacyDuplication(h.storageDir.legacyDuplication);
20720
- return `${state} (${[formatStorageSizes(h.storageDir.sizesByKey), legacyDuplication].filter((part) => Boolean(part)).join("; ")})`;
21197
+ let cacheWarning = "";
21198
+ if (h.storageDir.cacheRoot) {
21199
+ try {
21200
+ const parent = realpathSync8(dirname12(h.storageDir.cacheRoot));
21201
+ let durable = h.storageDir.path;
21202
+ while (!existsSync30(durable) && dirname12(durable) !== durable)
21203
+ durable = dirname12(durable);
21204
+ let registered = false;
21205
+ const history = join33(h.storageDir.path, "retention/cache-parents.json");
21206
+ if (existsSync30(history)) {
21207
+ const saved = JSON.parse(readFileSync17(history, "utf8"));
21208
+ registered = saved.parents?.some((entry) => entry.path === parent) === true;
21209
+ }
21210
+ if (!registered && statSync19(parent).dev === statSync19(durable).dev) {
21211
+ cacheWarning = "; cache_dir_on_storage_device: first enrollment requires another volume";
21212
+ }
21213
+ } catch {}
21214
+ }
21215
+ return `${state}${h.storageDir.cacheRoot ? `; cache root: ${h.storageDir.cacheRoot} (configured; unavailable roots disable caches)${cacheWarning}` : ""} (${[
21216
+ formatStorageSizes(h.storageDir.sizesByKey),
21217
+ legacyDuplication
21218
+ ].filter((part) => Boolean(part)).join("; ")})`;
20721
21219
  }
20722
21220
  async function confirmBinaryDownloadDespitePluginSkew(report, argv) {
20723
21221
  const skews = findPluginCliVersionSkews(report);
@@ -20909,18 +21407,18 @@ function deriveIssueTitleFromBody(body) {
20909
21407
  function writeIssueReviewFile(body) {
20910
21408
  let reviewDir = null;
20911
21409
  try {
20912
- reviewDir = mkdtempSync2(join31(tmpdir5(), "aft-issue-"));
21410
+ reviewDir = mkdtempSync2(join33(tmpdir5(), "aft-issue-"));
20913
21411
  if (process.platform !== "win32") {
20914
- chmodSync3(reviewDir, 448);
21412
+ chmodSync4(reviewDir, 448);
20915
21413
  }
20916
- const outPath = join31(reviewDir, "issue.md");
21414
+ const outPath = join33(reviewDir, "issue.md");
20917
21415
  writeFileSync5(outPath, `${body}
20918
21416
  `, { encoding: "utf8", mode: 384, flag: "wx" });
20919
- return { path: outPath, realPath: realpathSync7(outPath) };
21417
+ return { path: outPath, realPath: realpathSync8(outPath) };
20920
21418
  } catch (err) {
20921
21419
  if (reviewDir) {
20922
21420
  try {
20923
- rmSync7(reviewDir, { recursive: true, force: true });
21421
+ rmSync8(reviewDir, { recursive: true, force: true });
20924
21422
  } catch {}
20925
21423
  }
20926
21424
  log2.error(`Failed to write sanitized issue report: ${err instanceof Error ? err.message : String(err)}`);
@@ -20929,7 +21427,7 @@ function writeIssueReviewFile(body) {
20929
21427
  }
20930
21428
  function readReviewedIssueFile(reviewFile) {
20931
21429
  try {
20932
- const realPath = realpathSync7(reviewFile.path);
21430
+ const realPath = realpathSync8(reviewFile.path);
20933
21431
  if (realPath !== reviewFile.realPath) {
20934
21432
  log2.error(`Review file path changed before filing; refusing to read ${reviewFile.path}.`);
20935
21433
  return null;
@@ -20978,6 +21476,31 @@ function collectIssueLog(path, selectedSession) {
20978
21476
  `)
20979
21477
  };
20980
21478
  }
21479
+ function collectIssueEngineLog(path, selectedSession) {
21480
+ const directory = dirname12(path);
21481
+ const files = [];
21482
+ try {
21483
+ for (const name of readdirSync13(directory)) {
21484
+ if (!/^aft-\d+\.log(?:\.1)?$/.test(name))
21485
+ continue;
21486
+ try {
21487
+ const candidate = join33(directory, name);
21488
+ const stat = lstatSync5(candidate);
21489
+ if (stat.isFile())
21490
+ files.push({ path: candidate, modified: stat.mtimeMs });
21491
+ } catch {}
21492
+ }
21493
+ } catch {}
21494
+ files.sort((a, b) => b.modified - a.modified || a.path.localeCompare(b.path));
21495
+ const result = readLogHistory(files.map((file) => file.path), selectedSession);
21496
+ return {
21497
+ logText: result.lines.join(`
21498
+ `),
21499
+ errors: result.errors.join(`
21500
+ `),
21501
+ notice: result.boundHit ? "[scan limit reached; older engine lines may be missing]" : ""
21502
+ };
21503
+ }
20981
21504
  async function runIssueFlow(argv) {
20982
21505
  intro(`${CLI} doctor --issue`);
20983
21506
  if (!isInteractiveTerminal()) {
@@ -20996,19 +21519,32 @@ async function runIssueFlow(argv) {
20996
21519
  const selectedSession = await promptForIssueSession(adapters[0]);
20997
21520
  const selectedBareSessionId = selectedSession?.id.replace(/^ses_/, "") ?? null;
20998
21521
  const report = await collectDiagnostics(adapters);
20999
- const logResults = adapters.map((adapter) => {
21522
+ const logResults = adapters.flatMap((adapter) => {
21000
21523
  const path = adapter.getLogFile();
21001
21524
  const { logText, errors, notice } = collectIssueLog(path, selectedSession);
21002
- return {
21003
- section: `#### ${adapter.displayName} log (${path})
21525
+ const engine = collectIssueEngineLog(path, selectedSession);
21526
+ return [
21527
+ {
21528
+ section: `#### ${adapter.displayName} log (${path})
21004
21529
 
21005
21530
  ${notice}
21006
21531
  \`\`\`
21007
21532
  ${logText || "<no log output>"}
21008
21533
  \`\`\`
21009
21534
  `,
21010
- errors
21011
- };
21535
+ errors
21536
+ },
21537
+ {
21538
+ section: `#### Engine logs (${dirname12(path)})
21539
+
21540
+ ${engine.notice}
21541
+ \`\`\`
21542
+ ${engine.logText || "<no log output>"}
21543
+ \`\`\`
21544
+ `,
21545
+ errors: engine.errors
21546
+ }
21547
+ ];
21012
21548
  });
21013
21549
  const logSections = logResults.map((result) => result.section).join(`
21014
21550
  `);
@@ -21037,7 +21573,7 @@ ${logText || "<no log output>"}
21037
21573
  "",
21038
21574
  toolFailuresSection,
21039
21575
  "",
21040
- selectedSession ? `## Logs (last 200 lines for session ${selectedSession.id}; project ${selectedSession.projectRoot ?? "unknown"}, ${selectedSession.startedAt !== undefined ? new Date(selectedSession.startedAt).toISOString() : "unknown start"}–${new Date(selectedSession.lastActivity).toISOString()})` : "## Logs (last 200 lines per harness)",
21576
+ selectedSession ? `## Logs (last 200 lines per source for session ${selectedSession.id}; project ${selectedSession.projectRoot ?? "unknown"}, ${selectedSession.startedAt !== undefined ? new Date(selectedSession.startedAt).toISOString() : "unknown start"}–${new Date(selectedSession.lastActivity).toISOString()})` : "## Logs (last 200 lines per source)",
21041
21577
  logSections,
21042
21578
  "_Usernames and home paths have been stripped from this report._"
21043
21579
  ].join(`
@@ -21090,6 +21626,7 @@ https://github.com/cortexkit/aft/issues/new and paste the contents of ${outPath}
21090
21626
  var DOCTOR_CLEAR_TARGET_OPTIONS, DOCTOR_FORCE_CLEAR_TARGETS;
21091
21627
  var init_doctor = __esm(async () => {
21092
21628
  init_dist2();
21629
+ init_adapters();
21093
21630
  init_opencode();
21094
21631
  init_features();
21095
21632
  init_opencode2();
@@ -21140,24 +21677,24 @@ var init_doctor = __esm(async () => {
21140
21677
  });
21141
21678
 
21142
21679
  // src/commands/lsp.ts
21143
- import { existsSync as existsSync31, readdirSync as readdirSync13, statSync as statSync20 } from "node:fs";
21680
+ import { existsSync as existsSync31, readdirSync as readdirSync14, statSync as statSync20 } from "node:fs";
21144
21681
  import { createRequire as createRequire5 } from "node:module";
21145
- import { dirname as dirname12, join as join32, resolve as resolve15 } from "node:path";
21682
+ import { dirname as dirname13, join as join34, resolve as resolve15 } from "node:path";
21146
21683
  function findProjectRootForFile(filePath, fallbackCwd = process.cwd()) {
21147
21684
  const resolvedFile = resolve15(fallbackCwd, filePath);
21148
- let dir = dirname12(resolvedFile);
21685
+ let dir = dirname13(resolvedFile);
21149
21686
  try {
21150
21687
  if (existsSync31(resolvedFile) && statSync20(resolvedFile).isDirectory()) {
21151
21688
  dir = resolvedFile;
21152
21689
  }
21153
21690
  } catch {
21154
- dir = dirname12(resolvedFile);
21691
+ dir = dirname13(resolvedFile);
21155
21692
  }
21156
21693
  while (true) {
21157
- if (PROJECT_ROOT_MARKERS.some((marker) => existsSync31(join32(dir, marker)))) {
21694
+ if (PROJECT_ROOT_MARKERS.some((marker) => existsSync31(join34(dir, marker)))) {
21158
21695
  return dir;
21159
21696
  }
21160
- const parent = dirname12(dir);
21697
+ const parent = dirname13(dir);
21161
21698
  if (parent === dir)
21162
21699
  return resolve15(fallbackCwd);
21163
21700
  dir = parent;
@@ -21303,10 +21840,10 @@ function buildConfigureParams(adapter, projectRoot) {
21303
21840
  function inferLspPathsExtra(_lsp) {
21304
21841
  const paths = new Set;
21305
21842
  for (const entry of childDirs(getAftLspPackagesDir())) {
21306
- paths.add(join32(entry, "node_modules", ".bin"));
21843
+ paths.add(join34(entry, "node_modules", ".bin"));
21307
21844
  }
21308
21845
  for (const entry of childDirs(getAftLspBinariesDir())) {
21309
- paths.add(join32(entry, "bin"));
21846
+ paths.add(join34(entry, "bin"));
21310
21847
  }
21311
21848
  return [...paths];
21312
21849
  }
@@ -21314,7 +21851,7 @@ function childDirs(path) {
21314
21851
  if (!existsSync31(path))
21315
21852
  return [];
21316
21853
  try {
21317
- return readdirSync13(path).map((entry) => join32(path, entry)).filter((entry) => {
21854
+ return readdirSync14(path).map((entry) => join34(path, entry)).filter((entry) => {
21318
21855
  try {
21319
21856
  return statSync20(entry).isDirectory();
21320
21857
  } catch {
@@ -21358,7 +21895,7 @@ function typescriptPackageWarning(response) {
21358
21895
  if (!typescriptServerSpawned || !response.project_root || diagnosticsCount > 0)
21359
21896
  return null;
21360
21897
  try {
21361
- createRequire5(join32(response.project_root, "package.json")).resolve("typescript");
21898
+ createRequire5(join34(response.project_root, "package.json")).resolve("typescript");
21362
21899
  return null;
21363
21900
  } catch {
21364
21901
  return "typescript package not resolvable from project — server will produce no diagnostics";
@@ -21658,7 +22195,7 @@ function printHelp() {
21658
22195
  console.log(" doctor Check and fix configuration issues");
21659
22196
  console.log(" doctor --profile [seconds] Profile a running AFT daemon");
21660
22197
  console.log(" doctor lsp <file> Inspect LSP setup for one file");
21661
- console.log(" doctor --fix Auto-fix common issues (e.g. ONNX Runtime mismatch, retired config keys)");
22198
+ console.log(" doctor --fix Auto-fix common issues (e.g. ONNX Runtime mismatch, retired config keys)", " doctor --clean-old-caches Confirm removal of old storage-root caches after moving them");
21662
22199
  console.log(" doctor --reconfigure Rerun the feature wizard");
21663
22200
  console.log(" --verbose Also print AFT's internal log lines (always kept in aft-cli.log)");
21664
22201
  console.log(" doctor --clear Select caches to clear with an interactive prompt");
@@ -21706,6 +22243,10 @@ async function main() {
21706
22243
  return runCache(args);
21707
22244
  }
21708
22245
  if (command === "doctor") {
22246
+ if (args.includes("--clean-old-caches")) {
22247
+ await init_clean_old_caches();
22248
+ return runOldCacheCleanup(args);
22249
+ }
21709
22250
  if (args.includes("--profile")) {
21710
22251
  await init_doctor();
21711
22252
  return runDoctorProfile(args);