@buildautomaton/cli 0.1.29 → 0.1.31

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: path39, errorMaps, issueData } = params;
4069
- const fullPath = [...path39, ...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, path39, key) {
4377
+ constructor(parent, value, path41, key) {
4378
4378
  this._cachedPath = [];
4379
4379
  this.parent = parent;
4380
4380
  this.data = value;
4381
- this._path = path39;
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, path39) {
7997
- if (!path39)
7996
+ function getElementAtPath(obj, path41) {
7997
+ if (!path41)
7998
7998
  return obj;
7999
- return path39.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(path39, 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(path39);
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, path39 = []) => {
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 = [...path39, ...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(path39) {
8481
+ function toDotPath(path41) {
8482
8482
  const segs = [];
8483
- for (const seg of path39) {
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;
@@ -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(path39, isFile, isDirectory) {
21967
- log2(`checking %s`, path39);
21966
+ function check2(path41, isFile, isDirectory) {
21967
+ log2(`checking %s`, path41);
21968
21968
  try {
21969
- const stat2 = fs_1.statSync(path39);
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(path39, type = exports.READABLE) {
21990
- return check2(path39, (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 path39 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
22796
- if (!path39) continue;
22797
- rows.push({ path: path39, 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, path39, body) {
23672
- const url2 = `http://127.0.0.1:${port}${path39}`;
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, path39, 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} ${path39}: ${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.29".length > 0 ? "0.1.29" : "0.0.0-dev";
23965
+ var CLI_VERSION = "0.1.31".length > 0 ? "0.1.31" : "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();
@@ -25189,6 +25255,16 @@ function recordMigrationAndPruneCheckpointLegacy(db, migration, applied2) {
25189
25255
  }
25190
25256
  var CHECKPOINT_V1 = "001_cli_sqlite_checkpoint_v1";
25191
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
+ }
25192
25268
  var CLI_SQLITE_MIGRATIONS = [
25193
25269
  {
25194
25270
  name: CHECKPOINT_V1,
@@ -25196,6 +25272,23 @@ var CLI_SQLITE_MIGRATIONS = [
25196
25272
  migrate: (db) => {
25197
25273
  db.exec(CHECKPOINT_V1_SQL);
25198
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"
25199
25292
  }
25200
25293
  ];
25201
25294
  function migrateCliSqlite(db) {
@@ -25224,6 +25317,13 @@ function migrateCliSqlite(db) {
25224
25317
 
25225
25318
  // src/sqlite/cli-database.ts
25226
25319
  var { Database: SqliteDatabase } = sqliteWasm;
25320
+ function applyCliSqliteMemoryPragmas(db) {
25321
+ try {
25322
+ db.run("PRAGMA cache_size = -8192");
25323
+ db.run("PRAGMA temp_store = FILE");
25324
+ } catch {
25325
+ }
25326
+ }
25227
25327
  var openDatabases = /* @__PURE__ */ new Map();
25228
25328
  var processExitCloseRegistered = false;
25229
25329
  function registerProcessExitSqliteClose() {
@@ -25260,6 +25360,7 @@ function getCliDatabase(options) {
25260
25360
  ensureCliSqliteParentDir(sqlitePath);
25261
25361
  const db = new SqliteDatabase(sqlitePath);
25262
25362
  try {
25363
+ applyCliSqliteMemoryPragmas(db);
25263
25364
  migrateCliSqlite(db);
25264
25365
  importCliSqliteLegacyDiskData(db, options?.logLegacyMigration);
25265
25366
  } catch (e) {
@@ -25275,7 +25376,7 @@ function getCliDatabase(options) {
25275
25376
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
25276
25377
  const say = log2 ?? logImmediate;
25277
25378
  say("Cleaning up connections\u2026");
25278
- await new Promise((resolve16) => setImmediate(resolve16));
25379
+ await new Promise((resolve18) => setImmediate(resolve18));
25279
25380
  state.closedByUser = true;
25280
25381
  clearReconnectQuietTimer(state.mainQuiet);
25281
25382
  clearReconnectQuietTimer(state.firehoseQuiet);
@@ -25389,8 +25490,8 @@ function pathspec(...paths) {
25389
25490
  cache.set(key, paths);
25390
25491
  return key;
25391
25492
  }
25392
- function isPathSpec(path39) {
25393
- return path39 instanceof String && cache.has(path39);
25493
+ function isPathSpec(path41) {
25494
+ return path41 instanceof String && cache.has(path41);
25394
25495
  }
25395
25496
  function toPaths(pathSpec) {
25396
25497
  return cache.get(pathSpec) || [];
@@ -25479,8 +25580,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
25479
25580
  function forEachLineWithContent(input, callback) {
25480
25581
  return toLinesWithContent(input, true).map((line) => callback(line));
25481
25582
  }
25482
- function folderExists(path39) {
25483
- return (0, import_file_exists.exists)(path39, import_file_exists.FOLDER);
25583
+ function folderExists(path41) {
25584
+ return (0, import_file_exists.exists)(path41, import_file_exists.FOLDER);
25484
25585
  }
25485
25586
  function append(target, item) {
25486
25587
  if (Array.isArray(target)) {
@@ -25884,8 +25985,8 @@ function checkIsRepoRootTask() {
25884
25985
  commands,
25885
25986
  format: "utf-8",
25886
25987
  onError,
25887
- parser(path39) {
25888
- return /^\.(git)?$/.test(path39.trim());
25988
+ parser(path41) {
25989
+ return /^\.(git)?$/.test(path41.trim());
25889
25990
  }
25890
25991
  };
25891
25992
  }
@@ -26319,11 +26420,11 @@ function parseGrep(grep) {
26319
26420
  const paths = /* @__PURE__ */ new Set();
26320
26421
  const results = {};
26321
26422
  forEachLineWithContent(grep, (input) => {
26322
- const [path39, line, preview] = input.split(NULL);
26323
- paths.add(path39);
26324
- (results[path39] = results[path39] || []).push({
26423
+ const [path41, line, preview] = input.split(NULL);
26424
+ paths.add(path41);
26425
+ (results[path41] = results[path41] || []).push({
26325
26426
  line: asNumber(line),
26326
- path: path39,
26427
+ path: path41,
26327
26428
  preview
26328
26429
  });
26329
26430
  });
@@ -27088,14 +27189,14 @@ var init_hash_object = __esm2({
27088
27189
  init_task();
27089
27190
  }
27090
27191
  });
27091
- function parseInit(bare, path39, text) {
27192
+ function parseInit(bare, path41, text) {
27092
27193
  const response = String(text).trim();
27093
27194
  let result;
27094
27195
  if (result = initResponseRegex.exec(response)) {
27095
- return new InitSummary(bare, path39, false, result[1]);
27196
+ return new InitSummary(bare, path41, false, result[1]);
27096
27197
  }
27097
27198
  if (result = reInitResponseRegex.exec(response)) {
27098
- return new InitSummary(bare, path39, true, result[1]);
27199
+ return new InitSummary(bare, path41, true, result[1]);
27099
27200
  }
27100
27201
  let gitDir = "";
27101
27202
  const tokens = response.split(" ");
@@ -27106,7 +27207,7 @@ function parseInit(bare, path39, text) {
27106
27207
  break;
27107
27208
  }
27108
27209
  }
27109
- return new InitSummary(bare, path39, /^re/i.test(response), gitDir);
27210
+ return new InitSummary(bare, path41, /^re/i.test(response), gitDir);
27110
27211
  }
27111
27212
  var InitSummary;
27112
27213
  var initResponseRegex;
@@ -27115,9 +27216,9 @@ var init_InitSummary = __esm2({
27115
27216
  "src/lib/responses/InitSummary.ts"() {
27116
27217
  "use strict";
27117
27218
  InitSummary = class {
27118
- constructor(bare, path39, existing, gitDir) {
27219
+ constructor(bare, path41, existing, gitDir) {
27119
27220
  this.bare = bare;
27120
- this.path = path39;
27221
+ this.path = path41;
27121
27222
  this.existing = existing;
27122
27223
  this.gitDir = gitDir;
27123
27224
  }
@@ -27129,7 +27230,7 @@ var init_InitSummary = __esm2({
27129
27230
  function hasBareCommand(command) {
27130
27231
  return command.includes(bareCommand);
27131
27232
  }
27132
- function initTask(bare = false, path39, customArgs) {
27233
+ function initTask(bare = false, path41, customArgs) {
27133
27234
  const commands = ["init", ...customArgs];
27134
27235
  if (bare && !hasBareCommand(commands)) {
27135
27236
  commands.splice(1, 0, bareCommand);
@@ -27138,7 +27239,7 @@ function initTask(bare = false, path39, customArgs) {
27138
27239
  commands,
27139
27240
  format: "utf-8",
27140
27241
  parser(text) {
27141
- return parseInit(commands.includes("--bare"), path39, text);
27242
+ return parseInit(commands.includes("--bare"), path41, text);
27142
27243
  }
27143
27244
  };
27144
27245
  }
@@ -27954,12 +28055,12 @@ var init_FileStatusSummary = __esm2({
27954
28055
  "use strict";
27955
28056
  fromPathRegex = /^(.+)\0(.+)$/;
27956
28057
  FileStatusSummary = class {
27957
- constructor(path39, index, working_dir) {
27958
- this.path = path39;
28058
+ constructor(path41, index, working_dir) {
28059
+ this.path = path41;
27959
28060
  this.index = index;
27960
28061
  this.working_dir = working_dir;
27961
28062
  if (index === "R" || working_dir === "R") {
27962
- const detail = fromPathRegex.exec(path39) || [null, path39, path39];
28063
+ const detail = fromPathRegex.exec(path41) || [null, path41, path41];
27963
28064
  this.from = detail[2] || "";
27964
28065
  this.path = detail[1] || "";
27965
28066
  }
@@ -27990,14 +28091,14 @@ function splitLine(result, lineStr) {
27990
28091
  default:
27991
28092
  return;
27992
28093
  }
27993
- function data(index, workingDir, path39) {
28094
+ function data(index, workingDir, path41) {
27994
28095
  const raw = `${index}${workingDir}`;
27995
28096
  const handler = parsers6.get(raw);
27996
28097
  if (handler) {
27997
- handler(result, path39);
28098
+ handler(result, path41);
27998
28099
  }
27999
28100
  if (raw !== "##" && raw !== "!!") {
28000
- result.files.push(new FileStatusSummary(path39, index, workingDir));
28101
+ result.files.push(new FileStatusSummary(path41, index, workingDir));
28001
28102
  }
28002
28103
  }
28003
28104
  }
@@ -28306,9 +28407,9 @@ var init_simple_git_api = __esm2({
28306
28407
  next
28307
28408
  );
28308
28409
  }
28309
- hashObject(path39, write) {
28410
+ hashObject(path41, write) {
28310
28411
  return this._runTask(
28311
- hashObjectTask(path39, write === true),
28412
+ hashObjectTask(path41, write === true),
28312
28413
  trailingFunctionArgument(arguments)
28313
28414
  );
28314
28415
  }
@@ -28661,8 +28762,8 @@ var init_branch = __esm2({
28661
28762
  }
28662
28763
  });
28663
28764
  function toPath(input) {
28664
- const path39 = input.trim().replace(/^["']|["']$/g, "");
28665
- return path39 && normalize2(path39);
28765
+ const path41 = input.trim().replace(/^["']|["']$/g, "");
28766
+ return path41 && normalize2(path41);
28666
28767
  }
28667
28768
  var parseCheckIgnore;
28668
28769
  var init_CheckIgnore = __esm2({
@@ -28976,8 +29077,8 @@ __export2(sub_module_exports, {
28976
29077
  subModuleTask: () => subModuleTask,
28977
29078
  updateSubModuleTask: () => updateSubModuleTask
28978
29079
  });
28979
- function addSubModuleTask(repo, path39) {
28980
- return subModuleTask(["add", repo, path39]);
29080
+ function addSubModuleTask(repo, path41) {
29081
+ return subModuleTask(["add", repo, path41]);
28981
29082
  }
28982
29083
  function initSubModuleTask(customArgs) {
28983
29084
  return subModuleTask(["init", ...customArgs]);
@@ -29310,8 +29411,8 @@ var require_git = __commonJS2({
29310
29411
  }
29311
29412
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
29312
29413
  };
29313
- Git2.prototype.submoduleAdd = function(repo, path39, then) {
29314
- return this._runTask(addSubModuleTask2(repo, path39), trailingFunctionArgument2(arguments));
29414
+ Git2.prototype.submoduleAdd = function(repo, path41, then) {
29415
+ return this._runTask(addSubModuleTask2(repo, path41), trailingFunctionArgument2(arguments));
29315
29416
  };
29316
29417
  Git2.prototype.submoduleUpdate = function(args, then) {
29317
29418
  return this._runTask(
@@ -30198,9 +30299,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
30198
30299
  // src/agents/acp/put-summarize-change-summaries.ts
30199
30300
  async function putEncryptedChangeSummaryRows(params) {
30200
30301
  const base = params.apiBaseUrl.replace(/\/+$/, "");
30201
- const entries = params.rows.map(({ path: path39, summary }) => {
30302
+ const entries = params.rows.map(({ path: path41, summary }) => {
30202
30303
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
30203
- return { path: path39, summary: JSON.stringify(enc) };
30304
+ return { path: path41, summary: JSON.stringify(enc) };
30204
30305
  });
30205
30306
  const res = await fetch(
30206
30307
  `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -30733,7 +30834,7 @@ async function createCursorAcpClient(options) {
30733
30834
  logDebug,
30734
30835
  getStderrText: () => stderrCapture.getText()
30735
30836
  };
30736
- return new Promise((resolve16, reject) => {
30837
+ return new Promise((resolve18, reject) => {
30737
30838
  child.on("error", (err) => {
30738
30839
  child.kill();
30739
30840
  reject(new Error(formatSpawnError2(err, command[0])));
@@ -30812,12 +30913,16 @@ async function createCursorAcpClient(options) {
30812
30913
  }
30813
30914
  if (method === "session/request_permission" && typeof id === "number") {
30814
30915
  const params = msg.params ?? {};
30815
- pendingRequests.set(id, { method, params });
30816
- onRequest?.({
30817
- requestId: String(id),
30818
- method,
30819
- params
30820
- });
30916
+ if (onRequest) {
30917
+ pendingRequests.set(id, { method, params });
30918
+ onRequest({
30919
+ requestId: String(id),
30920
+ method,
30921
+ params
30922
+ });
30923
+ } else {
30924
+ respond(id, { outcome: { outcome: "denied" } });
30925
+ }
30821
30926
  return;
30822
30927
  }
30823
30928
  if (typeof id === "number" && method) {
@@ -30905,7 +31010,7 @@ async function createCursorAcpClient(options) {
30905
31010
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
30906
31011
  });
30907
31012
  const sessionId = established.sessionId;
30908
- resolve16({
31013
+ resolve18({
30909
31014
  sessionId,
30910
31015
  async sendPrompt(prompt, options2) {
30911
31016
  const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
@@ -31613,6 +31718,28 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, sessio
31613
31718
  }
31614
31719
  }
31615
31720
 
31721
+ // src/agents/acp/hooks/bridge-on-session-update/send-session-info-title-update.ts
31722
+ function extractSessionInfoTitle(params) {
31723
+ if (!params || typeof params !== "object") return null;
31724
+ const p = params;
31725
+ const title = typeof p.title === "string" ? p.title.trim() : "";
31726
+ return title ? title : null;
31727
+ }
31728
+ function sendSessionInfoTitleUpdate(params) {
31729
+ const title = extractSessionInfoTitle(params.payload);
31730
+ if (!title || !params.runId || !params.send) return;
31731
+ try {
31732
+ params.send({
31733
+ type: "session_title_update",
31734
+ ...params.sessionId ? { sessionId: params.sessionId } : {},
31735
+ runId: params.runId,
31736
+ title
31737
+ });
31738
+ } catch (err) {
31739
+ params.log(`[Bridge service] Session title update send failed: ${errorMessage(err)}`);
31740
+ }
31741
+ }
31742
+
31616
31743
  // src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
31617
31744
  function createBridgeOnSessionUpdate(opts) {
31618
31745
  const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
@@ -31628,6 +31755,10 @@ function createBridgeOnSessionUpdate(opts) {
31628
31755
  if (updateKind === "config_option_update") {
31629
31756
  return;
31630
31757
  }
31758
+ if (updateKind === "session_info_update") {
31759
+ sendSessionInfoTitleUpdate({ payload: params, runId, sessionId, send, log: log2 });
31760
+ return;
31761
+ }
31631
31762
  const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
31632
31763
  const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
31633
31764
  const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
@@ -31785,6 +31916,7 @@ async function ensureAcpClient(options) {
31785
31916
  sessionParentPath,
31786
31917
  routing,
31787
31918
  cloudSessionId,
31919
+ reportAgentCapabilities,
31788
31920
  sendSessionUpdate,
31789
31921
  sendRequest,
31790
31922
  log: log2
@@ -31872,6 +32004,12 @@ async function ensureAcpClient(options) {
31872
32004
  backendAgentType: preferredAgentType
31873
32005
  });
31874
32006
  }
32007
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(info.configOptions) && info.configOptions.length > 0) {
32008
+ reportAgentCapabilities({
32009
+ agentType: preferredAgentType,
32010
+ configOptions: info.configOptions
32011
+ });
32012
+ }
31875
32013
  },
31876
32014
  onAcpConfigOptionsUpdated: (configOptions) => {
31877
32015
  state.activeSessionConfigOptions = configOptions;
@@ -31881,6 +32019,12 @@ async function ensureAcpClient(options) {
31881
32019
  backendAgentType: preferredAgentType
31882
32020
  });
31883
32021
  }
32022
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(configOptions) && configOptions.length > 0) {
32023
+ reportAgentCapabilities({
32024
+ agentType: preferredAgentType,
32025
+ configOptions
32026
+ });
32027
+ }
31884
32028
  },
31885
32029
  onAgentSubprocessExit: () => {
31886
32030
  state.acpHandle = null;
@@ -31911,7 +32055,7 @@ async function ensureAcpClient(options) {
31911
32055
 
31912
32056
  // src/agents/acp/create-acp-manager.ts
31913
32057
  async function createAcpManager(options) {
31914
- const { log: log2 } = options;
32058
+ const { log: log2, reportAgentCapabilities } = options;
31915
32059
  const state = {
31916
32060
  acpHandle: null,
31917
32061
  acpStartPromise: null,
@@ -31955,7 +32099,8 @@ async function createAcpManager(options) {
31955
32099
  cloudApiBaseUrl,
31956
32100
  getCloudAccessToken,
31957
32101
  e2ee,
31958
- attachments
32102
+ attachments,
32103
+ agentId
31959
32104
  } = opts;
31960
32105
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
31961
32106
  pendingCancelRunId = void 0;
@@ -31973,7 +32118,8 @@ async function createAcpManager(options) {
31973
32118
  cloudSessionId: sessionId,
31974
32119
  sendSessionUpdate,
31975
32120
  sendRequest: sendSessionUpdate,
31976
- log: log2
32121
+ log: log2,
32122
+ reportAgentCapabilities
31977
32123
  });
31978
32124
  if (!handle) {
31979
32125
  const errMsg = state.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app.";
@@ -33340,43 +33486,39 @@ import path28 from "node:path";
33340
33486
 
33341
33487
  // src/runtime/yield-to-event-loop.ts
33342
33488
  function yieldToEventLoop() {
33343
- return new Promise((resolve16) => setImmediate(resolve16));
33489
+ return new Promise((resolve18) => setImmediate(resolve18));
33344
33490
  }
33345
33491
 
33346
33492
  // src/files/index/walk-workspace-tree.ts
33347
33493
  import fs24 from "node:fs";
33348
33494
  import path27 from "node:path";
33349
- async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
33495
+ function shouldSkipWorkspaceWalkEntry(name) {
33496
+ return name.startsWith(".");
33497
+ }
33498
+ function walkWorkspaceTreeSync(dir, baseDir, onFile) {
33350
33499
  let names;
33351
33500
  try {
33352
- names = await fs24.promises.readdir(dir);
33501
+ names = fs24.readdirSync(dir);
33353
33502
  } catch {
33354
33503
  return;
33355
33504
  }
33356
33505
  for (const name of names) {
33357
- if (name.startsWith(".")) continue;
33358
- if (state.n > 0 && state.n % INDEX_WORK_YIELD_EVERY === 0) {
33359
- await yieldToEventLoop();
33360
- }
33361
- state.n++;
33506
+ if (shouldSkipWorkspaceWalkEntry(name)) continue;
33362
33507
  const full = path27.join(dir, name);
33363
33508
  let stat2;
33364
33509
  try {
33365
- stat2 = await fs24.promises.stat(full);
33510
+ stat2 = fs24.statSync(full);
33366
33511
  } catch {
33367
33512
  continue;
33368
33513
  }
33369
33514
  const relative5 = path27.relative(baseDir, full).replace(/\\/g, "/");
33370
33515
  if (stat2.isDirectory()) {
33371
- await walkWorkspaceTreeAsync(full, baseDir, out, state);
33516
+ walkWorkspaceTreeSync(full, baseDir, onFile);
33372
33517
  } else if (stat2.isFile()) {
33373
- out.push(relative5);
33518
+ onFile(relative5);
33374
33519
  }
33375
33520
  }
33376
33521
  }
33377
- function createWalkYieldState() {
33378
- return { n: 0 };
33379
- }
33380
33522
 
33381
33523
  // src/files/index/file-index-sqlite-lock.ts
33382
33524
  import fs25 from "node:fs";
@@ -33409,20 +33551,29 @@ function withFileIndexSqliteLock(fn) {
33409
33551
  }
33410
33552
 
33411
33553
  // src/files/index/build-file-index.ts
33412
- function sortPaths(paths) {
33413
- paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
33414
- }
33415
- function persistPathsToSqlite(resolved, paths) {
33554
+ var FILE_INDEX_INSERT_BUFFER = 2048;
33555
+ function persistFileIndexForResolvedCwd(resolved) {
33416
33556
  const db = getCliDatabase();
33417
33557
  const h = getCwdHashForFileIndex(resolved);
33558
+ const buf = [];
33559
+ let pathCount = 0;
33418
33560
  db.run("BEGIN IMMEDIATE");
33419
33561
  try {
33420
33562
  db.run("DELETE FROM file_index_path WHERE cwd_hash = ?", [h]);
33421
33563
  const ins = db.prepare("INSERT INTO file_index_path (cwd_hash, path) VALUES (?, ?)");
33422
33564
  try {
33423
- for (const rel of paths) {
33424
- ins.run([h, rel]);
33425
- }
33565
+ const flushBuf = () => {
33566
+ for (const rel of buf) {
33567
+ ins.run([h, rel]);
33568
+ }
33569
+ pathCount += buf.length;
33570
+ buf.length = 0;
33571
+ };
33572
+ walkWorkspaceTreeSync(resolved, resolved, (rel) => {
33573
+ buf.push(rel);
33574
+ if (buf.length >= FILE_INDEX_INSERT_BUFFER) flushBuf();
33575
+ });
33576
+ flushBuf();
33426
33577
  } finally {
33427
33578
  ins.finalize();
33428
33579
  }
@@ -33434,22 +33585,26 @@ function persistPathsToSqlite(resolved, paths) {
33434
33585
  }
33435
33586
  throw e;
33436
33587
  }
33588
+ return pathCount;
33437
33589
  }
33438
33590
  async function buildFileIndexAsync(cwd) {
33439
33591
  return withFileIndexSqliteLock(async () => {
33440
33592
  const resolved = path28.resolve(cwd);
33441
- const paths = [];
33442
- await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
33443
33593
  await yieldToEventLoop();
33444
- sortPaths(paths);
33445
- persistPathsToSqlite(resolved, paths);
33446
- return { pathCount: paths.length };
33594
+ const pathCount = persistFileIndexForResolvedCwd(resolved);
33595
+ await yieldToEventLoop();
33596
+ return { pathCount };
33447
33597
  });
33448
33598
  }
33449
33599
 
33450
33600
  // src/files/index/ensure-file-index.ts
33451
33601
  import path29 from "node:path";
33452
33602
 
33603
+ // src/files/index/file-index-dependency-path.ts
33604
+ function sqliteExprBridgeFileIndexDependencyRank() {
33605
+ return `CASE WHEN lower(path) = 'node_modules' OR lower(path) LIKE 'node_modules/%' OR lower(path) LIKE '%/node_modules/%' OR lower(path) = 'bower_components' OR lower(path) LIKE 'bower_components/%' OR lower(path) LIKE '%/bower_components/%' THEN 1 ELSE 0 END`;
33606
+ }
33607
+
33453
33608
  // src/files/index/search-file-index.ts
33454
33609
  function escapeLikePattern(fragment) {
33455
33610
  return fragment.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
@@ -33474,8 +33629,9 @@ function searchBridgeFilePaths(resolvedCwd, query, limit = 100) {
33474
33629
  const h = getCwdHashForFileIndex(resolvedCwd);
33475
33630
  const pattern = `%${escapeLikePattern(q)}%`;
33476
33631
  const lim = Math.max(0, Math.min(1e4, Math.floor(limit)));
33632
+ const depRank = sqliteExprBridgeFileIndexDependencyRank();
33477
33633
  const rows = db.all(
33478
- `SELECT path FROM file_index_path WHERE cwd_hash = ? AND lower(path) LIKE ? ESCAPE '\\' LIMIT ?`,
33634
+ `SELECT path FROM file_index_path WHERE cwd_hash = ? AND lower(path) LIKE ? ESCAPE '\\' ORDER BY ${depRank}, path LIMIT ?`,
33479
33635
  [h, pattern, lim]
33480
33636
  );
33481
33637
  return rows.map((r) => String(r.path));
@@ -33500,7 +33656,6 @@ async function ensureFileIndexAsync(cwd) {
33500
33656
  var DEBOUNCE_MS = 900;
33501
33657
  function shouldIgnoreRelative(rel) {
33502
33658
  const n = rel.replace(/\\/g, "/");
33503
- if (n.includes("/node_modules/") || n.startsWith("node_modules/")) return true;
33504
33659
  if (n.includes("/.git/") || n === ".git" || n.startsWith(".git/")) return true;
33505
33660
  if (n.includes("/.buildautomaton/") || n.startsWith(".buildautomaton/")) return true;
33506
33661
  return false;
@@ -33564,7 +33719,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
33564
33719
  }
33565
33720
 
33566
33721
  // src/connection/create-bridge-connection.ts
33567
- import * as path38 from "node:path";
33722
+ import * as path40 from "node:path";
33568
33723
 
33569
33724
  // src/dev-servers/manager/dev-server-manager.ts
33570
33725
  import { rm as rm2 } from "node:fs/promises";
@@ -33586,15 +33741,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
33586
33741
 
33587
33742
  // src/dev-servers/process/terminate-child-process.ts
33588
33743
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
33589
- const exited = new Promise((resolve16) => {
33590
- proc.once("exit", () => resolve16());
33744
+ const exited = new Promise((resolve18) => {
33745
+ proc.once("exit", () => resolve18());
33591
33746
  });
33592
33747
  log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
33593
33748
  try {
33594
33749
  proc.kill("SIGTERM");
33595
33750
  } catch {
33596
33751
  }
33597
- await Promise.race([exited, new Promise((resolve16) => setTimeout(resolve16, graceMs))]);
33752
+ await Promise.race([exited, new Promise((resolve18) => setTimeout(resolve18, graceMs))]);
33598
33753
  }
33599
33754
  function forceKillChild(proc, log2, shortId, graceMs) {
33600
33755
  log2(
@@ -34948,6 +35103,13 @@ var handleBridgeIdentified = (msg, deps) => {
34948
35103
  `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
34949
35104
  );
34950
35105
  }
35106
+ try {
35107
+ await deps.warmupAgentCapabilitiesOnConnect?.();
35108
+ } catch (e) {
35109
+ deps.log(
35110
+ `[Bridge service] Agent capability warmup failed: ${e instanceof Error ? e.message : String(e)}`
35111
+ );
35112
+ }
34951
35113
  })();
34952
35114
  });
34953
35115
  setImmediate(() => {
@@ -35300,9 +35462,9 @@ function parseChangeSummarySnapshots(raw) {
35300
35462
  for (const item of raw) {
35301
35463
  if (!item || typeof item !== "object") continue;
35302
35464
  const o = item;
35303
- const path39 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
35304
- if (!path39) continue;
35305
- const row = { path: path39 };
35465
+ const path41 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
35466
+ if (!path41) continue;
35467
+ const row = { path: path41 };
35306
35468
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
35307
35469
  if (typeof o.oldText === "string") row.oldText = o.oldText;
35308
35470
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -35420,6 +35582,8 @@ function handleBridgePrompt(msg, deps) {
35420
35582
  const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : rawParent === "session_worktrees_root" ? "worktrees_root" : null;
35421
35583
  const sessionParentPath = typeof msg.sessionParentPath === "string" && msg.sessionParentPath.trim() ? msg.sessionParentPath.trim() : null;
35422
35584
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
35585
+ const rawAgentId = msg.agentId;
35586
+ const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
35423
35587
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
35424
35588
  const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
35425
35589
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
@@ -35457,6 +35621,7 @@ function handleBridgePrompt(msg, deps) {
35457
35621
  runId,
35458
35622
  mode,
35459
35623
  agentType,
35624
+ agentId,
35460
35625
  agentConfig,
35461
35626
  sessionParentPath: effectiveCwd,
35462
35627
  sendResult: sendResult2,
@@ -36551,6 +36716,242 @@ function createBridgeHeartbeatController(params) {
36551
36716
  };
36552
36717
  }
36553
36718
 
36719
+ // src/sqlite/hash-json-sha256.ts
36720
+ import { createHash } from "node:crypto";
36721
+ function hashJsonUtf8Sha256(value) {
36722
+ return createHash("sha256").update(JSON.stringify(value), "utf8").digest("hex");
36723
+ }
36724
+
36725
+ // src/sqlite/agent-capability-cache.ts
36726
+ function hasNonEmptyAgentCapabilityCache(db, workspaceId, agentType) {
36727
+ const t = agentType.trim();
36728
+ if (!t) return false;
36729
+ try {
36730
+ const row = db.get(
36731
+ `SELECT config_options_json FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
36732
+ [workspaceId, t]
36733
+ );
36734
+ if (row?.config_options_json == null || row.config_options_json === "") return false;
36735
+ const parsed = JSON.parse(row.config_options_json);
36736
+ return Array.isArray(parsed) && parsed.length > 0;
36737
+ } catch {
36738
+ return false;
36739
+ }
36740
+ }
36741
+ function upsertCliAgentCapabilityCache(db, row) {
36742
+ const t = row.agentType.trim();
36743
+ if (!t) return false;
36744
+ const hash = hashJsonUtf8Sha256(row.configOptions);
36745
+ try {
36746
+ const prev = db.get(
36747
+ `SELECT content_hash FROM agent_capabilities WHERE workspace_id = ? AND agent_type = ?`,
36748
+ [row.workspaceId, t]
36749
+ );
36750
+ if (prev?.content_hash === hash) return false;
36751
+ } catch {
36752
+ }
36753
+ const json2 = JSON.stringify(row.configOptions);
36754
+ const now = (/* @__PURE__ */ new Date()).toISOString();
36755
+ db.run(
36756
+ `INSERT INTO agent_capabilities (workspace_id, agent_type, config_options_json, content_hash, updated_at)
36757
+ VALUES (?, ?, ?, ?, ?)
36758
+ ON CONFLICT(workspace_id, agent_type) DO UPDATE SET
36759
+ config_options_json = excluded.config_options_json,
36760
+ content_hash = excluded.content_hash,
36761
+ updated_at = excluded.updated_at`,
36762
+ [row.workspaceId, t, json2, hash, now]
36763
+ );
36764
+ return true;
36765
+ }
36766
+ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
36767
+ const rows = db.all(
36768
+ `SELECT agent_type, config_options_json FROM agent_capabilities WHERE workspace_id = ?`,
36769
+ [workspaceId]
36770
+ );
36771
+ const out = [];
36772
+ for (const r of rows) {
36773
+ try {
36774
+ const parsed = JSON.parse(r.config_options_json);
36775
+ if (!Array.isArray(parsed) || parsed.length === 0) continue;
36776
+ out.push({ agentType: r.agent_type, configOptions: parsed });
36777
+ } catch {
36778
+ }
36779
+ }
36780
+ return out;
36781
+ }
36782
+
36783
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
36784
+ import * as path39 from "node:path";
36785
+
36786
+ // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
36787
+ import * as path38 from "node:path";
36788
+ async function probeOneAgentTypeForCapabilities(params) {
36789
+ const { agentType, cwd, workspaceId, log: log2, getDb, reportAgentCapabilities, bridgeReport = true } = params;
36790
+ const resolved = resolveAgentCommand(agentType);
36791
+ if (!resolved) return false;
36792
+ let sqliteChanged = false;
36793
+ const reportedRef = { done: false };
36794
+ const tryReport = (co) => {
36795
+ if (reportedRef.done) return;
36796
+ if (!Array.isArray(co) || co.length === 0) return;
36797
+ reportedRef.done = true;
36798
+ let changed = false;
36799
+ try {
36800
+ changed = upsertCliAgentCapabilityCache(getDb(), {
36801
+ workspaceId,
36802
+ agentType,
36803
+ configOptions: co
36804
+ });
36805
+ } catch {
36806
+ }
36807
+ sqliteChanged ||= changed;
36808
+ if (bridgeReport && changed) {
36809
+ reportAgentCapabilities?.({ agentType, configOptions: co });
36810
+ }
36811
+ };
36812
+ let handle = null;
36813
+ const killTimer = setTimeout(() => {
36814
+ try {
36815
+ handle?.disconnect();
36816
+ } catch {
36817
+ }
36818
+ }, 28e3);
36819
+ killTimer.unref?.();
36820
+ try {
36821
+ handle = await resolved.createClient({
36822
+ command: resolved.command,
36823
+ cwd: path38.resolve(cwd),
36824
+ backendAgentType: agentType,
36825
+ sessionMode: "agent",
36826
+ persistedAcpSessionId: null,
36827
+ agentConfig: null,
36828
+ getActiveConfigOptions: () => null,
36829
+ onAcpSessionEstablished: (info) => {
36830
+ tryReport(info.configOptions ?? null);
36831
+ },
36832
+ onAcpConfigOptionsUpdated: (co) => {
36833
+ tryReport(co);
36834
+ },
36835
+ onAgentSubprocessExit: () => {
36836
+ },
36837
+ onSessionUpdate: () => {
36838
+ }
36839
+ });
36840
+ await new Promise((r) => setTimeout(r, 1200));
36841
+ } catch (e) {
36842
+ log2(
36843
+ `[Bridge service] Agent capability probe (${agentType}): ${e instanceof Error ? e.message : String(e)}`
36844
+ );
36845
+ } finally {
36846
+ clearTimeout(killTimer);
36847
+ try {
36848
+ handle?.disconnect();
36849
+ } catch {
36850
+ }
36851
+ }
36852
+ return sqliteChanged;
36853
+ }
36854
+
36855
+ // src/agents/capabilities/probe-agent-capabilities-for-types.ts
36856
+ async function probeAgentCapabilitiesForDetectedTypes(params) {
36857
+ const {
36858
+ agentTypes,
36859
+ cwd,
36860
+ workspaceId,
36861
+ log: log2,
36862
+ getDb,
36863
+ reportAgentCapabilities,
36864
+ bridgeReport = true,
36865
+ forceAllTypes = false
36866
+ } = params;
36867
+ let changedCount = 0;
36868
+ for (let i = 0; i < agentTypes.length; i++) {
36869
+ if (i > 0) await yieldToEventLoop();
36870
+ const agentType = agentTypes[i];
36871
+ if (!agentType.trim()) continue;
36872
+ if (!forceAllTypes) {
36873
+ try {
36874
+ if (process.env.BUILDAUTOMATON_FORCE_PROBE_ACP_CAPABILITIES !== "1" && hasNonEmptyAgentCapabilityCache(getDb(), workspaceId, agentType)) {
36875
+ continue;
36876
+ }
36877
+ } catch {
36878
+ }
36879
+ }
36880
+ const changed = await probeOneAgentTypeForCapabilities({
36881
+ agentType,
36882
+ cwd,
36883
+ workspaceId,
36884
+ log: log2,
36885
+ getDb,
36886
+ reportAgentCapabilities,
36887
+ bridgeReport
36888
+ });
36889
+ if (changed) changedCount += 1;
36890
+ }
36891
+ return changedCount;
36892
+ }
36893
+
36894
+ // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
36895
+ async function warmupAgentCapabilitiesOnConnect(params) {
36896
+ const { workspaceId, log: log2, getDb, getWs } = params;
36897
+ const cwd = path39.resolve(getBridgeRoot());
36898
+ const db = getDb();
36899
+ function sendBatchFromCache() {
36900
+ const socket = getWs();
36901
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
36902
+ try {
36903
+ const rows = listCliAgentCapabilityCacheForWorkspace(db, workspaceId);
36904
+ if (rows.length === 0) return;
36905
+ sendWsMessage(socket, {
36906
+ type: "agent_capabilities_batch",
36907
+ items: rows.map((r) => ({ agentType: r.agentType, configOptions: r.configOptions }))
36908
+ });
36909
+ } catch (e) {
36910
+ log2(
36911
+ `[Bridge service] Agent capability batch to bridge failed: ${e instanceof Error ? e.message : String(e)}`
36912
+ );
36913
+ }
36914
+ }
36915
+ sendBatchFromCache();
36916
+ let types = [];
36917
+ try {
36918
+ types = [...await detectLocalAgentTypes()];
36919
+ } catch (e) {
36920
+ log2(`[Bridge service] detectLocalAgentTypes failed: ${e instanceof Error ? e.message : String(e)}`);
36921
+ }
36922
+ try {
36923
+ const n = await probeAgentCapabilitiesForDetectedTypes({
36924
+ agentTypes: types,
36925
+ cwd,
36926
+ workspaceId,
36927
+ log: log2,
36928
+ getDb,
36929
+ bridgeReport: false,
36930
+ forceAllTypes: false
36931
+ });
36932
+ if (n > 0) sendBatchFromCache();
36933
+ } catch (e) {
36934
+ log2(`[Bridge service] Agent capability probe (missing cache) failed: ${e instanceof Error ? e.message : String(e)}`);
36935
+ }
36936
+ void (async () => {
36937
+ try {
36938
+ await yieldToEventLoop();
36939
+ const n = await probeAgentCapabilitiesForDetectedTypes({
36940
+ agentTypes: types,
36941
+ cwd,
36942
+ workspaceId,
36943
+ log: log2,
36944
+ getDb,
36945
+ bridgeReport: false,
36946
+ forceAllTypes: true
36947
+ });
36948
+ if (n > 0) sendBatchFromCache();
36949
+ } catch (e) {
36950
+ log2(`[Bridge service] Agent capability lazy refresh failed: ${e instanceof Error ? e.message : String(e)}`);
36951
+ }
36952
+ })();
36953
+ }
36954
+
36554
36955
  // src/connection/create-bridge-connection.ts
36555
36956
  async function createBridgeConnection(options) {
36556
36957
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
@@ -36579,16 +36980,39 @@ async function createBridgeConnection(options) {
36579
36980
  firehoseOutage: createEmptyReconnectOutageTracker(),
36580
36981
  lastFirehoseReconnectCloseMeta: null
36581
36982
  };
36983
+ function getWs() {
36984
+ return state.currentWs;
36985
+ }
36986
+ function sendAgentCapabilitiesToBridge(info) {
36987
+ if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
36988
+ let changed = false;
36989
+ try {
36990
+ changed = upsertCliAgentCapabilityCache(getCliDatabase(), {
36991
+ workspaceId,
36992
+ agentType: info.agentType,
36993
+ configOptions: info.configOptions
36994
+ });
36995
+ } catch {
36996
+ }
36997
+ if (!changed) return;
36998
+ const socket = getWs();
36999
+ if (!socket || socket.readyState !== wrapper_default.OPEN) return;
37000
+ sendWsMessage(socket, {
37001
+ type: "agent_capabilities",
37002
+ agentType: info.agentType,
37003
+ configOptions: info.configOptions
37004
+ });
37005
+ }
36582
37006
  const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
36583
37007
  const sessionWorktreeManager = new SessionWorktreeManager({
36584
37008
  worktreesRootPath,
36585
37009
  log: logFn
36586
37010
  });
36587
- const acpManager = await createAcpManager({ log: logFn });
37011
+ const acpManager = await createAcpManager({
37012
+ log: logFn,
37013
+ reportAgentCapabilities: sendAgentCapabilitiesToBridge
37014
+ });
36588
37015
  logFn("CLI running. Press Ctrl+C to exit.");
36589
- function getWs() {
36590
- return state.currentWs;
36591
- }
36592
37016
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
36593
37017
  const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
36594
37018
  const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
@@ -36616,14 +37040,22 @@ async function createBridgeConnection(options) {
36616
37040
  },
36617
37041
  sendLocalSkillsReport,
36618
37042
  reportAutoDetectedAgents,
37043
+ warmupAgentCapabilitiesOnConnect: async () => {
37044
+ await warmupAgentCapabilitiesOnConnect({
37045
+ workspaceId,
37046
+ log: logFn,
37047
+ getDb: getCliDatabase,
37048
+ getWs
37049
+ });
37050
+ },
36619
37051
  devServerManager,
36620
37052
  e2ee,
36621
37053
  cloudApiBaseUrl: apiUrl,
36622
37054
  getCloudAccessToken: () => tokens.accessToken
36623
37055
  };
36624
37056
  const identifyReportedPaths = {
36625
- bridgeRootPath: path38.resolve(getBridgeRoot()),
36626
- worktreesRootPath: path38.resolve(worktreesRootPath)
37057
+ bridgeRootPath: path40.resolve(getBridgeRoot()),
37058
+ worktreesRootPath: path40.resolve(worktreesRootPath)
36627
37059
  };
36628
37060
  const { connect } = createMainBridgeWebSocketLifecycle({
36629
37061
  state,