@buildautomaton/cli 0.1.20 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -25064,7 +25064,7 @@ var {
25064
25064
  } = import_index.default;
25065
25065
 
25066
25066
  // src/cli-version.ts
25067
- var CLI_VERSION = "0.1.20".length > 0 ? "0.1.20" : "0.0.0-dev";
25067
+ var CLI_VERSION = "0.1.21".length > 0 ? "0.1.21" : "0.0.0-dev";
25068
25068
 
25069
25069
  // src/cli/defaults.ts
25070
25070
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -25428,17 +25428,17 @@ async function loadOrCreateE2eCertificates(directory) {
25428
25428
  };
25429
25429
  }
25430
25430
 
25431
- // src/files/cwd/bridge-workspace-directory.ts
25431
+ // src/files/cwd/bridge-root.ts
25432
25432
  import * as path3 from "node:path";
25433
- var bridgeWorkspaceDirectory = null;
25434
- function initBridgeWorkspaceDirectory() {
25435
- bridgeWorkspaceDirectory = path3.resolve(process.cwd());
25433
+ var bridgeRootPath = null;
25434
+ function initBridgeRoot() {
25435
+ bridgeRootPath = path3.resolve(process.cwd());
25436
25436
  }
25437
- function getBridgeWorkspaceDirectory() {
25438
- if (bridgeWorkspaceDirectory == null) {
25439
- bridgeWorkspaceDirectory = path3.resolve(process.cwd());
25437
+ function getBridgeRoot() {
25438
+ if (bridgeRootPath == null) {
25439
+ bridgeRootPath = path3.resolve(process.cwd());
25440
25440
  }
25441
- return bridgeWorkspaceDirectory;
25441
+ return bridgeRootPath;
25442
25442
  }
25443
25443
 
25444
25444
  // src/log.ts
@@ -26505,6 +26505,21 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
26505
26505
  say("Shutdown complete.");
26506
26506
  }
26507
26507
 
26508
+ // src/paths/session-layout-paths.ts
26509
+ import * as path5 from "node:path";
26510
+ function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
26511
+ const resolved = worktreePaths.map((p) => path5.resolve(p)).filter(Boolean);
26512
+ if (resolved.length === 0) return null;
26513
+ resolved.sort();
26514
+ return resolved[0];
26515
+ }
26516
+ function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
26517
+ if (resolvedSessionParentPath != null && String(resolvedSessionParentPath).trim() !== "") {
26518
+ return path5.resolve(String(resolvedSessionParentPath).trim());
26519
+ }
26520
+ return getBridgeRoot();
26521
+ }
26522
+
26508
26523
  // src/lib/local-agent-auth.ts
