@cortexkit/aft-opencode 0.43.1 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8233,6 +8233,8 @@ function resolveBashKillTimeout(modelTimeout, foregroundWaitMs) {
8233
8233
  }
8234
8234
  // ../aft-bridge/dist/bridge.js
8235
8235
  import { spawn } from "node:child_process";
8236
+ import { createHash } from "node:crypto";
8237
+ import { readFileSync } from "node:fs";
8236
8238
  import { homedir as homedir2 } from "node:os";
8237
8239
  import { join as join2 } from "node:path";
8238
8240
  import { StringDecoder } from "node:string_decoder";
@@ -8332,6 +8334,22 @@ function bashTaskIdFrom(response) {
8332
8334
  return camelCase;
8333
8335
  return;
8334
8336
  }
8337
+ function hashBinaryOnDisk(binaryPath) {
8338
+ try {
8339
+ return createHash("sha256").update(readFileSync(binaryPath)).digest("hex");
8340
+ } catch {
8341
+ return null;
8342
+ }
8343
+ }
8344
+ var binaryFingerprintReader = hashBinaryOnDisk;
8345
+ function readBinaryFingerprint(binaryPath) {
8346
+ try {
8347
+ const fingerprint = binaryFingerprintReader(binaryPath);
8348
+ return typeof fingerprint === "string" && fingerprint.length > 0 ? fingerprint : null;
8349
+ } catch {
8350
+ return null;
8351
+ }
8352
+ }
8335
8353
  function tagStderrLine(line) {
8336
8354
  return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
8337
8355
  }
