@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/index.js CHANGED
@@ -22065,12 +22065,19 @@ var wrapper_default = import_websocket.default;
22065
22065
  var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
22066
22066
  var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
22067
22067
  function createWsBridge(options) {
22068
- const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid } = options;
22068
+ const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
22069
22069
  const wsOptions = {};
22070
22070
  if (url2.startsWith("wss://")) {
22071
22071
  wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
22072
22072
  }
22073
22073
  const ws = new wrapper_default(url2, wsOptions);
22074
+ let clientPingTimer = null;
22075
+ function clearClientPing() {
22076
+ if (clientPingTimer != null) {
22077
+ clearInterval(clientPingTimer);
22078
+ clientPingTimer = null;
22079
+ }
22080
+ }
22074
22081
  ws.on("unexpected-response", (request, response) => {
22075
22082
  const status = response?.statusCode ?? 0;
22076
22083
  const headers = response?.headers ?? {};
@@ -22080,6 +22087,17 @@ function createWsBridge(options) {
22080
22087
  }
22081
22088
  });
22082
22089
  ws.on("open", () => {
22090
+ clearClientPing();
22091
+ if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
22092
+ clientPingTimer = setInterval(() => {
22093
+ if (ws.readyState === wrapper_default.OPEN) {
22094
+ try {
22095
+ ws.ping();
22096
+ } catch {
22097
+ }
22098
+ }
22099
+ }, clientPingIntervalMs);
22100
+ }
22083
22101
  onOpen?.();
22084
22102
  });
22085
22103
  ws.on("message", (raw) => {
@@ -22099,9 +22117,11 @@ function createWsBridge(options) {
22099
22117
  }
22100
22118
  });
22101
22119
  ws.on("close", (code, reason) => {
22120
+ clearClientPing();
22102
22121
  onClose?.(code, reason.toString());
22103
22122
  });
22104
22123
  ws.on("error", (err) => {
22124
+ clearClientPing();
22105
22125
  onError2?.(err);
22106
22126
  });
22107
22127
  return ws;
