@sideboard-ai/core 0.1.297 → 0.1.298

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 (37) hide show
  1. package/dist/{agents-ICHYDSCD.js → agents-GHZFY65E.js} +5 -5
  2. package/dist/{agents-DZTPUKVF.js → agents-XL67HSZH.js} +5 -5
  3. package/dist/{chunk-QT36F4TD.js → chunk-2POLTCSC.js} +326 -25
  4. package/dist/{chunk-VXNZG4J6.js → chunk-3IXGI2FP.js} +1 -1
  5. package/dist/{chunk-67BWWLEB.js → chunk-4FWMUMXG.js} +5 -5
  6. package/dist/{chunk-WQREAX35.js → chunk-5FMQU2RU.js} +9 -5
  7. package/dist/{chunk-ED4ZXQVO.js → chunk-5XQ43YZU.js} +2 -1
  8. package/dist/{chunk-JWXJXEA3.js → chunk-BIMHIHX3.js} +1 -1
  9. package/dist/{chunk-Q3DPZ4DE.js → chunk-F36QRDM4.js} +2 -1
  10. package/dist/{chunk-6GTABVNG.js → chunk-IL77XODJ.js} +6 -4
  11. package/dist/{chunk-ZXWRNA4G.js → chunk-JM5P36PM.js} +5 -5
  12. package/dist/{chunk-WZLV4WNS.js → chunk-K5EACGG2.js} +11 -0
  13. package/dist/{chunk-BGEECLKH.js → chunk-KMYCNSSI.js} +9 -5
  14. package/dist/{chunk-W2A63ZOK.js → chunk-KRGQWQME.js} +329 -26
  15. package/dist/{chunk-2O3ICR77.js → chunk-RQMLJZ2V.js} +6 -4
  16. package/dist/{conductor-ZXDIA6OF.js → conductor-HUITLA2M.js} +5 -5
  17. package/dist/{conductor-HJFDYIZD.js → conductor-NGIEDMAB.js} +5 -5
  18. package/dist/{coordinator-prompt-L3V2CMWX.js → coordinator-prompt-52T5A4SU.js} +3 -3
  19. package/dist/{coordinator-prompt-ILHSVPOG.js → coordinator-prompt-JUN3IITF.js} +3 -3
  20. package/dist/{global-workspace-47G453ZU.js → global-workspace-VVUIFQ72.js} +3 -3
  21. package/dist/{global-workspace-NMRUGGFF.js → global-workspace-Z5YRL67Y.js} +3 -3
  22. package/dist/index.cjs +793 -337
  23. package/dist/index.d.cts +16 -9
  24. package/dist/index.d.ts +16 -9
  25. package/dist/index.js +343 -228
  26. package/dist/mcp/run-stdio.cjs +739 -302
  27. package/dist/mcp/run-stdio.js +323 -217
  28. package/dist/{orchestrator-MR7TCIPP.js → orchestrator-JY4ZETFX.js} +7 -7
  29. package/dist/{orchestrator-ZQU62CES.js → orchestrator-S7TKI6DR.js} +7 -7
  30. package/dist/{thread-store-H6D6MDUM.js → thread-store-5XXHSWMN.js} +1 -1
  31. package/dist/{thread-store-SADLLKRE.js → thread-store-WXLXKDVN.js} +1 -1
  32. package/dist/{workspaces-PGY4UWZ3.js → workspaces-7FVNGUBV.js} +3 -3
  33. package/dist/{workspaces-WGZPRQJX.js → workspaces-OSF56FHS.js} +3 -3
  34. package/dist/{worktree-JZXDAKQF.js → worktree-6G5X4ABY.js} +2 -2
  35. package/dist/{worktree-3MF5J5XZ.js → worktree-LFSBIUCN.js} +2 -2
  36. package/package.json +1 -1
  37. /package/dist/{chunk-TVN4TDFB.js → chunk-HTL4FBTP.js} +0 -0