@@ -8427,6 +8445,9 @@ class BinaryBridge {
8427
8445
  binaryPath;
8428
8446
  cwd;
8429
8447
  process = null;
8448
+ spawnedBinaryFingerprint = null;
8449
+ lastBinaryFingerprintCheckAt = 0;
8450
+ _retiringDueToBinaryChange = false;
8430
8451
  pending = new Map;
8431
8452
  outstandingBackgroundTaskIds = new Set;
8432
8453
  nextId = 1;
@@ -8546,6 +8567,24 @@ class BinaryBridge {
8546
8567
  hasOutstandingBackgroundTasks() {
8547
8568
  return this.outstandingBackgroundTaskIds.size > 0;
8548
8569
  }
8570
+ maybeScheduleRespawnForUpdatedBinary(checkIntervalMs, now = Date.now()) {
8571
+ if (this._shuttingDown || this._retiringDueToBinaryChange || !this.isAlive())
8572
+ return false;
8573
+ if (this.pending.size > 0 || this.outstandingBackgroundTaskIds.size > 0)
8574
+ return false;
8575
+ if (now - this.lastBinaryFingerprintCheckAt < checkIntervalMs)
8576
+ return false;
8577
+ this.lastBinaryFingerprintCheckAt = now;
8578
+ const spawnedFingerprint = this.spawnedBinaryFingerprint;
8579
+ if (!spawnedFingerprint)
8580
+ return false;
8581
+ const currentFingerprint = readBinaryFingerprint(this.binaryPath);
8582
+ if (!currentFingerprint || currentFingerprint === spawnedFingerprint)
8583
+ return false;
8584
+ this._retiringDueToBinaryChange = true;
8585
+ this.logVia(`Binary contents changed on disk for ${this.binaryPath}; retiring this bridge so the next tool call respawns onto the updated binary.`);
8586
+ return true;
8587
+ }
8549
8588
  getCwd() {
8550
8589
  return this.cwd;
8551
8590
  }
@@ -8578,6 +8617,9 @@ class BinaryBridge {
8578
8617
  }
8579
8618
  async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
8580
8619
  try {
8620
+ if (this._retiringDueToBinaryChange) {
8621
+ throw new Error(`${this.errorPrefix} Bridge is retiring after the on-disk binary changed; retry to respawn on the updated binary`);
8622
+ }
8581
8623
  if (this._shuttingDown) {
8582
8624
  throw new Error(`${this.errorPrefix} Bridge is shutting down, cannot send "${command}"`);
8583
8625
  }
@@ -8776,6 +8818,7 @@ class BinaryBridge {
8776
8818
  }
8777
8819
  async shutdown() {
8778
8820
  this._shuttingDown = true;
8821
+ this.spawnedBinaryFingerprint = null;
8779
8822
  this.clearRestartResetTimer();
8780
8823
  this.configureWarningClients.clear();
8781
8824
  this.rejectAllPending(new Error(`${this.errorPrefix} Bridge shutting down`));
@@ -8829,6 +8872,9 @@ class BinaryBridge {
8829
8872
  async replaceCurrentBinary(newBinaryPath) {
8830
8873
  this.binaryPath = newBinaryPath;
8831
8874
  this.configured = false;
8875
+ this.spawnedBinaryFingerprint = null;
8876
+ this.lastBinaryFingerprintCheckAt = 0;
8877
+ this._retiringDueToBinaryChange = false;
8832
8878
  this.clearRestartResetTimer();
8833
8879
  this.rejectAllPending(new Error(`${this.errorPrefix} Bridge restarting with updated binary: ${newBinaryPath}`));
8834
8880
  if (!this.process)
@@ -8854,6 +8900,7 @@ class BinaryBridge {
8854
8900
  this.spawnProcess(triggeringSessionId);
8855
8901
  }
8856
8902
  spawnProcess(triggeringSessionId) {
8903
+ this._retiringDueToBinaryChange = false;
8857
8904
  this.lastStatusBar = undefined;
8858
8905
  if (triggeringSessionId) {
8859
8906
  this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
@@ -8944,6 +8991,8 @@ class BinaryBridge {
8944
8991
  this.handleCrash();
8945
8992
  });
8946
8993
  this.process = child;
8994
+ this.spawnedBinaryFingerprint = readBinaryFingerprint(this.binaryPath);
8995
+ this.lastBinaryFingerprintCheckAt = Date.now();
8947
8996
  this.stdoutBuffer = "";
8948
8997
  this.stdoutReadOffset = 0;
8949
8998
  this.stderrBuffer = "";
@@ -9122,6 +9171,7 @@ class BinaryBridge {
9122
9171
  }
9123
9172
  handleTimeout(triggeringSessionId) {
9124
9173
  this.consecutiveRequestTimeouts = 0;
9174
+ this.spawnedBinaryFingerprint = null;
9125
9175
  this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
9126
9176
  this.outstandingBackgroundTaskIds.clear();
9127
9177
  if (this.process) {
@@ -9148,6 +9198,7 @@ class BinaryBridge {
9148
9198
  handleCrash(cause) {
9149
9199
  const proc = this.process;
9150
9200
  this.process = null;
9201
+ this.spawnedBinaryFingerprint = null;
9151
9202
  if (proc && proc.exitCode === null && !proc.killed) {
9152
9203
  proc.kill("SIGKILL");
9153
9204
  }
@@ -9159,6 +9210,10 @@ class BinaryBridge {
9159
9210
  this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
9160
9211
  }
9161
9212
  this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${this.getLogFilePathVia()})`));
9213
+ if (this._retiringDueToBinaryChange) {
9214
+ this.logVia("Binary exited while retiring after an on-disk update; skipping auto-restart");
9215
+ return;
9216
+ }
9162
9217
  if (this._restartCount < this.maxRestarts) {
9163
9218
  const delay = 100 * 2 ** this._restartCount;
9164
9219
  this._restartCount++;
@@ -9288,13 +9343,13 @@ function isEmptyParam(value) {
9288
9343
  return false;
9289
9344
  }
9290
9345
  // ../aft-bridge/dist/config-tiers.js
9291
- import { existsSync, readFileSync } from "node:fs";
9346
+ import { existsSync, readFileSync as readFileSync2 } from "node:fs";
9292
9347
  import { resolve as resolve2 } from "node:path";
9293
9348
  function readConfigTiers(opts) {
9294
9349
  const tiers = [];
9295
9350
  try {
9296
9351
  if (existsSync(opts.userConfigPath)) {
9297
- const doc = readFileSync(opts.userConfigPath, "utf-8");
9352
+ const doc = readFileSync2(opts.userConfigPath, "utf-8");
9298
9353
  tiers.push({
9299
9354
  tier: "user",
9300
9355
  source: resolve2(opts.userConfigPath),
@@ -9304,7 +9359,7 @@ function readConfigTiers(opts) {
9304
9359
  } catch {}
9305
9360
  try {
9306
9361
  if (existsSync(opts.projectConfigPath)) {
9307
- const doc = readFileSync(opts.projectConfigPath, "utf-8");
9362
+ const doc = readFileSync2(opts.projectConfigPath, "utf-8");
9308
9363
  tiers.push({
9309
9364
  tier: "project",
9310
9365
  source: resolve2(opts.projectConfigPath),
@@ -9322,8 +9377,8 @@ function formatDroppedKeyWarnings(dropped) {
9322
9377
  }
9323
9378
  // ../aft-bridge/dist/downloader.js
9324
9379
  import { spawnSync } from "node:child_process";
9325
- import { createHash, randomUUID } from "node:crypto";
9326
- import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readFileSync as readFileSync2, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
9380
+ import { createHash as createHash2, randomUUID } from "node:crypto";
9381
+ import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readFileSync as readFileSync3, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
9327
9382
  import { homedir as homedir3 } from "node:os";
9328
9383
  import { join as join3 } from "node:path";
9329
9384
  import { Readable } from "node:stream";
@@ -9467,7 +9522,7 @@ async function downloadBinary(version) {
9467
9522
  warn(`Checksum verification failed: checksums.sha256 found but no entry for ${assetName}. ` + "Binary download aborted for security reasons.");
9468
9523
  return null;
9469
9524
  }
9470
- const hash = createHash("sha256");
9525
+ const hash = createHash2("sha256");
9471
9526
  let bytesWritten = 0;
9472
9527
  const guard = new TransformStream({
9473
9528
  transform(chunk, controller) {
@@ -9551,7 +9606,7 @@ async function acquireDownloadLock(lockPath) {
9551
9606
  closeSync(fd);
9552
9607
  } catch {}
9553
9608
  try {
9554
- if (readFileSync2(lockPath, "utf-8") === owner) {
9609
+ if (readFileSync3(lockPath, "utf-8") === owner) {
9555
9610
  rmSync(lockPath, { force: true });
9556
9611
  }
9557
9612
  } catch {}
@@ -9623,12 +9678,12 @@ function stripJsoncSymbols(value) {
9623
9678
  }
9624
9679
  // ../aft-bridge/dist/migration.js
9625
9680
  import { spawnSync as spawnSync2 } from "node:child_process";
9626
- import { closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync4, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
9681
+ import { closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
9627
9682
  import { homedir as homedir6, tmpdir } from "node:os";
9628
9683
  import { basename, dirname as dirname2, join as join6, resolve as resolve4 } from "node:path";
9629
9684
 
9630
9685
  // ../aft-bridge/dist/paths.js
9631
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync } from "node:fs";
9686
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync } from "node:fs";
9632
9687
  import { homedir as homedir4 } from "node:os";
9633
9688
  import { dirname, isAbsolute as isAbsolute2, join as join4, resolve as resolve3 } from "node:path";
9634
9689
  function homeDir() {
@@ -9702,7 +9757,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
9702
9757
  let lastVersion = "";
9703
9758
  try {
9704
9759
  if (existsSync3(versionFile)) {
9705
- lastVersion = readFileSync3(versionFile, "utf-8").trim();
9760
+ lastVersion = readFileSync4(versionFile, "utf-8").trim();
9706
9761
  }
9707
9762
  } catch {
9708
9763
  return false;
@@ -10099,7 +10154,7 @@ function atomicCopyConfigFile(sourcePath, targetPath) {
10099
10154
  let fd = null;
10100
10155
  try {
10101
10156
  fd = openSync3(tmpPath, "wx", 384);
10102
- writeFileSync2(fd, readFileSync4(sourcePath));
10157
+ writeFileSync2(fd, readFileSync5(sourcePath));
10103
10158
  closeSync3(fd);
10104
10159
  fd = null;
10105
10160
  renameSync4(tmpPath, targetPath);
@@ -10178,7 +10233,7 @@ function markLegacySourcesMovedAside(sources, targetPath, logger) {
10178
10233
  const desiredMarkerPath = `${source.path}${MOVED_MARKER_SUFFIX}`;
10179
10234
  const markerPath = nonClobberingSidecarPath(desiredMarkerPath);
10180
10235
  try {
10181
- const original = readFileSync4(source.path, "utf-8");
10236
+ const original = readFileSync5(source.path, "utf-8");
10182
10237
  if (markerPath !== desiredMarkerPath) {
10183
10238
  logger?.warn?.(`Preserving existing legacy AFT marker ${desiredMarkerPath}; writing new marker to ${markerPath}`);
10184
10239
  }
@@ -10247,10 +10302,10 @@ function migrateAftConfigFile(opts) {
10247
10302
  try {
10248
10303
  const sources = existingSources.map((source) => ({
10249
10304
  ...source,
10250
- content: readFileSync4(source.path, "utf-8")
10305
+ content: readFileSync5(source.path, "utf-8")
10251
10306
  }));
10252
10307
  if (existsSync5(opts.targetPath)) {
10253
- const targetContent = readFileSync4(opts.targetPath, "utf-8");
10308
+ const targetContent = readFileSync5(opts.targetPath, "utf-8");
10254
10309
  for (const source of sources) {
10255
10310
  if (fileSemanticsMatch(source.content, targetContent))
10256
10311
  continue;
@@ -10484,8 +10539,8 @@ function isNpmAvailable(deps = defaultDeps()) {
10484
10539
  }
10485
10540
  // ../aft-bridge/dist/onnx-runtime.js
10486
10541
  import { execFileSync } from "node:child_process";
10487
- import { createHash as createHash2 } from "node:crypto";
10488
- import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, readlinkSync, realpathSync, rmSync as rmSync3, statSync as statSync4, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
10542
+ import { createHash as createHash3 } from "node:crypto";
10543
+ import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync6, readlinkSync, realpathSync, rmSync as rmSync3, statSync as statSync4, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
10489
10544
  import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute4, join as join8, relative as relative2, resolve as resolve5, win32 } from "node:path";
10490
10545
  import { Readable as Readable2 } from "node:stream";
10491
10546
  import { pipeline as pipeline2 } from "node:stream/promises";
@@ -11038,7 +11093,7 @@ function readOnnxInstalledMeta(installDir) {
11038
11093
  try {
11039
11094
  if (!statSync4(path2).isFile())
11040
11095
  return null;
11041
- const raw = readFileSync5(path2, "utf8");
11096
+ const raw = readFileSync6(path2, "utf8");
11042
11097
  const parsed = JSON.parse(raw);
11043
11098
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
11044
11099
  return null;
@@ -11053,8 +11108,8 @@ function readOnnxInstalledMeta(installDir) {
11053
11108
  }
11054
11109
  }
11055
11110
  function sha256File(path2) {
11056
- const hash = createHash2("sha256");
11057
- hash.update(readFileSync5(path2));
11111
+ const hash = createHash3("sha256");
11112
+ hash.update(readFileSync6(path2));
11058
11113
  return hash.digest("hex");
11059
11114
  }
11060
11115
  function acquireLock(lockPath) {
@@ -11082,7 +11137,7 @@ ${new Date().toISOString()}
11082
11137
  let owningPid = null;
11083
11138
  let lockMtimeMs = 0;
11084
11139
  try {
11085
- const raw = readFileSync5(lockPath, "utf8");
11140
+ const raw = readFileSync6(lockPath, "utf8");
11086
11141
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
11087
11142
  const parsed = Number.parseInt(firstLine, 10);
11088
11143
  if (Number.isFinite(parsed) && parsed > 0)
@@ -11107,7 +11162,7 @@ function releaseLock(lockPath) {
11107
11162
  try {
11108
11163
  let owningPid = null;
11109
11164
  try {
11110
- const raw = readFileSync5(lockPath, "utf8");
11165
+ const raw = readFileSync6(lockPath, "utf8");
11111
11166
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
11112
11167
  const parsed = Number.parseInt(firstLine, 10);
11113
11168
  if (Number.isFinite(parsed) && parsed > 0)
@@ -11171,7 +11226,7 @@ function isProcessAlive(pid) {
11171
11226
  }
11172
11227
  }
11173
11228
  // ../aft-bridge/dist/project-identity.js
11174
- import { createHash as createHash3 } from "node:crypto";
11229
+ import { createHash as createHash4 } from "node:crypto";
11175
11230
  import { realpathSync as realpathSync2 } from "node:fs";
11176
11231
  import { resolve as resolve6 } from "node:path";
11177
11232
  function canonicalizeProjectRoot(dir) {
@@ -11202,7 +11257,7 @@ function normalizeWindowsRoot(p) {
11202
11257
  return s;
11203
11258
  }
11204
11259
  function projectRootKeyHash(dir) {
11205
- return createHash3("sha256").update(canonicalizeProjectRoot(dir)).digest("hex").slice(0, 16);
11260
+ return createHash4("sha256").update(canonicalizeProjectRoot(dir)).digest("hex").slice(0, 16);
11206
11261
  }
11207
11262
 
11208
11263
  // ../aft-bridge/dist/pool.js
@@ -11286,6 +11341,11 @@ class BridgePool {
11286
11341
  for (const [dir, entry] of this.bridges) {
11287
11342
  if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
11288
11343
  continue;
11344
+ if (entry.bridge.maybeScheduleRespawnForUpdatedBinary(CLEANUP_INTERVAL_MS, now)) {
11345
+ this.staleBridges.add(entry.bridge);
11346
+ this.bridges.delete(dir);
11347
+ continue;
11348
+ }
11289
11349
  if (now - entry.lastUsed > this.idleTimeoutMs) {
11290
11350
  entry.bridge.shutdown().catch((err) => this.error("cleanup shutdown failed:", err));
11291
11351
  this.bridges.delete(dir);
@@ -11382,10 +11442,11 @@ class BridgePool {
11382
11442
  function normalizeKey(projectRoot) {
11383
11443
  return canonicalizeProjectRoot(projectRoot);
11384
11444
  }
11385
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.0/node_modules/@cortexkit/subc-client/src/client.ts
11445
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/client.ts
11386
11446
  import { promises as fs2 } from "node:fs";
11447
+ import { debuglog } from "node:util";
11387
11448
 
11388
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.0/node_modules/@cortexkit/subc-client/src/auth.ts
11449
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/auth.ts
11389
11450
  import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
11390
11451
  var NONCE_LEN = 32;
11391
11452
  var MAX_AUTH_MESSAGE_LEN = 4096;
@@ -11449,7 +11510,7 @@ async function authenticateClient(sock, conn, deadlineMs) {
11449
11510
  await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
11450
11511
  }
11451
11512
 
11452
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.0/node_modules/@cortexkit/subc-client/src/connection-file.ts
11513
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/connection-file.ts
11453
11514
  import { promises as fs } from "node:fs";
11454
11515
  var SCHEMA_VERSION = 1;
11455
11516
  var MIN_KEY_LEN = 32;
@@ -11518,7 +11579,7 @@ async function readConnectionFile(path2) {
11518
11579
  return info;
11519
11580
  }
11520
11581
 
11521
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.0/node_modules/@cortexkit/subc-client/src/envelope.ts
11582
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/envelope.ts
11522
11583
  var PROTOCOL_VERSION = 1;
11523
11584
  var HEADER_LEN = 17;
11524
11585
  var FROZEN_PREFIX_LEN = 5;
@@ -11625,7 +11686,7 @@ function encodeFrame(frame) {
11625
11686
  return out;
11626
11687
  }
11627
11688
 
11628
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.0/node_modules/@cortexkit/subc-client/src/socket.ts
11689
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/socket.ts
11629
11690
  import net from "node:net";
11630
11691
 
11631
11692
  class SocketClosedError extends Error {
@@ -11656,6 +11717,9 @@ class SubcSocket {
11656
11717
  buffered = 0;
11657
11718
  waiter = null;
11658
11719
  closedErr = null;
11720
+ bufferedBytes() {
11721
+ return this.buffered;
11722
+ }
11659
11723
  constructor(sock) {
11660
11724
  this.sock = sock;
11661
11725
  sock.on("data", (chunk) => {
@@ -11672,6 +11736,9 @@ class SubcSocket {
11672
11736
  sock.on("end", () => fail(new SocketClosedError("subc closed the connection")));
11673
11737
  sock.on("close", () => fail(new SocketClosedError("subc connection closed")));
11674
11738
  }
11739
+ localPort() {
11740
+ return this.sock.localPort ?? null;
11741
+ }
11675
11742
  static connect(host, port, deadlineMs) {
11676
11743
  return new Promise((resolve7, reject) => {
11677
11744
  const sock = net.connect({ host, port });
@@ -11811,9 +11878,14 @@ class SubcSocket {
11811
11878
  }
11812
11879
  }
11813
11880
 
11814
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.0/node_modules/@cortexkit/subc-client/src/client.ts
11881
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/client.ts
11882
+ var debug = debuglog("subc-client");
11815
11883
  var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
11816
11884
  var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
11885
+ var TIMEOUT_ARBITRATION_GRACE_MS = 50;
11886
+ var REQUEST_DEADLINE_MARKER = "request_deadline";
11887
+ var DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed";
11888
+ var ROUTE_OPEN_RETRY_DEADLINE_MS = 1e4;
11817
11889
  var BODY_READ_TIMEOUT_MS = 30000;
11818
11890
  var EMPTY_BODY = new Uint8Array(0);
11819
11891
  var DEFAULT_MANAGED_TARGET_KIND = "management_surface";
@@ -11857,6 +11929,7 @@ class SubcClient {
11857
11929
  closeStarted = false;
11858
11930
  reconnecting = null;
11859
11931
  generation = 1;
11932
+ readerActive = false;
11860
11933
  constructor(sock, currentConn, opts) {
11861
11934
  this.sock = sock;
11862
11935
  this.currentConn = currentConn;
@@ -11915,7 +11988,7 @@ class SubcClient {
11915
11988
  }
11916
11989
  continue;
11917
11990
  }
11918
- if (err.kind === "outcome_unknown") {
11991
+ if (err.kind === "outcome_unknown" && err.code !== DEADLINE_NO_DROP_CODE) {
11919
11992
  this.scheduleReconnectAfterDrop(err);
11920
11993
  }
11921
11994
  throw err;
@@ -12038,7 +12111,7 @@ class SubcClient {
12038
12111
  timer: null
12039
12112
  };
12040
12113
  pending.timer = setTimeout(() => {
12041
- this.rejectPending(key, pending, new SubcError(`request on channel ${channel} timed out after ${ms}ms`));
12114
+ this.arbitrateTimeout(key, pending, channel, corr, ms);
12042
12115
  }, ms);
12043
12116
  this.pending.set(key, pending);
12044
12117
  this.sock.write(encodeFrame(frame), Date.now() + ms).catch((err) => {
@@ -12048,6 +12121,23 @@ class SubcClient {
12048
12121
  });
12049
12122
  });
12050
12123
  }
12124
+ arbitrateTimeout(key, pending, channel, corr, ms) {
12125
+ const settleAsTimeout = () => {
12126
+ this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
12127
+ };
12128
+ const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
12129
+ const arbitrate = () => {
12130
+ if (this.pending.get(key) !== pending)
12131
+ return;
12132
+ const readerDraining = this.readerActive || this.sock.bufferedBytes() > 0;
12133
+ if (readerDraining && Date.now() < graceDeadline) {
12134
+ setImmediate(arbitrate);
12135
+ return;
12136
+ }
12137
+ settleAsTimeout();
12138
+ };
12139
+ setImmediate(arbitrate);
12140
+ }
12051
12141
  async managedRequest(routeChannel, body, opts) {
12052
12142
  const bytes = body instanceof Uint8Array ? body : this.encode(body);
12053
12143
  const priority = opts.priority ?? 1 /* Interactive */;
@@ -12072,6 +12162,9 @@ class SubcClient {
12072
12162
  if (!handedToSocket) {
12073
12163
  return this.notSentCallError("request bytes were not queued to the subc socket", err);
12074
12164
  }
12165
+ if (err instanceof SubcError && err.code === REQUEST_DEADLINE_MARKER) {
12166
+ return new SubcCallError("outcome_unknown", `managed call deadline exceeded after request bytes were queued to the local socket; no terminal response was observed; outcome unknown${causeMessage(err)}`, DEADLINE_NO_DROP_CODE, err);
12167
+ }
12075
12168
  return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", err);
12076
12169
  };
12077
12170
  return new Promise((resolve7, reject) => {
@@ -12085,7 +12178,7 @@ class SubcClient {
12085
12178
  classifyFailure
12086
12179
  };
12087
12180
  pending.timer = setTimeout(() => {
12088
- this.rejectPending(key, pending, new SubcError(`request on channel ${channel} timed out after ${ms}ms`));
12181
+ this.arbitrateTimeout(key, pending, channel, corr, ms);
12089
12182
  }, ms);
12090
12183
  this.pending.set(key, pending);
12091
12184
  const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
@@ -12130,6 +12223,9 @@ class SubcClient {
12130
12223
  return cached.opening;
12131
12224
  }
12132
12225
  async openCachedRoute(cached) {
12226
+ const routeRetryDeadline = Date.now() + ROUTE_OPEN_RETRY_DEADLINE_MS;
12227
+ let routeRetryDelay = this.opts.reconnectBackoff.baseMs;
12228
+ let routeRetryAttempt = 0;
12133
12229
  for (;; ) {
12134
12230
  if (cached.closed)
12135
12231
  throw this.routeClosedDuringOpen();
@@ -12163,6 +12259,15 @@ class SubcClient {
12163
12259
  }
12164
12260
  continue;
12165
12261
  }
12262
+ if (!this.closeStarted && err instanceof SubcError && isRetryableRouteOpenCode(err.code)) {
12263
+ routeRetryAttempt += 1;
12264
+ if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
12265
+ await this.opts.sleep(routeRetryDelay);
12266
+ routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
12267
+ continue;
12268
+ }
12269
+ throw this.notSentCallError(`route.open failed for module ${cached.moduleId}: ${err.code} (retry budget exhausted)`, err);
12270
+ }
12166
12271
  throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`, err);
12167
12272
  }
12168
12273
  }
@@ -12233,7 +12338,9 @@ class SubcClient {
12233
12338
  for (const cached of this.routes.values()) {
12234
12339
  if (cached.closed)
12235
12340
  continue;
12236
- const channel = await this.routeOpen(cached.target, cached.identity);
12341
+ const channel = await this.routeOpen(cached.target, cached.identity, {
12342
+ consumerIdentity: cached.consumerIdentity ?? null
12343
+ });
12237
12344
  if (cached.closed) {
12238
12345
  this.sendRouteGoodbye(channel);
12239
12346
  continue;
@@ -12242,6 +12349,11 @@ class SubcClient {
12242
12349
  cached.generation = this.generation;
12243
12350
  }
12244
12351
  }
12352
+ timeoutMessage(channel, corr, ms) {
12353
+ const port = this.sock.localPort();
12354
+ const where = port === null ? "channel" : `local_port=${port} channel`;
12355
+ return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`;
12356
+ }
12245
12357
  routeClosedDuringOpen() {
12246
12358
  return new SubcCallError("not_sent", "route was closed before route.open completed", "route_closed");
12247
12359
  }
@@ -12249,9 +12361,16 @@ class SubcClient {
12249
12361
  try {
12250
12362
  for (;; ) {
12251
12363
  const headerBytes = await sock.readExact(HEADER_LEN, Number.POSITIVE_INFINITY);
12252
- const header = decodeHeader(headerBytes);
12253
- const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS);
12254
- this.dispatch({ header, body });
12364
+ this.readerActive = true;
12365
+ try {
12366
+ const header = decodeHeader(headerBytes);
12367
+ const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS);
12368
+ if (this.sock === sock && this.generation === generation) {
12369
+ this.dispatch({ header, body });
12370
+ }
12371
+ } finally {
12372
+ this.readerActive = false;
12373
+ }
12255
12374
  }
12256
12375
  } catch (err) {
12257
12376
  if (this.sock === sock && this.generation === generation) {
@@ -12283,13 +12402,20 @@ class SubcClient {
12283
12402
  this.failChannel(frame.header.channel, new SubcError("route closed by subc (GOODBYE)"));
12284
12403
  return;
12285
12404
  }
12405
+ if (frame.header.ty === 1 /* Response */ || frame.header.ty === 5 /* Error */ || frame.header.ty === 4 /* StreamEnd */) {
12406
+ debug("dropped terminal frame with no waiter: type=%d channel=%d corr=%s port=%s", frame.header.ty, frame.header.channel, frame.header.corr, this.sock.localPort() ?? "?");
12407
+ return;
12408
+ }
12286
12409
  }
12287
12410
  settle(key, pending, run) {
12411
+ if (this.pending.get(key) !== pending)
12412
+ return false;
12288
12413
  this.pending.delete(key);
12289
12414
  if (pending.timer)
12290
12415
  clearTimeout(pending.timer);
12291
12416
  run();
12292
12417
  pending.onSettle?.();
12418
+ return true;
12293
12419
  }
12294
12420
  rejectPending(key, pending, err) {
12295
12421
  this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
@@ -12353,6 +12479,9 @@ function isConsumerReconnectTransient(err) {
12353
12479
  const code = errorCode(err);
12354
12480
  return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
12355
12481
  }
12482
+ function isRetryableRouteOpenCode(code) {
12483
+ return code === "unknown_module" || code === "module_reloading" || code === "target_unavailable" || code === "module_timeout";
12484
+ }
12356
12485
  async function connectionFileExists(path2) {
12357
12486
  try {
12358
12487
  await fs2.access(path2);
@@ -12368,7 +12497,8 @@ function normalizeConnectOptions(opts) {
12368
12497
  identity: opts.identity,
12369
12498
  targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
12370
12499
  reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
12371
- sleep: opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)))
12500
+ sleep: opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms))),
12501
+ timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS
12372
12502
  };
12373
12503
  }
12374
12504
  function routeCacheKey(target, identity, consumerIdentity) {
@@ -12397,7 +12527,7 @@ function causeMessage(cause) {
12397
12527
  return "";
12398
12528
  return `: ${cause instanceof Error ? cause.message : String(cause)}`;
12399
12529
  }
12400
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.0/node_modules/@cortexkit/subc-client/src/provider.ts
12530
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/provider.ts
12401
12531
  import { Buffer as Buffer2 } from "node:buffer";
12402
12532
  var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
12403
12533
  var BODY_READ_TIMEOUT_MS2 = 30000;
@@ -13426,7 +13556,7 @@ async function createAftTransportPool(opts) {
13426
13556
  return new BridgePool(opts.binaryPath, opts.poolOptions, opts.configOverrides);
13427
13557
  }
13428
13558
  // src/bg-notifications.ts
13429
- import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
13559
+ import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypto";
13430
13560
 
13431
13561
  // src/logger.ts
13432
13562
  import * as fs3 from "node:fs";
@@ -13590,7 +13720,7 @@ async function resolvePromptContext(client, sessionId) {
13590
13720
 
13591
13721
  // src/bg-notifications.ts
13592
13722
  function hashReminder(text) {
13593
- return createHash4("sha256").update(text).digest("hex").slice(0, 16);
13723
+ return createHash5("sha256").update(text).digest("hex").slice(0, 16);
13594
13724
  }
13595
13725
  var CONSUMED_TASKIDS_CAP = 256;
13596
13726
  var DELIVERED_AWAITING_ACK_CAP = 4096;
@@ -14354,7 +14484,7 @@ function formatDuration(completion) {
14354
14484
  }
14355
14485
 
14356
14486
  // src/config.ts
14357
- import { existsSync as existsSync7, readFileSync as readFileSync6, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
14487
+ import { existsSync as existsSync7, readFileSync as readFileSync7, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
14358
14488
  var import_comment_json = __toESM(require_src2(), 1);
14359
14489
 
14360
14490
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
@@ -28728,6 +28858,7 @@ var InspectConfigSchema = exports_external.object({
28728
28858
  duplicates: exports_external.object({
28729
28859
  lower_bound: exports_external.number().int().positive().optional(),
28730
28860
  discard_cost: exports_external.number().int().min(0).optional(),
28861
+ expected_mirrors: exports_external.array(exports_external.tuple([exports_external.string().trim().min(1), exports_external.string().trim().min(1)])).optional(),
28731
28862
  anonymize: exports_external.object({
28732
28863
  variables: exports_external.boolean().optional(),
28733
28864
  fields: exports_external.boolean().optional(),
@@ -28945,7 +29076,7 @@ function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 })
28945
29076
  let tmpPath = null;
28946
29077
  let oldKeys = [];
28947
29078
  try {
28948
- const content = readFileSync6(configPath, "utf-8");
29079
+ const content = readFileSync7(configPath, "utf-8");
28949
29080
  const rawConfig = import_comment_json.parse(content);
28950
29081
  if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
28951
29082
  return { migrated: false, oldKeys: [] };
@@ -29022,7 +29153,7 @@ function loadConfigFromPath(configPath) {
29022
29153
  if (!existsSync7(configPath)) {
29023
29154
  return null;
29024
29155
  }
29025
- const content = readFileSync6(configPath, "utf-8");
29156
+ const content = readFileSync7(configPath, "utf-8");
29026
29157
  const rawConfig = import_comment_json.parse(content);
29027
29158
  migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
29028
29159
  const cleanConfig = stripJsoncSymbols(rawConfig);
@@ -29290,7 +29421,7 @@ function loadAftConfig(projectDirectory) {
29290
29421
  }
29291
29422
 
29292
29423
  // src/notifications.ts
29293
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
29424
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
29294
29425
  import { homedir as homedir9, platform } from "node:os";
29295
29426
  import { join as join11 } from "node:path";
29296
29427
  function isTuiMode() {
@@ -29333,7 +29464,7 @@ function readDesktopState() {
29333
29464
  return { serverUrl: null };
29334
29465
  }
29335
29466
  try {
29336
- const raw = readFileSync7(statePath, "utf-8");
29467
+ const raw = readFileSync8(statePath, "utf-8");
29337
29468
  const state = JSON.parse(raw);
29338
29469
  let serverUrl = null;
29339
29470
  const serverStr = state.server;
@@ -29832,7 +29963,7 @@ import {
29832
29963
  existsSync as existsSync11,
29833
29964
  mkdirSync as mkdirSync6,
29834
29965
  openSync as openSync5,
29835
- readFileSync as readFileSync10,
29966
+ readFileSync as readFileSync11,
29836
29967
  renameSync as renameSync6,
29837
29968
  rmSync as rmSync5,
29838
29969
  writeFileSync as writeFileSync7
@@ -29841,14 +29972,14 @@ import { dirname as dirname7 } from "node:path";
29841
29972
 
29842
29973
  // src/hooks/auto-update-checker/cache.ts
29843
29974
  import { spawn as spawn2 } from "node:child_process";
29844
- import { cpSync, existsSync as existsSync10, mkdtempSync, readFileSync as readFileSync9, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
29975
+ import { cpSync, existsSync as existsSync10, mkdtempSync, readFileSync as readFileSync10, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
29845
29976
  import { tmpdir as tmpdir3 } from "node:os";
29846
29977
  import { basename as basename3, dirname as dirname6, join as join14 } from "node:path";
29847
29978
  var import_comment_json3 = __toESM(require_src2(), 1);
29848
29979
 
29849
29980
  // src/hooks/auto-update-checker/checker.ts
29850
29981
  var import_comment_json2 = __toESM(require_src2(), 1);
29851
- import { existsSync as existsSync9, readFileSync as readFileSync8, statSync as statSync5, writeFileSync as writeFileSync5 } from "node:fs";
29982
+ import { existsSync as existsSync9, readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync5 } from "node:fs";
29852
29983
  import { homedir as homedir11 } from "node:os";
29853
29984
  import { dirname as dirname5, isAbsolute as isAbsolute6, join as join13, resolve as resolve7 } from "node:path";
29854
29985
  import { fileURLToPath } from "node:url";
@@ -29954,7 +30085,7 @@ function getLocalDevPath(directory) {
29954
30085
  try {
29955
30086
  if (!existsSync9(configPath))
29956
30087
  continue;
29957
- const rawConfig = parseJsonConfig(readFileSync8(configPath, "utf-8"));
30088
+ const rawConfig = parseJsonConfig(readFileSync9(configPath, "utf-8"));
29958
30089
  const plugins = getPluginEntries(rawConfig);
29959
30090
  for (const entry of plugins) {
29960
30091
  if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
@@ -29964,7 +30095,7 @@ function getLocalDevPath(directory) {
29964
30095
  const pkgPath = findPackageJsonUp(localPath);
29965
30096
  if (!pkgPath)
29966
30097
  continue;
29967
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
30098
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(pkgPath, "utf-8")));
29968
30099
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
29969
30100
  return localPath;
29970
30101
  }
@@ -29981,7 +30112,7 @@ function findPackageJsonUp(startPath) {
29981
30112
  const pkgPath = join13(dir, "package.json");
29982
30113
  if (existsSync9(pkgPath)) {
29983
30114
  try {
29984
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
30115
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(pkgPath, "utf-8")));
29985
30116
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
29986
30117
  return pkgPath;
29987
30118
  } catch {}
@@ -30002,7 +30133,7 @@ function getLocalDevVersion(directory) {
30002
30133
  const pkgPath = findPackageJsonUp(localPath);
30003
30134
  if (!pkgPath)
30004
30135
  return null;
30005
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
30136
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(pkgPath, "utf-8")));
30006
30137
  return pkg.success ? pkg.data.version ?? null : null;
30007
30138
  } catch {
30008
30139
  return null;
@@ -30021,7 +30152,7 @@ function findPluginEntry(directory) {
30021
30152
  try {
30022
30153
  if (!existsSync9(configPath))
30023
30154
  continue;
30024
- const rawConfig = parseJsonConfig(readFileSync8(configPath, "utf-8"));
30155
+ const rawConfig = parseJsonConfig(readFileSync9(configPath, "utf-8"));
30025
30156
  const plugins = getPluginEntries(rawConfig);
30026
30157
  for (const entry of plugins) {
30027
30158
  if (entry === PACKAGE_NAME) {
@@ -30054,7 +30185,7 @@ function getCachedVersion(spec) {
30054
30185
  try {
30055
30186
  if (!existsSync9(packageJsonPath))
30056
30187
  continue;
30057
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(packageJsonPath, "utf-8")));
30188
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(packageJsonPath, "utf-8")));
30058
30189
  if (pkg.success && pkg.data.version) {
30059
30190
  if (!spec)
30060
30191
  cachedPackageVersion = pkg.data.version;
@@ -30104,9 +30235,9 @@ function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
30104
30235
  cpSync(packageDir, stagedPackageDir, { recursive: true });
30105
30236
  return {
30106
30237
  packageJsonPath,
30107
- packageJson: existsSync10(packageJsonPath) ? readFileSync9(packageJsonPath, "utf-8") : null,
30238
+ packageJson: existsSync10(packageJsonPath) ? readFileSync10(packageJsonPath, "utf-8") : null,
30108
30239
  lockfilePath,
30109
- lockfile: existsSync10(lockfilePath) ? readFileSync9(lockfilePath, "utf-8") : null,
30240
+ lockfile: existsSync10(lockfilePath) ? readFileSync10(lockfilePath, "utf-8") : null,
30110
30241
  packageDir,
30111
30242
  stagedPackageDir,
30112
30243
  tempDir
@@ -30144,7 +30275,7 @@ function removeFromPackageLock(installDir, packageName) {
30144
30275
  if (!existsSync10(lockPath))
30145
30276
  return false;
30146
30277
  try {
30147
- const lock = import_comment_json3.parse(readFileSync9(lockPath, "utf-8"));
30278
+ const lock = import_comment_json3.parse(readFileSync10(lockPath, "utf-8"));
30148
30279
  let modified = false;
30149
30280
  if (lock.packages) {
30150
30281
  const key = `node_modules/${packageName}`;
@@ -30170,7 +30301,7 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
30170
30301
  if (!existsSync10(packageJsonPath))
30171
30302
  return false;
30172
30303
  try {
30173
- const raw = import_comment_json3.parse(readFileSync9(packageJsonPath, "utf-8"));
30304
+ const raw = import_comment_json3.parse(readFileSync10(packageJsonPath, "utf-8"));
30174
30305
  const pkgJson = PackageJsonSchema.safeParse(raw);
30175
30306
  if (!pkgJson.success)
30176
30307
  return false;
@@ -30424,7 +30555,7 @@ function hasRecentCheckTimestamp(file2, intervalMs) {
30424
30555
  if (!existsSync11(file2))
30425
30556
  return false;
30426
30557
  try {
30427
- const raw = JSON.parse(readFileSync10(file2, "utf-8"));
30558
+ const raw = JSON.parse(readFileSync11(file2, "utf-8"));
30428
30559
  const last = typeof raw.lastCheckedMs === "number" ? raw.lastCheckedMs : 0;
30429
30560
  return Number.isFinite(last) && Date.now() - last < intervalMs;
30430
30561
  } catch {
@@ -30528,12 +30659,12 @@ function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
30528
30659
 
30529
30660
  // src/lsp-auto-install.ts
30530
30661
  import { spawn as spawn3 } from "node:child_process";
30531
- import { createHash as createHash5 } from "node:crypto";
30662
+ import { createHash as createHash6 } from "node:crypto";
30532
30663
  import {
30533
30664
  createReadStream,
30534
30665
  existsSync as existsSync13,
30535
30666
  mkdirSync as mkdirSync8,
30536
- readFileSync as readFileSync13,
30667
+ readFileSync as readFileSync14,
30537
30668
  renameSync as renameSync7,
30538
30669
  rmSync as rmSync6,
30539
30670
  statSync as statSync7,
@@ -30546,7 +30677,7 @@ import {
30546
30677
  closeSync as closeSync6,
30547
30678
  mkdirSync as mkdirSync7,
30548
30679
  openSync as openSync6,
30549
- readFileSync as readFileSync11,
30680
+ readFileSync as readFileSync12,
30550
30681
  statSync as statSync6,
30551
30682
  unlinkSync as unlinkSync6,
30552
30683
  writeFileSync as writeFileSync8
@@ -30610,7 +30741,7 @@ function readInstalledMetaIn(installDir) {
30610
30741
  try {
30611
30742
  if (!statSync6(path3).isFile())
30612
30743
  return null;
30613
- const raw = readFileSync11(path3, "utf8");
30744
+ const raw = readFileSync12(path3, "utf8");
30614
30745
  const parsed = JSON.parse(raw);
30615
30746
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
30616
30747
  return null;
@@ -30660,7 +30791,7 @@ ${new Date().toISOString()}
30660
30791
  let owningPid = null;
30661
30792
  let lockMtimeMs = 0;
30662
30793
  try {
30663
- const raw = readFileSync11(lock, "utf8");
30794
+ const raw = readFileSync12(lock, "utf8");
30664
30795
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
30665
30796
  const parsed = Number.parseInt(firstLine, 10);
30666
30797
  if (Number.isFinite(parsed) && parsed > 0)
@@ -30698,7 +30829,7 @@ function releaseInstallLock(lockKey) {
30698
30829
  try {
30699
30830
  let owningPid = null;
30700
30831
  try {
30701
- const raw = readFileSync11(lock, "utf8");
30832
+ const raw = readFileSync12(lock, "utf8");
30702
30833
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
30703
30834
  const parsed = Number.parseInt(firstLine, 10);
30704
30835
  if (Number.isFinite(parsed) && parsed > 0)
@@ -30739,7 +30870,7 @@ var VERSION_CHECK_FILE = ".aft-version-check";
30739
30870
  function readVersionCheck(npmPackage) {
30740
30871
  const file2 = join15(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
30741
30872
  try {
30742
- const raw = readFileSync11(file2, "utf8");
30873
+ const raw = readFileSync12(file2, "utf8");
30743
30874
  const parsed = JSON.parse(raw);
30744
30875
  if (typeof parsed.last_checked === "string") {
30745
30876
  return {
@@ -30916,7 +31047,7 @@ var NPM_LSP_TABLE = [
30916
31047
  ];
30917
31048
 
30918
31049
  // src/lsp-project-relevance.ts
30919
- import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync12 } from "node:fs";
31050
+ import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync13 } from "node:fs";
30920
31051
  import { join as join16 } from "node:path";
30921
31052
  var MAX_WALK_DIRS = 200;
30922
31053
  var MAX_WALK_DEPTH = 4;
@@ -30958,7 +31089,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
30958
31089
  }
30959
31090
  function readPackageJson(projectRoot) {
30960
31091
  try {
30961
- const raw = readFileSync12(join16(projectRoot, "package.json"), "utf8");
31092
+ const raw = readFileSync13(join16(projectRoot, "package.json"), "utf8");
30962
31093
  const parsed = JSON.parse(raw);
30963
31094
  if (typeof parsed !== "object" || parsed === null)
30964
31095
  return null;
@@ -31280,7 +31411,7 @@ function hashInstalledBinary(spec) {
31280
31411
  reject(new Error(`installed binary not found at any of: ${candidates.join(", ")}`));
31281
31412
  return;
31282
31413
  }
31283
- const hash2 = createHash5("sha256");
31414
+ const hash2 = createHash6("sha256");
31284
31415
  const stream = createReadStream(pathToHash);
31285
31416
  stream.on("error", reject);
31286
31417
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -31303,7 +31434,7 @@ function installedBinaryPath(spec) {
31303
31434
  return null;
31304
31435
  }
31305
31436
  function sha256OfFileSync(path3) {
31306
- return createHash5("sha256").update(readFileSync13(path3)).digest("hex");
31437
+ return createHash6("sha256").update(readFileSync14(path3)).digest("hex");
31307
31438
  }
31308
31439
  function quarantineCachedNpmInstall(spec, reason) {
31309
31440
  const packageDir = lspPackageDir(spec.npm);
@@ -31388,7 +31519,7 @@ function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
31388
31519
 
31389
31520
  // src/lsp-github-install.ts
31390
31521
  import { execFileSync as execFileSync2 } from "node:child_process";
31391
- import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
31522
+ import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
31392
31523
  import {
31393
31524
  copyFileSync as copyFileSync4,
31394
31525
  createReadStream as createReadStream2,
@@ -31397,7 +31528,7 @@ import {
31397
31528
  lstatSync as lstatSync2,
31398
31529
  mkdirSync as mkdirSync9,
31399
31530
  readdirSync as readdirSync4,
31400
- readFileSync as readFileSync14,
31531
+ readFileSync as readFileSync15,
31401
31532
  readlinkSync as readlinkSync2,
31402
31533
  realpathSync as realpathSync3,
31403
31534
  renameSync as renameSync8,
@@ -31531,7 +31662,7 @@ function readGithubInstalledMetaIn(installDir) {
31531
31662
  const path3 = join18(installDir, INSTALLED_META_FILE2);
31532
31663
  if (!statSync8(path3).isFile())
31533
31664
  return null;
31534
- const parsed = JSON.parse(readFileSync14(path3, "utf8"));
31665
+ const parsed = JSON.parse(readFileSync15(path3, "utf8"));
31535
31666
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
31536
31667
  return null;
31537
31668
  return {
@@ -31564,7 +31695,7 @@ var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
31564
31695
  var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
31565
31696
  function sha256OfFile(path3) {
31566
31697
  return new Promise((resolve9, reject) => {
31567
- const hash2 = createHash6("sha256");
31698
+ const hash2 = createHash7("sha256");
31568
31699
  const stream = createReadStream2(path3);
31569
31700
  stream.on("error", reject);
31570
31701
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -31572,7 +31703,7 @@ function sha256OfFile(path3) {
31572
31703
  });
31573
31704
  }
31574
31705
  function sha256OfFileSync2(path3) {
31575
- return createHash6("sha256").update(readFileSync14(path3)).digest("hex");
31706
+ return createHash7("sha256").update(readFileSync15(path3)).digest("hex");
31576
31707
  }
31577
31708
  async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
31578
31709
  const candidates = [];
@@ -32351,7 +32482,7 @@ import {
32351
32482
  existsSync as existsSync15,
32352
32483
  mkdirSync as mkdirSync10,
32353
32484
  readdirSync as readdirSync5,
32354
- readFileSync as readFileSync15,
32485
+ readFileSync as readFileSync16,
32355
32486
  renameSync as renameSync9,
32356
32487
  unlinkSync as unlinkSync8,
32357
32488
  writeFileSync as writeFileSync11
@@ -32531,7 +32662,7 @@ class AftRpcServer {
32531
32662
  if (filePath === this.portFilePath)
32532
32663
  continue;
32533
32664
  try {
32534
- const record2 = parseRpcPortRecord(readFileSync15(filePath, "utf-8"));
32665
+ const record2 = parseRpcPortRecord(readFileSync16(filePath, "utf-8"));
32535
32666
  if (record2 === null) {
32536
32667
  unlinkSync8(filePath);
32537
32668
  continue;
@@ -33561,17 +33692,18 @@ var SUPPORTED_LANGS = [
33561
33692
  "rust",
33562
33693
  "go",
33563
33694
  "pascal",
33564
- "r"
33695
+ "r",
33696
+ "objc"
33565
33697
  ];
33566
33698
  function astTools(ctx) {
33567
33699
  const searchTool = {
33568
- description: `Search code patterns across filesystem using AST-aware matching. Supports 8 languages.
33700
+ description: `Search code patterns across filesystem using AST-aware matching. Supports 9 languages.
33569
33701
 
33570
33702
  ` + `Use meta-variables: $VAR matches a single AST node, $$$ matches multiple nodes (variadic).
33571
33703
  ` + `IMPORTANT: Patterns must be complete AST nodes (valid code fragments).
33572
33704
  ` + `For functions, include params and body: 'export async function $NAME($$$) { $$$ }' not just 'export async function $NAME'.
33573
33705
 
33574
- ` + "Examples: pattern='console.log($MSG)' lang='typescript', pattern='async function $NAME($$$) { $$$ }' lang='javascript', pattern='def $FUNC($$$): $$$' lang='python', pattern='Writeln($MSG);' lang='pascal', pattern='$X <- $Y' lang='r'",
33706
+ ` + "Examples: pattern='console.log($MSG)' lang='typescript', pattern='async function $NAME($$$) { $$$ }' lang='javascript', pattern='def $FUNC($$$): $$$' lang='python', pattern='Writeln($MSG);' lang='pascal', pattern='$X <- $Y' lang='r', pattern='[obj foo:$ARG]' lang='objc'",
33575
33707
  args: {
33576
33708
  pattern: z3.string().describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."),
33577
33709
  lang: z3.enum(SUPPORTED_LANGS).describe("Target language"),
@@ -33734,14 +33866,14 @@ function resolveForegroundWaitMs(configured) {
33734
33866
  }
33735
33867
  return configured;
33736
33868
  }
33737
- function orchestratedTransportTimeoutMs(blockToCompletion, effectiveTimeout, foregroundWaitMs) {
33738
- const waitBudget = blockToCompletion ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : foregroundWaitMs;
33869
+ function orchestratedTransportTimeoutMs(blockToCompletion, wait, effectiveTimeout, foregroundWaitMs) {
33870
+ const waitBudget = blockToCompletion || wait ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : foregroundWaitMs;
33739
33871
  return waitBudget + BASH_TRANSPORT_MARGIN_MS;
33740
33872
  }
33741
33873
  function bashToolDescription(aftSearchRegistered, compressionOn, backgroundOn) {
33742
33874
  const searchSteer = aftSearchRegistered ? "use aft_search (concepts, identifiers, regex, literals), read, aft_outline, or aft_zoom instead" : "use the grep tool, read, aft_outline, or aft_zoom instead";
33743
33875
  const compression = compressionOn ? " Output is compressed by default; pass compressed: false for raw output. Piped commands run verbatim and show the pipeline's output; for AFT's test/build summary, run the runner without | head, | tail, or | grep." : "";
33744
- const tasks = backgroundOn ? ' Commands run in the foreground and return inline; a long-running one auto-promotes to background and delivers a completion reminder when it finishes so for the common "I am waiting on this result" case, just run it and wait, no flags needed. Use background: true yourself ONLY when you have other useful work to do while it runs; then bash_watch waits on the task (sync blocks until exit/pattern, async notifies) and bash_status peeks at it — never background a command and immediately bash_watch it (that wastes a turn for what foreground returns in one), and never loop bash_status to wait. pty: true runs interactive programs (REPLs, TUIs), implies background, and is driven with bash_status({ outputMode: "screen" }) plus bash_write.' : " Commands run in the foreground to completion; timeout is the hard kill cap (default 30 minutes).";
33876
+ const tasks = backgroundOn ? ' Commands run in the foreground and return inline; wait: true blocks until a long command finishes instead of auto-promoting use it when you need the result before doing anything else; keep it off otherwise so auto-promote can remind you while you work. Use background: true yourself ONLY when you have other useful work to do while it runs; then bash_watch waits on the task (sync blocks until exit/pattern, async notifies) and bash_status peeks at it — never background a command and immediately bash_watch it (that wastes a turn for what foreground returns in one), and never loop bash_status to wait. pty: true runs interactive programs (REPLs, TUIs), implies background, and is driven with bash_status({ outputMode: "screen" }) plus bash_write.' : " Commands run in the foreground to completion; timeout is the hard kill cap (default 30 minutes).";
33745
33877
  return `Execute shell commands.${compression}${tasks}
33746
33878
 
33747
33879
  DO NOT use bash for code search or code exploration. If you are about to run grep, rg, sed, awk, find, or cat through bash to locate or read code: STOP — ${searchSteer}.`;
@@ -33807,6 +33939,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
33807
33939
  timeout: optionalInt(1, Number.MAX_SAFE_INTEGER).describe(initialBashCfg.background ? "Hard kill cap in milliseconds (positive integer). When omitted, the task can run up to 30 minutes. Foreground bash returns inline if the command finishes within ~8s (configurable via bash.foreground_wait_window_ms); otherwise it's automatically promoted to background and a completion reminder is delivered when the task actually finishes." : "Hard kill cap in milliseconds (positive integer). When omitted, the foreground command can run up to 30 minutes and returns inline when it finishes."),
33808
33940
  workdir: z4.string().optional().describe("Working directory for command execution. Relative paths resolve through the bridge; defaults to the current tool context/project root when omitted."),
33809
33941
  description: z4.string().optional().describe("Short 5-10 word human-readable summary shown in OpenCode UI metadata instead of raw shell syntax."),
33942
+ wait: z4.boolean().optional().describe("When true, run in the foreground without auto-promoting and wait until the command finishes or reaches its timeout. Use only when you know the result is required before doing anything else."),
33810
33943
  ...backgroundFlagArg,
33811
33944
  compressed: z4.boolean().optional().describe("When true or omitted, return compressed output with noisy terminal control sequences reduced. Set to false for raw output."),
33812
33945
  ...ptyArgs
@@ -33826,21 +33959,30 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
33826
33959
  const cwd = args2.workdir ?? context.directory;
33827
33960
  const isSubagent = await resolveIsSubagent(ctx.client, context.sessionID, context.directory);
33828
33961
  const backgroundDisabled = !bashCfg.background;
33829
- const requestedPty = !backgroundDisabled && coerceBoolean(args2.pty);
33830
- const requestedBackground = !backgroundDisabled && (coerceBoolean(args2.background) || requestedPty);
33962
+ const requestedWait = coerceBoolean(args2.wait);
33963
+ const rawRequestedPty = coerceBoolean(args2.pty);
33964
+ const rawRequestedBackground = coerceBoolean(args2.background);
33965
+ if (requestedWait && rawRequestedPty) {
33966
+ throw new Error("wait:true cannot be used with pty:true because PTY sessions run in background.");
33967
+ }
33968
+ if (requestedWait && rawRequestedBackground) {
33969
+ throw new Error("wait:true cannot be used with background:true.");
33970
+ }
33971
+ const requestedPty = !backgroundDisabled && rawRequestedPty;
33972
+ const requestedBackground = !backgroundDisabled && (rawRequestedBackground || requestedPty);
33831
33973
  if (requestedPty && isSubagent) {
33832
33974
  throw new Error("PTY mode is not available in subagent sessions; subagents cannot drive interactive terminals.");
33833
33975
  }
33834
33976
  const allowSubagentBg = bashCfg.subagent_background;
33835
33977
  const subagentForcedForeground = isSubagent && !allowSubagentBg;
33836
- const blockToCompletion = subagentForcedForeground || backgroundDisabled;
33978
+ const blockToCompletion = subagentForcedForeground || backgroundDisabled || requestedWait;
33837
33979
  const effectiveBackground = blockToCompletion ? false : requestedBackground;
33838
33980
  const rawTimeout = coerceOptionalInt(args2.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
33839
33981
  const ptyRows = coerceOptionalInt(args2.ptyRows, "ptyRows", 1, 60);
33840
33982
  const ptyCols = coerceOptionalInt(args2.ptyCols, "ptyCols", 1, 140);
33841
33983
  const compressed = coerceBoolean(args2.compressed, true);
33842
33984
  const foregroundWaitMs = resolveForegroundWaitMs(bashCfg.foreground_wait_window_ms);
33843
- const effectiveTimeout = effectiveBackground || backgroundDisabled ? rawTimeout : resolveBashKillTimeout(rawTimeout, foregroundWaitMs);
33985
+ const effectiveTimeout = requestedWait || effectiveBackground || backgroundDisabled ? rawTimeout : resolveBashKillTimeout(rawTimeout, foregroundWaitMs);
33844
33986
  if (subagentForcedForeground && requestedBackground) {
33845
33987
  sessionLog(context.sessionID, "[bash] subagent + background:true → converting to foreground (subagent would lose task_id)");
33846
33988
  }
@@ -33859,9 +34001,10 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
33859
34001
  pty_cols: ptyCols,
33860
34002
  permissions_requested: true,
33861
34003
  foreground_orchestrate: true,
33862
- block_to_completion: blockToCompletion
34004
+ block_to_completion: blockToCompletion,
34005
+ wait: requestedWait
33863
34006
  }, callBashBridge, {
33864
- transportTimeoutMs: orchestratedTransportTimeoutMs(blockToCompletion, effectiveTimeout, foregroundWaitMs),
34007
+ transportTimeoutMs: orchestratedTransportTimeoutMs(blockToCompletion, requestedWait, effectiveTimeout, foregroundWaitMs),
33865
34008
  onProgress: ({ text }) => {
33866
34009
  accumulatedOutput = preview(accumulatedOutput + text);
33867
34010
  metadata?.({ output: accumulatedOutput, description });
@@ -35319,11 +35462,11 @@ function createInspectTier2IdleScheduler(options) {
35319
35462
  }
35320
35463
  function inspectTools(ctx) {
35321
35464
  const inspectTool = {
35322
- description: "Codebase health snapshot. One call returns summary stats for: TODOs, diagnostics, file/symbol metrics, dead code, unused exports, code duplicates. Pass `sections` for per-category drill-down details.\n\n" + "Categories run in tiers — Tier 1 (todos, metrics) return synchronously from cache. Tier 2 (dead_code, unused_exports, duplicates) waits for a fresh reuse scan up to a short deadline; if a category is still scanning the response reports `complete: false` with `pending_categories: [...]` rather than a fabricated clean count.\n\n" + `Use when: starting work on unfamiliar code, after multi-edit batches to check diagnostics, before a refactor, before review, or to verify cleanup completeness.
35465
+ description: "Codebase health snapshot. One call returns summary stats for: TODOs, diagnostics, file/symbol metrics, dead code, unused exports, code duplicates, and TS/JS import cycles. Pass `sections` for per-category drill-down details.\n\n" + "Categories run in tiers — Tier 1 (todos, metrics) return synchronously from cache. Tier 2 (dead_code, unused_exports, duplicates, cycles) waits for a fresh reuse scan up to a short deadline; if a category is still scanning the response reports `complete: false` with `pending_categories: [...]` rather than a fabricated clean count. Rust module cycles are out of scope for `cycles`.\n\n" + `Use when: starting work on unfamiliar code, after multi-edit batches to check diagnostics, before a refactor, before review, or to verify cleanup completeness.
35323
35466
 
35324
35467
  ` + "Treat `dead_code` as a hint, not proof: reachability is call-based, so symbols reached only via method dispatch or referenced only in type position may be false positives — verify before deleting.",
35325
35468
  args: {
35326
- sections: arg(z10.union([z10.string(), z10.array(z10.string())]).optional().describe("Categories to include in detailed drill-down (e.g. 'todos' or ['todos', 'dead_code']). Use 'all' for every active category. Omit for summary-only mode.")),
35469
+ sections: arg(z10.union([z10.string(), z10.array(z10.string())]).optional().describe("Categories to include in detailed drill-down (e.g. 'todos' or ['todos', 'dead_code', 'cycles']). Use 'all' for every active category. Omit for summary-only mode.")),
35327
35470
  scope: arg(z10.union([z10.string(), z10.array(z10.string())]).optional().describe("Restrict scan/results to paths under this scope (file or directory, absolute or relative to project root). Tier 1 scopes the scan; Tier 2 scans project-wide and applies scope as a result filter.")),
35328
35471
  topK: arg(z10.number().int().positive().max(100).optional().describe("Max drill-down items per category. Default 20, max 100."))
35329
35472
  },
@@ -36235,7 +36378,7 @@ function buildWorkflowHints(opts) {
36235
36378
  }
36236
36379
  if (hasBash && hasBgBash) {
36237
36380
  sections.push([
36238
- `**Long-running commands** (builds, installs, full test suites): run them in the FOREGROUND — \`${bashName}({ command })\` waits and returns the result in ONE step, and if it outlives the short wait window it auto-promotes to background and delivers a completion reminder when it finishes. Don't reach for \`background: true\` for the common "I'm waiting on this result" case — that costs an extra turn.`,
36381
+ `**Long-running commands** (builds, installs, full test suites): run them in the FOREGROUND — use \`${bashName}({ command, wait: true })\` when you know it is long and need the result before anything else; otherwise omit \`wait\` so auto-promote can hand you a reminder while you work.`,
36239
36382
  "- `background: true` is ONLY for when you have OTHER useful work to do while it runs: start it, do the other work, and the completion reminder delivers the result (or spawn a subagent for the side work). Do NOT background a command and then immediately `bash_watch` it — that spends a whole extra turn waiting for something foreground returns in one.",
36240
36383
  "- `bash_watch` is for blocking on an ALREADY-backgrounded task once you've run out of parallel work (sync — the user can interrupt), or reacting to a specific early output line (async: background:true + pattern). Never loop `bash_status` to wait — it's a one-shot inspector."
36241
36384
  ].join(`
@@ -36292,12 +36435,13 @@ var PLUGIN_VERSION = (() => {
36292
36435
  return "0.0.0";
36293
36436
  }
36294
36437
  })();
36295
- var ANNOUNCEMENT_VERSION = "0.43.0";
36438
+ var ANNOUNCEMENT_VERSION = "0.45.0";
36296
36439
  var ANNOUNCEMENT_FEATURES = [
36297
- "Fewer permission prompts: system temp paths (/tmp and friends) no longer trigger external-directory asks, and aft_search now has its own permission id so a grep deny rule can't block it.",
36298
- `New enabled config toggle: set "enabled": false in a project's .cortexkit/aft.jsonc (or your user config) to turn AFT off there entirely.`,
36299
- "Bash compression honesty fix: empty successful output stays empty instead of being replaced with synthesized text like 'kubectl: no resources found'.",
36300
- "TUI sidebar now updates over a push channel instead of polling (lower idle CPU), and AFT no longer re-adds itself to tui.json if you remove it (aft setup re-registers)."
36440
+ "Code Health verified against 12 real repositories: dead code is only reported for languages we can actually analyze (Java/Kotlin now say 'not analyzed' instead of guessing), and generated code (gen/ trees, *_pb.ts, DO NOT EDIT banners) moves out of the headline counts into its own bucket.",
36441
+ "Config files (playwright/eslint/tsup/vitest *.config.ts), Storybook stories, Mocha hooks, and SvelteKit hooks/$lib imports are no longer flagged as dead code.",
36442
+ "Rust module aliases, inline-module calls, and cross-crate pub use re-exports now resolve; Go interface methods (sort.Interface, Bubble Tea) are no longer flagged dead.",
36443
+ "Fixed npx @cortexkit/aft doctor --fix and setup failing to download the binary on fresh installs.",
36444
+ "Bridges now detect an updated aft binary on disk and respawn onto it automatically."
36301
36445
  ];
36302
36446
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
36303
36447
  var plugin = async (input) => initializePluginForDirectory(input);