@cortexkit/aft-pi 0.30.0 → 0.30.2

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
@@ -12399,7 +12399,7 @@ var require_snapshot_utils = __commonJS((exports, module) => {
12399
12399
  // ../../node_modules/.bun/undici@7.25.0/node_modules/undici/lib/mock/snapshot-recorder.js
12400
12400
  var require_snapshot_recorder = __commonJS((exports, module) => {
12401
12401
  var { writeFile, readFile, mkdir } = __require("node:fs/promises");
12402
- var { dirname: dirname3, resolve: resolve2 } = __require("node:path");
12402
+ var { dirname: dirname4, resolve: resolve2 } = __require("node:path");
12403
12403
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
12404
12404
  var { InvalidArgumentError, UndiciError } = require_errors();
12405
12405
  var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -12594,7 +12594,7 @@ var require_snapshot_recorder = __commonJS((exports, module) => {
12594
12594
  throw new InvalidArgumentError("Snapshot path is required");
12595
12595
  }
12596
12596
  const resolvedPath = resolve2(path);
12597
- await mkdir(dirname3(resolvedPath), { recursive: true });
12597
+ await mkdir(dirname4(resolvedPath), { recursive: true });
12598
12598
  const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
12599
12599
  hash,
12600
12600
  snapshot
@@ -29886,10 +29886,10 @@ var require_stringify = __commonJS((exports, module) => {
29886
29886
  replacer = null;
29887
29887
  indent = EMPTY;
29888
29888
  };
29889
- var join8 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two)), gap) : EMPTY;
29889
+ var join9 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two)), gap) : EMPTY;
29890
29890
  var join_content = (inside, value, gap) => {
29891
29891
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
29892
- return join8(comment, inside, gap);
29892
+ return join9(comment, inside, gap);
29893
29893
  };
