@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.
@@ -1745,6 +1745,41 @@ async function cloneRepoIntoSideboard(opts) {
1745
1745
  return { repoPath, workspace };
1746
1746
  }
1747
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
+
1748
1783
  // src/threads/create.ts
1749
1784
  import { existsSync as existsSync6 } from "fs";
1750
1785
 
@@ -2251,8 +2286,8 @@ function forkChatTab(input) {
2251
2286
 
2252
2287
  // src/review/request-review.ts
2253
2288
  import { randomUUID as randomUUID2 } from "crypto";
2254
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "fs";
2255
- 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";
2256
2291
 
2257
2292
  // src/review/review-request-template.ts
2258
2293
  var REVIEW_REQUEST_TEMPLATE = `# Review guidelines:
@@ -2399,20 +2434,20 @@ function shouldRefreshReviewRequestTemplate(content) {
2399
2434
  function readTextIfPresent(abs) {
2400
2435
  if (!existsSync7(abs)) return null;
2401
2436
  try {
2402
- const content = readFileSync4(abs, "utf8");
2437
+ const content = readFileSync5(abs, "utf8");
2403
2438
  return content.trim() ? content : null;
2404
2439
  } catch {
2405
2440
  return null;
2406
2441
  }
2407
2442
  }
2408
2443
  function ensureAttachmentsGitignore(worktreePath) {
2409
- const gitignoreAbs = join7(worktreePath, ATTACHMENTS_DIR, ".gitignore");
2444
+ const gitignoreAbs = join8(worktreePath, ATTACHMENTS_DIR, ".gitignore");
2410
2445
  if (existsSync7(gitignoreAbs)) return;
2411
2446
  mkdirSync2(dirname2(gitignoreAbs), { recursive: true });
2412
- writeFileSync(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
2447
+ writeFileSync2(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
2413
2448
  }
2414
2449
  function resolveReviewGuidelines(worktreePath) {
2415
- const repoAbs = join7(worktreePath, REPO_REVIEW_PATH);
2450
+ const repoAbs = join8(worktreePath, REPO_REVIEW_PATH);
2416
2451
  const repoContent = readTextIfPresent(repoAbs);
2417
2452
  if (repoContent) {
2418
2453
  return {
@@ -2422,7 +2457,7 @@ function resolveReviewGuidelines(worktreePath) {
2422
2457
  source: "repo"
2423
2458
  };
2424
2459
  }
2425
- const localAbs = join7(worktreePath, REVIEW_REQUEST_PATH);
2460
+ const localAbs = join8(worktreePath, REVIEW_REQUEST_PATH);
2426
2461
  const localContent = readTextIfPresent(localAbs);
2427
2462
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
2428
2463
  return {
@@ -2432,7 +2467,7 @@ function resolveReviewGuidelines(worktreePath) {
2432
2467
  source: "local"
2433
2468
  };
2434
2469
  }
2435
- const legacyAbs = join7(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
2470
+ const legacyAbs = join8(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
2436
2471
  const legacyContent = readTextIfPresent(legacyAbs);
2437
2472
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
2438
2473
  return {
@@ -2444,7 +2479,7 @@ function resolveReviewGuidelines(worktreePath) {
2444
2479
  }
2445
2480
  ensureAttachmentsGitignore(worktreePath);
2446
2481
  mkdirSync2(dirname2(localAbs), { recursive: true });
2447
- writeFileSync(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
2482
+ writeFileSync2(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
2448
2483
  return {
2449
2484
  path: REVIEW_REQUEST_PATH,
2450
2485
  name: REVIEW_REQUEST_NAME,
@@ -2643,20 +2678,20 @@ import {
2643
2678
  existsSync as existsSync8,
2644
2679
  mkdtempSync,
2645
2680
  readdirSync as readdirSync3,
2646
- readFileSync as readFileSync5,
2681
+ readFileSync as readFileSync6,
2647
2682
  rmSync
2648
2683
  } from "fs";
2649
2684
  import { tmpdir } from "os";
2650
- import { join as join8 } from "path";
2685
+ import { join as join9 } from "path";
2651
2686
  import { createRequire } from "module";
2652
- var CONDUCTOR_APP_SUPPORT = join8(
2687
+ var CONDUCTOR_APP_SUPPORT = join9(
2653
2688
  process.env.HOME ?? "",
2654
2689
  "Library",
2655
2690
  "Application Support",
2656
2691
  "com.conductor.app"
2657
2692
  );
2658
- var CONDUCTOR_DB = join8(CONDUCTOR_APP_SUPPORT, "conductor.db");
2659
- 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");
2660
2695
  function openReadonlySqlite(file) {
2661
2696
  const req = createRequire(import.meta.url);
2662
2697
  const Database = req("better-sqlite3");
@@ -2683,11 +2718,11 @@ function resolveConductorCursorAgentId(workspacePath) {
2683
2718
  return null;
2684
2719
  }
2685
2720
  for (const hash of hashes) {
2686
- const agentsFile = join8(CURSOR_SDK_STORE, hash, "agents.ndjson");
2721
+ const agentsFile = join9(CURSOR_SDK_STORE, hash, "agents.ndjson");
2687
2722
  if (!existsSync8(agentsFile)) continue;
2688
2723
  let text3;
2689
2724
  try {
2690
- text3 = readFileSync5(agentsFile, "utf8");
2725
+ text3 = readFileSync6(agentsFile, "utf8");
2691
2726
  } catch {
2692
2727
  continue;
2693
2728
  }
@@ -2738,8 +2773,8 @@ function listConductorWorkspaces() {
2738
2773
  if (!existsSync8(CONDUCTOR_DB)) {
2739
2774
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
2740
2775
  }
2741
- const tmp = mkdtempSync(join8(tmpdir(), "sideboard-conductor-"));
2742
- const snapshot = join8(tmp, "conductor.db");
2776
+ const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
2777
+ const snapshot = join9(tmp, "conductor.db");
2743
2778
  try {
2744
2779
  copyFileSync2(CONDUCTOR_DB, snapshot);
2745
2780
  for (const suffix of ["-wal", "-shm"]) {
@@ -2829,8 +2864,8 @@ function importConductorWorkspace(workspaceId) {
2829
2864
  if (!existsSync8(CONDUCTOR_DB)) {
2830
2865
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
2831
2866
  }
2832
- const tmp = mkdtempSync(join8(tmpdir(), "sideboard-conductor-"));
2833
- const snapshot = join8(tmp, "conductor.db");
2867
+ const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
2868
+ const snapshot = join9(tmp, "conductor.db");
2834
2869
  try {
2835
2870
  copyFileSync2(CONDUCTOR_DB, snapshot);
2836
2871
  for (const suffix of ["-wal", "-shm"]) {
@@ -3210,8 +3245,8 @@ async function createPrStack(input, onSetupLine) {
3210
3245
  }
3211
3246
 
3212
3247
  // src/diff/diff.ts
3213
- import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
3214
- 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";
3215
3250
  async function inspectGitWorktree(worktreePath) {
3216
3251
  if (!worktreePath || !existsSync10(worktreePath)) return "missing_worktree";
3217
3252
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
@@ -3357,11 +3392,11 @@ new file mode 100644
3357
3392
  };
3358
3393
  }
3359
3394
  async function untrackedPatch(worktreePath, path, maxHunk) {
3360
- const abs = join9(worktreePath, path);
3395
+ const abs = join10(worktreePath, path);
3361
3396
  try {
3362
3397
  const st = statSync2(abs);
3363
3398
  if (st.isFile() && st.size > maxHunk) {
3364
- const buf = readFileSync6(abs).subarray(0, maxHunk);
3399
+ const buf = readFileSync7(abs).subarray(0, maxHunk);
3365
3400
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
3366
3401
  }
3367
3402
  } catch {
@@ -3862,7 +3897,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
3862
3897
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
3863
3898
  assertSafeRelativePath(relativePath);
3864
3899
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
3865
- const abs = join9(worktreePath, relativePath);
3900
+ const abs = join10(worktreePath, relativePath);
3866
3901
  const st = statSync2(abs);
3867
3902
  if (!st.isFile()) {
3868
3903
  throw new Error(`Not a file: ${relativePath}`);
@@ -3872,7 +3907,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
3872
3907
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
3873
3908
  );
3874
3909
  }
3875
- const buf = readFileSync6(abs);
3910
+ const buf = readFileSync7(abs);
3876
3911
  return {
3877
3912
  path: relativePath,
3878
3913
  contentBase64: buf.toString("base64"),
@@ -3882,12 +3917,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
3882
3917
  function readWorktreeFile(worktreePath, relativePath, opts) {
3883
3918
  assertSafeRelativePath(relativePath);
3884
3919
  const maxBytes = opts?.maxBytes ?? 2e5;
3885
- const abs = join9(worktreePath, relativePath);
3920
+ const abs = join10(worktreePath, relativePath);
3886
3921
  const st = statSync2(abs);
3887
3922
  if (!st.isFile()) {
3888
3923
  throw new Error(`Not a file: ${relativePath}`);
3889
3924
  }
3890
- const buf = readFileSync6(abs);
3925
+ const buf = readFileSync7(abs);
3891
3926
  if (isImageRelativePath(relativePath)) {
3892
3927
  const maxImageBytes = Math.max(maxBytes, 15e6);
3893
3928
  const truncated2 = buf.length > maxImageBytes;
@@ -3930,9 +3965,9 @@ function assertSafeRelativePath(relativePath) {
3930
3965
  }
3931
3966
  function writeWorktreeFile(worktreePath, relativePath, content) {
3932
3967
  assertSafeRelativePath(relativePath);
3933
- const abs = join9(worktreePath, relativePath);
3968
+ const abs = join10(worktreePath, relativePath);
3934
3969
  mkdirSync3(dirname3(abs), { recursive: true });
3935
- writeFileSync2(abs, content, "utf8");
3970
+ writeFileSync3(abs, content, "utf8");
3936
3971
  return { path: relativePath };
3937
3972
  }
3938
3973
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -4035,9 +4070,9 @@ async function confirmLand(thread, opts) {
4035
4070
  }
4036
4071
 
4037
4072
  // src/skills/discover.ts
4038
- 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";
4039
4074
  import { homedir } from "os";
4040
- import { join as join10 } from "path";
4075
+ import { join as join11 } from "path";
4041
4076
  function toCommand(name) {
4042
4077
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4043
4078
  }
@@ -4069,7 +4104,7 @@ function parseFrontmatter(content) {
4069
4104
  }
4070
4105
  function readSkill(skillMd, source) {
4071
4106
  try {
4072
- const content = readFileSync7(skillMd, "utf8");
4107
+ const content = readFileSync8(skillMd, "utf8");
4073
4108
  const { name: fmName, description } = parseFrontmatter(content);
4074
4109
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
4075
4110
  const name = fmName || dirName;
@@ -4097,7 +4132,7 @@ function scanSkillsDir(dir, source, out) {
4097
4132
  }
4098
4133
  for (const entry of entries) {
4099
4134
  if (entry.startsWith(".")) continue;
4100
- const skillMd = join10(dir, entry, "SKILL.md");
4135
+ const skillMd = join11(dir, entry, "SKILL.md");
4101
4136
  if (!existsSync11(skillMd)) continue;
4102
4137
  try {
4103
4138
  if (!statSync3(skillMd).isFile()) continue;
@@ -4119,12 +4154,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
4119
4154
  return;
4120
4155
  }
4121
4156
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
4122
- const skill = readSkill(join10(dir, "SKILL.md"), "cli");
4157
+ const skill = readSkill(join11(dir, "SKILL.md"), "cli");
4123
4158
  if (skill) out.push(skill);
4124
4159
  }
4125
4160
  for (const entry of entries) {
4126
4161
  if (entry === "node_modules" || entry === ".git") continue;
4127
- const full = join10(dir, entry);
4162
+ const full = join11(dir, entry);
4128
4163
  try {
4129
4164
  if (!statSync3(full).isDirectory()) continue;
4130
4165
  } catch {
@@ -4144,17 +4179,17 @@ function discoverSkills(worktreePath) {
4144
4179
  const home = homedir();
4145
4180
  const collected = [];
4146
4181
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
4147
- scanSkillsDir(join10(worktreePath, rel), "workspace", collected);
4182
+ scanSkillsDir(join11(worktreePath, rel), "workspace", collected);
4148
4183
  }
4149
4184
  for (const abs of [
4150
- join10(home, ".claude/skills"),
4151
- join10(home, ".cursor/skills"),
4152
- join10(home, ".sideboard/skills"),
4153
- join10(home, ".brightsy/skills")
4185
+ join11(home, ".claude/skills"),
4186
+ join11(home, ".cursor/skills"),
4187
+ join11(home, ".sideboard/skills"),
4188
+ join11(home, ".brightsy/skills")
4154
4189
  ]) {
4155
4190
  scanSkillsDir(abs, "user", collected);
4156
4191
  }
4157
- scanClaudePluginSkills(join10(home, ".claude/plugins"), collected);
4192
+ scanClaudePluginSkills(join11(home, ".claude/plugins"), collected);
4158
4193
  const rank = { workspace: 0, user: 1, cli: 2 };
4159
4194
  const byCommand = /* @__PURE__ */ new Map();
4160
4195
  for (const skill of collected) {
@@ -4166,7 +4201,7 @@ function discoverSkills(worktreePath) {
4166
4201
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
4167
4202
  }
4168
4203
  function readSkillBody(skillPath, maxChars = 12e3) {
4169
- const raw = readFileSync7(skillPath, "utf8");
4204
+ const raw = readFileSync8(skillPath, "utf8");
4170
4205
  if (raw.startsWith("---")) {
4171
4206
  const end = raw.indexOf("\n---", 3);
4172
4207
  if (end >= 0) {
@@ -4259,8 +4294,8 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
4259
4294
  }
4260
4295
 
4261
4296
  // src/composer/stage-files.ts
4262
- import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
4263
- 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";
4264
4299
  import { randomUUID as randomUUID4 } from "crypto";
4265
4300
  var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
4266
4301
  "png",
@@ -4295,22 +4330,22 @@ function imageMimeType(filePath) {
4295
4330
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
4296
4331
  }
4297
4332
  function ensureAttachmentsDir(worktreePath) {
4298
- const dir = join11(worktreePath, ATTACHMENTS_DIR);
4333
+ const dir = join12(worktreePath, ATTACHMENTS_DIR);
4299
4334
  mkdirSync4(dir, { recursive: true });
4300
- const gi = join11(dir, ".gitignore");
4335
+ const gi = join12(dir, ".gitignore");
4301
4336
  if (!existsSync12(gi)) {
4302
- writeFileSync3(gi, attachmentsGitignoreBody(), "utf8");
4337
+ writeFileSync4(gi, attachmentsGitignoreBody(), "utf8");
4303
4338
  }
4304
4339
  return dir;
4305
4340
  }
4306
4341
  function uniqueAttachmentName(dir, originalName) {
4307
4342
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
4308
- if (!existsSync12(join11(dir, safe))) return safe;
4343
+ if (!existsSync12(join12(dir, safe))) return safe;
4309
4344
  const ext = extname(safe);
4310
4345
  const stem = ext ? safe.slice(0, -ext.length) : safe;
4311
4346
  for (let i = 1; i < 1e4; i++) {
4312
4347
  const candidate = `${stem}-${i}${ext}`;
4313
- if (!existsSync12(join11(dir, candidate))) return candidate;
4348
+ if (!existsSync12(join12(dir, candidate))) return candidate;
4314
4349
  }
4315
4350
  return `${stem}-${randomUUID4()}${ext}`;
4316
4351
  }
@@ -4371,10 +4406,10 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
4371
4406
  const st = statSync4(abs);
4372
4407
  if (!st.isFile()) continue;
4373
4408
  const name = uniqueAttachmentName(dir, originalName);
4374
- const destAbs = join11(dir, name);
4409
+ const destAbs = join12(dir, name);
4375
4410
  copyFileSync3(abs, destAbs);
4376
4411
  const rel = `${ATTACHMENTS_DIR}/${name}`;
4377
- const buf = readFileSync8(destAbs);
4412
+ const buf = readFileSync9(destAbs);
4378
4413
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
4379
4414
  } catch (err) {
4380
4415
  out.push({
@@ -4396,8 +4431,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
4396
4431
  try {
4397
4432
  const buf = Buffer.from(item.dataBase64, "base64");
4398
4433
  const name = uniqueAttachmentName(dir, originalName);
4399
- const destAbs = join11(dir, name);
4400
- writeFileSync3(destAbs, buf);
4434
+ const destAbs = join12(dir, name);
4435
+ writeFileSync4(destAbs, buf);
4401
4436
  const rel = `${ATTACHMENTS_DIR}/${name}`;
4402
4437
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
4403
4438
  } catch (err) {
@@ -4425,10 +4460,10 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
4425
4460
  }
4426
4461
  const name = basename3(rel);
4427
4462
  try {
4428
- const abs = join11(worktreePath, rel);
4463
+ const abs = join12(worktreePath, rel);
4429
4464
  const st = statSync4(abs);
4430
4465
  if (!st.isFile()) continue;
4431
- const buf = readFileSync8(abs);
4466
+ const buf = readFileSync9(abs);
4432
4467
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
4433
4468
  } catch (err) {
4434
4469
  out.push({
@@ -4443,8 +4478,8 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
4443
4478
  }
4444
4479
 
4445
4480
  // src/agents/instructions.ts
4446
- import { existsSync as existsSync13, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
4447
- 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";
4448
4483
  function normPath(p) {
4449
4484
  return p.replace(/\/+$/, "");
4450
4485
  }
@@ -4961,7 +4996,9 @@ var Orchestrator = class {
4961
4996
  updateThread(thread.id, patch);
4962
4997
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
4963
4998
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
4964
- void this.drainQueue(thread.id);
4999
+ if (thisProcessShouldDrainAgentQueues()) {
5000
+ void this.drainQueue(thread.id);
5001
+ }
4965
5002
  return this.requireThread(thread.id);
4966
5003
  });
4967
5004
  }
@@ -5014,10 +5051,10 @@ var Orchestrator = class {
5014
5051
  async sendQueuedMessageNow(threadRef, index) {
5015
5052
  const thread = this.requireThread(threadRef);
5016
5053
  const promoted = await withThreadLock(thread.id, async () => {
5017
- const current = this.requireThread(thread.id);
5018
- if (index < 0 || index >= current.queue.length) return false;
5019
- const item = current.queue[index];
5020
- 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);
5021
5058
  const queue = [item, ...rest];
5022
5059
  this.haltDrain.delete(thread.id);
5023
5060
  updateThread(thread.id, { queue });
@@ -5025,10 +5062,16 @@ var Orchestrator = class {
5025
5062
  return true;
5026
5063
  });
5027
5064
  if (!promoted) return this.requireThread(thread.id);
5065
+ const current = this.requireThread(thread.id);
5028
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);
5029
5069
  if (inFlight) {
5030
5070
  this.stop(thread.id, { clearQueue: false, continueQueue: true });
5031
5071
  } else {
5072
+ if (foreignLive) {
5073
+ this.stop(thread.id, { clearQueue: false, continueQueue: true });
5074
+ }
5032
5075
  void this.drainQueue(thread.id);
5033
5076
  }
5034
5077
  return this.requireThread(thread.id);
@@ -5463,6 +5506,13 @@ var Orchestrator = class {
5463
5506
  if (handle) handle.kill();
5464
5507
  const proc = this.processes.get(`${thread.id}:agent`);
5465
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
+ }
5466
5516
  const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
5467
5517
  if (stopped.status === "stopped") {
5468
5518
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.98",
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",