@cortexkit/aft-pi 0.39.1 → 0.39.3

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
@@ -12020,6 +12020,11 @@ var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
12020
12020
  function timeoutForCommand(command) {
12021
12021
  return LONG_RUNNING_COMMAND_TIMEOUT_MS[command];
12022
12022
  }
12023
+ var PASSIVE_COMMANDS = new Set(["status"]);
12024
+ var PASSIVE_COMMAND_TIMEOUT_MS = 5000;
12025
+ function isPassiveCommand(command) {
12026
+ return PASSIVE_COMMANDS.has(command);
12027
+ }
12023
12028
 
12024
12029
  // ../aft-bridge/dist/status-bar.js
12025
12030
  var STATUS_BAR_HEARTBEAT_CALLS = 15;
@@ -12092,6 +12097,11 @@ function bashTaskIdFrom(response) {
12092
12097
  function tagStderrLine(line) {
12093
12098
  return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
12094
12099
  }
12100
+ var BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE = "failed to parse processor information from /proc/cpuinfo";
12101
+ function shouldSurfaceStderrLine(line) {
12102
+ const normalized = line.trim();
12103
+ return !(normalized === `Error in cpuinfo: ${BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE}` || normalized === BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE);
12104
+ }
12095
12105
  function compareSemver(a, b) {
12096
12106
  const [aMain, aPre] = a.split("-", 2);
12097
12107
  const [bMain, bPre] = b.split("-", 2);
@@ -12183,6 +12193,7 @@ class BinaryBridge {
12183
12193
  _restartCount = 0;
12184
12194
  _shuttingDown = false;
12185
12195
  timeoutMs;
12196
+ hangThreshold;
12186
12197
  maxRestarts;
12187
12198
  configured = false;
12188
12199
  _configurePromise = null;
@@ -12207,6 +12218,7 @@ class BinaryBridge {
12207
12218
  this.binaryPath = binaryPath;
12208
12219
  this.cwd = cwd;
12209
12220
  this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
12221
+ this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
12210
12222
  this.maxRestarts = options?.maxRestarts ?? 3;
12211
12223
  this.configOverrides = clampSemanticTimeout(configOverrides ?? {}, this.timeoutMs);
12212
12224
  this.minVersion = options?.minVersion;
@@ -12324,7 +12336,9 @@ class BinaryBridge {
12324
12336
  if (requestSessionId && options?.configureWarningClient !== undefined) {
12325
12337
  this.configureWarningClients.set(requestSessionId, options.configureWarningClient);
12326
12338
  }
12327
- const effectiveTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
12339
+ const passive = isPassiveCommand(command);
12340
+ const resolvedTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
12341
+ const effectiveTimeoutMs = passive ? Math.min(resolvedTimeoutMs, PASSIVE_COMMAND_TIMEOUT_MS) : resolvedTimeoutMs;
12328
12342
  const implicitTransportOptions = {
12329
12343
  ...options?.transportTimeoutMs !== undefined || options?.timeoutMs !== undefined ? { transportTimeoutMs: effectiveTimeoutMs } : {},
12330
12344
  markConfiguredOnSuccess: false
@@ -12374,7 +12388,7 @@ class BinaryBridge {
12374
12388
  }
12375
12389
  const line = `${JSON.stringify(request)}
12376
12390
  `;
12377
- const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
12391
+ const keepBridgeOnTimeout = passive || options?.keepBridgeOnTimeout === true;
12378
12392
  let requestSentAt = Date.now();
12379
12393
  const response = await new Promise((resolve2, reject) => {
12380
12394
  const timer = setTimeout(() => {
@@ -12396,7 +12410,7 @@ class BinaryBridge {
12396
12410
  const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
12397
12411
  const consecutiveTimeouts = this.consecutiveRequestTimeouts + 1;
12398
12412
  this.consecutiveRequestTimeouts = consecutiveTimeouts;
12399
- const keepWarm = childActiveSinceRequest || consecutiveTimeouts < BRIDGE_HANG_TIMEOUT_THRESHOLD;
12413
+ const keepWarm = childActiveSinceRequest || consecutiveTimeouts < this.hangThreshold;
12400
12414
  const restartSuffix = keepWarm ? " — bridge kept warm" : " — restarting bridge";
12401
12415
  const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
12402
12416
  if (requestSessionId) {
@@ -12691,7 +12705,7 @@ class BinaryBridge {
12691
12705
  `)) !== -1) {
12692
12706
  const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
12693
12707
  this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
12694
- if (!line)
12708
+ if (!line || !shouldSurfaceStderrLine(line))
12695
12709
  continue;
12696
12710
  const tagged = tagStderrLine(line);
12697
12711
  this.logVia(tagged);
@@ -12701,7 +12715,7 @@ class BinaryBridge {
12701
12715
  flushStderrBuffer() {
12702
12716
  const line = this.stderrBuffer.replace(/\r$/, "");
12703
12717
  this.stderrBuffer = "";
12704
- if (!line)
12718
+ if (!line || !shouldSurfaceStderrLine(line))
12705
12719
  return;
12706
12720
  const tagged = tagStderrLine(line);
12707
12721
  this.logVia(tagged);
@@ -12926,6 +12940,208 @@ class BinaryBridge {
12926
12940
  }
12927
12941
  }
12928
12942
  }
12943
+ // ../aft-bridge/dist/callgraph-format.js
12944
+ import { homedir as homedir3 } from "node:os";
12945
+ var PLAIN_CALLGRAPH_THEME = {
12946
+ fg: (_role, text) => text
12947
+ };
12948
+ function asRecord(value) {
12949
+ if (!value || typeof value !== "object" || Array.isArray(value))
12950
+ return;
12951
+ return value;
12952
+ }
12953
+ function asRecords(value) {
12954
+ return Array.isArray(value) ? value.map(asRecord).filter(Boolean) : [];
12955
+ }
12956
+ function asString(value) {
12957
+ return typeof value === "string" ? value : undefined;
12958
+ }
12959
+ function asNumber(value) {
12960
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
12961
+ }
12962
+ function asBoolean(value) {
12963
+ return typeof value === "boolean" ? value : undefined;
12964
+ }
12965
+ function shortenPath(path2) {
12966
+ const home = homedir3();
12967
+ if (path2.startsWith(home))
12968
+ return `~${path2.slice(home.length)}`;
12969
+ return path2;
12970
+ }
12971
+ function joinNonEmpty(parts, separator = " · ") {
12972
+ return parts.filter((part) => Boolean(part && part.length > 0)).join(separator);
12973
+ }
12974
+ function treeLine(depth, text) {
12975
+ return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
12976
+ }
12977
+ function renderCallTreeNode(node, depth, lines, theme) {
12978
+ const name = asString(node.name) ?? "(unknown)";
12979
+ const file = shortenPath(asString(node.file) ?? "(unknown file)");
12980
+ const line = asNumber(node.line);
12981
+ const unresolved = node.resolved === false ? ` ${theme.fg("warning", "[unresolved]")}` : "";
12982
+ const location = line !== undefined ? `[${file}:${line}]` : `[${file}]`;
12983
+ lines.push(treeLine(depth, `${name} ${location}${unresolved}`));
12984
+ asRecords(node.children).forEach((child) => {
12985
+ renderCallTreeNode(child, depth + 1, lines, theme);
12986
+ });
12987
+ }
12988
+ function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
12989
+ const limited = asBoolean(response[depthField]);
12990
+ const truncated = asNumber(response[truncatedField]) ?? 0;
12991
+ if (!limited && truncated === 0)
12992
+ return "";
12993
+ const detail = truncated > 0 ? `, ${truncated} truncated` : "";
12994
+ return theme.fg("warning", `(depth limited${detail})`);
12995
+ }
12996
+ function renderTracePath(path2, index, lines) {
12997
+ lines.push(`Path ${index + 1}`);
12998
+ asRecords(path2.hops).forEach((hop, hopIndex) => {
12999
+ const symbol = asString(hop.symbol) ?? "(unknown)";
13000
+ const file = shortenPath(asString(hop.file) ?? "(unknown file)");
13001
+ const line = asNumber(hop.line);
13002
+ const entry = hop.is_entry_point === true ? " [entry]" : "";
13003
+ lines.push(treeLine(hopIndex + 1, `${symbol}${entry} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}`));
13004
+ });
13005
+ }
13006
+ function renderCallersGroupLines(group, theme) {
13007
+ const file = shortenPath(asString(group.file) ?? "(unknown file)");
13008
+ const lines = [theme.fg("accent", file)];
13009
+ const callers = asRecords(group.callers);
13010
+ const bySymbol = new Map;
13011
+ for (const caller of callers) {
13012
+ const symbol = asString(caller.symbol) ?? "(unknown)";
13013
+ const line = asNumber(caller.line);
13014
+ const bucket = bySymbol.get(symbol) ?? [];
13015
+ if (line !== undefined)
13016
+ bucket.push(line);
13017
+ bySymbol.set(symbol, bucket);
13018
+ }
13019
+ const symbols = [...bySymbol.keys()].sort((a, b) => a.localeCompare(b));
13020
+ for (const symbol of symbols) {
13021
+ const lineNums = (bySymbol.get(symbol) ?? []).sort((a, b) => a - b);
13022
+ const linePart = lineNums.length > 0 ? lineNums.map(String).join(", ") : "?";
13023
+ lines.push(` ↳ ${symbol}:${linePart}`);
13024
+ }
13025
+ return lines;
13026
+ }
13027
+ function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME) {
13028
+ const record = asRecord(response);
13029
+ if (!record)
13030
+ return [theme.fg("muted", "No navigation result.")];
13031
+ if (op === "call_tree") {
13032
+ const lines = [];
13033
+ renderCallTreeNode(record, 0, lines, theme);
13034
+ const warning = depthWarning(record, theme);
13035
+ if (warning)
13036
+ lines.push(warning);
13037
+ return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
13038
+ }
13039
+ if (op === "callers") {
13040
+ const groups = asRecords(record.callers);
13041
+ const warning = depthWarning(record, theme);
13042
+ const total = asNumber(record.total_callers) ?? 0;
13043
+ const sections2 = [
13044
+ joinNonEmpty([
13045
+ theme.fg("success", `${total} caller${total === 1 ? "" : "s"}`),
13046
+ theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`),
13047
+ warning
13048
+ ])
13049
+ ];
13050
+ groups.forEach((group) => {
13051
+ sections2.push(renderCallersGroupLines(group, theme).join(`
13052
+ `));
13053
+ });
13054
+ return sections2;
13055
+ }
13056
+ if (op === "trace_to_symbol") {
13057
+ const path2 = asRecords(record.path);
13058
+ const complete = asBoolean(record.complete);
13059
+ const reason = asString(record.reason);
13060
+ if (path2.length === 0) {
13061
+ const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
13062
+ return [`${prefix}${reason ? ` (${reason})` : ""}`];
13063
+ }
13064
+ const lines = [theme.fg("success", `${path2.length} hop${path2.length === 1 ? "" : "s"}`)];
13065
+ path2.forEach((hop, index) => {
13066
+ const symbol = asString(hop.symbol) ?? "(unknown)";
13067
+ const file = shortenPath(asString(hop.file) ?? "(unknown file)");
13068
+ const line = asNumber(hop.line);
13069
+ lines.push(treeLine(index + 1, `${symbol} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}`));
13070
+ });
13071
+ return lines;
13072
+ }
13073
+ if (op === "trace_to") {
13074
+ const paths = asRecords(record.paths);
13075
+ const warning = depthWarning(record, theme, "max_depth_reached", "truncated_paths");
13076
+ const totalPaths = asNumber(record.total_paths) ?? paths.length;
13077
+ const entryPoints = asNumber(record.entry_points_found) ?? 0;
13078
+ const sections2 = [
13079
+ joinNonEmpty([
13080
+ theme.fg("success", `${totalPaths} path${totalPaths === 1 ? "" : "s"}`),
13081
+ theme.fg("muted", `${entryPoints} entry point${entryPoints === 1 ? "" : "s"}`),
13082
+ warning
13083
+ ])
13084
+ ];
13085
+ if (paths.length === 0)
13086
+ sections2.push(theme.fg("muted", "No entry paths found."));
13087
+ paths.forEach((path2, index) => {
13088
+ const lines = [];
13089
+ renderTracePath(path2, index, lines);
13090
+ sections2.push(lines.join(`
13091
+ `));
13092
+ });
13093
+ return sections2;
13094
+ }
13095
+ if (op === "impact") {
13096
+ const callers = asRecords(record.callers);
13097
+ const warning = depthWarning(record, theme);
13098
+ const totalAffected = asNumber(record.total_affected) ?? callers.length;
13099
+ const affectedFiles = asNumber(record.affected_files) ?? 0;
13100
+ const sections2 = [
13101
+ joinNonEmpty([
13102
+ theme.fg("warning", `${totalAffected} affected call site${totalAffected === 1 ? "" : "s"}`),
13103
+ theme.fg("muted", `${affectedFiles} file${affectedFiles === 1 ? "" : "s"}`),
13104
+ warning
13105
+ ])
13106
+ ];
13107
+ if (callers.length === 0)
13108
+ sections2.push(theme.fg("muted", "No impacted callers found."));
13109
+ callers.forEach((caller) => {
13110
+ const file = shortenPath(asString(caller.caller_file) ?? "(unknown file)");
13111
+ const symbol = asString(caller.caller_symbol) ?? "(unknown)";
13112
+ const line = asNumber(caller.line) ?? 0;
13113
+ const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
13114
+ const expression = asString(caller.call_expression);
13115
+ const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
13116
+ sections2.push([
13117
+ `${theme.fg("accent", file)}:${line}`,
13118
+ ` ↳ ${symbol}${entry}`,
13119
+ expression ? ` ${theme.fg("muted", expression)}` : undefined,
13120
+ params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
13121
+ ].filter(Boolean).join(`
13122
+ `));
13123
+ });
13124
+ return sections2;
13125
+ }
13126
+ const hops = asRecords(record.hops);
13127
+ const sections = [
13128
+ joinNonEmpty([
13129
+ theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`),
13130
+ asBoolean(record.depth_limited) ? theme.fg("warning", "(depth limited)") : undefined
13131
+ ])
13132
+ ];
13133
+ if (hops.length === 0)
13134
+ sections.push(theme.fg("muted", "No data-flow hops found."));
13135
+ hops.forEach((hop, index) => {
13136
+ const file = shortenPath(asString(hop.file) ?? "(unknown file)");
13137
+ const symbol = asString(hop.symbol) ?? "(unknown)";
13138
+ const variable = asString(hop.variable) ?? "(unknown)";
13139
+ const line = asNumber(hop.line) ?? 0;
13140
+ const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
13141
+ sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol} [${file}:${line}]${approximate}`));
13142
+ });
13143
+ return sections;
13144
+ }
12929
13145
  // ../aft-bridge/dist/coerce.js
12930
13146
  function coerceStringArray(value) {
12931
13147
  if (Array.isArray(value)) {
@@ -12976,7 +13192,7 @@ function isEmptyParam(value) {
12976
13192
  import { spawnSync } from "node:child_process";
12977
13193
  import { createHash, randomUUID } from "node:crypto";
12978
13194
  import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
12979
- import { homedir as homedir3 } from "node:os";
13195
+ import { homedir as homedir4 } from "node:os";
12980
13196
  import { join as join3 } from "node:path";
12981
13197
  import { Readable } from "node:stream";
12982
13198
  import { pipeline } from "node:stream/promises";
@@ -13034,10 +13250,10 @@ function isExpectedCachedBinary(binaryPath, tag) {
13034
13250
  function getCacheDir() {
13035
13251
  if (process.platform === "win32") {
13036
13252
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
13037
- const base2 = localAppData || join3(homedir3(), "AppData", "Local");
13253
+ const base2 = localAppData || join3(homedir4(), "AppData", "Local");
13038
13254
  return join3(base2, "aft", "bin");
13039
13255
  }
13040
- const base = process.env.XDG_CACHE_HOME || join3(homedir3(), ".cache");
13256
+ const base = process.env.XDG_CACHE_HOME || join3(homedir4(), ".cache");
13041
13257
  return join3(base, "aft", "bin");
13042
13258
  }
13043
13259
  function getBinaryName() {
@@ -13317,7 +13533,7 @@ function stripJsoncSymbols(value) {
13317
13533
  // ../aft-bridge/dist/migration.js
13318
13534
  import { spawnSync as spawnSync2 } from "node:child_process";
13319
13535
  import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
13320
- import { homedir as homedir5, tmpdir } from "node:os";
13536
+ import { homedir as homedir6, tmpdir } from "node:os";
13321
13537
  import { dirname as dirname2, join as join6 } from "node:path";
13322
13538
 
13323
13539
  // ../aft-bridge/dist/paths.js
@@ -13374,7 +13590,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
13374
13590
  import { execSync } from "node:child_process";
13375
13591
  import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
13376
13592
  import { createRequire as createRequire2 } from "node:module";
13377
- import { homedir as homedir4 } from "node:os";
13593
+ import { homedir as homedir5 } from "node:os";
13378
13594
  import { join as join5 } from "node:path";
13379
13595
  var ensureBinaryForResolver = ensureBinary;
13380
13596
  function copyToVersionedCache(npmBinaryPath, knownVersion) {
@@ -13416,7 +13632,7 @@ function normalizeBareVersion(version) {
13416
13632
  return version.startsWith("v") ? version.slice(1) : version;
13417
13633
  }
13418
13634
  function homeDirFromEnv(env) {
13419
- return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir4();
13635
+ return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir5();
13420
13636
  }
13421
13637
  function cacheDirFromEnv(env) {
13422
13638
  if (process.platform === "win32") {
@@ -13598,8 +13814,8 @@ function dataHome() {
13598
13814
  }
13599
13815
  function homeDir() {
13600
13816
  if (process.platform === "win32")
13601
- return process.env.USERPROFILE || process.env.HOME || homedir5();
13602
- return process.env.HOME || homedir5();
13817
+ return process.env.USERPROFILE || process.env.HOME || homedir6();
13818
+ return process.env.HOME || homedir6();
13603
13819
  }
13604
13820
  function resolveLegacyStorageRoot(harness) {
13605
13821
  if (harness === "pi")
@@ -13689,13 +13905,13 @@ async function ensureStorageMigrated(opts) {
13689
13905
  }
13690
13906
  // ../aft-bridge/dist/npm-resolver.js
13691
13907
  import { readdirSync, statSync as statSync2 } from "node:fs";
13692
- import { homedir as homedir6 } from "node:os";
13908
+ import { homedir as homedir7 } from "node:os";
13693
13909
  import { delimiter, dirname as dirname3, isAbsolute as isAbsolute2, join as join7 } from "node:path";
13694
13910
  function defaultDeps() {
13695
13911
  return {
13696
13912
  platform: process.platform,
13697
13913
  env: process.env,
13698
- home: homedir6(),
13914
+ home: homedir7(),
13699
13915
  execPath: process.execPath
13700
13916
  };
13701
13917
  }
@@ -15030,13 +15246,13 @@ function tokenizeStage(stage) {
15030
15246
  }
15031
15247
  // ../aft-bridge/dist/pool.js
15032
15248
  import { realpathSync as realpathSync2 } from "node:fs";
15033
- import { homedir as homedir7 } from "node:os";
15249
+ import { homedir as homedir8 } from "node:os";
15034
15250
  var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
15035
15251
  var DEFAULT_MAX_POOL_SIZE = 8;
15036
15252
  var CLEANUP_INTERVAL_MS = 60 * 1000;
15037
15253
  function canonicalHomeDir() {
15038
15254
  try {
15039
- const home = homedir7();
15255
+ const home = homedir8();
15040
15256
  if (!home)
15041
15257
  return null;
15042
15258
  try {
@@ -15074,6 +15290,7 @@ class BridgePool {
15074
15290
  this.projectConfigLoader = options.projectConfigLoader;
15075
15291
  this.bridgeOptions = {
15076
15292
  timeoutMs: options.timeoutMs,
15293
+ hangThreshold: options.hangThreshold,
15077
15294
  maxRestarts: options.maxRestarts,
15078
15295
  minVersion: options.minVersion,
15079
15296
  onVersionMismatch: options.onVersionMismatch,
@@ -16058,7 +16275,7 @@ import {
16058
16275
  // package.json
16059
16276
  var package_default = {
16060
16277
  name: "@cortexkit/aft-pi",
16061
- version: "0.39.1",
16278
+ version: "0.39.3",
16062
16279
  type: "module",
16063
16280
  description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
16064
16281
  main: "dist/index.js",
@@ -16081,7 +16298,7 @@ var package_default = {
16081
16298
  "test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
16082
16299
  },
16083
16300
  dependencies: {
16084
- "@cortexkit/aft-bridge": "0.39.1",
16301
+ "@cortexkit/aft-bridge": "0.39.3",
16085
16302
  "@xterm/headless": "^5.5.0",
16086
16303
  "comment-json": "^5.0.0",
16087
16304
  diff: "^8.0.4",
@@ -16089,12 +16306,12 @@ var package_default = {
16089
16306
  zod: "^4.1.8"
16090
16307
  },
16091
16308
  optionalDependencies: {
16092
- "@cortexkit/aft-darwin-arm64": "0.39.1",
16093
- "@cortexkit/aft-darwin-x64": "0.39.1",
16094
- "@cortexkit/aft-linux-arm64": "0.39.1",
16095
- "@cortexkit/aft-linux-x64": "0.39.1",
16096
- "@cortexkit/aft-win32-arm64": "0.39.1",
16097
- "@cortexkit/aft-win32-x64": "0.39.1"
16309
+ "@cortexkit/aft-darwin-arm64": "0.39.3",
16310
+ "@cortexkit/aft-darwin-x64": "0.39.3",
16311
+ "@cortexkit/aft-linux-arm64": "0.39.3",
16312
+ "@cortexkit/aft-linux-x64": "0.39.3",
16313
+ "@cortexkit/aft-win32-arm64": "0.39.3",
16314
+ "@cortexkit/aft-win32-x64": "0.39.3"
16098
16315
  },
16099
16316
  devDependencies: {
16100
16317
  "@earendil-works/pi-coding-agent": "*",
@@ -16121,7 +16338,7 @@ var package_default = {
16121
16338
  };
16122
16339
 
16123
16340
  // src/shared/status.ts
16124
- function asRecord(value) {
16341
+ function asRecord2(value) {
16125
16342
  return typeof value === "object" && value !== null ? value : {};
16126
16343
  }
16127
16344
  function readString(value, fallback = "") {
@@ -16140,7 +16357,7 @@ function readOptionalNumber(value) {
16140
16357
  return typeof value === "number" && Number.isFinite(value) ? value : null;
16141
16358
  }
16142
16359
  function readCompressionAggregate(value) {
16143
- const aggregate = asRecord(value);
16360
+ const aggregate = asRecord2(value);
16144
16361
  return {
16145
16362
  events: readNumber(aggregate.events),
16146
16363
  original_tokens: readNumber(aggregate.original_tokens),
@@ -16151,7 +16368,7 @@ function readCompressionAggregate(value) {
16151
16368
  function readCompression(value) {
16152
16369
  if (typeof value !== "object" || value === null)
16153
16370
  return;
16154
- const compression = asRecord(value);
16371
+ const compression = asRecord2(value);
16155
16372
  return {
16156
16373
  project: readCompressionAggregate(compression.project),
16157
16374
  session: readCompressionAggregate(compression.session)
@@ -16160,7 +16377,7 @@ function readCompression(value) {
16160
16377
  function readStatusBar(value) {
16161
16378
  if (typeof value !== "object" || value === null)
16162
16379
  return;
16163
- const bar = asRecord(value);
16380
+ const bar = asRecord2(value);
16164
16381
  return {
16165
16382
  errors: readNumber(bar.errors),
16166
16383
  warnings: readNumber(bar.warnings),
@@ -16204,16 +16421,16 @@ function formatBytes(bytes) {
16204
16421
  return `${value.toFixed(decimals)} ${units[unitIndex]}`;
16205
16422
  }
16206
16423
  function coerceAftStatus(response) {
16207
- const features = asRecord(response.features);
16208
- const searchIndex = asRecord(response.search_index);
16209
- const semanticIndex = asRecord(response.semantic_index);
16424
+ const features = asRecord2(response.features);
16425
+ const searchIndex = asRecord2(response.search_index);
16426
+ const semanticIndex = asRecord2(response.semantic_index);
16210
16427
  const semanticConfig = {
16211
- ...asRecord(response.semantic),
16212
- ...asRecord(response.semantic_config)
16428
+ ...asRecord2(response.semantic),
16429
+ ...asRecord2(response.semantic_config)
16213
16430
  };
16214
- const disk = asRecord(response.disk);
16215
- const symbolCache = asRecord(response.symbol_cache);
16216
- const session = asRecord(response.session);
16431
+ const disk = asRecord2(response.disk);
16432
+ const symbolCache = asRecord2(response.symbol_cache);
16433
+ const session = asRecord2(response.session);
16217
16434
  return {
16218
16435
  version: readString(response.version, "unknown"),
16219
16436
  project_root: readNullableString(response.project_root),
@@ -16669,7 +16886,7 @@ function registerStatusCommand(pi, ctx) {
16669
16886
 
16670
16887
  // src/config.ts
16671
16888
  import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
16672
- import { homedir as homedir8 } from "node:os";
16889
+ import { homedir as homedir9 } from "node:os";
16673
16890
  import { join as join10 } from "node:path";
16674
16891
  var import_comment_json = __toESM(require_src2(), 1);
16675
16892
 
@@ -30331,6 +30548,10 @@ var BashFeaturesSchema = exports_external.object({
30331
30548
  foreground_wait_window_ms: exports_external.number().int().positive().optional()
30332
30549
  });
30333
30550
  var BashConfigSchema = exports_external.union([exports_external.boolean(), BashFeaturesSchema]);
30551
+ var BridgeConfigSchema = exports_external.object({
30552
+ request_timeout_ms: exports_external.number().int().min(1000, { message: "bridge.request_timeout_ms must be at least 1000" }).optional(),
30553
+ hang_threshold: exports_external.number().int().min(1, { message: "bridge.hang_threshold must be at least 1" }).optional()
30554
+ });
30334
30555
  var InspectConfigSchema = exports_external.object({
30335
30556
  enabled: exports_external.boolean().optional(),
30336
30557
  tier2_idle_minutes: exports_external.number().min(0).optional(),
@@ -30362,13 +30583,16 @@ var AftConfigSchema = exports_external.object({
30362
30583
  restrict_to_project_root: exports_external.boolean().optional(),
30363
30584
  search_index: exports_external.boolean().optional(),
30364
30585
  semantic_search: exports_external.boolean().optional(),
30586
+ callgraph_store: exports_external.boolean().optional(),
30587
+ callgraph_chunk_size: exports_external.number().optional(),
30365
30588
  inspect: InspectConfigSchema.optional(),
30366
30589
  bash: BashConfigSchema.optional(),
30367
30590
  experimental: ExperimentalConfigSchema.optional(),
30368
30591
  lsp: LspConfigSchema.optional(),
30369
30592
  url_fetch_allow_private: exports_external.boolean().optional(),
30370
30593
  semantic: SemanticConfigSchema.optional(),
30371
- max_callgraph_files: exports_external.number().int().positive().optional()
30594
+ max_callgraph_files: exports_external.number().int().positive().optional(),
30595
+ bridge: BridgeConfigSchema.optional()
30372
30596
  }).strict();
30373
30597
  function normalizeLspExtension(extension) {
30374
30598
  return extension.trim().replace(/^\.+/, "");
@@ -30421,6 +30645,37 @@ function resolveLspConfigForConfigure(config2) {
30421
30645
  }
30422
30646
  return overrides;
30423
30647
  }
30648
+ function resolveProjectOverridesForConfigure(config2) {
30649
+ const overrides = {};
30650
+ if (config2.format_on_edit !== undefined)
30651
+ overrides.format_on_edit = config2.format_on_edit;
30652
+ if (config2.formatter_timeout_secs !== undefined)
30653
+ overrides.formatter_timeout_secs = config2.formatter_timeout_secs;
30654
+ if (config2.validate_on_edit !== undefined)
30655
+ overrides.validate_on_edit = config2.validate_on_edit;
30656
+ if (config2.formatter !== undefined)
30657
+ overrides.formatter = config2.formatter;
30658
+ if (config2.checker !== undefined)
30659
+ overrides.checker = config2.checker;
30660
+ overrides.restrict_to_project_root = config2.restrict_to_project_root ?? false;
30661
+ if (config2.search_index !== undefined)
30662
+ overrides.search_index = config2.search_index;
30663
+ if (config2.semantic_search !== undefined)
30664
+ overrides.semantic_search = config2.semantic_search;
30665
+ if (config2.callgraph_store !== undefined)
30666
+ overrides.callgraph_store = config2.callgraph_store;
30667
+ if (config2.callgraph_chunk_size !== undefined)
30668
+ overrides.callgraph_chunk_size = config2.callgraph_chunk_size;
30669
+ Object.assign(overrides, resolveExperimentalConfigForConfigure(config2));
30670
+ Object.assign(overrides, resolveLspConfigForConfigure(config2));
30671
+ if (config2.semantic !== undefined)
30672
+ overrides.semantic = config2.semantic;
30673
+ if (config2.inspect !== undefined)
30674
+ overrides.inspect = config2.inspect;
30675
+ if (config2.max_callgraph_files !== undefined)
30676
+ overrides.max_callgraph_files = config2.max_callgraph_files;
30677
+ return overrides;
30678
+ }
30424
30679
  function resolveExperimentalConfigForConfigure(config2) {
30425
30680
  const overrides = {};
30426
30681
  const bash = resolveBashConfig(config2);
@@ -30597,6 +30852,17 @@ function detectConfigFile(basePath) {
30597
30852
  return { format: "json", path: jsonPath };
30598
30853
  return { format: "none", path: jsonPath };
30599
30854
  }
30855
+ var configLoadErrors = [];
30856
+ function getConfigLoadErrors() {
30857
+ return configLoadErrors;
30858
+ }
30859
+ function formatConfigParseFailureMessage(configPath, errorMessage) {
30860
+ return `AFT config at ${configPath} failed to parse and was ignored (running on defaults): ${errorMessage}. ` + "Fix the syntax or run `npx @cortexkit/aft doctor`.";
30861
+ }
30862
+ function recordConfigParseFailure(configPath, errorMessage) {
30863
+ configLoadErrors.push({ path: configPath, message: errorMessage });
30864
+ warn2(formatConfigParseFailureMessage(configPath, errorMessage));
30865
+ }
30600
30866
  function loadConfigFromPath(configPath) {
30601
30867
  try {
30602
30868
  if (!existsSync6(configPath))
@@ -30604,7 +30870,7 @@ function loadConfigFromPath(configPath) {
30604
30870
  const content = readFileSync4(configPath, "utf-8");
30605
30871
  const rawConfig = import_comment_json.parse(content);
30606
30872
  if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
30607
- warn2(`Config validation error in ${configPath}: root must be an object`);
30873
+ recordConfigParseFailure(configPath, "root must be an object");
30608
30874
  return null;
30609
30875
  }
30610
30876
  migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
@@ -30620,6 +30886,7 @@ function loadConfigFromPath(configPath) {
30620
30886
  } catch (err) {
30621
30887
  const errorMsg = err instanceof Error ? err.message : String(err);
30622
30888
  error2(`Error loading config from ${configPath}: ${errorMsg}`);
30889
+ recordConfigParseFailure(configPath, errorMsg);
30623
30890
  return null;
30624
30891
  }
30625
30892
  }
@@ -30755,6 +31022,8 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
30755
31022
  "configure_warnings_delivery",
30756
31023
  "search_index",
30757
31024
  "semantic_search",
31025
+ "callgraph_store",
31026
+ "callgraph_chunk_size",
30758
31027
  "inspect",
30759
31028
  "experimental",
30760
31029
  "bash"
@@ -30776,6 +31045,8 @@ function getStrippedTopLevelKeys(override) {
30776
31045
  stripped.push("url_fetch_allow_private");
30777
31046
  if (override.max_callgraph_files !== undefined)
30778
31047
  stripped.push("max_callgraph_files");
31048
+ if (override.bridge !== undefined)
31049
+ stripped.push("bridge");
30779
31050
  return stripped;
30780
31051
  }
30781
31052
  function mergeConfigs(base, override) {
@@ -30787,6 +31058,7 @@ function mergeConfigs(base, override) {
30787
31058
  const experimental = mergeExperimentalConfig(base.experimental, override.experimental);
30788
31059
  const bash = mergeBashConfig(base.bash, override.bash);
30789
31060
  const inspect = mergeInspectConfig(base.inspect, override.inspect);
31061
+ const bridge = base.bridge;
30790
31062
  const safeOverride = pickProjectSafeFields(override);
30791
31063
  delete safeOverride.bash;
30792
31064
  delete safeOverride.inspect;
@@ -30800,13 +31072,23 @@ function mergeConfigs(base, override) {
30800
31072
  ...inspect !== undefined ? { inspect } : {},
30801
31073
  experimental,
30802
31074
  semantic,
31075
+ ...bridge !== undefined ? { bridge } : {},
30803
31076
  ...disabledTools.length > 0 ? { disabled_tools: [...new Set(disabledTools)] } : {}
30804
31077
  };
30805
31078
  }
31079
+ var DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS = 30000;
31080
+ var DEFAULT_BRIDGE_HANG_THRESHOLD = 2;
31081
+ function resolveBridgePoolTransportOptions(config2) {
31082
+ return {
31083
+ timeoutMs: config2.bridge?.request_timeout_ms ?? DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS,
31084
+ hangThreshold: config2.bridge?.hang_threshold ?? DEFAULT_BRIDGE_HANG_THRESHOLD
31085
+ };
31086
+ }
30806
31087
  function getGlobalPiDir() {
30807
- return join10(homedir8(), ".pi", "agent");
31088
+ return join10(homedir9(), ".pi", "agent");
30808
31089
  }
30809
31090
  function loadAftConfig(projectDirectory) {
31091
+ configLoadErrors = [];
30810
31092
  const userBasePath = join10(getGlobalPiDir(), "aft");
30811
31093
  migrateAftConfigFile(`${userBasePath}.jsonc`);
30812
31094
  migrateAftConfigFile(`${userBasePath}.json`);
@@ -30861,7 +31143,7 @@ import {
30861
31143
  unlinkSync as unlinkSync5,
30862
31144
  writeFileSync as writeFileSync4
30863
31145
  } from "node:fs";
30864
- import { homedir as homedir9 } from "node:os";
31146
+ import { homedir as homedir10 } from "node:os";
30865
31147
  import { join as join11 } from "node:path";
30866
31148
  function aftCacheBase() {
30867
31149
  const override = process.env.AFT_CACHE_DIR;
@@ -30869,10 +31151,10 @@ function aftCacheBase() {
30869
31151
  return override;
30870
31152
  if (process.platform === "win32") {
30871
31153
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
30872
- const base2 = localAppData || join11(homedir9(), "AppData", "Local");
31154
+ const base2 = localAppData || join11(homedir10(), "AppData", "Local");
30873
31155
  return join11(base2, "aft");
30874
31156
  }
30875
- const base = process.env.XDG_CACHE_HOME || join11(homedir9(), ".cache");
31157
+ const base = process.env.XDG_CACHE_HOME || join11(homedir10(), ".cache");
30876
31158
  return join11(base, "aft");
30877
31159
  }
30878
31160
  function lspCacheRoot() {
@@ -32585,6 +32867,8 @@ function warningTitle(warning) {
32585
32867
  return "Checker is not installed";
32586
32868
  case "lsp_binary_missing":
32587
32869
  return "LSP binary is missing";
32870
+ case "config_parse_failed":
32871
+ return "Config failed to parse";
32588
32872
  }
32589
32873
  }
32590
32874
  function formatConfigureWarning(warning) {
@@ -32830,7 +33114,7 @@ import { Type as Type3 } from "typebox";
32830
33114
 
32831
33115
  // src/tools/hoisted.ts
32832
33116
  import { stat } from "node:fs/promises";
32833
- import { homedir as homedir10 } from "node:os";
33117
+ import { homedir as homedir11 } from "node:os";
32834
33118
  import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4, sep } from "node:path";
32835
33119
  import {
32836
33120
  renderDiff
@@ -32952,12 +33236,66 @@ function expandTilde2(path3) {
32952
33236
  if (!path3 || !path3.startsWith("~"))
32953
33237
  return path3;
32954
33238
  if (path3 === "~")
32955
- return homedir10();
33239
+ return homedir11();
32956
33240
  if (path3.startsWith(`~${sep}`) || path3.startsWith("~/")) {
32957
- return resolve4(homedir10(), path3.slice(2));
33241
+ return resolve4(homedir11(), path3.slice(2));
32958
33242
  }
32959
33243
  return path3;
32960
33244
  }
33245
+ function absoluteSearchPath(cwd, target) {
33246
+ const expanded = expandTilde2(target);
33247
+ return isAbsolute4(expanded) ? expanded : resolve4(cwd, expanded);
33248
+ }
33249
+ async function searchPathExists(cwd, target) {
33250
+ try {
33251
+ await stat(absoluteSearchPath(cwd, target));
33252
+ return true;
33253
+ } catch {
33254
+ return false;
33255
+ }
33256
+ }
33257
+ async function splitSearchPathArg(cwd, raw) {
33258
+ if (await searchPathExists(cwd, raw) || !/\s/.test(raw)) {
33259
+ return { paths: [raw], missing: [] };
33260
+ }
33261
+ const fragments = raw.trim().split(/\s+/).filter(Boolean);
33262
+ if (fragments.length < 2) {
33263
+ return { paths: [raw], missing: [] };
33264
+ }
33265
+ const existing = [];
33266
+ const missing = [];
33267
+ for (const fragment of fragments) {
33268
+ if (await searchPathExists(cwd, fragment)) {
33269
+ existing.push(fragment);
33270
+ } else {
33271
+ missing.push(fragment);
33272
+ }
33273
+ }
33274
+ if (existing.length === 0) {
33275
+ return { paths: [raw], missing: [] };
33276
+ }
33277
+ return { paths: existing, missing };
33278
+ }
33279
+ async function bridgeSearchPathArg(cwd, split) {
33280
+ if (split.paths.length === 1 && split.missing.length === 0) {
33281
+ return await resolvePathArg(cwd, split.paths[0]);
33282
+ }
33283
+ return split.paths.map((target) => absoluteSearchPath(cwd, target)).join(" ");
33284
+ }
33285
+ function formatSkippedSearchPaths(missing) {
33286
+ if (missing.length === 0)
33287
+ return;
33288
+ const noun = missing.length === 1 ? "path" : "paths";
33289
+ return `Skipped ${missing.length} ${noun} not found: ${missing.join(", ")}`;
33290
+ }
33291
+ function appendSkippedSearchPaths(text, missing) {
33292
+ const note = formatSkippedSearchPaths(missing);
33293
+ if (!note)
33294
+ return text;
33295
+ return text.length > 0 ? `${text}
33296
+
33297
+ ${note}` : note;
33298
+ }
32961
33299
  function externalDirectoryPromptTimeoutMs() {
32962
33300
  const raw = process.env.AFT_PI_EXTERNAL_PROMPT_TIMEOUT_MS;
32963
33301
  if (raw === undefined)
@@ -33167,19 +33505,26 @@ function registerHoistedTools(pi, ctx, surface) {
33167
33505
  async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
33168
33506
  const bridge = bridgeFor(ctx, extCtx.cwd);
33169
33507
  const req = { pattern: params.pattern };
33508
+ let pathSplit;
33170
33509
  if (params.path) {
33171
- await assertExternalDirectoryPermission(extCtx, params.path, "search", {
33172
- restrictToProjectRoot: surface.restrictToProjectRoot
33173
- });
33174
- req.path = await resolvePathArg(extCtx.cwd, params.path);
33510
+ pathSplit = await splitSearchPathArg(extCtx.cwd, params.path);
33511
+ for (const target of pathSplit.paths) {
33512
+ await assertExternalDirectoryPermission(extCtx, absoluteSearchPath(extCtx.cwd, target), "search", {
33513
+ restrictToProjectRoot: surface.restrictToProjectRoot
33514
+ });
33515
+ }
33516
+ req.path = await bridgeSearchPathArg(extCtx.cwd, pathSplit);
33175
33517
  }
33176
33518
  if (params.include)
33177
33519
  req.include = splitIncludeGlobs(params.include);
33178
33520
  if (params.caseSensitive !== undefined)
33179
33521
  req.case_sensitive = params.caseSensitive;
33180
33522
  const response = await callBridge(bridge, "grep", req, extCtx);
33181
- const text = response.text ?? "";
33182
- return textResult(text);
33523
+ if (pathSplit && pathSplit.missing.length > 0) {
33524
+ response.complete = false;
33525
+ }
33526
+ const text = appendSkippedSearchPaths(response.text ?? "", pathSplit?.missing ?? []);
33527
+ return textResult(text, response);
33183
33528
  }
33184
33529
  });
33185
33530
  }
@@ -33287,7 +33632,7 @@ function reuseContainer(last) {
33287
33632
  }
33288
33633
  function renderMutationCall(toolName, filePath, theme, context) {
33289
33634
  const text = reuseText(context.lastComponent);
33290
- const pathDisplay = filePath ? theme.fg("accent", shortenPath(filePath)) : theme.fg("toolOutput", "...");
33635
+ const pathDisplay = filePath ? theme.fg("accent", shortenPath2(filePath)) : theme.fg("toolOutput", "...");
33291
33636
  text.setText(`${theme.fg("toolTitle", theme.bold(toolName))} ${pathDisplay}`);
33292
33637
  return text;
33293
33638
  }
@@ -33323,15 +33668,15 @@ ${summary}${suffix}`);
33323
33668
  container.addChild(new Text(renderDiff(diff), 1, 0));
33324
33669
  return container;
33325
33670
  }
33326
- function shortenPath(path3) {
33327
- const home = homedir10();
33671
+ function shortenPath2(path3) {
33672
+ const home = homedir11();
33328
33673
  if (path3.startsWith(home))
33329
33674
  return `~${path3.slice(home.length)}`;
33330
33675
  return path3;
33331
33676
  }
33332
33677
  async function resolvePathArg(cwd, path3) {
33333
33678
  const expanded = expandTilde2(path3);
33334
- const abs = isAbsolute4(expanded) ? expanded : resolve4(cwd, expanded);
33679
+ const abs = absoluteSearchPath(cwd, path3);
33335
33680
  try {
33336
33681
  await stat(abs);
33337
33682
  return abs;
@@ -33374,7 +33719,7 @@ function formatReadFooter2(agentSpecifiedRange, data) {
33374
33719
  }
33375
33720
 
33376
33721
  // src/tools/render-helpers.ts
33377
- import { homedir as homedir11 } from "node:os";
33722
+ import { homedir as homedir12 } from "node:os";
33378
33723
  import { renderDiff as renderDiff2 } from "@earendil-works/pi-coding-agent";
33379
33724
  import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
33380
33725
  function reuseText2(last) {
@@ -33383,8 +33728,8 @@ function reuseText2(last) {
33383
33728
  function reuseContainer2(last) {
33384
33729
  return last instanceof Container2 ? last : new Container2;
33385
33730
  }
33386
- function shortenPath2(path3) {
33387
- const home = homedir11();
33731
+ function shortenPath3(path3) {
33732
+ const home = homedir12();
33388
33733
  if (path3.startsWith(home))
33389
33734
  return `~${path3.slice(home.length)}`;
33390
33735
  return path3;
@@ -33398,7 +33743,7 @@ function renderToolCall(toolName, summary, theme, context) {
33398
33743
  function accentPath(theme, path3) {
33399
33744
  if (!path3)
33400
33745
  return theme.fg("toolOutput", "...");
33401
- return theme.fg("accent", shortenPath2(path3));
33746
+ return theme.fg("accent", shortenPath3(path3));
33402
33747
  }
33403
33748
  function collectTextContent(result) {
33404
33749
  return result.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
@@ -33437,23 +33782,20 @@ function renderSections(sections, context) {
33437
33782
  });
33438
33783
  return container;
33439
33784
  }
33440
- function asRecord2(value) {
33785
+ function asRecord3(value) {
33441
33786
  if (!value || typeof value !== "object" || Array.isArray(value))
33442
33787
  return;
33443
33788
  return value;
33444
33789
  }
33445
- function asRecords(value) {
33446
- return Array.isArray(value) ? value.map(asRecord2).filter(Boolean) : [];
33790
+ function asRecords2(value) {
33791
+ return Array.isArray(value) ? value.map(asRecord3).filter(Boolean) : [];
33447
33792
  }
33448
- function asString(value) {
33793
+ function asString2(value) {
33449
33794
  return typeof value === "string" ? value : undefined;
33450
33795
  }
33451
- function asNumber(value) {
33796
+ function asNumber2(value) {
33452
33797
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
33453
33798
  }
33454
- function asBoolean(value) {
33455
- return typeof value === "boolean" ? value : undefined;
33456
- }
33457
33799
  function formatValue(value) {
33458
33800
  if (Array.isArray(value))
33459
33801
  return value.map(formatValue).join(", ");
@@ -33566,7 +33908,7 @@ var ReplaceParams = Type3.Object({
33566
33908
  dryRun: Type3.Optional(Type3.Boolean({ description: "Preview without applying (default: false)" }))
33567
33909
  });
33568
33910
  function appendHintSection(response, sections, theme) {
33569
- const hint = asString(response.hint);
33911
+ const hint = asString2(response.hint);
33570
33912
  if (hint && hint.length > 0) {
33571
33913
  sections.push(theme.fg("warning", hint));
33572
33914
  }
@@ -33575,8 +33917,8 @@ function appendScopeSections(response, sections, theme) {
33575
33917
  if (response.no_files_matched_scope === true) {
33576
33918
  sections.push(theme.fg("warning", "No files matched the scope (paths/globs resolved to zero files)"));
33577
33919
  }
33578
- const warnings = asRecords(response.scope_warnings);
33579
- const warningStrings = Array.isArray(response.scope_warnings) ? response.scope_warnings.filter((w) => typeof w === "string") : warnings.map((w) => asString(w.warning) ?? "").filter(Boolean);
33920
+ const warnings = asRecords2(response.scope_warnings);
33921
+ const warningStrings = Array.isArray(response.scope_warnings) ? response.scope_warnings.filter((w) => typeof w === "string") : warnings.map((w) => asString2(w.warning) ?? "").filter(Boolean);
33580
33922
  if (warningStrings.length > 0) {
33581
33923
  sections.push(`${theme.fg("muted", "Scope warnings:")}
33582
33924
  ${warningStrings.map((w) => ` ${w}`).join(`
@@ -33600,13 +33942,13 @@ async function assertAstPathsPermission(extCtx, paths, action, restrictToProject
33600
33942
  }
33601
33943
  }
33602
33944
  function buildAstSearchSections(payload, theme) {
33603
- const response = asRecord2(payload);
33945
+ const response = asRecord3(payload);
33604
33946
  if (!response)
33605
33947
  return [theme.fg("muted", "No AST search results.")];
33606
- const matches = asRecords(response.matches);
33607
- const totalMatches = asNumber(response.total_matches) ?? matches.length;
33608
- const filesWithMatches = asNumber(response.files_with_matches) ?? groupByFile(matches, (match) => asString(match.file)).size;
33609
- const filesSearched = asNumber(response.files_searched);
33948
+ const matches = asRecords2(response.matches);
33949
+ const totalMatches = asNumber2(response.total_matches) ?? matches.length;
33950
+ const filesWithMatches = asNumber2(response.files_with_matches) ?? groupByFile(matches, (match) => asString2(match.file)).size;
33951
+ const filesSearched = asNumber2(response.files_searched);
33610
33952
  const header = [
33611
33953
  theme.fg("success", `${totalMatches} match${totalMatches === 1 ? "" : "es"}`),
33612
33954
  theme.fg("accent", `${filesWithMatches} file${filesWithMatches === 1 ? "" : "s"}`),
@@ -33618,26 +33960,26 @@ function buildAstSearchSections(payload, theme) {
33618
33960
  appendHintSection(response, sections2, theme);
33619
33961
  return sections2;
33620
33962
  }
33621
- const grouped = groupByFile(matches, (match) => asString(match.file));
33963
+ const grouped = groupByFile(matches, (match) => asString2(match.file));
33622
33964
  const sections = [header];
33623
33965
  for (const [file2, fileMatches] of grouped.entries()) {
33624
- const lines = [theme.fg("accent", shortenPath2(file2))];
33966
+ const lines = [theme.fg("accent", shortenPath3(file2))];
33625
33967
  fileMatches.forEach((match, index) => {
33626
- const line = asNumber(match.line) ?? 0;
33627
- const column = asNumber(match.column) ?? 0;
33628
- const snippet = asString(match.text)?.trim() || "(empty match)";
33968
+ const line = asNumber2(match.line) ?? 0;
33969
+ const column = asNumber2(match.column) ?? 0;
33970
+ const snippet = asString2(match.text)?.trim() || "(empty match)";
33629
33971
  lines.push(` ${index + 1}. ${theme.fg("muted", `${line}:${column}`)} ${snippet}`);
33630
- const metaVars = asRecord2(match.meta_variables);
33972
+ const metaVars = asRecord3(match.meta_variables);
33631
33973
  if (metaVars && Object.keys(metaVars).length > 0) {
33632
33974
  Object.entries(metaVars).forEach(([name, value]) => {
33633
33975
  lines.push(` ${theme.fg("muted", `${name} =`)} ${formatValue(value)}`);
33634
33976
  });
33635
33977
  }
33636
- const context = asRecords(match.context);
33978
+ const context = asRecords2(match.context);
33637
33979
  context.forEach((ctxLine) => {
33638
- const ctxNumber = asNumber(ctxLine.line) ?? 0;
33980
+ const ctxNumber = asNumber2(ctxLine.line) ?? 0;
33639
33981
  const prefix = ctxLine.is_match === true ? theme.fg("accent", ">") : theme.fg("muted", "|");
33640
- lines.push(` ${prefix} ${ctxNumber}: ${asString(ctxLine.text) ?? ""}`);
33982
+ lines.push(` ${prefix} ${ctxNumber}: ${asString2(ctxLine.text) ?? ""}`);
33641
33983
  });
33642
33984
  });
33643
33985
  sections.push(lines.join(`
@@ -33646,13 +33988,13 @@ function buildAstSearchSections(payload, theme) {
33646
33988
  return sections;
33647
33989
  }
33648
33990
  function buildAstReplaceSections(payload, theme) {
33649
- const response = asRecord2(payload);
33991
+ const response = asRecord3(payload);
33650
33992
  if (!response)
33651
33993
  return [theme.fg("muted", "No AST replace results.")];
33652
- const files = asRecords(response.files);
33653
- const totalReplacements = asNumber(response.total_replacements) ?? 0;
33654
- const totalFiles = asNumber(response.total_files) ?? files.length;
33655
- const filesWithMatches = asNumber(response.files_with_matches);
33994
+ const files = asRecords2(response.files);
33995
+ const totalReplacements = asNumber2(response.total_replacements) ?? 0;
33996
+ const totalFiles = asNumber2(response.total_files) ?? files.length;
33997
+ const filesWithMatches = asNumber2(response.files_with_matches);
33656
33998
  const dryRun = response.dry_run === true;
33657
33999
  const headerParts = [
33658
34000
  dryRun ? theme.fg("warning", "[dry run]") : theme.fg("success", "[applied]"),
@@ -33668,10 +34010,10 @@ function buildAstReplaceSections(payload, theme) {
33668
34010
  return sections;
33669
34011
  }
33670
34012
  files.forEach((fileResult) => {
33671
- const file2 = shortenPath2(asString(fileResult.file) ?? "(unknown file)");
33672
- const replacements = asNumber(fileResult.replacements) ?? 0;
33673
- const error50 = asString(fileResult.error);
33674
- const diff = asString(fileResult.diff);
34013
+ const file2 = shortenPath3(asString2(fileResult.file) ?? "(unknown file)");
34014
+ const replacements = asNumber2(fileResult.replacements) ?? 0;
34015
+ const error50 = asString2(fileResult.error);
34016
+ const diff = asString2(fileResult.diff);
33675
34017
  const lines = [
33676
34018
  `${theme.fg("accent", file2)} ${theme.fg("muted", `(${replacements} replacement${replacements === 1 ? "" : "s"})`)}`
33677
34019
  ];
@@ -33681,7 +34023,7 @@ function buildAstReplaceSections(payload, theme) {
33681
34023
  const rendered = renderUnifiedDiff(diff);
33682
34024
  lines.push(rendered || theme.fg("muted", "No diff available."));
33683
34025
  } else {
33684
- const backupId = asString(fileResult.backup_id);
34026
+ const backupId = asString2(fileResult.backup_id);
33685
34027
  lines.push(backupId ? `${theme.fg("success", "saved")} ${theme.fg("muted", backupId)}` : theme.fg("success", "saved"));
33686
34028
  }
33687
34029
  sections.push(lines.join(`
@@ -33778,7 +34120,7 @@ import { Type as Type4 } from "typebox";
33778
34120
  var FOREGROUND_POLL_INTERVAL_MS = 100;
33779
34121
  var BASH_WAIT_POLL_INTERVAL_MS = 100;
33780
34122
  var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
33781
- var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 300000;
34123
+ var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 30 * 60 * 1000;
33782
34124
  var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
33783
34125
  function resolveForegroundWaitMs(configured) {
33784
34126
  const override = process.env.AFT_TEST_FOREGROUND_WAIT_MS;
@@ -33790,7 +34132,8 @@ function resolveForegroundWaitMs(configured) {
33790
34132
  return configured;
33791
34133
  }
33792
34134
  var BASH_TRANSPORT_TIMEOUT_MS = 30000;
33793
- var BashParams = Type4.Object({
34135
+ var DEFAULT_HARD_TIMEOUT_MS = 30 * 60 * 1000;
34136
+ var BashBaseParams = {
33794
34137
  command: Type4.String({
33795
34138
  description: "Shell command to execute. Supports pipes, redirections, and shell syntax."
33796
34139
  }),
@@ -33800,19 +34143,38 @@ var BashParams = Type4.Object({
33800
34143
  })),
33801
34144
  description: Type4.Optional(Type4.String({
33802
34145
  description: "Human-readable description shown in UI logs. Helps users understand what the command does without reading shell syntax."
33803
- })),
34146
+ }))
34147
+ };
34148
+ var BashBackgroundFlagParam = {
33804
34149
  background: Type4.Optional(Type4.Boolean({
33805
- description: "Spawn command in background and return immediately with a task_id. Use bash_status to poll completion and bash_kill to terminate. Ideal for long-running tasks like builds or dev servers."
33806
- })),
34150
+ description: "Spawn command in background and return immediately with a task_id. Use bash_watch to wait for completion or output patterns; bash_status for a one-shot snapshot only. Use bash_kill to terminate. Ideal for long-running tasks like builds or dev servers."
34151
+ }))
34152
+ };
34153
+ var BashCompressionParam = {
33807
34154
  compressed: Type4.Optional(Type4.Boolean({
33808
34155
  description: "Compress output by removing ANSI codes, carriage returns, and excessive blank lines. Default: true. Set to false for raw terminal output including color codes."
33809
- })),
34156
+ }))
34157
+ };
34158
+ var BashPtyParams = {
33810
34159
  pty: Type4.Optional(Type4.Boolean({
33811
34160
  description: 'Spawn the command in a real PTY for interactive programs. Implies background: true automatically. Inspect with bash_status({ task_id, output_mode: "screen" }) and send input with bash_write.'
33812
34161
  })),
33813
34162
  ptyRows: optionalInt(1, 60),
33814
34163
  ptyCols: optionalInt(1, 140)
34164
+ };
34165
+ var BashParams = Type4.Object({
34166
+ ...BashBaseParams,
34167
+ ...BashBackgroundFlagParam,
34168
+ ...BashCompressionParam,
34169
+ ...BashPtyParams
34170
+ });
34171
+ var BashForegroundOnlyParams = Type4.Object({
34172
+ ...BashBaseParams,
34173
+ ...BashCompressionParam
33815
34174
  });
34175
+ function bashParamsForConfig(backgroundEnabled) {
34176
+ return backgroundEnabled ? BashParams : BashForegroundOnlyParams;
34177
+ }
33816
34178
  var BashTaskParams = Type4.Object({
33817
34179
  task_id: Type4.String({
33818
34180
  description: "Background bash task id returned by bash({ background: true })."
@@ -33886,28 +34248,30 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false) {
33886
34248
  const searchSteer = aftSearchRegistered ? "use `aft_search` (concepts, identifiers, regex, literals), `read`, `aft_outline`, or `aft_zoom` instead" : "use the `grep` tool, `read`, `aft_outline`, or `aft_zoom` instead";
33887
34249
  const bashCfg = resolveBashConfig(ctx.config);
33888
34250
  const compressionSentence = bashCfg.compress ? " Output is compressed by default; pass `compressed: false` for raw output." : "";
33889
- const tasksSentence = bashCfg.background ? ' Pass `background: true` to run in the background and get a task_id for `bash_status`/`bash_kill`. Pass `pty: true` for interactive programs (REPLs, TUIs) and drive them with `bash_status({ output_mode: "screen" })` plus `bash_write`.' : " Commands that outlive the foreground wait window are promoted to background tasks; inspect them with `bash_status({ task_id })` or terminate with `bash_kill`.";
34251
+ const tasksSentence = bashCfg.background ? ' Pass `background: true` to run in the background and get a task_id for `bash_status`/`bash_watch`/`bash_kill`. Pass `pty: true` for interactive programs (REPLs, TUIs) and drive them with `bash_status({ output_mode: "screen" })` plus `bash_write`. Use `bash_watch` to wait for exit or output patterns (sync blocks, async notifies). Do not loop `bash_status` to wait.' : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
33890
34252
  pi.registerTool({
33891
34253
  name: "bash",
33892
34254
  label: "bash",
33893
34255
  description: `Execute shell commands.${compressionSentence}${tasksSentence}
33894
34256
 
33895
34257
  DO NOT use bash for code search or code exploration. If you are about to run grep, rg, sed, awk, find, or cat through bash to locate or read code: STOP — ${searchSteer}.`,
33896
- promptSnippet: "Run shell commands (timeout in milliseconds; supports workdir, background tasks, compressed output, PTY mode)",
34258
+ promptSnippet: bashCfg.background ? "Run shell commands (timeout in milliseconds; supports workdir, background tasks, compressed output, PTY mode)" : "Run shell commands (timeout in milliseconds; supports workdir and compressed output)",
33897
34259
  promptGuidelines: [
33898
34260
  `DO NOT use bash for code search or exploration — ${searchSteer}.`,
33899
34261
  "Set compressed: false when you need ANSI color codes in the output."
33900
34262
  ],
33901
- parameters: BashParams,
34263
+ parameters: bashParamsForConfig(bashCfg.background),
33902
34264
  async execute(_toolCallId, params, _signal, onUpdate, extCtx) {
33903
34265
  const bridge = bridgeFor(ctx, extCtx.cwd);
33904
34266
  const bashCfg2 = resolveBashConfig(ctx.config);
33905
34267
  const foregroundWaitMs = resolveForegroundWaitMs(bashCfg2.foreground_wait_window_ms);
34268
+ const backgroundDisabled = !bashCfg2.background;
33906
34269
  const timeout = coerceOptionalInt(params.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
33907
- const ptyRows = coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
33908
- const ptyCols = coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
33909
- const effectiveBackground = params.background === true || params.pty === true;
33910
- const effectiveTimeout = effectiveBackground ? timeout : resolveBashKillTimeout(timeout, foregroundWaitMs);
34270
+ const ptyRows = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
34271
+ const ptyCols = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
34272
+ const requestedPty = !backgroundDisabled && params.pty === true;
34273
+ const effectiveBackground = !backgroundDisabled && (params.background === true || requestedPty);
34274
+ const effectiveTimeout = effectiveBackground || backgroundDisabled ? timeout : resolveBashKillTimeout(timeout, foregroundWaitMs);
33911
34275
  let spawnContext = {
33912
34276
  command: params.command,
33913
34277
  cwd: params.workdir
@@ -33932,7 +34296,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
33932
34296
  background: effectiveBackground,
33933
34297
  notify_on_completion: effectiveBackground,
33934
34298
  compressed: params.compressed,
33935
- pty: params.pty,
34299
+ pty: requestedPty,
33936
34300
  pty_rows: ptyRows,
33937
34301
  pty_cols: ptyCols
33938
34302
  }, extCtx, {
@@ -33954,9 +34318,9 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
33954
34318
  if (response.status === "running" && taskId) {
33955
34319
  if (effectiveBackground) {
33956
34320
  trackBgTask(resolveSessionId(extCtx), taskId);
33957
- return bashResult(appendPipeStripNote(formatBackgroundLaunch(taskId, params.pty === true), pipeStrip.note), { task_id: taskId });
34321
+ return bashResult(appendPipeStripNote(formatBackgroundLaunch(taskId, requestedPty), pipeStrip.note), { task_id: taskId });
33958
34322
  }
33959
- const waitTimeoutMs = effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
34323
+ const waitTimeoutMs = backgroundDisabled ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
33960
34324
  const startedAt = Date.now();
33961
34325
  while (true) {
33962
34326
  const status = await callBashBridge(bridge, "bash_status", { task_id: taskId }, extCtx);
@@ -33973,6 +34337,10 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
33973
34337
  });
33974
34338
  }
33975
34339
  if (Date.now() - startedAt >= waitTimeoutMs) {
34340
+ if (backgroundDisabled) {
34341
+ await sleep(FOREGROUND_POLL_INTERVAL_MS);
34342
+ continue;
34343
+ }
33976
34344
  const promoted = await callBashBridge(bridge, "bash_promote", { task_id: taskId }, extCtx);
33977
34345
  if (promoted.success === false) {
33978
34346
  throw new Error(promoted.message ?? "bash_promote failed");
@@ -34000,10 +34368,12 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
34000
34368
  return renderBashResult(result, theme, context);
34001
34369
  }
34002
34370
  });
34003
- pi.registerTool(createBashStatusTool(ctx));
34004
- pi.registerTool(createBashWatchTool(ctx));
34005
- pi.registerTool(createBashWriteTool(ctx));
34006
- pi.registerTool(createBashKillTool(ctx));
34371
+ if (bashCfg.background) {
34372
+ pi.registerTool(createBashStatusTool(ctx));
34373
+ pi.registerTool(createBashWatchTool(ctx));
34374
+ pi.registerTool(createBashWriteTool(ctx));
34375
+ pi.registerTool(createBashKillTool(ctx));
34376
+ }
34007
34377
  }
34008
34378
  function formatBackgroundLaunch(taskId, isPty) {
34009
34379
  if (isPty) {
@@ -34022,7 +34392,7 @@ function createBashStatusTool(ctx) {
34022
34392
  return {
34023
34393
  name: "bash_status",
34024
34394
  label: "bash_status",
34025
- description: "Read-only snapshot of a background bash task. Returns immediately. Never waits. Use bash_watch to block on or register for pattern matches and exit events.",
34395
+ description: "Read-only snapshot of a background bash task. Returns immediately. Never waits. One look to check on a task is fine — never loop it to wait for completion. To wait, use bash_watch.",
34026
34396
  promptSnippet: "Inspect a background bash task by task_id",
34027
34397
  parameters: BashStatusParams,
34028
34398
  async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
@@ -34037,7 +34407,7 @@ function createBashWatchTool(ctx) {
34037
34407
  return {
34038
34408
  name: "bash_watch",
34039
34409
  label: "bash_watch",
34040
- description: "Watch a background bash task. Two modes. Async (background:true, requires pattern) registers a non-blocking notification and returns immediately — use this to be pinged when a specific line appears or the task exits, without freezing your turn. Sync (default) blocks until a pattern matches/the task exits/timeout, and is ONLY for short bounded waits (seconds, e.g. a dev server printing a readiness line). Do NOT sync-wait for a long task (build/test/install): blocking locks the user out until it ends instead end your turn and let the automatic completion reminder arrive, or use async mode.",
34410
+ description: "Watch a background bash task. Sync (default) blocks until a pattern matches, the task exits, or timeout — use it when the result is the next thing you need, even for long builds/tests/installs (pass timeout_ms up to 30 min for those). 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. Never loop bash_status to wait.",
34041
34411
  promptSnippet: "Wait for or watch a background bash task",
34042
34412
  parameters: BashWatchParams,
34043
34413
  async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
@@ -34610,10 +34980,10 @@ function renderFsResult(toolName, args, result, theme, context) {
34610
34980
  const skipped = data.skipped_files ?? [];
34611
34981
  const lines = [];
34612
34982
  for (const entry of deletedPaths) {
34613
- lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath2(entry))}`);
34983
+ lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath3(entry))}`);
34614
34984
  }
34615
34985
  for (const entry of skipped) {
34616
- lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent", shortenPath2(entry.file))} ${theme.fg("muted", `(${entry.reason})`)}`);
34986
+ lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent", shortenPath3(entry.file))} ${theme.fg("muted", `(${entry.reason})`)}`);
34617
34987
  }
34618
34988
  if (lines.length === 0) {
34619
34989
  lines.push(theme.fg("muted", "(no files deleted)"));
@@ -34623,8 +34993,8 @@ function renderFsResult(toolName, args, result, theme, context) {
34623
34993
  }
34624
34994
  const moveArgs = args;
34625
34995
  return renderSections([
34626
- `${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath2(moveArgs.filePath))}`,
34627
- `${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath2(moveArgs.destination))}`
34996
+ `${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.filePath))}`,
34997
+ `${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath3(moveArgs.destination))}`
34628
34998
  ], context);
34629
34999
  }
34630
35000
  function registerFsTools(pi, ctx, surface) {
@@ -34738,33 +35108,33 @@ var ImportParams = Type7.Object({
34738
35108
  }))
34739
35109
  });
34740
35110
  function buildImportSections(args, payload, theme) {
34741
- const response = asRecord2(payload);
35111
+ const response = asRecord3(payload);
34742
35112
  if (!response)
34743
35113
  return [theme.fg("muted", "No import result.")];
34744
35114
  if (args.op === "organize") {
34745
- const groups = asRecords(response.groups);
34746
- const groupText = groups.length > 0 ? groups.map((group) => `${asString(group.name) ?? "unknown"}: ${asNumber(group.count) ?? 0}`).join(" · ") : "No imports found";
35115
+ const groups = asRecords2(response.groups);
35116
+ const groupText = groups.length > 0 ? groups.map((group) => `${asString2(group.name) ?? "unknown"}: ${asNumber2(group.count) ?? 0}`).join(" · ") : "No imports found";
34747
35117
  return [
34748
- `${theme.fg("success", "organized")} ${theme.fg("accent", asString(response.file) ?? args.filePath)}`,
35118
+ `${theme.fg("success", "organized")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
34749
35119
  `${theme.fg("muted", "groups")} ${groupText}`,
34750
- `${theme.fg("muted", "duplicates removed")} ${asNumber(response.removed_duplicates) ?? 0}`
35120
+ `${theme.fg("muted", "duplicates removed")} ${asNumber2(response.removed_duplicates) ?? 0}`
34751
35121
  ];
34752
35122
  }
34753
35123
  if (args.op === "add") {
34754
- const moduleName2 = asString(response.module) ?? args.module ?? "(module)";
35124
+ const moduleName2 = asString2(response.module) ?? args.module ?? "(module)";
34755
35125
  const status = response.already_present === true ? theme.fg("warning", "already present") : theme.fg("success", "added");
34756
35126
  return [
34757
35127
  `${status} ${theme.fg("accent", moduleName2)}`,
34758
- `${theme.fg("muted", "file")} ${theme.fg("accent", asString(response.file) ?? args.filePath)}`,
34759
- `${theme.fg("muted", "group")} ${asString(response.group) ?? "—"}`
35128
+ `${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
35129
+ `${theme.fg("muted", "group")} ${asString2(response.group) ?? "—"}`
34760
35130
  ];
34761
35131
  }
34762
- const moduleName = asString(response.module) ?? args.module ?? "(module)";
35132
+ const moduleName = asString2(response.module) ?? args.module ?? "(module)";
34763
35133
  const didRemove = response.removed !== false;
34764
35134
  const removeStatus = didRemove ? `${theme.fg("success", "removed")} ${theme.fg("accent", moduleName)}` : `${theme.fg("warning", "not present")} ${theme.fg("accent", moduleName)}`;
34765
35135
  return [
34766
35136
  removeStatus,
34767
- `${theme.fg("muted", "file")} ${theme.fg("accent", asString(response.file) ?? args.filePath)}`,
35137
+ `${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
34768
35138
  args.removeName ? `${theme.fg("muted", "name")} ${args.removeName}` : `${theme.fg("muted", "scope")} entire import`
34769
35139
  ];
34770
35140
  }
@@ -34897,16 +35267,16 @@ function diagnosticsServerSummary(section) {
34897
35267
  return parts.length > 0 ? parts.join("; ") : "none reported";
34898
35268
  }
34899
35269
  function diagnosticsSummaryPart(summary) {
34900
- const section = asRecord2(summary?.diagnostics);
35270
+ const section = asRecord3(summary?.diagnostics);
34901
35271
  if (!section)
34902
35272
  return;
34903
- const errors3 = asNumber(section.errors);
34904
- const warnings = asNumber(section.warnings);
34905
- const info = asNumber(section.info);
34906
- const hints = asNumber(section.hints);
35273
+ const errors3 = asNumber2(section.errors);
35274
+ const warnings = asNumber2(section.warnings);
35275
+ const info = asNumber2(section.info);
35276
+ const hints = asNumber2(section.hints);
34907
35277
  const hasCounts = [errors3, warnings, info, hints].some((value) => value !== undefined);
34908
35278
  const counts = `${errors3 ?? 0} errors/${warnings ?? 0} warnings/${info ?? 0} info/${hints ?? 0} hints`;
34909
- const status = asString(section.status);
35279
+ const status = asString2(section.status);
34910
35280
  if (status === "pending") {
34911
35281
  return hasCounts ? `diagnostics ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics pending (servers: ${diagnosticsServerSummary(section)})`;
34912
35282
  }
@@ -34919,9 +35289,9 @@ function diagnosticsSummaryPart(summary) {
34919
35289
  return;
34920
35290
  }
34921
35291
  function diagnosticLocation(diagnostic) {
34922
- const file2 = asString(diagnostic.file) ?? "(unknown file)";
34923
- const line = asNumber(diagnostic.line);
34924
- const column = asNumber(diagnostic.column);
35292
+ const file2 = asString2(diagnostic.file) ?? "(unknown file)";
35293
+ const line = asNumber2(diagnostic.line);
35294
+ const column = asNumber2(diagnostic.column);
34925
35295
  if (line === undefined)
34926
35296
  return file2;
34927
35297
  if (column === undefined)
@@ -34929,14 +35299,14 @@ function diagnosticLocation(diagnostic) {
34929
35299
  return `${file2}:${line}:${column}`;
34930
35300
  }
34931
35301
  function diagnosticsDetailSection(details) {
34932
- const diagnostics = asRecords(details?.diagnostics);
35302
+ const diagnostics = asRecords2(details?.diagnostics);
34933
35303
  if (diagnostics.length === 0)
34934
35304
  return;
34935
35305
  const lines = ["diagnostics"];
34936
35306
  for (const diagnostic of diagnostics) {
34937
- const severity = asString(diagnostic.severity) ?? "information";
34938
- const message = asString(diagnostic.message) ?? "(no message)";
34939
- const source = asString(diagnostic.source);
35307
+ const severity = asString2(diagnostic.severity) ?? "information";
35308
+ const message = asString2(diagnostic.message) ?? "(no message)";
35309
+ const source = asString2(diagnostic.source);
34940
35310
  const suffix = source ? ` [${source}]` : "";
34941
35311
  lines.push(`- ${diagnosticLocation(diagnostic)} ${severity} ${message}${suffix}`);
34942
35312
  }
@@ -34944,15 +35314,15 @@ function diagnosticsDetailSection(details) {
34944
35314
  `);
34945
35315
  }
34946
35316
  function countFrom(summary, key) {
34947
- const section = asRecord2(summary?.[key]);
34948
- return asNumber(section?.count);
35317
+ const section = asRecord3(summary?.[key]);
35318
+ return asNumber2(section?.count);
34949
35319
  }
34950
35320
  function tier2SummaryPart(summary, key, label) {
34951
- const section = asRecord2(summary?.[key]);
34952
- const count = asNumber(section?.count);
35321
+ const section = asRecord3(summary?.[key]);
35322
+ const count = asNumber2(section?.count);
34953
35323
  if (count !== undefined)
34954
35324
  return `${label} ${count}`;
34955
- const status = asString(section?.status);
35325
+ const status = asString2(section?.status);
34956
35326
  return `${label} ${status ?? "unavailable"}`;
34957
35327
  }
34958
35328
  function shortDupOccurrence(entry) {
@@ -34961,12 +35331,12 @@ function shortDupOccurrence(entry) {
34961
35331
  }
34962
35332
  function tier2TopPreview(summary, theme) {
34963
35333
  const lines = [];
34964
- const dup = asRecord2(summary?.duplicates);
35334
+ const dup = asRecord3(summary?.duplicates);
34965
35335
  const dupTop = Array.isArray(dup?.top) ? dup.top : [];
34966
35336
  for (const group of dupTop) {
34967
- const record2 = asRecord2(group);
35337
+ const record2 = asRecord3(group);
34968
35338
  const files = Array.isArray(record2?.files) ? record2.files : [];
34969
- const cost = asNumber(record2?.cost);
35339
+ const cost = asNumber2(record2?.cost);
34970
35340
  if (files.length < 2)
34971
35341
  continue;
34972
35342
  const a = shortDupOccurrence(String(files[0]));
@@ -34977,12 +35347,12 @@ function tier2TopPreview(summary, theme) {
34977
35347
  ["dead_code", "dead"],
34978
35348
  ["unused_exports", "unused"]
34979
35349
  ]) {
34980
- const section = asRecord2(summary?.[key]);
35350
+ const section = asRecord3(summary?.[key]);
34981
35351
  const top = Array.isArray(section?.top) ? section.top : [];
34982
35352
  for (const item of top) {
34983
- const record2 = asRecord2(item);
34984
- const file2 = asString(record2?.file);
34985
- const symbol2 = asString(record2?.symbol);
35353
+ const record2 = asRecord3(item);
35354
+ const file2 = asString2(record2?.file);
35355
+ const symbol2 = asString2(record2?.symbol);
34986
35356
  if (!file2 || !symbol2)
34987
35357
  continue;
34988
35358
  lines.push(` ${label} ${symbol2} (${file2.split("/").pop()})`);
@@ -34995,7 +35365,7 @@ ${lines.join(`
34995
35365
  `)}`;
34996
35366
  }
34997
35367
  function tier2RefreshCategories(response) {
34998
- const scannerState = asRecord2(response.scanner_state);
35368
+ const scannerState = asRecord3(response.scanner_state);
34999
35369
  const categories = new Set;
35000
35370
  for (const key of ["pending_categories", "stale_categories"]) {
35001
35371
  const values = scannerState?.[key];
@@ -35042,18 +35412,18 @@ function runPendingTier2Categories(bridge, categories, extCtx) {
35042
35412
  });
35043
35413
  }
35044
35414
  function buildInspectSections(payload, theme) {
35045
- const response = asRecord2(payload);
35415
+ const response = asRecord3(payload);
35046
35416
  if (!response)
35047
35417
  return [theme.fg("muted", "No inspect snapshot available.")];
35048
- const summary = asRecord2(response.summary);
35049
- const metrics = asRecord2(summary?.metrics);
35050
- const scannerState = asRecord2(response.scanner_state);
35418
+ const summary = asRecord3(response.summary);
35419
+ const metrics = asRecord3(summary?.metrics);
35420
+ const scannerState = asRecord3(response.scanner_state);
35051
35421
  const stale = Array.isArray(scannerState?.stale_categories) ? scannerState.stale_categories.length : 0;
35052
35422
  const pending = Array.isArray(scannerState?.pending_categories) ? scannerState.pending_categories.length : 0;
35053
35423
  const parts = [
35054
35424
  `todos ${countFrom(summary, "todos") ?? 0}`,
35055
35425
  diagnosticsSummaryPart(summary),
35056
- `metrics ${asNumber(metrics?.files) ?? 0} files/${asNumber(metrics?.symbols) ?? 0} symbols`,
35426
+ `metrics ${asNumber2(metrics?.files) ?? 0} files/${asNumber2(metrics?.symbols) ?? 0} symbols`,
35057
35427
  tier2SummaryPart(summary, "dead_code", "dead code"),
35058
35428
  tier2SummaryPart(summary, "unused_exports", "unused exports"),
35059
35429
  tier2SummaryPart(summary, "duplicates", "duplicates")
@@ -35065,7 +35435,7 @@ function buildInspectSections(payload, theme) {
35065
35435
  const topPreview = tier2TopPreview(summary, theme);
35066
35436
  if (topPreview)
35067
35437
  sections.push(topPreview);
35068
- const details = asRecord2(response.details);
35438
+ const details = asRecord3(response.details);
35069
35439
  if (details) {
35070
35440
  const names = Object.keys(details);
35071
35441
  sections.push(names.length > 0 ? `details: ${names.join(", ")}` : theme.fg("muted", "No drill-down details returned."));
@@ -35073,7 +35443,7 @@ function buildInspectSections(payload, theme) {
35073
35443
  if (diagnosticsDetails)
35074
35444
  sections.push(diagnosticsDetails);
35075
35445
  }
35076
- const text = asString(response.text);
35446
+ const text = asString2(response.text);
35077
35447
  if (text)
35078
35448
  sections.push(text);
35079
35449
  return sections;
@@ -35106,7 +35476,7 @@ function registerInspectTool(pi, ctx) {
35106
35476
  runPendingTier2Categories(bridge, tier2RefreshCategories(response), extCtx);
35107
35477
  const body = response.text;
35108
35478
  if (typeof body === "string") {
35109
- const diagnostics = diagnosticsSummaryPart(asRecord2(response.summary));
35479
+ const diagnostics = diagnosticsSummaryPart(asRecord3(response.summary));
35110
35480
  const text = diagnostics ? body ? `${body}
35111
35481
 
35112
35482
  ${diagnostics}` : diagnostics : body;
@@ -35146,138 +35516,11 @@ function navigateParamsSchema() {
35146
35516
  }))
35147
35517
  });
35148
35518
  }
35149
- function treeLine(depth, text) {
35150
- return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
35151
- }
35152
- function renderCallTreeNode(node, depth, lines) {
35153
- const name = asString(node.name) ?? "(unknown)";
35154
- const file2 = shortenPath2(asString(node.file) ?? "(unknown file)");
35155
- const line = asNumber(node.line);
35156
- lines.push(treeLine(depth, `${name} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
35157
- asRecords(node.children).forEach((child) => {
35158
- renderCallTreeNode(child, depth + 1, lines);
35159
- });
35160
- }
35161
- function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
35162
- const limited = asBoolean(response[depthField]);
35163
- const truncated = asNumber(response[truncatedField]) ?? 0;
35164
- if (!limited && truncated === 0)
35165
- return "";
35166
- const detail = truncated > 0 ? `, ${truncated} truncated` : "";
35167
- return theme.fg("warning", `(depth limited${detail})`);
35168
- }
35169
- function renderTracePath(path3, index, lines) {
35170
- lines.push(`Path ${index + 1}`);
35171
- asRecords(path3.hops).forEach((hop, hopIndex) => {
35172
- const symbol2 = asString(hop.symbol) ?? "(unknown)";
35173
- const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
35174
- const line = asNumber(hop.line);
35175
- const entry = hop.is_entry_point === true ? " [entry]" : "";
35176
- lines.push(treeLine(hopIndex + 1, `${symbol2}${entry} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
35177
- });
35178
- }
35179
35519
  function buildNavigateSections(args, payload, theme) {
35180
- const response = asRecord2(payload);
35181
- if (!response)
35182
- return [theme.fg("muted", "No navigation result.")];
35183
- if (args.op === "call_tree") {
35184
- const lines = [];
35185
- renderCallTreeNode(response, 0, lines);
35186
- const warning = depthWarning(response, theme);
35187
- if (warning)
35188
- lines.push(warning);
35189
- return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
35190
- }
35191
- if (args.op === "callers") {
35192
- const groups = asRecords(response.callers);
35193
- const warning = depthWarning(response, theme);
35194
- const sections2 = [
35195
- `${theme.fg("success", `${asNumber(response.total_callers) ?? 0} caller${(asNumber(response.total_callers) ?? 0) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`)} ${warning}`.trim()
35196
- ];
35197
- groups.forEach((group) => {
35198
- const file2 = shortenPath2(asString(group.file) ?? "(unknown file)");
35199
- const lines = [theme.fg("accent", file2)];
35200
- asRecords(group.callers).forEach((caller) => {
35201
- lines.push(` ↳ ${asString(caller.symbol) ?? "(unknown)"} ${theme.fg("muted", `line ${asNumber(caller.line) ?? "?"}`)}`);
35202
- });
35203
- sections2.push(lines.join(`
35204
- `));
35205
- });
35206
- return sections2;
35207
- }
35208
- if (args.op === "trace_to_symbol") {
35209
- const path3 = asRecords(response.path);
35210
- const complete = asBoolean(response.complete);
35211
- const reason = asString(response.reason);
35212
- if (path3.length === 0) {
35213
- const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
35214
- return [`${prefix}${reason ? ` (${reason})` : ""}`];
35215
- }
35216
- const lines = [theme.fg("success", `${path3.length} hop${path3.length === 1 ? "" : "s"}`)];
35217
- path3.forEach((hop, index) => {
35218
- const symbol2 = asString(hop.symbol) ?? "(unknown)";
35219
- const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
35220
- const line = asNumber(hop.line);
35221
- lines.push(treeLine(index + 1, `${symbol2} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
35222
- });
35223
- return lines;
35224
- }
35225
- if (args.op === "trace_to") {
35226
- const paths = asRecords(response.paths);
35227
- const warning = depthWarning(response, theme, "max_depth_reached", "truncated_paths");
35228
- const sections2 = [
35229
- `${theme.fg("success", `${asNumber(response.total_paths) ?? paths.length} path${(asNumber(response.total_paths) ?? paths.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.entry_points_found) ?? 0} entry point${(asNumber(response.entry_points_found) ?? 0) === 1 ? "" : "s"}`)} ${warning}`.trim()
35230
- ];
35231
- if (paths.length === 0)
35232
- sections2.push(theme.fg("muted", "No entry paths found."));
35233
- paths.forEach((path3, index) => {
35234
- const lines = [];
35235
- renderTracePath(path3, index, lines);
35236
- sections2.push(lines.join(`
35237
- `));
35238
- });
35239
- return sections2;
35240
- }
35241
- if (args.op === "impact") {
35242
- const callers = asRecords(response.callers);
35243
- const warning = depthWarning(response, theme);
35244
- const sections2 = [
35245
- `${theme.fg("warning", `${asNumber(response.total_affected) ?? callers.length} affected call site${(asNumber(response.total_affected) ?? callers.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.affected_files) ?? 0} file${(asNumber(response.affected_files) ?? 0) === 1 ? "" : "s"}`)} ${warning}`.trim()
35246
- ];
35247
- if (callers.length === 0)
35248
- sections2.push(theme.fg("muted", "No impacted callers found."));
35249
- callers.forEach((caller) => {
35250
- const file2 = shortenPath2(asString(caller.caller_file) ?? "(unknown file)");
35251
- const symbol2 = asString(caller.caller_symbol) ?? "(unknown)";
35252
- const line = asNumber(caller.line) ?? 0;
35253
- const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
35254
- const expression = asString(caller.call_expression);
35255
- const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
35256
- sections2.push([
35257
- `${theme.fg("accent", file2)}:${line}`,
35258
- ` ↳ ${symbol2}${entry}`,
35259
- expression ? ` ${theme.fg("muted", expression)}` : undefined,
35260
- params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
35261
- ].filter(Boolean).join(`
35262
- `));
35263
- });
35264
- return sections2;
35265
- }
35266
- const hops = asRecords(response.hops);
35267
- const sections = [
35268
- `${theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`)} ${asBoolean(response.depth_limited) ? theme.fg("warning", "(depth limited)") : ""}`.trim()
35269
- ];
35270
- if (hops.length === 0)
35271
- sections.push(theme.fg("muted", "No data-flow hops found."));
35272
- hops.forEach((hop, index) => {
35273
- const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
35274
- const symbol2 = asString(hop.symbol) ?? "(unknown)";
35275
- const variable = asString(hop.variable) ?? "(unknown)";
35276
- const line = asNumber(hop.line) ?? 0;
35277
- const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
35278
- sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol2} [${file2}:${line}]${approximate}`));
35279
- });
35280
- return sections;
35520
+ const themeAdapter = {
35521
+ fg: (role, s) => theme.fg(role, s)
35522
+ };
35523
+ return formatCallgraphSections(args.op, payload, themeAdapter);
35281
35524
  }
35282
35525
  function renderNavigateCall(args, theme, context) {
35283
35526
  const summary = [
@@ -35340,7 +35583,8 @@ function registerNavigateTool(pi, ctx) {
35340
35583
  req.toFile = toFile;
35341
35584
  try {
35342
35585
  const response = await callBridge(bridge, params.op, req, extCtx);
35343
- return textResult(JSON.stringify(response, null, 2));
35586
+ return textResult(formatCallgraphSections(params.op, response, PLAIN_CALLGRAPH_THEME).join(`
35587
+ `), response);
35344
35588
  } catch (error50) {
35345
35589
  if (error50 instanceof BridgeError && CALLGRAPH_SOFT_CODES.has(error50.code)) {
35346
35590
  return textResult(error50.message);
@@ -35418,25 +35662,25 @@ function buildOutlineSections(text, theme) {
35418
35662
  `)];
35419
35663
  }
35420
35664
  function buildZoomSections(args, payload, theme) {
35421
- const batch = asRecord2(payload);
35665
+ const batch = asRecord3(payload);
35422
35666
  const batchItems = Array.isArray(batch?.symbols) ? batch.symbols : Array.isArray(batch?.entries) ? batch.entries : null;
35423
35667
  if (batchItems) {
35424
35668
  const header = batch?.complete === false ? [theme.fg("warning", "Incomplete zoom results")] : [];
35425
35669
  return [
35426
35670
  ...header,
35427
35671
  ...batchItems.map((item) => {
35428
- const record2 = asRecord2(item);
35672
+ const record2 = asRecord3(item);
35429
35673
  if (!record2)
35430
35674
  return theme.fg("muted", "No zoom result available.");
35431
- const name = asString(record2.name) ?? "(unknown symbol)";
35432
- const itemTargetLabel = asString(record2.targetLabel) ?? zoomTargetLabel(args);
35675
+ const name = asString2(record2.name) ?? "(unknown symbol)";
35676
+ const itemTargetLabel = asString2(record2.targetLabel) ?? zoomTargetLabel(args);
35433
35677
  if (record2.success === false) {
35434
- const location = record2.targetLabel ? ` in ${shortenPath2(itemTargetLabel)}` : "";
35435
- return theme.fg("error", `Symbol "${name}" not found${location}: ${asString(record2.error) ?? "zoom failed"}`);
35678
+ const location = record2.targetLabel ? ` in ${shortenPath3(itemTargetLabel)}` : "";
35679
+ return theme.fg("error", `Symbol "${name}" not found${location}: ${asString2(record2.error) ?? "zoom failed"}`);
35436
35680
  }
35437
- const content = asString(record2.content);
35681
+ const content = asString2(record2.content);
35438
35682
  return [
35439
- `${theme.fg("accent", name)} ${theme.fg("muted", shortenPath2(itemTargetLabel))}`,
35683
+ `${theme.fg("accent", name)} ${theme.fg("muted", shortenPath3(itemTargetLabel))}`,
35440
35684
  content
35441
35685
  ].filter(Boolean).join(`
35442
35686
  `);
@@ -35447,32 +35691,32 @@ function buildZoomSections(args, payload, theme) {
35447
35691
  if (items.length === 0)
35448
35692
  return [theme.fg("muted", "No zoom result available.")];
35449
35693
  return items.map((item) => {
35450
- const record2 = asRecord2(item);
35694
+ const record2 = asRecord3(item);
35451
35695
  if (!record2)
35452
35696
  return theme.fg("muted", "No zoom result available.");
35453
- const name = asString(record2.name) ?? "(unknown symbol)";
35454
- const kind = asString(record2.kind) ?? "symbol";
35455
- const range = asRecord2(record2.range);
35697
+ const name = asString2(record2.name) ?? "(unknown symbol)";
35698
+ const kind = asString2(record2.kind) ?? "symbol";
35699
+ const range = asRecord3(record2.range);
35456
35700
  const startLine = range && typeof range.start_line === "number" ? range.start_line : undefined;
35457
35701
  const endLine = range && typeof range.end_line === "number" ? range.end_line : undefined;
35458
35702
  const targetLabel = zoomTargetLabel(args);
35459
- const location = startLine !== undefined ? `${shortenPath2(targetLabel)}:${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : shortenPath2(targetLabel);
35703
+ const location = startLine !== undefined ? `${shortenPath3(targetLabel)}:${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : shortenPath3(targetLabel);
35460
35704
  const lines = [`${theme.fg("accent", name)} ${theme.fg("muted", `[${kind}] ${location}`)}`];
35461
- const content = asString(record2.content);
35705
+ const content = asString2(record2.content);
35462
35706
  if (content) {
35463
35707
  lines.push(content.split(`
35464
35708
  `).map((line) => ` ${line}`).join(`
35465
35709
  `));
35466
35710
  }
35467
- const annotations = asRecord2(record2.annotations);
35468
- const callsOut = annotations ? asRecords(annotations.calls_out) : [];
35469
- const calledBy = annotations ? asRecords(annotations.called_by) : [];
35711
+ const annotations = asRecord3(record2.annotations);
35712
+ const callsOut = annotations ? asRecords2(annotations.calls_out) : [];
35713
+ const calledBy = annotations ? asRecords2(annotations.called_by) : [];
35470
35714
  if (callsOut.length > 0) {
35471
- lines.push(`${theme.fg("muted", "calls out")}`, callsOut.map((call) => ` ↳ ${asString(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
35715
+ lines.push(`${theme.fg("muted", "calls out")}`, callsOut.map((call) => ` ↳ ${asString2(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
35472
35716
  `));
35473
35717
  }
35474
35718
  if (calledBy.length > 0) {
35475
- lines.push(`${theme.fg("muted", "called by")}`, calledBy.map((call) => ` ↳ ${asString(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
35719
+ lines.push(`${theme.fg("muted", "called by")}`, calledBy.map((call) => ` ↳ ${asString2(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
35476
35720
  `));
35477
35721
  }
35478
35722
  return lines.join(`
@@ -35811,32 +36055,32 @@ var RefactorParams = Type11.Object({
35811
36055
  callSiteLine: optionalInt(1, Number.MAX_SAFE_INTEGER)
35812
36056
  });
35813
36057
  function buildRefactorSections(args, payload, theme) {
35814
- const response = asRecord2(payload);
36058
+ const response = asRecord3(payload);
35815
36059
  if (!response)
35816
36060
  return [theme.fg("muted", "No refactor result.")];
35817
36061
  if (args.op === "move") {
35818
- const results = asRecords(response.results);
36062
+ const results = asRecords2(response.results);
35819
36063
  return [
35820
36064
  `${theme.fg("success", "moved symbol")} ${theme.fg("toolOutput", args.symbol ?? "(symbol)")}`,
35821
- `${theme.fg("muted", "files modified")} ${asNumber(response.files_modified) ?? results.length}`,
35822
- `${theme.fg("muted", "consumers updated")} ${asNumber(response.consumers_updated) ?? 0}`,
35823
- results.length > 0 ? results.map((entry) => ` ↳ ${shortenPath2(asString(entry.file) ?? "(unknown file)")}`).join(`
36065
+ `${theme.fg("muted", "files modified")} ${asNumber2(response.files_modified) ?? results.length}`,
36066
+ `${theme.fg("muted", "consumers updated")} ${asNumber2(response.consumers_updated) ?? 0}`,
36067
+ results.length > 0 ? results.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(unknown file)")}`).join(`
35824
36068
  `) : theme.fg("muted", "No files reported.")
35825
36069
  ];
35826
36070
  }
35827
36071
  if (args.op === "extract") {
35828
36072
  return [
35829
- `${theme.fg("success", "extracted")} ${theme.fg("toolOutput", asString(response.name) ?? args.name ?? "(function)")}`,
35830
- `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath2(asString(response.file) ?? args.filePath))}`,
36073
+ `${theme.fg("success", "extracted")} ${theme.fg("toolOutput", asString2(response.name) ?? args.name ?? "(function)")}`,
36074
+ `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath))}`,
35831
36075
  `${theme.fg("muted", "params")} ${Array.isArray(response.parameters) ? response.parameters.join(", ") || "none" : "none"}`,
35832
- `${theme.fg("muted", "return type")} ${asString(response.return_type) ?? "unknown"}`
36076
+ `${theme.fg("muted", "return type")} ${asString2(response.return_type) ?? "unknown"}`
35833
36077
  ];
35834
36078
  }
35835
36079
  return [
35836
- `${theme.fg("success", "inlined")} ${theme.fg("toolOutput", asString(response.symbol) ?? args.symbol ?? "(symbol)")}`,
35837
- `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath2(asString(response.file) ?? args.filePath))}`,
35838
- `${theme.fg("muted", "context")} ${asString(response.call_context) ?? "unknown"}`,
35839
- `${theme.fg("muted", "substitutions")} ${asNumber(response.substitutions) ?? 0}`
36080
+ `${theme.fg("success", "inlined")} ${theme.fg("toolOutput", asString2(response.symbol) ?? args.symbol ?? "(symbol)")}`,
36081
+ `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath))}`,
36082
+ `${theme.fg("muted", "context")} ${asString2(response.call_context) ?? "unknown"}`,
36083
+ `${theme.fg("muted", "substitutions")} ${asNumber2(response.substitutions) ?? 0}`
35840
36084
  ];
35841
36085
  }
35842
36086
  function renderRefactorCall(args, theme, context) {
@@ -35945,34 +36189,34 @@ var SafetyParams = Type12.Object({
35945
36189
  }))
35946
36190
  });
35947
36191
  function buildSafetySections(args, payload, theme) {
35948
- const response = asRecord2(payload);
36192
+ const response = asRecord3(payload);
35949
36193
  if (!response)
35950
36194
  return [theme.fg("muted", "No safety result.")];
35951
36195
  if (args.op === "undo") {
35952
36196
  if (response.operation === true) {
35953
36197
  return [
35954
- `${theme.fg("success", "restored operation")} ${theme.fg("accent", asString(response.op_id) ?? "(operation)")}`,
35955
- `${theme.fg("muted", "files")} ${asNumber(response.restored_count) ?? asRecords(response.restored).length}`
36198
+ `${theme.fg("success", "restored operation")} ${theme.fg("accent", asString2(response.op_id) ?? "(operation)")}`,
36199
+ `${theme.fg("muted", "files")} ${asNumber2(response.restored_count) ?? asRecords2(response.restored).length}`
35956
36200
  ];
35957
36201
  }
35958
36202
  return [
35959
- `${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath2(asString(response.path) ?? args.filePath ?? "(file)"))}`,
35960
- `${theme.fg("muted", "backup")} ${asString(response.backup_id) ?? "—"}`
36203
+ `${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.filePath ?? "(file)"))}`,
36204
+ `${theme.fg("muted", "backup")} ${asString2(response.backup_id) ?? "—"}`
35961
36205
  ];
35962
36206
  }
35963
36207
  if (args.op === "history") {
35964
- const entries = asRecords(response.entries);
36208
+ const entries = asRecords2(response.entries);
35965
36209
  const sections2 = [
35966
- theme.fg("accent", shortenPath2(asString(response.file) ?? args.filePath ?? "(file)"))
36210
+ theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath ?? "(file)"))
35967
36211
  ];
35968
36212
  if (entries.length === 0) {
35969
36213
  sections2.push(theme.fg("muted", "No history entries."));
35970
36214
  return sections2;
35971
36215
  }
35972
36216
  sections2.push(entries.map((entry, index) => {
35973
- const backupId = asString(entry.backup_id) ?? `entry-${index + 1}`;
36217
+ const backupId = asString2(entry.backup_id) ?? `entry-${index + 1}`;
35974
36218
  const timestamp = formatTimestamp(entry.timestamp) ?? "unknown time";
35975
- const description = asString(entry.description) ?? "";
36219
+ const description = asString2(entry.description) ?? "";
35976
36220
  return `${index + 1}. ${backupId} ${theme.fg("muted", timestamp)}${description ? `
35977
36221
  ${description}` : ""}`;
35978
36222
  }).join(`
@@ -35980,22 +36224,22 @@ function buildSafetySections(args, payload, theme) {
35980
36224
  return sections2;
35981
36225
  }
35982
36226
  if (args.op === "checkpoint") {
35983
- const skipped = asRecords(response.skipped);
36227
+ const skipped = asRecords2(response.skipped);
35984
36228
  return [
35985
- `${theme.fg("success", "checkpoint created")} ${theme.fg("accent", asString(response.name) ?? args.name ?? "(checkpoint)")}`,
35986
- `${theme.fg("muted", "files")} ${asNumber(response.file_count) ?? 0}`,
36229
+ `${theme.fg("success", "checkpoint created")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
36230
+ `${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`,
35987
36231
  skipped.length > 0 ? `${theme.fg("warning", "skipped")}
35988
- ${skipped.map((entry) => ` ↳ ${shortenPath2(asString(entry.file) ?? "(file)")}: ${asString(entry.error) ?? "unknown error"}`).join(`
36232
+ ${skipped.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(file)")}: ${asString2(entry.error) ?? "unknown error"}`).join(`
35989
36233
  `)}` : theme.fg("muted", "No skipped files.")
35990
36234
  ];
35991
36235
  }
35992
36236
  if (args.op === "restore") {
35993
36237
  return [
35994
- `${theme.fg("success", "checkpoint restored")} ${theme.fg("accent", asString(response.name) ?? args.name ?? "(checkpoint)")}`,
35995
- `${theme.fg("muted", "files")} ${asNumber(response.file_count) ?? 0}`
36238
+ `${theme.fg("success", "checkpoint restored")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
36239
+ `${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`
35996
36240
  ];
35997
36241
  }
35998
- const checkpoints = asRecords(response.checkpoints);
36242
+ const checkpoints = asRecords2(response.checkpoints);
35999
36243
  const sections = [
36000
36244
  theme.fg("accent", `${checkpoints.length} checkpoint${checkpoints.length === 1 ? "" : "s"}`)
36001
36245
  ];
@@ -36004,8 +36248,8 @@ ${skipped.map((entry) => ` ↳ ${shortenPath2(asString(entry.file) ?? "(file)")
36004
36248
  return sections;
36005
36249
  }
36006
36250
  sections.push(checkpoints.map((checkpoint, index) => {
36007
- const name = asString(checkpoint.name) ?? `checkpoint-${index + 1}`;
36008
- const count = asNumber(checkpoint.file_count) ?? 0;
36251
+ const name = asString2(checkpoint.name) ?? `checkpoint-${index + 1}`;
36252
+ const count = asNumber2(checkpoint.file_count) ?? 0;
36009
36253
  const created = formatTimestamp(checkpoint.created_at) ?? "unknown time";
36010
36254
  return `${index + 1}. ${name} ${theme.fg("muted", `${count} file${count === 1 ? "" : "s"} · ${created}`)}`;
36011
36255
  }).join(`
@@ -36149,13 +36393,13 @@ var SearchParams2 = Type13.Object({
36149
36393
  }))
36150
36394
  });
36151
36395
  function buildSemanticSections(args, payload, theme) {
36152
- const response = asRecord2(payload);
36396
+ const response = asRecord3(payload);
36153
36397
  if (!response)
36154
36398
  return [theme.fg("muted", "No search result.")];
36155
- const status = asString(response.status) ?? "unknown";
36156
- const semanticStatus = asString(response.semantic_status) ?? status;
36157
- const interpretedAs = asString(response.interpreted_as) ?? "unknown";
36158
- const queryKind = asString(response.query_kind);
36399
+ const status = asString2(response.status) ?? "unknown";
36400
+ const semanticStatus = asString2(response.semantic_status) ?? status;
36401
+ const interpretedAs = asString2(response.interpreted_as) ?? "unknown";
36402
+ const queryKind = asString2(response.query_kind);
36159
36403
  const sections = [
36160
36404
  `${theme.fg(semanticStatus === "ready" ? "success" : "warning", `semantic: ${semanticStatus}`)} ${theme.fg("muted", `mode=${interpretedAs}${queryKind ? ` kind=${queryKind}` : ""} query=${JSON.stringify(args.query)} topK=${args.topK ?? 10}`)}`
36161
36405
  ];
@@ -36167,34 +36411,34 @@ function buildSemanticSections(args, payload, theme) {
36167
36411
  const honestyNote = semanticHonestyNote(response, theme);
36168
36412
  if (honestyNote)
36169
36413
  sections.push(honestyNote);
36170
- const results = asRecords(response.results);
36414
+ const results = asRecords2(response.results);
36171
36415
  if (status !== "ready" && results.length === 0) {
36172
- sections.push(asString(response.text) ?? theme.fg("muted", "Semantic index is not ready."));
36416
+ sections.push(asString2(response.text) ?? theme.fg("muted", "Semantic index is not ready."));
36173
36417
  return sections;
36174
36418
  }
36175
36419
  if (results.length === 0) {
36176
36420
  sections.push(theme.fg("muted", "No matches found."));
36177
36421
  return sections;
36178
36422
  }
36179
- const grouped = groupByFile(results, (result) => asString(result.file));
36423
+ const grouped = groupByFile(results, (result) => asString2(result.file));
36180
36424
  for (const [file2, fileResults] of grouped.entries()) {
36181
- const lines = [theme.fg("accent", shortenPath2(file2))];
36425
+ const lines = [theme.fg("accent", shortenPath3(file2))];
36182
36426
  fileResults.forEach((result) => {
36183
- if (asString(result.kind) === "GrepLine") {
36184
- const line = asNumber(result.line);
36185
- const column = asNumber(result.column);
36186
- const lineText = asString(result.line_text) ?? "";
36427
+ if (asString2(result.kind) === "GrepLine") {
36428
+ const line = asNumber2(result.line);
36429
+ const column = asNumber2(result.column);
36430
+ const lineText = asString2(result.line_text) ?? "";
36187
36431
  const location2 = line !== undefined ? `${line}${column !== undefined ? `:${column}` : ""}` : "?";
36188
36432
  lines.push(` ↳ ${theme.fg("muted", `line ${location2}`)} ${lineText}`);
36189
36433
  return;
36190
36434
  }
36191
- const score = asNumber(result.score);
36192
- const source = asString(result.source);
36193
- const kind = asString(result.kind) ?? "symbol";
36194
- const location = asString(result.location);
36435
+ const score = asNumber2(result.score);
36436
+ const source = asString2(result.source);
36437
+ const kind = asString2(result.kind) ?? "symbol";
36438
+ const location = asString2(result.location);
36195
36439
  if (source === "lexical") {
36196
36440
  lines.push(` ↳ ${theme.fg("muted", `[lexical match${score !== undefined ? ` — score ${score.toFixed(3)}` : ""}]`)}`);
36197
- const snippet2 = asString(result.snippet);
36441
+ const snippet2 = asString2(result.snippet);
36198
36442
  if (snippet2) {
36199
36443
  lines.push(...snippet2.split(`
36200
36444
  `).map((line) => ` ${line}`));
@@ -36202,16 +36446,16 @@ function buildSemanticSections(args, payload, theme) {
36202
36446
  return;
36203
36447
  }
36204
36448
  if (kind === "file_summary" || location === "[file summary]") {
36205
- const summary = asString(result.snippet) ?? asString(result.name) ?? "(no summary)";
36449
+ const summary = asString2(result.snippet) ?? asString2(result.name) ?? "(no summary)";
36206
36450
  lines.push(` ↳ ${summary} ${theme.fg("muted", `[file summary${score !== undefined ? ` score ${score.toFixed(3)}` : ""}]`)}`);
36207
36451
  return;
36208
36452
  }
36209
- const startLine = asNumber(result.start_line);
36210
- const endLine = asNumber(result.end_line);
36453
+ const startLine = asNumber2(result.start_line);
36454
+ const endLine = asNumber2(result.end_line);
36211
36455
  const range = startLine !== undefined ? `${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : "?";
36212
- const name = asString(result.name) ?? "(unknown)";
36456
+ const name = asString2(result.name) ?? "(unknown)";
36213
36457
  lines.push(` ↳ ${name} ${theme.fg("muted", `[${kind}] lines ${range}${score !== undefined ? ` score ${score.toFixed(3)}` : ""}`)}`);
36214
- const snippet = asString(result.snippet);
36458
+ const snippet = asString2(result.snippet);
36215
36459
  if (snippet) {
36216
36460
  lines.push(...snippet.split(`
36217
36461
  `).map((line) => ` ${line}`));
@@ -36323,11 +36567,11 @@ function buildWorkflowHints(opts) {
36323
36567
  }
36324
36568
  if (hasBgBash) {
36325
36569
  sections.push([
36326
- `**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately. Then do ONE of these:`,
36327
- "1. Keep working on something independent of the result.",
36328
- "2. End your turn a completion reminder with the result arrives automatically.",
36329
- `3. Need to react to a specific output line early? Register \`bash_watch({ task_id, pattern, background: true })\`, then end your turn — it pings you the moment the pattern appears.`,
36330
- `\`bash_status({ task_id })\` is for inspecting output AFTER the reminder arrives, or one quick look at a live task — never call it repeatedly to wait: the reminder already delivers the result, and each poll wastes a turn. Sync \`bash_watch\` (without \`background: true\`) blocks your turn and locks the user out reserve it for waits of a few seconds (a dev server printing its ready line), never for builds or test suites.`
36570
+ `**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately.`,
36571
+ "1. Nothing else useful to do (a build/test/validation whose result is the next thing you need) → sync `bash_watch` to block until it exits (pass a longer timeout_ms for long commands; the user can interrupt).",
36572
+ "2. Useful parallel work available → end your turn; the completion reminder delivers the result. (Or spawn a subagent for the side work.)",
36573
+ "3. Want to react to a specific early output line async `bash_watch` (background:true + pattern).",
36574
+ "Never loop `bash_status` to wait — it's a one-shot inspector, not a wait primitive."
36331
36575
  ].join(`
36332
36576
  `));
36333
36577
  sections.push(`**PTY / interactive commands**: PTY mode is for interactive REPLs and terminal apps (python, node, bash itself, vim). Start with \`${bashName}({ command: "python", pty: true, background: true })\`, read the screen with \`bash_status({ task_id, output_mode: "screen" })\`, and send input with \`bash_write({ task_id, input: "..." })\`.`);
@@ -36442,7 +36686,7 @@ function isConfigureWarning(value) {
36442
36686
  if (!value || typeof value !== "object" || Array.isArray(value))
36443
36687
  return false;
36444
36688
  const warning = value;
36445
- return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing") && typeof warning.hint === "string";
36689
+ return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing" || warning.kind === "config_parse_failed") && typeof warning.hint === "string";
36446
36690
  }
36447
36691
  function coerceConfigureWarnings(warnings) {
36448
36692
  return warnings.filter(isConfigureWarning);
@@ -36452,6 +36696,18 @@ function drainPendingEagerWarnings(projectRoot) {
36452
36696
  pendingEagerWarnings.delete(projectRoot);
36453
36697
  return pending;
36454
36698
  }
36699
+ function enqueueConfigParseWarnings(projectRoot, errors3) {
36700
+ if (!projectRoot || errors3.length === 0)
36701
+ return;
36702
+ const pending = pendingEagerWarnings.get(projectRoot) ?? [];
36703
+ for (const entry of errors3) {
36704
+ const hint = formatConfigParseFailureMessage(entry.path, entry.message);
36705
+ if (!pending.some((item) => item.kind === "config_parse_failed" && item.hint === hint)) {
36706
+ pending.push({ kind: "config_parse_failed", hint });
36707
+ }
36708
+ }
36709
+ pendingEagerWarnings.set(projectRoot, pending);
36710
+ }
36455
36711
  function shouldPrepareOnnxRuntime(config2) {
36456
36712
  const isFastembedSemanticBackend = (config2.semantic?.backend ?? "fastembed") === "fastembed";
36457
36713
  return config2.semantic_search === true && isFastembedSemanticBackend;
@@ -36560,6 +36816,7 @@ async function src_default(pi) {
36560
36816
  }
36561
36817
  await ensureStorageMigrated({ harness: "pi", binaryPath, logger: bridgeLogger });
36562
36818
  const config2 = loadAftConfig(process.cwd());
36819
+ enqueueConfigParseWarnings(process.cwd(), getConfigLoadErrors());
36563
36820
  const storageDir = resolveCortexKitStorageRoot();
36564
36821
  let onnxRuntimePromise = null;
36565
36822
  if (shouldPrepareOnnxRuntime(config2)) {
@@ -36568,30 +36825,7 @@ async function src_default(pi) {
36568
36825
  return null;
36569
36826
  });
36570
36827
  }
36571
- const configOverrides = {};
36572
- if (config2.format_on_edit !== undefined)
36573
- configOverrides.format_on_edit = config2.format_on_edit;
36574
- if (config2.formatter_timeout_secs !== undefined)
36575
- configOverrides.formatter_timeout_secs = config2.formatter_timeout_secs;
36576
- if (config2.validate_on_edit !== undefined)
36577
- configOverrides.validate_on_edit = config2.validate_on_edit;
36578
- if (config2.formatter !== undefined)
36579
- configOverrides.formatter = config2.formatter;
36580
- if (config2.checker !== undefined)
36581
- configOverrides.checker = config2.checker;
36582
- configOverrides.restrict_to_project_root = config2.restrict_to_project_root ?? false;
36583
- if (config2.search_index !== undefined)
36584
- configOverrides.search_index = config2.search_index;
36585
- if (config2.semantic_search !== undefined)
36586
- configOverrides.semantic_search = config2.semantic_search;
36587
- Object.assign(configOverrides, resolveExperimentalConfigForConfigure(config2));
36588
- Object.assign(configOverrides, resolveLspConfigForConfigure(config2));
36589
- if (config2.semantic !== undefined)
36590
- configOverrides.semantic = config2.semantic;
36591
- if (config2.inspect !== undefined)
36592
- configOverrides.inspect = config2.inspect;
36593
- if (config2.max_callgraph_files !== undefined)
36594
- configOverrides.max_callgraph_files = config2.max_callgraph_files;
36828
+ const configOverrides = resolveProjectOverridesForConfigure(config2);
36595
36829
  if (config2.url_fetch_allow_private !== undefined)
36596
36830
  configOverrides.url_fetch_allow_private = config2.url_fetch_allow_private;
36597
36831
  configOverrides.storage_dir = storageDir;
@@ -36658,6 +36892,7 @@ ${lines}
36658
36892
  }
36659
36893
  let pool;
36660
36894
  const poolOptions = {
36895
+ ...resolveBridgePoolTransportOptions(config2),
36661
36896
  errorPrefix: "[aft-pi]",
36662
36897
  minVersion: PLUGIN_VERSION,
36663
36898
  onVersionMismatch: createVersionMismatchHandler(() => pool),
@@ -36798,12 +37033,25 @@ ${lines}
36798
37033
  return { content, details: event.details, isError: event.isError };
36799
37034
  });
36800
37035
  pi.on("turn_end", async (_event, extCtx) => {
37036
+ const sessionID = resolveSessionId(extCtx);
36801
37037
  await handleTurnEndBgCompletions({
36802
37038
  ctx,
36803
37039
  directory: extCtx.cwd,
36804
- sessionID: resolveSessionId(extCtx),
37040
+ sessionID,
36805
37041
  runtime: pi
36806
37042
  });
37043
+ const bridge = pool.getActiveBridgeForRoot(extCtx.cwd);
37044
+ if (bridge && sessionID) {
37045
+ handleConfigureWarningsForSession({
37046
+ projectRoot: extCtx.cwd,
37047
+ sessionId: sessionID,
37048
+ client: pi,
37049
+ bridge,
37050
+ warnings: [],
37051
+ storageDir,
37052
+ pluginVersion: PLUGIN_VERSION
37053
+ });
37054
+ }
36807
37055
  });
36808
37056
  pi.on("input", (_event, extCtx) => {
36809
37057
  signalSyncWatchAbort(resolveSessionId(extCtx));
@@ -36832,6 +37080,7 @@ ${lines}
36832
37080
  log2(`AFT extension ready (surface=${config2.tool_surface ?? "recommended"})`);
36833
37081
  }
36834
37082
  var __test__2 = {
37083
+ enqueueConfigParseWarnings,
36835
37084
  bridgeDirectoryFromCallback,
36836
37085
  resolveToolSurface,
36837
37086
  handleConfigureWarningsForSession,