@sideboard-ai/core 0.1.97 → 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.
@@ -17,14 +17,14 @@ import {
17
17
  resolveQuotaFallbackAgent,
18
18
  sideboardMcpProfile,
19
19
  summarizeTurnStderr
20
- } from "../chunk-WBX46OPD.js";
20
+ } from "../chunk-FYS2BULQ.js";
21
21
  import "../chunk-DKHGWYWR.js";
22
22
  import {
23
23
  addWorkspace,
24
24
  ensureWorkspace,
25
25
  removeWorkspace,
26
26
  syncWorkspacesFromThreads
27
- } from "../chunk-6XBXVXX2.js";
27
+ } from "../chunk-OANJQTVG.js";
28
28
  import {
29
29
  extractPresentedPlan,
30
30
  readPlanFile,
@@ -41,8 +41,9 @@ import {
41
41
  isGlobalRepoPath,
42
42
  isGlobalThread,
43
43
  isOrchestratorThread,
44
+ isSlackCoordinatorThread,
44
45
  orchestratorSessionPoisonedByBuiltins
45
- } from "../chunk-NR6APJLD.js";
46
+ } from "../chunk-HI2OTFFR.js";
46
47
  import {
47
48
  SLACK_REPLY_FORMATTING,
48
49
  coordinatorSystemPrompt,
@@ -943,7 +944,7 @@ async function spawnAgentTurn(thread, input, onEvent) {
943
944
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
944
945
  );
945
946
  }
