@buildautomaton/cli 0.1.28 → 0.1.30

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 path43 = __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 = path43.resolve(baseDir, baseName);
1910
+ if (fs38.existsSync(localBin)) return localBin;
1911
+ if (sourceExt.includes(path43.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 = path43.resolve(
1930
+ path43.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 = path43.basename(
1938
1938
  this._scriptPath,
1939
- path37.extname(this._scriptPath)
1939
+ path43.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(path43.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 = path43.basename(filename, path43.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(path44) {
2805
+ if (path44 === void 0) return this._executableDir;
2806
+ this._executableDir = path44;
2807
2807
  return this;
2808
2808
  }
2809
2809
  /**
@@ -5236,7 +5236,7 @@ var require_websocket = __commonJS({
5236
5236
  var http = __require("http");
5237
5237
  var net = __require("net");
5238
5238
  var tls = __require("tls");
5239
- var { randomBytes: randomBytes3, createHash: createHash2 } = __require("crypto");
5239
+ var { randomBytes: randomBytes3, createHash: createHash3 } = __require("crypto");
5240
5240
  var { Duplex, Readable: Readable2 } = __require("stream");
5241
5241
  var { URL: URL2 } = __require("url");
5242
5242
  var PerMessageDeflate = require_permessage_deflate();
@@ -5896,7 +5896,7 @@ var require_websocket = __commonJS({
5896
5896
  abortHandshake(websocket, socket, "Invalid Upgrade header");
5897
5897
  return;
5898
5898
  }
5899
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
5899
+ const digest = createHash3("sha1").update(key + GUID).digest("base64");
5900
5900
  if (res.headers["sec-websocket-accept"] !== digest) {
5901
5901
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
5902
5902
  return;
@@ -6263,7 +6263,7 @@ var require_websocket_server = __commonJS({
6263
6263
  var EventEmitter2 = __require("events");
6264
6264
  var http = __require("http");
6265
6265
  var { Duplex } = __require("stream");
6266
- var { createHash: createHash2 } = __require("crypto");
6266
+ var { createHash: createHash3 } = __require("crypto");
6267
6267
  var extension = require_extension();
6268
6268
  var PerMessageDeflate = require_permessage_deflate();
6269
6269
  var subprotocol = require_subprotocol();
@@ -6564,7 +6564,7 @@ var require_websocket_server = __commonJS({
6564
6564
  );
6565
6565
  }
6566
6566
  if (this._state > RUNNING) return abortHandshake(socket, 503);
6567
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
6567
+ const digest = createHash3("sha1").update(key + GUID).digest("base64");
6568
6568
  const headers = [
6569
6569
  "HTTP/1.1 101 Switching Protocols",
6570
6570
  "Upgrade: websocket",
@@ -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: path43, errorMaps, issueData } = params;
7065
+ const fullPath = [...path43, ...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, path43, key) {
7374
7374
  this._cachedPath = [];
7375
7375
  this.parent = parent;
7376
7376
  this.data = value;
7377
- this._path = path37;
7377
+ this._path = path43;
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(path43, isFile, isDirectory) {
11533
+ log2(`checking %s`, path43);
11534
11534
  try {
11535
- const stat3 = fs_1.statSync(path37);
11535
+ const stat3 = fs_1.statSync(path43);
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(path43, type = exports.READABLE) {
11556
+ return check2(path43, (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, path43) {
11854
+ if (!path43)
11855
11855
  return obj;
11856
- return path37.reduce((acc, key) => acc?.[key], obj);
11856
+ return path43.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(path43, 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(path43);
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, path43 = []) => {
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 = [...path43, ...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(path43) {
12339
12339
  const segs = [];
12340
- for (const seg of path37) {
12340
+ for (const seg of path43) {
12341
12341
  if (typeof seg === "number")
12342
12342
  segs.push(`[${seg}]`);
12343
12343
  else if (typeof seg === "symbol")
@@ -24800,8 +24800,8 @@ var init_acp = __esm({
24800
24800
  this.#requestHandler = requestHandler;
24801
24801
  this.#notificationHandler = notificationHandler;
24802
24802
  this.#stream = stream;
24803
- this.#closedPromise = new Promise((resolve18) => {
24804
- this.#abortController.signal.addEventListener("abort", () => resolve18());
24803
+ this.#closedPromise = new Promise((resolve20) => {
24804
+ this.#abortController.signal.addEventListener("abort", () => resolve20());
24805
24805
  });
24806
24806
  this.#receive();
24807
24807
  }
@@ -24950,8 +24950,8 @@ var init_acp = __esm({
24950
24950
  }
24951
24951
  async sendRequest(method, params) {
24952
24952
  const id = this.#nextRequestId++;
24953
- const responsePromise = new Promise((resolve18, reject) => {
24954
- this.#pendingResponses.set(id, { resolve: resolve18, reject });
24953
+ const responsePromise = new Promise((resolve20, reject) => {
24954
+ this.#pendingResponses.set(id, { resolve: resolve20, reject });
24955
24955
  });
24956
24956
  await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
24957
24957
  return responsePromise;
@@ -25064,15 +25064,15 @@ var {
25064
25064
  } = import_index.default;
25065
25065
 
25066
25066
  // src/cli-version.ts
25067
- var CLI_VERSION = "0.1.28".length > 0 ? "0.1.28" : "0.0.0-dev";
25067
+ var CLI_VERSION = "0.1.30".length > 0 ? "0.1.30" : "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 path42 from "node:path";
25076
25076
 
25077
25077
  // src/cli-log-level.ts
25078
25078
  var verbosity = "info";
@@ -26077,14 +26077,14 @@ var baseOpen = async (options) => {
26077
26077
  }
26078
26078
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
26079
26079
  if (options.wait) {
26080
- return new Promise((resolve18, reject) => {
26080
+ return new Promise((resolve20, reject) => {
26081
26081
  subprocess.once("error", reject);
26082
26082
  subprocess.once("close", (exitCode) => {
26083
26083
  if (!options.allowNonzeroExitCode && exitCode > 0) {
26084
26084
  reject(new Error(`Exited with code ${exitCode}`));
26085
26085
  return;
26086
26086
  }
26087
- resolve18(subprocess);
26087
+ resolve20(subprocess);
26088
26088
  });
26089
26089
  });
26090
26090
  }
@@ -26573,8 +26573,8 @@ function runPendingAuth(options) {
26573
26573
  let hasOpenedBrowser = false;
26574
26574
  let resolved = false;
26575
26575
  let resolveAuth;
26576
- const authPromise = new Promise((resolve18) => {
26577
- resolveAuth = resolve18;
26576
+ const authPromise = new Promise((resolve20) => {
26577
+ resolveAuth = resolve20;
26578
26578
  });
26579
26579
  let reconnectAttempt = 0;
26580
26580
  const signInQuiet = createEmptyReconnectQuietSlot();
@@ -26696,11 +26696,353 @@ function runPendingAuth(options) {
26696
26696
  };
26697
26697
  }
26698
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 AGENT_CAPABILITIES_SQL = readCliSqliteMigrationSql("002_agent_capabilities.sql");
26933
+ function agentCapabilitiesTableState(db) {
26934
+ const rows = db.all(
26935
+ `SELECT name FROM sqlite_master WHERE type='table' AND name IN ('agent_capabilities', 'agent_capability_cache')`
26936
+ );
26937
+ const names = new Set(rows.map((r) => r.name));
26938
+ if (names.has("agent_capabilities")) return "current";
26939
+ if (names.has("agent_capability_cache")) return "legacy";
26940
+ return "new";
26941
+ }
26942
+ var CLI_SQLITE_MIGRATIONS = [
26943
+ {
26944
+ name: CHECKPOINT_V1,
26945
+ checkpoint: true,
26946
+ migrate: (db) => {
26947
+ db.exec(CHECKPOINT_V1_SQL);
26948
+ }
26949
+ },
26950
+ {
26951
+ name: "002_agent_capabilities",
26952
+ migrate: (db) => {
26953
+ const state = agentCapabilitiesTableState(db);
26954
+ if (state === "current") return;
26955
+ if (state === "legacy") {
26956
+ db.exec(`
26957
+ ALTER TABLE agent_capability_cache RENAME TO agent_capabilities;
26958
+ DROP INDEX IF EXISTS idx_agent_capability_cache_workspace;
26959
+ CREATE INDEX IF NOT EXISTS idx_agent_capabilities_workspace ON agent_capabilities(workspace_id);
26960
+ `);
26961
+ return;
26962
+ }
26963
+ db.exec(AGENT_CAPABILITIES_SQL);
26964
+ },
26965
+ alreadyApplied: (db) => agentCapabilitiesTableState(db) === "current"
26966
+ }
26967
+ ];
26968
+ function migrateCliSqlite(db) {
26969
+ db.exec(readCliSqliteMigrationSql("000_bootstrap_migrations_table.sql"));
26970
+ const appliedRows = db.all("SELECT name FROM __migrations");
26971
+ const applied2 = new Set(appliedRows.map((r) => r.name));
26972
+ for (const migration of CLI_SQLITE_MIGRATIONS) {
26973
+ if (applied2.has(migration.name)) continue;
26974
+ if (migration.checkpoint === true && checkpointSatisfiedByLegacyChain(applied2, migration.replacesLegacyMigrations)) {
26975
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
26976
+ continue;
26977
+ }
26978
+ if (migration.alreadyApplied?.(db)) {
26979
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
26980
+ continue;
26981
+ }
26982
+ try {
26983
+ migration.migrate(db);
26984
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
26985
+ } catch (e) {
26986
+ console.error(`[cli-sqlite] Migration failed: ${migration.name}`, e);
26987
+ throw e;
26988
+ }
26989
+ }
26990
+ }
26991
+
26992
+ // src/sqlite/cli-database.ts
26993
+ var { Database: SqliteDatabase } = sqliteWasm;
26994
+ var openDatabases = /* @__PURE__ */ new Map();
26995
+ var processExitCloseRegistered = false;
26996
+ function registerProcessExitSqliteClose() {
26997
+ if (processExitCloseRegistered) return;
26998
+ processExitCloseRegistered = true;
26999
+ process.once("exit", () => {
27000
+ for (const db of openDatabases.values()) {
27001
+ safeCloseCliSqliteDatabase(db);
27002
+ }
27003
+ openDatabases.clear();
27004
+ });
27005
+ }
27006
+ function safeCloseCliSqliteDatabase(db) {
27007
+ if (db == null) return;
27008
+ try {
27009
+ if (db.isOpen) db.close();
27010
+ } catch {
27011
+ }
27012
+ }
27013
+ function closeAllCliSqliteConnections() {
27014
+ for (const db of openDatabases.values()) {
27015
+ safeCloseCliSqliteDatabase(db);
27016
+ }
27017
+ openDatabases.clear();
27018
+ }
27019
+ function getCliDatabase(options) {
27020
+ const sqlitePath = getCliSqlitePath();
27021
+ const existing = openDatabases.get(sqlitePath);
27022
+ if (existing?.isOpen) return existing;
27023
+ if (existing && !existing.isOpen) {
27024
+ safeCloseCliSqliteDatabase(existing);
27025
+ openDatabases.delete(sqlitePath);
27026
+ }
27027
+ ensureCliSqliteParentDir(sqlitePath);
27028
+ const db = new SqliteDatabase(sqlitePath);
27029
+ try {
27030
+ migrateCliSqlite(db);
27031
+ importCliSqliteLegacyDiskData(db, options?.logLegacyMigration);
27032
+ } catch (e) {
27033
+ safeCloseCliSqliteDatabase(db);
27034
+ throw e;
27035
+ }
27036
+ openDatabases.set(sqlitePath, db);
27037
+ registerProcessExitSqliteClose();
27038
+ return db;
27039
+ }
27040
+
26699
27041
  // src/connection/close-bridge-connection.ts
26700
27042
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
26701
27043
  const say = log2 ?? logImmediate;
26702
27044
  say("Cleaning up connections\u2026");
26703
- await new Promise((resolve18) => setImmediate(resolve18));
27045
+ await new Promise((resolve20) => setImmediate(resolve20));
26704
27046
  state.closedByUser = true;
26705
27047
  clearReconnectQuietTimer(state.mainQuiet);
26706
27048
  clearReconnectQuietTimer(state.firehoseQuiet);
@@ -26739,20 +27081,24 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
26739
27081
  say("Stopping local dev server processes\u2026");
26740
27082
  await devServerManager.shutdownAllGraceful();
26741
27083
  }
27084
+ try {
27085
+ closeAllCliSqliteConnections();
27086
+ } catch {
27087
+ }
26742
27088
  say("Shutdown complete.");
26743
27089
  }
26744
27090
 
26745
27091
  // src/paths/session-layout-paths.ts
26746
- import * as path5 from "node:path";
27092
+ import * as path12 from "node:path";
26747
27093
  function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
26748
- const resolved = worktreePaths.map((p) => path5.resolve(p)).filter(Boolean);
27094
+ const resolved = worktreePaths.map((p) => path12.resolve(p)).filter(Boolean);
26749
27095
  if (resolved.length === 0) return null;
26750
27096
  resolved.sort();
26751
27097
  return resolved[0];
26752
27098
  }
26753
27099
  function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
26754
27100
  if (resolvedSessionParentPath != null && String(resolvedSessionParentPath).trim() !== "") {
26755
- return path5.resolve(String(resolvedSessionParentPath).trim());
27101
+ return path12.resolve(String(resolvedSessionParentPath).trim());
26756
27102
  }
26757
27103
  return getBridgeRoot();
26758
27104
  }
@@ -27084,9 +27430,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
27084
27430
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
27085
27431
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
27086
27432
  if (!rawPath || !summary) continue;
27087
- const path37 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
27088
- if (!path37) continue;
27089
- rows.push({ path: path37, summary: clampSummaryToAtMostTwoLines(summary) });
27433
+ const path43 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
27434
+ if (!path43) continue;
27435
+ rows.push({ path: path43, summary: clampSummaryToAtMostTwoLines(summary) });
27090
27436
  }
27091
27437
  return rows;
27092
27438
  }
