@sideboard-ai/core 0.1.98 → 0.1.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -367,6 +367,52 @@ import {
367
367
  withExportedPath
368
368
  } from "./chunk-LGXBYZZA.js";
369
369
 
370
+ // src/store/desktop-host.ts
371
+ import { readFileSync, unlinkSync, writeFileSync } from "fs";
372
+ import { join } from "path";
373
+ function desktopHostPidPath() {
374
+ return join(appDataDir(), "desktop-host.pid");
375
+ }
376
+ function claimDesktopHost(pid = process.pid) {
377
+ writeFileSync(desktopHostPidPath(), `${pid}
378
+ `, "utf8");
379
+ }
380
+ function releaseDesktopHost(pid = process.pid) {
381
+ if (readDesktopHostPid() !== pid) return;
382
+ try {
383
+ unlinkSync(desktopHostPidPath());
384
+ } catch {
385
+ }
386
+ }
387
+ function readDesktopHostPid() {
388
+ try {
389
+ const pid = Number.parseInt(readFileSync(desktopHostPidPath(), "utf8").trim(), 10);
390
+ if (!Number.isFinite(pid) || pid <= 0) return null;
391
+ return pid;
392
+ } catch {
393
+ return null;
394
+ }
395
+ }
396
+ function pidAlive(pid) {
397
+ try {
398
+ process.kill(pid, 0);
399
+ return true;
400
+ } catch {
401
+ return false;
402
+ }
403
+ }
404
+ function isDesktopHostAlive() {
405
+ const pid = readDesktopHostPid();
406
+ return pid != null && pidAlive(pid);
407
+ }
408
+ function isThisProcessDesktopHost() {
409
+ return readDesktopHostPid() === process.pid && pidAlive(process.pid);
410
+ }
411
+ function thisProcessShouldDrainAgentQueues() {
412
+ if (isThisProcessDesktopHost()) return true;
413
+ return !isDesktopHostAlive();
414
+ }
415
+
370
416
  // src/git/agent-git-actions.ts
