@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
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-UYQYK2RY.js";
7
+ } from "./chunk-MHJV4WS3.js";
8
8
  import {
9
9
  BRIGHTSY_MCP_ALLOWED_TOOLS,
10
10
  CLAUDE_MODEL_CATALOG,
@@ -65,7 +65,7 @@ import {
65
65
  threadRequestsBrightsyMcp,
66
66
  totalTokens,
67
67
  writeInjectedMcpConfig
68
- } from "./chunk-MBP3XG57.js";
68
+ } from "./chunk-OXC3MEFI.js";
69
69
  import {
70
70
  CLOUD_COORDINATOR_BUSY_REPLY,
71
71
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -93,7 +93,7 @@ import {
93
93
  parseForceStopMessage,
94
94
  slackCoordinatorSourceRef,
95
95
  takenTeamSlugsForOrchestration
96
- } from "./chunk-FO67IJTY.js";
96
+ } from "./chunk-GR4JKWR4.js";
97
97
  import {
98
98
  COORDINATOR_TOOL_PLAYBOOK,
99
99
  SLACK_REPLY_FORMATTING,
@@ -102,7 +102,7 @@ import {
102
102
  enrichWorkspacesWithGithub,
103
103
  ensureGlobalCoordinatorCwd,
104
104
  formatWorkspaceInventory
105
- } from "./chunk-XUWDLRAE.js";
105
+ } from "./chunk-HYKZEP5A.js";
106
106
  import {
107
107
  brightsyConfigPath,
108
108
  brightsyMcpServerName,
@@ -133,13 +133,14 @@ import {
133
133
  cursorSdkMessageToEvents,
134
134
  fallbackTurnFailDetail,
135
135
  formatTurnExitError,
136
- humanizeAgentFailDetail,
137
136
  looksLikeAgentFailureMessage,
138
137
  looksLikeInvalidAgentSession,
139
138
  parseCursorRunnerLine,
140
139
  pushTurnStderr,
141
- summarizeTurnStderr
142
- } from "./chunk-GXSYI7FH.js";
140
+ shouldRetryFailedAgentTurn,
141
+ summarizeTurnStderr,
142
+ turnFailChatText
143
+ } from "./chunk-HUKCGRAT.js";
143
144
  import {
144
145
  caffeinateHoldPath,
145
146
  getCaffeinateHold,
@@ -367,6 +368,52 @@ import {
367
368
  withExportedPath
368
369
  } from "./chunk-LGXBYZZA.js";
369
370
 
371
+ // src/store/desktop-host.ts
372
+ import { readFileSync, unlinkSync, writeFileSync } from "fs";
373
+ import { join } from "path";
374
+ function desktopHostPidPath() {
375
+ return join(appDataDir(), "desktop-host.pid");
376
+ }
377
+ function claimDesktopHost(pid = process.pid) {
378
+ writeFileSync(desktopHostPidPath(), `${pid}
379
+ `, "utf8");
380
+ }
381
+ function releaseDesktopHost(pid = process.pid) {
382
+ if (readDesktopHostPid() !== pid) return;
383
+ try {
384
+ unlinkSync(desktopHostPidPath());
385
+ } catch {
386
+ }
387
+ }
388
+ function readDesktopHostPid() {
389
+ try {
390
+ const pid = Number.parseInt(readFileSync(desktopHostPidPath(), "utf8").trim(), 10);
391
+ if (!Number.isFinite(pid) || pid <= 0) return null;
392
+ return pid;
393
+ } catch {
394
+ return null;
395
+ }
396
+ }
397
+ function pidAlive(pid) {
398
+ try {
399
+ process.kill(pid, 0);
400
+ return true;
401
+ } catch {
402
+ return false;
403
+ }
404
+ }
405
+ function isDesktopHostAlive() {
406
+ const pid = readDesktopHostPid();
407
+ return pid != null && pidAlive(pid);
408
+ }
409
+ function isThisProcessDesktopHost() {
410
+ return readDesktopHostPid() === process.pid && pidAlive(process.pid);
411
+ }
412
+ function thisProcessShouldDrainAgentQueues() {
413
+ if (isThisProcessDesktopHost()) return true;
414
+ return !isDesktopHostAlive();
415
+ }
416
+
370
417
  // src/git/agent-git-actions.ts
371
418
  var AGENT_GIT_ACTIONS = [
372
419
  "commit-push",
@@ -1339,9 +1386,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
1339
1386
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
1340
1387
  );
1341
1388
  }
