@buildautomaton/cli 0.1.29 → 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: 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.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();
@@ -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) {
@@ -25275,7 +25368,7 @@ function getCliDatabase(options) {
25275
25368
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
25276
25369
  const say = log2 ?? logImmediate;
25277
25370
  say("Cleaning up connections\u2026");
25278
- await new Promise((resolve16) => setImmediate(resolve16));
25371
+ await new Promise((resolve18) => setImmediate(resolve18));
25279
25372
  state.closedByUser = true;
25280
25373
  clearReconnectQuietTimer(state.mainQuiet);
25281
25374
  clearReconnectQuietTimer(state.firehoseQuiet);
@@ -25389,8 +25482,8 @@ function pathspec(...paths) {
25389
25482
  cache.set(key, paths);
25390
25483
  return key;
25391
25484
  }
25392
- function isPathSpec(path39) {
25393
- return path39 instanceof String && cache.has(path39);
25485
+ function isPathSpec(path41) {
25486
+ return path41 instanceof String && cache.has(path41);
25394
25487
  }
25395
25488
  function toPaths(pathSpec) {
25396
25489
  return cache.get(pathSpec) || [];
@@ -25479,8 +25572,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
25479
25572
  function forEachLineWithContent(input, callback) {
25480
25573
  return toLinesWithContent(input, true).map((line) => callback(line));
25481
25574
  }
25482
- function folderExists(path39) {
25483
- return (0, import_file_exists.exists)(path39, import_file_exists.FOLDER);
25575
+ function folderExists(path41) {
25576
+ return (0, import_file_exists.exists)(path41, import_file_exists.FOLDER);
25484
25577
  }
25485
25578
  function append(target, item) {
25486
25579
  if (Array.isArray(target)) {
@@ -25884,8 +25977,8 @@ function checkIsRepoRootTask() {
25884
25977
  commands,
25885
25978
  format: "utf-8",
25886
25979
  onError,
25887
- parser(path39) {
25888
- return /^\.(git)?$/.test(path39.trim());
25980
+ parser(path41) {
25981
+ return /^\.(git)?$/.test(path41.trim());
25889
25982
  }
25890
25983
  };
25891
25984
  }
@@ -26319,11 +26412,11 @@ function parseGrep(grep) {
26319
26412
  const paths = /* @__PURE__ */ new Set();
26320
26413
  const results = {};
26321
26414
  forEachLineWithContent(grep, (input) => {
26322
- const [path39, line, preview] = input.split(NULL);
26323
- paths.add(path39);
26324
- (results[path39] = results[path39] || []).push({
26415
+ const [path41, line, preview] = input.split(NULL);
26416
+ paths.add(path41);
26417
+ (results[path41] = results[path41] || []).push({
26325
26418
  line: asNumber(line),
26326
- path: path39,
26419
+ path: path41,
26327
26420
  preview
26328
26421
  });
26329
26422
  });
@@ -27088,14 +27181,14 @@ var init_hash_object = __esm2({
27088
27181
  init_task();
27089
27182
  }
27090
27183
  });
27091
- function parseInit(bare, path39, text) {
27184
+ function parseInit(bare, path41, text) {
27092
27185
  const response = String(text).trim();
27093
27186
  let result;
27094
27187
  if (result = initResponseRegex.exec(response)) {
27095
- return new InitSummary(bare, path39, false, result[1]);
27188
+ return new InitSummary(bare, path41, false, result[1]);
27096
27189
  }
27097
27190
  if (result = reInitResponseRegex.exec(response)) {
27098
- return new InitSummary(bare, path39, true, result[1]);
27191
+ return new InitSummary(bare, path41, true, result[1]);
27099
27192
  }
27100
27193
  let gitDir = "";
27101
27194
  const tokens = response.split(" ");
@@ -27106,7 +27199,7 @@ function parseInit(bare, path39, text) {
27106
27199
  break;
27107
27200
  }
27108
27201
  }
27109
- return new InitSummary(bare, path39, /^re/i.test(response), gitDir);
27202
+ return new InitSummary(bare, path41, /^re/i.test(response), gitDir);
27110
27203
  }
27111
27204
  var InitSummary;
27112
27205
  var initResponseRegex;
@@ -27115,9 +27208,9 @@ var init_InitSummary = __esm2({
27115
27208
  "src/lib/responses/InitSummary.ts"() {
27116
27209
  "use strict";
27117
27210
  InitSummary = class {
27118
- constructor(bare, path39, existing, gitDir) {
27211
+ constructor(bare, path41, existing, gitDir) {
27119
27212
  this.bare = bare;
27120
- this.path = path39;
27213
+ this.path = path41;
27121
27214
  this.existing = existing;
27122
27215
  this.gitDir = gitDir;
27123
27216
  }
@@ -27129,7 +27222,7 @@ var init_InitSummary = __esm2({
27129
27222
  function hasBareCommand(command) {
27130
27223
  return command.includes(bareCommand);
27131
27224
  }
27132
- function initTask(bare = false, path39, customArgs) {
27225
+ function initTask(bare = false, path41, customArgs) {
27133
27226
  const commands = ["init", ...customArgs];
27134
27227
  if (bare && !hasBareCommand(commands)) {
27135
27228
  commands.splice(1, 0, bareCommand);
@@ -27138,7 +27231,7 @@ function initTask(bare = false, path39, customArgs) {
27138
27231
  commands,
27139
27232
  format: "utf-8",
27140
27233
  parser(text) {
27141
- return parseInit(commands.includes("--bare"), path39, text);
27234
+ return parseInit(commands.includes("--bare"), path41, text);
27142
27235
  }
27143
27236
  };
27144
27237
  }
@@ -27954,12 +28047,12 @@ var init_FileStatusSummary = __esm2({
27954
28047
  "use strict";
27955
28048
  fromPathRegex = /^(.+)\0(.+)$/;
27956
28049
  FileStatusSummary = class {
27957
- constructor(path39, index, working_dir) {
27958
- this.path = path39;
28050
+ constructor(path41, index, working_dir) {
28051
+ this.path = path41;
27959
28052
  this.index = index;
27960
28053
  this.working_dir = working_dir;
27961
28054
  if (index === "R" || working_dir === "R") {
27962
- const detail = fromPathRegex.exec(path39) || [null, path39, path39];
28055
+ const detail = fromPathRegex.exec(path41) || [null, path41, path41];
27963
28056
  this.from = detail[2] || "";
27964
28057
  this.path = detail[1] || "";
27965
28058
  }
@@ -27990,14 +28083,14 @@ function splitLine(result, lineStr) {
27990
28083
  default:
27991
28084
  return;
27992
28085
  }
27993
- function data(index, workingDir, path39) {
28086
+ function data(index, workingDir, path41) {
27994
28087
  const raw = `${index}${workingDir}`;
27995
28088
  const handler = parsers6.get(raw);
27996
28089
  if (handler) {
27997
- handler(result, path39);
28090
+ handler(result, path41);
27998
28091
  }
27999
28092
  if (raw !== "##" && raw !== "!!") {
28000
- result.files.push(new FileStatusSummary(path39, index, workingDir));
28093
+ result.files.push(new FileStatusSummary(path41, index, workingDir));
28001
28094
  }
28002
28095
  }
28003
28096
  }
@@ -28306,9 +28399,9 @@ var init_simple_git_api = __esm2({
28306
28399
  next
28307
28400
  );
28308
28401
  }
28309
- hashObject(path39, write) {
28402
+ hashObject(path41, write) {
28310
28403
  return this._runTask(
28311
- hashObjectTask(path39, write === true),
28404
+ hashObjectTask(path41, write === true),
28312
28405
  trailingFunctionArgument(arguments)
28313
28406
  );
28314
28407
  }
@@ -28661,8 +28754,8 @@ var init_branch = __esm2({
28661
28754
  }
28662
28755
  });
28663
28756
  function toPath(input) {
28664
- const path39 = input.trim().replace(/^["']|["']$/g, "");
28665
- return path39 && normalize2(path39);
28757
+ const path41 = input.trim().replace(/^["']|["']$/g, "");
28758
+ return path41 && normalize2(path41);
28666
28759
  }
28667
28760
  var parseCheckIgnore;
28668
28761
  var init_CheckIgnore = __esm2({
@@ -28976,8 +29069,8 @@ __export2(sub_module_exports, {
28976
29069
  subModuleTask: () => subModuleTask,
28977
29070
  updateSubModuleTask: () => updateSubModuleTask
28978
29071
  });
28979
- function addSubModuleTask(repo, path39) {
28980
- return subModuleTask(["add", repo, path39]);
29072
+ function addSubModuleTask(repo, path41) {
29073
+ return subModuleTask(["add", repo, path41]);
28981
29074
  }
28982
29075
  function initSubModuleTask(customArgs) {
28983
29076
  return subModuleTask(["init", ...customArgs]);
@@ -29310,8 +29403,8 @@ var require_git = __commonJS2({
29310
29403
  }
29311
29404
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
29312
29405
  };
29313
- Git2.prototype.submoduleAdd = function(repo, path39, then) {
29314
- return this._runTask(addSubModuleTask2(repo, path39), trailingFunctionArgument2(arguments));
29406
+ Git2.prototype.submoduleAdd = function(repo, path41, then) {
29407
+ return this._runTask(addSubModuleTask2(repo, path41), trailingFunctionArgument2(arguments));
29315
29408
  };
29316
29409
  Git2.prototype.submoduleUpdate = function(args, then) {
29317
29410
  return this._runTask(
@@ -30198,9 +30291,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
30198
30291
  // src/agents/acp/put-summarize-change-summaries.ts
30199
30292
  async function putEncryptedChangeSummaryRows(params) {
30200
30293
  const base = params.apiBaseUrl.replace(/\/+$/, "");
30201
- const entries = params.rows.map(({ path: path39, summary }) => {
30294
+ const entries = params.rows.map(({ path: path41, summary }) => {
30202
30295
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
30203
- return { path: path39, summary: JSON.stringify(enc) };
30296
+ return { path: path41, summary: JSON.stringify(enc) };
30204
30297
  });
30205
30298
  const res = await fetch(
30206
30299
  `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -30733,7 +30826,7 @@ async function createCursorAcpClient(options) {
30733
30826
  logDebug,
30734
30827
  getStderrText: () => stderrCapture.getText()
30735
30828
  };
30736
- return new Promise((resolve16, reject) => {
30829
+ return new Promise((resolve18, reject) => {
30737
30830
  child.on("error", (err) => {
30738
30831
  child.kill();
30739
30832
  reject(new Error(formatSpawnError2(err, command[0])));
@@ -30812,12 +30905,16 @@ async function createCursorAcpClient(options) {
30812
30905
  }
30813
30906
  if (method === "session/request_permission" && typeof id === "number") {
30814
30907
  const params = msg.params ?? {};
30815
- pendingRequests.set(id, { method, params });
30816
- onRequest?.({
30817
- requestId: String(id),
30818
- method,
30819
- params
30820
- });
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
+ }
30821
30918
  return;
30822
30919
  }
30823
30920
  if (typeof id === "number" && method) {
@@ -30905,7 +31002,7 @@ async function createCursorAcpClient(options) {
30905
31002
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
30906
31003
  });
30907
31004
  const sessionId = established.sessionId;
30908
- resolve16({
31005
+ resolve18({
30909
31006
  sessionId,
30910
31007
  async sendPrompt(prompt, options2) {
30911
31008
  const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
@@ -31785,6 +31882,7 @@ async function ensureAcpClient(options) {
31785
31882
  sessionParentPath,
31786
31883
  routing,
31787
31884
  cloudSessionId,
31885
+ reportAgentCapabilities,
31788
31886
  sendSessionUpdate,
31789
31887
  sendRequest,
31790
31888
  log: log2
@@ -31872,6 +31970,12 @@ async function ensureAcpClient(options) {
31872
31970
  backendAgentType: preferredAgentType
31873
31971
  });
31874
31972
  }
31973
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(info.configOptions) && info.configOptions.length > 0) {
31974
+ reportAgentCapabilities({
31975
+ agentType: preferredAgentType,
31976
+ configOptions: info.configOptions
31977
+ });
31978
+ }
31875
31979
  },
31876
31980
  onAcpConfigOptionsUpdated: (configOptions) => {
31877
31981
  state.activeSessionConfigOptions = configOptions;
@@ -31881,6 +31985,12 @@ async function ensureAcpClient(options) {
31881
31985
  backendAgentType: preferredAgentType
31882
31986
  });
31883
31987
  }
31988
+ if (reportAgentCapabilities && preferredAgentType && Array.isArray(configOptions) && configOptions.length > 0) {
31989
+ reportAgentCapabilities({
31990
+ agentType: preferredAgentType,
31991
+ configOptions
31992
+ });
31993
+ }
31884
31994
  },
31885
31995
  onAgentSubprocessExit: () => {
31886
31996
  state.acpHandle = null;
@@ -31911,7 +32021,7 @@ async function ensureAcpClient(options) {
31911
32021
 
31912
32022
  // src/agents/acp/create-acp-manager.ts
31913
32023
  async function createAcpManager(options) {
31914
- const { log: log2 } = options;
32024
+ const { log: log2, reportAgentCapabilities } = options;
31915
32025
  const state = {
31916
32026
  acpHandle: null,
31917
32027
  acpStartPromise: null,
@@ -31955,7 +32065,8 @@ async function createAcpManager(options) {
31955
32065
  cloudApiBaseUrl,
31956
32066
  getCloudAccessToken,
31957
32067
  e2ee,
31958
- attachments
32068
+ attachments,
32069
+ agentId
31959
32070
  } = opts;
31960
32071
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
31961
32072
  pendingCancelRunId = void 0;
@@ -31973,7 +32084,8 @@ async function createAcpManager(options) {
31973
32084
  cloudSessionId: sessionId,
31974
32085
  sendSessionUpdate,
31975
32086
  sendRequest: sendSessionUpdate,
31976
- log: log2
32087
+ log: log2,
32088
+ reportAgentCapabilities
31977
32089
  });
31978
32090
  if (!handle) {
31979
32091
  const errMsg = state.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app.";
@@ -33340,7 +33452,7 @@ import path28 from "node:path";
33340
33452
 
33341
33453
  // src/runtime/yield-to-event-loop.ts
33342
33454
  function yieldToEventLoop() {
33343
- return new Promise((resolve16) => setImmediate(resolve16));
33455
+ return new Promise((resolve18) => setImmediate(resolve18));
33344
33456
  }
33345
33457
 
33346
33458
  // src/files/index/walk-workspace-tree.ts
@@ -33564,7 +33676,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
33564
33676
  }
33565
33677
 
33566
33678
  // src/connection/create-bridge-connection.ts
33567
- import * as path38 from "node:path";
33679
+ import * as path40 from "node:path";
33568
33680
 
33569
33681
  // src/dev-servers/manager/dev-server-manager.ts
33570
33682
  import { rm as rm2 } from "node:fs/promises";
@@ -33586,15 +33698,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
33586
33698
 
33587
33699
  // src/dev-servers/process/terminate-child-process.ts
33588
33700
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
33589
- const exited = new Promise((resolve16) => {
33590
- proc.once("exit", () => resolve16());
33701
+ const exited = new Promise((resolve18) => {
33702
+ proc.once("exit", () => resolve18());
33591
33703
  });
33592
33704
  log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
33593
33705
  try {
33594
33706
  proc.kill("SIGTERM");
33595
33707
  } catch {
33596
33708
  }
33597
- await Promise.race([exited, new Promise((resolve16) => setTimeout(resolve16, graceMs))]);
33709
+ await Promise.race([exited, new Promise((resolve18) => setTimeout(resolve18, graceMs))]);
33598
33710
  }
33599
33711
  function forceKillChild(proc, log2, shortId, graceMs) {
33600
33712
  log2(
@@ -34948,6 +35060,13 @@ var handleBridgeIdentified = (msg, deps) => {
34948
35060
  `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
34949
35061
  );
34950
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
+ }
34951
35070
  })();
34952
35071
  });
34953
35072
  setImmediate(() => {
@@ -35300,9 +35419,9 @@ function parseChangeSummarySnapshots(raw) {
35300
35419
  for (const item of raw) {
35301
35420
  if (!item || typeof item !== "object") continue;
35302
35421
  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 };
35422
+ const path41 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
35423
+ if (!path41) continue;
35424
+ const row = { path: path41 };
35306
35425
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
35307
35426
  if (typeof o.oldText === "string") row.oldText = o.oldText;
35308
35427
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -35420,6 +35539,8 @@ function handleBridgePrompt(msg, deps) {
35420
35539
  const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : rawParent === "session_worktrees_root" ? "worktrees_root" : null;
35421
35540
  const sessionParentPath = typeof msg.sessionParentPath === "string" && msg.sessionParentPath.trim() ? msg.sessionParentPath.trim() : null;
35422
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;
35423
35544
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
35424
35545
  const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
35425
35546
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
@@ -35457,6 +35578,7 @@ function handleBridgePrompt(msg, deps) {
35457
35578
  runId,
35458
35579
  mode,
35459
35580
  agentType,
35581
+ agentId,
35460
35582
  agentConfig,
35461
35583
  sessionParentPath: effectiveCwd,
35462
35584
  sendResult: sendResult2,
@@ -36551,6 +36673,242 @@ function createBridgeHeartbeatController(params) {
36551
36673
  };
36552
36674
  }
36553
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
+
36554
36912
  // src/connection/create-bridge-connection.ts
36555
36913
  async function createBridgeConnection(options) {
36556
36914
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
@@ -36579,16 +36937,39 @@ async function createBridgeConnection(options) {
36579
36937
  firehoseOutage: createEmptyReconnectOutageTracker(),
36580
36938
  lastFirehoseReconnectCloseMeta: null
36581
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
+ }
36582
36963
  const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
36583
36964
  const sessionWorktreeManager = new SessionWorktreeManager({
36584
36965
  worktreesRootPath,
36585
36966
  log: logFn
36586
36967
  });
36587
- const acpManager = await createAcpManager({ log: logFn });
36968
+ const acpManager = await createAcpManager({
36969
+ log: logFn,
36970
+ reportAgentCapabilities: sendAgentCapabilitiesToBridge
36971
+ });
36588
36972
  logFn("CLI running. Press Ctrl+C to exit.");
36589
- function getWs() {
36590
- return state.currentWs;
36591
- }
36592
36973
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
36593
36974
  const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
36594
36975
  const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
@@ -36616,14 +36997,22 @@ async function createBridgeConnection(options) {
36616
36997
  },
36617
36998
  sendLocalSkillsReport,
36618
36999
  reportAutoDetectedAgents,
37000
+ warmupAgentCapabilitiesOnConnect: async () => {
37001
+ await warmupAgentCapabilitiesOnConnect({
37002
+ workspaceId,
37003
+ log: logFn,
37004
+ getDb: getCliDatabase,
37005
+ getWs
37006
+ });
37007
+ },
36619
37008
  devServerManager,
36620
37009
  e2ee,
36621
37010
  cloudApiBaseUrl: apiUrl,
36622
37011
  getCloudAccessToken: () => tokens.accessToken
36623
37012
  };
36624
37013
  const identifyReportedPaths = {
36625
- bridgeRootPath: path38.resolve(getBridgeRoot()),
36626
- worktreesRootPath: path38.resolve(worktreesRootPath)
37014
+ bridgeRootPath: path40.resolve(getBridgeRoot()),
37015
+ worktreesRootPath: path40.resolve(worktreesRootPath)
36627
37016
  };
36628
37017
  const { connect } = createMainBridgeWebSocketLifecycle({
36629
37018
  state,