@cortexkit/aft-opencode 0.56.1 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/bg-notifications.d.ts +1 -0
  2. package/dist/bg-notifications.d.ts.map +1 -1
  3. package/dist/config.d.ts +2 -0
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/entry/server.js +2682 -282
  6. package/dist/index.js +448 -244
  7. package/dist/lsp-github-install.d.ts.map +1 -1
  8. package/dist/permissions/v2-service.d.ts +30 -0
  9. package/dist/permissions/v2-service.d.ts.map +1 -0
  10. package/dist/permissions/v2.d.ts +121 -9
  11. package/dist/permissions/v2.d.ts.map +1 -1
  12. package/dist/shared/rpc-server.d.ts.map +1 -1
  13. package/dist/shared/status.d.ts +8 -1
  14. package/dist/shared/status.d.ts.map +1 -1
  15. package/dist/subc-tool-schemas.d.ts +11 -0
  16. package/dist/subc-tool-schemas.d.ts.map +1 -1
  17. package/dist/tool-registration.d.ts.map +1 -1
  18. package/dist/tools/definitions/v2.d.ts +6 -0
  19. package/dist/tools/definitions/v2.d.ts.map +1 -1
  20. package/dist/tools/hoisted/v2.d.ts +12 -2
  21. package/dist/tools/hoisted/v2.d.ts.map +1 -1
  22. package/dist/tools/hoisted.d.ts.map +1 -1
  23. package/dist/tools/semantic.d.ts.map +1 -1
  24. package/dist/tui/v2-status.d.ts +8 -0
  25. package/dist/tui/v2-status.d.ts.map +1 -1
  26. package/index.js +14 -0
  27. package/package.json +14 -10
  28. package/server.js +8 -0
  29. package/src/entry/server-runtime.mjs +11 -0
  30. package/src/shared/rpc-server.ts +5 -0
  31. package/src/shared/status.ts +16 -9
  32. package/src/tui/index.tsx +1 -0
  33. package/src/tui/sidebar-view.tsx +722 -0
  34. package/src/tui/sidebar.tsx +42 -560
  35. package/src/tui/v2-status.ts +8 -1
  36. package/src/tui/v2.tsx +173 -30
  37. package/src/tui-compiled/index.tsx +1 -1
  38. package/src/tui-compiled/sidebar-view.tsx +815 -0
  39. package/src/tui-compiled/sidebar.tsx +45 -693
  40. package/src/tui-compiled/v2-status.ts +8 -1
  41. package/src/tui-compiled/v2.tsx +163 -54
  42. package/tui.js +7 -0
package/dist/index.js CHANGED
@@ -44,6 +44,17 @@ var __export = (target, all) => {
44
44
  set: __exportSetter.bind(all, name)
45
45
  });
46
46
  };
47
+ var __esm = (fn, res, err) => () => {
48
+ if (fn)
49
+ try {
50
+ res = fn(fn = 0);
51
+ } catch (e) {
52
+ err = [e];
53
+ }
54
+ if (err)
55
+ throw err[0];
56
+ return res;
57
+ };
47
58
 
48
59
  // ../../node_modules/.bun/esprima@4.0.1/node_modules/esprima/dist/esprima.js
49
60
  var require_esprima = __commonJS(function(exports, module) {
@@ -8677,6 +8688,7 @@ class BinaryBridge {
8677
8688
  pending = new Map;
8678
8689
  outstandingBackgroundTaskIds = new Set;
8679
8690
  nextId = 1;
8691
+ processGeneration = 0;
8680
8692
  stdoutBuffer = "";
8681
8693
  stdoutReadOffset = 0;
8682
8694
  stderrBuffer = "";
@@ -8989,6 +9001,7 @@ class BinaryBridge {
8989
9001
  }
8990
9002
  }
8991
9003
  const id = String(this.nextId++);
9004
+ const tool = command === "tool_call" && typeof params.name === "string" ? params.name : command;
8992
9005
  let request;
8993
9006
  if (command === "cancel_request" || Object.hasOwn(params, "command") || Object.hasOwn(params, "method")) {
8994
9007
  const nested = { ...params };
@@ -9042,14 +9055,20 @@ class BinaryBridge {
9042
9055
  return;
9043
9056
  }
9044
9057
  entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
9045
- this.handleTimeout(requestSessionId);
9058
+ this.handleTimeout({
9059
+ requestId: id,
9060
+ tool: entry.tool,
9061
+ generation: entry.generation
9062
+ }, requestSessionId);
9046
9063
  }, effectiveTimeoutMs);
9047
9064
  const entry = {
9048
9065
  resolve,
9049
9066
  reject,
9050
9067
  timer,
9051
9068
  onProgress: options?.onProgress,
9052
- command
9069
+ command,
9070
+ tool,
9071
+ generation: this.processGeneration
9053
9072
  };
9054
9073
  this.pending.set(id, entry);
9055
9074
  if (!child?.stdin?.writable) {
@@ -9352,6 +9371,7 @@ class BinaryBridge {
9352
9371
  this.handleCrash();
9353
9372
  });
9354
9373
  this.process = child;
9374
+ this.processGeneration += 1;
9355
9375
  this.spawnedBinaryFingerprint = readBinaryFingerprint(this.binaryPath);
9356
9376
  this.lastBinaryFingerprintCheckAt = Date.now();
9357
9377
  this.stdoutBuffer = "";
@@ -9523,10 +9543,15 @@ class BinaryBridge {
9523
9543
  this.outstandingBackgroundTaskIds.add(taskId);
9524
9544
  }
9525
9545
  }