29894
29894
  var stringify_string = (holder, key, value) => {
29895
29895
  const raw = get_raw_string_literal(holder, key);
@@ -29911,13 +29911,13 @@ var require_stringify = __commonJS((exports, module) => {
29911
29911
  if (i !== 0) {
29912
29912
  inside += COMMA;
29913
29913
  }
29914
- const before = join8(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
29914
+ const before = join9(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
29915
29915
  inside += before || LF + deeper_gap;
29916
29916
  inside += stringify(i, value, deeper_gap) || STR_NULL;
29917
29917
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
29918
29918
  after_comma = process_comments(value, AFTER(i), deeper_gap);
29919
29919
  }
29920
- inside += join8(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
29920
+ inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
29921
29921
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
29922
29922
  };
29923
29923
  var object_stringify = (value, gap) => {
@@ -29938,13 +29938,13 @@ var require_stringify = __commonJS((exports, module) => {
29938
29938
  inside += COMMA;
29939
29939
  }
29940
29940
  first = false;
29941
- const before = join8(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
29941
+ const before = join9(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
29942
29942
  inside += before || LF + deeper_gap;
29943
29943
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
29944
29944
  after_comma = process_comments(value, AFTER(key), deeper_gap);
29945
29945
  };
29946
29946
  keys.forEach(iteratee);
29947
- inside += join8(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
29947
+ inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
29948
29948
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
29949
29949
  };
29950
29950
  function stringify(key, holder, gap) {
@@ -34061,6 +34061,11 @@ class BinaryBridge {
34061
34061
  if (requestSessionId && options?.configureWarningClient !== undefined) {
34062
34062
  this.configureWarningClients.set(requestSessionId, options.configureWarningClient);
34063
34063
  }
34064
+ const effectiveTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
34065
+ const implicitTransportOptions = {
34066
+ ...options?.transportTimeoutMs !== undefined || options?.timeoutMs !== undefined ? { transportTimeoutMs: effectiveTimeoutMs } : {},
34067
+ markConfiguredOnSuccess: false
34068
+ };
34064
34069
  if (!this.configured) {
34065
34070
  if (command !== "configure" && command !== "version") {
34066
34071
  if (!this._configurePromise) {
@@ -34071,12 +34076,12 @@ class BinaryBridge {
34071
34076
  project_root: this.cwd,
34072
34077
  ...this.configOverrides,
34073
34078
  ...sessionIdForConfigure ? { session_id: sessionIdForConfigure } : {}
34074
- });
34079
+ }, implicitTransportOptions);
34075
34080
  if (configResult.success === false) {
34076
34081
  throw new Error(`${this.errorPrefix} Configure failed: ${configResult.message ?? "unknown error"}`);
34077
34082
  }
34078
34083
  await this.deliverConfigureWarnings(configResult, params, options);
34079
- await this.checkVersion();
34084
+ await this.checkVersion(implicitTransportOptions);
34080
34085
  if (!this.isAlive()) {
34081
34086
  throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${this.getLogFilePathVia()}`);
34082
34087
  }
@@ -34106,9 +34111,8 @@ class BinaryBridge {
34106
34111
  }
34107
34112
  const line = `${JSON.stringify(request)}
34108
34113
  `;
34109
- const effectiveTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
34110
34114
  const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
34111
- return new Promise((resolve, reject) => {
34115
+ const response = await new Promise((resolve, reject) => {
34112
34116
  const timer = setTimeout(() => {
34113
34117
  this.pending.delete(id);
34114
34118
  const restartSuffix = keepBridgeOnTimeout ? "" : " — restarting bridge";
@@ -34141,6 +34145,10 @@ class BinaryBridge {
34141
34145
  }
34142
34146
  });
34143
34147
  });
34148
+ if (command === "configure" && response.success === true && options?.markConfiguredOnSuccess !== false) {
34149
+ this.configured = true;
34150
+ }
34151
+ return response;
34144
34152
  } catch (err) {
34145
34153
  if (err instanceof BridgeReplacedDuringVersionCheck && canRetryAfterVersionSwap && command !== "configure" && command !== "version") {
34146
34154
  this.logVia(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
@@ -34231,11 +34239,11 @@ class BinaryBridge {
34231
34239
  });
34232
34240
  }
34233
34241
  }
34234
- async checkVersion() {
34242
+ async checkVersion(options) {
34235
34243
  if (!this.minVersion)
34236
34244
  return;
34237
34245
  try {
34238
- const resp = await this.send("version");
34246
+ const resp = await this.send("version", {}, options);
34239
34247
  if (resp.success === false) {
34240
34248
  throw new Error(`Binary version check failed: ${String(resp.code ?? "unknown")} — likely too old`);
34241
34249
  }
@@ -34310,10 +34318,14 @@ class BinaryBridge {
34310
34318
  ...process.env,
34311
34319
  ...envPath ? { PATH: envPath } : {}
34312
34320
  };
34321
+ this.logVia(`bridge.spawnProcess: useFastembedBackend=${useFastembedBackend}, ` + `parentORT=${process.env.ORT_DYLIB_PATH ?? "(unset)"}, ` + `ortLibraryPath=${ortLibraryPath ?? "(none)"}`);
34313
34322
  if (useFastembedBackend) {
34314
34323
  env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join(this.configOverrides.storage_dir, "semantic", "models") : join(homedir() || "", ".cache", "fastembed"));
34315
- if (ortLibraryPath) {
34324
+ if (process.env.ORT_DYLIB_PATH) {
34325
+ this.logVia(`ORT_DYLIB_PATH inherited from parent env: ${process.env.ORT_DYLIB_PATH}`);
34326
+ } else if (ortLibraryPath) {
34316
34327
  env.ORT_DYLIB_PATH = ortLibraryPath;
34328
+ this.logVia(`ORT_DYLIB_PATH set from managed ONNX Runtime: ${ortLibraryPath}`);
34317
34329
  }
34318
34330
  }
34319
34331
  const child = spawn(this.binaryPath, [], {
@@ -34559,8 +34571,9 @@ class BinaryBridge {
34559
34571
  }
34560
34572
  }
34561
34573
  // ../aft-bridge/dist/downloader.js
34562
- import { createHash } from "node:crypto";
34563
- import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, renameSync, rmSync, statSync, unlinkSync } from "node:fs";
34574
+ import { spawnSync } from "node:child_process";
34575
+ import { createHash, randomUUID } from "node:crypto";
34576
+ import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
34564
34577
  import { homedir as homedir2 } from "node:os";
34565
34578
  import { join as join2 } from "node:path";
34566
34579
  import { Readable } from "node:stream";
@@ -34588,6 +34601,34 @@ var LATEST_TAG_TIMEOUT_MS = 30000;
34588
34601
  var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
34589
34602
  var DOWNLOAD_LOCK_TIMEOUT_MS = 120000;
34590
34603
  var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
34604
+ function readBinaryVersion(binaryPath) {
34605
+ try {
34606
+ const result = spawnSync(binaryPath, ["--version"], {
34607
+ encoding: "utf-8",
34608
+ stdio: ["pipe", "pipe", "pipe"],
34609
+ timeout: 5000
34610
+ });
34611
+ const stdoutVersion = result.stdout?.trim();
34612
+ const stderrVersion = result.stderr?.trim();
34613
+ const rawVersion = stdoutVersion || stderrVersion;
34614
+ if (!rawVersion)
34615
+ return null;
34616
+ return rawVersion.replace(/^aft\s+/, "");
34617
+ } catch {
34618
+ return null;
34619
+ }
34620
+ }
34621
+ function expectedVersionFromTag(tag) {
34622
+ return tag.startsWith("v") ? tag.slice(1) : tag;
34623
+ }
34624
+ function isExpectedCachedBinary(binaryPath, tag) {
34625
+ const expected = expectedVersionFromTag(tag);
34626
+ const actual = readBinaryVersion(binaryPath);
34627
+ if (actual === expected)
34628
+ return true;
34629
+ warn(`Cached binary at ${binaryPath} reports ${actual ?? "no version"}, expected ${expected}; refreshing cache entry`);
34630
+ return false;
34631
+ }
34591
34632
  function getCacheDir() {
34592
34633
  if (process.platform === "win32") {
34593
34634
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
@@ -34623,7 +34664,7 @@ async function downloadBinary(version) {
34623
34664
  const versionedCacheDir = join2(getCacheDir(), tag);
34624
34665
  const binaryName = getBinaryName();
34625
34666
  const binaryPath = join2(versionedCacheDir, binaryName);
34626
- if (existsSync(binaryPath)) {
34667
+ if (existsSync(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
34627
34668
  return binaryPath;
34628
34669
  }
34629
34670
  const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
@@ -34641,7 +34682,7 @@ async function downloadBinary(version) {
34641
34682
  mkdirSync(versionedCacheDir, { recursive: true });
34642
34683
  }
34643
34684
  releaseLock = await acquireDownloadLock(lockPath);
34644
- if (existsSync(binaryPath)) {
34685
+ if (existsSync(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
34645
34686
  return binaryPath;
34646
34687
  }
34647
34688
  binaryController = new AbortController;
@@ -34730,7 +34771,7 @@ async function ensureBinary(version) {
34730
34771
  if (version) {
34731
34772
  const tag = version.startsWith("v") ? version : `v${version}`;
34732
34773
  const versionCached = getCachedBinaryPath(tag);
34733
- if (versionCached) {
34774
+ if (versionCached && isExpectedCachedBinary(versionCached, tag)) {
34734
34775
  log(`Found cached binary for ${tag}: ${versionCached}`);
34735
34776
  return versionCached;
34736
34777
  }
@@ -34744,13 +34785,17 @@ async function acquireDownloadLock(lockPath) {
34744
34785
  const startedAt = Date.now();
34745
34786
  while (true) {
34746
34787
  try {
34788
+ const owner = `${process.pid}:${Date.now()}:${randomUUID()}`;
34747
34789
  const fd = openSync(lockPath, "wx");
34790
+ writeSync(fd, owner);
34748
34791
  return () => {
34749
34792
  try {
34750
34793
  closeSync(fd);
34751
34794
  } catch {}
34752
34795
  try {
34753
- rmSync(lockPath, { force: true });
34796
+ if (readFileSync(lockPath, "utf-8") === owner) {
34797
+ rmSync(lockPath, { force: true });
34798
+ }
34754
34799
  } catch {}
34755
34800
  };
34756
34801
  } catch (err) {
@@ -34813,34 +34858,35 @@ function compressionSavingsPercent(original, compressed) {
34813
34858
  }
34814
34859
  // ../aft-bridge/dist/migration.js
34815
34860
  import { spawnSync as spawnSync2 } from "node:child_process";
34816
- import { existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
34861
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
34817
34862
  import { homedir as homedir4, tmpdir } from "node:os";
34818
- import { dirname, join as join4 } from "node:path";
34863
+ import { dirname as dirname2, join as join5 } from "node:path";
34864
+
34865
+ // ../aft-bridge/dist/paths.js
34866
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, renameSync as renameSync2 } from "node:fs";
34867
+ import { dirname, join as join3 } from "node:path";
34868
+ function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
34869
+ return join3(storageRoot, harness, ...segments);
34870
+ }
34871
+ function repairRootScopedStorageFile(storageRoot, harness, fileName) {
34872
+ const harnessPath = resolveHarnessStoragePath(storageRoot, harness, fileName);
34873
+ const rootPath = join3(storageRoot, fileName);
34874
+ if (existsSync2(harnessPath) || !existsSync2(rootPath))
34875
+ return harnessPath;
34876
+ try {
34877
+ mkdirSync2(dirname(harnessPath), { recursive: true });
34878
+ renameSync2(rootPath, harnessPath);
34879
+ } catch {}
34880
+ return harnessPath;
34881
+ }
34819
34882
 
34820
34883
  // ../aft-bridge/dist/resolver.js
34821
- import { execSync, spawnSync } from "node:child_process";
34822
- import { chmodSync as chmodSync2, copyFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, renameSync as renameSync2 } from "node:fs";
34884
+ import { execSync } from "node:child_process";
34885
+ import { chmodSync as chmodSync2, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync3 } from "node:fs";
34823
34886
  import { createRequire as createRequire2 } from "node:module";
34824
34887
  import { homedir as homedir3 } from "node:os";
34825
- import { join as join3 } from "node:path";
34888
+ import { join as join4 } from "node:path";
34826
34889
  var ensureBinaryForResolver = ensureBinary;
34827
- function readBinaryVersion(binaryPath) {
34828
- try {
34829
- const result = spawnSync(binaryPath, ["--version"], {
34830
- encoding: "utf-8",
34831
- stdio: ["pipe", "pipe", "pipe"],
34832
- timeout: 5000
34833
- });
34834
- const stdoutVersion = result.stdout?.trim();
34835
- const stderrVersion = result.stderr?.trim();
34836
- const rawVersion = stdoutVersion || stderrVersion;
34837
- if (!rawVersion)
34838
- return null;
34839
- return rawVersion.replace(/^aft\s+/, "");
34840
- } catch {
34841
- return null;
34842
- }
34843
- }
34844
34890
  function copyToVersionedCache(npmBinaryPath, knownVersion) {
34845
34891
  try {
34846
34892
  const version = knownVersion ?? readBinaryVersion(npmBinaryPath);
@@ -34848,22 +34894,22 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
34848
34894
  return null;
34849
34895
  const tag = version.startsWith("v") ? version : `v${version}`;
34850
34896
  const cacheDir = getCacheDir();
34851
- const versionedDir = join3(cacheDir, tag);
34897
+ const versionedDir = join4(cacheDir, tag);
34852
34898
  const ext = process.platform === "win32" ? ".exe" : "";
34853
- const cachedPath = join3(versionedDir, `aft${ext}`);
34854
- if (existsSync2(cachedPath)) {
34899
+ const cachedPath = join4(versionedDir, `aft${ext}`);
34900
+ if (existsSync3(cachedPath)) {
34855
34901
  const cachedVersion = readBinaryVersion(cachedPath);
34856
34902
  if (cachedVersion === version)
34857
34903
  return cachedPath;
34858
34904
  warn(`Cached binary at ${cachedPath} reports ${cachedVersion ?? "no version"}, expected ${version}; refreshing from npm package`);
34859
34905
  }
34860
- mkdirSync2(versionedDir, { recursive: true });
34906
+ mkdirSync3(versionedDir, { recursive: true });
34861
34907
  const tmpPath = `${cachedPath}.${process.pid}.${Date.now()}.tmp`;
34862
34908
  copyFileSync(npmBinaryPath, tmpPath);
34863
34909
  if (process.platform !== "win32") {
34864
34910
  chmodSync2(tmpPath, 493);
34865
34911
  }
34866
- renameSync2(tmpPath, cachedPath);
34912
+ renameSync3(tmpPath, cachedPath);
34867
34913
  log(`Copied npm binary to versioned cache: ${cachedPath}`);
34868
34914
  return cachedPath;
34869
34915
  } catch (err) {
@@ -34879,17 +34925,17 @@ function homeDirFromEnv(env) {
34879
34925
  }
34880
34926
  function cacheDirFromEnv(env) {
34881
34927
  if (process.platform === "win32") {
34882
- const base2 = env.LOCALAPPDATA || env.APPDATA || join3(homeDirFromEnv(env), "AppData", "Local");
34883
- return join3(base2, "aft", "bin");
34928
+ const base2 = env.LOCALAPPDATA || env.APPDATA || join4(homeDirFromEnv(env), "AppData", "Local");
34929
+ return join4(base2, "aft", "bin");
34884
34930
  }
34885
- const base = env.XDG_CACHE_HOME || join3(homeDirFromEnv(env), ".cache");
34886
- return join3(base, "aft", "bin");
34931
+ const base = env.XDG_CACHE_HOME || join4(homeDirFromEnv(env), ".cache");
34932
+ return join4(base, "aft", "bin");
34887
34933
  }
34888
34934
  function cachedBinaryPathFromEnv(version, env, ext) {
34889
- const binaryPath = join3(cacheDirFromEnv(env), version, `aft${ext}`);
34890
- return existsSync2(binaryPath) ? binaryPath : null;
34935
+ const binaryPath = join4(cacheDirFromEnv(env), version, `aft${ext}`);
34936
+ return existsSync3(binaryPath) ? binaryPath : null;
34891
34937
  }
34892
- function isExpectedCachedBinary(binaryPath, expectedVersion) {
34938
+ function isExpectedCachedBinary2(binaryPath, expectedVersion) {
34893
34939
  const expected = normalizeBareVersion(expectedVersion);
34894
34940
  const actual = readBinaryVersion(binaryPath);
34895
34941
  if (actual === expected)
@@ -34897,6 +34943,21 @@ function isExpectedCachedBinary(binaryPath, expectedVersion) {
34897
34943
  warn(`Cached binary at ${binaryPath} reports ${actual ?? "no version"}, expected ${expected}; skipping cache candidate`);
34898
34944
  return false;
34899
34945
  }
34946
+ function probeBinaryCandidate(binaryPath, source, expectedVersion) {
34947
+ const actual = readBinaryVersion(binaryPath);
34948
+ if (actual === null) {
34949
+ warn(`${source} binary at ${binaryPath} did not report a version; skipping`);
34950
+ return null;
34951
+ }
34952
+ if (expectedVersion && actual !== normalizeBareVersion(expectedVersion)) {
34953
+ warn(`${source} binary at ${binaryPath} reports ${actual}, expected ${normalizeBareVersion(expectedVersion)}; skipping`);
34954
+ return null;
34955
+ }
34956
+ return binaryPath;
34957
+ }
34958
+ function parsePathLookupOutput(output) {
34959
+ return output.split(/\r?\n/).map((candidate) => candidate.trim()).filter(Boolean);
34960
+ }
34900
34961
  function platformKey(platform = process.platform, arch = process.arch) {
34901
34962
  const archMap = PLATFORM_ARCH_MAP[platform];
34902
34963
  if (!archMap) {
@@ -34922,7 +34983,7 @@ function findBinarySync(expectedVersion) {
34922
34983
  if (pluginVersion) {
34923
34984
  const tag = pluginVersion.startsWith("v") ? pluginVersion : `v${pluginVersion}`;
34924
34985
  const versionCached = cachedBinaryPathFromEnv(tag, env, ext);
34925
- if (versionCached && isExpectedCachedBinary(versionCached, pluginVersion))
34986
+ if (versionCached && isExpectedCachedBinary2(versionCached, pluginVersion))
34926
34987
  return versionCached;
34927
34988
  }
34928
34989
  try {
@@ -34930,7 +34991,7 @@ function findBinarySync(expectedVersion) {
34930
34991
  const packageBin = `@cortexkit/aft-${key}/bin/aft${ext}`;
34931
34992
  const req = createRequire2(import.meta.url);
34932
34993
  const resolved = req.resolve(packageBin);
34933
- if (existsSync2(resolved)) {
34994
+ if (existsSync3(resolved)) {
34934
34995
  const npmVersion = readBinaryVersion(resolved);
34935
34996
  if (npmVersion === null) {
34936
34997
  warn(`npm platform package binary at ${resolved} did not report a version; skipping (continuing to PATH lookup)`);
@@ -34949,12 +35010,18 @@ function findBinarySync(expectedVersion) {
34949
35010
  env,
34950
35011
  stdio: ["pipe", "pipe", "pipe"]
34951
35012
  }).trim();
34952
- if (result)
34953
- return result;
35013
+ for (const candidate of parsePathLookupOutput(result)) {
35014
+ const usable = probeBinaryCandidate(candidate, "PATH", expectedVersion);
35015
+ if (usable)
35016
+ return usable;
35017
+ }
34954
35018
  } catch {}
34955
- const cargoPath = join3(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
34956
- if (existsSync2(cargoPath))
34957
- return cargoPath;
35019
+ const cargoPath = join4(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
35020
+ if (existsSync3(cargoPath)) {
35021
+ const usable = probeBinaryCandidate(cargoPath, "cargo", expectedVersion);
35022
+ if (usable)
35023
+ return usable;
35024
+ }
34958
35025
  return null;
34959
35026
  }
34960
35027
  async function findBinary(expectedVersion) {
@@ -34995,9 +35062,9 @@ function dataHome() {
34995
35062
  if (process.env.XDG_DATA_HOME)
34996
35063
  return process.env.XDG_DATA_HOME;
34997
35064
  if (process.platform === "win32") {
34998
- return process.env.LOCALAPPDATA || process.env.APPDATA || join4(homeDir(), "AppData", "Local");
35065
+ return process.env.LOCALAPPDATA || process.env.APPDATA || join5(homeDir(), "AppData", "Local");
34999
35066
  }
35000
- return join4(homeDir(), ".local", "share");
35067
+ return join5(homeDir(), ".local", "share");
35001
35068
  }
35002
35069
  function homeDir() {
35003
35070
  if (process.platform === "win32")
@@ -35006,11 +35073,11 @@ function homeDir() {
35006
35073
  }
35007
35074
  function resolveLegacyStorageRoot(harness) {
35008
35075
  if (harness === "pi")
35009
- return join4(homeDir(), ".pi", "agent", "aft");
35010
- return join4(dataHome(), "opencode", "storage", "plugin", "aft");
35076
+ return join5(homeDir(), ".pi", "agent", "aft");
35077
+ return join5(dataHome(), "opencode", "storage", "plugin", "aft");
35011
35078
  }
35012
35079
  function resolveCortexKitStorageRoot() {
35013
- return join4(dataHome(), "cortexkit", "aft");
35080
+ return join5(dataHome(), "cortexkit", "aft");
35014
35081
  }
35015
35082
  function tail(value) {
35016
35083
  if (!value)
@@ -35024,26 +35091,26 @@ function spawnErrorLabel(error2) {
35024
35091
  return [code, error2.message].filter(Boolean).join(": ");
35025
35092
  }
35026
35093
  function migrationLogPath(newRoot, harness, logger) {
35027
- const desired = join4(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
35094
+ const desired = join5(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
35028
35095
  try {
35029
- mkdirSync3(dirname(desired), { recursive: true });
35096
+ mkdirSync4(dirname2(desired), { recursive: true });
35030
35097
  return desired;
35031
35098
  } catch (err) {
35032
- const fallback = join4(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
35033
- logger?.warn?.(`Failed to create AFT migration log directory ${dirname(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
35099
+ const fallback = join5(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
35100
+ logger?.warn?.(`Failed to create AFT migration log directory ${dirname2(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
35034
35101
  return fallback;
35035
35102
  }
35036
35103
  }
35037
35104
  async function ensureStorageMigrated(opts) {
35038
35105
  const legacyRoot = resolveLegacyStorageRoot(opts.harness);
35039
35106
  const newRoot = resolveCortexKitStorageRoot();
35040
- const targetMarker = join4(newRoot, opts.harness, TARGET_MARKER);
35107
+ const targetMarker = resolveHarnessStoragePath(newRoot, opts.harness, TARGET_MARKER);
35041
35108
  const info = opts.logger?.info ?? opts.logger?.log;
35042
- if (existsSync3(targetMarker)) {
35109
+ if (existsSync4(targetMarker)) {
35043
35110
  info?.(`AFT storage already migrated for ${opts.harness}; using ${newRoot}`);
35044
35111
  return;
35045
35112
  }
35046
- if (!existsSync3(legacyRoot)) {
35113
+ if (!existsSync4(legacyRoot)) {
35047
35114
  info?.(`AFT storage migration skipped for ${opts.harness}: no legacy data at ${legacyRoot}; ` + `using ${newRoot} for fresh install`);
35048
35115
  return;
35049
35116
  }
@@ -35093,8 +35160,8 @@ async function ensureStorageMigrated(opts) {
35093
35160
  // ../aft-bridge/dist/onnx-runtime.js
35094
35161
  import { execFileSync } from "node:child_process";
35095
35162
  import { createHash as createHash2 } from "node:crypto";
35096
- import { chmodSync as chmodSync3, closeSync as closeSync2, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync4, openSync as openSync2, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
35097
- import { dirname as dirname2, join as join5, relative, resolve } from "node:path";
35163
+ import { chmodSync as chmodSync3, closeSync as closeSync2, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, openSync as openSync2, readdirSync, readFileSync as readFileSync2, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
35164
+ import { basename, dirname as dirname3, join as join6, relative, resolve } from "node:path";
35098
35165
  import { Readable as Readable2 } from "node:stream";
35099
35166
  import { pipeline as pipeline2 } from "node:stream/promises";
35100
35167
  var ORT_VERSION = "1.24.4";
@@ -35157,9 +35224,9 @@ function getManualInstallHint() {
35157
35224
  }
35158
35225
  async function ensureOnnxRuntime(storageDir) {
35159
35226
  const info = getPlatformInfo();
35160
- const ortDir = join5(storageDir, "onnxruntime", ORT_VERSION);
35161
- const libPath = join5(ortDir, info?.libName ?? "libonnxruntime.dylib");
35162
- if (existsSync4(libPath)) {
35227
+ const ortDir = join6(storageDir, "onnxruntime", ORT_VERSION);
35228
+ const libPath = join6(ortDir, info?.libName ?? "libonnxruntime.dylib");
35229
+ if (existsSync5(libPath)) {
35163
35230
  const meta = readOnnxInstalledMeta(ortDir);
35164
35231
  if (meta?.sha256) {
35165
35232
  try {
@@ -35188,9 +35255,9 @@ async function ensureOnnxRuntime(storageDir) {
35188
35255
  warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
35189
35256
  return null;
35190
35257
  }
35191
- const onnxBaseDir = join5(storageDir, "onnxruntime");
35192
- mkdirSync4(onnxBaseDir, { recursive: true });
35193
- const lockPath = join5(onnxBaseDir, ONNX_LOCK_FILE);
35258
+ const onnxBaseDir = join6(storageDir, "onnxruntime");
35259
+ mkdirSync5(onnxBaseDir, { recursive: true });
35260
+ const lockPath = join6(onnxBaseDir, ONNX_LOCK_FILE);
35194
35261
  cleanupAbandonedStagingDirs(onnxBaseDir);
35195
35262
  if (!acquireLock(lockPath)) {
35196
35263
  warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
@@ -35209,7 +35276,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
35209
35276
  for (const entry of entries) {
35210
35277
  if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
35211
35278
  continue;
35212
- const stagingDir = join5(onnxBaseDir, entry);
35279
+ const stagingDir = join6(onnxBaseDir, entry);
35213
35280
  const parts = entry.split(".");
35214
35281
  const pidStr = parts[parts.length - 2];
35215
35282
  const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
@@ -35241,7 +35308,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
35241
35308
  }
35242
35309
  function cleanupIncompleteTargetIfUnowned(ortDir) {
35243
35310
  try {
35244
- if (existsSync4(ortDir) && !existsSync4(join5(ortDir, ONNX_INSTALLED_META_FILE))) {
35311
+ if (existsSync5(ortDir) && !existsSync5(join6(ortDir, ONNX_INSTALLED_META_FILE))) {
35245
35312
  log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
35246
35313
  rmSync2(ortDir, { recursive: true, force: true });
35247
35314
  }
@@ -35251,6 +35318,14 @@ function cleanupIncompleteTargetIfUnowned(ortDir) {
35251
35318
  }
35252
35319
  var REQUIRED_ORT_MAJOR = 1;
35253
35320
  var REQUIRED_ORT_MIN_MINOR = 20;
35321
+ var INVALID_ORT_VERSION = "<invalid>";
35322
+ function parseOnnxVersionFromPath(value) {
35323
+ const name = basename(value);
35324
+ const semverish = name.match(/\.(\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?)(?:\.dylib)?$/);
35325
+ if (semverish)
35326
+ return semverish[1].split(/[-+]/, 1)[0];
35327
+ return /\d+\.\d+\.\d+/.test(name) ? INVALID_ORT_VERSION : null;
35328
+ }
35254
35329
  function detectOnnxVersion(libDir, libName) {
35255
35330
  try {
35256
35331
  const entries = readdirSync(libDir);
@@ -35258,23 +35333,23 @@ function detectOnnxVersion(libDir, libName) {
35258
35333
  for (const entry of entries) {
35259
35334
  if (!entry.startsWith(barePrefix))
35260
35335
  continue;
35261
- const match = entry.match(/\.(\d+\.\d+\.\d+)(?:\.dylib)?$/);
35262
- if (match)
35263
- return match[1];
35336
+ const version = parseOnnxVersionFromPath(entry);
35337
+ if (version)
35338
+ return version;
35264
35339
  }
35265
- const base = join5(libDir, libName);
35266
- if (existsSync4(base)) {
35340
+ const base = join6(libDir, libName);
35341
+ if (existsSync5(base)) {
35267
35342
  try {
35268
35343
  const real = realpathSync(base);
35269
- const m = real.match(/\.(\d+\.\d+\.\d+)(?:\.dylib)?$/);
35270
- if (m)
35271
- return m[1];
35344
+ const version = parseOnnxVersionFromPath(real);
35345
+ if (version)
35346
+ return version;
35272
35347
  } catch {}
35273
35348
  try {
35274
35349
  const target = readlinkSync(base);
35275
- const m = target.match(/\.(\d+\.\d+\.\d+)(?:\.dylib)?$/);
35276
- if (m)
35277
- return m[1];
35350
+ const version = parseOnnxVersionFromPath(target);
35351
+ if (version)
35352
+ return version;
35278
35353
  } catch {}
35279
35354
  }
35280
35355
  } catch {}
@@ -35299,7 +35374,7 @@ function findSystemOnnxRuntime(libName) {
35299
35374
  searchPaths.push("/usr/lib", "/usr/lib/x86_64-linux-gnu", "/usr/lib/aarch64-linux-gnu", "/usr/local/lib");
35300
35375
  }
35301
35376
  for (const dir of searchPaths) {
35302
- if (!existsSync4(join5(dir, libName)))
35377
+ if (!existsSync5(join6(dir, libName)))
35303
35378
  continue;
35304
35379
  const version = detectOnnxVersion(dir, libName);
35305
35380
  if (version && !isOnnxVersionCompatible(version)) {
@@ -35326,7 +35401,7 @@ async function downloadFileWithCap(url, destPath) {
35326
35401
  if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
35327
35402
  throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
35328
35403
  }
35329
- mkdirSync4(dirname2(destPath), { recursive: true });
35404
+ mkdirSync5(dirname3(destPath), { recursive: true });
35330
35405
  let bytesWritten = 0;
35331
35406
  const guard = new TransformStream({
35332
35407
  transform(chunk, transformController) {
@@ -35356,11 +35431,11 @@ function validateExtractedTree(stagingRoot) {
35356
35431
  const walk = (dir) => {
35357
35432
  const entries = readdirSync(dir);
35358
35433
  for (const entry of entries) {
35359
- const fullPath = join5(dir, entry);
35434
+ const fullPath = join6(dir, entry);
35360
35435
  const lst = lstatSync(fullPath);
35361
35436
  if (lst.isSymbolicLink()) {
35362
35437
  const linkTarget = readlinkSync(fullPath);
35363
- const resolvedTarget = resolve(dirname2(fullPath), linkTarget);
35438
+ const resolvedTarget = resolve(dirname3(fullPath), linkTarget);
35364
35439
  const rel2 = relative(realRoot, resolvedTarget);
35365
35440
  if (rel2.startsWith("..") || process.platform !== "win32" && rel2.startsWith("/")) {
35366
35441
  throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
@@ -35390,8 +35465,8 @@ async function downloadOnnxRuntime(info, targetDir) {
35390
35465
  log(`Downloading ONNX Runtime v${ORT_VERSION} for ${process.platform}/${process.arch}...`);
35391
35466
  const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
35392
35467
  try {
35393
- mkdirSync4(tmpDir, { recursive: true });
35394
- const archivePath = join5(tmpDir, `onnxruntime.${info.archiveType}`);
35468
+ mkdirSync5(tmpDir, { recursive: true });
35469
+ const archivePath = join6(tmpDir, `onnxruntime.${info.archiveType}`);
35395
35470
  await downloadFileWithCap(url, archivePath);
35396
35471
  const archiveSha256 = sha256File(archivePath);
35397
35472
  log(`ONNX Runtime archive sha256=${archiveSha256}`);
@@ -35407,16 +35482,16 @@ async function downloadOnnxRuntime(info, targetDir) {
35407
35482
  unlinkSync2(archivePath);
35408
35483
  } catch {}
35409
35484
  validateExtractedTree(tmpDir);
35410
- const extractedDir = join5(tmpDir, info.assetName, "lib");
35411
- if (!existsSync4(extractedDir)) {
35485
+ const extractedDir = join6(tmpDir, info.assetName, "lib");
35486
+ if (!existsSync5(extractedDir)) {
35412
35487
  throw new Error(`Expected directory not found: ${extractedDir}`);
35413
35488
  }
35414
- mkdirSync4(targetDir, { recursive: true });
35489
+ mkdirSync5(targetDir, { recursive: true });
35415
35490
  const libFiles = readdirSync(extractedDir).filter((f) => f.startsWith("libonnxruntime") || f.startsWith("onnxruntime"));
35416
35491
  const realFiles = [];
35417
35492
  const symlinks = [];
35418
35493
  for (const libFile of libFiles) {
35419
- const src = join5(extractedDir, libFile);
35494
+ const src = join6(extractedDir, libFile);
35420
35495
  try {
35421
35496
  const stat = lstatSync(src);
35422
35497
  log(`ORT extract: ${libFile} — isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
@@ -35431,7 +35506,7 @@ async function downloadOnnxRuntime(info, targetDir) {
35431
35506
  }
35432
35507
  }
35433
35508
  copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
35434
- const libPath = join5(targetDir, info.libName);
35509
+ const libPath = join6(targetDir, info.libName);
35435
35510
  let libHash = null;
35436
35511
  try {
35437
35512
  libHash = sha256File(libPath);
@@ -35456,8 +35531,8 @@ async function downloadOnnxRuntime(info, targetDir) {
35456
35531
  function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync2) {
35457
35532
  const requiredLibs = new Set([info.libName]);
35458
35533
  for (const libFile of realFiles) {
35459
- const src = join5(extractedDir, libFile);
35460
- const dst = join5(targetDir, libFile);
35534
+ const src = join6(extractedDir, libFile);
35535
+ const dst = join6(targetDir, libFile);
35461
35536
  try {
35462
35537
  copyFile(src, dst);
35463
35538
  if (process.platform !== "win32") {
@@ -35472,7 +35547,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
35472
35547
  }
35473
35548
  }
35474
35549
  for (const link of symlinks) {
35475
- const dst = join5(targetDir, link.name);
35550
+ const dst = join6(targetDir, link.name);
35476
35551
  try {
35477
35552
  unlinkSync2(dst);
35478
35553
  } catch {}
@@ -35486,8 +35561,8 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
35486
35561
  log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
35487
35562
  }
35488
35563
  }
35489
- const requiredPath = join5(targetDir, info.libName);
35490
- if (!existsSync4(requiredPath)) {
35564
+ const requiredPath = join6(targetDir, info.libName);
35565
+ if (!existsSync5(requiredPath)) {
35491
35566
  rmSync2(targetDir, { recursive: true, force: true });
35492
35567
  throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
35493
35568
  }
@@ -35513,17 +35588,17 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
35513
35588
  ...sha256 ? { sha256 } : {},
35514
35589
  archiveSha256
35515
35590
  };
35516
- writeFileSync(join5(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
35591
+ writeFileSync(join6(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
35517
35592
  } catch (err) {
35518
35593
  log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
35519
35594
  }
35520
35595
  }
35521
35596
  function readOnnxInstalledMeta(installDir) {
35522
- const path = join5(installDir, ONNX_INSTALLED_META_FILE);
35597
+ const path = join6(installDir, ONNX_INSTALLED_META_FILE);
35523
35598
  try {
35524
35599
  if (!statSync2(path).isFile())
35525
35600
  return null;
35526
- const raw = readFileSync(path, "utf8");
35601
+ const raw = readFileSync2(path, "utf8");
35527
35602
  const parsed = JSON.parse(raw);
35528
35603
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
35529
35604
  return null;
@@ -35539,7 +35614,7 @@ function readOnnxInstalledMeta(installDir) {
35539
35614
  }
35540
35615
  function sha256File(path) {
35541
35616
  const hash = createHash2("sha256");
35542
- hash.update(readFileSync(path));
35617
+ hash.update(readFileSync2(path));
35543
35618
  return hash.digest("hex");
35544
35619
  }
35545
35620
  function acquireLock(lockPath) {
@@ -35567,7 +35642,7 @@ ${new Date().toISOString()}
35567
35642
  let owningPid = null;
35568
35643
  let lockMtimeMs = 0;
35569
35644
  try {
35570
- const raw = readFileSync(lockPath, "utf8");
35645
+ const raw = readFileSync2(lockPath, "utf8");
35571
35646
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
35572
35647
  const parsed = Number.parseInt(firstLine, 10);
35573
35648
  if (Number.isFinite(parsed) && parsed > 0)
@@ -35593,7 +35668,7 @@ function releaseLock(lockPath) {
35593
35668
  try {
35594
35669
  let owningPid = null;
35595
35670
  try {
35596
- const raw = readFileSync(lockPath, "utf8");
35671
+ const raw = readFileSync2(lockPath, "utf8");
35597
35672
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
35598
35673
  const parsed = Number.parseInt(firstLine, 10);
35599
35674
  if (Number.isFinite(parsed) && parsed > 0)
@@ -35661,6 +35736,7 @@ function isHomeDirectoryRoot(normalizedKey) {
35661
35736
 
35662
35737
  class BridgePool {
35663
35738
  bridges = new Map;
35739
+ staleBridges = new Set;
35664
35740
  binaryPath;
35665
35741
  maxPoolSize;
35666
35742
  idleTimeoutMs;
@@ -35683,6 +35759,7 @@ class BridgePool {
35683
35759
  onConfigureWarnings: options.onConfigureWarnings,
35684
35760
  onBashCompletion: options.onBashCompletion,
35685
35761
  onBashLongRunning: options.onBashLongRunning,
35762
+ onBashPatternMatch: options.onBashPatternMatch,
35686
35763
  errorPrefix: options.errorPrefix,
35687
35764
  logger: options.logger
35688
35765
  };
@@ -35734,6 +35811,12 @@ class BridgePool {
35734
35811
  this.bridges.delete(dir);
35735
35812
  }
35736
35813
  }
35814
+ for (const bridge of this.staleBridges) {
35815
+ if (bridge.hasPendingRequests())
35816
+ continue;
35817
+ bridge.shutdown().catch((err) => this.error("stale cleanup shutdown failed:", err));
35818
+ this.staleBridges.delete(bridge);
35819
+ }
35737
35820
  }
35738
35821
  evictLRU() {
35739
35822
  let oldestDir = null;
@@ -35757,14 +35840,21 @@ class BridgePool {
35757
35840
  clearInterval(this.cleanupTimer);
35758
35841
  this.cleanupTimer = null;
35759
35842
  }
35760
- const shutdowns = Array.from(this.bridges.values()).map((e) => e.bridge.shutdown());
35843
+ const shutdowns = [
35844
+ ...Array.from(this.bridges.values(), (e) => e.bridge.shutdown()),
35845
+ ...Array.from(this.staleBridges.values(), (bridge) => bridge.shutdown())
35846
+ ];
35761
35847
  this.bridges.clear();
35848
+ this.staleBridges.clear();
35762
35849
  await Promise.allSettled(shutdowns);
35763
35850
  }
35764
35851
  async replaceBinary(newPath) {
35765
35852
  this.binaryPath = newPath;
35853
+ for (const entry of this.bridges.values()) {
35854
+ this.staleBridges.add(entry.bridge);
35855
+ }
35766
35856
  this.bridges.clear();
35767
- this.log(`Binary path updated to ${newPath}. All bridges cleared — next calls will use the new binary.`);
35857
+ this.log(`Binary path updated to ${newPath}. Active bridges marked stale — next calls will use the new binary.`);
35768
35858
  return newPath;
35769
35859
  }
35770
35860
  log(message, meta) {
@@ -35814,11 +35904,11 @@ function normalizeKey(projectRoot) {
35814
35904
  }
35815
35905
  }
35816
35906
  // ../aft-bridge/dist/url-fetch.js
35817
- import { createHash as createHash3 } from "node:crypto";
35907
+ import { createHash as createHash3, randomBytes } from "node:crypto";
35818
35908
  import { lookup } from "node:dns/promises";
35819
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
35909
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync2, readFileSync as readFileSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
35820
35910
  import { isIP } from "node:net";
35821
- import { join as join6 } from "node:path";
35911
+ import { join as join7 } from "node:path";
35822
35912
 
35823
35913
  // ../../node_modules/.bun/undici@7.25.0/node_modules/undici/index.js
35824
35914
  var __filename = "/home/runner/work/aft/aft/node_modules/.bun/undici@7.25.0/node_modules/undici/index.js";
@@ -35968,16 +36058,16 @@ var FETCH_TIMEOUT_MS = 30000;
35968
36058
  var BODY_CHUNK_TIMEOUT_MS = 15000;
35969
36059
  var MAX_REDIRECTS = 5;
35970
36060
  function cacheDir(storageDir) {
35971
- return join6(storageDir, "url_cache");
36061
+ return join7(storageDir, "url_cache");
35972
36062
  }
35973
36063
  function hashUrl(url) {
35974
36064
  return createHash3("sha256").update(url).digest("hex").slice(0, 16);
35975
36065
  }
35976
36066
  function metaPath(storageDir, hash) {
35977
- return join6(cacheDir(storageDir), `${hash}.meta.json`);
36067
+ return join7(cacheDir(storageDir), `${hash}.meta.json`);
35978
36068
  }
35979
36069
  function contentPath(storageDir, hash, extension) {
35980
- return join6(cacheDir(storageDir), `${hash}${extension}`);
36070
+ return join7(cacheDir(storageDir), `${hash}${extension}`);
35981
36071
  }
35982
36072
  function _isPrivateIpv4(address) {
35983
36073
  const parts = address.split(".").map((part) => Number.parseInt(part, 10));
@@ -36102,13 +36192,21 @@ function resolveRedirectUrl(currentUrl, location) {
36102
36192
  }
36103
36193
  return new URL(location, currentUrl);
36104
36194
  }
36195
+ var isBunRuntime = typeof globalThis.Bun !== "undefined";
36105
36196
  async function fetchWithRedirects(startUrl, allowPrivate, deps = {}) {
36106
36197
  let currentUrl = startUrl;
36107
- const fetchImpl2 = deps.fetchImpl ?? $fetch;
36198
+ const fetchImpl2 = deps.fetchImpl ?? (isBunRuntime ? globalThis.fetch : $fetch);
36108
36199
  const dnsLookup = deps.lookup ?? lookup;
36109
36200
  for (let redirectCount = 0;redirectCount <= MAX_REDIRECTS; redirectCount++) {
36110
36201
  const validatedIp = await assertPublicUrl(currentUrl, allowPrivate, dnsLookup);
36111
- const dispatcher = validatedIp ? (deps.dispatcherFactory ?? createPinnedDispatcher)(validatedIp) : undefined;
36202
+ let dispatcher;
36203
+ if (validatedIp) {
36204
+ if (deps.dispatcherFactory) {
36205
+ dispatcher = deps.dispatcherFactory(validatedIp);
36206
+ } else if (!isBunRuntime) {
36207
+ dispatcher = createPinnedDispatcher(validatedIp);
36208
+ }
36209
+ }
36112
36210
  const controller = new AbortController;
36113
36211
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
36114
36212
  let response;
@@ -36119,7 +36217,7 @@ async function fetchWithRedirects(startUrl, allowPrivate, deps = {}) {
36119
36217
  dispatcher,
36120
36218
  headers: {
36121
36219
  "user-agent": "aft-opencode-plugin",
36122
- accept: "application/vnd.github.raw, text/markdown, text/x-markdown, text/html;q=0.9, text/plain;q=0.5"
36220
+ accept: "application/vnd.github.raw, text/markdown, text/x-markdown, text/html;q=0.9, application/json;q=0.8, text/plain;q=0.5"
36123
36221
  }
36124
36222
  });
36125
36223
  } catch (err) {
@@ -36160,6 +36258,9 @@ function resolveExtension(contentType) {
36160
36258
  if (lower === "text/plain") {
36161
36259
  return ".md";
36162
36260
  }
36261
+ if (lower === "application/json" || lower === "application/ld+json" || lower.endsWith("+json")) {
36262
+ return ".json";
36263
+ }
36163
36264
  return null;
36164
36265
  }
36165
36266
  async function fetchUrlToTempFile(url, storageDir, options = {}) {
@@ -36175,15 +36276,15 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
36175
36276
  const allowPrivate = options.allowPrivate === true;
36176
36277
  await assertPublicUrl(parsed, allowPrivate, options.lookup);
36177
36278
  const dir = cacheDir(storageDir);
36178
- mkdirSync5(dir, { recursive: true });
36279
+ mkdirSync6(dir, { recursive: true });
36179
36280
  const hash = hashUrl(url);
36180
36281
  const metaFile = metaPath(storageDir, hash);
36181
- if (existsSync5(metaFile)) {
36282
+ if (existsSync6(metaFile)) {
36182
36283
  try {
36183
- const meta2 = JSON.parse(readFileSync2(metaFile, "utf8"));
36284
+ const meta2 = JSON.parse(readFileSync3(metaFile, "utf8"));
36184
36285
  const age = Date.now() - meta2.fetchedAt;
36185
36286
  const cached = contentPath(storageDir, hash, meta2.extension);
36186
- if (age < CACHE_TTL_MS && existsSync5(cached)) {
36287
+ if (age < CACHE_TTL_MS && existsSync6(cached)) {
36187
36288
  log(`URL cache hit: ${url} (${Math.round(age / 1000)}s old)`);
36188
36289
  return cached;
36189
36290
  }
@@ -36249,19 +36350,20 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
36249
36350
  }
36250
36351
  const body = Buffer.concat(chunks);
36251
36352
  const contentFile = contentPath(storageDir, hash, extension);
36252
- const tmpContent = `${contentFile}.tmp-${process.pid}`;
36353
+ const nonce = randomBytes(8).toString("hex");
36354
+ const tmpContent = `${contentFile}.tmp-${process.pid}-${nonce}`;
36253
36355
  writeFileSync2(tmpContent, body);
36254
- const { renameSync: renameSync3 } = await import("node:fs");
36255
- renameSync3(tmpContent, contentFile);
36356
+ const { renameSync: renameSync4 } = await import("node:fs");
36357
+ renameSync4(tmpContent, contentFile);
36256
36358
  const meta = {
36257
36359
  url,
36258
36360
  contentType,
36259
36361
  extension,
36260
36362
  fetchedAt: Date.now()
36261
36363
  };
36262
- const tmpMeta = `${metaFile}.tmp-${process.pid}`;
36364
+ const tmpMeta = `${metaFile}.tmp-${process.pid}-${nonce}`;
36263
36365
  writeFileSync2(tmpMeta, JSON.stringify(meta));
36264
- renameSync3(tmpMeta, metaFile);
36366
+ renameSync4(tmpMeta, metaFile);
36265
36367
  log(`URL cached (${total} bytes): ${url}`);
36266
36368
  return contentFile;
36267
36369
  }
@@ -36499,7 +36601,7 @@ function markExplicitControl(sessionID, taskId, trackOutstanding = true) {
36499
36601
  if (idx >= 0) {
36500
36602
  const completion = state.pendingCompletions[idx];
36501
36603
  state.pendingCompletions.splice(idx, 1);
36502
- queuePendingPatternMatch(state, completionToExitPattern(completion));
36604
+ queuePendingPatternMatch(state, completionToExitPattern(completion, true));
36503
36605
  state.wakeDeferredTaskIds.delete(taskId);
36504
36606
  }
36505
36607
  }
@@ -36527,13 +36629,18 @@ function routeExplicitControlCompletions(state) {
36527
36629
  state.outstandingTaskIds.delete(completion.task_id);
36528
36630
  state.explicitControlTasks.delete(completion.task_id);
36529
36631
  state.wakeDeferredTaskIds.delete(completion.task_id);
36530
- queuePendingPatternMatch(state, completionToExitPattern(completion));
36632
+ queuePendingPatternMatch(state, completionToExitPattern(completion, true));
36531
36633
  } else {
36532
36634
  remaining.push(completion);
36533
36635
  }
36534
36636
  }
36535
36637
  state.pendingCompletions = remaining;
36536
36638
  }
36639
+ async function handlePushedPatternMatch(drainContext, frame) {
36640
+ const state = stateFor(drainContext.sessionID);
36641
+ queuePendingPatternMatch(state, frame);
36642
+ await triggerWakeIfPending(drainContext, true);
36643
+ }
36537
36644
  function ingestBgCompletions(sessionID, completions) {
36538
36645
  if (!Array.isArray(completions) || completions.length === 0)
36539
36646
  return [];
@@ -36549,7 +36656,7 @@ function ingestBgCompletions(sessionID, completions) {
36549
36656
  if (state.explicitControlTasks.has(completion.task_id)) {
36550
36657
  state.outstandingTaskIds.delete(completion.task_id);
36551
36658
  state.explicitControlTasks.delete(completion.task_id);
36552
- queuePendingPatternMatch(state, completionToExitPattern(completion));
36659
+ queuePendingPatternMatch(state, completionToExitPattern(completion, true));
36553
36660
  continue;
36554
36661
  }
36555
36662
  if (!state.outstandingTaskIds.has(completion.task_id)) {
@@ -36585,6 +36692,8 @@ async function appendToolResultBgCompletions(drainContext, content) {
36585
36692
  if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0 && state.pendingPatternMatches.length === 0)
36586
36693
  return;
36587
36694
  const deliveredCompletions = [...state.pendingCompletions];
36695
+ const deliveredPatternMatches = [...state.pendingPatternMatches];
36696
+ const completionAcks = completionAcksForDelivery(deliveredCompletions, deliveredPatternMatches);
36588
36697
  const reminder = formatCombinedSystemReminder(state.pendingCompletions, state.pendingLongRunning, state.pendingPatternMatches);
36589
36698
  state.pendingCompletions = [];
36590
36699
  for (const completion of deliveredCompletions) {
@@ -36594,7 +36703,7 @@ async function appendToolResultBgCompletions(drainContext, content) {
36594
36703
  state.pendingPatternMatches = [];
36595
36704
  state.wakeRetryAttempts = 0;
36596
36705
  state.wakeHardStopped = false;
36597
- await ackCompletions(drainContext, deliveredCompletions);
36706
+ await ackCompletions(drainContext, completionAcks);
36598
36707
  if (state.debounceTimer) {
36599
36708
  clearTimeout(state.debounceTimer);
36600
36709
  state.debounceTimer = null;
@@ -36757,7 +36866,8 @@ function scheduleWake(state, sendWake, onSendFailure, includeDeferredCompletions
36757
36866
  state.wakeDeferredTaskIds.delete(taskId);
36758
36867
  state.pendingLongRunning = [];
36759
36868
  state.pendingPatternMatches = [];
36760
- sendWake(reminder, pending).then(() => {
36869
+ const completionAcks = completionAcksForDelivery(pending, pendingPatternMatches);
36870
+ sendWake(reminder, completionAcks).then(() => {
36761
36871
  state.retryDelayMs = null;
36762
36872
  state.wakeRetryAttempts = 0;
36763
36873
  state.wakeHardStopped = false;
@@ -36822,7 +36932,7 @@ function ingestDrainedBgCompletions(sessionID, completions) {
36822
36932
  state.outstandingTaskIds.delete(completion.task_id);
36823
36933
  if (state.explicitControlTasks.has(completion.task_id)) {
36824
36934
  state.explicitControlTasks.delete(completion.task_id);
36825
- queuePendingPatternMatch(state, completionToExitPattern(completion));
36935
+ queuePendingPatternMatch(state, completionToExitPattern(completion, true));
36826
36936
  continue;
36827
36937
  }
36828
36938
  if (state.consumedTaskIds.has(completion.task_id))
@@ -36858,10 +36968,10 @@ function bufferUnknownCompletion(state, completion) {
36858
36968
  function pruneUnknownCompletions(state, now) {
36859
36969
  state.unknownCompletions = state.unknownCompletions.filter((entry) => now - entry.receivedAt <= UNKNOWN_COMPLETION_TTL_MS);
36860
36970
  }
36861
- function completionToExitPattern(completion) {
36971
+ function completionToExitPattern(completion, ackCompletionOnDelivery = false) {
36862
36972
  const status = formatStatus(completion);
36863
36973
  const preview = formatOutputPreview(completion).replace(/^ {4}/gm, "").slice(-300);
36864
- return {
36974
+ const entry = {
36865
36975
  task_id: completion.task_id,
36866
36976
  session_id: "",
36867
36977
  watch_id: "exit",
@@ -36872,6 +36982,20 @@ ${preview}` : `task ${completion.task_id} exited (${status})`,
36872
36982
  once: true,
36873
36983
  reason: "task_exit"
36874
36984
  };
36985
+ if (ackCompletionOnDelivery)
36986
+ entry.ackCompletionOnDelivery = true;
36987
+ return entry;
36988
+ }
36989
+ function completionAcksForDelivery(completions, patternMatches) {
36990
+ const acks = [...completions];
36991
+ const ackedTaskIds = new Set(acks.map((completion) => completion.task_id));
36992
+ for (const match of patternMatches) {
36993
+ if (!match.ackCompletionOnDelivery || ackedTaskIds.has(match.task_id))
36994
+ continue;
36995
+ acks.push({ task_id: match.task_id, status: "unknown", exit_code: null, command: "" });
36996
+ ackedTaskIds.add(match.task_id);
36997
+ }
36998
+ return acks;
36875
36999
  }
36876
37000
  function isBgCompletion(value) {
36877
37001
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -36941,7 +37065,7 @@ import {
36941
37065
  // package.json
36942
37066
  var package_default = {
36943
37067
  name: "@cortexkit/aft-pi",
36944
- version: "0.30.0",
37068
+ version: "0.30.2",
36945
37069
  type: "module",
36946
37070
  description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
36947
37071
  main: "dist/index.js",
@@ -36963,7 +37087,7 @@ var package_default = {
36963
37087
  prepublishOnly: "bun run build"
36964
37088
  },
36965
37089
  dependencies: {
36966
- "@cortexkit/aft-bridge": "0.30.0",
37090
+ "@cortexkit/aft-bridge": "0.30.2",
36967
37091
  "@xterm/headless": "^5.5.0",
36968
37092
  "comment-json": "^5.0.0",
36969
37093
  diff: "^8.0.4",
@@ -36971,12 +37095,12 @@ var package_default = {
36971
37095
  zod: "^4.1.8"
36972
37096
  },
36973
37097
  optionalDependencies: {
36974
- "@cortexkit/aft-darwin-arm64": "0.30.0",
36975
- "@cortexkit/aft-darwin-x64": "0.30.0",
36976
- "@cortexkit/aft-linux-arm64": "0.30.0",
36977
- "@cortexkit/aft-linux-x64": "0.30.0",
36978
- "@cortexkit/aft-win32-arm64": "0.30.0",
36979
- "@cortexkit/aft-win32-x64": "0.30.0"
37098
+ "@cortexkit/aft-darwin-arm64": "0.30.2",
37099
+ "@cortexkit/aft-darwin-x64": "0.30.2",
37100
+ "@cortexkit/aft-linux-arm64": "0.30.2",
37101
+ "@cortexkit/aft-linux-x64": "0.30.2",
37102
+ "@cortexkit/aft-win32-arm64": "0.30.2",
37103
+ "@cortexkit/aft-win32-x64": "0.30.2"
36980
37104
  },
36981
37105
  devDependencies: {
36982
37106
  "@earendil-works/pi-coding-agent": "*",
@@ -37170,6 +37294,22 @@ function formatStatusDialogMessage(status) {
37170
37294
  }
37171
37295
 
37172
37296
  // src/tools/_shared.ts
37297
+ import { Type } from "typebox";
37298
+ var optionalInt = (_min, _max) => Type.Optional(Type.Any({ description: "(integer)" }));
37299
+ function coerceOptionalInt(v, paramName, min, max) {
37300
+ if (v === undefined || v === null || v === "")
37301
+ return;
37302
+ if (typeof v === "number" && (v === 0 || !Number.isFinite(v)))
37303
+ return;
37304
+ const n = typeof v === "string" ? Number(v) : v;
37305
+ if (typeof n !== "number" || !Number.isInteger(n)) {
37306
+ throw new Error(`${paramName} must be an integer between ${min} and ${max}`);
37307
+ }
37308
+ if (n < min || n > max) {
37309
+ throw new Error(`${paramName} must be between ${min} and ${max}`);
37310
+ }
37311
+ return n;
37312
+ }
37173
37313
  var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
37174
37314
  callers: 60000,
37175
37315
  trace_to: 60000,
@@ -37252,9 +37392,13 @@ class AftStatusDialogComponent {
37252
37392
  async fetchOnce() {
37253
37393
  try {
37254
37394
  const bridge = bridgeFor(this.props.pluginCtx, this.props.extCtx.cwd);
37395
+ const sessionId = resolveSessionId(this.props.extCtx);
37255
37396
  const cached = bridge.getCachedStatus();
37256
- const response = cached ? { success: true, ...cached } : await callBridge(bridge, "status", {}, this.props.extCtx);
37257
- if (!cached) {
37397
+ const cachedSession = cached?.session;
37398
+ const cachedSessionId = cachedSession?.id;
37399
+ const cacheUsable = cached !== null && cachedSessionId !== undefined && cachedSessionId === sessionId;
37400
+ const response = cacheUsable ? { success: true, ...cached } : await callBridge(bridge, "status", {}, this.props.extCtx);
37401
+ if (!cacheUsable) {
37258
37402
  bridge.cacheStatusSnapshot(response);
37259
37403
  }
37260
37404
  if (this.closed)
@@ -37483,9 +37627,13 @@ function registerStatusCommand(pi, ctx) {
37483
37627
  return;
37484
37628
  }
37485
37629
  const bridge = bridgeFor(ctx, extCtx.cwd);
37630
+ const sessionId = resolveSessionId(extCtx);
37486
37631
  const cached = bridge.getCachedStatus();
37487
- const response = cached ? { success: true, ...cached } : await callBridge(bridge, "status", {}, extCtx);
37488
- if (!cached) {
37632
+ const cachedSession = cached?.session;
37633
+ const cachedSessionId = cachedSession?.id;
37634
+ const cacheUsable = cached !== null && cachedSessionId !== undefined && cachedSessionId === sessionId;
37635
+ const response = cacheUsable ? { success: true, ...cached } : await callBridge(bridge, "status", {}, extCtx);
37636
+ if (!cacheUsable) {
37489
37637
  bridge.cacheStatusSnapshot(response);
37490
37638
  }
37491
37639
  const snapshot = coerceAftStatus(response);
@@ -37505,9 +37653,9 @@ function registerStatusCommand(pi, ctx) {
37505
37653
 
37506
37654
  // src/config.ts
37507
37655
  var import_comment_json = __toESM(require_src2(), 1);
37508
- import { existsSync as existsSync6, readFileSync as readFileSync3, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
37656
+ import { existsSync as existsSync7, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
37509
37657
  import { homedir as homedir6 } from "node:os";
37510
- import { join as join8 } from "node:path";
37658
+ import { join as join9 } from "node:path";
37511
37659
 
37512
37660
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
37513
37661
  var exports_external = {};
@@ -51351,13 +51499,13 @@ function migrateExperimentalBashBlock(rawConfig, configPath, logger) {
51351
51499
  return movedKeys;
51352
51500
  }
51353
51501
  function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
51354
- if (!existsSync6(configPath)) {
51502
+ if (!existsSync7(configPath)) {
51355
51503
  return { migrated: false, oldKeys: [] };
51356
51504
  }
51357
51505
  let tmpPath = null;
51358
51506
  let oldKeys = [];
51359
51507
  try {
51360
- const content = readFileSync3(configPath, "utf-8");
51508
+ const content = readFileSync4(configPath, "utf-8");
51361
51509
  const rawConfig = import_comment_json.parse(content);
51362
51510
  if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
51363
51511
  return { migrated: false, oldKeys: [] };
@@ -51374,7 +51522,7 @@ function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
51374
51522
  ${serialized}` : serialized;
51375
51523
  tmpPath = `${configPath}.tmp.${process.pid}`;
51376
51524
  writeFileSync3(tmpPath, nextContent, "utf-8");
51377
- renameSync3(tmpPath, configPath);
51525
+ renameSync4(tmpPath, configPath);
51378
51526
  logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
51379
51527
  return { migrated: true, oldKeys };
51380
51528
  } catch (err) {
@@ -51394,17 +51542,17 @@ ${serialized}` : serialized;
51394
51542
  function detectConfigFile(basePath) {
51395
51543
  const jsoncPath = `${basePath}.jsonc`;
51396
51544
  const jsonPath = `${basePath}.json`;
51397
- if (existsSync6(jsoncPath))
51545
+ if (existsSync7(jsoncPath))
51398
51546
  return { format: "jsonc", path: jsoncPath };
51399
- if (existsSync6(jsonPath))
51547
+ if (existsSync7(jsonPath))
51400
51548
  return { format: "json", path: jsonPath };
51401
51549
  return { format: "none", path: jsonPath };
51402
51550
  }
51403
51551
  function loadConfigFromPath(configPath) {
51404
51552
  try {
51405
- if (!existsSync6(configPath))
51553
+ if (!existsSync7(configPath))
51406
51554
  return null;
51407
- const content = readFileSync3(configPath, "utf-8");
51555
+ const content = readFileSync4(configPath, "utf-8");
51408
51556
  const rawConfig = import_comment_json.parse(content);
51409
51557
  if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
51410
51558
  warn2(`Config validation error in ${configPath}: root must be an object`);
@@ -51575,15 +51723,15 @@ function mergeConfigs(base, override) {
51575
51723
  };
51576
51724
  }
51577
51725
  function getGlobalPiDir() {
51578
- return join8(homedir6(), ".pi", "agent");
51726
+ return join9(homedir6(), ".pi", "agent");
51579
51727
  }
51580
51728
  function loadAftConfig(projectDirectory) {
51581
- const userBasePath = join8(getGlobalPiDir(), "aft");
51729
+ const userBasePath = join9(getGlobalPiDir(), "aft");
51582
51730
  migrateAftConfigFile(`${userBasePath}.jsonc`);
51583
51731
  migrateAftConfigFile(`${userBasePath}.json`);
51584
51732
  const userDetected = detectConfigFile(userBasePath);
51585
51733
  const userConfigPath = userDetected.format !== "none" ? userDetected.path : `${userBasePath}.json`;
51586
- const projectBasePath = join8(projectDirectory, ".pi", "aft");
51734
+ const projectBasePath = join9(projectDirectory, ".pi", "aft");
51587
51735
  migrateAftConfigFile(`${projectBasePath}.jsonc`);
51588
51736
  migrateAftConfigFile(`${projectBasePath}.json`);
51589
51737
  const projectDetected = detectConfigFile(projectBasePath);
@@ -51610,49 +51758,49 @@ function loadAftConfig(projectDirectory) {
51610
51758
  // src/lsp-auto-install.ts
51611
51759
  import { spawn as spawn2 } from "node:child_process";
51612
51760
  import { createHash as createHash4 } from "node:crypto";
51613
- import { createReadStream, mkdirSync as mkdirSync7, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
51614
- import { join as join11 } from "node:path";
51761
+ import { createReadStream, mkdirSync as mkdirSync8, readFileSync as readFileSync7, renameSync as renameSync5, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
51762
+ import { join as join12 } from "node:path";
51615
51763
 
51616
51764
  // src/lsp-cache.ts
51617
51765
  import {
51618
51766
  closeSync as closeSync3,
51619
- mkdirSync as mkdirSync6,
51767
+ mkdirSync as mkdirSync7,
51620
51768
  openSync as openSync3,
51621
- readFileSync as readFileSync4,
51769
+ readFileSync as readFileSync5,
51622
51770
  statSync as statSync3,
51623
51771
  unlinkSync as unlinkSync5,
51624
51772
  writeFileSync as writeFileSync4
51625
51773
  } from "node:fs";
51626
51774
  import { homedir as homedir7 } from "node:os";
51627
- import { join as join9 } from "node:path";
51775
+ import { join as join10 } from "node:path";
51628
51776
  function aftCacheBase() {
51629
51777
  const override = process.env.AFT_CACHE_DIR;
51630
51778
  if (override && override.length > 0)
51631
51779
  return override;
51632
51780
  if (process.platform === "win32") {
51633
51781
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
51634
- const base2 = localAppData || join9(homedir7(), "AppData", "Local");
51635
- return join9(base2, "aft");
51782
+ const base2 = localAppData || join10(homedir7(), "AppData", "Local");
51783
+ return join10(base2, "aft");
51636
51784
  }
51637
- const base = process.env.XDG_CACHE_HOME || join9(homedir7(), ".cache");
51638
- return join9(base, "aft");
51785
+ const base = process.env.XDG_CACHE_HOME || join10(homedir7(), ".cache");
51786
+ return join10(base, "aft");
51639
51787
  }
51640
51788
  function lspCacheRoot() {
51641
- return join9(aftCacheBase(), "lsp-packages");
51789
+ return join10(aftCacheBase(), "lsp-packages");
51642
51790
  }
51643
51791
  function lspPackageDir(npmPackage) {
51644
- return join9(lspCacheRoot(), encodeURIComponent(npmPackage));
51792
+ return join10(lspCacheRoot(), encodeURIComponent(npmPackage));
51645
51793
  }
51646
51794
  function lspBinaryPath(npmPackage, binary) {
51647
- return join9(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
51795
+ return join10(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
51648
51796
  }
51649
51797
  function lspBinDir(npmPackage) {
51650
- return join9(lspPackageDir(npmPackage), "node_modules", ".bin");
51798
+ return join10(lspPackageDir(npmPackage), "node_modules", ".bin");
51651
51799
  }
51652
51800
  function isInstalled(npmPackage, binary) {
51653
51801
  for (const candidate of lspBinaryCandidates(binary)) {
51654
51802
  try {
51655
- if (statSync3(join9(lspBinDir(npmPackage), candidate)).isFile())
51803
+ if (statSync3(join10(lspBinDir(npmPackage), candidate)).isFile())
51656
51804
  return true;
51657
51805
  } catch {}
51658
51806
  }
@@ -51666,23 +51814,23 @@ function lspBinaryCandidates(binary) {
51666
51814
  var INSTALLED_META_FILE = ".aft-installed";
51667
51815
  function writeInstalledMetaIn(installDir, version2, sha256) {
51668
51816
  try {
51669
- mkdirSync6(installDir, { recursive: true });
51817
+ mkdirSync7(installDir, { recursive: true });
51670
51818
  const meta3 = {
51671
51819
  version: version2,
51672
51820
  installedAt: new Date().toISOString(),
51673
51821
  ...sha256 ? { sha256 } : {}
51674
51822
  };
51675
- writeFileSync4(join9(installDir, INSTALLED_META_FILE), JSON.stringify(meta3), "utf8");
51823
+ writeFileSync4(join10(installDir, INSTALLED_META_FILE), JSON.stringify(meta3), "utf8");
51676
51824
  } catch (err) {
51677
51825
  log2(`[lsp-cache] failed to write installed-meta in ${installDir}: ${err}`);
51678
51826
  }
51679
51827
  }
51680
51828
  function readInstalledMetaIn(installDir) {
51681
- const path2 = join9(installDir, INSTALLED_META_FILE);
51829
+ const path2 = join10(installDir, INSTALLED_META_FILE);
51682
51830
  try {
51683
51831
  if (!statSync3(path2).isFile())
51684
51832
  return null;
51685
- const raw = readFileSync4(path2, "utf8");
51833
+ const raw = readFileSync5(path2, "utf8");
51686
51834
  const parsed = JSON.parse(raw);
51687
51835
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
51688
51836
  return null;
@@ -51702,11 +51850,11 @@ function readInstalledMeta(packageKey) {
51702
51850
  return readInstalledMetaIn(lspPackageDir(packageKey));
51703
51851
  }
51704
51852
  function lockPath(npmPackage) {
51705
- return join9(lspPackageDir(npmPackage), ".aft-installing");
51853
+ return join10(lspPackageDir(npmPackage), ".aft-installing");
51706
51854
  }
51707
51855
  var STALE_LOCK_MS2 = 30 * 60 * 1000;
51708
51856
  function acquireInstallLock(lockKey) {
51709
- mkdirSync6(lspPackageDir(lockKey), { recursive: true });
51857
+ mkdirSync7(lspPackageDir(lockKey), { recursive: true });
51710
51858
  const lock = lockPath(lockKey);
51711
51859
  const tryClaim = () => {
51712
51860
  try {
@@ -51732,7 +51880,7 @@ ${new Date().toISOString()}
51732
51880
  let owningPid = null;
51733
51881
  let lockMtimeMs = 0;
51734
51882
  try {
51735
- const raw = readFileSync4(lock, "utf8");
51883
+ const raw = readFileSync5(lock, "utf8");
51736
51884
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
51737
51885
  const parsed = Number.parseInt(firstLine, 10);
51738
51886
  if (Number.isFinite(parsed) && parsed > 0)
@@ -51770,7 +51918,7 @@ function releaseInstallLock(lockKey) {
51770
51918
  try {
51771
51919
  let owningPid = null;
51772
51920
  try {
51773
- const raw = readFileSync4(lock, "utf8");
51921
+ const raw = readFileSync5(lock, "utf8");
51774
51922
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
51775
51923
  const parsed = Number.parseInt(firstLine, 10);
51776
51924
  if (Number.isFinite(parsed) && parsed > 0)
@@ -51809,9 +51957,9 @@ async function withInstallLock(lockKey, task) {
51809
51957
  }
51810
51958
  var VERSION_CHECK_FILE = ".aft-version-check";
51811
51959
  function readVersionCheck(npmPackage) {
51812
- const file2 = join9(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
51960
+ const file2 = join10(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
51813
51961
  try {
51814
- const raw = readFileSync4(file2, "utf8");
51962
+ const raw = readFileSync5(file2, "utf8");
51815
51963
  const parsed = JSON.parse(raw);
51816
51964
  if (typeof parsed.last_checked === "string") {
51817
51965
  return {
@@ -51825,8 +51973,8 @@ function readVersionCheck(npmPackage) {
51825
51973
  }
51826
51974
  }
51827
51975
  function writeVersionCheck(npmPackage, latest) {
51828
- mkdirSync6(lspPackageDir(npmPackage), { recursive: true });
51829
- const file2 = join9(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
51976
+ mkdirSync7(lspPackageDir(npmPackage), { recursive: true });
51977
+ const file2 = join10(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
51830
51978
  const record2 = {
51831
51979
  last_checked: new Date().toISOString(),
51832
51980
  latest_eligible: latest
@@ -51988,8 +52136,8 @@ var NPM_LSP_TABLE = [
51988
52136
  ];
51989
52137
 
51990
52138
  // src/lsp-project-relevance.ts
51991
- import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
51992
- import { join as join10 } from "node:path";
52139
+ import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "node:fs";
52140
+ import { join as join11 } from "node:path";
51993
52141
  var MAX_WALK_DIRS = 200;
51994
52142
  var MAX_WALK_DEPTH = 4;
51995
52143
  var NOISE_DIRS = new Set([
@@ -52006,7 +52154,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
52006
52154
  if (!rootMarkers)
52007
52155
  return false;
52008
52156
  for (const marker of rootMarkers) {
52009
- if (existsSync7(join10(projectRoot, marker)))
52157
+ if (existsSync8(join11(projectRoot, marker)))
52010
52158
  return true;
52011
52159
  }
52012
52160
  return false;
@@ -52030,7 +52178,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
52030
52178
  }
52031
52179
  function readPackageJson(projectRoot) {
52032
52180
  try {
52033
- const raw = readFileSync5(join10(projectRoot, "package.json"), "utf8");
52181
+ const raw = readFileSync6(join11(projectRoot, "package.json"), "utf8");
52034
52182
  const parsed = JSON.parse(raw);
52035
52183
  if (typeof parsed !== "object" || parsed === null)
52036
52184
  return null;
@@ -52060,7 +52208,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
52060
52208
  for (const entry of entries) {
52061
52209
  if (entry.isDirectory()) {
52062
52210
  if (current.depth < MAX_WALK_DEPTH && !NOISE_DIRS.has(entry.name.toLowerCase())) {
52063
- queue.push({ dir: join10(current.dir, entry.name), depth: current.depth + 1 });
52211
+ queue.push({ dir: join11(current.dir, entry.name), depth: current.depth + 1 });
52064
52212
  }
52065
52213
  continue;
52066
52214
  }
@@ -52357,16 +52505,16 @@ function installedBinaryPath(spec) {
52357
52505
  return null;
52358
52506
  }
52359
52507
  function sha256OfFileSync(path2) {
52360
- return createHash4("sha256").update(readFileSync6(path2)).digest("hex");
52508
+ return createHash4("sha256").update(readFileSync7(path2)).digest("hex");
52361
52509
  }
52362
52510
  function quarantineCachedNpmInstall(spec, reason) {
52363
52511
  const packageDir = lspPackageDir(spec.npm);
52364
- const dest = join11(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
52512
+ const dest = join12(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
52365
52513
  warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
52366
52514
  try {
52367
- mkdirSync7(join11(dest, ".."), { recursive: true });
52515
+ mkdirSync8(join12(dest, ".."), { recursive: true });
52368
52516
  rmSync3(dest, { recursive: true, force: true });
52369
- renameSync4(packageDir, dest);
52517
+ renameSync5(packageDir, dest);
52370
52518
  } catch (err) {
52371
52519
  warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
52372
52520
  }
@@ -52413,7 +52561,6 @@ function runAutoInstall(projectRoot, config2, fetchImpl2 = fetch) {
52413
52561
  continue;
52414
52562
  }
52415
52563
  installsStarted += 1;
52416
- installingBinaries.push(spec.binary);
52417
52564
  const controller = new AbortController;
52418
52565
  const promise2 = ensureServerInstalled(spec, config2, fetchImpl2, controller.signal).then((outcome) => {
52419
52566
  if (!outcome.started)
@@ -52442,25 +52589,25 @@ function runAutoInstall(projectRoot, config2, fetchImpl2 = fetch) {
52442
52589
 
52443
52590
  // src/lsp-github-install.ts
52444
52591
  import { execFileSync as execFileSync2 } from "node:child_process";
52445
- import { createHash as createHash5, randomBytes } from "node:crypto";
52592
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
52446
52593
  import {
52447
52594
  copyFileSync as copyFileSync3,
52448
52595
  createReadStream as createReadStream2,
52449
52596
  createWriteStream as createWriteStream3,
52450
- existsSync as existsSync8,
52597
+ existsSync as existsSync9,
52451
52598
  lstatSync as lstatSync2,
52452
- mkdirSync as mkdirSync8,
52599
+ mkdirSync as mkdirSync9,
52453
52600
  readdirSync as readdirSync4,
52454
- readFileSync as readFileSync7,
52601
+ readFileSync as readFileSync8,
52455
52602
  readlinkSync as readlinkSync2,
52456
52603
  realpathSync as realpathSync3,
52457
- renameSync as renameSync5,
52604
+ renameSync as renameSync6,
52458
52605
  rmSync as rmSync4,
52459
52606
  statSync as statSync5,
52460
52607
  unlinkSync as unlinkSync6,
52461
52608
  writeFileSync as writeFileSync5
52462
52609
  } from "node:fs";
52463
- import { dirname as dirname3, join as join12, relative as relative2, resolve as resolve2 } from "node:path";
52610
+ import { dirname as dirname4, join as join13, relative as relative2, resolve as resolve2 } from "node:path";
52464
52611
  import { Readable as Readable3 } from "node:stream";
52465
52612
  import { pipeline as pipeline3 } from "node:stream/promises";
52466
52613
 
@@ -52550,26 +52697,26 @@ function detectHostPlatform() {
52550
52697
 
52551
52698
  // src/lsp-github-install.ts
52552
52699
  function ghCacheRoot() {
52553
- return join12(aftCacheBase(), "lsp-binaries");
52700
+ return join13(aftCacheBase(), "lsp-binaries");
52554
52701
  }
52555
52702
  function ghPackageDir(spec) {
52556
- return join12(ghCacheRoot(), spec.id);
52703
+ return join13(ghCacheRoot(), spec.id);
52557
52704
  }
52558
52705
  function ghBinDir(spec) {
52559
- return join12(ghPackageDir(spec), "bin");
52706
+ return join13(ghPackageDir(spec), "bin");
52560
52707
  }
52561
52708
  function ghExtractDir(spec) {
52562
- return join12(ghPackageDir(spec), "extracted");
52709
+ return join13(ghPackageDir(spec), "extracted");
52563
52710
  }
52564
52711
  var INSTALLED_META_FILE2 = ".aft-installed";
52565
52712
  function ghBinaryPath(spec, platform) {
52566
52713
  const ext = platform === "win32" ? ".exe" : "";
52567
- return join12(ghBinDir(spec), `${spec.binary}${ext}`);
52714
+ return join13(ghBinDir(spec), `${spec.binary}${ext}`);
52568
52715
  }
52569
52716
  function isGithubInstalled(spec, platform) {
52570
52717
  for (const candidate of ghBinaryCandidates(spec, platform)) {
52571
52718
  try {
52572
- if (statSync5(join12(ghBinDir(spec), candidate)).isFile())
52719
+ if (statSync5(join13(ghBinDir(spec), candidate)).isFile())
52573
52720
  return true;
52574
52721
  } catch {}
52575
52722
  }
@@ -52582,10 +52729,10 @@ function ghBinaryCandidates(spec, platform) {
52582
52729
  }
52583
52730
  function readGithubInstalledMetaIn(installDir) {
52584
52731
  try {
52585
- const path2 = join12(installDir, INSTALLED_META_FILE2);
52732
+ const path2 = join13(installDir, INSTALLED_META_FILE2);
52586
52733
  if (!statSync5(path2).isFile())
52587
52734
  return null;
52588
- const parsed = JSON.parse(readFileSync7(path2, "utf8"));
52735
+ const parsed = JSON.parse(readFileSync8(path2, "utf8"));
52589
52736
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
52590
52737
  return null;
52591
52738
  return {
@@ -52601,7 +52748,7 @@ function readGithubInstalledMetaIn(installDir) {
52601
52748
  }
52602
52749
  function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveSha256) {
52603
52750
  try {
52604
- mkdirSync8(installDir, { recursive: true });
52751
+ mkdirSync9(installDir, { recursive: true });
52605
52752
  const meta3 = {
52606
52753
  version: version2,
52607
52754
  installedAt: new Date().toISOString(),
@@ -52609,7 +52756,7 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
52609
52756
  binarySha256,
52610
52757
  ...archiveSha256 ? { archiveSha256 } : {}
52611
52758
  };
52612
- writeFileSync5(join12(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
52759
+ writeFileSync5(join13(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
52613
52760
  } catch (err) {
52614
52761
  warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
52615
52762
  }
@@ -52626,7 +52773,7 @@ function sha256OfFile(path2) {
52626
52773
  });
52627
52774
  }
52628
52775
  function sha256OfFileSync2(path2) {
52629
- return createHash5("sha256").update(readFileSync7(path2)).digest("hex");
52776
+ return createHash5("sha256").update(readFileSync8(path2)).digest("hex");
52630
52777
  }
52631
52778
  async function fetchReleaseByTag(githubRepo, tag, fetchImpl2, signal) {
52632
52779
  const candidates = [];
@@ -52802,7 +52949,7 @@ async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
52802
52949
  if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
52803
52950
  throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
52804
52951
  }
52805
- mkdirSync8(dirname3(destPath), { recursive: true });
52952
+ mkdirSync9(dirname4(destPath), { recursive: true });
52806
52953
  let bytesWritten = 0;
52807
52954
  const guard = new TransformStream({
52808
52955
  transform(chunk, controller) {
@@ -52858,7 +53005,7 @@ function validateExtraction(stagingRoot) {
52858
53005
  throw new Error(`failed to read staging dir ${dir}: ${err}`);
52859
53006
  }
52860
53007
  for (const entry of entries) {
52861
- const full = join12(dir, entry);
53008
+ const full = join13(dir, entry);
52862
53009
  let lst;
52863
53010
  try {
52864
53011
  lst = lstatSync2(full);
@@ -52923,12 +53070,12 @@ function precheckArchiveContents(archivePath, archiveType) {
52923
53070
  }
52924
53071
  }
52925
53072
  function extractArchiveSafely(archivePath, destDir, archiveType) {
52926
- const suffix = randomBytes(8).toString("hex");
53073
+ const suffix = randomBytes2(8).toString("hex");
52927
53074
  const stagingDir = `${destDir}.staging-${suffix}`;
52928
53075
  try {
52929
53076
  rmSync4(stagingDir, { recursive: true, force: true });
52930
53077
  } catch {}
52931
- mkdirSync8(stagingDir, { recursive: true });
53078
+ mkdirSync9(stagingDir, { recursive: true });
52932
53079
  try {
52933
53080
  precheckArchiveContents(archivePath, archiveType);
52934
53081
  runPlatformExtractor(archivePath, stagingDir, archiveType);
@@ -52936,7 +53083,7 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
52936
53083
  try {
52937
53084
  rmSync4(destDir, { recursive: true, force: true });
52938
53085
  } catch {}
52939
- renameSync5(stagingDir, destDir);
53086
+ renameSync6(stagingDir, destDir);
52940
53087
  } catch (err) {
52941
53088
  try {
52942
53089
  rmSync4(stagingDir, { recursive: true, force: true });
@@ -52946,12 +53093,12 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
52946
53093
  }
52947
53094
  function quarantineCachedGithubInstall(spec, reason) {
52948
53095
  const packageDir = ghPackageDir(spec);
52949
- const dest = join12(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
53096
+ const dest = join13(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
52950
53097
  warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
52951
53098
  try {
52952
- mkdirSync8(dirname3(dest), { recursive: true });
53099
+ mkdirSync9(dirname4(dest), { recursive: true });
52953
53100
  rmSync4(dest, { recursive: true, force: true });
52954
- renameSync5(packageDir, dest);
53101
+ renameSync6(packageDir, dest);
52955
53102
  } catch (err) {
52956
53103
  warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
52957
53104
  }
@@ -52959,7 +53106,7 @@ function quarantineCachedGithubInstall(spec, reason) {
52959
53106
  function validateCachedGithubInstall(spec, platform) {
52960
53107
  const packageDir = ghPackageDir(spec);
52961
53108
  const meta3 = readGithubInstalledMetaIn(packageDir);
52962
- const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join12(ghBinDir(spec), candidate)).find((candidate) => {
53109
+ const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join13(ghBinDir(spec), candidate)).find((candidate) => {
52963
53110
  try {
52964
53111
  return statSync5(candidate).isFile();
52965
53112
  } catch {
@@ -53037,7 +53184,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
53037
53184
  }
53038
53185
  const pkgDir = ghPackageDir(spec);
53039
53186
  const extractDir = ghExtractDir(spec);
53040
- const archivePath = join12(pkgDir, expected.name);
53187
+ const archivePath = join13(pkgDir, expected.name);
53041
53188
  log2(`[lsp] downloading ${spec.id} ${tag} → ${matchingAsset.url}`);
53042
53189
  try {
53043
53190
  await downloadFile(matchingAsset.url, archivePath, fetchImpl2, matchingAsset.size, signal);
@@ -53077,13 +53224,13 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
53077
53224
  unlinkSync6(archivePath);
53078
53225
  } catch {}
53079
53226
  }
53080
- const innerBinaryPath = join12(extractDir, spec.binaryPathInArchive(platform, arch, version2));
53081
- if (!existsSync8(innerBinaryPath)) {
53227
+ const innerBinaryPath = join13(extractDir, spec.binaryPathInArchive(platform, arch, version2));
53228
+ if (!existsSync9(innerBinaryPath)) {
53082
53229
  error2(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
53083
53230
  return null;
53084
53231
  }
53085
53232
  const targetBinary = ghBinaryPath(spec, platform);
53086
- mkdirSync8(dirname3(targetBinary), { recursive: true });
53233
+ mkdirSync9(dirname4(targetBinary), { recursive: true });
53087
53234
  try {
53088
53235
  copyFileSync3(innerBinaryPath, targetBinary);
53089
53236
  if (platform !== "win32") {
@@ -53162,7 +53309,7 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl2 = fetch) {
53162
53309
  if (!host) {
53163
53310
  for (const spec of GITHUB_LSP_TABLE) {
53164
53311
  try {
53165
- if (existsSync8(ghBinDir(spec))) {
53312
+ if (existsSync9(ghBinDir(spec))) {
53166
53313
  cachedBinDirs.push(ghBinDir(spec));
53167
53314
  }
53168
53315
  } catch {}
@@ -53192,7 +53339,6 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl2 = fetch) {
53192
53339
  continue;
53193
53340
  }
53194
53341
  installsStarted += 1;
53195
- installingBinaries.push(spec.binary);
53196
53342
  const controller = new AbortController;
53197
53343
  const promise2 = ensureGithubInstalled(spec, config2, fetchImpl2, host.platform, host.arch, controller.signal).then((outcome) => {
53198
53344
  if (!outcome.started)
@@ -53260,8 +53406,8 @@ function discoverRelevantGithubServers(projectRoot) {
53260
53406
  }
53261
53407
 
53262
53408
  // src/notifications.ts
53263
- import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
53264
- import { join as join13 } from "node:path";
53409
+ import { existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
53410
+ import { dirname as dirname5 } from "node:path";
53265
53411
  var WARNING_MARKER = "\uD83D\uDD27 AFT: ⚠️";
53266
53412
  var FEATURE_MARKER = "\uD83D\uDD27 AFT: ✨";
53267
53413
  function sendIgnoredMessage(client, sessionId, text) {
@@ -53354,10 +53500,10 @@ async function deliverConfigureWarnings(opts, warnings) {
53354
53500
  }
53355
53501
  }
53356
53502
  function sendFeatureAnnouncement(version2, features, footer, storageDir) {
53357
- const versionFile = join13(storageDir, "last_announced_version");
53503
+ const versionFile = repairRootScopedStorageFile(storageDir, "pi", "last_announced_version");
53358
53504
  try {
53359
- if (existsSync9(versionFile)) {
53360
- const lastVersion = readFileSync8(versionFile, "utf-8").trim();
53505
+ if (existsSync10(versionFile)) {
53506
+ const lastVersion = readFileSync9(versionFile, "utf-8").trim();
53361
53507
  if (lastVersion === version2)
53362
53508
  return;
53363
53509
  }
@@ -53372,7 +53518,7 @@ function sendFeatureAnnouncement(version2, features, footer, storageDir) {
53372
53518
  log2(sections.join(`
53373
53519
  `));
53374
53520
  try {
53375
- mkdirSync9(storageDir, { recursive: true });
53521
+ mkdirSync10(dirname5(versionFile), { recursive: true });
53376
53522
  writeFileSync6(versionFile, version2);
53377
53523
  } catch {}
53378
53524
  }
@@ -53515,7 +53661,7 @@ function registerShutdownCleanup(fn) {
53515
53661
 
53516
53662
  // src/tools/ast.ts
53517
53663
  import { StringEnum } from "@earendil-works/pi-ai";
53518
- import { Type } from "typebox";
53664
+ import { Type as Type2 } from "typebox";
53519
53665
 
53520
53666
  // src/tools/render-helpers.ts
53521
53667
  import { homedir as homedir8 } from "node:os";
@@ -53710,22 +53856,22 @@ function renderUnifiedDiff(unifiedDiff) {
53710
53856
  var AstLang = StringEnum(["typescript", "tsx", "javascript", "python", "rust", "go"], {
53711
53857
  description: "Target language"
53712
53858
  });
53713
- var SearchParams = Type.Object({
53714
- pattern: Type.String({
53859
+ var SearchParams = Type2.Object({
53860
+ pattern: Type2.String({
53715
53861
  description: "AST pattern with meta-variables (`$VAR` matches one node, `$$$` matches many). Must be a complete AST node."
53716
53862
  }),
53717
53863
  lang: AstLang,
53718
- paths: Type.Optional(Type.Array(Type.String(), { description: "Paths to search (default: ['.'])" })),
53719
- globs: Type.Optional(Type.Array(Type.String(), { description: "Include/exclude globs (prefix `!` to exclude)" })),
53720
- contextLines: Type.Optional(Type.Number({ description: "Number of context lines around each match" }))
53864
+ paths: Type2.Optional(Type2.Array(Type2.String(), { description: "Paths to search (default: ['.'])" })),
53865
+ globs: Type2.Optional(Type2.Array(Type2.String(), { description: "Include/exclude globs (prefix `!` to exclude)" })),
53866
+ contextLines: Type2.Optional(Type2.Number({ description: "Number of context lines around each match" }))
53721
53867
  });
53722
- var ReplaceParams = Type.Object({
53723
- pattern: Type.String({ description: "AST pattern with meta-variables" }),
53724
- rewrite: Type.String({ description: "Replacement pattern, can reference $VAR from pattern" }),
53868
+ var ReplaceParams = Type2.Object({
53869
+ pattern: Type2.String({ description: "AST pattern with meta-variables" }),
53870
+ rewrite: Type2.String({ description: "Replacement pattern, can reference $VAR from pattern" }),
53725
53871
  lang: AstLang,
53726
- paths: Type.Optional(Type.Array(Type.String(), { description: "Paths (default: ['.'])" })),
53727
- globs: Type.Optional(Type.Array(Type.String(), { description: "Include/exclude globs" })),
53728
- dryRun: Type.Optional(Type.Boolean({ description: "Preview without applying (default: false)" }))
53872
+ paths: Type2.Optional(Type2.Array(Type2.String(), { description: "Paths (default: ['.'])" })),
53873
+ globs: Type2.Optional(Type2.Array(Type2.String(), { description: "Include/exclude globs" })),
53874
+ dryRun: Type2.Optional(Type2.Boolean({ description: "Preview without applying (default: false)" }))
53729
53875
  });
53730
53876
  function appendHintSection(response, sections, theme) {
53731
53877
  const hint = asString(response.hint);
@@ -53916,79 +54062,68 @@ function registerAstTools(pi, ctx, surface) {
53916
54062
  // src/tools/bash.ts
53917
54063
  import * as fs3 from "node:fs/promises";
53918
54064
  import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
53919
- import { Type as Type2 } from "typebox";
54065
+ import { Type as Type3 } from "typebox";
53920
54066
  var FOREGROUND_WAIT_WINDOW_MS = 5000;
53921
54067
  var FOREGROUND_POLL_INTERVAL_MS = 100;
53922
54068
  var BASH_WAIT_POLL_INTERVAL_MS = 100;
53923
54069
  var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
53924
54070
  var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 300000;
53925
54071
  var BASH_TRANSPORT_TIMEOUT_MS = 30000;
53926
- var BashParams = Type2.Object({
53927
- command: Type2.String({
54072
+ var BashParams = Type3.Object({
54073
+ command: Type3.String({
53928
54074
  description: "Shell command to execute. Supports pipes, redirections, and shell syntax."
53929
54075
  }),
53930
- timeout: Type2.Optional(Type2.Integer({
53931
- minimum: 1,
53932
- description: "Hard kill cap in milliseconds (positive integer). When omitted, the task can run up to 30 minutes. Foreground bash returns inline if the command finishes within ~5s; otherwise it's automatically promoted to background and a completion reminder is delivered when the task actually finishes."
53933
- })),
53934
- workdir: Type2.Optional(Type2.String({
54076
+ timeout: optionalInt(1, Number.MAX_SAFE_INTEGER),
54077
+ workdir: Type3.Optional(Type3.String({
53935
54078
  description: "Working directory for command execution. Relative paths resolve against the project root. Defaults to the current session's working directory."
53936
54079
  })),
53937
- description: Type2.Optional(Type2.String({
54080
+ description: Type3.Optional(Type3.String({
53938
54081
  description: "Human-readable description shown in UI logs. Helps users understand what the command does without reading shell syntax."
53939
54082
  })),
53940
- background: Type2.Optional(Type2.Boolean({
54083
+ background: Type3.Optional(Type3.Boolean({
53941
54084
  description: "Spawn command in background and return immediately with a task_id. Use bash_status to poll completion and bash_kill to terminate. Ideal for long-running tasks like builds or dev servers."
53942
54085
  })),
53943
- compressed: Type2.Optional(Type2.Boolean({
54086
+ compressed: Type3.Optional(Type3.Boolean({
53944
54087
  description: "Compress output by removing ANSI codes, carriage returns, and excessive blank lines. Default: true. Set to false for raw terminal output including color codes."
53945
54088
  })),
53946
- pty: Type2.Optional(Type2.Boolean({
53947
- description: 'Spawn the command in a real PTY for interactive programs. Requires background: true. Inspect with bash_status({ task_id, output_mode: "screen" }) and send input with bash_write.'
54089
+ pty: Type3.Optional(Type3.Boolean({
54090
+ description: 'Spawn the command in a real PTY for interactive programs. Implies background: true automatically. Inspect with bash_status({ task_id, output_mode: "screen" }) and send input with bash_write.'
53948
54091
  })),
53949
- ptyRows: Type2.Optional(Type2.Integer({
53950
- minimum: 1,
53951
- maximum: 60,
53952
- description: "PTY terminal height in rows. Applies only when pty: true; defaults to 24. Minimum 1, maximum 60."
53953
- })),
53954
- ptyCols: Type2.Optional(Type2.Integer({
53955
- minimum: 1,
53956
- maximum: 140,
53957
- description: "PTY terminal width in columns. Applies only when pty: true; defaults to 80. Minimum 1, maximum 140."
53958
- }))
54092
+ ptyRows: optionalInt(1, 60),
54093
+ ptyCols: optionalInt(1, 140)
53959
54094
  });
53960
- var BashTaskParams = Type2.Object({
53961
- task_id: Type2.String({
54095
+ var BashTaskParams = Type3.Object({
54096
+ task_id: Type3.String({
53962
54097
  description: "Background bash task id returned by bash({ background: true })."
53963
54098
  })
53964
54099
  });
53965
- var BashStatusParams = Type2.Object({
53966
- task_id: Type2.String({
54100
+ var BashStatusParams = Type3.Object({
54101
+ task_id: Type3.String({
53967
54102
  description: "Background bash task id returned by bash({ background: true })."
53968
54103
  }),
53969
- output_mode: Type2.Optional(Type2.Union([Type2.Literal("screen"), Type2.Literal("raw"), Type2.Literal("both")], {
54104
+ output_mode: Type3.Optional(Type3.Union([Type3.Literal("screen"), Type3.Literal("raw"), Type3.Literal("both")], {
53970
54105
  description: "PTY output rendering mode. Defaults to screen for PTY tasks and preserves existing behavior for piped tasks when omitted."
53971
54106
  }))
53972
54107
  });
53973
- var BashWatchParams = Type2.Object({
53974
- task_id: Type2.String({
54108
+ var BashWatchParams = Type3.Object({
54109
+ task_id: Type3.String({
53975
54110
  description: "Background bash task id returned by bash({ background: true })."
53976
54111
  }),
53977
- pattern: Type2.Optional(Type2.Union([Type2.String(), Type2.Object({ regex: Type2.String() })])),
53978
- background: Type2.Optional(Type2.Boolean()),
53979
- timeout_ms: Type2.Optional(Type2.Integer({ minimum: 1 })),
53980
- once: Type2.Optional(Type2.Boolean())
54112
+ pattern: Type3.Optional(Type3.Union([Type3.String(), Type3.Object({ regex: Type3.String() })])),
54113
+ background: Type3.Optional(Type3.Boolean()),
54114
+ timeout_ms: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS),
54115
+ once: Type3.Optional(Type3.Boolean())
53981
54116
  });
53982
- var BashWriteParams = Type2.Object({
53983
- task_id: Type2.String({
54117
+ var BashWriteParams = Type3.Object({
54118
+ task_id: Type3.String({
53984
54119
  description: "Background PTY task id returned by bash({ pty: true, background: true })."
53985
54120
  }),
53986
- input: Type2.Union([
53987
- Type2.String(),
53988
- Type2.Array(Type2.Union([
53989
- Type2.String(),
53990
- Type2.Object({
53991
- key: Type2.String({
54121
+ input: Type3.Union([
54122
+ Type3.String(),
54123
+ Type3.Array(Type3.Union([
54124
+ Type3.String(),
54125
+ Type3.Object({
54126
+ key: Type3.String({
53992
54127
  description: "Named control key, e.g. 'esc', 'enter', 'up', 'ctrl-c'. Case-insensitive."
53993
54128
  })
53994
54129
  })
@@ -54032,12 +54167,10 @@ function registerBashTool(pi, ctx) {
54032
54167
  parameters: BashParams,
54033
54168
  async execute(_toolCallId, params, _signal, onUpdate, extCtx) {
54034
54169
  const bridge = bridgeFor(ctx, extCtx.cwd);
54035
- if (params.pty !== true && (params.ptyRows !== undefined || params.ptyCols !== undefined)) {
54036
- throw new Error("invalid_request: ptyRows/ptyCols require pty: true");
54037
- }
54038
- if (params.pty === true && params.background !== true) {
54039
- throw new Error("PTY mode requires background: true");
54040
- }
54170
+ const timeout = coerceOptionalInt(params.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
54171
+ const ptyRows = coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
54172
+ const ptyCols = coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
54173
+ const effectiveBackground = params.background === true || params.pty === true;
54041
54174
  let spawnContext = {
54042
54175
  command: params.command,
54043
54176
  cwd: params.workdir
@@ -54052,16 +54185,16 @@ function registerBashTool(pi, ctx) {
54052
54185
  let streamed = "";
54053
54186
  const response = await callBridge(bridge, "bash", {
54054
54187
  command: spawnContext.command,
54055
- timeout: params.timeout,
54188
+ timeout,
54056
54189
  workdir: spawnContext.cwd ?? params.workdir,
54057
54190
  env: spawnContext.env,
54058
54191
  description: params.description,
54059
- background: params.background,
54060
- notify_on_completion: params.background === true,
54192
+ background: effectiveBackground,
54193
+ notify_on_completion: effectiveBackground,
54061
54194
  compressed: params.compressed,
54062
54195
  pty: params.pty,
54063
- pty_rows: params.ptyRows,
54064
- pty_cols: params.ptyCols
54196
+ pty_rows: ptyRows,
54197
+ pty_cols: ptyCols
54065
54198
  }, extCtx, {
54066
54199
  transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS,
54067
54200
  keepBridgeOnTimeout: true,
@@ -54081,13 +54214,13 @@ function registerBashTool(pi, ctx) {
54081
54214
  }
54082
54215
  const taskId = response.task_id;
54083
54216
  if (response.status === "running" && taskId) {
54084
- if (params.background === true) {
54217
+ if (effectiveBackground) {
54085
54218
  trackBgTask(resolveSessionId(extCtx), taskId);
54086
54219
  return bashResult(formatBackgroundLaunch(taskId, params.pty === true), {
54087
54220
  task_id: taskId
54088
54221
  });
54089
54222
  }
54090
- const waitTimeoutMs = params.timeout !== undefined ? Math.min(params.timeout, FOREGROUND_WAIT_WINDOW_MS) : FOREGROUND_WAIT_WINDOW_MS;
54223
+ const waitTimeoutMs = timeout !== undefined ? Math.min(timeout, FOREGROUND_WAIT_WINDOW_MS) : FOREGROUND_WAIT_WINDOW_MS;
54091
54224
  const startedAt = Date.now();
54092
54225
  while (true) {
54093
54226
  const status = await callBridge(bridge, "bash_status", { task_id: taskId }, extCtx);
@@ -54228,7 +54361,7 @@ function createBashWatchTool(ctx) {
54228
54361
  return textResult(`Watch registered: ${registered.watch_id} on task ${params.task_id}
54229
54362
  A notification will fire when the pattern matches or the task exits.`, watchDetails);
54230
54363
  }
54231
- const data = await waitForBashStatus(ctx, bridge, extCtx, params.task_id, undefined, waitFor, true, Math.min(params.timeout_ms ?? DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS, MAX_BASH_STATUS_WAIT_TIMEOUT_MS));
54364
+ const data = await waitForBashStatus(ctx, bridge, extCtx, params.task_id, undefined, waitFor, true, Math.min(coerceOptionalInt(params.timeout_ms, "timeout_ms", 1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS) ?? DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS, MAX_BASH_STATUS_WAIT_TIMEOUT_MS));
54232
54365
  const text = await formatBashStatus(extCtx, params.task_id, data, undefined);
54233
54366
  return textResult(text, data);
54234
54367
  }
@@ -54301,7 +54434,7 @@ async function bashStatusSnapshot(bridge, extCtx, taskId, outputMode, options) {
54301
54434
  async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFor, waitForExit, effectiveWaitMs) {
54302
54435
  const startedAt = Date.now();
54303
54436
  const deadline = startedAt + effectiveWaitMs;
54304
- let spillCursor = 0;
54437
+ let spillCursor = { output: 0, stderr: 0, combined: 0 };
54305
54438
  let scanText = "";
54306
54439
  let scanBaseOffset = 0;
54307
54440
  const bridgeOptions = {
@@ -54315,12 +54448,7 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
54315
54448
  try {
54316
54449
  while (true) {
54317
54450
  const data = await bashStatusSnapshot(bridge, extCtx, taskId, outputMode, bridgeOptions);
54318
- if (waitForExit && isTerminalStatus(data.status)) {
54319
- sawTerminal = true;
54320
- consumeBgCompletion(sessionId, taskId);
54321
- await markBgCompletionDelivered({ ctx, directory: extCtx.cwd, sessionID: sessionId }, taskId);
54322
- return withWaited(data, { reason: "exited", elapsed_ms: Date.now() - startedAt });
54323
- }
54451
+ const terminal = isTerminalStatus(data.status);
54324
54452
  if (waitFor) {
54325
54453
  const scan = await readNewTaskOutput(extCtx, taskId, data, spillCursor);
54326
54454
  if (scan) {
@@ -54330,6 +54458,11 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
54330
54458
  scanText += scan.text;
54331
54459
  const match = findWaitMatch(scanText, waitFor);
54332
54460
  if (match) {
54461
+ if (waitForExit && terminal) {
54462
+ sawTerminal = true;
54463
+ consumeBgCompletion(sessionId, taskId);
54464
+ await markBgCompletionDelivered({ ctx, directory: extCtx.cwd, sessionID: sessionId }, taskId);
54465
+ }
54333
54466
  return withWaited(data, {
54334
54467
  reason: "matched",
54335
54468
  elapsed_ms: Date.now() - startedAt,
@@ -54338,9 +54471,14 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
54338
54471
  });
54339
54472
  }
54340
54473
  }
54341
- if (isTerminalStatus(data.status)) {
54342
- return withWaited(data, { reason: "exited", elapsed_ms: Date.now() - startedAt });
54474
+ }
54475
+ if (terminal) {
54476
+ if (waitForExit) {
54477
+ sawTerminal = true;
54478
+ consumeBgCompletion(sessionId, taskId);
54479
+ await markBgCompletionDelivered({ ctx, directory: extCtx.cwd, sessionID: sessionId }, taskId);
54343
54480
  }
54481
+ return withWaited(data, { reason: "exited", elapsed_ms: Date.now() - startedAt });
54344
54482
  }
54345
54483
  if (Date.now() >= deadline) {
54346
54484
  return withWaited(data, { reason: "timeout", elapsed_ms: Date.now() - startedAt });
@@ -54350,21 +54488,45 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
54350
54488
  } finally {
54351
54489
  if (waitForExit && !sawTerminal)
54352
54490
  unmarkTaskWaiting(sessionId, taskId);
54491
+ if (waitFor) {
54492
+ await disposePtyTerminal(watchPtyCacheKey(extCtx, taskId));
54493
+ }
54353
54494
  }
54354
54495
  }
54355
54496
  async function readNewTaskOutput(extCtx, taskId, data, cursor) {
54356
54497
  const outputPath = data.output_path;
54357
- if (!outputPath)
54358
- return;
54359
54498
  if (data.mode === "pty") {
54499
+ if (!outputPath)
54500
+ return;
54360
54501
  const { rows, cols } = ptyDimensions(data);
54361
- const state = await getOrCreatePtyTerminal(ptyCacheKey(extCtx, taskId), outputPath, rows, cols);
54502
+ const state = await getOrCreatePtyTerminal(watchPtyCacheKey(extCtx, taskId), outputPath, rows, cols);
54362
54503
  const baseOffset = state.offset;
54363
- const bytes2 = await readPtyBytes(state);
54364
- return { text: bytes2.toString("utf8"), baseOffset, nextCursor: state.offset };
54504
+ const bytes = await readPtyBytes(state);
54505
+ if (bytes.length === 0)
54506
+ return;
54507
+ return {
54508
+ text: bytes.toString("utf8"),
54509
+ baseOffset,
54510
+ nextCursor: { output: state.offset, stderr: 0, combined: state.offset }
54511
+ };
54365
54512
  }
54366
- const bytes = await readFileBytesFrom(outputPath, cursor);
54367
- return { text: bytes.toString("utf8"), baseOffset: cursor, nextCursor: cursor + bytes.length };
54513
+ const stderrPath = data.stderr_path;
54514
+ if (!outputPath && !stderrPath)
54515
+ return;
54516
+ const stdoutBytes = outputPath ? await readFileBytesFrom(outputPath, cursor.output) : Buffer.alloc(0);
54517
+ const stderrBytes = stderrPath ? await readFileBytesFrom(stderrPath, cursor.stderr) : Buffer.alloc(0);
54518
+ const bytesRead = stdoutBytes.length + stderrBytes.length;
54519
+ if (bytesRead === 0)
54520
+ return;
54521
+ return {
54522
+ text: Buffer.concat([stdoutBytes, stderrBytes]).toString("utf8"),
54523
+ baseOffset: cursor.combined,
54524
+ nextCursor: {
54525
+ output: cursor.output + stdoutBytes.length,
54526
+ stderr: cursor.stderr + stderrBytes.length,
54527
+ combined: cursor.combined + bytesRead
54528
+ }
54529
+ };
54368
54530
  }
54369
54531
  async function readFileBytesFrom(outputPath, cursor) {
54370
54532
  const handle = await fs3.open(outputPath, "r");
@@ -54476,6 +54638,9 @@ function ptyDimensions(data) {
54476
54638
  function ptyCacheKey(extCtx, taskId) {
54477
54639
  return `${extCtx.cwd}::${resolveSessionId(extCtx) ?? "__default__"}::${taskId}`;
54478
54640
  }
54641
+ function watchPtyCacheKey(extCtx, taskId) {
54642
+ return `${ptyCacheKey(extCtx, taskId)}::watch`;
54643
+ }
54479
54644
  function isTerminalStatus(status) {
54480
54645
  return status === "completed" || status === "failed" || status === "killed" || status === "timed_out";
54481
54646
  }
@@ -54546,8 +54711,8 @@ function shortenCommand(command) {
54546
54711
  }
54547
54712
 
54548
54713
  // src/tools/conflicts.ts
54549
- import { Type as Type3 } from "typebox";
54550
- var ConflictsParams = Type3.Object({});
54714
+ import { Type as Type4 } from "typebox";
54715
+ var ConflictsParams = Type4.Object({});
54551
54716
  function renderConflictCall(theme, context) {
54552
54717
  return renderToolCall("conflicts", undefined, theme, context);
54553
54718
  }
@@ -54595,19 +54760,19 @@ function registerConflictsTool(pi, ctx) {
54595
54760
  }
54596
54761
 
54597
54762
  // src/tools/fs.ts
54598
- import { Type as Type4 } from "typebox";
54599
- var DeleteParams = Type4.Object({
54600
- files: Type4.Array(Type4.String(), {
54763
+ import { Type as Type5 } from "typebox";
54764
+ var DeleteParams = Type5.Object({
54765
+ files: Type5.Array(Type5.String(), {
54601
54766
  description: "Paths to delete (one or more). May include directories when recursive=true.",
54602
54767
  minItems: 1
54603
54768
  }),
54604
- recursive: Type4.Optional(Type4.Boolean({
54769
+ recursive: Type5.Optional(Type5.Boolean({
54605
54770
  description: "Required to delete a directory and its contents. Defaults to false; passing a directory without this returns an error."
54606
54771
  }))
54607
54772
  });
54608
- var MoveParams = Type4.Object({
54609
- filePath: Type4.String({ description: "Source file path to move" }),
54610
- destination: Type4.String({ description: "Destination file path" })
54773
+ var MoveParams = Type5.Object({
54774
+ filePath: Type5.String({ description: "Source file path to move" }),
54775
+ destination: Type5.String({ description: "Destination file path" })
54611
54776
  });
54612
54777
  function renderFsCall(toolName, args, theme, context) {
54613
54778
  if (toolName === "aft_delete") {
@@ -54715,7 +54880,7 @@ import {
54715
54880
  renderDiff as renderDiff2
54716
54881
  } from "@earendil-works/pi-coding-agent";
54717
54882
  import { Container as Container3, Spacer as Spacer3, Text as Text3 } from "@earendil-works/pi-tui";
54718
- import { Type as Type5 } from "typebox";
54883
+ import { Type as Type6 } from "typebox";
54719
54884
 
54720
54885
  // src/tools/diff-format.ts
54721
54886
  import { diffLines } from "diff";
@@ -54835,33 +55000,33 @@ async function assertExternalDirectoryPermission(extCtx, target, action = "modif
54835
55000
  return;
54836
55001
  throw new Error("Permission denied: external directory access was cancelled.");
54837
55002
  }
54838
- var ReadParams = Type5.Object({
54839
- path: Type5.String({ description: "Path to the file to read (relative or absolute)" }),
54840
- offset: Type5.Optional(Type5.Number({ description: "Line number to start reading from (1-indexed)" })),
54841
- limit: Type5.Optional(Type5.Number({ description: "Maximum number of lines to read" }))
55003
+ var ReadParams = Type6.Object({
55004
+ path: Type6.String({ description: "Path to the file to read (relative or absolute)" }),
55005
+ offset: optionalInt(1, Number.MAX_SAFE_INTEGER),
55006
+ limit: optionalInt(1, Number.MAX_SAFE_INTEGER)
54842
55007
  });
54843
- var WriteParams = Type5.Object({
54844
- filePath: Type5.String({
55008
+ var WriteParams = Type6.Object({
55009
+ filePath: Type6.String({
54845
55010
  description: "Path to the file to write (absolute or project-relative)"
54846
55011
  }),
54847
- content: Type5.String({ description: "Full file contents to write" })
54848
- });
54849
- var EditParams = Type5.Object({
54850
- filePath: Type5.String({ description: "Path to the file to edit" }),
54851
- oldString: Type5.Optional(Type5.String({ description: "Text to find (exact match, fuzzy fallback)" })),
54852
- newString: Type5.Optional(Type5.String({ description: "Replacement text (omit to delete match)" })),
54853
- replaceAll: Type5.Optional(Type5.Boolean({ description: "Replace every occurrence" })),
54854
- occurrence: Type5.Optional(Type5.Number({ description: "0-indexed occurrence when multiple matches exist" })),
54855
- appendContent: Type5.Optional(Type5.String({
55012
+ content: Type6.String({ description: "Full file contents to write" })
55013
+ });
55014
+ var EditParams = Type6.Object({
55015
+ filePath: Type6.String({ description: "Path to the file to edit" }),
55016
+ oldString: Type6.Optional(Type6.String({ description: "Text to find (exact match, fuzzy fallback)" })),
55017
+ newString: Type6.Optional(Type6.String({ description: "Replacement text (omit to delete match)" })),
55018
+ replaceAll: Type6.Optional(Type6.Boolean({ description: "Replace every occurrence" })),
55019
+ occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER),
55020
+ appendContent: Type6.Optional(Type6.String({
54856
55021
  description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created). When set, oldString/newString are ignored."
54857
55022
  }))
54858
55023
  });
54859
- var GrepParams = Type5.Object({
54860
- pattern: Type5.String({ description: "Regex pattern to search for" }),
54861
- path: Type5.Optional(Type5.String({ description: "Path scope (file or directory)" })),
54862
- include: Type5.Optional(Type5.String({ description: "Glob filter for included files (e.g. '*.ts,*.tsx')" })),
54863
- caseSensitive: Type5.Optional(Type5.Boolean({ description: "Case-sensitive matching" })),
54864
- contextLines: Type5.Optional(Type5.Number({ description: "Lines of context before/after each match" }))
55024
+ var GrepParams = Type6.Object({
55025
+ pattern: Type6.String({ description: "Regex pattern to search for" }),
55026
+ path: Type6.Optional(Type6.String({ description: "Path scope (file or directory)" })),
55027
+ include: Type6.Optional(Type6.String({ description: "Glob filter for included files (e.g. '*.ts,*.tsx')" })),
55028
+ caseSensitive: Type6.Optional(Type6.Boolean({ description: "Case-sensitive matching" })),
55029
+ contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER)
54865
55030
  });
54866
55031
  function registerHoistedTools(pi, ctx, surface) {
54867
55032
  if (surface.hoistRead) {
@@ -54874,14 +55039,16 @@ function registerHoistedTools(pi, ctx, surface) {
54874
55039
  parameters: ReadParams,
54875
55040
  async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
54876
55041
  const bridge = bridgeFor(ctx, extCtx.cwd);
55042
+ const offset = coerceOptionalInt(params.offset, "offset", 1, Number.MAX_SAFE_INTEGER);
55043
+ const limit = coerceOptionalInt(params.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
54877
55044
  const req = { file: params.path };
54878
- if (params.offset !== undefined) {
54879
- req.start_line = params.offset;
54880
- if (params.limit !== undefined) {
54881
- req.end_line = params.offset + params.limit - 1;
55045
+ if (offset !== undefined) {
55046
+ req.start_line = offset;
55047
+ if (limit !== undefined) {
55048
+ req.end_line = offset + limit - 1;
54882
55049
  }
54883
- } else if (params.limit !== undefined) {
54884
- req.end_line = params.limit;
55050
+ } else if (limit !== undefined) {
55051
+ req.end_line = limit;
54885
55052
  }
54886
55053
  const response = await callBridge(bridge, "read", req, extCtx);
54887
55054
  if (Array.isArray(response.entries)) {
@@ -54889,7 +55056,7 @@ function registerHoistedTools(pi, ctx, surface) {
54889
55056
  `));
54890
55057
  }
54891
55058
  let text = response.content ?? "";
54892
- const agentSpecifiedRange = params.offset !== undefined || params.limit !== undefined;
55059
+ const agentSpecifiedRange = offset !== undefined || limit !== undefined;
54893
55060
  const footer = formatReadFooter(agentSpecifiedRange, response);
54894
55061
  if (footer)
54895
55062
  text += footer;
@@ -54959,8 +55126,9 @@ function registerHoistedTools(pi, ctx, surface) {
54959
55126
  };
54960
55127
  if (params.replaceAll === true)
54961
55128
  req.replace_all = true;
54962
- if (params.occurrence !== undefined)
54963
- req.occurrence = params.occurrence;
55129
+ const occurrence = coerceOptionalInt(params.occurrence, "occurrence", 0, Number.MAX_SAFE_INTEGER);
55130
+ if (occurrence !== undefined)
55131
+ req.occurrence = occurrence;
54964
55132
  const response = await callBridge(bridge, "edit_match", req, extCtx);
54965
55133
  return buildMutationResult(params.filePath, response);
54966
55134
  },
@@ -54991,8 +55159,9 @@ function registerHoistedTools(pi, ctx, surface) {
54991
55159
  req.include = splitIncludeGlobs(params.include);
54992
55160
  if (params.caseSensitive !== undefined)
54993
55161
  req.case_sensitive = params.caseSensitive;
54994
- if (params.contextLines !== undefined)
54995
- req.context_lines = params.contextLines;
55162
+ const contextLines = coerceOptionalInt(params.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
55163
+ if (contextLines !== undefined)
55164
+ req.context_lines = contextLines;
54996
55165
  const response = await callBridge(bridge, "grep", req, extCtx);
54997
55166
  const text = response.text ?? "";
54998
55167
  return textResult(text);
@@ -55211,16 +55380,16 @@ function formatReadFooter(agentSpecifiedRange, data) {
55211
55380
 
55212
55381
  // src/tools/imports.ts
55213
55382
  import { StringEnum as StringEnum2 } from "@earendil-works/pi-ai";
55214
- import { Type as Type6 } from "typebox";
55215
- var ImportParams = Type6.Object({
55383
+ import { Type as Type7 } from "typebox";
55384
+ var ImportParams = Type7.Object({
55216
55385
  op: StringEnum2(["add", "remove", "organize"], { description: "Import operation" }),
55217
- filePath: Type6.String({ description: "Path to the file" }),
55218
- module: Type6.Optional(Type6.String({ description: "Module path (required for add/remove), e.g. 'react', './utils'" })),
55219
- names: Type6.Optional(Type6.Array(Type6.String(), { description: "Named imports to add, e.g. ['useState']" })),
55220
- defaultImport: Type6.Optional(Type6.String({ description: "Default import name (e.g. 'React')" })),
55221
- removeName: Type6.Optional(Type6.String({ description: "Named import to remove; omit to remove entire import" })),
55222
- typeOnly: Type6.Optional(Type6.Boolean({ description: "Type-only import (TS only)" })),
55223
- validate: Type6.Optional(StringEnum2(["syntax", "full"], {
55386
+ filePath: Type7.String({ description: "Path to the file" }),
55387
+ module: Type7.Optional(Type7.String({ description: "Module path (required for add/remove), e.g. 'react', './utils'" })),
55388
+ names: Type7.Optional(Type7.Array(Type7.String(), { description: "Named imports to add, e.g. ['useState']" })),
55389
+ defaultImport: Type7.Optional(Type7.String({ description: "Default import name (e.g. 'React')" })),
55390
+ removeName: Type7.Optional(Type7.String({ description: "Named import to remove; omit to remove entire import" })),
55391
+ typeOnly: Type7.Optional(Type7.Boolean({ description: "Type-only import (TS only)" })),
55392
+ validate: Type7.Optional(StringEnum2(["syntax", "full"], {
55224
55393
  description: "Post-edit validation level (default: syntax)"
55225
55394
  }))
55226
55395
  });
@@ -55309,16 +55478,16 @@ function registerImportTools(pi, ctx) {
55309
55478
 
55310
55479
  // src/tools/lsp.ts
55311
55480
  import { StringEnum as StringEnum3 } from "@earendil-works/pi-ai";
55312
- import { Type as Type7 } from "typebox";
55313
- var LspDiagnosticsParams = Type7.Object({
55314
- filePath: Type7.Optional(Type7.String({ description: "File to get diagnostics for (mutually exclusive with directory)" })),
55315
- directory: Type7.Optional(Type7.String({
55481
+ import { Type as Type8 } from "typebox";
55482
+ var LspDiagnosticsParams = Type8.Object({
55483
+ filePath: Type8.Optional(Type8.String({ description: "File to get diagnostics for (mutually exclusive with directory)" })),
55484
+ directory: Type8.Optional(Type8.String({
55316
55485
  description: "Directory to get diagnostics for (mutually exclusive with filePath)"
55317
55486
  })),
55318
- severity: Type7.Optional(StringEnum3(["error", "warning", "information", "hint", "all"], {
55487
+ severity: Type8.Optional(StringEnum3(["error", "warning", "information", "hint", "all"], {
55319
55488
  description: "Filter by severity (default: all)"
55320
55489
  })),
55321
- waitMs: Type7.Optional(Type7.Number({
55490
+ waitMs: Type8.Optional(Type8.Number({
55322
55491
  description: "Wait N ms for fresh diagnostics (max 10000, default: 0)"
55323
55492
  }))
55324
55493
  });
@@ -55403,16 +55572,16 @@ function registerLspTools(pi, ctx) {
55403
55572
 
55404
55573
  // src/tools/navigate.ts
55405
55574
  import { StringEnum as StringEnum4 } from "@earendil-works/pi-ai";
55406
- import { Type as Type8 } from "typebox";
55575
+ import { Type as Type9 } from "typebox";
55407
55576
  function navigateParamsSchema() {
55408
- return Type8.Object({
55577
+ return Type9.Object({
55409
55578
  op: StringEnum4(["call_tree", "callers", "trace_to", "impact", "trace_data"], {
55410
55579
  description: "Navigation operation"
55411
55580
  }),
55412
- filePath: Type8.String({ description: "Source file containing the symbol" }),
55413
- symbol: Type8.String({ description: "Name of the symbol to analyze" }),
55414
- depth: Type8.Optional(Type8.Number({ description: "Max traversal depth" })),
55415
- expression: Type8.Optional(Type8.String({ description: "Expression to track (required for trace_data)" }))
55581
+ filePath: Type9.String({ description: "Source file containing the symbol" }),
55582
+ symbol: Type9.String({ description: "Name of the symbol to analyze" }),
55583
+ depth: optionalInt(1, Number.MAX_SAFE_INTEGER),
55584
+ expression: Type9.Optional(Type9.String({ description: "Expression to track (required for trace_data)" }))
55416
55585
  });
55417
55586
  }
55418
55587
  function treeLine(depth, text) {
@@ -55427,6 +55596,14 @@ function renderCallTreeNode(node, depth, lines) {
55427
55596
  renderCallTreeNode(child, depth + 1, lines);
55428
55597
  });
55429
55598
  }
55599
+ function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
55600
+ const limited = asBoolean(response[depthField]);
55601
+ const truncated = asNumber(response[truncatedField]) ?? 0;
55602
+ if (!limited && truncated === 0)
55603
+ return "";
55604
+ const detail = truncated > 0 ? `, ${truncated} truncated` : "";
55605
+ return theme.fg("warning", `(depth limited${detail})`);
55606
+ }
55430
55607
  function renderTracePath(path2, index, lines) {
55431
55608
  lines.push(`Path ${index + 1}`);
55432
55609
  asRecords(path2.hops).forEach((hop, hopIndex) => {
@@ -55444,12 +55621,16 @@ function buildNavigateSections(args, payload, theme) {
55444
55621
  if (args.op === "call_tree") {
55445
55622
  const lines = [];
55446
55623
  renderCallTreeNode(response, 0, lines);
55624
+ const warning = depthWarning(response, theme);
55625
+ if (warning)
55626
+ lines.push(warning);
55447
55627
  return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
55448
55628
  }
55449
55629
  if (args.op === "callers") {
55450
55630
  const groups = asRecords(response.callers);
55631
+ const warning = depthWarning(response, theme);
55451
55632
  const sections2 = [
55452
- `${theme.fg("success", `${asNumber(response.total_callers) ?? 0} caller${(asNumber(response.total_callers) ?? 0) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`)}`
55633
+ `${theme.fg("success", `${asNumber(response.total_callers) ?? 0} caller${(asNumber(response.total_callers) ?? 0) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`)} ${warning}`.trim()
55453
55634
  ];
55454
55635
  groups.forEach((group) => {
55455
55636
  const file2 = shortenPath(asString(group.file) ?? "(unknown file)");
@@ -55464,8 +55645,9 @@ function buildNavigateSections(args, payload, theme) {
55464
55645
  }
55465
55646
  if (args.op === "trace_to") {
55466
55647
  const paths = asRecords(response.paths);
55648
+ const warning = depthWarning(response, theme, "max_depth_reached", "truncated_paths");
55467
55649
  const sections2 = [
55468
- `${theme.fg("success", `${asNumber(response.total_paths) ?? paths.length} path${(asNumber(response.total_paths) ?? paths.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.entry_points_found) ?? 0} entry point${(asNumber(response.entry_points_found) ?? 0) === 1 ? "" : "s"}`)}`
55650
+ `${theme.fg("success", `${asNumber(response.total_paths) ?? paths.length} path${(asNumber(response.total_paths) ?? paths.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.entry_points_found) ?? 0} entry point${(asNumber(response.entry_points_found) ?? 0) === 1 ? "" : "s"}`)} ${warning}`.trim()
55469
55651
  ];
55470
55652
  if (paths.length === 0)
55471
55653
  sections2.push(theme.fg("muted", "No entry paths found."));
@@ -55479,8 +55661,9 @@ function buildNavigateSections(args, payload, theme) {
55479
55661
  }
55480
55662
  if (args.op === "impact") {
55481
55663
  const callers = asRecords(response.callers);
55664
+ const warning = depthWarning(response, theme);
55482
55665
  const sections2 = [
55483
- `${theme.fg("warning", `${asNumber(response.total_affected) ?? callers.length} affected call site${(asNumber(response.total_affected) ?? callers.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.affected_files) ?? 0} file${(asNumber(response.affected_files) ?? 0) === 1 ? "" : "s"}`)}`
55666
+ `${theme.fg("warning", `${asNumber(response.total_affected) ?? callers.length} affected call site${(asNumber(response.total_affected) ?? callers.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.affected_files) ?? 0} file${(asNumber(response.affected_files) ?? 0) === 1 ? "" : "s"}`)} ${warning}`.trim()
55484
55667
  ];
55485
55668
  if (callers.length === 0)
55486
55669
  sections2.push(theme.fg("muted", "No impacted callers found."));
@@ -55546,8 +55729,9 @@ function registerNavigateTool(pi, ctx) {
55546
55729
  file: params.filePath,
55547
55730
  symbol: params.symbol
55548
55731
  };
55549
- if (params.depth !== undefined)
55550
- req.depth = params.depth;
55732
+ const depth = coerceOptionalInt(params.depth, "depth", 1, Number.MAX_SAFE_INTEGER);
55733
+ if (depth !== undefined)
55734
+ req.depth = depth;
55551
55735
  if (params.expression !== undefined)
55552
55736
  req.expression = params.expression;
55553
55737
  const response = await callBridge(bridge, params.op, req, extCtx);
@@ -55565,20 +55749,20 @@ function registerNavigateTool(pi, ctx) {
55565
55749
  // src/tools/reading.ts
55566
55750
  import { stat as stat2 } from "node:fs/promises";
55567
55751
  import { resolve as resolve4 } from "node:path";
55568
- import { Type as Type9 } from "typebox";
55569
- var OutlineParams = Type9.Object({
55570
- target: Type9.Union([Type9.String(), Type9.Array(Type9.String())], {
55752
+ import { Type as Type10 } from "typebox";
55753
+ var OutlineParams = Type10.Object({
55754
+ target: Type10.Union([Type10.String(), Type10.Array(Type10.String())], {
55571
55755
  description: "What to outline: a file path, directory path, URL (http:// or https://), or array of file paths. The mode is auto-detected: URLs by `http://`/`https://` prefix, directories by stat, arrays as multi-file. Directory walks cap at 200 files."
55572
55756
  })
55573
55757
  });
55574
- var ZoomParams = Type9.Object({
55575
- filePath: Type9.Optional(Type9.String({ description: "Path to file (absolute or project-relative)" })),
55576
- url: Type9.Optional(Type9.String({
55758
+ var ZoomParams = Type10.Object({
55759
+ filePath: Type10.Optional(Type10.String({ description: "Path to file (absolute or project-relative)" })),
55760
+ url: Type10.Optional(Type10.String({
55577
55761
  description: "HTTP/HTTPS URL of an HTML or Markdown document to fetch and zoom into"
55578
55762
  })),
55579
- symbol: Type9.Optional(Type9.String({ description: "Symbol name (function/class/type) or Markdown heading" })),
55580
- symbols: Type9.Optional(Type9.Array(Type9.String(), { description: "Multiple symbols — returns array of matches" })),
55581
- contextLines: Type9.Optional(Type9.Number({ description: "Lines of context before/after (default: 3)" }))
55763
+ symbol: Type10.Optional(Type10.String({ description: "Symbol name (function/class/type) or Markdown heading" })),
55764
+ symbols: Type10.Optional(Type10.Array(Type10.String(), { description: "Multiple symbols — returns array of matches" })),
55765
+ contextLines: Type10.Optional(Type10.Number({ description: "Lines of context before/after (default: 3)" }))
55582
55766
  });
55583
55767
  function isUrl(s) {
55584
55768
  return s.startsWith("http://") || s.startsWith("https://");
@@ -55826,17 +56010,17 @@ ${lines}`;
55826
56010
 
55827
56011
  // src/tools/refactor.ts
55828
56012
  import { StringEnum as StringEnum5 } from "@earendil-works/pi-ai";
55829
- import { Type as Type10 } from "typebox";
55830
- var RefactorParams = Type10.Object({
56013
+ import { Type as Type11 } from "typebox";
56014
+ var RefactorParams = Type11.Object({
55831
56015
  op: StringEnum5(["move", "extract", "inline"], { description: "Refactoring operation" }),
55832
- filePath: Type10.String({ description: "Source file" }),
55833
- symbol: Type10.Optional(Type10.String({ description: "Symbol name (for move, inline)" })),
55834
- destination: Type10.Optional(Type10.String({ description: "Target file (for move)" })),
55835
- scope: Type10.Optional(Type10.String({ description: "Disambiguation scope for move op" })),
55836
- name: Type10.Optional(Type10.String({ description: "New function name (for extract)" })),
55837
- startLine: Type10.Optional(Type10.Number({ description: "1-based start line (for extract)" })),
55838
- endLine: Type10.Optional(Type10.Number({ description: "1-based end line, inclusive (for extract)" })),
55839
- callSiteLine: Type10.Optional(Type10.Number({ description: "1-based call site line (for inline)" }))
56016
+ filePath: Type11.String({ description: "Source file" }),
56017
+ symbol: Type11.Optional(Type11.String({ description: "Symbol name (for move, inline)" })),
56018
+ destination: Type11.Optional(Type11.String({ description: "Target file (for move)" })),
56019
+ scope: Type11.Optional(Type11.String({ description: "Disambiguation scope for move op" })),
56020
+ name: Type11.Optional(Type11.String({ description: "New function name (for extract)" })),
56021
+ startLine: optionalInt(1, Number.MAX_SAFE_INTEGER),
56022
+ endLine: optionalInt(1, Number.MAX_SAFE_INTEGER),
56023
+ callSiteLine: optionalInt(1, Number.MAX_SAFE_INTEGER)
55840
56024
  });
55841
56025
  function buildRefactorSections(args, payload, theme) {
55842
56026
  const response = asRecord2(payload);
@@ -55902,13 +56086,16 @@ function registerRefactorTool(pi, ctx) {
55902
56086
  req.scope = params.scope;
55903
56087
  if (params.name !== undefined)
55904
56088
  req.name = params.name;
55905
- if (params.startLine !== undefined)
55906
- req.start_line = params.startLine;
55907
- if (params.endLine !== undefined) {
55908
- req.end_line = params.op === "extract" ? params.endLine + 1 : params.endLine;
55909
- }
55910
- if (params.callSiteLine !== undefined)
55911
- req.call_site_line = params.callSiteLine;
56089
+ const startLine = coerceOptionalInt(params.startLine, "startLine", 1, Number.MAX_SAFE_INTEGER);
56090
+ const endLine = coerceOptionalInt(params.endLine, "endLine", 1, Number.MAX_SAFE_INTEGER);
56091
+ const callSiteLine = coerceOptionalInt(params.callSiteLine, "callSiteLine", 1, Number.MAX_SAFE_INTEGER);
56092
+ if (startLine !== undefined)
56093
+ req.start_line = startLine;
56094
+ if (endLine !== undefined) {
56095
+ req.end_line = params.op === "extract" ? endLine + 1 : endLine;
56096
+ }
56097
+ if (callSiteLine !== undefined)
56098
+ req.call_site_line = callSiteLine;
55912
56099
  const response = await callBridge(bridge, commandMap[params.op], req, extCtx);
55913
56100
  return textResult(JSON.stringify(response, null, 2));
55914
56101
  },
@@ -55923,14 +56110,14 @@ function registerRefactorTool(pi, ctx) {
55923
56110
 
55924
56111
  // src/tools/safety.ts
55925
56112
  import { StringEnum as StringEnum6 } from "@earendil-works/pi-ai";
55926
- import { Type as Type11 } from "typebox";
55927
- var SafetyParams = Type11.Object({
56113
+ import { Type as Type12 } from "typebox";
56114
+ var SafetyParams = Type12.Object({
55928
56115
  op: StringEnum6(["undo", "history", "checkpoint", "restore", "list"], {
55929
56116
  description: "Safety operation"
55930
56117
  }),
55931
- filePath: Type11.Optional(Type11.String({ description: "File path (required for history, optional for undo)" })),
55932
- name: Type11.Optional(Type11.String({ description: "Checkpoint name (required for checkpoint, restore)" })),
55933
- files: Type11.Optional(Type11.Array(Type11.String(), {
56118
+ filePath: Type12.Optional(Type12.String({ description: "File path (required for history, optional for undo)" })),
56119
+ name: Type12.Optional(Type12.String({ description: "Checkpoint name (required for checkpoint, restore)" })),
56120
+ files: Type12.Optional(Type12.Array(Type12.String(), {
55934
56121
  description: "Specific files for checkpoint (optional, defaults to all tracked)"
55935
56122
  }))
55936
56123
  });
@@ -56061,12 +56248,12 @@ function registerSafetyTool(pi, ctx) {
56061
56248
  }
56062
56249
 
56063
56250
  // src/tools/semantic.ts
56064
- import { Type as Type12 } from "typebox";
56065
- var SearchParams2 = Type12.Object({
56066
- query: Type12.String({
56251
+ import { Type as Type13 } from "typebox";
56252
+ var SearchParams2 = Type13.Object({
56253
+ query: Type13.String({
56067
56254
  description: "Concept or capability to find, phrased as a programmer would describe the code. Examples: 'fuzzy match with whitespace tolerance', 'undo backup before edit', 'retry failed network request'."
56068
56255
  }),
56069
- topK: Type12.Optional(Type12.Number({ description: "Maximum number of results (default: 10, max: 100)" }))
56256
+ topK: Type13.Optional(Type13.Number({ description: "Maximum number of results (default: 10, max: 100)" }))
56070
56257
  });
56071
56258
  function buildSemanticSections(args, payload, theme) {
56072
56259
  const response = asRecord2(payload);
@@ -56167,23 +56354,23 @@ function registerSemanticTool(pi, ctx) {
56167
56354
 
56168
56355
  // src/tools/structure.ts
56169
56356
  import { StringEnum as StringEnum7 } from "@earendil-works/pi-ai";
56170
- import { Type as Type13 } from "typebox";
56171
- var TransformParams = Type13.Object({
56357
+ import { Type as Type14 } from "typebox";
56358
+ var TransformParams = Type14.Object({
56172
56359
  op: StringEnum7(["add_member", "add_derive", "wrap_try_catch", "add_decorator", "add_struct_tags"], { description: "Transformation operation" }),
56173
- filePath: Type13.String({ description: "Path to the source file" }),
56174
- container: Type13.Optional(Type13.String({ description: "Class/struct/impl name for add_member" })),
56175
- code: Type13.Optional(Type13.String({ description: "Member code to insert (add_member)" })),
56176
- target: Type13.Optional(Type13.String({ description: "Target symbol name" })),
56177
- derives: Type13.Optional(Type13.Array(Type13.String(), { description: "Derive macro names (add_derive)" })),
56178
- catchBody: Type13.Optional(Type13.String({ description: "Catch block body (wrap_try_catch, default: 'throw error;')" })),
56179
- decorator: Type13.Optional(Type13.String({ description: "Decorator text without @ (add_decorator)" })),
56180
- field: Type13.Optional(Type13.String({ description: "Struct field name (add_struct_tags)" })),
56181
- tag: Type13.Optional(Type13.String({ description: "Tag key (add_struct_tags)" })),
56182
- value: Type13.Optional(Type13.String({ description: "Tag value (add_struct_tags)" })),
56183
- position: Type13.Optional(Type13.String({
56360
+ filePath: Type14.String({ description: "Path to the source file" }),
56361
+ container: Type14.Optional(Type14.String({ description: "Class/struct/impl name for add_member" })),
56362
+ code: Type14.Optional(Type14.String({ description: "Member code to insert (add_member)" })),
56363
+ target: Type14.Optional(Type14.String({ description: "Target symbol name" })),
56364
+ derives: Type14.Optional(Type14.Array(Type14.String(), { description: "Derive macro names (add_derive)" })),
56365
+ catchBody: Type14.Optional(Type14.String({ description: "Catch block body (wrap_try_catch, default: 'throw error;')" })),
56366
+ decorator: Type14.Optional(Type14.String({ description: "Decorator text without @ (add_decorator)" })),
56367
+ field: Type14.Optional(Type14.String({ description: "Struct field name (add_struct_tags)" })),
56368
+ tag: Type14.Optional(Type14.String({ description: "Tag key (add_struct_tags)" })),
56369
+ value: Type14.Optional(Type14.String({ description: "Tag value (add_struct_tags)" })),
56370
+ position: Type14.Optional(Type14.String({
56184
56371
  description: "Position hint: 'first', 'last' (default), 'before:name', 'after:name' for add_member"
56185
56372
  })),
56186
- validate: Type13.Optional(StringEnum7(["syntax", "full"], {
56373
+ validate: Type14.Optional(StringEnum7(["syntax", "full"], {
56187
56374
  description: "Validation level (default: syntax)"
56188
56375
  }))
56189
56376
  });
@@ -56448,6 +56635,10 @@ function shouldPrepareOnnxRuntime(config2) {
56448
56635
  const isFastembedSemanticBackend = (config2.semantic?.backend ?? "fastembed") === "fastembed";
56449
56636
  return config2.semantic_search === true && isFastembedSemanticBackend;
56450
56637
  }
56638
+ function bridgeDirectoryFromCallback(bridge, fallback) {
56639
+ const cwd = bridge?.cwd;
56640
+ return typeof cwd === "string" && cwd.length > 0 ? cwd : fallback;
56641
+ }
56451
56642
  async function handleConfigureWarningsForSession(context) {
56452
56643
  const validWarnings = coerceConfigureWarnings(context.warnings);
56453
56644
  if (!context.sessionId) {
@@ -56664,21 +56855,32 @@ ${lines}
56664
56855
  });
56665
56856
  }, 0);
56666
56857
  },
56667
- onBashCompletion: (completion) => {
56858
+ onBashCompletion: (completion, bridge) => {
56859
+ const directory = bridgeDirectoryFromCallback(bridge, process.cwd());
56668
56860
  handlePushedBgCompletion({
56669
56861
  ctx,
56670
- directory: process.cwd(),
56862
+ directory,
56671
56863
  sessionID: completion.session_id,
56672
56864
  runtime: pi
56673
56865
  }, completion);
56674
56866
  },
56675
- onBashLongRunning: (reminder) => {
56867
+ onBashLongRunning: (reminder, bridge) => {
56868
+ const directory = bridgeDirectoryFromCallback(bridge, process.cwd());
56676
56869
  handlePushedBgLongRunning({
56677
56870
  ctx,
56678
- directory: process.cwd(),
56871
+ directory,
56679
56872
  sessionID: reminder.session_id,
56680
56873
  runtime: pi
56681
56874
  }, reminder);
56875
+ },
56876
+ onBashPatternMatch: (frame, bridge) => {
56877
+ const directory = bridgeDirectoryFromCallback(bridge, process.cwd());
56878
+ handlePushedPatternMatch({
56879
+ ctx,
56880
+ directory,
56881
+ sessionID: frame.session_id,
56882
+ runtime: pi
56883
+ }, frame);
56682
56884
  }
56683
56885
  };
56684
56886
  pool = new BridgePool(binaryPath, poolOptions, configOverrides);
@@ -56775,28 +56977,31 @@ ${lines}
56775
56977
  runtime: pi
56776
56978
  });
56777
56979
  });
56778
- pi.on("session_shutdown", async () => {
56980
+ const unregisterShutdownCleanup = registerShutdownCleanup(async () => {
56779
56981
  try {
56780
56982
  await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
56781
56983
  await disposeAllPtyTerminals();
56782
56984
  await pool.shutdown();
56783
- log2("Bridge pool shut down");
56784
56985
  } catch (err) {
56785
- warn2(`Error during bridge shutdown: ${err instanceof Error ? err.message : String(err)}`);
56986
+ warn2(`Error during process shutdown: ${err instanceof Error ? err.message : String(err)}`);
56786
56987
  }
56787
56988
  });
56788
- registerShutdownCleanup(async () => {
56989
+ pi.on("session_shutdown", async () => {
56789
56990
  try {
56790
56991
  await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
56791
56992
  await disposeAllPtyTerminals();
56792
56993
  await pool.shutdown();
56994
+ log2("Bridge pool shut down");
56793
56995
  } catch (err) {
56794
- warn2(`Error during process shutdown: ${err instanceof Error ? err.message : String(err)}`);
56996
+ warn2(`Error during bridge shutdown: ${err instanceof Error ? err.message : String(err)}`);
56997
+ } finally {
56998
+ unregisterShutdownCleanup();
56795
56999
  }
56796
57000
  });
56797
57001
  log2(`AFT extension ready (surface=${config2.tool_surface ?? "recommended"})`);
56798
57002
  }
56799
57003
  var __test__2 = {
57004
+ bridgeDirectoryFromCallback,
56800
57005
  resolveToolSurface,
56801
57006
  handleConfigureWarningsForSession,
56802
57007
  shouldPrepareOnnxRuntime,