@cortexkit/aft 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.
package/dist/index.js CHANGED
@@ -366,6 +366,7 @@ var init_bridge = __esm(() => {
366
366
  pending = new Map;
367
367
  outstandingBackgroundTaskIds = new Set;
368
368
  nextId = 1;
369
+ processGeneration = 0;
369
370
  stdoutBuffer = "";
370
371
  stdoutReadOffset = 0;
371
372
  stderrBuffer = "";
@@ -678,6 +679,7 @@ var init_bridge = __esm(() => {
678
679
  }
679
680
  }
680
681
  const id = String(this.nextId++);
682
+ const tool = command === "tool_call" && typeof params.name === "string" ? params.name : command;
681
683
  let request;
682
684
  if (command === "cancel_request" || Object.hasOwn(params, "command") || Object.hasOwn(params, "method")) {
683
685
  const nested = { ...params };
@@ -731,14 +733,20 @@ var init_bridge = __esm(() => {
731
733
  return;
732
734
  }
733
735
  entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
734
- this.handleTimeout(requestSessionId);
736
+ this.handleTimeout({
737
+ requestId: id,
738
+ tool: entry.tool,
739
+ generation: entry.generation
740
+ }, requestSessionId);
735
741
  }, effectiveTimeoutMs);
736
742
  const entry = {
737
743
  resolve,
738
744
  reject,
739
745
  timer,
740
746
  onProgress: options?.onProgress,
741
- command
747
+ command,
748
+ tool,
749
+ generation: this.processGeneration
742
750
  };
743
751
  this.pending.set(id, entry);
744
752
  if (!child?.stdin?.writable) {
@@ -1041,6 +1049,7 @@ var init_bridge = __esm(() => {
1041
1049
  this.handleCrash();
1042
1050
  });
1043
1051
  this.process = child;
1052
+ this.processGeneration += 1;
1044
1053
  this.spawnedBinaryFingerprint = readBinaryFingerprint(this.binaryPath);
1045
1054
  this.lastBinaryFingerprintCheckAt = Date.now();
1046
1055
  this.stdoutBuffer = "";
@@ -1212,10 +1221,15 @@ var init_bridge = __esm(() => {
1212
1221
  this.outstandingBackgroundTaskIds.add(taskId);
1213
1222
  }
1214
1223
  }
1215
- handleTimeout(triggeringSessionId) {
1224
+ handleTimeout(trigger, triggeringSessionId) {
1216
1225
  this.consecutiveRequestTimeouts = 0;
1217
1226
  this.spawnedBinaryFingerprint = null;
1218
- this.rejectAllPending(new BridgeTransportUnknownOutcomeError(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
1227
+ const abortedSiblings = Array.from(this.pending, ([requestId, entry]) => ({
1228
+ request_id: requestId,
1229
+ tool: entry.tool
1230
+ }));
1231
+ const attribution = `triggering_request_id=${trigger.requestId} ` + `triggering_tool=${JSON.stringify(trigger.tool)} ` + `bridge_generation=${trigger.generation} ` + `aborted_siblings=${JSON.stringify(abortedSiblings)}`;
1232
+ this.rejectAllPending(new BridgeTransportUnknownOutcomeError(`${this.errorPrefix} bridge killed during sibling timeout — request aborted; ${attribution}`));
1219
1233
  this.outstandingBackgroundTaskIds.clear();
1220
1234
  if (this.process) {
1221
1235
  this.process.kill("SIGKILL");
@@ -1225,17 +1239,11 @@ var init_bridge = __esm(() => {
1225
1239
  this.configured = false;
1226
1240
  const tail = this.formatStderrTail();
1227
1241
  this.stderrTail = [];
1228
- const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${this.getLogFilePathVia()})`;
1229
- if (tail) {
1230
- if (triggeringSessionId) {
1231
- this.sessionErrorVia(triggeringSessionId, killedMsg);
1232
- } else {
1233
- this.errorVia(killedMsg);
1234
- }
1235
- } else if (triggeringSessionId) {
1236
- this.sessionWarnVia(triggeringSessionId, killedMsg);
1242
+ const killedMsg = tail ? `Bridge killed after timeout; ${attribution}.${tail}` : `Bridge killed after timeout; ${attribution} (see ${this.getLogFilePathVia()})`;
1243
+ if (triggeringSessionId) {
1244
+ this.sessionErrorVia(triggeringSessionId, killedMsg);
1237
1245
  } else {
1238
- this.warnVia(killedMsg);
1246
+ this.errorVia(killedMsg);
1239
1247
  }
1240
1248
  }
1241
1249
  handleCrash(cause) {
@@ -2709,11 +2717,34 @@ var init_npm_resolver = __esm(() => {
2709
2717
  };
2710
2718
  });
2711
2719
 
2712
- // ../aft-bridge/dist/onnx-runtime.js
2720
+ // ../aft-bridge/dist/tar-executable.js
2713
2721
  import { execFileSync } from "node:child_process";
2722
+ import { existsSync as existsSync4 } from "node:fs";
2723
+ import { join as join7 } from "node:path";
2724
+ function windowsTarExecutable(options = {}) {
2725
+ const platform = options.platform ?? process.platform;
2726
+ if (platform !== "win32")
2727
+ return "tar";
2728
+ const env = options.env ?? process.env;
2729
+ const systemTar = join7(env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
2730
+ return (options.exists ?? existsSync4)(systemTar) ? systemTar : "tar";
2731
+ }
2732
+ function execTarExtractionSync(args, timeout) {
2733
+ const executable = windowsTarExecutable();
2734
+ try {
2735
+ execFileSync(executable, args, { stdio: "pipe", timeout });
2736
+ } catch (cause) {
2737
+ const detail = cause instanceof Error ? cause.message : String(cause);
2738
+ throw new Error(`tar extraction failed using ${executable}: ${detail}`, { cause });
2739
+ }
2740
+ }
2741
+ var init_tar_executable = () => {};
2742
+
2743
+ // ../aft-bridge/dist/onnx-runtime.js
2744
+ import { execFileSync as execFileSync2 } from "node:child_process";
2714
2745
  import { createHash as createHash3 } from "node:crypto";
2715
- import { chmodSync as chmodSync3, closeSync as closeSync3, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync3, openSync as openSync3, readdirSync as readdirSync3, readFileSync as readFileSync4, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync3, symlinkSync, unlinkSync as unlinkSync3, writeFileSync } from "node:fs";
2716
- import { basename, dirname as dirname3, isAbsolute as isAbsolute3, join as join7, relative, resolve as resolve4, win32 } from "node:path";
2746
+ import { chmodSync as chmodSync3, closeSync as closeSync3, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, openSync as openSync3, readdirSync as readdirSync3, readFileSync as readFileSync4, readlinkSync, realpathSync, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync3, symlinkSync, unlinkSync as unlinkSync3, writeFileSync } from "node:fs";
2747
+ import { basename, dirname as dirname3, isAbsolute as isAbsolute3, join as join8, relative, resolve as resolve4, win32 } from "node:path";
2717
2748
  import { Readable as Readable2 } from "node:stream";
2718
2749
  import { pipeline as pipeline2 } from "node:stream/promises";
2719
2750
  function getPlatformInfo() {
@@ -2725,6 +2756,9 @@ function getPlatformInfo() {
2725
2756
  const ortArch = platformKey ? platformKey.split("-")[1] : process.arch;
2726
2757
  return platformMap[ortArch] || null;
2727
2758
  }
2759
+ function isOrtAutoDownloadSupported() {
2760
+ return getPlatformInfo() !== null;
2761
+ }
2728
2762
  function getManualInstallHint() {
2729
2763
  if (process.platform === "darwin" && process.arch === "x64") {
2730
2764
  return "brew install onnxruntime";
@@ -2736,11 +2770,11 @@ function getManualInstallHint() {
2736
2770
  }
2737
2771
  async function ensureOnnxRuntime(storageDir) {
2738
2772
  const info = getPlatformInfo();
2739
- const ortVersionDir = join7(storageDir, "onnxruntime", ORT_VERSION);
2773
+ const ortVersionDir = join8(storageDir, "onnxruntime", ORT_VERSION);
2740
2774
  const libName = info?.libName ?? "libonnxruntime.dylib";
2741
2775
  const resolvedOrtDir = resolveCachedOnnxRuntimeDir(ortVersionDir, libName);
2742
- const libPath = join7(resolvedOrtDir, libName);
2743
- if (existsSync4(libPath)) {
2776
+ const libPath = join8(resolvedOrtDir, libName);
2777
+ if (existsSync5(libPath)) {
2744
2778
  const meta = readOnnxInstalledMeta(ortVersionDir);
2745
2779
  if (meta?.sha256) {
2746
2780
  try {
@@ -2769,9 +2803,9 @@ async function ensureOnnxRuntime(storageDir) {
2769
2803
  warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
2770
2804
  return null;
2771
2805
  }
2772
- const onnxBaseDir = join7(storageDir, "onnxruntime");
2806
+ const onnxBaseDir = join8(storageDir, "onnxruntime");
2773
2807
  mkdirSync3(onnxBaseDir, { recursive: true });
2774
- const lockPath = join7(onnxBaseDir, ONNX_LOCK_FILE);
2808
+ const lockPath = join8(onnxBaseDir, ONNX_LOCK_FILE);
2775
2809
  cleanupAbandonedStagingDirs(onnxBaseDir);
2776
2810
  if (!acquireLock(lockPath)) {
2777
2811
  warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
@@ -2790,7 +2824,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
2790
2824
  for (const entry of entries) {
2791
2825
  if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
2792
2826
  continue;
2793
- const stagingDir = join7(onnxBaseDir, entry);
2827
+ const stagingDir = join8(onnxBaseDir, entry);
2794
2828
  const parts = entry.split(".");
2795
2829
  const pidStr = parts[parts.length - 2];
2796
2830
  const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
@@ -2827,7 +2861,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
2827
2861
  }
2828
2862
  function cleanupIncompleteTargetIfUnowned(ortDir) {
2829
2863
  try {
2830
- if (existsSync4(ortDir) && !existsSync4(join7(ortDir, ONNX_INSTALLED_META_FILE))) {
2864
+ if (existsSync5(ortDir) && !existsSync5(join8(ortDir, ONNX_INSTALLED_META_FILE))) {
2831
2865
  log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
2832
2866
  rmSync2(ortDir, { recursive: true, force: true });
2833
2867
  }
@@ -2868,8 +2902,8 @@ function detectOnnxVersion(libDir, libName) {
2868
2902
  if (version)
2869
2903
  return version;
2870
2904
  }
2871
- const base = join7(libDir, libName);
2872
- if (existsSync4(base)) {
2905
+ const base = join8(libDir, libName);
2906
+ if (existsSync5(base)) {
2873
2907
  try {
2874
2908
  const real = realpathSync(base);
2875
2909
  const version = parseOnnxVersionFromPath(real) ?? parseOnnxVersionFromDirectoryPath(real);
@@ -2935,10 +2969,10 @@ function directoryContainsLibrary(dir, libName) {
2935
2969
  }
2936
2970
  }
2937
2971
  function resolveCachedOnnxRuntimeDir(ortVersionDir, libName) {
2938
- if (existsSync4(join7(ortVersionDir, libName)))
2972
+ if (existsSync5(join8(ortVersionDir, libName)))
2939
2973
  return ortVersionDir;
2940
- const libSubdir = join7(ortVersionDir, "lib");
2941
- if (existsSync4(join7(libSubdir, libName)))
2974
+ const libSubdir = join8(ortVersionDir, "lib");
2975
+ if (existsSync5(join8(libSubdir, libName)))
2942
2976
  return libSubdir;
2943
2977
  return ortVersionDir;
2944
2978
  }
@@ -2953,13 +2987,13 @@ function findSystemOnnxRuntime(libName) {
2953
2987
  } else if (process.platform === "win32") {
2954
2988
  const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
2955
2989
  const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
2956
- searchPaths.push(join7(programFiles, "onnxruntime", "lib"), join7(programFiles, "Microsoft ONNX Runtime", "lib"), join7(programFiles, "Microsoft Machine Learning", "lib"), join7(programFilesX86, "onnxruntime", "lib"), ...(() => {
2990
+ searchPaths.push(join8(programFiles, "onnxruntime", "lib"), join8(programFiles, "Microsoft ONNX Runtime", "lib"), join8(programFiles, "Microsoft Machine Learning", "lib"), join8(programFilesX86, "onnxruntime", "lib"), ...(() => {
2957
2991
  const nugetPaths = [];
2958
2992
  const userProfile = process.env.USERPROFILE ?? "";
2959
2993
  if (!userProfile)
2960
2994
  return nugetPaths;
2961
- const nugetPackageDir = join7(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
2962
- if (!existsSync4(nugetPackageDir))
2995
+ const nugetPackageDir = join8(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
2996
+ if (!existsSync5(nugetPackageDir))
2963
2997
  return nugetPaths;
2964
2998
  try {
2965
2999
  for (const entry of readdirSync3(nugetPackageDir, { withFileTypes: true })) {
@@ -2967,7 +3001,7 @@ function findSystemOnnxRuntime(libName) {
2967
3001
  continue;
2968
3002
  if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
2969
3003
  continue;
2970
- nugetPaths.push(join7(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join7(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
3004
+ nugetPaths.push(join8(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join8(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
2971
3005
  }
2972
3006
  } catch (err) {
2973
3007
  warn(`Failed to scan NuGet ONNX Runtime cache ${nugetPackageDir}: ${err instanceof Error ? err.message : String(err)}`);
@@ -2989,11 +3023,11 @@ function findSystemOnnxRuntime(libName) {
2989
3023
  });
2990
3024
  const unknownVersionPaths = [];
2991
3025
  for (const dir of uniquePaths) {
2992
- const libPath = join7(dir, libName);
3026
+ const libPath = join8(dir, libName);
2993
3027
  if (process.platform === "win32") {
2994
3028
  if (!directoryContainsLibrary(dir, libName))
2995
3029
  continue;
2996
- } else if (!existsSync4(libPath)) {
3030
+ } else if (!existsSync5(libPath)) {
2997
3031
  continue;
2998
3032
  }
2999
3033
  const version = detectOnnxVersion(dir, libName);
@@ -3059,7 +3093,7 @@ function validateExtractedTree(stagingRoot) {
3059
3093
  const walk = (dir) => {
3060
3094
  const entries = readdirSync3(dir);
3061
3095
  for (const entry of entries) {
3062
- const fullPath = join7(dir, entry);
3096
+ const fullPath = join8(dir, entry);
3063
3097
  const lst = lstatSync(fullPath);
3064
3098
  if (lst.isSymbolicLink()) {
3065
3099
  const linkTarget = readlinkSync(fullPath);
@@ -3092,34 +3126,32 @@ async function downloadOnnxRuntime(info, targetDir) {
3092
3126
  const url = `https://github.com/${ORT_REPO}/releases/download/v${ORT_VERSION}/${info.assetName}.${info.archiveType === "tgz" ? "tgz" : "zip"}`;
3093
3127
  log(`Downloading ONNX Runtime v${ORT_VERSION} for ${process.platform}/${process.arch}...`);
3094
3128
  const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
3129
+ const extractionRoot = join8(tmpDir, "extract");
3130
+ const stagedInstallDir = join8(tmpDir, "install");
3095
3131
  try {
3096
- mkdirSync3(tmpDir, { recursive: true });
3097
- const archivePath = join7(tmpDir, `onnxruntime.${info.archiveType}`);
3132
+ mkdirSync3(extractionRoot, { recursive: true });
3133
+ const archivePath = join8(tmpDir, `onnxruntime.${info.archiveType}`);
3098
3134
  await downloadFileWithCap(url, archivePath);
3099
3135
  const archiveSha256 = sha256File(archivePath);
3100
3136
  log(`ONNX Runtime archive sha256=${archiveSha256}`);
3101
3137
  if (info.archiveType === "tgz") {
3102
- execFileSync("tar", ["xzf", archivePath, "-C", tmpDir], {
3103
- stdio: "pipe",
3104
- timeout: 120000
3105
- });
3138
+ execTarExtractionSync(["xzf", archivePath, "-C", extractionRoot], 120000);
3106
3139
  } else {
3107
- await extractZipArchive(archivePath, tmpDir);
3140
+ await extractZipArchive(archivePath, extractionRoot);
3108
3141
  }
3109
3142
  try {
3110
3143
  unlinkSync3(archivePath);
3111
3144
  } catch {}
3112
- validateExtractedTree(tmpDir);
3113
- const extractedDir = join7(tmpDir, info.assetName, "lib");
3114
- if (!existsSync4(extractedDir)) {
3145
+ validateExtractedTree(extractionRoot);
3146
+ const extractedDir = join8(extractionRoot, info.assetName, "lib");
3147
+ if (!existsSync5(extractedDir)) {
3115
3148
  throw new Error(`Expected directory not found: ${extractedDir}`);
3116
3149
  }
3117
- mkdirSync3(targetDir, { recursive: true });
3118
3150
  const libFiles = readdirSync3(extractedDir).filter((f) => f.startsWith("libonnxruntime") || f.startsWith("onnxruntime"));
3119
3151
  const realFiles = [];
3120
3152
  const symlinks = [];
3121
3153
  for (const libFile of libFiles) {
3122
- const src = join7(extractedDir, libFile);
3154
+ const src = join8(extractedDir, libFile);
3123
3155
  try {
3124
3156
  const stat = lstatSync(src);
3125
3157
  log(`ORT extract: ${libFile} — isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
@@ -3133,15 +3165,11 @@ async function downloadOnnxRuntime(info, targetDir) {
3133
3165
  realFiles.push(libFile);
3134
3166
  }
3135
3167
  }
3136
- copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
3137
- const libPath = join7(targetDir, info.libName);
3138
- let libHash = null;
3139
- try {
3140
- libHash = sha256File(libPath);
3141
- } catch (err) {
3142
- warn(`Could not hash newly-installed ONNX library at ${libPath}: ${err}`);
3143
- }
3144
- writeOnnxInstalledMeta(targetDir, ORT_VERSION, libHash, archiveSha256);
3168
+ copyOnnxLibraries(info, extractedDir, stagedInstallDir, realFiles, symlinks);
3169
+ const libPath = join8(stagedInstallDir, info.libName);
3170
+ const libHash = sha256File(libPath);
3171
+ writeOnnxInstalledMeta(stagedInstallDir, ORT_VERSION, libHash, archiveSha256);
3172
+ publishOnnxRuntime(stagedInstallDir, targetDir);
3145
3173
  rmSync2(tmpDir, { recursive: true, force: true });
3146
3174
  log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
3147
3175
  return targetDir;
@@ -3150,17 +3178,39 @@ async function downloadOnnxRuntime(info, targetDir) {
3150
3178
  try {
3151
3179
  rmSync2(tmpDir, { recursive: true, force: true });
3152
3180
  } catch {}
3153
- try {
3154
- rmSync2(targetDir, { recursive: true, force: true });
3155
- } catch {}
3156
3181
  return null;
3157
3182
  }
3158
3183
  }
3184
+ function publishOnnxRuntime(stagedInstallDir, targetDir) {
3185
+ const backupDir = `${targetDir}.backup.${process.pid}.${Date.now().toString(36)}`;
3186
+ const hadTarget = existsSync5(targetDir);
3187
+ if (hadTarget)
3188
+ renameSync3(targetDir, backupDir);
3189
+ try {
3190
+ renameSync3(stagedInstallDir, targetDir);
3191
+ } catch (installError) {
3192
+ if (hadTarget) {
3193
+ try {
3194
+ renameSync3(backupDir, targetDir);
3195
+ } catch (restoreError) {
3196
+ throw new Error(`ONNX Runtime replacement failed and the previous runtime could not be restored from ${backupDir}: ${restoreError}`, { cause: installError });
3197
+ }
3198
+ }
3199
+ throw installError;
3200
+ }
3201
+ if (hadTarget) {
3202
+ try {
3203
+ rmSync2(backupDir, { recursive: true, force: true });
3204
+ } catch (err) {
3205
+ warn(`Could not remove replaced ONNX Runtime backup at ${backupDir}: ${err}`);
3206
+ }
3207
+ }
3208
+ }
3159
3209
  function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync3) {
3160
3210
  const requiredLibs = new Set([info.libName]);
3161
3211
  for (const libFile of realFiles) {
3162
- const src = join7(extractedDir, libFile);
3163
- const dst = join7(targetDir, libFile);
3212
+ const src = join8(extractedDir, libFile);
3213
+ const dst = join8(targetDir, libFile);
3164
3214
  try {
3165
3215
  copyFile(src, dst);
3166
3216
  if (process.platform !== "win32") {
@@ -3176,11 +3226,11 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
3176
3226
  }
3177
3227
  const targetRoot = realpathSync(targetDir);
3178
3228
  for (const link of symlinks) {
3179
- const dst = join7(targetDir, link.name);
3229
+ const dst = join8(targetDir, link.name);
3180
3230
  try {
3181
3231
  unlinkSync3(dst);
3182
3232
  } catch {}
3183
- const dstForContainment = join7(targetRoot, link.name);
3233
+ const dstForContainment = join8(targetRoot, link.name);
3184
3234
  const resolvedTarget = resolve4(dirname3(dstForContainment), link.target);
3185
3235
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
3186
3236
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
@@ -3201,21 +3251,18 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
3201
3251
  log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
3202
3252
  }
3203
3253
  }
3204
- const requiredPath = join7(targetDir, info.libName);
3205
- if (!existsSync4(requiredPath)) {
3254
+ const requiredPath = join8(targetDir, info.libName);
3255
+ if (!existsSync5(requiredPath)) {
3206
3256
  rmSync2(targetDir, { recursive: true, force: true });
3207
3257
  throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
3208
3258
  }
3209
3259
  }
3210
3260
  async function extractZipArchive(archivePath, destinationDir) {
3211
3261
  if (process.platform === "win32") {
3212
- execFileSync("tar.exe", ["-xf", archivePath, "-C", destinationDir], {
3213
- stdio: "pipe",
3214
- timeout: 120000
3215
- });
3262
+ execTarExtractionSync(["-xf", archivePath, "-C", destinationDir], 120000);
3216
3263
  return;
3217
3264
  }
3218
- execFileSync("unzip", ["-q", archivePath, "-d", destinationDir], {
3265
+ execFileSync2("unzip", ["-q", archivePath, "-d", destinationDir], {
3219
3266
  stdio: "pipe",
3220
3267
  timeout: 120000
3221
3268
  });
@@ -3228,13 +3275,13 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
3228
3275
  ...sha256 ? { sha256 } : {},
3229
3276
  archiveSha256
3230
3277
  };
3231
- writeFileSync(join7(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
3278
+ writeFileSync(join8(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
3232
3279
  } catch (err) {
3233
3280
  log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
3234
3281
  }
3235
3282
  }
3236
3283
  function readOnnxInstalledMeta(installDir) {
3237
- const path = join7(installDir, ONNX_INSTALLED_META_FILE);
3284
+ const path = join8(installDir, ONNX_INSTALLED_META_FILE);
3238
3285
  try {
3239
3286
  if (!statSync3(path).isFile())
3240
3287
  return null;
@@ -3346,7 +3393,7 @@ function isWindowsProcessAlive(pid) {
3346
3393
  if (!Number.isInteger(pid) || pid <= 0)
3347
3394
  return false;
3348
3395
  try {
3349
- const output = execFileSync("tasklist.exe", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
3396
+ const output = execFileSync2("tasklist.exe", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
3350
3397
  encoding: "utf8",
3351
3398
  timeout: 2000,
3352
3399
  windowsHide: true
@@ -3374,6 +3421,7 @@ var ORT_VERSION = "1.24.4", ORT_REPO = "microsoft/onnxruntime", MAX_DOWNLOAD_BYT
3374
3421
  var init_onnx_runtime = __esm(() => {
3375
3422
  init_active_logger();
3376
3423
  init_platform();
3424
+ init_tar_executable();
3377
3425
  MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
3378
3426
  MAX_EXTRACT_BYTES = 1 * 1024 * 1024 * 1024;
3379
3427
  STALE_LOCK_MS = 5 * 60 * 1000;
@@ -3434,6 +3482,14 @@ var init_path_aliases = __esm(() => {
3434
3482
  ]);
3435
3483
  MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
3436
3484
  });
3485
+
3486
+ // ../aft-bridge/dist/semantic-status.js
3487
+ var SEMANTIC_PROGRESS_STATUSES, SEMANTIC_READY_STATUSES, SEMANTIC_INACTIVE_STATUSES;
3488
+ var init_semantic_status = __esm(() => {
3489
+ SEMANTIC_PROGRESS_STATUSES = new Set(["building", "loading"]);
3490
+ SEMANTIC_READY_STATUSES = new Set(["ready", "empty"]);
3491
+ SEMANTIC_INACTIVE_STATUSES = new Set(["disabled", "busy"]);
3492
+ });
3437
3493
  // ../aft-bridge/dist/index.js
3438
3494
  var init_dist2 = __esm(() => {
3439
3495
  init_active_logger();
@@ -3459,8 +3515,10 @@ var init_dist2 = __esm(() => {
3459
3515
  init_project_identity();
3460
3516
  init_resolver();
3461
3517
  init_revivable_transport();
3518
+ init_semantic_status();
3462
3519
  init_storage_paths();
3463
3520
  init_subc_transport();
3521
+ init_tar_executable();
3464
3522
  init_transport_factory();
3465
3523
  });
3466
3524
 
@@ -3477,10 +3535,10 @@ var init_paths2 = __esm(() => {
3477
3535
 
3478
3536
  // src/lib/binary-probe.ts
3479
3537
  import { execSync, spawnSync as spawnSync3 } from "node:child_process";
3480
- import { existsSync as existsSync5 } from "node:fs";
3538
+ import { existsSync as existsSync6, readdirSync as readdirSync4 } from "node:fs";
3481
3539
  import { createRequire } from "node:module";
3482
3540
  import { homedir as homedir6 } from "node:os";
3483
- import { join as join8 } from "node:path";
3541
+ import { join as join9 } from "node:path";
3484
3542
  async function loadPluginVersion() {
3485
3543
  try {
3486
3544
  const bridge = await Promise.resolve().then(() => (init_dist2(), {}));
@@ -3532,7 +3590,7 @@ function probeAftBinary(preferredVersion) {
3532
3590
  const candidates = [];
3533
3591
  for (const candidate of aftBinaryCandidates(preferredVersion)) {
3534
3592
  try {
3535
- if (!existsSync5(candidate))
3593
+ if (!existsSync6(candidate))
3536
3594
  continue;
3537
3595
  const result = spawnSync3(candidate, ["--version"], {
3538
3596
  stdio: ["ignore", "pipe", "pipe"],
@@ -3583,7 +3641,7 @@ function pushCandidate(candidates, candidate) {
3583
3641
  function firstExisting(candidates) {
3584
3642
  for (const candidate of candidates) {
3585
3643
  try {
3586
- if (!existsSync5(candidate))
3644
+ if (!existsSync6(candidate))
3587
3645
  continue;
3588
3646
  return candidate;
3589
3647
  } catch {}
@@ -3598,52 +3656,148 @@ function platformKey2(platform = process.platform, arch = process.arch) {
3598
3656
  };
3599
3657
  return table[platform]?.[arch] ?? null;
3600
3658
  }
3601
- function aftBinaryCandidates(preferredVersion) {
3602
- const candidates = [];
3659
+ function newestCachedBinary(cacheDir, binaryName) {
3660
+ let entries;
3661
+ try {
3662
+ entries = readdirSync4(cacheDir);
3663
+ } catch {
3664
+ return null;
3665
+ }
3666
+ let best = null;
3667
+ for (const entry of entries) {
3668
+ const version = entry.match(CACHE_VERSION_DIR)?.[1];
3669
+ if (!version)
3670
+ continue;
3671
+ const path = join9(cacheDir, entry, binaryName);
3672
+ try {
3673
+ if (!existsSync6(path))
3674
+ continue;
3675
+ } catch {
3676
+ continue;
3677
+ }
3678
+ if (!best || compareSemver(version, best.version) > 0)
3679
+ best = { version, path };
3680
+ }
3681
+ return best?.path ?? null;
3682
+ }
3683
+ function cacheLocation(preferredVersion) {
3684
+ const cacheDir = getAftBinaryCacheDir();
3685
+ const binaryName = getAftBinaryName();
3686
+ const label = "binary cache";
3687
+ const installedHere = "`aft doctor --fix` installs here";
3603
3688
  if (preferredVersion) {
3604
3689
  const tag = preferredVersion.startsWith("v") ? preferredVersion : `v${preferredVersion}`;
3605
- pushCandidate(candidates, join8(getAftBinaryCacheDir(), tag, getAftBinaryName()));
3690
+ return {
3691
+ label,
3692
+ paths: [join9(cacheDir, tag, binaryName)],
3693
+ emptyReason: `no ${tag}/${binaryName} under ${cacheDir} (${installedHere})`
3694
+ };
3606
3695
  }
3696
+ const newest = newestCachedBinary(cacheDir, binaryName);
3697
+ return {
3698
+ label,
3699
+ paths: newest ? [newest] : [],
3700
+ emptyReason: `no v<version>/${binaryName} under ${cacheDir} (${installedHere})`
3701
+ };
3702
+ }
3703
+ function platformPackageLocation() {
3704
+ const label = "npm platform package";
3607
3705
  const key = platformKey2();
3608
- if (key) {
3609
- try {
3610
- const require2 = createRequire(import.meta.url);
3611
- pushCandidate(candidates, require2.resolve(`@cortexkit/aft-${key}/bin/${getAftBinaryName()}`));
3612
- } catch {}
3706
+ if (!key) {
3707
+ return {
3708
+ label,
3709
+ paths: [],
3710
+ emptyReason: `no package published for ${process.platform}-${process.arch}`
3711
+ };
3712
+ }
3713
+ const packageName = `@cortexkit/aft-${key}`;
3714
+ try {
3715
+ const require2 = createRequire(import.meta.url);
3716
+ return {
3717
+ label,
3718
+ paths: [require2.resolve(`${packageName}/bin/${getAftBinaryName()}`)],
3719
+ emptyReason: `${packageName} not installed`
3720
+ };
3721
+ } catch {
3722
+ return { label, paths: [], emptyReason: `${packageName} not installed` };
3613
3723
  }
3724
+ }
3725
+ function pathLocation() {
3726
+ const lookup = process.platform === "win32" ? "where aft" : "which aft";
3727
+ const label = "PATH";
3728
+ let hits = [];
3614
3729
  try {
3615
- const lookup = process.platform === "win32" ? "where aft" : "which aft";
3616
3730
  const resolved = execSync(lookup, {
3617
3731
  stdio: "pipe",
3618
3732
  encoding: "utf-8",
3619
3733
  env: process.env
3620
3734
  }).trim();
3621
- for (const line of resolved.split(/\r?\n/)) {
3622
- const candidate = line.trim();
3623
- if (candidate && isNativeExecutable(candidate)) {
3624
- pushCandidate(candidates, candidate);
3625
- }
3626
- }
3735
+ hits = resolved.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
3627
3736
  } catch {}
3628
- pushCandidate(candidates, join8(homedir6(), ".cargo", "bin", getAftBinaryName()));
3737
+ const native = hits.filter((candidate) => isNativeExecutable(candidate));
3738
+ return {
3739
+ label,
3740
+ paths: native,
3741
+ emptyReason: hits.length > 0 ? `only non-native \`aft\` entries on PATH (${hits.join(", ")}), skipped because running them would re-enter this CLI` : `no \`aft\` on PATH (\`${lookup}\` found nothing)`
3742
+ };
3743
+ }
3744
+ function cargoLocation() {
3745
+ const path = join9(homedir6(), ".cargo", "bin", getAftBinaryName());
3746
+ return { label: "cargo install", paths: [path], emptyReason: `no ${path}` };
3747
+ }
3748
+ function aftBinarySearchLocations(preferredVersion) {
3749
+ return [
3750
+ cacheLocation(preferredVersion),
3751
+ platformPackageLocation(),
3752
+ pathLocation(),
3753
+ cargoLocation()
3754
+ ];
3755
+ }
3756
+ function aftBinaryCandidates(preferredVersion) {
3757
+ const candidates = [];
3758
+ for (const location of aftBinarySearchLocations(preferredVersion)) {
3759
+ for (const path of location.paths)
3760
+ pushCandidate(candidates, path);
3761
+ }
3629
3762
  return candidates;
3630
3763
  }
3764
+ function describeAftBinarySearch(preferredVersion) {
3765
+ return aftBinarySearchLocations(preferredVersion).map((location) => {
3766
+ const found = location.paths.filter((path) => {
3767
+ try {
3768
+ return existsSync6(path);
3769
+ } catch {
3770
+ return false;
3771
+ }
3772
+ });
3773
+ return `${location.label}: ${found.length > 0 ? found.join(", ") : location.emptyReason}`;
3774
+ });
3775
+ }
3776
+ function missingAftBinaryMessage(command, preferredVersion) {
3777
+ return [
3778
+ `${command} requires a native AFT binary and none was found. Searched:`,
3779
+ ...describeAftBinarySearch(preferredVersion).map((line) => ` - ${line}`),
3780
+ "`aft doctor --fix` installs into the binary cache directory named above; if it already reported success, that line is the directory this command searched — AFT_CACHE_DIR and XDG_CACHE_HOME change it."
3781
+ ].join(`
3782
+ `);
3783
+ }
3631
3784
  function findAftBinary(preferredVersion) {
3632
3785
  return firstExisting(aftBinaryCandidates(preferredVersion));
3633
3786
  }
3634
- var PLUGIN_VERSION, VERSION_LINE;
3787
+ var PLUGIN_VERSION, VERSION_LINE, CACHE_VERSION_DIR;
3635
3788
  var init_binary_probe = __esm(async () => {
3636
3789
  init_dist2();
3637
3790
  init_paths2();
3638
3791
  PLUGIN_VERSION = await loadPluginVersion();
3639
3792
  VERSION_LINE = /^(?:aft\s+)?v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/i;
3793
+ CACHE_VERSION_DIR = /^v(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/;
3640
3794
  });
3641
3795
 
3642
3796
  // src/lib/fs-util.ts
3643
- import { existsSync as existsSync6, readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
3644
- import { join as join9 } from "node:path";
3797
+ import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
3798
+ import { join as join10 } from "node:path";
3645
3799
  function dirSize(path) {
3646
- if (!existsSync6(path)) {
3800
+ if (!existsSync7(path)) {
3647
3801
  return 0;
3648
3802
  }
3649
3803
  const stat = statSync4(path);
@@ -3654,8 +3808,8 @@ function dirSize(path) {
3654
3808
  return 0;
3655
3809
  }
3656
3810
  let total = 0;
3657
- for (const entry of readdirSync4(path)) {
3658
- total += dirSize(join9(path, entry));
3811
+ for (const entry of readdirSync5(path)) {
3812
+ total += dirSize(join10(path, entry));
3659
3813
  }
3660
3814
  return total;
3661
3815
  }
@@ -3672,9 +3826,9 @@ function compareVersionLabels(a, b) {
3672
3826
  var init_fs_util = () => {};
3673
3827
 
3674
3828
  // src/lib/omp-paths.ts
3675
- import { existsSync as existsSync7 } from "node:fs";
3829
+ import { existsSync as existsSync8 } from "node:fs";
3676
3830
  import { homedir as homedir7 } from "node:os";
3677
- import { join as join10, resolve as resolve5 } from "node:path";
3831
+ import { join as join11, resolve as resolve5 } from "node:path";
3678
3832
  function envFirstHomeDir() {
3679
3833
  return process.env.HOME?.trim() || homedir7();
3680
3834
  }
@@ -3682,9 +3836,9 @@ function resolveOmpPaths() {
3682
3836
  const normalizedProfile = (process.env.OMP_PROFILE ?? process.env.PI_PROFILE)?.trim();
3683
3837
  const profile = normalizedProfile && normalizedProfile !== "default" && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(normalizedProfile) ? normalizedProfile : undefined;
3684
3838
  const configDirName = process.env.PI_CONFIG_DIR?.trim() || ".omp";
3685
- const baseConfigRoot = join10(envFirstHomeDir(), configDirName);
3686
- const configRoot = profile ? join10(baseConfigRoot, "profiles", profile) : baseConfigRoot;
3687
- const defaultAgentDir = join10(configRoot, "agent");
3839
+ const baseConfigRoot = join11(envFirstHomeDir(), configDirName);
3840
+ const configRoot = profile ? join11(baseConfigRoot, "profiles", profile) : baseConfigRoot;
3841
+ const defaultAgentDir = join11(configRoot, "agent");
3688
3842
  const configuredAgentDir = profile ? undefined : process.env.PI_CODING_AGENT_DIR?.trim();
3689
3843
  const agentDir = configuredAgentDir ? resolve5(configuredAgentDir) : defaultAgentDir;
3690
3844
  const canUseXdg = (process.platform === "linux" || process.platform === "darwin") && agentDir === defaultAgentDir;
@@ -3692,9 +3846,9 @@ function resolveOmpPaths() {
3692
3846
  if (canUseXdg) {
3693
3847
  const xdgDataHome = process.env.XDG_DATA_HOME?.trim();
3694
3848
  if (xdgDataHome) {
3695
- const appRoot = join10(xdgDataHome, "omp");
3696
- const candidate = profile ? join10(appRoot, "profiles", profile) : appRoot;
3697
- if (existsSync7(candidate))
3849
+ const appRoot = join11(xdgDataHome, "omp");
3850
+ const candidate = profile ? join11(appRoot, "profiles", profile) : appRoot;
3851
+ if (existsSync8(candidate))
3698
3852
  dataRoot = candidate;
3699
3853
  }
3700
3854
  }
@@ -3704,8 +3858,8 @@ function resolveOmpPaths() {
3704
3858
  agentDir,
3705
3859
  dataRoot,
3706
3860
  dataAgentRoot,
3707
- pluginsDir: join10(dataRoot, "plugins"),
3708
- sessionsRoot: join10(dataAgentRoot, "sessions")
3861
+ pluginsDir: join11(dataRoot, "plugins"),
3862
+ sessionsRoot: join11(dataAgentRoot, "sessions")
3709
3863
  };
3710
3864
  }
3711
3865
  function getOmpAgentDir() {
@@ -3715,7 +3869,7 @@ function getOmpPluginsDir() {
3715
3869
  return resolveOmpPaths().pluginsDir;
3716
3870
  }
3717
3871
  function getOmpPluginsLockPath() {
3718
- return join10(getOmpPluginsDir(), "omp-plugins.lock.json");
3872
+ return join11(getOmpPluginsDir(), "omp-plugins.lock.json");
3719
3873
  }
3720
3874
  function getOmpSessionsRoot() {
3721
3875
  return resolveOmpPaths().sessionsRoot;
@@ -3735,12 +3889,12 @@ var init_omp_paths = () => {};
3735
3889
 
3736
3890
  // src/lib/omp-helpers.ts
3737
3891
  import { spawnSync as spawnSync4 } from "node:child_process";
3738
- import { accessSync, constants, existsSync as existsSync8, readFileSync as readFileSync5, statSync as statSync5 } from "node:fs";
3892
+ import { accessSync, constants, existsSync as existsSync9, readFileSync as readFileSync5, statSync as statSync5 } from "node:fs";
3739
3893
  import { homedir as homedir8 } from "node:os";
3740
- import { delimiter, extname, join as join11, resolve as resolve6 } from "node:path";
3894
+ import { delimiter, extname, join as join12, resolve as resolve6 } from "node:path";
3741
3895
  function isExecutableFile(path, platform = process.platform) {
3742
3896
  try {
3743
- if (!existsSync8(path) || !statSync5(path).isFile())
3897
+ if (!existsSync9(path) || !statSync5(path).isFile())
3744
3898
  return false;
3745
3899
  accessSync(path, platform === "win32" ? constants.F_OK : constants.X_OK);
3746
3900
  return true;
@@ -3769,11 +3923,11 @@ function detectOmpPackageCli() {
3769
3923
  if (!packageDir || !findOnPath("bun"))
3770
3924
  return null;
3771
3925
  try {
3772
- const manifest = JSON.parse(readFileSync5(join11(packageDir, "package.json"), "utf8"));
3926
+ const manifest = JSON.parse(readFileSync5(join12(packageDir, "package.json"), "utf8"));
3773
3927
  if (manifest.name !== OMP_HOST_PACKAGE)
3774
3928
  return null;
3775
- const cli = join11(packageDir, "dist", "cli.js");
3776
- return existsSync8(cli) ? cli : null;
3929
+ const cli = join12(packageDir, "dist", "cli.js");
3930
+ return existsSync9(cli) ? cli : null;
3777
3931
  } catch {
3778
3932
  return null;
3779
3933
  }
@@ -3793,13 +3947,13 @@ function getOmpCommandInvocation(ompPath, args) {
3793
3947
  }
3794
3948
  function getOmpFallbackCandidates(platform, home, appData) {
3795
3949
  if (platform !== "win32") {
3796
- return [join11(home, ".bun", "bin", "omp"), join11(home, ".local", "bin", "omp")];
3950
+ return [join12(home, ".bun", "bin", "omp"), join12(home, ".local", "bin", "omp")];
3797
3951
  }
3798
3952
  const npmRoot = appData?.trim();
3799
3953
  return [
3800
- ...npmRoot ? [join11(npmRoot, "npm", "omp.cmd"), join11(npmRoot, "npm", "omp.exe")] : [],
3801
- join11(home, ".bun", "bin", "omp.exe"),
3802
- join11(home, ".bun", "bin", "omp.cmd")
3954
+ ...npmRoot ? [join12(npmRoot, "npm", "omp.cmd"), join12(npmRoot, "npm", "omp.exe")] : [],
3955
+ join12(home, ".bun", "bin", "omp.exe"),
3956
+ join12(home, ".bun", "bin", "omp.cmd")
3803
3957
  ];
3804
3958
  }
3805
3959
  function detectOmpBinary() {
@@ -3880,8 +4034,8 @@ var init_omp_helpers = __esm(() => {
3880
4034
  });
3881
4035
 
3882
4036
  // src/adapters/omp.ts
3883
- import { existsSync as existsSync9, readFileSync as readFileSync6 } from "node:fs";
3884
- import { join as join12 } from "node:path";
4037
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "node:fs";
4038
+ import { join as join13 } from "node:path";
3885
4039
 
3886
4040
  class OmpAdapter {
3887
4041
  kind = "omp";
@@ -3906,9 +4060,9 @@ class OmpAdapter {
3906
4060
  return {
3907
4061
  configDir,
3908
4062
  harnessConfig,
3909
- harnessConfigFormat: existsSync9(harnessConfig) ? "json" : "none",
4063
+ harnessConfigFormat: existsSync10(harnessConfig) ? "json" : "none",
3910
4064
  aftConfig,
3911
- aftConfigFormat: existsSync9(aftConfig) ? "jsonc" : "none"
4065
+ aftConfigFormat: existsSync10(aftConfig) ? "jsonc" : "none"
3912
4066
  };
3913
4067
  }
3914
4068
  hasPluginEntry() {
@@ -3999,7 +4153,7 @@ class OmpAdapter {
3999
4153
  path,
4000
4154
  cached: plugin?.version,
4001
4155
  latest: undefined,
4002
- exists: Boolean(plugin) || existsSync9(path)
4156
+ exists: Boolean(plugin) || existsSync10(path)
4003
4157
  };
4004
4158
  }
4005
4159
  getStorageDir() {
@@ -4023,12 +4177,12 @@ class OmpAdapter {
4023
4177
  describeStorageSubtrees() {
4024
4178
  const storage = this.getStorageDir();
4025
4179
  return {
4026
- index: dirSize(join12(storage, "index")),
4027
- semantic: dirSize(join12(storage, "semantic")),
4028
- backups: dirSize(join12(storage, "backups")),
4029
- url_cache: dirSize(join12(storage, "url_cache")),
4030
- onnxruntime: dirSize(join12(storage, "onnxruntime")),
4031
- logs: dirSize(join12(storage, "logs"))
4180
+ index: dirSize(join13(storage, "index")),
4181
+ semantic: dirSize(join13(storage, "semantic")),
4182
+ backups: dirSize(join13(storage, "backups")),
4183
+ url_cache: dirSize(join13(storage, "url_cache")),
4184
+ onnxruntime: dirSize(join13(storage, "onnxruntime")),
4185
+ logs: dirSize(join13(storage, "logs"))
4032
4186
  };
4033
4187
  }
4034
4188
  readRuntimeEnabled(configPath) {
@@ -11822,21 +11976,21 @@ var require_src2 = __commonJS(function(exports, module) {
11822
11976
  });
11823
11977
 
11824
11978
  // src/lib/jsonc.ts
11825
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
11979
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
11826
11980
  import { dirname as dirname4 } from "node:path";
11827
11981
  function detectJsoncFile(configDir, baseName) {
11828
11982
  const jsoncPath = `${configDir}/${baseName}.jsonc`;
11829
11983
  const jsonPath = `${configDir}/${baseName}.json`;
11830
- if (existsSync10(jsoncPath)) {
11984
+ if (existsSync11(jsoncPath)) {
11831
11985
  return { path: jsoncPath, format: "jsonc" };
11832
11986
  }
11833
- if (existsSync10(jsonPath)) {
11987
+ if (existsSync11(jsonPath)) {
11834
11988
  return { path: jsonPath, format: "json" };
11835
11989
  }
11836
11990
  return { path: jsonPath, format: "none" };
11837
11991
  }
11838
11992
  function readJsoncFile(path) {
11839
- if (!existsSync10(path)) {
11993
+ if (!existsSync11(path)) {
11840
11994
  return { value: null };
11841
11995
  }
11842
11996
  try {
@@ -11857,7 +12011,7 @@ function writeJsoncFile(path, value, format = "json") {
11857
12011
  `);
11858
12012
  }
11859
12013
  function ensureAftSchemaUrl(path, format) {
11860
- const existed = existsSync10(path);
12014
+ const existed = existsSync11(path);
11861
12015
  if (!existed) {
11862
12016
  const writeFormat = format === "jsonc" ? "jsonc" : "json";
11863
12017
  writeJsoncFile(path, { $schema: AFT_SCHEMA_URL }, writeFormat);
@@ -11914,62 +12068,130 @@ var opencode_version_default = `1.18.30
11914
12068
  var init_opencode_version = () => {};
11915
12069
 
11916
12070
  // src/setup/opencode-config.ts
12071
+ function openCodePluginKey(generation) {
12072
+ return generation === "v2" ? "plugins" : "plugin";
12073
+ }
12074
+ function otherOpenCodePluginKey(key) {
12075
+ return key === "plugins" ? "plugin" : "plugins";
12076
+ }
12077
+ function openCodePluginReadKeys(generation) {
12078
+ if (generation === "v1")
12079
+ return ["plugin"];
12080
+ if (generation === "v2")
12081
+ return ["plugins"];
12082
+ return ["plugin", "plugins"];
12083
+ }
11917
12084
  function pinnedPluginEntry(version) {
11918
12085
  return `${AFT_OPENCODE_PACKAGE}@${version}`;
11919
12086
  }
11920
12087
  function isAftNpmEntry(entry) {
11921
12088
  return typeof entry === "string" && (entry === AFT_OPENCODE_PACKAGE || entry.startsWith(`${AFT_OPENCODE_PACKAGE}@`));
11922
12089
  }
11923
- function ensurePinnedPluginConfig(value, version, hasLocalAftEntry = () => false) {
12090
+ function isPackageObjectEntry(entry) {
12091
+ return typeof entry === "object" && entry !== null && !Array.isArray(entry) && typeof entry.package === "string";
12092
+ }
12093
+ function isPackageTupleEntry(entry) {
12094
+ return Array.isArray(entry) && typeof entry[0] === "string";
12095
+ }
12096
+ function pluginEntryPackage(entry) {
12097
+ if (typeof entry === "string")
12098
+ return entry;
12099
+ if (isPackageTupleEntry(entry))
12100
+ return entry[0];
12101
+ if (isPackageObjectEntry(entry))
12102
+ return entry.package;
12103
+ return null;
12104
+ }
12105
+ function pluginEntryOptions(entry) {
12106
+ if (isPackageTupleEntry(entry))
12107
+ return entry.length > 1 ? entry[1] : undefined;
12108
+ if (isPackageObjectEntry(entry))
12109
+ return entry.options;
12110
+ return;
12111
+ }
12112
+ function pluginEntryFitsKey(entry, key) {
12113
+ if (typeof entry === "string")
12114
+ return true;
12115
+ if (isPackageTupleEntry(entry))
12116
+ return key === "plugin";
12117
+ if (isPackageObjectEntry(entry))
12118
+ return key === "plugins";
12119
+ return false;
12120
+ }
12121
+ function shapePluginEntry(key, packageSpec, options) {
12122
+ if (options === undefined)
12123
+ return packageSpec;
12124
+ return key === "plugins" ? { package: packageSpec, options } : [packageSpec, options];
12125
+ }
12126
+ function sameEntry(left, right) {
12127
+ return JSON.stringify(left) === JSON.stringify(right);
12128
+ }
12129
+ function findLocalAftEntry(list, hasLocalAftEntry) {
12130
+ for (let index = 0;index < list.length; index += 1) {
12131
+ const packageSpec = pluginEntryPackage(list[index]);
12132
+ if (packageSpec === null || isAftNpmEntry(packageSpec))
12133
+ continue;
12134
+ if (hasLocalAftEntry(packageSpec)) {
12135
+ return { index, packageSpec, options: pluginEntryOptions(list[index]) };
12136
+ }
12137
+ }
12138
+ return null;
12139
+ }
12140
+ function ensurePinnedPluginConfig(value, version, hasLocalAftEntry = () => false, generation = "unknown") {
11924
12141
  const entry = pinnedPluginEntry(version);
11925
- const singular = Array.isArray(value.plugin) ? value.plugin : [];
11926
- const plural = Array.isArray(value.plugins) ? value.plugins : [];
11927
- const hadPluralKey = Object.hasOwn(value, "plugins");
11928
- if (!Array.isArray(value.plugin))
11929
- value.plugin = singular;
11930
- if (plural !== singular)
11931
- singular.push(...plural);
11932
- if (hadPluralKey)
11933
- delete value.plugins;
12142
+ const key = openCodePluginKey(generation);
12143
+ const hadKey = Array.isArray(value[key]);
12144
+ const list = hadKey ? value[key] : [];
12145
+ if (!hadKey)
12146
+ value[key] = list;
11934
12147
  const npmIndexes = [];
11935
- let hasLocalEntry = false;
11936
- for (let index = 0;index < singular.length; index += 1) {
11937
- const candidate = singular[index];
11938
- if (isAftNpmEntry(candidate))
12148
+ for (let index = 0;index < list.length; index += 1) {
12149
+ const packageSpec = pluginEntryPackage(list[index]);
12150
+ if (packageSpec !== null && isAftNpmEntry(packageSpec))
11939
12151
  npmIndexes.push(index);
11940
- else if (typeof candidate === "string" && hasLocalAftEntry(candidate))
11941
- hasLocalEntry = true;
11942
12152
  }
11943
12153
  if (npmIndexes.length > 0) {
11944
12154
  const firstIndex = npmIndexes[0];
11945
- const wasExact = singular[firstIndex] === entry;
11946
- singular[firstIndex] = entry;
12155
+ const shaped = shapePluginEntry(key, entry, pluginEntryOptions(list[firstIndex]));
12156
+ const wasExact = sameEntry(list[firstIndex], shaped);
12157
+ list[firstIndex] = shaped;
11947
12158
  for (let index = npmIndexes.length - 1;index >= 1; index -= 1) {
11948
- singular.splice(npmIndexes[index], 1);
11949
- }
11950
- const changed = hadPluralKey || !wasExact || npmIndexes.length > 1;
11951
- return { action: changed ? "updated" : "already_present", changed, entry };
11952
- }
11953
- if (hasLocalEntry) {
11954
- return {
11955
- action: hadPluralKey ? "updated" : "already_present",
11956
- changed: hadPluralKey,
11957
- entry
11958
- };
11959
- }
11960
- singular.push(entry);
11961
- return { action: "added", changed: true, entry };
11962
- }
11963
- function pluginConfigNeedsUpdate(value, version, hasLocalAftEntry = () => false) {
11964
- if (Object.hasOwn(value, "plugins"))
11965
- return true;
11966
- if (!Array.isArray(value.plugin))
12159
+ list.splice(npmIndexes[index], 1);
12160
+ }
12161
+ const changed = !wasExact || npmIndexes.length > 1;
12162
+ return { action: changed ? "updated" : "already_present", changed, entry, key };
12163
+ }
12164
+ const local = findLocalAftEntry(list, hasLocalAftEntry);
12165
+ if (local) {
12166
+ const shaped = shapePluginEntry(key, local.packageSpec, local.options);
12167
+ const changed = !sameEntry(list[local.index], shaped);
12168
+ list[local.index] = shaped;
12169
+ return { action: changed ? "updated" : "already_present", changed, entry, key };
12170
+ }
12171
+ const sibling = value[otherOpenCodePluginKey(key)];
12172
+ const siblingList = Array.isArray(sibling) ? sibling : null;
12173
+ const carried = siblingList ? findLocalAftEntry(siblingList, hasLocalAftEntry) : null;
12174
+ list.push(carried ? shapePluginEntry(key, carried.packageSpec, carried.options) : entry);
12175
+ if (carried && siblingList)
12176
+ siblingList.splice(carried.index, 1);
12177
+ return { action: "added", changed: true, entry, key };
12178
+ }
12179
+ function pluginConfigNeedsUpdate(value, version, hasLocalAftEntry = () => false, generation = "unknown") {
12180
+ const key = openCodePluginKey(generation);
12181
+ const list = value[key];
12182
+ if (!Array.isArray(list))
11967
12183
  return true;
11968
- const npmEntries = value.plugin.filter(isAftNpmEntry);
12184
+ const npmEntries = list.filter((candidate) => {
12185
+ const packageSpec = pluginEntryPackage(candidate);
12186
+ return packageSpec !== null && isAftNpmEntry(packageSpec);
12187
+ });
11969
12188
  if (npmEntries.length > 0) {
11970
- return npmEntries.length !== 1 || npmEntries[0] !== pinnedPluginEntry(version);
12189
+ return npmEntries.length !== 1 || pluginEntryPackage(npmEntries[0]) !== pinnedPluginEntry(version) || !pluginEntryFitsKey(npmEntries[0], key);
11971
12190
  }
11972
- return !value.plugin.some((entry) => typeof entry === "string" && hasLocalAftEntry(entry));
12191
+ const local = findLocalAftEntry(list, hasLocalAftEntry);
12192
+ if (local)
12193
+ return !pluginEntryFitsKey(list[local.index], key);
12194
+ return true;
11973
12195
  }
11974
12196
  var AFT_OPENCODE_PACKAGE = "@cortexkit/aft-opencode", MODERN_V1_VERSION;
11975
12197
  var init_opencode_config = __esm(() => {
@@ -11984,11 +12206,11 @@ import {
11984
12206
  accessSync as accessSync2,
11985
12207
  closeSync as closeSync4,
11986
12208
  constants as constants2,
11987
- existsSync as existsSync11,
12209
+ existsSync as existsSync12,
11988
12210
  mkdirSync as mkdirSync5,
11989
12211
  mkdtempSync,
11990
12212
  openSync as openSync4,
11991
- readdirSync as readdirSync5,
12213
+ readdirSync as readdirSync6,
11992
12214
  readFileSync as readFileSync8,
11993
12215
  readSync as readSync2,
11994
12216
  realpathSync as realpathSync2,
@@ -11996,7 +12218,7 @@ import {
11996
12218
  statSync as statSync6
11997
12219
  } from "node:fs";
11998
12220
  import { homedir as homedir9, tmpdir } from "node:os";
11999
- import { delimiter as delimiter2, dirname as dirname5, join as join13, parse, resolve as resolve7 } from "node:path";
12221
+ import { delimiter as delimiter2, dirname as dirname5, join as join14, parse, resolve as resolve7 } from "node:path";
12000
12222
  function executableNames2(name, platform) {
12001
12223
  if (platform !== "win32")
12002
12224
  return [name];
@@ -12034,7 +12256,7 @@ function packageMetadataNearExecutable(executable, expectedName) {
12034
12256
  current = dirname5(executable);
12035
12257
  }
12036
12258
  while (true) {
12037
- const direct = packageMetadataAt(join13(current, "package.json"), expectedName);
12259
+ const direct = packageMetadataAt(join14(current, "package.json"), expectedName);
12038
12260
  if (direct)
12039
12261
  return direct;
12040
12262
  const parent = dirname5(current);
@@ -12072,7 +12294,7 @@ function hashFileBytes(hash, path, size) {
12072
12294
  }
12073
12295
  }
12074
12296
  function snapshotTree(path) {
12075
- if (!existsSync11(path))
12297
+ if (!existsSync12(path))
12076
12298
  return "missing";
12077
12299
  const hash = createHash4("sha256");
12078
12300
  const visit = (current, relativePath) => {
@@ -12085,8 +12307,8 @@ function snapshotTree(path) {
12085
12307
  }
12086
12308
  hash.update(`${relativePath}\x00${info.mode}\x00${info.size}\x00${info.mtimeMs}\x00`);
12087
12309
  if (info.isDirectory()) {
12088
- for (const entry of readdirSync5(current).sort()) {
12089
- visit(join13(current, entry), join13(relativePath, entry));
12310
+ for (const entry of readdirSync6(current).sort()) {
12311
+ visit(join14(current, entry), join14(relativePath, entry));
12090
12312
  }
12091
12313
  return;
12092
12314
  }
@@ -12102,24 +12324,31 @@ function snapshotTree(path) {
12102
12324
  return hash.digest("hex");
12103
12325
  }
12104
12326
  function snapshotOperatorState(operatorHome) {
12105
- const database = join13(operatorHome, ".local", "share", "opencode", "opencode.db");
12327
+ const database = join14(operatorHome, ".local", "share", "opencode", "opencode.db");
12106
12328
  return JSON.stringify({
12107
12329
  database: snapshotTree(database),
12108
12330
  databaseWal: snapshotTree(`${database}-wal`),
12109
12331
  databaseShm: snapshotTree(`${database}-shm`),
12110
- logs: snapshotTree(join13(operatorHome, ".local", "share", "opencode", "log"))
12332
+ logs: snapshotTree(join14(operatorHome, ".local", "share", "opencode", "log"))
12333
+ });
12334
+ }
12335
+ function probeCreatedIsolatedState(dataRoot, stateRoot) {
12336
+ return [dataRoot, stateRoot].some((root) => {
12337
+ const opencodeRoot = join14(root, "opencode");
12338
+ const database = join14(opencodeRoot, "opencode.db");
12339
+ return [database, `${database}-wal`, `${database}-shm`, join14(opencodeRoot, "log")].some(existsSync12);
12111
12340
  });
12112
12341
  }
12113
12342
  function probeOpenCodeV1Version(executable, options = {}) {
12114
12343
  const operatorHome = options.operatorHome ?? homedir9();
12115
12344
  const before = snapshotOperatorState(operatorHome);
12116
- const root = mkdtempSync(join13(options.tempParent ?? tmpdir(), "aft-opencode-probe-"));
12117
- const home = join13(root, "home");
12118
- const config = join13(root, "config");
12119
- const data = join13(root, "data");
12120
- const state = join13(root, "state");
12121
- const cache = join13(root, "cache");
12122
- const project = join13(root, "project");
12345
+ const root = mkdtempSync(join14(options.tempParent ?? tmpdir(), "aft-opencode-probe-"));
12346
+ const home = join14(root, "home");
12347
+ const config = join14(root, "config");
12348
+ const data = join14(root, "data");
12349
+ const state = join14(root, "state");
12350
+ const cache = join14(root, "cache");
12351
+ const project = join14(root, "project");
12123
12352
  for (const directory of [home, config, data, state, cache, project]) {
12124
12353
  mkdirSync5(directory, { recursive: true });
12125
12354
  }
@@ -12130,7 +12359,7 @@ function probeOpenCodeV1Version(executable, options = {}) {
12130
12359
  XDG_DATA_HOME: data,
12131
12360
  XDG_STATE_HOME: state,
12132
12361
  XDG_CACHE_HOME: cache,
12133
- TMPDIR: join13(root, "tmp")
12362
+ TMPDIR: join14(root, "tmp")
12134
12363
  };
12135
12364
  mkdirSync5(env.TMPDIR, { recursive: true });
12136
12365
  delete env.OPENCODE_CONFIG;
@@ -12151,41 +12380,75 @@ function probeOpenCodeV1Version(executable, options = {}) {
12151
12380
  probeError = error;
12152
12381
  }
12153
12382
  const after = snapshotOperatorState(operatorHome);
12383
+ const createdIsolatedState = probeCreatedIsolatedState(data, state);
12154
12384
  rmSync3(root, { recursive: true, force: true });
12155
- if (after !== before) {
12385
+ if (after !== before && env.AFT_LOAD_MATRIX_ALLOW_LIVE_OPERATOR === "0") {
12156
12386
  throw new Error("OpenCode host probe changed the operator database or log directory");
12157
12387
  }
12388
+ if (after !== before && !createdIsolatedState) {
12389
+ console.warn("OpenCode operator state changed during an isolated host probe while its data and state roots stayed empty; an active host may be writing concurrently");
12390
+ }
12158
12391
  if (probeError)
12159
12392
  throw probeError;
12160
12393
  if (!result || result.error || result.status !== 0)
12161
12394
  return null;
12162
- const output = String(result.stdout ?? "").trim();
12163
- return output.length > 0 ? output : null;
12395
+ return parseHostVersion(String(result.stdout ?? ""));
12396
+ }
12397
+ function parseHostVersion(output) {
12398
+ if (!output)
12399
+ return null;
12400
+ const match = output.match(/(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z][0-9A-Za-z.-]*)?)/);
12401
+ return match ? match[1] : null;
12402
+ }
12403
+ function v2MetadataNearExecutable(executable) {
12404
+ for (const name of V2_PACKAGE_NAMES) {
12405
+ const metadata = packageMetadataNearExecutable(executable, name);
12406
+ if (metadata)
12407
+ return metadata;
12408
+ }
12409
+ return null;
12410
+ }
12411
+ function isV2Version(version) {
12412
+ const parsed = parseHostVersion(version);
12413
+ if (!parsed)
12414
+ return false;
12415
+ if (/^0\.0\.0-(?:beta|dev)-/.test(parsed))
12416
+ return true;
12417
+ const major = Number.parseInt(parsed, 10);
12418
+ return Number.isFinite(major) && major >= 2;
12419
+ }
12420
+ function resolvedPath(executable) {
12421
+ try {
12422
+ return realpathSync2(executable);
12423
+ } catch {
12424
+ return executable;
12425
+ }
12164
12426
  }
12165
12427
  function detectOpenCodeHostGeneration(dependencies = {}) {
12166
12428
  const findExecutable = dependencies.findExecutable ?? ((name) => findExecutableOnPath(name, dependencies.path, dependencies.platform));
12167
- const v1Executable = findExecutable("opencode");
12168
- const v2Executable = findExecutable("opencode2");
12429
+ const openCodeExecutable = findExecutable("opencode");
12430
+ const openCode2Executable = findExecutable("opencode2");
12169
12431
  const evidence = [];
12170
- if (v1Executable) {
12171
- const v1Metadata = packageMetadataNearExecutable(v1Executable, "opencode-ai");
12172
- const v2Metadata = packageMetadataNearExecutable(v1Executable, "@opencode-ai/cli");
12432
+ const sharedExecutable = openCodeExecutable !== null && openCode2Executable !== null && resolvedPath(openCodeExecutable) === resolvedPath(openCode2Executable);
12433
+ if (openCodeExecutable) {
12434
+ const v1Metadata = packageMetadataNearExecutable(openCodeExecutable, "opencode-ai");
12435
+ const v2Metadata = v2MetadataNearExecutable(openCodeExecutable);
12173
12436
  const metadataVersion = packageVersion(v2Metadata) ?? packageVersion(v1Metadata);
12174
- const version = metadataVersion ?? (dependencies.probeV1Version ?? probeOpenCodeV1Version)(v1Executable);
12175
- const isV2 = Boolean(v2Metadata) || /^0\.0\.0-(?:beta|dev)-/.test(version ?? "");
12437
+ const version = metadataVersion ?? (dependencies.probeV1Version ?? probeOpenCodeV1Version)(openCodeExecutable);
12438
+ const isV2 = Boolean(v2Metadata) || sharedExecutable || isV2Version(version);
12176
12439
  evidence.push({
12177
12440
  generation: isV2 ? "v2" : "v1",
12178
- executable: v1Executable,
12441
+ executable: openCodeExecutable,
12179
12442
  version,
12180
12443
  runtime: "bun",
12181
12444
  modernV1: !isV2 && version === MODERN_V1_VERSION
12182
12445
  });
12183
12446
  }
12184
- if (v2Executable) {
12185
- const version = packageVersion(packageMetadataNearExecutable(v2Executable, "@opencode-ai/cli"));
12447
+ if (openCode2Executable && !sharedExecutable) {
12448
+ const version = packageVersion(v2MetadataNearExecutable(openCode2Executable));
12186
12449
  evidence.push({
12187
12450
  generation: "v2",
12188
- executable: v2Executable,
12451
+ executable: openCode2Executable,
12189
12452
  version,
12190
12453
  runtime: "bun",
12191
12454
  modernV1: false
@@ -12209,17 +12472,18 @@ function formatHostGenerations(detection) {
12209
12472
  return "V2";
12210
12473
  return "unknown";
12211
12474
  }
12212
- var FULL_CONTENT_HASH_MAX_BYTES, PARTIAL_CONTENT_WINDOW_BYTES;
12475
+ var FULL_CONTENT_HASH_MAX_BYTES, PARTIAL_CONTENT_WINDOW_BYTES, V2_PACKAGE_NAMES;
12213
12476
  var init_host_generation = __esm(() => {
12214
12477
  init_opencode_config();
12215
12478
  FULL_CONTENT_HASH_MAX_BYTES = 64 * 1024 * 1024;
12216
12479
  PARTIAL_CONTENT_WINDOW_BYTES = 1024 * 1024;
12480
+ V2_PACKAGE_NAMES = ["@opencode/cli", "@opencode-ai/cli"];
12217
12481
  });
12218
12482
 
12219
12483
  // src/adapters/opencode.ts
12220
- import { existsSync as existsSync12, readFileSync as readFileSync9, rmSync as rmSync4, statSync as statSync7 } from "node:fs";
12484
+ import { existsSync as existsSync13, readFileSync as readFileSync9, rmSync as rmSync4, statSync as statSync7 } from "node:fs";
12221
12485
  import { homedir as homedir10 } from "node:os";
12222
- import { dirname as dirname6, join as join14, parse as parse2, resolve as resolve8 } from "node:path";
12486
+ import { dirname as dirname6, join as join15, parse as parse2, resolve as resolve8 } from "node:path";
12223
12487
  import { fileURLToPath } from "node:url";
12224
12488
  function getOpenCodeConfigDir() {
12225
12489
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
@@ -12230,13 +12494,13 @@ function getOpenCodeConfigDir() {
12230
12494
  function getLegacyOpenCodePluginCachePath(primaryPath) {
12231
12495
  if (process.platform !== "win32")
12232
12496
  return null;
12233
- const localAppData = process.env.LOCALAPPDATA ?? join14(homedir10(), "AppData", "Local");
12234
- const legacyPath = join14(localAppData, "opencode", "packages", LEGACY_PLUGIN_ENTRY);
12497
+ const localAppData = process.env.LOCALAPPDATA ?? join15(homedir10(), "AppData", "Local");
12498
+ const legacyPath = join15(localAppData, "opencode", "packages", LEGACY_PLUGIN_ENTRY);
12235
12499
  return resolve8(legacyPath) === resolve8(primaryPath) ? null : legacyPath;
12236
12500
  }
12237
12501
  function clearLegacyOpenCodePluginCache(primaryPath) {
12238
12502
  const legacyPath = getLegacyOpenCodePluginCachePath(primaryPath);
12239
- if (!legacyPath || !existsSync12(legacyPath))
12503
+ if (!legacyPath || !existsSync13(legacyPath))
12240
12504
  return { clearedPath: null };
12241
12505
  try {
12242
12506
  rmSync4(legacyPath, { recursive: true, force: true });
@@ -12254,16 +12518,16 @@ function hasOpenCodeCli() {
12254
12518
  function openCodeDesktopAppExists() {
12255
12519
  const candidates = [];
12256
12520
  if (process.platform === "darwin") {
12257
- candidates.push("/Applications/OpenCode.app", "/Applications/OpenCode Beta.app", join14(homedir10(), "Applications", "OpenCode.app"), join14(homedir10(), "Applications", "OpenCode Beta.app"));
12521
+ candidates.push("/Applications/OpenCode.app", "/Applications/OpenCode Beta.app", join15(homedir10(), "Applications", "OpenCode.app"), join15(homedir10(), "Applications", "OpenCode Beta.app"));
12258
12522
  } else if (process.platform === "win32") {
12259
- const localAppData = process.env.LOCALAPPDATA ?? join14(homedir10(), "AppData", "Local");
12260
- candidates.push(join14(localAppData, "Programs", "opencode"), join14(localAppData, "opencode"));
12523
+ const localAppData = process.env.LOCALAPPDATA ?? join15(homedir10(), "AppData", "Local");
12524
+ candidates.push(join15(localAppData, "Programs", "opencode"), join15(localAppData, "opencode"));
12261
12525
  } else {
12262
- candidates.push("/opt/OpenCode", "/usr/lib/opencode", join14(homedir10(), ".local", "share", "applications", "opencode.desktop"));
12526
+ candidates.push("/opt/OpenCode", "/usr/lib/opencode", join15(homedir10(), ".local", "share", "applications", "opencode.desktop"));
12263
12527
  }
12264
12528
  return candidates.some((p) => {
12265
12529
  try {
12266
- return existsSync12(p);
12530
+ return existsSync13(p);
12267
12531
  } catch {
12268
12532
  return false;
12269
12533
  }
@@ -12286,13 +12550,13 @@ function pathPointsToOurPlugin(entry) {
12286
12550
  if (!fsPath)
12287
12551
  return false;
12288
12552
  try {
12289
- if (!existsSync12(fsPath))
12553
+ if (!existsSync13(fsPath))
12290
12554
  return false;
12291
12555
  let searchDir = statSync7(fsPath).isDirectory() ? fsPath : dirname6(fsPath);
12292
12556
  let pkgJsonPath = null;
12293
12557
  while (true) {
12294
- const candidate = join14(searchDir, "package.json");
12295
- if (existsSync12(candidate)) {
12558
+ const candidate = join15(searchDir, "package.json");
12559
+ if (existsSync13(candidate)) {
12296
12560
  pkgJsonPath = candidate;
12297
12561
  break;
12298
12562
  }
@@ -12316,6 +12580,19 @@ function matchesPluginEntry(entry) {
12316
12580
  return true;
12317
12581
  return pathPointsToOurPlugin(entry);
12318
12582
  }
12583
+ function configRegistersAft(value, generation) {
12584
+ if (!value)
12585
+ return false;
12586
+ return openCodePluginReadKeys(generation).some((key) => {
12587
+ const list = value[key];
12588
+ if (!Array.isArray(list))
12589
+ return false;
12590
+ return list.some((entry) => {
12591
+ const packageSpec = pluginEntryPackage(entry);
12592
+ return packageSpec !== null && matchesPluginEntry(packageSpec);
12593
+ });
12594
+ });
12595
+ }
12319
12596
 
12320
12597
  class OpenCodeAdapter {
12321
12598
  kind = "opencode";
@@ -12324,7 +12601,7 @@ class OpenCodeAdapter {
12324
12601
  pluginEntryWithVersion = PLUGIN_ENTRY;
12325
12602
  hostDetection;
12326
12603
  isInstalled() {
12327
- if (existsSync12(getOpenCodeConfigDir()))
12604
+ if (existsSync13(getOpenCodeConfigDir()))
12328
12605
  return true;
12329
12606
  if (openCodeDesktopAppExists())
12330
12607
  return true;
@@ -12334,6 +12611,12 @@ class OpenCodeAdapter {
12334
12611
  this.hostDetection ??= detectOpenCodeHostGeneration();
12335
12612
  return this.hostDetection;
12336
12613
  }
12614
+ useHostDetection(detection) {
12615
+ this.hostDetection = detection;
12616
+ }
12617
+ configGeneration() {
12618
+ return this.hostDetection?.status ?? "unknown";
12619
+ }
12337
12620
  getHostVersion() {
12338
12621
  const versions = this.detectHostGeneration().evidence.map((item) => item.version).filter((version) => Boolean(version));
12339
12622
  return versions.length > 0 ? versions.join(" + ") : null;
@@ -12342,7 +12625,7 @@ class OpenCodeAdapter {
12342
12625
  const configDir = getOpenCodeConfigDir();
12343
12626
  const harness = detectJsoncFile(configDir, "opencode");
12344
12627
  const aftConfigPath = resolveCortexKitUserConfigPath();
12345
- const aftConfigExists = existsSync12(aftConfigPath);
12628
+ const aftConfigExists = existsSync13(aftConfigPath);
12346
12629
  const tui = detectJsoncFile(configDir, "tui");
12347
12630
  return {
12348
12631
  configDir,
@@ -12357,8 +12640,7 @@ class OpenCodeAdapter {
12357
12640
  hasPluginEntry() {
12358
12641
  const paths = this.detectConfigPaths();
12359
12642
  const { value } = readJsoncFile(paths.harnessConfig);
12360
- const plugins = Array.isArray(value?.plugin) ? value.plugin : [];
12361
- return plugins.some((entry) => typeof entry === "string" && matchesPluginEntry(entry));
12643
+ return configRegistersAft(value, this.configGeneration());
12362
12644
  }
12363
12645
  async ensurePluginEntry() {
12364
12646
  const paths = this.detectConfigPaths();
@@ -12373,8 +12655,7 @@ class OpenCodeAdapter {
12373
12655
  if (!paths.tuiConfig)
12374
12656
  return false;
12375
12657
  const { value } = readJsoncFile(paths.tuiConfig);
12376
- const plugins = Array.isArray(value?.plugin) ? value.plugin : [];
12377
- return plugins.some((entry) => typeof entry === "string" && matchesPluginEntry(entry));
12658
+ return configRegistersAft(value, this.configGeneration());
12378
12659
  }
12379
12660
  async ensureTuiPluginEntry() {
12380
12661
  const paths = this.detectConfigPaths();
@@ -12393,12 +12674,14 @@ class OpenCodeAdapter {
12393
12674
  return paths.tuiConfig ? this.configEntryNeedsUpdate(paths.tuiConfig, paths.tuiConfigFormat ?? "none") : true;
12394
12675
  }
12395
12676
  ensureConfigEntry(configPath, format, label) {
12677
+ const generation = this.configGeneration();
12678
+ const key = openCodePluginKey(generation);
12396
12679
  if (format === "none") {
12397
- writeJsoncFile(configPath, { plugin: [PLUGIN_ENTRY] }, "json");
12680
+ writeJsoncFile(configPath, { [key]: [PLUGIN_ENTRY] }, "json");
12398
12681
  return {
12399
12682
  ok: true,
12400
12683
  action: "added",
12401
- message: `Created ${configPath} and added ${PLUGIN_ENTRY} (${label})`,
12684
+ message: `Created ${configPath} and added ${PLUGIN_ENTRY} under \`${key}\` (${label})`,
12402
12685
  configPath
12403
12686
  };
12404
12687
  }
@@ -12411,12 +12694,12 @@ class OpenCodeAdapter {
12411
12694
  configPath
12412
12695
  };
12413
12696
  }
12414
- const update = ensurePinnedPluginConfig(value, getSelfVersion(), pathPointsToOurPlugin);
12697
+ const update = ensurePinnedPluginConfig(value, getSelfVersion(), pathPointsToOurPlugin, generation);
12415
12698
  if (!update.changed) {
12416
12699
  return {
12417
12700
  ok: true,
12418
12701
  action: "already_present",
12419
- message: `${PLUGIN_ENTRY} is already registered in ${configPath}`,
12702
+ message: `${PLUGIN_ENTRY} is already registered under \`${update.key}\` in ${configPath}`,
12420
12703
  configPath
12421
12704
  };
12422
12705
  }
@@ -12424,7 +12707,7 @@ class OpenCodeAdapter {
12424
12707
  return {
12425
12708
  ok: true,
12426
12709
  action: update.action,
12427
- message: `${update.action === "added" ? "Added" : "Updated"} ${PLUGIN_ENTRY} in ${configPath} (${label})`,
12710
+ message: `${update.action === "added" ? "Added" : "Updated"} ${PLUGIN_ENTRY} under \`${update.key}\` in ${configPath} (${label})`,
12428
12711
  configPath
12429
12712
  };
12430
12713
  }
@@ -12434,17 +12717,17 @@ class OpenCodeAdapter {
12434
12717
  const { value, error } = readJsoncFile(configPath);
12435
12718
  if (error || !value)
12436
12719
  return false;
12437
- return pluginConfigNeedsUpdate(value, getSelfVersion(), pathPointsToOurPlugin);
12720
+ return pluginConfigNeedsUpdate(value, getSelfVersion(), pathPointsToOurPlugin, this.configGeneration());
12438
12721
  }
12439
12722
  getPluginCacheInfo() {
12440
12723
  const configPath = this.detectConfigPaths().harnessConfig;
12441
12724
  const { value } = readJsoncFile(configPath);
12442
- const configuredEntry = Array.isArray(value?.plugin) ? value.plugin.find(isAftNpmEntry) : undefined;
12443
- const path = join14(getOpenCodeCacheDir(), "packages", configuredEntry ?? PLUGIN_ENTRY);
12725
+ const configuredEntry = openCodePluginReadKeys(this.configGeneration()).flatMap((key) => Array.isArray(value?.[key]) ? value[key] : []).map(pluginEntryPackage).find(isAftNpmEntry);
12726
+ const path = join15(getOpenCodeCacheDir(), "packages", configuredEntry ?? PLUGIN_ENTRY);
12444
12727
  let cached;
12445
12728
  try {
12446
- const installedPkgPath = join14(path, "node_modules", "@cortexkit", "aft-opencode", "package.json");
12447
- if (existsSync12(installedPkgPath)) {
12729
+ const installedPkgPath = join15(path, "node_modules", "@cortexkit", "aft-opencode", "package.json");
12730
+ if (existsSync13(installedPkgPath)) {
12448
12731
  const pkg = JSON.parse(readFileSync9(installedPkgPath, "utf-8"));
12449
12732
  cached = typeof pkg.version === "string" ? pkg.version : undefined;
12450
12733
  }
@@ -12455,7 +12738,7 @@ class OpenCodeAdapter {
12455
12738
  path,
12456
12739
  cached,
12457
12740
  latest: getSelfVersion(),
12458
- exists: existsSync12(path)
12741
+ exists: existsSync13(path)
12459
12742
  };
12460
12743
  }
12461
12744
  getStorageDir() {
@@ -12518,12 +12801,12 @@ class OpenCodeAdapter {
12518
12801
  describeStorageSubtrees() {
12519
12802
  const storage = this.getStorageDir();
12520
12803
  return {
12521
- index: dirSize(join14(storage, "index")),
12522
- semantic: dirSize(join14(storage, "semantic")),
12523
- backups: dirSize(join14(storage, "backups")),
12524
- url_cache: dirSize(join14(storage, "url_cache")),
12525
- onnxruntime: dirSize(join14(storage, "onnxruntime")),
12526
- logs: dirSize(join14(storage, "logs"))
12804
+ index: dirSize(join15(storage, "index")),
12805
+ semantic: dirSize(join15(storage, "semantic")),
12806
+ backups: dirSize(join15(storage, "backups")),
12807
+ url_cache: dirSize(join15(storage, "url_cache")),
12808
+ onnxruntime: dirSize(join15(storage, "onnxruntime")),
12809
+ logs: dirSize(join15(storage, "logs"))
12527
12810
  };
12528
12811
  }
12529
12812
  }
@@ -12543,20 +12826,20 @@ var init_opencode = __esm(() => {
12543
12826
 
12544
12827
  // src/adapters/pi.ts
12545
12828
  import { execSync as execSync2, spawnSync as spawnSync6 } from "node:child_process";
12546
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
12829
+ import { existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
12547
12830
  import { homedir as homedir11 } from "node:os";
12548
- import { join as join15, resolve as resolve9 } from "node:path";
12831
+ import { join as join16, resolve as resolve9 } from "node:path";
12549
12832
  function getPiAgentDir() {
12550
12833
  const configuredDir = process.env.PI_CODING_AGENT_DIR?.trim();
12551
12834
  if (configuredDir)
12552
12835
  return resolve9(configuredDir);
12553
12836
  const envHome = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
12554
12837
  const home = envHome && envHome.length > 0 ? envHome : homedir11();
12555
- return join15(home, ".pi", "agent");
12838
+ return join16(home, ".pi", "agent");
12556
12839
  }
12557
12840
  function readPiExtensionIndex() {
12558
- const settingsPath = join15(getPiAgentDir(), "settings.json");
12559
- if (existsSync13(settingsPath)) {
12841
+ const settingsPath = join16(getPiAgentDir(), "settings.json");
12842
+ if (existsSync14(settingsPath)) {
12560
12843
  try {
12561
12844
  const raw = readFileSync10(settingsPath, "utf-8");
12562
12845
  const trimmed = raw.replace(/^\uFEFF/, "");
@@ -12576,13 +12859,13 @@ function readPiExtensionIndex() {
12576
12859
  } catch {}
12577
12860
  }
12578
12861
  const candidates = [
12579
- join15(getPiAgentDir(), "extensions.json"),
12580
- join15(getPiAgentDir(), "extensions.jsonc"),
12581
- join15(getPiAgentDir(), "config.json"),
12582
- join15(getPiAgentDir(), "config.jsonc")
12862
+ join16(getPiAgentDir(), "extensions.json"),
12863
+ join16(getPiAgentDir(), "extensions.jsonc"),
12864
+ join16(getPiAgentDir(), "config.json"),
12865
+ join16(getPiAgentDir(), "config.jsonc")
12583
12866
  ];
12584
12867
  for (const path of candidates) {
12585
- if (!existsSync13(path))
12868
+ if (!existsSync14(path))
12586
12869
  continue;
12587
12870
  try {
12588
12871
  const { value } = readJsoncFile(path);
@@ -12615,15 +12898,15 @@ function piEntryMatchesAft(entry) {
12615
12898
  } else if (entry.startsWith("/")) {
12616
12899
  resolved = entry;
12617
12900
  } else if (entry.length > 0) {
12618
- resolved = join15(getPiAgentDir(), entry);
12901
+ resolved = join16(getPiAgentDir(), entry);
12619
12902
  }
12620
12903
  if (!resolved)
12621
12904
  return false;
12622
12905
  try {
12623
- if (!existsSync13(resolved))
12906
+ if (!existsSync14(resolved))
12624
12907
  return false;
12625
- const pkgPath = join15(resolved, "package.json");
12626
- if (!existsSync13(pkgPath))
12908
+ const pkgPath = join16(resolved, "package.json");
12909
+ if (!existsSync14(pkgPath))
12627
12910
  return false;
12628
12911
  const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
12629
12912
  return pkg.name === PLUGIN_NAME2;
@@ -12671,10 +12954,10 @@ class PiAdapter {
12671
12954
  const configDir = getPiAgentDir();
12672
12955
  const index = readPiExtensionIndex();
12673
12956
  const aftConfigPath = resolveCortexKitUserConfigPath();
12674
- const aftConfigExists = existsSync13(aftConfigPath);
12957
+ const aftConfigExists = existsSync14(aftConfigPath);
12675
12958
  return {
12676
12959
  configDir,
12677
- harnessConfig: index.path ?? join15(configDir, "extensions.json"),
12960
+ harnessConfig: index.path ?? join16(configDir, "extensions.json"),
12678
12961
  harnessConfigFormat: index.path ? "json" : "none",
12679
12962
  aftConfig: aftConfigPath,
12680
12963
  aftConfigFormat: aftConfigExists ? "jsonc" : "none"
@@ -12719,12 +13002,12 @@ class PiAdapter {
12719
13002
  }
12720
13003
  getPluginCacheInfo() {
12721
13004
  const candidates = [
12722
- join15(getPiAgentDir(), "npm", "node_modules", "@cortexkit", "aft-pi", "package.json"),
12723
- join15(getPiAgentDir(), "node_modules", "@cortexkit", "aft-pi", "package.json"),
12724
- join15(getPiAgentDir(), "extensions", "node_modules", "@cortexkit", "aft-pi", "package.json")
13005
+ join16(getPiAgentDir(), "npm", "node_modules", "@cortexkit", "aft-pi", "package.json"),
13006
+ join16(getPiAgentDir(), "node_modules", "@cortexkit", "aft-pi", "package.json"),
13007
+ join16(getPiAgentDir(), "extensions", "node_modules", "@cortexkit", "aft-pi", "package.json")
12725
13008
  ];
12726
13009
  for (const candidate of candidates) {
12727
- if (!existsSync13(candidate))
13010
+ if (!existsSync14(candidate))
12728
13011
  continue;
12729
13012
  try {
12730
13013
  const pkg = JSON.parse(readFileSync10(candidate, "utf-8"));
@@ -12738,7 +13021,7 @@ class PiAdapter {
12738
13021
  } catch {}
12739
13022
  }
12740
13023
  return {
12741
- path: join15(getPiAgentDir(), "npm", "node_modules", "@cortexkit", "aft-pi", "package.json"),
13024
+ path: join16(getPiAgentDir(), "npm", "node_modules", "@cortexkit", "aft-pi", "package.json"),
12742
13025
  exists: false
12743
13026
  };
12744
13027
  }
@@ -12760,12 +13043,12 @@ class PiAdapter {
12760
13043
  describeStorageSubtrees() {
12761
13044
  const storage = this.getStorageDir();
12762
13045
  return {
12763
- index: dirSize(join15(storage, "index")),
12764
- semantic: dirSize(join15(storage, "semantic")),
12765
- backups: dirSize(join15(storage, "backups")),
12766
- url_cache: dirSize(join15(storage, "url_cache")),
12767
- onnxruntime: dirSize(join15(storage, "onnxruntime")),
12768
- logs: dirSize(join15(storage, "logs"))
13046
+ index: dirSize(join16(storage, "index")),
13047
+ semantic: dirSize(join16(storage, "semantic")),
13048
+ backups: dirSize(join16(storage, "backups")),
13049
+ url_cache: dirSize(join16(storage, "url_cache")),
13050
+ onnxruntime: dirSize(join16(storage, "onnxruntime")),
13051
+ logs: dirSize(join16(storage, "logs"))
12769
13052
  };
12770
13053
  }
12771
13054
  }
@@ -14151,6 +14434,7 @@ async function runSetup(argv, options = {}) {
14151
14434
  }
14152
14435
  if (adapter instanceof OpenCodeAdapter) {
14153
14436
  const detection = options.detectOpenCodeHost ? options.detectOpenCodeHost() : adapter.detectHostGeneration();
14437
+ adapter.useHostDetection(detection);
14154
14438
  log2.info(`${adapter.displayName}: host generation ${formatHostGenerations(detection)}`);
14155
14439
  if (detection.status === "ambiguous") {
14156
14440
  log2.error(`${adapter.displayName}: both V1 and V2 hosts are installed; refusing to change either config until only one generation is selected on PATH.`);
@@ -14235,7 +14519,7 @@ import { spawnSync as spawnSync7 } from "node:child_process";
14235
14519
  function runIndex(argv) {
14236
14520
  const binary = findAftBinary();
14237
14521
  if (!binary) {
14238
- console.error("aft index requires a native AFT binary; run `aft doctor` to install or repair it.");
14522
+ console.error(missingAftBinaryMessage("aft index"));
14239
14523
  return 1;
14240
14524
  }
14241
14525
  const result = spawnSync7(binary, ["index", ...argv], {
@@ -14253,24 +14537,50 @@ var init_commands = __esm(async () => {
14253
14537
  });
14254
14538
 
14255
14539
  // src/doctor/opencode.ts
14256
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
14257
- import { dirname as dirname7, join as join16 } from "node:path";
14540
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
14541
+ import { dirname as dirname7, join as join17 } from "node:path";
14258
14542
  import { fileURLToPath as fileURLToPath2 } from "node:url";
14259
14543
  function isLoadPath(value) {
14260
14544
  return OPENCODE_LOAD_PATHS.includes(value);
14261
14545
  }
14262
- function configuredAftEntry(value) {
14263
- if (!Array.isArray(value?.plugin))
14546
+ function aftEntryUnderKey(value, key) {
14547
+ const list = value?.[key];
14548
+ if (!Array.isArray(list))
14264
14549
  return null;
14265
- for (const entry of value.plugin) {
14266
- if (isAftNpmEntry(entry))
14267
- return entry;
14268
- const root = localPluginRoot(entry);
14550
+ for (const entry of list) {
14551
+ const packageSpec = pluginEntryPackage(entry);
14552
+ if (packageSpec === null)
14553
+ continue;
14554
+ if (isAftNpmEntry(packageSpec))
14555
+ return packageSpec;
14556
+ const root = localPluginRoot(packageSpec);
14269
14557
  if (root && manifestFromLocalPath(root))
14558
+ return packageSpec;
14559
+ }
14560
+ return null;
14561
+ }
14562
+ function configuredAftEntry(value, generation) {
14563
+ for (const key of openCodePluginReadKeys(generation)) {
14564
+ const entry = aftEntryUnderKey(value, key);
14565
+ if (entry)
14270
14566
  return entry;
14271
14567
  }
14272
14568
  return null;
14273
14569
  }
14570
+ function misshapenEntries(value, key) {
14571
+ const list = value?.[key];
14572
+ if (!Array.isArray(list))
14573
+ return [];
14574
+ const packages = [];
14575
+ for (const entry of list) {
14576
+ if (pluginEntryFitsKey(entry, key))
14577
+ continue;
14578
+ const packageSpec = pluginEntryPackage(entry);
14579
+ if (packageSpec !== null)
14580
+ packages.push(packageSpec);
14581
+ }
14582
+ return packages;
14583
+ }
14274
14584
  function localPluginRoot(entry) {
14275
14585
  if (typeof entry !== "string")
14276
14586
  return null;
@@ -14299,10 +14609,10 @@ function readManifest(path) {
14299
14609
  }
14300
14610
  function manifestFromLocalPath(path) {
14301
14611
  let current = path;
14302
- if (!existsSync14(join16(current, "package.json")))
14612
+ if (!existsSync15(join17(current, "package.json")))
14303
14613
  current = dirname7(current);
14304
14614
  while (true) {
14305
- const manifest = readManifest(join16(current, "package.json"));
14615
+ const manifest = readManifest(join17(current, "package.json"));
14306
14616
  if (manifest)
14307
14617
  return manifest;
14308
14618
  const parent = dirname7(current);
@@ -14315,21 +14625,18 @@ function pluginManifest(entry, cachePath) {
14315
14625
  const localRoot = localPluginRoot(entry);
14316
14626
  if (localRoot)
14317
14627
  return manifestFromLocalPath(localRoot);
14318
- return readManifest(join16(cachePath, "node_modules", "@cortexkit", "aft-opencode", "package.json"));
14628
+ return readManifest(join17(cachePath, "node_modules", "@cortexkit", "aft-opencode", "package.json"));
14319
14629
  }
14320
- function manifestUsesServerExport(manifest) {
14321
- if (!manifest)
14322
- return true;
14323
- const discovered = manifest["oc-plugin"];
14324
- const exports = manifest.exports;
14325
- return Array.isArray(discovered) && discovered.includes("server") && typeof exports === "object" && exports !== null && Object.hasOwn(exports, "./server");
14630
+ function hasExplicitSemver(entry) {
14631
+ const version = configuredVersion(entry);
14632
+ return Boolean(version && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(version));
14326
14633
  }
14327
14634
  function latestLoggedLoadPath(logPath) {
14328
- if (!existsSync14(logPath))
14635
+ if (!existsSync15(logPath))
14329
14636
  return null;
14330
14637
  try {
14331
14638
  const text = readFileSync11(logPath, "utf8");
14332
- const pattern = /(?:load path|load_path)\s*[:=]\s*(root-default|export-server-v1|export-server-effect-bun|export-server-effect-node)/gi;
14639
+ const pattern = /(?:load path|load_path)\s*[:=]\s*(root-default|export-server-effect-bun|export-server-effect-node)/gi;
14333
14640
  let latest = null;
14334
14641
  for (const match of text.matchAll(pattern)) {
14335
14642
  const value = match[1];
@@ -14346,7 +14653,7 @@ function detectedV2Runtime(detection) {
14346
14653
  }
14347
14654
  function expectedOpenCodeLoadPath(detection) {
14348
14655
  if (detection.status === "v1")
14349
- return "export-server-v1";
14656
+ return "root-default";
14350
14657
  if (detection.status === "v2") {
14351
14658
  return detectedV2Runtime(detection) === "bun" ? "export-server-effect-bun" : "export-server-effect-node";
14352
14659
  }
@@ -14354,27 +14661,25 @@ function expectedOpenCodeLoadPath(detection) {
14354
14661
  }
14355
14662
  function diagnoseOpenCodeLoad(input) {
14356
14663
  const expectedLoadPath = expectedOpenCodeLoadPath(input.detection);
14664
+ const generation = input.detection.status;
14357
14665
  const config = readJsoncFile(input.configPath).value;
14358
- const entry = configuredAftEntry(config);
14666
+ const entry = configuredAftEntry(config, generation);
14359
14667
  const manifest = pluginManifest(entry, input.pluginCachePath);
14360
14668
  const logged = latestLoggedLoadPath(input.logPath);
14361
14669
  let takenLoadPath = logged;
14362
- if (!takenLoadPath && input.detection.status === "v1") {
14363
- takenLoadPath = manifestUsesServerExport(manifest) ? "export-server-v1" : "root-default";
14364
- } else if (!takenLoadPath && input.detection.status === "v2") {
14670
+ if (!takenLoadPath && generation === "v1") {
14671
+ takenLoadPath = "root-default";
14672
+ } else if (!takenLoadPath && generation === "v2") {
14365
14673
  takenLoadPath = expectedLoadPath;
14366
14674
  }
14367
14675
  const pluginVersion = (typeof manifest?.version === "string" ? manifest.version : null) ?? input.cachedPluginVersion ?? configuredVersion(entry) ?? null;
14368
- const problems = [];
14369
- if (config && Object.hasOwn(config, "plugins")) {
14370
- problems.push("config uses unsupported `plugins`; run doctor --fix to migrate it to `plugin`");
14371
- }
14372
- if (entry && isAftNpmEntry(entry) && input.expectedPluginEntry && entry !== input.expectedPluginEntry) {
14676
+ const problems = [...describePluginKeyProblems(config, generation)];
14677
+ if (entry && isAftNpmEntry(entry) && input.expectedPluginEntry && entry !== input.expectedPluginEntry && !(input.acceptExplicitPluginVersion && hasExplicitSemver(entry))) {
14373
14678
  problems.push(`plugin entry ${entry} is not the required exact pin ${input.expectedPluginEntry}`);
14374
14679
  }
14375
- if (input.detection.status === "ambiguous") {
14376
- problems.push("both OpenCode V1 and V2 hosts were detected; refusing configuration writes");
14377
- } else if (input.detection.status === "unknown") {
14680
+ if (generation === "ambiguous") {
14681
+ problems.push("both OpenCode V1 and V2 hosts were detected; refusing configuration writes (a V1 host reads `plugin`, a V2 host reads `plugins`)");
14682
+ } else if (generation === "unknown") {
14378
14683
  problems.push("OpenCode host generation could not be detected");
14379
14684
  }
14380
14685
  if (expectedLoadPath && takenLoadPath && expectedLoadPath !== takenLoadPath) {
@@ -14382,13 +14687,28 @@ function diagnoseOpenCodeLoad(input) {
14382
14687
  }
14383
14688
  return { expectedLoadPath, takenLoadPath, pluginVersion, problems };
14384
14689
  }
14690
+ function describePluginKeyProblems(config, generation) {
14691
+ if (!config || generation === "ambiguous" || generation === "unknown")
14692
+ return [];
14693
+ const hostKey = openCodePluginKey(generation);
14694
+ const otherKey = otherOpenCodePluginKey(hostKey);
14695
+ const host = generation.toUpperCase();
14696
+ const problems = [];
14697
+ if (!aftEntryUnderKey(config, hostKey) && aftEntryUnderKey(config, otherKey)) {
14698
+ problems.push(`AFT is registered under \`${otherKey}\`, which a ${host} host does not read; run doctor --fix to register it under \`${hostKey}\``);
14699
+ }
14700
+ const aftEntry = aftEntryUnderKey(config, hostKey);
14701
+ for (const packageSpec of misshapenEntries(config, hostKey)) {
14702
+ problems.push(packageSpec === aftEntry ? `plugin entry ${packageSpec} under \`${hostKey}\` uses the ${host === "V1" ? "V2" : "V1"} entry shape; run doctor --fix to rewrite it` : `plugin entry ${packageSpec} under \`${hostKey}\` uses the ${host === "V1" ? "V2" : "V1"} entry shape, which a ${host} host cannot load; left unchanged because AFT did not register it`);
14703
+ }
14704
+ return problems;
14705
+ }
14385
14706
  var OPENCODE_LOAD_PATHS;
14386
14707
  var init_opencode2 = __esm(() => {
14387
14708
  init_jsonc();
14388
14709
  init_opencode_config();
14389
14710
  OPENCODE_LOAD_PATHS = [
14390
14711
  "root-default",
14391
- "export-server-v1",
14392
14712
  "export-server-effect-bun",
14393
14713
  "export-server-effect-node"
14394
14714
  ];
@@ -14521,11 +14841,11 @@ var MAX_NOISE_LINES_IN_ERROR = 5;
14521
14841
  var init_aft_bridge = () => {};
14522
14842
 
14523
14843
  // src/lib/binary-cache.ts
14524
- import { existsSync as existsSync15, readdirSync as readdirSync6, statSync as statSync8 } from "node:fs";
14525
- import { join as join17 } from "node:path";
14844
+ import { existsSync as existsSync16, readdirSync as readdirSync7, statSync as statSync8 } from "node:fs";
14845
+ import { join as join18 } from "node:path";
14526
14846
  function getBinaryCacheInfo(activeVersion) {
14527
14847
  const path = getAftBinaryCacheDir();
14528
- if (!existsSync15(path)) {
14848
+ if (!existsSync16(path)) {
14529
14849
  return {
14530
14850
  versions: [],
14531
14851
  activeVersion: null,
@@ -14533,9 +14853,9 @@ function getBinaryCacheInfo(activeVersion) {
14533
14853
  path
14534
14854
  };
14535
14855
  }
14536
- const versions = readdirSync6(path).filter((entry) => {
14856
+ const versions = readdirSync7(path).filter((entry) => {
14537
14857
  try {
14538
- return statSync8(join17(path, entry)).isDirectory();
14858
+ return statSync8(join18(path, entry)).isDirectory();
14539
14859
  } catch {
14540
14860
  return false;
14541
14861
  }
@@ -14633,13 +14953,13 @@ var init_sanitize = __esm(() => {
14633
14953
  });
14634
14954
 
14635
14955
  // src/lib/bridge-tool-failures.ts
14636
- import { closeSync as closeSync5, existsSync as existsSync16, openSync as openSync5, readSync as readSync3, statSync as statSync9 } from "node:fs";
14956
+ import { closeSync as closeSync5, existsSync as existsSync17, openSync as openSync5, readSync as readSync3, statSync as statSync9 } from "node:fs";
14637
14957
  function resolveBridgePluginLogPath() {
14638
14958
  const isTestEnv = process.env.BUN_TEST === "1" || false;
14639
14959
  return resolveAftLogPath(isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
14640
14960
  }
14641
14961
  function tailLogFileBytes(path, maxBytes) {
14642
- if (!existsSync16(path) || maxBytes <= 0)
14962
+ if (!existsSync17(path) || maxBytes <= 0)
14643
14963
  return "";
14644
14964
  let fd = null;
14645
14965
  try {
@@ -14775,9 +15095,9 @@ var init_bridge_tool_failures = __esm(() => {
14775
15095
  });
14776
15096
 
14777
15097
  // src/lib/build-breaker.ts
14778
- import { existsSync as existsSync17, readdirSync as readdirSync7 } from "node:fs";
15098
+ import { existsSync as existsSync18, readdirSync as readdirSync8 } from "node:fs";
14779
15099
  import { createRequire as createRequire3 } from "node:module";
14780
- import { join as join18 } from "node:path";
15100
+ import { join as join19 } from "node:path";
14781
15101
  function loadDatabaseSync() {
14782
15102
  const require2 = createRequire3(import.meta.url);
14783
15103
  try {
@@ -14791,11 +15111,11 @@ function loadDatabaseSync() {
14791
15111
  }
14792
15112
  }
14793
15113
  function buildBreakerDatabases(storageRoot) {
14794
- const callgraphRoot = join18(storageRoot, "callgraph");
14795
- if (!existsSync17(callgraphRoot))
15114
+ const callgraphRoot = join19(storageRoot, "callgraph");
15115
+ if (!existsSync18(callgraphRoot))
14796
15116
  return [];
14797
15117
  try {
14798
- return readdirSync7(callgraphRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join18(callgraphRoot, entry.name, "build-breaker.sqlite")).filter((path) => existsSync17(path));
15118
+ return readdirSync8(callgraphRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join19(callgraphRoot, entry.name, "build-breaker.sqlite")).filter((path) => existsSync18(path));
14799
15119
  } catch {
14800
15120
  return [];
14801
15121
  }
@@ -14871,20 +15191,20 @@ var init_build_breaker = __esm(() => {
14871
15191
  });
14872
15192
 
14873
15193
  // src/lib/legacy-storage.ts
14874
- import { existsSync as existsSync18, readdirSync as readdirSync8, statSync as statSync10 } from "node:fs";
14875
- import { join as join19 } from "node:path";
15194
+ import { existsSync as existsSync19, readdirSync as readdirSync9, statSync as statSync10 } from "node:fs";
15195
+ import { join as join20 } from "node:path";
14876
15196
  function summarizeLegacyPartitionDuplication(storageRoot) {
14877
- if (!existsSync18(storageRoot)) {
15197
+ if (!existsSync19(storageRoot)) {
14878
15198
  return { totalPartitions: 0, totalBytes: 0, byHarness: [] };
14879
15199
  }
14880
15200
  const byHarness = [];
14881
15201
  for (const harness of safeReadDir(storageRoot)) {
14882
- const harnessPath = join19(storageRoot, harness);
15202
+ const harnessPath = join20(storageRoot, harness);
14883
15203
  if (!isDirectory(harnessPath))
14884
15204
  continue;
14885
15205
  const partitions = new Map;
14886
- collectCallgraphPartitions(join19(harnessPath, "callgraph"), partitions);
14887
- collectInspectPartitions(join19(harnessPath, "inspect"), partitions);
15206
+ collectCallgraphPartitions(join20(harnessPath, "callgraph"), partitions);
15207
+ collectInspectPartitions(join20(harnessPath, "inspect"), partitions);
14888
15208
  if (partitions.size === 0)
14889
15209
  continue;
14890
15210
  let bytes = 0;
@@ -14903,7 +15223,7 @@ function collectCallgraphPartitions(domainPath, partitions) {
14903
15223
  if (!isDirectory(domainPath))
14904
15224
  return;
14905
15225
  for (const name of safeReadDir(domainPath)) {
14906
- const path = join19(domainPath, name);
15226
+ const path = join20(domainPath, name);
14907
15227
  if (isDirectory(path)) {
14908
15228
  if (!looksLikePartitionKey(name))
14909
15229
  continue;
@@ -14920,7 +15240,7 @@ function collectInspectPartitions(domainPath, partitions) {
14920
15240
  if (!isDirectory(domainPath))
14921
15241
  return;
14922
15242
  for (const name of safeReadDir(domainPath)) {
14923
- const path = join19(domainPath, name);
15243
+ const path = join20(domainPath, name);
14924
15244
  if (isDirectory(path)) {
14925
15245
  if (!looksLikePartitionKey(name))
14926
15246
  continue;
@@ -14969,7 +15289,7 @@ function looksLikePartitionKey(value) {
14969
15289
  }
14970
15290
  function safeReadDir(path) {
14971
15291
  try {
14972
- return readdirSync8(path).sort((left, right) => left.localeCompare(right));
15292
+ return readdirSync9(path).sort((left, right) => left.localeCompare(right));
14973
15293
  } catch {
14974
15294
  return [];
14975
15295
  }
@@ -14995,22 +15315,22 @@ var init_legacy_storage = __esm(() => {
14995
15315
  });
14996
15316
 
14997
15317
  // src/lib/lsp-cache.ts
14998
- import { existsSync as existsSync19, readdirSync as readdirSync9, rmSync as rmSync5, statSync as statSync11 } from "node:fs";
14999
- import { join as join20 } from "node:path";
15318
+ import { existsSync as existsSync20, readdirSync as readdirSync10, rmSync as rmSync5, statSync as statSync11 } from "node:fs";
15319
+ import { join as join21 } from "node:path";
15000
15320
  function inspectDir(path) {
15001
- if (!existsSync19(path)) {
15321
+ if (!existsSync20(path)) {
15002
15322
  return { entries: [], totalSize: 0 };
15003
15323
  }
15004
15324
  const entries = [];
15005
15325
  let totalSize = 0;
15006
15326
  let names;
15007
15327
  try {
15008
- names = readdirSync9(path);
15328
+ names = readdirSync10(path);
15009
15329
  } catch {
15010
15330
  return { entries: [], totalSize: 0 };
15011
15331
  }
15012
15332
  for (const name of names) {
15013
- const full = join20(path, name);
15333
+ const full = join21(path, name);
15014
15334
  try {
15015
15335
  if (!statSync11(full).isDirectory())
15016
15336
  continue;
@@ -15068,8 +15388,8 @@ var init_lsp_cache = __esm(() => {
15068
15388
  });
15069
15389
 
15070
15390
  // src/lib/onnx.ts
15071
- import { existsSync as existsSync20, readdirSync as readdirSync10, readlinkSync as readlinkSync2, realpathSync as realpathSync4 } from "node:fs";
15072
- import { basename as basename2, isAbsolute as isAbsolute4, join as join21, resolve as resolve10, win32 as win322 } from "node:path";
15391
+ import { existsSync as existsSync21, readdirSync as readdirSync11, readlinkSync as readlinkSync2, realpathSync as realpathSync4 } from "node:fs";
15392
+ import { basename as basename2, isAbsolute as isAbsolute4, join as join22, resolve as resolve10, win32 as win322 } from "node:path";
15073
15393
  function getOnnxLibraryName() {
15074
15394
  if (process.platform === "darwin")
15075
15395
  return "libonnxruntime.dylib";
@@ -15080,22 +15400,21 @@ function getOnnxLibraryName() {
15080
15400
  function getManualInstallHint2() {
15081
15401
  const p = process.platform;
15082
15402
  const a = process.arch;
15083
- if (p === "darwin") {
15084
- if (a === "arm64")
15085
- return "brew install onnxruntime (Apple Silicon)";
15086
- return "Intel Mac requires manual install — see docs";
15087
- }
15088
- if (p === "linux") {
15089
- if (a === "x64" || a === "arm64") {
15403
+ if (isOrtAutoDownloadSupported()) {
15404
+ if (p === "darwin")
15405
+ return "AFT auto-downloads ONNX Runtime on Apple Silicon";
15406
+ if (p === "linux")
15090
15407
  return "AFT auto-downloads ONNX Runtime on supported Linux (glibc)";
15091
- }
15092
- return "manual install required for this Linux arch";
15093
- }
15094
- if (p === "win32") {
15095
- if (a === "x64" || a === "arm64")
15408
+ if (p === "win32")
15096
15409
  return "AFT auto-downloads ONNX Runtime on Windows";
15097
- return "manual install required for this Windows arch";
15098
- }
15410
+ return `AFT auto-downloads ONNX Runtime on ${p}/${a}`;
15411
+ }
15412
+ if (p === "darwin")
15413
+ return "brew install onnxruntime (Intel Mac — no published build)";
15414
+ if (p === "linux")
15415
+ return `manual install required for this Linux arch (${a})`;
15416
+ if (p === "win32")
15417
+ return `manual install required for this Windows arch (${a})`;
15099
15418
  return "ONNX Runtime must be installed manually for this platform";
15100
15419
  }
15101
15420
  function pathEnvValue2() {
@@ -15122,7 +15441,7 @@ function isWindowsSystem32Directory2(dir) {
15122
15441
  }
15123
15442
  function directoryContainsLibrary2(dir, libName) {
15124
15443
  try {
15125
- const entries = readdirSync10(dir);
15444
+ const entries = readdirSync11(dir);
15126
15445
  if (process.platform === "win32") {
15127
15446
  const expected = libName.toLowerCase();
15128
15447
  return entries.some((entry) => entry.toLowerCase() === expected);
@@ -15140,7 +15459,7 @@ function findIgnoredWindowsSystemOnnxRuntime() {
15140
15459
  for (const root of windowsRoots) {
15141
15460
  if (!root)
15142
15461
  continue;
15143
- const systemDir = join21(root, "System32");
15462
+ const systemDir = join22(root, "System32");
15144
15463
  const key = win322.resolve(systemDir).toLowerCase();
15145
15464
  if (seen.has(key))
15146
15465
  continue;
@@ -15161,21 +15480,21 @@ function findSystemOnnxRuntime2() {
15161
15480
  searchPaths.push(...pathEntriesForPlatform2());
15162
15481
  const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
15163
15482
  const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
15164
- searchPaths.push(join21(programFiles, "onnxruntime", "lib"), join21(programFiles, "Microsoft ONNX Runtime", "lib"), join21(programFiles, "Microsoft Machine Learning", "lib"), join21(programFilesX86, "onnxruntime", "lib"), ...(() => {
15483
+ searchPaths.push(join22(programFiles, "onnxruntime", "lib"), join22(programFiles, "Microsoft ONNX Runtime", "lib"), join22(programFiles, "Microsoft Machine Learning", "lib"), join22(programFilesX86, "onnxruntime", "lib"), ...(() => {
15165
15484
  const nugetPaths = [];
15166
15485
  const userProfile = process.env.USERPROFILE ?? "";
15167
15486
  if (!userProfile)
15168
15487
  return nugetPaths;
15169
- const nugetPackageDir = join21(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
15170
- if (!existsSync20(nugetPackageDir))
15488
+ const nugetPackageDir = join22(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
15489
+ if (!existsSync21(nugetPackageDir))
15171
15490
  return nugetPaths;
15172
15491
  try {
15173
- for (const entry of readdirSync10(nugetPackageDir, { withFileTypes: true })) {
15492
+ for (const entry of readdirSync11(nugetPackageDir, { withFileTypes: true })) {
15174
15493
  if (!entry.isDirectory())
15175
15494
  continue;
15176
15495
  if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
15177
15496
  continue;
15178
- nugetPaths.push(join21(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join21(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
15497
+ nugetPaths.push(join22(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join22(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
15179
15498
  }
15180
15499
  } catch {}
15181
15500
  return nugetPaths;
@@ -15207,12 +15526,12 @@ function findSystemOnnxRuntime2() {
15207
15526
  return unknownVersionPaths[0] ?? null;
15208
15527
  }
15209
15528
  function findCachedOnnxRuntime(storageDir) {
15210
- const ortDir = join21(storageDir, "onnxruntime", ONNX_RUNTIME_VERSION);
15529
+ const ortDir = join22(storageDir, "onnxruntime", ONNX_RUNTIME_VERSION);
15211
15530
  const libName = getOnnxLibraryName();
15212
- if (existsSync20(join21(ortDir, libName)))
15531
+ if (existsSync21(join22(ortDir, libName)))
15213
15532
  return ortDir;
15214
- const libSubdir = join21(ortDir, "lib");
15215
- if (existsSync20(join21(libSubdir, libName)))
15533
+ const libSubdir = join22(ortDir, "lib");
15534
+ if (existsSync21(join22(libSubdir, libName)))
15216
15535
  return libSubdir;
15217
15536
  return null;
15218
15537
  }
@@ -15233,11 +15552,11 @@ function parseOrtVersionFromDirectoryPath(value) {
15233
15552
  return null;
15234
15553
  }
15235
15554
  function detectOrtVersion(libDir) {
15236
- if (!existsSync20(libDir))
15555
+ if (!existsSync21(libDir))
15237
15556
  return null;
15238
15557
  const libName = getOnnxLibraryName();
15239
15558
  try {
15240
- const entries = readdirSync10(libDir);
15559
+ const entries = readdirSync11(libDir);
15241
15560
  const barePrefix = libName.replace(/\.(so|dylib|dll)$/, "");
15242
15561
  const expectedPrefix = process.platform === "win32" ? barePrefix.toLowerCase() : barePrefix;
15243
15562
  for (const entry of entries) {
@@ -15248,8 +15567,8 @@ function detectOrtVersion(libDir) {
15248
15567
  if (version)
15249
15568
  return version;
15250
15569
  }
15251
- const base = join21(libDir, libName);
15252
- if (existsSync20(base)) {
15570
+ const base = join22(libDir, libName);
15571
+ if (existsSync21(base)) {
15253
15572
  try {
15254
15573
  const real = realpathSync4(base);
15255
15574
  const version = parseOrtVersionFromPath(real) ?? parseOrtVersionFromDirectoryPath(real);
@@ -15277,14 +15596,16 @@ function isOrtVersionCompatible(version) {
15277
15596
  return minor >= REQUIRED_ORT_MIN_MINOR2;
15278
15597
  }
15279
15598
  var ONNX_RUNTIME_VERSION = "1.24.4", INVALID_ORT_VERSION2 = "<invalid>", REQUIRED_ORT_MAJOR2 = 1, REQUIRED_ORT_MIN_MINOR2 = 20;
15280
- var init_onnx = () => {};
15599
+ var init_onnx = __esm(() => {
15600
+ init_dist2();
15601
+ });
15281
15602
 
15282
15603
  // src/lib/diagnostics.ts
15283
15604
  import {
15284
15605
  accessSync as accessSync3,
15285
15606
  closeSync as closeSync6,
15286
15607
  constants as constants3,
15287
- existsSync as existsSync21,
15608
+ existsSync as existsSync22,
15288
15609
  openSync as openSync6,
15289
15610
  readSync as readSync4,
15290
15611
  statSync as statSync12
@@ -15323,7 +15644,7 @@ async function diagnoseHarness(adapter) {
15323
15644
  const logPath = adapter.getLogFile();
15324
15645
  const pluginCache = adapter.getPluginCacheInfo();
15325
15646
  const storageAccessible = (() => {
15326
- if (!existsSync21(storage))
15647
+ if (!existsSync22(storage))
15327
15648
  return false;
15328
15649
  try {
15329
15650
  accessSync3(storage, constants3.R_OK | constants3.W_OK);
@@ -15348,7 +15669,7 @@ async function diagnoseHarness(adapter) {
15348
15669
  pluginRegistered: adapter.hasPluginEntry(),
15349
15670
  configPaths,
15350
15671
  aftConfig: {
15351
- exists: existsSync21(configPaths.aftConfig),
15672
+ exists: existsSync22(configPaths.aftConfig),
15352
15673
  ...aftConfigRead.error ? { parseError: aftConfigRead.error } : {},
15353
15674
  enabled: aftEnabled,
15354
15675
  ...aftEnabledSource ? { enabledSource: aftEnabledSource } : {},
@@ -15357,7 +15678,7 @@ async function diagnoseHarness(adapter) {
15357
15678
  pluginCache,
15358
15679
  storageDir: {
15359
15680
  path: storage,
15360
- exists: existsSync21(storage),
15681
+ exists: existsSync22(storage),
15361
15682
  accessible: storageAccessible,
15362
15683
  sizesByKey: describeStorage,
15363
15684
  ...legacyDuplication.totalPartitions > 0 ? { legacyDuplication } : {}
@@ -15374,12 +15695,13 @@ async function diagnoseHarness(adapter) {
15374
15695
  cachedCompatible: cachedVersion ? isOrtVersionCompatible(cachedVersion) : null,
15375
15696
  platform: `${process.platform}-${process.arch}`,
15376
15697
  installHint: getManualInstallHint2(),
15698
+ autoDownloadable: isOrtAutoDownloadSupported(),
15377
15699
  requirement: `>=${REQUIRED_ORT_MAJOR2}.${REQUIRED_ORT_MIN_MINOR2}`
15378
15700
  },
15379
15701
  logFile: {
15380
15702
  path: logPath,
15381
- exists: existsSync21(logPath),
15382
- sizeKb: existsSync21(logPath) ? Math.round(statSync12(logPath).size / 1024) : 0
15703
+ exists: existsSync22(logPath),
15704
+ sizeKb: existsSync22(logPath) ? Math.round(statSync12(logPath).size / 1024) : 0
15383
15705
  }
15384
15706
  };
15385
15707
  }
@@ -15536,7 +15858,7 @@ function collectDiagnosticIssues(report) {
15536
15858
  severity: "medium",
15537
15859
  scope: h.displayName,
15538
15860
  message: h.onnxRuntime.ignoredSystemPath ? `ONNX Runtime at ${h.onnxRuntime.ignoredSystemPath} has a ${h.onnxRuntime.ignoredSystemReason}; no compatible runtime was detected.` : "ONNX Runtime is required for semantic search but was not detected.",
15539
- remediation: `Run \`${CLI} doctor --fix\` or install ONNX Runtime manually (${h.onnxRuntime.installHint}).`
15861
+ remediation: h.onnxRuntime.autoDownloadable ? `Run \`${CLI} doctor --fix\` to download the AFT-managed ONNX Runtime.` : `Install ONNX Runtime manually (${h.onnxRuntime.installHint}); AFT has no ONNX Runtime download for ${h.onnxRuntime.platform}.`
15540
15862
  });
15541
15863
  }
15542
15864
  if (h.onnxRuntime.cachedCompatible === false) {
@@ -15576,7 +15898,7 @@ function formatDiagnosticIssuesSection(report) {
15576
15898
  return lines;
15577
15899
  }
15578
15900
  function tailLogFile(path, lines) {
15579
- if (!existsSync21(path))
15901
+ if (!existsSync22(path))
15580
15902
  return "";
15581
15903
  if (lines <= 0)
15582
15904
  return "";
@@ -15765,23 +16087,24 @@ var init_issue_body = __esm(() => {
15765
16087
  });
15766
16088
 
15767
16089
  // src/lib/onnx-fix.ts
15768
- import { existsSync as existsSync22, rmSync as rmSync6 } from "node:fs";
15769
- import { join as join22 } from "node:path";
16090
+ import { randomBytes } from "node:crypto";
16091
+ import { existsSync as existsSync23, mkdirSync as mkdirSync6, renameSync as renameSync4, rmSync as rmSync6 } from "node:fs";
16092
+ import { isAbsolute as isAbsolute5, join as join23, relative as relative2 } from "node:path";
15770
16093
  function findOnnxFixCandidates(report) {
15771
16094
  const candidates = [];
15772
16095
  for (const harness of report.harnesses) {
15773
16096
  if (!harness.onnxRuntime.required)
15774
16097
  continue;
15775
- const storageOnnxDir = join22(harness.storageDir.path, "onnxruntime");
16098
+ const storageOnnxDir = join23(harness.storageDir.path, "onnxruntime");
15776
16099
  const systemTooOld = harness.onnxRuntime.systemPath !== null && harness.onnxRuntime.systemCompatible === false;
15777
16100
  const cachedTooOld = harness.onnxRuntime.cachedPath !== null && harness.onnxRuntime.cachedCompatible === false;
15778
16101
  const hasCompatibleCached = harness.onnxRuntime.cachedCompatible === true;
15779
16102
  if (cachedTooOld) {
15780
16103
  candidates.push({
15781
16104
  harness,
15782
- reason: `cached ONNX Runtime at ${harness.onnxRuntime.cachedPath} is v${harness.onnxRuntime.cachedVersion}, but AFT requires ${harness.onnxRuntime.requirement}. Clearing it allows an immediate managed download.`,
16105
+ reason: `cached ONNX Runtime at ${harness.onnxRuntime.cachedPath} is v${harness.onnxRuntime.cachedVersion}, but AFT requires ${harness.onnxRuntime.requirement}. AFT will stage and verify a managed replacement before removing it.`,
15783
16106
  storageOnnxDir,
15784
- storageOnnxBytes: existsSync22(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
16107
+ storageOnnxBytes: existsSync23(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
15785
16108
  });
15786
16109
  continue;
15787
16110
  }
@@ -15790,7 +16113,7 @@ function findOnnxFixCandidates(report) {
15790
16113
  harness,
15791
16114
  reason: `system ONNX Runtime at ${harness.onnxRuntime.systemPath} is v${harness.onnxRuntime.systemVersion}, but AFT requires ${harness.onnxRuntime.requirement}, and no AFT-managed install is present. AFT will leave the system copy untouched and download v1.24 into managed storage.`,
15792
16115
  storageOnnxDir,
15793
- storageOnnxBytes: existsSync22(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
16116
+ storageOnnxBytes: existsSync23(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
15794
16117
  });
15795
16118
  continue;
15796
16119
  }
@@ -15800,12 +16123,61 @@ function findOnnxFixCandidates(report) {
15800
16123
  harness,
15801
16124
  reason: `no compatible ONNX Runtime is installed.${ignoredCopy} AFT will download v1.24 into managed storage.`,
15802
16125
  storageOnnxDir,
15803
- storageOnnxBytes: existsSync22(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
16126
+ storageOnnxBytes: existsSync23(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
15804
16127
  });
15805
16128
  }
15806
16129
  }
15807
16130
  return candidates;
15808
16131
  }
16132
+ async function installManagedReplacement(candidate, ensureFn, rmFn) {
16133
+ const suffix = `${process.pid}.${Date.now().toString(36)}.${randomBytes(6).toString("hex")}`;
16134
+ const stagingStorageDir = `${candidate.harness.storageDir.path}.onnx-fix.tmp.${suffix}`;
16135
+ const stagedOnnxDir = join23(stagingStorageDir, "onnxruntime");
16136
+ const backupDir = `${candidate.storageOnnxDir}.backup.${suffix}`;
16137
+ try {
16138
+ const stagedInstalledPath = await ensureFn(stagingStorageDir);
16139
+ if (!stagedInstalledPath || !existsSync23(stagedInstalledPath)) {
16140
+ throw new Error("managed ONNX Runtime download was unavailable");
16141
+ }
16142
+ const installedRelativePath = relative2(stagedOnnxDir, stagedInstalledPath);
16143
+ if (isAbsolute5(installedRelativePath) || installedRelativePath === ".." || installedRelativePath.startsWith("../") || installedRelativePath.startsWith("..\\")) {
16144
+ throw new Error(`managed ONNX Runtime was installed outside staging: ${stagedInstalledPath}`);
16145
+ }
16146
+ mkdirSync6(candidate.harness.storageDir.path, { recursive: true });
16147
+ const hadPrevious = existsSync23(candidate.storageOnnxDir);
16148
+ if (hadPrevious)
16149
+ renameSync4(candidate.storageOnnxDir, backupDir);
16150
+ try {
16151
+ renameSync4(stagedOnnxDir, candidate.storageOnnxDir);
16152
+ } catch (installError) {
16153
+ if (hadPrevious) {
16154
+ try {
16155
+ renameSync4(backupDir, candidate.storageOnnxDir);
16156
+ } catch (restoreError) {
16157
+ throw new Error(`managed ONNX Runtime replacement failed and the previous runtime could not be restored from ${backupDir}: ${restoreError}`, { cause: installError });
16158
+ }
16159
+ }
16160
+ throw installError;
16161
+ }
16162
+ let cleanupError;
16163
+ if (hadPrevious) {
16164
+ try {
16165
+ rmFn(backupDir, { recursive: true, force: true });
16166
+ } catch (err) {
16167
+ cleanupError = err instanceof Error ? err.message : String(err);
16168
+ }
16169
+ }
16170
+ return {
16171
+ installedPath: join23(candidate.storageOnnxDir, installedRelativePath),
16172
+ clearedPrevious: hadPrevious && cleanupError === undefined,
16173
+ cleanupError
16174
+ };
16175
+ } finally {
16176
+ try {
16177
+ rmSync6(stagingStorageDir, { recursive: true, force: true });
16178
+ } catch {}
16179
+ }
16180
+ }
15809
16181
  async function runOnnxFix(adapters, report, options = {}) {
15810
16182
  const candidates = findOnnxFixCandidates(report);
15811
16183
  if (candidates.length === 0)
@@ -15814,12 +16186,12 @@ async function runOnnxFix(adapters, report, options = {}) {
15814
16186
  for (const candidate of candidates) {
15815
16187
  log2.info(` • ${candidate.harness.displayName}: ${candidate.reason}`);
15816
16188
  if (candidate.storageOnnxBytes > 0) {
15817
- log2.info(` will delete: ${candidate.storageOnnxDir} (${formatBytes(candidate.storageOnnxBytes)})`);
16189
+ log2.info(` will replace after a verified download: ${candidate.storageOnnxDir} (${formatBytes(candidate.storageOnnxBytes)})`);
15818
16190
  } else {
15819
16191
  log2.info(" no AFT-managed ONNX cache to delete");
15820
16192
  }
15821
16193
  }
15822
- note("This NEVER touches system paths like /usr/lib or C:\\Windows\\System32. It only replaces AFT's own ONNX cache, then downloads the compatible managed runtime.", "Safe operation");
16194
+ note("This NEVER touches system paths like /usr/lib or C:\\Windows\\System32. It downloads and verifies a replacement before swapping AFT's own ONNX cache.", "Safe operation");
15823
16195
  const confirmFn = options.confirmFn ?? confirm2;
15824
16196
  const proceed = options.yes ? true : await confirmFn("Proceed with the fixes above?", true);
15825
16197
  if (!proceed) {
@@ -15830,30 +16202,20 @@ async function runOnnxFix(adapters, report, options = {}) {
15830
16202
  const rmFn = options.rmFn ?? rmSync6;
15831
16203
  const ensureFn = options.ensureFn ?? ensureOnnxRuntime;
15832
16204
  for (const candidate of candidates) {
15833
- if (existsSync22(candidate.storageOnnxDir)) {
15834
- try {
15835
- rmFn(candidate.storageOnnxDir, { recursive: true, force: true });
16205
+ try {
16206
+ log2.info(`${candidate.harness.displayName}: downloading managed ONNX Runtime…`);
16207
+ const replacement = await installManagedReplacement(candidate, ensureFn, rmFn);
16208
+ if (replacement.clearedPrevious) {
15836
16209
  result.cleared += 1;
15837
16210
  result.bytesReclaimed += candidate.storageOnnxBytes;
15838
- log2.success(`${candidate.harness.displayName}: cleared ${candidate.storageOnnxDir} (reclaimed ${formatBytes(candidate.storageOnnxBytes)})`);
15839
- } catch (err) {
15840
- const message = err instanceof Error ? err.message : String(err);
15841
- log2.error(`${candidate.harness.displayName}: failed to clear ${candidate.storageOnnxDir}: ${message}`);
15842
- result.errors.push({ path: candidate.storageOnnxDir, error: message });
15843
- continue;
16211
+ log2.success(`${candidate.harness.displayName}: replaced ${candidate.storageOnnxDir} (reclaimed ${formatBytes(candidate.storageOnnxBytes)})`);
15844
16212
  }
15845
- }
15846
- try {
15847
- log2.info(`${candidate.harness.displayName}: downloading managed ONNX Runtime…`);
15848
- const installedPath = await ensureFn(candidate.harness.storageDir.path);
15849
- if (!installedPath) {
15850
- const message = "managed ONNX Runtime download was unavailable";
15851
- log2.error(`${candidate.harness.displayName}: ${message}`);
15852
- result.errors.push({ path: candidate.storageOnnxDir, error: message });
15853
- continue;
16213
+ if (replacement.cleanupError) {
16214
+ log2.warn(`${candidate.harness.displayName}: installed the replacement but could not remove the previous runtime backup: ${replacement.cleanupError}`);
16215
+ result.errors.push({ path: candidate.storageOnnxDir, error: replacement.cleanupError });
15854
16216
  }
15855
16217
  result.installed += 1;
15856
- log2.success(`${candidate.harness.displayName}: ONNX Runtime installed at ${installedPath}`);
16218
+ log2.success(`${candidate.harness.displayName}: ONNX Runtime installed at ${replacement.installedPath}`);
15857
16219
  } catch (err) {
15858
16220
  const message = err instanceof Error ? err.message : String(err);
15859
16221
  log2.error(`${candidate.harness.displayName}: ONNX Runtime download failed: ${message}`);
@@ -15872,10 +16234,10 @@ var init_onnx_fix = __esm(() => {
15872
16234
  });
15873
16235
 
15874
16236
  // src/lib/sessions.ts
15875
- import { existsSync as existsSync23, readdirSync as readdirSync11, readFileSync as readFileSync12, statSync as statSync13 } from "node:fs";
16237
+ import { existsSync as existsSync24, readdirSync as readdirSync12, readFileSync as readFileSync12, statSync as statSync13 } from "node:fs";
15876
16238
  import { createRequire as createRequire4 } from "node:module";
15877
16239
  import { homedir as homedir13 } from "node:os";
15878
- import { basename as basename3, join as join23 } from "node:path";
16240
+ import { basename as basename3, join as join24 } from "node:path";
15879
16241
  function listRecentSessions(adapter) {
15880
16242
  try {
15881
16243
  if (adapter.kind === "opencode")
@@ -15906,8 +16268,8 @@ function mapOpenCodeSessionRows(rows) {
15906
16268
  }).filter((session) => session !== null).sort((a, b) => b.lastActivity - a.lastActivity).slice(0, MAX_RECENT_SESSIONS);
15907
16269
  }
15908
16270
  function listRecentOpenCodeSessions() {
15909
- const dbPath = join23(getXdgDataHome(), "opencode", "opencode.db");
15910
- if (!existsSync23(dbPath))
16271
+ const dbPath = join24(getXdgDataHome(), "opencode", "opencode.db");
16272
+ if (!existsSync24(dbPath))
15911
16273
  return [];
15912
16274
  let db = null;
15913
16275
  try {
@@ -15926,10 +16288,10 @@ function listRecentOpenCodeSessions() {
15926
16288
  }
15927
16289
  function getXdgDataHome() {
15928
16290
  const xdgDataHome = process.env.XDG_DATA_HOME;
15929
- return xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : join23(homedir13(), ".local", "share");
16291
+ return xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : join24(homedir13(), ".local", "share");
15930
16292
  }
15931
16293
  function listRecentPiSessions() {
15932
- return listPiSessionsFromDir(join23(getHomeDir(), ".pi", "agent", "sessions"));
16294
+ return listPiSessionsFromDir(join24(getHomeDir(), ".pi", "agent", "sessions"));
15933
16295
  }
15934
16296
  function getHomeDir() {
15935
16297
  const envHome = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
@@ -15937,7 +16299,7 @@ function getHomeDir() {
15937
16299
  }
15938
16300
  function listPiSessionsFromDir(sessionsDir) {
15939
16301
  try {
15940
- if (!existsSync23(sessionsDir))
16302
+ if (!existsSync24(sessionsDir))
15941
16303
  return [];
15942
16304
  const files = collectJsonlFiles(sessionsDir).map((filePath) => {
15943
16305
  try {
@@ -15970,12 +16332,12 @@ function collectJsonlFiles(root) {
15970
16332
  continue;
15971
16333
  let entries;
15972
16334
  try {
15973
- entries = readdirSync11(dir, { withFileTypes: true });
16335
+ entries = readdirSync12(dir, { withFileTypes: true });
15974
16336
  } catch {
15975
16337
  continue;
15976
16338
  }
15977
16339
  for (const entry of entries) {
15978
- const path = join23(dir, entry.name);
16340
+ const path = join24(dir, entry.name);
15979
16341
  if (entry.isDirectory()) {
15980
16342
  stack.push(path);
15981
16343
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -16062,8 +16424,8 @@ var init_sessions = __esm(() => {
16062
16424
  import { spawn as spawn4, spawnSync as spawnSync9 } from "node:child_process";
16063
16425
  import {
16064
16426
  chmodSync as chmodSync4,
16065
- existsSync as existsSync24,
16066
- mkdirSync as mkdirSync6,
16427
+ existsSync as existsSync25,
16428
+ mkdirSync as mkdirSync7,
16067
16429
  mkdtempSync as mkdtempSync2,
16068
16430
  readFileSync as readFileSync13,
16069
16431
  realpathSync as realpathSync5,
@@ -16072,7 +16434,7 @@ import {
16072
16434
  writeFileSync as writeFileSync3
16073
16435
  } from "node:fs";
16074
16436
  import { tmpdir as tmpdir2 } from "node:os";
16075
- import { join as join24 } from "node:path";
16437
+ import { join as join25 } from "node:path";
16076
16438
  function openCodeAdapter(adapters) {
16077
16439
  return adapters.find((adapter) => adapter.kind === "opencode");
16078
16440
  }
@@ -16080,9 +16442,11 @@ function detectOpenCodeForCommand(adapters, override) {
16080
16442
  const adapter = openCodeAdapter(adapters);
16081
16443
  if (!adapter)
16082
16444
  return null;
16083
- if (override)
16084
- return override();
16085
- return adapter instanceof OpenCodeAdapter ? adapter.detectHostGeneration() : null;
16445
+ if (!(adapter instanceof OpenCodeAdapter))
16446
+ return override ? override() : null;
16447
+ const detection = override ? override() : adapter.detectHostGeneration();
16448
+ adapter.useHostDetection(detection);
16449
+ return detection;
16086
16450
  }
16087
16451
  function refuseAmbiguousOpenCodeWrites(detection) {
16088
16452
  if (detection?.status !== "ambiguous")
@@ -16107,7 +16471,7 @@ function buildDoctorProfileArgs(argv) {
16107
16471
  function runDoctorProfile(argv) {
16108
16472
  const binary = findAftBinary();
16109
16473
  if (!binary) {
16110
- console.error("aft doctor --profile requires a native AFT binary; run `aft doctor --fix` first.");
16474
+ console.error(missingAftBinaryMessage("aft doctor --profile"));
16111
16475
  return 1;
16112
16476
  }
16113
16477
  const result = spawnSync9(binary, buildDoctorProfileArgs(argv), {
@@ -16126,7 +16490,7 @@ async function runDoctor(options) {
16126
16490
  }
16127
16491
  intro(`${CLI} doctor`);
16128
16492
  if (options.fix) {
16129
- return runFixFlow(options.argv, options.detectOpenCodeHost, options.resolveAdapters, options.collectDiagnostics);
16493
+ return runFixFlow(options.argv, options.detectOpenCodeHost, options.resolveAdapters, options.collectDiagnostics, options.applyOnnxFix);
16130
16494
  }
16131
16495
  if (options.clear) {
16132
16496
  return runClearFlow(options.argv);
@@ -16147,7 +16511,8 @@ async function runDoctor(options) {
16147
16511
  logPath: opencodeHarness.logFile.path,
16148
16512
  pluginCachePath: opencodeHarness.pluginCache.path,
16149
16513
  cachedPluginVersion: opencodeHarness.pluginCache.cached,
16150
- expectedPluginEntry: opencodeAdapter.pluginEntryWithVersion
16514
+ expectedPluginEntry: hostDetection.status === "v1" ? `${opencodeAdapter.pluginPackageName}@latest` : opencodeAdapter.pluginEntryWithVersion,
16515
+ acceptExplicitPluginVersion: hostDetection.status === "v1"
16151
16516
  }) : null;
16152
16517
  log2.info(`AFT CLI v${report.cliVersion}, AFT binary ${report.binaryVersion ?? "unknown"}`);
16153
16518
  if (!report.binaryVersion) {
@@ -16432,7 +16797,7 @@ function clearOldBinaries() {
16432
16797
  errors: [],
16433
16798
  keptVersion: keepTag
16434
16799
  };
16435
- if (!existsSync24(info.path)) {
16800
+ if (!existsSync25(info.path)) {
16436
16801
  log2.info(`Binary cache: nothing to clear at ${info.path}`);
16437
16802
  return result;
16438
16803
  }
@@ -16442,7 +16807,7 @@ function clearOldBinaries() {
16442
16807
  return result;
16443
16808
  }
16444
16809
  for (const version of stale) {
16445
- const dir = join24(info.path, version);
16810
+ const dir = join25(info.path, version);
16446
16811
  let bytes = 0;
16447
16812
  try {
16448
16813
  bytes = statSync14(dir).isDirectory() ? dirSize(dir) : 0;
@@ -16732,15 +17097,16 @@ function logUnmatchedBinaryCandidates(expectedVersion) {
16732
17097
  log2.warn(` unmatched: ${candidate.path} reported v${candidate.version ?? "unknown"}`);
16733
17098
  }
16734
17099
  }
16735
- async function runFixFlow(argv, detectOpenCodeHost, resolveAdapters = resolveAdaptersForCommand, collect = collectDiagnostics) {
17100
+ async function runFixFlow(argv, detectOpenCodeHost, resolveAdapters = resolveAdaptersForCommand, collect = collectDiagnostics, applyOnnxFix = runOnnxFix) {
16736
17101
  const adapters = await resolveAdapters(argv, {
16737
17102
  allowMulti: false,
16738
17103
  verb: "auto-fix issues for"
16739
17104
  });
16740
17105
  const hostDetection = detectOpenCodeForCommand(adapters, detectOpenCodeHost);
16741
- if (refuseAmbiguousOpenCodeWrites(hostDetection)) {
16742
- outro("Done — no changes made.");
16743
- return 1;
17106
+ const configWritesRefused = refuseAmbiguousOpenCodeWrites(hostDetection);
17107
+ const skipped = [];
17108
+ if (configWritesRefused) {
17109
+ skipped.push("plugin registration in the OpenCode config (leave one OpenCode generation on PATH and re-run)");
16744
17110
  }
16745
17111
  if (hostDetection?.status === "unknown") {
16746
17112
  log2.warn("OpenCode: host generation is unavailable; applying only generation-independent exact-pin config fixes.");
@@ -16752,7 +17118,7 @@ async function runFixFlow(argv, detectOpenCodeHost, resolveAdapters = resolveAda
16752
17118
  if (!report.binaryVersion) {
16753
17119
  logUnmatchedBinaryCandidates(report.cliVersion);
16754
17120
  }
16755
- const plan = buildDoctorFixPlan(adapters, report);
17121
+ const plan = buildDoctorFixPlan(adapters, report).filter((item) => !(configWritesRefused && item.kind === "plugin"));
16756
17122
  if (plan.length > 0) {
16757
17123
  log2.warn("Planned changes:");
16758
17124
  for (const item of plan) {
@@ -16764,7 +17130,7 @@ async function runFixFlow(argv, detectOpenCodeHost, resolveAdapters = resolveAda
16764
17130
  return 0;
16765
17131
  }
16766
17132
  }
16767
- await fixPluginEntries(adapters);
17133
+ const pluginEntrySummary = configWritesRefused ? { changed: 0, errors: 0 } : await fixPluginEntries(adapters);
16768
17134
  const pluginUpdateSummary = await applyPluginUpdates(findPluginUpdateTargets(adapters, report));
16769
17135
  const storageSummary = ensureStorageDirsForRegisteredPlugins(adapters);
16770
17136
  const schemaSummary = applySchemaFixes(findSchemaFixTargets(adapters));
@@ -16775,6 +17141,7 @@ async function runFixFlow(argv, detectOpenCodeHost, resolveAdapters = resolveAda
16775
17141
  const shouldDownload = await confirmBinaryDownloadDespitePluginSkew(report, argv);
16776
17142
  if (!shouldDownload) {
16777
17143
  binaryDownloadSkipped = true;
17144
+ skipped.push("aft binary download (declined because the installed plugin would not use it)");
16778
17145
  } else {
16779
17146
  log2.info("AFT binary not found. Downloading…");
16780
17147
  try {
@@ -16794,20 +17161,37 @@ async function runFixFlow(argv, detectOpenCodeHost, resolveAdapters = resolveAda
16794
17161
  }
16795
17162
  }
16796
17163
  }
16797
- const onnxResult = await runOnnxFix(adapters, report, { yes: true });
16798
- if (onnxResult === null && !binaryDownloaded && !binaryDownloadSkipped && !binaryDownloadError && storageSummary.created === 0 && storageSummary.errors === 0 && schemaSummary.changed === 0 && schemaSummary.errors === 0 && pluginUpdateSummary.updated === 0 && pluginUpdateSummary.errors === 0) {
17164
+ const onnxResult = await applyOnnxFix(adapters, report, { yes: true });
17165
+ const applied = [];
17166
+ if (pluginEntrySummary.changed > 0)
17167
+ applied.push("plugin registration");
17168
+ if (pluginUpdateSummary.updated > 0)
17169
+ applied.push("plugin package update");
17170
+ if (storageSummary.created > 0)
17171
+ applied.push("AFT storage directory");
17172
+ if (schemaSummary.changed > 0)
17173
+ applied.push("AFT config $schema");
17174
+ if (binaryDownloaded)
17175
+ applied.push("aft binary download");
17176
+ if ((onnxResult?.installed ?? 0) > 0)
17177
+ applied.push("ONNX Runtime install");
17178
+ const nothingAttempted = onnxResult === null && !binaryDownloaded && !binaryDownloadSkipped && !binaryDownloadError && storageSummary.created === 0 && storageSummary.errors === 0 && schemaSummary.changed === 0 && schemaSummary.errors === 0 && pluginEntrySummary.changed === 0 && pluginEntrySummary.errors === 0 && pluginUpdateSummary.updated === 0 && pluginUpdateSummary.errors === 0;
17179
+ if (nothingAttempted && skipped.length === 0) {
16799
17180
  log2.info("No auto-fixable issues detected.");
16800
17181
  note(`If you're still seeing 'Semantic Index: failed' in the TUI sidebar, run \`${CLI} doctor\` (without --fix) for a full diagnostic dump.`, "Tip");
16801
- const afterReport = await collect(adapters);
16802
- const stillHasProblems = hasDoctorProblems(afterReport);
16803
- outro(stillHasProblems ? "Done — some issues remain." : "Done.");
16804
- return stillHasProblems ? 1 : 0;
17182
+ } else {
17183
+ logDoctorFixSummary(applied, skipped);
16805
17184
  }
16806
- const hadErrors = (onnxResult?.errors.length ?? 0) > 0 || binaryDownloadError !== null || storageSummary.errors > 0 || schemaSummary.errors > 0 || pluginUpdateSummary.errors > 0;
16807
- const afterReport = await collectDiagnostics(adapters);
17185
+ const hadErrors = (onnxResult?.errors.length ?? 0) > 0 || binaryDownloadError !== null || storageSummary.errors > 0 || schemaSummary.errors > 0 || pluginEntrySummary.errors > 0 || pluginUpdateSummary.errors > 0;
17186
+ const afterReport = await collect(adapters);
16808
17187
  const stillHasProblems = hasDoctorProblems(afterReport);
16809
- outro(hadErrors ? "Done — some fixes failed." : stillHasProblems ? "Done — some issues remain." : "Done.");
16810
- return hadErrors || stillHasProblems ? 1 : 0;
17188
+ outro(hadErrors ? "Done — some fixes failed." : skipped.length > 0 ? "Done — some fixes were skipped." : stillHasProblems ? "Done — some issues remain." : "Done.");
17189
+ return hadErrors || stillHasProblems || configWritesRefused ? 1 : 0;
17190
+ }
17191
+ function logDoctorFixSummary(applied, skipped) {
17192
+ log2.info(applied.length > 0 ? `Applied: ${applied.join(", ")}.` : "Applied: no changes.");
17193
+ if (skipped.length > 0)
17194
+ log2.warn(`Skipped: ${skipped.join("; ")}.`);
16811
17195
  }
16812
17196
  function logDoctorIssues(report) {
16813
17197
  const lines = formatDiagnosticIssuesSection(report);
@@ -16861,9 +17245,9 @@ function ensureStorageDirsForRegisteredPlugins(adapters) {
16861
17245
  if (!adapter.isInstalled() || !adapter.hasPluginEntry())
16862
17246
  continue;
16863
17247
  const storageDir = adapter.getStorageDir();
16864
- if (existsSync24(storageDir))
17248
+ if (existsSync25(storageDir))
16865
17249
  continue;
16866
- mkdirSync6(storageDir, { recursive: true });
17250
+ mkdirSync7(storageDir, { recursive: true });
16867
17251
  summary.created += 1;
16868
17252
  log2.success(`${adapter.displayName}: created AFT storage directory at ${storageDir}`);
16869
17253
  } catch (err) {
@@ -16910,19 +17294,27 @@ function reportLspCacheClear(cleanup) {
16910
17294
  }
16911
17295
  }
16912
17296
  async function fixPluginEntries(adapters) {
17297
+ const summary = { changed: 0, errors: 0 };
16913
17298
  for (const adapter of adapters) {
16914
- await maybeFixPlugin(adapter);
17299
+ const result = await maybeFixPlugin(adapter);
17300
+ summary.changed += result.changed;
17301
+ summary.errors += result.errors;
16915
17302
  }
17303
+ return summary;
16916
17304
  }
16917
17305
  async function maybeFixPlugin(adapter) {
17306
+ const summary = { changed: 0, errors: 0 };
16918
17307
  if (!adapter.isInstalled())
16919
- return;
17308
+ return summary;
16920
17309
  if (!adapter.hasPluginEntry() || adapterConfigNeedsUpdate(adapter)) {
16921
17310
  log2.info(`${adapter.displayName}: attempting to register or update plugin config…`);
16922
17311
  const result = await adapter.ensurePluginEntry();
16923
17312
  if (result.ok) {
17313
+ if (result.action === "added" || result.action === "updated")
17314
+ summary.changed += 1;
16924
17315
  log2.success(`${adapter.displayName}: ${result.message}`);
16925
17316
  } else {
17317
+ summary.errors += 1;
16926
17318
  log2.error(`${adapter.displayName}: ${result.message}`);
16927
17319
  }
16928
17320
  }
@@ -16931,12 +17323,15 @@ async function maybeFixPlugin(adapter) {
16931
17323
  if (!hasTuiEntry || adapterConfigNeedsUpdate(adapter, true)) {
16932
17324
  const result = await adapter.ensureTuiPluginEntry();
16933
17325
  if (result.ok && (result.action === "added" || result.action === "updated")) {
17326
+ summary.changed += 1;
16934
17327
  log2.success(`${adapter.displayName}: ${result.message}`);
16935
17328
  } else if (!result.ok) {
17329
+ summary.errors += 1;
16936
17330
  log2.error(`${adapter.displayName}: ${result.message}`);
16937
17331
  }
16938
17332
  }
16939
17333
  }
17334
+ return summary;
16940
17335
  }
16941
17336
  function describeAdapterInstallHint(kind) {
16942
17337
  if (kind === "opencode")
@@ -17011,11 +17406,11 @@ function deriveIssueTitleFromBody(body) {
17011
17406
  function writeIssueReviewFile(body) {
17012
17407
  let reviewDir = null;
17013
17408
  try {
17014
- reviewDir = mkdtempSync2(join24(tmpdir2(), "aft-issue-"));
17409
+ reviewDir = mkdtempSync2(join25(tmpdir2(), "aft-issue-"));
17015
17410
  if (process.platform !== "win32") {
17016
17411
  chmodSync4(reviewDir, 448);
17017
17412
  }
17018
- const outPath = join24(reviewDir, "issue.md");
17413
+ const outPath = join25(reviewDir, "issue.md");
17019
17414
  writeFileSync3(outPath, `${body}
17020
17415
  `, { encoding: "utf8", mode: 384, flag: "wx" });
17021
17416
  return { path: outPath, realPath: realpathSync5(outPath) };
@@ -17217,21 +17612,21 @@ var init_doctor = __esm(async () => {
17217
17612
  });
17218
17613
 
17219
17614
  // src/commands/lsp.ts
17220
- import { existsSync as existsSync25, readdirSync as readdirSync12, statSync as statSync15 } from "node:fs";
17615
+ import { existsSync as existsSync26, readdirSync as readdirSync13, statSync as statSync15 } from "node:fs";
17221
17616
  import { createRequire as createRequire5 } from "node:module";
17222
- import { dirname as dirname8, join as join25, resolve as resolve11 } from "node:path";
17617
+ import { dirname as dirname8, join as join26, resolve as resolve11 } from "node:path";
17223
17618
  function findProjectRootForFile(filePath, fallbackCwd = process.cwd()) {
17224
17619
  const resolvedFile = resolve11(fallbackCwd, filePath);
17225
17620
  let dir = dirname8(resolvedFile);
17226
17621
  try {
17227
- if (existsSync25(resolvedFile) && statSync15(resolvedFile).isDirectory()) {
17622
+ if (existsSync26(resolvedFile) && statSync15(resolvedFile).isDirectory()) {
17228
17623
  dir = resolvedFile;
17229
17624
  }
17230
17625
  } catch {
17231
17626
  dir = dirname8(resolvedFile);
17232
17627
  }
17233
17628
  while (true) {
17234
- if (PROJECT_ROOT_MARKERS.some((marker) => existsSync25(join25(dir, marker)))) {
17629
+ if (PROJECT_ROOT_MARKERS.some((marker) => existsSync26(join26(dir, marker)))) {
17235
17630
  return dir;
17236
17631
  }
17237
17632
  const parent = dirname8(dir);
@@ -17380,18 +17775,18 @@ function buildConfigureParams(adapter, projectRoot) {
17380
17775
  function inferLspPathsExtra(_lsp) {
17381
17776
  const paths = new Set;
17382
17777
  for (const entry of childDirs(getAftLspPackagesDir())) {
17383
- paths.add(join25(entry, "node_modules", ".bin"));
17778
+ paths.add(join26(entry, "node_modules", ".bin"));
17384
17779
  }
17385
17780
  for (const entry of childDirs(getAftLspBinariesDir())) {
17386
- paths.add(join25(entry, "bin"));
17781
+ paths.add(join26(entry, "bin"));
17387
17782
  }
17388
17783
  return [...paths];
17389
17784
  }
17390
17785
  function childDirs(path) {
17391
- if (!existsSync25(path))
17786
+ if (!existsSync26(path))
17392
17787
  return [];
17393
17788
  try {
17394
- return readdirSync12(path).map((entry) => join25(path, entry)).filter((entry) => {
17789
+ return readdirSync13(path).map((entry) => join26(path, entry)).filter((entry) => {
17395
17790
  try {
17396
17791
  return statSync15(entry).isDirectory();
17397
17792
  } catch {
@@ -17433,7 +17828,7 @@ function typescriptPackageWarning(response) {
17433
17828
  if (!typescriptServerSpawned || !response.project_root || diagnosticsCount > 0)
17434
17829
  return null;
17435
17830
  try {
17436
- createRequire5(join25(response.project_root, "package.json")).resolve("typescript");
17831
+ createRequire5(join26(response.project_root, "package.json")).resolve("typescript");
17437
17832
  return null;
17438
17833
  } catch {
17439
17834
  return "typescript package not resolvable from project — server will produce no diagnostics";
@@ -17473,9 +17868,9 @@ var init_lsp = __esm(async () => {
17473
17868
  });
17474
17869
 
17475
17870
  // src/commands/doctor-filters.ts
17476
- import { existsSync as existsSync26 } from "node:fs";
17871
+ import { existsSync as existsSync27 } from "node:fs";
17477
17872
  import { homedir as homedir14 } from "node:os";
17478
- import { relative as relative2, resolve as resolve12 } from "node:path";
17873
+ import { relative as relative3, resolve as resolve12 } from "node:path";
17479
17874
  function printDoctorFiltersHelp() {
17480
17875
  console.log(`Usage: ${CLI} doctor filters [--show <name>] [trust|untrust]`);
17481
17876
  console.log("");
@@ -17516,7 +17911,7 @@ async function runDoctorFilters(options) {
17516
17911
  log2.error(list.message ?? list.code ?? "list_filters failed");
17517
17912
  return 1;
17518
17913
  }
17519
- list.project_dir_exists = list.project_dir ? existsSync26(list.project_dir) : false;
17914
+ list.project_dir_exists = list.project_dir ? existsSync27(list.project_dir) : false;
17520
17915
  if (mode.kind === "list") {
17521
17916
  console.log(renderFilterList(list, projectRoot));
17522
17917
  return 0;
@@ -17698,7 +18093,7 @@ function formatHome(path) {
17698
18093
  function formatProjectPath(path, projectRoot) {
17699
18094
  if (!path)
17700
18095
  return "";
17701
- const rel = relative2(projectRoot, path);
18096
+ const rel = relative3(projectRoot, path);
17702
18097
  return rel.startsWith("..") || rel === "" ? path : `./${rel}`;
17703
18098
  }
17704
18099
  function formatSourcePath(path, projectRoot) {