371
417
  var AGENT_GIT_ACTIONS = [
372
418
  "commit-push",
@@ -1452,8 +1498,8 @@ async function spawnAgentTurn(thread, input, onEvent) {
1452
1498
  }
1453
1499
 
1454
1500
  // src/agents/instructions.ts
1455
- import { existsSync, readFileSync, statSync } from "fs";
1456
- import { join } from "path";
1501
+ import { existsSync, readFileSync as readFileSync2, statSync } from "fs";
1502
+ import { join as join2 } from "path";
1457
1503
  function normPath(p) {
1458
1504
  return p.replace(/\/+$/, "");
1459
1505
  }
@@ -1622,11 +1668,11 @@ function loadAgentInstructions(worktreePath, agent) {
1622
1668
  const out = [];
1623
1669
  for (const rel of candidates) {
1624
1670
  if (seenPaths.has(rel)) continue;
1625
- const abs = join(worktreePath, rel);
1671
+ const abs = join2(worktreePath, rel);
1626
1672
  if (!existsSync(abs)) continue;
1627
1673
  try {
1628
1674
  if (!statSync(abs).isFile()) continue;
1629
- let content = readFileSync(abs, "utf8");
1675
+ let content = readFileSync2(abs, "utf8");
1630
1676
  if (!content.trim()) continue;
1631
1677
  if (content.length > MAX_CHARS_PER_FILE) {
1632
1678
  content = `${content.slice(0, MAX_CHARS_PER_FILE)}
@@ -1706,10 +1752,10 @@ import {
1706
1752
  existsSync as existsSync2,
1707
1753
  mkdirSync,
1708
1754
  readdirSync,
1709
- readFileSync as readFileSync2
1755
+ readFileSync as readFileSync3
1710
1756
  } from "fs";
1711
1757
  import { createServer as createServer2 } from "net";
1712
- import { basename, dirname, join as join2 } from "path";
1758
+ import { basename, dirname, join as join3 } from "path";
1713
1759
  import { execa as execa2 } from "execa";
1714
1760
  import { createInterface as createInterface2 } from "readline";
1715
1761
  var PORT_RANGE_SIZE = 10;
@@ -1718,9 +1764,9 @@ function matchSimpleGlob(pattern, name) {
1718
1764
  return new RegExp(`^${escaped}$`).test(name);
1719
1765
  }
1720
1766
  function readWorktreeInclude(repoPath) {
1721
- const path2 = join2(repoPath, ".worktreeinclude");
1767
+ const path2 = join3(repoPath, ".worktreeinclude");
1722
1768
  if (!existsSync2(path2)) return [];
1723
- return readFileSync2(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1769
+ return readFileSync3(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1724
1770
  }
1725
1771
  function resolveFilesToCopy(repoPath) {
1726
1772
  const fromInclude = readWorktreeInclude(repoPath);
@@ -1759,9 +1805,9 @@ function copyConfiguredFiles(repoPath, worktreePath) {
1759
1805
  const patterns = resolveFilesToCopy(repoPath);
1760
1806
  const copied = [];
1761
1807
  for (const rel of patterns) {
1762
- const src = join2(repoPath, rel);
1808
+ const src = join3(repoPath, rel);
1763
1809
  if (!existsSync2(src)) continue;
1764
- const dest = join2(worktreePath, rel);
1810
+ const dest = join3(worktreePath, rel);
1765
1811
  mkdirSync(dirname(dest), { recursive: true });
1766
1812
  copyFileSync(src, dest);
1767
1813
  copied.push(rel);
@@ -2056,8 +2102,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2056
2102
  }
2057
2103
 
2058
2104
  // 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";
2105
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
2106
+ import { dirname as dirname2, join as join4 } from "path";
2061
2107
  async function inspectGitWorktree(worktreePath) {
2062
2108
  if (!worktreePath || !existsSync3(worktreePath)) return "missing_worktree";
2063
2109
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
@@ -2203,11 +2249,11 @@ new file mode 100644
2203
2249
  };
2204
2250
  }
2205
2251
  async function untrackedPatch(worktreePath, path2, maxHunk) {
2206
- const abs = join3(worktreePath, path2);
2252
+ const abs = join4(worktreePath, path2);
2207
2253
  try {
2208
2254
  const st = statSync2(abs);
2209
2255
  if (st.isFile() && st.size > maxHunk) {
2210
- const buf = readFileSync3(abs).subarray(0, maxHunk);
2256
+ const buf = readFileSync4(abs).subarray(0, maxHunk);
2211
2257
  return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
2212
2258
  }
2213
2259
  } catch {
@@ -2708,7 +2754,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
2708
2754
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
2709
2755
  assertSafeRelativePath(relativePath);
2710
2756
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
2711
- const abs = join3(worktreePath, relativePath);
2757
+ const abs = join4(worktreePath, relativePath);
2712
2758
  const st = statSync2(abs);
2713
2759
  if (!st.isFile()) {
2714
2760
  throw new Error(`Not a file: ${relativePath}`);
@@ -2718,7 +2764,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
2718
2764
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
2719
2765
  );
2720
2766
  }
2721
- const buf = readFileSync3(abs);
2767
+ const buf = readFileSync4(abs);
2722
2768
  return {
2723
2769
  path: relativePath,
2724
2770
  contentBase64: buf.toString("base64"),
@@ -2728,12 +2774,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
2728
2774
  function readWorktreeFile(worktreePath, relativePath, opts) {
2729
2775
  assertSafeRelativePath(relativePath);
2730
2776
  const maxBytes = opts?.maxBytes ?? 2e5;
2731
- const abs = join3(worktreePath, relativePath);
2777
+ const abs = join4(worktreePath, relativePath);
2732
2778
  const st = statSync2(abs);
2733
2779
  if (!st.isFile()) {
2734
2780
  throw new Error(`Not a file: ${relativePath}`);
2735
2781
  }
2736
- const buf = readFileSync3(abs);
2782
+ const buf = readFileSync4(abs);
2737
2783
  if (isImageRelativePath(relativePath)) {
2738
2784
  const maxImageBytes = Math.max(maxBytes, 15e6);
2739
2785
  const truncated2 = buf.length > maxImageBytes;
@@ -2776,9 +2822,9 @@ function assertSafeRelativePath(relativePath) {
2776
2822
  }
2777
2823
  function writeWorktreeFile(worktreePath, relativePath, content) {
2778
2824
  assertSafeRelativePath(relativePath);
2779
- const abs = join3(worktreePath, relativePath);
2825
+ const abs = join4(worktreePath, relativePath);
2780
2826
  mkdirSync2(dirname2(abs), { recursive: true });
2781
- writeFileSync(abs, content, "utf8");
2827
+ writeFileSync2(abs, content, "utf8");
2782
2828
  return { path: relativePath };
2783
2829
  }
2784
2830
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -2797,9 +2843,9 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
2797
2843
  }
2798
2844
 
2799
2845
  // src/skills/discover.ts
2800
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
2846
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
2801
2847
  import { homedir } from "os";
2802
- import { join as join4 } from "path";
2848
+ import { join as join5 } from "path";
2803
2849
  function toCommand(name) {
2804
2850
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2805
2851
  }
@@ -2831,7 +2877,7 @@ function parseFrontmatter(content) {
2831
2877
  }
2832
2878
  function readSkill(skillMd, source) {
2833
2879
  try {
2834
- const content = readFileSync4(skillMd, "utf8");
2880
+ const content = readFileSync5(skillMd, "utf8");
2835
2881
  const { name: fmName, description } = parseFrontmatter(content);
2836
2882
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
2837
2883
  const name = fmName || dirName;
@@ -2859,7 +2905,7 @@ function scanSkillsDir(dir, source, out) {
2859
2905
  }
2860
2906
  for (const entry of entries) {
2861
2907
  if (entry.startsWith(".")) continue;
2862
- const skillMd = join4(dir, entry, "SKILL.md");
2908
+ const skillMd = join5(dir, entry, "SKILL.md");
2863
2909
  if (!existsSync4(skillMd)) continue;
2864
2910
  try {
2865
2911
  if (!statSync3(skillMd).isFile()) continue;
@@ -2881,12 +2927,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
2881
2927
  return;
2882
2928
  }
2883
2929
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
2884
- const skill = readSkill(join4(dir, "SKILL.md"), "cli");
2930
+ const skill = readSkill(join5(dir, "SKILL.md"), "cli");
2885
2931
  if (skill) out.push(skill);
2886
2932
  }
2887
2933
  for (const entry of entries) {
2888
2934
  if (entry === "node_modules" || entry === ".git") continue;
2889
- const full = join4(dir, entry);
2935
+ const full = join5(dir, entry);
2890
2936
  try {
2891
2937
  if (!statSync3(full).isDirectory()) continue;
2892
2938
  } catch {
@@ -2906,17 +2952,17 @@ function discoverSkills(worktreePath) {
2906
2952
  const home = homedir();
2907
2953
  const collected = [];
2908
2954
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
2909
- scanSkillsDir(join4(worktreePath, rel), "workspace", collected);
2955
+ scanSkillsDir(join5(worktreePath, rel), "workspace", collected);
2910
2956
  }
2911
2957
  for (const abs of [
2912
- join4(home, ".claude/skills"),
2913
- join4(home, ".cursor/skills"),
2914
- join4(home, ".sideboard/skills"),
2915
- join4(home, ".brightsy/skills")
2958
+ join5(home, ".claude/skills"),
2959
+ join5(home, ".cursor/skills"),
2960
+ join5(home, ".sideboard/skills"),
2961
+ join5(home, ".brightsy/skills")
2916
2962
  ]) {
2917
2963
  scanSkillsDir(abs, "user", collected);
2918
2964
  }
2919
- scanClaudePluginSkills(join4(home, ".claude/plugins"), collected);
2965
+ scanClaudePluginSkills(join5(home, ".claude/plugins"), collected);
2920
2966
  const rank = { workspace: 0, user: 1, cli: 2 };
2921
2967
  const byCommand = /* @__PURE__ */ new Map();
2922
2968
  for (const skill of collected) {
@@ -2928,7 +2974,7 @@ function discoverSkills(worktreePath) {
2928
2974
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
2929
2975
  }
2930
2976
  function readSkillBody(skillPath, maxChars = 12e3) {
2931
- const raw = readFileSync4(skillPath, "utf8");
2977
+ const raw = readFileSync5(skillPath, "utf8");
2932
2978
  if (raw.startsWith("---")) {
2933
2979
  const end = raw.indexOf("\n---", 3);
2934
2980
  if (end >= 0) {
@@ -3069,8 +3115,8 @@ function buildDiffCommentAttachment(input) {
3069
3115
  }
3070
3116
 
3071
3117
  // 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";
3118
+ import { copyFileSync as copyFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
3119
+ import { basename as basename2, extname, join as join6 } from "path";
3074
3120
  import { randomUUID } from "crypto";
3075
3121
  var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
3076
3122
  "png",
@@ -3105,22 +3151,22 @@ function imageMimeType(filePath) {
3105
3151
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
3106
3152
  }
3107
3153
  function ensureAttachmentsDir(worktreePath) {
3108
- const dir = join5(worktreePath, ATTACHMENTS_DIR);
3154
+ const dir = join6(worktreePath, ATTACHMENTS_DIR);
3109
3155
  mkdirSync3(dir, { recursive: true });
3110
- const gi = join5(dir, ".gitignore");
3156
+ const gi = join6(dir, ".gitignore");
3111
3157
  if (!existsSync5(gi)) {
3112
- writeFileSync2(gi, attachmentsGitignoreBody(), "utf8");
3158
+ writeFileSync3(gi, attachmentsGitignoreBody(), "utf8");
3113
3159
  }
3114
3160
  return dir;
3115
3161
  }
3116
3162
  function uniqueAttachmentName(dir, originalName) {
3117
3163
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
3118
- if (!existsSync5(join5(dir, safe))) return safe;
3164
+ if (!existsSync5(join6(dir, safe))) return safe;
3119
3165
  const ext = extname(safe);
3120
3166
  const stem = ext ? safe.slice(0, -ext.length) : safe;
3121
3167
  for (let i = 1; i < 1e4; i++) {
3122
3168
  const candidate = `${stem}-${i}${ext}`;
3123
- if (!existsSync5(join5(dir, candidate))) return candidate;
3169
+ if (!existsSync5(join6(dir, candidate))) return candidate;
3124
3170
  }
3125
3171
  return `${stem}-${randomUUID()}${ext}`;
3126
3172
  }
@@ -3183,7 +3229,7 @@ function attachmentFromAbsolutePath(absolutePath) {
3183
3229
  content: `(not a file: ${absolutePath})`
3184
3230
  };
3185
3231
  }
3186
- const buf = readFileSync5(absolutePath);
3232
+ const buf = readFileSync6(absolutePath);
3187
3233
  return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
3188
3234
  } catch (err) {
3189
3235
  return {
@@ -3204,10 +3250,10 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
3204
3250
  const st = statSync4(abs);
3205
3251
  if (!st.isFile()) continue;
3206
3252
  const name = uniqueAttachmentName(dir, originalName);
3207
- const destAbs = join5(dir, name);
3253
+ const destAbs = join6(dir, name);
3208
3254
  copyFileSync2(abs, destAbs);
3209
3255
  const rel = `${ATTACHMENTS_DIR}/${name}`;
3210
- const buf = readFileSync5(destAbs);
3256
+ const buf = readFileSync6(destAbs);
3211
3257
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
3212
3258
  } catch (err) {
3213
3259
  out.push({
@@ -3229,8 +3275,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
3229
3275
  try {
3230
3276
  const buf = Buffer.from(item.dataBase64, "base64");
3231
3277
  const name = uniqueAttachmentName(dir, originalName);
3232
- const destAbs = join5(dir, name);
3233
- writeFileSync2(destAbs, buf);
3278
+ const destAbs = join6(dir, name);
3279
+ writeFileSync3(destAbs, buf);
3234
3280
  const rel = `${ATTACHMENTS_DIR}/${name}`;
3235
3281
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
3236
3282
  } catch (err) {
@@ -3274,10 +3320,10 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
3274
3320
  }
3275
3321
  const name = basename2(rel);
3276
3322
  try {
3277
- const abs = join5(worktreePath, rel);
3323
+ const abs = join6(worktreePath, rel);
3278
3324
  const st = statSync4(abs);
3279
3325
  if (!st.isFile()) continue;
3280
- const buf = readFileSync5(abs);
3326
+ const buf = readFileSync6(abs);
3281
3327
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
3282
3328
  } catch (err) {
3283
3329
  out.push({
@@ -4300,20 +4346,20 @@ import {
4300
4346
  existsSync as existsSync8,
4301
4347
  mkdtempSync,
4302
4348
  readdirSync as readdirSync3,
4303
- readFileSync as readFileSync6,
4349
+ readFileSync as readFileSync7,
4304
4350
  rmSync
4305
4351
  } from "fs";
4306
4352
  import { tmpdir } from "os";
4307
- import { join as join6 } from "path";
4353
+ import { join as join7 } from "path";
4308
4354
  import { createRequire } from "module";
4309
- var CONDUCTOR_APP_SUPPORT = join6(
4355
+ var CONDUCTOR_APP_SUPPORT = join7(
4310
4356
  process.env.HOME ?? "",
4311
4357
  "Library",
4312
4358
  "Application Support",
4313
4359
  "com.conductor.app"
4314
4360
  );
4315
- var CONDUCTOR_DB = join6(CONDUCTOR_APP_SUPPORT, "conductor.db");
4316
- var CURSOR_SDK_STORE = join6(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4361
+ var CONDUCTOR_DB = join7(CONDUCTOR_APP_SUPPORT, "conductor.db");
4362
+ var CURSOR_SDK_STORE = join7(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
4317
4363
  function openReadonlySqlite(file) {
4318
4364
  const req = createRequire(import.meta.url);
4319
4365
  const Database = req("better-sqlite3");
@@ -4340,11 +4386,11 @@ function resolveConductorCursorAgentId(workspacePath) {
4340
4386
  return null;
4341
4387
  }
4342
4388
  for (const hash of hashes) {
4343
- const agentsFile = join6(CURSOR_SDK_STORE, hash, "agents.ndjson");
4389
+ const agentsFile = join7(CURSOR_SDK_STORE, hash, "agents.ndjson");
4344
4390
  if (!existsSync8(agentsFile)) continue;
4345
4391
  let text3;
4346
4392
  try {
4347
- text3 = readFileSync6(agentsFile, "utf8");
4393
+ text3 = readFileSync7(agentsFile, "utf8");
4348
4394
  } catch {
4349
4395
  continue;
4350
4396
  }
@@ -4398,8 +4444,8 @@ function listConductorWorkspaces() {
4398
4444
  if (!existsSync8(CONDUCTOR_DB)) {
4399
4445
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4400
4446
  }
4401
- const tmp = mkdtempSync(join6(tmpdir(), "sideboard-conductor-"));
4402
- const snapshot = join6(tmp, "conductor.db");
4447
+ const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
4448
+ const snapshot = join7(tmp, "conductor.db");
4403
4449
  try {
4404
4450
  copyFileSync3(CONDUCTOR_DB, snapshot);
4405
4451
  for (const suffix of ["-wal", "-shm"]) {
@@ -4489,8 +4535,8 @@ function importConductorWorkspace(workspaceId) {
4489
4535
  if (!existsSync8(CONDUCTOR_DB)) {
4490
4536
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
4491
4537
  }
4492
- const tmp = mkdtempSync(join6(tmpdir(), "sideboard-conductor-"));
4493
- const snapshot = join6(tmp, "conductor.db");
4538
+ const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
4539
+ const snapshot = join7(tmp, "conductor.db");
4494
4540
  try {
4495
4541
  copyFileSync3(CONDUCTOR_DB, snapshot);
4496
4542
  for (const suffix of ["-wal", "-shm"]) {
@@ -4589,8 +4635,8 @@ async function importConductorWorkspaceAsync(workspaceId) {
4589
4635
  import { EventEmitter } from "events";
4590
4636
 
4591
4637
  // src/slack/outbound-watch.ts
4592
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
4593
- import { join as join9 } from "path";
4638
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
4639
+ import { join as join10 } from "path";
4594
4640
 
4595
4641
  // src/slack/api.ts
4596
4642
  var SLACK_API = "https://slack.com/api";
@@ -4632,16 +4678,16 @@ async function slackAuthTest(token) {
4632
4678
  }
4633
4679
 
4634
4680
  // src/slack/reply-target.ts
4635
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
4636
- import { join as join7 } from "path";
4681
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
4682
+ import { join as join8 } from "path";
4637
4683
  function storePath() {
4638
- return join7(appDataDir(), "slack-reply-to.json");
4684
+ return join8(appDataDir(), "slack-reply-to.json");
4639
4685
  }
4640
4686
  function readStore() {
4641
4687
  const path2 = storePath();
4642
4688
  if (!existsSync9(path2)) return {};
4643
4689
  try {
4644
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync7(path2, "utf8"));
4690
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync8(path2, "utf8"));
4645
4691
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
4646
4692
  } catch {
4647
4693
  return {};
@@ -4658,9 +4704,9 @@ function getSlackReplyTarget(threadId) {
4658
4704
  }
4659
4705
 
4660
4706
  // src/slack/workspaces.ts
4661
- import { join as join8 } from "path";
4707
+ import { join as join9 } from "path";
4662
4708
  function storePath2() {
4663
- return join8(appDataDir(), "slack-workspaces.json");
4709
+ return join9(appDataDir(), "slack-workspaces.json");
4664
4710
  }
4665
4711
  function readStore2() {
4666
4712
  try {
@@ -4776,7 +4822,7 @@ var POLL_INTERVAL_MS = 12e3;
4776
4822
  var lastPollMs = 0;
4777
4823
  var nameCache = /* @__PURE__ */ new Map();
4778
4824
  function storePath3() {
4779
- return join9(appDataDir(), "slack-outbound-watch.json");
4825
+ return join10(appDataDir(), "slack-outbound-watch.json");
4780
4826
  }
4781
4827
  function watchId(teamId, channelId, ts) {
4782
4828
  return `${teamId}:${channelId}:${ts}`;
@@ -4808,7 +4854,7 @@ function readStore3() {
4808
4854
  const path2 = storePath3();
4809
4855
  if (!existsSync10(path2)) return [];
4810
4856
  try {
4811
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync8(path2, "utf8"));
4857
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync9(path2, "utf8"));
4812
4858
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
4813
4859
  } catch {
4814
4860
  return [];
@@ -5171,7 +5217,7 @@ function shouldAutoArchiveOnPrMerge(opts) {
5171
5217
 
5172
5218
  // src/git/orphan-cleanup.ts
5173
5219
  import { existsSync as existsSync11, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
5174
- import { join as join10 } from "path";
5220
+ import { join as join11 } from "path";
5175
5221
  function isSideboardWorktreePath(path2) {
5176
5222
  return path2.includes("/.sideboard/worktrees/") || path2.includes("/sideboard/workspaces/");
5177
5223
  }
@@ -5218,9 +5264,9 @@ async function findOrphanWorktrees(repoPaths) {
5218
5264
  if (existsSync11(root)) {
5219
5265
  for (const entry of readdirSync4(root, { withFileTypes: true })) {
5220
5266
  if (!entry.isDirectory()) continue;
5221
- const path2 = join10(root, entry.name).replace(/\/$/, "");
5267
+ const path2 = join11(root, entry.name).replace(/\/$/, "");
5222
5268
  if (known.has(path2) || seen.has(path2)) continue;
5223
- if (!existsSync11(join10(path2, ".git"))) continue;
5269
+ if (!existsSync11(join11(path2, ".git"))) continue;
5224
5270
  seen.add(path2);
5225
5271
  let mtimeMs = 0;
5226
5272
  try {
@@ -5370,7 +5416,7 @@ async function applyThreadIntoMain(thread, opts) {
5370
5416
 
5371
5417
  // src/git/clone-repo.ts
5372
5418
  import { existsSync as existsSync12 } from "fs";
5373
- import { basename as basename3, join as join11 } from "path";
5419
+ import { basename as basename3, join as join12 } from "path";
5374
5420
  import { execa as execa4 } from "execa";
5375
5421
  async function cloneRepoIntoSideboard(opts) {
5376
5422
  const url = opts.url.trim();
@@ -5381,7 +5427,7 @@ async function cloneRepoIntoSideboard(opts) {
5381
5427
  name = leaf || "repo";
5382
5428
  }
5383
5429
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
5384
- const dest = join11(sideboardReposDir(), name);
5430
+ const dest = join12(sideboardReposDir(), name);
5385
5431
  if (existsSync12(dest)) {
5386
5432
  const repoPath2 = await resolveRepoRoot(dest);
5387
5433
  const workspace2 = await ensureWorkspace(repoPath2);
@@ -5400,8 +5446,8 @@ async function cloneRepoIntoSideboard(opts) {
5400
5446
 
5401
5447
  // src/review/request-review.ts
5402
5448
  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";
5449
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
5450
+ import { dirname as dirname3, join as join13 } from "path";
5405
5451
 
5406
5452
  // src/review/review-request-template.ts
5407
5453
  var REVIEW_REQUEST_TEMPLATE = `# Review guidelines:
@@ -5548,20 +5594,20 @@ function shouldRefreshReviewRequestTemplate(content) {
5548
5594
  function readTextIfPresent(abs) {
5549
5595
  if (!existsSync13(abs)) return null;
5550
5596
  try {
5551
- const content = readFileSync9(abs, "utf8");
5597
+ const content = readFileSync10(abs, "utf8");
5552
5598
  return content.trim() ? content : null;
5553
5599
  } catch {
5554
5600
  return null;
5555
5601
  }
5556
5602
  }
5557
5603
  function ensureAttachmentsGitignore(worktreePath) {
5558
- const gitignoreAbs = join12(worktreePath, ATTACHMENTS_DIR, ".gitignore");
5604
+ const gitignoreAbs = join13(worktreePath, ATTACHMENTS_DIR, ".gitignore");
5559
5605
  if (existsSync13(gitignoreAbs)) return;
5560
5606
  mkdirSync4(dirname3(gitignoreAbs), { recursive: true });
5561
- writeFileSync3(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
5607
+ writeFileSync4(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
5562
5608
  }
5563
5609
  function resolveReviewGuidelines(worktreePath) {
5564
- const repoAbs = join12(worktreePath, REPO_REVIEW_PATH);
5610
+ const repoAbs = join13(worktreePath, REPO_REVIEW_PATH);
5565
5611
  const repoContent = readTextIfPresent(repoAbs);
5566
5612
  if (repoContent) {
5567
5613
  return {
@@ -5571,7 +5617,7 @@ function resolveReviewGuidelines(worktreePath) {
5571
5617
  source: "repo"
5572
5618
  };
5573
5619
  }
5574
- const localAbs = join12(worktreePath, REVIEW_REQUEST_PATH);
5620
+ const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
5575
5621
  const localContent = readTextIfPresent(localAbs);
5576
5622
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
5577
5623
  return {
@@ -5581,7 +5627,7 @@ function resolveReviewGuidelines(worktreePath) {
5581
5627
  source: "local"
5582
5628
  };
5583
5629
  }
5584
- const legacyAbs = join12(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
5630
+ const legacyAbs = join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
5585
5631
  const legacyContent = readTextIfPresent(legacyAbs);
5586
5632
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
5587
5633
  return {
@@ -5593,7 +5639,7 @@ function resolveReviewGuidelines(worktreePath) {
5593
5639
  }
5594
5640
  ensureAttachmentsGitignore(worktreePath);
5595
5641
  mkdirSync4(dirname3(localAbs), { recursive: true });
5596
- writeFileSync3(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5642
+ writeFileSync4(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5597
5643
  return {
5598
5644
  path: REVIEW_REQUEST_PATH,
5599
5645
  name: REVIEW_REQUEST_NAME,
@@ -5602,7 +5648,7 @@ function resolveReviewGuidelines(worktreePath) {
5602
5648
  };
5603
5649
  }
5604
5650
  function ensureReviewRequestFile(worktreePath) {
5605
- const repoAbs = join12(worktreePath, REPO_REVIEW_PATH);
5651
+ const repoAbs = join13(worktreePath, REPO_REVIEW_PATH);
5606
5652
  const repoContent = readTextIfPresent(repoAbs);
5607
5653
  if (repoContent) {
5608
5654
  return {
@@ -5612,8 +5658,8 @@ function ensureReviewRequestFile(worktreePath) {
5612
5658
  source: "repo"
5613
5659
  };
5614
5660
  }
5615
- const localAbs = join12(worktreePath, REVIEW_REQUEST_PATH);
5616
- const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join12(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5661
+ const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
5662
+ const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5617
5663
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
5618
5664
  const path2 = existsSync13(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
5619
5665
  return {
@@ -5624,7 +5670,7 @@ function ensureReviewRequestFile(worktreePath) {
5624
5670
  };
5625
5671
  }
5626
5672
  mkdirSync4(dirname3(repoAbs), { recursive: true });
5627
- writeFileSync3(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5673
+ writeFileSync4(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
5628
5674
  return {
5629
5675
  path: REPO_REVIEW_PATH,
5630
5676
  name: REPO_REVIEW_NAME,
@@ -5644,7 +5690,7 @@ function buildReviewRequestAttachment(content, opts) {
5644
5690
  };
5645
5691
  }
5646
5692
  function readExistingReviewRequestFile(worktreePath) {
5647
- return readTextIfPresent(join12(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join12(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join12(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5693
+ return readTextIfPresent(join13(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join13(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5648
5694
  }
5649
5695
  async function requestReview(threadRef, send2) {
5650
5696
  const from = findThreadByRef(threadRef);
@@ -6145,7 +6191,9 @@ var Orchestrator = class {
6145
6191
  updateThread(thread.id, patch);
6146
6192
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
6147
6193
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
6148
- void this.drainQueue(thread.id);
6194
+ if (thisProcessShouldDrainAgentQueues()) {
6195
+ void this.drainQueue(thread.id);
6196
+ }
6149
6197
  return this.requireThread(thread.id);
6150
6198
  });
6151
6199
  }
@@ -6198,10 +6246,10 @@ var Orchestrator = class {
6198
6246
  async sendQueuedMessageNow(threadRef, index) {
6199
6247
  const thread = this.requireThread(threadRef);
6200
6248
  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);
6249
+ const current2 = this.requireThread(thread.id);
6250
+ if (index < 0 || index >= current2.queue.length) return false;
6251
+ const item = current2.queue[index];
6252
+ const rest = current2.queue.filter((_, i) => i !== index);
6205
6253
  const queue = [item, ...rest];
6206
6254
  this.haltDrain.delete(thread.id);
6207
6255
  updateThread(thread.id, { queue });
@@ -6209,10 +6257,16 @@ var Orchestrator = class {
6209
6257
  return true;
6210
6258
  });
6211
6259
  if (!promoted) return this.requireThread(thread.id);
6260
+ const current = this.requireThread(thread.id);
6212
6261
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
6262
+ const livePid = current.agentPid;
6263
+ const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
6213
6264
  if (inFlight) {
6214
6265
  this.stop(thread.id, { clearQueue: false, continueQueue: true });
6215
6266
  } else {
6267
+ if (foreignLive) {
6268
+ this.stop(thread.id, { clearQueue: false, continueQueue: true });
6269
+ }
6216
6270
  void this.drainQueue(thread.id);
6217
6271
  }
6218
6272
  return this.requireThread(thread.id);
@@ -6647,6 +6701,13 @@ var Orchestrator = class {
6647
6701
  if (handle) handle.kill();
6648
6702
  const proc = this.processes.get(`${thread.id}:agent`);
6649
6703
  if (proc) proc.kill();
6704
+ const pid = thread.agentPid;
6705
+ if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) {
6706
+ try {
6707
+ process.kill(pid, "SIGTERM");
6708
+ } catch {
6709
+ }
6710
+ }
6650
6711
  const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
6651
6712
  if (stopped.status === "stopped") {
6652
6713
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
@@ -9632,14 +9693,14 @@ async function runCloudConnect(opts) {
9632
9693
  }
9633
9694
 
9634
9695
  // src/agents/user-mcp-config.ts
9635
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
9696
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
9636
9697
  import { homedir as homedir2 } from "os";
9637
- import { dirname as dirname4, join as join13 } from "path";
9698
+ import { dirname as dirname4, join as join14 } from "path";
9638
9699
  function userCursorMcpConfigPath() {
9639
- return join13(homedir2(), ".cursor", "mcp.json");
9700
+ return join14(homedir2(), ".cursor", "mcp.json");
9640
9701
  }
9641
9702
  function userClaudeMcpConfigPath() {
9642
- return join13(homedir2(), ".claude.json");
9703
+ return join14(homedir2(), ".claude.json");
9643
9704
  }
9644
9705
  function asObject(value) {
9645
9706
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
@@ -9668,14 +9729,14 @@ function writeMergedMcpServersJson(configPath, sideboard) {
9668
9729
  let existing = {};
9669
9730
  if (existsSync15(configPath)) {
9670
9731
  try {
9671
- existing = JSON.parse(readFileSync10(configPath, "utf8"));
9732
+ existing = JSON.parse(readFileSync11(configPath, "utf8"));
9672
9733
  } catch {
9673
9734
  existing = {};
9674
9735
  }
9675
9736
  }
9676
9737
  const next = mergeSideboardIntoMcpServersJson(existing, sideboard);
9677
9738
  mkdirSync5(dirname4(configPath), { recursive: true });
9678
- writeFileSync4(configPath, `${JSON.stringify(next, null, 2)}
9739
+ writeFileSync5(configPath, `${JSON.stringify(next, null, 2)}
9679
9740
  `);
9680
9741
  }
9681
9742
  function launchFromResolved(server) {
@@ -11492,6 +11553,7 @@ export {
11492
11553
  captureTurnBaseline,
11493
11554
  checkoutPrStackLayer,
11494
11555
  childEnvWithAppSettings,
11556
+ claimDesktopHost,
11495
11557
  claudeAdapter,
11496
11558
  claudeChromeEnabled,
11497
11559
  claudeUserSettingsPath,
@@ -11530,6 +11592,7 @@ export {
11530
11592
  decodeBrightsyTarget,
11531
11593
  deleteBranchOnPurgeEnabled,
11532
11594
  deleteThreadRecord,
11595
+ desktopHostPidPath,
11533
11596
  detectAgents,
11534
11597
  detectGhStack,
11535
11598
  detectLocalMergeConflicts,
@@ -11655,6 +11718,7 @@ export {
11655
11718
  isConductorBundledCli,
11656
11719
  isCursorAutoModel,
11657
11720
  isDefaultishSourceRef,
11721
+ isDesktopHostAlive,
11658
11722
  isDirty,
11659
11723
  isGhRateLimitError,
11660
11724
  isGlobalRepoPath,
@@ -11676,6 +11740,7 @@ export {
11676
11740
  isSlackExternalReplyPrompt,
11677
11741
  isSlackOAuthCancelled,
11678
11742
  isThinkingEffort,
11743
+ isThisProcessDesktopHost,
11679
11744
  isThreadCaffeinated,
11680
11745
  isWorkspaceScratchPath,
11681
11746
  linearAuthorizationHeader,
@@ -11777,6 +11842,7 @@ export {
11777
11842
  refreshSlackReplyBadges,
11778
11843
  registerPackagedUserMcpClients,
11779
11844
  releaseCaffeinateHoldForThread,
11845
+ releaseDesktopHost,
11780
11846
  removeWorkspace,
11781
11847
  removeWorktree,
11782
11848
  repoSlug,
@@ -11877,6 +11943,7 @@ export {
11877
11943
  taskMessageText,
11878
11944
  thinkingEffortBars,
11879
11945
  thinkingEffortLabel,
11946
+ thisProcessShouldDrainAgentQueues,
11880
11947
  threadDisplayLabel,
11881
11948
  threadFilePath,
11882
11949
  threadLockPath,