@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/index.js CHANGED
@@ -22268,14 +22268,14 @@ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
22268
22268
  return out.join("\n");
22269
22269
  }
22270
22270
 
22271
- // src/files/cwd/bridge-workspace-directory.ts
22271
+ // src/files/cwd/bridge-root.ts
22272
22272
  import * as path from "node:path";
22273
- var bridgeWorkspaceDirectory = null;
22274
- function getBridgeWorkspaceDirectory() {
22275
- if (bridgeWorkspaceDirectory == null) {
22276
- bridgeWorkspaceDirectory = path.resolve(process.cwd());
22273
+ var bridgeRootPath = null;
22274
+ function getBridgeRoot() {
22275
+ if (bridgeRootPath == null) {
22276
+ bridgeRootPath = path.resolve(process.cwd());
22277
22277
  }
22278
- return bridgeWorkspaceDirectory;
22278
+ return bridgeRootPath;
22279
22279
  }
22280
22280
 
22281
22281
  // src/agents/acp/safe-fs-path.ts
@@ -22373,6 +22373,52 @@ function createSdkStdioExtNotificationHandler(options) {
22373
22373
  }
22374
22374
  }
22375
22375
 
22376
+ // src/agents/acp/enrich-acp-permission-rpc-result.ts
22377
+ var META_KEY = "permissionOptionKind";
22378
+ function optionRecordId(rec) {
22379
+ const raw = rec.optionId ?? rec.id;
22380
+ if (typeof raw === "string" && raw.trim() !== "") return raw.trim();
22381
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
22382
+ return "";
22383
+ }
22384
+ function enrichAcpPermissionRpcResultFromRequestParams(result, params) {
22385
+ if (params == null || result == null || typeof result !== "object" || Array.isArray(result)) {
22386
+ return result;
22387
+ }
22388
+ const root = result;
22389
+ const outcome = root.outcome;
22390
+ if (outcome == null || typeof outcome !== "object" || Array.isArray(outcome)) return result;
22391
+ const o = outcome;
22392
+ if (o.outcome !== "selected" || typeof o.optionId !== "string" || o.optionId.trim() === "") {
22393
+ return result;
22394
+ }
22395
+ const selectedId = o.optionId.trim();
22396
+ const prevMeta = o._meta != null && typeof o._meta === "object" && !Array.isArray(o._meta) ? o._meta : {};
22397
+ if (typeof prevMeta[META_KEY] === "string" && prevMeta[META_KEY].trim() !== "") {
22398
+ return result;
22399
+ }
22400
+ const rawOpts = Array.isArray(params.options) ? params.options : [];
22401
+ let matchedKind;
22402
+ for (const item of rawOpts) {
22403
+ if (item == null || typeof item !== "object" || Array.isArray(item)) continue;
22404
+ const rec = item;
22405
+ const id = optionRecordId(rec);
22406
+ if (!id || id !== selectedId) continue;
22407
+ if (typeof rec.kind === "string" && rec.kind.trim() !== "") {
22408
+ matchedKind = rec.kind.trim();
22409
+ break;
22410
+ }
22411
+ }
22412
+ if (!matchedKind) return result;
22413
+ return {
22414
+ ...root,
22415
+ outcome: {
22416
+ ...o,
22417
+ _meta: { ...prevMeta, [META_KEY]: matchedKind }
22418
+ }
22419
+ };
22420
+ }
22421
+
22376
22422
  // src/agents/acp/clients/sdk-stdio-acp-client.ts
22377
22423
  function formatSpawnError(err, command) {
22378
22424
  if (err.code === "ENOENT") {
@@ -22394,7 +22440,7 @@ async function createSdkStdioAcpClient(options) {
22394
22440
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
22395
22441
  const {
22396
22442
  command,
22397
- cwd = getBridgeWorkspaceDirectory(),
22443
+ cwd = getBridgeRoot(),
22398
22444
  backendAgentType,
22399
22445
  onSessionUpdate,
22400
22446
  onRequest,
@@ -22450,7 +22496,7 @@ async function createSdkStdioAcpClient(options) {
22450
22496
  onSessionUpdate
22451
22497
  });
22452
22498
  let permissionSeq = 0;
22453
- const pendingPermissionResolvers = /* @__PURE__ */ new Map();
22499
+ const pendingPermissionReplies = /* @__PURE__ */ new Map();
22454
22500
  const client = (_agent) => ({
22455
22501
  async requestPermission(params) {
22456
22502
  const requestId = `perm-${++permissionSeq}`;
@@ -22464,14 +22510,14 @@ async function createSdkStdioAcpClient(options) {
22464
22510
  } catch {
22465
22511
  }
22466
22512
  return await new Promise((resolve17) => {
22467
- pendingPermissionResolvers.set(requestId, resolve17);
22513
+ pendingPermissionReplies.set(requestId, { resolve: resolve17, params: paramsRecord });
22468
22514
  });
22469
22515
  },
22470
22516
  async readTextFile(params) {
22471
- const abs = resolveSafePathUnderCwd(cwd, params.path);
22472
- if (!abs) throw new Error("Invalid or disallowed path");
22517
+ const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
22518
+ if (!resolvedPath) throw new Error("Invalid or disallowed path");
22473
22519
  try {
22474
- let content = readFileSync(abs, "utf8");
22520
+ let content = readFileSync(resolvedPath, "utf8");
22475
22521
  content = sliceFileContentRange(content, params.line, params.limit);
22476
22522
  return { content };
22477
22523
  } catch (e) {
@@ -22480,17 +22526,17 @@ async function createSdkStdioAcpClient(options) {
22480
22526
  }
22481
22527
  },
22482
22528
  async writeTextFile(params) {
22483
- const abs = resolveSafePathUnderCwd(cwd, params.path);
22484
- if (!abs) throw new Error("Invalid or disallowed path");
22529
+ const resolvedPath = resolveSafePathUnderCwd(cwd, params.path);
22530
+ if (!resolvedPath) throw new Error("Invalid or disallowed path");
22485
22531
  let oldText = "";
22486
22532
  try {
22487
- oldText = readFileSync(abs, "utf8");
22533
+ oldText = readFileSync(resolvedPath, "utf8");
22488
22534
  } catch (e) {
22489
22535
  if (e.code !== "ENOENT") throw e;
22490
22536
  }
22491
- mkdirSync(dirname(abs), { recursive: true });
22492
- writeFileSync(abs, params.content, "utf8");
22493
- const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
22537
+ mkdirSync(dirname(resolvedPath), { recursive: true });
22538
+ writeFileSync(resolvedPath, params.content, "utf8");
22539
+ const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
22494
22540
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
22495
22541
  onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
22496
22542
  return {};
@@ -22572,9 +22618,9 @@ async function createSdkStdioAcpClient(options) {
22572
22618
  }
22573
22619
  },
22574
22620
  async cancel() {
22575
- for (const [id, resolve17] of [...pendingPermissionResolvers.entries()]) {
22576
- pendingPermissionResolvers.delete(id);
22577
- resolve17({ outcome: { outcome: "cancelled" } });
22621
+ for (const [id, entry] of [...pendingPermissionReplies.entries()]) {
22622
+ pendingPermissionReplies.delete(id);
22623
+ entry.resolve({ outcome: { outcome: "cancelled" } });
22578
22624
  }
22579
22625
  try {
22580
22626
  await connection.cancel({ sessionId });
@@ -22590,10 +22636,11 @@ async function createSdkStdioAcpClient(options) {
22590
22636
  }
22591
22637
  },
22592
22638
  resolveRequest(requestId, result) {
22593
- const resolve17 = pendingPermissionResolvers.get(requestId);
22594
- if (!resolve17) return;
22595
- pendingPermissionResolvers.delete(requestId);
22596
- resolve17(result);
22639
+ const entry = pendingPermissionReplies.get(requestId);
22640
+ if (!entry) return;
22641
+ pendingPermissionReplies.delete(requestId);
22642
+ const enriched = enrichAcpPermissionRpcResultFromRequestParams(result, entry.params);
22643
+ entry.resolve(enriched);
22597
22644
  },
22598
22645
  disconnect() {
22599
22646
  child.kill();
@@ -22862,7 +22909,7 @@ var previewSkill = {
22862
22909
  const exe = parts[0];
22863
22910
  const args = parts.slice(1);
22864
22911
  previewProcess = spawn2(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
22865
- cwd: getBridgeWorkspaceDirectory(),
22912
+ cwd: getBridgeRoot(),
22866
22913
  stdio: ["ignore", "pipe", "pipe"],
22867
22914
  env: {
22868
22915
  ...process.env,
@@ -23041,7 +23088,7 @@ function installBridgeProcessResilience() {
23041
23088
  }
23042
23089
 
23043
23090
  // src/cli-version.ts
23044
- var CLI_VERSION = "0.1.20".length > 0 ? "0.1.20" : "0.0.0-dev";
23091
+ var CLI_VERSION = "0.1.21".length > 0 ? "0.1.21" : "0.0.0-dev";
23045
23092
 
23046
23093
  // ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
23047
23094
  import process7 from "node:process";
@@ -23944,21 +23991,36 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
23944
23991
  say("Shutdown complete.");
23945
23992
  }
23946
23993
 
23994
+ // src/paths/session-layout-paths.ts
23995
+ import * as path5 from "node:path";
23996
+ function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
23997
+ const resolved = worktreePaths.map((p) => path5.resolve(p)).filter(Boolean);
23998
+ if (resolved.length === 0) return null;
23999
+ resolved.sort();
24000
+ return resolved[0];
24001
+ }
24002
+ function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
24003
+ if (resolvedSessionParentPath != null && String(resolvedSessionParentPath).trim() !== "") {
24004
+ return path5.resolve(String(resolvedSessionParentPath).trim());
24005
+ }
24006
+ return getBridgeRoot();
24007
+ }
24008
+
23947
24009
  // src/git/session-git-queue.ts
23948
24010
  import { execFile as execFile7 } from "node:child_process";
23949
24011
  import { readFile, stat } from "node:fs/promises";
23950
24012
  import { promisify as promisify7 } from "node:util";
23951
- import * as path7 from "node:path";
24013
+ import * as path8 from "node:path";
23952
24014
 
23953
24015
  // src/git/pre-turn-snapshot.ts
23954
24016
  import * as fs8 from "node:fs";
23955
- import * as path6 from "node:path";
24017
+ import * as path7 from "node:path";
23956
24018
  import { execFile as execFile6 } from "node:child_process";
23957
24019
  import { promisify as promisify6 } from "node:util";
23958
24020
 
23959
24021
  // src/git/discover-repos.ts
23960
24022
  import * as fs7 from "node:fs";
23961
- import * as path5 from "node:path";
24023
+ import * as path6 from "node:path";
23962
24024
 
23963
24025
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
23964
24026
  var import_file_exists = __toESM(require_dist(), 1);
@@ -28543,9 +28605,9 @@ async function isGitRepoDirectory(dirPath) {
28543
28605
  }
28544
28606
 
28545
28607
  // src/git/discover-repos.ts
28546
- async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
28608
+ async function discoverGitRepos(cwd = getBridgeRoot()) {
28547
28609
  const result = [];
28548
- const cwdResolved = path5.resolve(cwd);
28610
+ const cwdResolved = path6.resolve(cwd);
28549
28611
  if (await isGitRepoDirectory(cwdResolved)) {
28550
28612
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
28551
28613
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -28558,7 +28620,7 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
28558
28620
  }
28559
28621
  for (const ent of entries) {
28560
28622
  if (!ent.isDirectory()) continue;
28561
- const childPath = path5.join(cwdResolved, ent.name);
28623
+ const childPath = path6.join(cwdResolved, ent.name);
28562
28624
  if (await isGitRepoDirectory(childPath)) {
28563
28625
  const remoteUrl = await getRemoteOriginUrl(childPath);
28564
28626
  result.push({ absolutePath: childPath, remoteUrl });
@@ -28566,12 +28628,12 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
28566
28628
  }
28567
28629
  return result;
28568
28630
  }
28569
- async function discoverGitReposUnderRoot(rootAbs) {
28570
- const root = path5.resolve(rootAbs);
28631
+ async function discoverGitReposUnderRoot(rootPath) {
28632
+ const root = path6.resolve(rootPath);
28571
28633
  const roots = [];
28572
28634
  async function walk(dir) {
28573
28635
  if (await isGitRepoDirectory(dir)) {
28574
- roots.push(path5.resolve(dir));
28636
+ roots.push(path6.resolve(dir));
28575
28637
  return;
28576
28638
  }
28577
28639
  let entries;
@@ -28582,7 +28644,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
28582
28644
  }
28583
28645
  for (const ent of entries) {
28584
28646
  if (!ent.isDirectory() || ent.name === ".git") continue;
28585
- await walk(path5.join(dir, ent.name));
28647
+ await walk(path6.join(dir, ent.name));
28586
28648
  }
28587
28649
  }
28588
28650
  await walk(root);
@@ -28598,7 +28660,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
28598
28660
  // src/git/pre-turn-snapshot.ts
28599
28661
  var execFileAsync5 = promisify6(execFile6);
28600
28662
  function snapshotsDirForCwd(agentCwd) {
28601
- return path6.join(agentCwd, ".buildautomaton", "snapshots");
28663
+ return path7.join(agentCwd, ".buildautomaton", "snapshots");
28602
28664
  }
28603
28665
  async function gitStashCreate(repoRoot, log2) {
28604
28666
  try {
@@ -28625,14 +28687,20 @@ async function gitRun(repoRoot, args, log2, label) {
28625
28687
  }
28626
28688
  }
28627
28689
  async function resolveSnapshotRepoRoots(options) {
28628
- const { worktreePaths, fallbackCwd, log: log2 } = options;
28690
+ const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
28629
28691
  if (worktreePaths?.length) {
28630
- const uniq = [...new Set(worktreePaths.map((p) => path6.resolve(p)))];
28692
+ const uniq = [...new Set(worktreePaths.map((p) => path7.resolve(p)))];
28631
28693
  return uniq;
28632
28694
  }
28633
28695
  try {
28634
28696
  const repos = await discoverGitReposUnderRoot(fallbackCwd);
28635
- return repos.map((r) => r.absolutePath);
28697
+ const mapped = repos.map((r) => r.absolutePath);
28698
+ const sid = sessionId?.trim();
28699
+ if (sid) {
28700
+ const filtered = mapped.filter((root) => path7.basename(root) === sid);
28701
+ if (filtered.length > 0) return filtered;
28702
+ }
28703
+ return mapped;
28636
28704
  } catch (e) {
28637
28705
  log2(`[snapshot] Discover repositories failed: ${e instanceof Error ? e.message : String(e)}`);
28638
28706
  return [];
@@ -28659,7 +28727,7 @@ async function capturePreTurnSnapshot(options) {
28659
28727
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
28660
28728
  repos
28661
28729
  };
28662
- const filePath = path6.join(dir, `${runId}.json`);
28730
+ const filePath = path7.join(dir, `${runId}.json`);
28663
28731
  try {
28664
28732
  fs8.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
28665
28733
  } catch (e) {
@@ -28697,7 +28765,7 @@ async function applyPreTurnSnapshot(filePath, log2) {
28697
28765
  return { ok: true };
28698
28766
  }
28699
28767
  function snapshotFilePath(agentCwd, runId) {
28700
- return path6.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
28768
+ return path7.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
28701
28769
  }
28702
28770
 
28703
28771
  // src/git/session-git-queue.ts
@@ -28746,7 +28814,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
28746
28814
  continue;
28747
28815
  }
28748
28816
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
28749
- const slug = path7.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
28817
+ const slug = path8.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
28750
28818
  for (const rel of lines) {
28751
28819
  if (rel.includes("..")) continue;
28752
28820
  try {
@@ -28760,8 +28828,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
28760
28828
  );
28761
28829
  if (!patchContent.trim()) continue;
28762
28830
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
28763
- const absFile = path7.join(repo.path, rel);
28764
- const newText = await readWorkspaceFileAsUtf8(absFile);
28831
+ const workspaceFilePath = path8.join(repo.path, rel);
28832
+ const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
28765
28833
  sendSessionUpdate({
28766
28834
  type: "session_file_change",
28767
28835
  sessionId,
@@ -29376,7 +29444,7 @@ async function sendPromptToAgent(options) {
29376
29444
 
29377
29445
  // src/agents/acp/ensure-acp-client.ts
29378
29446
  import * as fs9 from "node:fs";
29379
- import * as path10 from "node:path";
29447
+ import * as path11 from "node:path";
29380
29448
 
29381
29449
  // src/error-message.ts
29382
29450
  function errorMessage(err) {
@@ -29477,7 +29545,7 @@ __export(cursor_acp_client_exports, {
29477
29545
  detectLocalAgentPresence: () => detectLocalAgentPresence3
29478
29546
  });
29479
29547
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
29480
- import { dirname as dirname2 } from "node:path";
29548
+ import { dirname as dirname3 } from "node:path";
29481
29549
  import { spawn as spawn4 } from "node:child_process";
29482
29550
  import * as readline from "node:readline";
29483
29551
 
@@ -29527,7 +29595,7 @@ function buildCursorAcpSpawnCommand(base, sessionMode) {
29527
29595
  async function createCursorAcpClient(options) {
29528
29596
  const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
29529
29597
  const {
29530
- cwd = getBridgeWorkspaceDirectory(),
29598
+ cwd = getBridgeRoot(),
29531
29599
  backendAgentType,
29532
29600
  onSessionUpdate,
29533
29601
  onRequest,
@@ -29569,7 +29637,9 @@ async function createCursorAcpClient(options) {
29569
29637
  child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
29570
29638
  }
29571
29639
  function resolveRequest(requestId, result) {
29572
- respond(requestId, result);
29640
+ const pending2 = pendingRequests.get(requestId);
29641
+ const payload = pending2?.method === "session/request_permission" ? enrichAcpPermissionRpcResultFromRequestParams(result, pending2.params) : result;
29642
+ respond(requestId, payload);
29573
29643
  pendingRequests.delete(requestId);
29574
29644
  }
29575
29645
  let promptOutputBuffer = "";
@@ -29625,14 +29695,14 @@ async function createCursorAcpClient(options) {
29625
29695
  if (dbgFs) {
29626
29696
  console.error(`[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""}`);
29627
29697
  }
29628
- const abs = resolveSafePathUnderCwd(cwd, filePath);
29629
- if (!abs) {
29698
+ const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
29699
+ if (!resolvedPath) {
29630
29700
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty)`);
29631
29701
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
29632
29702
  return;
29633
29703
  }
29634
29704
  try {
29635
- let content = readFileSync3(abs, "utf8");
29705
+ let content = readFileSync3(resolvedPath, "utf8");
29636
29706
  const line2 = typeof params.line === "number" ? params.line : void 0;
29637
29707
  const limit = typeof params.limit === "number" ? params.limit : void 0;
29638
29708
  content = sliceLinesByRange(content, line2, limit);
@@ -29656,15 +29726,15 @@ async function createCursorAcpClient(options) {
29656
29726
  `[acp-fs] ${method} path=${filePath.slice(0, 200)}${filePath.length > 200 ? "\u2026" : ""} newBytes=${newText.length}`
29657
29727
  );
29658
29728
  }
29659
- const abs = resolveSafePathUnderCwd(cwd, filePath);
29660
- if (!abs) {
29729
+ const resolvedPath = resolveSafePathUnderCwd(cwd, filePath);
29730
+ if (!resolvedPath) {
29661
29731
  if (dbgFs) console.error(`[acp-fs] ${method} rejected path (outside cwd or empty): ${filePath.slice(0, 120)}`);
29662
29732
  respondJsonRpcError(id, -32602, "Invalid or disallowed path");
29663
29733
  return;
29664
29734
  }
29665
29735
  let oldText = "";
29666
29736
  try {
29667
- oldText = readFileSync3(abs, "utf8");
29737
+ oldText = readFileSync3(resolvedPath, "utf8");
29668
29738
  } catch (e) {
29669
29739
  if (e.code !== "ENOENT") {
29670
29740
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
@@ -29672,13 +29742,13 @@ async function createCursorAcpClient(options) {
29672
29742
  }
29673
29743
  }
29674
29744
  try {
29675
- mkdirSync3(dirname2(abs), { recursive: true });
29676
- writeFileSync3(abs, newText, "utf8");
29745
+ mkdirSync3(dirname3(resolvedPath), { recursive: true });
29746
+ writeFileSync3(resolvedPath, newText, "utf8");
29677
29747
  } catch (e) {
29678
29748
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
29679
29749
  return;
29680
29750
  }
29681
- const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
29751
+ const displayPath = toDisplayPathRelativeToCwd(cwd, resolvedPath);
29682
29752
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
29683
29753
  onFileChange?.({ path: displayPath, oldText, newText, patchContent });
29684
29754
  respond(id, null);
@@ -29794,8 +29864,7 @@ async function createCursorAcpClient(options) {
29794
29864
  resolveRequest(requestId, result) {
29795
29865
  const numericId = Number(requestId);
29796
29866
  if (!Number.isFinite(numericId)) return;
29797
- const pendingRequest = pendingRequests.get(numericId);
29798
- if (!pendingRequest) return;
29867
+ if (!pendingRequests.get(numericId)) return;
29799
29868
  resolveRequest(numericId, result);
29800
29869
  },
29801
29870
  disconnect() {
@@ -29921,16 +29990,16 @@ import { existsSync, statSync } from "node:fs";
29921
29990
 
29922
29991
  // src/git/get-git-repo-root-sync.ts
29923
29992
  import { execFileSync as execFileSync2 } from "node:child_process";
29924
- import * as path8 from "node:path";
29993
+ import * as path9 from "node:path";
29925
29994
  function getGitRepoRootSync(startDir) {
29926
29995
  try {
29927
29996
  const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
29928
- cwd: path8.resolve(startDir),
29997
+ cwd: path9.resolve(startDir),
29929
29998
  encoding: "utf8",
29930
29999
  stdio: ["ignore", "pipe", "ignore"],
29931
30000
  maxBuffer: 1024 * 1024
29932
30001
  }).trim();
29933
- return out ? path8.resolve(out) : null;
30002
+ return out ? path9.resolve(out) : null;
29934
30003
  } catch {
29935
30004
  return null;
29936
30005
  }
@@ -29939,64 +30008,64 @@ function getGitRepoRootSync(startDir) {
29939
30008
  // src/agents/acp/workspace-files.ts
29940
30009
  import { execFileSync as execFileSync3 } from "node:child_process";
29941
30010
  import { readFileSync as readFileSync4 } from "node:fs";
29942
- import * as path9 from "node:path";
29943
- function resolveWorkspaceFilePath(cwd, rawPath) {
30011
+ import * as path10 from "node:path";
30012
+ function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
29944
30013
  const trimmed2 = rawPath.trim();
29945
30014
  if (!trimmed2) return null;
29946
- const normalizedCwd = path9.resolve(cwd);
29947
- let abs = resolveSafePathUnderCwd(cwd, trimmed2);
29948
- if (!abs) {
29949
- const candidate = path9.isAbsolute(trimmed2) ? path9.normalize(trimmed2) : path9.normalize(path9.resolve(normalizedCwd, trimmed2));
29950
- const gitRoot2 = getGitRepoRootSync(cwd);
30015
+ const normalizedSessionParent = path10.resolve(sessionParentPath);
30016
+ let resolvedPath = resolveSafePathUnderCwd(sessionParentPath, trimmed2);
30017
+ if (!resolvedPath) {
30018
+ const candidate = path10.isAbsolute(trimmed2) ? path10.normalize(trimmed2) : path10.normalize(path10.resolve(normalizedSessionParent, trimmed2));
30019
+ const gitRoot2 = getGitRepoRootSync(sessionParentPath);
29951
30020
  if (!gitRoot2) return null;
29952
- const rel = path9.relative(gitRoot2, candidate);
29953
- if (rel.startsWith("..") || path9.isAbsolute(rel)) return null;
29954
- abs = candidate;
30021
+ const rel = path10.relative(gitRoot2, candidate);
30022
+ if (rel.startsWith("..") || path10.isAbsolute(rel)) return null;
30023
+ resolvedPath = candidate;
29955
30024
  }
29956
- const gitRoot = getGitRepoRootSync(cwd);
30025
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
29957
30026
  if (gitRoot) {
29958
- const relFromRoot = path9.relative(gitRoot, abs);
29959
- if (!relFromRoot.startsWith("..") && !path9.isAbsolute(relFromRoot)) {
29960
- return { abs, display: relFromRoot.split(path9.sep).join("/") };
30027
+ const relFromRoot = path10.relative(gitRoot, resolvedPath);
30028
+ if (!relFromRoot.startsWith("..") && !path10.isAbsolute(relFromRoot)) {
30029
+ return { resolvedPath, display: relFromRoot.split(path10.sep).join("/") };
29961
30030
  }
29962
30031
  }
29963
- return { abs, display: toDisplayPathRelativeToCwd(cwd, abs) };
30032
+ return { resolvedPath, display: toDisplayPathRelativeToCwd(sessionParentPath, resolvedPath) };
29964
30033
  }
29965
- function readUtf8WorkspaceFile(cwd, displayPath) {
30034
+ function readUtf8WorkspaceFile(sessionParentPath, displayPath) {
29966
30035
  if (!displayPath || displayPath.includes("..")) return "";
29967
- const gitRoot = getGitRepoRootSync(cwd);
30036
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
29968
30037
  if (gitRoot) {
29969
- const abs2 = path9.resolve(gitRoot, displayPath);
29970
- const rel = path9.relative(gitRoot, abs2);
29971
- if (!rel.startsWith("..") && !path9.isAbsolute(rel)) {
30038
+ const resolvedPath2 = path10.resolve(gitRoot, displayPath);
30039
+ const rel = path10.relative(gitRoot, resolvedPath2);
30040
+ if (!rel.startsWith("..") && !path10.isAbsolute(rel)) {
29972
30041
  try {
29973
- return readFileSync4(abs2, "utf8");
30042
+ return readFileSync4(resolvedPath2, "utf8");
29974
30043
  } catch {
29975
30044
  }
29976
30045
  }
29977
30046
  }
29978
- const abs = resolveSafePathUnderCwd(cwd, displayPath);
29979
- if (!abs) return "";
30047
+ const resolvedPath = resolveSafePathUnderCwd(sessionParentPath, displayPath);
30048
+ if (!resolvedPath) return "";
29980
30049
  try {
29981
- return readFileSync4(abs, "utf8");
30050
+ return readFileSync4(resolvedPath, "utf8");
29982
30051
  } catch {
29983
30052
  return "";
29984
30053
  }
29985
30054
  }
29986
- function tryWorkspaceDisplayToAbs(cwd, displayPath) {
30055
+ function tryWorkspaceDisplayToPath(sessionParentPath, displayPath) {
29987
30056
  if (!displayPath || displayPath.includes("..")) return null;
29988
- const gitRoot = getGitRepoRootSync(cwd);
30057
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
29989
30058
  if (gitRoot) {
29990
- const abs = path9.resolve(gitRoot, displayPath);
29991
- const rel = path9.relative(gitRoot, abs);
29992
- if (!rel.startsWith("..") && !path9.isAbsolute(rel)) return abs;
30059
+ const resolvedPath = path10.resolve(gitRoot, displayPath);
30060
+ const rel = path10.relative(gitRoot, resolvedPath);
30061
+ if (!rel.startsWith("..") && !path10.isAbsolute(rel)) return resolvedPath;
29993
30062
  }
29994
- return resolveSafePathUnderCwd(cwd, displayPath);
30063
+ return resolveSafePathUnderCwd(sessionParentPath, displayPath);
29995
30064
  }
29996
- function readGitHeadBlob(cwd, displayPath) {
30065
+ function readGitHeadBlob(sessionParentPath, displayPath) {
29997
30066
  if (!displayPath || displayPath.includes("..")) return "";
29998
- const gitRoot = getGitRepoRootSync(cwd);
29999
- const execCwd = gitRoot ?? cwd;
30067
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
30068
+ const execCwd = gitRoot ?? sessionParentPath;
30000
30069
  try {
30001
30070
  return execFileSync3("git", ["show", `HEAD:${displayPath}`], {
30002
30071
  cwd: execCwd,
@@ -30009,9 +30078,9 @@ function readGitHeadBlob(cwd, displayPath) {
30009
30078
  }
30010
30079
 
30011
30080
  // src/agents/acp/session-file-change-path-kind.ts
30012
- function gitHeadPathObjectType(cwd, displayPath) {
30081
+ function gitHeadPathObjectType(sessionParentPath, displayPath) {
30013
30082
  if (!displayPath || displayPath.includes("..")) return null;
30014
- const gitRoot = getGitRepoRootSync(cwd);
30083
+ const gitRoot = getGitRepoRootSync(sessionParentPath);
30015
30084
  if (!gitRoot) return null;
30016
30085
  try {
30017
30086
  return execFileSync4("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
@@ -30022,11 +30091,11 @@ function gitHeadPathObjectType(cwd, displayPath) {
30022
30091
  return null;
30023
30092
  }
30024
30093
  }
30025
- function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
30026
- const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
30027
- if (abs && existsSync(abs)) {
30094
+ function getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath) {
30095
+ const resolvedPath = tryWorkspaceDisplayToPath(sessionParentPath, displayPath);
30096
+ if (resolvedPath && existsSync(resolvedPath)) {
30028
30097
  try {
30029
- if (statSync(abs).isDirectory()) {
30098
+ if (statSync(resolvedPath).isDirectory()) {
30030
30099
  return { isDirectory: true, directoryRemoved: false };
30031
30100
  }
30032
30101
  return { isDirectory: false, directoryRemoved: false };
@@ -30034,7 +30103,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
30034
30103
  return { isDirectory: false, directoryRemoved: false };
30035
30104
  }
30036
30105
  }
30037
- if (gitHeadPathObjectType(cwd, displayPath) === "tree") {
30106
+ if (gitHeadPathObjectType(sessionParentPath, displayPath) === "tree") {
30038
30107
  return { isDirectory: true, directoryRemoved: true };
30039
30108
  }
30040
30109
  return { isDirectory: false, directoryRemoved: false };
@@ -30042,7 +30111,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
30042
30111
 
30043
30112
  // src/agents/acp/hooks/bridge-on-file-change.ts
30044
30113
  function createBridgeOnFileChange(opts) {
30045
- const { routing, getSendSessionUpdate, log: log2 } = opts;
30114
+ const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
30046
30115
  return (evt) => {
30047
30116
  const runId = routing.runId;
30048
30117
  const sessionId = routing.sessionId;
@@ -30053,8 +30122,7 @@ function createBridgeOnFileChange(opts) {
30053
30122
  );
30054
30123
  return;
30055
30124
  }
30056
- const cwd = getBridgeWorkspaceDirectory();
30057
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, evt.path);
30125
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, evt.path);
30058
30126
  try {
30059
30127
  send({
30060
30128
  type: "session_file_change",
@@ -30131,12 +30199,12 @@ function extractDiffPath(o) {
30131
30199
  }
30132
30200
 
30133
30201
  // src/agents/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
30134
- function pushDiffIfComplete(o, cwd, out) {
30202
+ function pushDiffIfComplete(o, sessionParentPath, out) {
30135
30203
  const t = o.type;
30136
30204
  if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
30137
30205
  const rawPath = extractDiffPath(o);
30138
30206
  if (!rawPath) return;
30139
- const resolved = resolveWorkspaceFilePath(cwd, rawPath);
30207
+ const resolved = resolveWorkspaceFilePath(sessionParentPath, rawPath);
30140
30208
  if (!resolved) return;
30141
30209
  const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
30142
30210
  const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
@@ -30155,17 +30223,17 @@ var NEST_KEYS = [
30155
30223
  "data",
30156
30224
  "arguments"
30157
30225
  ];
30158
- function walkValue(value, cwd, depth, out) {
30226
+ function walkValue(value, sessionParentPath, depth, out) {
30159
30227
  if (depth > 12 || value == null) return;
30160
30228
  if (Array.isArray(value)) {
30161
- for (const x of value) walkValue(x, cwd, depth + 1, out);
30229
+ for (const x of value) walkValue(x, sessionParentPath, depth + 1, out);
30162
30230
  return;
30163
30231
  }
30164
30232
  if (typeof value !== "object") return;
30165
30233
  const o = value;
30166
- pushDiffIfComplete(o, cwd, out);
30234
+ pushDiffIfComplete(o, sessionParentPath, out);
30167
30235
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
30168
- walkValue(o.content, cwd, depth + 1, out);
30236
+ walkValue(o.content, sessionParentPath, depth + 1, out);
30169
30237
  }
30170
30238
  for (const k of NEST_KEYS) {
30171
30239
  if (o.type === "content" && k === "content") continue;
@@ -30173,23 +30241,23 @@ function walkValue(value, cwd, depth, out) {
30173
30241
  if (v == null) continue;
30174
30242
  if (k === "arguments" && typeof v === "string") {
30175
30243
  try {
30176
- walkValue(JSON.parse(v), cwd, depth + 1, out);
30244
+ walkValue(JSON.parse(v), sessionParentPath, depth + 1, out);
30177
30245
  } catch {
30178
30246
  }
30179
30247
  continue;
30180
30248
  }
30181
- walkValue(v, cwd, depth + 1, out);
30249
+ walkValue(v, sessionParentPath, depth + 1, out);
30182
30250
  }
30183
30251
  }
30184
30252
 
30185
30253
  // src/agents/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
30186
- function extractAcpFileDiffsFromUpdate(update, cwd) {
30254
+ function extractAcpFileDiffsFromUpdate(update, sessionParentPath) {
30187
30255
  if (!update || typeof update !== "object") return [];
30188
30256
  const u = update;
30189
30257
  const out = [];
30190
30258
  const content = u.content;
30191
30259
  if (Array.isArray(content)) {
30192
- for (const x of content) walkValue(x, cwd, 0, out);
30260
+ for (const x of content) walkValue(x, sessionParentPath, 0, out);
30193
30261
  }
30194
30262
  const byPath = /* @__PURE__ */ new Map();
30195
30263
  for (const d of out) byPath.set(d.path, d);
@@ -30197,18 +30265,18 @@ function extractAcpFileDiffsFromUpdate(update, cwd) {
30197
30265
  }
30198
30266
 
30199
30267
  // src/agents/acp/hooks/extract-tool-target-paths.ts
30200
- function addPath(cwd, raw, out) {
30268
+ function addPath(sessionParentPath, raw, out) {
30201
30269
  if (typeof raw !== "string") return;
30202
30270
  const trimmed2 = raw.trim();
30203
30271
  if (!trimmed2) return;
30204
- const resolved = resolveWorkspaceFilePath(cwd, trimmed2);
30272
+ const resolved = resolveWorkspaceFilePath(sessionParentPath, trimmed2);
30205
30273
  if (!resolved) return;
30206
30274
  out.add(resolved.display);
30207
30275
  }
30208
- function walkLocations(cwd, loc, out) {
30276
+ function walkLocations(sessionParentPath, loc, out) {
30209
30277
  if (!Array.isArray(loc)) return;
30210
30278
  for (const item of loc) {
30211
- if (item && typeof item === "object") addPath(cwd, item.path, out);
30279
+ if (item && typeof item === "object") addPath(sessionParentPath, item.path, out);
30212
30280
  }
30213
30281
  }
30214
30282
  var PATH_KEYS = [
@@ -30221,56 +30289,56 @@ var PATH_KEYS = [
30221
30289
  "file_path",
30222
30290
  "target_file"
30223
30291
  ];
30224
- function collectFromObject(cwd, obj, out, depth) {
30292
+ function collectFromObject(sessionParentPath, obj, out, depth) {
30225
30293
  if (depth > 10) return;
30226
30294
  for (const k of PATH_KEYS) {
30227
- if (k in obj) addPath(cwd, obj[k], out);
30295
+ if (k in obj) addPath(sessionParentPath, obj[k], out);
30228
30296
  }
30229
30297
  for (const v of Object.values(obj)) {
30230
- if (v != null && typeof v === "object") collectUnknown(cwd, v, out, depth + 1);
30298
+ if (v != null && typeof v === "object") collectUnknown(sessionParentPath, v, out, depth + 1);
30231
30299
  }
30232
30300
  }
30233
- function collectUnknown(cwd, v, out, depth) {
30301
+ function collectUnknown(sessionParentPath, v, out, depth) {
30234
30302
  if (depth > 10 || v == null) return;
30235
30303
  if (Array.isArray(v)) {
30236
- for (const x of v) collectUnknown(cwd, x, out, depth + 1);
30304
+ for (const x of v) collectUnknown(sessionParentPath, x, out, depth + 1);
30237
30305
  return;
30238
30306
  }
30239
- if (typeof v === "object") collectFromObject(cwd, v, out, depth);
30307
+ if (typeof v === "object") collectFromObject(sessionParentPath, v, out, depth);
30240
30308
  }
30241
- function walkContentArray(cwd, content, out) {
30309
+ function walkContentArray(sessionParentPath, content, out) {
30242
30310
  if (!Array.isArray(content)) return;
30243
30311
  for (const item of content) {
30244
30312
  if (!item || typeof item !== "object") continue;
30245
30313
  const o = item;
30246
30314
  if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
30247
- for (const k of PATH_KEYS) if (k in o) addPath(cwd, o[k], out);
30315
+ for (const k of PATH_KEYS) if (k in o) addPath(sessionParentPath, o[k], out);
30248
30316
  }
30249
30317
  if (o.type === "content" && o.content != null && typeof o.content === "object") {
30250
30318
  const inner = o.content;
30251
30319
  if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
30252
- for (const k of PATH_KEYS) if (k in inner) addPath(cwd, inner[k], out);
30320
+ for (const k of PATH_KEYS) if (k in inner) addPath(sessionParentPath, inner[k], out);
30253
30321
  }
30254
30322
  }
30255
30323
  }
30256
30324
  }
30257
- function extractToolTargetDisplayPaths(update, cwd) {
30325
+ function extractToolTargetDisplayPaths(update, sessionParentPath) {
30258
30326
  const out = /* @__PURE__ */ new Set();
30259
30327
  if (!update || typeof update !== "object") return [];
30260
30328
  const u = update;
30261
- walkLocations(cwd, u.locations, out);
30262
- walkLocations(cwd, u.fileLocations, out);
30263
- walkLocations(cwd, u.file_locations, out);
30329
+ walkLocations(sessionParentPath, u.locations, out);
30330
+ walkLocations(sessionParentPath, u.fileLocations, out);
30331
+ walkLocations(sessionParentPath, u.file_locations, out);
30264
30332
  const tc = u.toolCall ?? u.tool_call;
30265
30333
  if (tc && typeof tc.arguments === "string") {
30266
30334
  try {
30267
30335
  const parsed = JSON.parse(tc.arguments);
30268
- collectUnknown(cwd, parsed, out, 0);
30336
+ collectUnknown(sessionParentPath, parsed, out, 0);
30269
30337
  } catch {
30270
30338
  }
30271
30339
  }
30272
- walkContentArray(cwd, u.content, out);
30273
- collectFromObject(cwd, u, out, 0);
30340
+ walkContentArray(sessionParentPath, u.content, out);
30341
+ collectFromObject(sessionParentPath, u, out, 0);
30274
30342
  return [...out];
30275
30343
  }
30276
30344
 
@@ -30336,7 +30404,7 @@ var PathSnapshotTracker = class {
30336
30404
  this.beforeByToolKey.delete(toolKey);
30337
30405
  this.accumulatedPathsByToolKey.delete(toolKey);
30338
30406
  }
30339
- captureBeforeFromDisk(toolKey, paths, cwd) {
30407
+ captureBeforeFromDisk(toolKey, paths, sessionParentPath) {
30340
30408
  if (paths.length === 0) return;
30341
30409
  let m = this.beforeByToolKey.get(toolKey);
30342
30410
  if (!m) {
@@ -30345,10 +30413,10 @@ var PathSnapshotTracker = class {
30345
30413
  }
30346
30414
  for (const p of paths) {
30347
30415
  if (m.has(p)) continue;
30348
- m.set(p, readUtf8WorkspaceFile(cwd, p));
30416
+ m.set(p, readUtf8WorkspaceFile(sessionParentPath, p));
30349
30417
  }
30350
30418
  }
30351
- ensureBeforeFromHeadForMissing(toolKey, paths, cwd) {
30419
+ ensureBeforeFromHeadForMissing(toolKey, paths, sessionParentPath) {
30352
30420
  let m = this.beforeByToolKey.get(toolKey);
30353
30421
  if (!m) {
30354
30422
  m = /* @__PURE__ */ new Map();
@@ -30356,10 +30424,10 @@ var PathSnapshotTracker = class {
30356
30424
  }
30357
30425
  for (const p of paths) {
30358
30426
  if (m.has(p)) continue;
30359
- m.set(p, readGitHeadBlob(cwd, p));
30427
+ m.set(p, readGitHeadBlob(sessionParentPath, p));
30360
30428
  }
30361
30429
  }
30362
- flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
30430
+ flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
30363
30431
  const t = this.debouncers.get(toolKey);
30364
30432
  if (t) clearTimeout(t);
30365
30433
  this.debouncers.delete(toolKey);
@@ -30368,10 +30436,10 @@ var PathSnapshotTracker = class {
30368
30436
  this.beforeByToolKey.delete(toolKey);
30369
30437
  if (!send || !runId || !sessionId) return;
30370
30438
  for (const [displayPath, oldText] of beforeMap) {
30371
- const newText = readUtf8WorkspaceFile(cwd, displayPath);
30439
+ const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
30372
30440
  if (oldText === newText) continue;
30373
30441
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
30374
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
30442
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
30375
30443
  try {
30376
30444
  send({
30377
30445
  type: "session_file_change",
@@ -30390,31 +30458,31 @@ var PathSnapshotTracker = class {
30390
30458
  }
30391
30459
  }
30392
30460
  }
30393
- scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2) {
30461
+ scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
30394
30462
  const prev = this.debouncers.get(toolKey);
30395
30463
  if (prev) clearTimeout(prev);
30396
30464
  this.debouncers.set(
30397
30465
  toolKey,
30398
30466
  setTimeout(() => {
30399
30467
  this.debouncers.delete(toolKey);
30400
- this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
30468
+ this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
30401
30469
  }, PATH_SNAPSHOT_DEBOUNCE_MS)
30402
30470
  );
30403
30471
  }
30404
- handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, cwd, sentPaths, send, runId, sessionId, log2) {
30472
+ handleToolCallLifecycle(updateKind, toolKey, toolPaths, status, sessionParentPath, sentPaths, send, runId, sessionId, log2) {
30405
30473
  if (updateKind === "tool_call") {
30406
30474
  this.resetToolSnapshots(toolKey);
30407
30475
  }
30408
30476
  if (updateKind === "tool_call") {
30409
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
30477
+ this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
30410
30478
  } else if (updateKind === "tool_call_update") {
30411
30479
  if (isCompletedToolStatus(status)) {
30412
- this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, cwd);
30413
- this.flushPathSnapshots(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
30480
+ this.ensureBeforeFromHeadForMissing(toolKey, toolPaths, sessionParentPath);
30481
+ this.flushPathSnapshots(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
30414
30482
  } else {
30415
- this.captureBeforeFromDisk(toolKey, toolPaths, cwd);
30483
+ this.captureBeforeFromDisk(toolKey, toolPaths, sessionParentPath);
30416
30484
  if (this.beforeByToolKey.has(toolKey)) {
30417
- this.scheduleDebouncedFlush(toolKey, cwd, sentPaths, send, runId, sessionId, log2);
30485
+ this.scheduleDebouncedFlush(toolKey, sessionParentPath, sentPaths, send, runId, sessionId, log2);
30418
30486
  }
30419
30487
  }
30420
30488
  }
@@ -30422,11 +30490,11 @@ var PathSnapshotTracker = class {
30422
30490
  };
30423
30491
 
30424
30492
  // src/agents/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
30425
- function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentPaths, log2) {
30493
+ function sendExtractedDiffsAsSessionFileChanges(diffs, send, sessionParentPath, sessionId, runId, sentPaths, log2) {
30426
30494
  for (const d of diffs) {
30427
30495
  try {
30428
30496
  const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
30429
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, d.path);
30497
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, d.path);
30430
30498
  send({
30431
30499
  type: "session_file_change",
30432
30500
  sessionId,
@@ -30444,15 +30512,15 @@ function sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, run
30444
30512
  }
30445
30513
  }
30446
30514
  }
30447
- function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, sessionId, runId, log2) {
30515
+ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, sessionParentPath, sessionId, runId, log2) {
30448
30516
  for (const displayPath of mergedPaths) {
30449
30517
  if (sentPaths.has(displayPath)) continue;
30450
- const oldText = readGitHeadBlob(cwd, displayPath);
30451
- const newText = readUtf8WorkspaceFile(cwd, displayPath);
30518
+ const oldText = readGitHeadBlob(sessionParentPath, displayPath);
30519
+ const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
30452
30520
  if (oldText === newText) continue;
30453
30521
  try {
30454
30522
  const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
30455
- const dirFlags = getSessionFileChangeDirectoryFlags(cwd, displayPath);
30523
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
30456
30524
  send({
30457
30525
  type: "session_file_change",
30458
30526
  sessionId,
@@ -30473,13 +30541,12 @@ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, cwd, s
30473
30541
 
30474
30542
  // src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
30475
30543
  function createBridgeOnSessionUpdate(opts) {
30476
- const { routing, getSendSessionUpdate, log: log2 } = opts;
30544
+ const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
30477
30545
  const pathTracker = new PathSnapshotTracker();
30478
30546
  return (params) => {
30479
30547
  const runId = routing.runId;
30480
30548
  const sessionId = routing.sessionId;
30481
30549
  pathTracker.onRunIdChanged(runId);
30482
- const cwd = getBridgeWorkspaceDirectory();
30483
30550
  const send = getSendSessionUpdate();
30484
30551
  const sentFileChangePaths = /* @__PURE__ */ new Set();
30485
30552
  const p = params;
@@ -30487,7 +30554,7 @@ function createBridgeOnSessionUpdate(opts) {
30487
30554
  const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
30488
30555
  const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
30489
30556
  const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
30490
- const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, cwd) : [];
30557
+ const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, sessionParentPath) : [];
30491
30558
  const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
30492
30559
  if (updateKind === "tool_call") {
30493
30560
  pathTracker.resetToolSnapshots(toolKey);
@@ -30502,7 +30569,7 @@ function createBridgeOnSessionUpdate(opts) {
30502
30569
  toolKey,
30503
30570
  toolPaths,
30504
30571
  p.status,
30505
- cwd,
30572
+ sessionParentPath,
30506
30573
  sentFileChangePaths,
30507
30574
  deliver,
30508
30575
  runId ?? "",
@@ -30510,9 +30577,17 @@ function createBridgeOnSessionUpdate(opts) {
30510
30577
  log2
30511
30578
  );
30512
30579
  }
30513
- const diffs = extractAcpFileDiffsFromUpdate(params, cwd);
30580
+ const diffs = extractAcpFileDiffsFromUpdate(params, sessionParentPath);
30514
30581
  if (diffs.length > 0 && send && runId && sessionId) {
30515
- sendExtractedDiffsAsSessionFileChanges(diffs, send, cwd, sessionId, runId, sentFileChangePaths, log2);
30582
+ sendExtractedDiffsAsSessionFileChanges(
30583
+ diffs,
30584
+ send,
30585
+ sessionParentPath,
30586
+ sessionId,
30587
+ runId,
30588
+ sentFileChangePaths,
30589
+ log2
30590
+ );
30516
30591
  } else if (diffs.length > 0) {
30517
30592
  log2(
30518
30593
  `[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
@@ -30521,7 +30596,15 @@ function createBridgeOnSessionUpdate(opts) {
30521
30596
  if (isCompletedToolCallUpdate && send && runId && sessionId) {
30522
30597
  const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
30523
30598
  const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
30524
- sendGitHeadVsWorkspaceForToolPaths(merged, sentFileChangePaths, send, cwd, sessionId, runId, log2);
30599
+ sendGitHeadVsWorkspaceForToolPaths(
30600
+ merged,
30601
+ sentFileChangePaths,
30602
+ send,
30603
+ sessionParentPath,
30604
+ sessionId,
30605
+ runId,
30606
+ log2
30607
+ );
30525
30608
  pathTracker.accumulatedPathsByToolKey.delete(toolKey);
30526
30609
  }
30527
30610
  if (runId && send) {
@@ -30556,11 +30639,12 @@ function buildAcpSessionBridgeHooks(opts) {
30556
30639
 
30557
30640
  // src/agents/acp/ensure-acp-client.ts
30558
30641
  async function ensureAcpClient(options) {
30559
- const { state, preferredAgentType, mode, cwd, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
30560
- const targetCwd = path10.resolve(
30561
- cwd != null && String(cwd).trim() !== "" ? String(cwd).trim() : getBridgeWorkspaceDirectory()
30562
- );
30563
- if (state.acpHandle && state.lastAcpCwd != null && path10.resolve(state.lastAcpCwd) !== path10.resolve(targetCwd)) {
30642
+ const { state, preferredAgentType, mode, sessionParentPath, routing, sendSessionUpdate, sendRequest, log: log2 } = options;
30643
+ const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
30644
+ if (state.acpStartPromise && !state.acpHandle) {
30645
+ await state.acpStartPromise;
30646
+ }
30647
+ if (state.acpHandle && state.lastAcpCwd != null && path11.resolve(state.lastAcpCwd) !== path11.resolve(targetSessionParentPath)) {
30564
30648
  try {
30565
30649
  state.acpHandle.disconnect();
30566
30650
  } catch {
@@ -30592,14 +30676,14 @@ async function ensureAcpClient(options) {
30592
30676
  if (!state.acpStartPromise) {
30593
30677
  let statOk = false;
30594
30678
  try {
30595
- const st = fs9.statSync(targetCwd);
30679
+ const st = fs9.statSync(targetSessionParentPath);
30596
30680
  statOk = st.isDirectory();
30597
30681
  if (!statOk) {
30598
- state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
30682
+ state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
30599
30683
  log2(`[Agent] ${state.lastAcpStartError}`);
30600
30684
  }
30601
30685
  } catch {
30602
- state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetCwd}`;
30686
+ state.lastAcpStartError = `Agent cwd missing or inaccessible: ${targetSessionParentPath}`;
30603
30687
  log2(`[Agent] ${state.lastAcpStartError}`);
30604
30688
  }
30605
30689
  if (!statOk) {
@@ -30607,6 +30691,7 @@ async function ensureAcpClient(options) {
30607
30691
  }
30608
30692
  const hooks = buildAcpSessionBridgeHooks({
30609
30693
  routing,
30694
+ sessionParentPath: targetSessionParentPath,
30610
30695
  getSendSessionUpdate: () => sendSessionUpdate,
30611
30696
  getSendRequest: () => sendRequest,
30612
30697
  log: log2
@@ -30614,7 +30699,6 @@ async function ensureAcpClient(options) {
30614
30699
  state.acpStartPromise = resolved.createClient({
30615
30700
  command: resolved.command,
30616
30701
  sessionMode: mode,
30617
- cwd: targetCwd,
30618
30702
  backendAgentType: preferredAgentType,
30619
30703
  onAgentSubprocessExit: () => {
30620
30704
  state.acpHandle = null;
@@ -30622,11 +30706,12 @@ async function ensureAcpClient(options) {
30622
30706
  state.acpAgentKey = null;
30623
30707
  state.lastAcpStartError = "Agent subprocess exited";
30624
30708
  },
30625
- ...hooks
30709
+ ...hooks,
30710
+ cwd: targetSessionParentPath
30626
30711
  }).then((h) => {
30627
30712
  state.lastAcpStartError = null;
30628
30713
  state.acpHandle = h;
30629
- state.lastAcpCwd = targetCwd;
30714
+ state.lastAcpCwd = targetSessionParentPath;
30630
30715
  state.acpAgentKey = agentKey;
30631
30716
  return h;
30632
30717
  }).catch((err) => {
@@ -30675,7 +30760,7 @@ async function createAcpManager(options) {
30675
30760
  runId,
30676
30761
  mode,
30677
30762
  agentType,
30678
- cwd,
30763
+ sessionParentPath,
30679
30764
  sendResult: sendResult2,
30680
30765
  sendSessionUpdate,
30681
30766
  followUpCatalogPromptId,
@@ -30694,7 +30779,7 @@ async function createAcpManager(options) {
30694
30779
  state,
30695
30780
  preferredAgentType: preferredForPrompt,
30696
30781
  mode,
30697
- cwd,
30782
+ sessionParentPath,
30698
30783
  routing: promptRouting,
30699
30784
  sendSessionUpdate,
30700
30785
  sendRequest: sendSessionUpdate,
@@ -30743,7 +30828,7 @@ async function createAcpManager(options) {
30743
30828
  sessionId,
30744
30829
  runId,
30745
30830
  agentType: preferredForPrompt,
30746
- agentCwd: cwd,
30831
+ agentCwd: resolveSessionParentPathForAgentProcess(sessionParentPath),
30747
30832
  sendResult: sendResult2,
30748
30833
  sendSessionUpdate,
30749
30834
  log: log2,
@@ -30798,12 +30883,12 @@ async function createAcpManager(options) {
30798
30883
  }
30799
30884
 
30800
30885
  // src/worktrees/session-worktree-manager.ts
30801
- import * as path17 from "node:path";
30886
+ import * as path18 from "node:path";
30802
30887
  import os4 from "node:os";
30803
30888
 
30804
30889
  // src/worktrees/prepare-new-session-worktrees.ts
30805
30890
  import * as fs11 from "node:fs";
30806
- import * as path12 from "node:path";
30891
+ import * as path13 from "node:path";
30807
30892
 
30808
30893
  // src/git/worktree-add.ts
30809
30894
  async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
@@ -30813,11 +30898,11 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
30813
30898
 
30814
30899
  // src/worktrees/worktree-layout-file.ts
30815
30900
  import * as fs10 from "node:fs";
30816
- import * as path11 from "node:path";
30901
+ import * as path12 from "node:path";
30817
30902
  import os3 from "node:os";
30818
30903
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
30819
30904
  function defaultWorktreeLayoutPath() {
30820
- return path11.join(os3.homedir(), ".buildautomaton", LAYOUT_FILENAME);
30905
+ return path12.join(os3.homedir(), ".buildautomaton", LAYOUT_FILENAME);
30821
30906
  }
30822
30907
  function normalizeLoadedLayout(raw) {
30823
30908
  if (raw && typeof raw === "object" && "launcherCwds" in raw) {
@@ -30838,24 +30923,24 @@ function loadWorktreeLayout() {
30838
30923
  }
30839
30924
  function saveWorktreeLayout(layout) {
30840
30925
  try {
30841
- const dir = path11.dirname(defaultWorktreeLayoutPath());
30926
+ const dir = path12.dirname(defaultWorktreeLayoutPath());
30842
30927
  fs10.mkdirSync(dir, { recursive: true });
30843
30928
  fs10.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
30844
30929
  } catch {
30845
30930
  }
30846
30931
  }
30847
- function baseNameSafe(abs) {
30848
- return path11.basename(abs).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
30932
+ function baseNameSafe(pathString) {
30933
+ return path12.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
30849
30934
  }
30850
- function getLauncherDirNameIfPresent(layout, launcherCwdAbs) {
30851
- const norm = path11.resolve(launcherCwdAbs);
30852
- const existing = layout.launcherCwds.find((e) => path11.resolve(e.absolutePath) === norm);
30935
+ function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
30936
+ const norm = path12.resolve(bridgeRootPath2);
30937
+ const existing = layout.launcherCwds.find((e) => path12.resolve(e.absolutePath) === norm);
30853
30938
  return existing?.dirName;
30854
30939
  }
30855
- function allocateDirNameForLauncherCwd(layout, launcherCwdAbs) {
30856
- const existing = getLauncherDirNameIfPresent(layout, launcherCwdAbs);
30940
+ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
30941
+ const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
30857
30942
  if (existing) return existing;
30858
- const norm = path11.resolve(launcherCwdAbs);
30943
+ const norm = path12.resolve(bridgeRootPath2);
30859
30944
  const base = baseNameSafe(norm);
30860
30945
  const used = new Set(layout.launcherCwds.map((e) => e.dirName));
30861
30946
  let name = base;
@@ -30871,24 +30956,27 @@ function allocateDirNameForLauncherCwd(layout, launcherCwdAbs) {
30871
30956
 
30872
30957
  // src/worktrees/prepare-new-session-worktrees.ts
30873
30958
  async function prepareNewSessionWorktrees(options) {
30874
- const { rootAbs, launcherCwd, sessionId, layout, log: log2 } = options;
30875
- const launcherResolved = path12.resolve(launcherCwd);
30876
- const cwdKey = allocateDirNameForLauncherCwd(layout, launcherResolved);
30877
- const agentMirrorRoot = path12.join(rootAbs, cwdKey);
30878
- const repos = await discoverGitReposUnderRoot(launcherResolved);
30959
+ const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
30960
+ const bridgeResolved = path13.resolve(bridgeRoot);
30961
+ const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
30962
+ const bridgeKeyDir = path13.join(worktreesRootPath, cwdKey);
30963
+ const sessionDir = path13.join(bridgeKeyDir, sessionId);
30964
+ const repos = await discoverGitReposUnderRoot(bridgeResolved);
30879
30965
  if (repos.length === 0) {
30880
- log2("[worktrees] No Git repositories under launcher working directory; skipping worktree creation.");
30966
+ log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
30881
30967
  return null;
30882
30968
  }
30883
30969
  const branch = `session-${sessionId}`;
30884
30970
  const worktreePaths = [];
30885
- fs11.mkdirSync(agentMirrorRoot, { recursive: true });
30971
+ fs11.mkdirSync(sessionDir, { recursive: true });
30886
30972
  for (const repo of repos) {
30887
- let rel = path12.relative(launcherResolved, repo.absolutePath);
30888
- if (rel.startsWith("..") || path12.isAbsolute(rel)) continue;
30973
+ let rel = path13.relative(bridgeResolved, repo.absolutePath);
30974
+ if (rel.startsWith("..") || path13.isAbsolute(rel)) continue;
30889
30975
  const relNorm = rel === "" ? "." : rel;
30890
- const wtPath = path12.join(agentMirrorRoot, relNorm, sessionId);
30891
- fs11.mkdirSync(path12.dirname(wtPath), { recursive: true });
30976
+ const wtPath = relNorm === "." ? sessionDir : path13.join(sessionDir, relNorm);
30977
+ if (relNorm !== ".") {
30978
+ fs11.mkdirSync(path13.dirname(wtPath), { recursive: true });
30979
+ }
30892
30980
  try {
30893
30981
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
30894
30982
  log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
@@ -30900,7 +30988,11 @@ async function prepareNewSessionWorktrees(options) {
30900
30988
  }
30901
30989
  }
30902
30990
  if (worktreePaths.length === 0) return null;
30903
- return { worktreePaths, agentCwd: agentMirrorRoot };
30991
+ return {
30992
+ worktreePaths,
30993
+ sessionParentPath: sessionDir,
30994
+ workingTreeRelRoot: sessionDir
30995
+ };
30904
30996
  }
30905
30997
 
30906
30998
  // src/git/rename-branch.ts
@@ -30932,16 +31024,16 @@ import * as fs13 from "node:fs";
30932
31024
 
30933
31025
  // src/git/resolve-main-repo-from-git-file.ts
30934
31026
  import * as fs12 from "node:fs";
30935
- import * as path13 from "node:path";
31027
+ import * as path14 from "node:path";
30936
31028
  function resolveMainRepoFromWorktreeGitFile(wt) {
30937
- const gitDirFile = path13.join(wt, ".git");
31029
+ const gitDirFile = path14.join(wt, ".git");
30938
31030
  if (!fs12.existsSync(gitDirFile) || !fs12.statSync(gitDirFile).isFile()) return "";
30939
31031
  const first2 = fs12.readFileSync(gitDirFile, "utf8").trim();
30940
31032
  const m = first2.match(/^gitdir:\s*(.+)$/im);
30941
31033
  if (!m) return "";
30942
- const gitWorktreePath = path13.resolve(wt, m[1].trim());
30943
- const gitDir = path13.dirname(path13.dirname(gitWorktreePath));
30944
- return path13.dirname(gitDir);
31034
+ const gitWorktreePath = path14.resolve(wt, m[1].trim());
31035
+ const gitDir = path14.dirname(path14.dirname(gitWorktreePath));
31036
+ return path14.dirname(gitDir);
30945
31037
  }
30946
31038
 
30947
31039
  // src/git/worktree-remove.ts
@@ -31183,7 +31275,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
31183
31275
  }
31184
31276
 
31185
31277
  // src/git/working-directory/changes/get-working-tree-change-repo-details.ts
31186
- import * as path15 from "node:path";
31278
+ import * as path16 from "node:path";
31187
31279
 
31188
31280
  // src/git/working-directory/changes/parse-git-status.ts
31189
31281
  function parseNameStatusLines(lines) {
@@ -31304,16 +31396,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
31304
31396
 
31305
31397
  // src/git/working-directory/changes/list-changed-files-for-repo.ts
31306
31398
  import * as fs16 from "node:fs";
31307
- import * as path14 from "node:path";
31399
+ import * as path15 from "node:path";
31308
31400
 
31309
31401
  // src/git/working-directory/changes/count-lines.ts
31310
31402
  import { createReadStream } from "node:fs";
31311
31403
  import * as readline2 from "node:readline";
31312
- async function countTextFileLines(absFile) {
31404
+ async function countTextFileLines(filePath) {
31313
31405
  let bytes = 0;
31314
31406
  const maxBytes = 512e3;
31315
31407
  let lines = 0;
31316
- const stream = createReadStream(absFile, { encoding: "utf8" });
31408
+ const stream = createReadStream(filePath, { encoding: "utf8" });
31317
31409
  const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
31318
31410
  for await (const _line of rl) {
31319
31411
  lines += 1;
@@ -31341,15 +31433,15 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
31341
31433
  return null;
31342
31434
  }
31343
31435
  }
31344
- async function readWorktreeFileLines(abs) {
31436
+ async function readWorktreeFileLines(filePath) {
31345
31437
  try {
31346
- const raw = await fs15.promises.readFile(abs, "utf8");
31438
+ const raw = await fs15.promises.readFile(filePath, "utf8");
31347
31439
  return raw.split(/\r?\n/);
31348
31440
  } catch {
31349
31441
  return null;
31350
31442
  }
31351
31443
  }
31352
- async function hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pathInRepo, change) {
31444
+ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, change) {
31353
31445
  if (!patch.trim() || patch.includes("Binary files")) return patch;
31354
31446
  const all = patch.split("\n");
31355
31447
  const out = [];
@@ -31366,7 +31458,7 @@ async function hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pa
31366
31458
  };
31367
31459
  const diskLines = async () => {
31368
31460
  if (diskCache !== void 0) return diskCache;
31369
- diskCache = await readWorktreeFileLines(absFile);
31461
+ diskCache = await readWorktreeFileLines(filePath);
31370
31462
  return diskCache;
31371
31463
  };
31372
31464
  while (i < all.length) {
@@ -31478,7 +31570,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
31478
31570
  const rows = [];
31479
31571
  for (const pathInRepo of paths) {
31480
31572
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
31481
- const abs = path14.join(repoGitCwd, pathInRepo);
31573
+ const repoFilePath = path15.join(repoGitCwd, pathInRepo);
31482
31574
  const nums = numByPath.get(pathInRepo);
31483
31575
  let additions = nums?.additions ?? 0;
31484
31576
  let deletions = nums?.deletions ?? 0;
@@ -31491,8 +31583,8 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
31491
31583
  deletions = fromGit.deletions;
31492
31584
  } else {
31493
31585
  try {
31494
- const st = await fs16.promises.stat(abs);
31495
- if (st.isFile()) additions = await countTextFileLines(abs);
31586
+ const st = await fs16.promises.stat(repoFilePath);
31587
+ if (st.isFile()) additions = await countTextFileLines(repoFilePath);
31496
31588
  else additions = 0;
31497
31589
  } catch {
31498
31590
  additions = 0;
@@ -31517,10 +31609,10 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
31517
31609
  } else {
31518
31610
  pathInRepo = row.pathRelLauncher;
31519
31611
  }
31520
- const absFile = path14.join(repoGitCwd, pathInRepo);
31612
+ const filePath = path15.join(repoGitCwd, pathInRepo);
31521
31613
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
31522
31614
  if (patch) {
31523
- patch = await hydrateUnifiedPatchWithFileContext(patch, absFile, repoGitCwd, pathInRepo, row.change);
31615
+ patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
31524
31616
  }
31525
31617
  row.patchContent = patch;
31526
31618
  }
@@ -31533,8 +31625,9 @@ function normRepoRel(p) {
31533
31625
  return x === "" ? "." : x;
31534
31626
  }
31535
31627
  async function getWorkingTreeChangeRepoDetails(options) {
31536
- const launcher = path15.resolve(getBridgeWorkspaceDirectory());
31537
- const mirror = options.agentMirrorRootAbs ? path15.resolve(options.agentMirrorRootAbs) : null;
31628
+ const bridgeRoot = path16.resolve(getBridgeRoot());
31629
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path16.resolve(options.sessionWorktreeRootPath) : null;
31630
+ const legacyNested = options.legacyRepoNestedSessionLayout === true;
31538
31631
  const out = [];
31539
31632
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
31540
31633
  const basisInput = options.basis ?? { kind: "working" };
@@ -31545,8 +31638,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
31545
31638
  throw new Error("commit sha is required for commit changes");
31546
31639
  }
31547
31640
  const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
31548
- for (const target of options.commitTargetAbsDirs) {
31549
- const t = path15.resolve(target);
31641
+ for (const target of options.commitTargetPaths) {
31642
+ const t = path16.resolve(target);
31550
31643
  if (!await isGitRepoDirectory(t)) continue;
31551
31644
  const g = simpleGit(t);
31552
31645
  let branch = "HEAD";
@@ -31558,8 +31651,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
31558
31651
  const remoteUrl = await getRemoteOriginUrl(t);
31559
31652
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
31560
31653
  let repoRelPath;
31561
- if (mirror) {
31562
- const relNorm = path15.relative(mirror, path15.dirname(t));
31654
+ if (sessionWtRoot) {
31655
+ const anchor = legacyNested ? path16.dirname(t) : t;
31656
+ const relNorm = path16.relative(sessionWtRoot, anchor);
31563
31657
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
31564
31658
  } else {
31565
31659
  let top = t;
@@ -31568,8 +31662,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
31568
31662
  } catch {
31569
31663
  top = t;
31570
31664
  }
31571
- const rel = path15.relative(launcher, path15.resolve(top)).replace(/\\/g, "/") || ".";
31572
- repoRelPath = rel.startsWith("..") ? path15.basename(path15.resolve(top)) : rel;
31665
+ const rel = path16.relative(bridgeRoot, path16.resolve(top)).replace(/\\/g, "/") || ".";
31666
+ repoRelPath = rel.startsWith("..") ? path16.basename(path16.resolve(top)) : rel;
31573
31667
  }
31574
31668
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
31575
31669
  if (filter && norm !== filter) continue;
@@ -31635,14 +31729,37 @@ async function commitSessionWorktrees(options) {
31635
31729
 
31636
31730
  // src/worktrees/discover-session-worktree-on-disk.ts
31637
31731
  import * as fs17 from "node:fs";
31638
- import * as path16 from "node:path";
31639
- function isGitDir(abs) {
31732
+ import * as path17 from "node:path";
31733
+ function isGitDir(dirPath) {
31640
31734
  try {
31641
- return fs17.existsSync(path16.join(abs, ".git"));
31735
+ return fs17.existsSync(path17.join(dirPath, ".git"));
31642
31736
  } catch {
31643
31737
  return false;
31644
31738
  }
31645
31739
  }
31740
+ function collectGitRepoRootsUnderDirectory(rootPath) {
31741
+ const out = [];
31742
+ const walk = (dir) => {
31743
+ if (isGitDir(dir)) {
31744
+ out.push(path17.resolve(dir));
31745
+ return;
31746
+ }
31747
+ let entries;
31748
+ try {
31749
+ entries = fs17.readdirSync(dir, { withFileTypes: true });
31750
+ } catch {
31751
+ return;
31752
+ }
31753
+ for (const e of entries) {
31754
+ if (e.name.startsWith(".")) continue;
31755
+ const full = path17.join(dir, e.name);
31756
+ if (!e.isDirectory()) continue;
31757
+ walk(full);
31758
+ }
31759
+ };
31760
+ walk(path17.resolve(rootPath));
31761
+ return [...new Set(out)];
31762
+ }
31646
31763
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
31647
31764
  const out = [];
31648
31765
  const walk = (dir, depth) => {
@@ -31655,10 +31772,10 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
31655
31772
  }
31656
31773
  for (const e of entries) {
31657
31774
  if (e.name.startsWith(".")) continue;
31658
- const full = path16.join(dir, e.name);
31775
+ const full = path17.join(dir, e.name);
31659
31776
  if (!e.isDirectory()) continue;
31660
31777
  if (e.name === sessionId) {
31661
- if (isGitDir(full)) out.push(path16.resolve(full));
31778
+ if (isGitDir(full)) out.push(path17.resolve(full));
31662
31779
  } else {
31663
31780
  walk(full, depth + 1);
31664
31781
  }
@@ -31667,16 +31784,55 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
31667
31784
  walk(root, 0);
31668
31785
  return out;
31669
31786
  }
31787
+ function tryBindingFromSessionDirectory(sessionDir) {
31788
+ let st;
31789
+ try {
31790
+ st = fs17.statSync(sessionDir);
31791
+ } catch {
31792
+ return null;
31793
+ }
31794
+ if (!st.isDirectory()) return null;
31795
+ const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
31796
+ if (worktreePaths.length === 0) return null;
31797
+ const abs = path17.resolve(sessionDir);
31798
+ return {
31799
+ sessionParentPath: abs,
31800
+ workingTreeRelRoot: abs,
31801
+ repoCheckoutPaths: worktreePaths
31802
+ };
31803
+ }
31804
+ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
31805
+ const sid = sessionId.trim();
31806
+ if (!sid) return null;
31807
+ const hintR = path17.resolve(checkoutPath);
31808
+ let best = null;
31809
+ let cur = path17.dirname(hintR);
31810
+ for (let i = 0; i < 40; i++) {
31811
+ const paths = collectWorktreeRootsNamed(cur, sid, 24);
31812
+ if (paths.some((p) => path17.resolve(p) === hintR)) {
31813
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path17.resolve(paths[0]);
31814
+ best = {
31815
+ sessionParentPath: path17.resolve(isolated),
31816
+ workingTreeRelRoot: path17.resolve(cur),
31817
+ repoCheckoutPaths: paths.map((p) => path17.resolve(p))
31818
+ };
31819
+ }
31820
+ const next = path17.dirname(cur);
31821
+ if (next === cur) break;
31822
+ cur = next;
31823
+ }
31824
+ return best;
31825
+ }
31670
31826
  function discoverSessionWorktreeOnDisk(options) {
31671
- const { sessionId, worktreesRootAbs, layout, launcherCwd } = options;
31672
- if (!sessionId.trim() || !fs17.existsSync(worktreesRootAbs)) return null;
31673
- const preferredKey = getLauncherDirNameIfPresent(layout, launcherCwd);
31827
+ const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
31828
+ if (!sessionId.trim() || !fs17.existsSync(worktreesRootPath)) return null;
31829
+ const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
31674
31830
  const keys = [];
31675
31831
  if (preferredKey) keys.push(preferredKey);
31676
31832
  try {
31677
- for (const name of fs17.readdirSync(worktreesRootAbs)) {
31833
+ for (const name of fs17.readdirSync(worktreesRootPath)) {
31678
31834
  if (name.startsWith(".")) continue;
31679
- const p = path16.join(worktreesRootAbs, name);
31835
+ const p = path17.join(worktreesRootPath, name);
31680
31836
  if (!fs17.statSync(p).isDirectory()) continue;
31681
31837
  if (name !== preferredKey) keys.push(name);
31682
31838
  }
@@ -31684,26 +31840,58 @@ function discoverSessionWorktreeOnDisk(options) {
31684
31840
  return null;
31685
31841
  }
31686
31842
  for (const key of keys) {
31687
- const mirrorRoot = path16.join(worktreesRootAbs, key);
31688
- if (!fs17.existsSync(mirrorRoot) || !fs17.statSync(mirrorRoot).isDirectory()) continue;
31689
- const worktreePaths = collectWorktreeRootsNamed(mirrorRoot, sessionId, 24);
31690
- if (worktreePaths.length > 0) {
31691
- return { agentCwd: path16.resolve(mirrorRoot), worktreePaths };
31843
+ const layoutRoot = path17.join(worktreesRootPath, key);
31844
+ if (!fs17.existsSync(layoutRoot) || !fs17.statSync(layoutRoot).isDirectory()) continue;
31845
+ const sessionDir = path17.join(layoutRoot, sessionId);
31846
+ const nested = tryBindingFromSessionDirectory(sessionDir);
31847
+ if (nested) return nested;
31848
+ const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
31849
+ if (legacyPaths.length > 0) {
31850
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path17.resolve(legacyPaths[0]);
31851
+ return {
31852
+ sessionParentPath: path17.resolve(isolated),
31853
+ workingTreeRelRoot: path17.resolve(layoutRoot),
31854
+ repoCheckoutPaths: legacyPaths.map((p) => path17.resolve(p))
31855
+ };
31692
31856
  }
31693
31857
  }
31694
31858
  return null;
31695
31859
  }
31696
- function discoverSessionWorktreesUnderMirrorRoot(mirrorRootAbs, sessionId) {
31697
- const mirrorRoot = path16.resolve(mirrorRootAbs);
31698
- if (!sessionId.trim() || !fs17.existsSync(mirrorRoot)) return null;
31860
+ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
31861
+ const sid = sessionId.trim();
31862
+ if (!sid) return null;
31863
+ const hint = path17.resolve(sessionWorktreeRootPathOrHint);
31864
+ const underHint = tryBindingFromSessionDirectory(path17.join(hint, sid));
31865
+ if (underHint) return underHint;
31866
+ const direct = tryBindingFromSessionDirectory(hint);
31867
+ if (direct) {
31868
+ if (path17.basename(hint) === sid && isGitDir(hint)) {
31869
+ const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
31870
+ if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
31871
+ return legacyFromCheckout;
31872
+ }
31873
+ }
31874
+ return direct;
31875
+ }
31876
+ if (path17.basename(hint) === sid && isGitDir(hint)) {
31877
+ const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
31878
+ if (legacyFromCheckout) return legacyFromCheckout;
31879
+ }
31880
+ let st;
31699
31881
  try {
31700
- if (!fs17.statSync(mirrorRoot).isDirectory()) return null;
31882
+ st = fs17.statSync(hint);
31701
31883
  } catch {
31702
31884
  return null;
31703
31885
  }
31704
- const worktreePaths = collectWorktreeRootsNamed(mirrorRoot, sessionId, 24);
31705
- if (worktreePaths.length === 0) return null;
31706
- return { agentCwd: mirrorRoot, worktreePaths };
31886
+ if (!st.isDirectory()) return null;
31887
+ const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
31888
+ if (legacyPaths.length === 0) return null;
31889
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path17.resolve(legacyPaths[0]);
31890
+ return {
31891
+ sessionParentPath: path17.resolve(isolated),
31892
+ workingTreeRelRoot: hint,
31893
+ repoCheckoutPaths: legacyPaths.map((p) => path17.resolve(p))
31894
+ };
31707
31895
  }
31708
31896
 
31709
31897
  // src/worktrees/session-worktree-manager.ts
@@ -31713,42 +31901,86 @@ function parseSessionParent(v) {
31713
31901
  return null;
31714
31902
  }
31715
31903
  var SessionWorktreeManager = class {
31716
- rootAbs;
31904
+ worktreesRootPath;
31717
31905
  log;
31718
- sessionPaths = /* @__PURE__ */ new Map();
31719
- sessionAgentCwd = /* @__PURE__ */ new Map();
31906
+ sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
31907
+ sessionParentPathBySession = /* @__PURE__ */ new Map();
31908
+ sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
31720
31909
  layout;
31721
31910
  constructor(options) {
31722
- this.rootAbs = options.worktreesRootAbs;
31911
+ this.worktreesRootPath = options.worktreesRootPath;
31723
31912
  this.log = options.log;
31724
31913
  this.layout = loadWorktreeLayout();
31725
31914
  }
31726
- rememberWorktrees(sessionId, agentCwd, worktreePaths) {
31727
- this.sessionPaths.set(sessionId, worktreePaths);
31728
- this.sessionAgentCwd.set(sessionId, path17.resolve(agentCwd));
31915
+ rememberSessionWorktrees(sessionId, binding) {
31916
+ const paths = binding.repoCheckoutPaths.map((p) => path18.resolve(p));
31917
+ this.sessionRepoCheckoutPaths.set(sessionId, paths);
31918
+ this.sessionParentPathBySession.set(sessionId, path18.resolve(binding.sessionParentPath));
31919
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path18.resolve(binding.workingTreeRelRoot));
31920
+ }
31921
+ sessionParentPathAfterRemember(sessionId) {
31922
+ return this.sessionParentPathBySession.get(sessionId);
31729
31923
  }
31730
31924
  tryDiscoverFromDisk(sessionId) {
31731
31925
  return discoverSessionWorktreeOnDisk({
31732
31926
  sessionId,
31733
- worktreesRootAbs: this.rootAbs,
31927
+ worktreesRootPath: this.worktreesRootPath,
31734
31928
  layout: this.layout,
31735
- launcherCwd: getBridgeWorkspaceDirectory()
31929
+ bridgeRoot: getBridgeRoot()
31736
31930
  });
31737
31931
  }
31932
+ isLegacyNestedLayout(sessionId) {
31933
+ const parent = this.sessionParentPathBySession.get(sessionId);
31934
+ const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
31935
+ if (!parent || !relRoot) return false;
31936
+ return path18.resolve(parent) !== path18.resolve(relRoot);
31937
+ }
31938
+ /**
31939
+ * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
31940
+ */
31941
+ getIsolatedSessionParentPathForSession(sessionId) {
31942
+ if (!sessionId) return null;
31943
+ const sid = sessionId.trim();
31944
+ const cached2 = this.sessionParentPathBySession.get(sid);
31945
+ if (cached2) return path18.resolve(cached2);
31946
+ const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
31947
+ if (!paths?.length) return null;
31948
+ return resolveIsolatedSessionParentPathFromCheckouts(paths);
31949
+ }
31738
31950
  /**
31739
- * Returns cwd for the agent (mirror of launcher tree), or undefined to use the bridge workspace directory.
31951
+ * Resolved **session parent path** for the agent: session directory in worktrees mode,
31952
+ * or `undefined` meaning use {@link getBridgeRoot}.
31740
31953
  */
31741
- async resolveCwdForPrompt(sessionId, opts) {
31954
+ async resolveSessionParentPathForPrompt(sessionId, opts) {
31742
31955
  if (!sessionId) return void 0;
31956
+ const sid = sessionId.trim();
31743
31957
  const parentPathRaw = opts.sessionParentPath?.trim();
31744
31958
  if (parentPathRaw) {
31745
- const agentCwd = path17.resolve(parentPathRaw);
31746
- const sid = sessionId?.trim();
31959
+ const resolved = path18.resolve(parentPathRaw);
31747
31960
  if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
31748
- const fromMirror = discoverSessionWorktreesUnderMirrorRoot(agentCwd, sid);
31749
- if (fromMirror) this.rememberWorktrees(sid, fromMirror.agentCwd, fromMirror.worktreePaths);
31961
+ const diskFirst = this.tryDiscoverFromDisk(sid);
31962
+ if (diskFirst) {
31963
+ this.rememberSessionWorktrees(sid, diskFirst);
31964
+ return this.sessionParentPathAfterRemember(sid);
31965
+ }
31966
+ const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
31967
+ if (fromRoot) {
31968
+ this.rememberSessionWorktrees(sid, fromRoot);
31969
+ return this.sessionParentPathAfterRemember(sid);
31970
+ }
31971
+ let cur = resolved;
31972
+ for (let i = 0; i < 16; i++) {
31973
+ const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
31974
+ if (tryRoot) {
31975
+ this.rememberSessionWorktrees(sid, tryRoot);
31976
+ return this.sessionParentPathAfterRemember(sid);
31977
+ }
31978
+ const next = path18.dirname(cur);
31979
+ if (next === cur) break;
31980
+ cur = next;
31981
+ }
31750
31982
  }
31751
- return agentCwd;
31983
+ return resolved;
31752
31984
  }
31753
31985
  const parentKind = parseSessionParent(opts.sessionParent);
31754
31986
  if (parentKind === "bridge_root") {
@@ -31756,78 +31988,102 @@ var SessionWorktreeManager = class {
31756
31988
  }
31757
31989
  if (parentKind === "worktrees_root") {
31758
31990
  if (!opts.isNewSession) {
31759
- const cached2 = this.sessionAgentCwd.get(sessionId);
31760
- if (cached2) return path17.resolve(cached2);
31761
- const disc = this.tryDiscoverFromDisk(sessionId);
31991
+ const cached2 = this.sessionParentPathAfterRemember(sid);
31992
+ if (cached2) return cached2;
31993
+ const disc = this.tryDiscoverFromDisk(sid);
31762
31994
  if (disc) {
31763
- this.rememberWorktrees(sessionId, disc.agentCwd, disc.worktreePaths);
31764
- return path17.resolve(disc.agentCwd);
31995
+ this.rememberSessionWorktrees(sid, disc);
31996
+ return this.sessionParentPathAfterRemember(sid);
31765
31997
  }
31766
31998
  return void 0;
31767
31999
  }
31768
32000
  const prep2 = await prepareNewSessionWorktrees({
31769
- rootAbs: this.rootAbs,
31770
- launcherCwd: getBridgeWorkspaceDirectory(),
31771
- sessionId,
32001
+ worktreesRootPath: this.worktreesRootPath,
32002
+ bridgeRoot: getBridgeRoot(),
32003
+ sessionId: sid,
31772
32004
  layout: this.layout,
31773
32005
  log: this.log
31774
32006
  });
31775
32007
  if (!prep2) return void 0;
31776
- this.rememberWorktrees(sessionId, prep2.agentCwd, prep2.worktreePaths);
31777
- return path17.resolve(prep2.agentCwd);
32008
+ this.rememberSessionWorktrees(sid, {
32009
+ sessionParentPath: prep2.sessionParentPath,
32010
+ workingTreeRelRoot: prep2.workingTreeRelRoot,
32011
+ repoCheckoutPaths: prep2.worktreePaths
32012
+ });
32013
+ return this.sessionParentPathAfterRemember(sid);
31778
32014
  }
31779
32015
  if (!opts.isNewSession) {
31780
- const cached2 = this.sessionAgentCwd.get(sessionId);
31781
- if (cached2) return path17.resolve(cached2);
31782
- const disc = this.tryDiscoverFromDisk(sessionId);
32016
+ const cached2 = this.sessionParentPathAfterRemember(sid);
32017
+ if (cached2) return cached2;
32018
+ const disc = this.tryDiscoverFromDisk(sid);
31783
32019
  if (disc) {
31784
- this.rememberWorktrees(sessionId, disc.agentCwd, disc.worktreePaths);
31785
- return path17.resolve(disc.agentCwd);
32020
+ this.rememberSessionWorktrees(sid, disc);
32021
+ return this.sessionParentPathAfterRemember(sid);
31786
32022
  }
31787
32023
  return void 0;
31788
32024
  }
31789
32025
  const prep = await prepareNewSessionWorktrees({
31790
- rootAbs: this.rootAbs,
31791
- launcherCwd: getBridgeWorkspaceDirectory(),
31792
- sessionId,
32026
+ worktreesRootPath: this.worktreesRootPath,
32027
+ bridgeRoot: getBridgeRoot(),
32028
+ sessionId: sid,
31793
32029
  layout: this.layout,
31794
32030
  log: this.log
31795
32031
  });
31796
32032
  if (!prep) return void 0;
31797
- this.rememberWorktrees(sessionId, prep.agentCwd, prep.worktreePaths);
31798
- return path17.resolve(prep.agentCwd);
32033
+ this.rememberSessionWorktrees(sid, {
32034
+ sessionParentPath: prep.sessionParentPath,
32035
+ workingTreeRelRoot: prep.workingTreeRelRoot,
32036
+ repoCheckoutPaths: prep.worktreePaths
32037
+ });
32038
+ return this.sessionParentPathAfterRemember(sid);
31799
32039
  }
31800
32040
  async renameSessionBranch(sessionId, newBranch) {
31801
- const paths = this.sessionPaths.get(sessionId);
32041
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
31802
32042
  if (!paths?.length) return;
31803
32043
  await renameSessionWorktreeBranches(paths, newBranch, this.log);
31804
32044
  }
31805
- /** True when this session runs in an isolated worktree mirror (not launcher cwd). */
32045
+ /** True when this session uses an isolated worktree layout (not the bridge root). */
31806
32046
  usesWorktreeSession(sessionId) {
31807
32047
  if (!sessionId) return false;
31808
- return this.sessionAgentCwd.has(sessionId);
32048
+ return this.sessionParentPathBySession.has(sessionId);
31809
32049
  }
31810
- getWorktreePathsForSession(sessionId) {
32050
+ /** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
32051
+ getRepoCheckoutPathsForSession(sessionId) {
31811
32052
  if (!sessionId) return void 0;
31812
- const paths = this.sessionPaths.get(sessionId);
32053
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
31813
32054
  return paths?.length ? [...paths] : void 0;
31814
32055
  }
31815
- /** Session mirror root (parent of per-repo worktrees), when using worktrees for this session. */
31816
- getAgentCwdForSession(sessionId) {
31817
- if (!sessionId) return null;
31818
- const c = this.sessionAgentCwd.get(sessionId);
31819
- return c ? path17.resolve(c) : null;
32056
+ /**
32057
+ * Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
32058
+ * restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
32059
+ */
32060
+ ensureRepoCheckoutPathsForSession(sessionId) {
32061
+ if (!sessionId?.trim()) return void 0;
32062
+ const sid = sessionId.trim();
32063
+ const cached2 = this.sessionRepoCheckoutPaths.get(sid);
32064
+ if (cached2?.length) return [...cached2];
32065
+ const disc = this.tryDiscoverFromDisk(sid);
32066
+ if (disc?.repoCheckoutPaths.length) {
32067
+ this.rememberSessionWorktrees(sid, disc);
32068
+ return [...disc.repoCheckoutPaths];
32069
+ }
32070
+ return void 0;
32071
+ }
32072
+ /** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
32073
+ getSessionWorktreeRootForSession(sessionId) {
32074
+ return this.getIsolatedSessionParentPathForSession(sessionId);
31820
32075
  }
31821
32076
  async removeSessionWorktrees(sessionId) {
31822
- const paths = this.sessionPaths.get(sessionId);
31823
- this.sessionPaths.delete(sessionId);
31824
- this.sessionAgentCwd.delete(sessionId);
32077
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
32078
+ this.sessionRepoCheckoutPaths.delete(sessionId);
32079
+ this.sessionParentPathBySession.delete(sessionId);
32080
+ this.sessionWorkingTreeRelRootBySession.delete(sessionId);
31825
32081
  if (!paths?.length) return;
31826
32082
  await removeSessionWorktrees(paths, this.log);
31827
32083
  }
31828
32084
  async commitSession(params) {
31829
- const paths = this.sessionPaths.get(params.sessionId);
31830
- const targets = paths?.length ? paths : [getBridgeWorkspaceDirectory()];
32085
+ const paths = this.sessionRepoCheckoutPaths.get(params.sessionId);
32086
+ const targets = paths?.length ? paths : [getBridgeRoot()];
31831
32087
  return commitSessionWorktrees({
31832
32088
  paths: targets,
31833
32089
  branch: params.branch,
@@ -31836,14 +32092,14 @@ var SessionWorktreeManager = class {
31836
32092
  });
31837
32093
  }
31838
32094
  resolveCommitTargets(sessionId) {
31839
- const paths = this.sessionPaths.get(sessionId);
32095
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
31840
32096
  if (paths?.length) return paths;
31841
32097
  const disc = this.tryDiscoverFromDisk(sessionId);
31842
- if (disc?.worktreePaths.length) {
31843
- this.rememberWorktrees(sessionId, disc.agentCwd, disc.worktreePaths);
31844
- return disc.worktreePaths;
32098
+ if (disc?.repoCheckoutPaths.length) {
32099
+ this.rememberSessionWorktrees(sessionId, disc);
32100
+ return disc.repoCheckoutPaths;
31845
32101
  }
31846
- return [getBridgeWorkspaceDirectory()];
32102
+ return [getBridgeRoot()];
31847
32103
  }
31848
32104
  async getSessionWorkingTreeStatus(sessionId) {
31849
32105
  return aggregateSessionPathsWorkingTreeStatus(this.resolveCommitTargets(sessionId));
@@ -31851,10 +32107,12 @@ var SessionWorktreeManager = class {
31851
32107
  /** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
31852
32108
  async getSessionWorkingTreeChangeDetails(sessionId, opts) {
31853
32109
  const targets = this.resolveCommitTargets(sessionId);
31854
- const mirror = this.getAgentCwdForSession(sessionId);
32110
+ const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
32111
+ const legacyNested = this.isLegacyNestedLayout(sessionId);
31855
32112
  return getWorkingTreeChangeRepoDetails({
31856
- commitTargetAbsDirs: targets,
31857
- agentMirrorRootAbs: mirror,
32113
+ commitTargetPaths: targets,
32114
+ sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
32115
+ legacyRepoNestedSessionLayout: legacyNested,
31858
32116
  repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
31859
32117
  basis: opts?.basis
31860
32118
  });
@@ -31869,16 +32127,16 @@ var SessionWorktreeManager = class {
31869
32127
  }
31870
32128
  }
31871
32129
  };
31872
- function defaultWorktreesRootAbs() {
31873
- return path17.join(os4.homedir(), ".buildautomaton", "worktrees");
32130
+ function defaultWorktreesRootPath() {
32131
+ return path18.join(os4.homedir(), ".buildautomaton", "worktrees");
31874
32132
  }
31875
32133
 
31876
32134
  // src/files/watch-file-index.ts
31877
32135
  import { watch } from "node:fs";
31878
- import path24 from "node:path";
32136
+ import path25 from "node:path";
31879
32137
 
31880
32138
  // src/files/index/build-file-index.ts
31881
- import path21 from "node:path";
32139
+ import path22 from "node:path";
31882
32140
 
31883
32141
  // src/runtime/yield-to-event-loop.ts
31884
32142
  function yieldToEventLoop() {
@@ -31887,13 +32145,13 @@ function yieldToEventLoop() {
31887
32145
 
31888
32146
  // src/files/index/walk-workspace-tree.ts
31889
32147
  import fs18 from "node:fs";
31890
- import path19 from "node:path";
32148
+ import path20 from "node:path";
31891
32149
 
31892
32150
  // src/files/index/constants.ts
31893
- import path18 from "node:path";
32151
+ import path19 from "node:path";
31894
32152
  import os5 from "node:os";
31895
32153
  var INDEX_WORK_YIELD_EVERY = 256;
31896
- var INDEX_DIR = path18.join(os5.homedir(), ".buildautomaton");
32154
+ var INDEX_DIR = path19.join(os5.homedir(), ".buildautomaton");
31897
32155
  var INDEX_HASH_LEN = 16;
31898
32156
  var INDEX_VERSION = 2;
31899
32157
  var INDEX_LOG_PREFIX = "[file-index]";
@@ -31908,14 +32166,14 @@ function walkWorkspaceTreeSync(dir, baseDir, out) {
31908
32166
  }
31909
32167
  for (const name of names) {
31910
32168
  if (name.startsWith(".")) continue;
31911
- const full = path19.join(dir, name);
32169
+ const full = path20.join(dir, name);
31912
32170
  let stat2;
31913
32171
  try {
31914
32172
  stat2 = fs18.statSync(full);
31915
32173
  } catch {
31916
32174
  continue;
31917
32175
  }
31918
- const relative5 = path19.relative(baseDir, full).replace(/\\/g, "/");
32176
+ const relative5 = path20.relative(baseDir, full).replace(/\\/g, "/");
31919
32177
  if (stat2.isDirectory()) {
31920
32178
  walkWorkspaceTreeSync(full, baseDir, out);
31921
32179
  } else if (stat2.isFile()) {
@@ -31936,14 +32194,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, out, state) {
31936
32194
  await yieldToEventLoop();
31937
32195
  }
31938
32196
  state.n++;
31939
- const full = path19.join(dir, name);
32197
+ const full = path20.join(dir, name);
31940
32198
  let stat2;
31941
32199
  try {
31942
32200
  stat2 = await fs18.promises.stat(full);
31943
32201
  } catch {
31944
32202
  continue;
31945
32203
  }
31946
- const relative5 = path19.relative(baseDir, full).replace(/\\/g, "/");
32204
+ const relative5 = path20.relative(baseDir, full).replace(/\\/g, "/");
31947
32205
  if (stat2.isDirectory()) {
31948
32206
  await walkWorkspaceTreeAsync(full, baseDir, out, state);
31949
32207
  } else if (stat2.isFile()) {
@@ -32027,11 +32285,11 @@ async function buildTrigramMapForPathsAsync(paths) {
32027
32285
  import fs19 from "node:fs";
32028
32286
 
32029
32287
  // src/files/index/paths.ts
32030
- import path20 from "node:path";
32288
+ import path21 from "node:path";
32031
32289
  import crypto2 from "node:crypto";
32032
32290
  function getIndexPathForCwd(resolvedCwd) {
32033
32291
  const hash = crypto2.createHash("sha256").update(resolvedCwd).digest("hex").slice(0, INDEX_HASH_LEN);
32034
- return path20.join(INDEX_DIR, `.file-index-${hash}.json`);
32292
+ return path21.join(INDEX_DIR, `.file-index-${hash}.json`);
32035
32293
  }
32036
32294
 
32037
32295
  // src/files/index/write-index-file.ts
@@ -32062,7 +32320,7 @@ function sortPaths(paths) {
32062
32320
  paths.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
32063
32321
  }
32064
32322
  function buildFileIndex(cwd) {
32065
- const resolved = path21.resolve(cwd);
32323
+ const resolved = path22.resolve(cwd);
32066
32324
  const paths = [];
32067
32325
  walkWorkspaceTreeSync(resolved, resolved, paths);
32068
32326
  sortPaths(paths);
@@ -32072,7 +32330,7 @@ function buildFileIndex(cwd) {
32072
32330
  return data;
32073
32331
  }
32074
32332
  async function buildFileIndexAsync(cwd) {
32075
- const resolved = path21.resolve(cwd);
32333
+ const resolved = path22.resolve(cwd);
32076
32334
  const paths = [];
32077
32335
  await walkWorkspaceTreeAsync(resolved, resolved, paths, createWalkYieldState());
32078
32336
  await yieldToEventLoop();
@@ -32085,9 +32343,9 @@ async function buildFileIndexAsync(cwd) {
32085
32343
 
32086
32344
  // src/files/index/load-file-index.ts
32087
32345
  import fs20 from "node:fs";
32088
- import path22 from "node:path";
32346
+ import path23 from "node:path";
32089
32347
  function loadFileIndex(cwd) {
32090
- const resolved = path22.resolve(cwd);
32348
+ const resolved = path23.resolve(cwd);
32091
32349
  const indexPath = getIndexPathForCwd(resolved);
32092
32350
  try {
32093
32351
  const raw = fs20.readFileSync(indexPath, "utf8");
@@ -32109,9 +32367,9 @@ function loadFileIndex(cwd) {
32109
32367
  }
32110
32368
 
32111
32369
  // src/files/index/ensure-file-index.ts
32112
- import path23 from "node:path";
32370
+ import path24 from "node:path";
32113
32371
  async function ensureFileIndexAsync(cwd) {
32114
- const resolved = path23.resolve(cwd);
32372
+ const resolved = path24.resolve(cwd);
32115
32373
  const cached2 = loadFileIndex(resolved);
32116
32374
  if (cached2 !== null) return { data: cached2, fromCache: true };
32117
32375
  const data = await buildFileIndexAsync(resolved);
@@ -32193,8 +32451,8 @@ function createFsWatcher(resolved, schedule) {
32193
32451
  throw e;
32194
32452
  }
32195
32453
  }
32196
- function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
32197
- const resolved = path24.resolve(cwd);
32454
+ function startFileIndexWatcher(cwd = getBridgeRoot()) {
32455
+ const resolved = path25.resolve(cwd);
32198
32456
  void buildFileIndexAsync(resolved).catch((e) => {
32199
32457
  console.error("[file-index] Initial index build failed:", e);
32200
32458
  });
@@ -32532,10 +32790,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
32532
32790
  import { spawn as spawn7 } from "node:child_process";
32533
32791
  import fs23 from "node:fs";
32534
32792
  import { tmpdir } from "node:os";
32535
- import path25 from "node:path";
32793
+ import path26 from "node:path";
32536
32794
  function trySpawnMergedLogFile(command, env, cwd, signal) {
32537
- const tmpRoot = fs23.mkdtempSync(path25.join(tmpdir(), "ba-devsrv-log-"));
32538
- const logPath = path25.join(tmpRoot, "combined.log");
32795
+ const tmpRoot = fs23.mkdtempSync(path26.join(tmpdir(), "ba-devsrv-log-"));
32796
+ const logPath = path26.join(tmpRoot, "combined.log");
32539
32797
  let logFd;
32540
32798
  try {
32541
32799
  logFd = fs23.openSync(logPath, "a");
@@ -32579,15 +32837,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
32579
32837
  import { spawn as spawn8 } from "node:child_process";
32580
32838
  import fs24 from "node:fs";
32581
32839
  import { tmpdir as tmpdir2 } from "node:os";
32582
- import path26 from "node:path";
32840
+ import path27 from "node:path";
32583
32841
  function shSingleQuote(s) {
32584
32842
  return `'${s.replace(/'/g, `'\\''`)}'`;
32585
32843
  }
32586
32844
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
32587
- const tmpRoot = fs24.mkdtempSync(path26.join(tmpdir2(), "ba-devsrv-sh-"));
32588
- const logPath = path26.join(tmpRoot, "combined.log");
32589
- const innerPath = path26.join(tmpRoot, "_cmd.sh");
32590
- const runnerPath = path26.join(tmpRoot, "_run.sh");
32845
+ const tmpRoot = fs24.mkdtempSync(path27.join(tmpdir2(), "ba-devsrv-sh-"));
32846
+ const logPath = path27.join(tmpRoot, "combined.log");
32847
+ const innerPath = path27.join(tmpRoot, "_cmd.sh");
32848
+ const runnerPath = path27.join(tmpRoot, "_run.sh");
32591
32849
  try {
32592
32850
  fs24.writeFileSync(innerPath, `#!/bin/sh
32593
32851
  ${command}
@@ -32618,9 +32876,9 @@ cd ${shSingleQuote(cwd)}
32618
32876
  }
32619
32877
  }
32620
32878
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
32621
- const tmpRoot = fs24.mkdtempSync(path26.join(tmpdir2(), "ba-devsrv-sh-"));
32622
- const logPath = path26.join(tmpRoot, "combined.log");
32623
- const runnerPath = path26.join(tmpRoot, "_run.bat");
32879
+ const tmpRoot = fs24.mkdtempSync(path27.join(tmpdir2(), "ba-devsrv-sh-"));
32880
+ const logPath = path27.join(tmpRoot, "combined.log");
32881
+ const runnerPath = path27.join(tmpRoot, "_run.bat");
32624
32882
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
32625
32883
  const com = process.env.ComSpec || "cmd.exe";
32626
32884
  try {
@@ -32830,13 +33088,13 @@ var DevServerManager = class {
32830
33088
  abortControllersByServerId = /* @__PURE__ */ new Map();
32831
33089
  getWs;
32832
33090
  log;
32833
- getBridgeCwd;
33091
+ getBridgeRoot;
32834
33092
  e2ee;
32835
33093
  firehoseSink;
32836
33094
  constructor(options) {
32837
33095
  this.getWs = options.getWs;
32838
33096
  this.log = options.log;
32839
- this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
33097
+ this.getBridgeRoot = options.getBridgeRoot ?? (() => process.cwd());
32840
33098
  this.e2ee = options.e2ee;
32841
33099
  this.firehoseSink = new DevServerFirehoseSink({
32842
33100
  getTails: (serverId) => this.snapshotTails(serverId),
@@ -32925,7 +33183,7 @@ var DevServerManager = class {
32925
33183
  this.sendStatus(serverId, "starting", void 0, emptyTails());
32926
33184
  const ac = new AbortController();
32927
33185
  this.abortControllersByServerId.set(serverId, ac);
32928
- const cwd = this.getBridgeCwd();
33186
+ const cwd = this.getBridgeRoot();
32929
33187
  const childEnv = envForSpawn(process.env, def.env, def.ports);
32930
33188
  const cmd = substituteCommand(def.command.trim(), childEnv);
32931
33189
  const title = def.name.trim() || serverId.slice(0, 8);
@@ -33348,13 +33606,13 @@ function createOnBridgeIdentified(opts) {
33348
33606
 
33349
33607
  // src/skills/discover-local-agent-skills.ts
33350
33608
  import fs25 from "node:fs";
33351
- import path27 from "node:path";
33609
+ import path28 from "node:path";
33352
33610
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
33353
33611
  function discoverLocalSkills(cwd) {
33354
33612
  const out = [];
33355
33613
  const seenKeys = /* @__PURE__ */ new Set();
33356
33614
  for (const rel of SKILL_DISCOVERY_ROOTS) {
33357
- const base = path27.join(cwd, rel);
33615
+ const base = path28.join(cwd, rel);
33358
33616
  if (!fs25.existsSync(base) || !fs25.statSync(base).isDirectory()) continue;
33359
33617
  let entries = [];
33360
33618
  try {
@@ -33363,13 +33621,13 @@ function discoverLocalSkills(cwd) {
33363
33621
  continue;
33364
33622
  }
33365
33623
  for (const name of entries) {
33366
- const dir = path27.join(base, name);
33624
+ const dir = path28.join(base, name);
33367
33625
  try {
33368
33626
  if (!fs25.statSync(dir).isDirectory()) continue;
33369
33627
  } catch {
33370
33628
  continue;
33371
33629
  }
33372
- const skillMd = path27.join(dir, "SKILL.md");
33630
+ const skillMd = path28.join(dir, "SKILL.md");
33373
33631
  if (!fs25.existsSync(skillMd)) continue;
33374
33632
  const key = `${rel}/${name}`;
33375
33633
  if (seenKeys.has(key)) continue;
@@ -33382,7 +33640,7 @@ function discoverLocalSkills(cwd) {
33382
33640
  function discoverSkillLayoutRoots(cwd) {
33383
33641
  const roots = [];
33384
33642
  for (const rel of SKILL_DISCOVERY_ROOTS) {
33385
- const base = path27.join(cwd, rel);
33643
+ const base = path28.join(cwd, rel);
33386
33644
  if (!fs25.existsSync(base) || !fs25.statSync(base).isDirectory()) continue;
33387
33645
  let entries = [];
33388
33646
  try {
@@ -33392,13 +33650,13 @@ function discoverSkillLayoutRoots(cwd) {
33392
33650
  }
33393
33651
  const skills2 = [];
33394
33652
  for (const name of entries) {
33395
- const dir = path27.join(base, name);
33653
+ const dir = path28.join(base, name);
33396
33654
  try {
33397
33655
  if (!fs25.statSync(dir).isDirectory()) continue;
33398
33656
  } catch {
33399
33657
  continue;
33400
33658
  }
33401
- if (!fs25.existsSync(path27.join(dir, "SKILL.md"))) continue;
33659
+ if (!fs25.existsSync(path28.join(dir, "SKILL.md"))) continue;
33402
33660
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
33403
33661
  skills2.push({ name, relPath });
33404
33662
  }
@@ -33442,7 +33700,7 @@ function createSendLocalSkillsReport(getWs, logFn) {
33442
33700
  try {
33443
33701
  const socket = getWs();
33444
33702
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
33445
- const skills2 = discoverLocalSkills(getBridgeWorkspaceDirectory());
33703
+ const skills2 = discoverLocalSkills(getBridgeRoot());
33446
33704
  sendWsMessage(socket, { type: "local_skills", skills: skills2 });
33447
33705
  } catch (e) {
33448
33706
  logFn(
@@ -33580,9 +33838,6 @@ var handleAgentConfigMessage = (msg, deps) => {
33580
33838
  handleBridgeAgentConfig(msg, deps);
33581
33839
  };
33582
33840
 
33583
- // src/agents/acp/from-bridge/handle-bridge-prompt.ts
33584
- import * as path29 from "node:path";
33585
-
33586
33841
  // src/prompt-turn-queue/client-report.ts
33587
33842
  function sendPromptQueueClientReport(ws, queues) {
33588
33843
  if (!ws) return false;
@@ -33596,7 +33851,7 @@ import fs27 from "node:fs";
33596
33851
  // src/prompt-turn-queue/paths.ts
33597
33852
  import crypto3 from "node:crypto";
33598
33853
  import fs26 from "node:fs";
33599
- import path28 from "node:path";
33854
+ import path29 from "node:path";
33600
33855
  import os6 from "node:os";
33601
33856
  var QUEUE_KEY_HEX_64 = /^[a-f0-9]{64}$/i;
33602
33857
  function queueStateFileSlug(queueKey) {
@@ -33605,15 +33860,15 @@ function queueStateFileSlug(queueKey) {
33605
33860
  }
33606
33861
  function getPromptQueuesDirectory() {
33607
33862
  const override = process.env.BUILDAMATON_PROMPT_QUEUES_DIR?.trim();
33608
- if (override) return path28.resolve(override);
33609
- return path28.join(os6.homedir(), ".buildautomaton", "queues");
33863
+ if (override) return path29.resolve(override);
33864
+ return path29.join(os6.homedir(), ".buildautomaton", "queues");
33610
33865
  }
33611
33866
  function ensurePromptQueuesDirectory() {
33612
33867
  const dir = getPromptQueuesDirectory();
33613
33868
  if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
33614
33869
  }
33615
33870
  function queueStateFilePath(queueKey) {
33616
- return path28.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
33871
+ return path29.join(getPromptQueuesDirectory(), `${queueStateFileSlug(queueKey)}.json`);
33617
33872
  }
33618
33873
 
33619
33874
  // src/prompt-turn-queue/disk-store.ts
@@ -33685,6 +33940,10 @@ function hasRunningTurn(turns) {
33685
33940
  }
33686
33941
  function dispatchLocalPrompt(next, deps) {
33687
33942
  const pl = next.payload;
33943
+ const rawParent = pl["sessionParent"];
33944
+ const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
33945
+ const rawParentPath = pl["sessionParentPath"];
33946
+ const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
33688
33947
  const msg = {
33689
33948
  type: "prompt",
33690
33949
  sessionId: next.sessionId,
@@ -33692,6 +33951,8 @@ function dispatchLocalPrompt(next, deps) {
33692
33951
  prompt: pl.prompt,
33693
33952
  mode: typeof pl.mode === "string" ? pl.mode : "agent",
33694
33953
  isNewSession: pl.isNewSession === true,
33954
+ ...sessionParent ? { sessionParent } : {},
33955
+ ...sessionParentPath ? { sessionParentPath } : {},
33695
33956
  ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
33696
33957
  ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
33697
33958
  ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
@@ -33803,23 +34064,24 @@ async function readGitBranch(cwd) {
33803
34064
  async function runBridgePromptPreamble(params) {
33804
34065
  const { getWs, log: log2, sessionWorktreeManager, sessionId, runId, effectiveCwd } = params;
33805
34066
  const s = getWs();
33806
- const worktreePaths = sessionWorktreeManager.getWorktreePathsForSession(sessionId) ?? [];
34067
+ const repoCheckoutPaths = sessionWorktreeManager.ensureRepoCheckoutPathsForSession(sessionId);
33807
34068
  const repoRoots = await resolveSnapshotRepoRoots({
33808
- worktreePaths,
34069
+ worktreePaths: repoCheckoutPaths,
33809
34070
  fallbackCwd: effectiveCwd,
34071
+ sessionId: sessionId?.trim() || void 0,
33810
34072
  log: log2
33811
34073
  });
33812
34074
  if (s && sessionId) {
33813
34075
  const cliGitBranch = await readGitBranch(effectiveCwd);
33814
34076
  const usesWt = sessionWorktreeManager.usesWorktreeSession(sessionId);
33815
- const mirrorAbs = sessionWorktreeManager.getAgentCwdForSession(sessionId);
34077
+ const isolatedSessionParentPath = sessionWorktreeManager.getIsolatedSessionParentPathForSession(sessionId);
33816
34078
  sendWsMessage(s, {
33817
34079
  type: "session_git_context_report",
33818
34080
  sessionId,
33819
34081
  cliGitBranch,
33820
34082
  agentUsesWorktree: usesWt,
33821
34083
  sessionParent: usesWt ? "worktrees_root" : "bridge_root",
33822
- sessionParentPath: usesWt ? mirrorAbs : getBridgeWorkspaceDirectory()
34084
+ sessionParentPath: usesWt ? isolatedSessionParentPath ?? effectiveCwd : getBridgeRoot()
33823
34085
  });
33824
34086
  }
33825
34087
  if (s && sessionId && runId) {
@@ -33972,7 +34234,7 @@ function handleBridgePrompt(msg, deps) {
33972
34234
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
33973
34235
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
33974
34236
  async function preambleAndPrompt(resolvedCwd) {
33975
- const effectiveCwd = path29.resolve(resolvedCwd ?? getBridgeWorkspaceDirectory());
34237
+ const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
33976
34238
  await runBridgePromptPreamble({
33977
34239
  getWs,
33978
34240
  log: log2,
@@ -34005,7 +34267,7 @@ function handleBridgePrompt(msg, deps) {
34005
34267
  runId,
34006
34268
  mode,
34007
34269
  agentType,
34008
- cwd: effectiveCwd,
34270
+ sessionParentPath: effectiveCwd,
34009
34271
  sendResult: sendResult2,
34010
34272
  sendSessionUpdate,
34011
34273
  followUpCatalogPromptId,
@@ -34015,8 +34277,8 @@ function handleBridgePrompt(msg, deps) {
34015
34277
  e2ee: deps.e2ee
34016
34278
  });
34017
34279
  }
34018
- void sessionWorktreeManager.resolveCwdForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
34019
- log2(`[Agent] Worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`);
34280
+ void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
34281
+ log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
34020
34282
  void preambleAndPrompt(void 0);
34021
34283
  });
34022
34284
  }
@@ -34099,7 +34361,7 @@ import path31 from "node:path";
34099
34361
 
34100
34362
  // src/files/ensure-under-cwd.ts
34101
34363
  import path30 from "node:path";
34102
- function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
34364
+ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
34103
34365
  const normalized = path30.normalize(relativePath).replace(/^(\.\/)+/, "");
34104
34366
  const resolved = path30.resolve(cwd, normalized);
34105
34367
  if (!resolved.startsWith(cwd + path30.sep) && resolved !== cwd) {
@@ -34111,7 +34373,7 @@ function ensureUnderCwd(relativePath, cwd = getBridgeWorkspaceDirectory()) {
34111
34373
  // src/files/list-dir.ts
34112
34374
  var LIST_DIR_YIELD_EVERY = 256;
34113
34375
  async function listDirAsync(relativePath) {
34114
- const resolved = ensureUnderCwd(relativePath || ".", getBridgeWorkspaceDirectory());
34376
+ const resolved = ensureUnderCwd(relativePath || ".", getBridgeRoot());
34115
34377
  if (!resolved) {
34116
34378
  return { error: "Path is outside working directory" };
34117
34379
  }
@@ -34157,7 +34419,7 @@ async function listDirAsync(relativePath) {
34157
34419
  import fs29 from "node:fs";
34158
34420
  import { StringDecoder } from "node:string_decoder";
34159
34421
  function resolveFilePath(relativePath) {
34160
- const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
34422
+ const resolved = ensureUnderCwd(relativePath, getBridgeRoot());
34161
34423
  if (!resolved) return { error: "Path is outside working directory" };
34162
34424
  let real;
34163
34425
  try {
@@ -34350,7 +34612,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
34350
34612
  void (async () => {
34351
34613
  await yieldToEventLoop();
34352
34614
  const q = typeof msg.q === "string" ? msg.q : "";
34353
- const cwd = getBridgeWorkspaceDirectory();
34615
+ const cwd = getBridgeRoot();
34354
34616
  const index = loadFileIndex(cwd);
34355
34617
  if (index === null) {
34356
34618
  const payload2 = {
@@ -34374,7 +34636,7 @@ function handleFileBrowserSearch(msg, socket, e2ee) {
34374
34636
  }
34375
34637
  function triggerFileIndexBuild() {
34376
34638
  setImmediate(() => {
34377
- void ensureFileIndexAsync(getBridgeWorkspaceDirectory()).catch((e) => {
34639
+ void ensureFileIndexAsync(getBridgeRoot()).catch((e) => {
34378
34640
  console.error("[file-index] Background build failed:", e);
34379
34641
  });
34380
34642
  });
@@ -34443,7 +34705,7 @@ function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
34443
34705
  function handleSkillLayoutRequest(msg, deps) {
34444
34706
  const socket = deps.getWs();
34445
34707
  const id = typeof msg.id === "string" ? msg.id : "";
34446
- const roots = discoverSkillLayoutRoots(getBridgeWorkspaceDirectory());
34708
+ const roots = discoverSkillLayoutRoots(getBridgeRoot());
34447
34709
  if (socket) {
34448
34710
  sendWsMessage(socket, { type: "skill_layout_response", id, roots });
34449
34711
  }
@@ -34491,7 +34753,7 @@ var handleInstallSkillsMessage = (msg, deps) => {
34491
34753
  const id = typeof msg.id === "string" ? msg.id : "";
34492
34754
  const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
34493
34755
  const rawItems = msg.items;
34494
- const cwd = getBridgeWorkspaceDirectory();
34756
+ const cwd = getBridgeRoot();
34495
34757
  const result = installRemoteSkills(cwd, targetDir, rawItems);
34496
34758
  if (!result.success) {
34497
34759
  const err = result.error ?? "Invalid items";
@@ -34630,7 +34892,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
34630
34892
  void (async () => {
34631
34893
  const s = getWs();
34632
34894
  if (!s) return;
34633
- const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
34895
+ const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
34634
34896
  const file2 = snapshotFilePath(agentBase, turnId);
34635
34897
  if (!fs31.existsSync(file2)) {
34636
34898
  sendWsMessage(s, {
@@ -34990,9 +35252,9 @@ async function createBridgeConnection(options) {
34990
35252
  firehoseGeneration: 0,
34991
35253
  firehoseQuiet: createEmptyReconnectQuietSlot()
34992
35254
  };
34993
- const worktreesRootAbs = options.worktreesRootAbs ?? defaultWorktreesRootAbs();
35255
+ const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
34994
35256
  const sessionWorktreeManager = new SessionWorktreeManager({
34995
- worktreesRootAbs,
35257
+ worktreesRootPath,
34996
35258
  log: logFn
34997
35259
  });
34998
35260
  const acpManager = await createAcpManager({ log: logFn });
@@ -35001,7 +35263,7 @@ async function createBridgeConnection(options) {
35001
35263
  return state.currentWs;
35002
35264
  }
35003
35265
  const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
35004
- const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory, e2ee });
35266
+ const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
35005
35267
  const onBridgeIdentified = createOnBridgeIdentified({
35006
35268
  devServerManager,
35007
35269
  firehoseServerUrl,
@@ -35025,8 +35287,8 @@ async function createBridgeConnection(options) {
35025
35287
  getCloudAccessToken: () => tokens.accessToken
35026
35288
  };
35027
35289
  const identifyReportedPaths = {
35028
- bridgeRootPath: path33.resolve(getBridgeWorkspaceDirectory()),
35029
- worktreesRootPath: path33.resolve(worktreesRootAbs)
35290
+ bridgeRootPath: path33.resolve(getBridgeRoot()),
35291
+ worktreesRootPath: path33.resolve(worktreesRootPath)
35030
35292
  };
35031
35293
  const { connect } = createMainBridgeWebSocketLifecycle({
35032
35294
  state,
@@ -35043,7 +35305,7 @@ async function createBridgeConnection(options) {
35043
35305
  identifyReportedPaths
35044
35306
  });
35045
35307
  connect();
35046
- const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
35308
+ const stopFileIndexWatcher = startFileIndexWatcher(getBridgeRoot());
35047
35309
  return {
35048
35310
  close: async () => {
35049
35311
  stopFileIndexWatcher();
@@ -35113,7 +35375,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
35113
35375
  authToken,
35114
35376
  refreshToken,
35115
35377
  justAuthenticated,
35116
- worktreesRootAbs,
35378
+ worktreesRootPath,
35117
35379
  e2eCertificate
35118
35380
  } = options;
35119
35381
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -35125,7 +35387,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
35125
35387
  refreshToken,
35126
35388
  firehoseServerUrl,
35127
35389
  justAuthenticated,
35128
- worktreesRootAbs,
35390
+ worktreesRootPath,
35129
35391
  e2eCertificate,
35130
35392
  log,
35131
35393
  persistTokens: (t) => {
@@ -35140,7 +35402,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
35140
35402
  log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
35141
35403
  clearConfigForApi(apiUrl);
35142
35404
  void handle.close().then(() => {
35143
- void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootAbs, e2eCertificate });
35405
+ void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootPath, e2eCertificate });
35144
35406
  });
35145
35407
  }
35146
35408
  });
@@ -35188,7 +35450,7 @@ async function runBridge(options) {
35188
35450
  workspaceId,
35189
35451
  authToken,
35190
35452
  bridgeName,
35191
- worktreesRootAbs,
35453
+ worktreesRootPath,
35192
35454
  e2eCertificate
35193
35455
  } = options;
35194
35456
  const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
@@ -35233,7 +35495,7 @@ async function runBridge(options) {
35233
35495
  firehoseServerUrl,
35234
35496
  bridgeName,
35235
35497
  justAuthenticated: true,
35236
- worktreesRootAbs,
35498
+ worktreesRootPath,
35237
35499
  e2eCertificate
35238
35500
  });
35239
35501
  return;