@@ -27296,6 +27642,7 @@ function buildCliAutoApprovedPermissionRpcResult(requestParams) {
27296
27642
  // ../types/src/agent-config.ts
27297
27643
  var AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY = "claude_permission_mode";
27298
27644
  var AGENT_CONFIG_CLI_PERMISSION_MODE_KEY = "cli_permission_mode";
27645
+ var AGENT_CONFIG_AGENT_MODEL_KEY = "agent_model";
27299
27646
  function getClaudePermissionModeFromAgentConfig(config2) {
27300
27647
  if (!config2) return null;
27301
27648
  const raw = config2[AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY];
@@ -27307,22 +27654,29 @@ function getCliPermissionModeFromAgentConfig(config2) {
27307
27654
  if (!config2) return CLI_PERMISSION_MODE_DEFAULT;
27308
27655
  return normalizeCliPermissionModeInput(config2[AGENT_CONFIG_CLI_PERMISSION_MODE_KEY]);
27309
27656
  }
27657
+ function getAgentModelFromAgentConfig(config2) {
27658
+ if (!config2) return null;
27659
+ const cur = config2[AGENT_CONFIG_AGENT_MODEL_KEY];
27660
+ if (typeof cur !== "string") return null;
27661
+ const t = cur.trim();
27662
+ return t !== "" ? t : null;
27663
+ }
27310
27664
 
27311
27665
  // src/git/session-git-queue.ts
27312
27666
  import { execFile as execFile7 } from "node:child_process";
27313
27667
  import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
27314
27668
  import { promisify as promisify7 } from "node:util";
27315
- import * as path8 from "node:path";
27669
+ import * as path15 from "node:path";
27316
27670
 
27317
27671
  // src/git/pre-turn-snapshot.ts
27318
- import * as fs9 from "node:fs";
27319
- import * as path7 from "node:path";
27672
+ import * as fs15 from "node:fs";
27673
+ import * as path14 from "node:path";
27320
27674
  import { execFile as execFile6 } from "node:child_process";
27321
27675
  import { promisify as promisify6 } from "node:util";
27322
27676
 
27323
27677
  // src/git/discover-repos.ts
27324
- import * as fs8 from "node:fs";
27325
- import * as path6 from "node:path";
27678
+ import * as fs14 from "node:fs";
27679
+ import * as path13 from "node:path";
27326
27680
 
27327
27681
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
27328
27682
  var import_file_exists = __toESM(require_dist(), 1);
@@ -27361,8 +27715,8 @@ function pathspec(...paths) {
27361
27715
  cache.set(key, paths);
27362
27716
  return key;
27363
27717
  }
27364
- function isPathSpec(path37) {
27365
- return path37 instanceof String && cache.has(path37);
27718
+ function isPathSpec(path43) {
27719
+ return path43 instanceof String && cache.has(path43);
27366
27720
  }
27367
27721
  function toPaths(pathSpec) {
27368
27722
  return cache.get(pathSpec) || [];
@@ -27451,8 +27805,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
27451
27805
  function forEachLineWithContent(input, callback) {
27452
27806
  return toLinesWithContent(input, true).map((line) => callback(line));
27453
27807
  }
27454
- function folderExists(path37) {
27455
- return (0, import_file_exists.exists)(path37, import_file_exists.FOLDER);
27808
+ function folderExists(path43) {
27809
+ return (0, import_file_exists.exists)(path43, import_file_exists.FOLDER);
27456
27810
  }
27457
27811
  function append(target, item) {
27458
27812
  if (Array.isArray(target)) {
@@ -27856,8 +28210,8 @@ function checkIsRepoRootTask() {
27856
28210
  commands,
27857
28211
  format: "utf-8",
27858
28212
  onError,
27859
- parser(path37) {
27860
- return /^\.(git)?$/.test(path37.trim());
28213
+ parser(path43) {
28214
+ return /^\.(git)?$/.test(path43.trim());
27861
28215
  }
27862
28216
  };
27863
28217
  }
@@ -28291,11 +28645,11 @@ function parseGrep(grep) {
28291
28645
  const paths = /* @__PURE__ */ new Set();
28292
28646
  const results = {};
28293
28647
  forEachLineWithContent(grep, (input) => {
28294
- const [path37, line, preview] = input.split(NULL);
28295
- paths.add(path37);
28296
- (results[path37] = results[path37] || []).push({
28648
+ const [path43, line, preview] = input.split(NULL);
28649
+ paths.add(path43);
28650
+ (results[path43] = results[path43] || []).push({
28297
28651
  line: asNumber(line),
28298
- path: path37,
28652
+ path: path43,
28299
28653
  preview
28300
28654
  });
28301
28655
  });
@@ -29060,14 +29414,14 @@ var init_hash_object = __esm2({
29060
29414
  init_task();
29061
29415
  }
29062
29416
  });
29063
- function parseInit(bare, path37, text) {
29417
+ function parseInit(bare, path43, text) {
29064
29418
  const response = String(text).trim();
29065
29419
  let result;
29066
29420
  if (result = initResponseRegex.exec(response)) {
29067
- return new InitSummary(bare, path37, false, result[1]);
29421
+ return new InitSummary(bare, path43, false, result[1]);
29068
29422
  }
29069
29423
  if (result = reInitResponseRegex.exec(response)) {
29070
- return new InitSummary(bare, path37, true, result[1]);
29424
+ return new InitSummary(bare, path43, true, result[1]);
29071
29425
  }
29072
29426
  let gitDir = "";
29073
29427
  const tokens = response.split(" ");
@@ -29078,7 +29432,7 @@ function parseInit(bare, path37, text) {
29078
29432
  break;
29079
29433
  }
29080
29434
  }
29081
- return new InitSummary(bare, path37, /^re/i.test(response), gitDir);
29435
+ return new InitSummary(bare, path43, /^re/i.test(response), gitDir);
29082
29436
  }
29083
29437
  var InitSummary;
29084
29438
  var initResponseRegex;
@@ -29087,9 +29441,9 @@ var init_InitSummary = __esm2({
29087
29441
  "src/lib/responses/InitSummary.ts"() {
29088
29442
  "use strict";
29089
29443
  InitSummary = class {
29090
- constructor(bare, path37, existing, gitDir) {
29444
+ constructor(bare, path43, existing, gitDir) {
29091
29445
  this.bare = bare;
29092
- this.path = path37;
29446
+ this.path = path43;
29093
29447
  this.existing = existing;
29094
29448
  this.gitDir = gitDir;
29095
29449
  }
@@ -29101,7 +29455,7 @@ var init_InitSummary = __esm2({
29101
29455
  function hasBareCommand(command) {
29102
29456
  return command.includes(bareCommand);
29103
29457
  }
29104
- function initTask(bare = false, path37, customArgs) {
29458
+ function initTask(bare = false, path43, customArgs) {
29105
29459
  const commands = ["init", ...customArgs];
29106
29460
  if (bare && !hasBareCommand(commands)) {
29107
29461
  commands.splice(1, 0, bareCommand);
@@ -29110,7 +29464,7 @@ function initTask(bare = false, path37, customArgs) {
29110
29464
  commands,
29111
29465
  format: "utf-8",
29112
29466
  parser(text) {
29113
- return parseInit(commands.includes("--bare"), path37, text);
29467
+ return parseInit(commands.includes("--bare"), path43, text);
29114
29468
  }
29115
29469
  };
29116
29470
  }
@@ -29926,12 +30280,12 @@ var init_FileStatusSummary = __esm2({
29926
30280
  "use strict";
29927
30281
  fromPathRegex = /^(.+)\0(.+)$/;
29928
30282
  FileStatusSummary = class {
29929
- constructor(path37, index, working_dir) {
29930
- this.path = path37;
30283
+ constructor(path43, index, working_dir) {
30284
+ this.path = path43;
29931
30285
  this.index = index;
29932
30286
  this.working_dir = working_dir;
29933
30287
  if (index === "R" || working_dir === "R") {
29934
- const detail = fromPathRegex.exec(path37) || [null, path37, path37];
30288
+ const detail = fromPathRegex.exec(path43) || [null, path43, path43];
29935
30289
  this.from = detail[2] || "";
29936
30290
  this.path = detail[1] || "";
29937
30291
  }
@@ -29962,14 +30316,14 @@ function splitLine(result, lineStr) {
29962
30316
  default:
29963
30317
  return;
29964
30318
  }
29965
- function data(index, workingDir, path37) {
30319
+ function data(index, workingDir, path43) {
29966
30320
  const raw = `${index}${workingDir}`;
29967
30321
  const handler = parsers6.get(raw);
29968
30322
  if (handler) {
29969
- handler(result, path37);
30323
+ handler(result, path43);
29970
30324
  }
29971
30325
  if (raw !== "##" && raw !== "!!") {
29972
- result.files.push(new FileStatusSummary(path37, index, workingDir));
30326
+ result.files.push(new FileStatusSummary(path43, index, workingDir));
29973
30327
  }
29974
30328
  }
29975
30329
  }
@@ -30242,15 +30596,15 @@ var init_simple_git_api = __esm2({
30242
30596
  this._executor = _executor;
30243
30597
  }
30244
30598
  _runTask(task, then) {
30245
- const chain = this._executor.chain();
30246
- const promise2 = chain.push(task);
30599
+ const chain2 = this._executor.chain();
30600
+ const promise2 = chain2.push(task);
30247
30601
  if (then) {
30248
30602
  taskCallback(task, promise2, then);
30249
30603
  }
30250
30604
  return Object.create(this, {
30251
30605
  then: { value: promise2.then.bind(promise2) },
30252
30606
  catch: { value: promise2.catch.bind(promise2) },
30253
- _executor: { value: chain }
30607
+ _executor: { value: chain2 }
30254
30608
  });
30255
30609
  }
30256
30610
  add(files) {
@@ -30278,9 +30632,9 @@ var init_simple_git_api = __esm2({
30278
30632
  next
30279
30633
  );
30280
30634
  }
30281
- hashObject(path37, write) {
30635
+ hashObject(path43, write) {
30282
30636
  return this._runTask(
30283
- hashObjectTask(path37, write === true),
30637
+ hashObjectTask(path43, write === true),
30284
30638
  trailingFunctionArgument(arguments)
30285
30639
  );
30286
30640
  }
@@ -30633,8 +30987,8 @@ var init_branch = __esm2({
30633
30987
  }
30634
30988
  });
30635
30989
  function toPath(input) {
30636
- const path37 = input.trim().replace(/^["']|["']$/g, "");
30637
- return path37 && normalize(path37);
30990
+ const path43 = input.trim().replace(/^["']|["']$/g, "");
30991
+ return path43 && normalize(path43);
30638
30992
  }
30639
30993
  var parseCheckIgnore;
30640
30994
  var init_CheckIgnore = __esm2({
@@ -30948,8 +31302,8 @@ __export2(sub_module_exports, {
30948
31302
  subModuleTask: () => subModuleTask,
30949
31303
  updateSubModuleTask: () => updateSubModuleTask
30950
31304
  });
30951
- function addSubModuleTask(repo, path37) {
30952
- return subModuleTask(["add", repo, path37]);
31305
+ function addSubModuleTask(repo, path43) {
31306
+ return subModuleTask(["add", repo, path43]);
30953
31307
  }
30954
31308
  function initSubModuleTask(customArgs) {
30955
31309
  return subModuleTask(["init", ...customArgs]);
@@ -31282,8 +31636,8 @@ var require_git = __commonJS2({
31282
31636
  }
31283
31637
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
31284
31638
  };
31285
- Git2.prototype.submoduleAdd = function(repo, path37, then) {
31286
- return this._runTask(addSubModuleTask2(repo, path37), trailingFunctionArgument2(arguments));
31639
+ Git2.prototype.submoduleAdd = function(repo, path43, then) {
31640
+ return this._runTask(addSubModuleTask2(repo, path43), trailingFunctionArgument2(arguments));
31287
31641
  };
31288
31642
  Git2.prototype.submoduleUpdate = function(args, then) {
31289
31643
  return this._runTask(
@@ -31927,20 +32281,20 @@ async function isGitRepoDirectory(dirPath) {
31927
32281
  // src/git/discover-repos.ts
31928
32282
  async function discoverGitRepos(cwd = getBridgeRoot()) {
31929
32283
  const result = [];
31930
- const cwdResolved = path6.resolve(cwd);
32284
+ const cwdResolved = path13.resolve(cwd);
31931
32285
  if (await isGitRepoDirectory(cwdResolved)) {
31932
32286
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
31933
32287
  result.push({ absolutePath: cwdResolved, remoteUrl });
31934
32288
  }
31935
32289
  let entries;
31936
32290
  try {
31937
- entries = fs8.readdirSync(cwdResolved, { withFileTypes: true });
32291
+ entries = fs14.readdirSync(cwdResolved, { withFileTypes: true });
31938
32292
  } catch {
31939
32293
  return result;
31940
32294
  }
31941
32295
  for (const ent of entries) {
31942
32296
  if (!ent.isDirectory()) continue;
31943
- const childPath = path6.join(cwdResolved, ent.name);
32297
+ const childPath = path13.join(cwdResolved, ent.name);
31944
32298
  if (await isGitRepoDirectory(childPath)) {
31945
32299
  const remoteUrl = await getRemoteOriginUrl(childPath);
31946
32300
  result.push({ absolutePath: childPath, remoteUrl });
@@ -31949,22 +32303,22 @@ async function discoverGitRepos(cwd = getBridgeRoot()) {
31949
32303
  return result;
31950
32304
  }
31951
32305
  async function discoverGitReposUnderRoot(rootPath) {
31952
- const root = path6.resolve(rootPath);
32306
+ const root = path13.resolve(rootPath);
31953
32307
  const roots = [];
31954
32308
  async function walk(dir) {
31955
32309
  if (await isGitRepoDirectory(dir)) {
31956
- roots.push(path6.resolve(dir));
32310
+ roots.push(path13.resolve(dir));
31957
32311
  return;
31958
32312
  }
31959
32313
  let entries;
31960
32314
  try {
31961
- entries = fs8.readdirSync(dir, { withFileTypes: true });
32315
+ entries = fs14.readdirSync(dir, { withFileTypes: true });
31962
32316
  } catch {
31963
32317
  return;
31964
32318
  }
31965
32319
  for (const ent of entries) {
31966
32320
  if (!ent.isDirectory() || ent.name === ".git") continue;
31967
- await walk(path6.join(dir, ent.name));
32321
+ await walk(path13.join(dir, ent.name));
31968
32322
  }
31969
32323
  }
31970
32324
  await walk(root);
@@ -31980,7 +32334,7 @@ async function discoverGitReposUnderRoot(rootPath) {
31980
32334
  // src/git/pre-turn-snapshot.ts
31981
32335
  var execFileAsync5 = promisify6(execFile6);
31982
32336
  function snapshotsDirForCwd(agentCwd) {
31983
- return path7.join(agentCwd, ".buildautomaton", "snapshots");
32337
+ return path14.join(agentCwd, ".buildautomaton", "snapshots");
31984
32338
  }
31985
32339
  async function gitStashCreate(repoRoot, log2) {
31986
32340
  try {
@@ -32009,7 +32363,7 @@ async function gitRun(repoRoot, args, log2, label) {
32009
32363
  async function resolveSnapshotRepoRoots(options) {
32010
32364
  const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
32011
32365
  if (worktreePaths?.length) {
32012
- const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
32366
+ const uniq = [...new Set(worktreePaths.map((p) => path14.resolve(p)))];
32013
32367
  return uniq;
32014
32368
  }
32015
32369
  try {
@@ -32017,7 +32371,7 @@ async function resolveSnapshotRepoRoots(options) {
32017
32371
  const mapped = repos.map((r) => r.absolutePath);
32018
32372
  const sid = sessionId?.trim();
32019
32373
  if (sid) {
32020
- const filtered = mapped.filter((root) => path7.basename(root) === sid);
32374
+ const filtered = mapped.filter((root) => path14.basename(root) === sid);
32021
32375
  if (filtered.length > 0) return filtered;
32022
32376
  }
32023
32377
  return mapped;
@@ -32038,7 +32392,7 @@ async function capturePreTurnSnapshot(options) {
32038
32392
  }
32039
32393
  const dir = snapshotsDirForCwd(agentCwd);
32040
32394
  try {
32041
- fs9.mkdirSync(dir, { recursive: true });
32395
+ fs15.mkdirSync(dir, { recursive: true });
32042
32396
  } catch (e) {
32043
32397
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
32044
32398
  }
@@ -32047,9 +32401,9 @@ async function capturePreTurnSnapshot(options) {
32047
32401
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
32048
32402
  repos
32049
32403
  };
32050
- const filePath = path7.join(dir, `${runId}.json`);
32404
+ const filePath = path14.join(dir, `${runId}.json`);
32051
32405
  try {
32052
- fs9.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
32406
+ fs15.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
32053
32407
  } catch (e) {
32054
32408
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
32055
32409
  }
@@ -32062,7 +32416,7 @@ async function capturePreTurnSnapshot(options) {
32062
32416
  async function applyPreTurnSnapshot(filePath, log2) {
32063
32417
  let data;
32064
32418
  try {
32065
- const raw = fs9.readFileSync(filePath, "utf8");
32419
+ const raw = fs15.readFileSync(filePath, "utf8");
32066
32420
  data = JSON.parse(raw);
32067
32421
  } catch (e) {
32068
32422
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
@@ -32085,7 +32439,7 @@ async function applyPreTurnSnapshot(filePath, log2) {
32085
32439
  return { ok: true };
32086
32440
  }
32087
32441
  function snapshotFilePath(agentCwd, runId) {
32088
- return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
32442
+ return path14.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
32089
32443
  }
32090
32444
 
32091
32445
  // src/git/session-git-queue.ts
@@ -32134,7 +32488,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32134
32488
  continue;
32135
32489
  }
32136
32490
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
32137
- const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
32491
+ const slug = path15.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
32138
32492
  for (const rel of lines) {
32139
32493
  if (rel.includes("..")) continue;
32140
32494
  try {
@@ -32148,7 +32502,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32148
32502
  );
32149
32503
  if (!patchContent.trim()) continue;
32150
32504
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
32151
- const workspaceFilePath = path8.join(repo.path, rel);
32505
+ const workspaceFilePath = path15.join(repo.path, rel);
32152
32506
  const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
32153
32507
  sendSessionUpdate({
32154
32508
  type: "session_file_change",
@@ -32170,9 +32524,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32170
32524
  // src/agents/acp/put-summarize-change-summaries.ts
32171
32525
  async function putEncryptedChangeSummaryRows(params) {
32172
32526
  const base = params.apiBaseUrl.replace(/\/+$/, "");
32173
- const entries = params.rows.map(({ path: path37, summary }) => {
32527
+ const entries = params.rows.map(({ path: path43, summary }) => {
32174
32528
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
32175
- return { path: path37, summary: JSON.stringify(enc) };
32529
+ return { path: path43, summary: JSON.stringify(enc) };
32176
32530
  });
32177
32531
  const res = await fetch(
32178
32532
  `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -32483,8 +32837,8 @@ async function sendPromptToAgent(options) {
32483
32837
  }
32484
32838
 
32485
32839
  // src/agents/acp/ensure-acp-client.ts
32486
- import * as fs11 from "node:fs";
32487
- import * as path13 from "node:path";
32840
+ import * as fs16 from "node:fs";
32841
+ import * as path19 from "node:path";
32488
32842
 
32489
32843
  // src/error-message.ts
32490
32844
  function errorMessage(err) {
@@ -32648,8 +33002,8 @@ function enrichAcpPermissionRpcResultFromRequestParams(result, params) {
32648
33002
  }
32649
33003
 
32650
33004
  // src/agents/acp/clients/shared/acp-fs-read-write.ts
32651
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
32652
- import { dirname as dirname2 } from "node:path";
33005
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
33006
+ import { dirname as dirname3 } from "node:path";
32653
33007
 
32654
33008
  // src/files/diff/unified-diff.ts
32655
33009
  function computeLineDiff(oldText, newText) {
@@ -32698,21 +33052,21 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
32698
33052
  }
32699
33053
 
32700
33054
  // src/agents/acp/safe-fs-path.ts
32701
- import * as path9 from "node:path";
33055
+ import * as path16 from "node:path";
32702
33056
  function resolveSafePathUnderCwd(cwd, filePath) {
32703
33057
  const trimmed2 = filePath.trim();
32704
33058
  if (!trimmed2) return null;
32705
- const normalizedCwd = path9.resolve(cwd);
32706
- const resolved = path9.isAbsolute(trimmed2) ? path9.normalize(trimmed2) : path9.resolve(normalizedCwd, trimmed2);
32707
- const rel = path9.relative(normalizedCwd, resolved);
32708
- if (rel.startsWith("..") || path9.isAbsolute(rel)) return null;
33059
+ const normalizedCwd = path16.resolve(cwd);
33060
+ const resolved = path16.isAbsolute(trimmed2) ? path16.normalize(trimmed2) : path16.resolve(normalizedCwd, trimmed2);
33061
+ const rel = path16.relative(normalizedCwd, resolved);
33062
+ if (rel.startsWith("..") || path16.isAbsolute(rel)) return null;
32709
33063
  return resolved;
32710
33064
  }
32711
33065
  function toDisplayPathRelativeToCwd(cwd, absolutePath) {
32712
- const normalizedCwd = path9.resolve(cwd);
32713
- const rel = path9.relative(normalizedCwd, path9.resolve(absolutePath));
32714
- if (!rel || rel === "") return path9.basename(absolutePath);
32715
- return rel.split(path9.sep).join("/");
33066
+ const normalizedCwd = path16.resolve(cwd);
33067
+ const rel = path16.relative(normalizedCwd, path16.resolve(absolutePath));
33068
+ if (!rel || rel === "") return path16.basename(absolutePath);
33069
+ return rel.split(path16.sep).join("/");
32716
33070
  }
32717
33071
 
32718
33072
  // src/agents/acp/clients/shared/acp-fs-read-write.ts
@@ -32727,7 +33081,7 @@ function acpReadTextFileInProcess(ctx, filePath, line, limit) {
32727
33081
  const resolvedPath = resolveSafePathUnderCwd(ctx.cwd, filePath);
32728
33082
  if (!resolvedPath) throw new Error("Invalid or disallowed path");
32729
33083
  try {
32730
- let content = readFileSync2(resolvedPath, "utf8");
33084
+ let content = readFileSync3(resolvedPath, "utf8");
32731
33085
  content = sliceFileContentForAcp(content, line, limit);
32732
33086
  return { content };
32733
33087
  } catch (e) {
@@ -32740,11 +33094,11 @@ function acpWriteTextFileInProcess(ctx, filePath, newText) {
32740
33094
  if (!resolvedPath) throw new Error("Invalid or disallowed path");
32741
33095
  let oldText = "";
32742
33096
  try {
32743
- oldText = readFileSync2(resolvedPath, "utf8");
33097
+ oldText = readFileSync3(resolvedPath, "utf8");
32744
33098
  } catch (e) {
32745
33099
  if (e.code !== "ENOENT") throw e;
32746
33100
  }
32747
- mkdirSync2(dirname2(resolvedPath), { recursive: true });
33101
+ mkdirSync2(dirname3(resolvedPath), { recursive: true });
32748
33102
  writeFileSync2(resolvedPath, newText, "utf8");
32749
33103
  const displayPath = toDisplayPathRelativeToCwd(ctx.cwd, resolvedPath);
32750
33104
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
@@ -32806,6 +33160,48 @@ async function applyClaudePermissionFromAcpSession(params) {
32806
33160
  }
32807
33161
  }
32808
33162
 
33163
+ // src/agents/acp/apply-acp-model-from-agent-session.ts
33164
+ function flattenSelectOptions2(options) {
33165
+ if (options == null || options.length === 0) return [];
33166
+ const first2 = options[0];
33167
+ if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
33168
+ return options.flatMap((g) => Array.isArray(g.options) ? g.options : []);
33169
+ }
33170
+ return options;
33171
+ }
33172
+ function looksLikeModelOption(o) {
33173
+ if (o.category === "model" || o.category === "models") return true;
33174
+ const id = typeof o.id === "string" ? o.id.toLowerCase() : "";
33175
+ if (id === "model" || id.endsWith("_model") || id.includes("model")) return true;
33176
+ const name = typeof o.name === "string" ? o.name.toLowerCase() : "";
33177
+ return name.includes("model") && !name.includes("mode");
33178
+ }
33179
+ function pickModelConfigOption(configOptions) {
33180
+ if (configOptions == null || configOptions.length === 0) return null;
33181
+ return configOptions.find(looksLikeModelOption) ?? null;
33182
+ }
33183
+ async function applyAcpModelFromAcpSession(params) {
33184
+ const { sessionId, agentConfig, configOptions, setSessionConfigOption, logDebug: logDebug2 } = params;
33185
+ const desired = getAgentModelFromAgentConfig(agentConfig);
33186
+ if (desired == null) return;
33187
+ const modelOpt = pickModelConfigOption(configOptions ?? null);
33188
+ if (modelOpt == null) return;
33189
+ const flat = flattenSelectOptions2(modelOpt.options);
33190
+ const allowed = flat.some((o) => o.value === desired);
33191
+ if (!allowed) return;
33192
+ if (modelOpt.currentValue === desired) return;
33193
+ try {
33194
+ logDebug2(
33195
+ `[Agent] ACP session/set_config_option (model) configId=${JSON.stringify(modelOpt.id)} value=${JSON.stringify(desired)} was=${JSON.stringify(modelOpt.currentValue)} sessionId=${sessionId.slice(0, 8)}\u2026`
33196
+ );
33197
+ await setSessionConfigOption({ sessionId, configId: modelOpt.id, value: desired });
33198
+ } catch (e) {
33199
+ logDebug2(
33200
+ `[Agent] ACP session/set_config_option (model) failed: ${e instanceof Error ? e.message : String(e)}`
33201
+ );
33202
+ }
33203
+ }
33204
+
32809
33205
  // src/agents/acp/clients/shared/config-options-for-permission.ts
32810
33206
  function configOptionsForPermission(getActive, established) {
32811
33207
  const mem = getActive?.();
@@ -32898,6 +33294,17 @@ async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
32898
33294
  logDebug: ctx.logDebug
32899
33295
  });
32900
33296
  }
33297
+ const cfgAll = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
33298
+ const configOptionsForModel = established.configOptions;
33299
+ if (transport.setSessionConfigOption) {
33300
+ await applyAcpModelFromAcpSession({
33301
+ sessionId,
33302
+ agentConfig: cfgAll,
33303
+ configOptions: configOptionsForPermission(ctx.getActiveConfigOptions, configOptionsForModel),
33304
+ setSessionConfigOption: (p) => transport.setSessionConfigOption(p),
33305
+ logDebug: ctx.logDebug
33306
+ });
33307
+ }
32901
33308
  return established;
32902
33309
  }
32903
33310
 
@@ -33003,10 +33410,15 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText,
33003
33410
  // src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
33004
33411
  function awaitSdkStdioPermissionRequestHandshake(params) {
33005
33412
  const { requestId, paramsRecord, pending, onRequest } = params;
33006
- return new Promise((resolve18) => {
33007
- pending.set(requestId, { resolve: resolve18, params: paramsRecord });
33413
+ return new Promise((resolve20) => {
33414
+ pending.set(requestId, { resolve: resolve20, params: paramsRecord });
33415
+ if (onRequest == null) {
33416
+ pending.delete(requestId);
33417
+ resolve20({ outcome: { outcome: "denied" } });
33418
+ return;
33419
+ }
33008
33420
  try {
33009
- onRequest?.({
33421
+ onRequest({
33010
33422
  requestId,
33011
33423
  method: "session/request_permission",
33012
33424
  params: paramsRecord
@@ -33068,7 +33480,7 @@ async function createSdkStdioAcpClient(options) {
33068
33480
  child.once("close", (code, signal) => {
33069
33481
  onAgentSubprocessExit?.({ code, signal });
33070
33482
  });
33071
- return new Promise((resolve18, reject) => {
33483
+ return new Promise((resolve20, reject) => {
33072
33484
  let initSettled = false;
33073
33485
  const settleReject = (err) => {
33074
33486
  if (initSettled) return;
@@ -33082,7 +33494,7 @@ async function createSdkStdioAcpClient(options) {
33082
33494
  const settleResolve = (handle) => {
33083
33495
  if (initSettled) return;
33084
33496
  initSettled = true;
33085
- resolve18(handle);
33497
+ resolve20(handle);
33086
33498
  };
33087
33499
  child.on("error", (err) => {
33088
33500
  settleReject(new Error(formatSpawnError(err, command[0])));
@@ -33374,7 +33786,7 @@ async function createCursorAcpClient(options) {
33374
33786
  logDebug,
33375
33787
  getStderrText: () => stderrCapture.getText()
33376
33788
  };
33377
- return new Promise((resolve18, reject) => {
33789
+ return new Promise((resolve20, reject) => {
33378
33790
  child.on("error", (err) => {
33379
33791
  child.kill();
33380
33792
  reject(new Error(formatSpawnError2(err, command[0])));
@@ -33453,12 +33865,16 @@ async function createCursorAcpClient(options) {
33453
33865
  }
33454
33866
  if (method === "session/request_permission" && typeof id === "number") {
33455
33867
  const params = msg.params ?? {};
33456
- pendingRequests.set(id, { method, params });
33457
- onRequest?.({
33458
- requestId: String(id),
33459
- method,
33460
- params
33461
- });
33868
+ if (onRequest) {
33869
+ pendingRequests.set(id, { method, params });
33870
+ onRequest({
33871
+ requestId: String(id),
33872
+ method,
33873
+ params
33874
+ });
33875
+ } else {
33876
+ respond(id, { outcome: { outcome: "denied" } });
33877
+ }
33462
33878
  return;
33463
33879
  }
33464
33880
  if (typeof id === "number" && method) {
@@ -33546,7 +33962,7 @@ async function createCursorAcpClient(options) {
33546
33962
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
33547
33963
  });
33548
33964
  const sessionId = established.sessionId;
33549
- resolve18({
33965
+ resolve20({
33550
33966
  sessionId,
33551
33967
  async sendPrompt(prompt, options2) {
33552
33968
  const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
@@ -33681,20 +34097,20 @@ function resolveAgentCommand(preferredAgentType) {
33681
34097
 
33682
34098
  // src/agents/acp/session-file-change-path-kind.ts
33683
34099
  import { execFileSync as execFileSync4 } from "node:child_process";
33684
- import { existsSync, statSync } from "node:fs";
34100
+ import { existsSync as existsSync2, statSync } from "node:fs";
33685
34101
 
33686
34102
  // src/git/get-git-repo-root-sync.ts
33687
34103
  import { execFileSync as execFileSync2 } from "node:child_process";
33688
- import * as path10 from "node:path";
34104
+ import * as path17 from "node:path";
33689
34105
  function getGitRepoRootSync(startDir) {
33690
34106
  try {
33691
34107
  const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
33692
- cwd: path10.resolve(startDir),
34108
+ cwd: path17.resolve(startDir),
33693
34109
  encoding: "utf8",
33694
34110
  stdio: ["ignore", "pipe", "ignore"],
33695
34111
  maxBuffer: 1024 * 1024
33696
34112
  }).trim();
33697
- return out ? path10.resolve(out) : null;
34113
+ return out ? path17.resolve(out) : null;
33698
34114
  } catch {
33699
34115
  return null;
33700
34116
  }
@@ -33702,26 +34118,26 @@ function getGitRepoRootSync(startDir) {
33702
34118
 
33703
34119
  // src/agents/acp/workspace-files.ts
33704
34120
  import { execFileSync as execFileSync3 } from "node:child_process";
33705
- import { readFileSync as readFileSync3 } from "node:fs";
33706
- import * as path11 from "node:path";
34121
+ import { readFileSync as readFileSync4 } from "node:fs";
34122
+ import * as path18 from "node:path";
33707
34123
  function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
33708
34124
  const trimmed2 = rawPath.trim();
33709
34125
  if (!trimmed2) return null;
33710
- const normalizedSessionParent = path11.resolve(sessionParentPath);
34126
+ const normalizedSessionParent = path18.resolve(sessionParentPath);
33711
34127
  let resolvedPath = resolveSafePathUnderCwd(sessionParentPath, trimmed2);
33712
34128
  if (!resolvedPath) {
33713
- const candidate = path11.isAbsolute(trimmed2) ? path11.normalize(trimmed2) : path11.normalize(path11.resolve(normalizedSessionParent, trimmed2));
34129
+ const candidate = path18.isAbsolute(trimmed2) ? path18.normalize(trimmed2) : path18.normalize(path18.resolve(normalizedSessionParent, trimmed2));
33714
34130
  const gitRoot2 = getGitRepoRootSync(sessionParentPath);
33715
34131
  if (!gitRoot2) return null;
33716
- const rel = path11.relative(gitRoot2, candidate);
33717
- if (rel.startsWith("..") || path11.isAbsolute(rel)) return null;
34132
+ const rel = path18.relative(gitRoot2, candidate);
34133
+ if (rel.startsWith("..") || path18.isAbsolute(rel)) return null;
33718
34134
  resolvedPath = candidate;
33719
34135
  }
33720
34136
  const gitRoot = getGitRepoRootSync(sessionParentPath);
33721
34137
  if (gitRoot) {
33722
- const relFromRoot = path11.relative(gitRoot, resolvedPath);
33723
- if (!relFromRoot.startsWith("..") && !path11.isAbsolute(relFromRoot)) {
33724
- return { resolvedPath, display: relFromRoot.split(path11.sep).join("/") };
34138
+ const relFromRoot = path18.relative(gitRoot, resolvedPath);
34139
+ if (!relFromRoot.startsWith("..") && !path18.isAbsolute(relFromRoot)) {
34140
+ return { resolvedPath, display: relFromRoot.split(path18.sep).join("/") };
33725
34141
  }
33726
34142
  }
33727
34143
  return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
@@ -33730,11 +34146,11 @@ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
33730
34146
  if (!displayPath || displayPath.includes("..")) return "";
33731
34147
  const gitRoot = getGitRepoRootSync(sessionParentPath);
33732
34148
  if (gitRoot) {
33733
- const resolvedPath2 = path11.resolve(gitRoot, displayPath);
33734
- const rel = path11.relative(gitRoot, resolvedPath2);
33735
- if (!rel.startsWith("..") && !path11.isAbsolute(rel)) {
34149
+ const resolvedPath2 = path18.resolve(gitRoot, displayPath);
34150
+ const rel = path18.relative(gitRoot, resolvedPath2);
34151
+ if (!rel.startsWith("..") && !path18.isAbsolute(rel)) {
33736
34152
  try {
33737
- return readFileSync3(resolvedPath2, "utf8");
34153
+ return readFileSync4(resolvedPath2, "utf8");
33738
34154
  } catch {
33739
34155
  }
33740
34156
  }
@@ -33742,7 +34158,7 @@ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
33742
34158
  const resolvedPath = resolveSafePathUnderCwd(sessionParentPath, displayPath);
33743
34159
  if (!resolvedPath) return "";
33744
34160
  try {
33745
- return readFileSync3(resolvedPath, "utf8");
34161
+ return readFileSync4(resolvedPath, "utf8");
33746
34162
  } catch {
33747
34163
  return "";
33748
34164
  }
@@ -33751,9 +34167,9 @@ function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
33751
34167
  if (!displayPath || displayPath.includes("..")) return null;
33752
34168
  const gitRoot = getGitRepoRootSync(sessionParentPath);
33753
34169
  if (gitRoot) {
33754
- const resolvedPath = path11.resolve(gitRoot, displayPath);
33755
- const rel = path11.relative(gitRoot, resolvedPath);
33756
- if (!rel.startsWith("..") && !path11.isAbsolute(rel)) return resolvedPath;
34170
+ const resolvedPath = path18.resolve(gitRoot, displayPath);
34171
+ const rel = path18.relative(gitRoot, resolvedPath);
34172
+ if (!rel.startsWith("..") && !path18.isAbsolute(rel)) return resolvedPath;
33757
34173
  }
33758
34174
  return resolveSafePathUnderCwd(sessionParentPath, displayPath);
33759
34175
  }
@@ -33788,7 +34204,7 @@ function gitHeadPathObjectType(sessionParentPath, displayPath) {
33788
34204
  }
33789
34205
  function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
33790
34206
  const resolvedPath = tryWorkspaceDisplayToPath(sessionParentPath, displayPath);
33791
- if (resolvedPath && existsSync(resolvedPath)) {
34207
+ if (resolvedPath && existsSync2(resolvedPath)) {
33792
34208
  try {
33793
34209
  if (statSync(resolvedPath).isDirectory()) {
33794
34210
  return { isDirectory: true, directoryRemoved: false };
@@ -33888,7 +34304,7 @@ function createBridgeOnRequest(opts) {
33888
34304
  }
33889
34305
 
33890
34306
  // src/agents/acp/hooks/extract-acp-file-diffs-from-update/paths-and-text.ts
33891
- import { fileURLToPath as fileURLToPath2 } from "node:url";
34307
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
33892
34308
  function readOptionalTextField(v) {
33893
34309
  if (v === null || v === void 0) return "";
33894
34310
  if (typeof v === "string") return v;
@@ -33898,7 +34314,7 @@ function normalizePathField(raw) {
33898
34314
  const t = raw.trim();
33899
34315
  if (t.startsWith("file://")) {
33900
34316
  try {
33901
- return fileURLToPath2(t);
34317
+ return fileURLToPath3(t);
33902
34318
  } catch {
33903
34319
  return t;
33904
34320
  }
@@ -34356,29 +34772,34 @@ function buildAcpSessionBridgeHooks(opts) {
34356
34772
  }
34357
34773
 
34358
34774
  // src/agents/acp/local-agent-session-file.ts
34359
- import fs10 from "node:fs";
34360
- import os3 from "node:os";
34361
- import path12 from "node:path";
34362
- var LOCAL_AGENT_SESSION_DIR = path12.join(os3.homedir(), ".buildautomaton", "agent-sessions");
34363
- function safeFileSlug(cloudSessionId) {
34775
+ function sessionKeyForCloudSessionId(cloudSessionId) {
34364
34776
  const t = cloudSessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 220);
34365
34777
  return t.length > 0 ? t : "session";
34366
34778
  }
34367
- function localAgentSessionFilePath(cloudSessionId) {
34368
- return path12.join(LOCAL_AGENT_SESSION_DIR, `${safeFileSlug(cloudSessionId)}.json`);
34369
- }
34370
34779
  function readLocalAgentSessionFile(cloudSessionId) {
34371
34780
  try {
34372
- const p = localAgentSessionFilePath(cloudSessionId);
34373
- const raw = fs10.readFileSync(p, "utf8");
34374
- const parsed = JSON.parse(raw);
34375
- if (parsed.v !== 1) return null;
34781
+ const db = getCliDatabase();
34782
+ const key = sessionKeyForCloudSessionId(cloudSessionId);
34783
+ const row = db.get(
34784
+ "SELECT acp_session_id, backend_agent_type, config_options_json, updated_at FROM agent_session WHERE session_key = ?",
34785
+ [key]
34786
+ );
34787
+ if (!row) return null;
34788
+ let configOptions = null;
34789
+ if (row.config_options_json != null && row.config_options_json !== "") {
34790
+ try {
34791
+ const parsed = JSON.parse(row.config_options_json);
34792
+ configOptions = Array.isArray(parsed) ? parsed : null;
34793
+ } catch {
34794
+ configOptions = null;
34795
+ }
34796
+ }
34376
34797
  return {
34377
34798
  v: 1,
34378
- acpSessionId: typeof parsed.acpSessionId === "string" ? parsed.acpSessionId : null,
34379
- backendAgentType: typeof parsed.backendAgentType === "string" ? parsed.backendAgentType : null,
34380
- configOptions: Array.isArray(parsed.configOptions) ? parsed.configOptions : null,
34381
- updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
34799
+ acpSessionId: row.acp_session_id,
34800
+ backendAgentType: row.backend_agent_type,
34801
+ configOptions,
34802
+ updatedAt: row.updated_at
34382
34803
  };
34383
34804
  } catch {
34384
34805
  return null;
@@ -34386,9 +34807,8 @@ function readLocalAgentSessionFile(cloudSessionId) {
34386
34807
  }
34387
34808
  function writeLocalAgentSessionFile(cloudSessionId, patch) {
34388
34809
  try {
34389
- const dir = LOCAL_AGENT_SESSION_DIR;
34390
- if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
34391
- const p = localAgentSessionFilePath(cloudSessionId);
34810
+ const db = getCliDatabase();
34811
+ const key = sessionKeyForCloudSessionId(cloudSessionId);
34392
34812
  const prev = readLocalAgentSessionFile(cloudSessionId);
34393
34813
  const next = {
34394
34814
  v: 1,
@@ -34397,7 +34817,17 @@ function writeLocalAgentSessionFile(cloudSessionId, patch) {
34397
34817
  configOptions: patch.configOptions !== void 0 ? patch.configOptions : prev?.configOptions ?? null,
34398
34818
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
34399
34819
  };
34400
- fs10.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
34820
+ const configJson = next.configOptions != null ? JSON.stringify(next.configOptions) : null;
34821
+ db.run(
34822
+ `INSERT INTO agent_session (session_key, acp_session_id, backend_agent_type, config_options_json, updated_at)
34823
+ VALUES (?, ?, ?, ?, ?)
34824
+ ON CONFLICT(session_key) DO UPDATE SET
34825
+ acp_session_id = excluded.acp_session_id,
34826
+ backend_agent_type = excluded.backend_agent_type,
34827
+ config_options_json = excluded.config_options_json,
34828
+ updated_at = excluded.updated_at`,
34829
+ [key, next.acpSessionId, next.backendAgentType, configJson, next.updatedAt]
34830
+ );
34401
34831
  } catch {
34402
34832
  }
34403
34833
  }
@@ -34412,6 +34842,7 @@ async function ensureAcpClient(options) {
34412
34842
  sessionParentPath,
34413
34843
  routing,
34414
34844
  cloudSessionId,
34845
+ reportAgentCapabilities,
34415
34846
  sendSessionUpdate,
34416
34847
  sendRequest,
34417
34848
  log: log2
@@ -34421,7 +34852,7 @@ async function ensureAcpClient(options) {
34421
34852
  if (state.acpStartPromise && !state.acpHandle) {
34422
34853
  await state.acpStartPromise;
34423
34854
  }
34424
- if (state.acpHandle && state.lastAcpCwd != null && path13.resolve(state.lastAcpCwd) !== path13.resolve(targetSessionParentPath)) {
34855
+ if (state.acpHandle && state.lastAcpCwd != null && path19.resolve(state.lastAcpCwd) !== path19.resolve(targetSessionParentPath)) {
34425
34856
  try {
34426
34857
  state.acpHandle.disconnect();
34427
34858
  } catch {
@@ -34456,7 +34887,7 @@ async function ensureAcpClient(options) {
34456
34887
  if (!state.acpStartPromise) {
34457
34888
  let statOk = false;
34458
34889
  try {
34459
- const st = fs11.statSync(targetSessionParentPath);
34890
+ const st = fs16.statSync(targetSessionParentPath);
34460
34891
  statOk = st.isDirectory();
34461
34892
  if (!statOk) {
34462
34893
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
@@ -34499,6 +34930,12 @@ async function ensureAcpClient(options) {
34499
34930
  backendAgentType: preferredAgentType
34500
34931
  });
34501
34932
  }
34933
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(info.configOptions) && info.configOptions.length > 0) {
34934
+ reportAgentCapabilities({
34935
+ agentType: preferredAgentType,
34936
+ configOptions: info.configOptions
34937
+ });
34938
+ }
34502
34939
  },
34503
34940
  onAcpConfigOptionsUpdated: (configOptions) => {
34504
34941
  state.activeSessionConfigOptions = configOptions;
@@ -34508,6 +34945,12 @@ async function ensureAcpClient(options) {
34508
34945
  backendAgentType: preferredAgentType
34509
34946
  });
34510
34947
  }
34948
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(configOptions) && configOptions.length > 0) {
34949
+ reportAgentCapabilities({
34950
+ agentType: preferredAgentType,
34951
+ configOptions
34952
+ });
34953
+ }
34511
34954
  },
34512
34955
  onAgentSubprocessExit: () => {
34513
34956
  state.acpHandle = null;
@@ -34538,7 +34981,7 @@ async function ensureAcpClient(options) {
34538
34981
 
34539
34982
  // src/agents/acp/create-acp-manager.ts
34540
34983
  async function createAcpManager(options) {
34541
- const { log: log2 } = options;
34984
+ const { log: log2, reportAgentCapabilities } = options;
34542
34985
  const state = {
34543
34986
  acpHandle: null,
34544
34987
  acpStartPromise: null,
@@ -34582,7 +35025,8 @@ async function createAcpManager(options) {
34582
35025
  cloudApiBaseUrl,
34583
35026
  getCloudAccessToken,
34584
35027
  e2ee,
34585
- attachments
35028
+ attachments,
35029
+ agentId
34586
35030
  } = opts;
34587
35031
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
34588
35032
  pendingCancelRunId = void 0;
@@ -34600,7 +35044,8 @@ async function createAcpManager(options) {
34600
35044
  cloudSessionId: sessionId,
34601
35045
  sendSessionUpdate,
34602
35046
  sendRequest: sendSessionUpdate,
34603
- log: log2
35047
+ log: log2,
35048
+ reportAgentCapabilities
34604
35049
  });
34605
35050
  if (!handle) {
34606
35051
  const errMsg = state.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app.";
@@ -34703,12 +35148,12 @@ async function createAcpManager(options) {
34703
35148
  }
34704
35149
 
34705
35150
  // src/worktrees/session-worktree-manager.ts
34706
- import * as path20 from "node:path";
34707
- import os5 from "node:os";
35151
+ import * as path26 from "node:path";
35152
+ import os8 from "node:os";
34708
35153
 
34709
35154
  // src/worktrees/prepare-new-session-worktrees.ts
34710
- import * as fs13 from "node:fs";
34711
- import * as path15 from "node:path";
35155
+ import * as fs18 from "node:fs";
35156
+ import * as path21 from "node:path";
34712
35157
 
34713
35158
  // src/git/worktree-add.ts
34714
35159
  async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
@@ -34717,12 +35162,12 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
34717
35162
  }
34718
35163
 
34719
35164
  // src/worktrees/worktree-layout-file.ts
34720
- import * as fs12 from "node:fs";
34721
- import * as path14 from "node:path";
34722
- import os4 from "node:os";
35165
+ import * as fs17 from "node:fs";
35166
+ import * as path20 from "node:path";
35167
+ import os7 from "node:os";
34723
35168
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
34724
35169
  function defaultWorktreeLayoutPath() {
34725
- return path14.join(os4.homedir(), ".buildautomaton", LAYOUT_FILENAME);
35170
+ return path20.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
34726
35171
  }
34727
35172
  function normalizeLoadedLayout(raw) {
34728
35173
  if (raw && typeof raw === "object" && "launcherCwds" in raw) {
@@ -34734,8 +35179,8 @@ function normalizeLoadedLayout(raw) {
34734
35179
  function loadWorktreeLayout() {
34735
35180
  try {
34736
35181
  const p = defaultWorktreeLayoutPath();
34737
- if (!fs12.existsSync(p)) return { launcherCwds: [] };
34738
- const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
35182
+ if (!fs17.existsSync(p)) return { launcherCwds: [] };
35183
+ const raw = JSON.parse(fs17.readFileSync(p, "utf8"));
34739
35184
  return normalizeLoadedLayout(raw);
34740
35185
  } catch {
34741
35186
  return { launcherCwds: [] };
@@ -34743,24 +35188,24 @@ function loadWorktreeLayout() {
34743
35188
  }
34744
35189
  function saveWorktreeLayout(layout) {
34745
35190
  try {
34746
- const dir = path14.dirname(defaultWorktreeLayoutPath());
34747
- fs12.mkdirSync(dir, { recursive: true });
34748
- fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
35191
+ const dir = path20.dirname(defaultWorktreeLayoutPath());
35192
+ fs17.mkdirSync(dir, { recursive: true });
35193
+ fs17.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
34749
35194
  } catch {
34750
35195
  }
34751
35196
  }
34752
35197
  function baseNameSafe(pathString) {
34753
- return path14.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
35198
+ return path20.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
34754
35199
  }
34755
35200
  function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
34756
- const norm = path14.resolve(bridgeRootPath2);
34757
- const existing = layout.launcherCwds.find((e) => path14.resolve(e.absolutePath) === norm);
35201
+ const norm = path20.resolve(bridgeRootPath2);
35202
+ const existing = layout.launcherCwds.find((e) => path20.resolve(e.absolutePath) === norm);
34758
35203
  return existing?.dirName;
34759
35204
  }
34760
35205
  function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
34761
35206
  const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
34762
35207
  if (existing) return existing;
34763
- const norm = path14.resolve(bridgeRootPath2);
35208
+ const norm = path20.resolve(bridgeRootPath2);
34764
35209
  const base = baseNameSafe(norm);
34765
35210
  const used = new Set(layout.launcherCwds.map((e) => e.dirName));
34766
35211
  let name = base;
@@ -34777,10 +35222,10 @@ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
34777
35222
  // src/worktrees/prepare-new-session-worktrees.ts
34778
35223
  async function prepareNewSessionWorktrees(options) {
34779
35224
  const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
34780
- const bridgeResolved = path15.resolve(bridgeRoot);
35225
+ const bridgeResolved = path21.resolve(bridgeRoot);
34781
35226
  const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
34782
- const bridgeKeyDir = path15.join(worktreesRootPath, cwdKey);
34783
- const sessionDir = path15.join(bridgeKeyDir, sessionId);
35227
+ const bridgeKeyDir = path21.join(worktreesRootPath, cwdKey);
35228
+ const sessionDir = path21.join(bridgeKeyDir, sessionId);
34784
35229
  const repos = await discoverGitReposUnderRoot(bridgeResolved);
34785
35230
  if (repos.length === 0) {
34786
35231
  log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
@@ -34788,14 +35233,14 @@ async function prepareNewSessionWorktrees(options) {
34788
35233
  }
34789
35234
  const branch = `session-${sessionId}`;
34790
35235
  const worktreePaths = [];
34791
- fs13.mkdirSync(sessionDir, { recursive: true });
35236
+ fs18.mkdirSync(sessionDir, { recursive: true });
34792
35237
  for (const repo of repos) {
34793
- let rel = path15.relative(bridgeResolved, repo.absolutePath);
34794
- if (rel.startsWith("..") || path15.isAbsolute(rel)) continue;
35238
+ let rel = path21.relative(bridgeResolved, repo.absolutePath);
35239
+ if (rel.startsWith("..") || path21.isAbsolute(rel)) continue;
34795
35240
  const relNorm = rel === "" ? "." : rel;
34796
- const wtPath = relNorm === "." ? sessionDir : path15.join(sessionDir, relNorm);
35241
+ const wtPath = relNorm === "." ? sessionDir : path21.join(sessionDir, relNorm);
34797
35242
  if (relNorm !== ".") {
34798
- fs13.mkdirSync(path15.dirname(wtPath), { recursive: true });
35243
+ fs18.mkdirSync(path21.dirname(wtPath), { recursive: true });
34799
35244
  }
34800
35245
  try {
34801
35246
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
@@ -34837,23 +35282,23 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
34837
35282
  }
34838
35283
 
34839
35284
  // src/worktrees/remove-session-worktrees.ts
34840
- import * as fs16 from "node:fs";
35285
+ import * as fs21 from "node:fs";
34841
35286
 
34842
35287
  // src/git/worktree-remove.ts
34843
- import * as fs15 from "node:fs";
35288
+ import * as fs20 from "node:fs";
34844
35289
 
34845
35290
  // src/git/resolve-main-repo-from-git-file.ts
34846
- import * as fs14 from "node:fs";
34847
- import * as path16 from "node:path";
35291
+ import * as fs19 from "node:fs";
35292
+ import * as path22 from "node:path";
34848
35293
  function resolveMainRepoFromWorktreeGitFile(wt) {
34849
- const gitDirFile = path16.join(wt, ".git");
34850
- if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
34851
- const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
35294
+ const gitDirFile = path22.join(wt, ".git");
35295
+ if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
35296
+ const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
34852
35297
  const m = first2.match(/^gitdir:\s*(.+)$/im);
34853
35298
  if (!m) return "";
34854
- const gitWorktreePath = path16.resolve(wt, m[1].trim());
34855
- const gitDir = path16.dirname(path16.dirname(gitWorktreePath));
34856
- return path16.dirname(gitDir);
35299
+ const gitWorktreePath = path22.resolve(wt, m[1].trim());
35300
+ const gitDir = path22.dirname(path22.dirname(gitWorktreePath));
35301
+ return path22.dirname(gitDir);
34857
35302
  }
34858
35303
 
34859
35304
  // src/git/worktree-remove.ts
@@ -34862,7 +35307,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
34862
35307
  if (mainRepo) {
34863
35308
  await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
34864
35309
  } else {
34865
- fs15.rmSync(worktreePath, { recursive: true, force: true });
35310
+ fs20.rmSync(worktreePath, { recursive: true, force: true });
34866
35311
  }
34867
35312
  }
34868
35313
 
@@ -34875,7 +35320,7 @@ async function removeSessionWorktrees(paths, log2) {
34875
35320
  } catch (e) {
34876
35321
  log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
34877
35322
  try {
34878
- fs16.rmSync(wt, { recursive: true, force: true });
35323
+ fs21.rmSync(wt, { recursive: true, force: true });
34879
35324
  } catch {
34880
35325
  }
34881
35326
  }
@@ -35095,7 +35540,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
35095
35540
  }
35096
35541
 
35097
35542
  // src/git/working-directory/changes/get-working-tree-change-repo-details.ts
35098
- import * as path18 from "node:path";
35543
+ import * as path24 from "node:path";
35099
35544
 
35100
35545
  // src/git/working-directory/changes/parse-git-status.ts
35101
35546
  function parseNameStatusLines(lines) {
@@ -35215,8 +35660,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
35215
35660
  }
35216
35661
 
35217
35662
  // src/git/working-directory/changes/list-changed-files-for-repo.ts
35218
- import * as fs18 from "node:fs";
35219
- import * as path17 from "node:path";
35663
+ import * as fs23 from "node:fs";
35664
+ import * as path23 from "node:path";
35220
35665
 
35221
35666
  // src/git/working-directory/changes/count-lines.ts
35222
35667
  import { createReadStream } from "node:fs";
@@ -35240,7 +35685,7 @@ async function countTextFileLines(filePath) {
35240
35685
  }
35241
35686
 
35242
35687
  // src/git/working-directory/changes/hydrate-patch.ts
35243
- import * as fs17 from "node:fs";
35688
+ import * as fs22 from "node:fs";
35244
35689
  var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
35245
35690
  var MAX_HYDRATE_LINES_PER_GAP = 8e3;
35246
35691
  var MAX_HYDRATE_LINES_PER_FILE = 8e4;
@@ -35255,7 +35700,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
35255
35700
  }
35256
35701
  async function readWorktreeFileLines(filePath) {
35257
35702
  try {
35258
- const raw = await fs17.promises.readFile(filePath, "utf8");
35703
+ const raw = await fs22.promises.readFile(filePath, "utf8");
35259
35704
  return raw.split(/\r?\n/);
35260
35705
  } catch {
35261
35706
  return null;
@@ -35390,7 +35835,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
35390
35835
  const rows = [];
35391
35836
  for (const pathInRepo of paths) {
35392
35837
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
35393
- const repoFilePath = path17.join(repoGitCwd, pathInRepo);
35838
+ const repoFilePath = path23.join(repoGitCwd, pathInRepo);
35394
35839
  const nums = numByPath.get(pathInRepo);
35395
35840
  let additions = nums?.additions ?? 0;
35396
35841
  let deletions = nums?.deletions ?? 0;
@@ -35403,7 +35848,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
35403
35848
  deletions = fromGit.deletions;
35404
35849
  } else {
35405
35850
  try {
35406
- const st = await fs18.promises.stat(repoFilePath);
35851
+ const st = await fs23.promises.stat(repoFilePath);
35407
35852
  if (st.isFile()) additions = await countTextFileLines(repoFilePath);
35408
35853
  else additions = 0;
35409
35854
  } catch {
@@ -35429,7 +35874,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
35429
35874
  } else {
35430
35875
  pathInRepo = row.pathRelLauncher;
35431
35876
  }
35432
- const filePath = path17.join(repoGitCwd, pathInRepo);
35877
+ const filePath = path23.join(repoGitCwd, pathInRepo);
35433
35878
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
35434
35879
  if (patch) {
35435
35880
  patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
@@ -35445,8 +35890,8 @@ function normRepoRel(p) {
35445
35890
  return x === "" ? "." : x;
35446
35891
  }
35447
35892
  async function getWorkingTreeChangeRepoDetails(options) {
35448
- const bridgeRoot = path18.resolve(getBridgeRoot());
35449
- const sessionWtRoot = options.sessionWorktreeRootPath ? path18.resolve(options.sessionWorktreeRootPath) : null;
35893
+ const bridgeRoot = path24.resolve(getBridgeRoot());
35894
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path24.resolve(options.sessionWorktreeRootPath) : null;
35450
35895
  const legacyNested = options.legacyRepoNestedSessionLayout === true;
35451
35896
  const out = [];
35452
35897
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
@@ -35459,7 +35904,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
35459
35904
  }
35460
35905
  const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
35461
35906
  for (const target of options.commitTargetPaths) {
35462
- const t = path18.resolve(target);
35907
+ const t = path24.resolve(target);
35463
35908
  if (!await isGitRepoDirectory(t)) continue;
35464
35909
  const g = cliSimpleGit(t);
35465
35910
  let branch = "HEAD";
@@ -35472,8 +35917,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
35472
35917
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
35473
35918
  let repoRelPath;
35474
35919
  if (sessionWtRoot) {
35475
- const anchor = legacyNested ? path18.dirname(t) : t;
35476
- const relNorm = path18.relative(sessionWtRoot, anchor);
35920
+ const anchor = legacyNested ? path24.dirname(t) : t;
35921
+ const relNorm = path24.relative(sessionWtRoot, anchor);
35477
35922
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
35478
35923
  } else {
35479
35924
  let top = t;
@@ -35482,8 +35927,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
35482
35927
  } catch {
35483
35928
  top = t;
35484
35929
  }
35485
- const rel = path18.relative(bridgeRoot, path18.resolve(top)).replace(/\\/g, "/") || ".";
35486
- repoRelPath = rel.startsWith("..") ? path18.basename(path18.resolve(top)) : rel;
35930
+ const rel = path24.relative(bridgeRoot, path24.resolve(top)).replace(/\\/g, "/") || ".";
35931
+ repoRelPath = rel.startsWith("..") ? path24.basename(path24.resolve(top)) : rel;
35487
35932
  }
35488
35933
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
35489
35934
  if (filter && norm !== filter) continue;
@@ -35548,11 +35993,11 @@ async function commitSessionWorktrees(options) {
35548
35993
  }
35549
35994
 
35550
35995
  // src/worktrees/discover-session-worktree-on-disk.ts
35551
- import * as fs19 from "node:fs";
35552
- import * as path19 from "node:path";
35996
+ import * as fs24 from "node:fs";
35997
+ import * as path25 from "node:path";
35553
35998
  function isGitDir(dirPath) {
35554
35999
  try {
35555
- return fs19.existsSync(path19.join(dirPath, ".git"));
36000
+ return fs24.existsSync(path25.join(dirPath, ".git"));
35556
36001
  } catch {
35557
36002
  return false;
35558
36003
  }
@@ -35561,23 +36006,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
35561
36006
  const out = [];
35562
36007
  const walk = (dir) => {
35563
36008
  if (isGitDir(dir)) {
35564
- out.push(path19.resolve(dir));
36009
+ out.push(path25.resolve(dir));
35565
36010
  return;
35566
36011
  }
35567
36012
  let entries;
35568
36013
  try {
35569
- entries = fs19.readdirSync(dir, { withFileTypes: true });
36014
+ entries = fs24.readdirSync(dir, { withFileTypes: true });
35570
36015
  } catch {
35571
36016
  return;
35572
36017
  }
35573
36018
  for (const e of entries) {
35574
36019
  if (e.name.startsWith(".")) continue;
35575
- const full = path19.join(dir, e.name);
36020
+ const full = path25.join(dir, e.name);
35576
36021
  if (!e.isDirectory()) continue;
35577
36022
  walk(full);
35578
36023
  }
35579
36024
  };
35580
- walk(path19.resolve(rootPath));
36025
+ walk(path25.resolve(rootPath));
35581
36026
  return [...new Set(out)];
35582
36027
  }
35583
36028
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
@@ -35586,16 +36031,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
35586
36031
  if (depth > maxDepth) return;
35587
36032
  let entries;
35588
36033
  try {
35589
- entries = fs19.readdirSync(dir, { withFileTypes: true });
36034
+ entries = fs24.readdirSync(dir, { withFileTypes: true });
35590
36035
  } catch {
35591
36036
  return;
35592
36037
  }
35593
36038
  for (const e of entries) {
35594
36039
  if (e.name.startsWith(".")) continue;
35595
- const full = path19.join(dir, e.name);
36040
+ const full = path25.join(dir, e.name);
35596
36041
  if (!e.isDirectory()) continue;
35597
36042
  if (e.name === sessionId) {
35598
- if (isGitDir(full)) out.push(path19.resolve(full));
36043
+ if (isGitDir(full)) out.push(path25.resolve(full));
35599
36044
  } else {
35600
36045
  walk(full, depth + 1);
35601
36046
  }
@@ -35607,14 +36052,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
35607
36052
  function tryBindingFromSessionDirectory(sessionDir) {
35608
36053
  let st;
35609
36054
  try {
35610
- st = fs19.statSync(sessionDir);
36055
+ st = fs24.statSync(sessionDir);
35611
36056
  } catch {
35612
36057
  return null;
35613
36058
  }
35614
36059
  if (!st.isDirectory()) return null;
35615
36060
  const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
35616
36061
  if (worktreePaths.length === 0) return null;
35617
- const abs = path19.resolve(sessionDir);
36062
+ const abs = path25.resolve(sessionDir);
35618
36063
  return {
35619
36064
  sessionParentPath: abs,
35620
36065
  workingTreeRelRoot: abs,
@@ -35624,20 +36069,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
35624
36069
  function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
35625
36070
  const sid = sessionId.trim();
35626
36071
  if (!sid) return null;
35627
- const hintR = path19.resolve(checkoutPath);
36072
+ const hintR = path25.resolve(checkoutPath);
35628
36073
  let best = null;
35629
- let cur = path19.dirname(hintR);
36074
+ let cur = path25.dirname(hintR);
35630
36075
  for (let i = 0; i < 40; i++) {
35631
36076
  const paths = collectWorktreeRootsNamed(cur, sid, 24);
35632
- if (paths.some((p) => path19.resolve(p) === hintR)) {
35633
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path19.resolve(paths[0]);
36077
+ if (paths.some((p) => path25.resolve(p) === hintR)) {
36078
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path25.resolve(paths[0]);
35634
36079
  best = {
35635
- sessionParentPath: path19.resolve(isolated),
35636
- workingTreeRelRoot: path19.resolve(cur),
35637
- repoCheckoutPaths: paths.map((p) => path19.resolve(p))
36080
+ sessionParentPath: path25.resolve(isolated),
36081
+ workingTreeRelRoot: path25.resolve(cur),
36082
+ repoCheckoutPaths: paths.map((p) => path25.resolve(p))
35638
36083
  };
35639
36084
  }
35640
- const next = path19.dirname(cur);
36085
+ const next = path25.dirname(cur);
35641
36086
  if (next === cur) break;
35642
36087
  cur = next;
35643
36088
  }
@@ -35645,33 +36090,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
35645
36090
  }
35646
36091
  function discoverSessionWorktreeOnDisk(options) {
35647
36092
  const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
35648
- if (!sessionId.trim() || !fs19.existsSync(worktreesRootPath)) return null;
36093
+ if (!sessionId.trim() || !fs24.existsSync(worktreesRootPath)) return null;
35649
36094
  const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
35650
36095
  const keys = [];
35651
36096
  if (preferredKey) keys.push(preferredKey);
35652
36097
  try {
35653
- for (const name of fs19.readdirSync(worktreesRootPath)) {
36098
+ for (const name of fs24.readdirSync(worktreesRootPath)) {
35654
36099
  if (name.startsWith(".")) continue;
35655
- const p = path19.join(worktreesRootPath, name);
35656
- if (!fs19.statSync(p).isDirectory()) continue;
36100
+ const p = path25.join(worktreesRootPath, name);
36101
+ if (!fs24.statSync(p).isDirectory()) continue;
35657
36102
  if (name !== preferredKey) keys.push(name);
35658
36103
  }
35659
36104
  } catch {
35660
36105
  return null;
35661
36106
  }
35662
36107
  for (const key of keys) {
35663
- const layoutRoot = path19.join(worktreesRootPath, key);
35664
- if (!fs19.existsSync(layoutRoot) || !fs19.statSync(layoutRoot).isDirectory()) continue;
35665
- const sessionDir = path19.join(layoutRoot, sessionId);
36108
+ const layoutRoot = path25.join(worktreesRootPath, key);
36109
+ if (!fs24.existsSync(layoutRoot) || !fs24.statSync(layoutRoot).isDirectory()) continue;
36110
+ const sessionDir = path25.join(layoutRoot, sessionId);
35666
36111
  const nested = tryBindingFromSessionDirectory(sessionDir);
35667
36112
  if (nested) return nested;
35668
36113
  const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
35669
36114
  if (legacyPaths.length > 0) {
35670
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path19.resolve(legacyPaths[0]);
36115
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
35671
36116
  return {
35672
- sessionParentPath: path19.resolve(isolated),
35673
- workingTreeRelRoot: path19.resolve(layoutRoot),
35674
- repoCheckoutPaths: legacyPaths.map((p) => path19.resolve(p))
36117
+ sessionParentPath: path25.resolve(isolated),
36118
+ workingTreeRelRoot: path25.resolve(layoutRoot),
36119
+ repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
35675
36120
  };
35676
36121
  }
35677
36122
  }
@@ -35680,12 +36125,12 @@ function discoverSessionWorktreeOnDisk(options) {
35680
36125
  function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
35681
36126
  const sid = sessionId.trim();
35682
36127
  if (!sid) return null;
35683
- const hint = path19.resolve(sessionWorktreeRootPathOrHint);
35684
- const underHint = tryBindingFromSessionDirectory(path19.join(hint, sid));
36128
+ const hint = path25.resolve(sessionWorktreeRootPathOrHint);
36129
+ const underHint = tryBindingFromSessionDirectory(path25.join(hint, sid));
35685
36130
  if (underHint) return underHint;
35686
36131
  const direct = tryBindingFromSessionDirectory(hint);
35687
36132
  if (direct) {
35688
- if (path19.basename(hint) === sid && isGitDir(hint)) {
36133
+ if (path25.basename(hint) === sid && isGitDir(hint)) {
35689
36134
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
35690
36135
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
35691
36136
  return legacyFromCheckout;
@@ -35693,24 +36138,24 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
35693
36138
  }
35694
36139
  return direct;
35695
36140
  }
35696
- if (path19.basename(hint) === sid && isGitDir(hint)) {
36141
+ if (path25.basename(hint) === sid && isGitDir(hint)) {
35697
36142
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
35698
36143
  if (legacyFromCheckout) return legacyFromCheckout;
35699
36144
  }
35700
36145
  let st;
35701
36146
  try {
35702
- st = fs19.statSync(hint);
36147
+ st = fs24.statSync(hint);
35703
36148
  } catch {
35704
36149
  return null;
35705
36150
  }
35706
36151
  if (!st.isDirectory()) return null;
35707
36152
  const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
35708
36153
  if (legacyPaths.length === 0) return null;
35709
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path19.resolve(legacyPaths[0]);
36154
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
35710
36155
  return {
35711
- sessionParentPath: path19.resolve(isolated),
36156
+ sessionParentPath: path25.resolve(isolated),
35712
36157
  workingTreeRelRoot: hint,
35713
- repoCheckoutPaths: legacyPaths.map((p) => path19.resolve(p))
36158
+ repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
35714
36159
  };
35715
36160
  }
35716
36161
 
@@ -35733,10 +36178,10 @@ var SessionWorktreeManager = class {
35733
36178
  this.layout = loadWorktreeLayout();
35734
36179
  }
35735
36180
  rememberSessionWorktrees(sessionId, binding) {
35736
- const paths = binding.repoCheckoutPaths.map((p) => path20.resolve(p));
36181
+ const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
35737
36182
  this.sessionRepoCheckoutPaths.set(sessionId, paths);
35738
- this.sessionParentPathBySession.set(sessionId, path20.resolve(binding.sessionParentPath));
35739
- this.sessionWorkingTreeRelRootBySession.set(sessionId, path20.resolve(binding.workingTreeRelRoot));
36183
+ this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
36184
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path26.resolve(binding.workingTreeRelRoot));
35740
36185
  }
35741
36186
  sessionParentPathAfterRemember(sessionId) {
35742
36187
  return this.sessionParentPathBySession.get(sessionId);
@@ -35753,7 +36198,7 @@ var SessionWorktreeManager = class {
35753
36198
  const parent = this.sessionParentPathBySession.get(sessionId);
35754
36199
  const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
35755
36200
  if (!parent || !relRoot) return false;
35756
- return path20.resolve(parent) !== path20.resolve(relRoot);
36201
+ return path26.resolve(parent) !== path26.resolve(relRoot);
35757
36202
  }
35758
36203
  /**
35759
36204
  * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
@@ -35762,7 +36207,7 @@ var SessionWorktreeManager = class {
35762
36207
  if (!sessionId) return null;
35763
36208
  const sid = sessionId.trim();
35764
36209
  const cached2 = this.sessionParentPathBySession.get(sid);
35765
- if (cached2) return path20.resolve(cached2);
36210
+ if (cached2) return path26.resolve(cached2);
35766
36211
  const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
35767
36212
  if (!paths?.length) return null;
35768
36213
  return resolveIsolatedSessionParentPathFromCheckouts(paths);
@@ -35776,7 +36221,7 @@ var SessionWorktreeManager = class {
35776
36221
  const sid = sessionId.trim();
35777
36222
  const parentPathRaw = opts.sessionParentPath?.trim();
35778
36223
  if (parentPathRaw) {
35779
- const resolved = path20.resolve(parentPathRaw);
36224
+ const resolved = path26.resolve(parentPathRaw);
35780
36225
  if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
35781
36226
  const diskFirst = this.tryDiscoverFromDisk(sid);
35782
36227
  if (diskFirst) {
@@ -35795,7 +36240,7 @@ var SessionWorktreeManager = class {
35795
36240
  this.rememberSessionWorktrees(sid, tryRoot);
35796
36241
  return this.sessionParentPathAfterRemember(sid);
35797
36242
  }
35798
- const next = path20.dirname(cur);
36243
+ const next = path26.dirname(cur);
35799
36244
  if (next === cur) break;
35800
36245
  cur = next;
35801
36246
  }
@@ -35948,63 +36393,35 @@ var SessionWorktreeManager = class {
35948
36393
  }
35949
36394
  };
35950
36395
  function defaultWorktreesRootPath() {
35951
- return path20.join(os5.homedir(), ".buildautomaton", "worktrees");
36396
+ return path26.join(os8.homedir(), ".buildautomaton", "worktrees");
35952
36397
  }
35953
36398
 
35954
36399
  // src/files/watch-file-index.ts
35955
36400
  import { watch } from "node:fs";
36401
+ import path31 from "node:path";
36402
+
36403
+ // src/files/index/paths.ts
35956
36404
  import path27 from "node:path";
36405
+ import crypto2 from "node:crypto";
36406
+ function getCwdHashForFileIndex(resolvedCwd) {
36407
+ return crypto2.createHash("sha256").update(path27.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
36408
+ }
35957
36409
 
35958
36410
  // src/files/index/build-file-index.ts
35959
- import path24 from "node:path";
36411
+ import path29 from "node:path";
35960
36412
 
35961
36413
  // src/runtime/yield-to-event-loop.ts
35962
36414
  function yieldToEventLoop() {
35963
- return new Promise((resolve18) => setImmediate(resolve18));
36415
+ return new Promise((resolve20) => setImmediate(resolve20));
35964
36416
  }
35965
36417
 
35966
36418
  // src/files/index/walk-workspace-tree.ts
35967
- import fs20 from "node:fs";
35968
- import path22 from "node:path";
35969
-
35970
- // src/files/index/constants.ts
35971
- import path21 from "node:path";
35972
- import os6 from "node:os";
35973
- var INDEX_WORK_YIELD_EVERY = 256;
35974
- var INDEX_DIR = path21.join(os6.homedir(), ".buildautomaton");
35975
- var INDEX_HASH_LEN = 16;
35976
- var INDEX_VERSION = 2;
35977
- var INDEX_LOG_PREFIX = "[file-index]";
35978
-
35979
- // src/files/index/walk-workspace-tree.ts
35980
- function walkWorkspaceTreeSync(dir, baseDir, out) {
35981
- let names;
35982
- try {
35983
- names = fs20.readdirSync(dir);
35984
- } catch {
35985
- return;
35986
- }
35987
- for (const name of names) {
35988
- if (name.startsWith(".")) continue;
35989
- const full = path22.join(dir, name);
35990
- let stat3;
35991
- try {
35992
- stat3 = fs20.statSync(full);
35993
- } catch {
35994
- continue;
35995
- }
35996
- const relative5 = path22.relative(baseDir, full).replace(/\\/g, "/");
35997
- if (stat3.isDirectory()) {
35998
- walkWorkspaceTreeSync(full, baseDir, out);
35999
- } else if (stat3.isFile()) {
36000
- out.push(relative5);
36001
- }
36002
- }
36003
- }
36419
+ import fs25 from "node:fs";
36420
+ import path28 from "node:path";
36004
36421
  async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
36005
36422
  let names;
36006
36423
  try {
36007
- names = await fs20.promises.readdir(dir);
36424
+ names = await fs25.promises.readdir(dir);
36008
36425
  } catch {
36009
36426
  return;
36010
36427
  }
@@ -36014,14 +36431,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
36014
36431
  await yieldToEventLoop();
36015
36432
  }
36016
36433
  state.n++;
36017
- const full = path22.join(dir, name);
36434
+ const full = path28.join(dir, name);
36018
36435
  let stat3;
36019
36436
  try {
36020
- stat3 = await fs20.promises.stat(full);
36437
+ stat3 = await fs25.promises.stat(full);
36021
36438
  } catch {
36022
36439
  continue;
36023
36440
  }
36024
- const relative5 = path22.relative(baseDir, full).replace(/\\/g, "/");
36441
+ const relative5 = path28.relative(baseDir, full).replace(/\\/g, "/");
36025
36442
  if (stat3.isDirectory()) {
36026
36443
  await walkWorkspaceTreeAsync(full, baseDir, out, state);
36027
36444
  } else if (stat3.isFile()) {
@@ -36033,205 +36450,124 @@ function createWalkYieldState() {
36033
36450
  return { n: 0 };
36034
36451
  }
36035
36452
 
36036
- // src/files/index/trigram-utils.ts
36037
- function getTrigrams(s) {
36038
- const lower = s.toLowerCase();
36039
- const out = [];
36040
- for (let i = 0; i <= lower.length - 3; i++) {
36041
- out.push(lower.slice(i, i + 3));
36042
- }
36043
- return out;
36044
- }
36045
- function binarySearch(arr, x) {
36046
- let lo = 0;
36047
- let hi = arr.length - 1;
36048
- while (lo <= hi) {
36049
- const mid = lo + hi >>> 1;
36050
- if (arr[mid] < x) lo = mid + 1;
36051
- else if (arr[mid] > x) hi = mid - 1;
36052
- else return mid;
36053
- }
36054
- return -1;
36055
- }
36056
- function intersectSortedTrigramSets(arrays) {
36057
- if (arrays.length === 0) return [];
36058
- if (arrays.length === 1) return arrays[0];
36059
- const byLength = arrays.slice().sort((a, b) => a.length - b.length);
36060
- const smallest = byLength[0];
36061
- const rest = byLength.slice(1);
36062
- const result = [];
36063
- for (const idx of smallest) {
36064
- if (rest.every((arr) => binarySearch(arr, idx) >= 0)) {
36065
- result.push(idx);
36066
- }
36067
- }
36068
- return result;
36069
- }
36070
-
36071
- // src/files/index/build-trigram-map.ts
36072
- function buildTrigramMapForPaths(paths) {
36073
- const trigramIndex = {};
36074
- for (let i = 0; i < paths.length; i++) {
36075
- const trigrams = getTrigrams(paths[i]);
36076
- const seen = /* @__PURE__ */ new Set();
36077
- for (const tri of trigrams) {
36078
- if (seen.has(tri)) continue;
36079
- seen.add(tri);
36080
- if (!trigramIndex[tri]) trigramIndex[tri] = [];
36081
- trigramIndex[tri].push(i);
36082
- }
36083
- }
36084
- return trigramIndex;
36453
+ // src/files/index/file-index-sqlite-lock.ts
36454
+ import fs26 from "node:fs";
36455
+ function isSqliteCorruptError(e) {
36456
+ const msg = e instanceof Error ? e.message : String(e);
36457
+ return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
36085
36458
  }
36086
- async function buildTrigramMapForPathsAsync(paths) {
36087
- const trigramIndex = {};
36088
- for (let i = 0; i < paths.length; i++) {
36089
- if (i > 0 && i % INDEX_WORK_YIELD_EVERY === 0) {
36090
- await yieldToEventLoop();
36091
- }
36092
- const trigrams = getTrigrams(paths[i]);
36093
- const seen = /* @__PURE__ */ new Set();
36094
- for (const tri of trigrams) {
36095
- if (seen.has(tri)) continue;
36096
- seen.add(tri);
36097
- if (!trigramIndex[tri]) trigramIndex[tri] = [];
36098
- trigramIndex[tri].push(i);
36459
+ var chain = Promise.resolve();
36460
+ function withFileIndexSqliteLock(fn) {
36461
+ const run = async () => {
36462
+ try {
36463
+ return await Promise.resolve(fn());
36464
+ } catch (e) {
36465
+ if (!isSqliteCorruptError(e)) throw e;
36466
+ closeAllCliSqliteConnections();
36467
+ try {
36468
+ fs26.unlinkSync(getCliSqlitePath());
36469
+ } catch {
36470
+ }
36471
+ chain = Promise.resolve();
36472
+ return await Promise.resolve(fn());
36099
36473
  }
36100
- }
36101
- return trigramIndex;
36102
- }
36103
-
36104
- // src/files/index/write-index-file.ts
36105
- import fs21 from "node:fs";
36106
-
36107
- // src/files/index/paths.ts
36108
- import path23 from "node:path";
36109
- import crypto2 from "node:crypto";
36110
- function getIndexPathForCwd(resolvedCwd) {
36111
- const hash = crypto2.createHash("sha256").update(resolvedCwd).digest("hex").slice(0, INDEX_HASH_LEN);
36112
- return path23.join(INDEX_DIR, `.file-index-${hash}.json`);
36113
- }
36114
-
36115
- // src/files/index/write-index-file.ts
36116
- function writeIndexFileSync(resolvedCwd, data) {
36117
- const indexPath = getIndexPathForCwd(resolvedCwd);
36118
- try {
36119
- if (!fs21.existsSync(INDEX_DIR)) fs21.mkdirSync(INDEX_DIR, { recursive: true });
36120
- fs21.writeFileSync(indexPath, JSON.stringify(data), "utf8");
36121
- } catch (e) {
36122
- console.error(`${INDEX_LOG_PREFIX} Failed to write index:`, e);
36123
- }
36124
- }
36125
- async function writeIndexFileAsync(resolvedCwd, data) {
36126
- const indexPath = getIndexPathForCwd(resolvedCwd);
36127
- try {
36128
- await fs21.promises.mkdir(INDEX_DIR, { recursive: true });
36129
- await fs21.promises.writeFile(indexPath, JSON.stringify(data), "utf8");
36130
- } catch (e) {
36131
- console.error(`${INDEX_LOG_PREFIX} Failed to write index:`, e);
36132
- }
36133
- }
36134
- function makeTrigramIndexData(paths, trigramIndex) {
36135
- return { version: INDEX_VERSION, paths, trigramIndex };
36474
+ };
36475
+ const next = chain.then(run);
36476
+ chain = next.then(
36477
+ () => void 0,
36478
+ () => void 0
36479
+ );
36480
+ return next;
36136
36481
  }
36137
36482
 
36138
36483
  // src/files/index/build-file-index.ts
36139
36484
  function sortPaths(paths) {
36140
36485
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
36141
36486
  }
36142
- function buildFileIndex(cwd) {
36143
- const resolved = path24.resolve(cwd);
36144
- const paths = [];
36145
- walkWorkspaceTreeSync(resolved, resolved, paths);
36146
- sortPaths(paths);
36147
- const trigramIndex = buildTrigramMapForPaths(paths);
36148
- const data = makeTrigramIndexData(paths, trigramIndex);
36149
- writeIndexFileSync(resolved, data);
36150
- return data;
36151
- }
36152
- async function buildFileIndexAsync(cwd) {
36153
- const resolved = path24.resolve(cwd);
36154
- const paths = [];
36155
- await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
36156
- await yieldToEventLoop();
36157
- sortPaths(paths);
36158
- const trigramIndex = await buildTrigramMapForPathsAsync(paths);
36159
- const data = makeTrigramIndexData(paths, trigramIndex);
36160
- await writeIndexFileAsync(resolved, data);
36161
- return data;
36162
- }
36163
-
36164
- // src/files/index/load-file-index.ts
36165
- import fs22 from "node:fs";
36166
- import path25 from "node:path";
36167
- function loadFileIndex(cwd) {
36168
- const resolved = path25.resolve(cwd);
36169
- const indexPath = getIndexPathForCwd(resolved);
36487
+ function persistPathsToSqlite(resolved, paths) {
36488
+ const db = getCliDatabase();
36489
+ const h = getCwdHashForFileIndex(resolved);
36490
+ db.run("BEGIN IMMEDIATE");
36170
36491
  try {
36171
- const raw = fs22.readFileSync(indexPath, "utf8");
36172
- const parsed = JSON.parse(raw);
36173
- if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
36174
- const obj = parsed;
36175
- if (obj.version === INDEX_VERSION && obj.trigramIndex && typeof obj.trigramIndex === "object") {
36176
- return obj;
36492
+ db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
36493
+ const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
36494
+ try {
36495
+ for (const rel of paths) {
36496
+ ins.run([h, rel]);
36177
36497
  }
36178
- return buildFileIndex(resolved);
36498
+ } finally {
36499
+ ins.finalize();
36179
36500
  }
36180
- if (Array.isArray(parsed) && parsed.every((p) => typeof p === "string")) {
36181
- return buildFileIndex(resolved);
36501
+ db.run("COMMIT");
36502
+ } catch (e) {
36503
+ try {
36504
+ db.run("ROLLBACK");
36505
+ } catch {
36182
36506
  }
36183
- return null;
36184
- } catch {
36185
- return null;
36507
+ throw e;
36186
36508
  }
36187
36509
  }
36510
+ async function buildFileIndexAsync(cwd) {
36511
+ return withFileIndexSqliteLock(async () => {
36512
+ const resolved = path29.resolve(cwd);
36513
+ const paths = [];
36514
+ await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
36515
+ await yieldToEventLoop();
36516
+ sortPaths(paths);
36517
+ persistPathsToSqlite(resolved, paths);
36518
+ return { pathCount: paths.length };
36519
+ });
36520
+ }
36188
36521
 
36189
36522
  // src/files/index/ensure-file-index.ts
36190
- import path26 from "node:path";
36191
- async function ensureFileIndexAsync(cwd) {
36192
- const resolved = path26.resolve(cwd);
36193
- const cached2 = loadFileIndex(resolved);
36194
- if (cached2 !== null) return { data: cached2, fromCache: true };
36195
- const data = await buildFileIndexAsync(resolved);
36196
- return { data, fromCache: false };
36197
- }
36523
+ import path30 from "node:path";
36198
36524
 
36199
36525
  // src/files/index/search-file-index.ts
36200
- function candidatePathIndices(index, q) {
36201
- const { paths, trigramIndex } = index;
36202
- if (q.length < 3) {
36203
- return paths.map((_, i) => i).filter((i) => paths[i].toLowerCase().includes(q));
36204
- }
36205
- const trigrams = getTrigrams(q);
36206
- if (trigrams.length === 0) {
36207
- return paths.map((_, i) => i).filter((i) => paths[i].toLowerCase().includes(q));
36208
- }
36209
- const arrays = trigrams.map((tri) => trigramIndex[tri]).filter((arr) => arr != null && arr.length > 0);
36210
- if (arrays.length === 0) return [];
36211
- return intersectSortedTrigramSets(arrays);
36212
- }
36213
- async function searchFileIndexAsync(index, query, limit = 100) {
36214
- await yieldToEventLoop();
36526
+ function escapeLikePattern(fragment) {
36527
+ return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
36528
+ }
36529
+ function bridgeFileIndexIsPopulated(resolvedCwd) {
36530
+ const db = getCliDatabase();
36531
+ const h = getCwdHashForFileIndex(resolvedCwd);
36532
+ const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
36533
+ return row != null;
36534
+ }
36535
+ function bridgeFileIndexPathCount(resolvedCwd) {
36536
+ const db = getCliDatabase();
36537
+ const h = getCwdHashForFileIndex(resolvedCwd);
36538
+ const row = db.get("SELECT COUNT(*) as c FROM file_index_path WHERE cwd_hash = ?", [h]);
36539
+ const c = row?.c ?? 0;
36540
+ return Number(c);
36541
+ }
36542
+ function searchBridgeFilePaths(resolvedCwd, query, limit = 100) {
36215
36543
  const q = query.trim().toLowerCase();
36216
36544
  if (!q) return [];
36217
- const { paths } = index;
36218
- const candidateIndices = candidatePathIndices(index, q);
36219
- const out = [];
36220
- let n = 0;
36221
- for (const i of candidateIndices) {
36222
- if (n > 0 && n % INDEX_WORK_YIELD_EVERY === 0) {
36223
- await yieldToEventLoop();
36224
- }
36225
- const p = paths[i];
36226
- if (p.toLowerCase().includes(q)) {
36227
- out.push(p);
36228
- if (out.length >= limit) break;
36229
- }
36230
- n++;
36231
- }
36545
+ const db = getCliDatabase();
36546
+ const h = getCwdHashForFileIndex(resolvedCwd);
36547
+ const pattern = `%${escapeLikePattern(q)}%`;
36548
+ const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
36549
+ const rows = db.all(
36550
+ `SELECT path FROM file_index_path WHERE cwd_hash = ? AND lower(path) LIKE ? ESCAPE '\\' LIMIT ?`,
36551
+ [h, pattern, lim]
36552
+ );
36553
+ return rows.map((r) => String(r.path));
36554
+ }
36555
+ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
36556
+ await yieldToEventLoop();
36557
+ const out = searchBridgeFilePaths(resolvedCwd, query, limit);
36558
+ if (out.length >= INDEX_WORK_YIELD_EVERY) await yieldToEventLoop();
36232
36559
  return out;
36233
36560
  }
36234
36561
 
36562
+ // src/files/index/ensure-file-index.ts
36563
+ async function ensureFileIndexAsync(cwd) {
36564
+ const resolved = path30.resolve(cwd);
36565
+ if (bridgeFileIndexIsPopulated(resolved)) {
36566
+ return { fromCache: true, pathCount: bridgeFileIndexPathCount(resolved) };
36567
+ }
36568
+ return { ...await buildFileIndexAsync(resolved), fromCache: false };
36569
+ }
36570
+
36235
36571
  // src/files/watch-file-index.ts
36236
36572
  var DEBOUNCE_MS = 900;
36237
36573
  function shouldIgnoreRelative(rel) {
@@ -36272,7 +36608,7 @@ function createFsWatcher(resolved, schedule) {
36272
36608
  }
36273
36609
  }
36274
36610
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
36275
- const resolved = path27.resolve(cwd);
36611
+ const resolved = path31.resolve(cwd);
36276
36612
  void buildFileIndexAsync(resolved).catch((e) => {
36277
36613
  console.error("[file-index] Initial index build failed:", e);
36278
36614
  });
@@ -36300,7 +36636,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
36300
36636
  }
36301
36637
 
36302
36638
  // src/connection/create-bridge-connection.ts
36303
- import * as path35 from "node:path";
36639
+ import * as path41 from "node:path";
36304
36640
 
36305
36641
  // src/dev-servers/manager/dev-server-manager.ts
36306
36642
  import { rm as rm2 } from "node:fs/promises";
@@ -36322,15 +36658,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
36322
36658
 
36323
36659
  // src/dev-servers/process/terminate-child-process.ts
36324
36660
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
36325
- const exited = new Promise((resolve18) => {
36326
- proc.once("exit", () => resolve18());
36661
+ const exited = new Promise((resolve20) => {
36662
+ proc.once("exit", () => resolve20());
36327
36663
  });
36328
36664
  log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
36329
36665
  try {
36330
36666
  proc.kill("SIGTERM");
36331
36667
  } catch {
36332
36668
  }
36333
- await Promise.race([exited, new Promise((resolve18) => setTimeout(resolve18, graceMs))]);
36669
+ await Promise.race([exited, new Promise((resolve20) => setTimeout(resolve20, graceMs))]);
36334
36670
  }
36335
36671
  function forceKillChild(proc, log2, shortId, graceMs) {
36336
36672
  log2(
@@ -36344,7 +36680,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
36344
36680
  }
36345
36681
 
36346
36682
  // src/dev-servers/process/wire-dev-server-child-process.ts
36347
- import fs23 from "node:fs";
36683
+ import fs27 from "node:fs";
36348
36684
 
36349
36685
  // src/dev-servers/manager/forward-pipe.ts
36350
36686
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -36380,7 +36716,7 @@ function wireDevServerChildProcess(d) {
36380
36716
  d.setPollInterval(void 0);
36381
36717
  return;
36382
36718
  }
36383
- fs23.readFile(d.mergedLogPath, (err, buf) => {
36719
+ fs27.readFile(d.mergedLogPath, (err, buf) => {
36384
36720
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
36385
36721
  if (buf.length <= d.mergedReadPos.value) return;
36386
36722
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -36418,7 +36754,7 @@ ${errTail}` : ""}`);
36418
36754
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
36419
36755
  };
36420
36756
  if (mergedPath) {
36421
- fs23.readFile(mergedPath, (err, buf) => {
36757
+ fs27.readFile(mergedPath, (err, buf) => {
36422
36758
  if (!err && buf.length > d.mergedReadPos.value) {
36423
36759
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
36424
36760
  if (chunk.length > 0) {
@@ -36520,13 +36856,13 @@ function parseDevServerDefs(servers) {
36520
36856
  }
36521
36857
 
36522
36858
  // src/dev-servers/manager/shell-spawn/utils.ts
36523
- import fs24 from "node:fs";
36859
+ import fs28 from "node:fs";
36524
36860
  function isSpawnEbadf(e) {
36525
36861
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
36526
36862
  }
36527
36863
  function rmDirQuiet(dir) {
36528
36864
  try {
36529
- fs24.rmSync(dir, { recursive: true, force: true });
36865
+ fs28.rmSync(dir, { recursive: true, force: true });
36530
36866
  } catch {
36531
36867
  }
36532
36868
  }
@@ -36534,7 +36870,7 @@ var cachedDevNullReadFd;
36534
36870
  function devNullReadFd() {
36535
36871
  if (cachedDevNullReadFd === void 0) {
36536
36872
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
36537
- cachedDevNullReadFd = fs24.openSync(devPath, "r");
36873
+ cachedDevNullReadFd = fs28.openSync(devPath, "r");
36538
36874
  }
36539
36875
  return cachedDevNullReadFd;
36540
36876
  }
@@ -36608,15 +36944,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
36608
36944
 
36609
36945
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
36610
36946
  import { spawn as spawn6 } from "node:child_process";
36611
- import fs25 from "node:fs";
36947
+ import fs29 from "node:fs";
36612
36948
  import { tmpdir } from "node:os";
36613
- import path28 from "node:path";
36949
+ import path32 from "node:path";
36614
36950
  function trySpawnMergedLogFile(command, env, cwd, signal) {
36615
- const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir(), "ba-devsrv-log-"));
36616
- const logPath = path28.join(tmpRoot, "combined.log");
36951
+ const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir(), "ba-devsrv-log-"));
36952
+ const logPath = path32.join(tmpRoot, "combined.log");
36617
36953
  let logFd;
36618
36954
  try {
36619
- logFd = fs25.openSync(logPath, "a");
36955
+ logFd = fs29.openSync(logPath, "a");
36620
36956
  } catch {
36621
36957
  rmDirQuiet(tmpRoot);
36622
36958
  return null;
@@ -36635,7 +36971,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
36635
36971
  } else {
36636
36972
  proc = spawn6("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
36637
36973
  }
36638
- fs25.closeSync(logFd);
36974
+ fs29.closeSync(logFd);
36639
36975
  return {
36640
36976
  proc,
36641
36977
  pipedStdoutStderr: true,
@@ -36644,7 +36980,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
36644
36980
  };
36645
36981
  } catch (e) {
36646
36982
  try {
36647
- fs25.closeSync(logFd);
36983
+ fs29.closeSync(logFd);
36648
36984
  } catch {
36649
36985
  }
36650
36986
  rmDirQuiet(tmpRoot);
@@ -36655,22 +36991,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
36655
36991
 
36656
36992
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
36657
36993
  import { spawn as spawn7 } from "node:child_process";
36658
- import fs26 from "node:fs";
36994
+ import fs30 from "node:fs";
36659
36995
  import { tmpdir as tmpdir2 } from "node:os";
36660
- import path29 from "node:path";
36996
+ import path33 from "node:path";
36661
36997
  function shSingleQuote(s) {
36662
36998
  return `'${s.replace(/'/g, `'\\''`)}'`;
36663
36999
  }
36664
37000
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
36665
- const tmpRoot = fs26.mkdtempSync(path29.join(tmpdir2(), "ba-devsrv-sh-"));
36666
- const logPath = path29.join(tmpRoot, "combined.log");
36667
- const innerPath = path29.join(tmpRoot, "_cmd.sh");
36668
- const runnerPath = path29.join(tmpRoot, "_run.sh");
37001
+ const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
37002
+ const logPath = path33.join(tmpRoot, "combined.log");
37003
+ const innerPath = path33.join(tmpRoot, "_cmd.sh");
37004
+ const runnerPath = path33.join(tmpRoot, "_run.sh");
36669
37005
  try {
36670
- fs26.writeFileSync(innerPath, `#!/bin/sh
37006
+ fs30.writeFileSync(innerPath, `#!/bin/sh
36671
37007
  ${command}
36672
37008
  `);
36673
- fs26.writeFileSync(
37009
+ fs30.writeFileSync(
36674
37010
  runnerPath,
36675
37011
  `#!/bin/sh
36676
37012
  cd ${shSingleQuote(cwd)}
@@ -36696,13 +37032,13 @@ cd ${shSingleQuote(cwd)}
36696
37032
  }
36697
37033
  }
36698
37034
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
36699
- const tmpRoot = fs26.mkdtempSync(path29.join(tmpdir2(), "ba-devsrv-sh-"));
36700
- const logPath = path29.join(tmpRoot, "combined.log");
36701
- const runnerPath = path29.join(tmpRoot, "_run.bat");
37035
+ const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
37036
+ const logPath = path33.join(tmpRoot, "combined.log");
37037
+ const runnerPath = path33.join(tmpRoot, "_run.bat");
36702
37038
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
36703
37039
  const com = process.env.ComSpec || "cmd.exe";
36704
37040
  try {
36705
- fs26.writeFileSync(
37041
+ fs30.writeFileSync(
36706
37042
  runnerPath,
36707
37043
  `@ECHO OFF\r
36708
37044
  CD /D ${q(cwd)}\r
@@ -37213,7 +37549,7 @@ async function proxyToLocal(request) {
37213
37549
  };
37214
37550
  const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
37215
37551
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
37216
- const once = await new Promise((resolve18) => {
37552
+ const once = await new Promise((resolve20) => {
37217
37553
  const req = mod.request(opts, (res) => {
37218
37554
  const chunks = [];
37219
37555
  res.on("data", (c) => chunks.push(c));
@@ -37224,7 +37560,7 @@ async function proxyToLocal(request) {
37224
37560
  if (typeof v === "string") headers[k] = v;
37225
37561
  else if (Array.isArray(v) && v[0]) headers[k] = v[0];
37226
37562
  }
37227
- resolve18({
37563
+ resolve20({
37228
37564
  id: request.id,
37229
37565
  statusCode: res.statusCode ?? 0,
37230
37566
  headers,
@@ -37233,7 +37569,7 @@ async function proxyToLocal(request) {
37233
37569
  });
37234
37570
  });
37235
37571
  req.on("error", (err) => {
37236
- resolve18({
37572
+ resolve20({
37237
37573
  id: request.id,
37238
37574
  statusCode: 0,
37239
37575
  headers: {},
@@ -37639,30 +37975,30 @@ function createOnBridgeIdentified(opts) {
37639
37975
  }
37640
37976
 
37641
37977
  // src/skills/discover-local-agent-skills.ts
37642
- import fs27 from "node:fs";
37643
- import path30 from "node:path";
37978
+ import fs31 from "node:fs";
37979
+ import path34 from "node:path";
37644
37980
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
37645
37981
  function discoverLocalSkills(cwd) {
37646
37982
  const out = [];
37647
37983
  const seenKeys = /* @__PURE__ */ new Set();
37648
37984
  for (const rel of SKILL_DISCOVERY_ROOTS) {
37649
- const base = path30.join(cwd, rel);
37650
- if (!fs27.existsSync(base) || !fs27.statSync(base).isDirectory()) continue;
37985
+ const base = path34.join(cwd, rel);
37986
+ if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
37651
37987
  let entries = [];
37652
37988
  try {
37653
- entries = fs27.readdirSync(base);
37989
+ entries = fs31.readdirSync(base);
37654
37990
  } catch {
37655
37991
  continue;
37656
37992
  }
37657
37993
  for (const name of entries) {
37658
- const dir = path30.join(base, name);
37994
+ const dir = path34.join(base, name);
37659
37995
  try {
37660
- if (!fs27.statSync(dir).isDirectory()) continue;
37996
+ if (!fs31.statSync(dir).isDirectory()) continue;
37661
37997
  } catch {
37662
37998
  continue;
37663
37999
  }
37664
- const skillMd = path30.join(dir, "SKILL.md");
37665
- if (!fs27.existsSync(skillMd)) continue;
38000
+ const skillMd = path34.join(dir, "SKILL.md");
38001
+ if (!fs31.existsSync(skillMd)) continue;
37666
38002
  const key = `${rel}/${name}`;
37667
38003
  if (seenKeys.has(key)) continue;
37668
38004
  seenKeys.add(key);
@@ -37674,23 +38010,23 @@ function discoverLocalSkills(cwd) {
37674
38010
  function discoverSkillLayoutRoots(cwd) {
37675
38011
  const roots = [];
37676
38012
  for (const rel of SKILL_DISCOVERY_ROOTS) {
37677
- const base = path30.join(cwd, rel);
37678
- if (!fs27.existsSync(base) || !fs27.statSync(base).isDirectory()) continue;
38013
+ const base = path34.join(cwd, rel);
38014
+ if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
37679
38015
  let entries = [];
37680
38016
  try {
37681
- entries = fs27.readdirSync(base);
38017
+ entries = fs31.readdirSync(base);
37682
38018
  } catch {
37683
38019
  continue;
37684
38020
  }
37685
38021
  const skills2 = [];
37686
38022
  for (const name of entries) {
37687
- const dir = path30.join(base, name);
38023
+ const dir = path34.join(base, name);
37688
38024
  try {
37689
- if (!fs27.statSync(dir).isDirectory()) continue;
38025
+ if (!fs31.statSync(dir).isDirectory()) continue;
37690
38026
  } catch {
37691
38027
  continue;
37692
38028
  }
37693
- if (!fs27.existsSync(path30.join(dir, "SKILL.md"))) continue;
38029
+ if (!fs31.existsSync(path34.join(dir, "SKILL.md"))) continue;
37694
38030
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
37695
38031
  skills2.push({ name, relPath });
37696
38032
  }
@@ -37852,6 +38188,13 @@ var handleBridgeIdentified = (msg, deps) => {
37852
38188
  `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
37853
38189
  );
37854
38190
  }
38191
+ try {
38192
+ await deps.warmupAgentCapabilitiesOnConnect?.();
38193
+ } catch (e) {
38194
+ deps.log(
38195
+ `[Bridge service] Agent capability warmup failed: ${e instanceof Error ? e.message : String(e)}`
38196
+ );
38197
+ }
37855
38198
  })();
37856
38199
  });
37857
38200
  setImmediate(() => {
@@ -37884,7 +38227,7 @@ var handleAgentConfigMessage = (msg, deps) => {
37884
38227
  };
37885
38228
 
37886
38229
  // src/prompt-turn-queue/runner.ts
37887
- import fs30 from "node:fs";
38230
+ import fs32 from "node:fs";
37888
38231
 
37889
38232
  // src/prompt-turn-queue/client-report.ts
37890
38233
  function sendPromptQueueClientReport(ws, queues) {
@@ -37893,32 +38236,6 @@ function sendPromptQueueClientReport(ws, queues) {
37893
38236
  return true;
37894
38237
  }
37895
38238
 
37896
- // src/prompt-turn-queue/disk-store.ts
37897
- import fs29 from "node:fs";
37898
-
37899
- // src/prompt-turn-queue/paths.ts
37900
- import crypto3 from "node:crypto";
37901
- import fs28 from "node:fs";
37902
- import path31 from "node:path";
37903
- import os7 from "node:os";
37904
- var QUEUE_KEY_HEX_64 = /^[a-f0-9]{64}$/i;
37905
- function queueStateFileSlug(queueKey) {
37906
- if (QUEUE_KEY_HEX_64.test(queueKey)) return queueKey.toLowerCase();
37907
- return crypto3.createHash("sha256").update(queueKey, "utf8").digest("hex");
37908
- }
37909
- function getPromptQueuesDirectory() {
37910
- const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
37911
- if (override) return path31.resolve(override);
37912
- return path31.join(os7.homedir(), ".buildautomaton", "queues");
37913
- }
37914
- function ensurePromptQueuesDirectory() {
37915
- const dir = getPromptQueuesDirectory();
37916
- if (!fs28.existsSync(dir)) fs28.mkdirSync(dir, { recursive: true });
37917
- }
37918
- function queueStateFilePath(queueKey) {
37919
- return path31.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
37920
- }
37921
-
37922
38239
  // src/prompt-turn-queue/disk-store.ts
37923
38240
  var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
37924
38241
  "queued",
@@ -37928,28 +38245,27 @@ var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
37928
38245
  "stopping",
37929
38246
  "discarded"
37930
38247
  ]);
37931
- function parsePersistedQueueFile(raw) {
37932
- try {
37933
- const o = JSON.parse(raw);
37934
- const queueKey = typeof o.queueKey === "string" ? o.queueKey : typeof o.queueKeyHash === "string" ? o.queueKeyHash : null;
37935
- if (!queueKey || typeof o.updatedAt !== "string" || !Array.isArray(o.turns)) return null;
37936
- return { queueKey, updatedAt: o.updatedAt, turns: o.turns };
37937
- } catch {
37938
- return null;
37939
- }
37940
- }
37941
38248
  function readPersistedQueue(queueKey) {
37942
- const p = queueStateFilePath(queueKey);
38249
+ const db = getCliDatabase();
38250
+ const row = db.get("SELECT queue_key, updated_at, turns_json FROM prompt_queue WHERE queue_key = ?", [
38251
+ queueKey
38252
+ ]);
38253
+ if (!row) return null;
37943
38254
  try {
37944
- return parsePersistedQueueFile(fs29.readFileSync(p, "utf8"));
38255
+ const turns = JSON.parse(row.turns_json);
38256
+ if (!Array.isArray(turns)) return null;
38257
+ return { queueKey: row.queue_key, updatedAt: row.updated_at, turns };
37945
38258
  } catch {
37946
38259
  return null;
37947
38260
  }
37948
38261
  }
37949
38262
  function writePersistedQueue(file2) {
37950
- ensurePromptQueuesDirectory();
37951
- const p = queueStateFilePath(file2.queueKey);
37952
- fs29.writeFileSync(p, JSON.stringify(file2, null, 2), "utf8");
38263
+ const db = getCliDatabase();
38264
+ db.run(
38265
+ `INSERT INTO prompt_queue (queue_key, updated_at, turns_json) VALUES (?, ?, ?)
38266
+ ON CONFLICT(queue_key) DO UPDATE SET updated_at = excluded.updated_at, turns_json = excluded.turns_json`,
38267
+ [file2.queueKey, file2.updatedAt, JSON.stringify(file2.turns)]
38268
+ );
37953
38269
  }
37954
38270
  function mergeServerQueueSnapshot(queueKey, serverTurns) {
37955
38271
  const prev = readPersistedQueue(queueKey);
@@ -38007,7 +38323,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
38007
38323
  const tid = typeof pl.snapshotRevertTurnId === "string" && pl.snapshotRevertTurnId.trim() !== "" ? pl.snapshotRevertTurnId.trim() : next.turnId;
38008
38324
  const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
38009
38325
  const file2 = snapshotFilePath(agentBase, tid);
38010
- if (!fs30.existsSync(file2)) {
38326
+ if (!fs32.existsSync(file2)) {
38011
38327
  deps.log(
38012
38328
  `[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
38013
38329
  );
@@ -38231,9 +38547,9 @@ function parseChangeSummarySnapshots(raw) {
38231
38547
  for (const item of raw) {
38232
38548
  if (!item || typeof item !== "object") continue;
38233
38549
  const o = item;
38234
- const path37 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
38235
- if (!path37) continue;
38236
- const row = { path: path37 };
38550
+ const path43 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
38551
+ if (!path43) continue;
38552
+ const row = { path: path43 };
38237
38553
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
38238
38554
  if (typeof o.oldText === "string") row.oldText = o.oldText;
38239
38555
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -38351,6 +38667,8 @@ function handleBridgePrompt(msg, deps) {
38351
38667
  const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : rawParent === "session_worktrees_root" ? "worktrees_root" : null;
38352
38668
  const sessionParentPath = typeof msg.sessionParentPath === "string" && msg.sessionParentPath.trim() ? msg.sessionParentPath.trim() : null;
38353
38669
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
38670
+ const rawAgentId = msg.agentId;
38671
+ const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
38354
38672
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
38355
38673
  const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
38356
38674
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
@@ -38388,6 +38706,7 @@ function handleBridgePrompt(msg, deps) {
38388
38706
  runId,
38389
38707
  mode,
38390
38708
  agentType,
38709
+ agentId,
38391
38710
  agentConfig,
38392
38711
  sessionParentPath: effectiveCwd,
38393
38712
  sendResult: sendResult2,
@@ -38450,8 +38769,8 @@ function randomSecret() {
38450
38769
  }
38451
38770
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
38452
38771
  }
38453
- async function requestPreviewApi(port, secret, method, path37, body) {
38454
- const url2 = `http://127.0.0.1:${port}${path37}`;
38772
+ async function requestPreviewApi(port, secret, method, path43, body) {
38773
+ const url2 = `http://127.0.0.1:${port}${path43}`;
38455
38774
  const headers = {
38456
38775
  [PREVIEW_SECRET_HEADER]: secret,
38457
38776
  "Content-Type": "application/json"
@@ -38463,7 +38782,7 @@ async function requestPreviewApi(port, secret, method, path37, body) {
38463
38782
  });
38464
38783
  const data = await res.json().catch(() => ({}));
38465
38784
  if (!res.ok) {
38466
- throw new Error(data?.error ?? `Preview API ${method} ${path37}: ${res.status}`);
38785
+ throw new Error(data?.error ?? `Preview API ${method} ${path43}: ${res.status}`);
38467
38786
  }
38468
38787
  return data;
38469
38788
  }
@@ -38628,15 +38947,15 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
38628
38947
  };
38629
38948
 
38630
38949
  // src/files/list-dir.ts
38631
- import fs31 from "node:fs";
38632
- import path33 from "node:path";
38950
+ import fs33 from "node:fs";
38951
+ import path36 from "node:path";
38633
38952
 
38634
38953
  // src/files/ensure-under-cwd.ts
38635
- import path32 from "node:path";
38954
+ import path35 from "node:path";
38636
38955
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
38637
- const normalized = path32.normalize(relativePath).replace(/^(\.\/)+/, "");
38638
- const resolved = path32.resolve(cwd, normalized);
38639
- if (!resolved.startsWith(cwd + path32.sep) && resolved !== cwd) {
38956
+ const normalized = path35.normalize(relativePath).replace(/^(\.\/)+/, "");
38957
+ const resolved = path35.resolve(cwd, normalized);
38958
+ if (!resolved.startsWith(cwd + path35.sep) && resolved !== cwd) {
38640
38959
  return null;
38641
38960
  }
38642
38961
  return resolved;
@@ -38650,7 +38969,7 @@ async function listDirAsync(relativePath) {
38650
38969
  return { error: "Path is outside working directory" };
38651
38970
  }
38652
38971
  try {
38653
- const names = await fs31.promises.readdir(resolved, { withFileTypes: true });
38972
+ const names = await fs33.promises.readdir(resolved, { withFileTypes: true });
38654
38973
  const visible = names.filter((d) => !d.name.startsWith("."));
38655
38974
  const entries = [];
38656
38975
  for (let i = 0; i < visible.length; i++) {
@@ -38658,12 +38977,12 @@ async function listDirAsync(relativePath) {
38658
38977
  await yieldToEventLoop();
38659
38978
  }
38660
38979
  const d = visible[i];
38661
- const entryPath = path33.join(relativePath || ".", d.name).replace(/\\/g, "/");
38662
- const fullPath = path33.join(resolved, d.name);
38980
+ const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
38981
+ const fullPath = path36.join(resolved, d.name);
38663
38982
  let isDir = d.isDirectory();
38664
38983
  if (d.isSymbolicLink()) {
38665
38984
  try {
38666
- const targetStat = await fs31.promises.stat(fullPath);
38985
+ const targetStat = await fs33.promises.stat(fullPath);
38667
38986
  isDir = targetStat.isDirectory();
38668
38987
  } catch {
38669
38988
  isDir = false;
@@ -38688,25 +39007,25 @@ async function listDirAsync(relativePath) {
38688
39007
  }
38689
39008
 
38690
39009
  // src/files/read-file.ts
38691
- import fs32 from "node:fs";
39010
+ import fs34 from "node:fs";
38692
39011
  import { StringDecoder } from "node:string_decoder";
38693
39012
  function resolveFilePath(relativePath) {
38694
39013
  const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
38695
39014
  if (!resolved) return { error: "Path is outside working directory" };
38696
39015
  let real;
38697
39016
  try {
38698
- real = fs32.realpathSync(resolved);
39017
+ real = fs34.realpathSync(resolved);
38699
39018
  } catch {
38700
39019
  real = resolved;
38701
39020
  }
38702
- const stat3 = fs32.statSync(real);
39021
+ const stat3 = fs34.statSync(real);
38703
39022
  if (!stat3.isFile()) return { error: "Not a file" };
38704
39023
  return real;
38705
39024
  }
38706
39025
  var LINE_CHUNK_SIZE = 64 * 1024;
38707
39026
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
38708
- const fileSize = fs32.statSync(filePath).size;
38709
- const fd = fs32.openSync(filePath, "r");
39027
+ const fileSize = fs34.statSync(filePath).size;
39028
+ const fd = fs34.openSync(filePath, "r");
38710
39029
  const bufSize = 64 * 1024;
38711
39030
  const buf = Buffer.alloc(bufSize);
38712
39031
  const decoder = new StringDecoder("utf8");
@@ -38719,7 +39038,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
38719
39038
  let line0Accum = "";
38720
39039
  try {
38721
39040
  let bytesRead;
38722
- while (!done && (bytesRead = fs32.readSync(fd, buf, 0, bufSize, null)) > 0) {
39041
+ while (!done && (bytesRead = fs34.readSync(fd, buf, 0, bufSize, null)) > 0) {
38723
39042
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
38724
39043
  partial2 = "";
38725
39044
  let lineStart = 0;
@@ -38854,7 +39173,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
38854
39173
  }
38855
39174
  return { content: resultLines.join("\n"), size: fileSize };
38856
39175
  } finally {
38857
- fs32.closeSync(fd);
39176
+ fs34.closeSync(fd);
38858
39177
  }
38859
39178
  }
38860
39179
  function readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
@@ -38865,8 +39184,8 @@ function readFile3(relativePath, startLine, endLine, lineOffset, lineChunkSize =
38865
39184
  if (hasRange) {
38866
39185
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
38867
39186
  }
38868
- const stat3 = fs32.statSync(result);
38869
- const raw = fs32.readFileSync(result, "utf8");
39187
+ const stat3 = fs34.statSync(result);
39188
+ const raw = fs34.readFileSync(result, "utf8");
38870
39189
  const lines = raw.split(/\r?\n/);
38871
39190
  return { content: raw, totalLines: lines.length, size: stat3.size };
38872
39191
  } catch (err) {
@@ -38879,14 +39198,14 @@ async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineC
38879
39198
  }
38880
39199
 
38881
39200
  // src/files/handle-file-browser-search.ts
39201
+ import path37 from "node:path";
38882
39202
  var SEARCH_LIMIT = 100;
38883
39203
  function handleFileBrowserSearch(msg, socket, e2ee) {
38884
39204
  void (async () => {
38885
39205
  await yieldToEventLoop();
38886
39206
  const q = typeof msg.q === "string" ? msg.q : "";
38887
- const cwd = getBridgeRoot();
38888
- const index = loadFileIndex(cwd);
38889
- if (index === null) {
39207
+ const cwd = path37.resolve(getBridgeRoot());
39208
+ if (!bridgeFileIndexIsPopulated(cwd)) {
38890
39209
  const payload2 = {
38891
39210
  type: "file_browser_search_response",
38892
39211
  id: msg.id,
@@ -38896,7 +39215,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
38896
39215
  sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
38897
39216
  return;
38898
39217
  }
38899
- const results = await searchFileIndexAsync(index, q, SEARCH_LIMIT);
39218
+ const results = await searchBridgeFilePathsAsync(cwd, q, SEARCH_LIMIT);
38900
39219
  const payload = {
38901
39220
  type: "file_browser_search_response",
38902
39221
  id: msg.id,
@@ -38984,8 +39303,8 @@ function handleSkillLayoutRequest(msg, deps) {
38984
39303
  }
38985
39304
 
38986
39305
  // src/skills/install-remote-skills.ts
38987
- import fs33 from "node:fs";
38988
- import path34 from "node:path";
39306
+ import fs35 from "node:fs";
39307
+ import path38 from "node:path";
38989
39308
  function installRemoteSkills(cwd, targetDir, items) {
38990
39309
  const installed2 = [];
38991
39310
  if (!Array.isArray(items)) {
@@ -38996,15 +39315,15 @@ function installRemoteSkills(cwd, targetDir, items) {
38996
39315
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
38997
39316
  continue;
38998
39317
  }
38999
- const skillDir = path34.join(cwd, targetDir, item.skillName);
39318
+ const skillDir = path38.join(cwd, targetDir, item.skillName);
39000
39319
  for (const f of item.files) {
39001
39320
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
39002
- const dest = path34.join(skillDir, f.path);
39003
- fs33.mkdirSync(path34.dirname(dest), { recursive: true });
39321
+ const dest = path38.join(skillDir, f.path);
39322
+ fs35.mkdirSync(path38.dirname(dest), { recursive: true });
39004
39323
  if (f.text !== void 0) {
39005
- fs33.writeFileSync(dest, f.text, "utf8");
39324
+ fs35.writeFileSync(dest, f.text, "utf8");
39006
39325
  } else if (f.base64) {
39007
- fs33.writeFileSync(dest, Buffer.from(f.base64, "base64"));
39326
+ fs35.writeFileSync(dest, Buffer.from(f.base64, "base64"));
39008
39327
  }
39009
39328
  }
39010
39329
  installed2.push({
@@ -39154,7 +39473,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
39154
39473
  };
39155
39474
 
39156
39475
  // src/routing/handlers/revert-turn-snapshot.ts
39157
- import * as fs34 from "node:fs";
39476
+ import * as fs36 from "node:fs";
39158
39477
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
39159
39478
  const id = typeof msg.id === "string" ? msg.id : "";
39160
39479
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -39166,7 +39485,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
39166
39485
  if (!s) return;
39167
39486
  const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
39168
39487
  const file2 = snapshotFilePath(agentBase, turnId);
39169
- if (!fs34.existsSync(file2)) {
39488
+ if (!fs36.existsSync(file2)) {
39170
39489
  sendWsMessage(s, {
39171
39490
  type: "revert_turn_snapshot_result",
39172
39491
  id,
@@ -39577,11 +39896,248 @@ function createBridgeHeartbeatController(params) {
39577
39896
  };
39578
39897
  }
39579
39898
 
39899
+ // src/sqlite/hash-json-sha256.ts
39900
+ import { createHash as createHash2 } from "node:crypto";
39901
+ function hashJsonUtf8Sha256(value) {
39902
+ return createHash2("sha256").update(JSON.stringify(value), "utf8").digest("hex");
39903
+ }
39904
+
39905
+ // src/sqlite/agent-capability-cache.ts
39906
+ function hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType) {
39907
+ const t = agentType.trim();
39908
+ if (!t) return false;
39909
+ try {
39910
+ const row = db.get(
39911
+ `SELECT config_options_json FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
39912
+ [workspaceId, t]
39913
+ );
39914
+ if (row?.config_options_json == null || row.config_options_json === "") return false;
39915
+ const parsed = JSON.parse(row.config_options_json);
39916
+ return Array.isArray(parsed) && parsed.length > 0;
39917
+ } catch {
39918
+ return false;
39919
+ }
39920
+ }
39921
+ function upsertCliAgentCapabilityCache(db, row) {
39922
+ const t = row.agentType.trim();
39923
+ if (!t) return false;
39924
+ const hash = hashJsonUtf8Sha256(row.configOptions);
39925
+ try {
39926
+ const prev = db.get(
39927
+ `SELECT content_hash FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
39928
+ [row.workspaceId, t]
39929
+ );
39930
+ if (prev?.content_hash === hash) return false;
39931
+ } catch {
39932
+ }
39933
+ const json2 = JSON.stringify(row.configOptions);
39934
+ const now = (/* @__PURE__ */ new Date()).toISOString();
39935
+ db.run(
39936
+ `INSERT INTO agent_capabilities (workspace_id, agent_type, config_options_json, content_hash, updated_at)
39937
+ VALUES (?, ?, ?, ?, ?)
39938
+ ON CONFLICT(workspace_id, agent_type) DO UPDATE SET
39939
+ config_options_json = excluded.config_options_json,
39940
+ content_hash = excluded.content_hash,
39941
+ updated_at = excluded.updated_at`,
39942
+ [row.workspaceId, t, json2, hash, now]
39943
+ );
39944
+ return true;
39945
+ }
39946
+ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
39947
+ const rows = db.all(
39948
+ `SELECT agent_type, config_options_json FROM agent_capabilities WHERE workspace_id = ?`,
39949
+ [workspaceId]
39950
+ );
39951
+ const out = [];
39952
+ for (const r of rows) {
39953
+ try {
39954
+ const parsed = JSON.parse(r.config_options_json);
39955
+ if (!Array.isArray(parsed) || parsed.length === 0) continue;
39956
+ out.push({ agentType: r.agent_type, configOptions: parsed });
39957
+ } catch {
39958
+ }
39959
+ }
39960
+ return out;
39961
+ }
39962
+
39963
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
39964
+ import * as path40 from "node:path";
39965
+
39966
+ // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
39967
+ import * as path39 from "node:path";
39968
+ async function probeOneAgentTypeForCapabilities(params) {
39969
+ const { agentType, cwd, workspaceId, log: log2, getDb, reportAgentCapabilities, bridgeReport = true } = params;
39970
+ const resolved = resolveAgentCommand(agentType);
39971
+ if (!resolved) return false;
39972
+ let sqliteChanged = false;
39973
+ const reportedRef = { done: false };
39974
+ const tryReport = (co) => {
39975
+ if (reportedRef.done) return;
39976
+ if (!Array.isArray(co) || co.length === 0) return;
39977
+ reportedRef.done = true;
39978
+ let changed = false;
39979
+ try {
39980
+ changed = upsertCliAgentCapabilityCache(getDb(), {
39981
+ workspaceId,
39982
+ agentType,
39983
+ configOptions: co
39984
+ });
39985
+ } catch {
39986
+ }
39987
+ sqliteChanged ||= changed;
39988
+ if (bridgeReport && changed) {
39989
+ reportAgentCapabilities?.({ agentType, configOptions: co });
39990
+ }
39991
+ };
39992
+ let handle = null;
39993
+ const killTimer = setTimeout(() => {
39994
+ try {
39995
+ handle?.disconnect();
39996
+ } catch {
39997
+ }
39998
+ }, 28e3);
39999
+ killTimer.unref?.();
40000
+ try {
40001
+ handle = await resolved.createClient({
40002
+ command: resolved.command,
40003
+ cwd: path39.resolve(cwd),
40004
+ backendAgentType: agentType,
40005
+ sessionMode: "agent",
40006
+ persistedAcpSessionId: null,
40007
+ agentConfig: null,
40008
+ getActiveConfigOptions: () => null,
40009
+ onAcpSessionEstablished: (info) => {
40010
+ tryReport(info.configOptions ?? null);
40011
+ },
40012
+ onAcpConfigOptionsUpdated: (co) => {
40013
+ tryReport(co);
40014
+ },
40015
+ onAgentSubprocessExit: () => {
40016
+ },
40017
+ onSessionUpdate: () => {
40018
+ }
40019
+ });
40020
+ await new Promise((r) => setTimeout(r, 1200));
40021
+ } catch (e) {
40022
+ log2(
40023
+ `[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
40024
+ );
40025
+ } finally {
40026
+ clearTimeout(killTimer);
40027
+ try {
40028
+ handle?.disconnect();
40029
+ } catch {
40030
+ }
40031
+ }
40032
+ return sqliteChanged;
40033
+ }
40034
+
40035
+ // src/agents/capabilities/probe-agent-capabilities-for-types.ts
40036
+ async function probeAgentCapabilitiesForDetectedTypes(params) {
40037
+ const {
40038
+ agentTypes,
40039
+ cwd,
40040
+ workspaceId,
40041
+ log: log2,
40042
+ getDb,
40043
+ reportAgentCapabilities,
40044
+ bridgeReport = true,
40045
+ forceAllTypes = false
40046
+ } = params;
40047
+ let changedCount = 0;
40048
+ for (let i = 0; i < agentTypes.length; i++) {
40049
+ if (i > 0) await yieldToEventLoop();
40050
+ const agentType = agentTypes[i];
40051
+ if (!agentType.trim()) continue;
40052
+ if (!forceAllTypes) {
40053
+ try {
40054
+ if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" && hasNonEmptyAgentCapabilityCache(getDb(), workspaceId, agentType)) {
40055
+ continue;
40056
+ }
40057
+ } catch {
40058
+ }
40059
+ }
40060
+ const changed = await probeOneAgentTypeForCapabilities({
40061
+ agentType,
40062
+ cwd,
40063
+ workspaceId,
40064
+ log: log2,
40065
+ getDb,
40066
+ reportAgentCapabilities,
40067
+ bridgeReport
40068
+ });
40069
+ if (changed) changedCount += 1;
40070
+ }
40071
+ return changedCount;
40072
+ }
40073
+
40074
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
40075
+ async function warmupAgentCapabilitiesOnConnect(params) {
40076
+ const { workspaceId, log: log2, getDb, getWs } = params;
40077
+ const cwd = path40.resolve(getBridgeRoot());
40078
+ const db = getDb();
40079
+ function sendBatchFromCache() {
40080
+ const socket = getWs();
40081
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
40082
+ try {
40083
+ const rows = listCliAgentCapabilityCacheForWorkspace(db, workspaceId);
40084
+ if (rows.length === 0) return;
40085
+ sendWsMessage(socket, {
40086
+ type: "agent_capabilities_batch",
40087
+ items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
40088
+ });
40089
+ } catch (e) {
40090
+ log2(
40091
+ `[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
40092
+ );
40093
+ }
40094
+ }
40095
+ sendBatchFromCache();
40096
+ let types = [];
40097
+ try {
40098
+ types = [...await detectLocalAgentTypes()];
40099
+ } catch (e) {
40100
+ log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
40101
+ }
40102
+ try {
40103
+ const n = await probeAgentCapabilitiesForDetectedTypes({
40104
+ agentTypes: types,
40105
+ cwd,
40106
+ workspaceId,
40107
+ log: log2,
40108
+ getDb,
40109
+ bridgeReport: false,
40110
+ forceAllTypes: false
40111
+ });
40112
+ if (n > 0) sendBatchFromCache();
40113
+ } catch (e) {
40114
+ log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
40115
+ }
40116
+ void (async () => {
40117
+ try {
40118
+ await yieldToEventLoop();
40119
+ const n = await probeAgentCapabilitiesForDetectedTypes({
40120
+ agentTypes: types,
40121
+ cwd,
40122
+ workspaceId,
40123
+ log: log2,
40124
+ getDb,
40125
+ bridgeReport: false,
40126
+ forceAllTypes: true
40127
+ });
40128
+ if (n > 0) sendBatchFromCache();
40129
+ } catch (e) {
40130
+ log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
40131
+ }
40132
+ })();
40133
+ }
40134
+
39580
40135
  // src/connection/create-bridge-connection.ts
39581
40136
  async function createBridgeConnection(options) {
39582
40137
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
39583
40138
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
39584
40139
  const logFn = options.log ?? log;
40140
+ getCliDatabase({ logLegacyMigration: logFn });
39585
40141
  const tokens = {
39586
40142
  accessToken: options.authToken,
39587
40143
  refreshToken: options.refreshToken
@@ -39604,16 +40160,39 @@ async function createBridgeConnection(options) {
39604
40160
  firehoseOutage: createEmptyReconnectOutageTracker(),
39605
40161
  lastFirehoseReconnectCloseMeta: null
39606
40162
  };
40163
+ function getWs() {
40164
+ return state.currentWs;
40165
+ }
40166
+ function sendAgentCapabilitiesToBridge(info) {
40167
+ if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
40168
+ let changed = false;
40169
+ try {
40170
+ changed = upsertCliAgentCapabilityCache(getCliDatabase(), {
40171
+ workspaceId,
40172
+ agentType: info.agentType,
40173
+ configOptions: info.configOptions
40174
+ });
40175
+ } catch {
40176
+ }
40177
+ if (!changed) return;
40178
+ const socket = getWs();
40179
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
40180
+ sendWsMessage(socket, {
40181
+ type: "agent_capabilities",
40182
+ agentType: info.agentType,
40183
+ configOptions: info.configOptions
40184
+ });
40185
+ }
39607
40186
  const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
39608
40187
  const sessionWorktreeManager = new SessionWorktreeManager({
39609
40188
  worktreesRootPath,
39610
40189
  log: logFn
39611
40190
  });
39612
- const acpManager = await createAcpManager({ log: logFn });
40191
+ const acpManager = await createAcpManager({
40192
+ log: logFn,
40193
+ reportAgentCapabilities: sendAgentCapabilitiesToBridge
40194
+ });
39613
40195
  logFn("CLI running. Press Ctrl+C to exit.");
39614
- function getWs() {
39615
- return state.currentWs;
39616
- }
39617
40196
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
39618
40197
  const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
39619
40198
  const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
@@ -39641,14 +40220,22 @@ async function createBridgeConnection(options) {
39641
40220
  },
39642
40221
  sendLocalSkillsReport,
39643
40222
  reportAutoDetectedAgents,
40223
+ warmupAgentCapabilitiesOnConnect: async () => {
40224
+ await warmupAgentCapabilitiesOnConnect({
40225
+ workspaceId,
40226
+ log: logFn,
40227
+ getDb: getCliDatabase,
40228
+ getWs
40229
+ });
40230
+ },
39644
40231
  devServerManager,
39645
40232
  e2ee,
39646
40233
  cloudApiBaseUrl: apiUrl,
39647
40234
  getCloudAccessToken: () => tokens.accessToken
39648
40235
  };
39649
40236
  const identifyReportedPaths = {
39650
- bridgeRootPath: path35.resolve(getBridgeRoot()),
39651
- worktreesRootPath: path35.resolve(worktreesRootPath)
40237
+ bridgeRootPath: path41.resolve(getBridgeRoot()),
40238
+ worktreesRootPath: path41.resolve(worktreesRootPath)
39652
40239
  };
39653
40240
  const { connect } = createMainBridgeWebSocketLifecycle({
39654
40241
  state,
@@ -39886,9 +40473,9 @@ async function runCliAction(program2, opts) {
39886
40473
  const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
39887
40474
  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);
39888
40475
  if (bridgeRootOpt) {
39889
- const resolvedBridgeRoot = path36.resolve(process.cwd(), bridgeRootOpt);
40476
+ const resolvedBridgeRoot = path42.resolve(process.cwd(), bridgeRootOpt);
39890
40477
  try {
39891
- const st = fs35.statSync(resolvedBridgeRoot);
40478
+ const st = fs37.statSync(resolvedBridgeRoot);
39892
40479
  if (!st.isDirectory()) {
39893
40480
  console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
39894
40481
  process.exit(1);
@@ -39908,7 +40495,7 @@ async function runCliAction(program2, opts) {
39908
40495
  );
39909
40496
  let worktreesRootPath;
39910
40497
  if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
39911
- worktreesRootPath = path36.resolve(opts.worktreesRoot.trim());
40498
+ worktreesRootPath = path42.resolve(opts.worktreesRoot.trim());
39912
40499
  }
39913
40500
  const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
39914
40501
  if (e2eCertificates) {