946
- const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-3GNPQCLE.js");
947
+ const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-WMF3BJP5.js");
947
948
  if (isGlobalThread2(thread)) {
948
949
  const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-OQOOD5ET.js");
949
950
  ensureGlobalCoordinatorCwd2(
@@ -1744,6 +1745,41 @@ async function cloneRepoIntoSideboard(opts) {
1744
1745
  return { repoPath, workspace };
1745
1746
  }
1746
1747
 
1748
+ // src/store/desktop-host.ts
1749
+ import { readFileSync as readFileSync4, unlinkSync, writeFileSync } from "fs";
1750
+ import { join as join7 } from "path";
1751
+ function desktopHostPidPath() {
1752
+ return join7(appDataDir(), "desktop-host.pid");
1753
+ }
1754
+ function readDesktopHostPid() {
1755
+ try {
1756
+ const pid = Number.parseInt(readFileSync4(desktopHostPidPath(), "utf8").trim(), 10);
1757
+ if (!Number.isFinite(pid) || pid <= 0) return null;
1758
+ return pid;
1759
+ } catch {
1760
+ return null;
1761
+ }
1762
+ }
1763
+ function pidAlive(pid) {
1764
+ try {
1765
+ process.kill(pid, 0);
1766
+ return true;
1767
+ } catch {
1768
+ return false;
1769
+ }
1770
+ }
1771
+ function isDesktopHostAlive() {
1772
+ const pid = readDesktopHostPid();
1773
+ return pid != null && pidAlive(pid);
1774
+ }
1775
+ function isThisProcessDesktopHost() {
1776
+ return readDesktopHostPid() === process.pid && pidAlive(process.pid);
1777
+ }
1778
+ function thisProcessShouldDrainAgentQueues() {
1779
+ if (isThisProcessDesktopHost()) return true;
1780
+ return !isDesktopHostAlive();
1781
+ }
1782
+
1747
1783
  // src/threads/create.ts
1748
1784
  import { existsSync as existsSync6 } from "fs";
1749
1785
 
@@ -1851,7 +1887,7 @@ async function createThread(input, _onSetupLine) {
1851
1887
  return readThread(thread.id) ?? thread;
1852
1888
  }
1853
1889
  async function listLinearIssues(agent, repoPath) {
1854
- const { getAdapter: getAdapter2 } = await import("../agents-HBLA6FEV.js");
1890
+ const { getAdapter: getAdapter2 } = await import("../agents-ESJKIQQA.js");
1855
1891
  await requireAgent(agent, { requireLinear: true });
1856
1892
  const adapter = getAdapter2(agent);
1857
1893
  if (!adapter.listLinearIssues) {
@@ -2205,6 +2241,7 @@ function createChatTab(input) {
2205
2241
  if (isOrchestratorThread(from) || binding.sourceType === "orchestration") {
2206
2242
  assertOrchestratorCapableAgent(nextAgent);
2207
2243
  }
2244
+ const singletonInbox = isSlackCoordinatorThread(from) || isCloudCoordinatorThread(from);
2208
2245
  const thread = createEmptyThread({
2209
2246
  title,
2210
2247
  // Chat-tab nicknames (soccer team or explicit) must stick. Post-turn
@@ -2212,6 +2249,10 @@ function createChatTab(input) {
2212
2249
  // shared worktree folder name (e.g. fork "Arsenal" → "Monaco").
2213
2250
  userSetTitle: true,
2214
2251
  ...binding,
2252
+ // Slack / Brightsy cloud identity stays on the original chat. A + tab
2253
+ // that copies `slack:team:user` would steal inbound DMs after you close
2254
+ // the connected chat.
2255
+ sourceRef: singletonInbox ? title : binding.sourceRef,
2215
2256
  agent: nextAgent,
2216
2257
  model: input.model !== void 0 ? input.model : input.agent && input.agent !== from.agent ? null : from.model,
2217
2258
  effort: input.effort !== void 0 ? input.effort : from.effort,
@@ -2245,8 +2286,8 @@ function forkChatTab(input) {
2245
2286
 
2246
2287
  // src/review/request-review.ts
2247
2288
  import { randomUUID as randomUUID2 } from "crypto";
2248
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "fs";
2249
- import { dirname as dirname2, join as join7 } from "path";
2289
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
2290
+ import { dirname as dirname2, join as join8 } from "path";
2250
2291
 
2251
2292
  // src/review/review-request-template.ts
2252
2293
  var REVIEW_REQUEST_TEMPLATE = `# Review guidelines:
@@ -2393,20 +2434,20 @@ function shouldRefreshReviewRequestTemplate(content) {
2393
2434
  function readTextIfPresent(abs) {
2394
2435
  if (!existsSync7(abs)) return null;
2395
2436
  try {
2396
- const content = readFileSync4(abs, "utf8");
2437
+ const content = readFileSync5(abs, "utf8");
2397
2438
  return content.trim() ? content : null;
2398
2439
  } catch {
2399
2440
  return null;
2400
2441
  }
2401
2442
  }
2402
2443
  function ensureAttachmentsGitignore(worktreePath) {
2403
- const gitignoreAbs = join7(worktreePath, ATTACHMENTS_DIR, ".gitignore");
2444
+ const gitignoreAbs = join8(worktreePath, ATTACHMENTS_DIR, ".gitignore");
2404
2445
  if (existsSync7(gitignoreAbs)) return;
2405
2446
  mkdirSync2(dirname2(gitignoreAbs), { recursive: true });
2406
- writeFileSync(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
2447
+ writeFileSync2(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
2407
2448
  }
2408
2449
  function resolveReviewGuidelines(worktreePath) {
2409
- const repoAbs = join7(worktreePath, REPO_REVIEW_PATH);
2450
+ const repoAbs = join8(worktreePath, REPO_REVIEW_PATH);
2410
2451
  const repoContent = readTextIfPresent(repoAbs);
2411
2452
  if (repoContent) {
2412
2453
  return {
@@ -2416,7 +2457,7 @@ function resolveReviewGuidelines(worktreePath) {
2416
2457
  source: "repo"
2417
2458
  };
2418
2459
  }
2419
- const localAbs = join7(worktreePath, REVIEW_REQUEST_PATH);
2460
+ const localAbs = join8(worktreePath, REVIEW_REQUEST_PATH);
2420
2461
  const localContent = readTextIfPresent(localAbs);
2421
2462
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
2422
2463
  return {
@@ -2426,7 +2467,7 @@ function resolveReviewGuidelines(worktreePath) {
2426
2467
  source: "local"
2427
2468
  };
2428
2469
  }
2429
- const legacyAbs = join7(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
2470
+ const legacyAbs = join8(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
2430
2471
  const legacyContent = readTextIfPresent(legacyAbs);
2431
2472
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
2432
2473
  return {
@@ -2438,7 +2479,7 @@ function resolveReviewGuidelines(worktreePath) {
2438
2479
  }
2439
2480
  ensureAttachmentsGitignore(worktreePath);
2440
2481
  mkdirSync2(dirname2(localAbs), { recursive: true });
2441
- writeFileSync(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
2482
+ writeFileSync2(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
2442
2483
  return {
2443
2484
  path: REVIEW_REQUEST_PATH,
2444
2485
  name: REVIEW_REQUEST_NAME,
@@ -2637,20 +2678,20 @@ import {
2637
2678
  existsSync as existsSync8,
2638
2679
  mkdtempSync,
2639
2680
  readdirSync as readdirSync3,
2640
- readFileSync as readFileSync5,
2681
+ readFileSync as readFileSync6,
2641
2682
  rmSync
2642
2683
  } from "fs";
2643
2684
  import { tmpdir } from "os";
2644
- import { join as join8 } from "path";
2685
+ import { join as join9 } from "path";
2645
2686
  import { createRequire } from "module";
2646
- var CONDUCTOR_APP_SUPPORT = join8(
2687
+ var CONDUCTOR_APP_SUPPORT = join9(
2647
2688
  process.env.HOME ?? "",
2648
2689
  "Library",
2649
2690
  "Application Support",
2650
2691
  "com.conductor.app"
2651
2692
  );
2652
- var CONDUCTOR_DB = join8(CONDUCTOR_APP_SUPPORT, "conductor.db");
2653
- var CURSOR_SDK_STORE = join8(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
2693
+ var CONDUCTOR_DB = join9(CONDUCTOR_APP_SUPPORT, "conductor.db");
2694
+ var CURSOR_SDK_STORE = join9(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
2654
2695
  function openReadonlySqlite(file) {
2655
2696
  const req = createRequire(import.meta.url);
2656
2697
  const Database = req("better-sqlite3");
@@ -2677,11 +2718,11 @@ function resolveConductorCursorAgentId(workspacePath) {
2677
2718
  return null;
2678
2719
  }
2679
2720
  for (const hash of hashes) {
2680
- const agentsFile = join8(CURSOR_SDK_STORE, hash, "agents.ndjson");
2721
+ const agentsFile = join9(CURSOR_SDK_STORE, hash, "agents.ndjson");
2681
2722
  if (!existsSync8(agentsFile)) continue;
2682
2723
  let text3;
2683
2724
  try {
2684
- text3 = readFileSync5(agentsFile, "utf8");
2725
+ text3 = readFileSync6(agentsFile, "utf8");
2685
2726
  } catch {
2686
2727
  continue;
2687
2728
  }
@@ -2724,7 +2765,7 @@ async function adoptThread(input) {
2724
2765
  messages: input.messages ?? []
2725
2766
  });
2726
2767
  writeThread(thread);
2727
- const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-J4WG6UFR.js");
2768
+ const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-MZVQHRSJ.js");
2728
2769
  await ensureWorkspace2(repoPath);
2729
2770
  return thread;
2730
2771
  }
@@ -2732,8 +2773,8 @@ function listConductorWorkspaces() {
2732
2773
  if (!existsSync8(CONDUCTOR_DB)) {
2733
2774
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
2734
2775
  }
2735
- const tmp = mkdtempSync(join8(tmpdir(), "sideboard-conductor-"));
2736
- const snapshot = join8(tmp, "conductor.db");
2776
+ const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
2777
+ const snapshot = join9(tmp, "conductor.db");
2737
2778
  try {
2738
2779
  copyFileSync2(CONDUCTOR_DB, snapshot);
2739
2780
  for (const suffix of ["-wal", "-shm"]) {
@@ -2823,8 +2864,8 @@ function importConductorWorkspace(workspaceId) {
2823
2864
  if (!existsSync8(CONDUCTOR_DB)) {
2824
2865
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
2825
2866
  }
2826
- const tmp = mkdtempSync(join8(tmpdir(), "sideboard-conductor-"));
2827
- const snapshot = join8(tmp, "conductor.db");
2867
+ const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
2868
+ const snapshot = join9(tmp, "conductor.db");
2828
2869
  try {
2829
2870
  copyFileSync2(CONDUCTOR_DB, snapshot);
2830
2871
  for (const suffix of ["-wal", "-shm"]) {
@@ -3204,8 +3245,8 @@ async function createPrStack(input, onSetupLine) {
3204
3245
  }
3205
3246
 
3206
3247
  // src/diff/diff.ts
3207
- import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
3208
- import { dirname as dirname3, join as join9 } from "path";
3248
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync7, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
3249
+ import { dirname as dirname3, join as join10 } from "path";
3209
3250
  async function inspectGitWorktree(worktreePath) {
3210
3251
  if (!worktreePath || !existsSync10(worktreePath)) return "missing_worktree";
3211
3252
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
@@ -3351,11 +3392,11 @@ new file mode 100644
3351
3392
  };
3352
3393
  }
3353
3394
  async function untrackedPatch(worktreePath, path, maxHunk) {
3354
- const abs = join9(worktreePath, path);
3395
+ const abs = join10(worktreePath, path);
3355
3396
  try {
3356
3397
  const st = statSync2(abs);
3357
3398
  if (st.isFile() && st.size > maxHunk) {
3358
- const buf = readFileSync6(abs).subarray(0, maxHunk);
3399
+ const buf = readFileSync7(abs).subarray(0, maxHunk);
3359
3400
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
3360
3401
  }
3361
3402
  } catch {
@@ -3856,7 +3897,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
3856
3897
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
3857
3898
  assertSafeRelativePath(relativePath);
3858
3899
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
3859
- const abs = join9(worktreePath, relativePath);
3900
+ const abs = join10(worktreePath, relativePath);
3860
3901
  const st = statSync2(abs);
3861
3902
  if (!st.isFile()) {
3862
3903
  throw new Error(`Not a file: ${relativePath}`);
@@ -3866,7 +3907,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
3866
3907
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
3867
3908
  );
3868
3909
  }
3869
- const buf = readFileSync6(abs);
3910
+ const buf = readFileSync7(abs);
3870
3911
  return {
3871
3912
  path: relativePath,
3872
3913
  contentBase64: buf.toString("base64"),
@@ -3876,12 +3917,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
3876
3917
  function readWorktreeFile(worktreePath, relativePath, opts) {
3877
3918
  assertSafeRelativePath(relativePath);
3878
3919
  const maxBytes = opts?.maxBytes ?? 2e5;
3879
- const abs = join9(worktreePath, relativePath);
3920
+ const abs = join10(worktreePath, relativePath);
3880
3921
  const st = statSync2(abs);
3881
3922
  if (!st.isFile()) {
3882
3923
  throw new Error(`Not a file: ${relativePath}`);
3883
3924
  }
3884
- const buf = readFileSync6(abs);
3925
+ const buf = readFileSync7(abs);
3885
3926
  if (isImageRelativePath(relativePath)) {
3886
3927
  const maxImageBytes = Math.max(maxBytes, 15e6);
3887
3928
  const truncated2 = buf.length > maxImageBytes;
@@ -3924,9 +3965,9 @@ function assertSafeRelativePath(relativePath) {
3924
3965
  }
3925
3966
  function writeWorktreeFile(worktreePath, relativePath, content) {
3926
3967
  assertSafeRelativePath(relativePath);
3927
- const abs = join9(worktreePath, relativePath);
3968
+ const abs = join10(worktreePath, relativePath);
3928
3969
  mkdirSync3(dirname3(abs), { recursive: true });
3929
- writeFileSync2(abs, content, "utf8");
3970
+ writeFileSync3(abs, content, "utf8");
3930
3971
  return { path: relativePath };
3931
3972
  }
3932
3973
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -4029,9 +4070,9 @@ async function confirmLand(thread, opts) {
4029
4070
  }
4030
4071
 
4031
4072
  // src/skills/discover.ts
4032
- import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
4073
+ import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
4033
4074
  import { homedir } from "os";
4034
- import { join as join10 } from "path";
4075
+ import { join as join11 } from "path";
4035
4076
  function toCommand(name) {
4036
4077
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4037
4078
  }
@@ -4063,7 +4104,7 @@ function parseFrontmatter(content) {
4063
4104
  }
4064
4105
  function readSkill(skillMd, source) {
4065
4106
  try {
4066
- const content = readFileSync7(skillMd, "utf8");
4107
+ const content = readFileSync8(skillMd, "utf8");
4067
4108
  const { name: fmName, description } = parseFrontmatter(content);
4068
4109
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
4069
4110
  const name = fmName || dirName;
@@ -4091,7 +4132,7 @@ function scanSkillsDir(dir, source, out) {
4091
4132
  }
4092
4133
  for (const entry of entries) {
4093
4134
  if (entry.startsWith(".")) continue;
4094
- const skillMd = join10(dir, entry, "SKILL.md");
4135
+ const skillMd = join11(dir, entry, "SKILL.md");
4095
4136
  if (!existsSync11(skillMd)) continue;
4096
4137
  try {
4097
4138
  if (!statSync3(skillMd).isFile()) continue;
@@ -4113,12 +4154,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
4113
4154
  return;
4114
4155
  }
4115
4156
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
4116
- const skill = readSkill(join10(dir, "SKILL.md"), "cli");
4157
+ const skill = readSkill(join11(dir, "SKILL.md"), "cli");
4117
4158
  if (skill) out.push(skill);
4118
4159
  }
4119
4160
  for (const entry of entries) {
4120
4161
  if (entry === "node_modules" || entry === ".git") continue;
4121
- const full = join10(dir, entry);
4162
+ const full = join11(dir, entry);
4122
4163
  try {
4123
4164
  if (!statSync3(full).isDirectory()) continue;
4124
4165
  } catch {
@@ -4138,17 +4179,17 @@ function discoverSkills(worktreePath) {
4138
4179
  const home = homedir();
4139
4180
  const collected = [];
4140
4181
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
4141
- scanSkillsDir(join10(worktreePath, rel), "workspace", collected);
4182
+ scanSkillsDir(join11(worktreePath, rel), "workspace", collected);
4142
4183
  }
4143
4184
  for (const abs of [
4144
- join10(home, ".claude/skills"),
4145
- join10(home, ".cursor/skills"),
4146
- join10(home, ".sideboard/skills"),
4147
- join10(home, ".brightsy/skills")
4185
+ join11(home, ".claude/skills"),
4186
+ join11(home, ".cursor/skills"),
4187
+ join11(home, ".sideboard/skills"),
4188
+ join11(home, ".brightsy/skills")
4148
4189
  ]) {
4149
4190
  scanSkillsDir(abs, "user", collected);
4150
4191
  }
4151
- scanClaudePluginSkills(join10(home, ".claude/plugins"), collected);
4192
+ scanClaudePluginSkills(join11(home, ".claude/plugins"), collected);
4152
4193
  const rank = { workspace: 0, user: 1, cli: 2 };
4153
4194
  const byCommand = /* @__PURE__ */ new Map();
4154
4195
  for (const skill of collected) {
@@ -4160,7 +4201,7 @@ function discoverSkills(worktreePath) {
4160
4201
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
4161
4202
  }
4162
4203
  function readSkillBody(skillPath, maxChars = 12e3) {
4163
- const raw = readFileSync7(skillPath, "utf8");
4204
+ const raw = readFileSync8(skillPath, "utf8");
4164
4205
  if (raw.startsWith("---")) {
4165
4206
  const end = raw.indexOf("\n---", 3);
4166
4207
  if (end >= 0) {
@@ -4253,8 +4294,8 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
4253
4294
  }
4254
4295
 
4255
4296
  // src/composer/stage-files.ts
4256
- import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
4257
- import { basename as basename3, extname, join as join11 } from "path";
4297
+ import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync9, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
4298
+ import { basename as basename3, extname, join as join12 } from "path";
4258
4299
  import { randomUUID as randomUUID4 } from "crypto";
4259
4300
  var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
4260
4301
  "png",
@@ -4289,22 +4330,22 @@ function imageMimeType(filePath) {
4289
4330
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
4290
4331
  }
4291
4332
  function ensureAttachmentsDir(worktreePath) {
4292
- const dir = join11(worktreePath, ATTACHMENTS_DIR);
4333
+ const dir = join12(worktreePath, ATTACHMENTS_DIR);
4293
4334
  mkdirSync4(dir, { recursive: true });
4294
- const gi = join11(dir, ".gitignore");
4335
+ const gi = join12(dir, ".gitignore");
4295
4336
  if (!existsSync12(gi)) {
4296
- writeFileSync3(gi, attachmentsGitignoreBody(), "utf8");
4337
+ writeFileSync4(gi, attachmentsGitignoreBody(), "utf8");
4297
4338
  }
4298
4339
  return dir;
4299
4340
  }
4300
4341
  function uniqueAttachmentName(dir, originalName) {
4301
4342
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
4302
- if (!existsSync12(join11(dir, safe))) return safe;
4343
+ if (!existsSync12(join12(dir, safe))) return safe;
4303
4344
  const ext = extname(safe);
4304
4345
  const stem = ext ? safe.slice(0, -ext.length) : safe;
4305
4346
  for (let i = 1; i < 1e4; i++) {
4306
4347
  const candidate = `${stem}-${i}${ext}`;
4307
- if (!existsSync12(join11(dir, candidate))) return candidate;
4348
+ if (!existsSync12(join12(dir, candidate))) return candidate;
4308
4349
  }
4309
4350
  return `${stem}-${randomUUID4()}${ext}`;
4310
4351
  }
@@ -4365,10 +4406,10 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
4365
4406
  const st = statSync4(abs);
4366
4407
  if (!st.isFile()) continue;
4367
4408
  const name = uniqueAttachmentName(dir, originalName);
4368
- const destAbs = join11(dir, name);
4409
+ const destAbs = join12(dir, name);
4369
4410
  copyFileSync3(abs, destAbs);
4370
4411
  const rel = `${ATTACHMENTS_DIR}/${name}`;
4371
- const buf = readFileSync8(destAbs);
4412
+ const buf = readFileSync9(destAbs);
4372
4413
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
4373
4414
  } catch (err) {
4374
4415
  out.push({
@@ -4390,8 +4431,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
4390
4431
  try {
4391
4432
  const buf = Buffer.from(item.dataBase64, "base64");
4392
4433
  const name = uniqueAttachmentName(dir, originalName);
4393
- const destAbs = join11(dir, name);
4394
- writeFileSync3(destAbs, buf);
4434
+ const destAbs = join12(dir, name);
4435
+ writeFileSync4(destAbs, buf);
4395
4436
  const rel = `${ATTACHMENTS_DIR}/${name}`;
4396
4437
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
4397
4438
  } catch (err) {
@@ -4419,10 +4460,10 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
4419
4460
  }
4420
4461
  const name = basename3(rel);
4421
4462
  try {
4422
- const abs = join11(worktreePath, rel);
4463
+ const abs = join12(worktreePath, rel);
4423
4464
  const st = statSync4(abs);
4424
4465
  if (!st.isFile()) continue;
4425
- const buf = readFileSync8(abs);
4466
+ const buf = readFileSync9(abs);
4426
4467
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
4427
4468
  } catch (err) {
4428
4469
  out.push({
@@ -4437,8 +4478,8 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
4437
4478
  }
4438
4479
 
4439
4480
  // src/agents/instructions.ts
4440
- import { existsSync as existsSync13, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
4441
- import { join as join12 } from "path";
4481
+ import { existsSync as existsSync13, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
4482
+ import { join as join13 } from "path";
4442
4483
  function normPath(p) {
4443
4484
  return p.replace(/\/+$/, "");
4444
4485
  }
@@ -4942,6 +4983,9 @@ var Orchestrator = class {
4942
4983
  }
4943
4984
  return withThreadLock(thread.id, async () => {
4944
4985
  const current = this.requireThread(thread.id);
4986
+ if (current.status === "archived") {
4987
+ throw new Error(`Thread is archived: ${thread.id}`);
4988
+ }
4945
4989
  const queue = [...current.queue, prompt];
4946
4990
  this.haltDrain.delete(thread.id);
4947
4991
  const patch = { queue, status: "queued" };
@@ -4952,7 +4996,9 @@ var Orchestrator = class {
4952
4996
  updateThread(thread.id, patch);
4953
4997
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
4954
4998
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
4955
- void this.drainQueue(thread.id);
4999
+ if (thisProcessShouldDrainAgentQueues()) {
5000
+ void this.drainQueue(thread.id);
5001
+ }
4956
5002
  return this.requireThread(thread.id);
4957
5003
  });
4958
5004
  }
@@ -5005,10 +5051,10 @@ var Orchestrator = class {
5005
5051
  async sendQueuedMessageNow(threadRef, index) {
5006
5052
  const thread = this.requireThread(threadRef);
5007
5053
  const promoted = await withThreadLock(thread.id, async () => {
5008
- const current = this.requireThread(thread.id);
5009
- if (index < 0 || index >= current.queue.length) return false;
5010
- const item = current.queue[index];
5011
- const rest = current.queue.filter((_, i) => i !== index);
5054
+ const current2 = this.requireThread(thread.id);
5055
+ if (index < 0 || index >= current2.queue.length) return false;
5056
+ const item = current2.queue[index];
5057
+ const rest = current2.queue.filter((_, i) => i !== index);
5012
5058
  const queue = [item, ...rest];
5013
5059
  this.haltDrain.delete(thread.id);
5014
5060
  updateThread(thread.id, { queue });
@@ -5016,10 +5062,16 @@ var Orchestrator = class {
5016
5062
  return true;
5017
5063
  });
5018
5064
  if (!promoted) return this.requireThread(thread.id);
5065
+ const current = this.requireThread(thread.id);
5019
5066
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
5067
+ const livePid = current.agentPid;
5068
+ const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
5020
5069
  if (inFlight) {
5021
5070
  this.stop(thread.id, { clearQueue: false, continueQueue: true });
5022
5071
  } else {
5072
+ if (foreignLive) {
5073
+ this.stop(thread.id, { clearQueue: false, continueQueue: true });
5074
+ }
5023
5075
  void this.drainQueue(thread.id);
5024
5076
  }
5025
5077
  return this.requireThread(thread.id);
@@ -5454,6 +5506,13 @@ var Orchestrator = class {
5454
5506
  if (handle) handle.kill();
5455
5507
  const proc = this.processes.get(`${thread.id}:agent`);
5456
5508
  if (proc) proc.kill();
5509
+ const pid = thread.agentPid;
5510
+ if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) {
5511
+ try {
5512
+ process.kill(pid, "SIGTERM");
5513
+ } catch {
5514
+ }
5515
+ }
5457
5516
  const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
5458
5517
  if (stopped.status === "stopped") {
5459
5518
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
@@ -6192,7 +6251,7 @@ var Orchestrator = class {
6192
6251
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
6193
6252
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
6194
6253
  try {
6195
- const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-J4WG6UFR.js");
6254
+ const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-MZVQHRSJ.js");
6196
6255
  await ensureWorkspace2(thread.repoPath);
6197
6256
  } catch {
6198
6257
  }
@@ -4,8 +4,8 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-QKYO6BHB.js";
8
- import "./chunk-CLGO7TLO.js";
7
+ } from "./chunk-UYQYK2RY.js";
8
+ import "./chunk-FO67IJTY.js";
9
9
  import "./chunk-XUWDLRAE.js";
10
10
  import "./chunk-CIRXAYWS.js";
11
11
  import "./chunk-FKOIHGKV.js";
@@ -6,8 +6,8 @@ import {
6
6
  listWorkspaces,
7
7
  removeWorkspace,
8
8
  syncWorkspacesFromThreads
9
- } from "./chunk-6XBXVXX2.js";
10
- import "./chunk-NR6APJLD.js";
9
+ } from "./chunk-OANJQTVG.js";
10
+ import "./chunk-HI2OTFFR.js";
11
11
  import "./chunk-XH2GS2LO.js";
12
12
  import "./chunk-R7BQBSDT.js";
13
13
  import "./chunk-B3SJXYIJ.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.97",
3
+ "version": "0.1.99",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",