@sideboard-ai/core 0.1.56 → 0.1.57

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.cjs CHANGED
@@ -11640,6 +11640,7 @@ var Orchestrator = class {
11640
11640
  }
11641
11641
  async reconcile(repoPath, opts) {
11642
11642
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
11643
+ const drainQueues = opts?.drainQueues !== false;
11643
11644
  healOrchestrationSoccerTitles();
11644
11645
  for (const thread of listThreads({ includeArchived: true })) {
11645
11646
  if (thread.status === "archived") continue;
@@ -11686,12 +11687,36 @@ var Orchestrator = class {
11686
11687
  }
11687
11688
  } catch {
11688
11689
  }
11690
+ if (drainQueues) {
11691
+ this.adoptPersistedQueues();
11692
+ }
11693
+ this.schedulePendingQuotaResumes();
11694
+ }
11695
+ /**
11696
+ * Adopt queues persisted by another process (MCP stdio / CLI) into this
11697
+ * orchestrator's drain loops. Desktop calls this on thread-store changes so
11698
+ * MCP-created review threads don't stay `queued` after the MCP child exits.
11699
+ */
11700
+ adoptPersistedQueues() {
11689
11701
  for (const thread of listThreads()) {
11690
- if (thread.queue.length > 0 && thread.status !== "stopped") {
11702
+ if (thread.status === "stopped" || thread.status === "archived") continue;
11703
+ const pid = thread.agentPid;
11704
+ const deadPid = typeof pid === "number" && pid > 0 && !isPidAlive(pid) ? true : false;
11705
+ if (deadPid) {
11706
+ updateThread(thread.id, { agentPid: null });
11707
+ }
11708
+ if (thread.status === "queued" && thread.queue.length === 0) {
11709
+ if (!this.activeTurns.has(thread.id) && !this.startingTurns.has(thread.id)) {
11710
+ setStatus(thread.id, "idle");
11711
+ this.emit({ type: "status_changed", threadId: thread.id, status: "idle" });
11712
+ }
11713
+ continue;
11714
+ }
11715
+ if (thread.queue.length > 0) {
11716
+ this.haltDrain.delete(thread.id);
11691
11717
  void this.drainQueue(thread.id);
11692
11718
  }
11693
11719
  }
11694
- this.schedulePendingQuotaResumes();
11695
11720
  }
11696
11721
  clearQuotaResumeTimer(threadId) {
11697
11722
  const timer = this.quotaResumeTimers.get(threadId);
@@ -11873,7 +11898,12 @@ var Orchestrator = class {
11873
11898
  const current = this.requireThread(thread.id);
11874
11899
  const queue = [...current.queue, prompt];
11875
11900
  this.haltDrain.delete(thread.id);
11876
- updateThread(thread.id, { queue, status: "queued" });
11901
+ const patch = { queue, status: "queued" };
11902
+ const pid = current.agentPid;
11903
+ if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
11904
+ patch.agentPid = null;
11905
+ }
11906
+ updateThread(thread.id, patch);
11877
11907
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
11878
11908
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
11879
11909
  void this.drainQueue(thread.id);
@@ -13222,7 +13252,12 @@ function mcpArchiveBlockedReason(thread) {
13222
13252
  var MAX_ORCH_THREADS = 5;
13223
13253
  async function startMcpServer() {
13224
13254
  const orch = getOrchestrator();
13225
- await orch.reconcile(void 0, { reclaimStaleTurns: false });
13255
+ try {
13256
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
13257
+ orch.setMaxConcurrent(maxConcurrentAgents2());
13258
+ } catch {
13259
+ }
13260
+ await orch.reconcile(void 0, { reclaimStaleTurns: false, drainQueues: false });
13226
13261
  const server = new import_mcp.McpServer({
13227
13262
  name: "sideboard",
13228
13263
  version: "0.1.0"
@@ -13724,7 +13759,7 @@ async function startMcpServer() {
13724
13759
  async ({ ref, through_index, agent, model, title }) => {
13725
13760
  try {
13726
13761
  const source = orch.getThread(ref);
13727
- if (source) await orch.reconcile(source.repoPath);
13762
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
13728
13763
  const thread = await orch.forkThreadWorktree({
13729
13764
  threadId: ref,
13730
13765
  throughIndex: through_index,
package/dist/index.d.cts CHANGED
@@ -2231,7 +2231,19 @@ declare class Orchestrator {
2231
2231
  * real app/CLI startup recovery.
2232
2232
  */
2233
2233
  reclaimStaleTurns?: boolean;
2234
+ /**
2235
+ * When false, skip draining persisted queues. MCP stdio boots must not
2236
+ * steal the whole fleet into a short-lived process (desktop adopts instead).
2237
+ * Default true for desktop/CLI.
2238
+ */
2239
+ drainQueues?: boolean;
2234
2240
  }): Promise<void>;
2241
+ /**
2242
+ * Adopt queues persisted by another process (MCP stdio / CLI) into this
2243
+ * orchestrator's drain loops. Desktop calls this on thread-store changes so
2244
+ * MCP-created review threads don't stay `queued` after the MCP child exits.
2245
+ */
2246
+ adoptPersistedQueues(): void;
2235
2247
  private clearQuotaResumeTimer;
2236
2248
  /** Schedule (or fire) auto-retry after a provider session/usage limit reset. */
2237
2249
  private scheduleQuotaResume;
package/dist/index.d.ts CHANGED
@@ -2231,7 +2231,19 @@ declare class Orchestrator {
2231
2231
  * real app/CLI startup recovery.
2232
2232
  */
2233
2233
  reclaimStaleTurns?: boolean;
2234
+ /**
2235
+ * When false, skip draining persisted queues. MCP stdio boots must not
2236
+ * steal the whole fleet into a short-lived process (desktop adopts instead).
2237
+ * Default true for desktop/CLI.
2238
+ */
2239
+ drainQueues?: boolean;
2234
2240
  }): Promise<void>;
2241
+ /**
2242
+ * Adopt queues persisted by another process (MCP stdio / CLI) into this
2243
+ * orchestrator's drain loops. Desktop calls this on thread-store changes so
2244
+ * MCP-created review threads don't stay `queued` after the MCP child exits.
2245
+ */
2246
+ adoptPersistedQueues(): void;
2235
2247
  private clearQuotaResumeTimer;
2236
2248
  /** Schedule (or fire) auto-retry after a provider session/usage limit reset. */
2237
2249
  private scheduleQuotaResume;
package/dist/index.js CHANGED
@@ -4401,6 +4401,7 @@ var Orchestrator = class {
4401
4401
  }
4402
4402
  async reconcile(repoPath, opts) {
4403
4403
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
4404
+ const drainQueues = opts?.drainQueues !== false;
4404
4405
  healOrchestrationSoccerTitles();
4405
4406
  for (const thread of listThreads({ includeArchived: true })) {
4406
4407
  if (thread.status === "archived") continue;
@@ -4447,12 +4448,36 @@ var Orchestrator = class {
4447
4448
  }
4448
4449
  } catch {
4449
4450
  }
4451
+ if (drainQueues) {
4452
+ this.adoptPersistedQueues();
4453
+ }
4454
+ this.schedulePendingQuotaResumes();
4455
+ }
4456
+ /**
4457
+ * Adopt queues persisted by another process (MCP stdio / CLI) into this
4458
+ * orchestrator's drain loops. Desktop calls this on thread-store changes so
4459
+ * MCP-created review threads don't stay `queued` after the MCP child exits.
4460
+ */
4461
+ adoptPersistedQueues() {
4450
4462
  for (const thread of listThreads()) {
4451
- if (thread.queue.length > 0 && thread.status !== "stopped") {
4463
+ if (thread.status === "stopped" || thread.status === "archived") continue;
4464
+ const pid = thread.agentPid;
4465
+ const deadPid = typeof pid === "number" && pid > 0 && !isPidAlive(pid) ? true : false;
4466
+ if (deadPid) {
4467
+ updateThread(thread.id, { agentPid: null });
4468
+ }
4469
+ if (thread.status === "queued" && thread.queue.length === 0) {
4470
+ if (!this.activeTurns.has(thread.id) && !this.startingTurns.has(thread.id)) {
4471
+ setStatus(thread.id, "idle");
4472
+ this.emit({ type: "status_changed", threadId: thread.id, status: "idle" });
4473
+ }
4474
+ continue;
4475
+ }
4476
+ if (thread.queue.length > 0) {
4477
+ this.haltDrain.delete(thread.id);
4452
4478
  void this.drainQueue(thread.id);
4453
4479
  }
4454
4480
  }
4455
- this.schedulePendingQuotaResumes();
4456
4481
  }
4457
4482
  clearQuotaResumeTimer(threadId) {
4458
4483
  const timer = this.quotaResumeTimers.get(threadId);
@@ -4634,7 +4659,12 @@ var Orchestrator = class {
4634
4659
  const current = this.requireThread(thread.id);
4635
4660
  const queue = [...current.queue, prompt];
4636
4661
  this.haltDrain.delete(thread.id);
4637
- updateThread(thread.id, { queue, status: "queued" });
4662
+ const patch = { queue, status: "queued" };
4663
+ const pid = current.agentPid;
4664
+ if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
4665
+ patch.agentPid = null;
4666
+ }
4667
+ updateThread(thread.id, patch);
4638
4668
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
4639
4669
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
4640
4670
  void this.drainQueue(thread.id);
@@ -5971,7 +6001,12 @@ function mcpArchiveBlockedReason(thread) {
5971
6001
  var MAX_ORCH_THREADS = 5;
5972
6002
  async function startMcpServer() {
5973
6003
  const orch = getOrchestrator();
5974
- await orch.reconcile(void 0, { reclaimStaleTurns: false });
6004
+ try {
6005
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-2LQNRTBE.js");
6006
+ orch.setMaxConcurrent(maxConcurrentAgents2());
6007
+ } catch {
6008
+ }
6009
+ await orch.reconcile(void 0, { reclaimStaleTurns: false, drainQueues: false });
5975
6010
  const server = new McpServer({
5976
6011
  name: "sideboard",
5977
6012
  version: "0.1.0"
@@ -6473,7 +6508,7 @@ async function startMcpServer() {
6473
6508
  async ({ ref, through_index, agent, model, title }) => {
6474
6509
  try {
6475
6510
  const source = orch.getThread(ref);
6476
- if (source) await orch.reconcile(source.repoPath);
6511
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
6477
6512
  const thread = await orch.forkThreadWorktree({
6478
6513
  threadId: ref,
6479
6514
  throughIndex: through_index,
@@ -10548,6 +10548,7 @@ var Orchestrator = class {
10548
10548
  }
10549
10549
  async reconcile(repoPath, opts) {
10550
10550
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
10551
+ const drainQueues = opts?.drainQueues !== false;
10551
10552
  healOrchestrationSoccerTitles();
10552
10553
  for (const thread of listThreads({ includeArchived: true })) {
10553
10554
  if (thread.status === "archived") continue;
@@ -10594,12 +10595,36 @@ var Orchestrator = class {
10594
10595
  }
10595
10596
  } catch {
10596
10597
  }
10598
+ if (drainQueues) {
10599
+ this.adoptPersistedQueues();
10600
+ }
10601
+ this.schedulePendingQuotaResumes();
10602
+ }
10603
+ /**
10604
+ * Adopt queues persisted by another process (MCP stdio / CLI) into this
10605
+ * orchestrator's drain loops. Desktop calls this on thread-store changes so
10606
+ * MCP-created review threads don't stay `queued` after the MCP child exits.
10607
+ */
10608
+ adoptPersistedQueues() {
10597
10609
  for (const thread of listThreads()) {
10598
- if (thread.queue.length > 0 && thread.status !== "stopped") {
10610
+ if (thread.status === "stopped" || thread.status === "archived") continue;
10611
+ const pid = thread.agentPid;
10612
+ const deadPid = typeof pid === "number" && pid > 0 && !isPidAlive(pid) ? true : false;
10613
+ if (deadPid) {
10614
+ updateThread(thread.id, { agentPid: null });
10615
+ }
10616
+ if (thread.status === "queued" && thread.queue.length === 0) {
10617
+ if (!this.activeTurns.has(thread.id) && !this.startingTurns.has(thread.id)) {
10618
+ setStatus(thread.id, "idle");
10619
+ this.emit({ type: "status_changed", threadId: thread.id, status: "idle" });
10620
+ }
10621
+ continue;
10622
+ }
10623
+ if (thread.queue.length > 0) {
10624
+ this.haltDrain.delete(thread.id);
10599
10625
  void this.drainQueue(thread.id);
10600
10626
  }
10601
10627
  }
10602
- this.schedulePendingQuotaResumes();
10603
10628
  }
10604
10629
  clearQuotaResumeTimer(threadId) {
10605
10630
  const timer = this.quotaResumeTimers.get(threadId);
@@ -10781,7 +10806,12 @@ var Orchestrator = class {
10781
10806
  const current = this.requireThread(thread.id);
10782
10807
  const queue = [...current.queue, prompt];
10783
10808
  this.haltDrain.delete(thread.id);
10784
- updateThread(thread.id, { queue, status: "queued" });
10809
+ const patch = { queue, status: "queued" };
10810
+ const pid = current.agentPid;
10811
+ if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
10812
+ patch.agentPid = null;
10813
+ }
10814
+ updateThread(thread.id, patch);
10785
10815
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
10786
10816
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
10787
10817
  void this.drainQueue(thread.id);
@@ -12083,7 +12113,12 @@ function mcpArchiveBlockedReason(thread) {
12083
12113
  var MAX_ORCH_THREADS = 5;
12084
12114
  async function startMcpServer() {
12085
12115
  const orch = getOrchestrator();
12086
- await orch.reconcile(void 0, { reclaimStaleTurns: false });
12116
+ try {
12117
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
12118
+ orch.setMaxConcurrent(maxConcurrentAgents2());
12119
+ } catch {
12120
+ }
12121
+ await orch.reconcile(void 0, { reclaimStaleTurns: false, drainQueues: false });
12087
12122
  const server = new import_mcp.McpServer({
12088
12123
  name: "sideboard",
12089
12124
  version: "0.1.0"
@@ -12585,7 +12620,7 @@ async function startMcpServer() {
12585
12620
  async ({ ref, through_index, agent, model, title }) => {
12586
12621
  try {
12587
12622
  const source = orch.getThread(ref);
12588
- if (source) await orch.reconcile(source.repoPath);
12623
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
12589
12624
  const thread = await orch.forkThreadWorktree({
12590
12625
  threadId: ref,
12591
12626
  throughIndex: through_index,
@@ -3894,6 +3894,7 @@ var Orchestrator = class {
3894
3894
  }
3895
3895
  async reconcile(repoPath, opts) {
3896
3896
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
3897
+ const drainQueues = opts?.drainQueues !== false;
3897
3898
  healOrchestrationSoccerTitles();
3898
3899
  for (const thread of listThreads({ includeArchived: true })) {
3899
3900
  if (thread.status === "archived") continue;
@@ -3940,12 +3941,36 @@ var Orchestrator = class {
3940
3941
  }
3941
3942
  } catch {
3942
3943
  }
3944
+ if (drainQueues) {
3945
+ this.adoptPersistedQueues();
3946
+ }
3947
+ this.schedulePendingQuotaResumes();
3948
+ }
3949
+ /**
3950
+ * Adopt queues persisted by another process (MCP stdio / CLI) into this
3951
+ * orchestrator's drain loops. Desktop calls this on thread-store changes so
3952
+ * MCP-created review threads don't stay `queued` after the MCP child exits.
3953
+ */
3954
+ adoptPersistedQueues() {
3943
3955
  for (const thread of listThreads()) {
3944
- if (thread.queue.length > 0 && thread.status !== "stopped") {
3956
+ if (thread.status === "stopped" || thread.status === "archived") continue;
3957
+ const pid = thread.agentPid;
3958
+ const deadPid = typeof pid === "number" && pid > 0 && !isPidAlive(pid) ? true : false;
3959
+ if (deadPid) {
3960
+ updateThread(thread.id, { agentPid: null });
3961
+ }
3962
+ if (thread.status === "queued" && thread.queue.length === 0) {
3963
+ if (!this.activeTurns.has(thread.id) && !this.startingTurns.has(thread.id)) {
3964
+ setStatus(thread.id, "idle");
3965
+ this.emit({ type: "status_changed", threadId: thread.id, status: "idle" });
3966
+ }
3967
+ continue;
3968
+ }
3969
+ if (thread.queue.length > 0) {
3970
+ this.haltDrain.delete(thread.id);
3945
3971
  void this.drainQueue(thread.id);
3946
3972
  }
3947
3973
  }
3948
- this.schedulePendingQuotaResumes();
3949
3974
  }
3950
3975
  clearQuotaResumeTimer(threadId) {
3951
3976
  const timer = this.quotaResumeTimers.get(threadId);
@@ -4127,7 +4152,12 @@ var Orchestrator = class {
4127
4152
  const current = this.requireThread(thread.id);
4128
4153
  const queue = [...current.queue, prompt];
4129
4154
  this.haltDrain.delete(thread.id);
4130
- updateThread(thread.id, { queue, status: "queued" });
4155
+ const patch = { queue, status: "queued" };
4156
+ const pid = current.agentPid;
4157
+ if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
4158
+ patch.agentPid = null;
4159
+ }
4160
+ updateThread(thread.id, patch);
4131
4161
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
4132
4162
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
4133
4163
  void this.drainQueue(thread.id);
@@ -5415,7 +5445,12 @@ function mcpArchiveBlockedReason(thread) {
5415
5445
  var MAX_ORCH_THREADS = 5;
5416
5446
  async function startMcpServer() {
5417
5447
  const orch = getOrchestrator();
5418
- await orch.reconcile(void 0, { reclaimStaleTurns: false });
5448
+ try {
5449
+ const { maxConcurrentAgents } = await import("../app-settings-73DI4B6T.js");
5450
+ orch.setMaxConcurrent(maxConcurrentAgents());
5451
+ } catch {
5452
+ }
5453
+ await orch.reconcile(void 0, { reclaimStaleTurns: false, drainQueues: false });
5419
5454
  const server = new McpServer({
5420
5455
  name: "sideboard",
5421
5456
  version: "0.1.0"
@@ -5917,7 +5952,7 @@ async function startMcpServer() {
5917
5952
  async ({ ref, through_index, agent, model, title }) => {
5918
5953
  try {
5919
5954
  const source = orch.getThread(ref);
5920
- if (source) await orch.reconcile(source.repoPath);
5955
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
5921
5956
  const thread = await orch.forkThreadWorktree({
5922
5957
  threadId: ref,
5923
5958
  throughIndex: through_index,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",