@cortexkit/aft-pi 0.63.5 → 0.64.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
@@ -7871,7 +7871,7 @@ var require_src2 = __commonJS(function(exports, module) {
7871
7871
  });
7872
7872
 
7873
7873
  // src/index.ts
7874
- import { createRequire as createRequire2 } from "node:module";
7874
+ import { createRequire as createRequire3 } from "node:module";
7875
7875
 
7876
7876
  // ../aft-bridge/dist/active-logger.js
7877
7877
  var ACTIVE_LOGGER_SYMBOL = Symbol.for("aft-bridge-active-logger");
@@ -8923,9 +8923,10 @@ var BASH_HOST_FALLBACK_BANNER = "[AFT host fallback - module transport down; no
8923
8923
  var BASH_HOST_FALLBACK_MAX_OUTPUT_BYTES = 100 * 1024;
8924
8924
  var BASH_HOST_FALLBACK_MAX_TIMEOUT_MS = 10 * 60 * 1000;
8925
8925
  var BASH_HOST_FALLBACK_REFUSAL = "AFT module connection is down";
8926
- var BASH_RUNON_DESCRIPTION = 'Run on a remote build server: `linux` (2 vCPUs), or `linux,Nc` for N vCPUs such as `linux,8c`; add ",net" for outbound internet (offline by default). `windows` runs the command in Git Bash on a Windows build machine (no vCPU or network options). `macos` sends the command to a macOS machine for builds and tests that need macOS (no vCPU or network options).';
8927
- var BASH_RETURN_PATHS_DESCRIPTION = "With runon: up to 64 files to copy back from the build server after the command, as paths relative to workdir inside the project (10 MiB in total). Each verified file replaces the local file at the same path; the result lists every path as returned or not returned, with the reason.";
8928
- var BASH_RUNON_GUIDANCE = 'When remote runs are available, put `runon: "linux"` on build and test lines (cargo, bun test), including chains and pipes. Keep git, gh, interactive and file-editing commands local, and keep a line local if it needs this machine\'s own operating system or hardware (its sandbox, code signing, service manager, permission prompts or GUI frameworks) or runs a binary that already exists on this machine; a build or test that needs only macOS (its sandbox, code signing or file watching) can use runon: "macos" instead; building and testing still go remote, and a remote build leaves no binaries or target/ output in the local worktree. Add `,Nc` to request N vCPUs (`linux,4c`); without it the job gets 2 vCPUs. The job sees exactly the CPUs it gets, so ask for what the command uses: 2c (the default) for a single test binary or script, 4c for `cargo check`/clippy on one crate, 8c for a workspace test suite, and 16c for a large release build.';
8926
+ var BASH_RUNON_MEMORY_DESCRIPTION = "On Linux, add `,Ngb` to ask for N GiB of memory, for example `linux,2c,16gb` for a Rust build.";
8927
+ var BASH_RUNON_DESCRIPTION = 'Run on a remote build server: `linux` (2 vCPUs), or `linux,Nc` for N vCPUs such as `linux,8c`; add ",net" for outbound internet (offline by default). `windows` runs the command in Git Bash on a Windows build machine (no vCPU or network options). `macos` sends the command to a macOS machine for builds and tests that need macOS (no vCPU or network options).' + ` ${BASH_RUNON_MEMORY_DESCRIPTION}`;
8928
+ var BASH_RETURN_PATHS_DESCRIPTION = "With runon: up to 64 files to copy back from the build server after the command, as paths relative to workdir inside the project (up to 128 MiB in total). Each verified file replaces the local file at the same path; the result lists every path as returned or not returned, with the reason.";
8929
+ var BASH_RUNON_GUIDANCE = 'When remote runs are available, put `runon: "linux"` on build and test lines (cargo, bun test), including chains and pipes. A runon job\'s file changes are not copied back; only output, the exit code and return_paths files return. Edit locally first; name a file in return_paths to regenerate it remotely. Keep git, gh, interactive and file-editing commands local, and keep a line local if it needs this machine\'s own operating system or hardware (its sandbox, code signing, service manager, permission prompts or GUI frameworks) or runs a binary that already exists on this machine; a build or test that needs only macOS (its sandbox, code signing or file watching) can use runon: "macos" instead; building and testing still go remote, and a remote build leaves no binaries or target/ output in the local worktree. Add `,Nc` to request N vCPUs (`linux,4c`); without it the job gets 2 vCPUs. The job sees exactly the CPUs it gets, so ask for what the command uses: 2c (the default) for a single test binary or script, 4c for `cargo check`/clippy on one crate, 8c for a workspace test suite, and 16c for a large release build.';
8929
8930
  function pathKeyForPlatform(env, platform) {
8930
8931
  return platform === "win32" ? Object.keys(env).find((key) => key.toLowerCase() === "path") : "PATH";
8931
8932
  }
@@ -9180,6 +9181,7 @@ function bashAcksConfirmed(response, targets) {
9180
9181
  }
9181
9182
  // ../aft-bridge/dist/bash-permissions.js
9182
9183
  var BASH_PERMISSION_NOT_SETTLED = "bash permission not settled after {rounds} rounds: still asking for {kind} {path} (operation: {operation}; granted already: {granted})";
9184
+ var BASH_PERMISSION_DENIED = "Permission denied for this shell command: {parts} ({host reason}). The parts are checked together, so one denied part denies the whole command; run the allowed parts in a separate call.";
9183
9185
 
9184
9186
  class BashPermissionLoopError extends Error {
9185
9187
  }
@@ -9197,6 +9199,10 @@ ${ask.command ?? ""}
9197
9199
  Working directory:
