@sideboard-ai/core 0.1.98 → 0.1.100

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.
Files changed (26) hide show
  1. package/dist/agents/cursor-runner.cjs +69 -24
  2. package/dist/agents/cursor-runner.js +48 -3
  3. package/dist/{agents-ESJKIQQA.js → agents-CLP5BG4O.js} +3 -3
  4. package/dist/{agents-3MWWWSMF.js → agents-OXZDMPSE.js} +4 -4
  5. package/dist/{chunk-FYS2BULQ.js → chunk-EQPQTLW6.js} +209 -59
  6. package/dist/{chunk-FO67IJTY.js → chunk-GR4JKWR4.js} +1 -1
  7. package/dist/{chunk-GXSYI7FH.js → chunk-HUKCGRAT.js} +209 -59
  8. package/dist/{chunk-XUWDLRAE.js → chunk-HYKZEP5A.js} +2 -2
  9. package/dist/{chunk-XH2GS2LO.js → chunk-IFHKPZHA.js} +2 -2
  10. package/dist/{chunk-UYQYK2RY.js → chunk-MHJV4WS3.js} +1 -1
  11. package/dist/{chunk-OANJQTVG.js → chunk-MRBKGFTL.js} +1 -1
  12. package/dist/{chunk-MBP3XG57.js → chunk-OXC3MEFI.js} +3 -3
  13. package/dist/{chunk-HI2OTFFR.js → chunk-WQTTUC4N.js} +1 -1
  14. package/dist/{coordinator-prompt-AKEY4WSO.js → coordinator-prompt-HHRKQWCX.js} +1 -1
  15. package/dist/{coordinator-prompt-OQOOD5ET.js → coordinator-prompt-OOBSQCWQ.js} +1 -1
  16. package/dist/{global-workspace-RSQXRLT7.js → global-workspace-KYEBFWKB.js} +2 -2
  17. package/dist/{global-workspace-WMF3BJP5.js → global-workspace-YZWYHLQY.js} +2 -2
  18. package/dist/index.cjs +662 -440
  19. package/dist/index.d.cts +16 -2
  20. package/dist/index.d.ts +16 -2
  21. package/dist/index.js +194 -128
  22. package/dist/mcp/run-stdio.cjs +424 -227
  23. package/dist/mcp/run-stdio.js +141 -92
  24. package/dist/{workspaces-MZVQHRSJ.js → workspaces-ENVUDK6C.js} +3 -3
  25. package/dist/{workspaces-4ZY4QPWQ.js → workspaces-NNPD7QVV.js} +3 -3
  26. package/package.json +2 -2