26509
26524
  var LOCAL_AGENT_AUTH_ERROR_HINTS = {
26510
26525
  "kiro-acp": [/not logged in/i, /kiro-cli\s+login/i, /log in with kiro-cli/i],
@@ -26536,17 +26551,17 @@ function localAgentErrorSuggestsAuth(agentType, errorText) {
26536
26551
  import { execFile as execFile7 } from "node:child_process";
26537
26552
  import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
26538
26553
  import { promisify as promisify7 } from "node:util";
26539
- import * as path7 from "node:path";
26554
+ import * as path8 from "node:path";
26540
26555
 
26541
26556
  // src/git/pre-turn-snapshot.ts
26542
26557
  import * as fs9 from "node:fs";
26543
- import * as path6 from "node:path";
26558
+ import * as path7 from "node:path";
26544
26559
  import { execFile as execFile6 } from "node:child_process";
26545
26560
  import { promisify as promisify6 } from "node:util";
26546
26561
 
26547
26562
  // src/git/discover-repos.ts
26548
26563
  import * as fs8 from "node:fs";
26549
- import * as path5 from "node:path";
26564
+ import * as path6 from "node:path";
26550
26565
 
26551
26566
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
26552
26567
  var import_file_exists = __toESM(require_dist(), 1);
@@ -31131,9 +31146,9 @@ async function isGitRepoDirectory(dirPath) {
31131
31146
  }
31132
31147
 
31133
31148
  // src/git/discover-repos.ts
31134
- async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
31149
+ async function discoverGitRepos(cwd = getBridgeRoot()) {
31135
31150
  const result = [];
31136
- const cwdResolved = path5.resolve(cwd);
31151
+ const cwdResolved = path6.resolve(cwd);
31137
31152
  if (await isGitRepoDirectory(cwdResolved)) {
31138
31153
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
31139
31154
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -31146,7 +31161,7 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
31146
31161
  }
31147
31162
  for (const ent of entries) {
31148
31163
  if (!ent.isDirectory()) continue;
31149
- const childPath = path5.join(cwdResolved, ent.name);
31164
+ const childPath = path6.join(cwdResolved, ent.name);
31150
31165
  if (await isGitRepoDirectory(childPath)) {
31151
31166
  const remoteUrl = await getRemoteOriginUrl(childPath);
31152
31167
  result.push({ absolutePath: childPath, remoteUrl });
@@ -31154,12 +31169,12 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
31154
31169
  }
31155
31170
  return result;
31156
31171
  }
31157
- async function discoverGitReposUnderRoot(rootAbs) {
31158
- const root = path5.resolve(rootAbs);
31172
+ async function discoverGitReposUnderRoot(rootPath) {
31173
+ const root = path6.resolve(rootPath);
31159
31174
  const roots = [];
31160
31175
  async function walk(dir) {
31161
31176
  if (await isGitRepoDirectory(dir)) {
31162
- roots.push(path5.resolve(dir));
31177
+ roots.push(path6.resolve(dir));
31163
31178
  return;
31164
31179
  }
31165
31180
  let entries;
@@ -31170,7 +31185,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
31170
31185
  }
31171
31186
  for (const ent of entries) {
31172
31187
  if (!ent.isDirectory() || ent.name === ".git") continue;
31173
- await walk(path5.join(dir, ent.name));
31188
+ await walk(path6.join(dir, ent.name));
31174
31189
  }
31175
31190
  }
31176
31191
  await walk(root);
@@ -31186,7 +31201,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
31186
31201
  // src/git/pre-turn-snapshot.ts
31187
31202
  var execFileAsync5 = promisify6(execFile6);
31188
31203
  function snapshotsDirForCwd(agentCwd) {
31189
- return path6.join(agentCwd, ".buildautomaton", "snapshots");
31204
+ return path7.join(agentCwd, ".buildautomaton", "snapshots");
31190
31205
  }
31191
31206
  async function gitStashCreate(repoRoot, log2) {
31192
31207
  try {
@@ -31213,14 +31228,20 @@ async function gitRun(repoRoot, args, log2, label) {
31213
31228
  }
31214
31229
  }
31215
31230
  async function resolveSnapshotRepoRoots(options) {
31216
- const { worktreePaths, fallbackCwd, log: log2 } = options;
31231
+ const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
31217
31232
  if (worktreePaths?.length) {
31218
- const uniq = [...new Set(worktreePaths.map((p) => path6.resolve(p)))];
31233
+ const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
31219
31234
  return uniq;
31220
31235
  }
31221
31236
  try {
31222
31237
  const repos = await discoverGitReposUnderRoot(fallbackCwd);
31223
- return repos.map((r) => r.absolutePath);
31238
+ const mapped = repos.map((r) => r.absolutePath);
31239
+ const sid = sessionId?.trim();
31240
+ if (sid) {
31241
+ const filtered = mapped.filter((root) => path7.basename(root) === sid);
31242
+ if (filtered.length > 0) return filtered;
31243
+ }
31244
+ return mapped;
31224
31245
  } catch (e) {
31225
31246
  log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
31226
31247
  return [];
@@ -31247,7 +31268,7 @@ async function capturePreTurnSnapshot(options) {
31247
31268
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
31248
31269
  repos
31249
31270
  };
31250
- const filePath = path6.join(dir, `${runId}.json`);
31271
+ const filePath = path7.join(dir, `${runId}.json`);
31251
31272
  try {
31252
31273
  fs9.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
31253
31274
  } catch (e) {
@@ -31285,7 +31306,7 @@ async function applyPreTurnSnapshot(filePath, log2) {
31285
31306
  return { ok: true };
31286
31307
  }
31287
31308
  function snapshotFilePath(agentCwd, runId) {
31288
- return path6.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
31309
+ return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
31289
31310
  }
31290
31311
 
31291
31312
  // src/git/session-git-queue.ts
@@ -31334,7 +31355,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
31334
31355
  continue;
31335
31356
  }
31336
31357
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
31337
- const slug = path7.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
31358
+ const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
31338
31359
  for (const rel of lines) {
31339
31360
  if (rel.includes("..")) continue;
31340
31361
  try {
@@ -31348,8 +31369,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
31348
31369
  );
31349
31370
  if (!patchContent.trim()) continue;
31350
31371
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
31351
- const absFile = path7.join(repo.path, rel);
31352
- const newText = await readWorkspaceFileAsUtf8(absFile);
31372
+ const workspaceFilePath = path8.join(repo.path, rel);
31373
+ const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
31353
31374
  sendSessionUpdate({
31354
31375
  type: "session_file_change",
31355
31376
  sessionId,
@@ -31964,7 +31985,7 @@ async function sendPromptToAgent(options) {
31964
31985
 
31965
31986
  // src/agents/acp/ensure-acp-client.ts
31966
31987
  import * as fs10 from "node:fs";
31967
- import * as path11 from "node:path";
31988
+ import * as path12 from "node:path";
31968
31989
 
31969
31990
  // src/error-message.ts
31970
31991
  function errorMessage(err) {
@@ -32003,7 +32024,7 @@ async function isCommandOnPath(command, timeoutMs = 4e3) {
32003
32024
  // src/agents/acp/clients/sdk-stdio-acp-client.ts
32004
32025
  import { spawn as spawn2 } from "node:child_process";
32005
32026
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
32006
- import { dirname } from "node:path";
32027
+ import { dirname as dirname2 } from "node:path";
32007
32028
  import { Readable, Writable } from "node:stream";
32008
32029
 
32009
32030
  // src/files/diff/unified-diff.ts
@@ -32053,21 +32074,21 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
32053
32074
  }
32054
32075
 
32055
32076
  // src/agents/acp/safe-fs-path.ts
32056
- import * as path8 from "node:path";
32077
+ import * as path9 from "node:path";
32057
32078
  function resolveSafePathUnderCwd(cwd, filePath) {
32058
32079
  const trimmed2 = filePath.trim();
32059
32080
  if (!trimmed2) return null;
32060
- const normalizedCwd = path8.resolve(cwd);
32061
- const resolved = path8.isAbsolute(trimmed2) ? path8.normalize(trimmed2) : path8.resolve(normalizedCwd, trimmed2);
32062
- const rel = path8.relative(normalizedCwd, resolved);
32063
- if (rel.startsWith("..") || path8.isAbsolute(rel)) return null;
32081
+ const normalizedCwd = path9.resolve(cwd);
32082
+ const resolved = path9.isAbsolute(trimmed2) ? path9.normalize(trimmed2) : path9.resolve(normalizedCwd, trimmed2);
32083
+ const rel = path9.relative(normalizedCwd, resolved);
32084
+ if (rel.startsWith("..") || path9.isAbsolute(rel)) return null;
32064
32085
  return resolved;
32065
32086
  }
32066
32087
  function toDisplayPathRelativeToCwd(cwd, absolutePath) {
32067
- const normalizedCwd = path8.resolve(cwd);
32068
- const rel = path8.relative(normalizedCwd, path8.resolve(absolutePath));
32069
- if (!rel || rel === "") return path8.basename(absolutePath);
32070
- return rel.split(path8.sep).join("/");
32088
+ const normalizedCwd = path9.resolve(cwd);
32089
+ const rel = path9.relative(normalizedCwd, path9.resolve(absolutePath));
32090
+ if (!rel || rel === "") return path9.basename(absolutePath);
32091
+ return rel.split(path9.sep).join("/");
32071
32092
  }
32072
32093
 
32073
32094
  // src/agents/acp/clients/agent-stderr-capture.ts
@@ -32147,6 +32168,52 @@ function createSdkStdioExtNotificationHandler(options) {
32147
32168
  }
32148
32169
  }
32149
32170
 
32171
+ // src/agents/acp/enrich-acp-permission-rpc-result.ts
32172
+ var META_KEY = "permissionOptionKind";
32173
+ function optionRecordId(rec) {
32174
+ const raw = rec.optionId ?? rec.id;
32175
+ if (typeof raw === "string" && raw.trim() !== "") return raw.trim();
32176
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
32177
+ return "";
32178
+ }
32179
+ function enrichAcpPermissionRpcResultFromRequestParams(result, params) {
32180
+ if (params == null || result == null || typeof result !== "object" || Array.isArray(result)) {
32181
+ return result;
32182
+ }
32183
+ const root = result;
32184
+ const outcome = root.outcome;
32185
+ if (outcome == null || typeof outcome !== "object" || Array.isArray(outcome)) return result;
32186
+ const o = outcome;
32187
+ if (o.outcome !== "selected" || typeof o.optionId !== "string" || o.optionId.trim() === "") {
32188
+ return result;
32189
+ }
32190
+ const selectedId = o.optionId.trim();
32191
+ const prevMeta = o._meta != null && typeof o._meta === "object" && !Array.isArray(o._meta) ? o._meta : {};
32192
+ if (typeof prevMeta[META_KEY] === "string" && prevMeta[META_KEY].trim() !== "") {
32193
+ return result;
32194
+ }
32195
+ const rawOpts = Array.isArray(params.options) ? params.options : [];
32196
+ let matchedKind;
32197
+ for (const item of rawOpts) {
32198
+ if (item == null || typeof item !== "object" || Array.isArray(item)) continue;
32199
+ const rec = item;
32200
+ const id = optionRecordId(rec);
32201
+ if (!id || id !== selectedId) continue;
32202
+ if (typeof rec.kind === "string" && rec.kind.trim() !== "") {
32203
+ matchedKind = rec.kind.trim();
32204
+ break;
32205
+ }
32206
+ }
32207
+ if (!matchedKind) return result;
32208
+ return {
32209
+ ...root,
32210
+ outcome: {
32211
+ ...o,
32212
+ _meta: { ...prevMeta, [META_KEY]: matchedKind }
32213
+ }
32214
+ };
32215
+ }
32216
+
32150
32217
  // src/agents/acp/clients/sdk-stdio-acp-client.ts
32151
32218
  function formatSpawnError(err, command) {
32152
32219
  if (err.code === "ENOENT") {
@@ -32168,7 +32235,7 @@ async function createSdkStdioAcpClient(options) {
32168
32235
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
32169
32236
  const {
32170
32237
  command,
32171
- cwd = getBridgeWorkspaceDirectory(),
32238
+ cwd = getBridgeRoot(),
32172
32239
  backendAgentType,
32173
32240
  onSessionUpdate,
32174
32241
  onRequest,
@@ -32224,7 +32291,7 @@ async function createSdkStdioAcpClient(options) {
32224
32291
  onSessionUpdate
32225
32292
  });
32226
32293
  let permissionSeq = 0;
32227
- const pendingPermissionResolvers = /* @__PURE__ */ new Map();
32294
+ const pendingPermissionReplies = /* @__PURE__ */ new Map();
32228
32295
  const client = (_agent) => ({
32229
32296
  async requestPermission(params) {
32230
32297
  const requestId = `perm-${++permissionSeq}`;
@@ -32238,14 +32305,14 @@ async function createSdkStdioAcpClient(options) {
32238
32305
  } catch {
32239
32306
  }
32240
32307
  return await new Promise((resolve19) => {
32241
- pendingPermissionResolvers.set(requestId, resolve19);
32308
+ pendingPermissionReplies.set(requestId, { resolve: resolve19, params: paramsRecord });
32242
32309
  });
32243
32310
  },
32244
32311
  async readTextFile(params) {
32245
- const abs = resolveSafePathUnderCwd(cwd, params.path);
32246
- if (!abs) throw new Error("Invalid or disallowed path");
32312
+ const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
32313
+ if (!resolvedPath) throw new Error("Invalid or disallowed path");
32247
32314
  try {
32248
- let content = readFileSync2(abs, "utf8");
32315
+ let content = readFileSync2(resolvedPath, "utf8");
32249
32316
  content = sliceFileContentRange(content, params.line, params.limit);
32250
32317
  return { content };
32251
32318
  } catch (e) {
@@ -32254,17 +32321,17 @@ async function createSdkStdioAcpClient(options) {
32254
32321
  }
32255
32322
  },
32256
32323
  async writeTextFile(params) {
32257
- const abs = resolveSafePathUnderCwd(cwd, params.path);
32258
- if (!abs) throw new Error("Invalid or disallowed path");
32324
+ const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
32325
+ if (!resolvedPath) throw new Error("Invalid or disallowed path");
32259
32326
  let oldText = "";
32260
32327
  try {
32261
- oldText = readFileSync2(abs, "utf8");
32328
+ oldText = readFileSync2(resolvedPath, "utf8");
32262
32329
  } catch (e) {
32263
32330
  if (e.code !== "ENOENT") throw e;
32264
32331
  }
32265
- mkdirSync2(dirname(abs), { recursive: true });
32266
- writeFileSync2(abs, params.content, "utf8");
32267
- const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
32332
+ mkdirSync2(dirname2(resolvedPath), { recursive: true });
32333
+ writeFileSync2(resolvedPath, params.content, "utf8");
32334
+ const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
32268
32335
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
32269
32336
  onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
32270
32337
  return {};
@@ -32346,9 +32413,9 @@ async function createSdkStdioAcpClient(options) {
32346
32413
  }
32347
32414
  },
32348
32415
  async cancel() {
32349
- for (const [id, resolve19] of [...pendingPermissionResolvers.entries()]) {
32350
- pendingPermissionResolvers.delete(id);
32351
- resolve19({ outcome: { outcome: "cancelled" } });
32416
+ for (const [id, entry] of [...pendingPermissionReplies.entries()]) {
32417
+ pendingPermissionReplies.delete(id);
32418
+ entry.resolve({ outcome: { outcome: "cancelled" } });
32352
32419
  }
32353
32420
  try {
32354
32421
  await connection.cancel({ sessionId });
@@ -32364,10 +32431,11 @@ async function createSdkStdioAcpClient(options) {
32364
32431
  }
32365
32432
  },
32366
32433
  resolveRequest(requestId, result) {
32367
- const resolve19 = pendingPermissionResolvers.get(requestId);
32368
- if (!resolve19) return;
32369
- pendingPermissionResolvers.delete(requestId);
32370
- resolve19(result);
32434
+ const entry = pendingPermissionReplies.get(requestId);
32435
+ if (!entry) return;
32436
+ pendingPermissionReplies.delete(requestId);
32437
+ const enriched = enrichAcpPermissionRpcResultFromRequestParams(result, entry.params);
32438
+ entry.resolve(enriched);
32371
32439
  },
32372
32440
  disconnect() {
32373
32441
  child.kill();
@@ -32452,7 +32520,7 @@ __export(cursor_acp_client_exports, {
32452
32520
  detectLocalAgentPresence: () => detectLocalAgentPresence3
32453
32521
  });
32454
32522
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
32455
- import { dirname as dirname2 } from "node:path";
32523
+ import { dirname as dirname3 } from "node:path";
32456
32524
  import { spawn as spawn3 } from "node:child_process";
32457
32525
  import * as readline from "node:readline";
32458
32526
 
@@ -32502,7 +32570,7 @@ function buildCursorAcpSpawnCommand(base, sessionMode) {
32502
32570
  async function createCursorAcpClient(options) {
32503
32571
  const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
32504
32572
  const {
32505
- cwd = getBridgeWorkspaceDirectory(),
32573
+ cwd = getBridgeRoot(),
32506
32574
  backendAgentType,
32507
32575
  onSessionUpdate,
32508
32576
  onRequest,
@@ -32544,7 +32612,9 @@ async function createCursorAcpClient(options) {
32544
32612
  child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
32545
32613
  }
32546
32614
  function resolveRequest(requestId, result) {
32547
- respond(requestId, result);
32615
+ const pending2 = pendingRequests.get(requestId);
32616
+ const payload = pending2?.method === "session/request_permission" ? enrichAcpPermissionRpcResultFromRequestParams(result, pending2.params) : result;
32617
+ respond(requestId, payload);
32548
32618
  pendingRequests.delete(requestId);
32549
32619
  }
32550
32620
  let promptOutputBuffer = "";
@@ -32600,14 +32670,14 @@ async function createCursorAcpClient(options) {
32600
32670
  if (dbgFs) {
32601
32671
  console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
32602
32672
  }
32603
- const abs = resolveSafePathUnderCwd(cwd, filePath);
32604
- if (!abs) {
32673
+ const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
32674
+ if (!resolvedPath) {
32605
32675
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
32606
32676
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
32607
32677
  return;
32608
32678
  }
32609
32679
  try {
32610
- let content = readFileSync3(abs, "utf8");
32680
+ let content = readFileSync3(resolvedPath, "utf8");
32611
32681
  const line2 = typeof params.line === "number" ? params.line : void 0;
32612
32682
  const limit = typeof params.limit === "number" ? params.limit : void 0;
32613
32683
  content = sliceLinesByRange(content, line2, limit);
@@ -32631,15 +32701,15 @@ async function createCursorAcpClient(options) {
32631
32701
  `[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
32632
32702
  );
32633
32703
  }
32634
- const abs = resolveSafePathUnderCwd(cwd, filePath);
32635
- if (!abs) {
32704
+ const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
32705
+ if (!resolvedPath) {
32636
32706
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
32637
32707
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
32638
32708
  return;
32639
32709
  }
32640
32710
  let oldText = "";
32641
32711
  try {
32642
- oldText = readFileSync3(abs, "utf8");
32712
+ oldText = readFileSync3(resolvedPath, "utf8");
32643
32713
  } catch (e) {
32644
32714
  if (e.code !== "ENOENT") {
32645
32715
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
@@ -32647,13 +32717,13 @@ async function createCursorAcpClient(options) {
32647
32717
  }
32648
32718
  }
32649
32719
  try {
32650
- mkdirSync3(dirname2(abs), { recursive: true });
32651
- writeFileSync3(abs, newText, "utf8");
32720
+ mkdirSync3(dirname3(resolvedPath), { recursive: true });
32721
+ writeFileSync3(resolvedPath, newText, "utf8");
32652
32722
  } catch (e) {
32653
32723
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
32654
32724
  return;
32655
32725
  }
32656
- const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
32726
+ const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
32657
32727
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
32658
32728
  onFileChange?.({ path: displayPath, oldText, newText, patchContent });
32659
32729
  respond(id, null);
@@ -32769,8 +32839,7 @@ async function createCursorAcpClient(options) {
32769
32839
  resolveRequest(requestId, result) {
32770
32840
  const numericId = Number(requestId);
32771
32841
  if (!Number.isFinite(numericId)) return;
32772
- const pendingRequest = pendingRequests.get(numericId);
32773
- if (!pendingRequest) return;
32842
+ if (!pendingRequests.get(numericId)) return;
32774
32843
  resolveRequest(numericId, result);
32775
32844
  },
32776
32845
  disconnect() {
@@ -32896,16 +32965,16 @@ import { existsSync, statSync } from "node:fs";
32896
32965
 
32897
32966
  // src/git/get-git-repo-root-sync.ts
32898
32967
  import { execFileSync as execFileSync2 } from "node:child_process";
32899
- import * as path9 from "node:path";
32968
+ import * as path10 from "node:path";
32900
32969
  function getGitRepoRootSync(startDir) {
32901
32970
  try {
32902
32971
  const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
32903
- cwd: path9.resolve(startDir),
32972
+ cwd: path10.resolve(startDir),
32904
32973
  encoding: "utf8",
32905
32974
  stdio: ["ignore", "pipe", "ignore"],
32906
32975
  maxBuffer: 1024 * 1024
32907
32976
  }).trim();
32908
- return out ? path9.resolve(out) : null;
32977
+ return out ? path10.resolve(out) : null;
32909
32978
  } catch {
32910
32979
  return null;
32911
32980
  }
@@ -32914,64 +32983,64 @@ function getGitRepoRootSync(startDir) {
32914
32983
  // src/agents/acp/workspace-files.ts
32915
32984
  import { execFileSync as execFileSync3 } from "node:child_process";
32916
32985
  import { readFileSync as readFileSync4 } from "node:fs";
32917
- import * as path10 from "node:path";
32918
- function resolveWorkspaceFilePath(cwd, rawPath) {
32986
+ import * as path11 from "node:path";
32987
+ function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
32919
32988
  const trimmed2 = rawPath.trim();
32920
32989
  if (!trimmed2) return null;
32921
- const normalizedCwd = path10.resolve(cwd);
32922
- let abs = resolveSafePathUnderCwd(cwd, trimmed2);
32923
- if (!abs) {
32924
- const candidate = path10.isAbsolute(trimmed2) ? path10.normalize(trimmed2) : path10.normalize(path10.resolve(normalizedCwd, trimmed2));
32925
- const gitRoot2 = getGitRepoRootSync(cwd);
32990
+ const normalizedSessionParent = path11.resolve(sessionParentPath);
32991
+ let resolvedPath = resolveSafePathUnderCwd(sessionParentPath, trimmed2);
32992
+ if (!resolvedPath) {
32993
+ const candidate = path11.isAbsolute(trimmed2) ? path11.normalize(trimmed2) : path11.normalize(path11.resolve(normalizedSessionParent, trimmed2));
32994
+ const gitRoot2 = getGitRepoRootSync(sessionParentPath);
32926
32995
  if (!gitRoot2) return null;
32927
- const rel = path10.relative(gitRoot2, candidate);
32928
- if (rel.startsWith("..") || path10.isAbsolute(rel)) return null;
32929
- abs = candidate;
32996
+ const rel = path11.relative(gitRoot2, candidate);
32997
+ if (rel.startsWith("..") || path11.isAbsolute(rel)) return null;
32998
+ resolvedPath = candidate;
32930
32999
  }
32931
- const gitRoot = getGitRepoRootSync(cwd);
33000
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
32932
33001
  if (gitRoot) {
32933
- const relFromRoot = path10.relative(gitRoot, abs);
32934
- if (!relFromRoot.startsWith("..") && !path10.isAbsolute(relFromRoot)) {
32935
- return { abs, display: relFromRoot.split(path10.sep).join("/") };
33002
+ const relFromRoot = path11.relative(gitRoot, resolvedPath);
33003
+ if (!relFromRoot.startsWith("..") && !path11.isAbsolute(relFromRoot)) {
33004
+ return { resolvedPath, display: relFromRoot.split(path11.sep).join("/") };
32936
33005
  }
32937
33006
  }
32938
- return { abs, display: toDisplayPathRelativeToCwd(cwd, abs) };
33007
+ return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
32939
33008
  }
32940
- function readUtf8WorkspaceFile(cwd, displayPath) {
33009
+ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
32941
33010
  if (!displayPath || displayPath.includes("..")) return "";
32942
- const gitRoot = getGitRepoRootSync(cwd);
33011
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
32943
33012
  if (gitRoot) {
32944
- const abs2 = path10.resolve(gitRoot, displayPath);
32945
- const rel = path10.relative(gitRoot, abs2);
32946
- if (!rel.startsWith("..") && !path10.isAbsolute(rel)) {
33013
+ const resolvedPath2 = path11.resolve(gitRoot, displayPath);
33014
+ const rel = path11.relative(gitRoot, resolvedPath2);
33015
+ if (!rel.startsWith("..") && !path11.isAbsolute(rel)) {
32947
33016
  try {
32948
- return readFileSync4(abs2, "utf8");
33017
+ return readFileSync4(resolvedPath2, "utf8");
32949
33018
  } catch {
32950
33019
  }
32951
33020
  }
32952
33021
  }
32953
- const abs = resolveSafePathUnderCwd(cwd, displayPath);
32954
- if (!abs) return "";
33022
+ const resolvedPath = resolveSafePathUnderCwd(sessionParentPath, displayPath);
33023
+ if (!resolvedPath) return "";
32955
33024
  try {
32956
- return readFileSync4(abs, "utf8");
33025
+ return readFileSync4(resolvedPath, "utf8");
32957
33026
  } catch {
32958
33027
  return "";
32959
33028
  }
32960
33029
  }
32961
- function tryWorkspaceDisplayToAbs(cwd, displayPath) {
33030
+ function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
32962
33031
  if (!displayPath || displayPath.includes("..")) return null;
32963
- const gitRoot = getGitRepoRootSync(cwd);
33032
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
32964
33033
  if (gitRoot) {
32965
- const abs = path10.resolve(gitRoot, displayPath);
32966
- const rel = path10.relative(gitRoot, abs);
32967
- if (!rel.startsWith("..") && !path10.isAbsolute(rel)) return abs;
33034
+ const resolvedPath = path11.resolve(gitRoot, displayPath);
33035
+ const rel = path11.relative(gitRoot, resolvedPath);
33036
+ if (!rel.startsWith("..") && !path11.isAbsolute(rel)) return resolvedPath;
32968
33037
  }
32969
- return resolveSafePathUnderCwd(cwd, displayPath);
33038
+ return resolveSafePathUnderCwd(sessionParentPath, displayPath);
32970
33039
  }
32971
- function readGitHeadBlob(cwd, displayPath) {
33040
+ function readGitHeadBlob(sessionParentPath, displayPath) {
32972
33041
  if (!displayPath || displayPath.includes("..")) return "";
32973
- const gitRoot = getGitRepoRootSync(cwd);
32974
- const execCwd = gitRoot ?? cwd;
33042
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
33043
+ const execCwd = gitRoot ?? sessionParentPath;
32975
33044
  try {
32976
33045
  return execFileSync3("git", ["show", `HEAD:${displayPath}`], {
32977
33046
  cwd: execCwd,
@@ -32984,9 +33053,9 @@ function readGitHeadBlob(cwd, displayPath) {
32984
33053
  }
32985
33054
 
32986
33055
  // src/agents/acp/session-file-change-path-kind.ts
32987
- function gitHeadPathObjectType(cwd, displayPath) {
33056
+ function gitHeadPathObjectType(sessionParentPath, displayPath) {
32988
33057
  if (!displayPath || displayPath.includes("..")) return null;
32989
- const gitRoot = getGitRepoRootSync(cwd);
33058
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
32990
33059
  if (!gitRoot) return null;
32991
33060
  try {
32992
33061
  return execFileSync4("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
@@ -32997,11 +33066,11 @@ function gitHeadPathObjectType(cwd, displayPath) {
32997
33066
  return null;
32998
33067
  }
32999
33068
  }
33000
- function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
33001
- const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
33002
- if (abs && existsSync(abs)) {
33069
+ function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
33070
+ const resolvedPath = tryWorkspaceDisplayToPath(sessionParentPath, displayPath);
33071
+ if (resolvedPath && existsSync(resolvedPath)) {
33003
33072
  try {
33004
- if (statSync(abs).isDirectory()) {
33073
+ if (statSync(resolvedPath).isDirectory()) {
33005
33074
  return { isDirectory: true, directoryRemoved: false };
33006
33075
  }
33007
33076
  return { isDirectory: false, directoryRemoved: false };
@@ -33009,7 +33078,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
33009
33078
  return { isDirectory: false, directoryRemoved: false };
33010
33079
  }
33011
33080
  }
33012
- if (gitHeadPathObjectType(cwd, displayPath) === "tree") {
33081
+ if (gitHeadPathObjectType(sessionParentPath, displayPath) === "tree") {
33013
33082
  return { isDirectory: true, directoryRemoved: true };
33014
33083
  }
33015
33084
  return { isDirectory: false, directoryRemoved: false };
@@ -33017,7 +33086,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
33017
33086
 
33018
33087
  // src/agents/acp/hooks/bridge-on-file-change.ts
33019
33088
  function createBridgeOnFileChange(opts) {
33020
- const { routing, getSendSessionUpdate, log: log2 } = opts;
33089
+ const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
33021
33090
  return (evt) => {
33022
33091
  const runId = routing.runId;
33023
33092
  const sessionId = routing.sessionId;
@@ -33028,8 +33097,7 @@ function createBridgeOnFileChange(opts) {
33028
33097
  );
33029
33098
  return;
33030
33099
  }
33031
- const cwd = getBridgeWorkspaceDirectory();
33032
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
33100
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, evt.path);
33033
33101
  try {
33034
33102
  send({
33035
33103
  type: "session_file_change",
@@ -33106,12 +33174,12 @@ function extractDiffPath(o) {
33106
33174
  }
33107
33175
 
33108
33176
  // src/agents/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
33109
- function pushDiffIfComplete(o, cwd, out) {
33177
+ function pushDiffIfComplete(o, sessionParentPath, out) {
33110
33178
  const t = o.type;
33111
33179
  if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
33112
33180
  const rawPath = extractDiffPath(o);
33113
33181
  if (!rawPath) return;
33114
- const resolved = resolveWorkspaceFilePath(cwd, rawPath);
33182
+ const resolved = resolveWorkspaceFilePath(sessionParentPath, rawPath);
33115
33183
  if (!resolved) return;
33116
33184
  const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
33117
33185
  const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
@@ -33130,17 +33198,17 @@ var NEST_KEYS = [
33130
33198
  "data",
33131
33199
  "arguments"
33132
33200
  ];
33133
- function walkValue(value, cwd, depth, out) {
33201
+ function walkValue(value, sessionParentPath, depth, out) {
33134
33202
  if (depth > 12 || value == null) return;
33135
33203
  if (Array.isArray(value)) {
33136
- for (const x of value) walkValue(x, cwd, depth + 1, out);
33204
+ for (const x of value) walkValue(x, sessionParentPath, depth + 1, out);
33137
33205
  return;
33138
33206
  }
33139
33207
  if (typeof value !== "object") return;
33140
33208
  const o = value;
33141
- pushDiffIfComplete(o, cwd, out);
33209
+ pushDiffIfComplete(o, sessionParentPath, out);
33142
33210
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
33143
- walkValue(o.content, cwd, depth + 1, out);
33211
+ walkValue(o.content, sessionParentPath, depth + 1, out);
33144
33212
  }
33145
33213
  for (const k of NEST_KEYS) {
33146
33214
  if (o.type === "content" && k === "content") continue;
@@ -33148,23 +33216,23 @@ function walkValue(value, cwd, depth, out) {
33148
33216
  if (v == null) continue;
33149
33217
  if (k === "arguments" && typeof v === "string") {
33150
33218
  try {
33151
- walkValue(JSON.parse(v), cwd, depth + 1, out);
33219
+ walkValue(JSON.parse(v), sessionParentPath, depth + 1, out);
33152
33220
  } catch {
33153
33221
  }
33154
33222
  continue;
33155
33223
  }
33156
- walkValue(v, cwd, depth + 1, out);
33224
+ walkValue(v, sessionParentPath, depth + 1, out);
33157
33225
  }
33158
33226
  }
33159
33227
 
33160
33228
  // src/agents/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
33161
- function extractAcpFileDiffsFromUpdate(update, cwd) {
33229
+ function extractAcpFileDiffsFromUpdate(update, sessionParentPath) {
33162
33230
  if (!update || typeof update !== "object") return [];
33163
33231
  const u = update;
33164
33232
  const out = [];
33165
33233
  const content = u.content;
33166
33234
  if (Array.isArray(content)) {
33167
- for (const x of content) walkValue(x, cwd, 0, out);
33235
+ for (const x of content) walkValue(x, sessionParentPath, 0, out);
33168
33236
  }
33169
33237
  const byPath = /* @__PURE__ */ new Map();
33170
33238
  for (const d of out) byPath.set(d.path, d);
@@ -33172,18 +33240,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd) {
33172
33240
  }
33173
33241
 
33174
33242
  // src/agents/acp/hooks/extract-tool-target-paths.ts
33175
- function addPath(cwd, raw, out) {
33243
+ function addPath(sessionParentPath, raw, out) {
33176
33244
  if (typeof raw !== "string") return;
33177
33245
  const trimmed2 = raw.trim();
33178
33246
  if (!trimmed2) return;
33179
- const resolved = resolveWorkspaceFilePath(cwd, trimmed2);
33247
+ const resolved = resolveWorkspaceFilePath(sessionParentPath, trimmed2);
33180
33248
  if (!resolved) return;
33181
33249
  out.add(resolved.display);
33182
33250
  }
33183
- function walkLocations(cwd, loc, out) {
33251
+ function walkLocations(sessionParentPath, loc, out) {
33184
33252
  if (!Array.isArray(loc)) return;
33185
33253
  for (const item of loc) {
33186
- if (item && typeof item === "object") addPath(cwd, item.path, out);
33254
+ if (item && typeof item === "object") addPath(sessionParentPath, item.path, out);
33187
33255
  }
33188
33256
  }
33189
33257
  var PATH_KEYS = [
@@ -33196,56 +33264,56 @@ var PATH_KEYS = [
33196
33264
  "file_path",
33197
33265
  "target_file"
33198
33266
  ];
33199
- function collectFromObject(cwd, obj, out, depth) {
33267
+ function collectFromObject(sessionParentPath, obj, out, depth) {
33200
33268
  if (depth > 10) return;
33201
33269
  for (const k of PATH_KEYS) {
33202
- if (k in obj) addPath(cwd, obj[k], out);
33270
+ if (k in obj) addPath(sessionParentPath, obj[k], out);
33203
33271
  }
33204
33272
  for (const v of Object.values(obj)) {
33205
- if (v != null && typeof v === "object") collectUnknown(cwd, v, out, depth + 1);
33273
+ if (v != null && typeof v === "object") collectUnknown(sessionParentPath, v, out, depth + 1);
33206
33274
  }
33207
33275
  }
33208
- function collectUnknown(cwd, v, out, depth) {
33276
+ function collectUnknown(sessionParentPath, v, out, depth) {
33209
33277
  if (depth > 10 || v == null) return;
33210
33278
  if (Array.isArray(v)) {
33211
- for (const x of v) collectUnknown(cwd, x, out, depth + 1);
33279
+ for (const x of v) collectUnknown(sessionParentPath, x, out, depth + 1);
33212
33280
  return;
33213
33281
  }
33214
- if (typeof v === "object") collectFromObject(cwd, v, out, depth);
33282
+ if (typeof v === "object") collectFromObject(sessionParentPath, v, out, depth);
33215
33283
  }
33216
- function walkContentArray(cwd, content, out) {
33284
+ function walkContentArray(sessionParentPath, content, out) {
33217
33285
  if (!Array.isArray(content)) return;
33218
33286
  for (const item of content) {
33219
33287
  if (!item || typeof item !== "object") continue;
33220
33288
  const o = item;
33221
33289
  if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
33222
- for (const k of PATH_KEYS) if (k in o) addPath(cwd, o[k], out);
33290
+ for (const k of PATH_KEYS) if (k in o) addPath(sessionParentPath, o[k], out);
33223
33291
  }
33224
33292
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
33225
33293
  const inner = o.content;
33226
33294
  if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
33227
- for (const k of PATH_KEYS) if (k in inner) addPath(cwd, inner[k], out);
33295
+ for (const k of PATH_KEYS) if (k in inner) addPath(sessionParentPath, inner[k], out);
33228
33296
  }
33229
33297
  }
33230
33298
  }
33231
33299
  }
33232
- function extractToolTargetDisplayPaths(update, cwd) {
33300
+ function extractToolTargetDisplayPaths(update, sessionParentPath) {
33233
33301
  const out = /* @__PURE__ */ new Set();
33234
33302
  if (!update || typeof update !== "object") return [];
33235
33303
  const u = update;
33236
- walkLocations(cwd, u.locations, out);
33237
- walkLocations(cwd, u.fileLocations, out);
33238
- walkLocations(cwd, u.file_locations, out);
33304
+ walkLocations(sessionParentPath, u.locations, out);
33305
+ walkLocations(sessionParentPath, u.fileLocations, out);
33306
+ walkLocations(sessionParentPath, u.file_locations, out);
33239
33307
  const tc = u.toolCall ?? u.tool_call;
33240
33308
  if (tc && typeof tc.arguments === "string") {
33241
33309
  try {
33242
33310
  const parsed = JSON.parse(tc.arguments);
33243
- collectUnknown(cwd, parsed, out, 0);
33311
+ collectUnknown(sessionParentPath, parsed, out, 0);
33244
33312
  } catch {
33245
33313
  }
33246
33314
  }
33247
- walkContentArray(cwd, u.content, out);
33248
- collectFromObject(cwd, u, out, 0);
33315
+ walkContentArray(sessionParentPath, u.content, out);
33316
+ collectFromObject(sessionParentPath, u, out, 0);
33249
33317
  return [...out];
33250
33318
  }
33251
33319
 
@@ -33311,7 +33379,7 @@ var PathSnapshotTracker = class {
33311
33379
  this.beforeByToolKey.delete(toolKey);
33312
33380
  this.accumulatedPathsByToolKey.delete(toolKey);
33313
33381
  }
33314
- captureBeforeFromDisk(toolKey, paths, cwd) {
33382
+ captureBeforeFromDisk(toolKey, paths, sessionParentPath) {
33315
33383
  if (paths.length === 0) return;
33316
33384
  let m = this.beforeByToolKey.get(toolKey);
33317
33385
  if (!m) {
@@ -33320,10 +33388,10 @@ var PathSnapshotTracker = class {
33320
33388
  }
33321
33389
  for (const p of paths) {
33322
33390
  if (m.has(p)) continue;
33323
- m.set(p, readUtf8WorkspaceFile(cwd, p));
33391
+ m.set(p, readUtf8WorkspaceFile(sessionParentPath, p));
33324
33392
  }
33325
33393
  }
33326
- ensureBeforeFromHeadForMissing(toolKey, paths, cwd) {
33394
+ ensureBeforeFromHeadForMissing(toolKey, paths, sessionParentPath) {
33327
33395
  let m = this.beforeByToolKey.get(toolKey);
33328
33396
  if (!m) {
33329
33397
  m = /* @__PURE__ */ new Map();
@@ -33331,10 +33399,10 @@ var PathSnapshotTracker = class {
33331
33399
  }
33332
33400
  for (const p of paths) {
33333
33401
  if (m.has(p)) continue;
33334
- m.set(p, readGitHeadBlob(cwd, p));
33402
+ m.set(p, readGitHeadBlob(sessionParentPath, p));
33335
33403
  }
33336
33404
  }
33337
- flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
33405
+ flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
33338
33406
  const t = this.debouncers.get(toolKey);
33339
33407
  if (t) clearTimeout(t);
33340
33408
  this.debouncers.delete(toolKey);
@@ -33343,10 +33411,10 @@ var PathSnapshotTracker = class {
33343
33411
  this.beforeByToolKey.delete(toolKey);
33344
33412
  if (!send || !runId || !sessionId) return;
33345
33413
  for (const [displayPath, oldText] of beforeMap) {
33346
- const newText = readUtf8WorkspaceFile(cwd, displayPath);
33414
+ const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
33347
33415
  if (oldText === newText) continue;
33348
33416
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
33349
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
33417
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
33350
33418
  try {
33351
33419
  send({
33352
33420
  type: "session_file_change",
@@ -33365,31 +33433,31 @@ var PathSnapshotTracker = class {
33365
33433
  }
33366
33434
  }
33367
33435
  }
33368
- scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
33436
+ scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
33369
33437
  const prev = this.debouncers.get(toolKey);
33370
33438
  if (prev) clearTimeout(prev);
33371
33439
  this.debouncers.set(
33372
33440
  toolKey,
33373
33441
  setTimeout(() => {
33374
33442
  this.debouncers.delete(toolKey);
33375
- this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
33443
+ this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
33376
33444
  }, PATH_SNAPSHOT_DEBOUNCE_MS)
33377
33445
  );
33378
33446
  }
33379
- handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd, sentPaths, send, runId, sessionId, log2) {
33447
+ handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
33380
33448
  if (updateKind === "tool_call") {
33381
33449
  this.resetToolSnapshots(toolKey);
33382
33450
  }
33383
33451
  if (updateKind === "tool_call") {
33384
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
33452
+ this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
33385
33453
  } else if (updateKind === "tool_call_update") {
33386
33454
  if (isCompletedToolStatus(status)) {
33387
- this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd);
33388
- this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
33455
+ this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, sessionParentPath);
33456
+ this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
33389
33457
  } else {
33390
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
33458
+ this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
33391
33459
  if (this.beforeByToolKey.has(toolKey)) {
33392
- this.scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
33460
+ this.scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
33393
33461
  }
33394
33462
  }
33395
33463
  }
@@ -33397,11 +33465,11 @@ var PathSnapshotTracker = class {
33397
33465
  };
33398
33466
 
33399
33467
  // src/agents/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
33400
- function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentPaths, log2) {
33468
+ function sendExtractedDiffsAsSessionFileChanges(diffs, send, sessionParentPath, sessionId, runId, sentPaths, log2) {
33401
33469
  for (const d of diffs) {
33402
33470
  try {
33403
33471
  const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
33404
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, d.path);
33472
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, d.path);
33405
33473
  send({
33406
33474
  type: "session_file_change",
33407
33475
  sessionId,
@@ -33419,15 +33487,15 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, run
33419
33487
  }
33420
33488
  }
33421
33489
  }
33422
- function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, sessionId, runId, log2) {
33490
+ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, sessionParentPath, sessionId, runId, log2) {
33423
33491
  for (const displayPath of mergedPaths) {
33424
33492
  if (sentPaths.has(displayPath)) continue;
33425
- const oldText = readGitHeadBlob(cwd, displayPath);
33426
- const newText = readUtf8WorkspaceFile(cwd, displayPath);
33493
+ const oldText = readGitHeadBlob(sessionParentPath, displayPath);
33494
+ const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
33427
33495
  if (oldText === newText) continue;
33428
33496
  try {
33429
33497
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
33430
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
33498
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
33431
33499
  send({
33432
33500
  type: "session_file_change",
33433
33501
  sessionId,
@@ -33448,13 +33516,12 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, s
33448
33516
 
33449
33517
  // src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
33450
33518
  function createBridgeOnSessionUpdate(opts) {
33451
- const { routing, getSendSessionUpdate, log: log2 } = opts;
33519
+ const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
33452
33520
  const pathTracker = new PathSnapshotTracker();
33453
33521
  return (params) => {
33454
33522
  const runId = routing.runId;
33455
33523
  const sessionId = routing.sessionId;
33456
33524
  pathTracker.onRunIdChanged(runId);
33457
- const cwd = getBridgeWorkspaceDirectory();
33458
33525
  const send = getSendSessionUpdate();
33459
33526
  const sentFileChangePaths = /* @__PURE__ */ new Set();
33460
33527
  const p = params;
@@ -33462,7 +33529,7 @@ function createBridgeOnSessionUpdate(opts) {
33462
33529
  const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
33463
33530
  const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
33464
33531
  const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
33465
- const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd) : [];
33532
+ const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, sessionParentPath) : [];
33466
33533
  const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
33467
33534
  if (updateKind === "tool_call") {
33468
33535
  pathTracker.resetToolSnapshots(toolKey);
@@ -33477,7 +33544,7 @@ function createBridgeOnSessionUpdate(opts) {
33477
33544
  toolKey,
33478
33545
  toolPaths,
33479
33546
  p.status,
33480
- cwd,
33547
+ sessionParentPath,
33481
33548
  sentFileChangePaths,
33482
33549
  deliver,
33483
33550
  runId ?? "",
@@ -33485,9 +33552,17 @@ function createBridgeOnSessionUpdate(opts) {
33485
33552
  log2
33486
33553
  );
33487
33554
  }
33488
- const diffs = extractAcpFileDiffsFromUpdate(params, cwd);
33555
+ const diffs = extractAcpFileDiffsFromUpdate(params, sessionParentPath);
33489
33556
  if (diffs.length > 0 && send && runId && sessionId) {
33490
- sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentFileChangePaths, log2);
33557
+ sendExtractedDiffsAsSessionFileChanges(
33558
+ diffs,
33559
+ send,
33560
+ sessionParentPath,
33561
+ sessionId,
33562
+ runId,
33563
+ sentFileChangePaths,
33564
+ log2
33565
+ );
33491
33566
  } else if (diffs.length > 0) {
33492
33567
  log2(
33493
33568
  `[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
@@ -33496,7 +33571,15 @@ function createBridgeOnSessionUpdate(opts) {
33496
33571
  if (isCompletedToolCallUpdate && send && runId && sessionId) {
33497
33572
  const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
33498
33573
  const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
33499
- sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd, sessionId, runId, log2);
33574
+ sendGitHeadVsWorkspaceForToolPaths(
33575
+ merged,
33576
+ sentFileChangePaths,
33577
+ send,
33578
+ sessionParentPath,
33579
+ sessionId,
33580
+ runId,
33581
+ log2
33582
+ );
33500
33583
  pathTracker.accumulatedPathsByToolKey.delete(toolKey);
33501
33584
  }
33502
33585
  if (runId && send) {
@@ -33531,11 +33614,12 @@ function buildAcpSessionBridgeHooks(opts) {
33531
33614
 
33532
33615
  // src/agents/acp/ensure-acp-client.ts
33533
33616
  async function ensureAcpClient(options) {
33534
- const { state, preferredAgentType, mode, cwd, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
33535
- const targetCwd = path11.resolve(
33536
- cwd != null && String(cwd).trim() !== "" ? String(cwd).trim() : getBridgeWorkspaceDirectory()
33537
- );
33538
- if (state.acpHandle && state.lastAcpCwd != null && path11.resolve(state.lastAcpCwd) !== path11.resolve(targetCwd)) {
33617
+ const { state, preferredAgentType, mode, sessionParentPath, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
33618
+ const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
33619
+ if (state.acpStartPromise && !state.acpHandle) {
33620
+ await state.acpStartPromise;
33621
+ }
33622
+ if (state.acpHandle && state.lastAcpCwd != null && path12.resolve(state.lastAcpCwd) !== path12.resolve(targetSessionParentPath)) {
33539
33623
  try {
33540
33624
  state.acpHandle.disconnect();
33541
33625
  } catch {
@@ -33567,14 +33651,14 @@ async function ensureAcpClient(options) {
33567
33651
  if (!state.acpStartPromise) {
33568
33652
  let statOk = false;
33569
33653
  try {
33570
- const st = fs10.statSync(targetCwd);
33654
+ const st = fs10.statSync(targetSessionParentPath);
33571
33655
  statOk = st.isDirectory();
33572
33656
  if (!statOk) {
33573
- state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
33657
+ state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
33574
33658
  log2(`[Agent] ${state.lastAcpStartError}`);
33575
33659
  }
33576
33660
  } catch {
33577
- state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetCwd}`;
33661
+ state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetSessionParentPath}`;
33578
33662
  log2(`[Agent] ${state.lastAcpStartError}`);
33579
33663
  }
33580
33664
  if (!statOk) {
@@ -33582,6 +33666,7 @@ async function ensureAcpClient(options) {
33582
33666
  }
33583
33667
  const hooks = buildAcpSessionBridgeHooks({
33584
33668
  routing,
33669
+ sessionParentPath: targetSessionParentPath,
33585
33670
  getSendSessionUpdate: () => sendSessionUpdate,
33586
33671
  getSendRequest: () => sendRequest,
33587
33672
  log: log2
@@ -33589,7 +33674,6 @@ async function ensureAcpClient(options) {
33589
33674
  state.acpStartPromise = resolved.createClient({
33590
33675
  command: resolved.command,
33591
33676
  sessionMode: mode,
33592
- cwd: targetCwd,
33593
33677
  backendAgentType: preferredAgentType,
33594
33678
  onAgentSubprocessExit: () => {
33595
33679
  state.acpHandle = null;
@@ -33597,11 +33681,12 @@ async function ensureAcpClient(options) {
33597
33681
  state.acpAgentKey = null;
33598
33682
  state.lastAcpStartError = "Agent subprocess exited";
33599
33683
  },
33600
- ...hooks
33684
+ ...hooks,
33685
+ cwd: targetSessionParentPath
33601
33686
  }).then((h) => {
33602
33687
  state.lastAcpStartError = null;
33603
33688
  state.acpHandle = h;
33604
- state.lastAcpCwd = targetCwd;
33689
+ state.lastAcpCwd = targetSessionParentPath;
33605
33690
  state.acpAgentKey = agentKey;
33606
33691
  return h;
33607
33692
  }).catch((err) => {
@@ -33650,7 +33735,7 @@ async function createAcpManager(options) {
33650
33735
  runId,
33651
33736
  mode,
33652
33737
  agentType,
33653
- cwd,
33738
+ sessionParentPath,
33654
33739
  sendResult: sendResult2,
33655
33740
  sendSessionUpdate,
33656
33741
  followUpCatalogPromptId,
@@ -33669,7 +33754,7 @@ async function createAcpManager(options) {
33669
33754
  state,
33670
33755
  preferredAgentType: preferredForPrompt,
33671
33756
  mode,
33672
- cwd,
33757
+ sessionParentPath,
33673
33758
  routing: promptRouting,
33674
33759
  sendSessionUpdate,
33675
33760
  sendRequest: sendSessionUpdate,
@@ -33718,7 +33803,7 @@ async function createAcpManager(options) {
33718
33803
  sessionId,
33719
33804
  runId,
33720
33805
  agentType: preferredForPrompt,
33721
- agentCwd: cwd,
33806
+ agentCwd: resolveSessionParentPathForAgentProcess(sessionParentPath),
33722
33807
  sendResult: sendResult2,
33723
33808
  sendSessionUpdate,
33724
33809
  log: log2,
@@ -33773,12 +33858,12 @@ async function createAcpManager(options) {
33773
33858
  }
33774
33859
 
33775
33860
  // src/worktrees/session-worktree-manager.ts
33776
- import * as path18 from "node:path";
33861
+ import * as path19 from "node:path";
33777
33862
  import os4 from "node:os";
33778
33863
 
33779
33864
  // src/worktrees/prepare-new-session-worktrees.ts
33780
33865
  import * as fs12 from "node:fs";
33781
- import * as path13 from "node:path";
33866
+ import * as path14 from "node:path";
33782
33867
 
33783
33868
  // src/git/worktree-add.ts
33784
33869
  async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
@@ -33788,11 +33873,11 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
33788
33873
 
33789
33874
  // src/worktrees/worktree-layout-file.ts
33790
33875
  import * as fs11 from "node:fs";
33791
- import * as path12 from "node:path";
33876
+ import * as path13 from "node:path";
33792
33877
  import os3 from "node:os";
33793
33878
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
33794
33879
  function defaultWorktreeLayoutPath() {
33795
- return path12.join(os3.homedir(), ".buildautomaton", LAYOUT_FILENAME);
33880
+ return path13.join(os3.homedir(), ".buildautomaton", LAYOUT_FILENAME);
33796
33881
  }
33797
33882
  function normalizeLoadedLayout(raw) {
33798
33883
  if (raw && typeof raw === "object" && "launcherCwds" in raw) {
@@ -33813,24 +33898,24 @@ function loadWorktreeLayout() {
33813
33898
  }
33814
33899
  function saveWorktreeLayout(layout) {
33815
33900
  try {
33816
- const dir = path12.dirname(defaultWorktreeLayoutPath());
33901
+ const dir = path13.dirname(defaultWorktreeLayoutPath());
33817
33902
  fs11.mkdirSync(dir, { recursive: true });
33818
33903
  fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
33819
33904
  } catch {
33820
33905
  }
33821
33906
  }
33822
- function baseNameSafe(abs) {
33823
- return path12.basename(abs).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
33907
+ function baseNameSafe(pathString) {
33908
+ return path13.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
33824
33909
  }
33825
- function getLauncherDirNameIfPresent(layout, launcherCwdAbs) {
33826
- const norm = path12.resolve(launcherCwdAbs);
33827
- const existing = layout.launcherCwds.find((e) => path12.resolve(e.absolutePath) === norm);
33910
+ function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
33911
+ const norm = path13.resolve(bridgeRootPath2);
33912
+ const existing = layout.launcherCwds.find((e) => path13.resolve(e.absolutePath) === norm);
33828
33913
  return existing?.dirName;
33829
33914
  }
33830
- function allocateDirNameForLauncherCwd(layout, launcherCwdAbs) {
33831
- const existing = getLauncherDirNameIfPresent(layout, launcherCwdAbs);
33915
+ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
33916
+ const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
33832
33917
  if (existing) return existing;
33833
- const norm = path12.resolve(launcherCwdAbs);
33918
+ const norm = path13.resolve(bridgeRootPath2);
33834
33919
  const base = baseNameSafe(norm);
33835
33920
  const used = new Set(layout.launcherCwds.map((e) => e.dirName));
33836
33921
  let name = base;
@@ -33846,24 +33931,27 @@ function allocateDirNameForLauncherCwd(layout, launcherCwdAbs) {
33846
33931
 
33847
33932
  // src/worktrees/prepare-new-session-worktrees.ts
33848
33933
  async function prepareNewSessionWorktrees(options) {
33849
- const { rootAbs, launcherCwd, sessionId, layout, log: log2 } = options;
33850
- const launcherResolved = path13.resolve(launcherCwd);
33851
- const cwdKey = allocateDirNameForLauncherCwd(layout, launcherResolved);
33852
- const agentMirrorRoot = path13.join(rootAbs, cwdKey);
33853
- const repos = await discoverGitReposUnderRoot(launcherResolved);
33934
+ const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
33935
+ const bridgeResolved = path14.resolve(bridgeRoot);
33936
+ const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
33937
+ const bridgeKeyDir = path14.join(worktreesRootPath, cwdKey);
33938
+ const sessionDir = path14.join(bridgeKeyDir, sessionId);
33939
+ const repos = await discoverGitReposUnderRoot(bridgeResolved);
33854
33940
  if (repos.length === 0) {
33855
- log2("[worktrees] No Git repositories under launcher working directory; skipping worktree creation.");
33941
+ log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
33856
33942
  return null;
33857
33943
  }
33858
33944
  const branch = `session-${sessionId}`;
33859
33945
  const worktreePaths = [];
33860
- fs12.mkdirSync(agentMirrorRoot, { recursive: true });
33946
+ fs12.mkdirSync(sessionDir, { recursive: true });
33861
33947
  for (const repo of repos) {
33862
- let rel = path13.relative(launcherResolved, repo.absolutePath);
33863
- if (rel.startsWith("..") || path13.isAbsolute(rel)) continue;
33948
+ let rel = path14.relative(bridgeResolved, repo.absolutePath);
33949
+ if (rel.startsWith("..") || path14.isAbsolute(rel)) continue;
33864
33950
  const relNorm = rel === "" ? "." : rel;
33865
- const wtPath = path13.join(agentMirrorRoot, relNorm, sessionId);
33866
- fs12.mkdirSync(path13.dirname(wtPath), { recursive: true });
33951
+ const wtPath = relNorm === "." ? sessionDir : path14.join(sessionDir, relNorm);
33952
+ if (relNorm !== ".") {
33953
+ fs12.mkdirSync(path14.dirname(wtPath), { recursive: true });
33954
+ }
33867
33955
  try {
33868
33956
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
33869
33957
  log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
@@ -33875,7 +33963,11 @@ async function prepareNewSessionWorktrees(options) {
33875
33963
  }
33876
33964
  }
33877
33965
  if (worktreePaths.length === 0) return null;
33878
- return { worktreePaths, agentCwd: agentMirrorRoot };
33966
+ return {
33967
+ worktreePaths,
33968
+ sessionParentPath: sessionDir,
33969
+ workingTreeRelRoot: sessionDir
33970
+ };
33879
33971
  }
33880
33972
 
33881
33973
  // src/git/rename-branch.ts
@@ -33907,16 +33999,16 @@ import * as fs14 from "node:fs";
33907
33999
 
33908
34000
  // src/git/resolve-main-repo-from-git-file.ts
33909
34001
  import * as fs13 from "node:fs";
33910
- import * as path14 from "node:path";
34002
+ import * as path15 from "node:path";
33911
34003
  function resolveMainRepoFromWorktreeGitFile(wt) {
33912
- const gitDirFile = path14.join(wt, ".git");
34004
+ const gitDirFile = path15.join(wt, ".git");
33913
34005
  if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
33914
34006
  const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
33915
34007
  const m = first2.match(/^gitdir:\s*(.+)$/im);
33916
34008
  if (!m) return "";
33917
- const gitWorktreePath = path14.resolve(wt, m[1].trim());
33918
- const gitDir = path14.dirname(path14.dirname(gitWorktreePath));
33919
- return path14.dirname(gitDir);
34009
+ const gitWorktreePath = path15.resolve(wt, m[1].trim());
34010
+ const gitDir = path15.dirname(path15.dirname(gitWorktreePath));
34011
+ return path15.dirname(gitDir);
33920
34012
  }
33921
34013
 
33922
34014
  // src/git/worktree-remove.ts
@@ -34158,7 +34250,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
34158
34250
  }
34159
34251
 
34160
34252
  // src/git/working-directory/changes/get-working-tree-change-repo-details.ts
34161
- import * as path16 from "node:path";
34253
+ import * as path17 from "node:path";
34162
34254
 
34163
34255
  // src/git/working-directory/changes/parse-git-status.ts
34164
34256
  function parseNameStatusLines(lines) {
@@ -34279,16 +34371,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
34279
34371
 
34280
34372
  // src/git/working-directory/changes/list-changed-files-for-repo.ts
34281
34373
  import * as fs17 from "node:fs";
34282
- import * as path15 from "node:path";
34374
+ import * as path16 from "node:path";
34283
34375
 
34284
34376
  // src/git/working-directory/changes/count-lines.ts
34285
34377
  import { createReadStream } from "node:fs";
34286
34378
  import * as readline2 from "node:readline";
34287
- async function countTextFileLines(absFile) {
34379
+ async function countTextFileLines(filePath) {
34288
34380
  let bytes = 0;
34289
34381
  const maxBytes = 512e3;
34290
34382
  let lines = 0;
34291
- const stream = createReadStream(absFile, { encoding: "utf8" });
34383
+ const stream = createReadStream(filePath, { encoding: "utf8" });
34292
34384
  const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
34293
34385
  for await (const _line of rl) {
34294
34386
  lines += 1;
@@ -34316,15 +34408,15 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
34316
34408
  return null;
34317
34409
  }
34318
34410
  }
34319
- async function readWorktreeFileLines(abs) {
34411
+ async function readWorktreeFileLines(filePath) {
34320
34412
  try {
34321
- const raw = await fs16.promises.readFile(abs, "utf8");
34413
+ const raw = await fs16.promises.readFile(filePath, "utf8");
34322
34414
  return raw.split(/\r?\n/);
34323
34415
  } catch {
34324
34416
  return null;
34325
34417
  }
34326
34418
  }
34327
- async function hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pathInRepo, change) {
34419
+ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, change) {
34328
34420
  if (!patch.trim() || patch.includes("Binary files")) return patch;
34329
34421
  const all = patch.split("\n");
34330
34422
  const out = [];
@@ -34341,7 +34433,7 @@ async function hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pa
34341
34433
  };
34342
34434
  const diskLines = async () => {
34343
34435
  if (diskCache !== void 0) return diskCache;
34344
- diskCache = await readWorktreeFileLines(absFile);
34436
+ diskCache = await readWorktreeFileLines(filePath);
34345
34437
  return diskCache;
34346
34438
  };
34347
34439
  while (i < all.length) {
@@ -34453,7 +34545,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
34453
34545
  const rows = [];
34454
34546
  for (const pathInRepo of paths) {
34455
34547
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
34456
- const abs = path15.join(repoGitCwd, pathInRepo);
34548
+ const repoFilePath = path16.join(repoGitCwd, pathInRepo);
34457
34549
  const nums = numByPath.get(pathInRepo);
34458
34550
  let additions = nums?.additions ?? 0;
34459
34551
  let deletions = nums?.deletions ?? 0;
@@ -34466,8 +34558,8 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
34466
34558
  deletions = fromGit.deletions;
34467
34559
  } else {
34468
34560
  try {
34469
- const st = await fs17.promises.stat(abs);
34470
- if (st.isFile()) additions = await countTextFileLines(abs);
34561
+ const st = await fs17.promises.stat(repoFilePath);
34562
+ if (st.isFile()) additions = await countTextFileLines(repoFilePath);
34471
34563
  else additions = 0;
34472
34564
  } catch {
34473
34565
  additions = 0;
@@ -34492,10 +34584,10 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
34492
34584
  } else {
34493
34585
  pathInRepo = row.pathRelLauncher;
34494
34586
  }
34495
- const absFile = path15.join(repoGitCwd, pathInRepo);
34587
+ const filePath = path16.join(repoGitCwd, pathInRepo);
34496
34588
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
34497
34589
  if (patch) {
34498
- patch = await hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pathInRepo, row.change);
34590
+ patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
34499
34591
  }
34500
34592
  row.patchContent = patch;
34501
34593
  }
@@ -34508,8 +34600,9 @@ function normRepoRel(p) {
34508
34600
  return x === "" ? "." : x;
34509
34601
  }
34510
34602
  async function getWorkingTreeChangeRepoDetails(options) {
34511
- const launcher = path16.resolve(getBridgeWorkspaceDirectory());
34512
- const mirror = options.agentMirrorRootAbs ? path16.resolve(options.agentMirrorRootAbs) : null;
34603
+ const bridgeRoot = path17.resolve(getBridgeRoot());
34604
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path17.resolve(options.sessionWorktreeRootPath) : null;
34605
+ const legacyNested = options.legacyRepoNestedSessionLayout === true;
34513
34606
  const out = [];
34514
34607
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
34515
34608
  const basisInput = options.basis ?? { kind: "working" };
@@ -34520,8 +34613,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
34520
34613
  throw new Error("commit sha is required for commit changes");
34521
34614
  }
34522
34615
  const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
34523
- for (const target of options.commitTargetAbsDirs) {
34524
- const t = path16.resolve(target);
34616
+ for (const target of options.commitTargetPaths) {
34617
+ const t = path17.resolve(target);
34525
34618
  if (!await isGitRepoDirectory(t)) continue;
34526
34619
  const g = simpleGit(t);
34527
34620
  let branch = "HEAD";
@@ -34533,8 +34626,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
34533
34626
  const remoteUrl = await getRemoteOriginUrl(t);
34534
34627
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
34535
34628
  let repoRelPath;
34536
- if (mirror) {
34537
- const relNorm = path16.relative(mirror, path16.dirname(t));
34629
+ if (sessionWtRoot) {
34630
+ const anchor = legacyNested ? path17.dirname(t) : t;
34631
+ const relNorm = path17.relative(sessionWtRoot, anchor);
34538
34632
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
34539
34633
  } else {
34540
34634
  let top = t;
@@ -34543,8 +34637,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
34543
34637
  } catch {
34544
34638
  top = t;
34545
34639
  }
34546
- const rel = path16.relative(launcher, path16.resolve(top)).replace(/\\/g, "/") || ".";
34547
- repoRelPath = rel.startsWith("..") ? path16.basename(path16.resolve(top)) : rel;
34640
+ const rel = path17.relative(bridgeRoot, path17.resolve(top)).replace(/\\/g, "/") || ".";
34641
+ repoRelPath = rel.startsWith("..") ? path17.basename(path17.resolve(top)) : rel;
34548
34642
  }
34549
34643
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
34550
34644
  if (filter && norm !== filter) continue;
@@ -34610,14 +34704,37 @@ async function commitSessionWorktrees(options) {
34610
34704
 
34611
34705
  // src/worktrees/discover-session-worktree-on-disk.ts
34612
34706
  import * as fs18 from "node:fs";
34613
- import * as path17 from "node:path";
34614
- function isGitDir(abs) {
34707
+ import * as path18 from "node:path";
34708
+ function isGitDir(dirPath) {
34615
34709
  try {
34616
- return fs18.existsSync(path17.join(abs, ".git"));
34710
+ return fs18.existsSync(path18.join(dirPath, ".git"));
34617
34711
  } catch {
34618
34712
  return false;
34619
34713
  }
34620
34714
  }
34715
+ function collectGitRepoRootsUnderDirectory(rootPath) {
34716
+ const out = [];
34717
+ const walk = (dir) => {
34718
+ if (isGitDir(dir)) {
34719
+ out.push(path18.resolve(dir));
34720
+ return;
34721
+ }
34722
+ let entries;
34723
+ try {
34724
+ entries = fs18.readdirSync(dir, { withFileTypes: true });
34725
+ } catch {
34726
+ return;
34727
+ }
34728
+ for (const e of entries) {
34729
+ if (e.name.startsWith(".")) continue;
34730
+ const full = path18.join(dir, e.name);
34731
+ if (!e.isDirectory()) continue;
34732
+ walk(full);
34733
+ }
34734
+ };
34735
+ walk(path18.resolve(rootPath));
34736
+ return [...new Set(out)];
34737
+ }
34621
34738
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
34622
34739
  const out = [];
34623
34740
  const walk = (dir, depth) => {
@@ -34630,10 +34747,10 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
34630
34747
  }
34631
34748
  for (const e of entries) {
34632
34749
  if (e.name.startsWith(".")) continue;
34633
- const full = path17.join(dir, e.name);
34750
+ const full = path18.join(dir, e.name);
34634
34751
  if (!e.isDirectory()) continue;
34635
34752
  if (e.name === sessionId) {
34636
- if (isGitDir(full)) out.push(path17.resolve(full));
34753
+ if (isGitDir(full)) out.push(path18.resolve(full));
34637
34754
  } else {
34638
34755
  walk(full, depth + 1);
34639
34756
  }
@@ -34642,16 +34759,55 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
34642
34759
  walk(root, 0);
34643
34760
  return out;
34644
34761
  }
34762
+ function tryBindingFromSessionDirectory(sessionDir) {
34763
+ let st;
34764
+ try {
34765
+ st = fs18.statSync(sessionDir);
34766
+ } catch {
34767
+ return null;
34768
+ }
34769
+ if (!st.isDirectory()) return null;
34770
+ const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
34771
+ if (worktreePaths.length === 0) return null;
34772
+ const abs = path18.resolve(sessionDir);
34773
+ return {
34774
+ sessionParentPath: abs,
34775
+ workingTreeRelRoot: abs,
34776
+ repoCheckoutPaths: worktreePaths
34777
+ };
34778
+ }
34779
+ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
34780
+ const sid = sessionId.trim();
34781
+ if (!sid) return null;
34782
+ const hintR = path18.resolve(checkoutPath);
34783
+ let best = null;
34784
+ let cur = path18.dirname(hintR);
34785
+ for (let i = 0; i < 40; i++) {
34786
+ const paths = collectWorktreeRootsNamed(cur, sid, 24);
34787
+ if (paths.some((p) => path18.resolve(p) === hintR)) {
34788
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path18.resolve(paths[0]);
34789
+ best = {
34790
+ sessionParentPath: path18.resolve(isolated),
34791
+ workingTreeRelRoot: path18.resolve(cur),
34792
+ repoCheckoutPaths: paths.map((p) => path18.resolve(p))
34793
+ };
34794
+ }
34795
+ const next = path18.dirname(cur);
34796
+ if (next === cur) break;
34797
+ cur = next;
34798
+ }
34799
+ return best;
34800
+ }
34645
34801
  function discoverSessionWorktreeOnDisk(options) {
34646
- const { sessionId, worktreesRootAbs, layout, launcherCwd } = options;
34647
- if (!sessionId.trim() || !fs18.existsSync(worktreesRootAbs)) return null;
34648
- const preferredKey = getLauncherDirNameIfPresent(layout, launcherCwd);
34802
+ const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
34803
+ if (!sessionId.trim() || !fs18.existsSync(worktreesRootPath)) return null;
34804
+ const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
34649
34805
  const keys = [];
34650
34806
  if (preferredKey) keys.push(preferredKey);
34651
34807
  try {
34652
- for (const name of fs18.readdirSync(worktreesRootAbs)) {
34808
+ for (const name of fs18.readdirSync(worktreesRootPath)) {
34653
34809
  if (name.startsWith(".")) continue;
34654
- const p = path17.join(worktreesRootAbs, name);
34810
+ const p = path18.join(worktreesRootPath, name);
34655
34811
  if (!fs18.statSync(p).isDirectory()) continue;
34656
34812
  if (name !== preferredKey) keys.push(name);
34657
34813
  }
@@ -34659,26 +34815,58 @@ function discoverSessionWorktreeOnDisk(options) {
34659
34815
  return null;
34660
34816
  }
34661
34817
  for (const key of keys) {
34662
- const mirrorRoot = path17.join(worktreesRootAbs, key);
34663
- if (!fs18.existsSync(mirrorRoot) || !fs18.statSync(mirrorRoot).isDirectory()) continue;
34664
- const worktreePaths = collectWorktreeRootsNamed(mirrorRoot, sessionId, 24);
34665
- if (worktreePaths.length > 0) {
34666
- return { agentCwd: path17.resolve(mirrorRoot), worktreePaths };
34818
+ const layoutRoot = path18.join(worktreesRootPath, key);
34819
+ if (!fs18.existsSync(layoutRoot) || !fs18.statSync(layoutRoot).isDirectory()) continue;
34820
+ const sessionDir = path18.join(layoutRoot, sessionId);
34821
+ const nested = tryBindingFromSessionDirectory(sessionDir);
34822
+ if (nested) return nested;
34823
+ const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
34824
+ if (legacyPaths.length > 0) {
34825
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path18.resolve(legacyPaths[0]);
34826
+ return {
34827
+ sessionParentPath: path18.resolve(isolated),
34828
+ workingTreeRelRoot: path18.resolve(layoutRoot),
34829
+ repoCheckoutPaths: legacyPaths.map((p) => path18.resolve(p))
34830
+ };
34667
34831
  }
34668
34832
  }
34669
34833
  return null;
34670
34834
  }
34671
- function discoverSessionWorktreesUnderMirrorRoot(mirrorRootAbs, sessionId) {
34672
- const mirrorRoot = path17.resolve(mirrorRootAbs);
34673
- if (!sessionId.trim() || !fs18.existsSync(mirrorRoot)) return null;
34835
+ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
34836
+ const sid = sessionId.trim();
34837
+ if (!sid) return null;
34838
+ const hint = path18.resolve(sessionWorktreeRootPathOrHint);
34839
+ const underHint = tryBindingFromSessionDirectory(path18.join(hint, sid));
34840
+ if (underHint) return underHint;
34841
+ const direct = tryBindingFromSessionDirectory(hint);
34842
+ if (direct) {
34843
+ if (path18.basename(hint) === sid && isGitDir(hint)) {
34844
+ const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
34845
+ if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
34846
+ return legacyFromCheckout;
34847
+ }
34848
+ }
34849
+ return direct;
34850
+ }
34851
+ if (path18.basename(hint) === sid && isGitDir(hint)) {
34852
+ const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
34853
+ if (legacyFromCheckout) return legacyFromCheckout;
34854
+ }
34855
+ let st;
34674
34856
  try {
34675
- if (!fs18.statSync(mirrorRoot).isDirectory()) return null;
34857
+ st = fs18.statSync(hint);
34676
34858
  } catch {
34677
34859
  return null;
34678
34860
  }
34679
- const worktreePaths = collectWorktreeRootsNamed(mirrorRoot, sessionId, 24);
34680
- if (worktreePaths.length === 0) return null;
34681
- return { agentCwd: mirrorRoot, worktreePaths };
34861
+ if (!st.isDirectory()) return null;
34862
+ const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
34863
+ if (legacyPaths.length === 0) return null;
34864
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path18.resolve(legacyPaths[0]);
34865
+ return {
34866
+ sessionParentPath: path18.resolve(isolated),
34867
+ workingTreeRelRoot: hint,
34868
+ repoCheckoutPaths: legacyPaths.map((p) => path18.resolve(p))
34869
+ };
34682
34870
  }
34683
34871
 
34684
34872
  // src/worktrees/session-worktree-manager.ts
@@ -34688,42 +34876,86 @@ function parseSessionParent(v) {
34688
34876
  return null;
34689
34877
  }
34690
34878
  var SessionWorktreeManager = class {
34691
- rootAbs;
34879
+ worktreesRootPath;
34692
34880
  log;
34693
- sessionPaths = /* @__PURE__ */ new Map();
34694
- sessionAgentCwd = /* @__PURE__ */ new Map();
34881
+ sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
34882
+ sessionParentPathBySession = /* @__PURE__ */ new Map();
34883
+ sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
34695
34884
  layout;
34696
34885
  constructor(options) {
34697
- this.rootAbs = options.worktreesRootAbs;
34886
+ this.worktreesRootPath = options.worktreesRootPath;
34698
34887
  this.log = options.log;
34699
34888
  this.layout = loadWorktreeLayout();
34700
34889
  }
34701
- rememberWorktrees(sessionId, agentCwd, worktreePaths) {
34702
- this.sessionPaths.set(sessionId, worktreePaths);
34703
- this.sessionAgentCwd.set(sessionId, path18.resolve(agentCwd));
34890
+ rememberSessionWorktrees(sessionId, binding) {
34891
+ const paths = binding.repoCheckoutPaths.map((p) => path19.resolve(p));
34892
+ this.sessionRepoCheckoutPaths.set(sessionId, paths);
34893
+ this.sessionParentPathBySession.set(sessionId, path19.resolve(binding.sessionParentPath));
34894
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path19.resolve(binding.workingTreeRelRoot));
34895
+ }
34896
+ sessionParentPathAfterRemember(sessionId) {
34897
+ return this.sessionParentPathBySession.get(sessionId);
34704
34898
  }
34705
34899
  tryDiscoverFromDisk(sessionId) {
34706
34900
  return discoverSessionWorktreeOnDisk({
34707
34901
  sessionId,
34708
- worktreesRootAbs: this.rootAbs,
34902
+ worktreesRootPath: this.worktreesRootPath,
34709
34903
  layout: this.layout,
34710
- launcherCwd: getBridgeWorkspaceDirectory()
34904
+ bridgeRoot: getBridgeRoot()
34711
34905
  });
34712
34906
  }
34907
+ isLegacyNestedLayout(sessionId) {
34908
+ const parent = this.sessionParentPathBySession.get(sessionId);
34909
+ const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
34910
+ if (!parent || !relRoot) return false;
34911
+ return path19.resolve(parent) !== path19.resolve(relRoot);
34912
+ }
34913
+ /**
34914
+ * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
34915
+ */
34916
+ getIsolatedSessionParentPathForSession(sessionId) {
34917
+ if (!sessionId) return null;
34918
+ const sid = sessionId.trim();
34919
+ const cached2 = this.sessionParentPathBySession.get(sid);
34920
+ if (cached2) return path19.resolve(cached2);
34921
+ const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
34922
+ if (!paths?.length) return null;
34923
+ return resolveIsolatedSessionParentPathFromCheckouts(paths);
34924
+ }
34713
34925
  /**
34714
- * Returns cwd for the agent (mirror of launcher tree), or undefined to use the bridge workspace directory.
34926
+ * Resolved **session parent path** for the agent: session directory in worktrees mode,
34927
+ * or `undefined` meaning use {@link getBridgeRoot}.
34715
34928
  */
34716
- async resolveCwdForPrompt(sessionId, opts) {
34929
+ async resolveSessionParentPathForPrompt(sessionId, opts) {
34717
34930
  if (!sessionId) return void 0;
34931
+ const sid = sessionId.trim();
34718
34932
  const parentPathRaw = opts.sessionParentPath?.trim();
34719
34933
  if (parentPathRaw) {
34720
- const agentCwd = path18.resolve(parentPathRaw);
34721
- const sid = sessionId?.trim();
34934
+ const resolved = path19.resolve(parentPathRaw);
34722
34935
  if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
34723
- const fromMirror = discoverSessionWorktreesUnderMirrorRoot(agentCwd, sid);
34724
- if (fromMirror) this.rememberWorktrees(sid, fromMirror.agentCwd, fromMirror.worktreePaths);
34936
+ const diskFirst = this.tryDiscoverFromDisk(sid);
34937
+ if (diskFirst) {
34938
+ this.rememberSessionWorktrees(sid, diskFirst);
34939
+ return this.sessionParentPathAfterRemember(sid);
34940
+ }
34941
+ const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
34942
+ if (fromRoot) {
34943
+ this.rememberSessionWorktrees(sid, fromRoot);
34944
+ return this.sessionParentPathAfterRemember(sid);
34945
+ }
34946
+ let cur = resolved;
34947
+ for (let i = 0; i < 16; i++) {
34948
+ const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
34949
+ if (tryRoot) {
34950
+ this.rememberSessionWorktrees(sid, tryRoot);
34951
+ return this.sessionParentPathAfterRemember(sid);
34952
+ }
34953
+ const next = path19.dirname(cur);
34954
+ if (next === cur) break;
34955
+ cur = next;
34956
+ }
34725
34957
  }
34726
- return agentCwd;
34958
+ return resolved;
34727
34959
  }
34728
34960
  const parentKind = parseSessionParent(opts.sessionParent);
34729
34961
  if (parentKind === "bridge_root") {
@@ -34731,78 +34963,102 @@ var SessionWorktreeManager = class {
34731
34963
  }
34732
34964
  if (parentKind === "worktrees_root") {
34733
34965
  if (!opts.isNewSession) {
34734
- const cached2 = this.sessionAgentCwd.get(sessionId);
34735
- if (cached2) return path18.resolve(cached2);
34736
- const disc = this.tryDiscoverFromDisk(sessionId);
34966
+ const cached2 = this.sessionParentPathAfterRemember(sid);
34967
+ if (cached2) return cached2;
34968
+ const disc = this.tryDiscoverFromDisk(sid);
34737
34969
  if (disc) {
34738
- this.rememberWorktrees(sessionId, disc.agentCwd, disc.worktreePaths);
34739
- return path18.resolve(disc.agentCwd);
34970
+ this.rememberSessionWorktrees(sid, disc);
34971
+ return this.sessionParentPathAfterRemember(sid);
34740
34972
  }
34741
34973
  return void 0;
34742
34974
  }
34743
34975
  const prep2 = await prepareNewSessionWorktrees({
34744
- rootAbs: this.rootAbs,
34745
- launcherCwd: getBridgeWorkspaceDirectory(),
34746
- sessionId,
34976
+ worktreesRootPath: this.worktreesRootPath,
34977
+ bridgeRoot: getBridgeRoot(),
34978
+ sessionId: sid,
34747
34979
  layout: this.layout,
34748
34980
  log: this.log
34749
34981
  });
34750
34982
  if (!prep2) return void 0;
34751
- this.rememberWorktrees(sessionId, prep2.agentCwd, prep2.worktreePaths);
34752
- return path18.resolve(prep2.agentCwd);
34983
+ this.rememberSessionWorktrees(sid, {
34984
+ sessionParentPath: prep2.sessionParentPath,
34985
+ workingTreeRelRoot: prep2.workingTreeRelRoot,
34986
+ repoCheckoutPaths: prep2.worktreePaths
34987
+ });
34988
+ return this.sessionParentPathAfterRemember(sid);
34753
34989
  }
34754
34990
  if (!opts.isNewSession) {
34755
- const cached2 = this.sessionAgentCwd.get(sessionId);
34756
- if (cached2) return path18.resolve(cached2);
34757
- const disc = this.tryDiscoverFromDisk(sessionId);
34991
+ const cached2 = this.sessionParentPathAfterRemember(sid);
34992
+ if (cached2) return cached2;
34993
+ const disc = this.tryDiscoverFromDisk(sid);
34758
34994
  if (disc) {
34759
- this.rememberWorktrees(sessionId, disc.agentCwd, disc.worktreePaths);
34760
- return path18.resolve(disc.agentCwd);
34995
+ this.rememberSessionWorktrees(sid, disc);
34996
+ return this.sessionParentPathAfterRemember(sid);
34761
34997
  }
34762
34998
  return void 0;
34763
34999
  }
34764
35000
  const prep = await prepareNewSessionWorktrees({
34765
- rootAbs: this.rootAbs,
34766
- launcherCwd: getBridgeWorkspaceDirectory(),
34767
- sessionId,
35001
+ worktreesRootPath: this.worktreesRootPath,
35002
+ bridgeRoot: getBridgeRoot(),
35003
+ sessionId: sid,
34768
35004
  layout: this.layout,
34769
35005
  log: this.log
34770
35006
  });
34771
35007
  if (!prep) return void 0;
34772
- this.rememberWorktrees(sessionId, prep.agentCwd, prep.worktreePaths);
34773
- return path18.resolve(prep.agentCwd);
35008
+ this.rememberSessionWorktrees(sid, {
35009
+ sessionParentPath: prep.sessionParentPath,
35010
+ workingTreeRelRoot: prep.workingTreeRelRoot,
35011
+ repoCheckoutPaths: prep.worktreePaths
35012
+ });
35013
+ return this.sessionParentPathAfterRemember(sid);
34774
35014
  }
34775
35015
  async renameSessionBranch(sessionId, newBranch) {
34776
- const paths = this.sessionPaths.get(sessionId);
35016
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
34777
35017
  if (!paths?.length) return;
34778
35018
  await renameSessionWorktreeBranches(paths, newBranch, this.log);
34779
35019
  }
34780
- /** True when this session runs in an isolated worktree mirror (not launcher cwd). */
35020
+ /** True when this session uses an isolated worktree layout (not the bridge root). */
34781
35021
  usesWorktreeSession(sessionId) {
34782
35022
  if (!sessionId) return false;
34783
- return this.sessionAgentCwd.has(sessionId);
35023
+ return this.sessionParentPathBySession.has(sessionId);
34784
35024
  }
34785
- getWorktreePathsForSession(sessionId) {
35025
+ /** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
35026
+ getRepoCheckoutPathsForSession(sessionId) {
34786
35027
  if (!sessionId) return void 0;
34787
- const paths = this.sessionPaths.get(sessionId);
35028
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
34788
35029
  return paths?.length ? [...paths] : void 0;
34789
35030
  }
34790
- /** Session mirror root (parent of per-repo worktrees), when using worktrees for this session. */
34791
- getAgentCwdForSession(sessionId) {
34792
- if (!sessionId) return null;
34793
- const c = this.sessionAgentCwd.get(sessionId);
34794
- return c ? path18.resolve(c) : null;
35031
+ /**
35032
+ * Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
35033
+ * restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
35034
+ */
35035
+ ensureRepoCheckoutPathsForSession(sessionId) {
35036
+ if (!sessionId?.trim()) return void 0;
35037
+ const sid = sessionId.trim();
35038
+ const cached2 = this.sessionRepoCheckoutPaths.get(sid);
35039
+ if (cached2?.length) return [...cached2];
35040
+ const disc = this.tryDiscoverFromDisk(sid);
35041
+ if (disc?.repoCheckoutPaths.length) {
35042
+ this.rememberSessionWorktrees(sid, disc);
35043
+ return [...disc.repoCheckoutPaths];
35044
+ }
35045
+ return void 0;
35046
+ }
35047
+ /** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
35048
+ getSessionWorktreeRootForSession(sessionId) {
35049
+ return this.getIsolatedSessionParentPathForSession(sessionId);
34795
35050
  }
34796
35051
  async removeSessionWorktrees(sessionId) {
34797
- const paths = this.sessionPaths.get(sessionId);
34798
- this.sessionPaths.delete(sessionId);
34799
- this.sessionAgentCwd.delete(sessionId);
35052
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
35053
+ this.sessionRepoCheckoutPaths.delete(sessionId);
35054
+ this.sessionParentPathBySession.delete(sessionId);
35055
+ this.sessionWorkingTreeRelRootBySession.delete(sessionId);
34800
35056
  if (!paths?.length) return;
34801
35057
  await removeSessionWorktrees(paths, this.log);
34802
35058
  }
34803
35059
  async commitSession(params) {
34804
- const paths = this.sessionPaths.get(params.sessionId);
34805
- const targets = paths?.length ? paths : [getBridgeWorkspaceDirectory()];
35060
+ const paths = this.sessionRepoCheckoutPaths.get(params.sessionId);
35061
+ const targets = paths?.length ? paths : [getBridgeRoot()];
34806
35062
  return commitSessionWorktrees({
34807
35063
  paths: targets,
34808
35064
  branch: params.branch,
@@ -34811,14 +35067,14 @@ var SessionWorktreeManager = class {
34811
35067
  });
34812
35068
  }
34813
35069
  resolveCommitTargets(sessionId) {
34814
- const paths = this.sessionPaths.get(sessionId);
35070
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
34815
35071
  if (paths?.length) return paths;
34816
35072
  const disc = this.tryDiscoverFromDisk(sessionId);
34817
- if (disc?.worktreePaths.length) {
34818
- this.rememberWorktrees(sessionId, disc.agentCwd, disc.worktreePaths);
34819
- return disc.worktreePaths;
35073
+ if (disc?.repoCheckoutPaths.length) {
35074
+ this.rememberSessionWorktrees(sessionId, disc);
35075
+ return disc.repoCheckoutPaths;
34820
35076
  }
34821
- return [getBridgeWorkspaceDirectory()];
35077
+ return [getBridgeRoot()];
34822
35078
  }
34823
35079
  async getSessionWorkingTreeStatus(sessionId) {
34824
35080
  return aggregateSessionPathsWorkingTreeStatus(this.resolveCommitTargets(sessionId));
@@ -34826,10 +35082,12 @@ var SessionWorktreeManager = class {
34826
35082
  /** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
34827
35083
  async getSessionWorkingTreeChangeDetails(sessionId, opts) {
34828
35084
  const targets = this.resolveCommitTargets(sessionId);
34829
- const mirror = this.getAgentCwdForSession(sessionId);
35085
+ const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
35086
+ const legacyNested = this.isLegacyNestedLayout(sessionId);
34830
35087
  return getWorkingTreeChangeRepoDetails({
34831
- commitTargetAbsDirs: targets,
34832
- agentMirrorRootAbs: mirror,
35088
+ commitTargetPaths: targets,
35089
+ sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
35090
+ legacyRepoNestedSessionLayout: legacyNested,
34833
35091
  repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
34834
35092
  basis: opts?.basis
34835
35093
  });
@@ -34844,16 +35102,16 @@ var SessionWorktreeManager = class {
34844
35102
  }
34845
35103
  }
34846
35104
  };
34847
- function defaultWorktreesRootAbs() {
34848
- return path18.join(os4.homedir(), ".buildautomaton", "worktrees");
35105
+ function defaultWorktreesRootPath() {
35106
+ return path19.join(os4.homedir(), ".buildautomaton", "worktrees");
34849
35107
  }
34850
35108
 
34851
35109
  // src/files/watch-file-index.ts
34852
35110
  import { watch } from "node:fs";
34853
- import path25 from "node:path";
35111
+ import path26 from "node:path";
34854
35112
 
34855
35113
  // src/files/index/build-file-index.ts
34856
- import path22 from "node:path";
35114
+ import path23 from "node:path";
34857
35115
 
34858
35116
  // src/runtime/yield-to-event-loop.ts
34859
35117
  function yieldToEventLoop() {
@@ -34862,13 +35120,13 @@ function yieldToEventLoop() {
34862
35120
 
34863
35121
  // src/files/index/walk-workspace-tree.ts
34864
35122
  import fs19 from "node:fs";
34865
- import path20 from "node:path";
35123
+ import path21 from "node:path";
34866
35124
 
34867
35125
  // src/files/index/constants.ts
34868
- import path19 from "node:path";
35126
+ import path20 from "node:path";
34869
35127
  import os5 from "node:os";
34870
35128
  var INDEX_WORK_YIELD_EVERY = 256;
34871
- var INDEX_DIR = path19.join(os5.homedir(), ".buildautomaton");
35129
+ var INDEX_DIR = path20.join(os5.homedir(), ".buildautomaton");
34872
35130
  var INDEX_HASH_LEN = 16;
34873
35131
  var INDEX_VERSION = 2;
34874
35132
  var INDEX_LOG_PREFIX = "[file-index]";
@@ -34883,14 +35141,14 @@ function walkWorkspaceTreeSync(dir, baseDir, out) {
34883
35141
  }
34884
35142
  for (const name of names) {
34885
35143
  if (name.startsWith(".")) continue;
34886
- const full = path20.join(dir, name);
35144
+ const full = path21.join(dir, name);
34887
35145
  let stat3;
34888
35146
  try {
34889
35147
  stat3 = fs19.statSync(full);
34890
35148
  } catch {
34891
35149
  continue;
34892
35150
  }
34893
- const relative5 = path20.relative(baseDir, full).replace(/\\/g, "/");
35151
+ const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
34894
35152
  if (stat3.isDirectory()) {
34895
35153
  walkWorkspaceTreeSync(full, baseDir, out);
34896
35154
  } else if (stat3.isFile()) {
@@ -34911,14 +35169,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
34911
35169
  await yieldToEventLoop();
34912
35170
  }
34913
35171
  state.n++;
34914
- const full = path20.join(dir, name);
35172
+ const full = path21.join(dir, name);
34915
35173
  let stat3;
34916
35174
  try {
34917
35175
  stat3 = await fs19.promises.stat(full);
34918
35176
  } catch {
34919
35177
  continue;
34920
35178
  }
34921
- const relative5 = path20.relative(baseDir, full).replace(/\\/g, "/");
35179
+ const relative5 = path21.relative(baseDir, full).replace(/\\/g, "/");
34922
35180
  if (stat3.isDirectory()) {
34923
35181
  await walkWorkspaceTreeAsync(full, baseDir, out, state);
34924
35182
  } else if (stat3.isFile()) {
@@ -35002,11 +35260,11 @@ async function buildTrigramMapForPathsAsync(paths) {
35002
35260
  import fs20 from "node:fs";
35003
35261
 
35004
35262
  // src/files/index/paths.ts
35005
- import path21 from "node:path";
35263
+ import path22 from "node:path";
35006
35264
  import crypto2 from "node:crypto";
35007
35265
  function getIndexPathForCwd(resolvedCwd) {
35008
35266
  const hash = crypto2.createHash("sha256").update(resolvedCwd).digest("hex").slice(0, INDEX_HASH_LEN);
35009
- return path21.join(INDEX_DIR, `.file-index-${hash}.json`);
35267
+ return path22.join(INDEX_DIR, `.file-index-${hash}.json`);
35010
35268
  }
35011
35269
 
35012
35270
  // src/files/index/write-index-file.ts
@@ -35037,7 +35295,7 @@ function sortPaths(paths) {
35037
35295
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
35038
35296
  }
35039
35297
  function buildFileIndex(cwd) {
35040
- const resolved = path22.resolve(cwd);
35298
+ const resolved = path23.resolve(cwd);
35041
35299
  const paths = [];
35042
35300
  walkWorkspaceTreeSync(resolved, resolved, paths);
35043
35301
  sortPaths(paths);
@@ -35047,7 +35305,7 @@ function buildFileIndex(cwd) {
35047
35305
  return data;
35048
35306
  }
35049
35307
  async function buildFileIndexAsync(cwd) {
35050
- const resolved = path22.resolve(cwd);
35308
+ const resolved = path23.resolve(cwd);
35051
35309
  const paths = [];
35052
35310
  await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
35053
35311
  await yieldToEventLoop();
@@ -35060,9 +35318,9 @@ async function buildFileIndexAsync(cwd) {
35060
35318
 
35061
35319
  // src/files/index/load-file-index.ts
35062
35320
  import fs21 from "node:fs";
35063
- import path23 from "node:path";
35321
+ import path24 from "node:path";
35064
35322
  function loadFileIndex(cwd) {
35065
- const resolved = path23.resolve(cwd);
35323
+ const resolved = path24.resolve(cwd);
35066
35324
  const indexPath = getIndexPathForCwd(resolved);
35067
35325
  try {
35068
35326
  const raw = fs21.readFileSync(indexPath, "utf8");
@@ -35084,9 +35342,9 @@ function loadFileIndex(cwd) {
35084
35342
  }
35085
35343
 
35086
35344
  // src/files/index/ensure-file-index.ts
35087
- import path24 from "node:path";
35345
+ import path25 from "node:path";
35088
35346
  async function ensureFileIndexAsync(cwd) {
35089
- const resolved = path24.resolve(cwd);
35347
+ const resolved = path25.resolve(cwd);
35090
35348
  const cached2 = loadFileIndex(resolved);
35091
35349
  if (cached2 !== null) return { data: cached2, fromCache: true };
35092
35350
  const data = await buildFileIndexAsync(resolved);
@@ -35168,8 +35426,8 @@ function createFsWatcher(resolved, schedule) {
35168
35426
  throw e;
35169
35427
  }
35170
35428
  }
35171
- function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
35172
- const resolved = path25.resolve(cwd);
35429
+ function startFileIndexWatcher(cwd = getBridgeRoot()) {
35430
+ const resolved = path26.resolve(cwd);
35173
35431
  void buildFileIndexAsync(resolved).catch((e) => {
35174
35432
  console.error("[file-index] Initial index build failed:", e);
35175
35433
  });
@@ -35507,10 +35765,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
35507
35765
  import { spawn as spawn6 } from "node:child_process";
35508
35766
  import fs24 from "node:fs";
35509
35767
  import { tmpdir } from "node:os";
35510
- import path26 from "node:path";
35768
+ import path27 from "node:path";
35511
35769
  function trySpawnMergedLogFile(command, env, cwd, signal) {
35512
- const tmpRoot = fs24.mkdtempSync(path26.join(tmpdir(), "ba-devsrv-log-"));
35513
- const logPath = path26.join(tmpRoot, "combined.log");
35770
+ const tmpRoot = fs24.mkdtempSync(path27.join(tmpdir(), "ba-devsrv-log-"));
35771
+ const logPath = path27.join(tmpRoot, "combined.log");
35514
35772
  let logFd;
35515
35773
  try {
35516
35774
  logFd = fs24.openSync(logPath, "a");
@@ -35554,15 +35812,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
35554
35812
  import { spawn as spawn7 } from "node:child_process";
35555
35813
  import fs25 from "node:fs";
35556
35814
  import { tmpdir as tmpdir2 } from "node:os";
35557
- import path27 from "node:path";
35815
+ import path28 from "node:path";
35558
35816
  function shSingleQuote(s) {
35559
35817
  return `'${s.replace(/'/g, `'\\''`)}'`;
35560
35818
  }
35561
35819
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
35562
- const tmpRoot = fs25.mkdtempSync(path27.join(tmpdir2(), "ba-devsrv-sh-"));
35563
- const logPath = path27.join(tmpRoot, "combined.log");
35564
- const innerPath = path27.join(tmpRoot, "_cmd.sh");
35565
- const runnerPath = path27.join(tmpRoot, "_run.sh");
35820
+ const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir2(), "ba-devsrv-sh-"));
35821
+ const logPath = path28.join(tmpRoot, "combined.log");
35822
+ const innerPath = path28.join(tmpRoot, "_cmd.sh");
35823
+ const runnerPath = path28.join(tmpRoot, "_run.sh");
35566
35824
  try {
35567
35825
  fs25.writeFileSync(innerPath, `#!/bin/sh
35568
35826
  ${command}
@@ -35593,9 +35851,9 @@ cd ${shSingleQuote(cwd)}
35593
35851
  }
35594
35852
  }
35595
35853
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
35596
- const tmpRoot = fs25.mkdtempSync(path27.join(tmpdir2(), "ba-devsrv-sh-"));
35597
- const logPath = path27.join(tmpRoot, "combined.log");
35598
- const runnerPath = path27.join(tmpRoot, "_run.bat");
35854
+ const tmpRoot = fs25.mkdtempSync(path28.join(tmpdir2(), "ba-devsrv-sh-"));
35855
+ const logPath = path28.join(tmpRoot, "combined.log");
35856
+ const runnerPath = path28.join(tmpRoot, "_run.bat");
35599
35857
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
35600
35858
  const com = process.env.ComSpec || "cmd.exe";
35601
35859
  try {
@@ -35805,13 +36063,13 @@ var DevServerManager = class {
35805
36063
  abortControllersByServerId = /* @__PURE__ */ new Map();
35806
36064
  getWs;
35807
36065
  log;
35808
- getBridgeCwd;
36066
+ getBridgeRoot;
35809
36067
  e2ee;
35810
36068
  firehoseSink;
35811
36069
  constructor(options) {
35812
36070
  this.getWs = options.getWs;
35813
36071
  this.log = options.log;
35814
- this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
36072
+ this.getBridgeRoot = options.getBridgeRoot ?? (() => process.cwd());
35815
36073
  this.e2ee = options.e2ee;
35816
36074
  this.firehoseSink = new DevServerFirehoseSink({
35817
36075
  getTails: (serverId) => this.snapshotTails(serverId),
@@ -35900,7 +36158,7 @@ var DevServerManager = class {
35900
36158
  this.sendStatus(serverId, "starting", void 0, emptyTails());
35901
36159
  const ac = new AbortController();
35902
36160
  this.abortControllersByServerId.set(serverId, ac);
35903
- const cwd = this.getBridgeCwd();
36161
+ const cwd = this.getBridgeRoot();
35904
36162
  const childEnv = envForSpawn(process.env, def.env, def.ports);
35905
36163
  const cmd = substituteCommand(def.command.trim(), childEnv);
35906
36164
  const title = def.name.trim() || serverId.slice(0, 8);
@@ -36491,13 +36749,13 @@ function createOnBridgeIdentified(opts) {
36491
36749
 
36492
36750
  // src/skills/discover-local-agent-skills.ts
36493
36751
  import fs26 from "node:fs";
36494
- import path28 from "node:path";
36752
+ import path29 from "node:path";
36495
36753
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
36496
36754
  function discoverLocalSkills(cwd) {
36497
36755
  const out = [];
36498
36756
  const seenKeys = /* @__PURE__ */ new Set();
36499
36757
  for (const rel of SKILL_DISCOVERY_ROOTS) {
36500
- const base = path28.join(cwd, rel);
36758
+ const base = path29.join(cwd, rel);
36501
36759
  if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
36502
36760
  let entries = [];
36503
36761
  try {
@@ -36506,13 +36764,13 @@ function discoverLocalSkills(cwd) {
36506
36764
  continue;
36507
36765
  }
36508
36766
  for (const name of entries) {
36509
- const dir = path28.join(base, name);
36767
+ const dir = path29.join(base, name);
36510
36768
  try {
36511
36769
  if (!fs26.statSync(dir).isDirectory()) continue;
36512
36770
  } catch {
36513
36771
  continue;
36514
36772
  }
36515
- const skillMd = path28.join(dir, "SKILL.md");
36773
+ const skillMd = path29.join(dir, "SKILL.md");
36516
36774
  if (!fs26.existsSync(skillMd)) continue;
36517
36775
  const key = `${rel}/${name}`;
36518
36776
  if (seenKeys.has(key)) continue;
@@ -36525,7 +36783,7 @@ function discoverLocalSkills(cwd) {
36525
36783
  function discoverSkillLayoutRoots(cwd) {
36526
36784
  const roots = [];
36527
36785
  for (const rel of SKILL_DISCOVERY_ROOTS) {
36528
- const base = path28.join(cwd, rel);
36786
+ const base = path29.join(cwd, rel);
36529
36787
  if (!fs26.existsSync(base) || !fs26.statSync(base).isDirectory()) continue;
36530
36788
  let entries = [];
36531
36789
  try {
@@ -36535,13 +36793,13 @@ function discoverSkillLayoutRoots(cwd) {
36535
36793
  }
36536
36794
  const skills2 = [];
36537
36795
  for (const name of entries) {
36538
- const dir = path28.join(base, name);
36796
+ const dir = path29.join(base, name);
36539
36797
  try {
36540
36798
  if (!fs26.statSync(dir).isDirectory()) continue;
36541
36799
  } catch {
36542
36800
  continue;
36543
36801
  }
36544
- if (!fs26.existsSync(path28.join(dir, "SKILL.md"))) continue;
36802
+ if (!fs26.existsSync(path29.join(dir, "SKILL.md"))) continue;
36545
36803
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
36546
36804
  skills2.push({ name, relPath });
36547
36805
  }
@@ -36585,7 +36843,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
36585
36843
  try {
36586
36844
  const socket = getWs();
36587
36845
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
36588
- const skills2 = discoverLocalSkills(getBridgeWorkspaceDirectory());
36846
+ const skills2 = discoverLocalSkills(getBridgeRoot());
36589
36847
  sendWsMessage(socket, { type: "local_skills", skills: skills2 });
36590
36848
  } catch (e) {
36591
36849
  logFn(
@@ -36723,9 +36981,6 @@ var handleAgentConfigMessage = (msg, deps) => {
36723
36981
  handleBridgeAgentConfig(msg, deps);
36724
36982
  };
36725
36983
 
36726
- // src/agents/acp/from-bridge/handle-bridge-prompt.ts
36727
- import * as path30 from "node:path";
36728
-
36729
36984
  // src/prompt-turn-queue/client-report.ts
36730
36985
  function sendPromptQueueClientReport(ws, queues) {
36731
36986
  if (!ws) return false;
@@ -36739,7 +36994,7 @@ import fs28 from "node:fs";
36739
36994
  // src/prompt-turn-queue/paths.ts
36740
36995
  import crypto3 from "node:crypto";
36741
36996
  import fs27 from "node:fs";
36742
- import path29 from "node:path";
36997
+ import path30 from "node:path";
36743
36998
  import os6 from "node:os";
36744
36999
  var QUEUE_KEY_HEX_64 = /^[a-f0-9]{64}$/i;
36745
37000
  function queueStateFileSlug(queueKey) {
@@ -36748,15 +37003,15 @@ function queueStateFileSlug(queueKey) {
36748
37003
  }
36749
37004
  function getPromptQueuesDirectory() {
36750
37005
  const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
36751
- if (override) return path29.resolve(override);
36752
- return path29.join(os6.homedir(), ".buildautomaton", "queues");
37006
+ if (override) return path30.resolve(override);
37007
+ return path30.join(os6.homedir(), ".buildautomaton", "queues");
36753
37008
  }
36754
37009
  function ensurePromptQueuesDirectory() {
36755
37010
  const dir = getPromptQueuesDirectory();
36756
37011
  if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
36757
37012
  }
36758
37013
  function queueStateFilePath(queueKey) {
36759
- return path29.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
37014
+ return path30.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
36760
37015
  }
36761
37016
 
36762
37017
  // src/prompt-turn-queue/disk-store.ts
@@ -36828,6 +37083,10 @@ function hasRunningTurn(turns) {
36828
37083
  }
36829
37084
  function dispatchLocalPrompt(next, deps) {
36830
37085
  const pl = next.payload;
37086
+ const rawParent = pl["sessionParent"];
37087
+ const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
37088
+ const rawParentPath = pl["sessionParentPath"];
37089
+ const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
36831
37090
  const msg = {
36832
37091
  type: "prompt",
36833
37092
  sessionId: next.sessionId,
@@ -36835,6 +37094,8 @@ function dispatchLocalPrompt(next, deps) {
36835
37094
  prompt: pl.prompt,
36836
37095
  mode: typeof pl.mode === "string" ? pl.mode : "agent",
36837
37096
  isNewSession: pl.isNewSession === true,
37097
+ ...sessionParent ? { sessionParent } : {},
37098
+ ...sessionParentPath ? { sessionParentPath } : {},
36838
37099
  ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
36839
37100
  ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
36840
37101
  ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
@@ -36946,23 +37207,24 @@ async function readGitBranch(cwd) {
36946
37207
  async function runBridgePromptPreamble(params) {
36947
37208
  const { getWs, log: log2, sessionWorktreeManager, sessionId, runId, effectiveCwd } = params;
36948
37209
  const s = getWs();
36949
- const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
37210
+ const repoCheckoutPaths = sessionWorktreeManager.ensureRepoCheckoutPathsForSession(sessionId);
36950
37211
  const repoRoots = await resolveSnapshotRepoRoots({
36951
- worktreePaths,
37212
+ worktreePaths: repoCheckoutPaths,
36952
37213
  fallbackCwd: effectiveCwd,
37214
+ sessionId: sessionId?.trim() || void 0,
36953
37215
  log: log2
36954
37216
  });
36955
37217
  if (s && sessionId) {
36956
37218
  const cliGitBranch = await readGitBranch(effectiveCwd);
36957
37219
  const usesWt = sessionWorktreeManager.usesWorktreeSession(sessionId);
36958
- const mirrorAbs = sessionWorktreeManager.getAgentCwdForSession(sessionId);
37220
+ const isolatedSessionParentPath = sessionWorktreeManager.getIsolatedSessionParentPathForSession(sessionId);
36959
37221
  sendWsMessage(s, {
36960
37222
  type: "session_git_context_report",
36961
37223
  sessionId,
36962
37224
  cliGitBranch,
36963
37225
  agentUsesWorktree: usesWt,
36964
37226
  sessionParent: usesWt ? "worktrees_root" : "bridge_root",
36965
- sessionParentPath: usesWt ? mirrorAbs : getBridgeWorkspaceDirectory()
37227
+ sessionParentPath: usesWt ? isolatedSessionParentPath ?? effectiveCwd : getBridgeRoot()
36966
37228
  });
36967
37229
  }
36968
37230
  if (s && sessionId && runId) {
@@ -37089,7 +37351,7 @@ function handleBridgePrompt(msg, deps) {
37089
37351
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
37090
37352
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
37091
37353
  async function preambleAndPrompt(resolvedCwd) {
37092
- const effectiveCwd = path30.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
37354
+ const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
37093
37355
  await runBridgePromptPreamble({
37094
37356
  getWs,
37095
37357
  log: log2,
@@ -37122,7 +37384,7 @@ function handleBridgePrompt(msg, deps) {
37122
37384
  runId,
37123
37385
  mode,
37124
37386
  agentType,
37125
- cwd: effectiveCwd,
37387
+ sessionParentPath: effectiveCwd,
37126
37388
  sendResult: sendResult2,
37127
37389
  sendSessionUpdate,
37128
37390
  followUpCatalogPromptId,
@@ -37132,8 +37394,8 @@ function handleBridgePrompt(msg, deps) {
37132
37394
  e2ee: deps.e2ee
37133
37395
  });
37134
37396
  }
37135
- void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
37136
- log2(`[Agent] Worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
37397
+ void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
37398
+ log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
37137
37399
  void preambleAndPrompt(void 0);
37138
37400
  });
37139
37401
  }
@@ -37267,7 +37529,7 @@ var previewSkill = {
37267
37529
  const exe = parts[0];
37268
37530
  const args = parts.slice(1);
37269
37531
  previewProcess = spawn9(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
37270
- cwd: getBridgeWorkspaceDirectory(),
37532
+ cwd: getBridgeRoot(),
37271
37533
  stdio: ["ignore", "pipe", "pipe"],
37272
37534
  env: {
37273
37535
  ...process.env,
@@ -37390,7 +37652,7 @@ import path32 from "node:path";
37390
37652
 
37391
37653
  // src/files/ensure-under-cwd.ts
37392
37654
  import path31 from "node:path";
37393
- function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
37655
+ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
37394
37656
  const normalized = path31.normalize(relativePath).replace(/^(\.\/)+/, "");
37395
37657
  const resolved = path31.resolve(cwd, normalized);
37396
37658
  if (!resolved.startsWith(cwd + path31.sep) && resolved !== cwd) {
@@ -37402,7 +37664,7 @@ function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
37402
37664
  // src/files/list-dir.ts
37403
37665
  var LIST_DIR_YIELD_EVERY = 256;
37404
37666
  async function listDirAsync(relativePath) {
37405
- const resolved = ensureUnderCwd(relativePath || ".", getBridgeWorkspaceDirectory());
37667
+ const resolved = ensureUnderCwd(relativePath || ".", getBridgeRoot());
37406
37668
  if (!resolved) {
37407
37669
  return { error: "Path is outside working directory" };
37408
37670
  }
@@ -37448,7 +37710,7 @@ async function listDirAsync(relativePath) {
37448
37710
  import fs30 from "node:fs";
37449
37711
  import { StringDecoder } from "node:string_decoder";
37450
37712
  function resolveFilePath(relativePath) {
37451
- const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
37713
+ const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
37452
37714
  if (!resolved) return { error: "Path is outside working directory" };
37453
37715
  let real;
37454
37716
  try {
@@ -37641,7 +37903,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
37641
37903
  void (async () => {
37642
37904
  await yieldToEventLoop();
37643
37905
  const q = typeof msg.q === "string" ? msg.q : "";
37644
- const cwd = getBridgeWorkspaceDirectory();
37906
+ const cwd = getBridgeRoot();
37645
37907
  const index = loadFileIndex(cwd);
37646
37908
  if (index === null) {
37647
37909
  const payload2 = {
@@ -37665,7 +37927,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
37665
37927
  }
37666
37928
  function triggerFileIndexBuild() {
37667
37929
  setImmediate(() => {
37668
- void ensureFileIndexAsync(getBridgeWorkspaceDirectory()).catch((e) => {
37930
+ void ensureFileIndexAsync(getBridgeRoot()).catch((e) => {
37669
37931
  console.error("[file-index] Background build failed:", e);
37670
37932
  });
37671
37933
  });
@@ -37734,7 +37996,7 @@ function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
37734
37996
  function handleSkillLayoutRequest(msg, deps) {
37735
37997
  const socket = deps.getWs();
37736
37998
  const id = typeof msg.id === "string" ? msg.id : "";
37737
- const roots = discoverSkillLayoutRoots(getBridgeWorkspaceDirectory());
37999
+ const roots = discoverSkillLayoutRoots(getBridgeRoot());
37738
38000
  if (socket) {
37739
38001
  sendWsMessage(socket, { type: "skill_layout_response", id, roots });
37740
38002
  }
@@ -37782,7 +38044,7 @@ var handleInstallSkillsMessage = (msg, deps) => {
37782
38044
  const id = typeof msg.id === "string" ? msg.id : "";
37783
38045
  const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
37784
38046
  const rawItems = msg.items;
37785
- const cwd = getBridgeWorkspaceDirectory();
38047
+ const cwd = getBridgeRoot();
37786
38048
  const result = installRemoteSkills(cwd, targetDir, rawItems);
37787
38049
  if (!result.success) {
37788
38050
  const err = result.error ?? "Invalid items";
@@ -37921,7 +38183,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
37921
38183
  void (async () => {
37922
38184
  const s = getWs();
37923
38185
  if (!s) return;
37924
- const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
38186
+ const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
37925
38187
  const file2 = snapshotFilePath(agentBase, turnId);
37926
38188
  if (!fs32.existsSync(file2)) {
37927
38189
  sendWsMessage(s, {
@@ -38213,9 +38475,9 @@ async function createBridgeConnection(options) {
38213
38475
  firehoseGeneration: 0,
38214
38476
  firehoseQuiet: createEmptyReconnectQuietSlot()
38215
38477
  };
38216
- const worktreesRootAbs = options.worktreesRootAbs ?? defaultWorktreesRootAbs();
38478
+ const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
38217
38479
  const sessionWorktreeManager = new SessionWorktreeManager({
38218
- worktreesRootAbs,
38480
+ worktreesRootPath,
38219
38481
  log: logFn
38220
38482
  });
38221
38483
  const acpManager = await createAcpManager({ log: logFn });
@@ -38224,7 +38486,7 @@ async function createBridgeConnection(options) {
38224
38486
  return state.currentWs;
38225
38487
  }
38226
38488
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
38227
- const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory, e2ee });
38489
+ const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
38228
38490
  const onBridgeIdentified = createOnBridgeIdentified({
38229
38491
  devServerManager,
38230
38492
  firehoseServerUrl,
@@ -38248,8 +38510,8 @@ async function createBridgeConnection(options) {
38248
38510
  getCloudAccessToken: () => tokens.accessToken
38249
38511
  };
38250
38512
  const identifyReportedPaths = {
38251
- bridgeRootPath: path34.resolve(getBridgeWorkspaceDirectory()),
38252
- worktreesRootPath: path34.resolve(worktreesRootAbs)
38513
+ bridgeRootPath: path34.resolve(getBridgeRoot()),
38514
+ worktreesRootPath: path34.resolve(worktreesRootPath)
38253
38515
  };
38254
38516
  const { connect } = createMainBridgeWebSocketLifecycle({
38255
38517
  state,
@@ -38266,7 +38528,7 @@ async function createBridgeConnection(options) {
38266
38528
  identifyReportedPaths
38267
38529
  });
38268
38530
  connect();
38269
- const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
38531
+ const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
38270
38532
  return {
38271
38533
  close: async () => {
38272
38534
  stopFileIndexWatcher();
@@ -38336,7 +38598,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
38336
38598
  authToken,
38337
38599
  refreshToken,
38338
38600
  justAuthenticated,
38339
- worktreesRootAbs,
38601
+ worktreesRootPath,
38340
38602
  e2eCertificate
38341
38603
  } = options;
38342
38604
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -38348,7 +38610,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
38348
38610
  refreshToken,
38349
38611
  firehoseServerUrl,
38350
38612
  justAuthenticated,
38351
- worktreesRootAbs,
38613
+ worktreesRootPath,
38352
38614
  e2eCertificate,
38353
38615
  log,
38354
38616
  persistTokens: (t) => {
@@ -38363,7 +38625,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
38363
38625
  log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
38364
38626
  clearConfigForApi(apiUrl);
38365
38627
  void handle.close().then(() => {
38366
- void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootAbs, e2eCertificate });
38628
+ void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
38367
38629
  });
38368
38630
  }
38369
38631
  });
@@ -38411,7 +38673,7 @@ async function runBridge(options) {
38411
38673
  workspaceId,
38412
38674
  authToken,
38413
38675
  bridgeName,
38414
- worktreesRootAbs,
38676
+ worktreesRootPath,
38415
38677
  e2eCertificate
38416
38678
  } = options;
38417
38679
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -38456,7 +38718,7 @@ async function runBridge(options) {
38456
38718
  firehoseServerUrl,
38457
38719
  bridgeName,
38458
38720
  justAuthenticated: true,
38459
- worktreesRootAbs,
38721
+ worktreesRootPath,
38460
38722
  e2eCertificate
38461
38723
  });
38462
38724
  return;
@@ -38480,30 +38742,31 @@ async function runCliAction(program2, opts) {
38480
38742
  let workspaceId = opts.workspaceId ?? "";
38481
38743
  let authToken = opts.token;
38482
38744
  const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
38483
- if (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim()) {
38484
- const resolvedCwd = path35.resolve(process.cwd(), opts.cwd.trim());
38745
+ const bridgeRootOpt = (opts.bridgeRoot && typeof opts.bridgeRoot === "string" && opts.bridgeRoot.trim() ? opts.bridgeRoot.trim() : null) ?? (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim() ? opts.cwd.trim() : null);
38746
+ if (bridgeRootOpt) {
38747
+ const resolvedBridgeRoot = path35.resolve(process.cwd(), bridgeRootOpt);
38485
38748
  try {
38486
- const st = fs33.statSync(resolvedCwd);
38749
+ const st = fs33.statSync(resolvedBridgeRoot);
38487
38750
  if (!st.isDirectory()) {
38488
- console.error(`--cwd is not a directory: ${resolvedCwd}`);
38751
+ console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
38489
38752
  process.exit(1);
38490
38753
  }
38491
38754
  } catch {
38492
- console.error(`--cwd path does not exist or is not accessible: ${resolvedCwd}`);
38755
+ console.error(`Bridge root path does not exist or is not accessible: ${resolvedBridgeRoot}`);
38493
38756
  process.exit(1);
38494
38757
  }
38495
- process.chdir(resolvedCwd);
38758
+ process.chdir(resolvedBridgeRoot);
38496
38759
  }
38497
- initBridgeWorkspaceDirectory();
38760
+ initBridgeRoot();
38498
38761
  console.log(
38499
38762
  colorize(
38500
38763
  GOLDEN_YELLOW,
38501
- `[Workspace] Using ${getBridgeWorkspaceDirectory()} as the working directory for file access and agents.`
38764
+ `[Bridge] Using ${getBridgeRoot()} as the bridge root (repos, file index, agents, and bridge_root sessions).`
38502
38765
  )
38503
38766
  );
38504
- let worktreesRootAbs;
38767
+ let worktreesRootPath;
38505
38768
  if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
38506
- worktreesRootAbs = path35.resolve(opts.worktreesRoot.trim());
38769
+ worktreesRootPath = path35.resolve(opts.worktreesRoot.trim());
38507
38770
  }
38508
38771
  const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
38509
38772
  if (e2eCertificates) {
@@ -38538,7 +38801,7 @@ async function runCliAction(program2, opts) {
38538
38801
  refreshToken,
38539
38802
  firehoseServerUrl,
38540
38803
  bridgeName: opts.name?.trim() || void 0,
38541
- worktreesRootAbs,
38804
+ worktreesRootPath,
38542
38805
  e2eCertificate: e2eCertificates?.activeCertificate
38543
38806
  });
38544
38807
  }
@@ -38551,8 +38814,11 @@ async function main() {
38551
38814
  "Firehose server URL (default: Fly app; or BUILDAUTOMATON_FIREHOSE_URL / legacy BUILDAUTOMATON_PROXY_URL)",
38552
38815
  process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL
38553
38816
  ).option("--proxy-url <url>", "Deprecated alias for --firehose-url", void 0).option(
38817
+ "--bridge-root <path>",
38818
+ "Bridge root directory (absolute or relative to the current directory): git repos, skills, file index, and agent cwd for bridge_root sessions"
38819
+ ).option(
38554
38820
  "--cwd <path>",
38555
- "Working directory for the bridge (absolute or relative to the current directory); affects skills, git, file index, and agent cwd"
38821
+ "Deprecated: same as --bridge-root"
38556
38822
  ).option("-n, --name <name>", "Bridge name when creating via browser (alphanumeric and underscores only)").option(
38557
38823
  "--worktrees-root <path>",
38558
38824
  "Root directory for per-session git worktrees (default: ~/.buildautomaton/worktrees)."