9198
9200
  ${ask.cwd ?? ""}`;
9199
9201
  }
9202
+ function permissionAskPath(ask) {
9203
+ const paths = ask.patterns?.length ? ask.patterns : bashPermissionRetryGrants(ask);
9204
+ return ask.kind === "escalation" && ask.command ? `${ask.command}${ask.cwd ? ` @ ${ask.cwd}` : ""}` : paths.length ? paths.join(", ") : "<no asks returned>";
9205
+ }
9200
9206
  async function withBashPermissionLoop(options) {
9201
9207
  const granted = new Set;
9202
9208
  const alreadyGranted = (ask) => {
@@ -9218,20 +9224,22 @@ async function withBashPermissionLoop(options) {
9218
9224
  already_granted: alreadyGranted(ask)
9219
9225
  }))
9220
9226
  });
9221
- const unsettled = (cause) => {
9227
+ const unsettled = () => {
9222
9228
  const message = (asks.length ? asks : [{}]).map((ask) => {
9223
- const paths = ask.patterns?.length ? ask.patterns : bashPermissionRetryGrants(ask);
9224
- const path = ask.kind === "escalation" && ask.command ? `${ask.command}${ask.cwd ? ` @ ${ask.cwd}` : ""}` : paths.length ? paths.join(", ") : "<no asks returned>";
9225
- return BASH_PERMISSION_NOT_SETTLED.replace("{rounds}", String(round)).replace("{kind}", ask.kind ?? "unknown").replace("{operation}", ask.operation ?? options.operation ?? "bash").replace("{granted}", alreadyGranted(ask) ? "yes" : "no").replace("{path}", () => path);
9229
+ return BASH_PERMISSION_NOT_SETTLED.replace("{rounds}", String(round)).replace("{kind}", ask.kind ?? "unknown").replace("{operation}", ask.operation ?? options.operation ?? "bash").replace("{granted}", alreadyGranted(ask) ? "yes" : "no").replace("{path}", () => permissionAskPath(ask));
9226
9230
  }).join("; ");
9227
- return new BashPermissionLoopError(cause instanceof Error ? `${message}: ${cause.message}` : message, { cause });
9231
+ return new BashPermissionLoopError(message);
9228
9232
  };
9229
9233
  if (asks.length === 0 || asks.every(alreadyGranted) || round >= 8)
9230
9234
  throw unsettled();
9235
+ const pendingAsks = asks.filter((ask) => !alreadyGranted(ask));
9231
9236
  try {
9232
- await options.approve(asks.filter((ask) => !alreadyGranted(ask)));
9237
+ await options.approve(pendingAsks);
9233
9238
  } catch (error) {
9234
- throw unsettled(error);
9239
+ const parts = pendingAsks.map(permissionAskPath).join(", ");
9240
+ const reason = error instanceof Error && error.message ? error.message.trim().replace(/\.$/, "") : "";
9241
+ const message = reason ? BASH_PERMISSION_DENIED.replace("{host reason}", () => reason) : BASH_PERMISSION_DENIED.replace(" ({host reason})", "");
9242
+ throw new BashPermissionLoopError(message.replace("{parts}", () => parts), { cause: error });
9235
9243
  }
9236
9244
  for (const ask of asks) {
9237
9245
  for (const grant of bashPermissionRetryGrants(ask))
@@ -11309,15 +11317,57 @@ function hubSummaryLine(response, theme) {
11309
11317
  const message = summary ? asString(summary.message) : undefined;
11310
11318
  return message ? theme.fg("warning", message) : undefined;
11311
11319
  }
11320
+ var ROOT_KINDS = ["main", "test", "attribute", "public_api", "no_caller_found"];
11321
+ var ROOT_LABELS = {
11322
+ main: "main",
11323
+ test: "test",
11324
+ attribute: "attribute",
11325
+ public_api: "public API",
11326
+ no_caller_found: "no caller found"
11327
+ };
11328
+ function rootCounted(kind, count) {
11329
+ const plural = count === 1 ? "" : "s";
11330
+ switch (kind) {
11331
+ case "main":
11332
+ return count === 1 ? "main" : `${count} main functions`;
11333
+ case "test":
11334
+ return `${count} test${plural}`;
11335
+ case "attribute":
11336
+ return `${count} function${plural} with an entry attribute`;
11337
+ case "public_api":
11338
+ return `${count} public API function${plural}`;
11339
+ default:
11340
+ return `${count} function${plural} with no caller found`;
11341
+ }
11342
+ }
11343
+ function rootSummary(record) {
11344
+ const counts = asRecord(record.root_counts);
11345
+ if (!counts)
11346
+ return;
11347
+ const parts = ROOT_KINDS.flatMap((kind) => {
11348
+ const count = asNumber(counts[kind]);
11349
+ return count && count > 0 ? [rootCounted(kind, count)] : [];
11350
+ });
11351
+ return parts.length > 0 ? parts.join(", ") : undefined;
11352
+ }
11353
+ function rootTag(record) {
11354
+ const kind = asString(record.root);
11355
+ const label = kind ? ROOT_LABELS[kind] : undefined;
11356
+ if (!kind || !label)
11357
+ return;
11358
+ const attribute = asString(record.attribute);
11359
+ return kind === "attribute" && attribute ? `[root: attribute ${attribute}]` : `[root: ${label}]`;
11360
+ }
11312
11361
  function renderTracePath(path, index, lines, theme) {
11313
11362
  lines.push(`Path ${index + 1}`);
11363
+ const root = rootTag(path);
11314
11364
  asRecords(path.hops).forEach((hop, hopIndex) => {
11315
11365
  const symbol = asString(hop.symbol) ?? "(unknown)";
11316
11366
  const file = shortenHomePath(asString(hop.file) ?? "(unknown file)");
11317
11367
  const line = asNumber(hop.line);
11318
- const entry = hop.is_entry_point === true ? " [entry]" : "";
11368
+ const tag = hopIndex === 0 ? root ? ` ${theme.fg("warning", root)}` : "" : hop.exported === true ? " [exported]" : "";
11319
11369
  const nameMatch = nameMatchEdgeMarker(hop, theme);
11320
- lines.push(treeLine(hopIndex + 1, `${symbol}${entry} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}${nameMatch}`));
11370
+ lines.push(treeLine(hopIndex + 1, `${symbol}${tag} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}${nameMatch}`));
11321
11371
  });
11322
11372
  }
11323
11373
  function renderCallersGroupLines(group, theme, callbackLabel) {
@@ -11422,18 +11472,18 @@ function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME, op
11422
11472
  const hubSummary = hubSummaryLine(record, theme);
11423
11473
  const totalPaths = asNumber(record.total_paths) ?? paths.length;
11424
11474
  const totalPathsIsLowerBound = asBoolean(record.total_paths_is_lower_bound) ?? false;
11425
- const entryPoints = asNumber(record.entry_points_found) ?? 0;
11475
+ const roots = rootSummary(record);
11426
11476
  const sections = [
11427
11477
  joinNonEmpty([
11428
11478
  theme.fg("success", `${totalPathsIsLowerBound ? "at least " : ""}${totalPaths} path${totalPaths === 1 ? "" : "s"}`),
11429
- theme.fg("muted", `${entryPoints} entry point${entryPoints === 1 ? "" : "s"}`),
11479
+ roots ? theme.fg("muted", `roots: ${roots}`) : undefined,
11430
11480
  warning
11431
11481
  ])
11432
11482
  ];
11433
11483
  if (hubSummary)
11434
11484
  sections.push(hubSummary);
11435
11485
  if (paths.length === 0)
11436
- sections.push(theme.fg("muted", "No entry paths found."));
11486
+ sections.push(theme.fg("muted", "No paths found."));
11437
11487
  paths.forEach((path, index) => {
11438
11488
  const lines = [];
11439
11489
  renderTracePath(path, index, lines, theme);
@@ -11466,13 +11516,17 @@ function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME, op
11466
11516
  sections.unshift(theme.fg("warning", testFilterNote));
11467
11517
  if (hubSummary)
11468
11518
  sections.push(hubSummary);
11519
+ const roots = rootSummary(record);
11520
+ if (roots)
11521
+ sections.push(theme.fg("muted", `Reaches ${roots}`));
11469
11522
  if (callers.length === 0)
11470
11523
  sections.push(theme.fg("muted", "No impacted callers found."));
11471
11524
  callers.forEach((caller) => {
11472
11525
  const file = shortenHomePath(asString(caller.caller_file) ?? "(unknown file)");
11473
11526
  const symbol = asString(caller.caller_symbol) ?? "(unknown)";
11474
11527
  const line = asNumber(caller.line) ?? 0;
11475
- const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
11528
+ const root = rootTag(caller);
11529
+ const entry = root ? ` ${theme.fg("warning", root)}` : "";
11476
11530
  const nameMatch = nameMatchEdgeMarker(caller, theme);
11477
11531
  const reference = caller.relation === "callback_reference" ? asString(record.callback_reference_label) ?? "" : "";
11478
11532
  const expression = asString(caller.call_expression);
@@ -11758,8 +11812,9 @@ function formatDroppedKeyWarnings(dropped) {
11758
11812
  return dropped.map((item) => `Ignoring ${item.key} from ${item.tier} config (${item.reason})`);
11759
11813
  }
11760
11814
  // ../aft-bridge/dist/config-watch.js
11761
- import { readFileSync as readFileSync3, statSync as statSync3, watch } from "node:fs";
11762
- import { basename, dirname as dirname2 } from "node:path";
11815
+ import { createHash as createHash2 } from "node:crypto";
11816
+ import { closeSync as closeSync2, fstatSync, openSync as openSync2, readFileSync as readFileSync3, readSync as readSync2, realpathSync as realpathSync2, statSync as statSync3, watch } from "node:fs";
11817
+ import { basename, dirname as dirname2, resolve as resolve5 } from "node:path";
11763
11818
  var CONFIG_WATCH_DEBOUNCE_MS = 150;
11764
11819
  var CONFIG_LIVE_KEEP_NOTE = "AFT keeps using the last valid configuration until the file is fixed.";
11765
11820
  function pathKey(name) {
@@ -11813,7 +11868,6 @@ function aftLiveConfigKeys(resolveBash) {
11813
11868
  pathKey("restrict_to_project_root"),
11814
11869
  pathKey("revision_reads"),
11815
11870
  pathKey("inspect.diagnostics_timeout_ms"),
11816
- pathKey("inspect.tier2_idle_minutes"),
11817
11871
  pathKey("lsp.idle_minutes"),
11818
11872
  ...[
11819
11873
  "diagnostics",
@@ -11885,6 +11939,29 @@ function readTextOrNull(path) {
11885
11939
  var CONFIG_WATCH_REVALIDATE_MS = 2000;
11886
11940
  var CONFIG_WATCH_REJECTED_RETRIES = 3;
11887
11941
  var CONFIG_WATCH_MAX_DELAY_MS = 1000;
11942
+ var CONFIG_READ_LIMIT = 1024 * 1024;
11943
+ function contentFingerprint(path) {
11944
+ let fd;
11945
+ try {
11946
+ fd = openSync2(path, "r");
11947
+ const metadata = fstatSync(fd, { bigint: true });
11948
+ const bytes = Buffer.alloc(Math.min(Number(metadata.size), CONFIG_READ_LIMIT));
11949
+ let offset = 0;
11950
+ while (offset < bytes.length) {
11951
+ const read = readSync2(fd, bytes, offset, bytes.length - offset, null);
11952
+ if (read === 0)
11953
+ break;
11954
+ offset += read;
11955
+ }
11956
+ const hash = createHash2("sha256").update(bytes.subarray(0, offset)).digest("hex");
11957
+ return `${metadata.size}:${metadata.mtimeNs}:${hash}`;
11958
+ } catch {
11959
+ return null;
11960
+ } finally {
11961
+ if (fd !== undefined)
11962
+ closeSync2(fd);
11963
+ }
11964
+ }
11888
11965
  function identityOf(dir) {
11889
11966
  try {
11890
11967
  const stat = statSync3(dir, { bigint: true });
@@ -11900,6 +11977,13 @@ function watchDirFor(file) {
11900
11977
  dir = dirname2(dir);
11901
11978
  return dir;
11902
11979
  }
11980
+ function canonicalDirectory(dir) {
11981
+ try {
11982
+ return realpathSync2(dir);
11983
+ } catch {
11984
+ return resolve5(dir);
11985
+ }
11986
+ }
11903
11987
  function watchAftConfigFiles(options) {
11904
11988
  const debounceMs = options.debounceMs ?? CONFIG_WATCH_DEBOUNCE_MS;
11905
11989
  const lastSeen = new Map;
@@ -11913,6 +11997,8 @@ function watchAftConfigFiles(options) {
11913
11997
  let firstPendingAt = null;
11914
11998
  let stopped = false;
11915
11999
  const watchers = new Map;
12000
+ const targets = new Map;
12001
+ const fallbackSeen = new Map;
11916
12002
  const watchImpl = options.watchImpl ?? watch;
11917
12003
  let rejectedRetries = 0;
11918
12004
  const check = () => {
@@ -11966,8 +12052,34 @@ function watchAftConfigFiles(options) {
11966
12052
  timer.unref?.();
11967
12053
  };
11968
12054
  function revalidate() {
11969
- const wanted = new Set(options.paths.map(watchDirFor));
12055
+ const wanted = new Set;
12056
+ const wantedCanonical = new Set;
12057
+ const wantDirectory = (dir) => {
12058
+ const canonical = canonicalDirectory(dir);
12059
+ if (wantedCanonical.has(canonical))
12060
+ return;
12061
+ wantedCanonical.add(canonical);
12062
+ wanted.add(dir);
12063
+ };
12064
+ for (const path of options.paths)
12065
+ wantDirectory(watchDirFor(path));
11970
12066
  let moved = false;
12067
+ for (const path of options.paths) {
12068
+ try {
12069
+ const resolved = realpathSync2(path);
12070
+ const target = resolved === resolve5(path) ? undefined : resolved;
12071
+ if (targets.get(path) !== target) {
12072
+ if (target)
12073
+ targets.set(path, target);
12074
+ else
12075
+ targets.delete(path);
12076
+ fallbackSeen.delete(path);
12077
+ moved = true;
12078
+ }
12079
+ } catch {}
12080
+ }
12081
+ for (const target of targets.values())
12082
+ wantDirectory(watchDirFor(target));
11971
12083
  for (const [dir, entry] of watchers) {
11972
12084
  if (!wanted.has(dir) || entry.stale || identityOf(dir) !== entry.identity) {
11973
12085
  entry.watcher.close();
@@ -12006,6 +12118,17 @@ function watchAftConfigFiles(options) {
12006
12118
  moved = true;
12007
12119
  } catch {}
12008
12120
  }
12121
+ const watchedCanonical = new Set(Array.from(watchers.keys(), canonicalDirectory));
12122
+ for (const [path, target] of targets) {
12123
+ if (watchedCanonical.has(canonicalDirectory(dirname2(target)))) {
12124
+ fallbackSeen.delete(path);
12125
+ continue;
12126
+ }
12127
+ const fingerprint = contentFingerprint(path);
12128
+ if (!fallbackSeen.has(path) || fallbackSeen.get(path) !== fingerprint)
12129
+ moved = true;
12130
+ fallbackSeen.set(path, fingerprint);
12131
+ }
12009
12132
  return moved;
12010
12133
  }
12011
12134
  revalidate();
@@ -12162,8 +12285,8 @@ function startLiveConfigReload(options) {
12162
12285
  return { reload, stop };
12163
12286
  }
12164
12287
  // ../aft-bridge/dist/downloader.js
12165
- import { createHash as createHash2, randomUUID } from "node:crypto";
12166
- import { chmodSync, closeSync as closeSync2, copyFileSync, createWriteStream, existsSync as existsSync3, mkdirSync as mkdirSync2, openSync as openSync2, readdirSync, readFileSync as readFileSync4, renameSync as renameSync2, rmSync, statSync as statSync4, unlinkSync as unlinkSync2, writeSync } from "node:fs";
12288
+ import { createHash as createHash3, randomUUID } from "node:crypto";
12289
+ import { chmodSync, closeSync as closeSync3, copyFileSync, createWriteStream, existsSync as existsSync3, mkdirSync as mkdirSync2, openSync as openSync3, readdirSync, readFileSync as readFileSync4, renameSync as renameSync2, rmSync, statSync as statSync4, unlinkSync as unlinkSync2, writeSync } from "node:fs";
12167
12290
  import { hostname } from "node:os";
12168
12291
  import { join as join8 } from "node:path";
12169
12292
  import { Readable } from "node:stream";
@@ -12381,7 +12504,7 @@ async function downloadBinary(version) {
12381
12504
  warn(`Checksum verification failed: checksums.sha256 found but no entry for ${assetName}. ` + "Binary download aborted for security reasons.");
12382
12505
  return null;
12383
12506
  }
12384
- const hash = createHash2("sha256");
12507
+ const hash = createHash3("sha256");
12385
12508
  let bytesWritten = 0;
12386
12509
  const guard = new TransformStream({
12387
12510
  transform(chunk, controller) {
@@ -12548,11 +12671,11 @@ async function acquireDownloadLock(lockPath, timing = {}) {
12548
12671
  while (true) {
12549
12672
  try {
12550
12673
  const owner = createDownloadLockOwner();
12551
- const fd = openSync2(lockPath, "wx");
12674
+ const fd = openSync3(lockPath, "wx");
12552
12675
  try {
12553
12676
  writeSync(fd, owner);
12554
12677
  } finally {
12555
- closeSync2(fd);
12678
+ closeSync3(fd);
12556
12679
  }
12557
12680
  return () => {
12558
12681
  try {
@@ -12623,8 +12746,8 @@ import { appendFile, lstat, mkdir, open, rename, rm, stat } from "node:fs/promis
12623
12746
  import { dirname as dirname3 } from "node:path";
12624
12747
 
12625
12748
  // ../aft-bridge/dist/private-storage.js
12626
- import { closeSync as closeSync3, constants, copyFileSync as copyFileSync2, fchmodSync, fstatSync, lstatSync, mkdirSync as mkdirSync3, openSync as openSync3, readSync as readSync2, writeSync as writeSync2 } from "node:fs";
12627
- import { isAbsolute as isAbsolute4, join as join9, parse, relative as relative3, resolve as resolve5, sep } from "node:path";
12749
+ import { closeSync as closeSync4, constants, copyFileSync as copyFileSync2, fchmodSync, fstatSync as fstatSync2, lstatSync, mkdirSync as mkdirSync3, openSync as openSync4, readSync as readSync3, writeSync as writeSync2 } from "node:fs";
12750
+ import { isAbsolute as isAbsolute4, join as join9, parse, relative as relative3, resolve as resolve6, sep } from "node:path";
12628
12751
  var PRIVATE_FILE_MODE = 384;
12629
12752
  var PRIVATE_DIRECTORY_MODE = 448;
12630
12753
  function privateMkdirSync(path, recursive = true) {
@@ -12638,13 +12761,13 @@ function privateCopyFileSync(source, destination) {
12638
12761
  copyFileSync2(source, destination);
12639
12762
  return;
12640
12763
  }
12641
- const input = openSync3(source, "r");
12764
+ const input = openSync4(source, "r");
12642
12765
  let output;
12643
12766
  try {
12644
- output = openSync3(destination, "wx", PRIVATE_FILE_MODE);
12767
+ output = openSync4(destination, "wx", PRIVATE_FILE_MODE);
12645
12768
  const buffer = Buffer.allocUnsafe(64 * 1024);
12646
12769
  for (;; ) {
12647
- const bytes = readSync2(input, buffer);
12770
+ const bytes = readSync3(input, buffer);
12648
12771
  if (bytes === 0)
12649
12772
  break;
12650
12773
  let offset = 0;
@@ -12652,18 +12775,18 @@ function privateCopyFileSync(source, destination) {
12652
12775
  offset += writeSync2(output, buffer, offset, bytes - offset);
12653
12776
  }
12654
12777
  } finally {
12655
- closeSync3(input);
12778
+ closeSync4(input);
12656
12779
  if (output !== undefined)
12657
- closeSync3(output);
12780
+ closeSync4(output);
12658
12781
  }
12659
12782
  }
12660
12783
  var warned = new Set;
12661
12784
  function openPrivateStorageDir(root, path = root) {
12662
- const base = resolve5(root);
12785
+ const base = resolve6(root);
12663
12786
  if (!root || base === parse(base).root) {
12664
12787
  throw new Error(`refusing filesystem root as private storage: ${base}`);
12665
12788
  }
12666
- const rel = relative3(base, resolve5(path));
12789
+ const rel = relative3(base, resolve6(path));
12667
12790
  if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute4(rel))
12668
12791
  return;
12669
12792
  privateMkdirSync(path);
@@ -12676,8 +12799,8 @@ function openPrivateStorageDir(root, path = root) {
12676
12799
  try {
12677
12800
  if (lstatSync(current).isSymbolicLink())
12678
12801
  return;
12679
- fd = openSync3(current, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
12680
- if ((fstatSync(fd).mode & 63) !== 0)
12802
+ fd = openSync4(current, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
12803
+ if ((fstatSync2(fd).mode & 63) !== 0)
12681
12804
  fchmodSync(fd, PRIVATE_DIRECTORY_MODE);
12682
12805
  } catch (error) {
12683
12806
  if (!warned.has(current)) {
@@ -12688,7 +12811,7 @@ function openPrivateStorageDir(root, path = root) {
12688
12811
  return;
12689
12812
  } finally {
12690
12813
  if (fd !== undefined)
12691
- closeSync3(fd);
12814
+ closeSync4(fd);
12692
12815
  }
12693
12816
  }
12694
12817
  }
@@ -13570,7 +13693,7 @@ async function readConnectionFileForUid(path, expectedUid) {
13570
13693
  }
13571
13694
 
13572
13695
  // ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/launch-nonce.js
13573
- import { closeSync as closeSync4, fstatSync as fstatSync2, readSync as readSync3 } from "node:fs";
13696
+ import { closeSync as closeSync5, fstatSync as fstatSync3, readSync as readSync4 } from "node:fs";
13574
13697
  var SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE";
13575
13698
  var SUBC_LAUNCH_NONCE_FD_ENV = "SUBC_LAUNCH_NONCE_FD";
13576
13699
  class LaunchNonceError extends Error {
@@ -13617,7 +13740,7 @@ class LaunchNonceCell {
13617
13740
  lookup;
13618
13741
  platform;
13619
13742
  stat;
13620
- constructor(lookup, platform = process.platform, stat = (fd) => fstatSync2(fd, { bigint: true })) {
13743
+ constructor(lookup, platform = process.platform, stat = (fd) => fstatSync3(fd, { bigint: true })) {
13621
13744
  this.lookup = lookup;
13622
13745
  this.platform = platform;
13623
13746
  this.stat = stat;
@@ -13665,7 +13788,7 @@ class LaunchNonceCell {
13665
13788
  const chunk = Buffer.alloc(256);
13666
13789
  let got;
13667
13790
  try {
13668
- got = readSync3(fd, chunk, 0, chunk.length, null);
13791
+ got = readSync4(fd, chunk, 0, chunk.length, null);
13669
13792
  } catch (error) {
13670
13793
  if (isWouldBlock(error))
13671
13794
  throw emptyError(fd);
@@ -13677,7 +13800,7 @@ class LaunchNonceCell {
13677
13800
  const parts = [Buffer.from(chunk.subarray(0, got))];
13678
13801
  try {
13679
13802
  for (;; ) {
13680
- const n = readSync3(fd, chunk, 0, chunk.length, null);
13803
+ const n = readSync4(fd, chunk, 0, chunk.length, null);
13681
13804
  if (n === 0)
13682
13805
  break;
13683
13806
  parts.push(Buffer.from(chunk.subarray(0, n)));
@@ -13754,7 +13877,7 @@ function errnoOf(error) {
13754
13877
  }
13755
13878
  function closeQuietly(fd) {
13756
13879
  try {
13757
- closeSync4(fd);
13880
+ closeSync5(fd);
13758
13881
  } catch {}
13759
13882
  }
13760
13883
 
@@ -15629,15 +15752,15 @@ function getLifecycleRegistry() {
15629
15752
  }
15630
15753
 
15631
15754
  // ../aft-bridge/dist/project-identity.js
15632
- import { realpathSync as realpathSync2 } from "node:fs";
15633
- import { posix as posix2, resolve as resolve6, win32 as win322 } from "node:path";
15755
+ import { realpathSync as realpathSync3 } from "node:fs";
15756
+ import { posix as posix2, resolve as resolve7, win32 as win322 } from "node:path";
15634
15757
  function canonicalizeProjectRoot(dir) {
15635
15758
  const trimmed = trimProjectRootSeparators(dir);
15636
15759
  let canonical;
15637
15760
  try {
15638
- canonical = realpathSync2(trimmed);
15761
+ canonical = realpathSync3(trimmed);
15639
15762
  } catch {
15640
- canonical = resolve6(trimmed);
15763
+ canonical = resolve7(trimmed);
15641
15764
  }
15642
15765
  return normalizeWindowsRoot(canonical);
15643
15766
  }
@@ -17617,7 +17740,9 @@ class SubcTransportPool {
17617
17740
  });
17618
17741
  retry = state.bindPending;
17619
17742
  } else {
17620
- retry = this.hostScope(client, record.identity);
17743
+ retry = this.hostScope(client, record.identity).finally(() => {
17744
+ this.scheduleHostScopeRetry(session, state);
17745
+ });
17621
17746
  }
17622
17747
  retry.catch((error) => {
17623
17748
  warn(`subc head scope: session=${session} background retry failed code=${error instanceof Error ? error.name : "Error"}`);
@@ -17785,12 +17910,18 @@ class SubcTransportPool {
17785
17910
  }
17786
17911
  return this.openScopedHostToolRoute(client, identity, record, entry, channel);
17787
17912
  }
17788
- bindHostToolRoute(client, identity, channel, reason, scope) {
17913
+ async bindHostToolRoute(client, identity, channel, reason, scope) {
17789
17914
  log(`subc head scope: session=${identity.session} bind=${scope ? "scoped" : "unscoped"} channel=${channel} root=${identity.project_root} reason=${reason}` + (scope ? ` ref=${scope.ref.slice(0, 8)} epoch=${scope.scopeEpoch}` : ""));
17790
- return client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, {
17915
+ const route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, {
17791
17916
  consumerIdentity: this.consumerIdentity,
17792
17917
  ...scope ? { scope } : {}
17793
17918
  });
17919
+ const state = this.hostScopeCache.get(identity.session);
17920
+ if (scope && state?.unscopedReason && state.answer?.result === "scope" && state.answer.scope === scope) {
17921
+ log(`subc head scope: session=${identity.session} route=upgraded from=unscoped ref=${scope.ref.slice(0, 8)} epoch=${scope.scopeEpoch}`);
17922
+ state.unscopedReason = undefined;
17923
+ }
17924
+ return route;
17794
17925
  }
17795
17926
  async openScopedHostToolRoute(client, identity, record, entry, channel = "tool") {
17796
17927
  const decision = await this.hostScope(client, identity);
@@ -18422,7 +18553,8 @@ function taskIdFromError(error) {
18422
18553
  return;
18423
18554
  }
18424
18555
  // ../aft-bridge/dist/feature-config.js
18425
- import { createHash as createHash3 } from "node:crypto";
18556
+ import { createHash as createHash4 } from "node:crypto";
18557
+ import { createRequire } from "node:module";
18426
18558
  var CANONICAL_TOOLS = [
18427
18559
  "aft_callgraph",
18428
18560
  "aft_conflicts",
@@ -18472,23 +18604,114 @@ var REMOVED_OPENCODE_PATHS = [
18472
18604
  "opencode.server_url",
18473
18605
  "opencode.server_password_env"
18474
18606
  ];
18475
- function removedConfigKeyErrors(value) {
18607
+ var REMOVED_DATA_LAYER_PATHS = [
18608
+ "views.callgraph_copies",
18609
+ "views.enabled",
18610
+ "worktree.ram_overlay"
18611
+ ];
18612
+ var RETIRED_IDLE_CHECKOUT_HOURS = [
18613
+ "views.idle_checkout_hours",
18614
+ "idle.checkout_hours"
18615
+ ];
18616
+ var DATA_LAYER_KEYS_REFUSED_FROM = [0, 65];
18617
+ var DATA_LAYER_RETIRED_PATHS = [
18618
+ "views.callgraph_copies",
18619
+ "views.enabled",
18620
+ "views.idle_checkout_hours",
18621
+ "worktree.ram_overlay"
18622
+ ];
18623
+ var IDLE_CHECKOUT_HOURS_DEFAULT = 168;
18624
+ var IDLE_CHECKOUT_HOURS_MIN = 1;
18625
+ var IDLE_CHECKOUT_HOURS_MAX = 24 * 365;
18626
+ function parseRelease(version) {
18627
+ const match = /^(\d+)\.(\d+)(?:\.|$)/.exec(version);
18628
+ return match ? [Number(match[1]), Number(match[2])] : undefined;
18629
+ }
18630
+ var AFT_PACKAGE_NAMES = new Set([
18631
+ "@cortexkit/aft-bridge",
18632
+ "@cortexkit/aft-opencode",
18633
+ "@cortexkit/aft-pi",
18634
+ "@cortexkit/aft"
18635
+ ]);
18636
+ function readRunningRelease() {
18637
+ const req = createRequire(import.meta.url);
18638
+ for (const candidate of ["../package.json", "../../package.json", "../../../package.json"]) {
18639
+ try {
18640
+ const manifest = req(candidate);
18641
+ if (manifest.name && AFT_PACKAGE_NAMES.has(manifest.name) && manifest.version) {
18642
+ return parseRelease(manifest.version) ?? [0, 0];
18643
+ }
18644
+ } catch {}
18645
+ }
18646
+ return [0, 0];
18647
+ }
18648
+ var runningRelease;
18649
+ function getRunningRelease() {
18650
+ runningRelease ??= readRunningRelease();
18651
+ return runningRelease;
18652
+ }
18653
+ function releaseAtLeast(release, floor) {
18654
+ return release[0] !== floor[0] ? release[0] > floor[0] : release[1] >= floor[1];
18655
+ }
18656
+ function refusedFromVersion() {
18657
+ return `${DATA_LAYER_KEYS_REFUSED_FROM[0]}.${DATA_LAYER_KEYS_REFUSED_FROM[1]}.0`;
18658
+ }
18659
+ function removedConfigKeyErrors(value, release = getRunningRelease(), refuseRetired = true) {
18476
18660
  if (!isRecord2(value))
18477
18661
  return [];
18478
- const errors = removedOpencodeKeyErrors(value);
18662
+ const errors = removedBlockKeyErrors(value, release, refuseRetired);
18479
18663
  if (isRecord2(value.harnesses)) {
18480
18664
  for (const [name, block] of Object.entries(value.harnesses)) {
18481
18665
  if (!isRecord2(block))
18482
18666
  continue;
18483
- for (const error of removedOpencodeKeyErrors(block)) {
18667
+ for (const error of removedBlockKeyErrors(block, release, refuseRetired)) {
18484
18668
  errors.push(error.replace("removed_config_key:", `removed_config_key:harnesses.${name}.`));
18485
18669
  }
18486
18670
  }
18487
18671
  }
18488
18672
  return errors;
18489
18673
  }
18490
- function removedOpencodeKeyErrors(value) {
18491
- return REMOVED_OPENCODE_PATHS.filter((path) => hasPath(value, path)).map((path) => `removed_config_key:${path}; run aft doctor --fix`);
18674
+ function removedBlockKeyErrors(value, release, refuseRetired) {
18675
+ const paths = REMOVED_OPENCODE_PATHS.filter((path) => hasPath(value, path));
18676
+ if (refuseRetired)
18677
+ paths.push(...refusedRetiredNames(value));
18678
+ if (releaseAtLeast(release, DATA_LAYER_KEYS_REFUSED_FROM))
18679
+ paths.push(...DATA_LAYER_RETIRED_PATHS.filter((path) => hasPath(value, path)));
18680
+ return paths.map((path) => `removed_config_key:${path}; run npx @cortexkit/aft doctor --fix`);
18681
+ }
18682
+ var REFUSED_RETIRED_PATHS = [
18683
+ "sandbox.enabled",
18684
+ "tool_surface",
18685
+ "hoist_builtin_tools",
18686
+ "enabled",
18687
+ "search_index",
18688
+ "experimental_search_index",
18689
+ "semantic_search",
18690
+ "experimental_semantic_search",
18691
+ "callgraph_store",
18692
+ "github.enabled",
18693
+ "gh_read",
18694
+ "gh_shim.enabled",
18695
+ "experimental_lsp_ty",
18696
+ "experimental_bash_rewrite",
18697
+ "experimental_bash_compress",
18698
+ "experimental_bash_background",
18699
+ "experimental.bash",
18700
+ "idle.lsp_ttl_minutes",
18701
+ "inspect.tier2_soft_deadline_ms",
18702
+ "inspect.max_drill_down_items"
18703
+ ];
18704
+ function refusedRetiredNames(block) {
18705
+ const names = REFUSED_RETIRED_PATHS.filter((path) => hasPath(block, path));
18706
+ if (Array.isArray(block.disabled_tools)) {
18707
+ for (const name of block.disabled_tools) {
18708
+ if (typeof name !== "string" || !Object.hasOwn(LEGACY_TOOL_ALIASES, name))
18709
+ continue;
18710
+ if (!names.includes(name))
18711
+ names.push(name);
18712
+ }
18713
+ }
18714
+ return names;
18492
18715
  }
18493
18716
  var RETIRED_PATHS = [
18494
18717
  ["sandbox.enabled", "sandbox.profile"],
@@ -18643,7 +18866,10 @@ function translateInspectLspPaths(map, prefix, out) {
18643
18866
  recordRetired(out, prefix, `inspect.${leaf}`);
18644
18867
  }
18645
18868
  }
18646
- var REMOVED_NO_EFFECT_PATHS = ["bridge.hang_threshold"];
18869
+ var REMOVED_NO_EFFECT_PATHS = [
18870
+ "bridge.hang_threshold",
18871
+ "inspect.tier2_idle_minutes"
18872
+ ];
18647
18873
  function dropNoEffectPaths(map, prefix, out) {
18648
18874
  for (const path of REMOVED_NO_EFFECT_PATHS) {
18649
18875
  const [container, leaf] = path.split(".");
@@ -18652,6 +18878,29 @@ function dropNoEffectPaths(map, prefix, out) {
18652
18878
  }
18653
18879
  }
18654
18880
  }
18881
+ function retiredIdleCheckoutHours(value) {
18882
+ const raw = typeof value === "number" && Number.isSafeInteger(value) ? value : IDLE_CHECKOUT_HOURS_DEFAULT;
18883
+ return Math.min(IDLE_CHECKOUT_HOURS_MAX, Math.max(IDLE_CHECKOUT_HOURS_MIN, raw));
18884
+ }
18885
+ function translateDataLayerPaths(map, prefix, out) {
18886
+ const [retired, replacement] = RETIRED_IDLE_CHECKOUT_HOURS;
18887
+ const hours = takeNested(map, "views", "idle_checkout_hours");
18888
+ if (hours.found) {
18889
+ recordRetired(out, prefix, retired);
18890
+ if (isRecord2(map.idle) && map.idle.checkout_hours !== undefined) {
18891
+ superseded(out, `${prefix}${retired}`, `${retired} is ignored because ${replacement} is set`);
18892
+ } else {
18893
+ if (!Object.hasOwn(map, "idle"))
18894
+ map.idle = {};
18895
+ if (isRecord2(map.idle))
18896
+ map.idle.checkout_hours = retiredIdleCheckoutHours(hours.value);
18897
+ }
18898
+ }
18899
+ for (const path of REMOVED_DATA_LAYER_PATHS) {
18900
+ if (hasPath(map, path))
18901
+ out.removedKeys.push(`${prefix}${path}`);
18902
+ }
18903
+ }
18655
18904
  function translateExperimentalPaths(map, prefix, out) {
18656
18905
  for (const [oldKey, leaf] of [
18657
18906
  ["experimental_lsp_ty", "lsp_ty"],
@@ -18684,23 +18933,25 @@ function translateExperimentalPaths(map, prefix, out) {
18684
18933
  const experimental = map.experimental;
18685
18934
  const legacy = experimental.bash;
18686
18935
  if (isRecord2(legacy)) {
18687
- if (!["rewrite", "compress", "background"].some((leaf) => Object.hasOwn(legacy, leaf)))
18688
- return;
18936
+ if (Object.keys(legacy).length === 0)
18937
+ recordRetired(out, prefix, "experimental.bash");
18689
18938
  for (const leaf of Object.keys(legacy))
18690
18939
  recordRetired(out, prefix, `experimental.bash.${leaf}`);
18691
18940
  if (Object.hasOwn(map, "bash")) {
18692
18941
  superseded(out, "experimental.bash", 'experimental.bash is ignored because top-level "bash" is already set');
18693
18942
  } else {
18694
- const bash = {
18695
- rewrite: legacy.rewrite === true,
18696
- compress: legacy.compress === true,
18697
- background: legacy.background === true
18698
- };
18943
+ const bash = {};
18944
+ if (["rewrite", "compress", "background"].some((leaf) => Object.hasOwn(legacy, leaf))) {
18945
+ bash.rewrite = legacy.rewrite === true;
18946
+ bash.compress = legacy.compress === true;
18947
+ bash.background = legacy.background === true;
18948
+ }
18699
18949
  for (const leaf of ["long_running_reminder_enabled", "long_running_reminder_interval_ms"]) {
18700
18950
  if (Object.hasOwn(legacy, leaf))
18701
18951
  bash[leaf] = legacy[leaf];
18702
18952
  }
18703
- map.bash = bash;
18953
+ if (Object.keys(bash).length > 0)
18954
+ map.bash = bash;
18704
18955
  }
18705
18956
  } else {
18706
18957
  recordRetired(out, prefix, "experimental.bash");
@@ -18713,6 +18964,7 @@ function translateBlock(map, userBase, blockLabel, out) {
18713
18964
  const prefix = blockLabel === "base" ? "" : `${blockLabel}.`;
18714
18965
  translateGithubAliases(map, prefix, out);
18715
18966
  translateInspectLspPaths(map, prefix, out);
18967
+ translateDataLayerPaths(map, prefix, out);
18716
18968
  dropNoEffectPaths(map, prefix, out);
18717
18969
  translateExperimentalPaths(map, prefix, out);
18718
18970
  if (isRecord2(map.sandbox) && Object.hasOwn(map.sandbox, "enabled")) {
@@ -18751,8 +19003,6 @@ function translateBlock(map, userBase, blockLabel, out) {
18751
19003
  });
18752
19004
  }
18753
19005
  for (const [path] of RETIRED_PATHS) {
18754
- if (path === "experimental.bash")
18755
- continue;
18756
19006
  if (hasPath(map, path))
18757
19007
  recordRetired(out, prefix, path);
18758
19008
  }
@@ -18888,13 +19138,57 @@ var NO_EFFECT_RETIRED_NOTES = [
18888
19138
  "inspect.max_drill_down_items",
18889
19139
  "inspect.max_drill_down_items never had an effect, so the setting can be removed."
18890
19140
  ],
19141
+ [
19142
+ "inspect.tier2_idle_minutes",
19143
+ "inspect.tier2_idle_minutes no longer does anything: AFT runs whole-repository analyses only when aft_inspect is called, so the setting can be removed."
19144
+ ],
18891
19145
  [
18892
19146
  "inspect.tier2_soft_deadline_ms",
18893
19147
  "inspect.tier2_soft_deadline_ms never had an effect, so the setting can be removed."
18894
19148
  ]
18895
19149
  ];
18896
19150
  function noEffectNote(key) {
18897
- return NO_EFFECT_RETIRED_NOTES.find(([path]) => key === path || key.endsWith(`.${path}`))?.[1];
19151
+ return NO_EFFECT_RETIRED_NOTES.find(([path]) => namesPath(key, path))?.[1];
19152
+ }
19153
+ function namesPath(key, path) {
19154
+ return key === path || key.endsWith(`.${path}`);
19155
+ }
19156
+ var REMOVED_DATA_LAYER_NOTES = [
19157
+ [
19158
+ "views.callgraph_copies",
19159
+ "views.callgraph_copies is gone because no checkout borrows another checkout's call graph any more."
19160
+ ],
19161
+ [
19162
+ "views.enabled",
19163
+ "views.enabled is gone because AFT now keeps one index store per repository and always uses it."
19164
+ ],
19165
+ [
19166
+ "worktree.ram_overlay",
19167
+ "worktree.ram_overlay is gone because every worktree now has its own index state, so there is no borrowed index to patch in memory."
19168
+ ]
19169
+ ];
19170
+ function retiredIdleCheckoutHoursNote() {
19171
+ const [retired, replacement] = RETIRED_IDLE_CHECKOUT_HOURS;
19172
+ return `${retired} is now ${replacement}; from ${refusedFromVersion()} AFT refuses a config file that still sets ${retired}.`;
19173
+ }
19174
+ function removedConfigKeysNoticeMessage(configPath, translation) {
19175
+ const removed = translation.removedKeys ?? [];
19176
+ if (removed.length === 0)
19177
+ return "";
19178
+ const parts = [
19179
+ `${configPath} sets removed keys (${removed.join(", ")}); AFT ignores them, and from ${refusedFromVersion()} it refuses a config file that sets them. Run \`npx @cortexkit/aft doctor --fix\` to remove them.`
19180
+ ];
19181
+ for (const [path, note] of REMOVED_DATA_LAYER_NOTES) {
19182
+ if (removed.some((key) => namesPath(key, path)))
19183
+ parts.push(note);
19184
+ }
19185
+ return parts.join(" ");
19186
+ }
19187
+ function removedConfigKeysNoticeDigest(translation) {
19188
+ return noticeDigest({
19189
+ notice: "removed_data_layer_keys_v1",
19190
+ keys: sortedUnique(translation.removedKeys)
19191
+ });
18898
19192
  }