1342
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-RSQXRLT7.js");
1389
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-KYEBFWKB.js");
1343
1390
  if (isGlobalThread2(thread)) {
1344
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-AKEY4WSO.js");
1391
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-HHRKQWCX.js");
1345
1392
  ensureGlobalCoordinatorCwd2(
1346
1393
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
1347
1394
  );
@@ -1452,8 +1499,8 @@ async function spawnAgentTurn(thread, input, onEvent) {
1452
1499
  }
1453
1500
 
1454
1501
  // src/agents/instructions.ts
1455
- import { existsSync, readFileSync, statSync } from "fs";
1456
- import { join } from "path";
1502
+ import { existsSync, readFileSync as readFileSync2, statSync } from "fs";
1503
+ import { join as join2 } from "path";
1457
1504
  function normPath(p) {
1458
1505
  return p.replace(/\/+$/, "");
1459
1506
  }
@@ -1622,11 +1669,11 @@ function loadAgentInstructions(worktreePath, agent) {
1622
1669
  const out = [];
1623
1670
  for (const rel of candidates) {
1624
1671
  if (seenPaths.has(rel)) continue;
1625
- const abs = join(worktreePath, rel);
1672
+ const abs = join2(worktreePath, rel);
1626
1673
  if (!existsSync(abs)) continue;
1627
1674
  try {
1628
1675
  if (!statSync(abs).isFile()) continue;
1629
- let content = readFileSync(abs, "utf8");
1676
+ let content = readFileSync2(abs, "utf8");
1630
1677
  if (!content.trim()) continue;
1631
1678
  if (content.length > MAX_CHARS_PER_FILE) {
1632
1679
  content = `${content.slice(0, MAX_CHARS_PER_FILE)}
@@ -1706,10 +1753,10 @@ import {
1706
1753
  existsSync as existsSync2,
1707
1754
  mkdirSync,
1708
1755
  readdirSync,
1709
- readFileSync as readFileSync2
1756
+ readFileSync as readFileSync3
1710
1757
  } from "fs";
1711
1758
  import { createServer as createServer2 } from "net";
1712
- import { basename, dirname, join as join2 } from "path";
1759
+ import { basename, dirname, join as join3 } from "path";
1713
1760
  import { execa as execa2 } from "execa";
1714
1761
  import { createInterface as createInterface2 } from "readline";
1715
1762
  var PORT_RANGE_SIZE = 10;
@@ -1718,9 +1765,9 @@ function matchSimpleGlob(pattern, name) {
1718
1765
  return new RegExp(`^${escaped}$`).test(name);
1719
1766
  }
1720
1767
  function readWorktreeInclude(repoPath) {
1721
- const path2 = join2(repoPath, ".worktreeinclude");
1768
+ const path2 = join3(repoPath, ".worktreeinclude");
1722
1769
  if (!existsSync2(path2)) return [];
1723
- return readFileSync2(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1770
+ return readFileSync3(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1724
1771
  }
1725
1772
  function resolveFilesToCopy(repoPath) {
1726
1773
  const fromInclude = readWorktreeInclude(repoPath);
@@ -1759,9 +1806,9 @@ function copyConfiguredFiles(repoPath, worktreePath) {
1759
1806
  const patterns = resolveFilesToCopy(repoPath);
1760
1807
  const copied = [];
1761
1808
  for (const rel of patterns) {
1762
- const src = join2(repoPath, rel);
1809
+ const src = join3(repoPath, rel);
1763
1810
  if (!existsSync2(src)) continue;
1764
- const dest = join2(worktreePath, rel);
1811
+ const dest = join3(worktreePath, rel);
1765
1812
  mkdirSync(dirname(dest), { recursive: true });
1766
1813
  copyFileSync(src, dest);
1767
1814
  copied.push(rel);
@@ -2056,8 +2103,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2056
2103
  }
2057
2104
 
2058
2105
  // src/diff/diff.ts
2059
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, statSync as statSync2, writeFileSync } from "fs";
2060
- import { dirname as dirname2, join as join3 } from "path";
2106
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
2107
+ import { dirname as dirname2, join as join4 } from "path";
2061
2108
  async function inspectGitWorktree(worktreePath) {
2062
2109
  if (!worktreePath || !existsSync3(worktreePath)) return "missing_worktree";
2063
2110
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
@@ -2203,11 +2250,11 @@ new file mode 100644
2203
2250
  };
2204
2251
  }
2205
2252
  async function untrackedPatch(worktreePath, path2, maxHunk) {
2206
- const abs = join3(worktreePath, path2);
2253
+ const abs = join4(worktreePath, path2);
2207
2254
  try {
2208
2255
  const st = statSync2(abs);
2209
2256
  if (st.isFile() && st.size > maxHunk) {
2210
- const buf = readFileSync3(abs).subarray(0, maxHunk);
2257
+ const buf = readFileSync4(abs).subarray(0, maxHunk);
2211
2258
  return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
2212
2259
  }
2213
2260
  } catch {
@@ -2708,7 +2755,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
2708
2755
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
2709
2756
  assertSafeRelativePath(relativePath);
2710
2757
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
2711
- const abs = join3(worktreePath, relativePath);
2758
+ const abs = join4(worktreePath, relativePath);
2712
2759
  const st = statSync2(abs);
2713
2760
  if (!st.isFile()) {
2714
2761
  throw new Error(`Not a file: ${relativePath}`);
@@ -2718,7 +2765,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
2718
2765
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
2719
2766
  );
2720
2767
  }
2721
- const buf = readFileSync3(abs);
2768
+ const buf = readFileSync4(abs);
2722
2769
  return {
2723
2770
  path: relativePath,
2724
2771
  contentBase64: buf.toString("base64"),
@@ -2728,12 +2775,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
2728
2775
  function readWorktreeFile(worktreePath, relativePath, opts) {
2729
2776
  assertSafeRelativePath(relativePath);
2730
2777
  const maxBytes = opts?.maxBytes ?? 2e5;
2731
- const abs = join3(worktreePath, relativePath);
2778
+ const abs = join4(worktreePath, relativePath);
2732
2779
  const st = statSync2(abs);
2733
2780
  if (!st.isFile()) {
2734
2781
  throw new Error(`Not a file: ${relativePath}`);
2735
2782
  }
2736
- const buf = readFileSync3(abs);
2783
+ const buf = readFileSync4(abs);
2737
2784
  if (isImageRelativePath(relativePath)) {
2738
2785
  const maxImageBytes = Math.max(maxBytes, 15e6);
2739
2786
  const truncated2 = buf.length > maxImageBytes;
@@ -2776,9 +2823,9 @@ function assertSafeRelativePath(relativePath) {
2776
2823
  }
2777
2824
  function writeWorktreeFile(worktreePath, relativePath, content) {
2778
2825
  assertSafeRelativePath(relativePath);
2779
- const abs = join3(worktreePath, relativePath);
2826
+ const abs = join4(worktreePath, relativePath);
2780
2827
  mkdirSync2(dirname2(abs), { recursive: true });
2781
- writeFileSync(abs, content, "utf8");
2828
+ writeFileSync2(abs, content, "utf8");
2782
2829
  return { path: relativePath };
2783
2830
  }
2784
2831
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -2797,9 +2844,9 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
2797
2844
  }
2798
2845
 
2799
2846
  // src/skills/discover.ts
2800
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
2847
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
2801
2848
  import { homedir } from "os";
2802
- import { join as join4 } from "path";
2849
+ import { join as join5 } from "path";
2803
2850
  function toCommand(name) {
2804
2851
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2805
2852
  }
@@ -2831,7 +2878,7 @@ function parseFrontmatter(content) {
2831
2878
  }
2832
2879
  function readSkill(skillMd, source) {
2833
2880
  try {
2834
- const content = readFileSync4(skillMd, "utf8");
2881
+ const content = readFileSync5(skillMd, "utf8");
2835
2882
  const { name: fmName, description } = parseFrontmatter(content);
2836
2883
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
2837
2884
  const name = fmName || dirName;
@@ -2859,7 +2906,7 @@ function scanSkillsDir(dir, source, out) {
2859
2906
  }
2860
2907
  for (const entry of entries) {
2861
2908
  if (entry.startsWith(".")) continue;
2862
- const skillMd = join4(dir, entry, "SKILL.md");
2909
+ const skillMd = join5(dir, entry, "SKILL.md");
2863
2910
  if (!existsSync4(skillMd)) continue;
2864
2911
  try {
2865
2912
  if (!statSync3(skillMd).isFile()) continue;
@@ -2881,12 +2928,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
2881
2928
  return;
2882
2929
  }
2883
2930
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
2884
- const skill = readSkill(join4(dir, "SKILL.md"), "cli");
2931
+ const skill = readSkill(join5(dir, "SKILL.md"), "cli");
2885
2932
  if (skill) out.push(skill);
2886
2933
  }
2887
2934
  for (const entry of entries) {
2888
2935
  if (entry === "node_modules" || entry === ".git") continue;
2889
- const full = join4(dir, entry);
2936
+ const full = join5(dir, entry);
2890
2937
  try {
2891
2938
  if (!statSync3(full).isDirectory()) continue;
2892
2939
  } catch {
@@ -2906,17 +2953,17 @@ function discoverSkills(worktreePath) {
2906
2953
  const home = homedir();
2907
2954
  const collected = [];
2908
2955
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
2909
- scanSkillsDir(join4(worktreePath, rel), "workspace", collected);
2956
+ scanSkillsDir(join5(worktreePath, rel), "workspace", collected);
2910
2957
  }
2911
2958
  for (const abs of [
2912
- join4(home, ".claude/skills"),
2913
- join4(home, ".cursor/skills"),
2914
- join4(home, ".sideboard/skills"),
2915
- join4(home, ".brightsy/skills")
2959
+ join5(home, ".claude/skills"),
2960
+ join5(home, ".cursor/skills"),
2961
+ join5(home, ".sideboard/skills"),
2962
+ join5(home, ".brightsy/skills")
2916
2963
  ]) {
2917
2964
  scanSkillsDir(abs, "user", collected);
2918
2965
  }
2919
- scanClaudePluginSkills(join4(home, ".claude/plugins"), collected);
2966
+ scanClaudePluginSkills(join5(home, ".claude/plugins"), collected);
2920
2967
  const rank = { workspace: 0, user: 1, cli: 2 };
2921
2968
  const byCommand = /* @__PURE__ */ new Map();
2922
2969
  for (const skill of collected) {
@@ -2928,7 +2975,7 @@ function discoverSkills(worktreePath) {
2928
2975
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
2929
2976
  }
2930
2977
  function readSkillBody(skillPath, maxChars = 12e3) {
2931
- const raw = readFileSync4(skillPath, "utf8");
2978
+ const raw = readFileSync5(skillPath, "utf8");
2932
2979
  if (raw.startsWith("---")) {
2933
2980
  const end = raw.indexOf("\n---", 3);
2934
2981
  if (end >= 0) {
@@ -3069,8 +3116,8 @@ function buildDiffCommentAttachment(input) {
3069
3116
  }
3070
3117
 
3071
3118
  // src/composer/stage-files.ts
3072
- import { copyFileSync as copyFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
3073
- import { basename as basename2, extname, join as join5 } from "path";
3119
+ import { copyFileSync as copyFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
3120
+ import { basename as basename2, extname, join as join6 } from "path";
3074
3121
  import { randomUUID } from "crypto";
3075
3122
  var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
3076
3123
  "png",
@@ -3105,22 +3152,22 @@ function imageMimeType(filePath) {
3105
3152
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
3106
3153
  }
3107
3154
  function ensureAttachmentsDir(worktreePath) {
3108
- const dir = join5(worktreePath, ATTACHMENTS_DIR);
3155
+ const dir = join6(worktreePath, ATTACHMENTS_DIR);
3109
3156
  mkdirSync3(dir, { recursive: true });
3110
- const gi = join5(dir, ".gitignore");
3157
+ const gi = join6(dir, ".gitignore");
3111
3158
  if (!existsSync5(gi)) {
3112
- writeFileSync2(gi, attachmentsGitignoreBody(), "utf8");
3159
+ writeFileSync3(gi, attachmentsGitignoreBody(), "utf8");
3113
3160
  }
3114
3161
  return dir;
3115
3162
  }
3116
3163
  function uniqueAttachmentName(dir, originalName) {
3117
3164
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
3118
- if (!existsSync5(join5(dir, safe))) return safe;
3165
+ if (!existsSync5(join6(dir, safe))) return safe;
3119
3166
  const ext = extname(safe);
3120
3167
  const stem = ext ? safe.slice(0, -ext.length) : safe;
3121
3168
  for (let i = 1; i < 1e4; i++) {
3122
3169
  const candidate = `${stem}-${i}${ext}`;
3123
- if (!existsSync5(join5(dir, candidate))) return candidate;
3170
+ if (!existsSync5(join6(dir, candidate))) return candidate;
3124
3171
  }
3125
3172
  return `${stem}-${randomUUID()}${ext}`;
3126
3173
  }
@@ -3183,7 +3230,7 @@ function attachmentFromAbsolutePath(absolutePath) {
3183
3230
  content: `(not a file: ${absolutePath})`
3184
3231
  };
3185
3232
  }
3186
- const buf = readFileSync5(absolutePath);
3233
+ const buf = readFileSync6(absolutePath);
3187
3234
  return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
3188
3235
  } catch (err) {
3189
3236
  return {
@@ -3204,10 +3251,10 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
3204
3251
  const st = statSync4(abs);
3205
3252
  if (!st.isFile()) continue;
3206
3253
  const name = uniqueAttachmentName(dir, originalName);
3207
- const destAbs = join5(dir, name);
3254
+ const destAbs = join6(dir, name);
3208
3255
  copyFileSync2(abs, destAbs);
3209
3256
  const rel = `${ATTACHMENTS_DIR}/${name}`;
3210
- const buf = readFileSync5(destAbs);
3257
+ const buf = readFileSync6(destAbs);
3211
3258
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
3212
3259
  } catch (err) {
3213
3260
  out.push({
@@ -3229,8 +3276,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
3229
3276
  try {
3230
3277
  const buf = Buffer.from(item.dataBase64, "base64");
3231
3278
  const name = uniqueAttachmentName(dir, originalName);
3232
- const destAbs = join5(dir, name);
3233
- writeFileSync2(destAbs, buf);
3279
+ const destAbs = join6(dir, name);
3280
+ writeFileSync3(destAbs, buf);
3234
3281
  const rel = `${ATTACHMENTS_DIR}/${name}`;
3235
3282
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
3236
3283
  } catch (err) {
@@ -3274,10 +3321,10 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
3274
3321
  }
3275
3322
  const name = basename2(rel);
3276
3323
  try {
3277
- const abs = join5(worktreePath, rel);
3324
+ const abs = join6(worktreePath, rel);
3278
3325
  const st = statSync4(abs);
3279
3326
  if (!st.isFile()) continue;
3280
- const buf = readFileSync5(abs);
3327
+ const buf = readFileSync6(abs);
3281
3328
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
3282
3329
  } catch (err) {
3283
3330
  out.push({
@@ -3834,7 +3881,7 @@ async function createThread(input, _onSetupLine) {
3834
3881
  return readThread(thread.id) ?? thread;
3835
3882
  }
3836
3883
  async function listLinearIssues(agent, repoPath) {
3837
- const { getAdapter: getAdapter2 } = await import("./agents-3MWWWSMF.js");
3884
+ const { getAdapter: getAdapter2 } = await import("./agents-OXZDMPSE.js");
3838
3885
  await requireAgent(agent, { requireLinear: true });
3839
3886
  const adapter = getAdapter2(agent);
3840
3887
  if (!adapter.listLinearIssues) {
@@ -4300,20 +4347,20 @@ import {
4300
4347
  existsSync as existsSync8,
4301
4348
  mkdtempSync,
4302
4349
  readdirSync as readdirSync3,
4303
- readFileSync as readFileSync6,
4350
+ readFileSync as readFileSync7,
4304
4351
  rmSync
4305
4352
  } from "fs";
4306
4353
  import { tmpdir } from "os";
4307
- import { join as join6 } from "path";
4354
+ import { join as join7 } from "path";
4308
4355
  import { createRequire } from "module";
4309
- var CONDUCTOR_APP_SUPPORT = join6(
4356
+ var CONDUCTOR_APP_SUPPORT = join7(
4310
4357
  process.env.HOME ?? "",
4311
4358
  "Library",
4312
4359
  "Application Support",
4313
4360
  "com.conductor.app"
4314
4361
  );
4315
- var CONDUCTOR_DB = join6(CONDUCTOR_APP_SUPPORT, "conductor.db");
4316
- var CURSOR_SDK_STORE = join6(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4362
+ var CONDUCTOR_DB = join7(CONDUCTOR_APP_SUPPORT, "conductor.db");
4363
+ var CURSOR_SDK_STORE = join7(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4317
4364
  function openReadonlySqlite(file) {
4318
4365
  const req = createRequire(import.meta.url);
4319
4366
  const Database = req("better-sqlite3");
@@ -4340,11 +4387,11 @@ function resolveConductorCursorAgentId(workspacePath) {
4340
4387
  return null;
4341
4388
  }
4342
4389
  for (const hash of hashes) {
4343
- const agentsFile = join6(CURSOR_SDK_STORE, hash, "agents.ndjson");
4390
+ const agentsFile = join7(CURSOR_SDK_STORE, hash, "agents.ndjson");
4344
4391
  if (!existsSync8(agentsFile)) continue;
4345
4392
  let text3;
4346
4393
  try {
4347
- text3 = readFileSync6(agentsFile, "utf8");
4394
+ text3 = readFileSync7(agentsFile, "utf8");
4348
4395
  } catch {
4349
4396
  continue;
4350
4397
  }
@@ -4387,7 +4434,7 @@ async function adoptThread(input) {
4387
4434
  messages: input.messages ?? []
4388
4435
  });
4389
4436
  writeThread(thread);
4390
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-4ZY4QPWQ.js");
4437
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-NNPD7QVV.js");
4391
4438
  await ensureWorkspace2(repoPath);
4392
4439
  return thread;
4393
4440
  }
@@ -4398,8 +4445,8 @@ function listConductorWorkspaces() {
4398
4445
  if (!existsSync8(CONDUCTOR_DB)) {
4399
4446
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4400
4447
  }
4401
- const tmp = mkdtempSync(join6(tmpdir(), "sideboard-conductor-"));
4402
- const snapshot = join6(tmp, "conductor.db");
4448
+ const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
4449
+ const snapshot = join7(tmp, "conductor.db");
4403
4450
  try {
4404
4451
  copyFileSync3(CONDUCTOR_DB, snapshot);
4405
4452
  for (const suffix of ["-wal", "-shm"]) {
@@ -4489,8 +4536,8 @@ function importConductorWorkspace(workspaceId) {
4489
4536
  if (!existsSync8(CONDUCTOR_DB)) {
4490
4537
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4491
4538
  }
4492
- const tmp = mkdtempSync(join6(tmpdir(), "sideboard-conductor-"));
4493
- const snapshot = join6(tmp, "conductor.db");
4539
+ const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
4540
+ const snapshot = join7(tmp, "conductor.db");
4494
4541
  try {
4495
4542
  copyFileSync3(CONDUCTOR_DB, snapshot);
4496
4543
  for (const suffix of ["-wal", "-shm"]) {
@@ -4589,8 +4636,8 @@ async function importConductorWorkspaceAsync(workspaceId) {
4589
4636
  import { EventEmitter } from "events";
4590
4637
 
4591
4638
  // src/slack/outbound-watch.ts
4592
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
4593
- import { join as join9 } from "path";
4639
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
4640
+ import { join as join10 } from "path";
4594
4641
 
4595
4642
  // src/slack/api.ts
4596
4643
  var SLACK_API = "https://slack.com/api";
@@ -4632,16 +4679,16 @@ async function slackAuthTest(token) {
4632
4679
  }
4633
4680
 
4634
4681
  // src/slack/reply-target.ts
4635
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
4636
- import { join as join7 } from "path";
4682
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
4683
+ import { join as join8 } from "path";
4637
4684
  function storePath() {
4638
- return join7(appDataDir(), "slack-reply-to.json");
4685
+ return join8(appDataDir(), "slack-reply-to.json");
4639
4686
  }
4640
4687
  function readStore() {
4641
4688
  const path2 = storePath();
4642
4689
  if (!existsSync9(path2)) return {};
4643
4690
  try {
4644
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync7(path2, "utf8"));
4691
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync8(path2, "utf8"));
4645
4692
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
4646
4693
  } catch {
4647
4694
  return {};
@@ -4658,9 +4705,9 @@ function getSlackReplyTarget(threadId) {
4658
4705
  }
4659
4706
 
4660
4707
  // src/slack/workspaces.ts
4661
- import { join as join8 } from "path";
4708
+ import { join as join9 } from "path";
4662
4709
  function storePath2() {
4663
- return join8(appDataDir(), "slack-workspaces.json");
4710
+ return join9(appDataDir(), "slack-workspaces.json");
4664
4711
  }
4665
4712
  function readStore2() {
4666
4713
  try {
@@ -4776,7 +4823,7 @@ var POLL_INTERVAL_MS = 12e3;
4776
4823
  var lastPollMs = 0;
4777
4824
  var nameCache = /* @__PURE__ */ new Map();
4778
4825
  function storePath3() {
4779
- return join9(appDataDir(), "slack-outbound-watch.json");
4826
+ return join10(appDataDir(), "slack-outbound-watch.json");
4780
4827
  }
4781
4828
  function watchId(teamId, channelId, ts) {
4782
4829
  return `${teamId}:${channelId}:${ts}`;
@@ -4808,7 +4855,7 @@ function readStore3() {
4808
4855
  const path2 = storePath3();
4809
4856
  if (!existsSync10(path2)) return [];
4810
4857
  try {
4811
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync8(path2, "utf8"));
4858
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync9(path2, "utf8"));
4812
4859
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
4813
4860
  } catch {
4814
4861
  return [];
@@ -5171,7 +5218,7 @@ function shouldAutoArchiveOnPrMerge(opts) {
5171
5218
 
5172
5219
  // src/git/orphan-cleanup.ts
5173
5220
  import { existsSync as existsSync11, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
5174
- import { join as join10 } from "path";
5221
+ import { join as join11 } from "path";
5175
5222
  function isSideboardWorktreePath(path2) {
5176
5223
  return path2.includes("/.sideboard/worktrees/") || path2.includes("/sideboard/workspaces/");
5177
5224
  }
@@ -5218,9 +5265,9 @@ async function findOrphanWorktrees(repoPaths) {
5218
5265
  if (existsSync11(root)) {
5219
5266
  for (const entry of readdirSync4(root, { withFileTypes: true })) {
5220
5267
  if (!entry.isDirectory()) continue;
5221
- const path2 = join10(root, entry.name).replace(/\/$/, "");
5268
+ const path2 = join11(root, entry.name).replace(/\/$/, "");
5222
5269
  if (known.has(path2) || seen.has(path2)) continue;
5223
- if (!existsSync11(join10(path2, ".git"))) continue;
5270
+ if (!existsSync11(join11(path2, ".git"))) continue;
5224
5271
  seen.add(path2);
5225
5272
  let mtimeMs = 0;
5226
5273
  try {
@@ -5370,7 +5417,7 @@ async function applyThreadIntoMain(thread, opts) {
5370
5417
 
5371
5418
  // src/git/clone-repo.ts
5372
5419
  import { existsSync as existsSync12 } from "fs";
5373
- import { basename as basename3, join as join11 } from "path";
5420
+ import { basename as basename3, join as join12 } from "path";
5374
5421
  import { execa as execa4 } from "execa";
5375
5422
  async function cloneRepoIntoSideboard(opts) {
5376
5423
  const url = opts.url.trim();
@@ -5381,7 +5428,7 @@ async function cloneRepoIntoSideboard(opts) {
5381
5428
  name = leaf || "repo";
5382
5429
  }
5383
5430
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
5384
- const dest = join11(sideboardReposDir(), name);
5431
+ const dest = join12(sideboardReposDir(), name);
5385
5432
  if (existsSync12(dest)) {
5386
5433
  const repoPath2 = await resolveRepoRoot(dest);
5387
5434
  const workspace2 = await ensureWorkspace(repoPath2);
@@ -5400,8 +5447,8 @@ async function cloneRepoIntoSideboard(opts) {
5400
5447
 
5401
5448
  // src/review/request-review.ts
5402
5449
  import { randomUUID as randomUUID4 } from "crypto";
5403
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
5404
- import { dirname as dirname3, join as join12 } from "path";
5450
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
5451
+ import { dirname as dirname3, join as join13 } from "path";
5405
5452
 
5406
5453
  // src/review/review-request-template.ts
5407
5454
  var REVIEW_REQUEST_TEMPLATE = `# Review guidelines:
@@ -5548,20 +5595,20 @@ function shouldRefreshReviewRequestTemplate(content) {
5548
5595
  function readTextIfPresent(abs) {
5549
5596
  if (!existsSync13(abs)) return null;
5550
5597
  try {
5551
- const content = readFileSync9(abs, "utf8");
5598
+ const content = readFileSync10(abs, "utf8");
5552
5599
  return content.trim() ? content : null;
5553
5600
  } catch {
5554
5601
  return null;
5555
5602
  }
5556
5603
  }
5557
5604
  function ensureAttachmentsGitignore(worktreePath) {
5558
- const gitignoreAbs = join12(worktreePath, ATTACHMENTS_DIR, ".gitignore");
5605
+ const gitignoreAbs = join13(worktreePath, ATTACHMENTS_DIR, ".gitignore");
5559
5606
  if (existsSync13(gitignoreAbs)) return;
5560
5607
  mkdirSync4(dirname3(gitignoreAbs), { recursive: true });
5561
- writeFileSync3(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
5608
+ writeFileSync4(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
5562
5609
  }
5563
5610
  function resolveReviewGuidelines(worktreePath) {
5564
- const repoAbs = join12(worktreePath, REPO_REVIEW_PATH);
5611
+ const repoAbs = join13(worktreePath, REPO_REVIEW_PATH);
5565
5612
  const repoContent = readTextIfPresent(repoAbs);
5566
5613
  if (repoContent) {
5567
5614
  return {
@@ -5571,7 +5618,7 @@ function resolveReviewGuidelines(worktreePath) {
5571
5618
  source: "repo"
5572
5619
  };
5573
5620
  }
5574
- const localAbs = join12(worktreePath, REVIEW_REQUEST_PATH);
5621
+ const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
5575
5622
  const localContent = readTextIfPresent(localAbs);
5576
5623
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
5577
5624
  return {
@@ -5581,7 +5628,7 @@ function resolveReviewGuidelines(worktreePath) {
5581
5628
  source: "local"
5582
5629
  };
5583
5630
  }
5584
- const legacyAbs = join12(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
5631
+ const legacyAbs = join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
5585
5632
  const legacyContent = readTextIfPresent(legacyAbs);
5586
5633
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
5587
5634
  return {
@@ -5593,7 +5640,7 @@ function resolveReviewGuidelines(worktreePath) {
5593
5640
  }
5594
5641
  ensureAttachmentsGitignore(worktreePath);
5595
5642
  mkdirSync4(dirname3(localAbs), { recursive: true });
5596
- writeFileSync3(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5643
+ writeFileSync4(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5597
5644
  return {
5598
5645
  path: REVIEW_REQUEST_PATH,
5599
5646
  name: REVIEW_REQUEST_NAME,
@@ -5602,7 +5649,7 @@ function resolveReviewGuidelines(worktreePath) {
5602
5649
  };
5603
5650
  }
5604
5651
  function ensureReviewRequestFile(worktreePath) {
5605
- const repoAbs = join12(worktreePath, REPO_REVIEW_PATH);
5652
+ const repoAbs = join13(worktreePath, REPO_REVIEW_PATH);
5606
5653
  const repoContent = readTextIfPresent(repoAbs);
5607
5654
  if (repoContent) {
5608
5655
  return {
@@ -5612,8 +5659,8 @@ function ensureReviewRequestFile(worktreePath) {
5612
5659
  source: "repo"
5613
5660
  };
5614
5661
  }
5615
- const localAbs = join12(worktreePath, REVIEW_REQUEST_PATH);
5616
- const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join12(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5662
+ const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
5663
+ const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5617
5664
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
5618
5665
  const path2 = existsSync13(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
5619
5666
  return {
@@ -5624,7 +5671,7 @@ function ensureReviewRequestFile(worktreePath) {
5624
5671
  };
5625
5672
  }
5626
5673
  mkdirSync4(dirname3(repoAbs), { recursive: true });
5627
- writeFileSync3(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5674
+ writeFileSync4(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5628
5675
  return {
5629
5676
  path: REPO_REVIEW_PATH,
5630
5677
  name: REPO_REVIEW_NAME,
@@ -5644,7 +5691,7 @@ function buildReviewRequestAttachment(content, opts) {
5644
5691
  };
5645
5692
  }
5646
5693
  function readExistingReviewRequestFile(worktreePath) {
5647
- return readTextIfPresent(join12(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join12(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join12(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5694
+ return readTextIfPresent(join13(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join13(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5648
5695
  }
5649
5696
  async function requestReview(threadRef, send2) {
5650
5697
  const from = findThreadByRef(threadRef);
@@ -6145,7 +6192,9 @@ var Orchestrator = class {
6145
6192
  updateThread(thread.id, patch);
6146
6193
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
6147
6194
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
6148
- void this.drainQueue(thread.id);
6195
+ if (thisProcessShouldDrainAgentQueues()) {
6196
+ void this.drainQueue(thread.id);
6197
+ }
6149
6198
  return this.requireThread(thread.id);
6150
6199
  });
6151
6200
  }
@@ -6198,10 +6247,10 @@ var Orchestrator = class {
6198
6247
  async sendQueuedMessageNow(threadRef, index) {
6199
6248
  const thread = this.requireThread(threadRef);
6200
6249
  const promoted = await withThreadLock(thread.id, async () => {
6201
- const current = this.requireThread(thread.id);
6202
- if (index < 0 || index >= current.queue.length) return false;
6203
- const item = current.queue[index];
6204
- const rest = current.queue.filter((_, i) => i !== index);
6250
+ const current2 = this.requireThread(thread.id);
6251
+ if (index < 0 || index >= current2.queue.length) return false;
6252
+ const item = current2.queue[index];
6253
+ const rest = current2.queue.filter((_, i) => i !== index);
6205
6254
  const queue = [item, ...rest];
6206
6255
  this.haltDrain.delete(thread.id);
6207
6256
  updateThread(thread.id, { queue });
@@ -6209,10 +6258,16 @@ var Orchestrator = class {
6209
6258
  return true;
6210
6259
  });
6211
6260
  if (!promoted) return this.requireThread(thread.id);
6261
+ const current = this.requireThread(thread.id);
6212
6262
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
6263
+ const livePid = current.agentPid;
6264
+ const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
6213
6265
  if (inFlight) {
6214
6266
  this.stop(thread.id, { clearQueue: false, continueQueue: true });
6215
6267
  } else {
6268
+ if (foreignLive) {
6269
+ this.stop(thread.id, { clearQueue: false, continueQueue: true });
6270
+ }
6216
6271
  void this.drainQueue(thread.id);
6217
6272
  }
6218
6273
  return this.requireThread(thread.id);
@@ -6463,19 +6518,16 @@ var Orchestrator = class {
6463
6518
  }
6464
6519
  let lastStderr = summarizeTurnStderr(stderrTail);
6465
6520
  let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
6466
- if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "brightsy") {
6521
+ if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent !== "brightsy" && shouldRetryFailedAgentTurn(detail, {
6522
+ hasSession: Boolean(this.requireThread(threadId).sessionId)
6523
+ })) {
6467
6524
  updateThread(threadId, { sessionId: null });
6468
- pushTurnStderr(
6469
- stderrTail,
6470
- "Agent session missing \u2014 starting a fresh session"
6471
- );
6525
+ const retryNote = looksLikeInvalidAgentSession(detail) ? "Agent session missing \u2014 starting a fresh session" : "Agent runner crashed \u2014 restarting Node once";
6526
+ pushTurnStderr(stderrTail, retryNote);
6472
6527
  this.emit({
6473
6528
  type: "turn_output",
6474
6529
  threadId,
6475
- event: {
6476
- type: "stderr",
6477
- data: "Agent session missing \u2014 starting a fresh session"
6478
- }
6530
+ event: { type: "stderr", data: retryNote }
6479
6531
  });
6480
6532
  const retryThread = this.requireThread(threadId);
6481
6533
  const prior = retryThread.messages.slice(0, -1);
@@ -6524,10 +6576,7 @@ var Orchestrator = class {
6524
6576
  lastStderr = summarizeTurnStderr(stderrTail);
6525
6577
  detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
6526
6578
  }
6527
- let chatText = assistantText;
6528
- if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
6529
- chatText = humanizeAgentFailDetail(detail);
6530
- }
6579
+ let chatText = this.stoppedTurns.has(threadId) ? assistantText : turnFailChatText({ exitCode, assistantText, detail });
6531
6580
  if (chatText || parts.length > 0) {
6532
6581
  appendMessage(threadId, {
6533
6582
  role: "agent",
@@ -6647,6 +6696,13 @@ var Orchestrator = class {
6647
6696
  if (handle) handle.kill();
6648
6697
  const proc = this.processes.get(`${thread.id}:agent`);
6649
6698
  if (proc) proc.kill();
6699
+ const pid = thread.agentPid;
6700
+ if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) {
6701
+ try {
6702
+ process.kill(pid, "SIGTERM");
6703
+ } catch {
6704
+ }
6705
+ }
6650
6706
  const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
6651
6707
  if (stopped.status === "stopped") {
6652
6708
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
@@ -6928,10 +6984,13 @@ var Orchestrator = class {
6928
6984
  getTurnResult(threadRef) {
6929
6985
  const thread = this.requireThread(threadRef);
6930
6986
  const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
6987
+ const lastError = thread.lastError ?? null;
6988
+ const text3 = (lastAgent?.text ?? "").trim() || (thread.status === "error" ? lastError ?? "" : "");
6931
6989
  return {
6932
- text: lastAgent?.text ?? "",
6990
+ text: text3,
6933
6991
  status: thread.status,
6934
- sessionId: thread.sessionId
6992
+ sessionId: thread.sessionId,
6993
+ lastError
6935
6994
  };
6936
6995
  }
6937
6996
  assertNotGlobal(thread, action) {
@@ -7385,7 +7444,7 @@ var Orchestrator = class {
7385
7444
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
7386
7445
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7387
7446
  try {
7388
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-4ZY4QPWQ.js");
7447
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-NNPD7QVV.js");
7389
7448
  await ensureWorkspace2(thread.repoPath);
7390
7449
  } catch {
7391
7450
  }
@@ -8696,7 +8755,7 @@ async function startMcpServer() {
8696
8755
  );
8697
8756
  server.tool(
8698
8757
  "wait_for_turn",
8699
- "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread or ask_git to read the agent reply.",
8758
+ "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.",
8700
8759
  {
8701
8760
  ref: z3.string(),
8702
8761
  timeoutMs: z3.number().optional()
@@ -8711,7 +8770,8 @@ async function startMcpServer() {
8711
8770
  text: JSON.stringify({
8712
8771
  id: thread.id,
8713
8772
  status: result.status,
8714
- text: result.text
8773
+ text: result.text,
8774
+ lastError: result.lastError
8715
8775
  })
8716
8776
  }
8717
8777
  ]
@@ -8720,7 +8780,7 @@ async function startMcpServer() {
8720
8780
  );
8721
8781
  server.tool(
8722
8782
  "get_turn_result",
8723
- "Final assistant message only (not full transcript)",
8783
+ "Final assistant message (and lastError when the turn failed). Not the full transcript.",
8724
8784
  { ref: z3.string() },
8725
8785
  async ({ ref }) => {
8726
8786
  const result = orch.getTurnResult(ref);
@@ -9632,14 +9692,14 @@ async function runCloudConnect(opts) {
9632
9692
  }
9633
9693
 
9634
9694
  // src/agents/user-mcp-config.ts
9635
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
9695
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
9636
9696
  import { homedir as homedir2 } from "os";
9637
- import { dirname as dirname4, join as join13 } from "path";
9697
+ import { dirname as dirname4, join as join14 } from "path";
9638
9698
  function userCursorMcpConfigPath() {
9639
- return join13(homedir2(), ".cursor", "mcp.json");
9699
+ return join14(homedir2(), ".cursor", "mcp.json");
9640
9700
  }
9641
9701
  function userClaudeMcpConfigPath() {
9642
- return join13(homedir2(), ".claude.json");
9702
+ return join14(homedir2(), ".claude.json");
9643
9703
  }
9644
9704
  function asObject(value) {
9645
9705
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
@@ -9668,14 +9728,14 @@ function writeMergedMcpServersJson(configPath, sideboard) {
9668
9728
  let existing = {};
9669
9729
  if (existsSync15(configPath)) {
9670
9730
  try {
9671
- existing = JSON.parse(readFileSync10(configPath, "utf8"));
9731
+ existing = JSON.parse(readFileSync11(configPath, "utf8"));
9672
9732
  } catch {
9673
9733
  existing = {};
9674
9734
  }
9675
9735
  }
9676
9736
  const next = mergeSideboardIntoMcpServersJson(existing, sideboard);
9677
9737
  mkdirSync5(dirname4(configPath), { recursive: true });
9678
- writeFileSync4(configPath, `${JSON.stringify(next, null, 2)}
9738
+ writeFileSync5(configPath, `${JSON.stringify(next, null, 2)}
9679
9739
  `);
9680
9740
  }
9681
9741
  function launchFromResolved(server) {
@@ -11492,6 +11552,7 @@ export {
11492
11552
  captureTurnBaseline,
11493
11553
  checkoutPrStackLayer,
11494
11554
  childEnvWithAppSettings,
11555
+ claimDesktopHost,
11495
11556
  claudeAdapter,
11496
11557
  claudeChromeEnabled,
11497
11558
  claudeUserSettingsPath,
@@ -11530,6 +11591,7 @@ export {
11530
11591
  decodeBrightsyTarget,
11531
11592
  deleteBranchOnPurgeEnabled,
11532
11593
  deleteThreadRecord,
11594
+ desktopHostPidPath,
11533
11595
  detectAgents,
11534
11596
  detectGhStack,
11535
11597
  detectLocalMergeConflicts,
@@ -11655,6 +11717,7 @@ export {
11655
11717
  isConductorBundledCli,
11656
11718
  isCursorAutoModel,
11657
11719
  isDefaultishSourceRef,
11720
+ isDesktopHostAlive,
11658
11721
  isDirty,
11659
11722
  isGhRateLimitError,
11660
11723
  isGlobalRepoPath,
@@ -11676,6 +11739,7 @@ export {
11676
11739
  isSlackExternalReplyPrompt,
11677
11740
  isSlackOAuthCancelled,
11678
11741
  isThinkingEffort,
11742
+ isThisProcessDesktopHost,
11679
11743
  isThreadCaffeinated,
11680
11744
  isWorkspaceScratchPath,
11681
11745
  linearAuthorizationHeader,
@@ -11777,6 +11841,7 @@ export {
11777
11841
  refreshSlackReplyBadges,
11778
11842
  registerPackagedUserMcpClients,
11779
11843
  releaseCaffeinateHoldForThread,
11844
+ releaseDesktopHost,
11780
11845
  removeWorkspace,
11781
11846
  removeWorktree,
11782
11847
  repoSlug,
@@ -11877,6 +11942,7 @@ export {
11877
11942
  taskMessageText,
11878
11943
  thinkingEffortBars,
11879
11944
  thinkingEffortLabel,
11945
+ thisProcessShouldDrainAgentQueues,
11880
11946
  threadDisplayLabel,
11881
11947
  threadFilePath,
11882
11948
  threadLockPath,