@cortexkit/aft-pi 0.26.0 → 0.26.1

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
@@ -6889,7 +6889,7 @@ ${len.toString(16)}\r
6889
6889
  // ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/dispatcher/client-h2.js
6890
6890
  var require_client_h2 = __commonJS((exports, module) => {
6891
6891
  var assert = __require("node:assert");
6892
- var { pipeline: pipeline2 } = __require("node:stream");
6892
+ var { pipeline: pipeline3 } = __require("node:stream");
6893
6893
  var util = require_util();
6894
6894
  var {
6895
6895
  RequestContentLengthMismatchError,
@@ -7658,7 +7658,7 @@ var require_client_h2 = __commonJS((exports, module) => {
7658
7658
  }
7659
7659
  function writeStream(abort, socket, expectsPayload, h2stream, body, client, request, contentLength) {
7660
7660
  assert(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined");
7661
- const pipe = pipeline2(body, h2stream, (err) => {
7661
+ const pipe = pipeline3(body, h2stream, (err) => {
7662
7662
  if (err) {
7663
7663
  util.destroy(pipe, err);
7664
7664
  abort(err);
@@ -10422,7 +10422,7 @@ var require_h2c_client = __commonJS((exports, module) => {
10422
10422
  var require_readable = __commonJS((exports, module) => {
10423
10423
  var assert = __require("node:assert");
10424
10424
  var { addAbortListener } = __require("node:events");
10425
- var { Readable: Readable2 } = __require("node:stream");
10425
+ var { Readable: Readable3 } = __require("node:stream");
10426
10426
  var { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require_errors();
10427
10427
  var util = require_util();
10428
10428
  var { ReadableStreamFrom } = require_util();
@@ -10436,7 +10436,7 @@ var require_readable = __commonJS((exports, module) => {
10436
10436
  var kBytesRead = Symbol("kBytesRead");
10437
10437
  var noop = () => {};
10438
10438
 
10439
- class BodyReadable extends Readable2 {
10439
+ class BodyReadable extends Readable3 {
10440
10440
  constructor({
10441
10441
  resume,
10442
10442
  abort,
@@ -10723,7 +10723,7 @@ var require_readable = __commonJS((exports, module) => {
10723
10723
  var require_api_request = __commonJS((exports, module) => {
10724
10724
  var assert = __require("node:assert");
10725
10725
  var { AsyncResource } = __require("node:async_hooks");
10726
- var { Readable: Readable2 } = require_readable();
10726
+ var { Readable: Readable3 } = require_readable();
10727
10727
  var { InvalidArgumentError, RequestAbortedError } = require_errors();
10728
10728
  var util = require_util();
10729
10729
  function noop() {}
@@ -10807,7 +10807,7 @@ var require_api_request = __commonJS((exports, module) => {
10807
10807
  const parsedHeaders = headers;
10808
10808
  const contentType = parsedHeaders?.["content-type"];
10809
10809
  const contentLength = parsedHeaders?.["content-length"];
10810
- const res = new Readable2({
10810
+ const res = new Readable3({
10811
10811
  resume: () => controller.resume(),
10812
10812
  abort: (reason) => controller.abort(reason),
10813
10813
  contentType,
@@ -11137,7 +11137,7 @@ var require_api_stream = __commonJS((exports, module) => {
11137
11137
  // ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/api/api-pipeline.js
11138
11138
  var require_api_pipeline = __commonJS((exports, module) => {
11139
11139
  var {
11140
- Readable: Readable2,
11140
+ Readable: Readable3,
11141
11141
  Duplex,
11142
11142
  PassThrough
11143
11143
  } = __require("node:stream");
@@ -11153,7 +11153,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
11153
11153
  function noop() {}
11154
11154
  var kResume = Symbol("resume");
11155
11155
 
11156
- class PipelineRequest extends Readable2 {
11156
+ class PipelineRequest extends Readable3 {
11157
11157
  constructor() {
11158
11158
  super({ autoDestroy: true });
11159
11159
  this[kResume] = null;
@@ -11171,7 +11171,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
11171
11171
  }
11172
11172
  }
11173
11173
 
11174
- class PipelineResponse extends Readable2 {
11174
+ class PipelineResponse extends Readable3 {
11175
11175
  constructor(resume) {
11176
11176
  super({ autoDestroy: true });
11177
11177
  this[kResume] = resume;
@@ -11326,7 +11326,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
11326
11326
  util.destroy(ret, err);
11327
11327
  }
11328
11328
  }
11329
- function pipeline2(opts, handler) {
11329
+ function pipeline3(opts, handler) {
11330
11330
  try {
11331
11331
  const pipelineHandler = new PipelineHandler(opts, handler);
11332
11332
  this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler);
@@ -11335,7 +11335,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
11335
11335
  return new PassThrough().destroy(err);
11336
11336
  }
11337
11337
  }
11338
- module.exports = pipeline2;
11338
+ module.exports = pipeline3;
11339
11339
  });
11340
11340
 
11341
11341
  // ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/api/api-upgrade.js
@@ -15358,7 +15358,7 @@ var require_cache_revalidation_handler = __commonJS((exports, module) => {
15358
15358
  // ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/interceptor/cache.js
15359
15359
  var require_cache2 = __commonJS((exports, module) => {
15360
15360
  var assert = __require("node:assert");
15361
- var { Readable: Readable2 } = __require("node:stream");
15361
+ var { Readable: Readable3 } = __require("node:stream");
15362
15362
  var util = require_util();
15363
15363
  var CacheHandler = require_cache_handler();
15364
15364
  var MemoryCacheStore = require_memory_cache_store();
@@ -15454,7 +15454,7 @@ var require_cache2 = __commonJS((exports, module) => {
15454
15454
  return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler));
15455
15455
  }
15456
15456
  function sendCachedValue(handler, opts, result, age, context, isStale2) {
15457
- const stream = util.isStream(result.body) ? result.body : Readable2.from(result.body ?? []);
15457
+ const stream = util.isStream(result.body) ? result.body : Readable3.from(result.body ?? []);
15458
15458
  assert(!stream.destroyed, "stream should not be destroyed");
15459
15459
  assert(!stream.readableDidRead, "stream should not be readableDidRead");
15460
15460
  const controller = {
@@ -15668,7 +15668,7 @@ var require_cache2 = __commonJS((exports, module) => {
15668
15668
  // ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/interceptor/decompress.js
15669
15669
  var require_decompress = __commonJS((exports, module) => {
15670
15670
  var { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = __require("node:zlib");
15671
- var { pipeline: pipeline2 } = __require("node:stream");
15671
+ var { pipeline: pipeline3 } = __require("node:stream");
15672
15672
  var DecoratorHandler = require_decorator_handler();
15673
15673
  var supportedEncodings = {
15674
15674
  gzip: createGunzip,
@@ -15743,7 +15743,7 @@ var require_decompress = __commonJS((exports, module) => {
15743
15743
  #setupMultipleDecompressors(controller) {
15744
15744
  const lastDecompressor = this.#decompressors[this.#decompressors.length - 1];
15745
15745
  this.#setupDecompressorEvents(lastDecompressor, controller);
15746
- pipeline2(this.#decompressors, (err) => {
15746
+ pipeline3(this.#decompressors, (err) => {
15747
15747
  if (err) {
15748
15748
  super.onResponseError(controller, err);
15749
15749
  return;
@@ -18046,7 +18046,7 @@ var require_fetch = __commonJS((exports, module) => {
18046
18046
  subresourceSet
18047
18047
  } = require_constants3();
18048
18048
  var EE = __require("node:events");
18049
- var { Readable: Readable2, pipeline: pipeline2, finished, isErrored, isReadable } = __require("node:stream");
18049
+ var { Readable: Readable3, pipeline: pipeline3, finished, isErrored, isReadable } = __require("node:stream");
18050
18050
  var { addAbortListener, bufferToLowerCasedHeaderName } = require_util();
18051
18051
  var { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require_data_url();
18052
18052
  var { getGlobalDispatcher } = require_global2();
@@ -18937,7 +18937,7 @@ var require_fetch = __commonJS((exports, module) => {
18937
18937
  const headersList = new HeadersList;
18938
18938
  appendHeadersListFromResponseHeaders(headersList, headers, rawHeaders);
18939
18939
  const location = headersList.get("location", true);
18940
- this.body = new Readable2({ read: () => controller.resume() });
18940
+ this.body = new Readable3({ read: () => controller.resume() });
18941
18941
  const willFollow = location && request.redirect === "follow" && redirectStatusSet.has(status);
18942
18942
  const decoders = [];
18943
18943
  if (request.method !== "HEAD" && request.method !== "CONNECT" && !nullBodyStatus.includes(status) && !willFollow) {
@@ -18981,7 +18981,7 @@ var require_fetch = __commonJS((exports, module) => {
18981
18981
  status,
18982
18982
  statusText,
18983
18983
  headersList,
18984
- body: decoders.length ? pipeline2(this.body, ...decoders, (err) => {
18984
+ body: decoders.length ? pipeline3(this.body, ...decoders, (err) => {
18985
18985
  if (err) {
18986
18986
  this.onResponseError(controller, err);
18987
18987
  }
@@ -22363,7 +22363,7 @@ ${value}`;
22363
22363
 
22364
22364
  // ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/web/eventsource/eventsource.js
22365
22365
  var require_eventsource = __commonJS((exports, module) => {
22366
- var { pipeline: pipeline2 } = __require("node:stream");
22366
+ var { pipeline: pipeline3 } = __require("node:stream");
22367
22367
  var { fetching } = require_fetch();
22368
22368
  var { makeRequest } = require_request2();
22369
22369
  var { webidl } = require_webidl();
@@ -22492,7 +22492,7 @@ var require_eventsource = __commonJS((exports, module) => {
22492
22492
  this.dispatchEvent(createFastMessageEvent(event.type, event.options));
22493
22493
  }
22494
22494
  });
22495
- pipeline2(response.body.stream, eventSourceStream, (error2) => {
22495
+ pipeline3(response.body.stream, eventSourceStream, (error2) => {
22496
22496
  if (error2?.aborted === false) {
22497
22497
  this.close();
22498
22498
  this.dispatchEvent(new Event("error"));
@@ -30490,12 +30490,22 @@ function getActiveLogger() {
30490
30490
  return loggerGlobal()[ACTIVE_LOGGER_SYMBOL];
30491
30491
  }
30492
30492
  function getLogFilePath() {
30493
- return getActiveLogger()?.getLogFilePath?.();
30493
+ try {
30494
+ return getActiveLogger()?.getLogFilePath?.();
30495
+ } catch (err) {
30496
+ console.error(`[aft-bridge] ERROR: active logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
30497
+ return;
30498
+ }
30494
30499
  }
30495
30500
  function log(message, meta) {
30496
30501
  const active = getActiveLogger();
30497
30502
  if (active) {
30498
- active.log(message, meta);
30503
+ try {
30504
+ active.log(message, meta);
30505
+ } catch (err) {
30506
+ console.error(`[aft-bridge] ERROR: active logger log threw: ${err instanceof Error ? err.message : String(err)}`);
30507
+ console.error(`[aft-bridge] ${message}`);
30508
+ }
30499
30509
  } else {
30500
30510
  console.error(`[aft-bridge] ${message}`);
30501
30511
  }
@@ -30503,7 +30513,12 @@ function log(message, meta) {
30503
30513
  function warn(message, meta) {
30504
30514
  const active = getActiveLogger();
30505
30515
  if (active) {
30506
- active.warn(message, meta);
30516
+ try {
30517
+ active.warn(message, meta);
30518
+ } catch (err) {
30519
+ console.error(`[aft-bridge] ERROR: active logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
30520
+ console.error(`[aft-bridge] WARN: ${message}`);
30521
+ }
30507
30522
  } else {
30508
30523
  console.error(`[aft-bridge] WARN: ${message}`);
30509
30524
  }
@@ -30511,20 +30526,16 @@ function warn(message, meta) {
30511
30526
  function error(message, meta) {
30512
30527
  const active = getActiveLogger();
30513
30528
  if (active) {
30514
- active.error(message, meta);
30529
+ try {
30530
+ active.error(message, meta);
30531
+ } catch (err) {
30532
+ console.error(`[aft-bridge] ERROR: active logger error threw: ${err instanceof Error ? err.message : String(err)}`);
30533
+ console.error(`[aft-bridge] ERROR: ${message}`);
30534
+ }
30515
30535
  } else {
30516
30536
  console.error(`[aft-bridge] ERROR: ${message}`);
30517
30537
  }
30518
30538
  }
30519
- function sessionLog(sessionId, message) {
30520
- log(message, sessionId ? { sessionId } : undefined);
30521
- }
30522
- function sessionWarn(sessionId, message) {
30523
- warn(message, sessionId ? { sessionId } : undefined);
30524
- }
30525
- function sessionError(sessionId, message) {
30526
- error(message, sessionId ? { sessionId } : undefined);
30527
- }
30528
30539
  // ../aft-bridge/dist/bridge.js
30529
30540
  import { spawn } from "node:child_process";
30530
30541
  import { homedir } from "node:os";
@@ -30619,6 +30630,7 @@ class BinaryBridge {
30619
30630
  pending = new Map;
30620
30631
  nextId = 1;
30621
30632
  stdoutBuffer = "";
30633
+ stderrBuffer = "";
30622
30634
  stderrTail = [];
30623
30635
  _restartCount = 0;
30624
30636
  _shuttingDown = false;
@@ -30654,24 +30666,62 @@ class BinaryBridge {
30654
30666
  }
30655
30667
  logVia(message, meta) {
30656
30668
  const logger = this.logger ?? getActiveLogger();
30657
- if (logger)
30658
- logger.log(message, meta);
30659
- else
30669
+ if (logger) {
30670
+ try {
30671
+ logger.log(message, meta);
30672
+ } catch (err) {
30673
+ console.error(`[aft-bridge] ERROR: logger log threw: ${err instanceof Error ? err.message : String(err)}`);
30674
+ console.error(`[aft-bridge] ${message}`);
30675
+ }
30676
+ } else {
30660
30677
  log(message, meta);
30678
+ }
30661
30679
  }
30662
30680
  warnVia(message, meta) {
30663
30681
  const logger = this.logger ?? getActiveLogger();
30664
- if (logger)
30665
- logger.warn(message, meta);
30666
- else
30682
+ if (logger) {
30683
+ try {
30684
+ logger.warn(message, meta);
30685
+ } catch (err) {
30686
+ console.error(`[aft-bridge] ERROR: logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
30687
+ console.error(`[aft-bridge] WARN: ${message}`);
30688
+ }
30689
+ } else {
30667
30690
  warn(message, meta);
30691
+ }
30668
30692
  }
30669
30693
  errorVia(message, meta) {
30670
30694
  const logger = this.logger ?? getActiveLogger();
30671
- if (logger)
30672
- logger.error(message, meta);
30673
- else
30695
+ if (logger) {
30696
+ try {
30697
+ logger.error(message, meta);
30698
+ } catch (err) {
30699
+ console.error(`[aft-bridge] ERROR: logger error threw: ${err instanceof Error ? err.message : String(err)}`);
30700
+ console.error(`[aft-bridge] ERROR: ${message}`);
30701
+ }
30702
+ } else {
30674
30703
  error(message, meta);
30704
+ }
30705
+ }
30706
+ getLogFilePathVia() {
30707
+ if (this.logger?.getLogFilePath) {
30708
+ try {
30709
+ return this.logger.getLogFilePath();
30710
+ } catch (err) {
30711
+ console.error(`[aft-bridge] ERROR: logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
30712
+ return;
30713
+ }
30714
+ }
30715
+ return getLogFilePath();
30716
+ }
30717
+ sessionLogVia(sessionId, message) {
30718
+ this.logVia(message, sessionId ? { sessionId } : undefined);
30719
+ }
30720
+ sessionWarnVia(sessionId, message) {
30721
+ this.warnVia(message, sessionId ? { sessionId } : undefined);
30722
+ }
30723
+ sessionErrorVia(sessionId, message) {
30724
+ this.errorVia(message, sessionId ? { sessionId } : undefined);
30675
30725
  }
30676
30726
  get restartCount() {
30677
30727
  return this._restartCount;
@@ -30730,7 +30780,7 @@ class BinaryBridge {
30730
30780
  await this.deliverConfigureWarnings(configResult, params, options);
30731
30781
  await this.checkVersion();
30732
30782
  if (!this.isAlive()) {
30733
- throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${getLogFilePath()}`);
30783
+ throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${this.getLogFilePathVia()}`);
30734
30784
  }
30735
30785
  this.configured = true;
30736
30786
  } finally {
@@ -30766,9 +30816,9 @@ class BinaryBridge {
30766
30816
  const restartSuffix = keepBridgeOnTimeout ? "" : " — restarting bridge";
30767
30817
  const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
30768
30818
  if (requestSessionId) {
30769
- sessionWarn(requestSessionId, timeoutMsg);
30819
+ this.sessionWarnVia(requestSessionId, timeoutMsg);
30770
30820
  } else {
30771
- warn(timeoutMsg);
30821
+ this.warnVia(timeoutMsg);
30772
30822
  }
30773
30823
  reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
30774
30824
  if (!keepBridgeOnTimeout) {
@@ -30795,7 +30845,7 @@ class BinaryBridge {
30795
30845
  });
30796
30846
  } catch (err) {
30797
30847
  if (err instanceof BridgeReplacedDuringVersionCheck && canRetryAfterVersionSwap && command !== "configure" && command !== "version") {
30798
- log(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
30848
+ this.logVia(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
30799
30849
  return this.sendWithVersionMismatchRetry(command, params, options, false);
30800
30850
  }
30801
30851
  throw err;
@@ -30815,7 +30865,7 @@ class BinaryBridge {
30815
30865
  warnings: configResult.warnings
30816
30866
  });
30817
30867
  } catch (err) {
30818
- warn(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
30868
+ this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
30819
30869
  } finally {
30820
30870
  if (sessionId) {
30821
30871
  this.configureWarningClients.delete(sessionId);
@@ -30849,7 +30899,7 @@ class BinaryBridge {
30849
30899
  if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
30850
30900
  return;
30851
30901
  this.cachedStatus = snapshot;
30852
- log("Received status_changed push frame; cached AFT status snapshot");
30902
+ this.logVia("Received status_changed push frame; cached AFT status snapshot");
30853
30903
  for (const listener of this.statusListeners) {
30854
30904
  this.deliverStatusSnapshot(listener, this.cachedStatus);
30855
30905
  }
@@ -30858,7 +30908,7 @@ class BinaryBridge {
30858
30908
  try {
30859
30909
  listener(snapshot);
30860
30910
  } catch (err) {
30861
- warn(`status listener threw: ${err instanceof Error ? err.message : String(err)}`);
30911
+ this.warnVia(`status listener threw: ${err instanceof Error ? err.message : String(err)}`);
30862
30912
  }
30863
30913
  }
30864
30914
  async shutdown() {
@@ -30876,7 +30926,7 @@ class BinaryBridge {
30876
30926
  }, 5000);
30877
30927
  proc.once("exit", () => {
30878
30928
  clearTimeout(forceKillTimer);
30879
- log("Process exited during shutdown");
30929
+ this.logVia("Process exited during shutdown");
30880
30930
  resolve();
30881
30931
  });
30882
30932
  proc.kill("SIGTERM");
@@ -30895,9 +30945,9 @@ class BinaryBridge {
30895
30945
  if (typeof binaryVersion !== "string") {
30896
30946
  throw new Error(`Binary did not report a version — likely too old (minVersion: ${this.minVersion})`);
30897
30947
  }
30898
- log(`Binary version: ${binaryVersion}`);
30948
+ this.logVia(`Binary version: ${binaryVersion}`);
30899
30949
  if (compareSemver(binaryVersion, this.minVersion) < 0) {
30900
- warn(`Binary version ${binaryVersion} is older than required ${this.minVersion}`);
30950
+ this.warnVia(`Binary version ${binaryVersion} is older than required ${this.minVersion}`);
30901
30951
  const replacementPath = await this.onVersionMismatch?.(binaryVersion, this.minVersion);
30902
30952
  if (replacementPath === undefined) {
30903
30953
  return;
@@ -30909,7 +30959,7 @@ class BinaryBridge {
30909
30959
  throw new BridgeReplacedDuringVersionCheck(replacementPath);
30910
30960
  }
30911
30961
  } catch (err) {
30912
- warn(`Version check failed: ${err.message}`);
30962
+ this.warnVia(`Version check failed: ${err.message}`);
30913
30963
  throw err;
30914
30964
  }
30915
30965
  }
@@ -30929,7 +30979,7 @@ class BinaryBridge {
30929
30979
  }, 5000);
30930
30980
  proc.once("exit", () => {
30931
30981
  clearTimeout(forceKillTimer);
30932
- log("Process exited during coordinated binary replacement");
30982
+ this.logVia("Process exited during coordinated binary replacement");
30933
30983
  resolve();
30934
30984
  });
30935
30985
  proc.kill("SIGTERM");
@@ -30942,9 +30992,9 @@ class BinaryBridge {
30942
30992
  }
30943
30993
  spawnProcess(triggeringSessionId) {
30944
30994
  if (triggeringSessionId) {
30945
- sessionLog(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
30995
+ this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
30946
30996
  } else {
30947
- log(`Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
30997
+ this.logVia(`Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
30948
30998
  }
30949
30999
  const semantic = this.configOverrides.semantic;
30950
31000
  const semanticBackend = (() => {
@@ -30991,11 +31041,12 @@ class BinaryBridge {
30991
31041
  const remaining = stderrDecoder.end();
30992
31042
  if (remaining)
30993
31043
  this.onStderrData(remaining);
31044
+ this.flushStderrBuffer();
30994
31045
  });
30995
31046
  child.on("error", (err) => {
30996
31047
  if (this.process !== currentChild)
30997
31048
  return;
30998
- error(`Process error: ${err.message}${this.formatStderrTail()}`);
31049
+ this.errorVia(`Process error: ${err.message}${this.formatStderrTail()}`);
30999
31050
  this.handleCrash();
31000
31051
  });
31001
31052
  child.on("exit", (code, signal) => {
@@ -31003,7 +31054,7 @@ class BinaryBridge {
31003
31054
  return;
31004
31055
  if (this._shuttingDown)
31005
31056
  return;
31006
- log(`Process exited: code=${code}, signal=${signal}`);
31057
+ this.logVia(`Process exited: code=${code}, signal=${signal}`);
31007
31058
  if (signal === "SIGTERM" || signal === "SIGKILL" || signal === "SIGHUP" || signal === "SIGINT") {
31008
31059
  this.process = null;
31009
31060
  this.configured = false;
@@ -31015,6 +31066,7 @@ class BinaryBridge {
31015
31066
  });
31016
31067
  this.process = child;
31017
31068
  this.stdoutBuffer = "";
31069
+ this.stderrBuffer = "";
31018
31070
  this.stderrTail = [];
31019
31071
  }
31020
31072
  pushStderrLine(line) {
@@ -31024,16 +31076,28 @@ class BinaryBridge {
31024
31076
  }
31025
31077
  }
31026
31078
  onStderrData(data) {
31027
- const lines = data.trimEnd().split(`
31028
- `);
31029
- for (const line of lines) {
31079
+ this.stderrBuffer += data;
31080
+ let newlineIdx;
31081
+ while ((newlineIdx = this.stderrBuffer.indexOf(`
31082
+ `)) !== -1) {
31083
+ const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
31084
+ this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
31030
31085
  if (!line)
31031
31086
  continue;
31032
31087
  const tagged = tagStderrLine(line);
31033
- log(tagged);
31088
+ this.logVia(tagged);
31034
31089
  this.pushStderrLine(tagged);
31035
31090
  }
31036
31091
  }
31092
+ flushStderrBuffer() {
31093
+ const line = this.stderrBuffer.replace(/\r$/, "");
31094
+ this.stderrBuffer = "";
31095
+ if (!line)
31096
+ return;
31097
+ const tagged = tagStderrLine(line);
31098
+ this.logVia(tagged);
31099
+ this.pushStderrLine(tagged);
31100
+ }
31037
31101
  formatStderrTail() {
31038
31102
  if (this.stderrTail.length === 0)
31039
31103
  return "";
@@ -31091,7 +31155,7 @@ class BinaryBridge {
31091
31155
  }
31092
31156
  if (response.type === "configure_warnings") {
31093
31157
  this.handleConfigureWarningsFrame(response).catch((err) => {
31094
- warn(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
31158
+ this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
31095
31159
  });
31096
31160
  continue;
31097
31161
  }
@@ -31109,10 +31173,10 @@ class BinaryBridge {
31109
31173
  this.scheduleRestartCountReset();
31110
31174
  entry.resolve(response);
31111
31175
  } else if (typeof response.type === "string") {
31112
- log(`Ignoring unknown stdout push frame type: ${response.type}`);
31176
+ this.logVia(`Ignoring unknown stdout push frame type: ${response.type}`);
31113
31177
  }
31114
31178
  } catch (_err) {
31115
- warn(`Failed to parse stdout line: ${line}`);
31179
+ this.warnVia(`Failed to parse stdout line: ${line}`);
31116
31180
  }
31117
31181
  }
31118
31182
  }
@@ -31126,17 +31190,17 @@ class BinaryBridge {
31126
31190
  this.configured = false;
31127
31191
  const tail = this.formatStderrTail();
31128
31192
  this.stderrTail = [];
31129
- const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${getLogFilePath()})`;
31193
+ const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${this.getLogFilePathVia()})`;
31130
31194
  if (tail) {
31131
31195
  if (triggeringSessionId) {
31132
- sessionError(triggeringSessionId, killedMsg);
31196
+ this.sessionErrorVia(triggeringSessionId, killedMsg);
31133
31197
  } else {
31134
- error(killedMsg);
31198
+ this.errorVia(killedMsg);
31135
31199
  }
31136
31200
  } else if (triggeringSessionId) {
31137
- sessionWarn(triggeringSessionId, killedMsg);
31201
+ this.sessionWarnVia(triggeringSessionId, killedMsg);
31138
31202
  } else {
31139
- warn(killedMsg);
31203
+ this.warnVia(killedMsg);
31140
31204
  }
31141
31205
  }
31142
31206
  handleCrash(cause) {
@@ -31149,25 +31213,25 @@ class BinaryBridge {
31149
31213
  this.configured = false;
31150
31214
  const tail = this.formatStderrTail();
31151
31215
  if (tail) {
31152
- error(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
31216
+ this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
31153
31217
  }
31154
- this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${getLogFilePath()})`));
31218
+ this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${this.getLogFilePathVia()})`));
31155
31219
  if (this._restartCount < this.maxRestarts) {
31156
31220
  const delay = 100 * 2 ** this._restartCount;
31157
31221
  this._restartCount++;
31158
- log(`Auto-restart #${this._restartCount} in ${delay}ms`);
31222
+ this.logVia(`Auto-restart #${this._restartCount} in ${delay}ms`);
31159
31223
  setTimeout(() => {
31160
31224
  if (!this._shuttingDown && !this.isAlive()) {
31161
31225
  try {
31162
31226
  this.spawnProcess();
31163
31227
  } catch (err) {
31164
- error(`Failed to restart: ${err.message}`);
31228
+ this.errorVia(`Failed to restart: ${err.message}`);
31165
31229
  }
31166
31230
  }
31167
31231
  }, delay);
31168
31232
  this.scheduleRestartCountReset();
31169
31233
  } else {
31170
- error(`Max restarts (${this.maxRestarts}) reached, giving up. Logs: ${getLogFilePath()}${tail}`);
31234
+ this.errorVia(`Max restarts (${this.maxRestarts}) reached, giving up. Logs: ${this.getLogFilePathVia()}${tail}`);
31171
31235
  this.scheduleRestartCountReset();
31172
31236
  }
31173
31237
  }
@@ -31193,9 +31257,12 @@ class BinaryBridge {
31193
31257
  }
31194
31258
  }
31195
31259
  // ../aft-bridge/dist/downloader.js
31196
- import { chmodSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
31260
+ import { createHash } from "node:crypto";
31261
+ import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, renameSync, rmSync, statSync, unlinkSync } from "node:fs";
31197
31262
  import { homedir as homedir2 } from "node:os";
31198
31263
  import { join as join2 } from "node:path";
31264
+ import { Readable } from "node:stream";
31265
+ import { pipeline } from "node:stream/promises";
31199
31266
 
31200
31267
  // ../aft-bridge/dist/platform.js
31201
31268
  var PLATFORM_ARCH_MAP = {
@@ -31213,6 +31280,11 @@ var PLATFORM_ASSET_MAP = {
31213
31280
 
31214
31281
  // ../aft-bridge/dist/downloader.js
31215
31282
  var REPO = "cortexkit/aft";
31283
+ var DOWNLOAD_TIMEOUT_MS = 300000;
31284
+ var LATEST_TAG_TIMEOUT_MS = 30000;
31285
+ var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
31286
+ var DOWNLOAD_LOCK_TIMEOUT_MS = 120000;
31287
+ var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
31216
31288
  function getCacheDir() {
31217
31289
  if (process.platform === "win32") {
31218
31290
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
@@ -31254,54 +31326,101 @@ async function downloadBinary(version) {
31254
31326
  const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
31255
31327
  const checksumUrl = `https://github.com/${REPO}/releases/download/${tag}/checksums.sha256`;
31256
31328
  log(`Downloading AFT binary (${tag}) for ${platformKey}...`);
31329
+ const lockPath = join2(versionedCacheDir, ".download.lock");
31330
+ let releaseLock = null;
31331
+ let binaryController = null;
31332
+ let checksumController = null;
31333
+ let binaryTimeout = null;
31334
+ let checksumTimeout = null;
31335
+ const tmpPath = `${binaryPath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
31257
31336
  try {
31258
31337
  if (!existsSync(versionedCacheDir)) {
31259
31338
  mkdirSync(versionedCacheDir, { recursive: true });
31260
31339
  }
31340
+ releaseLock = await acquireDownloadLock(lockPath);
31341
+ if (existsSync(binaryPath)) {
31342
+ return binaryPath;
31343
+ }
31344
+ binaryController = new AbortController;
31345
+ checksumController = new AbortController;
31346
+ const activeBinaryController = binaryController;
31347
+ const activeChecksumController = checksumController;
31348
+ binaryTimeout = setTimeout(() => activeBinaryController.abort(), DOWNLOAD_TIMEOUT_MS);
31349
+ checksumTimeout = setTimeout(() => activeChecksumController.abort(), DOWNLOAD_TIMEOUT_MS);
31261
31350
  const [binaryResponse, checksumResponse] = await Promise.all([
31262
- fetch(downloadUrl, { redirect: "follow" }),
31263
- fetch(checksumUrl, { redirect: "follow" })
31351
+ fetch(downloadUrl, { redirect: "follow", signal: activeBinaryController.signal }),
31352
+ fetch(checksumUrl, { redirect: "follow", signal: activeChecksumController.signal })
31264
31353
  ]);
31265
31354
  if (!binaryResponse.ok) {
31266
31355
  throw new Error(`HTTP ${binaryResponse.status}: ${binaryResponse.statusText} (${downloadUrl})`);
31267
31356
  }
31268
- const arrayBuffer = await binaryResponse.arrayBuffer();
31357
+ if (!binaryResponse.body) {
31358
+ throw new Error(`Download response for ${assetName} had no body`);
31359
+ }
31360
+ const advertised = Number.parseInt(binaryResponse.headers.get("content-length") ?? "", 10);
31361
+ if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES) {
31362
+ throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES}`);
31363
+ }
31269
31364
  if (!checksumResponse.ok) {
31270
31365
  warn(`Checksum verification failed: no checksums.sha256 found for ${tag}. ` + "Binary download aborted for security reasons.");
31271
31366
  return null;
31272
31367
  }
31273
31368
  const checksumText = await checksumResponse.text();
31369
+ clearTimeout(checksumTimeout);
31370
+ checksumTimeout = null;
31274
31371
  const expectedHash = parseChecksumForAsset(checksumText, assetName);
31275
31372
  if (!expectedHash) {
31276
31373
  warn(`Checksum verification failed: checksums.sha256 found but no entry for ${assetName}. ` + "Binary download aborted for security reasons.");
31277
31374
  return null;
31278
31375
  }
31279
- const { createHash } = await import("node:crypto");
31280
- const actualHash = createHash("sha256").update(Buffer.from(arrayBuffer)).digest("hex");
31376
+ const hash = createHash("sha256");
31377
+ let bytesWritten = 0;
31378
+ const guard = new TransformStream({
31379
+ transform(chunk, controller) {
31380
+ bytesWritten += chunk.byteLength;
31381
+ if (bytesWritten > MAX_DOWNLOAD_BYTES) {
31382
+ controller.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES} bytes after streaming (server lied about size or sent unbounded body)`));
31383
+ return;
31384
+ }
31385
+ hash.update(chunk);
31386
+ controller.enqueue(chunk);
31387
+ }
31388
+ });
31389
+ const guarded = binaryResponse.body.pipeThrough(guard);
31390
+ const nodeStream = Readable.fromWeb(guarded);
31391
+ await pipeline(nodeStream, createWriteStream(tmpPath), { signal: binaryController.signal });
31392
+ clearTimeout(binaryTimeout);
31393
+ binaryTimeout = null;
31394
+ const actualHash = hash.digest("hex");
31281
31395
  if (actualHash !== expectedHash) {
31282
- throw new Error(`Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}. The binary may have been tampered with.`);
31396
+ throw new Error(`Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}. ` + "The binary may have been tampered with.");
31283
31397
  }
31284
31398
  log(`Checksum verified (SHA-256: ${actualHash.slice(0, 16)}...)`);
31285
- const tmpPath = `${binaryPath}.tmp`;
31286
- const { writeFileSync } = await import("node:fs");
31287
- writeFileSync(tmpPath, Buffer.from(arrayBuffer));
31288
31399
  if (process.platform !== "win32") {
31289
31400
  chmodSync(tmpPath, 493);
31290
31401
  }
31291
- const { renameSync } = await import("node:fs");
31292
31402
  renameSync(tmpPath, binaryPath);
31293
31403
  log(`AFT binary ready at ${binaryPath}`);
31294
31404
  return binaryPath;
31295
31405
  } catch (err) {
31296
31406
  const msg = err instanceof Error ? err.message : String(err);
31297
31407
  error(`Failed to download AFT binary: ${msg}`);
31298
- const tmpPath = `${binaryPath}.tmp`;
31299
31408
  if (existsSync(tmpPath)) {
31300
31409
  try {
31301
31410
  unlinkSync(tmpPath);
31302
31411
  } catch {}
31303
31412
  }
31304
31413
  return null;
31414
+ } finally {
31415
+ if (binaryTimeout) {
31416
+ binaryController?.abort();
31417
+ clearTimeout(binaryTimeout);
31418
+ }
31419
+ if (checksumTimeout) {
31420
+ checksumController?.abort();
31421
+ clearTimeout(checksumTimeout);
31422
+ }
31423
+ releaseLock?.();
31305
31424
  }
31306
31425
  }
31307
31426
  async function ensureBinary(version) {
@@ -31318,6 +31437,39 @@ async function ensureBinary(version) {
31318
31437
  log("No cached binary found, downloading latest...");
31319
31438
  return downloadBinary();
31320
31439
  }
31440
+ async function acquireDownloadLock(lockPath) {
31441
+ const startedAt = Date.now();
31442
+ while (true) {
31443
+ try {
31444
+ const fd = openSync(lockPath, "wx");
31445
+ return () => {
31446
+ try {
31447
+ closeSync(fd);
31448
+ } catch {}
31449
+ try {
31450
+ rmSync(lockPath, { force: true });
31451
+ } catch {}
31452
+ };
31453
+ } catch (err) {
31454
+ const code = err.code;
31455
+ if (code !== "EEXIST")
31456
+ throw err;
31457
+ try {
31458
+ const ageMs = Date.now() - statSync(lockPath).mtimeMs;
31459
+ if (ageMs > DOWNLOAD_LOCK_STALE_MS) {
31460
+ rmSync(lockPath, { force: true });
31461
+ continue;
31462
+ }
31463
+ } catch {
31464
+ continue;
31465
+ }
31466
+ if (Date.now() - startedAt > DOWNLOAD_LOCK_TIMEOUT_MS) {
31467
+ throw new Error(`Timed out waiting for download lock: ${lockPath}`);
31468
+ }
31469
+ await new Promise((resolve) => setTimeout(resolve, 100));
31470
+ }
31471
+ }
31472
+ }
31321
31473
  function parseChecksumForAsset(checksumText, assetName) {
31322
31474
  for (const line of checksumText.split(`
31323
31475
  `)) {
@@ -31332,9 +31484,12 @@ function parseChecksumForAsset(checksumText, assetName) {
31332
31484
  return null;
31333
31485
  }
31334
31486
  async function fetchLatestTag() {
31487
+ const controller = new AbortController;
31488
+ const timeout = setTimeout(() => controller.abort(), LATEST_TAG_TIMEOUT_MS);
31335
31489
  try {
31336
31490
  const response = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
31337
- headers: { Accept: "application/vnd.github.v3+json" }
31491
+ headers: { Accept: "application/vnd.github.v3+json" },
31492
+ signal: controller.signal
31338
31493
  });
31339
31494
  if (!response.ok)
31340
31495
  return null;
@@ -31342,18 +31497,20 @@ async function fetchLatestTag() {
31342
31497
  return data.tag_name ?? null;
31343
31498
  } catch {
31344
31499
  return null;
31500
+ } finally {
31501
+ clearTimeout(timeout);
31345
31502
  }
31346
31503
  }
31347
31504
  // ../aft-bridge/dist/onnx-runtime.js
31348
31505
  import { execFileSync } from "node:child_process";
31349
- import { createHash } from "node:crypto";
31350
- import { chmodSync as chmodSync2, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, lstatSync, mkdirSync as mkdirSync2, openSync, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
31506
+ import { createHash as createHash2 } from "node:crypto";
31507
+ import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync, createWriteStream as createWriteStream2, existsSync as existsSync2, lstatSync, mkdirSync as mkdirSync2, openSync as openSync2, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
31351
31508
  import { dirname, join as join3, relative, resolve } from "node:path";
31352
- import { Readable } from "node:stream";
31353
- import { pipeline } from "node:stream/promises";
31509
+ import { Readable as Readable2 } from "node:stream";
31510
+ import { pipeline as pipeline2 } from "node:stream/promises";
31354
31511
  var ORT_VERSION = "1.24.4";
31355
31512
  var ORT_REPO = "microsoft/onnxruntime";
31356
- var MAX_DOWNLOAD_BYTES = 256 * 1024 * 1024;
31513
+ var MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
31357
31514
  var MAX_EXTRACT_BYTES = 1 * 1024 * 1024 * 1024;
31358
31515
  var ONNX_LOCK_FILE = ".aft-onnx-installing";
31359
31516
  var ONNX_INSTALLED_META_FILE = ".aft-onnx-installed";
@@ -31471,7 +31628,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
31471
31628
  if (Number.isFinite(pid) && pid > 0) {
31472
31629
  if (process.platform === "win32") {
31473
31630
  try {
31474
- const ageMs = Date.now() - statSync(stagingDir).mtimeMs;
31631
+ const ageMs = Date.now() - statSync2(stagingDir).mtimeMs;
31475
31632
  abandoned = ageMs > STALE_LOCK_MS;
31476
31633
  } catch {
31477
31634
  abandoned = true;
@@ -31485,7 +31642,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
31485
31642
  if (abandoned) {
31486
31643
  log(`[onnx] removing abandoned staging dir ${stagingDir}`);
31487
31644
  try {
31488
- rmSync(stagingDir, { recursive: true, force: true });
31645
+ rmSync2(stagingDir, { recursive: true, force: true });
31489
31646
  } catch (err) {
31490
31647
  warn(`[onnx] failed to remove ${stagingDir}: ${err}`);
31491
31648
  }
@@ -31497,7 +31654,7 @@ function cleanupIncompleteTargetIfUnowned(ortDir) {
31497
31654
  try {
31498
31655
  if (existsSync2(ortDir) && !existsSync2(join3(ortDir, ONNX_INSTALLED_META_FILE))) {
31499
31656
  log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
31500
- rmSync(ortDir, { recursive: true, force: true });
31657
+ rmSync2(ortDir, { recursive: true, force: true });
31501
31658
  }
31502
31659
  } catch (err) {
31503
31660
  warn(`[onnx] failed to sweep ${ortDir}: ${err}`);
@@ -31577,24 +31734,24 @@ async function downloadFileWithCap(url, destPath) {
31577
31734
  throw new Error(`download failed (HTTP ${res.status})`);
31578
31735
  }
31579
31736
  const advertised = Number.parseInt(res.headers.get("content-length") ?? "", 10);
31580
- if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES) {
31581
- throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES}`);
31737
+ if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
31738
+ throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
31582
31739
  }
31583
31740
  mkdirSync2(dirname(destPath), { recursive: true });
31584
31741
  let bytesWritten = 0;
31585
31742
  const guard = new TransformStream({
31586
31743
  transform(chunk, transformController) {
31587
31744
  bytesWritten += chunk.byteLength;
31588
- if (bytesWritten > MAX_DOWNLOAD_BYTES) {
31589
- transformController.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES} bytes after streaming (server lied about size or sent unbounded body)`));
31745
+ if (bytesWritten > MAX_DOWNLOAD_BYTES2) {
31746
+ transformController.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES2} bytes after streaming (server lied about size or sent unbounded body)`));
31590
31747
  return;
31591
31748
  }
31592
31749
  transformController.enqueue(chunk);
31593
31750
  }
31594
31751
  });
31595
31752
  const guarded = res.body.pipeThrough(guard);
31596
- const nodeStream = Readable.fromWeb(guarded);
31597
- await pipeline(nodeStream, createWriteStream(destPath), { signal: controller.signal });
31753
+ const nodeStream = Readable2.fromWeb(guarded);
31754
+ await pipeline2(nodeStream, createWriteStream2(destPath), { signal: controller.signal });
31598
31755
  } catch (err) {
31599
31756
  try {
31600
31757
  unlinkSync2(destPath);
@@ -31693,16 +31850,16 @@ async function downloadOnnxRuntime(info, targetDir) {
31693
31850
  warn(`Could not hash newly-installed ONNX library at ${libPath}: ${err}`);
31694
31851
  }
31695
31852
  writeOnnxInstalledMeta(targetDir, ORT_VERSION, libHash, archiveSha256);
31696
- rmSync(tmpDir, { recursive: true, force: true });
31853
+ rmSync2(tmpDir, { recursive: true, force: true });
31697
31854
  log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
31698
31855
  return targetDir;
31699
31856
  } catch (err) {
31700
31857
  error(`Failed to download ONNX Runtime: ${err}`);
31701
31858
  try {
31702
- rmSync(tmpDir, { recursive: true, force: true });
31859
+ rmSync2(tmpDir, { recursive: true, force: true });
31703
31860
  } catch {}
31704
31861
  try {
31705
- rmSync(targetDir, { recursive: true, force: true });
31862
+ rmSync2(targetDir, { recursive: true, force: true });
31706
31863
  } catch {}
31707
31864
  return null;
31708
31865
  }
@@ -31719,7 +31876,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
31719
31876
  }
31720
31877
  } catch (copyErr) {
31721
31878
  if (requiredLibs.has(libFile)) {
31722
- rmSync(targetDir, { recursive: true, force: true });
31879
+ rmSync2(targetDir, { recursive: true, force: true });
31723
31880
  throw copyErr;
31724
31881
  }
31725
31882
  log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
@@ -31734,7 +31891,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
31734
31891
  symlinkSync(link.target, dst);
31735
31892
  } catch (symlinkErr) {
31736
31893
  if (requiredLibs.has(link.name)) {
31737
- rmSync(targetDir, { recursive: true, force: true });
31894
+ rmSync2(targetDir, { recursive: true, force: true });
31738
31895
  throw symlinkErr;
31739
31896
  }
31740
31897
  log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
@@ -31742,7 +31899,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
31742
31899
  }
31743
31900
  const requiredPath = join3(targetDir, info.libName);
31744
31901
  if (!existsSync2(requiredPath)) {
31745
- rmSync(targetDir, { recursive: true, force: true });
31902
+ rmSync2(targetDir, { recursive: true, force: true });
31746
31903
  throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
31747
31904
  }
31748
31905
  }
@@ -31775,7 +31932,7 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
31775
31932
  function readOnnxInstalledMeta(installDir) {
31776
31933
  const path = join3(installDir, ONNX_INSTALLED_META_FILE);
31777
31934
  try {
31778
- if (!statSync(path).isFile())
31935
+ if (!statSync2(path).isFile())
31779
31936
  return null;
31780
31937
  const raw = readFileSync(path, "utf8");
31781
31938
  const parsed = JSON.parse(raw);
@@ -31792,20 +31949,20 @@ function readOnnxInstalledMeta(installDir) {
31792
31949
  }
31793
31950
  }
31794
31951
  function sha256File(path) {
31795
- const hash = createHash("sha256");
31952
+ const hash = createHash2("sha256");
31796
31953
  hash.update(readFileSync(path));
31797
31954
  return hash.digest("hex");
31798
31955
  }
31799
31956
  function acquireLock(lockPath) {
31800
31957
  const tryClaim = () => {
31801
31958
  try {
31802
- const fd = openSync(lockPath, "wx");
31959
+ const fd = openSync2(lockPath, "wx");
31803
31960
  try {
31804
31961
  writeFileSync(fd, `${process.pid}
31805
31962
  ${new Date().toISOString()}
31806
31963
  `);
31807
31964
  } finally {
31808
- closeSync(fd);
31965
+ closeSync2(fd);
31809
31966
  }
31810
31967
  return true;
31811
31968
  } catch (err) {
@@ -31826,7 +31983,7 @@ ${new Date().toISOString()}
31826
31983
  const parsed = Number.parseInt(firstLine, 10);
31827
31984
  if (Number.isFinite(parsed) && parsed > 0)
31828
31985
  owningPid = parsed;
31829
- lockMtimeMs = statSync(lockPath).mtimeMs;
31986
+ lockMtimeMs = statSync2(lockPath).mtimeMs;
31830
31987
  } catch {
31831
31988
  return tryClaim();
31832
31989
  }
@@ -31943,7 +32100,9 @@ class BridgePool {
31943
32100
  onVersionMismatch: options.onVersionMismatch,
31944
32101
  onConfigureWarnings: options.onConfigureWarnings,
31945
32102
  onBashCompletion: options.onBashCompletion,
31946
- onBashLongRunning: options.onBashLongRunning
32103
+ onBashLongRunning: options.onBashLongRunning,
32104
+ errorPrefix: options.errorPrefix,
32105
+ logger: options.logger
31947
32106
  };
31948
32107
  this.configOverrides = configOverrides;
31949
32108
  if (Number.isFinite(this.idleTimeoutMs)) {
@@ -32015,24 +32174,32 @@ class BridgePool {
32015
32174
  }
32016
32175
  async replaceBinary(newPath) {
32017
32176
  this.binaryPath = newPath;
32018
- const shutdowns = Array.from(this.bridges.values()).map((entry) => entry.bridge.shutdown());
32019
32177
  this.bridges.clear();
32020
- await Promise.allSettled(shutdowns);
32021
32178
  this.log(`Binary path updated to ${newPath}. All bridges cleared — next calls will use the new binary.`);
32022
32179
  return newPath;
32023
32180
  }
32024
32181
  log(message, meta) {
32025
32182
  const logger = this.logger ?? getActiveLogger();
32026
- if (logger)
32027
- logger.log(message, meta);
32028
- else
32183
+ if (logger) {
32184
+ try {
32185
+ logger.log(message, meta);
32186
+ } catch (err) {
32187
+ console.error(`[aft-bridge] ERROR: pool logger log threw: ${err instanceof Error ? err.message : String(err)}`);
32188
+ console.error(`[aft-bridge] ${message}`);
32189
+ }
32190
+ } else
32029
32191
  log(message, meta);
32030
32192
  }
32031
32193
  error(message, meta) {
32032
32194
  const logger = this.logger ?? getActiveLogger();
32033
- if (logger)
32034
- logger.error(message, meta);
32035
- else
32195
+ if (logger) {
32196
+ try {
32197
+ logger.error(message, meta);
32198
+ } catch (err) {
32199
+ console.error(`[aft-bridge] ERROR: pool logger error threw: ${err instanceof Error ? err.message : String(err)}`);
32200
+ console.error(`[aft-bridge] ERROR: ${message}`);
32201
+ }
32202
+ } else
32036
32203
  error(message, meta);
32037
32204
  }
32038
32205
  setConfigureOverride(key, value) {
@@ -32059,7 +32226,7 @@ function normalizeKey(projectRoot) {
32059
32226
  }
32060
32227
  // ../aft-bridge/dist/resolver.js
32061
32228
  import { execSync, spawnSync } from "node:child_process";
32062
- import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync } from "node:fs";
32229
+ import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync2 } from "node:fs";
32063
32230
  import { createRequire as createRequire2 } from "node:module";
32064
32231
  import { homedir as homedir4 } from "node:os";
32065
32232
  import { join as join4 } from "node:path";
@@ -32090,15 +32257,19 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
32090
32257
  const versionedDir = join4(cacheDir, tag);
32091
32258
  const ext = process.platform === "win32" ? ".exe" : "";
32092
32259
  const cachedPath = join4(versionedDir, `aft${ext}`);
32093
- if (existsSync3(cachedPath))
32094
- return cachedPath;
32260
+ if (existsSync3(cachedPath)) {
32261
+ const cachedVersion = readBinaryVersion(cachedPath);
32262
+ if (cachedVersion === version)
32263
+ return cachedPath;
32264
+ warn(`Cached binary at ${cachedPath} reports ${cachedVersion ?? "no version"}, expected ${version}; refreshing from npm package`);
32265
+ }
32095
32266
  mkdirSync3(versionedDir, { recursive: true });
32096
- const tmpPath = `${cachedPath}.tmp`;
32267
+ const tmpPath = `${cachedPath}.${process.pid}.${Date.now()}.tmp`;
32097
32268
  copyFileSync2(npmBinaryPath, tmpPath);
32098
32269
  if (process.platform !== "win32") {
32099
32270
  chmodSync3(tmpPath, 493);
32100
32271
  }
32101
- renameSync(tmpPath, cachedPath);
32272
+ renameSync2(tmpPath, cachedPath);
32102
32273
  log(`Copied npm binary to versioned cache: ${cachedPath}`);
32103
32274
  return cachedPath;
32104
32275
  } catch (err) {
@@ -32106,6 +32277,17 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
32106
32277
  return null;
32107
32278
  }
32108
32279
  }
32280
+ function normalizeBareVersion(version) {
32281
+ return version.startsWith("v") ? version.slice(1) : version;
32282
+ }
32283
+ function isExpectedCachedBinary(binaryPath, expectedVersion) {
32284
+ const expected = normalizeBareVersion(expectedVersion);
32285
+ const actual = readBinaryVersion(binaryPath);
32286
+ if (actual === expected)
32287
+ return true;
32288
+ warn(`Cached binary at ${binaryPath} reports ${actual ?? "no version"}, expected ${expected}; skipping cache candidate`);
32289
+ return false;
32290
+ }
32109
32291
  function platformKey(platform = process.platform, arch = process.arch) {
32110
32292
  const archMap = PLATFORM_ARCH_MAP[platform];
32111
32293
  if (!archMap) {
@@ -32130,7 +32312,7 @@ function findBinarySync(expectedVersion) {
32130
32312
  if (pluginVersion) {
32131
32313
  const tag = pluginVersion.startsWith("v") ? pluginVersion : `v${pluginVersion}`;
32132
32314
  const versionCached = getCachedBinaryPath(tag);
32133
- if (versionCached)
32315
+ if (versionCached && isExpectedCachedBinary(versionCached, pluginVersion))
32134
32316
  return versionCached;
32135
32317
  }
32136
32318
  try {
@@ -32140,10 +32322,12 @@ function findBinarySync(expectedVersion) {
32140
32322
  const resolved = req.resolve(packageBin);
32141
32323
  if (existsSync3(resolved)) {
32142
32324
  const npmVersion = readBinaryVersion(resolved);
32143
- if (pluginVersion && npmVersion && npmVersion !== pluginVersion) {
32325
+ if (npmVersion === null) {
32326
+ warn(`npm platform package binary at ${resolved} did not report a version; skipping (continuing to PATH lookup)`);
32327
+ } else if (pluginVersion && npmVersion !== normalizeBareVersion(pluginVersion)) {
32144
32328
  warn(`npm platform package binary v${npmVersion} does not match plugin v${pluginVersion}; skipping (continuing to PATH lookup)`);
32145
32329
  } else {
32146
- const copied = copyToVersionedCache(resolved, npmVersion ?? undefined);
32330
+ const copied = copyToVersionedCache(resolved, npmVersion);
32147
32331
  return copied ?? resolved;
32148
32332
  }
32149
32333
  }
@@ -32192,7 +32376,7 @@ async function findBinary(expectedVersion) {
32192
32376
  `));
32193
32377
  }
32194
32378
  // ../aft-bridge/dist/url-fetch.js
32195
- import { createHash as createHash2 } from "node:crypto";
32379
+ import { createHash as createHash3 } from "node:crypto";
32196
32380
  import { lookup } from "node:dns/promises";
32197
32381
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
32198
32382
  import { isIP } from "node:net";
@@ -32349,7 +32533,7 @@ function cacheDir(storageDir) {
32349
32533
  return join5(storageDir, "url_cache");
32350
32534
  }
32351
32535
  function hashUrl(url) {
32352
- return createHash2("sha256").update(url).digest("hex").slice(0, 16);
32536
+ return createHash3("sha256").update(url).digest("hex").slice(0, 16);
32353
32537
  }
32354
32538
  function metaPath(storageDir, hash) {
32355
32539
  return join5(cacheDir(storageDir), `${hash}.meta.json`);
@@ -32607,8 +32791,8 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
32607
32791
  const contentFile = contentPath(storageDir, hash, extension);
32608
32792
  const tmpContent = `${contentFile}.tmp-${process.pid}`;
32609
32793
  writeFileSync2(tmpContent, body);
32610
- const { renameSync: renameSync2 } = await import("node:fs");
32611
- renameSync2(tmpContent, contentFile);
32794
+ const { renameSync: renameSync3 } = await import("node:fs");
32795
+ renameSync3(tmpContent, contentFile);
32612
32796
  const meta = {
32613
32797
  url,
32614
32798
  contentType,
@@ -32617,7 +32801,7 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
32617
32801
  };
32618
32802
  const tmpMeta = `${metaFile}.tmp-${process.pid}`;
32619
32803
  writeFileSync2(tmpMeta, JSON.stringify(meta));
32620
- renameSync2(tmpMeta, metaFile);
32804
+ renameSync3(tmpMeta, metaFile);
32621
32805
  log(`URL cached (${total} bytes): ${url}`);
32622
32806
  return contentFile;
32623
32807
  }
@@ -32735,31 +32919,31 @@ function warn2(message, data) {
32735
32919
  function error2(message, data) {
32736
32920
  write("ERROR", message, data);
32737
32921
  }
32738
- function sessionLog2(sessionId, message, data) {
32922
+ function sessionLog(sessionId, message, data) {
32739
32923
  write("INFO", message, data, sessionId);
32740
32924
  }
32741
- function sessionWarn2(sessionId, message, data) {
32925
+ function sessionWarn(sessionId, message, data) {
32742
32926
  write("WARN", message, data, sessionId);
32743
32927
  }
32744
- function sessionError2(sessionId, message, data) {
32928
+ function sessionError(sessionId, message, data) {
32745
32929
  write("ERROR", message, data, sessionId);
32746
32930
  }
32747
32931
  var bridgeLogger = {
32748
32932
  log(message, meta) {
32749
32933
  if (meta?.sessionId)
32750
- sessionLog2(meta.sessionId, message);
32934
+ sessionLog(meta.sessionId, message);
32751
32935
  else
32752
32936
  write("INFO", message);
32753
32937
  },
32754
32938
  warn(message, meta) {
32755
32939
  if (meta?.sessionId)
32756
- sessionWarn2(meta.sessionId, message);
32940
+ sessionWarn(meta.sessionId, message);
32757
32941
  else
32758
32942
  write("WARN", message);
32759
32943
  },
32760
32944
  error(message, meta) {
32761
32945
  if (meta?.sessionId)
32762
- sessionError2(meta.sessionId, message);
32946
+ sessionError(meta.sessionId, message);
32763
32947
  else
32764
32948
  write("ERROR", message);
32765
32949
  },
@@ -32771,6 +32955,7 @@ var sessionBgStates = new Map;
32771
32955
  var SESSION_BG_STATE_IDLE_TTL_MS = 60 * 60 * 1000;
32772
32956
  var DEBOUNCE_STEP_MS = 200;
32773
32957
  var DEBOUNCE_CAP_MS = 1000;
32958
+ var MAX_WAKE_SEND_ATTEMPTS = 5;
32774
32959
  var UNKNOWN_COMPLETION_TTL_MS = 5000;
32775
32960
  var UNKNOWN_COMPLETION_CAP = 32;
32776
32961
  var DEFAULT_SESSION_ID = "__default__";
@@ -32820,16 +33005,22 @@ async function handlePushedBgLongRunning(drainContext, reminder) {
32820
33005
  }
32821
33006
  async function appendToolResultBgCompletions(drainContext, content) {
32822
33007
  const state = stateFor(drainContext.sessionID);
33008
+ if (state.outstandingTaskIds.size === 0 && state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
33009
+ await drainCompletions(drainContext);
32823
33010
  if (state.outstandingTaskIds.size === 0 && state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
32824
33011
  return;
32825
- if (state.outstandingTaskIds.size > 0) {
33012
+ if (state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted) {
32826
33013
  await drainCompletions(drainContext);
32827
33014
  }
32828
33015
  if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
32829
33016
  return;
33017
+ const deliveredCompletions = [...state.pendingCompletions];
32830
33018
  const reminder = formatCombinedSystemReminder(state.pendingCompletions, state.pendingLongRunning);
32831
33019
  state.pendingCompletions = [];
32832
33020
  state.pendingLongRunning = [];
33021
+ state.wakeRetryAttempts = 0;
33022
+ state.wakeHardStopped = false;
33023
+ await ackCompletions(drainContext, deliveredCompletions);
32833
33024
  if (state.debounceTimer) {
32834
33025
  clearTimeout(state.debounceTimer);
32835
33026
  state.debounceTimer = null;
@@ -32844,15 +33035,16 @@ async function handleTurnEndBgCompletions(drainContext) {
32844
33035
  }
32845
33036
  async function triggerWakeIfPending(drainContext, skipDrain) {
32846
33037
  const state = stateFor(drainContext.sessionID);
32847
- if (!skipDrain && state.outstandingTaskIds.size > 0) {
33038
+ if (!skipDrain && (state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted)) {
32848
33039
  await drainCompletions(drainContext);
32849
33040
  }
32850
33041
  if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
32851
33042
  return;
32852
- scheduleWake(state, async (reminder) => {
33043
+ scheduleWake(state, async (reminder, deliveredCompletions) => {
32853
33044
  drainContext.runtime.sendUserMessage(reminder, { deliverAs: "steer" });
32854
- }, (err) => {
32855
- sessionWarn2(drainContext.sessionID ?? "", `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
33045
+ await ackCompletions(drainContext, deliveredCompletions);
33046
+ }, (err, hardStopped) => {
33047
+ sessionWarn(drainContext.sessionID ?? "", hardStopped ? `${LOG_PREFIX} wake send failed ${MAX_WAKE_SEND_ATTEMPTS} times; stopping retries: ${err instanceof Error ? err.message : String(err)}` : `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
32856
33048
  });
32857
33049
  }
32858
33050
  function formatSystemReminder(completions) {
@@ -32885,20 +33077,39 @@ function formatCombinedSystemReminder(completions, longRunning) {
32885
33077
  ${formatLongRunningReminder(longRunning)}`;
32886
33078
  }
32887
33079
  async function drainCompletions({ ctx, directory, sessionID }) {
33080
+ const state = stateFor(sessionID);
32888
33081
  try {
32889
33082
  const bridge = ctx.pool.getActiveBridgeForRoot(directory) ?? ctx.pool.getBridge(directory);
32890
33083
  const params = sessionID ? { session_id: sessionID } : {};
32891
33084
  const response = await bridge.send("bash_drain_completions", params);
32892
33085
  if (response.success === false) {
32893
- sessionWarn2(sessionID ?? "", `${LOG_PREFIX} drain failed: ${String(response.message ?? "unknown error")}`);
33086
+ sessionWarn(sessionID ?? "", `${LOG_PREFIX} drain failed: ${String(response.message ?? "unknown error")}`);
32894
33087
  return;
32895
33088
  }
32896
- ingestBgCompletions(sessionID, response.bg_completions);
33089
+ state.forcedDrainCompleted = true;
33090
+ ingestDrainedBgCompletions(sessionID, response.bg_completions);
32897
33091
  } catch (err) {
32898
- sessionWarn2(sessionID ?? "", `${LOG_PREFIX} drain failed: ${err instanceof Error ? err.message : String(err)}`);
33092
+ sessionWarn(sessionID ?? "", `${LOG_PREFIX} drain failed: ${err instanceof Error ? err.message : String(err)}`);
33093
+ }
33094
+ }
33095
+ async function ackCompletions({ ctx, directory, sessionID }, completions) {
33096
+ const taskIds = [...new Set(completions.map((completion) => completion.task_id))];
33097
+ if (taskIds.length === 0)
33098
+ return;
33099
+ try {
33100
+ const bridge = ctx.pool.getActiveBridgeForRoot(directory) ?? ctx.pool.getBridge(directory);
33101
+ const params = sessionID ? { session_id: sessionID, task_ids: taskIds } : { task_ids: taskIds };
33102
+ const response = await bridge.send("bash_ack_completions", params);
33103
+ if (response.success === false) {
33104
+ sessionWarn(sessionID ?? "", `${LOG_PREFIX} ack failed: ${String(response.message ?? "unknown error")}`);
33105
+ }
33106
+ } catch (err) {
33107
+ sessionWarn(sessionID ?? "", `${LOG_PREFIX} ack failed: ${err instanceof Error ? err.message : String(err)}`);
32899
33108
  }
32900
33109
  }
32901
33110
  function scheduleWake(state, sendWake, onSendFailure) {
33111
+ if (state.wakeHardStopped)
33112
+ return;
32902
33113
  const now = Date.now();
32903
33114
  const pendingCount = state.pendingCompletions.length + state.pendingLongRunning.length;
32904
33115
  if (state.debounceTimer && pendingCount <= state.scheduledCompletionCount) {
@@ -32927,13 +33138,22 @@ function scheduleWake(state, sendWake, onSendFailure) {
32927
33138
  const reminder = formatCombinedSystemReminder(pending, pendingLongRunning);
32928
33139
  state.pendingCompletions = [];
32929
33140
  state.pendingLongRunning = [];
32930
- sendWake(reminder).then(() => {
33141
+ sendWake(reminder, pending).then(() => {
32931
33142
  state.retryDelayMs = null;
33143
+ state.wakeRetryAttempts = 0;
33144
+ state.wakeHardStopped = false;
32932
33145
  }).catch((err) => {
32933
33146
  state.pendingCompletions = [...pending, ...state.pendingCompletions];
32934
33147
  state.pendingLongRunning = [...pendingLongRunning, ...state.pendingLongRunning];
33148
+ state.wakeRetryAttempts += 1;
33149
+ if (state.wakeRetryAttempts >= MAX_WAKE_SEND_ATTEMPTS) {
33150
+ state.retryDelayMs = null;
33151
+ state.wakeHardStopped = true;
33152
+ onSendFailure(err, true);
33153
+ return;
33154
+ }
32935
33155
  state.retryDelayMs = Math.min((delay || DEBOUNCE_STEP_MS) * 2, DEBOUNCE_CAP_MS);
32936
- onSendFailure(err);
33156
+ onSendFailure(err, false);
32937
33157
  scheduleWake(state, sendWake, onSendFailure);
32938
33158
  });
32939
33159
  }, delay);
@@ -32954,6 +33174,9 @@ function stateFor(sessionID) {
32954
33174
  scheduledFireAt: null,
32955
33175
  scheduledCompletionCount: 0,
32956
33176
  retryDelayMs: null,
33177
+ wakeRetryAttempts: 0,
33178
+ wakeHardStopped: false,
33179
+ forcedDrainCompleted: false,
32957
33180
  unknownCompletions: [],
32958
33181
  lastSeenAt: now
32959
33182
  };
@@ -32963,6 +33186,22 @@ function stateFor(sessionID) {
32963
33186
  }
32964
33187
  return state;
32965
33188
  }
33189
+ function ingestDrainedBgCompletions(sessionID, completions) {
33190
+ if (!Array.isArray(completions) || completions.length === 0)
33191
+ return [];
33192
+ const state = stateFor(sessionID);
33193
+ const accepted = [];
33194
+ for (const completion of completions) {
33195
+ if (!isBgCompletion(completion))
33196
+ continue;
33197
+ state.outstandingTaskIds.delete(completion.task_id);
33198
+ if (!state.pendingCompletions.some((pending) => pending.task_id === completion.task_id) && !accepted.some((pending) => pending.task_id === completion.task_id)) {
33199
+ accepted.push(completion);
33200
+ }
33201
+ }
33202
+ state.pendingCompletions.push(...accepted);
33203
+ return accepted;
33204
+ }
32966
33205
  function cleanupIdleSessionStates(now = Date.now()) {
32967
33206
  const cutoff = now - SESSION_BG_STATE_IDLE_TTL_MS;
32968
33207
  for (const [sessionID, state] of sessionBgStates) {
@@ -33055,7 +33294,7 @@ import {
33055
33294
  // package.json
33056
33295
  var package_default = {
33057
33296
  name: "@cortexkit/aft-pi",
33058
- version: "0.26.0",
33297
+ version: "0.26.1",
33059
33298
  type: "module",
33060
33299
  description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
33061
33300
  main: "dist/index.js",
@@ -33077,18 +33316,18 @@ var package_default = {
33077
33316
  prepublishOnly: "bun run build"
33078
33317
  },
33079
33318
  dependencies: {
33080
- "@cortexkit/aft-bridge": "0.26.0",
33319
+ "@cortexkit/aft-bridge": "0.26.1",
33081
33320
  typebox: "^1.1.24",
33082
33321
  "comment-json": "^5.0.0",
33083
33322
  diff: "^8.0.4",
33084
33323
  zod: "^4.1.8"
33085
33324
  },
33086
33325
  optionalDependencies: {
33087
- "@cortexkit/aft-darwin-arm64": "0.26.0",
33088
- "@cortexkit/aft-darwin-x64": "0.26.0",
33089
- "@cortexkit/aft-linux-arm64": "0.26.0",
33090
- "@cortexkit/aft-linux-x64": "0.26.0",
33091
- "@cortexkit/aft-win32-x64": "0.26.0"
33326
+ "@cortexkit/aft-darwin-arm64": "0.26.1",
33327
+ "@cortexkit/aft-darwin-x64": "0.26.1",
33328
+ "@cortexkit/aft-linux-arm64": "0.26.1",
33329
+ "@cortexkit/aft-linux-x64": "0.26.1",
33330
+ "@cortexkit/aft-win32-x64": "0.26.1"
33092
33331
  },
33093
33332
  devDependencies: {
33094
33333
  "@earendil-works/pi-coding-agent": "*",
@@ -33573,7 +33812,7 @@ function registerStatusCommand(pi, ctx) {
33573
33812
 
33574
33813
  // src/config.ts
33575
33814
  var import_comment_json = __toESM(require_src2(), 1);
33576
- import { existsSync as existsSync5, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
33815
+ import { existsSync as existsSync5, readFileSync as readFileSync3, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
33577
33816
  import { homedir as homedir5 } from "node:os";
33578
33817
  import { join as join7 } from "node:path";
33579
33818
 
@@ -47348,7 +47587,7 @@ function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
47348
47587
  ${serialized}` : serialized;
47349
47588
  tmpPath = `${configPath}.tmp.${process.pid}`;
47350
47589
  writeFileSync3(tmpPath, nextContent, "utf-8");
47351
- renameSync2(tmpPath, configPath);
47590
+ renameSync3(tmpPath, configPath);
47352
47591
  logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
47353
47592
  return { migrated: true, oldKeys };
47354
47593
  } catch (err) {
@@ -47563,17 +47802,17 @@ function loadAftConfig(projectDirectory) {
47563
47802
 
47564
47803
  // src/lsp-auto-install.ts
47565
47804
  import { spawn as spawn2 } from "node:child_process";
47566
- import { createHash as createHash3 } from "node:crypto";
47567
- import { createReadStream, mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
47805
+ import { createHash as createHash4 } from "node:crypto";
47806
+ import { createReadStream, mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
47568
47807
  import { join as join10 } from "node:path";
47569
47808
 
47570
47809
  // src/lsp-cache.ts
47571
47810
  import {
47572
- closeSync as closeSync2,
47811
+ closeSync as closeSync3,
47573
47812
  mkdirSync as mkdirSync5,
47574
- openSync as openSync2,
47813
+ openSync as openSync3,
47575
47814
  readFileSync as readFileSync4,
47576
- statSync as statSync2,
47815
+ statSync as statSync3,
47577
47816
  unlinkSync as unlinkSync5,
47578
47817
  writeFileSync as writeFileSync4
47579
47818
  } from "node:fs";
@@ -47606,7 +47845,7 @@ function lspBinDir(npmPackage) {
47606
47845
  function isInstalled(npmPackage, binary) {
47607
47846
  for (const candidate of lspBinaryCandidates(binary)) {
47608
47847
  try {
47609
- if (statSync2(join8(lspBinDir(npmPackage), candidate)).isFile())
47848
+ if (statSync3(join8(lspBinDir(npmPackage), candidate)).isFile())
47610
47849
  return true;
47611
47850
  } catch {}
47612
47851
  }
@@ -47634,7 +47873,7 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
47634
47873
  function readInstalledMetaIn(installDir) {
47635
47874
  const path2 = join8(installDir, INSTALLED_META_FILE);
47636
47875
  try {
47637
- if (!statSync2(path2).isFile())
47876
+ if (!statSync3(path2).isFile())
47638
47877
  return null;
47639
47878
  const raw = readFileSync4(path2, "utf8");
47640
47879
  const parsed = JSON.parse(raw);
@@ -47664,13 +47903,13 @@ function acquireInstallLock(lockKey) {
47664
47903
  const lock = lockPath(lockKey);
47665
47904
  const tryClaim = () => {
47666
47905
  try {
47667
- const fd = openSync2(lock, "wx");
47906
+ const fd = openSync3(lock, "wx");
47668
47907
  try {
47669
47908
  writeFileSync4(fd, `${process.pid}
47670
47909
  ${new Date().toISOString()}
47671
47910
  `);
47672
47911
  } finally {
47673
- closeSync2(fd);
47912
+ closeSync3(fd);
47674
47913
  }
47675
47914
  return true;
47676
47915
  } catch (err) {
@@ -47691,7 +47930,7 @@ ${new Date().toISOString()}
47691
47930
  const parsed = Number.parseInt(firstLine, 10);
47692
47931
  if (Number.isFinite(parsed) && parsed > 0)
47693
47932
  owningPid = parsed;
47694
- lockMtimeMs = statSync2(lock).mtimeMs;
47933
+ lockMtimeMs = statSync3(lock).mtimeMs;
47695
47934
  } catch {
47696
47935
  return tryClaim();
47697
47936
  }
@@ -48233,7 +48472,7 @@ function hashInstalledBinary(spec) {
48233
48472
  let pathToHash = null;
48234
48473
  for (const p of candidates) {
48235
48474
  try {
48236
- if (statSync3(p).isFile()) {
48475
+ if (statSync4(p).isFile()) {
48237
48476
  pathToHash = p;
48238
48477
  break;
48239
48478
  }
@@ -48243,7 +48482,7 @@ function hashInstalledBinary(spec) {
48243
48482
  reject(new Error(`installed binary not found at any of: ${candidates.join(", ")}`));
48244
48483
  return;
48245
48484
  }
48246
- const hash2 = createHash3("sha256");
48485
+ const hash2 = createHash4("sha256");
48247
48486
  const stream = createReadStream(pathToHash);
48248
48487
  stream.on("error", reject);
48249
48488
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -48259,14 +48498,14 @@ function installedBinaryPath(spec) {
48259
48498
  ] : [lspBinaryPath(spec.npm, spec.binary)];
48260
48499
  for (const candidate of candidates) {
48261
48500
  try {
48262
- if (statSync3(candidate).isFile())
48501
+ if (statSync4(candidate).isFile())
48263
48502
  return candidate;
48264
48503
  } catch {}
48265
48504
  }
48266
48505
  return null;
48267
48506
  }
48268
48507
  function sha256OfFileSync(path2) {
48269
- return createHash3("sha256").update(readFileSync5(path2)).digest("hex");
48508
+ return createHash4("sha256").update(readFileSync5(path2)).digest("hex");
48270
48509
  }
48271
48510
  function quarantineCachedNpmInstall(spec, reason) {
48272
48511
  const packageDir = lspPackageDir(spec.npm);
@@ -48274,8 +48513,8 @@ function quarantineCachedNpmInstall(spec, reason) {
48274
48513
  warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
48275
48514
  try {
48276
48515
  mkdirSync6(join10(dest, ".."), { recursive: true });
48277
- rmSync2(dest, { recursive: true, force: true });
48278
- renameSync3(packageDir, dest);
48516
+ rmSync3(dest, { recursive: true, force: true });
48517
+ renameSync4(packageDir, dest);
48279
48518
  } catch (err) {
48280
48519
  warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
48281
48520
  }
@@ -48351,11 +48590,11 @@ function runAutoInstall(projectRoot, config2, fetchImpl2 = fetch) {
48351
48590
 
48352
48591
  // src/lsp-github-install.ts
48353
48592
  import { execFileSync as execFileSync2 } from "node:child_process";
48354
- import { createHash as createHash4, randomBytes } from "node:crypto";
48593
+ import { createHash as createHash5, randomBytes } from "node:crypto";
48355
48594
  import {
48356
48595
  copyFileSync as copyFileSync3,
48357
48596
  createReadStream as createReadStream2,
48358
- createWriteStream as createWriteStream2,
48597
+ createWriteStream as createWriteStream3,
48359
48598
  existsSync as existsSync7,
48360
48599
  lstatSync as lstatSync2,
48361
48600
  mkdirSync as mkdirSync7,
@@ -48363,14 +48602,15 @@ import {
48363
48602
  readFileSync as readFileSync6,
48364
48603
  readlinkSync as readlinkSync2,
48365
48604
  realpathSync as realpathSync3,
48366
- renameSync as renameSync4,
48367
- rmSync as rmSync3,
48368
- statSync as statSync4,
48369
- unlinkSync as unlinkSync6
48605
+ renameSync as renameSync5,
48606
+ rmSync as rmSync4,
48607
+ statSync as statSync5,
48608
+ unlinkSync as unlinkSync6,
48609
+ writeFileSync as writeFileSync5
48370
48610
  } from "node:fs";
48371
48611
  import { dirname as dirname2, join as join11, relative as relative2, resolve as resolve2 } from "node:path";
48372
- import { Readable as Readable2 } from "node:stream";
48373
- import { pipeline as pipeline2 } from "node:stream/promises";
48612
+ import { Readable as Readable3 } from "node:stream";
48613
+ import { pipeline as pipeline3 } from "node:stream/promises";
48374
48614
 
48375
48615
  // src/lsp-github-table.ts
48376
48616
  function exe(platform, name) {
@@ -48469,6 +48709,7 @@ function ghBinDir(spec) {
48469
48709
  function ghExtractDir(spec) {
48470
48710
  return join11(ghPackageDir(spec), "extracted");
48471
48711
  }
48712
+ var INSTALLED_META_FILE2 = ".aft-installed";
48472
48713
  function ghBinaryPath(spec, platform) {
48473
48714
  const ext = platform === "win32" ? ".exe" : "";
48474
48715
  return join11(ghBinDir(spec), `${spec.binary}${ext}`);
@@ -48476,7 +48717,7 @@ function ghBinaryPath(spec, platform) {
48476
48717
  function isGithubInstalled(spec, platform) {
48477
48718
  for (const candidate of ghBinaryCandidates(spec, platform)) {
48478
48719
  try {
48479
- if (statSync4(join11(ghBinDir(spec), candidate)).isFile())
48720
+ if (statSync5(join11(ghBinDir(spec), candidate)).isFile())
48480
48721
  return true;
48481
48722
  } catch {}
48482
48723
  }
@@ -48487,11 +48728,45 @@ function ghBinaryCandidates(spec, platform) {
48487
48728
  return [spec.binary];
48488
48729
  return [spec.binary, `${spec.binary}.cmd`, `${spec.binary}.exe`, `${spec.binary}.bat`];
48489
48730
  }
48490
- var MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
48731
+ function readGithubInstalledMetaIn(installDir) {
48732
+ try {
48733
+ const path2 = join11(installDir, INSTALLED_META_FILE2);
48734
+ if (!statSync5(path2).isFile())
48735
+ return null;
48736
+ const parsed = JSON.parse(readFileSync6(path2, "utf8"));
48737
+ if (typeof parsed.version !== "string" || parsed.version.length === 0)
48738
+ return null;
48739
+ return {
48740
+ version: parsed.version,
48741
+ installedAt: typeof parsed.installedAt === "string" ? parsed.installedAt : "",
48742
+ ...typeof parsed.sha256 === "string" && parsed.sha256.length > 0 ? { sha256: parsed.sha256 } : {},
48743
+ ...typeof parsed.binarySha256 === "string" && parsed.binarySha256.length > 0 ? { binarySha256: parsed.binarySha256 } : {},
48744
+ ...typeof parsed.archiveSha256 === "string" && parsed.archiveSha256.length > 0 ? { archiveSha256: parsed.archiveSha256 } : {}
48745
+ };
48746
+ } catch {
48747
+ return null;
48748
+ }
48749
+ }
48750
+ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveSha256) {
48751
+ try {
48752
+ mkdirSync7(installDir, { recursive: true });
48753
+ const meta3 = {
48754
+ version: version2,
48755
+ installedAt: new Date().toISOString(),
48756
+ sha256: binarySha256,
48757
+ binarySha256,
48758
+ ...archiveSha256 ? { archiveSha256 } : {}
48759
+ };
48760
+ writeFileSync5(join11(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
48761
+ } catch (err) {
48762
+ warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
48763
+ }
48764
+ }
48765
+ var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
48491
48766
  var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
48492
48767
  function sha256OfFile(path2) {
48493
48768
  return new Promise((resolve3, reject) => {
48494
- const hash2 = createHash4("sha256");
48769
+ const hash2 = createHash5("sha256");
48495
48770
  const stream = createReadStream2(path2);
48496
48771
  stream.on("error", reject);
48497
48772
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -48499,7 +48774,7 @@ function sha256OfFile(path2) {
48499
48774
  });
48500
48775
  }
48501
48776
  function sha256OfFileSync2(path2) {
48502
- return createHash4("sha256").update(readFileSync6(path2)).digest("hex");
48777
+ return createHash5("sha256").update(readFileSync6(path2)).digest("hex");
48503
48778
  }
48504
48779
  async function fetchReleaseByTag(githubRepo, tag, fetchImpl2, signal) {
48505
48780
  const candidates = [];
@@ -48662,8 +48937,8 @@ function assertAllowedDownloadUrl(rawUrl) {
48662
48937
  return parsed;
48663
48938
  }
48664
48939
  async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
48665
- if (assetSize !== undefined && assetSize > MAX_DOWNLOAD_BYTES2) {
48666
- throw new Error(`asset size ${assetSize} exceeds max ${MAX_DOWNLOAD_BYTES2} (set lsp.versions to pin a smaller release if this is wrong)`);
48940
+ if (assetSize !== undefined && assetSize > MAX_DOWNLOAD_BYTES3) {
48941
+ throw new Error(`asset size ${assetSize} exceeds max ${MAX_DOWNLOAD_BYTES3} (set lsp.versions to pin a smaller release if this is wrong)`);
48667
48942
  }
48668
48943
  const timeout = controlledTimeoutSignal(120000, signal);
48669
48944
  try {
@@ -48672,24 +48947,24 @@ async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
48672
48947
  throw new Error(`download failed (${res.status})`);
48673
48948
  }
48674
48949
  const advertised = Number.parseInt(res.headers.get("content-length") ?? "", 10);
48675
- if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
48676
- throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
48950
+ if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
48951
+ throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
48677
48952
  }
48678
48953
  mkdirSync7(dirname2(destPath), { recursive: true });
48679
48954
  let bytesWritten = 0;
48680
48955
  const guard = new TransformStream({
48681
48956
  transform(chunk, controller) {
48682
48957
  bytesWritten += chunk.byteLength;
48683
- if (bytesWritten > MAX_DOWNLOAD_BYTES2) {
48684
- controller.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES2} bytes after streaming (server lied about size or sent unbounded body)`));
48958
+ if (bytesWritten > MAX_DOWNLOAD_BYTES3) {
48959
+ controller.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES3} bytes after streaming (server lied about size or sent unbounded body)`));
48685
48960
  return;
48686
48961
  }
48687
48962
  controller.enqueue(chunk);
48688
48963
  }
48689
48964
  });
48690
48965
  const guarded = res.body.pipeThrough(guard);
48691
- const nodeStream = Readable2.fromWeb(guarded);
48692
- await pipeline2(nodeStream, createWriteStream2(destPath), { signal: timeout.signal });
48966
+ const nodeStream = Readable3.fromWeb(guarded);
48967
+ await pipeline3(nodeStream, createWriteStream3(destPath), { signal: timeout.signal });
48693
48968
  } catch (err) {
48694
48969
  try {
48695
48970
  unlinkSync6(destPath);
@@ -48769,7 +49044,7 @@ function validateExtraction(stagingRoot) {
48769
49044
  };
48770
49045
  walk(realStagingRoot);
48771
49046
  }
48772
- function precheckArchiveSize(archivePath, archiveType) {
49047
+ function precheckArchiveContents(archivePath, archiveType) {
48773
49048
  let totalBytes = 0;
48774
49049
  if (archiveType === "zip") {
48775
49050
  const out = execFileSync2("unzip", ["-l", archivePath], { encoding: "utf8" });
@@ -48780,6 +49055,9 @@ function precheckArchiveSize(archivePath, archiveType) {
48780
49055
  const out = execFileSync2("tar", ["-tvf", archivePath], { encoding: "utf8" });
48781
49056
  for (const line of out.split(`
48782
49057
  `)) {
49058
+ if (line.startsWith("h")) {
49059
+ throw new Error(`archive contains hardlink entry: ${line.trim()}`);
49060
+ }
48783
49061
  const parts = line.trim().split(/\s+/);
48784
49062
  if (parts.length >= 6) {
48785
49063
  const numeric = parts.map((part) => Number.parseInt(part, 10)).filter((value) => Number.isFinite(value) && value >= 0);
@@ -48796,20 +49074,20 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
48796
49074
  const suffix = randomBytes(8).toString("hex");
48797
49075
  const stagingDir = `${destDir}.staging-${suffix}`;
48798
49076
  try {
48799
- rmSync3(stagingDir, { recursive: true, force: true });
49077
+ rmSync4(stagingDir, { recursive: true, force: true });
48800
49078
  } catch {}
48801
49079
  mkdirSync7(stagingDir, { recursive: true });
48802
49080
  try {
48803
- precheckArchiveSize(archivePath, archiveType);
49081
+ precheckArchiveContents(archivePath, archiveType);
48804
49082
  runPlatformExtractor(archivePath, stagingDir, archiveType);
48805
49083
  validateExtraction(stagingDir);
48806
49084
  try {
48807
- rmSync3(destDir, { recursive: true, force: true });
49085
+ rmSync4(destDir, { recursive: true, force: true });
48808
49086
  } catch {}
48809
- renameSync4(stagingDir, destDir);
49087
+ renameSync5(stagingDir, destDir);
48810
49088
  } catch (err) {
48811
49089
  try {
48812
- rmSync3(stagingDir, { recursive: true, force: true });
49090
+ rmSync4(stagingDir, { recursive: true, force: true });
48813
49091
  } catch {}
48814
49092
  throw err;
48815
49093
  }
@@ -48820,28 +49098,44 @@ function quarantineCachedGithubInstall(spec, reason) {
48820
49098
  warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
48821
49099
  try {
48822
49100
  mkdirSync7(dirname2(dest), { recursive: true });
48823
- rmSync3(dest, { recursive: true, force: true });
48824
- renameSync4(packageDir, dest);
49101
+ rmSync4(dest, { recursive: true, force: true });
49102
+ renameSync5(packageDir, dest);
48825
49103
  } catch (err) {
48826
49104
  warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
48827
49105
  }
48828
49106
  }
48829
49107
  function validateCachedGithubInstall(spec, platform) {
48830
- const meta3 = readInstalledMetaIn(ghPackageDir(spec));
49108
+ const packageDir = ghPackageDir(spec);
49109
+ const meta3 = readGithubInstalledMetaIn(packageDir);
48831
49110
  const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join11(ghBinDir(spec), candidate)).find((candidate) => {
48832
49111
  try {
48833
- return statSync4(candidate).isFile();
49112
+ return statSync5(candidate).isFile();
48834
49113
  } catch {
48835
49114
  return false;
48836
49115
  }
48837
49116
  });
48838
- if (!meta3?.sha256 || !meta3.version || !isSafeVersion(meta3.version) || !binaryPath) {
49117
+ if (!meta3?.version || !isSafeVersion(meta3.version) || !binaryPath) {
48839
49118
  quarantineCachedGithubInstall(spec, "missing/unsafe metadata or binary");
48840
49119
  return false;
48841
49120
  }
48842
49121
  const currentHash = sha256OfFileSync2(binaryPath);
48843
- if (currentHash !== meta3.sha256) {
48844
- quarantineCachedGithubInstall(spec, `recorded ${meta3.sha256}, current ${currentHash}`);
49122
+ const recordedBinaryHash = meta3.binarySha256 ?? meta3.sha256;
49123
+ if (recordedBinaryHash && currentHash === recordedBinaryHash) {
49124
+ if (meta3.sha256 !== currentHash || meta3.binarySha256 !== currentHash) {
49125
+ writeGithubInstalledMetaIn(packageDir, meta3.version, currentHash, meta3.archiveSha256);
49126
+ }
49127
+ return true;
49128
+ }
49129
+ if (meta3.sha256 && !meta3.binarySha256 && !meta3.archiveSha256) {
49130
+ writeGithubInstalledMetaIn(packageDir, meta3.version, currentHash, meta3.sha256);
49131
+ return true;
49132
+ }
49133
+ if (!recordedBinaryHash) {
49134
+ quarantineCachedGithubInstall(spec, "missing binary sha256 metadata");
49135
+ return false;
49136
+ }
49137
+ if (currentHash !== recordedBinaryHash) {
49138
+ quarantineCachedGithubInstall(spec, `recorded ${recordedBinaryHash}, current ${currentHash}`);
48845
49139
  return false;
48846
49140
  }
48847
49141
  return true;
@@ -48910,10 +49204,11 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
48910
49204
  return null;
48911
49205
  }
48912
49206
  log2(`[lsp] ${spec.id} ${tag} sha256=${archiveSha256}`);
48913
- const previousMeta = readInstalledMetaIn(ghPackageDir(spec));
48914
- if (previousMeta && previousMeta.version === tag && previousMeta.sha256) {
48915
- if (previousMeta.sha256 !== archiveSha256) {
48916
- error2(`[lsp] ${spec.id} ${tag}: TOFU sha256 mismatch — refusing install. ` + `Previously installed sha256=${previousMeta.sha256}, downloaded sha256=${archiveSha256}. ` + `This means the published release for tag ${tag} changed. Investigate before proceeding.`);
49207
+ const previousMeta = readGithubInstalledMetaIn(ghPackageDir(spec));
49208
+ const previousArchiveSha256 = previousMeta?.archiveSha256 ?? (previousMeta?.binarySha256 ? undefined : previousMeta?.sha256);
49209
+ if (previousMeta && previousMeta.version === tag && previousArchiveSha256) {
49210
+ if (previousArchiveSha256 !== archiveSha256) {
49211
+ error2(`[lsp] ${spec.id} ${tag}: TOFU sha256 mismatch — refusing install. ` + `Previously installed archive sha256=${previousArchiveSha256}, downloaded sha256=${archiveSha256}. ` + `This means the published release for tag ${tag} changed. Investigate before proceeding.`);
48917
49212
  try {
48918
49213
  unlinkSync6(archivePath);
48919
49214
  } catch {}
@@ -48948,7 +49243,9 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
48948
49243
  return null;
48949
49244
  }
48950
49245
  log2(`[lsp] installed ${spec.id} ${tag} at ${targetBinary}`);
48951
- return archiveSha256;
49246
+ const binarySha256 = await sha256OfFile(targetBinary);
49247
+ log2(`[lsp] ${spec.id} ${tag} binary_sha256=${binarySha256}`);
49248
+ return { archiveSha256, binarySha256 };
48952
49249
  }
48953
49250
  async function ensureGithubInstalled(spec, config2, fetchImpl2, platform, arch, signal) {
48954
49251
  const outcome = await withInstallLock(spec.githubRepo, async () => {
@@ -48974,14 +49271,14 @@ async function ensureGithubInstalled(spec, config2, fetchImpl2, platform, arch,
48974
49271
  log2(`[lsp] reinstalling ${spec.id}@${tag}: no installed-version metadata recorded`);
48975
49272
  }
48976
49273
  }
48977
- const archiveSha256 = await downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2, signal).catch((err) => {
49274
+ const hashes = await downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2, signal).catch((err) => {
48978
49275
  error2(`[lsp] github install ${spec.id} crashed: ${err}`);
48979
49276
  return null;
48980
49277
  });
48981
- if (!archiveSha256) {
49278
+ if (!hashes) {
48982
49279
  return { started: true, reason: "install failed (see plugin log)" };
48983
49280
  }
48984
- writeInstalledMetaIn(ghPackageDir(spec), tag, archiveSha256);
49281
+ writeGithubInstalledMetaIn(ghPackageDir(spec), tag, hashes.binarySha256, hashes.archiveSha256);
48985
49282
  return { started: true };
48986
49283
  });
48987
49284
  if (outcome === null) {
@@ -49111,7 +49408,7 @@ function discoverRelevantGithubServers(projectRoot) {
49111
49408
  }
49112
49409
 
49113
49410
  // src/notifications.ts
49114
- import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
49411
+ import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
49115
49412
  import { join as join12 } from "node:path";
49116
49413
  var WARNING_MARKER = "\uD83D\uDD27 AFT: ⚠️";
49117
49414
  var FEATURE_MARKER = "\uD83D\uDD27 AFT: ✨";
@@ -49124,7 +49421,7 @@ function sendIgnoredMessage(client, sessionId, text) {
49124
49421
  typedClient.ui.notify(text, "warning");
49125
49422
  return true;
49126
49423
  } catch (err) {
49127
- sessionLog2(sessionId, `[aft-pi] notification send failed: ${err instanceof Error ? err.message : String(err)}`);
49424
+ sessionLog(sessionId, `[aft-pi] notification send failed: ${err instanceof Error ? err.message : String(err)}`);
49128
49425
  return false;
49129
49426
  }
49130
49427
  }
@@ -49152,9 +49449,9 @@ function writeWarnedTools(storageDir, warned) {
49152
49449
  mkdirSync8(storageDir, { recursive: true });
49153
49450
  const warnedToolsPath = join12(storageDir, WARNED_TOOLS_FILE);
49154
49451
  const tmpPath = join12(storageDir, `${WARNED_TOOLS_FILE}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
49155
- writeFileSync5(tmpPath, `${JSON.stringify(warned, null, 2)}
49452
+ writeFileSync6(tmpPath, `${JSON.stringify(warned, null, 2)}
49156
49453
  `);
49157
- renameSync5(tmpPath, warnedToolsPath);
49454
+ renameSync6(tmpPath, warnedToolsPath);
49158
49455
  } catch {}
49159
49456
  }
49160
49457
  async function withWarnedToolsLock(storageDir, fn) {
@@ -49166,7 +49463,7 @@ async function withWarnedToolsLock(storageDir, fn) {
49166
49463
  try {
49167
49464
  return await fn();
49168
49465
  } finally {
49169
- rmSync4(lockDir, { recursive: true, force: true });
49466
+ rmSync5(lockDir, { recursive: true, force: true });
49170
49467
  }
49171
49468
  } catch (err) {
49172
49469
  const code = err.code;
@@ -49253,7 +49550,7 @@ function sendFeatureAnnouncement(version2, features, storageDir) {
49253
49550
  `));
49254
49551
  try {
49255
49552
  mkdirSync8(storageDir, { recursive: true });
49256
- writeFileSync5(versionFile, version2);
49553
+ writeFileSync6(versionFile, version2);
49257
49554
  } catch {}
49258
49555
  }
49259
49556
 
@@ -51856,33 +52153,39 @@ ${hintsBlock}` };
51856
52153
  // src/index.ts
51857
52154
  setActiveLogger(bridgeLogger);
51858
52155
  function createVersionMismatchHandler(getPool, ensureCompatibleBinary = ensureBinary) {
51859
- let versionUpgradeAttempted = null;
52156
+ const versionUpgradePromises = new Map;
51860
52157
  return async (binaryVersion, minVersion) => {
51861
- if (versionUpgradeAttempted === binaryVersion) {
51862
- log2(`Version ${binaryVersion} < ${minVersion} but upgrade already attempted — continuing`);
51863
- return null;
52158
+ const existing = versionUpgradePromises.get(minVersion);
52159
+ if (existing) {
52160
+ log2(`Version ${binaryVersion} < ${minVersion}; awaiting in-flight compatible binary upgrade`);
52161
+ return existing;
51864
52162
  }
51865
- versionUpgradeAttempted = binaryVersion;
51866
- warn2(`WARNING: aft binary v${binaryVersion} is older than plugin v${minVersion}. ` + "Some features may not work. Attempting to download a compatible binary...");
51867
- try {
51868
- const path2 = await ensureCompatibleBinary(`v${minVersion}`);
51869
- if (!path2) {
51870
- warn2(`Could not find or download v${minVersion}. Continuing with v${binaryVersion}.`);
51871
- return null;
51872
- }
51873
- const pool = getPool();
51874
- if (!pool) {
51875
- warn2(`Found/downloaded compatible binary at ${path2}, but bridge pool is not ready.`);
52163
+ const upgradePromise = (async () => {
52164
+ warn2(`WARNING: aft binary v${binaryVersion} is older than plugin v${minVersion}. ` + "Some features may not work. Attempting to download a compatible binary...");
52165
+ try {
52166
+ const path2 = await ensureCompatibleBinary(`v${minVersion}`);
52167
+ if (!path2) {
52168
+ warn2(`Could not find or download v${minVersion}. Continuing with v${binaryVersion}.`);
52169
+ return null;
52170
+ }
52171
+ const pool = getPool();
52172
+ if (!pool) {
52173
+ warn2(`Found/downloaded compatible binary at ${path2}, but bridge pool is not ready.`);
52174
+ return null;
52175
+ }
52176
+ log2(`Found/downloaded compatible binary at ${path2}. Replacing running bridges...`);
52177
+ const replaced = await pool.replaceBinary(path2);
52178
+ log2("Binary replaced successfully. New bridges will use the updated binary.");
52179
+ return replaced;
52180
+ } catch (err) {
52181
+ error2(`Auto-download failed: ${err.message}. Install manually: cargo install agent-file-tools@${minVersion}`);
51876
52182
  return null;
52183
+ } finally {
52184
+ versionUpgradePromises.delete(minVersion);
51877
52185
  }
51878
- log2(`Found/downloaded compatible binary at ${path2}. Replacing running bridges...`);
51879
- const replaced = await pool.replaceBinary(path2);
51880
- log2("Binary replaced successfully. New bridges will use the updated binary.");
51881
- return replaced;
51882
- } catch (err) {
51883
- error2(`Auto-download failed: ${err.message}. Install manually: cargo install agent-file-tools@${minVersion}`);
51884
- return null;
51885
- }
52186
+ })();
52187
+ versionUpgradePromises.set(minVersion, upgradePromise);
52188
+ return upgradePromise;
51886
52189
  };
51887
52190
  }
51888
52191
  var PLUGIN_VERSION = (() => {