@@ -883,6 +883,13 @@ function looksLikeMinifiedJsDump(line) {
883
883
  function looksLikeNestedElectronCrash(line) {
884
884
  return /HasCustomHostObject|ElectronInitializeICUandStartNode/i.test(line);
885
885
  }
886
+ function looksLikeHomebrewLibuvCrash(line) {
887
+ const uvRun = /uv_run/i.test(line);
888
+ const spin = /SpinEventLoopInternal/i.test(line);
889
+ const homebrewUv = /Cellar\/libuv|libuv\.\d\.dylib/i.test(line);
890
+ const homebrewNode = /Cellar\/node\//i.test(line);
891
+ return uvRun && (homebrewUv || spin || homebrewNode) || spin && (homebrewUv || homebrewNode);
892
+ }
886
893
  function clipStderr(text3, maxChars) {
887
894
  const trimmed = text3.trim();
888
895
  if (trimmed.length <= maxChars) return trimmed;
@@ -893,6 +900,7 @@ function summarizeTurnStderr(tail, maxChars = 500) {
893
900
  const cursorStartup = [...tail].reverse().find((line) => /cursor startup failed:/i.test(line));
894
901
  if (cursorStartup) return clipStderr(cursorStartup, maxChars);
895
902
  if (tail.some(looksLikeNestedElectronCrash)) return NESTED_ELECTRON_SUMMARY;
903
+ if (tail.some(looksLikeHomebrewLibuvCrash)) return HOMEBREW_LIBUV_SUMMARY;
896
904
  if (tail.some(
897
905
  (line) => /\[resource_exhausted\]|resource_exhausted/i.test(line) || /findFilesWithRipgrep/.test(line)
898
906
  )) {
@@ -915,6 +923,20 @@ function looksLikeInvalidAgentSession(text3) {
915
923
  if (!lower) return false;
916
924
  return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower) || /corrupt local agent checkpoint/.test(lower) || /missing root blob/.test(lower) || /\bagent\b.{0,120}\bnot found\b/.test(lower);
917
925
  }
926
+ function looksLikeRetryableRunnerCrash(text3) {
927
+ if (looksLikeAgentFailureMessage(text3)) return false;
928
+ if (looksLikeInvalidAgentSession(text3)) return false;
929
+ const lower = text3.trim().toLowerCase();
930
+ if (/cannot find (?:package|module)|err_module_not_found/.test(lower)) return false;
931
+ if (!lower) return true;
932
+ return /uv_run|spineventloopinternal|libuv|homebrew node \+ shared libuv|hascustomhostobject|electroninitializeicuandstartnode|nested chromium|truncated crash dump|sig(?:segv|abrt|ill)|segmentation fault|illegal instruction|fatal error/.test(
933
+ lower
934
+ );
935
+ }
936
+ function shouldRetryFailedAgentTurn(detail, opts) {
937
+ if (looksLikeInvalidAgentSession(detail) && opts.hasSession) return true;
938
+ return looksLikeRetryableRunnerCrash(detail);
939
+ }
918
940
  function looksLikeAgentFailureMessage(text3) {
919
941
  const lower = text3.trim().toLowerCase();
920
942
  if (!lower) return false;
@@ -959,11 +981,22 @@ function humanizeAgentFailDetail(detail) {
959
981
  if (/hascustomhostobject|electroninitializeicuandstartnode|nested chromium/i.test(lower)) {
960
982
  return `${raw} \u2014 retry the turn; if it keeps failing, pick another agent.`;
961
983
  }
984
+ if (/homebrew node \+ shared libuv|uv_run|spineventloopinternal/i.test(lower)) {
985
+ return /brew install node@22/i.test(raw) ? raw : `${raw} \u2014 install Node 22 LTS (\`brew install node@22\`) and retry.`;
986
+ }
962
987
  if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
963
988
  return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
964
989
  }
965
990
  return raw;
966
991
  }
992
+ function turnFailChatText(opts) {
993
+ const chat = opts.assistantText.trim();
994
+ if (chat) return chat;
995
+ if (opts.exitCode === 0) return "";
996
+ const detail = opts.detail.trim();
997
+ if (detail) return humanizeAgentFailDetail(detail);
998
+ return formatTurnExitError(opts.exitCode ?? 1, "");
999
+ }
967
1000
  function formatTurnExitError(exitCode, stderrSummary) {
968
1001
  const code = exitCode ?? 1;
969
1002
  const raw = stderrSummary.trim();
@@ -977,12 +1010,13 @@ function formatTurnExitError(exitCode, stderrSummary) {
977
1010
  if (looksLikeAgentFailureMessage(raw)) return detail;
978
1011
  return `exit ${code}: ${detail}`;
979
1012
  }
980
- var NODE_VERSION_FOOTER, NESTED_ELECTRON_SUMMARY, MINIFIED_DUMP_SUMMARY;
1013
+ var NODE_VERSION_FOOTER, NESTED_ELECTRON_SUMMARY, HOMEBREW_LIBUV_SUMMARY, MINIFIED_DUMP_SUMMARY;
981
1014
  var init_error_detail = __esm({
982
1015
  "src/agents/error-detail.ts"() {
983
1016
  "use strict";
984
1017
  NODE_VERSION_FOOTER = /^Node\.js v\d+/i;
985
1018
  NESTED_ELECTRON_SUMMARY = "Cursor local agent crashed at Electron startup (nested Chromium / HasCustomHostObject)";
1019
+ HOMEBREW_LIBUV_SUMMARY = "Cursor runner crashed in Node (Homebrew Node + shared libuv). Install Node 22 LTS (`brew install node@22`) and retry.";
986
1020
  MINIFIED_DUMP_SUMMARY = "Cursor local agent crashed during startup (truncated crash dump)";
987
1021
  }
988
1022
  });
@@ -5007,7 +5041,7 @@ function coordinatorTurnReminder(opts) {
5007
5041
  goal ? `- Goal / title: ${goal}` : null,
5008
5042
  accountDefaultsPlaybookLine(),
5009
5043
  `- For "what's going on": call list_threads (and list_workspaces if needed). Summarize fleet status \u2014 do not ls/git-status this synthetic home.`,
5010
- "- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn. Commit/push/draft PR with ask_git on the child, then wait_for_turn \u2014 never git/gh from this cwd. Call ask_git merge only if the user explicitly asked to merge.",
5044
+ "- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn. If status is error, lastError/text is the failure \u2014 adapt (switch agent, tell the user). Commit/push/draft PR with ask_git on the child, then wait_for_turn \u2014 never git/gh from this cwd. Call ask_git merge only if the user explicitly asked to merge.",
5011
5045
  "- New repo: Bash (clone or gh repo create under ~/sideboard/repos/<name>) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 ask_git create-draft.",
5012
5046
  "- When naming threads for the user, link them as `[Title](sideboard://thread/<id>)`.",
5013
5047
  "- If they will wait on Slack or leave the Mac, call set_caffeinate enabled=true. When they say they are done / wrapping up / going to sleep, call set_caffeinate enabled=false. Closing this chat also turns it off."
@@ -5130,7 +5164,7 @@ var init_coordinator_prompt = __esm({
5130
5164
  "- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
5131
5165
  "- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
5132
5166
  "- send_to_thread \u2014 queue a prompt (start/continue a chat turn); pass force_stop: true to interrupt mid-turn / clear stale queued prompts before replacing with a new request",
5133
- "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply",
5167
+ "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success.",
5134
5168
  "- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
5135
5169
  "- archive_thread / restore_thread \u2014 archive (tears down worktree when last tab) or restore",
5136
5170
  "Setup / run:",
@@ -6046,6 +6080,55 @@ var init_profile = __esm({
6046
6080
  }
6047
6081
  });
6048
6082
 
6083
+ // src/agents/packaged-runtime.ts
6084
+ function electronResourcesPath() {
6085
+ const resources = process.resourcesPath;
6086
+ if (typeof resources !== "string" || !resources) return null;
6087
+ return resources;
6088
+ }
6089
+ function packagedCursorRuntimeDir() {
6090
+ const resources = electronResourcesPath();
6091
+ if (!resources) return null;
6092
+ const dir = (0, import_node_path19.join)(resources, "cursor-runtime");
6093
+ if (!(0, import_node_fs18.existsSync)((0, import_node_path19.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
6094
+ return dir;
6095
+ }
6096
+ function packagedCursorRunnerPath() {
6097
+ const dir = packagedCursorRuntimeDir();
6098
+ return dir ? (0, import_node_path19.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
6099
+ }
6100
+ function packagedMcpDir() {
6101
+ const resources = electronResourcesPath();
6102
+ if (!resources) return null;
6103
+ const dir = (0, import_node_path19.join)(resources, "sideboard-mcp");
6104
+ if (!(0, import_node_fs18.existsSync)((0, import_node_path19.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
6105
+ return dir;
6106
+ }
6107
+ function packagedMcpStdioPath() {
6108
+ const dir = packagedMcpDir();
6109
+ return dir ? (0, import_node_path19.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
6110
+ }
6111
+ function packagedBundledNodePath() {
6112
+ const resources = electronResourcesPath();
6113
+ if (!resources) return null;
6114
+ const bin = (0, import_node_path19.join)(resources, "node", "bin", "node");
6115
+ if (!(0, import_node_fs18.existsSync)(bin)) return null;
6116
+ return bin;
6117
+ }
6118
+ function packagedCursorRipgrepCandidate(platformPkg, binName) {
6119
+ const dir = packagedCursorRuntimeDir();
6120
+ if (!dir) return null;
6121
+ return (0, import_node_path19.join)(dir, "node_modules", platformPkg, "bin", binName);
6122
+ }
6123
+ var import_node_fs18, import_node_path19;
6124
+ var init_packaged_runtime = __esm({
6125
+ "src/agents/packaged-runtime.ts"() {
6126
+ "use strict";
6127
+ import_node_fs18 = require("fs");
6128
+ import_node_path19 = require("path");
6129
+ }
6130
+ });
6131
+
6049
6132
  // src/agents/node-launch.ts
6050
6133
  function isAsarPath(filePath) {
6051
6134
  if (/\.asar\.unpacked([/\\]|$)/.test(filePath)) return false;
@@ -6055,24 +6138,129 @@ function unpackedAsarPath(filePath) {
6055
6138
  if (!isAsarPath(filePath)) return null;
6056
6139
  const unpacked = filePath.replace(/\.asar(?=[/\\])/, ".asar.unpacked");
6057
6140
  if (unpacked === filePath) return null;
6058
- return (0, import_node_fs18.existsSync)(unpacked) ? unpacked : null;
6141
+ return (0, import_node_fs19.existsSync)(unpacked) ? unpacked : null;
6059
6142
  }
6060
6143
  function nodeReadableScriptPath(scriptPath) {
6061
6144
  return unpackedAsarPath(scriptPath) ?? scriptPath;
6062
6145
  }
6063
- async function findSystemNode() {
6064
- const whichNode = await run("which", ["node"], { reject: false });
6065
- const fromWhich = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : "";
6066
- if (fromWhich && !isElectronLikeCommand(fromWhich)) return fromWhich;
6067
- const fallbacks = [
6068
- ...WELL_KNOWN_NODE_BINS,
6069
- (0, import_node_path19.join)((0, import_node_os7.homedir)(), ".local/share/fnm/aliases/default/bin/node"),
6070
- (0, import_node_path19.join)((0, import_node_os7.homedir)(), ".nvm/current/bin/node")
6146
+ function parseNodeMajor(version) {
6147
+ const match = /^v?(\d+)/.exec(version.trim());
6148
+ if (!match) return null;
6149
+ const major = Number(match[1]);
6150
+ return Number.isInteger(major) ? major : null;
6151
+ }
6152
+ function scoreNodeForAgentRuntime(candidate) {
6153
+ const major = parseNodeMajor(candidate.version);
6154
+ if (major == null) return Number.NEGATIVE_INFINITY;
6155
+ if (major < 20) return major - 100;
6156
+ const posix = candidate.path.replace(/\\/g, "/");
6157
+ let score = 0;
6158
+ if (major % 2 === 0) {
6159
+ score += 1e3 + major * 10;
6160
+ } else {
6161
+ score += major;
6162
+ }
6163
+ if (/\/Cellar\/node\/\d/.test(posix) && !/\/Cellar\/node@\d+/.test(posix)) {
6164
+ score -= 50;
6165
+ }
6166
+ return score;
6167
+ }
6168
+ function pickPreferredNode(candidates) {
6169
+ let best = null;
6170
+ let bestScore = Number.NEGATIVE_INFINITY;
6171
+ for (const candidate of candidates) {
6172
+ const score = scoreNodeForAgentRuntime(candidate);
6173
+ if (score > bestScore) {
6174
+ bestScore = score;
6175
+ best = candidate;
6176
+ }
6177
+ }
6178
+ return best;
6179
+ }
6180
+ function versionDirNodeBins(root, toBin) {
6181
+ if (!(0, import_node_fs19.existsSync)(root)) return [];
6182
+ try {
6183
+ return (0, import_node_fs19.readdirSync)(root).map(toBin);
6184
+ } catch {
6185
+ return [];
6186
+ }
6187
+ }
6188
+ function defaultNodeBinCandidates(home = (0, import_node_os7.homedir)()) {
6189
+ const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
6190
+ (prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path20.join)(prefix, "opt", `node@${major}`, "bin", "node"))
6191
+ );
6192
+ return [
6193
+ ...kegs,
6194
+ "/opt/homebrew/bin/node",
6195
+ "/usr/local/bin/node",
6196
+ (0, import_node_path20.join)(home, ".local/share/fnm/aliases/default/bin/node"),
6197
+ (0, import_node_path20.join)(home, ".nvm/current/bin/node"),
6198
+ (0, import_node_path20.join)(home, ".volta/bin/node"),
6199
+ (0, import_node_path20.join)(home, ".asdf/shims/node"),
6200
+ (0, import_node_path20.join)(home, ".local/share/mise/shims/node"),
6201
+ ...versionDirNodeBins(
6202
+ (0, import_node_path20.join)(home, ".nvm", "versions", "node"),
6203
+ (name) => (0, import_node_path20.join)(home, ".nvm", "versions", "node", name, "bin", "node")
6204
+ ),
6205
+ ...versionDirNodeBins(
6206
+ (0, import_node_path20.join)(home, ".local/share/fnm", "node-versions"),
6207
+ (name) => (0, import_node_path20.join)(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
6208
+ ),
6209
+ ...versionDirNodeBins(
6210
+ (0, import_node_path20.join)(home, ".volta", "tools", "image", "node"),
6211
+ (name) => (0, import_node_path20.join)(home, ".volta", "tools", "image", "node", name, "bin", "node")
6212
+ )
6071
6213
  ];
6072
- for (const bin of fallbacks) {
6073
- if ((0, import_node_fs18.existsSync)(bin) && !isElectronLikeCommand(bin)) return bin;
6214
+ }
6215
+ function uniqueExistingNodeBins(paths) {
6216
+ const seen = /* @__PURE__ */ new Set();
6217
+ const out = [];
6218
+ for (const raw of paths) {
6219
+ const p = raw.trim();
6220
+ if (!p || !(0, import_node_fs19.existsSync)(p) || isElectronLikeCommand(p)) continue;
6221
+ let key = p;
6222
+ try {
6223
+ key = (0, import_node_fs19.realpathSync)(p);
6224
+ } catch {
6225
+ continue;
6226
+ }
6227
+ if (seen.has(key)) continue;
6228
+ seen.add(key);
6229
+ out.push(p);
6074
6230
  }
6075
- return null;
6231
+ return out;
6232
+ }
6233
+ async function whichAllNode() {
6234
+ if (process.platform === "win32") {
6235
+ const located = await run("where", ["node"], { reject: false, timeoutMs: 5e3 });
6236
+ if (located.exitCode !== 0) return [];
6237
+ return located.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
6238
+ }
6239
+ const all = await run("which", ["-a", "node"], { reject: false, timeoutMs: 5e3 });
6240
+ if (all.exitCode === 0 && all.stdout.trim()) {
6241
+ return all.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
6242
+ }
6243
+ const single = await run("which", ["node"], { reject: false, timeoutMs: 5e3 });
6244
+ if (single.exitCode !== 0 || !single.stdout.trim()) return [];
6245
+ return [single.stdout.trim()];
6246
+ }
6247
+ async function probeNodeVersion(bin) {
6248
+ const probed = await run(bin, ["-v"], { reject: false, timeoutMs: 4e3 });
6249
+ if (probed.exitCode !== 0) return null;
6250
+ const version = probed.stdout.trim().split(/\r?\n/).find(Boolean);
6251
+ return version || null;
6252
+ }
6253
+ async function findSystemNode() {
6254
+ const fromPath = await whichAllNode();
6255
+ const bins = uniqueExistingNodeBins([...fromPath, ...defaultNodeBinCandidates()]);
6256
+ if (bins.length === 0) return null;
6257
+ const probed = await Promise.all(
6258
+ bins.map(async (path) => {
6259
+ const version = await probeNodeVersion(path);
6260
+ return version ? { path, version } : null;
6261
+ })
6262
+ );
6263
+ return pickPreferredNode(probed.filter((c) => c != null));
6076
6264
  }
6077
6265
  function applyNodeLaunch(launch, args) {
6078
6266
  const readableArgs = args.map(nodeReadableScriptPath);
@@ -6090,9 +6278,13 @@ function applyNodeLaunch(launch, args) {
6090
6278
  async function resolveNodeLaunch(scriptPath) {
6091
6279
  const script = nodeReadableScriptPath(scriptPath);
6092
6280
  if (!isAsarPath(script)) {
6093
- const nodeBin = await findSystemNode();
6094
- if (nodeBin) {
6095
- return { file: nodeBin, env: {} };
6281
+ const bundled = packagedBundledNodePath();
6282
+ if (bundled && !isElectronLikeCommand(bundled)) {
6283
+ return { file: bundled, env: {} };
6284
+ }
6285
+ const node = await findSystemNode();
6286
+ if (node) {
6287
+ return { file: node.path, env: {}, nodeVersion: node.version };
6096
6288
  }
6097
6289
  }
6098
6290
  return {
@@ -6100,61 +6292,17 @@ async function resolveNodeLaunch(scriptPath) {
6100
6292
  env: { ELECTRON_RUN_AS_NODE: "1" }
6101
6293
  };
6102
6294
  }
6103
- var import_node_fs18, import_node_os7, import_node_path19, WELL_KNOWN_NODE_BINS;
6295
+ var import_node_fs19, import_node_os7, import_node_path20, PREFERRED_LTS_MAJORS;
6104
6296
  var init_node_launch = __esm({
6105
6297
  "src/agents/node-launch.ts"() {
6106
6298
  "use strict";
6107
- import_node_fs18 = require("fs");
6299
+ import_node_fs19 = require("fs");
6108
6300
  import_node_os7 = require("os");
6109
- import_node_path19 = require("path");
6301
+ import_node_path20 = require("path");
6110
6302
  init_nested_electron_env();
6111
6303
  init_run();
6112
- WELL_KNOWN_NODE_BINS = [
6113
- "/opt/homebrew/bin/node",
6114
- "/usr/local/bin/node"
6115
- ];
6116
- }
6117
- });
6118
-
6119
- // src/agents/packaged-runtime.ts
6120
- function electronResourcesPath() {
6121
- const resources = process.resourcesPath;
6122
- if (typeof resources !== "string" || !resources) return null;
6123
- return resources;
6124
- }
6125
- function packagedCursorRuntimeDir() {
6126
- const resources = electronResourcesPath();
6127
- if (!resources) return null;
6128
- const dir = (0, import_node_path20.join)(resources, "cursor-runtime");
6129
- if (!(0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
6130
- return dir;
6131
- }
6132
- function packagedCursorRunnerPath() {
6133
- const dir = packagedCursorRuntimeDir();
6134
- return dir ? (0, import_node_path20.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
6135
- }
6136
- function packagedMcpDir() {
6137
- const resources = electronResourcesPath();
6138
- if (!resources) return null;
6139
- const dir = (0, import_node_path20.join)(resources, "sideboard-mcp");
6140
- if (!(0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
6141
- return dir;
6142
- }
6143
- function packagedMcpStdioPath() {
6144
- const dir = packagedMcpDir();
6145
- return dir ? (0, import_node_path20.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
6146
- }
6147
- function packagedCursorRipgrepCandidate(platformPkg, binName) {
6148
- const dir = packagedCursorRuntimeDir();
6149
- if (!dir) return null;
6150
- return (0, import_node_path20.join)(dir, "node_modules", platformPkg, "bin", binName);
6151
- }
6152
- var import_node_fs19, import_node_path20;
6153
- var init_packaged_runtime = __esm({
6154
- "src/agents/packaged-runtime.ts"() {
6155
- "use strict";
6156
- import_node_fs19 = require("fs");
6157
- import_node_path20 = require("path");
6304
+ init_packaged_runtime();
6305
+ PREFERRED_LTS_MAJORS = [24, 22, 20];
6158
6306
  }
6159
6307
  });
6160
6308
 
@@ -8644,40 +8792,40 @@ __export(plan_file_exports, {
8644
8792
  writePlanFile: () => writePlanFile
8645
8793
  });
8646
8794
  function ensureAttachmentsGitignore2(worktreePath) {
8647
- const gitignoreAbs = (0, import_node_path35.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
8648
- if ((0, import_node_fs38.existsSync)(gitignoreAbs)) return;
8649
- (0, import_node_fs38.mkdirSync)((0, import_node_path35.dirname)(gitignoreAbs), { recursive: true });
8650
- (0, import_node_fs38.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
8795
+ const gitignoreAbs = (0, import_node_path36.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
8796
+ if ((0, import_node_fs39.existsSync)(gitignoreAbs)) return;
8797
+ (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(gitignoreAbs), { recursive: true });
8798
+ (0, import_node_fs39.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
8651
8799
  }
8652
8800
  function planFileAbs(worktreePath) {
8653
- return (0, import_node_path35.join)(worktreePath, PLAN_FILE_REL);
8801
+ return (0, import_node_path36.join)(worktreePath, PLAN_FILE_REL);
8654
8802
  }
8655
8803
  function readTextIfPresent2(abs) {
8656
- if (!(0, import_node_fs38.existsSync)(abs)) return null;
8804
+ if (!(0, import_node_fs39.existsSync)(abs)) return null;
8657
8805
  try {
8658
- const content = (0, import_node_fs38.readFileSync)(abs, "utf8");
8806
+ const content = (0, import_node_fs39.readFileSync)(abs, "utf8");
8659
8807
  return content.trim() ? content : null;
8660
8808
  } catch {
8661
8809
  return null;
8662
8810
  }
8663
8811
  }
8664
8812
  function readPlanFile(worktreePath) {
8665
- return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path35.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path35.join)(worktreePath, LEGACY_PLAN_FILE_REL));
8813
+ return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path36.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path36.join)(worktreePath, LEGACY_PLAN_FILE_REL));
8666
8814
  }
8667
8815
  function writePlanFile(worktreePath, content) {
8668
8816
  ensureAttachmentsGitignore2(worktreePath);
8669
8817
  const abs = planFileAbs(worktreePath);
8670
- (0, import_node_fs38.mkdirSync)((0, import_node_path35.dirname)(abs), { recursive: true });
8818
+ (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(abs), { recursive: true });
8671
8819
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
8672
- (0, import_node_fs38.writeFileSync)(abs, body, "utf8");
8820
+ (0, import_node_fs39.writeFileSync)(abs, body, "utf8");
8673
8821
  return PLAN_FILE_REL;
8674
8822
  }
8675
- var import_node_fs38, import_node_path35;
8823
+ var import_node_fs39, import_node_path36;
8676
8824
  var init_plan_file = __esm({
8677
8825
  "src/plan/plan-file.ts"() {
8678
8826
  "use strict";
8679
- import_node_fs38 = require("fs");
8680
- import_node_path35 = require("path");
8827
+ import_node_fs39 = require("fs");
8828
+ import_node_path36 = require("path");
8681
8829
  init_workspace_scratch();
8682
8830
  init_plan_present();
8683
8831
  init_plan_present();
@@ -8698,7 +8846,7 @@ function setCaffeinateHoldHooks(next) {
8698
8846
  hooks = next;
8699
8847
  }
8700
8848
  function caffeinateHoldPath() {
8701
- return (0, import_node_path36.join)(appDataDir(), "caffeinate-hold.json");
8849
+ return (0, import_node_path37.join)(appDataDir(), "caffeinate-hold.json");
8702
8850
  }
8703
8851
  function processAlive(pid) {
8704
8852
  if (hooks.processAlive) return hooks.processAlive(pid);
@@ -8732,9 +8880,9 @@ function uniqueIds(ids) {
8732
8880
  }
8733
8881
  function readHold() {
8734
8882
  const path = caffeinateHoldPath();
8735
- if (!(0, import_node_fs39.existsSync)(path)) return null;
8883
+ if (!(0, import_node_fs40.existsSync)(path)) return null;
8736
8884
  try {
8737
- const parsed = JSON.parse((0, import_node_fs39.readFileSync)(path, "utf8"));
8885
+ const parsed = JSON.parse((0, import_node_fs40.readFileSync)(path, "utf8"));
8738
8886
  if (typeof parsed?.pid === "number" && parsed.pid > 0) {
8739
8887
  return {
8740
8888
  pid: parsed.pid,
@@ -8756,7 +8904,7 @@ function writeHold(pid, threadIds) {
8756
8904
  }
8757
8905
  function clearHold() {
8758
8906
  try {
8759
- (0, import_node_fs39.unlinkSync)(caffeinateHoldPath());
8907
+ (0, import_node_fs40.unlinkSync)(caffeinateHoldPath());
8760
8908
  } catch {
8761
8909
  }
8762
8910
  }
@@ -8841,13 +8989,13 @@ function releaseCaffeinateHoldForThread(threadId) {
8841
8989
  }
8842
8990
  return setCaffeinateHold(false, { threadId: id });
8843
8991
  }
8844
- var import_node_child_process4, import_node_fs39, import_node_path36, hooks;
8992
+ var import_node_child_process4, import_node_fs40, import_node_path37, hooks;
8845
8993
  var init_caffeinate_hold = __esm({
8846
8994
  "src/store/caffeinate-hold.ts"() {
8847
8995
  "use strict";
8848
8996
  import_node_child_process4 = require("child_process");
8849
- import_node_fs39 = require("fs");
8850
- import_node_path36 = require("path");
8997
+ import_node_fs40 = require("fs");
8998
+ import_node_path37 = require("path");
8851
8999
  init_paths();
8852
9000
  init_private_file();
8853
9001
  hooks = {};
@@ -8862,10 +9010,10 @@ __export(cursor_recover_exports, {
8862
9010
  function recoverFinishedCursorRun(opts) {
8863
9011
  const agentId = opts.agentId.trim();
8864
9012
  if (!agentId) return null;
8865
- const runsPath = (0, import_node_path37.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
8866
- if (!(0, import_node_fs40.existsSync)(runsPath)) return null;
9013
+ const runsPath = (0, import_node_path38.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
9014
+ if (!(0, import_node_fs41.existsSync)(runsPath)) return null;
8867
9015
  try {
8868
- const lines = (0, import_node_fs40.readFileSync)(runsPath, "utf8").split("\n");
9016
+ const lines = (0, import_node_fs41.readFileSync)(runsPath, "utf8").split("\n");
8869
9017
  let best = null;
8870
9018
  for (const line of lines) {
8871
9019
  const trimmed = line.trim();
@@ -8891,12 +9039,12 @@ function recoverFinishedCursorRun(opts) {
8891
9039
  return null;
8892
9040
  }
8893
9041
  }
8894
- var import_node_fs40, import_node_path37;
9042
+ var import_node_fs41, import_node_path38;
8895
9043
  var init_cursor_recover = __esm({
8896
9044
  "src/agents/cursor-recover.ts"() {
8897
9045
  "use strict";
8898
- import_node_fs40 = require("fs");
8899
- import_node_path37 = require("path");
9046
+ import_node_fs41 = require("fs");
9047
+ import_node_path38 = require("path");
8900
9048
  init_paths();
8901
9049
  }
8902
9050
  });
@@ -8908,7 +9056,7 @@ init_nested_electron_env();
8908
9056
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
8909
9057
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
8910
9058
  var import_zod3 = require("zod");
8911
- var import_node_path38 = require("path");
9059
+ var import_node_path39 = require("path");
8912
9060
 
8913
9061
  // src/orchestrator/orchestrator.ts
8914
9062
  var import_node_events = require("events");
@@ -9419,7 +9567,7 @@ async function refreshSlackReplyBadges(opts) {
9419
9567
  }
9420
9568
 
9421
9569
  // src/orchestrator/orchestrator.ts
9422
- var import_node_fs41 = require("fs");
9570
+ var import_node_fs42 = require("fs");
9423
9571
  init_error_detail();
9424
9572
 
9425
9573
  // src/agents/spawn.ts
@@ -10536,8 +10684,44 @@ async function cloneRepoIntoSideboard(opts) {
10536
10684
  // src/orchestrator/orchestrator.ts
10537
10685
  init_thread_store();
10538
10686
 
10539
- // src/threads/create.ts
10687
+ // src/store/desktop-host.ts
10540
10688
  var import_node_fs30 = require("fs");
10689
+ var import_node_path29 = require("path");
10690
+ init_paths();
10691
+ function desktopHostPidPath() {
10692
+ return (0, import_node_path29.join)(appDataDir(), "desktop-host.pid");
10693
+ }
10694
+ function readDesktopHostPid() {
10695
+ try {
10696
+ const pid = Number.parseInt((0, import_node_fs30.readFileSync)(desktopHostPidPath(), "utf8").trim(), 10);
10697
+ if (!Number.isFinite(pid) || pid <= 0) return null;
10698
+ return pid;
10699
+ } catch {
10700
+ return null;
10701
+ }
10702
+ }
10703
+ function pidAlive(pid) {
10704
+ try {
10705
+ process.kill(pid, 0);
10706
+ return true;
10707
+ } catch {
10708
+ return false;
10709
+ }
10710
+ }
10711
+ function isDesktopHostAlive() {
10712
+ const pid = readDesktopHostPid();
10713
+ return pid != null && pidAlive(pid);
10714
+ }
10715
+ function isThisProcessDesktopHost() {
10716
+ return readDesktopHostPid() === process.pid && pidAlive(process.pid);
10717
+ }
10718
+ function thisProcessShouldDrainAgentQueues() {
10719
+ if (isThisProcessDesktopHost()) return true;
10720
+ return !isDesktopHostAlive();
10721
+ }
10722
+
10723
+ // src/threads/create.ts
10724
+ var import_node_fs31 = require("fs");
10541
10725
 
10542
10726
  // src/detect/detect.ts
10543
10727
  init_agents();
@@ -10582,7 +10766,7 @@ async function createThread(input, _onSetupLine) {
10582
10766
  });
10583
10767
  await requireAgent(resolved.agent);
10584
10768
  const repoPath = await resolveRepoRoot(input.repoPath);
10585
- if (!(0, import_node_fs30.existsSync)(repoPath)) {
10769
+ if (!(0, import_node_fs31.existsSync)(repoPath)) {
10586
10770
  throw new Error(`Repo not found: ${repoPath}`);
10587
10771
  }
10588
10772
  let sourceRef = input.sourceRef;
@@ -11058,8 +11242,8 @@ function forkChatTab(input) {
11058
11242
 
11059
11243
  // src/review/request-review.ts
11060
11244
  var import_node_crypto5 = require("crypto");
11061
- var import_node_fs31 = require("fs");
11062
- var import_node_path29 = require("path");
11245
+ var import_node_fs32 = require("fs");
11246
+ var import_node_path30 = require("path");
11063
11247
  init_global_workspace();
11064
11248
  init_thread_store();
11065
11249
 
@@ -11207,22 +11391,22 @@ function shouldRefreshReviewRequestTemplate(content) {
11207
11391
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
11208
11392
  }
11209
11393
  function readTextIfPresent(abs) {
11210
- if (!(0, import_node_fs31.existsSync)(abs)) return null;
11394
+ if (!(0, import_node_fs32.existsSync)(abs)) return null;
11211
11395
  try {
11212
- const content = (0, import_node_fs31.readFileSync)(abs, "utf8");
11396
+ const content = (0, import_node_fs32.readFileSync)(abs, "utf8");
11213
11397
  return content.trim() ? content : null;
11214
11398
  } catch {
11215
11399
  return null;
11216
11400
  }
11217
11401
  }
11218
11402
  function ensureAttachmentsGitignore(worktreePath) {
11219
- const gitignoreAbs = (0, import_node_path29.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
11220
- if ((0, import_node_fs31.existsSync)(gitignoreAbs)) return;
11221
- (0, import_node_fs31.mkdirSync)((0, import_node_path29.dirname)(gitignoreAbs), { recursive: true });
11222
- (0, import_node_fs31.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
11403
+ const gitignoreAbs = (0, import_node_path30.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
11404
+ if ((0, import_node_fs32.existsSync)(gitignoreAbs)) return;
11405
+ (0, import_node_fs32.mkdirSync)((0, import_node_path30.dirname)(gitignoreAbs), { recursive: true });
11406
+ (0, import_node_fs32.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
11223
11407
  }
11224
11408
  function resolveReviewGuidelines(worktreePath) {
11225
- const repoAbs = (0, import_node_path29.join)(worktreePath, REPO_REVIEW_PATH);
11409
+ const repoAbs = (0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH);
11226
11410
  const repoContent = readTextIfPresent(repoAbs);
11227
11411
  if (repoContent) {
11228
11412
  return {
@@ -11232,7 +11416,7 @@ function resolveReviewGuidelines(worktreePath) {
11232
11416
  source: "repo"
11233
11417
  };
11234
11418
  }
11235
- const localAbs = (0, import_node_path29.join)(worktreePath, REVIEW_REQUEST_PATH);
11419
+ const localAbs = (0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH);
11236
11420
  const localContent = readTextIfPresent(localAbs);
11237
11421
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
11238
11422
  return {
@@ -11242,7 +11426,7 @@ function resolveReviewGuidelines(worktreePath) {
11242
11426
  source: "local"
11243
11427
  };
11244
11428
  }
11245
- const legacyAbs = (0, import_node_path29.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
11429
+ const legacyAbs = (0, import_node_path30.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
11246
11430
  const legacyContent = readTextIfPresent(legacyAbs);
11247
11431
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
11248
11432
  return {
@@ -11253,8 +11437,8 @@ function resolveReviewGuidelines(worktreePath) {
11253
11437
  };
11254
11438
  }
11255
11439
  ensureAttachmentsGitignore(worktreePath);
11256
- (0, import_node_fs31.mkdirSync)((0, import_node_path29.dirname)(localAbs), { recursive: true });
11257
- (0, import_node_fs31.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11440
+ (0, import_node_fs32.mkdirSync)((0, import_node_path30.dirname)(localAbs), { recursive: true });
11441
+ (0, import_node_fs32.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
11258
11442
  return {
11259
11443
  path: REVIEW_REQUEST_PATH,
11260
11444
  name: REVIEW_REQUEST_NAME,
@@ -11454,21 +11638,21 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
11454
11638
 
11455
11639
  // src/threads/adopt.ts
11456
11640
  var import_node_child_process3 = require("child_process");
11457
- var import_node_fs32 = require("fs");
11641
+ var import_node_fs33 = require("fs");
11458
11642
  var import_node_os10 = require("os");
11459
- var import_node_path30 = require("path");
11643
+ var import_node_path31 = require("path");
11460
11644
  var import_node_module4 = require("module");
11461
11645
  init_worktree();
11462
11646
  init_thread_store();
11463
11647
  var import_meta4 = {};
11464
- var CONDUCTOR_APP_SUPPORT = (0, import_node_path30.join)(
11648
+ var CONDUCTOR_APP_SUPPORT = (0, import_node_path31.join)(
11465
11649
  process.env.HOME ?? "",
11466
11650
  "Library",
11467
11651
  "Application Support",
11468
11652
  "com.conductor.app"
11469
11653
  );
11470
- var CONDUCTOR_DB = (0, import_node_path30.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
11471
- var CURSOR_SDK_STORE = (0, import_node_path30.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
11654
+ var CONDUCTOR_DB = (0, import_node_path31.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
11655
+ var CURSOR_SDK_STORE = (0, import_node_path31.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
11472
11656
  function openReadonlySqlite(file) {
11473
11657
  const req = (0, import_node_module4.createRequire)(import_meta4.url);
11474
11658
  const Database = req("better-sqlite3");
@@ -11485,21 +11669,21 @@ function mapAgentType(raw) {
11485
11669
  return null;
11486
11670
  }
11487
11671
  function resolveConductorCursorAgentId(workspacePath) {
11488
- if (!workspacePath || !(0, import_node_fs32.existsSync)(CURSOR_SDK_STORE)) return null;
11672
+ if (!workspacePath || !(0, import_node_fs33.existsSync)(CURSOR_SDK_STORE)) return null;
11489
11673
  const normalized = workspacePath.replace(/\/$/, "");
11490
11674
  let best = null;
11491
11675
  let hashes;
11492
11676
  try {
11493
- hashes = (0, import_node_fs32.readdirSync)(CURSOR_SDK_STORE);
11677
+ hashes = (0, import_node_fs33.readdirSync)(CURSOR_SDK_STORE);
11494
11678
  } catch {
11495
11679
  return null;
11496
11680
  }
11497
11681
  for (const hash of hashes) {
11498
- const agentsFile = (0, import_node_path30.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
11499
- if (!(0, import_node_fs32.existsSync)(agentsFile)) continue;
11682
+ const agentsFile = (0, import_node_path31.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
11683
+ if (!(0, import_node_fs33.existsSync)(agentsFile)) continue;
11500
11684
  let text3;
11501
11685
  try {
11502
- text3 = (0, import_node_fs32.readFileSync)(agentsFile, "utf8");
11686
+ text3 = (0, import_node_fs33.readFileSync)(agentsFile, "utf8");
11503
11687
  } catch {
11504
11688
  continue;
11505
11689
  }
@@ -11523,7 +11707,7 @@ function resolveConductorCursorAgentId(workspacePath) {
11523
11707
  return best?.agentId ?? null;
11524
11708
  }
11525
11709
  async function adoptThread(input) {
11526
- if (!(0, import_node_fs32.existsSync)(input.worktreePath)) {
11710
+ if (!(0, import_node_fs33.existsSync)(input.worktreePath)) {
11527
11711
  throw new Error(`Worktree not found: ${input.worktreePath}`);
11528
11712
  }
11529
11713
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -11547,18 +11731,18 @@ async function adoptThread(input) {
11547
11731
  return thread;
11548
11732
  }
11549
11733
  function listConductorWorkspaces() {
11550
- if (!(0, import_node_fs32.existsSync)(CONDUCTOR_DB)) {
11734
+ if (!(0, import_node_fs33.existsSync)(CONDUCTOR_DB)) {
11551
11735
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
11552
11736
  }
11553
- const tmp = (0, import_node_fs32.mkdtempSync)((0, import_node_path30.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
11554
- const snapshot = (0, import_node_path30.join)(tmp, "conductor.db");
11737
+ const tmp = (0, import_node_fs33.mkdtempSync)((0, import_node_path31.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
11738
+ const snapshot = (0, import_node_path31.join)(tmp, "conductor.db");
11555
11739
  try {
11556
- (0, import_node_fs32.copyFileSync)(CONDUCTOR_DB, snapshot);
11740
+ (0, import_node_fs33.copyFileSync)(CONDUCTOR_DB, snapshot);
11557
11741
  for (const suffix of ["-wal", "-shm"]) {
11558
11742
  const src = `${CONDUCTOR_DB}${suffix}`;
11559
- if ((0, import_node_fs32.existsSync)(src)) {
11743
+ if ((0, import_node_fs33.existsSync)(src)) {
11560
11744
  try {
11561
- (0, import_node_fs32.copyFileSync)(src, `${snapshot}${suffix}`);
11745
+ (0, import_node_fs33.copyFileSync)(src, `${snapshot}${suffix}`);
11562
11746
  } catch {
11563
11747
  }
11564
11748
  }
@@ -11634,22 +11818,22 @@ function listConductorWorkspaces() {
11634
11818
  db.close();
11635
11819
  }
11636
11820
  } finally {
11637
- (0, import_node_fs32.rmSync)(tmp, { recursive: true, force: true });
11821
+ (0, import_node_fs33.rmSync)(tmp, { recursive: true, force: true });
11638
11822
  }
11639
11823
  }
11640
11824
  function importConductorWorkspace(workspaceId) {
11641
- if (!(0, import_node_fs32.existsSync)(CONDUCTOR_DB)) {
11825
+ if (!(0, import_node_fs33.existsSync)(CONDUCTOR_DB)) {
11642
11826
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
11643
11827
  }
11644
- const tmp = (0, import_node_fs32.mkdtempSync)((0, import_node_path30.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
11645
- const snapshot = (0, import_node_path30.join)(tmp, "conductor.db");
11828
+ const tmp = (0, import_node_fs33.mkdtempSync)((0, import_node_path31.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
11829
+ const snapshot = (0, import_node_path31.join)(tmp, "conductor.db");
11646
11830
  try {
11647
- (0, import_node_fs32.copyFileSync)(CONDUCTOR_DB, snapshot);
11831
+ (0, import_node_fs33.copyFileSync)(CONDUCTOR_DB, snapshot);
11648
11832
  for (const suffix of ["-wal", "-shm"]) {
11649
11833
  const src = `${CONDUCTOR_DB}${suffix}`;
11650
- if ((0, import_node_fs32.existsSync)(src)) {
11834
+ if ((0, import_node_fs33.existsSync)(src)) {
11651
11835
  try {
11652
- (0, import_node_fs32.copyFileSync)(src, `${snapshot}${suffix}`);
11836
+ (0, import_node_fs33.copyFileSync)(src, `${snapshot}${suffix}`);
11653
11837
  } catch {
11654
11838
  }
11655
11839
  }
@@ -11667,7 +11851,7 @@ function importConductorWorkspace(workspaceId) {
11667
11851
  ).get(workspaceId);
11668
11852
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
11669
11853
  const worktreePath = String(row.workspacePath);
11670
- if (!(0, import_node_fs32.existsSync)(worktreePath)) {
11854
+ if (!(0, import_node_fs33.existsSync)(worktreePath)) {
11671
11855
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
11672
11856
  }
11673
11857
  let sessionId = null;
@@ -11730,7 +11914,7 @@ function importConductorWorkspace(workspaceId) {
11730
11914
  db.close();
11731
11915
  }
11732
11916
  } finally {
11733
- (0, import_node_fs32.rmSync)(tmp, { recursive: true, force: true });
11917
+ (0, import_node_fs33.rmSync)(tmp, { recursive: true, force: true });
11734
11918
  }
11735
11919
  }
11736
11920
  async function importConductorWorkspaceAsync(workspaceId) {
@@ -11738,7 +11922,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
11738
11922
  }
11739
11923
 
11740
11924
  // src/threads/stack-layers.ts
11741
- var import_node_fs33 = require("fs");
11925
+ var import_node_fs34 = require("fs");
11742
11926
  init_run();
11743
11927
  init_stack();
11744
11928
  init_worktree();
@@ -11806,7 +11990,7 @@ async function openStackLayer(input, _onSetupLine) {
11806
11990
  let createdWorktree = false;
11807
11991
  const trees = await listWorktrees(repoPath);
11808
11992
  const checkedOut = trees.find((w) => w.branch === branchName);
11809
- if (checkedOut?.path && (0, import_node_fs33.existsSync)(checkedOut.path)) {
11993
+ if (checkedOut?.path && (0, import_node_fs34.existsSync)(checkedOut.path)) {
11810
11994
  if (input.reuseExistingWorktree !== false) {
11811
11995
  worktreePath = checkedOut.path;
11812
11996
  } else {
@@ -11948,7 +12132,7 @@ async function initStackFromThread(input, onSetupLine) {
11948
12132
  async function createPrStack(input, onSetupLine) {
11949
12133
  await requireAgent(input.agent);
11950
12134
  const repoPath = await resolveRepoRoot(input.repoPath);
11951
- if (!(0, import_node_fs33.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
12135
+ if (!(0, import_node_fs34.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
11952
12136
  if (!input.branches.length) throw new Error("At least one branch name required");
11953
12137
  const status = await detectGhStack(repoPath);
11954
12138
  if (!status.available) throw new Error(status.reason);
@@ -12015,7 +12199,7 @@ async function createPrStack(input, onSetupLine) {
12015
12199
  }
12016
12200
  }
12017
12201
  const claimed = new Set(threads.map((t) => t.worktreePath));
12018
- if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs33.existsSync)(bootstrap.worktreePath)) {
12202
+ if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs34.existsSync)(bootstrap.worktreePath)) {
12019
12203
  try {
12020
12204
  await removeWorktree(repoPath, bootstrap.worktreePath, {
12021
12205
  deleteBranch: bootstrap.branchName
@@ -12030,12 +12214,12 @@ async function createPrStack(input, onSetupLine) {
12030
12214
  init_worktree();
12031
12215
 
12032
12216
  // src/diff/diff.ts
12033
- var import_node_fs34 = require("fs");
12034
- var import_node_path31 = require("path");
12217
+ var import_node_fs35 = require("fs");
12218
+ var import_node_path32 = require("path");
12035
12219
  init_run();
12036
12220
  init_worktree();
12037
12221
  async function inspectGitWorktree(worktreePath) {
12038
- if (!worktreePath || !(0, import_node_fs34.existsSync)(worktreePath)) return "missing_worktree";
12222
+ if (!worktreePath || !(0, import_node_fs35.existsSync)(worktreePath)) return "missing_worktree";
12039
12223
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
12040
12224
  reject: false
12041
12225
  });
@@ -12043,7 +12227,7 @@ async function inspectGitWorktree(worktreePath) {
12043
12227
  return "ok";
12044
12228
  }
12045
12229
  async function initializeGitRepository(worktreePath) {
12046
- if (!worktreePath || !(0, import_node_fs34.existsSync)(worktreePath)) {
12230
+ if (!worktreePath || !(0, import_node_fs35.existsSync)(worktreePath)) {
12047
12231
  throw new Error("Worktree not found");
12048
12232
  }
12049
12233
  const status = await inspectGitWorktree(worktreePath);
@@ -12179,11 +12363,11 @@ new file mode 100644
12179
12363
  };
12180
12364
  }
12181
12365
  async function untrackedPatch(worktreePath, path, maxHunk) {
12182
- const abs = (0, import_node_path31.join)(worktreePath, path);
12366
+ const abs = (0, import_node_path32.join)(worktreePath, path);
12183
12367
  try {
12184
- const st = (0, import_node_fs34.statSync)(abs);
12368
+ const st = (0, import_node_fs35.statSync)(abs);
12185
12369
  if (st.isFile() && st.size > maxHunk) {
12186
- const buf = (0, import_node_fs34.readFileSync)(abs).subarray(0, maxHunk);
12370
+ const buf = (0, import_node_fs35.readFileSync)(abs).subarray(0, maxHunk);
12187
12371
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
12188
12372
  }
12189
12373
  } catch {
@@ -12684,8 +12868,8 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
12684
12868
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
12685
12869
  assertSafeRelativePath(relativePath);
12686
12870
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
12687
- const abs = (0, import_node_path31.join)(worktreePath, relativePath);
12688
- const st = (0, import_node_fs34.statSync)(abs);
12871
+ const abs = (0, import_node_path32.join)(worktreePath, relativePath);
12872
+ const st = (0, import_node_fs35.statSync)(abs);
12689
12873
  if (!st.isFile()) {
12690
12874
  throw new Error(`Not a file: ${relativePath}`);
12691
12875
  }
@@ -12694,7 +12878,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
12694
12878
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
12695
12879
  );
12696
12880
  }
12697
- const buf = (0, import_node_fs34.readFileSync)(abs);
12881
+ const buf = (0, import_node_fs35.readFileSync)(abs);
12698
12882
  return {
12699
12883
  path: relativePath,
12700
12884
  contentBase64: buf.toString("base64"),
@@ -12704,12 +12888,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
12704
12888
  function readWorktreeFile(worktreePath, relativePath, opts) {
12705
12889
  assertSafeRelativePath(relativePath);
12706
12890
  const maxBytes = opts?.maxBytes ?? 2e5;
12707
- const abs = (0, import_node_path31.join)(worktreePath, relativePath);
12708
- const st = (0, import_node_fs34.statSync)(abs);
12891
+ const abs = (0, import_node_path32.join)(worktreePath, relativePath);
12892
+ const st = (0, import_node_fs35.statSync)(abs);
12709
12893
  if (!st.isFile()) {
12710
12894
  throw new Error(`Not a file: ${relativePath}`);
12711
12895
  }
12712
- const buf = (0, import_node_fs34.readFileSync)(abs);
12896
+ const buf = (0, import_node_fs35.readFileSync)(abs);
12713
12897
  if (isImageRelativePath(relativePath)) {
12714
12898
  const maxImageBytes = Math.max(maxBytes, 15e6);
12715
12899
  const truncated2 = buf.length > maxImageBytes;
@@ -12752,9 +12936,9 @@ function assertSafeRelativePath(relativePath) {
12752
12936
  }
12753
12937
  function writeWorktreeFile(worktreePath, relativePath, content) {
12754
12938
  assertSafeRelativePath(relativePath);
12755
- const abs = (0, import_node_path31.join)(worktreePath, relativePath);
12756
- (0, import_node_fs34.mkdirSync)((0, import_node_path31.dirname)(abs), { recursive: true });
12757
- (0, import_node_fs34.writeFileSync)(abs, content, "utf8");
12939
+ const abs = (0, import_node_path32.join)(worktreePath, relativePath);
12940
+ (0, import_node_fs35.mkdirSync)((0, import_node_path32.dirname)(abs), { recursive: true });
12941
+ (0, import_node_fs35.writeFileSync)(abs, content, "utf8");
12758
12942
  return { path: relativePath };
12759
12943
  }
12760
12944
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -12858,9 +13042,9 @@ async function confirmLand(thread, opts) {
12858
13042
  }
12859
13043
 
12860
13044
  // src/skills/discover.ts
12861
- var import_node_fs35 = require("fs");
13045
+ var import_node_fs36 = require("fs");
12862
13046
  var import_node_os11 = require("os");
12863
- var import_node_path32 = require("path");
13047
+ var import_node_path33 = require("path");
12864
13048
  function toCommand(name) {
12865
13049
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
12866
13050
  }
@@ -12892,7 +13076,7 @@ function parseFrontmatter(content) {
12892
13076
  }
12893
13077
  function readSkill(skillMd, source) {
12894
13078
  try {
12895
- const content = (0, import_node_fs35.readFileSync)(skillMd, "utf8");
13079
+ const content = (0, import_node_fs36.readFileSync)(skillMd, "utf8");
12896
13080
  const { name: fmName, description } = parseFrontmatter(content);
12897
13081
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
12898
13082
  const name = fmName || dirName;
@@ -12911,19 +13095,19 @@ function readSkill(skillMd, source) {
12911
13095
  }
12912
13096
  }
12913
13097
  function scanSkillsDir(dir, source, out) {
12914
- if (!(0, import_node_fs35.existsSync)(dir)) return;
13098
+ if (!(0, import_node_fs36.existsSync)(dir)) return;
12915
13099
  let entries;
12916
13100
  try {
12917
- entries = (0, import_node_fs35.readdirSync)(dir);
13101
+ entries = (0, import_node_fs36.readdirSync)(dir);
12918
13102
  } catch {
12919
13103
  return;
12920
13104
  }
12921
13105
  for (const entry of entries) {
12922
13106
  if (entry.startsWith(".")) continue;
12923
- const skillMd = (0, import_node_path32.join)(dir, entry, "SKILL.md");
12924
- if (!(0, import_node_fs35.existsSync)(skillMd)) continue;
13107
+ const skillMd = (0, import_node_path33.join)(dir, entry, "SKILL.md");
13108
+ if (!(0, import_node_fs36.existsSync)(skillMd)) continue;
12925
13109
  try {
12926
- if (!(0, import_node_fs35.statSync)(skillMd).isFile()) continue;
13110
+ if (!(0, import_node_fs36.statSync)(skillMd).isFile()) continue;
12927
13111
  } catch {
12928
13112
  continue;
12929
13113
  }
@@ -12932,24 +13116,24 @@ function scanSkillsDir(dir, source, out) {
12932
13116
  }
12933
13117
  }
12934
13118
  function scanClaudePluginSkills(pluginsRoot, out) {
12935
- if (!(0, import_node_fs35.existsSync)(pluginsRoot)) return;
13119
+ if (!(0, import_node_fs36.existsSync)(pluginsRoot)) return;
12936
13120
  const walk = (dir, depth, lookingForSkillsDir) => {
12937
13121
  if (depth > 7) return;
12938
13122
  let entries;
12939
13123
  try {
12940
- entries = (0, import_node_fs35.readdirSync)(dir);
13124
+ entries = (0, import_node_fs36.readdirSync)(dir);
12941
13125
  } catch {
12942
13126
  return;
12943
13127
  }
12944
13128
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
12945
- const skill = readSkill((0, import_node_path32.join)(dir, "SKILL.md"), "cli");
13129
+ const skill = readSkill((0, import_node_path33.join)(dir, "SKILL.md"), "cli");
12946
13130
  if (skill) out.push(skill);
12947
13131
  }
12948
13132
  for (const entry of entries) {
12949
13133
  if (entry === "node_modules" || entry === ".git") continue;
12950
- const full = (0, import_node_path32.join)(dir, entry);
13134
+ const full = (0, import_node_path33.join)(dir, entry);
12951
13135
  try {
12952
- if (!(0, import_node_fs35.statSync)(full).isDirectory()) continue;
13136
+ if (!(0, import_node_fs36.statSync)(full).isDirectory()) continue;
12953
13137
  } catch {
12954
13138
  continue;
12955
13139
  }
@@ -12967,17 +13151,17 @@ function discoverSkills(worktreePath) {
12967
13151
  const home = (0, import_node_os11.homedir)();
12968
13152
  const collected = [];
12969
13153
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
12970
- scanSkillsDir((0, import_node_path32.join)(worktreePath, rel), "workspace", collected);
13154
+ scanSkillsDir((0, import_node_path33.join)(worktreePath, rel), "workspace", collected);
12971
13155
  }
12972
13156
  for (const abs of [
12973
- (0, import_node_path32.join)(home, ".claude/skills"),
12974
- (0, import_node_path32.join)(home, ".cursor/skills"),
12975
- (0, import_node_path32.join)(home, ".sideboard/skills"),
12976
- (0, import_node_path32.join)(home, ".brightsy/skills")
13157
+ (0, import_node_path33.join)(home, ".claude/skills"),
13158
+ (0, import_node_path33.join)(home, ".cursor/skills"),
13159
+ (0, import_node_path33.join)(home, ".sideboard/skills"),
13160
+ (0, import_node_path33.join)(home, ".brightsy/skills")
12977
13161
  ]) {
12978
13162
  scanSkillsDir(abs, "user", collected);
12979
13163
  }
12980
- scanClaudePluginSkills((0, import_node_path32.join)(home, ".claude/plugins"), collected);
13164
+ scanClaudePluginSkills((0, import_node_path33.join)(home, ".claude/plugins"), collected);
12981
13165
  const rank = { workspace: 0, user: 1, cli: 2 };
12982
13166
  const byCommand = /* @__PURE__ */ new Map();
12983
13167
  for (const skill of collected) {
@@ -12989,7 +13173,7 @@ function discoverSkills(worktreePath) {
12989
13173
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
12990
13174
  }
12991
13175
  function readSkillBody(skillPath, maxChars = 12e3) {
12992
- const raw = (0, import_node_fs35.readFileSync)(skillPath, "utf8");
13176
+ const raw = (0, import_node_fs36.readFileSync)(skillPath, "utf8");
12993
13177
  if (raw.startsWith("---")) {
12994
13178
  const end = raw.indexOf("\n---", 3);
12995
13179
  if (end >= 0) {
@@ -13082,8 +13266,8 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
13082
13266
  }
13083
13267
 
13084
13268
  // src/composer/stage-files.ts
13085
- var import_node_fs36 = require("fs");
13086
- var import_node_path33 = require("path");
13269
+ var import_node_fs37 = require("fs");
13270
+ var import_node_path34 = require("path");
13087
13271
  var import_node_crypto7 = require("crypto");
13088
13272
  init_workspace_scratch();
13089
13273
  var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
@@ -13109,7 +13293,7 @@ var IMAGE_MIME_BY_EXT = {
13109
13293
  var MAX_INLINE_BYTES = 4e5;
13110
13294
  var MAX_PREVIEW_BYTES = 5e6;
13111
13295
  function fileExtension(filePath) {
13112
- const base = (0, import_node_path33.basename)(filePath).toLowerCase();
13296
+ const base = (0, import_node_path34.basename)(filePath).toLowerCase();
13113
13297
  return base.includes(".") ? base.split(".").pop() || "" : "";
13114
13298
  }
13115
13299
  function isImageFilePath(filePath) {
@@ -13119,22 +13303,22 @@ function imageMimeType(filePath) {
13119
13303
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
13120
13304
  }
13121
13305
  function ensureAttachmentsDir(worktreePath) {
13122
- const dir = (0, import_node_path33.join)(worktreePath, ATTACHMENTS_DIR);
13123
- (0, import_node_fs36.mkdirSync)(dir, { recursive: true });
13124
- const gi = (0, import_node_path33.join)(dir, ".gitignore");
13125
- if (!(0, import_node_fs36.existsSync)(gi)) {
13126
- (0, import_node_fs36.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
13306
+ const dir = (0, import_node_path34.join)(worktreePath, ATTACHMENTS_DIR);
13307
+ (0, import_node_fs37.mkdirSync)(dir, { recursive: true });
13308
+ const gi = (0, import_node_path34.join)(dir, ".gitignore");
13309
+ if (!(0, import_node_fs37.existsSync)(gi)) {
13310
+ (0, import_node_fs37.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
13127
13311
  }
13128
13312
  return dir;
13129
13313
  }
13130
13314
  function uniqueAttachmentName(dir, originalName) {
13131
13315
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
13132
- if (!(0, import_node_fs36.existsSync)((0, import_node_path33.join)(dir, safe))) return safe;
13133
- const ext = (0, import_node_path33.extname)(safe);
13316
+ if (!(0, import_node_fs37.existsSync)((0, import_node_path34.join)(dir, safe))) return safe;
13317
+ const ext = (0, import_node_path34.extname)(safe);
13134
13318
  const stem = ext ? safe.slice(0, -ext.length) : safe;
13135
13319
  for (let i = 1; i < 1e4; i++) {
13136
13320
  const candidate = `${stem}-${i}${ext}`;
13137
- if (!(0, import_node_fs36.existsSync)((0, import_node_path33.join)(dir, candidate))) return candidate;
13321
+ if (!(0, import_node_fs37.existsSync)((0, import_node_path34.join)(dir, candidate))) return candidate;
13138
13322
  }
13139
13323
  return `${stem}-${(0, import_node_crypto7.randomUUID)()}${ext}`;
13140
13324
  }
@@ -13190,15 +13374,15 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
13190
13374
  const dir = ensureAttachmentsDir(worktreePath);
13191
13375
  const out = [];
13192
13376
  for (const abs of absolutePaths) {
13193
- const originalName = (0, import_node_path33.basename)(abs);
13377
+ const originalName = (0, import_node_path34.basename)(abs);
13194
13378
  try {
13195
- const st = (0, import_node_fs36.statSync)(abs);
13379
+ const st = (0, import_node_fs37.statSync)(abs);
13196
13380
  if (!st.isFile()) continue;
13197
13381
  const name = uniqueAttachmentName(dir, originalName);
13198
- const destAbs = (0, import_node_path33.join)(dir, name);
13199
- (0, import_node_fs36.copyFileSync)(abs, destAbs);
13382
+ const destAbs = (0, import_node_path34.join)(dir, name);
13383
+ (0, import_node_fs37.copyFileSync)(abs, destAbs);
13200
13384
  const rel = `${ATTACHMENTS_DIR}/${name}`;
13201
- const buf = (0, import_node_fs36.readFileSync)(destAbs);
13385
+ const buf = (0, import_node_fs37.readFileSync)(destAbs);
13202
13386
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
13203
13387
  } catch (err) {
13204
13388
  out.push({
@@ -13220,8 +13404,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
13220
13404
  try {
13221
13405
  const buf = Buffer.from(item.dataBase64, "base64");
13222
13406
  const name = uniqueAttachmentName(dir, originalName);
13223
- const destAbs = (0, import_node_path33.join)(dir, name);
13224
- (0, import_node_fs36.writeFileSync)(destAbs, buf);
13407
+ const destAbs = (0, import_node_path34.join)(dir, name);
13408
+ (0, import_node_fs37.writeFileSync)(destAbs, buf);
13225
13409
  const rel = `${ATTACHMENTS_DIR}/${name}`;
13226
13410
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
13227
13411
  } catch (err) {
@@ -13241,18 +13425,18 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
13241
13425
  if (!rel || rel.includes("..") || rel.startsWith("/")) {
13242
13426
  out.push({
13243
13427
  id: (0, import_node_crypto7.randomUUID)(),
13244
- name: (0, import_node_path33.basename)(rel) || "file",
13428
+ name: (0, import_node_path34.basename)(rel) || "file",
13245
13429
  kind: "file",
13246
13430
  content: `(invalid path: ${rel})`
13247
13431
  });
13248
13432
  continue;
13249
13433
  }
13250
- const name = (0, import_node_path33.basename)(rel);
13434
+ const name = (0, import_node_path34.basename)(rel);
13251
13435
  try {
13252
- const abs = (0, import_node_path33.join)(worktreePath, rel);
13253
- const st = (0, import_node_fs36.statSync)(abs);
13436
+ const abs = (0, import_node_path34.join)(worktreePath, rel);
13437
+ const st = (0, import_node_fs37.statSync)(abs);
13254
13438
  if (!st.isFile()) continue;
13255
- const buf = (0, import_node_fs36.readFileSync)(abs);
13439
+ const buf = (0, import_node_fs37.readFileSync)(abs);
13256
13440
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
13257
13441
  } catch (err) {
13258
13442
  out.push({
@@ -13267,8 +13451,8 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
13267
13451
  }
13268
13452
 
13269
13453
  // src/agents/instructions.ts
13270
- var import_node_fs37 = require("fs");
13271
- var import_node_path34 = require("path");
13454
+ var import_node_fs38 = require("fs");
13455
+ var import_node_path35 = require("path");
13272
13456
  init_git_auth_mode();
13273
13457
  init_worktree_labels();
13274
13458
  function normPath3(p) {
@@ -13548,7 +13732,7 @@ var Orchestrator = class {
13548
13732
  }
13549
13733
  continue;
13550
13734
  }
13551
- if (!(0, import_node_fs41.existsSync)(thread.worktreePath)) {
13735
+ if (!(0, import_node_fs42.existsSync)(thread.worktreePath)) {
13552
13736
  setStatus(thread.id, "broken", "Worktree missing on disk");
13553
13737
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
13554
13738
  continue;
@@ -13799,7 +13983,9 @@ var Orchestrator = class {
13799
13983
  updateThread(thread.id, patch);
13800
13984
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
13801
13985
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
13802
- void this.drainQueue(thread.id);
13986
+ if (thisProcessShouldDrainAgentQueues()) {
13987
+ void this.drainQueue(thread.id);
13988
+ }
13803
13989
  return this.requireThread(thread.id);
13804
13990
  });
13805
13991
  }
@@ -13852,10 +14038,10 @@ var Orchestrator = class {
13852
14038
  async sendQueuedMessageNow(threadRef, index) {
13853
14039
  const thread = this.requireThread(threadRef);
13854
14040
  const promoted = await withThreadLock(thread.id, async () => {
13855
- const current = this.requireThread(thread.id);
13856
- if (index < 0 || index >= current.queue.length) return false;
13857
- const item = current.queue[index];
13858
- const rest = current.queue.filter((_, i) => i !== index);
14041
+ const current2 = this.requireThread(thread.id);
14042
+ if (index < 0 || index >= current2.queue.length) return false;
14043
+ const item = current2.queue[index];
14044
+ const rest = current2.queue.filter((_, i) => i !== index);
13859
14045
  const queue = [item, ...rest];
13860
14046
  this.haltDrain.delete(thread.id);
13861
14047
  updateThread(thread.id, { queue });
@@ -13863,10 +14049,16 @@ var Orchestrator = class {
13863
14049
  return true;
13864
14050
  });
13865
14051
  if (!promoted) return this.requireThread(thread.id);
14052
+ const current = this.requireThread(thread.id);
13866
14053
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
14054
+ const livePid = current.agentPid;
14055
+ const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
13867
14056
  if (inFlight) {
13868
14057
  this.stop(thread.id, { clearQueue: false, continueQueue: true });
13869
14058
  } else {
14059
+ if (foreignLive) {
14060
+ this.stop(thread.id, { clearQueue: false, continueQueue: true });
14061
+ }
13870
14062
  void this.drainQueue(thread.id);
13871
14063
  }
13872
14064
  return this.requireThread(thread.id);
@@ -14117,19 +14309,16 @@ var Orchestrator = class {
14117
14309
  }
14118
14310
  let lastStderr = summarizeTurnStderr(stderrTail);
14119
14311
  let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
14120
- if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "brightsy") {
14312
+ if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent !== "brightsy" && shouldRetryFailedAgentTurn(detail, {
14313
+ hasSession: Boolean(this.requireThread(threadId).sessionId)
14314
+ })) {
14121
14315
  updateThread(threadId, { sessionId: null });
14122
- pushTurnStderr(
14123
- stderrTail,
14124
- "Agent session missing \u2014 starting a fresh session"
14125
- );
14316
+ const retryNote = looksLikeInvalidAgentSession(detail) ? "Agent session missing \u2014 starting a fresh session" : "Agent runner crashed \u2014 restarting Node once";
14317
+ pushTurnStderr(stderrTail, retryNote);
14126
14318
  this.emit({
14127
14319
  type: "turn_output",
14128
14320
  threadId,
14129
- event: {
14130
- type: "stderr",
14131
- data: "Agent session missing \u2014 starting a fresh session"
14132
- }
14321
+ event: { type: "stderr", data: retryNote }
14133
14322
  });
14134
14323
  const retryThread = this.requireThread(threadId);
14135
14324
  const prior = retryThread.messages.slice(0, -1);
@@ -14178,10 +14367,7 @@ var Orchestrator = class {
14178
14367
  lastStderr = summarizeTurnStderr(stderrTail);
14179
14368
  detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
14180
14369
  }
14181
- let chatText = assistantText;
14182
- if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
14183
- chatText = humanizeAgentFailDetail(detail);
14184
- }
14370
+ let chatText = this.stoppedTurns.has(threadId) ? assistantText : turnFailChatText({ exitCode, assistantText, detail });
14185
14371
  if (chatText || parts.length > 0) {
14186
14372
  appendMessage(threadId, {
14187
14373
  role: "agent",
@@ -14301,6 +14487,13 @@ var Orchestrator = class {
14301
14487
  if (handle) handle.kill();
14302
14488
  const proc = this.processes.get(`${thread.id}:agent`);
14303
14489
  if (proc) proc.kill();
14490
+ const pid = thread.agentPid;
14491
+ if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) {
14492
+ try {
14493
+ process.kill(pid, "SIGTERM");
14494
+ } catch {
14495
+ }
14496
+ }
14304
14497
  const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
14305
14498
  if (stopped.status === "stopped") {
14306
14499
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
@@ -14582,10 +14775,13 @@ var Orchestrator = class {
14582
14775
  getTurnResult(threadRef) {
14583
14776
  const thread = this.requireThread(threadRef);
14584
14777
  const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
14778
+ const lastError = thread.lastError ?? null;
14779
+ const text3 = (lastAgent?.text ?? "").trim() || (thread.status === "error" ? lastError ?? "" : "");
14585
14780
  return {
14586
- text: lastAgent?.text ?? "",
14781
+ text: text3,
14587
14782
  status: thread.status,
14588
- sessionId: thread.sessionId
14783
+ sessionId: thread.sessionId,
14784
+ lastError
14589
14785
  };
14590
14786
  }
14591
14787
  assertNotGlobal(thread, action) {
@@ -15081,7 +15277,7 @@ var Orchestrator = class {
15081
15277
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
15082
15278
  return restored2;
15083
15279
  }
15084
- if (!(0, import_node_fs41.existsSync)(thread.worktreePath)) {
15280
+ if (!(0, import_node_fs42.existsSync)(thread.worktreePath)) {
15085
15281
  const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
15086
15282
  const { execa: execa7 } = await import("execa");
15087
15283
  const slug = thread.worktreePath.split("/").pop();
@@ -16321,7 +16517,7 @@ async function startMcpServer() {
16321
16517
  async () => {
16322
16518
  const threads = orch.getThreads(true);
16323
16519
  const lines = threads.map((t) => {
16324
- const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path38.basename)(t.repoPath) || t.repoPath;
16520
+ const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path39.basename)(t.repoPath) || t.repoPath;
16325
16521
  return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title} sideboard://thread/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}`;
16326
16522
  });
16327
16523
  return {
@@ -16678,7 +16874,7 @@ async function startMcpServer() {
16678
16874
  );
16679
16875
  server.tool(
16680
16876
  "wait_for_turn",
16681
- "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread or ask_git to read the agent reply.",
16877
+ "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread or ask_git. Returns status, text, and lastError. If status is error, lastError/text is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success.",
16682
16878
  {
16683
16879
  ref: import_zod3.z.string(),
16684
16880
  timeoutMs: import_zod3.z.number().optional()
@@ -16693,7 +16889,8 @@ async function startMcpServer() {
16693
16889
  text: JSON.stringify({
16694
16890
  id: thread.id,
16695
16891
  status: result.status,
16696
- text: result.text
16892
+ text: result.text,
16893
+ lastError: result.lastError
16697
16894
  })
16698
16895
  }
16699
16896
  ]
@@ -16702,7 +16899,7 @@ async function startMcpServer() {
16702
16899
  );
16703
16900
  server.tool(
16704
16901
  "get_turn_result",
16705
- "Final assistant message only (not full transcript)",
16902
+ "Final assistant message (and lastError when the turn failed). Not the full transcript.",
16706
16903
  { ref: import_zod3.z.string() },
16707
16904
  async ({ ref }) => {
16708
16905
  const result = orch.getTurnResult(ref);