@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
package/dist/index.cjs CHANGED
@@ -8298,10 +8298,7 @@ var init_run_log = __esm({
8298
8298
  }
8299
8299
  });
8300
8300
 
8301
- // src/store/schedules.ts
8302
- function schedulesPath() {
8303
- return (0, import_node_path18.join)(appDataDir(), "schedules.json");
8304
- }
8301
+ // src/store/schedule-when.ts
8305
8302
  function parseDurationMs(every) {
8306
8303
  const m = every.trim().match(DURATION_RE);
8307
8304
  if (!m) return null;
@@ -8310,18 +8307,72 @@ function parseDurationMs(every) {
8310
8307
  const unit = m[2].toLowerCase();
8311
8308
  return n * UNIT_MS[unit];
8312
8309
  }
8313
- function defaultScheduleName(prompt) {
8314
- const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
8315
- if (!line) return "Untitled automation";
8316
- return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
8317
- }
8318
- function resolveScheduleThreadId(raw, env = process.env) {
8319
- const value = raw?.trim() || "";
8320
- if (!value) return null;
8321
- if (value.toLowerCase() === "self") {
8322
- return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
8310
+ function dayIndex(token) {
8311
+ const text7 = token.trim().toLowerCase();
8312
+ if (/^[0-6]$/.test(text7)) return Number(text7);
8313
+ return DAY_ALIASES[text7];
8314
+ }
8315
+ function normalizeWeekdays(days) {
8316
+ const set = /* @__PURE__ */ new Set();
8317
+ for (const day of days) {
8318
+ if (!Number.isInteger(day) || day < 0 || day > 6) {
8319
+ throw new Error("Days must be 0 (Sunday) through 6 (Saturday)");
8320
+ }
8321
+ set.add(day);
8322
+ }
8323
+ if (set.size === 0) throw new Error("Pick at least one day");
8324
+ return [...set].sort((a, b) => a - b);
8325
+ }
8326
+ function parseWeeklyDays(raw) {
8327
+ const text7 = raw.trim().toLowerCase();
8328
+ if (!text7) throw new Error("Pick at least one day");
8329
+ if (text7 === "daily" || text7 === "all" || text7 === "every day" || text7 === "everyday") {
8330
+ return [0, 1, 2, 3, 4, 5, 6];
8331
+ }
8332
+ if (text7 === "weekdays" || text7 === "weekday") return [1, 2, 3, 4, 5];
8333
+ if (text7 === "weekends" || text7 === "weekend") return [0, 6];
8334
+ const days = [];
8335
+ for (const part of text7.split(/[\s,]+/).filter(Boolean)) {
8336
+ const range = part.split("-");
8337
+ if (range.length === 2 && range[0] && range[1]) {
8338
+ const start = dayIndex(range[0]);
8339
+ const end = dayIndex(range[1]);
8340
+ if (start === void 0 || end === void 0) {
8341
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
8342
+ }
8343
+ if (start <= end) {
8344
+ for (let d = start; d <= end; d++) days.push(d);
8345
+ } else {
8346
+ for (let d = start; d <= 6; d++) days.push(d);
8347
+ for (let d = 0; d <= end; d++) days.push(d);
8348
+ }
8349
+ continue;
8350
+ }
8351
+ const n = dayIndex(part);
8352
+ if (n === void 0) {
8353
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
8354
+ }
8355
+ days.push(n);
8323
8356
  }
8324
- return value;
8357
+ return normalizeWeekdays(days);
8358
+ }
8359
+ function normalizeWeeklyTime(raw) {
8360
+ const m = raw.trim().match(TIME_RE);
8361
+ if (!m) throw new Error(`Invalid time: ${raw} (use HH:mm, 24-hour)`);
8362
+ return `${m[1]}:${m[2]}`;
8363
+ }
8364
+ function formatWeeklyDays(days) {
8365
+ const key = normalizeWeekdays(days).join(",");
8366
+ if (key === "0,1,2,3,4,5,6") return "daily";
8367
+ if (key === "1,2,3,4,5") return "weekdays";
8368
+ if (key === "0,6") return "weekends";
8369
+ return normalizeWeekdays(days).map((day) => WEEKDAY_LABELS[day]).join(", ");
8370
+ }
8371
+ function weeklyCronExpr(when) {
8372
+ const time = normalizeWeeklyTime(when.time);
8373
+ const [hh, mm] = time.split(":");
8374
+ const days = normalizeWeekdays(when.days);
8375
+ return `${Number(mm)} ${Number(hh)} * * ${days.join(",")}`;
8325
8376
  }
8326
8377
  function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
8327
8378
  if (when.kind === "once") {
@@ -8338,6 +8389,12 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
8338
8389
  }
8339
8390
  return new Date(from.getTime() + ms);
8340
8391
  }
8392
+ if (when.kind === "weekly") {
8393
+ return computeNextRunAt(
8394
+ { kind: "cron", expr: weeklyCronExpr(when), tz: when.tz },
8395
+ from
8396
+ );
8397
+ }
8341
8398
  try {
8342
8399
  const job = new import_croner.Cron(when.expr, {
8343
8400
  timezone: when.tz?.trim() || void 0,
@@ -8356,12 +8413,14 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
8356
8413
  function formatScheduleWhen(when) {
8357
8414
  if (when.kind === "once") return `once at ${when.at}`;
8358
8415
  if (when.kind === "every") return `every ${when.every}`;
8416
+ if (when.kind === "weekly") {
8417
+ const label = `${formatWeeklyDays(when.days)} at ${normalizeWeeklyTime(when.time)}`;
8418
+ const tz2 = when.tz?.trim();
8419
+ return tz2 ? `${label} (${tz2})` : label;
8420
+ }
8359
8421
  const tz = when.tz?.trim();
8360
8422
  return tz ? `cron ${when.expr} (${tz})` : `cron ${when.expr}`;
8361
8423
  }
8362
- function nowIso2(d = /* @__PURE__ */ new Date()) {
8363
- return d.toISOString();
8364
- }
8365
8424
  function normalizeWhen(raw) {
8366
8425
  if (!raw || typeof raw !== "object") {
8367
8426
  throw new Error("Schedule is missing `when`");
@@ -8381,7 +8440,71 @@ function normalizeWhen(raw) {
8381
8440
  tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
8382
8441
  };
8383
8442
  }
8384
- throw new Error("Schedule `when` must be once, every, or cron");
8443
+ if (kind === "weekly" && Array.isArray(rec.days) && typeof rec.time === "string") {
8444
+ return {
8445
+ kind: "weekly",
8446
+ days: normalizeWeekdays(rec.days.map((day) => Number(day))),
8447
+ time: normalizeWeeklyTime(rec.time),
8448
+ tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
8449
+ };
8450
+ }
8451
+ throw new Error("Schedule `when` must be once, every, weekly, or cron");
8452
+ }
8453
+ var import_croner, WEEKDAY_LABELS, DURATION_RE, UNIT_MS, DAY_ALIASES, TIME_RE;
8454
+ var init_schedule_when = __esm({
8455
+ "src/store/schedule-when.ts"() {
8456
+ "use strict";
8457
+ import_croner = require("croner");
8458
+ WEEKDAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
8459
+ DURATION_RE = /^(\d+)(s|m|h|d)$/i;
8460
+ UNIT_MS = {
8461
+ s: 1e3,
8462
+ m: 6e4,
8463
+ h: 36e5,
8464
+ d: 864e5
8465
+ };
8466
+ DAY_ALIASES = {
8467
+ sun: 0,
8468
+ sunday: 0,
8469
+ mon: 1,
8470
+ monday: 1,
8471
+ tue: 2,
8472
+ tues: 2,
8473
+ tuesday: 2,
8474
+ wed: 3,
8475
+ wednesday: 3,
8476
+ thu: 4,
8477
+ thur: 4,
8478
+ thurs: 4,
8479
+ thursday: 4,
8480
+ fri: 5,
8481
+ friday: 5,
8482
+ sat: 6,
8483
+ saturday: 6
8484
+ };
8485
+ TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/;
8486
+ }
8487
+ });
8488
+
8489
+ // src/store/schedules.ts
8490
+ function schedulesPath() {
8491
+ return (0, import_node_path18.join)(appDataDir(), "schedules.json");
8492
+ }
8493
+ function defaultScheduleName(prompt) {
8494
+ const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
8495
+ if (!line) return "Untitled automation";
8496
+ return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
8497
+ }
8498
+ function resolveScheduleThreadId(raw, env = process.env) {
8499
+ const value = raw?.trim() || "";
8500
+ if (!value) return null;
8501
+ if (value.toLowerCase() === "self") {
8502
+ return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
8503
+ }
8504
+ return value;
8505
+ }
8506
+ function nowIso2(d = /* @__PURE__ */ new Date()) {
8507
+ return d.toISOString();
8385
8508
  }
8386
8509
  function normalizeOptionalBool(raw) {
8387
8510
  if (raw === true || raw === false) return raw;
@@ -8431,6 +8554,7 @@ function normalizeTask(raw) {
8431
8554
  planMode: normalizeOptionalBool(raw.planMode),
8432
8555
  autonomy: normalizeAutonomy2(raw.autonomy),
8433
8556
  attachments: normalizeAttachments(raw.attachments),
8557
+ archiveChat: raw.archiveChat !== false,
8434
8558
  nextRunAt: raw.nextRunAt,
8435
8559
  lastRunAt: raw.lastRunAt ?? null,
8436
8560
  lastThreadId: raw.lastThreadId ?? null,
@@ -8482,6 +8606,7 @@ function buildTask(input, now) {
8482
8606
  planMode: normalizeOptionalBool(input.planMode),
8483
8607
  autonomy: normalizeAutonomy2(input.autonomy),
8484
8608
  attachments: normalizeAttachments(input.attachments),
8609
+ archiveChat: input.archiveChat !== false,
8485
8610
  nextRunAt,
8486
8611
  lastRunAt: null,
8487
8612
  lastThreadId: null,
@@ -8546,6 +8671,7 @@ function updateSchedule(id, patch) {
8546
8671
  planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
8547
8672
  autonomy: patch.autonomy !== void 0 ? normalizeAutonomy2(patch.autonomy) : prev.autonomy,
8548
8673
  attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
8674
+ archiveChat: patch.archiveChat !== void 0 ? patch.archiveChat !== false : prev.archiveChat,
8549
8675
  nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
8550
8676
  lastError: whenChanged ? null : prev.lastError,
8551
8677
  updatedAt: nowIso2()
@@ -8582,26 +8708,20 @@ function recordScheduleRun(id, result) {
8582
8708
  writeAll(rows);
8583
8709
  return next;
8584
8710
  }
8585
- var import_node_crypto8, import_node_fs15, import_node_path18, import_croner, DURATION_RE, UNIT_MS, ATTACHMENT_KINDS;
8711
+ var import_node_crypto8, import_node_fs15, import_node_path18, ATTACHMENT_KINDS;
8586
8712
  var init_schedules = __esm({
8587
8713
  "src/store/schedules.ts"() {
8588
8714
  "use strict";
8589
8715
  import_node_crypto8 = require("crypto");
8590
8716
  import_node_fs15 = require("fs");
8591
8717
  import_node_path18 = require("path");
8592
- import_croner = require("croner");
8718
+ init_schedule_when();
8593
8719
  init_orchestrator_capable();
8594
8720
  init_thinking_effort();
8595
8721
  init_paths();
8596
8722
  init_private_file();
8597
8723
  init_app_settings();
8598
- DURATION_RE = /^(\d+)(s|m|h|d)$/i;
8599
- UNIT_MS = {
8600
- s: 1e3,
8601
- m: 6e4,
8602
- h: 36e5,
8603
- d: 864e5
8604
- };
8724
+ init_schedule_when();
8605
8725
  ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
8606
8726
  "transcript",
8607
8727
  "file",
@@ -13010,7 +13130,7 @@ var init_coordinator_prompt = __esm({
13010
13130
  "- 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.",
13011
13131
  "- 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.",
13012
13132
  "- 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.",
13013
- "- 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.",
13133
+ "- 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.",
13014
13134
  "Projects:",
13015
13135
  "- add_project / remove_project \u2014 register or unregister a git repo",
13016
13136
  "Workspaces and chats:",
@@ -23844,12 +23964,12 @@ function ephemeralRunReadyToArchive(thread) {
23844
23964
  }
23845
23965
  function markEphemeralAutomation(threadId, opts) {
23846
23966
  const id = opts.ephemeralAutomationId?.trim();
23967
+ const title = opts.ephemeralTitle?.trim();
23847
23968
  const current = readThread(threadId);
23848
23969
  if (!current) throw new Error(`Thread not found: ${threadId}`);
23849
- if (!id) return current;
23850
- const title = opts.ephemeralTitle?.trim();
23970
+ if (!id && !title) return current;
23851
23971
  return updateThread(threadId, {
23852
- ephemeralAutomationId: id,
23972
+ ...id ? { ephemeralAutomationId: id } : {},
23853
23973
  ...title ? { title, userSetTitle: true } : {}
23854
23974
  });
23855
23975
  }
@@ -23884,11 +24004,19 @@ function clearEphemeralAutomation(threadId) {
23884
24004
  function forgetOlderEphemeralRuns(automationId, keepId) {
23885
24005
  const id = automationId.trim();
23886
24006
  if (!id || !keepId) return [];
24007
+ const archived = listThreads({ includeArchived: true }).filter(
24008
+ (thread) => thread.status === "archived" && thread.ephemeralAutomationId === id
24009
+ );
24010
+ const ranked = [...archived].sort((a, b) => {
24011
+ if (a.id === keepId) return -1;
24012
+ if (b.id === keepId) return 1;
24013
+ return archivedUpdatedMs(b) - archivedUpdatedMs(a);
24014
+ });
24015
+ const keep = new Set(ranked.slice(0, EPHEMERAL_AUTOMATION_KEEP).map((thread) => thread.id));
24016
+ keep.add(keepId);
23887
24017
  const removed = [];
23888
- for (const thread of listThreads({ includeArchived: true })) {
23889
- if (thread.id === keepId) continue;
23890
- if (thread.status !== "archived") continue;
23891
- if (thread.ephemeralAutomationId !== id) continue;
24018
+ for (const thread of archived) {
24019
+ if (keep.has(thread.id)) continue;
23892
24020
  try {
23893
24021
  deleteThreadRecord(thread.id);
23894
24022
  removed.push(thread.id);
@@ -23897,10 +24025,13 @@ function forgetOlderEphemeralRuns(automationId, keepId) {
23897
24025
  }
23898
24026
  return removed;
23899
24027
  }
24028
+ var EPHEMERAL_AUTOMATION_KEEP;
23900
24029
  var init_ephemeral_automation = __esm({
23901
24030
  "src/orchestrator/ephemeral-automation.ts"() {
23902
24031
  "use strict";
24032
+ init_history_retention();
23903
24033
  init_thread_store();
24034
+ EPHEMERAL_AUTOMATION_KEEP = 5;
23904
24035
  }
23905
24036
  });
23906
24037
 
@@ -29529,7 +29660,7 @@ function formatWorktreeDirective(thread, opts) {
29529
29660
  lines.push(formatDevAccessHint(opts?.activeRuns));
29530
29661
  lines.push("");
29531
29662
  lines.push(
29532
- "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."
29663
+ "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."
29533
29664
  );
29534
29665
  lines.push(
29535
29666
  "`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
@@ -29549,7 +29680,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
29549
29680
  return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
29550
29681
  }
29551
29682
  function formatWorktreeReminder() {
29552
- 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).";
29683
+ 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).";
29553
29684
  }
29554
29685
  function formatClaudeSubagentReminder() {
29555
29686
  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.';
@@ -33690,6 +33821,7 @@ async function fireSchedule(id) {
33690
33821
  });
33691
33822
  }
33692
33823
  const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
33824
+ const archiveFresh = schedule.archiveChat !== false && !pinReplacement;
33693
33825
  const created = await deps.startOrchestration({
33694
33826
  goal: prompt,
33695
33827
  agent: pickAgent(schedule),
@@ -33699,7 +33831,8 @@ async function fireSchedule(id) {
33699
33831
  ...schedule.planMode != null ? { planMode: schedule.planMode } : {},
33700
33832
  ...schedule.autonomy ? { autonomy: schedule.autonomy } : {},
33701
33833
  attachments: schedule.attachments,
33702
- ...pinReplacement ? {} : { ephemeralAutomationId: schedule.id, ephemeralTitle: schedule.name }
33834
+ ...archiveFresh ? { ephemeralAutomationId: schedule.id } : {},
33835
+ ephemeralTitle: schedule.name
33703
33836
  });
33704
33837
  if (pinReplacement) {
33705
33838
  updateSchedule(schedule.id, { threadId: created.id });
@@ -33930,6 +34063,7 @@ __export(index_exports, {
33930
34063
  THINKING_EFFORTS: () => THINKING_EFFORTS,
33931
34064
  THINKING_PART_MAX_CHARS: () => THINKING_PART_MAX_CHARS,
33932
34065
  USAGE_ON_LIMIT_VALUES: () => USAGE_ON_LIMIT_VALUES,
34066
+ WEEKDAY_LABELS: () => WEEKDAY_LABELS,
33933
34067
  WORKTREE_ABLETIME_MCP_TOOLS: () => WORKTREE_ABLETIME_MCP_TOOLS,
33934
34068
  WORKTREE_GITHUB_MCP_TOOLS: () => WORKTREE_GITHUB_MCP_TOOLS,
33935
34069
  WORKTREE_LINEAR_MCP_TOOLS: () => WORKTREE_LINEAR_MCP_TOOLS,
@@ -34257,6 +34391,7 @@ __export(index_exports, {
34257
34391
  formatUiReminder: () => formatUiReminder,
34258
34392
  formatViewerContextDirective: () => formatViewerContextDirective,
34259
34393
  formatViewerContextReminder: () => formatViewerContextReminder,
34394
+ formatWeeklyDays: () => formatWeeklyDays,
34260
34395
  formatWorkspaceInventory: () => formatWorkspaceInventory,
34261
34396
  formatWorkspaceProfileSuffix: () => formatWorkspaceProfileSuffix,
34262
34397
  formatWorktreeDirective: () => formatWorktreeDirective,
@@ -34553,6 +34688,8 @@ __export(index_exports, {
34553
34688
  normalizeThinkingEffort: () => normalizeThinkingEffort,
34554
34689
  normalizeThread: () => normalizeThread,
34555
34690
  normalizeTurnInput: () => normalizeTurnInput,
34691
+ normalizeWeekdays: () => normalizeWeekdays,
34692
+ normalizeWeeklyTime: () => normalizeWeeklyTime,
34556
34693
  normalizeWorkspaceTags: () => normalizeWorkspaceTags,
34557
34694
  normalizeWorktreePath: () => normalizeWorktreePath,
34558
34695
  notifyOrchestrator: () => notifyOrchestrator,
@@ -34592,6 +34729,7 @@ __export(index_exports, {
34592
34729
  parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
34593
34730
  parseSlackRelayClientMessage: () => parseSlackRelayClientMessage,
34594
34731
  parseSlackRelayServerMessage: () => parseSlackRelayServerMessage,
34732
+ parseWeeklyDays: () => parseWeeklyDays,
34595
34733
  partsToAssistantText: () => partsToAssistantText,
34596
34734
  pastedTextStats: () => pastedTextStats,
34597
34735
  patchReadAloudSettings: () => patchReadAloudSettings,
@@ -34869,6 +35007,7 @@ __export(index_exports, {
34869
35007
  visibleToolRowDetail: () => visibleToolRowDetail,
34870
35008
  waitForPidExit: () => waitForPidExit,
34871
35009
  warmGithubAgentAuth: () => warmGithubAgentAuth,
35010
+ weeklyCronExpr: () => weeklyCronExpr,
34872
35011
  whichOnPath: () => whichOnPath,
34873
35012
  withAddedAccount: () => withAddedAccount,
34874
35013
  withEventParentId: () => withEventParentId,
@@ -44901,18 +45040,79 @@ function fail4(err) {
44901
45040
  );
44902
45041
  }
44903
45042
  function parseWhen(args) {
44904
- const count = [args.at, args.every, args.cron].filter((v) => v?.trim()).length;
44905
- if (count !== 1) {
44906
- throw new Error("Pass exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron");
45043
+ const at = args.at?.trim();
45044
+ const every = args.every?.trim();
45045
+ const cron = args.cron?.trim();
45046
+ const time = args.time?.trim();
45047
+ const days = args.days?.trim();
45048
+ if (days && !time && !at && !every && !cron) {
45049
+ throw new Error("days needs time (HH:mm), for example time=07:30 days=weekdays");
45050
+ }
45051
+ const picked = [at, every, cron, time].filter(Boolean);
45052
+ if (picked.length !== 1) {
45053
+ throw new Error(
45054
+ "Pass exactly one of at (ISO datetime), every (15m/1h/6h/1d), time (HH:mm), or cron"
45055
+ );
45056
+ }
45057
+ if (at) return { kind: "once", at };
45058
+ if (every) return { kind: "every", every };
45059
+ if (time) {
45060
+ return {
45061
+ kind: "weekly",
45062
+ time,
45063
+ days: parseWeeklyDays(days || "daily"),
45064
+ tz: args.tz?.trim() || void 0
45065
+ };
44907
45066
  }
44908
- if (args.at?.trim()) return { kind: "once", at: args.at.trim() };
44909
- if (args.every?.trim()) return { kind: "every", every: args.every.trim() };
44910
45067
  return {
44911
45068
  kind: "cron",
44912
- expr: args.cron.trim(),
45069
+ expr: cron,
44913
45070
  tz: args.tz?.trim() || void 0
44914
45071
  };
44915
45072
  }
45073
+ function whenFromUpdate(id, args) {
45074
+ const at = args.at?.trim();
45075
+ const every = args.every?.trim();
45076
+ const cron = args.cron?.trim();
45077
+ const time = args.time?.trim();
45078
+ const days = args.days?.trim();
45079
+ const tzSet = args.tz !== void 0;
45080
+ if (!at && !every && !cron && !time && !days && !tzSet) return void 0;
45081
+ if ((time || days) && !at && !every && !cron) {
45082
+ const current = getSchedule(id);
45083
+ const weekly = current?.when.kind === "weekly" ? current.when : null;
45084
+ if (!weekly && !(time && days)) {
45085
+ const kind = current?.when.kind ?? "missing";
45086
+ throw new Error(
45087
+ `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.`
45088
+ );
45089
+ }
45090
+ const clock = time || weekly?.time;
45091
+ if (!clock) {
45092
+ throw new Error("time (HH:mm) is required, for example time=07:30 days=weekdays");
45093
+ }
45094
+ const prevTz = current?.when.kind === "weekly" || current?.when.kind === "cron" ? current.when.tz : void 0;
45095
+ return {
45096
+ kind: "weekly",
45097
+ time: clock,
45098
+ days: days ? parseWeeklyDays(days) : weekly?.days ?? parseWeeklyDays("daily"),
45099
+ tz: tzSet ? args.tz?.trim() || void 0 : prevTz
45100
+ };
45101
+ }
45102
+ if (!at && !every && !cron && !time && !days && tzSet) {
45103
+ const current = getSchedule(id);
45104
+ if (current?.when.kind === "weekly" || current?.when.kind === "cron") {
45105
+ return { ...current.when, tz: args.tz?.trim() || void 0 };
45106
+ }
45107
+ return void 0;
45108
+ }
45109
+ return parseWhen(args);
45110
+ }
45111
+ function createdHint(schedule) {
45112
+ const awake = scheduleNeedsCaffeinate(schedule) ? " Caffeinate while automations are enabled is now on so the Mac can stay awake until this job is due." : "";
45113
+ 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.";
45114
+ return `Fires while Sideboard.app is running.${fresh}${awake}`;
45115
+ }
44916
45116
  function registerScheduleTools(server) {
44917
45117
  server.tool(
44918
45118
  "list_schedules",
@@ -44928,14 +45128,19 @@ function registerScheduleTools(server) {
44928
45128
  );
44929
45129
  server.tool(
44930
45130
  "create_schedule",
44931
- "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.",
45131
+ "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.",
44932
45132
  {
44933
45133
  prompt: import_zod10.z.string().describe("User message / goal queued when the automation fires"),
44934
45134
  name: import_zod10.z.string().optional(),
44935
45135
  at: import_zod10.z.string().optional().describe("ISO datetime for a one-shot"),
44936
45136
  every: import_zod10.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
45137
+ time: import_zod10.z.string().optional().describe("HH:mm (24-hour) clock time. Omit days for every day."),
45138
+ days: import_zod10.z.string().optional().describe("daily, weekdays, weekends, or days such as mon,wed,fri or mon-fri. Use with time."),
44937
45139
  cron: import_zod10.z.string().optional().describe("5-field cron expression"),
44938
- tz: import_zod10.z.string().optional().describe("IANA timezone for cron (default: system)"),
45140
+ tz: import_zod10.z.string().optional().describe("IANA timezone for cron or time (default: system)"),
45141
+ archiveChat: import_zod10.z.boolean().optional().describe(
45142
+ "Archive a new Global chat when the run finishes. Default true. Pass false to leave the chat open."
45143
+ ),
44939
45144
  chatId: import_zod10.z.string().optional().describe(
44940
45145
  'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
44941
45146
  ),
@@ -44962,11 +45167,12 @@ function registerScheduleTools(server) {
44962
45167
  threadId,
44963
45168
  agent: args.agent,
44964
45169
  model: args.model,
45170
+ archiveChat: args.archiveChat,
44965
45171
  createdBy: "mcp"
44966
45172
  });
44967
45173
  return text6({
44968
45174
  schedule,
44969
- 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)."
45175
+ hint: createdHint(schedule)
44970
45176
  });
44971
45177
  } catch (err) {
44972
45178
  return fail4(err);
@@ -44975,15 +45181,18 @@ function registerScheduleTools(server) {
44975
45181
  );
44976
45182
  server.tool(
44977
45183
  "update_schedule",
44978
- "Update a local automation (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
45184
+ "Update a local automation (prompt, cadence, target chat, archiveChat, enabled). Pass id from list_schedules.",
44979
45185
  {
44980
45186
  id: import_zod10.z.string(),
44981
45187
  prompt: import_zod10.z.string().optional(),
44982
45188
  name: import_zod10.z.string().optional(),
44983
45189
  at: import_zod10.z.string().optional(),
44984
45190
  every: import_zod10.z.string().optional(),
45191
+ time: import_zod10.z.string().optional().describe("HH:mm clock time. Use with days, or alone to change the time of a weekly job."),
45192
+ days: import_zod10.z.string().optional().describe("daily, weekdays, weekends, or mon,wed,fri. Use with time, or alone on an existing weekly job."),
44985
45193
  cron: import_zod10.z.string().optional(),
44986
45194
  tz: import_zod10.z.string().optional(),
45195
+ archiveChat: import_zod10.z.boolean().optional().describe("Archive a new Global chat when the run finishes. False leaves it open."),
44987
45196
  chatId: import_zod10.z.string().optional().describe(
44988
45197
  'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
44989
45198
  ),
@@ -44994,13 +45203,7 @@ function registerScheduleTools(server) {
44994
45203
  },
44995
45204
  async (args) => {
44996
45205
  try {
44997
- const cadenceSet = [args.at, args.every, args.cron].some((v) => v?.trim());
44998
- const when = cadenceSet ? parseWhen({
44999
- at: args.at,
45000
- every: args.every,
45001
- cron: args.cron,
45002
- tz: args.tz
45003
- }) : void 0;
45206
+ const when = whenFromUpdate(args.id, args);
45004
45207
  let threadId;
45005
45208
  const rawChatId = args.chatId ?? args.threadId;
45006
45209
  if (rawChatId !== void 0) {
@@ -45020,7 +45223,8 @@ function registerScheduleTools(server) {
45020
45223
  threadId,
45021
45224
  agent: args.agent,
45022
45225
  enabled: args.enabled,
45023
- model: args.model
45226
+ model: args.model,
45227
+ archiveChat: args.archiveChat
45024
45228
  });
45025
45229
  return text6({ schedule });
45026
45230
  } catch (err) {
@@ -49393,6 +49597,7 @@ async function pollSlackOutboundWatches(opts) {
49393
49597
  THINKING_EFFORTS,
49394
49598
  THINKING_PART_MAX_CHARS,
49395
49599
  USAGE_ON_LIMIT_VALUES,
49600
+ WEEKDAY_LABELS,
49396
49601
  WORKTREE_ABLETIME_MCP_TOOLS,
49397
49602
  WORKTREE_GITHUB_MCP_TOOLS,
49398
49603
  WORKTREE_LINEAR_MCP_TOOLS,
@@ -49720,6 +49925,7 @@ async function pollSlackOutboundWatches(opts) {
49720
49925
  formatUiReminder,
49721
49926
  formatViewerContextDirective,
49722
49927
  formatViewerContextReminder,
49928
+ formatWeeklyDays,
49723
49929
  formatWorkspaceInventory,
49724
49930
  formatWorkspaceProfileSuffix,
49725
49931
  formatWorktreeDirective,
@@ -50016,6 +50222,8 @@ async function pollSlackOutboundWatches(opts) {
50016
50222
  normalizeThinkingEffort,
50017
50223
  normalizeThread,
50018
50224
  normalizeTurnInput,
50225
+ normalizeWeekdays,
50226
+ normalizeWeeklyTime,
50019
50227
  normalizeWorkspaceTags,
50020
50228
  normalizeWorktreePath,
50021
50229
  notifyOrchestrator,
@@ -50055,6 +50263,7 @@ async function pollSlackOutboundWatches(opts) {
50055
50263
  parseSessionQuotaResetAt,
50056
50264
  parseSlackRelayClientMessage,
50057
50265
  parseSlackRelayServerMessage,
50266
+ parseWeeklyDays,
50058
50267
  partsToAssistantText,
50059
50268
  pastedTextStats,
50060
50269
  patchReadAloudSettings,
@@ -50332,6 +50541,7 @@ async function pollSlackOutboundWatches(opts) {
50332
50541
  visibleToolRowDetail,
50333
50542
  waitForPidExit,
50334
50543
  warmGithubAgentAuth,
50544
+ weeklyCronExpr,
50335
50545
  whichOnPath,
50336
50546
  withAddedAccount,
50337
50547
  withEventParentId,