@cortexkit/aft-opencode 0.44.0 → 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.
Files changed (3) hide show
  1. package/dist/index.js +202 -82
  2. package/dist/tui.js +139 -19
  3. package/package.json +8 -8
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.1/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.1/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.1/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.1/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.1/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) => {
@@ -11814,9 +11878,14 @@ class SubcSocket {
11814
11878
  }
11815
11879
  }
11816
11880
 
11817
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/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");
11818
11883
  var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
11819
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;
11820
11889
  var BODY_READ_TIMEOUT_MS = 30000;
11821
11890
  var EMPTY_BODY = new Uint8Array(0);
11822
11891
  var DEFAULT_MANAGED_TARGET_KIND = "management_surface";
@@ -11860,6 +11929,7 @@ class SubcClient {
11860
11929
  closeStarted = false;
11861
11930
  reconnecting = null;
11862
11931
  generation = 1;
11932
+ readerActive = false;
11863
11933
  constructor(sock, currentConn, opts) {
11864
11934
  this.sock = sock;
11865
11935
  this.currentConn = currentConn;
@@ -11918,7 +11988,7 @@ class SubcClient {
11918
11988
  }
11919
11989
  continue;
11920
11990
  }
11921
- if (err.kind === "outcome_unknown") {
11991
+ if (err.kind === "outcome_unknown" && err.code !== DEADLINE_NO_DROP_CODE) {
11922
11992
  this.scheduleReconnectAfterDrop(err);
11923
11993
  }
11924
11994
  throw err;
@@ -12041,7 +12111,7 @@ class SubcClient {
12041
12111
  timer: null
12042
12112
  };
12043
12113
  pending.timer = setTimeout(() => {
12044
- this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms)));
12114
+ this.arbitrateTimeout(key, pending, channel, corr, ms);
12045
12115
  }, ms);
12046
12116
  this.pending.set(key, pending);
12047
12117
  this.sock.write(encodeFrame(frame), Date.now() + ms).catch((err) => {
@@ -12051,6 +12121,23 @@ class SubcClient {
12051
12121
  });
12052
12122
  });
12053
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
+ }
12054
12141
  async managedRequest(routeChannel, body, opts) {
12055
12142
  const bytes = body instanceof Uint8Array ? body : this.encode(body);
12056
12143
  const priority = opts.priority ?? 1 /* Interactive */;
@@ -12075,6 +12162,9 @@ class SubcClient {
12075
12162
  if (!handedToSocket) {
12076
12163
  return this.notSentCallError("request bytes were not queued to the subc socket", err);
12077
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
+ }
12078
12168
  return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", err);
12079
12169
  };
12080
12170
  return new Promise((resolve7, reject) => {
@@ -12088,7 +12178,7 @@ class SubcClient {
12088
12178
  classifyFailure
12089
12179
  };
12090
12180
  pending.timer = setTimeout(() => {
12091
- this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms)));
12181
+ this.arbitrateTimeout(key, pending, channel, corr, ms);
12092
12182
  }, ms);
12093
12183
  this.pending.set(key, pending);
12094
12184
  const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
@@ -12133,6 +12223,9 @@ class SubcClient {
12133
12223
  return cached.opening;
12134
12224
  }
