@buildautomaton/cli 0.1.56 → 0.1.57

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/cli.js CHANGED
@@ -973,8 +973,8 @@ var require_command = __commonJS({
973
973
  "../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports) {
974
974
  var EventEmitter2 = __require("node:events").EventEmitter;
975
975
  var childProcess2 = __require("node:child_process");
976
- var path73 = __require("node:path");
977
- var fs54 = __require("node:fs");
976
+ var path75 = __require("node:path");
977
+ var fs53 = __require("node:fs");
978
978
  var process8 = __require("node:process");
979
979
  var { Argument: Argument2, humanReadableArgName } = require_argument();
980
980
  var { CommanderError: CommanderError2 } = require_error();
@@ -1906,11 +1906,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1906
1906
  let launchWithNode = false;
1907
1907
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1908
1908
  function findFile(baseDir, baseName) {
1909
- const localBin = path73.resolve(baseDir, baseName);
1910
- if (fs54.existsSync(localBin)) return localBin;
1911
- if (sourceExt.includes(path73.extname(baseName))) return void 0;
1909
+ const localBin = path75.resolve(baseDir, baseName);
1910
+ if (fs53.existsSync(localBin)) return localBin;
1911
+ if (sourceExt.includes(path75.extname(baseName))) return void 0;
1912
1912
  const foundExt = sourceExt.find(
1913
- (ext) => fs54.existsSync(`${localBin}${ext}`)
1913
+ (ext) => fs53.existsSync(`${localBin}${ext}`)
1914
1914
  );
1915
1915
  if (foundExt) return `${localBin}${foundExt}`;
1916
1916
  return void 0;
@@ -1922,21 +1922,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1922
1922
  if (this._scriptPath) {
1923
1923
  let resolvedScriptPath;
1924
1924
  try {
1925
- resolvedScriptPath = fs54.realpathSync(this._scriptPath);
1925
+ resolvedScriptPath = fs53.realpathSync(this._scriptPath);
1926
1926
  } catch (err) {
1927
1927
  resolvedScriptPath = this._scriptPath;
1928
1928
  }
1929
- executableDir = path73.resolve(
1930
- path73.dirname(resolvedScriptPath),
1929
+ executableDir = path75.resolve(
1930
+ path75.dirname(resolvedScriptPath),
1931
1931
  executableDir
1932
1932
  );
1933
1933
  }
1934
1934
  if (executableDir) {
1935
1935
  let localFile = findFile(executableDir, executableFile);
1936
1936
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1937
- const legacyName = path73.basename(
1937
+ const legacyName = path75.basename(
1938
1938
  this._scriptPath,
1939
- path73.extname(this._scriptPath)
1939
+ path75.extname(this._scriptPath)
1940
1940
  );
1941
1941
  if (legacyName !== this._name) {
1942
1942
  localFile = findFile(
@@ -1947,7 +1947,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1947
1947
  }
1948
1948
  executableFile = localFile || executableFile;
1949
1949
  }
1950
- launchWithNode = sourceExt.includes(path73.extname(executableFile));
1950
+ launchWithNode = sourceExt.includes(path75.extname(executableFile));
1951
1951
  let proc;
1952
1952
  if (process8.platform !== "win32") {
1953
1953
  if (launchWithNode) {
@@ -2787,7 +2787,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2787
2787
  * @return {Command}
2788
2788
  */
2789
2789
  nameFromFilename(filename) {
2790
- this._name = path73.basename(filename, path73.extname(filename));
2790
+ this._name = path75.basename(filename, path75.extname(filename));
2791
2791
  return this;
2792
2792
  }
2793
2793
  /**
@@ -2801,9 +2801,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2801
2801
  * @param {string} [path]
2802
2802
  * @return {(string|null|Command)}
2803
2803
  */
2804
- executableDir(path74) {
2805
- if (path74 === void 0) return this._executableDir;
2806
- this._executableDir = path74;
2804
+ executableDir(path76) {
2805
+ if (path76 === void 0) return this._executableDir;
2806
+ this._executableDir = path76;
2807
2807
  return this;
2808
2808
  }
2809
2809
  /**
@@ -7061,8 +7061,8 @@ var init_parseUtil = __esm({
7061
7061
  init_errors();
7062
7062
  init_en();
7063
7063
  makeIssue = (params) => {
7064
- const { data, path: path73, errorMaps, issueData } = params;
7065
- const fullPath = [...path73, ...issueData.path || []];
7064
+ const { data, path: path75, errorMaps, issueData } = params;
7065
+ const fullPath = [...path75, ...issueData.path || []];
7066
7066
  const fullIssue = {
7067
7067
  ...issueData,
7068
7068
  path: fullPath
@@ -7370,11 +7370,11 @@ var init_types = __esm({
7370
7370
  init_parseUtil();
7371
7371
  init_util();
7372
7372
  ParseInputLazyPath = class {
7373
- constructor(parent, value, path73, key) {
7373
+ constructor(parent, value, path75, key) {
7374
7374
  this._cachedPath = [];
7375
7375
  this.parent = parent;
7376
7376
  this.data = value;
7377
- this._path = path73;
7377
+ this._path = path75;
7378
7378
  this._key = key;
7379
7379
  }
7380
7380
  get path() {
@@ -11099,10 +11099,10 @@ function assignProp(target, prop, value) {
11099
11099
  configurable: true
11100
11100
  });
11101
11101
  }
11102
- function getElementAtPath(obj, path73) {
11103
- if (!path73)
11102
+ function getElementAtPath(obj, path75) {
11103
+ if (!path75)
11104
11104
  return obj;
11105
- return path73.reduce((acc, key) => acc?.[key], obj);
11105
+ return path75.reduce((acc, key) => acc?.[key], obj);
11106
11106
  }
11107
11107
  function promiseAllObject(promisesObj) {
11108
11108
  const keys = Object.keys(promisesObj);
@@ -11351,11 +11351,11 @@ function aborted(x, startIndex = 0) {
11351
11351
  }
11352
11352
  return false;
11353
11353
  }
11354
- function prefixIssues(path73, issues) {
11354
+ function prefixIssues(path75, issues) {
11355
11355
  return issues.map((iss) => {
11356
11356
  var _a2;
11357
11357
  (_a2 = iss).path ?? (_a2.path = []);
11358
- iss.path.unshift(path73);
11358
+ iss.path.unshift(path75);
11359
11359
  return iss;
11360
11360
  });
11361
11361
  }
@@ -11544,7 +11544,7 @@ function treeifyError(error40, _mapper) {
11544
11544
  return issue2.message;
11545
11545
  };
11546
11546
  const result = { errors: [] };
11547
- const processError = (error41, path73 = []) => {
11547
+ const processError = (error41, path75 = []) => {
11548
11548
  var _a2, _b;
11549
11549
  for (const issue2 of error41.issues) {
11550
11550
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -11554,7 +11554,7 @@ function treeifyError(error40, _mapper) {
11554
11554
  } else if (issue2.code === "invalid_element") {
11555
11555
  processError({ issues: issue2.issues }, issue2.path);
11556
11556
  } else {
11557
- const fullpath = [...path73, ...issue2.path];
11557
+ const fullpath = [...path75, ...issue2.path];
11558
11558
  if (fullpath.length === 0) {
11559
11559
  result.errors.push(mapper(issue2));
11560
11560
  continue;
@@ -11584,9 +11584,9 @@ function treeifyError(error40, _mapper) {
11584
11584
  processError(error40);
11585
11585
  return result;
11586
11586
  }
11587
- function toDotPath(path73) {
11587
+ function toDotPath(path75) {
11588
11588
  const segs = [];
11589
- for (const seg of path73) {
11589
+ for (const seg of path75) {
11590
11590
  if (typeof seg === "number")
11591
11591
  segs.push(`[${seg}]`);
11592
11592
  else if (typeof seg === "symbol")
@@ -24592,7 +24592,7 @@ var require_ignore = __commonJS({
24592
24592
  // path matching.
24593
24593
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
24594
24594
  // @returns {TestResult} true if a file is ignored
24595
- test(path73, checkUnignored, mode) {
24595
+ test(path75, checkUnignored, mode) {
24596
24596
  let ignored = false;
24597
24597
  let unignored = false;
24598
24598
  let matchedRule;
@@ -24601,7 +24601,7 @@ var require_ignore = __commonJS({
24601
24601
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
24602
24602
  return;
24603
24603
  }
24604
- const matched = rule[mode].test(path73);
24604
+ const matched = rule[mode].test(path75);
24605
24605
  if (!matched) {
24606
24606
  return;
24607
24607
  }
@@ -24622,17 +24622,17 @@ var require_ignore = __commonJS({
24622
24622
  var throwError = (message, Ctor) => {
24623
24623
  throw new Ctor(message);
24624
24624
  };
24625
- var checkPath = (path73, originalPath, doThrow) => {
24626
- if (!isString(path73)) {
24625
+ var checkPath = (path75, originalPath, doThrow) => {
24626
+ if (!isString(path75)) {
24627
24627
  return doThrow(
24628
24628
  `path must be a string, but got \`${originalPath}\``,
24629
24629
  TypeError
24630
24630
  );
24631
24631
  }
24632
- if (!path73) {
24632
+ if (!path75) {
24633
24633
  return doThrow(`path must not be empty`, TypeError);
24634
24634
  }
24635
- if (checkPath.isNotRelative(path73)) {
24635
+ if (checkPath.isNotRelative(path75)) {
24636
24636
  const r = "`path.relative()`d";
24637
24637
  return doThrow(
24638
24638
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -24641,7 +24641,7 @@ var require_ignore = __commonJS({
24641
24641
  }
24642
24642
  return true;
24643
24643
  };
24644
- var isNotRelative = (path73) => REGEX_TEST_INVALID_PATH.test(path73);
24644
+ var isNotRelative = (path75) => REGEX_TEST_INVALID_PATH.test(path75);
24645
24645
  checkPath.isNotRelative = isNotRelative;
24646
24646
  checkPath.convert = (p) => p;
24647
24647
  var Ignore = class {
@@ -24671,19 +24671,19 @@ var require_ignore = __commonJS({
24671
24671
  }
24672
24672
  // @returns {TestResult}
24673
24673
  _test(originalPath, cache2, checkUnignored, slices) {
24674
- const path73 = originalPath && checkPath.convert(originalPath);
24674
+ const path75 = originalPath && checkPath.convert(originalPath);
24675
24675
  checkPath(
24676
- path73,
24676
+ path75,
24677
24677
  originalPath,
24678
24678
  this._strictPathCheck ? throwError : RETURN_FALSE
24679
24679
  );
24680
- return this._t(path73, cache2, checkUnignored, slices);
24680
+ return this._t(path75, cache2, checkUnignored, slices);
24681
24681
  }
24682
- checkIgnore(path73) {
24683
- if (!REGEX_TEST_TRAILING_SLASH.test(path73)) {
24684
- return this.test(path73);
24682
+ checkIgnore(path75) {
24683
+ if (!REGEX_TEST_TRAILING_SLASH.test(path75)) {
24684
+ return this.test(path75);
24685
24685
  }
24686
- const slices = path73.split(SLASH).filter(Boolean);
24686
+ const slices = path75.split(SLASH).filter(Boolean);
24687
24687
  slices.pop();
24688
24688
  if (slices.length) {
24689
24689
  const parent = this._t(
@@ -24696,18 +24696,18 @@ var require_ignore = __commonJS({
24696
24696
  return parent;
24697
24697
  }
24698
24698
  }
24699
- return this._rules.test(path73, false, MODE_CHECK_IGNORE);
24699
+ return this._rules.test(path75, false, MODE_CHECK_IGNORE);
24700
24700
  }
24701
- _t(path73, cache2, checkUnignored, slices) {
24702
- if (path73 in cache2) {
24703
- return cache2[path73];
24701
+ _t(path75, cache2, checkUnignored, slices) {
24702
+ if (path75 in cache2) {
24703
+ return cache2[path75];
24704
24704
  }
24705
24705
  if (!slices) {
24706
- slices = path73.split(SLASH).filter(Boolean);
24706
+ slices = path75.split(SLASH).filter(Boolean);
24707
24707
  }
24708
24708
  slices.pop();
24709
24709
  if (!slices.length) {
24710
- return cache2[path73] = this._rules.test(path73, checkUnignored, MODE_IGNORE);
24710
+ return cache2[path75] = this._rules.test(path75, checkUnignored, MODE_IGNORE);
24711
24711
  }
24712
24712
  const parent = this._t(
24713
24713
  slices.join(SLASH) + SLASH,
@@ -24715,29 +24715,29 @@ var require_ignore = __commonJS({
24715
24715
  checkUnignored,
24716
24716
  slices
24717
24717
  );
24718
- return cache2[path73] = parent.ignored ? parent : this._rules.test(path73, checkUnignored, MODE_IGNORE);
24718
+ return cache2[path75] = parent.ignored ? parent : this._rules.test(path75, checkUnignored, MODE_IGNORE);
24719
24719
  }
24720
- ignores(path73) {
24721
- return this._test(path73, this._ignoreCache, false).ignored;
24720
+ ignores(path75) {
24721
+ return this._test(path75, this._ignoreCache, false).ignored;
24722
24722
  }
24723
24723
  createFilter() {
24724
- return (path73) => !this.ignores(path73);
24724
+ return (path75) => !this.ignores(path75);
24725
24725
  }
24726
24726
  filter(paths) {
24727
24727
  return makeArray(paths).filter(this.createFilter());
24728
24728
  }
24729
24729
  // @returns {TestResult}
24730
- test(path73) {
24731
- return this._test(path73, this._testCache, true);
24730
+ test(path75) {
24731
+ return this._test(path75, this._testCache, true);
24732
24732
  }
24733
24733
  };
24734
24734
  var factory = (options) => new Ignore(options);
24735
- var isPathValid = (path73) => checkPath(path73 && checkPath.convert(path73), path73, RETURN_FALSE);
24735
+ var isPathValid = (path75) => checkPath(path75 && checkPath.convert(path75), path75, RETURN_FALSE);
24736
24736
  var setupWindows = () => {
24737
24737
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
24738
24738
  checkPath.convert = makePosix;
24739
24739
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
24740
- checkPath.isNotRelative = (path73) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path73) || isNotRelative(path73);
24740
+ checkPath.isNotRelative = (path75) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path75) || isNotRelative(path75);
24741
24741
  };
24742
24742
  if (
24743
24743
  // Detect `process` so that it can run in browsers.
@@ -25526,10 +25526,10 @@ var require_src2 = __commonJS({
25526
25526
  var fs_1 = __require("fs");
25527
25527
  var debug_1 = __importDefault(require_src());
25528
25528
  var log2 = debug_1.default("@kwsites/file-exists");
25529
- function check2(path73, isFile, isDirectory) {
25530
- log2(`checking %s`, path73);
25529
+ function check2(path75, isFile, isDirectory) {
25530
+ log2(`checking %s`, path75);
25531
25531
  try {
25532
- const stat3 = fs_1.statSync(path73);
25532
+ const stat3 = fs_1.statSync(path75);
25533
25533
  if (stat3.isFile() && isFile) {
25534
25534
  log2(`[OK] path represents a file`);
25535
25535
  return true;
@@ -25549,8 +25549,8 @@ var require_src2 = __commonJS({
25549
25549
  throw e;
25550
25550
  }
25551
25551
  }
25552
- function exists2(path73, type = exports.READABLE) {
25553
- return check2(path73, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
25552
+ function exists2(path75, type = exports.READABLE) {
25553
+ return check2(path75, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
25554
25554
  }
25555
25555
  exports.exists = exists2;
25556
25556
  exports.FILE = 1;
@@ -25631,15 +25631,15 @@ var {
25631
25631
  } = import_index.default;
25632
25632
 
25633
25633
  // src/cli-version.ts
25634
- var CLI_VERSION = "0.1.56".length > 0 ? "0.1.56" : "0.0.0-dev";
25634
+ var CLI_VERSION = "0.1.57".length > 0 ? "0.1.57" : "0.0.0-dev";
25635
25635
 
25636
25636
  // src/cli/defaults.ts
25637
25637
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
25638
25638
  var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
25639
25639
 
25640
25640
  // src/cli/run-cli-action.ts
25641
- import * as fs53 from "node:fs";
25642
- import * as path72 from "node:path";
25641
+ import * as fs52 from "node:fs";
25642
+ import * as path74 from "node:path";
25643
25643
 
25644
25644
  // src/cli-log-level.ts
25645
25645
  var verbosity = "info";
@@ -27183,7 +27183,7 @@ function clearMainBridgeReconnectQuietOnOpen(state, log2) {
27183
27183
  function scheduleMainBridgeReconnect(state, connect, log2, closeMeta) {
27184
27184
  if (state.closedByUser || state.currentWs != null) return;
27185
27185
  const meta = closeMeta ?? state.lastReconnectCloseMeta ?? void 0;
27186
- const delay2 = applyTieredReconnectPlanAndLog(
27186
+ const delay3 = applyTieredReconnectPlanAndLog(
27187
27187
  state.mainOutage,
27188
27188
  log2,
27189
27189
  BRIDGE_SERVICE_LABEL,
@@ -27192,7 +27192,7 @@ function scheduleMainBridgeReconnect(state, connect, log2, closeMeta) {
27192
27192
  meta?.reason
27193
27193
  );
27194
27194
  armReconnectDelayTimer({
27195
- delayMs: delay2,
27195
+ delayMs: delay3,
27196
27196
  bumpAttempt: () => {
27197
27197
  state.reconnectAttempt += 1;
27198
27198
  },
@@ -27394,10 +27394,10 @@ function runPendingAuth(options) {
27394
27394
  }
27395
27395
  if (resolved) return;
27396
27396
  beginDeferredPendingCloseLog(code, reason);
27397
- const delay2 = pendingAuthReconnectDelayMs(reconnectAttempt);
27397
+ const delay3 = pendingAuthReconnectDelayMs(reconnectAttempt);
27398
27398
  reconnectAttempt += 1;
27399
27399
  if (signInQuiet.verboseLogs) {
27400
- const delayLabel = formatReconnectDelayForLog(delay2);
27400
+ const delayLabel = formatReconnectDelayForLog(delay3);
27401
27401
  logFn(
27402
27402
  `[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${reconnectAttempt}).`
27403
27403
  );
@@ -27405,7 +27405,7 @@ function runPendingAuth(options) {
27405
27405
  reconnectTimeout = setTimeout(() => {
27406
27406
  reconnectTimeout = null;
27407
27407
  connect();
27408
- }, delay2);
27408
+ }, delay3);
27409
27409
  },
27410
27410
  onError: (err) => logCliWebSocketError(logFn, "[Bridge service]", err, "while waiting for sign-in"),
27411
27411
  onMessage: (data) => {
@@ -27499,11 +27499,7 @@ function isCliSqliteLockError(e) {
27499
27499
  return lower.includes("database is locked") || lower.includes("sqlite_busy") || lower.includes("sqlite3_busy") || lower.includes("database") && lower.includes("locked");
27500
27500
  }
27501
27501
 
27502
- // src/sqlite/create-sqlite-database-access.ts
27503
- var { Database: SqliteDatabase } = sqliteWasm;
27504
- var SYNC_RETRY_MAX = 40;
27505
- var ASYNC_RETRY_MAX = 60;
27506
- var ASYNC_BASE_DELAY_MS = 20;
27502
+ // src/sqlite/sqlite-pragmas.ts
27507
27503
  function applySqliteConcurrencyPragmas(db) {
27508
27504
  try {
27509
27505
  db.run("PRAGMA foreign_keys = ON");
@@ -27529,12 +27525,17 @@ function applySqliteMemoryPragmas(db) {
27529
27525
  } catch {
27530
27526
  }
27531
27527
  }
27528
+
27529
+ // src/sqlite/sqlite-lock-retry.ts
27530
+ var SYNC_RETRY_MAX = 40;
27531
+ var ASYNC_RETRY_MAX = 60;
27532
+ var ASYNC_BASE_DELAY_MS = 20;
27532
27533
  function syncSleepMs(ms) {
27533
27534
  if (ms <= 0) return;
27534
27535
  const deadline = Date.now() + ms;
27535
27536
  while (Date.now() < deadline) {
27536
27537
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
27537
- const chunk = Math.min(80, deadline - Date.now());
27538
+ const chunk = Math.min(10, deadline - Date.now());
27538
27539
  if (chunk <= 0) break;
27539
27540
  try {
27540
27541
  const sab = new SharedArrayBuffer(4);
@@ -27558,7 +27559,22 @@ async function asyncDelayMsInterruptible(ms) {
27558
27559
  await yieldToEventLoop();
27559
27560
  }
27560
27561
  }
27562
+
27563
+ // src/sqlite/create-sqlite-database-access.ts
27564
+ var { Database: SqliteDatabase } = sqliteWasm;
27561
27565
  function createSqliteDatabaseAccess(config2) {
27566
+ let migrateDoneForPath = null;
27567
+ function shouldSkipMigrate(sqlitePath, db) {
27568
+ if (migrateDoneForPath !== sqlitePath) return false;
27569
+ try {
27570
+ const row = db.get(
27571
+ "SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='__migrations' LIMIT 1"
27572
+ );
27573
+ return row != null;
27574
+ } catch {
27575
+ return false;
27576
+ }
27577
+ }
27562
27578
  function safeClose(db) {
27563
27579
  if (db == null) return;
27564
27580
  try {
@@ -27574,7 +27590,11 @@ function createSqliteDatabaseAccess(config2) {
27574
27590
  try {
27575
27591
  applySqliteConcurrencyPragmas(db);
27576
27592
  applySqliteMemoryPragmas(db);
27577
- config2.migrate(db);
27593
+ const sqlitePath2 = config2.getPath();
27594
+ if (!shouldSkipMigrate(sqlitePath2, db)) {
27595
+ config2.migrate(db);
27596
+ migrateDoneForPath = sqlitePath2;
27597
+ }
27578
27598
  config2.afterOpen?.(db, options);
27579
27599
  } catch (e) {
27580
27600
  safeClose(db);
@@ -27630,14 +27650,16 @@ function createSqliteDatabaseAccess(config2) {
27630
27650
  async function ensureInitialized(options) {
27631
27651
  await withAsync(() => void 0, options);
27632
27652
  }
27653
+ function closeAllConnections() {
27654
+ migrateDoneForPath = null;
27655
+ }
27633
27656
  return {
27634
27657
  safeClose,
27635
27658
  openConnection,
27636
27659
  withSync,
27637
27660
  withAsync,
27638
27661
  ensureInitialized,
27639
- closeAllConnections: () => {
27640
- }
27662
+ closeAllConnections
27641
27663
  };
27642
27664
  }
27643
27665
 
@@ -27837,7 +27859,7 @@ function readMigrationSql(filename, ...searchSubdirs) {
27837
27859
  join2(moduleDir, "..", sub, filename),
27838
27860
  join2(moduleDir, "..", "..", "dist", sub, filename)
27839
27861
  ]);
27840
- const resolved = candidates.find((path73) => existsSync(path73));
27862
+ const resolved = candidates.find((path75) => existsSync(path75));
27841
27863
  if (!resolved) {
27842
27864
  throw new Error(`Missing SQLite migration SQL: ${filename} (searched ${searchSubdirs.join(", ")})`);
27843
27865
  }
@@ -27969,6 +27991,161 @@ var withCliSqliteSync = cliSqlite.withSync;
27969
27991
  var withCliSqlite = cliSqlite.withAsync;
27970
27992
  var ensureCliSqliteInitialized = cliSqlite.ensureInitialized;
27971
27993
 
27994
+ // src/sqlite/code-nav-cache/code-nav-cache-paths.ts
27995
+ import fs15 from "node:fs";
27996
+ import path12 from "node:path";
27997
+ import os7 from "node:os";
27998
+ function getCodeNavCacheSqlitePath() {
27999
+ const override = process.env.BUILDAMATON_CODE_NAV_CACHE_SQLITE_PATH?.trim();
28000
+ if (override) return path12.resolve(override);
28001
+ return path12.join(os7.homedir(), ".buildautomaton", "code-nav-cache.sqlite");
28002
+ }
28003
+ function ensureCodeNavCacheSqliteParentDir(sqlitePath) {
28004
+ const dir = path12.dirname(sqlitePath);
28005
+ if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
28006
+ }
28007
+
28008
+ // src/sqlite/code-nav-cache/migrate-code-nav-cache-sqlite.ts
28009
+ var CHECKPOINT_V12 = "001_code_nav_cache_checkpoint_v1";
28010
+ var CHECKPOINT_V1_SQL2 = readCodeNavCacheMigrationSql("001_code_nav_cache_checkpoint_v1.sql");
28011
+ var MIGRATION_002 = "002_content_hash_symbol_index";
28012
+ var MIGRATION_002_SQL = readCodeNavCacheMigrationSql("002_content_hash_symbol_index.sql");
28013
+ var MIGRATION_003 = "003_content_hash_tag_rows";
28014
+ var MIGRATION_003_SQL = readCodeNavCacheMigrationSql("003_content_hash_tag_rows.sql");
28015
+ var CODE_NAV_CACHE_SQLITE_MIGRATIONS = [
28016
+ {
28017
+ name: CHECKPOINT_V12,
28018
+ checkpoint: true,
28019
+ migrate: (db) => {
28020
+ db.exec(CHECKPOINT_V1_SQL2);
28021
+ },
28022
+ alreadyApplied: (db) => {
28023
+ const row = db.get(
28024
+ `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='file_index_parent_path' LIMIT 1`
28025
+ );
28026
+ return row != null;
28027
+ }
28028
+ },
28029
+ {
28030
+ name: MIGRATION_002,
28031
+ migrate: (db) => {
28032
+ db.exec(MIGRATION_002_SQL);
28033
+ },
28034
+ alreadyApplied: (db) => {
28035
+ const row = db.get(
28036
+ `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='symbol_file_blob' LIMIT 1`
28037
+ );
28038
+ return row != null;
28039
+ }
28040
+ },
28041
+ {
28042
+ name: MIGRATION_003,
28043
+ migrate: (db) => {
28044
+ db.exec(MIGRATION_003_SQL);
28045
+ },
28046
+ alreadyApplied: (db) => {
28047
+ const row = db.get(
28048
+ `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='symbol_index_tag' LIMIT 1`
28049
+ );
28050
+ if (row == null) return false;
28051
+ const blob = db.get(
28052
+ `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='symbol_file_blob' LIMIT 1`
28053
+ );
28054
+ return blob == null;
28055
+ }
28056
+ }
28057
+ ];
28058
+ function migrateCodeNavCacheSqlite(db) {
28059
+ runSqliteMigrations(
28060
+ db,
28061
+ CODE_NAV_CACHE_SQLITE_MIGRATIONS,
28062
+ readCodeNavCacheMigrationSql("000_bootstrap_migrations_table.sql"),
28063
+ "code-nav-cache"
28064
+ );
28065
+ }
28066
+
28067
+ // src/sqlite/code-nav-cache/code-nav-cache-sqlite-work-lock.ts
28068
+ import fs16 from "node:fs";
28069
+ function isSqliteCorruptError(e) {
28070
+ const msg = e instanceof Error ? e.message : String(e);
28071
+ return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
28072
+ }
28073
+ var chain = Promise.resolve();
28074
+ var workLockDepth = 0;
28075
+ function resetCodeNavCacheSqliteWorkLock() {
28076
+ chain = Promise.resolve();
28077
+ workLockDepth = 0;
28078
+ }
28079
+ function withCodeNavCacheSqliteWorkLock(fn) {
28080
+ if (workLockDepth > 0) {
28081
+ return Promise.resolve(fn());
28082
+ }
28083
+ const run = async () => {
28084
+ workLockDepth += 1;
28085
+ try {
28086
+ return await Promise.resolve(fn());
28087
+ } finally {
28088
+ workLockDepth = Math.max(0, workLockDepth - 1);
28089
+ }
28090
+ };
28091
+ const runWithCorruptRecovery = async () => {
28092
+ try {
28093
+ return await run();
28094
+ } catch (e) {
28095
+ if (!isSqliteCorruptError(e)) throw e;
28096
+ try {
28097
+ fs16.unlinkSync(getCodeNavCacheSqlitePath());
28098
+ } catch {
28099
+ }
28100
+ chain = Promise.resolve();
28101
+ return await run();
28102
+ }
28103
+ };
28104
+ const next = chain.then(runWithCorruptRecovery);
28105
+ chain = next.then(
28106
+ () => void 0,
28107
+ () => void 0
28108
+ );
28109
+ return next;
28110
+ }
28111
+
28112
+ // src/sqlite/code-nav-cache/code-nav-cache-database.ts
28113
+ var codeNavCacheSqlite = createSqliteDatabaseAccess({
28114
+ getPath: getCodeNavCacheSqlitePath,
28115
+ ensureParentDir: ensureCodeNavCacheSqliteParentDir,
28116
+ migrate: migrateCodeNavCacheSqlite
28117
+ });
28118
+ var codeNavCacheAccessDepth = 0;
28119
+ async function waitForOuterCodeNavCacheAccessAsync() {
28120
+ while (codeNavCacheAccessDepth > 0) {
28121
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
28122
+ await new Promise((resolve34) => setTimeout(resolve34, 5));
28123
+ await yieldToEventLoop();
28124
+ }
28125
+ }
28126
+ var safeCloseCodeNavCacheSqliteDatabase = codeNavCacheSqlite.safeClose;
28127
+ function closeAllCodeNavCacheSqliteConnections() {
28128
+ codeNavCacheSqlite.closeAllConnections();
28129
+ resetCodeNavCacheSqliteWorkLock();
28130
+ codeNavCacheAccessDepth = 0;
28131
+ }
28132
+ var openCodeNavCacheSqliteConnection = codeNavCacheSqlite.openConnection;
28133
+ async function withCodeNavCacheSqlite(fn) {
28134
+ return withCodeNavCacheSqliteWorkLock(async () => {
28135
+ const nested = codeNavCacheAccessDepth > 0;
28136
+ if (!nested) await waitForOuterCodeNavCacheAccessAsync();
28137
+ codeNavCacheAccessDepth += 1;
28138
+ try {
28139
+ return await codeNavCacheSqlite.withAsync(fn);
28140
+ } finally {
28141
+ codeNavCacheAccessDepth = Math.max(0, codeNavCacheAccessDepth - 1);
28142
+ }
28143
+ });
28144
+ }
28145
+ async function ensureCodeNavCacheSqliteInitialized() {
28146
+ await withCodeNavCacheSqlite(() => void 0);
28147
+ }
28148
+
27972
28149
  // src/connection/close-bridge-connection.ts
27973
28150
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
27974
28151
  requestCliImmediateShutdown();
@@ -28014,6 +28191,7 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
28014
28191
  }
28015
28192
  try {
28016
28193
  closeAllCliSqliteConnections();
28194
+ closeAllCodeNavCacheSqliteConnections();
28017
28195
  } catch {
28018
28196
  }
28019
28197
  say("Shutdown complete.");
@@ -28635,9 +28813,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
28635
28813
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
28636
28814
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
28637
28815
  if (!rawPath || !summary) continue;
28638
- const path73 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
28639
- if (!path73) continue;
28640
- rows.push({ path: path73, summary: clampSummaryToAtMostTwoLines(summary) });
28816
+ const path75 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
28817
+ if (!path75) continue;
28818
+ rows.push({ path: path75, summary: clampSummaryToAtMostTwoLines(summary) });
28641
28819
  }
28642
28820
  return rows;
28643
28821
  }
@@ -28991,16 +29169,16 @@ function getAgentModelFromAgentConfig(config2) {
28991
29169
  }
28992
29170
 
28993
29171
  // src/paths/session-layout-paths.ts
28994
- import * as path12 from "node:path";
29172
+ import * as path13 from "node:path";
28995
29173
  function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
28996
- const resolved = worktreePaths.map((p) => path12.resolve(p)).filter(Boolean);
29174
+ const resolved = worktreePaths.map((p) => path13.resolve(p)).filter(Boolean);
28997
29175
  if (resolved.length === 0) return null;
28998
29176
  resolved.sort();
28999
29177
  return resolved[0];
29000
29178
  }
29001
29179
  function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
29002
29180
  if (resolvedSessionParentPath != null && String(resolvedSessionParentPath).trim() !== "") {
29003
- return path12.resolve(String(resolvedSessionParentPath).trim());
29181
+ return path13.resolve(String(resolvedSessionParentPath).trim());
29004
29182
  }
29005
29183
  return getBridgeRoot();
29006
29184
  }
@@ -29345,21 +29523,21 @@ import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs
29345
29523
  import { dirname as dirname3 } from "node:path";
29346
29524
 
29347
29525
  // src/agents/acp/safe-fs-path.ts
29348
- import * as path13 from "node:path";
29526
+ import * as path14 from "node:path";
29349
29527
  function resolveSafePathUnderCwd(cwd, filePath) {
29350
29528
  const trimmed2 = filePath.trim();
29351
29529
  if (!trimmed2) return null;
29352
- const normalizedCwd = path13.resolve(cwd);
29353
- const resolved = path13.isAbsolute(trimmed2) ? path13.normalize(trimmed2) : path13.resolve(normalizedCwd, trimmed2);
29354
- const rel = path13.relative(normalizedCwd, resolved);
29355
- if (rel.startsWith("..") || path13.isAbsolute(rel)) return null;
29530
+ const normalizedCwd = path14.resolve(cwd);
29531
+ const resolved = path14.isAbsolute(trimmed2) ? path14.normalize(trimmed2) : path14.resolve(normalizedCwd, trimmed2);
29532
+ const rel = path14.relative(normalizedCwd, resolved);
29533
+ if (rel.startsWith("..") || path14.isAbsolute(rel)) return null;
29356
29534
  return resolved;
29357
29535
  }
29358
29536
  function toDisplayPathRelativeToCwd(cwd, absolutePath) {
29359
- const normalizedCwd = path13.resolve(cwd);
29360
- const rel = path13.relative(normalizedCwd, path13.resolve(absolutePath));
29361
- if (!rel || rel === "") return path13.basename(absolutePath);
29362
- return rel.split(path13.sep).join("/");
29537
+ const normalizedCwd = path14.resolve(cwd);
29538
+ const rel = path14.relative(normalizedCwd, path14.resolve(absolutePath));
29539
+ if (!rel || rel === "") return path14.basename(absolutePath);
29540
+ return rel.split(path14.sep).join("/");
29363
29541
  }
29364
29542
 
29365
29543
  // src/agents/acp/clients/shared/acp-fs-read-write.ts
@@ -30762,16 +30940,16 @@ import { existsSync as existsSync2, statSync } from "node:fs";
30762
30940
 
30763
30941
  // src/git/get-git-repo-root-sync.ts
30764
30942
  import { execFileSync as execFileSync2 } from "node:child_process";
30765
- import * as path14 from "node:path";
30943
+ import * as path15 from "node:path";
30766
30944
  function getGitRepoRootSync(startDir) {
30767
30945
  try {
30768
30946
  const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
30769
- cwd: path14.resolve(startDir),
30947
+ cwd: path15.resolve(startDir),
30770
30948
  encoding: "utf8",
30771
30949
  stdio: ["ignore", "pipe", "ignore"],
30772
30950
  maxBuffer: 1024 * 1024
30773
30951
  }).trim();
30774
- return out ? path14.resolve(out) : null;
30952
+ return out ? path15.resolve(out) : null;
30775
30953
  } catch {
30776
30954
  return null;
30777
30955
  }
@@ -30780,25 +30958,25 @@ function getGitRepoRootSync(startDir) {
30780
30958
  // src/agents/acp/workspace-files.ts
30781
30959
  import { execFileSync as execFileSync3 } from "node:child_process";
30782
30960
  import { readFileSync as readFileSync3 } from "node:fs";
30783
- import * as path15 from "node:path";
30961
+ import * as path16 from "node:path";
30784
30962
  function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
30785
30963
  const trimmed2 = rawPath.trim();
30786
30964
  if (!trimmed2) return null;
30787
- const normalizedSessionParent = path15.resolve(sessionParentPath);
30965
+ const normalizedSessionParent = path16.resolve(sessionParentPath);
30788
30966
  let resolvedPath = resolveSafePathUnderCwd(sessionParentPath, trimmed2);
30789
30967
  if (!resolvedPath) {
30790
- const candidate = path15.isAbsolute(trimmed2) ? path15.normalize(trimmed2) : path15.normalize(path15.resolve(normalizedSessionParent, trimmed2));
30968
+ const candidate = path16.isAbsolute(trimmed2) ? path16.normalize(trimmed2) : path16.normalize(path16.resolve(normalizedSessionParent, trimmed2));
30791
30969
  const gitRoot2 = getGitRepoRootSync(sessionParentPath);
30792
30970
  if (!gitRoot2) return null;
30793
- const rel = path15.relative(gitRoot2, candidate);
30794
- if (rel.startsWith("..") || path15.isAbsolute(rel)) return null;
30971
+ const rel = path16.relative(gitRoot2, candidate);
30972
+ if (rel.startsWith("..") || path16.isAbsolute(rel)) return null;
30795
30973
  resolvedPath = candidate;
30796
30974
  }
30797
30975
  const gitRoot = getGitRepoRootSync(sessionParentPath);
30798
30976
  if (gitRoot) {
30799
- const relFromRoot = path15.relative(gitRoot, resolvedPath);
30800
- if (!relFromRoot.startsWith("..") && !path15.isAbsolute(relFromRoot)) {
30801
- return { resolvedPath, display: relFromRoot.split(path15.sep).join("/") };
30977
+ const relFromRoot = path16.relative(gitRoot, resolvedPath);
30978
+ if (!relFromRoot.startsWith("..") && !path16.isAbsolute(relFromRoot)) {
30979
+ return { resolvedPath, display: relFromRoot.split(path16.sep).join("/") };
30802
30980
  }
30803
30981
  }
30804
30982
  return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
@@ -30807,9 +30985,9 @@ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
30807
30985
  if (!displayPath || displayPath.includes("..")) return "";
30808
30986
  const gitRoot = getGitRepoRootSync(sessionParentPath);
30809
30987
  if (gitRoot) {
30810
- const resolvedPath2 = path15.resolve(gitRoot, displayPath);
30811
- const rel = path15.relative(gitRoot, resolvedPath2);
30812
- if (!rel.startsWith("..") && !path15.isAbsolute(rel)) {
30988
+ const resolvedPath2 = path16.resolve(gitRoot, displayPath);
30989
+ const rel = path16.relative(gitRoot, resolvedPath2);
30990
+ if (!rel.startsWith("..") && !path16.isAbsolute(rel)) {
30813
30991
  try {
30814
30992
  return readFileSync3(resolvedPath2, "utf8");
30815
30993
  } catch {
@@ -30828,9 +31006,9 @@ function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
30828
31006
  if (!displayPath || displayPath.includes("..")) return null;
30829
31007
  const gitRoot = getGitRepoRootSync(sessionParentPath);
30830
31008
  if (gitRoot) {
30831
- const resolvedPath = path15.resolve(gitRoot, displayPath);
30832
- const rel = path15.relative(gitRoot, resolvedPath);
30833
- if (!rel.startsWith("..") && !path15.isAbsolute(rel)) return resolvedPath;
31009
+ const resolvedPath = path16.resolve(gitRoot, displayPath);
31010
+ const rel = path16.relative(gitRoot, resolvedPath);
31011
+ if (!rel.startsWith("..") && !path16.isAbsolute(rel)) return resolvedPath;
30834
31012
  }
30835
31013
  return resolveSafePathUnderCwd(sessionParentPath, displayPath);
30836
31014
  }
@@ -30881,92 +31059,22 @@ function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
30881
31059
  return { isDirectory: false, directoryRemoved: false };
30882
31060
  }
30883
31061
 
30884
- // src/sqlite/code-nav-cache/code-nav-cache-paths.ts
30885
- import fs15 from "node:fs";
30886
- import path16 from "node:path";
30887
- import os7 from "node:os";
30888
- function getCodeNavCacheSqlitePath() {
30889
- const override = process.env.BUILDAMATON_CODE_NAV_CACHE_SQLITE_PATH?.trim();
30890
- if (override) return path16.resolve(override);
30891
- return path16.join(os7.homedir(), ".buildautomaton", "code-nav-cache.sqlite");
30892
- }
30893
- function ensureCodeNavCacheSqliteParentDir(sqlitePath) {
30894
- const dir = path16.dirname(sqlitePath);
30895
- if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
30896
- }
30897
-
30898
- // src/sqlite/code-nav-cache/migrate-code-nav-cache-sqlite.ts
30899
- var CHECKPOINT_V12 = "001_code_nav_cache_checkpoint_v1";
30900
- var CHECKPOINT_V1_SQL2 = readCodeNavCacheMigrationSql("001_code_nav_cache_checkpoint_v1.sql");
30901
- var MIGRATION_002 = "002_content_hash_symbol_index";
30902
- var MIGRATION_002_SQL = readCodeNavCacheMigrationSql("002_content_hash_symbol_index.sql");
30903
- var MIGRATION_003 = "003_content_hash_tag_rows";
30904
- var MIGRATION_003_SQL = readCodeNavCacheMigrationSql("003_content_hash_tag_rows.sql");
30905
- var CODE_NAV_CACHE_SQLITE_MIGRATIONS = [
30906
- {
30907
- name: CHECKPOINT_V12,
30908
- checkpoint: true,
30909
- migrate: (db) => {
30910
- db.exec(CHECKPOINT_V1_SQL2);
30911
- },
30912
- alreadyApplied: (db) => {
30913
- const row = db.get(
30914
- `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='file_index_parent_path' LIMIT 1`
30915
- );
30916
- return row != null;
30917
- }
30918
- },
30919
- {
30920
- name: MIGRATION_002,
30921
- migrate: (db) => {
30922
- db.exec(MIGRATION_002_SQL);
30923
- },
30924
- alreadyApplied: (db) => {
30925
- const row = db.get(
30926
- `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='symbol_file_blob' LIMIT 1`
30927
- );
30928
- return row != null;
30929
- }
30930
- },
30931
- {
30932
- name: MIGRATION_003,
30933
- migrate: (db) => {
30934
- db.exec(MIGRATION_003_SQL);
30935
- },
30936
- alreadyApplied: (db) => {
30937
- const row = db.get(
30938
- `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='symbol_index_tag' LIMIT 1`
30939
- );
30940
- if (row == null) return false;
30941
- const blob = db.get(
30942
- `SELECT 1 as ok FROM sqlite_master WHERE type='table' AND name='symbol_file_blob' LIMIT 1`
30943
- );
30944
- return blob == null;
31062
+ // src/sqlite/code-nav-cache/code-nav-cache-sqlite-transaction.ts
31063
+ function runCodeNavCacheWriteTransaction(db, fn) {
31064
+ db.run("BEGIN IMMEDIATE");
31065
+ try {
31066
+ const result = fn();
31067
+ db.run("COMMIT");
31068
+ return result;
31069
+ } catch (e) {
31070
+ try {
31071
+ db.run("ROLLBACK");
31072
+ } catch {
30945
31073
  }
31074
+ throw e;
30946
31075
  }
30947
- ];
30948
- function migrateCodeNavCacheSqlite(db) {
30949
- runSqliteMigrations(
30950
- db,
30951
- CODE_NAV_CACHE_SQLITE_MIGRATIONS,
30952
- readCodeNavCacheMigrationSql("000_bootstrap_migrations_table.sql"),
30953
- "code-nav-cache"
30954
- );
30955
31076
  }
30956
31077
 
30957
- // src/sqlite/code-nav-cache/code-nav-cache-database.ts
30958
- var codeNavCacheSqlite = createSqliteDatabaseAccess({
30959
- getPath: getCodeNavCacheSqlitePath,
30960
- ensureParentDir: ensureCodeNavCacheSqliteParentDir,
30961
- migrate: migrateCodeNavCacheSqlite
30962
- });
30963
- var safeCloseCodeNavCacheSqliteDatabase = codeNavCacheSqlite.safeClose;
30964
- var closeAllCodeNavCacheSqliteConnections = codeNavCacheSqlite.closeAllConnections;
30965
- var openCodeNavCacheSqliteConnection = codeNavCacheSqlite.openConnection;
30966
- var withCodeNavCacheSqliteSync = codeNavCacheSqlite.withSync;
30967
- var withCodeNavCacheSqlite = codeNavCacheSqlite.withAsync;
30968
- var ensureCodeNavCacheSqliteInitialized = codeNavCacheSqlite.ensureInitialized;
30969
-
30970
31078
  // src/code-nav/yield-during-work.ts
30971
31079
  var CODE_NAV_YIELD_EVERY = INDEX_WORK_YIELD_EVERY;
30972
31080
  function createCodeNavYieldState() {
@@ -30981,11 +31089,11 @@ async function yieldDuringCodeNavWork(state) {
30981
31089
  }
30982
31090
 
30983
31091
  // src/code-nav/walk-code-nav-files.ts
30984
- import fs18 from "node:fs";
31092
+ import fs19 from "node:fs";
30985
31093
  import path20 from "node:path";
30986
31094
 
30987
31095
  // src/files/index/walk-workspace-tree.ts
30988
- import fs16 from "node:fs";
31096
+ import fs17 from "node:fs";
30989
31097
  import path17 from "node:path";
30990
31098
  var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
30991
31099
  "node_modules",
@@ -31003,7 +31111,7 @@ function shouldSkipWorkspaceWalkEntry(name) {
31003
31111
  async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
31004
31112
  let names;
31005
31113
  try {
31006
- names = await fs16.promises.readdir(dir);
31114
+ names = await fs17.promises.readdir(dir);
31007
31115
  } catch {
31008
31116
  return;
31009
31117
  }
@@ -31018,7 +31126,7 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
31018
31126
  const full = path17.join(dir, name);
31019
31127
  let stat3;
31020
31128
  try {
31021
- stat3 = await fs16.promises.stat(full);
31129
+ stat3 = await fs17.promises.stat(full);
31022
31130
  } catch {
31023
31131
  continue;
31024
31132
  }
@@ -31036,7 +31144,7 @@ function createWalkYieldState() {
31036
31144
 
31037
31145
  // src/code-nav/gitignore-filter.ts
31038
31146
  var import_ignore = __toESM(require_ignore(), 1);
31039
- import fs17 from "node:fs";
31147
+ import fs18 from "node:fs";
31040
31148
  import path18 from "node:path";
31041
31149
  var DEFAULT_IGNORED = [".git", ".git/**"];
31042
31150
  function normalizeRelPath(relPath) {
@@ -31047,7 +31155,7 @@ var GitignoreFilter = class {
31047
31155
  /** Load `.gitignore` in `absDir` and push a layer when the file exists. */
31048
31156
  async enterDirectory(absDir, dirRel) {
31049
31157
  try {
31050
- const content = await fs17.promises.readFile(path18.join(absDir, ".gitignore"), "utf8");
31158
+ const content = await fs18.promises.readFile(path18.join(absDir, ".gitignore"), "utf8");
31051
31159
  this.layers.push({ dirRel, ig: (0, import_ignore.default)().add(content) });
31052
31160
  } catch {
31053
31161
  }
@@ -31150,7 +31258,7 @@ async function walkCodeNavFilesInDirectory(dir, baseDir, relDir, filter, onFile,
31150
31258
  await filter.enterDirectory(dir, relDir);
31151
31259
  let names;
31152
31260
  try {
31153
- names = await fs18.promises.readdir(dir);
31261
+ names = await fs19.promises.readdir(dir);
31154
31262
  } catch {
31155
31263
  filter.leaveDirectory(relDir);
31156
31264
  return;
@@ -31167,7 +31275,7 @@ async function walkCodeNavFilesInDirectory(dir, baseDir, relDir, filter, onFile,
31167
31275
  const full = path20.join(dir, name);
31168
31276
  let stat3;
31169
31277
  try {
31170
- stat3 = await fs18.promises.stat(full);
31278
+ stat3 = await fs19.promises.stat(full);
31171
31279
  } catch {
31172
31280
  continue;
31173
31281
  }
@@ -31374,12 +31482,27 @@ function removeParentFileMapEntry(db, parentId, filePath) {
31374
31482
  db.run("DELETE FROM symbol_parent_file_map WHERE parent_id = ? AND file_path = ?", [parentId, filePath]);
31375
31483
  return previous === void 0 ? null : previous;
31376
31484
  }
31377
- function clearParentFileMap(db, parentId) {
31378
- const rows = db.all("SELECT content_hash FROM symbol_parent_file_map WHERE parent_id = ? AND content_hash IS NOT NULL", [
31379
- parentId
31380
- ]);
31381
- db.run("DELETE FROM symbol_parent_file_map WHERE parent_id = ?", [parentId]);
31382
- return rows.map((row) => row.content_hash);
31485
+ function clearParentFileMapBatch(db, parentId, limit) {
31486
+ const rows = db.all(
31487
+ `SELECT file_path, content_hash
31488
+ FROM symbol_parent_file_map
31489
+ WHERE parent_id = ?
31490
+ LIMIT ?`,
31491
+ [parentId, limit]
31492
+ );
31493
+ if (rows.length === 0) return { released: [], remaining: false };
31494
+ const del = db.prepare("DELETE FROM symbol_parent_file_map WHERE parent_id = ? AND file_path = ?");
31495
+ try {
31496
+ for (const row of rows) {
31497
+ del.run([parentId, row.file_path]);
31498
+ }
31499
+ } finally {
31500
+ del.finalize();
31501
+ }
31502
+ return {
31503
+ released: rows.flatMap((row) => row.content_hash == null ? [] : [row.content_hash]),
31504
+ remaining: rows.length >= limit
31505
+ };
31383
31506
  }
31384
31507
 
31385
31508
  // src/code-nav/symbol-index/storage/symbol-content-index.ts
@@ -31468,139 +31591,15 @@ function removeSymbolIndexFileData(db, ctx, filePath) {
31468
31591
  const released = removeParentFileMapEntry(db, ctx.parentId, filePath);
31469
31592
  gcReleasedHashes(db, [released]);
31470
31593
  }
31471
- function clearSymbolIndexParentData(db, ctx) {
31472
- const released = clearParentFileMap(db, ctx.parentId);
31594
+ function clearSymbolIndexParentDataBatch(db, ctx, limit) {
31595
+ const { released, remaining } = clearParentFileMapBatch(db, ctx.parentId, limit);
31473
31596
  gcReleasedHashes(db, released);
31474
- }
31475
-
31476
- // src/code-nav/symbol-index/query/tag-query-shared.ts
31477
- var TAG_SELECT = `file_path, name, role, kind, start_line, start_column, end_line, end_column,
31478
- scope_start_line, scope_start_column, parent_scope_start_line, parent_scope_start_column, nav_target_path, scope_chain`;
31479
- function rowToLocation(row) {
31480
- return {
31481
- path: row.file_path,
31482
- line: row.start_line + 1,
31483
- column: row.start_column,
31484
- kind: row.kind,
31485
- role: row.role === "def" ? "definition" : "reference"
31486
- };
31487
- }
31488
- function modulePathLocation(row) {
31489
- return {
31490
- path: row.nav_target_path,
31491
- line: 1,
31492
- column: 0,
31493
- kind: "module",
31494
- role: "reference"
31495
- };
31496
- }
31497
- function emptyRefRow(filePath) {
31498
- return {
31499
- file_path: filePath,
31500
- name: "",
31501
- role: "ref",
31502
- kind: "",
31503
- start_line: 0,
31504
- start_column: 0,
31505
- end_line: 0,
31506
- end_column: 0,
31507
- scope_start_line: null,
31508
- scope_start_column: null,
31509
- parent_scope_start_line: null,
31510
- parent_scope_start_column: null,
31511
- nav_target_path: null,
31512
- scope_chain: null
31513
- };
31514
- }
31515
-
31516
- // src/code-nav/symbol-index/storage/file-content-load.ts
31517
- function loadFileTags(db, ctx, filePath) {
31518
- const contentHash = getEffectiveContentHash(db, ctx, filePath);
31519
- if (contentHash == null) return [];
31520
- return db.all(
31521
- `SELECT ${TAG_SELECT}
31522
- FROM symbol_index_tag
31523
- WHERE content_hash = ? AND file_path = ?
31524
- ORDER BY start_line, start_column`,
31525
- [contentHash, filePath]
31526
- );
31527
- }
31528
- function loadFileImports(db, ctx, filePath) {
31529
- const contentHash = getEffectiveContentHash(db, ctx, filePath);
31530
- if (contentHash == null) return [];
31531
- return db.all(
31532
- `SELECT file_path, local_name, source_name, module_specifier, source_file_path, import_kind, start_line, start_column
31533
- FROM symbol_index_import
31534
- WHERE content_hash = ? AND file_path = ?
31535
- ORDER BY start_line, start_column`,
31536
- [contentHash, filePath]
31537
- );
31538
- }
31539
- function findTagsByName(db, ctx, name) {
31540
- const filePaths = listEffectiveIndexedFilePaths(db, ctx);
31541
- const rows = [];
31542
- for (const filePath of filePaths) {
31543
- const contentHash = getEffectiveContentHash(db, ctx, filePath);
31544
- if (contentHash == null) continue;
31545
- const matches = db.all(
31546
- `SELECT ${TAG_SELECT}
31547
- FROM symbol_index_tag
31548
- WHERE content_hash = ? AND file_path = ? AND name = ?
31549
- ORDER BY start_line, start_column`,
31550
- [contentHash, filePath, name]
31551
- );
31552
- rows.push(...matches);
31553
- }
31554
- rows.sort(
31555
- (a, b) => a.file_path.localeCompare(b.file_path) || a.start_line - b.start_line || a.start_column - b.start_column
31556
- );
31557
- return rows;
31558
- }
31559
-
31560
- // src/code-nav/symbol-index/lifecycle/symbol-index-file-ready.ts
31561
- function symbolIndexFileIsReady(parentPath, filePath) {
31562
- const normalizedParent = normalizeResolvedPath(parentPath);
31563
- const normalizedFile = filePath.replace(/\\/g, "/").replace(/^\/+/, "");
31564
- if (!normalizedFile) return false;
31565
- return withCodeNavCacheSqliteSync((db) => {
31566
- const ctx = getSymbolIndexParentContext(db, normalizedParent);
31567
- if (ctx == null) return false;
31568
- const versionRow = db.get("SELECT index_version FROM symbol_index_parent_path WHERE id = ?", [
31569
- ctx.parentId
31570
- ]);
31571
- if (versionRow == null || versionRow.index_version < SYMBOL_INDEX_VERSION) return false;
31572
- return getEffectiveContentHash(db, ctx, normalizedFile) != null;
31573
- });
31597
+ return remaining;
31574
31598
  }
31575
31599
 
31576
31600
  // src/code-nav/symbol-index/sqlite/symbol-index-sqlite-lock.ts
31577
- import fs19 from "node:fs";
31578
- function isSqliteCorruptError(e) {
31579
- const msg = e instanceof Error ? e.message : String(e);
31580
- return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
31581
- }
31582
- var chain = Promise.resolve();
31583
31601
  function withSymbolIndexSqliteLock(fn) {
31584
- const run = async () => {
31585
- try {
31586
- return await Promise.resolve(fn());
31587
- } catch (e) {
31588
- if (!isSqliteCorruptError(e)) throw e;
31589
- closeAllCodeNavCacheSqliteConnections();
31590
- try {
31591
- fs19.unlinkSync(getCodeNavCacheSqlitePath());
31592
- } catch {
31593
- }
31594
- chain = Promise.resolve();
31595
- return await Promise.resolve(fn());
31596
- }
31597
- };
31598
- const next = chain.then(run);
31599
- chain = next.then(
31600
- () => void 0,
31601
- () => void 0
31602
- );
31603
- return next;
31602
+ return withCodeNavCacheSqliteWorkLock(fn);
31604
31603
  }
31605
31604
 
31606
31605
  // src/code-nav/symbol-index/imports/star-reexport-names.ts
@@ -31705,43 +31704,111 @@ function expandStarReexportBindings(collected) {
31705
31704
  }
31706
31705
 
31707
31706
  // src/code-nav/symbol-index/persistence/persist-single-file.ts
31708
- function persistSymbolIndexFile(resolved, relPath, source, tags, imports) {
31709
- withCodeNavCacheSqliteSync((db) => {
31710
- db.run("BEGIN IMMEDIATE");
31711
- try {
31707
+ async function persistSymbolIndexFileAsync(resolved, relPath, source, tags, imports) {
31708
+ await withCodeNavCacheSqlite(async (db) => {
31709
+ runCodeNavCacheWriteTransaction(db, () => {
31712
31710
  upsertSymbolIndexParentPath(db, resolved);
31713
31711
  const ctx = getSymbolIndexParentContext(db, resolved);
31714
31712
  if (ctx == null) throw new Error(`Failed to resolve symbol index parent: ${resolved}`);
31715
31713
  persistSymbolIndexFileData(db, ctx, relPath, source, tags, imports);
31716
- db.run("COMMIT");
31717
- } catch (e) {
31718
- try {
31719
- db.run("ROLLBACK");
31720
- } catch {
31721
- }
31722
- throw e;
31723
- }
31714
+ });
31724
31715
  });
31725
31716
  }
31726
- function removeSymbolIndexFile(resolved, relPath) {
31727
- withCodeNavCacheSqliteSync((db) => {
31728
- db.run("BEGIN IMMEDIATE");
31729
- try {
31717
+ async function removeSymbolIndexFileAsync(resolved, relPath) {
31718
+ await withCodeNavCacheSqlite(async (db) => {
31719
+ runCodeNavCacheWriteTransaction(db, () => {
31730
31720
  upsertSymbolIndexParentPath(db, resolved);
31731
31721
  const ctx = getSymbolIndexParentContext(db, resolved);
31732
31722
  if (ctx == null) throw new Error(`Failed to resolve symbol index parent: ${resolved}`);
31733
31723
  removeSymbolIndexFileData(db, ctx, relPath);
31734
- db.run("COMMIT");
31735
- } catch (e) {
31736
- try {
31737
- db.run("ROLLBACK");
31738
- } catch {
31739
- }
31740
- throw e;
31741
- }
31724
+ });
31742
31725
  });
31743
31726
  }
31744
31727
 
31728
+ // src/code-nav/symbol-index/query/tag-query-shared.ts
31729
+ var TAG_SELECT = `file_path, name, role, kind, start_line, start_column, end_line, end_column,
31730
+ scope_start_line, scope_start_column, parent_scope_start_line, parent_scope_start_column, nav_target_path, scope_chain`;
31731
+ function rowToLocation(row) {
31732
+ return {
31733
+ path: row.file_path,
31734
+ line: row.start_line + 1,
31735
+ column: row.start_column,
31736
+ kind: row.kind,
31737
+ role: row.role === "def" ? "definition" : "reference"
31738
+ };
31739
+ }
31740
+ function modulePathLocation(row) {
31741
+ return {
31742
+ path: row.nav_target_path,
31743
+ line: 1,
31744
+ column: 0,
31745
+ kind: "module",
31746
+ role: "reference"
31747
+ };
31748
+ }
31749
+ function emptyRefRow(filePath) {
31750
+ return {
31751
+ file_path: filePath,
31752
+ name: "",
31753
+ role: "ref",
31754
+ kind: "",
31755
+ start_line: 0,
31756
+ start_column: 0,
31757
+ end_line: 0,
31758
+ end_column: 0,
31759
+ scope_start_line: null,
31760
+ scope_start_column: null,
31761
+ parent_scope_start_line: null,
31762
+ parent_scope_start_column: null,
31763
+ nav_target_path: null,
31764
+ scope_chain: null
31765
+ };
31766
+ }
31767
+
31768
+ // src/code-nav/symbol-index/storage/file-content-load.ts
31769
+ function loadFileTags(db, ctx, filePath) {
31770
+ const contentHash = getEffectiveContentHash(db, ctx, filePath);
31771
+ if (contentHash == null) return [];
31772
+ return db.all(
31773
+ `SELECT ${TAG_SELECT}
31774
+ FROM symbol_index_tag
31775
+ WHERE content_hash = ? AND file_path = ?
31776
+ ORDER BY start_line, start_column`,
31777
+ [contentHash, filePath]
31778
+ );
31779
+ }
31780
+ function loadFileImports(db, ctx, filePath) {
31781
+ const contentHash = getEffectiveContentHash(db, ctx, filePath);
31782
+ if (contentHash == null) return [];
31783
+ return db.all(
31784
+ `SELECT file_path, local_name, source_name, module_specifier, source_file_path, import_kind, start_line, start_column
31785
+ FROM symbol_index_import
31786
+ WHERE content_hash = ? AND file_path = ?
31787
+ ORDER BY start_line, start_column`,
31788
+ [contentHash, filePath]
31789
+ );
31790
+ }
31791
+ function findTagsByName(db, ctx, name) {
31792
+ const filePaths = listEffectiveIndexedFilePaths(db, ctx);
31793
+ const rows = [];
31794
+ for (const filePath of filePaths) {
31795
+ const contentHash = getEffectiveContentHash(db, ctx, filePath);
31796
+ if (contentHash == null) continue;
31797
+ const matches = db.all(
31798
+ `SELECT ${TAG_SELECT}
31799
+ FROM symbol_index_tag
31800
+ WHERE content_hash = ? AND file_path = ? AND name = ?
31801
+ ORDER BY start_line, start_column`,
31802
+ [contentHash, filePath, name]
31803
+ );
31804
+ rows.push(...matches);
31805
+ }
31806
+ rows.sort(
31807
+ (a, b) => a.file_path.localeCompare(b.file_path) || a.start_line - b.start_line || a.start_column - b.start_column
31808
+ );
31809
+ return rows;
31810
+ }
31811
+
31745
31812
  // src/code-nav/symbol-index/build/load-indexed-file-entry.ts
31746
31813
  function dbTagToSymbolTag(row) {
31747
31814
  return {
@@ -31787,21 +31854,52 @@ function loadIndexedFileEntry(db, ctx, relPath) {
31787
31854
  function normalizeRelPath2(filePath) {
31788
31855
  return filePath.replace(/\\/g, "/").replace(/^\/+/, "");
31789
31856
  }
31790
- function listDirectImportTargetsForFile(parentPath, filePath) {
31857
+ function listDirectImportTargetsWithDb(db, resolved, relPath) {
31858
+ const ctx = getSymbolIndexParentContext(db, resolved);
31859
+ if (ctx == null) return [];
31860
+ const entry = loadIndexedFileEntry(db, ctx, relPath);
31861
+ if (entry == null) return [];
31862
+ const paths = /* @__PURE__ */ new Set();
31863
+ for (const binding of entry.imports) {
31864
+ if (binding.sourceFilePath) paths.add(binding.sourceFilePath);
31865
+ }
31866
+ return Array.from(paths);
31867
+ }
31868
+ async function listDirectImportTargetsForFileAsync(parentPath, filePath) {
31791
31869
  const resolved = normalizeResolvedPath(parentPath);
31792
31870
  const relPath = normalizeRelPath2(filePath);
31793
31871
  if (!relPath) return [];
31794
- return withCodeNavCacheSqliteSync((db) => {
31795
- const ctx = getSymbolIndexParentContext(db, resolved);
31872
+ return withCodeNavCacheSqlite((db) => listDirectImportTargetsWithDb(db, resolved, relPath));
31873
+ }
31874
+
31875
+ // src/code-nav/symbol-index/lifecycle/symbol-index-file-ready.ts
31876
+ function symbolIndexFileIsReadyWithDb(db, parentPath, filePath) {
31877
+ const normalizedParent = normalizeResolvedPath(parentPath);
31878
+ const normalizedFile = filePath.replace(/\\/g, "/").replace(/^\/+/, "");
31879
+ if (!normalizedFile) return false;
31880
+ const ctx = getSymbolIndexParentContext(db, normalizedParent);
31881
+ if (ctx == null) return false;
31882
+ const versionRow = db.get("SELECT index_version FROM symbol_index_parent_path WHERE id = ?", [
31883
+ ctx.parentId
31884
+ ]);
31885
+ if (versionRow == null || versionRow.index_version < SYMBOL_INDEX_VERSION) return false;
31886
+ return getEffectiveContentHash(db, ctx, normalizedFile) != null;
31887
+ }
31888
+ async function symbolIndexFileIsReadyAsync(parentPath, filePath) {
31889
+ return withCodeNavCacheSqlite((db) => symbolIndexFileIsReadyWithDb(db, parentPath, filePath));
31890
+ }
31891
+ async function loadSymbolIndexReadyFileSet(parentPath) {
31892
+ const normalizedParent = normalizeResolvedPath(parentPath);
31893
+ const paths = await withCodeNavCacheSqlite((db) => {
31894
+ const ctx = getSymbolIndexParentContext(db, normalizedParent);
31796
31895
  if (ctx == null) return [];
31797
- const entry = loadIndexedFileEntry(db, ctx, relPath);
31798
- if (entry == null) return [];
31799
- const paths = /* @__PURE__ */ new Set();
31800
- for (const binding of entry.imports) {
31801
- if (binding.sourceFilePath) paths.add(binding.sourceFilePath);
31802
- }
31803
- return [...paths];
31896
+ const versionRow = db.get("SELECT index_version FROM symbol_index_parent_path WHERE id = ?", [
31897
+ ctx.parentId
31898
+ ]);
31899
+ if (versionRow == null || versionRow.index_version < SYMBOL_INDEX_VERSION) return [];
31900
+ return listEffectiveIndexedFilePaths(db, ctx);
31804
31901
  });
31902
+ return new Set(paths);
31805
31903
  }
31806
31904
 
31807
31905
  // src/code-nav/symbol-index/build/extract-file-symbol-data.ts
@@ -32111,9 +32209,9 @@ async function augmentTypeReferenceTags(tags, nameNodes, root) {
32111
32209
  }
32112
32210
 
32113
32211
  // src/code-nav/scope-chain.ts
32114
- function serializeScopeChain(chain3) {
32115
- if (chain3.length === 0) return null;
32116
- return chain3.map((anchor) => `${anchor.startLine}:${anchor.startColumn}`).join(",");
32212
+ function serializeScopeChain(chain2) {
32213
+ if (chain2.length === 0) return null;
32214
+ return chain2.map((anchor) => `${anchor.startLine}:${anchor.startColumn}`).join(",");
32117
32215
  }
32118
32216
  function parseScopeChain(raw) {
32119
32217
  if (!raw) return [];
@@ -32129,9 +32227,9 @@ function parseScopeChain(raw) {
32129
32227
  }
32130
32228
  return out;
32131
32229
  }
32132
- function anchorInScopeChain(line, column, chain3) {
32133
- if (line == null || column == null) return chain3.length === 0;
32134
- return chain3.some((anchor) => anchor.startLine === line && anchor.startColumn === column);
32230
+ function anchorInScopeChain(line, column, chain2) {
32231
+ if (line == null || column == null) return chain2.length === 0;
32232
+ return chain2.some((anchor) => anchor.startLine === line && anchor.startColumn === column);
32135
32233
  }
32136
32234
 
32137
32235
  // src/code-nav/enrich-tag-scope.ts
@@ -32168,15 +32266,15 @@ function scopeAnchorFromNode(node) {
32168
32266
  };
32169
32267
  }
32170
32268
  function findEnclosingScopeChain(nameNode) {
32171
- const chain3 = [];
32269
+ const chain2 = [];
32172
32270
  let current = nameNode.parent;
32173
32271
  while (current) {
32174
32272
  if (SCOPE_CONTAINER_TYPES.has(current.type)) {
32175
- chain3.push(scopeAnchorFromNode(current));
32273
+ chain2.push(scopeAnchorFromNode(current));
32176
32274
  }
32177
32275
  current = current.parent;
32178
32276
  }
32179
- return chain3;
32277
+ return chain2;
32180
32278
  }
32181
32279
  function findEnclosingScopes(nameNode) {
32182
32280
  const scopeChain = findEnclosingScopeChain(nameNode);
@@ -33033,7 +33131,7 @@ async function extractFileSymbolData(resolved, relPath, workspaceFilesOverride)
33033
33131
  }
33034
33132
  }
33035
33133
  await yieldToEventLoop();
33036
- const workspaceFiles = workspaceFilesOverride ?? withCodeNavCacheSqliteSync((db) => listWorkspaceFiles(db, resolved, relPath));
33134
+ const workspaceFiles = workspaceFilesOverride ?? await withCodeNavCacheSqlite((db) => listWorkspaceFiles(db, resolved, relPath));
33037
33135
  const imports = source != null ? await extractImportBindingsFromSource(relPath, source, workspaceFiles) : [];
33038
33136
  const yieldState = createCodeNavYieldState();
33039
33137
  return {
@@ -33058,7 +33156,7 @@ async function indexOneSymbolFileForBuild(resolved, relPath, workspaceFiles) {
33058
33156
  };
33059
33157
  if (data.imports.some((binding) => binding.importKind === "star")) {
33060
33158
  await yieldToEventLoop();
33061
- entry = withCodeNavCacheSqliteSync((db) => {
33159
+ entry = await withCodeNavCacheSqlite((db) => {
33062
33160
  const ctx = getSymbolIndexParentContext(db, resolved);
33063
33161
  if (ctx == null) return entry;
33064
33162
  const resolveEntry = (filePath) => loadIndexedFileEntry(db, ctx, filePath);
@@ -33068,7 +33166,7 @@ async function indexOneSymbolFileForBuild(resolved, relPath, workspaceFiles) {
33068
33166
  }
33069
33167
  await withSymbolIndexSqliteLock(async () => {
33070
33168
  await yieldToEventLoop();
33071
- persistSymbolIndexFile(resolved, relPath, entry.source, entry.tags, entry.imports);
33169
+ await persistSymbolIndexFileAsync(resolved, relPath, entry.source, entry.tags, entry.imports);
33072
33170
  });
33073
33171
  }
33074
33172
  async function ensureSymbolIndexFileAsync(parentPath, filePath, opts) {
@@ -33076,41 +33174,51 @@ async function ensureSymbolIndexFileAsync(parentPath, filePath, opts) {
33076
33174
  const relPath = normalizeRelPath3(filePath);
33077
33175
  if (!relPath) return false;
33078
33176
  const importDepth = opts?.importDepth ?? 0;
33079
- withCodeNavCacheSqliteSync((db) => {
33177
+ await withCodeNavCacheSqlite((db) => {
33080
33178
  upsertSymbolIndexParentPath(db, resolved);
33081
33179
  });
33082
- if (!symbolIndexFileIsReady(resolved, relPath)) {
33180
+ if (!await symbolIndexFileIsReadyAsync(resolved, relPath)) {
33083
33181
  await indexOneSymbolFileForBuild(resolved, relPath, void 0);
33084
33182
  }
33085
- if (importDepth > 0 && symbolIndexFileIsReady(resolved, relPath)) {
33086
- const targets = listDirectImportTargetsForFile(resolved, relPath).slice(0, MAX_IMPORT_PREFETCH_COUNT);
33183
+ if (importDepth > 0 && await symbolIndexFileIsReadyAsync(resolved, relPath)) {
33184
+ const targets = (await listDirectImportTargetsForFileAsync(resolved, relPath)).slice(
33185
+ 0,
33186
+ MAX_IMPORT_PREFETCH_COUNT
33187
+ );
33087
33188
  for (const target of targets) {
33088
33189
  await yieldToEventLoop();
33089
- if (!symbolIndexFileIsReady(resolved, target)) {
33190
+ if (!await symbolIndexFileIsReadyAsync(resolved, target)) {
33090
33191
  await ensureSymbolIndexFileAsync(resolved, target, { importDepth: importDepth - 1 });
33091
33192
  }
33092
33193
  }
33093
33194
  }
33094
- return symbolIndexFileIsReady(resolved, relPath);
33195
+ return await symbolIndexFileIsReadyAsync(resolved, relPath);
33095
33196
  }
33096
- function registerSymbolIndexParent(parentPath) {
33197
+ async function registerSymbolIndexParentAsync(parentPath) {
33097
33198
  const resolved = normalizeResolvedPath(parentPath);
33098
- withCodeNavCacheSqliteSync((db) => {
33199
+ await withCodeNavCacheSqlite((db) => {
33099
33200
  upsertSymbolIndexParentPath(db, resolved);
33100
33201
  });
33101
33202
  }
33102
33203
 
33103
33204
  // src/code-nav/symbol-index/build/build-symbol-index.ts
33205
+ var SYMBOL_PARENT_CLEAR_BATCH_SIZE = 256;
33104
33206
  async function buildSymbolIndexAsync(parentPath) {
33105
33207
  const resolved = normalizeResolvedPath(parentPath);
33106
33208
  await yieldToEventLoop();
33107
- await withSymbolIndexSqliteLock(() => {
33108
- withCodeNavCacheSqliteSync((db) => {
33109
- upsertSymbolIndexParentPath(db, resolved);
33110
- const ctx = getSymbolIndexParentContext(db, resolved);
33111
- if (ctx != null) clearSymbolIndexParentData(db, ctx);
33209
+ let clearRemaining = true;
33210
+ while (clearRemaining) {
33211
+ await withSymbolIndexSqliteLock(async () => {
33212
+ clearRemaining = await withCodeNavCacheSqlite(
33213
+ (db) => runCodeNavCacheWriteTransaction(db, () => {
33214
+ upsertSymbolIndexParentPath(db, resolved);
33215
+ const ctx = getSymbolIndexParentContext(db, resolved);
33216
+ return ctx == null ? false : clearSymbolIndexParentDataBatch(db, ctx, SYMBOL_PARENT_CLEAR_BATCH_SIZE);
33217
+ })
33218
+ );
33112
33219
  });
33113
- });
33220
+ if (clearRemaining) await yieldToEventLoop();
33221
+ }
33114
33222
  const workspaceFiles = /* @__PURE__ */ new Set();
33115
33223
  const walkState = createWalkYieldState();
33116
33224
  const yieldState = createCodeNavYieldState();
@@ -33127,9 +33235,7 @@ async function buildSymbolIndexAsync(parentPath) {
33127
33235
  resolved,
33128
33236
  async (relPath) => {
33129
33237
  await yieldDuringCodeNavWork(yieldState);
33130
- if (!symbolIndexFileIsReady(resolved, relPath)) {
33131
- await indexOneSymbolFileForBuild(resolved, relPath, workspaceFiles);
33132
- }
33238
+ await indexOneSymbolFileForBuild(resolved, relPath, workspaceFiles);
33133
33239
  fileCount += 1;
33134
33240
  },
33135
33241
  walkState
@@ -33138,18 +33244,79 @@ async function buildSymbolIndexAsync(parentPath) {
33138
33244
  return { fileCount, tagCount: 0 };
33139
33245
  }
33140
33246
 
33247
+ // src/code-nav/symbol-index/scheduler/task-priority-heap.ts
33248
+ function higherPriority(a, b) {
33249
+ return a.priority > b.priority;
33250
+ }
33251
+ var SymbolIndexTaskPriorityHeap = class {
33252
+ heap = [];
33253
+ clear() {
33254
+ this.heap.length = 0;
33255
+ }
33256
+ push(task) {
33257
+ this.heap.push(task);
33258
+ let i = this.heap.length - 1;
33259
+ while (i > 0) {
33260
+ const parent = Math.floor((i - 1) / 2);
33261
+ if (!higherPriority(this.heap[i], this.heap[parent])) break;
33262
+ this.swap(i, parent);
33263
+ i = parent;
33264
+ }
33265
+ }
33266
+ pop() {
33267
+ if (this.heap.length === 0) return void 0;
33268
+ const top = this.heap[0];
33269
+ const last2 = this.heap.pop();
33270
+ if (this.heap.length > 0) {
33271
+ this.heap[0] = last2;
33272
+ this.sink(0);
33273
+ }
33274
+ return top;
33275
+ }
33276
+ get size() {
33277
+ return this.heap.length;
33278
+ }
33279
+ sink(start) {
33280
+ let i = start;
33281
+ while (true) {
33282
+ const left = i * 2 + 1;
33283
+ const right = left + 1;
33284
+ let best = i;
33285
+ if (left < this.heap.length && higherPriority(this.heap[left], this.heap[best])) best = left;
33286
+ if (right < this.heap.length && higherPriority(this.heap[right], this.heap[best])) best = right;
33287
+ if (best === i) break;
33288
+ this.swap(i, best);
33289
+ i = best;
33290
+ }
33291
+ }
33292
+ swap(i, j) {
33293
+ const tmp = this.heap[i];
33294
+ this.heap[i] = this.heap[j];
33295
+ this.heap[j] = tmp;
33296
+ }
33297
+ };
33298
+
33299
+ // src/code-nav/symbol-index/scheduler/task-queue-types.ts
33300
+ function symbolIndexTaskKey(parentPath, filePath) {
33301
+ return `${parentPath}\0${filePath}`;
33302
+ }
33303
+
33141
33304
  // src/code-nav/symbol-index/scheduler/task-queue.ts
33142
33305
  var pending = /* @__PURE__ */ new Map();
33306
+ var heap = new SymbolIndexTaskPriorityHeap();
33307
+ var latestActiveSequenceByParent = /* @__PURE__ */ new Map();
33143
33308
  var workerRunning = false;
33144
- function taskKey(parentPath, filePath) {
33145
- return `${parentPath}\0${filePath}`;
33309
+ function taskMatchesCurrentPending(task) {
33310
+ const current = pending.get(symbolIndexTaskKey(task.parentPath, task.filePath));
33311
+ return current === task;
33146
33312
  }
33147
33313
  function pickNextTask() {
33148
- let best;
33149
- for (const task of pending.values()) {
33150
- if (best == null || task.priority > best.priority) best = task;
33314
+ while (heap.size > 0) {
33315
+ const task = heap.pop();
33316
+ if (task == null || !taskMatchesCurrentPending(task)) continue;
33317
+ return task;
33151
33318
  }
33152
- return best;
33319
+ return void 0;
33153
33320
  }
33154
33321
  async function runWorker() {
33155
33322
  if (workerRunning) return;
@@ -33159,74 +33326,93 @@ async function runWorker() {
33159
33326
  while (pending.size > 0) {
33160
33327
  const task = pickNextTask();
33161
33328
  if (task == null) break;
33162
- pending.delete(taskKey(task.parentPath, task.filePath));
33329
+ pending.delete(symbolIndexTaskKey(task.parentPath, task.filePath));
33163
33330
  await yieldDuringCodeNavWork(yieldState);
33164
- if (!symbolIndexFileIsReady(task.parentPath, task.filePath)) {
33331
+ const latestActiveSequence = latestActiveSequenceByParent.get(task.parentPath);
33332
+ if (latestActiveSequence != null && task.sequence != null && task.sequence < latestActiveSequence) {
33333
+ continue;
33334
+ }
33335
+ if (!await symbolIndexFileIsReadyAsync(task.parentPath, task.filePath)) {
33165
33336
  await ensureSymbolIndexFileAsync(task.parentPath, task.filePath);
33166
33337
  }
33167
33338
  await yieldToEventLoop();
33168
33339
  }
33169
33340
  } finally {
33341
+ if (pending.size === 0) heap.clear();
33170
33342
  workerRunning = false;
33171
33343
  if (pending.size > 0) void runWorker();
33172
33344
  }
33173
33345
  }
33174
- function enqueueSymbolIndexTask(parentPath, filePath, priority) {
33346
+ function enqueueSymbolIndexTask(parentPath, filePath, priority, sequence) {
33175
33347
  const normalizedParent = normalizeResolvedPath(parentPath);
33176
33348
  const normalizedFile = filePath.replace(/\\/g, "/").replace(/^\/+/, "");
33177
33349
  if (!normalizedFile) return;
33178
- if (symbolIndexFileIsReady(normalizedParent, normalizedFile)) return;
33179
- const key = taskKey(normalizedParent, normalizedFile);
33350
+ const key = symbolIndexTaskKey(normalizedParent, normalizedFile);
33180
33351
  const existing = pending.get(key);
33181
33352
  if (existing != null && existing.priority >= priority) return;
33182
- pending.set(key, { parentPath: normalizedParent, filePath: normalizedFile, priority });
33353
+ const task = { parentPath: normalizedParent, filePath: normalizedFile, priority, sequence };
33354
+ pending.set(key, task);
33355
+ heap.push(task);
33183
33356
  void runWorker();
33184
33357
  }
33358
+ function markLatestActiveSymbolIndexTask(parentPath, sequence) {
33359
+ const normalizedParent = normalizeResolvedPath(parentPath);
33360
+ const existing = latestActiveSequenceByParent.get(normalizedParent);
33361
+ if (existing != null && existing >= sequence) return;
33362
+ latestActiveSequenceByParent.set(normalizedParent, sequence);
33363
+ for (const [key, task] of pending) {
33364
+ if (task.parentPath === normalizedParent && task.sequence != null && task.sequence < sequence) {
33365
+ pending.delete(key);
33366
+ }
33367
+ }
33368
+ if (pending.size === 0) heap.clear();
33369
+ }
33185
33370
 
33186
33371
  // src/code-nav/symbol-index/scheduler/symbol-index-scheduler.ts
33187
33372
  var requestSeq = 0;
33188
33373
  var warmupSeq = 0;
33189
33374
  var warmupScheduled = /* @__PURE__ */ new Set();
33375
+ var warmupCompleted = /* @__PURE__ */ new Set();
33190
33376
  function scheduleSymbolIndexFile(parentPath, filePath) {
33191
- enqueueSymbolIndexTask(parentPath, filePath, 1e6 + ++requestSeq);
33377
+ const sequence = ++requestSeq;
33378
+ markLatestActiveSymbolIndexTask(parentPath, sequence);
33379
+ enqueueSymbolIndexTask(parentPath, filePath, 1e6 + sequence, sequence);
33192
33380
  }
33193
33381
  function scheduleSymbolIndexImportNeighbors(parentPath, filePaths) {
33194
33382
  let priority = 5e5 + requestSeq;
33195
33383
  for (const filePath of filePaths) {
33196
- enqueueSymbolIndexTask(parentPath, filePath, priority);
33384
+ enqueueSymbolIndexTask(parentPath, filePath, priority, requestSeq);
33197
33385
  priority -= 1;
33198
33386
  }
33199
33387
  }
33200
33388
  function scheduleSymbolIndexWarmup(parentPath) {
33201
33389
  const resolved = normalizeResolvedPath(parentPath);
33390
+ if (warmupCompleted.has(resolved)) return;
33202
33391
  if (warmupScheduled.has(resolved)) return;
33203
33392
  warmupScheduled.add(resolved);
33204
33393
  setImmediate(() => {
33205
33394
  void (async () => {
33206
33395
  try {
33396
+ const readyFiles = await loadSymbolIndexReadyFileSet(resolved);
33207
33397
  const walkState = createWalkYieldState();
33208
33398
  const yieldState = createCodeNavYieldState();
33209
33399
  await walkCodeNavFilesAsync(
33210
33400
  resolved,
33211
33401
  async (relPath) => {
33212
33402
  await yieldDuringCodeNavWork(yieldState);
33213
- if (!symbolIndexFileIsReady(resolved, relPath)) {
33214
- enqueueSymbolIndexTask(resolved, relPath, ++warmupSeq);
33403
+ if (!readyFiles.has(relPath)) {
33404
+ enqueueSymbolIndexTask(resolved, relPath, ++warmupSeq, requestSeq);
33215
33405
  }
33216
33406
  },
33217
33407
  walkState
33218
33408
  );
33409
+ warmupCompleted.add(resolved);
33219
33410
  } finally {
33220
33411
  warmupScheduled.delete(resolved);
33221
33412
  }
33222
33413
  })();
33223
33414
  });
33224
33415
  }
33225
- function scheduleSymbolIndexForRequest(parentPath, reqPath, importTargets) {
33226
- scheduleSymbolIndexFile(parentPath, reqPath);
33227
- scheduleSymbolIndexImportNeighbors(parentPath, importTargets);
33228
- scheduleSymbolIndexWarmup(parentPath);
33229
- }
33230
33416
 
33231
33417
  // src/code-nav/symbol-index/lifecycle/normalize-symbol-index-path.ts
33232
33418
  import path24 from "node:path";
@@ -33242,27 +33428,23 @@ function normalizeSymbolIndexRelPath(parentPath, changedPath) {
33242
33428
  async function updateSymbolIndexForFile(parentPath, changedPath) {
33243
33429
  const relPath = normalizeSymbolIndexRelPath(parentPath, changedPath);
33244
33430
  if (relPath == null) return;
33431
+ const resolved = normalizeResolvedPath(parentPath);
33432
+ const { source, tags, imports } = await extractFileSymbolData(resolved, relPath);
33433
+ await yieldToEventLoop();
33245
33434
  await withSymbolIndexSqliteLock(async () => {
33246
- const resolved = normalizeResolvedPath(parentPath);
33247
- const { source, tags, imports } = await extractFileSymbolData(resolved, relPath);
33248
- await yieldToEventLoop();
33249
- persistSymbolIndexFile(resolved, relPath, source, tags, imports);
33435
+ await persistSymbolIndexFileAsync(resolved, relPath, source, tags, imports);
33250
33436
  });
33251
33437
  }
33252
33438
  async function removeSymbolIndexForFile(parentPath, changedPath) {
33253
33439
  const relPath = normalizeSymbolIndexRelPath(parentPath, changedPath);
33254
33440
  if (relPath == null) return;
33441
+ const resolved = normalizeResolvedPath(parentPath);
33255
33442
  await withSymbolIndexSqliteLock(async () => {
33256
- removeSymbolIndexFile(normalizeResolvedPath(parentPath), relPath);
33443
+ await removeSymbolIndexFileAsync(resolved, relPath);
33257
33444
  });
33258
33445
  }
33259
33446
 
33260
33447
  // src/code-nav/symbol-index/sqlite/symbol-index-import-sqlite.ts
33261
- function listImportsForFile(db, parentId, filePath) {
33262
- const ctx = getSymbolIndexParentContextById(db, parentId);
33263
- if (ctx == null) return [];
33264
- return loadFileImports(db, ctx, filePath);
33265
- }
33266
33448
  function prefersTypeImport(refKind) {
33267
33449
  return refKind === "type";
33268
33450
  }
@@ -33288,7 +33470,7 @@ function findImportBindingForLocalName(imports, localName, ref) {
33288
33470
  function defsByFileNameKey(filePath, name) {
33289
33471
  return `${filePath}\0${name}`;
33290
33472
  }
33291
- function createNavigationLookups(db, parentId) {
33473
+ function createSnapshotNavigationLookups(db, parentId) {
33292
33474
  const ctx = getSymbolIndexParentContextById(db, parentId);
33293
33475
  if (ctx == null) {
33294
33476
  return {
@@ -33300,29 +33482,24 @@ function createNavigationLookups(db, parentId) {
33300
33482
  const fileDefsCache = /* @__PURE__ */ new Map();
33301
33483
  const defsByFileNameCache = /* @__PURE__ */ new Map();
33302
33484
  const importsCache = /* @__PURE__ */ new Map();
33303
- return {
33304
- loadFileDefs(filePath) {
33305
- let cached2 = fileDefsCache.get(filePath);
33306
- if (cached2) return cached2;
33307
- cached2 = loadFileTags(db, ctx, filePath).filter((row) => row.role === "def");
33308
- fileDefsCache.set(filePath, cached2);
33309
- return cached2;
33310
- },
33311
- lookupDefsInFileNamed(filePath, name) {
33312
- const key = defsByFileNameKey(filePath, name);
33313
- let cached2 = defsByFileNameCache.get(key);
33314
- if (cached2) return cached2;
33315
- cached2 = loadFileTags(db, ctx, filePath).filter((row) => row.role === "def" && row.name === name);
33316
- defsByFileNameCache.set(key, cached2);
33317
- return cached2;
33318
- },
33319
- lookupImportsForFile(filePath) {
33320
- let cached2 = importsCache.get(filePath);
33321
- if (cached2) return cached2;
33322
- cached2 = listImportsForFile(db, parentId, filePath);
33323
- importsCache.set(filePath, cached2);
33324
- return cached2;
33485
+ for (const filePath of listEffectiveIndexedFilePaths(db, ctx)) {
33486
+ const defs = loadFileTags(db, ctx, filePath).filter((row) => row.role === "def");
33487
+ fileDefsCache.set(filePath, defs);
33488
+ for (const def of defs) {
33489
+ const key = defsByFileNameKey(filePath, def.name);
33490
+ const existing = defsByFileNameCache.get(key);
33491
+ if (existing) {
33492
+ existing.push(def);
33493
+ } else {
33494
+ defsByFileNameCache.set(key, [def]);
33495
+ }
33325
33496
  }
33497
+ importsCache.set(filePath, loadFileImports(db, ctx, filePath));
33498
+ }
33499
+ return {
33500
+ loadFileDefs: (filePath) => fileDefsCache.get(filePath) ?? [],
33501
+ lookupDefsInFileNamed: (filePath, name) => defsByFileNameCache.get(defsByFileNameKey(filePath, name)) ?? [],
33502
+ lookupImportsForFile: (filePath) => importsCache.get(filePath) ?? []
33326
33503
  };
33327
33504
  }
33328
33505
 
@@ -33331,11 +33508,11 @@ function refScopeChain(ref) {
33331
33508
  const parsed = parseScopeChain(ref.scope_chain);
33332
33509
  if (parsed.length > 0) return parsed;
33333
33510
  if (ref.scope_start_line != null && ref.scope_start_column != null) {
33334
- const chain3 = [{ startLine: ref.scope_start_line, startColumn: ref.scope_start_column }];
33511
+ const chain2 = [{ startLine: ref.scope_start_line, startColumn: ref.scope_start_column }];
33335
33512
  if (ref.parent_scope_start_line != null && ref.parent_scope_start_column != null) {
33336
- chain3.push({ startLine: ref.parent_scope_start_line, startColumn: ref.parent_scope_start_column });
33513
+ chain2.push({ startLine: ref.parent_scope_start_line, startColumn: ref.parent_scope_start_column });
33337
33514
  }
33338
- return chain3;
33515
+ return chain2;
33339
33516
  }
33340
33517
  return [];
33341
33518
  }
@@ -33346,17 +33523,17 @@ function defAtScopeAnchor(def, anchor) {
33346
33523
  return scopesMatch(def.scope_start_line, def.scope_start_column, anchor.startLine, anchor.startColumn) || scopesMatch(def.start_line, def.start_column, anchor.startLine, anchor.startColumn);
33347
33524
  }
33348
33525
  function defVisibleInRefScopeChain(def, ref) {
33349
- const chain3 = refScopeChain(ref);
33350
- if (chain3.length === 0) return isModuleLevelDef2(def);
33351
- return anchorInScopeChain(def.scope_start_line, def.scope_start_column, chain3) || anchorInScopeChain(def.start_line, def.start_column, chain3.slice(1));
33526
+ const chain2 = refScopeChain(ref);
33527
+ if (chain2.length === 0) return isModuleLevelDef2(def);
33528
+ return anchorInScopeChain(def.scope_start_line, def.scope_start_column, chain2) || anchorInScopeChain(def.start_line, def.start_column, chain2.slice(1));
33352
33529
  }
33353
33530
  function scopeDepthInChain(def, ref) {
33354
- const chain3 = refScopeChain(ref);
33355
- for (let index = 0; index < chain3.length; index++) {
33356
- const anchor = chain3[index];
33531
+ const chain2 = refScopeChain(ref);
33532
+ for (let index = 0; index < chain2.length; index++) {
33533
+ const anchor = chain2[index];
33357
33534
  if (defAtScopeAnchor(def, anchor)) return index;
33358
33535
  }
33359
- return chain3.length;
33536
+ return chain2.length;
33360
33537
  }
33361
33538
 
33362
33539
  // src/code-nav/resolution/navigation/confidence.ts
@@ -33522,9 +33699,9 @@ function resolveScopeLevelCandidates(defs) {
33522
33699
  function resolveLocalDefinitions(ref, fileDefs) {
33523
33700
  const named = fileDefs.filter((def) => def.name === ref.name && declaredBeforeRef(def, ref));
33524
33701
  if (named.length === 0) return [];
33525
- const chain3 = refScopeChain(ref);
33526
- if (chain3.length > 0) {
33527
- for (const anchor of chain3) {
33702
+ const chain2 = refScopeChain(ref);
33703
+ if (chain2.length > 0) {
33704
+ for (const anchor of chain2) {
33528
33705
  const atLevel = named.filter((def) => defAtScopeAnchor(def, anchor));
33529
33706
  if (atLevel.length > 0) return resolveScopeLevelCandidates(atLevel);
33530
33707
  }
@@ -33539,10 +33716,10 @@ function resolveLocalDefinitions(ref, fileDefs) {
33539
33716
  function resolveLocalDefinitionCandidates(ref, fileDefs) {
33540
33717
  const named = fileDefs.filter((def) => def.name === ref.name && declaredBeforeRef(def, ref));
33541
33718
  if (named.length === 0) return [];
33542
- const chain3 = refScopeChain(ref);
33543
- if (chain3.length === 0) return resolveScopeLevelCandidates(named.filter((def) => isModuleLevelDef2(def)));
33719
+ const chain2 = refScopeChain(ref);
33720
+ if (chain2.length === 0) return resolveScopeLevelCandidates(named.filter((def) => isModuleLevelDef2(def)));
33544
33721
  const visible = named.filter((def) => {
33545
- if (chain3.some((anchor) => defAtScopeAnchor(def, anchor))) return true;
33722
+ if (chain2.some((anchor) => defAtScopeAnchor(def, anchor))) return true;
33546
33723
  if (defVisibleInRefScopeChain(def, ref)) return true;
33547
33724
  return isModuleLevelDef2(def);
33548
33725
  });
@@ -33723,32 +33900,41 @@ function pickPrimaryTagAtPosition(rows, filePath, line, column) {
33723
33900
  async function queryDefinitionsNavigation(parentPath, filePath, line, column) {
33724
33901
  await yieldToEventLoop();
33725
33902
  const resolved = normalizeResolvedPath(parentPath);
33726
- const result = withCodeNavCacheSqliteSync((db) => {
33903
+ const snapshot = await withCodeNavCacheSqlite((db) => {
33727
33904
  const ctx = getSymbolIndexParentContext(db, resolved);
33728
33905
  if (ctx == null) return null;
33729
33906
  const fileRows = loadFileTags(db, ctx, filePath);
33730
33907
  const primary = pickPrimaryTagAtPosition(fileRows, filePath, line, column);
33731
33908
  if (!primary) return null;
33732
33909
  const atPos = findTagsAtPosition(fileRows, filePath, line, column);
33733
- if (primary.kind === "module" && primary.nav_target_path) {
33734
- return { definitions: [modulePathLocation(primary)], confidentTarget: true };
33735
- }
33736
- const onDefinition = atPos.some((row) => row.role === "def");
33737
- if (onDefinition) {
33738
- const defs = atPos.filter((row) => row.role === "def").map(rowToLocation);
33739
- return { definitions: defs, confidentTarget: defs.length === 1 };
33740
- }
33741
- const lookups = createNavigationLookups(db, ctx.parentId);
33910
+ const lookups = createSnapshotNavigationLookups(db, ctx.parentId);
33742
33911
  const workspaceFiles = listWorkspaceFiles(db, resolved, filePath);
33743
- const navCtx = buildNavigationContext(filePath, primary, lookups, workspaceFiles);
33744
- const navigation = resolveNavigation(navCtx);
33745
- return {
33746
- definitions: navigation.definitions.map(rowToLocation),
33747
- confidentTarget: navigation.confidentTarget
33748
- };
33912
+ return { primary, atPos, lookups, workspaceFiles };
33749
33913
  });
33914
+ if (snapshot == null) {
33915
+ await yieldToEventLoop();
33916
+ return { definitions: [], confidentTarget: false };
33917
+ }
33918
+ const result = resolveDefinitionsFromSnapshot(filePath, snapshot);
33750
33919
  await yieldToEventLoop();
33751
- return result ?? { definitions: [], confidentTarget: false };
33920
+ return result;
33921
+ }
33922
+ function resolveDefinitionsFromSnapshot(filePath, snapshot) {
33923
+ const { primary, atPos, lookups, workspaceFiles } = snapshot;
33924
+ if (primary.kind === "module" && primary.nav_target_path) {
33925
+ return { definitions: [modulePathLocation(primary)], confidentTarget: true };
33926
+ }
33927
+ const onDefinition = atPos.some((row) => row.role === "def");
33928
+ if (onDefinition) {
33929
+ const defs = atPos.filter((row) => row.role === "def").map(rowToLocation);
33930
+ return { definitions: defs, confidentTarget: defs.length === 1 };
33931
+ }
33932
+ const navCtx = buildNavigationContext(filePath, primary, lookups, workspaceFiles);
33933
+ const navigation = resolveNavigation(navCtx);
33934
+ return {
33935
+ definitions: navigation.definitions.map(rowToLocation),
33936
+ confidentTarget: navigation.confidentTarget
33937
+ };
33752
33938
  }
33753
33939
  async function queryDefinitionsAt(parentPath, filePath, line, column) {
33754
33940
  return (await queryDefinitionsNavigation(parentPath, filePath, line, column)).definitions;
@@ -33761,7 +33947,7 @@ async function queryDefinitionAt(parentPath, filePath, line, column) {
33761
33947
  async function queryReferencesAt(parentPath, filePath, line, column) {
33762
33948
  await yieldToEventLoop();
33763
33949
  const resolved = normalizeResolvedPath(parentPath);
33764
- const rows = withCodeNavCacheSqliteSync((db) => {
33950
+ const rows = await withCodeNavCacheSqlite((db) => {
33765
33951
  const ctx = getSymbolIndexParentContext(db, resolved);
33766
33952
  if (ctx == null) return null;
33767
33953
  const fileRows = loadFileTags(db, ctx, filePath);
@@ -33796,23 +33982,28 @@ function rowToSymbolSpan(row, clickable) {
33796
33982
  async function querySymbolsInFile(parentPath, filePath) {
33797
33983
  await yieldToEventLoop();
33798
33984
  const resolved = normalizeResolvedPath(parentPath);
33799
- const spans = withCodeNavCacheSqliteSync((db) => {
33985
+ const snapshot = await withCodeNavCacheSqlite((db) => {
33800
33986
  const ctx = getSymbolIndexParentContext(db, resolved);
33801
- if (ctx == null) return [];
33987
+ if (ctx == null) return null;
33802
33988
  const rows = loadFileTags(db, ctx, filePath);
33803
- if (rows.length === 0) return [];
33804
- const lookups = createNavigationLookups(db, ctx.parentId);
33989
+ if (rows.length === 0) return null;
33990
+ const lookups = createSnapshotNavigationLookups(db, ctx.parentId);
33805
33991
  const workspaceFiles = listWorkspaceFiles(db, resolved, filePath);
33806
- const baseCtx = buildNavigationContext(
33807
- filePath,
33808
- rows[0] ?? emptyRefRow(filePath),
33809
- lookups,
33810
- workspaceFiles
33811
- );
33812
- return rows.map((row) => {
33813
- const navCtx = { ...baseCtx, ref: row };
33814
- return rowToSymbolSpan(row, refHasResolvableDefinitions(navCtx));
33815
- });
33992
+ return { rows, lookups, workspaceFiles };
33993
+ });
33994
+ if (snapshot == null) {
33995
+ await yieldToEventLoop();
33996
+ return [];
33997
+ }
33998
+ const baseCtx = buildNavigationContext(
33999
+ filePath,
34000
+ snapshot.rows[0] ?? emptyRefRow(filePath),
34001
+ snapshot.lookups,
34002
+ snapshot.workspaceFiles
34003
+ );
34004
+ const spans = snapshot.rows.map((row) => {
34005
+ const navCtx = { ...baseCtx, ref: row };
34006
+ return rowToSymbolSpan(row, refHasResolvableDefinitions(navCtx));
33816
34007
  });
33817
34008
  await yieldToEventLoop();
33818
34009
  return spans;
@@ -34815,8 +35006,8 @@ async function execGitFile(args, options) {
34815
35006
  }
34816
35007
 
34817
35008
  // src/git/changes/parse/normalize-git-diff-path.ts
34818
- function normalizeGitDiffPath(path73) {
34819
- return path73.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
35009
+ function normalizeGitDiffPath(path75) {
35010
+ return path75.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
34820
35011
  }
34821
35012
 
34822
35013
  // src/git/changes/parse/parse-name-status-entries.ts
@@ -35103,9 +35294,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
35103
35294
  // src/agents/acp/put-summarize-change-summaries.ts
35104
35295
  async function putEncryptedChangeSummaryRows(params) {
35105
35296
  const base = params.apiBaseUrl.replace(/\/+$/, "");
35106
- const entries = params.rows.map(({ path: path73, summary }) => {
35297
+ const entries = params.rows.map(({ path: path75, summary }) => {
35107
35298
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
35108
- return { path: path73, summary: JSON.stringify(enc) };
35299
+ return { path: path75, summary: JSON.stringify(enc) };
35109
35300
  });
35110
35301
  const res = await fetch(
35111
35302
  `${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -35940,8 +36131,8 @@ function pathspec(...paths) {
35940
36131
  cache.set(key, paths);
35941
36132
  return key;
35942
36133
  }
35943
- function isPathSpec(path73) {
35944
- return path73 instanceof String && cache.has(path73);
36134
+ function isPathSpec(path75) {
36135
+ return path75 instanceof String && cache.has(path75);
35945
36136
  }
35946
36137
  function toPaths(pathSpec) {
35947
36138
  return cache.get(pathSpec) || [];
@@ -36030,8 +36221,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
36030
36221
  function forEachLineWithContent(input, callback) {
36031
36222
  return toLinesWithContent(input, true).map((line) => callback(line));
36032
36223
  }
36033
- function folderExists(path73) {
36034
- return (0, import_file_exists.exists)(path73, import_file_exists.FOLDER);
36224
+ function folderExists(path75) {
36225
+ return (0, import_file_exists.exists)(path75, import_file_exists.FOLDER);
36035
36226
  }
36036
36227
  function append(target, item) {
36037
36228
  if (Array.isArray(target)) {
@@ -36435,8 +36626,8 @@ function checkIsRepoRootTask() {
36435
36626
  commands,
36436
36627
  format: "utf-8",
36437
36628
  onError,
36438
- parser(path73) {
36439
- return /^\.(git)?$/.test(path73.trim());
36629
+ parser(path75) {
36630
+ return /^\.(git)?$/.test(path75.trim());
36440
36631
  }
36441
36632
  };
36442
36633
  }
@@ -36870,11 +37061,11 @@ function parseGrep(grep) {
36870
37061
  const paths = /* @__PURE__ */ new Set();
36871
37062
  const results = {};
36872
37063
  forEachLineWithContent(grep, (input) => {
36873
- const [path73, line, preview] = input.split(NULL);
36874
- paths.add(path73);
36875
- (results[path73] = results[path73] || []).push({
37064
+ const [path75, line, preview] = input.split(NULL);
37065
+ paths.add(path75);
37066
+ (results[path75] = results[path75] || []).push({
36876
37067
  line: asNumber(line),
36877
- path: path73,
37068
+ path: path75,
36878
37069
  preview
36879
37070
  });
36880
37071
  });
@@ -37639,14 +37830,14 @@ var init_hash_object = __esm2({
37639
37830
  init_task();
37640
37831
  }
37641
37832
  });
37642
- function parseInit(bare, path73, text) {
37833
+ function parseInit(bare, path75, text) {
37643
37834
  const response = String(text).trim();
37644
37835
  let result;
37645
37836
  if (result = initResponseRegex.exec(response)) {
37646
- return new InitSummary(bare, path73, false, result[1]);
37837
+ return new InitSummary(bare, path75, false, result[1]);
37647
37838
  }
37648
37839
  if (result = reInitResponseRegex.exec(response)) {
37649
- return new InitSummary(bare, path73, true, result[1]);
37840
+ return new InitSummary(bare, path75, true, result[1]);
37650
37841
  }
37651
37842
  let gitDir = "";
37652
37843
  const tokens = response.split(" ");
@@ -37657,7 +37848,7 @@ function parseInit(bare, path73, text) {
37657
37848
  break;
37658
37849
  }
37659
37850
  }
37660
- return new InitSummary(bare, path73, /^re/i.test(response), gitDir);
37851
+ return new InitSummary(bare, path75, /^re/i.test(response), gitDir);
37661
37852
  }
37662
37853
  var InitSummary;
37663
37854
  var initResponseRegex;
@@ -37666,9 +37857,9 @@ var init_InitSummary = __esm2({
37666
37857
  "src/lib/responses/InitSummary.ts"() {
37667
37858
  "use strict";
37668
37859
  InitSummary = class {
37669
- constructor(bare, path73, existing, gitDir) {
37860
+ constructor(bare, path75, existing, gitDir) {
37670
37861
  this.bare = bare;
37671
- this.path = path73;
37862
+ this.path = path75;
37672
37863
  this.existing = existing;
37673
37864
  this.gitDir = gitDir;
37674
37865
  }
@@ -37680,7 +37871,7 @@ var init_InitSummary = __esm2({
37680
37871
  function hasBareCommand(command) {
37681
37872
  return command.includes(bareCommand);
37682
37873
  }
37683
- function initTask(bare = false, path73, customArgs) {
37874
+ function initTask(bare = false, path75, customArgs) {
37684
37875
  const commands = ["init", ...customArgs];
37685
37876
  if (bare && !hasBareCommand(commands)) {
37686
37877
  commands.splice(1, 0, bareCommand);
@@ -37689,7 +37880,7 @@ function initTask(bare = false, path73, customArgs) {
37689
37880
  commands,
37690
37881
  format: "utf-8",
37691
37882
  parser(text) {
37692
- return parseInit(commands.includes("--bare"), path73, text);
37883
+ return parseInit(commands.includes("--bare"), path75, text);
37693
37884
  }
37694
37885
  };
37695
37886
  }
@@ -38505,12 +38696,12 @@ var init_FileStatusSummary = __esm2({
38505
38696
  "use strict";
38506
38697
  fromPathRegex = /^(.+)\0(.+)$/;
38507
38698
  FileStatusSummary = class {
38508
- constructor(path73, index, working_dir) {
38509
- this.path = path73;
38699
+ constructor(path75, index, working_dir) {
38700
+ this.path = path75;
38510
38701
  this.index = index;
38511
38702
  this.working_dir = working_dir;
38512
38703
  if (index === "R" || working_dir === "R") {
38513
- const detail = fromPathRegex.exec(path73) || [null, path73, path73];
38704
+ const detail = fromPathRegex.exec(path75) || [null, path75, path75];
38514
38705
  this.from = detail[2] || "";
38515
38706
  this.path = detail[1] || "";
38516
38707
  }
@@ -38541,14 +38732,14 @@ function splitLine(result, lineStr) {
38541
38732
  default:
38542
38733
  return;
38543
38734
  }
38544
- function data(index, workingDir, path73) {
38735
+ function data(index, workingDir, path75) {
38545
38736
  const raw = `${index}${workingDir}`;
38546
38737
  const handler = parsers6.get(raw);
38547
38738
  if (handler) {
38548
- handler(result, path73);
38739
+ handler(result, path75);
38549
38740
  }
38550
38741
  if (raw !== "##" && raw !== "!!") {
38551
- result.files.push(new FileStatusSummary(path73, index, workingDir));
38742
+ result.files.push(new FileStatusSummary(path75, index, workingDir));
38552
38743
  }
38553
38744
  }
38554
38745
  }
@@ -38821,15 +39012,15 @@ var init_simple_git_api = __esm2({
38821
39012
  this._executor = _executor;
38822
39013
  }
38823
39014
  _runTask(task, then) {
38824
- const chain3 = this._executor.chain();
38825
- const promise2 = chain3.push(task);
39015
+ const chain2 = this._executor.chain();
39016
+ const promise2 = chain2.push(task);
38826
39017
  if (then) {
38827
39018
  taskCallback(task, promise2, then);
38828
39019
  }
38829
39020
  return Object.create(this, {
38830
39021
  then: { value: promise2.then.bind(promise2) },
38831
39022
  catch: { value: promise2.catch.bind(promise2) },
38832
- _executor: { value: chain3 }
39023
+ _executor: { value: chain2 }
38833
39024
  });
38834
39025
  }
38835
39026
  add(files) {
@@ -38857,9 +39048,9 @@ var init_simple_git_api = __esm2({
38857
39048
  next
38858
39049
  );
38859
39050
  }
38860
- hashObject(path73, write) {
39051
+ hashObject(path75, write) {
38861
39052
  return this._runTask(
38862
- hashObjectTask(path73, write === true),
39053
+ hashObjectTask(path75, write === true),
38863
39054
  trailingFunctionArgument(arguments)
38864
39055
  );
38865
39056
  }
@@ -39212,8 +39403,8 @@ var init_branch = __esm2({
39212
39403
  }
39213
39404
  });
39214
39405
  function toPath(input) {
39215
- const path73 = input.trim().replace(/^["']|["']$/g, "");
39216
- return path73 && normalize3(path73);
39406
+ const path75 = input.trim().replace(/^["']|["']$/g, "");
39407
+ return path75 && normalize3(path75);
39217
39408
  }
39218
39409
  var parseCheckIgnore;
39219
39410
  var init_CheckIgnore = __esm2({
@@ -39527,8 +39718,8 @@ __export2(sub_module_exports, {
39527
39718
  subModuleTask: () => subModuleTask,
39528
39719
  updateSubModuleTask: () => updateSubModuleTask
39529
39720
  });
39530
- function addSubModuleTask(repo, path73) {
39531
- return subModuleTask(["add", repo, path73]);
39721
+ function addSubModuleTask(repo, path75) {
39722
+ return subModuleTask(["add", repo, path75]);
39532
39723
  }
39533
39724
  function initSubModuleTask(customArgs) {
39534
39725
  return subModuleTask(["init", ...customArgs]);
@@ -39861,8 +40052,8 @@ var require_git = __commonJS2({
39861
40052
  }
39862
40053
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
39863
40054
  };
39864
- Git2.prototype.submoduleAdd = function(repo, path73, then) {
39865
- return this._runTask(addSubModuleTask2(repo, path73), trailingFunctionArgument2(arguments));
40055
+ Git2.prototype.submoduleAdd = function(repo, path75, then) {
40056
+ return this._runTask(addSubModuleTask2(repo, path75), trailingFunctionArgument2(arguments));
39866
40057
  };
39867
40058
  Git2.prototype.submoduleUpdate = function(args, then) {
39868
40059
  return this._runTask(
@@ -40598,8 +40789,8 @@ function parseNumstatEntries(lines) {
40598
40789
  }
40599
40790
  function parseNumstat(lines) {
40600
40791
  const m = /* @__PURE__ */ new Map();
40601
- for (const [path73, entry] of parseNumstatEntries(lines)) {
40602
- m.set(path73, { additions: entry.additions, deletions: entry.deletions });
40792
+ for (const [path75, entry] of parseNumstatEntries(lines)) {
40793
+ m.set(path75, { additions: entry.additions, deletions: entry.deletions });
40603
40794
  }
40604
40795
  return m;
40605
40796
  }
@@ -42643,37 +42834,16 @@ function defaultWorktreesRootPath() {
42643
42834
  }
42644
42835
 
42645
42836
  // src/files/watch-file-index.ts
42646
- import fs37 from "node:fs";
42647
- import { watch } from "node:fs";
42648
- import path57 from "node:path";
42837
+ import path59 from "node:path";
42649
42838
 
42650
42839
  // src/files/index/build-file-index.ts
42651
42840
  import path55 from "node:path";
42652
42841
 
42653
42842
  // src/files/index/file-index-sqlite-lock.ts
42654
- import fs36 from "node:fs";
42655
- function isSqliteCorruptError2(e) {
42656
- const msg = e instanceof Error ? e.message : String(e);
42657
- return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
42658
- }
42659
- var chain2 = Promise.resolve();
42843
+ var fileIndexChain = Promise.resolve();
42660
42844
  function withFileIndexSqliteLock(fn) {
42661
- const run = async () => {
42662
- try {
42663
- return await Promise.resolve(fn());
42664
- } catch (e) {
42665
- if (!isSqliteCorruptError2(e)) throw e;
42666
- closeAllCodeNavCacheSqliteConnections();
42667
- try {
42668
- fs36.unlinkSync(getCodeNavCacheSqlitePath());
42669
- } catch {
42670
- }
42671
- chain2 = Promise.resolve();
42672
- return await Promise.resolve(fn());
42673
- }
42674
- };
42675
- const next = chain2.then(run);
42676
- chain2 = next.then(
42845
+ const next = fileIndexChain.then(() => Promise.resolve(fn()));
42846
+ fileIndexChain = next.then(
42677
42847
  () => void 0,
42678
42848
  () => void 0
42679
42849
  );
@@ -42694,6 +42864,14 @@ async function collectWorkspacePathsAsync(resolved) {
42694
42864
  return paths;
42695
42865
  }
42696
42866
 
42867
+ // src/files/index/file-index-persist-constants.ts
42868
+ var FILE_INDEX_INSERT_BUFFER = 2048;
42869
+
42870
+ // src/files/index/file-index-persist-shutdown.ts
42871
+ function assertFileIndexPersistNotShutdown() {
42872
+ if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
42873
+ }
42874
+
42697
42875
  // src/files/index/file-index-parent-path.ts
42698
42876
  function upsertFileIndexParentPath(db, resolved) {
42699
42877
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -42708,57 +42886,56 @@ function upsertFileIndexParentPath(db, resolved) {
42708
42886
  return Number(row.id);
42709
42887
  }
42710
42888
 
42711
- // src/files/index/persist-file-index.ts
42712
- var FILE_INDEX_INTERRUPT_CHECK_EVERY = 256;
42713
- function assertNotShutdown2() {
42714
- if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
42889
+ // src/files/index/file-index-persist-transaction.ts
42890
+ function clearFileIndexChildPaths(db, resolved) {
42891
+ runCodeNavCacheWriteTransaction(db, () => {
42892
+ const parentId = upsertFileIndexParentPath(db, resolved);
42893
+ db.run("DELETE FROM file_index_child_path WHERE parent_id = ?", [parentId]);
42894
+ });
42715
42895
  }
42716
- function persistFileIndexPaths(resolved, paths) {
42717
- return withCodeNavCacheSqliteSync((db) => {
42718
- let pathCount = 0;
42719
- db.run("BEGIN IMMEDIATE");
42896
+ function insertFileIndexChildPathBatch(db, resolved, paths) {
42897
+ return runCodeNavCacheWriteTransaction(db, () => {
42898
+ const parentId = upsertFileIndexParentPath(db, resolved);
42899
+ const ins = db.prepare(
42900
+ "INSERT INTO file_index_child_path (parent_id, path) VALUES (?, ?)"
42901
+ );
42720
42902
  try {
42721
- const parentId = upsertFileIndexParentPath(db, resolved);
42722
- db.run("DELETE FROM file_index_child_path WHERE parent_id = ?", [parentId]);
42723
- const ins = db.prepare("INSERT INTO file_index_child_path (parent_id, path) VALUES (?, ?)");
42724
- try {
42725
- let batch = 0;
42726
- for (const rel of paths) {
42727
- if (++batch >= FILE_INDEX_INTERRUPT_CHECK_EVERY) {
42728
- batch = 0;
42729
- assertNotShutdown2();
42730
- }
42731
- ins.run([parentId, rel]);
42732
- pathCount += 1;
42733
- }
42734
- } finally {
42735
- ins.finalize();
42903
+ for (const rel of paths) {
42904
+ ins.run([parentId, rel]);
42736
42905
  }
42737
- db.run("COMMIT");
42738
- } catch (e) {
42739
- try {
42740
- db.run("ROLLBACK");
42741
- } catch {
42742
- }
42743
- throw e;
42906
+ return paths.length;
42907
+ } finally {
42908
+ ins.finalize();
42744
42909
  }
42745
- return pathCount;
42746
42910
  });
42747
42911
  }
42748
42912
 
42913
+ // src/files/index/persist-file-index-paths.ts
42914
+ async function persistFileIndexPathsAsync(resolved, paths) {
42915
+ await withCodeNavCacheSqlite((db) => clearFileIndexChildPaths(db, resolved));
42916
+ let pathCount = 0;
42917
+ for (let offset = 0; offset < paths.length; offset += FILE_INDEX_INSERT_BUFFER) {
42918
+ assertFileIndexPersistNotShutdown();
42919
+ const batch = paths.slice(offset, offset + FILE_INDEX_INSERT_BUFFER);
42920
+ pathCount += await withCodeNavCacheSqlite((db) => insertFileIndexChildPathBatch(db, resolved, batch));
42921
+ await yieldToEventLoop();
42922
+ }
42923
+ return pathCount;
42924
+ }
42925
+
42749
42926
  // src/files/index/build-file-index.ts
42750
- function assertNotShutdown3() {
42927
+ function assertNotShutdown2() {
42751
42928
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
42752
42929
  }
42753
42930
  async function buildFileIndexAsync(cwd) {
42754
- return withFileIndexSqliteLock(async () => {
42755
- const resolved = path55.resolve(cwd);
42756
- await yieldToEventLoop();
42757
- assertNotShutdown3();
42758
- const paths = await collectWorkspacePathsAsync(resolved);
42759
- await yieldToEventLoop();
42760
- assertNotShutdown3();
42761
- const pathCount = persistFileIndexPaths(resolved, paths);
42931
+ const resolved = path55.resolve(cwd);
42932
+ await yieldToEventLoop();
42933
+ assertNotShutdown2();
42934
+ const paths = await collectWorkspacePathsAsync(resolved);
42935
+ await yieldToEventLoop();
42936
+ assertNotShutdown2();
42937
+ return await withFileIndexSqliteLock(async () => {
42938
+ const pathCount = await persistFileIndexPathsAsync(resolved, paths);
42762
42939
  return { pathCount };
42763
42940
  });
42764
42941
  }
@@ -42823,8 +43000,9 @@ async function ensureFileIndexAsync(cwd) {
42823
43000
  return { ...await buildFileIndexAsync(resolved), fromCache: false };
42824
43001
  }
42825
43002
 
42826
- // src/files/watch-file-index.ts
42827
- var DEBOUNCE_MS = 900;
43003
+ // src/files/index/file-index-fs-watcher.ts
43004
+ import { watch } from "node:fs";
43005
+ var FILE_INDEX_WATCH_DEBOUNCE_MS = 900;
42828
43006
  function shouldIgnoreRelative(rel) {
42829
43007
  const n = rel.replace(/\\/g, "/");
42830
43008
  return n.split("/").some((segment) => shouldSkipWorkspaceWalkEntry(segment));
@@ -42834,7 +43012,7 @@ function attachWatchErrorLog(w) {
42834
43012
  console.error("[file-index] File watcher error:", err);
42835
43013
  });
42836
43014
  }
42837
- function createFsWatcher(resolved, schedule) {
43015
+ function createFileIndexFsWatcher(resolved, schedule) {
42838
43016
  const onEvent = (_event, filename) => {
42839
43017
  if (filename != null) {
42840
43018
  const rel = typeof filename === "string" ? filename.replace(/\\/g, "/") : filename.toString("utf8").replace(/\\/g, "/");
@@ -42861,11 +43039,15 @@ function createFsWatcher(resolved, schedule) {
42861
43039
  throw e;
42862
43040
  }
42863
43041
  }
43042
+
43043
+ // src/files/index/file-index-incremental-symbol-updates.ts
43044
+ import fs36 from "node:fs";
43045
+ import path57 from "node:path";
42864
43046
  async function applyIncrementalSymbolIndexUpdates(resolved, relPaths) {
42865
43047
  for (const rel of relPaths) {
42866
43048
  const absPath = path57.join(resolved, rel);
42867
43049
  try {
42868
- const stat3 = await fs37.promises.stat(absPath);
43050
+ const stat3 = await fs36.promises.stat(absPath);
42869
43051
  if (stat3.isFile()) {
42870
43052
  await updateSymbolIndexForFile(resolved, absPath);
42871
43053
  } else {
@@ -42881,54 +43063,80 @@ async function applyIncrementalSymbolIndexUpdates(resolved, relPaths) {
42881
43063
  await yieldToEventLoop();
42882
43064
  }
42883
43065
  }
42884
- function startFileIndexWatcher(cwd = getBridgeRoot()) {
42885
- const resolved = path57.resolve(cwd);
42886
- void buildFileIndexAsync(resolved).catch((e) => {
43066
+
43067
+ // src/files/index/file-index-initial-builds.ts
43068
+ import path58 from "node:path";
43069
+ async function runInitialIndexBuilds(cwd = getBridgeRoot()) {
43070
+ const resolved = path58.resolve(cwd);
43071
+ try {
43072
+ await buildFileIndexAsync(resolved);
43073
+ await buildSymbolIndexAsync(resolved);
43074
+ } catch (e) {
42887
43075
  if (e instanceof CliSqliteInterrupted) return;
42888
43076
  console.error("[file-index] Initial index build failed:", e);
43077
+ }
43078
+ }
43079
+ function scheduleInitialIndexBuilds(cwd = getBridgeRoot()) {
43080
+ setImmediate(() => {
43081
+ void runInitialIndexBuilds(cwd);
42889
43082
  });
42890
- void buildSymbolIndexAsync(resolved).catch((e) => {
42891
- if (e instanceof CliSqliteInterrupted) return;
42892
- console.error("[code-nav] Initial symbol index build failed:", e);
42893
- });
43083
+ }
43084
+
43085
+ // src/files/watch-file-index.ts
43086
+ var MAX_INCREMENTAL_SYMBOL_UPDATES = 512;
43087
+ function startFileIndexWatcher(cwd = getBridgeRoot()) {
43088
+ const resolved = path59.resolve(cwd);
42894
43089
  let timer = null;
42895
43090
  const pendingSymbolUpdates = /* @__PURE__ */ new Set();
42896
43091
  let needsFullSymbolRebuild = false;
43092
+ let rebuildRunning = false;
43093
+ let rebuildQueued = false;
42897
43094
  const runRebuild = () => {
42898
- void buildFileIndexAsync(resolved).catch((e) => {
42899
- if (e instanceof CliSqliteInterrupted) return;
42900
- console.error("[file-index] Watch rebuild failed:", e);
42901
- });
42902
- if (needsFullSymbolRebuild) {
42903
- needsFullSymbolRebuild = false;
42904
- pendingSymbolUpdates.clear();
42905
- void buildSymbolIndexAsync(resolved).catch((e) => {
42906
- if (e instanceof CliSqliteInterrupted) return;
42907
- console.error("[code-nav] Watch rebuild failed:", e);
42908
- });
43095
+ if (rebuildRunning) {
43096
+ rebuildQueued = true;
42909
43097
  return;
42910
43098
  }
42911
- const relPaths = [...pendingSymbolUpdates];
43099
+ rebuildRunning = true;
43100
+ const fullSymbol = needsFullSymbolRebuild;
43101
+ needsFullSymbolRebuild = false;
43102
+ const relPaths = fullSymbol ? [] : [...pendingSymbolUpdates];
42912
43103
  pendingSymbolUpdates.clear();
42913
- if (relPaths.length === 0) return;
42914
- void applyIncrementalSymbolIndexUpdates(resolved, relPaths).catch((e) => {
43104
+ void buildFileIndexAsync(resolved).then(async () => {
43105
+ if (fullSymbol) {
43106
+ await buildSymbolIndexAsync(resolved);
43107
+ return;
43108
+ }
43109
+ if (relPaths.length === 0) return;
43110
+ await applyIncrementalSymbolIndexUpdates(resolved, relPaths);
43111
+ }).catch((e) => {
42915
43112
  if (e instanceof CliSqliteInterrupted) return;
42916
- console.error("[code-nav] Incremental watch update failed:", e);
43113
+ console.error("[file-index] Watch rebuild failed:", e);
43114
+ }).finally(() => {
43115
+ rebuildRunning = false;
43116
+ if (rebuildQueued || needsFullSymbolRebuild || pendingSymbolUpdates.size > 0) {
43117
+ rebuildQueued = false;
43118
+ setImmediate(runRebuild);
43119
+ }
42917
43120
  });
42918
43121
  };
42919
43122
  const schedule = (relPath) => {
42920
- if (relPath != null) {
43123
+ if (relPath != null && !needsFullSymbolRebuild) {
42921
43124
  pendingSymbolUpdates.add(relPath);
43125
+ if (pendingSymbolUpdates.size > MAX_INCREMENTAL_SYMBOL_UPDATES) {
43126
+ pendingSymbolUpdates.clear();
43127
+ needsFullSymbolRebuild = true;
43128
+ }
42922
43129
  } else {
43130
+ pendingSymbolUpdates.clear();
42923
43131
  needsFullSymbolRebuild = true;
42924
43132
  }
42925
43133
  if (timer) clearTimeout(timer);
42926
43134
  timer = setTimeout(() => {
42927
43135
  timer = null;
42928
43136
  runRebuild();
42929
- }, DEBOUNCE_MS);
43137
+ }, FILE_INDEX_WATCH_DEBOUNCE_MS);
42930
43138
  };
42931
- const watcher = createFsWatcher(resolved, schedule);
43139
+ const watcher = createFileIndexFsWatcher(resolved, schedule);
42932
43140
  return () => {
42933
43141
  if (timer) {
42934
43142
  clearTimeout(timer);
@@ -42939,7 +43147,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
42939
43147
  }
42940
43148
 
42941
43149
  // src/connection/create-bridge-connection.ts
42942
- import * as path71 from "node:path";
43150
+ import * as path73 from "node:path";
42943
43151
 
42944
43152
  // src/dev-servers/manager/dev-server-manager.ts
42945
43153
  import { rm as rm2 } from "node:fs/promises";
@@ -42983,7 +43191,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
42983
43191
  }
42984
43192
 
42985
43193
  // src/dev-servers/process/wire-dev-server-child-process.ts
42986
- import fs38 from "node:fs";
43194
+ import fs37 from "node:fs";
42987
43195
 
42988
43196
  // src/dev-servers/manager/forward-pipe.ts
42989
43197
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -43019,7 +43227,7 @@ function wireDevServerChildProcess(d) {
43019
43227
  d.setPollInterval(void 0);
43020
43228
  return;
43021
43229
  }
43022
- fs38.readFile(d.mergedLogPath, (err, buf) => {
43230
+ fs37.readFile(d.mergedLogPath, (err, buf) => {
43023
43231
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
43024
43232
  if (buf.length <= d.mergedReadPos.value) return;
43025
43233
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -43057,7 +43265,7 @@ ${errTail}` : ""}`);
43057
43265
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
43058
43266
  };
43059
43267
  if (mergedPath) {
43060
- fs38.readFile(mergedPath, (err, buf) => {
43268
+ fs37.readFile(mergedPath, (err, buf) => {
43061
43269
  if (!err && buf.length > d.mergedReadPos.value) {
43062
43270
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
43063
43271
  if (chunk.length > 0) {
@@ -43159,13 +43367,13 @@ function parseDevServerDefs(servers) {
43159
43367
  }
43160
43368
 
43161
43369
  // src/dev-servers/manager/shell-spawn/utils.ts
43162
- import fs39 from "node:fs";
43370
+ import fs38 from "node:fs";
43163
43371
  function isSpawnEbadf(e) {
43164
43372
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
43165
43373
  }
43166
43374
  function rmDirQuiet(dir) {
43167
43375
  try {
43168
- fs39.rmSync(dir, { recursive: true, force: true });
43376
+ fs38.rmSync(dir, { recursive: true, force: true });
43169
43377
  } catch {
43170
43378
  }
43171
43379
  }
@@ -43173,7 +43381,7 @@ var cachedDevNullReadFd;
43173
43381
  function devNullReadFd() {
43174
43382
  if (cachedDevNullReadFd === void 0) {
43175
43383
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
43176
- cachedDevNullReadFd = fs39.openSync(devPath, "r");
43384
+ cachedDevNullReadFd = fs38.openSync(devPath, "r");
43177
43385
  }
43178
43386
  return cachedDevNullReadFd;
43179
43387
  }
@@ -43247,15 +43455,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
43247
43455
 
43248
43456
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
43249
43457
  import { spawn as spawn7 } from "node:child_process";
43250
- import fs40 from "node:fs";
43458
+ import fs39 from "node:fs";
43251
43459
  import { tmpdir } from "node:os";
43252
- import path58 from "node:path";
43460
+ import path60 from "node:path";
43253
43461
  function trySpawnMergedLogFile(command, env, cwd, signal) {
43254
- const tmpRoot = fs40.mkdtempSync(path58.join(tmpdir(), "ba-devsrv-log-"));
43255
- const logPath = path58.join(tmpRoot, "combined.log");
43462
+ const tmpRoot = fs39.mkdtempSync(path60.join(tmpdir(), "ba-devsrv-log-"));
43463
+ const logPath = path60.join(tmpRoot, "combined.log");
43256
43464
  let logFd;
43257
43465
  try {
43258
- logFd = fs40.openSync(logPath, "a");
43466
+ logFd = fs39.openSync(logPath, "a");
43259
43467
  } catch {
43260
43468
  rmDirQuiet(tmpRoot);
43261
43469
  return null;
@@ -43274,7 +43482,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
43274
43482
  } else {
43275
43483
  proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
43276
43484
  }
43277
- fs40.closeSync(logFd);
43485
+ fs39.closeSync(logFd);
43278
43486
  return {
43279
43487
  proc,
43280
43488
  pipedStdoutStderr: true,
@@ -43283,7 +43491,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
43283
43491
  };
43284
43492
  } catch (e) {
43285
43493
  try {
43286
- fs40.closeSync(logFd);
43494
+ fs39.closeSync(logFd);
43287
43495
  } catch {
43288
43496
  }
43289
43497
  rmDirQuiet(tmpRoot);
@@ -43294,22 +43502,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
43294
43502
 
43295
43503
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
43296
43504
  import { spawn as spawn8 } from "node:child_process";
43297
- import fs41 from "node:fs";
43505
+ import fs40 from "node:fs";
43298
43506
  import { tmpdir as tmpdir2 } from "node:os";
43299
- import path59 from "node:path";
43507
+ import path61 from "node:path";
43300
43508
  function shSingleQuote(s) {
43301
43509
  return `'${s.replace(/'/g, `'\\''`)}'`;
43302
43510
  }
43303
43511
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
43304
- const tmpRoot = fs41.mkdtempSync(path59.join(tmpdir2(), "ba-devsrv-sh-"));
43305
- const logPath = path59.join(tmpRoot, "combined.log");
43306
- const innerPath = path59.join(tmpRoot, "_cmd.sh");
43307
- const runnerPath = path59.join(tmpRoot, "_run.sh");
43512
+ const tmpRoot = fs40.mkdtempSync(path61.join(tmpdir2(), "ba-devsrv-sh-"));
43513
+ const logPath = path61.join(tmpRoot, "combined.log");
43514
+ const innerPath = path61.join(tmpRoot, "_cmd.sh");
43515
+ const runnerPath = path61.join(tmpRoot, "_run.sh");
43308
43516
  try {
43309
- fs41.writeFileSync(innerPath, `#!/bin/sh
43517
+ fs40.writeFileSync(innerPath, `#!/bin/sh
43310
43518
  ${command}
43311
43519
  `);
43312
- fs41.writeFileSync(
43520
+ fs40.writeFileSync(
43313
43521
  runnerPath,
43314
43522
  `#!/bin/sh
43315
43523
  cd ${shSingleQuote(cwd)}
@@ -43335,13 +43543,13 @@ cd ${shSingleQuote(cwd)}
43335
43543
  }
43336
43544
  }
43337
43545
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
43338
- const tmpRoot = fs41.mkdtempSync(path59.join(tmpdir2(), "ba-devsrv-sh-"));
43339
- const logPath = path59.join(tmpRoot, "combined.log");
43340
- const runnerPath = path59.join(tmpRoot, "_run.bat");
43546
+ const tmpRoot = fs40.mkdtempSync(path61.join(tmpdir2(), "ba-devsrv-sh-"));
43547
+ const logPath = path61.join(tmpRoot, "combined.log");
43548
+ const runnerPath = path61.join(tmpRoot, "_run.bat");
43341
43549
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
43342
43550
  const com = process.env.ComSpec || "cmd.exe";
43343
43551
  try {
43344
- fs41.writeFileSync(
43552
+ fs40.writeFileSync(
43345
43553
  runnerPath,
43346
43554
  `@ECHO OFF\r
43347
43555
  CD /D ${q(cwd)}\r
@@ -44166,7 +44374,7 @@ function attachFirehoseAfterIdentified(ctx, params) {
44166
44374
  function scheduleFirehoseRetryAfterDrop(closeMeta) {
44167
44375
  if (state.closedByUser) return;
44168
44376
  const meta = closeMeta ?? state.lastFirehoseReconnectCloseMeta ?? void 0;
44169
- const delay2 = applyTieredReconnectPlanAndLog(
44377
+ const delay3 = applyTieredReconnectPlanAndLog(
44170
44378
  state.firehoseOutage,
44171
44379
  logFn,
44172
44380
  PREVIEW_TUNNEL_SERVICE_LABEL,
@@ -44175,7 +44383,7 @@ function attachFirehoseAfterIdentified(ctx, params) {
44175
44383
  meta?.reason
44176
44384
  );
44177
44385
  armReconnectDelayTimer({
44178
- delayMs: delay2,
44386
+ delayMs: delay3,
44179
44387
  bumpAttempt: () => {
44180
44388
  state.firehoseReconnectAttempt += 1;
44181
44389
  },
@@ -44286,17 +44494,17 @@ async function yieldSkillDiscoveryWork(entryCount) {
44286
44494
  }
44287
44495
 
44288
44496
  // src/skills/discovery/discover-local-skills.ts
44289
- import fs42 from "node:fs";
44290
- import path60 from "node:path";
44497
+ import fs41 from "node:fs";
44498
+ import path62 from "node:path";
44291
44499
  function collectSkillFromDir(rel, base, name, seenKeys, out) {
44292
- const dir = path60.join(base, name);
44500
+ const dir = path62.join(base, name);
44293
44501
  try {
44294
- if (!fs42.statSync(dir).isDirectory()) return;
44502
+ if (!fs41.statSync(dir).isDirectory()) return;
44295
44503
  } catch {
44296
44504
  return;
44297
44505
  }
44298
- const skillMd = path60.join(dir, "SKILL.md");
44299
- if (!fs42.existsSync(skillMd)) return;
44506
+ const skillMd = path62.join(dir, "SKILL.md");
44507
+ if (!fs41.existsSync(skillMd)) return;
44300
44508
  const key = `${rel}/${name}`;
44301
44509
  if (seenKeys.has(key)) return;
44302
44510
  seenKeys.add(key);
@@ -44308,11 +44516,11 @@ async function discoverLocalSkillsAsync(cwd) {
44308
44516
  let work = 0;
44309
44517
  for (const rel of SKILL_DISCOVERY_ROOTS) {
44310
44518
  await yieldSkillDiscoveryWork(++work);
44311
- const base = path60.join(cwd, rel);
44312
- if (!fs42.existsSync(base) || !fs42.statSync(base).isDirectory()) continue;
44519
+ const base = path62.join(cwd, rel);
44520
+ if (!fs41.existsSync(base) || !fs41.statSync(base).isDirectory()) continue;
44313
44521
  let entries = [];
44314
44522
  try {
44315
- entries = fs42.readdirSync(base);
44523
+ entries = fs41.readdirSync(base);
44316
44524
  } catch {
44317
44525
  continue;
44318
44526
  }
@@ -44325,16 +44533,16 @@ async function discoverLocalSkillsAsync(cwd) {
44325
44533
  }
44326
44534
 
44327
44535
  // src/skills/discovery/discover-skill-layout-roots.ts
44328
- import fs43 from "node:fs";
44329
- import path61 from "node:path";
44536
+ import fs42 from "node:fs";
44537
+ import path63 from "node:path";
44330
44538
  function collectLayoutSkill(rel, base, name, skills2) {
44331
- const dir = path61.join(base, name);
44539
+ const dir = path63.join(base, name);
44332
44540
  try {
44333
- if (!fs43.statSync(dir).isDirectory()) return;
44541
+ if (!fs42.statSync(dir).isDirectory()) return;
44334
44542
  } catch {
44335
44543
  return;
44336
44544
  }
44337
- if (!fs43.existsSync(path61.join(dir, "SKILL.md"))) return;
44545
+ if (!fs42.existsSync(path63.join(dir, "SKILL.md"))) return;
44338
44546
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
44339
44547
  skills2.push({ name, relPath });
44340
44548
  }
@@ -44343,11 +44551,11 @@ async function discoverSkillLayoutRootsAsync(cwd) {
44343
44551
  let work = 0;
44344
44552
  for (const rel of SKILL_DISCOVERY_ROOTS) {
44345
44553
  await yieldSkillDiscoveryWork(++work);
44346
- const base = path61.join(cwd, rel);
44347
- if (!fs43.existsSync(base) || !fs43.statSync(base).isDirectory()) continue;
44554
+ const base = path63.join(cwd, rel);
44555
+ if (!fs42.existsSync(base) || !fs42.statSync(base).isDirectory()) continue;
44348
44556
  let entries = [];
44349
44557
  try {
44350
- entries = fs43.readdirSync(base);
44558
+ entries = fs42.readdirSync(base);
44351
44559
  } catch {
44352
44560
  continue;
44353
44561
  }
@@ -44435,27 +44643,6 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
44435
44643
  return `${base}/ws/bridge?${params.toString()}`;
44436
44644
  }
44437
44645
 
44438
- // src/connection/report-git-repos.ts
44439
- function reportGitRepos(getWs, log2) {
44440
- setImmediate(() => {
44441
- discoverGitRepos().then((repos) => {
44442
- if (repos.length > 0) {
44443
- const socket = getWs();
44444
- if (socket) {
44445
- sendWsMessage(socket, {
44446
- type: "git_repos",
44447
- repos: repos.map((r) => ({ absolutePath: r.absolutePath, remoteUrl: r.remoteUrl }))
44448
- });
44449
- }
44450
- }
44451
- }).catch((err) => {
44452
- log2(
44453
- `[Bridge service] Git repository discovery failed: ${err instanceof Error ? err.message : String(err)}`
44454
- );
44455
- });
44456
- });
44457
- }
44458
-
44459
44646
  // src/types/api-to-bridge-messages.ts
44460
44647
  var API_TO_BRIDGE_MESSAGE_TYPES = [
44461
44648
  "auth_token",
@@ -44712,11 +44899,11 @@ function syncRunningTurnQueueKeys(turns, queueKey) {
44712
44899
  }
44713
44900
 
44714
44901
  // src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
44715
- import fs45 from "node:fs";
44902
+ import fs44 from "node:fs";
44716
44903
 
44717
44904
  // src/git/snapshot/capture.ts
44718
- import * as fs44 from "node:fs";
44719
- import * as path62 from "node:path";
44905
+ import * as fs43 from "node:fs";
44906
+ import * as path64 from "node:path";
44720
44907
 
44721
44908
  // src/git/snapshot/git.ts
44722
44909
  async function gitStashCreate(repoRoot, log2) {
@@ -44756,7 +44943,7 @@ async function gitRun(repoRoot, args, log2, label) {
44756
44943
  async function resolveSnapshotRepoRoots(options) {
44757
44944
  const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
44758
44945
  if (worktreePaths?.length) {
44759
- const uniq = [...new Set(worktreePaths.map((p) => path62.resolve(p)))];
44946
+ const uniq = [...new Set(worktreePaths.map((p) => path64.resolve(p)))];
44760
44947
  return uniq;
44761
44948
  }
44762
44949
  try {
@@ -44764,7 +44951,7 @@ async function resolveSnapshotRepoRoots(options) {
44764
44951
  const mapped = repos.map((r) => r.absolutePath);
44765
44952
  const sid = sessionId?.trim();
44766
44953
  if (sid) {
44767
- const filtered = mapped.filter((root) => path62.basename(root) === sid);
44954
+ const filtered = mapped.filter((root) => path64.basename(root) === sid);
44768
44955
  if (filtered.length > 0) return filtered;
44769
44956
  }
44770
44957
  return mapped;
@@ -44786,7 +44973,7 @@ async function capturePreTurnSnapshot(options) {
44786
44973
  });
44787
44974
  const dir = snapshotsDirForCwd(agentCwd);
44788
44975
  try {
44789
- fs44.mkdirSync(dir, { recursive: true });
44976
+ fs43.mkdirSync(dir, { recursive: true });
44790
44977
  } catch (e) {
44791
44978
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
44792
44979
  }
@@ -44795,9 +44982,9 @@ async function capturePreTurnSnapshot(options) {
44795
44982
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
44796
44983
  repos
44797
44984
  };
44798
- const filePath = path62.join(dir, `${runId}.json`);
44985
+ const filePath = path64.join(dir, `${runId}.json`);
44799
44986
  try {
44800
- fs44.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
44987
+ fs43.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
44801
44988
  } catch (e) {
44802
44989
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
44803
44990
  }
@@ -44810,7 +44997,7 @@ async function capturePreTurnSnapshot(options) {
44810
44997
  async function applyPreTurnSnapshot(filePath, log2) {
44811
44998
  let data;
44812
44999
  try {
44813
- const raw = fs44.readFileSync(filePath, "utf8");
45000
+ const raw = fs43.readFileSync(filePath, "utf8");
44814
45001
  data = JSON.parse(raw);
44815
45002
  } catch (e) {
44816
45003
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
@@ -44850,7 +45037,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
44850
45037
  const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
44851
45038
  const file2 = snapshotFilePath(agentBase, tid);
44852
45039
  try {
44853
- await fs45.promises.access(file2, fs45.constants.F_OK);
45040
+ await fs44.promises.access(file2, fs44.constants.F_OK);
44854
45041
  } catch {
44855
45042
  deps.log(
44856
45043
  `[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
@@ -45138,9 +45325,9 @@ function parseChangeSummarySnapshots(raw) {
45138
45325
  for (const item of raw) {
45139
45326
  if (!item || typeof item !== "object") continue;
45140
45327
  const o = item;
45141
- const path73 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
45142
- if (!path73) continue;
45143
- const row = { path: path73 };
45328
+ const path75 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
45329
+ if (!path75) continue;
45330
+ const row = { path: path75 };
45144
45331
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
45145
45332
  if (typeof o.oldText === "string") row.oldText = o.oldText;
45146
45333
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -45350,8 +45537,8 @@ function randomSecret() {
45350
45537
  }
45351
45538
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
45352
45539
  }
45353
- async function requestPreviewApi(port, secret, method, path73, body) {
45354
- const url2 = `http://127.0.0.1:${port}${path73}`;
45540
+ async function requestPreviewApi(port, secret, method, path75, body) {
45541
+ const url2 = `http://127.0.0.1:${port}${path75}`;
45355
45542
  const headers = {
45356
45543
  [PREVIEW_SECRET_HEADER]: secret,
45357
45544
  "Content-Type": "application/json"
@@ -45363,7 +45550,7 @@ async function requestPreviewApi(port, secret, method, path73, body) {
45363
45550
  });
45364
45551
  const data = await res.json().catch(() => ({}));
45365
45552
  if (!res.ok) {
45366
- throw new Error(data?.error ?? `Preview API ${method} ${path73}: ${res.status}`);
45553
+ throw new Error(data?.error ?? `Preview API ${method} ${path75}: ${res.status}`);
45367
45554
  }
45368
45555
  return data;
45369
45556
  }
@@ -45539,14 +45726,14 @@ async function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId
45539
45726
  }
45540
45727
 
45541
45728
  // src/files/list-dir/index.ts
45542
- import fs47 from "node:fs";
45729
+ import fs46 from "node:fs";
45543
45730
 
45544
45731
  // src/files/ensure-under-cwd.ts
45545
- import path63 from "node:path";
45732
+ import path65 from "node:path";
45546
45733
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
45547
- const normalized = path63.normalize(relativePath).replace(/^(\.\/)+/, "");
45548
- const resolved = path63.resolve(cwd, normalized);
45549
- if (!resolved.startsWith(cwd + path63.sep) && resolved !== cwd) {
45734
+ const normalized = path65.normalize(relativePath).replace(/^(\.\/)+/, "");
45735
+ const resolved = path65.resolve(cwd, normalized);
45736
+ if (!resolved.startsWith(cwd + path65.sep) && resolved !== cwd) {
45550
45737
  return null;
45551
45738
  }
45552
45739
  return resolved;
@@ -45556,15 +45743,15 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
45556
45743
  var LIST_DIR_YIELD_EVERY = 256;
45557
45744
 
45558
45745
  // src/files/list-dir/map-dir-entry.ts
45559
- import path64 from "node:path";
45560
- import fs46 from "node:fs";
45746
+ import path66 from "node:path";
45747
+ import fs45 from "node:fs";
45561
45748
  async function mapDirEntry(d, relativePath, resolved) {
45562
- const entryPath = path64.join(relativePath || ".", d.name).replace(/\\/g, "/");
45563
- const fullPath = path64.join(resolved, d.name);
45749
+ const entryPath = path66.join(relativePath || ".", d.name).replace(/\\/g, "/");
45750
+ const fullPath = path66.join(resolved, d.name);
45564
45751
  let isDir = d.isDirectory();
45565
45752
  if (d.isSymbolicLink()) {
45566
45753
  try {
45567
- const targetStat = await fs46.promises.stat(fullPath);
45754
+ const targetStat = await fs45.promises.stat(fullPath);
45568
45755
  isDir = targetStat.isDirectory();
45569
45756
  } catch {
45570
45757
  isDir = false;
@@ -45594,7 +45781,7 @@ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
45594
45781
  return { error: "Path is outside working directory" };
45595
45782
  }
45596
45783
  try {
45597
- const names = await fs47.promises.readdir(resolved, { withFileTypes: true });
45784
+ const names = await fs46.promises.readdir(resolved, { withFileTypes: true });
45598
45785
  const entries = [];
45599
45786
  for (let i = 0; i < names.length; i++) {
45600
45787
  if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
@@ -45610,13 +45797,13 @@ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
45610
45797
  }
45611
45798
 
45612
45799
  // src/files/handle-file-browser-search.ts
45613
- import path65 from "node:path";
45800
+ import path67 from "node:path";
45614
45801
  var SEARCH_LIMIT = 100;
45615
45802
  function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
45616
45803
  void (async () => {
45617
45804
  await yieldToEventLoop();
45618
45805
  const q = typeof msg.q === "string" ? msg.q : "";
45619
- const sessionParentPath = path65.resolve(
45806
+ const sessionParentPath = path67.resolve(
45620
45807
  sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
45621
45808
  );
45622
45809
  if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
@@ -45720,6 +45907,19 @@ async function resolveCodeNavParentPath(sessionWorktreeManager, sessionId) {
45720
45907
  }
45721
45908
 
45722
45909
  // src/code-nav/handlers/prepare-code-nav-request.ts
45910
+ var CODE_NAV_INDEX_READY_WAIT_MS = 75;
45911
+ var CODE_NAV_INDEX_READY_POLL_MS = 15;
45912
+ function delay2(ms) {
45913
+ return new Promise((resolve34) => setTimeout(resolve34, ms));
45914
+ }
45915
+ async function waitForSymbolIndexReady(sessionParentPath, reqPath) {
45916
+ const deadline = Date.now() + CODE_NAV_INDEX_READY_WAIT_MS;
45917
+ do {
45918
+ if (await symbolIndexFileIsReadyAsync(sessionParentPath, reqPath)) return true;
45919
+ await delay2(CODE_NAV_INDEX_READY_POLL_MS);
45920
+ } while (Date.now() < deadline);
45921
+ return await symbolIndexFileIsReadyAsync(sessionParentPath, reqPath);
45922
+ }
45723
45923
  async function prepareCodeNavRequest(msg, socket, e2ee, sessionWorktreeManager) {
45724
45924
  await yieldToEventLoop();
45725
45925
  const reqPath = msg.path.replace(/^\/+/, "") || "";
@@ -45732,12 +45932,15 @@ async function prepareCodeNavRequest(msg, socket, e2ee, sessionWorktreeManager)
45732
45932
  return null;
45733
45933
  }
45734
45934
  const sessionParentPath = await resolveCodeNavParentPath(sessionWorktreeManager, msg.sessionId);
45735
- registerSymbolIndexParent(sessionParentPath);
45736
- await ensureSymbolIndexFileAsync(sessionParentPath, reqPath, { importDepth: 1 });
45935
+ await registerSymbolIndexParentAsync(sessionParentPath);
45936
+ scheduleSymbolIndexFile(sessionParentPath, reqPath);
45937
+ const indexReady = await waitForSymbolIndexReady(sessionParentPath, reqPath);
45938
+ if (indexReady) {
45939
+ const importTargets = await listDirectImportTargetsForFileAsync(sessionParentPath, reqPath);
45940
+ scheduleSymbolIndexImportNeighbors(sessionParentPath, importTargets);
45941
+ }
45942
+ scheduleSymbolIndexWarmup(sessionParentPath);
45737
45943
  await yieldToEventLoop();
45738
- const importTargets = listDirectImportTargetsForFile(sessionParentPath, reqPath);
45739
- scheduleSymbolIndexForRequest(sessionParentPath, reqPath, importTargets);
45740
- const indexReady = symbolIndexFileIsReady(sessionParentPath, reqPath);
45741
45944
  return {
45742
45945
  socket,
45743
45946
  e2ee,
@@ -45801,20 +46004,22 @@ function handleCodeNavRequest(msg, socket, e2ee, sessionWorktreeManager) {
45801
46004
  // src/code-nav/handlers/trigger-symbol-index-build.ts
45802
46005
  function triggerSymbolIndexBuild(parentPath) {
45803
46006
  setImmediate(() => {
45804
- const resolved = normalizeResolvedPath(parentPath);
45805
- registerSymbolIndexParent(resolved);
45806
- scheduleSymbolIndexWarmup(resolved);
46007
+ void (async () => {
46008
+ const resolved = normalizeResolvedPath(parentPath);
46009
+ await registerSymbolIndexParentAsync(resolved);
46010
+ scheduleSymbolIndexWarmup(resolved);
46011
+ })();
45807
46012
  });
45808
46013
  }
45809
46014
 
45810
46015
  // src/git/tree/resolve-repo-abs-path.ts
45811
- import * as path66 from "node:path";
46016
+ import * as path68 from "node:path";
45812
46017
  function resolveRepoAbsPathFromBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
45813
- const bridgeResolved = path66.resolve(bridgeRoot);
46018
+ const bridgeResolved = path68.resolve(bridgeRoot);
45814
46019
  const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
45815
- const repoPath = rel === "" ? bridgeResolved : path66.join(bridgeResolved, rel);
45816
- const resolved = path66.resolve(repoPath);
45817
- if (!resolved.startsWith(bridgeResolved + path66.sep) && resolved !== bridgeResolved) {
46020
+ const repoPath = rel === "" ? bridgeResolved : path68.join(bridgeResolved, rel);
46021
+ const resolved = path68.resolve(repoPath);
46022
+ if (!resolved.startsWith(bridgeResolved + path68.sep) && resolved !== bridgeResolved) {
45818
46023
  return null;
45819
46024
  }
45820
46025
  return resolved;
@@ -46212,18 +46417,18 @@ async function handleFileBrowserList(socket, e2ee, id, reqPath, sessionParentPat
46212
46417
  }
46213
46418
 
46214
46419
  // src/files/read-file/resolve-file-path.ts
46215
- import fs48 from "node:fs";
46420
+ import fs47 from "node:fs";
46216
46421
  async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
46217
46422
  const resolved = ensureUnderCwd(relativePath, sessionParentPath);
46218
46423
  if (!resolved) return { error: "Path is outside working directory" };
46219
46424
  let real;
46220
46425
  try {
46221
- real = await fs48.promises.realpath(resolved);
46426
+ real = await fs47.promises.realpath(resolved);
46222
46427
  } catch {
46223
46428
  real = resolved;
46224
46429
  }
46225
46430
  try {
46226
- const stat3 = await fs48.promises.stat(real);
46431
+ const stat3 = await fs47.promises.stat(real);
46227
46432
  if (!stat3.isFile()) return { error: "Not a file" };
46228
46433
  return real;
46229
46434
  } catch (err) {
@@ -46232,11 +46437,11 @@ async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeR
46232
46437
  }
46233
46438
 
46234
46439
  // src/files/read-file/read-file-range-async.ts
46235
- import fs49 from "node:fs";
46440
+ import fs48 from "node:fs";
46236
46441
  import { StringDecoder as StringDecoder2 } from "node:string_decoder";
46237
46442
  async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
46238
- const fileSize = (await fs49.promises.stat(filePath)).size;
46239
- const fd = await fs49.promises.open(filePath, "r");
46443
+ const fileSize = (await fs48.promises.stat(filePath)).size;
46444
+ const fd = await fs48.promises.open(filePath, "r");
46240
46445
  const bufSize = 64 * 1024;
46241
46446
  const buf = Buffer.alloc(bufSize);
46242
46447
  const decoder = new StringDecoder2("utf8");
@@ -46398,11 +46603,11 @@ async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, li
46398
46603
  }
46399
46604
 
46400
46605
  // src/files/read-file/read-file-buffer-full-async.ts
46401
- import fs50 from "node:fs";
46606
+ import fs49 from "node:fs";
46402
46607
  var READ_CHUNK_BYTES = 256 * 1024;
46403
46608
  async function readFileBufferFullAsync(filePath) {
46404
- const stat3 = await fs50.promises.stat(filePath);
46405
- const fd = await fs50.promises.open(filePath, "r");
46609
+ const stat3 = await fs49.promises.stat(filePath);
46610
+ const fd = await fs49.promises.open(filePath, "r");
46406
46611
  const chunks = [];
46407
46612
  let position = 0;
46408
46613
  let bytesSinceYield = 0;
@@ -46596,8 +46801,8 @@ function isValidRemoteSkillInstallItem(item) {
46596
46801
  }
46597
46802
 
46598
46803
  // src/skills/install/install-remote-skills-async.ts
46599
- import fs51 from "node:fs";
46600
- import path67 from "node:path";
46804
+ import fs50 from "node:fs";
46805
+ import path69 from "node:path";
46601
46806
 
46602
46807
  // src/skills/install/constants.ts
46603
46808
  var INSTALL_SKILLS_YIELD_EVERY = 16;
@@ -46608,12 +46813,12 @@ async function writeInstallFileAsync(skillDir, f, filesWritten) {
46608
46813
  if (++filesWritten.count % INSTALL_SKILLS_YIELD_EVERY === 0) {
46609
46814
  await yieldToEventLoop();
46610
46815
  }
46611
- const dest = path67.join(skillDir, f.path);
46612
- await fs51.promises.mkdir(path67.dirname(dest), { recursive: true });
46816
+ const dest = path69.join(skillDir, f.path);
46817
+ await fs50.promises.mkdir(path69.dirname(dest), { recursive: true });
46613
46818
  if (f.text !== void 0) {
46614
- await fs51.promises.writeFile(dest, f.text, "utf8");
46819
+ await fs50.promises.writeFile(dest, f.text, "utf8");
46615
46820
  } else if (f.base64) {
46616
- await fs51.promises.writeFile(dest, Buffer.from(f.base64, "base64"));
46821
+ await fs50.promises.writeFile(dest, Buffer.from(f.base64, "base64"));
46617
46822
  }
46618
46823
  }
46619
46824
  async function installRemoteSkillsAsync(cwd, targetDir, items) {
@@ -46625,7 +46830,7 @@ async function installRemoteSkillsAsync(cwd, targetDir, items) {
46625
46830
  try {
46626
46831
  for (const item of items) {
46627
46832
  if (!isValidRemoteSkillInstallItem(item)) continue;
46628
- const skillDir = path67.join(cwd, targetDir, item.skillName);
46833
+ const skillDir = path69.join(cwd, targetDir, item.skillName);
46629
46834
  for (const f of item.files) {
46630
46835
  await writeInstallFileAsync(skillDir, f, filesWritten);
46631
46836
  }
@@ -46907,7 +47112,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
46907
47112
  };
46908
47113
 
46909
47114
  // src/routing/handlers/revert-turn-snapshot.ts
46910
- import * as fs52 from "node:fs";
47115
+ import * as fs51 from "node:fs";
46911
47116
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
46912
47117
  const id = typeof msg.id === "string" ? msg.id : "";
46913
47118
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -46920,7 +47125,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
46920
47125
  const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
46921
47126
  const file2 = snapshotFilePath(agentBase, turnId);
46922
47127
  try {
46923
- await fs52.promises.access(file2, fs52.constants.F_OK);
47128
+ await fs51.promises.access(file2, fs51.constants.F_OK);
46924
47129
  } catch {
46925
47130
  sendWsMessage(s, {
46926
47131
  type: "revert_turn_snapshot_result",
@@ -46964,7 +47169,7 @@ var handleDevServersConfig = (msg, deps) => {
46964
47169
  };
46965
47170
 
46966
47171
  // src/git/bridge-git-context.ts
46967
- import * as path68 from "node:path";
47172
+ import * as path70 from "node:path";
46968
47173
 
46969
47174
  // src/git/branches/get-current-branch.ts
46970
47175
  async function getCurrentBranch(repoPath) {
@@ -47014,12 +47219,12 @@ async function listRepoBranchRefs(repoPath) {
47014
47219
  // src/git/bridge-git-context.ts
47015
47220
  function folderNameForRelPath(relPath, bridgeRoot) {
47016
47221
  if (relPath === "." || relPath === "") {
47017
- return path68.basename(path68.resolve(bridgeRoot)) || "repo";
47222
+ return path70.basename(path70.resolve(bridgeRoot)) || "repo";
47018
47223
  }
47019
- return path68.basename(relPath) || relPath;
47224
+ return path70.basename(relPath) || relPath;
47020
47225
  }
47021
47226
  async function discoverGitReposForBridgeContext(bridgeRoot) {
47022
- const root = path68.resolve(bridgeRoot);
47227
+ const root = path70.resolve(bridgeRoot);
47023
47228
  if (await isGitRepoDirectory(root)) {
47024
47229
  const remoteUrl = await getRemoteOriginUrl(root);
47025
47230
  return [{ absolutePath: root, remoteUrl }];
@@ -47027,19 +47232,19 @@ async function discoverGitReposForBridgeContext(bridgeRoot) {
47027
47232
  const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
47028
47233
  const byPath = /* @__PURE__ */ new Map();
47029
47234
  for (const repo of [...deep, ...shallow]) {
47030
- byPath.set(path68.resolve(repo.absolutePath), repo);
47235
+ byPath.set(path70.resolve(repo.absolutePath), repo);
47031
47236
  }
47032
47237
  return [...byPath.values()];
47033
47238
  }
47034
47239
  async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
47035
- const bridgeResolved = path68.resolve(bridgeRoot);
47240
+ const bridgeResolved = path70.resolve(bridgeRoot);
47036
47241
  const repos = await discoverGitReposForBridgeContext(bridgeResolved);
47037
47242
  const rows = [];
47038
47243
  for (let i = 0; i < repos.length; i++) {
47039
47244
  if (i > 0) await yieldToEventLoop();
47040
47245
  const repo = repos[i];
47041
- let rel = path68.relative(bridgeResolved, repo.absolutePath);
47042
- if (rel.startsWith("..") || path68.isAbsolute(rel)) continue;
47246
+ let rel = path70.relative(bridgeResolved, repo.absolutePath);
47247
+ if (rel.startsWith("..") || path70.isAbsolute(rel)) continue;
47043
47248
  const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
47044
47249
  const currentBranch = await getCurrentBranch(repo.absolutePath);
47045
47250
  const remoteUrl = repo.remoteUrl.trim() || null;
@@ -47054,11 +47259,11 @@ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
47054
47259
  return rows;
47055
47260
  }
47056
47261
  async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
47057
- const bridgeResolved = path68.resolve(bridgeRoot);
47262
+ const bridgeResolved = path70.resolve(bridgeRoot);
47058
47263
  const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
47059
- const repoPath = rel === "" ? bridgeResolved : path68.join(bridgeResolved, rel);
47060
- const resolved = path68.resolve(repoPath);
47061
- if (!resolved.startsWith(bridgeResolved + path68.sep) && resolved !== bridgeResolved) {
47264
+ const repoPath = rel === "" ? bridgeResolved : path70.join(bridgeResolved, rel);
47265
+ const resolved = path70.resolve(repoPath);
47266
+ if (!resolved.startsWith(bridgeResolved + path70.sep) && resolved !== bridgeResolved) {
47062
47267
  return [];
47063
47268
  }
47064
47269
  return listRepoBranchRefs(resolved);
@@ -47227,25 +47432,127 @@ async function refreshBridgeTokens(params) {
47227
47432
  }
47228
47433
  }
47229
47434
 
47230
- // src/connection/main-bridge-ws-lifecycle.ts
47231
- function createMainBridgeWebSocketLifecycle(params) {
47435
+ // src/connection/main-bridge-auth-refresh-handler.ts
47436
+ function resetAfterAuthInvalid(params) {
47437
+ const { state } = params;
47438
+ state.reconnectAttempt = 0;
47439
+ resetReconnectOutageTracker(state.mainOutage);
47440
+ state.lastReconnectCloseMeta = null;
47441
+ }
47442
+ function createMainBridgeAuthRefreshHandler(params, connect) {
47443
+ const { apiUrl, workspaceId, logFn, tokens, persistTokens, onAuthInvalid } = params;
47444
+ let authRefreshInFlight = false;
47445
+ return () => {
47446
+ if (authRefreshInFlight) return;
47447
+ void (async () => {
47448
+ authRefreshInFlight = true;
47449
+ try {
47450
+ if (tokens.refreshToken) {
47451
+ const next = await refreshBridgeTokens({
47452
+ apiUrl,
47453
+ workspaceId,
47454
+ refreshToken: tokens.refreshToken
47455
+ });
47456
+ if (next?.token && next.refreshToken) {
47457
+ tokens.accessToken = next.token;
47458
+ tokens.refreshToken = next.refreshToken;
47459
+ persistTokens?.({ token: tokens.accessToken, refreshToken: tokens.refreshToken });
47460
+ try {
47461
+ logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
47462
+ } catch {
47463
+ }
47464
+ resetAfterAuthInvalid(params);
47465
+ params.state.logBridgeOpenAsReconnect = true;
47466
+ authRefreshInFlight = false;
47467
+ connect();
47468
+ return;
47469
+ }
47470
+ }
47471
+ authRefreshInFlight = false;
47472
+ resetAfterAuthInvalid(params);
47473
+ onAuthInvalid();
47474
+ } catch {
47475
+ authRefreshInFlight = false;
47476
+ resetAfterAuthInvalid(params);
47477
+ onAuthInvalid();
47478
+ }
47479
+ })();
47480
+ };
47481
+ }
47482
+
47483
+ // src/connection/main-bridge-ws-close-handler.ts
47484
+ function createMainBridgeCloseHandler(params, connect) {
47485
+ const { state, logFn, bridgeHeartbeat } = params;
47486
+ return (code, reason) => {
47487
+ bridgeHeartbeat?.stop();
47488
+ try {
47489
+ const was = state.currentWs;
47490
+ state.currentWs = null;
47491
+ if (was) was.removeAllListeners();
47492
+ const willReconnect = !state.closedByUser;
47493
+ if (willReconnect) {
47494
+ const duplicateResult = evaluateDuplicateBridgeDisconnect(
47495
+ state.duplicateBridgeFlap,
47496
+ Date.now(),
47497
+ code,
47498
+ reason
47499
+ );
47500
+ logDuplicateBridgeWarning(logFn, duplicateResult);
47501
+ }
47502
+ beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
47503
+ if (willReconnect) {
47504
+ state.lastReconnectCloseMeta = { code, reason };
47505
+ try {
47506
+ scheduleMainBridgeReconnect(state, connect, logFn, { code, reason });
47507
+ } catch {
47508
+ }
47509
+ }
47510
+ } catch {
47511
+ if (!state.closedByUser) {
47512
+ try {
47513
+ scheduleMainBridgeReconnect(state, connect, logFn);
47514
+ } catch {
47515
+ }
47516
+ }
47517
+ }
47518
+ };
47519
+ }
47520
+
47521
+ // src/connection/report-git-repos.ts
47522
+ function reportGitRepos(getWs, log2) {
47523
+ setImmediate(() => {
47524
+ discoverGitRepos().then((repos) => {
47525
+ if (repos.length > 0) {
47526
+ const socket = getWs();
47527
+ if (socket) {
47528
+ sendWsMessage(socket, {
47529
+ type: "git_repos",
47530
+ repos: repos.map((r) => ({ absolutePath: r.absolutePath, remoteUrl: r.remoteUrl }))
47531
+ });
47532
+ }
47533
+ }
47534
+ }).catch((err) => {
47535
+ log2(
47536
+ `[Bridge service] Git repository discovery failed: ${err instanceof Error ? err.message : String(err)}`
47537
+ );
47538
+ });
47539
+ });
47540
+ }
47541
+
47542
+ // src/connection/main-bridge-ws-open-handler.ts
47543
+ function createMainBridgeOpenHandler(params) {
47232
47544
  const {
47233
47545
  state,
47234
47546
  getWs,
47235
- apiUrl,
47236
47547
  workspaceId,
47237
47548
  justAuthenticated,
47238
47549
  logFn,
47239
- messageDeps,
47240
47550
  tokens,
47241
- persistTokens,
47242
- onAuthInvalid,
47243
47551
  e2ee,
47244
47552
  identifyReportedPaths,
47245
- bridgeHeartbeat
47553
+ onBridgeSocketOpen
47246
47554
  } = params;
47247
- let authRefreshInFlight = false;
47248
- function handleOpen() {
47555
+ return () => {
47249
47556
  recordBridgeSessionOpened(state.duplicateBridgeFlap, Date.now());
47250
47557
  const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
47251
47558
  clearMainBridgeReconnectQuietOnOpen(state, logFn);
@@ -47265,6 +47572,10 @@ function createMainBridgeWebSocketLifecycle(params) {
47265
47572
  ...e2ee ? { e: e2ee.handshake } : {}
47266
47573
  });
47267
47574
  reportGitRepos(getWs, logFn);
47575
+ try {
47576
+ onBridgeSocketOpen?.();
47577
+ } catch {
47578
+ }
47268
47579
  }
47269
47580
  if (justAuthenticated && socket) {
47270
47581
  logFn(
@@ -47273,41 +47584,25 @@ function createMainBridgeWebSocketLifecycle(params) {
47273
47584
  logFn(` export BUILDAUTOMATON_AUTH_TOKEN="${tokens.accessToken}"`);
47274
47585
  logFn(` export BUILDAUTOMATON_WORKSPACE_ID="${workspaceId}"`);
47275
47586
  }
47276
- }
47277
- function handleClose(code, reason) {
47278
- bridgeHeartbeat?.stop();
47279
- try {
47280
- const was = state.currentWs;
47281
- state.currentWs = null;
47282
- if (was) was.removeAllListeners();
47283
- const willReconnect = !state.closedByUser;
47284
- if (willReconnect) {
47285
- const duplicateResult = evaluateDuplicateBridgeDisconnect(
47286
- state.duplicateBridgeFlap,
47287
- Date.now(),
47288
- code,
47289
- reason
47290
- );
47291
- logDuplicateBridgeWarning(logFn, duplicateResult);
47292
- }
47293
- beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
47294
- if (willReconnect) {
47295
- state.lastReconnectCloseMeta = { code, reason };
47296
- try {
47297
- scheduleMainBridgeReconnect(state, connect, logFn, { code, reason });
47298
- } catch {
47299
- }
47300
- }
47301
- } catch {
47302
- if (!state.closedByUser) {
47303
- try {
47304
- scheduleMainBridgeReconnect(state, connect, logFn);
47305
- } catch {
47306
- }
47307
- }
47308
- }
47309
- }
47310
- function connect() {
47587
+ };
47588
+ }
47589
+
47590
+ // src/connection/main-bridge-ws-lifecycle.ts
47591
+ function createMainBridgeWebSocketLifecycle(params) {
47592
+ const {
47593
+ state,
47594
+ apiUrl,
47595
+ workspaceId,
47596
+ logFn,
47597
+ messageDeps,
47598
+ tokens,
47599
+ bridgeHeartbeat
47600
+ } = params;
47601
+ let connect;
47602
+ const handleOpen = createMainBridgeOpenHandler(params);
47603
+ const handleClose = createMainBridgeCloseHandler(params, () => connect());
47604
+ const handleAuthInvalid = createMainBridgeAuthRefreshHandler(params, () => connect());
47605
+ connect = () => {
47311
47606
  if (state.closedByUser) return;
47312
47607
  if (state.reconnectTimeout != null) {
47313
47608
  clearTimeout(state.reconnectTimeout);
@@ -47343,48 +47638,7 @@ function createMainBridgeWebSocketLifecycle(params) {
47343
47638
  onCompactHeartbeatAck: (seq) => {
47344
47639
  messageDeps.onBridgeHeartbeatAck?.(seq);
47345
47640
  },
47346
- onAuthInvalid: () => {
47347
- if (authRefreshInFlight) return;
47348
- void (async () => {
47349
- authRefreshInFlight = true;
47350
- try {
47351
- if (tokens.refreshToken) {
47352
- const next = await refreshBridgeTokens({
47353
- apiUrl,
47354
- workspaceId,
47355
- refreshToken: tokens.refreshToken
47356
- });
47357
- if (next?.token && next.refreshToken) {
47358
- tokens.accessToken = next.token;
47359
- tokens.refreshToken = next.refreshToken;
47360
- persistTokens?.({ token: tokens.accessToken, refreshToken: tokens.refreshToken });
47361
- try {
47362
- logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
47363
- } catch {
47364
- }
47365
- state.reconnectAttempt = 0;
47366
- resetReconnectOutageTracker(state.mainOutage);
47367
- state.lastReconnectCloseMeta = null;
47368
- state.logBridgeOpenAsReconnect = true;
47369
- authRefreshInFlight = false;
47370
- connect();
47371
- return;
47372
- }
47373
- }
47374
- authRefreshInFlight = false;
47375
- state.reconnectAttempt = 0;
47376
- resetReconnectOutageTracker(state.mainOutage);
47377
- state.lastReconnectCloseMeta = null;
47378
- onAuthInvalid();
47379
- } catch {
47380
- authRefreshInFlight = false;
47381
- state.reconnectAttempt = 0;
47382
- resetReconnectOutageTracker(state.mainOutage);
47383
- state.lastReconnectCloseMeta = null;
47384
- onAuthInvalid();
47385
- }
47386
- })();
47387
- },
47641
+ onAuthInvalid: handleAuthInvalid,
47388
47642
  onOpen: handleOpen,
47389
47643
  onClose: handleClose,
47390
47644
  onError: (err) => {
@@ -47411,7 +47665,7 @@ function createMainBridgeWebSocketLifecycle(params) {
47411
47665
  }
47412
47666
  }
47413
47667
  }
47414
- }
47668
+ };
47415
47669
  return { connect };
47416
47670
  }
47417
47671
 
@@ -47501,256 +47755,6 @@ function createBridgeHeartbeatController(params) {
47501
47755
  };
47502
47756
  }
47503
47757
 
47504
- // src/sqlite/hash-json-sha256.ts
47505
- import { createHash as createHash2 } from "node:crypto";
47506
- function hashJsonUtf8Sha256(value) {
47507
- return createHash2("sha256").update(JSON.stringify(value), "utf8").digest("hex");
47508
- }
47509
-
47510
- // src/sqlite/agent-capability-cache.ts
47511
- function hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType) {
47512
- const t = agentType.trim();
47513
- if (!t) return false;
47514
- try {
47515
- const row = db.get(
47516
- `SELECT config_options_json FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
47517
- [workspaceId, t]
47518
- );
47519
- if (row?.config_options_json == null || row.config_options_json === "") return false;
47520
- const parsed = JSON.parse(row.config_options_json);
47521
- return Array.isArray(parsed) && parsed.length > 0;
47522
- } catch {
47523
- return false;
47524
- }
47525
- }
47526
- function upsertCliAgentCapabilityCache(db, row) {
47527
- const t = row.agentType.trim();
47528
- if (!t) return false;
47529
- const hash = hashJsonUtf8Sha256(row.configOptions);
47530
- try {
47531
- const prev = db.get(
47532
- `SELECT content_hash FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
47533
- [row.workspaceId, t]
47534
- );
47535
- if (prev?.content_hash === hash) return false;
47536
- } catch {
47537
- }
47538
- const json2 = JSON.stringify(row.configOptions);
47539
- const now = (/* @__PURE__ */ new Date()).toISOString();
47540
- db.run(
47541
- `INSERT INTO agent_capabilities (workspace_id, agent_type, config_options_json, content_hash, updated_at)
47542
- VALUES (?, ?, ?, ?, ?)
47543
- ON CONFLICT(workspace_id, agent_type) DO UPDATE SET
47544
- config_options_json = excluded.config_options_json,
47545
- content_hash = excluded.content_hash,
47546
- updated_at = excluded.updated_at`,
47547
- [row.workspaceId, t, json2, hash, now]
47548
- );
47549
- return true;
47550
- }
47551
- function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
47552
- const rows = db.all(
47553
- `SELECT agent_type, config_options_json FROM agent_capabilities WHERE workspace_id = ?`,
47554
- [workspaceId]
47555
- );
47556
- const out = [];
47557
- for (const r of rows) {
47558
- try {
47559
- const parsed = JSON.parse(r.config_options_json);
47560
- if (!Array.isArray(parsed) || parsed.length === 0) continue;
47561
- out.push({ agentType: r.agent_type, configOptions: parsed });
47562
- } catch {
47563
- }
47564
- }
47565
- return out;
47566
- }
47567
-
47568
- // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
47569
- import * as path70 from "node:path";
47570
-
47571
- // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
47572
- import * as path69 from "node:path";
47573
- async function probeOneAgentTypeForCapabilities(params) {
47574
- const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true, shouldContinue } = params;
47575
- const canContinue = () => shouldContinue?.() !== false;
47576
- if (!canContinue()) return false;
47577
- const resolved = resolveAgentCommand(agentType);
47578
- if (!resolved) return false;
47579
- let sqliteChanged = false;
47580
- const reportedRef = { done: false };
47581
- const tryReport = (co) => {
47582
- if (reportedRef.done) return;
47583
- if (!Array.isArray(co) || co.length === 0) return;
47584
- reportedRef.done = true;
47585
- let changed = false;
47586
- try {
47587
- changed = withCliSqliteSync(
47588
- (db) => upsertCliAgentCapabilityCache(db, {
47589
- workspaceId,
47590
- agentType,
47591
- configOptions: co
47592
- })
47593
- );
47594
- } catch {
47595
- }
47596
- sqliteChanged ||= changed;
47597
- if (bridgeReport && changed) {
47598
- reportAgentCapabilities?.({ agentType, configOptions: co });
47599
- }
47600
- };
47601
- let handle = null;
47602
- const killTimer = setTimeout(() => {
47603
- void handle?.disconnectGracefully();
47604
- }, 28e3);
47605
- killTimer.unref?.();
47606
- try {
47607
- if (!canContinue()) return false;
47608
- handle = await resolved.createClient({
47609
- command: resolved.command,
47610
- cwd: path69.resolve(cwd),
47611
- backendAgentType: agentType,
47612
- sessionMode: "agent",
47613
- persistedAcpSessionId: null,
47614
- agentConfig: null,
47615
- getActiveConfigOptions: () => null,
47616
- onAcpSessionEstablished: (info) => {
47617
- tryReport(info.configOptions ?? null);
47618
- },
47619
- onAcpConfigOptionsUpdated: (co) => {
47620
- tryReport(co);
47621
- },
47622
- onAgentSubprocessExit: () => {
47623
- },
47624
- onSessionUpdate: () => {
47625
- }
47626
- });
47627
- if (!await delayMsUnlessShutdownRequested(1200) || !canContinue()) return false;
47628
- } catch (e) {
47629
- log2(
47630
- `[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
47631
- );
47632
- } finally {
47633
- clearTimeout(killTimer);
47634
- try {
47635
- await handle?.disconnectGracefully();
47636
- } catch {
47637
- }
47638
- }
47639
- return sqliteChanged;
47640
- }
47641
-
47642
- // src/agents/capabilities/probe-agent-capabilities-for-types.ts
47643
- async function probeAgentCapabilitiesForDetectedTypes(params) {
47644
- const {
47645
- agentTypes,
47646
- cwd,
47647
- workspaceId,
47648
- log: log2,
47649
- reportAgentCapabilities,
47650
- bridgeReport = true,
47651
- forceAllTypes = false,
47652
- shouldContinue
47653
- } = params;
47654
- const canContinue = () => !isCliImmediateShutdownRequested() && shouldContinue?.() !== false;
47655
- let changedCount = 0;
47656
- for (let i = 0; i < agentTypes.length; i++) {
47657
- if (!canContinue()) return changedCount;
47658
- if (i > 0) await yieldToEventLoop();
47659
- const agentType = agentTypes[i];
47660
- if (!agentType.trim()) continue;
47661
- if (!forceAllTypes) {
47662
- try {
47663
- if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" && await withCliSqlite((db) => hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType))) {
47664
- continue;
47665
- }
47666
- } catch {
47667
- }
47668
- }
47669
- const changed = await probeOneAgentTypeForCapabilities({
47670
- agentType,
47671
- cwd,
47672
- workspaceId,
47673
- log: log2,
47674
- reportAgentCapabilities,
47675
- bridgeReport,
47676
- shouldContinue: canContinue
47677
- });
47678
- if (changed) changedCount += 1;
47679
- }
47680
- return changedCount;
47681
- }
47682
-
47683
- // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
47684
- async function warmupAgentCapabilitiesOnConnect(params) {
47685
- const { workspaceId, log: log2, getWs } = params;
47686
- const isCurrent = beginAgentCapabilityWarmupRun();
47687
- if (!isCurrent()) return;
47688
- const cwd = path70.resolve(getBridgeRoot());
47689
- async function sendBatchFromCache() {
47690
- if (!isCurrent()) return;
47691
- const socket = getWs();
47692
- if (!socket || socket.readyState !== wrapper_default.OPEN) return;
47693
- try {
47694
- const rows = await withCliSqlite((db) => listCliAgentCapabilityCacheForWorkspace(db, workspaceId));
47695
- if (!isCurrent()) return;
47696
- if (rows.length === 0) return;
47697
- sendWsMessage(socket, {
47698
- type: "agent_capabilities_batch",
47699
- items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
47700
- });
47701
- } catch (e) {
47702
- if (e instanceof CliSqliteInterrupted) return;
47703
- log2(
47704
- `[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
47705
- );
47706
- }
47707
- }
47708
- await sendBatchFromCache();
47709
- if (!isCurrent()) return;
47710
- let types = [];
47711
- try {
47712
- types = [...await detectLocalAgentTypes()];
47713
- } catch (e) {
47714
- log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
47715
- }
47716
- if (!isCurrent()) return;
47717
- try {
47718
- const n = await probeAgentCapabilitiesForDetectedTypes({
47719
- agentTypes: types,
47720
- cwd,
47721
- workspaceId,
47722
- log: log2,
47723
- bridgeReport: false,
47724
- forceAllTypes: false,
47725
- shouldContinue: isCurrent
47726
- });
47727
- if (n > 0) await sendBatchFromCache();
47728
- } catch (e) {
47729
- if (e instanceof CliSqliteInterrupted) return;
47730
- log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
47731
- }
47732
- if (!isCurrent()) return;
47733
- void (async () => {
47734
- try {
47735
- await yieldToEventLoop();
47736
- if (!isCurrent()) return;
47737
- const n = await probeAgentCapabilitiesForDetectedTypes({
47738
- agentTypes: types,
47739
- cwd,
47740
- workspaceId,
47741
- log: log2,
47742
- bridgeReport: false,
47743
- forceAllTypes: true,
47744
- shouldContinue: isCurrent
47745
- });
47746
- if (n > 0) await sendBatchFromCache();
47747
- } catch (e) {
47748
- if (e instanceof CliSqliteInterrupted) return;
47749
- log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
47750
- }
47751
- })();
47752
- }
47753
-
47754
47758
  // src/mcp/tools/session-history/fork/mcp-text-result.ts
47755
47759
  function mcpTextResult(text, isError = false) {
47756
47760
  return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
@@ -48135,6 +48139,327 @@ async function createBridgeAccessState(options = {}) {
48135
48139
  };
48136
48140
  }
48137
48141
 
48142
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
48143
+ import * as path72 from "node:path";
48144
+
48145
+ // src/sqlite/hash-json-sha256.ts
48146
+ import { createHash as createHash2 } from "node:crypto";
48147
+ function hashJsonUtf8Sha256(value) {
48148
+ return createHash2("sha256").update(JSON.stringify(value), "utf8").digest("hex");
48149
+ }
48150
+
48151
+ // src/sqlite/agent-capability-cache.ts
48152
+ function hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType) {
48153
+ const t = agentType.trim();
48154
+ if (!t) return false;
48155
+ try {
48156
+ const row = db.get(
48157
+ `SELECT config_options_json FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
48158
+ [workspaceId, t]
48159
+ );
48160
+ if (row?.config_options_json == null || row.config_options_json === "") return false;
48161
+ const parsed = JSON.parse(row.config_options_json);
48162
+ return Array.isArray(parsed) && parsed.length > 0;
48163
+ } catch {
48164
+ return false;
48165
+ }
48166
+ }
48167
+ function upsertCliAgentCapabilityCache(db, row) {
48168
+ const t = row.agentType.trim();
48169
+ if (!t) return false;
48170
+ const hash = hashJsonUtf8Sha256(row.configOptions);
48171
+ try {
48172
+ const prev = db.get(
48173
+ `SELECT content_hash FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
48174
+ [row.workspaceId, t]
48175
+ );
48176
+ if (prev?.content_hash === hash) return false;
48177
+ } catch {
48178
+ }
48179
+ const json2 = JSON.stringify(row.configOptions);
48180
+ const now = (/* @__PURE__ */ new Date()).toISOString();
48181
+ db.run(
48182
+ `INSERT INTO agent_capabilities (workspace_id, agent_type, config_options_json, content_hash, updated_at)
48183
+ VALUES (?, ?, ?, ?, ?)
48184
+ ON CONFLICT(workspace_id, agent_type) DO UPDATE SET
48185
+ config_options_json = excluded.config_options_json,
48186
+ content_hash = excluded.content_hash,
48187
+ updated_at = excluded.updated_at`,
48188
+ [row.workspaceId, t, json2, hash, now]
48189
+ );
48190
+ return true;
48191
+ }
48192
+ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
48193
+ const rows = db.all(
48194
+ `SELECT agent_type, config_options_json FROM agent_capabilities WHERE workspace_id = ?`,
48195
+ [workspaceId]
48196
+ );
48197
+ const out = [];
48198
+ for (const r of rows) {
48199
+ try {
48200
+ const parsed = JSON.parse(r.config_options_json);
48201
+ if (!Array.isArray(parsed) || parsed.length === 0) continue;
48202
+ out.push({ agentType: r.agent_type, configOptions: parsed });
48203
+ } catch {
48204
+ }
48205
+ }
48206
+ return out;
48207
+ }
48208
+
48209
+ // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
48210
+ import * as path71 from "node:path";
48211
+ async function probeOneAgentTypeForCapabilities(params) {
48212
+ const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true, shouldContinue } = params;
48213
+ const canContinue = () => shouldContinue?.() !== false;
48214
+ if (!canContinue()) return false;
48215
+ const resolved = resolveAgentCommand(agentType);
48216
+ if (!resolved) return false;
48217
+ let sqliteChanged = false;
48218
+ const reportedRef = { done: false };
48219
+ const tryReport = (co) => {
48220
+ if (reportedRef.done) return;
48221
+ if (!Array.isArray(co) || co.length === 0) return;
48222
+ reportedRef.done = true;
48223
+ let changed = false;
48224
+ try {
48225
+ changed = withCliSqliteSync(
48226
+ (db) => upsertCliAgentCapabilityCache(db, {
48227
+ workspaceId,
48228
+ agentType,
48229
+ configOptions: co
48230
+ })
48231
+ );
48232
+ } catch {
48233
+ }
48234
+ sqliteChanged ||= changed;
48235
+ if (bridgeReport && changed) {
48236
+ reportAgentCapabilities?.({ agentType, configOptions: co });
48237
+ }
48238
+ };
48239
+ let handle = null;
48240
+ const killTimer = setTimeout(() => {
48241
+ void handle?.disconnectGracefully();
48242
+ }, 28e3);
48243
+ killTimer.unref?.();
48244
+ try {
48245
+ if (!canContinue()) return false;
48246
+ handle = await resolved.createClient({
48247
+ command: resolved.command,
48248
+ cwd: path71.resolve(cwd),
48249
+ backendAgentType: agentType,
48250
+ sessionMode: "agent",
48251
+ persistedAcpSessionId: null,
48252
+ agentConfig: null,
48253
+ getActiveConfigOptions: () => null,
48254
+ onAcpSessionEstablished: (info) => {
48255
+ tryReport(info.configOptions ?? null);
48256
+ },
48257
+ onAcpConfigOptionsUpdated: (co) => {
48258
+ tryReport(co);
48259
+ },
48260
+ onAgentSubprocessExit: () => {
48261
+ },
48262
+ onSessionUpdate: () => {
48263
+ }
48264
+ });
48265
+ if (!await delayMsUnlessShutdownRequested(1200) || !canContinue()) return false;
48266
+ } catch (e) {
48267
+ log2(
48268
+ `[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
48269
+ );
48270
+ } finally {
48271
+ clearTimeout(killTimer);
48272
+ try {
48273
+ await handle?.disconnectGracefully();
48274
+ } catch {
48275
+ }
48276
+ }
48277
+ return sqliteChanged;
48278
+ }
48279
+
48280
+ // src/agents/capabilities/probe-agent-capabilities-for-types.ts
48281
+ async function probeAgentCapabilitiesForDetectedTypes(params) {
48282
+ const {
48283
+ agentTypes,
48284
+ cwd,
48285
+ workspaceId,
48286
+ log: log2,
48287
+ reportAgentCapabilities,
48288
+ bridgeReport = true,
48289
+ forceAllTypes = false,
48290
+ shouldContinue
48291
+ } = params;
48292
+ const canContinue = () => !isCliImmediateShutdownRequested() && shouldContinue?.() !== false;
48293
+ let changedCount = 0;
48294
+ for (let i = 0; i < agentTypes.length; i++) {
48295
+ if (!canContinue()) return changedCount;
48296
+ if (i > 0) await yieldToEventLoop();
48297
+ const agentType = agentTypes[i];
48298
+ if (!agentType.trim()) continue;
48299
+ if (!forceAllTypes) {
48300
+ try {
48301
+ if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" && await withCliSqlite((db) => hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType))) {
48302
+ continue;
48303
+ }
48304
+ } catch {
48305
+ }
48306
+ }
48307
+ const changed = await probeOneAgentTypeForCapabilities({
48308
+ agentType,
48309
+ cwd,
48310
+ workspaceId,
48311
+ log: log2,
48312
+ reportAgentCapabilities,
48313
+ bridgeReport,
48314
+ shouldContinue: canContinue
48315
+ });
48316
+ if (changed) changedCount += 1;
48317
+ }
48318
+ return changedCount;
48319
+ }
48320
+
48321
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
48322
+ async function warmupAgentCapabilitiesOnConnect(params) {
48323
+ const { workspaceId, log: log2, getWs } = params;
48324
+ const isCurrent = beginAgentCapabilityWarmupRun();
48325
+ if (!isCurrent()) return;
48326
+ const cwd = path72.resolve(getBridgeRoot());
48327
+ async function sendBatchFromCache() {
48328
+ if (!isCurrent()) return;
48329
+ const socket = getWs();
48330
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
48331
+ try {
48332
+ const rows = await withCliSqlite((db) => listCliAgentCapabilityCacheForWorkspace(db, workspaceId));
48333
+ if (!isCurrent()) return;
48334
+ if (rows.length === 0) return;
48335
+ sendWsMessage(socket, {
48336
+ type: "agent_capabilities_batch",
48337
+ items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
48338
+ });
48339
+ } catch (e) {
48340
+ if (e instanceof CliSqliteInterrupted) return;
48341
+ log2(
48342
+ `[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
48343
+ );
48344
+ }
48345
+ }
48346
+ await sendBatchFromCache();
48347
+ if (!isCurrent()) return;
48348
+ let types = [];
48349
+ try {
48350
+ types = [...await detectLocalAgentTypes()];
48351
+ } catch (e) {
48352
+ log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
48353
+ }
48354
+ if (!isCurrent()) return;
48355
+ try {
48356
+ const n = await probeAgentCapabilitiesForDetectedTypes({
48357
+ agentTypes: types,
48358
+ cwd,
48359
+ workspaceId,
48360
+ log: log2,
48361
+ bridgeReport: false,
48362
+ forceAllTypes: false,
48363
+ shouldContinue: isCurrent
48364
+ });
48365
+ if (n > 0) await sendBatchFromCache();
48366
+ } catch (e) {
48367
+ if (e instanceof CliSqliteInterrupted) return;
48368
+ log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
48369
+ }
48370
+ if (!isCurrent()) return;
48371
+ void (async () => {
48372
+ try {
48373
+ await yieldToEventLoop();
48374
+ if (!isCurrent()) return;
48375
+ const n = await probeAgentCapabilitiesForDetectedTypes({
48376
+ agentTypes: types,
48377
+ cwd,
48378
+ workspaceId,
48379
+ log: log2,
48380
+ bridgeReport: false,
48381
+ forceAllTypes: true,
48382
+ shouldContinue: isCurrent
48383
+ });
48384
+ if (n > 0) await sendBatchFromCache();
48385
+ } catch (e) {
48386
+ if (e instanceof CliSqliteInterrupted) return;
48387
+ log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
48388
+ }
48389
+ })();
48390
+ }
48391
+
48392
+ // src/connection/create-bridge-agent-capabilities-reporter.ts
48393
+ function createAgentCapabilitiesReporter(params) {
48394
+ const { workspaceId, getWs } = params;
48395
+ return (info) => {
48396
+ if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
48397
+ setImmediate(() => {
48398
+ let changed = false;
48399
+ try {
48400
+ changed = withCliSqliteSync(
48401
+ (db) => upsertCliAgentCapabilityCache(db, {
48402
+ workspaceId,
48403
+ agentType: info.agentType,
48404
+ configOptions: info.configOptions
48405
+ })
48406
+ );
48407
+ } catch (e) {
48408
+ if (e instanceof CliSqliteInterrupted) return;
48409
+ }
48410
+ if (!changed) return;
48411
+ const socket = getWs();
48412
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
48413
+ sendWsMessage(socket, {
48414
+ type: "agent_capabilities",
48415
+ agentType: info.agentType,
48416
+ configOptions: info.configOptions
48417
+ });
48418
+ });
48419
+ };
48420
+ }
48421
+ function createWarmupAgentCapabilitiesOnConnect(params) {
48422
+ return async () => {
48423
+ await warmupAgentCapabilitiesOnConnect(params);
48424
+ };
48425
+ }
48426
+
48427
+ // src/connection/create-bridge-message-deps.ts
48428
+ function createBridgeMessageDeps(params) {
48429
+ const {
48430
+ getWs,
48431
+ log: log2,
48432
+ acpManager,
48433
+ sessionWorktreeManager,
48434
+ onBridgeIdentified,
48435
+ bridgeHeartbeat,
48436
+ sendLocalSkillsReport,
48437
+ reportAutoDetectedAgents,
48438
+ warmupAgentCapabilitiesOnConnect: warmupAgentCapabilitiesOnConnect2,
48439
+ devServerManager,
48440
+ e2ee,
48441
+ cloudApiBaseUrl,
48442
+ getCloudAccessToken
48443
+ } = params;
48444
+ return {
48445
+ getWs,
48446
+ log: log2,
48447
+ acpManager,
48448
+ sessionWorktreeManager,
48449
+ onBridgeIdentified,
48450
+ onBridgeHeartbeatAck: (seq) => {
48451
+ bridgeHeartbeat.onAck(seq);
48452
+ },
48453
+ sendLocalSkillsReport,
48454
+ reportAutoDetectedAgents,
48455
+ warmupAgentCapabilitiesOnConnect: warmupAgentCapabilitiesOnConnect2,
48456
+ devServerManager,
48457
+ e2ee,
48458
+ cloudApiBaseUrl,
48459
+ getCloudAccessToken
48460
+ };
48461
+ }
48462
+
48138
48463
  // src/connection/create-bridge-connection.ts
48139
48464
  async function createBridgeConnection(options) {
48140
48465
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
@@ -48168,48 +48493,36 @@ async function createBridgeConnection(options) {
48168
48493
  function getWs() {
48169
48494
  return state.currentWs;
48170
48495
  }
48171
- function sendAgentCapabilitiesToBridge(info) {
48172
- if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
48173
- setImmediate(() => {
48174
- let changed = false;
48175
- try {
48176
- changed = withCliSqliteSync(
48177
- (db) => upsertCliAgentCapabilityCache(db, {
48178
- workspaceId,
48179
- agentType: info.agentType,
48180
- configOptions: info.configOptions
48181
- })
48182
- );
48183
- } catch (e) {
48184
- if (e instanceof CliSqliteInterrupted) return;
48185
- }
48186
- if (!changed) return;
48187
- const socket = getWs();
48188
- if (!socket || socket.readyState !== wrapper_default.OPEN) return;
48189
- sendWsMessage(socket, {
48190
- type: "agent_capabilities",
48191
- agentType: info.agentType,
48192
- configOptions: info.configOptions
48193
- });
48194
- });
48195
- }
48496
+ const sendAgentCapabilitiesToBridge = createAgentCapabilitiesReporter({ workspaceId, getWs });
48196
48497
  const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
48197
48498
  const sessionWorktreeManager = new SessionWorktreeManager({
48198
48499
  worktreesRootPath,
48199
48500
  log: logFn
48200
48501
  });
48201
48502
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
48202
- const bridgeAccess = await createBridgeAccessState({
48503
+ const bridgeAccessRef = {
48504
+ current: null
48505
+ };
48506
+ const bridgeAccessPromise = createBridgeAccessState({
48203
48507
  cloudApiBaseUrl: apiUrl,
48204
48508
  getCloudAccessToken: () => tokens.accessToken,
48205
48509
  e2ee
48510
+ }).then((bridgeAccess2) => {
48511
+ bridgeAccessRef.current = bridgeAccess2;
48512
+ return bridgeAccess2;
48206
48513
  });
48207
48514
  const acpManager = await createAcpManager({
48208
48515
  log: logFn,
48209
- getBridgeAccessPort: () => bridgeAccess.getPort(),
48516
+ getBridgeAccessPort: () => bridgeAccessRef.current?.getPort() ?? 0,
48210
48517
  reportAgentCapabilities: sendAgentCapabilitiesToBridge
48211
48518
  });
48212
48519
  logFn("CLI running. Press Ctrl+C to exit.");
48520
+ let initialIndexBuildsScheduled = false;
48521
+ const scheduleInitialIndexBuildsOnce = () => {
48522
+ if (initialIndexBuildsScheduled) return;
48523
+ initialIndexBuildsScheduled = true;
48524
+ scheduleInitialIndexBuilds(getBridgeRoot());
48525
+ };
48213
48526
  const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
48214
48527
  const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
48215
48528
  const baseOnBridgeIdentified = createOnBridgeIdentified({
@@ -48225,32 +48538,28 @@ async function createBridgeConnection(options) {
48225
48538
  };
48226
48539
  const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
48227
48540
  const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
48228
- const messageDeps = {
48541
+ const messageDeps = createBridgeMessageDeps({
48229
48542
  getWs,
48230
48543
  log: logFn,
48231
48544
  acpManager,
48232
48545
  sessionWorktreeManager,
48233
48546
  onBridgeIdentified,
48234
- onBridgeHeartbeatAck: (seq) => {
48235
- bridgeHeartbeat.onAck(seq);
48236
- },
48547
+ bridgeHeartbeat,
48237
48548
  sendLocalSkillsReport,
48238
48549
  reportAutoDetectedAgents,
48239
- warmupAgentCapabilitiesOnConnect: async () => {
48240
- await warmupAgentCapabilitiesOnConnect({
48241
- workspaceId,
48242
- log: logFn,
48243
- getWs
48244
- });
48245
- },
48550
+ warmupAgentCapabilitiesOnConnect: createWarmupAgentCapabilitiesOnConnect({
48551
+ workspaceId,
48552
+ log: logFn,
48553
+ getWs
48554
+ }),
48246
48555
  devServerManager,
48247
48556
  e2ee,
48248
48557
  cloudApiBaseUrl: apiUrl,
48249
48558
  getCloudAccessToken: () => tokens.accessToken
48250
- };
48559
+ });
48251
48560
  const identifyReportedPaths = {
48252
- bridgeRootPath: path71.resolve(getBridgeRoot()),
48253
- worktreesRootPath: path71.resolve(worktreesRootPath)
48561
+ bridgeRootPath: path73.resolve(getBridgeRoot()),
48562
+ worktreesRootPath: path73.resolve(worktreesRootPath)
48254
48563
  };
48255
48564
  const { connect } = createMainBridgeWebSocketLifecycle({
48256
48565
  state,
@@ -48265,10 +48574,12 @@ async function createBridgeConnection(options) {
48265
48574
  onAuthInvalid,
48266
48575
  e2ee,
48267
48576
  identifyReportedPaths,
48268
- bridgeHeartbeat
48577
+ bridgeHeartbeat,
48578
+ onBridgeSocketOpen: scheduleInitialIndexBuildsOnce
48269
48579
  });
48270
48580
  connect();
48271
48581
  const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
48582
+ const bridgeAccess = await bridgeAccessPromise;
48272
48583
  return {
48273
48584
  close: async () => {
48274
48585
  requestCliImmediateShutdown();
@@ -48505,9 +48816,9 @@ async function runCliAction(program2, opts) {
48505
48816
  const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
48506
48817
  const bridgeRootOpt = (opts.bridgeRoot && typeof opts.bridgeRoot === "string" && opts.bridgeRoot.trim() ? opts.bridgeRoot.trim() : null) ?? (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim() ? opts.cwd.trim() : null);
48507
48818
  if (bridgeRootOpt) {
48508
- const resolvedBridgeRoot = path72.resolve(process.cwd(), bridgeRootOpt);
48819
+ const resolvedBridgeRoot = path74.resolve(process.cwd(), bridgeRootOpt);
48509
48820
  try {
48510
- const st = fs53.statSync(resolvedBridgeRoot);
48821
+ const st = fs52.statSync(resolvedBridgeRoot);
48511
48822
  if (!st.isDirectory()) {
48512
48823
  console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
48513
48824
  process.exit(1);
@@ -48527,7 +48838,7 @@ async function runCliAction(program2, opts) {
48527
48838
  );
48528
48839
  let worktreesRootPath;
48529
48840
  if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
48530
- worktreesRootPath = path72.resolve(opts.worktreesRoot.trim());
48841
+ worktreesRootPath = path74.resolve(opts.worktreesRoot.trim());
48531
48842
  }
48532
48843
  const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
48533
48844
  if (e2eCertificates) {