@buildautomaton/cli 0.1.57 → 0.1.59

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
@@ -4175,8 +4175,8 @@ var init_parseUtil = __esm({
4175
4175
  init_errors();
4176
4176
  init_en();
4177
4177
  makeIssue = (params) => {
4178
- const { data, path: path73, errorMaps, issueData } = params;
4179
- const fullPath = [...path73, ...issueData.path || []];
4178
+ const { data, path: path74, errorMaps, issueData } = params;
4179
+ const fullPath = [...path74, ...issueData.path || []];
4180
4180
  const fullIssue = {
4181
4181
  ...issueData,
4182
4182
  path: fullPath
@@ -4484,11 +4484,11 @@ var init_types = __esm({
4484
4484
  init_parseUtil();
4485
4485
  init_util();
4486
4486
  ParseInputLazyPath = class {
4487
- constructor(parent, value, path73, key) {
4487
+ constructor(parent, value, path74, key) {
4488
4488
  this._cachedPath = [];
4489
4489
  this.parent = parent;
4490
4490
  this.data = value;
4491
- this._path = path73;
4491
+ this._path = path74;
4492
4492
  this._key = key;
4493
4493
  }
4494
4494
  get path() {
@@ -8103,10 +8103,10 @@ function assignProp(target, prop, value) {
8103
8103
  configurable: true
8104
8104
  });
8105
8105
  }
8106
- function getElementAtPath(obj, path73) {
8107
- if (!path73)
8106
+ function getElementAtPath(obj, path74) {
8107
+ if (!path74)
8108
8108
  return obj;
8109
- return path73.reduce((acc, key) => acc?.[key], obj);
8109
+ return path74.reduce((acc, key) => acc?.[key], obj);
8110
8110
  }
8111
8111
  function promiseAllObject(promisesObj) {
8112
8112
  const keys = Object.keys(promisesObj);
@@ -8355,11 +8355,11 @@ function aborted(x, startIndex = 0) {
8355
8355
  }
8356
8356
  return false;
8357
8357
  }
8358
- function prefixIssues(path73, issues) {
8358
+ function prefixIssues(path74, issues) {
8359
8359
  return issues.map((iss) => {
8360
8360
  var _a2;
8361
8361
  (_a2 = iss).path ?? (_a2.path = []);
8362
- iss.path.unshift(path73);
8362
+ iss.path.unshift(path74);
8363
8363
  return iss;
8364
8364
  });
8365
8365
  }
@@ -8548,7 +8548,7 @@ function treeifyError(error40, _mapper) {
8548
8548
  return issue2.message;
8549
8549
  };
8550
8550
  const result = { errors: [] };
8551
- const processError = (error41, path73 = []) => {
8551
+ const processError = (error41, path74 = []) => {
8552
8552
  var _a2, _b;
8553
8553
  for (const issue2 of error41.issues) {
8554
8554
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -8558,7 +8558,7 @@ function treeifyError(error40, _mapper) {
8558
8558
  } else if (issue2.code === "invalid_element") {
8559
8559
  processError({ issues: issue2.issues }, issue2.path);
8560
8560
  } else {
8561
- const fullpath = [...path73, ...issue2.path];
8561
+ const fullpath = [...path74, ...issue2.path];
8562
8562
  if (fullpath.length === 0) {
8563
8563
  result.errors.push(mapper(issue2));
8564
8564
  continue;
@@ -8588,9 +8588,9 @@ function treeifyError(error40, _mapper) {
8588
8588
  processError(error40);
8589
8589
  return result;
8590
8590
  }
8591
- function toDotPath(path73) {
8591
+ function toDotPath(path74) {
8592
8592
  const segs = [];
8593
- for (const seg of path73) {
8593
+ for (const seg of path74) {
8594
8594
  if (typeof seg === "number")
8595
8595
  segs.push(`[${seg}]`);
8596
8596
  else if (typeof seg === "symbol")
@@ -21596,7 +21596,7 @@ var require_ignore = __commonJS({
21596
21596
  // path matching.
21597
21597
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
21598
21598
  // @returns {TestResult} true if a file is ignored
21599
- test(path73, checkUnignored, mode) {
21599
+ test(path74, checkUnignored, mode) {
21600
21600
  let ignored = false;
21601
21601
  let unignored = false;
21602
21602
  let matchedRule;
@@ -21605,7 +21605,7 @@ var require_ignore = __commonJS({
21605
21605
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
21606
21606
  return;
21607
21607
  }
21608
- const matched = rule[mode].test(path73);
21608
+ const matched = rule[mode].test(path74);
21609
21609
  if (!matched) {
21610
21610
  return;
21611
21611
  }
@@ -21626,17 +21626,17 @@ var require_ignore = __commonJS({
21626
21626
  var throwError = (message, Ctor) => {
21627
21627
  throw new Ctor(message);
21628
21628
  };
21629
- var checkPath = (path73, originalPath, doThrow) => {
21630
- if (!isString(path73)) {
21629
+ var checkPath = (path74, originalPath, doThrow) => {
21630
+ if (!isString(path74)) {
21631
21631
  return doThrow(
21632
21632
  `path must be a string, but got \`${originalPath}\``,
21633
21633
  TypeError
21634
21634
  );
21635
21635
  }
21636
- if (!path73) {
21636
+ if (!path74) {
21637
21637
  return doThrow(`path must not be empty`, TypeError);
21638
21638
  }
21639
- if (checkPath.isNotRelative(path73)) {
21639
+ if (checkPath.isNotRelative(path74)) {
21640
21640
  const r = "`path.relative()`d";
21641
21641
  return doThrow(
21642
21642
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -21645,7 +21645,7 @@ var require_ignore = __commonJS({
21645
21645
  }
21646
21646
  return true;
21647
21647
  };
21648
- var isNotRelative = (path73) => REGEX_TEST_INVALID_PATH.test(path73);
21648
+ var isNotRelative = (path74) => REGEX_TEST_INVALID_PATH.test(path74);
21649
21649
  checkPath.isNotRelative = isNotRelative;
21650
21650
  checkPath.convert = (p) => p;
21651
21651
  var Ignore = class {
@@ -21675,19 +21675,19 @@ var require_ignore = __commonJS({
21675
21675
  }
21676
21676
  // @returns {TestResult}
21677
21677
  _test(originalPath, cache2, checkUnignored, slices) {
21678
- const path73 = originalPath && checkPath.convert(originalPath);
21678
+ const path74 = originalPath && checkPath.convert(originalPath);
21679
21679
  checkPath(
21680
- path73,
21680
+ path74,
21681
21681
  originalPath,
21682
21682
  this._strictPathCheck ? throwError : RETURN_FALSE
21683
21683
  );
21684
- return this._t(path73, cache2, checkUnignored, slices);
21684
+ return this._t(path74, cache2, checkUnignored, slices);
21685
21685
  }
21686
- checkIgnore(path73) {
21687
- if (!REGEX_TEST_TRAILING_SLASH.test(path73)) {
21688
- return this.test(path73);
21686
+ checkIgnore(path74) {
21687
+ if (!REGEX_TEST_TRAILING_SLASH.test(path74)) {
21688
+ return this.test(path74);
21689
21689
  }
21690
- const slices = path73.split(SLASH).filter(Boolean);
21690
+ const slices = path74.split(SLASH).filter(Boolean);
21691
21691
  slices.pop();
21692
21692
  if (slices.length) {
21693
21693
  const parent = this._t(
@@ -21700,18 +21700,18 @@ var require_ignore = __commonJS({
21700
21700
  return parent;
21701
21701
  }
21702
21702
  }
21703
- return this._rules.test(path73, false, MODE_CHECK_IGNORE);
21703
+ return this._rules.test(path74, false, MODE_CHECK_IGNORE);
21704
21704
  }
21705
- _t(path73, cache2, checkUnignored, slices) {
21706
- if (path73 in cache2) {
21707
- return cache2[path73];
21705
+ _t(path74, cache2, checkUnignored, slices) {
21706
+ if (path74 in cache2) {
21707
+ return cache2[path74];
21708
21708
  }
21709
21709
  if (!slices) {
21710
- slices = path73.split(SLASH).filter(Boolean);
21710
+ slices = path74.split(SLASH).filter(Boolean);
21711
21711
  }
21712
21712
  slices.pop();
21713
21713
  if (!slices.length) {
21714
- return cache2[path73] = this._rules.test(path73, checkUnignored, MODE_IGNORE);
21714
+ return cache2[path74] = this._rules.test(path74, checkUnignored, MODE_IGNORE);
21715
21715
  }
21716
21716
  const parent = this._t(
21717
21717
  slices.join(SLASH) + SLASH,
@@ -21719,29 +21719,29 @@ var require_ignore = __commonJS({
21719
21719
  checkUnignored,
21720
21720
  slices
21721
21721
  );
21722
- return cache2[path73] = parent.ignored ? parent : this._rules.test(path73, checkUnignored, MODE_IGNORE);
21722
+ return cache2[path74] = parent.ignored ? parent : this._rules.test(path74, checkUnignored, MODE_IGNORE);
21723
21723
  }
21724
- ignores(path73) {
21725
- return this._test(path73, this._ignoreCache, false).ignored;
21724
+ ignores(path74) {
21725
+ return this._test(path74, this._ignoreCache, false).ignored;
21726
21726
  }
21727
21727
  createFilter() {
21728
- return (path73) => !this.ignores(path73);
21728
+ return (path74) => !this.ignores(path74);
21729
21729
  }
21730
21730
  filter(paths) {
21731
21731
  return makeArray(paths).filter(this.createFilter());
21732
21732
  }
21733
21733
  // @returns {TestResult}
21734
- test(path73) {
21735
- return this._test(path73, this._testCache, true);
21734
+ test(path74) {
21735
+ return this._test(path74, this._testCache, true);
21736
21736
  }
21737
21737
  };
21738
21738
  var factory = (options) => new Ignore(options);
21739
- var isPathValid = (path73) => checkPath(path73 && checkPath.convert(path73), path73, RETURN_FALSE);
21739
+ var isPathValid = (path74) => checkPath(path74 && checkPath.convert(path74), path74, RETURN_FALSE);
21740
21740
  var setupWindows = () => {
21741
21741
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
21742
21742
  checkPath.convert = makePosix;
21743
21743
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
21744
- checkPath.isNotRelative = (path73) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path73) || isNotRelative(path73);
21744
+ checkPath.isNotRelative = (path74) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path74) || isNotRelative(path74);
21745
21745
  };
21746
21746
  if (
21747
21747
  // Detect `process` so that it can run in browsers.
@@ -22530,10 +22530,10 @@ var require_src2 = __commonJS({
22530
22530
  var fs_1 = __require("fs");
22531
22531
  var debug_1 = __importDefault(require_src());
22532
22532
  var log2 = debug_1.default("@kwsites/file-exists");
22533
- function check2(path73, isFile, isDirectory) {
22534
- log2(`checking %s`, path73);
22533
+ function check2(path74, isFile, isDirectory) {
22534
+ log2(`checking %s`, path74);
22535
22535
  try {
22536
- const stat2 = fs_1.statSync(path73);
22536
+ const stat2 = fs_1.statSync(path74);
22537
22537
  if (stat2.isFile() && isFile) {
22538
22538
  log2(`[OK] path represents a file`);
22539
22539
  return true;
@@ -22553,8 +22553,8 @@ var require_src2 = __commonJS({
22553
22553
  throw e;
22554
22554
  }
22555
22555
  }
22556
- function exists2(path73, type = exports.READABLE) {
22557
- return check2(path73, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
22556
+ function exists2(path74, type = exports.READABLE) {
22557
+ return check2(path74, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
22558
22558
  }
22559
22559
  exports.exists = exists2;
22560
22560
  exports.FILE = 1;
@@ -23584,6 +23584,81 @@ function isUserEndedSessionTurnErrorText(errorText) {
23584
23584
  // ../types/src/session-kind.ts
23585
23585
  var SESSION_KINDS = ["regular", "planning", "planned"];
23586
23586
  var SESSION_KIND_SET = new Set(SESSION_KINDS);
23587
+ function parseSessionKind(raw) {
23588
+ if (raw === "todo") return "planned";
23589
+ if (typeof raw === "string" && SESSION_KIND_SET.has(raw)) return raw;
23590
+ return "regular";
23591
+ }
23592
+
23593
+ // ../types/src/planning-todo.ts
23594
+ function planningTodoTitle(item) {
23595
+ return item.content.title;
23596
+ }
23597
+
23598
+ // ../types/src/parse-planning-todo-agent-json.ts
23599
+ function parsePlanningTodoAgentJson(raw) {
23600
+ if (raw == null || raw.trim() === "") return [];
23601
+ let text = raw.trim();
23602
+ const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
23603
+ if (fence?.[1]) text = fence[1].trim();
23604
+ let parsed;
23605
+ try {
23606
+ parsed = JSON.parse(text);
23607
+ } catch {
23608
+ const start = text.indexOf("[");
23609
+ const end = text.lastIndexOf("]");
23610
+ if (start < 0 || end <= start) return [];
23611
+ try {
23612
+ parsed = JSON.parse(text.slice(start, end + 1));
23613
+ } catch {
23614
+ return [];
23615
+ }
23616
+ }
23617
+ if (!Array.isArray(parsed)) return [];
23618
+ const rows = [];
23619
+ for (const item of parsed) {
23620
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
23621
+ const o = item;
23622
+ const title = typeof o.title === "string" ? o.title.trim() : "";
23623
+ const prompt = typeof o.prompt === "string" ? o.prompt.trim() : typeof o.promptText === "string" ? o.promptText.trim() : "";
23624
+ if (!title || !prompt) continue;
23625
+ rows.push({ title, prompt });
23626
+ }
23627
+ return rows;
23628
+ }
23629
+
23630
+ // ../types/src/build-planning-session-agent-prompt.ts
23631
+ var PLANNING_INSTRUCTIONS = [
23632
+ "PLANNING MODE: You must NOT make any changes to the codebase, filesystem, or environment.",
23633
+ "Do not run tools that modify files or execute commands that change state.",
23634
+ "Your only task is to analyze and produce a plan.",
23635
+ "",
23636
+ "Respond with ONLY a single JSON array \u2014 no markdown fences, no other text before or after.",
23637
+ "Each array element must be an object with exactly:",
23638
+ '- "title": a one-line summary of the task',
23639
+ '- "prompt": the full prompt to use when creating a new draft session from this todo',
23640
+ "",
23641
+ "Example:",
23642
+ '[{"title":"Add user login","prompt":"Implement a login form with email and password..."}]'
23643
+ ].join("\n");
23644
+ function buildPlanningSessionAgentPrompt(userPrompt) {
23645
+ return [PLANNING_INSTRUCTIONS, "", "User request:", userPrompt.trim()].join("\n");
23646
+ }
23647
+ function buildPlanningSessionFollowUpAgentPrompt(userPrompt, existingTodos) {
23648
+ const todoLines = existingTodos.length > 0 ? existingTodos.map((t) => {
23649
+ const status = t.plannedSessionId ? "DONE (draft session already created)" : "OPEN";
23650
+ return `- [${status}] ${planningTodoTitle(t)}`;
23651
+ }).join("\n") : "(none yet)";
23652
+ return [
23653
+ PLANNING_INSTRUCTIONS,
23654
+ "",
23655
+ "Existing todo items from this planning session (consider these when responding; do not duplicate completed items):",
23656
+ todoLines,
23657
+ "",
23658
+ "User follow-up:",
23659
+ userPrompt.trim()
23660
+ ].join("\n");
23661
+ }
23587
23662
 
23588
23663
  // ../types/src/change-summary-path.ts
23589
23664
  function normalizeRepoRelativePath(p) {
@@ -23640,9 +23715,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
23640
23715
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
23641
23716
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
23642
23717
  if (!rawPath || !summary) continue;
23643
- const path73 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
23644
- if (!path73) continue;
23645
- rows.push({ path: path73, summary: clampSummaryToAtMostTwoLines(summary) });
23718
+ const path74 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
23719
+ if (!path74) continue;
23720
+ rows.push({ path: path74, summary: clampSummaryToAtMostTwoLines(summary) });
23646
23721
  }
23647
23722
  return rows;
23648
23723
  }
@@ -23967,6 +24042,19 @@ function buildCliAutoApprovedPermissionRpcResult(requestParams) {
23967
24042
  };
23968
24043
  }
23969
24044
 
24045
+ // ../types/src/acp-agent-text-stream-bucket.ts
24046
+ function resolveAgentTextStreamBucket(params) {
24047
+ const k = params.transcriptRowKind;
24048
+ const su = params.sessionUpdate;
24049
+ if (k === "agent_thought_chunk" || su === "agent_thought_chunk" || k === "thinking" || su === "thinking") {
24050
+ return "thought";
24051
+ }
24052
+ if (k === "agent_message_chunk" || su === "agent_message_chunk" || k === "update" || su === "update") {
24053
+ return "message";
24054
+ }
24055
+ return null;
24056
+ }
24057
+
23970
24058
  // ../types/src/agent-config.ts
23971
24059
  var AGENT_CONFIG_CLAUDE_PERMISSION_MODE_KEY = "claude_permission_mode";
23972
24060
  var AGENT_CONFIG_CLI_PERMISSION_MODE_KEY = "cli_permission_mode";
@@ -24956,8 +25044,8 @@ function randomSecret() {
24956
25044
  }
24957
25045
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
24958
25046
  }
24959
- async function requestPreviewApi(port, secret, method, path73, body) {
24960
- const url2 = `http://127.0.0.1:${port}${path73}`;
25047
+ async function requestPreviewApi(port, secret, method, path74, body) {
25048
+ const url2 = `http://127.0.0.1:${port}${path74}`;
24961
25049
  const headers = {
24962
25050
  [PREVIEW_SECRET_HEADER]: secret,
24963
25051
  "Content-Type": "application/json"
@@ -24969,7 +25057,7 @@ async function requestPreviewApi(port, secret, method, path73, body) {
24969
25057
  });
24970
25058
  const data = await res.json().catch(() => ({}));
24971
25059
  if (!res.ok) {
24972
- throw new Error(data?.error ?? `Preview API ${method} ${path73}: ${res.status}`);
25060
+ throw new Error(data?.error ?? `Preview API ${method} ${path74}: ${res.status}`);
24973
25061
  }
24974
25062
  return data;
24975
25063
  }
@@ -25184,7 +25272,7 @@ function installBridgeProcessResilience() {
25184
25272
  }
25185
25273
 
25186
25274
  // src/cli-version.ts
25187
- var CLI_VERSION = "0.1.57".length > 0 ? "0.1.57" : "0.0.0-dev";
25275
+ var CLI_VERSION = "0.1.59".length > 0 ? "0.1.59" : "0.0.0-dev";
25188
25276
 
25189
25277
  // src/connection/heartbeat/constants.ts
25190
25278
  var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -26719,7 +26807,7 @@ function readMigrationSql(filename, ...searchSubdirs) {
26719
26807
  join(moduleDir, "..", sub, filename),
26720
26808
  join(moduleDir, "..", "..", "dist", sub, filename)
26721
26809
  ]);
26722
- const resolved = candidates.find((path73) => existsSync(path73));
26810
+ const resolved = candidates.find((path74) => existsSync(path74));
26723
26811
  if (!resolved) {
26724
26812
  throw new Error(`Missing SQLite migration SQL: ${filename} (searched ${searchSubdirs.join(", ")})`);
26725
26813
  }
@@ -31057,176 +31145,6 @@ function createBridgeOnRequest(opts) {
31057
31145
  };
31058
31146
  }
31059
31147
 
31060
- // src/agents/acp/hooks/extract-acp-file-diffs-from-update/paths-and-text.ts
31061
- import { fileURLToPath as fileURLToPath5 } from "node:url";
31062
- function readOptionalTextField(v) {
31063
- if (v === null || v === void 0) return "";
31064
- if (typeof v === "string") return v;
31065
- return "";
31066
- }
31067
- function normalizePathField(raw) {
31068
- const t = raw.trim();
31069
- if (t.startsWith("file://")) {
31070
- try {
31071
- return fileURLToPath5(t);
31072
- } catch {
31073
- return t;
31074
- }
31075
- }
31076
- return t;
31077
- }
31078
- function extractDiffPath(o) {
31079
- for (const k of ["path", "file", "filePath", "filepath", "target_path", "targetPath"]) {
31080
- const v = o[k];
31081
- if (typeof v === "string" && v.trim()) return normalizePathField(v);
31082
- }
31083
- return null;
31084
- }
31085
-
31086
- // src/agents/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
31087
- function pushDiffIfComplete(o, sessionParentPath, out) {
31088
- const t = o.type;
31089
- if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
31090
- const rawPath = extractDiffPath(o);
31091
- if (!rawPath) return;
31092
- const resolved = resolveWorkspaceFilePath(sessionParentPath, rawPath);
31093
- if (!resolved) return;
31094
- const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
31095
- const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
31096
- out.push({ path: resolved.display, oldText, newText });
31097
- }
31098
-
31099
- // src/agents/acp/hooks/extract-acp-file-diffs-from-update/walk-update-for-diffs.ts
31100
- var NEST_KEYS = [
31101
- "content",
31102
- "contents",
31103
- "result",
31104
- "output",
31105
- "delta",
31106
- "toolResult",
31107
- "tool_result",
31108
- "data",
31109
- "arguments"
31110
- ];
31111
- function walkValue(value, sessionParentPath, depth, out) {
31112
- if (depth > 12 || value == null) return;
31113
- if (Array.isArray(value)) {
31114
- for (const x of value) walkValue(x, sessionParentPath, depth + 1, out);
31115
- return;
31116
- }
31117
- if (typeof value !== "object") return;
31118
- const o = value;
31119
- pushDiffIfComplete(o, sessionParentPath, out);
31120
- if (o.type === "content" && o.content != null && typeof o.content === "object") {
31121
- walkValue(o.content, sessionParentPath, depth + 1, out);
31122
- }
31123
- for (const k of NEST_KEYS) {
31124
- if (o.type === "content" && k === "content") continue;
31125
- const v = o[k];
31126
- if (v == null) continue;
31127
- if (k === "arguments" && typeof v === "string") {
31128
- try {
31129
- walkValue(JSON.parse(v), sessionParentPath, depth + 1, out);
31130
- } catch {
31131
- }
31132
- continue;
31133
- }
31134
- walkValue(v, sessionParentPath, depth + 1, out);
31135
- }
31136
- }
31137
-
31138
- // src/agents/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
31139
- function extractAcpFileDiffsFromUpdate(update, sessionParentPath) {
31140
- if (!update || typeof update !== "object") return [];
31141
- const u = update;
31142
- const out = [];
31143
- const content = u.content;
31144
- if (Array.isArray(content)) {
31145
- for (const x of content) walkValue(x, sessionParentPath, 0, out);
31146
- }
31147
- const byPath = /* @__PURE__ */ new Map();
31148
- for (const d of out) byPath.set(d.path, d);
31149
- return [...byPath.values()];
31150
- }
31151
-
31152
- // src/agents/acp/hooks/extract-tool-target-paths.ts
31153
- function addPath(sessionParentPath, raw, out) {
31154
- if (typeof raw !== "string") return;
31155
- const trimmed2 = raw.trim();
31156
- if (!trimmed2) return;
31157
- const resolved = resolveWorkspaceFilePath(sessionParentPath, trimmed2);
31158
- if (!resolved) return;
31159
- out.add(resolved.display);
31160
- }
31161
- function walkLocations(sessionParentPath, loc, out) {
31162
- if (!Array.isArray(loc)) return;
31163
- for (const item of loc) {
31164
- if (item && typeof item === "object") addPath(sessionParentPath, item.path, out);
31165
- }
31166
- }
31167
- var PATH_KEYS = [
31168
- "path",
31169
- "file",
31170
- "filePath",
31171
- "filepath",
31172
- "target_path",
31173
- "targetPath",
31174
- "file_path",
31175
- "target_file"
31176
- ];
31177
- function collectFromObject(sessionParentPath, obj, out, depth) {
31178
- if (depth > 10) return;
31179
- for (const k of PATH_KEYS) {
31180
- if (k in obj) addPath(sessionParentPath, obj[k], out);
31181
- }
31182
- for (const v of Object.values(obj)) {
31183
- if (v != null && typeof v === "object") collectUnknown(sessionParentPath, v, out, depth + 1);
31184
- }
31185
- }
31186
- function collectUnknown(sessionParentPath, v, out, depth) {
31187
- if (depth > 10 || v == null) return;
31188
- if (Array.isArray(v)) {
31189
- for (const x of v) collectUnknown(sessionParentPath, x, out, depth + 1);
31190
- return;
31191
- }
31192
- if (typeof v === "object") collectFromObject(sessionParentPath, v, out, depth);
31193
- }
31194
- function walkContentArray(sessionParentPath, content, out) {
31195
- if (!Array.isArray(content)) return;
31196
- for (const item of content) {
31197
- if (!item || typeof item !== "object") continue;
31198
- const o = item;
31199
- if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
31200
- for (const k of PATH_KEYS) if (k in o) addPath(sessionParentPath, o[k], out);
31201
- }
31202
- if (o.type === "content" && o.content != null && typeof o.content === "object") {
31203
- const inner = o.content;
31204
- if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
31205
- for (const k of PATH_KEYS) if (k in inner) addPath(sessionParentPath, inner[k], out);
31206
- }
31207
- }
31208
- }
31209
- }
31210
- function extractToolTargetDisplayPaths(update, sessionParentPath) {
31211
- const out = /* @__PURE__ */ new Set();
31212
- if (!update || typeof update !== "object") return [];
31213
- const u = update;
31214
- walkLocations(sessionParentPath, u.locations, out);
31215
- walkLocations(sessionParentPath, u.fileLocations, out);
31216
- walkLocations(sessionParentPath, u.file_locations, out);
31217
- const tc = u.toolCall ?? u.tool_call;
31218
- if (tc && typeof tc.arguments === "string") {
31219
- try {
31220
- const parsed = JSON.parse(tc.arguments);
31221
- collectUnknown(sessionParentPath, parsed, out, 0);
31222
- } catch {
31223
- }
31224
- }
31225
- walkContentArray(sessionParentPath, u.content, out);
31226
- collectFromObject(sessionParentPath, u, out, 0);
31227
- return [...out];
31228
- }
31229
-
31230
31148
  // src/agents/acp/hooks/bridge-on-session-update/constants.ts
31231
31149
  var PATH_SNAPSHOT_DEBOUNCE_MS = 500;
31232
31150
 
@@ -31374,56 +31292,6 @@ var PathSnapshotTracker = class {
31374
31292
  }
31375
31293
  };
31376
31294
 
31377
- // src/agents/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
31378
- function sendExtractedDiffsAsSessionFileChanges(diffs, send, sessionParentPath, sessionId, runId, sentPaths, log2) {
31379
- for (const d of diffs) {
31380
- try {
31381
- const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
31382
- const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, d.path);
31383
- send({
31384
- type: "session_file_change",
31385
- sessionId,
31386
- runId,
31387
- path: d.path,
31388
- oldText: d.oldText,
31389
- newText: d.newText,
31390
- patchContent,
31391
- isDirectory: dirFlags.isDirectory,
31392
- directoryRemoved: dirFlags.directoryRemoved
31393
- });
31394
- sentPaths.add(d.path);
31395
- } catch (err) {
31396
- log2(`[Bridge service] Session file change failed for ${d.path}: ${errorMessage(err)}`);
31397
- }
31398
- }
31399
- }
31400
- function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, sessionParentPath, sessionId, runId, log2) {
31401
- for (const displayPath of mergedPaths) {
31402
- if (sentPaths.has(displayPath)) continue;
31403
- const oldText = readGitHeadBlob(sessionParentPath, displayPath);
31404
- const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
31405
- if (oldText === newText) continue;
31406
- try {
31407
- const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
31408
- const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
31409
- send({
31410
- type: "session_file_change",
31411
- sessionId,
31412
- runId,
31413
- path: displayPath,
31414
- oldText,
31415
- newText,
31416
- patchContent,
31417
- isDirectory: dirFlags.isDirectory,
31418
- directoryRemoved: dirFlags.directoryRemoved
31419
- });
31420
- sentPaths.add(displayPath);
31421
- } catch (err) {
31422
- log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
31423
- }
31424
- }
31425
- }
31426
-
31427
31295
  // src/agents/acp/hooks/bridge-on-session-update/send-session-info-title-update.ts
31428
31296
  function extractSessionInfoTitle(params) {
31429
31297
  if (!params || typeof params !== "object") return null;
@@ -31446,6 +31314,391 @@ function sendSessionInfoTitleUpdate(params) {
31446
31314
  }
31447
31315
  }
31448
31316
 
31317
+ // src/agents/acp/hooks/bridge-on-session-update/parse-bridge-session-update.ts
31318
+ function parseBridgeSessionUpdate(params) {
31319
+ const p = params;
31320
+ const updateKind = p.sessionUpdate ?? p.session_update ?? p.type ?? "update";
31321
+ const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
31322
+ const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
31323
+ const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
31324
+ return { updateKind, toolName, isToolUpdate, isCompletedToolCallUpdate };
31325
+ }
31326
+
31327
+ // src/agents/acp/hooks/extract-acp-file-diffs-from-update/paths-and-text.ts
31328
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
31329
+ function readOptionalTextField(v) {
31330
+ if (v === null || v === void 0) return "";
31331
+ if (typeof v === "string") return v;
31332
+ return "";
31333
+ }
31334
+ function normalizePathField(raw) {
31335
+ const t = raw.trim();
31336
+ if (t.startsWith("file://")) {
31337
+ try {
31338
+ return fileURLToPath5(t);
31339
+ } catch {
31340
+ return t;
31341
+ }
31342
+ }
31343
+ return t;
31344
+ }
31345
+ function extractDiffPath(o) {
31346
+ for (const k of ["path", "file", "filePath", "filepath", "target_path", "targetPath"]) {
31347
+ const v = o[k];
31348
+ if (typeof v === "string" && v.trim()) return normalizePathField(v);
31349
+ }
31350
+ return null;
31351
+ }
31352
+
31353
+ // src/agents/acp/hooks/extract-acp-file-diffs-from-update/push-diff.ts
31354
+ function pushDiffIfComplete(o, sessionParentPath, out) {
31355
+ const t = o.type;
31356
+ if (typeof t !== "string" || t.toLowerCase() !== "diff") return;
31357
+ const rawPath = extractDiffPath(o);
31358
+ if (!rawPath) return;
31359
+ const resolved = resolveWorkspaceFilePath(sessionParentPath, rawPath);
31360
+ if (!resolved) return;
31361
+ const oldText = readOptionalTextField(o.oldText ?? o.old_text ?? o.before ?? o.oldContent);
31362
+ const newText = readOptionalTextField(o.newText ?? o.new_text ?? o.after ?? o.newContent);
31363
+ out.push({ path: resolved.display, oldText, newText });
31364
+ }
31365
+
31366
+ // src/agents/acp/hooks/extract-acp-file-diffs-from-update/walk-update-for-diffs.ts
31367
+ var NEST_KEYS = [
31368
+ "content",
31369
+ "contents",
31370
+ "result",
31371
+ "output",
31372
+ "delta",
31373
+ "toolResult",
31374
+ "tool_result",
31375
+ "data",
31376
+ "arguments"
31377
+ ];
31378
+ function walkValue(value, sessionParentPath, depth, out) {
31379
+ if (depth > 12 || value == null) return;
31380
+ if (Array.isArray(value)) {
31381
+ for (const x of value) walkValue(x, sessionParentPath, depth + 1, out);
31382
+ return;
31383
+ }
31384
+ if (typeof value !== "object") return;
31385
+ const o = value;
31386
+ pushDiffIfComplete(o, sessionParentPath, out);
31387
+ if (o.type === "content" && o.content != null && typeof o.content === "object") {
31388
+ walkValue(o.content, sessionParentPath, depth + 1, out);
31389
+ }
31390
+ for (const k of NEST_KEYS) {
31391
+ if (o.type === "content" && k === "content") continue;
31392
+ const v = o[k];
31393
+ if (v == null) continue;
31394
+ if (k === "arguments" && typeof v === "string") {
31395
+ try {
31396
+ walkValue(JSON.parse(v), sessionParentPath, depth + 1, out);
31397
+ } catch {
31398
+ }
31399
+ continue;
31400
+ }
31401
+ walkValue(v, sessionParentPath, depth + 1, out);
31402
+ }
31403
+ }
31404
+
31405
+ // src/agents/acp/hooks/extract-acp-file-diffs-from-update/extract.ts
31406
+ function extractAcpFileDiffsFromUpdate(update, sessionParentPath) {
31407
+ if (!update || typeof update !== "object") return [];
31408
+ const u = update;
31409
+ const out = [];
31410
+ const content = u.content;
31411
+ if (Array.isArray(content)) {
31412
+ for (const x of content) walkValue(x, sessionParentPath, 0, out);
31413
+ }
31414
+ const byPath = /* @__PURE__ */ new Map();
31415
+ for (const d of out) byPath.set(d.path, d);
31416
+ return [...byPath.values()];
31417
+ }
31418
+
31419
+ // src/agents/acp/hooks/extract-tool-target-paths.ts
31420
+ function addPath(sessionParentPath, raw, out) {
31421
+ if (typeof raw !== "string") return;
31422
+ const trimmed2 = raw.trim();
31423
+ if (!trimmed2) return;
31424
+ const resolved = resolveWorkspaceFilePath(sessionParentPath, trimmed2);
31425
+ if (!resolved) return;
31426
+ out.add(resolved.display);
31427
+ }
31428
+ function walkLocations(sessionParentPath, loc, out) {
31429
+ if (!Array.isArray(loc)) return;
31430
+ for (const item of loc) {
31431
+ if (item && typeof item === "object") addPath(sessionParentPath, item.path, out);
31432
+ }
31433
+ }
31434
+ var PATH_KEYS = [
31435
+ "path",
31436
+ "file",
31437
+ "filePath",
31438
+ "filepath",
31439
+ "target_path",
31440
+ "targetPath",
31441
+ "file_path",
31442
+ "target_file"
31443
+ ];
31444
+ function collectFromObject(sessionParentPath, obj, out, depth) {
31445
+ if (depth > 10) return;
31446
+ for (const k of PATH_KEYS) {
31447
+ if (k in obj) addPath(sessionParentPath, obj[k], out);
31448
+ }
31449
+ for (const v of Object.values(obj)) {
31450
+ if (v != null && typeof v === "object") collectUnknown(sessionParentPath, v, out, depth + 1);
31451
+ }
31452
+ }
31453
+ function collectUnknown(sessionParentPath, v, out, depth) {
31454
+ if (depth > 10 || v == null) return;
31455
+ if (Array.isArray(v)) {
31456
+ for (const x of v) collectUnknown(sessionParentPath, x, out, depth + 1);
31457
+ return;
31458
+ }
31459
+ if (typeof v === "object") collectFromObject(sessionParentPath, v, out, depth);
31460
+ }
31461
+ function walkContentArray(sessionParentPath, content, out) {
31462
+ if (!Array.isArray(content)) return;
31463
+ for (const item of content) {
31464
+ if (!item || typeof item !== "object") continue;
31465
+ const o = item;
31466
+ if (typeof o.type === "string" && o.type.toLowerCase() === "diff") {
31467
+ for (const k of PATH_KEYS) if (k in o) addPath(sessionParentPath, o[k], out);
31468
+ }
31469
+ if (o.type === "content" && o.content != null && typeof o.content === "object") {
31470
+ const inner = o.content;
31471
+ if (typeof inner.type === "string" && inner.type.toLowerCase() === "diff") {
31472
+ for (const k of PATH_KEYS) if (k in inner) addPath(sessionParentPath, inner[k], out);
31473
+ }
31474
+ }
31475
+ }
31476
+ }
31477
+ function extractToolTargetDisplayPaths(update, sessionParentPath) {
31478
+ const out = /* @__PURE__ */ new Set();
31479
+ if (!update || typeof update !== "object") return [];
31480
+ const u = update;
31481
+ walkLocations(sessionParentPath, u.locations, out);
31482
+ walkLocations(sessionParentPath, u.fileLocations, out);
31483
+ walkLocations(sessionParentPath, u.file_locations, out);
31484
+ const tc = u.toolCall ?? u.tool_call;
31485
+ if (tc && typeof tc.arguments === "string") {
31486
+ try {
31487
+ const parsed = JSON.parse(tc.arguments);
31488
+ collectUnknown(sessionParentPath, parsed, out, 0);
31489
+ } catch {
31490
+ }
31491
+ }
31492
+ walkContentArray(sessionParentPath, u.content, out);
31493
+ collectFromObject(sessionParentPath, u, out, 0);
31494
+ return [...out];
31495
+ }
31496
+
31497
+ // src/agents/acp/hooks/bridge-on-session-update/send-structured-file-changes.ts
31498
+ function sendExtractedDiffsAsSessionFileChanges(diffs, send, sessionParentPath, sessionId, runId, sentPaths, log2) {
31499
+ for (const d of diffs) {
31500
+ try {
31501
+ const patchContent = editSnippetToUnifiedDiff(d.path, d.oldText, d.newText);
31502
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, d.path);
31503
+ send({
31504
+ type: "session_file_change",
31505
+ sessionId,
31506
+ runId,
31507
+ path: d.path,
31508
+ oldText: d.oldText,
31509
+ newText: d.newText,
31510
+ patchContent,
31511
+ isDirectory: dirFlags.isDirectory,
31512
+ directoryRemoved: dirFlags.directoryRemoved
31513
+ });
31514
+ sentPaths.add(d.path);
31515
+ } catch (err) {
31516
+ log2(`[Bridge service] Session file change failed for ${d.path}: ${errorMessage(err)}`);
31517
+ }
31518
+ }
31519
+ }
31520
+ function sendGitHeadVsWorkspaceForToolPaths(mergedPaths, sentPaths, send, sessionParentPath, sessionId, runId, log2) {
31521
+ for (const displayPath of mergedPaths) {
31522
+ if (sentPaths.has(displayPath)) continue;
31523
+ const oldText = readGitHeadBlob(sessionParentPath, displayPath);
31524
+ const newText = readUtf8WorkspaceFile(sessionParentPath, displayPath);
31525
+ if (oldText === newText) continue;
31526
+ try {
31527
+ const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, newText);
31528
+ const dirFlags = getSessionFileChangeDirectoryFlags(sessionParentPath, displayPath);
31529
+ send({
31530
+ type: "session_file_change",
31531
+ sessionId,
31532
+ runId,
31533
+ path: displayPath,
31534
+ oldText,
31535
+ newText,
31536
+ patchContent,
31537
+ isDirectory: dirFlags.isDirectory,
31538
+ directoryRemoved: dirFlags.directoryRemoved
31539
+ });
31540
+ sentPaths.add(displayPath);
31541
+ } catch (err) {
31542
+ log2(`[Bridge service] Session file change failed for ${displayPath}: ${errorMessage(err)}`);
31543
+ }
31544
+ }
31545
+ }
31546
+
31547
+ // src/agents/acp/hooks/bridge-on-session-update/handle-bridge-tool-session-update.ts
31548
+ function handleBridgeToolSessionUpdate(params) {
31549
+ const {
31550
+ params: updateParams,
31551
+ updateKind,
31552
+ isToolUpdate,
31553
+ isCompletedToolCallUpdate,
31554
+ toolKey,
31555
+ toolPaths,
31556
+ status,
31557
+ sessionParentPath,
31558
+ pathTracker,
31559
+ sentFileChangePaths,
31560
+ send,
31561
+ runId,
31562
+ sessionId,
31563
+ log: log2
31564
+ } = params;
31565
+ if (updateKind === "tool_call") {
31566
+ pathTracker.resetToolSnapshots(toolKey);
31567
+ }
31568
+ if (isToolUpdate) {
31569
+ accumulateToolPaths(toolKey, toolPaths, pathTracker.accumulatedPathsByToolKey);
31570
+ }
31571
+ if (isToolUpdate) {
31572
+ const deliver = send && runId && sessionId ? (msg) => send(msg) : null;
31573
+ pathTracker.handleToolCallLifecycle(
31574
+ updateKind,
31575
+ toolKey,
31576
+ toolPaths,
31577
+ status,
31578
+ sessionParentPath,
31579
+ sentFileChangePaths,
31580
+ deliver,
31581
+ runId,
31582
+ sessionId,
31583
+ log2
31584
+ );
31585
+ }
31586
+ const diffs = extractAcpFileDiffsFromUpdate(updateParams, sessionParentPath);
31587
+ if (diffs.length > 0 && send && runId && sessionId) {
31588
+ sendExtractedDiffsAsSessionFileChanges(
31589
+ diffs,
31590
+ send,
31591
+ sessionParentPath,
31592
+ sessionId,
31593
+ runId,
31594
+ sentFileChangePaths,
31595
+ log2
31596
+ );
31597
+ } else if (diffs.length > 0) {
31598
+ log2(
31599
+ `[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
31600
+ );
31601
+ }
31602
+ if (isCompletedToolCallUpdate && send && runId && sessionId) {
31603
+ const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
31604
+ const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
31605
+ sendGitHeadVsWorkspaceForToolPaths(
31606
+ merged,
31607
+ sentFileChangePaths,
31608
+ send,
31609
+ sessionParentPath,
31610
+ sessionId,
31611
+ runId,
31612
+ log2
31613
+ );
31614
+ pathTracker.accumulatedPathsByToolKey.delete(toolKey);
31615
+ }
31616
+ }
31617
+ function resolveBridgeToolPaths(params, isToolUpdate, sessionParentPath) {
31618
+ return isToolUpdate ? extractToolTargetDisplayPaths(params, sessionParentPath) : [];
31619
+ }
31620
+
31621
+ // src/agents/planning/extract-session-update-message-text.ts
31622
+ function extractSessionUpdateMessageText(params) {
31623
+ if (params == null || typeof params !== "object" || Array.isArray(params)) return null;
31624
+ const o = params;
31625
+ if (typeof o.text === "string" && o.text.length > 0) return o.text;
31626
+ const content = o.content;
31627
+ if (typeof content === "string" && content.length > 0) return content;
31628
+ if (content != null && typeof content === "object" && !Array.isArray(content)) {
31629
+ const c = content;
31630
+ if (typeof c.text === "string" && c.text.length > 0) return c.text;
31631
+ }
31632
+ const update = o.update;
31633
+ if (typeof update === "string" && update.length > 0) return update;
31634
+ if (update != null && typeof update === "object" && !Array.isArray(update)) {
31635
+ const u = update;
31636
+ const innerContent = u.content;
31637
+ if (typeof innerContent === "string" && innerContent.length > 0) return innerContent;
31638
+ if (innerContent != null && typeof innerContent === "object" && !Array.isArray(innerContent)) {
31639
+ const ic = innerContent;
31640
+ if (typeof ic.text === "string" && ic.text.length > 0) return ic.text;
31641
+ }
31642
+ }
31643
+ return null;
31644
+ }
31645
+
31646
+ // src/agents/planning/planning-session-turn-buffer.ts
31647
+ var buffersByRunId = /* @__PURE__ */ new Map();
31648
+ function registerPlanningSessionTurn(runId) {
31649
+ buffersByRunId.set(runId, { messageBuffer: "" });
31650
+ }
31651
+ function clearPlanningSessionTurn(runId) {
31652
+ buffersByRunId.delete(runId);
31653
+ }
31654
+ function capturePlanningSessionUpdate(params) {
31655
+ const state = buffersByRunId.get(params.runId);
31656
+ if (!state) return false;
31657
+ const sessionUpdate = params.payload != null && typeof params.payload === "object" && !Array.isArray(params.payload) ? params.payload.sessionUpdate ?? params.payload.session_update : void 0;
31658
+ const bucket = resolveAgentTextStreamBucket({
31659
+ transcriptRowKind: params.updateKind,
31660
+ sessionUpdate: typeof sessionUpdate === "string" ? sessionUpdate : void 0
31661
+ });
31662
+ if (bucket !== "message") return false;
31663
+ const text = extractSessionUpdateMessageText(params.payload);
31664
+ if (text) state.messageBuffer += text;
31665
+ return true;
31666
+ }
31667
+ function getPlanningSessionTurnOutput(runId) {
31668
+ return buffersByRunId.get(runId)?.messageBuffer ?? "";
31669
+ }
31670
+ function consumePlanningSessionTurnOutput(runId) {
31671
+ const state = buffersByRunId.get(runId);
31672
+ buffersByRunId.delete(runId);
31673
+ return state?.messageBuffer ?? "";
31674
+ }
31675
+
31676
+ // src/agents/acp/hooks/bridge-on-session-update/forward-bridge-session-update.ts
31677
+ function forwardBridgeSessionUpdate(params) {
31678
+ const { runId, sessionId, updateKind, payload, send, nextTranscriptStreamSeq, log: log2 } = params;
31679
+ if (updateKind === "permission") {
31680
+ return;
31681
+ }
31682
+ if (capturePlanningSessionUpdate({ runId, updateKind, payload })) {
31683
+ return;
31684
+ }
31685
+ try {
31686
+ const transcriptStreamSeq = nextTranscriptStreamSeq?.(runId);
31687
+ send({
31688
+ type: "session_update",
31689
+ ...sessionId ? { sessionId } : {},
31690
+ runId,
31691
+ kind: updateKind,
31692
+ payload,
31693
+ ...transcriptStreamSeq != null ? { transcriptStreamSeq } : {}
31694
+ });
31695
+ } catch (err) {
31696
+ log2(
31697
+ `[Bridge service] Session update send failed (${formatSessionUpdateKindForLog(updateKind)}): ${errorMessage(err)}`
31698
+ );
31699
+ }
31700
+ }
31701
+
31449
31702
  // src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
31450
31703
  function createBridgeOnSessionUpdate(opts) {
31451
31704
  const { resolveRouting, getSendSessionUpdate, log: log2, sessionParentPath, nextTranscriptStreamSeq } = opts;
@@ -31457,8 +31710,7 @@ function createBridgeOnSessionUpdate(opts) {
31457
31710
  pathTracker.onRunIdChanged(runId);
31458
31711
  const send = getSendSessionUpdate();
31459
31712
  const sentFileChangePaths = /* @__PURE__ */ new Set();
31460
- const p = params;
31461
- const updateKind = p.sessionUpdate ?? p.session_update ?? p.type ?? "update";
31713
+ const { updateKind, isToolUpdate, isCompletedToolCallUpdate } = parseBridgeSessionUpdate(params);
31462
31714
  if (updateKind === "config_option_update") {
31463
31715
  return;
31464
31716
  }
@@ -31466,81 +31718,35 @@ function createBridgeOnSessionUpdate(opts) {
31466
31718
  sendSessionInfoTitleUpdate({ payload: params, runId, sessionId, send, log: log2 });
31467
31719
  return;
31468
31720
  }
31469
- const isCompletedToolCallUpdate = updateKind === "tool_call_update" && isCompletedToolStatus(p.status);
31470
- const toolName = p.toolCall?.name ?? p.tool_call?.name ?? "";
31471
- const isToolUpdate = updateKind === "tool_call" || updateKind === "tool_call_update" || typeof toolName === "string" && toolName.length > 0;
31472
- const toolPaths = isToolUpdate ? extractToolTargetDisplayPaths(params, sessionParentPath) : [];
31721
+ const toolPaths = resolveBridgeToolPaths(params, isToolUpdate, sessionParentPath);
31473
31722
  const toolKey = isToolUpdate ? pathTracker.resolveToolKey(params, updateKind) : "";
31474
- if (updateKind === "tool_call") {
31475
- pathTracker.resetToolSnapshots(toolKey);
31476
- }
31477
- if (isToolUpdate) {
31478
- accumulateToolPaths(toolKey, toolPaths, pathTracker.accumulatedPathsByToolKey);
31479
- }
31480
- if (isToolUpdate) {
31481
- const deliver = send && runId && sessionId ? (msg) => send(msg) : null;
31482
- pathTracker.handleToolCallLifecycle(
31483
- updateKind,
31484
- toolKey,
31485
- toolPaths,
31486
- p.status,
31487
- sessionParentPath,
31488
- sentFileChangePaths,
31489
- deliver,
31490
- runId ?? "",
31491
- sessionId ?? "",
31492
- log2
31493
- );
31494
- }
31495
- const diffs = extractAcpFileDiffsFromUpdate(params, sessionParentPath);
31496
- if (diffs.length > 0 && send && runId && sessionId) {
31497
- sendExtractedDiffsAsSessionFileChanges(
31498
- diffs,
31499
- send,
31500
- sessionParentPath,
31501
- sessionId,
31723
+ const status = params.status;
31724
+ handleBridgeToolSessionUpdate({
31725
+ params,
31726
+ updateKind,
31727
+ isToolUpdate,
31728
+ isCompletedToolCallUpdate,
31729
+ toolKey,
31730
+ toolPaths,
31731
+ status,
31732
+ sessionParentPath,
31733
+ pathTracker,
31734
+ sentFileChangePaths,
31735
+ send,
31736
+ runId: runId ?? "",
31737
+ sessionId: sessionId ?? "",
31738
+ log: log2
31739
+ });
31740
+ if (runId && send) {
31741
+ forwardBridgeSessionUpdate({
31502
31742
  runId,
31503
- sentFileChangePaths,
31504
- log2
31505
- );
31506
- } else if (diffs.length > 0) {
31507
- log2(
31508
- `[Bridge service] Agent file diff(s) not forwarded (${diffs.length}): session or run not wired to the bridge.`
31509
- );
31510
- }
31511
- if (isCompletedToolCallUpdate && send && runId && sessionId) {
31512
- const acc = pathTracker.accumulatedPathsByToolKey.get(toolKey);
31513
- const merged = [.../* @__PURE__ */ new Set([...acc ? [...acc] : [], ...toolPaths])];
31514
- sendGitHeadVsWorkspaceForToolPaths(
31515
- merged,
31516
- sentFileChangePaths,
31517
- send,
31518
- sessionParentPath,
31519
31743
  sessionId,
31520
- runId,
31521
- log2
31522
- );
31523
- pathTracker.accumulatedPathsByToolKey.delete(toolKey);
31524
- }
31525
- if (runId && send) {
31526
- if (updateKind === "permission") {
31527
- return;
31528
- }
31529
- try {
31530
- const transcriptStreamSeq = nextTranscriptStreamSeq?.(runId);
31531
- send({
31532
- type: "session_update",
31533
- ...sessionId ? { sessionId } : {},
31534
- runId,
31535
- kind: updateKind,
31536
- payload: params,
31537
- ...transcriptStreamSeq != null ? { transcriptStreamSeq } : {}
31538
- });
31539
- } catch (err) {
31540
- log2(
31541
- `[Bridge service] Session update send failed (${formatSessionUpdateKindForLog(updateKind)}): ${errorMessage(err)}`
31542
- );
31543
- }
31744
+ updateKind,
31745
+ payload: params,
31746
+ send,
31747
+ nextTranscriptStreamSeq,
31748
+ log: log2
31749
+ });
31544
31750
  }
31545
31751
  };
31546
31752
  }
@@ -31880,6 +32086,40 @@ async function disconnectAll(ctx) {
31880
32086
  ctx.pendingCancelRunIds.clear();
31881
32087
  }
31882
32088
 
32089
+ // src/agents/acp/manager/resolve-prompt-run-context.ts
32090
+ function resolvePromptRunContext(ctx, opts) {
32091
+ const { runId, mode, agentType, agentConfig, sessionId } = opts;
32092
+ const preferredForPrompt = agentType ?? ctx.backendFallbackAgentType ?? null;
32093
+ if (!runId) {
32094
+ ctx.log("[Agent] Prompt ignored: missing runId (cannot route session updates).");
32095
+ return null;
32096
+ }
32097
+ const acpAgentKey = computeAcpAgentKey(preferredForPrompt, mode, agentConfig ?? null);
32098
+ if (!acpAgentKey) {
32099
+ return null;
32100
+ }
32101
+ const activeAcpSessionAgentKey = computeAcpSessionAgentKey(sessionId, acpAgentKey);
32102
+ ctx.pendingCancelRunIds.delete(runId);
32103
+ ctx.promptRouting.registerRun({ sessionId, runId });
32104
+ ctx.runDispatch.set(runId, { acpSessionAgentKey: activeAcpSessionAgentKey });
32105
+ return {
32106
+ activeRunId: runId,
32107
+ activeAcpAgentKey: acpAgentKey,
32108
+ activeAcpSessionAgentKey,
32109
+ preferredForPrompt
32110
+ };
32111
+ }
32112
+
32113
+ // src/agents/acp/send-prompt-result-augment.ts
32114
+ function augmentPromptResultAuthFields(agentType, errorText) {
32115
+ const err = errorText ?? "";
32116
+ const at = agentType ?? null;
32117
+ const evaluated = Boolean(at && err.trim());
32118
+ const suggestsAuth = evaluated && at ? localAgentErrorSuggestsAuth(at, err) : false;
32119
+ if (!suggestsAuth || !agentType) return {};
32120
+ return { agentAuthRequired: true, agentType };
32121
+ }
32122
+
31883
32123
  // src/git/git-runtime.ts
31884
32124
  var activeGitChildProcesses = /* @__PURE__ */ new Set();
31885
32125
  function abortActiveGitChildProcesses() {
@@ -31954,8 +32194,8 @@ async function execGitFile(args, options) {
31954
32194
  }
31955
32195
 
31956
32196
  // src/git/changes/parse/normalize-git-diff-path.ts
31957
- function normalizeGitDiffPath(path73) {
31958
- return path73.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
32197
+ function normalizeGitDiffPath(path74) {
32198
+ return path74.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
31959
32199
  }
31960
32200
 
31961
32201
  // src/git/changes/parse/parse-name-status-entries.ts
@@ -32242,9 +32482,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32242
32482
  // src/agents/acp/put-summarize-change-summaries.ts
32243
32483
  async function putEncryptedChangeSummaryRows(params) {
32244
32484
  const base = params.apiBaseUrl.replace(/\/+$/, "");
32245
- const entries = params.rows.map(({ path: path73, summary }) => {
32485
+ const entries = params.rows.map(({ path: path74, summary }) => {
32246
32486
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
32247
- return { path: path73, summary: JSON.stringify(enc) };
32487
+ return { path: path74, summary: JSON.stringify(enc) };
32248
32488
  });
32249
32489
  const res = await fetch(
32250
32490
  `${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -32335,130 +32575,136 @@ async function maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess(params) {
32335
32575
  }
32336
32576
  }
32337
32577
 
32338
- // src/agents/acp/send-prompt-result-augment.ts
32339
- function augmentPromptResultAuthFields(agentType, errorText) {
32340
- const err = errorText ?? "";
32341
- const at = agentType ?? null;
32342
- const evaluated = Boolean(at && err.trim());
32343
- const suggestsAuth = evaluated && at ? localAgentErrorSuggestsAuth(at, err) : false;
32344
- if (!suggestsAuth || !agentType) return {};
32345
- return { agentAuthRequired: true, agentType };
32346
- }
32347
-
32348
- // ../e2ee/src/constants.ts
32349
- var E2EE_NONCE_BYTES = 12;
32350
-
32351
- // ../e2ee/src/types.ts
32352
- function isE2eeEnvelope(value) {
32353
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
32354
- const o = value;
32355
- return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
32356
- }
32357
-
32358
- // ../e2ee/src/encoding.ts
32359
- function base64UrlEncode(bytes) {
32360
- let binary = "";
32361
- for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
32362
- const b64 = btoa(binary);
32363
- return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
32364
- }
32365
- function base64UrlDecode(value) {
32366
- const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
32367
- const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
32368
- const binary = atob(padded);
32369
- const out = new Uint8Array(binary.length);
32370
- for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
32371
- return out;
32578
+ // src/agents/planning/submit-planning-todos-for-turn.ts
32579
+ async function submitPlanningTodosForTurn(params) {
32580
+ const token = params.getCloudAccessToken();
32581
+ if (!token.trim()) return { ok: false, error: "Missing cloud access token" };
32582
+ const base = params.cloudApiBaseUrl.replace(/\/$/, "");
32583
+ const url2 = `${base}/internal/sessions/todos/submit`;
32584
+ const res = await fetch(url2, {
32585
+ method: "POST",
32586
+ headers: {
32587
+ Authorization: `Bearer ${token}`,
32588
+ "Content-Type": "application/json"
32589
+ },
32590
+ body: JSON.stringify({
32591
+ sessionId: params.sessionId,
32592
+ turnId: params.turnId,
32593
+ items: params.items
32594
+ })
32595
+ });
32596
+ if (!res.ok) {
32597
+ const body = await res.json().catch(() => null);
32598
+ return { ok: false, error: body?.error ?? `Planning todo submit failed (${res.status})` };
32599
+ }
32600
+ return { ok: true };
32372
32601
  }
32373
32602
 
32374
- // src/agents/acp/fetch-session-attachments.ts
32375
- function metaSaysEncrypted(meta) {
32376
- if (!meta) return false;
32377
- const e = meta.encrypted;
32378
- return e === true || e === "true" || e === 1;
32379
- }
32380
- function warnIfDecodedImageMagicUnexpected(buf, mimeType, idShort) {
32381
- const m = mimeType.toLowerCase();
32382
- if (!m.startsWith("image/") || buf.length < 4) return;
32383
- const b0 = buf[0];
32384
- const b1 = buf[1];
32385
- const b2 = buf[2];
32386
- const b3 = buf[3];
32387
- let looksOk = false;
32388
- if (m.includes("png") && b0 === 137 && b1 === 80 && b2 === 78 && b3 === 71) looksOk = true;
32389
- else if ((m.includes("jpeg") || m.includes("jpg")) && b0 === 255 && b1 === 216 && b2 === 255) looksOk = true;
32390
- else if (m.includes("gif") && b0 === 71 && b1 === 73 && b2 === 70) looksOk = true;
32391
- else if (m.includes("webp") && buf.length >= 12 && buf.subarray(0, 4).toString("ascii") === "RIFF" && buf.subarray(8, 12).toString("ascii") === "WEBP")
32392
- looksOk = true;
32393
- else if (!/(png|jpe?g|gif|webp)/i.test(m)) looksOk = true;
32394
- if (!looksOk) {
32395
- logDebug(
32396
- `[Agent] Attachment ${idShort} (${mimeType}): decoded bytes do not match common image signatures \u2014 wrong E2EE key, corrupt blob, or metadata mismatch. First 12 bytes (hex): ${buf.subarray(0, Math.min(12, buf.length)).toString("hex")}`
32397
- );
32603
+ // src/agents/planning/maybe-submit-planning-todos-after-agent-success.ts
32604
+ async function maybeSubmitPlanningTodosAfterAgentSuccess(params) {
32605
+ const { sessionId, runId, resultSuccess, output, cloudApiBaseUrl, getCloudAccessToken, log: log2 } = params;
32606
+ const buffered = runId ? getPlanningSessionTurnOutput(runId) : "";
32607
+ const outputFromResult = typeof output === "string" ? output : "";
32608
+ const outputStr = buffered.trim() !== "" ? buffered : outputFromResult;
32609
+ if (!sessionId || !runId || !resultSuccess || outputStr.trim() === "") {
32610
+ return { submitted: false, suppressOutput: false };
32398
32611
  }
32399
- }
32400
- async function fetchSessionAttachmentPayloadsForAgent(params) {
32401
- const { attachments, sessionId, cloudApiBaseUrl, getCloudAccessToken, e2ee, log: log2 } = params;
32402
- const token = getCloudAccessToken();
32403
- if (!token) {
32404
- return { ok: false, error: "Missing cloud access token; cannot download attachments." };
32612
+ if (!cloudApiBaseUrl || !getCloudAccessToken) {
32613
+ return { submitted: false, suppressOutput: false };
32405
32614
  }
32406
- const wantedCount = attachments.filter((a) => typeof a.attachmentId === "string" && a.attachmentId.trim() !== "").length;
32407
- if (wantedCount === 0) {
32408
- return { ok: false, error: "No valid attachment ids in prompt." };
32615
+ const items = parsePlanningTodoAgentJson(outputStr);
32616
+ if (items.length === 0) {
32617
+ return { submitted: false, suppressOutput: false };
32409
32618
  }
32410
- const base = cloudApiBaseUrl.replace(/\/+$/, "");
32411
- const out = [];
32412
- for (const a of attachments) {
32413
- const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
32414
- if (!id) continue;
32415
- const metaUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
32416
- const blobUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
32417
- const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
32418
- if (!metaRes.ok) {
32419
- const t = await metaRes.text().catch(() => "");
32420
- log2(`[Agent] Attachment meta fetch failed ${metaRes.status}: ${t.slice(0, 200)}`);
32421
- return { ok: false, error: `Could not load attachment (${id.slice(0, 8)}\u2026): ${metaRes.status}` };
32422
- }
32423
- const meta = await metaRes.json().catch(() => null);
32424
- const mimeType = typeof meta?.mimeType === "string" && meta.mimeType.trim() ? meta.mimeType.trim() : a.mimeType;
32425
- const blobRes = await fetch(blobUrl, { headers: { Authorization: `Bearer ${token}` } });
32426
- if (!blobRes.ok) {
32427
- const t = await blobRes.text().catch(() => "");
32428
- log2(`[Agent] Attachment blob fetch failed ${blobRes.status}: ${t.slice(0, 200)}`);
32429
- return { ok: false, error: `Could not load attachment data (${id.slice(0, 8)}\u2026): ${blobRes.status}` };
32430
- }
32431
- const buf = Buffer.from(await blobRes.arrayBuffer());
32432
- let imageBytes;
32433
- const encrypted = metaSaysEncrypted(meta);
32434
- if (encrypted) {
32435
- if (!e2ee) {
32436
- return { ok: false, error: "Encrypted attachments require E2EE keys on this bridge." };
32437
- }
32438
- const k = typeof meta?.k === "string" ? meta.k : "";
32439
- const n = typeof meta?.n === "string" ? meta.n : "";
32440
- if (!k || !n) {
32441
- return { ok: false, error: "Invalid encrypted attachment metadata (missing key id or nonce)." };
32442
- }
32443
- const c = base64UrlEncode(buf);
32444
- imageBytes = e2ee.decryptEnvelopeToBuffer({ k, n, c });
32619
+ const result = await submitPlanningTodosForTurn({
32620
+ sessionId,
32621
+ turnId: runId,
32622
+ items,
32623
+ cloudApiBaseUrl,
32624
+ getCloudAccessToken
32625
+ });
32626
+ if (!result.ok) {
32627
+ log2(`[Agent] Planning todo submit failed: ${result.error}`);
32628
+ return { submitted: false, suppressOutput: false };
32629
+ }
32630
+ if (runId) consumePlanningSessionTurnOutput(runId);
32631
+ log2(`[Agent] Planning session: stored ${items.length} todo(s) via internal API`);
32632
+ return { submitted: true, suppressOutput: true };
32633
+ }
32634
+
32635
+ // src/agents/acp/prompts/finalize-and-send-prompt-result.ts
32636
+ async function finalizeAndSendPromptResult(params) {
32637
+ const {
32638
+ result,
32639
+ sessionId,
32640
+ runId,
32641
+ promptId,
32642
+ agentType,
32643
+ agentCwd,
32644
+ isPlanningSession,
32645
+ followUpCatalogPromptId,
32646
+ sessionChangeSummaryFilePaths,
32647
+ cloudApiBaseUrl,
32648
+ getCloudAccessToken,
32649
+ e2ee,
32650
+ sendResult,
32651
+ sendSessionUpdate,
32652
+ log: log2
32653
+ } = params;
32654
+ if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
32655
+ await collectTurnGitDiffFromPreTurnSnapshot({
32656
+ sessionId,
32657
+ runId,
32658
+ agentCwd,
32659
+ sendSessionUpdate,
32660
+ log: log2
32661
+ });
32662
+ }
32663
+ await maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess({
32664
+ sessionId,
32665
+ runId,
32666
+ resultSuccess: result.success === true,
32667
+ output: result.output,
32668
+ e2ee,
32669
+ cloudApiBaseUrl,
32670
+ getCloudAccessToken,
32671
+ followUpCatalogPromptId,
32672
+ sessionChangeSummaryFilePaths,
32673
+ log: log2
32674
+ });
32675
+ const planningTodosSubmit = await maybeSubmitPlanningTodosAfterAgentSuccess({
32676
+ sessionId,
32677
+ runId,
32678
+ resultSuccess: result.success === true,
32679
+ output: result.output,
32680
+ cloudApiBaseUrl,
32681
+ getCloudAccessToken,
32682
+ log: log2
32683
+ });
32684
+ const errStr = typeof result.error === "string" ? result.error : void 0;
32685
+ const resultStop = typeof result.stopReason === "string" ? result.stopReason.trim() : "";
32686
+ const cancelledByAgent = resultStop.toLowerCase() === "cancelled" || errStr != null && isUserEndedSessionTurnErrorText(errStr);
32687
+ const planningFallbackOutput = !planningTodosSubmit.suppressOutput && isPlanningSession && runId ? consumePlanningSessionTurnOutput(runId) : "";
32688
+ sendResult({
32689
+ type: "prompt_result",
32690
+ id: promptId,
32691
+ ...sessionId ? { sessionId } : {},
32692
+ ...runId ? { runId } : {},
32693
+ ...result,
32694
+ ...planningTodosSubmit.suppressOutput ? { output: void 0 } : planningFallbackOutput.trim() !== "" ? { output: planningFallbackOutput } : {},
32695
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
32696
+ ...augmentPromptResultAuthFields(agentType, errStr),
32697
+ ...!result.success && cancelledByAgent ? { stopReason: "cancelled" } : {}
32698
+ });
32699
+ if (!result.success) {
32700
+ if (cancelledByAgent) {
32701
+ log2("[Agent] Run ended after stop request (stopped by user).");
32445
32702
  } else {
32446
- imageBytes = buf;
32703
+ log2(
32704
+ `[Agent] Prompt did not run successfully on the agent (no successful start/completion): ${result.error ?? "Unknown error"}`
32705
+ );
32447
32706
  }
32448
- warnIfDecodedImageMagicUnexpected(imageBytes, mimeType, id.slice(0, 8));
32449
- logDebug(
32450
- `[Agent] Loaded prompt image ${id.slice(0, 8)}\u2026: ${imageBytes.length} bytes, mime=${mimeType}, ${encrypted ? "E2EE decrypted" : "plaintext from storage"}`
32451
- );
32452
- const dataBase64 = imageBytes.toString("base64");
32453
- out.push({ mimeType, dataBase64 });
32454
- }
32455
- if (out.length !== wantedCount) {
32456
- return {
32457
- ok: false,
32458
- error: `Expected ${wantedCount} image attachment(s) but only loaded ${out.length} (check attachment ids and empty rows).`
32459
- };
32460
32707
  }
32461
- return { ok: true, images: out };
32462
32708
  }
32463
32709
 
32464
32710
  // src/agents/acp/build-forked-session-agent-prompt.ts
@@ -32518,6 +32764,32 @@ async function fetchCloudSessionMeta(params) {
32518
32764
  });
32519
32765
  }
32520
32766
 
32767
+ // ../e2ee/src/constants.ts
32768
+ var E2EE_NONCE_BYTES = 12;
32769
+
32770
+ // ../e2ee/src/types.ts
32771
+ function isE2eeEnvelope(value) {
32772
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
32773
+ const o = value;
32774
+ return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
32775
+ }
32776
+
32777
+ // ../e2ee/src/encoding.ts
32778
+ function base64UrlEncode(bytes) {
32779
+ let binary = "";
32780
+ for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
32781
+ const b64 = btoa(binary);
32782
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
32783
+ }
32784
+ function base64UrlDecode(value) {
32785
+ const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
32786
+ const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
32787
+ const binary = atob(padded);
32788
+ const out = new Uint8Array(binary.length);
32789
+ for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
32790
+ return out;
32791
+ }
32792
+
32521
32793
  // src/lib/e2ee/decrypt-stored-e2ee-content.ts
32522
32794
  function parseJsonObject(raw) {
32523
32795
  try {
@@ -32715,7 +32987,213 @@ function enrichIntegrationContentPromptForAgent(params) {
32715
32987
  return injectIntegrationContentAgentPromptNote(params.agentPromptText, params.sessionId);
32716
32988
  }
32717
32989
 
32718
- // src/agents/acp/send-prompt-to-agent.ts
32990
+ // src/agents/planning/enrich-planning-session-prompt-for-agent.ts
32991
+ async function enrichPlanningSessionPromptForAgent(params) {
32992
+ const meta = await fetchCloudSessionMeta({
32993
+ sessionId: params.sessionId,
32994
+ cloudApiBaseUrl: params.cloudApiBaseUrl,
32995
+ getCloudAccessToken: params.getCloudAccessToken
32996
+ });
32997
+ if (!meta.ok) return { promptText: params.promptText, isPlanningSession: false };
32998
+ const sessionKind = parseSessionKind(meta.data.sessionKind);
32999
+ if (sessionKind !== "planning") return { promptText: params.promptText, isPlanningSession: false };
33000
+ const todos = Array.isArray(meta.data.planningTodos) ? meta.data.planningTodos : [];
33001
+ if (params.isNewSession || todos.length === 0) {
33002
+ return {
33003
+ promptText: buildPlanningSessionAgentPrompt(params.promptText),
33004
+ isPlanningSession: true
33005
+ };
33006
+ }
33007
+ return {
33008
+ promptText: buildPlanningSessionFollowUpAgentPrompt(params.promptText, todos),
33009
+ isPlanningSession: true
33010
+ };
33011
+ }
33012
+
33013
+ // src/agents/acp/prompts/prepare-agent-prompt-text.ts
33014
+ async function prepareAgentPromptText(params) {
33015
+ const { promptText, sessionId, runId, isNewSession, cloudApiBaseUrl, getCloudAccessToken } = params;
33016
+ let agentPromptText = promptText;
33017
+ let isPlanningSession = false;
33018
+ if (sessionId && cloudApiBaseUrl && getCloudAccessToken) {
33019
+ agentPromptText = await enrichForkedSessionPromptForAgent({
33020
+ sessionId,
33021
+ promptText,
33022
+ cloudApiBaseUrl,
33023
+ getCloudAccessToken
33024
+ });
33025
+ const planningEnriched = await enrichPlanningSessionPromptForAgent({
33026
+ sessionId,
33027
+ promptText: agentPromptText,
33028
+ isNewSession,
33029
+ cloudApiBaseUrl,
33030
+ getCloudAccessToken
33031
+ });
33032
+ agentPromptText = planningEnriched.promptText;
33033
+ isPlanningSession = planningEnriched.isPlanningSession;
33034
+ }
33035
+ if (isPlanningSession && runId) {
33036
+ registerPlanningSessionTurn(runId);
33037
+ }
33038
+ if (sessionId) {
33039
+ agentPromptText = enrichIntegrationContentPromptForAgent({
33040
+ sessionId,
33041
+ originalPromptText: promptText,
33042
+ agentPromptText
33043
+ });
33044
+ }
33045
+ return { agentPromptText, isPlanningSession };
33046
+ }
33047
+
33048
+ // src/agents/acp/fetch-session-attachments.ts
33049
+ function metaSaysEncrypted(meta) {
33050
+ if (!meta) return false;
33051
+ const e = meta.encrypted;
33052
+ return e === true || e === "true" || e === 1;
33053
+ }
33054
+ function warnIfDecodedImageMagicUnexpected(buf, mimeType, idShort) {
33055
+ const m = mimeType.toLowerCase();
33056
+ if (!m.startsWith("image/") || buf.length < 4) return;
33057
+ const b0 = buf[0];
33058
+ const b1 = buf[1];
33059
+ const b2 = buf[2];
33060
+ const b3 = buf[3];
33061
+ let looksOk = false;
33062
+ if (m.includes("png") && b0 === 137 && b1 === 80 && b2 === 78 && b3 === 71) looksOk = true;
33063
+ else if ((m.includes("jpeg") || m.includes("jpg")) && b0 === 255 && b1 === 216 && b2 === 255) looksOk = true;
33064
+ else if (m.includes("gif") && b0 === 71 && b1 === 73 && b2 === 70) looksOk = true;
33065
+ else if (m.includes("webp") && buf.length >= 12 && buf.subarray(0, 4).toString("ascii") === "RIFF" && buf.subarray(8, 12).toString("ascii") === "WEBP")
33066
+ looksOk = true;
33067
+ else if (!/(png|jpe?g|gif|webp)/i.test(m)) looksOk = true;
33068
+ if (!looksOk) {
33069
+ logDebug(
33070
+ `[Agent] Attachment ${idShort} (${mimeType}): decoded bytes do not match common image signatures \u2014 wrong E2EE key, corrupt blob, or metadata mismatch. First 12 bytes (hex): ${buf.subarray(0, Math.min(12, buf.length)).toString("hex")}`
33071
+ );
33072
+ }
33073
+ }
33074
+ async function fetchSessionAttachmentPayloadsForAgent(params) {
33075
+ const { attachments, sessionId, cloudApiBaseUrl, getCloudAccessToken, e2ee, log: log2 } = params;
33076
+ const token = getCloudAccessToken();
33077
+ if (!token) {
33078
+ return { ok: false, error: "Missing cloud access token; cannot download attachments." };
33079
+ }
33080
+ const wantedCount = attachments.filter((a) => typeof a.attachmentId === "string" && a.attachmentId.trim() !== "").length;
33081
+ if (wantedCount === 0) {
33082
+ return { ok: false, error: "No valid attachment ids in prompt." };
33083
+ }
33084
+ const base = cloudApiBaseUrl.replace(/\/+$/, "");
33085
+ const out = [];
33086
+ for (const a of attachments) {
33087
+ const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
33088
+ if (!id) continue;
33089
+ const metaUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
33090
+ const blobUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
33091
+ const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
33092
+ if (!metaRes.ok) {
33093
+ const t = await metaRes.text().catch(() => "");
33094
+ log2(`[Agent] Attachment meta fetch failed ${metaRes.status}: ${t.slice(0, 200)}`);
33095
+ return { ok: false, error: `Could not load attachment (${id.slice(0, 8)}\u2026): ${metaRes.status}` };
33096
+ }
33097
+ const meta = await metaRes.json().catch(() => null);
33098
+ const mimeType = typeof meta?.mimeType === "string" && meta.mimeType.trim() ? meta.mimeType.trim() : a.mimeType;
33099
+ const blobRes = await fetch(blobUrl, { headers: { Authorization: `Bearer ${token}` } });
33100
+ if (!blobRes.ok) {
33101
+ const t = await blobRes.text().catch(() => "");
33102
+ log2(`[Agent] Attachment blob fetch failed ${blobRes.status}: ${t.slice(0, 200)}`);
33103
+ return { ok: false, error: `Could not load attachment data (${id.slice(0, 8)}\u2026): ${blobRes.status}` };
33104
+ }
33105
+ const buf = Buffer.from(await blobRes.arrayBuffer());
33106
+ let imageBytes;
33107
+ const encrypted = metaSaysEncrypted(meta);
33108
+ if (encrypted) {
33109
+ if (!e2ee) {
33110
+ return { ok: false, error: "Encrypted attachments require E2EE keys on this bridge." };
33111
+ }
33112
+ const k = typeof meta?.k === "string" ? meta.k : "";
33113
+ const n = typeof meta?.n === "string" ? meta.n : "";
33114
+ if (!k || !n) {
33115
+ return { ok: false, error: "Invalid encrypted attachment metadata (missing key id or nonce)." };
33116
+ }
33117
+ const c = base64UrlEncode(buf);
33118
+ imageBytes = e2ee.decryptEnvelopeToBuffer({ k, n, c });
33119
+ } else {
33120
+ imageBytes = buf;
33121
+ }
33122
+ warnIfDecodedImageMagicUnexpected(imageBytes, mimeType, id.slice(0, 8));
33123
+ logDebug(
33124
+ `[Agent] Loaded prompt image ${id.slice(0, 8)}\u2026: ${imageBytes.length} bytes, mime=${mimeType}, ${encrypted ? "E2EE decrypted" : "plaintext from storage"}`
33125
+ );
33126
+ const dataBase64 = imageBytes.toString("base64");
33127
+ out.push({ mimeType, dataBase64 });
33128
+ }
33129
+ if (out.length !== wantedCount) {
33130
+ return {
33131
+ ok: false,
33132
+ error: `Expected ${wantedCount} image attachment(s) but only loaded ${out.length} (check attachment ids and empty rows).`
33133
+ };
33134
+ }
33135
+ return { ok: true, images: out };
33136
+ }
33137
+
33138
+ // src/agents/acp/prompts/resolve-send-prompt-images.ts
33139
+ async function resolveSendPromptImages(params) {
33140
+ const {
33141
+ attachments,
33142
+ sessionId,
33143
+ promptId,
33144
+ runId,
33145
+ agentType,
33146
+ followUpCatalogPromptId,
33147
+ cloudApiBaseUrl,
33148
+ getCloudAccessToken,
33149
+ e2ee,
33150
+ log: log2
33151
+ } = params;
33152
+ if (!attachments || attachments.length === 0) {
33153
+ return { ok: true, sendOpts: {} };
33154
+ }
33155
+ if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
33156
+ return {
33157
+ ok: false,
33158
+ errorResult: {
33159
+ type: "prompt_result",
33160
+ id: promptId,
33161
+ ...sessionId ? { sessionId } : {},
33162
+ ...runId ? { runId } : {},
33163
+ success: false,
33164
+ error: "Prompt includes images but the bridge is not configured with a cloud API URL and token to fetch them.",
33165
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
33166
+ ...augmentPromptResultAuthFields(agentType, "missing cloud for images download")
33167
+ }
33168
+ };
33169
+ }
33170
+ const resolved = await fetchSessionAttachmentPayloadsForAgent({
33171
+ attachments,
33172
+ sessionId,
33173
+ cloudApiBaseUrl,
33174
+ getCloudAccessToken,
33175
+ e2ee,
33176
+ log: log2
33177
+ });
33178
+ if (!resolved.ok) {
33179
+ return {
33180
+ ok: false,
33181
+ errorResult: {
33182
+ type: "prompt_result",
33183
+ id: promptId,
33184
+ ...sessionId ? { sessionId } : {},
33185
+ ...runId ? { runId } : {},
33186
+ success: false,
33187
+ error: resolved.error,
33188
+ ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
33189
+ ...augmentPromptResultAuthFields(agentType, resolved.error)
33190
+ }
33191
+ };
33192
+ }
33193
+ return { ok: true, sendOpts: { images: resolved.images } };
33194
+ }
33195
+
33196
+ // src/agents/acp/prompts/send-prompt-to-agent.ts
32719
33197
  async function sendPromptToAgent(options) {
32720
33198
  const {
32721
33199
  handle,
@@ -32733,107 +33211,54 @@ async function sendPromptToAgent(options) {
32733
33211
  cloudApiBaseUrl,
32734
33212
  getCloudAccessToken,
32735
33213
  e2ee,
32736
- attachments
33214
+ attachments,
33215
+ isNewSession = false
32737
33216
  } = options;
33217
+ let isPlanningSession = false;
32738
33218
  try {
32739
- let agentPromptText = promptText;
32740
- if (sessionId && cloudApiBaseUrl && getCloudAccessToken) {
32741
- agentPromptText = await enrichForkedSessionPromptForAgent({
32742
- sessionId,
32743
- promptText,
32744
- cloudApiBaseUrl,
32745
- getCloudAccessToken
32746
- });
32747
- }
32748
- if (sessionId) {
32749
- agentPromptText = enrichIntegrationContentPromptForAgent({
32750
- sessionId,
32751
- originalPromptText: promptText,
32752
- agentPromptText
32753
- });
32754
- }
32755
- let sendOpts = {};
32756
- if (attachments && attachments.length > 0) {
32757
- if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
32758
- sendResult({
32759
- type: "prompt_result",
32760
- id: promptId,
32761
- ...sessionId ? { sessionId } : {},
32762
- ...runId ? { runId } : {},
32763
- success: false,
32764
- error: "Prompt includes images but the bridge is not configured with a cloud API URL and token to fetch them.",
32765
- ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
32766
- ...augmentPromptResultAuthFields(agentType, "missing cloud for images download")
32767
- });
32768
- return;
32769
- }
32770
- const resolved = await fetchSessionAttachmentPayloadsForAgent({
32771
- attachments,
32772
- sessionId,
32773
- cloudApiBaseUrl,
32774
- getCloudAccessToken,
32775
- e2ee,
32776
- log: log2
32777
- });
32778
- if (!resolved.ok) {
32779
- sendResult({
32780
- type: "prompt_result",
32781
- id: promptId,
32782
- ...sessionId ? { sessionId } : {},
32783
- ...runId ? { runId } : {},
32784
- success: false,
32785
- error: resolved.error,
32786
- ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
32787
- ...augmentPromptResultAuthFields(agentType, resolved.error)
32788
- });
32789
- return;
32790
- }
32791
- sendOpts = { images: resolved.images };
32792
- }
32793
- const result = await handle.sendPrompt(agentPromptText, sendOpts);
32794
- if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
32795
- await collectTurnGitDiffFromPreTurnSnapshot({
32796
- sessionId,
32797
- runId,
32798
- agentCwd,
32799
- sendSessionUpdate,
32800
- log: log2
32801
- });
32802
- }
32803
- await maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess({
33219
+ const prepared = await prepareAgentPromptText({
33220
+ promptText,
32804
33221
  sessionId,
32805
33222
  runId,
32806
- resultSuccess: result.success === true,
32807
- output: result.output,
32808
- e2ee,
33223
+ isNewSession,
33224
+ cloudApiBaseUrl,
33225
+ getCloudAccessToken
33226
+ });
33227
+ isPlanningSession = prepared.isPlanningSession;
33228
+ const imagesResolved = await resolveSendPromptImages({
33229
+ attachments,
33230
+ sessionId,
33231
+ promptId,
33232
+ runId,
33233
+ agentType,
33234
+ followUpCatalogPromptId,
32809
33235
  cloudApiBaseUrl,
32810
33236
  getCloudAccessToken,
33237
+ e2ee,
33238
+ log: log2
33239
+ });
33240
+ if (!imagesResolved.ok) {
33241
+ sendResult(imagesResolved.errorResult);
33242
+ return;
33243
+ }
33244
+ const result = await handle.sendPrompt(prepared.agentPromptText, imagesResolved.sendOpts);
33245
+ await finalizeAndSendPromptResult({
33246
+ result,
33247
+ sessionId,
33248
+ runId,
33249
+ promptId,
33250
+ agentType,
33251
+ agentCwd,
33252
+ isPlanningSession,
32811
33253
  followUpCatalogPromptId,
32812
33254
  sessionChangeSummaryFilePaths,
33255
+ cloudApiBaseUrl,
33256
+ getCloudAccessToken,
33257
+ e2ee,
33258
+ sendResult,
33259
+ sendSessionUpdate,
32813
33260
  log: log2
32814
33261
  });
32815
- const errStr = typeof result.error === "string" ? result.error : void 0;
32816
- const resultStop = typeof result.stopReason === "string" ? result.stopReason.trim() : "";
32817
- const cancelledByAgent = resultStop.toLowerCase() === "cancelled" || errStr != null && isUserEndedSessionTurnErrorText(errStr);
32818
- sendResult({
32819
- type: "prompt_result",
32820
- id: promptId,
32821
- ...sessionId ? { sessionId } : {},
32822
- ...runId ? { runId } : {},
32823
- ...result,
32824
- ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
32825
- ...augmentPromptResultAuthFields(agentType, errStr),
32826
- ...!result.success && cancelledByAgent ? { stopReason: "cancelled" } : {}
32827
- });
32828
- if (!result.success) {
32829
- if (cancelledByAgent) {
32830
- log2("[Agent] Run ended after stop request (stopped by user).");
32831
- } else {
32832
- log2(
32833
- `[Agent] Prompt did not run successfully on the agent (no successful start/completion): ${result.error ?? "Unknown error"}`
32834
- );
32835
- }
32836
- }
32837
33262
  } catch (err) {
32838
33263
  const errMsg = err instanceof Error ? err.message : String(err);
32839
33264
  log2(`[Agent] Send failed: ${errMsg}`);
@@ -32847,6 +33272,8 @@ async function sendPromptToAgent(options) {
32847
33272
  ...followUpCatalogPromptId != null && followUpCatalogPromptId !== "" ? { followUpCatalogPromptId } : {},
32848
33273
  ...augmentPromptResultAuthFields(agentType, errMsg)
32849
33274
  });
33275
+ } finally {
33276
+ if (runId && isPlanningSession) clearPlanningSessionTurn(runId);
32850
33277
  }
32851
33278
  }
32852
33279
 
@@ -32860,15 +33287,36 @@ function getAcpSessionAgentState(ctx, acpSessionAgentKey) {
32860
33287
  return state;
32861
33288
  }
32862
33289
 
32863
- // src/agents/acp/manager/handle-prompt.ts
32864
- function handlePrompt(ctx, opts) {
33290
+ // src/agents/acp/manager/handle-pending-prompt-cancel.ts
33291
+ async function handlePendingPromptCancel(params) {
33292
+ const { ctx, activeRunId, promptId, sessionId, handle, sendResult } = params;
33293
+ if (!ctx.pendingCancelRunIds.has(activeRunId)) {
33294
+ return false;
33295
+ }
33296
+ ctx.pendingCancelRunIds.delete(activeRunId);
33297
+ try {
33298
+ await handle.cancel?.();
33299
+ } catch {
33300
+ }
33301
+ sendResult({
33302
+ type: "prompt_result",
33303
+ id: promptId,
33304
+ ...sessionId ? { sessionId } : {},
33305
+ runId: activeRunId,
33306
+ success: false,
33307
+ error: "Stopped by user",
33308
+ stopReason: "cancelled"
33309
+ });
33310
+ return true;
33311
+ }
33312
+
33313
+ // src/agents/acp/manager/run-acp-prompt.ts
33314
+ async function runAcpPrompt(ctx, runCtx, opts) {
32865
33315
  const {
32866
33316
  promptText,
32867
33317
  promptId,
32868
33318
  sessionId,
32869
- runId,
32870
33319
  mode,
32871
- agentType,
32872
33320
  agentConfig,
32873
33321
  sessionParentPath,
32874
33322
  sendResult,
@@ -32878,109 +33326,99 @@ function handlePrompt(ctx, opts) {
32878
33326
  cloudApiBaseUrl,
32879
33327
  getCloudAccessToken,
32880
33328
  e2ee,
32881
- attachments
33329
+ attachments,
33330
+ isNewSession
32882
33331
  } = opts;
32883
- const preferredForPrompt = agentType ?? ctx.backendFallbackAgentType ?? null;
32884
- if (!runId) {
32885
- ctx.log("[Agent] Prompt ignored: missing runId (cannot route session updates).");
32886
- return;
32887
- }
32888
- const activeRunId = runId;
32889
- const acpAgentKey = computeAcpAgentKey(preferredForPrompt, mode, agentConfig ?? null);
32890
- if (!acpAgentKey) {
33332
+ const { activeRunId, activeAcpAgentKey, activeAcpSessionAgentKey, preferredForPrompt } = runCtx;
33333
+ const acpAgentState = getAcpSessionAgentState(ctx, activeAcpSessionAgentKey);
33334
+ const handle = await ensureAcpClient({
33335
+ state: acpAgentState,
33336
+ acpAgentKey: activeAcpAgentKey,
33337
+ preferredAgentType: preferredForPrompt,
33338
+ mode,
33339
+ agentConfig: agentConfig ?? null,
33340
+ sessionParentPath,
33341
+ resolveRouting: () => ctx.promptRouting.resolveRouting(activeAcpSessionAgentKey),
33342
+ cloudSessionId: sessionId,
33343
+ bridgeAccessPort: ctx.getBridgeAccessPort(),
33344
+ sendSessionUpdate,
33345
+ sendRequest: sendSessionUpdate,
33346
+ log: ctx.log,
33347
+ reportAgentCapabilities: ctx.reportAgentCapabilities
33348
+ });
33349
+ if (!handle) {
33350
+ const errMsg = acpAgentState.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app.";
33351
+ const evaluated = Boolean(preferredForPrompt && errMsg.trim());
33352
+ const suggestsAuth = evaluated ? localAgentErrorSuggestsAuth(preferredForPrompt, errMsg) : false;
33353
+ const auth = suggestsAuth && preferredForPrompt ? { agentAuthRequired: true, agentType: preferredForPrompt } : {};
32891
33354
  sendResult({
32892
33355
  type: "prompt_result",
32893
33356
  id: promptId,
32894
33357
  ...sessionId ? { sessionId } : {},
32895
33358
  runId: activeRunId,
32896
33359
  success: false,
32897
- error: "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge."
33360
+ error: errMsg,
33361
+ ...auth
32898
33362
  });
32899
33363
  return;
32900
33364
  }
32901
- const activeAcpAgentKey = acpAgentKey;
32902
- const activeAcpSessionAgentKey = computeAcpSessionAgentKey(sessionId, activeAcpAgentKey);
32903
- ctx.pendingCancelRunIds.delete(activeRunId);
32904
- ctx.promptRouting.registerRun({ sessionId, runId: activeRunId });
32905
- ctx.runDispatch.set(activeRunId, { acpSessionAgentKey: activeAcpSessionAgentKey });
32906
- async function run() {
32907
- const acpAgentState = getAcpSessionAgentState(ctx, activeAcpSessionAgentKey);
32908
- const handle = await ensureAcpClient({
32909
- state: acpAgentState,
32910
- acpAgentKey: activeAcpAgentKey,
32911
- preferredAgentType: preferredForPrompt,
32912
- mode,
32913
- agentConfig: agentConfig ?? null,
32914
- sessionParentPath,
32915
- resolveRouting: () => ctx.promptRouting.resolveRouting(activeAcpSessionAgentKey),
32916
- cloudSessionId: sessionId,
32917
- bridgeAccessPort: ctx.getBridgeAccessPort(),
33365
+ if (!ctx.promptRouting.isRegisteredRun(activeRunId)) {
33366
+ return;
33367
+ }
33368
+ const cancelled = await handlePendingPromptCancel({
33369
+ ctx,
33370
+ activeRunId,
33371
+ promptId,
33372
+ sessionId,
33373
+ handle,
33374
+ sendResult
33375
+ });
33376
+ if (cancelled) return;
33377
+ ctx.promptRouting.setStreamingRunId(activeAcpSessionAgentKey, activeRunId);
33378
+ try {
33379
+ await sendPromptToAgent({
33380
+ handle,
33381
+ promptText,
33382
+ promptId,
33383
+ sessionId,
33384
+ runId: activeRunId,
33385
+ agentType: preferredForPrompt,
33386
+ agentCwd: resolveSessionParentPathForAgentProcess(sessionParentPath),
33387
+ sendResult,
32918
33388
  sendSessionUpdate,
32919
- sendRequest: sendSessionUpdate,
32920
33389
  log: ctx.log,
32921
- reportAgentCapabilities: ctx.reportAgentCapabilities
33390
+ followUpCatalogPromptId,
33391
+ sessionChangeSummaryFilePaths,
33392
+ cloudApiBaseUrl,
33393
+ getCloudAccessToken,
33394
+ e2ee,
33395
+ attachments,
33396
+ isNewSession
32922
33397
  });
32923
- if (!handle) {
32924
- const errMsg = acpAgentState.lastAcpStartError || "No agent configured. Register local agents on this bridge in the app.";
32925
- const evaluated = Boolean(preferredForPrompt && errMsg.trim());
32926
- const suggestsAuth = evaluated ? localAgentErrorSuggestsAuth(preferredForPrompt, errMsg) : false;
32927
- const auth = suggestsAuth && preferredForPrompt ? { agentAuthRequired: true, agentType: preferredForPrompt } : {};
32928
- sendResult({
32929
- type: "prompt_result",
32930
- id: promptId,
32931
- ...sessionId ? { sessionId } : {},
32932
- runId: activeRunId,
32933
- success: false,
32934
- error: errMsg,
32935
- ...auth
32936
- });
32937
- return;
32938
- }
32939
- if (!ctx.promptRouting.isRegisteredRun(activeRunId)) {
32940
- return;
32941
- }
32942
- if (ctx.pendingCancelRunIds.has(activeRunId)) {
32943
- ctx.pendingCancelRunIds.delete(activeRunId);
32944
- try {
32945
- await handle.cancel?.();
32946
- } catch {
32947
- }
33398
+ } finally {
33399
+ ctx.promptRouting.clearStreamingRunId(activeAcpSessionAgentKey, activeRunId);
33400
+ }
33401
+ }
33402
+
33403
+ // src/agents/acp/manager/handle-prompt.ts
33404
+ function handlePrompt(ctx, opts) {
33405
+ const { promptId, sessionId, runId, mode, agentType, agentConfig, sendResult } = opts;
33406
+ const runCtx = resolvePromptRunContext(ctx, { runId, mode, agentType, agentConfig, sessionId });
33407
+ if (!runCtx) {
33408
+ if (runId) {
32948
33409
  sendResult({
32949
33410
  type: "prompt_result",
32950
33411
  id: promptId,
32951
33412
  ...sessionId ? { sessionId } : {},
32952
- runId: activeRunId,
33413
+ runId,
32953
33414
  success: false,
32954
- error: "Stopped by user",
32955
- stopReason: "cancelled"
33415
+ error: "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge."
32956
33416
  });
32957
- return;
32958
- }
32959
- ctx.promptRouting.setStreamingRunId(activeAcpSessionAgentKey, activeRunId);
32960
- try {
32961
- await sendPromptToAgent({
32962
- handle,
32963
- promptText,
32964
- promptId,
32965
- sessionId,
32966
- runId: activeRunId,
32967
- agentType: preferredForPrompt,
32968
- agentCwd: resolveSessionParentPathForAgentProcess(sessionParentPath),
32969
- sendResult,
32970
- sendSessionUpdate,
32971
- log: ctx.log,
32972
- followUpCatalogPromptId,
32973
- sessionChangeSummaryFilePaths,
32974
- cloudApiBaseUrl,
32975
- getCloudAccessToken,
32976
- e2ee,
32977
- attachments
32978
- });
32979
- } finally {
32980
- ctx.promptRouting.clearStreamingRunId(activeAcpSessionAgentKey, activeRunId);
32981
33417
  }
33418
+ return;
32982
33419
  }
32983
- void run().finally(() => {
33420
+ const { activeRunId } = runCtx;
33421
+ void runAcpPrompt(ctx, runCtx, opts).finally(() => {
32984
33422
  ctx.promptRouting.unregisterRun(activeRunId);
32985
33423
  ctx.runDispatch.delete(activeRunId);
32986
33424
  ctx.pendingCancelRunIds.delete(activeRunId);
@@ -33171,8 +33609,8 @@ function pathspec(...paths) {
33171
33609
  cache.set(key, paths);
33172
33610
  return key;
33173
33611
  }
33174
- function isPathSpec(path73) {
33175
- return path73 instanceof String && cache.has(path73);
33612
+ function isPathSpec(path74) {
33613
+ return path74 instanceof String && cache.has(path74);
33176
33614
  }
33177
33615
  function toPaths(pathSpec) {
33178
33616
  return cache.get(pathSpec) || [];
@@ -33261,8 +33699,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
33261
33699
  function forEachLineWithContent(input, callback) {
33262
33700
  return toLinesWithContent(input, true).map((line) => callback(line));
33263
33701
  }
33264
- function folderExists(path73) {
33265
- return (0, import_file_exists.exists)(path73, import_file_exists.FOLDER);
33702
+ function folderExists(path74) {
33703
+ return (0, import_file_exists.exists)(path74, import_file_exists.FOLDER);
33266
33704
  }
33267
33705
  function append(target, item) {
33268
33706
  if (Array.isArray(target)) {
@@ -33666,8 +34104,8 @@ function checkIsRepoRootTask() {
33666
34104
  commands,
33667
34105
  format: "utf-8",
33668
34106
  onError,
33669
- parser(path73) {
33670
- return /^\.(git)?$/.test(path73.trim());
34107
+ parser(path74) {
34108
+ return /^\.(git)?$/.test(path74.trim());
33671
34109
  }
33672
34110
  };
33673
34111
  }
@@ -34101,11 +34539,11 @@ function parseGrep(grep) {
34101
34539
  const paths = /* @__PURE__ */ new Set();
34102
34540
  const results = {};
34103
34541
  forEachLineWithContent(grep, (input) => {
34104
- const [path73, line, preview] = input.split(NULL);
34105
- paths.add(path73);
34106
- (results[path73] = results[path73] || []).push({
34542
+ const [path74, line, preview] = input.split(NULL);
34543
+ paths.add(path74);
34544
+ (results[path74] = results[path74] || []).push({
34107
34545
  line: asNumber(line),
34108
- path: path73,
34546
+ path: path74,
34109
34547
  preview
34110
34548
  });
34111
34549
  });
@@ -34870,14 +35308,14 @@ var init_hash_object = __esm2({
34870
35308
  init_task();
34871
35309
  }
34872
35310
  });
34873
- function parseInit(bare, path73, text) {
35311
+ function parseInit(bare, path74, text) {
34874
35312
  const response = String(text).trim();
34875
35313
  let result;
34876
35314
  if (result = initResponseRegex.exec(response)) {
34877
- return new InitSummary(bare, path73, false, result[1]);
35315
+ return new InitSummary(bare, path74, false, result[1]);
34878
35316
  }
34879
35317
  if (result = reInitResponseRegex.exec(response)) {
34880
- return new InitSummary(bare, path73, true, result[1]);
35318
+ return new InitSummary(bare, path74, true, result[1]);
34881
35319
  }
34882
35320
  let gitDir = "";
34883
35321
  const tokens = response.split(" ");
@@ -34888,7 +35326,7 @@ function parseInit(bare, path73, text) {
34888
35326
  break;
34889
35327
  }
34890
35328
  }
34891
- return new InitSummary(bare, path73, /^re/i.test(response), gitDir);
35329
+ return new InitSummary(bare, path74, /^re/i.test(response), gitDir);
34892
35330
  }
34893
35331
  var InitSummary;
34894
35332
  var initResponseRegex;
@@ -34897,9 +35335,9 @@ var init_InitSummary = __esm2({
34897
35335
  "src/lib/responses/InitSummary.ts"() {
34898
35336
  "use strict";
34899
35337
  InitSummary = class {
34900
- constructor(bare, path73, existing, gitDir) {
35338
+ constructor(bare, path74, existing, gitDir) {
34901
35339
  this.bare = bare;
34902
- this.path = path73;
35340
+ this.path = path74;
34903
35341
  this.existing = existing;
34904
35342
  this.gitDir = gitDir;
34905
35343
  }
@@ -34911,7 +35349,7 @@ var init_InitSummary = __esm2({
34911
35349
  function hasBareCommand(command) {
34912
35350
  return command.includes(bareCommand);
34913
35351
  }
34914
- function initTask(bare = false, path73, customArgs) {
35352
+ function initTask(bare = false, path74, customArgs) {
34915
35353
  const commands = ["init", ...customArgs];
34916
35354
  if (bare && !hasBareCommand(commands)) {
34917
35355
  commands.splice(1, 0, bareCommand);
@@ -34920,7 +35358,7 @@ function initTask(bare = false, path73, customArgs) {
34920
35358
  commands,
34921
35359
  format: "utf-8",
34922
35360
  parser(text) {
34923
- return parseInit(commands.includes("--bare"), path73, text);
35361
+ return parseInit(commands.includes("--bare"), path74, text);
34924
35362
  }
34925
35363
  };
34926
35364
  }
@@ -35736,12 +36174,12 @@ var init_FileStatusSummary = __esm2({
35736
36174
  "use strict";
35737
36175
  fromPathRegex = /^(.+)\0(.+)$/;
35738
36176
  FileStatusSummary = class {
35739
- constructor(path73, index, working_dir) {
35740
- this.path = path73;
36177
+ constructor(path74, index, working_dir) {
36178
+ this.path = path74;
35741
36179
  this.index = index;
35742
36180
  this.working_dir = working_dir;
35743
36181
  if (index === "R" || working_dir === "R") {
35744
- const detail = fromPathRegex.exec(path73) || [null, path73, path73];
36182
+ const detail = fromPathRegex.exec(path74) || [null, path74, path74];
35745
36183
  this.from = detail[2] || "";
35746
36184
  this.path = detail[1] || "";
35747
36185
  }
@@ -35772,14 +36210,14 @@ function splitLine(result, lineStr) {
35772
36210
  default:
35773
36211
  return;
35774
36212
  }
35775
- function data(index, workingDir, path73) {
36213
+ function data(index, workingDir, path74) {
35776
36214
  const raw = `${index}${workingDir}`;
35777
36215
  const handler = parsers6.get(raw);
35778
36216
  if (handler) {
35779
- handler(result, path73);
36217
+ handler(result, path74);
35780
36218
  }
35781
36219
  if (raw !== "##" && raw !== "!!") {
35782
- result.files.push(new FileStatusSummary(path73, index, workingDir));
36220
+ result.files.push(new FileStatusSummary(path74, index, workingDir));
35783
36221
  }
35784
36222
  }
35785
36223
  }
@@ -36088,9 +36526,9 @@ var init_simple_git_api = __esm2({
36088
36526
  next
36089
36527
  );
36090
36528
  }
36091
- hashObject(path73, write) {
36529
+ hashObject(path74, write) {
36092
36530
  return this._runTask(
36093
- hashObjectTask(path73, write === true),
36531
+ hashObjectTask(path74, write === true),
36094
36532
  trailingFunctionArgument(arguments)
36095
36533
  );
36096
36534
  }
@@ -36443,8 +36881,8 @@ var init_branch = __esm2({
36443
36881
  }
36444
36882
  });
36445
36883
  function toPath(input) {
36446
- const path73 = input.trim().replace(/^["']|["']$/g, "");
36447
- return path73 && normalize3(path73);
36884
+ const path74 = input.trim().replace(/^["']|["']$/g, "");
36885
+ return path74 && normalize3(path74);
36448
36886
  }
36449
36887
  var parseCheckIgnore;
36450
36888
  var init_CheckIgnore = __esm2({
@@ -36758,8 +37196,8 @@ __export2(sub_module_exports, {
36758
37196
  subModuleTask: () => subModuleTask,
36759
37197
  updateSubModuleTask: () => updateSubModuleTask
36760
37198
  });
36761
- function addSubModuleTask(repo, path73) {
36762
- return subModuleTask(["add", repo, path73]);
37199
+ function addSubModuleTask(repo, path74) {
37200
+ return subModuleTask(["add", repo, path74]);
36763
37201
  }
36764
37202
  function initSubModuleTask(customArgs) {
36765
37203
  return subModuleTask(["init", ...customArgs]);
@@ -37092,8 +37530,8 @@ var require_git = __commonJS2({
37092
37530
  }
37093
37531
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
37094
37532
  };
37095
- Git2.prototype.submoduleAdd = function(repo, path73, then) {
37096
- return this._runTask(addSubModuleTask2(repo, path73), trailingFunctionArgument2(arguments));
37533
+ Git2.prototype.submoduleAdd = function(repo, path74, then) {
37534
+ return this._runTask(addSubModuleTask2(repo, path74), trailingFunctionArgument2(arguments));
37097
37535
  };
37098
37536
  Git2.prototype.submoduleUpdate = function(args, then) {
37099
37537
  return this._runTask(
@@ -37829,8 +38267,8 @@ function parseNumstatEntries(lines) {
37829
38267
  }
37830
38268
  function parseNumstat(lines) {
37831
38269
  const m = /* @__PURE__ */ new Map();
37832
- for (const [path73, entry] of parseNumstatEntries(lines)) {
37833
- m.set(path73, { additions: entry.additions, deletions: entry.deletions });
38270
+ for (const [path74, entry] of parseNumstatEntries(lines)) {
38271
+ m.set(path74, { additions: entry.additions, deletions: entry.deletions });
37834
38272
  }
37835
38273
  return m;
37836
38274
  }
@@ -39137,18 +39575,32 @@ import * as fs27 from "node:fs";
39137
39575
  // src/git/worktrees/worktree-remove.ts
39138
39576
  import * as fs26 from "node:fs";
39139
39577
 
39578
+ // src/worktrees/is-removable-session-worktree-checkout-path.ts
39579
+ import path42 from "node:path";
39580
+ function isBridgeRootCheckoutPath(checkoutPath) {
39581
+ return normalizeResolvedPath(checkoutPath) === normalizeResolvedPath(getBridgeRoot());
39582
+ }
39583
+ function isRemovableSessionWorktreeCheckoutPath(checkoutPath, worktreesRootPath) {
39584
+ if (isBridgeRootCheckoutPath(checkoutPath)) return false;
39585
+ const normalized = normalizeResolvedPath(checkoutPath);
39586
+ const worktreesRoot = normalizeResolvedPath(worktreesRootPath);
39587
+ const rel = path42.relative(worktreesRoot, normalized);
39588
+ if (rel === "" || rel.startsWith("..") || path42.isAbsolute(rel)) return false;
39589
+ return true;
39590
+ }
39591
+
39140
39592
  // src/git/worktrees/resolve-main-repo-from-git-file.ts
39141
39593
  import * as fs25 from "node:fs";
39142
- import * as path42 from "node:path";
39594
+ import * as path43 from "node:path";
39143
39595
  function resolveMainRepoFromWorktreeGitFile(wt) {
39144
- const gitDirFile = path42.join(wt, ".git");
39596
+ const gitDirFile = path43.join(wt, ".git");
39145
39597
  if (!fs25.existsSync(gitDirFile) || !fs25.statSync(gitDirFile).isFile()) return "";
39146
39598
  const first2 = fs25.readFileSync(gitDirFile, "utf8").trim();
39147
39599
  const m = first2.match(/^gitdir:\s*(.+)$/im);
39148
39600
  if (!m) return "";
39149
- const gitWorktreePath = path42.resolve(wt, m[1].trim());
39150
- const gitDir = path42.dirname(path42.dirname(gitWorktreePath));
39151
- return path42.dirname(gitDir);
39601
+ const gitWorktreePath = path43.resolve(wt, m[1].trim());
39602
+ const gitDir = path43.dirname(path43.dirname(gitWorktreePath));
39603
+ return path43.dirname(gitDir);
39152
39604
  }
39153
39605
 
39154
39606
  // src/git/worktrees/worktree-remove.ts
@@ -39156,32 +39608,44 @@ async function gitWorktreeRemoveForce(worktreePath) {
39156
39608
  const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
39157
39609
  if (mainRepo) {
39158
39610
  await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
39159
- } else {
39160
- fs26.rmSync(worktreePath, { recursive: true, force: true });
39611
+ return true;
39161
39612
  }
39613
+ if (isBridgeRootCheckoutPath(worktreePath)) return false;
39614
+ fs26.rmSync(worktreePath, { recursive: true, force: true });
39615
+ return true;
39162
39616
  }
39163
39617
 
39164
39618
  // src/worktrees/remove-session-worktrees.ts
39165
- async function removeSessionWorktrees(paths, log2) {
39619
+ async function removeSessionWorktrees(paths, worktreesRootPath, log2) {
39166
39620
  for (const wt of paths) {
39621
+ if (!isRemovableSessionWorktreeCheckoutPath(wt, worktreesRootPath)) {
39622
+ log2(`[worktrees] Skipping removal of ${wt} (bridge root or outside worktrees root).`);
39623
+ continue;
39624
+ }
39167
39625
  try {
39168
- await gitWorktreeRemoveForce(wt);
39169
- log2(`[worktrees] Removed worktree ${wt}`);
39626
+ const removed = await gitWorktreeRemoveForce(wt);
39627
+ if (removed) {
39628
+ log2(`[worktrees] Removed worktree ${wt}`);
39629
+ } else {
39630
+ log2(`[worktrees] Skipping removal of ${wt} (bridge root).`);
39631
+ }
39170
39632
  } catch (e) {
39171
39633
  log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
39172
- try {
39173
- fs27.rmSync(wt, { recursive: true, force: true });
39174
- } catch {
39634
+ if (isRemovableSessionWorktreeCheckoutPath(wt, worktreesRootPath)) {
39635
+ try {
39636
+ fs27.rmSync(wt, { recursive: true, force: true });
39637
+ } catch {
39638
+ }
39175
39639
  }
39176
39640
  }
39177
39641
  }
39178
39642
  }
39179
39643
 
39180
39644
  // src/worktrees/manager/git/remove-session-worktree-checkouts.ts
39181
- async function removeSessionWorktreeCheckouts(cache2, sessionId, log2) {
39645
+ async function removeSessionWorktreeCheckouts(cache2, sessionId, worktreesRootPath, log2) {
39182
39646
  const paths = cache2.clearSession(sessionId);
39183
39647
  if (!paths?.length) return;
39184
- await removeSessionWorktrees(paths, log2);
39648
+ await removeSessionWorktrees(paths, worktreesRootPath, log2);
39185
39649
  }
39186
39650
 
39187
39651
  // src/git/branches/rename-branch.ts
@@ -39214,11 +39678,11 @@ async function renameSessionWorktreeBranch(cache2, sessionId, newBranch, log2) {
39214
39678
 
39215
39679
  // src/worktrees/discovery/discover-session-worktree-on-disk.ts
39216
39680
  import * as fs31 from "node:fs";
39217
- import * as path47 from "node:path";
39681
+ import * as path48 from "node:path";
39218
39682
 
39219
39683
  // src/worktrees/discovery/collect-worktree-paths.ts
39220
39684
  import * as fs29 from "node:fs";
39221
- import * as path44 from "node:path";
39685
+ import * as path45 from "node:path";
39222
39686
 
39223
39687
  // src/worktrees/discovery/disk-walk-constants.ts
39224
39688
  var DISK_WALK_YIELD_EVERY = 64;
@@ -39247,7 +39711,7 @@ function shouldSkipDiskWalkEntry(name) {
39247
39711
 
39248
39712
  // src/worktrees/discovery/disk-walk-utils.ts
39249
39713
  import * as fs28 from "node:fs";
39250
- import * as path43 from "node:path";
39714
+ import * as path44 from "node:path";
39251
39715
  async function yieldDuringDiskWalk(state) {
39252
39716
  state.entries++;
39253
39717
  if (state.entries % DISK_WALK_YIELD_EVERY !== 0) return true;
@@ -39257,7 +39721,7 @@ async function yieldDuringDiskWalk(state) {
39257
39721
  }
39258
39722
  async function isGitDir(dirPath) {
39259
39723
  try {
39260
- await fs28.promises.access(path43.join(dirPath, ".git"));
39724
+ await fs28.promises.access(path44.join(dirPath, ".git"));
39261
39725
  return true;
39262
39726
  } catch {
39263
39727
  return false;
@@ -39271,7 +39735,7 @@ async function collectGitRepoRootsUnderDirectory(rootPath) {
39271
39735
  const walk = async (dir) => {
39272
39736
  if (!await yieldDuringDiskWalk(state)) return;
39273
39737
  if (await isGitDir(dir)) {
39274
- out.push(path44.resolve(dir));
39738
+ out.push(path45.resolve(dir));
39275
39739
  return;
39276
39740
  }
39277
39741
  let entries;
@@ -39282,12 +39746,12 @@ async function collectGitRepoRootsUnderDirectory(rootPath) {
39282
39746
  }
39283
39747
  for (const e of entries) {
39284
39748
  if (shouldSkipDiskWalkEntry(e.name)) continue;
39285
- const full = path44.join(dir, e.name);
39749
+ const full = path45.join(dir, e.name);
39286
39750
  if (!e.isDirectory()) continue;
39287
39751
  await walk(full);
39288
39752
  }
39289
39753
  };
39290
- await walk(path44.resolve(rootPath));
39754
+ await walk(path45.resolve(rootPath));
39291
39755
  return { paths: [...new Set(out)], entriesVisited: state.entries };
39292
39756
  }
39293
39757
  async function collectWorktreeRootsNamed(root, sessionId, maxDepth = LEGACY_WALK_MAX_DEPTH) {
@@ -39304,10 +39768,10 @@ async function collectWorktreeRootsNamed(root, sessionId, maxDepth = LEGACY_WALK
39304
39768
  }
39305
39769
  for (const e of entries) {
39306
39770
  if (shouldSkipDiskWalkEntry(e.name)) continue;
39307
- const full = path44.join(dir, e.name);
39771
+ const full = path45.join(dir, e.name);
39308
39772
  if (!e.isDirectory()) continue;
39309
39773
  if (e.name === sessionId) {
39310
- if (await isGitDir(full)) out.push(path44.resolve(full));
39774
+ if (await isGitDir(full)) out.push(path45.resolve(full));
39311
39775
  continue;
39312
39776
  }
39313
39777
  if (await isGitDir(full)) continue;
@@ -39319,14 +39783,14 @@ async function collectWorktreeRootsNamed(root, sessionId, maxDepth = LEGACY_WALK
39319
39783
  }
39320
39784
 
39321
39785
  // src/worktrees/discovery/layout-keys-for-bridge-root.ts
39322
- import * as path45 from "node:path";
39786
+ import * as path46 from "node:path";
39323
39787
  function layoutKeysForBridgeRoot(layout, bridgeRoot, keysOnDisk) {
39324
- const bridge = path45.resolve(bridgeRoot);
39788
+ const bridge = path46.resolve(bridgeRoot);
39325
39789
  const preferred = getLauncherDirNameIfPresent(layout, bridgeRoot);
39326
39790
  const relevant = layout.launcherCwds.filter((e) => {
39327
- const entry = path45.resolve(e.absolutePath);
39328
- return bridge === entry || bridge.startsWith(entry + path45.sep) || entry.startsWith(bridge + path45.sep);
39329
- }).sort((a, b) => path45.resolve(b.absolutePath).length - path45.resolve(a.absolutePath).length).map((e) => e.dirName).filter((name) => keysOnDisk.includes(name));
39791
+ const entry = path46.resolve(e.absolutePath);
39792
+ return bridge === entry || bridge.startsWith(entry + path46.sep) || entry.startsWith(bridge + path46.sep);
39793
+ }).sort((a, b) => path46.resolve(b.absolutePath).length - path46.resolve(a.absolutePath).length).map((e) => e.dirName).filter((name) => keysOnDisk.includes(name));
39330
39794
  const ordered = [];
39331
39795
  const seen = /* @__PURE__ */ new Set();
39332
39796
  const add = (k) => {
@@ -39345,7 +39809,7 @@ function layoutKeysForBridgeRoot(layout, bridgeRoot, keysOnDisk) {
39345
39809
 
39346
39810
  // src/worktrees/discovery/try-binding-from-session-directory.ts
39347
39811
  import * as fs30 from "node:fs";
39348
- import * as path46 from "node:path";
39812
+ import * as path47 from "node:path";
39349
39813
  async function tryBindingFromSessionDirectory(sessionDir) {
39350
39814
  let st;
39351
39815
  try {
@@ -39356,7 +39820,7 @@ async function tryBindingFromSessionDirectory(sessionDir) {
39356
39820
  if (!st.isDirectory()) return null;
39357
39821
  const { paths: worktreePaths } = await collectGitRepoRootsUnderDirectory(sessionDir);
39358
39822
  if (worktreePaths.length === 0) return null;
39359
- const abs = path46.resolve(sessionDir);
39823
+ const abs = path47.resolve(sessionDir);
39360
39824
  return {
39361
39825
  sessionParentPath: abs,
39362
39826
  workingTreeRelRoot: abs,
@@ -39380,7 +39844,7 @@ async function discoverSessionWorktreeOnDisk(options) {
39380
39844
  try {
39381
39845
  for (const name of await fs31.promises.readdir(worktreesRootPath)) {
39382
39846
  if (name.startsWith(".")) continue;
39383
- const p = path47.join(worktreesRootPath, name);
39847
+ const p = path48.join(worktreesRootPath, name);
39384
39848
  let st;
39385
39849
  try {
39386
39850
  st = await fs31.promises.stat(p);
@@ -39397,7 +39861,7 @@ async function discoverSessionWorktreeOnDisk(options) {
39397
39861
  for (const key of keys) {
39398
39862
  if (isCliImmediateShutdownRequested()) return null;
39399
39863
  await yieldToEventLoop();
39400
- const layoutRoot = path47.join(worktreesRootPath, key);
39864
+ const layoutRoot = path48.join(worktreesRootPath, key);
39401
39865
  let layoutSt;
39402
39866
  try {
39403
39867
  layoutSt = await fs31.promises.stat(layoutRoot);
@@ -39405,16 +39869,16 @@ async function discoverSessionWorktreeOnDisk(options) {
39405
39869
  continue;
39406
39870
  }
39407
39871
  if (!layoutSt.isDirectory()) continue;
39408
- const sessionDir = path47.join(layoutRoot, sid);
39872
+ const sessionDir = path48.join(layoutRoot, sid);
39409
39873
  const nested = await tryBindingFromSessionDirectory(sessionDir);
39410
39874
  if (nested) return nested;
39411
39875
  const legacy = await collectWorktreeRootsNamed(layoutRoot, sid);
39412
39876
  if (legacy.paths.length > 0) {
39413
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacy.paths) ?? path47.resolve(legacy.paths[0]);
39877
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacy.paths) ?? path48.resolve(legacy.paths[0]);
39414
39878
  return {
39415
- sessionParentPath: path47.resolve(isolated),
39416
- workingTreeRelRoot: path47.resolve(layoutRoot),
39417
- repoCheckoutPaths: legacy.paths.map((p) => path47.resolve(p))
39879
+ sessionParentPath: path48.resolve(isolated),
39880
+ workingTreeRelRoot: path48.resolve(layoutRoot),
39881
+ repoCheckoutPaths: legacy.paths.map((p) => path48.resolve(p))
39418
39882
  };
39419
39883
  }
39420
39884
  }
@@ -39423,29 +39887,29 @@ async function discoverSessionWorktreeOnDisk(options) {
39423
39887
 
39424
39888
  // src/worktrees/discovery/discover-session-worktrees-under-session-worktree-root.ts
39425
39889
  import * as fs32 from "node:fs";
39426
- import * as path49 from "node:path";
39890
+ import * as path50 from "node:path";
39427
39891
 
39428
39892
  // src/worktrees/discovery/discover-legacy-binding-ascending-from-checkout.ts
39429
- import * as path48 from "node:path";
39893
+ import * as path49 from "node:path";
39430
39894
  async function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
39431
39895
  const sid = sessionId.trim();
39432
39896
  if (!sid) return null;
39433
- const hintR = path48.resolve(checkoutPath);
39897
+ const hintR = path49.resolve(checkoutPath);
39434
39898
  let best = null;
39435
- let cur = path48.dirname(hintR);
39899
+ let cur = path49.dirname(hintR);
39436
39900
  for (let i = 0; i < 40; i++) {
39437
39901
  if (isCliImmediateShutdownRequested()) return best;
39438
39902
  await yieldToEventLoop();
39439
39903
  const paths = await collectWorktreeRootsNamed(cur, sid);
39440
- if (paths.paths.some((p) => path48.resolve(p) === hintR)) {
39441
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths.paths) ?? path48.resolve(paths.paths[0]);
39904
+ if (paths.paths.some((p) => path49.resolve(p) === hintR)) {
39905
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths.paths) ?? path49.resolve(paths.paths[0]);
39442
39906
  best = {
39443
- sessionParentPath: path48.resolve(isolated),
39444
- workingTreeRelRoot: path48.resolve(cur),
39445
- repoCheckoutPaths: paths.paths.map((p) => path48.resolve(p))
39907
+ sessionParentPath: path49.resolve(isolated),
39908
+ workingTreeRelRoot: path49.resolve(cur),
39909
+ repoCheckoutPaths: paths.paths.map((p) => path49.resolve(p))
39446
39910
  };
39447
39911
  }
39448
- const next = path48.dirname(cur);
39912
+ const next = path49.dirname(cur);
39449
39913
  if (next === cur) break;
39450
39914
  cur = next;
39451
39915
  }
@@ -39456,12 +39920,12 @@ async function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPat
39456
39920
  async function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
39457
39921
  const sid = sessionId.trim();
39458
39922
  if (!sid) return null;
39459
- const hint = path49.resolve(sessionWorktreeRootPathOrHint);
39460
- const underHint = await tryBindingFromSessionDirectory(path49.join(hint, sid));
39923
+ const hint = path50.resolve(sessionWorktreeRootPathOrHint);
39924
+ const underHint = await tryBindingFromSessionDirectory(path50.join(hint, sid));
39461
39925
  if (underHint) return underHint;
39462
39926
  const direct = await tryBindingFromSessionDirectory(hint);
39463
39927
  if (direct) {
39464
- if (path49.basename(hint) === sid && await isGitDir(hint)) {
39928
+ if (path50.basename(hint) === sid && await isGitDir(hint)) {
39465
39929
  const legacyFromCheckout = await discoverLegacyBindingAscendingFromCheckout(sid, hint);
39466
39930
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
39467
39931
  return legacyFromCheckout;
@@ -39469,7 +39933,7 @@ async function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeR
39469
39933
  }
39470
39934
  return direct;
39471
39935
  }
39472
- if (path49.basename(hint) === sid && await isGitDir(hint)) {
39936
+ if (path50.basename(hint) === sid && await isGitDir(hint)) {
39473
39937
  const legacyFromCheckout = await discoverLegacyBindingAscendingFromCheckout(sid, hint);
39474
39938
  if (legacyFromCheckout) return legacyFromCheckout;
39475
39939
  }
@@ -39482,11 +39946,11 @@ async function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeR
39482
39946
  if (!st.isDirectory()) return null;
39483
39947
  const legacyPaths = await collectWorktreeRootsNamed(hint, sid);
39484
39948
  if (legacyPaths.paths.length === 0) return null;
39485
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths.paths) ?? path49.resolve(legacyPaths.paths[0]);
39949
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths.paths) ?? path50.resolve(legacyPaths.paths[0]);
39486
39950
  return {
39487
- sessionParentPath: path49.resolve(isolated),
39951
+ sessionParentPath: path50.resolve(isolated),
39488
39952
  workingTreeRelRoot: hint,
39489
- repoCheckoutPaths: legacyPaths.paths.map((p) => path49.resolve(p))
39953
+ repoCheckoutPaths: legacyPaths.paths.map((p) => path50.resolve(p))
39490
39954
  };
39491
39955
  }
39492
39956
 
@@ -39548,11 +40012,11 @@ function parseSessionParent(v) {
39548
40012
 
39549
40013
  // src/worktrees/prepare-new-session-worktrees.ts
39550
40014
  import * as fs34 from "node:fs";
39551
- import * as path51 from "node:path";
40015
+ import * as path52 from "node:path";
39552
40016
 
39553
40017
  // src/git/discover-repos.ts
39554
40018
  import * as fs33 from "node:fs";
39555
- import * as path50 from "node:path";
40019
+ import * as path51 from "node:path";
39556
40020
  var GIT_DISCOVER_YIELD_EVERY = 32;
39557
40021
  async function yieldGitDiscoverWork(entryCount) {
39558
40022
  if (entryCount > 0 && entryCount % GIT_DISCOVER_YIELD_EVERY === 0) {
@@ -39561,7 +40025,7 @@ async function yieldGitDiscoverWork(entryCount) {
39561
40025
  }
39562
40026
  async function discoverGitRepos(cwd = getBridgeRoot()) {
39563
40027
  const result = [];
39564
- const cwdResolved = path50.resolve(cwd);
40028
+ const cwdResolved = path51.resolve(cwd);
39565
40029
  if (await isGitRepoDirectory(cwdResolved)) {
39566
40030
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
39567
40031
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -39576,7 +40040,7 @@ async function discoverGitRepos(cwd = getBridgeRoot()) {
39576
40040
  await yieldGitDiscoverWork(i + 1);
39577
40041
  const ent = entries[i];
39578
40042
  if (!ent.isDirectory()) continue;
39579
- const childPath2 = path50.join(cwdResolved, ent.name);
40043
+ const childPath2 = path51.join(cwdResolved, ent.name);
39580
40044
  if (await isGitRepoDirectory(childPath2)) {
39581
40045
  const remoteUrl = await getRemoteOriginUrl(childPath2);
39582
40046
  result.push({ absolutePath: childPath2, remoteUrl });
@@ -39585,12 +40049,12 @@ async function discoverGitRepos(cwd = getBridgeRoot()) {
39585
40049
  return result;
39586
40050
  }
39587
40051
  async function discoverGitReposUnderRoot(rootPath) {
39588
- const root = path50.resolve(rootPath);
40052
+ const root = path51.resolve(rootPath);
39589
40053
  const roots = [];
39590
40054
  let walkEntries = 0;
39591
40055
  async function walk(dir) {
39592
40056
  if (await isGitRepoDirectory(dir)) {
39593
- roots.push(path50.resolve(dir));
40057
+ roots.push(path51.resolve(dir));
39594
40058
  return;
39595
40059
  }
39596
40060
  let entries;
@@ -39603,7 +40067,7 @@ async function discoverGitReposUnderRoot(rootPath) {
39603
40067
  await yieldGitDiscoverWork(++walkEntries);
39604
40068
  const ent = entries[i];
39605
40069
  if (!ent.isDirectory() || ent.name === ".git") continue;
39606
- await walk(path50.join(dir, ent.name));
40070
+ await walk(path51.join(dir, ent.name));
39607
40071
  }
39608
40072
  }
39609
40073
  await walk(root);
@@ -39638,10 +40102,10 @@ function resolveBaseRefForRepo(relNorm, baseBranches) {
39638
40102
  }
39639
40103
  async function prepareNewSessionWorktrees(options) {
39640
40104
  const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
39641
- const bridgeResolved = path51.resolve(bridgeRoot);
40105
+ const bridgeResolved = path52.resolve(bridgeRoot);
39642
40106
  const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
39643
- const bridgeKeyDir = path51.join(worktreesRootPath, cwdKey);
39644
- const sessionDir = path51.join(bridgeKeyDir, sessionId);
40107
+ const bridgeKeyDir = path52.join(worktreesRootPath, cwdKey);
40108
+ const sessionDir = path52.join(bridgeKeyDir, sessionId);
39645
40109
  const repos = await discoverGitReposUnderRoot(bridgeResolved);
39646
40110
  if (repos.length === 0) {
39647
40111
  log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
@@ -39651,12 +40115,12 @@ async function prepareNewSessionWorktrees(options) {
39651
40115
  const worktreePaths = [];
39652
40116
  fs34.mkdirSync(sessionDir, { recursive: true });
39653
40117
  for (const repo of repos) {
39654
- let rel = path51.relative(bridgeResolved, repo.absolutePath);
39655
- if (rel.startsWith("..") || path51.isAbsolute(rel)) continue;
40118
+ let rel = path52.relative(bridgeResolved, repo.absolutePath);
40119
+ if (rel.startsWith("..") || path52.isAbsolute(rel)) continue;
39656
40120
  const relNorm = normalizeRepoRelPath2(rel === "" ? "." : rel);
39657
- const wtPath = relNorm === "." ? sessionDir : path51.join(sessionDir, relNorm);
40121
+ const wtPath = relNorm === "." ? sessionDir : path52.join(sessionDir, relNorm);
39658
40122
  if (relNorm !== ".") {
39659
- fs34.mkdirSync(path51.dirname(wtPath), { recursive: true });
40123
+ fs34.mkdirSync(path52.dirname(wtPath), { recursive: true });
39660
40124
  }
39661
40125
  const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
39662
40126
  try {
@@ -39709,9 +40173,9 @@ async function resolveExistingSessionParentPath(sessionId, cache2, discover) {
39709
40173
  }
39710
40174
 
39711
40175
  // src/worktrees/manager/resolve-explicit-session-parent-path.ts
39712
- import * as path52 from "node:path";
40176
+ import * as path53 from "node:path";
39713
40177
  async function resolveExplicitSessionParentPath(params) {
39714
- const resolved = path52.resolve(params.parentPathRaw);
40178
+ const resolved = path53.resolve(params.parentPathRaw);
39715
40179
  if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
39716
40180
  return resolved;
39717
40181
  }
@@ -39728,7 +40192,7 @@ async function resolveExplicitSessionParentPath(params) {
39728
40192
  await yieldToEventLoop();
39729
40193
  const tryRoot = await discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
39730
40194
  if (tryRoot) return rememberAndReturn(tryRoot);
39731
- const next = path52.dirname(cur);
40195
+ const next = path53.dirname(cur);
39732
40196
  if (next === cur) break;
39733
40197
  cur = next;
39734
40198
  }
@@ -39838,7 +40302,7 @@ function createSessionWorktreeGitApi(ctx) {
39838
40302
  await renameSessionWorktreeBranch(ctx.cache, sessionId, newBranch, ctx.log);
39839
40303
  },
39840
40304
  async removeSessionWorktrees(sessionId) {
39841
- await removeSessionWorktreeCheckouts(ctx.cache, sessionId, ctx.log);
40305
+ await removeSessionWorktreeCheckouts(ctx.cache, sessionId, ctx.worktreesRootPath, ctx.log);
39842
40306
  },
39843
40307
  async commitSession(params) {
39844
40308
  return commitSessionWorktree(ctx.cache, params);
@@ -39867,17 +40331,17 @@ var SessionWorktreeManager = class {
39867
40331
  };
39868
40332
 
39869
40333
  // src/worktrees/manager/default-worktrees-root-path.ts
39870
- import * as path53 from "node:path";
40334
+ import * as path54 from "node:path";
39871
40335
  import os9 from "node:os";
39872
40336
  function defaultWorktreesRootPath() {
39873
- return path53.join(os9.homedir(), ".buildautomaton", "worktrees");
40337
+ return path54.join(os9.homedir(), ".buildautomaton", "worktrees");
39874
40338
  }
39875
40339
 
39876
40340
  // src/files/watch-file-index.ts
39877
- import path58 from "node:path";
40341
+ import path59 from "node:path";
39878
40342
 
39879
40343
  // src/files/index/build-file-index.ts
39880
- import path54 from "node:path";
40344
+ import path55 from "node:path";
39881
40345
 
39882
40346
  // src/files/index/file-index-sqlite-lock.ts
39883
40347
  var fileIndexChain = Promise.resolve();
@@ -39968,7 +40432,7 @@ function assertNotShutdown2() {
39968
40432
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
39969
40433
  }
39970
40434
  async function buildFileIndexAsync(cwd) {
39971
- const resolved = path54.resolve(cwd);
40435
+ const resolved = path55.resolve(cwd);
39972
40436
  await yieldToEventLoop();
39973
40437
  assertNotShutdown2();
39974
40438
  const paths = await collectWorkspacePathsAsync(resolved);
@@ -39981,7 +40445,7 @@ async function buildFileIndexAsync(cwd) {
39981
40445
  }
39982
40446
 
39983
40447
  // src/files/index/ensure-file-index.ts
39984
- import path55 from "node:path";
40448
+ import path56 from "node:path";
39985
40449
 
39986
40450
  // src/files/index/search-file-index.ts
39987
40451
  function escapeLikePattern(fragment) {
@@ -40033,7 +40497,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
40033
40497
 
40034
40498
  // src/files/index/ensure-file-index.ts
40035
40499
  async function ensureFileIndexAsync(cwd) {
40036
- const resolved = path55.resolve(cwd);
40500
+ const resolved = path56.resolve(cwd);
40037
40501
  if (await bridgeFileIndexIsPopulated(resolved)) {
40038
40502
  return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
40039
40503
  }
@@ -40082,10 +40546,10 @@ function createFileIndexFsWatcher(resolved, schedule) {
40082
40546
 
40083
40547
  // src/files/index/file-index-incremental-symbol-updates.ts
40084
40548
  import fs35 from "node:fs";
40085
- import path56 from "node:path";
40549
+ import path57 from "node:path";
40086
40550
  async function applyIncrementalSymbolIndexUpdates(resolved, relPaths) {
40087
40551
  for (const rel of relPaths) {
40088
- const absPath = path56.join(resolved, rel);
40552
+ const absPath = path57.join(resolved, rel);
40089
40553
  try {
40090
40554
  const stat2 = await fs35.promises.stat(absPath);
40091
40555
  if (stat2.isFile()) {
@@ -40105,9 +40569,9 @@ async function applyIncrementalSymbolIndexUpdates(resolved, relPaths) {
40105
40569
  }
40106
40570
 
40107
40571
  // src/files/index/file-index-initial-builds.ts
40108
- import path57 from "node:path";
40572
+ import path58 from "node:path";
40109
40573
  async function runInitialIndexBuilds(cwd = getBridgeRoot()) {
40110
- const resolved = path57.resolve(cwd);
40574
+ const resolved = path58.resolve(cwd);
40111
40575
  try {
40112
40576
  await buildFileIndexAsync(resolved);
40113
40577
  await buildSymbolIndexAsync(resolved);
@@ -40125,7 +40589,7 @@ function scheduleInitialIndexBuilds(cwd = getBridgeRoot()) {
40125
40589
  // src/files/watch-file-index.ts
40126
40590
  var MAX_INCREMENTAL_SYMBOL_UPDATES = 512;
40127
40591
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
40128
- const resolved = path58.resolve(cwd);
40592
+ const resolved = path59.resolve(cwd);
40129
40593
  let timer = null;
40130
40594
  const pendingSymbolUpdates = /* @__PURE__ */ new Set();
40131
40595
  let needsFullSymbolRebuild = false;
@@ -40187,7 +40651,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
40187
40651
  }
40188
40652
 
40189
40653
  // src/connection/create-bridge-connection.ts
40190
- import * as path72 from "node:path";
40654
+ import * as path73 from "node:path";
40191
40655
 
40192
40656
  // src/dev-servers/manager/dev-server-manager.ts
40193
40657
  import { rm as rm2 } from "node:fs/promises";
@@ -40497,10 +40961,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
40497
40961
  import { spawn as spawn8 } from "node:child_process";
40498
40962
  import fs38 from "node:fs";
40499
40963
  import { tmpdir } from "node:os";
40500
- import path59 from "node:path";
40964
+ import path60 from "node:path";
40501
40965
  function trySpawnMergedLogFile(command, env, cwd, signal) {
40502
- const tmpRoot = fs38.mkdtempSync(path59.join(tmpdir(), "ba-devsrv-log-"));
40503
- const logPath = path59.join(tmpRoot, "combined.log");
40966
+ const tmpRoot = fs38.mkdtempSync(path60.join(tmpdir(), "ba-devsrv-log-"));
40967
+ const logPath = path60.join(tmpRoot, "combined.log");
40504
40968
  let logFd;
40505
40969
  try {
40506
40970
  logFd = fs38.openSync(logPath, "a");
@@ -40544,15 +41008,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
40544
41008
  import { spawn as spawn9 } from "node:child_process";
40545
41009
  import fs39 from "node:fs";
40546
41010
  import { tmpdir as tmpdir2 } from "node:os";
40547
- import path60 from "node:path";
41011
+ import path61 from "node:path";
40548
41012
  function shSingleQuote(s) {
40549
41013
  return `'${s.replace(/'/g, `'\\''`)}'`;
40550
41014
  }
40551
41015
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
40552
- const tmpRoot = fs39.mkdtempSync(path60.join(tmpdir2(), "ba-devsrv-sh-"));
40553
- const logPath = path60.join(tmpRoot, "combined.log");
40554
- const innerPath = path60.join(tmpRoot, "_cmd.sh");
40555
- const runnerPath = path60.join(tmpRoot, "_run.sh");
41016
+ const tmpRoot = fs39.mkdtempSync(path61.join(tmpdir2(), "ba-devsrv-sh-"));
41017
+ const logPath = path61.join(tmpRoot, "combined.log");
41018
+ const innerPath = path61.join(tmpRoot, "_cmd.sh");
41019
+ const runnerPath = path61.join(tmpRoot, "_run.sh");
40556
41020
  try {
40557
41021
  fs39.writeFileSync(innerPath, `#!/bin/sh
40558
41022
  ${command}
@@ -40583,9 +41047,9 @@ cd ${shSingleQuote(cwd)}
40583
41047
  }
40584
41048
  }
40585
41049
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
40586
- const tmpRoot = fs39.mkdtempSync(path60.join(tmpdir2(), "ba-devsrv-sh-"));
40587
- const logPath = path60.join(tmpRoot, "combined.log");
40588
- const runnerPath = path60.join(tmpRoot, "_run.bat");
41050
+ const tmpRoot = fs39.mkdtempSync(path61.join(tmpdir2(), "ba-devsrv-sh-"));
41051
+ const logPath = path61.join(tmpRoot, "combined.log");
41052
+ const runnerPath = path61.join(tmpRoot, "_run.bat");
40589
41053
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
40590
41054
  const com = process.env.ComSpec || "cmd.exe";
40591
41055
  try {
@@ -41367,15 +41831,15 @@ async function yieldSkillDiscoveryWork(entryCount) {
41367
41831
 
41368
41832
  // src/skills/discovery/discover-local-skills.ts
41369
41833
  import fs40 from "node:fs";
41370
- import path61 from "node:path";
41834
+ import path62 from "node:path";
41371
41835
  function collectSkillFromDir(rel, base, name, seenKeys, out) {
41372
- const dir = path61.join(base, name);
41836
+ const dir = path62.join(base, name);
41373
41837
  try {
41374
41838
  if (!fs40.statSync(dir).isDirectory()) return;
41375
41839
  } catch {
41376
41840
  return;
41377
41841
  }
41378
- const skillMd = path61.join(dir, "SKILL.md");
41842
+ const skillMd = path62.join(dir, "SKILL.md");
41379
41843
  if (!fs40.existsSync(skillMd)) return;
41380
41844
  const key = `${rel}/${name}`;
41381
41845
  if (seenKeys.has(key)) return;
@@ -41388,7 +41852,7 @@ async function discoverLocalSkillsAsync(cwd) {
41388
41852
  let work = 0;
41389
41853
  for (const rel of SKILL_DISCOVERY_ROOTS) {
41390
41854
  await yieldSkillDiscoveryWork(++work);
41391
- const base = path61.join(cwd, rel);
41855
+ const base = path62.join(cwd, rel);
41392
41856
  if (!fs40.existsSync(base) || !fs40.statSync(base).isDirectory()) continue;
41393
41857
  let entries = [];
41394
41858
  try {
@@ -41406,15 +41870,15 @@ async function discoverLocalSkillsAsync(cwd) {
41406
41870
 
41407
41871
  // src/skills/discovery/discover-skill-layout-roots.ts
41408
41872
  import fs41 from "node:fs";
41409
- import path62 from "node:path";
41873
+ import path63 from "node:path";
41410
41874
  function collectLayoutSkill(rel, base, name, skills2) {
41411
- const dir = path62.join(base, name);
41875
+ const dir = path63.join(base, name);
41412
41876
  try {
41413
41877
  if (!fs41.statSync(dir).isDirectory()) return;
41414
41878
  } catch {
41415
41879
  return;
41416
41880
  }
41417
- if (!fs41.existsSync(path62.join(dir, "SKILL.md"))) return;
41881
+ if (!fs41.existsSync(path63.join(dir, "SKILL.md"))) return;
41418
41882
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
41419
41883
  skills2.push({ name, relPath });
41420
41884
  }
@@ -41423,7 +41887,7 @@ async function discoverSkillLayoutRootsAsync(cwd) {
41423
41887
  let work = 0;
41424
41888
  for (const rel of SKILL_DISCOVERY_ROOTS) {
41425
41889
  await yieldSkillDiscoveryWork(++work);
41426
- const base = path62.join(cwd, rel);
41890
+ const base = path63.join(cwd, rel);
41427
41891
  if (!fs41.existsSync(base) || !fs41.statSync(base).isDirectory()) continue;
41428
41892
  let entries = [];
41429
41893
  try {
@@ -41775,7 +42239,7 @@ import fs43 from "node:fs";
41775
42239
 
41776
42240
  // src/git/snapshot/capture.ts
41777
42241
  import * as fs42 from "node:fs";
41778
- import * as path63 from "node:path";
42242
+ import * as path64 from "node:path";
41779
42243
 
41780
42244
  // src/git/snapshot/git.ts
41781
42245
  async function gitStashCreate(repoRoot, log2) {
@@ -41815,7 +42279,7 @@ async function gitRun(repoRoot, args, log2, label) {
41815
42279
  async function resolveSnapshotRepoRoots(options) {
41816
42280
  const { worktreePaths, fallbackCwd, sessionId, log: log2 } = options;
41817
42281
  if (worktreePaths?.length) {
41818
- const uniq = [...new Set(worktreePaths.map((p) => path63.resolve(p)))];
42282
+ const uniq = [...new Set(worktreePaths.map((p) => path64.resolve(p)))];
41819
42283
  return uniq;
41820
42284
  }
41821
42285
  try {
@@ -41823,7 +42287,7 @@ async function resolveSnapshotRepoRoots(options) {
41823
42287
  const mapped = repos.map((r) => r.absolutePath);
41824
42288
  const sid = sessionId?.trim();
41825
42289
  if (sid) {
41826
- const filtered = mapped.filter((root) => path63.basename(root) === sid);
42290
+ const filtered = mapped.filter((root) => path64.basename(root) === sid);
41827
42291
  if (filtered.length > 0) return filtered;
41828
42292
  }
41829
42293
  return mapped;
@@ -41854,7 +42318,7 @@ async function capturePreTurnSnapshot(options) {
41854
42318
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
41855
42319
  repos
41856
42320
  };
41857
- const filePath = path63.join(dir, `${runId}.json`);
42321
+ const filePath = path64.join(dir, `${runId}.json`);
41858
42322
  try {
41859
42323
  fs42.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
41860
42324
  } catch (e) {
@@ -42197,9 +42661,9 @@ function parseChangeSummarySnapshots(raw) {
42197
42661
  for (const item of raw) {
42198
42662
  if (!item || typeof item !== "object") continue;
42199
42663
  const o = item;
42200
- const path73 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
42201
- if (!path73) continue;
42202
- const row = { path: path73 };
42664
+ const path74 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
42665
+ if (!path74) continue;
42666
+ const row = { path: path74 };
42203
42667
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
42204
42668
  if (typeof o.oldText === "string") row.oldText = o.oldText;
42205
42669
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -42278,7 +42742,8 @@ async function runPreambleAndPrompt(params) {
42278
42742
  cloudApiBaseUrl: deps.cloudApiBaseUrl,
42279
42743
  getCloudAccessToken: deps.getCloudAccessToken,
42280
42744
  e2ee: deps.e2ee,
42281
- ...attachments.length > 0 ? { attachments } : {}
42745
+ ...attachments.length > 0 ? { attachments } : {},
42746
+ isNewSession: msg.isNewSession === true
42282
42747
  });
42283
42748
  }
42284
42749
 
@@ -42427,11 +42892,11 @@ async function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId
42427
42892
  import fs45 from "node:fs";
42428
42893
 
42429
42894
  // src/files/ensure-under-cwd.ts
42430
- import path64 from "node:path";
42895
+ import path65 from "node:path";
42431
42896
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
42432
- const normalized = path64.normalize(relativePath).replace(/^(\.\/)+/, "");
42433
- const resolved = path64.resolve(cwd, normalized);
42434
- if (!resolved.startsWith(cwd + path64.sep) && resolved !== cwd) {
42897
+ const normalized = path65.normalize(relativePath).replace(/^(\.\/)+/, "");
42898
+ const resolved = path65.resolve(cwd, normalized);
42899
+ if (!resolved.startsWith(cwd + path65.sep) && resolved !== cwd) {
42435
42900
  return null;
42436
42901
  }
42437
42902
  return resolved;
@@ -42441,11 +42906,11 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
42441
42906
  var LIST_DIR_YIELD_EVERY = 256;
42442
42907
 
42443
42908
  // src/files/list-dir/map-dir-entry.ts
42444
- import path65 from "node:path";
42909
+ import path66 from "node:path";
42445
42910
  import fs44 from "node:fs";
42446
42911
  async function mapDirEntry(d, relativePath, resolved) {
42447
- const entryPath = path65.join(relativePath || ".", d.name).replace(/\\/g, "/");
42448
- const fullPath = path65.join(resolved, d.name);
42912
+ const entryPath = path66.join(relativePath || ".", d.name).replace(/\\/g, "/");
42913
+ const fullPath = path66.join(resolved, d.name);
42449
42914
  let isDir = d.isDirectory();
42450
42915
  if (d.isSymbolicLink()) {
42451
42916
  try {
@@ -42495,13 +42960,13 @@ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
42495
42960
  }
42496
42961
 
42497
42962
  // src/files/handle-file-browser-search.ts
42498
- import path66 from "node:path";
42963
+ import path67 from "node:path";
42499
42964
  var SEARCH_LIMIT = 100;
42500
42965
  function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
42501
42966
  void (async () => {
42502
42967
  await yieldToEventLoop();
42503
42968
  const q = typeof msg.q === "string" ? msg.q : "";
42504
- const sessionParentPath = path66.resolve(
42969
+ const sessionParentPath = path67.resolve(
42505
42970
  sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
42506
42971
  );
42507
42972
  if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
@@ -42711,13 +43176,13 @@ function triggerSymbolIndexBuild(parentPath) {
42711
43176
  }
42712
43177
 
42713
43178
  // src/git/tree/resolve-repo-abs-path.ts
42714
- import * as path67 from "node:path";
43179
+ import * as path68 from "node:path";
42715
43180
  function resolveRepoAbsPathFromBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
42716
- const bridgeResolved = path67.resolve(bridgeRoot);
43181
+ const bridgeResolved = path68.resolve(bridgeRoot);
42717
43182
  const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
42718
- const repoPath = rel === "" ? bridgeResolved : path67.join(bridgeResolved, rel);
42719
- const resolved = path67.resolve(repoPath);
42720
- if (!resolved.startsWith(bridgeResolved + path67.sep) && resolved !== bridgeResolved) {
43183
+ const repoPath = rel === "" ? bridgeResolved : path68.join(bridgeResolved, rel);
43184
+ const resolved = path68.resolve(repoPath);
43185
+ if (!resolved.startsWith(bridgeResolved + path68.sep) && resolved !== bridgeResolved) {
42721
43186
  return null;
42722
43187
  }
42723
43188
  return resolved;
@@ -43500,7 +43965,7 @@ function isValidRemoteSkillInstallItem(item) {
43500
43965
 
43501
43966
  // src/skills/install/install-remote-skills-async.ts
43502
43967
  import fs49 from "node:fs";
43503
- import path68 from "node:path";
43968
+ import path69 from "node:path";
43504
43969
 
43505
43970
  // src/skills/install/constants.ts
43506
43971
  var INSTALL_SKILLS_YIELD_EVERY = 16;
@@ -43511,8 +43976,8 @@ async function writeInstallFileAsync(skillDir, f, filesWritten) {
43511
43976
  if (++filesWritten.count % INSTALL_SKILLS_YIELD_EVERY === 0) {
43512
43977
  await yieldToEventLoop();
43513
43978
  }
43514
- const dest = path68.join(skillDir, f.path);
43515
- await fs49.promises.mkdir(path68.dirname(dest), { recursive: true });
43979
+ const dest = path69.join(skillDir, f.path);
43980
+ await fs49.promises.mkdir(path69.dirname(dest), { recursive: true });
43516
43981
  if (f.text !== void 0) {
43517
43982
  await fs49.promises.writeFile(dest, f.text, "utf8");
43518
43983
  } else if (f.base64) {
@@ -43528,7 +43993,7 @@ async function installRemoteSkillsAsync(cwd, targetDir, items) {
43528
43993
  try {
43529
43994
  for (const item of items) {
43530
43995
  if (!isValidRemoteSkillInstallItem(item)) continue;
43531
- const skillDir = path68.join(cwd, targetDir, item.skillName);
43996
+ const skillDir = path69.join(cwd, targetDir, item.skillName);
43532
43997
  for (const f of item.files) {
43533
43998
  await writeInstallFileAsync(skillDir, f, filesWritten);
43534
43999
  }
@@ -43867,7 +44332,7 @@ var handleDevServersConfig = (msg, deps) => {
43867
44332
  };
43868
44333
 
43869
44334
  // src/git/bridge-git-context.ts
43870
- import * as path69 from "node:path";
44335
+ import * as path70 from "node:path";
43871
44336
 
43872
44337
  // src/git/branches/get-current-branch.ts
43873
44338
  async function getCurrentBranch(repoPath) {
@@ -43917,12 +44382,12 @@ async function listRepoBranchRefs(repoPath) {
43917
44382
  // src/git/bridge-git-context.ts
43918
44383
  function folderNameForRelPath(relPath, bridgeRoot) {
43919
44384
  if (relPath === "." || relPath === "") {
43920
- return path69.basename(path69.resolve(bridgeRoot)) || "repo";
44385
+ return path70.basename(path70.resolve(bridgeRoot)) || "repo";
43921
44386
  }
43922
- return path69.basename(relPath) || relPath;
44387
+ return path70.basename(relPath) || relPath;
43923
44388
  }
43924
44389
  async function discoverGitReposForBridgeContext(bridgeRoot) {
43925
- const root = path69.resolve(bridgeRoot);
44390
+ const root = path70.resolve(bridgeRoot);
43926
44391
  if (await isGitRepoDirectory(root)) {
43927
44392
  const remoteUrl = await getRemoteOriginUrl(root);
43928
44393
  return [{ absolutePath: root, remoteUrl }];
@@ -43930,19 +44395,19 @@ async function discoverGitReposForBridgeContext(bridgeRoot) {
43930
44395
  const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
43931
44396
  const byPath = /* @__PURE__ */ new Map();
43932
44397
  for (const repo of [...deep, ...shallow]) {
43933
- byPath.set(path69.resolve(repo.absolutePath), repo);
44398
+ byPath.set(path70.resolve(repo.absolutePath), repo);
43934
44399
  }
43935
44400
  return [...byPath.values()];
43936
44401
  }
43937
44402
  async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
43938
- const bridgeResolved = path69.resolve(bridgeRoot);
44403
+ const bridgeResolved = path70.resolve(bridgeRoot);
43939
44404
  const repos = await discoverGitReposForBridgeContext(bridgeResolved);
43940
44405
  const rows = [];
43941
44406
  for (let i = 0; i < repos.length; i++) {
43942
44407
  if (i > 0) await yieldToEventLoop();
43943
44408
  const repo = repos[i];
43944
- let rel = path69.relative(bridgeResolved, repo.absolutePath);
43945
- if (rel.startsWith("..") || path69.isAbsolute(rel)) continue;
44409
+ let rel = path70.relative(bridgeResolved, repo.absolutePath);
44410
+ if (rel.startsWith("..") || path70.isAbsolute(rel)) continue;
43946
44411
  const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
43947
44412
  const currentBranch = await getCurrentBranch(repo.absolutePath);
43948
44413
  const remoteUrl = repo.remoteUrl.trim() || null;
@@ -43957,11 +44422,11 @@ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
43957
44422
  return rows;
43958
44423
  }
43959
44424
  async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
43960
- const bridgeResolved = path69.resolve(bridgeRoot);
44425
+ const bridgeResolved = path70.resolve(bridgeRoot);
43961
44426
  const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
43962
- const repoPath = rel === "" ? bridgeResolved : path69.join(bridgeResolved, rel);
43963
- const resolved = path69.resolve(repoPath);
43964
- if (!resolved.startsWith(bridgeResolved + path69.sep) && resolved !== bridgeResolved) {
44427
+ const repoPath = rel === "" ? bridgeResolved : path70.join(bridgeResolved, rel);
44428
+ const resolved = path70.resolve(repoPath);
44429
+ if (!resolved.startsWith(bridgeResolved + path70.sep) && resolved !== bridgeResolved) {
43965
44430
  return [];
43966
44431
  }
43967
44432
  return listRepoBranchRefs(resolved);
@@ -44917,7 +45382,7 @@ async function createBridgeAccessState(options = {}) {
44917
45382
  }
44918
45383
 
44919
45384
  // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
44920
- import * as path71 from "node:path";
45385
+ import * as path72 from "node:path";
44921
45386
 
44922
45387
  // src/sqlite/hash-json-sha256.ts
44923
45388
  import { createHash } from "node:crypto";
@@ -44984,7 +45449,7 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
44984
45449
  }
44985
45450
 
44986
45451
  // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
44987
- import * as path70 from "node:path";
45452
+ import * as path71 from "node:path";
44988
45453
  async function probeOneAgentTypeForCapabilities(params) {
44989
45454
  const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true, shouldContinue } = params;
44990
45455
  const canContinue = () => shouldContinue?.() !== false;
@@ -45022,7 +45487,7 @@ async function probeOneAgentTypeForCapabilities(params) {
45022
45487
  if (!canContinue()) return false;
45023
45488
  handle = await resolved.createClient({
45024
45489
  command: resolved.command,
45025
- cwd: path70.resolve(cwd),
45490
+ cwd: path71.resolve(cwd),
45026
45491
  backendAgentType: agentType,
45027
45492
  sessionMode: "agent",
45028
45493
  persistedAcpSessionId: null,
@@ -45100,7 +45565,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
45100
45565
  const { workspaceId, log: log2, getWs } = params;
45101
45566
  const isCurrent = beginAgentCapabilityWarmupRun();
45102
45567
  if (!isCurrent()) return;
45103
- const cwd = path71.resolve(getBridgeRoot());
45568
+ const cwd = path72.resolve(getBridgeRoot());
45104
45569
  async function sendBatchFromCache() {
45105
45570
  if (!isCurrent()) return;
45106
45571
  const socket = getWs();
@@ -45335,8 +45800,8 @@ async function createBridgeConnection(options) {
45335
45800
  getCloudAccessToken: () => tokens.accessToken
45336
45801
  });
45337
45802
  const identifyReportedPaths = {
45338
- bridgeRootPath: path72.resolve(getBridgeRoot()),
45339
- worktreesRootPath: path72.resolve(worktreesRootPath)
45803
+ bridgeRootPath: path73.resolve(getBridgeRoot()),
45804
+ worktreesRootPath: path73.resolve(worktreesRootPath)
45340
45805
  };
45341
45806
  const { connect } = createMainBridgeWebSocketLifecycle({
45342
45807
  state,