@sideboard-ai/core 0.1.322 → 0.1.323

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-PFVYC36J.js → agents-2ZXORBEJ.js} +5 -5
  8. package/dist/{agents-ROFDNE7Z.js → agents-SPRAOMGI.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-45UB2J47.js → chunk-4C5PQ7JA.js} +160 -46
  12. package/dist/{chunk-XDQSHSL6.js → chunk-53SBTCKF.js} +4 -4
  13. package/dist/{chunk-BPDVHB4U.js → chunk-5GDJSFCT.js} +2 -2
  14. package/dist/{chunk-H6GXYV5Q.js → chunk-6W3XRKD5.js} +3 -3
  15. package/dist/{chunk-RHJKZOPQ.js → chunk-74OPNF7U.js} +2 -2
  16. package/dist/{chunk-BTIXTH4N.js → chunk-7PVT73H6.js} +3 -3
  17. package/dist/{chunk-JNEFEA2A.js → chunk-D3VSKNVA.js} +4 -4
  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-LD2DXHBP.js → chunk-JDX3474T.js} +182 -50
  24. package/dist/{chunk-SPMIZSKH.js → chunk-MYVKOIIY.js} +3 -3
  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-RTVS3M74.js} +4 -4
  33. package/dist/{chunk-RGUZPMTF.js → chunk-SKO7IGKU.js} +4 -4
  34. package/dist/{chunk-72PUPPV2.js → chunk-Y3WREIPQ.js} +3 -3
  35. package/dist/{conductor-XLXJU24U.js → conductor-52KTQ3IM.js} +4 -4
  36. package/dist/{conductor-CNT3VL3S.js → conductor-FB7MKLRN.js} +4 -4
  37. package/dist/{coordinator-prompt-D5EE2YTV.js → coordinator-prompt-7HXV7HJ5.js} +3 -3
  38. package/dist/{coordinator-prompt-TU24TQ4I.js → coordinator-prompt-7W7WPY4X.js} +3 -3
  39. package/dist/{global-workspace-UPMXDQM7.js → global-workspace-5BWUTSPJ.js} +3 -3
  40. package/dist/{global-workspace-743TPQNF.js → global-workspace-EI3KBGZE.js} +3 -3
  41. package/dist/index.cjs +267 -57
  42. package/dist/index.d.cts +45 -17
  43. package/dist/index.d.ts +45 -17
  44. package/dist/index.js +113 -36
  45. package/dist/mcp/run-stdio.cjs +242 -57
  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-2RDE2YS3.js} +8 -8
  50. package/dist/{orchestrator-B4STIW4B.js → orchestrator-RMPZETOO.js} +8 -8
  51. package/dist/{workspaces-75FD4QUR.js → workspaces-LJQS73K6.js} +3 -3
  52. package/dist/{workspaces-47WQL5IG.js → workspaces-OPQ5NMM6.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
@@ -10641,7 +10641,7 @@ var init_coordinator_prompt = __esm({
10641
10641
  "- 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.",
10642
10642
  "- 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.",
10643
10643
  "- 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.",
10644
- "- 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.",
10644
+ "- 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.",
10645
10645
  "Projects:",
10646
10646
  "- add_project / remove_project \u2014 register or unregister a git repo",
10647
10647
  "Workspaces and chats:",
@@ -20515,12 +20515,12 @@ function ephemeralRunReadyToArchive(thread) {
20515
20515
  }
20516
20516
  function markEphemeralAutomation(threadId, opts) {
20517
20517
  const id = opts.ephemeralAutomationId?.trim();
20518
+ const title = opts.ephemeralTitle?.trim();
20518
20519
  const current = readThread(threadId);
20519
20520
  if (!current) throw new Error(`Thread not found: ${threadId}`);
20520
- if (!id) return current;
20521
- const title = opts.ephemeralTitle?.trim();
20521
+ if (!id && !title) return current;
20522
20522
  return updateThread(threadId, {
20523
- ephemeralAutomationId: id,
20523
+ ...id ? { ephemeralAutomationId: id } : {},
20524
20524
  ...title ? { title, userSetTitle: true } : {}
20525
20525
  });
20526
20526
  }
@@ -20555,11 +20555,19 @@ function clearEphemeralAutomation(threadId) {
20555
20555
  function forgetOlderEphemeralRuns(automationId, keepId) {
20556
20556
  const id = automationId.trim();
20557
20557
  if (!id || !keepId) return [];
20558
+ const archived = listThreads({ includeArchived: true }).filter(
20559
+ (thread) => thread.status === "archived" && thread.ephemeralAutomationId === id
20560
+ );
20561
+ const ranked = [...archived].sort((a, b) => {
20562
+ if (a.id === keepId) return -1;
20563
+ if (b.id === keepId) return 1;
20564
+ return archivedUpdatedMs(b) - archivedUpdatedMs(a);
20565
+ });
20566
+ const keep = new Set(ranked.slice(0, EPHEMERAL_AUTOMATION_KEEP).map((thread) => thread.id));
20567
+ keep.add(keepId);
20558
20568
  const removed = [];
20559
- for (const thread of listThreads({ includeArchived: true })) {
20560
- if (thread.id === keepId) continue;
20561
- if (thread.status !== "archived") continue;
20562
- if (thread.ephemeralAutomationId !== id) continue;
20569
+ for (const thread of archived) {
20570
+ if (keep.has(thread.id)) continue;
20563
20571
  try {
20564
20572
  deleteThreadRecord(thread.id);
20565
20573
  removed.push(thread.id);
@@ -20568,10 +20576,13 @@ function forgetOlderEphemeralRuns(automationId, keepId) {
20568
20576
  }
20569
20577
  return removed;
20570
20578
  }
20579
+ var EPHEMERAL_AUTOMATION_KEEP;
20571
20580
  var init_ephemeral_automation = __esm({
20572
20581
  "src/orchestrator/ephemeral-automation.ts"() {
20573
20582
  "use strict";
20583
+ init_history_retention();
20574
20584
  init_thread_store();
20585
+ EPHEMERAL_AUTOMATION_KEEP = 5;
20575
20586
  }
20576
20587
  });
20577
20588
 
@@ -26727,7 +26738,7 @@ function formatWorktreeDirective(thread, opts) {
26727
26738
  lines.push(formatDevAccessHint(opts?.activeRuns));
26728
26739
  lines.push("");
26729
26740
  lines.push(
26730
- "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."
26741
+ "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."
26731
26742
  );
26732
26743
  lines.push(
26733
26744
  "`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
@@ -26747,7 +26758,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
26747
26758
  return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
26748
26759
  }
26749
26760
  function formatWorktreeReminder() {
26750
- 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).";
26761
+ 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).";
26751
26762
  }
26752
26763
  function formatClaudeSubagentReminder() {
26753
26764
  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.';
@@ -27858,10 +27869,7 @@ var init_caffeinate_hold = __esm({
27858
27869
  }
27859
27870
  });
27860
27871
 
27861
- // src/store/schedules.ts
27862
- function schedulesPath() {
27863
- return (0, import_node_path65.join)(appDataDir(), "schedules.json");
27864
- }
27872
+ // src/store/schedule-when.ts
27865
27873
  function parseDurationMs(every) {
27866
27874
  const m = every.trim().match(DURATION_RE);
27867
27875
  if (!m) return null;
@@ -27870,18 +27878,65 @@ function parseDurationMs(every) {
27870
27878
  const unit = m[2].toLowerCase();
27871
27879
  return n * UNIT_MS[unit];
27872
27880
  }
27873
- function defaultScheduleName(prompt) {
27874
- const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
27875
- if (!line) return "Untitled automation";
27876
- return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
27877
- }
27878
- function resolveScheduleThreadId(raw, env = process.env) {
27879
- const value = raw?.trim() || "";
27880
- if (!value) return null;
27881
- if (value.toLowerCase() === "self") {
27882
- return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
27881
+ function dayIndex(token) {
27882
+ const text7 = token.trim().toLowerCase();
27883
+ if (/^[0-6]$/.test(text7)) return Number(text7);
27884
+ return DAY_ALIASES[text7];
27885
+ }
27886
+ function normalizeWeekdays(days) {
27887
+ const set = /* @__PURE__ */ new Set();
27888
+ for (const day of days) {
27889
+ if (!Number.isInteger(day) || day < 0 || day > 6) {
27890
+ throw new Error("Days must be 0 (Sunday) through 6 (Saturday)");
27891
+ }
27892
+ set.add(day);
27893
+ }
27894
+ if (set.size === 0) throw new Error("Pick at least one day");
27895
+ return [...set].sort((a, b) => a - b);
27896
+ }
27897
+ function parseWeeklyDays(raw) {
27898
+ const text7 = raw.trim().toLowerCase();
27899
+ if (!text7) throw new Error("Pick at least one day");
27900
+ if (text7 === "daily" || text7 === "all" || text7 === "every day" || text7 === "everyday") {
27901
+ return [0, 1, 2, 3, 4, 5, 6];
27902
+ }
27903
+ if (text7 === "weekdays" || text7 === "weekday") return [1, 2, 3, 4, 5];
27904
+ if (text7 === "weekends" || text7 === "weekend") return [0, 6];
27905
+ const days = [];
27906
+ for (const part of text7.split(/[\s,]+/).filter(Boolean)) {
27907
+ const range = part.split("-");
27908
+ if (range.length === 2 && range[0] && range[1]) {
27909
+ const start = dayIndex(range[0]);
27910
+ const end = dayIndex(range[1]);
27911
+ if (start === void 0 || end === void 0) {
27912
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
27913
+ }
27914
+ if (start <= end) {
27915
+ for (let d = start; d <= end; d++) days.push(d);
27916
+ } else {
27917
+ for (let d = start; d <= 6; d++) days.push(d);
27918
+ for (let d = 0; d <= end; d++) days.push(d);
27919
+ }
27920
+ continue;
27921
+ }
27922
+ const n = dayIndex(part);
27923
+ if (n === void 0) {
27924
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
27925
+ }
27926
+ days.push(n);
27883
27927
  }
27884
- return value;
27928
+ return normalizeWeekdays(days);
27929
+ }
27930
+ function normalizeWeeklyTime(raw) {
27931
+ const m = raw.trim().match(TIME_RE);
27932
+ if (!m) throw new Error(`Invalid time: ${raw} (use HH:mm, 24-hour)`);
27933
+ return `${m[1]}:${m[2]}`;
27934
+ }
27935
+ function weeklyCronExpr(when) {
27936
+ const time = normalizeWeeklyTime(when.time);
27937
+ const [hh, mm] = time.split(":");
27938
+ const days = normalizeWeekdays(when.days);
27939
+ return `${Number(mm)} ${Number(hh)} * * ${days.join(",")}`;
27885
27940
  }
27886
27941
  function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
27887
27942
  if (when.kind === "once") {
@@ -27898,6 +27953,12 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
27898
27953
  }
27899
27954
  return new Date(from.getTime() + ms);
27900
27955
  }
27956
+ if (when.kind === "weekly") {
27957
+ return computeNextRunAt(
27958
+ { kind: "cron", expr: weeklyCronExpr(when), tz: when.tz },
27959
+ from
27960
+ );
27961
+ }
27901
27962
  try {
27902
27963
  const job = new import_croner.Cron(when.expr, {
27903
27964
  timezone: when.tz?.trim() || void 0,
@@ -27913,9 +27974,6 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
27913
27974
  throw new Error(`Invalid cron: ${message}`);
27914
27975
  }
27915
27976
  }
27916
- function nowIso2(d = /* @__PURE__ */ new Date()) {
27917
- return d.toISOString();
27918
- }
27919
27977
  function normalizeWhen(raw) {
27920
27978
  if (!raw || typeof raw !== "object") {
27921
27979
  throw new Error("Schedule is missing `when`");
@@ -27935,7 +27993,70 @@ function normalizeWhen(raw) {
27935
27993
  tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
27936
27994
  };
27937
27995
  }
27938
- throw new Error("Schedule `when` must be once, every, or cron");
27996
+ if (kind === "weekly" && Array.isArray(rec.days) && typeof rec.time === "string") {
27997
+ return {
27998
+ kind: "weekly",
27999
+ days: normalizeWeekdays(rec.days.map((day) => Number(day))),
28000
+ time: normalizeWeeklyTime(rec.time),
28001
+ tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
28002
+ };
28003
+ }
28004
+ throw new Error("Schedule `when` must be once, every, weekly, or cron");
28005
+ }
28006
+ var import_croner, DURATION_RE, UNIT_MS, DAY_ALIASES, TIME_RE;
28007
+ var init_schedule_when = __esm({
28008
+ "src/store/schedule-when.ts"() {
28009
+ "use strict";
28010
+ import_croner = require("croner");
28011
+ DURATION_RE = /^(\d+)(s|m|h|d)$/i;
28012
+ UNIT_MS = {
28013
+ s: 1e3,
28014
+ m: 6e4,
28015
+ h: 36e5,
28016
+ d: 864e5
28017
+ };
28018
+ DAY_ALIASES = {
28019
+ sun: 0,
28020
+ sunday: 0,
28021
+ mon: 1,
28022
+ monday: 1,
28023
+ tue: 2,
28024
+ tues: 2,
28025
+ tuesday: 2,
28026
+ wed: 3,
28027
+ wednesday: 3,
28028
+ thu: 4,
28029
+ thur: 4,
28030
+ thurs: 4,
28031
+ thursday: 4,
28032
+ fri: 5,
28033
+ friday: 5,
28034
+ sat: 6,
28035
+ saturday: 6
28036
+ };
28037
+ TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/;
28038
+ }
28039
+ });
28040
+
28041
+ // src/store/schedules.ts
28042
+ function schedulesPath() {
28043
+ return (0, import_node_path65.join)(appDataDir(), "schedules.json");
28044
+ }
28045
+ function defaultScheduleName(prompt) {
28046
+ const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
28047
+ if (!line) return "Untitled automation";
28048
+ return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
28049
+ }
28050
+ function resolveScheduleThreadId(raw, env = process.env) {
28051
+ const value = raw?.trim() || "";
28052
+ if (!value) return null;
28053
+ if (value.toLowerCase() === "self") {
28054
+ return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
28055
+ }
28056
+ return value;
28057
+ }
28058
+ function nowIso2(d = /* @__PURE__ */ new Date()) {
28059
+ return d.toISOString();
27939
28060
  }
27940
28061
  function normalizeOptionalBool(raw) {
27941
28062
  if (raw === true || raw === false) return raw;
@@ -27985,6 +28106,7 @@ function normalizeTask(raw) {
27985
28106
  planMode: normalizeOptionalBool(raw.planMode),
27986
28107
  autonomy: normalizeAutonomy2(raw.autonomy),
27987
28108
  attachments: normalizeAttachments(raw.attachments),
28109
+ archiveChat: raw.archiveChat !== false,
27988
28110
  nextRunAt: raw.nextRunAt,
27989
28111
  lastRunAt: raw.lastRunAt ?? null,
27990
28112
  lastThreadId: raw.lastThreadId ?? null,
@@ -28036,6 +28158,7 @@ function buildTask(input, now) {
28036
28158
  planMode: normalizeOptionalBool(input.planMode),
28037
28159
  autonomy: normalizeAutonomy2(input.autonomy),
28038
28160
  attachments: normalizeAttachments(input.attachments),
28161
+ archiveChat: input.archiveChat !== false,
28039
28162
  nextRunAt,
28040
28163
  lastRunAt: null,
28041
28164
  lastThreadId: null,
@@ -28097,6 +28220,7 @@ function updateSchedule(id, patch) {
28097
28220
  planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
28098
28221
  autonomy: patch.autonomy !== void 0 ? normalizeAutonomy2(patch.autonomy) : prev.autonomy,
28099
28222
  attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
28223
+ archiveChat: patch.archiveChat !== void 0 ? patch.archiveChat !== false : prev.archiveChat,
28100
28224
  nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
28101
28225
  lastError: whenChanged ? null : prev.lastError,
28102
28226
  updatedAt: nowIso2()
@@ -28133,26 +28257,20 @@ function recordScheduleRun(id, result) {
28133
28257
  writeAll2(rows);
28134
28258
  return next;
28135
28259
  }
28136
- var import_node_crypto17, import_node_fs66, import_node_path65, import_croner, DURATION_RE, UNIT_MS, ATTACHMENT_KINDS;
28260
+ var import_node_crypto17, import_node_fs66, import_node_path65, ATTACHMENT_KINDS;
28137
28261
  var init_schedules = __esm({
28138
28262
  "src/store/schedules.ts"() {
28139
28263
  "use strict";
28140
28264
  import_node_crypto17 = require("crypto");
28141
28265
  import_node_fs66 = require("fs");
28142
28266
  import_node_path65 = require("path");
28143
- import_croner = require("croner");
28267
+ init_schedule_when();
28144
28268
  init_orchestrator_capable();
28145
28269
  init_thinking_effort();
28146
28270
  init_paths();
28147
28271
  init_private_file();
28148
28272
  init_app_settings();
28149
- DURATION_RE = /^(\d+)(s|m|h|d)$/i;
28150
- UNIT_MS = {
28151
- s: 1e3,
28152
- m: 6e4,
28153
- h: 36e5,
28154
- d: 864e5
28155
- };
28273
+ init_schedule_when();
28156
28274
  ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
28157
28275
  "transcript",
28158
28276
  "file",
@@ -28237,6 +28355,7 @@ async function fireSchedule(id) {
28237
28355
  });
28238
28356
  }
28239
28357
  const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
28358
+ const archiveFresh = schedule.archiveChat !== false && !pinReplacement;
28240
28359
  const created = await deps.startOrchestration({
28241
28360
  goal: prompt,
28242
28361
  agent: pickAgent(schedule),
@@ -28246,7 +28365,8 @@ async function fireSchedule(id) {
28246
28365
  ...schedule.planMode != null ? { planMode: schedule.planMode } : {},
28247
28366
  ...schedule.autonomy ? { autonomy: schedule.autonomy } : {},
28248
28367
  attachments: schedule.attachments,
28249
- ...pinReplacement ? {} : { ephemeralAutomationId: schedule.id, ephemeralTitle: schedule.name }
28368
+ ...archiveFresh ? { ephemeralAutomationId: schedule.id } : {},
28369
+ ephemeralTitle: schedule.name
28250
28370
  });
28251
28371
  if (pinReplacement) {
28252
28372
  updateSchedule(schedule.id, { threadId: created.id });
@@ -34791,18 +34911,79 @@ function fail4(err) {
34791
34911
  );
34792
34912
  }
34793
34913
  function parseWhen(args) {
34794
- const count = [args.at, args.every, args.cron].filter((v) => v?.trim()).length;
34795
- if (count !== 1) {
34796
- throw new Error("Pass exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron");
34914
+ const at = args.at?.trim();
34915
+ const every = args.every?.trim();
34916
+ const cron = args.cron?.trim();
34917
+ const time = args.time?.trim();
34918
+ const days = args.days?.trim();
34919
+ if (days && !time && !at && !every && !cron) {
34920
+ throw new Error("days needs time (HH:mm), for example time=07:30 days=weekdays");
34921
+ }
34922
+ const picked = [at, every, cron, time].filter(Boolean);
34923
+ if (picked.length !== 1) {
34924
+ throw new Error(
34925
+ "Pass exactly one of at (ISO datetime), every (15m/1h/6h/1d), time (HH:mm), or cron"
34926
+ );
34927
+ }
34928
+ if (at) return { kind: "once", at };
34929
+ if (every) return { kind: "every", every };
34930
+ if (time) {
34931
+ return {
34932
+ kind: "weekly",
34933
+ time,
34934
+ days: parseWeeklyDays(days || "daily"),
34935
+ tz: args.tz?.trim() || void 0
34936
+ };
34797
34937
  }
34798
- if (args.at?.trim()) return { kind: "once", at: args.at.trim() };
34799
- if (args.every?.trim()) return { kind: "every", every: args.every.trim() };
34800
34938
  return {
34801
34939
  kind: "cron",
34802
- expr: args.cron.trim(),
34940
+ expr: cron,
34803
34941
  tz: args.tz?.trim() || void 0
34804
34942
  };
34805
34943
  }
34944
+ function whenFromUpdate(id, args) {
34945
+ const at = args.at?.trim();
34946
+ const every = args.every?.trim();
34947
+ const cron = args.cron?.trim();
34948
+ const time = args.time?.trim();
34949
+ const days = args.days?.trim();
34950
+ const tzSet = args.tz !== void 0;
34951
+ if (!at && !every && !cron && !time && !days && !tzSet) return void 0;
34952
+ if ((time || days) && !at && !every && !cron) {
34953
+ const current = getSchedule(id);
34954
+ const weekly = current?.when.kind === "weekly" ? current.when : null;
34955
+ if (!weekly && !(time && days)) {
34956
+ const kind = current?.when.kind ?? "missing";
34957
+ throw new Error(
34958
+ `time and days change a days-at-a-time automation. This job is ${kind}. Pass time and days together to switch it, or at / every / cron to replace the cadence.`
34959
+ );
34960
+ }
34961
+ const clock = time || weekly?.time;
34962
+ if (!clock) {
34963
+ throw new Error("time (HH:mm) is required, for example time=07:30 days=weekdays");
34964
+ }
34965
+ const prevTz = current?.when.kind === "weekly" || current?.when.kind === "cron" ? current.when.tz : void 0;
34966
+ return {
34967
+ kind: "weekly",
34968
+ time: clock,
34969
+ days: days ? parseWeeklyDays(days) : weekly?.days ?? parseWeeklyDays("daily"),
34970
+ tz: tzSet ? args.tz?.trim() || void 0 : prevTz
34971
+ };
34972
+ }
34973
+ if (!at && !every && !cron && !time && !days && tzSet) {
34974
+ const current = getSchedule(id);
34975
+ if (current?.when.kind === "weekly" || current?.when.kind === "cron") {
34976
+ return { ...current.when, tz: args.tz?.trim() || void 0 };
34977
+ }
34978
+ return void 0;
34979
+ }
34980
+ return parseWhen(args);
34981
+ }
34982
+ function createdHint(schedule) {
34983
+ const awake = scheduleNeedsCaffeinate(schedule) ? " Caffeinate while automations are enabled is now on so the Mac can stay awake until this job is due." : "";
34984
+ const fresh = schedule.threadId ? "" : schedule.archiveChat === false ? " This job leaves the new chat open so you can keep talking." : " A new Global chat is archived when the run finishes (History keeps the last 5 runs). Pass archiveChat=false to leave it open.";
34985
+ return `Fires while Sideboard.app is running.${fresh}${awake}`;
34986
+ }
34806
34987
  function registerScheduleTools(server) {
34807
34988
  server.tool(
34808
34989
  "list_schedules",
@@ -34818,14 +34999,19 @@ function registerScheduleTools(server) {
34818
34999
  );
34819
35000
  server.tool(
34820
35001
  "create_schedule",
34821
- "Create a local automation 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 and archive it when that run finishes. History keeps the latest run for that automation. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
35002
+ "Create a local automation 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), time (HH:mm, with days such as weekdays or mon,wed), or cron (5-field). Recurring jobs without chatId open a new Global chat each run and archive it when that run finishes unless archiveChat is false (leave the chat open to keep talking, for example a morning start-my-day run). History keeps the last 5 archived runs for that automation. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
34822
35003
  {
34823
35004
  prompt: import_zod10.z.string().describe("User message / goal queued when the automation fires"),
34824
35005
  name: import_zod10.z.string().optional(),
34825
35006
  at: import_zod10.z.string().optional().describe("ISO datetime for a one-shot"),
34826
35007
  every: import_zod10.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
35008
+ time: import_zod10.z.string().optional().describe("HH:mm (24-hour) clock time. Omit days for every day."),
35009
+ days: import_zod10.z.string().optional().describe("daily, weekdays, weekends, or days such as mon,wed,fri or mon-fri. Use with time."),
34827
35010
  cron: import_zod10.z.string().optional().describe("5-field cron expression"),
34828
- tz: import_zod10.z.string().optional().describe("IANA timezone for cron (default: system)"),
35011
+ tz: import_zod10.z.string().optional().describe("IANA timezone for cron or time (default: system)"),
35012
+ archiveChat: import_zod10.z.boolean().optional().describe(
35013
+ "Archive a new Global chat when the run finishes. Default true. Pass false to leave the chat open."
35014
+ ),
34829
35015
  chatId: import_zod10.z.string().optional().describe(
34830
35016
  'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
34831
35017
  ),
@@ -34852,11 +35038,12 @@ function registerScheduleTools(server) {
34852
35038
  threadId,
34853
35039
  agent: args.agent,
34854
35040
  model: args.model,
35041
+ archiveChat: args.archiveChat,
34855
35042
  createdBy: "mcp"
34856
35043
  });
34857
35044
  return text6({
34858
35045
  schedule,
34859
- hint: scheduleNeedsCaffeinate(schedule) ? "Fires while Sideboard.app is running. Recurring jobs without chatId create a new orchestration chat each run and archive it when that run finishes (History keeps the latest). Caffeinate while automations 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 chatId create a new orchestration chat each run and archive it when that run finishes (History keeps the latest)."
35046
+ hint: createdHint(schedule)
34860
35047
  });
34861
35048
  } catch (err) {
34862
35049
  return fail4(err);
@@ -34865,15 +35052,18 @@ function registerScheduleTools(server) {
34865
35052
  );
34866
35053
  server.tool(
34867
35054
  "update_schedule",
34868
- "Update a local automation (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
35055
+ "Update a local automation (prompt, cadence, target chat, archiveChat, enabled). Pass id from list_schedules.",
34869
35056
  {
34870
35057
  id: import_zod10.z.string(),
34871
35058
  prompt: import_zod10.z.string().optional(),
34872
35059
  name: import_zod10.z.string().optional(),
34873
35060
  at: import_zod10.z.string().optional(),
34874
35061
  every: import_zod10.z.string().optional(),
35062
+ time: import_zod10.z.string().optional().describe("HH:mm clock time. Use with days, or alone to change the time of a weekly job."),
35063
+ days: import_zod10.z.string().optional().describe("daily, weekdays, weekends, or mon,wed,fri. Use with time, or alone on an existing weekly job."),
34875
35064
  cron: import_zod10.z.string().optional(),
34876
35065
  tz: import_zod10.z.string().optional(),
35066
+ archiveChat: import_zod10.z.boolean().optional().describe("Archive a new Global chat when the run finishes. False leaves it open."),
34877
35067
  chatId: import_zod10.z.string().optional().describe(
34878
35068
  'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
34879
35069
  ),
@@ -34884,13 +35074,7 @@ function registerScheduleTools(server) {
34884
35074
  },
34885
35075
  async (args) => {
34886
35076
  try {
34887
- const cadenceSet = [args.at, args.every, args.cron].some((v) => v?.trim());
34888
- const when = cadenceSet ? parseWhen({
34889
- at: args.at,
34890
- every: args.every,
34891
- cron: args.cron,
34892
- tz: args.tz
34893
- }) : void 0;
35077
+ const when = whenFromUpdate(args.id, args);
34894
35078
  let threadId;
34895
35079
  const rawChatId = args.chatId ?? args.threadId;
34896
35080
  if (rawChatId !== void 0) {
@@ -34910,7 +35094,8 @@ function registerScheduleTools(server) {
34910
35094
  threadId,
34911
35095
  agent: args.agent,
34912
35096
  enabled: args.enabled,
34913
- model: args.model
35097
+ model: args.model,
35098
+ archiveChat: args.archiveChat
34914
35099
  });
34915
35100
  return text6({ schedule });
34916
35101
  } catch (err) {