9526
- handleTimeout(triggeringSessionId) {
9546
+ handleTimeout(trigger, triggeringSessionId) {
9527
9547
  this.consecutiveRequestTimeouts = 0;
9528
9548
  this.spawnedBinaryFingerprint = null;
9529
- this.rejectAllPending(new BridgeTransportUnknownOutcomeError(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
9549
+ const abortedSiblings = Array.from(this.pending, ([requestId, entry]) => ({
9550
+ request_id: requestId,
9551
+ tool: entry.tool
9552
+ }));
9553
+ const attribution = `triggering_request_id=${trigger.requestId} ` + `triggering_tool=${JSON.stringify(trigger.tool)} ` + `bridge_generation=${trigger.generation} ` + `aborted_siblings=${JSON.stringify(abortedSiblings)}`;
9554
+ this.rejectAllPending(new BridgeTransportUnknownOutcomeError(`${this.errorPrefix} bridge killed during sibling timeout — request aborted; ${attribution}`));
9530
9555
  this.outstandingBackgroundTaskIds.clear();
9531
9556
  if (this.process) {
9532
9557
  this.process.kill("SIGKILL");
@@ -9536,17 +9561,11 @@ class BinaryBridge {
9536
9561
  this.configured = false;
9537
9562
  const tail = this.formatStderrTail();
9538
9563
  this.stderrTail = [];
9539
- const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${this.getLogFilePathVia()})`;
9540
- if (tail) {
9541
- if (triggeringSessionId) {
9542
- this.sessionErrorVia(triggeringSessionId, killedMsg);
9543
- } else {
9544
- this.errorVia(killedMsg);
9545
- }
9546
- } else if (triggeringSessionId) {
9547
- this.sessionWarnVia(triggeringSessionId, killedMsg);
9564
+ const killedMsg = tail ? `Bridge killed after timeout; ${attribution}.${tail}` : `Bridge killed after timeout; ${attribution} (see ${this.getLogFilePathVia()})`;
9565
+ if (triggeringSessionId) {
9566
+ this.sessionErrorVia(triggeringSessionId, killedMsg);
9548
9567
  } else {
9549
- this.warnVia(killedMsg);
9568
+ this.errorVia(killedMsg);
9550
9569
  }
9551
9570
  }
9552
9571
  handleCrash(cause) {
@@ -15535,12 +15554,36 @@ function isNpmAvailable(deps = defaultDeps()) {
15535
15554
  return resolveNpm(deps) !== null;
15536
15555
  }
15537
15556
  // ../aft-bridge/dist/onnx-runtime.js
15538
- import { execFileSync } from "node:child_process";
15557
+ import { execFileSync as execFileSync2 } from "node:child_process";
15539
15558
  import { createHash as createHash4 } from "node:crypto";
15540
- import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, readlinkSync, realpathSync as realpathSync2, rmSync as rmSync3, statSync as statSync6, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
15541
- import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute5, join as join12, relative as relative2, resolve as resolve7, win32 } from "node:path";
15559
+ import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync10, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, readlinkSync, realpathSync as realpathSync2, renameSync as renameSync5, rmSync as rmSync3, statSync as statSync6, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
15560
+ import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute5, join as join13, relative as relative2, resolve as resolve7, win32 } from "node:path";
15542
15561
  import { Readable as Readable2 } from "node:stream";
15543
15562
  import { pipeline as pipeline2 } from "node:stream/promises";
15563
+
15564
+ // ../aft-bridge/dist/tar-executable.js
15565
+ import { execFileSync } from "node:child_process";
15566
+ import { existsSync as existsSync9 } from "node:fs";
15567
+ import { join as join12 } from "node:path";
15568
+ function windowsTarExecutable(options = {}) {
15569
+ const platform = options.platform ?? process.platform;
15570
+ if (platform !== "win32")
15571
+ return "tar";
15572
+ const env = options.env ?? process.env;
15573
+ const systemTar = join12(env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
15574
+ return (options.exists ?? existsSync9)(systemTar) ? systemTar : "tar";
15575
+ }
15576
+ function execTarExtractionSync(args, timeout) {
15577
+ const executable = windowsTarExecutable();
15578
+ try {
15579
+ execFileSync(executable, args, { stdio: "pipe", timeout });
15580
+ } catch (cause) {
15581
+ const detail = cause instanceof Error ? cause.message : String(cause);
15582
+ throw new Error(`tar extraction failed using ${executable}: ${detail}`, { cause });
15583
+ }
15584
+ }
15585
+
15586
+ // ../aft-bridge/dist/onnx-runtime.js
15544
15587
  var ORT_VERSION = "1.24.4";
15545
15588
  var ORT_REPO = "microsoft/onnxruntime";
15546
15589
  var MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
@@ -15604,11 +15647,11 @@ function getManualInstallHint() {
15604
15647
  }
15605
15648
  async function ensureOnnxRuntime(storageDir) {
15606
15649
  const info = getPlatformInfo();
15607
- const ortVersionDir = join12(storageDir, "onnxruntime", ORT_VERSION);
15650
+ const ortVersionDir = join13(storageDir, "onnxruntime", ORT_VERSION);
15608
15651
  const libName = info?.libName ?? "libonnxruntime.dylib";
15609
15652
  const resolvedOrtDir = resolveCachedOnnxRuntimeDir(ortVersionDir, libName);
15610
- const libPath = join12(resolvedOrtDir, libName);
15611
- if (existsSync9(libPath)) {
15653
+ const libPath = join13(resolvedOrtDir, libName);
15654
+ if (existsSync10(libPath)) {
15612
15655
  const meta = readOnnxInstalledMeta(ortVersionDir);
15613
15656
  if (meta?.sha256) {
15614
15657
  try {
@@ -15637,9 +15680,9 @@ async function ensureOnnxRuntime(storageDir) {
15637
15680
  warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
15638
15681
  return null;
15639
15682
  }
15640
- const onnxBaseDir = join12(storageDir, "onnxruntime");
15683
+ const onnxBaseDir = join13(storageDir, "onnxruntime");
15641
15684
  mkdirSync5(onnxBaseDir, { recursive: true });
15642
- const lockPath = join12(onnxBaseDir, ONNX_LOCK_FILE);
15685
+ const lockPath = join13(onnxBaseDir, ONNX_LOCK_FILE);
15643
15686
  cleanupAbandonedStagingDirs(onnxBaseDir);
15644
15687
  if (!acquireLock(lockPath)) {
15645
15688
  warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
@@ -15658,7 +15701,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
15658
15701
  for (const entry of entries) {
15659
15702
  if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
15660
15703
  continue;
15661
- const stagingDir = join12(onnxBaseDir, entry);
15704
+ const stagingDir = join13(onnxBaseDir, entry);
15662
15705
  const parts = entry.split(".");
15663
15706
  const pidStr = parts[parts.length - 2];
15664
15707
  const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
@@ -15695,7 +15738,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
15695
15738
  }
15696
15739
  function cleanupIncompleteTargetIfUnowned(ortDir) {
15697
15740
  try {
15698
- if (existsSync9(ortDir) && !existsSync9(join12(ortDir, ONNX_INSTALLED_META_FILE))) {
15741
+ if (existsSync10(ortDir) && !existsSync10(join13(ortDir, ONNX_INSTALLED_META_FILE))) {
15699
15742
  log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
15700
15743
  rmSync3(ortDir, { recursive: true, force: true });
15701
15744
  }
@@ -15739,8 +15782,8 @@ function detectOnnxVersion(libDir, libName) {
15739
15782
  if (version)
15740
15783
  return version;
15741
15784
  }
15742
- const base = join12(libDir, libName);
15743
- if (existsSync9(base)) {
15785
+ const base = join13(libDir, libName);
15786
+ if (existsSync10(base)) {
15744
15787
  try {
15745
15788
  const real = realpathSync2(base);
15746
15789
  const version = parseOnnxVersionFromPath(real) ?? parseOnnxVersionFromDirectoryPath(real);
@@ -15806,10 +15849,10 @@ function directoryContainsLibrary(dir, libName) {
15806
15849
  }
15807
15850
  }
15808
15851
  function resolveCachedOnnxRuntimeDir(ortVersionDir, libName) {
15809
- if (existsSync9(join12(ortVersionDir, libName)))
15852
+ if (existsSync10(join13(ortVersionDir, libName)))
15810
15853
  return ortVersionDir;
15811
- const libSubdir = join12(ortVersionDir, "lib");
15812
- if (existsSync9(join12(libSubdir, libName)))
15854
+ const libSubdir = join13(ortVersionDir, "lib");
15855
+ if (existsSync10(join13(libSubdir, libName)))
15813
15856
  return libSubdir;
15814
15857
  return ortVersionDir;
15815
15858
  }
@@ -15824,13 +15867,13 @@ function findSystemOnnxRuntime(libName) {
15824
15867
  } else if (process.platform === "win32") {
15825
15868
  const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
15826
15869
  const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
15827
- searchPaths.push(join12(programFiles, "onnxruntime", "lib"), join12(programFiles, "Microsoft ONNX Runtime", "lib"), join12(programFiles, "Microsoft Machine Learning", "lib"), join12(programFilesX86, "onnxruntime", "lib"), ...(() => {
15870
+ searchPaths.push(join13(programFiles, "onnxruntime", "lib"), join13(programFiles, "Microsoft ONNX Runtime", "lib"), join13(programFiles, "Microsoft Machine Learning", "lib"), join13(programFilesX86, "onnxruntime", "lib"), ...(() => {
15828
15871
  const nugetPaths = [];
15829
15872
  const userProfile = process.env.USERPROFILE ?? "";
15830
15873
  if (!userProfile)
15831
15874
  return nugetPaths;
15832
- const nugetPackageDir = join12(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
15833
- if (!existsSync9(nugetPackageDir))
15875
+ const nugetPackageDir = join13(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
15876
+ if (!existsSync10(nugetPackageDir))
15834
15877
  return nugetPaths;
15835
15878
  try {
15836
15879
  for (const entry of readdirSync3(nugetPackageDir, { withFileTypes: true })) {
@@ -15838,7 +15881,7 @@ function findSystemOnnxRuntime(libName) {
15838
15881
  continue;
15839
15882
  if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
15840
15883
  continue;
15841
- nugetPaths.push(join12(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join12(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
15884
+ nugetPaths.push(join13(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join13(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
15842
15885
  }
15843
15886
  } catch (err) {
15844
15887
  warn(`Failed to scan NuGet ONNX Runtime cache ${nugetPackageDir}: ${err instanceof Error ? err.message : String(err)}`);
@@ -15860,11 +15903,11 @@ function findSystemOnnxRuntime(libName) {
15860
15903
  });
15861
15904
  const unknownVersionPaths = [];
15862
15905
  for (const dir of uniquePaths) {
15863
- const libPath = join12(dir, libName);
15906
+ const libPath = join13(dir, libName);
15864
15907
  if (process.platform === "win32") {
15865
15908
  if (!directoryContainsLibrary(dir, libName))
15866
15909
  continue;
15867
- } else if (!existsSync9(libPath)) {
15910
+ } else if (!existsSync10(libPath)) {
15868
15911
  continue;
15869
15912
  }
15870
15913
  const version = detectOnnxVersion(dir, libName);
@@ -15930,7 +15973,7 @@ function validateExtractedTree(stagingRoot) {
15930
15973
  const walk = (dir) => {
15931
15974
  const entries = readdirSync3(dir);
15932
15975
  for (const entry of entries) {
15933
- const fullPath = join12(dir, entry);
15976
+ const fullPath = join13(dir, entry);
15934
15977
  const lst = lstatSync(fullPath);
15935
15978
  if (lst.isSymbolicLink()) {
15936
15979
  const linkTarget = readlinkSync(fullPath);
@@ -15963,34 +16006,32 @@ async function downloadOnnxRuntime(info, targetDir) {
15963
16006
  const url = `https://github.com/${ORT_REPO}/releases/download/v${ORT_VERSION}/${info.assetName}.${info.archiveType === "tgz" ? "tgz" : "zip"}`;
15964
16007
  log(`Downloading ONNX Runtime v${ORT_VERSION} for ${process.platform}/${process.arch}...`);
15965
16008
  const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
16009
+ const extractionRoot = join13(tmpDir, "extract");
16010
+ const stagedInstallDir = join13(tmpDir, "install");
15966
16011
  try {
15967
- mkdirSync5(tmpDir, { recursive: true });
15968
- const archivePath = join12(tmpDir, `onnxruntime.${info.archiveType}`);
16012
+ mkdirSync5(extractionRoot, { recursive: true });
16013
+ const archivePath = join13(tmpDir, `onnxruntime.${info.archiveType}`);
15969
16014
  await downloadFileWithCap(url, archivePath);
15970
16015
  const archiveSha256 = sha256File(archivePath);
15971
16016
  log(`ONNX Runtime archive sha256=${archiveSha256}`);
15972
16017
  if (info.archiveType === "tgz") {
15973
- execFileSync("tar", ["xzf", archivePath, "-C", tmpDir], {
15974
- stdio: "pipe",
15975
- timeout: 120000
15976
- });
16018
+ execTarExtractionSync(["xzf", archivePath, "-C", extractionRoot], 120000);
15977
16019
  } else {
15978
- await extractZipArchive(archivePath, tmpDir);
16020
+ await extractZipArchive(archivePath, extractionRoot);
15979
16021
  }
15980
16022
  try {
15981
16023
  unlinkSync4(archivePath);
15982
16024
  } catch {}
15983
- validateExtractedTree(tmpDir);
15984
- const extractedDir = join12(tmpDir, info.assetName, "lib");
15985
- if (!existsSync9(extractedDir)) {
16025
+ validateExtractedTree(extractionRoot);
16026
+ const extractedDir = join13(extractionRoot, info.assetName, "lib");
16027
+ if (!existsSync10(extractedDir)) {
15986
16028
  throw new Error(`Expected directory not found: ${extractedDir}`);
15987
16029
  }
15988
- mkdirSync5(targetDir, { recursive: true });
15989
16030
  const libFiles = readdirSync3(extractedDir).filter((f) => f.startsWith("libonnxruntime") || f.startsWith("onnxruntime"));
15990
16031
  const realFiles = [];
15991
16032
  const symlinks = [];
15992
16033
  for (const libFile of libFiles) {
15993
- const src = join12(extractedDir, libFile);
16034
+ const src = join13(extractedDir, libFile);
15994
16035
  try {
15995
16036
  const stat = lstatSync(src);
15996
16037
  log(`ORT extract: ${libFile} — isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
@@ -16004,15 +16045,11 @@ async function downloadOnnxRuntime(info, targetDir) {
16004
16045
  realFiles.push(libFile);
16005
16046
  }
16006
16047
  }
16007
- copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
16008
- const libPath = join12(targetDir, info.libName);
16009
- let libHash = null;
16010
- try {
16011
- libHash = sha256File(libPath);
16012
- } catch (err) {
16013
- warn(`Could not hash newly-installed ONNX library at ${libPath}: ${err}`);
16014
- }
16015
- writeOnnxInstalledMeta(targetDir, ORT_VERSION, libHash, archiveSha256);
16048
+ copyOnnxLibraries(info, extractedDir, stagedInstallDir, realFiles, symlinks);
16049
+ const libPath = join13(stagedInstallDir, info.libName);
16050
+ const libHash = sha256File(libPath);
16051
+ writeOnnxInstalledMeta(stagedInstallDir, ORT_VERSION, libHash, archiveSha256);
16052
+ publishOnnxRuntime(stagedInstallDir, targetDir);
16016
16053
  rmSync3(tmpDir, { recursive: true, force: true });
16017
16054
  log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
16018
16055
  return targetDir;
@@ -16021,17 +16058,39 @@ async function downloadOnnxRuntime(info, targetDir) {
16021
16058
  try {
16022
16059
  rmSync3(tmpDir, { recursive: true, force: true });
16023
16060
  } catch {}
16024
- try {
16025
- rmSync3(targetDir, { recursive: true, force: true });
16026
- } catch {}
16027
16061
  return null;
16028
16062
  }
16029
16063
  }
16064
+ function publishOnnxRuntime(stagedInstallDir, targetDir) {
16065
+ const backupDir = `${targetDir}.backup.${process.pid}.${Date.now().toString(36)}`;
16066
+ const hadTarget = existsSync10(targetDir);
16067
+ if (hadTarget)
16068
+ renameSync5(targetDir, backupDir);
16069
+ try {
16070
+ renameSync5(stagedInstallDir, targetDir);
16071
+ } catch (installError) {
16072
+ if (hadTarget) {
16073
+ try {
16074
+ renameSync5(backupDir, targetDir);
16075
+ } catch (restoreError) {
16076
+ throw new Error(`ONNX Runtime replacement failed and the previous runtime could not be restored from ${backupDir}: ${restoreError}`, { cause: installError });
16077
+ }
16078
+ }
16079
+ throw installError;
16080
+ }
16081
+ if (hadTarget) {
16082
+ try {
16083
+ rmSync3(backupDir, { recursive: true, force: true });
16084
+ } catch (err) {
16085
+ warn(`Could not remove replaced ONNX Runtime backup at ${backupDir}: ${err}`);
16086
+ }
16087
+ }
16088
+ }
16030
16089
  function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync3) {
16031
16090
  const requiredLibs = new Set([info.libName]);
16032
16091
  for (const libFile of realFiles) {
16033
- const src = join12(extractedDir, libFile);
16034
- const dst = join12(targetDir, libFile);
16092
+ const src = join13(extractedDir, libFile);
16093
+ const dst = join13(targetDir, libFile);
16035
16094
  try {
16036
16095
  copyFile(src, dst);
16037
16096
  if (process.platform !== "win32") {
@@ -16047,11 +16106,11 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
16047
16106
  }
16048
16107
  const targetRoot = realpathSync2(targetDir);
16049
16108
  for (const link of symlinks) {
16050
- const dst = join12(targetDir, link.name);
16109
+ const dst = join13(targetDir, link.name);
16051
16110
  try {
16052
16111
  unlinkSync4(dst);
16053
16112
  } catch {}
16054
- const dstForContainment = join12(targetRoot, link.name);
16113
+ const dstForContainment = join13(targetRoot, link.name);
16055
16114
  const resolvedTarget = resolve7(dirname5(dstForContainment), link.target);
16056
16115
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
16057
16116
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
@@ -16072,21 +16131,18 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
16072
16131
  log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
16073
16132
  }
16074
16133
  }
16075
- const requiredPath = join12(targetDir, info.libName);
16076
- if (!existsSync9(requiredPath)) {
16134
+ const requiredPath = join13(targetDir, info.libName);
16135
+ if (!existsSync10(requiredPath)) {
16077
16136
  rmSync3(targetDir, { recursive: true, force: true });
16078
16137
  throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
16079
16138
  }
16080
16139
  }
16081
16140
  async function extractZipArchive(archivePath, destinationDir) {
16082
16141
  if (process.platform === "win32") {
16083
- execFileSync("tar.exe", ["-xf", archivePath, "-C", destinationDir], {
16084
- stdio: "pipe",
16085
- timeout: 120000
16086
- });
16142
+ execTarExtractionSync(["-xf", archivePath, "-C", destinationDir], 120000);
16087
16143
  return;
16088
16144
  }
16089
- execFileSync("unzip", ["-q", archivePath, "-d", destinationDir], {
16145
+ execFileSync2("unzip", ["-q", archivePath, "-d", destinationDir], {
16090
16146
  stdio: "pipe",
16091
16147
  timeout: 120000
16092
16148
  });
@@ -16099,13 +16155,13 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
16099
16155
  ...sha256 ? { sha256 } : {},
16100
16156
  archiveSha256
16101
16157
  };
16102
- writeFileSync3(join12(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
16158
+ writeFileSync3(join13(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
16103
16159
  } catch (err) {
16104
16160
  log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
16105
16161
  }
16106
16162
  }
16107
16163
  function readOnnxInstalledMeta(installDir) {
16108
- const path = join12(installDir, ONNX_INSTALLED_META_FILE);
16164
+ const path = join13(installDir, ONNX_INSTALLED_META_FILE);
16109
16165
  try {
16110
16166
  if (!statSync6(path).isFile())
16111
16167
  return null;
@@ -16217,7 +16273,7 @@ function isWindowsProcessAlive(pid) {
16217
16273
  if (!Number.isInteger(pid) || pid <= 0)
16218
16274
  return false;
16219
16275
  try {
16220
- const output = execFileSync("tasklist.exe", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
16276
+ const output = execFileSync2("tasklist.exe", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
16221
16277
  encoding: "utf8",
16222
16278
  timeout: 2000,
16223
16279
  windowsHide: true
@@ -16438,10 +16494,8 @@ function prepareCanonicalEditArguments(toolName, rawArguments) {
16438
16494
  if (unknownRootKeys.length > 0) {
16439
16495
  throw new InvalidRequestError(formatUnknownKeys(unknownRootKeys));
16440
16496
  }
16497
+ validateSymbolModePair(record);
16441
16498
  const modes = editModesPresent(record);
16442
- if (hasOrphanedSymbolContent(record)) {
16443
- throw new InvalidRequestError("edit: 'content' requires a non-empty string 'symbol' when symbol mode is selected");
16444
- }
16445
16499
  if (modes.length > 1) {
16446
16500
  throw new InvalidRequestError(`edit: conflicting modes: ${modes.join(", ")}. ${OMIT_OPTIONAL_FIELDS_STEERING}`);
16447
16501
  }
@@ -16461,7 +16515,7 @@ function prepareCanonicalEditArguments(toolName, rawArguments) {
16461
16515
  throw new InvalidRequestError("edit: 'symbol' must be a string when symbol mode is selected");
16462
16516
  }
16463
16517
  if (!hasOwn(record, "content") || typeof record.content !== "string") {
16464
- throw new InvalidRequestError("edit: symbol mode requires both 'symbol' and 'content' string properties");
16518
+ throw new InvalidRequestError("edit: incomplete symbol mode: property 'content' must be a string. " + "Retry with `symbol` + `content`, or use `edits[]`.");
16465
16519
  }
16466
16520
  } else {
16467
16521
  const item = {};
@@ -16514,6 +16568,29 @@ function validateEditPath(record) {
16514
16568
  function formatUnknownKeys(keys) {
16515
16569
  return `Unrecognized keys: ${keys.map((key) => `"${key}"`).join(", ")}`;
16516
16570
  }
16571
+ function validateSymbolModePair(record) {
16572
+ const completeShapes = "Retry with `symbol` + `content`, or use `edits[]`.";
16573
+ const hasSymbol = isNonEmptyString(record.symbol);
16574
+ const hasContent = isNonEmptyString(record.content);
16575
+ if (hasSymbol) {
16576
+ if (!hasOwn(record, "content")) {
16577
+ throw new InvalidRequestError(`edit: incomplete symbol mode: missing property 'content'. ${completeShapes}`);
16578
+ }
16579
+ if (record.content === null) {
16580
+ throw new InvalidRequestError(`edit: incomplete symbol mode: property 'content' is null. ${completeShapes}`);
16581
+ }
16582
+ return;
16583
+ }
16584
+ if (!hasContent)
16585
+ return;
16586
+ if (!hasOwn(record, "symbol")) {
16587
+ throw new InvalidRequestError(`edit: incomplete symbol mode: missing property 'symbol'. ${completeShapes}`);
16588
+ }
16589
+ if (record.symbol === null) {
16590
+ throw new InvalidRequestError(`edit: incomplete symbol mode: property 'symbol' is null. ${completeShapes}`);
16591
+ }
16592
+ throw new InvalidRequestError(`edit: incomplete symbol mode: property 'symbol' must be a non-empty string. ${completeShapes}`);
16593
+ }
16517
16594
  function editModesPresent(record) {
16518
16595
  const hasAppendContent = isNonEmptyString(record.appendContent);
16519
16596
  if (!hasAppendContent)
@@ -16599,9 +16676,6 @@ function normalizeEditArraySentinels(record) {
16599
16676
  return true;
16600
16677
  }
16601
16678
  }
16602
- function hasOrphanedSymbolContent(record) {
16603
- return isNonEmptyString(record.content) && !isNonEmptyString(record.symbol);
16604
- }
16605
16679
  function parseEditArray(value) {
16606
16680
  if (typeof value === "string") {
16607
16681
  let parsed;
@@ -16784,8 +16858,50 @@ function copyOwnProperties(source) {
16784
16858
  copy[key] = source[key];
16785
16859
  return copy;
16786
16860
  }
16861
+ // ../aft-bridge/dist/semantic-status.js
16862
+ var MISSING_ONNX_RUNTIME_MARKER = "ONNX Runtime not found.";
16863
+ var MISSING_ONNX_RUNTIME_LABEL = "unavailable — ONNX Runtime missing (npx @cortexkit/aft doctor --fix)";
16864
+ var REBUILDING_LABEL = "Rebuilding (model changed)";
16865
+ var SEMANTIC_PROGRESS_STATUSES = new Set(["building", "loading"]);
16866
+ var SEMANTIC_READY_STATUSES = new Set(["ready", "empty"]);
16867
+ var SEMANTIC_INACTIVE_STATUSES = new Set(["disabled", "busy"]);
16868
+ var SEMANTIC_FAILURE_LABELS = {
16869
+ backend_unavailable: "backend unavailable",
16870
+ degraded: "degraded",
16871
+ error: "error",
16872
+ failed: "failed",
16873
+ unavailable: "unavailable"
16874
+ };
16875
+ function semanticIndexStatusKind(status) {
16876
+ if (SEMANTIC_READY_STATUSES.has(status))
16877
+ return "ready";
16878
+ if (SEMANTIC_PROGRESS_STATUSES.has(status))
16879
+ return "progress";
16880
+ if (status in SEMANTIC_FAILURE_LABELS)
16881
+ return "failure";
16882
+ if (SEMANTIC_INACTIVE_STATUSES.has(status))
16883
+ return "inactive";
16884
+ return "unrecognized";
16885
+ }
16886
+ function mentionsMissingOnnxRuntime(...values) {
16887
+ return values.some((value) => typeof value === "string" && value.includes(MISSING_ONNX_RUNTIME_MARKER));
16888
+ }
16889
+ function semanticFailureLabel(status, stage, error) {
16890
+ if (mentionsMissingOnnxRuntime(error, stage))
16891
+ return MISSING_ONNX_RUNTIME_LABEL;
16892
+ return SEMANTIC_FAILURE_LABELS[status] ?? null;
16893
+ }
16894
+ function formatSemanticIndexStatus(status, stage, error) {
16895
+ const failure = semanticFailureLabel(status, stage, error);
16896
+ if (failure)
16897
+ return failure;
16898
+ if (semanticIndexStatusKind(status) === "progress" && stage === "fingerprint_change") {
16899
+ return REBUILDING_LABEL;
16900
+ }
16901
+ return status;
16902
+ }
16787
16903
  // src/config.ts
16788
- import { existsSync as existsSync10, readFileSync as readFileSync7, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
16904
+ import { existsSync as existsSync11, readFileSync as readFileSync7, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
16789
16905
  import { homedir as homedir10 } from "node:os";
16790
16906
  import { isAbsolute as isAbsolute6, parse as parsePath, resolve as resolvePath } from "node:path";
16791
16907
  var import_comment_json = __toESM(require_src2(), 1);
@@ -21492,6 +21608,9 @@ function write(level, message, data, sessionId) {
21492
21608
  function log2(message, data) {
21493
21609
  write("INFO", message, data);
21494
21610
  }
21611
+ function debug2(message, data) {
21612
+ write("DEBUG", message, data);
21613
+ }
21495
21614
  function warn2(message, data) {
21496
21615
  write("WARN", message, data);
21497
21616
  }
@@ -21690,6 +21809,7 @@ var InspectConfigSchema = object({
21690
21809
  enabled: boolean2().optional(),
21691
21810
  diagnostics_timeout_ms: number2().int().positive().optional().transform((value) => value === undefined ? undefined : clampInspectDiagnosticsTimeoutMs(value)),
21692
21811
  tier2_idle_minutes: number2().min(0).optional(),
21812
+ tier2_pass_timeout_ms: number2().int().positive().optional(),
21693
21813
  categories: record(string2(), boolean2()).optional(),
21694
21814
  tier2_soft_deadline_ms: number2().int().positive().optional(),
21695
21815
  max_drill_down_items: number2().int().positive().max(100).optional(),
@@ -22012,7 +22132,7 @@ function migrateExperimentalBashBlock(rawConfig, configPath, logger) {
22012
22132
  return movedKeys;
22013
22133
  }
22014
22134
  function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 }) {
22015
- if (!existsSync10(configPath)) {
22135
+ if (!existsSync11(configPath)) {
22016
22136
  return { migrated: false, oldKeys: [] };
22017
22137
  }
22018
22138
  let tmpPath = null;
@@ -22036,7 +22156,7 @@ function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 })
22036
22156
  ${serialized}` : serialized;
22037
22157
  tmpPath = `${configPath}.tmp.${process.pid}`;
22038
22158
  writeFileSync4(tmpPath, nextContent, "utf-8");
22039
- renameSync5(tmpPath, configPath);
22159
+ renameSync6(tmpPath, configPath);
22040
22160
  logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
22041
22161
  return { migrated: true, oldKeys };
22042
22162
  } catch (err) {
@@ -22097,7 +22217,7 @@ function recordConfigParseFailure(configPath, errorMessage) {
22097
22217
  }
22098
22218
  function loadConfigFromPath(configPath) {
22099
22219
  try {
22100
- if (!existsSync10(configPath)) {
22220
+ if (!existsSync11(configPath)) {
22101
22221
  return null;
22102
22222
  }
22103
22223
  const content = readFileSync7(configPath, "utf-8");
@@ -22437,7 +22557,7 @@ function loadAftConfig(projectDirectory) {
22437
22557
  }
22438
22558
 
22439
22559
  // src/tools/_shared.ts
22440
- import { existsSync as existsSync11 } from "node:fs";
22560
+ import { existsSync as existsSync12 } from "node:fs";
22441
22561
  import * as os2 from "node:os";
22442
22562
  import * as path2 from "node:path";
22443
22563
 
@@ -34850,6 +34970,7 @@ var DEBOUNCE_STEP_MS = 200;
34850
34970
  var DEBOUNCE_CAP_MS = 1000;
34851
34971
  var MAX_WAKE_SEND_ATTEMPTS = 5;
34852
34972
  var DEFAULT_WAKE_CONFIRMATION_WINDOW_MS = 5000;
34973
+ var BUSY_WAKE_DEFER_CEILING_MS = 60000;
34853
34974
  var UNKNOWN_COMPLETION_TTL_MS = 5000;
34854
34975
  var UNKNOWN_COMPLETION_CAP = 32;
34855
34976
  var DEFAULT_SESSION_ID2 = "__default__";
@@ -34881,10 +35002,12 @@ function logDroppedForeignSessionFrame(drainContext, droppedSessionId, liveSessi
34881
35002
  var DEFAULT_BG_HOP_TIMEOUT_MS = 15000;
34882
35003
  var bgHopTimeoutMs = DEFAULT_BG_HOP_TIMEOUT_MS;
34883
35004
  var wakeConfirmationWindowMs = DEFAULT_WAKE_CONFIRMATION_WINDOW_MS;
35005
+ var busyWakeDeferCeilingMs = BUSY_WAKE_DEFER_CEILING_MS;
34884
35006
  var lastWakeMessageTimestamp = 0;
34885
35007
  var wakeMessageCounter = 0;
34886
35008
  var subcNudgesInFlight = new Map;
34887
35009
  var subcNudgeLogState = new Map;
35010
+ var deferredBusyWakes = new Map;
34888
35011
  function consumeBgCompletion(sessionID, taskId) {
34889
35012
  const state = stateFor(sessionID);
34890
35013
  state.pendingCompletions = state.pendingCompletions.filter((c) => c.task_id !== taskId);
@@ -34899,6 +35022,9 @@ function consumeBgCompletion(sessionID, taskId) {
34899
35022
  }
34900
35023
  }
34901
35024
  clearWakeTimerIfNoPending(state);
35025
+ if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0 && state.pendingPatternMatches.length === 0) {
35026
+ clearDeferredBusyWake(sessionID || DEFAULT_SESSION_ID2);
35027
+ }
34902
35028
  }
34903
35029
  async function markBgCompletionDelivered(drainContext, taskId) {
34904
35030
  const state = stateFor(drainContext.sessionID);
@@ -35004,7 +35130,7 @@ async function handlePushedPatternMatch(drainContext, frame) {
35004
35130
  }
35005
35131
  const state = stateFor(drainContext.sessionID);
35006
35132
  queuePendingPatternMatch(state, { ...frame, ackCompletionOnDelivery: true });
35007
- await triggerWakeIfPending(drainContext, true);
35133
+ await triggerWakeIfPending(drainContext, true, true, false, true);
35008
35134
  }
35009
35135
  function ingestBgCompletions(sessionID, completions) {
35010
35136
  if (!Array.isArray(completions) || completions.length === 0)
@@ -35042,11 +35168,11 @@ function ingestBgCompletions(sessionID, completions) {
35042
35168
  }
35043
35169
  async function handlePushedBgCompletion(drainContext, completion) {
35044
35170
  ingestBgCompletions(drainContext.sessionID, [completion]);
35045
- await triggerWakeIfPending(drainContext, true, false);
35171
+ await triggerWakeIfPending(drainContext, true, false, false, true);
35046
35172
  }
35047
35173
  async function handlePushedBgLongRunning(drainContext, reminder) {
35048
35174
  stateFor(drainContext.sessionID).pendingLongRunning.push(reminder);
35049
- await triggerWakeIfPending(drainContext, true);
35175
+ await triggerWakeIfPending(drainContext, true, true, false, true);
35050
35176
  }
35051
35177
  async function appendInTurnBgCompletions(drainContext, output) {
35052
35178
  if (!output)
@@ -35091,6 +35217,7 @@ async function appendInTurnBgCompletions(drainContext, output) {
35091
35217
  if (inTurnAcked)
35092
35218
  confirmAcked(state, inTurnAckIds);
35093
35219
  clearWakeTimerIfNoPending(state);
35220
+ clearDeferredBusyWake(drainContext.sessionID);
35094
35221
  }
35095
35222
  async function handleIdleBgCompletions(drainContext) {
35096
35223
  stateFor(drainContext.sessionID).wakeDeferredTaskIds.clear();
@@ -35118,7 +35245,7 @@ async function handleSubcBgEventsNudgeOnce(drainContext) {
35118
35245
  const state = stateFor(drainContext.sessionID);
35119
35246
  state.wakeDeferredTaskIds.clear();
35120
35247
  rearmHardStoppedWake(state, drainContext);
35121
- await triggerWakeIfPending(drainContext, false, true, true);
35248
+ await triggerWakeIfPending(drainContext, false, true, true, true);
35122
35249
  }
35123
35250
  function logSubcNudgeLifecycle(drainContext, kind, message) {
35124
35251
  logDeliveryHop(drainContext, kind, message, {
@@ -35333,6 +35460,42 @@ function registerWakeAdmission(state, admission) {
35333
35460
  }, wakeConfirmationWindowMs);
35334
35461
  tracked.confirmationTimer.unref?.();
35335
35462
  }
35463
+ function clearDeferredBusyWake(sessionID) {
35464
+ const deferred = deferredBusyWakes.get(sessionID);
35465
+ if (!deferred)
35466
+ return;
35467
+ deferredBusyWakes.delete(sessionID);
35468
+ clearTimeout(deferred.timer);
35469
+ return deferred;
35470
+ }
35471
+ function deferBusyWake(drainContext) {
35472
+ const existing = deferredBusyWakes.get(drainContext.sessionID);
35473
+ if (existing) {
35474
+ existing.context = drainContext;
35475
+ return;
35476
+ }
35477
+ const deferredAt = Date.now();
35478
+ const deferred = {
35479
+ context: drainContext,
35480
+ deferredAt,
35481
+ timer: setTimeout(() => {
35482
+ if (deferredBusyWakes.get(drainContext.sessionID) !== deferred)
35483
+ return;
35484
+ deferredBusyWakes.delete(drainContext.sessionID);
35485
+ const waitMs = Math.max(0, Date.now() - deferredAt);
35486
+ sessionLog(drainContext.sessionID, `${LOG_PREFIX} busy wake defer ceiling reached`, {
35487
+ event: "bash_completion_wake_deferred_ceiling",
35488
+ wait_ms: waitMs,
35489
+ ceiling_ms: busyWakeDeferCeilingMs
35490
+ });
35491
+ triggerWakeIfPending(deferred.context, true).catch(() => {
35492
+ return;
35493
+ });
35494
+ }, busyWakeDeferCeilingMs)
35495
+ };
35496
+ deferred.timer.unref?.();
35497
+ deferredBusyWakes.set(drainContext.sessionID, deferred);
35498
+ }
35336
35499
  function observeOpenCodeBgNotificationEvent(event) {
35337
35500
  try {
35338
35501
  if (!event || typeof event !== "object")
@@ -35381,12 +35544,17 @@ function observeOpenCodeBgNotificationEvent(event) {
35381
35544
  return;
35382
35545
  }
35383
35546
  if (nextStatus === "idle") {
35547
+ const deferred = clearDeferredBusyWake(sessionID);
35548
+ if (deferred)
35549
+ triggerWakeIfPending(deferred.context, true).catch(() => {
35550
+ return;
35551
+ });
35384
35552
  for (const admission of state.wakeAdmissions.values())
35385
35553
  queueWakeRefire(state, admission);
35386
35554
  }
35387
35555
  } catch {}
35388
35556
  }
35389
- async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredCompletions = true, forceDrain = false) {
35557
+ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredCompletions = true, forceDrain = false, deferWhileBusy = false) {
35390
35558
  const state = stateFor(drainContext.sessionID);
35391
35559
  if (!skipDrain && (forceDrain || state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted)) {
35392
35560
  await drainCompletions(drainContext);
@@ -35394,6 +35562,10 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
35394
35562
  routeExplicitControlCompletions(state);
35395
35563
  if (!hasWakeEligiblePending(state, includeDeferredCompletions))
35396
35564
  return;
35565
+ if (deferWhileBusy && state.hostStatus === "busy") {
35566
+ deferBusyWake(drainContext);
35567
+ return;
35568
+ }
35397
35569
  scheduleWake(state, async (reminder, deliveredCompletions, deliveredPatternMatches) => {
35398
35570
  const liveSession = getActiveSessionId() ?? drainContext.sessionID;
35399
35571
  if (liveSession && drainContext.sessionID && drainContext.sessionID !== liveSession) {
@@ -36235,7 +36407,7 @@ function bridgeFor(ctx, runtime) {
36235
36407
  if (ctx.isProjectEnabled?.(projectRoot) === false) {
36236
36408
  throw new Error(`AFT disabled by config for ${projectRoot}`);
36237
36409
  }
36238
- if (!existsSync11(projectRoot)) {
36410
+ if (!existsSync12(projectRoot)) {
36239
36411
  throw new Error(`project directory no longer exists: ${projectRoot} (stale restored session?)`);
36240
36412
  }
36241
36413
  return ctx.pool.getBridge(projectRoot);
@@ -36392,9 +36564,9 @@ async function signalBashWaitDetachForProject(pool, projectRoot, sessionID) {
36392
36564
  }
36393
36565
 
36394
36566
  // src/notifications.ts
36395
- import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
36567
+ import { existsSync as existsSync13, readFileSync as readFileSync8 } from "node:fs";
36396
36568
  import { homedir as homedir12, platform } from "node:os";
36397
- import { join as join13 } from "node:path";
36569
+ import { join as join14 } from "node:path";
36398
36570
  function isTuiMode() {
36399
36571
  return process.env.OPENCODE_CLIENT === "cli";
36400
36572
  }
@@ -36416,21 +36588,21 @@ function getDesktopStatePath() {
36416
36588
  const os = platform();
36417
36589
  const home = homedir12();
36418
36590
  if (os === "darwin") {
36419
- return join13(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
36591
+ return join14(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
36420
36592
  }
36421
36593
  if (os === "linux") {
36422
- const xdgConfig = process.env.XDG_CONFIG_HOME || join13(home, ".config");
36423
- return join13(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
36594
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join14(home, ".config");
36595
+ return join14(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
36424
36596
  }
36425
36597
  if (os === "win32") {
36426
- const appData = process.env.APPDATA || join13(home, "AppData", "Roaming");
36427
- return join13(appData, "ai.opencode.desktop", "opencode.global.dat");
36598
+ const appData = process.env.APPDATA || join14(home, "AppData", "Roaming");
36599
+ return join14(appData, "ai.opencode.desktop", "opencode.global.dat");
36428
36600
  }
36429
36601
  return null;
36430
36602
  }
36431
36603
  function readDesktopState() {
36432
36604
  const statePath = getDesktopStatePath();
36433
- if (!statePath || !existsSync12(statePath)) {
36605
+ if (!statePath || !existsSync13(statePath)) {
36434
36606
  return { serverUrl: null };
36435
36607
  }
36436
36608
  try {
@@ -36930,11 +37102,11 @@ async function flushConfigureWarningsOnIdle(sessionId) {
36930
37102
  // src/hooks/auto-update-checker/index.ts
36931
37103
  import {
36932
37104
  closeSync as closeSync5,
36933
- existsSync as existsSync15,
37105
+ existsSync as existsSync16,
36934
37106
  mkdirSync as mkdirSync6,
36935
37107
  openSync as openSync5,
36936
37108
  readFileSync as readFileSync11,
36937
- renameSync as renameSync6,
37109
+ renameSync as renameSync7,
36938
37110
  rmSync as rmSync5,
36939
37111
  statSync as statSync8,
36940
37112
  writeFileSync as writeFileSync7
@@ -36943,30 +37115,30 @@ import { dirname as dirname8 } from "node:path";
36943
37115
 
36944
37116
  // src/hooks/auto-update-checker/cache.ts
36945
37117
  import { spawn as spawn4 } from "node:child_process";
36946
- import { cpSync, existsSync as existsSync14, mkdtempSync, readFileSync as readFileSync10, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
37118
+ import { cpSync, existsSync as existsSync15, mkdtempSync, readFileSync as readFileSync10, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
36947
37119
  import { tmpdir as tmpdir2 } from "node:os";
36948
- import { basename as basename3, dirname as dirname7, join as join16 } from "node:path";
37120
+ import { basename as basename3, dirname as dirname7, join as join17 } from "node:path";
36949
37121
  var import_comment_json3 = __toESM(require_src2(), 1);
36950
37122
 
36951
37123
  // src/hooks/auto-update-checker/checker.ts
36952
- import { existsSync as existsSync13, readFileSync as readFileSync9, statSync as statSync7, writeFileSync as writeFileSync5 } from "node:fs";
36953
- import { dirname as dirname6, isAbsolute as isAbsolute8, join as join15, resolve as resolve9 } from "node:path";
37124
+ import { existsSync as existsSync14, readFileSync as readFileSync9, statSync as statSync7, writeFileSync as writeFileSync5 } from "node:fs";
37125
+ import { dirname as dirname6, isAbsolute as isAbsolute8, join as join16, resolve as resolve9 } from "node:path";
36954
37126
  import { fileURLToPath } from "node:url";
36955
37127
  var import_comment_json2 = __toESM(require_src2(), 1);
36956
37128
 
36957
37129
  // src/hooks/auto-update-checker/constants.ts
36958
- import { join as join14 } from "node:path";
37130
+ import { join as join15 } from "node:path";
36959
37131
  var PACKAGE_NAME = "@cortexkit/aft-opencode";
36960
37132
  var NPM_REGISTRY_URL = "https://registry.npmjs.org";
36961
37133
  var NPM_FETCH_TIMEOUT = 1e4;
36962
37134
  function cacheDir() {
36963
- return join14(getOpenCodeCacheRoot(), "packages");
37135
+ return join15(getOpenCodeCacheRoot(), "packages");
36964
37136
  }
36965
37137
  function userOpenCodeConfig() {
36966
- return join14(getOpenCodeConfigRoot(), "opencode.json");
37138
+ return join15(getOpenCodeConfigRoot(), "opencode.json");
36967
37139
  }
36968
37140
  function userOpenCodeConfigJsonc() {
36969
- return join14(getOpenCodeConfigRoot(), "opencode.jsonc");
37141
+ return join15(getOpenCodeConfigRoot(), "opencode.jsonc");
36970
37142
  }
36971
37143
 
36972
37144
  // src/hooks/auto-update-checker/types.ts
@@ -37025,8 +37197,8 @@ function extractChannel(version) {
37025
37197
  }
37026
37198
  function getConfigPaths(directory) {
37027
37199
  return [
37028
- join15(directory, ".opencode", "opencode.json"),
37029
- join15(directory, ".opencode", "opencode.jsonc"),
37200
+ join16(directory, ".opencode", "opencode.json"),
37201
+ join16(directory, ".opencode", "opencode.jsonc"),
37030
37202
  userOpenCodeConfig(),
37031
37203
  userOpenCodeConfigJsonc()
37032
37204
  ];
@@ -37046,7 +37218,7 @@ function resolvePathPluginSpec(spec, configPath) {
37046
37218
  function getLocalDevPath(directory) {
37047
37219
  for (const configPath of getConfigPaths(directory)) {
37048
37220
  try {
37049
- if (!existsSync13(configPath))
37221
+ if (!existsSync14(configPath))
37050
37222
  continue;
37051
37223
  const rawConfig = parseJsonConfig(readFileSync9(configPath, "utf-8"));
37052
37224
  const plugins = getPluginEntries(rawConfig);
@@ -37072,8 +37244,8 @@ function findPackageJsonUp(startPath) {
37072
37244
  const stat = statSync7(startPath);
37073
37245
  let dir = stat.isDirectory() ? startPath : dirname6(startPath);
37074
37246
  for (let i = 0;i < 10; i++) {
37075
- const pkgPath = join15(dir, "package.json");
37076
- if (existsSync13(pkgPath)) {
37247
+ const pkgPath = join16(dir, "package.json");
37248
+ if (existsSync14(pkgPath)) {
37077
37249
  try {
37078
37250
  const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(pkgPath, "utf-8")));
37079
37251
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
@@ -37113,7 +37285,7 @@ function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
37113
37285
  function findPluginEntry(directory) {
37114
37286
  for (const configPath of getConfigPaths(directory)) {
37115
37287
  try {
37116
- if (!existsSync13(configPath))
37288
+ if (!existsSync14(configPath))
37117
37289
  continue;
37118
37290
  const rawConfig = parseJsonConfig(readFileSync9(configPath, "utf-8"));
37119
37291
  const plugins = getPluginEntries(rawConfig);
@@ -37133,7 +37305,7 @@ function findPluginEntry(directory) {
37133
37305
  }
37134
37306
  var cachedPackageVersion = null;
37135
37307
  function getSpecCachePackageJsonPath(spec) {
37136
- return join15(cacheDir(), spec, "node_modules", PACKAGE_NAME, "package.json");
37308
+ return join16(cacheDir(), spec, "node_modules", PACKAGE_NAME, "package.json");
37137
37309
  }
37138
37310
  function getCachedVersion(spec) {
37139
37311
  if (!spec && cachedPackageVersion)
@@ -37142,11 +37314,11 @@ function getCachedVersion(spec) {
37142
37314
  getCurrentRuntimePackageJsonPath(),
37143
37315
  spec ? getSpecCachePackageJsonPath(spec) : null,
37144
37316
  getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
37145
- join15(getOpenCodeCacheRoot(), "node_modules", PACKAGE_NAME, "package.json")
37317
+ join16(getOpenCodeCacheRoot(), "node_modules", PACKAGE_NAME, "package.json")
37146
37318
  ].filter(isString);
37147
37319
  for (const packageJsonPath of candidates) {
37148
37320
  try {
37149
- if (!existsSync13(packageJsonPath))
37321
+ if (!existsSync14(packageJsonPath))
37150
37322
  continue;
37151
37323
  const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync9(packageJsonPath, "utf-8")));
37152
37324
  if (pkg.success && pkg.data.version) {
@@ -37191,17 +37363,17 @@ async function getLatestVersion(channel = "latest", options = {}) {
37191
37363
  var pendingSnapshots = new Map;
37192
37364
  var quarantinedInstallDirs = new Set;
37193
37365
  function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
37194
- const packageDir = join16(installDir, "node_modules", packageName);
37195
- const lockfilePath = join16(installDir, "package-lock.json");
37196
- const tempDir = mkdtempSync(join16(tmpdir2(), "aft-auto-update-"));
37197
- const stagedPackageDir = existsSync14(packageDir) ? join16(tempDir, "package") : null;
37366
+ const packageDir = join17(installDir, "node_modules", packageName);
37367
+ const lockfilePath = join17(installDir, "package-lock.json");
37368
+ const tempDir = mkdtempSync(join17(tmpdir2(), "aft-auto-update-"));
37369
+ const stagedPackageDir = existsSync15(packageDir) ? join17(tempDir, "package") : null;
37198
37370
  if (stagedPackageDir)
37199
37371
  cpSync(packageDir, stagedPackageDir, { recursive: true });
37200
37372
  return {
37201
37373
  packageJsonPath,
37202
- packageJson: existsSync14(packageJsonPath) ? readFileSync10(packageJsonPath, "utf-8") : null,
37374
+ packageJson: existsSync15(packageJsonPath) ? readFileSync10(packageJsonPath, "utf-8") : null,
37203
37375
  lockfilePath,
37204
- lockfile: existsSync14(lockfilePath) ? readFileSync10(lockfilePath, "utf-8") : null,
37376
+ lockfile: existsSync15(lockfilePath) ? readFileSync10(lockfilePath, "utf-8") : null,
37205
37377
  packageDir,
37206
37378
  stagedPackageDir,
37207
37379
  tempDir
@@ -37235,8 +37407,8 @@ function stripPackageNameFromPath(pathValue, packageName) {
37235
37407
  return current;
37236
37408
  }
37237
37409
  function removeFromPackageLock(installDir, packageName) {
37238
- const lockPath = join16(installDir, "package-lock.json");
37239
- if (!existsSync14(lockPath))
37410
+ const lockPath = join17(installDir, "package-lock.json");
37411
+ if (!existsSync15(lockPath))
37240
37412
  return false;
37241
37413
  try {
37242
37414
  const lock = import_comment_json3.parse(readFileSync10(lockPath, "utf-8"));
@@ -37262,7 +37434,7 @@ function removeFromPackageLock(installDir, packageName) {
37262
37434
  }
37263
37435
  }
37264
37436
  function ensureDependencyVersion(packageJsonPath, packageName, version) {
37265
- if (!existsSync14(packageJsonPath))
37437
+ if (!existsSync15(packageJsonPath))
37266
37438
  return false;
37267
37439
  try {
37268
37440
  const raw = import_comment_json3.parse(readFileSync10(packageJsonPath, "utf-8"));
@@ -37291,8 +37463,8 @@ function restorePendingSnapshot(installDir) {
37291
37463
  restoreAutoUpdateSnapshot(snapshot);
37292
37464
  }
37293
37465
  function removeInstalledPackage(installDir, packageName) {
37294
- const packageDir = join16(installDir, "node_modules", packageName);
37295
- if (!existsSync14(packageDir))
37466
+ const packageDir = join17(installDir, "node_modules", packageName);
37467
+ if (!existsSync15(packageDir))
37296
37468
  return false;
37297
37469
  rmSync4(packageDir, { recursive: true, force: true });
37298
37470
  log2(`[auto-update-checker] Package removed: ${packageDir}`);
@@ -37304,15 +37476,15 @@ function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackage
37304
37476
  const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
37305
37477
  if (nodeModulesDir && basename3(nodeModulesDir) === "node_modules") {
37306
37478
  const installDir = dirname7(nodeModulesDir);
37307
- const packageJsonPath = join16(installDir, "package.json");
37308
- if (existsSync14(packageJsonPath))
37479
+ const packageJsonPath = join17(installDir, "package.json");
37480
+ if (existsSync15(packageJsonPath))
37309
37481
  return { installDir, packageJsonPath };
37310
37482
  }
37311
37483
  return null;
37312
37484
  }
37313
37485
  const cacheRoot = dirname7(cacheDir());
37314
- const legacyPackageJsonPath = join16(cacheRoot, "package.json");
37315
- if (existsSync14(legacyPackageJsonPath)) {
37486
+ const legacyPackageJsonPath = join17(cacheRoot, "package.json");
37487
+ if (existsSync15(legacyPackageJsonPath)) {
37316
37488
  return { installDir: cacheRoot, packageJsonPath: legacyPackageJsonPath };
37317
37489
  }
37318
37490
  return null;
@@ -37535,7 +37707,7 @@ function claimCheckSlot(storageDir, intervalMs) {
37535
37707
  const replacementPath = `${lockPath}.tmp.${process.pid}`;
37536
37708
  writeFileSync7(replacementPath, JSON.stringify(owner), { encoding: "utf-8", mode: 384 });
37537
37709
  try {
37538
- renameSync6(replacementPath, lockPath);
37710
+ renameSync7(replacementPath, lockPath);
37539
37711
  } catch (replaceError) {
37540
37712
  rmSync5(replacementPath, { force: true });
37541
37713
  throw replaceError;
@@ -37603,7 +37775,7 @@ function checkLockIsOwnedBy(lockPath, owner) {
37603
37775
  }
37604
37776
  }
37605
37777
  function hasRecentCheckTimestamp(file, intervalMs) {
37606
- if (!existsSync15(file))
37778
+ if (!existsSync16(file))
37607
37779
  return false;
37608
37780
  try {
37609
37781
  const raw = JSON.parse(readFileSync11(file, "utf-8"));
@@ -37616,7 +37788,7 @@ function hasRecentCheckTimestamp(file, intervalMs) {
37616
37788
  function writeCheckTimestamp(file) {
37617
37789
  const tmp = `${file}.tmp.${process.pid}`;
37618
37790
  writeFileSync7(tmp, JSON.stringify({ lastCheckedMs: Date.now() }), "utf-8");
37619
- renameSync6(tmp, file);
37791
+ renameSync7(tmp, file);
37620
37792
  }
37621
37793
  async function runStartupCheck(ctx, options) {
37622
37794
  if (options.signal.aborted)
@@ -37713,15 +37885,15 @@ import { spawn as spawn5 } from "node:child_process";
37713
37885
  import { createHash as createHash6 } from "node:crypto";
37714
37886
  import {
37715
37887
  createReadStream,
37716
- existsSync as existsSync17,
37888
+ existsSync as existsSync18,
37717
37889
  mkdirSync as mkdirSync8,
37718
37890
  readFileSync as readFileSync14,
37719
- renameSync as renameSync8,
37891
+ renameSync as renameSync9,
37720
37892
  rmSync as rmSync7,
37721
37893
  statSync as statSync10,
37722
37894
  writeFileSync as writeFileSync9
37723
37895
  } from "node:fs";
37724
- import { join as join19 } from "node:path";
37896
+ import { join as join20 } from "node:path";
37725
37897
 
37726
37898
  // src/lsp-cache.ts
37727
37899
  import { randomUUID as randomUUID3 } from "node:crypto";
@@ -37730,13 +37902,13 @@ import {
37730
37902
  mkdirSync as mkdirSync7,
37731
37903
  openSync as openSync6,
37732
37904
  readFileSync as readFileSync12,
37733
- renameSync as renameSync7,
37905
+ renameSync as renameSync8,
37734
37906
  rmSync as rmSync6,
37735
37907
  statSync as statSync9,
37736
37908
  unlinkSync as unlinkSync6,
37737
37909
  writeFileSync as writeFileSync8
37738
37910
  } from "node:fs";
37739
- import { join as join17 } from "node:path";
37911
+ import { join as join18 } from "node:path";
37740
37912
  function aftCacheBase() {
37741
37913
  return getAftCacheRoot();
37742
37914
  }
@@ -37744,18 +37916,18 @@ function lspCacheRoot() {
37744
37916
  return getAftLspPackagesDir();
37745
37917
  }
37746
37918
  function lspPackageDir(npmPackage) {
37747
- return join17(lspCacheRoot(), encodeURIComponent(npmPackage));
37919
+ return join18(lspCacheRoot(), encodeURIComponent(npmPackage));
37748
37920
  }
37749
37921
  function lspBinaryPath(npmPackage, binary) {
37750
- return join17(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
37922
+ return join18(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
37751
37923
  }
37752
37924
  function lspBinDir(npmPackage) {
37753
- return join17(lspPackageDir(npmPackage), "node_modules", ".bin");
37925
+ return join18(lspPackageDir(npmPackage), "node_modules", ".bin");
37754
37926
  }
37755
37927
  function isInstalled(npmPackage, binary) {
37756
37928
  for (const candidate of lspBinaryCandidates(binary)) {
37757
37929
  try {
37758
- if (statSync9(join17(lspBinDir(npmPackage), candidate)).isFile())
37930
+ if (statSync9(join18(lspBinDir(npmPackage), candidate)).isFile())
37759
37931
  return true;
37760
37932
  } catch {}
37761
37933
  }
@@ -37775,13 +37947,13 @@ function writeInstalledMetaIn(installDir, version, sha256) {
37775
37947
  installedAt: new Date().toISOString(),
37776
37948
  ...sha256 ? { sha256 } : {}
37777
37949
  };
37778
- writeFileSync8(join17(installDir, INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
37950
+ writeFileSync8(join18(installDir, INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
37779
37951
  } catch (err) {
37780
37952
  log2(`[lsp-cache] failed to write installed-meta in ${installDir}: ${err}`);
37781
37953
  }
37782
37954
  }
37783
37955
  function readInstalledMetaIn(installDir) {
37784
- const path = join17(installDir, INSTALLED_META_FILE);
37956
+ const path = join18(installDir, INSTALLED_META_FILE);
37785
37957
  try {
37786
37958
  if (!statSync9(path).isFile())
37787
37959
  return null;
@@ -37805,7 +37977,7 @@ function readInstalledMeta(packageKey) {
37805
37977
  return readInstalledMetaIn(lspPackageDir(packageKey));
37806
37978
  }
37807
37979
  function lockPath(npmPackage) {
37808
- return join17(lspPackageDir(npmPackage), ".aft-installing");
37980
+ return join18(lspPackageDir(npmPackage), ".aft-installing");
37809
37981
  }
37810
37982
  var STALE_LOCK_MS2 = 30 * 60 * 1000;
37811
37983
  function acquireInstallLock(lockKey) {
@@ -37936,7 +38108,7 @@ var AUTO_INSTALL_STAMP_FILE = "last-lsp-autoinstall.json";
37936
38108
  var AUTO_INSTALL_WINDOW_MS = 60 * 1000;
37937
38109
  var AUTO_INSTALL_LOCK_STALE_MS = 30 * 60 * 1000;
37938
38110
  function autoInstallStampPath() {
37939
- return join17(aftCacheBase(), AUTO_INSTALL_STAMP_FILE);
38111
+ return join18(aftCacheBase(), AUTO_INSTALL_STAMP_FILE);
37940
38112
  }
37941
38113
  function hasRecentAutoInstallStamp(path) {
37942
38114
  try {
@@ -38022,7 +38194,7 @@ function claimLspAutoInstallPass() {
38022
38194
  encoding: "utf8",
38023
38195
  mode: 384
38024
38196
  });
38025
- renameSync7(tempPath, stampPath);
38197
+ renameSync8(tempPath, stampPath);
38026
38198
  return { release };
38027
38199
  } catch (err) {
38028
38200
  if (claimed) {
@@ -38037,7 +38209,7 @@ function claimLspAutoInstallPass() {
38037
38209
  }
38038
38210
  var VERSION_CHECK_FILE = ".aft-version-check";
38039
38211
  function readVersionCheck(npmPackage) {
38040
- const file = join17(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
38212
+ const file = join18(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
38041
38213
  try {
38042
38214
  const raw = readFileSync12(file, "utf8");
38043
38215
  const parsed = JSON.parse(raw);
@@ -38054,7 +38226,7 @@ function readVersionCheck(npmPackage) {
38054
38226
  }
38055
38227
  function writeVersionCheck(npmPackage, latest) {
38056
38228
  mkdirSync7(lspPackageDir(npmPackage), { recursive: true });
38057
- const file = join17(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
38229
+ const file = join18(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
38058
38230
  const record = {
38059
38231
  last_checked: new Date().toISOString(),
38060
38232
  latest_eligible: latest
@@ -38216,8 +38388,8 @@ var NPM_LSP_TABLE = [
38216
38388
  ];
38217
38389
 
38218
38390
  // src/lsp-project-relevance.ts
38219
- import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "node:fs";
38220
- import { join as join18 } from "node:path";
38391
+ import { existsSync as existsSync17, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "node:fs";
38392
+ import { join as join19 } from "node:path";
38221
38393
  var MAX_WALK_DIRS = 200;
38222
38394
  var MAX_WALK_DEPTH = 4;
38223
38395
  var NOISE_DIRS = new Set([
@@ -38234,7 +38406,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
38234
38406
  if (!rootMarkers)
38235
38407
  return false;
38236
38408
  for (const marker of rootMarkers) {
38237
- if (existsSync16(join18(projectRoot, marker)))
38409
+ if (existsSync17(join19(projectRoot, marker)))
38238
38410
  return true;
38239
38411
  }
38240
38412
  return false;
@@ -38258,7 +38430,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
38258
38430
  }
38259
38431
  function readPackageJson(projectRoot) {
38260
38432
  try {
38261
- const raw = readFileSync13(join18(projectRoot, "package.json"), "utf8");
38433
+ const raw = readFileSync13(join19(projectRoot, "package.json"), "utf8");
38262
38434
  const parsed = JSON.parse(raw);
38263
38435
  if (typeof parsed !== "object" || parsed === null)
38264
38436
  return null;
@@ -38288,7 +38460,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
38288
38460
  for (const entry of entries) {
38289
38461
  if (entry.isDirectory()) {
38290
38462
  if (current.depth < MAX_WALK_DEPTH && !NOISE_DIRS.has(entry.name.toLowerCase())) {
38291
- queue.push({ dir: join18(current.dir, entry.name), depth: current.depth + 1 });
38463
+ queue.push({ dir: join19(current.dir, entry.name), depth: current.depth + 1 });
38292
38464
  }
38293
38465
  continue;
38294
38466
  }
@@ -38416,8 +38588,8 @@ async function resolveTargetVersion(spec, config, fetchImpl = fetch) {
38416
38588
  }
38417
38589
  function ensureInstallAnchor(cwd) {
38418
38590
  try {
38419
- const stub = join19(cwd, "package.json");
38420
- if (!existsSync17(stub)) {
38591
+ const stub = join20(cwd, "package.json");
38592
+ if (!existsSync18(stub)) {
38421
38593
  writeFileSync9(stub, `${JSON.stringify({ name: "aft-lsp-cache", version: "0.0.0", private: true })}
38422
38594
  `);
38423
38595
  }
@@ -38643,12 +38815,12 @@ function sha256OfFileSync(path) {
38643
38815
  }
38644
38816
  function quarantineCachedNpmInstall(spec, reason) {
38645
38817
  const packageDir = lspPackageDir(spec.npm);
38646
- const dest = join19(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
38818
+ const dest = join20(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
38647
38819
  warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
38648
38820
  try {
38649
- mkdirSync8(join19(dest, ".."), { recursive: true });
38821
+ mkdirSync8(join20(dest, ".."), { recursive: true });
38650
38822
  rmSync7(dest, { recursive: true, force: true });
38651
- renameSync8(packageDir, dest);
38823
+ renameSync9(packageDir, dest);
38652
38824
  } catch (err) {
38653
38825
  warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
38654
38826
  }
@@ -38729,26 +38901,26 @@ async function pushLspPathsAfterAutoInstall(pool, projectRoot, cachedBinDirs) {
38729
38901
  }
38730
38902
 
38731
38903
  // src/lsp-github-install.ts
38732
- import { execFileSync as execFileSync2 } from "node:child_process";
38904
+ import { execFileSync as execFileSync3 } from "node:child_process";
38733
38905
  import { createHash as createHash7, randomBytes as randomBytes3 } from "node:crypto";
38734
38906
  import {
38735
38907
  copyFileSync as copyFileSync4,
38736
38908
  createReadStream as createReadStream2,
38737
38909
  createWriteStream as createWriteStream3,
38738
- existsSync as existsSync18,
38910
+ existsSync as existsSync19,
38739
38911
  lstatSync as lstatSync2,
38740
38912
  mkdirSync as mkdirSync9,
38741
38913
  readdirSync as readdirSync5,
38742
38914
  readFileSync as readFileSync15,
38743
38915
  readlinkSync as readlinkSync2,
38744
38916
  realpathSync as realpathSync3,
38745
- renameSync as renameSync9,
38917
+ renameSync as renameSync10,
38746
38918
  rmSync as rmSync8,
38747
38919
  statSync as statSync11,
38748
38920
  unlinkSync as unlinkSync7,
38749
38921
  writeFileSync as writeFileSync10
38750
38922
  } from "node:fs";
38751
- import { dirname as dirname9, join as join20, relative as relative3, resolve as resolve10 } from "node:path";
38923
+ import { dirname as dirname9, join as join21, relative as relative3, resolve as resolve10 } from "node:path";
38752
38924
  import { Readable as Readable3 } from "node:stream";
38753
38925
  import { pipeline as pipeline3 } from "node:stream/promises";
38754
38926
 
@@ -38841,19 +39013,19 @@ function ghCacheRoot() {
38841
39013
  return getAftLspBinariesDir();
38842
39014
  }
38843
39015
  function ghPackageDir(spec) {
38844
- return join20(ghCacheRoot(), spec.id);
39016
+ return join21(ghCacheRoot(), spec.id);
38845
39017
  }
38846
39018
  function ghBinDir(spec) {
38847
- return join20(ghPackageDir(spec), "bin");
39019
+ return join21(ghPackageDir(spec), "bin");
38848
39020
  }
38849
39021
  function ghExtractDir(spec) {
38850
- return join20(ghPackageDir(spec), "extracted");
39022
+ return join21(ghPackageDir(spec), "extracted");
38851
39023
  }
38852
39024
  var INSTALLED_META_FILE2 = ".aft-installed";
38853
39025
  var INSTALL_FAILED_FILE = ".aft-install-failed.json";
38854
39026
  var INSTALL_FAILURE_BACKOFF_MS = 6 * 60 * 60 * 1000;
38855
39027
  function installFailureMarkerPath(spec) {
38856
- return join20(ghPackageDir(spec), INSTALL_FAILED_FILE);
39028
+ return join21(ghPackageDir(spec), INSTALL_FAILED_FILE);
38857
39029
  }
38858
39030
  function readInstallFailureMarker(spec) {
38859
39031
  try {
@@ -38874,7 +39046,7 @@ function writeInstallFailureMarker(spec, release, sha256, installError) {
38874
39046
  mkdirSync9(dirname9(path), { recursive: true });
38875
39047
  writeFileSync10(tempPath, `${JSON.stringify({ release, sha256, error: markerError, at: new Date().toISOString() })}
38876
39048
  `, { encoding: "utf8", mode: 384 });
38877
- renameSync9(tempPath, path);
39049
+ renameSync10(tempPath, path);
38878
39050
  } catch (err) {
38879
39051
  try {
38880
39052
  rmSync8(tempPath, { force: true });
@@ -38895,12 +39067,12 @@ function isFreshInstallFailure(marker) {
38895
39067
  }
38896
39068
  function ghBinaryPath(spec, platform) {
38897
39069
  const ext = platform === "win32" ? ".exe" : "";
38898
- return join20(ghBinDir(spec), `${spec.binary}${ext}`);
39070
+ return join21(ghBinDir(spec), `${spec.binary}${ext}`);
38899
39071
  }
38900
39072
  function isGithubInstalled(spec, platform) {
38901
39073
  for (const candidate of ghBinaryCandidates(spec, platform)) {
38902
39074
  try {
38903
- if (statSync11(join20(ghBinDir(spec), candidate)).isFile())
39075
+ if (statSync11(join21(ghBinDir(spec), candidate)).isFile())
38904
39076
  return true;
38905
39077
  } catch {}
38906
39078
  }
@@ -38913,7 +39085,7 @@ function ghBinaryCandidates(spec, platform) {
38913
39085
  }
38914
39086
  function readGithubInstalledMetaIn(installDir) {
38915
39087
  try {
38916
- const path = join20(installDir, INSTALLED_META_FILE2);
39088
+ const path = join21(installDir, INSTALLED_META_FILE2);
38917
39089
  if (!statSync11(path).isFile())
38918
39090
  return null;
38919
39091
  const parsed = JSON.parse(readFileSync15(path, "utf8"));
@@ -38940,7 +39112,7 @@ function writeGithubInstalledMetaIn(installDir, version, binarySha256, archiveSh
38940
39112
  binarySha256,
38941
39113
  ...archiveSha256 ? { archiveSha256 } : {}
38942
39114
  };
38943
- writeFileSync10(join20(installDir, INSTALLED_META_FILE2), JSON.stringify(meta), "utf8");
39115
+ writeFileSync10(join21(installDir, INSTALLED_META_FILE2), JSON.stringify(meta), "utf8");
38944
39116
  } catch (err) {
38945
39117
  warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
38946
39118
  }
@@ -39189,7 +39361,7 @@ function validateExtraction(stagingRoot) {
39189
39361
  throw new Error(`failed to read staging dir ${dir}: ${err}`);
39190
39362
  }
39191
39363
  for (const entry of entries) {
39192
- const full = join20(dir, entry);
39364
+ const full = join21(dir, entry);
39193
39365
  let lst;
39194
39366
  try {
39195
39367
  lst = lstatSync2(full);
@@ -39235,7 +39407,7 @@ function assertArchiveEntryPath(entry) {
39235
39407
  }
39236
39408
  function precheckWithTar(archivePath, command) {
39237
39409
  let totalBytes = 0;
39238
- const verbose = execFileSync2(command, ["-tvf", archivePath], { encoding: "utf8" });
39410
+ const verbose = execFileSync3(command, ["-tvf", archivePath], { encoding: "utf8" });
39239
39411
  for (const line of verbose.split(`
39240
39412
  `)) {
39241
39413
  if (line.startsWith("h")) {
@@ -39248,7 +39420,7 @@ function precheckWithTar(archivePath, command) {
39248
39420
  totalBytes += Math.max(...numeric);
39249
39421
  }
39250
39422
  }
39251
- const names = execFileSync2(command, ["-tf", archivePath], { encoding: "utf8" });
39423
+ const names = execFileSync3(command, ["-tf", archivePath], { encoding: "utf8" });
39252
39424
  for (const entry of names.split(`
39253
39425
  `)) {
39254
39426
  if (entry.length > 0)
@@ -39259,19 +39431,18 @@ function precheckWithTar(archivePath, command) {
39259
39431
  function precheckArchiveContents(archivePath, archiveType, zipListingMode = "platform") {
39260
39432
  let totalBytes = 0;
39261
39433
  if (archiveType === "zip" && zipListingMode === "platform" && process.platform !== "win32") {
39262
- const out = execFileSync2("unzip", ["-l", archivePath], { encoding: "utf8" });
39434
+ const out = execFileSync3("unzip", ["-l", archivePath], { encoding: "utf8" });
39263
39435
  const match = out.match(/^\s*(\d+)\s+\d+\s+files?\s*$/m);
39264
39436
  if (match)
39265
39437
  totalBytes = Number.parseInt(match[1] ?? "0", 10);
39266
- const names = execFileSync2("unzip", ["-Z1", archivePath], { encoding: "utf8" });
39438
+ const names = execFileSync3("unzip", ["-Z1", archivePath], { encoding: "utf8" });
39267
39439
  for (const entry of names.split(`
39268
39440
  `)) {
39269
39441
  if (entry.length > 0)
39270
39442
  assertArchiveEntryPath(entry);
39271
39443
  }
39272
39444
  } else {
39273
- const command = process.platform === "win32" ? "tar.exe" : "tar";
39274
- totalBytes = precheckWithTar(archivePath, command);
39445
+ totalBytes = precheckWithTar(archivePath, windowsTarExecutable());
39275
39446
  }
39276
39447
  if (totalBytes > MAX_EXTRACT_BYTES2) {
39277
39448
  throw new Error(`archive uncompressed size ${totalBytes} exceeds ${MAX_EXTRACT_BYTES2}`);
@@ -39291,7 +39462,7 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
39291
39462
  try {
39292
39463
  rmSync8(destDir, { recursive: true, force: true });
39293
39464
  } catch {}
39294
- renameSync9(stagingDir, destDir);
39465
+ renameSync10(stagingDir, destDir);
39295
39466
  } catch (err) {
39296
39467
  try {
39297
39468
  rmSync8(stagingDir, { recursive: true, force: true });
@@ -39301,12 +39472,12 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
39301
39472
  }
39302
39473
  function quarantineCachedGithubInstall(spec, reason) {
39303
39474
  const packageDir = ghPackageDir(spec);
39304
- const dest = join20(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
39475
+ const dest = join21(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
39305
39476
  warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
39306
39477
  try {
39307
39478
  mkdirSync9(dirname9(dest), { recursive: true });
39308
39479
  rmSync8(dest, { recursive: true, force: true });
39309
- renameSync9(packageDir, dest);
39480
+ renameSync10(packageDir, dest);
39310
39481
  } catch (err) {
39311
39482
  warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
39312
39483
  }
@@ -39314,7 +39485,7 @@ function quarantineCachedGithubInstall(spec, reason) {
39314
39485
  function validateCachedGithubInstall(spec, platform) {
39315
39486
  const packageDir = ghPackageDir(spec);
39316
39487
  const meta = readGithubInstalledMetaIn(packageDir);
39317
- const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join20(ghBinDir(spec), candidate)).find((candidate) => {
39488
+ const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join21(ghBinDir(spec), candidate)).find((candidate) => {
39318
39489
  try {
39319
39490
  return statSync11(candidate).isFile();
39320
39491
  } catch {
@@ -39350,30 +39521,21 @@ function validateCachedGithubInstall(spec, platform) {
39350
39521
  function runPlatformExtractor(archivePath, destDir, archiveType) {
39351
39522
  if (archiveType === "zip") {
39352
39523
  if (process.platform === "win32") {
39353
- execFileSync2("tar.exe", ["-xf", archivePath, "-C", destDir], {
39354
- stdio: "pipe",
39355
- timeout: 180000
39356
- });
39524
+ execTarExtractionSync(["-xf", archivePath, "-C", destDir], 180000);
39357
39525
  return;
39358
39526
  }
39359
- execFileSync2("unzip", ["-q", "-o", archivePath, "-d", destDir], {
39527
+ execFileSync3("unzip", ["-q", "-o", archivePath, "-d", destDir], {
39360
39528
  stdio: "pipe",
39361
39529
  timeout: 180000
39362
39530
  });
39363
39531
  return;
39364
39532
  }
39365
39533
  if (archiveType === "tar.gz") {
39366
- execFileSync2("tar", ["-xzf", archivePath, "-C", destDir], {
39367
- stdio: "pipe",
39368
- timeout: 180000
39369
- });
39534
+ execTarExtractionSync(["-xzf", archivePath, "-C", destDir], 180000);
39370
39535
  return;
39371
39536
  }
39372
39537
  if (archiveType === "tar.xz") {
39373
- execFileSync2("tar", ["-xf", archivePath, "-C", destDir], {
39374
- stdio: "pipe",
39375
- timeout: 180000
39376
- });
39538
+ execTarExtractionSync(["-xf", archivePath, "-C", destDir], 180000);
39377
39539
  return;
39378
39540
  }
39379
39541
  throw new Error(`unsupported archive type: ${archiveType}`);
@@ -39394,7 +39556,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
39394
39556
  }
39395
39557
  const pkgDir = ghPackageDir(spec);
39396
39558
  const extractDir = ghExtractDir(spec);
39397
- const archivePath = join20(pkgDir, expected.name);
39559
+ const archivePath = join21(pkgDir, expected.name);
39398
39560
  log2(`[lsp] downloading ${spec.id} ${tag} → ${matchingAsset.url}`);
39399
39561
  try {
39400
39562
  await downloadFile(matchingAsset.url, archivePath, fetchImpl, matchingAsset.size, signal);
@@ -39436,8 +39598,8 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
39436
39598
  unlinkSync7(archivePath);
39437
39599
  } catch {}
39438
39600
  }
39439
- const innerBinaryPath = join20(extractDir, spec.binaryPathInArchive(platform, arch, version));
39440
- if (!existsSync18(innerBinaryPath)) {
39601
+ const innerBinaryPath = join21(extractDir, spec.binaryPathInArchive(platform, arch, version));
39602
+ if (!existsSync19(innerBinaryPath)) {
39441
39603
  const message = `extracted binary not found at ${innerBinaryPath}`;
39442
39604
  error2(`[lsp] ${spec.id}: ${message}`);
39443
39605
  return { ok: false, sha256: archiveSha256, error: message };
@@ -39542,7 +39704,7 @@ function runGithubAutoInstall(relevantServers, config, fetchImpl = fetch) {
39542
39704
  if (!host) {
39543
39705
  for (const spec of GITHUB_LSP_TABLE) {
39544
39706
  try {
39545
- if (existsSync18(ghBinDir(spec))) {
39707
+ if (existsSync19(ghBinDir(spec))) {
39546
39708
  cachedBinDirs.push(ghBinDir(spec));
39547
39709
  }
39548
39710
  } catch {}
@@ -39859,25 +40021,25 @@ function isTuiConnected(sessionId) {
39859
40021
  // src/shared/rpc-server.ts
39860
40022
  import { randomBytes as randomBytes4, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
39861
40023
  import {
39862
- existsSync as existsSync19,
40024
+ existsSync as existsSync20,
39863
40025
  mkdirSync as mkdirSync10,
39864
40026
  readdirSync as readdirSync6,
39865
40027
  readFileSync as readFileSync16,
39866
- renameSync as renameSync10,
40028
+ renameSync as renameSync11,
39867
40029
  unlinkSync as unlinkSync8,
39868
40030
  writeFileSync as writeFileSync11
39869
40031
  } from "node:fs";
39870
40032
  import { createServer } from "node:http";
39871
- import { dirname as dirname10, join as join22 } from "node:path";
40033
+ import { dirname as dirname10, join as join23 } from "node:path";
39872
40034
 
39873
40035
  // src/shared/rpc-utils.ts
39874
- import { join as join21 } from "node:path";
40036
+ import { join as join22 } from "node:path";
39875
40037
  function projectHash(directory) {
39876
40038
  return projectRootKeyHash(directory);
39877
40039
  }
39878
40040
  function rpcPortFileDir(storageDir, directory) {
39879
40041
  const hash = projectHash(directory);
39880
- return join21(storageDir, "rpc", hash, "ports");
40042
+ return join22(storageDir, "rpc", hash, "ports");
39881
40043
  }
39882
40044
  function isPidAlive(pid) {
39883
40045
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
@@ -39951,7 +40113,7 @@ class AftRpcServer {
39951
40113
  constructor(storageDir, directory) {
39952
40114
  this.portsDir = rpcPortFileDir(storageDir, directory);
39953
40115
  this.instanceId = randomBytes4(8).toString("hex");
39954
- this.portFilePath = join22(this.portsDir, `${this.instanceId}.json`);
40116
+ this.portFilePath = join23(this.portsDir, `${this.instanceId}.json`);
39955
40117
  }
39956
40118
  handle(method, handler) {
39957
40119
  this.handlers.set(method, handler);
@@ -39981,6 +40143,7 @@ class AftRpcServer {
39981
40143
  });
39982
40144
  this.bunServer = server;
39983
40145
  this.port = server.port ?? 0;
40146
+ server.unref?.();
39984
40147
  this.afterServerStarted();
39985
40148
  return this.port;
39986
40149
  }
@@ -40026,7 +40189,7 @@ class AftRpcServer {
40026
40189
  pid: process.pid,
40027
40190
  started_at: Date.now()
40028
40191
  }), { encoding: "utf-8", mode: 384 });
40029
- renameSync10(tmpPath, this.portFilePath);
40192
+ renameSync11(tmpPath, this.portFilePath);
40030
40193
  }
40031
40194
  sweepDeadPortFiles() {
40032
40195
  let entries;
@@ -40038,7 +40201,7 @@ class AftRpcServer {
40038
40201
  for (const entry of entries) {
40039
40202
  if (!entry.endsWith(".json"))
40040
40203
  continue;
40041
- const filePath = join22(this.portsDir, entry);
40204
+ const filePath = join23(this.portsDir, entry);
40042
40205
  if (filePath === this.portFilePath)
40043
40206
  continue;
40044
40207
  try {
@@ -40057,7 +40220,7 @@ class AftRpcServer {
40057
40220
  if (!this.nodeServer && !this.bunServer || this.port <= 0)
40058
40221
  return;
40059
40222
  try {
40060
- if (existsSync19(this.portFilePath))
40223
+ if (existsSync20(this.portFilePath))
40061
40224
  return;
40062
40225
  this.writePortFile();
40063
40226
  log2(`RPC port file was missing; rewrote ${this.portFilePath}`);
@@ -40324,12 +40487,6 @@ function formatCacheRoleLabel(role) {
40324
40487
  }
40325
40488
  return role;
40326
40489
  }
40327
- function formatSemanticIndexStatus(status, stage) {
40328
- if ((status === "loading" || status === "building") && stage === "fingerprint_change") {
40329
- return "Rebuilding (model changed)";
40330
- }
40331
- return status;
40332
- }
40333
40490
  function formatSemanticRefreshing(refreshingCount) {
40334
40491
  if (!Number.isFinite(refreshingCount) || refreshingCount <= 0)
40335
40492
  return null;
@@ -40441,7 +40598,7 @@ function formatStatusMarkdown(status) {
40441
40598
  `- **Cache role:** \`${formatCacheRoleLabel(status.cache_role)}\``
40442
40599
  ];
40443
40600
  appendDegradedStatus(lines, status, true);
40444
- lines.push("", "### Enabled features", `- \`format_on_edit\`: ${formatFlag(status.features.format_on_edit)}`, `- \`search_index\`: ${formatFlag(status.features.search_index)}`, `- \`semantic_search\`: ${formatFlag(status.features.semantic_search)}`, `- \`callgraph_store\`: ${formatFlag(status.features.callgraph_store)}`, "", "### Search index", `- **Status:** \`${status.search_index.status}\``, `- **Files:** ${formatCount(status.search_index.files)}`, `- **Trigrams:** ${formatCount(status.search_index.trigrams)}`, "", "### Semantic index", `- **Status:** \`${formatSemanticIndexStatus(status.semantic_index.status, status.semantic_index.stage)}\``);
40601
+ lines.push("", "### Enabled features", `- \`format_on_edit\`: ${formatFlag(status.features.format_on_edit)}`, `- \`search_index\`: ${formatFlag(status.features.search_index)}`, `- \`semantic_search\`: ${formatFlag(status.features.semantic_search)}`, `- \`callgraph_store\`: ${formatFlag(status.features.callgraph_store)}`, "", "### Search index", `- **Status:** \`${status.search_index.status}\``, `- **Files:** ${formatCount(status.search_index.files)}`, `- **Trigrams:** ${formatCount(status.search_index.trigrams)}`, "", "### Semantic index", `- **Status:** \`${formatSemanticIndexStatus(status.semantic_index.status, status.semantic_index.stage, status.semantic_index.error)}\``);
40445
40602
  const refreshing = formatSemanticRefreshing(status.semantic_index.refreshing_count);
40446
40603
  if (refreshing) {
40447
40604
  lines.push(`- **Refresh:** ${refreshing}`);
@@ -40610,7 +40767,7 @@ function isSyncWatchAborted(sessionID) {
40610
40767
  }
40611
40768
 
40612
40769
  // src/tools/permissions.ts
40613
- import { execFileSync as execFileSync3 } from "node:child_process";
40770
+ import { execFileSync as execFileSync4 } from "node:child_process";
40614
40771
  import * as fs4 from "node:fs";
40615
40772
  import { tmpdir as tmpdir3 } from "node:os";
40616
40773
  import * as path3 from "node:path";
@@ -40865,7 +41022,7 @@ function gitRootForNearestExistingParent(resolved) {
40865
41022
  cwd = path3.dirname(nearest);
40866
41023
  } catch {}
40867
41024
  try {
40868
- const out = execFileSync3("git", ["rev-parse", "--show-toplevel"], {
41025
+ const out = execFileSync4("git", ["rev-parse", "--show-toplevel"], {
40869
41026
  cwd,
40870
41027
  encoding: "utf8",
40871
41028
  stdio: ["ignore", "pipe", "ignore"]
@@ -41178,6 +41335,26 @@ function failure(error) {
41178
41335
  })();
41179
41336
  return new Error(`V2 tool execution rejected with a non-Error value: ${detail}`);
41180
41337
  }
41338
+ function isPlainObject3(value) {
41339
+ if (typeof value !== "object" || value === null)
41340
+ return false;
41341
+ const prototype = Object.getPrototypeOf(value);
41342
+ return prototype === Object.prototype || prototype === null;
41343
+ }
41344
+ function jsonSafeMetadata(value) {
41345
+ if (Array.isArray(value)) {
41346
+ return value.map((entry) => entry === undefined ? null : jsonSafeMetadata(entry));
41347
+ }
41348
+ if (!isPlainObject3(value))
41349
+ return value;
41350
+ const cleaned = {};
41351
+ for (const [key, entry] of Object.entries(value)) {
41352
+ if (entry === undefined)
41353
+ continue;
41354
+ cleaned[key] = jsonSafeMetadata(entry);
41355
+ }
41356
+ return cleaned;
41357
+ }
41181
41358
  function resultContent(result) {
41182
41359
  if (typeof result === "string")
41183
41360
  return { content: result };
@@ -41188,15 +41365,39 @@ function resultContent(result) {
41188
41365
  ...attachment.filename ? { name: attachment.filename } : {}
41189
41366
  }));
41190
41367
  const content = attachments.length ? [{ type: "text", text: result.output }, ...attachments] : result.output;
41191
- const metadata = {
41368
+ const metadata = jsonSafeMetadata({
41192
41369
  ...result.metadata ?? {},
41193
41370
  ...result.title ? { title: result.title } : {}
41194
- };
41371
+ });
41195
41372
  return {
41196
41373
  content,
41197
41374
  ...Object.keys(metadata).length ? { metadata } : {}
41198
41375
  };
41199
41376
  }
41377
+ function failureEnvelopeMessage(result) {
41378
+ const output = typeof result === "string" ? result : result.output;
41379
+ if (typeof output !== "string")
41380
+ return;
41381
+ const trimmed = output.trim();
41382
+ if (!trimmed.startsWith("{") || !trimmed.includes('"success"'))
41383
+ return;
41384
+ let parsed;
41385
+ try {
41386
+ parsed = JSON.parse(trimmed);
41387
+ } catch {
41388
+ return;
41389
+ }
41390
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
41391
+ return;
41392
+ const envelope = parsed;
41393
+ if (envelope.success !== false)
41394
+ return;
41395
+ for (const field of [envelope.message, envelope.error, envelope.code]) {
41396
+ if (typeof field === "string" && field.length > 0)
41397
+ return field;
41398
+ }
41399
+ return "The tool call failed.";
41400
+ }
41200
41401
  function bareToolName3(name) {
41201
41402
  return name.startsWith("aft_") ? name.slice(4) : name;
41202
41403
  }
@@ -41259,15 +41460,15 @@ function runtimeFor(location, context, signal, consumers) {
41259
41460
  abort: signal,
41260
41461
  effectAbort: signal,
41261
41462
  metadata: (update) => {
41262
- Effect.runPromise(context.progress({
41463
+ Effect.runPromise(context.progress(jsonSafeMetadata({
41263
41464
  ...update.metadata ?? {},
41264
41465
  ...update.title ? { title: update.title } : {}
41265
- }));
41466
+ })));
41266
41467
  },
41267
41468
  ask: (request) => {
41268
41469
  if (consumers.requestPermission)
41269
41470
  return consumers.requestPermission(request, context);
41270
- return Promise.reject(new Error(`The "${request.permission}" operation was refused because the OpenCode V2 host did not provide a permission request endpoint.`));
41471
+ return Promise.reject(new Error(`The "${request.permission}" operation was refused because this AFT runtime has no permission evaluator bound, so the host's permission rules for it could not be consulted.`));
41271
41472
  },
41272
41473
  progress: context.progress
41273
41474
  };
@@ -41296,6 +41497,9 @@ function projectV2Tool(name, definition, location, consumers = {}) {
41296
41497
  context: runtime,
41297
41498
  definition
41298
41499
  }) : await definition.execute(input, runtime);
41500
+ const refusal = failureEnvelopeMessage(result);
41501
+ if (refusal)
41502
+ throw new Error(refusal);
41299
41503
  return resultContent(result);
41300
41504
  },
41301
41505
  catch: failure
@@ -42390,7 +42594,7 @@ function createReadTool(ctx) {
42390
42594
  try {
42391
42595
  await runAsk(context.ask({
42392
42596
  permission: "read",
42393
- patterns: [filePath],
42597
+ patterns: [permissionPath(context, filePath)],
42394
42598
  always: ["*"],
42395
42599
  metadata: {}
42396
42600
  }));
@@ -42551,7 +42755,7 @@ function getEditDescription(ctx, writeToolName) {
42551
42755
 
42552
42756
  **Modes** (determined by which parameters you provide):
42553
42757
 
42554
- Provide exactly one mode per call: appendContent, edits[], or symbol plus content. Mixing modes or providing none is rejected — there is no implicit "write" fallback. To edit multiple files, make parallel \`edit\` calls in one response.
42758
+ Provide exactly one mode per call: appendContent, edits[], or symbol plus content. \`symbol\` and \`content\` are only meaningful together; a call carrying one without the other is incomplete. Mixing modes or providing none is rejected — there is no implicit "write" fallback. To edit multiple files, make parallel \`edit\` calls in one response.
42555
42759
 
42556
42760
  1. **Append** — pass \`path\` + \`appendContent\`
42557
42761
  Appends text to the end of a file, creating it if it does not exist.
@@ -42652,8 +42856,8 @@ function createEditTool(ctx, writeToolName = "write") {
42652
42856
  description: getEditDescription(ctx, writeToolName),
42653
42857
  args: {
42654
42858
  filePath: z7.string().describe("Path to the file to edit (absolute or relative to project root)"),
42655
- symbol: z7.string().optional().describe("Named symbol to replace (function, class, type)"),
42656
- content: z7.string().optional().describe("Replacement content for symbol mode. For whole-file writes, use the `write` tool."),
42859
+ symbol: z7.string().optional().describe("Named symbol to replace; only meaningful together with `content`. A call carrying one without the other is incomplete."),
42860
+ content: z7.string().optional().describe("Replacement content for symbol mode; only meaningful together with `symbol`. A call carrying one without the other is incomplete. For whole-file writes, use the `write` tool."),
42657
42861
  appendContent: z7.string().optional().describe("Text to append to the end of path; creates the file if needed"),
42658
42862
  edits: z7.array(z7.object({
42659
42863
  oldString: z7.string().optional().describe("Text to find for a batch find/replace edit"),
@@ -42895,7 +43099,7 @@ function createDeleteTool(ctx) {
42895
43099
  }
42896
43100
  await runAsk(context.ask({
42897
43101
  permission: "edit",
42898
- patterns: absolutePaths,
43102
+ patterns: resolveRelativePatterns(context, absolutePaths),
42899
43103
  always: ["*"],
42900
43104
  metadata: { action: "delete", count: absolutePaths.length }
42901
43105
  }));
@@ -42948,7 +43152,7 @@ function createMoveTool(ctx) {
42948
43152
  }
42949
43153
  await runAsk(context.ask({
42950
43154
  permission: "edit",
42951
- patterns: [filePath, destPath],
43155
+ patterns: resolveRelativePatterns(context, [filePath, destPath]),
42952
43156
  always: ["*"],
42953
43157
  metadata: { action: "move" }
42954
43158
  }));
@@ -43278,7 +43482,7 @@ function inspectTools(ctx) {
43278
43482
  ` + "Treat `dead_code` as a hint, not proof: reachability is call-based, so symbols reached only via method dispatch or referenced only in type position may be false positives — verify before deleting.\n\n" + "When a list is cut, the reply ends with `shown N of M <unit> (<reason>) · narrow: <knobs>`; absence of that line means the list is complete.",
43279
43483
  args: {
43280
43484
  sections: arg(z9.union([z9.string(), z9.array(z9.string())]).optional().describe("Categories to include in detailed drill-down (e.g. 'todos' or ['todos', 'dead_code', 'cycles']). Use 'all' for every active category. Omit for summary-only mode. `sections` changes detail, not the categories verified.")),
43281
- scope: arg(z9.union([z9.string(), z9.array(z9.string())]).optional().describe("Restrict returned results to paths under this scope (file or directory, absolute or relative to project root). `scope=` narrows results and limits Rust LSP startup to owning Cargo workspaces; it does not trigger per-file diagnostic collection. Scoped files no producer has authoritatively analyzed are reported as named gaps (complete: false), never as a clean empty result.")),
43485
+ scope: arg(z9.union([z9.string(), z9.array(z9.string())]).optional().describe("Restrict returned results to paths under this scope (one path, or an array of paths — not a space-separated list; file or directory; absolute or relative to project root). `scope=` narrows results and limits Rust LSP startup to owning Cargo workspaces; it does not trigger per-file diagnostic collection. Scoped files no producer has authoritatively analyzed are reported as named gaps (complete: false), never as a clean empty result.")),
43282
43486
  topK: arg(z9.number().int().positive().max(100).optional().describe("Max drill-down items per category. Default 20, max 100."))
43283
43487
  },
43284
43488
  execute: async (args, context) => {
@@ -43671,7 +43875,7 @@ function safetyTools(ctx) {
43671
43875
  ` + `- 'undo': Undo the entire last tool call when 'path' is omitted (typical), or undo the last edit to one file when 'path' is provided. Note: pops from the undo stack (irreversible, no redo). Use 'history' to inspect per-file history before undoing.
43672
43876
  ` + `- 'history': List all edit snapshots for a file. Requires 'path'.
43673
43877
  ` + `- 'checkpoint': Save a named snapshot. Explicit 'files' may be untracked or gitignored; omit them to snapshot backup-tracked files. Checkpoints are session-scoped and lost on bridge or daemon restart. Requires 'name'.
43674
- ` + `- 'restore': Restore files to a previously saved checkpoint. Requires 'name'.
43878
+ ` + `- 'restore': Restore files to a previously saved checkpoint. Optional 'path'/'files' scopes the restore; omitted scope restores the whole checkpoint. Requires 'name'.
43675
43879
  ` + `- 'list': List all available named checkpoints. No extra params needed.
43676
43880
 
43677
43881
  ` + `Each op requires specific parameters — see parameter descriptions for requirements.
@@ -43679,9 +43883,9 @@ function safetyTools(ctx) {
43679
43883
  ` + "Use checkpoint before risky multi-file changes. Use undo for quick single-file rollback.",
43680
43884
  args: {
43681
43885
  op: z12.enum(["undo", "history", "checkpoint", "restore", "list"]).describe("Safety operation"),
43682
- path: z12.string().optional().describe("File path (required for history, optional for undo). Absolute or relative to project root"),
43886
+ path: z12.string().optional().describe("File path (required for history, optional for undo, optional for restore — restores only that file). Absolute or relative to project root"),
43683
43887
  name: z12.string().optional().describe("Checkpoint name (required for checkpoint, restore)"),
43684
- files: z12.array(z12.string()).optional().describe("Specific files to include in checkpoint (optional, defaults to backup-tracked files; explicit files may be untracked or gitignored)")
43888
+ files: z12.array(z12.string()).optional().describe("Specific files to include in checkpoint, or to restore from a checkpoint (optional; restore without path/files restores the whole checkpoint; explicit checkpoint files may be untracked or gitignored)")
43685
43889
  },
43686
43890
  execute: async (args, context) => {
43687
43891
  const op = args.op;
@@ -44000,7 +44204,7 @@ function semanticTools(ctx) {
44000
44204
  `),
44001
44205
  args: {
44002
44206
  query: arg3(z13.string().describe("Concept, regex, literal text, filename, or capability to find. Examples: 'fuzzy match with whitespace tolerance', '^export', 'Cargo.lock'.")),
44003
- topK: arg3(optionalInt(1, 100).describe("Number of results (default: 10, max: 100)")),
44207
+ topK: arg3(optionalInt(1, 50).describe("Number of results (default: 10, max: 50)")),
44004
44208
  offset: arg3(optionalInt(0, 1e5).describe("Zero-based result offset (default: 0, max: 100000).")),
44005
44209
  includeTests: arg3(z13.boolean().optional().describe("Include test files (*.test.*, *_test.rs, __tests__/, …) plus test-support, fixture, mock, snapshot, and corpus files. Defaults to false.")),
44006
44210
  path: arg3(z13.string().optional().describe("Only set this to search a different Git project (absolute or ~ path). Omit it for the current configured workspace, including non-Git workspace roots; this is not a subdirectory filter. Unindexed external projects use a bounded lexical fallback."))
@@ -44016,9 +44220,9 @@ function semanticTools(ctx) {
44016
44220
  if (denied)
44017
44221
  return permissionDeniedResponse(denied);
44018
44222
  const rawArgs = { query };
44019
- const topK = coerceOptionalInt(args.topK, "topK", 1, 100);
44223
+ const topK = coerceOptionalInt(args.topK, "topK", 1, 50);
44020
44224
  const offset = coerceOptionalInt(args.offset, "offset", 0, 1e5);
44021
- const includeTests = typeof args.includeTests === "boolean" ? args.includeTests : undefined;
44225
+ const includeTests = !isEmptyParam(args.includeTests) ? coerceBoolean(args.includeTests) : undefined;
44022
44226
  if (topK !== undefined)
44023
44227
  rawArgs.topK = topK;
44024
44228
  if (offset !== undefined)
@@ -44258,11 +44462,11 @@ function throwSentinel(command) {
44258
44462
  throw sentinel;
44259
44463
  }
44260
44464
  var PLUGIN_VERSION = resolvePluginVersion(import.meta.url);
44261
- var ANNOUNCEMENT_VERSION = "0.56.0";
44465
+ var ANNOUNCEMENT_VERSION = "0.57.0";
44262
44466
  var ANNOUNCEMENT_FEATURES = [
44263
- "Callgraph stays consistent under load: edits arriving mid-rebuild can no longer be silently lost from navigation results.",
44264
- "aft_zoom handles real-world .jsonc: files with comment banners above the opening brace now resolve, and not-found errors point at the segment that actually failed (thanks @iceteaSA).",
44265
- "Disk hygiene: stale per-checkout inspect caches from old worktrees are now swept automatically — one machine reclaimed 21 GB."
44467
+ "OpenCode 2 is supported: tools, permission prompts, the sidebar and cancellation all work on the GA host, and an end-to-end matrix guards them.",
44468
+ "`doctor` configures a machine that has only OpenCode 2 installed, and `--fix` no longer skips unrelated repairs when it cannot decide the generation.",
44469
+ "A code-health scan no longer starts while the same project is still building its call graph, which cost about 90 MB of peak memory on large repositories."
44266
44470
  ];
44267
44471
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
44268
44472
  var plugin = async (input) => initializePluginForDirectory(input);