12135
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;
12136
12229
  for (;; ) {
12137
12230
  if (cached.closed)
12138
12231
  throw this.routeClosedDuringOpen();
@@ -12166,6 +12259,15 @@ class SubcClient {
12166
12259
  }
12167
12260
  continue;
12168
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
+ }
12169
12271
  throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`, err);
12170
12272
  }
12171
12273
  }
@@ -12259,9 +12361,16 @@ class SubcClient {
12259
12361
  try {
12260
12362
  for (;; ) {
12261
12363
  const headerBytes = await sock.readExact(HEADER_LEN, Number.POSITIVE_INFINITY);
12262
- const header = decodeHeader(headerBytes);
12263
- const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS);
12264
- 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
+ }
12265
12374
  }
12266
12375
  } catch (err) {
12267
12376
  if (this.sock === sock && this.generation === generation) {
@@ -12293,13 +12402,20 @@ class SubcClient {
12293
12402
  this.failChannel(frame.header.channel, new SubcError("route closed by subc (GOODBYE)"));
12294
12403
  return;
12295
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
+ }
12296
12409
  }
12297
12410
  settle(key, pending, run) {
12411
+ if (this.pending.get(key) !== pending)
12412
+ return false;
12298
12413
  this.pending.delete(key);
12299
12414
  if (pending.timer)
12300
12415
  clearTimeout(pending.timer);
12301
12416
  run();
12302
12417
  pending.onSettle?.();
12418
+ return true;
12303
12419
  }
12304
12420
  rejectPending(key, pending, err) {
12305
12421
  this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
@@ -12363,6 +12479,9 @@ function isConsumerReconnectTransient(err) {
12363
12479
  const code = errorCode(err);
12364
12480
  return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
12365
12481
  }
12482
+ function isRetryableRouteOpenCode(code) {
12483
+ return code === "unknown_module" || code === "module_reloading" || code === "target_unavailable" || code === "module_timeout";
12484
+ }
12366
12485
  async function connectionFileExists(path2) {
12367
12486
  try {
12368
12487
  await fs2.access(path2);
@@ -12378,7 +12497,8 @@ function normalizeConnectOptions(opts) {
12378
12497
  identity: opts.identity,
12379
12498
  targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
12380
12499
  reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
12381
- 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
12382
12502
  };
12383
12503
  }
12384
12504
  function routeCacheKey(target, identity, consumerIdentity) {
@@ -12407,7 +12527,7 @@ function causeMessage(cause) {
12407
12527
  return "";
12408
12528
  return `: ${cause instanceof Error ? cause.message : String(cause)}`;
12409
12529
  }
12410
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/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
12411
12531
  import { Buffer as Buffer2 } from "node:buffer";
12412
12532
  var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
12413
12533
  var BODY_READ_TIMEOUT_MS2 = 30000;
@@ -13436,7 +13556,7 @@ async function createAftTransportPool(opts) {
13436
13556
  return new BridgePool(opts.binaryPath, opts.poolOptions, opts.configOverrides);
13437
13557
  }
13438
13558
  // src/bg-notifications.ts
13439
- import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
13559
+ import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypto";
13440
13560
 
13441
13561
  // src/logger.ts
13442
13562
  import * as fs3 from "node:fs";
@@ -13600,7 +13720,7 @@ async function resolvePromptContext(client, sessionId) {
13600
13720
 
13601
13721
  // src/bg-notifications.ts
13602
13722
  function hashReminder(text) {
13603
- return createHash4("sha256").update(text).digest("hex").slice(0, 16);
13723
+ return createHash5("sha256").update(text).digest("hex").slice(0, 16);
13604
13724
  }
13605
13725
  var CONSUMED_TASKIDS_CAP = 256;
13606
13726
  var DELIVERED_AWAITING_ACK_CAP = 4096;
@@ -14364,7 +14484,7 @@ function formatDuration(completion) {
14364
14484
  }
14365
14485
 
14366
14486
  // src/config.ts
14367
- 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";
14368
14488
  var import_comment_json = __toESM(require_src2(), 1);
14369
14489
 
14370
14490
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
@@ -28956,7 +29076,7 @@ function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 })
28956
29076
  let tmpPath = null;
28957
29077
  let oldKeys = [];
28958
29078
  try {
28959
- const content = readFileSync6(configPath, "utf-8");
29079
+ const content = readFileSync7(configPath, "utf-8");
28960
29080
  const rawConfig = import_comment_json.parse(content);
28961
29081
  if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
28962
29082
  return { migrated: false, oldKeys: [] };
@@ -29033,7 +29153,7 @@ function loadConfigFromPath(configPath) {
29033
29153
  if (!existsSync7(configPath)) {
29034
29154
  return null;
29035
29155
  }
29036
- const content = readFileSync6(configPath, "utf-8");
29156
+ const content = readFileSync7(configPath, "utf-8");
29037
29157
  const rawConfig = import_comment_json.parse(content);
29038
29158
  migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
29039
29159
  const cleanConfig = stripJsoncSymbols(rawConfig);
@@ -29301,7 +29421,7 @@ function loadAftConfig(projectDirectory) {
29301
29421
  }
29302
29422
 
29303
29423
  // src/notifications.ts
29304
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
29424
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
29305
29425
  import { homedir as homedir9, platform } from "node:os";
29306
29426
  import { join as join11 } from "node:path";
29307
29427
  function isTuiMode() {
@@ -29344,7 +29464,7 @@ function readDesktopState() {
29344
29464
  return { serverUrl: null };
29345
29465
  }
29346
29466
  try {
29347
- const raw = readFileSync7(statePath, "utf-8");
29467
+ const raw = readFileSync8(statePath, "utf-8");
29348
29468
  const state = JSON.parse(raw);
29349
29469
  let serverUrl = null;
29350
29470
  const serverStr = state.server;
@@ -29843,7 +29963,7 @@ import {
29843
29963
  existsSync as existsSync11,
29844
29964
  mkdirSync as mkdirSync6,
29845
29965
  openSync as openSync5,
29846
- readFileSync as readFileSync10,
29966
+ readFileSync as readFileSync11,
29847
29967
  renameSync as renameSync6,
29848
29968
  rmSync as rmSync5,
29849
29969
  writeFileSync as writeFileSync7
@@ -29852,14 +29972,14 @@ import { dirname as dirname7 } from "node:path";
29852
29972
 
29853
29973
  // src/hooks/auto-update-checker/cache.ts
29854
29974
  import { spawn as spawn2 } from "node:child_process";
29855
- 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";
29856
29976
  import { tmpdir as tmpdir3 } from "node:os";
29857
29977
  import { basename as basename3, dirname as dirname6, join as join14 } from "node:path";
29858
29978
  var import_comment_json3 = __toESM(require_src2(), 1);
29859
29979
 
29860
29980
  // src/hooks/auto-update-checker/checker.ts
29861
29981
  var import_comment_json2 = __toESM(require_src2(), 1);
29862
- 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";
29863
29983
  import { homedir as homedir11 } from "node:os";
29864
29984
  import { dirname as dirname5, isAbsolute as isAbsolute6, join as join13, resolve as resolve7 } from "node:path";
29865
29985
  import { fileURLToPath } from "node:url";
@@ -29965,7 +30085,7 @@ function getLocalDevPath(directory) {
29965
30085
  try {
29966
30086
  if (!existsSync9(configPath))
29967
30087
  continue;
29968
- const rawConfig = parseJsonConfig(readFileSync8(configPath, "utf-8"));
30088
+ const rawConfig = parseJsonConfig(readFileSync9(configPath, "utf-8"));
29969
30089
  const plugins = getPluginEntries(rawConfig);
29970
30090
  for (const entry of plugins) {
29971
30091
  if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
@@ -29975,7 +30095,7 @@ function getLocalDevPath(directory) {
29975
30095
  const pkgPath = findPackageJsonUp(localPath);
29976
30096
  if (!pkgPath)
29977
30097
  continue;
29978
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
30098
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(pkgPath, "utf-8")));
29979
30099
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
29980
30100
  return localPath;
29981
30101
  }
@@ -29992,7 +30112,7 @@ function findPackageJsonUp(startPath) {
29992
30112
  const pkgPath = join13(dir, "package.json");
29993
30113
  if (existsSync9(pkgPath)) {
29994
30114
  try {
29995
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
30115
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(pkgPath, "utf-8")));
29996
30116
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
29997
30117
  return pkgPath;
29998
30118
  } catch {}
@@ -30013,7 +30133,7 @@ function getLocalDevVersion(directory) {
30013
30133
  const pkgPath = findPackageJsonUp(localPath);
30014
30134
  if (!pkgPath)
30015
30135
  return null;
30016
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
30136
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(pkgPath, "utf-8")));
30017
30137
  return pkg.success ? pkg.data.version ?? null : null;
30018
30138
  } catch {
30019
30139
  return null;
@@ -30032,7 +30152,7 @@ function findPluginEntry(directory) {
30032
30152
  try {
30033
30153
  if (!existsSync9(configPath))
30034
30154
  continue;
30035
- const rawConfig = parseJsonConfig(readFileSync8(configPath, "utf-8"));
30155
+ const rawConfig = parseJsonConfig(readFileSync9(configPath, "utf-8"));
30036
30156
  const plugins = getPluginEntries(rawConfig);
30037
30157
  for (const entry of plugins) {
30038
30158
  if (entry === PACKAGE_NAME) {
@@ -30065,7 +30185,7 @@ function getCachedVersion(spec) {
30065
30185
  try {
30066
30186
  if (!existsSync9(packageJsonPath))
30067
30187
  continue;
30068
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(packageJsonPath, "utf-8")));
30188
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(packageJsonPath, "utf-8")));
30069
30189
  if (pkg.success && pkg.data.version) {
30070
30190
  if (!spec)
30071
30191
  cachedPackageVersion = pkg.data.version;
@@ -30115,9 +30235,9 @@ function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
30115
30235
  cpSync(packageDir, stagedPackageDir, { recursive: true });
30116
30236
  return {
30117
30237
  packageJsonPath,
30118
- packageJson: existsSync10(packageJsonPath) ? readFileSync9(packageJsonPath, "utf-8") : null,
30238
+ packageJson: existsSync10(packageJsonPath) ? readFileSync10(packageJsonPath, "utf-8") : null,
30119
30239
  lockfilePath,
30120
- lockfile: existsSync10(lockfilePath) ? readFileSync9(lockfilePath, "utf-8") : null,
30240
+ lockfile: existsSync10(lockfilePath) ? readFileSync10(lockfilePath, "utf-8") : null,
30121
30241
  packageDir,
30122
30242
  stagedPackageDir,
30123
30243
  tempDir
@@ -30155,7 +30275,7 @@ function removeFromPackageLock(installDir, packageName) {
30155
30275
  if (!existsSync10(lockPath))
30156
30276
  return false;
30157
30277
  try {
30158
- const lock = import_comment_json3.parse(readFileSync9(lockPath, "utf-8"));
30278
+ const lock = import_comment_json3.parse(readFileSync10(lockPath, "utf-8"));
30159
30279
  let modified = false;
30160
30280
  if (lock.packages) {
30161
30281
  const key = `node_modules/${packageName}`;
@@ -30181,7 +30301,7 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
30181
30301
  if (!existsSync10(packageJsonPath))
30182
30302
  return false;
30183
30303
  try {
30184
- const raw = import_comment_json3.parse(readFileSync9(packageJsonPath, "utf-8"));
30304
+ const raw = import_comment_json3.parse(readFileSync10(packageJsonPath, "utf-8"));
30185
30305
  const pkgJson = PackageJsonSchema.safeParse(raw);
30186
30306
  if (!pkgJson.success)
30187
30307
  return false;
@@ -30435,7 +30555,7 @@ function hasRecentCheckTimestamp(file2, intervalMs) {
30435
30555
  if (!existsSync11(file2))
30436
30556
  return false;
30437
30557
  try {
30438
- const raw = JSON.parse(readFileSync10(file2, "utf-8"));
30558
+ const raw = JSON.parse(readFileSync11(file2, "utf-8"));
30439
30559
  const last = typeof raw.lastCheckedMs === "number" ? raw.lastCheckedMs : 0;
30440
30560
  return Number.isFinite(last) && Date.now() - last < intervalMs;
30441
30561
  } catch {
@@ -30539,12 +30659,12 @@ function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
30539
30659
 
30540
30660
  // src/lsp-auto-install.ts
30541
30661
  import { spawn as spawn3 } from "node:child_process";
30542
- import { createHash as createHash5 } from "node:crypto";
30662
+ import { createHash as createHash6 } from "node:crypto";
30543
30663
  import {
30544
30664
  createReadStream,
30545
30665
  existsSync as existsSync13,
30546
30666
  mkdirSync as mkdirSync8,
30547
- readFileSync as readFileSync13,
30667
+ readFileSync as readFileSync14,
30548
30668
  renameSync as renameSync7,
30549
30669
  rmSync as rmSync6,
30550
30670
  statSync as statSync7,
@@ -30557,7 +30677,7 @@ import {
30557
30677
  closeSync as closeSync6,
30558
30678
  mkdirSync as mkdirSync7,
30559
30679
  openSync as openSync6,
30560
- readFileSync as readFileSync11,
30680
+ readFileSync as readFileSync12,
30561
30681
  statSync as statSync6,
30562
30682
  unlinkSync as unlinkSync6,
30563
30683
  writeFileSync as writeFileSync8
@@ -30621,7 +30741,7 @@ function readInstalledMetaIn(installDir) {
30621
30741
  try {
30622
30742
  if (!statSync6(path3).isFile())
30623
30743
  return null;
30624
- const raw = readFileSync11(path3, "utf8");
30744
+ const raw = readFileSync12(path3, "utf8");
30625
30745
  const parsed = JSON.parse(raw);
30626
30746
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
30627
30747
  return null;
@@ -30671,7 +30791,7 @@ ${new Date().toISOString()}
30671
30791
  let owningPid = null;
30672
30792
  let lockMtimeMs = 0;
30673
30793
  try {
30674
- const raw = readFileSync11(lock, "utf8");
30794
+ const raw = readFileSync12(lock, "utf8");
30675
30795
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
30676
30796
  const parsed = Number.parseInt(firstLine, 10);
30677
30797
  if (Number.isFinite(parsed) && parsed > 0)
@@ -30709,7 +30829,7 @@ function releaseInstallLock(lockKey) {
30709
30829
  try {
30710
30830
  let owningPid = null;
30711
30831
  try {
30712
- const raw = readFileSync11(lock, "utf8");
30832
+ const raw = readFileSync12(lock, "utf8");
30713
30833
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
30714
30834
  const parsed = Number.parseInt(firstLine, 10);
30715
30835
  if (Number.isFinite(parsed) && parsed > 0)
@@ -30750,7 +30870,7 @@ var VERSION_CHECK_FILE = ".aft-version-check";
30750
30870
  function readVersionCheck(npmPackage) {
30751
30871
  const file2 = join15(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
30752
30872
  try {
30753
- const raw = readFileSync11(file2, "utf8");
30873
+ const raw = readFileSync12(file2, "utf8");
30754
30874
  const parsed = JSON.parse(raw);
30755
30875
  if (typeof parsed.last_checked === "string") {
30756
30876
  return {
@@ -30927,7 +31047,7 @@ var NPM_LSP_TABLE = [
30927
31047
  ];
30928
31048
 
30929
31049
  // src/lsp-project-relevance.ts
30930
- 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";
30931
31051
  import { join as join16 } from "node:path";
30932
31052
  var MAX_WALK_DIRS = 200;
30933
31053
  var MAX_WALK_DEPTH = 4;
@@ -30969,7 +31089,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
30969
31089
  }
30970
31090
  function readPackageJson(projectRoot) {
30971
31091
  try {
30972
- const raw = readFileSync12(join16(projectRoot, "package.json"), "utf8");
31092
+ const raw = readFileSync13(join16(projectRoot, "package.json"), "utf8");
30973
31093
  const parsed = JSON.parse(raw);
30974
31094
  if (typeof parsed !== "object" || parsed === null)
30975
31095
  return null;
@@ -31291,7 +31411,7 @@ function hashInstalledBinary(spec) {
31291
31411
  reject(new Error(`installed binary not found at any of: ${candidates.join(", ")}`));
31292
31412
  return;
31293
31413
  }
31294
- const hash2 = createHash5("sha256");
31414
+ const hash2 = createHash6("sha256");
31295
31415
  const stream = createReadStream(pathToHash);
31296
31416
  stream.on("error", reject);
31297
31417
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -31314,7 +31434,7 @@ function installedBinaryPath(spec) {
31314
31434
  return null;
31315
31435
  }
31316
31436
  function sha256OfFileSync(path3) {
31317
- return createHash5("sha256").update(readFileSync13(path3)).digest("hex");
31437
+ return createHash6("sha256").update(readFileSync14(path3)).digest("hex");
31318
31438
  }
31319
31439
  function quarantineCachedNpmInstall(spec, reason) {
31320
31440
  const packageDir = lspPackageDir(spec.npm);
@@ -31399,7 +31519,7 @@ function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
31399
31519
 
31400
31520
  // src/lsp-github-install.ts
31401
31521
  import { execFileSync as execFileSync2 } from "node:child_process";
31402
- import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
31522
+ import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
31403
31523
  import {
31404
31524
  copyFileSync as copyFileSync4,
31405
31525
  createReadStream as createReadStream2,
@@ -31408,7 +31528,7 @@ import {
31408
31528
  lstatSync as lstatSync2,
31409
31529
  mkdirSync as mkdirSync9,
31410
31530
  readdirSync as readdirSync4,
31411
- readFileSync as readFileSync14,
31531
+ readFileSync as readFileSync15,
31412
31532
  readlinkSync as readlinkSync2,
31413
31533
  realpathSync as realpathSync3,
31414
31534
  renameSync as renameSync8,
@@ -31542,7 +31662,7 @@ function readGithubInstalledMetaIn(installDir) {
31542
31662
  const path3 = join18(installDir, INSTALLED_META_FILE2);
31543
31663
  if (!statSync8(path3).isFile())
31544
31664
  return null;
31545
- const parsed = JSON.parse(readFileSync14(path3, "utf8"));
31665
+ const parsed = JSON.parse(readFileSync15(path3, "utf8"));
31546
31666
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
31547
31667
  return null;
31548
31668
  return {
@@ -31575,7 +31695,7 @@ var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
31575
31695
  var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
31576
31696
  function sha256OfFile(path3) {
31577
31697
  return new Promise((resolve9, reject) => {
31578
- const hash2 = createHash6("sha256");
31698
+ const hash2 = createHash7("sha256");
31579
31699
  const stream = createReadStream2(path3);
31580
31700
  stream.on("error", reject);
31581
31701
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -31583,7 +31703,7 @@ function sha256OfFile(path3) {
31583
31703
  });
31584
31704
  }
31585
31705
  function sha256OfFileSync2(path3) {
31586
- return createHash6("sha256").update(readFileSync14(path3)).digest("hex");
31706
+ return createHash7("sha256").update(readFileSync15(path3)).digest("hex");
31587
31707
  }
31588
31708
  async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
31589
31709
  const candidates = [];
@@ -32362,7 +32482,7 @@ import {
32362
32482
  existsSync as existsSync15,
32363
32483
  mkdirSync as mkdirSync10,
32364
32484
  readdirSync as readdirSync5,
32365
- readFileSync as readFileSync15,
32485
+ readFileSync as readFileSync16,
32366
32486
  renameSync as renameSync9,
32367
32487
  unlinkSync as unlinkSync8,
32368
32488
  writeFileSync as writeFileSync11
@@ -32542,7 +32662,7 @@ class AftRpcServer {
32542
32662
  if (filePath === this.portFilePath)
32543
32663
  continue;
32544
32664
  try {
32545
- const record2 = parseRpcPortRecord(readFileSync15(filePath, "utf-8"));
32665
+ const record2 = parseRpcPortRecord(readFileSync16(filePath, "utf-8"));
32546
32666
  if (record2 === null) {
32547
32667
  unlinkSync8(filePath);
32548
32668
  continue;
@@ -36315,13 +36435,13 @@ var PLUGIN_VERSION = (() => {
36315
36435
  return "0.0.0";
36316
36436
  }
36317
36437
  })();
36318
- var ANNOUNCEMENT_VERSION = "0.44.0";
36438
+ var ANNOUNCEMENT_VERSION = "0.45.0";
36319
36439
  var ANNOUNCEMENT_FEATURES = [
36320
- "Code Health accuracy overhaul: framework entry points (Tauri commands, Next.js/Nuxt/SvelteKit/Remix/Astro routes, NestJS decorators) are no longer flagged dead, barrel re-exports collapse to their real definition, and test-only usage moves out of the headline counts into its own tier.",
36321
- "New: import cycle detection for TS/JS (aft_inspect sections: cycles), and expected_mirrors config to suppress intentional duplicate pairs like plugin/pi-plugin parity trees.",
36322
- "Stale diagnostics fixed: files edited outside AFT (scripts, git) no longer show outdated errors in aft_inspect or the status bar.",
36323
- "bash wait: true blocks until a long command finishes instead of promoting it to background.",
36324
- "Objective-C support (.m/.mm): outline, zoom, AST search, and semantic indexing."
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."
36325
36445
  ];
36326
36446
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
36327
36447
  var plugin = async (input) => initializePluginForDirectory(input);
package/dist/tui.js CHANGED
@@ -7790,10 +7790,10 @@ var require_src2 = __commonJS((exports, module) => {
7790
7790
  // src/tui/index.tsx
7791
7791
  import { createMemo as createMemo2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
7792
7792
  // package.json
7793
- var version = "0.44.0";
7793
+ var version = "0.45.0";
7794
7794
 
7795
7795
  // src/shared/rpc-client.ts
7796
- import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
7796
+ import { existsSync, readdirSync, readFileSync as readFileSync2, unlinkSync } from "node:fs";
7797
7797
  import { join as join3 } from "node:path";
7798
7798
 
7799
7799
  // src/shared/rpc-utils.ts
@@ -7856,6 +7856,8 @@ function error(message, meta) {
7856
7856
  }
7857
7857
  // ../aft-bridge/dist/bridge.js
7858
7858
  import { spawn } from "node:child_process";
7859
+ import { createHash } from "node:crypto";
7860
+ import { readFileSync } from "node:fs";
7859
7861
  import { homedir } from "node:os";
7860
7862
  import { join } from "node:path";
7861
7863
  import { StringDecoder } from "node:string_decoder";
@@ -7912,6 +7914,22 @@ function bashTaskIdFrom(response) {
7912
7914
  return camelCase;
7913
7915
  return;
7914
7916
  }
7917
+ function hashBinaryOnDisk(binaryPath) {
7918
+ try {
7919
+ return createHash("sha256").update(readFileSync(binaryPath)).digest("hex");
7920
+ } catch {
7921
+ return null;
7922
+ }
7923
+ }
7924
+ var binaryFingerprintReader = hashBinaryOnDisk;
7925
+ function readBinaryFingerprint(binaryPath) {
7926
+ try {
7927
+ const fingerprint = binaryFingerprintReader(binaryPath);
7928
+ return typeof fingerprint === "string" && fingerprint.length > 0 ? fingerprint : null;
7929
+ } catch {
7930
+ return null;
7931
+ }
7932
+ }
7915
7933
  function tagStderrLine(line) {
7916
7934
  return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
7917
7935
  }
@@ -8003,6 +8021,9 @@ class BinaryBridge {
8003
8021
  binaryPath;
8004
8022
  cwd;
8005
8023
  process = null;
8024
+ spawnedBinaryFingerprint = null;
8025
+ lastBinaryFingerprintCheckAt = 0;
8026
+ _retiringDueToBinaryChange = false;
8006
8027
  pending = new Map;
8007
8028
  outstandingBackgroundTaskIds = new Set;
8008
8029
  nextId = 1;
@@ -8122,6 +8143,24 @@ class BinaryBridge {
8122
8143
  hasOutstandingBackgroundTasks() {
8123
8144
  return this.outstandingBackgroundTaskIds.size > 0;
8124
8145
  }
8146
+ maybeScheduleRespawnForUpdatedBinary(checkIntervalMs, now = Date.now()) {
8147
+ if (this._shuttingDown || this._retiringDueToBinaryChange || !this.isAlive())
8148
+ return false;
8149
+ if (this.pending.size > 0 || this.outstandingBackgroundTaskIds.size > 0)
8150
+ return false;
8151
+ if (now - this.lastBinaryFingerprintCheckAt < checkIntervalMs)
8152
+ return false;
8153
+ this.lastBinaryFingerprintCheckAt = now;
8154
+ const spawnedFingerprint = this.spawnedBinaryFingerprint;
8155
+ if (!spawnedFingerprint)
8156
+ return false;
8157
+ const currentFingerprint = readBinaryFingerprint(this.binaryPath);
8158
+ if (!currentFingerprint || currentFingerprint === spawnedFingerprint)
8159
+ return false;
8160
+ this._retiringDueToBinaryChange = true;
8161
+ this.logVia(`Binary contents changed on disk for ${this.binaryPath}; retiring this bridge so the next tool call respawns onto the updated binary.`);
8162
+ return true;
8163
+ }
8125
8164
  getCwd() {
8126
8165
  return this.cwd;
8127
8166
  }
@@ -8154,6 +8193,9 @@ class BinaryBridge {
8154
8193
  }
8155
8194
  async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
8156
8195
  try {
8196
+ if (this._retiringDueToBinaryChange) {
8197
+ throw new Error(`${this.errorPrefix} Bridge is retiring after the on-disk binary changed; retry to respawn on the updated binary`);
8198
+ }
8157
8199
  if (this._shuttingDown) {
8158
8200
  throw new Error(`${this.errorPrefix} Bridge is shutting down, cannot send "${command}"`);
8159
8201
  }
@@ -8352,6 +8394,7 @@ class BinaryBridge {
8352
8394
  }
8353
8395
  async shutdown() {
8354
8396
  this._shuttingDown = true;
8397
+ this.spawnedBinaryFingerprint = null;
8355
8398
  this.clearRestartResetTimer();
8356
8399
  this.configureWarningClients.clear();
8357
8400
  this.rejectAllPending(new Error(`${this.errorPrefix} Bridge shutting down`));
@@ -8405,6 +8448,9 @@ class BinaryBridge {
8405
8448
  async replaceCurrentBinary(newBinaryPath) {
8406
8449
  this.binaryPath = newBinaryPath;
8407
8450
  this.configured = false;
8451
+ this.spawnedBinaryFingerprint = null;
8452
+ this.lastBinaryFingerprintCheckAt = 0;
8453
+ this._retiringDueToBinaryChange = false;
8408
8454
  this.clearRestartResetTimer();
8409
8455
  this.rejectAllPending(new Error(`${this.errorPrefix} Bridge restarting with updated binary: ${newBinaryPath}`));
8410
8456
  if (!this.process)
@@ -8430,6 +8476,7 @@ class BinaryBridge {
8430
8476
  this.spawnProcess(triggeringSessionId);
8431
8477
  }
8432
8478
  spawnProcess(triggeringSessionId) {
8479
+ this._retiringDueToBinaryChange = false;
8433
8480
  this.lastStatusBar = undefined;
8434
8481
  if (triggeringSessionId) {
8435
8482
  this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
@@ -8520,6 +8567,8 @@ class BinaryBridge {
8520
8567
  this.handleCrash();
8521
8568
  });
8522
8569
  this.process = child;
8570
+ this.spawnedBinaryFingerprint = readBinaryFingerprint(this.binaryPath);
8571
+ this.lastBinaryFingerprintCheckAt = Date.now();
8523
8572
  this.stdoutBuffer = "";
8524
8573
  this.stdoutReadOffset = 0;
8525
8574
  this.stderrBuffer = "";
@@ -8698,6 +8747,7 @@ class BinaryBridge {
8698
8747
  }
8699
8748
  handleTimeout(triggeringSessionId) {
8700
8749
  this.consecutiveRequestTimeouts = 0;
8750
+ this.spawnedBinaryFingerprint = null;
8701
8751
  this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
8702
8752
  this.outstandingBackgroundTaskIds.clear();
8703
8753
  if (this.process) {
@@ -8724,6 +8774,7 @@ class BinaryBridge {
8724
8774
  handleCrash(cause) {
8725
8775
  const proc = this.process;
8726
8776
  this.process = null;
8777
+ this.spawnedBinaryFingerprint = null;
8727
8778
  if (proc && proc.exitCode === null && !proc.killed) {
8728
8779
  proc.kill("SIGKILL");
8729
8780
  }
@@ -8735,6 +8786,10 @@ class BinaryBridge {
8735
8786
  this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
8736
8787
  }
8737
8788
  this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${this.getLogFilePathVia()})`));
