@buildautomaton/cli 0.1.27 → 0.1.29

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 path37 = __require("node:path");
977
- var fs36 = __require("node:fs");
976
+ var path41 = __require("node:path");
977
+ var fs38 = __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 = path37.resolve(baseDir, baseName);
1910
- if (fs36.existsSync(localBin)) return localBin;
1911
- if (sourceExt.includes(path37.extname(baseName))) return void 0;
1909
+ const localBin = path41.resolve(baseDir, baseName);
1910
+ if (fs38.existsSync(localBin)) return localBin;
1911
+ if (sourceExt.includes(path41.extname(baseName))) return void 0;
1912
1912
  const foundExt = sourceExt.find(
1913
- (ext) => fs36.existsSync(`${localBin}${ext}`)
1913
+ (ext) => fs38.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 = fs36.realpathSync(this._scriptPath);
1925
+ resolvedScriptPath = fs38.realpathSync(this._scriptPath);
1926
1926
  } catch (err) {
1927
1927
  resolvedScriptPath = this._scriptPath;
1928
1928
  }
1929
- executableDir = path37.resolve(
1930
- path37.dirname(resolvedScriptPath),
1929
+ executableDir = path41.resolve(
1930
+ path41.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 = path37.basename(
1937
+ const legacyName = path41.basename(
1938
1938
  this._scriptPath,
1939
- path37.extname(this._scriptPath)
1939
+ path41.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(path37.extname(executableFile));
1950
+ launchWithNode = sourceExt.includes(path41.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 = path37.basename(filename, path37.extname(filename));
2790
+ this._name = path41.basename(filename, path41.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(path38) {
2805
- if (path38 === void 0) return this._executableDir;
2806
- this._executableDir = path38;
2804
+ executableDir(path42) {
2805
+ if (path42 === void 0) return this._executableDir;
2806
+ this._executableDir = path42;
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: path37, errorMaps, issueData } = params;
7065
- const fullPath = [...path37, ...issueData.path || []];
7064
+ const { data, path: path41, errorMaps, issueData } = params;
7065
+ const fullPath = [...path41, ...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, path37, key) {
7373
+ constructor(parent, value, path41, key) {
7374
7374
  this._cachedPath = [];
7375
7375
  this.parent = parent;
7376
7376
  this.data = value;
7377
- this._path = path37;
7377
+ this._path = path41;
7378
7378
  this._key = key;
7379
7379
  }
7380
7380
  get path() {
@@ -11235,7 +11235,7 @@ var require_has_flag = __commonJS({
11235
11235
  var require_supports_color = __commonJS({
11236
11236
  "../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports, module) {
11237
11237
  "use strict";
11238
- var os8 = __require("os");
11238
+ var os9 = __require("os");
11239
11239
  var tty = __require("tty");
11240
11240
  var hasFlag = require_has_flag();
11241
11241
  var { env } = process;
@@ -11283,7 +11283,7 @@ var require_supports_color = __commonJS({
11283
11283
  return min;
11284
11284
  }
11285
11285
  if (process.platform === "win32") {
11286
- const osRelease = os8.release().split(".");
11286
+ const osRelease = os9.release().split(".");
11287
11287
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
11288
11288
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
11289
11289
  }
@@ -11529,10 +11529,10 @@ var require_src2 = __commonJS({
11529
11529
  var fs_1 = __require("fs");
11530
11530
  var debug_1 = __importDefault(require_src());
11531
11531
  var log2 = debug_1.default("@kwsites/file-exists");
11532
- function check2(path37, isFile, isDirectory) {
11533
- log2(`checking %s`, path37);
11532
+ function check2(path41, isFile, isDirectory) {
11533
+ log2(`checking %s`, path41);
11534
11534
  try {
11535
- const stat3 = fs_1.statSync(path37);
11535
+ const stat3 = fs_1.statSync(path41);
11536
11536
  if (stat3.isFile() && isFile) {
11537
11537
  log2(`[OK] path represents a file`);
11538
11538
  return true;
@@ -11552,8 +11552,8 @@ var require_src2 = __commonJS({
11552
11552
  throw e;
11553
11553
  }
11554
11554
  }
11555
- function exists2(path37, type = exports.READABLE) {
11556
- return check2(path37, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
11555
+ function exists2(path41, type = exports.READABLE) {
11556
+ return check2(path41, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
11557
11557
  }
11558
11558
  exports.exists = exists2;
11559
11559
  exports.FILE = 1;
@@ -11850,10 +11850,10 @@ function assignProp(target, prop, value) {
11850
11850
  configurable: true
11851
11851
  });
11852
11852
  }
11853
- function getElementAtPath(obj, path37) {
11854
- if (!path37)
11853
+ function getElementAtPath(obj, path41) {
11854
+ if (!path41)
11855
11855
  return obj;
11856
- return path37.reduce((acc, key) => acc?.[key], obj);
11856
+ return path41.reduce((acc, key) => acc?.[key], obj);
11857
11857
  }
11858
11858
  function promiseAllObject(promisesObj) {
11859
11859
  const keys = Object.keys(promisesObj);
@@ -12102,11 +12102,11 @@ function aborted(x, startIndex = 0) {
12102
12102
  }
12103
12103
  return false;
12104
12104
  }
12105
- function prefixIssues(path37, issues) {
12105
+ function prefixIssues(path41, issues) {
12106
12106
  return issues.map((iss) => {
12107
12107
  var _a2;
12108
12108
  (_a2 = iss).path ?? (_a2.path = []);
12109
- iss.path.unshift(path37);
12109
+ iss.path.unshift(path41);
12110
12110
  return iss;
12111
12111
  });
12112
12112
  }
@@ -12295,7 +12295,7 @@ function treeifyError(error40, _mapper) {
12295
12295
  return issue2.message;
12296
12296
  };
12297
12297
  const result = { errors: [] };
12298
- const processError = (error41, path37 = []) => {
12298
+ const processError = (error41, path41 = []) => {
12299
12299
  var _a2, _b;
12300
12300
  for (const issue2 of error41.issues) {
12301
12301
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -12305,7 +12305,7 @@ function treeifyError(error40, _mapper) {
12305
12305
  } else if (issue2.code === "invalid_element") {
12306
12306
  processError({ issues: issue2.issues }, issue2.path);
12307
12307
  } else {
12308
- const fullpath = [...path37, ...issue2.path];
12308
+ const fullpath = [...path41, ...issue2.path];
12309
12309
  if (fullpath.length === 0) {
12310
12310
  result.errors.push(mapper(issue2));
12311
12311
  continue;
@@ -12335,9 +12335,9 @@ function treeifyError(error40, _mapper) {
12335
12335
  processError(error40);
12336
12336
  return result;
12337
12337
  }
12338
- function toDotPath(path37) {
12338
+ function toDotPath(path41) {
12339
12339
  const segs = [];
12340
- for (const seg of path37) {
12340
+ for (const seg of path41) {
12341
12341
  if (typeof seg === "number")
12342
12342
  segs.push(`[${seg}]`);
12343
12343
  else if (typeof seg === "symbol")
@@ -25064,15 +25064,15 @@ var {
25064
25064
  } = import_index.default;
25065
25065
 
25066
25066
  // src/cli-version.ts
25067
- var CLI_VERSION = "0.1.27".length > 0 ? "0.1.27" : "0.0.0-dev";
25067
+ var CLI_VERSION = "0.1.29".length > 0 ? "0.1.29" : "0.0.0-dev";
25068
25068
 
25069
25069
  // src/cli/defaults.ts
25070
25070
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
25071
25071
  var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
25072
25072
 
25073
25073
  // src/cli/run-cli-action.ts
25074
- import * as fs35 from "node:fs";
25075
- import * as path36 from "node:path";
25074
+ import * as fs37 from "node:fs";
25075
+ import * as path40 from "node:path";
25076
25076
 
25077
25077
  // src/cli-log-level.ts
25078
25078
  var verbosity = "info";
@@ -25638,6 +25638,12 @@ function sendWsMessage(ws, payload) {
25638
25638
  }
25639
25639
  }
25640
25640
 
25641
+ // src/connection/heartbeat/constants.ts
25642
+ var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
25643
+ var BRIDGE_HEARTBEAT_SEQ_MAX = 2147483646;
25644
+ var BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT = 4;
25645
+ var BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX = 5;
25646
+
25641
25647
  // ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
25642
25648
  import process7 from "node:process";
25643
25649
  import { Buffer as Buffer2 } from "node:buffer";
@@ -26616,14 +26622,18 @@ function runPendingAuth(options) {
26616
26622
  }
26617
26623
  function connect() {
26618
26624
  const url2 = buildPendingBridgeUrl(apiUrl, connectionId);
26625
+ let pendingHbSeq = -1;
26619
26626
  ws = createWsBridge({
26620
26627
  url: url2,
26621
26628
  onOpen: () => {
26622
26629
  clearQuietOnOpen();
26630
+ pendingHbSeq = -1;
26623
26631
  sendWsMessage(ws, { type: "identify", role: "cli", cliVersion: CLI_VERSION });
26624
26632
  keepaliveInterval = setInterval(() => {
26625
26633
  if (resolved || !ws || ws.readyState !== 1) return;
26626
- sendWsMessage(ws, { type: "ping", timestamp: Date.now() });
26634
+ pendingHbSeq = pendingHbSeq >= BRIDGE_HEARTBEAT_SEQ_MAX ? 0 : pendingHbSeq + 1;
26635
+ const hb = { t: "h", s: pendingHbSeq };
26636
+ sendWsMessage(ws, hb);
26627
26637
  }, PENDING_KEEPALIVE_MS);
26628
26638
  if (browserFallback) {
26629
26639
  clearTimeout(browserFallback);
@@ -26686,6 +26696,321 @@ function runPendingAuth(options) {
26686
26696
  };
26687
26697
  }
26688
26698
 
26699
+ // src/sqlite/cli-database.ts
26700
+ import sqliteWasm from "node-sqlite3-wasm";
26701
+
26702
+ // src/sqlite/cli-sqlite-paths.ts
26703
+ import fs8 from "node:fs";
26704
+ import path5 from "node:path";
26705
+ import os3 from "node:os";
26706
+ function getCliSqlitePath() {
26707
+ const override = process.env.BUILDAMATON_CLI_SQLITE_PATH?.trim();
26708
+ if (override) return path5.resolve(override);
26709
+ return path5.join(os3.homedir(), ".buildautomaton", "cli.sqlite");
26710
+ }
26711
+ function ensureCliSqliteParentDir(sqlitePath) {
26712
+ const dir = path5.dirname(sqlitePath);
26713
+ if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
26714
+ }
26715
+
26716
+ // src/sqlite/legacy_migration/import-cli-legacy-disk-data.ts
26717
+ import fs13 from "node:fs";
26718
+
26719
+ // src/files/index/constants.ts
26720
+ import path6 from "node:path";
26721
+ import os4 from "node:os";
26722
+ var INDEX_WORK_YIELD_EVERY = 256;
26723
+ var INDEX_DIR = path6.join(os4.homedir(), ".buildautomaton");
26724
+ var INDEX_HASH_LEN = 16;
26725
+
26726
+ // src/prompt-turn-queue/paths.ts
26727
+ import path7 from "node:path";
26728
+ import os5 from "node:os";
26729
+ function getPromptQueuesDirectory() {
26730
+ const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
26731
+ if (override) return path7.resolve(override);
26732
+ return path7.join(os5.homedir(), ".buildautomaton", "queues");
26733
+ }
26734
+
26735
+ // src/sqlite/legacy_migration/archive-file-index-json.ts
26736
+ import fs10 from "node:fs";
26737
+ import path9 from "node:path";
26738
+
26739
+ // src/sqlite/legacy_migration/archive-to-old-version.ts
26740
+ import fs9 from "node:fs";
26741
+ import path8 from "node:path";
26742
+ var OLD_VERSION_DIR = path8.join(INDEX_DIR, "old-version");
26743
+ function moveLegacyFileToOldVersionBackup(category, sourcePath) {
26744
+ const destDir = path8.join(OLD_VERSION_DIR, category);
26745
+ fs9.mkdirSync(destDir, { recursive: true });
26746
+ const base = path8.basename(sourcePath);
26747
+ let dest = path8.join(destDir, base);
26748
+ if (fs9.existsSync(dest)) {
26749
+ const ext = path8.extname(base);
26750
+ const stem = ext ? base.slice(0, -ext.length) : base;
26751
+ dest = path8.join(destDir, `${stem}-${Date.now()}${ext}`);
26752
+ }
26753
+ fs9.renameSync(sourcePath, dest);
26754
+ }
26755
+
26756
+ // src/sqlite/legacy_migration/archive-file-index-json.ts
26757
+ function archiveLegacyFileIndexJsonFiles() {
26758
+ try {
26759
+ if (!fs10.existsSync(INDEX_DIR)) return;
26760
+ for (const name of fs10.readdirSync(INDEX_DIR)) {
26761
+ if (name.startsWith(".file-index-") && name.endsWith(".json")) {
26762
+ const full = path9.join(INDEX_DIR, name);
26763
+ try {
26764
+ moveLegacyFileToOldVersionBackup("file-index", full);
26765
+ } catch (err) {
26766
+ console.error(`[cli-sqlite] legacy import: could not archive ${name}`, err);
26767
+ }
26768
+ }
26769
+ }
26770
+ } catch (e) {
26771
+ console.error("[cli-sqlite] legacy import: archive file-index json failed", e);
26772
+ }
26773
+ }
26774
+
26775
+ // src/sqlite/legacy_migration/import-agent-sessions-from-disk.ts
26776
+ import fs11 from "node:fs";
26777
+ import path10 from "node:path";
26778
+ import os6 from "node:os";
26779
+ var LEGACY_AGENT_SESSION_DIR = path10.join(os6.homedir(), ".buildautomaton", "agent-sessions");
26780
+ function importLegacyAgentSessionsFromDisk(db) {
26781
+ try {
26782
+ if (!fs11.existsSync(LEGACY_AGENT_SESSION_DIR)) return;
26783
+ const names = fs11.readdirSync(LEGACY_AGENT_SESSION_DIR).filter((n) => n.endsWith(".json"));
26784
+ for (const name of names) {
26785
+ const sessionKey = name.slice(0, -".json".length);
26786
+ const full = path10.join(LEGACY_AGENT_SESSION_DIR, name);
26787
+ let raw;
26788
+ try {
26789
+ raw = fs11.readFileSync(full, "utf8");
26790
+ } catch {
26791
+ continue;
26792
+ }
26793
+ let parsed;
26794
+ try {
26795
+ parsed = JSON.parse(raw);
26796
+ } catch {
26797
+ continue;
26798
+ }
26799
+ if (parsed.v !== 1) continue;
26800
+ const acpSessionId = typeof parsed.acpSessionId === "string" ? parsed.acpSessionId : null;
26801
+ const backendAgentType = typeof parsed.backendAgentType === "string" ? parsed.backendAgentType : null;
26802
+ const configOptionsJson = Array.isArray(parsed.configOptions) ? JSON.stringify(parsed.configOptions) : null;
26803
+ const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
26804
+ db.run(
26805
+ `INSERT OR REPLACE INTO agent_session (session_key, acp_session_id, backend_agent_type, config_options_json, updated_at)
26806
+ VALUES (?, ?, ?, ?, ?)`,
26807
+ [sessionKey, acpSessionId, backendAgentType, configOptionsJson, updatedAt]
26808
+ );
26809
+ try {
26810
+ moveLegacyFileToOldVersionBackup("agent-sessions", full);
26811
+ } catch {
26812
+ }
26813
+ }
26814
+ } catch (e) {
26815
+ console.error("[cli-sqlite] legacy import: agent_sessions from disk failed", e);
26816
+ }
26817
+ }
26818
+
26819
+ // src/sqlite/legacy_migration/import-prompt-queues-from-disk.ts
26820
+ import fs12 from "node:fs";
26821
+ import path11 from "node:path";
26822
+
26823
+ // src/sqlite/legacy_migration/parse-persisted-queue-json.ts
26824
+ function parsePersistedQueueFromJson(raw) {
26825
+ try {
26826
+ const o = JSON.parse(raw);
26827
+ const queueKey = typeof o.queueKey === "string" ? o.queueKey : typeof o.queueKeyHash === "string" ? o.queueKeyHash : null;
26828
+ if (!queueKey || typeof o.updatedAt !== "string" || !Array.isArray(o.turns)) return null;
26829
+ return { queueKey, updatedAt: o.updatedAt, turns: o.turns };
26830
+ } catch {
26831
+ return null;
26832
+ }
26833
+ }
26834
+
26835
+ // src/sqlite/legacy_migration/import-prompt-queues-from-disk.ts
26836
+ function importLegacyPromptQueuesFromDisk(db) {
26837
+ try {
26838
+ const dir = getPromptQueuesDirectory();
26839
+ if (!fs12.existsSync(dir)) return;
26840
+ for (const name of fs12.readdirSync(dir)) {
26841
+ if (!name.endsWith(".json")) continue;
26842
+ const full = path11.join(dir, name);
26843
+ let raw;
26844
+ try {
26845
+ raw = fs12.readFileSync(full, "utf8");
26846
+ } catch {
26847
+ continue;
26848
+ }
26849
+ const file2 = parsePersistedQueueFromJson(raw);
26850
+ if (!file2) continue;
26851
+ db.run(
26852
+ `INSERT OR REPLACE INTO prompt_queue (queue_key, updated_at, turns_json) VALUES (?, ?, ?)`,
26853
+ [file2.queueKey, file2.updatedAt, JSON.stringify(file2.turns)]
26854
+ );
26855
+ try {
26856
+ moveLegacyFileToOldVersionBackup("queues", full);
26857
+ } catch {
26858
+ }
26859
+ }
26860
+ } catch (e) {
26861
+ console.error("[cli-sqlite] legacy import: prompt queues from disk failed", e);
26862
+ }
26863
+ }
26864
+
26865
+ // src/sqlite/legacy_migration/import-cli-legacy-disk-data.ts
26866
+ function legacyCliDiskMigrationIsPending() {
26867
+ try {
26868
+ if (fs13.existsSync(INDEX_DIR)) {
26869
+ for (const name of fs13.readdirSync(INDEX_DIR)) {
26870
+ if (name.startsWith(".file-index-") && name.endsWith(".json")) return true;
26871
+ }
26872
+ }
26873
+ } catch {
26874
+ }
26875
+ try {
26876
+ if (fs13.existsSync(LEGACY_AGENT_SESSION_DIR)) {
26877
+ if (fs13.readdirSync(LEGACY_AGENT_SESSION_DIR).some((n) => n.endsWith(".json"))) return true;
26878
+ }
26879
+ } catch {
26880
+ }
26881
+ try {
26882
+ const dir = getPromptQueuesDirectory();
26883
+ if (fs13.existsSync(dir) && fs13.readdirSync(dir).some((n) => n.endsWith(".json"))) return true;
26884
+ } catch {
26885
+ }
26886
+ return false;
26887
+ }
26888
+ function importCliSqliteLegacyDiskData(db, log2) {
26889
+ const pending = legacyCliDiskMigrationIsPending();
26890
+ if (pending && log2) {
26891
+ log2("Migrating legacy on-disk CLI data to SQLite\u2026");
26892
+ }
26893
+ archiveLegacyFileIndexJsonFiles();
26894
+ importLegacyAgentSessionsFromDisk(db);
26895
+ importLegacyPromptQueuesFromDisk(db);
26896
+ if (pending && log2) {
26897
+ log2("Legacy on-disk CLI data migration finished.");
26898
+ }
26899
+ }
26900
+
26901
+ // src/sqlite/load-cli-migration-sql.ts
26902
+ import { existsSync, readFileSync } from "node:fs";
26903
+ import { dirname, join as join2 } from "node:path";
26904
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
26905
+ function readCliSqliteMigrationSql(filename) {
26906
+ const dir = dirname(fileURLToPath2(import.meta.url));
26907
+ const resolved = join2(dir, "migrations", filename);
26908
+ if (!existsSync(resolved)) {
26909
+ throw new Error(`Missing CLI SQLite migration SQL: ${resolved}`);
26910
+ }
26911
+ return readFileSync(resolved, "utf8");
26912
+ }
26913
+
26914
+ // src/sqlite/migrate-cli-sqlite.ts
26915
+ function checkpointSatisfiedByLegacyChain(applied2, replacesLegacyMigrations) {
26916
+ return Array.isArray(replacesLegacyMigrations) && replacesLegacyMigrations.length > 0 && replacesLegacyMigrations.every((n) => applied2.has(n));
26917
+ }
26918
+ function recordMigrationAndPruneCheckpointLegacy(db, migration, applied2) {
26919
+ db.run("INSERT INTO __migrations (name) VALUES (?)", [migration.name]);
26920
+ applied2.add(migration.name);
26921
+ if (migration.checkpoint !== true) return;
26922
+ const legacy = migration.replacesLegacyMigrations;
26923
+ if (!legacy?.length) return;
26924
+ for (const legacyName of legacy) {
26925
+ if (legacyName === migration.name) continue;
26926
+ db.run("DELETE FROM __migrations WHERE name = ?", [legacyName]);
26927
+ applied2.delete(legacyName);
26928
+ }
26929
+ }
26930
+ var CHECKPOINT_V1 = "001_cli_sqlite_checkpoint_v1";
26931
+ var CHECKPOINT_V1_SQL = readCliSqliteMigrationSql("001_cli_sqlite_checkpoint_v1.sql");
26932
+ var CLI_SQLITE_MIGRATIONS = [
26933
+ {
26934
+ name: CHECKPOINT_V1,
26935
+ checkpoint: true,
26936
+ migrate: (db) => {
26937
+ db.exec(CHECKPOINT_V1_SQL);
26938
+ }
26939
+ }
26940
+ ];
26941
+ function migrateCliSqlite(db) {
26942
+ db.exec(readCliSqliteMigrationSql("000_bootstrap_migrations_table.sql"));
26943
+ const appliedRows = db.all("SELECT name FROM __migrations");
26944
+ const applied2 = new Set(appliedRows.map((r) => r.name));
26945
+ for (const migration of CLI_SQLITE_MIGRATIONS) {
26946
+ if (applied2.has(migration.name)) continue;
26947
+ if (migration.checkpoint === true && checkpointSatisfiedByLegacyChain(applied2, migration.replacesLegacyMigrations)) {
26948
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
26949
+ continue;
26950
+ }
26951
+ if (migration.alreadyApplied?.(db)) {
26952
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
26953
+ continue;
26954
+ }
26955
+ try {
26956
+ migration.migrate(db);
26957
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
26958
+ } catch (e) {
26959
+ console.error(`[cli-sqlite] Migration failed: ${migration.name}`, e);
26960
+ throw e;
26961
+ }
26962
+ }
26963
+ }
26964
+
26965
+ // src/sqlite/cli-database.ts
26966
+ var { Database: SqliteDatabase } = sqliteWasm;
26967
+ var openDatabases = /* @__PURE__ */ new Map();
26968
+ var processExitCloseRegistered = false;
26969
+ function registerProcessExitSqliteClose() {
26970
+ if (processExitCloseRegistered) return;
26971
+ processExitCloseRegistered = true;
26972
+ process.once("exit", () => {
26973
+ for (const db of openDatabases.values()) {
26974
+ safeCloseCliSqliteDatabase(db);
26975
+ }
26976
+ openDatabases.clear();
26977
+ });
26978
+ }
26979
+ function safeCloseCliSqliteDatabase(db) {
26980
+ if (db == null) return;
26981
+ try {
26982
+ if (db.isOpen) db.close();
26983
+ } catch {
26984
+ }
26985
+ }
26986
+ function closeAllCliSqliteConnections() {
26987
+ for (const db of openDatabases.values()) {
26988
+ safeCloseCliSqliteDatabase(db);
26989
+ }
26990
+ openDatabases.clear();
26991
+ }
26992
+ function getCliDatabase(options) {
26993
+ const sqlitePath = getCliSqlitePath();
26994
+ const existing = openDatabases.get(sqlitePath);
26995
+ if (existing?.isOpen) return existing;
26996
+ if (existing && !existing.isOpen) {
26997
+ safeCloseCliSqliteDatabase(existing);
26998
+ openDatabases.delete(sqlitePath);
26999
+ }
27000
+ ensureCliSqliteParentDir(sqlitePath);
27001
+ const db = new SqliteDatabase(sqlitePath);
27002
+ try {
27003
+ migrateCliSqlite(db);
27004
+ importCliSqliteLegacyDiskData(db, options?.logLegacyMigration);
27005
+ } catch (e) {
27006
+ safeCloseCliSqliteDatabase(db);
27007
+ throw e;
27008
+ }
27009
+ openDatabases.set(sqlitePath, db);
27010
+ registerProcessExitSqliteClose();
27011
+ return db;
27012
+ }
27013
+
26689
27014
  // src/connection/close-bridge-connection.ts
26690
27015
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
26691
27016
  const say = log2 ?? logImmediate;
@@ -26729,20 +27054,24 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
26729
27054
  say("Stopping local dev server processes\u2026");
26730
27055
  await devServerManager.shutdownAllGraceful();
26731
27056
  }
27057
+ try {
27058
+ closeAllCliSqliteConnections();
27059
+ } catch {
27060
+ }
26732
27061
  say("Shutdown complete.");
26733
27062
  }
26734
27063
 
26735
27064
  // src/paths/session-layout-paths.ts
26736
- import * as path5 from "node:path";
27065
+ import * as path12 from "node:path";
26737
27066
  function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
26738
- const resolved = worktreePaths.map((p) => path5.resolve(p)).filter(Boolean);
27067
+ const resolved = worktreePaths.map((p) => path12.resolve(p)).filter(Boolean);
26739
27068
  if (resolved.length === 0) return null;
26740
27069
  resolved.sort();
26741
27070
  return resolved[0];
26742
27071
  }
26743
27072
  function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
26744
27073
  if (resolvedSessionParentPath != null && String(resolvedSessionParentPath).trim() !== "") {
26745
- return path5.resolve(String(resolvedSessionParentPath).trim());
27074
+ return path12.resolve(String(resolvedSessionParentPath).trim());
26746
27075
  }
26747
27076
  return getBridgeRoot();
26748
27077
  }
@@ -27074,9 +27403,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
27074
27403
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
27075
27404
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
27076
27405
  if (!rawPath || !summary) continue;
27077
- const path37 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
27078
- if (!path37) continue;
27079
- rows.push({ path: path37, summary: clampSummaryToAtMostTwoLines(summary) });
27406
+ const path41 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
27407
+ if (!path41) continue;
27408
+ rows.push({ path: path41, summary: clampSummaryToAtMostTwoLines(summary) });
27080
27409
  }
27081
27410
  return rows;
27082
27411
  }
@@ -27302,17 +27631,17 @@ function getCliPermissionModeFromAgentConfig(config2) {
27302
27631
  import { execFile as execFile7 } from "node:child_process";
27303
27632
  import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
27304
27633
  import { promisify as promisify7 } from "node:util";
27305
- import * as path8 from "node:path";
27634
+ import * as path15 from "node:path";
27306
27635
 
27307
27636
  // src/git/pre-turn-snapshot.ts
27308
- import * as fs9 from "node:fs";
27309
- import * as path7 from "node:path";
27637
+ import * as fs15 from "node:fs";
27638
+ import * as path14 from "node:path";
27310
27639
  import { execFile as execFile6 } from "node:child_process";
27311
27640
  import { promisify as promisify6 } from "node:util";
27312
27641
 
27313
27642
  // src/git/discover-repos.ts
27314
- import * as fs8 from "node:fs";
27315
- import * as path6 from "node:path";
27643
+ import * as fs14 from "node:fs";
27644
+ import * as path13 from "node:path";
27316
27645
 
27317
27646
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
27318
27647
  var import_file_exists = __toESM(require_dist(), 1);
@@ -27351,8 +27680,8 @@ function pathspec(...paths) {
27351
27680
  cache.set(key, paths);
27352
27681
  return key;
27353
27682
  }
27354
- function isPathSpec(path37) {
27355
- return path37 instanceof String && cache.has(path37);
27683
+ function isPathSpec(path41) {
27684
+ return path41 instanceof String && cache.has(path41);
27356
27685
  }
27357
27686
  function toPaths(pathSpec) {
27358
27687
  return cache.get(pathSpec) || [];
@@ -27441,8 +27770,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
27441
27770
  function forEachLineWithContent(input, callback) {
27442
27771
  return toLinesWithContent(input, true).map((line) => callback(line));
27443
27772
  }
27444
- function folderExists(path37) {
27445
- return (0, import_file_exists.exists)(path37, import_file_exists.FOLDER);
27773
+ function folderExists(path41) {
27774
+ return (0, import_file_exists.exists)(path41, import_file_exists.FOLDER);
27446
27775
  }
27447
27776
  function append(target, item) {
27448
27777
  if (Array.isArray(target)) {
@@ -27846,8 +28175,8 @@ function checkIsRepoRootTask() {
27846
28175
  commands,
27847
28176
  format: "utf-8",
27848
28177
  onError,
27849
- parser(path37) {
27850
- return /^\.(git)?$/.test(path37.trim());
28178
+ parser(path41) {
28179
+ return /^\.(git)?$/.test(path41.trim());
27851
28180
  }
27852
28181
  };
27853
28182
  }
@@ -28281,11 +28610,11 @@ function parseGrep(grep) {
28281
28610
  const paths = /* @__PURE__ */ new Set();
28282
28611
  const results = {};
28283
28612
  forEachLineWithContent(grep, (input) => {
28284
- const [path37, line, preview] = input.split(NULL);
28285
- paths.add(path37);
28286
- (results[path37] = results[path37] || []).push({
28613
+ const [path41, line, preview] = input.split(NULL);
28614
+ paths.add(path41);
28615
+ (results[path41] = results[path41] || []).push({
28287
28616
  line: asNumber(line),
28288
- path: path37,
28617
+ path: path41,
28289
28618
  preview
28290
28619
  });
28291
28620
  });
@@ -29050,14 +29379,14 @@ var init_hash_object = __esm2({
29050
29379
  init_task();
29051
29380
  }
29052
29381
  });
29053
- function parseInit(bare, path37, text) {
29382
+ function parseInit(bare, path41, text) {
29054
29383
  const response = String(text).trim();
29055
29384
  let result;
29056
29385
  if (result = initResponseRegex.exec(response)) {
29057
- return new InitSummary(bare, path37, false, result[1]);
29386
+ return new InitSummary(bare, path41, false, result[1]);
29058
29387
  }
29059
29388
  if (result = reInitResponseRegex.exec(response)) {
29060
- return new InitSummary(bare, path37, true, result[1]);
29389
+ return new InitSummary(bare, path41, true, result[1]);
29061
29390
  }
29062
29391
  let gitDir = "";
29063
29392
  const tokens = response.split(" ");
@@ -29068,7 +29397,7 @@ function parseInit(bare, path37, text) {
29068
29397
  break;
29069
29398
  }
29070
29399
  }
29071
- return new InitSummary(bare, path37, /^re/i.test(response), gitDir);
29400
+ return new InitSummary(bare, path41, /^re/i.test(response), gitDir);
29072
29401
  }
29073
29402
  var InitSummary;
29074
29403
  var initResponseRegex;
@@ -29077,9 +29406,9 @@ var init_InitSummary = __esm2({
29077
29406
  "src/lib/responses/InitSummary.ts"() {
29078
29407
  "use strict";
29079
29408
  InitSummary = class {
29080
- constructor(bare, path37, existing, gitDir) {
29409
+ constructor(bare, path41, existing, gitDir) {
29081
29410
  this.bare = bare;
29082
- this.path = path37;
29411
+ this.path = path41;
29083
29412
  this.existing = existing;
29084
29413
  this.gitDir = gitDir;
29085
29414
  }
@@ -29091,7 +29420,7 @@ var init_InitSummary = __esm2({
29091
29420
  function hasBareCommand(command) {
29092
29421
  return command.includes(bareCommand);
29093
29422
  }
29094
- function initTask(bare = false, path37, customArgs) {
29423
+ function initTask(bare = false, path41, customArgs) {
29095
29424
  const commands = ["init", ...customArgs];
29096
29425
  if (bare && !hasBareCommand(commands)) {
29097
29426
  commands.splice(1, 0, bareCommand);
@@ -29100,7 +29429,7 @@ function initTask(bare = false, path37, customArgs) {
29100
29429
  commands,
29101
29430
  format: "utf-8",
29102
29431
  parser(text) {
29103
- return parseInit(commands.includes("--bare"), path37, text);
29432
+ return parseInit(commands.includes("--bare"), path41, text);
29104
29433
  }
29105
29434
  };
29106
29435
  }
@@ -29916,12 +30245,12 @@ var init_FileStatusSummary = __esm2({
29916
30245
  "use strict";
29917
30246
  fromPathRegex = /^(.+)\0(.+)$/;
29918
30247
  FileStatusSummary = class {
29919
- constructor(path37, index, working_dir) {
29920
- this.path = path37;
30248
+ constructor(path41, index, working_dir) {
30249
+ this.path = path41;
29921
30250
  this.index = index;
29922
30251
  this.working_dir = working_dir;
29923
30252
  if (index === "R" || working_dir === "R") {
29924
- const detail = fromPathRegex.exec(path37) || [null, path37, path37];
30253
+ const detail = fromPathRegex.exec(path41) || [null, path41, path41];
29925
30254
  this.from = detail[2] || "";
29926
30255
  this.path = detail[1] || "";
29927
30256
  }
@@ -29952,14 +30281,14 @@ function splitLine(result, lineStr) {
29952
30281
  default:
29953
30282
  return;
29954
30283
  }
29955
- function data(index, workingDir, path37) {
30284
+ function data(index, workingDir, path41) {
29956
30285
  const raw = `${index}${workingDir}`;
29957
30286
  const handler = parsers6.get(raw);
29958
30287
  if (handler) {
29959
- handler(result, path37);
30288
+ handler(result, path41);
29960
30289
  }
29961
30290
  if (raw !== "##" && raw !== "!!") {
29962
- result.files.push(new FileStatusSummary(path37, index, workingDir));
30291
+ result.files.push(new FileStatusSummary(path41, index, workingDir));
29963
30292
  }
29964
30293
  }
29965
30294
  }
@@ -30232,15 +30561,15 @@ var init_simple_git_api = __esm2({
30232
30561
  this._executor = _executor;
30233
30562
  }
30234
30563
  _runTask(task, then) {
30235
- const chain = this._executor.chain();
30236
- const promise2 = chain.push(task);
30564
+ const chain2 = this._executor.chain();
30565
+ const promise2 = chain2.push(task);
30237
30566
  if (then) {
30238
30567
  taskCallback(task, promise2, then);
30239
30568
  }
30240
30569
  return Object.create(this, {
30241
30570
  then: { value: promise2.then.bind(promise2) },
30242
30571
  catch: { value: promise2.catch.bind(promise2) },
30243
- _executor: { value: chain }
30572
+ _executor: { value: chain2 }
30244
30573
  });
30245
30574
  }
30246
30575
  add(files) {
@@ -30268,9 +30597,9 @@ var init_simple_git_api = __esm2({
30268
30597
  next
30269
30598
  );
30270
30599
  }
30271
- hashObject(path37, write) {
30600
+ hashObject(path41, write) {
30272
30601
  return this._runTask(
30273
- hashObjectTask(path37, write === true),
30602
+ hashObjectTask(path41, write === true),
30274
30603
  trailingFunctionArgument(arguments)
30275
30604
  );
30276
30605
  }
@@ -30623,8 +30952,8 @@ var init_branch = __esm2({
30623
30952
  }
30624
30953
  });
30625
30954
  function toPath(input) {
30626
- const path37 = input.trim().replace(/^["']|["']$/g, "");
30627
- return path37 && normalize(path37);
30955
+ const path41 = input.trim().replace(/^["']|["']$/g, "");
30956
+ return path41 && normalize(path41);
30628
30957
  }
30629
30958
  var parseCheckIgnore;
30630
30959
  var init_CheckIgnore = __esm2({
@@ -30938,8 +31267,8 @@ __export2(sub_module_exports, {
30938
31267
  subModuleTask: () => subModuleTask,
30939
31268
  updateSubModuleTask: () => updateSubModuleTask
30940
31269
  });
30941
- function addSubModuleTask(repo, path37) {
30942
- return subModuleTask(["add", repo, path37]);
31270
+ function addSubModuleTask(repo, path41) {
31271
+ return subModuleTask(["add", repo, path41]);
30943
31272
  }
30944
31273
  function initSubModuleTask(customArgs) {
30945
31274
  return subModuleTask(["init", ...customArgs]);
@@ -31272,8 +31601,8 @@ var require_git = __commonJS2({
31272
31601
  }
31273
31602
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
31274
31603
  };
31275
- Git2.prototype.submoduleAdd = function(repo, path37, then) {
31276
- return this._runTask(addSubModuleTask2(repo, path37), trailingFunctionArgument2(arguments));
31604
+ Git2.prototype.submoduleAdd = function(repo, path41, then) {
31605
+ return this._runTask(addSubModuleTask2(repo, path41), trailingFunctionArgument2(arguments));
31277
31606
  };
31278
31607
  Git2.prototype.submoduleUpdate = function(args, then) {
31279
31608
  return this._runTask(
@@ -31917,20 +32246,20 @@ async function isGitRepoDirectory(dirPath) {
31917
32246
  // src/git/discover-repos.ts
31918
32247
  async function discoverGitRepos(cwd = getBridgeRoot()) {
31919
32248
  const result = [];
31920
- const cwdResolved = path6.resolve(cwd);
32249
+ const cwdResolved = path13.resolve(cwd);
31921
32250
  if (await isGitRepoDirectory(cwdResolved)) {
31922
32251
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
31923
32252
  result.push({ absolutePath: cwdResolved, remoteUrl });
31924
32253
  }
31925
32254
  let entries;
31926
32255
  try {
31927
- entries = fs8.readdirSync(cwdResolved, { withFileTypes: true });
32256
+ entries = fs14.readdirSync(cwdResolved, { withFileTypes: true });
31928
32257
  } catch {
31929
32258
  return result;
31930
32259
  }
31931
32260
  for (const ent of entries) {
31932
32261
  if (!ent.isDirectory()) continue;
31933
- const childPath = path6.join(cwdResolved, ent.name);
32262
+ const childPath = path13.join(cwdResolved, ent.name);
31934
32263
  if (await isGitRepoDirectory(childPath)) {
31935
32264
  const remoteUrl = await getRemoteOriginUrl(childPath);
31936
32265
  result.push({ absolutePath: childPath, remoteUrl });
@@ -31939,22 +32268,22 @@ async function discoverGitRepos(cwd = getBridgeRoot()) {
31939
32268
  return result;
31940
32269
  }
31941
32270
  async function discoverGitReposUnderRoot(rootPath) {
31942
- const root = path6.resolve(rootPath);
32271
+ const root = path13.resolve(rootPath);
31943
32272
  const roots = [];
31944
32273
  async function walk(dir) {
31945
32274
  if (await isGitRepoDirectory(dir)) {
31946
- roots.push(path6.resolve(dir));
32275
+ roots.push(path13.resolve(dir));
31947
32276
  return;
31948
32277
  }
31949
32278
  let entries;
31950
32279
  try {
31951
- entries = fs8.readdirSync(dir, { withFileTypes: true });
32280
+ entries = fs14.readdirSync(dir, { withFileTypes: true });
31952
32281
  } catch {
31953
32282
  return;
31954
32283
  }
31955
32284
  for (const ent of entries) {
31956
32285
  if (!ent.isDirectory() || ent.name === ".git") continue;
31957
- await walk(path6.join(dir, ent.name));
32286
+ await walk(path13.join(dir, ent.name));
31958
32287
  }
31959
32288
  }
31960
32289
  await walk(root);
@@ -31970,7 +32299,7 @@ async function discoverGitReposUnderRoot(rootPath) {
31970
32299
  // src/git/pre-turn-snapshot.ts
31971
32300
  var execFileAsync5 = promisify6(execFile6);
31972
32301
  function snapshotsDirForCwd(agentCwd) {
31973
- return path7.join(agentCwd, ".buildautomaton", "snapshots");
32302
+ return path14.join(agentCwd, ".buildautomaton", "snapshots");
31974
32303
  }
31975
32304
  async function gitStashCreate(repoRoot, log2) {
31976
32305
  try {
@@ -31999,7 +32328,7 @@ async function gitRun(repoRoot, args, log2, label) {
31999
32328
  async function resolveSnapshotRepoRoots(options) {
32000
32329
  const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
32001
32330
  if (worktreePaths?.length) {
32002
- const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
32331
+ const uniq = [...new Set(worktreePaths.map((p) => path14.resolve(p)))];
32003
32332
  return uniq;
32004
32333
  }
32005
32334
  try {
@@ -32007,7 +32336,7 @@ async function resolveSnapshotRepoRoots(options) {
32007
32336
  const mapped = repos.map((r) => r.absolutePath);
32008
32337
  const sid = sessionId?.trim();
32009
32338
  if (sid) {
32010
- const filtered = mapped.filter((root) => path7.basename(root) === sid);
32339
+ const filtered = mapped.filter((root) => path14.basename(root) === sid);
32011
32340
  if (filtered.length > 0) return filtered;
32012
32341
  }
32013
32342
  return mapped;
@@ -32028,7 +32357,7 @@ async function capturePreTurnSnapshot(options) {
32028
32357
  }
32029
32358
  const dir = snapshotsDirForCwd(agentCwd);
32030
32359
  try {
32031
- fs9.mkdirSync(dir, { recursive: true });
32360
+ fs15.mkdirSync(dir, { recursive: true });
32032
32361
  } catch (e) {
32033
32362
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
32034
32363
  }
@@ -32037,9 +32366,9 @@ async function capturePreTurnSnapshot(options) {
32037
32366
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
32038
32367
  repos
32039
32368
  };
32040
- const filePath = path7.join(dir, `${runId}.json`);
32369
+ const filePath = path14.join(dir, `${runId}.json`);
32041
32370
  try {
32042
- fs9.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
32371
+ fs15.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
32043
32372
  } catch (e) {
32044
32373
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
32045
32374
  }
@@ -32052,7 +32381,7 @@ async function capturePreTurnSnapshot(options) {
32052
32381
  async function applyPreTurnSnapshot(filePath, log2) {
32053
32382
  let data;
32054
32383
  try {
32055
- const raw = fs9.readFileSync(filePath, "utf8");
32384
+ const raw = fs15.readFileSync(filePath, "utf8");
32056
32385
  data = JSON.parse(raw);
32057
32386
  } catch (e) {
32058
32387
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
@@ -32075,7 +32404,7 @@ async function applyPreTurnSnapshot(filePath, log2) {
32075
32404
  return { ok: true };
32076
32405
  }
32077
32406
  function snapshotFilePath(agentCwd, runId) {
32078
- return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
32407
+ return path14.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
32079
32408
  }
32080
32409
 
32081
32410
  // src/git/session-git-queue.ts
@@ -32124,7 +32453,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32124
32453
  continue;
32125
32454
  }
32126
32455
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
32127
- const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
32456
+ const slug = path15.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
32128
32457
  for (const rel of lines) {
32129
32458
  if (rel.includes("..")) continue;
32130
32459
  try {
@@ -32138,7 +32467,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32138
32467
  );
32139
32468
  if (!patchContent.trim()) continue;
32140
32469
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
32141
- const workspaceFilePath = path8.join(repo.path, rel);
32470
+ const workspaceFilePath = path15.join(repo.path, rel);
32142
32471
  const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
32143
32472
  sendSessionUpdate({
32144
32473
  type: "session_file_change",
@@ -32160,9 +32489,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32160
32489
  // src/agents/acp/put-summarize-change-summaries.ts
32161
32490
  async function putEncryptedChangeSummaryRows(params) {
32162
32491
  const base = params.apiBaseUrl.replace(/\/+$/, "");
32163
- const entries = params.rows.map(({ path: path37, summary }) => {
32492
+ const entries = params.rows.map(({ path: path41, summary }) => {
32164
32493
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
32165
- return { path: path37, summary: JSON.stringify(enc) };
32494
+ return { path: path41, summary: JSON.stringify(enc) };
32166
32495
  });
32167
32496
  const res = await fetch(
32168
32497
  `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -32473,8 +32802,8 @@ async function sendPromptToAgent(options) {
32473
32802
  }
32474
32803
 
32475
32804
  // src/agents/acp/ensure-acp-client.ts
32476
- import * as fs11 from "node:fs";
32477
- import * as path13 from "node:path";
32805
+ import * as fs16 from "node:fs";
32806
+ import * as path19 from "node:path";
32478
32807
 
32479
32808
  // src/error-message.ts
32480
32809
  function errorMessage(err) {
@@ -32638,8 +32967,8 @@ function enrichAcpPermissionRpcResultFromRequestParams(result, params) {
32638
32967
  }
32639
32968
 
32640
32969
  // src/agents/acp/clients/shared/acp-fs-read-write.ts
32641
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
32642
- import { dirname as dirname2 } from "node:path";
32970
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
32971
+ import { dirname as dirname3 } from "node:path";
32643
32972
 
32644
32973
  // src/files/diff/unified-diff.ts
32645
32974
  function computeLineDiff(oldText, newText) {
@@ -32688,21 +33017,21 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
32688
33017
  }
32689
33018
 
32690
33019
  // src/agents/acp/safe-fs-path.ts
32691
- import * as path9 from "node:path";
33020
+ import * as path16 from "node:path";
32692
33021
  function resolveSafePathUnderCwd(cwd, filePath) {
32693
33022
  const trimmed2 = filePath.trim();
32694
33023
  if (!trimmed2) return null;
32695
- const normalizedCwd = path9.resolve(cwd);
32696
- const resolved = path9.isAbsolute(trimmed2) ? path9.normalize(trimmed2) : path9.resolve(normalizedCwd, trimmed2);
32697
- const rel = path9.relative(normalizedCwd, resolved);
32698
- if (rel.startsWith("..") || path9.isAbsolute(rel)) return null;
33024
+ const normalizedCwd = path16.resolve(cwd);
33025
+ const resolved = path16.isAbsolute(trimmed2) ? path16.normalize(trimmed2) : path16.resolve(normalizedCwd, trimmed2);
33026
+ const rel = path16.relative(normalizedCwd, resolved);
33027
+ if (rel.startsWith("..") || path16.isAbsolute(rel)) return null;
32699
33028
  return resolved;
32700
33029
  }
32701
33030
  function toDisplayPathRelativeToCwd(cwd, absolutePath) {
32702
- const normalizedCwd = path9.resolve(cwd);
32703
- const rel = path9.relative(normalizedCwd, path9.resolve(absolutePath));
32704
- if (!rel || rel === "") return path9.basename(absolutePath);
32705
- return rel.split(path9.sep).join("/");
33031
+ const normalizedCwd = path16.resolve(cwd);
33032
+ const rel = path16.relative(normalizedCwd, path16.resolve(absolutePath));
33033
+ if (!rel || rel === "") return path16.basename(absolutePath);
33034
+ return rel.split(path16.sep).join("/");
32706
33035
  }
32707
33036
 
32708
33037
  // src/agents/acp/clients/shared/acp-fs-read-write.ts
@@ -32717,7 +33046,7 @@ function acpReadTextFileInProcess(ctx, filePath, line, limit) {
32717
33046
  const resolvedPath = resolveSafePathUnderCwd(ctx.cwd, filePath);
32718
33047
  if (!resolvedPath) throw new Error("Invalid or disallowed path");
32719
33048
  try {
32720
- let content = readFileSync2(resolvedPath, "utf8");
33049
+ let content = readFileSync3(resolvedPath, "utf8");
32721
33050
  content = sliceFileContentForAcp(content, line, limit);
32722
33051
  return { content };
32723
33052
  } catch (e) {
@@ -32730,11 +33059,11 @@ function acpWriteTextFileInProcess(ctx, filePath, newText) {
32730
33059
  if (!resolvedPath) throw new Error("Invalid or disallowed path");
32731
33060
  let oldText = "";
32732
33061
  try {
32733
- oldText = readFileSync2(resolvedPath, "utf8");
33062
+ oldText = readFileSync3(resolvedPath, "utf8");
32734
33063
  } catch (e) {
32735
33064
  if (e.code !== "ENOENT") throw e;
32736
33065
  }
32737
- mkdirSync2(dirname2(resolvedPath), { recursive: true });
33066
+ mkdirSync2(dirname3(resolvedPath), { recursive: true });
32738
33067
  writeFileSync2(resolvedPath, newText, "utf8");
32739
33068
  const displayPath = toDisplayPathRelativeToCwd(ctx.cwd, resolvedPath);
32740
33069
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
@@ -33671,20 +34000,20 @@ function resolveAgentCommand(preferredAgentType) {
33671
34000
 
33672
34001
  // src/agents/acp/session-file-change-path-kind.ts
33673
34002
  import { execFileSync as execFileSync4 } from "node:child_process";
33674
- import { existsSync, statSync } from "node:fs";
34003
+ import { existsSync as existsSync2, statSync } from "node:fs";
33675
34004
 
33676
34005
  // src/git/get-git-repo-root-sync.ts
33677
34006
  import { execFileSync as execFileSync2 } from "node:child_process";
33678
- import * as path10 from "node:path";
34007
+ import * as path17 from "node:path";
33679
34008
  function getGitRepoRootSync(startDir) {
33680
34009
  try {
33681
34010
  const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
33682
- cwd: path10.resolve(startDir),
34011
+ cwd: path17.resolve(startDir),
33683
34012
  encoding: "utf8",
33684
34013
  stdio: ["ignore", "pipe", "ignore"],
33685
34014
  maxBuffer: 1024 * 1024
33686
34015
  }).trim();
33687
- return out ? path10.resolve(out) : null;
34016
+ return out ? path17.resolve(out) : null;
33688
34017
  } catch {
33689
34018
  return null;
33690
34019
  }
@@ -33692,26 +34021,26 @@ function getGitRepoRootSync(startDir) {
33692
34021
 
33693
34022
  // src/agents/acp/workspace-files.ts
33694
34023
  import { execFileSync as execFileSync3 } from "node:child_process";
33695
- import { readFileSync as readFileSync3 } from "node:fs";
33696
- import * as path11 from "node:path";
34024
+ import { readFileSync as readFileSync4 } from "node:fs";
34025
+ import * as path18 from "node:path";
33697
34026
  function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
33698
34027
  const trimmed2 = rawPath.trim();
33699
34028
  if (!trimmed2) return null;
33700
- const normalizedSessionParent = path11.resolve(sessionParentPath);
34029
+ const normalizedSessionParent = path18.resolve(sessionParentPath);
33701
34030
  let resolvedPath = resolveSafePathUnderCwd(sessionParentPath, trimmed2);
33702
34031
  if (!resolvedPath) {
33703
- const candidate = path11.isAbsolute(trimmed2) ? path11.normalize(trimmed2) : path11.normalize(path11.resolve(normalizedSessionParent, trimmed2));
34032
+ const candidate = path18.isAbsolute(trimmed2) ? path18.normalize(trimmed2) : path18.normalize(path18.resolve(normalizedSessionParent, trimmed2));
33704
34033
  const gitRoot2 = getGitRepoRootSync(sessionParentPath);
33705
34034
  if (!gitRoot2) return null;
33706
- const rel = path11.relative(gitRoot2, candidate);
33707
- if (rel.startsWith("..") || path11.isAbsolute(rel)) return null;
34035
+ const rel = path18.relative(gitRoot2, candidate);
34036
+ if (rel.startsWith("..") || path18.isAbsolute(rel)) return null;
33708
34037
  resolvedPath = candidate;
33709
34038
  }
33710
34039
  const gitRoot = getGitRepoRootSync(sessionParentPath);
33711
34040
  if (gitRoot) {
33712
- const relFromRoot = path11.relative(gitRoot, resolvedPath);
33713
- if (!relFromRoot.startsWith("..") && !path11.isAbsolute(relFromRoot)) {
33714
- return { resolvedPath, display: relFromRoot.split(path11.sep).join("/") };
34041
+ const relFromRoot = path18.relative(gitRoot, resolvedPath);
34042
+ if (!relFromRoot.startsWith("..") && !path18.isAbsolute(relFromRoot)) {
34043
+ return { resolvedPath, display: relFromRoot.split(path18.sep).join("/") };
33715
34044
  }
33716
34045
  }
33717
34046
  return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
@@ -33720,11 +34049,11 @@ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
33720
34049
  if (!displayPath || displayPath.includes("..")) return "";
33721
34050
  const gitRoot = getGitRepoRootSync(sessionParentPath);
33722
34051
  if (gitRoot) {
33723
- const resolvedPath2 = path11.resolve(gitRoot, displayPath);
33724
- const rel = path11.relative(gitRoot, resolvedPath2);
33725
- if (!rel.startsWith("..") && !path11.isAbsolute(rel)) {
34052
+ const resolvedPath2 = path18.resolve(gitRoot, displayPath);
34053
+ const rel = path18.relative(gitRoot, resolvedPath2);
34054
+ if (!rel.startsWith("..") && !path18.isAbsolute(rel)) {
33726
34055
  try {
33727
- return readFileSync3(resolvedPath2, "utf8");
34056
+ return readFileSync4(resolvedPath2, "utf8");
33728
34057
  } catch {
33729
34058
  }
33730
34059
  }
@@ -33732,7 +34061,7 @@ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
33732
34061
  const resolvedPath = resolveSafePathUnderCwd(sessionParentPath, displayPath);
33733
34062
  if (!resolvedPath) return "";
33734
34063
  try {
33735
- return readFileSync3(resolvedPath, "utf8");
34064
+ return readFileSync4(resolvedPath, "utf8");
33736
34065
  } catch {
33737
34066
  return "";
33738
34067
  }
@@ -33741,9 +34070,9 @@ function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
33741
34070
  if (!displayPath || displayPath.includes("..")) return null;
33742
34071
  const gitRoot = getGitRepoRootSync(sessionParentPath);
33743
34072
  if (gitRoot) {
33744
- const resolvedPath = path11.resolve(gitRoot, displayPath);
33745
- const rel = path11.relative(gitRoot, resolvedPath);
33746
- if (!rel.startsWith("..") && !path11.isAbsolute(rel)) return resolvedPath;
34073
+ const resolvedPath = path18.resolve(gitRoot, displayPath);
34074
+ const rel = path18.relative(gitRoot, resolvedPath);
34075
+ if (!rel.startsWith("..") && !path18.isAbsolute(rel)) return resolvedPath;
33747
34076
  }
33748
34077
  return resolveSafePathUnderCwd(sessionParentPath, displayPath);
33749
34078
  }
@@ -33778,7 +34107,7 @@ function gitHeadPathObjectType(sessionParentPath, displayPath) {
33778
34107
  }
33779
34108
  function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
33780
34109
  const resolvedPath = tryWorkspaceDisplayToPath(sessionParentPath, displayPath);
33781
- if (resolvedPath && existsSync(resolvedPath)) {
34110
+ if (resolvedPath && existsSync2(resolvedPath)) {
33782
34111
  try {
33783
34112
  if (statSync(resolvedPath).isDirectory()) {
33784
34113
  return { isDirectory: true, directoryRemoved: false };
@@ -33878,7 +34207,7 @@ function createBridgeOnRequest(opts) {
33878
34207
  }
33879
34208
 
33880
34209
  // src/agents/acp/hooks/extract-acp-file-diffs-from-update/paths-and-text.ts
33881
- import { fileURLToPath as fileURLToPath2 } from "node:url";
34210
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
33882
34211
  function readOptionalTextField(v) {
33883
34212
  if (v === null || v === void 0) return "";
33884
34213
  if (typeof v === "string") return v;
@@ -33888,7 +34217,7 @@ function normalizePathField(raw) {
33888
34217
  const t = raw.trim();
33889
34218
  if (t.startsWith("file://")) {
33890
34219
  try {
33891
- return fileURLToPath2(t);
34220
+ return fileURLToPath3(t);
33892
34221
  } catch {
33893
34222
  return t;
33894
34223
  }
@@ -34346,29 +34675,34 @@ function buildAcpSessionBridgeHooks(opts) {
34346
34675
  }
34347
34676
 
34348
34677
  // src/agents/acp/local-agent-session-file.ts
34349
- import fs10 from "node:fs";
34350
- import os3 from "node:os";
34351
- import path12 from "node:path";
34352
- var LOCAL_AGENT_SESSION_DIR = path12.join(os3.homedir(), ".buildautomaton", "agent-sessions");
34353
- function safeFileSlug(cloudSessionId) {
34678
+ function sessionKeyForCloudSessionId(cloudSessionId) {
34354
34679
  const t = cloudSessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 220);
34355
34680
  return t.length > 0 ? t : "session";
34356
34681
  }
34357
- function localAgentSessionFilePath(cloudSessionId) {
34358
- return path12.join(LOCAL_AGENT_SESSION_DIR, `${safeFileSlug(cloudSessionId)}.json`);
34359
- }
34360
34682
  function readLocalAgentSessionFile(cloudSessionId) {
34361
34683
  try {
34362
- const p = localAgentSessionFilePath(cloudSessionId);
34363
- const raw = fs10.readFileSync(p, "utf8");
34364
- const parsed = JSON.parse(raw);
34365
- if (parsed.v !== 1) return null;
34684
+ const db = getCliDatabase();
34685
+ const key = sessionKeyForCloudSessionId(cloudSessionId);
34686
+ const row = db.get(
34687
+ "SELECT acp_session_id, backend_agent_type, config_options_json, updated_at FROM agent_session WHERE session_key = ?",
34688
+ [key]
34689
+ );
34690
+ if (!row) return null;
34691
+ let configOptions = null;
34692
+ if (row.config_options_json != null && row.config_options_json !== "") {
34693
+ try {
34694
+ const parsed = JSON.parse(row.config_options_json);
34695
+ configOptions = Array.isArray(parsed) ? parsed : null;
34696
+ } catch {
34697
+ configOptions = null;
34698
+ }
34699
+ }
34366
34700
  return {
34367
34701
  v: 1,
34368
- acpSessionId: typeof parsed.acpSessionId === "string" ? parsed.acpSessionId : null,
34369
- backendAgentType: typeof parsed.backendAgentType === "string" ? parsed.backendAgentType : null,
34370
- configOptions: Array.isArray(parsed.configOptions) ? parsed.configOptions : null,
34371
- updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
34702
+ acpSessionId: row.acp_session_id,
34703
+ backendAgentType: row.backend_agent_type,
34704
+ configOptions,
34705
+ updatedAt: row.updated_at
34372
34706
  };
34373
34707
  } catch {
34374
34708
  return null;
@@ -34376,9 +34710,8 @@ function readLocalAgentSessionFile(cloudSessionId) {
34376
34710
  }
34377
34711
  function writeLocalAgentSessionFile(cloudSessionId, patch) {
34378
34712
  try {
34379
- const dir = LOCAL_AGENT_SESSION_DIR;
34380
- if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
34381
- const p = localAgentSessionFilePath(cloudSessionId);
34713
+ const db = getCliDatabase();
34714
+ const key = sessionKeyForCloudSessionId(cloudSessionId);
34382
34715
  const prev = readLocalAgentSessionFile(cloudSessionId);
34383
34716
  const next = {
34384
34717
  v: 1,
@@ -34387,7 +34720,17 @@ function writeLocalAgentSessionFile(cloudSessionId, patch) {
34387
34720
  configOptions: patch.configOptions !== void 0 ? patch.configOptions : prev?.configOptions ?? null,
34388
34721
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
34389
34722
  };
34390
- fs10.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
34723
+ const configJson = next.configOptions != null ? JSON.stringify(next.configOptions) : null;
34724
+ db.run(
34725
+ `INSERT INTO agent_session (session_key, acp_session_id, backend_agent_type, config_options_json, updated_at)
34726
+ VALUES (?, ?, ?, ?, ?)
34727
+ ON CONFLICT(session_key) DO UPDATE SET
34728
+ acp_session_id = excluded.acp_session_id,
34729
+ backend_agent_type = excluded.backend_agent_type,
34730
+ config_options_json = excluded.config_options_json,
34731
+ updated_at = excluded.updated_at`,
34732
+ [key, next.acpSessionId, next.backendAgentType, configJson, next.updatedAt]
34733
+ );
34391
34734
  } catch {
34392
34735
  }
34393
34736
  }
@@ -34411,7 +34754,7 @@ async function ensureAcpClient(options) {
34411
34754
  if (state.acpStartPromise && !state.acpHandle) {
34412
34755
  await state.acpStartPromise;
34413
34756
  }
34414
- if (state.acpHandle && state.lastAcpCwd != null && path13.resolve(state.lastAcpCwd) !== path13.resolve(targetSessionParentPath)) {
34757
+ if (state.acpHandle && state.lastAcpCwd != null && path19.resolve(state.lastAcpCwd) !== path19.resolve(targetSessionParentPath)) {
34415
34758
  try {
34416
34759
  state.acpHandle.disconnect();
34417
34760
  } catch {
@@ -34446,7 +34789,7 @@ async function ensureAcpClient(options) {
34446
34789
  if (!state.acpStartPromise) {
34447
34790
  let statOk = false;
34448
34791
  try {
34449
- const st = fs11.statSync(targetSessionParentPath);
34792
+ const st = fs16.statSync(targetSessionParentPath);
34450
34793
  statOk = st.isDirectory();
34451
34794
  if (!statOk) {
34452
34795
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
@@ -34693,12 +35036,12 @@ async function createAcpManager(options) {
34693
35036
  }
34694
35037
 
34695
35038
  // src/worktrees/session-worktree-manager.ts
34696
- import * as path20 from "node:path";
34697
- import os5 from "node:os";
35039
+ import * as path26 from "node:path";
35040
+ import os8 from "node:os";
34698
35041
 
34699
35042
  // src/worktrees/prepare-new-session-worktrees.ts
34700
- import * as fs13 from "node:fs";
34701
- import * as path15 from "node:path";
35043
+ import * as fs18 from "node:fs";
35044
+ import * as path21 from "node:path";
34702
35045
 
34703
35046
  // src/git/worktree-add.ts
34704
35047
  async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
@@ -34707,12 +35050,12 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
34707
35050
  }
34708
35051
 
34709
35052
  // src/worktrees/worktree-layout-file.ts
34710
- import * as fs12 from "node:fs";
34711
- import * as path14 from "node:path";
34712
- import os4 from "node:os";
35053
+ import * as fs17 from "node:fs";
35054
+ import * as path20 from "node:path";
35055
+ import os7 from "node:os";
34713
35056
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
34714
35057
  function defaultWorktreeLayoutPath() {
34715
- return path14.join(os4.homedir(), ".buildautomaton", LAYOUT_FILENAME);
35058
+ return path20.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
34716
35059
  }
34717
35060
  function normalizeLoadedLayout(raw) {
34718
35061
  if (raw && typeof raw === "object" && "launcherCwds" in raw) {
@@ -34724,8 +35067,8 @@ function normalizeLoadedLayout(raw) {
34724
35067
  function loadWorktreeLayout() {
34725
35068
  try {
34726
35069
  const p = defaultWorktreeLayoutPath();
34727
- if (!fs12.existsSync(p)) return { launcherCwds: [] };
34728
- const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
35070
+ if (!fs17.existsSync(p)) return { launcherCwds: [] };
35071
+ const raw = JSON.parse(fs17.readFileSync(p, "utf8"));
34729
35072
  return normalizeLoadedLayout(raw);
34730
35073
  } catch {
34731
35074
  return { launcherCwds: [] };
@@ -34733,24 +35076,24 @@ function loadWorktreeLayout() {
34733
35076
  }
34734
35077
  function saveWorktreeLayout(layout) {
34735
35078
  try {
34736
- const dir = path14.dirname(defaultWorktreeLayoutPath());
34737
- fs12.mkdirSync(dir, { recursive: true });
34738
- fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
35079
+ const dir = path20.dirname(defaultWorktreeLayoutPath());
35080
+ fs17.mkdirSync(dir, { recursive: true });
35081
+ fs17.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
34739
35082
  } catch {
34740
35083
  }
34741
35084
  }
34742
35085
  function baseNameSafe(pathString) {
34743
- return path14.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
35086
+ return path20.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
34744
35087
  }
34745
35088
  function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
34746
- const norm = path14.resolve(bridgeRootPath2);
34747
- const existing = layout.launcherCwds.find((e) => path14.resolve(e.absolutePath) === norm);
35089
+ const norm = path20.resolve(bridgeRootPath2);
35090
+ const existing = layout.launcherCwds.find((e) => path20.resolve(e.absolutePath) === norm);
34748
35091
  return existing?.dirName;
34749
35092
  }
34750
35093
  function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
34751
35094
  const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
34752
35095
  if (existing) return existing;
34753
- const norm = path14.resolve(bridgeRootPath2);
35096
+ const norm = path20.resolve(bridgeRootPath2);
34754
35097
  const base = baseNameSafe(norm);
34755
35098
  const used = new Set(layout.launcherCwds.map((e) => e.dirName));
34756
35099
  let name = base;
@@ -34767,10 +35110,10 @@ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
34767
35110
  // src/worktrees/prepare-new-session-worktrees.ts
34768
35111
  async function prepareNewSessionWorktrees(options) {
34769
35112
  const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
34770
- const bridgeResolved = path15.resolve(bridgeRoot);
35113
+ const bridgeResolved = path21.resolve(bridgeRoot);
34771
35114
  const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
34772
- const bridgeKeyDir = path15.join(worktreesRootPath, cwdKey);
34773
- const sessionDir = path15.join(bridgeKeyDir, sessionId);
35115
+ const bridgeKeyDir = path21.join(worktreesRootPath, cwdKey);
35116
+ const sessionDir = path21.join(bridgeKeyDir, sessionId);
34774
35117
  const repos = await discoverGitReposUnderRoot(bridgeResolved);
34775
35118
  if (repos.length === 0) {
34776
35119
  log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
@@ -34778,14 +35121,14 @@ async function prepareNewSessionWorktrees(options) {
34778
35121
  }
34779
35122
  const branch = `session-${sessionId}`;
34780
35123
  const worktreePaths = [];
34781
- fs13.mkdirSync(sessionDir, { recursive: true });
35124
+ fs18.mkdirSync(sessionDir, { recursive: true });
34782
35125
  for (const repo of repos) {
34783
- let rel = path15.relative(bridgeResolved, repo.absolutePath);
34784
- if (rel.startsWith("..") || path15.isAbsolute(rel)) continue;
35126
+ let rel = path21.relative(bridgeResolved, repo.absolutePath);
35127
+ if (rel.startsWith("..") || path21.isAbsolute(rel)) continue;
34785
35128
  const relNorm = rel === "" ? "." : rel;
34786
- const wtPath = relNorm === "." ? sessionDir : path15.join(sessionDir, relNorm);
35129
+ const wtPath = relNorm === "." ? sessionDir : path21.join(sessionDir, relNorm);
34787
35130
  if (relNorm !== ".") {
34788
- fs13.mkdirSync(path15.dirname(wtPath), { recursive: true });
35131
+ fs18.mkdirSync(path21.dirname(wtPath), { recursive: true });
34789
35132
  }
34790
35133
  try {
34791
35134
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
@@ -34827,23 +35170,23 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
34827
35170
  }
34828
35171
 
34829
35172
  // src/worktrees/remove-session-worktrees.ts
34830
- import * as fs16 from "node:fs";
35173
+ import * as fs21 from "node:fs";
34831
35174
 
34832
35175
  // src/git/worktree-remove.ts
34833
- import * as fs15 from "node:fs";
35176
+ import * as fs20 from "node:fs";
34834
35177
 
34835
35178
  // src/git/resolve-main-repo-from-git-file.ts
34836
- import * as fs14 from "node:fs";
34837
- import * as path16 from "node:path";
35179
+ import * as fs19 from "node:fs";
35180
+ import * as path22 from "node:path";
34838
35181
  function resolveMainRepoFromWorktreeGitFile(wt) {
34839
- const gitDirFile = path16.join(wt, ".git");
34840
- if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
34841
- const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
35182
+ const gitDirFile = path22.join(wt, ".git");
35183
+ if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
35184
+ const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
34842
35185
  const m = first2.match(/^gitdir:\s*(.+)$/im);
34843
35186
  if (!m) return "";
34844
- const gitWorktreePath = path16.resolve(wt, m[1].trim());
34845
- const gitDir = path16.dirname(path16.dirname(gitWorktreePath));
34846
- return path16.dirname(gitDir);
35187
+ const gitWorktreePath = path22.resolve(wt, m[1].trim());
35188
+ const gitDir = path22.dirname(path22.dirname(gitWorktreePath));
35189
+ return path22.dirname(gitDir);
34847
35190
  }
34848
35191
 
34849
35192
  // src/git/worktree-remove.ts
@@ -34852,7 +35195,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
34852
35195
  if (mainRepo) {
34853
35196
  await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
34854
35197
  } else {
34855
- fs15.rmSync(worktreePath, { recursive: true, force: true });
35198
+ fs20.rmSync(worktreePath, { recursive: true, force: true });
34856
35199
  }
34857
35200
  }
34858
35201
 
@@ -34865,7 +35208,7 @@ async function removeSessionWorktrees(paths, log2) {
34865
35208
  } catch (e) {
34866
35209
  log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
34867
35210
  try {
34868
- fs16.rmSync(wt, { recursive: true, force: true });
35211
+ fs21.rmSync(wt, { recursive: true, force: true });
34869
35212
  } catch {
34870
35213
  }
34871
35214
  }
@@ -35085,7 +35428,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
35085
35428
  }
35086
35429
 
35087
35430
  // src/git/working-directory/changes/get-working-tree-change-repo-details.ts
35088
- import * as path18 from "node:path";
35431
+ import * as path24 from "node:path";
35089
35432
 
35090
35433
  // src/git/working-directory/changes/parse-git-status.ts
35091
35434
  function parseNameStatusLines(lines) {
@@ -35205,8 +35548,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
35205
35548
  }
35206
35549
 
35207
35550
  // src/git/working-directory/changes/list-changed-files-for-repo.ts
35208
- import * as fs18 from "node:fs";
35209
- import * as path17 from "node:path";
35551
+ import * as fs23 from "node:fs";
35552
+ import * as path23 from "node:path";
35210
35553
 
35211
35554
  // src/git/working-directory/changes/count-lines.ts
35212
35555
  import { createReadStream } from "node:fs";
@@ -35230,7 +35573,7 @@ async function countTextFileLines(filePath) {
35230
35573
  }
35231
35574
 
35232
35575
  // src/git/working-directory/changes/hydrate-patch.ts
35233
- import * as fs17 from "node:fs";
35576
+ import * as fs22 from "node:fs";
35234
35577
  var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
35235
35578
  var MAX_HYDRATE_LINES_PER_GAP = 8e3;
35236
35579
  var MAX_HYDRATE_LINES_PER_FILE = 8e4;
@@ -35245,7 +35588,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
35245
35588
  }
35246
35589
  async function readWorktreeFileLines(filePath) {
35247
35590
  try {
35248
- const raw = await fs17.promises.readFile(filePath, "utf8");
35591
+ const raw = await fs22.promises.readFile(filePath, "utf8");
35249
35592
  return raw.split(/\r?\n/);
35250
35593
  } catch {
35251
35594
  return null;
@@ -35380,7 +35723,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
35380
35723
  const rows = [];
35381
35724
  for (const pathInRepo of paths) {
35382
35725
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
35383
- const repoFilePath = path17.join(repoGitCwd, pathInRepo);
35726
+ const repoFilePath = path23.join(repoGitCwd, pathInRepo);
35384
35727
  const nums = numByPath.get(pathInRepo);
35385
35728
  let additions = nums?.additions ?? 0;
35386
35729
  let deletions = nums?.deletions ?? 0;
@@ -35393,7 +35736,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
35393
35736
  deletions = fromGit.deletions;
35394
35737
  } else {
35395
35738
  try {
35396
- const st = await fs18.promises.stat(repoFilePath);
35739
+ const st = await fs23.promises.stat(repoFilePath);
35397
35740
  if (st.isFile()) additions = await countTextFileLines(repoFilePath);
35398
35741
  else additions = 0;
35399
35742
  } catch {
@@ -35419,7 +35762,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
35419
35762
  } else {
35420
35763
  pathInRepo = row.pathRelLauncher;
35421
35764
  }
35422
- const filePath = path17.join(repoGitCwd, pathInRepo);
35765
+ const filePath = path23.join(repoGitCwd, pathInRepo);
35423
35766
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
35424
35767
  if (patch) {
35425
35768
  patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
@@ -35435,8 +35778,8 @@ function normRepoRel(p) {
35435
35778
  return x === "" ? "." : x;
35436
35779
  }
35437
35780
  async function getWorkingTreeChangeRepoDetails(options) {
35438
- const bridgeRoot = path18.resolve(getBridgeRoot());
35439
- const sessionWtRoot = options.sessionWorktreeRootPath ? path18.resolve(options.sessionWorktreeRootPath) : null;
35781
+ const bridgeRoot = path24.resolve(getBridgeRoot());
35782
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path24.resolve(options.sessionWorktreeRootPath) : null;
35440
35783
  const legacyNested = options.legacyRepoNestedSessionLayout === true;
35441
35784
  const out = [];
35442
35785
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
@@ -35449,7 +35792,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
35449
35792
  }
35450
35793
  const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
35451
35794
  for (const target of options.commitTargetPaths) {
35452
- const t = path18.resolve(target);
35795
+ const t = path24.resolve(target);
35453
35796
  if (!await isGitRepoDirectory(t)) continue;
35454
35797
  const g = cliSimpleGit(t);
35455
35798
  let branch = "HEAD";
@@ -35462,8 +35805,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
35462
35805
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
35463
35806
  let repoRelPath;
35464
35807
  if (sessionWtRoot) {
35465
- const anchor = legacyNested ? path18.dirname(t) : t;
35466
- const relNorm = path18.relative(sessionWtRoot, anchor);
35808
+ const anchor = legacyNested ? path24.dirname(t) : t;
35809
+ const relNorm = path24.relative(sessionWtRoot, anchor);
35467
35810
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
35468
35811
  } else {
35469
35812
  let top = t;
@@ -35472,8 +35815,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
35472
35815
  } catch {
35473
35816
  top = t;
35474
35817
  }
35475
- const rel = path18.relative(bridgeRoot, path18.resolve(top)).replace(/\\/g, "/") || ".";
35476
- repoRelPath = rel.startsWith("..") ? path18.basename(path18.resolve(top)) : rel;
35818
+ const rel = path24.relative(bridgeRoot, path24.resolve(top)).replace(/\\/g, "/") || ".";
35819
+ repoRelPath = rel.startsWith("..") ? path24.basename(path24.resolve(top)) : rel;
35477
35820
  }
35478
35821
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
35479
35822
  if (filter && norm !== filter) continue;
@@ -35538,11 +35881,11 @@ async function commitSessionWorktrees(options) {
35538
35881
  }
35539
35882
 
35540
35883
  // src/worktrees/discover-session-worktree-on-disk.ts
35541
- import * as fs19 from "node:fs";
35542
- import * as path19 from "node:path";
35884
+ import * as fs24 from "node:fs";
35885
+ import * as path25 from "node:path";
35543
35886
  function isGitDir(dirPath) {
35544
35887
  try {
35545
- return fs19.existsSync(path19.join(dirPath, ".git"));
35888
+ return fs24.existsSync(path25.join(dirPath, ".git"));
35546
35889
  } catch {
35547
35890
  return false;
35548
35891
  }
@@ -35551,23 +35894,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
35551
35894
  const out = [];
35552
35895
  const walk = (dir) => {
35553
35896
  if (isGitDir(dir)) {
35554
- out.push(path19.resolve(dir));
35897
+ out.push(path25.resolve(dir));
35555
35898
  return;
35556
35899
  }
35557
35900
  let entries;
35558
35901
  try {
35559
- entries = fs19.readdirSync(dir, { withFileTypes: true });
35902
+ entries = fs24.readdirSync(dir, { withFileTypes: true });
35560
35903
  } catch {
35561
35904
  return;
35562
35905
  }
35563
35906
  for (const e of entries) {
35564
35907
  if (e.name.startsWith(".")) continue;
35565
- const full = path19.join(dir, e.name);
35908
+ const full = path25.join(dir, e.name);
35566
35909
  if (!e.isDirectory()) continue;
35567
35910
  walk(full);
35568
35911
  }
35569
35912
  };
35570
- walk(path19.resolve(rootPath));
35913
+ walk(path25.resolve(rootPath));
35571
35914
  return [...new Set(out)];
35572
35915
  }
35573
35916
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
@@ -35576,16 +35919,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
35576
35919
  if (depth > maxDepth) return;
35577
35920
  let entries;
35578
35921
  try {
35579
- entries = fs19.readdirSync(dir, { withFileTypes: true });
35922
+ entries = fs24.readdirSync(dir, { withFileTypes: true });
35580
35923
  } catch {
35581
35924
  return;
35582
35925
  }
35583
35926
  for (const e of entries) {
35584
35927
  if (e.name.startsWith(".")) continue;
35585
- const full = path19.join(dir, e.name);
35928
+ const full = path25.join(dir, e.name);
35586
35929
  if (!e.isDirectory()) continue;
35587
35930
  if (e.name === sessionId) {
35588
- if (isGitDir(full)) out.push(path19.resolve(full));
35931
+ if (isGitDir(full)) out.push(path25.resolve(full));
35589
35932
  } else {
35590
35933
  walk(full, depth + 1);
35591
35934
  }
@@ -35597,14 +35940,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
35597
35940
  function tryBindingFromSessionDirectory(sessionDir) {
35598
35941
  let st;
35599
35942
  try {
35600
- st = fs19.statSync(sessionDir);
35943
+ st = fs24.statSync(sessionDir);
35601
35944
  } catch {
35602
35945
  return null;
35603
35946
  }
35604
35947
  if (!st.isDirectory()) return null;
35605
35948
  const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
35606
35949
  if (worktreePaths.length === 0) return null;
35607
- const abs = path19.resolve(sessionDir);
35950
+ const abs = path25.resolve(sessionDir);
35608
35951
  return {
35609
35952
  sessionParentPath: abs,
35610
35953
  workingTreeRelRoot: abs,
@@ -35614,20 +35957,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
35614
35957
  function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
35615
35958
  const sid = sessionId.trim();
35616
35959
  if (!sid) return null;
35617
- const hintR = path19.resolve(checkoutPath);
35960
+ const hintR = path25.resolve(checkoutPath);
35618
35961
  let best = null;
35619
- let cur = path19.dirname(hintR);
35962
+ let cur = path25.dirname(hintR);
35620
35963
  for (let i = 0; i < 40; i++) {
35621
35964
  const paths = collectWorktreeRootsNamed(cur, sid, 24);
35622
- if (paths.some((p) => path19.resolve(p) === hintR)) {
35623
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path19.resolve(paths[0]);
35965
+ if (paths.some((p) => path25.resolve(p) === hintR)) {
35966
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path25.resolve(paths[0]);
35624
35967
  best = {
35625
- sessionParentPath: path19.resolve(isolated),
35626
- workingTreeRelRoot: path19.resolve(cur),
35627
- repoCheckoutPaths: paths.map((p) => path19.resolve(p))
35968
+ sessionParentPath: path25.resolve(isolated),
35969
+ workingTreeRelRoot: path25.resolve(cur),
35970
+ repoCheckoutPaths: paths.map((p) => path25.resolve(p))
35628
35971
  };
35629
35972
  }
35630
- const next = path19.dirname(cur);
35973
+ const next = path25.dirname(cur);
35631
35974
  if (next === cur) break;
35632
35975
  cur = next;
35633
35976
  }
@@ -35635,33 +35978,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
35635
35978
  }
35636
35979
  function discoverSessionWorktreeOnDisk(options) {
35637
35980
  const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
35638
- if (!sessionId.trim() || !fs19.existsSync(worktreesRootPath)) return null;
35981
+ if (!sessionId.trim() || !fs24.existsSync(worktreesRootPath)) return null;
35639
35982
  const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
35640
35983
  const keys = [];
35641
35984
  if (preferredKey) keys.push(preferredKey);
35642
35985
  try {
35643
- for (const name of fs19.readdirSync(worktreesRootPath)) {
35986
+ for (const name of fs24.readdirSync(worktreesRootPath)) {
35644
35987
  if (name.startsWith(".")) continue;
35645
- const p = path19.join(worktreesRootPath, name);
35646
- if (!fs19.statSync(p).isDirectory()) continue;
35988
+ const p = path25.join(worktreesRootPath, name);
35989
+ if (!fs24.statSync(p).isDirectory()) continue;
35647
35990
  if (name !== preferredKey) keys.push(name);
35648
35991
  }
35649
35992
  } catch {
35650
35993
  return null;
35651
35994
  }
35652
35995
  for (const key of keys) {
35653
- const layoutRoot = path19.join(worktreesRootPath, key);
35654
- if (!fs19.existsSync(layoutRoot) || !fs19.statSync(layoutRoot).isDirectory()) continue;
35655
- const sessionDir = path19.join(layoutRoot, sessionId);
35996
+ const layoutRoot = path25.join(worktreesRootPath, key);
35997
+ if (!fs24.existsSync(layoutRoot) || !fs24.statSync(layoutRoot).isDirectory()) continue;
35998
+ const sessionDir = path25.join(layoutRoot, sessionId);
35656
35999
  const nested = tryBindingFromSessionDirectory(sessionDir);
35657
36000
  if (nested) return nested;
35658
36001
  const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
35659
36002
  if (legacyPaths.length > 0) {
35660
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path19.resolve(legacyPaths[0]);
36003
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
35661
36004
  return {
35662
- sessionParentPath: path19.resolve(isolated),
35663
- workingTreeRelRoot: path19.resolve(layoutRoot),
35664
- repoCheckoutPaths: legacyPaths.map((p) => path19.resolve(p))
36005
+ sessionParentPath: path25.resolve(isolated),
36006
+ workingTreeRelRoot: path25.resolve(layoutRoot),
36007
+ repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
35665
36008
  };
35666
36009
  }
35667
36010
  }
@@ -35670,12 +36013,12 @@ function discoverSessionWorktreeOnDisk(options) {
35670
36013
  function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
35671
36014
  const sid = sessionId.trim();
35672
36015
  if (!sid) return null;
35673
- const hint = path19.resolve(sessionWorktreeRootPathOrHint);
35674
- const underHint = tryBindingFromSessionDirectory(path19.join(hint, sid));
36016
+ const hint = path25.resolve(sessionWorktreeRootPathOrHint);
36017
+ const underHint = tryBindingFromSessionDirectory(path25.join(hint, sid));
35675
36018
  if (underHint) return underHint;
35676
36019
  const direct = tryBindingFromSessionDirectory(hint);
35677
36020
  if (direct) {
35678
- if (path19.basename(hint) === sid && isGitDir(hint)) {
36021
+ if (path25.basename(hint) === sid && isGitDir(hint)) {
35679
36022
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
35680
36023
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
35681
36024
  return legacyFromCheckout;
@@ -35683,24 +36026,24 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
35683
36026
  }
35684
36027
  return direct;
35685
36028
  }
35686
- if (path19.basename(hint) === sid && isGitDir(hint)) {
36029
+ if (path25.basename(hint) === sid && isGitDir(hint)) {
35687
36030
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
35688
36031
  if (legacyFromCheckout) return legacyFromCheckout;
35689
36032
  }
35690
36033
  let st;
35691
36034
  try {
35692
- st = fs19.statSync(hint);
36035
+ st = fs24.statSync(hint);
35693
36036
  } catch {
35694
36037
  return null;
35695
36038
  }
35696
36039
  if (!st.isDirectory()) return null;
35697
36040
  const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
35698
36041
  if (legacyPaths.length === 0) return null;
35699
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path19.resolve(legacyPaths[0]);
36042
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
35700
36043
  return {
35701
- sessionParentPath: path19.resolve(isolated),
36044
+ sessionParentPath: path25.resolve(isolated),
35702
36045
  workingTreeRelRoot: hint,
35703
- repoCheckoutPaths: legacyPaths.map((p) => path19.resolve(p))
36046
+ repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
35704
36047
  };
35705
36048
  }
35706
36049
 
@@ -35723,10 +36066,10 @@ var SessionWorktreeManager = class {
35723
36066
  this.layout = loadWorktreeLayout();
35724
36067
  }
35725
36068
  rememberSessionWorktrees(sessionId, binding) {
35726
- const paths = binding.repoCheckoutPaths.map((p) => path20.resolve(p));
36069
+ const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
35727
36070
  this.sessionRepoCheckoutPaths.set(sessionId, paths);
35728
- this.sessionParentPathBySession.set(sessionId, path20.resolve(binding.sessionParentPath));
35729
- this.sessionWorkingTreeRelRootBySession.set(sessionId, path20.resolve(binding.workingTreeRelRoot));
36071
+ this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
36072
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path26.resolve(binding.workingTreeRelRoot));
35730
36073
  }
35731
36074
  sessionParentPathAfterRemember(sessionId) {
35732
36075
  return this.sessionParentPathBySession.get(sessionId);
@@ -35743,7 +36086,7 @@ var SessionWorktreeManager = class {
35743
36086
  const parent = this.sessionParentPathBySession.get(sessionId);
35744
36087
  const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
35745
36088
  if (!parent || !relRoot) return false;
35746
- return path20.resolve(parent) !== path20.resolve(relRoot);
36089
+ return path26.resolve(parent) !== path26.resolve(relRoot);
35747
36090
  }
35748
36091
  /**
35749
36092
  * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
@@ -35752,7 +36095,7 @@ var SessionWorktreeManager = class {
35752
36095
  if (!sessionId) return null;
35753
36096
  const sid = sessionId.trim();
35754
36097
  const cached2 = this.sessionParentPathBySession.get(sid);
35755
- if (cached2) return path20.resolve(cached2);
36098
+ if (cached2) return path26.resolve(cached2);
35756
36099
  const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
35757
36100
  if (!paths?.length) return null;
35758
36101
  return resolveIsolatedSessionParentPathFromCheckouts(paths);
@@ -35766,7 +36109,7 @@ var SessionWorktreeManager = class {
35766
36109
  const sid = sessionId.trim();
35767
36110
  const parentPathRaw = opts.sessionParentPath?.trim();
35768
36111
  if (parentPathRaw) {
35769
- const resolved = path20.resolve(parentPathRaw);
36112
+ const resolved = path26.resolve(parentPathRaw);
35770
36113
  if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
35771
36114
  const diskFirst = this.tryDiscoverFromDisk(sid);
35772
36115
  if (diskFirst) {
@@ -35785,7 +36128,7 @@ var SessionWorktreeManager = class {
35785
36128
  this.rememberSessionWorktrees(sid, tryRoot);
35786
36129
  return this.sessionParentPathAfterRemember(sid);
35787
36130
  }
35788
- const next = path20.dirname(cur);
36131
+ const next = path26.dirname(cur);
35789
36132
  if (next === cur) break;
35790
36133
  cur = next;
35791
36134
  }
@@ -35938,15 +36281,22 @@ var SessionWorktreeManager = class {
35938
36281
  }
35939
36282
  };
35940
36283
  function defaultWorktreesRootPath() {
35941
- return path20.join(os5.homedir(), ".buildautomaton", "worktrees");
36284
+ return path26.join(os8.homedir(), ".buildautomaton", "worktrees");
35942
36285
  }
35943
36286
 
35944
36287
  // src/files/watch-file-index.ts
35945
36288
  import { watch } from "node:fs";
36289
+ import path31 from "node:path";
36290
+
36291
+ // src/files/index/paths.ts
35946
36292
  import path27 from "node:path";
36293
+ import crypto2 from "node:crypto";
36294
+ function getCwdHashForFileIndex(resolvedCwd) {
36295
+ return crypto2.createHash("sha256").update(path27.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
36296
+ }
35947
36297
 
35948
36298
  // src/files/index/build-file-index.ts
35949
- import path24 from "node:path";
36299
+ import path29 from "node:path";
35950
36300
 
35951
36301
  // src/runtime/yield-to-event-loop.ts
35952
36302
  function yieldToEventLoop() {
@@ -35954,47 +36304,12 @@ function yieldToEventLoop() {
35954
36304
  }
35955
36305
 
35956
36306
  // src/files/index/walk-workspace-tree.ts
35957
- import fs20 from "node:fs";
35958
- import path22 from "node:path";
35959
-
35960
- // src/files/index/constants.ts
35961
- import path21 from "node:path";
35962
- import os6 from "node:os";
35963
- var INDEX_WORK_YIELD_EVERY = 256;
35964
- var INDEX_DIR = path21.join(os6.homedir(), ".buildautomaton");
35965
- var INDEX_HASH_LEN = 16;
35966
- var INDEX_VERSION = 2;
35967
- var INDEX_LOG_PREFIX = "[file-index]";
35968
-
35969
- // src/files/index/walk-workspace-tree.ts
35970
- function walkWorkspaceTreeSync(dir, baseDir, out) {
35971
- let names;
35972
- try {
35973
- names = fs20.readdirSync(dir);
35974
- } catch {
35975
- return;
35976
- }
35977
- for (const name of names) {
35978
- if (name.startsWith(".")) continue;
35979
- const full = path22.join(dir, name);
35980
- let stat3;
35981
- try {
35982
- stat3 = fs20.statSync(full);
35983
- } catch {
35984
- continue;
35985
- }
35986
- const relative5 = path22.relative(baseDir, full).replace(/\\/g, "/");
35987
- if (stat3.isDirectory()) {
35988
- walkWorkspaceTreeSync(full, baseDir, out);
35989
- } else if (stat3.isFile()) {
35990
- out.push(relative5);
35991
- }
35992
- }
35993
- }
36307
+ import fs25 from "node:fs";
36308
+ import path28 from "node:path";
35994
36309
  async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
35995
36310
  let names;
35996
36311
  try {
35997
- names = await fs20.promises.readdir(dir);
36312
+ names = await fs25.promises.readdir(dir);
35998
36313
  } catch {
35999
36314
  return;
36000
36315
  }
@@ -36004,14 +36319,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
36004
36319
  await yieldToEventLoop();
36005
36320
  }
36006
36321
  state.n++;
36007
- const full = path22.join(dir, name);
36322
+ const full = path28.join(dir, name);
36008
36323
  let stat3;
36009
36324
  try {
36010
- stat3 = await fs20.promises.stat(full);
36325
+ stat3 = await fs25.promises.stat(full);
36011
36326
  } catch {
36012
36327
  continue;
36013
36328
  }
36014
- const relative5 = path22.relative(baseDir, full).replace(/\\/g, "/");
36329
+ const relative5 = path28.relative(baseDir, full).replace(/\\/g, "/");
36015
36330
  if (stat3.isDirectory()) {
36016
36331
  await walkWorkspaceTreeAsync(full, baseDir, out, state);
36017
36332
  } else if (stat3.isFile()) {
@@ -36023,205 +36338,124 @@ function createWalkYieldState() {
36023
36338
  return { n: 0 };
36024
36339
  }
36025
36340
 
36026
- // src/files/index/trigram-utils.ts
36027
- function getTrigrams(s) {
36028
- const lower = s.toLowerCase();
36029
- const out = [];
36030
- for (let i = 0; i <= lower.length - 3; i++) {
36031
- out.push(lower.slice(i, i + 3));
36032
- }
36033
- return out;
36034
- }
36035
- function binarySearch(arr, x) {
36036
- let lo = 0;
36037
- let hi = arr.length - 1;
36038
- while (lo <= hi) {
36039
- const mid = lo + hi >>> 1;
36040
- if (arr[mid] < x) lo = mid + 1;
36041
- else if (arr[mid] > x) hi = mid - 1;
36042
- else return mid;
36043
- }
36044
- return -1;
36045
- }
36046
- function intersectSortedTrigramSets(arrays) {
36047
- if (arrays.length === 0) return [];
36048
- if (arrays.length === 1) return arrays[0];
36049
- const byLength = arrays.slice().sort((a, b) => a.length - b.length);
36050
- const smallest = byLength[0];
36051
- const rest = byLength.slice(1);
36052
- const result = [];
36053
- for (const idx of smallest) {
36054
- if (rest.every((arr) => binarySearch(arr, idx) >= 0)) {
36055
- result.push(idx);
36056
- }
36057
- }
36058
- return result;
36059
- }
36060
-
36061
- // src/files/index/build-trigram-map.ts
36062
- function buildTrigramMapForPaths(paths) {
36063
- const trigramIndex = {};
36064
- for (let i = 0; i < paths.length; i++) {
36065
- const trigrams = getTrigrams(paths[i]);
36066
- const seen = /* @__PURE__ */ new Set();
36067
- for (const tri of trigrams) {
36068
- if (seen.has(tri)) continue;
36069
- seen.add(tri);
36070
- if (!trigramIndex[tri]) trigramIndex[tri] = [];
36071
- trigramIndex[tri].push(i);
36072
- }
36073
- }
36074
- return trigramIndex;
36341
+ // src/files/index/file-index-sqlite-lock.ts
36342
+ import fs26 from "node:fs";
36343
+ function isSqliteCorruptError(e) {
36344
+ const msg = e instanceof Error ? e.message : String(e);
36345
+ return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
36075
36346
  }
36076
- async function buildTrigramMapForPathsAsync(paths) {
36077
- const trigramIndex = {};
36078
- for (let i = 0; i < paths.length; i++) {
36079
- if (i > 0 && i % INDEX_WORK_YIELD_EVERY === 0) {
36080
- await yieldToEventLoop();
36081
- }
36082
- const trigrams = getTrigrams(paths[i]);
36083
- const seen = /* @__PURE__ */ new Set();
36084
- for (const tri of trigrams) {
36085
- if (seen.has(tri)) continue;
36086
- seen.add(tri);
36087
- if (!trigramIndex[tri]) trigramIndex[tri] = [];
36088
- trigramIndex[tri].push(i);
36347
+ var chain = Promise.resolve();
36348
+ function withFileIndexSqliteLock(fn) {
36349
+ const run = async () => {
36350
+ try {
36351
+ return await Promise.resolve(fn());
36352
+ } catch (e) {
36353
+ if (!isSqliteCorruptError(e)) throw e;
36354
+ closeAllCliSqliteConnections();
36355
+ try {
36356
+ fs26.unlinkSync(getCliSqlitePath());
36357
+ } catch {
36358
+ }
36359
+ chain = Promise.resolve();
36360
+ return await Promise.resolve(fn());
36089
36361
  }
36090
- }
36091
- return trigramIndex;
36092
- }
36093
-
36094
- // src/files/index/write-index-file.ts
36095
- import fs21 from "node:fs";
36096
-
36097
- // src/files/index/paths.ts
36098
- import path23 from "node:path";
36099
- import crypto2 from "node:crypto";
36100
- function getIndexPathForCwd(resolvedCwd) {
36101
- const hash = crypto2.createHash("sha256").update(resolvedCwd).digest("hex").slice(0, INDEX_HASH_LEN);
36102
- return path23.join(INDEX_DIR, `.file-index-${hash}.json`);
36103
- }
36104
-
36105
- // src/files/index/write-index-file.ts
36106
- function writeIndexFileSync(resolvedCwd, data) {
36107
- const indexPath = getIndexPathForCwd(resolvedCwd);
36108
- try {
36109
- if (!fs21.existsSync(INDEX_DIR)) fs21.mkdirSync(INDEX_DIR, { recursive: true });
36110
- fs21.writeFileSync(indexPath, JSON.stringify(data), "utf8");
36111
- } catch (e) {
36112
- console.error(`${INDEX_LOG_PREFIX} Failed to write index:`, e);
36113
- }
36114
- }
36115
- async function writeIndexFileAsync(resolvedCwd, data) {
36116
- const indexPath = getIndexPathForCwd(resolvedCwd);
36117
- try {
36118
- await fs21.promises.mkdir(INDEX_DIR, { recursive: true });
36119
- await fs21.promises.writeFile(indexPath, JSON.stringify(data), "utf8");
36120
- } catch (e) {
36121
- console.error(`${INDEX_LOG_PREFIX} Failed to write index:`, e);
36122
- }
36123
- }
36124
- function makeTrigramIndexData(paths, trigramIndex) {
36125
- return { version: INDEX_VERSION, paths, trigramIndex };
36362
+ };
36363
+ const next = chain.then(run);
36364
+ chain = next.then(
36365
+ () => void 0,
36366
+ () => void 0
36367
+ );
36368
+ return next;
36126
36369
  }
36127
36370
 
36128
36371
  // src/files/index/build-file-index.ts
36129
36372
  function sortPaths(paths) {
36130
36373
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
36131
36374
  }
36132
- function buildFileIndex(cwd) {
36133
- const resolved = path24.resolve(cwd);
36134
- const paths = [];
36135
- walkWorkspaceTreeSync(resolved, resolved, paths);
36136
- sortPaths(paths);
36137
- const trigramIndex = buildTrigramMapForPaths(paths);
36138
- const data = makeTrigramIndexData(paths, trigramIndex);
36139
- writeIndexFileSync(resolved, data);
36140
- return data;
36141
- }
36142
- async function buildFileIndexAsync(cwd) {
36143
- const resolved = path24.resolve(cwd);
36144
- const paths = [];
36145
- await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
36146
- await yieldToEventLoop();
36147
- sortPaths(paths);
36148
- const trigramIndex = await buildTrigramMapForPathsAsync(paths);
36149
- const data = makeTrigramIndexData(paths, trigramIndex);
36150
- await writeIndexFileAsync(resolved, data);
36151
- return data;
36152
- }
36153
-
36154
- // src/files/index/load-file-index.ts
36155
- import fs22 from "node:fs";
36156
- import path25 from "node:path";
36157
- function loadFileIndex(cwd) {
36158
- const resolved = path25.resolve(cwd);
36159
- const indexPath = getIndexPathForCwd(resolved);
36375
+ function persistPathsToSqlite(resolved, paths) {
36376
+ const db = getCliDatabase();
36377
+ const h = getCwdHashForFileIndex(resolved);
36378
+ db.run("BEGIN IMMEDIATE");
36160
36379
  try {
36161
- const raw = fs22.readFileSync(indexPath, "utf8");
36162
- const parsed = JSON.parse(raw);
36163
- if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
36164
- const obj = parsed;
36165
- if (obj.version === INDEX_VERSION && obj.trigramIndex && typeof obj.trigramIndex === "object") {
36166
- return obj;
36380
+ db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
36381
+ const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
36382
+ try {
36383
+ for (const rel of paths) {
36384
+ ins.run([h, rel]);
36167
36385
  }
36168
- return buildFileIndex(resolved);
36386
+ } finally {
36387
+ ins.finalize();
36169
36388
  }
36170
- if (Array.isArray(parsed) && parsed.every((p) => typeof p === "string")) {
36171
- return buildFileIndex(resolved);
36389
+ db.run("COMMIT");
36390
+ } catch (e) {
36391
+ try {
36392
+ db.run("ROLLBACK");
36393
+ } catch {
36172
36394
  }
36173
- return null;
36174
- } catch {
36175
- return null;
36395
+ throw e;
36176
36396
  }
36177
36397
  }
36398
+ async function buildFileIndexAsync(cwd) {
36399
+ return withFileIndexSqliteLock(async () => {
36400
+ const resolved = path29.resolve(cwd);
36401
+ const paths = [];
36402
+ await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
36403
+ await yieldToEventLoop();
36404
+ sortPaths(paths);
36405
+ persistPathsToSqlite(resolved, paths);
36406
+ return { pathCount: paths.length };
36407
+ });
36408
+ }
36178
36409
 
36179
36410
  // src/files/index/ensure-file-index.ts
36180
- import path26 from "node:path";
36181
- async function ensureFileIndexAsync(cwd) {
36182
- const resolved = path26.resolve(cwd);
36183
- const cached2 = loadFileIndex(resolved);
36184
- if (cached2 !== null) return { data: cached2, fromCache: true };
36185
- const data = await buildFileIndexAsync(resolved);
36186
- return { data, fromCache: false };
36187
- }
36411
+ import path30 from "node:path";
36188
36412
 
36189
36413
  // src/files/index/search-file-index.ts
36190
- function candidatePathIndices(index, q) {
36191
- const { paths, trigramIndex } = index;
36192
- if (q.length < 3) {
36193
- return paths.map((_, i) => i).filter((i) => paths[i].toLowerCase().includes(q));
36194
- }
36195
- const trigrams = getTrigrams(q);
36196
- if (trigrams.length === 0) {
36197
- return paths.map((_, i) => i).filter((i) => paths[i].toLowerCase().includes(q));
36198
- }
36199
- const arrays = trigrams.map((tri) => trigramIndex[tri]).filter((arr) => arr != null && arr.length > 0);
36200
- if (arrays.length === 0) return [];
36201
- return intersectSortedTrigramSets(arrays);
36202
- }
36203
- async function searchFileIndexAsync(index, query, limit = 100) {
36204
- await yieldToEventLoop();
36414
+ function escapeLikePattern(fragment) {
36415
+ return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
36416
+ }
36417
+ function bridgeFileIndexIsPopulated(resolvedCwd) {
36418
+ const db = getCliDatabase();
36419
+ const h = getCwdHashForFileIndex(resolvedCwd);
36420
+ const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
36421
+ return row != null;
36422
+ }
36423
+ function bridgeFileIndexPathCount(resolvedCwd) {
36424
+ const db = getCliDatabase();
36425
+ const h = getCwdHashForFileIndex(resolvedCwd);
36426
+ const row = db.get("SELECT COUNT(*) as c FROM file_index_path WHERE cwd_hash = ?", [h]);
36427
+ const c = row?.c ?? 0;
36428
+ return Number(c);
36429
+ }
36430
+ function searchBridgeFilePaths(resolvedCwd, query, limit = 100) {
36205
36431
  const q = query.trim().toLowerCase();
36206
36432
  if (!q) return [];
36207
- const { paths } = index;
36208
- const candidateIndices = candidatePathIndices(index, q);
36209
- const out = [];
36210
- let n = 0;
36211
- for (const i of candidateIndices) {
36212
- if (n > 0 && n % INDEX_WORK_YIELD_EVERY === 0) {
36213
- await yieldToEventLoop();
36214
- }
36215
- const p = paths[i];
36216
- if (p.toLowerCase().includes(q)) {
36217
- out.push(p);
36218
- if (out.length >= limit) break;
36219
- }
36220
- n++;
36221
- }
36433
+ const db = getCliDatabase();
36434
+ const h = getCwdHashForFileIndex(resolvedCwd);
36435
+ const pattern = `%${escapeLikePattern(q)}%`;
36436
+ const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
36437
+ const rows = db.all(
36438
+ `SELECT path FROM file_index_path WHERE cwd_hash = ? AND lower(path) LIKE ? ESCAPE '\\' LIMIT ?`,
36439
+ [h, pattern, lim]
36440
+ );
36441
+ return rows.map((r) => String(r.path));
36442
+ }
36443
+ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
36444
+ await yieldToEventLoop();
36445
+ const out = searchBridgeFilePaths(resolvedCwd, query, limit);
36446
+ if (out.length >= INDEX_WORK_YIELD_EVERY) await yieldToEventLoop();
36222
36447
  return out;
36223
36448
  }
36224
36449
 
36450
+ // src/files/index/ensure-file-index.ts
36451
+ async function ensureFileIndexAsync(cwd) {
36452
+ const resolved = path30.resolve(cwd);
36453
+ if (bridgeFileIndexIsPopulated(resolved)) {
36454
+ return { fromCache: true, pathCount: bridgeFileIndexPathCount(resolved) };
36455
+ }
36456
+ return { ...await buildFileIndexAsync(resolved), fromCache: false };
36457
+ }
36458
+
36225
36459
  // src/files/watch-file-index.ts
36226
36460
  var DEBOUNCE_MS = 900;
36227
36461
  function shouldIgnoreRelative(rel) {
@@ -36262,7 +36496,7 @@ function createFsWatcher(resolved, schedule) {
36262
36496
  }
36263
36497
  }
36264
36498
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
36265
- const resolved = path27.resolve(cwd);
36499
+ const resolved = path31.resolve(cwd);
36266
36500
  void buildFileIndexAsync(resolved).catch((e) => {
36267
36501
  console.error("[file-index] Initial index build failed:", e);
36268
36502
  });
@@ -36290,7 +36524,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
36290
36524
  }
36291
36525
 
36292
36526
  // src/connection/create-bridge-connection.ts
36293
- import * as path35 from "node:path";
36527
+ import * as path39 from "node:path";
36294
36528
 
36295
36529
  // src/dev-servers/manager/dev-server-manager.ts
36296
36530
  import { rm as rm2 } from "node:fs/promises";
@@ -36334,7 +36568,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
36334
36568
  }
36335
36569
 
36336
36570
  // src/dev-servers/process/wire-dev-server-child-process.ts
36337
- import fs23 from "node:fs";
36571
+ import fs27 from "node:fs";
36338
36572
 
36339
36573
  // src/dev-servers/manager/forward-pipe.ts
36340
36574
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -36370,7 +36604,7 @@ function wireDevServerChildProcess(d) {
36370
36604
  d.setPollInterval(void 0);
36371
36605
  return;
36372
36606
  }
36373
- fs23.readFile(d.mergedLogPath, (err, buf) => {
36607
+ fs27.readFile(d.mergedLogPath, (err, buf) => {
36374
36608
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
36375
36609
  if (buf.length <= d.mergedReadPos.value) return;
36376
36610
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -36408,7 +36642,7 @@ ${errTail}` : ""}`);
36408
36642
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
36409
36643
  };
36410
36644
  if (mergedPath) {
36411
- fs23.readFile(mergedPath, (err, buf) => {
36645
+ fs27.readFile(mergedPath, (err, buf) => {
36412
36646
  if (!err && buf.length > d.mergedReadPos.value) {
36413
36647
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
36414
36648
  if (chunk.length > 0) {
@@ -36510,13 +36744,13 @@ function parseDevServerDefs(servers) {
36510
36744
  }
36511
36745
 
36512
36746
  // src/dev-servers/manager/shell-spawn/utils.ts
36513
- import fs24 from "node:fs";
36747
+ import fs28 from "node:fs";
36514
36748
  function isSpawnEbadf(e) {
36515
36749
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
36516
36750
  }
36517
36751
  function rmDirQuiet(dir) {
36518
36752
  try {
36519
- fs24.rmSync(dir, { recursive: true, force: true });
36753
+ fs28.rmSync(dir, { recursive: true, force: true });
36520
36754
  } catch {
36521
36755
  }
36522
36756
  }
@@ -36524,7 +36758,7 @@ var cachedDevNullReadFd;
36524
36758
  function devNullReadFd() {
36525
36759
  if (cachedDevNullReadFd === void 0) {
36526
36760
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
36527
- cachedDevNullReadFd = fs24.openSync(devPath, "r");
36761
+ cachedDevNullReadFd = fs28.openSync(devPath, "r");
36528
36762
  }
36529
36763
  return cachedDevNullReadFd;
36530
36764
  }
@@ -36598,15 +36832,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
36598
36832
 
36599
36833
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
36600
36834
  import { spawn as spawn6 } from "node:child_process";
36601
- import fs25 from "node:fs";
36835
+ import fs29 from "node:fs";
36602
36836
  import { tmpdir } from "node:os";
36603
- import path28 from "node:path";
36837
+ import path32 from "node:path";
36604
36838
  function trySpawnMergedLogFile(command, env, cwd, signal) {
36605
- const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir(), "ba-devsrv-log-"));
36606
- const logPath = path28.join(tmpRoot, "combined.log");
36839
+ const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir(), "ba-devsrv-log-"));
36840
+ const logPath = path32.join(tmpRoot, "combined.log");
36607
36841
  let logFd;
36608
36842
  try {
36609
- logFd = fs25.openSync(logPath, "a");
36843
+ logFd = fs29.openSync(logPath, "a");
36610
36844
  } catch {
36611
36845
  rmDirQuiet(tmpRoot);
36612
36846
  return null;
@@ -36625,7 +36859,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
36625
36859
  } else {
36626
36860
  proc = spawn6("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
36627
36861
  }
36628
- fs25.closeSync(logFd);
36862
+ fs29.closeSync(logFd);
36629
36863
  return {
36630
36864
  proc,
36631
36865
  pipedStdoutStderr: true,
@@ -36634,7 +36868,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
36634
36868
  };
36635
36869
  } catch (e) {
36636
36870
  try {
36637
- fs25.closeSync(logFd);
36871
+ fs29.closeSync(logFd);
36638
36872
  } catch {
36639
36873
  }
36640
36874
  rmDirQuiet(tmpRoot);
@@ -36645,22 +36879,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
36645
36879
 
36646
36880
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
36647
36881
  import { spawn as spawn7 } from "node:child_process";
36648
- import fs26 from "node:fs";
36882
+ import fs30 from "node:fs";
36649
36883
  import { tmpdir as tmpdir2 } from "node:os";
36650
- import path29 from "node:path";
36884
+ import path33 from "node:path";
36651
36885
  function shSingleQuote(s) {
36652
36886
  return `'${s.replace(/'/g, `'\\''`)}'`;
36653
36887
  }
36654
36888
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
36655
- const tmpRoot = fs26.mkdtempSync(path29.join(tmpdir2(), "ba-devsrv-sh-"));
36656
- const logPath = path29.join(tmpRoot, "combined.log");
36657
- const innerPath = path29.join(tmpRoot, "_cmd.sh");
36658
- const runnerPath = path29.join(tmpRoot, "_run.sh");
36889
+ const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
36890
+ const logPath = path33.join(tmpRoot, "combined.log");
36891
+ const innerPath = path33.join(tmpRoot, "_cmd.sh");
36892
+ const runnerPath = path33.join(tmpRoot, "_run.sh");
36659
36893
  try {
36660
- fs26.writeFileSync(innerPath, `#!/bin/sh
36894
+ fs30.writeFileSync(innerPath, `#!/bin/sh
36661
36895
  ${command}
36662
36896
  `);
36663
- fs26.writeFileSync(
36897
+ fs30.writeFileSync(
36664
36898
  runnerPath,
36665
36899
  `#!/bin/sh
36666
36900
  cd ${shSingleQuote(cwd)}
@@ -36686,13 +36920,13 @@ cd ${shSingleQuote(cwd)}
36686
36920
  }
36687
36921
  }
36688
36922
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
36689
- const tmpRoot = fs26.mkdtempSync(path29.join(tmpdir2(), "ba-devsrv-sh-"));
36690
- const logPath = path29.join(tmpRoot, "combined.log");
36691
- const runnerPath = path29.join(tmpRoot, "_run.bat");
36923
+ const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
36924
+ const logPath = path33.join(tmpRoot, "combined.log");
36925
+ const runnerPath = path33.join(tmpRoot, "_run.bat");
36692
36926
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
36693
36927
  const com = process.env.ComSpec || "cmd.exe";
36694
36928
  try {
36695
- fs26.writeFileSync(
36929
+ fs30.writeFileSync(
36696
36930
  runnerPath,
36697
36931
  `@ECHO OFF\r
36698
36932
  CD /D ${q(cwd)}\r
@@ -37629,30 +37863,30 @@ function createOnBridgeIdentified(opts) {
37629
37863
  }
37630
37864
 
37631
37865
  // src/skills/discover-local-agent-skills.ts
37632
- import fs27 from "node:fs";
37633
- import path30 from "node:path";
37866
+ import fs31 from "node:fs";
37867
+ import path34 from "node:path";
37634
37868
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
37635
37869
  function discoverLocalSkills(cwd) {
37636
37870
  const out = [];
37637
37871
  const seenKeys = /* @__PURE__ */ new Set();
37638
37872
  for (const rel of SKILL_DISCOVERY_ROOTS) {
37639
- const base = path30.join(cwd, rel);
37640
- if (!fs27.existsSync(base) || !fs27.statSync(base).isDirectory()) continue;
37873
+ const base = path34.join(cwd, rel);
37874
+ if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
37641
37875
  let entries = [];
37642
37876
  try {
37643
- entries = fs27.readdirSync(base);
37877
+ entries = fs31.readdirSync(base);
37644
37878
  } catch {
37645
37879
  continue;
37646
37880
  }
37647
37881
  for (const name of entries) {
37648
- const dir = path30.join(base, name);
37882
+ const dir = path34.join(base, name);
37649
37883
  try {
37650
- if (!fs27.statSync(dir).isDirectory()) continue;
37884
+ if (!fs31.statSync(dir).isDirectory()) continue;
37651
37885
  } catch {
37652
37886
  continue;
37653
37887
  }
37654
- const skillMd = path30.join(dir, "SKILL.md");
37655
- if (!fs27.existsSync(skillMd)) continue;
37888
+ const skillMd = path34.join(dir, "SKILL.md");
37889
+ if (!fs31.existsSync(skillMd)) continue;
37656
37890
  const key = `${rel}/${name}`;
37657
37891
  if (seenKeys.has(key)) continue;
37658
37892
  seenKeys.add(key);
@@ -37664,23 +37898,23 @@ function discoverLocalSkills(cwd) {
37664
37898
  function discoverSkillLayoutRoots(cwd) {
37665
37899
  const roots = [];
37666
37900
  for (const rel of SKILL_DISCOVERY_ROOTS) {
37667
- const base = path30.join(cwd, rel);
37668
- if (!fs27.existsSync(base) || !fs27.statSync(base).isDirectory()) continue;
37901
+ const base = path34.join(cwd, rel);
37902
+ if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
37669
37903
  let entries = [];
37670
37904
  try {
37671
- entries = fs27.readdirSync(base);
37905
+ entries = fs31.readdirSync(base);
37672
37906
  } catch {
37673
37907
  continue;
37674
37908
  }
37675
37909
  const skills2 = [];
37676
37910
  for (const name of entries) {
37677
- const dir = path30.join(base, name);
37911
+ const dir = path34.join(base, name);
37678
37912
  try {
37679
- if (!fs27.statSync(dir).isDirectory()) continue;
37913
+ if (!fs31.statSync(dir).isDirectory()) continue;
37680
37914
  } catch {
37681
37915
  continue;
37682
37916
  }
37683
- if (!fs27.existsSync(path30.join(dir, "SKILL.md"))) continue;
37917
+ if (!fs31.existsSync(path34.join(dir, "SKILL.md"))) continue;
37684
37918
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
37685
37919
  skills2.push({ name, relPath });
37686
37920
  }
@@ -37782,6 +38016,7 @@ function reportGitRepos(getWs, log2) {
37782
38016
  var API_TO_BRIDGE_MESSAGE_TYPES = [
37783
38017
  "auth_token",
37784
38018
  "bridge_identified",
38019
+ "ha",
37785
38020
  "dev_servers_config",
37786
38021
  "server_control",
37787
38022
  "agent_config",
@@ -37810,6 +38045,10 @@ function parseApiToBridgeMessage(data, log2) {
37810
38045
  }
37811
38046
  return null;
37812
38047
  }
38048
+ if (t === "ha") {
38049
+ const s = data.s;
38050
+ if (typeof s !== "number" || !Number.isFinite(s)) return null;
38051
+ }
37813
38052
  return data;
37814
38053
  }
37815
38054
 
@@ -37850,6 +38089,13 @@ var handleBridgeIdentified = (msg, deps) => {
37850
38089
  });
37851
38090
  };
37852
38091
 
38092
+ // src/connection/heartbeat/ack.ts
38093
+ var handleBridgeHeartbeatAck = (msg, deps) => {
38094
+ const raw = msg.s;
38095
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return;
38096
+ deps.onBridgeHeartbeatAck?.(Math.trunc(raw));
38097
+ };
38098
+
37853
38099
  // src/agents/acp/from-bridge/handle-bridge-agent-config.ts
37854
38100
  function handleBridgeAgentConfig(msg, { acpManager }) {
37855
38101
  if (!Array.isArray(msg.agents) || msg.agents.length === 0) return;
@@ -37862,7 +38108,7 @@ var handleAgentConfigMessage = (msg, deps) => {
37862
38108
  };
37863
38109
 
37864
38110
  // src/prompt-turn-queue/runner.ts
37865
- import fs30 from "node:fs";
38111
+ import fs32 from "node:fs";
37866
38112
 
37867
38113
  // src/prompt-turn-queue/client-report.ts
37868
38114
  function sendPromptQueueClientReport(ws, queues) {
@@ -37871,32 +38117,6 @@ function sendPromptQueueClientReport(ws, queues) {
37871
38117
  return true;
37872
38118
  }
37873
38119
 
37874
- // src/prompt-turn-queue/disk-store.ts
37875
- import fs29 from "node:fs";
37876
-
37877
- // src/prompt-turn-queue/paths.ts
37878
- import crypto3 from "node:crypto";
37879
- import fs28 from "node:fs";
37880
- import path31 from "node:path";
37881
- import os7 from "node:os";
37882
- var QUEUE_KEY_HEX_64 = /^[a-f0-9]{64}$/i;
37883
- function queueStateFileSlug(queueKey) {
37884
- if (QUEUE_KEY_HEX_64.test(queueKey)) return queueKey.toLowerCase();
37885
- return crypto3.createHash("sha256").update(queueKey, "utf8").digest("hex");
37886
- }
37887
- function getPromptQueuesDirectory() {
37888
- const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
37889
- if (override) return path31.resolve(override);
37890
- return path31.join(os7.homedir(), ".buildautomaton", "queues");
37891
- }
37892
- function ensurePromptQueuesDirectory() {
37893
- const dir = getPromptQueuesDirectory();
37894
- if (!fs28.existsSync(dir)) fs28.mkdirSync(dir, { recursive: true });
37895
- }
37896
- function queueStateFilePath(queueKey) {
37897
- return path31.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
37898
- }
37899
-
37900
38120
  // src/prompt-turn-queue/disk-store.ts
37901
38121
  var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
37902
38122
  "queued",
@@ -37906,28 +38126,27 @@ var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
37906
38126
  "stopping",
37907
38127
  "discarded"
37908
38128
  ]);
37909
- function parsePersistedQueueFile(raw) {
37910
- try {
37911
- const o = JSON.parse(raw);
37912
- const queueKey = typeof o.queueKey === "string" ? o.queueKey : typeof o.queueKeyHash === "string" ? o.queueKeyHash : null;
37913
- if (!queueKey || typeof o.updatedAt !== "string" || !Array.isArray(o.turns)) return null;
37914
- return { queueKey, updatedAt: o.updatedAt, turns: o.turns };
37915
- } catch {
37916
- return null;
37917
- }
37918
- }
37919
38129
  function readPersistedQueue(queueKey) {
37920
- const p = queueStateFilePath(queueKey);
38130
+ const db = getCliDatabase();
38131
+ const row = db.get("SELECT queue_key, updated_at, turns_json FROM prompt_queue WHERE queue_key = ?", [
38132
+ queueKey
38133
+ ]);
38134
+ if (!row) return null;
37921
38135
  try {
37922
- return parsePersistedQueueFile(fs29.readFileSync(p, "utf8"));
38136
+ const turns = JSON.parse(row.turns_json);
38137
+ if (!Array.isArray(turns)) return null;
38138
+ return { queueKey: row.queue_key, updatedAt: row.updated_at, turns };
37923
38139
  } catch {
37924
38140
  return null;
37925
38141
  }
37926
38142
  }
37927
38143
  function writePersistedQueue(file2) {
37928
- ensurePromptQueuesDirectory();
37929
- const p = queueStateFilePath(file2.queueKey);
37930
- fs29.writeFileSync(p, JSON.stringify(file2, null, 2), "utf8");
38144
+ const db = getCliDatabase();
38145
+ db.run(
38146
+ `INSERT INTO prompt_queue (queue_key, updated_at, turns_json) VALUES (?, ?, ?)
38147
+ ON CONFLICT(queue_key) DO UPDATE SET updated_at = excluded.updated_at, turns_json = excluded.turns_json`,
38148
+ [file2.queueKey, file2.updatedAt, JSON.stringify(file2.turns)]
38149
+ );
37931
38150
  }
37932
38151
  function mergeServerQueueSnapshot(queueKey, serverTurns) {
37933
38152
  const prev = readPersistedQueue(queueKey);
@@ -37985,7 +38204,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
37985
38204
  const tid = typeof pl.snapshotRevertTurnId === "string" && pl.snapshotRevertTurnId.trim() !== "" ? pl.snapshotRevertTurnId.trim() : next.turnId;
37986
38205
  const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
37987
38206
  const file2 = snapshotFilePath(agentBase, tid);
37988
- if (!fs30.existsSync(file2)) {
38207
+ if (!fs32.existsSync(file2)) {
37989
38208
  deps.log(
37990
38209
  `[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
37991
38210
  );
@@ -38209,9 +38428,9 @@ function parseChangeSummarySnapshots(raw) {
38209
38428
  for (const item of raw) {
38210
38429
  if (!item || typeof item !== "object") continue;
38211
38430
  const o = item;
38212
- const path37 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
38213
- if (!path37) continue;
38214
- const row = { path: path37 };
38431
+ const path41 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
38432
+ if (!path41) continue;
38433
+ const row = { path: path41 };
38215
38434
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
38216
38435
  if (typeof o.oldText === "string") row.oldText = o.oldText;
38217
38436
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -38428,8 +38647,8 @@ function randomSecret() {
38428
38647
  }
38429
38648
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
38430
38649
  }
38431
- async function requestPreviewApi(port, secret, method, path37, body) {
38432
- const url2 = `http://127.0.0.1:${port}${path37}`;
38650
+ async function requestPreviewApi(port, secret, method, path41, body) {
38651
+ const url2 = `http://127.0.0.1:${port}${path41}`;
38433
38652
  const headers = {
38434
38653
  [PREVIEW_SECRET_HEADER]: secret,
38435
38654
  "Content-Type": "application/json"
@@ -38441,7 +38660,7 @@ async function requestPreviewApi(port, secret, method, path37, body) {
38441
38660
  });
38442
38661
  const data = await res.json().catch(() => ({}));
38443
38662
  if (!res.ok) {
38444
- throw new Error(data?.error ?? `Preview API ${method} ${path37}: ${res.status}`);
38663
+ throw new Error(data?.error ?? `Preview API ${method} ${path41}: ${res.status}`);
38445
38664
  }
38446
38665
  return data;
38447
38666
  }
@@ -38606,15 +38825,15 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
38606
38825
  };
38607
38826
 
38608
38827
  // src/files/list-dir.ts
38609
- import fs31 from "node:fs";
38610
- import path33 from "node:path";
38828
+ import fs33 from "node:fs";
38829
+ import path36 from "node:path";
38611
38830
 
38612
38831
  // src/files/ensure-under-cwd.ts
38613
- import path32 from "node:path";
38832
+ import path35 from "node:path";
38614
38833
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
38615
- const normalized = path32.normalize(relativePath).replace(/^(\.\/)+/, "");
38616
- const resolved = path32.resolve(cwd, normalized);
38617
- if (!resolved.startsWith(cwd + path32.sep) && resolved !== cwd) {
38834
+ const normalized = path35.normalize(relativePath).replace(/^(\.\/)+/, "");
38835
+ const resolved = path35.resolve(cwd, normalized);
38836
+ if (!resolved.startsWith(cwd + path35.sep) && resolved !== cwd) {
38618
38837
  return null;
38619
38838
  }
38620
38839
  return resolved;
@@ -38628,7 +38847,7 @@ async function listDirAsync(relativePath) {
38628
38847
  return { error: "Path is outside working directory" };
38629
38848
  }
38630
38849
  try {
38631
- const names = await fs31.promises.readdir(resolved, { withFileTypes: true });
38850
+ const names = await fs33.promises.readdir(resolved, { withFileTypes: true });
38632
38851
  const visible = names.filter((d) => !d.name.startsWith("."));
38633
38852
  const entries = [];
38634
38853
  for (let i = 0; i < visible.length; i++) {
@@ -38636,12 +38855,12 @@ async function listDirAsync(relativePath) {
38636
38855
  await yieldToEventLoop();
38637
38856
  }
38638
38857
  const d = visible[i];
38639
- const entryPath = path33.join(relativePath || ".", d.name).replace(/\\/g, "/");
38640
- const fullPath = path33.join(resolved, d.name);
38858
+ const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
38859
+ const fullPath = path36.join(resolved, d.name);
38641
38860
  let isDir = d.isDirectory();
38642
38861
  if (d.isSymbolicLink()) {
38643
38862
  try {
38644
- const targetStat = await fs31.promises.stat(fullPath);
38863
+ const targetStat = await fs33.promises.stat(fullPath);
38645
38864
  isDir = targetStat.isDirectory();
38646
38865
  } catch {
38647
38866
  isDir = false;
@@ -38666,25 +38885,25 @@ async function listDirAsync(relativePath) {
38666
38885
  }
38667
38886
 
38668
38887
  // src/files/read-file.ts
38669
- import fs32 from "node:fs";
38888
+ import fs34 from "node:fs";
38670
38889
  import { StringDecoder } from "node:string_decoder";
38671
38890
  function resolveFilePath(relativePath) {
38672
38891
  const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
38673
38892
  if (!resolved) return { error: "Path is outside working directory" };
38674
38893
  let real;
38675
38894
  try {
38676
- real = fs32.realpathSync(resolved);
38895
+ real = fs34.realpathSync(resolved);
38677
38896
  } catch {
38678
38897
  real = resolved;
38679
38898
  }
38680
- const stat3 = fs32.statSync(real);
38899
+ const stat3 = fs34.statSync(real);
38681
38900
  if (!stat3.isFile()) return { error: "Not a file" };
38682
38901
  return real;
38683
38902
  }
38684
38903
  var LINE_CHUNK_SIZE = 64 * 1024;
38685
38904
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
38686
- const fileSize = fs32.statSync(filePath).size;
38687
- const fd = fs32.openSync(filePath, "r");
38905
+ const fileSize = fs34.statSync(filePath).size;
38906
+ const fd = fs34.openSync(filePath, "r");
38688
38907
  const bufSize = 64 * 1024;
38689
38908
  const buf = Buffer.alloc(bufSize);
38690
38909
  const decoder = new StringDecoder("utf8");
@@ -38697,7 +38916,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
38697
38916
  let line0Accum = "";
38698
38917
  try {
38699
38918
  let bytesRead;
38700
- while (!done && (bytesRead = fs32.readSync(fd, buf, 0, bufSize, null)) > 0) {
38919
+ while (!done && (bytesRead = fs34.readSync(fd, buf, 0, bufSize, null)) > 0) {
38701
38920
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
38702
38921
  partial2 = "";
38703
38922
  let lineStart = 0;
@@ -38832,7 +39051,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
38832
39051
  }
38833
39052
  return { content: resultLines.join("\n"), size: fileSize };
38834
39053
  } finally {
38835
- fs32.closeSync(fd);
39054
+ fs34.closeSync(fd);
38836
39055
  }
38837
39056
  }
38838
39057
  function readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
@@ -38843,8 +39062,8 @@ function readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize =
38843
39062
  if (hasRange) {
38844
39063
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
38845
39064
  }
38846
- const stat3 = fs32.statSync(result);
38847
- const raw = fs32.readFileSync(result, "utf8");
39065
+ const stat3 = fs34.statSync(result);
39066
+ const raw = fs34.readFileSync(result, "utf8");
38848
39067
  const lines = raw.split(/\r?\n/);
38849
39068
  return { content: raw, totalLines: lines.length, size: stat3.size };
38850
39069
  } catch (err) {
@@ -38857,14 +39076,14 @@ async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineC
38857
39076
  }
38858
39077
 
38859
39078
  // src/files/handle-file-browser-search.ts
39079
+ import path37 from "node:path";
38860
39080
  var SEARCH_LIMIT = 100;
38861
39081
  function handleFileBrowserSearch(msg, socket, e2ee) {
38862
39082
  void (async () => {
38863
39083
  await yieldToEventLoop();
38864
39084
  const q = typeof msg.q === "string" ? msg.q : "";
38865
- const cwd = getBridgeRoot();
38866
- const index = loadFileIndex(cwd);
38867
- if (index === null) {
39085
+ const cwd = path37.resolve(getBridgeRoot());
39086
+ if (!bridgeFileIndexIsPopulated(cwd)) {
38868
39087
  const payload2 = {
38869
39088
  type: "file_browser_search_response",
38870
39089
  id: msg.id,
@@ -38874,7 +39093,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
38874
39093
  sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
38875
39094
  return;
38876
39095
  }
38877
- const results = await searchFileIndexAsync(index, q, SEARCH_LIMIT);
39096
+ const results = await searchBridgeFilePathsAsync(cwd, q, SEARCH_LIMIT);
38878
39097
  const payload = {
38879
39098
  type: "file_browser_search_response",
38880
39099
  id: msg.id,
@@ -38962,8 +39181,8 @@ function handleSkillLayoutRequest(msg, deps) {
38962
39181
  }
38963
39182
 
38964
39183
  // src/skills/install-remote-skills.ts
38965
- import fs33 from "node:fs";
38966
- import path34 from "node:path";
39184
+ import fs35 from "node:fs";
39185
+ import path38 from "node:path";
38967
39186
  function installRemoteSkills(cwd, targetDir, items) {
38968
39187
  const installed2 = [];
38969
39188
  if (!Array.isArray(items)) {
@@ -38974,15 +39193,15 @@ function installRemoteSkills(cwd, targetDir, items) {
38974
39193
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
38975
39194
  continue;
38976
39195
  }
38977
- const skillDir = path34.join(cwd, targetDir, item.skillName);
39196
+ const skillDir = path38.join(cwd, targetDir, item.skillName);
38978
39197
  for (const f of item.files) {
38979
39198
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
38980
- const dest = path34.join(skillDir, f.path);
38981
- fs33.mkdirSync(path34.dirname(dest), { recursive: true });
39199
+ const dest = path38.join(skillDir, f.path);
39200
+ fs35.mkdirSync(path38.dirname(dest), { recursive: true });
38982
39201
  if (f.text !== void 0) {
38983
- fs33.writeFileSync(dest, f.text, "utf8");
39202
+ fs35.writeFileSync(dest, f.text, "utf8");
38984
39203
  } else if (f.base64) {
38985
- fs33.writeFileSync(dest, Buffer.from(f.base64, "base64"));
39204
+ fs35.writeFileSync(dest, Buffer.from(f.base64, "base64"));
38986
39205
  }
38987
39206
  }
38988
39207
  installed2.push({
@@ -39132,7 +39351,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
39132
39351
  };
39133
39352
 
39134
39353
  // src/routing/handlers/revert-turn-snapshot.ts
39135
- import * as fs34 from "node:fs";
39354
+ import * as fs36 from "node:fs";
39136
39355
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
39137
39356
  const id = typeof msg.id === "string" ? msg.id : "";
39138
39357
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -39144,7 +39363,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
39144
39363
  if (!s) return;
39145
39364
  const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
39146
39365
  const file2 = snapshotFilePath(agentBase, turnId);
39147
- if (!fs34.existsSync(file2)) {
39366
+ if (!fs36.existsSync(file2)) {
39148
39367
  sendWsMessage(s, {
39149
39368
  type: "revert_turn_snapshot_result",
39150
39369
  id,
@@ -39193,6 +39412,9 @@ function dispatchBridgeMessage(msg, deps) {
39193
39412
  case "bridge_identified":
39194
39413
  handleBridgeIdentified(msg, deps);
39195
39414
  break;
39415
+ case "ha":
39416
+ handleBridgeHeartbeatAck(msg, deps);
39417
+ break;
39196
39418
  case "dev_servers_config":
39197
39419
  handleDevServersConfig(msg, deps);
39198
39420
  break;
@@ -39248,9 +39470,17 @@ function dispatchBridgeMessage(msg, deps) {
39248
39470
  }
39249
39471
 
39250
39472
  // src/routing/handle-bridge-message.ts
39473
+ function normalizeInboundBridgeWebSocketJson(data) {
39474
+ if (data === null || typeof data !== "object" || Array.isArray(data)) return data;
39475
+ const o = data;
39476
+ if (o.t === "ha" && typeof o.s === "number" && Number.isFinite(o.s)) {
39477
+ return { type: "ha", s: Math.trunc(o.s) };
39478
+ }
39479
+ return data;
39480
+ }
39251
39481
  function handleBridgeMessage(data, deps) {
39252
39482
  if (!deps.getWs()) return;
39253
- const msg = parseApiToBridgeMessage(data, deps.log);
39483
+ const msg = parseApiToBridgeMessage(normalizeInboundBridgeWebSocketJson(data), deps.log);
39254
39484
  if (!msg) return;
39255
39485
  setImmediate(() => {
39256
39486
  dispatchBridgeMessage(msg, deps);
@@ -39298,7 +39528,8 @@ function createMainBridgeWebSocketLifecycle(params) {
39298
39528
  persistTokens,
39299
39529
  onAuthInvalid,
39300
39530
  e2ee,
39301
- identifyReportedPaths
39531
+ identifyReportedPaths,
39532
+ bridgeHeartbeat
39302
39533
  } = params;
39303
39534
  let authRefreshInFlight = false;
39304
39535
  function handleOpen() {
@@ -39330,6 +39561,7 @@ function createMainBridgeWebSocketLifecycle(params) {
39330
39561
  }
39331
39562
  }
39332
39563
  function handleClose(code, reason) {
39564
+ bridgeHeartbeat?.stop();
39333
39565
  try {
39334
39566
  const was = state.currentWs;
39335
39567
  state.currentWs = null;
@@ -39364,6 +39596,7 @@ function createMainBridgeWebSocketLifecycle(params) {
39364
39596
  } catch {
39365
39597
  }
39366
39598
  }
39599
+ bridgeHeartbeat?.stop();
39367
39600
  const prev = state.currentWs;
39368
39601
  if (prev) {
39369
39602
  prev.removeAllListeners();
@@ -39455,11 +39688,98 @@ function createMainBridgeWebSocketLifecycle(params) {
39455
39688
  return { connect };
39456
39689
  }
39457
39690
 
39691
+ // src/connection/heartbeat/controller.ts
39692
+ function meanRttMs(samples) {
39693
+ if (samples.length === 0) return void 0;
39694
+ let sum = 0;
39695
+ for (const x of samples) sum += x;
39696
+ return sum / samples.length;
39697
+ }
39698
+ function createBridgeHeartbeatController(params) {
39699
+ const { getWs, log: log2 } = params;
39700
+ let interval = null;
39701
+ let seqCursor = -1;
39702
+ let awaitingSeq = null;
39703
+ let sentAtMs = 0;
39704
+ let missed = 0;
39705
+ const rttSamples = [];
39706
+ function clearTimer() {
39707
+ if (interval != null) {
39708
+ clearInterval(interval);
39709
+ interval = null;
39710
+ }
39711
+ }
39712
+ function nextSeq() {
39713
+ seqCursor = seqCursor >= BRIDGE_HEARTBEAT_SEQ_MAX ? 0 : seqCursor + 1;
39714
+ return seqCursor;
39715
+ }
39716
+ function tick() {
39717
+ const ws = getWs();
39718
+ if (!ws || ws.readyState !== wrapper_default.OPEN) return;
39719
+ if (awaitingSeq !== null) {
39720
+ missed++;
39721
+ if (missed >= BRIDGE_HEARTBEAT_MISSED_ACKS_BEFORE_RECONNECT) {
39722
+ try {
39723
+ log2("[Bridge service] Heartbeat missed repeatedly; reconnecting\u2026");
39724
+ } catch {
39725
+ }
39726
+ clearTimer();
39727
+ awaitingSeq = null;
39728
+ missed = 0;
39729
+ rttSamples.length = 0;
39730
+ safeCloseWebSocket(ws);
39731
+ return;
39732
+ }
39733
+ }
39734
+ const seq = nextSeq();
39735
+ const mean = meanRttMs(rttSamples);
39736
+ const payload = mean !== void 0 && Number.isFinite(mean) ? { t: "h", s: seq, m: Math.round(mean) } : { t: "h", s: seq };
39737
+ sendWsMessage(ws, payload);
39738
+ awaitingSeq = seq;
39739
+ sentAtMs = Date.now();
39740
+ }
39741
+ return {
39742
+ start() {
39743
+ clearTimer();
39744
+ awaitingSeq = null;
39745
+ missed = 0;
39746
+ seqCursor = -1;
39747
+ rttSamples.length = 0;
39748
+ interval = setInterval(tick, BRIDGE_APP_HEARTBEAT_INTERVAL_MS);
39749
+ },
39750
+ stop() {
39751
+ clearTimer();
39752
+ awaitingSeq = null;
39753
+ missed = 0;
39754
+ rttSamples.length = 0;
39755
+ },
39756
+ onAck(seq) {
39757
+ if (awaitingSeq === null) return;
39758
+ if (!Number.isFinite(seq)) return;
39759
+ const ack = Math.trunc(seq);
39760
+ const pending = awaitingSeq;
39761
+ if (ack < pending) return;
39762
+ if (ack === pending) {
39763
+ const rtt = Date.now() - sentAtMs;
39764
+ if (Number.isFinite(rtt) && rtt >= 0 && rtt < 6e5) {
39765
+ rttSamples.push(rtt);
39766
+ if (rttSamples.length > BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX) {
39767
+ rttSamples.splice(0, rttSamples.length - BRIDGE_HEARTBEAT_RTT_SAMPLE_MAX);
39768
+ }
39769
+ }
39770
+ }
39771
+ awaitingSeq = null;
39772
+ missed = 0;
39773
+ }
39774
+ };
39775
+ }
39776
+
39458
39777
  // src/connection/create-bridge-connection.ts
39459
39778
  async function createBridgeConnection(options) {
39460
39779
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
39461
39780
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
39462
39781
  const logFn = options.log ?? log;
39782
+ getCliDatabase({ logLegacyMigration: logFn });
39463
39783
  const tokens = {
39464
39784
  accessToken: options.authToken,
39465
39785
  refreshToken: options.refreshToken
@@ -39494,13 +39814,18 @@ async function createBridgeConnection(options) {
39494
39814
  }
39495
39815
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
39496
39816
  const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
39497
- const onBridgeIdentified = createOnBridgeIdentified({
39817
+ const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
39818
+ const baseOnBridgeIdentified = createOnBridgeIdentified({
39498
39819
  devServerManager,
39499
39820
  firehoseServerUrl,
39500
39821
  workspaceId,
39501
39822
  state,
39502
39823
  logFn
39503
39824
  });
39825
+ const onBridgeIdentified = (msg) => {
39826
+ baseOnBridgeIdentified(msg);
39827
+ bridgeHeartbeat.start();
39828
+ };
39504
39829
  const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
39505
39830
  const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
39506
39831
  const messageDeps = {
@@ -39509,6 +39834,9 @@ async function createBridgeConnection(options) {
39509
39834
  acpManager,
39510
39835
  sessionWorktreeManager,
39511
39836
  onBridgeIdentified,
39837
+ onBridgeHeartbeatAck: (seq) => {
39838
+ bridgeHeartbeat.onAck(seq);
39839
+ },
39512
39840
  sendLocalSkillsReport,
39513
39841
  reportAutoDetectedAgents,
39514
39842
  devServerManager,
@@ -39517,8 +39845,8 @@ async function createBridgeConnection(options) {
39517
39845
  getCloudAccessToken: () => tokens.accessToken
39518
39846
  };
39519
39847
  const identifyReportedPaths = {
39520
- bridgeRootPath: path35.resolve(getBridgeRoot()),
39521
- worktreesRootPath: path35.resolve(worktreesRootPath)
39848
+ bridgeRootPath: path39.resolve(getBridgeRoot()),
39849
+ worktreesRootPath: path39.resolve(worktreesRootPath)
39522
39850
  };
39523
39851
  const { connect } = createMainBridgeWebSocketLifecycle({
39524
39852
  state,
@@ -39532,13 +39860,15 @@ async function createBridgeConnection(options) {
39532
39860
  persistTokens,
39533
39861
  onAuthInvalid,
39534
39862
  e2ee,
39535
- identifyReportedPaths
39863
+ identifyReportedPaths,
39864
+ bridgeHeartbeat
39536
39865
  });
39537
39866
  connect();
39538
39867
  const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
39539
39868
  return {
39540
39869
  close: async () => {
39541
39870
  stopFileIndexWatcher();
39871
+ bridgeHeartbeat.stop();
39542
39872
  await closeBridgeConnection(state, acpManager, devServerManager, logFn);
39543
39873
  }
39544
39874
  };
@@ -39754,9 +40084,9 @@ async function runCliAction(program2, opts) {
39754
40084
  const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
39755
40085
  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);
39756
40086
  if (bridgeRootOpt) {
39757
- const resolvedBridgeRoot = path36.resolve(process.cwd(), bridgeRootOpt);
40087
+ const resolvedBridgeRoot = path40.resolve(process.cwd(), bridgeRootOpt);
39758
40088
  try {
39759
- const st = fs35.statSync(resolvedBridgeRoot);
40089
+ const st = fs37.statSync(resolvedBridgeRoot);
39760
40090
  if (!st.isDirectory()) {
39761
40091
  console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
39762
40092
  process.exit(1);
@@ -39776,7 +40106,7 @@ async function runCliAction(program2, opts) {
39776
40106
  );
39777
40107
  let worktreesRootPath;
39778
40108
  if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
39779
- worktreesRootPath = path36.resolve(opts.worktreesRoot.trim());
40109
+ worktreesRootPath = path40.resolve(opts.worktreesRoot.trim());
39780
40110
  }
39781
40111
  const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
39782
40112
  if (e2eCertificates) {