@sideboard-ai/core 0.1.56 → 0.1.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/{agents-2YYWW723.js → agents-B2723EOF.js} +4 -4
  2. package/dist/{agents-AQFEFKBL.js → agents-EADRAAPG.js} +4 -4
  3. package/dist/{app-settings-2LQNRTBE.js → app-settings-TBHTJWLQ.js} +1 -1
  4. package/dist/{app-settings-73DI4B6T.js → app-settings-YBNZ4XSR.js} +1 -1
  5. package/dist/{chunk-5DMYULLC.js → chunk-2P2UK7E7.js} +2 -2
  6. package/dist/{chunk-7LNGIP3X.js → chunk-73AT5K4A.js} +15 -3
  7. package/dist/{chunk-DIOF73S2.js → chunk-75QR74KM.js} +7 -2
  8. package/dist/{chunk-EBWEKG52.js → chunk-CHGOMFTA.js} +3 -3
  9. package/dist/{chunk-C4BCC5X5.js → chunk-FGNBTFRD.js} +7 -2
  10. package/dist/{chunk-H6GGDLYS.js → chunk-JB2H7E7V.js} +1 -1
  11. package/dist/{chunk-BXJ76RHF.js → chunk-MBAL5FE6.js} +4 -4
  12. package/dist/{chunk-XOU6HNQJ.js → chunk-MFGGIRUA.js} +39 -13
  13. package/dist/{chunk-S42XDFKF.js → chunk-MLAST5BB.js} +3 -3
  14. package/dist/{chunk-C3J4GDW4.js → chunk-MX7UH5AT.js} +76 -60
  15. package/dist/{chunk-DQJ5D42H.js → chunk-NTYRBRE7.js} +44 -13
  16. package/dist/{chunk-DZFH2KLT.js → chunk-P7EQUFB6.js} +39 -13
  17. package/dist/{chunk-V2DUTUNC.js → chunk-QJBC4BRK.js} +2 -2
  18. package/dist/{chunk-ZNM4MAN4.js → chunk-RIWUU7FO.js} +15 -3
  19. package/dist/{chunk-UA5NDAHL.js → chunk-XWPKLRB3.js} +44 -13
  20. package/dist/{chunk-7F454EE2.js → chunk-ZXDOI6IH.js} +76 -53
  21. package/dist/{connected-teams-ONC4666A.js → connected-teams-MKIYKOYA.js} +2 -2
  22. package/dist/{connected-teams-2ANNZCP5.js → connected-teams-RSL4VB3X.js} +2 -2
  23. package/dist/{coordinator-prompt-RBKUNRPR.js → coordinator-prompt-4P23VVFC.js} +4 -4
  24. package/dist/{coordinator-prompt-ICF36NUQ.js → coordinator-prompt-LNWHZ64D.js} +4 -4
  25. package/dist/{global-workspace-667XLBW6.js → global-workspace-BHT4XUAA.js} +5 -5
  26. package/dist/{global-workspace-S7VLWPWG.js → global-workspace-TMMAVRVW.js} +5 -5
  27. package/dist/index.cjs +342 -119
  28. package/dist/index.d.cts +27 -6
  29. package/dist/index.d.ts +27 -6
  30. package/dist/index.js +195 -67
  31. package/dist/mcp/run-stdio.cjs +342 -130
  32. package/dist/mcp/run-stdio.js +194 -71
  33. package/dist/{run-ZJKYHXGN.js → run-GPSVV5OH.js} +1 -1
  34. package/dist/{run-H3IMNOYN.js → run-HZTBWGQR.js} +1 -1
  35. package/dist/{workspaces-FPJJXXAE.js → workspaces-3GO3EDQG.js} +6 -6
  36. package/dist/{workspaces-LYIDE2VR.js → workspaces-GFPGDGCS.js} +6 -6
  37. package/dist/{worktree-5KEQWSAF.js → worktree-3L2G6PCM.js} +2 -2
  38. package/dist/{worktree-RWGL7FUV.js → worktree-GBLHEFHR.js} +2 -2
  39. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-5DMYULLC.js";