18899
19193
  function legacyConfigNoticeMessage(configPath, translation) {
18900
19194
  const equivalents = translation.retiredKeys.filter((key) => noEffectNote(key) === undefined);
@@ -18904,11 +19198,17 @@ function legacyConfigNoticeMessage(configPath, translation) {
18904
19198
  parts.push(`${configPath} uses retired keys (${equivalents.join(", ")}); AFT applied their current equivalents, with the same limits a project config has for those keys. Run \`npx @cortexkit/aft doctor --fix\` to update the file.`);
18905
19199
  if (translation.retiredEnabledFalse)
18906
19200
  parts.push(RETIRED_ENABLED_FALSE_INDEXES_NOTE);
19201
+ if (equivalents.some((key) => namesPath(key, RETIRED_IDLE_CHECKOUT_HOURS[0]))) {
19202
+ parts.push(retiredIdleCheckoutHoursNote());
19203
+ }
18907
19204
  }
18908
19205
  for (const [, note] of NO_EFFECT_RETIRED_NOTES) {
18909
19206
  if (noEffect.has(note))
18910
19207
  parts.push(note);
18911
19208
  }
19209
+ const removed = removedConfigKeysNoticeMessage(configPath, translation);
19210
+ if (removed)
19211
+ parts.push(removed);
18912
19212
  return parts.join(" ");
18913
19213
  }
18914
19214
  function translateConfigDocument(map, tier) {
@@ -18916,6 +19216,7 @@ function translateConfigDocument(map, tier) {
18916
19216
  warnings: [],
18917
19217
  legacyInput: false,
18918
19218
  retiredKeys: [],
19219
+ removedKeys: [],
18919
19220
  retiredEnabledFalse: false
18920
19221
  };
18921
19222
  translateBlock(map, tier === "user", "base", out);
@@ -18926,6 +19227,7 @@ function translateConfigDocument(map, tier) {
18926
19227
  }
18927
19228
  }
18928
19229
  out.retiredKeys = sortedUnique(out.retiredKeys);
19230
+ out.removedKeys = sortedUnique(out.removedKeys);
18929
19231
  return out;
18930
19232
  }
