@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/index.js CHANGED
@@ -2240,7 +2240,7 @@ var require_websocket = __commonJS({
2240
2240
  var http = __require("http");
2241
2241
  var net = __require("net");
2242
2242
  var tls = __require("tls");
2243
- var { randomBytes: randomBytes2, createHash } = __require("crypto");
2243
+ var { randomBytes: randomBytes2, createHash: createHash2 } = __require("crypto");
2244
2244
  var { Duplex, Readable: Readable2 } = __require("stream");
2245
2245
  var { URL: URL2 } = __require("url");
2246
2246
  var PerMessageDeflate = require_permessage_deflate();
@@ -2900,7 +2900,7 @@ var require_websocket = __commonJS({
2900
2900
  abortHandshake(websocket, socket, "Invalid Upgrade header");
2901
2901
  return;
2902
2902
  }
2903
- const digest = createHash("sha1").update(key + GUID).digest("base64");
2903
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
2904
2904
  if (res.headers["sec-websocket-accept"] !== digest) {
2905
2905
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2906
2906
  return;
@@ -3267,7 +3267,7 @@ var require_websocket_server = __commonJS({
3267
3267
  var EventEmitter2 = __require("events");
3268
3268
  var http = __require("http");
3269
3269
  var { Duplex } = __require("stream");
3270
- var { createHash } = __require("crypto");
3270
+ var { createHash: createHash2 } = __require("crypto");
3271
3271
  var extension = require_extension();
3272
3272
  var PerMessageDeflate = require_permessage_deflate();
3273
3273
  var subprotocol = require_subprotocol();
@@ -3568,7 +3568,7 @@ var require_websocket_server = __commonJS({
3568
3568
  );
3569
3569
  }
3570
3570
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3571
- const digest = createHash("sha1").update(key + GUID).digest("base64");
3571
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
3572
3572
  const headers = [
3573
3573
  "HTTP/1.1 101 Switching Protocols",
3574
3574
  "Upgrade: websocket",
@@ -4065,8 +4065,8 @@ var init_parseUtil = __esm({
4065
4065
  init_errors();
4066
4066
  init_en();
4067
4067
  makeIssue = (params) => {
4068
- const { data, path: path35, errorMaps, issueData } = params;
4069
- const fullPath = [...path35, ...issueData.path || []];
4068
+ const { data, path: path41, errorMaps, issueData } = params;
4069
+ const fullPath = [...path41, ...issueData.path || []];
4070
4070
  const fullIssue = {
4071
4071
  ...issueData,
4072
4072
  path: fullPath
@@ -4374,11 +4374,11 @@ var init_types = __esm({
4374
4374
  init_parseUtil();
4375
4375
  init_util();
4376
4376
  ParseInputLazyPath = class {
4377
- constructor(parent, value, path35, key) {
4377
+ constructor(parent, value, path41, key) {
4378
4378
  this._cachedPath = [];
4379
4379
  this.parent = parent;
4380
4380
  this.data = value;
4381
- this._path = path35;
4381
+ this._path = path41;
4382
4382
  this._key = key;
4383
4383
  }
4384
4384
  get path() {
@@ -7993,10 +7993,10 @@ function assignProp(target, prop, value) {
7993
7993
  configurable: true
7994
7994
  });
7995
7995
  }
7996
- function getElementAtPath(obj, path35) {
7997
- if (!path35)
7996
+ function getElementAtPath(obj, path41) {
7997
+ if (!path41)
7998
7998
  return obj;
7999
- return path35.reduce((acc, key) => acc?.[key], obj);
7999
+ return path41.reduce((acc, key) => acc?.[key], obj);
8000
8000
  }
8001
8001
  function promiseAllObject(promisesObj) {
8002
8002
  const keys = Object.keys(promisesObj);
@@ -8245,11 +8245,11 @@ function aborted(x, startIndex = 0) {
8245
8245
  }
8246
8246
  return false;
8247
8247
  }
8248
- function prefixIssues(path35, issues) {
8248
+ function prefixIssues(path41, issues) {
8249
8249
  return issues.map((iss) => {
8250
8250
  var _a2;
8251
8251
  (_a2 = iss).path ?? (_a2.path = []);
8252
- iss.path.unshift(path35);
8252
+ iss.path.unshift(path41);
8253
8253
  return iss;
8254
8254
  });
8255
8255
  }
@@ -8438,7 +8438,7 @@ function treeifyError(error40, _mapper) {
8438
8438
  return issue2.message;
8439
8439
  };
8440
8440
  const result = { errors: [] };
8441
- const processError = (error41, path35 = []) => {
8441
+ const processError = (error41, path41 = []) => {
8442
8442
  var _a2, _b;
8443
8443
  for (const issue2 of error41.issues) {
8444
8444
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -8448,7 +8448,7 @@ function treeifyError(error40, _mapper) {
8448
8448
  } else if (issue2.code === "invalid_element") {
8449
8449
  processError({ issues: issue2.issues }, issue2.path);
8450
8450
  } else {
8451
- const fullpath = [...path35, ...issue2.path];
8451
+ const fullpath = [...path41, ...issue2.path];
8452
8452
  if (fullpath.length === 0) {
8453
8453
  result.errors.push(mapper(issue2));
8454
8454
  continue;
@@ -8478,9 +8478,9 @@ function treeifyError(error40, _mapper) {
8478
8478
  processError(error40);
8479
8479
  return result;
8480
8480
  }
8481
- function toDotPath(path35) {
8481
+ function toDotPath(path41) {
8482
8482
  const segs = [];
8483
- for (const seg of path35) {
8483
+ for (const seg of path41) {
8484
8484
  if (typeof seg === "number")
8485
8485
  segs.push(`[${seg}]`);
8486
8486
  else if (typeof seg === "symbol")
@@ -20943,8 +20943,8 @@ var init_acp = __esm({
20943
20943
  this.#requestHandler = requestHandler;
20944
20944
  this.#notificationHandler = notificationHandler;
20945
20945
  this.#stream = stream;
20946
- this.#closedPromise = new Promise((resolve16) => {
20947
- this.#abortController.signal.addEventListener("abort", () => resolve16());
20946
+ this.#closedPromise = new Promise((resolve18) => {
20947
+ this.#abortController.signal.addEventListener("abort", () => resolve18());
20948
20948
  });
20949
20949
  this.#receive();
20950
20950
  }
@@ -21093,8 +21093,8 @@ var init_acp = __esm({
21093
21093
  }
21094
21094
  async sendRequest(method, params) {
21095
21095
  const id = this.#nextRequestId++;
21096
- const responsePromise = new Promise((resolve16, reject) => {
21097
- this.#pendingResponses.set(id, { resolve: resolve16, reject });
21096
+ const responsePromise = new Promise((resolve18, reject) => {
21097
+ this.#pendingResponses.set(id, { resolve: resolve18, reject });
21098
21098
  });
21099
21099
  await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
21100
21100
  return responsePromise;
@@ -21669,7 +21669,7 @@ var require_has_flag = __commonJS({
21669
21669
  var require_supports_color = __commonJS({
21670
21670
  "../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports, module) {
21671
21671
  "use strict";
21672
- var os8 = __require("os");
21672
+ var os9 = __require("os");
21673
21673
  var tty = __require("tty");
21674
21674
  var hasFlag = require_has_flag();
21675
21675
  var { env } = process;
@@ -21717,7 +21717,7 @@ var require_supports_color = __commonJS({
21717
21717
  return min;
21718
21718
  }
21719
21719
  if (process.platform === "win32") {
21720
- const osRelease = os8.release().split(".");
21720
+ const osRelease = os9.release().split(".");
21721
21721
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
21722
21722
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
21723
21723
  }
@@ -21963,10 +21963,10 @@ var require_src2 = __commonJS({
21963
21963
  var fs_1 = __require("fs");
21964
21964
  var debug_1 = __importDefault(require_src());
21965
21965
  var log2 = debug_1.default("@kwsites/file-exists");
21966
- function check2(path35, isFile, isDirectory) {
21967
- log2(`checking %s`, path35);
21966
+ function check2(path41, isFile, isDirectory) {
21967
+ log2(`checking %s`, path41);
21968
21968
  try {
21969
- const stat2 = fs_1.statSync(path35);
21969
+ const stat2 = fs_1.statSync(path41);
21970
21970
  if (stat2.isFile() && isFile) {
21971
21971
  log2(`[OK] path represents a file`);
21972
21972
  return true;
@@ -21986,8 +21986,8 @@ var require_src2 = __commonJS({
21986
21986
  throw e;
21987
21987
  }
21988
21988
  }
21989
- function exists2(path35, type = exports.READABLE) {
21990
- return check2(path35, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
21989
+ function exists2(path41, type = exports.READABLE) {
21990
+ return check2(path41, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
21991
21991
  }
21992
21992
  exports.exists = exists2;
21993
21993
  exports.FILE = 1;
@@ -22792,9 +22792,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
22792
22792
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
22793
22793
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
22794
22794
  if (!rawPath || !summary) continue;
22795
- const path35 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
22796
- if (!path35) continue;
22797
- rows.push({ path: path35, summary: clampSummaryToAtMostTwoLines(summary) });
22795
+ const path41 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
22796
+ if (!path41) continue;
22797
+ rows.push({ path: path41, summary: clampSummaryToAtMostTwoLines(summary) });
22798
22798
  }
22799
22799
  return rows;
22800
22800
  }
@@ -23004,6 +23004,7 @@ function buildCliAutoApprovedPermissionRpcResult(requestParams) {
23004
23004
  // ../types/src/agent-config.ts
23005
23005
  var AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY = "claude_permission_mode";
23006
23006
  var AGENT_CONFIG_CLI_PERMISSION_MODE_KEY = "cli_permission_mode";
23007
+ var AGENT_CONFIG_AGENT_MODEL_KEY = "agent_model";
23007
23008
  function getClaudePermissionModeFromAgentConfig(config2) {
23008
23009
  if (!config2) return null;
23009
23010
  const raw = config2[AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY];
@@ -23015,6 +23016,13 @@ function getCliPermissionModeFromAgentConfig(config2) {
23015
23016
  if (!config2) return CLI_PERMISSION_MODE_DEFAULT;
23016
23017
  return normalizeCliPermissionModeInput(config2[AGENT_CONFIG_CLI_PERMISSION_MODE_KEY]);
23017
23018
  }
23019
+ function getAgentModelFromAgentConfig(config2) {
23020
+ if (!config2) return null;
23021
+ const cur = config2[AGENT_CONFIG_AGENT_MODEL_KEY];
23022
+ if (typeof cur !== "string") return null;
23023
+ const t = cur.trim();
23024
+ return t !== "" ? t : null;
23025
+ }
23018
23026
 
23019
23027
  // src/agents/acp/claude-acp-permission-from-session.ts
23020
23028
  function flattenSelectOptions(options) {
@@ -23070,6 +23078,48 @@ async function applyClaudePermissionFromAcpSession(params) {
23070
23078
  }
23071
23079
  }
23072
23080
 
23081
+ // src/agents/acp/apply-acp-model-from-agent-session.ts
23082
+ function flattenSelectOptions2(options) {
23083
+ if (options == null || options.length === 0) return [];
23084
+ const first2 = options[0];
23085
+ if (first2 != null && typeof first2 === "object" && "group" in first2 && first2.group != null) {
23086
+ return options.flatMap((g) => Array.isArray(g.options) ? g.options : []);
23087
+ }
23088
+ return options;
23089
+ }
23090
+ function looksLikeModelOption(o) {
23091
+ if (o.category === "model" || o.category === "models") return true;
23092
+ const id = typeof o.id === "string" ? o.id.toLowerCase() : "";
23093
+ if (id === "model" || id.endsWith("_model") || id.includes("model")) return true;
23094
+ const name = typeof o.name === "string" ? o.name.toLowerCase() : "";
23095
+ return name.includes("model") && !name.includes("mode");
23096
+ }
23097
+ function pickModelConfigOption(configOptions) {
23098
+ if (configOptions == null || configOptions.length === 0) return null;
23099
+ return configOptions.find(looksLikeModelOption) ?? null;
23100
+ }
23101
+ async function applyAcpModelFromAcpSession(params) {
23102
+ const { sessionId, agentConfig, configOptions, setSessionConfigOption, logDebug: logDebug2 } = params;
23103
+ const desired = getAgentModelFromAgentConfig(agentConfig);
23104
+ if (desired == null) return;
23105
+ const modelOpt = pickModelConfigOption(configOptions ?? null);
23106
+ if (modelOpt == null) return;
23107
+ const flat = flattenSelectOptions2(modelOpt.options);
23108
+ const allowed = flat.some((o) => o.value === desired);
23109
+ if (!allowed) return;
23110
+ if (modelOpt.currentValue === desired) return;
23111
+ try {
23112
+ logDebug2(
23113
+ `[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`
23114
+ );
23115
+ await setSessionConfigOption({ sessionId, configId: modelOpt.id, value: desired });
23116
+ } catch (e) {
23117
+ logDebug2(
23118
+ `[Agent] ACP session/set_config_option (model) failed: ${e instanceof Error ? e.message : String(e)}`
23119
+ );
23120
+ }
23121
+ }
23122
+
23073
23123
  // src/agents/acp/clients/shared/config-options-for-permission.ts
23074
23124
  function configOptionsForPermission(getActive, established) {
23075
23125
  const mem = getActive?.();
@@ -23162,6 +23212,17 @@ async function bootstrapAcpWireSession(transport, ctx, initializeRequest) {
23162
23212
  logDebug: ctx.logDebug
23163
23213
  });
23164
23214
  }
23215
+ const cfgAll = ctx.agentConfig != null && typeof ctx.agentConfig === "object" && !Array.isArray(ctx.agentConfig) ? ctx.agentConfig : null;
23216
+ const configOptionsForModel = established.configOptions;
23217
+ if (transport.setSessionConfigOption) {
23218
+ await applyAcpModelFromAcpSession({
23219
+ sessionId,
23220
+ agentConfig: cfgAll,
23221
+ configOptions: configOptionsForPermission(ctx.getActiveConfigOptions, configOptionsForModel),
23222
+ setSessionConfigOption: (p) => transport.setSessionConfigOption(p),
23223
+ logDebug: ctx.logDebug
23224
+ });
23225
+ }
23165
23226
  return established;
23166
23227
  }
23167
23228
 
@@ -23267,10 +23328,15 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText,
23267
23328
  // src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
23268
23329
  function awaitSdkStdioPermissionRequestHandshake(params) {
23269
23330
  const { requestId, paramsRecord, pending, onRequest } = params;
23270
- return new Promise((resolve16) => {
23271
- pending.set(requestId, { resolve: resolve16, params: paramsRecord });
23331
+ return new Promise((resolve18) => {
23332
+ pending.set(requestId, { resolve: resolve18, params: paramsRecord });
23333
+ if (onRequest == null) {
23334
+ pending.delete(requestId);
23335
+ resolve18({ outcome: { outcome: "denied" } });
23336
+ return;
23337
+ }
23272
23338
  try {
23273
- onRequest?.({
23339
+ onRequest({
23274
23340
  requestId,
23275
23341
  method: "session/request_permission",
23276
23342
  params: paramsRecord
@@ -23332,7 +23398,7 @@ async function createSdkStdioAcpClient(options) {
23332
23398
  child.once("close", (code, signal) => {
23333
23399
  onAgentSubprocessExit?.({ code, signal });
23334
23400
  });
23335
- return new Promise((resolve16, reject) => {
23401
+ return new Promise((resolve18, reject) => {
23336
23402
  let initSettled = false;
23337
23403
  const settleReject = (err) => {
23338
23404
  if (initSettled) return;
@@ -23346,7 +23412,7 @@ async function createSdkStdioAcpClient(options) {
23346
23412
  const settleResolve = (handle) => {
23347
23413
  if (initSettled) return;
23348
23414
  initSettled = true;
23349
- resolve16(handle);
23415
+ resolve18(handle);
23350
23416
  };
23351
23417
  child.on("error", (err) => {
23352
23418
  settleReject(new Error(formatSpawnError(err, command[0])));
@@ -23566,7 +23632,7 @@ async function proxyToLocal(request) {
23566
23632
  };
23567
23633
  const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
23568
23634
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
23569
- const once = await new Promise((resolve16) => {
23635
+ const once = await new Promise((resolve18) => {
23570
23636
  const req = mod.request(opts, (res) => {
23571
23637
  const chunks = [];
23572
23638
  res.on("data", (c) => chunks.push(c));
@@ -23577,7 +23643,7 @@ async function proxyToLocal(request) {
23577
23643
  if (typeof v === "string") headers[k] = v;
23578
23644
  else if (Array.isArray(v) && v[0]) headers[k] = v[0];
23579
23645
  }
23580
- resolve16({
23646
+ resolve18({
23581
23647
  id: request.id,
23582
23648
  statusCode: res.statusCode ?? 0,
23583
23649
  headers,
@@ -23586,7 +23652,7 @@ async function proxyToLocal(request) {
23586
23652
  });
23587
23653
  });
23588
23654
  req.on("error", (err) => {
23589
- resolve16({
23655
+ resolve18({
23590
23656
  id: request.id,
23591
23657
  statusCode: 0,
23592
23658
  headers: {},
@@ -23668,8 +23734,8 @@ function randomSecret() {
23668
23734
  }
23669
23735
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
23670
23736
  }
23671
- async function requestPreviewApi(port, secret, method, path35, body) {
23672
- const url2 = `http://127.0.0.1:${port}${path35}`;
23737
+ async function requestPreviewApi(port, secret, method, path41, body) {
23738
+ const url2 = `http://127.0.0.1:${port}${path41}`;
23673
23739
  const headers = {
23674
23740
  [PREVIEW_SECRET_HEADER]: secret,
23675
23741
  "Content-Type": "application/json"
@@ -23681,7 +23747,7 @@ async function requestPreviewApi(port, secret, method, path35, body) {
23681
23747
  });
23682
23748
  const data = await res.json().catch(() => ({}));
23683
23749
  if (!res.ok) {
23684
- throw new Error(data?.error ?? `Preview API ${method} ${path35}: ${res.status}`);
23750
+ throw new Error(data?.error ?? `Preview API ${method} ${path41}: ${res.status}`);
23685
23751
  }
23686
23752
  return data;
23687
23753
  }
@@ -23896,7 +23962,7 @@ function installBridgeProcessResilience() {
23896
23962
  }
23897
23963
 
23898
23964
  // src/cli-version.ts
23899
- var CLI_VERSION = "0.1.28".length > 0 ? "0.1.28" : "0.0.0-dev";
23965
+ var CLI_VERSION = "0.1.30".length > 0 ? "0.1.30" : "0.0.0-dev";
23900
23966
 
23901
23967
  // src/connection/heartbeat/constants.ts
23902
23968
  var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -24337,14 +24403,14 @@ var baseOpen = async (options) => {
24337
24403
  }
24338
24404
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
24339
24405
  if (options.wait) {
24340
- return new Promise((resolve16, reject) => {
24406
+ return new Promise((resolve18, reject) => {
24341
24407
  subprocess.once("error", reject);
24342
24408
  subprocess.once("close", (exitCode) => {
24343
24409
  if (!options.allowNonzeroExitCode && exitCode > 0) {
24344
24410
  reject(new Error(`Exited with code ${exitCode}`));
24345
24411
  return;
24346
24412
  }
24347
- resolve16(subprocess);
24413
+ resolve18(subprocess);
24348
24414
  });
24349
24415
  });
24350
24416
  }
@@ -24833,8 +24899,8 @@ function runPendingAuth(options) {
24833
24899
  let hasOpenedBrowser = false;
24834
24900
  let resolved = false;
24835
24901
  let resolveAuth;
24836
- const authPromise = new Promise((resolve16) => {
24837
- resolveAuth = resolve16;
24902
+ const authPromise = new Promise((resolve18) => {
24903
+ resolveAuth = resolve18;
24838
24904
  });
24839
24905
  let reconnectAttempt = 0;
24840
24906
  const signInQuiet = createEmptyReconnectQuietSlot();
@@ -24956,11 +25022,353 @@ function runPendingAuth(options) {
24956
25022
  };
24957
25023
  }
24958
25024
 
25025
+ // src/sqlite/cli-database.ts
25026
+ import sqliteWasm from "node-sqlite3-wasm";
25027
+
25028
+ // src/sqlite/cli-sqlite-paths.ts
25029
+ import fs7 from "node:fs";
25030
+ import path5 from "node:path";
25031
+ import os3 from "node:os";
25032
+ function getCliSqlitePath() {
25033
+ const override = process.env.BUILDAMATON_CLI_SQLITE_PATH?.trim();
25034
+ if (override) return path5.resolve(override);
25035
+ return path5.join(os3.homedir(), ".buildautomaton", "cli.sqlite");
25036
+ }
25037
+ function ensureCliSqliteParentDir(sqlitePath) {
25038
+ const dir = path5.dirname(sqlitePath);
25039
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
25040
+ }
25041
+
25042
+ // src/sqlite/legacy_migration/import-cli-legacy-disk-data.ts
25043
+ import fs12 from "node:fs";
25044
+
25045
+ // src/files/index/constants.ts
25046
+ import path6 from "node:path";
25047
+ import os4 from "node:os";
25048
+ var INDEX_WORK_YIELD_EVERY = 256;
25049
+ var INDEX_DIR = path6.join(os4.homedir(), ".buildautomaton");
25050
+ var INDEX_HASH_LEN = 16;
25051
+
25052
+ // src/prompt-turn-queue/paths.ts
25053
+ import path7 from "node:path";
25054
+ import os5 from "node:os";
25055
+ function getPromptQueuesDirectory() {
25056
+ const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
25057
+ if (override) return path7.resolve(override);
25058
+ return path7.join(os5.homedir(), ".buildautomaton", "queues");
25059
+ }
25060
+
25061
+ // src/sqlite/legacy_migration/archive-file-index-json.ts
25062
+ import fs9 from "node:fs";
25063
+ import path9 from "node:path";
25064
+
25065
+ // src/sqlite/legacy_migration/archive-to-old-version.ts
25066
+ import fs8 from "node:fs";
25067
+ import path8 from "node:path";
25068
+ var OLD_VERSION_DIR = path8.join(INDEX_DIR, "old-version");
25069
+ function moveLegacyFileToOldVersionBackup(category, sourcePath) {
25070
+ const destDir = path8.join(OLD_VERSION_DIR, category);
25071
+ fs8.mkdirSync(destDir, { recursive: true });
25072
+ const base = path8.basename(sourcePath);
25073
+ let dest = path8.join(destDir, base);
25074
+ if (fs8.existsSync(dest)) {
25075
+ const ext = path8.extname(base);
25076
+ const stem = ext ? base.slice(0, -ext.length) : base;
25077
+ dest = path8.join(destDir, `${stem}-${Date.now()}${ext}`);
25078
+ }
25079
+ fs8.renameSync(sourcePath, dest);
25080
+ }
25081
+
25082
+ // src/sqlite/legacy_migration/archive-file-index-json.ts
25083
+ function archiveLegacyFileIndexJsonFiles() {
25084
+ try {
25085
+ if (!fs9.existsSync(INDEX_DIR)) return;
25086
+ for (const name of fs9.readdirSync(INDEX_DIR)) {
25087
+ if (name.startsWith(".file-index-") && name.endsWith(".json")) {
25088
+ const full = path9.join(INDEX_DIR, name);
25089
+ try {
25090
+ moveLegacyFileToOldVersionBackup("file-index", full);
25091
+ } catch (err) {
25092
+ console.error(`[cli-sqlite] legacy import: could not archive ${name}`, err);
25093
+ }
25094
+ }
25095
+ }
25096
+ } catch (e) {
25097
+ console.error("[cli-sqlite] legacy import: archive file-index json failed", e);
25098
+ }
25099
+ }
25100
+
25101
+ // src/sqlite/legacy_migration/import-agent-sessions-from-disk.ts
25102
+ import fs10 from "node:fs";
25103
+ import path10 from "node:path";
25104
+ import os6 from "node:os";
25105
+ var LEGACY_AGENT_SESSION_DIR = path10.join(os6.homedir(), ".buildautomaton", "agent-sessions");
25106
+ function importLegacyAgentSessionsFromDisk(db) {
25107
+ try {
25108
+ if (!fs10.existsSync(LEGACY_AGENT_SESSION_DIR)) return;
25109
+ const names = fs10.readdirSync(LEGACY_AGENT_SESSION_DIR).filter((n) => n.endsWith(".json"));
25110
+ for (const name of names) {
25111
+ const sessionKey = name.slice(0, -".json".length);
25112
+ const full = path10.join(LEGACY_AGENT_SESSION_DIR, name);
25113
+ let raw;
25114
+ try {
25115
+ raw = fs10.readFileSync(full, "utf8");
25116
+ } catch {
25117
+ continue;
25118
+ }
25119
+ let parsed;
25120
+ try {
25121
+ parsed = JSON.parse(raw);
25122
+ } catch {
25123
+ continue;
25124
+ }
25125
+ if (parsed.v !== 1) continue;
25126
+ const acpSessionId = typeof parsed.acpSessionId === "string" ? parsed.acpSessionId : null;
25127
+ const backendAgentType = typeof parsed.backendAgentType === "string" ? parsed.backendAgentType : null;
25128
+ const configOptionsJson = Array.isArray(parsed.configOptions) ? JSON.stringify(parsed.configOptions) : null;
25129
+ const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
25130
+ db.run(
25131
+ `INSERT OR REPLACE INTO agent_session (session_key, acp_session_id, backend_agent_type, config_options_json, updated_at)
25132
+ VALUES (?, ?, ?, ?, ?)`,
25133
+ [sessionKey, acpSessionId, backendAgentType, configOptionsJson, updatedAt]
25134
+ );
25135
+ try {
25136
+ moveLegacyFileToOldVersionBackup("agent-sessions", full);
25137
+ } catch {
25138
+ }
25139
+ }
25140
+ } catch (e) {
25141
+ console.error("[cli-sqlite] legacy import: agent_sessions from disk failed", e);
25142
+ }
25143
+ }
25144
+
25145
+ // src/sqlite/legacy_migration/import-prompt-queues-from-disk.ts
25146
+ import fs11 from "node:fs";
25147
+ import path11 from "node:path";
25148
+
25149
+ // src/sqlite/legacy_migration/parse-persisted-queue-json.ts
25150
+ function parsePersistedQueueFromJson(raw) {
25151
+ try {
25152
+ const o = JSON.parse(raw);
25153
+ const queueKey = typeof o.queueKey === "string" ? o.queueKey : typeof o.queueKeyHash === "string" ? o.queueKeyHash : null;
25154
+ if (!queueKey || typeof o.updatedAt !== "string" || !Array.isArray(o.turns)) return null;
25155
+ return { queueKey, updatedAt: o.updatedAt, turns: o.turns };
25156
+ } catch {
25157
+ return null;
25158
+ }
25159
+ }
25160
+
25161
+ // src/sqlite/legacy_migration/import-prompt-queues-from-disk.ts
25162
+ function importLegacyPromptQueuesFromDisk(db) {
25163
+ try {
25164
+ const dir = getPromptQueuesDirectory();
25165
+ if (!fs11.existsSync(dir)) return;
25166
+ for (const name of fs11.readdirSync(dir)) {
25167
+ if (!name.endsWith(".json")) continue;
25168
+ const full = path11.join(dir, name);
25169
+ let raw;
25170
+ try {
25171
+ raw = fs11.readFileSync(full, "utf8");
25172
+ } catch {
25173
+ continue;
25174
+ }
25175
+ const file2 = parsePersistedQueueFromJson(raw);
25176
+ if (!file2) continue;
25177
+ db.run(
25178
+ `INSERT OR REPLACE INTO prompt_queue (queue_key, updated_at, turns_json) VALUES (?, ?, ?)`,
25179
+ [file2.queueKey, file2.updatedAt, JSON.stringify(file2.turns)]
25180
+ );
25181
+ try {
25182
+ moveLegacyFileToOldVersionBackup("queues", full);
25183
+ } catch {
25184
+ }
25185
+ }
25186
+ } catch (e) {
25187
+ console.error("[cli-sqlite] legacy import: prompt queues from disk failed", e);
25188
+ }
25189
+ }
25190
+
25191
+ // src/sqlite/legacy_migration/import-cli-legacy-disk-data.ts
25192
+ function legacyCliDiskMigrationIsPending() {
25193
+ try {
25194
+ if (fs12.existsSync(INDEX_DIR)) {
25195
+ for (const name of fs12.readdirSync(INDEX_DIR)) {
25196
+ if (name.startsWith(".file-index-") && name.endsWith(".json")) return true;
25197
+ }
25198
+ }
25199
+ } catch {
25200
+ }
25201
+ try {
25202
+ if (fs12.existsSync(LEGACY_AGENT_SESSION_DIR)) {
25203
+ if (fs12.readdirSync(LEGACY_AGENT_SESSION_DIR).some((n) => n.endsWith(".json"))) return true;
25204
+ }
25205
+ } catch {
25206
+ }
25207
+ try {
25208
+ const dir = getPromptQueuesDirectory();
25209
+ if (fs12.existsSync(dir) && fs12.readdirSync(dir).some((n) => n.endsWith(".json"))) return true;
25210
+ } catch {
25211
+ }
25212
+ return false;
25213
+ }
25214
+ function importCliSqliteLegacyDiskData(db, log2) {
25215
+ const pending = legacyCliDiskMigrationIsPending();
25216
+ if (pending && log2) {
25217
+ log2("Migrating legacy on-disk CLI data to SQLite\u2026");
25218
+ }
25219
+ archiveLegacyFileIndexJsonFiles();
25220
+ importLegacyAgentSessionsFromDisk(db);
25221
+ importLegacyPromptQueuesFromDisk(db);
25222
+ if (pending && log2) {
25223
+ log2("Legacy on-disk CLI data migration finished.");
25224
+ }
25225
+ }
25226
+
25227
+ // src/sqlite/load-cli-migration-sql.ts
25228
+ import { existsSync, readFileSync as readFileSync2 } from "node:fs";
25229
+ import { dirname as dirname2, join } from "node:path";
25230
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
25231
+ function readCliSqliteMigrationSql(filename) {
25232
+ const dir = dirname2(fileURLToPath2(import.meta.url));
25233
+ const resolved = join(dir, "migrations", filename);
25234
+ if (!existsSync(resolved)) {
25235
+ throw new Error(`Missing CLI SQLite migration SQL: ${resolved}`);
25236
+ }
25237
+ return readFileSync2(resolved, "utf8");
25238
+ }
25239
+
25240
+ // src/sqlite/migrate-cli-sqlite.ts
25241
+ function checkpointSatisfiedByLegacyChain(applied2, replacesLegacyMigrations) {
25242
+ return Array.isArray(replacesLegacyMigrations) && replacesLegacyMigrations.length > 0 && replacesLegacyMigrations.every((n) => applied2.has(n));
25243
+ }
25244
+ function recordMigrationAndPruneCheckpointLegacy(db, migration, applied2) {
25245
+ db.run("INSERT INTO __migrations (name) VALUES (?)", [migration.name]);
25246
+ applied2.add(migration.name);
25247
+ if (migration.checkpoint !== true) return;
25248
+ const legacy = migration.replacesLegacyMigrations;
25249
+ if (!legacy?.length) return;
25250
+ for (const legacyName of legacy) {
25251
+ if (legacyName === migration.name) continue;
25252
+ db.run("DELETE FROM __migrations WHERE name = ?", [legacyName]);
25253
+ applied2.delete(legacyName);
25254
+ }
25255
+ }
25256
+ var CHECKPOINT_V1 = "001_cli_sqlite_checkpoint_v1";
25257
+ var CHECKPOINT_V1_SQL = readCliSqliteMigrationSql("001_cli_sqlite_checkpoint_v1.sql");
25258
+ var AGENT_CAPABILITIES_SQL = readCliSqliteMigrationSql("002_agent_capabilities.sql");
25259
+ function agentCapabilitiesTableState(db) {
25260
+ const rows = db.all(
25261
+ `SELECT name FROM sqlite_master WHERE type='table' AND name IN ('agent_capabilities', 'agent_capability_cache')`
25262
+ );
25263
+ const names = new Set(rows.map((r) => r.name));
25264
+ if (names.has("agent_capabilities")) return "current";
25265
+ if (names.has("agent_capability_cache")) return "legacy";
25266
+ return "new";
25267
+ }
25268
+ var CLI_SQLITE_MIGRATIONS = [
25269
+ {
25270
+ name: CHECKPOINT_V1,
25271
+ checkpoint: true,
25272
+ migrate: (db) => {
25273
+ db.exec(CHECKPOINT_V1_SQL);
25274
+ }
25275
+ },
25276
+ {
25277
+ name: "002_agent_capabilities",
25278
+ migrate: (db) => {
25279
+ const state = agentCapabilitiesTableState(db);
25280
+ if (state === "current") return;
25281
+ if (state === "legacy") {
25282
+ db.exec(`
25283
+ ALTER TABLE agent_capability_cache RENAME TO agent_capabilities;
25284
+ DROP INDEX IF EXISTS idx_agent_capability_cache_workspace;
25285
+ CREATE INDEX IF NOT EXISTS idx_agent_capabilities_workspace ON agent_capabilities(workspace_id);
25286
+ `);
25287
+ return;
25288
+ }
25289
+ db.exec(AGENT_CAPABILITIES_SQL);
25290
+ },
25291
+ alreadyApplied: (db) => agentCapabilitiesTableState(db) === "current"
25292
+ }
25293
+ ];
25294
+ function migrateCliSqlite(db) {
25295
+ db.exec(readCliSqliteMigrationSql("000_bootstrap_migrations_table.sql"));
25296
+ const appliedRows = db.all("SELECT name FROM __migrations");
25297
+ const applied2 = new Set(appliedRows.map((r) => r.name));
25298
+ for (const migration of CLI_SQLITE_MIGRATIONS) {
25299
+ if (applied2.has(migration.name)) continue;
25300
+ if (migration.checkpoint === true && checkpointSatisfiedByLegacyChain(applied2, migration.replacesLegacyMigrations)) {
25301
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
25302
+ continue;
25303
+ }
25304
+ if (migration.alreadyApplied?.(db)) {
25305
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
25306
+ continue;
25307
+ }
25308
+ try {
25309
+ migration.migrate(db);
25310
+ recordMigrationAndPruneCheckpointLegacy(db, migration, applied2);
25311
+ } catch (e) {
25312
+ console.error(`[cli-sqlite] Migration failed: ${migration.name}`, e);
25313
+ throw e;
25314
+ }
25315
+ }
25316
+ }
25317
+
25318
+ // src/sqlite/cli-database.ts
25319
+ var { Database: SqliteDatabase } = sqliteWasm;
25320
+ var openDatabases = /* @__PURE__ */ new Map();
25321
+ var processExitCloseRegistered = false;
25322
+ function registerProcessExitSqliteClose() {
25323
+ if (processExitCloseRegistered) return;
25324
+ processExitCloseRegistered = true;
25325
+ process.once("exit", () => {
25326
+ for (const db of openDatabases.values()) {
25327
+ safeCloseCliSqliteDatabase(db);
25328
+ }
25329
+ openDatabases.clear();
25330
+ });
25331
+ }
25332
+ function safeCloseCliSqliteDatabase(db) {
25333
+ if (db == null) return;
25334
+ try {
25335
+ if (db.isOpen) db.close();
25336
+ } catch {
25337
+ }
25338
+ }
25339
+ function closeAllCliSqliteConnections() {
25340
+ for (const db of openDatabases.values()) {
25341
+ safeCloseCliSqliteDatabase(db);
25342
+ }
25343
+ openDatabases.clear();
25344
+ }
25345
+ function getCliDatabase(options) {
25346
+ const sqlitePath = getCliSqlitePath();
25347
+ const existing = openDatabases.get(sqlitePath);
25348
+ if (existing?.isOpen) return existing;
25349
+ if (existing && !existing.isOpen) {
25350
+ safeCloseCliSqliteDatabase(existing);
25351
+ openDatabases.delete(sqlitePath);
25352
+ }
25353
+ ensureCliSqliteParentDir(sqlitePath);
25354
+ const db = new SqliteDatabase(sqlitePath);
25355
+ try {
25356
+ migrateCliSqlite(db);
25357
+ importCliSqliteLegacyDiskData(db, options?.logLegacyMigration);
25358
+ } catch (e) {
25359
+ safeCloseCliSqliteDatabase(db);
25360
+ throw e;
25361
+ }
25362
+ openDatabases.set(sqlitePath, db);
25363
+ registerProcessExitSqliteClose();
25364
+ return db;
25365
+ }
25366
+
24959
25367
  // src/connection/close-bridge-connection.ts
24960
25368
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
24961
25369
  const say = log2 ?? logImmediate;
24962
25370
  say("Cleaning up connections\u2026");
24963
- await new Promise((resolve16) => setImmediate(resolve16));
25371
+ await new Promise((resolve18) => setImmediate(resolve18));
24964
25372
  state.closedByUser = true;
24965
25373
  clearReconnectQuietTimer(state.mainQuiet);
24966
25374
  clearReconnectQuietTimer(state.firehoseQuiet);
@@ -24999,20 +25407,24 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
24999
25407
  say("Stopping local dev server processes\u2026");
25000
25408
  await devServerManager.shutdownAllGraceful();
25001
25409
  }
25410
+ try {
25411
+ closeAllCliSqliteConnections();
25412
+ } catch {
25413
+ }
25002
25414
  say("Shutdown complete.");
25003
25415
  }
25004
25416
 
25005
25417
  // src/paths/session-layout-paths.ts
25006
- import * as path5 from "node:path";
25418
+ import * as path12 from "node:path";
25007
25419
  function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
25008
- const resolved = worktreePaths.map((p) => path5.resolve(p)).filter(Boolean);
25420
+ const resolved = worktreePaths.map((p) => path12.resolve(p)).filter(Boolean);
25009
25421
  if (resolved.length === 0) return null;
25010
25422
  resolved.sort();
25011
25423
  return resolved[0];
25012
25424
  }
25013
25425
  function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
25014
25426
  if (resolvedSessionParentPath != null && String(resolvedSessionParentPath).trim() !== "") {
25015
- return path5.resolve(String(resolvedSessionParentPath).trim());
25427
+ return path12.resolve(String(resolvedSessionParentPath).trim());
25016
25428
  }
25017
25429
  return getBridgeRoot();
25018
25430
  }
@@ -25021,17 +25433,17 @@ function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
25021
25433
  import { execFile as execFile7 } from "node:child_process";
25022
25434
  import { readFile, stat } from "node:fs/promises";
25023
25435
  import { promisify as promisify7 } from "node:util";
25024
- import * as path8 from "node:path";
25436
+ import * as path15 from "node:path";
25025
25437
 
25026
25438
  // src/git/pre-turn-snapshot.ts
25027
- import * as fs8 from "node:fs";
25028
- import * as path7 from "node:path";
25439
+ import * as fs14 from "node:fs";
25440
+ import * as path14 from "node:path";
25029
25441
  import { execFile as execFile6 } from "node:child_process";
25030
25442
  import { promisify as promisify6 } from "node:util";
25031
25443
 
25032
25444
  // src/git/discover-repos.ts
25033
- import * as fs7 from "node:fs";
25034
- import * as path6 from "node:path";
25445
+ import * as fs13 from "node:fs";
25446
+ import * as path13 from "node:path";
25035
25447
 
25036
25448
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
25037
25449
  var import_file_exists = __toESM(require_dist(), 1);
@@ -25070,8 +25482,8 @@ function pathspec(...paths) {
25070
25482
  cache.set(key, paths);
25071
25483
  return key;
25072
25484
  }
25073
- function isPathSpec(path35) {
25074
- return path35 instanceof String && cache.has(path35);
25485
+ function isPathSpec(path41) {
25486
+ return path41 instanceof String && cache.has(path41);
25075
25487
  }
25076
25488
  function toPaths(pathSpec) {
25077
25489
  return cache.get(pathSpec) || [];
@@ -25160,8 +25572,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
25160
25572
  function forEachLineWithContent(input, callback) {
25161
25573
  return toLinesWithContent(input, true).map((line) => callback(line));
25162
25574
  }
25163
- function folderExists(path35) {
25164
- return (0, import_file_exists.exists)(path35, import_file_exists.FOLDER);
25575
+ function folderExists(path41) {
25576
+ return (0, import_file_exists.exists)(path41, import_file_exists.FOLDER);
25165
25577
  }
25166
25578
  function append(target, item) {
25167
25579
  if (Array.isArray(target)) {
@@ -25565,8 +25977,8 @@ function checkIsRepoRootTask() {
25565
25977
  commands,
25566
25978
  format: "utf-8",
25567
25979
  onError,
25568
- parser(path35) {
25569
- return /^\.(git)?$/.test(path35.trim());
25980
+ parser(path41) {
25981
+ return /^\.(git)?$/.test(path41.trim());
25570
25982
  }
25571
25983
  };
25572
25984
  }
@@ -26000,11 +26412,11 @@ function parseGrep(grep) {
26000
26412
  const paths = /* @__PURE__ */ new Set();
26001
26413
  const results = {};
26002
26414
  forEachLineWithContent(grep, (input) => {
26003
- const [path35, line, preview] = input.split(NULL);
26004
- paths.add(path35);
26005
- (results[path35] = results[path35] || []).push({
26415
+ const [path41, line, preview] = input.split(NULL);
26416
+ paths.add(path41);
26417
+ (results[path41] = results[path41] || []).push({
26006
26418
  line: asNumber(line),
26007
- path: path35,
26419
+ path: path41,
26008
26420
  preview
26009
26421
  });
26010
26422
  });
@@ -26769,14 +27181,14 @@ var init_hash_object = __esm2({
26769
27181
  init_task();
26770
27182
  }
26771
27183
  });
26772
- function parseInit(bare, path35, text) {
27184
+ function parseInit(bare, path41, text) {
26773
27185
  const response = String(text).trim();
26774
27186
  let result;
26775
27187
  if (result = initResponseRegex.exec(response)) {
26776
- return new InitSummary(bare, path35, false, result[1]);
27188
+ return new InitSummary(bare, path41, false, result[1]);
26777
27189
  }
26778
27190
  if (result = reInitResponseRegex.exec(response)) {
26779
- return new InitSummary(bare, path35, true, result[1]);
27191
+ return new InitSummary(bare, path41, true, result[1]);
26780
27192
  }
26781
27193
  let gitDir = "";
26782
27194
  const tokens = response.split(" ");
@@ -26787,7 +27199,7 @@ function parseInit(bare, path35, text) {
26787
27199
  break;
26788
27200
  }
26789
27201
  }
26790
- return new InitSummary(bare, path35, /^re/i.test(response), gitDir);
27202
+ return new InitSummary(bare, path41, /^re/i.test(response), gitDir);
26791
27203
  }
26792
27204
  var InitSummary;
26793
27205
  var initResponseRegex;
@@ -26796,9 +27208,9 @@ var init_InitSummary = __esm2({
26796
27208
  "src/lib/responses/InitSummary.ts"() {
26797
27209
  "use strict";
26798
27210
  InitSummary = class {
26799
- constructor(bare, path35, existing, gitDir) {
27211
+ constructor(bare, path41, existing, gitDir) {
26800
27212
  this.bare = bare;
26801
- this.path = path35;
27213
+ this.path = path41;
26802
27214
  this.existing = existing;
26803
27215
  this.gitDir = gitDir;
26804
27216
  }
@@ -26810,7 +27222,7 @@ var init_InitSummary = __esm2({
26810
27222
  function hasBareCommand(command) {
26811
27223
  return command.includes(bareCommand);
26812
27224
  }
26813
- function initTask(bare = false, path35, customArgs) {
27225
+ function initTask(bare = false, path41, customArgs) {
26814
27226
  const commands = ["init", ...customArgs];
26815
27227
  if (bare && !hasBareCommand(commands)) {
26816
27228
  commands.splice(1, 0, bareCommand);
@@ -26819,7 +27231,7 @@ function initTask(bare = false, path35, customArgs) {
26819
27231
  commands,
26820
27232
  format: "utf-8",
26821
27233
  parser(text) {
26822
- return parseInit(commands.includes("--bare"), path35, text);
27234
+ return parseInit(commands.includes("--bare"), path41, text);
26823
27235
  }
26824
27236
  };
26825
27237
  }
@@ -27635,12 +28047,12 @@ var init_FileStatusSummary = __esm2({
27635
28047
  "use strict";
27636
28048
  fromPathRegex = /^(.+)\0(.+)$/;
27637
28049
  FileStatusSummary = class {
27638
- constructor(path35, index, working_dir) {
27639
- this.path = path35;
28050
+ constructor(path41, index, working_dir) {
28051
+ this.path = path41;
27640
28052
  this.index = index;
27641
28053
  this.working_dir = working_dir;
27642
28054
  if (index === "R" || working_dir === "R") {
27643
- const detail = fromPathRegex.exec(path35) || [null, path35, path35];
28055
+ const detail = fromPathRegex.exec(path41) || [null, path41, path41];
27644
28056
  this.from = detail[2] || "";
27645
28057
  this.path = detail[1] || "";
27646
28058
  }
@@ -27671,14 +28083,14 @@ function splitLine(result, lineStr) {
27671
28083
  default:
27672
28084
  return;
27673
28085
  }
27674
- function data(index, workingDir, path35) {
28086
+ function data(index, workingDir, path41) {
27675
28087
  const raw = `${index}${workingDir}`;
27676
28088
  const handler = parsers6.get(raw);
27677
28089
  if (handler) {
27678
- handler(result, path35);
28090
+ handler(result, path41);
27679
28091
  }
27680
28092
  if (raw !== "##" && raw !== "!!") {
27681
- result.files.push(new FileStatusSummary(path35, index, workingDir));
28093
+ result.files.push(new FileStatusSummary(path41, index, workingDir));
27682
28094
  }
27683
28095
  }
27684
28096
  }
@@ -27951,15 +28363,15 @@ var init_simple_git_api = __esm2({
27951
28363
  this._executor = _executor;
27952
28364
  }
27953
28365
  _runTask(task, then) {
27954
- const chain = this._executor.chain();
27955
- const promise2 = chain.push(task);
28366
+ const chain2 = this._executor.chain();
28367
+ const promise2 = chain2.push(task);
27956
28368
  if (then) {
27957
28369
  taskCallback(task, promise2, then);
27958
28370
  }
27959
28371
  return Object.create(this, {
27960
28372
  then: { value: promise2.then.bind(promise2) },
27961
28373
  catch: { value: promise2.catch.bind(promise2) },
27962
- _executor: { value: chain }
28374
+ _executor: { value: chain2 }
27963
28375
  });
27964
28376
  }
27965
28377
  add(files) {
@@ -27987,9 +28399,9 @@ var init_simple_git_api = __esm2({
27987
28399
  next
27988
28400
  );
27989
28401
  }
27990
- hashObject(path35, write) {
28402
+ hashObject(path41, write) {
27991
28403
  return this._runTask(
27992
- hashObjectTask(path35, write === true),
28404
+ hashObjectTask(path41, write === true),
27993
28405
  trailingFunctionArgument(arguments)
27994
28406
  );
27995
28407
  }
@@ -28342,8 +28754,8 @@ var init_branch = __esm2({
28342
28754
  }
28343
28755
  });
28344
28756
  function toPath(input) {
28345
- const path35 = input.trim().replace(/^["']|["']$/g, "");
28346
- return path35 && normalize2(path35);
28757
+ const path41 = input.trim().replace(/^["']|["']$/g, "");
28758
+ return path41 && normalize2(path41);
28347
28759
  }
28348
28760
  var parseCheckIgnore;
28349
28761
  var init_CheckIgnore = __esm2({
@@ -28657,8 +29069,8 @@ __export2(sub_module_exports, {
28657
29069
  subModuleTask: () => subModuleTask,
28658
29070
  updateSubModuleTask: () => updateSubModuleTask
28659
29071
  });
28660
- function addSubModuleTask(repo, path35) {
28661
- return subModuleTask(["add", repo, path35]);
29072
+ function addSubModuleTask(repo, path41) {
29073
+ return subModuleTask(["add", repo, path41]);
28662
29074
  }
28663
29075
  function initSubModuleTask(customArgs) {
28664
29076
  return subModuleTask(["init", ...customArgs]);
@@ -28991,8 +29403,8 @@ var require_git = __commonJS2({
28991
29403
  }
28992
29404
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
28993
29405
  };
28994
- Git2.prototype.submoduleAdd = function(repo, path35, then) {
28995
- return this._runTask(addSubModuleTask2(repo, path35), trailingFunctionArgument2(arguments));
29406
+ Git2.prototype.submoduleAdd = function(repo, path41, then) {
29407
+ return this._runTask(addSubModuleTask2(repo, path41), trailingFunctionArgument2(arguments));
28996
29408
  };
28997
29409
  Git2.prototype.submoduleUpdate = function(args, then) {
28998
29410
  return this._runTask(
@@ -29636,20 +30048,20 @@ async function isGitRepoDirectory(dirPath) {
29636
30048
  // src/git/discover-repos.ts
29637
30049
  async function discoverGitRepos(cwd = getBridgeRoot()) {
29638
30050
  const result = [];
29639
- const cwdResolved = path6.resolve(cwd);
30051
+ const cwdResolved = path13.resolve(cwd);
29640
30052
  if (await isGitRepoDirectory(cwdResolved)) {
29641
30053
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
29642
30054
  result.push({ absolutePath: cwdResolved, remoteUrl });
29643
30055
  }
29644
30056
  let entries;
29645
30057
  try {
29646
- entries = fs7.readdirSync(cwdResolved, { withFileTypes: true });
30058
+ entries = fs13.readdirSync(cwdResolved, { withFileTypes: true });
29647
30059
  } catch {
29648
30060
  return result;
29649
30061
  }
29650
30062
  for (const ent of entries) {
29651
30063
  if (!ent.isDirectory()) continue;
29652
- const childPath = path6.join(cwdResolved, ent.name);
30064
+ const childPath = path13.join(cwdResolved, ent.name);
29653
30065
  if (await isGitRepoDirectory(childPath)) {
29654
30066
  const remoteUrl = await getRemoteOriginUrl(childPath);
29655
30067
  result.push({ absolutePath: childPath, remoteUrl });
@@ -29658,22 +30070,22 @@ async function discoverGitRepos(cwd = getBridgeRoot()) {
29658
30070
  return result;
29659
30071
  }
29660
30072
  async function discoverGitReposUnderRoot(rootPath) {
29661
- const root = path6.resolve(rootPath);
30073
+ const root = path13.resolve(rootPath);
29662
30074
  const roots = [];
29663
30075
  async function walk(dir) {
29664
30076
  if (await isGitRepoDirectory(dir)) {
29665
- roots.push(path6.resolve(dir));
30077
+ roots.push(path13.resolve(dir));
29666
30078
  return;
29667
30079
  }
29668
30080
  let entries;
29669
30081
  try {
29670
- entries = fs7.readdirSync(dir, { withFileTypes: true });
30082
+ entries = fs13.readdirSync(dir, { withFileTypes: true });
29671
30083
  } catch {
29672
30084
  return;
29673
30085
  }
29674
30086
  for (const ent of entries) {
29675
30087
  if (!ent.isDirectory() || ent.name === ".git") continue;
29676
- await walk(path6.join(dir, ent.name));
30088
+ await walk(path13.join(dir, ent.name));
29677
30089
  }
29678
30090
  }
29679
30091
  await walk(root);
@@ -29689,7 +30101,7 @@ async function discoverGitReposUnderRoot(rootPath) {
29689
30101
  // src/git/pre-turn-snapshot.ts
29690
30102
  var execFileAsync5 = promisify6(execFile6);
29691
30103
  function snapshotsDirForCwd(agentCwd) {
29692
- return path7.join(agentCwd, ".buildautomaton", "snapshots");
30104
+ return path14.join(agentCwd, ".buildautomaton", "snapshots");
29693
30105
  }
29694
30106
  async function gitStashCreate(repoRoot, log2) {
29695
30107
  try {
@@ -29718,7 +30130,7 @@ async function gitRun(repoRoot, args, log2, label) {
29718
30130
  async function resolveSnapshotRepoRoots(options) {
29719
30131
  const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
29720
30132
  if (worktreePaths?.length) {
29721
- const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
30133
+ const uniq = [...new Set(worktreePaths.map((p) => path14.resolve(p)))];
29722
30134
  return uniq;
29723
30135
  }
29724
30136
  try {
@@ -29726,7 +30138,7 @@ async function resolveSnapshotRepoRoots(options) {
29726
30138
  const mapped = repos.map((r) => r.absolutePath);
29727
30139
  const sid = sessionId?.trim();
29728
30140
  if (sid) {
29729
- const filtered = mapped.filter((root) => path7.basename(root) === sid);
30141
+ const filtered = mapped.filter((root) => path14.basename(root) === sid);
29730
30142
  if (filtered.length > 0) return filtered;
29731
30143
  }
29732
30144
  return mapped;
@@ -29747,7 +30159,7 @@ async function capturePreTurnSnapshot(options) {
29747
30159
  }
29748
30160
  const dir = snapshotsDirForCwd(agentCwd);
29749
30161
  try {
29750
- fs8.mkdirSync(dir, { recursive: true });
30162
+ fs14.mkdirSync(dir, { recursive: true });
29751
30163
  } catch (e) {
29752
30164
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
29753
30165
  }
@@ -29756,9 +30168,9 @@ async function capturePreTurnSnapshot(options) {
29756
30168
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
29757
30169
  repos
29758
30170
  };
29759
- const filePath = path7.join(dir, `${runId}.json`);
30171
+ const filePath = path14.join(dir, `${runId}.json`);
29760
30172
  try {
29761
- fs8.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
30173
+ fs14.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
29762
30174
  } catch (e) {
29763
30175
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
29764
30176
  }
@@ -29771,7 +30183,7 @@ async function capturePreTurnSnapshot(options) {
29771
30183
  async function applyPreTurnSnapshot(filePath, log2) {
29772
30184
  let data;
29773
30185
  try {
29774
- const raw = fs8.readFileSync(filePath, "utf8");
30186
+ const raw = fs14.readFileSync(filePath, "utf8");
29775
30187
  data = JSON.parse(raw);
29776
30188
  } catch (e) {
29777
30189
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
@@ -29794,7 +30206,7 @@ async function applyPreTurnSnapshot(filePath, log2) {
29794
30206
  return { ok: true };
29795
30207
  }
29796
30208
  function snapshotFilePath(agentCwd, runId) {
29797
- return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
30209
+ return path14.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
29798
30210
  }
29799
30211
 
29800
30212
  // src/git/session-git-queue.ts
@@ -29843,7 +30255,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
29843
30255
  continue;
29844
30256
  }
29845
30257
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
29846
- const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
30258
+ const slug = path15.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
29847
30259
  for (const rel of lines) {
29848
30260
  if (rel.includes("..")) continue;
29849
30261
  try {
@@ -29857,7 +30269,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
29857
30269
  );
29858
30270
  if (!patchContent.trim()) continue;
29859
30271
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
29860
- const workspaceFilePath = path8.join(repo.path, rel);
30272
+ const workspaceFilePath = path15.join(repo.path, rel);
29861
30273
  const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
29862
30274
  sendSessionUpdate({
29863
30275
  type: "session_file_change",
@@ -29879,9 +30291,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
29879
30291
  // src/agents/acp/put-summarize-change-summaries.ts
29880
30292
  async function putEncryptedChangeSummaryRows(params) {
29881
30293
  const base = params.apiBaseUrl.replace(/\/+$/, "");
29882
- const entries = params.rows.map(({ path: path35, summary }) => {
30294
+ const entries = params.rows.map(({ path: path41, summary }) => {
29883
30295
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
29884
- return { path: path35, summary: JSON.stringify(enc) };
30296
+ return { path: path41, summary: JSON.stringify(enc) };
29885
30297
  });
29886
30298
  const res = await fetch(
29887
30299
  `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -30218,8 +30630,8 @@ async function sendPromptToAgent(options) {
30218
30630
  }
30219
30631
 
30220
30632
  // src/agents/acp/ensure-acp-client.ts
30221
- import * as fs10 from "node:fs";
30222
- import * as path12 from "node:path";
30633
+ import * as fs15 from "node:fs";
30634
+ import * as path18 from "node:path";
30223
30635
 
30224
30636
  // src/error-message.ts
30225
30637
  function errorMessage(err) {
@@ -30414,7 +30826,7 @@ async function createCursorAcpClient(options) {
30414
30826
  logDebug,
30415
30827
  getStderrText: () => stderrCapture.getText()
30416
30828
  };
30417
- return new Promise((resolve16, reject) => {
30829
+ return new Promise((resolve18, reject) => {
30418
30830
  child.on("error", (err) => {
30419
30831
  child.kill();
30420
30832
  reject(new Error(formatSpawnError2(err, command[0])));
@@ -30493,12 +30905,16 @@ async function createCursorAcpClient(options) {
30493
30905
  }
30494
30906
  if (method === "session/request_permission" && typeof id === "number") {
30495
30907
  const params = msg.params ?? {};
30496
- pendingRequests.set(id, { method, params });
30497
- onRequest?.({
30498
- requestId: String(id),
30499
- method,
30500
- params
30501
- });
30908
+ if (onRequest) {
30909
+ pendingRequests.set(id, { method, params });
30910
+ onRequest({
30911
+ requestId: String(id),
30912
+ method,
30913
+ params
30914
+ });
30915
+ } else {
30916
+ respond(id, { outcome: { outcome: "denied" } });
30917
+ }
30502
30918
  return;
30503
30919
  }
30504
30920
  if (typeof id === "number" && method) {
@@ -30586,7 +31002,7 @@ async function createCursorAcpClient(options) {
30586
31002
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
30587
31003
  });
30588
31004
  const sessionId = established.sessionId;
30589
- resolve16({
31005
+ resolve18({
30590
31006
  sessionId,
30591
31007
  async sendPrompt(prompt, options2) {
30592
31008
  const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
@@ -30721,20 +31137,20 @@ function resolveAgentCommand(preferredAgentType) {
30721
31137
 
30722
31138
  // src/agents/acp/session-file-change-path-kind.ts
30723
31139
  import { execFileSync as execFileSync4 } from "node:child_process";
30724
- import { existsSync, statSync } from "node:fs";
31140
+ import { existsSync as existsSync2, statSync } from "node:fs";
30725
31141
 
30726
31142
  // src/git/get-git-repo-root-sync.ts
30727
31143
  import { execFileSync as execFileSync2 } from "node:child_process";
30728
- import * as path9 from "node:path";
31144
+ import * as path16 from "node:path";
30729
31145
  function getGitRepoRootSync(startDir) {
30730
31146
  try {
30731
31147
  const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
30732
- cwd: path9.resolve(startDir),
31148
+ cwd: path16.resolve(startDir),
30733
31149
  encoding: "utf8",
30734
31150
  stdio: ["ignore", "pipe", "ignore"],
30735
31151
  maxBuffer: 1024 * 1024
30736
31152
  }).trim();
30737
- return out ? path9.resolve(out) : null;
31153
+ return out ? path16.resolve(out) : null;
30738
31154
  } catch {
30739
31155
  return null;
30740
31156
  }
@@ -30742,26 +31158,26 @@ function getGitRepoRootSync(startDir) {
30742
31158
 
30743
31159
  // src/agents/acp/workspace-files.ts
30744
31160
  import { execFileSync as execFileSync3 } from "node:child_process";
30745
- import { readFileSync as readFileSync3 } from "node:fs";
30746
- import * as path10 from "node:path";
31161
+ import { readFileSync as readFileSync4 } from "node:fs";
31162
+ import * as path17 from "node:path";
30747
31163
  function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
30748
31164
  const trimmed2 = rawPath.trim();
30749
31165
  if (!trimmed2) return null;
30750
- const normalizedSessionParent = path10.resolve(sessionParentPath);
31166
+ const normalizedSessionParent = path17.resolve(sessionParentPath);
30751
31167
  let resolvedPath = resolveSafePathUnderCwd(sessionParentPath, trimmed2);
30752
31168
  if (!resolvedPath) {
30753
- const candidate = path10.isAbsolute(trimmed2) ? path10.normalize(trimmed2) : path10.normalize(path10.resolve(normalizedSessionParent, trimmed2));
31169
+ const candidate = path17.isAbsolute(trimmed2) ? path17.normalize(trimmed2) : path17.normalize(path17.resolve(normalizedSessionParent, trimmed2));
30754
31170
  const gitRoot2 = getGitRepoRootSync(sessionParentPath);
30755
31171
  if (!gitRoot2) return null;
30756
- const rel = path10.relative(gitRoot2, candidate);
30757
- if (rel.startsWith("..") || path10.isAbsolute(rel)) return null;
31172
+ const rel = path17.relative(gitRoot2, candidate);
31173
+ if (rel.startsWith("..") || path17.isAbsolute(rel)) return null;
30758
31174
  resolvedPath = candidate;
30759
31175
  }
30760
31176
  const gitRoot = getGitRepoRootSync(sessionParentPath);
30761
31177
  if (gitRoot) {
30762
- const relFromRoot = path10.relative(gitRoot, resolvedPath);
30763
- if (!relFromRoot.startsWith("..") && !path10.isAbsolute(relFromRoot)) {
30764
- return { resolvedPath, display: relFromRoot.split(path10.sep).join("/") };
31178
+ const relFromRoot = path17.relative(gitRoot, resolvedPath);
31179
+ if (!relFromRoot.startsWith("..") && !path17.isAbsolute(relFromRoot)) {
31180
+ return { resolvedPath, display: relFromRoot.split(path17.sep).join("/") };
30765
31181
  }
30766
31182
  }
30767
31183
  return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
@@ -30770,11 +31186,11 @@ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
30770
31186
  if (!displayPath || displayPath.includes("..")) return "";
30771
31187
  const gitRoot = getGitRepoRootSync(sessionParentPath);
30772
31188
  if (gitRoot) {
30773
- const resolvedPath2 = path10.resolve(gitRoot, displayPath);
30774
- const rel = path10.relative(gitRoot, resolvedPath2);
30775
- if (!rel.startsWith("..") && !path10.isAbsolute(rel)) {
31189
+ const resolvedPath2 = path17.resolve(gitRoot, displayPath);
31190
+ const rel = path17.relative(gitRoot, resolvedPath2);
31191
+ if (!rel.startsWith("..") && !path17.isAbsolute(rel)) {
30776
31192
  try {
30777
- return readFileSync3(resolvedPath2, "utf8");
31193
+ return readFileSync4(resolvedPath2, "utf8");
30778
31194
  } catch {
30779
31195
  }
30780
31196
  }
@@ -30782,7 +31198,7 @@ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
30782
31198
  const resolvedPath = resolveSafePathUnderCwd(sessionParentPath, displayPath);
30783
31199
  if (!resolvedPath) return "";
30784
31200
  try {
30785
- return readFileSync3(resolvedPath, "utf8");
31201
+ return readFileSync4(resolvedPath, "utf8");
30786
31202
  } catch {
30787
31203
  return "";
30788
31204
  }
@@ -30791,9 +31207,9 @@ function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
30791
31207
  if (!displayPath || displayPath.includes("..")) return null;
30792
31208
  const gitRoot = getGitRepoRootSync(sessionParentPath);
30793
31209
  if (gitRoot) {
30794
- const resolvedPath = path10.resolve(gitRoot, displayPath);
30795
- const rel = path10.relative(gitRoot, resolvedPath);
30796
- if (!rel.startsWith("..") && !path10.isAbsolute(rel)) return resolvedPath;
31210
+ const resolvedPath = path17.resolve(gitRoot, displayPath);
31211
+ const rel = path17.relative(gitRoot, resolvedPath);
31212
+ if (!rel.startsWith("..") && !path17.isAbsolute(rel)) return resolvedPath;
30797
31213
  }
30798
31214
  return resolveSafePathUnderCwd(sessionParentPath, displayPath);
30799
31215
  }
@@ -30828,7 +31244,7 @@ function gitHeadPathObjectType(sessionParentPath, displayPath) {
30828
31244
  }
30829
31245
  function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
30830
31246
  const resolvedPath = tryWorkspaceDisplayToPath(sessionParentPath, displayPath);
30831
- if (resolvedPath && existsSync(resolvedPath)) {
31247
+ if (resolvedPath && existsSync2(resolvedPath)) {
30832
31248
  try {
30833
31249
  if (statSync(resolvedPath).isDirectory()) {
30834
31250
  return { isDirectory: true, directoryRemoved: false };
@@ -30928,7 +31344,7 @@ function createBridgeOnRequest(opts) {
30928
31344
  }
30929
31345
 
30930
31346
  // src/agents/acp/hooks/extract-acp-file-diffs-from-update/paths-and-text.ts
30931
- import { fileURLToPath as fileURLToPath2 } from "node:url";
31347
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
30932
31348
  function readOptionalTextField(v) {
30933
31349
  if (v === null || v === void 0) return "";
30934
31350
  if (typeof v === "string") return v;
@@ -30938,7 +31354,7 @@ function normalizePathField(raw) {
30938
31354
  const t = raw.trim();
30939
31355
  if (t.startsWith("file://")) {
30940
31356
  try {
30941
- return fileURLToPath2(t);
31357
+ return fileURLToPath3(t);
30942
31358
  } catch {
30943
31359
  return t;
30944
31360
  }
@@ -31396,29 +31812,34 @@ function buildAcpSessionBridgeHooks(opts) {
31396
31812
  }
31397
31813
 
31398
31814
  // src/agents/acp/local-agent-session-file.ts
31399
- import fs9 from "node:fs";
31400
- import os3 from "node:os";
31401
- import path11 from "node:path";
31402
- var LOCAL_AGENT_SESSION_DIR = path11.join(os3.homedir(), ".buildautomaton", "agent-sessions");
31403
- function safeFileSlug(cloudSessionId) {
31815
+ function sessionKeyForCloudSessionId(cloudSessionId) {
31404
31816
  const t = cloudSessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 220);
31405
31817
  return t.length > 0 ? t : "session";
31406
31818
  }
31407
- function localAgentSessionFilePath(cloudSessionId) {
31408
- return path11.join(LOCAL_AGENT_SESSION_DIR, `${safeFileSlug(cloudSessionId)}.json`);
31409
- }
31410
31819
  function readLocalAgentSessionFile(cloudSessionId) {
31411
31820
  try {
31412
- const p = localAgentSessionFilePath(cloudSessionId);
31413
- const raw = fs9.readFileSync(p, "utf8");
31414
- const parsed = JSON.parse(raw);
31415
- if (parsed.v !== 1) return null;
31821
+ const db = getCliDatabase();
31822
+ const key = sessionKeyForCloudSessionId(cloudSessionId);
31823
+ const row = db.get(
31824
+ "SELECT acp_session_id, backend_agent_type, config_options_json, updated_at FROM agent_session WHERE session_key = ?",
31825
+ [key]
31826
+ );
31827
+ if (!row) return null;
31828
+ let configOptions = null;
31829
+ if (row.config_options_json != null && row.config_options_json !== "") {
31830
+ try {
31831
+ const parsed = JSON.parse(row.config_options_json);
31832
+ configOptions = Array.isArray(parsed) ? parsed : null;
31833
+ } catch {
31834
+ configOptions = null;
31835
+ }
31836
+ }
31416
31837
  return {
31417
31838
  v: 1,
31418
- acpSessionId: typeof parsed.acpSessionId === "string" ? parsed.acpSessionId : null,
31419
- backendAgentType: typeof parsed.backendAgentType === "string" ? parsed.backendAgentType : null,
31420
- configOptions: Array.isArray(parsed.configOptions) ? parsed.configOptions : null,
31421
- updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
31839
+ acpSessionId: row.acp_session_id,
31840
+ backendAgentType: row.backend_agent_type,
31841
+ configOptions,
31842
+ updatedAt: row.updated_at
31422
31843
  };
31423
31844
  } catch {
31424
31845
  return null;
@@ -31426,9 +31847,8 @@ function readLocalAgentSessionFile(cloudSessionId) {
31426
31847
  }
31427
31848
  function writeLocalAgentSessionFile(cloudSessionId, patch) {
31428
31849
  try {
31429
- const dir = LOCAL_AGENT_SESSION_DIR;
31430
- if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
31431
- const p = localAgentSessionFilePath(cloudSessionId);
31850
+ const db = getCliDatabase();
31851
+ const key = sessionKeyForCloudSessionId(cloudSessionId);
31432
31852
  const prev = readLocalAgentSessionFile(cloudSessionId);
31433
31853
  const next = {
31434
31854
  v: 1,
@@ -31437,7 +31857,17 @@ function writeLocalAgentSessionFile(cloudSessionId, patch) {
31437
31857
  configOptions: patch.configOptions !== void 0 ? patch.configOptions : prev?.configOptions ?? null,
31438
31858
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
31439
31859
  };
31440
- fs9.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
31860
+ const configJson = next.configOptions != null ? JSON.stringify(next.configOptions) : null;
31861
+ db.run(
31862
+ `INSERT INTO agent_session (session_key, acp_session_id, backend_agent_type, config_options_json, updated_at)
31863
+ VALUES (?, ?, ?, ?, ?)
31864
+ ON CONFLICT(session_key) DO UPDATE SET
31865
+ acp_session_id = excluded.acp_session_id,
31866
+ backend_agent_type = excluded.backend_agent_type,
31867
+ config_options_json = excluded.config_options_json,
31868
+ updated_at = excluded.updated_at`,
31869
+ [key, next.acpSessionId, next.backendAgentType, configJson, next.updatedAt]
31870
+ );
31441
31871
  } catch {
31442
31872
  }
31443
31873
  }
@@ -31452,6 +31882,7 @@ async function ensureAcpClient(options) {
31452
31882
  sessionParentPath,
31453
31883
  routing,
31454
31884
  cloudSessionId,
31885
+ reportAgentCapabilities,
31455
31886
  sendSessionUpdate,
31456
31887
  sendRequest,
31457
31888
  log: log2
@@ -31461,7 +31892,7 @@ async function ensureAcpClient(options) {
31461
31892
  if (state.acpStartPromise && !state.acpHandle) {
31462
31893
  await state.acpStartPromise;
31463
31894
  }
31464
- if (state.acpHandle && state.lastAcpCwd != null && path12.resolve(state.lastAcpCwd) !== path12.resolve(targetSessionParentPath)) {
31895
+ if (state.acpHandle && state.lastAcpCwd != null && path18.resolve(state.lastAcpCwd) !== path18.resolve(targetSessionParentPath)) {
31465
31896
  try {
31466
31897
  state.acpHandle.disconnect();
31467
31898
  } catch {
@@ -31496,7 +31927,7 @@ async function ensureAcpClient(options) {
31496
31927
  if (!state.acpStartPromise) {
31497
31928
  let statOk = false;
31498
31929
  try {
31499
- const st = fs10.statSync(targetSessionParentPath);
31930
+ const st = fs15.statSync(targetSessionParentPath);
31500
31931
  statOk = st.isDirectory();
31501
31932
  if (!statOk) {
31502
31933
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
@@ -31539,6 +31970,12 @@ async function ensureAcpClient(options) {
31539
31970
  backendAgentType: preferredAgentType
31540
31971
  });
31541
31972
  }
31973
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(info.configOptions) && info.configOptions.length > 0) {
31974
+ reportAgentCapabilities({
31975
+ agentType: preferredAgentType,
31976
+ configOptions: info.configOptions
31977
+ });
31978
+ }
31542
31979
  },
31543
31980
  onAcpConfigOptionsUpdated: (configOptions) => {
31544
31981
  state.activeSessionConfigOptions = configOptions;
@@ -31548,6 +31985,12 @@ async function ensureAcpClient(options) {
31548
31985
  backendAgentType: preferredAgentType
31549
31986
  });
31550
31987
  }
31988
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(configOptions) && configOptions.length > 0) {
31989
+ reportAgentCapabilities({
31990
+ agentType: preferredAgentType,
31991
+ configOptions
31992
+ });
31993
+ }
31551
31994
  },
31552
31995
  onAgentSubprocessExit: () => {
31553
31996
  state.acpHandle = null;
@@ -31578,7 +32021,7 @@ async function ensureAcpClient(options) {
31578
32021
 
31579
32022
  // src/agents/acp/create-acp-manager.ts
31580
32023
  async function createAcpManager(options) {
31581
- const { log: log2 } = options;
32024
+ const { log: log2, reportAgentCapabilities } = options;
31582
32025
  const state = {
31583
32026
  acpHandle: null,
31584
32027
  acpStartPromise: null,
@@ -31622,7 +32065,8 @@ async function createAcpManager(options) {
31622
32065
  cloudApiBaseUrl,
31623
32066
  getCloudAccessToken,
31624
32067
  e2ee,
31625
- attachments
32068
+ attachments,
32069
+ agentId
31626
32070
  } = opts;
31627
32071
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
31628
32072
  pendingCancelRunId = void 0;
@@ -31640,7 +32084,8 @@ async function createAcpManager(options) {
31640
32084
  cloudSessionId: sessionId,
31641
32085
  sendSessionUpdate,
31642
32086
  sendRequest: sendSessionUpdate,
31643
- log: log2
32087
+ log: log2,
32088
+ reportAgentCapabilities
31644
32089
  });
31645
32090
  if (!handle) {
31646
32091
  const errMsg = state.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app.";
@@ -31743,12 +32188,12 @@ async function createAcpManager(options) {
31743
32188
  }
31744
32189
 
31745
32190
  // src/worktrees/session-worktree-manager.ts
31746
- import * as path19 from "node:path";
31747
- import os5 from "node:os";
32191
+ import * as path25 from "node:path";
32192
+ import os8 from "node:os";
31748
32193
 
31749
32194
  // src/worktrees/prepare-new-session-worktrees.ts
31750
- import * as fs12 from "node:fs";
31751
- import * as path14 from "node:path";
32195
+ import * as fs17 from "node:fs";
32196
+ import * as path20 from "node:path";
31752
32197
 
31753
32198
  // src/git/worktree-add.ts
31754
32199
  async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
@@ -31757,12 +32202,12 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
31757
32202
  }
31758
32203
 
31759
32204
  // src/worktrees/worktree-layout-file.ts
31760
- import * as fs11 from "node:fs";
31761
- import * as path13 from "node:path";
31762
- import os4 from "node:os";
32205
+ import * as fs16 from "node:fs";
32206
+ import * as path19 from "node:path";
32207
+ import os7 from "node:os";
31763
32208
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
31764
32209
  function defaultWorktreeLayoutPath() {
31765
- return path13.join(os4.homedir(), ".buildautomaton", LAYOUT_FILENAME);
32210
+ return path19.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
31766
32211
  }
31767
32212
  function normalizeLoadedLayout(raw) {
31768
32213
  if (raw && typeof raw === "object" && "launcherCwds" in raw) {
@@ -31774,8 +32219,8 @@ function normalizeLoadedLayout(raw) {
31774
32219
  function loadWorktreeLayout() {
31775
32220
  try {
31776
32221
  const p = defaultWorktreeLayoutPath();
31777
- if (!fs11.existsSync(p)) return { launcherCwds: [] };
31778
- const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
32222
+ if (!fs16.existsSync(p)) return { launcherCwds: [] };
32223
+ const raw = JSON.parse(fs16.readFileSync(p, "utf8"));
31779
32224
  return normalizeLoadedLayout(raw);
31780
32225
  } catch {
31781
32226
  return { launcherCwds: [] };
@@ -31783,24 +32228,24 @@ function loadWorktreeLayout() {
31783
32228
  }
31784
32229
  function saveWorktreeLayout(layout) {
31785
32230
  try {
31786
- const dir = path13.dirname(defaultWorktreeLayoutPath());
31787
- fs11.mkdirSync(dir, { recursive: true });
31788
- fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
32231
+ const dir = path19.dirname(defaultWorktreeLayoutPath());
32232
+ fs16.mkdirSync(dir, { recursive: true });
32233
+ fs16.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
31789
32234
  } catch {
31790
32235
  }
31791
32236
  }
31792
32237
  function baseNameSafe(pathString) {
31793
- return path13.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
32238
+ return path19.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
31794
32239
  }
31795
32240
  function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
31796
- const norm = path13.resolve(bridgeRootPath2);
31797
- const existing = layout.launcherCwds.find((e) => path13.resolve(e.absolutePath) === norm);
32241
+ const norm = path19.resolve(bridgeRootPath2);
32242
+ const existing = layout.launcherCwds.find((e) => path19.resolve(e.absolutePath) === norm);
31798
32243
  return existing?.dirName;
31799
32244
  }
31800
32245
  function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
31801
32246
  const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
31802
32247
  if (existing) return existing;
31803
- const norm = path13.resolve(bridgeRootPath2);
32248
+ const norm = path19.resolve(bridgeRootPath2);
31804
32249
  const base = baseNameSafe(norm);
31805
32250
  const used = new Set(layout.launcherCwds.map((e) => e.dirName));
31806
32251
  let name = base;
@@ -31817,10 +32262,10 @@ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
31817
32262
  // src/worktrees/prepare-new-session-worktrees.ts
31818
32263
  async function prepareNewSessionWorktrees(options) {
31819
32264
  const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
31820
- const bridgeResolved = path14.resolve(bridgeRoot);
32265
+ const bridgeResolved = path20.resolve(bridgeRoot);
31821
32266
  const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
31822
- const bridgeKeyDir = path14.join(worktreesRootPath, cwdKey);
31823
- const sessionDir = path14.join(bridgeKeyDir, sessionId);
32267
+ const bridgeKeyDir = path20.join(worktreesRootPath, cwdKey);
32268
+ const sessionDir = path20.join(bridgeKeyDir, sessionId);
31824
32269
  const repos = await discoverGitReposUnderRoot(bridgeResolved);
31825
32270
  if (repos.length === 0) {
31826
32271
  log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
@@ -31828,14 +32273,14 @@ async function prepareNewSessionWorktrees(options) {
31828
32273
  }
31829
32274
  const branch = `session-${sessionId}`;
31830
32275
  const worktreePaths = [];
31831
- fs12.mkdirSync(sessionDir, { recursive: true });
32276
+ fs17.mkdirSync(sessionDir, { recursive: true });
31832
32277
  for (const repo of repos) {
31833
- let rel = path14.relative(bridgeResolved, repo.absolutePath);
31834
- if (rel.startsWith("..") || path14.isAbsolute(rel)) continue;
32278
+ let rel = path20.relative(bridgeResolved, repo.absolutePath);
32279
+ if (rel.startsWith("..") || path20.isAbsolute(rel)) continue;
31835
32280
  const relNorm = rel === "" ? "." : rel;
31836
- const wtPath = relNorm === "." ? sessionDir : path14.join(sessionDir, relNorm);
32281
+ const wtPath = relNorm === "." ? sessionDir : path20.join(sessionDir, relNorm);
31837
32282
  if (relNorm !== ".") {
31838
- fs12.mkdirSync(path14.dirname(wtPath), { recursive: true });
32283
+ fs17.mkdirSync(path20.dirname(wtPath), { recursive: true });
31839
32284
  }
31840
32285
  try {
31841
32286
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
@@ -31877,23 +32322,23 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
31877
32322
  }
31878
32323
 
31879
32324
  // src/worktrees/remove-session-worktrees.ts
31880
- import * as fs15 from "node:fs";
32325
+ import * as fs20 from "node:fs";
31881
32326
 
31882
32327
  // src/git/worktree-remove.ts
31883
- import * as fs14 from "node:fs";
32328
+ import * as fs19 from "node:fs";
31884
32329
 
31885
32330
  // src/git/resolve-main-repo-from-git-file.ts
31886
- import * as fs13 from "node:fs";
31887
- import * as path15 from "node:path";
32331
+ import * as fs18 from "node:fs";
32332
+ import * as path21 from "node:path";
31888
32333
  function resolveMainRepoFromWorktreeGitFile(wt) {
31889
- const gitDirFile = path15.join(wt, ".git");
31890
- if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
31891
- const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
32334
+ const gitDirFile = path21.join(wt, ".git");
32335
+ if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
32336
+ const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
31892
32337
  const m = first2.match(/^gitdir:\s*(.+)$/im);
31893
32338
  if (!m) return "";
31894
- const gitWorktreePath = path15.resolve(wt, m[1].trim());
31895
- const gitDir = path15.dirname(path15.dirname(gitWorktreePath));
31896
- return path15.dirname(gitDir);
32339
+ const gitWorktreePath = path21.resolve(wt, m[1].trim());
32340
+ const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
32341
+ return path21.dirname(gitDir);
31897
32342
  }
31898
32343
 
31899
32344
  // src/git/worktree-remove.ts
@@ -31902,7 +32347,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
31902
32347
  if (mainRepo) {
31903
32348
  await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
31904
32349
  } else {
31905
- fs14.rmSync(worktreePath, { recursive: true, force: true });
32350
+ fs19.rmSync(worktreePath, { recursive: true, force: true });
31906
32351
  }
31907
32352
  }
31908
32353
 
@@ -31915,7 +32360,7 @@ async function removeSessionWorktrees(paths, log2) {
31915
32360
  } catch (e) {
31916
32361
  log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
31917
32362
  try {
31918
- fs15.rmSync(wt, { recursive: true, force: true });
32363
+ fs20.rmSync(wt, { recursive: true, force: true });
31919
32364
  } catch {
31920
32365
  }
31921
32366
  }
@@ -32135,7 +32580,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
32135
32580
  }
32136
32581
 
32137
32582
  // src/git/working-directory/changes/get-working-tree-change-repo-details.ts
32138
- import * as path17 from "node:path";
32583
+ import * as path23 from "node:path";
32139
32584
 
32140
32585
  // src/git/working-directory/changes/parse-git-status.ts
32141
32586
  function parseNameStatusLines(lines) {
@@ -32255,8 +32700,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
32255
32700
  }
32256
32701
 
32257
32702
  // src/git/working-directory/changes/list-changed-files-for-repo.ts
32258
- import * as fs17 from "node:fs";
32259
- import * as path16 from "node:path";
32703
+ import * as fs22 from "node:fs";
32704
+ import * as path22 from "node:path";
32260
32705
 
32261
32706
  // src/git/working-directory/changes/count-lines.ts
32262
32707
  import { createReadStream } from "node:fs";
@@ -32280,7 +32725,7 @@ async function countTextFileLines(filePath) {
32280
32725
  }
32281
32726
 
32282
32727
  // src/git/working-directory/changes/hydrate-patch.ts
32283
- import * as fs16 from "node:fs";
32728
+ import * as fs21 from "node:fs";
32284
32729
  var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
32285
32730
  var MAX_HYDRATE_LINES_PER_GAP = 8e3;
32286
32731
  var MAX_HYDRATE_LINES_PER_FILE = 8e4;
@@ -32295,7 +32740,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
32295
32740
  }
32296
32741
  async function readWorktreeFileLines(filePath) {
32297
32742
  try {
32298
- const raw = await fs16.promises.readFile(filePath, "utf8");
32743
+ const raw = await fs21.promises.readFile(filePath, "utf8");
32299
32744
  return raw.split(/\r?\n/);
32300
32745
  } catch {
32301
32746
  return null;
@@ -32430,7 +32875,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
32430
32875
  const rows = [];
32431
32876
  for (const pathInRepo of paths) {
32432
32877
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
32433
- const repoFilePath = path16.join(repoGitCwd, pathInRepo);
32878
+ const repoFilePath = path22.join(repoGitCwd, pathInRepo);
32434
32879
  const nums = numByPath.get(pathInRepo);
32435
32880
  let additions = nums?.additions ?? 0;
32436
32881
  let deletions = nums?.deletions ?? 0;
@@ -32443,7 +32888,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
32443
32888
  deletions = fromGit.deletions;
32444
32889
  } else {
32445
32890
  try {
32446
- const st = await fs17.promises.stat(repoFilePath);
32891
+ const st = await fs22.promises.stat(repoFilePath);
32447
32892
  if (st.isFile()) additions = await countTextFileLines(repoFilePath);
32448
32893
  else additions = 0;
32449
32894
  } catch {
@@ -32469,7 +32914,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
32469
32914
  } else {
32470
32915
  pathInRepo = row.pathRelLauncher;
32471
32916
  }
32472
- const filePath = path16.join(repoGitCwd, pathInRepo);
32917
+ const filePath = path22.join(repoGitCwd, pathInRepo);
32473
32918
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
32474
32919
  if (patch) {
32475
32920
  patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
@@ -32485,8 +32930,8 @@ function normRepoRel(p) {
32485
32930
  return x === "" ? "." : x;
32486
32931
  }
32487
32932
  async function getWorkingTreeChangeRepoDetails(options) {
32488
- const bridgeRoot = path17.resolve(getBridgeRoot());
32489
- const sessionWtRoot = options.sessionWorktreeRootPath ? path17.resolve(options.sessionWorktreeRootPath) : null;
32933
+ const bridgeRoot = path23.resolve(getBridgeRoot());
32934
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path23.resolve(options.sessionWorktreeRootPath) : null;
32490
32935
  const legacyNested = options.legacyRepoNestedSessionLayout === true;
32491
32936
  const out = [];
32492
32937
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
@@ -32499,7 +32944,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
32499
32944
  }
32500
32945
  const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
32501
32946
  for (const target of options.commitTargetPaths) {
32502
- const t = path17.resolve(target);
32947
+ const t = path23.resolve(target);
32503
32948
  if (!await isGitRepoDirectory(t)) continue;
32504
32949
  const g = cliSimpleGit(t);
32505
32950
  let branch = "HEAD";
@@ -32512,8 +32957,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
32512
32957
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
32513
32958
  let repoRelPath;
32514
32959
  if (sessionWtRoot) {
32515
- const anchor = legacyNested ? path17.dirname(t) : t;
32516
- const relNorm = path17.relative(sessionWtRoot, anchor);
32960
+ const anchor = legacyNested ? path23.dirname(t) : t;
32961
+ const relNorm = path23.relative(sessionWtRoot, anchor);
32517
32962
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
32518
32963
  } else {
32519
32964
  let top = t;
@@ -32522,8 +32967,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
32522
32967
  } catch {
32523
32968
  top = t;
32524
32969
  }
32525
- const rel = path17.relative(bridgeRoot, path17.resolve(top)).replace(/\\/g, "/") || ".";
32526
- repoRelPath = rel.startsWith("..") ? path17.basename(path17.resolve(top)) : rel;
32970
+ const rel = path23.relative(bridgeRoot, path23.resolve(top)).replace(/\\/g, "/") || ".";
32971
+ repoRelPath = rel.startsWith("..") ? path23.basename(path23.resolve(top)) : rel;
32527
32972
  }
32528
32973
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
32529
32974
  if (filter && norm !== filter) continue;
@@ -32588,11 +33033,11 @@ async function commitSessionWorktrees(options) {
32588
33033
  }
32589
33034
 
32590
33035
  // src/worktrees/discover-session-worktree-on-disk.ts
32591
- import * as fs18 from "node:fs";
32592
- import * as path18 from "node:path";
33036
+ import * as fs23 from "node:fs";
33037
+ import * as path24 from "node:path";
32593
33038
  function isGitDir(dirPath) {
32594
33039
  try {
32595
- return fs18.existsSync(path18.join(dirPath, ".git"));
33040
+ return fs23.existsSync(path24.join(dirPath, ".git"));
32596
33041
  } catch {
32597
33042
  return false;
32598
33043
  }
@@ -32601,23 +33046,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
32601
33046
  const out = [];
32602
33047
  const walk = (dir) => {
32603
33048
  if (isGitDir(dir)) {
32604
- out.push(path18.resolve(dir));
33049
+ out.push(path24.resolve(dir));
32605
33050
  return;
32606
33051
  }
32607
33052
  let entries;
32608
33053
  try {
32609
- entries = fs18.readdirSync(dir, { withFileTypes: true });
33054
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
32610
33055
  } catch {
32611
33056
  return;
32612
33057
  }
32613
33058
  for (const e of entries) {
32614
33059
  if (e.name.startsWith(".")) continue;
32615
- const full = path18.join(dir, e.name);
33060
+ const full = path24.join(dir, e.name);
32616
33061
  if (!e.isDirectory()) continue;
32617
33062
  walk(full);
32618
33063
  }
32619
33064
  };
32620
- walk(path18.resolve(rootPath));
33065
+ walk(path24.resolve(rootPath));
32621
33066
  return [...new Set(out)];
32622
33067
  }
32623
33068
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
@@ -32626,16 +33071,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
32626
33071
  if (depth > maxDepth) return;
32627
33072
  let entries;
32628
33073
  try {
32629
- entries = fs18.readdirSync(dir, { withFileTypes: true });
33074
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
32630
33075
  } catch {
32631
33076
  return;
32632
33077
  }
32633
33078
  for (const e of entries) {
32634
33079
  if (e.name.startsWith(".")) continue;
32635
- const full = path18.join(dir, e.name);
33080
+ const full = path24.join(dir, e.name);
32636
33081
  if (!e.isDirectory()) continue;
32637
33082
  if (e.name === sessionId) {
32638
- if (isGitDir(full)) out.push(path18.resolve(full));
33083
+ if (isGitDir(full)) out.push(path24.resolve(full));
32639
33084
  } else {
32640
33085
  walk(full, depth + 1);
32641
33086
  }
@@ -32647,14 +33092,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
32647
33092
  function tryBindingFromSessionDirectory(sessionDir) {
32648
33093
  let st;
32649
33094
  try {
32650
- st = fs18.statSync(sessionDir);
33095
+ st = fs23.statSync(sessionDir);
32651
33096
  } catch {
32652
33097
  return null;
32653
33098
  }
32654
33099
  if (!st.isDirectory()) return null;
32655
33100
  const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
32656
33101
  if (worktreePaths.length === 0) return null;
32657
- const abs = path18.resolve(sessionDir);
33102
+ const abs = path24.resolve(sessionDir);
32658
33103
  return {
32659
33104
  sessionParentPath: abs,
32660
33105
  workingTreeRelRoot: abs,
@@ -32664,20 +33109,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
32664
33109
  function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
32665
33110
  const sid = sessionId.trim();
32666
33111
  if (!sid) return null;
32667
- const hintR = path18.resolve(checkoutPath);
33112
+ const hintR = path24.resolve(checkoutPath);
32668
33113
  let best = null;
32669
- let cur = path18.dirname(hintR);
33114
+ let cur = path24.dirname(hintR);
32670
33115
  for (let i = 0; i < 40; i++) {
32671
33116
  const paths = collectWorktreeRootsNamed(cur, sid, 24);
32672
- if (paths.some((p) => path18.resolve(p) === hintR)) {
32673
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path18.resolve(paths[0]);
33117
+ if (paths.some((p) => path24.resolve(p) === hintR)) {
33118
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path24.resolve(paths[0]);
32674
33119
  best = {
32675
- sessionParentPath: path18.resolve(isolated),
32676
- workingTreeRelRoot: path18.resolve(cur),
32677
- repoCheckoutPaths: paths.map((p) => path18.resolve(p))
33120
+ sessionParentPath: path24.resolve(isolated),
33121
+ workingTreeRelRoot: path24.resolve(cur),
33122
+ repoCheckoutPaths: paths.map((p) => path24.resolve(p))
32678
33123
  };
32679
33124
  }
32680
- const next = path18.dirname(cur);
33125
+ const next = path24.dirname(cur);
32681
33126
  if (next === cur) break;
32682
33127
  cur = next;
32683
33128
  }
@@ -32685,33 +33130,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
32685
33130
  }
32686
33131
  function discoverSessionWorktreeOnDisk(options) {
32687
33132
  const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
32688
- if (!sessionId.trim() || !fs18.existsSync(worktreesRootPath)) return null;
33133
+ if (!sessionId.trim() || !fs23.existsSync(worktreesRootPath)) return null;
32689
33134
  const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
32690
33135
  const keys = [];
32691
33136
  if (preferredKey) keys.push(preferredKey);
32692
33137
  try {
32693
- for (const name of fs18.readdirSync(worktreesRootPath)) {
33138
+ for (const name of fs23.readdirSync(worktreesRootPath)) {
32694
33139
  if (name.startsWith(".")) continue;
32695
- const p = path18.join(worktreesRootPath, name);
32696
- if (!fs18.statSync(p).isDirectory()) continue;
33140
+ const p = path24.join(worktreesRootPath, name);
33141
+ if (!fs23.statSync(p).isDirectory()) continue;
32697
33142
  if (name !== preferredKey) keys.push(name);
32698
33143
  }
32699
33144
  } catch {
32700
33145
  return null;
32701
33146
  }
32702
33147
  for (const key of keys) {
32703
- const layoutRoot = path18.join(worktreesRootPath, key);
32704
- if (!fs18.existsSync(layoutRoot) || !fs18.statSync(layoutRoot).isDirectory()) continue;
32705
- const sessionDir = path18.join(layoutRoot, sessionId);
33148
+ const layoutRoot = path24.join(worktreesRootPath, key);
33149
+ if (!fs23.existsSync(layoutRoot) || !fs23.statSync(layoutRoot).isDirectory()) continue;
33150
+ const sessionDir = path24.join(layoutRoot, sessionId);
32706
33151
  const nested = tryBindingFromSessionDirectory(sessionDir);
32707
33152
  if (nested) return nested;
32708
33153
  const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
32709
33154
  if (legacyPaths.length > 0) {
32710
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path18.resolve(legacyPaths[0]);
33155
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
32711
33156
  return {
32712
- sessionParentPath: path18.resolve(isolated),
32713
- workingTreeRelRoot: path18.resolve(layoutRoot),
32714
- repoCheckoutPaths: legacyPaths.map((p) => path18.resolve(p))
33157
+ sessionParentPath: path24.resolve(isolated),
33158
+ workingTreeRelRoot: path24.resolve(layoutRoot),
33159
+ repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
32715
33160
  };
32716
33161
  }
32717
33162
  }
@@ -32720,12 +33165,12 @@ function discoverSessionWorktreeOnDisk(options) {
32720
33165
  function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
32721
33166
  const sid = sessionId.trim();
32722
33167
  if (!sid) return null;
32723
- const hint = path18.resolve(sessionWorktreeRootPathOrHint);
32724
- const underHint = tryBindingFromSessionDirectory(path18.join(hint, sid));
33168
+ const hint = path24.resolve(sessionWorktreeRootPathOrHint);
33169
+ const underHint = tryBindingFromSessionDirectory(path24.join(hint, sid));
32725
33170
  if (underHint) return underHint;
32726
33171
  const direct = tryBindingFromSessionDirectory(hint);
32727
33172
  if (direct) {
32728
- if (path18.basename(hint) === sid && isGitDir(hint)) {
33173
+ if (path24.basename(hint) === sid && isGitDir(hint)) {
32729
33174
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
32730
33175
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
32731
33176
  return legacyFromCheckout;
@@ -32733,24 +33178,24 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
32733
33178
  }
32734
33179
  return direct;
32735
33180
  }
32736
- if (path18.basename(hint) === sid && isGitDir(hint)) {
33181
+ if (path24.basename(hint) === sid && isGitDir(hint)) {
32737
33182
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
32738
33183
  if (legacyFromCheckout) return legacyFromCheckout;
32739
33184
  }
32740
33185
  let st;
32741
33186
  try {
32742
- st = fs18.statSync(hint);
33187
+ st = fs23.statSync(hint);
32743
33188
  } catch {
32744
33189
  return null;
32745
33190
  }
32746
33191
  if (!st.isDirectory()) return null;
32747
33192
  const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
32748
33193
  if (legacyPaths.length === 0) return null;
32749
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path18.resolve(legacyPaths[0]);
33194
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
32750
33195
  return {
32751
- sessionParentPath: path18.resolve(isolated),
33196
+ sessionParentPath: path24.resolve(isolated),
32752
33197
  workingTreeRelRoot: hint,
32753
- repoCheckoutPaths: legacyPaths.map((p) => path18.resolve(p))
33198
+ repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
32754
33199
  };
32755
33200
  }
32756
33201
 
@@ -32773,10 +33218,10 @@ var SessionWorktreeManager = class {
32773
33218
  this.layout = loadWorktreeLayout();
32774
33219
  }
32775
33220
  rememberSessionWorktrees(sessionId, binding) {
32776
- const paths = binding.repoCheckoutPaths.map((p) => path19.resolve(p));
33221
+ const paths = binding.repoCheckoutPaths.map((p) => path25.resolve(p));
32777
33222
  this.sessionRepoCheckoutPaths.set(sessionId, paths);
32778
- this.sessionParentPathBySession.set(sessionId, path19.resolve(binding.sessionParentPath));
32779
- this.sessionWorkingTreeRelRootBySession.set(sessionId, path19.resolve(binding.workingTreeRelRoot));
33223
+ this.sessionParentPathBySession.set(sessionId, path25.resolve(binding.sessionParentPath));
33224
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path25.resolve(binding.workingTreeRelRoot));
32780
33225
  }
32781
33226
  sessionParentPathAfterRemember(sessionId) {
32782
33227
  return this.sessionParentPathBySession.get(sessionId);
@@ -32793,7 +33238,7 @@ var SessionWorktreeManager = class {
32793
33238
  const parent = this.sessionParentPathBySession.get(sessionId);
32794
33239
  const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
32795
33240
  if (!parent || !relRoot) return false;
32796
- return path19.resolve(parent) !== path19.resolve(relRoot);
33241
+ return path25.resolve(parent) !== path25.resolve(relRoot);
32797
33242
  }
32798
33243
  /**
32799
33244
  * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
@@ -32802,7 +33247,7 @@ var SessionWorktreeManager = class {
32802
33247
  if (!sessionId) return null;
32803
33248
  const sid = sessionId.trim();
32804
33249
  const cached2 = this.sessionParentPathBySession.get(sid);
32805
- if (cached2) return path19.resolve(cached2);
33250
+ if (cached2) return path25.resolve(cached2);
32806
33251
  const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
32807
33252
  if (!paths?.length) return null;
32808
33253
  return resolveIsolatedSessionParentPathFromCheckouts(paths);
@@ -32816,7 +33261,7 @@ var SessionWorktreeManager = class {
32816
33261
  const sid = sessionId.trim();
32817
33262
  const parentPathRaw = opts.sessionParentPath?.trim();
32818
33263
  if (parentPathRaw) {
32819
- const resolved = path19.resolve(parentPathRaw);
33264
+ const resolved = path25.resolve(parentPathRaw);
32820
33265
  if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
32821
33266
  const diskFirst = this.tryDiscoverFromDisk(sid);
32822
33267
  if (diskFirst) {
@@ -32835,7 +33280,7 @@ var SessionWorktreeManager = class {
32835
33280
  this.rememberSessionWorktrees(sid, tryRoot);
32836
33281
  return this.sessionParentPathAfterRemember(sid);
32837
33282
  }
32838
- const next = path19.dirname(cur);
33283
+ const next = path25.dirname(cur);
32839
33284
  if (next === cur) break;
32840
33285
  cur = next;
32841
33286
  }
@@ -32988,63 +33433,35 @@ var SessionWorktreeManager = class {
32988
33433
  }
32989
33434
  };
32990
33435
  function defaultWorktreesRootPath() {
32991
- return path19.join(os5.homedir(), ".buildautomaton", "worktrees");
33436
+ return path25.join(os8.homedir(), ".buildautomaton", "worktrees");
32992
33437
  }
32993
33438
 
32994
33439
  // src/files/watch-file-index.ts
32995
33440
  import { watch } from "node:fs";
33441
+ import path30 from "node:path";
33442
+
33443
+ // src/files/index/paths.ts
32996
33444
  import path26 from "node:path";
33445
+ import crypto2 from "node:crypto";
33446
+ function getCwdHashForFileIndex(resolvedCwd) {
33447
+ return crypto2.createHash("sha256").update(path26.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
33448
+ }
32997
33449
 
32998
33450
  // src/files/index/build-file-index.ts
32999
- import path23 from "node:path";
33451
+ import path28 from "node:path";
33000
33452
 
33001
33453
  // src/runtime/yield-to-event-loop.ts
33002
33454
  function yieldToEventLoop() {
33003
- return new Promise((resolve16) => setImmediate(resolve16));
33455
+ return new Promise((resolve18) => setImmediate(resolve18));
33004
33456
  }
33005
33457
 
33006
33458
  // src/files/index/walk-workspace-tree.ts
33007
- import fs19 from "node:fs";
33008
- import path21 from "node:path";
33009
-
33010
- // src/files/index/constants.ts
33011
- import path20 from "node:path";
33012
- import os6 from "node:os";
33013
- var INDEX_WORK_YIELD_EVERY = 256;
33014
- var INDEX_DIR = path20.join(os6.homedir(), ".buildautomaton");
33015
- var INDEX_HASH_LEN = 16;
33016
- var INDEX_VERSION = 2;
33017
- var INDEX_LOG_PREFIX = "[file-index]";
33018
-
33019
- // src/files/index/walk-workspace-tree.ts
33020
- function walkWorkspaceTreeSync(dir, baseDir, out) {
33021
- let names;
33022
- try {
33023
- names = fs19.readdirSync(dir);
33024
- } catch {
33025
- return;
33026
- }
33027
- for (const name of names) {
33028
- if (name.startsWith(".")) continue;
33029
- const full = path21.join(dir, name);
33030
- let stat2;
33031
- try {
33032
- stat2 = fs19.statSync(full);
33033
- } catch {
33034
- continue;
33035
- }
33036
- const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
33037
- if (stat2.isDirectory()) {
33038
- walkWorkspaceTreeSync(full, baseDir, out);
33039
- } else if (stat2.isFile()) {
33040
- out.push(relative5);
33041
- }
33042
- }
33043
- }
33459
+ import fs24 from "node:fs";
33460
+ import path27 from "node:path";
33044
33461
  async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
33045
33462
  let names;
33046
33463
  try {
33047
- names = await fs19.promises.readdir(dir);
33464
+ names = await fs24.promises.readdir(dir);
33048
33465
  } catch {
33049
33466
  return;
33050
33467
  }
@@ -33054,14 +33471,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
33054
33471
  await yieldToEventLoop();
33055
33472
  }
33056
33473
  state.n++;
33057
- const full = path21.join(dir, name);
33474
+ const full = path27.join(dir, name);
33058
33475
  let stat2;
33059
33476
  try {
33060
- stat2 = await fs19.promises.stat(full);
33477
+ stat2 = await fs24.promises.stat(full);
33061
33478
  } catch {
33062
33479
  continue;
33063
33480
  }
33064
- const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
33481
+ const relative5 = path27.relative(baseDir, full).replace(/\\/g, "/");
33065
33482
  if (stat2.isDirectory()) {
33066
33483
  await walkWorkspaceTreeAsync(full, baseDir, out, state);
33067
33484
  } else if (stat2.isFile()) {
@@ -33073,205 +33490,124 @@ function createWalkYieldState() {
33073
33490
  return { n: 0 };
33074
33491
  }
33075
33492
 
33076
- // src/files/index/trigram-utils.ts
33077
- function getTrigrams(s) {
33078
- const lower = s.toLowerCase();
33079
- const out = [];
33080
- for (let i = 0; i <= lower.length - 3; i++) {
33081
- out.push(lower.slice(i, i + 3));
33082
- }
33083
- return out;
33084
- }
33085
- function binarySearch(arr, x) {
33086
- let lo = 0;
33087
- let hi = arr.length - 1;
33088
- while (lo <= hi) {
33089
- const mid = lo + hi >>> 1;
33090
- if (arr[mid] < x) lo = mid + 1;
33091
- else if (arr[mid] > x) hi = mid - 1;
33092
- else return mid;
33093
- }
33094
- return -1;
33095
- }
33096
- function intersectSortedTrigramSets(arrays) {
33097
- if (arrays.length === 0) return [];
33098
- if (arrays.length === 1) return arrays[0];
33099
- const byLength = arrays.slice().sort((a, b) => a.length - b.length);
33100
- const smallest = byLength[0];
33101
- const rest = byLength.slice(1);
33102
- const result = [];
33103
- for (const idx of smallest) {
33104
- if (rest.every((arr) => binarySearch(arr, idx) >= 0)) {
33105
- result.push(idx);
33106
- }
33107
- }
33108
- return result;
33109
- }
33110
-
33111
- // src/files/index/build-trigram-map.ts
33112
- function buildTrigramMapForPaths(paths) {
33113
- const trigramIndex = {};
33114
- for (let i = 0; i < paths.length; i++) {
33115
- const trigrams = getTrigrams(paths[i]);
33116
- const seen = /* @__PURE__ */ new Set();
33117
- for (const tri of trigrams) {
33118
- if (seen.has(tri)) continue;
33119
- seen.add(tri);
33120
- if (!trigramIndex[tri]) trigramIndex[tri] = [];
33121
- trigramIndex[tri].push(i);
33122
- }
33123
- }
33124
- return trigramIndex;
33493
+ // src/files/index/file-index-sqlite-lock.ts
33494
+ import fs25 from "node:fs";
33495
+ function isSqliteCorruptError(e) {
33496
+ const msg = e instanceof Error ? e.message : String(e);
33497
+ return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
33125
33498
  }
33126
- async function buildTrigramMapForPathsAsync(paths) {
33127
- const trigramIndex = {};
33128
- for (let i = 0; i < paths.length; i++) {
33129
- if (i > 0 && i % INDEX_WORK_YIELD_EVERY === 0) {
33130
- await yieldToEventLoop();
33131
- }
33132
- const trigrams = getTrigrams(paths[i]);
33133
- const seen = /* @__PURE__ */ new Set();
33134
- for (const tri of trigrams) {
33135
- if (seen.has(tri)) continue;
33136
- seen.add(tri);
33137
- if (!trigramIndex[tri]) trigramIndex[tri] = [];
33138
- trigramIndex[tri].push(i);
33499
+ var chain = Promise.resolve();
33500
+ function withFileIndexSqliteLock(fn) {
33501
+ const run = async () => {
33502
+ try {
33503
+ return await Promise.resolve(fn());
33504
+ } catch (e) {
33505
+ if (!isSqliteCorruptError(e)) throw e;
33506
+ closeAllCliSqliteConnections();
33507
+ try {
33508
+ fs25.unlinkSync(getCliSqlitePath());
33509
+ } catch {
33510
+ }
33511
+ chain = Promise.resolve();
33512
+ return await Promise.resolve(fn());
33139
33513
  }
33140
- }
33141
- return trigramIndex;
33142
- }
33143
-
33144
- // src/files/index/write-index-file.ts
33145
- import fs20 from "node:fs";
33146
-
33147
- // src/files/index/paths.ts
33148
- import path22 from "node:path";
33149
- import crypto2 from "node:crypto";
33150
- function getIndexPathForCwd(resolvedCwd) {
33151
- const hash = crypto2.createHash("sha256").update(resolvedCwd).digest("hex").slice(0, INDEX_HASH_LEN);
33152
- return path22.join(INDEX_DIR, `.file-index-${hash}.json`);
33153
- }
33154
-
33155
- // src/files/index/write-index-file.ts
33156
- function writeIndexFileSync(resolvedCwd, data) {
33157
- const indexPath = getIndexPathForCwd(resolvedCwd);
33158
- try {
33159
- if (!fs20.existsSync(INDEX_DIR)) fs20.mkdirSync(INDEX_DIR, { recursive: true });
33160
- fs20.writeFileSync(indexPath, JSON.stringify(data), "utf8");
33161
- } catch (e) {
33162
- console.error(`${INDEX_LOG_PREFIX} Failed to write index:`, e);
33163
- }
33164
- }
33165
- async function writeIndexFileAsync(resolvedCwd, data) {
33166
- const indexPath = getIndexPathForCwd(resolvedCwd);
33167
- try {
33168
- await fs20.promises.mkdir(INDEX_DIR, { recursive: true });
33169
- await fs20.promises.writeFile(indexPath, JSON.stringify(data), "utf8");
33170
- } catch (e) {
33171
- console.error(`${INDEX_LOG_PREFIX} Failed to write index:`, e);
33172
- }
33173
- }
33174
- function makeTrigramIndexData(paths, trigramIndex) {
33175
- return { version: INDEX_VERSION, paths, trigramIndex };
33514
+ };
33515
+ const next = chain.then(run);
33516
+ chain = next.then(
33517
+ () => void 0,
33518
+ () => void 0
33519
+ );
33520
+ return next;
33176
33521
  }
33177
33522
 
33178
33523
  // src/files/index/build-file-index.ts
33179
33524
  function sortPaths(paths) {
33180
33525
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
33181
33526
  }
33182
- function buildFileIndex(cwd) {
33183
- const resolved = path23.resolve(cwd);
33184
- const paths = [];
33185
- walkWorkspaceTreeSync(resolved, resolved, paths);
33186
- sortPaths(paths);
33187
- const trigramIndex = buildTrigramMapForPaths(paths);
33188
- const data = makeTrigramIndexData(paths, trigramIndex);
33189
- writeIndexFileSync(resolved, data);
33190
- return data;
33191
- }
33192
- async function buildFileIndexAsync(cwd) {
33193
- const resolved = path23.resolve(cwd);
33194
- const paths = [];
33195
- await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
33196
- await yieldToEventLoop();
33197
- sortPaths(paths);
33198
- const trigramIndex = await buildTrigramMapForPathsAsync(paths);
33199
- const data = makeTrigramIndexData(paths, trigramIndex);
33200
- await writeIndexFileAsync(resolved, data);
33201
- return data;
33202
- }
33203
-
33204
- // src/files/index/load-file-index.ts
33205
- import fs21 from "node:fs";
33206
- import path24 from "node:path";
33207
- function loadFileIndex(cwd) {
33208
- const resolved = path24.resolve(cwd);
33209
- const indexPath = getIndexPathForCwd(resolved);
33527
+ function persistPathsToSqlite(resolved, paths) {
33528
+ const db = getCliDatabase();
33529
+ const h = getCwdHashForFileIndex(resolved);
33530
+ db.run("BEGIN IMMEDIATE");
33210
33531
  try {
33211
- const raw = fs21.readFileSync(indexPath, "utf8");
33212
- const parsed = JSON.parse(raw);
33213
- if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
33214
- const obj = parsed;
33215
- if (obj.version === INDEX_VERSION && obj.trigramIndex && typeof obj.trigramIndex === "object") {
33216
- return obj;
33532
+ db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
33533
+ const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
33534
+ try {
33535
+ for (const rel of paths) {
33536
+ ins.run([h, rel]);
33217
33537
  }
33218
- return buildFileIndex(resolved);
33538
+ } finally {
33539
+ ins.finalize();
33219
33540
  }
33220
- if (Array.isArray(parsed) && parsed.every((p) => typeof p === "string")) {
33221
- return buildFileIndex(resolved);
33541
+ db.run("COMMIT");
33542
+ } catch (e) {
33543
+ try {
33544
+ db.run("ROLLBACK");
33545
+ } catch {
33222
33546
  }
33223
- return null;
33224
- } catch {
33225
- return null;
33547
+ throw e;
33226
33548
  }
33227
33549
  }
33550
+ async function buildFileIndexAsync(cwd) {
33551
+ return withFileIndexSqliteLock(async () => {
33552
+ const resolved = path28.resolve(cwd);
33553
+ const paths = [];
33554
+ await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
33555
+ await yieldToEventLoop();
33556
+ sortPaths(paths);
33557
+ persistPathsToSqlite(resolved, paths);
33558
+ return { pathCount: paths.length };
33559
+ });
33560
+ }
33228
33561
 
33229
33562
  // src/files/index/ensure-file-index.ts
33230
- import path25 from "node:path";
33231
- async function ensureFileIndexAsync(cwd) {
33232
- const resolved = path25.resolve(cwd);
33233
- const cached2 = loadFileIndex(resolved);
33234
- if (cached2 !== null) return { data: cached2, fromCache: true };
33235
- const data = await buildFileIndexAsync(resolved);
33236
- return { data, fromCache: false };
33237
- }
33563
+ import path29 from "node:path";
33238
33564
 
33239
33565
  // src/files/index/search-file-index.ts
33240
- function candidatePathIndices(index, q) {
33241
- const { paths, trigramIndex } = index;
33242
- if (q.length < 3) {
33243
- return paths.map((_, i) => i).filter((i) => paths[i].toLowerCase().includes(q));
33244
- }
33245
- const trigrams = getTrigrams(q);
33246
- if (trigrams.length === 0) {
33247
- return paths.map((_, i) => i).filter((i) => paths[i].toLowerCase().includes(q));
33248
- }
33249
- const arrays = trigrams.map((tri) => trigramIndex[tri]).filter((arr) => arr != null && arr.length > 0);
33250
- if (arrays.length === 0) return [];
33251
- return intersectSortedTrigramSets(arrays);
33252
- }
33253
- async function searchFileIndexAsync(index, query, limit = 100) {
33254
- await yieldToEventLoop();
33566
+ function escapeLikePattern(fragment) {
33567
+ return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
33568
+ }
33569
+ function bridgeFileIndexIsPopulated(resolvedCwd) {
33570
+ const db = getCliDatabase();
33571
+ const h = getCwdHashForFileIndex(resolvedCwd);
33572
+ const row = db.get("SELECT 1 as ok FROM file_index_path WHERE cwd_hash = ? LIMIT 1", [h]);
33573
+ return row != null;
33574
+ }
33575
+ function bridgeFileIndexPathCount(resolvedCwd) {
33576
+ const db = getCliDatabase();
33577
+ const h = getCwdHashForFileIndex(resolvedCwd);
33578
+ const row = db.get("SELECT COUNT(*) as c FROM file_index_path WHERE cwd_hash = ?", [h]);
33579
+ const c = row?.c ?? 0;
33580
+ return Number(c);
33581
+ }
33582
+ function searchBridgeFilePaths(resolvedCwd, query, limit = 100) {
33255
33583
  const q = query.trim().toLowerCase();
33256
33584
  if (!q) return [];
33257
- const { paths } = index;
33258
- const candidateIndices = candidatePathIndices(index, q);
33259
- const out = [];
33260
- let n = 0;
33261
- for (const i of candidateIndices) {
33262
- if (n > 0 && n % INDEX_WORK_YIELD_EVERY === 0) {
33263
- await yieldToEventLoop();
33264
- }
33265
- const p = paths[i];
33266
- if (p.toLowerCase().includes(q)) {
33267
- out.push(p);
33268
- if (out.length >= limit) break;
33269
- }
33270
- n++;
33271
- }
33585
+ const db = getCliDatabase();
33586
+ const h = getCwdHashForFileIndex(resolvedCwd);
33587
+ const pattern = `%${escapeLikePattern(q)}%`;
33588
+ const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
33589
+ const rows = db.all(
33590
+ `SELECT path FROM file_index_path WHERE cwd_hash = ? AND lower(path) LIKE ? ESCAPE '\\' LIMIT ?`,
33591
+ [h, pattern, lim]
33592
+ );
33593
+ return rows.map((r) => String(r.path));
33594
+ }
33595
+ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
33596
+ await yieldToEventLoop();
33597
+ const out = searchBridgeFilePaths(resolvedCwd, query, limit);
33598
+ if (out.length >= INDEX_WORK_YIELD_EVERY) await yieldToEventLoop();
33272
33599
  return out;
33273
33600
  }
33274
33601
 
33602
+ // src/files/index/ensure-file-index.ts
33603
+ async function ensureFileIndexAsync(cwd) {
33604
+ const resolved = path29.resolve(cwd);
33605
+ if (bridgeFileIndexIsPopulated(resolved)) {
33606
+ return { fromCache: true, pathCount: bridgeFileIndexPathCount(resolved) };
33607
+ }
33608
+ return { ...await buildFileIndexAsync(resolved), fromCache: false };
33609
+ }
33610
+
33275
33611
  // src/files/watch-file-index.ts
33276
33612
  var DEBOUNCE_MS = 900;
33277
33613
  function shouldIgnoreRelative(rel) {
@@ -33312,7 +33648,7 @@ function createFsWatcher(resolved, schedule) {
33312
33648
  }
33313
33649
  }
33314
33650
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
33315
- const resolved = path26.resolve(cwd);
33651
+ const resolved = path30.resolve(cwd);
33316
33652
  void buildFileIndexAsync(resolved).catch((e) => {
33317
33653
  console.error("[file-index] Initial index build failed:", e);
33318
33654
  });
@@ -33340,7 +33676,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
33340
33676
  }
33341
33677
 
33342
33678
  // src/connection/create-bridge-connection.ts
33343
- import * as path34 from "node:path";
33679
+ import * as path40 from "node:path";
33344
33680
 
33345
33681
  // src/dev-servers/manager/dev-server-manager.ts
33346
33682
  import { rm as rm2 } from "node:fs/promises";
@@ -33362,15 +33698,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
33362
33698
 
33363
33699
  // src/dev-servers/process/terminate-child-process.ts
33364
33700
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
33365
- const exited = new Promise((resolve16) => {
33366
- proc.once("exit", () => resolve16());
33701
+ const exited = new Promise((resolve18) => {
33702
+ proc.once("exit", () => resolve18());
33367
33703
  });
33368
33704
  log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
33369
33705
  try {
33370
33706
  proc.kill("SIGTERM");
33371
33707
  } catch {
33372
33708
  }
33373
- await Promise.race([exited, new Promise((resolve16) => setTimeout(resolve16, graceMs))]);
33709
+ await Promise.race([exited, new Promise((resolve18) => setTimeout(resolve18, graceMs))]);
33374
33710
  }
33375
33711
  function forceKillChild(proc, log2, shortId, graceMs) {
33376
33712
  log2(
@@ -33384,7 +33720,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
33384
33720
  }
33385
33721
 
33386
33722
  // src/dev-servers/process/wire-dev-server-child-process.ts
33387
- import fs22 from "node:fs";
33723
+ import fs26 from "node:fs";
33388
33724
 
33389
33725
  // src/dev-servers/manager/forward-pipe.ts
33390
33726
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -33420,7 +33756,7 @@ function wireDevServerChildProcess(d) {
33420
33756
  d.setPollInterval(void 0);
33421
33757
  return;
33422
33758
  }
33423
- fs22.readFile(d.mergedLogPath, (err, buf) => {
33759
+ fs26.readFile(d.mergedLogPath, (err, buf) => {
33424
33760
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
33425
33761
  if (buf.length <= d.mergedReadPos.value) return;
33426
33762
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -33458,7 +33794,7 @@ ${errTail}` : ""}`);
33458
33794
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
33459
33795
  };
33460
33796
  if (mergedPath) {
33461
- fs22.readFile(mergedPath, (err, buf) => {
33797
+ fs26.readFile(mergedPath, (err, buf) => {
33462
33798
  if (!err && buf.length > d.mergedReadPos.value) {
33463
33799
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
33464
33800
  if (chunk.length > 0) {
@@ -33560,13 +33896,13 @@ function parseDevServerDefs(servers) {
33560
33896
  }
33561
33897
 
33562
33898
  // src/dev-servers/manager/shell-spawn/utils.ts
33563
- import fs23 from "node:fs";
33899
+ import fs27 from "node:fs";
33564
33900
  function isSpawnEbadf(e) {
33565
33901
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
33566
33902
  }
33567
33903
  function rmDirQuiet(dir) {
33568
33904
  try {
33569
- fs23.rmSync(dir, { recursive: true, force: true });
33905
+ fs27.rmSync(dir, { recursive: true, force: true });
33570
33906
  } catch {
33571
33907
  }
33572
33908
  }
@@ -33574,7 +33910,7 @@ var cachedDevNullReadFd;
33574
33910
  function devNullReadFd() {
33575
33911
  if (cachedDevNullReadFd === void 0) {
33576
33912
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
33577
- cachedDevNullReadFd = fs23.openSync(devPath, "r");
33913
+ cachedDevNullReadFd = fs27.openSync(devPath, "r");
33578
33914
  }
33579
33915
  return cachedDevNullReadFd;
33580
33916
  }
@@ -33648,15 +33984,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
33648
33984
 
33649
33985
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
33650
33986
  import { spawn as spawn7 } from "node:child_process";
33651
- import fs24 from "node:fs";
33987
+ import fs28 from "node:fs";
33652
33988
  import { tmpdir } from "node:os";
33653
- import path27 from "node:path";
33989
+ import path31 from "node:path";
33654
33990
  function trySpawnMergedLogFile(command, env, cwd, signal) {
33655
- const tmpRoot = fs24.mkdtempSync(path27.join(tmpdir(), "ba-devsrv-log-"));
33656
- const logPath = path27.join(tmpRoot, "combined.log");
33991
+ const tmpRoot = fs28.mkdtempSync(path31.join(tmpdir(), "ba-devsrv-log-"));
33992
+ const logPath = path31.join(tmpRoot, "combined.log");
33657
33993
  let logFd;
33658
33994
  try {
33659
- logFd = fs24.openSync(logPath, "a");
33995
+ logFd = fs28.openSync(logPath, "a");
33660
33996
  } catch {
33661
33997
  rmDirQuiet(tmpRoot);
33662
33998
  return null;
@@ -33675,7 +34011,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
33675
34011
  } else {
33676
34012
  proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
33677
34013
  }
33678
- fs24.closeSync(logFd);
34014
+ fs28.closeSync(logFd);
33679
34015
  return {
33680
34016
  proc,
33681
34017
  pipedStdoutStderr: true,
@@ -33684,7 +34020,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
33684
34020
  };
33685
34021
  } catch (e) {
33686
34022
  try {
33687
- fs24.closeSync(logFd);
34023
+ fs28.closeSync(logFd);
33688
34024
  } catch {
33689
34025
  }
33690
34026
  rmDirQuiet(tmpRoot);
@@ -33695,22 +34031,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
33695
34031
 
33696
34032
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
33697
34033
  import { spawn as spawn8 } from "node:child_process";
33698
- import fs25 from "node:fs";
34034
+ import fs29 from "node:fs";
33699
34035
  import { tmpdir as tmpdir2 } from "node:os";
33700
- import path28 from "node:path";
34036
+ import path32 from "node:path";
33701
34037
  function shSingleQuote(s) {
33702
34038
  return `'${s.replace(/'/g, `'\\''`)}'`;
33703
34039
  }
33704
34040
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
33705
- const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir2(), "ba-devsrv-sh-"));
33706
- const logPath = path28.join(tmpRoot, "combined.log");
33707
- const innerPath = path28.join(tmpRoot, "_cmd.sh");
33708
- const runnerPath = path28.join(tmpRoot, "_run.sh");
34041
+ const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir2(), "ba-devsrv-sh-"));
34042
+ const logPath = path32.join(tmpRoot, "combined.log");
34043
+ const innerPath = path32.join(tmpRoot, "_cmd.sh");
34044
+ const runnerPath = path32.join(tmpRoot, "_run.sh");
33709
34045
  try {
33710
- fs25.writeFileSync(innerPath, `#!/bin/sh
34046
+ fs29.writeFileSync(innerPath, `#!/bin/sh
33711
34047
  ${command}
33712
34048
  `);
33713
- fs25.writeFileSync(
34049
+ fs29.writeFileSync(
33714
34050
  runnerPath,
33715
34051
  `#!/bin/sh
33716
34052
  cd ${shSingleQuote(cwd)}
@@ -33736,13 +34072,13 @@ cd ${shSingleQuote(cwd)}
33736
34072
  }
33737
34073
  }
33738
34074
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
33739
- const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir2(), "ba-devsrv-sh-"));
33740
- const logPath = path28.join(tmpRoot, "combined.log");
33741
- const runnerPath = path28.join(tmpRoot, "_run.bat");
34075
+ const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir2(), "ba-devsrv-sh-"));
34076
+ const logPath = path32.join(tmpRoot, "combined.log");
34077
+ const runnerPath = path32.join(tmpRoot, "_run.bat");
33742
34078
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
33743
34079
  const com = process.env.ComSpec || "cmd.exe";
33744
34080
  try {
33745
- fs25.writeFileSync(
34081
+ fs29.writeFileSync(
33746
34082
  runnerPath,
33747
34083
  `@ECHO OFF\r
33748
34084
  CD /D ${q(cwd)}\r
@@ -34511,30 +34847,30 @@ function createOnBridgeIdentified(opts) {
34511
34847
  }
34512
34848
 
34513
34849
  // src/skills/discover-local-agent-skills.ts
34514
- import fs26 from "node:fs";
34515
- import path29 from "node:path";
34850
+ import fs30 from "node:fs";
34851
+ import path33 from "node:path";
34516
34852
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
34517
34853
  function discoverLocalSkills(cwd) {
34518
34854
  const out = [];
34519
34855
  const seenKeys = /* @__PURE__ */ new Set();
34520
34856
  for (const rel of SKILL_DISCOVERY_ROOTS) {
34521
- const base = path29.join(cwd, rel);
34522
- if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
34857
+ const base = path33.join(cwd, rel);
34858
+ if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
34523
34859
  let entries = [];
34524
34860
  try {
34525
- entries = fs26.readdirSync(base);
34861
+ entries = fs30.readdirSync(base);
34526
34862
  } catch {
34527
34863
  continue;
34528
34864
  }
34529
34865
  for (const name of entries) {
34530
- const dir = path29.join(base, name);
34866
+ const dir = path33.join(base, name);
34531
34867
  try {
34532
- if (!fs26.statSync(dir).isDirectory()) continue;
34868
+ if (!fs30.statSync(dir).isDirectory()) continue;
34533
34869
  } catch {
34534
34870
  continue;
34535
34871
  }
34536
- const skillMd = path29.join(dir, "SKILL.md");
34537
- if (!fs26.existsSync(skillMd)) continue;
34872
+ const skillMd = path33.join(dir, "SKILL.md");
34873
+ if (!fs30.existsSync(skillMd)) continue;
34538
34874
  const key = `${rel}/${name}`;
34539
34875
  if (seenKeys.has(key)) continue;
34540
34876
  seenKeys.add(key);
@@ -34546,23 +34882,23 @@ function discoverLocalSkills(cwd) {
34546
34882
  function discoverSkillLayoutRoots(cwd) {
34547
34883
  const roots = [];
34548
34884
  for (const rel of SKILL_DISCOVERY_ROOTS) {
34549
- const base = path29.join(cwd, rel);
34550
- if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
34885
+ const base = path33.join(cwd, rel);
34886
+ if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
34551
34887
  let entries = [];
34552
34888
  try {
34553
- entries = fs26.readdirSync(base);
34889
+ entries = fs30.readdirSync(base);
34554
34890
  } catch {
34555
34891
  continue;
34556
34892
  }
34557
34893
  const skills2 = [];
34558
34894
  for (const name of entries) {
34559
- const dir = path29.join(base, name);
34895
+ const dir = path33.join(base, name);
34560
34896
  try {
34561
- if (!fs26.statSync(dir).isDirectory()) continue;
34897
+ if (!fs30.statSync(dir).isDirectory()) continue;
34562
34898
  } catch {
34563
34899
  continue;
34564
34900
  }
34565
- if (!fs26.existsSync(path29.join(dir, "SKILL.md"))) continue;
34901
+ if (!fs30.existsSync(path33.join(dir, "SKILL.md"))) continue;
34566
34902
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
34567
34903
  skills2.push({ name, relPath });
34568
34904
  }
@@ -34724,6 +35060,13 @@ var handleBridgeIdentified = (msg, deps) => {
34724
35060
  `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
34725
35061
  );
34726
35062
  }
35063
+ try {
35064
+ await deps.warmupAgentCapabilitiesOnConnect?.();
35065
+ } catch (e) {
35066
+ deps.log(
35067
+ `[Bridge service] Agent capability warmup failed: ${e instanceof Error ? e.message : String(e)}`
35068
+ );
35069
+ }
34727
35070
  })();
34728
35071
  });
34729
35072
  setImmediate(() => {
@@ -34756,7 +35099,7 @@ var handleAgentConfigMessage = (msg, deps) => {
34756
35099
  };
34757
35100
 
34758
35101
  // src/prompt-turn-queue/runner.ts
34759
- import fs29 from "node:fs";
35102
+ import fs31 from "node:fs";
34760
35103
 
34761
35104
  // src/prompt-turn-queue/client-report.ts
34762
35105
  function sendPromptQueueClientReport(ws, queues) {
@@ -34765,32 +35108,6 @@ function sendPromptQueueClientReport(ws, queues) {
34765
35108
  return true;
34766
35109
  }
34767
35110
 
34768
- // src/prompt-turn-queue/disk-store.ts
34769
- import fs28 from "node:fs";
34770
-
34771
- // src/prompt-turn-queue/paths.ts
34772
- import crypto3 from "node:crypto";
34773
- import fs27 from "node:fs";
34774
- import path30 from "node:path";
34775
- import os7 from "node:os";
34776
- var QUEUE_KEY_HEX_64 = /^[a-f0-9]{64}$/i;
34777
- function queueStateFileSlug(queueKey) {
34778
- if (QUEUE_KEY_HEX_64.test(queueKey)) return queueKey.toLowerCase();
34779
- return crypto3.createHash("sha256").update(queueKey, "utf8").digest("hex");
34780
- }
34781
- function getPromptQueuesDirectory() {
34782
- const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
34783
- if (override) return path30.resolve(override);
34784
- return path30.join(os7.homedir(), ".buildautomaton", "queues");
34785
- }
34786
- function ensurePromptQueuesDirectory() {
34787
- const dir = getPromptQueuesDirectory();
34788
- if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
34789
- }
34790
- function queueStateFilePath(queueKey) {
34791
- return path30.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
34792
- }
34793
-
34794
35111
  // src/prompt-turn-queue/disk-store.ts
34795
35112
  var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
34796
35113
  "queued",
@@ -34800,28 +35117,27 @@ var MERGEABLE_SERVER_STATES = /* @__PURE__ */ new Set([
34800
35117
  "stopping",
34801
35118
  "discarded"
34802
35119
  ]);
34803
- function parsePersistedQueueFile(raw) {
34804
- try {
34805
- const o = JSON.parse(raw);
34806
- const queueKey = typeof o.queueKey === "string" ? o.queueKey : typeof o.queueKeyHash === "string" ? o.queueKeyHash : null;
34807
- if (!queueKey || typeof o.updatedAt !== "string" || !Array.isArray(o.turns)) return null;
34808
- return { queueKey, updatedAt: o.updatedAt, turns: o.turns };
34809
- } catch {
34810
- return null;
34811
- }
34812
- }
34813
35120
  function readPersistedQueue(queueKey) {
34814
- const p = queueStateFilePath(queueKey);
35121
+ const db = getCliDatabase();
35122
+ const row = db.get("SELECT queue_key, updated_at, turns_json FROM prompt_queue WHERE queue_key = ?", [
35123
+ queueKey
35124
+ ]);
35125
+ if (!row) return null;
34815
35126
  try {
34816
- return parsePersistedQueueFile(fs28.readFileSync(p, "utf8"));
35127
+ const turns = JSON.parse(row.turns_json);
35128
+ if (!Array.isArray(turns)) return null;
35129
+ return { queueKey: row.queue_key, updatedAt: row.updated_at, turns };
34817
35130
  } catch {
34818
35131
  return null;
34819
35132
  }
34820
35133
  }
34821
35134
  function writePersistedQueue(file2) {
34822
- ensurePromptQueuesDirectory();
34823
- const p = queueStateFilePath(file2.queueKey);
34824
- fs28.writeFileSync(p, JSON.stringify(file2, null, 2), "utf8");
35135
+ const db = getCliDatabase();
35136
+ db.run(
35137
+ `INSERT INTO prompt_queue (queue_key, updated_at, turns_json) VALUES (?, ?, ?)
35138
+ ON CONFLICT(queue_key) DO UPDATE SET updated_at = excluded.updated_at, turns_json = excluded.turns_json`,
35139
+ [file2.queueKey, file2.updatedAt, JSON.stringify(file2.turns)]
35140
+ );
34825
35141
  }
34826
35142
  function mergeServerQueueSnapshot(queueKey, serverTurns) {
34827
35143
  const prev = readPersistedQueue(queueKey);
@@ -34879,7 +35195,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
34879
35195
  const tid = typeof pl.snapshotRevertTurnId === "string" && pl.snapshotRevertTurnId.trim() !== "" ? pl.snapshotRevertTurnId.trim() : next.turnId;
34880
35196
  const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
34881
35197
  const file2 = snapshotFilePath(agentBase, tid);
34882
- if (!fs29.existsSync(file2)) {
35198
+ if (!fs31.existsSync(file2)) {
34883
35199
  deps.log(
34884
35200
  `[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
34885
35201
  );
@@ -35103,9 +35419,9 @@ function parseChangeSummarySnapshots(raw) {
35103
35419
  for (const item of raw) {
35104
35420
  if (!item || typeof item !== "object") continue;
35105
35421
  const o = item;
35106
- const path35 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
35107
- if (!path35) continue;
35108
- const row = { path: path35 };
35422
+ const path41 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
35423
+ if (!path41) continue;
35424
+ const row = { path: path41 };
35109
35425
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
35110
35426
  if (typeof o.oldText === "string") row.oldText = o.oldText;
35111
35427
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -35223,6 +35539,8 @@ function handleBridgePrompt(msg, deps) {
35223
35539
  const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : rawParent === "session_worktrees_root" ? "worktrees_root" : null;
35224
35540
  const sessionParentPath = typeof msg.sessionParentPath === "string" && msg.sessionParentPath.trim() ? msg.sessionParentPath.trim() : null;
35225
35541
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
35542
+ const rawAgentId = msg.agentId;
35543
+ const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
35226
35544
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
35227
35545
  const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
35228
35546
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
@@ -35260,6 +35578,7 @@ function handleBridgePrompt(msg, deps) {
35260
35578
  runId,
35261
35579
  mode,
35262
35580
  agentType,
35581
+ agentId,
35263
35582
  agentConfig,
35264
35583
  sessionParentPath: effectiveCwd,
35265
35584
  sendResult: sendResult2,
@@ -35326,15 +35645,15 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
35326
35645
  };
35327
35646
 
35328
35647
  // src/files/list-dir.ts
35329
- import fs30 from "node:fs";
35330
- import path32 from "node:path";
35648
+ import fs32 from "node:fs";
35649
+ import path35 from "node:path";
35331
35650
 
35332
35651
  // src/files/ensure-under-cwd.ts
35333
- import path31 from "node:path";
35652
+ import path34 from "node:path";
35334
35653
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
35335
- const normalized = path31.normalize(relativePath).replace(/^(\.\/)+/, "");
35336
- const resolved = path31.resolve(cwd, normalized);
35337
- if (!resolved.startsWith(cwd + path31.sep) && resolved !== cwd) {
35654
+ const normalized = path34.normalize(relativePath).replace(/^(\.\/)+/, "");
35655
+ const resolved = path34.resolve(cwd, normalized);
35656
+ if (!resolved.startsWith(cwd + path34.sep) && resolved !== cwd) {
35338
35657
  return null;
35339
35658
  }
35340
35659
  return resolved;
@@ -35348,7 +35667,7 @@ async function listDirAsync(relativePath) {
35348
35667
  return { error: "Path is outside working directory" };
35349
35668
  }
35350
35669
  try {
35351
- const names = await fs30.promises.readdir(resolved, { withFileTypes: true });
35670
+ const names = await fs32.promises.readdir(resolved, { withFileTypes: true });
35352
35671
  const visible = names.filter((d) => !d.name.startsWith("."));
35353
35672
  const entries = [];
35354
35673
  for (let i = 0; i < visible.length; i++) {
@@ -35356,12 +35675,12 @@ async function listDirAsync(relativePath) {
35356
35675
  await yieldToEventLoop();
35357
35676
  }
35358
35677
  const d = visible[i];
35359
- const entryPath = path32.join(relativePath || ".", d.name).replace(/\\/g, "/");
35360
- const fullPath = path32.join(resolved, d.name);
35678
+ const entryPath = path35.join(relativePath || ".", d.name).replace(/\\/g, "/");
35679
+ const fullPath = path35.join(resolved, d.name);
35361
35680
  let isDir = d.isDirectory();
35362
35681
  if (d.isSymbolicLink()) {
35363
35682
  try {
35364
- const targetStat = await fs30.promises.stat(fullPath);
35683
+ const targetStat = await fs32.promises.stat(fullPath);
35365
35684
  isDir = targetStat.isDirectory();
35366
35685
  } catch {
35367
35686
  isDir = false;
@@ -35386,25 +35705,25 @@ async function listDirAsync(relativePath) {
35386
35705
  }
35387
35706
 
35388
35707
  // src/files/read-file.ts
35389
- import fs31 from "node:fs";
35708
+ import fs33 from "node:fs";
35390
35709
  import { StringDecoder } from "node:string_decoder";
35391
35710
  function resolveFilePath(relativePath) {
35392
35711
  const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
35393
35712
  if (!resolved) return { error: "Path is outside working directory" };
35394
35713
  let real;
35395
35714
  try {
35396
- real = fs31.realpathSync(resolved);
35715
+ real = fs33.realpathSync(resolved);
35397
35716
  } catch {
35398
35717
  real = resolved;
35399
35718
  }
35400
- const stat2 = fs31.statSync(real);
35719
+ const stat2 = fs33.statSync(real);
35401
35720
  if (!stat2.isFile()) return { error: "Not a file" };
35402
35721
  return real;
35403
35722
  }
35404
35723
  var LINE_CHUNK_SIZE = 64 * 1024;
35405
35724
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
35406
- const fileSize = fs31.statSync(filePath).size;
35407
- const fd = fs31.openSync(filePath, "r");
35725
+ const fileSize = fs33.statSync(filePath).size;
35726
+ const fd = fs33.openSync(filePath, "r");
35408
35727
  const bufSize = 64 * 1024;
35409
35728
  const buf = Buffer.alloc(bufSize);
35410
35729
  const decoder = new StringDecoder("utf8");
@@ -35417,7 +35736,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
35417
35736
  let line0Accum = "";
35418
35737
  try {
35419
35738
  let bytesRead;
35420
- while (!done && (bytesRead = fs31.readSync(fd, buf, 0, bufSize, null)) > 0) {
35739
+ while (!done && (bytesRead = fs33.readSync(fd, buf, 0, bufSize, null)) > 0) {
35421
35740
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
35422
35741
  partial2 = "";
35423
35742
  let lineStart = 0;
@@ -35552,7 +35871,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
35552
35871
  }
35553
35872
  return { content: resultLines.join("\n"), size: fileSize };
35554
35873
  } finally {
35555
- fs31.closeSync(fd);
35874
+ fs33.closeSync(fd);
35556
35875
  }
35557
35876
  }
35558
35877
  function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
@@ -35563,8 +35882,8 @@ function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize =
35563
35882
  if (hasRange) {
35564
35883
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
35565
35884
  }
35566
- const stat2 = fs31.statSync(result);
35567
- const raw = fs31.readFileSync(result, "utf8");
35885
+ const stat2 = fs33.statSync(result);
35886
+ const raw = fs33.readFileSync(result, "utf8");
35568
35887
  const lines = raw.split(/\r?\n/);
35569
35888
  return { content: raw, totalLines: lines.length, size: stat2.size };
35570
35889
  } catch (err) {
@@ -35577,14 +35896,14 @@ async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineC
35577
35896
  }
35578
35897
 
35579
35898
  // src/files/handle-file-browser-search.ts
35899
+ import path36 from "node:path";
35580
35900
  var SEARCH_LIMIT = 100;
35581
35901
  function handleFileBrowserSearch(msg, socket, e2ee) {
35582
35902
  void (async () => {
35583
35903
  await yieldToEventLoop();
35584
35904
  const q = typeof msg.q === "string" ? msg.q : "";
35585
- const cwd = getBridgeRoot();
35586
- const index = loadFileIndex(cwd);
35587
- if (index === null) {
35905
+ const cwd = path36.resolve(getBridgeRoot());
35906
+ if (!bridgeFileIndexIsPopulated(cwd)) {
35588
35907
  const payload2 = {
35589
35908
  type: "file_browser_search_response",
35590
35909
  id: msg.id,
@@ -35594,7 +35913,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
35594
35913
  sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
35595
35914
  return;
35596
35915
  }
35597
- const results = await searchFileIndexAsync(index, q, SEARCH_LIMIT);
35916
+ const results = await searchBridgeFilePathsAsync(cwd, q, SEARCH_LIMIT);
35598
35917
  const payload = {
35599
35918
  type: "file_browser_search_response",
35600
35919
  id: msg.id,
@@ -35682,8 +36001,8 @@ function handleSkillLayoutRequest(msg, deps) {
35682
36001
  }
35683
36002
 
35684
36003
  // src/skills/install-remote-skills.ts
35685
- import fs32 from "node:fs";
35686
- import path33 from "node:path";
36004
+ import fs34 from "node:fs";
36005
+ import path37 from "node:path";
35687
36006
  function installRemoteSkills(cwd, targetDir, items) {
35688
36007
  const installed2 = [];
35689
36008
  if (!Array.isArray(items)) {
@@ -35694,15 +36013,15 @@ function installRemoteSkills(cwd, targetDir, items) {
35694
36013
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
35695
36014
  continue;
35696
36015
  }
35697
- const skillDir = path33.join(cwd, targetDir, item.skillName);
36016
+ const skillDir = path37.join(cwd, targetDir, item.skillName);
35698
36017
  for (const f of item.files) {
35699
36018
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
35700
- const dest = path33.join(skillDir, f.path);
35701
- fs32.mkdirSync(path33.dirname(dest), { recursive: true });
36019
+ const dest = path37.join(skillDir, f.path);
36020
+ fs34.mkdirSync(path37.dirname(dest), { recursive: true });
35702
36021
  if (f.text !== void 0) {
35703
- fs32.writeFileSync(dest, f.text, "utf8");
36022
+ fs34.writeFileSync(dest, f.text, "utf8");
35704
36023
  } else if (f.base64) {
35705
- fs32.writeFileSync(dest, Buffer.from(f.base64, "base64"));
36024
+ fs34.writeFileSync(dest, Buffer.from(f.base64, "base64"));
35706
36025
  }
35707
36026
  }
35708
36027
  installed2.push({
@@ -35852,7 +36171,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
35852
36171
  };
35853
36172
 
35854
36173
  // src/routing/handlers/revert-turn-snapshot.ts
35855
- import * as fs33 from "node:fs";
36174
+ import * as fs35 from "node:fs";
35856
36175
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
35857
36176
  const id = typeof msg.id === "string" ? msg.id : "";
35858
36177
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -35864,7 +36183,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
35864
36183
  if (!s) return;
35865
36184
  const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
35866
36185
  const file2 = snapshotFilePath(agentBase, turnId);
35867
- if (!fs33.existsSync(file2)) {
36186
+ if (!fs35.existsSync(file2)) {
35868
36187
  sendWsMessage(s, {
35869
36188
  type: "revert_turn_snapshot_result",
35870
36189
  id,
@@ -36354,11 +36673,248 @@ function createBridgeHeartbeatController(params) {
36354
36673
  };
36355
36674
  }
36356
36675
 
36676
+ // src/sqlite/hash-json-sha256.ts
36677
+ import { createHash } from "node:crypto";
36678
+ function hashJsonUtf8Sha256(value) {
36679
+ return createHash("sha256").update(JSON.stringify(value), "utf8").digest("hex");
36680
+ }
36681
+
36682
+ // src/sqlite/agent-capability-cache.ts
36683
+ function hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType) {
36684
+ const t = agentType.trim();
36685
+ if (!t) return false;
36686
+ try {
36687
+ const row = db.get(
36688
+ `SELECT config_options_json FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
36689
+ [workspaceId, t]
36690
+ );
36691
+ if (row?.config_options_json == null || row.config_options_json === "") return false;
36692
+ const parsed = JSON.parse(row.config_options_json);
36693
+ return Array.isArray(parsed) && parsed.length > 0;
36694
+ } catch {
36695
+ return false;
36696
+ }
36697
+ }
36698
+ function upsertCliAgentCapabilityCache(db, row) {
36699
+ const t = row.agentType.trim();
36700
+ if (!t) return false;
36701
+ const hash = hashJsonUtf8Sha256(row.configOptions);
36702
+ try {
36703
+ const prev = db.get(
36704
+ `SELECT content_hash FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
36705
+ [row.workspaceId, t]
36706
+ );
36707
+ if (prev?.content_hash === hash) return false;
36708
+ } catch {
36709
+ }
36710
+ const json2 = JSON.stringify(row.configOptions);
36711
+ const now = (/* @__PURE__ */ new Date()).toISOString();
36712
+ db.run(
36713
+ `INSERT INTO agent_capabilities (workspace_id, agent_type, config_options_json, content_hash, updated_at)
36714
+ VALUES (?, ?, ?, ?, ?)
36715
+ ON CONFLICT(workspace_id, agent_type) DO UPDATE SET
36716
+ config_options_json = excluded.config_options_json,
36717
+ content_hash = excluded.content_hash,
36718
+ updated_at = excluded.updated_at`,
36719
+ [row.workspaceId, t, json2, hash, now]
36720
+ );
36721
+ return true;
36722
+ }
36723
+ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
36724
+ const rows = db.all(
36725
+ `SELECT agent_type, config_options_json FROM agent_capabilities WHERE workspace_id = ?`,
36726
+ [workspaceId]
36727
+ );
36728
+ const out = [];
36729
+ for (const r of rows) {
36730
+ try {
36731
+ const parsed = JSON.parse(r.config_options_json);
36732
+ if (!Array.isArray(parsed) || parsed.length === 0) continue;
36733
+ out.push({ agentType: r.agent_type, configOptions: parsed });
36734
+ } catch {
36735
+ }
36736
+ }
36737
+ return out;
36738
+ }
36739
+
36740
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
36741
+ import * as path39 from "node:path";
36742
+
36743
+ // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
36744
+ import * as path38 from "node:path";
36745
+ async function probeOneAgentTypeForCapabilities(params) {
36746
+ const { agentType, cwd, workspaceId, log: log2, getDb, reportAgentCapabilities, bridgeReport = true } = params;
36747
+ const resolved = resolveAgentCommand(agentType);
36748
+ if (!resolved) return false;
36749
+ let sqliteChanged = false;
36750
+ const reportedRef = { done: false };
36751
+ const tryReport = (co) => {
36752
+ if (reportedRef.done) return;
36753
+ if (!Array.isArray(co) || co.length === 0) return;
36754
+ reportedRef.done = true;
36755
+ let changed = false;
36756
+ try {
36757
+ changed = upsertCliAgentCapabilityCache(getDb(), {
36758
+ workspaceId,
36759
+ agentType,
36760
+ configOptions: co
36761
+ });
36762
+ } catch {
36763
+ }
36764
+ sqliteChanged ||= changed;
36765
+ if (bridgeReport && changed) {
36766
+ reportAgentCapabilities?.({ agentType, configOptions: co });
36767
+ }
36768
+ };
36769
+ let handle = null;
36770
+ const killTimer = setTimeout(() => {
36771
+ try {
36772
+ handle?.disconnect();
36773
+ } catch {
36774
+ }
36775
+ }, 28e3);
36776
+ killTimer.unref?.();
36777
+ try {
36778
+ handle = await resolved.createClient({
36779
+ command: resolved.command,
36780
+ cwd: path38.resolve(cwd),
36781
+ backendAgentType: agentType,
36782
+ sessionMode: "agent",
36783
+ persistedAcpSessionId: null,
36784
+ agentConfig: null,
36785
+ getActiveConfigOptions: () => null,
36786
+ onAcpSessionEstablished: (info) => {
36787
+ tryReport(info.configOptions ?? null);
36788
+ },
36789
+ onAcpConfigOptionsUpdated: (co) => {
36790
+ tryReport(co);
36791
+ },
36792
+ onAgentSubprocessExit: () => {
36793
+ },
36794
+ onSessionUpdate: () => {
36795
+ }
36796
+ });
36797
+ await new Promise((r) => setTimeout(r, 1200));
36798
+ } catch (e) {
36799
+ log2(
36800
+ `[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
36801
+ );
36802
+ } finally {
36803
+ clearTimeout(killTimer);
36804
+ try {
36805
+ handle?.disconnect();
36806
+ } catch {
36807
+ }
36808
+ }
36809
+ return sqliteChanged;
36810
+ }
36811
+
36812
+ // src/agents/capabilities/probe-agent-capabilities-for-types.ts
36813
+ async function probeAgentCapabilitiesForDetectedTypes(params) {
36814
+ const {
36815
+ agentTypes,
36816
+ cwd,
36817
+ workspaceId,
36818
+ log: log2,
36819
+ getDb,
36820
+ reportAgentCapabilities,
36821
+ bridgeReport = true,
36822
+ forceAllTypes = false
36823
+ } = params;
36824
+ let changedCount = 0;
36825
+ for (let i = 0; i < agentTypes.length; i++) {
36826
+ if (i > 0) await yieldToEventLoop();
36827
+ const agentType = agentTypes[i];
36828
+ if (!agentType.trim()) continue;
36829
+ if (!forceAllTypes) {
36830
+ try {
36831
+ if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" && hasNonEmptyAgentCapabilityCache(getDb(), workspaceId, agentType)) {
36832
+ continue;
36833
+ }
36834
+ } catch {
36835
+ }
36836
+ }
36837
+ const changed = await probeOneAgentTypeForCapabilities({
36838
+ agentType,
36839
+ cwd,
36840
+ workspaceId,
36841
+ log: log2,
36842
+ getDb,
36843
+ reportAgentCapabilities,
36844
+ bridgeReport
36845
+ });
36846
+ if (changed) changedCount += 1;
36847
+ }
36848
+ return changedCount;
36849
+ }
36850
+
36851
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
36852
+ async function warmupAgentCapabilitiesOnConnect(params) {
36853
+ const { workspaceId, log: log2, getDb, getWs } = params;
36854
+ const cwd = path39.resolve(getBridgeRoot());
36855
+ const db = getDb();
36856
+ function sendBatchFromCache() {
36857
+ const socket = getWs();
36858
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
36859
+ try {
36860
+ const rows = listCliAgentCapabilityCacheForWorkspace(db, workspaceId);
36861
+ if (rows.length === 0) return;
36862
+ sendWsMessage(socket, {
36863
+ type: "agent_capabilities_batch",
36864
+ items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
36865
+ });
36866
+ } catch (e) {
36867
+ log2(
36868
+ `[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
36869
+ );
36870
+ }
36871
+ }
36872
+ sendBatchFromCache();
36873
+ let types = [];
36874
+ try {
36875
+ types = [...await detectLocalAgentTypes()];
36876
+ } catch (e) {
36877
+ log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
36878
+ }
36879
+ try {
36880
+ const n = await probeAgentCapabilitiesForDetectedTypes({
36881
+ agentTypes: types,
36882
+ cwd,
36883
+ workspaceId,
36884
+ log: log2,
36885
+ getDb,
36886
+ bridgeReport: false,
36887
+ forceAllTypes: false
36888
+ });
36889
+ if (n > 0) sendBatchFromCache();
36890
+ } catch (e) {
36891
+ log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
36892
+ }
36893
+ void (async () => {
36894
+ try {
36895
+ await yieldToEventLoop();
36896
+ const n = await probeAgentCapabilitiesForDetectedTypes({
36897
+ agentTypes: types,
36898
+ cwd,
36899
+ workspaceId,
36900
+ log: log2,
36901
+ getDb,
36902
+ bridgeReport: false,
36903
+ forceAllTypes: true
36904
+ });
36905
+ if (n > 0) sendBatchFromCache();
36906
+ } catch (e) {
36907
+ log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
36908
+ }
36909
+ })();
36910
+ }
36911
+
36357
36912
  // src/connection/create-bridge-connection.ts
36358
36913
  async function createBridgeConnection(options) {
36359
36914
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
36360
36915
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
36361
36916
  const logFn = options.log ?? log;
36917
+ getCliDatabase({ logLegacyMigration: logFn });
36362
36918
  const tokens = {
36363
36919
  accessToken: options.authToken,
36364
36920
  refreshToken: options.refreshToken
@@ -36381,16 +36937,39 @@ async function createBridgeConnection(options) {
36381
36937
  firehoseOutage: createEmptyReconnectOutageTracker(),
36382
36938
  lastFirehoseReconnectCloseMeta: null
36383
36939
  };
36940
+ function getWs() {
36941
+ return state.currentWs;
36942
+ }
36943
+ function sendAgentCapabilitiesToBridge(info) {
36944
+ if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
36945
+ let changed = false;
36946
+ try {
36947
+ changed = upsertCliAgentCapabilityCache(getCliDatabase(), {
36948
+ workspaceId,
36949
+ agentType: info.agentType,
36950
+ configOptions: info.configOptions
36951
+ });
36952
+ } catch {
36953
+ }
36954
+ if (!changed) return;
36955
+ const socket = getWs();
36956
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
36957
+ sendWsMessage(socket, {
36958
+ type: "agent_capabilities",
36959
+ agentType: info.agentType,
36960
+ configOptions: info.configOptions
36961
+ });
36962
+ }
36384
36963
  const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
36385
36964
  const sessionWorktreeManager = new SessionWorktreeManager({
36386
36965
  worktreesRootPath,
36387
36966
  log: logFn
36388
36967
  });
36389
- const acpManager = await createAcpManager({ log: logFn });
36968
+ const acpManager = await createAcpManager({
36969
+ log: logFn,
36970
+ reportAgentCapabilities: sendAgentCapabilitiesToBridge
36971
+ });
36390
36972
  logFn("CLI running. Press Ctrl+C to exit.");
36391
- function getWs() {
36392
- return state.currentWs;
36393
- }
36394
36973
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
36395
36974
  const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
36396
36975
  const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
@@ -36418,14 +36997,22 @@ async function createBridgeConnection(options) {
36418
36997
  },
36419
36998
  sendLocalSkillsReport,
36420
36999
  reportAutoDetectedAgents,
37000
+ warmupAgentCapabilitiesOnConnect: async () => {
37001
+ await warmupAgentCapabilitiesOnConnect({
37002
+ workspaceId,
37003
+ log: logFn,
37004
+ getDb: getCliDatabase,
37005
+ getWs
37006
+ });
37007
+ },
36421
37008
  devServerManager,
36422
37009
  e2ee,
36423
37010
  cloudApiBaseUrl: apiUrl,
36424
37011
  getCloudAccessToken: () => tokens.accessToken
36425
37012
  };
36426
37013
  const identifyReportedPaths = {
36427
- bridgeRootPath: path34.resolve(getBridgeRoot()),
36428
- worktreesRootPath: path34.resolve(worktreesRootPath)
37014
+ bridgeRootPath: path40.resolve(getBridgeRoot()),
37015
+ worktreesRootPath: path40.resolve(worktreesRootPath)
36429
37016
  };
36430
37017
  const { connect } = createMainBridgeWebSocketLifecycle({
36431
37018
  state,