7
+ } from "./chunk-2P2UK7E7.js";
8
8
  import {
9
9
  CLOUD_COORDINATOR_BUSY_REPLY,
10
10
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -24,7 +24,7 @@ import {
24
24
  orchestratorSessionPoisonedByBuiltins,
25
25
  parseForceStopMessage,
26
26
  takenTeamSlugsForOrchestration
27
- } from "./chunk-S42XDFKF.js";
27
+ } from "./chunk-MLAST5BB.js";
28
28
  import {
29
29
  BRIGHTSY_MCP_ALLOWED_TOOLS,
30
30
  CLAUDE_MODEL_CATALOG,
@@ -70,7 +70,7 @@ import {
70
70
  resolveQuotaFallbackAgent,
71
71
  sanitizeMcpServerName,
72
72
  writeInjectedMcpConfig
73
- } from "./chunk-7F454EE2.js";
73
+ } from "./chunk-ZXDOI6IH.js";
74
74
  import {
75
75
  ORCHESTRATOR_AGENT_KINDS,
76
76
  assertOrchestratorCapableAgent,
@@ -88,7 +88,7 @@ import {
88
88
  loadBrightsyConfig,
89
89
  saveBrightsyConfig,
90
90
  switchBrightsyAccount
91
- } from "./chunk-BXJ76RHF.js";
91
+ } from "./chunk-MBAL5FE6.js";
92
92
  import {
93
93
  LEGACY_PLAN_FILE_REL,
94
94
  PLAN_FILE_NAME,
@@ -118,7 +118,7 @@ import {
118
118
  enrichWorkspacesWithGithub,
119
119
  ensureGlobalCoordinatorCwd,
120
120
  formatWorkspaceInventory
121
- } from "./chunk-UA5NDAHL.js";
121
+ } from "./chunk-XWPKLRB3.js";
122
122
  import {
123
123
  HARNESS_ENV_KEYS,
124
124
  appSettingsPath,
@@ -157,7 +157,7 @@ import {
157
157
  updateClaudeSettings,
158
158
  updateDefaultsSettings,
159
159
  updateIntegrationsSettings
160
- } from "./chunk-DIOF73S2.js";
160
+ } from "./chunk-75QR74KM.js";
161
161
  import {
162
162
  FAMOUS_SOCCER_TEAMS,
163
163
  addPrStackLayer,
@@ -220,7 +220,7 @@ import {
220
220
  worktreeDisplayLabel,
221
221
  worktreeDisplayLabelForGroup,
222
222
  worktreeNameFromPath
223
- } from "./chunk-DZFH2KLT.js";
223
+ } from "./chunk-P7EQUFB6.js";
224
224
  import {
225
225
  ATTACHMENTS_DIR,
226
226
  LEGACY_ATTACHMENTS_DIR,
@@ -277,7 +277,7 @@ import {
277
277
  git,
278
278
  resolveGhAuthToken,
279
279
  run
280
- } from "./chunk-7LNGIP3X.js";
280
+ } from "./chunk-73AT5K4A.js";
281
281
 
282
282
  // src/integrations/github.ts
283
283
  async function getGitHubStatus() {
@@ -706,10 +706,12 @@ async function spawnAgentTurn(thread, input, onEvent) {
706
706
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
707
707
  );
708
708
  }
709
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-S7VLWPWG.js");
709
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-TMMAVRVW.js");
710
710
  if (isGlobalThread2(thread)) {
711
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-ICF36NUQ.js");
712
- ensureGlobalCoordinatorCwd2();
711
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LNWHZ64D.js");
712
+ ensureGlobalCoordinatorCwd2(
713
+ isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
714
+ );
713
715
  }
