@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
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",
@@ -12881,7 +13001,7 @@ function coordinatorTurnReminder(opts) {
12881
13001
  const goal = opts.goal?.trim();
12882
13002
  return [
12883
13003
  "Sideboard Orchestration (mandatory):",
12884
- "- You oversee worktree agents from a synthetic empty cwd (not a git repo). Follow AGENTS.md / CLAUDE.md.",
13004
+ "- You oversee worktree agents from a synthetic empty cwd (not a git repo). Follow AGENTS.md (CLAUDE.md imports it).",
12885
13005
  `- YOUR orchestration chat id is ${opts.parentId} \u2014 pass parentChatId="${opts.parentId}" on create_workspace, or omit it.`,
12886
13006
  goal ? `- Goal / title: ${goal}` : null,
12887
13007
  accountDefaultsPlaybookLine(),
@@ -12945,10 +13065,9 @@ function ensureGlobalCoordinatorCwd(opts) {
12945
13065
  "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."
12946
13066
  ].join("\n");
12947
13067
  try {
12948
- (0, import_node_fs25.writeFileSync)((0, import_node_path29.join)(dir, "CLAUDE.md"), `${body}
12949
- `, "utf8");
12950
13068
  (0, import_node_fs25.writeFileSync)((0, import_node_path29.join)(dir, "AGENTS.md"), `${body}
12951
13069
  `, "utf8");
13070
+ (0, import_node_fs25.writeFileSync)((0, import_node_path29.join)(dir, "CLAUDE.md"), "@AGENTS.md\n", "utf8");
12952
13071
  } catch {
12953
13072
  }
12954
13073
  return dir;
@@ -12972,7 +13091,7 @@ function coordinatorSystemPrompt(opts) {
12972
13091
  ...intro,
12973
13092
  "You operate across ALL registered workspaces below.",
12974
13093
  "You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
12975
- "Follow AGENTS.md / CLAUDE.md in this cwd for the fleet playbook (they are the same document).",
13094
+ "Follow AGENTS.md in this cwd for the fleet playbook. CLAUDE.md is `@AGENTS.md` so Cursor does not load a second copy.",
12976
13095
  `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.`,
12977
13096
  `Goal: ${opts.goal}`,
12978
13097
  "Registered workspaces:",
@@ -13010,7 +13129,7 @@ var init_coordinator_prompt = __esm({
13010
13129
  "- 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
13130
  "- 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
13131
  "- 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.",
13132
+ "- 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
13133
  "Projects:",
13015
13134
  "- add_project / remove_project \u2014 register or unregister a git repo",
13016
13135
  "Workspaces and chats:",
@@ -23844,12 +23963,12 @@ function ephemeralRunReadyToArchive(thread) {
23844
23963
  }
23845
23964
  function markEphemeralAutomation(threadId, opts) {
23846
23965
  const id = opts.ephemeralAutomationId?.trim();
23966
+ const title = opts.ephemeralTitle?.trim();
23847
23967
  const current = readThread(threadId);
23848
23968
  if (!current) throw new Error(`Thread not found: ${threadId}`);
23849
- if (!id) return current;
23850
- const title = opts.ephemeralTitle?.trim();
23969
+ if (!id && !title) return current;
23851
23970
  return updateThread(threadId, {
23852
- ephemeralAutomationId: id,
23971
+ ...id ? { ephemeralAutomationId: id } : {},
23853
23972
  ...title ? { title, userSetTitle: true } : {}
23854
23973
  });
23855
23974
  }
@@ -23884,11 +24003,19 @@ function clearEphemeralAutomation(threadId) {
23884
24003
  function forgetOlderEphemeralRuns(automationId, keepId) {
23885
24004
  const id = automationId.trim();
23886
24005
  if (!id || !keepId) return [];
24006
+ const archived = listThreads({ includeArchived: true }).filter(
24007
+ (thread) => thread.status === "archived" && thread.ephemeralAutomationId === id
24008
+ );
24009
+ const ranked = [...archived].sort((a, b) => {
24010
+ if (a.id === keepId) return -1;
24011
+ if (b.id === keepId) return 1;
24012
+ return archivedUpdatedMs(b) - archivedUpdatedMs(a);
24013
+ });
24014
+ const keep = new Set(ranked.slice(0, EPHEMERAL_AUTOMATION_KEEP).map((thread) => thread.id));
24015
+ keep.add(keepId);
23887
24016
  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;
24017
+ for (const thread of archived) {
24018
+ if (keep.has(thread.id)) continue;
23892
24019
  try {
23893
24020
  deleteThreadRecord(thread.id);
23894
24021
  removed.push(thread.id);
@@ -23897,10 +24024,13 @@ function forgetOlderEphemeralRuns(automationId, keepId) {
23897
24024
  }
23898
24025
  return removed;
23899
24026
  }
24027
+ var EPHEMERAL_AUTOMATION_KEEP;
23900
24028
  var init_ephemeral_automation = __esm({
23901
24029
  "src/orchestrator/ephemeral-automation.ts"() {
23902
24030
  "use strict";
24031
+ init_history_retention();
23903
24032
  init_thread_store();
24033
+ EPHEMERAL_AUTOMATION_KEEP = 5;
23904
24034
  }
23905
24035
  });
23906
24036
 
@@ -29529,7 +29659,7 @@ function formatWorktreeDirective(thread, opts) {
29529
29659
  lines.push(formatDevAccessHint(opts?.activeRuns));
29530
29660
  lines.push("");
29531
29661
  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."
29662
+ "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
29663
  );
29534
29664
  lines.push(
29535
29665
  "`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
@@ -29549,7 +29679,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
29549
29679
  return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
29550
29680
  }
29551
29681
  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).";
29682
+ 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
29683
  }
29554
29684
  function formatClaudeSubagentReminder() {
29555
29685
  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 +33820,7 @@ async function fireSchedule(id) {
33690
33820
  });
33691
33821
  }
33692
33822
  const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
33823
+ const archiveFresh = schedule.archiveChat !== false && !pinReplacement;
33693
33824
  const created = await deps.startOrchestration({
33694
33825
  goal: prompt,
33695
33826
  agent: pickAgent(schedule),
@@ -33699,7 +33830,8 @@ async function fireSchedule(id) {
33699
33830
  ...schedule.planMode != null ? { planMode: schedule.planMode } : {},
33700
33831
  ...schedule.autonomy ? { autonomy: schedule.autonomy } : {},
33701
33832
  attachments: schedule.attachments,
33702
- ...pinReplacement ? {} : { ephemeralAutomationId: schedule.id, ephemeralTitle: schedule.name }
33833
+ ...archiveFresh ? { ephemeralAutomationId: schedule.id } : {},
33834
+ ephemeralTitle: schedule.name
33703
33835
  });
33704
33836
  if (pinReplacement) {
33705
33837
  updateSchedule(schedule.id, { threadId: created.id });
@@ -33930,6 +34062,7 @@ __export(index_exports, {
33930
34062
  THINKING_EFFORTS: () => THINKING_EFFORTS,
33931
34063
  THINKING_PART_MAX_CHARS: () => THINKING_PART_MAX_CHARS,
33932
34064
  USAGE_ON_LIMIT_VALUES: () => USAGE_ON_LIMIT_VALUES,
34065
+ WEEKDAY_LABELS: () => WEEKDAY_LABELS,
33933
34066
  WORKTREE_ABLETIME_MCP_TOOLS: () => WORKTREE_ABLETIME_MCP_TOOLS,
33934
34067
  WORKTREE_GITHUB_MCP_TOOLS: () => WORKTREE_GITHUB_MCP_TOOLS,
33935
34068
  WORKTREE_LINEAR_MCP_TOOLS: () => WORKTREE_LINEAR_MCP_TOOLS,
@@ -34257,6 +34390,7 @@ __export(index_exports, {
34257
34390
  formatUiReminder: () => formatUiReminder,
34258
34391
  formatViewerContextDirective: () => formatViewerContextDirective,
34259
34392
  formatViewerContextReminder: () => formatViewerContextReminder,
34393
+ formatWeeklyDays: () => formatWeeklyDays,
34260
34394
  formatWorkspaceInventory: () => formatWorkspaceInventory,
34261
34395
  formatWorkspaceProfileSuffix: () => formatWorkspaceProfileSuffix,
34262
34396
  formatWorktreeDirective: () => formatWorktreeDirective,
@@ -34553,6 +34687,8 @@ __export(index_exports, {
34553
34687
  normalizeThinkingEffort: () => normalizeThinkingEffort,
34554
34688
  normalizeThread: () => normalizeThread,
34555
34689
  normalizeTurnInput: () => normalizeTurnInput,
34690
+ normalizeWeekdays: () => normalizeWeekdays,
34691
+ normalizeWeeklyTime: () => normalizeWeeklyTime,
34556
34692
  normalizeWorkspaceTags: () => normalizeWorkspaceTags,
34557
34693
  normalizeWorktreePath: () => normalizeWorktreePath,
34558
34694
  notifyOrchestrator: () => notifyOrchestrator,
@@ -34592,6 +34728,7 @@ __export(index_exports, {
34592
34728
  parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
34593
34729
  parseSlackRelayClientMessage: () => parseSlackRelayClientMessage,
34594
34730
  parseSlackRelayServerMessage: () => parseSlackRelayServerMessage,
34731
+ parseWeeklyDays: () => parseWeeklyDays,
34595
34732
  partsToAssistantText: () => partsToAssistantText,
34596
34733
  pastedTextStats: () => pastedTextStats,
34597
34734
  patchReadAloudSettings: () => patchReadAloudSettings,
@@ -34869,6 +35006,7 @@ __export(index_exports, {
34869
35006
  visibleToolRowDetail: () => visibleToolRowDetail,
34870
35007
  waitForPidExit: () => waitForPidExit,
34871
35008
  warmGithubAgentAuth: () => warmGithubAgentAuth,
35009
+ weeklyCronExpr: () => weeklyCronExpr,
34872
35010
  whichOnPath: () => whichOnPath,
34873
35011
  withAddedAccount: () => withAddedAccount,
34874
35012
  withEventParentId: () => withEventParentId,
@@ -44901,18 +45039,79 @@ function fail4(err) {
44901
45039
  );
44902
45040
  }
44903
45041
  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");
45042
+ const at = args.at?.trim();
45043
+ const every = args.every?.trim();
45044
+ const cron = args.cron?.trim();
45045
+ const time = args.time?.trim();
45046
+ const days = args.days?.trim();
45047
+ if (days && !time && !at && !every && !cron) {
45048
+ throw new Error("days needs time (HH:mm), for example time=07:30 days=weekdays");
45049
+ }
45050
+ const picked = [at, every, cron, time].filter(Boolean);
45051
+ if (picked.length !== 1) {
45052
+ throw new Error(
45053
+ "Pass exactly one of at (ISO datetime), every (15m/1h/6h/1d), time (HH:mm), or cron"
45054
+ );
45055
+ }
45056
+ if (at) return { kind: "once", at };
45057
+ if (every) return { kind: "every", every };
45058
+ if (time) {
45059
+ return {
45060
+ kind: "weekly",
45061
+ time,
45062
+ days: parseWeeklyDays(days || "daily"),
45063
+ tz: args.tz?.trim() || void 0
45064
+ };
44907
45065
  }
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
45066
  return {
44911
45067
  kind: "cron",
44912
- expr: args.cron.trim(),
45068
+ expr: cron,
44913
45069
  tz: args.tz?.trim() || void 0
44914
45070
  };
44915
45071
  }
45072
+ function whenFromUpdate(id, args) {
45073
+ const at = args.at?.trim();
45074
+ const every = args.every?.trim();
45075
+ const cron = args.cron?.trim();
45076
+ const time = args.time?.trim();
45077
+ const days = args.days?.trim();
45078
+ const tzSet = args.tz !== void 0;
45079
+ if (!at && !every && !cron && !time && !days && !tzSet) return void 0;
45080
+ if ((time || days) && !at && !every && !cron) {
45081
+ const current = getSchedule(id);
45082
+ const weekly = current?.when.kind === "weekly" ? current.when : null;
45083
+ if (!weekly && !(time && days)) {
45084
+ const kind = current?.when.kind ?? "missing";
45085
+ throw new Error(
45086
+ `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.`
45087
+ );
45088
+ }
45089
+ const clock = time || weekly?.time;
45090
+ if (!clock) {
45091
+ throw new Error("time (HH:mm) is required, for example time=07:30 days=weekdays");
45092
+ }
45093
+ const prevTz = current?.when.kind === "weekly" || current?.when.kind === "cron" ? current.when.tz : void 0;
45094
+ return {
45095
+ kind: "weekly",
45096
+ time: clock,
45097
+ days: days ? parseWeeklyDays(days) : weekly?.days ?? parseWeeklyDays("daily"),
45098
+ tz: tzSet ? args.tz?.trim() || void 0 : prevTz
45099
+ };
45100
+ }
45101
+ if (!at && !every && !cron && !time && !days && tzSet) {
45102
+ const current = getSchedule(id);
45103
+ if (current?.when.kind === "weekly" || current?.when.kind === "cron") {
45104
+ return { ...current.when, tz: args.tz?.trim() || void 0 };
45105
+ }
45106
+ return void 0;
45107
+ }
45108
+ return parseWhen(args);
45109
+ }
45110
+ function createdHint(schedule) {
45111
+ const awake = scheduleNeedsCaffeinate(schedule) ? " Caffeinate while automations are enabled is now on so the Mac can stay awake until this job is due." : "";
45112
+ 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.";
45113
+ return `Fires while Sideboard.app is running.${fresh}${awake}`;
45114
+ }
44916
45115
  function registerScheduleTools(server) {
44917
45116
  server.tool(
44918
45117
  "list_schedules",
@@ -44928,14 +45127,19 @@ function registerScheduleTools(server) {
44928
45127
  );
44929
45128
  server.tool(
44930
45129
  "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.",
45130
+ "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
45131
  {
44933
45132
  prompt: import_zod10.z.string().describe("User message / goal queued when the automation fires"),
44934
45133
  name: import_zod10.z.string().optional(),
44935
45134
  at: import_zod10.z.string().optional().describe("ISO datetime for a one-shot"),
44936
45135
  every: import_zod10.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
45136
+ time: import_zod10.z.string().optional().describe("HH:mm (24-hour) clock time. Omit days for every day."),
45137
+ days: import_zod10.z.string().optional().describe("daily, weekdays, weekends, or days such as mon,wed,fri or mon-fri. Use with time."),
44937
45138
  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)"),
45139
+ tz: import_zod10.z.string().optional().describe("IANA timezone for cron or time (default: system)"),
45140
+ archiveChat: import_zod10.z.boolean().optional().describe(
45141
+ "Archive a new Global chat when the run finishes. Default true. Pass false to leave the chat open."
45142
+ ),
44939
45143
  chatId: import_zod10.z.string().optional().describe(
44940
45144
  'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
44941
45145
  ),
@@ -44962,11 +45166,12 @@ function registerScheduleTools(server) {
44962
45166
  threadId,
44963
45167
  agent: args.agent,
44964
45168
  model: args.model,
45169
+ archiveChat: args.archiveChat,
44965
45170
  createdBy: "mcp"
44966
45171
  });
44967
45172
  return text6({
44968
45173
  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)."
45174
+ hint: createdHint(schedule)
44970
45175
  });
44971
45176
  } catch (err) {
44972
45177
  return fail4(err);
@@ -44975,15 +45180,18 @@ function registerScheduleTools(server) {
44975
45180
  );
44976
45181
  server.tool(
44977
45182
  "update_schedule",
44978
- "Update a local automation (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
45183
+ "Update a local automation (prompt, cadence, target chat, archiveChat, enabled). Pass id from list_schedules.",
44979
45184
  {
44980
45185
  id: import_zod10.z.string(),
44981
45186
  prompt: import_zod10.z.string().optional(),
44982
45187
  name: import_zod10.z.string().optional(),
44983
45188
  at: import_zod10.z.string().optional(),
44984
45189
  every: import_zod10.z.string().optional(),
45190
+ time: import_zod10.z.string().optional().describe("HH:mm clock time. Use with days, or alone to change the time of a weekly job."),
45191
+ 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
45192
  cron: import_zod10.z.string().optional(),
44986
45193
  tz: import_zod10.z.string().optional(),
45194
+ archiveChat: import_zod10.z.boolean().optional().describe("Archive a new Global chat when the run finishes. False leaves it open."),
44987
45195
  chatId: import_zod10.z.string().optional().describe(
44988
45196
  'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
44989
45197
  ),
@@ -44994,13 +45202,7 @@ function registerScheduleTools(server) {
44994
45202
  },
44995
45203
  async (args) => {
44996
45204
  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;
45205
+ const when = whenFromUpdate(args.id, args);
45004
45206
  let threadId;
45005
45207
  const rawChatId = args.chatId ?? args.threadId;
45006
45208
  if (rawChatId !== void 0) {
@@ -45020,7 +45222,8 @@ function registerScheduleTools(server) {
45020
45222
  threadId,
45021
45223
  agent: args.agent,
45022
45224
  enabled: args.enabled,
45023
- model: args.model
45225
+ model: args.model,
45226
+ archiveChat: args.archiveChat
45024
45227
  });
45025
45228
  return text6({ schedule });
45026
45229
  } catch (err) {
@@ -49393,6 +49596,7 @@ async function pollSlackOutboundWatches(opts) {
49393
49596
  THINKING_EFFORTS,
49394
49597
  THINKING_PART_MAX_CHARS,
49395
49598
  USAGE_ON_LIMIT_VALUES,
49599
+ WEEKDAY_LABELS,
49396
49600
  WORKTREE_ABLETIME_MCP_TOOLS,
49397
49601
  WORKTREE_GITHUB_MCP_TOOLS,
49398
49602
  WORKTREE_LINEAR_MCP_TOOLS,
@@ -49720,6 +49924,7 @@ async function pollSlackOutboundWatches(opts) {
49720
49924
  formatUiReminder,
49721
49925
  formatViewerContextDirective,
49722
49926
  formatViewerContextReminder,
49927
+ formatWeeklyDays,
49723
49928
  formatWorkspaceInventory,
49724
49929
  formatWorkspaceProfileSuffix,
49725
49930
  formatWorktreeDirective,
@@ -50016,6 +50221,8 @@ async function pollSlackOutboundWatches(opts) {
50016
50221
  normalizeThinkingEffort,
50017
50222
  normalizeThread,
50018
50223
  normalizeTurnInput,
50224
+ normalizeWeekdays,
50225
+ normalizeWeeklyTime,
50019
50226
  normalizeWorkspaceTags,
50020
50227
  normalizeWorktreePath,
50021
50228
  notifyOrchestrator,
@@ -50055,6 +50262,7 @@ async function pollSlackOutboundWatches(opts) {
50055
50262
  parseSessionQuotaResetAt,
50056
50263
  parseSlackRelayClientMessage,
50057
50264
  parseSlackRelayServerMessage,
50265
+ parseWeeklyDays,
50058
50266
  partsToAssistantText,
50059
50267
  pastedTextStats,
50060
50268
  patchReadAloudSettings,
@@ -50332,6 +50540,7 @@ async function pollSlackOutboundWatches(opts) {
50332
50540
  visibleToolRowDetail,
50333
50541
  waitForPidExit,
50334
50542
  warmGithubAgentAuth,
50543
+ weeklyCronExpr,
50335
50544
  whichOnPath,
50336
50545
  withAddedAccount,
50337
50546
  withEventParentId,