@sideboard-ai/core 0.1.251 → 0.1.253

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 (27) hide show
  1. package/dist/{agents-FY2DFOMV.js → agents-6HDC7UA7.js} +3 -3
  2. package/dist/{agents-PU2P5VWM.js → agents-RKZ2NBIV.js} +3 -3
  3. package/dist/{chunk-6Z66OYYO.js → chunk-46KDLP6T.js} +50 -26
  4. package/dist/{chunk-GNO7RXNH.js → chunk-6ZYSXLXK.js} +37 -14
  5. package/dist/{chunk-2LZ3CNTN.js → chunk-GZRS5QVL.js} +52 -27
  6. package/dist/{chunk-PL23EQ3S.js → chunk-IAS7Q2YF.js} +2 -2
  7. package/dist/{chunk-TMLS5WYR.js → chunk-JUYD2YUC.js} +2 -2
  8. package/dist/{chunk-7R5I5UWW.js → chunk-MLB5CFUP.js} +44 -15
  9. package/dist/{chunk-WI75LO3T.js → chunk-PLMMPA6A.js} +62 -13
  10. package/dist/{chunk-JLLISK4D.js → chunk-WVNQML4W.js} +62 -13
  11. package/dist/{coordinator-prompt-6CWVH2US.js → coordinator-prompt-JKW7YZOC.js} +2 -2
  12. package/dist/{coordinator-prompt-R7JNOUZ3.js → coordinator-prompt-YSIC6YFC.js} +2 -2
  13. package/dist/{global-workspace-GTEL4UUZ.js → global-workspace-3Q57XYG4.js} +2 -2
  14. package/dist/{global-workspace-DWKW4CFY.js → global-workspace-DMR5JB7T.js} +2 -2
  15. package/dist/index.cjs +738 -628
  16. package/dist/index.d.cts +36 -9
  17. package/dist/index.d.ts +36 -9
  18. package/dist/index.js +24 -14
  19. package/dist/mcp/run-stdio.cjs +152 -52
  20. package/dist/mcp/run-stdio.js +21 -14
  21. package/dist/{orchestrator-7XDC4UOC.js → orchestrator-PJCQSVLY.js} +4 -4
  22. package/dist/{orchestrator-W7A5BA5V.js → orchestrator-W6DUFWVE.js} +4 -4
  23. package/dist/{workspaces-E6IL3MIH.js → workspaces-6A3ASHHI.js} +2 -2
  24. package/dist/{workspaces-PSDDVERD.js → workspaces-PS3IO4VJ.js} +2 -2
  25. package/dist/{worktree-MWX6ZVNS.js → worktree-7WTVHFGF.js} +1 -1
  26. package/dist/{worktree-Q4T7RSSW.js → worktree-DLSVHUF6.js} +1 -1
  27. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -302,6 +302,7 @@ import {
302
302
  runWorkspaceSetup,
303
303
  sameWorktreePath,
304
304
  saveWorktreeRunPorts,
305
+ scheduleNeedsCaffeinate,
305
306
  schedulesPath,
306
307
  shiftQueuedItem,
307
308
  shouldCompactContext,
@@ -338,7 +339,7 @@ import {
338
339
  worktreeRunPortsPath,
339
340
  wrapReviewSkillMarkdown,
340
341
  writeWorktreeFile
341
- } from "./chunk-2LZ3CNTN.js";
342
+ } from "./chunk-GZRS5QVL.js";
342
343
  import {
343
344
  LEGACY_PLAN_FILE_REL,
344
345
  PLAN_FILE_NAME,
@@ -419,6 +420,7 @@ import {
419
420
  isShellToolName,
420
421
  isSkillToolName,
421
422
  isSubagentToolName,
423
+ isSystemStatusThinking,
422
424
  lastAssistantMessageText,
423
425
  listAgentSetupInfo,
424
426
  listBrightsyChatTargets,
@@ -476,7 +478,7 @@ import {
476
478
  withEventParentId,
477
479
  withEventsParentId,
478
480
  writeInjectedMcpConfig
479
- } from "./chunk-7R5I5UWW.js";
481
+ } from "./chunk-MLB5CFUP.js";
480
482
  import {
481
483
  CLOUD_COORDINATOR_BUSY_REPLY,
482
484
  CLOUD_COORDINATOR_CANCELED_REPLY,
@@ -522,7 +524,7 @@ import {
522
524
  stageBuffersAsAttachments,
523
525
  syncWorkspacesFromThreads,
524
526
  takenTeamSlugsForOrchestration
525
- } from "./chunk-TMLS5WYR.js";
527
+ } from "./chunk-JUYD2YUC.js";
526
528
  import {
527
529
  FAMOUS_SOCCER_TEAMS,
528
530
  GITHUB_PR_BODY_MAX_CHARS,
@@ -621,7 +623,7 @@ import {
621
623
  worktreeDisplayLabelForGroup,
622
624
  worktreeNameFromPath,
623
625
  worktreeSlugForTicket
624
- } from "./chunk-WI75LO3T.js";
626
+ } from "./chunk-PLMMPA6A.js";
625
627
  import {
626
628
  ATTACHMENTS_DIR,
627
629
  CLI_DIR,
@@ -3972,7 +3974,7 @@ async function continueSourceThread(threadId, prompt) {
3972
3974
  await continueOnReply(threadId, prompt);
3973
3975
  return;
3974
3976
  }
3975
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-7XDC4UOC.js");
3977
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-PJCQSVLY.js");
3976
3978
  await getOrchestrator2().send(threadId, prompt);
3977
3979
  } catch {
3978
3980
  }
@@ -5517,7 +5519,7 @@ function parseWhen(args) {
5517
5519
  function registerScheduleTools(server) {
5518
5520
  server.tool(
5519
5521
  "list_schedules",
5520
- "List local Sideboard schedules that trigger orchestration agents. Jobs fire only while Sideboard.app is running on this Mac (sleep skips until wake).",
5522
+ "List local Sideboard schedules that trigger a chat when due. Jobs fire only while Sideboard.app is running on this Mac (sleep skips until wake).",
5521
5523
  {},
5522
5524
  async () => {
5523
5525
  try {
@@ -5529,7 +5531,7 @@ function registerScheduleTools(server) {
5529
5531
  );
5530
5532
  server.tool(
5531
5533
  "create_schedule",
5532
- "Create a local schedule that, when due, sends a prompt to an orchestration chat (threadId) or starts a new Global orchestration chat (omit threadId). Pass threadId=self to continue this coordinator. Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without threadId open a new chat each run. Overnight/unattended runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate. Sideboard.app must be running for the job to fire.",
5534
+ "Create a local schedule that, when due, sends a prompt to an existing chat (threadId) or starts a new Global orchestration chat (omit threadId). Pass threadId=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 threadId 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.",
5533
5535
  {
5534
5536
  prompt: z8.string().describe("User message / goal queued when the schedule fires"),
5535
5537
  name: z8.string().optional(),
@@ -5538,7 +5540,7 @@ function registerScheduleTools(server) {
5538
5540
  cron: z8.string().optional().describe("5-field cron expression"),
5539
5541
  tz: z8.string().optional().describe("IANA timezone for cron (default: system)"),
5540
5542
  threadId: z8.string().optional().describe(
5541
- 'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
5543
+ 'Existing chat id (orchestration or worktree), or "self" for this chat. Omit to create a new Global chat on fire.'
5542
5544
  ),
5543
5545
  agent: z8.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
5544
5546
  model: z8.string().optional()
@@ -5549,7 +5551,9 @@ function registerScheduleTools(server) {
5549
5551
  const threadId = resolveScheduleThreadId(args.threadId);
5550
5552
  if (args.threadId?.trim().toLowerCase() === "self" && !threadId) {
5551
5553
  return fail5(
5552
- new Error("threadId=self requires this turn to be an orchestration chat")
5554
+ new Error(
5555
+ "threadId=self needs this chat id (SIDEBOARD_THREAD_ID or SIDEBOARD_ORCHESTRATOR_THREAD_ID)"
5556
+ )
5553
5557
  );
5554
5558
  }
5555
5559
  const schedule = createSchedule({
@@ -5563,7 +5567,7 @@ function registerScheduleTools(server) {
5563
5567
  });
5564
5568
  return text6({
5565
5569
  schedule,
5566
- hint: "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run. Overnight runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate."
5570
+ hint: scheduleNeedsCaffeinate(schedule) ? "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run. Caffeinate while schedules are enabled is now on so the Mac can stay awake until this job is due." : "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run."
5567
5571
  });
5568
5572
  } catch (err) {
5569
5573
  return fail5(err);
@@ -5581,7 +5585,9 @@ function registerScheduleTools(server) {
5581
5585
  every: z8.string().optional(),
5582
5586
  cron: z8.string().optional(),
5583
5587
  tz: z8.string().optional(),
5584
- threadId: z8.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
5588
+ threadId: z8.string().optional().describe(
5589
+ 'Existing chat (orchestration or worktree), "self", or empty string to create a new Global chat each run'
5590
+ ),
5585
5591
  agent: z8.enum(["claude", "cursor", "codex", "opencode"]).optional(),
5586
5592
  model: z8.string().optional(),
5587
5593
  enabled: z8.boolean().optional()
@@ -5600,7 +5606,9 @@ function registerScheduleTools(server) {
5600
5606
  threadId = resolveScheduleThreadId(args.threadId);
5601
5607
  if (args.threadId.trim().toLowerCase() === "self" && !threadId) {
5602
5608
  return fail5(
5603
- new Error("threadId=self requires this turn to be an orchestration chat")
5609
+ new Error(
5610
+ "threadId=self needs this chat id (SIDEBOARD_THREAD_ID or SIDEBOARD_ORCHESTRATOR_THREAD_ID)"
5611
+ )
5604
5612
  );
5605
5613
  }
5606
5614
  }
@@ -5634,7 +5642,7 @@ function registerScheduleTools(server) {
5634
5642
  );
5635
5643
  server.tool(
5636
5644
  "run_schedule",
5637
- "Fire a schedule now (does not wait for nextRunAt). Queues the prompt or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
5645
+ "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.",
5638
5646
  { id: z8.string() },
5639
5647
  async ({ id }) => {
5640
5648
  try {
@@ -6577,10 +6585,10 @@ async function startMcpServer() {
6577
6585
  }
6578
6586
  );
6579
6587
  }
6588
+ registerScheduleTools(server);
6580
6589
  if (!worktreeProfile) {
6581
6590
  registerSlackTools(server);
6582
6591
  registerConnectedIssueVendorTools(server);
6583
- registerScheduleTools(server);
6584
6592
  const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-DYJGWK6Y.js");
6585
6593
  const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-ONYJ36XH.js");
6586
6594
  const accountDefaults = resolveThreadDefaults2();
@@ -10125,6 +10133,7 @@ export {
10125
10133
  isSlackOAuthCancelled,
10126
10134
  isStaleLastErrorDuringTurn,
10127
10135
  isSubagentToolName,
10136
+ isSystemStatusThinking,
10128
10137
  isTerminalTaskState,
10129
10138
  isThinkingEffort,
10130
10139
  isThisProcessDesktopHost,
@@ -10398,6 +10407,7 @@ export {
10398
10407
  saveAppSettings,
10399
10408
  saveLinearOAuth,
10400
10409
  saveWorktreeRunPorts,
10410
+ scheduleNeedsCaffeinate,
10401
10411
  schedulesPath,
10402
10412
  scrubGithubTokensFromChildEnv,
10403
10413
  searchAbleTimeTasks,
@@ -5888,6 +5888,23 @@ function prIsInMergeQueue(gate) {
5888
5888
  if (gate.isInMergeQueue) return true;
5889
5889
  return (gate.mergeStateStatus ?? "").toUpperCase() === "QUEUED";
5890
5890
  }
5891
+ function applyCleanLocalMergeTree(gate) {
5892
+ const mergeable = (gate.mergeable ?? "").toUpperCase();
5893
+ const mergeState = (gate.mergeStateStatus ?? "").toUpperCase();
5894
+ const githubConflicting = mergeable === "CONFLICTING" || mergeState === "DIRTY";
5895
+ if (githubConflicting) {
5896
+ return {
5897
+ ...gate,
5898
+ mergeable: "MERGEABLE",
5899
+ mergeStateStatus: mergeState === "BEHIND" ? "BEHIND" : "CLEAN"
5900
+ };
5901
+ }
5902
+ return {
5903
+ ...gate,
5904
+ mergeable: mergeable === "UNKNOWN" || !mergeable ? "MERGEABLE" : gate.mergeable,
5905
+ mergeStateStatus: mergeState === "BEHIND" ? "BEHIND" : mergeState === "UNKNOWN" || !mergeState ? "CLEAN" : gate.mergeStateStatus
5906
+ };
5907
+ }
5891
5908
  function ciChecksFailed(checks) {
5892
5909
  if (!checks?.length) return false;
5893
5910
  return checks.some(
@@ -6511,6 +6528,21 @@ async function listPrs(repoPath, opts) {
6511
6528
  if (exitCode !== 0 || !stdout.trim()) return [];
6512
6529
  return applyPrListResponse(stdout, { ...resolved, limit, viewerTeams });
6513
6530
  }
6531
+ function prInfoFromGhView(view) {
6532
+ const author = view.author;
6533
+ const login = author?.login?.trim();
6534
+ return {
6535
+ number: Number(view.number),
6536
+ title: String(view.title ?? ""),
6537
+ headRefName: String(view.headRefName ?? ""),
6538
+ url: String(view.url ?? ""),
6539
+ isCrossRepository: Boolean(view.isCrossRepository),
6540
+ author: login ? { login } : null,
6541
+ isDraft: Boolean(view.isDraft),
6542
+ state: typeof view.state === "string" ? view.state : void 0,
6543
+ labels: labelNames(view.labels)
6544
+ };
6545
+ }
6514
6546
  async function getPr(repoPath, number) {
6515
6547
  const slug = await resolveGithubRepoSlug(repoPath);
6516
6548
  const args = [
@@ -6518,12 +6550,16 @@ async function getPr(repoPath, number) {
6518
6550
  "view",
6519
6551
  String(number),
6520
6552
  "--json",
6521
- "number,title,headRefName,url,isCrossRepository,author"
6553
+ PR_INFO_JSON_FIELDS
6522
6554
  ];
6523
6555
  if (slug) args.push("--repo", slug);
6524
6556
  const { stdout, exitCode } = await gh(args, repoPath, { reject: false });
6525
6557
  if (exitCode !== 0 || !stdout.trim()) return null;
6526
- return JSON.parse(stdout);
6558
+ try {
6559
+ return prInfoFromGhView(JSON.parse(stdout));
6560
+ } catch {
6561
+ return null;
6562
+ }
6527
6563
  }
6528
6564
  function normalizeGitBranchName(ref) {
6529
6565
  return ref.trim().replace(/^refs\/heads\//, "").replace(/^origin\//, "");
@@ -6577,7 +6613,7 @@ async function getPrForHeadBranch(repoPath, branch) {
6577
6613
  "--head",
6578
6614
  listHead,
6579
6615
  "--json",
6580
- "number,title,headRefName,url,isCrossRepository,author",
6616
+ PR_INFO_JSON_FIELDS,
6581
6617
  "--limit",
6582
6618
  "1",
6583
6619
  "--state",
@@ -6588,7 +6624,7 @@ async function getPrForHeadBranch(repoPath, branch) {
6588
6624
  if (listed.exitCode === 0 && listed.stdout.trim()) {
6589
6625
  try {
6590
6626
  const rows = JSON.parse(listed.stdout);
6591
- if (rows[0]) return rows[0];
6627
+ if (rows[0]) return prInfoFromGhView(rows[0]);
6592
6628
  } catch {
6593
6629
  }
6594
6630
  }
@@ -6597,13 +6633,13 @@ async function getPrForHeadBranch(repoPath, branch) {
6597
6633
  "view",
6598
6634
  head,
6599
6635
  "--json",
6600
- "number,title,headRefName,url,isCrossRepository,author,state"
6636
+ `${PR_INFO_JSON_FIELDS},state`
6601
6637
  ];
6602
6638
  if (slug) viewArgs.push("--repo", slug);
6603
6639
  const viewed = await gh(viewArgs, repoPath, { reject: false });
6604
6640
  if (viewed.exitCode !== 0 || !viewed.stdout.trim()) return null;
6605
6641
  try {
6606
- const info = JSON.parse(viewed.stdout);
6642
+ const info = prInfoFromGhView(JSON.parse(viewed.stdout));
6607
6643
  const state = (info.state ?? "OPEN").trim().toUpperCase();
6608
6644
  if (state !== "OPEN") return null;
6609
6645
  return info;
@@ -6724,11 +6760,7 @@ async function probeLocalMergeGate(cwd, gate) {
6724
6760
  }
6725
6761
  if (gate && (githubConflicting || githubUnknown)) {
6726
6762
  return {
6727
- gate: {
6728
- ...gate,
6729
- mergeable: "MERGEABLE",
6730
- mergeStateStatus: mergeState === "BEHIND" ? "BEHIND" : "CLEAN"
6731
- },
6763
+ gate: applyCleanLocalMergeTree(gate),
6732
6764
  files: []
6733
6765
  };
6734
6766
  }
@@ -6854,6 +6886,7 @@ function parsePrDetailsView(view) {
6854
6886
  const author = view.author ?? {};
6855
6887
  const comments = Array.isArray(view.comments) ? view.comments : [];
6856
6888
  const reviews = Array.isArray(view.reviews) ? view.reviews : [];
6889
+ const commits = Array.isArray(view.commits) ? view.commits : [];
6857
6890
  const requested = {
6858
6891
  ids: reviewerIdentities(view.reviewRequests),
6859
6892
  users: humanReviewerLogins(view.reviewRequests),
@@ -6874,8 +6907,22 @@ function parsePrDetailsView(view) {
6874
6907
  additions: Number(view.additions ?? 0),
6875
6908
  deletions: Number(view.deletions ?? 0),
6876
6909
  changedFiles: Number(view.changedFiles ?? 0),
6877
- // Commits live in Changes; omit from GraphQL to save rate-limit points.
6878
- commits: [],
6910
+ commits: commits.map((c) => {
6911
+ const row = c;
6912
+ const authors = Array.isArray(row.authors) ? row.authors.map((a) => {
6913
+ const actor = a;
6914
+ return {
6915
+ login: actor.login?.trim() || actor.name?.trim() || "unknown",
6916
+ name: actor.name ?? null
6917
+ };
6918
+ }) : [];
6919
+ return {
6920
+ oid: String(row.oid ?? ""),
6921
+ messageHeadline: String(row.messageHeadline ?? ""),
6922
+ committedDate: String(row.committedDate ?? ""),
6923
+ authors
6924
+ };
6925
+ }),
6879
6926
  comments: comments.map((c) => {
6880
6927
  const row = c;
6881
6928
  const a = row.author ?? {};
@@ -7644,7 +7691,7 @@ function allocateTeamSlug(repoPath) {
7644
7691
  }
7645
7692
  throw new Error("No available soccer team worktree directories left");
7646
7693
  }
7647
- var import_node_crypto4, import_node_fs15, import_node_os6, import_node_path18, PR_DETAILS_JSON_FIELDS;
7694
+ var import_node_crypto4, import_node_fs15, import_node_os6, import_node_path18, PR_INFO_JSON_FIELDS, PR_DETAILS_JSON_FIELDS;
7648
7695
  var init_worktree = __esm({
7649
7696
  "src/git/worktree.ts"() {
7650
7697
  "use strict";
@@ -7670,6 +7717,7 @@ var init_worktree = __esm({
7670
7717
  init_git_auth_mode();
7671
7718
  init_workspace_scratch();
7672
7719
  init_worktree_labels();
7720
+ PR_INFO_JSON_FIELDS = "number,title,headRefName,url,isCrossRepository,author,isDraft,labels";
7673
7721
  PR_DETAILS_JSON_FIELDS = [
7674
7722
  "number",
7675
7723
  "title",
@@ -7685,6 +7733,7 @@ var init_worktree = __esm({
7685
7733
  "additions",
7686
7734
  "deletions",
7687
7735
  "changedFiles",
7736
+ "commits",
7688
7737
  "comments",
7689
7738
  "reviews",
7690
7739
  "assignees",
@@ -8366,7 +8415,7 @@ var init_coordinator_prompt = __esm({
8366
8415
  "- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API, Save this context / Do not save). Never for hellos, check-ins, invented \u201Cwhat should we do?\u201D menus, or after a review \u2014 reply in chat. Explain options first, description on every option, then wait.",
8367
8416
  "- get_viewer_context / update_viewer_context \u2014 read or replace Settings \u2192 Agents (account) and Settings \u2192 Projects (per-repo) context. You can update a project: pass scope=project and repoPath from list_workspaces (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
8368
8417
  "- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for Slack / away-from-keyboard work, overnight schedules, or when the user will be away. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
8369
- "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to an orchestration chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Jobs fire only while Sideboard.app is running; sleep skips until wake. Overnight/unattended runs: ask the user to enable Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or call set_caffeinate.",
8418
+ "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to a chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Worktree agents have the same tools (`threadId=self` continues that coding chat). Jobs fire only while Sideboard.app is running; sleep skips until wake. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled (or call set_caffeinate).",
8370
8419
  "Workspaces:",
8371
8420
  "- add_workspace / remove_workspace \u2014 register or unregister a git repo",
8372
8421
  "Worktree threads (chats):",
@@ -10119,7 +10168,12 @@ var init_injected_mcp = __esm({
10119
10168
  "mcp__sideboard__get_run_log",
10120
10169
  "mcp__sideboard__notify_orchestrator",
10121
10170
  "mcp__sideboard__get_viewer_context",
10122
- "mcp__sideboard__update_viewer_context"
10171
+ "mcp__sideboard__update_viewer_context",
10172
+ "mcp__sideboard__list_schedules",
10173
+ "mcp__sideboard__create_schedule",
10174
+ "mcp__sideboard__update_schedule",
10175
+ "mcp__sideboard__delete_schedule",
10176
+ "mcp__sideboard__run_schedule"
10123
10177
  ];
10124
10178
  SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
10125
10179
  SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
@@ -10348,6 +10402,15 @@ function isContextCompactInProgress(text7) {
10348
10402
  if (isContextCompactDone(t)) return false;
10349
10403
  return /^Compressing context/i.test(t);
10350
10404
  }
10405
+ function isSystemStatusThinking(event) {
10406
+ if (event.replace) return true;
10407
+ const t = (event.data ?? "").trim();
10408
+ if (!t) return true;
10409
+ if (/^API retry\b/i.test(t)) return true;
10410
+ if (/^MCP:/i.test(t)) return true;
10411
+ if (isContextCompactInProgress(t) || isContextCompactDone(t)) return true;
10412
+ return false;
10413
+ }
10351
10414
  function looksLikeConversationSummary(text7) {
10352
10415
  const t = text7.trim();
10353
10416
  if (t.length < 80) return false;
@@ -10425,6 +10488,13 @@ function toolDescription(name, input) {
10425
10488
  return str2(input?.name) ? `Read ${str2(input?.name)} log` : "Read Dev log";
10426
10489
  }
10427
10490
  if (/notify_orchestrator$/i.test(name)) return "Notify orchestrator";
10491
+ if (/list_schedules$/i.test(name)) return "List schedules";
10492
+ if (/create_schedule$/i.test(name)) {
10493
+ return str2(input?.name) ? `Schedule ${str2(input?.name)}` : "Create schedule";
10494
+ }
10495
+ if (/update_schedule$/i.test(name)) return "Update schedule";
10496
+ if (/delete_schedule$/i.test(name)) return "Delete schedule";
10497
+ if (/run_schedule$/i.test(name)) return "Run schedule";
10428
10498
  if (isSkillToolName(name)) {
10429
10499
  const cmd = skillCommandFromInput(input);
10430
10500
  return cmd ? `Using /${cmd}` : "Using skill";
@@ -10799,15 +10869,6 @@ function applyAgentEvent(parts, event) {
10799
10869
  );
10800
10870
  return next2;
10801
10871
  }
10802
- if (contextCompactInProgress(parts, event.parentId)) {
10803
- return [
10804
- ...parts,
10805
- compactToolPart(data, `compact-${parts.length}`, {
10806
- parentId: event.parentId,
10807
- status: "running"
10808
- })
10809
- ];
10810
- }
10811
10872
  const runningSkillIdx = lastRunningSkillIndex(parts, event.parentId);
10812
10873
  if (runningSkillIdx >= 0) {
10813
10874
  const prev = parts[runningSkillIdx];
@@ -10826,6 +10887,15 @@ function applyAgentEvent(parts, event) {
10826
10887
  );
10827
10888
  return next2;
10828
10889
  }
10890
+ if (contextCompactInProgress(parts, event.parentId)) {
10891
+ return [
10892
+ ...parts,
10893
+ compactToolPart(data, `compact-${parts.length}`, {
10894
+ parentId: event.parentId,
10895
+ status: "running"
10896
+ })
10897
+ ];
10898
+ }
10829
10899
  const next = [...parts];
10830
10900
  const last = next[next.length - 1];
10831
10901
  if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
@@ -10862,7 +10932,9 @@ function applyAgentEvent(parts, event) {
10862
10932
  if (event.type === "thinking") {
10863
10933
  const data = event.data;
10864
10934
  if (!data) return parts;
10865
- parts = markRunningSkillsDone(parts, event.parentId);
10935
+ if (!isSystemStatusThinking(event)) {
10936
+ parts = markRunningSkillsDone(parts, event.parentId);
10937
+ }
10866
10938
  const next = [...parts];
10867
10939
  if (event.replace) {
10868
10940
  for (let i = next.length - 1; i >= 0; i--) {
@@ -18694,6 +18766,7 @@ async function createThread(input, _onSetupLine) {
18694
18766
  let prTitle = null;
18695
18767
  let prAuthorLogin2 = null;
18696
18768
  let prLabels = [];
18769
+ let prIsDraft = false;
18697
18770
  if (sourceType === "pr") {
18698
18771
  const num2 = Number(input.sourceRef.replace(/^#/, ""));
18699
18772
  if (!Number.isFinite(num2)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
@@ -18711,6 +18784,7 @@ async function createThread(input, _onSetupLine) {
18711
18784
  prUrl = pr.url;
18712
18785
  prAuthorLogin2 = pr.author?.login?.trim() || null;
18713
18786
  prLabels = pr.labels ?? [];
18787
+ prIsDraft = Boolean(pr.isDraft);
18714
18788
  const localFetchBranch = `sideboard-pr-${pr.number}`;
18715
18789
  await fetchPrHead(repoPath, pr.number, localFetchBranch);
18716
18790
  sourceRef = localFetchBranch;
@@ -18735,6 +18809,7 @@ async function createThread(input, _onSetupLine) {
18735
18809
  prTitle = existing.title;
18736
18810
  prAuthorLogin2 = existing.author?.login?.trim() || null;
18737
18811
  prLabels = existing.labels ?? [];
18812
+ prIsDraft = Boolean(existing.isDraft);
18738
18813
  }
18739
18814
  }
18740
18815
  }
@@ -18771,7 +18846,8 @@ async function createThread(input, _onSetupLine) {
18771
18846
  prUrl,
18772
18847
  prTitle,
18773
18848
  prAuthorLogin: prAuthorLogin2,
18774
- prLabels
18849
+ prLabels,
18850
+ prIsDraft
18775
18851
  });
18776
18852
  writeThread(thread);
18777
18853
  await ensureWorkspace(repoPath);
@@ -21357,11 +21433,15 @@ function formatWorktreeDirective(thread, opts) {
21357
21433
  lines.push("");
21358
21434
  lines.push(formatDevAccessHint(opts?.activeRuns));
21359
21435
  lines.push("");
21436
+ lines.push(
21437
+ "Schedules: `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `threadId=self` continues this chat; omit threadId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled."
21438
+ );
21439
+ lines.push("");
21360
21440
  lines.push(formatProcessGuideDirective({ worktreePath: thread.worktreePath }));
21361
21441
  return lines.join("\n");
21362
21442
  }
21363
21443
  function formatWorktreeReminder() {
21364
- return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push.";
21444
+ return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; threadId=self for this chat).";
21365
21445
  }
21366
21446
  function mentionsPrGoal(prompt) {
21367
21447
  return PR_GOAL_RE.test(prompt);
@@ -21988,7 +22068,7 @@ function resolveScheduleThreadId(raw, env = process.env) {
21988
22068
  const value = raw?.trim() || "";
21989
22069
  if (!value) return null;
21990
22070
  if (value.toLowerCase() === "self") {
21991
- return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
22071
+ return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
21992
22072
  }
21993
22073
  return value;
21994
22074
  }
@@ -22116,6 +22196,17 @@ function buildTask(input, now) {
22116
22196
  function listSchedules() {
22117
22197
  return readAll2().sort((a, b) => a.nextRunAt.localeCompare(b.nextRunAt));
22118
22198
  }
22199
+ function scheduleNeedsCaffeinate(task, now = /* @__PURE__ */ new Date()) {
22200
+ if (!task.enabled) return false;
22201
+ const at = new Date(task.nextRunAt);
22202
+ if (Number.isNaN(at.getTime())) return false;
22203
+ return at.getTime() > now.getTime();
22204
+ }
22205
+ function ensureCaffeinateForFutureSchedule(task) {
22206
+ if (!scheduleNeedsCaffeinate(task)) return;
22207
+ if (caffeinateWhileSchedulesEnabled()) return;
22208
+ updateAdvancedSettings({ caffeinateWhileSchedules: true });
22209
+ }
22119
22210
  function getSchedule(id) {
22120
22211
  const key = id.trim();
22121
22212
  if (!key) return null;
@@ -22128,6 +22219,7 @@ function createSchedule(input) {
22128
22219
  const rows = readAll2();
22129
22220
  rows.push(task);
22130
22221
  writeAll2(rows);
22222
+ ensureCaffeinateForFutureSchedule(task);
22131
22223
  return task;
22132
22224
  }
22133
22225
  function updateSchedule(id, patch) {
@@ -22154,6 +22246,7 @@ function updateSchedule(id, patch) {
22154
22246
  };
22155
22247
  rows[idx] = next;
22156
22248
  writeAll2(rows);
22249
+ ensureCaffeinateForFutureSchedule(next);
22157
22250
  return next;
22158
22251
  }
22159
22252
  function deleteSchedule(id) {
@@ -22194,6 +22287,7 @@ var init_schedules = __esm({
22194
22287
  init_orchestrator_capable();
22195
22288
  init_paths();
22196
22289
  init_private_file();
22290
+ init_app_settings();
22197
22291
  DURATION_RE = /^(\d+)(s|m|h|d)$/i;
22198
22292
  UNIT_MS = {
22199
22293
  s: 1e3,
@@ -22230,19 +22324,9 @@ async function fireSchedule(id) {
22230
22324
  try {
22231
22325
  const deps = fireHooks ?? await defaultDeps();
22232
22326
  const prompt = formatScheduledPrompt(schedule.name, schedule.prompt);
22233
- if (schedule.threadId) {
22234
- const thread = deps.findThread(schedule.threadId);
22235
- if (!thread || thread.status === "archived") {
22236
- return recordScheduleRun(schedule.id, {
22237
- lastError: thread ? `Orchestration chat ${schedule.threadId} is archived` : `Orchestration chat not found: ${schedule.threadId}`
22238
- });
22239
- }
22240
- if (!isOrchestratorThread(thread)) {
22241
- return recordScheduleRun(schedule.id, {
22242
- lastError: `Thread ${schedule.threadId} is not an orchestration chat`
22243
- });
22244
- }
22245
- const sent = await deps.send(thread.id, prompt);
22327
+ const target = schedule.threadId ? deps.findThread(schedule.threadId) : null;
22328
+ if (target && target.status !== "archived") {
22329
+ const sent = await deps.send(target.id, prompt);
22246
22330
  return recordScheduleRun(schedule.id, {
22247
22331
  lastThreadId: sent.id,
22248
22332
  lastError: null
@@ -22253,6 +22337,9 @@ async function fireSchedule(id) {
22253
22337
  agent: pickAgent(schedule),
22254
22338
  model: schedule.model
22255
22339
  });
22340
+ if (schedule.threadId && schedule.when.kind !== "once") {
22341
+ updateSchedule(schedule.id, { threadId: created.id });
22342
+ }
22256
22343
  return recordScheduleRun(schedule.id, {
22257
22344
  lastThreadId: created.id,
22258
22345
  lastError: null
@@ -22321,7 +22408,6 @@ var init_schedule_runner = __esm({
22321
22408
  init_app_settings();
22322
22409
  init_schedules();
22323
22410
  init_thread_store();
22324
- init_global_workspace();
22325
22411
  SET_TIMEOUT_MAX = 2147483647;
22326
22412
  fireHooks = null;
22327
22413
  timers = /* @__PURE__ */ new Map();
@@ -24657,6 +24743,14 @@ var init_orchestrator = __esm({
24657
24743
  const thread = this.requireThread(threadRef);
24658
24744
  return discoverSkills(thread.worktreePath);
24659
24745
  }
24746
+ /** Skills for a project folder before a worktree exists (create modal). */
24747
+ listSkillsForRepo(repoPath) {
24748
+ const root = repoPath.trim();
24749
+ if (!root || root === "/" || root === ".") {
24750
+ return discoverSkills("/__sideboard_no_workspace__");
24751
+ }
24752
+ return discoverSkills(root);
24753
+ }
24660
24754
  async previewLand(threadRef) {
24661
24755
  const thread = this.requireThread(threadRef);
24662
24756
  this.assertNotGlobal(thread, "Land");
@@ -29057,7 +29151,7 @@ function parseWhen(args) {
29057
29151
  function registerScheduleTools(server) {
29058
29152
  server.tool(
29059
29153
  "list_schedules",
29060
- "List local Sideboard schedules that trigger orchestration agents. Jobs fire only while Sideboard.app is running on this Mac (sleep skips until wake).",
29154
+ "List local Sideboard schedules that trigger a chat when due. Jobs fire only while Sideboard.app is running on this Mac (sleep skips until wake).",
29061
29155
  {},
29062
29156
  async () => {
29063
29157
  try {
@@ -29069,7 +29163,7 @@ function registerScheduleTools(server) {
29069
29163
  );
29070
29164
  server.tool(
29071
29165
  "create_schedule",
29072
- "Create a local schedule that, when due, sends a prompt to an orchestration chat (threadId) or starts a new Global orchestration chat (omit threadId). Pass threadId=self to continue this coordinator. Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without threadId open a new chat each run. Overnight/unattended runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate. Sideboard.app must be running for the job to fire.",
29166
+ "Create a local schedule that, when due, sends a prompt to an existing chat (threadId) or starts a new Global orchestration chat (omit threadId). Pass threadId=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 threadId 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.",
29073
29167
  {
29074
29168
  prompt: import_zod8.z.string().describe("User message / goal queued when the schedule fires"),
29075
29169
  name: import_zod8.z.string().optional(),
@@ -29078,7 +29172,7 @@ function registerScheduleTools(server) {
29078
29172
  cron: import_zod8.z.string().optional().describe("5-field cron expression"),
29079
29173
  tz: import_zod8.z.string().optional().describe("IANA timezone for cron (default: system)"),
29080
29174
  threadId: import_zod8.z.string().optional().describe(
29081
- 'Existing orchestration chat id, or "self" for this coordinator. Omit to create a new Global chat on fire.'
29175
+ 'Existing chat id (orchestration or worktree), or "self" for this chat. Omit to create a new Global chat on fire.'
29082
29176
  ),
29083
29177
  agent: import_zod8.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
29084
29178
  model: import_zod8.z.string().optional()
@@ -29089,7 +29183,9 @@ function registerScheduleTools(server) {
29089
29183
  const threadId = resolveScheduleThreadId(args.threadId);
29090
29184
  if (args.threadId?.trim().toLowerCase() === "self" && !threadId) {
29091
29185
  return fail5(
29092
- new Error("threadId=self requires this turn to be an orchestration chat")
29186
+ new Error(
29187
+ "threadId=self needs this chat id (SIDEBOARD_THREAD_ID or SIDEBOARD_ORCHESTRATOR_THREAD_ID)"
29188
+ )
29093
29189
  );
29094
29190
  }
29095
29191
  const schedule = createSchedule({
@@ -29103,7 +29199,7 @@ function registerScheduleTools(server) {
29103
29199
  });
29104
29200
  return text6({
29105
29201
  schedule,
29106
- hint: "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run. Overnight runs need Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or set_caffeinate."
29202
+ hint: scheduleNeedsCaffeinate(schedule) ? "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run. Caffeinate while schedules are enabled is now on so the Mac can stay awake until this job is due." : "Fires while Sideboard.app is running. Recurring jobs without threadId create a new orchestration chat each run."
29107
29203
  });
29108
29204
  } catch (err) {
29109
29205
  return fail5(err);
@@ -29121,7 +29217,9 @@ function registerScheduleTools(server) {
29121
29217
  every: import_zod8.z.string().optional(),
29122
29218
  cron: import_zod8.z.string().optional(),
29123
29219
  tz: import_zod8.z.string().optional(),
29124
- threadId: import_zod8.z.string().optional().describe('Existing orchestration chat, "self", or empty string to create a new chat each run'),
29220
+ threadId: import_zod8.z.string().optional().describe(
29221
+ 'Existing chat (orchestration or worktree), "self", or empty string to create a new Global chat each run'
29222
+ ),
29125
29223
  agent: import_zod8.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
29126
29224
  model: import_zod8.z.string().optional(),
29127
29225
  enabled: import_zod8.z.boolean().optional()
@@ -29140,7 +29238,9 @@ function registerScheduleTools(server) {
29140
29238
  threadId = resolveScheduleThreadId(args.threadId);
29141
29239
  if (args.threadId.trim().toLowerCase() === "self" && !threadId) {
29142
29240
  return fail5(
29143
- new Error("threadId=self requires this turn to be an orchestration chat")
29241
+ new Error(
29242
+ "threadId=self needs this chat id (SIDEBOARD_THREAD_ID or SIDEBOARD_ORCHESTRATOR_THREAD_ID)"
29243
+ )
29144
29244
  );
29145
29245
  }
29146
29246
  }
@@ -29174,7 +29274,7 @@ function registerScheduleTools(server) {
29174
29274
  );
29175
29275
  server.tool(
29176
29276
  "run_schedule",
29177
- "Fire a schedule now (does not wait for nextRunAt). Queues the prompt or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
29277
+ "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.",
29178
29278
  { id: import_zod8.z.string() },
29179
29279
  async ({ id }) => {
29180
29280
  try {
@@ -30114,10 +30214,10 @@ async function startMcpServer() {
30114
30214
  }
30115
30215
  );
30116
30216
  }
30217
+ registerScheduleTools(server);
30117
30218
  if (!worktreeProfile) {
30118
30219
  registerSlackTools(server);
30119
30220
  registerConnectedIssueVendorTools(server);
30120
- registerScheduleTools(server);
30121
30221
  const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await Promise.resolve().then(() => (init_caffeinate_hold(), caffeinate_hold_exports));
30122
30222
  const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
30123
30223
  const accountDefaults = resolveThreadDefaults2();