@sideboard-ai/core 0.1.53 → 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.
Files changed (30) hide show
  1. package/dist/agents/cursor-runner.cjs +83 -7
  2. package/dist/agents/cursor-runner.js +1 -1
  3. package/dist/{agents-KYACODJ3.js → agents-2YYWW723.js} +3 -3
  4. package/dist/{agents-KP7UJEHJ.js → agents-AQFEFKBL.js} +2 -2
  5. package/dist/{app-settings-LZP632KI.js → app-settings-2LQNRTBE.js} +3 -1
  6. package/dist/{app-settings-7XVDQJ7F.js → app-settings-73DI4B6T.js} +3 -1
  7. package/dist/{chunk-VG22SETP.js → chunk-3WJAUKIL.js} +83 -7
  8. package/dist/{chunk-GNML24AW.js → chunk-5DMYULLC.js} +1 -1
  9. package/dist/{chunk-A6HVEMIB.js → chunk-7F454EE2.js} +86 -11
  10. package/dist/{chunk-BZST4HMJ.js → chunk-C3J4GDW4.js} +172 -21
  11. package/dist/{chunk-HBJSHRY2.js → chunk-C4BCC5X5.js} +17 -1
  12. package/dist/{chunk-T5QQVXK3.js → chunk-DIOF73S2.js} +17 -1
  13. package/dist/{chunk-UEAHMGHW.js → chunk-DQJ5D42H.js} +16 -2
  14. package/dist/{chunk-YOWIYAVA.js → chunk-EBWEKG52.js} +36 -11
  15. package/dist/{chunk-XX5BB7NV.js → chunk-S42XDFKF.js} +36 -11
  16. package/dist/{chunk-LRLKJM3O.js → chunk-UA5NDAHL.js} +16 -2
  17. package/dist/{chunk-YDXQ72MD.js → chunk-V2DUTUNC.js} +1 -1
  18. package/dist/{coordinator-prompt-S6JZD5EF.js → coordinator-prompt-ICF36NUQ.js} +2 -1
  19. package/dist/{coordinator-prompt-6FXVTSFN.js → coordinator-prompt-RBKUNRPR.js} +2 -1
  20. package/dist/{global-workspace-EV4G2WMQ.js → global-workspace-667XLBW6.js} +3 -2
  21. package/dist/{global-workspace-MSX2K27Y.js → global-workspace-S7VLWPWG.js} +3 -2
  22. package/dist/index.cjs +338 -55
  23. package/dist/index.d.cts +36 -2
  24. package/dist/index.d.ts +36 -2
  25. package/dist/index.js +121 -40
  26. package/dist/mcp/run-stdio.cjs +332 -51
  27. package/dist/mcp/run-stdio.js +121 -41
  28. package/dist/{workspaces-AYTBR6KQ.js → workspaces-FPJJXXAE.js} +4 -3
  29. package/dist/{workspaces-3RQQZQRO.js → workspaces-LYIDE2VR.js} +4 -3
  30. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -416,6 +416,7 @@ __export(app_settings_exports, {
416
416
  orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
417
417
  resolveClaudeExecutable: () => resolveClaudeExecutable,
418
418
  resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
419
+ resolveNewThreadOptions: () => resolveNewThreadOptions,
419
420
  resolveThreadDefaults: () => resolveThreadDefaults,
420
421
  saveAppSettings: () => saveAppSettings,
421
422
  updateAdvancedSettings: () => updateAdvancedSettings,
@@ -708,6 +709,16 @@ function resolveThreadDefaults(settings = loadAppSettings()) {
708
709
  fast: getDefaultFast(settings)
709
710
  };
710
711
  }
712
+ function resolveNewThreadOptions(overrides = {}, settings = loadAppSettings()) {
713
+ const defaults = resolveThreadDefaults(settings);
714
+ const effort = overrides.effort === void 0 || overrides.effort === null ? defaults.effort : normalizeThinkingEffort(overrides.effort) ?? defaults.effort;
715
+ return {
716
+ agent: overrides.agent ?? defaults.agent,
717
+ model: overrides.model === void 0 ? defaults.model : overrides.model?.trim() || null,
718
+ effort,
719
+ fast: overrides.fast === void 0 || overrides.fast === null ? defaults.fast : Boolean(overrides.fast)
720
+ };
721
+ }
711
722
  function isLinearConnected(settings = loadAppSettings()) {
712
723
  return Boolean(settings.integrations.linearApiKey?.trim());
713
724
  }
@@ -727,7 +738,12 @@ function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
727
738
  return Boolean(settings.brightsy.cloudConnectEnabled);
728
739
  }
729
740
  function brightsyCloudConnectAgent(settings = loadAppSettings()) {
730
- return settings.brightsy.cloudConnectAgent ?? "claude";
741
+ const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
742
+ const preferred = getDefaultAgent(settings);
743
+ if (CLOUD_CONNECT_AGENTS.has(preferred)) {
744
+ return preferred;
745
+ }
746
+ return fallback;
731
747
  }
