@sideboard-ai/core 0.1.322 → 0.1.324

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 (55) hide show
  1. package/dist/{abletime-CRRLEYTE.js → abletime-5ZY3ZUUO.js} +4 -4
  2. package/dist/{abletime-ICW6Q2AB.js → abletime-CU2UPMJF.js} +4 -4
  3. package/dist/{abletime-oauth-P3KPPJE4.js → abletime-oauth-4TTCR3CT.js} +3 -3
  4. package/dist/{abletime-oauth-Q76XBSPG.js → abletime-oauth-7EOSBEFM.js} +3 -3
  5. package/dist/{abletime-rest-DQ3PNKXP.js → abletime-rest-3TG4G7LW.js} +3 -3
  6. package/dist/{abletime-rest-DNFAYYLP.js → abletime-rest-KOKE7V4B.js} +3 -3
  7. package/dist/{agents-ROFDNE7Z.js → agents-LUSTCMDN.js} +5 -5
  8. package/dist/{agents-PFVYC36J.js → agents-YK65UDLT.js} +5 -5
  9. package/dist/{app-settings-KMPVI2B7.js → app-settings-I5HM5RMP.js} +1 -1
  10. package/dist/{app-settings-P3H4SEIK.js → app-settings-XIF4EC4K.js} +1 -1
  11. package/dist/{chunk-BPDVHB4U.js → chunk-5GDJSFCT.js} +2 -2
  12. package/dist/{chunk-RHJKZOPQ.js → chunk-74OPNF7U.js} +2 -2
  13. package/dist/{chunk-XDQSHSL6.js → chunk-7D5QIU3N.js} +4 -4
  14. package/dist/{chunk-45UB2J47.js → chunk-7MQSYPAY.js} +160 -46
  15. package/dist/{chunk-BTIXTH4N.js → chunk-7PVT73H6.js} +3 -3
  16. package/dist/{chunk-RGUZPMTF.js → chunk-BYUCNIXK.js} +4 -4
  17. package/dist/{chunk-LD2DXHBP.js → chunk-DF5BM5OY.js} +182 -50
  18. package/dist/{chunk-FZKAAAEB.js → chunk-EJ66LEKH.js} +1 -0
  19. package/dist/{chunk-DFUXW2IT.js → chunk-EKU7FQVG.js} +2 -2
  20. package/dist/{chunk-5OQONGHO.js → chunk-FR3SAYUL.js} +5 -5
  21. package/dist/{chunk-UWJUARFA.js → chunk-FUFNMUNR.js} +1 -1
  22. package/dist/{chunk-TEFFR6PB.js → chunk-INBUV7HY.js} +2 -2
  23. package/dist/{chunk-H6GXYV5Q.js → chunk-JE3JLHMO.js} +6 -7
  24. package/dist/{chunk-JNEFEA2A.js → chunk-JH5SMVM5.js} +4 -4
  25. package/dist/{chunk-7JV5QP5K.js → chunk-MZIZTSR2.js} +2 -2
  26. package/dist/{chunk-HHPTGIKS.js → chunk-NXUN4U5P.js} +2 -2
  27. package/dist/{chunk-V76MQ7MH.js → chunk-ODH3XHMI.js} +5 -5
  28. package/dist/{chunk-IFU4W4C4.js → chunk-OIHRC2ZJ.js} +1 -0
  29. package/dist/{chunk-56M6VEXI.js → chunk-Q3YJZPQI.js} +3 -3
  30. package/dist/{chunk-UX6EDDGR.js → chunk-QOS4465Y.js} +1 -1
  31. package/dist/{chunk-YWBLQBSN.js → chunk-ROOZDDFA.js} +1 -1
  32. package/dist/{chunk-BANN5HUX.js → chunk-RR4WCGNB.js} +4 -4
  33. package/dist/{chunk-72PUPPV2.js → chunk-Y3WREIPQ.js} +3 -3
  34. package/dist/{chunk-SPMIZSKH.js → chunk-ZIRQZUZY.js} +6 -7
  35. package/dist/{conductor-XLXJU24U.js → conductor-JFABMXDA.js} +4 -4
  36. package/dist/{conductor-CNT3VL3S.js → conductor-SPZOTC7W.js} +4 -4
  37. package/dist/{coordinator-prompt-D5EE2YTV.js → coordinator-prompt-K66RGNWK.js} +3 -3
  38. package/dist/{coordinator-prompt-TU24TQ4I.js → coordinator-prompt-Z6UFHFDH.js} +3 -3
  39. package/dist/{global-workspace-743TPQNF.js → global-workspace-A6UO6PJV.js} +3 -3
  40. package/dist/{global-workspace-UPMXDQM7.js → global-workspace-RHEO55B4.js} +3 -3
  41. package/dist/index.cjs +270 -61
  42. package/dist/index.d.cts +48 -20
  43. package/dist/index.d.ts +48 -20
  44. package/dist/index.js +113 -36
  45. package/dist/mcp/run-stdio.cjs +245 -61
  46. package/dist/mcp/run-stdio.js +97 -31
  47. package/dist/{optional-cli-USD6PROE.js → optional-cli-RMMS5L6F.js} +3 -3
  48. package/dist/{optional-cli-2YDQQCCD.js → optional-cli-Y3Z3GIPZ.js} +4 -4
  49. package/dist/{orchestrator-VQ4HRYVB.js → orchestrator-2O6OJQLX.js} +8 -8
  50. package/dist/{orchestrator-B4STIW4B.js → orchestrator-7VTAUY24.js} +8 -8
  51. package/dist/{workspaces-47WQL5IG.js → workspaces-42E6AGXD.js} +3 -3
  52. package/dist/{workspaces-75FD4QUR.js → workspaces-NRKEXOYR.js} +3 -3
  53. package/dist/{worktree-G3E6WP66.js → worktree-4NGXCCUO.js} +2 -2
  54. package/dist/{worktree-PDNPL5CQ.js → worktree-ZDA2UJ7C.js} +2 -2
  55. package/package.json +1 -1