package/dist/index.js CHANGED
@@ -42,6 +42,8 @@ import {
42
42
  computeNextRunAt,
43
43
  conductorDbPath,
44
44
  confirmLand,
45
+ connectedOrchestrator,
46
+ connectedOrchestratorTurnReminder,
45
47
  consumeComposerAttachments,
46
48
  createPrStack,
47
49
  createSchedule,
@@ -67,8 +69,10 @@ import {
67
69
  forkThreadWorktree,
68
70
  formatArtifactDirective,
69
71
  formatAskUserNotifyMessage,
72
+ formatConnectedOrchestratorReminder,
70
73
  formatDetachedJobInvoke,
71
74
  formatDevAccessHint,
75
+ formatFleetArchive,
72
76
  formatGitHubSearchUpdatedSince,
73
77
  formatInjectedNoticesForTurn,
74
78
  formatIssueToolsDirective,
@@ -120,6 +124,7 @@ import {
120
124
  linearTicketIdFromThread,
121
125
  listBranchCommits,
122
126
  listConductorWorkspaces,
127
+ listFleetArchive,
123
128
  listLinearIssues,
124
129
  listSchedules,
125
130
  listWorktreeFiles,
@@ -191,7 +196,7 @@ import {
191
196
  worktreeCleanupSettings,
192
197
  writeAgentBlock,
193
198
  writeWorktreeFile
194
- } from "./chunk-W2A63ZOK.js";
199
+ } from "./chunk-KRGQWQME.js";
195
200
  import {
196
201
  LEGACY_PLAN_FILE_REL,
197
202
  PLAN_FILE_NAME,
@@ -287,7 +292,7 @@ import {
287
292
  userMcpNamesToDisable,
288
293
  worktreeMcpToolNames,
289
294
  writeInjectedMcpConfig
290
- } from "./chunk-6GTABVNG.js";
295
+ } from "./chunk-IL77XODJ.js";
291
296
  import {
292
297
  brightsyMcpServerName,
293
298
  connectBrightsyTeam,
@@ -415,7 +420,7 @@ import {
415
420
  workspaceAgentChatSummaries,
416
421
  worktreeRunPortsPath,
417
422
  wrapReviewSkillMarkdown
418
- } from "./chunk-67BWWLEB.js";
423
+ } from "./chunk-4FWMUMXG.js";
419
424
  import {
420
425
  STDERR_EVENT_MAX_CHARS,
421
426
  STDOUT_EVENT_MAX_CHARS,
@@ -457,7 +462,7 @@ import {
457
462
  visibleToolRowDetail,
458
463
  withEventParentId,
459
464
  withEventsParentId
460
- } from "./chunk-Q3DPZ4DE.js";
465
+ } from "./chunk-F36QRDM4.js";
461
466
  import "./chunk-7H655NEW.js";
462
467
  import {
463
468
  ABLETIME_OAUTH_CALLBACK_PATH,
@@ -615,7 +620,7 @@ import {
615
620
  stageBuffersAsAttachments,
616
621
  syncWorkspacesFromThreads,
617
622
  takenTeamSlugsForOrchestration
618
- } from "./chunk-BGEECLKH.js";
623
+ } from "./chunk-KMYCNSSI.js";
619
624
  import {
620
625
  GITHUB_PR_BODY_MAX_CHARS,
621
626
  GITHUB_PR_BODY_SAFE_CHARS,
@@ -703,7 +708,7 @@ import {
703
708
  suggestSlug,
704
709
  switchWorktreeBranch,
705
710
  warmGithubAgentAuth
706
- } from "./chunk-JWXJXEA3.js";
711
+ } from "./chunk-BIMHIHX3.js";
707
712
  import {
708
713
  FAMOUS_SOCCER_TEAMS,
709
714
  allocateTeamName,
@@ -729,13 +734,14 @@ import {
729
734
  ticketSlugForBranch,
730
735
  updateThread,
731
736
  withThreadLock,
737
+ workspaceNameLabel,
732
738
  workspaceTagsFromGroup,
733
739
  worktreeDisplayLabel,
734
740
  worktreeDisplayLabelForGroup,
735
741
  worktreeNameFromPath,
736
742
  worktreeSlugForTicket,
737
743
  writeThread
738
- } from "./chunk-WZLV4WNS.js";
744
+ } from "./chunk-K5EACGG2.js";
739
745
  import "./chunk-2P4XKWV2.js";
740
746
  import {
741
747
  BRIGHTSY_OAUTH_CLIENT_ID,
@@ -3643,18 +3649,10 @@ function createClippedMcpServer() {
3643
3649
  }
3644
3650
 
3645
3651
  // src/mcp/server.ts
3646
- import { z as z7 } from "zod";
3652
+ import { z as z8 } from "zod";
3647
3653
  import { randomUUID as randomUUID5 } from "crypto";
3648
3654
  import { basename as basename2 } from "path";
3649
3655
 
3650
- // src/mcp/archive-guard.ts
3651
- function mcpArchiveBlockedReason(thread) {
3652
- if (isCloudCoordinatorThread(thread)) {
3653
- return "Refusing to archive the cloud coordinator via MCP. Archive it from the Sideboard UI if needed.";
3654
- }
3655
- return null;
3656
- }
3657
-
3658
3656
  // src/mcp/run-script-ref.ts
3659
3657
  function resolveRunScriptThreadRef(ref) {
3660
3658
  const explicit = ref?.trim();
@@ -4881,8 +4879,137 @@ function formatMcpPrList(input) {
4881
4879
  };
4882
4880
  }
4883
4881
 
4884
- // src/mcp/schedule-tools.ts
4882
+ // src/mcp/archive-tools.ts
4885
4883
  import { z as z6 } from "zod";
4884
+
4885
+ // src/mcp/archive-guard.ts
4886
+ function mcpArchiveBlockedReason(thread) {
4887
+ if (isCloudCoordinatorThread(thread)) {
4888
+ return "Refusing to archive the cloud coordinator via MCP. Archive it from the Sideboard UI if needed.";
4889
+ }
4890
+ return null;
4891
+ }
4892
+
4893
+ // src/mcp/archive-tools.ts
4894
+ function registerArchiveTools(server, orch) {
4895
+ server.tool(
4896
+ "list_archive",
4897
+ "Browse Settings \u2192 History (archived workspaces and agents), newest first. A workspace row is a checkout with no live agents \u2014 it is off Home and list_board. Nested lines are that workspace's agents; any of those ids restores the whole checkout. An agent row is an orchestration chat, or one agent archived while its workspace stayed live (workspace-live). kind=agent lists every archived agent, including agents on an archived workspace (workspace-archived). query matches title, branch, repo, agent, ticket, or PR. limit default 40, max 250. Then restore_workspace (whole checkout) or restore_chat (one agent).",
4898
+ {
4899
+ query: z6.string().optional().describe("Title, branch, repo, agent, ticket, or PR"),
4900
+ kind: z6.enum(["all", "workspace", "agent"]).optional().describe("all (default), workspace, or agent"),
4901
+ limit: z6.number().int().positive().max(250).optional().describe("Page size (default 40, max 250)")
4902
+ },
4903
+ async ({ query, kind, limit }) => {
4904
+ const view = listFleetArchive(orch.getThreads(true), { query, kind, limit });
4905
+ return { content: [{ type: "text", text: formatFleetArchive(view) }] };
4906
+ }
4907
+ );
4908
+ server.tool(
4909
+ "archive_workspace",
4910
+ "Archive a workspace: every live agent on that checkout, then remove the worktree when the last agent archives (cowboy keeps the project folder). The Home card moves to Settings \u2192 History. Pass any chat id on the workspace. Orchestration chats are agents \u2014 use archive_chat. To archive one agent and leave the checkout up, use archive_chat.",
4911
+ { ref: z6.string().describe("Any chat id on the workspace") },
4912
+ async ({ ref }) => {
4913
+ const thread = orch.getThread(ref);
4914
+ if (!thread) {
4915
+ return { content: [{ type: "text", text: `Chat not found: ${ref}` }], isError: true };
4916
+ }
4917
+ for (const chat of threadsSharingWorktree(thread.worktreePath)) {
4918
+ const blocked = mcpArchiveBlockedReason(chat);
4919
+ if (blocked) {
4920
+ return { content: [{ type: "text", text: blocked }], isError: true };
4921
+ }
4922
+ }
4923
+ try {
4924
+ const archived = await orch.archiveWorkspace(ref);
4925
+ return mcpJson({
4926
+ worktreePath: thread.worktreePath,
4927
+ archived: archived.map((chat) => ({
4928
+ id: chat.id,
4929
+ title: chat.title,
4930
+ agent: chat.agent,
4931
+ status: chat.status,
4932
+ link: `sideboard://chat/${chat.id}`
4933
+ }))
4934
+ });
4935
+ } catch (err) {
4936
+ const message = err instanceof Error ? err.message : String(err);
4937
+ return { content: [{ type: "text", text: message }], isError: true };
4938
+ }
4939
+ }
4940
+ );
4941
+ server.tool(
4942
+ "archive_chat",
4943
+ "Archive one agent chat: stop the turn and move that chat to Settings \u2192 History. Other agents on the workspace stay, and the worktree is removed only when this is the last agent. Orchestration chats archive without removing a project checkout. Refuses the cloud coordinator. To archive every agent on a checkout, use archive_workspace. Commit, push, and open PRs via ask_git before archiving when the work should be kept. Merge only when the user explicitly asked.",
4944
+ { ref: z6.string().describe("Chat id to archive") },
4945
+ async ({ ref }) => {
4946
+ const t = orch.getThread(ref);
4947
+ if (!t) {
4948
+ return {
4949
+ content: [{ type: "text", text: `Chat not found: ${ref}` }],
4950
+ isError: true
4951
+ };
4952
+ }
4953
+ const blocked = mcpArchiveBlockedReason(t);
4954
+ if (blocked) {
4955
+ return {
4956
+ content: [{ type: "text", text: blocked }],
4957
+ isError: true
4958
+ };
4959
+ }
4960
+ const archived = await orch.archive(ref);
4961
+ return mcpJson({
4962
+ id: archived.id,
4963
+ status: archived.status
4964
+ });
4965
+ }
4966
+ );
4967
+ server.tool(
4968
+ "restore_workspace",
4969
+ "Restore a workspace from Settings \u2192 History: recreate the worktree if it was removed and bring every archived agent on that checkout back. Pass any chat id from list_archive. If some agents are still live, only the archived ones return to that checkout. One agent only: restore_chat. Orchestration agents: restore_chat.",
4970
+ { ref: z6.string().describe("Any chat id from list_archive on that workspace") },
4971
+ async ({ ref }) => {
4972
+ try {
4973
+ const restored = await orch.restoreWorkspace(ref);
4974
+ return mcpJson({
4975
+ worktreePath: restored[0]?.worktreePath ?? null,
4976
+ restored: restored.map((chat) => ({
4977
+ id: chat.id,
4978
+ title: chat.title,
4979
+ agent: chat.agent,
4980
+ status: chat.status,
4981
+ worktreePath: chat.worktreePath,
4982
+ link: `sideboard://chat/${chat.id}`
4983
+ }))
4984
+ });
4985
+ } catch (err) {
4986
+ const message = err instanceof Error ? err.message : String(err);
4987
+ return { content: [{ type: "text", text: message }], isError: true };
4988
+ }
4989
+ }
4990
+ );
4991
+ server.tool(
4992
+ "restore_chat",
4993
+ "Restore one archived agent from list_archive. Recreates the worktree when that checkout was fully archived and the folder is gone. Other archived agents on the checkout stay in History until restore_workspace or another restore_chat. Orchestration agents come back as Global chats.",
4994
+ { ref: z6.string().describe("Archived chat id from list_archive") },
4995
+ async ({ ref }) => {
4996
+ try {
4997
+ const restored = await orch.restore(ref);
4998
+ return mcpJson({
4999
+ id: restored.id,
5000
+ status: restored.status,
5001
+ worktreePath: restored.worktreePath
5002
+ });
5003
+ } catch (err) {
5004
+ const message = err instanceof Error ? err.message : String(err);
5005
+ return { content: [{ type: "text", text: message }], isError: true };
5006
+ }
5007
+ }
5008
+ );
5009
+ }
5010
+
5011
+ // src/mcp/schedule-tools.ts
5012
+ import { z as z7 } from "zod";
4886
5013
  function text4(payload, isError = false) {
4887
5014
  return mcpJson(payload, isError);
4888
5015
  }
@@ -4922,18 +5049,18 @@ function registerScheduleTools(server) {
4922
5049
  "create_schedule",
4923
5050
  "Create a local schedule that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without chatId open a new Global chat each run. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
4924
5051
  {
4925
- prompt: z6.string().describe("User message / goal queued when the schedule fires"),
4926
- name: z6.string().optional(),
4927
- at: z6.string().optional().describe("ISO datetime for a one-shot"),
4928
- every: z6.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
4929
- cron: z6.string().optional().describe("5-field cron expression"),
4930
- tz: z6.string().optional().describe("IANA timezone for cron (default: system)"),
4931
- chatId: z6.string().optional().describe(
5052
+ prompt: z7.string().describe("User message / goal queued when the schedule fires"),
5053
+ name: z7.string().optional(),
5054
+ at: z7.string().optional().describe("ISO datetime for a one-shot"),
5055
+ every: z7.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
5056
+ cron: z7.string().optional().describe("5-field cron expression"),
5057
+ tz: z7.string().optional().describe("IANA timezone for cron (default: system)"),
5058
+ chatId: z7.string().optional().describe(
4932
5059
  'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
4933
5060
  ),
4934
- threadId: z6.string().optional().describe("Legacy alias for chatId."),
4935
- agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
4936
- model: z6.string().optional()
5061
+ threadId: z7.string().optional().describe("Legacy alias for chatId."),
5062
+ agent: z7.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
5063
+ model: z7.string().optional()
4937
5064
  },
4938
5065
  async (args) => {
4939
5066
  try {
@@ -4969,20 +5096,20 @@ function registerScheduleTools(server) {
4969
5096
  "update_schedule",
4970
5097
  "Update a local schedule (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
4971
5098
  {
4972
- id: z6.string(),
4973
- prompt: z6.string().optional(),
4974
- name: z6.string().optional(),
4975
- at: z6.string().optional(),
4976
- every: z6.string().optional(),
4977
- cron: z6.string().optional(),
4978
- tz: z6.string().optional(),
4979
- chatId: z6.string().optional().describe(
5099
+ id: z7.string(),
5100
+ prompt: z7.string().optional(),
5101
+ name: z7.string().optional(),
5102
+ at: z7.string().optional(),
5103
+ every: z7.string().optional(),
5104
+ cron: z7.string().optional(),
5105
+ tz: z7.string().optional(),
5106
+ chatId: z7.string().optional().describe(
4980
5107
  'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
4981
5108
  ),
4982
- threadId: z6.string().optional().describe("Legacy alias for chatId."),
4983
- agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional(),
4984
- model: z6.string().optional(),
4985
- enabled: z6.boolean().optional()
5109
+ threadId: z7.string().optional().describe("Legacy alias for chatId."),
5110
+ agent: z7.enum(["claude", "cursor", "codex", "opencode"]).optional(),
5111
+ model: z7.string().optional(),
5112
+ enabled: z7.boolean().optional()
4986
5113
  },
4987
5114
  async (args) => {
4988
5115
  try {
@@ -5023,7 +5150,7 @@ function registerScheduleTools(server) {
5023
5150
  server.tool(
5024
5151
  "delete_schedule",
5025
5152
  "Delete a local schedule. Pass id from list_schedules.",
5026
- { id: z6.string() },
5153
+ { id: z7.string() },
5027
5154
  async ({ id }) => {
5028
5155
  try {
5029
5156
  deleteSchedule(id);
@@ -5036,7 +5163,7 @@ function registerScheduleTools(server) {
5036
5163
  server.tool(
5037
5164
  "run_schedule",
5038
5165
  "Fire a schedule now (does not wait for nextRunAt). Queues the prompt on the target chat or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
5039
- { id: z6.string() },
5166
+ { id: z7.string() },
5040
5167
  async ({ id }) => {
5041
5168
  try {
5042
5169
  if (!getSchedule(id)) {
@@ -5264,7 +5391,7 @@ async function handleJobStop(params, _ctx) {
5264
5391
  return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
5265
5392
  }
5266
5393
  async function defaultTurnWait(ref, timeoutMs) {
5267
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZQU62CES.js");
5394
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-S7TKI6DR.js");
5268
5395
  const orch = getOrchestrator2();
5269
5396
  const thread = await orch.waitForTurn(ref, timeoutMs, {
5270
5397
  resolveIfStillRunning: true
@@ -5678,10 +5805,10 @@ async function startMcpServer() {
5678
5805
  );
5679
5806
  server.tool(
5680
5807
  "list_chats",
5681
- "List chats across all projects (one summary line each \u2014 token-frugal). Includes parent id, last message preview, blocked: when an agent is waiting on a person, and live progress so you can see workspace children. Each line ends with sideboard://chat/<id> \u2014 use that URL in markdown links so the UI can open the chat.",
5808
+ "List live chats across all projects (one summary line each \u2014 token-frugal). Includes parent id, last message preview, blocked: when an agent is waiting on a person, and live progress so you can see workspace children. Each line ends with sideboard://chat/<id> \u2014 use that URL in markdown links so the UI can open the chat. Archived workspaces and agents are list_archive, not this list.",
5682
5809
  {},
5683
5810
  async () => {
5684
- const threads = orch.getThreads(true);
5811
+ const threads = orch.getThreads();
5685
5812
  const lines = threads.map((t) => {
5686
5813
  const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : basename2(t.repoPath) || t.repoPath;
5687
5814
  const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
@@ -5699,18 +5826,18 @@ async function startMcpServer() {
5699
5826
  );
5700
5827
  server.tool(
5701
5828
  "list_board",
5702
- "Home Kanban of workspaces (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chats on that card are separate agents. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_workspace adds a workspace (and a Home card), or returns the live checkout if that ticket/PR/named branch is already checked out. That reuse is one checkout, not one agent \u2014 fork_chat to add another.",
5829
+ "Home Kanban of workspaces (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chats on that card are separate agents. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_workspace adds a workspace (and a Home card), or returns the live checkout if that ticket/PR/named branch is already checked out. That reuse is one checkout, not one agent \u2014 fork_chat to add another.",
5703
5830
  {
5704
- query: z7.string().optional().describe("Case-insensitive token search across title, id, labels, workspace tags, repo"),
5705
- repoPath: z7.string().optional().describe("Limit to one project path from list_projects"),
5706
- kind: z7.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
5707
- ownership: z7.enum(["all", "mine", "reviewing"]).optional().describe(
5831
+ query: z8.string().optional().describe("Case-insensitive token search across title, id, labels, workspace tags, repo"),
5832
+ repoPath: z8.string().optional().describe("Limit to one project path from list_projects"),
5833
+ kind: z8.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
5834
+ ownership: z8.enum(["all", "mine", "reviewing"]).optional().describe(
5708
5835
  "Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
5709
5836
  ),
5710
- column: z7.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
5837
+ column: z8.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
5711
5838
  "Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
5712
5839
  ),
5713
- limit: z7.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
5840
+ limit: z8.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
5714
5841
  },
5715
5842
  async ({ query, repoPath, kind, ownership, column, limit }) => {
5716
5843
  const workspaces = orch.listWorkspaces();
@@ -5740,7 +5867,7 @@ async function startMcpServer() {
5740
5867
  server.tool(
5741
5868
  "get_chat",
5742
5869
  "Get a compact chat summary by id/ref. Includes last message preview, parentChatId, and child workspace chats (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (billed token + costUsd totals when providers reported cost) and lastTurnUsage. blockedReason is why that agent is waiting on a person (question, plan approval, or a reported block).",
5743
- { ref: z7.string() },
5870
+ { ref: z8.string() },
5744
5871
  async ({ ref }) => {
5745
5872
  const t = orch.getThread(ref);
5746
5873
  if (!t) {
@@ -5787,17 +5914,17 @@ async function startMcpServer() {
5787
5914
  "present_artifact",
5788
5915
  "Show a document or live log in Sideboard\u2019s side column. For html/svg/markdown/react, pass the FULL document and do not also fence that same body in chat. For type=log, pass only NEW lines (same artifact_id appends). Prefer type=log for long-running job output \u2014 do not resend HTML. type=react is a single default-export component (JSX/TSX); only react/react-dom imports.",
5789
5916
  {
5790
- title: z7.string().describe("Short title shown in the artifact pane header"),
5791
- type: z7.enum(["html", "svg", "markdown", "react", "log"]).describe(
5917
+ title: z8.string().describe("Short title shown in the artifact pane header"),
5918
+ type: z8.enum(["html", "svg", "markdown", "react", "log"]).describe(
5792
5919
  "html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
5793
5920
  ),
5794
- content: z7.string().describe(
5921
+ content: z8.string().describe(
5795
5922
  "html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
5796
5923
  ),
5797
- artifact_id: z7.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
5798
- status: z7.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
5799
- phase: z7.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
5800
- mode: z7.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
5924
+ artifact_id: z8.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
5925
+ status: z8.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
5926
+ phase: z8.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
5927
+ mode: z8.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
5801
5928
  },
5802
5929
  async ({ title, type, artifact_id, status, phase, mode }) => {
5803
5930
  return mcpJson(
@@ -5810,15 +5937,15 @@ async function startMcpServer() {
5810
5937
  "ask_user",
5811
5938
  "Ask the user a clarifying multiple-choice question in Sideboard\u2019s composer. Call only when work is blocked on choosing among a few concrete options (approach fork, which API, auth vs cookies). Do not call for greetings, check-ins, \u201Chello\u201D, open-ended how-can-I-help, or to invent a menu of possible next tasks \u2014 reply in chat instead. If one option is the obvious default, proceed without asking. Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. After calling, stop and wait for answers. Not for \u201Cis the plan ready?\u201D.",
5812
5939
  {
5813
- questions: z7.array(
5814
- z7.object({
5815
- question: z7.string().describe("Full question text ending with ?"),
5816
- header: z7.string().max(24).optional().describe("Short label shown above the question"),
5817
- multiSelect: z7.boolean().optional().describe("Allow selecting multiple options"),
5818
- options: z7.array(
5819
- z7.object({
5820
- label: z7.string(),
5821
- description: z7.string().optional().describe("What this option means / when to choose it (strongly preferred)")
5940
+ questions: z8.array(
5941
+ z8.object({
5942
+ question: z8.string().describe("Full question text ending with ?"),
5943
+ header: z8.string().max(24).optional().describe("Short label shown above the question"),
5944
+ multiSelect: z8.boolean().optional().describe("Allow selecting multiple options"),
5945
+ options: z8.array(
5946
+ z8.object({
5947
+ label: z8.string(),
5948
+ description: z8.string().optional().describe("What this option means / when to choose it (strongly preferred)")
5822
5949
  })
5823
5950
  ).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
5824
5951
  })
@@ -5844,9 +5971,9 @@ async function startMcpServer() {
5844
5971
  "present_plan",
5845
5972
  PRESENT_PLAN_TOOL_DESCRIPTION,
5846
5973
  {
5847
- title: z7.string().optional().describe("Short plan title (defaults to Plan)"),
5848
- content: z7.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
5849
- thread_id: z7.string().optional().describe("Sideboard thread id when cwd is not the worktree")
5974
+ title: z8.string().optional().describe("Short plan title (defaults to Plan)"),
5975
+ content: z8.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
5976
+ thread_id: z8.string().optional().describe("Sideboard thread id when cwd is not the worktree")
5850
5977
  },
5851
5978
  async ({ title, content, thread_id }) => {
5852
5979
  const { writePlanFile: writePlanFile2 } = await import("./plan-file-7MMIPV7Y.js");
@@ -5875,15 +6002,15 @@ async function startMcpServer() {
5875
6002
  "present_schema",
5876
6003
  "Open Sideboard\u2019s schema-driven side column (filterable table and/or form) when the user needs to filter, edit, publish, or persist records. Do not call this just to re-display rows you already wrote as a markdown table. If the user asks for an editable / interactive table, call this even if chat already showed those rows. Pass JSON Schema + optional schemaUi. Prefer datasource=inline with embedded resource/records. Use datasource=brightsy with resource_id only when the user is logged into Brightsy.",
5877
6004
  {
5878
- title: z7.string().describe("Pane title"),
5879
- mode: z7.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
5880
- datasource: z7.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
5881
- resource_id: z7.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
5882
- record_id: z7.string().optional().describe("Record id when opening form mode"),
5883
- resource: z7.record(z7.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
5884
- record: z7.record(z7.unknown()).optional().describe("Inline record { id, data, published_at? }"),
5885
- records: z7.array(z7.record(z7.unknown())).optional().describe("Inline records for table mode"),
5886
- pane_id: z7.string().optional().describe("Stable pane id across updates")
6005
+ title: z8.string().describe("Pane title"),
6006
+ mode: z8.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
6007
+ datasource: z8.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
6008
+ resource_id: z8.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
6009
+ record_id: z8.string().optional().describe("Record id when opening form mode"),
6010
+ resource: z8.record(z8.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
6011
+ record: z8.record(z8.unknown()).optional().describe("Inline record { id, data, published_at? }"),
6012
+ records: z8.array(z8.record(z8.unknown())).optional().describe("Inline records for table mode"),
6013
+ pane_id: z8.string().optional().describe("Stable pane id across updates")
5887
6014
  },
5888
6015
  async (args) => {
5889
6016
  const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
@@ -5904,10 +6031,10 @@ async function startMcpServer() {
5904
6031
  "present_files",
5905
6032
  "Open Sideboard\u2019s Files column (CMS-style file manager: browse, upload, pick). Use datasource=brightsy when the user is logged into Brightsy account storage; datasource=memory for a session-local demo store. Prefer this over claiming a file manager UI is unavailable. Pair with present_schema when editing records that need media.",
5906
6033
  {
5907
- title: z7.string().optional().describe("Pane title (default: Files)"),
5908
- datasource: z7.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
5909
- path: z7.string().optional().describe("Initial folder path (e.g. public)"),
5910
- pane_id: z7.string().optional().describe("Stable pane id across updates")
6034
+ title: z8.string().optional().describe("Pane title (default: Files)"),
6035
+ datasource: z8.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
6036
+ path: z8.string().optional().describe("Initial folder path (e.g. public)"),
6037
+ pane_id: z8.string().optional().describe("Stable pane id across updates")
5911
6038
  },
5912
6039
  async (args) => {
5913
6040
  const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
@@ -5927,8 +6054,8 @@ async function startMcpServer() {
5927
6054
  "wait_for_job",
5928
6055
  "Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. MCP clients kill tools around 60s, so this returns within 45s. Sideboard opens a type=log pane from this result (artifact_id = job id, content = delta). stillRunning is the source of truth \u2014 if true, call wait_for_job again. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
5929
6056
  {
5930
- id: z7.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
5931
- timeoutMs: z7.number().optional()
6057
+ id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
6058
+ timeoutMs: z8.number().optional()
5932
6059
  },
5933
6060
  async ({ id, timeoutMs }) => {
5934
6061
  const result = await waitForDetachedJob(process.cwd(), id, {
@@ -5941,8 +6068,8 @@ async function startMcpServer() {
5941
6068
  "stop_job",
5942
6069
  "Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. The type=log pane updates to status=failed with the last delta. Then decide the next step.",
5943
6070
  {
5944
- id: z7.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
5945
- reason: z7.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
6071
+ id: z8.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
6072
+ reason: z8.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
5946
6073
  },
5947
6074
  async ({ id, reason }) => {
5948
6075
  const result = await stopDetachedJob(process.cwd(), id, { reason });
@@ -5954,7 +6081,7 @@ async function startMcpServer() {
5954
6081
  "list_run_scripts",
5955
6082
  "List .sideboard/.conductor run scripts for this thread (same menu as the desktop Dev button) and which are active. Each script and active run includes preview: url (open http://localhost:<SIDEBOARD_PORT>) or window (Electron: native window is the app; url is renderer/HMR only). Never guess :3000. Worktree turns may omit ref (uses cwd).",
5956
6083
  {
5957
- ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd).")
6084
+ ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd).")
5958
6085
  },
5959
6086
  async ({ ref }) => {
5960
6087
  try {
@@ -5984,8 +6111,8 @@ async function startMcpServer() {
5984
6111
  "run_dev_script",
5985
6112
  "Start a .sideboard/.conductor run script (same as the desktop Dev / play button). Several named scripts can run at once; each gets its own port, and starting one does not stop the others. Logs and Stop appear in the thread UI \u2014 do not detached-job or shell-spawn the same command. Returns {port, ports, url, preview}. preview=url \u2192 open url (browser, curl, Playwright). preview=window \u2192 Electron: the native window is the app; url is renderer/HMR only (no preload/IPC in a browser). The port is allocated SIDEBOARD_PORT, not 3000. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
5986
6113
  {
5987
- ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
5988
- name: z7.string().optional().describe("Script name from list_run_scripts (default script if omitted).")
6114
+ ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
6115
+ name: z8.string().optional().describe("Script name from list_run_scripts (default script if omitted).")
5989
6116
  },
5990
6117
  async ({ ref, name }) => {
5991
6118
  try {
@@ -6013,8 +6140,8 @@ async function startMcpServer() {
6013
6140
  "stop_dev_script",
6014
6141
  "Stop a run script started with run_dev_script or the desktop Dev button (same as UI Stop). Omit name to stop all scripts on this worktree. Worktree turns may omit ref (uses cwd).",
6015
6142
  {
6016
- ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
6017
- name: z7.string().optional().describe("Script name to stop; omit to stop all active scripts.")
6143
+ ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
6144
+ name: z8.string().optional().describe("Script name to stop; omit to stop all active scripts.")
6018
6145
  },
6019
6146
  async ({ ref, name }) => {
6020
6147
  try {
@@ -6031,9 +6158,9 @@ async function startMcpServer() {
6031
6158
  "get_run_log",
6032
6159
  "Read the Run-tab terminal for a worktree script (same stdout/stderr as the desktop Dev pane). Default is a tail \u2014 raise `tail` only if the error is missing. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
6033
6160
  {
6034
- ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
6035
- name: z7.string().optional().describe("Script name from list_run_scripts (default script if omitted)."),
6036
- tail: z7.number().int().min(1).max(MAX_RUN_LOG_CHARS).optional().describe(
6161
+ ref: z8.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
6162
+ name: z8.string().optional().describe("Script name from list_run_scripts (default script if omitted)."),
6163
+ tail: z8.number().int().min(1).max(MAX_RUN_LOG_CHARS).optional().describe(
6037
6164
  `Max characters to return from the end (default ${DEFAULT_RUN_LOG_TAIL_CHARS}). Do not request the full buffer unless the tail is truncated and you still need more.`
6038
6165
  )
6039
6166
  },
@@ -6067,12 +6194,37 @@ async function startMcpServer() {
6067
6194
  }
6068
6195
  );
6069
6196
  if (worktreeProfile) {
6197
+ server.tool(
6198
+ "get_orchestrator",
6199
+ "The orchestration chat this agent is connected to \u2014 the parent that lists this workspace among its agents. Returns id, title, status, agent, and a sideboard://chat link. connected is false when this chat was not opened by an orchestrator. notify_orchestrator wakes that chat. You cannot list or steer its other agents from here.",
6200
+ {},
6201
+ async () => {
6202
+ try {
6203
+ const child = resolveNotifyCallerThread();
6204
+ const parent = connectedOrchestrator(child, (id) => orch.getThread(id));
6205
+ if (!parent) return mcpJson({ connected: false, orchestrator: null });
6206
+ return mcpJson({
6207
+ connected: true,
6208
+ orchestrator: {
6209
+ id: parent.id,
6210
+ title: parent.title,
6211
+ status: parent.status,
6212
+ agent: parent.agent,
6213
+ link: `sideboard://chat/${parent.id}`
6214
+ }
6215
+ });
6216
+ } catch (err) {
6217
+ const text5 = err instanceof Error ? err.message : String(err);
6218
+ return { content: [{ type: "text", text: text5 }], isError: true };
6219
+ }
6220
+ }
6221
+ );
6070
6222
  server.tool(
6071
6223
  "notify_orchestrator",
6072
6224
  "Wake the parent Global orchestrator with a short status so it does not have to poll wait_for_turn. Use when you are blocked and the parent may have moved on (missing access, waiting on something the coordinator must handle). The message is shown on the sidebar and board until the next user message in this chat. ask_user already notifies for input-required \u2014 do not call this for the same question. Information only: never a git command. Do not use this to steer other worktrees.",
6073
6225
  {
6074
- message: z7.string().min(1).max(1500).describe("Why you are blocked / what the parent should know (short)."),
6075
- reason: z7.enum(["input-required", "blocked"]).optional().describe(
6226
+ message: z8.string().min(1).max(1500).describe("Why you are blocked / what the parent should know (short)."),
6227
+ reason: z8.enum(["input-required", "blocked"]).optional().describe(
6076
6228
  "input-required = waiting on the user in this chat; blocked = need the coordinator (default)."
6077
6229
  )
6078
6230
  },
@@ -6109,11 +6261,11 @@ async function startMcpServer() {
6109
6261
  "set_workspace_tags",
6110
6262
  worktreeProfile ? "Update tags on this workspace (the checkout you are in). Omit ref to use this chat. mode=replace (default), add, or remove. Short labels \u2014 the same ones the orchestrator used for this task. You cannot retag a different checkout." : "Label a project workspace so related checkouts stay recognizable in the sidebar and on the phone. Pass any chat id on that workspace. Use the same tags on every workspace from one task. mode=replace is the default; add and remove change the set. create_workspace tags= sets them at create time. Not for orchestration chats.",
6111
6263
  {
6112
- ref: z7.string().optional().describe(
6264
+ ref: z8.string().optional().describe(
6113
6265
  worktreeProfile ? "Omit for this chat. Another chat id only if it is on this same checkout." : "Chat id on the workspace to label."
6114
6266
  ),
6115
- tags: z7.union([z7.string(), z7.array(z7.string())]).describe("Labels to set. A string may be comma-separated."),
6116
- mode: z7.enum(["replace", "add", "remove"]).optional()
6267
+ tags: z8.union([z8.string(), z8.array(z8.string())]).describe("Labels to set. A string may be comma-separated."),
6268
+ mode: z8.enum(["replace", "add", "remove"]).optional()
6117
6269
  },
6118
6270
  async ({ ref, tags, mode }) => {
6119
6271
  try {
@@ -6147,7 +6299,7 @@ async function startMcpServer() {
6147
6299
  "set_caffeinate",
6148
6300
  "Keep this Mac awake with caffeinate (like Claude Code) across turns \u2014 independent of Settings toggles. Turn ON when the user will be away from the keyboard, is driving work from Slack, or asks you to keep the machine awake. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the Mac awake. Closing or archiving this orchestration chat also releases the hold. macOS only.",
6149
6301
  {
6150
- enabled: z7.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
6302
+ enabled: z8.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
6151
6303
  },
6152
6304
  async ({ enabled }) => {
6153
6305
  const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
@@ -6173,24 +6325,24 @@ async function startMcpServer() {
6173
6325
  "create_workspace",
6174
6326
  `Create a workspace (isolated git checkout + first chat) from branch, pr, or ticket. A ticket, PR, or named branch has one checkout \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. That is not one agent: chats lists every agent on that checkout. Add another with fork_chat; send_to_chat only the chat that should do this job. Creating from the default branch still opens a new isolated checkout. Pass repoPath from list_projects. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentChatId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Omit agent/model \u2014 Sideboard applies ${accountDefaultsHint}. Do not pass your own agent or agent=cursor; those are ignored. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Pass the same tags on every workspace you open for one task (short labels such as "phone-sync") so they still match in the sidebar the next day. Reusing a checkout adds those tags.`,
6175
6327
  {
6176
- sourceType: z7.enum(["branch", "pr", "ticket"]),
6177
- sourceRef: z7.string(),
6178
- agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(
6328
+ sourceType: z8.enum(["branch", "pr", "ticket"]),
6329
+ sourceRef: z8.string(),
6330
+ agent: z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(
6179
6331
  `Usually omit. Sideboard uses Account default agent (${accountDefaults.agent}). Passing your own agent or cursor is ignored.`
6180
6332
  ),
6181
- model: z7.string().nullable().optional().describe(
6333
+ model: z8.string().nullable().optional().describe(
6182
6334
  `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
6183
6335
  ),
6184
- repoPath: z7.string(),
6185
- title: z7.string().optional(),
6186
- cowboy: z7.boolean().optional().describe(
6336
+ repoPath: z8.string(),
6337
+ title: z8.string().optional(),
6338
+ cowboy: z8.boolean().optional().describe(
6187
6339
  "If true, work in the project folder on the default branch (no thread/* worktree). Requires Settings \u2192 Advanced \u2192 Cowboy mode. Land is commit+push to that branch. Archive does not delete the folder."
6188
6340
  ),
6189
- parentChatId: z7.string().optional().describe(
6341
+ parentChatId: z8.string().optional().describe(
6190
6342
  "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
6191
6343
  ),
6192
- parentThreadId: z7.string().optional().describe("Legacy alias for parentChatId."),
6193
- tags: z7.union([z7.string(), z7.array(z7.string())]).optional().describe(
6344
+ parentThreadId: z8.string().optional().describe("Legacy alias for parentChatId."),
6345
+ tags: z8.union([z8.string(), z8.array(z8.string())]).optional().describe(
6194
6346
  "Short labels for this workspace (or a comma-separated string). Use the same tags on every checkout for one task. Added when the checkout already exists."
6195
6347
  )
6196
6348
  },
@@ -6206,11 +6358,11 @@ async function startMcpServer() {
6206
6358
  "start_board_card",
6207
6359
  "Same as create_workspace for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second checkout when one already matches \u2014 returns that workspace (alreadyStarted) and its chats. A workspace can have many agents. fork_chat to add one; send_to_chat only the chat that should do this job.",
6208
6360
  {
6209
- kind: z7.enum(["ticket", "pr", "branch"]),
6210
- ref: z7.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
6211
- repoPath: z7.string().describe("Project path from list_projects / list_board"),
6212
- title: z7.string().optional(),
6213
- tags: z7.union([z7.string(), z7.array(z7.string())]).optional().describe(
6361
+ kind: z8.enum(["ticket", "pr", "branch"]),
6362
+ ref: z8.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
6363
+ repoPath: z8.string().describe("Project path from list_projects / list_board"),
6364
+ title: z8.string().optional(),
6365
+ tags: z8.union([z8.string(), z8.array(z8.string())]).optional().describe(
6214
6366
  "Short labels for this workspace. Same tags on every checkout for one task. Added when the checkout already exists."
6215
6367
  )
6216
6368
  },
@@ -6315,9 +6467,9 @@ async function startMcpServer() {
6315
6467
  "send_to_chat",
6316
6468
  'Steer a prompt on a workspace chat (Settings \u2192 Follow-up, default steer: interrupt the in-flight turn and start now, same as the workspace composer). Use after create_workspace to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_chat to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
6317
6469
  {
6318
- ref: z7.string(),
6319
- prompt: z7.string(),
6320
- force_stop: z7.boolean().optional()
6470
+ ref: z8.string(),
6471
+ prompt: z8.string(),
6472
+ force_stop: z8.boolean().optional()
6321
6473
  },
6322
6474
  async ({ ref, prompt, force_stop }) => {
6323
6475
  if (force_stop) {
@@ -6342,8 +6494,8 @@ async function startMcpServer() {
6342
6494
  "wait_for_turn",
6343
6495
  "Wait until the chat finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. taskState is the A2A-style lifecycle: submitted (queued, not started), working, input-required (ask_user), completed, failed, canceled. stillRunning is true only for submitted/working. blockedReason is why the child is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. If stillRunning, text and usage are empty (they would be the previous turn) \u2014 read progress and call wait_for_turn again. Do not send_to_chat a check-in (that steers / interrupts). On failed, lastError/text is the failure. On canceled, the child did not finish \u2014 resume with send_to_chat or tell the user. On input-required, wait for the user in that chat. When finished, text is this turn\u2019s assistant reply and usage is that turn\u2019s tokens + costUsd (this turn, not the Claude session total; sessionCostUsd is the provider session total when present).",
6344
6496
  {
6345
- ref: z7.string(),
6346
- timeoutMs: z7.number().optional()
6497
+ ref: z8.string(),
6498
+ timeoutMs: z8.number().optional()
6347
6499
  },
6348
6500
  async ({ ref, timeoutMs }) => {
6349
6501
  const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
@@ -6371,7 +6523,7 @@ async function startMcpServer() {
6371
6523
  server.tool(
6372
6524
  "get_turn_result",
6373
6525
  "Assistant message and last-turn usage when the turn finished. While stillRunning (submitted/working), text and usage are empty so a previous reply cannot be mistaken for this turn \u2014 use progress for tools/thinking. Not the full transcript. Includes taskState (A2A lifecycle) and blockedReason (why the agent is waiting on a person).",
6374
- { ref: z7.string() },
6526
+ { ref: z8.string() },
6375
6527
  async ({ ref }) => {
6376
6528
  const result = orch.getTurnResult(ref);
6377
6529
  const hint = mcpWaitTaskHint(result.taskState, result.status, result.blockedReason);
@@ -6387,9 +6539,9 @@ async function startMcpServer() {
6387
6539
  "stop_chat",
6388
6540
  "Force-stop a thread: kill any in-flight agent turn AND clear queued prompts so drainQueue cannot continue. Does not archive the worktree. Optional force defaults to true. Pass reason \u2014 a short note on why this chat is stopping. It is stored on the thread and kept when the turn unwinds. If you omit it, Sideboard still records that the queue was cleared.",
6389
6541
  {
6390
- ref: z7.string(),
6391
- force: z7.boolean().optional(),
6392
- reason: z7.string().optional().describe("Why this chat is stopping. Stored on the thread for the next session.")
6542
+ ref: z8.string(),
6543
+ force: z8.boolean().optional(),
6544
+ reason: z8.string().optional().describe("Why this chat is stopping. Stored on the thread for the next session.")
6393
6545
  },
6394
6546
  async ({ ref, force, reason }) => {
6395
6547
  const t = orch.getThread(ref);
@@ -6410,56 +6562,13 @@ async function startMcpServer() {
6410
6562
  });
6411
6563
  }
6412
6564
  );
6413
- server.tool(
6414
- "archive_chat",
6415
- "Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, and open PRs by asking the worktree agent (ask_git). Merge only when the user explicitly asked.",
6416
- { ref: z7.string() },
6417
- async ({ ref }) => {
6418
- const t = orch.getThread(ref);
6419
- if (!t) {
6420
- return {
6421
- content: [{ type: "text", text: `Chat not found: ${ref}` }],
6422
- isError: true
6423
- };
6424
- }
6425
- const blocked = mcpArchiveBlockedReason(t);
6426
- if (blocked) {
6427
- return {
6428
- content: [{ type: "text", text: blocked }],
6429
- isError: true
6430
- };
6431
- }
6432
- const archived = await orch.archive(ref);
6433
- return mcpJson({
6434
- id: archived.id,
6435
- status: archived.status
6436
- });
6437
- }
6438
- );
6439
- server.tool(
6440
- "restore_chat",
6441
- "Restore an archived thread (recreates worktree from branch when needed)",
6442
- { ref: z7.string() },
6443
- async ({ ref }) => {
6444
- try {
6445
- const restored = await orch.restore(ref);
6446
- return mcpJson({
6447
- id: restored.id,
6448
- status: restored.status,
6449
- worktreePath: restored.worktreePath
6450
- });
6451
- } catch (err) {
6452
- const message = err instanceof Error ? err.message : String(err);
6453
- return { content: [{ type: "text", text: message }], isError: true };
6454
- }
6455
- }
6456
- );
6565
+ registerArchiveTools(server, orch);
6457
6566
  server.tool(
6458
6567
  "get_diff",
6459
6568
  "Compact diff summary (capped hunks, paginated)",
6460
6569
  {
6461
- ref: z7.string(),
6462
- maxFiles: z7.number().optional()
6570
+ ref: z8.string(),
6571
+ maxFiles: z8.number().optional()
6463
6572
  },
6464
6573
  async ({ ref, maxFiles }) => {
6465
6574
  const summary = await orch.diffSummary(ref);
@@ -6472,7 +6581,7 @@ async function startMcpServer() {
6472
6581
  server.tool(
6473
6582
  "get_pr_checks",
6474
6583
  "Snapshot of GitHub PR checks for a worktree thread (`gh pr checks` plus merge/review gates). null = no PR. If the user gave a goal (Greptile 5/5, CI green), the worktree agent watches with `gh pr checks --watch` via /long-running \u2014 this tool is a snapshot, not a waiter. Coordinators: do not run gh from the orchestration cwd.",
6475
- { ref: z7.string().describe("Worktree thread id/ref") },
6584
+ { ref: z8.string().describe("Worktree thread id/ref") },
6476
6585
  async ({ ref }) => {
6477
6586
  try {
6478
6587
  const checks = await orch.getPrChecks(ref);
@@ -6486,7 +6595,7 @@ async function startMcpServer() {
6486
6595
  server.tool(
6487
6596
  "request_review",
6488
6597
  'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). If that worktree has a single unused idle agent (no messages), runs the review there; otherwise opens a new Review chat tab. Attaches .claude/skills/review/SKILL.md when present (else copies .sideboard/review.md into .context/review.md, or seeds that file from the stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information in that chat. The review stays there so the user can read it and type next steps \u2014 do not comment on or update the PR or ticket, and do not ask_user after the review, until they ask. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review thread id.',
6489
- { ref: z7.string().describe("Worktree thread id/ref to review") },
6598
+ { ref: z8.string().describe("Worktree thread id/ref to review") },
6490
6599
  async ({ ref }) => {
6491
6600
  try {
6492
6601
  const tab = await orch.requestReview(ref);
@@ -6508,8 +6617,8 @@ async function startMcpServer() {
6508
6617
  "ask_git",
6509
6618
  "Commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same prompts as the desktop git buttons (including Resolve). Always steers the worktree agent; does not push or open the PR locally. Then wait_for_turn (loop while stillRunning). Do not start a checks loop on a plain push \u2014 only if the user gave a goal (Greptile 5/5, CI green). Pass a worktree thread ref (not the orchestrator). action=ready-for-review and action=merge only when the user explicitly asked (or clicked the desktop button). Do not run git or gh from the orchestration cwd.",
6510
6619
  {
6511
- ref: z7.string().describe("Worktree thread id/ref"),
6512
- action: z7.enum(AGENT_GIT_ACTIONS).describe(
6620
+ ref: z8.string().describe("Worktree thread id/ref"),
6621
+ action: z8.enum(AGENT_GIT_ACTIONS).describe(
6513
6622
  "commit-push | create-draft | create-web | resolve-conflicts | ready-for-review | merge"
6514
6623
  )
6515
6624
  },
@@ -6538,7 +6647,7 @@ async function startMcpServer() {
6538
6647
  }
6539
6648
  }
6540
6649
  );
6541
- const agentEnum = z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
6650
+ const agentEnum = z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
6542
6651
  server.tool(
6543
6652
  "list_models",
6544
6653
  "List models for an agent. Prefer Auto: do not call this unless you have a reason to pin a specific model (user request, cost/latency, capability). Omit agent to list all.",
@@ -6559,11 +6668,11 @@ async function startMcpServer() {
6559
6668
  "fork_workspace",
6560
6669
  "Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id.",
6561
6670
  {
6562
- ref: z7.string().describe("Worktree thread id/ref to fork"),
6563
- through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6671
+ ref: z8.string().describe("Worktree thread id/ref to fork"),
6672
+ through_index: z8.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6564
6673
  agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6565
- model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6566
- title: z7.string().optional()
6674
+ model: z8.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6675
+ title: z8.string().optional()
6567
6676
  },
6568
6677
  async ({ ref, through_index, agent, model, title }) => {
6569
6678
  try {
@@ -6597,11 +6706,11 @@ async function startMcpServer() {
6597
6706
  "fork_chat",
6598
6707
  "Add another agent chat on the SAME workspace (same files, Run, and git). Also forks a Global orchestration chat into a new orchestration tab. Pass agent when the new chat should use a different harness. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Use this for a second agent on a checkout (review, another harness, parallel work on the same branch). Do not create_workspace again for that \u2014 it returns the existing checkout. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id. Use fork_workspace only when the work needs its own git checkout.",
6599
6708
  {
6600
- ref: z7.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
6601
- through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6709
+ ref: z8.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
6710
+ through_index: z8.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6602
6711
  agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6603
- model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6604
- title: z7.string().optional()
6712
+ model: z8.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6713
+ title: z8.string().optional()
6605
6714
  },
6606
6715
  async ({ ref, through_index, agent, model, title }) => {
6607
6716
  try {
@@ -6639,7 +6748,7 @@ async function startMcpServer() {
6639
6748
  server.tool(
6640
6749
  "run_setup",
6641
6750
  "Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
6642
- { ref: z7.string() },
6751
+ { ref: z8.string() },
6643
6752
  async ({ ref }) => {
6644
6753
  const result = await orch.runSetup(ref);
6645
6754
  return mcpJson(result);
@@ -6648,7 +6757,7 @@ async function startMcpServer() {
6648
6757
  server.tool(
6649
6758
  "add_project",
6650
6759
  "Register a git repo as a Sideboard workspace",
6651
- { repoPath: z7.string() },
6760
+ { repoPath: z8.string() },
6652
6761
  async ({ repoPath }) => {
6653
6762
  const ws = await orch.addWorkspace(repoPath);
6654
6763
  return mcpJson(ws);
@@ -6657,7 +6766,7 @@ async function startMcpServer() {
6657
6766
  server.tool(
6658
6767
  "remove_project",
6659
6768
  "Unregister a Sideboard workspace and archive its chats",
6660
- { repoPath: z7.string() },
6769
+ { repoPath: z8.string() },
6661
6770
  async ({ repoPath }) => {
6662
6771
  await orch.removeWorkspace(repoPath);
6663
6772
  return { content: [{ type: "text", text: "ok" }] };
@@ -6667,12 +6776,12 @@ async function startMcpServer() {
6667
6776
  "fanout",
6668
6777
  "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
6669
6778
  {
6670
- prompt: z7.string(),
6671
- agents: z7.array(z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
6672
- repoPath: z7.string(),
6673
- sourceType: z7.enum(["branch", "pr", "ticket"]).optional(),
6674
- sourceRef: z7.string().optional(),
6675
- title: z7.string().optional()
6779
+ prompt: z8.string(),
6780
+ agents: z8.array(z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
6781
+ repoPath: z8.string(),
6782
+ sourceType: z8.enum(["branch", "pr", "ticket"]).optional(),
6783
+ sourceRef: z8.string().optional(),
6784
+ title: z8.string().optional()
6676
6785
  },
6677
6786
  async (args) => {
6678
6787
  const threads = await orch.bestOfN(args);
@@ -6690,8 +6799,8 @@ async function startMcpServer() {
6690
6799
  "list_branches",
6691
6800
  "List git branches in a registered workspace. Pass repoPath from list_projects (unmerged into the default branch by default \u2014 for create_workspace sourceType=branch).",
6692
6801
  {
6693
- repoPath: z7.string(),
6694
- unmergedOnly: z7.boolean().optional()
6802
+ repoPath: z8.string(),
6803
+ unmergedOnly: z8.boolean().optional()
6695
6804
  },
6696
6805
  async ({ repoPath, unmergedOnly }) => {
6697
6806
  const root = await resolveRepoRoot(repoPath);
@@ -6712,19 +6821,19 @@ async function startMcpServer() {
6712
6821
  "list_prs",
6713
6822
  `List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_projects. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled with this project's ready-for-review GitHub label (Settings \u2192 Projects; default eng-review) with no individual user reviewer. Prefer teams that match Settings \u2192 Agents / Projects roles for this repo. A team like engineering-team is not a claim (you are on that team). Also: state (open|closed|merged|all|review), label, reviewer (me|unassigned|login), query, limit (default 40, max 250). Then create_workspace with sourceType=pr.`,
6714
6823
  {
6715
- repoPath: z7.string(),
6716
- query: z7.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
6717
- queue: z7.enum(["review", "mine", "approved", "changes"]).optional().describe(
6824
+ repoPath: z8.string(),
6825
+ query: z8.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
6826
+ queue: z8.enum(["review", "mine", "approved", "changes"]).optional().describe(
6718
6827
  "review = unclaimed ready-for-review inbox (Settings \u2192 Projects label, default eng-review). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes."
6719
6828
  ),
6720
- state: z7.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
6721
- label: z7.string().optional().describe(
6829
+ state: z8.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
6830
+ label: z8.string().optional().describe(
6722
6831
  "GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
6723
6832
  ),
6724
- reviewer: z7.string().optional().describe(
6833
+ reviewer: z8.string().optional().describe(
6725
6834
  "me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
6726
6835
  ),
6727
- limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
6836
+ limit: z8.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
6728
6837
  },
6729
6838
  async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
6730
6839
  const root = await resolveRepoRoot(repoPath);
@@ -6757,7 +6866,7 @@ async function startMcpServer() {
6757
6866
  server.tool(
6758
6867
  "get_pr_stack",
6759
6868
  "Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs (and only merge when the user explicitly asked).",
6760
- { ref: z7.string() },
6869
+ { ref: z8.string() },
6761
6870
  async ({ ref }) => {
6762
6871
  const stack = await orch.getPrStack(ref);
6763
6872
  return mcpJson(stack);
@@ -6767,8 +6876,8 @@ async function startMcpServer() {
6767
6876
  "open_pr_stack_layers",
6768
6877
  "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
6769
6878
  {
6770
- ref: z7.string(),
6771
- layer: z7.number().int().positive().optional()
6879
+ ref: z8.string(),
6880
+ layer: z8.number().int().positive().optional()
6772
6881
  },
6773
6882
  async ({ ref, layer }) => {
6774
6883
  const result = await orch.openPrStackLayers(ref, { layer });
@@ -6791,9 +6900,9 @@ async function startMcpServer() {
6791
6900
  "add_stack_layer",
6792
6901
  "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
6793
6902
  {
6794
- ref: z7.string(),
6795
- branchName: z7.string(),
6796
- title: z7.string().optional()
6903
+ ref: z8.string(),
6904
+ branchName: z8.string(),
6905
+ title: z8.string().optional()
6797
6906
  },
6798
6907
  async ({ ref, branchName, title }) => {
6799
6908
  const result = await orch.addStackLayer(ref, branchName, { title });
@@ -6811,11 +6920,11 @@ async function startMcpServer() {
6811
6920
  "create_pr_stack",
6812
6921
  "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
6813
6922
  {
6814
- repoPath: z7.string(),
6815
- branches: z7.array(z7.string()).min(1),
6816
- agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
6817
- base: z7.string().optional(),
6818
- title: z7.string().optional()
6923
+ repoPath: z8.string(),
6924
+ branches: z8.array(z8.string()).min(1),
6925
+ agent: z8.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
6926
+ base: z8.string().optional(),
6927
+ title: z8.string().optional()
6819
6928
  },
6820
6929
  async (args) => {
6821
6930
  const result = await orch.createPrStack({
@@ -6843,10 +6952,10 @@ async function startMcpServer() {
6843
6952
  "list_issues",
6844
6953
  "List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then create_workspace with sourceType=ticket.",
6845
6954
  {
6846
- repoPath: z7.string(),
6847
- query: z7.string().optional().describe("Search title, identifier, or description"),
6848
- assignee: z7.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
6849
- limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true."),
6955
+ repoPath: z8.string(),
6956
+ query: z8.string().optional().describe("Search title, identifier, or description"),
6957
+ assignee: z8.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
6958
+ limit: z8.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true."),
6850
6959
  updatedSince: mcpIssueUpdatedSinceSchema
6851
6960
  },
6852
6961
  async ({ repoPath, query, assignee, limit, updatedSince }) => {
@@ -10173,16 +10282,20 @@ function historyCursor(chat) {
10173
10282
  function listPhoneHistory(opts) {
10174
10283
  const query = opts?.query?.trim().toLowerCase() ?? "";
10175
10284
  const after = opts?.after?.trim() ?? "";
10176
- const all = listThreads({ includeArchived: true }).filter((thread) => thread.status === "archived").filter((thread) => isGlobalThread(thread) || isProjectPath(thread.repoPath)).sort((a, b) => historyStamp(a) < historyStamp(b) ? 1 : historyStamp(a) > historyStamp(b) ? -1 : 0).map((thread) => ({
10177
- id: thread.id,
10178
- title: thread.title?.trim() || "Untitled",
10179
- preview: previewOf(thread),
10180
- updatedAt: historyStamp(thread),
10181
- where: isGlobalThread(thread) ? "Orchestration" : repoName(thread.repoPath),
10182
- agent: thread.agent
10183
- })).filter((chat) => {
10285
+ const all = listThreads({ includeArchived: true }).filter((thread) => thread.status === "archived").filter((thread) => isGlobalThread(thread) || isProjectPath(thread.repoPath)).sort((a, b) => historyStamp(a) < historyStamp(b) ? 1 : historyStamp(a) > historyStamp(b) ? -1 : 0).map((thread) => {
10286
+ const workspace = workspaceNameLabel(thread);
10287
+ return {
10288
+ id: thread.id,
10289
+ title: thread.title?.trim() || "Untitled",
10290
+ preview: previewOf(thread),
10291
+ updatedAt: historyStamp(thread),
10292
+ where: isGlobalThread(thread) ? "Orchestration" : repoName(thread.repoPath),
10293
+ agent: thread.agent,
10294
+ ...workspace ? { workspace } : {}
10295
+ };
10296
+ }).filter((chat) => {
10184
10297
  if (!query) return true;
10185
- return `${chat.title} ${chat.where} ${chat.agent} ${chat.preview}`.toLowerCase().includes(query);
10298
+ return `${chat.title} ${chat.where} ${chat.agent} ${chat.workspace ?? ""} ${chat.preview}`.toLowerCase().includes(query);
10186
10299
  });
10187
10300
  let start = 0;
10188
10301
  if (after) {
@@ -13416,7 +13529,7 @@ async function continueSourceThread(threadId, prompt) {
13416
13529
  await continueOnReply(threadId, prompt);
13417
13530
  return;
13418
13531
  }
13419
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZQU62CES.js");
13532
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-S7TKI6DR.js");
13420
13533
  await getOrchestrator2().send(threadId, prompt);
13421
13534
  } catch {
13422
13535
  }
@@ -13971,6 +14084,7 @@ export {
13971
14084
  connectOptionalService,
13972
14085
  connectSlackToken,
13973
14086
  connectedOptionalServices,
14087
+ connectedOrchestratorTurnReminder,
13974
14088
  connectedPrSelectors,
13975
14089
  consumeComposerAttachments,
13976
14090
  contextTokens,
@@ -14094,6 +14208,7 @@ export {
14094
14208
  formatClaudeUsageResetClock,
14095
14209
  formatClaudeUsageTooltip,
14096
14210
  formatCloudTurnReply,
14211
+ formatConnectedOrchestratorReminder,
14097
14212
  formatDetachedJobInvoke,
14098
14213
  formatDevAccessHint,
14099
14214
  formatFetchError,