@@ -22115,6 +22135,18 @@ function sendWsMessage(ws, payload) {
22115
22135
  // src/acp/clients/acp-client.ts
22116
22136
  import { spawn } from "node:child_process";
22117
22137
  import { Readable, Writable } from "node:stream";
22138
+
22139
+ // src/files/cwd/bridge-workspace-directory.ts
22140
+ import * as path from "node:path";
22141
+ var bridgeWorkspaceDirectory = null;
22142
+ function getBridgeWorkspaceDirectory() {
22143
+ if (bridgeWorkspaceDirectory == null) {
22144
+ bridgeWorkspaceDirectory = path.resolve(process.cwd());
22145
+ }
22146
+ return bridgeWorkspaceDirectory;
22147
+ }
22148
+
22149
+ // src/acp/clients/acp-client.ts
22118
22150
  function formatSpawnError(err, command) {
22119
22151
  if (err.code === "ENOENT") {
22120
22152
  return `Command "${command}" not found. Install the agent (e.g. Cursor CLI) or add it to PATH.`;
@@ -22131,10 +22163,10 @@ function toErrorMessage(err) {
22131
22163
  }
22132
22164
  async function createAcpClient(options) {
22133
22165
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
22134
- const { command, cwd: cwd3 = process.cwd(), onSessionUpdate } = options;
22166
+ const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate } = options;
22135
22167
  const isWindows = process.platform === "win32";
22136
22168
  const child = spawn(command[0], command.slice(1), {
22137
- cwd: cwd3,
22169
+ cwd,
22138
22170
  stdio: ["pipe", "pipe", "inherit"],
22139
22171
  env: process.env,
22140
22172
  shell: isWindows
@@ -22166,7 +22198,7 @@ async function createAcpClient(options) {
22166
22198
  capabilities: {},
22167
22199
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
22168
22200
  });
22169
- const newSessionRes = await connection.newSession({ workingDirectory: cwd3 });
22201
+ const newSessionRes = await connection.newSession({ workingDirectory: cwd });
22170
22202
  const sessionId = newSessionRes.sessionId;
22171
22203
  resolve14({
22172
22204
  sessionId,
@@ -22399,7 +22431,7 @@ var previewSkill = {
22399
22431
  const exe = parts[0];
22400
22432
  const args = parts.slice(1);
22401
22433
  previewProcess = spawn2(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
22402
- cwd: process.cwd(),
22434
+ cwd: getBridgeWorkspaceDirectory(),
22403
22435
  stdio: ["ignore", "pipe", "pipe"],
22404
22436
  env: {
22405
22437
  ...process.env,
@@ -22508,11 +22540,11 @@ function logImmediate(line) {
22508
22540
 
22509
22541
  // src/config.ts
22510
22542
  import fs from "node:fs";
22511
- import path from "node:path";
22543
+ import path2 from "node:path";
22512
22544
  import os from "node:os";
22513
22545
  function getConfigPath() {
22514
- const dir = path.join(os.homedir(), ".buildautomaton");
22515
- return path.join(dir, "config.json");
22546
+ const dir = path2.join(os.homedir(), ".buildautomaton");
22547
+ return path2.join(dir, "config.json");
22516
22548
  }
22517
22549
  function normalizeApiUrl(url2) {
22518
22550
  return url2.replace(/\/$/, "");
@@ -22528,7 +22560,7 @@ function readRawConfig() {
22528
22560
  }
22529
22561
  function writeConfigForApi(apiUrl, auth) {
22530
22562
  const p = getConfigPath();
22531
- const dir = path.dirname(p);
22563
+ const dir = path2.dirname(p);
22532
22564
  const key = normalizeApiUrl(apiUrl);
22533
22565
  const prev = readRawConfig() ?? {};
22534
22566
  const servers = { ...prev.servers ?? {}, [key]: { ...auth } };
@@ -22537,7 +22569,7 @@ function writeConfigForApi(apiUrl, auth) {
22537
22569
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
22538
22570
  fs.writeFileSync(p, JSON.stringify(next, null, 2), "utf8");
22539
22571
  } catch (e) {
22540
- console.error("Could not save auth config:", e);
22572
+ console.error("Could not save authentication config:", e);
22541
22573
  }
22542
22574
  }
22543
22575
  function clearConfigForApi(apiUrl) {
@@ -22599,6 +22631,28 @@ function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiU
22599
22631
  }
22600
22632
  }
22601
22633
 
22634
+ // src/bridge/connection/reconnect/constants.ts
22635
+ var RECONNECT_FIRST_MS = 100;
22636
+ var RECONNECT_MAX_MS = 3e4;
22637
+ var RECONNECT_QUIET_MS = 2e3;
22638
+ function reconnectDelayMs(attemptBeforeIncrement) {
22639
+ return Math.min(RECONNECT_FIRST_MS * 2 ** attemptBeforeIncrement, RECONNECT_MAX_MS);
22640
+ }
22641
+
22642
+ // src/bridge/connection/reconnect/format-reconnect-delay-for-log.ts
22643
+ function formatReconnectDelayForLog(delayMs) {
22644
+ if (delayMs < 1e3) return `${delayMs}ms`;
22645
+ const s = delayMs / 1e3;
22646
+ return Number.isInteger(s) ? `${s}s` : `${s.toFixed(1)}s`;
22647
+ }
22648
+
22649
+ // src/bridge/connection/reconnect/log-next-reconnect-attempt.ts
22650
+ function logNextReconnectAttempt(log2, serviceLabel, quiet, delayMs, attempt) {
22651
+ if (!quiet.verboseLogs) return;
22652
+ const delayLabel = formatReconnectDelayForLog(delayMs);
22653
+ log2(`${serviceLabel} Next connection attempt in ${delayLabel} (attempt ${attempt}).`);
22654
+ }
22655
+
22602
22656
  // src/bridge/connection/ws-close-diagnostics.ts
22603
22657
  function describeWebSocketCloseCode(code) {
22604
22658
  const known = {
@@ -22627,11 +22681,122 @@ function formatWebSocketClose(label, code, reason, extra) {
22627
22681
  const r = reason.trim();
22628
22682
  const reasonPart = r ? ` reason="${r}"` : "";
22629
22683
  const extraPart = extra ? ` ${extra}` : "";
22630
- return `${label} closed: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
22684
+ return `${label} Disconnected: code=${code} (${describeWebSocketCloseCode(code)})${reasonPart}${extraPart}`;
22685
+ }
22686
+
22687
+ // src/bridge/connection/reconnect/reconnect-quiet-slot.ts
22688
+ function createEmptyReconnectQuietSlot() {
22689
+ return { timer: null, verboseLogs: false, pendingCloseLog: null };
22690
+ }
22691
+ function clearReconnectQuietTimer(quiet) {
22692
+ if (quiet.timer != null) {
22693
+ clearTimeout(quiet.timer);
22694
+ quiet.timer = null;
22695
+ }
22696
+ }
22697
+ function clearReconnectQuietOnSuccessfulConnection(quiet, log2, reconnectedMessage) {
22698
+ clearReconnectQuietTimer(quiet);
22699
+ quiet.pendingCloseLog = null;
22700
+ if (quiet.verboseLogs) {
22701
+ log2(reconnectedMessage);
22702
+ quiet.verboseLogs = false;
22703
+ }
22704
+ }
22705
+ function beginDeferredDisconnectForReconnect(options) {
22706
+ const {
22707
+ isClosedByUser,
22708
+ quiet,
22709
+ code,
22710
+ reason,
22711
+ willReconnect,
22712
+ log: log2,
22713
+ serviceLabel,
22714
+ shutdownDetail,
22715
+ reconnectingDetail,
22716
+ quietMs = RECONNECT_QUIET_MS,
22717
+ shouldAbortQuietWindow
22718
+ } = options;
22719
+ if (!willReconnect) {
22720
+ log2(formatWebSocketClose(serviceLabel, code, reason, shutdownDetail));
22721
+ return;
22722
+ }
22723
+ quiet.pendingCloseLog = { code, reason };
22724
+ if (quiet.timer == null) {
22725
+ quiet.timer = setTimeout(() => {
22726
+ quiet.timer = null;
22727
+ if (isClosedByUser()) return;
22728
+ if (shouldAbortQuietWindow()) return;
22729
+ if (quiet.pendingCloseLog) {
22730
+ const { code: c, reason: r } = quiet.pendingCloseLog;
22731
+ quiet.pendingCloseLog = null;
22732
+ log2(formatWebSocketClose(serviceLabel, c, r, reconnectingDetail));
22733
+ }
22734
+ quiet.verboseLogs = true;
22735
+ }, quietMs);
22736
+ }
22737
+ }
22738
+
22739
+ // src/bridge/connection/reconnect/bridge-main-reconnect.ts
22740
+ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconnect) {
22741
+ beginDeferredDisconnectForReconnect({
22742
+ isClosedByUser: () => state.closedByUser,
22743
+ quiet: state.mainQuiet,
22744
+ code,
22745
+ reason,
22746
+ willReconnect,
22747
+ log: log2,
22748
+ serviceLabel: "[Bridge service]",
22749
+ shutdownDetail: "Not reconnecting (shutting down).",
22750
+ reconnectingDetail: "Reconnecting\u2026",
22751
+ shouldAbortQuietWindow: () => {
22752
+ const w = state.currentWs;
22753
+ return w != null && w.readyState === wrapper_default.OPEN;
22754
+ }
22755
+ });
22756
+ }
22757
+ function clearMainBridgeReconnectQuietOnOpen(state, log2) {
22758
+ clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "[Bridge service] Reconnected.");
22759
+ }
22760
+ function scheduleMainBridgeReconnect(state, connect, log2) {
22761
+ if (state.closedByUser || state.currentWs != null) return;
22762
+ const delay2 = reconnectDelayMs(state.reconnectAttempt);
22763
+ state.reconnectAttempt += 1;
22764
+ logNextReconnectAttempt(log2, "[Bridge service]", state.mainQuiet, delay2, state.reconnectAttempt);
22765
+ state.reconnectTimeout = setTimeout(() => {
22766
+ state.reconnectTimeout = null;
22767
+ connect();
22768
+ }, delay2);
22769
+ }
22770
+
22771
+ // src/bridge/connection/reconnect/firehose-reconnect.ts
22772
+ var PROXY_AND_LOG_SERVICE_LABEL = "[Proxy and log service]";
22773
+ function beginFirehoseDeferredDisconnect(ctx, code, reason, log2) {
22774
+ beginDeferredDisconnectForReconnect({
22775
+ isClosedByUser: () => ctx.closedByUser,
22776
+ quiet: ctx.firehoseQuiet,
22777
+ code,
22778
+ reason,
22779
+ willReconnect: true,
22780
+ log: log2,
22781
+ serviceLabel: PROXY_AND_LOG_SERVICE_LABEL,
22782
+ shutdownDetail: "Not reconnecting (shutting down).",
22783
+ reconnectingDetail: "Reconnecting\u2026",
22784
+ shouldAbortQuietWindow: () => {
22785
+ const w = ctx.currentWs;
22786
+ if (!w || w.readyState !== wrapper_default.OPEN) return true;
22787
+ return ctx.firehoseHandle?.isConnected() ?? false;
22788
+ }
22789
+ });
22790
+ }
22791
+ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
22792
+ clearReconnectQuietOnSuccessfulConnection(
22793
+ ctx.firehoseQuiet,
22794
+ log2,
22795
+ `${PROXY_AND_LOG_SERVICE_LABEL} Reconnected.`
22796
+ );
22631
22797
  }
22632
22798
 
22633
22799
  // src/auth/run-pending-auth.ts
22634
- var PENDING_RECONNECT_MS = 2e3;
22635
22800
  var PENDING_KEEPALIVE_MS = 25e3;
22636
22801
  var BROWSER_OPEN_FALLBACK_MS = 4e3;
22637
22802
  function buildPendingBridgeUrl(apiUrl, connectionId) {
@@ -22653,7 +22818,32 @@ function runPendingAuth(options) {
22653
22818
  const authPromise = new Promise((resolve14) => {
22654
22819
  resolveAuth = resolve14;
22655
22820
  });
22821
+ let reconnectAttempt = 0;
22822
+ const signInQuiet = createEmptyReconnectQuietSlot();
22823
+ function clearQuietOnOpen() {
22824
+ clearReconnectQuietOnSuccessfulConnection(
22825
+ signInQuiet,
22826
+ logFn,
22827
+ "[Bridge service] Sign-in connection restored."
22828
+ );
22829
+ reconnectAttempt = 0;
22830
+ }
22831
+ function beginDeferredPendingCloseLog(code, reason) {
22832
+ beginDeferredDisconnectForReconnect({
22833
+ isClosedByUser: () => resolved,
22834
+ quiet: signInQuiet,
22835
+ code,
22836
+ reason,
22837
+ willReconnect: true,
22838
+ log: logFn,
22839
+ serviceLabel: "[Bridge service]",
22840
+ shutdownDetail: "Not reconnecting (shutting down).",
22841
+ reconnectingDetail: "Waiting for browser sign-in; reconnecting\u2026",
22842
+ shouldAbortQuietWindow: () => ws != null && ws.readyState === wrapper_default.OPEN
22843
+ });
22844
+ }
22656
22845
  function cleanup() {
22846
+ clearReconnectQuietTimer(signInQuiet);
22657
22847
  if (reconnectTimeout) {
22658
22848
  clearTimeout(reconnectTimeout);
22659
22849
  reconnectTimeout = null;
@@ -22677,6 +22867,7 @@ function runPendingAuth(options) {
22677
22867
  ws = createWsBridge({
22678
22868
  url: url2,
22679
22869
  onOpen: () => {
22870
+ clearQuietOnOpen();
22680
22871
  sendWsMessage(ws, { type: "identify", role: "cli" });
22681
22872
  keepaliveInterval = setInterval(() => {
22682
22873
  if (resolved || !ws || ws.readyState !== 1) return;
@@ -22697,15 +22888,21 @@ function runPendingAuth(options) {
22697
22888
  keepaliveInterval = null;
22698
22889
  }
22699
22890
  if (resolved) return;
22700
- logFn(
22701
- formatWebSocketClose("[Bridge service]", code, reason, "pending sign-in; will reconnect in 2s")
22702
- );
22891
+ beginDeferredPendingCloseLog(code, reason);
22892
+ const delay2 = reconnectDelayMs(reconnectAttempt);
22893
+ reconnectAttempt += 1;
22894
+ if (signInQuiet.verboseLogs) {
22895
+ const delayLabel = formatReconnectDelayForLog(delay2);
22896
+ logFn(
22897
+ `[Bridge service] Next sign-in connection attempt in ${delayLabel} (attempt ${reconnectAttempt}).`
22898
+ );
22899
+ }
22703
22900
  reconnectTimeout = setTimeout(() => {
22704
22901
  reconnectTimeout = null;
22705
22902
  connect();
22706
- }, PENDING_RECONNECT_MS);
22903
+ }, delay2);
22707
22904
  },
22708
- onError: (err) => logFn(`[Bridge service] WebSocket error (pending sign-in): ${err.message}`),
22905
+ onError: (err) => logFn(`[Bridge service] WebSocket error while waiting for sign-in: ${err.message}`),
22709
22906
  onMessage: (data) => {
22710
22907
  const msg = data;
22711
22908
  if (msg.type === "auth_token" && typeof msg.token === "string") {
@@ -22746,7 +22943,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
22746
22943
 
22747
22944
  // src/git/discover-repos.ts
22748
22945
  import * as fs2 from "node:fs";
22749
- import * as path2 from "node:path";
22946
+ import * as path3 from "node:path";
22750
22947
 
22751
22948
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
22752
22949
  var import_file_exists = __toESM(require_dist(), 1);
@@ -24008,8 +24205,8 @@ var init_git_executor_chain = __esm2({
24008
24205
  get cwd() {
24009
24206
  return this._cwd || this._executor.cwd;
24010
24207
  }
24011
- set cwd(cwd3) {
24012
- this._cwd = cwd3;
24208
+ set cwd(cwd) {
24209
+ this._cwd = cwd;
24013
24210
  }
24014
24211
  get env() {
24015
24212
  return this._executor.env;
@@ -24200,8 +24397,8 @@ var init_git_executor = __esm2({
24200
24397
  "use strict";
24201
24398
  init_git_executor_chain();
24202
24399
  GitExecutor = class {
24203
- constructor(cwd3, _scheduler, _plugins) {
24204
- this.cwd = cwd3;
24400
+ constructor(cwd, _scheduler, _plugins) {
24401
+ this.cwd = cwd;
24205
24402
  this._scheduler = _scheduler;
24206
24403
  this._plugins = _plugins;
24207
24404
  this._chain = new GitExecutorChain(this, this._scheduler, this._plugins);
@@ -27331,9 +27528,9 @@ async function isGitRepoDirectory(dirPath) {
27331
27528
  }
27332
27529
 
27333
27530
  // src/git/discover-repos.ts
27334
- async function discoverGitRepos(cwd3 = process.cwd()) {
27531
+ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27335
27532
  const result = [];
27336
- const cwdResolved = path2.resolve(cwd3);
27533
+ const cwdResolved = path3.resolve(cwd);
27337
27534
  if (await isGitRepoDirectory(cwdResolved)) {
27338
27535
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
27339
27536
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -27346,7 +27543,7 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
27346
27543
  }
27347
27544
  for (const ent of entries) {
27348
27545
  if (!ent.isDirectory()) continue;
27349
- const childPath = path2.join(cwdResolved, ent.name);
27546
+ const childPath = path3.join(cwdResolved, ent.name);
27350
27547
  if (await isGitRepoDirectory(childPath)) {
27351
27548
  const remoteUrl = await getRemoteOriginUrl(childPath);
27352
27549
  result.push({ absolutePath: childPath, remoteUrl });
@@ -27355,11 +27552,11 @@ async function discoverGitRepos(cwd3 = process.cwd()) {
27355
27552
  return result;
27356
27553
  }
27357
27554
  async function discoverGitReposUnderRoot(rootAbs) {
27358
- const root = path2.resolve(rootAbs);
27555
+ const root = path3.resolve(rootAbs);
27359
27556
  const roots = [];
27360
27557
  async function walk(dir) {
27361
27558
  if (await isGitRepoDirectory(dir)) {
27362
- roots.push(path2.resolve(dir));
27559
+ roots.push(path3.resolve(dir));
27363
27560
  return;
27364
27561
  }
27365
27562
  let entries;
@@ -27370,7 +27567,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
27370
27567
  }
27371
27568
  for (const ent of entries) {
27372
27569
  if (!ent.isDirectory() || ent.name === ".git") continue;
27373
- await walk(path2.join(dir, ent.name));
27570
+ await walk(path3.join(dir, ent.name));
27374
27571
  }
27375
27572
  }
27376
27573
  await walk(root);
@@ -27397,28 +27594,13 @@ function reportGitRepos(getWs, log2) {
27397
27594
  }
27398
27595
  }
27399
27596
  }).catch((err) => {
27400
- log2(`[Bridge service] git repo discovery failed: ${err instanceof Error ? err.message : String(err)}`);
27597
+ log2(
27598
+ `[Bridge service] Git repository discovery failed: ${err instanceof Error ? err.message : String(err)}`
27599
+ );
27401
27600
  });
27402
27601
  });
27403
27602
  }
27404
27603
 
27405
- // src/bridge/connection/schedule-reconnect.ts
27406
- var RECONNECT_BASE_MS = 1e3;
27407
- var RECONNECT_MAX_MS = 3e4;
27408
- function scheduleReconnect(state, connect, log2) {
27409
- if (state.closedByUser || state.currentWs != null) return;
27410
- const delay2 = Math.min(
27411
- RECONNECT_BASE_MS * 2 ** state.reconnectAttempt,
27412
- RECONNECT_MAX_MS
27413
- );
27414
- state.reconnectAttempt += 1;
27415
- log2(`[Bridge service] reconnect attempt ${state.reconnectAttempt} in ${delay2 / 1e3}s\u2026`);
27416
- state.reconnectTimeout = setTimeout(() => {
27417
- state.reconnectTimeout = null;
27418
- connect();
27419
- }, delay2);
27420
- }
27421
-
27422
27604
  // src/bridge/connection/close-bridge-connection.ts
27423
27605
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
27424
27606
  log2?.("Shutting down\u2026");
@@ -27428,25 +27610,27 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
27428
27610
  await devServerManager.shutdownAllGraceful();
27429
27611
  }
27430
27612
  state.closedByUser = true;
27613
+ clearReconnectQuietTimer(state.mainQuiet);
27614
+ clearReconnectQuietTimer(state.firehoseQuiet);
27431
27615
  if (state.reconnectTimeout != null) {
27432
27616
  log2?.("Cancelling reconnect timer\u2026");
27433
27617
  clearTimeout(state.reconnectTimeout);
27434
27618
  state.reconnectTimeout = null;
27435
27619
  }
27436
27620
  if (state.firehoseReconnectTimeout != null) {
27437
- log2?.("[Proxy and log service] cancelling reconnect timer\u2026");
27621
+ log2?.("[Proxy and log service] Cancelling reconnect timer\u2026");
27438
27622
  clearTimeout(state.firehoseReconnectTimeout);
27439
27623
  state.firehoseReconnectTimeout = null;
27440
27624
  }
27441
27625
  if (state.firehoseHandle) {
27442
- log2?.("[Proxy and log service] closing connection (CLI shutdown)\u2026");
27626
+ log2?.("[Proxy and log service] Closing connection (CLI shutdown)\u2026");
27443
27627
  state.firehoseHandle.close();
27444
27628
  state.firehoseHandle = null;
27445
27629
  }
27446
27630
  log2?.("Disconnecting local agents (ACP)\u2026");
27447
27631
  acpManager.disconnect();
27448
27632
  if (state.currentWs) {
27449
- log2?.("[Bridge service] closing connection (CLI shutdown)\u2026");
27633
+ log2?.("[Bridge service] Closing connection (CLI shutdown)\u2026");
27450
27634
  state.currentWs.removeAllListeners();
27451
27635
  const wsState = state.currentWs.readyState;
27452
27636
  if (wsState === 1 || wsState === 2) {
@@ -27462,11 +27646,20 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
27462
27646
  }
27463
27647
 
27464
27648
  // src/git/session-git-queue.ts
27649
+ import { execFile as execFile2 } from "node:child_process";
27650
+ import { promisify as promisify2 } from "node:util";
27651
+ import * as fs4 from "node:fs";
27652
+ import * as path5 from "node:path";
27653
+
27654
+ // src/git/pre-turn-snapshot.ts
27655
+ import * as fs3 from "node:fs";
27656
+ import * as path4 from "node:path";
27465
27657
  import { execFile } from "node:child_process";
27466
27658
  import { promisify } from "node:util";
27467
- import * as path3 from "node:path";
27468
27659
  var execFileAsync = promisify(execFile);
27469
- var sessionBoundaryBySessionId = /* @__PURE__ */ new Map();
27660
+ function snapshotsDirForCwd(agentCwd) {
27661
+ return path4.join(agentCwd, ".buildautomaton", "snapshots");
27662
+ }
27470
27663
  async function gitStashCreate(repoRoot, log2) {
27471
27664
  try {
27472
27665
  const { stdout } = await execFileAsync("git", ["stash", "create"], {
@@ -27475,51 +27668,149 @@ async function gitStashCreate(repoRoot, log2) {
27475
27668
  });
27476
27669
  return stdout.trim();
27477
27670
  } catch (e) {
27478
- log2(`[session-git-queue] stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
27671
+ log2(
27672
+ `[snapshot] Git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`
27673
+ );
27479
27674
  return "";
27480
27675
  }
27481
27676
  }
27482
- async function captureSessionGitBoundary(options) {
27483
- const { sessionId, repoRoots, log: log2 } = options;
27484
- if (!repoRoots.length) return;
27677
+ async function gitRun(repoRoot, args, log2, label) {
27678
+ try {
27679
+ await execFileAsync("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
27680
+ return { ok: true };
27681
+ } catch (e) {
27682
+ const msg = e instanceof Error ? e.message : String(e);
27683
+ log2(`[snapshot] Git ${label} failed in ${repoRoot}: ${msg}`);
27684
+ return { ok: false, error: msg };
27685
+ }
27686
+ }
27687
+ async function resolveSnapshotRepoRoots(options) {
27688
+ const { worktreePaths, fallbackCwd, log: log2 } = options;
27689
+ if (worktreePaths?.length) {
27690
+ const uniq = [...new Set(worktreePaths.map((p) => path4.resolve(p)))];
27691
+ return uniq;
27692
+ }
27693
+ try {
27694
+ const repos = await discoverGitReposUnderRoot(fallbackCwd);
27695
+ return repos.map((r) => r.absolutePath);
27696
+ } catch (e) {
27697
+ log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
27698
+ return [];
27699
+ }
27700
+ }
27701
+ async function capturePreTurnSnapshot(options) {
27702
+ const { runId, repoRoots, agentCwd, log: log2 } = options;
27703
+ if (!runId || !repoRoots.length) {
27704
+ return { ok: false, error: "No git repos to snapshot" };
27705
+ }
27485
27706
  const repos = [];
27486
27707
  for (const root of repoRoots) {
27487
27708
  const stashSha = await gitStashCreate(root, log2);
27488
27709
  repos.push({ path: root, stashSha });
27489
27710
  }
27490
- sessionBoundaryBySessionId.set(sessionId, repos);
27491
- log2(`[session-git-queue] boundary stash ${sessionId.slice(0, 8)}\u2026 (${repos.length} repo(s))`);
27711
+ const dir = snapshotsDirForCwd(agentCwd);
27712
+ try {
27713
+ fs3.mkdirSync(dir, { recursive: true });
27714
+ } catch (e) {
27715
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
27716
+ }
27717
+ const payload = {
27718
+ runId,
27719
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
27720
+ repos
27721
+ };
27722
+ const filePath = path4.join(dir, `${runId}.json`);
27723
+ try {
27724
+ fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
27725
+ } catch (e) {
27726
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
27727
+ }
27728
+ const repoList = repos.map((r) => r.path).join(", ");
27729
+ log2(
27730
+ `[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)): ${repoList}`
27731
+ );
27732
+ return { ok: true, filePath, repos };
27733
+ }
27734
+ async function applyPreTurnSnapshot(filePath, log2) {
27735
+ let data;
27736
+ try {
27737
+ const raw = fs3.readFileSync(filePath, "utf8");
27738
+ data = JSON.parse(raw);
27739
+ } catch (e) {
27740
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
27741
+ }
27742
+ if (!Array.isArray(data.repos)) {
27743
+ return { ok: false, error: "Invalid snapshot file" };
27744
+ }
27745
+ for (const r of data.repos) {
27746
+ if (!r.path) continue;
27747
+ const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
27748
+ if (!reset.ok) return reset;
27749
+ const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
27750
+ if (!clean.ok) return clean;
27751
+ if (r.stashSha) {
27752
+ const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
27753
+ if (!ap.ok) return ap;
27754
+ }
27755
+ }
27756
+ log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
27757
+ return { ok: true };
27758
+ }
27759
+ function snapshotFilePath(agentCwd, runId) {
27760
+ return path4.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
27761
+ }
27762
+
27763
+ // src/git/session-git-queue.ts
27764
+ var execFileAsync2 = promisify2(execFile2);
27765
+ var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
27766
+ function readWorkspaceFileAsUtf8(absPath) {
27767
+ try {
27768
+ const st = fs4.statSync(absPath);
27769
+ if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
27770
+ return fs4.readFileSync(absPath, "utf8");
27771
+ } catch {
27772
+ return void 0;
27773
+ }
27492
27774
  }
27493
- async function collectSessionDiffAndNotify(options) {
27494
- const { sessionId, runId, sendSessionUpdate, log: log2 } = options;
27495
- const repos = sessionBoundaryBySessionId.get(sessionId);
27496
- if (!repos?.length) {
27497
- log2(`[session-git-queue] no boundary for ${sessionId.slice(0, 8)}\u2026; skip aggregate diff`);
27775
+ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27776
+ const { sessionId, runId, agentCwd, sendSessionUpdate, log: log2 } = options;
27777
+ const filePath = snapshotFilePath(agentCwd, runId);
27778
+ let data;
27779
+ try {
27780
+ const raw = fs4.readFileSync(filePath, "utf8");
27781
+ data = JSON.parse(raw);
27782
+ } catch (e) {
27783
+ log2(
27784
+ `[session-git-queue] No pre-turn snapshot for run ${runId.slice(0, 8)}\u2026: ${e instanceof Error ? e.message : String(e)}`
27785
+ );
27498
27786
  return;
27499
27787
  }
27500
- sessionBoundaryBySessionId.delete(sessionId);
27501
- const multiRepo = repos.length > 1;
27502
- for (const repo of repos) {
27788
+ if (!Array.isArray(data.repos) || !data.repos.length) {
27789
+ log2(`[session-git-queue] Empty repos in snapshot ${runId.slice(0, 8)}\u2026; skipping aggregate diff.`);
27790
+ return;
27791
+ }
27792
+ const multiRepo = data.repos.length > 1;
27793
+ for (const repo of data.repos) {
27503
27794
  if (!repo.stashSha) continue;
27504
27795
  let namesRaw;
27505
27796
  try {
27506
- const { stdout } = await execFileAsync("git", ["diff", "--name-only", repo.stashSha], {
27797
+ const { stdout } = await execFileAsync2("git", ["diff", "--name-only", repo.stashSha], {
27507
27798
  cwd: repo.path,
27508
27799
  maxBuffer: 10 * 1024 * 1024
27509
27800
  });
27510
27801
  namesRaw = stdout;
27511
27802
  } catch (e) {
27512
27803
  log2(
27513
- `[session-git-queue] git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
27804
+ `[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
27514
27805
  );
27515
27806
  continue;
27516
27807
  }
27517
27808
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
27518
- const slug = path3.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
27809
+ const slug = path5.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
27519
27810
  for (const rel of lines) {
27520
27811
  if (rel.includes("..")) continue;
27521
27812
  try {
27522
- const { stdout: patchContent } = await execFileAsync(
27813
+ const { stdout: patchContent } = await execFileAsync2(
27523
27814
  "git",
27524
27815
  ["diff", "--no-color", repo.stashSha, "--", rel],
27525
27816
  {
@@ -27529,15 +27820,20 @@ async function collectSessionDiffAndNotify(options) {
27529
27820
  );
27530
27821
  if (!patchContent.trim()) continue;
27531
27822
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
27823
+ const absFile = path5.join(repo.path, rel);
27824
+ const newText = readWorkspaceFileAsUtf8(absFile);
27532
27825
  sendSessionUpdate({
27533
27826
  type: "session_file_change",
27534
27827
  sessionId,
27535
27828
  runId,
27536
27829
  path: displayPath,
27537
- patchContent
27830
+ patchContent,
27831
+ ...newText !== void 0 ? { newText } : {}
27538
27832
  });
27539
27833
  } catch (e) {
27540
- log2(`[session-git-queue] git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`);
27834
+ log2(
27835
+ `[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
27836
+ );
27541
27837
  }
27542
27838
  }
27543
27839
  }
@@ -27551,15 +27847,21 @@ async function sendPromptToAgent(options) {
27551
27847
  promptId,
27552
27848
  sessionId,
27553
27849
  runId,
27850
+ agentCwd,
27554
27851
  sendResult,
27555
27852
  sendSessionUpdate,
27556
- collectSessionDiffAfterTurn,
27557
27853
  log: log2
27558
27854
  } = options;
27559
27855
  try {
27560
27856
  const result = await handle.sendPrompt(promptText, {});
27561
- if (collectSessionDiffAfterTurn && sessionId && runId && sendSessionUpdate && result.success) {
27562
- await collectSessionDiffAndNotify({ sessionId, runId, sendSessionUpdate, log: log2 });
27857
+ if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
27858
+ await collectTurnGitDiffFromPreTurnSnapshot({
27859
+ sessionId,
27860
+ runId,
27861
+ agentCwd,
27862
+ sendSessionUpdate,
27863
+ log: log2
27864
+ });
27563
27865
  }
27564
27866
  sendResult({
27565
27867
  type: "prompt_result",
@@ -27569,12 +27871,12 @@ async function sendPromptToAgent(options) {
27569
27871
  ...result
27570
27872
  });
27571
27873
  if (!result.success) {
27572
- log2(`[agent] ${result.error ?? "error"}`);
27874
+ log2(`[Agent] ${result.error ?? "Error"}`);
27573
27875
  }
27574
27876
  } catch (err) {
27575
27877
  const errMsg = err instanceof Error ? err.message : String(err);
27576
- log2(`[agent] send failed: ${errMsg}`);
27577
- if (err instanceof Error && err.stack) log2(`[agent] ${err.stack}`);
27878
+ log2(`[Agent] Send failed: ${errMsg}`);
27879
+ if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
27578
27880
  sendResult({
27579
27881
  type: "prompt_result",
27580
27882
  id: promptId,
@@ -27587,8 +27889,8 @@ async function sendPromptToAgent(options) {
27587
27889
  }
27588
27890
 
27589
27891
  // src/acp/ensure-acp-client.ts
27590
- import * as fs3 from "node:fs";
27591
- import * as path7 from "node:path";
27892
+ import * as fs5 from "node:fs";
27893
+ import * as path9 from "node:path";
27592
27894
 
27593
27895
  // src/error-message.ts
27594
27896
  function errorMessage(err) {
@@ -27612,27 +27914,27 @@ async function createCodexAcpClient(options) {
27612
27914
  }
27613
27915
 
27614
27916
  // src/acp/clients/cursor-acp-client.ts
27615
- import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
27917
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
27616
27918
  import { dirname } from "node:path";
27617
27919
  import { spawn as spawn4 } from "node:child_process";
27618
27920
  import * as readline from "node:readline";
27619
27921
 
27620
27922
  // src/acp/safe-fs-path.ts
27621
- import * as path4 from "node:path";
27622
- function resolveSafePathUnderCwd(cwd3, filePath) {
27923
+ import * as path6 from "node:path";
27924
+ function resolveSafePathUnderCwd(cwd, filePath) {
27623
27925
  const trimmed2 = filePath.trim();
27624
27926
  if (!trimmed2) return null;
27625
- const normalizedCwd = path4.resolve(cwd3);
27626
- const resolved = path4.isAbsolute(trimmed2) ? path4.normalize(trimmed2) : path4.resolve(normalizedCwd, trimmed2);
27627
- const rel = path4.relative(normalizedCwd, resolved);
27628
- if (rel.startsWith("..") || path4.isAbsolute(rel)) return null;
27927
+ const normalizedCwd = path6.resolve(cwd);
27928
+ const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
27929
+ const rel = path6.relative(normalizedCwd, resolved);
27930
+ if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
27629
27931
  return resolved;
27630
27932
  }
27631
- function toDisplayPathRelativeToCwd(cwd3, absolutePath) {
27632
- const normalizedCwd = path4.resolve(cwd3);
27633
- const rel = path4.relative(normalizedCwd, path4.resolve(absolutePath));
27634
- if (!rel || rel === "") return path4.basename(absolutePath);
27635
- return rel.split(path4.sep).join("/");
27933
+ function toDisplayPathRelativeToCwd(cwd, absolutePath) {
27934
+ const normalizedCwd = path6.resolve(cwd);
27935
+ const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
27936
+ if (!rel || rel === "") return path6.basename(absolutePath);
27937
+ return rel.split(path6.sep).join("/");
27636
27938
  }
27637
27939
 
27638
27940
  // src/files/diff/unified-diff.ts
@@ -27681,6 +27983,19 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
27681
27983
  return out.join("\n");
27682
27984
  }
27683
27985
 
27986
+ // src/acp/format-session-update-kind-for-log.ts
27987
+ var SESSION_UPDATE_KIND_LABELS = {
27988
+ tool_call: "Tool call",
27989
+ tool_call_update: "Tool call status",
27990
+ agent_message_chunk: "Agent message chunk",
27991
+ update: "Session update"
27992
+ };
27993
+ function formatSessionUpdateKindForLog(kind) {
27994
+ const known = SESSION_UPDATE_KIND_LABELS[kind];
27995
+ if (known) return known;
27996
+ return kind.split("_").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
27997
+ }
27998
+
27684
27999
  // src/acp/clients/cursor-acp-client.ts
27685
28000
  var FS_READ_METHODS = /* @__PURE__ */ new Set(["fs/read_text_file", "fs/readTextFile"]);
27686
28001
  var FS_WRITE_METHODS = /* @__PURE__ */ new Set(["fs/write_text_file", "fs/writeTextFile"]);
@@ -27706,11 +28021,11 @@ function sliceLinesByRange(content, line, limit) {
27706
28021
  return lines.slice(start, end).join("\n");
27707
28022
  }
27708
28023
  async function createCursorAcpClient(options) {
27709
- const { command, cwd: cwd3 = process.cwd(), onSessionUpdate, onRequest, onFileChange } = options;
28024
+ const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
27710
28025
  const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
27711
28026
  const isWindows = process.platform === "win32";
27712
28027
  const child = spawn4(command[0], command.slice(1), {
27713
- cwd: cwd3,
28028
+ cwd,
27714
28029
  stdio: ["pipe", "pipe", "inherit"],
27715
28030
  env: process.env,
27716
28031
  shell: isWindows
@@ -27768,7 +28083,10 @@ async function createCursorAcpClient(options) {
27768
28083
  const toolCall = update.toolCall ?? update.tool_call;
27769
28084
  const toolName = typeof toolCall?.name === "string" ? toolCall.name : "";
27770
28085
  if (dbgFs && (sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update")) {
27771
- console.error(`[acp] recv session/update kind=${sessionUpdate} tool=${toolName || "(none)"}`);
28086
+ const kindLabel = formatSessionUpdateKindForLog(sessionUpdate ?? "update");
28087
+ console.error(
28088
+ `[acp] Received session update (${kindLabel}) tool=${toolName || "(none)"}`
28089
+ );
27772
28090
  }
27773
28091
  const isTextChunk = sessionUpdate === "agent_message_chunk" && update.content?.text;
27774
28092
  if (isTextChunk && update.content?.text) {
@@ -27788,14 +28106,14 @@ async function createCursorAcpClient(options) {
27788
28106
  if (dbgFs) {
27789
28107
  console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
27790
28108
  }
27791
- const abs = resolveSafePathUnderCwd(cwd3, filePath);
28109
+ const abs = resolveSafePathUnderCwd(cwd, filePath);
27792
28110
  if (!abs) {
27793
28111
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
27794
28112
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
27795
28113
  return;
27796
28114
  }
27797
28115
  try {
27798
- let content = readFileSync(abs, "utf8");
28116
+ let content = readFileSync3(abs, "utf8");
27799
28117
  const line2 = typeof params.line === "number" ? params.line : void 0;
27800
28118
  const limit = typeof params.limit === "number" ? params.limit : void 0;
27801
28119
  content = sliceLinesByRange(content, line2, limit);
@@ -27819,7 +28137,7 @@ async function createCursorAcpClient(options) {
27819
28137
  `[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
27820
28138
  );
27821
28139
  }
27822
- const abs = resolveSafePathUnderCwd(cwd3, filePath);
28140
+ const abs = resolveSafePathUnderCwd(cwd, filePath);
27823
28141
  if (!abs) {
27824
28142
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
27825
28143
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
@@ -27827,7 +28145,7 @@ async function createCursorAcpClient(options) {
27827
28145
  }
27828
28146
  let oldText = "";
27829
28147
  try {
27830
- oldText = readFileSync(abs, "utf8");
28148
+ oldText = readFileSync3(abs, "utf8");
27831
28149
  } catch (e) {
27832
28150
  if (e.code !== "ENOENT") {
27833
28151
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
@@ -27835,13 +28153,13 @@ async function createCursorAcpClient(options) {
27835
28153
  }
27836
28154
  }
27837
28155
  try {
27838
- mkdirSync(dirname(abs), { recursive: true });
27839
- writeFileSync(abs, newText, "utf8");
28156
+ mkdirSync2(dirname(abs), { recursive: true });
28157
+ writeFileSync2(abs, newText, "utf8");
27840
28158
  } catch (e) {
27841
28159
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
27842
28160
  return;
27843
28161
  }
27844
- const displayPath = toDisplayPathRelativeToCwd(cwd3, abs);
28162
+ const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
27845
28163
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
27846
28164
  onFileChange?.({ path: displayPath, oldText, newText, patchContent });
27847
28165
  respond(id, null);
@@ -27883,7 +28201,7 @@ async function createCursorAcpClient(options) {
27883
28201
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
27884
28202
  });
27885
28203
  await send("authenticate", { methodId: "cursor_login" });
27886
- const newResult = await send("session/new", { cwd: cwd3, mcpServers: [] });
28204
+ const newResult = await send("session/new", { cwd, mcpServers: [] });
27887
28205
  const sessionId = newResult?.sessionId ?? "";
27888
28206
  if (!sessionId) throw new Error("Cursor ACP session/new did not return sessionId");
27889
28207
  resolve14({
@@ -27955,20 +28273,20 @@ function resolveAgentCommand(preferredAgentType) {
27955
28273
 
27956
28274
  // src/acp/session-file-change-path-kind.ts
27957
28275
  import { execFileSync as execFileSync3 } from "node:child_process";
27958
- import { existsSync, statSync } from "node:fs";
28276
+ import { existsSync, statSync as statSync2 } from "node:fs";
27959
28277
 
27960
28278
  // src/git/get-git-repo-root-sync.ts
27961
28279
  import { execFileSync } from "node:child_process";
27962
- import * as path5 from "node:path";
28280
+ import * as path7 from "node:path";
27963
28281
  function getGitRepoRootSync(startDir) {
27964
28282
  try {
27965
28283
  const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
27966
- cwd: path5.resolve(startDir),
28284
+ cwd: path7.resolve(startDir),
27967
28285
  encoding: "utf8",
27968
28286
  stdio: ["ignore", "pipe", "ignore"],
27969
28287
  maxBuffer: 1024 * 1024
27970
28288
  }).trim();
27971
- return out ? path5.resolve(out) : null;
28289
+ return out ? path7.resolve(out) : null;
27972
28290
  } catch {
27973
28291
  return null;
27974
28292
  }
@@ -27976,65 +28294,65 @@ function getGitRepoRootSync(startDir) {
27976
28294
 
27977
28295
  // src/acp/workspace-files.ts
27978
28296
  import { execFileSync as execFileSync2 } from "node:child_process";
27979
- import { readFileSync as readFileSync2 } from "node:fs";
27980
- import * as path6 from "node:path";
27981
- function resolveWorkspaceFilePath(cwd3, rawPath) {
28297
+ import { readFileSync as readFileSync4 } from "node:fs";
28298
+ import * as path8 from "node:path";
28299
+ function resolveWorkspaceFilePath(cwd, rawPath) {
27982
28300
  const trimmed2 = rawPath.trim();
27983
28301
  if (!trimmed2) return null;
27984
- const normalizedCwd = path6.resolve(cwd3);
27985
- let abs = resolveSafePathUnderCwd(cwd3, trimmed2);
28302
+ const normalizedCwd = path8.resolve(cwd);
28303
+ let abs = resolveSafePathUnderCwd(cwd, trimmed2);
27986
28304
  if (!abs) {
27987
- const candidate = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.normalize(path6.resolve(normalizedCwd, trimmed2));
27988
- const gitRoot2 = getGitRepoRootSync(cwd3);
28305
+ const candidate = path8.isAbsolute(trimmed2) ? path8.normalize(trimmed2) : path8.normalize(path8.resolve(normalizedCwd, trimmed2));
28306
+ const gitRoot2 = getGitRepoRootSync(cwd);
27989
28307
  if (!gitRoot2) return null;
27990
- const rel = path6.relative(gitRoot2, candidate);
27991
- if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
28308
+ const rel = path8.relative(gitRoot2, candidate);
28309
+ if (rel.startsWith("..") || path8.isAbsolute(rel)) return null;
27992
28310
  abs = candidate;
27993
28311
  }
27994
- const gitRoot = getGitRepoRootSync(cwd3);
28312
+ const gitRoot = getGitRepoRootSync(cwd);
27995
28313
  if (gitRoot) {
27996
- const relFromRoot = path6.relative(gitRoot, abs);
27997
- if (!relFromRoot.startsWith("..") && !path6.isAbsolute(relFromRoot)) {
27998
- return { abs, display: relFromRoot.split(path6.sep).join("/") };
28314
+ const relFromRoot = path8.relative(gitRoot, abs);
28315
+ if (!relFromRoot.startsWith("..") && !path8.isAbsolute(relFromRoot)) {
28316
+ return { abs, display: relFromRoot.split(path8.sep).join("/") };
27999
28317
  }
28000
28318
  }
28001
- return { abs, display: toDisplayPathRelativeToCwd(cwd3, abs) };
28319
+ return { abs, display: toDisplayPathRelativeToCwd(cwd, abs) };
28002
28320
  }
28003
- function readUtf8WorkspaceFile(cwd3, displayPath) {
28321
+ function readUtf8WorkspaceFile(cwd, displayPath) {
28004
28322
  if (!displayPath || displayPath.includes("..")) return "";
28005
- const gitRoot = getGitRepoRootSync(cwd3);
28323
+ const gitRoot = getGitRepoRootSync(cwd);
28006
28324
  if (gitRoot) {
28007
- const abs2 = path6.resolve(gitRoot, displayPath);
28008
- const rel = path6.relative(gitRoot, abs2);
28009
- if (!rel.startsWith("..") && !path6.isAbsolute(rel)) {
28325
+ const abs2 = path8.resolve(gitRoot, displayPath);
28326
+ const rel = path8.relative(gitRoot, abs2);
28327
+ if (!rel.startsWith("..") && !path8.isAbsolute(rel)) {
28010
28328
  try {
28011
- return readFileSync2(abs2, "utf8");
28329
+ return readFileSync4(abs2, "utf8");
28012
28330
  } catch {
28013
28331
  }
28014
28332
  }
28015
28333
  }
28016
- const abs = resolveSafePathUnderCwd(cwd3, displayPath);
28334
+ const abs = resolveSafePathUnderCwd(cwd, displayPath);
28017
28335
  if (!abs) return "";
28018
28336
  try {
28019
- return readFileSync2(abs, "utf8");
28337
+ return readFileSync4(abs, "utf8");
28020
28338
  } catch {
28021
28339
  return "";
28022
28340
  }
28023
28341
  }
28024
- function tryWorkspaceDisplayToAbs(cwd3, displayPath) {
28342
+ function tryWorkspaceDisplayToAbs(cwd, displayPath) {
28025
28343
  if (!displayPath || displayPath.includes("..")) return null;
28026
- const gitRoot = getGitRepoRootSync(cwd3);
28344
+ const gitRoot = getGitRepoRootSync(cwd);
28027
28345
  if (gitRoot) {
28028
- const abs = path6.resolve(gitRoot, displayPath);
28029
- const rel = path6.relative(gitRoot, abs);
28030
- if (!rel.startsWith("..") && !path6.isAbsolute(rel)) return abs;
28346
+ const abs = path8.resolve(gitRoot, displayPath);
28347
+ const rel = path8.relative(gitRoot, abs);
28348
+ if (!rel.startsWith("..") && !path8.isAbsolute(rel)) return abs;
28031
28349
  }
28032
- return resolveSafePathUnderCwd(cwd3, displayPath);
28350
+ return resolveSafePathUnderCwd(cwd, displayPath);
28033
28351
  }
28034
- function readGitHeadBlob(cwd3, displayPath) {
28352
+ function readGitHeadBlob(cwd, displayPath) {
28035
28353
  if (!displayPath || displayPath.includes("..")) return "";
28036
- const gitRoot = getGitRepoRootSync(cwd3);
28037
- const execCwd = gitRoot ?? cwd3;
28354
+ const gitRoot = getGitRepoRootSync(cwd);
28355
+ const execCwd = gitRoot ?? cwd;
28038
28356
  try {
28039
28357
  return execFileSync2("git", ["show", `HEAD:${displayPath}`], {
28040
28358
  cwd: execCwd,
@@ -28047,9 +28365,9 @@ function readGitHeadBlob(cwd3, displayPath) {
28047
28365
  }
28048
28366
 
28049
28367
  // src/acp/session-file-change-path-kind.ts
28050
- function gitHeadPathObjectType(cwd3, displayPath) {
28368
+ function gitHeadPathObjectType(cwd, displayPath) {
28051
28369
  if (!displayPath || displayPath.includes("..")) return null;
28052
- const gitRoot = getGitRepoRootSync(cwd3);
28370
+ const gitRoot = getGitRepoRootSync(cwd);
28053
28371
  if (!gitRoot) return null;
28054
28372
  try {
28055
28373
  return execFileSync3("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
@@ -28060,11 +28378,11 @@ function gitHeadPathObjectType(cwd3, displayPath) {
28060
28378
  return null;
28061
28379
  }
28062
28380
  }
28063
- function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
28064
- const abs = tryWorkspaceDisplayToAbs(cwd3, displayPath);
28381
+ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
28382
+ const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
28065
28383
  if (abs && existsSync(abs)) {
28066
28384
  try {
28067
- if (statSync(abs).isDirectory()) {
28385
+ if (statSync2(abs).isDirectory()) {
28068
28386
  return { isDirectory: true, directoryRemoved: false };
28069
28387
  }
28070
28388
  return { isDirectory: false, directoryRemoved: false };
@@ -28072,7 +28390,7 @@ function getSessionFileChangeDirectoryFlags(cwd3, displayPath) {
28072
28390
  return { isDirectory: false, directoryRemoved: false };
28073
28391
  }
28074
28392
  }
28075
- if (gitHeadPathObjectType(cwd3, displayPath) === "tree") {
28393
+ if (gitHeadPathObjectType(cwd, displayPath) === "tree") {
28076
28394
  return { isDirectory: true, directoryRemoved: true };
28077
28395
  }
28078
28396
  return { isDirectory: false, directoryRemoved: false };
@@ -28086,11 +28404,13 @@ function createBridgeOnFileChange(opts) {
28086
28404
  const sessionId = routing.sessionId;
28087
28405
  const send = getSendSessionUpdate();
28088
28406
  if (!send || !runId || !sessionId) {
28089
- log2(`[Bridge service] file change not forwarded path=${evt.path} (session/run not wired)`);
28407
+ log2(
28408
+ `[Bridge service] File change not forwarded (${evt.path}): session or run not wired to the bridge.`
28409
+ );
28090
28410
  return;
28091
28411
  }
28092
- const cwd3 = process.cwd();
28093
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, evt.path);
28412
+ const cwd = getBridgeWorkspaceDirectory();
28413
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
28094
28414
  try {
28095
28415
  send({
28096
28416
  type: "session_file_change",
@@ -28104,7 +28424,7 @@ function createBridgeOnFileChange(opts) {
28104
28424
  directoryRemoved: dirFlags.directoryRemoved
28105
28425
  });
28106
28426
  } catch (err) {
28107
- log2(`[Bridge service] session_file_change failed ${evt.path}: ${errorMessage(err)}`);
28427
+ log2(`[Bridge service] Session file change failed for ${evt.path}: ${errorMessage(err)}`);
28108
28428
  }
28109
28429
  };
28110
28430
  }
@@ -28131,7 +28451,9 @@ function createBridgeOnRequest(opts) {
28131
28451
  }
28132
28452
  });
28133
28453
  } catch (err) {
28134
- log2(`[Bridge service] ACP request forward failed (${request.method}): ${errorMessage(err)}`);
28454
+ log2(
28455
+ `[Bridge service] Agent protocol request forward failed (${request.method}): ${errorMessage(err)}`
28456
+ );
28135
28457
  }
28136
28458
  };
28137
28459
  }
@@ -28163,12 +28485,12 @@ function extractDiffPath(o) {
28163
28485
  }
28164
28486
 
28165
28487
  // src/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
28166
- function pushDiffIfComplete(o, cwd3, out) {
28488
+ function pushDiffIfComplete(o, cwd, out) {
28167
28489
  const t = o.type;
28168
28490
  if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
28169
28491
  const rawPath = extractDiffPath(o);
28170
28492
  if (!rawPath) return;
28171
- const resolved = resolveWorkspaceFilePath(cwd3, rawPath);
28493
+ const resolved = resolveWorkspaceFilePath(cwd, rawPath);
28172
28494
  if (!resolved) return;
28173
28495
  const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
28174
28496
  const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
@@ -28187,17 +28509,17 @@ var NEST_KEYS = [
28187
28509
  "data",
28188
28510
  "arguments"
28189
28511
  ];
28190
- function walkValue(value, cwd3, depth, out) {
28512
+ function walkValue(value, cwd, depth, out) {
28191
28513
  if (depth > 12 || value == null) return;
28192
28514
  if (Array.isArray(value)) {
28193
- for (const x of value) walkValue(x, cwd3, depth + 1, out);
28515
+ for (const x of value) walkValue(x, cwd, depth + 1, out);
28194
28516
  return;
28195
28517
  }
28196
28518
  if (typeof value !== "object") return;
28197
28519
  const o = value;
28198
- pushDiffIfComplete(o, cwd3, out);
28520
+ pushDiffIfComplete(o, cwd, out);
28199
28521
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
28200
- walkValue(o.content, cwd3, depth + 1, out);
28522
+ walkValue(o.content, cwd, depth + 1, out);
28201
28523
  }
28202
28524
  for (const k of NEST_KEYS) {
28203
28525
  if (o.type === "content" && k === "content") continue;
@@ -28205,23 +28527,23 @@ function walkValue(value, cwd3, depth, out) {
28205
28527
  if (v == null) continue;
28206
28528
  if (k === "arguments" && typeof v === "string") {
28207
28529
  try {
28208
- walkValue(JSON.parse(v), cwd3, depth + 1, out);
28530
+ walkValue(JSON.parse(v), cwd, depth + 1, out);
28209
28531
  } catch {
28210
28532
  }
28211
28533
  continue;
28212
28534
  }
28213
- walkValue(v, cwd3, depth + 1, out);
28535
+ walkValue(v, cwd, depth + 1, out);
28214
28536
  }
28215
28537
  }
28216
28538
 
28217
28539
  // src/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
28218
- function extractAcpFileDiffsFromUpdate(update, cwd3) {
28540
+ function extractAcpFileDiffsFromUpdate(update, cwd) {
28219
28541
  if (!update || typeof update !== "object") return [];
28220
28542
  const u = update;
28221
28543
  const out = [];
28222
28544
  const content = u.content;
28223
28545
  if (Array.isArray(content)) {
28224
- for (const x of content) walkValue(x, cwd3, 0, out);
28546
+ for (const x of content) walkValue(x, cwd, 0, out);
28225
28547
  }
28226
28548
  const byPath = /* @__PURE__ */ new Map();
28227
28549
  for (const d of out) byPath.set(d.path, d);
@@ -28229,18 +28551,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd3) {
28229
28551
  }
28230
28552
 
28231
28553
  // src/acp/hooks/extract-tool-target-paths.ts
28232
- function addPath(cwd3, raw, out) {
28554
+ function addPath(cwd, raw, out) {
28233
28555
  if (typeof raw !== "string") return;
28234
28556
  const trimmed2 = raw.trim();
28235
28557
  if (!trimmed2) return;
28236
- const resolved = resolveWorkspaceFilePath(cwd3, trimmed2);
28558
+ const resolved = resolveWorkspaceFilePath(cwd, trimmed2);
28237
28559
  if (!resolved) return;
28238
28560
  out.add(resolved.display);
28239
28561
  }
28240
- function walkLocations(cwd3, loc, out) {
28562
+ function walkLocations(cwd, loc, out) {
28241
28563
  if (!Array.isArray(loc)) return;
28242
28564
  for (const item of loc) {
28243
- if (item && typeof item === "object") addPath(cwd3, item.path, out);
28565
+ if (item && typeof item === "object") addPath(cwd, item.path, out);
28244
28566
  }
28245
28567
  }
28246
28568
  var PATH_KEYS = [
@@ -28253,56 +28575,56 @@ var PATH_KEYS = [
28253
28575
  "file_path",
28254
28576
  "target_file"
28255
28577
  ];
28256
- function collectFromObject(cwd3, obj, out, depth) {
28578
+ function collectFromObject(cwd, obj, out, depth) {
28257
28579
  if (depth > 10) return;
28258
28580
  for (const k of PATH_KEYS) {
28259
- if (k in obj) addPath(cwd3, obj[k], out);
28581
+ if (k in obj) addPath(cwd, obj[k], out);
28260
28582
  }
28261
28583
  for (const v of Object.values(obj)) {
28262
- if (v != null && typeof v === "object") collectUnknown(cwd3, v, out, depth + 1);
28584
+ if (v != null && typeof v === "object") collectUnknown(cwd, v, out, depth + 1);
28263
28585
  }
28264
28586
  }
28265
- function collectUnknown(cwd3, v, out, depth) {
28587
+ function collectUnknown(cwd, v, out, depth) {
28266
28588
  if (depth > 10 || v == null) return;
28267
28589
  if (Array.isArray(v)) {
28268
- for (const x of v) collectUnknown(cwd3, x, out, depth + 1);
28590
+ for (const x of v) collectUnknown(cwd, x, out, depth + 1);
28269
28591
  return;
28270
28592
  }
28271
- if (typeof v === "object") collectFromObject(cwd3, v, out, depth);
28593
+ if (typeof v === "object") collectFromObject(cwd, v, out, depth);
28272
28594
  }
28273
- function walkContentArray(cwd3, content, out) {
28595
+ function walkContentArray(cwd, content, out) {
28274
28596
  if (!Array.isArray(content)) return;
28275
28597
  for (const item of content) {
28276
28598
  if (!item || typeof item !== "object") continue;
28277
28599
  const o = item;
28278
28600
  if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
28279
- for (const k of PATH_KEYS) if (k in o) addPath(cwd3, o[k], out);
28601
+ for (const k of PATH_KEYS) if (k in o) addPath(cwd, o[k], out);
28280
28602
  }
28281
28603
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
28282
28604
  const inner = o.content;
28283
28605
  if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
28284
- for (const k of PATH_KEYS) if (k in inner) addPath(cwd3, inner[k], out);
28606
+ for (const k of PATH_KEYS) if (k in inner) addPath(cwd, inner[k], out);
28285
28607
  }
28286
28608
  }
28287
28609
  }
28288
28610
  }
28289
- function extractToolTargetDisplayPaths(update, cwd3) {
28611
+ function extractToolTargetDisplayPaths(update, cwd) {
28290
28612
  const out = /* @__PURE__ */ new Set();
28291
28613
  if (!update || typeof update !== "object") return [];
28292
28614
  const u = update;
28293
- walkLocations(cwd3, u.locations, out);
28294
- walkLocations(cwd3, u.fileLocations, out);
28295
- walkLocations(cwd3, u.file_locations, out);
28615
+ walkLocations(cwd, u.locations, out);
28616
+ walkLocations(cwd, u.fileLocations, out);
28617
+ walkLocations(cwd, u.file_locations, out);
28296
28618
  const tc = u.toolCall ?? u.tool_call;
28297
28619
  if (tc && typeof tc.arguments === "string") {
28298
28620
  try {
28299
28621
  const parsed = JSON.parse(tc.arguments);
28300
- collectUnknown(cwd3, parsed, out, 0);
28622
+ collectUnknown(cwd, parsed, out, 0);
28301
28623
  } catch {
28302
28624
  }
28303
28625
  }
28304
- walkContentArray(cwd3, u.content, out);
28305
- collectFromObject(cwd3, u, out, 0);
28626
+ walkContentArray(cwd, u.content, out);
28627
+ collectFromObject(cwd, u, out, 0);
28306
28628
  return [...out];
28307
28629
  }
28308
28630
 
@@ -28368,7 +28690,7 @@ var PathSnapshotTracker = class {
28368
28690
  this.beforeByToolKey.delete(toolKey);
28369
28691
  this.accumulatedPathsByToolKey.delete(toolKey);
28370
28692
  }
28371
- captureBeforeFromDisk(toolKey, paths, cwd3) {
28693
+ captureBeforeFromDisk(toolKey, paths, cwd) {
28372
28694
  if (paths.length === 0) return;
28373
28695
  let m = this.beforeByToolKey.get(toolKey);
28374
28696
  if (!m) {
@@ -28377,10 +28699,10 @@ var PathSnapshotTracker = class {
28377
28699
  }
28378
28700
  for (const p of paths) {
28379
28701
  if (m.has(p)) continue;
28380
- m.set(p, readUtf8WorkspaceFile(cwd3, p));
28702
+ m.set(p, readUtf8WorkspaceFile(cwd, p));
28381
28703
  }
28382
28704
  }
28383
- ensureBeforeFromHeadForMissing(toolKey, paths, cwd3) {
28705
+ ensureBeforeFromHeadForMissing(toolKey, paths, cwd) {
28384
28706
  let m = this.beforeByToolKey.get(toolKey);
28385
28707
  if (!m) {
28386
28708
  m = /* @__PURE__ */ new Map();
@@ -28388,10 +28710,10 @@ var PathSnapshotTracker = class {
28388
28710
  }
28389
28711
  for (const p of paths) {
28390
28712
  if (m.has(p)) continue;
28391
- m.set(p, readGitHeadBlob(cwd3, p));
28713
+ m.set(p, readGitHeadBlob(cwd, p));
28392
28714
  }
28393
28715
  }
28394
- flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2) {
28716
+ flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
28395
28717
  const t = this.debouncers.get(toolKey);
28396
28718
  if (t) clearTimeout(t);
28397
28719
  this.debouncers.delete(toolKey);
@@ -28400,10 +28722,10 @@ var PathSnapshotTracker = class {
28400
28722
  this.beforeByToolKey.delete(toolKey);
28401
28723
  if (!send || !runId || !sessionId) return;
28402
28724
  for (const [displayPath, oldText] of beforeMap) {
28403
- const newText = readUtf8WorkspaceFile(cwd3, displayPath);
28725
+ const newText = readUtf8WorkspaceFile(cwd, displayPath);
28404
28726
  if (oldText === newText) continue;
28405
28727
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
28406
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, displayPath);
28728
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
28407
28729
  try {
28408
28730
  send({
28409
28731
  type: "session_file_change",
@@ -28418,35 +28740,35 @@ var PathSnapshotTracker = class {
28418
28740
  });
28419
28741
  sentPaths.add(displayPath);
28420
28742
  } catch (err) {
28421
- log2(`[Bridge service] session_file_change failed ${displayPath}: ${errorMessage(err)}`);
28743
+ log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
28422
28744
  }
28423
28745
  }
28424
28746
  }
28425
- scheduleDebouncedFlush(toolKey, cwd3, sentPaths, send, runId, sessionId, log2) {
28747
+ scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
28426
28748
  const prev = this.debouncers.get(toolKey);
28427
28749
  if (prev) clearTimeout(prev);
28428
28750
  this.debouncers.set(
28429
28751
  toolKey,
28430
28752
  setTimeout(() => {
28431
28753
  this.debouncers.delete(toolKey);
28432
- this.flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
28754
+ this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
28433
28755
  }, PATH_SNAPSHOT_DEBOUNCE_MS)
28434
28756
  );
28435
28757
  }
28436
- handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd3, sentPaths, send, runId, sessionId, log2) {
28758
+ handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd, sentPaths, send, runId, sessionId, log2) {
28437
28759
  if (updateKind === "tool_call") {
28438
28760
  this.resetToolSnapshots(toolKey);
28439
28761
  }
28440
28762
  if (updateKind === "tool_call") {
28441
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd3);
28763
+ this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
28442
28764
  } else if (updateKind === "tool_call_update") {
28443
28765
  if (isCompletedToolStatus(status)) {
28444
- this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd3);
28445
- this.flushPathSnapshots(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
28766
+ this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd);
28767
+ this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
28446
28768
  } else {
28447
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd3);
28769
+ this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
28448
28770
  if (this.beforeByToolKey.has(toolKey)) {
28449
- this.scheduleDebouncedFlush(toolKey, cwd3, sentPaths, send, runId, sessionId, log2);
28771
+ this.scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
28450
28772
  }
28451
28773
  }
28452
28774
  }
@@ -28454,11 +28776,11 @@ var PathSnapshotTracker = class {
28454
28776
  };
28455
28777
 
28456
28778
  // src/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
28457
- function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, runId, sentPaths, log2) {
28779
+ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentPaths, log2) {
28458
28780
  for (const d of diffs) {
28459
28781
  try {
28460
28782
  const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
28461
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, d.path);
28783
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, d.path);
28462
28784
  send({
28463
28785
  type: "session_file_change",
28464
28786
  sessionId,
@@ -28472,19 +28794,19 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, ru
28472
28794
  });
28473
28795
  sentPaths.add(d.path);
28474
28796
  } catch (err) {
28475
- log2(`[Bridge service] session_file_change failed ${d.path}: ${errorMessage(err)}`);
28797
+ log2(`[Bridge service] Session file change failed for ${d.path}: ${errorMessage(err)}`);
28476
28798
  }
28477
28799
  }
28478
28800
  }
28479
- function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3, sessionId, runId, log2) {
28801
+ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, sessionId, runId, log2) {
28480
28802
  for (const displayPath of mergedPaths) {
28481
28803
  if (sentPaths.has(displayPath)) continue;
28482
- const oldText = readGitHeadBlob(cwd3, displayPath);
28483
- const newText = readUtf8WorkspaceFile(cwd3, displayPath);
28804
+ const oldText = readGitHeadBlob(cwd, displayPath);
28805
+ const newText = readUtf8WorkspaceFile(cwd, displayPath);
28484
28806
  if (oldText === newText) continue;
28485
28807
  try {
28486
28808
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
28487
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd3, displayPath);
28809
+ const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
28488
28810
  send({
28489
28811
  type: "session_file_change",
28490
28812
  sessionId,
@@ -28498,7 +28820,7 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd3,
28498
28820
  });
28499
28821
  sentPaths.add(displayPath);
28500
28822
  } catch (err) {
28501
- log2(`[Bridge service] session_file_change failed ${displayPath}: ${errorMessage(err)}`);
28823
+ log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
28502
28824
  }
28503
28825
  }
28504
28826
  }
@@ -28511,7 +28833,7 @@ function createBridgeOnSessionUpdate(opts) {
28511
28833
  const runId = routing.runId;
28512
28834
  const sessionId = routing.sessionId;
28513
28835
  pathTracker.onRunIdChanged(runId);
28514
- const cwd3 = process.cwd();
28836
+ const cwd = getBridgeWorkspaceDirectory();
28515
28837
  const send = getSendSessionUpdate();
28516
28838
  const sentFileChangePaths = /* @__PURE__ */ new Set();
28517
28839
  const p = params;
@@ -28519,7 +28841,7 @@ function createBridgeOnSessionUpdate(opts) {
28519
28841
  const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
28520
28842
  const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
28521
28843
  const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
28522
- const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd3) : [];
28844
+ const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd) : [];
28523
28845
  const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
28524
28846
  if (updateKind === "tool_call") {
28525
28847
  pathTracker.resetToolSnapshots(toolKey);
@@ -28534,7 +28856,7 @@ function createBridgeOnSessionUpdate(opts) {
28534
28856
  toolKey,
28535
28857
  toolPaths,
28536
28858
  p.status,
28537
- cwd3,
28859
+ cwd,
28538
28860
  sentFileChangePaths,
28539
28861
  deliver,
28540
28862
  runId ?? "",
@@ -28542,18 +28864,18 @@ function createBridgeOnSessionUpdate(opts) {
28542
28864
  log2
28543
28865
  );
28544
28866
  }
28545
- const diffs = extractAcpFileDiffsFromUpdate(params, cwd3);
28867
+ const diffs = extractAcpFileDiffsFromUpdate(params, cwd);
28546
28868
  if (diffs.length > 0 && send && runId && sessionId) {
28547
- sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd3, sessionId, runId, sentFileChangePaths, log2);
28869
+ sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentFileChangePaths, log2);
28548
28870
  } else if (diffs.length > 0) {
28549
28871
  log2(
28550
- `[Bridge service] ACP file diff(s) not forwarded (${diffs.length}): session/run not wired to bridge`
28872
+ `[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
28551
28873
  );
28552
28874
  }
28553
28875
  if (isCompletedToolCallUpdate && send && runId && sessionId) {
28554
28876
  const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
28555
28877
  const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
28556
- sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd3, sessionId, runId, log2);
28878
+ sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd, sessionId, runId, log2);
28557
28879
  pathTracker.accumulatedPathsByToolKey.delete(toolKey);
28558
28880
  }
28559
28881
  if (runId && send) {
@@ -28566,7 +28888,9 @@ function createBridgeOnSessionUpdate(opts) {
28566
28888
  payload: params
28567
28889
  });
28568
28890
  } catch (err) {
28569
- log2(`[Bridge service] session_update send failed (${updateKind}): ${errorMessage(err)}`);
28891
+ log2(
28892
+ `[Bridge service] Session update send failed (${formatSessionUpdateKindForLog(updateKind)}): ${errorMessage(err)}`
28893
+ );
28570
28894
  }
28571
28895
  }
28572
28896
  };
@@ -28583,11 +28907,11 @@ function buildAcpSessionBridgeHooks(opts) {
28583
28907
 
28584
28908
  // src/acp/ensure-acp-client.ts
28585
28909
  async function ensureAcpClient(options) {
28586
- const { state, preferredAgentType, mode, cwd: cwd3, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
28587
- const targetCwd = path7.resolve(
28588
- cwd3 != null && String(cwd3).trim() !== "" ? String(cwd3).trim() : process.cwd()
28910
+ const { state, preferredAgentType, mode, cwd, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
28911
+ const targetCwd = path9.resolve(
28912
+ cwd != null && String(cwd).trim() !== "" ? String(cwd).trim() : getBridgeWorkspaceDirectory()
28589
28913
  );
28590
- if (state.acpHandle && state.lastAcpCwd != null && path7.resolve(state.lastAcpCwd) !== path7.resolve(targetCwd)) {
28914
+ if (state.acpHandle && state.lastAcpCwd != null && path9.resolve(state.lastAcpCwd) !== path9.resolve(targetCwd)) {
28591
28915
  try {
28592
28916
  state.acpHandle.disconnect();
28593
28917
  } catch {
@@ -28599,7 +28923,7 @@ async function ensureAcpClient(options) {
28599
28923
  const resolved = resolveAgentCommand(preferredAgentType);
28600
28924
  if (!resolved) {
28601
28925
  log2(
28602
- `[agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agentType on prompts or agent_config after identify.`
28926
+ `[Agent] No local agent type (${preferredAgentType === null ? "none" : `"${preferredAgentType}"`}). Send agent type on prompts or agent configuration after identify.`
28603
28927
  );
28604
28928
  state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
28605
28929
  return null;
@@ -28618,15 +28942,15 @@ async function ensureAcpClient(options) {
28618
28942
  if (!state.acpStartPromise) {
28619
28943
  let statOk = false;
28620
28944
  try {
28621
- const st = fs3.statSync(targetCwd);
28945
+ const st = fs5.statSync(targetCwd);
28622
28946
  statOk = st.isDirectory();
28623
28947
  if (!statOk) {
28624
28948
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
28625
- log2(`[agent] ${state.lastAcpStartError}`);
28949
+ log2(`[Agent] ${state.lastAcpStartError}`);
28626
28950
  }
28627
28951
  } catch {
28628
28952
  state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetCwd}`;
28629
- log2(`[agent] ${state.lastAcpStartError}`);
28953
+ log2(`[Agent] ${state.lastAcpStartError}`);
28630
28954
  }
28631
28955
  if (!statOk) {
28632
28956
  return null;
@@ -28651,7 +28975,7 @@ async function ensureAcpClient(options) {
28651
28975
  return h;
28652
28976
  }).catch((err) => {
28653
28977
  state.lastAcpStartError = errorMessage(err);
28654
- log2(`[agent] failed to start: ${state.lastAcpStartError}`);
28978
+ log2(`[Agent] Failed to start: ${state.lastAcpStartError}`);
28655
28979
  state.acpStartPromise = null;
28656
28980
  state.acpAgentKey = null;
28657
28981
  return null;
@@ -28687,10 +29011,9 @@ async function createAcpManager(options) {
28687
29011
  runId,
28688
29012
  mode,
28689
29013
  agentType,
28690
- cwd: cwd3,
29014
+ cwd,
28691
29015
  sendResult,
28692
- sendSessionUpdate,
28693
- collectSessionDiffAfterTurn
29016
+ sendSessionUpdate
28694
29017
  } = opts;
28695
29018
  const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
28696
29019
  pendingCancelRunId = void 0;
@@ -28702,7 +29025,7 @@ async function createAcpManager(options) {
28702
29025
  state,
28703
29026
  preferredAgentType: preferredForPrompt,
28704
29027
  mode,
28705
- cwd: cwd3,
29028
+ cwd,
28706
29029
  routing: promptRouting,
28707
29030
  sendSessionUpdate,
28708
29031
  sendRequest: sendSessionUpdate,
@@ -28743,9 +29066,9 @@ async function createAcpManager(options) {
28743
29066
  promptId,
28744
29067
  sessionId,
28745
29068
  runId,
29069
+ agentCwd: cwd,
28746
29070
  sendResult,
28747
29071
  sendSessionUpdate,
28748
- collectSessionDiffAfterTurn,
28749
29072
  log: log2
28750
29073
  });
28751
29074
  }
@@ -28764,7 +29087,7 @@ async function createAcpManager(options) {
28764
29087
  await handle.cancel();
28765
29088
  return true;
28766
29089
  } catch (err) {
28767
- log2(`[agent] cancel failed: ${err instanceof Error ? err.message : String(err)}`);
29090
+ log2(`[Agent] Cancel failed: ${err instanceof Error ? err.message : String(err)}`);
28768
29091
  return false;
28769
29092
  }
28770
29093
  }
@@ -28801,7 +29124,9 @@ var handleBridgeIdentified = (msg, deps) => {
28801
29124
  try {
28802
29125
  await deps.reportAutoDetectedAgents?.();
28803
29126
  } catch (e) {
28804
- deps.log(`[Bridge service] auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`);
29127
+ deps.log(
29128
+ `[Bridge service] Auto-detect agents failed: ${e instanceof Error ? e.message : String(e)}`
29129
+ );
28805
29130
  }
28806
29131
  })();
28807
29132
  });
@@ -28809,7 +29134,9 @@ var handleBridgeIdentified = (msg, deps) => {
28809
29134
  try {
28810
29135
  deps.sendLocalSkillsReport?.();
28811
29136
  } catch (e) {
28812
- deps.log(`[Bridge service] local skills report failed: ${e instanceof Error ? e.message : String(e)}`);
29137
+ deps.log(
29138
+ `[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
29139
+ );
28813
29140
  }
28814
29141
  });
28815
29142
  };
@@ -28826,12 +29153,12 @@ var handleAgentConfigMessage = (msg, deps) => {
28826
29153
  };
28827
29154
 
28828
29155
  // src/acp/from-bridge/handle-bridge-prompt.ts
28829
- import * as path10 from "node:path";
29156
+ import * as path11 from "node:path";
28830
29157
  import { execFile as execFile3 } from "node:child_process";
28831
29158
  import { promisify as promisify3 } from "node:util";
28832
29159
 
28833
29160
  // src/git/bridge-queue-key.ts
28834
- import * as path8 from "node:path";
29161
+ import * as path10 from "node:path";
28835
29162
  import { createHash } from "node:crypto";
28836
29163
  function normalizeCanonicalGitUrl(url2) {
28837
29164
  let s = url2.trim();
@@ -28859,13 +29186,13 @@ function canonicalUrlToRepoIdSync(url2) {
28859
29186
  return createHash("sha256").update(normalized).digest("hex").slice(0, 32);
28860
29187
  }
28861
29188
  function fallbackRepoIdFromPath(absPath) {
28862
- return createHash("sha256").update(path8.resolve(absPath)).digest("hex").slice(0, 32);
29189
+ return createHash("sha256").update(path10.resolve(absPath)).digest("hex").slice(0, 32);
28863
29190
  }
28864
29191
  async function resolveBridgeQueueBindFields(options) {
28865
29192
  const { effectiveCwd, worktreePaths, primaryRepoRoots, log: log2 } = options;
28866
- const cwdAbs = worktreePaths.length > 0 ? path8.resolve(worktreePaths[0]) : path8.resolve(effectiveCwd);
29193
+ const cwdAbs = worktreePaths.length > 0 ? path10.resolve(worktreePaths[0]) : path10.resolve(effectiveCwd);
28867
29194
  if (!primaryRepoRoots.length) {
28868
- log2("[Bridge service] prompt queue bind skipped: no git repo roots under cwd");
29195
+ log2("[Bridge service] Prompt queue bind skipped: no Git repository roots under the working directory.");
28869
29196
  return null;
28870
29197
  }
28871
29198
  let primaryRoot = primaryRepoRoots[0];
@@ -28885,115 +29212,11 @@ async function resolveBridgeQueueBindFields(options) {
28885
29212
  return { canonicalQueueKey, repoId, cwdAbs };
28886
29213
  }
28887
29214
 
28888
- // src/git/pre-turn-snapshot.ts
28889
- import * as fs4 from "node:fs";
28890
- import * as path9 from "node:path";
28891
- import { execFile as execFile2 } from "node:child_process";
28892
- import { promisify as promisify2 } from "node:util";
28893
- var execFileAsync2 = promisify2(execFile2);
28894
- function snapshotsDirForCwd(agentCwd) {
28895
- return path9.join(agentCwd, ".buildautomaton", "snapshots");
28896
- }
28897
- async function gitStashCreate2(repoRoot, log2) {
28898
- try {
28899
- const { stdout } = await execFileAsync2("git", ["stash", "create"], {
28900
- cwd: repoRoot,
28901
- maxBuffer: 10 * 1024 * 1024
28902
- });
28903
- return stdout.trim();
28904
- } catch (e) {
28905
- log2(`[snapshot] git stash create failed in ${repoRoot}: ${e instanceof Error ? e.message : String(e)}`);
28906
- return "";
28907
- }
28908
- }
28909
- async function gitRun(repoRoot, args, log2, label) {
28910
- try {
28911
- await execFileAsync2("git", args, { cwd: repoRoot, maxBuffer: 10 * 1024 * 1024 });
28912
- return { ok: true };
28913
- } catch (e) {
28914
- const msg = e instanceof Error ? e.message : String(e);
28915
- log2(`[snapshot] git ${label} failed in ${repoRoot}: ${msg}`);
28916
- return { ok: false, error: msg };
28917
- }
28918
- }
28919
- async function resolveSnapshotRepoRoots(options) {
28920
- const { worktreePaths, fallbackCwd, log: log2 } = options;
28921
- if (worktreePaths?.length) {
28922
- const uniq = [...new Set(worktreePaths.map((p) => path9.resolve(p)))];
28923
- return uniq;
28924
- }
28925
- try {
28926
- const repos = await discoverGitReposUnderRoot(fallbackCwd);
28927
- return repos.map((r) => r.absolutePath);
28928
- } catch (e) {
28929
- log2(`[snapshot] discover repos failed: ${e instanceof Error ? e.message : String(e)}`);
28930
- return [];
28931
- }
28932
- }
28933
- async function capturePreTurnSnapshot(options) {
28934
- const { runId, repoRoots, agentCwd, log: log2 } = options;
28935
- if (!runId || !repoRoots.length) {
28936
- return { ok: false, error: "No git repos to snapshot" };
28937
- }
28938
- const repos = [];
28939
- for (const root of repoRoots) {
28940
- const stashSha = await gitStashCreate2(root, log2);
28941
- repos.push({ path: root, stashSha });
28942
- }
28943
- const dir = snapshotsDirForCwd(agentCwd);
28944
- try {
28945
- fs4.mkdirSync(dir, { recursive: true });
28946
- } catch (e) {
28947
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28948
- }
28949
- const payload = {
28950
- runId,
28951
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
28952
- repos
28953
- };
28954
- const filePath = path9.join(dir, `${runId}.json`);
28955
- try {
28956
- fs4.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
28957
- } catch (e) {
28958
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28959
- }
28960
- log2(`[snapshot] Saved pre-turn snapshot ${runId.slice(0, 8)}\u2026 (${repos.length} repo(s)) \u2192 ${filePath}`);
28961
- return { ok: true, filePath, repos };
28962
- }
28963
- async function applyPreTurnSnapshot(filePath, log2) {
28964
- let data;
28965
- try {
28966
- const raw = fs4.readFileSync(filePath, "utf8");
28967
- data = JSON.parse(raw);
28968
- } catch (e) {
28969
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
28970
- }
28971
- if (!Array.isArray(data.repos)) {
28972
- return { ok: false, error: "Invalid snapshot file" };
28973
- }
28974
- for (const r of data.repos) {
28975
- if (!r.path) continue;
28976
- const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
28977
- if (!reset.ok) return reset;
28978
- const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
28979
- if (!clean.ok) return clean;
28980
- if (r.stashSha) {
28981
- const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
28982
- if (!ap.ok) return ap;
28983
- }
28984
- }
28985
- log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
28986
- return { ok: true };
28987
- }
28988
- function snapshotFilePath(agentCwd, runId) {
28989
- return path9.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
28990
- }
28991
-
28992
29215
  // src/acp/from-bridge/handle-bridge-prompt.ts
28993
29216
  var execFileAsync3 = promisify3(execFile3);
28994
- async function readGitBranch(cwd3) {
29217
+ async function readGitBranch(cwd) {
28995
29218
  try {
28996
- const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: cwd3, maxBuffer: 64 * 1024 });
29219
+ const { stdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
28997
29220
  const b = stdout.trim();
28998
29221
  return b || null;
28999
29222
  } catch {
@@ -29006,7 +29229,7 @@ function handleBridgePrompt(msg, deps) {
29006
29229
  const promptText = typeof rawPrompt === "string" ? rawPrompt : rawPrompt != null ? String(rawPrompt) : "";
29007
29230
  if (!promptText.trim()) {
29008
29231
  log2(
29009
- `[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)`
29232
+ `[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).`
29010
29233
  );
29011
29234
  return;
29012
29235
  }
@@ -29015,8 +29238,6 @@ function handleBridgePrompt(msg, deps) {
29015
29238
  const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
29016
29239
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
29017
29240
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
29018
- const sessionGitQueueStart = msg.sessionGitQueueStart === true;
29019
- const collectSessionDiffAfterTurn = msg.collectSessionDiffAfterTurn === true;
29020
29241
  const sendResult = (result) => {
29021
29242
  const s = getWs();
29022
29243
  if (s) sendWsMessage(s, result);
@@ -29024,7 +29245,7 @@ function handleBridgePrompt(msg, deps) {
29024
29245
  const sendSessionUpdate = (payload) => {
29025
29246
  const s = getWs();
29026
29247
  if (!s) {
29027
- log2("[Bridge service] session_update not sent: not connected");
29248
+ log2("[Bridge service] Session update not sent: not connected to the bridge.");
29028
29249
  return;
29029
29250
  }
29030
29251
  const p = payload;
@@ -29032,7 +29253,7 @@ function handleBridgePrompt(msg, deps) {
29032
29253
  };
29033
29254
  async function preambleAndPrompt(resolvedCwd) {
29034
29255
  const s = getWs();
29035
- const effectiveCwd = path10.resolve(resolvedCwd ?? process.cwd());
29256
+ const effectiveCwd = path11.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
29036
29257
  const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
29037
29258
  const repoRoots = await resolveSnapshotRepoRoots({
29038
29259
  worktreePaths,
@@ -29065,9 +29286,6 @@ function handleBridgePrompt(msg, deps) {
29065
29286
  agentUsesWorktree: sessionWorktreeManager.usesWorktreeSession(sessionId)
29066
29287
  });
29067
29288
  }
29068
- if (sessionGitQueueStart && sessionId && repoRoots.length > 0) {
29069
- await captureSessionGitBoundary({ sessionId, repoRoots, log: log2 });
29070
- }
29071
29289
  if (s && sessionId && runId) {
29072
29290
  const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
29073
29291
  sendWsMessage(s, {
@@ -29086,12 +29304,11 @@ function handleBridgePrompt(msg, deps) {
29086
29304
  agentType,
29087
29305
  cwd: effectiveCwd,
29088
29306
  sendResult,
29089
- sendSessionUpdate,
29090
- collectSessionDiffAfterTurn
29307
+ sendSessionUpdate
29091
29308
  });
29092
29309
  }
29093
- void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd3) => preambleAndPrompt(cwd3)).catch((err) => {
29094
- log2(`[agent] worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
29310
+ void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionWorktreesEnabled }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
29311
+ log2(`[Agent] Worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
29095
29312
  void preambleAndPrompt(void 0);
29096
29313
  });
29097
29314
  }
@@ -29108,7 +29325,7 @@ function handleBridgeCancelRun(msg, { log: log2, acpManager }) {
29108
29325
  void acpManager.cancelRun(runId).then((sent) => {
29109
29326
  if (!sent) {
29110
29327
  log2(
29111
- `[agent] cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available)`
29328
+ `[Agent] Cancel ignored for run ${runId.slice(0, 8)}\u2026 (no active run or cancel not available).`
29112
29329
  );
29113
29330
  }
29114
29331
  });
@@ -29136,7 +29353,7 @@ function handleSkillCall(msg, socket, log2) {
29136
29353
  sendWsMessage(socket, { type: "skill_result", id: msg.id, result });
29137
29354
  }).catch((err) => {
29138
29355
  sendWsMessage(socket, { type: "skill_result", id: msg.id, error: String(err) });
29139
- log2(`[Bridge service] skill_call failed (${msg.skillId}/${msg.operationId}): ${err}`);
29356
+ log2(`[Bridge service] Skill invocation failed (${msg.skillId}/${msg.operationId}): ${err}`);
29140
29357
  });
29141
29358
  }
29142
29359
 
@@ -29153,36 +29370,35 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
29153
29370
  };
29154
29371
 
29155
29372
  // src/files/list-dir.ts
29156
- import fs5 from "node:fs";
29157
- import path12 from "node:path";
29373
+ import fs6 from "node:fs";
29374
+ import path13 from "node:path";
29158
29375
 
29159
29376
  // src/files/ensure-under-cwd.ts
29160
- import path11 from "node:path";
29161
- function ensureUnderCwd(relativePath, cwd3 = process.cwd()) {
29162
- const normalized = path11.normalize(relativePath).replace(/^(\.\/)+/, "");
29163
- const resolved = path11.resolve(cwd3, normalized);
29164
- if (!resolved.startsWith(cwd3 + path11.sep) && resolved !== cwd3) {
29377
+ import path12 from "node:path";
29378
+ function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
29379
+ const normalized = path12.normalize(relativePath).replace(/^(\.\/)+/, "");
29380
+ const resolved = path12.resolve(cwd, normalized);
29381
+ if (!resolved.startsWith(cwd + path12.sep) && resolved !== cwd) {
29165
29382
  return null;
29166
29383
  }
29167
29384
  return resolved;
29168
29385
  }
29169
29386
 
29170
29387
  // src/files/list-dir.ts
29171
- var cwd = process.cwd();
29172
29388
  function listDir(relativePath) {
29173
- const resolved = ensureUnderCwd(relativePath || ".", cwd);
29389
+ const resolved = ensureUnderCwd(relativePath || ".", getBridgeWorkspaceDirectory());
29174
29390
  if (!resolved) {
29175
29391
  return { error: "Path is outside working directory" };
29176
29392
  }
29177
29393
  try {
29178
- const names = fs5.readdirSync(resolved, { withFileTypes: true });
29394
+ const names = fs6.readdirSync(resolved, { withFileTypes: true });
29179
29395
  const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
29180
- const entryPath = path12.join(relativePath || ".", d.name).replace(/\\/g, "/");
29181
- const fullPath = path12.join(resolved, d.name);
29396
+ const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
29397
+ const fullPath = path13.join(resolved, d.name);
29182
29398
  let isDir = d.isDirectory();
29183
29399
  if (d.isSymbolicLink()) {
29184
29400
  try {
29185
- const targetStat = fs5.statSync(fullPath);
29401
+ const targetStat = fs6.statSync(fullPath);
29186
29402
  isDir = targetStat.isDirectory();
29187
29403
  } catch {
29188
29404
  isDir = false;
@@ -29206,26 +29422,25 @@ function listDir(relativePath) {
29206
29422
  }
29207
29423
 
29208
29424
  // src/files/read-file.ts
29209
- import fs6 from "node:fs";
29425
+ import fs7 from "node:fs";
29210
29426
  import { StringDecoder } from "node:string_decoder";
29211
- var cwd2 = process.cwd();
29212
29427
  function resolveFilePath(relativePath) {
29213
- const resolved = ensureUnderCwd(relativePath, cwd2);
29428
+ const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
29214
29429
  if (!resolved) return { error: "Path is outside working directory" };
29215
29430
  let real;
29216
29431
  try {
29217
- real = fs6.realpathSync(resolved);
29432
+ real = fs7.realpathSync(resolved);
29218
29433
  } catch {
29219
29434
  real = resolved;
29220
29435
  }
29221
- const stat = fs6.statSync(real);
29436
+ const stat = fs7.statSync(real);
29222
29437
  if (!stat.isFile()) return { error: "Not a file" };
29223
29438
  return real;
29224
29439
  }
29225
29440
  var LINE_CHUNK_SIZE = 64 * 1024;
29226
29441
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
29227
- const fileSize = fs6.statSync(filePath).size;
29228
- const fd = fs6.openSync(filePath, "r");
29442
+ const fileSize = fs7.statSync(filePath).size;
29443
+ const fd = fs7.openSync(filePath, "r");
29229
29444
  const bufSize = 64 * 1024;
29230
29445
  const buf = Buffer.alloc(bufSize);
29231
29446
  const decoder = new StringDecoder("utf8");
@@ -29238,7 +29453,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29238
29453
  let line0Accum = "";
29239
29454
  try {
29240
29455
  let bytesRead;
29241
- while (!done && (bytesRead = fs6.readSync(fd, buf, 0, bufSize, null)) > 0) {
29456
+ while (!done && (bytesRead = fs7.readSync(fd, buf, 0, bufSize, null)) > 0) {
29242
29457
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
29243
29458
  partial2 = "";
29244
29459
  let lineStart = 0;
@@ -29373,7 +29588,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29373
29588
  }
29374
29589
  return { content: resultLines.join("\n"), size: fileSize };
29375
29590
  } finally {
29376
- fs6.closeSync(fd);
29591
+ fs7.closeSync(fd);
29377
29592
  }
29378
29593
  }
29379
29594
  function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
@@ -29384,8 +29599,8 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
29384
29599
  if (hasRange) {
29385
29600
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
29386
29601
  }
29387
- const stat = fs6.statSync(result);
29388
- const raw = fs6.readFileSync(result, "utf8");
29602
+ const stat = fs7.statSync(result);
29603
+ const raw = fs7.readFileSync(result, "utf8");
29389
29604
  const lines = raw.split(/\r?\n/);
29390
29605
  return { content: raw, totalLines: lines.length, size: stat.size };
29391
29606
  } catch (err) {
@@ -29394,16 +29609,16 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
29394
29609
  }
29395
29610
 
29396
29611
  // src/files/file-index.ts
29397
- import fs7 from "node:fs";
29398
- import path13 from "node:path";
29612
+ import fs8 from "node:fs";
29613
+ import path14 from "node:path";
29399
29614
  import os2 from "node:os";
29400
29615
  import crypto2 from "node:crypto";
29401
- var INDEX_DIR = path13.join(os2.homedir(), ".buildautomaton");
29616
+ var INDEX_DIR = path14.join(os2.homedir(), ".buildautomaton");
29402
29617
  var HASH_LEN = 16;
29403
29618
  var INDEX_VERSION = 2;
29404
- function getIndexPath(cwd3) {
29405
- const hash = crypto2.createHash("sha256").update(cwd3).digest("hex").slice(0, HASH_LEN);
29406
- return path13.join(INDEX_DIR, `.file-index-${hash}.json`);
29619
+ function getIndexPath(cwd) {
29620
+ const hash = crypto2.createHash("sha256").update(cwd).digest("hex").slice(0, HASH_LEN);
29621
+ return path14.join(INDEX_DIR, `.file-index-${hash}.json`);
29407
29622
  }
29408
29623
  function getTrigrams(s) {
29409
29624
  const lower = s.toLowerCase();
@@ -29441,20 +29656,20 @@ function binarySearch(arr, x) {
29441
29656
  function walkDir(dir, baseDir, out) {
29442
29657
  let names;
29443
29658
  try {
29444
- names = fs7.readdirSync(dir);
29659
+ names = fs8.readdirSync(dir);
29445
29660
  } catch {
29446
29661
  return;
29447
29662
  }
29448
29663
  for (const name of names) {
29449
29664
  if (name.startsWith(".")) continue;
29450
- const full = path13.join(dir, name);
29665
+ const full = path14.join(dir, name);
29451
29666
  let stat;
29452
29667
  try {
29453
- stat = fs7.statSync(full);
29668
+ stat = fs8.statSync(full);
29454
29669
  } catch {
29455
29670
  continue;
29456
29671
  }
29457
- const relative4 = path13.relative(baseDir, full).replace(/\\/g, "/");
29672
+ const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
29458
29673
  if (stat.isDirectory()) {
29459
29674
  walkDir(full, baseDir, out);
29460
29675
  } else if (stat.isFile()) {
@@ -29462,8 +29677,8 @@ function walkDir(dir, baseDir, out) {
29462
29677
  }
29463
29678
  }
29464
29679
  }
29465
- function buildFileIndex(cwd3) {
29466
- const resolved = path13.resolve(cwd3);
29680
+ function buildFileIndex(cwd) {
29681
+ const resolved = path14.resolve(cwd);
29467
29682
  const paths = [];
29468
29683
  walkDir(resolved, resolved, paths);
29469
29684
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
@@ -29481,18 +29696,18 @@ function buildFileIndex(cwd3) {
29481
29696
  const data = { version: INDEX_VERSION, paths, trigramIndex };
29482
29697
  const indexPath = getIndexPath(resolved);
29483
29698
  try {
29484
- if (!fs7.existsSync(INDEX_DIR)) fs7.mkdirSync(INDEX_DIR, { recursive: true });
29485
- fs7.writeFileSync(indexPath, JSON.stringify(data), "utf8");
29699
+ if (!fs8.existsSync(INDEX_DIR)) fs8.mkdirSync(INDEX_DIR, { recursive: true });
29700
+ fs8.writeFileSync(indexPath, JSON.stringify(data), "utf8");
29486
29701
  } catch (e) {
29487
29702
  console.error("[file-index] Failed to write index:", e);
29488
29703
  }
29489
29704
  return data;
29490
29705
  }
29491
- function loadFileIndex(cwd3) {
29492
- const resolved = path13.resolve(cwd3);
29706
+ function loadFileIndex(cwd) {
29707
+ const resolved = path14.resolve(cwd);
29493
29708
  const indexPath = getIndexPath(resolved);
29494
29709
  try {
29495
- const raw = fs7.readFileSync(indexPath, "utf8");
29710
+ const raw = fs8.readFileSync(indexPath, "utf8");
29496
29711
  const parsed = JSON.parse(raw);
29497
29712
  if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
29498
29713
  const obj = parsed;
@@ -29509,8 +29724,8 @@ function loadFileIndex(cwd3) {
29509
29724
  return null;
29510
29725
  }
29511
29726
  }
29512
- function ensureFileIndex(cwd3) {
29513
- const resolved = path13.resolve(cwd3);
29727
+ function ensureFileIndex(cwd) {
29728
+ const resolved = path14.resolve(cwd);
29514
29729
  const cached2 = loadFileIndex(resolved);
29515
29730
  if (cached2 !== null) return { data: cached2, fromCache: true };
29516
29731
  const data = buildFileIndex(resolved);
@@ -29548,8 +29763,8 @@ function searchFileIndex(index, query, limit = 100) {
29548
29763
  var SEARCH_LIMIT = 100;
29549
29764
  function handleFileBrowserSearch(msg, socket) {
29550
29765
  const q = typeof msg.q === "string" ? msg.q : "";
29551
- const cwd3 = process.cwd();
29552
- const index = loadFileIndex(cwd3);
29766
+ const cwd = getBridgeWorkspaceDirectory();
29767
+ const index = loadFileIndex(cwd);
29553
29768
  if (index === null) {
29554
29769
  sendWsMessage(socket, {
29555
29770
  type: "file_browser_search_response",
@@ -29570,7 +29785,7 @@ function handleFileBrowserSearch(msg, socket) {
29570
29785
  function triggerFileIndexBuild() {
29571
29786
  setImmediate(() => {
29572
29787
  try {
29573
- ensureFileIndex(process.cwd());
29788
+ ensureFileIndex(getBridgeWorkspaceDirectory());
29574
29789
  } catch (e) {
29575
29790
  console.error("[file-index] Background build failed:", e);
29576
29791
  }
@@ -29631,30 +29846,30 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
29631
29846
  }
29632
29847
 
29633
29848
  // src/skills/discover-local-agent-skills.ts
29634
- import fs8 from "node:fs";
29635
- import path14 from "node:path";
29849
+ import fs9 from "node:fs";
29850
+ import path15 from "node:path";
29636
29851
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
29637
- function discoverLocalSkills(cwd3) {
29852
+ function discoverLocalSkills(cwd) {
29638
29853
  const out = [];
29639
29854
  const seenKeys = /* @__PURE__ */ new Set();
29640
29855
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29641
- const base = path14.join(cwd3, rel);
29642
- if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29856
+ const base = path15.join(cwd, rel);
29857
+ if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
29643
29858
  let entries = [];
29644
29859
  try {
29645
- entries = fs8.readdirSync(base);
29860
+ entries = fs9.readdirSync(base);
29646
29861
  } catch {
29647
29862
  continue;
29648
29863
  }
29649
29864
  for (const name of entries) {
29650
- const dir = path14.join(base, name);
29865
+ const dir = path15.join(base, name);
29651
29866
  try {
29652
- if (!fs8.statSync(dir).isDirectory()) continue;
29867
+ if (!fs9.statSync(dir).isDirectory()) continue;
29653
29868
  } catch {
29654
29869
  continue;
29655
29870
  }
29656
- const skillMd = path14.join(dir, "SKILL.md");
29657
- if (!fs8.existsSync(skillMd)) continue;
29871
+ const skillMd = path15.join(dir, "SKILL.md");
29872
+ if (!fs9.existsSync(skillMd)) continue;
29658
29873
  const key = `${rel}/${name}`;
29659
29874
  if (seenKeys.has(key)) continue;
29660
29875
  seenKeys.add(key);
@@ -29663,26 +29878,26 @@ function discoverLocalSkills(cwd3) {
29663
29878
  }
29664
29879
  return out;
29665
29880
  }
29666
- function discoverSkillLayoutRoots(cwd3) {
29881
+ function discoverSkillLayoutRoots(cwd) {
29667
29882
  const roots = [];
29668
29883
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29669
- const base = path14.join(cwd3, rel);
29670
- if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29884
+ const base = path15.join(cwd, rel);
29885
+ if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
29671
29886
  let entries = [];
29672
29887
  try {
29673
- entries = fs8.readdirSync(base);
29888
+ entries = fs9.readdirSync(base);
29674
29889
  } catch {
29675
29890
  continue;
29676
29891
  }
29677
29892
  const skills2 = [];
29678
29893
  for (const name of entries) {
29679
- const dir = path14.join(base, name);
29894
+ const dir = path15.join(base, name);
29680
29895
  try {
29681
- if (!fs8.statSync(dir).isDirectory()) continue;
29896
+ if (!fs9.statSync(dir).isDirectory()) continue;
29682
29897
  } catch {
29683
29898
  continue;
29684
29899
  }
29685
- if (!fs8.existsSync(path14.join(dir, "SKILL.md"))) continue;
29900
+ if (!fs9.existsSync(path15.join(dir, "SKILL.md"))) continue;
29686
29901
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
29687
29902
  skills2.push({ name, relPath });
29688
29903
  }
@@ -29697,14 +29912,14 @@ function discoverSkillLayoutRoots(cwd3) {
29697
29912
  function handleSkillLayoutRequest(msg, deps) {
29698
29913
  const socket = deps.getWs();
29699
29914
  const id = typeof msg.id === "string" ? msg.id : "";
29700
- const roots = discoverSkillLayoutRoots(process.cwd());
29915
+ const roots = discoverSkillLayoutRoots(getBridgeWorkspaceDirectory());
29701
29916
  socket?.send(JSON.stringify({ type: "skill_layout_response", id, roots }));
29702
29917
  }
29703
29918
 
29704
29919
  // src/skills/install-remote-skills.ts
29705
- import fs9 from "node:fs";
29706
- import path15 from "node:path";
29707
- function installRemoteSkills(cwd3, targetDir, items) {
29920
+ import fs10 from "node:fs";
29921
+ import path16 from "node:path";
29922
+ function installRemoteSkills(cwd, targetDir, items) {
29708
29923
  const installed = [];
29709
29924
  if (!Array.isArray(items)) {
29710
29925
  return { success: false, error: "Invalid items" };
@@ -29714,15 +29929,15 @@ function installRemoteSkills(cwd3, targetDir, items) {
29714
29929
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
29715
29930
  continue;
29716
29931
  }
29717
- const skillDir = path15.join(cwd3, targetDir, item.skillName);
29932
+ const skillDir = path16.join(cwd, targetDir, item.skillName);
29718
29933
  for (const f of item.files) {
29719
29934
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
29720
- const dest = path15.join(skillDir, f.path);
29721
- fs9.mkdirSync(path15.dirname(dest), { recursive: true });
29935
+ const dest = path16.join(skillDir, f.path);
29936
+ fs10.mkdirSync(path16.dirname(dest), { recursive: true });
29722
29937
  if (f.text !== void 0) {
29723
- fs9.writeFileSync(dest, f.text, "utf8");
29938
+ fs10.writeFileSync(dest, f.text, "utf8");
29724
29939
  } else if (f.base64) {
29725
- fs9.writeFileSync(dest, Buffer.from(f.base64, "base64"));
29940
+ fs10.writeFileSync(dest, Buffer.from(f.base64, "base64"));
29726
29941
  }
29727
29942
  }
29728
29943
  installed.push({
@@ -29743,11 +29958,11 @@ var handleInstallSkillsMessage = (msg, deps) => {
29743
29958
  const id = typeof msg.id === "string" ? msg.id : "";
29744
29959
  const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
29745
29960
  const rawItems = msg.items;
29746
- const cwd3 = process.cwd();
29747
- const result = installRemoteSkills(cwd3, targetDir, rawItems);
29961
+ const cwd = getBridgeWorkspaceDirectory();
29962
+ const result = installRemoteSkills(cwd, targetDir, rawItems);
29748
29963
  if (!result.success) {
29749
29964
  const err = result.error ?? "Invalid items";
29750
- deps.log(`[Bridge service] install_skills failed: ${err}`);
29965
+ deps.log(`[Bridge service] Install skills failed: ${err}`);
29751
29966
  socket?.send(JSON.stringify({ type: "install_skills_result", id, success: false, error: err }));
29752
29967
  return;
29753
29968
  }
@@ -29816,8 +30031,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
29816
30031
  };
29817
30032
 
29818
30033
  // src/bridge/routing/handlers/revert-turn-snapshot.ts
29819
- import * as fs10 from "node:fs";
29820
- import * as path16 from "node:path";
30034
+ import * as fs11 from "node:fs";
29821
30035
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
29822
30036
  const id = typeof msg.id === "string" ? msg.id : "";
29823
30037
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -29827,9 +30041,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
29827
30041
  void (async () => {
29828
30042
  const s = getWs();
29829
30043
  if (!s) return;
29830
- const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? path16.resolve(process.cwd());
30044
+ const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
29831
30045
  const file2 = snapshotFilePath(agentBase, turnId);
29832
- if (!fs10.existsSync(file2)) {
30046
+ if (!fs11.existsSync(file2)) {
29833
30047
  sendWsMessage(s, {
29834
30048
  type: "revert_turn_snapshot_result",
29835
30049
  id,
@@ -29956,7 +30170,7 @@ import * as path20 from "node:path";
29956
30170
  import os4 from "node:os";
29957
30171
 
29958
30172
  // src/worktrees/prepare-new-session-worktrees.ts
29959
- import * as fs12 from "node:fs";
30173
+ import * as fs13 from "node:fs";
29960
30174
  import * as path18 from "node:path";
29961
30175
 
29962
30176
  // src/git/worktree-add.ts
@@ -29966,7 +30180,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
29966
30180
  }
29967
30181
 
29968
30182
  // src/worktrees/worktree-layout-file.ts
29969
- import * as fs11 from "node:fs";
30183
+ import * as fs12 from "node:fs";
29970
30184
  import * as path17 from "node:path";
29971
30185
  import os3 from "node:os";
29972
30186
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
@@ -29983,8 +30197,8 @@ function normalizeLoadedLayout(raw) {
29983
30197
  function loadWorktreeLayout() {
29984
30198
  try {
29985
30199
  const p = defaultWorktreeLayoutPath();
29986
- if (!fs11.existsSync(p)) return { launcherCwds: [] };
29987
- const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
30200
+ if (!fs12.existsSync(p)) return { launcherCwds: [] };
30201
+ const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
29988
30202
  return normalizeLoadedLayout(raw);
29989
30203
  } catch {
29990
30204
  return { launcherCwds: [] };
@@ -29993,8 +30207,8 @@ function loadWorktreeLayout() {
29993
30207
  function saveWorktreeLayout(layout) {
29994
30208
  try {
29995
30209
  const dir = path17.dirname(defaultWorktreeLayoutPath());
29996
- fs11.mkdirSync(dir, { recursive: true });
29997
- fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
30210
+ fs12.mkdirSync(dir, { recursive: true });
30211
+ fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
29998
30212
  } catch {
29999
30213
  }
30000
30214
  }
@@ -30026,25 +30240,25 @@ async function prepareNewSessionWorktrees(options) {
30026
30240
  const agentMirrorRoot = path18.join(rootAbs, cwdKey);
30027
30241
  const repos = await discoverGitReposUnderRoot(launcherResolved);
30028
30242
  if (repos.length === 0) {
30029
- log2("[worktrees] No git repos under launcher cwd; skipping worktree creation");
30243
+ log2("[worktrees] No Git repositories under launcher working directory; skipping worktree creation.");
30030
30244
  return null;
30031
30245
  }
30032
30246
  const branch = `session-${sessionId}`;
30033
30247
  const worktreePaths = [];
30034
- fs12.mkdirSync(agentMirrorRoot, { recursive: true });
30248
+ fs13.mkdirSync(agentMirrorRoot, { recursive: true });
30035
30249
  for (const repo of repos) {
30036
30250
  let rel = path18.relative(launcherResolved, repo.absolutePath);
30037
30251
  if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
30038
30252
  const relNorm = rel === "" ? "." : rel;
30039
30253
  const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
30040
- fs12.mkdirSync(path18.dirname(wtPath), { recursive: true });
30254
+ fs13.mkdirSync(path18.dirname(wtPath), { recursive: true });
30041
30255
  try {
30042
30256
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
30043
- log2(`[worktrees] Added worktree ${wtPath} (branch ${branch})`);
30257
+ log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
30044
30258
  worktreePaths.push(wtPath);
30045
30259
  } catch (e) {
30046
30260
  log2(
30047
- `[worktrees] worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
30261
+ `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
30048
30262
  );
30049
30263
  }
30050
30264
  }
@@ -30066,24 +30280,26 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
30066
30280
  await gitRenameCurrentBranch(wt, safe);
30067
30281
  log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
30068
30282
  } catch (e) {
30069
- log2(`[worktrees] branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30283
+ log2(
30284
+ `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
30285
+ );
30070
30286
  }
30071
30287
  }
30072
30288
  }
30073
30289
 
30074
30290
  // src/worktrees/remove-session-worktrees.ts
30075
- import * as fs15 from "node:fs";
30291
+ import * as fs16 from "node:fs";
30076
30292
 
30077
30293
  // src/git/worktree-remove.ts
30078
- import * as fs14 from "node:fs";
30294
+ import * as fs15 from "node:fs";
30079
30295
 
30080
30296
  // src/git/resolve-main-repo-from-git-file.ts
30081
- import * as fs13 from "node:fs";
30297
+ import * as fs14 from "node:fs";
30082
30298
  import * as path19 from "node:path";
30083
30299
  function resolveMainRepoFromWorktreeGitFile(wt) {
30084
30300
  const gitDirFile = path19.join(wt, ".git");
30085
- if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
30086
- const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
30301
+ if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
30302
+ const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
30087
30303
  const m = first2.match(/^gitdir:\s*(.+)$/im);
30088
30304
  if (!m) return "";
30089
30305
  const gitWorktreePath = path19.resolve(wt, m[1].trim());
@@ -30097,7 +30313,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
30097
30313
  if (mainRepo) {
30098
30314
  await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
30099
30315
  } else {
30100
- fs14.rmSync(worktreePath, { recursive: true, force: true });
30316
+ fs15.rmSync(worktreePath, { recursive: true, force: true });
30101
30317
  }
30102
30318
  }
30103
30319
 
@@ -30108,9 +30324,9 @@ async function removeSessionWorktrees(paths, log2) {
30108
30324
  await gitWorktreeRemoveForce(wt);
30109
30325
  log2(`[worktrees] Removed worktree ${wt}`);
30110
30326
  } catch (e) {
30111
- log2(`[worktrees] remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30327
+ log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30112
30328
  try {
30113
- fs15.rmSync(wt, { recursive: true, force: true });
30329
+ fs16.rmSync(wt, { recursive: true, force: true });
30114
30330
  } catch {
30115
30331
  }
30116
30332
  }
@@ -30174,7 +30390,7 @@ var SessionWorktreeManager = class {
30174
30390
  return this.bridgeWantsWorktrees;
30175
30391
  }
30176
30392
  /**
30177
- * Returns cwd for the agent (mirror of launcher tree), or undefined to use process.cwd().
30393
+ * Returns cwd for the agent (mirror of launcher tree), or undefined to use the bridge workspace directory.
30178
30394
  */
30179
30395
  async resolveCwdForPrompt(sessionId, opts) {
30180
30396
  if (!sessionId || !this.effective() || !opts.sessionWorktreesEnabled) {
@@ -30187,7 +30403,7 @@ var SessionWorktreeManager = class {
30187
30403
  }
30188
30404
  const prep = await prepareNewSessionWorktrees({
30189
30405
  rootAbs: this.rootAbs,
30190
- launcherCwd: process.cwd(),
30406
+ launcherCwd: getBridgeWorkspaceDirectory(),
30191
30407
  sessionId,
30192
30408
  layout: this.layout,
30193
30409
  log: this.log
@@ -30227,7 +30443,7 @@ var SessionWorktreeManager = class {
30227
30443
  }
30228
30444
  async commitSession(params) {
30229
30445
  const paths = this.sessionPaths.get(params.sessionId);
30230
- const targets = paths?.length ? paths : [process.cwd()];
30446
+ const targets = paths?.length ? paths : [getBridgeWorkspaceDirectory()];
30231
30447
  return commitSessionWorktrees({
30232
30448
  paths: targets,
30233
30449
  branch: params.branch,
@@ -30280,7 +30496,7 @@ function shouldIgnoreRelative(rel) {
30280
30496
  }
30281
30497
  function attachWatchErrorLog(w) {
30282
30498
  w.on("error", (err) => {
30283
- console.error("[file-index] fs.watch error:", err);
30499
+ console.error("[file-index] File watcher error:", err);
30284
30500
  });
30285
30501
  }
30286
30502
  function createFsWatcher(resolved, schedule) {
@@ -30299,7 +30515,7 @@ function createFsWatcher(resolved, schedule) {
30299
30515
  const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
30300
30516
  if (code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM") {
30301
30517
  console.warn(
30302
- "[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."
30518
+ "[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."
30303
30519
  );
30304
30520
  const w = watch(resolved, { recursive: false }, onEvent);
30305
30521
  attachWatchErrorLog(w);
@@ -30308,8 +30524,8 @@ function createFsWatcher(resolved, schedule) {
30308
30524
  throw e;
30309
30525
  }
30310
30526
  }
30311
- function startFileIndexWatcher(cwd3 = process.cwd()) {
30312
- const resolved = path21.resolve(cwd3);
30527
+ function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
30528
+ const resolved = path21.resolve(cwd);
30313
30529
  try {
30314
30530
  buildFileIndex(resolved);
30315
30531
  } catch (e) {
@@ -30363,7 +30579,7 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
30363
30579
  const exited = new Promise((resolve14) => {
30364
30580
  proc.once("exit", () => resolve14());
30365
30581
  });
30366
- log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"})\u2026`);
30582
+ log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
30367
30583
  try {
30368
30584
  proc.kill("SIGTERM");
30369
30585
  } catch {
@@ -30371,7 +30587,9 @@ async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
30371
30587
  await Promise.race([exited, new Promise((resolve14) => setTimeout(resolve14, graceMs))]);
30372
30588
  }
30373
30589
  function forceKillChild(proc, log2, shortId, graceMs) {
30374
- log2(`[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"})\u2026`);
30590
+ log2(
30591
+ `[dev-server] ${shortId} did not exit within ${graceMs}ms; sending SIGKILL (pid=${proc.pid ?? "?"}).`
30592
+ );
30375
30593
  proc.removeAllListeners();
30376
30594
  try {
30377
30595
  proc.kill("SIGKILL");
@@ -30380,7 +30598,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
30380
30598
  }
30381
30599
 
30382
30600
  // src/dev-servers/process/wire-dev-server-child-process.ts
30383
- import fs16 from "node:fs";
30601
+ import fs17 from "node:fs";
30384
30602
 
30385
30603
  // src/dev-servers/manager/forward-pipe.ts
30386
30604
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -30416,7 +30634,7 @@ function wireDevServerChildProcess(d) {
30416
30634
  d.setPollInterval(void 0);
30417
30635
  return;
30418
30636
  }
30419
- fs16.readFile(d.mergedLogPath, (err, buf) => {
30637
+ fs17.readFile(d.mergedLogPath, (err, buf) => {
30420
30638
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
30421
30639
  if (buf.length <= d.mergedReadPos.value) return;
30422
30640
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -30441,7 +30659,7 @@ function wireDevServerChildProcess(d) {
30441
30659
  d.rmMergedCleanupDir(cleanupDir);
30442
30660
  }
30443
30661
  if (signal) {
30444
- d.log(`[dev-server] ${title} stopped (signal ${String(signal)})`);
30662
+ d.log(`[dev-server] ${title} stopped (signal: ${String(signal)}).`);
30445
30663
  } else if (code !== null && code !== 0) {
30446
30664
  const errTail = d.stderrTail.getTail().slice(-3).join("\n");
30447
30665
  d.log(`[dev-server] ${title} exited with code ${code}${errTail ? `
@@ -30454,7 +30672,7 @@ ${errTail}` : ""}`);
30454
30672
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
30455
30673
  };
30456
30674
  if (mergedPath) {
30457
- fs16.readFile(mergedPath, (err, buf) => {
30675
+ fs17.readFile(mergedPath, (err, buf) => {
30458
30676
  if (!err && buf.length > d.mergedReadPos.value) {
30459
30677
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
30460
30678
  if (chunk.length > 0) {
@@ -30556,13 +30774,13 @@ function parseDevServerDefs(servers) {
30556
30774
  }
30557
30775
 
30558
30776
  // src/dev-servers/manager/shell-spawn/utils.ts
30559
- import fs17 from "node:fs";
30777
+ import fs18 from "node:fs";
30560
30778
  function isSpawnEbadf(e) {
30561
30779
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
30562
30780
  }
30563
30781
  function rmDirQuiet(dir) {
30564
30782
  try {
30565
- fs17.rmSync(dir, { recursive: true, force: true });
30783
+ fs18.rmSync(dir, { recursive: true, force: true });
30566
30784
  } catch {
30567
30785
  }
30568
30786
  }
@@ -30570,7 +30788,7 @@ var cachedDevNullReadFd;
30570
30788
  function devNullReadFd() {
30571
30789
  if (cachedDevNullReadFd === void 0) {
30572
30790
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
30573
- cachedDevNullReadFd = fs17.openSync(devPath, "r");
30791
+ cachedDevNullReadFd = fs18.openSync(devPath, "r");
30574
30792
  }
30575
30793
  return cachedDevNullReadFd;
30576
30794
  }
@@ -30580,7 +30798,7 @@ function pipedStdoutStderrFor(attemptStdio) {
30580
30798
 
30581
30799
  // src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
30582
30800
  import { spawn as spawn5 } from "node:child_process";
30583
- function trySpawnPipedViaSh(command, env, cwd3, signal) {
30801
+ function trySpawnPipedViaSh(command, env, cwd, signal) {
30584
30802
  const attempts = [
30585
30803
  { stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
30586
30804
  { stdio: ["ignore", "pipe", "pipe"], endStdin: true },
@@ -30591,7 +30809,7 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
30591
30809
  const attempt = attempts[i];
30592
30810
  const opts = {
30593
30811
  env,
30594
- cwd: cwd3,
30812
+ cwd,
30595
30813
  stdio: attempt.stdio,
30596
30814
  ...signal ? { signal } : {}
30597
30815
  };
@@ -30623,11 +30841,11 @@ function trySpawnPipedViaSh(command, env, cwd3, signal) {
30623
30841
 
30624
30842
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
30625
30843
  import { spawn as spawn6 } from "node:child_process";
30626
- function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
30844
+ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
30627
30845
  try {
30628
30846
  const opts = {
30629
30847
  env,
30630
- cwd: cwd3,
30848
+ cwd,
30631
30849
  stdio: [devNullFd, "pipe", "pipe"],
30632
30850
  shell: true,
30633
30851
  ...signal ? { signal } : {}
@@ -30644,15 +30862,15 @@ function trySpawnShellTruePiped(command, env, cwd3, devNullFd, signal) {
30644
30862
 
30645
30863
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
30646
30864
  import { spawn as spawn7 } from "node:child_process";
30647
- import fs18 from "node:fs";
30865
+ import fs19 from "node:fs";
30648
30866
  import { tmpdir } from "node:os";
30649
30867
  import path22 from "node:path";
30650
- function trySpawnMergedLogFile(command, env, cwd3, signal) {
30651
- const tmpRoot = fs18.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
30868
+ function trySpawnMergedLogFile(command, env, cwd, signal) {
30869
+ const tmpRoot = fs19.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
30652
30870
  const logPath = path22.join(tmpRoot, "combined.log");
30653
30871
  let logFd;
30654
30872
  try {
30655
- logFd = fs18.openSync(logPath, "a");
30873
+ logFd = fs19.openSync(logPath, "a");
30656
30874
  } catch {
30657
30875
  rmDirQuiet(tmpRoot);
30658
30876
  return null;
@@ -30663,15 +30881,15 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
30663
30881
  if (process.platform === "win32") {
30664
30882
  proc = spawn7(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
30665
30883
  env,
30666
- cwd: cwd3,
30884
+ cwd,
30667
30885
  stdio,
30668
30886
  windowsHide: true,
30669
30887
  ...signal ? { signal } : {}
30670
30888
  });
30671
30889
  } else {
30672
- proc = spawn7("/bin/sh", ["-c", command], { env, cwd: cwd3, stdio, ...signal ? { signal } : {} });
30890
+ proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
30673
30891
  }
30674
- fs18.closeSync(logFd);
30892
+ fs19.closeSync(logFd);
30675
30893
  return {
30676
30894
  proc,
30677
30895
  pipedStdoutStderr: true,
@@ -30680,7 +30898,7 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
30680
30898
  };
30681
30899
  } catch (e) {
30682
30900
  try {
30683
- fs18.closeSync(logFd);
30901
+ fs19.closeSync(logFd);
30684
30902
  } catch {
30685
30903
  }
30686
30904
  rmDirQuiet(tmpRoot);
@@ -30691,25 +30909,25 @@ function trySpawnMergedLogFile(command, env, cwd3, signal) {
30691
30909
 
30692
30910
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
30693
30911
  import { spawn as spawn8 } from "node:child_process";
30694
- import fs19 from "node:fs";
30912
+ import fs20 from "node:fs";
30695
30913
  import { tmpdir as tmpdir2 } from "node:os";
30696
30914
  import path23 from "node:path";
30697
30915
  function shSingleQuote(s) {
30698
30916
  return `'${s.replace(/'/g, `'\\''`)}'`;
30699
30917
  }
30700
- function trySpawnShellScriptLogRedirectUnix(command, env, cwd3, signal) {
30701
- const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30918
+ function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
30919
+ const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30702
30920
  const logPath = path23.join(tmpRoot, "combined.log");
30703
30921
  const innerPath = path23.join(tmpRoot, "_cmd.sh");
30704
30922
  const runnerPath = path23.join(tmpRoot, "_run.sh");
30705
30923
  try {
30706
- fs19.writeFileSync(innerPath, `#!/bin/sh
30924
+ fs20.writeFileSync(innerPath, `#!/bin/sh
30707
30925
  ${command}
30708
30926
  `);
30709
- fs19.writeFileSync(
30927
+ fs20.writeFileSync(
30710
30928
  runnerPath,
30711
30929
  `#!/bin/sh
30712
- cd ${shSingleQuote(cwd3)}
30930
+ cd ${shSingleQuote(cwd)}
30713
30931
  /bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
30714
30932
  `
30715
30933
  );
@@ -30731,17 +30949,17 @@ cd ${shSingleQuote(cwd3)}
30731
30949
  throw e;
30732
30950
  }
30733
30951
  }
30734
- function trySpawnShellScriptLogRedirectWin(command, env, cwd3, signal) {
30735
- const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30952
+ function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
30953
+ const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30736
30954
  const logPath = path23.join(tmpRoot, "combined.log");
30737
30955
  const runnerPath = path23.join(tmpRoot, "_run.bat");
30738
30956
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
30739
30957
  const com = process.env.ComSpec || "cmd.exe";
30740
30958
  try {
30741
- fs19.writeFileSync(
30959
+ fs20.writeFileSync(
30742
30960
  runnerPath,
30743
30961
  `@ECHO OFF\r
30744
- CD /D ${q(cwd3)}\r
30962
+ CD /D ${q(cwd)}\r
30745
30963
  ${command} >> ${q(logPath)} 2>&1\r
30746
30964
  `
30747
30965
  );
@@ -30767,10 +30985,10 @@ ${command} >> ${q(logPath)} 2>&1\r
30767
30985
 
30768
30986
  // src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
30769
30987
  import { spawn as spawn9 } from "node:child_process";
30770
- function trySpawnInheritStdio(command, env, cwd3, signal) {
30988
+ function trySpawnInheritStdio(command, env, cwd, signal) {
30771
30989
  const opts = {
30772
30990
  env,
30773
- cwd: cwd3,
30991
+ cwd,
30774
30992
  stdio: "inherit",
30775
30993
  ...signal ? { signal } : {}
30776
30994
  };
@@ -30786,27 +31004,27 @@ function trySpawnInheritStdio(command, env, cwd3, signal) {
30786
31004
  }
30787
31005
 
30788
31006
  // src/dev-servers/manager/shell-spawn/shell-spawn.ts
30789
- function shellSpawn(command, env, cwd3, options) {
31007
+ function shellSpawn(command, env, cwd, options) {
30790
31008
  const signal = options?.signal;
30791
- const piped = trySpawnPipedViaSh(command, env, cwd3, signal);
31009
+ const piped = trySpawnPipedViaSh(command, env, cwd, signal);
30792
31010
  if (piped.ok) {
30793
31011
  return piped.result;
30794
31012
  }
30795
31013
  let lastErr = piped.lastErr;
30796
- const shellTrueProc = trySpawnShellTruePiped(command, env, cwd3, devNullReadFd(), signal);
31014
+ const shellTrueProc = trySpawnShellTruePiped(command, env, cwd, devNullReadFd(), signal);
30797
31015
  if (shellTrueProc) {
30798
31016
  return { proc: shellTrueProc, pipedStdoutStderr: true };
30799
31017
  }
30800
- const fileCapture = trySpawnMergedLogFile(command, env, cwd3, signal);
31018
+ const fileCapture = trySpawnMergedLogFile(command, env, cwd, signal);
30801
31019
  if (fileCapture) {
30802
31020
  return fileCapture;
30803
31021
  }
30804
- const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd3, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd3, signal);
31022
+ const scriptCapture = process.platform === "win32" ? trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) : trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal);
30805
31023
  if (scriptCapture) {
30806
31024
  return scriptCapture;
30807
31025
  }
30808
31026
  try {
30809
- return trySpawnInheritStdio(command, env, cwd3, signal);
31027
+ return trySpawnInheritStdio(command, env, cwd, signal);
30810
31028
  } catch (e) {
30811
31029
  throw lastErr instanceof Error ? lastErr : e instanceof Error ? e : new Error(String(e));
30812
31030
  }
@@ -30864,9 +31082,11 @@ var DevServerManager = class {
30864
31082
  abortControllersByServerId = /* @__PURE__ */ new Map();
30865
31083
  getWs;
30866
31084
  log;
31085
+ getBridgeCwd;
30867
31086
  constructor(options) {
30868
31087
  this.getWs = options.getWs;
30869
31088
  this.log = options.log;
31089
+ this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
30870
31090
  }
30871
31091
  attachFirehose(send) {
30872
31092
  this.firehoseSend = send;
@@ -30990,7 +31210,7 @@ var DevServerManager = class {
30990
31210
  this.sendStatus(serverId, "starting", void 0, emptyTails());
30991
31211
  const ac = new AbortController();
30992
31212
  this.abortControllersByServerId.set(serverId, ac);
30993
- const cwd3 = process.cwd();
31213
+ const cwd = this.getBridgeCwd();
30994
31214
  const childEnv = envForSpawn(process.env, def.env, def.ports);
30995
31215
  const cmd = substituteCommand(def.command.trim(), childEnv);
30996
31216
  const title = def.name.trim() || serverId.slice(0, 8);
@@ -31005,7 +31225,7 @@ var DevServerManager = class {
31005
31225
  let mergedLogPath;
31006
31226
  let mergedCleanupDir;
31007
31227
  try {
31008
- const spawned = shellSpawn(cmd, childEnv, cwd3, {
31228
+ const spawned = shellSpawn(cmd, childEnv, cwd, {
31009
31229
  signal: ac.signal
31010
31230
  });
31011
31231
  proc = spawned.proc;
@@ -31014,7 +31234,7 @@ var DevServerManager = class {
31014
31234
  mergedCleanupDir = spawned.mergedLogCleanupDir;
31015
31235
  } catch (e) {
31016
31236
  const msg = e instanceof Error ? e.message : String(e);
31017
- this.log(`[dev-server] ${title} failed to start: ${msg}`);
31237
+ this.log(`[dev-server] Failed to start ${title}: ${msg}`);
31018
31238
  this.abortControllersByServerId.delete(serverId);
31019
31239
  this.sendStatus(serverId, "error", msg, emptyTails());
31020
31240
  return;
@@ -31139,7 +31359,7 @@ function startStreamingProxy(ws, log2, pr) {
31139
31359
  },
31140
31360
  onEnd: () => sendWsMessage(ws, { type: "proxy_result_end", id: pr.id }),
31141
31361
  onError: (error40) => {
31142
- log2(`[Proxy and log service] streaming preview failed: ${error40}`);
31362
+ log2(`[Proxy and log service] Streaming preview failed: ${error40}`);
31143
31363
  sendWsMessage(ws, { type: "proxy_result_error", id: pr.id, error: error40 });
31144
31364
  }
31145
31365
  });
@@ -31212,11 +31432,11 @@ var handleProxyMessage = (msg, deps) => {
31212
31432
  return;
31213
31433
  }
31214
31434
  void proxyToLocal(pr).then((res) => {
31215
- if (res.error) deps.log(`[Proxy and log service] preview proxy failed: ${res.error}`);
31435
+ if (res.error) deps.log(`[Proxy and log service] Preview proxy failed: ${res.error}`);
31216
31436
  sendWsMessage(deps.ws, { type: "proxy_result", ...res, id: pr.id });
31217
31437
  }).catch((err) => {
31218
31438
  deps.log(
31219
- `[Proxy and log service] preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
31439
+ `[Proxy and log service] Preview proxy failed: ${err instanceof Error ? err.message : String(err)}`
31220
31440
  );
31221
31441
  sendWsMessage(deps.ws, { type: "proxy_result", id: pr.id, error: String(err) });
31222
31442
  });
@@ -31253,6 +31473,7 @@ function tryConsumeBinaryProxyBody(raw, deps) {
31253
31473
  }
31254
31474
 
31255
31475
  // src/firehose/connect-firehose.ts
31476
+ var FIREHOSE_CLIENT_PING_MS = 25e3;
31256
31477
  function connectFirehose(options) {
31257
31478
  const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
31258
31479
  const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
@@ -31261,6 +31482,13 @@ function connectFirehose(options) {
31261
31482
  wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
31262
31483
  }
31263
31484
  const ws = new wrapper_default(wsUrl, wsOptions);
31485
+ let clientPingTimer = null;
31486
+ function clearClientPing() {
31487
+ if (clientPingTimer != null) {
31488
+ clearInterval(clientPingTimer);
31489
+ clientPingTimer = null;
31490
+ }
31491
+ }
31264
31492
  const firehoseSend = (payload) => {
31265
31493
  sendWsMessage(ws, payload);
31266
31494
  };
@@ -31273,6 +31501,15 @@ function connectFirehose(options) {
31273
31501
  startStreamingProxy: (pr) => startStreamingProxy(ws, log2, pr)
31274
31502
  };
31275
31503
  ws.on("open", () => {
31504
+ clearClientPing();
31505
+ clientPingTimer = setInterval(() => {
31506
+ if (ws.readyState === wrapper_default.OPEN) {
31507
+ try {
31508
+ ws.ping();
31509
+ } catch {
31510
+ }
31511
+ }
31512
+ }, FIREHOSE_CLIENT_PING_MS);
31276
31513
  onOpen?.();
31277
31514
  devServerManager.attachFirehose(firehoseSend);
31278
31515
  sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
@@ -31290,35 +31527,30 @@ function connectFirehose(options) {
31290
31527
  });
31291
31528
  });
31292
31529
  ws.on("close", (code, reason) => {
31530
+ clearClientPing();
31293
31531
  devServerManager.detachFirehose();
31294
- log2(
31295
- formatWebSocketClose(
31296
- "[Proxy and log service]",
31297
- code,
31298
- typeof reason === "string" ? reason : reason.toString(),
31299
- "reconnects automatically if the bridge service stays connected"
31300
- )
31301
- );
31302
- onClose?.();
31532
+ const reasonStr = typeof reason === "string" ? reason : reason.toString();
31533
+ onClose?.(code, reasonStr);
31303
31534
  });
31304
31535
  ws.on("error", (err) => {
31536
+ clearClientPing();
31305
31537
  log2(`[Proxy and log service] WebSocket error: ${err.message}`);
31306
31538
  });
31307
31539
  return {
31308
31540
  close() {
31541
+ clearClientPing();
31309
31542
  devServerManager.detachFirehose();
31310
31543
  try {
31311
31544
  ws.removeAllListeners();
31312
31545
  ws.close();
31313
31546
  } catch {
31314
31547
  }
31315
- }
31548
+ },
31549
+ isConnected: () => ws.readyState === wrapper_default.OPEN
31316
31550
  };
31317
31551
  }
31318
31552
 
31319
31553
  // src/bridge/connection/create-bridge-identified-handler.ts
31320
- var FH_RECONNECT_BASE_MS = 2e3;
31321
- var FH_RECONNECT_MAX_MS = 3e4;
31322
31554
  function createOnBridgeIdentified(opts) {
31323
31555
  const { sessionWorktreeManager, devServerManager, firehoseServerUrl, workspaceId, state, logFn } = opts;
31324
31556
  function clearFirehoseReconnectTimer() {
@@ -31327,6 +31559,14 @@ function createOnBridgeIdentified(opts) {
31327
31559
  state.firehoseReconnectTimeout = null;
31328
31560
  }
31329
31561
  }
31562
+ function firehoseCtx() {
31563
+ return {
31564
+ closedByUser: state.closedByUser,
31565
+ currentWs: state.currentWs,
31566
+ firehoseHandle: state.firehoseHandle,
31567
+ firehoseQuiet: state.firehoseQuiet
31568
+ };
31569
+ }
31330
31570
  function attachFirehose(params) {
31331
31571
  state.lastFirehoseParams = params;
31332
31572
  clearFirehoseReconnectTimer();
@@ -31345,37 +31585,52 @@ function createOnBridgeIdentified(opts) {
31345
31585
  devServerManager,
31346
31586
  onOpen: () => {
31347
31587
  if (myGen !== state.firehoseGeneration) return;
31588
+ clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
31589
+ const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
31348
31590
  state.firehoseReconnectAttempt = 0;
31591
+ if (logOpenAsFirehoseReconnect) {
31592
+ logFn("[Proxy and log service] reconnected.");
31593
+ }
31349
31594
  },
31350
- onClose: () => {
31595
+ onClose: (code, reason) => {
31351
31596
  if (myGen !== state.firehoseGeneration) return;
31352
31597
  state.firehoseHandle = null;
31353
31598
  if (state.closedByUser) return;
31354
31599
  const main = state.currentWs;
31355
31600
  if (!main || main.readyState !== wrapper_default.OPEN) {
31356
- logFn("[Proxy and log service] not reconnecting: bridge service socket is not open");
31601
+ logFn(
31602
+ `${PROXY_AND_LOG_SERVICE_LABEL} Not reconnecting preview and log stream: main bridge connection is not open.`
31603
+ );
31357
31604
  return;
31358
31605
  }
31606
+ beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
31359
31607
  clearFirehoseReconnectTimer();
31360
- const delay2 = Math.min(
31361
- FH_RECONNECT_BASE_MS * 2 ** state.firehoseReconnectAttempt,
31362
- FH_RECONNECT_MAX_MS
31363
- );
31608
+ const delay2 = reconnectDelayMs(state.firehoseReconnectAttempt);
31364
31609
  state.firehoseReconnectAttempt += 1;
31365
- logFn(
31366
- `[Proxy and log service] reconnect attempt ${state.firehoseReconnectAttempt} in ${delay2 / 1e3}s\u2026`
31610
+ logNextReconnectAttempt(
31611
+ logFn,
31612
+ PROXY_AND_LOG_SERVICE_LABEL,
31613
+ state.firehoseQuiet,
31614
+ delay2,
31615
+ state.firehoseReconnectAttempt
31367
31616
  );
31368
31617
  state.firehoseReconnectTimeout = setTimeout(() => {
31369
31618
  state.firehoseReconnectTimeout = null;
31370
31619
  if (state.closedByUser) return;
31371
31620
  const w = state.currentWs;
31372
31621
  if (!w || w.readyState !== wrapper_default.OPEN) {
31373
- logFn("[Proxy and log service] reconnect skipped: bridge service socket closed");
31622
+ if (state.firehoseQuiet.verboseLogs) {
31623
+ logFn(
31624
+ `${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: main bridge connection closed before preview stream could reconnect.`
31625
+ );
31626
+ }
31374
31627
  return;
31375
31628
  }
31376
31629
  const p = state.lastFirehoseParams;
31377
31630
  if (!p) {
31378
- logFn("[Proxy and log service] reconnect skipped: no stored connection params");
31631
+ if (state.firehoseQuiet.verboseLogs) {
31632
+ logFn(`${PROXY_AND_LOG_SERVICE_LABEL} Reconnect skipped: no stored connection parameters.`);
31633
+ }
31379
31634
  return;
31380
31635
  }
31381
31636
  attachFirehose(p);
@@ -31458,10 +31713,12 @@ function createSendLocalSkillsReport(getWs, logFn) {
31458
31713
  try {
31459
31714
  const socket = getWs();
31460
31715
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
31461
- const skills2 = discoverLocalSkills(process.cwd());
31716
+ const skills2 = discoverLocalSkills(getBridgeWorkspaceDirectory());
31462
31717
  socket.send(JSON.stringify({ type: "local_skills", skills: skills2 }));
31463
31718
  } catch (e) {
31464
- logFn(`[Bridge service] local_skills report failed: ${e instanceof Error ? e.message : String(e)}`);
31719
+ logFn(
31720
+ `[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
31721
+ );
31465
31722
  }
31466
31723
  };
31467
31724
  }
@@ -31474,12 +31731,15 @@ function createReportAutoDetectedAgents(getWs, logFn) {
31474
31731
  sendWsMessage(socket, { type: "auto_detected_agents_report", agentTypes: types });
31475
31732
  }
31476
31733
  } catch (e) {
31477
- logFn(`[Bridge service] auto_detected_agents report failed: ${e instanceof Error ? e.message : String(e)}`);
31734
+ logFn(
31735
+ `[Bridge service] Auto-detected agents report failed: ${e instanceof Error ? e.message : String(e)}`
31736
+ );
31478
31737
  }
31479
31738
  };
31480
31739
  }
31481
31740
 
31482
31741
  // src/bridge/connection/create-bridge-connection.ts
31742
+ var BRIDGE_CLIENT_PING_MS = 25e3;
31483
31743
  async function createBridgeConnection(options) {
31484
31744
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
31485
31745
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -31490,13 +31750,16 @@ async function createBridgeConnection(options) {
31490
31750
  const state = {
31491
31751
  closedByUser: false,
31492
31752
  reconnectAttempt: 0,
31753
+ logBridgeOpenAsReconnect: false,
31493
31754
  reconnectTimeout: null,
31494
31755
  currentWs: null,
31756
+ mainQuiet: createEmptyReconnectQuietSlot(),
31495
31757
  firehoseHandle: null,
31496
31758
  lastFirehoseParams: null,
31497
31759
  firehoseReconnectTimeout: null,
31498
31760
  firehoseReconnectAttempt: 0,
31499
- firehoseGeneration: 0
31761
+ firehoseGeneration: 0,
31762
+ firehoseQuiet: createEmptyReconnectQuietSlot()
31500
31763
  };
31501
31764
  const worktreesRootAbs = options.worktreesRootAbs ?? defaultWorktreesRootAbs();
31502
31765
  const sessionWorktreeManager = new SessionWorktreeManager({
@@ -31508,7 +31771,7 @@ async function createBridgeConnection(options) {
31508
31771
  function getWs() {
31509
31772
  return state.currentWs;
31510
31773
  }
31511
- const devServerManager = new DevServerManager({ getWs, log: logFn });
31774
+ const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory });
31512
31775
  const onBridgeIdentified = createOnBridgeIdentified({
31513
31776
  sessionWorktreeManager,
31514
31777
  devServerManager,
@@ -31520,7 +31783,13 @@ async function createBridgeConnection(options) {
31520
31783
  const sendLocalSkillsReport = createSendLocalSkillsReport(getWs, logFn);
31521
31784
  const reportAutoDetectedAgents = createReportAutoDetectedAgents(getWs, logFn);
31522
31785
  function handleOpen() {
31786
+ const logOpenAsPostRefreshReconnect = state.logBridgeOpenAsReconnect;
31787
+ clearMainBridgeReconnectQuietOnOpen(state, logFn);
31523
31788
  state.reconnectAttempt = 0;
31789
+ state.logBridgeOpenAsReconnect = false;
31790
+ if (logOpenAsPostRefreshReconnect) {
31791
+ logFn("[Bridge service] reconnected.");
31792
+ }
31524
31793
  const socket = getWs();
31525
31794
  if (socket) {
31526
31795
  sendWsMessage(socket, { type: "identify", role: "cli" });
@@ -31537,11 +31806,9 @@ async function createBridgeConnection(options) {
31537
31806
  state.currentWs = null;
31538
31807
  if (was) was.removeAllListeners();
31539
31808
  const willReconnect = !state.closedByUser;
31540
- logFn(
31541
- formatWebSocketClose("[Bridge service]", code, reason, willReconnect ? "will schedule reconnect" : "not reconnecting (CLI shutting down)")
31542
- );
31809
+ beginMainBridgeDeferredDisconnect(state, code, reason, logFn, willReconnect);
31543
31810
  if (willReconnect) {
31544
- scheduleReconnect(state, connect, logFn);
31811
+ scheduleMainBridgeReconnect(state, connect, logFn);
31545
31812
  }
31546
31813
  }
31547
31814
  const messageDeps = {
@@ -31556,6 +31823,10 @@ async function createBridgeConnection(options) {
31556
31823
  };
31557
31824
  function connect() {
31558
31825
  if (state.closedByUser) return;
31826
+ if (state.reconnectTimeout != null) {
31827
+ clearTimeout(state.reconnectTimeout);
31828
+ state.reconnectTimeout = null;
31829
+ }
31559
31830
  const prev = state.currentWs;
31560
31831
  if (prev) {
31561
31832
  prev.removeAllListeners();
@@ -31568,6 +31839,7 @@ async function createBridgeConnection(options) {
31568
31839
  const url2 = buildBridgeUrl(apiUrl, workspaceId, accessToken);
31569
31840
  state.currentWs = createWsBridge({
31570
31841
  url: url2,
31842
+ clientPingIntervalMs: BRIDGE_CLIENT_PING_MS,
31571
31843
  onAuthInvalid: () => {
31572
31844
  if (authRefreshInFlight) return;
31573
31845
  void (async () => {
@@ -31579,8 +31851,9 @@ async function createBridgeConnection(options) {
31579
31851
  accessToken = next.token;
31580
31852
  refreshTok = next.refreshToken;
31581
31853
  persistTokens?.({ token: accessToken, refreshToken: refreshTok });
31582
- logFn("[Bridge service] access token refreshed; reconnecting\u2026");
31854
+ logFn("[Bridge service] Access token refreshed; reconnecting\u2026");
31583
31855
  state.reconnectAttempt = 0;
31856
+ state.logBridgeOpenAsReconnect = true;
31584
31857
  authRefreshInFlight = false;
31585
31858
  connect();
31586
31859
  return;
@@ -31603,7 +31876,7 @@ async function createBridgeConnection(options) {
31603
31876
  });
31604
31877
  }
31605
31878
  connect();
31606
- const stopFileIndexWatcher = startFileIndexWatcher(process.cwd());
31879
+ const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
31607
31880
  return {
31608
31881
  close: async () => {
31609
31882
  stopFileIndexWatcher();
@@ -31676,7 +31949,7 @@ async function runBridge(options) {
31676
31949
  });
31677
31950
  },
31678
31951
  onAuthInvalid: () => {
31679
- log("[Bridge service] token invalid or revoked; re-authenticating\u2026");
31952
+ log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
31680
31953
  clearConfigForApi(apiUrl);
31681
31954
  void handle.close().then(() => {
31682
31955
  void runBridge({ apiUrl, firehoseServerUrl, worktreesRootAbs });