8789
+ if (this._retiringDueToBinaryChange) {
8790
+ this.logVia("Binary exited while retiring after an on-disk update; skipping auto-restart");
8791
+ return;
8792
+ }
8738
8793
  if (this._restartCount < this.maxRestarts) {
8739
8794
  const delay = 100 * 2 ** this._restartCount;
8740
8795
  this._restartCount++;
@@ -8819,7 +8874,7 @@ var ORT_PLATFORM_MAP = {
8819
8874
  }
8820
8875
  };
8821
8876
  // ../aft-bridge/dist/project-identity.js
8822
- import { createHash } from "node:crypto";
8877
+ import { createHash as createHash2 } from "node:crypto";
8823
8878
  import { realpathSync } from "node:fs";
8824
8879
  import { resolve } from "node:path";
8825
8880
  function canonicalizeProjectRoot(dir) {
@@ -8850,7 +8905,7 @@ function normalizeWindowsRoot(p) {
8850
8905
  return s;
8851
8906
  }
8852
8907
  function projectRootKeyHash(dir) {
8853
- return createHash("sha256").update(canonicalizeProjectRoot(dir)).digest("hex").slice(0, 16);
8908
+ return createHash2("sha256").update(canonicalizeProjectRoot(dir)).digest("hex").slice(0, 16);
8854
8909
  }
8855
8910
 
8856
8911
  // ../aft-bridge/dist/pool.js
@@ -8934,6 +8989,11 @@ class BridgePool {
8934
8989
  for (const [dir, entry] of this.bridges) {
8935
8990
  if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
8936
8991
  continue;
8992
+ if (entry.bridge.maybeScheduleRespawnForUpdatedBinary(CLEANUP_INTERVAL_MS, now)) {
8993
+ this.staleBridges.add(entry.bridge);
8994
+ this.bridges.delete(dir);
8995
+ continue;
8996
+ }
8937
8997
  if (now - entry.lastUsed > this.idleTimeoutMs) {
8938
8998
  entry.bridge.shutdown().catch((err) => this.error("cleanup shutdown failed:", err));
8939
8999
  this.bridges.delete(dir);
@@ -9030,10 +9090,11 @@ class BridgePool {
9030
9090
  function normalizeKey(projectRoot) {
9031
9091
  return canonicalizeProjectRoot(projectRoot);
9032
9092
  }
9033
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/node_modules/@cortexkit/subc-client/src/client.ts
9093
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/client.ts
9034
9094
  import { promises as fs2 } from "node:fs";
9095
+ import { debuglog } from "node:util";
9035
9096
 
9036
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/node_modules/@cortexkit/subc-client/src/auth.ts
9097
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/auth.ts
9037
9098
  import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
9038
9099
  var NONCE_LEN = 32;
9039
9100
  var MAX_AUTH_MESSAGE_LEN = 4096;
@@ -9097,7 +9158,7 @@ async function authenticateClient(sock, conn, deadlineMs) {
9097
9158
  await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
9098
9159
  }
9099
9160
 
9100
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/node_modules/@cortexkit/subc-client/src/connection-file.ts
9161
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/connection-file.ts
9101
9162
  import { promises as fs } from "node:fs";
9102
9163
  var SCHEMA_VERSION = 1;
9103
9164
  var MIN_KEY_LEN = 32;
@@ -9166,7 +9227,7 @@ async function readConnectionFile(path) {
9166
9227
  return info;
9167
9228
  }
9168
9229
 
9169
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/node_modules/@cortexkit/subc-client/src/envelope.ts
9230
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/envelope.ts
9170
9231
  var PROTOCOL_VERSION = 1;
9171
9232
  var HEADER_LEN = 17;
9172
9233
  var FROZEN_PREFIX_LEN = 5;
@@ -9273,7 +9334,7 @@ function encodeFrame(frame) {
9273
9334
  return out;
9274
9335
  }
9275
9336
 
9276
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/node_modules/@cortexkit/subc-client/src/socket.ts
9337
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/socket.ts
9277
9338
  import net from "node:net";
9278
9339
 
9279
9340
  class SocketClosedError extends Error {
@@ -9304,6 +9365,9 @@ class SubcSocket {
9304
9365
  buffered = 0;
9305
9366
  waiter = null;
9306
9367
  closedErr = null;
9368
+ bufferedBytes() {
9369
+ return this.buffered;
9370
+ }
9307
9371
  constructor(sock) {
9308
9372
  this.sock = sock;
9309
9373
  sock.on("data", (chunk) => {
@@ -9462,9 +9526,14 @@ class SubcSocket {
9462
9526
  }
9463
9527
  }
9464
9528
 
9465
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/node_modules/@cortexkit/subc-client/src/client.ts
9529
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/client.ts
9530
+ var debug = debuglog("subc-client");
9466
9531
  var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
9467
9532
  var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
9533
+ var TIMEOUT_ARBITRATION_GRACE_MS = 50;
9534
+ var REQUEST_DEADLINE_MARKER = "request_deadline";
9535
+ var DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed";
9536
+ var ROUTE_OPEN_RETRY_DEADLINE_MS = 1e4;
9468
9537
  var BODY_READ_TIMEOUT_MS = 30000;
9469
9538
  var EMPTY_BODY = new Uint8Array(0);
9470
9539
  var DEFAULT_MANAGED_TARGET_KIND = "management_surface";
@@ -9508,6 +9577,7 @@ class SubcClient {
9508
9577
  closeStarted = false;
9509
9578
  reconnecting = null;
9510
9579
  generation = 1;
9580
+ readerActive = false;
9511
9581
  constructor(sock, currentConn, opts) {
9512
9582
  this.sock = sock;
9513
9583
  this.currentConn = currentConn;
@@ -9566,7 +9636,7 @@ class SubcClient {
9566
9636
  }
9567
9637
  continue;
9568
9638
  }
9569
- if (err.kind === "outcome_unknown") {
9639
+ if (err.kind === "outcome_unknown" && err.code !== DEADLINE_NO_DROP_CODE) {
9570
9640
  this.scheduleReconnectAfterDrop(err);
9571
9641
  }
9572
9642
  throw err;
@@ -9689,7 +9759,7 @@ class SubcClient {
9689
9759
  timer: null
9690
9760
  };
9691
9761
  pending.timer = setTimeout(() => {
9692
- this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms)));
9762
+ this.arbitrateTimeout(key, pending, channel, corr, ms);
9693
9763
  }, ms);
9694
9764
  this.pending.set(key, pending);
9695
9765
  this.sock.write(encodeFrame(frame), Date.now() + ms).catch((err) => {
@@ -9699,6 +9769,23 @@ class SubcClient {
9699
9769
  });
9700
9770
  });
9701
9771
  }
9772
+ arbitrateTimeout(key, pending, channel, corr, ms) {
9773
+ const settleAsTimeout = () => {
9774
+ this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
9775
+ };
9776
+ const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
9777
+ const arbitrate = () => {
9778
+ if (this.pending.get(key) !== pending)
9779
+ return;
9780
+ const readerDraining = this.readerActive || this.sock.bufferedBytes() > 0;
9781
+ if (readerDraining && Date.now() < graceDeadline) {
9782
+ setImmediate(arbitrate);
9783
+ return;
9784
+ }
9785
+ settleAsTimeout();
9786
+ };
9787
+ setImmediate(arbitrate);
9788
+ }
9702
9789
  async managedRequest(routeChannel, body, opts) {
9703
9790
  const bytes = body instanceof Uint8Array ? body : this.encode(body);
9704
9791
  const priority = opts.priority ?? 1 /* Interactive */;
@@ -9723,6 +9810,9 @@ class SubcClient {
9723
9810
  if (!handedToSocket) {
9724
9811
  return this.notSentCallError("request bytes were not queued to the subc socket", err);
9725
9812
  }
9813
+ if (err instanceof SubcError && err.code === REQUEST_DEADLINE_MARKER) {
9814
+ 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);
9815
+ }
9726
9816
  return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", err);
9727
9817
  };
9728
9818
  return new Promise((resolve2, reject) => {
@@ -9736,7 +9826,7 @@ class SubcClient {
9736
9826
  classifyFailure
9737
9827
  };
9738
9828
  pending.timer = setTimeout(() => {
9739
- this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms)));
9829
+ this.arbitrateTimeout(key, pending, channel, corr, ms);
9740
9830
  }, ms);
9741
9831
  this.pending.set(key, pending);
9742
9832
  const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
@@ -9781,6 +9871,9 @@ class SubcClient {
9781
9871
  return cached.opening;
9782
9872
  }
9783
9873
  async openCachedRoute(cached) {
9874
+ const routeRetryDeadline = Date.now() + ROUTE_OPEN_RETRY_DEADLINE_MS;
9875
+ let routeRetryDelay = this.opts.reconnectBackoff.baseMs;
9876
+ let routeRetryAttempt = 0;
9784
9877
  for (;; ) {
9785
9878
  if (cached.closed)
9786
9879
  throw this.routeClosedDuringOpen();
@@ -9814,6 +9907,15 @@ class SubcClient {
9814
9907
  }
9815
9908
  continue;
9816
9909
  }
9910
+ if (!this.closeStarted && err instanceof SubcError && isRetryableRouteOpenCode(err.code)) {
9911
+ routeRetryAttempt += 1;
9912
+ if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
9913
+ await this.opts.sleep(routeRetryDelay);
9914
+ routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
9915
+ continue;
9916
+ }
9917
+ throw this.notSentCallError(`route.open failed for module ${cached.moduleId}: ${err.code} (retry budget exhausted)`, err);
9918
+ }
9817
9919
  throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`, err);
9818
9920
  }
9819
9921
  }
@@ -9907,9 +10009,16 @@ class SubcClient {
9907
10009
  try {
9908
10010
  for (;; ) {
9909
10011
  const headerBytes = await sock.readExact(HEADER_LEN, Number.POSITIVE_INFINITY);
9910
- const header = decodeHeader(headerBytes);
9911
- const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS);
9912
- this.dispatch({ header, body });
10012
+ this.readerActive = true;
10013
+ try {
10014
+ const header = decodeHeader(headerBytes);
10015
+ const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS);
10016
+ if (this.sock === sock && this.generation === generation) {
10017
+ this.dispatch({ header, body });
10018
+ }
10019
+ } finally {
10020
+ this.readerActive = false;
10021
+ }
9913
10022
  }
9914
10023
  } catch (err) {
9915
10024
  if (this.sock === sock && this.generation === generation) {
@@ -9941,13 +10050,20 @@ class SubcClient {
9941
10050
  this.failChannel(frame.header.channel, new SubcError("route closed by subc (GOODBYE)"));
9942
10051
  return;
9943
10052
  }
10053
+ if (frame.header.ty === 1 /* Response */ || frame.header.ty === 5 /* Error */ || frame.header.ty === 4 /* StreamEnd */) {
10054
+ 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() ?? "?");
10055
+ return;
10056
+ }
9944
10057
  }
9945
10058
  settle(key, pending, run) {
10059
+ if (this.pending.get(key) !== pending)
10060
+ return false;
9946
10061
  this.pending.delete(key);
9947
10062
  if (pending.timer)
9948
10063
  clearTimeout(pending.timer);
9949
10064
  run();
9950
10065
  pending.onSettle?.();
10066
+ return true;
9951
10067
  }
9952
10068
  rejectPending(key, pending, err) {
9953
10069
  this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
@@ -10011,6 +10127,9 @@ function isConsumerReconnectTransient(err) {
10011
10127
  const code = errorCode(err);
10012
10128
  return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
10013
10129
  }
10130
+ function isRetryableRouteOpenCode(code) {
10131
+ return code === "unknown_module" || code === "module_reloading" || code === "target_unavailable" || code === "module_timeout";
10132
+ }
10014
10133
  async function connectionFileExists(path) {
10015
10134
  try {
10016
10135
  await fs2.access(path);
@@ -10026,7 +10145,8 @@ function normalizeConnectOptions(opts) {
10026
10145
  identity: opts.identity,
10027
10146
  targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
10028
10147
  reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
10029
- sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)))
10148
+ sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
10149
+ timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS
10030
10150
  };
10031
10151
  }
10032
10152
  function routeCacheKey(target, identity, consumerIdentity) {
@@ -10055,7 +10175,7 @@ function causeMessage(cause) {
10055
10175
  return "";
10056
10176
  return `: ${cause instanceof Error ? cause.message : String(cause)}`;
10057
10177
  }
10058
- // ../../node_modules/.bun/@cortexkit+subc-client@0.2.1/node_modules/@cortexkit/subc-client/src/provider.ts
10178
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/provider.ts
10059
10179
  import { Buffer as Buffer2 } from "node:buffer";
10060
10180
  var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
10061
10181
  var BODY_READ_TIMEOUT_MS2 = 30000;
@@ -11273,7 +11393,7 @@ class AftRpcClient {
11273
11393
  }
11274
11394
  parsePortFile(filePath) {
11275
11395
  try {
11276
- const content = readFileSync(filePath, "utf-8");
11396
+ const content = readFileSync2(filePath, "utf-8");
11277
11397
  return parseRpcPortRecord(content);
11278
11398
  } catch {
11279
11399
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/aft-opencode",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
6
6
  "main": "dist/index.js",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@clack/prompts": "^1.6.0",
34
- "@cortexkit/aft-bridge": "0.44.0",
34
+ "@cortexkit/aft-bridge": "0.45.0",
35
35
  "@opentui/core": "^0.4.2",
36
36
  "@opentui/solid": "^0.4.2",
37
37
  "comment-json": "^4.6.2",
@@ -40,12 +40,12 @@
40
40
  "zod": "^4.4.3"
41
41
  },
42
42
  "optionalDependencies": {
43
- "@cortexkit/aft-darwin-arm64": "0.44.0",
44
- "@cortexkit/aft-darwin-x64": "0.44.0",
45
- "@cortexkit/aft-linux-arm64": "0.44.0",
46
- "@cortexkit/aft-linux-x64": "0.44.0",
47
- "@cortexkit/aft-win32-arm64": "0.44.0",
48
- "@cortexkit/aft-win32-x64": "0.44.0"
43
+ "@cortexkit/aft-darwin-arm64": "0.45.0",
44
+ "@cortexkit/aft-darwin-x64": "0.45.0",
45
+ "@cortexkit/aft-linux-arm64": "0.45.0",
46
+ "@cortexkit/aft-linux-x64": "0.45.0",
47
+ "@cortexkit/aft-win32-arm64": "0.45.0",
48
+ "@cortexkit/aft-win32-x64": "0.45.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@opencode-ai/plugin": "^1.17.11",