@@ -14,7 +14,7 @@ import {
14
14
  parseSessionQuotaResetAt,
15
15
  planModeTurnInstruction,
16
16
  resolveQuotaFallbackAgent
17
- } from "./chunk-JNEFEA2A.js";
17
+ } from "./chunk-JH5SMVM5.js";
18
18
  import {
19
19
  applyWorktreePkgCacheEnv,
20
20
  askBlockStillWaiting,
@@ -52,7 +52,7 @@ import {
52
52
  worktreeDevProcessKey,
53
53
  worktreeRunProcessKey,
54
54
  worktreeSetupProcessKey
55
- } from "./chunk-BANN5HUX.js";
55
+ } from "./chunk-RR4WCGNB.js";
56
56
  import {
57
57
  applyAgentEvent,
58
58
  clipAgentEventForPaint,
@@ -74,7 +74,7 @@ import {
74
74
  import {
75
75
  formatOptionalServicesDirectiveForTurn,
76
76
  formatOptionalServicesReminderForTurn
77
- } from "./chunk-TEFFR6PB.js";
77
+ } from "./chunk-INBUV7HY.js";
78
78
  import {
79
79
  SLACK_REPLY_FORMATTING,
80
80
  addWorkspace,
@@ -99,7 +99,7 @@ import {
99
99
  stageAbsolutePathsAsAttachments,
100
100
  stageBuffersAsAttachments,
101
101
  syncWorkspacesFromThreads
102
- } from "./chunk-H6GXYV5Q.js";
102
+ } from "./chunk-JE3JLHMO.js";
103
103
  import {
104
104
  addPrStackLayer,
105
105
  allocateTeamSlug,
@@ -150,7 +150,7 @@ import {
150
150
  resolveRepoRoot,
151
151
  switchWorktreeBranch,
152
152
  withRepoGitLock
153
- } from "./chunk-UWJUARFA.js";
153
+ } from "./chunk-FUFNMUNR.js";
154
154
  import {
155
155
  REVIEW_TAB_TITLE,
156
156
  agentNicknameFromWorktreePath,
@@ -216,6 +216,7 @@ import {
216
216
  import {
217
217
  HISTORY_CLEANUP_INTERVAL_HOURS_DEFAULT,
218
218
  applyTurnUsage,
219
+ archivedUpdatedMs,
219
220
  assertOrchestratorCapableAgent,
220
221
  autoCleanupHistoryEnabled,
221
222
  caffeinateWhileSchedulesEnabled,
@@ -247,7 +248,7 @@ import {
247
248
  usageOnLimit,
248
249
  withTurnScopedCost,
249
250
  writeViewerContext
250
- } from "./chunk-FZKAAAEB.js";
251
+ } from "./chunk-EJ66LEKH.js";
251
252
  import {
252
253
  normalizeThinkingEffort
253
254
  } from "./chunk-77WWLBCI.js";
@@ -472,9 +473,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
472
473
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
473
474
  );
474
475
  }
475
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-UPMXDQM7.js");
476
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-RHEO55B4.js");
476
477
  if (isGlobalThread2(thread)) {
477
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-D5EE2YTV.js");
478
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-K66RGNWK.js");
478
479
  ensureGlobalCoordinatorCwd2(
479
480
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
480
481
  );
@@ -2139,6 +2140,7 @@ function claimSteerRequest(threadId) {
2139
2140
  }
2140
2141
 
2141
2142
  // src/orchestrator/ephemeral-automation.ts
2143
+ var EPHEMERAL_AUTOMATION_KEEP = 5;
2142
2144
  function ephemeralRunReadyToArchive(thread) {
2143
2145
  if (!thread.ephemeralAutomationId?.trim()) return false;
2144
2146
  if (thread.status !== "idle" && thread.status !== "error") return false;
@@ -2149,12 +2151,12 @@ function ephemeralRunReadyToArchive(thread) {
2149
2151
  }
2150
2152
  function markEphemeralAutomation(threadId, opts) {
2151
2153
  const id = opts.ephemeralAutomationId?.trim();
2154
+ const title = opts.ephemeralTitle?.trim();
2152
2155
  const current = readThread(threadId);
2153
2156
  if (!current) throw new Error(`Thread not found: ${threadId}`);
2154
- if (!id) return current;
2155
- const title = opts.ephemeralTitle?.trim();
2157
+ if (!id && !title) return current;
2156
2158
  return updateThread(threadId, {
2157
- ephemeralAutomationId: id,
2159
+ ...id ? { ephemeralAutomationId: id } : {},
2158
2160
  ...title ? { title, userSetTitle: true } : {}
2159
2161
  });
2160
2162
  }
@@ -2189,11 +2191,19 @@ function clearEphemeralAutomation(threadId) {
2189
2191
  function forgetOlderEphemeralRuns(automationId, keepId) {
2190
2192
  const id = automationId.trim();
2191
2193
  if (!id || !keepId) return [];
2194
+ const archived = listThreads({ includeArchived: true }).filter(
2195
+ (thread) => thread.status === "archived" && thread.ephemeralAutomationId === id
2196
+ );
2197
+ const ranked = [...archived].sort((a, b) => {
2198
+ if (a.id === keepId) return -1;
2199
+ if (b.id === keepId) return 1;
2200
+ return archivedUpdatedMs(b) - archivedUpdatedMs(a);
2201
+ });
2202
+ const keep = new Set(ranked.slice(0, EPHEMERAL_AUTOMATION_KEEP).map((thread) => thread.id));
2203
+ keep.add(keepId);
2192
2204
  const removed = [];
2193
- for (const thread of listThreads({ includeArchived: true })) {
2194
- if (thread.id === keepId) continue;
2195
- if (thread.status !== "archived") continue;
2196
- if (thread.ephemeralAutomationId !== id) continue;
2205
+ for (const thread of archived) {
2206
+ if (keep.has(thread.id)) continue;
2197
2207
  try {
2198
2208
  deleteThreadRecord(thread.id);
2199
2209
  removed.push(thread.id);
@@ -3059,7 +3069,7 @@ async function createThread(input, _onSetupLine) {
3059
3069
  });
3060
3070
  await requireAgent(resolved.agent);
3061
3071
  if (input.cowboy) {
3062
- const { cowboyModeEnabled } = await import("./app-settings-P3H4SEIK.js");
3072
+ const { cowboyModeEnabled } = await import("./app-settings-XIF4EC4K.js");
3063
3073
  if (!cowboyModeEnabled()) {
3064
3074
  throw new Error(
3065
3075
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -3118,7 +3128,7 @@ async function createThread(input, _onSetupLine) {
3118
3128
  let sourceIsFork = false;
3119
3129
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
3120
3130
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
3121
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-ICW6Q2AB.js");
3131
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-CU2UPMJF.js");
3122
3132
  const task = await ensureAbleTimeTask({
3123
3133
  title: title?.trim() || "Untitled work",
3124
3134
  description: input.prompt?.trim() || void 0
@@ -3229,7 +3239,7 @@ async function createThread(input, _onSetupLine) {
3229
3239
  return readThread(thread.id) ?? thread;
3230
3240
  }
3231
3241
  async function listLinearIssues(agent, repoPath) {
3232
- const { getAdapter: getAdapter2 } = await import("./agents-ROFDNE7Z.js");
3242
+ const { getAdapter: getAdapter2 } = await import("./agents-LUSTCMDN.js");
3233
3243
  await requireAgent(agent, { requireLinear: true });
3234
3244
  const adapter = getAdapter2(agent);
3235
3245
  if (!adapter.listLinearIssues) {
@@ -4012,7 +4022,7 @@ async function adoptThread(input) {
4012
4022
  messages: input.messages ?? []
4013
4023
  });
4014
4024
  writeThread(thread);
4015
- const { addWorkspace: addWorkspace2 } = await import("./workspaces-47WQL5IG.js");
4025
+ const { addWorkspace: addWorkspace2 } = await import("./workspaces-42E6AGXD.js");
4016
4026
  await addWorkspace2(repoPath);
4017
4027
  const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-INRDQFWV.js");
4018
4028
  await ensureWorktreeSideboardIgnored(input.worktreePath);
@@ -6415,7 +6425,7 @@ function formatWorktreeDirective(thread, opts) {
6415
6425
  lines.push(formatDevAccessHint(opts?.activeRuns));
6416
6426
  lines.push("");
6417
6427
  lines.push(
6418
- "Automations (sidebar, after Board): `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled."
6428
+ "Automations (sidebar, after Board): `create_schedule` / `list_schedules` (`at` / `every` / `cron` / `time`+`days`). `archiveChat=false` keeps the new chat. `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled."
6419
6429
  );
6420
6430
  lines.push(
6421
6431
  "`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
@@ -6435,7 +6445,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
6435
6445
  return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
6436
6446
  }
6437
6447
  function formatWorktreeReminder() {
6438
- return "Sideboard workspace: 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. Automations: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
6448
+ return "Sideboard workspace: 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. Automations: create_schedule (at / every / cron, or time + days such as weekdays at 07:30; archiveChat=false leaves the new chat open; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
6439
6449
  }
6440
6450
  function formatClaudeSubagentReminder() {
6441
6451
  return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself. That note is another worker in this chat, or a worker Claude still has marked running. Do not mention it to the user.';
@@ -7055,7 +7065,10 @@ async function syncThreadBranchFromGit(threadId) {
7055
7065
  import { randomUUID as randomUUID4 } from "crypto";
7056
7066
  import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
7057
7067
  import { join as join14 } from "path";
7068
+
7069
+ // src/store/schedule-when.ts
7058
7070
  import { Cron } from "croner";
7071
+ var WEEKDAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
7059
7072
  var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
7060
7073
  var UNIT_MS = {
7061
7074
  s: 1e3,
@@ -7063,9 +7076,26 @@ var UNIT_MS = {
7063
7076
  h: 36e5,
7064
7077
  d: 864e5
7065
7078
  };
7066
- function schedulesPath() {
7067
- return join14(appDataDir(), "schedules.json");
7068
- }
7079
+ var DAY_ALIASES = {
7080
+ sun: 0,
7081
+ sunday: 0,
7082
+ mon: 1,
7083
+ monday: 1,
7084
+ tue: 2,
7085
+ tues: 2,
7086
+ tuesday: 2,
7087
+ wed: 3,
7088
+ wednesday: 3,
7089
+ thu: 4,
7090
+ thur: 4,
7091
+ thurs: 4,
7092
+ thursday: 4,
7093
+ fri: 5,
7094
+ friday: 5,
7095
+ sat: 6,
7096
+ saturday: 6
7097
+ };
7098
+ var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/;
7069
7099
  function parseDurationMs(every) {
7070
7100
  const m = every.trim().match(DURATION_RE);
7071
7101
  if (!m) return null;
@@ -7074,18 +7104,72 @@ function parseDurationMs(every) {
7074
7104
  const unit = m[2].toLowerCase();
7075
7105
  return n * UNIT_MS[unit];
7076
7106
  }
7077
- function defaultScheduleName(prompt) {
7078
- const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
7079
- if (!line) return "Untitled automation";
7080
- return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
7081
- }
7082
- function resolveScheduleThreadId(raw, env = process.env) {
7083
- const value = raw?.trim() || "";
7084
- if (!value) return null;
7085
- if (value.toLowerCase() === "self") {
7086
- return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
7107
+ function dayIndex(token) {
7108
+ const text3 = token.trim().toLowerCase();
7109
+ if (/^[0-6]$/.test(text3)) return Number(text3);
7110
+ return DAY_ALIASES[text3];
7111
+ }
7112
+ function normalizeWeekdays(days) {
7113
+ const set = /* @__PURE__ */ new Set();
7114
+ for (const day of days) {
7115
+ if (!Number.isInteger(day) || day < 0 || day > 6) {
7116
+ throw new Error("Days must be 0 (Sunday) through 6 (Saturday)");
7117
+ }
7118
+ set.add(day);
7119
+ }
7120
+ if (set.size === 0) throw new Error("Pick at least one day");
7121
+ return [...set].sort((a, b) => a - b);
7122
+ }
7123
+ function parseWeeklyDays(raw) {
7124
+ const text3 = raw.trim().toLowerCase();
7125
+ if (!text3) throw new Error("Pick at least one day");
7126
+ if (text3 === "daily" || text3 === "all" || text3 === "every day" || text3 === "everyday") {
7127
+ return [0, 1, 2, 3, 4, 5, 6];
7128
+ }
7129
+ if (text3 === "weekdays" || text3 === "weekday") return [1, 2, 3, 4, 5];
7130
+ if (text3 === "weekends" || text3 === "weekend") return [0, 6];
7131
+ const days = [];
7132
+ for (const part of text3.split(/[\s,]+/).filter(Boolean)) {
7133
+ const range = part.split("-");
7134
+ if (range.length === 2 && range[0] && range[1]) {
7135
+ const start = dayIndex(range[0]);
7136
+ const end = dayIndex(range[1]);
7137
+ if (start === void 0 || end === void 0) {
7138
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
7139
+ }
7140
+ if (start <= end) {
7141
+ for (let d = start; d <= end; d++) days.push(d);
7142
+ } else {
7143
+ for (let d = start; d <= 6; d++) days.push(d);
7144
+ for (let d = 0; d <= end; d++) days.push(d);
7145
+ }
7146
+ continue;
7147
+ }
7148
+ const n = dayIndex(part);
7149
+ if (n === void 0) {
7150
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
7151
+ }
7152
+ days.push(n);
7087
7153
  }
7088
- return value;
7154
+ return normalizeWeekdays(days);
7155
+ }
7156
+ function normalizeWeeklyTime(raw) {
7157
+ const m = raw.trim().match(TIME_RE);
7158
+ if (!m) throw new Error(`Invalid time: ${raw} (use HH:mm, 24-hour)`);
7159
+ return `${m[1]}:${m[2]}`;
7160
+ }
7161
+ function formatWeeklyDays(days) {
7162
+ const key = normalizeWeekdays(days).join(",");
7163
+ if (key === "0,1,2,3,4,5,6") return "daily";
7164
+ if (key === "1,2,3,4,5") return "weekdays";
7165
+ if (key === "0,6") return "weekends";
7166
+ return normalizeWeekdays(days).map((day) => WEEKDAY_LABELS[day]).join(", ");
7167
+ }
7168
+ function weeklyCronExpr(when) {
7169
+ const time = normalizeWeeklyTime(when.time);
7170
+ const [hh, mm] = time.split(":");
7171
+ const days = normalizeWeekdays(when.days);
7172
+ return `${Number(mm)} ${Number(hh)} * * ${days.join(",")}`;
7089
7173
  }
7090
7174
  function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
7091
7175
  if (when.kind === "once") {
@@ -7102,6 +7186,12 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
7102
7186
  }
7103
7187
  return new Date(from.getTime() + ms);
7104
7188
  }
7189
+ if (when.kind === "weekly") {
7190
+ return computeNextRunAt(
7191
+ { kind: "cron", expr: weeklyCronExpr(when), tz: when.tz },
7192
+ from
7193
+ );
7194
+ }
7105
7195
  try {
7106
7196
  const job = new Cron(when.expr, {
7107
7197
  timezone: when.tz?.trim() || void 0,
@@ -7120,12 +7210,14 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
7120
7210
  function formatScheduleWhen(when) {
7121
7211
  if (when.kind === "once") return `once at ${when.at}`;
7122
7212
  if (when.kind === "every") return `every ${when.every}`;
7213
+ if (when.kind === "weekly") {
7214
+ const label = `${formatWeeklyDays(when.days)} at ${normalizeWeeklyTime(when.time)}`;
7215
+ const tz2 = when.tz?.trim();
7216
+ return tz2 ? `${label} (${tz2})` : label;
7217
+ }
7123
7218
  const tz = when.tz?.trim();
7124
7219
  return tz ? `cron ${when.expr} (${tz})` : `cron ${when.expr}`;
7125
7220
  }
7126
- function nowIso(d = /* @__PURE__ */ new Date()) {
7127
- return d.toISOString();
7128
- }
7129
7221
  function normalizeWhen(raw) {
7130
7222
  if (!raw || typeof raw !== "object") {
7131
7223
  throw new Error("Schedule is missing `when`");
@@ -7145,7 +7237,36 @@ function normalizeWhen(raw) {
7145
7237
  tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
7146
7238
  };
7147
7239
  }
7148
- throw new Error("Schedule `when` must be once, every, or cron");
7240
+ if (kind === "weekly" && Array.isArray(rec.days) && typeof rec.time === "string") {
7241
+ return {
7242
+ kind: "weekly",
7243
+ days: normalizeWeekdays(rec.days.map((day) => Number(day))),
7244
+ time: normalizeWeeklyTime(rec.time),
7245
+ tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
7246
+ };
7247
+ }
7248
+ throw new Error("Schedule `when` must be once, every, weekly, or cron");
7249
+ }
7250
+
7251
+ // src/store/schedules.ts
7252
+ function schedulesPath() {
7253
+ return join14(appDataDir(), "schedules.json");
7254
+ }
7255
+ function defaultScheduleName(prompt) {
7256
+ const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
7257
+ if (!line) return "Untitled automation";
7258
+ return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
7259
+ }
7260
+ function resolveScheduleThreadId(raw, env = process.env) {
7261
+ const value = raw?.trim() || "";
7262
+ if (!value) return null;
7263
+ if (value.toLowerCase() === "self") {
7264
+ return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
7265
+ }
7266
+ return value;
7267
+ }
7268
+ function nowIso(d = /* @__PURE__ */ new Date()) {
7269
+ return d.toISOString();
7149
7270
  }
7150
7271
  var ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
7151
7272
  "transcript",
@@ -7203,6 +7324,7 @@ function normalizeTask(raw) {
7203
7324
  planMode: normalizeOptionalBool(raw.planMode),
7204
7325
  autonomy: normalizeAutonomy(raw.autonomy),
7205
7326
  attachments: normalizeAttachments(raw.attachments),
7327
+ archiveChat: raw.archiveChat !== false,
7206
7328
  nextRunAt: raw.nextRunAt,
7207
7329
  lastRunAt: raw.lastRunAt ?? null,
7208
7330
  lastThreadId: raw.lastThreadId ?? null,
@@ -7254,6 +7376,7 @@ function buildTask(input, now) {
7254
7376
  planMode: normalizeOptionalBool(input.planMode),
7255
7377
  autonomy: normalizeAutonomy(input.autonomy),
7256
7378
  attachments: normalizeAttachments(input.attachments),
7379
+ archiveChat: input.archiveChat !== false,
7257
7380
  nextRunAt,
7258
7381
  lastRunAt: null,
7259
7382
  lastThreadId: null,
@@ -7318,6 +7441,7 @@ function updateSchedule(id, patch) {
7318
7441
  planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
7319
7442
  autonomy: patch.autonomy !== void 0 ? normalizeAutonomy(patch.autonomy) : prev.autonomy,
7320
7443
  attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
7444
+ archiveChat: patch.archiveChat !== void 0 ? patch.archiveChat !== false : prev.archiveChat,
7321
7445
  nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
7322
7446
  lastError: whenChanged ? null : prev.lastError,
7323
7447
  updatedAt: nowIso()
@@ -7365,7 +7489,7 @@ function formatScheduledPrompt(name, prompt) {
7365
7489
  ${prompt}`;
7366
7490
  }
7367
7491
  async function defaultDeps() {
7368
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-B4STIW4B.js");
7492
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-7VTAUY24.js");
7369
7493
  const orch = getOrchestrator2();
7370
7494
  return {
7371
7495
  findThread: (id) => findThreadByRef(id),
@@ -7432,6 +7556,7 @@ async function fireSchedule(id) {
7432
7556
  });
7433
7557
  }
7434
7558
  const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
7559
+ const archiveFresh = schedule.archiveChat !== false && !pinReplacement;
7435
7560
  const created = await deps.startOrchestration({
7436
7561
  goal: prompt,
7437
7562
  agent: pickAgent(schedule),
@@ -7441,7 +7566,8 @@ async function fireSchedule(id) {
7441
7566
  ...schedule.planMode != null ? { planMode: schedule.planMode } : {},
7442
7567
  ...schedule.autonomy ? { autonomy: schedule.autonomy } : {},
7443
7568
  attachments: schedule.attachments,
7444
- ...pinReplacement ? {} : { ephemeralAutomationId: schedule.id, ephemeralTitle: schedule.name }
7569
+ ...archiveFresh ? { ephemeralAutomationId: schedule.id } : {},
7570
+ ephemeralTitle: schedule.name
7445
7571
  });
7446
7572
  if (pinReplacement) {
7447
7573
  updateSchedule(schedule.id, { threadId: created.id });
@@ -7731,7 +7857,7 @@ var Orchestrator = class {
7731
7857
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
7732
7858
  });
7733
7859
  }
7734
- const { autoCleanupOrphansEnabled } = await import("./app-settings-P3H4SEIK.js");
7860
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-XIF4EC4K.js");
7735
7861
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
7736
7862
  await cleanupOrphanWorktrees({ repoPaths });
7737
7863
  }
@@ -8125,7 +8251,7 @@ var Orchestrator = class {
8125
8251
  }
8126
8252
  await setup;
8127
8253
  if (skipSetup) return;
8128
- const { autoRunAfterSetupEnabled } = await import("./app-settings-P3H4SEIK.js");
8254
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-XIF4EC4K.js");
8129
8255
  if (autoRunAfterSetupEnabled()) {
8130
8256
  try {
8131
8257
  await this.startDev(threadId);
@@ -8503,7 +8629,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
8503
8629
  }
8504
8630
  const isBrightsy = fresh.agent === "brightsy";
8505
8631
  const isOrchestration = isOrchestratorThread(fresh);
8506
- const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-P3H4SEIK.js");
8632
+ const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-XIF4EC4K.js");
8507
8633
  const gitAuthMode = getGithubGitAuthMode();
8508
8634
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
8509
8635
  githubSlug: turnOrigin?.forge === "github" ? turnOrigin.slug : await withTimeout(resolveGithubRepoSlug(fresh.worktreePath), 8e3, "resolveGithubRepoSlug").catch(() => null),
@@ -9830,7 +9956,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
9830
9956
  if (Object.keys(patch).length === 0) continue;
9831
9957
  updateThread(t.id, patch);
9832
9958
  }
9833
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-P3H4SEIK.js");
9959
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-XIF4EC4K.js");
9834
9960
  const latest = this.requireThread(thread.id);
9835
9961
  if (!shouldAutoArchiveOnPrMerge({
9836
9962
  previousPrState: prevState || null,
@@ -10154,7 +10280,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
10154
10280
  await runArchiveScript(thread.repoPath, thread.worktreePath);
10155
10281
  } catch {
10156
10282
  }
10157
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-P3H4SEIK.js");
10283
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-XIF4EC4K.js");
10158
10284
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
10159
10285
  await removeWorktree(thread.repoPath, thread.worktreePath, {
10160
10286
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -10292,7 +10418,7 @@ async function startOrchestration(opts) {
10292
10418
  sourceRef: "default",
10293
10419
  ...createOpts
10294
10420
  }).catch(async () => {
10295
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-PDNPL5CQ.js");
10421
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-ZDA2UJ7C.js");
10296
10422
  const repo = await resolveRepoRoot2(repoPath);
10297
10423
  const def = await resolveDefaultBranch2(repo);
10298
10424
  return orch.createThread({
@@ -10358,12 +10484,18 @@ export {
10358
10484
  emptyRunLog,
10359
10485
  sliceRunLogOutput,
10360
10486
  readRunLog,
10361
- schedulesPath,
10487
+ WEEKDAY_LABELS,
10362
10488
  parseDurationMs,
10363
- defaultScheduleName,
10364
- resolveScheduleThreadId,
10489
+ normalizeWeekdays,
10490
+ parseWeeklyDays,
10491
+ normalizeWeeklyTime,
10492
+ formatWeeklyDays,
10493
+ weeklyCronExpr,
10365
10494
  computeNextRunAt,
10366
10495
  formatScheduleWhen,
10496
+ schedulesPath,
10497
+ defaultScheduleName,
10498
+ resolveScheduleThreadId,
10367
10499
  listSchedules,
10368
10500
  hasEnabledSchedules,
10369
10501
  scheduleNeedsCaffeinate,
@@ -2622,6 +2622,7 @@ export {
2622
2622
  HISTORY_DISCARDED_TEXT,
2623
2623
  clampHistoryMaxCount,
2624
2624
  clampHistoryMaxDays,
2625
+ archivedUpdatedMs,
2625
2626
  isHistoryRetentionStub,
2626
2627
  historyRetentionStubMessage,
2627
2628
  planHistoryRetention,
@@ -4,14 +4,14 @@ import {
4
4
  normalizeAbleTimeCredential,
5
5
  normalizeAbleTimeHost,
6
6
  rewriteAbleTimeError
7
- } from "./chunk-V76MQ7MH.js";
7
+ } from "./chunk-ODH3XHMI.js";
8
8
  import {
9
9
  httpFetch
10
10
  } from "./chunk-JAT3JFOS.js";
11
11
  import {
12
12
  getAbleTimeAccessToken,
13
13
  getAbleTimeHost
14
- } from "./chunk-FZKAAAEB.js";
14
+ } from "./chunk-EJ66LEKH.js";
15
15
 
16
16
  // src/integrations/abletime-rest.ts
17
17
  var ABLETIME_REST_PREFIX = "/api/public/v2";
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  getAbleTimeHost,
8
8
  isAbleTimeConnected
9
- } from "./chunk-IFU4W4C4.js";
9
+ } from "./chunk-OIHRC2ZJ.js";
10
10
 
11
11
  // src/integrations/abletime-mcp.ts
12
12
  var DEFAULT_ABLETIME_HOST = "https://track.abletime.com";
@@ -155,7 +155,7 @@ function textsFromUnknown(value) {
155
155
  return "";
156
156
  }
157
157
  async function abletimeMcpRequest(method, params, opts) {
158
- const rawToken = opts?.token ?? await import("./abletime-oauth-P3KPPJE4.js").then((m) => m.ensureAbleTimeAccessToken());
158
+ const rawToken = opts?.token ?? await import("./abletime-oauth-4TTCR3CT.js").then((m) => m.ensureAbleTimeAccessToken());
159
159
  if (!rawToken?.trim()) {
160
160
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
161
161
  }
@@ -226,9 +226,9 @@ async function callAbleTimeTool(name, args = {}, opts) {
226
226
  if (!opts?.token && !isAbleTimeConnected()) {
227
227
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
228
228
  }
229
- const rawToken = opts?.token ?? await import("./abletime-oauth-P3KPPJE4.js").then((m) => m.ensureAbleTimeAccessToken());
229
+ const rawToken = opts?.token ?? await import("./abletime-oauth-4TTCR3CT.js").then((m) => m.ensureAbleTimeAccessToken());
230
230
  if (rawToken && ableTimeUsesRest(rawToken)) {
231
- const { callAbleTimeRestTool } = await import("./abletime-rest-DQ3PNKXP.js");
231
+ const { callAbleTimeRestTool } = await import("./abletime-rest-3TG4G7LW.js");
232
232
  return callAbleTimeRestTool(name, args, { ...opts, token: rawToken });
233
233
  }
234
234
  const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
@@ -244,7 +244,7 @@ async function callAbleTimeTool(name, args = {}, opts) {
244
244
  return await call();
245
245
  }
246
246
  if (rawToken && ableTimeUsesRest(rawToken) && isAbleTimeRejectedCredential(message)) {
247
- const { callAbleTimeRestTool } = await import("./abletime-rest-DQ3PNKXP.js");
247
+ const { callAbleTimeRestTool } = await import("./abletime-rest-3TG4G7LW.js");
248
248
  return callAbleTimeRestTool(name, args, { ...opts, token: rawToken });
249
249
  }
250
250
  throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
@@ -25,7 +25,7 @@ import {
25
25
  import {
26
26
  getGithubGitAuthMode,
27
27
  getGithubPat
28
- } from "./chunk-FZKAAAEB.js";
28
+ } from "./chunk-EJ66LEKH.js";
29
29
  import {
30
30
  worktreesRoot
31
31
  } from "./chunk-H4UEFJRI.js";
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  loadAppSettings,
6
6
  saveAppSettings
7
- } from "./chunk-FZKAAAEB.js";
7
+ } from "./chunk-EJ66LEKH.js";
8
8
 
9
9
  // src/integrations/optional-services.ts
10
10
  var OPTIONAL_SERVICE_IDS = ["vercel", "supabase", "posthog", "sentry"];
@@ -273,7 +273,7 @@ async function connectedCliPresence(integrations) {
273
273
  const ids = connectedOptionalServices(integrations).filter((spec) => spec.cli).map((spec) => spec.id);
274
274
  if (ids.length === 0) return void 0;
275
275
  try {
276
- const { optionalCliPresence } = await import("./optional-cli-2YDQQCCD.js");
276
+ const { optionalCliPresence } = await import("./optional-cli-Y3Z3GIPZ.js");
277
277
  return await optionalCliPresence(ids);
278
278
  } catch {
279
279
  return void 0;
@@ -5,7 +5,7 @@ import {
5
5
  resolveGithubRepoSlug,
6
6
  resolveOriginRemote,
7
7
  resolveRepoRoot
8
- } from "./chunk-UWJUARFA.js";
8
+ } from "./chunk-FUFNMUNR.js";
9
9
  import {
10
10
  allocateTeamName,
11
11
  createEmptyThread,
@@ -31,7 +31,7 @@ import {
31
31
  resolveThreadDefaults,
32
32
  resolveViewerProfile,
33
33
  resolveViewerProfileForRepo
34
- } from "./chunk-FZKAAAEB.js";
34
+ } from "./chunk-EJ66LEKH.js";
35
35
  import {
36
36
  appDataDir,
37
37
  globalAgentCwd,
@@ -402,7 +402,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
402
402
  "- 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_projects (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
403
403
  "- list_env / set_env / delete_env \u2014 Settings \u2192 Environment. scope=project requires repoPath from list_projects and stores the key under that project prefix (the project name, uppercased, plus _). APPLE_ID for sideboard is SIDEBOARD_APPLE_ID. scope=account is shared by every project and is not prefixed. list_env returns names only.",
404
404
  "- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for phone remote / 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.",
405
- "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 automations that send a prompt to a chat (chatId or self) or start a new Global chat (omit chatId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without chatId open a new chat each run and archive it when that run finishes (History keeps the latest run for that automation). Worktree agents have the same tools (`chatId=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 automations are enabled (or call set_caffeinate). The desktop lists them in the sidebar after Board.",
405
+ "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 automations that send a prompt to a chat (chatId or self) or start a new Global chat (omit chatId). One-shot `at`, interval `every` (15m/1h/6h/1d), clock time `time` + `days` (07:30 and weekdays, or mon,wed), or 5-field `cron`. Recurring jobs without chatId open a new chat each run and archive it when that run finishes unless archiveChat is false (leave the chat open to keep talking). History keeps the last 5 archived runs for that automation. Worktree agents have the same tools (`chatId=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 automations are enabled (or call set_caffeinate). The desktop lists them in the sidebar after Board.",
406
406
  "Projects:",
407
407
  "- add_project / remove_project \u2014 register or unregister a git repo",
408
408
  "Workspaces and chats:",
@@ -499,7 +499,7 @@ function coordinatorTurnReminder(opts) {
499
499
  const goal = opts.goal?.trim();
500
500
  return [
501
501
  "Sideboard Orchestration (mandatory):",
502
- "- You oversee worktree agents from a synthetic empty cwd (not a git repo). Follow AGENTS.md / CLAUDE.md.",
502
+ "- You oversee worktree agents from a synthetic empty cwd (not a git repo). Follow AGENTS.md (CLAUDE.md imports it).",
503
503
  `- YOUR orchestration chat id is ${opts.parentId} \u2014 pass parentChatId="${opts.parentId}" on create_workspace, or omit it.`,
504
504
  goal ? `- Goal / title: ${goal}` : null,
505
505
  accountDefaultsPlaybookLine(),
@@ -563,10 +563,9 @@ function ensureGlobalCoordinatorCwd(opts) {
563
563
  "Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_chat`). If the user gave a goal (Greptile 5/5, CI green), pass that goal through so they watch-fix-push until it lands \u2014 do not start that loop on a plain push, and do not tell the human to poll. Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
564
564
  ].join("\n");
565
565
  try {
566
- writeFileSync2(join2(dir, "CLAUDE.md"), `${body}
567
- `, "utf8");
568
566
  writeFileSync2(join2(dir, "AGENTS.md"), `${body}
569
567
  `, "utf8");
568
+ writeFileSync2(join2(dir, "CLAUDE.md"), "@AGENTS.md\n", "utf8");
570
569
  } catch {
571
570
  }
572
571
  return dir;
@@ -590,7 +589,7 @@ function coordinatorSystemPrompt(opts) {
590
589
  ...intro,
591
590
  "You operate across ALL registered workspaces below.",
592
591
  "You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
593
- "Follow AGENTS.md / CLAUDE.md in this cwd for the fleet playbook (they are the same document).",
592
+ "Follow AGENTS.md in this cwd for the fleet playbook. CLAUDE.md is `@AGENTS.md` so Cursor does not load a second copy.",
594
593
  `YOUR orchestration chat id is ${opts.parentId} \u2014 pass parentChatId="${opts.parentId}" on create_workspace, or omit parentChatId (Sideboard binds it). Never invent a uuid.`,
595
594
  `Goal: ${opts.goal}`,
596
595
  "Registered workspaces:",