18931
19233
  function unknownDisabledTools(disabled) {
@@ -18961,7 +19263,11 @@ function validateResolvedConfig(value) {
18961
19263
  var ABSENT = { absent: true };
18962
19264
  function projectBlock(block) {
18963
19265
  const legacyPaths = {};
18964
- for (const path of [...RETIRED_PATHS.map(([retired]) => retired), ...REMOVED_NO_EFFECT_PATHS]) {
19266
+ for (const path of [
19267
+ ...RETIRED_PATHS.map(([retired]) => retired),
19268
+ ...REMOVED_NO_EFFECT_PATHS,
19269
+ ...DATA_LAYER_RETIRED_PATHS
19270
+ ]) {
18965
19271
  const dot = path.indexOf(".");
18966
19272
  if (dot < 0) {
18967
19273
  if (Object.hasOwn(block, path))
@@ -19040,7 +19346,7 @@ function canonicalJson(value) {
19040
19346
  return JSON.stringify(value);
19041
19347
  }
19042
19348
  function noticeDigest(projection) {
19043
- return createHash3("sha256").update(canonicalJson(projection), "utf8").digest("hex");
19349
+ return createHash4("sha256").update(canonicalJson(projection), "utf8").digest("hex");
19044
19350
  }
19045
19351
  var SEMANTIC_COST_NOTICE = "AFT indexes now default on; the local semantic backend may download an ONNX runtime and model and use CPU. Run npx @cortexkit/aft setup to change indexes.semantic.";
19046
19352
  var SEMANTIC_COST_NOTICE_DIGEST = noticeDigest({ notice: "semantic_default_cost_v1" });
@@ -19913,9 +20219,9 @@ async function createConcreteAftTransportPool(opts) {
19913
20219
  var LIFECYCLE_STATE = Symbol.for("@cortexkit/aft-bridge/location-lifecycle/v1");
19914
20220
  var RELEASE_LOCATION = Symbol.for("@cortexkit/aft-bridge/location-release/v1");
19915
20221
  // ../aft-bridge/dist/lsp-github-install.js
19916
- import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
19917
- import { closeSync as closeSync5, copyFileSync as copyFileSync3, createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync4, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, readlinkSync, readSync as readSync4, realpathSync as realpathSync3, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
19918
- import { dirname as dirname4, join as join11, relative as relative4, resolve as resolve7 } from "node:path";
20222
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
20223
+ import { closeSync as closeSync6, copyFileSync as copyFileSync3, createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync4, openSync as openSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, readlinkSync, readSync as readSync5, realpathSync as realpathSync4, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
20224
+ import { dirname as dirname4, join as join11, relative as relative4, resolve as resolve8 } from "node:path";
19919
20225
  import { Readable as Readable2 } from "node:stream";
19920
20226
  import { pipeline as pipeline2 } from "node:stream/promises";
19921
20227
 
@@ -20312,7 +20618,7 @@ function createGithubInstaller(services) {
20312
20618
  const MAX_EXTRACT_BYTES = 1024 * 1024 * 1024;
20313
20619
  function sha256OfFile(path) {
20314
20620
  return new Promise((resolve, reject) => {
20315
- const hash = createHash4("sha256");
20621
+ const hash = createHash5("sha256");
20316
20622
  const stream = createReadStream2(path);
20317
20623
  stream.on("error", reject);
20318
20624
  stream.on("data", (chunk) => hash.update(chunk));
@@ -20320,17 +20626,17 @@ function createGithubInstaller(services) {
20320
20626
  });
20321
20627
  }
20322
20628
  function sha256OfFileSync(path) {
20323
- const hash = createHash4("sha256");
20324
- const fd = openSync4(path, "r");
20629
+ const hash = createHash5("sha256");
20630
+ const fd = openSync5(path, "r");
20325
20631
  try {
20326
20632
  const buffer = Buffer.allocUnsafe(1024 * 1024);
20327
- let read = readSync4(fd, buffer, 0, buffer.length, null);
20633
+ let read = readSync5(fd, buffer, 0, buffer.length, null);
20328
20634
  while (read > 0) {
20329
20635
  hash.update(buffer.subarray(0, read));
20330
- read = readSync4(fd, buffer, 0, buffer.length, null);
20636
+ read = readSync5(fd, buffer, 0, buffer.length, null);
20331
20637
  }
20332
20638
  } finally {
20333
- closeSync5(fd);
20639
+ closeSync6(fd);
20334
20640
  }
20335
20641
  return hash.digest("hex");
20336
20642
  }
@@ -20538,7 +20844,7 @@ function createGithubInstaller(services) {
20538
20844
  throw new Error(`too many redirects (>${maxRedirects})`);
20539
20845
  }
20540
20846
  function validateExtraction(stagingRoot) {
20541
- const realStagingRoot = realpathSync3(stagingRoot);
20847
+ const realStagingRoot = realpathSync4(stagingRoot);
20542
20848
  let totalBytes = 0;
20543
20849
  const walk = (dir) => {
20544
20850
  let entries;
@@ -20564,12 +20870,12 @@ function createGithubInstaller(services) {
20564
20870
  }
20565
20871
  let realFull;
20566
20872
  try {
20567
- realFull = realpathSync3(full);
20873
+ realFull = realpathSync4(full);
20568
20874
  } catch (err) {
20569
20875
  throw new Error(`failed to realpath ${full}: ${err}`);
20570
20876
  }
20571
20877
  const rel = relative4(realStagingRoot, realFull);
20572
- if (relativePathEscapesRoot(rel) || resolve7(realStagingRoot, rel) !== realFull) {
20878
+ if (relativePathEscapesRoot(rel) || resolve8(realStagingRoot, rel) !== realFull) {
20573
20879
  throw new Error(`archive entry escapes staging root: ${full} → ${realFull} (zip-slip defense)`);
20574
20880
  }
20575
20881
  if (lst.isDirectory()) {
@@ -21332,6 +21638,21 @@ function resolveNpm(deps = defaultDeps()) {
21332
21638
  }
21333
21639
  return null;
21334
21640
  }
21641
+ function npmCandidates(deps = defaultDeps()) {
21642
+ const name = npmBinaryName(deps.platform);
21643
+ const dirs = [
21644
+ ...(deps.env.PATH ?? "").split(deps.platform === "win32" ? ";" : ":").filter(isAbsolute5),
21645
+ dirname5(deps.execPath),
21646
+ ...wellKnownNpmDirs(deps)
21647
+ ];
21648
+ return [...new Set(dirs)].flatMap((binDir) => {
21649
+ return [
21650
+ join12(binDir, name),
21651
+ join12(binDir, "node_modules", "npm", "bin", "npm-cli.js"),
21652
+ join12(binDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
21653
+ ].filter(isFile).map((command) => ({ command, binDir }));
21654
+ });
21655
+ }
21335
21656
  function quoteCmdArgument(value) {
21336
21657
  if (/[\0\r\n"%]/.test(value)) {
21337
21658
  throw new Error(`npm argument cannot be represented safely for cmd.exe: ${JSON.stringify(value)}`);
@@ -21339,6 +21660,8 @@ function quoteCmdArgument(value) {
21339
21660
  return `"${value}"`;
21340
21661
  }
21341
21662
  function npmInvocation(resolved, npmArgs, platform = process.platform, env = process.env) {
21663
+ if (resolved.cliPath)
21664
+ return { command: resolved.command, args: [resolved.cliPath, ...npmArgs] };
21342
21665
  if (platform !== "win32" || !/\.(?:cmd|bat)$/i.test(resolved.command)) {
21343
21666
  return { command: resolved.command, args: [...npmArgs] };
21344
21667
  }
@@ -21591,10 +21914,11 @@ function probeNpmCutoffSupport(npm, clock) {
21591
21914
  });
21592
21915
  }
21593
21916
  async function npmSupportsCutoff(npm, clock) {
21594
- const identity = await stat2(npm.command).then(({ size, mtimeMs }) => ({ size, mtimeMs })).catch(() => {
21917
+ const key = npm.cliPath ? `${npm.command}\x00${npm.cliPath}` : npm.command;
21918
+ const identity = await stat2(npm.cliPath ?? npm.command).then(({ size, mtimeMs }) => ({ size, mtimeMs })).catch(() => {
21595
21919
  return;
21596
21920
  });
21597
- const existing = npmCutoffSupport.get(npm.command);
21921
+ const existing = npmCutoffSupport.get(key);
21598
21922
  const sameIdentity = existing && existing.identity?.size === identity?.size && existing.identity?.mtimeMs === identity?.mtimeMs;
21599
21923
  if (existing && sameIdentity) {
21600
21924
  if (!existing.settled || identity !== undefined && existing.settled.outcome !== "transient" || clock.now() < existing.settled.retryAt)
@@ -21605,7 +21929,7 @@ async function npmSupportsCutoff(npm, clock) {
21605
21929
  return outcome;
21606
21930
  });
21607
21931
  const entry = { identity, pending };
21608
- npmCutoffSupport.set(npm.command, entry);
21932
+ npmCutoffSupport.set(key, entry);
21609
21933
  return pending;
21610
21934
  }
21611
21935
  function lspPublicationCutoff(graceDays, pinned, now = Date.now()) {
@@ -21623,6 +21947,79 @@ async function lspNpmInstallInvocation(npm, target, before, clock = SYSTEM_CLOCK
21623
21947
  target
21624
21948
  ]);
21625
21949
  }
21950
+ // ../aft-bridge/dist/lsp-npm-selection.js
21951
+ import { readFile, realpath, stat as stat3 } from "node:fs/promises";
21952
+ import { dirname as dirname6, join as join13, resolve as resolve9 } from "node:path";
21953
+ var LSP_NPM_REFUSAL = "AFT can't install language servers with the npm at <path> (<reason>). Put a real npm 7 or newer first on PATH; pinning a server version won't help.";
21954
+ function lspNpmRefusal(path, reason) {
21955
+ return LSP_NPM_REFUSAL.replace("<path>", path).replace("<reason>", reason);
21956
+ }
21957
+ async function genuineNpm(candidate) {
21958
+ let executable;
21959
+ try {
21960
+ executable = await realpath(candidate.command);
21961
+ } catch (error) {
21962
+ return { reason: `probe failed: ${String(error)}` };
21963
+ }
21964
+ const windowsShim = /\.(?:cmd|bat)$/i.test(executable);
21965
+ let current = dirname6(executable);
21966
+ if (windowsShim)
21967
+ current = join13(current, "node_modules", "npm");
21968
+ for (;; ) {
21969
+ try {
21970
+ const pkg = JSON.parse(await readFile(join13(current, "package.json"), "utf8"));
21971
+ const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.npm;
21972
+ if (pkg.name === "npm" && typeof bin === "string") {
21973
+ const cli = await realpath(resolve9(current, bin));
21974
+ if (!windowsShim && cli !== executable)
21975
+ return { reason: "not npm: command is not npm's declared CLI" };
21976
+ const major = /^(\d+)\.\d+\.\d+$/.exec(pkg.version)?.[1];
21977
+ if (!major)
21978
+ return { reason: "probe failed: npm manifest has an unknown version" };
21979
+ if (Number(major) < 7)
21980
+ return { reason: `old npm: ${pkg.version} (requires npm 7 or newer)` };
21981
+ if (windowsShim || process.platform === "win32" && /\.[cm]?js$/i.test(cli)) {
21982
+ const node = join13(candidate.binDir ?? dirname6(executable), "node.exe");
21983
+ if (!(await stat3(node)).isFile())
21984
+ return { reason: "probe failed: adjacent Node is missing" };
21985
+ return { npm: { command: node, binDir: dirname6(node), cliPath: cli }, reason: "" };
21986
+ }
21987
+ return { npm: { command: cli, binDir: candidate.binDir }, reason: "" };
21988
+ }
21989
+ } catch {}
21990
+ const parent = dirname6(current);
21991
+ if (parent === current || windowsShim)
21992
+ break;
21993
+ current = parent;
21994
+ }
21995
+ return { reason: "not npm: no matching npm package manifest and CLI" };
21996
+ }
21997
+ async function selectLspNpm(preferred, candidates, signal) {
21998
+ const aborted = () => ({
21999
+ refusal: lspNpmRefusal(preferred.command, "probe failed: installation aborted")
22000
+ });
22001
+ if (signal?.aborted)
22002
+ return aborted();
22003
+ const first = await genuineNpm(preferred);
22004
+ if (signal?.aborted)
22005
+ return aborted();
22006
+ if (first.npm)
22007
+ return { npm: first.npm };
22008
+ const seen = new Set([preferred.command]);
22009
+ for (const candidate of candidates ?? npmCandidates()) {
22010
+ if (signal?.aborted)
22011
+ return aborted();
22012
+ if (seen.has(candidate.command))
22013
+ continue;
22014
+ seen.add(candidate.command);
22015
+ const result = await genuineNpm(candidate);
22016
+ if (signal?.aborted)
22017
+ return aborted();
22018
+ if (result.npm)
22019
+ return { npm: result.npm };
22020
+ }
22021
+ return { refusal: lspNpmRefusal(preferred.command, first.reason) };
22022
+ }
21626
22023
  // ../aft-bridge/dist/message-detach.js
21627
22024
  var BASH_WAIT_DETACH_MAGIC_KEYWORD = "&detach";
21628
22025
  var STANDALONE_DETACH_KEYWORD_SOURCE = `(^|[^\\p{L}\\p{N}_])${BASH_WAIT_DETACH_MAGIC_KEYWORD}(?![\\p{L}\\p{N}_])`;
@@ -21672,14 +22069,14 @@ function shouldInterruptWaitsForMessage(detachOnUserMessage, operatorText) {
21672
22069
  return detachOnUserMessage || containsStandaloneDetachKeyword(operatorText);
21673
22070
  }
21674
22071
  // ../aft-bridge/dist/migration.js
21675
- import { closeSync as closeSync7, existsSync as existsSync9, mkdirSync as mkdirSync5, openSync as openSync6, readFileSync as readFileSync8, renameSync as renameSync5, rmSync as rmSync3, statSync as statSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync4 } from "node:fs";
22072
+ import { closeSync as closeSync8, existsSync as existsSync9, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync8, renameSync as renameSync5, rmSync as rmSync3, statSync as statSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync4 } from "node:fs";
21676
22073
  import { homedir as homedir12, tmpdir as tmpdir3 } from "node:os";
21677
- import { basename as basename2, dirname as dirname8, join as join15, resolve as resolve9 } from "node:path";
22074
+ import { basename as basename2, dirname as dirname9, join as join16, resolve as resolve11 } from "node:path";
21678
22075
 
21679
22076
  // ../aft-bridge/dist/paths.js
21680
- import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync as realpathSync4, renameSync as renameSync4, writeFileSync as writeFileSync3 } from "node:fs";
22077
+ import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync as realpathSync5, renameSync as renameSync4, writeFileSync as writeFileSync3 } from "node:fs";
21681
22078
  import { homedir as homedir10 } from "node:os";
21682
- import { dirname as dirname6, isAbsolute as isAbsolute6, join as join13, resolve as resolve8 } from "node:path";
22079
+ import { dirname as dirname7, isAbsolute as isAbsolute6, join as join14, resolve as resolve10 } from "node:path";
21683
22080
  function homeDir3() {
21684
22081
  if (process.platform === "win32")
21685
22082
  return process.env.USERPROFILE || process.env.HOME || homedir10();
@@ -21689,16 +22086,16 @@ function configHome() {
21689
22086
  const xdg = process.env.XDG_CONFIG_HOME;
21690
22087
  if (xdg && isAbsolute6(xdg))
21691
22088
  return xdg;
21692
- return join13(homeDir3(), ".config");
22089
+ return join14(homeDir3(), ".config");
21693
22090
  }
21694
22091
  function legacyOpenCodeConfigDir() {
21695
22092
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
21696
22093
  if (envDir)
21697
- return resolve8(envDir);
21698
- return join13(configHome(), "opencode");
22094
+ return resolve10(envDir);
22095
+ return join14(configHome(), "opencode");
21699
22096
  }
21700
22097
  function legacyPiAgentDir() {
21701
- return join13(homeDir3(), ".pi", "agent");
22098
+ return join14(homeDir3(), ".pi", "agent");
21702
22099
  }
21703
22100
  function legacySources(basePath, label, harness) {
21704
22101
  return [
@@ -21707,10 +22104,10 @@ function legacySources(basePath, label, harness) {
21707
22104
  ];
21708
22105
  }
21709
22106
  function resolveCortexKitUserConfigPath() {
21710
- return join13(configHome(), "cortexkit", "aft.jsonc");
22107
+ return join14(configHome(), "cortexkit", "aft.jsonc");
21711
22108
  }
21712
22109
  function resolveCortexKitProjectConfigPath(projectDirectory) {
21713
- return join13(projectDirectory, ".cortexkit", "aft.jsonc");
22110
+ return join14(projectDirectory, ".cortexkit", "aft.jsonc");
21714
22111
  }
21715
22112
  function resolveProjectConfigReadPath(projectDirectory, operatingHarness) {
21716
22113
  const canonical = resolveCortexKitProjectConfigPath(projectDirectory);
@@ -21737,14 +22134,14 @@ function resolveCortexKitConfigPaths(projectDirectory) {
21737
22134
  }
21738
22135
  function resolveLegacyAftConfigSources(projectDirectory) {
21739
22136
  const project = [
21740
- ...legacySources(join13(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
21741
- ...legacySources(join13(projectDirectory, ".pi", "aft"), "Pi project", "pi")
22137
+ ...legacySources(join14(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
22138
+ ...legacySources(join14(projectDirectory, ".pi", "aft"), "Pi project", "pi")
21742
22139
  ];
21743
22140
  const identity = (path) => {
21744
22141
  try {
21745
- return realpathSync4(path);
22142
+ return realpathSync5(path);
21746
22143
  } catch {
21747
- return resolve8(path);
22144
+ return resolve10(path);
21748
22145
  }
21749
22146
  };
21750
22147
  const protectedPaths = new Set([
@@ -21753,22 +22150,22 @@ function resolveLegacyAftConfigSources(projectDirectory) {
21753
22150
  ].map(identity));
21754
22151
  return {
21755
22152
  user: [
21756
- ...legacySources(join13(legacyOpenCodeConfigDir(), "aft"), "OpenCode user", "opencode"),
21757
- ...legacySources(join13(legacyPiAgentDir(), "aft"), "Pi user", "pi")
22153
+ ...legacySources(join14(legacyOpenCodeConfigDir(), "aft"), "OpenCode user", "opencode"),
22154
+ ...legacySources(join14(legacyPiAgentDir(), "aft"), "Pi user", "pi")
21758
22155
  ].filter((source) => !protectedPaths.has(identity(source.path))),
21759
22156
  project
21760
22157
  };
21761
22158
  }
21762
22159
  function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
21763
- return join13(storageRoot, harness, ...segments);
22160
+ return join14(storageRoot, harness, ...segments);
21764
22161
  }
21765
22162
  function repairRootScopedStorageFile(storageRoot, harness, fileName) {
21766
22163
  const harnessPath = resolveHarnessStoragePath(storageRoot, harness, fileName);
21767
- const rootPath = join13(storageRoot, fileName);
22164
+ const rootPath = join14(storageRoot, fileName);
21768
22165
  if (existsSync7(harnessPath) || !existsSync7(rootPath))
21769
22166
  return harnessPath;
21770
22167
  try {
21771
- openPrivateStorageDir(storageRoot, dirname6(harnessPath));
22168
+ openPrivateStorageDir(storageRoot, dirname7(harnessPath));
21772
22169
  renameSync4(rootPath, harnessPath);
21773
22170
  } catch {}
21774
22171
  return harnessPath;
@@ -21789,7 +22186,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
21789
22186
  return false;
21790
22187
  if (!lastVersion) {
21791
22188
  try {
21792
- openPrivateStorageDir(storageRoot, dirname6(versionFile));
22189
+ openPrivateStorageDir(storageRoot, dirname7(versionFile));
21793
22190
  writeFileSync3(versionFile, currentVersion, { mode: PRIVATE_FILE_MODE });
21794
22191
  } catch {}
21795
22192
  return false;
@@ -21801,7 +22198,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
21801
22198
  return;
21802
22199
  const versionFile = repairRootScopedStorageFile(storageRoot, harness, "last_announced_version");
21803
22200
  try {
21804
- openPrivateStorageDir(storageRoot, dirname6(versionFile));
22201
+ openPrivateStorageDir(storageRoot, dirname7(versionFile));
21805
22202
  writeFileSync3(versionFile, currentVersion, { mode: PRIVATE_FILE_MODE });
21806
22203
  } catch {}
21807
22204
  }
@@ -21850,18 +22247,18 @@ function decodeFileUrl(target) {
21850
22247
  }
21851
22248
 
21852
22249
  // ../aft-bridge/dist/resolver.js
21853
- import { closeSync as closeSync6, existsSync as existsSync8, promises as fsPromises, openSync as openSync5, readFileSync as readFileSync7, readSync as readSync5 } from "node:fs";
21854
- import { createRequire } from "node:module";
22250
+ import { closeSync as closeSync7, existsSync as existsSync8, promises as fsPromises, openSync as openSync6, readFileSync as readFileSync7, readSync as readSync6 } from "node:fs";
22251
+ import { createRequire as createRequire2 } from "node:module";
21855
22252
  import { homedir as homedir11 } from "node:os";
21856
- import { delimiter, dirname as dirname7, isAbsolute as isAbsolute7, join as join14 } from "node:path";
22253
+ import { delimiter, dirname as dirname8, isAbsolute as isAbsolute7, join as join15 } from "node:path";
21857
22254
  var ensureBinaryForResolver = ensureBinary;
21858
22255
  var npmPlatformPackageReader = null;
21859
22256
  var cacheCopiesInFlight = new Map;
21860
22257
  function copyToVersionedCache(npmBinaryPath, version) {
21861
22258
  const tag = version.startsWith("v") ? version : `v${version}`;
21862
22259
  const ext = process.platform === "win32" ? ".exe" : "";
21863
- const versionedDir = join14(getAftBinaryCacheDir(), tag);
21864
- const cachedPath = join14(versionedDir, `aft${ext}`);
22260
+ const versionedDir = join15(getAftBinaryCacheDir(), tag);
22261
+ const cachedPath = join15(versionedDir, `aft${ext}`);
21865
22262
  if (existsSync8(cachedPath) && isTrustedCachedBinary(cachedPath, version))
21866
22263
  return { cachedPath };
21867
22264
  const existing = cacheCopiesInFlight.get(cachedPath);
@@ -21907,7 +22304,7 @@ function homeDirFromEnv(env) {
21907
22304
  return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir11();
21908
22305
  }
21909
22306
  function cachedBinaryPathFromEnv(version, env, ext) {
21910
- const binaryPath = join14(getAftBinaryCacheDir(env), version, `aft${ext}`);
22307
+ const binaryPath = join15(getAftBinaryCacheDir(env), version, `aft${ext}`);
21911
22308
  return existsSync8(binaryPath) ? binaryPath : null;
21912
22309
  }
21913
22310
  async function probeBinaryCandidateOffThread(binaryPath, source, expectedVersion) {
@@ -21929,7 +22326,7 @@ function pathCandidates(env, ext) {
21929
22326
  for (const dir of rawPath.split(delimiter)) {
21930
22327
  if (!dir || !isAbsolute7(dir))
21931
22328
  continue;
21932
- const candidate = join14(dir, `aft${ext}`);
22329
+ const candidate = join15(dir, `aft${ext}`);
21933
22330
  if (seen.has(candidate))
21934
22331
  continue;
21935
22332
  seen.add(candidate);
@@ -21941,9 +22338,9 @@ function pathCandidates(env, ext) {
21941
22338
  function isNativeExecutable(binaryPath) {
21942
22339
  let fd = null;
21943
22340
  try {
21944
- fd = openSync5(binaryPath, "r");
22341
+ fd = openSync6(binaryPath, "r");
21945
22342
  const buf = Buffer.alloc(4);
21946
- const read = readSync5(fd, buf, 0, 4, 0);
22343
+ const read = readSync6(fd, buf, 0, 4, 0);
21947
22344
  if (read < 2)
21948
22345
  return false;
21949
22346
  const b0 = buf[0];
@@ -21964,7 +22361,7 @@ function isNativeExecutable(binaryPath) {
21964
22361
  } finally {
21965
22362
  if (fd !== null) {
21966
22363
  try {
21967
- closeSync6(fd);
22364
+ closeSync7(fd);
21968
22365
  } catch {}
21969
22366
  }
21970
22367
  }
@@ -21994,7 +22391,7 @@ function explicitBinaryOverride(env) {
21994
22391
  }
21995
22392
  function ownPackageVersion() {
21996
22393
  try {
21997
- const req = createRequire(import.meta.url);
22394
+ const req = createRequire2(import.meta.url);
21998
22395
  return req("../package.json").version;
21999
22396
  } catch {
22000
22397
  return null;
@@ -22004,10 +22401,10 @@ function npmPlatformPackage(ext) {
22004
22401
  if (npmPlatformPackageReader)
22005
22402
  return npmPlatformPackageReader(ext);
22006
22403
  try {
22007
- const req = createRequire(import.meta.url);
22404
+ const req = createRequire2(import.meta.url);
22008
22405
  const manifestPath = req.resolve(`@cortexkit/aft-${platformKey()}/package.json`);
22009
22406
  const manifest = JSON.parse(readFileSync7(manifestPath, "utf8"));
22010
- const binaryPath = join14(dirname7(manifestPath), "bin", `aft${ext}`);
22407
+ const binaryPath = join15(dirname8(manifestPath), "bin", `aft${ext}`);
22011
22408
  if (typeof manifest.version !== "string" || !existsSync8(binaryPath))
22012
22409
  return null;
22013
22410
  return { binaryPath, version: normalizeBareVersion(manifest.version) };
@@ -22074,7 +22471,7 @@ async function findProbedBinary(expectedVersion, env) {
22074
22471
  if (usable)
22075
22472
  return { path: usable, source: "PATH" };
22076
22473
  }
22077
- const cargoPath = join14(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
22474
+ const cargoPath = join15(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
22078
22475
  if (existsSync8(cargoPath)) {
22079
22476
  const usable = await probeBinaryCandidateOffThread(cargoPath, "cargo", pluginVersion);
22080
22477
  if (usable)
@@ -22173,9 +22570,9 @@ function dataHome() {
22173
22570
  if (process.env.XDG_DATA_HOME)
22174
22571
  return process.env.XDG_DATA_HOME;
22175
22572
  if (process.platform === "win32") {
22176
- return process.env.LOCALAPPDATA || process.env.APPDATA || join15(homeDir4(), "AppData", "Local");
22573
+ return process.env.LOCALAPPDATA || process.env.APPDATA || join16(homeDir4(), "AppData", "Local");
22177
22574
  }
22178
- return join15(homeDir4(), ".local", "share");
22575
+ return join16(homeDir4(), ".local", "share");
22179
22576
  }
22180
22577
  function homeDir4() {
22181
22578
  if (process.platform === "win32")
@@ -22184,8 +22581,8 @@ function homeDir4() {
22184
22581
  }
22185
22582
  function resolveLegacyStorageRoot(harness) {
22186
22583
  if (harness === "pi")
22187
- return join15(homeDir4(), ".pi", "agent", "aft");
22188
- return join15(dataHome(), "opencode", "storage", "plugin", "aft");
22584
+ return join16(homeDir4(), ".pi", "agent", "aft");
22585
+ return join16(dataHome(), "opencode", "storage", "plugin", "aft");
22189
22586
  }
22190
22587
  function stripJsoncForParse(input) {
22191
22588
  let out = "";
@@ -22314,19 +22711,19 @@ function acquireConfigMigrationLock(lockDir) {
22314
22711
  }
22315
22712
  }
22316
22713
  function atomicCopyConfigFile(sourcePath, targetPath) {
22317
- mkdirSync5(dirname8(targetPath), { recursive: true });
22318
- const tmpPath = join15(dirname8(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
22714
+ mkdirSync5(dirname9(targetPath), { recursive: true });
22715
+ const tmpPath = join16(dirname9(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
22319
22716
  let fd = null;
22320
22717
  try {
22321
- fd = openSync6(tmpPath, "wx", 384);
22718
+ fd = openSync7(tmpPath, "wx", 384);
22322
22719
  writeFileSync4(fd, readFileSync8(sourcePath));
22323
- closeSync7(fd);
22720
+ closeSync8(fd);
22324
22721
  fd = null;
22325
22722
  renameSync5(tmpPath, targetPath);
22326
22723
  } catch (err) {
22327
22724
  if (fd !== null) {
22328
22725
  try {
22329
- closeSync7(fd);
22726
+ closeSync8(fd);
22330
22727
  } catch {}
22331
22728
  }
22332
22729
  try {
@@ -22336,19 +22733,19 @@ function atomicCopyConfigFile(sourcePath, targetPath) {
22336
22733
  }
22337
22734
  }
22338
22735
  function atomicWriteConfigFile(targetPath, content) {
22339
- mkdirSync5(dirname8(targetPath), { recursive: true });
22340
- const tmpPath = join15(dirname8(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
22736
+ mkdirSync5(dirname9(targetPath), { recursive: true });
22737
+ const tmpPath = join16(dirname9(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
22341
22738
  let fd = null;
22342
22739
  try {
22343
- fd = openSync6(tmpPath, "wx", 384);
22740
+ fd = openSync7(tmpPath, "wx", 384);
22344
22741
  writeFileSync4(fd, content);
22345
- closeSync7(fd);
22742
+ closeSync8(fd);
22346
22743
  fd = null;
22347
22744
  renameSync5(tmpPath, targetPath);
22348
22745
  } catch (err) {
22349
22746
  if (fd !== null) {
22350
22747
  try {
22351
- closeSync7(fd);
22748
+ closeSync8(fd);
22352
22749
  } catch {}
22353
22750
  }
22354
22751
  try {
@@ -22456,13 +22853,13 @@ function visibleConfigMigrationWarning(scope, targetPath, paths, reason) {
22456
22853
  }
22457
22854
  function migrateAftConfigFile(opts) {
22458
22855
  const warnings = [];
22459
- const resolvedTarget = resolve9(opts.targetPath);
22460
- const existingSources = opts.legacySources.filter((source) => existsSync9(source.path) && resolve9(source.path) !== resolvedTarget);
22856
+ const resolvedTarget = resolve11(opts.targetPath);
22857
+ const existingSources = opts.legacySources.filter((source) => existsSync9(source.path) && resolve11(source.path) !== resolvedTarget);
22461
22858
  const info = opts.logger?.info ?? opts.logger?.log;
22462
22859
  if (existingSources.length === 0) {
22463
22860
  return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
22464
22861
  }
22465
- mkdirSync5(dirname8(opts.targetPath), { recursive: true });
22862
+ mkdirSync5(dirname9(opts.targetPath), { recursive: true });
22466
22863
  const release = acquireConfigMigrationLock(`${opts.targetPath}.lock`);
22467
22864
  try {
22468
22865
  const sources = existingSources.map((source) => ({
@@ -22519,13 +22916,13 @@ function spawnErrorLabel(error) {
22519
22916
  return [code, error.message].filter(Boolean).join(": ");
22520
22917
  }
22521
22918
  function migrationLogPath(newRoot, harness, logger) {
22522
- const desired = join15(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
22919
+ const desired = join16(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
22523
22920
  try {
22524
- mkdirSync5(dirname8(desired), { recursive: true });
22921
+ mkdirSync5(dirname9(desired), { recursive: true });
22525
22922
  return desired;
22526
22923
  } catch (err) {
22527
- const fallback = join15(tmpdir3(), `aft-migration-${harness}-${Date.now()}.jsonl`);
22528
- logger?.warn?.(`Failed to create AFT migration log directory ${dirname8(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
22924
+ const fallback = join16(tmpdir3(), `aft-migration-${harness}-${Date.now()}.jsonl`);
22925
+ logger?.warn?.(`Failed to create AFT migration log directory ${dirname9(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
22529
22926
  return fallback;
22530
22927
  }
22531
22928
  }
@@ -22584,13 +22981,13 @@ async function ensureStorageMigrated(opts) {
22584
22981
  // ../aft-bridge/dist/migration-notices.js
22585
22982
  import { readFileSync as readFileSync9, renameSync as renameSync6, rmdirSync, statSync as statSync9, writeFileSync as writeFileSync5 } from "node:fs";
22586
22983
  import { homedir as homedir13 } from "node:os";
22587
- import { dirname as dirname9, join as join16, resolve as resolve10 } from "node:path";
22984
+ import { dirname as dirname10, join as join17, resolve as resolve12 } from "node:path";
22588
22985
  function migrationNoticeStorePath(env = process.env) {
22589
- const stateHome = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0 ? env.XDG_STATE_HOME : join16(homedir13(), ".local", "state");
22590
- return join16(stateHome, "cortexkit", "aft", "migration-notices.json");
22986
+ const stateHome = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0 ? env.XDG_STATE_HOME : join17(homedir13(), ".local", "state");
22987
+ return join17(stateHome, "cortexkit", "aft", "migration-notices.json");
22591
22988
  }
22592
22989
  function noticeKey(configPath, digest) {
22593
- return `${resolve10(configPath)}|${MIGRATION_POLICY_ID}|${digest}`;
22990
+ return `${resolve12(configPath)}|${MIGRATION_POLICY_ID}|${digest}`;
22594
22991
  }
22595
22992
  function readRecords(storePath) {
22596
22993
  try {
@@ -22603,7 +23000,7 @@ function readRecords(storePath) {
22603
23000
  var LOCK_STALE_MS = 1e4;
22604
23001
  function withStoreLock(storePath, action) {
22605
23002
  const lockPath = `${storePath}.lock`;
22606
- openPrivateStorageDir(dirname9(storePath));
23003
+ openPrivateStorageDir(dirname10(storePath));
22607
23004
  const deadline = Date.now() + 2000;
22608
23005
  for (;; ) {
22609
23006
  try {
@@ -22657,7 +23054,7 @@ function deliverMigrationNoticeOnce(options) {
22657
23054
  }
22658
23055
  }
22659
23056
  // ../aft-bridge/dist/onnx-probe.js
22660
- import { closeSync as closeSync8, existsSync as existsSync10, openSync as openSync7, readSync as readSync6, realpathSync as realpathSync5 } from "node:fs";
23057
+ import { closeSync as closeSync9, existsSync as existsSync10, openSync as openSync8, readSync as readSync7, realpathSync as realpathSync6 } from "node:fs";
22661
23058
  var MH_MAGIC = 4277009102;
22662
23059
  var MH_MAGIC_64 = 4277009103;
22663
23060
  var FAT_MAGIC = 3405691582;
@@ -22670,7 +23067,7 @@ var MAX_HEADER_BYTES = 4 * 1024 * 1024;
22670
23067
  var UNCHECKED = { loadable: true, checked: false };
22671
23068
  function readAt(fd, offset, length) {
22672
23069
  const buffer = Buffer.alloc(length);
22673
- const read = readSync6(fd, buffer, 0, length, offset);
23070
+ const read = readSync7(fd, buffer, 0, length, offset);
22674
23071
  return buffer.subarray(0, read);
22675
23072
  }
22676
23073
  function cpuTypeForArch(arch) {
@@ -22744,7 +23141,7 @@ function isUncheckableDependency(path) {
22744
23141
  function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
22745
23142
  let fd;
22746
23143
  try {
22747
- fd = openSync7(realpathSync5(libPath), "r");
23144
+ fd = openSync8(realpathSync6(libPath), "r");
22748
23145
  } catch (err) {
22749
23146
  return {
22750
23147
  loadable: false,
@@ -22778,13 +23175,13 @@ function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
22778
23175
  } catch {
22779
23176
  return UNCHECKED;
22780
23177
  } finally {
22781
- closeSync8(fd);
23178
+ closeSync9(fd);
22782
23179
  }
22783
23180
  }
22784
23181
  // ../aft-bridge/dist/onnx-runtime.js
22785
23182
  import { randomBytes as randomBytes3 } from "node:crypto";
22786
- import { chmodSync as chmodSync2, closeSync as closeSync9, copyFileSync as copyFileSync4, createWriteStream as createWriteStream3, existsSync as existsSync11, lstatSync as lstatSync3, openSync as openSync8, readdirSync as readdirSync4, readFileSync as readFileSync10, readlinkSync as readlinkSync2, realpathSync as realpathSync6, renameSync as renameSync7, rmSync as rmSync4, statSync as statSync10, symlinkSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "node:fs";
22787
- import { basename as basename3, dirname as dirname10, isAbsolute as isAbsolute8, join as join17, relative as relative5, resolve as resolve11, win32 as win323 } from "node:path";
23183
+ import { chmodSync as chmodSync2, closeSync as closeSync10, copyFileSync as copyFileSync4, createWriteStream as createWriteStream3, existsSync as existsSync11, lstatSync as lstatSync3, openSync as openSync9, readdirSync as readdirSync4, readFileSync as readFileSync10, readlinkSync as readlinkSync2, realpathSync as realpathSync7, renameSync as renameSync7, rmSync as rmSync4, statSync as statSync10, symlinkSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "node:fs";
23184
+ import { basename as basename3, dirname as dirname11, isAbsolute as isAbsolute8, join as join18, relative as relative5, resolve as resolve13, win32 as win323 } from "node:path";
22788
23185
  import { Readable as Readable3 } from "node:stream";
22789
23186
  import { pipeline as pipeline3 } from "node:stream/promises";
22790
23187
  var ORT_VERSION = "1.24.4";
@@ -22872,7 +23269,7 @@ function resolveOnnxRuntime(storageDir, seams) {
22872
23269
  }
22873
23270
  async function findCachedOnnxRuntime(ortVersionDir, libName) {
22874
23271
  const resolvedOrtDir = resolveCachedOnnxRuntimeDir(ortVersionDir, libName);
22875
- const libPath = join17(resolvedOrtDir, libName);
23272
+ const libPath = join18(resolvedOrtDir, libName);
22876
23273
  if (existsSync11(libPath)) {
22877
23274
  const meta = readOnnxInstalledMeta(ortVersionDir);
22878
23275
  if (meta?.sha256) {
@@ -22897,7 +23294,7 @@ async function findCachedOnnxRuntime(ortVersionDir, libName) {
22897
23294
  }
22898
23295
  async function resolveOnnxRuntimeUncoalesced(storageDir, seams) {
22899
23296
  const info = seams.platformInfo !== undefined ? seams.platformInfo : getPlatformInfo();
22900
- const ortVersionDir = join17(storageDir, "onnxruntime", ORT_VERSION);
23297
+ const ortVersionDir = join18(storageDir, "onnxruntime", ORT_VERSION);
22901
23298
  const libName = info?.libName ?? "libonnxruntime.dylib";
22902
23299
  const cached = await findCachedOnnxRuntime(ortVersionDir, libName);
22903
23300
  if (cached)
@@ -22911,9 +23308,9 @@ async function resolveOnnxRuntimeUncoalesced(storageDir, seams) {
22911
23308
  warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
22912
23309
  return null;
22913
23310
  }
22914
- const onnxBaseDir = join17(storageDir, "onnxruntime");
23311
+ const onnxBaseDir = join18(storageDir, "onnxruntime");
22915
23312
  openPrivateStorageDir(storageDir, onnxBaseDir);
22916
- const lockPath = join17(onnxBaseDir, ONNX_LOCK_FILE);
23313
+ const lockPath = join18(onnxBaseDir, ONNX_LOCK_FILE);
22917
23314
  const pollMs = seams.lockPollMs ?? LOCK_POLL_MS;
22918
23315
  const deadline = Date.now() + LOCK_WAIT_MS;
22919
23316
  let announcedWait = false;
@@ -22958,7 +23355,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
22958
23355
  for (const entry of entries) {
22959
23356
  if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
22960
23357
  continue;
22961
- const stagingDir = join17(onnxBaseDir, entry);
23358
+ const stagingDir = join18(onnxBaseDir, entry);
22962
23359
  const parts = entry.split(".");
22963
23360
  const pidStr = parts[parts.length - 2];
22964
23361
  const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
@@ -22995,7 +23392,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
22995
23392
  }
22996
23393
  function cleanupIncompleteTargetIfUnowned(ortDir) {
22997
23394
  try {
22998
- if (existsSync11(ortDir) && !existsSync11(join17(ortDir, ONNX_INSTALLED_META_FILE))) {
23395
+ if (existsSync11(ortDir) && !existsSync11(join18(ortDir, ONNX_INSTALLED_META_FILE))) {
22999
23396
  log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
23000
23397
  rmSync4(ortDir, { recursive: true, force: true });
23001
23398
  }
@@ -23030,7 +23427,7 @@ function detectOnnxVersion(libDir, libName) {
23030
23427
  try {
23031
23428
  const entries = readdirSync4(libDir);
23032
23429
  try {
23033
- const resolved = parseOnnxVersionFromPath(realpathSync6(join17(libDir, libName)));
23430
+ const resolved = parseOnnxVersionFromPath(realpathSync7(join18(libDir, libName)));
23034
23431
  if (resolved && resolved !== INVALID_ORT_VERSION)
23035
23432
  return resolved;
23036
23433
  } catch {}
@@ -23044,10 +23441,10 @@ function detectOnnxVersion(libDir, libName) {
23044
23441
  if (version)
23045
23442
  return version;
23046
23443
  }
23047
- const base = join17(libDir, libName);
23444
+ const base = join18(libDir, libName);
23048
23445
  if (existsSync11(base)) {
23049
23446
  try {
23050
- const real = realpathSync6(base);
23447
+ const real = realpathSync7(base);
23051
23448
  const version = parseOnnxVersionFromPath(real) ?? parseOnnxVersionFromDirectoryPath(real);
23052
23449
  if (version)
23053
23450
  return version;
@@ -23111,10 +23508,10 @@ function directoryContainsLibrary(dir, libName) {
23111
23508
  }
23112
23509
  }
23113
23510
  function resolveCachedOnnxRuntimeDir(ortVersionDir, libName) {
23114
- if (existsSync11(join17(ortVersionDir, libName)))
23511
+ if (existsSync11(join18(ortVersionDir, libName)))
23115
23512
  return ortVersionDir;
23116
- const libSubdir = join17(ortVersionDir, "lib");
23117
- if (existsSync11(join17(libSubdir, libName)))
23513
+ const libSubdir = join18(ortVersionDir, "lib");
23514
+ if (existsSync11(join18(libSubdir, libName)))
23118
23515
  return libSubdir;
23119
23516
  return ortVersionDir;
23120
23517
  }
@@ -23129,12 +23526,12 @@ function findSystemOnnxRuntime(libName, searchPathsOverride) {
23129
23526
  } else if (process.platform === "win32") {
23130
23527
  const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
23131
23528
  const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
23132
- searchPaths.push(join17(programFiles, "onnxruntime", "lib"), join17(programFiles, "Microsoft ONNX Runtime", "lib"), join17(programFiles, "Microsoft Machine Learning", "lib"), join17(programFilesX86, "onnxruntime", "lib"), ...(() => {
23529
+ searchPaths.push(join18(programFiles, "onnxruntime", "lib"), join18(programFiles, "Microsoft ONNX Runtime", "lib"), join18(programFiles, "Microsoft Machine Learning", "lib"), join18(programFilesX86, "onnxruntime", "lib"), ...(() => {
23133
23530
  const nugetPaths = [];
23134
23531
  const userProfile = process.env.USERPROFILE ?? "";
23135
23532
  if (!userProfile)
23136
23533
  return nugetPaths;
23137
- const nugetPackageDir = join17(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
23534
+ const nugetPackageDir = join18(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
23138
23535
  if (!existsSync11(nugetPackageDir))
23139
23536
  return nugetPaths;
23140
23537
  try {
@@ -23143,7 +23540,7 @@ function findSystemOnnxRuntime(libName, searchPathsOverride) {
23143
23540
  continue;
23144
23541
  if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
23145
23542
  continue;
23146
- nugetPaths.push(join17(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join17(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
23543
+ nugetPaths.push(join18(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join18(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
23147
23544
  }
23148
23545
  } catch (err) {
23149
23546
  warn(`Failed to scan NuGet ONNX Runtime cache ${nugetPackageDir}: ${err instanceof Error ? err.message : String(err)}`);
@@ -23155,7 +23552,7 @@ function findSystemOnnxRuntime(libName, searchPathsOverride) {
23155
23552
  const normalizeCase = process.platform === "win32" || process.platform === "darwin";
23156
23553
  const seen = new Set;
23157
23554
  const uniquePaths = searchPaths.filter((p) => {
23158
- let key = resolve11(p).replace(/[/\\]+$/, "");
23555
+ let key = resolve13(p).replace(/[/\\]+$/, "");
23159
23556
  if (normalizeCase)
23160
23557
  key = key.toLowerCase();
23161
23558
  if (seen.has(key))
@@ -23165,7 +23562,7 @@ function findSystemOnnxRuntime(libName, searchPathsOverride) {
23165
23562
  });
23166
23563
  const unknownVersionPaths = [];
23167
23564
  for (const dir of uniquePaths) {
23168
- const libPath = join17(dir, libName);
23565
+ const libPath = join18(dir, libName);
23169
23566
  if (process.platform === "win32") {
23170
23567
  if (!directoryContainsLibrary(dir, libName))
23171
23568
  continue;
@@ -23212,7 +23609,7 @@ async function downloadFileWithCap(url, destPath) {
23212
23609
  if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
23213
23610
  throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
23214
23611
  }
23215
- privateMkdirSync(dirname10(destPath));
23612
+ privateMkdirSync(dirname11(destPath));
23216
23613
  let bytesWritten = 0;
23217
23614
  const guard = new TransformStream({
23218
23615
  transform(chunk, transformController) {
@@ -23239,16 +23636,16 @@ async function downloadFileWithCap(url, destPath) {
23239
23636
  }
23240
23637
  }
23241
23638
  function validateExtractedTree(stagingRoot) {
23242
- const realRoot = realpathSync6(stagingRoot);
23639
+ const realRoot = realpathSync7(stagingRoot);
23243
23640
  let totalBytes = 0;
23244
23641
  const walk = (dir) => {
23245
23642
  const entries = readdirSync4(dir);
23246
23643
  for (const entry of entries) {
23247
- const fullPath = join17(dir, entry);
23644
+ const fullPath = join18(dir, entry);
23248
23645
  const lst = lstatSync3(fullPath);
23249
23646
  if (lst.isSymbolicLink()) {
23250
23647
  const linkTarget = readlinkSync2(fullPath);
23251
- const resolvedTarget = resolve11(dirname10(fullPath), linkTarget);
23648
+ const resolvedTarget = resolve13(dirname11(fullPath), linkTarget);
23252
23649
  const rel = relative5(realRoot, resolvedTarget);
23253
23650
  if (relativePathEscapesRoot(rel) || win323.isAbsolute(rel)) {
23254
23651
  throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
@@ -23281,11 +23678,11 @@ async function downloadOnnxRuntime(info, targetDir) {
23281
23678
  const url = `https://github.com/${ORT_REPO}/releases/download/v${ORT_VERSION}/${info.assetName}.${info.archiveType === "tgz" ? "tgz" : "zip"}`;
23282
23679
  log(`Downloading ONNX Runtime v${ORT_VERSION} for ${process.platform}/${process.arch}...`);
23283
23680
  const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}${randomBytes3(4).toString("hex")}`;
23284
- const extractionRoot = join17(tmpDir, "extract");
23285
- const stagedInstallDir = join17(tmpDir, "install");
23681
+ const extractionRoot = join18(tmpDir, "extract");
23682
+ const stagedInstallDir = join18(tmpDir, "install");
23286
23683
  try {
23287
23684
  privateMkdirSync(extractionRoot);
23288
- const archivePath = join17(tmpDir, `onnxruntime.${info.archiveType}`);
23685
+ const archivePath = join18(tmpDir, `onnxruntime.${info.archiveType}`);
23289
23686
  await downloadFileWithCap(url, archivePath);
23290
23687
  const archiveSha256 = await sha256File(archivePath);
23291
23688
  log(`ONNX Runtime archive sha256=${archiveSha256}`);
@@ -23298,7 +23695,7 @@ async function downloadOnnxRuntime(info, targetDir) {
23298
23695
  unlinkSync5(archivePath);
23299
23696
  } catch {}
23300
23697
  validateExtractedTree(extractionRoot);
23301
- const extractedDir = join17(extractionRoot, info.assetName, "lib");
23698
+ const extractedDir = join18(extractionRoot, info.assetName, "lib");
23302
23699
  if (!existsSync11(extractedDir)) {
23303
23700
  throw new Error(`Expected directory not found: ${extractedDir}`);
23304
23701
  }
@@ -23306,7 +23703,7 @@ async function downloadOnnxRuntime(info, targetDir) {
23306
23703
  const realFiles = [];
23307
23704
  const symlinks = [];
23308
23705
  for (const libFile of libFiles) {
23309
- const src = join17(extractedDir, libFile);
23706
+ const src = join18(extractedDir, libFile);
23310
23707
  try {
23311
23708
  const stat = lstatSync3(src);
23312
23709
  log(`ORT extract: ${libFile} — isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
@@ -23321,14 +23718,14 @@ async function downloadOnnxRuntime(info, targetDir) {
23321
23718
  }
23322
23719
  }
23323
23720
  copyOnnxLibraries(info, extractedDir, stagedInstallDir, realFiles, symlinks);
23324
- const libPath = join17(stagedInstallDir, info.libName);
23721
+ const libPath = join18(stagedInstallDir, info.libName);
23325
23722
  const libHash = await sha256File(libPath);
23326
23723
  if (libHash !== info.librarySha256) {
23327
23724
  throw new Error(`onnx runtime library hash mismatch at ${libPath}: sha256 ${libHash}, ` + `expected the pinned ${info.librarySha256} for ${info.assetName}`);
23328
23725
  }
23329
23726
  writeOnnxInstalledMeta(stagedInstallDir, ORT_VERSION, libHash, archiveSha256);
23330
23727
  publishOnnxRuntime(stagedInstallDir, targetDir);
23331
- writeStampedFileDigest(join17(targetDir, info.libName), libHash);
23728
+ writeStampedFileDigest(join18(targetDir, info.libName), libHash);
23332
23729
  rmSync4(tmpDir, { recursive: true, force: true });
23333
23730
  log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
23334
23731
  return targetDir;
@@ -23370,8 +23767,8 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
23370
23767
  const requiredLibs = new Set([info.libName]);
23371
23768
  privateMkdirSync(targetDir);
23372
23769
  for (const libFile of realFiles) {
23373
- const src = join17(extractedDir, libFile);
23374
- const dst = join17(targetDir, libFile);
23770
+ const src = join18(extractedDir, libFile);
23771
+ const dst = join18(targetDir, libFile);
23375
23772
  try {
23376
23773
  if (copyFile === copyFileSync4)
23377
23774
  privateCopyFileSync(src, dst);
@@ -23388,14 +23785,14 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
23388
23785
  log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
23389
23786
  }
23390
23787
  }
23391
- const targetRoot = realpathSync6(targetDir);
23788
+ const targetRoot = realpathSync7(targetDir);
23392
23789
  for (const link of symlinks) {
23393
- const dst = join17(targetDir, link.name);
23790
+ const dst = join18(targetDir, link.name);
23394
23791
  try {
23395
23792
  unlinkSync5(dst);
23396
23793
  } catch {}
23397
- const dstForContainment = join17(targetRoot, link.name);
23398
- const resolvedTarget = resolve11(dirname10(dstForContainment), link.target);
23794
+ const dstForContainment = join18(targetRoot, link.name);
23795
+ const resolvedTarget = resolve13(dirname11(dstForContainment), link.target);
23399
23796
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
23400
23797
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
23401
23798
  if (requiredLibs.has(link.name)) {
@@ -23415,7 +23812,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
23415
23812
  log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
23416
23813
  }
23417
23814
  }
23418
- const requiredPath = join17(targetDir, info.libName);
23815
+ const requiredPath = join18(targetDir, info.libName);
23419
23816
  if (!existsSync11(requiredPath)) {
23420
23817
  rmSync4(targetDir, { recursive: true, force: true });
23421
23818
  throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
@@ -23443,7 +23840,7 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
23443
23840
  ...sha256 ? { sha256 } : {},
23444
23841
  archiveSha256
23445
23842
  };
23446
- writeFileSync6(join17(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), {
23843
+ writeFileSync6(join18(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), {
23447
23844
  encoding: "utf8",
23448
23845
  mode: PRIVATE_FILE_MODE
23449
23846
  });
@@ -23452,7 +23849,7 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
23452
23849
  }
23453
23850
  }
23454
23851
  function readOnnxInstalledMeta(installDir) {
23455
- const path = join17(installDir, ONNX_INSTALLED_META_FILE);
23852
+ const path = join18(installDir, ONNX_INSTALLED_META_FILE);
23456
23853
  try {
23457
23854
  if (!statSync10(path).isFile())
23458
23855
  return null;
@@ -23473,13 +23870,13 @@ function readOnnxInstalledMeta(installDir) {
23473
23870
  function acquireLock(lockPath) {
23474
23871
  const tryClaim = () => {
23475
23872
  try {
23476
- const fd = openSync8(lockPath, "wx", PRIVATE_FILE_MODE);
23873
+ const fd = openSync9(lockPath, "wx", PRIVATE_FILE_MODE);
23477
23874
  try {
23478
23875
  writeFileSync6(fd, `${process.pid}
23479
23876
  ${new Date().toISOString()}
23480
23877
  `);
23481
23878
  } finally {
23482
- closeSync9(fd);
23879
+ closeSync10(fd);
23483
23880
  }
23484
23881
  return true;
23485
23882
  } catch (err) {
@@ -25375,7 +25772,7 @@ import {
25375
25772
  // package.json
25376
25773
  var package_default = {
25377
25774
  name: "@cortexkit/aft-pi",
25378
- version: "0.63.5",
25775
+ version: "0.64.0",
25379
25776
  type: "module",
25380
25777
  description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
25381
25778
  main: "dist/index.js",
@@ -25399,18 +25796,18 @@ var package_default = {
25399
25796
  "test:e2e": "bun test src/__tests__/e2e/"
25400
25797
  },
25401
25798
  dependencies: {
25402
- "@cortexkit/aft-bridge": "0.63.5",
25799
+ "@cortexkit/aft-bridge": "0.64.0",
25403
25800
  "comment-json": "^5.0.0",
25404
25801
  diff: "^8.0.4",
25405
25802
  zod: "^4.4.3"
25406
25803
  },
25407
25804
  optionalDependencies: {
25408
- "@cortexkit/aft-darwin-arm64": "0.63.5",
25409
- "@cortexkit/aft-darwin-x64": "0.63.5",
25410
- "@cortexkit/aft-linux-arm64": "0.63.5",
25411
- "@cortexkit/aft-linux-x64": "0.63.5",
25412
- "@cortexkit/aft-win32-arm64": "0.63.5",
25413
- "@cortexkit/aft-win32-x64": "0.63.5"
25805
+ "@cortexkit/aft-darwin-arm64": "0.64.0",
25806
+ "@cortexkit/aft-darwin-x64": "0.64.0",
25807
+ "@cortexkit/aft-linux-arm64": "0.64.0",
25808
+ "@cortexkit/aft-linux-x64": "0.64.0",
25809
+ "@cortexkit/aft-win32-arm64": "0.64.0",
25810
+ "@cortexkit/aft-win32-x64": "0.64.0"
25414
25811
  },
25415
25812
  devDependencies: {
25416
25813
  "@earendil-works/pi-coding-agent": "^0.80.2",
@@ -25490,22 +25887,15 @@ function readStatusBar(value) {
25490
25887
  const disabled = Array.isArray(bar.disabled_categories) ? bar.disabled_categories.filter((key) => typeof key === "string") : [];
25491
25888
  const errors = readOptionalNumber(bar.errors);
25492
25889
  const warnings = readOptionalNumber(bar.warnings);
25493
- const deadCode = readOptionalNumber(bar.dead_code);
25494
- const unusedExports = readOptionalNumber(bar.unused_exports);
25495
- const duplicates = readOptionalNumber(bar.duplicates);
25496
25890
  const todos = readOptionalNumber(bar.todos);
25497
- if (errors === null && warnings === null && deadCode === null && unusedExports === null && duplicates === null && todos === null && disabled.length === 0) {
25891
+ if (errors === null && warnings === null && todos === null && disabled.length === 0) {
25498
25892
  return;
25499
25893
  }
25500
25894
  return {
25501
25895
  ...disabled.length > 0 ? { disabled_categories: disabled } : {},
25502
25896
  ...errors !== null ? { errors } : {},
25503
25897
  ...warnings !== null ? { warnings } : {},
25504
- ...deadCode !== null ? { dead_code: deadCode } : {},
25505
- ...unusedExports !== null ? { unused_exports: unusedExports } : {},
25506
- ...duplicates !== null ? { duplicates } : {},
25507
- ...todos !== null ? { todos } : {},
25508
- ...bar.tier2_stale === true ? { tier2_stale: true } : {}
25898
+ ...todos !== null ? { todos } : {}
25509
25899
  };
25510
25900
  }
25511
25901
  function formatFlag(enabled) {
@@ -25661,13 +26051,10 @@ function formatStatusDialogMessage(status) {
25661
26051
  const rows = [
25662
26052
  ["errors", sb.errors],
25663
26053
  ["warnings", sb.warnings],
25664
- ["dead code", sb.dead_code],
25665
- ["unused exports", sb.unused_exports],
25666
- ["duplicates", sb.duplicates],
25667
26054
  ["todos", sb.todos]
25668
26055
  ].filter((row) => typeof row[1] === "number");
25669
26056
  if (rows.length > 0) {
25670
- lines.push("", `Code Health${sb.tier2_stale ? " (~ stale)" : ""}`);
26057
+ lines.push("", "Code Health");
25671
26058
  for (const [label, value] of rows)
25672
26059
  lines.push(`- ${label}: ${formatCount(value)}`);
25673
26060
  }
@@ -30505,9 +30892,8 @@ function clampBashWatchSyncMaxMs(value) {
30505
30892
  }
30506
30893
  function resolveBashConfig(config) {
30507
30894
  const top = config.bash;
30508
- const legacy = config.experimental?.bash;
30509
- const reminderEnabled = (typeof top === "object" && top !== null ? top.long_running_reminder_enabled : undefined) ?? legacy?.long_running_reminder_enabled;
30510
- const reminderInterval = (typeof top === "object" && top !== null ? top.long_running_reminder_interval_ms : undefined) ?? legacy?.long_running_reminder_interval_ms;
30895
+ const reminderEnabled = typeof top === "object" && top !== null ? top.long_running_reminder_enabled : undefined;
30896
+ const reminderInterval = typeof top === "object" && top !== null ? top.long_running_reminder_interval_ms : undefined;
30511
30897
  const topDetachOnUserMessage = typeof top === "object" && top !== null ? top.detach_on_user_message ?? true : true;
30512
30898
  const rawForegroundWait = typeof top === "object" && top !== null ? top.foreground_wait_window_ms : undefined;
30513
30899
  const foregroundWaitWindowMs = Math.max(FOREGROUND_WAIT_WINDOW_MIN_MS, rawForegroundWait ?? FOREGROUND_WAIT_WINDOW_DEFAULT_MS);
@@ -30551,13 +30937,6 @@ function resolveBashConfig(config) {
30551
30937
  detach_on_user_message: topDetachOnUserMessage
30552
30938
  };
30553
30939
  }
30554
- const hasLegacyFeatureFlag = legacy && (legacy.rewrite !== undefined || legacy.compress !== undefined || legacy.background !== undefined);
30555
- if (hasLegacyFeatureFlag) {
30556
- const rewrite = legacy.rewrite === true;
30557
- const compress = legacy.compress === true;
30558
- const background = legacy.background === true;
30559
- return { ...base, enabled: true, rewrite, compress, background };
30560
- }
30561
30940
  return { ...base, enabled: true, rewrite: true, compress: true, background: true };
30562
30941
  }
30563
30942
  var FormatterEnum = _enum([
@@ -30659,13 +31038,6 @@ var LspConfigSchema = object({
30659
31038
  versions: record(string2().trim().min(1), string2().trim().min(1)).optional()
30660
31039
  });
30661
31040
  var ExperimentalConfigSchema = object({
30662
- bash: object({
30663
- rewrite: boolean2().optional(),
30664
- compress: boolean2().optional(),
30665
- background: boolean2().optional(),
30666
- long_running_reminder_enabled: boolean2().optional(),
30667
- long_running_reminder_interval_ms: number2().int().positive().optional()
30668
- }).optional(),
30669
31041
  lsp_ty: boolean2().optional()
30670
31042
  });
30671
31043
  var BashEnvSchema = record(string2(), string2()).superRefine((env, context) => {
@@ -30763,7 +31135,6 @@ var PiConfigSchema = object({
30763
31135
  var InspectConfigSchema = object({
30764
31136
  enabled: boolean2().optional(),
30765
31137
  diagnostics_timeout_ms: number2().int().positive().optional().transform((value) => value === undefined ? undefined : clampInspectDiagnosticsTimeoutMs(value)),
30766
- tier2_idle_minutes: number2().min(0).optional(),
30767
31138
  tier2_pass_timeout_ms: number2().int().positive().optional(),
30768
31139
  categories: object({
30769
31140
  diagnostics: boolean2().optional(),
@@ -30781,11 +31152,16 @@ var InspectConfigSchema = object({
30781
31152
  function clampIdleRootTtlMinutes(value) {
30782
31153
  return Math.min(30, Math.max(5, value));
30783
31154
  }
31155
+ function clampIdleCheckoutHours(value) {
31156
+ return Math.min(IDLE_CHECKOUT_HOURS_MAX, Math.max(IDLE_CHECKOUT_HOURS_MIN, value));
31157
+ }
30784
31158
  var IdleConfigSchema = object({
30785
- root_ttl_minutes: number2().int().optional().transform((value) => value === undefined ? undefined : clampIdleRootTtlMinutes(value))
31159
+ root_ttl_minutes: number2().int().optional().transform((value) => value === undefined ? undefined : clampIdleRootTtlMinutes(value)),
31160
+ checkout_hours: number2().int().optional().transform((value) => value === undefined ? undefined : clampIdleCheckoutHours(value))
30786
31161
  });
30787
31162
  var ViewsConfigSchema = object({
30788
- enabled: boolean2().optional()
31163
+ enabled: boolean2().optional(),
31164
+ callgraph_copies: number2().int().optional()
30789
31165
  });
30790
31166
  var WorktreeConfigSchema = object({
30791
31167
  ram_overlay: boolean2().optional()
@@ -30863,6 +31239,11 @@ function applyActiveHarnessOverride(config, trusted) {
30863
31239
  delete merged.indexes;
30864
31240
  else
30865
31241
  merged.indexes = indexes;
31242
+ const idle = mergeIdleConfig(base.idle, override.idle);
31243
+ if (idle === undefined)
31244
+ delete merged.idle;
31245
+ else
31246
+ merged.idle = idle;
30866
31247
  merged.sandbox = mergeSandboxConfig(base.sandbox, override.sandbox, true);
30867
31248
  if (!trusted && override.sandbox?.write_allow !== undefined) {
30868
31249
  merged.sandbox = { ...merged.sandbox, write_allow: override.sandbox.write_allow };
@@ -30965,7 +31346,7 @@ function loadConfigFromPath(configPath, tier) {
30965
31346
  recordConfigParseFailure(configPath, errorMsg);
30966
31347
  throw new ConfigRejectedError(["invalid_config_document"], configPath);
30967
31348
  }
30968
- const removed = removedConfigKeyErrors(cleanConfig);
31349
+ const removed = removedConfigKeyErrors(cleanConfig, undefined, false);
30969
31350
  if (removed.length) {
30970
31351
  throw new ConfigRejectedError(removed);
30971
31352
  }
@@ -30988,9 +31369,18 @@ function loadConfigFromPath(configPath, tier) {
30988
31369
  warn2(text);
30989
31370
  }
30990
31371
  }
30991
- if (translation.legacyInput && tier === "user") {
30992
- log2(`Config ${configPath} uses retired keys (${translation.retiredKeys.join(", ")}); applied their current equivalents in memory`);
30993
- } else if (translation.legacyInput) {
31372
+ if (tier === "user") {
31373
+ if (translation.legacyInput) {
31374
+ log2(`Config ${configPath} uses retired keys (${translation.retiredKeys.join(", ")}); applied their current equivalents in memory`);
31375
+ }
31376
+ if (translation.removedKeys.length > 0) {
31377
+ configLoadNotices.push({
31378
+ configPath,
31379
+ digest: removedConfigKeysNoticeDigest(translation),
31380
+ message: removedConfigKeysNoticeMessage(configPath, translation)
31381
+ });
31382
+ }
31383
+ } else if (translation.legacyInput || translation.removedKeys.length > 0) {
30994
31384
  configLoadNotices.push({
30995
31385
  configPath,
30996
31386
  digest: noticeDigest(projection),
@@ -31104,6 +31494,13 @@ function mergeWorktreeConfig(baseWorktree, overrideWorktree) {
31104
31494
  return overrideWorktree;
31105
31495
  return { ...baseWorktree, ...overrideWorktree };
31106
31496
  }
31497
+ function mergeIdleConfig(baseIdle, overrideIdle) {
31498
+ const merged = {
31499
+ root_ttl_minutes: overrideIdle?.root_ttl_minutes ?? baseIdle?.root_ttl_minutes,
31500
+ checkout_hours: overrideIdle?.checkout_hours ?? baseIdle?.checkout_hours
31501
+ };
31502
+ return merged.root_ttl_minutes === undefined && merged.checkout_hours === undefined ? undefined : merged;
31503
+ }
31107
31504
  function mergeInspectConfig(baseInspect, overrideInspect) {
31108
31505
  const diagnosticsTimeoutConfigured = baseInspect?.diagnostics_timeout_ms !== undefined || overrideInspect?.diagnostics_timeout_ms !== undefined;
31109
31506
  const diagnosticsTimeoutMs = Math.max(clampInspectDiagnosticsTimeoutMs(baseInspect?.diagnostics_timeout_ms ?? DEFAULT_INSPECT_DIAGNOSTICS_TIMEOUT_MS), clampInspectDiagnosticsTimeoutMs(overrideInspect?.diagnostics_timeout_ms ?? DEFAULT_INSPECT_DIAGNOSTICS_TIMEOUT_MS));
@@ -31182,19 +31579,10 @@ function mergeBashConfig(baseBash, overrideBash, mode = "tier") {
31182
31579
  return merged;
31183
31580
  }
31184
31581
  function mergeExperimentalConfig(base, override) {
31185
- const bash = {
31186
- ...base?.bash,
31187
- ...override?.bash
31188
- };
31189
31582
  const experimental = {
31190
31583
  ...base,
31191
31584
  ...override
31192
31585
  };
31193
- if (Object.values(bash).some((value) => value !== undefined)) {
31194
- experimental.bash = bash;
31195
- } else {
31196
- delete experimental.bash;
31197
- }
31198
31586
  if (Object.values(experimental).every((value) => value === undefined))
31199
31587
  return;
31200
31588
  return Object.fromEntries(Object.entries(experimental).filter(([, value]) => value !== undefined));
@@ -31341,17 +31729,21 @@ function mergeConfigs(base, override) {
31341
31729
  }
31342
31730
  const inspect = mergeInspectConfig(base.inspect, override.inspect);
31343
31731
  const worktree = mergeWorktreeConfig(base.worktree, override.worktree);
31732
+ const idle = mergeIdleConfig(base.idle, override.idle);
31344
31733
  const sandbox = mergeSandboxConfig(base.sandbox, override.sandbox);
31345
31734
  const remoteExec = mergeRemoteExecConfig(base.remote_exec, override.remote_exec);
31346
31735
  const backup = mergeProjectBackupConfig(base.backup, override.backup);
31347
31736
  const pi = mergePiConfig(base.pi, override.pi);
31737
+ const views = base.views !== undefined || override.views !== undefined ? { ...base.views, ...override.views } : undefined;
31348
31738
  const bridge = base.bridge;
31349
31739
  const safeOverride = pickProjectSafeFields(override);
31350
31740
  delete safeOverride.indexes;
31351
31741
  delete safeOverride.bash;
31352
31742
  delete safeOverride.inspect;
31353
31743
  delete safeOverride.worktree;
31744
+ delete safeOverride.idle;
31354
31745
  delete safeOverride.pi;
31746
+ delete safeOverride.views;
31355
31747
  return {
31356
31748
  ...base,
31357
31749
  ...safeOverride,
@@ -31361,10 +31753,12 @@ function mergeConfigs(base, override) {
31361
31753
  ...bash !== undefined ? { bash } : {},
31362
31754
  ...inspect !== undefined ? { inspect } : {},
31363
31755
  ...worktree !== undefined ? { worktree } : {},
31756
+ ...idle !== undefined ? { idle } : {},
31364
31757
  ...sandbox !== undefined ? { sandbox } : {},
31365
31758
  ...remoteExec !== undefined ? { remote_exec: remoteExec } : {},
31366
31759
  ...backup !== undefined ? { backup } : {},
31367
31760
  ...pi !== undefined ? { pi } : {},
31761
+ ...views !== undefined ? { views } : {},
31368
31762
  experimental,
31369
31763
  semantic,
31370
31764
  search,
@@ -31680,9 +32074,9 @@ function detectPiHarness(api) {
31680
32074
  }
31681
32075
 
31682
32076
  // src/omp-approval.ts
31683
- import { realpathSync as realpathSync7 } from "node:fs";
32077
+ import { realpathSync as realpathSync8 } from "node:fs";
31684
32078
  import { homedir as homedir15 } from "node:os";
31685
- import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute10, join as join18, relative as relative6, resolve as resolve12, sep as sep2 } from "node:path";
32079
+ import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute10, join as join19, relative as relative6, resolve as resolve14, sep as sep2 } from "node:path";
31686
32080
  var OMP_TOOL_TIERS = {
31687
32081
  read: "read",
31688
32082
  grep: "read",
@@ -31727,7 +32121,7 @@ function expandPath(value) {
31727
32121
  if (decoded === "~")
31728
32122
  return homedir15();
31729
32123
  if (decoded.startsWith("~/") || decoded.startsWith(`~${sep2}`))
31730
- return resolve12(homedir15(), decoded.slice(2));
32124
+ return resolve14(homedir15(), decoded.slice(2));
31731
32125
  return decoded;
31732
32126
  }
31733
32127
  function realLocation(path) {
@@ -31735,12 +32129,12 @@ function realLocation(path) {
31735
32129
  let current = path;
31736
32130
  for (;; ) {
31737
32131
  try {
31738
- return missing.length > 0 ? join18(realpathSync7(current), ...missing) : realpathSync7(current);
32132
+ return missing.length > 0 ? join19(realpathSync8(current), ...missing) : realpathSync8(current);
31739
32133
  } catch (error) {
31740
32134
  if (error.code !== "ENOENT")
31741
32135
  throw error;
31742
32136
  }
31743
- const parent = dirname11(current);
32137
+ const parent = dirname12(current);
31744
32138
  if (parent === current)
31745
32139
  return path;
31746
32140
  missing.unshift(basename4(current));
@@ -31755,7 +32149,7 @@ function namesOutsidePath(root, value) {
31755
32149
  const expanded = expandPath(value);
31756
32150
  if (expanded.startsWith("file:"))
31757
32151
  return false;
31758
- const absolute = isAbsolute10(expanded) ? expanded : resolve12(root, expanded);
32152
+ const absolute = isAbsolute10(expanded) ? expanded : resolve14(root, expanded);
31759
32153
  if (relativePathEscapesRoot(relative6(root, absolute)))
31760
32154
  return true;
31761
32155
  try {
@@ -31791,11 +32185,10 @@ function unquote(value) {
31791
32185
  const first = value[0];
31792
32186
  return value.length >= 2 && (first === '"' || first === "'") && value.endsWith(first) ? value.slice(1, -1) : value;
31793
32187
  }
31794
- function hashlinePatchPaths(patch) {
32188
+ function hashlinePatchSections(patch) {
31795
32189
  if (patch.trim().length === 0)
31796
32190
  return;
31797
- const paths = [];
31798
- let sections = 0;
32191
+ const sections = [];
31799
32192
  const rows = patch.split(`
31800
32193
  `);
31801
32194
  for (let index = 0;index < rows.length; index++) {
@@ -31811,28 +32204,31 @@ function hashlinePatchPaths(patch) {
31811
32204
  const hash = inner.indexOf("#");
31812
32205
  if (hash <= 0)
31813
32206
  return;
31814
- paths.push(inner.slice(0, hash));
31815
- sections++;
32207
+ sections.push({ path: inner.slice(0, hash), moves: [] });
31816
32208
  continue;
31817
32209
  }
31818
32210
  const directive = /^(PUT|CUT|REM|MV)(?:\s|$)/.exec(line);
31819
32211
  if (directive) {
31820
- if (sections === 0)
32212
+ const current = sections[sections.length - 1];
32213
+ if (current === undefined)
31821
32214
  return;
31822
32215
  if (directive[1] === "MV") {
31823
32216
  const destination = line.slice(2).trim();
31824
32217
  if (destination.length === 0 || /\s/.test(destination))
31825
32218
  return;
31826
- paths.push(unquote(destination));
32219
+ current.moves.push(unquote(destination));
31827
32220
  }
31828
32221
  continue;
31829
32222
  }
31830
- if (trimmed.length === 0 && sections === 0)
32223
+ if (trimmed.length === 0 && sections.length === 0)
31831
32224
  continue;
31832
- if (sections === 0 || !line.startsWith("+"))
32225
+ if (sections.length === 0 || !line.startsWith("+"))
31833
32226
  return;
31834
32227
  }
31835
- return sections > 0 ? paths : undefined;
32228
+ return sections.length > 0 ? sections : undefined;
32229
+ }
32230
+ function hashlinePatchPaths(patch) {
32231
+ return hashlinePatchSections(patch)?.flatMap((section) => [section.path, ...section.moves]);
31836
32232
  }
31837
32233
  var APPLY_PATCH_TARGETS = [
31838
32234
  "*** Add File:",
@@ -32492,14 +32888,14 @@ function registerStatusCommand(pi, ctx) {
32492
32888
 
32493
32889
  // src/lsp-auto-install.ts
32494
32890
  import { existsSync as existsSync15, mkdirSync as mkdirSync7, renameSync as renameSync10, rmSync as rmSync7, statSync as statSync12, writeFileSync as writeFileSync9 } from "node:fs";
32495
- import { join as join21 } from "node:path";
32891
+ import { join as join22 } from "node:path";
32496
32892
 
32497
32893
  // src/lsp-cache.ts
32498
32894
  import { randomUUID as randomUUID3 } from "node:crypto";
32499
32895
  import {
32500
- closeSync as closeSync10,
32896
+ closeSync as closeSync11,
32501
32897
  mkdirSync as mkdirSync6,
32502
- openSync as openSync9,
32898
+ openSync as openSync10,
32503
32899
  readFileSync as readFileSync12,
32504
32900
  renameSync as renameSync9,
32505
32901
  rmSync as rmSync6,
@@ -32507,7 +32903,7 @@ import {
32507
32903
  unlinkSync as unlinkSync6,
32508
32904
  writeFileSync as writeFileSync8
32509
32905
  } from "node:fs";
32510
- import { join as join19 } from "node:path";
32906
+ import { join as join20 } from "node:path";
32511
32907
 
32512
32908
  // src/atomic-file.ts
32513
32909
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -32530,18 +32926,18 @@ function lspCacheRoot() {
32530
32926
  return getAftLspPackagesDir();
32531
32927
  }
32532
32928
  function lspPackageDir(npmPackage) {
32533
- return join19(lspCacheRoot(), encodeURIComponent(npmPackage));
32929
+ return join20(lspCacheRoot(), encodeURIComponent(npmPackage));
32534
32930
  }
32535
32931
  function lspBinaryPath(npmPackage, binary) {
32536
- return join19(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
32932
+ return join20(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
32537
32933
  }
32538
32934
  function lspBinDir(npmPackage) {
32539
- return join19(lspPackageDir(npmPackage), "node_modules", ".bin");
32935
+ return join20(lspPackageDir(npmPackage), "node_modules", ".bin");
32540
32936
  }
32541
32937
  function isInstalled(npmPackage, binary) {
32542
32938
  for (const candidate of lspBinaryCandidates(binary)) {
32543
32939
  try {
32544
- if (statSync11(join19(lspBinDir(npmPackage), candidate)).isFile())
32940
+ if (statSync11(join20(lspBinDir(npmPackage), candidate)).isFile())
32545
32941
  return true;
32546
32942
  } catch {}
32547
32943
  }
@@ -32561,13 +32957,13 @@ function writeInstalledMetaIn(installDir, version, sha256) {
32561
32957
  installedAt: new Date().toISOString(),
32562
32958
  ...sha256 ? { sha256 } : {}
32563
32959
  };
32564
- writeAtomicFile(join19(installDir, INSTALLED_META_FILE), JSON.stringify(meta));
32960
+ writeAtomicFile(join20(installDir, INSTALLED_META_FILE), JSON.stringify(meta));
32565
32961
  } catch (err) {
32566
32962
  log2(`[lsp-cache] failed to write installed-meta in ${installDir}: ${err}`);
32567
32963
  }
32568
32964
  }
32569
32965
  function readInstalledMetaIn(installDir) {
32570
- const path = join19(installDir, INSTALLED_META_FILE);
32966
+ const path = join20(installDir, INSTALLED_META_FILE);
32571
32967
  try {
32572
32968
  if (!statSync11(path).isFile())
32573
32969
  return null;
@@ -32591,7 +32987,7 @@ function readInstalledMeta(packageKey) {
32591
32987
  return readInstalledMetaIn(lspPackageDir(packageKey));
32592
32988
  }
32593
32989
  function lockPath(npmPackage) {
32594
- return join19(lspPackageDir(npmPackage), ".aft-installing");
32990
+ return join20(lspPackageDir(npmPackage), ".aft-installing");
32595
32991
  }
32596
32992
  var STALE_LOCK_MS2 = 30 * 60 * 1000;
32597
32993
  function acquireInstallLock(lockKey) {
@@ -32600,14 +32996,14 @@ function acquireInstallLock(lockKey) {
32600
32996
  const token = randomUUID3();
32601
32997
  const tryClaim = () => {
32602
32998
  try {
32603
- const fd = openSync9(lock, "wx");
32999
+ const fd = openSync10(lock, "wx");
32604
33000
  try {
32605
33001
  writeFileSync8(fd, `${process.pid}
32606
33002
  ${new Date().toISOString()}
32607
33003
  ${token}
32608
33004
  `);
32609
33005
  } finally {
32610
- closeSync10(fd);
33006
+ closeSync11(fd);
32611
33007
  }
32612
33008
  let retained = false;
32613
33009
  let released = false;
@@ -32722,7 +33118,7 @@ var AUTO_INSTALL_STAMP_FILE = "last-lsp-autoinstall.json";
32722
33118
  var AUTO_INSTALL_WINDOW_MS = 60 * 1000;
32723
33119
  var AUTO_INSTALL_LOCK_STALE_MS = 30 * 60 * 1000;
32724
33120
  function autoInstallStampPath() {
32725
- return join19(aftCacheBase(), AUTO_INSTALL_STAMP_FILE);
33121
+ return join20(aftCacheBase(), AUTO_INSTALL_STAMP_FILE);
32726
33122
  }
32727
33123
  function hasRecentAutoInstallStamp(path) {
32728
33124
  try {
@@ -32770,11 +33166,11 @@ function claimLspAutoInstallPass() {
32770
33166
  };
32771
33167
  const tryClaim = () => {
32772
33168
  try {
32773
- const fd = openSync9(lock, "wx");
33169
+ const fd = openSync10(lock, "wx");
32774
33170
  try {
32775
33171
  writeFileSync8(fd, JSON.stringify(owner));
32776
33172
  } finally {
32777
- closeSync10(fd);
33173
+ closeSync11(fd);
32778
33174
  }
32779
33175
  return true;
32780
33176
  } catch (err) {
@@ -32823,7 +33219,7 @@ function claimLspAutoInstallPass() {
32823
33219
  }
32824
33220
  var VERSION_CHECK_FILE = ".aft-version-check";
32825
33221
  function readVersionCheck(npmPackage) {
32826
- const file = join19(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
33222
+ const file = join20(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
32827
33223
  try {
32828
33224
  const raw = readFileSync12(file, "utf8");
32829
33225
  const parsed = JSON.parse(raw);
@@ -32840,7 +33236,7 @@ function readVersionCheck(npmPackage) {
32840
33236
  }
32841
33237
  function writeVersionCheck(npmPackage, latest) {
32842
33238
  mkdirSync6(lspPackageDir(npmPackage), { recursive: true });
32843
- const file = join19(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
33239
+ const file = join20(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
32844
33240
  const record = {
32845
33241
  last_checked: new Date().toISOString(),
32846
33242
  latest_eligible: latest
@@ -32957,7 +33353,7 @@ function isVersionSupported(spec, version) {
32957
33353
 
32958
33354
  // src/lsp-project-relevance.ts
32959
33355
  import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync13 } from "node:fs";
32960
- import { join as join20 } from "node:path";
33356
+ import { join as join21 } from "node:path";
32961
33357
  var MAX_WALK_DIRS = 200;
32962
33358
  var MAX_WALK_DEPTH = 4;
32963
33359
  var NOISE_DIRS = new Set([
@@ -32974,7 +33370,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
32974
33370
  if (!rootMarkers)
32975
33371
  return false;
32976
33372
  for (const marker of rootMarkers) {
32977
- if (existsSync14(join20(projectRoot, marker)))
33373
+ if (existsSync14(join21(projectRoot, marker)))
32978
33374
  return true;
32979
33375
  }
32980
33376
  return false;
@@ -32998,7 +33394,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
32998
33394
  }
32999
33395
  function readPackageJson(projectRoot) {
33000
33396
  try {
33001
- const raw = readFileSync13(join20(projectRoot, "package.json"), "utf8");
33397
+ const raw = readFileSync13(join21(projectRoot, "package.json"), "utf8");
33002
33398
  const parsed = JSON.parse(raw);
33003
33399
  if (typeof parsed !== "object" || parsed === null)
33004
33400
  return null;
@@ -33028,7 +33424,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
33028
33424
  for (const entry of entries) {
33029
33425
  if (entry.isDirectory()) {
33030
33426
  if (current.depth < MAX_WALK_DEPTH && !NOISE_DIRS.has(entry.name.toLowerCase())) {
33031
- queue.push({ dir: join20(current.dir, entry.name), depth: current.depth + 1 });
33427
+ queue.push({ dir: join21(current.dir, entry.name), depth: current.depth + 1 });
33032
33428
  }
33033
33429
  continue;
33034
33430
  }
@@ -33161,7 +33557,7 @@ async function resolveTargetVersion(spec, config, fetchImpl = fetch) {
33161
33557
  }
33162
33558
  function ensureInstallAnchor(cwd) {
33163
33559
  try {
33164
- const stub = join21(cwd, "package.json");
33560
+ const stub = join22(cwd, "package.json");
33165
33561
  if (!existsSync15(stub)) {
33166
33562
  writeFileSync9(stub, `${JSON.stringify({ name: "aft-lsp-cache", version: "0.0.0", private: true })}
33167
33563
  `);
@@ -33178,18 +33574,26 @@ async function runInstall(spec, version, cwd, installLock, before, signal, deadl
33178
33574
  warn2(`[lsp] install ${target} aborted before spawn`);
33179
33575
  return false;
33180
33576
  }
33181
- const npm = resolveNpm();
33182
- if (!npm) {
33577
+ const preferredNpm = resolveNpm();
33578
+ if (!preferredNpm) {
33183
33579
  warn2(`[lsp] npm not found on PATH or known locations; cannot install ${target}`);
33184
33580
  return false;
33185
33581
  }
33582
+ const selected = await selectLspNpm(preferredNpm, undefined, signal);
33583
+ if (signal?.aborted)
33584
+ return false;
33585
+ if (!selected.npm) {
33586
+ warn2(`[lsp] ${selected.refusal}`);
33587
+ return false;
33588
+ }
33589
+ const npm = selected.npm;
33186
33590
  const invocation = await lspNpmInstallInvocation(npm, target, before);
33187
33591
  if (signal?.aborted) {
33188
33592
  warn2(`[lsp] install ${target} aborted during npm capability probe`);
33189
33593
  return false;
33190
33594
  }
33191
33595
  if (!invocation) {
33192
- warn2(`[lsp] npm cannot enforce the publication cutoff; cannot install ${target}`);
33596
+ warn2(`[lsp] ${lspNpmRefusal(npm.command, "probe failed: cannot verify publication-cutoff support")}`);
33193
33597
  return false;
33194
33598
  }
33195
33599
  ensureInstallAnchor(cwd);
@@ -33271,6 +33675,9 @@ async function runInstall(spec, version, cwd, installLock, before, signal, deadl
33271
33675
  log2(`[lsp] installed ${target}`);
33272
33676
  finish(true);
33273
33677
  } else {
33678
+ const rejectedOption = /--(?:no-save|before|ignore-scripts)\b/.exec(stderrBuf)?.[0];
33679
+ if (rejectedOption)
33680
+ warn2(`[lsp] ${lspNpmRefusal(npm.command, `rejected option: ${rejectedOption}`)}`);
33274
33681
  error2(`[lsp] install ${target} exited with code ${code}; last stderr:
33275
33682
  ${stderrBuf.trim()}`);
33276
33683
  finish(false);
@@ -33377,10 +33784,10 @@ function sha256OfFileSync(path) {
33377
33784
  }
33378
33785
  function quarantineCachedNpmInstall(spec, reason) {
33379
33786
  const packageDir = lspPackageDir(spec.npm);
33380
- const dest = join21(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
33787
+ const dest = join22(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
33381
33788
  warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
33382
33789
  try {
33383
- mkdirSync7(join21(dest, ".."), { recursive: true });
33790
+ mkdirSync7(join22(dest, ".."), { recursive: true });
33384
33791
  rmSync7(dest, { recursive: true, force: true });
33385
33792
  renameSync10(packageDir, dest);
33386
33793
  } catch (err) {
@@ -34113,9 +34520,9 @@ import { StringEnum } from "@earendil-works/pi-ai";
34113
34520
  import { Type as Type4 } from "typebox";
34114
34521
 
34115
34522
  // src/tools/hoisted.ts
34116
- import { stat as stat3 } from "node:fs/promises";
34523
+ import { stat as stat4 } from "node:fs/promises";
34117
34524
  import { homedir as homedir16 } from "node:os";
34118
- import { isAbsolute as isAbsolute11, relative as relative7, resolve as resolve13, sep as sep3 } from "node:path";
34525
+ import { isAbsolute as isAbsolute11, relative as relative7, resolve as resolve15, sep as sep3 } from "node:path";
34119
34526
  import {
34120
34527
  renderDiff as renderDiff2
34121
34528
  } from "@earendil-works/pi-coding-agent";
@@ -34303,17 +34710,17 @@ function normalizePathInput(path) {
34303
34710
  if (decoded === "~")
34304
34711
  return homedir16();
34305
34712
  if (decoded.startsWith(`~${sep3}`) || decoded.startsWith("~/")) {
34306
- return resolve13(homedir16(), decoded.slice(2));
34713
+ return resolve15(homedir16(), decoded.slice(2));
34307
34714
  }
34308
34715
  return decoded;
34309
34716
  }
34310
34717
  function absoluteSearchPath(cwd, target) {
34311
34718
  const expanded = normalizePathInput(target);
34312
- return isAbsolute11(expanded) ? expanded : resolve13(cwd, expanded);
34719
+ return isAbsolute11(expanded) ? expanded : resolve15(cwd, expanded);
34313
34720
  }
34314
34721
  async function searchPathExists(cwd, target) {
34315
34722
  try {
34316
- await stat3(absoluteSearchPath(cwd, target));
34723
+ await stat4(absoluteSearchPath(cwd, target));
34317
34724
  return true;
34318
34725
  } catch {
34319
34726
  return false;
@@ -34365,7 +34772,7 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
34365
34772
  if (!target)
34366
34773
  return;
34367
34774
  const expanded = normalizePathInput(target);
34368
- const absoluteTarget = isAbsolute11(expanded) ? expanded : resolve13(extCtx.cwd, expanded);
34775
+ const absoluteTarget = isAbsolute11(expanded) ? expanded : resolve15(extCtx.cwd, expanded);
34369
34776
  if (containsPath(extCtx.cwd, absoluteTarget))
34370
34777
  return;
34371
34778
  if (options.restrictToProjectRoot === false)
@@ -34374,7 +34781,7 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
34374
34781
  }
34375
34782
  async function assertExternalReadPermission(ctx, extCtx, target, restrictToProjectRoot = ctx.config.restrict_to_project_root ?? false) {
34376
34783
  const expanded = normalizePathInput(target);
34377
- const absoluteTarget = isAbsolute11(expanded) ? expanded : resolve13(extCtx.cwd, expanded);
34784
+ const absoluteTarget = isAbsolute11(expanded) ? expanded : resolve15(extCtx.cwd, expanded);
34378
34785
  if (!restrictToProjectRoot || containsPath(extCtx.cwd, absoluteTarget))
34379
34786
  return;
34380
34787
  try {
@@ -34707,7 +35114,13 @@ PDFs aren't supported on the Pi harness yet.`, response);
34707
35114
  const response = await callToolCallForSession(bridge, "edit", rawArgs, sessionId, extCtx);
34708
35115
  if (response.success === false)
34709
35116
  throw toolErrorFromResponse("edit", response);
34710
- return buildMutationResult(response);
35117
+ return buildHashlineMutationResult(response);
35118
+ },
35119
+ renderCall(args, theme, context) {
35120
+ return renderHashlineEditCall(args, theme, context);
35121
+ },
35122
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
35123
+ return renderMutationResult(result, theme, context, options);
34711
35124
  }
34712
35125
  });
34713
35126
  }
@@ -34883,6 +35296,71 @@ ${formatDiagnosticsText(diagnostics)}`;
34883
35296
  }
34884
35297
  };
34885
35298
  }
35299
+ function buildHashlineMutationResult(response) {
35300
+ const result = buildMutationResult(response);
35301
+ const rows = asRecords2(asRecord3(response.metadata)?.files);
35302
+ const files = rows.map(hashlineFileDetails);
35303
+ if (files.length === 0)
35304
+ return result;
35305
+ const changed = files.filter((file) => file.diff !== undefined);
35306
+ const diff = changed.length === 0 ? undefined : files.length === 1 ? files[0]?.diff : files.map((file) => file.diff === undefined ? mutationFileHeader(file) : `${mutationFileHeader(file)}
35307
+ ${file.diff}`).join(`
35308
+ `);
35309
+ const firstChangedLine = rows.map((row) => firstChangedLineOfPatch(asString2(row.patch) ?? "")).find((line) => line !== undefined);
35310
+ return {
35311
+ ...result,
35312
+ details: {
35313
+ ...result.details,
35314
+ diff,
35315
+ firstChangedLine,
35316
+ additions: files.reduce((sum, file) => sum + file.additions, 0),
35317
+ deletions: files.reduce((sum, file) => sum + file.deletions, 0),
35318
+ files
35319
+ }
35320
+ };
35321
+ }
35322
+ function hashlineFileDetails(row) {
35323
+ const patch = asString2(row.patch) ?? "";
35324
+ const diff = formatUnifiedDiffForPi(patch);
35325
+ return {
35326
+ path: asString2(row.relativePath) ?? "",
35327
+ sourcePath: asString2(row.sourcePath) ?? asString2(row.movePath),
35328
+ diff: diff.length > 0 ? diff : undefined,
35329
+ additions: asNumber2(row.additions) ?? 0,
35330
+ deletions: asNumber2(row.deletions) ?? 0
35331
+ };
35332
+ }
35333
+ function firstChangedLineOfPatch(patch) {
35334
+ let newLine;
35335
+ for (const line of patch.split(`
35336
+ `)) {
35337
+ const hunk = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
35338
+ if (hunk) {
35339
+ newLine = Number(hunk[1]);
35340
+ continue;
35341
+ }
35342
+ if (newLine === undefined)
35343
+ continue;
35344
+ if (line.startsWith("+") || line.startsWith("-"))
35345
+ return Math.max(1, newLine);
35346
+ if (line.startsWith(" "))
35347
+ newLine++;
35348
+ }
35349
+ return;
35350
+ }
35351
+ function mutationFileName(file) {
35352
+ return file.sourcePath ? `${shortenHomePath(file.sourcePath)} → ${shortenHomePath(file.path)}` : shortenHomePath(file.path);
35353
+ }
35354
+ function mutationFileHeader(file) {
35355
+ return `${mutationFileName(file)} (+${file.additions}/-${file.deletions})`;
35356
+ }
35357
+ function mutationFilesLabel(files) {
35358
+ const first = files?.[0];
35359
+ if (!first || !files)
35360
+ return;
35361
+ const name = mutationFileName(first);
35362
+ return files.length > 1 ? `${name} and ${files.length - 1} more` : name;
35363
+ }
34886
35364
  function formatGlobSkipReasonsNote(reasons) {
34887
35365
  if (!Array.isArray(reasons))
34888
35366
  return;
@@ -34934,6 +35412,29 @@ function renderMutationCall(toolName, filePath, theme, context) {
34934
35412
  text.setText(`${theme.fg("toolTitle", theme.bold(toolName))} ${pathDisplay}`);
34935
35413
  return text;
34936
35414
  }
35415
+ function renderHashlineEditCall(args, theme, context) {
35416
+ const text = reuseText2(context.lastComponent);
35417
+ const target = hashlinePatchTarget(asString2(asRecord3(args)?.patch));
35418
+ const targetDisplay = target ? `${theme.fg("accent", target.name)}${target.more > 0 ? theme.fg("muted", ` and ${target.more} more`) : ""}` : theme.fg("toolOutput", "...");
35419
+ text.setText(`${theme.fg("toolTitle", theme.bold("edit"))} ${targetDisplay}`);
35420
+ return text;
35421
+ }
35422
+ function hashlinePatchTarget(patch) {
35423
+ if (!patch)
35424
+ return;
35425
+ const sections = hashlinePatchSections(patch) ?? hashlinePatchSections(patch.slice(0, patch.lastIndexOf(`
35426
+ `) + 1));
35427
+ const first = sections?.[0];
35428
+ if (!sections || !first)
35429
+ return;
35430
+ const destination = first.moves[first.moves.length - 1];
35431
+ const name = destination ? `${shortenHomePath(first.path)} → ${shortenHomePath(destination)}` : shortenHomePath(first.path);
35432
+ const others = new Set(sections.flatMap((section) => [section.path, ...section.moves]));
35433
+ others.delete(first.path);
35434
+ if (destination)
35435
+ others.delete(destination);
35436
+ return { name, more: others.size };
35437
+ }
34937
35438
  function renderMutationResult(result, theme, context, options = { expanded: true }) {
34938
35439
  if (context.isError) {
34939
35440
  const errorText = result.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join(`
@@ -34969,15 +35470,21 @@ ${summary}${suffix}`);
34969
35470
  const container = reuseContainer2(context.lastComponent);
34970
35471
  container.clear();
34971
35472
  container.addChild(new Spacer2(1));
34972
- let renderedDiff;
34973
- try {
34974
- renderedDiff = renderDiff2(diff);
34975
- } catch {
34976
- renderedDiff = diff;
35473
+ const files = details?.files ?? [];
35474
+ if (files.length > 1) {
35475
+ for (const file of files) {
35476
+ const counts = theme.fg("muted", ` (+${file.additions}/-${file.deletions})`);
35477
+ container.addChild(new Text3(`${theme.fg("accent", mutationFileName(file))}${counts}`, 1, 0));
35478
+ if (file.diff !== undefined) {
35479
+ container.addChild(new Text3(renderTerminalDiff(file.diff), 1, 0));
35480
+ }
35481
+ }
35482
+ } else {
35483
+ container.addChild(new Text3(renderTerminalDiff(diff), 1, 0));
34977
35484
  }
34978
- container.addChild(new Text3(normalizeTerminalText(renderedDiff), 1, 0));
34979
35485
  const rawPath = context.args && typeof context.args === "object" && typeof context.args.path === "string" ? context.args.path : undefined;
34980
- const summaryPath = rawPath ? ` ${shortenHomePath(rawPath)}` : "";
35486
+ const summaryLabel = rawPath ? shortenHomePath(rawPath) : mutationFilesLabel(details?.files);
35487
+ const summaryPath = summaryLabel ? ` ${summaryLabel}` : "";
34981
35488
  const summary = `edited${summaryPath} (+${details?.additions ?? 0}/-${details?.deletions ?? 0})`;
34982
35489
  return collapsibleResult({
34983
35490
  summary,
@@ -34986,11 +35493,20 @@ ${summary}${suffix}`);
34986
35493
  context
34987
35494
  });
34988
35495
  }
35496
+ function renderTerminalDiff(diff) {
35497
+ let renderedDiff;
35498
+ try {
35499
+ renderedDiff = renderDiff2(diff);
35500
+ } catch {
35501
+ renderedDiff = diff;
35502
+ }
35503
+ return normalizeTerminalText(renderedDiff);
35504
+ }
34989
35505
  async function resolvePathArg(cwd, path) {
34990
35506
  const expanded = normalizePathInput(path);
34991
35507
  const abs = absoluteSearchPath(cwd, path);
34992
35508
  try {
34993
- await stat3(abs);
35509
+ await stat4(abs);
34994
35510
  return abs;
34995
35511
  } catch {
34996
35512
  return expanded;
@@ -35565,7 +36081,7 @@ function getBashSpawnHook(pi) {
35565
36081
  function backgroundWaitSentence(c) {
35566
36082
  if (c.watch) {
35567
36083
  const noPolling = c.status ? ", and never loop `bash_status` to wait" : "";
35568
- return `then \`bash_watch\` handles only a short remaining wait (in a main session a watch defaults to 30s, max bash.watch_sync_max_ms, 120s by default; in a delegated session a watch without a timeout waits up to the worker wait limit, bash.worker_wait_max_ms, 30 minutes by default, then reports it is still running; watch again to keep waiting); for anything longer end the turn and let the completion reminder wake you, or use bash({wait:true}) when the result is needed before anything else — never background a command and immediately \`bash_watch\` it (that wastes a turn for what foreground returns in one)${noPolling}.`;
36084
+ return `then \`bash_watch\` handles only a short remaining wait (in a main session a watch defaults to 30s, max bash.watch_sync_max_ms, 120s by default; in a delegated session a watch without a timeout waits up to the worker wait limit, bash.worker_wait_max_ms, 30 minutes by default, then reports it is still running; watch again to keep waiting); for anything longer, first do the work that doesn't need this result, end the turn only when everything left needs it, and let the completion reminder wake you, or use bash({wait:true}) when the result is needed before anything else — never background a command and immediately \`bash_watch\` it (that wastes a turn for what foreground returns in one)${noPolling}.`;
35569
36085
  }
35570
36086
  return c.status ? "the task keeps running after the call returns, a completion reminder arrives when it exits, and `bash_status` reports its state and output. Use bash({wait:true}) instead when the result is needed before anything else." : "the task keeps running after the call returns and a completion reminder arrives when it exits. Use bash({wait:true}) instead when the result is needed before anything else.";
35571
36087
  }
@@ -35602,7 +36118,7 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false, registeredName =
35602
36118
  "PTY mode"
35603
36119
  ].join(", ") : bashCfg.compress ? "workdir and compressed output" : "workdir";
35604
36120
  const detachSentence = bashCfg.detach_on_user_message ? "Any new message detaches this wait. Set `bash.detach_on_user_message: false` to keep it blocking; even then, a message containing the literal `&detach` forces detachment, and the token is stripped before delivery; the rest of the message is preserved, while a token-only message becomes `(requested background detach)`." : "Because `bash.detach_on_user_message` is false, a new message leaves this wait blocking; include the literal `&detach` anywhere to force detachment, and the token is stripped before delivery; the rest of the message is preserved, while a token-only message becomes `(requested background detach)`.";
35605
- const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting (it blocks up to \`bash.worker_wait_max_ms\`, 30 minutes by default, then reports the command is still running; watch again to keep waiting); ${detachSentence} Use it when you need the result before doing anything else; keep it off otherwise so auto-promote can remind you while you work. Use \`background: true\` yourself ONLY when you have other useful work to do while it runs; ${backgroundWaitSentence(companions)} Do not change or delete files a background command uses until it finishes. A \`nohup … &\` launch still holds the call if the child keeps stdout/stderr; redirect both or use background:true. \`pty: true\` runs interactive programs (REPLs, TUIs), implies background${ptyDriveClause(companions)}.` : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
36121
+ const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting (it blocks up to \`bash.worker_wait_max_ms\`, 30 minutes by default, then reports the command is still running; watch again to keep waiting); ${detachSentence} Use it when you need the result before doing anything else; keep it off otherwise so auto-promote can remind you while you work. Use \`background: true\` yourself ONLY when you have other work to do while it runs, and any work that doesn't need this command's result counts; ${backgroundWaitSentence(companions)} Do not change or delete files a background command uses until it finishes. A \`nohup … &\` launch still holds the call if the child keeps stdout/stderr; redirect both or use background:true. \`pty: true\` runs interactive programs (REPLs, TUIs), implies background${ptyDriveClause(companions)}.` : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
35606
36122
  const remoteRuns = () => !isPowerShell && remoteRunsOffered(ctx.config);
35607
36123
  pi.registerTool({
35608
36124
  name: registeredName,
@@ -35833,7 +36349,7 @@ function createBashWatchTool(ctx) {
35833
36349
  return {
35834
36350
  name: "bash_watch",
35835
36351
  label: "bash_watch",
35836
- description: `Watch a background bash task. ${WATCH_SYNC_DEFAULTS_DESCRIPTION}. In a main session sync waits are for a short remaining wait on a task; for anything longer end the turn on \`bash({background:true})\` and let the completion reminder wake you, or use \`bash({wait:true})\` when the result is needed before anything else. The user can interrupt anytime; the wait auto-converts to an async notification. Async (background:true, requires pattern) registers a non-blocking notification and returns immediately — use when you have parallel work or want to end your turn.${bashCompanionRegistered(ctx.config, "bash_status") ? " Never loop bash_status to wait." : ""}`,
36352
+ description: `Watch a background bash task. ${WATCH_SYNC_DEFAULTS_DESCRIPTION}. In a main session sync waits are for a short remaining wait on a task; for anything longer, first do the work that doesn't need the result, end the turn only when everything left needs it, and let the completion reminder wake you, or use \`bash({wait:true})\` when the result is needed before anything else. The user can interrupt anytime; the wait auto-converts to an async notification. Async (background:true, requires pattern) registers a non-blocking notification and returns immediately — use when you have parallel work or want to end your turn.${bashCompanionRegistered(ctx.config, "bash_status") ? " Never loop bash_status to wait." : ""}`,
35837
36353
  promptSnippet: "Wait for or watch a background bash task",
35838
36354
  parameters: BashWatchParams,
35839
36355
  async execute(_toolCallId, params, signal, _onUpdate, extCtx) {
@@ -37244,7 +37760,7 @@ function registerNavigateTool(pi, ctx) {
37244
37760
  const navigateTool = withPathAliasPreparation({
37245
37761
  name: "aft_callgraph",
37246
37762
  label: "callgraph",
37247
- description: `Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. ${zoomEnabled ? "Use aft_zoom with `callgraph:true` for one-level forward calls-out while reading source; use aft_callgraph only for reverse callers or multi-level traces so you do not double-fetch the same relationships." : "Use aft_callgraph call_tree (depth 1) for one-level forward calls-out; use it for reverse callers or multi-level traces."} All ops require both \`path\` and \`symbol\`. Use \`callers\` for call sites (before renaming/signature changes), \`impact\` for blast radius (what breaks if a symbol changes), \`call_tree\` for what a function calls, \`trace_to\` for how execution reaches a symbol from entry points, \`trace_to_symbol\` for the shortest path from one symbol to another (requires \`toSymbol\`; if ambiguous, the error returns candidate files — retry with \`toPath\`), \`trace_data\` to follow a value across assignments/params. Markers: ~ = edge resolved by name only (may point at the wrong same-named symbol); [unresolved] = callee not resolved to a definition, so the location shown is the call site. Unmarked edges are resolved exactly. By default, unresolved external/stdlib leaf calls in call_tree are collapsed into one summary per parent; pass includeUnresolved=true to show every unresolved edge individually. When a list is cut, the reply ends with \`shown N of M <unit> (<reason>) · narrow: <knobs>\`; absence of that line means the list is complete.`,
37763
+ description: `Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. ${zoomEnabled ? "Use aft_zoom with `callgraph:true` for one-level forward calls-out while reading source; use aft_callgraph only for reverse callers or multi-level traces so you do not double-fetch the same relationships." : "Use aft_callgraph call_tree (depth 1) for one-level forward calls-out; use it for reverse callers or multi-level traces."} All ops require both \`path\` and \`symbol\`. Use \`callers\` for call sites (before renaming/signature changes), \`impact\` for blast radius (what breaks if a symbol changes), \`call_tree\` for what a function calls, \`trace_to\` for how execution reaches a symbol (each path runs back to a root labelled main, test, attribute, public API, or no caller found), \`trace_to_symbol\` for the shortest path from one symbol to another (requires \`toSymbol\`; if ambiguous, the error returns candidate files — retry with \`toPath\`), \`trace_data\` to follow a value across assignments/params. Markers: ~ = edge resolved by name only (may point at the wrong same-named symbol); [unresolved] = callee not resolved to a definition, so the location shown is the call site. Unmarked edges are resolved exactly. By default, unresolved external/stdlib leaf calls in call_tree are collapsed into one summary per parent; pass includeUnresolved=true to show every unresolved edge individually. When a list is cut, the reply ends with \`shown N of M <unit> (<reason>) · narrow: <knobs>\`; absence of that line means the list is complete.`,
37248
37764
  parameters: navigateParamsSchema(),
37249
37765
  async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
37250
37766
  if (isEmptyParam(params.path)) {
@@ -37882,7 +38398,7 @@ function registerSafetyTool(pi, ctx) {
37882
38398
 
37883
38399
  // src/tools/semantic.ts
37884
38400
  import { homedir as homedir17 } from "node:os";
37885
- import { isAbsolute as isAbsolute12, resolve as resolve14 } from "node:path";
38401
+ import { isAbsolute as isAbsolute12, resolve as resolve16 } from "node:path";
37886
38402
  import { Type as Type13 } from "typebox";
37887
38403
  var FILESYSTEM_SCAN_STATUS = "answered by a filesystem scan";
37888
38404
  function semanticHonestyNote(response, theme) {
@@ -37905,8 +38421,8 @@ function selectedProjectRoot(cwd, pathArg) {
37905
38421
  const target = pathArg?.trim();
37906
38422
  if (!target)
37907
38423
  return canonicalizeProjectRoot(cwd);
37908
- const expanded = target === "~" ? homedir17() : /^~[/\\]/u.test(target) ? resolve14(homedir17(), target.slice(2)) : target;
37909
- return canonicalizeProjectRoot(isAbsolute12(expanded) ? expanded : resolve14(cwd, expanded));
38424
+ const expanded = target === "~" ? homedir17() : /^~[/\\]/u.test(target) ? resolve16(homedir17(), target.slice(2)) : target;
38425
+ return canonicalizeProjectRoot(isAbsolute12(expanded) ? expanded : resolve16(cwd, expanded));
37910
38426
  }
37911
38427
  function continuityKey(projectRoot, query, includeTests, topK) {
37912
38428
  return JSON.stringify([
@@ -38551,15 +39067,15 @@ function buildWorkflowHints(opts) {
38551
39067
  `));
38552
39068
  }
38553
39069
  if (hasInspect) {
38554
- sections.push("**Codebase health & diagnostics**: AFT does not surface compile/type errors automatically after edits — pull them with `aft_inspect`. Run it after a batch of edits and before you run tests or commit, when starting in unfamiliar code, or before a refactor/review. One call summarizes diagnostics (compile/type errors), TODOs, metrics, dead code, unused exports, and duplicates; pass `sections` for focused drill-down and `scope` to actively pull diagnostics for a specific file or directory. Its diagnostics are a fast checkpoint, not the authority — a clean `tsc` / `cargo check` / `pyright` run is the real gate. Treat stale_categories/pending_categories as stale or incomplete cache state. AFT schedules a Tier-2 refresh after its next idle or inspect-triggered background run; use one later normal aft_inspect after that refresh, not a polling loop.");
38555
- sections.push("**AFT status bar**: tool results may end with a one-line health bar `[AFT E<errors> W<warnings> | D<dead-code> U<unused-exports> C<clone/dup-groups> | T<todos>]` — an IDE-style glance that appears when a count changes. `E`/`W` are live LSP diagnostics for files touched this session (your universal compile-error signal across every language with an LSP). A `~` before `D` means the dead-code/unused/dup counts predate your latest edit — run `aft_inspect` for current numbers and detail. A `?` in place of a number means that count has no value yet (not zero). When `E>0`, you likely just introduced errors; investigate before moving on.");
39070
+ sections.push("**Codebase health & diagnostics**: AFT does not surface compile/type errors automatically after edits — pull them with `aft_inspect`. Run it after a batch of edits and before you run tests or commit, when starting in unfamiliar code, or before a refactor/review. One call summarizes diagnostics (compile/type errors), TODOs, metrics, dead code, unused exports, and duplicates; pass `sections` for focused drill-down and `scope` to actively pull diagnostics for a specific file or directory. Its diagnostics are a fast checkpoint, not the authority — a clean `tsc` / `cargo check` / `pyright` run is the real gate. Dead code, unused exports, duplicates, cycles and complexity are computed only when you call aft_inspect, and the call waits until they are current; nothing refreshes them in the background, so run it again after further edits instead of polling.");
39071
+ sections.push("**AFT status bar**: tool results may end with a one-line health bar `[AFT E<errors> W<warnings> | T<todos>]` — an IDE-style glance that appears when a count changes. `E`/`W` are live LSP diagnostics for files touched this session (your universal compile-error signal across every language with an LSP). Dead code, unused exports and duplicates are not on the bar; run `aft_inspect` for them. A `?` in place of a number means that count has no value yet (not zero). When `E>0`, you likely just introduced errors; investigate before moving on.");
38556
39072
  }
38557
39073
  if (hasNavigate) {
38558
39074
  sections.push([
38559
39075
  "Use `aft_callgraph` for code-relationship questions instead of grep + read chains:",
38560
39076
  "- `callers` — find all call sites before changing a function signature",
38561
39077
  "- `impact` — blast radius (which functions/files will need updates)",
38562
- "- `trace_to` — how execution reaches this code from entry points (routes, exports, main)",
39078
+ "- `trace_to` — how execution reaches this code; each path runs back to a root labelled main, test, attribute, public API, or no caller found",
38563
39079
  "- `trace_to_symbol` — shortest call path from one symbol to another",
38564
39080
  "- `trace_data` — follow a value through assignments and parameters across files"
38565
39081
  ].join(`
@@ -38694,18 +39210,18 @@ function createVersionMismatchHandler(getPool, ensureCompatibleBinary = ensureBi
38694
39210
  }
38695
39211
  var PLUGIN_VERSION = (() => {
38696
39212
  try {
38697
- const req = createRequire2(import.meta.url);
39213
+ const req = createRequire3(import.meta.url);
38698
39214
  return req("../package.json").version;
38699
39215
  } catch {
38700
39216
  return "0.0.0";
38701
39217
  }
38702
39218
  })();
38703
- var ANNOUNCEMENT_VERSION = "0.63.0";
39219
+ var ANNOUNCEMENT_VERSION = "0.64.0";
38704
39220
  var ANNOUNCEMENT_FEATURES = [
38705
- "Cap a shell command's total memory, background and terminal commands included, with `bash.memory_limit_mib`.",
38706
- "Shell results can list or diff the files a command changed: set `bash.changed_files` to `list` or `diff`.",
38707
- "Put rebuildable caches on another disk with `storage.cache_dir`.",
38708
- "Call graphs mark callback references and link constructors, Python edits keep their indentation, and many reported issues are fixed."
39221
+ "A new index store makes AFT much faster: call graphs build 26x faster, edits reach the call graph about 9x faster, dead-code checks run 4x faster, and the index takes 4x less disk and 3x less memory.",
39222
+ "All worktrees of a repository share one index, so a new worktree is ready in under a second. `npx @cortexkit/aft cache prune-legacy` lists the old index folders and can remove them.",
39223
+ "`trace_to` labels each root it reaches: main, test, attribute, public API, or no caller found.",
39224
+ "Dead code, unused exports, duplicates, cycles and complexity run only when `aft_inspect` is called; the status bar shows errors, warnings and TODOs."
38709
39225
  ];
38710
39226
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
38711
39227
  var pendingEagerWarnings = new Map;