@buildautomaton/cli 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -974,7 +974,7 @@ var require_command = __commonJS({
974
974
  var EventEmitter2 = __require("node:events").EventEmitter;
975
975
  var childProcess = __require("node:child_process");
976
976
  var path25 = __require("node:path");
977
- var fs21 = __require("node:fs");
977
+ var fs22 = __require("node:fs");
978
978
  var process2 = __require("node:process");
979
979
  var { Argument: Argument2, humanReadableArgName } = require_argument();
980
980
  var { CommanderError: CommanderError2 } = require_error();
@@ -1907,10 +1907,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1907
1907
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1908
1908
  function findFile(baseDir, baseName) {
1909
1909
  const localBin = path25.resolve(baseDir, baseName);
1910
- if (fs21.existsSync(localBin)) return localBin;
1910
+ if (fs22.existsSync(localBin)) return localBin;
1911
1911
  if (sourceExt.includes(path25.extname(baseName))) return void 0;
1912
1912
  const foundExt = sourceExt.find(
1913
- (ext) => fs21.existsSync(`${localBin}${ext}`)
1913
+ (ext) => fs22.existsSync(`${localBin}${ext}`)
1914
1914
  );
1915
1915
  if (foundExt) return `${localBin}${foundExt}`;
1916
1916
  return void 0;
@@ -1922,7 +1922,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1922
1922
  if (this._scriptPath) {
1923
1923
  let resolvedScriptPath;
1924
1924
  try {
1925
- resolvedScriptPath = fs21.realpathSync(this._scriptPath);
1925
+ resolvedScriptPath = fs22.realpathSync(this._scriptPath);
1926
1926
  } catch (err) {
1927
1927
  resolvedScriptPath = this._scriptPath;
1928
1928
  }
@@ -25047,7 +25047,7 @@ var init_acp = __esm({
25047
25047
  });
25048
25048
 
25049
25049
  // src/cli.ts
25050
- import * as fs20 from "node:fs";
25050
+ import * as fs21 from "node:fs";
25051
25051
  import * as path24 from "node:path";
25052
25052
 
25053
25053
  // ../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
@@ -25118,7 +25118,7 @@ function writeConfigForApi(apiUrl, auth) {
25118
25118
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
25119
25119
  fs.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
25120
25120
  } catch (e) {
25121
- console.error("Could not save auth config:", e);
25121
+ console.error("Could not save authentication config:", e);
25122
25122
  }
25123
25123
  }
25124
25124
  function clearConfigForApi(apiUrl) {
@@ -25145,6 +25145,19 @@ function clearConfigForApi(apiUrl) {
25145
25145
  }
25146
25146
  }
25147
25147
 
25148
+ // src/files/cwd/bridge-workspace-directory.ts
25149
+ import * as path2 from "node:path";
25150
+ var bridgeWorkspaceDirectory = null;
25151
+ function initBridgeWorkspaceDirectory() {
25152
+ bridgeWorkspaceDirectory = path2.resolve(process.cwd());
25153
+ }
25154
+ function getBridgeWorkspaceDirectory() {
25155
+ if (bridgeWorkspaceDirectory == null) {
25156
+ bridgeWorkspaceDirectory = path2.resolve(process.cwd());
25157
+ }
25158
+ return bridgeWorkspaceDirectory;
25159
+ }
25160
+
25148
25161
  // src/log.ts
25149
25162
  function log(line) {
25150
25163
  const time3 = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
@@ -25156,9 +25169,6 @@ function logImmediate(line) {
25156
25169
  `);
25157
25170
  }
25158
25171
 
25159
- // src/bridge/connection/create-ws-bridge.ts
25160
- import https from "node:https";
25161
-
25162
25172
  // ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
25163
25173
  var import_stream = __toESM(require_stream(), 1);
25164
25174
  var import_receiver = __toESM(require_receiver(), 1);
@@ -25168,15 +25178,23 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
25168
25178
  var wrapper_default = import_websocket.default;
25169
25179
 
25170
25180
  // src/bridge/connection/create-ws-bridge.ts
25181
+ import https from "node:https";
25171
25182
  var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
25172
25183
  var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
25173
25184
  function createWsBridge(options) {
25174
- const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid } = options;
25185
+ const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
25175
25186
  const wsOptions = {};
25176
25187
  if (url2.startsWith("wss://")) {
25177
25188
  wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
25178
25189
  }
25179
25190
  const ws = new wrapper_default(url2, wsOptions);
25191
+ let clientPingTimer = null;
25192
+ function clearClientPing() {
25193
+ if (clientPingTimer != null) {
25194
+ clearInterval(clientPingTimer);
25195
+ clientPingTimer = null;
25196
+ }
25197
+ }
25180
25198
  ws.on("unexpected-response", (request, response) => {
25181
25199
  const status = response?.statusCode ?? 0;
25182
25200
  const headers = response?.headers ?? {};
@@ -25186,6 +25204,17 @@ function createWsBridge(options) {
25186
25204
  }
25187
25205
  });
25188
25206
  ws.on("open", () => {
25207
+ clearClientPing();
25208
+ if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
25209
+ clientPingTimer = setInterval(() => {
25210
+ if (ws.readyState === wrapper_default.OPEN) {
25211
+ try {
25212
+ ws.ping();
25213
+ } catch {
25214
+ }
25215
+ }
25216
+ }, clientPingIntervalMs);
25217
+ }
25189
25218
  onOpen?.();
25190
25219
  });
25191
25220
  ws.on("message", (raw) => {
@@ -25205,9 +25234,11 @@ function createWsBridge(options) {
25205
25234
  }
25206
25235
  });
25207
25236
  ws.on("close", (code, reason) => {
25237
+ clearClientPing();
25208
25238
  onClose?.(code, reason.toString());
25209
25239
  });
25210
25240
  ws.on("error", (err) => {
25241
+ clearClientPing();
25211
25242
  onError2?.(err);
25212
25243
  });
25213
25244
  return ws;
@@ -25253,6 +25284,28 @@ function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiU
25253
25284
  }
25254
25285
  }
25255
25286
 
25287
+ // src/bridge/connection/reconnect/constants.ts
25288
+ var RECONNECT_FIRST_MS = 100;
25289
+ var RECONNECT_MAX_MS = 3e4;
25290
+ var RECONNECT_QUIET_MS = 2e3;
25291
+ function reconnectDelayMs(attemptBeforeIncrement) {
25292
+ return Math.min(RECONNECT_FIRST_MS * 2 ** attemptBeforeIncrement, RECONNECT_MAX_MS);
25293
+ }
25294
+
25295
+ // src/bridge/connection/reconnect/format-reconnect-delay-for-log.ts
25296
+ function formatReconnectDelayForLog(delayMs) {
25297
+ if (delayMs < 1e3) return `${delayMs}ms`;
25298
+ const s = delayMs / 1e3;
25299
+ return Number.isInteger(s) ? `${s}s` : `${s.toFixed(1)}s`;
25300
+ }
25301
+
25302
+ // src/bridge/connection/reconnect/log-next-reconnect-attempt.ts
25303
+ function logNextReconnectAttempt(log2, serviceLabel, quiet, delayMs, attempt) {
25304
+ if (!quiet.verboseLogs) return;
25305
+ const delayLabel = formatReconnectDelayForLog(delayMs);
25306
+ log2(`${serviceLabel} Next connection attempt in ${delayLabel} (attempt ${attempt}).`);
25307
+ }
25308
+
25256
25309
  // src/bridge/connection/ws-close-diagnostics.ts
25257
25310
  function describeWebSocketCloseCode(code) {
25258
25311
  const known = {
@@ -25281,11 +25334,122 @@ function formatWebSocketClose(label, code, reason, extra) {
25281
25334
  const r = reason.trim();
25282
25335
  const reasonPart = r ? ` reason="${r}"` : "";
25283
25336
  const extraPart = extra ? ` ${extra}` : "";
25284
- return `${label} closed: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
25337
+ return `${label} Disconnected: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
25338
+ }
25339
+
25340
+ // src/bridge/connection/reconnect/reconnect-quiet-slot.ts
25341
+ function createEmptyReconnectQuietSlot() {
25342
+ return { timer: null, verboseLogs: false, pendingCloseLog: null };
25343
+ }
25344
+ function clearReconnectQuietTimer(quiet) {
25345
+ if (quiet.timer != null) {
25346
+ clearTimeout(quiet.timer);
25347
+ quiet.timer = null;
25348
+ }
25349
+ }
25350
+ function clearReconnectQuietOnSuccessfulConnection(quiet, log2, reconnectedMessage) {
25351
+ clearReconnectQuietTimer(quiet);
25352
+ quiet.pendingCloseLog = null;
25353
+ if (quiet.verboseLogs) {
25354
+ log2(reconnectedMessage);
25355
+ quiet.verboseLogs = false;
25356
+ }
25357
+ }
25358
+ function beginDeferredDisconnectForReconnect(options) {
25359
+ const {
25360
+ isClosedByUser,
25361
+ quiet,
25362
+ code,
25363
+ reason,
25364
+ willReconnect,
25365
+ log: log2,
25366
+ serviceLabel,
25367
+ shutdownDetail,
25368
+ reconnectingDetail,
25369
+ quietMs = RECONNECT_QUIET_MS,
25370
+ shouldAbortQuietWindow
25371
+ } = options;
25372
+ if (!willReconnect) {
25373
+ log2(formatWebSocketClose(serviceLabel, code, reason, shutdownDetail));
25374
+ return;
25375
+ }
25376
+ quiet.pendingCloseLog = { code, reason };
25377
+ if (quiet.timer == null) {
25378
+ quiet.timer = setTimeout(() => {
25379
+ quiet.timer = null;
25380
+ if (isClosedByUser()) return;
25381
+ if (shouldAbortQuietWindow()) return;
25382
+ if (quiet.pendingCloseLog) {
25383
+ const { code: c, reason: r } = quiet.pendingCloseLog;
25384
+ quiet.pendingCloseLog = null;
25385
+ log2(formatWebSocketClose(serviceLabel, c, r, reconnectingDetail));
25386
+ }
25387
+ quiet.verboseLogs = true;
25388
+ }, quietMs);
25389
+ }
25390
+ }
25391
+
25392
+ // src/bridge/connection/reconnect/bridge-main-reconnect.ts
25393
+ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconnect) {
25394
+ beginDeferredDisconnectForReconnect({
25395
+ isClosedByUser: () => state.closedByUser,
25396
+ quiet: state.mainQuiet,
25397
+ code,
25398
+ reason,
25399
+ willReconnect,
25400
+ log: log2,
25401
+ serviceLabel: "[Bridge service]",
25402
+ shutdownDetail: "Not reconnecting (shutting down).",
25403
+ reconnectingDetail: "Reconnecting\u2026",
25404
+ shouldAbortQuietWindow: () => {
25405
+ const w = state.currentWs;
25406
+ return w != null && w.readyState === wrapper_default.OPEN;
25407
+ }
25408
+ });
25409
+ }
25410
+ function clearMainBridgeReconnectQuietOnOpen(state, log2) {
25411
+ clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "[Bridge service] Reconnected.");
25412
+ }
25413
+ function scheduleMainBridgeReconnect(state, connect, log2) {
25414
+ if (state.closedByUser || state.currentWs != null) return;
25415
+ const delay2 = reconnectDelayMs(state.reconnectAttempt);
25416
+ state.reconnectAttempt += 1;
25417
+ logNextReconnectAttempt(log2, "[Bridge service]", state.mainQuiet, delay2, state.reconnectAttempt);
25418
+ state.reconnectTimeout = setTimeout(() => {
25419
+ state.reconnectTimeout = null;
25420
+ connect();
25421
+ }, delay2);
25422
+ }
25423
+
25424
+ // src/bridge/connection/reconnect/firehose-reconnect.ts
25425
+ var PROXY_AND_LOG_SERVICE_LABEL = "[Proxy and log service]";
25426
+ function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
25427
+ beginDeferredDisconnectForReconnect({
25428
+ isClosedByUser: () => ctx.closedByUser,
25429
+ quiet: ctx.firehoseQuiet,
25430
+ code,
25431
+ reason,
25432
+ willReconnect: true,
25433
+ log: log2,
25434
+ serviceLabel: PROXY_AND_LOG_SERVICE_LABEL,
25435
+ shutdownDetail: "Not reconnecting (shutting down).",
25436
+ reconnectingDetail: "Reconnecting\u2026",
25437
+ shouldAbortQuietWindow: () => {
25438
+ const w = ctx.currentWs;
25439
+ if (!w || w.readyState !== wrapper_default.OPEN) return true;
25440
+ return ctx.firehoseHandle?.isConnected() ?? false;
25441
+ }
25442
+ });
25443
+ }
25444
+ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
25445
+ clearReconnectQuietOnSuccessfulConnection(
25446
+ ctx.firehoseQuiet,
25447
+ log2,
25448
+ `${PROXY_AND_LOG_SERVICE_LABEL} Reconnected.`
25449
+ );
25285
25450
  }
25286
25451
 
25287
25452
  // src/auth/run-pending-auth.ts
25288
- var PENDING_RECONNECT_MS = 2e3;
25289
25453
  var PENDING_KEEPALIVE_MS = 25e3;
25290
25454
  var BROWSER_OPEN_FALLBACK_MS = 4e3;
25291
25455
  function buildPendingBridgeUrl(apiUrl, connectionId) {
@@ -25307,7 +25471,32 @@ function runPendingAuth(options) {
25307
25471
  const authPromise = new Promise((resolve15) => {
25308
25472
  resolveAuth = resolve15;
25309
25473
  });
25474
+ let reconnectAttempt = 0;
25475
+ const signInQuiet = createEmptyReconnectQuietSlot();
25476
+ function clearQuietOnOpen() {
25477
+ clearReconnectQuietOnSuccessfulConnection(
25478
+ signInQuiet,
25479
+ logFn,
25480
+ "[Bridge service] Sign-in connection restored."
25481
+ );
25482
+ reconnectAttempt = 0;
25483
+ }
25484
+ function beginDeferredPendingCloseLog(code, reason) {
25485
+ beginDeferredDisconnectForReconnect({
25486
+ isClosedByUser: () => resolved,
25487
+ quiet: signInQuiet,
25488
+ code,
25489
+ reason,
25490
+ willReconnect: true,
25491
+ log: logFn,
25492
+ serviceLabel: "[Bridge service]",
25493
+ shutdownDetail: "Not reconnecting (shutting down).",
25494
+ reconnectingDetail: "Waiting for browser sign-in; reconnecting\u2026",
25495
+ shouldAbortQuietWindow: () => ws != null && ws.readyState === wrapper_default.OPEN
25496
+ });
25497
+ }
25310
25498
  function cleanup() {
25499
+ clearReconnectQuietTimer(signInQuiet);
25311
25500
  if (reconnectTimeout) {
25312
25501
  clearTimeout(reconnectTimeout);
25313
25502
  reconnectTimeout = null;
@@ -25331,6 +25520,7 @@ function runPendingAuth(options) {
25331
25520
  ws = createWsBridge({
25332
25521
  url: url2,
25333
25522
  onOpen: () => {
25523
+ clearQuietOnOpen();
25334
25524
  sendWsMessage(ws, { type: "identify", role: "cli" });
25335
25525
  keepaliveInterval = setInterval(() => {
25336
25526
  if (resolved || !ws || ws.readyState !== 1) return;
@@ -25351,15 +25541,21 @@ function runPendingAuth(options) {
25351
25541
  keepaliveInterval = null;
25352
25542
  }
25353
25543
  if (resolved) return;
25354
- logFn(
25355
- formatWebSocketClose("[Bridge service]", code, reason, "pending sign-in; will reconnect in 2s")
25356
- );
25544
+ beginDeferredPendingCloseLog(code, reason);
25545
+ const delay2 = reconnectDelayMs(reconnectAttempt);
25546
+ reconnectAttempt += 1;
25547
+ if (signInQuiet.verboseLogs) {
25548
+ const delayLabel = formatReconnectDelayForLog(delay2);
25549
+ logFn(
25550
+ `[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${reconnectAttempt}).`
25551
+ );
25552
+ }
25357
25553
  reconnectTimeout = setTimeout(() => {
25358
25554
  reconnectTimeout = null;
25359
25555
  connect();
25360
- }, PENDING_RECONNECT_MS);
25556
+ }, delay2);
25361
25557
  },
25362
- onError: (err) => logFn(`[Bridge service] WebSocket error (pending sign-in): ${err.message}`),
25558
+ onError: (err) => logFn(`[Bridge service] WebSocket error while waiting for sign-in: ${err.message}`),
25363
25559
  onMessage: (data) => {
25364
25560
  const msg = data;
25365
25561
  if (msg.type === "auth_token" && typeof msg.token === "string") {
@@ -25400,7 +25596,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
25400
25596
 
25401
25597
  // src/git/discover-repos.ts
25402
25598
  import * as fs2 from "node:fs";
25403
- import * as path2 from "node:path";
25599
+ import * as path3 from "node:path";
25404
25600
 
25405
25601
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
25406
25602
  var import_file_exists = __toESM(require_dist(), 1);
@@ -26662,8 +26858,8 @@ var init_git_executor_chain = __esm2({
26662
26858
  get cwd() {
26663
26859
  return this._cwd || this._executor.cwd;
26664
26860
  }
26665
- set cwd(cwd3) {
26666
- this._cwd = cwd3;
26861
+ set cwd(cwd) {
26862
+ this._cwd = cwd;
26667
26863
  }
26668
26864
  get env() {
26669
26865
  return this._executor.env;
@@ -26854,8 +27050,8 @@ var init_git_executor = __esm2({
26854
27050
  "use strict";
26855
27051
  init_git_executor_chain();
26856
27052
  GitExecutor = class {
26857
- constructor(cwd3, _scheduler, _plugins) {
26858
- this.cwd = cwd3;
27053
+ constructor(cwd, _scheduler, _plugins) {
27054
+ this.cwd = cwd;
26859
27055
  this._scheduler = _scheduler;
26860
27056
  this._plugins = _plugins;
26861
27057
  this._chain = new GitExecutorChain(this, this._scheduler, this._plugins);
@@ -29985,9 +30181,9 @@ async function isGitRepoDirectory(dirPath) {
29985
30181
  }
29986
30182
 
29987
30183
  // src/git/discover-repos.ts
29988
- async function discoverGitRepos(cwd3 = process.cwd()) {
30184
+ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
29989
30185
  const result = [];
29990
- const cwdResolved = path2.resolve(cwd3);
30186
+ const cwdResolved = path3.resolve(cwd);
29991
30187
  if (await isGitRepoDirectory(cwdResolved)) {
29992
30188
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
29993
30189
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -30000,7 +30196,7 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
30000
30196
  }
30001
30197
  for (const ent of entries) {
30002
30198
  if (!ent.isDirectory()) continue;
30003
- const childPath = path2.join(cwdResolved, ent.name);
30199
+ const childPath = path3.join(cwdResolved, ent.name);
30004
30200
  if (await isGitRepoDirectory(childPath)) {
30005
30201
  const remoteUrl = await getRemoteOriginUrl(childPath);
30006
30202
  result.push({ absolutePath: childPath, remoteUrl });
@@ -30009,11 +30205,11 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
30009
30205
  return result;
30010
30206
  }
30011
30207
  async function discoverGitReposUnderRoot(rootAbs) {
30012
- const root = path2.resolve(rootAbs);
30208
+ const root = path3.resolve(rootAbs);
30013
30209
  const roots = [];
30014
30210
  async function walk(dir) {
30015
30211
  if (await isGitRepoDirectory(dir)) {
30016
- roots.push(path2.resolve(dir));
30212
+ roots.push(path3.resolve(dir));
30017
30213
  return;
30018
30214
  }
30019
30215
  let entries;
@@ -30024,7 +30220,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
30024
30220
  }
30025
30221
  for (const ent of entries) {
30026
30222
  if (!ent.isDirectory() || ent.name === ".git") continue;
30027
- await walk(path2.join(dir, ent.name));
30223
+ await walk(path3.join(dir, ent.name));
30028
30224
  }
30029
30225
  }
30030
30226
  await walk(root);
@@ -30051,28 +30247,13 @@ function reportGitRepos(getWs, log2) {
30051
30247
  }
30052
30248
  }
30053
30249
  }).catch((err) => {
30054
- log2(`[Bridge service] git repo discovery failed: ${err instanceof Error ? err.message : String(err)}`);
30250
+ log2(
30251
+ `[Bridge service] Git repository discovery failed: ${err instanceof Error ? err.message : String(err)}`
30252
+ );
30055
30253
  });
30056
30254
  });
30057
30255
  }
30058
30256
 
30059
- // src/bridge/connection/schedule-reconnect.ts
30060
- var RECONNECT_BASE_MS = 1e3;
30061
- var RECONNECT_MAX_MS = 3e4;
30062
- function scheduleReconnect(state, connect, log2) {
30063
- if (state.closedByUser || state.currentWs != null) return;
30064
- const delay2 = Math.min(
30065
- RECONNECT_BASE_MS * 2 ** state.reconnectAttempt,
30066
- RECONNECT_MAX_MS
30067
- );
30068
- state.reconnectAttempt += 1;
30069
- log2(`[Bridge service] reconnect attempt ${state.reconnectAttempt} in ${delay2 / 1e3}s\u2026`);
30070
- state.reconnectTimeout = setTimeout(() => {
30071
- state.reconnectTimeout = null;
30072
- connect();
30073
- }, delay2);
30074
- }
30075
-
30076
30257
  // src/bridge/connection/close-bridge-connection.ts
30077
30258
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
30078
30259
  log2?.("Shutting down\u2026");
@@ -30082,25 +30263,27 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
30082
30263
  await devServerManager.shutdownAllGraceful();
30083
30264
  }
30084
30265
  state.closedByUser = true;
30266
+ clearReconnectQuietTimer(state.mainQuiet);
30267
+ clearReconnectQuietTimer(state.firehoseQuiet);
30085
30268
  if (state.reconnectTimeout != null) {
30086
30269
  log2?.("Cancelling reconnect timer\u2026");
30087
30270
  clearTimeout(state.reconnectTimeout);
30088
30271
  state.reconnectTimeout = null;
30089
30272
  }
30090
30273
  if (state.firehoseReconnectTimeout != null) {
30091
- log2?.("[Proxy and log service] cancelling reconnect timer\u2026");
30274
+ log2?.("[Proxy and log service] Cancelling reconnect timer\u2026");
30092
30275
  clearTimeout(state.firehoseReconnectTimeout);
30093
30276
  state.firehoseReconnectTimeout = null;
30094
30277
  }
30095
30278
  if (state.firehoseHandle) {
30096
- log2?.("[Proxy and log service] closing connection (CLI shutdown)\u2026");
30279
+ log2?.("[Proxy and log service] Closing connection (CLI shutdown)\u2026");
30097
30280
  state.firehoseHandle.close();
30098
30281
  state.firehoseHandle = null;
30099
30282
  }
30100
30283
  log2?.("Disconnecting local agents (ACP)\u2026");
30101
30284
  acpManager.disconnect();
30102
30285
  if (state.currentWs) {
30103
- log2?.("[Bridge service] closing connection (CLI shutdown)\u2026");
30286
+ log2?.("[Bridge service] Closing connection (CLI shutdown)\u2026");
30104
30287
  state.currentWs.removeAllListeners();
30105
30288
  const wsState = state.currentWs.readyState;
30106
30289
  if (wsState === 1 || wsState === 2) {
@@ -30116,11 +30299,20 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
30116
30299
  }
30117
30300
 
30118
30301
  // src/git/session-git-queue.ts
30302
+ import { execFile as execFile2 } from "node:child_process";
30303
+ import { promisify as promisify2 } from "node:util";
30304
+ import * as fs4 from "node:fs";
30305
+ import * as path5 from "node:path";
30306
+
30307
+ // src/git/pre-turn-snapshot.ts
30308
+ import * as fs3 from "node:fs";
30309
+ import * as path4 from "node:path";
30119
30310
  import { execFile } from "node:child_process";
30120
30311
  import { promisify } from "node:util";
30121
- import * as path3 from "node:path";
30122
30312
  var execFileAsync = promisify(execFile);
30123
- var sessionBoundaryBySessionId = /* @__PURE__ */ new Map();
30313
+ function snapshotsDirForCwd(agentCwd) {
30314
+ return path4.join(agentCwd, ".buildautomaton", "snapshots");
30315
+ }
30124
30316
  async function gitStashCreate(repoRoot, log2) {
30125
30317
  try {
30126
30318
  const { stdout } = await execFileAsync("git", ["stash", "create"], {
@@ -30129,51 +30321,149 @@ async function gitStashCreate(repoRoot, log2) {
30129
30321
  });
30130
30322
  return stdout.trim();
30131
30323
  } catch (e) {
30132
- log2(`[session-git-queue] stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
30324
+ log2(
30325
+ `[snapshot] Git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`
30326
+ );
30133
30327
  return "";
30134
30328
  }
30135
30329
  }
30136
- async function captureSessionGitBoundary(options) {
30137
- const { sessionId, repoRoots, log: log2 } = options;
30138
- if (!repoRoots.length) return;
30330
+ async function gitRun(repoRoot, args, log2, label) {
30331
+ try {
30332
+ await execFileAsync("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
30333
+ return { ok: true };
30334
+ } catch (e) {
30335
+ const msg = e instanceof Error ? e.message : String(e);
30336
+ log2(`[snapshot] Git ${label} failed in ${repoRoot}: ${msg}`);
30337
+ return { ok: false, error: msg };
30338
+ }
30339
+ }
30340
+ async function resolveSnapshotRepoRoots(options) {
30341
+ const { worktreePaths, fallbackCwd, log: log2 } = options;
30342
+ if (worktreePaths?.length) {
30343
+ const uniq = [...new Set(worktreePaths.map((p) => path4.resolve(p)))];
30344
+ return uniq;
30345
+ }
30346
+ try {
30347
+ const repos = await discoverGitReposUnderRoot(fallbackCwd);
30348
+ return repos.map((r) => r.absolutePath);
30349
+ } catch (e) {
30350
+ log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
30351
+ return [];
30352
+ }
30353
+ }
30354
+ async function capturePreTurnSnapshot(options) {
30355
+ const { runId, repoRoots, agentCwd, log: log2 } = options;
30356
+ if (!runId || !repoRoots.length) {
30357
+ return { ok: false, error: "No git repos to snapshot" };
30358
+ }
30139
30359
  const repos = [];
30140
30360
  for (const root of repoRoots) {
30141
30361
  const stashSha = await gitStashCreate(root, log2);
30142
30362
  repos.push({ path: root, stashSha });
30143
30363
  }
30144
- sessionBoundaryBySessionId.set(sessionId, repos);
30145
- log2(`[session-git-queue] boundary stash ${sessionId.slice(0, 8)}\u2026 (${repos.length} repo(s))`);
30364
+ const dir = snapshotsDirForCwd(agentCwd);
30365
+ try {
30366
+ fs3.mkdirSync(dir, { recursive: true });
30367
+ } catch (e) {
30368
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
30369
+ }
30370
+ const payload = {
30371
+ runId,
30372
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
30373
+ repos
30374
+ };
30375
+ const filePath = path4.join(dir, `${runId}.json`);
30376
+ try {
30377
+ fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
30378
+ } catch (e) {
30379
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
30380
+ }
30381
+ const repoList = repos.map((r) => r.path).join(", ");
30382
+ log2(
30383
+ `[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)): ${repoList}`
30384
+ );
30385
+ return { ok: true, filePath, repos };
30386
+ }
30387
+ async function applyPreTurnSnapshot(filePath, log2) {
30388
+ let data;
30389
+ try {
30390
+ const raw = fs3.readFileSync(filePath, "utf8");
30391
+ data = JSON.parse(raw);
30392
+ } catch (e) {
30393
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
30394
+ }
30395
+ if (!Array.isArray(data.repos)) {
30396
+ return { ok: false, error: "Invalid snapshot file" };
30397
+ }
30398
+ for (const r of data.repos) {
30399
+ if (!r.path) continue;
30400
+ const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
30401
+ if (!reset.ok) return reset;
30402
+ const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
30403
+ if (!clean.ok) return clean;
30404
+ if (r.stashSha) {
30405
+ const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
30406
+ if (!ap.ok) return ap;
30407
+ }
30408
+ }
30409
+ log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
30410
+ return { ok: true };
30146
30411
  }
30147
- async function collectSessionDiffAndNotify(options) {
30148
- const { sessionId, runId, sendSessionUpdate, log: log2 } = options;
30149
- const repos = sessionBoundaryBySessionId.get(sessionId);
30150
- if (!repos?.length) {
30151
- log2(`[session-git-queue] no boundary for ${sessionId.slice(0, 8)}\u2026; skip aggregate diff`);
30412
+ function snapshotFilePath(agentCwd, runId) {
30413
+ return path4.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
30414
+ }
30415
+
30416
+ // src/git/session-git-queue.ts
30417
+ var execFileAsync2 = promisify2(execFile2);
30418
+ var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
30419
+ function readWorkspaceFileAsUtf8(absPath) {
30420
+ try {
30421
+ const st = fs4.statSync(absPath);
30422
+ if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
30423
+ return fs4.readFileSync(absPath, "utf8");
30424
+ } catch {
30425
+ return void 0;
30426
+ }
30427
+ }
30428
+ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
30429
+ const { sessionId, runId, agentCwd, sendSessionUpdate, log: log2 } = options;
30430
+ const filePath = snapshotFilePath(agentCwd, runId);
30431
+ let data;
30432
+ try {
30433
+ const raw = fs4.readFileSync(filePath, "utf8");
30434
+ data = JSON.parse(raw);
30435
+ } catch (e) {
30436
+ log2(
30437
+ `[session-git-queue] No pre-turn snapshot for run ${runId.slice(0, 8)}\u2026: ${e instanceof Error ? e.message : String(e)}`
30438
+ );
30152
30439
  return;
30153
30440
  }
30154
- sessionBoundaryBySessionId.delete(sessionId);
30155
- const multiRepo = repos.length > 1;
30156
- for (const repo of repos) {
30441
+ if (!Array.isArray(data.repos) || !data.repos.length) {
30442
+ log2(`[session-git-queue] Empty repos in snapshot ${runId.slice(0, 8)}\u2026; skipping aggregate diff.`);
30443
+ return;
30444
+ }
30445
+ const multiRepo = data.repos.length > 1;
30446
+ for (const repo of data.repos) {
30157
30447
  if (!repo.stashSha) continue;
30158
30448
  let namesRaw;
30159
30449
  try {
30160
- const { stdout } = await execFileAsync("git", ["diff", "--name-only", repo.stashSha], {
30450
+ const { stdout } = await execFileAsync2("git", ["diff", "--name-only", repo.stashSha], {
30161
30451
  cwd: repo.path,
30162
30452
  maxBuffer: 10 * 1024 * 1024
30163
30453
  });
30164
30454
  namesRaw = stdout;
30165
30455
  } catch (e) {
30166
30456
  log2(
30167
- `[session-git-queue] git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
30457
+ `[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
30168
30458
  );
30169
30459
  continue;
30170
30460
  }
30171
30461
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
30172
- const slug = path3.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
30462
+ const slug = path5.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
30173
30463
  for (const rel of lines) {
30174
30464
  if (rel.includes("..")) continue;
30175
30465
  try {
30176
- const { stdout: patchContent } = await execFileAsync(
30466
+ const { stdout: patchContent } = await execFileAsync2(
30177
30467
  "git",
30178
30468
  ["diff", "--no-color", repo.stashSha, "--", rel],
30179
30469
  {
@@ -30183,15 +30473,20 @@ async function collectSessionDiffAndNotify(options) {
30183
30473
  );
30184
30474
  if (!patchContent.trim()) continue;
30185
30475
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
30476
+ const absFile = path5.join(repo.path, rel);
30477
+ const newText = readWorkspaceFileAsUtf8(absFile);
30186
30478
  sendSessionUpdate({
30187
30479
  type: "session_file_change",
30188
30480
  sessionId,
30189
30481
  runId,
30190
30482
  path: displayPath,
30191
- patchContent
30483
+ patchContent,
30484
+ ...newText !== void 0 ? { newText } : {}
30192
30485
  });
30193
30486
  } catch (e) {
30194
- log2(`[session-git-queue] git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`);
30487
+ log2(
30488
+ `[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
30489
+ );
30195
30490
  }
30196
30491
  }
30197
30492
  }
@@ -30205,15 +30500,21 @@ async function sendPromptToAgent(options) {
30205
30500
  promptId,
30206
30501
  sessionId,
30207
30502
  runId,
30503
+ agentCwd,
30208
30504
  sendResult,
30209
30505
  sendSessionUpdate,
30210
- collectSessionDiffAfterTurn,
30211
30506
  log: log2
30212
30507
  } = options;
30213
30508
  try {
30214
30509
  const result = await handle.sendPrompt(promptText, {});
30215
- if (collectSessionDiffAfterTurn && sessionId && runId && sendSessionUpdate && result.success) {
30216
- await collectSessionDiffAndNotify({ sessionId, runId, sendSessionUpdate, log: log2 });
30510
+ if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
30511
+ await collectTurnGitDiffFromPreTurnSnapshot({
30512
+ sessionId,
30513
+ runId,
30514
+ agentCwd,
30515
+ sendSessionUpdate,
30516
+ log: log2
30517
+ });
30217
30518
  }
30218
30519
  sendResult({
30219
30520
  type: "prompt_result",
@@ -30223,12 +30524,12 @@ async function sendPromptToAgent(options) {
30223
30524
  ...result
30224
30525
  });
30225
30526
  if (!result.success) {
30226
- log2(`[agent] ${result.error ?? "error"}`);
30527
+ log2(`[Agent] ${result.error ?? "Error"}`);
30227
30528
  }
30228
30529
  } catch (err) {
30229
30530
  const errMsg = err instanceof Error ? err.message : String(err);
30230
- log2(`[agent] send failed: ${errMsg}`);
30231
- if (err instanceof Error && err.stack) log2(`[agent] ${err.stack}`);
30531
+ log2(`[Agent] Send failed: ${errMsg}`);
30532
+ if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
30232
30533
  sendResult({
30233
30534
  type: "prompt_result",
30234
30535
  id: promptId,
@@ -30241,8 +30542,8 @@ async function sendPromptToAgent(options) {
30241
30542
  }
30242
30543
 
30243
30544
  // src/acp/ensure-acp-client.ts
30244
- import * as fs3 from "node:fs";
30245
- import * as path7 from "node:path";
30545
+ import * as fs5 from "node:fs";
30546
+ import * as path9 from "node:path";
30246
30547
 
30247
30548
  // src/error-message.ts
30248
30549
  function errorMessage(err) {
@@ -30273,10 +30574,10 @@ function toErrorMessage(err) {
30273
30574
  }
30274
30575
  async function createAcpClient(options) {
30275
30576
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
30276
- const { command, cwd: cwd3 = process.cwd(), onSessionUpdate } = options;
30577
+ const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate } = options;
30277
30578
  const isWindows = process.platform === "win32";
30278
30579
  const child = spawn2(command[0], command.slice(1), {
30279
- cwd: cwd3,
30580
+ cwd,
30280
30581
  stdio: ["pipe", "pipe", "inherit"],
30281
30582
  env: process.env,
30282
30583
  shell: isWindows
@@ -30308,7 +30609,7 @@ async function createAcpClient(options) {
30308
30609
  capabilities: {},
30309
30610
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
30310
30611
  });
30311
- const newSessionRes = await connection.newSession({ workingDirectory: cwd3 });
30612
+ const newSessionRes = await connection.newSession({ workingDirectory: cwd });
30312
30613
  const sessionId = newSessionRes.sessionId;
30313
30614
  resolve15({
30314
30615
  sessionId,
@@ -30365,27 +30666,27 @@ async function createCodexAcpClient(options) {
30365
30666
  }
30366
30667
 
30367
30668
  // src/acp/clients/cursor-acp-client.ts
30368
- import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
30669
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
30369
30670
  import { dirname } from "node:path";
30370
30671
  import { spawn as spawn3 } from "node:child_process";
30371
30672
  import * as readline from "node:readline";
30372
30673
 
30373
30674
  // src/acp/safe-fs-path.ts
30374
- import * as path4 from "node:path";
30375
- function resolveSafePathUnderCwd(cwd3, filePath) {
30675
+ import * as path6 from "node:path";
30676
+ function resolveSafePathUnderCwd(cwd, filePath) {
30376
30677
  const trimmed2 = filePath.trim();
30377
30678
  if (!trimmed2) return null;
30378
- const normalizedCwd = path4.resolve(cwd3);
30379
- const resolved = path4.isAbsolute(trimmed2) ? path4.normalize(trimmed2) : path4.resolve(normalizedCwd, trimmed2);
30380
- const rel = path4.relative(normalizedCwd, resolved);
30381
- if (rel.startsWith("..") || path4.isAbsolute(rel)) return null;
30679
+ const normalizedCwd = path6.resolve(cwd);
30680
+ const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
30681
+ const rel = path6.relative(normalizedCwd, resolved);
30682
+ if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
30382
30683
  return resolved;
30383
30684
  }
30384
- function toDisplayPathRelativeToCwd(cwd3, absolutePath) {
30385
- const normalizedCwd = path4.resolve(cwd3);
30386
- const rel = path4.relative(normalizedCwd, path4.resolve(absolutePath));
30387
- if (!rel || rel === "") return path4.basename(absolutePath);
30388
- return rel.split(path4.sep).join("/");
30685
+ function toDisplayPathRelativeToCwd(cwd, absolutePath) {
30686
+ const normalizedCwd = path6.resolve(cwd);
30687
+ const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
30688
+ if (!rel || rel === "") return path6.basename(absolutePath);
30689
+ return rel.split(path6.sep).join("/");
30389
30690
  }
30390
30691
 
30391
30692
  // src/files/diff/unified-diff.ts
@@ -30434,6 +30735,19 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
30434
30735
  return out.join("\n");
30435
30736
  }
30436
30737
 
30738
+ // src/acp/format-session-update-kind-for-log.ts
30739
+ var SESSION_UPDATE_KIND_LABELS = {
30740
+ tool_call: "Tool call",
30741
+ tool_call_update: "Tool call status",
30742
+ agent_message_chunk: "Agent message chunk",
30743
+ update: "Session update"
30744
+ };
30745
+ function formatSessionUpdateKindForLog(kind) {
30746
+ const known = SESSION_UPDATE_KIND_LABELS[kind];
30747
+ if (known) return known;
30748
+ return kind.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
30749
+ }
30750
+
30437
30751
  // src/acp/clients/cursor-acp-client.ts
30438
30752
  var FS_READ_METHODS = /* @__PURE__ */ new Set(["fs/read_text_file", "fs/readTextFile"]);
30439
30753
  var FS_WRITE_METHODS = /* @__PURE__ */ new Set(["fs/write_text_file", "fs/writeTextFile"]);
@@ -30459,11 +30773,11 @@ function sliceLinesByRange(content, line, limit) {
30459
30773
  return lines.slice(start, end).join("\n");
30460
30774
  }
30461
30775
  async function createCursorAcpClient(options) {
30462
- const { command, cwd: cwd3 = process.cwd(), onSessionUpdate, onRequest, onFileChange } = options;
30776
+ const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
30463
30777
  const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
30464
30778
  const isWindows = process.platform === "win32";
30465
30779
  const child = spawn3(command[0], command.slice(1), {
30466
- cwd: cwd3,
30780
+ cwd,
30467
30781
  stdio: ["pipe", "pipe", "inherit"],
30468
30782
  env: process.env,
30469
30783
  shell: isWindows
@@ -30521,7 +30835,10 @@ async function createCursorAcpClient(options) {
30521
30835
  const toolCall = update.toolCall ?? update.tool_call;
30522
30836
  const toolName = typeof toolCall?.name === "string" ? toolCall.name : "";
30523
30837
  if (dbgFs && (sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update")) {
30524
- console.error(`[acp] recv session/update kind=${sessionUpdate} tool=${toolName || "(none)"}`);
30838
+ const kindLabel = formatSessionUpdateKindForLog(sessionUpdate ?? "update");
30839
+ console.error(
30840
+ `[acp] Received session update (${kindLabel}) tool=${toolName || "(none)"}`
30841
+ );
30525
30842
  }
30526
30843
  const isTextChunk = sessionUpdate === "agent_message_chunk" && update.content?.text;
30527
30844
  if (isTextChunk && update.content?.text) {
@@ -30541,14 +30858,14 @@ async function createCursorAcpClient(options) {
30541
30858
  if (dbgFs) {
30542
30859
  console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
30543
30860
  }
30544
- const abs = resolveSafePathUnderCwd(cwd3, filePath);
30861
+ const abs = resolveSafePathUnderCwd(cwd, filePath);
30545
30862
  if (!abs) {
30546
30863
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
30547
30864
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
30548
30865
  return;
30549
30866
  }
30550
30867
  try {
30551
- let content = readFileSync(abs, "utf8");
30868
+ let content = readFileSync3(abs, "utf8");
30552
30869
  const line2 = typeof params.line === "number" ? params.line : void 0;
30553
30870
  const limit = typeof params.limit === "number" ? params.limit : void 0;
30554
30871
  content = sliceLinesByRange(content, line2, limit);
@@ -30572,7 +30889,7 @@ async function createCursorAcpClient(options) {
30572
30889
  `[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
30573
30890
  );
30574
30891
  }
30575
- const abs = resolveSafePathUnderCwd(cwd3, filePath);
30892
+ const abs = resolveSafePathUnderCwd(cwd, filePath);
30576
30893
  if (!abs) {
30577
30894
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
30578
30895
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
@@ -30580,7 +30897,7 @@ async function createCursorAcpClient(options) {
30580
30897
  }
30581
30898
  let oldText = "";
30582
30899
  try {
30583
- oldText = readFileSync(abs, "utf8");
30900
+ oldText = readFileSync3(abs, "utf8");
30584
30901
  } catch (e) {
30585
30902
  if (e.code !== "ENOENT") {
30586
30903
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
@@ -30588,13 +30905,13 @@ async function createCursorAcpClient(options) {
30588
30905
  }
30589
30906
  }
30590
30907
  try {
30591
- mkdirSync(dirname(abs), { recursive: true });
30592
- writeFileSync(abs, newText, "utf8");
30908
+ mkdirSync2(dirname(abs), { recursive: true });
30909
+ writeFileSync2(abs, newText, "utf8");
30593
30910
  } catch (e) {
30594
30911
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
30595
30912
  return;
30596
30913
  }
30597
- const displayPath = toDisplayPathRelativeToCwd(cwd3, abs);
30914
+ const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
30598
30915
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
30599
30916
  onFileChange?.({ path: displayPath, oldText, newText, patchContent });
30600
30917
  respond(id, null);
@@ -30636,7 +30953,7 @@ async function createCursorAcpClient(options) {
30636
30953
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
30637
30954
  });
30638
30955
  await send("authenticate", { methodId: "cursor_login" });
30639
- const newResult = await send("session/new", { cwd: cwd3, mcpServers: [] });
30956
+ const newResult = await send("session/new", { cwd, mcpServers: [] });
30640
30957
  const sessionId = newResult?.sessionId ?? "";
30641
30958
  if (!sessionId) throw new Error("Cursor ACP session/new did not return sessionId");
30642
30959
  resolve15({
@@ -30708,20 +31025,20 @@ function resolveAgentCommand(preferredAgentType) {
30708
31025
 
30709
31026
  // src/acp/session-file-change-path-kind.ts
30710
31027
  import { execFileSync as execFileSync3 } from "node:child_process";
30711
- import { existsSync, statSync } from "node:fs";
31028
+ import { existsSync, statSync as statSync2 } from "node:fs";
30712
31029
 
30713
31030
  // src/git/get-git-repo-root-sync.ts
30714
31031
  import { execFileSync } from "node:child_process";
30715
- import * as path5 from "node:path";
31032
+ import * as path7 from "node:path";
30716
31033
  function getGitRepoRootSync(startDir) {
30717
31034
  try {
30718
31035
  const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
30719
- cwd: path5.resolve(startDir),
31036
+ cwd: path7.resolve(startDir),
30720
31037
  encoding: "utf8",
30721
31038
  stdio: ["ignore", "pipe", "ignore"],
30722
31039
  maxBuffer: 1024 * 1024
30723
31040
  }).trim();
30724
- return out ? path5.resolve(out) : null;
31041
+ return out ? path7.resolve(out) : null;
30725
31042
  } catch {
30726
31043
  return null;
30727
31044
  }
@@ -30729,65 +31046,65 @@ function getGitRepoRootSync(startDir) {
30729
31046
 
30730
31047
  // src/acp/workspace-files.ts
30731
31048
  import { execFileSync as execFileSync2 } from "node:child_process";
30732
- import { readFileSync as readFileSync2 } from "node:fs";
30733
- import * as path6 from "node:path";
30734
- function resolveWorkspaceFilePath(cwd3, rawPath) {
31049
+ import { readFileSync as readFileSync4 } from "node:fs";
31050
+ import * as path8 from "node:path";
31051
+ function resolveWorkspaceFilePath(cwd, rawPath) {
30735
31052
  const trimmed2 = rawPath.trim();
30736
31053
  if (!trimmed2) return null;
30737
- const normalizedCwd = path6.resolve(cwd3);
30738
- let abs = resolveSafePathUnderCwd(cwd3, trimmed2);
31054
+ const normalizedCwd = path8.resolve(cwd);
31055
+ let abs = resolveSafePathUnderCwd(cwd, trimmed2);
30739
31056
  if (!abs) {
30740
- const candidate = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.normalize(path6.resolve(normalizedCwd, trimmed2));
30741
- const gitRoot2 = getGitRepoRootSync(cwd3);
31057
+ const candidate = path8.isAbsolute(trimmed2) ? path8.normalize(trimmed2) : path8.normalize(path8.resolve(normalizedCwd, trimmed2));
31058
+ const gitRoot2 = getGitRepoRootSync(cwd);
30742
31059
  if (!gitRoot2) return null;
30743
- const rel = path6.relative(gitRoot2, candidate);
30744
- if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
31060
+ const rel = path8.relative(gitRoot2, candidate);
31061
+ if (rel.startsWith("..") || path8.isAbsolute(rel)) return null;
30745
31062
  abs = candidate;
30746
31063
  }
30747
- const gitRoot = getGitRepoRootSync(cwd3);
31064
+ const gitRoot = getGitRepoRootSync(cwd);
30748
31065
  if (gitRoot) {
30749
- const relFromRoot = path6.relative(gitRoot, abs);
30750
- if (!relFromRoot.startsWith("..") && !path6.isAbsolute(relFromRoot)) {
30751
- return { abs, display: relFromRoot.split(path6.sep).join("/") };
31066
+ const relFromRoot = path8.relative(gitRoot, abs);
31067
+ if (!relFromRoot.startsWith("..") && !path8.isAbsolute(relFromRoot)) {
31068
+ return { abs, display: relFromRoot.split(path8.sep).join("/") };
30752
31069
  }
30753
31070
  }
30754
- return { abs, display: toDisplayPathRelativeToCwd(cwd3, abs) };
31071
+ return { abs, display: toDisplayPathRelativeToCwd(cwd, abs) };
30755
31072
  }
30756
- function readUtf8WorkspaceFile(cwd3, displayPath) {
31073
+ function readUtf8WorkspaceFile(cwd, displayPath) {
30757
31074
  if (!displayPath || displayPath.includes("..")) return "";
30758
- const gitRoot = getGitRepoRootSync(cwd3);
31075
+ const gitRoot = getGitRepoRootSync(cwd);
30759
31076
  if (gitRoot) {
30760
- const abs2 = path6.resolve(gitRoot, displayPath);
30761
- const rel = path6.relative(gitRoot, abs2);
30762
- if (!rel.startsWith("..") && !path6.isAbsolute(rel)) {
31077
+ const abs2 = path8.resolve(gitRoot, displayPath);
31078
+ const rel = path8.relative(gitRoot, abs2);
31079
+ if (!rel.startsWith("..") && !path8.isAbsolute(rel)) {
30763
31080
  try {
30764
- return readFileSync2(abs2, "utf8");
31081
+ return readFileSync4(abs2, "utf8");
30765
31082
  } catch {
30766
31083
  }
30767
31084
  }
30768
31085
  }
30769
- const abs = resolveSafePathUnderCwd(cwd3, displayPath);
31086
+ const abs = resolveSafePathUnderCwd(cwd, displayPath);
30770
31087
  if (!abs) return "";
30771
31088
  try {
30772
- return readFileSync2(abs, "utf8");
31089
+ return readFileSync4(abs, "utf8");
30773
31090
  } catch {
30774
31091
  return "";
30775
31092
  }
30776
31093
  }
30777
- function tryWorkspaceDisplayToAbs(cwd3, displayPath) {
31094
+ function tryWorkspaceDisplayToAbs(cwd, displayPath) {
30778
31095
  if (!displayPath || displayPath.includes("..")) return null;
30779
- const gitRoot = getGitRepoRootSync(cwd3);
31096
+ const gitRoot = getGitRepoRootSync(cwd);
30780
31097
  if (gitRoot) {
30781
- const abs = path6.resolve(gitRoot, displayPath);
30782
- const rel = path6.relative(gitRoot, abs);
30783
- if (!rel.startsWith("..") && !path6.isAbsolute(rel)) return abs;
31098
+ const abs = path8.resolve(gitRoot, displayPath);
31099
+ const rel = path8.relative(gitRoot, abs);
31100
+ if (!rel.startsWith("..") && !path8.isAbsolute(rel)) return abs;
30784
31101
  }
30785
- return resolveSafePathUnderCwd(cwd3, displayPath);
31102
+ return resolveSafePathUnderCwd(cwd, displayPath);
30786
31103
  }
30787
- function readGitHeadBlob(cwd3, displayPath) {
31104
+ function readGitHeadBlob(cwd, displayPath) {
30788
31105
  if (!displayPath || displayPath.includes("..")) return "";
30789
- const gitRoot = getGitRepoRootSync(cwd3);
30790
- const execCwd = gitRoot ?? cwd3;
31106
+ const gitRoot = getGitRepoRootSync(cwd);
31107
+ const execCwd = gitRoot ?? cwd;
30791
31108
  try {
30792
31109
  return execFileSync2("git", ["show", `HEAD:${displayPath}`], {
30793
31110
  cwd: execCwd,
@@ -30800,9 +31117,9 @@ function readGitHeadBlob(cwd3, displayPath) {
30800
31117
  }
30801
31118
 
30802
31119
  // src/acp/session-file-change-path-kind.ts
30803
- function gitHeadPathObjectType(cwd3, displayPath) {
31120
+ function gitHeadPathObjectType(cwd, displayPath) {
30804
31121
  if (!displayPath || displayPath.includes("..")) return null;
30805
- const gitRoot = getGitRepoRootSync(cwd3);
31122
+ const gitRoot = getGitRepoRootSync(cwd);
30806
31123
  if (!gitRoot) return null;
30807
31124
  try {
30808
31125
  return execFileSync3("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
@@ -30813,11 +31130,11 @@ function gitHeadPathObjectType(cwd3, displayPath) {
30813
31130
  return null;
30814
31131
  }
30815
31132
  }
30816
- function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
30817
- const abs = tryWorkspaceDisplayToAbs(cwd3, displayPath);
31133
+ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
31134
+ const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
30818
31135
  if (abs && existsSync(abs)) {
30819
31136
  try {
30820
- if (statSync(abs).isDirectory()) {
31137
+ if (statSync2(abs).isDirectory()) {
30821
31138
  return { isDirectory: true, directoryRemoved: false };
30822
31139
  }
30823
31140
  return { isDirectory: false, directoryRemoved: false };
@@ -30825,7 +31142,7 @@ function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
30825
31142
  return { isDirectory: false, directoryRemoved: false };
30826
31143
  }
30827
31144
  }
30828
- if (gitHeadPathObjectType(cwd3, displayPath) === "tree") {
31145
+ if (gitHeadPathObjectType(cwd, displayPath) === "tree") {
30829
31146
  return { isDirectory: true, directoryRemoved: true };
30830
31147
  }
30831
31148
  return { isDirectory: false, directoryRemoved: false };
@@ -30839,11 +31156,13 @@ function createBridgeOnFileChange(opts) {
30839
31156
  const sessionId = routing.sessionId;
30840
31157
  const send = getSendSessionUpdate();
30841
31158
  if (!send || !runId || !sessionId) {
30842
- log2(`[Bridge service] file change not forwarded path=${evt.path} (session/run not wired)`);
31159
+ log2(
31160
+ `[Bridge service] File change not forwarded (${evt.path}): session or run not wired to the bridge.`
31161
+ );
30843
31162
  return;
30844
31163
  }
30845
- const cwd3 = process.cwd();
30846
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, evt.path);
31164
+ const cwd = getBridgeWorkspaceDirectory();
31165
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
30847
31166
  try {
30848
31167
  send({
30849
31168
  type: "session_file_change",
@@ -30857,7 +31176,7 @@ function createBridgeOnFileChange(opts) {
30857
31176
  directoryRemoved: dirFlags.directoryRemoved
30858
31177
  });
30859
31178
  } catch (err) {
30860
- log2(`[Bridge service] session_file_change failed ${evt.path}: ${errorMessage(err)}`);
31179
+ log2(`[Bridge service] Session file change failed for ${evt.path}: ${errorMessage(err)}`);
30861
31180
  }
30862
31181
  };
30863
31182
  }
@@ -30884,7 +31203,9 @@ function createBridgeOnRequest(opts) {
30884
31203
  }
30885
31204
  });
30886
31205
  } catch (err) {
30887
- log2(`[Bridge service] ACP request forward failed (${request.method}): ${errorMessage(err)}`);
31206
+ log2(
31207
+ `[Bridge service] Agent protocol request forward failed (${request.method}): ${errorMessage(err)}`
31208
+ );
30888
31209
  }
30889
31210
  };
30890
31211
  }
@@ -30916,12 +31237,12 @@ function extractDiffPath(o) {
30916
31237
  }
30917
31238
 
30918
31239
  // src/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
30919
- function pushDiffIfComplete(o, cwd3, out) {
31240
+ function pushDiffIfComplete(o, cwd, out) {
30920
31241
  const t = o.type;
30921
31242
  if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
30922
31243
  const rawPath = extractDiffPath(o);
30923
31244
  if (!rawPath) return;
30924
- const resolved = resolveWorkspaceFilePath(cwd3, rawPath);
31245
+ const resolved = resolveWorkspaceFilePath(cwd, rawPath);
30925
31246
  if (!resolved) return;
30926
31247
  const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
30927
31248
  const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
@@ -30940,17 +31261,17 @@ var NEST_KEYS = [
30940
31261
  "data",
30941
31262
  "arguments"
30942
31263
  ];
30943
- function walkValue(value, cwd3, depth, out) {
31264
+ function walkValue(value, cwd, depth, out) {
30944
31265
  if (depth > 12 || value == null) return;
30945
31266
  if (Array.isArray(value)) {
30946
- for (const x of value) walkValue(x, cwd3, depth + 1, out);
31267
+ for (const x of value) walkValue(x, cwd, depth + 1, out);
30947
31268
  return;
30948
31269
  }
30949
31270
  if (typeof value !== "object") return;
30950
31271
  const o = value;
30951
- pushDiffIfComplete(o, cwd3, out);
31272
+ pushDiffIfComplete(o, cwd, out);
30952
31273
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
30953
- walkValue(o.content, cwd3, depth + 1, out);
31274
+ walkValue(o.content, cwd, depth + 1, out);
30954
31275
  }
30955
31276
  for (const k of NEST_KEYS) {
30956
31277
  if (o.type === "content" && k === "content") continue;
@@ -30958,23 +31279,23 @@ function walkValue(value, cwd3, depth, out) {
30958
31279
  if (v == null) continue;
30959
31280
  if (k === "arguments" && typeof v === "string") {
30960
31281
  try {
30961
- walkValue(JSON.parse(v), cwd3, depth + 1, out);
31282
+ walkValue(JSON.parse(v), cwd, depth + 1, out);
30962
31283
  } catch {
30963
31284
  }
30964
31285
  continue;
30965
31286
  }
30966
- walkValue(v, cwd3, depth + 1, out);
31287
+ walkValue(v, cwd, depth + 1, out);
30967
31288
  }
30968
31289
  }
30969
31290
 
30970
31291
  // src/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
30971
- function extractAcpFileDiffsFromUpdate(update, cwd3) {
31292
+ function extractAcpFileDiffsFromUpdate(update, cwd) {
30972
31293
  if (!update || typeof update !== "object") return [];
30973
31294
  const u = update;
30974
31295
  const out = [];
30975
31296
  const content = u.content;
30976
31297
  if (Array.isArray(content)) {
30977
- for (const x of content) walkValue(x, cwd3, 0, out);
31298
+ for (const x of content) walkValue(x, cwd, 0, out);
30978
31299
  }
30979
31300
  const byPath = /* @__PURE__ */ new Map();
30980
31301
  for (const d of out) byPath.set(d.path, d);
@@ -30982,18 +31303,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd3) {
30982
31303
  }
30983
31304
 
30984
31305
  // src/acp/hooks/extract-tool-target-paths.ts
30985
- function addPath(cwd3, raw, out) {
31306
+ function addPath(cwd, raw, out) {
30986
31307
  if (typeof raw !== "string") return;
30987
31308
  const trimmed2 = raw.trim();
30988
31309
  if (!trimmed2) return;
30989
- const resolved = resolveWorkspaceFilePath(cwd3, trimmed2);
31310
+ const resolved = resolveWorkspaceFilePath(cwd, trimmed2);
30990
31311
  if (!resolved) return;
30991
31312
  out.add(resolved.display);
30992
31313
  }
30993
- function walkLocations(cwd3, loc, out) {
31314
+ function walkLocations(cwd, loc, out) {
30994
31315
  if (!Array.isArray(loc)) return;
30995
31316
  for (const item of loc) {
30996
- if (item && typeof item === "object") addPath(cwd3, item.path, out);
31317
+ if (item && typeof item === "object") addPath(cwd, item.path, out);
30997
31318
  }
30998
31319
  }
30999
31320
  var PATH_KEYS = [
@@ -31006,56 +31327,56 @@ var PATH_KEYS = [
31006
31327
  "file_path",
31007
31328
  "target_file"
31008
31329
  ];
31009
- function collectFromObject(cwd3, obj, out, depth) {
31330
+ function collectFromObject(cwd, obj, out, depth) {
31010
31331
  if (depth > 10) return;
31011
31332
  for (const k of PATH_KEYS) {
31012
- if (k in obj) addPath(cwd3, obj[k], out);
31333
+ if (k in obj) addPath(cwd, obj[k], out);
31013
31334
  }
31014
31335
  for (const v of Object.values(obj)) {
31015
- if (v != null && typeof v === "object") collectUnknown(cwd3, v, out, depth + 1);
31336
+ if (v != null && typeof v === "object") collectUnknown(cwd, v, out, depth + 1);
31016
31337
  }
31017
31338
  }
31018
- function collectUnknown(cwd3, v, out, depth) {
31339
+ function collectUnknown(cwd, v, out, depth) {
31019
31340
  if (depth > 10 || v == null) return;
31020
31341
  if (Array.isArray(v)) {
31021
- for (const x of v) collectUnknown(cwd3, x, out, depth + 1);
31342
+ for (const x of v) collectUnknown(cwd, x, out, depth + 1);
31022
31343
  return;
31023
31344
  }
31024
- if (typeof v === "object") collectFromObject(cwd3, v, out, depth);
31345
+ if (typeof v === "object") collectFromObject(cwd, v, out, depth);
31025
31346
  }
31026
- function walkContentArray(cwd3, content, out) {
31347
+ function walkContentArray(cwd, content, out) {
31027
31348
  if (!Array.isArray(content)) return;
31028
31349
  for (const item of content) {
31029
31350
  if (!item || typeof item !== "object") continue;
31030
31351
  const o = item;
31031
31352
  if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
31032
- for (const k of PATH_KEYS) if (k in o) addPath(cwd3, o[k], out);
31353
+ for (const k of PATH_KEYS) if (k in o) addPath(cwd, o[k], out);
31033
31354
  }
31034
31355
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
31035
31356
  const inner = o.content;
31036
31357
  if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
31037
- for (const k of PATH_KEYS) if (k in inner) addPath(cwd3, inner[k], out);
31358
+ for (const k of PATH_KEYS) if (k in inner) addPath(cwd, inner[k], out);
31038
31359
  }
31039
31360
  }
31040
31361
  }
31041
31362
  }
31042
- function extractToolTargetDisplayPaths(update, cwd3) {
31363
+ function extractToolTargetDisplayPaths(update, cwd) {
31043
31364
  const out = /* @__PURE__ */ new Set();
31044
31365
  if (!update || typeof update !== "object") return [];
31045
31366
  const u = update;
31046
- walkLocations(cwd3, u.locations, out);
31047
- walkLocations(cwd3, u.fileLocations, out);
31048
- walkLocations(cwd3, u.file_locations, out);
31367
+ walkLocations(cwd, u.locations, out);
31368
+ walkLocations(cwd, u.fileLocations, out);
31369
+ walkLocations(cwd, u.file_locations, out);
31049
31370
  const tc = u.toolCall ?? u.tool_call;
31050
31371
  if (tc && typeof tc.arguments === "string") {
31051
31372
  try {
31052
31373
  const parsed = JSON.parse(tc.arguments);
31053
- collectUnknown(cwd3, parsed, out, 0);
31374
+ collectUnknown(cwd, parsed, out, 0);
31054
31375
  } catch {
31055
31376
  }
31056
31377
  }
31057
- walkContentArray(cwd3, u.content, out);
31058
- collectFromObject(cwd3, u, out, 0);
31378
+ walkContentArray(cwd, u.content, out);
31379
+ collectFromObject(cwd, u, out, 0);
31059
31380
  return [...out];
31060
31381
  }
31061
31382
 
@@ -31121,7 +31442,7 @@ var PathSnapshotTracker = class {
31121
31442
  this.beforeByToolKey.delete(toolKey);
31122
31443
  this.accumulatedPathsByToolKey.delete(toolKey);
31123
31444
  }
31124
- captureBeforeFromDisk(toolKey, paths, cwd3) {
31445
+ captureBeforeFromDisk(toolKey, paths, cwd) {
31125
31446
  if (paths.length === 0) return;
31126
31447
  let m = this.beforeByToolKey.get(toolKey);
31127
31448
  if (!m) {
@@ -31130,10 +31451,10 @@ var PathSnapshotTracker = class {
31130
31451
  }
31131
31452
  for (const p of paths) {
31132
31453
  if (m.has(p)) continue;
31133
- m.set(p, readUtf8WorkspaceFile(cwd3, p));
31454
+ m.set(p, readUtf8WorkspaceFile(cwd, p));
31134
31455
  }
31135
31456
  }
31136
- ensureBeforeFromHeadForMissing(toolKey, paths, cwd3) {
31457
+ ensureBeforeFromHeadForMissing(toolKey, paths, cwd) {
31137
31458
  let m = this.beforeByToolKey.get(toolKey);
31138
31459
  if (!m) {
31139
31460
  m = /* @__PURE__ */ new Map();
@@ -31141,10 +31462,10 @@ var PathSnapshotTracker = class {
31141
31462
  }
31142
31463
  for (const p of paths) {
31143
31464
  if (m.has(p)) continue;
31144
- m.set(p, readGitHeadBlob(cwd3, p));
31465
+ m.set(p, readGitHeadBlob(cwd, p));
31145
31466
  }
31146
31467
  }
31147
- flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2) {
31468
+ flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
31148
31469
  const t = this.debouncers.get(toolKey);
31149
31470
  if (t) clearTimeout(t);
31150
31471
  this.debouncers.delete(toolKey);
@@ -31153,10 +31474,10 @@ var PathSnapshotTracker = class {
31153
31474
  this.beforeByToolKey.delete(toolKey);
31154
31475
  if (!send || !runId || !sessionId) return;
31155
31476
  for (const [displayPath, oldText] of beforeMap) {
31156
- const newText = readUtf8WorkspaceFile(cwd3, displayPath);
31477
+ const newText = readUtf8WorkspaceFile(cwd, displayPath);
31157
31478
  if (oldText === newText) continue;
31158
31479
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
31159
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, displayPath);
31480
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
31160
31481
  try {
31161
31482
  send({
31162
31483
  type: "session_file_change",
@@ -31171,35 +31492,35 @@ var PathSnapshotTracker = class {
31171
31492
  });
31172
31493
  sentPaths.add(displayPath);
31173
31494
  } catch (err) {
31174
- log2(`[Bridge service] session_file_change failed ${displayPath}: ${errorMessage(err)}`);
31495
+ log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
31175
31496
  }
31176
31497
  }
31177
31498
  }
31178
- scheduleDebouncedFlush(toolKey, cwd3, sentPaths, send, runId, sessionId, log2) {
31499
+ scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
31179
31500
  const prev = this.debouncers.get(toolKey);
31180
31501
  if (prev) clearTimeout(prev);
31181
31502
  this.debouncers.set(
31182
31503
  toolKey,
31183
31504
  setTimeout(() => {
31184
31505
  this.debouncers.delete(toolKey);
31185
- this.flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
31506
+ this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
31186
31507
  }, PATH_SNAPSHOT_DEBOUNCE_MS)
31187
31508
  );
31188
31509
  }
31189
- handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd3, sentPaths, send, runId, sessionId, log2) {
31510
+ handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd, sentPaths, send, runId, sessionId, log2) {
31190
31511
  if (updateKind === "tool_call") {
31191
31512
  this.resetToolSnapshots(toolKey);
31192
31513
  }
31193
31514
  if (updateKind === "tool_call") {
31194
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd3);
31515
+ this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
31195
31516
  } else if (updateKind === "tool_call_update") {
31196
31517
  if (isCompletedToolStatus(status)) {
31197
- this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd3);
31198
- this.flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
31518
+ this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd);
31519
+ this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
31199
31520
  } else {
31200
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd3);
31521
+ this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
31201
31522
  if (this.beforeByToolKey.has(toolKey)) {
31202
- this.scheduleDebouncedFlush(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
31523
+ this.scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
31203
31524
  }
31204
31525
  }
31205
31526
  }
@@ -31207,11 +31528,11 @@ var PathSnapshotTracker = class {
31207
31528
  };
31208
31529
 
31209
31530
  // src/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
31210
- function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, runId, sentPaths, log2) {
31531
+ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentPaths, log2) {
31211
31532
  for (const d of diffs) {
31212
31533
  try {
31213
31534
  const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
31214
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, d.path);
31535
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, d.path);
31215
31536
  send({
31216
31537
  type: "session_file_change",
31217
31538
  sessionId,
@@ -31225,19 +31546,19 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, ru
31225
31546
  });
31226
31547
  sentPaths.add(d.path);
31227
31548
  } catch (err) {
31228
- log2(`[Bridge service] session_file_change failed ${d.path}: ${errorMessage(err)}`);
31549
+ log2(`[Bridge service] Session file change failed for ${d.path}: ${errorMessage(err)}`);
31229
31550
  }
31230
31551
  }
31231
31552
  }
31232
- function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3, sessionId, runId, log2) {
31553
+ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, sessionId, runId, log2) {
31233
31554
  for (const displayPath of mergedPaths) {
31234
31555
  if (sentPaths.has(displayPath)) continue;
31235
- const oldText = readGitHeadBlob(cwd3, displayPath);
31236
- const newText = readUtf8WorkspaceFile(cwd3, displayPath);
31556
+ const oldText = readGitHeadBlob(cwd, displayPath);
31557
+ const newText = readUtf8WorkspaceFile(cwd, displayPath);
31237
31558
  if (oldText === newText) continue;
31238
31559
  try {
31239
31560
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
31240
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, displayPath);
31561
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
31241
31562
  send({
31242
31563
  type: "session_file_change",
31243
31564
  sessionId,
@@ -31251,7 +31572,7 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3,
31251
31572
  });
31252
31573
  sentPaths.add(displayPath);
31253
31574
  } catch (err) {
31254
- log2(`[Bridge service] session_file_change failed ${displayPath}: ${errorMessage(err)}`);
31575
+ log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
31255
31576
  }
31256
31577
  }
31257
31578
  }
@@ -31264,7 +31585,7 @@ function createBridgeOnSessionUpdate(opts) {
31264
31585
  const runId = routing.runId;
31265
31586
  const sessionId = routing.sessionId;
31266
31587
  pathTracker.onRunIdChanged(runId);
31267
- const cwd3 = process.cwd();
31588
+ const cwd = getBridgeWorkspaceDirectory();
31268
31589
  const send = getSendSessionUpdate();
31269
31590
  const sentFileChangePaths = /* @__PURE__ */ new Set();
31270
31591
  const p = params;
@@ -31272,7 +31593,7 @@ function createBridgeOnSessionUpdate(opts) {
31272
31593
  const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
31273
31594
  const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
31274
31595
  const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
31275
- const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd3) : [];
31596
+ const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd) : [];
31276
31597
  const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
31277
31598
  if (updateKind === "tool_call") {
31278
31599
  pathTracker.resetToolSnapshots(toolKey);
@@ -31287,7 +31608,7 @@ function createBridgeOnSessionUpdate(opts) {
31287
31608
  toolKey,
31288
31609
  toolPaths,
31289
31610
  p.status,
31290
- cwd3,
31611
+ cwd,
31291
31612
  sentFileChangePaths,
31292
31613
  deliver,
31293
31614
  runId ?? "",
@@ -31295,18 +31616,18 @@ function createBridgeOnSessionUpdate(opts) {
31295
31616
  log2
31296
31617
  );
31297
31618
  }
31298
- const diffs = extractAcpFileDiffsFromUpdate(params, cwd3);
31619
+ const diffs = extractAcpFileDiffsFromUpdate(params, cwd);
31299
31620
  if (diffs.length > 0 && send && runId && sessionId) {
31300
- sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, runId, sentFileChangePaths, log2);
31621
+ sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentFileChangePaths, log2);
31301
31622
  } else if (diffs.length > 0) {
31302
31623
  log2(
31303
- `[Bridge service] ACP file diff(s) not forwarded (${diffs.length}): session/run not wired to bridge`
31624
+ `[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
31304
31625
  );
31305
31626
  }
31306
31627
  if (isCompletedToolCallUpdate && send && runId && sessionId) {
31307
31628
  const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
31308
31629
  const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
31309
- sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd3, sessionId, runId, log2);
31630
+ sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd, sessionId, runId, log2);
31310
31631
  pathTracker.accumulatedPathsByToolKey.delete(toolKey);
31311
31632
  }
31312
31633
  if (runId && send) {
@@ -31319,7 +31640,9 @@ function createBridgeOnSessionUpdate(opts) {
31319
31640
  payload: params
31320
31641
  });
31321
31642
  } catch (err) {
31322
- log2(`[Bridge service] session_update send failed (${updateKind}): ${errorMessage(err)}`);
31643
+ log2(
31644
+ `[Bridge service] Session update send failed (${formatSessionUpdateKindForLog(updateKind)}): ${errorMessage(err)}`
31645
+ );
31323
31646
  }
31324
31647
  }
31325
31648
  };
@@ -31336,11 +31659,11 @@ function buildAcpSessionBridgeHooks(opts) {
31336
31659
 
31337
31660
  // src/acp/ensure-acp-client.ts
31338
31661
  async function ensureAcpClient(options) {
31339
- const { state, preferredAgentType, mode, cwd: cwd3, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
31340
- const targetCwd = path7.resolve(
31341
- cwd3 != null && String(cwd3).trim() !== "" ? String(cwd3).trim() : process.cwd()
31662
+ const { state, preferredAgentType, mode, cwd, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
31663
+ const targetCwd = path9.resolve(
31664
+ cwd != null && String(cwd).trim() !== "" ? String(cwd).trim() : getBridgeWorkspaceDirectory()
31342
31665
  );
31343
- if (state.acpHandle && state.lastAcpCwd != null && path7.resolve(state.lastAcpCwd) !== path7.resolve(targetCwd)) {
31666
+ if (state.acpHandle && state.lastAcpCwd != null && path9.resolve(state.lastAcpCwd) !== path9.resolve(targetCwd)) {
31344
31667
  try {
31345
31668
  state.acpHandle.disconnect();
31346
31669
  } catch {
@@ -31352,7 +31675,7 @@ async function ensureAcpClient(options) {
31352
31675
  const resolved = resolveAgentCommand(preferredAgentType);
31353
31676
  if (!resolved) {
31354
31677
  log2(
31355
- `[agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agentType on prompts or agent_config after identify.`
31678
+ `[Agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agent type on prompts or agent configuration after identify.`
31356
31679
  );
31357
31680
  state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
31358
31681
  return null;
@@ -31371,15 +31694,15 @@ async function ensureAcpClient(options) {
31371
31694
  if (!state.acpStartPromise) {
31372
31695
  let statOk = false;
31373
31696
  try {
31374
- const st = fs3.statSync(targetCwd);
31697
+ const st = fs5.statSync(targetCwd);
31375
31698
  statOk = st.isDirectory();
31376
31699
  if (!statOk) {
31377
31700
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
31378
- log2(`[agent] ${state.lastAcpStartError}`);
31701
+ log2(`[Agent] ${state.lastAcpStartError}`);
31379
31702
  }
31380
31703
  } catch {
31381
31704
  state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetCwd}`;
31382
- log2(`[agent] ${state.lastAcpStartError}`);
31705
+ log2(`[Agent] ${state.lastAcpStartError}`);
31383
31706
  }
31384
31707
  if (!statOk) {
31385
31708
  return null;
@@ -31404,7 +31727,7 @@ async function ensureAcpClient(options) {
31404
31727
  return h;
31405
31728
  }).catch((err) => {
31406
31729
  state.lastAcpStartError = errorMessage(err);
31407
- log2(`[agent] failed to start: ${state.lastAcpStartError}`);
31730
+ log2(`[Agent] Failed to start: ${state.lastAcpStartError}`);
31408
31731
  state.acpStartPromise = null;
31409
31732
  state.acpAgentKey = null;
31410
31733
  return null;
@@ -31440,10 +31763,9 @@ async function createAcpManager(options) {
31440
31763
  runId,
31441
31764
  mode,
31442
31765
  agentType,
31443
- cwd: cwd3,
31766
+ cwd,
31444
31767
  sendResult,
31445
- sendSessionUpdate,
31446
- collectSessionDiffAfterTurn
31768
+ sendSessionUpdate
31447
31769
  } = opts;
31448
31770
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
31449
31771
  pendingCancelRunId = void 0;
@@ -31455,7 +31777,7 @@ async function createAcpManager(options) {
31455
31777
  state,
31456
31778
  preferredAgentType: preferredForPrompt,
31457
31779
  mode,
31458
- cwd: cwd3,
31780
+ cwd,
31459
31781
  routing: promptRouting,
31460
31782
  sendSessionUpdate,
31461
31783
  sendRequest: sendSessionUpdate,
@@ -31496,9 +31818,9 @@ async function createAcpManager(options) {
31496
31818
  promptId,
31497
31819
  sessionId,
31498
31820
  runId,
31821
+ agentCwd: cwd,
31499
31822
  sendResult,
31500
31823
  sendSessionUpdate,
31501
- collectSessionDiffAfterTurn,
31502
31824
  log: log2
31503
31825
  });
31504
31826
  }
@@ -31517,7 +31839,7 @@ async function createAcpManager(options) {
31517
31839
  await handle.cancel();
31518
31840
  return true;
31519
31841
  } catch (err) {
31520
- log2(`[agent] cancel failed: ${err instanceof Error ? err.message : String(err)}`);
31842
+ log2(`[Agent] Cancel failed: ${err instanceof Error ? err.message : String(err)}`);
31521
31843
  return false;
31522
31844
  }
31523
31845
  }
@@ -31554,7 +31876,9 @@ var handleBridgeIdentified = (msg, deps) => {
31554
31876
  try {
31555
31877
  await deps.reportAutoDetectedAgents?.();
31556
31878
  } catch (e) {
31557
- deps.log(`[Bridge service] auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`);
31879
+ deps.log(
31880
+ `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
31881
+ );
31558
31882
  }
31559
31883
  })();
31560
31884
  });
@@ -31562,7 +31886,9 @@ var handleBridgeIdentified = (msg, deps) => {
31562
31886
  try {
31563
31887
  deps.sendLocalSkillsReport?.();
31564
31888
  } catch (e) {
31565
- deps.log(`[Bridge service] local skills report failed: ${e instanceof Error ? e.message : String(e)}`);
31889
+ deps.log(
31890
+ `[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
31891
+ );
31566
31892
  }
31567
31893
  });
31568
31894
  };
@@ -31579,12 +31905,12 @@ var handleAgentConfigMessage = (msg, deps) => {
31579
31905
  };
31580
31906
 
31581
31907
  // src/acp/from-bridge/handle-bridge-prompt.ts
31582
- import * as path10 from "node:path";
31908
+ import * as path11 from "node:path";
31583
31909
  import { execFile as execFile3 } from "node:child_process";
31584
31910
  import { promisify as promisify3 } from "node:util";
31585
31911
 
31586
31912
  // src/git/bridge-queue-key.ts
31587
- import * as path8 from "node:path";
31913
+ import * as path10 from "node:path";
31588
31914
  import { createHash } from "node:crypto";
31589
31915
  function normalizeCanonicalGitUrl(url2) {
31590
31916
  let s = url2.trim();
@@ -31612,13 +31938,13 @@ function canonicalUrlToRepoIdSync(url2) {
31612
31938
  return createHash("sha256").update(normalized).digest("hex").slice(0, 32);
31613
31939
  }
31614
31940
  function fallbackRepoIdFromPath(absPath) {
31615
- return createHash("sha256").update(path8.resolve(absPath)).digest("hex").slice(0, 32);
31941
+ return createHash("sha256").update(path10.resolve(absPath)).digest("hex").slice(0, 32);
31616
31942
  }
31617
31943
  async function resolveBridgeQueueBindFields(options) {
31618
31944
  const { effectiveCwd, worktreePaths, primaryRepoRoots, log: log2 } = options;
31619
- const cwdAbs = worktreePaths.length > 0 ? path8.resolve(worktreePaths[0]) : path8.resolve(effectiveCwd);
31945
+ const cwdAbs = worktreePaths.length > 0 ? path10.resolve(worktreePaths[0]) : path10.resolve(effectiveCwd);
31620
31946
  if (!primaryRepoRoots.length) {
31621
- log2("[Bridge service] prompt queue bind skipped: no git repo roots under cwd");
31947
+ log2("[Bridge service] Prompt queue bind skipped: no Git repository roots under the working directory.");
31622
31948
  return null;
31623
31949
  }
31624
31950
  let primaryRoot = primaryRepoRoots[0];
@@ -31638,115 +31964,11 @@ async function resolveBridgeQueueBindFields(options) {
31638
31964
  return { canonicalQueueKey, repoId, cwdAbs };
31639
31965
  }
31640
31966
 
31641
- // src/git/pre-turn-snapshot.ts
31642
- import * as fs4 from "node:fs";
31643
- import * as path9 from "node:path";
31644
- import { execFile as execFile2 } from "node:child_process";
31645
- import { promisify as promisify2 } from "node:util";
31646
- var execFileAsync2 = promisify2(execFile2);
31647
- function snapshotsDirForCwd(agentCwd) {
31648
- return path9.join(agentCwd, ".buildautomaton", "snapshots");
31649
- }
31650
- async function gitStashCreate2(repoRoot, log2) {
31651
- try {
31652
- const { stdout } = await execFileAsync2("git", ["stash", "create"], {
31653
- cwd: repoRoot,
31654
- maxBuffer: 10 * 1024 * 1024
31655
- });
31656
- return stdout.trim();
31657
- } catch (e) {
31658
- log2(`[snapshot] git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
31659
- return "";
31660
- }
31661
- }
31662
- async function gitRun(repoRoot, args, log2, label) {
31663
- try {
31664
- await execFileAsync2("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
31665
- return { ok: true };
31666
- } catch (e) {
31667
- const msg = e instanceof Error ? e.message : String(e);
31668
- log2(`[snapshot] git ${label} failed in ${repoRoot}: ${msg}`);
31669
- return { ok: false, error: msg };
31670
- }
31671
- }
31672
- async function resolveSnapshotRepoRoots(options) {
31673
- const { worktreePaths, fallbackCwd, log: log2 } = options;
31674
- if (worktreePaths?.length) {
31675
- const uniq = [...new Set(worktreePaths.map((p) => path9.resolve(p)))];
31676
- return uniq;
31677
- }
31678
- try {
31679
- const repos = await discoverGitReposUnderRoot(fallbackCwd);
31680
- return repos.map((r) => r.absolutePath);
31681
- } catch (e) {
31682
- log2(`[snapshot] discover repos failed: ${e instanceof Error ? e.message : String(e)}`);
31683
- return [];
31684
- }
31685
- }
31686
- async function capturePreTurnSnapshot(options) {
31687
- const { runId, repoRoots, agentCwd, log: log2 } = options;
31688
- if (!runId || !repoRoots.length) {
31689
- return { ok: false, error: "No git repos to snapshot" };
31690
- }
31691
- const repos = [];
31692
- for (const root of repoRoots) {
31693
- const stashSha = await gitStashCreate2(root, log2);
31694
- repos.push({ path: root, stashSha });
31695
- }
31696
- const dir = snapshotsDirForCwd(agentCwd);
31697
- try {
31698
- fs4.mkdirSync(dir, { recursive: true });
31699
- } catch (e) {
31700
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
31701
- }
31702
- const payload = {
31703
- runId,
31704
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
31705
- repos
31706
- };
31707
- const filePath = path9.join(dir, `${runId}.json`);
31708
- try {
31709
- fs4.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
31710
- } catch (e) {
31711
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
31712
- }
31713
- log2(`[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)) \u2192 ${filePath}`);
31714
- return { ok: true, filePath, repos };
31715
- }
31716
- async function applyPreTurnSnapshot(filePath, log2) {
31717
- let data;
31718
- try {
31719
- const raw = fs4.readFileSync(filePath, "utf8");
31720
- data = JSON.parse(raw);
31721
- } catch (e) {
31722
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
31723
- }
31724
- if (!Array.isArray(data.repos)) {
31725
- return { ok: false, error: "Invalid snapshot file" };
31726
- }
31727
- for (const r of data.repos) {
31728
- if (!r.path) continue;
31729
- const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
31730
- if (!reset.ok) return reset;
31731
- const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
31732
- if (!clean.ok) return clean;
31733
- if (r.stashSha) {
31734
- const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
31735
- if (!ap.ok) return ap;
31736
- }
31737
- }
31738
- log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
31739
- return { ok: true };
31740
- }
31741
- function snapshotFilePath(agentCwd, runId) {
31742
- return path9.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
31743
- }
31744
-
31745
31967
  // src/acp/from-bridge/handle-bridge-prompt.ts
31746
31968
  var execFileAsync3 = promisify3(execFile3);
31747
- async function readGitBranch(cwd3) {
31969
+ async function readGitBranch(cwd) {
31748
31970
  try {
31749
- const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: cwd3, maxBuffer: 64 * 1024 });
31971
+ const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
31750
31972
  const b = stdout.trim();
31751
31973
  return b || null;
31752
31974
  } catch {
@@ -31759,7 +31981,7 @@ function handleBridgePrompt(msg, deps) {
31759
31981
  const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
31760
31982
  if (!promptText.trim()) {
31761
31983
  log2(
31762
- `[Bridge service] prompt ignored: empty or missing prompt field (sessionId=${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026 runId=${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026)`
31984
+ `[Bridge service] Prompt ignored: empty or missing prompt text (session ${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026, run ${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026).`
31763
31985
  );
31764
31986
  return;
31765
31987
  }
@@ -31768,8 +31990,6 @@ function handleBridgePrompt(msg, deps) {
31768
31990
  const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
31769
31991
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
31770
31992
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
31771
- const sessionGitQueueStart = msg.sessionGitQueueStart === true;
31772
- const collectSessionDiffAfterTurn = msg.collectSessionDiffAfterTurn === true;
31773
31993
  const sendResult = (result) => {
31774
31994
  const s = getWs();
31775
31995
  if (s) sendWsMessage(s, result);
@@ -31777,7 +31997,7 @@ function handleBridgePrompt(msg, deps) {
31777
31997
  const sendSessionUpdate = (payload) => {
31778
31998
  const s = getWs();
31779
31999
  if (!s) {
31780
- log2("[Bridge service] session_update not sent: not connected");
32000
+ log2("[Bridge service] Session update not sent: not connected to the bridge.");
31781
32001
  return;
31782
32002
  }
31783
32003
  const p = payload;
@@ -31785,7 +32005,7 @@ function handleBridgePrompt(msg, deps) {
31785
32005
  };
31786
32006
  async function preambleAndPrompt(resolvedCwd) {
31787
32007
  const s = getWs();
31788
- const effectiveCwd = path10.resolve(resolvedCwd ?? process.cwd());
32008
+ const effectiveCwd = path11.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
31789
32009
  const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
31790
32010
  const repoRoots = await resolveSnapshotRepoRoots({
31791
32011
  worktreePaths,
@@ -31818,9 +32038,6 @@ function handleBridgePrompt(msg, deps) {
31818
32038
  agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
31819
32039
  });
31820
32040
  }
31821
- if (sessionGitQueueStart && sessionId && repoRoots.length > 0) {
31822
- await captureSessionGitBoundary({ sessionId, repoRoots, log: log2 });
31823
- }
31824
32041
  if (s && sessionId && runId) {
31825
32042
  const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
31826
32043
  sendWsMessage(s, {
@@ -31839,12 +32056,11 @@ function handleBridgePrompt(msg, deps) {
31839
32056
  agentType,
31840
32057
  cwd: effectiveCwd,
31841
32058
  sendResult,
31842
- sendSessionUpdate,
31843
- collectSessionDiffAfterTurn
32059
+ sendSessionUpdate
31844
32060
  });
31845
32061
  }
31846
- void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd3) => preambleAndPrompt(cwd3)).catch((err) => {
31847
- log2(`[agent] worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
32062
+ void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
32063
+ log2(`[Agent] Worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
31848
32064
  void preambleAndPrompt(void 0);
31849
32065
  });
31850
32066
  }
@@ -31861,7 +32077,7 @@ function handleBridgeCancelRun(msg, { log: log2, acpManager }) {
31861
32077
  void acpManager.cancelRun(runId).then((sent) => {
31862
32078
  if (!sent) {
31863
32079
  log2(
31864
- `[agent] cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available)`
32080
+ `[Agent] Cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available).`
31865
32081
  );
31866
32082
  }
31867
32083
  });
@@ -31964,7 +32180,7 @@ var previewSkill = {
31964
32180
  const exe = parts[0];
31965
32181
  const args = parts.slice(1);
31966
32182
  previewProcess = spawn4(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
31967
- cwd: process.cwd(),
32183
+ cwd: getBridgeWorkspaceDirectory(),
31968
32184
  stdio: ["ignore", "pipe", "pipe"],
31969
32185
  env: {
31970
32186
  ...process.env,
@@ -32063,7 +32279,7 @@ function handleSkillCall(msg, socket, log2) {
32063
32279
  sendWsMessage(socket, { type: "skill_result", id: msg.id, result });
32064
32280
  }).catch((err) => {
32065
32281
  sendWsMessage(socket, { type: "skill_result", id: msg.id, error: String(err) });
32066
- log2(`[Bridge service] skill_call failed (${msg.skillId}/${msg.operationId}): ${err}`);
32282
+ log2(`[Bridge service] Skill invocation failed (${msg.skillId}/${msg.operationId}): ${err}`);
32067
32283
  });
32068
32284
  }
32069
32285
 
@@ -32080,36 +32296,35 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
32080
32296
  };
32081
32297
 
32082
32298
  // src/files/list-dir.ts
32083
- import fs5 from "node:fs";
32084
- import path12 from "node:path";
32299
+ import fs6 from "node:fs";
32300
+ import path13 from "node:path";
32085
32301
 
32086
32302
  // src/files/ensure-under-cwd.ts
32087
- import path11 from "node:path";
32088
- function ensureUnderCwd(relativePath, cwd3 = process.cwd()) {
32089
- const normalized = path11.normalize(relativePath).replace(/^(\.\/)+/, "");
32090
- const resolved = path11.resolve(cwd3, normalized);
32091
- if (!resolved.startsWith(cwd3 + path11.sep) && resolved !== cwd3) {
32303
+ import path12 from "node:path";
32304
+ function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
32305
+ const normalized = path12.normalize(relativePath).replace(/^(\.\/)+/, "");
32306
+ const resolved = path12.resolve(cwd, normalized);
32307
+ if (!resolved.startsWith(cwd + path12.sep) && resolved !== cwd) {
32092
32308
  return null;
32093
32309
  }
32094
32310
  return resolved;
32095
32311
  }
32096
32312
 
32097
32313
  // src/files/list-dir.ts
32098
- var cwd = process.cwd();
32099
32314
  function listDir(relativePath) {
32100
- const resolved = ensureUnderCwd(relativePath || ".", cwd);
32315
+ const resolved = ensureUnderCwd(relativePath || ".", getBridgeWorkspaceDirectory());
32101
32316
  if (!resolved) {
32102
32317
  return { error: "Path is outside working directory" };
32103
32318
  }
32104
32319
  try {
32105
- const names = fs5.readdirSync(resolved, { withFileTypes: true });
32320
+ const names = fs6.readdirSync(resolved, { withFileTypes: true });
32106
32321
  const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
32107
- const entryPath = path12.join(relativePath || ".", d.name).replace(/\\/g, "/");
32108
- const fullPath = path12.join(resolved, d.name);
32322
+ const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
32323
+ const fullPath = path13.join(resolved, d.name);
32109
32324
  let isDir = d.isDirectory();
32110
32325
  if (d.isSymbolicLink()) {
32111
32326
  try {
32112
- const targetStat = fs5.statSync(fullPath);
32327
+ const targetStat = fs6.statSync(fullPath);
32113
32328
  isDir = targetStat.isDirectory();
32114
32329
  } catch {
32115
32330
  isDir = false;
@@ -32133,26 +32348,25 @@ function listDir(relativePath) {
32133
32348
  }
32134
32349
 
32135
32350
  // src/files/read-file.ts
32136
- import fs6 from "node:fs";
32351
+ import fs7 from "node:fs";
32137
32352
  import { StringDecoder } from "node:string_decoder";
32138
- var cwd2 = process.cwd();
32139
32353
  function resolveFilePath(relativePath) {
32140
- const resolved = ensureUnderCwd(relativePath, cwd2);
32354
+ const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
32141
32355
  if (!resolved) return { error: "Path is outside working directory" };
32142
32356
  let real;
32143
32357
  try {
32144
- real = fs6.realpathSync(resolved);
32358
+ real = fs7.realpathSync(resolved);
32145
32359
  } catch {
32146
32360
  real = resolved;
32147
32361
  }
32148
- const stat = fs6.statSync(real);
32362
+ const stat = fs7.statSync(real);
32149
32363
  if (!stat.isFile()) return { error: "Not a file" };
32150
32364
  return real;
32151
32365
  }
32152
32366
  var LINE_CHUNK_SIZE = 64 * 1024;
32153
32367
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
32154
- const fileSize = fs6.statSync(filePath).size;
32155
- const fd = fs6.openSync(filePath, "r");
32368
+ const fileSize = fs7.statSync(filePath).size;
32369
+ const fd = fs7.openSync(filePath, "r");
32156
32370
  const bufSize = 64 * 1024;
32157
32371
  const buf = Buffer.alloc(bufSize);
32158
32372
  const decoder = new StringDecoder("utf8");
@@ -32165,7 +32379,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
32165
32379
  let line0Accum = "";
32166
32380
  try {
32167
32381
  let bytesRead;
32168
- while (!done && (bytesRead = fs6.readSync(fd, buf, 0, bufSize, null)) > 0) {
32382
+ while (!done && (bytesRead = fs7.readSync(fd, buf, 0, bufSize, null)) > 0) {
32169
32383
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
32170
32384
  partial2 = "";
32171
32385
  let lineStart = 0;
@@ -32300,7 +32514,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
32300
32514
  }
32301
32515
  return { content: resultLines.join("\n"), size: fileSize };
32302
32516
  } finally {
32303
- fs6.closeSync(fd);
32517
+ fs7.closeSync(fd);
32304
32518
  }
32305
32519
  }
32306
32520
  function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
@@ -32311,8 +32525,8 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
32311
32525
  if (hasRange) {
32312
32526
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
32313
32527
  }
32314
- const stat = fs6.statSync(result);
32315
- const raw = fs6.readFileSync(result, "utf8");
32528
+ const stat = fs7.statSync(result);
32529
+ const raw = fs7.readFileSync(result, "utf8");
32316
32530
  const lines = raw.split(/\r?\n/);
32317
32531
  return { content: raw, totalLines: lines.length, size: stat.size };
32318
32532
  } catch (err) {
@@ -32321,16 +32535,16 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
32321
32535
  }
32322
32536
 
32323
32537
  // src/files/file-index.ts
32324
- import fs7 from "node:fs";
32325
- import path13 from "node:path";
32538
+ import fs8 from "node:fs";
32539
+ import path14 from "node:path";
32326
32540
  import os2 from "node:os";
32327
32541
  import crypto2 from "node:crypto";
32328
- var INDEX_DIR = path13.join(os2.homedir(), ".buildautomaton");
32542
+ var INDEX_DIR = path14.join(os2.homedir(), ".buildautomaton");
32329
32543
  var HASH_LEN = 16;
32330
32544
  var INDEX_VERSION = 2;
32331
- function getIndexPath(cwd3) {
32332
- const hash = crypto2.createHash("sha256").update(cwd3).digest("hex").slice(0, HASH_LEN);
32333
- return path13.join(INDEX_DIR, `.file-index-${hash}.json`);
32545
+ function getIndexPath(cwd) {
32546
+ const hash = crypto2.createHash("sha256").update(cwd).digest("hex").slice(0, HASH_LEN);
32547
+ return path14.join(INDEX_DIR, `.file-index-${hash}.json`);
32334
32548
  }
32335
32549
  function getTrigrams(s) {
32336
32550
  const lower = s.toLowerCase();
@@ -32368,20 +32582,20 @@ function binarySearch(arr, x) {
32368
32582
  function walkDir(dir, baseDir, out) {
32369
32583
  let names;
32370
32584
  try {
32371
- names = fs7.readdirSync(dir);
32585
+ names = fs8.readdirSync(dir);
32372
32586
  } catch {
32373
32587
  return;
32374
32588
  }
32375
32589
  for (const name of names) {
32376
32590
  if (name.startsWith(".")) continue;
32377
- const full = path13.join(dir, name);
32591
+ const full = path14.join(dir, name);
32378
32592
  let stat;
32379
32593
  try {
32380
- stat = fs7.statSync(full);
32594
+ stat = fs8.statSync(full);
32381
32595
  } catch {
32382
32596
  continue;
32383
32597
  }
32384
- const relative4 = path13.relative(baseDir, full).replace(/\\/g, "/");
32598
+ const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
32385
32599
  if (stat.isDirectory()) {
32386
32600
  walkDir(full, baseDir, out);
32387
32601
  } else if (stat.isFile()) {
@@ -32389,8 +32603,8 @@ function walkDir(dir, baseDir, out) {
32389
32603
  }
32390
32604
  }
32391
32605
  }
32392
- function buildFileIndex(cwd3) {
32393
- const resolved = path13.resolve(cwd3);
32606
+ function buildFileIndex(cwd) {
32607
+ const resolved = path14.resolve(cwd);
32394
32608
  const paths = [];
32395
32609
  walkDir(resolved, resolved, paths);
32396
32610
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
@@ -32408,18 +32622,18 @@ function buildFileIndex(cwd3) {
32408
32622
  const data = { version: INDEX_VERSION, paths, trigramIndex };
32409
32623
  const indexPath = getIndexPath(resolved);
32410
32624
  try {
32411
- if (!fs7.existsSync(INDEX_DIR)) fs7.mkdirSync(INDEX_DIR, { recursive: true });
32412
- fs7.writeFileSync(indexPath, JSON.stringify(data), "utf8");
32625
+ if (!fs8.existsSync(INDEX_DIR)) fs8.mkdirSync(INDEX_DIR, { recursive: true });
32626
+ fs8.writeFileSync(indexPath, JSON.stringify(data), "utf8");
32413
32627
  } catch (e) {
32414
32628
  console.error("[file-index] Failed to write index:", e);
32415
32629
  }
32416
32630
  return data;
32417
32631
  }
32418
- function loadFileIndex(cwd3) {
32419
- const resolved = path13.resolve(cwd3);
32632
+ function loadFileIndex(cwd) {
32633
+ const resolved = path14.resolve(cwd);
32420
32634
  const indexPath = getIndexPath(resolved);
32421
32635
  try {
32422
- const raw = fs7.readFileSync(indexPath, "utf8");
32636
+ const raw = fs8.readFileSync(indexPath, "utf8");
32423
32637
  const parsed = JSON.parse(raw);
32424
32638
  if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
32425
32639
  const obj = parsed;
@@ -32436,8 +32650,8 @@ function loadFileIndex(cwd3) {
32436
32650
  return null;
32437
32651
  }
32438
32652
  }
32439
- function ensureFileIndex(cwd3) {
32440
- const resolved = path13.resolve(cwd3);
32653
+ function ensureFileIndex(cwd) {
32654
+ const resolved = path14.resolve(cwd);
32441
32655
  const cached2 = loadFileIndex(resolved);
32442
32656
  if (cached2 !== null) return { data: cached2, fromCache: true };
32443
32657
  const data = buildFileIndex(resolved);
@@ -32475,8 +32689,8 @@ function searchFileIndex(index, query, limit = 100) {
32475
32689
  var SEARCH_LIMIT = 100;
32476
32690
  function handleFileBrowserSearch(msg, socket) {
32477
32691
  const q = typeof msg.q === "string" ? msg.q : "";
32478
- const cwd3 = process.cwd();
32479
- const index = loadFileIndex(cwd3);
32692
+ const cwd = getBridgeWorkspaceDirectory();
32693
+ const index = loadFileIndex(cwd);
32480
32694
  if (index === null) {
32481
32695
  sendWsMessage(socket, {
32482
32696
  type: "file_browser_search_response",
@@ -32497,7 +32711,7 @@ function handleFileBrowserSearch(msg, socket) {
32497
32711
  function triggerFileIndexBuild() {
32498
32712
  setImmediate(() => {
32499
32713
  try {
32500
- ensureFileIndex(process.cwd());
32714
+ ensureFileIndex(getBridgeWorkspaceDirectory());
32501
32715
  } catch (e) {
32502
32716
  console.error("[file-index] Background build failed:", e);
32503
32717
  }
@@ -32558,30 +32772,30 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
32558
32772
  }
32559
32773
 
32560
32774
  // src/skills/discover-local-agent-skills.ts
32561
- import fs8 from "node:fs";
32562
- import path14 from "node:path";
32775
+ import fs9 from "node:fs";
32776
+ import path15 from "node:path";
32563
32777
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
32564
- function discoverLocalSkills(cwd3) {
32778
+ function discoverLocalSkills(cwd) {
32565
32779
  const out = [];
32566
32780
  const seenKeys = /* @__PURE__ */ new Set();
32567
32781
  for (const rel of SKILL_DISCOVERY_ROOTS) {
32568
- const base = path14.join(cwd3, rel);
32569
- if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
32782
+ const base = path15.join(cwd, rel);
32783
+ if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
32570
32784
  let entries = [];
32571
32785
  try {
32572
- entries = fs8.readdirSync(base);
32786
+ entries = fs9.readdirSync(base);
32573
32787
  } catch {
32574
32788
  continue;
32575
32789
  }
32576
32790
  for (const name of entries) {
32577
- const dir = path14.join(base, name);
32791
+ const dir = path15.join(base, name);
32578
32792
  try {
32579
- if (!fs8.statSync(dir).isDirectory()) continue;
32793
+ if (!fs9.statSync(dir).isDirectory()) continue;
32580
32794
  } catch {
32581
32795
  continue;
32582
32796
  }
32583
- const skillMd = path14.join(dir, "SKILL.md");
32584
- if (!fs8.existsSync(skillMd)) continue;
32797
+ const skillMd = path15.join(dir, "SKILL.md");
32798
+ if (!fs9.existsSync(skillMd)) continue;
32585
32799
  const key = `${rel}/${name}`;
32586
32800
  if (seenKeys.has(key)) continue;
32587
32801
  seenKeys.add(key);
@@ -32590,26 +32804,26 @@ function discoverLocalSkills(cwd3) {
32590
32804
  }
32591
32805
  return out;
32592
32806
  }
32593
- function discoverSkillLayoutRoots(cwd3) {
32807
+ function discoverSkillLayoutRoots(cwd) {
32594
32808
  const roots = [];
32595
32809
  for (const rel of SKILL_DISCOVERY_ROOTS) {
32596
- const base = path14.join(cwd3, rel);
32597
- if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
32810
+ const base = path15.join(cwd, rel);
32811
+ if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
32598
32812
  let entries = [];
32599
32813
  try {
32600
- entries = fs8.readdirSync(base);
32814
+ entries = fs9.readdirSync(base);
32601
32815
  } catch {
32602
32816
  continue;
32603
32817
  }
32604
32818
  const skills2 = [];
32605
32819
  for (const name of entries) {
32606
- const dir = path14.join(base, name);
32820
+ const dir = path15.join(base, name);
32607
32821
  try {
32608
- if (!fs8.statSync(dir).isDirectory()) continue;
32822
+ if (!fs9.statSync(dir).isDirectory()) continue;
32609
32823
  } catch {
32610
32824
  continue;
32611
32825
  }
32612
- if (!fs8.existsSync(path14.join(dir, "SKILL.md"))) continue;
32826
+ if (!fs9.existsSync(path15.join(dir, "SKILL.md"))) continue;
32613
32827
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
32614
32828
  skills2.push({ name, relPath });
32615
32829
  }
@@ -32624,14 +32838,14 @@ function discoverSkillLayoutRoots(cwd3) {
32624
32838
  function handleSkillLayoutRequest(msg, deps) {
32625
32839
  const socket = deps.getWs();
32626
32840
  const id = typeof msg.id === "string" ? msg.id : "";
32627
- const roots = discoverSkillLayoutRoots(process.cwd());
32841
+ const roots = discoverSkillLayoutRoots(getBridgeWorkspaceDirectory());
32628
32842
  socket?.send(JSON.stringify({ type: "skill_layout_response", id, roots }));
32629
32843
  }
32630
32844
 
32631
32845
  // src/skills/install-remote-skills.ts
32632
- import fs9 from "node:fs";
32633
- import path15 from "node:path";
32634
- function installRemoteSkills(cwd3, targetDir, items) {
32846
+ import fs10 from "node:fs";
32847
+ import path16 from "node:path";
32848
+ function installRemoteSkills(cwd, targetDir, items) {
32635
32849
  const installed = [];
32636
32850
  if (!Array.isArray(items)) {
32637
32851
  return { success: false, error: "Invalid items" };
@@ -32641,15 +32855,15 @@ function installRemoteSkills(cwd3, targetDir, items) {
32641
32855
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
32642
32856
  continue;
32643
32857
  }
32644
- const skillDir = path15.join(cwd3, targetDir, item.skillName);
32858
+ const skillDir = path16.join(cwd, targetDir, item.skillName);
32645
32859
  for (const f of item.files) {
32646
32860
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
32647
- const dest = path15.join(skillDir, f.path);
32648
- fs9.mkdirSync(path15.dirname(dest), { recursive: true });
32861
+ const dest = path16.join(skillDir, f.path);
32862
+ fs10.mkdirSync(path16.dirname(dest), { recursive: true });
32649
32863
  if (f.text !== void 0) {
32650
- fs9.writeFileSync(dest, f.text, "utf8");
32864
+ fs10.writeFileSync(dest, f.text, "utf8");
32651
32865
  } else if (f.base64) {
32652
- fs9.writeFileSync(dest, Buffer.from(f.base64, "base64"));
32866
+ fs10.writeFileSync(dest, Buffer.from(f.base64, "base64"));
32653
32867
  }
32654
32868
  }
32655
32869
  installed.push({
@@ -32670,11 +32884,11 @@ var handleInstallSkillsMessage = (msg, deps) => {
32670
32884
  const id = typeof msg.id === "string" ? msg.id : "";
32671
32885
  const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
32672
32886
  const rawItems = msg.items;
32673
- const cwd3 = process.cwd();
32674
- const result = installRemoteSkills(cwd3, targetDir, rawItems);
32887
+ const cwd = getBridgeWorkspaceDirectory();
32888
+ const result = installRemoteSkills(cwd, targetDir, rawItems);
32675
32889
  if (!result.success) {
32676
32890
  const err = result.error ?? "Invalid items";
32677
- deps.log(`[Bridge service] install_skills failed: ${err}`);
32891
+ deps.log(`[Bridge service] Install skills failed: ${err}`);
32678
32892
  socket?.send(JSON.stringify({ type: "install_skills_result", id, success: false, error: err }));
32679
32893
  return;
32680
32894
  }
@@ -32743,8 +32957,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
32743
32957
  };
32744
32958
 
32745
32959
  // src/bridge/routing/handlers/revert-turn-snapshot.ts
32746
- import * as fs10 from "node:fs";
32747
- import * as path16 from "node:path";
32960
+ import * as fs11 from "node:fs";
32748
32961
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
32749
32962
  const id = typeof msg.id === "string" ? msg.id : "";
32750
32963
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -32754,9 +32967,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
32754
32967
  void (async () => {
32755
32968
  const s = getWs();
32756
32969
  if (!s) return;
32757
- const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? path16.resolve(process.cwd());
32970
+ const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
32758
32971
  const file2 = snapshotFilePath(agentBase, turnId);
32759
- if (!fs10.existsSync(file2)) {
32972
+ if (!fs11.existsSync(file2)) {
32760
32973
  sendWsMessage(s, {
32761
32974
  type: "revert_turn_snapshot_result",
32762
32975
  id,
@@ -32883,7 +33096,7 @@ import * as path20 from "node:path";
32883
33096
  import os4 from "node:os";
32884
33097
 
32885
33098
  // src/worktrees/prepare-new-session-worktrees.ts
32886
- import * as fs12 from "node:fs";
33099
+ import * as fs13 from "node:fs";
32887
33100
  import * as path18 from "node:path";
32888
33101
 
32889
33102
  // src/git/worktree-add.ts
@@ -32893,7 +33106,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
32893
33106
  }
32894
33107
 
32895
33108
  // src/worktrees/worktree-layout-file.ts
32896
- import * as fs11 from "node:fs";
33109
+ import * as fs12 from "node:fs";
32897
33110
  import * as path17 from "node:path";
32898
33111
  import os3 from "node:os";
32899
33112
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
@@ -32910,8 +33123,8 @@ function normalizeLoadedLayout(raw) {
32910
33123
  function loadWorktreeLayout() {
32911
33124
  try {
32912
33125
  const p = defaultWorktreeLayoutPath();
32913
- if (!fs11.existsSync(p)) return { launcherCwds: [] };
32914
- const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
33126
+ if (!fs12.existsSync(p)) return { launcherCwds: [] };
33127
+ const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
32915
33128
  return normalizeLoadedLayout(raw);
32916
33129
  } catch {
32917
33130
  return { launcherCwds: [] };
@@ -32920,8 +33133,8 @@ function loadWorktreeLayout() {
32920
33133
  function saveWorktreeLayout(layout) {
32921
33134
  try {
32922
33135
  const dir = path17.dirname(defaultWorktreeLayoutPath());
32923
- fs11.mkdirSync(dir, { recursive: true });
32924
- fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
33136
+ fs12.mkdirSync(dir, { recursive: true });
33137
+ fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
32925
33138
  } catch {
32926
33139
  }
32927
33140
  }
@@ -32953,25 +33166,25 @@ async function prepareNewSessionWorktrees(options) {
32953
33166
  const agentMirrorRoot = path18.join(rootAbs, cwdKey);
32954
33167
  const repos = await discoverGitReposUnderRoot(launcherResolved);
32955
33168
  if (repos.length === 0) {
32956
- log2("[worktrees] No git repos under launcher cwd; skipping worktree creation");
33169
+ log2("[worktrees] No Git repositories under launcher working directory; skipping worktree creation.");
32957
33170
  return null;
32958
33171
  }
32959
33172
  const branch = `session-${sessionId}`;
32960
33173
  const worktreePaths = [];
32961
- fs12.mkdirSync(agentMirrorRoot, { recursive: true });
33174
+ fs13.mkdirSync(agentMirrorRoot, { recursive: true });
32962
33175
  for (const repo of repos) {
32963
33176
  let rel = path18.relative(launcherResolved, repo.absolutePath);
32964
33177
  if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
32965
33178
  const relNorm = rel === "" ? "." : rel;
32966
33179
  const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
32967
- fs12.mkdirSync(path18.dirname(wtPath), { recursive: true });
33180
+ fs13.mkdirSync(path18.dirname(wtPath), { recursive: true });
32968
33181
  try {
32969
33182
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
32970
- log2(`[worktrees] Added worktree ${wtPath} (branch ${branch})`);
33183
+ log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
32971
33184
  worktreePaths.push(wtPath);
32972
33185
  } catch (e) {
32973
33186
  log2(
32974
- `[worktrees] worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
33187
+ `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
32975
33188
  );
32976
33189
  }
32977
33190
  }
@@ -32993,24 +33206,26 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
32993
33206
  await gitRenameCurrentBranch(wt, safe);
32994
33207
  log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
32995
33208
  } catch (e) {
32996
- log2(`[worktrees] branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`);
33209
+ log2(
33210
+ `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
33211
+ );
32997
33212
  }
32998
33213
  }
32999
33214
  }
33000
33215
 
33001
33216
  // src/worktrees/remove-session-worktrees.ts
33002
- import * as fs15 from "node:fs";
33217
+ import * as fs16 from "node:fs";
33003
33218
 
33004
33219
  // src/git/worktree-remove.ts
33005
- import * as fs14 from "node:fs";
33220
+ import * as fs15 from "node:fs";
33006
33221
 
33007
33222
  // src/git/resolve-main-repo-from-git-file.ts
33008
- import * as fs13 from "node:fs";
33223
+ import * as fs14 from "node:fs";
33009
33224
  import * as path19 from "node:path";
33010
33225
  function resolveMainRepoFromWorktreeGitFile(wt) {
33011
33226
  const gitDirFile = path19.join(wt, ".git");
33012
- if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
33013
- const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
33227
+ if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
33228
+ const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
33014
33229
  const m = first2.match(/^gitdir:\s*(.+)$/im);
33015
33230
  if (!m) return "";
33016
33231
  const gitWorktreePath = path19.resolve(wt, m[1].trim());
@@ -33024,7 +33239,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
33024
33239
  if (mainRepo) {
33025
33240
  await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
33026
33241
  } else {
33027
- fs14.rmSync(worktreePath, { recursive: true, force: true });
33242
+ fs15.rmSync(worktreePath, { recursive: true, force: true });
33028
33243
  }
33029
33244
  }
33030
33245
 
@@ -33035,9 +33250,9 @@ async function removeSessionWorktrees(paths, log2) {
33035
33250
  await gitWorktreeRemoveForce(wt);
33036
33251
  log2(`[worktrees] Removed worktree ${wt}`);
33037
33252
  } catch (e) {
33038
- log2(`[worktrees] remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
33253
+ log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
33039
33254
  try {
33040
- fs15.rmSync(wt, { recursive: true, force: true });
33255
+ fs16.rmSync(wt, { recursive: true, force: true });
33041
33256
  } catch {
33042
33257
  }
33043
33258
  }
@@ -33101,7 +33316,7 @@ var SessionWorktreeManager = class {
33101
33316
  return this.bridgeWantsWorktrees;
33102
33317
  }
33103
33318
  /**
33104
- * Returns cwd for the agent (mirror of launcher tree), or undefined to use process.cwd().
33319
+ * Returns cwd for the agent (mirror of launcher tree), or undefined to use the bridge workspace directory.
33105
33320
  */
33106
33321
  async resolveCwdForPrompt(sessionId, opts) {
33107
33322
  if (!sessionId || !this.effective() || !opts.sessionWorktreesEnabled) {
@@ -33114,7 +33329,7 @@ var SessionWorktreeManager = class {
33114
33329
  }
33115
33330
  const prep = await prepareNewSessionWorktrees({
33116
33331
  rootAbs: this.rootAbs,
33117
- launcherCwd: process.cwd(),
33332
+ launcherCwd: getBridgeWorkspaceDirectory(),
33118
33333
  sessionId,
33119
33334
  layout: this.layout,
33120
33335
  log: this.log
@@ -33154,7 +33369,7 @@ var SessionWorktreeManager = class {
33154
33369
  }
33155
33370
  async commitSession(params) {
33156
33371
  const paths = this.sessionPaths.get(params.sessionId);
33157
- const targets = paths?.length ? paths : [process.cwd()];
33372
+ const targets = paths?.length ? paths : [getBridgeWorkspaceDirectory()];
33158
33373
  return commitSessionWorktrees({
33159
33374
  paths: targets,
33160
33375
  branch: params.branch,
@@ -33207,7 +33422,7 @@ function shouldIgnoreRelative(rel) {
33207
33422
  }
33208
33423
  function attachWatchErrorLog(w) {
33209
33424
  w.on("error", (err) => {
33210
- console.error("[file-index] fs.watch error:", err);
33425
+ console.error("[file-index] File watcher error:", err);
33211
33426
  });
33212
33427
  }
33213
33428
  function createFsWatcher(resolved, schedule) {
@@ -33226,7 +33441,7 @@ function createFsWatcher(resolved, schedule) {
33226
33441
  const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
33227
33442
  if (code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM") {
33228
33443
  console.warn(
33229
- "[file-index] fs.watch recursive unavailable here; using non-recursive watch (Node 20+ on Linux enables recursive). Nested file changes may be missed until upgrade."
33444
+ "[file-index] Recursive file watching is unavailable on this platform; using non-recursive watch (Node 20+ on Linux enables recursive). Nested file changes may be missed until you upgrade."
33230
33445
  );
33231
33446
  const w = watch(resolved, { recursive: false }, onEvent);
33232
33447
  attachWatchErrorLog(w);
@@ -33235,8 +33450,8 @@ function createFsWatcher(resolved, schedule) {
33235
33450
  throw e;
33236
33451
  }
33237
33452
  }
33238
- function startFileIndexWatcher(cwd3 = process.cwd()) {
33239
- const resolved = path21.resolve(cwd3);
33453
+ function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
33454
+ const resolved = path21.resolve(cwd);
33240
33455
  try {
33241
33456
  buildFileIndex(resolved);
33242
33457
  } catch (e) {
@@ -33290,7 +33505,7 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
33290
33505
  const exited = new Promise((resolve15) => {
33291
33506
  proc.once("exit", () => resolve15());
33292
33507
  });
33293
- log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"})\u2026`);
33508
+ log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
33294
33509
  try {
33295
33510
  proc.kill("SIGTERM");
33296
33511
  } catch {
@@ -33298,7 +33513,9 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
33298
33513
  await Promise.race([exited, new Promise((resolve15) => setTimeout(resolve15, graceMs))]);
33299
33514
  }
33300
33515
  function forceKillChild(proc, log2, shortId, graceMs) {
33301
- log2(`[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"})\u2026`);
33516
+ log2(
33517
+ `[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"}).`
33518
+ );
33302
33519
  proc.removeAllListeners();
33303
33520
  try {
33304
33521
  proc.kill("SIGKILL");
@@ -33307,7 +33524,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
33307
33524
  }
33308
33525
 
33309
33526
  // src/dev-servers/process/wire-dev-server-child-process.ts
33310
- import fs16 from "node:fs";
33527
+ import fs17 from "node:fs";
33311
33528
 
33312
33529
  // src/dev-servers/manager/forward-pipe.ts
33313
33530
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -33343,7 +33560,7 @@ function wireDevServerChildProcess(d) {
33343
33560
  d.setPollInterval(void 0);
33344
33561
  return;
33345
33562
  }
33346
- fs16.readFile(d.mergedLogPath, (err, buf) => {
33563
+ fs17.readFile(d.mergedLogPath, (err, buf) => {
33347
33564
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
33348
33565
  if (buf.length <= d.mergedReadPos.value) return;
33349
33566
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -33368,7 +33585,7 @@ function wireDevServerChildProcess(d) {
33368
33585
  d.rmMergedCleanupDir(cleanupDir);
33369
33586
  }
33370
33587
  if (signal) {
33371
- d.log(`[dev-server] ${title} stopped (signal ${String(signal)})`);
33588
+ d.log(`[dev-server] ${title} stopped (signal: ${String(signal)}).`);
33372
33589
  } else if (code !== null && code !== 0) {
33373
33590
  const errTail = d.stderrTail.getTail().slice(-3).join("\n");
33374
33591
  d.log(`[dev-server] ${title} exited with code ${code}${errTail ? `
@@ -33381,7 +33598,7 @@ ${errTail}` : ""}`);
33381
33598
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
33382
33599
  };
33383
33600
  if (mergedPath) {
33384
- fs16.readFile(mergedPath, (err, buf) => {
33601
+ fs17.readFile(mergedPath, (err, buf) => {
33385
33602
  if (!err && buf.length > d.mergedReadPos.value) {
33386
33603
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
33387
33604
  if (chunk.length > 0) {
@@ -33483,13 +33700,13 @@ function parseDevServerDefs(servers) {
33483
33700
  }
33484
33701
 
33485
33702
  // src/dev-servers/manager/shell-spawn/utils.ts
33486
- import fs17 from "node:fs";
33703
+ import fs18 from "node:fs";
33487
33704
  function isSpawnEbadf(e) {
33488
33705
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
33489
33706
  }
33490
33707
  function rmDirQuiet(dir) {
33491
33708
  try {
33492
- fs17.rmSync(dir, { recursive: true, force: true });
33709
+ fs18.rmSync(dir, { recursive: true, force: true });
33493
33710
  } catch {
33494
33711
  }
33495
33712
  }
@@ -33497,7 +33714,7 @@ var cachedDevNullReadFd;
33497
33714
  function devNullReadFd() {
33498
33715
  if (cachedDevNullReadFd === void 0) {
33499
33716
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
33500
- cachedDevNullReadFd = fs17.openSync(devPath, "r");
33717
+ cachedDevNullReadFd = fs18.openSync(devPath, "r");
33501
33718
  }
33502
33719
  return cachedDevNullReadFd;
33503
33720
  }
@@ -33507,7 +33724,7 @@ function pipedStdoutStderrFor(attemptStdio) {
33507
33724
 
33508
33725
  // src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
33509
33726
  import { spawn as spawn5 } from "node:child_process";
33510
- function trySpawnPipedViaSh(command, env, cwd3, signal) {
33727
+ function trySpawnPipedViaSh(command, env, cwd, signal) {
33511
33728
  const attempts = [
33512
33729
  { stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
33513
33730
  { stdio: ["ignore", "pipe", "pipe"], endStdin: true },
@@ -33518,7 +33735,7 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
33518
33735
  const attempt = attempts[i];
33519
33736
  const opts = {
33520
33737
  env,
33521
- cwd: cwd3,
33738
+ cwd,
33522
33739
  stdio: attempt.stdio,
33523
33740
  ...signal ? { signal } : {}
33524
33741
  };
@@ -33550,11 +33767,11 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
33550
33767
 
33551
33768
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
33552
33769
  import { spawn as spawn6 } from "node:child_process";
33553
- function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
33770
+ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
33554
33771
  try {
33555
33772
  const opts = {
33556
33773
  env,
33557
- cwd: cwd3,
33774
+ cwd,
33558
33775
  stdio: [devNullFd, "pipe", "pipe"],
33559
33776
  shell: true,
33560
33777
  ...signal ? { signal } : {}
@@ -33571,15 +33788,15 @@ function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
33571
33788
 
33572
33789
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
33573
33790
  import { spawn as spawn7 } from "node:child_process";
33574
- import fs18 from "node:fs";
33791
+ import fs19 from "node:fs";
33575
33792
  import { tmpdir } from "node:os";
33576
33793
  import path22 from "node:path";
33577
- function trySpawnMergedLogFile(command, env, cwd3, signal) {
33578
- const tmpRoot = fs18.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
33794
+ function trySpawnMergedLogFile(command, env, cwd, signal) {
33795
+ const tmpRoot = fs19.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
33579
33796
  const logPath = path22.join(tmpRoot, "combined.log");
33580
33797
  let logFd;
33581
33798
  try {
33582
- logFd = fs18.openSync(logPath, "a");
33799
+ logFd = fs19.openSync(logPath, "a");
33583
33800
  } catch {
33584
33801
  rmDirQuiet(tmpRoot);
33585
33802
  return null;
@@ -33590,15 +33807,15 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
33590
33807
  if (process.platform === "win32") {
33591
33808
  proc = spawn7(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
33592
33809
  env,
33593
- cwd: cwd3,
33810
+ cwd,
33594
33811
  stdio,
33595
33812
  windowsHide: true,
33596
33813
  ...signal ? { signal } : {}
33597
33814
  });
33598
33815
  } else {
33599
- proc = spawn7("/bin/sh", ["-c", command], { env, cwd: cwd3, stdio, ...signal ? { signal } : {} });
33816
+ proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
33600
33817
  }
33601
- fs18.closeSync(logFd);
33818
+ fs19.closeSync(logFd);
33602
33819
  return {
33603
33820
  proc,
33604
33821
  pipedStdoutStderr: true,
@@ -33607,7 +33824,7 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
33607
33824
  };
33608
33825
  } catch (e) {
33609
33826
  try {
33610
- fs18.closeSync(logFd);
33827
+ fs19.closeSync(logFd);
33611
33828
  } catch {
33612
33829
  }
33613
33830
  rmDirQuiet(tmpRoot);
@@ -33618,25 +33835,25 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
33618
33835
 
33619
33836
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
33620
33837
  import { spawn as spawn8 } from "node:child_process";
33621
- import fs19 from "node:fs";
33838
+ import fs20 from "node:fs";
33622
33839
  import { tmpdir as tmpdir2 } from "node:os";
33623
33840
  import path23 from "node:path";
33624
33841
  function shSingleQuote(s) {
33625
33842
  return `'${s.replace(/'/g, `'\\''`)}'`;
33626
33843
  }
33627
- function trySpawnShellScriptLogRedirectUnix(command, env, cwd3, signal) {
33628
- const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
33844
+ function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
33845
+ const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
33629
33846
  const logPath = path23.join(tmpRoot, "combined.log");
33630
33847
  const innerPath = path23.join(tmpRoot, "_cmd.sh");
33631
33848
  const runnerPath = path23.join(tmpRoot, "_run.sh");
33632
33849
  try {
33633
- fs19.writeFileSync(innerPath, `#!/bin/sh
33850
+ fs20.writeFileSync(innerPath, `#!/bin/sh
33634
33851
  ${command}
33635
33852
  `);
33636
- fs19.writeFileSync(
33853
+ fs20.writeFileSync(
33637
33854
  runnerPath,
33638
33855
  `#!/bin/sh
33639
- cd ${shSingleQuote(cwd3)}
33856
+ cd ${shSingleQuote(cwd)}
33640
33857
  /bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
33641
33858
  `
33642
33859
  );
@@ -33658,17 +33875,17 @@ cd ${shSingleQuote(cwd3)}
33658
33875
  throw e;
33659
33876
  }
33660
33877
  }
33661
- function trySpawnShellScriptLogRedirectWin(command, env, cwd3, signal) {
33662
- const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
33878
+ function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
33879
+ const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
33663
33880
  const logPath = path23.join(tmpRoot, "combined.log");
33664
33881
  const runnerPath = path23.join(tmpRoot, "_run.bat");
33665
33882
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
33666
33883
  const com = process.env.ComSpec || "cmd.exe";
33667
33884
  try {
33668
- fs19.writeFileSync(
33885
+ fs20.writeFileSync(
33669
33886
  runnerPath,
33670
33887
  `@ECHO OFF\r
33671
- CD /D ${q(cwd3)}\r
33888
+ CD /D ${q(cwd)}\r
33672
33889
  ${command} >> ${q(logPath)} 2>&1\r
33673
33890
  `
33674
33891
  );
@@ -33694,10 +33911,10 @@ ${command} >> ${q(logPath)} 2>&1\r
33694
33911
 
33695
33912
  // src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
33696
33913
  import { spawn as spawn9 } from "node:child_process";
33697
- function trySpawnInheritStdio(command, env, cwd3, signal) {
33914
+ function trySpawnInheritStdio(command, env, cwd, signal) {
33698
33915
  const opts = {
33699
33916
  env,
33700
- cwd: cwd3,
33917
+ cwd,
33701
33918
  stdio: "inherit",
33702
33919
  ...signal ? { signal } : {}
33703
33920
  };
@@ -33713,27 +33930,27 @@ function trySpawnInheritStdio(command, env, cwd3, signal) {
33713
33930
  }
33714
33931
 
33715
33932
  // src/dev-servers/manager/shell-spawn/shell-spawn.ts
33716
- function shellSpawn(command, env, cwd3, options) {
33933
+ function shellSpawn(command, env, cwd, options) {
33717
33934
  const signal = options?.signal;
33718
- const piped = trySpawnPipedViaSh(command, env, cwd3, signal);
33935
+ const piped = trySpawnPipedViaSh(command, env, cwd, signal);
33719
33936
  if (piped.ok) {
33720
33937
  return piped.result;
33721
33938
  }
33722
33939
  let lastErr = piped.lastErr;
33723
- const shellTrueProc = trySpawnShellTruePiped(command, env, cwd3, devNullReadFd(), signal);
33940
+ const shellTrueProc = trySpawnShellTruePiped(command, env, cwd, devNullReadFd(), signal);
33724
33941
  if (shellTrueProc) {
33725
33942
  return { proc: shellTrueProc, pipedStdoutStderr: true };
33726
33943
  }
33727
- const fileCapture = trySpawnMergedLogFile(command, env, cwd3, signal);
33944
+ const fileCapture = trySpawnMergedLogFile(command, env, cwd, signal);
33728
33945
  if (fileCapture) {
33729
33946
  return fileCapture;
33730
33947
  }
33731
- const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd3, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd3, signal);
33948
+ const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal);
33732
33949
  if (scriptCapture) {
33733
33950
  return scriptCapture;
33734
33951
  }
33735
33952
  try {
33736
- return trySpawnInheritStdio(command, env, cwd3, signal);
33953
+ return trySpawnInheritStdio(command, env, cwd, signal);
33737
33954
  } catch (e) {
33738
33955
  throw lastErr instanceof Error ? lastErr : e instanceof Error ? e : new Error(String(e));
33739
33956
  }
@@ -33791,9 +34008,11 @@ var DevServerManager = class {
33791
34008
  abortControllersByServerId = /* @__PURE__ */ new Map();
33792
34009
  getWs;
33793
34010
  log;
34011
+ getBridgeCwd;
33794
34012
  constructor(options) {
33795
34013
  this.getWs = options.getWs;
33796
34014
  this.log = options.log;
34015
+ this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
33797
34016
  }
33798
34017
  attachFirehose(send) {
33799
34018
  this.firehoseSend = send;
@@ -33917,7 +34136,7 @@ var DevServerManager = class {
33917
34136
  this.sendStatus(serverId, "starting", void 0, emptyTails());
33918
34137
  const ac = new AbortController();
33919
34138
  this.abortControllersByServerId.set(serverId, ac);
33920
- const cwd3 = process.cwd();
34139
+ const cwd = this.getBridgeCwd();
33921
34140
  const childEnv = envForSpawn(process.env, def.env, def.ports);
33922
34141
  const cmd = substituteCommand(def.command.trim(), childEnv);
33923
34142
  const title = def.name.trim() || serverId.slice(0, 8);
@@ -33932,7 +34151,7 @@ var DevServerManager = class {
33932
34151
  let mergedLogPath;
33933
34152
  let mergedCleanupDir;
33934
34153
  try {
33935
- const spawned = shellSpawn(cmd, childEnv, cwd3, {
34154
+ const spawned = shellSpawn(cmd, childEnv, cwd, {
33936
34155
  signal: ac.signal
33937
34156
  });
33938
34157
  proc = spawned.proc;
@@ -33941,7 +34160,7 @@ var DevServerManager = class {
33941
34160
  mergedCleanupDir = spawned.mergedLogCleanupDir;
33942
34161
  } catch (e) {
33943
34162
  const msg = e instanceof Error ? e.message : String(e);
33944
- this.log(`[dev-server] ${title} failed to start: ${msg}`);
34163
+ this.log(`[dev-server] Failed to start ${title}: ${msg}`);
33945
34164
  this.abortControllersByServerId.delete(serverId);
33946
34165
  this.sendStatus(serverId, "error", msg, emptyTails());
33947
34166
  return;
@@ -34173,7 +34392,7 @@ function startStreamingProxy(ws, log2, pr) {
34173
34392
  },
34174
34393
  onEnd: () => sendWsMessage(ws, { type: "proxy_result_end", id: pr.id }),
34175
34394
  onError: (error40) => {
34176
- log2(`[Proxy and log service] streaming preview failed: ${error40}`);
34395
+ log2(`[Proxy and log service] Streaming preview failed: ${error40}`);
34177
34396
  sendWsMessage(ws, { type: "proxy_result_error", id: pr.id, error: error40 });
34178
34397
  }
34179
34398
  });
@@ -34246,11 +34465,11 @@ var handleProxyMessage = (msg, deps) => {
34246
34465
  return;
34247
34466
  }
34248
34467
  void proxyToLocal(pr).then((res) => {
34249
- if (res.error) deps.log(`[Proxy and log service] preview proxy failed: ${res.error}`);
34468
+ if (res.error) deps.log(`[Proxy and log service] Preview proxy failed: ${res.error}`);
34250
34469
  sendWsMessage(deps.ws, { type: "proxy_result", ...res, id: pr.id });
34251
34470
  }).catch((err) => {
34252
34471
  deps.log(
34253
- `[Proxy and log service] preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
34472
+ `[Proxy and log service] Preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
34254
34473
  );
34255
34474
  sendWsMessage(deps.ws, { type: "proxy_result", id: pr.id, error: String(err) });
34256
34475
  });
@@ -34287,6 +34506,7 @@ function tryConsumeBinaryProxyBody(raw, deps) {
34287
34506
  }
34288
34507
 
34289
34508
  // src/firehose/connect-firehose.ts
34509
+ var FIREHOSE_CLIENT_PING_MS = 25e3;
34290
34510
  function connectFirehose(options) {
34291
34511
  const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
34292
34512
  const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
@@ -34295,6 +34515,13 @@ function connectFirehose(options) {
34295
34515
  wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
34296
34516
  }
34297
34517
  const ws = new wrapper_default(wsUrl, wsOptions);
34518
+ let clientPingTimer = null;
34519
+ function clearClientPing() {
34520
+ if (clientPingTimer != null) {
34521
+ clearInterval(clientPingTimer);
34522
+ clientPingTimer = null;
34523
+ }
34524
+ }
34298
34525
  const firehoseSend = (payload) => {
34299
34526
  sendWsMessage(ws, payload);
34300
34527
  };
@@ -34307,6 +34534,15 @@ function connectFirehose(options) {
34307
34534
  startStreamingProxy: (pr) => startStreamingProxy(ws, log2, pr)
34308
34535
  };
34309
34536
  ws.on("open", () => {
34537
+ clearClientPing();
34538
+ clientPingTimer = setInterval(() => {
34539
+ if (ws.readyState === wrapper_default.OPEN) {
34540
+ try {
34541
+ ws.ping();
34542
+ } catch {
34543
+ }
34544
+ }
34545
+ }, FIREHOSE_CLIENT_PING_MS);
34310
34546
  onOpen?.();
34311
34547
  devServerManager.attachFirehose(firehoseSend);
34312
34548
  sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
@@ -34324,35 +34560,30 @@ function connectFirehose(options) {
34324
34560
  });
34325
34561
  });
34326
34562
  ws.on("close", (code, reason) => {
34563
+ clearClientPing();
34327
34564
  devServerManager.detachFirehose();
34328
- log2(
34329
- formatWebSocketClose(
34330
- "[Proxy and log service]",
34331
- code,
34332
- typeof reason === "string" ? reason : reason.toString(),
34333
- "reconnects automatically if the bridge service stays connected"
34334
- )
34335
- );
34336
- onClose?.();
34565
+ const reasonStr = typeof reason === "string" ? reason : reason.toString();
34566
+ onClose?.(code, reasonStr);
34337
34567
  });
34338
34568
  ws.on("error", (err) => {
34569
+ clearClientPing();
34339
34570
  log2(`[Proxy and log service] WebSocket error: ${err.message}`);
34340
34571
  });
34341
34572
  return {
34342
34573
  close() {
34574
+ clearClientPing();
34343
34575
  devServerManager.detachFirehose();
34344
34576
  try {
34345
34577
  ws.removeAllListeners();
34346
34578
  ws.close();
34347
34579
  } catch {
34348
34580
  }
34349
- }
34581
+ },
34582
+ isConnected: () => ws.readyState === wrapper_default.OPEN
34350
34583
  };
34351
34584
  }
34352
34585
 
34353
34586
  // src/bridge/connection/create-bridge-identified-handler.ts
34354
- var FH_RECONNECT_BASE_MS = 2e3;
34355
- var FH_RECONNECT_MAX_MS = 3e4;
34356
34587
  function createOnBridgeIdentified(opts) {
34357
34588
  const { sessionWorktreeManager, devServerManager, firehoseServerUrl, workspaceId, state, logFn } = opts;
34358
34589
  function clearFirehoseReconnectTimer() {
@@ -34361,6 +34592,14 @@ function createOnBridgeIdentified(opts) {
34361
34592
  state.firehoseReconnectTimeout = null;
34362
34593
  }
34363
34594
  }
34595
+ function firehoseCtx() {
34596
+ return {
34597
+ closedByUser: state.closedByUser,
34598
+ currentWs: state.currentWs,
34599
+ firehoseHandle: state.firehoseHandle,
34600
+ firehoseQuiet: state.firehoseQuiet
34601
+ };
34602
+ }
34364
34603
  function attachFirehose(params) {
34365
34604
  state.lastFirehoseParams = params;
34366
34605
  clearFirehoseReconnectTimer();
@@ -34379,37 +34618,52 @@ function createOnBridgeIdentified(opts) {
34379
34618
  devServerManager,
34380
34619
  onOpen: () => {
34381
34620
  if (myGen !== state.firehoseGeneration) return;
34621
+ clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
34622
+ const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
34382
34623
  state.firehoseReconnectAttempt = 0;
34624
+ if (logOpenAsFirehoseReconnect) {
34625
+ logFn("[Proxy and log service] reconnected.");
34626
+ }
34383
34627
  },
34384
- onClose: () => {
34628
+ onClose: (code, reason) => {
34385
34629
  if (myGen !== state.firehoseGeneration) return;
34386
34630
  state.firehoseHandle = null;
34387
34631
  if (state.closedByUser) return;
34388
34632
  const main2 = state.currentWs;
34389
34633
  if (!main2 || main2.readyState !== wrapper_default.OPEN) {
34390
- logFn("[Proxy and log service] not reconnecting: bridge service socket is not open");
34634
+ logFn(
34635
+ `${PROXY_AND_LOG_SERVICE_LABEL} Not reconnecting preview and log stream: main bridge connection is not open.`
34636
+ );
34391
34637
  return;
34392
34638
  }
34639
+ beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
34393
34640
  clearFirehoseReconnectTimer();
34394
- const delay2 = Math.min(
34395
- FH_RECONNECT_BASE_MS * 2 ** state.firehoseReconnectAttempt,
34396
- FH_RECONNECT_MAX_MS
34397
- );
34641
+ const delay2 = reconnectDelayMs(state.firehoseReconnectAttempt);
34398
34642
  state.firehoseReconnectAttempt += 1;
34399
- logFn(
34400
- `[Proxy and log service] reconnect attempt ${state.firehoseReconnectAttempt} in ${delay2 / 1e3}s\u2026`
34643
+ logNextReconnectAttempt(
34644
+ logFn,
34645
+ PROXY_AND_LOG_SERVICE_LABEL,
34646
+ state.firehoseQuiet,
34647
+ delay2,
34648
+ state.firehoseReconnectAttempt
34401
34649
  );
34402
34650
  state.firehoseReconnectTimeout = setTimeout(() => {
34403
34651
  state.firehoseReconnectTimeout = null;
34404
34652
  if (state.closedByUser) return;
34405
34653
  const w = state.currentWs;
34406
34654
  if (!w || w.readyState !== wrapper_default.OPEN) {
34407
- logFn("[Proxy and log service] reconnect skipped: bridge service socket closed");
34655
+ if (state.firehoseQuiet.verboseLogs) {
34656
+ logFn(
34657
+ `${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: main bridge connection closed before preview stream could reconnect.`
34658
+ );
34659
+ }
34408
34660
  return;
34409
34661
  }
34410
34662
  const p = state.lastFirehoseParams;
34411
34663
  if (!p) {
34412
- logFn("[Proxy and log service] reconnect skipped: no stored connection params");
34664
+ if (state.firehoseQuiet.verboseLogs) {
34665
+ logFn(`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: no stored connection parameters.`);
34666
+ }
34413
34667
  return;
34414
34668
  }
34415
34669
  attachFirehose(p);
@@ -34492,10 +34746,12 @@ function createSendLocalSkillsReport(getWs, logFn) {
34492
34746
  try {
34493
34747
  const socket = getWs();
34494
34748
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
34495
- const skills2 = discoverLocalSkills(process.cwd());
34749
+ const skills2 = discoverLocalSkills(getBridgeWorkspaceDirectory());
34496
34750
  socket.send(JSON.stringify({ type: "local_skills", skills: skills2 }));
34497
34751
  } catch (e) {
34498
- logFn(`[Bridge service] local_skills report failed: ${e instanceof Error ? e.message : String(e)}`);
34752
+ logFn(
34753
+ `[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
34754
+ );
34499
34755
  }
34500
34756
  };
34501
34757
  }
@@ -34508,12 +34764,15 @@ function createReportAutoDetectedAgents(getWs, logFn) {
34508
34764
  sendWsMessage(socket, { type: "auto_detected_agents_report", agentTypes: types });
34509
34765
  }
34510
34766
  } catch (e) {
34511
- logFn(`[Bridge service] auto_detected_agents report failed: ${e instanceof Error ? e.message : String(e)}`);
34767
+ logFn(
34768
+ `[Bridge service] Auto-detected agents report failed: ${e instanceof Error ? e.message : String(e)}`
34769
+ );
34512
34770
  }
34513
34771
  };
34514
34772
  }
34515
34773
 
34516
34774
  // src/bridge/connection/create-bridge-connection.ts
34775
+ var BRIDGE_CLIENT_PING_MS = 25e3;
34517
34776
  async function createBridgeConnection(options) {
34518
34777
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
34519
34778
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -34524,13 +34783,16 @@ async function createBridgeConnection(options) {
34524
34783
  const state = {
34525
34784
  closedByUser: false,
34526
34785
  reconnectAttempt: 0,
34786
+ logBridgeOpenAsReconnect: false,
34527
34787
  reconnectTimeout: null,
34528
34788
  currentWs: null,
34789
+ mainQuiet: createEmptyReconnectQuietSlot(),
34529
34790
  firehoseHandle: null,
34530
34791
  lastFirehoseParams: null,
34531
34792
  firehoseReconnectTimeout: null,
34532
34793
  firehoseReconnectAttempt: 0,
34533
- firehoseGeneration: 0
34794
+ firehoseGeneration: 0,
34795
+ firehoseQuiet: createEmptyReconnectQuietSlot()
34534
34796
  };
34535
34797
  const worktreesRootAbs = options.worktreesRootAbs ?? defaultWorktreesRootAbs();
34536
34798
  const sessionWorktreeManager = new SessionWorktreeManager({
@@ -34542,7 +34804,7 @@ async function createBridgeConnection(options) {
34542
34804
  function getWs() {
34543
34805
  return state.currentWs;
34544
34806
  }
34545
- const devServerManager = new DevServerManager({ getWs, log: logFn });
34807
+ const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory });
34546
34808
  const onBridgeIdentified = createOnBridgeIdentified({
34547
34809
  sessionWorktreeManager,
34548
34810
  devServerManager,
@@ -34554,7 +34816,13 @@ async function createBridgeConnection(options) {
34554
34816
  const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
34555
34817
  const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
34556
34818
  function handleOpen() {
34819
+ const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
34820
+ clearMainBridgeReconnectQuietOnOpen(state, logFn);
34557
34821
  state.reconnectAttempt = 0;
34822
+ state.logBridgeOpenAsReconnect = false;
34823
+ if (logOpenAsPostRefreshReconnect) {
34824
+ logFn("[Bridge service] reconnected.");
34825
+ }
34558
34826
  const socket = getWs();
34559
34827
  if (socket) {
34560
34828
  sendWsMessage(socket, { type: "identify", role: "cli" });
@@ -34571,11 +34839,9 @@ async function createBridgeConnection(options) {
34571
34839
  state.currentWs = null;
34572
34840
  if (was) was.removeAllListeners();
34573
34841
  const willReconnect = !state.closedByUser;
34574
- logFn(
34575
- formatWebSocketClose("[Bridge service]", code, reason, willReconnect ? "will schedule reconnect" : "not reconnecting (CLI shutting down)")
34576
- );
34842
+ beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
34577
34843
  if (willReconnect) {
34578
- scheduleReconnect(state, connect, logFn);
34844
+ scheduleMainBridgeReconnect(state, connect, logFn);
34579
34845
  }
34580
34846
  }
34581
34847
  const messageDeps = {
@@ -34590,6 +34856,10 @@ async function createBridgeConnection(options) {
34590
34856
  };
34591
34857
  function connect() {
34592
34858
  if (state.closedByUser) return;
34859
+ if (state.reconnectTimeout != null) {
34860
+ clearTimeout(state.reconnectTimeout);
34861
+ state.reconnectTimeout = null;
34862
+ }
34593
34863
  const prev = state.currentWs;
34594
34864
  if (prev) {
34595
34865
  prev.removeAllListeners();
@@ -34602,6 +34872,7 @@ async function createBridgeConnection(options) {
34602
34872
  const url2 = buildBridgeUrl(apiUrl, workspaceId, accessToken);
34603
34873
  state.currentWs = createWsBridge({
34604
34874
  url: url2,
34875
+ clientPingIntervalMs: BRIDGE_CLIENT_PING_MS,
34605
34876
  onAuthInvalid: () => {
34606
34877
  if (authRefreshInFlight) return;
34607
34878
  void (async () => {
@@ -34613,8 +34884,9 @@ async function createBridgeConnection(options) {
34613
34884
  accessToken = next.token;
34614
34885
  refreshTok = next.refreshToken;
34615
34886
  persistTokens?.({ token: accessToken, refreshToken: refreshTok });
34616
- logFn("[Bridge service] access token refreshed; reconnecting\u2026");
34887
+ logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
34617
34888
  state.reconnectAttempt = 0;
34889
+ state.logBridgeOpenAsReconnect = true;
34618
34890
  authRefreshInFlight = false;
34619
34891
  connect();
34620
34892
  return;
@@ -34637,7 +34909,7 @@ async function createBridgeConnection(options) {
34637
34909
  });
34638
34910
  }
34639
34911
  connect();
34640
- const stopFileIndexWatcher = startFileIndexWatcher(process.cwd());
34912
+ const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
34641
34913
  return {
34642
34914
  close: async () => {
34643
34915
  stopFileIndexWatcher();
@@ -34710,7 +34982,7 @@ async function runBridge(options) {
34710
34982
  });
34711
34983
  },
34712
34984
  onAuthInvalid: () => {
34713
- log("[Bridge service] token invalid or revoked; re-authenticating\u2026");
34985
+ log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
34714
34986
  clearConfigForApi(apiUrl);
34715
34987
  void handle.close().then(() => {
34716
34988
  void runBridge({ apiUrl, firehoseServerUrl, worktreesRootAbs });
@@ -34757,7 +35029,7 @@ async function main() {
34757
35029
  if (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim()) {
34758
35030
  const resolvedCwd = path24.resolve(process.cwd(), opts.cwd.trim());
34759
35031
  try {
34760
- const st = fs20.statSync(resolvedCwd);
35032
+ const st = fs21.statSync(resolvedCwd);
34761
35033
  if (!st.isDirectory()) {
34762
35034
  console.error(`--cwd is not a directory: ${resolvedCwd}`);
34763
35035
  process.exit(1);
@@ -34768,6 +35040,7 @@ async function main() {
34768
35040
  }
34769
35041
  process.chdir(resolvedCwd);
34770
35042
  }
35043
+ initBridgeWorkspaceDirectory();
34771
35044
  let worktreesRootAbs;
34772
35045
  if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
34773
35046
  worktreesRootAbs = path24.resolve(opts.worktreesRoot.trim());