732
748
  function updateAdvancedSettings(patch) {
733
749
  const current = loadAppSettings();
@@ -3512,6 +3528,11 @@ function coordinatorGreenfieldPlaybook(reposDir) {
3512
3528
  "- Do coding work in the child worktree thread, not by editing files in this home cwd."
3513
3529
  ].join("\n");
3514
3530
  }
3531
+ function accountDefaultsPlaybookLine() {
3532
+ const d = resolveThreadDefaults();
3533
+ const model = d.model?.trim() || "Auto";
3534
+ return `- Account defaults for create_thread (omit agent/model to use these): agent=${d.agent}, model=${model}, effort=${d.effort}`;
3535
+ }
3515
3536
  function coordinatorTurnReminder(opts) {
3516
3537
  const goal = opts.goal?.trim();
3517
3538
  return [
@@ -3521,6 +3542,7 @@ function coordinatorTurnReminder(opts) {
3521
3542
  "- Registered workspaces / child threads are the fleet you manage via Sideboard MCP.",
3522
3543
  `- Parent thread id (pass as parentThreadId when creating children): ${opts.parentId}`,
3523
3544
  goal ? `- Goal / title: ${goal}` : null,
3545
+ accountDefaultsPlaybookLine(),
3524
3546
  `- For "what's going on": call list_threads (and list_workspaces if needed). Summarize fleet status \u2014 do not ls/git-status this synthetic home.`,
3525
3547
  "- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn.",
3526
3548
  "- New repo: Bash (clone or gh repo create under ~/sideboard/repos/<name>) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR.",
@@ -3543,9 +3565,12 @@ function ensureGlobalCoordinatorCwd() {
3543
3565
  "",
3544
3566
  COORDINATOR_TOOL_PLAYBOOK,
3545
3567
  "",
3568
+ accountDefaultsPlaybookLine(),
3569
+ "",
3546
3570
  coordinatorGreenfieldPlaybook(reposDir),
3547
3571
  "",
3548
3572
  "When creating threads, pass `repoPath` from `list_workspaces` (or the path you just registered) and `parentThreadId` for children.",
3573
+ "Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
3549
3574
  "Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn.",
3550
3575
  "Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
3551
3576
  "Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
@@ -3571,8 +3596,10 @@ function coordinatorSystemPrompt(opts) {
3571
3596
  "You operate across ALL registered workspaces below.",
3572
3597
  "You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
3573
3598
  COORDINATOR_TOOL_PLAYBOOK,
3599
+ accountDefaultsPlaybookLine(),
3574
3600
  coordinatorGreenfieldPlaybook(reposDir),
3575
3601
  "When creating threads, pass the correct repoPath for the target workspace and parentThreadId for children.",
3602
+ "Omit agent/model on create_thread unless you need to override Account defaults.",
3576
3603
  "Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 send_to_thread (ask for `gh pr create --draft -R <origin-owner/name>` using the workspace github slug) \u2192 wait_for_turn. Never target upstream. Never open PRs from the orchestrator.",
3577
3604
  "Typical flow (new app): Bash under repos dir (clone or gh repo create) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
3578
3605
  `Goal: ${opts.goal}`,
@@ -3588,6 +3615,7 @@ var init_coordinator_prompt = __esm({
3588
3615
  import_node_fs6 = require("fs");
3589
3616
  import_node_path7 = require("path");
3590
3617
  init_worktree();
3618
+ init_app_settings();
3591
3619
  init_paths();
3592
3620
  COORDINATOR_TOOL_PLAYBOOK = [
3593
3621
  "Role: you oversee Sideboard worktree agents across registered repos. You do not live inside one of those worktrees.",
@@ -3596,12 +3624,12 @@ var init_coordinator_prompt = __esm({
3596
3624
  "- list_workspaces \u2014 registered repos (path + github slug when known)",
3597
3625
  "- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear API or GitHub Issues)",
3598
3626
  "- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
3599
- "- list_models \u2014 only when you need a specific model (rare); otherwise leave model unset = Auto",
3627
+ "- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
3600
3628
  "- list_threads / get_thread \u2014 fleet status (what is going on)",
3601
3629
  "Workspaces:",
3602
3630
  "- add_workspace / remove_workspace \u2014 register or unregister a git repo",
3603
3631
  "Worktree threads (chats):",
3604
- "- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId",
3632
+ "- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId; omit agent and model to use Sideboard Account defaults (Settings \u2192 Default agent, model & effort)",
3605
3633
  "- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
3606
3634
  "- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
3607
3635
  "- send_to_thread \u2014 queue a prompt (start/continue a chat turn); pass force_stop: true to interrupt mid-turn / clear stale queued prompts before replacing with a new request",
@@ -3701,7 +3729,13 @@ function createGlobalChat(opts) {
3701
3729
  const explicit = opts.title?.trim();
3702
3730
  const title = explicit && explicit !== CLOUD_ORCHESTRATOR_GOAL ? explicit : allocateTeamName(takenTeamSlugsForOrchestration()).name;
3703
3731
  const sourceRef = opts.sourceRef?.trim() || (isCloud ? CLOUD_ORCHESTRATOR_GOAL : title);
3704
- const agent = assertOrchestratorCapableAgent(opts.agent);
3732
+ const resolved = resolveNewThreadOptions({
3733
+ agent: opts.agent,
3734
+ model: opts.model,
3735
+ effort: opts.effort,
3736
+ fast: opts.fast
3737
+ });
3738
+ const agent = assertOrchestratorCapableAgent(resolved.agent);
3705
3739
  const thread = createEmptyThread({
3706
3740
  title,
3707
3741
  // Stick nicknames the same way chat tabs do (avoid later sync overwrites).
@@ -3713,9 +3747,9 @@ function createGlobalChat(opts) {
3713
3747
  repoPath: GLOBAL_WORKSPACE_ID,
3714
3748
  agent,
3715
3749
  autonomy: opts.autonomy ?? "default",
3716
- model: opts.model ?? null,
3717
- effort: opts.effort ?? "high",
3718
- fast: Boolean(opts.fast),
3750
+ model: resolved.model,
3751
+ effort: resolved.effort,
3752
+ fast: resolved.fast,
3719
3753
  planMode: Boolean(opts.planMode),
3720
3754
  attachments: opts.attachments ?? [],
3721
3755
  parentThreadId: opts.parentThreadId ?? null,
@@ -3748,20 +3782,35 @@ function findCloudCoordinator() {
3748
3782
  );
3749
3783
  }
3750
3784
  function ensureCloudCoordinator(agent) {
3785
+ const defaults = resolveThreadDefaults();
3786
+ const desired = assertOrchestratorCapableAgent(agent);
3751
3787
  const existing = findCloudCoordinator();
3752
3788
  if (existing) {
3789
+ const patch = {};
3753
3790
  if (existing.repoPath !== GLOBAL_WORKSPACE_ID) {
3754
- return updateThread(existing.id, {
3755
- repoPath: GLOBAL_WORKSPACE_ID,
3756
- worktreePath: globalAgentCwd(),
3757
- branchName: "global"
3758
- });
3791
+ patch.repoPath = GLOBAL_WORKSPACE_ID;
3792
+ patch.worktreePath = globalAgentCwd();
3793
+ patch.branchName = "global";
3794
+ }
3795
+ const hasAgentTurns = existing.messages.some((m) => m.role === "agent");
3796
+ const canRetarget = !existing.sessionId && !hasAgentTurns && existing.status !== "running" && existing.status !== "queued";
3797
+ if (canRetarget) {
3798
+ if (existing.agent !== desired) patch.agent = desired;
3799
+ if (existing.model !== defaults.model) patch.model = defaults.model;
3800
+ if (existing.effort !== defaults.effort) patch.effort = defaults.effort;
3801
+ if (existing.fast !== defaults.fast) patch.fast = defaults.fast;
3802
+ }
3803
+ if (Object.keys(patch).length > 0) {
3804
+ return updateThread(existing.id, patch);
3759
3805
  }
3760
3806
  return existing;
3761
3807
  }
3762
3808
  const created = createGlobalChat({
3763
3809
  sourceRef: CLOUD_ORCHESTRATOR_GOAL,
3764
- agent
3810
+ agent: desired,
3811
+ model: defaults.model,
3812
+ effort: defaults.effort,
3813
+ fast: defaults.fast
3765
3814
  });
3766
3815
  const all = listThreads({ includeArchived: true }).filter(
3767
3816
  (t) => t.status !== "archived" && isCloudCoordinatorThread(t)
@@ -3776,6 +3825,7 @@ var init_global_workspace = __esm({
3776
3825
  init_orchestrator_capable();
3777
3826
  init_teams();
3778
3827
  init_coordinator_prompt();
3828
+ init_app_settings();
3779
3829
  init_paths();
3780
3830
  init_thread_store();
3781
3831
  GLOBAL_WORKSPACE_ID = "__global__";
@@ -4958,6 +5008,7 @@ function toCodexMcpConfigArgs(servers) {
4958
5008
  args.push("-c", `${prefix}.env.${key}=${JSON.stringify(value)}`);
4959
5009
  }
4960
5010
  }
5011
+ args.push("-c", `${prefix}.default_tools_approval_mode=${JSON.stringify("approve")}`);
4961
5012
  }
4962
5013
  return args;
4963
5014
  }
@@ -5473,6 +5524,32 @@ function codexConfigHasNetworkAccess() {
5473
5524
  }
5474
5525
  return false;
5475
5526
  }
5527
+ function unwrapCodexMcpResult(result) {
5528
+ if (result == null) return void 0;
5529
+ if (typeof result === "string") return result;
5530
+ if (typeof result !== "object") return String(result);
5531
+ const rec = result;
5532
+ if (Array.isArray(rec.content)) {
5533
+ const texts = [];
5534
+ for (const item of rec.content) {
5535
+ if (!item || typeof item !== "object") continue;
5536
+ const text = item.text;
5537
+ if (typeof text === "string") texts.push(text);
5538
+ }
5539
+ if (texts.length) return texts.join("\n");
5540
+ }
5541
+ try {
5542
+ return JSON.stringify(result);
5543
+ } catch {
5544
+ return String(result);
5545
+ }
5546
+ }
5547
+ function asRecord(value) {
5548
+ if (value && typeof value === "object" && !Array.isArray(value)) {
5549
+ return value;
5550
+ }
5551
+ return void 0;
5552
+ }
5476
5553
  var import_node_fs12, import_node_os7, import_node_path12, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
5477
5554
  var init_codex = __esm({
5478
5555
  "src/agents/codex.ts"() {
@@ -5585,15 +5662,49 @@ var init_codex = __esm({
5585
5662
  }
5586
5663
  if (typeof obj.item === "object" && obj.item !== null) {
5587
5664
  const item = obj.item;
5588
- if (item.type === "error") {
5665
+ const itemType = item.type ?? item.item_type;
5666
+ if (itemType === "error") {
5589
5667
  const detail = item.message?.trim() || extractJsonErrorMessage(obj) || "Codex item error";
5590
5668
  return { type: "stderr", data: detail };
5591
5669
  }
5592
- if (item.type === "agent_message" && item.text) {
5670
+ if (itemType === "agent_message" && item.text) {
5593
5671
  return { type: "stdout", data: item.text };
5594
5672
  }
5673
+ if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
5674
+ const name = `mcp__${item.server}__${item.tool}`;
5675
+ const input = asRecord(item.arguments);
5676
+ const events = [
5677
+ { type: "tool_use", id: item.id, name, input }
5678
+ ];
5679
+ const finished = type === "item.completed" || item.status === "completed" || item.status === "failed";
5680
+ if (finished) {
5681
+ const errMsg = item.error && typeof item.error.message === "string" ? item.error.message : void 0;
5682
+ events.push({
5683
+ type: "tool_result",
5684
+ id: item.id,
5685
+ content: unwrapCodexMcpResult(item.result) ?? errMsg,
5686
+ isError: item.status === "failed" || Boolean(errMsg)
5687
+ });
5688
+ }
5689
+ return events.length === 1 ? events[0] : events;
5690
+ }
5691
+ if (itemType === "command_execution" && item.id) {
5692
+ const input = item.command ? { command: item.command } : void 0;
5693
+ const events = [
5694
+ { type: "tool_use", id: item.id, name: "Bash", input }
5695
+ ];
5696
+ if (type === "item.completed" || item.status === "completed" || item.status === "failed") {
5697
+ events.push({
5698
+ type: "tool_result",
5699
+ id: item.id,
5700
+ content: item.aggregated_output,
5701
+ isError: item.status === "failed"
5702
+ });
5703
+ }
5704
+ return events.length === 1 ? events[0] : events;
5705
+ }
5595
5706
  if (item.status === "failed") {
5596
- const detail = item.message?.trim() || extractJsonErrorMessage(item) || `Codex ${item.type ?? "item"} failed`;
5707
+ const detail = item.message?.trim() || extractJsonErrorMessage(item) || `Codex ${itemType ?? "item"} failed`;
5597
5708
  return { type: "stderr", data: detail };
5598
5709
  }
5599
5710
  }
@@ -5679,6 +5790,75 @@ function usageFromCursor(usage) {
5679
5790
  cacheWriteTokens: usage.cacheWriteTokens ? Number(usage.cacheWriteTokens) : void 0
5680
5791
  };
5681
5792
  }
5793
+ function asRecord2(value) {
5794
+ if (value && typeof value === "object" && !Array.isArray(value)) {
5795
+ return value;
5796
+ }
5797
+ return void 0;
5798
+ }
5799
+ function normalizeCursorToolCall(name, args) {
5800
+ const raw = asRecord2(args);
5801
+ const toolName = typeof raw?.toolName === "string" && raw.toolName.trim() ? raw.toolName.trim() : null;
5802
+ const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord2(raw?.args) != null);
5803
+ if (looksLikeMcp && toolName) {
5804
+ const providerRaw = typeof raw.providerIdentifier === "string" && raw.providerIdentifier.trim() ? raw.providerIdentifier.trim() : "sideboard";
5805
+ const provider = providerRaw.replace(/[^a-zA-Z0-9_-]/g, "_");
5806
+ const nested = asRecord2(raw.args);
5807
+ return {
5808
+ name: `mcp__${provider}__${toolName}`,
5809
+ input: nested ?? { toolName }
5810
+ };
5811
+ }
5812
+ return { name, input: raw };
5813
+ }
5814
+ function unwrapCursorToolResult(result) {
5815
+ if (result == null) return void 0;
5816
+ if (typeof result === "string") return result;
5817
+ const rec = asRecord2(result);
5818
+ if (!rec) {
5819
+ try {
5820
+ return JSON.stringify(result);
5821
+ } catch {
5822
+ return String(result);
5823
+ }
5824
+ }
5825
+ const collectTexts = (items) => {
5826
+ const texts = [];
5827
+ for (const item of items) {
5828
+ const row = asRecord2(item);
5829
+ if (!row) continue;
5830
+ if (typeof row.text === "string") {
5831
+ texts.push(row.text);
5832
+ continue;
5833
+ }
5834
+ const nested = asRecord2(row.text);
5835
+ if (typeof nested?.text === "string") texts.push(nested.text);
5836
+ }
5837
+ return texts;
5838
+ };
5839
+ const value = asRecord2(rec.value);
5840
+ if (value && Array.isArray(value.content)) {
5841
+ const texts = collectTexts(value.content);
5842
+ if (texts.length) return texts.join("\n");
5843
+ }
5844
+ if (Array.isArray(rec.content)) {
5845
+ const texts = collectTexts(rec.content);
5846
+ if (texts.length) return texts.join("\n");
5847
+ }
5848
+ try {
5849
+ return JSON.stringify(result);
5850
+ } catch {
5851
+ return String(result);
5852
+ }
5853
+ }
5854
+ function cursorToolResultIsError(status, result) {
5855
+ if (status === "error") return true;
5856
+ const rec = asRecord2(result);
5857
+ if (!rec) return false;
5858
+ if (rec.status === "error") return true;
5859
+ const value = asRecord2(rec.value);
5860
+ return value?.isError === true;
5861
+ }
5682
5862
  function cursorSdkMessageToEvents(msg) {
5683
5863
  if (!msg?.type) return [];
5684
5864
  if (msg.type === "system" && msg.agent_id) {
@@ -5693,35 +5873,42 @@ function cursorSdkMessageToEvents(msg) {
5693
5873
  if (block?.type === "text" && block.text) {
5694
5874
  out.push({ type: "stdout", data: block.text });
5695
5875
  } else if (block?.type === "tool_use" && block.id && block.name) {
5876
+ const normalized = normalizeCursorToolCall(block.name, block.input);
5696
5877
  out.push({
5697
5878
  type: "tool_use",
5698
5879
  id: block.id,
5699
- name: block.name,
5700
- input: block.input && typeof block.input === "object" ? block.input : void 0
5880
+ name: normalized.name,
5881
+ input: normalized.input
5701
5882
  });
5702
5883
  }
5703
5884
  }
5704
5885
  return out;
5705
5886
  }
5706
5887
  if (msg.type === "tool_call" && msg.call_id && msg.name) {
5888
+ const normalized = normalizeCursorToolCall(msg.name, msg.args);
5707
5889
  if (msg.status === "running") {
5708
5890
  return [
5709
5891
  {
5710
5892
  type: "tool_use",
5711
5893
  id: msg.call_id,
5712
- name: msg.name,
5713
- input: msg.args && typeof msg.args === "object" ? msg.args : void 0
5894
+ name: normalized.name,
5895
+ input: normalized.input
5714
5896
  }
5715
5897
  ];
5716
5898
  }
5717
5899
  if (msg.status === "completed" || msg.status === "error") {
5718
- const content = typeof msg.result === "string" ? msg.result : msg.result != null ? JSON.stringify(msg.result) : void 0;
5719
5900
  return [
5901
+ {
5902
+ type: "tool_use",
5903
+ id: msg.call_id,
5904
+ name: normalized.name,
5905
+ input: normalized.input
5906
+ },
5720
5907
  {
5721
5908
  type: "tool_result",
5722
5909
  id: msg.call_id,
5723
- content,
5724
- isError: msg.status === "error"
5910
+ content: unwrapCursorToolResult(msg.result),
5911
+ isError: cursorToolResultIsError(msg.status, msg.result)
5725
5912
  }
5726
5913
  ];
5727
5914
  }
@@ -6091,17 +6278,31 @@ var init_opencode = __esm({
6091
6278
  return { type: "stderr", data: detail };
6092
6279
  }
6093
6280
  const sid = typeof obj.sessionID === "string" && obj.sessionID || typeof obj.sessionId === "string" && obj.sessionId || typeof obj.session_id === "string" && obj.session_id;
6094
- if (sid) return { type: "session_id", data: sid };
6281
+ if (sid && (!obj.type || obj.type === "step_start" || obj.type === "session")) {
6282
+ return { type: "session_id", data: sid };
6283
+ }
6095
6284
  if (obj.type === "text") {
6096
6285
  const text = obj.part?.text ?? obj.text;
6097
6286
  if (text) return { type: "stdout", data: text };
6098
6287
  }
6099
6288
  if (obj.type === "tool_use") {
6100
6289
  const part = obj.part;
6101
- const id = part?.id ?? obj.id ?? `tool-${Date.now()}`;
6102
- const name = part?.name ?? part?.tool ?? obj.name ?? obj.tool ?? "tool";
6103
- const input = part?.input ?? obj.input;
6104
- return { type: "tool_use", id, name, input };
6290
+ const id = part?.callID ?? part?.id ?? obj.id ?? `tool-${Date.now()}`;
6291
+ const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
6292
+ const state = part?.state;
6293
+ const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
6294
+ const events = [{ type: "tool_use", id, name, input }];
6295
+ const output = state?.output;
6296
+ if (output != null || state?.status === "completed" || state?.status === "error") {
6297
+ const content = typeof output === "string" ? output : output != null ? JSON.stringify(output) : formatUnknownDetail(state?.error) || void 0;
6298
+ events.push({
6299
+ type: "tool_result",
6300
+ id,
6301
+ content,
6302
+ isError: state?.status === "error" || state?.status === "failed"
6303
+ });
6304
+ }
6305
+ return events.length === 1 ? events[0] : events;
6105
6306
  }
6106
6307
  if (obj.type === "tool_result") {
6107
6308
  const part = obj.part;
@@ -6654,7 +6855,7 @@ var init_agents = __esm({
6654
6855
  });
6655
6856
 
6656
6857
  // src/plan/plan-present.ts
6657
- function asRecord2(v) {
6858
+ function asRecord4(v) {
6658
6859
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
6659
6860
  }
6660
6861
  function isPresentPlanToolName(name) {
@@ -6666,7 +6867,7 @@ function extractPresentedPlan(parts) {
6666
6867
  for (let i = parts.length - 1; i >= 0; i--) {
6667
6868
  const p = parts[i];
6668
6869
  if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
6669
- const input = asRecord2(p.input) ?? {};
6870
+ const input = asRecord4(p.input) ?? {};
6670
6871
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
6671
6872
  if (!content.trim()) continue;
6672
6873
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
@@ -7148,6 +7349,7 @@ __export(index_exports, {
7148
7349
  resolveFilesToCopy: () => resolveFilesToCopy,
7149
7350
  resolveGhAuthToken: () => resolveGhAuthToken,
7150
7351
  resolveGithubRepoSlug: () => resolveGithubRepoSlug,
7352
+ resolveNewThreadOptions: () => resolveNewThreadOptions,
7151
7353
  resolvePlanMarkdown: () => resolvePlanMarkdown,
7152
7354
  resolvePrSelector: () => resolvePrSelector,
7153
7355
  resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
@@ -7426,7 +7628,7 @@ init_agents();
7426
7628
  init_orchestrator_capable();
7427
7629
 
7428
7630
  // src/agents/message-parts.ts
7429
- function asRecord(input) {
7631
+ function asRecord3(input) {
7430
7632
  if (input && typeof input === "object" && !Array.isArray(input)) {
7431
7633
  return input;
7432
7634
  }
@@ -7561,7 +7763,7 @@ function applyAgentEvent(parts, event) {
7561
7763
  return next;
7562
7764
  }
7563
7765
  if (event.type === "tool_use") {
7564
- const input = asRecord(event.input);
7766
+ const input = asRecord3(event.input);
7565
7767
  const diff = diffFromInput(input);
7566
7768
  const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
7567
7769
  if (existing >= 0) {
@@ -7595,9 +7797,25 @@ function applyAgentEvent(parts, event) {
7595
7797
  ];
7596
7798
  }
7597
7799
  if (event.type === "tool_result") {
7598
- const next = parts.map((p) => {
7599
- if (p.type !== "tool" || p.id !== event.id) return p;
7600
- const fromResult = parseDiffStat(event.content);
7800
+ const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
7801
+ const fromResult = parseDiffStat(event.content);
7802
+ if (existing < 0) {
7803
+ return [
7804
+ ...parts,
7805
+ {
7806
+ type: "tool",
7807
+ id: event.id,
7808
+ name: "tool",
7809
+ description: "tool",
7810
+ status: event.isError ? "error" : "done",
7811
+ result: event.content,
7812
+ ...fromResult.additions != null ? { additions: fromResult.additions } : {},
7813
+ ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
7814
+ }
7815
+ ];
7816
+ }
7817
+ return parts.map((p, i) => {
7818
+ if (i !== existing || p.type !== "tool") return p;
7601
7819
  return {
7602
7820
  ...p,
7603
7821
  status: event.isError ? "error" : "done",
@@ -7606,7 +7824,6 @@ function applyAgentEvent(parts, event) {
7606
7824
  ...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
7607
7825
  };
7608
7826
  });
7609
- return next;
7610
7827
  }
7611
7828
  return parts;
7612
7829
  }
@@ -9874,10 +10091,17 @@ async function confirmLand(thread, opts) {
9874
10091
  // src/threads/create.ts
9875
10092
  var import_node_fs20 = require("fs");
9876
10093
  init_worktree();
10094
+ init_app_settings();
9877
10095
  init_thread_store();
9878
10096
  init_workspaces();
9879
10097
  async function createThread(input, _onSetupLine) {
9880
- await requireAgent(input.agent);
10098
+ const resolved = resolveNewThreadOptions({
10099
+ agent: input.agent,
10100
+ model: input.model,
10101
+ effort: input.effort,
10102
+ fast: input.fast
10103
+ });
10104
+ await requireAgent(resolved.agent);
9881
10105
  const repoPath = await resolveRepoRoot(input.repoPath);
9882
10106
  if (!(0, import_node_fs20.existsSync)(repoPath)) {
9883
10107
  throw new Error(`Repo not found: ${repoPath}`);
@@ -9920,11 +10144,11 @@ async function createThread(input, _onSetupLine) {
9920
10144
  branchName,
9921
10145
  worktreePath,
9922
10146
  repoPath,
9923
- agent: input.agent,
10147
+ agent: resolved.agent,
9924
10148
  autonomy: input.autonomy ?? "default",
9925
- model: input.model ?? null,
9926
- effort: input.effort ?? "high",
9927
- fast: Boolean(input.fast),
10149
+ model: resolved.model,
10150
+ effort: resolved.effort,
10151
+ fast: resolved.fast,
9928
10152
  planMode: Boolean(input.planMode),
9929
10153
  attachments: input.attachments ?? [],
9930
10154
  sourceIsFork,
@@ -11416,6 +11640,7 @@ var Orchestrator = class {
11416
11640
  }
11417
11641
  async reconcile(repoPath, opts) {
11418
11642
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
11643
+ const drainQueues = opts?.drainQueues !== false;
11419
11644
  healOrchestrationSoccerTitles();
11420
11645
  for (const thread of listThreads({ includeArchived: true })) {
11421
11646
  if (thread.status === "archived") continue;
@@ -11462,12 +11687,36 @@ var Orchestrator = class {
11462
11687
  }
11463
11688
  } catch {
11464
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() {
11465
11701
  for (const thread of listThreads()) {
11466
- 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);
11467
11717
  void this.drainQueue(thread.id);
11468
11718
  }
11469
11719
  }
11470
- this.schedulePendingQuotaResumes();
11471
11720
  }
11472
11721
  clearQuotaResumeTimer(threadId) {
11473
11722
  const timer = this.quotaResumeTimers.get(threadId);
@@ -11621,7 +11870,7 @@ var Orchestrator = class {
11621
11870
  }
11622
11871
  }
11623
11872
  listWorkspaces() {
11624
- const fromThreads = listThreads({ includeArchived: false }).map((t) => t.repoPath);
11873
+ const fromThreads = listThreads({ includeArchived: true }).map((t) => t.repoPath);
11625
11874
  return syncWorkspacesFromThreads(fromThreads);
11626
11875
  }
11627
11876
  async addWorkspace(repoPath) {
@@ -11649,7 +11898,12 @@ var Orchestrator = class {
11649
11898
  const current = this.requireThread(thread.id);
11650
11899
  const queue = [...current.queue, prompt];
11651
11900
  this.haltDrain.delete(thread.id);
11652
- 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);
11653
11907
  this.emit({ type: "queue_changed", threadId: thread.id, queue });
11654
11908
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
11655
11909
  void this.drainQueue(thread.id);
@@ -12721,7 +12975,15 @@ var Orchestrator = class {
12721
12975
  }
12722
12976
  await removeWorktree(thread.repoPath, thread.worktreePath);
12723
12977
  }
12724
- return setStatus(thread.id, "archived");
12978
+ const archived = setStatus(thread.id, "archived");
12979
+ if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
12980
+ try {
12981
+ const { ensureWorkspace: ensureWorkspace2 } = await Promise.resolve().then(() => (init_workspaces(), workspaces_exports));
12982
+ await ensureWorkspace2(thread.repoPath);
12983
+ } catch {
12984
+ }
12985
+ }
12986
+ return archived;
12725
12987
  }
12726
12988
  async purge(threadRef, opts) {
12727
12989
  const thread = this.requireThread(threadRef);
@@ -12866,7 +13128,7 @@ async function startOrchestration(opts) {
12866
13128
  init_workspace_scratch();
12867
13129
 
12868
13130
  // src/plan/ask-user.ts
12869
- function asRecord3(v) {
13131
+ function asRecord5(v) {
12870
13132
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
12871
13133
  }
12872
13134
  function parseOptions(raw) {
@@ -12877,7 +13139,7 @@ function parseOptions(raw) {
12877
13139
  out.push({ label: item.trim() });
12878
13140
  continue;
12879
13141
  }
12880
- const o = asRecord3(item);
13142
+ const o = asRecord5(item);
12881
13143
  if (!o) continue;
12882
13144
  const label = typeof o.label === "string" ? o.label.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.title === "string" ? o.title.trim() : "";
12883
13145
  if (!label) continue;
@@ -12887,7 +13149,7 @@ function parseOptions(raw) {
12887
13149
  return out;
12888
13150
  }
12889
13151
  function parseOneQuestion(raw) {
12890
- const o = asRecord3(raw);
13152
+ const o = asRecord5(raw);
12891
13153
  if (!o) return null;
12892
13154
  const question = typeof o.question === "string" ? o.question.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.prompt === "string" ? o.prompt.trim() : "";
12893
13155
  if (!question) return null;
@@ -12898,7 +13160,7 @@ function parseOneQuestion(raw) {
12898
13160
  return { question, header, multiSelect, options };
12899
13161
  }
12900
13162
  function parsePlanQuestionsInput(input) {
12901
- const root = asRecord3(input);
13163
+ const root = asRecord5(input);
12902
13164
  if (!root) return [];
12903
13165
  const list = Array.isArray(root.questions) ? root.questions : Array.isArray(root.question) ? root.question : root.question || root.prompt ? [root] : [];
12904
13166
  const out = [];
@@ -12990,7 +13252,12 @@ function mcpArchiveBlockedReason(thread) {
12990
13252
  var MAX_ORCH_THREADS = 5;
12991
13253
  async function startMcpServer() {
12992
13254
  const orch = getOrchestrator();
12993
- 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 });
12994
13261
  const server = new import_mcp.McpServer({
12995
13262
  name: "sideboard",
12996
13263
  version: "0.1.0"
@@ -13190,13 +13457,19 @@ async function startMcpServer() {
13190
13457
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
13191
13458
  }
13192
13459
  );
13460
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
13461
+ const accountDefaults = resolveThreadDefaults2();
13462
+ const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
13193
13463
  server.tool(
13194
13464
  "create_thread",
13195
- "Create a new worktree thread (chat) from branch, pr, or ticket. Pass repoPath from list_workspaces and parentThreadId when spawning from an orchestrator. Then use send_to_thread to chat.",
13465
+ `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.`,
13196
13466
  {
13197
13467
  sourceType: import_zod.z.enum(["branch", "pr", "ticket"]),
13198
13468
  sourceRef: import_zod.z.string(),
13199
- agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
13469
+ agent: import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(`Omit to use Account default agent (${accountDefaults.agent})`),
13470
+ model: import_zod.z.string().nullable().optional().describe(
13471
+ `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
13472
+ ),
13200
13473
  repoPath: import_zod.z.string(),
13201
13474
  title: import_zod.z.string().optional(),
13202
13475
  parentThreadId: import_zod.z.string().optional()
@@ -13216,10 +13489,17 @@ async function startMcpServer() {
13216
13489
  };
13217
13490
  }
13218
13491
  }
13492
+ const opts = resolveNewThreadOptions2({
13493
+ agent: args.agent,
13494
+ model: args.model
13495
+ });
13219
13496
  const thread = await orch.createThread({
13220
13497
  sourceType: args.sourceType,
13221
13498
  sourceRef: args.sourceRef,
13222
- agent: args.agent,
13499
+ agent: opts.agent,
13500
+ model: opts.model,
13501
+ effort: opts.effort,
13502
+ fast: opts.fast,
13223
13503
  repoPath: args.repoPath,
13224
13504
  title: args.title,
13225
13505
  parentThreadId: args.parentThreadId ?? null
@@ -13233,6 +13513,8 @@ async function startMcpServer() {
13233
13513
  title: thread.title,
13234
13514
  branchName: thread.branchName,
13235
13515
  worktreePath: thread.worktreePath,
13516
+ agent: thread.agent,
13517
+ model: thread.model,
13236
13518
  status: thread.status,
13237
13519
  link: `sideboard://thread/${thread.id}`
13238
13520
  })
@@ -13477,7 +13759,7 @@ async function startMcpServer() {
13477
13759
  async ({ ref, through_index, agent, model, title }) => {
13478
13760
  try {
13479
13761
  const source = orch.getThread(ref);
13480
- if (source) await orch.reconcile(source.repoPath);
13762
+ if (source) await orch.reconcile(source.repoPath, { drainQueues: false });
13481
13763
  const thread = await orch.forkThreadWorktree({
13482
13764
  threadId: ref,
13483
13765
  throughIndex: through_index,
@@ -14507,6 +14789,7 @@ init_injected_mcp();
14507
14789
  resolveFilesToCopy,
14508
14790
  resolveGhAuthToken,
14509
14791
  resolveGithubRepoSlug,
14792
+ resolveNewThreadOptions,
14510
14793
  resolvePlanMarkdown,
14511
14794
  resolvePrSelector,
14512
14795
  resolveQuotaFallbackAgent,