714
716
  if (isOrchestratorThread(thread)) {
715
717
  assertOrchestratorCapableAgent(thread.agent);
@@ -995,9 +997,21 @@ async function detectAgents() {
995
997
  ensureAgentPath();
996
998
  return Promise.all(allAdapters().map((a) => a.detect()));
997
999
  }
1000
+ var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
998
1001
  async function requireAgent(agent, opts) {
999
- const status = (await detectAgents()).find((s) => s.agent === agent);
1000
- if (!status) throw new Error(`Unknown agent: ${agent}`);
1002
+ ensureAgentPath();
1003
+ const status = await Promise.race([
1004
+ getAdapter(agent).detect(),
1005
+ new Promise((_, reject) => {
1006
+ setTimeout(() => {
1007
+ reject(
1008
+ new Error(
1009
+ `${agent} detect timed out after ${REQUIRE_AGENT_TIMEOUT_MS / 1e3}s`
1010
+ )
1011
+ );
1012
+ }, REQUIRE_AGENT_TIMEOUT_MS);
1013
+ })
1014
+ ]);
1001
1015
  if (!status.installed || !status.authenticated) {
1002
1016
  throw new Error(status.reason ?? `${agent} is not available`);
1003
1017
  }
@@ -2827,7 +2841,7 @@ async function previewLand(thread) {
2827
2841
  const target = await resolveDefaultBranch(thread.repoPath);
2828
2842
  if (thread.branchName === target || thread.branchName === "main" || thread.branchName === "master") {
2829
2843
  }
2830
- const { stdout: head } = await import("./run-ZJKYHXGN.js").then(
2844
+ const { stdout: head } = await import("./run-GPSVV5OH.js").then(
2831
2845
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], thread.worktreePath)
2832
2846
  );
2833
2847
  const current = head.trim();
@@ -2866,7 +2880,7 @@ async function confirmLand(thread, opts) {
2866
2880
  if (preview.dirty) {
2867
2881
  committed = await commitAll(thread.worktreePath, meta.commitMessage);
2868
2882
  }
2869
- const { git: git2 } = await import("./run-ZJKYHXGN.js");
2883
+ const { git: git2 } = await import("./run-GPSVV5OH.js");
2870
2884
  const { stdout: headOut } = await git2(
2871
2885
  ["rev-parse", "--abbrev-ref", "HEAD"],
2872
2886
  thread.worktreePath,
@@ -2964,7 +2978,7 @@ async function createThread(input, _onSetupLine) {
2964
2978
  return readThread(thread.id) ?? thread;
2965
2979
  }
2966
2980
  async function listLinearIssues(agent, repoPath) {
2967
- const { getAdapter: getAdapter2 } = await import("./agents-2YYWW723.js");
2981
+ const { getAdapter: getAdapter2 } = await import("./agents-B2723EOF.js");
2968
2982
  await requireAgent(agent, { requireLinear: true });
2969
2983
  const adapter = getAdapter2(agent);
2970
2984
  if (!adapter.listLinearIssues) {
@@ -3490,7 +3504,7 @@ async function adoptThread(input) {
3490
3504
  throw new Error(`Worktree not found: ${input.worktreePath}`);
3491
3505
  }
3492
3506
  const repoPath = await resolveRepoRoot(input.worktreePath);
3493
- const { stdout: branch } = await import("./run-ZJKYHXGN.js").then(
3507
+ const { stdout: branch } = await import("./run-GPSVV5OH.js").then(
3494
3508
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], input.worktreePath)
3495
3509
  );
3496
3510
  const thread = createEmptyThread({
@@ -3505,7 +3519,7 @@ async function adoptThread(input) {
3505
3519
  messages: input.messages ?? []
3506
3520
  });
3507
3521
  writeThread(thread);
3508
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FPJJXXAE.js");
3522
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-3GO3EDQG.js");
3509
3523
  await ensureWorkspace2(repoPath);
3510
3524
  return thread;
3511
3525
  }
@@ -4066,7 +4080,7 @@ var REPO_REVIEW_NAME = "review.md";
4066
4080
  var REVIEW_REQUEST_PATH = `${ATTACHMENTS_DIR}/Review request.md`;
4067
4081
  var LEGACY_REVIEW_REQUEST_PATH = `${LEGACY_ATTACHMENTS_DIR}/Review request.md`;
4068
4082
  var REVIEW_REQUEST_NAME = "Review request.md";
4069
- var REVIEW_REQUEST_PREFILL = "Review.";
4083
+ var REVIEW_REQUEST_PREFILL = "Review changes in this workspace.";
4070
4084
  var LEGACY_REVIEW_TEMPLATE_MARKERS = [
4071
4085
  "You are acting as a reviewer for a proposed code change made by another engineer.",
4072
4086
  "HOW MANY FINDINGS TO RETURN:"
@@ -4401,6 +4415,7 @@ var Orchestrator = class {
4401
4415
  }
4402
4416
  async reconcile(repoPath, opts) {
4403
4417
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
4418
+ const drainQueues = opts?.drainQueues !== false;
4404
4419
  healOrchestrationSoccerTitles();
4405
4420
  for (const thread of listThreads({ includeArchived: true })) {
4406
4421
  if (thread.status === "archived") continue;
@@ -4441,18 +4456,42 @@ var Orchestrator = class {
4441
4456
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
4442
4457
  });
4443
4458
  }
4444
- const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-2LQNRTBE.js");
4459
+ const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
4445
4460
  if (autoCleanupOrphansEnabled2() && shouldRunWorktreeCleanup() && orphans.length > 0) {
4446
4461
  await cleanupOrphanWorktrees({ repoPaths });
4447
4462
  }
4448
4463
  } catch {
4449
4464
  }
4465
+ if (drainQueues) {
4466
+ this.adoptPersistedQueues();
4467
+ }
4468
+ this.schedulePendingQuotaResumes();
4469
+ }
4470
+ /**
4471
+ * Adopt queues persisted by another process (MCP stdio / CLI) into this
4472
+ * orchestrator's drain loops. Desktop calls this on thread-store changes so
4473
+ * MCP-created review threads don't stay `queued` after the MCP child exits.
4474
+ */
4475
+ adoptPersistedQueues() {
4450
4476
  for (const thread of listThreads()) {
4451
- if (thread.queue.length > 0 && thread.status !== "stopped") {
4477
+ if (thread.status === "stopped" || thread.status === "archived") continue;
4478
+ const pid = thread.agentPid;
4479
+ const deadPid = typeof pid === "number" && pid > 0 && !isPidAlive(pid) ? true : false;
4480
+ if (deadPid) {
4481
+ updateThread(thread.id, { agentPid: null });
4482
+ }
4483
+ if (thread.status === "queued" && thread.queue.length === 0) {
4484
+ if (!this.activeTurns.has(thread.id) && !this.startingTurns.has(thread.id)) {
4485
+ setStatus(thread.id, "idle");
4486
+ this.emit({ type: "status_changed", threadId: thread.id, status: "idle" });
4487
+ }
4488
+ continue;
4489
+ }
4490
+ if (thread.queue.length > 0) {
4491
+ this.haltDrain.delete(thread.id);
4452
4492
  void this.drainQueue(thread.id);
4453
4493
  }
4454
4494
  }
4455
- this.schedulePendingQuotaResumes();
4456
4495
  }
4457
4496
  clearQuotaResumeTimer(threadId) {
4458
4497
  const timer = this.quotaResumeTimers.get(threadId);
@@ -4574,7 +4613,7 @@ var Orchestrator = class {
4574
4613
  }
4575
4614
  async finishCreateThread(threadId, prompt) {
4576
4615
  await this.runSetupAfterCreate(threadId);
4577
- const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-2LQNRTBE.js");
4616
+ const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
4578
4617
  if (autoRunAfterSetupEnabled2()) {
4579
4618
  try {
4580
4619
  await this.startDev(threadId);
@@ -4634,7 +4673,12 @@ var Orchestrator = class {
4634
4673
  const current = this.requireThread(thread.id);
4635
4674
  const queue = [...current.queue, prompt];
4636
4675
  this.haltDrain.delete(thread.id);
4637
- updateThread(thread.id, { queue, status: "queued" });
4676
+ const patch = { queue, status: "queued" };
4677
+ const pid = current.agentPid;
4678
+ if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
4679
+ patch.agentPid = null;
4680
+ }
4681
+ updateThread(thread.id, patch);
4638
4682
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
4639
4683
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
4640
4684
  void this.drainQueue(thread.id);
@@ -4840,7 +4884,7 @@ var Orchestrator = class {
4840
4884
  });
4841
4885
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
4842
4886
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
4843
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-2LQNRTBE.js");
4887
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
4844
4888
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
4845
4889
  customPrompt: settings?.prompts?.renameBranch
4846
4890
  }) : null;
@@ -4852,7 +4896,9 @@ var Orchestrator = class {
4852
4896
  }
4853
4897
  let coordinatorDirective = null;
4854
4898
  if (isOrchestration) {
4855
- if (isGlobalThread(fresh)) ensureGlobalCoordinatorCwd();
4899
+ if (isGlobalThread(fresh)) {
4900
+ ensureGlobalCoordinatorCwd({ orchestratorThreadId: fresh.id });
4901
+ }
4856
4902
  if (!fresh.sessionId) {
4857
4903
  const inventory = await enrichWorkspacesWithGithub(this.listWorkspaces());
4858
4904
  coordinatorDirective = coordinatorSystemPrompt({
@@ -5709,7 +5755,7 @@ var Orchestrator = class {
5709
5755
  const archived = setStatus(thread.id, "archived");
5710
5756
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
5711
5757
  try {
5712
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FPJJXXAE.js");
5758
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-3GO3EDQG.js");
5713
5759
  await ensureWorkspace2(thread.repoPath);
5714
5760
  } catch {
5715
5761
  }
@@ -5730,7 +5776,7 @@ var Orchestrator = class {
5730
5776
  await runArchiveScript(thread.repoPath, thread.worktreePath);
5731
5777
  } catch {
5732
5778
  }
5733
- const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-2LQNRTBE.js");
5779
+ const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-TBHTJWLQ.js");
5734
5780
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled2();
5735
5781
  await removeWorktree(thread.repoPath, thread.worktreePath, {
5736
5782
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -5749,7 +5795,7 @@ var Orchestrator = class {
5749
5795
  return setStatus(thread.id, "idle");
5750
5796
  }
5751
5797
  if (!existsSync13(thread.worktreePath)) {
5752
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-RWGL7FUV.js");
5798
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-GBLHEFHR.js");
5753
5799
  const { execa: execa6 } = await import("execa");
5754
5800
  const slug = thread.worktreePath.split("/").pop();
5755
5801
  const dest = thread.worktreePath;
@@ -5834,7 +5880,7 @@ async function startOrchestration(opts) {
5834
5880
  sourceRef: "default",
5835
5881
  ...createOpts
5836
5882
  }).catch(async () => {
5837
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-RWGL7FUV.js");
5883
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-GBLHEFHR.js");
5838
5884
  const repo = await resolveRepoRoot2(repoPath);
5839
5885
  const def = await resolveDefaultBranch2(repo);
5840
5886
  return createThread({
@@ -5969,9 +6015,39 @@ function mcpArchiveBlockedReason(thread) {
5969
6015
 
5970
6016
  // src/mcp/server.ts
5971
6017
  var MAX_ORCH_THREADS = 5;
6018
+ var CREATE_THREAD_TIMEOUT_MS = 9e4;
6019
+ function withTimeout(promise, ms, label) {
6020
+ return new Promise((resolve, reject) => {
6021
+ const timer = setTimeout(() => {
6022
+ reject(new Error(`${label} timed out after ${Math.round(ms / 1e3)}s`));
6023
+ }, ms);
6024
+ promise.then(
6025
+ (value) => {
6026
+ clearTimeout(timer);
6027
+ resolve(value);
6028
+ },
6029
+ (err) => {
6030
+ clearTimeout(timer);
6031
+ reject(err);
6032
+ }
6033
+ );
6034
+ });
6035
+ }
5972
6036
  async function startMcpServer() {
5973
6037
  const orch = getOrchestrator();
5974
- await orch.reconcile(void 0, { reclaimStaleTurns: false });
6038
+ try {
6039
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-TBHTJWLQ.js");
6040
+ orch.setMaxConcurrent(maxConcurrentAgents2());
6041
+ } catch {
6042
+ }
6043
+ try {
6044
+ await orch.reconcile(void 0, { reclaimStaleTurns: false, drainQueues: false });
6045
+ } catch (err) {
6046
+ console.error(
6047
+ "[sideboard-mcp] reconcile on boot failed (continuing):",
6048
+ err instanceof Error ? err.message : err
6049
+ );
6050
+ }
5975
6051
  const server = new McpServer({
5976
6052
  name: "sideboard",
5977
6053
  version: "0.1.0"
@@ -6171,12 +6247,12 @@ async function startMcpServer() {
6171
6247
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
6172
6248
  }
6173
6249
  );
6174
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-2LQNRTBE.js");
6250
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-TBHTJWLQ.js");
6175
6251
  const accountDefaults = resolveThreadDefaults2();
6176
6252
  const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
6177
6253
  server.tool(
6178
6254
  "create_thread",
6179
- `Create a new worktree thread (chat) from branch, pr, or ticket. Pass repoPath from list_workspaces and parentThreadId when spawning from an orchestrator. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Then use send_to_thread to chat.`,
6255
+ `Create a new worktree thread (chat) from branch, pr, or ticket. Pass repoPath from list_workspaces. From an orchestration chat, omit parentThreadId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Prefer omitting agent/model so Sideboard applies ${accountDefaultsHint}. Then use send_to_thread to chat.`,
6180
6256
  {
6181
6257
  sourceType: z.enum(["branch", "pr", "ticket"]),
6182
6258
  sourceRef: z.string(),
@@ -6186,11 +6262,31 @@ async function startMcpServer() {
6186
6262
  ),
6187
6263
  repoPath: z.string(),
6188
6264
  title: z.string().optional(),
6189
- parentThreadId: z.string().optional()
6265
+ parentThreadId: z.string().optional().describe(
6266
+ "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
6267
+ )
6190
6268
  },
6191
6269
  async (args) => {
6192
- if (args.parentThreadId) {
6193
- const children = orch.getThreads(true).filter((t) => t.parentThreadId === args.parentThreadId);
6270
+ const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
6271
+ let parentId = args.parentThreadId?.trim() || "";
6272
+ let parent = parentId ? orch.getThread(parentId) : null;
6273
+ let parentCorrectedFrom;
6274
+ if (envParentId) {
6275
+ const envParent = orch.getThread(envParentId);
6276
+ if (envParent) {
6277
+ if (parentId && parentId !== envParentId) parentCorrectedFrom = parentId;
6278
+ else if (!parentId) parentCorrectedFrom = void 0;
6279
+ parentId = envParentId;
6280
+ parent = envParent;
6281
+ }
6282
+ }
6283
+ if (parentId && !parent) {
6284
+ parentCorrectedFrom = parentId;
6285
+ parentId = "";
6286
+ parent = null;
6287
+ }
6288
+ if (parentId) {
6289
+ const children = orch.getThreads(true).filter((t) => t.parentThreadId === parentId);
6194
6290
  if (children.length >= MAX_ORCH_THREADS) {
6195
6291
  return {
6196
6292
  content: [
@@ -6203,38 +6299,70 @@ async function startMcpServer() {
6203
6299
  };
6204
6300
  }
6205
6301
  }
6302
+ let agentArg = args.agent;
6303
+ let agentCoercedFrom;
6304
+ const resolvedProbe = resolveNewThreadOptions2({ agent: agentArg }).agent;
6305
+ if (resolvedProbe === "codex") {
6306
+ agentCoercedFrom = agentArg ?? "codex";
6307
+ const accountAgent = resolveNewThreadOptions2({}).agent;
6308
+ agentArg = accountAgent !== "codex" ? accountAgent : "cursor";
6309
+ }
6206
6310
  const opts = resolveNewThreadOptions2({
6207
- agent: args.agent,
6311
+ agent: agentArg,
6208
6312
  model: args.model
6209
6313
  });
6210
- const thread = await orch.createThread({
6211
- sourceType: args.sourceType,
6212
- sourceRef: args.sourceRef,
6213
- agent: opts.agent,
6214
- model: opts.model,
6215
- effort: opts.effort,
6216
- fast: opts.fast,
6217
- repoPath: args.repoPath,
6218
- title: args.title,
6219
- parentThreadId: args.parentThreadId ?? null
6220
- });
6221
- return {
6222
- content: [
6223
- {
6224
- type: "text",
6225
- text: JSON.stringify({
6226
- id: thread.id,
6227
- title: thread.title,
6228
- branchName: thread.branchName,
6229
- worktreePath: thread.worktreePath,
6230
- agent: thread.agent,
6231
- model: thread.model,
6232
- status: thread.status,
6233
- link: `sideboard://thread/${thread.id}`
6234
- })
6235
- }
6236
- ]
6237
- };
6314
+ try {
6315
+ const thread = await withTimeout(
6316
+ orch.createThread({
6317
+ sourceType: args.sourceType,
6318
+ sourceRef: args.sourceRef,
6319
+ agent: opts.agent,
6320
+ model: opts.model,
6321
+ effort: opts.effort,
6322
+ fast: opts.fast,
6323
+ repoPath: args.repoPath,
6324
+ title: args.title,
6325
+ parentThreadId: parentId || null
6326
+ }),
6327
+ CREATE_THREAD_TIMEOUT_MS,
6328
+ "create_thread"
6329
+ );
6330
+ const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
6331
+ return {
6332
+ content: [
6333
+ {
6334
+ type: "text",
6335
+ text: JSON.stringify({
6336
+ id: thread.id,
6337
+ title: thread.title,
6338
+ branchName: thread.branchName,
6339
+ worktreePath: thread.worktreePath,
6340
+ agent: thread.agent,
6341
+ model: thread.model,
6342
+ status: thread.status,
6343
+ link: `sideboard://thread/${thread.id}`,
6344
+ parentThreadId: thread.parentThreadId,
6345
+ ...agentCoercedFrom ? {
6346
+ agentCoercedFrom,
6347
+ note: `Avoid nested Codex under a Codex orchestrator \u2014 used Account default agent=${thread.agent}`
6348
+ } : {},
6349
+ ...parentCorrectedFrom ? { parentCorrectedFrom, parentNote } : {}
6350
+ })
6351
+ }
6352
+ ]
6353
+ };
6354
+ } catch (err) {
6355
+ const message = err instanceof Error ? err.message : String(err);
6356
+ return {
6357
+ content: [
6358
+ {
6359
+ type: "text",
6360
+ text: `create_thread failed: ${message}`
6361
+ }
6362
+ ],
6363
+ isError: true
6364
+ };
6365
+ }
6238
6366
  }
6239
6367
  );
6240
6368
  server.tool(
@@ -6415,7 +6543,7 @@ async function startMcpServer() {
6415
6543
  );
6416
6544
  server.tool(
6417
6545
  "request_review",
6418
- 'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.',
6546
+ 'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.',
6419
6547
  { ref: z.string().describe("Worktree thread id/ref to review") },
6420
6548
  async ({ ref }) => {
6421
6549
  try {
@@ -6473,7 +6601,7 @@ async function startMcpServer() {
6473
6601
  async ({ ref, through_index, agent, model, title }) => {
6474
6602
  try {
6475
6603
  const source = orch.getThread(ref);
6476
- if (source) await orch.reconcile(source.repoPath);
6604
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
6477
6605
  const thread = await orch.forkThreadWorktree({
6478
6606
  threadId: ref,
6479
6607
  throughIndex: through_index,