@sideboard-ai/core 0.1.326 → 0.1.327

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 (73) hide show
  1. package/dist/{abletime-RYYFGKMU.js → abletime-NW4PJGHN.js} +4 -5
  2. package/dist/{abletime-RDHYSZL2.js → abletime-OW7LTFQD.js} +4 -5
  3. package/dist/{abletime-oauth-OYGS2DAU.js → abletime-oauth-KWQQRHOQ.js} +3 -4
  4. package/dist/{abletime-oauth-QR2QDVMK.js → abletime-oauth-TPR2QDRI.js} +3 -4
  5. package/dist/{abletime-rest-QDPTWNS4.js → abletime-rest-5DCWN3P4.js} +3 -4
  6. package/dist/{abletime-rest-BKYKNPTC.js → abletime-rest-UJ6SXDBN.js} +3 -4
  7. package/dist/agents/claude-runner.cjs +11 -0
  8. package/dist/agents/claude-runner.js +7 -1
  9. package/dist/{agents-QO7GKBYT.js → agents-IYBY3JT5.js} +8 -9
  10. package/dist/{agents-HANWZNI6.js → agents-XWIPONJR.js} +8 -9
  11. package/dist/{app-settings-RXBH7KY4.js → app-settings-A5D364JD.js} +1 -2
  12. package/dist/{app-settings-TUVLZNDE.js → app-settings-SA4HX3ZQ.js} +1 -2
  13. package/dist/{chunk-3Q4XNYHY.js → chunk-2GEUFPQQ.js} +1 -1
  14. package/dist/{chunk-MAW7DIG7.js → chunk-4F7THWIO.js} +2 -2
  15. package/dist/{chunk-K7M7MIWL.js → chunk-4G3W23X3.js} +3 -3
  16. package/dist/{chunk-Q3FHTGIW.js → chunk-5Y2C7Q55.js} +441 -20
  17. package/dist/{chunk-TMCK5TLA.js → chunk-ATQLP22S.js} +1 -1
  18. package/dist/{chunk-PB6HW7SA.js → chunk-CHYUUETG.js} +3 -3
  19. package/dist/{chunk-QPSKQP6E.js → chunk-D446IWPP.js} +48 -3
  20. package/dist/{chunk-C7HUCBX7.js → chunk-DACWFN4O.js} +5 -5
  21. package/dist/{chunk-GP3RLZ6A.js → chunk-DEB47BII.js} +51 -487
  22. package/dist/{chunk-B244TJWC.js → chunk-ECY7WL6T.js} +2 -2
  23. package/dist/{chunk-D744HOI5.js → chunk-EJRCHCXM.js} +18 -3
  24. package/dist/{chunk-KXRF6RNL.js → chunk-EUIVQAPK.js} +2 -2
  25. package/dist/{chunk-46FECFZU.js → chunk-IXWPCOLQ.js} +8 -195
  26. package/dist/{chunk-75RPUXJS.js → chunk-JKHCADKB.js} +5 -5
  27. package/dist/{chunk-4H5MWZ6J.js → chunk-KLVW3J2B.js} +2 -2
  28. package/dist/{chunk-QG4CYRA3.js → chunk-MO46K6WK.js} +14 -14
  29. package/dist/{chunk-F2PVRF3B.js → chunk-N6ZF3XRP.js} +2 -1
  30. package/dist/{chunk-BBNWKL4I.js → chunk-NQGLDQYB.js} +8 -8
  31. package/dist/{chunk-YGVGXQA7.js → chunk-Q4UPRQOS.js} +12 -12
  32. package/dist/{chunk-ZUAYA57W.js → chunk-QMLDR63X.js} +2 -2
  33. package/dist/{chunk-KECHK3ZT.js → chunk-R2C45NHT.js} +2 -2
  34. package/dist/{chunk-FWVUGLBG.js → chunk-RDXODLEN.js} +4 -4
  35. package/dist/{chunk-XLVLMQKN.js → chunk-RZJ3QJZT.js} +11 -11
  36. package/dist/{chunk-JM2TGOHP.js → chunk-SB5ADEZZ.js} +5 -5
  37. package/dist/{chunk-SO7G6O24.js → chunk-SFXGD5KJ.js} +1 -1
  38. package/dist/{chunk-LRHUBGEM.js → chunk-TUH4ICHQ.js} +1 -1
  39. package/dist/chunk-ULWPSBAB.js +192 -0
  40. package/dist/{chunk-WUCAVIIP.js → chunk-UMXBH344.js} +1 -1
  41. package/dist/{chunk-RZVOX2MT.js → chunk-XBBZZHJY.js} +3 -3
  42. package/dist/{chunk-ZSHS46V5.js → chunk-Y6OHM672.js} +476 -20
  43. package/dist/{chunk-2EOLWHXH.js → chunk-YUHZEIVG.js} +69 -473
  44. package/dist/{chunk-YWQF7WAG.js → chunk-ZPBOWO64.js} +2 -2
  45. package/dist/{conductor-4GVTHUUB.js → conductor-CP7TW6TN.js} +7 -7
  46. package/dist/{conductor-TGWI6ETZ.js → conductor-D62Q2XJC.js} +6 -7
  47. package/dist/{coordinator-prompt-3E6SIDTM.js → coordinator-prompt-3PJLU3UH.js} +5 -6
  48. package/dist/{coordinator-prompt-WEYJREV6.js → coordinator-prompt-J7O7JB26.js} +5 -6
  49. package/dist/{global-workspace-UIEXAG2B.js → global-workspace-4CTLU2BV.js} +5 -6
  50. package/dist/{global-workspace-FPDZKUPF.js → global-workspace-Y3UDZYJX.js} +5 -6
  51. package/dist/index.cjs +507 -492
  52. package/dist/index.d.cts +5 -2
  53. package/dist/index.d.ts +5 -2
  54. package/dist/index.js +67 -65
  55. package/dist/{linear-6S7MK5YK.js → linear-CXWGH2TW.js} +2 -3
  56. package/dist/{linear-Z2T4KUAQ.js → linear-WLDCJ3AF.js} +2 -3
  57. package/dist/{linear-list-Q4M7AZO2.js → linear-list-52LEDEWO.js} +1 -1
  58. package/dist/{linear-list-A37WLUBO.js → linear-list-FGVN65JU.js} +1 -1
  59. package/dist/mcp/run-stdio.cjs +19654 -19641
  60. package/dist/mcp/run-stdio.js +33 -34
  61. package/dist/{optional-cli-CA4LL322.js → optional-cli-FN4MGB6W.js} +4 -5
  62. package/dist/{optional-cli-AANBL4U5.js → optional-cli-INRVBBBG.js} +3 -4
  63. package/dist/{orchestrator-7FVDBE7Y.js → orchestrator-EN3ZKD73.js} +12 -12
  64. package/dist/{orchestrator-RQXRZW7Z.js → orchestrator-W4TZBBDO.js} +11 -12
  65. package/dist/{thread-store-4ZN3DS2W.js → thread-store-4WE24XA2.js} +6 -2
  66. package/dist/{thread-store-IC4BQXDW.js → thread-store-K2ZNGUSM.js} +7 -3
  67. package/dist/{workspaces-LEW4UP56.js → workspaces-MM7CQYR6.js} +5 -6
  68. package/dist/{workspaces-QONNLHNR.js → workspaces-WYCVZ6XH.js} +5 -6
  69. package/dist/{worktree-KFEJAQ2H.js → worktree-JBNEMJ5Z.js} +7 -8
  70. package/dist/{worktree-6GIONDQO.js → worktree-NNVRGPP4.js} +4 -5
  71. package/package.json +1 -1
  72. package/dist/chunk-77WWLBCI.js +0 -50
  73. package/dist/chunk-KGZBYWZ3.js +0 -23
package/dist/index.cjs CHANGED
@@ -133,7 +133,8 @@ var init_claude_mcp = __esm({
133
133
  "TaskStop",
134
134
  "EnterWorktree",
135
135
  "ExitWorktree",
136
- "Skill"
136
+ "Skill",
137
+ "AskUserQuestion"
137
138
  ];
138
139
  CLAUDE_PRINT_BG_WAIT_CEILING_MS = 72e5;
139
140
  CLAUDE_CHROME_ALLOWED_TOOLS = [
@@ -5185,6 +5186,449 @@ var init_cursor_store = __esm({
5185
5186
  }
5186
5187
  });
5187
5188
 
5189
+ // src/store/schedule-when.ts
5190
+ function parseDurationMs(every) {
5191
+ const m = every.trim().match(DURATION_RE);
5192
+ if (!m) return null;
5193
+ const n = Number.parseInt(m[1], 10);
5194
+ if (!Number.isFinite(n) || n <= 0) return null;
5195
+ const unit = m[2].toLowerCase();
5196
+ return n * UNIT_MS[unit];
5197
+ }
5198
+ function dayIndex(token) {
5199
+ const text7 = token.trim().toLowerCase();
5200
+ if (/^[0-6]$/.test(text7)) return Number(text7);
5201
+ return DAY_ALIASES[text7];
5202
+ }
5203
+ function normalizeWeekdays(days) {
5204
+ const set = /* @__PURE__ */ new Set();
5205
+ for (const day of days) {
5206
+ if (!Number.isInteger(day) || day < 0 || day > 6) {
5207
+ throw new Error("Days must be 0 (Sunday) through 6 (Saturday)");
5208
+ }
5209
+ set.add(day);
5210
+ }
5211
+ if (set.size === 0) throw new Error("Pick at least one day");
5212
+ return [...set].sort((a, b) => a - b);
5213
+ }
5214
+ function parseWeeklyDays(raw) {
5215
+ const text7 = raw.trim().toLowerCase();
5216
+ if (!text7) throw new Error("Pick at least one day");
5217
+ if (text7 === "daily" || text7 === "all" || text7 === "every day" || text7 === "everyday") {
5218
+ return [0, 1, 2, 3, 4, 5, 6];
5219
+ }
5220
+ if (text7 === "weekdays" || text7 === "weekday") return [1, 2, 3, 4, 5];
5221
+ if (text7 === "weekends" || text7 === "weekend") return [0, 6];
5222
+ const days = [];
5223
+ for (const part of text7.split(/[\s,]+/).filter(Boolean)) {
5224
+ const range = part.split("-");
5225
+ if (range.length === 2 && range[0] && range[1]) {
5226
+ const start = dayIndex(range[0]);
5227
+ const end = dayIndex(range[1]);
5228
+ if (start === void 0 || end === void 0) {
5229
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
5230
+ }
5231
+ if (start <= end) {
5232
+ for (let d = start; d <= end; d++) days.push(d);
5233
+ } else {
5234
+ for (let d = start; d <= 6; d++) days.push(d);
5235
+ for (let d = 0; d <= end; d++) days.push(d);
5236
+ }
5237
+ continue;
5238
+ }
5239
+ const n = dayIndex(part);
5240
+ if (n === void 0) {
5241
+ throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
5242
+ }
5243
+ days.push(n);
5244
+ }
5245
+ return normalizeWeekdays(days);
5246
+ }
5247
+ function normalizeWeeklyTime(raw) {
5248
+ const m = raw.trim().match(TIME_RE);
5249
+ if (!m) throw new Error(`Invalid time: ${raw} (use HH:mm, 24-hour)`);
5250
+ return `${m[1]}:${m[2]}`;
5251
+ }
5252
+ function formatWeeklyDays(days) {
5253
+ const key = normalizeWeekdays(days).join(",");
5254
+ if (key === "0,1,2,3,4,5,6") return "daily";
5255
+ if (key === "1,2,3,4,5") return "weekdays";
5256
+ if (key === "0,6") return "weekends";
5257
+ return normalizeWeekdays(days).map((day) => WEEKDAY_LABELS[day]).join(", ");
5258
+ }
5259
+ function weeklyCronExpr(when) {
5260
+ const time = normalizeWeeklyTime(when.time);
5261
+ const [hh, mm] = time.split(":");
5262
+ const days = normalizeWeekdays(when.days);
5263
+ return `${Number(mm)} ${Number(hh)} * * ${days.join(",")}`;
5264
+ }
5265
+ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
5266
+ if (when.kind === "once") {
5267
+ const at = new Date(when.at);
5268
+ if (Number.isNaN(at.getTime())) {
5269
+ throw new Error(`Invalid once-at datetime: ${when.at}`);
5270
+ }
5271
+ return at;
5272
+ }
5273
+ if (when.kind === "every") {
5274
+ const ms = parseDurationMs(when.every);
5275
+ if (!ms) {
5276
+ throw new Error(`Invalid interval: ${when.every} (use 15m, 1h, 6h, 1d)`);
5277
+ }
5278
+ return new Date(from.getTime() + ms);
5279
+ }
5280
+ if (when.kind === "weekly") {
5281
+ return computeNextRunAt(
5282
+ { kind: "cron", expr: weeklyCronExpr(when), tz: when.tz },
5283
+ from
5284
+ );
5285
+ }
5286
+ try {
5287
+ const job = new import_croner.Cron(when.expr, {
5288
+ timezone: when.tz?.trim() || void 0,
5289
+ paused: true
5290
+ });
5291
+ const next = job.nextRun(from);
5292
+ if (!next) {
5293
+ throw new Error(`Cron expression has no next run: ${when.expr}`);
5294
+ }
5295
+ return next;
5296
+ } catch (err) {
5297
+ const message = err instanceof Error ? err.message : String(err);
5298
+ throw new Error(`Invalid cron: ${message}`);
5299
+ }
5300
+ }
5301
+ function formatScheduleWhen(when) {
5302
+ if (when.kind === "once") return `once at ${when.at}`;
5303
+ if (when.kind === "every") return `every ${when.every}`;
5304
+ if (when.kind === "weekly") {
5305
+ const label = `${formatWeeklyDays(when.days)} at ${normalizeWeeklyTime(when.time)}`;
5306
+ const tz2 = when.tz?.trim();
5307
+ return tz2 ? `${label} (${tz2})` : label;
5308
+ }
5309
+ const tz = when.tz?.trim();
5310
+ return tz ? `cron ${when.expr} (${tz})` : `cron ${when.expr}`;
5311
+ }
5312
+ function normalizeWhen(raw) {
5313
+ if (!raw || typeof raw !== "object") {
5314
+ throw new Error("Schedule is missing `when`");
5315
+ }
5316
+ const rec = raw;
5317
+ const kind = rec.kind;
5318
+ if (kind === "once" && typeof rec.at === "string") {
5319
+ return { kind: "once", at: rec.at };
5320
+ }
5321
+ if (kind === "every" && typeof rec.every === "string") {
5322
+ return { kind: "every", every: rec.every };
5323
+ }
5324
+ if (kind === "cron" && typeof rec.expr === "string") {
5325
+ return {
5326
+ kind: "cron",
5327
+ expr: rec.expr,
5328
+ tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
5329
+ };
5330
+ }
5331
+ if (kind === "weekly" && Array.isArray(rec.days) && typeof rec.time === "string") {
5332
+ return {
5333
+ kind: "weekly",
5334
+ days: normalizeWeekdays(rec.days.map((day) => Number(day))),
5335
+ time: normalizeWeeklyTime(rec.time),
5336
+ tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
5337
+ };
5338
+ }
5339
+ throw new Error("Schedule `when` must be once, every, weekly, or cron");
5340
+ }
5341
+ var import_croner, WEEKDAY_LABELS, DURATION_RE, UNIT_MS, DAY_ALIASES, TIME_RE;
5342
+ var init_schedule_when = __esm({
5343
+ "src/store/schedule-when.ts"() {
5344
+ "use strict";
5345
+ import_croner = require("croner");
5346
+ WEEKDAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
5347
+ DURATION_RE = /^(\d+)(s|m|h|d)$/i;
5348
+ UNIT_MS = {
5349
+ s: 1e3,
5350
+ m: 6e4,
5351
+ h: 36e5,
5352
+ d: 864e5
5353
+ };
5354
+ DAY_ALIASES = {
5355
+ sun: 0,
5356
+ sunday: 0,
5357
+ mon: 1,
5358
+ monday: 1,
5359
+ tue: 2,
5360
+ tues: 2,
5361
+ tuesday: 2,
5362
+ wed: 3,
5363
+ wednesday: 3,
5364
+ thu: 4,
5365
+ thur: 4,
5366
+ thurs: 4,
5367
+ thursday: 4,
5368
+ fri: 5,
5369
+ friday: 5,
5370
+ sat: 6,
5371
+ saturday: 6
5372
+ };
5373
+ TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/;
5374
+ }
5375
+ });
5376
+
5377
+ // src/store/schedules.ts
5378
+ function schedulesPath() {
5379
+ return (0, import_node_path15.join)(appDataDir(), "schedules.json");
5380
+ }
5381
+ function defaultScheduleName(prompt) {
5382
+ const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
5383
+ if (!line) return "Untitled automation";
5384
+ return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
5385
+ }
5386
+ function resolveScheduleThreadId(raw, env = process.env) {
5387
+ const value = raw?.trim() || "";
5388
+ if (!value) return null;
5389
+ if (value.toLowerCase() === "self") {
5390
+ return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
5391
+ }
5392
+ return value;
5393
+ }
5394
+ function nowIso(d = /* @__PURE__ */ new Date()) {
5395
+ return d.toISOString();
5396
+ }
5397
+ function normalizeOptionalBool(raw) {
5398
+ if (raw === true || raw === false) return raw;
5399
+ return null;
5400
+ }
5401
+ function normalizeAutonomy2(raw) {
5402
+ return raw === "default" || raw === "full" ? raw : null;
5403
+ }
5404
+ function normalizeAttachments(raw) {
5405
+ if (!Array.isArray(raw)) return [];
5406
+ const out = [];
5407
+ for (const item of raw) {
5408
+ if (!item || typeof item !== "object") continue;
5409
+ const row2 = item;
5410
+ if (typeof row2.id !== "string" || !row2.id.trim()) continue;
5411
+ if (typeof row2.name !== "string" || !row2.name) continue;
5412
+ if (typeof row2.content !== "string") continue;
5413
+ if (!ATTACHMENT_KINDS.has(row2.kind)) continue;
5414
+ const next = {
5415
+ id: row2.id,
5416
+ name: row2.name,
5417
+ kind: row2.kind,
5418
+ content: row2.content
5419
+ };
5420
+ if (typeof row2.path === "string" && row2.path) next.path = row2.path;
5421
+ if (typeof row2.previewDataUrl === "string" && row2.previewDataUrl) {
5422
+ next.previewDataUrl = row2.previewDataUrl;
5423
+ }
5424
+ out.push(next);
5425
+ }
5426
+ return out;
5427
+ }
5428
+ function normalizeTask(raw) {
5429
+ const when = normalizeWhen(raw.when);
5430
+ const agent = raw.agent && isOrchestratorCapableAgent(raw.agent) ? raw.agent : null;
5431
+ return {
5432
+ id: raw.id,
5433
+ name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : defaultScheduleName(raw.prompt),
5434
+ prompt: String(raw.prompt ?? ""),
5435
+ enabled: Boolean(raw.enabled),
5436
+ when,
5437
+ threadId: raw.threadId?.trim() || null,
5438
+ agent,
5439
+ model: raw.model?.trim() || null,
5440
+ effort: normalizeThinkingEffort(raw.effort),
5441
+ fast: normalizeOptionalBool(raw.fast),
5442
+ planMode: normalizeOptionalBool(raw.planMode),
5443
+ autonomy: normalizeAutonomy2(raw.autonomy),
5444
+ attachments: normalizeAttachments(raw.attachments),
5445
+ archiveChat: raw.archiveChat !== false,
5446
+ nextRunAt: raw.nextRunAt,
5447
+ lastRunAt: raw.lastRunAt ?? null,
5448
+ lastThreadId: raw.lastThreadId ?? null,
5449
+ lastError: raw.lastError ?? null,
5450
+ createdBy: raw.createdBy === "mcp" || raw.createdBy === "cli" || raw.createdBy === "ui" ? raw.createdBy : "ui",
5451
+ createdAt: raw.createdAt,
5452
+ updatedAt: raw.updatedAt
5453
+ };
5454
+ }
5455
+ function readAll() {
5456
+ const path2 = schedulesPath();
5457
+ if (!(0, import_node_fs12.existsSync)(path2)) return [];
5458
+ try {
5459
+ const parsed = JSON.parse((0, import_node_fs12.readFileSync)(path2, "utf8"));
5460
+ if (!Array.isArray(parsed)) return [];
5461
+ return parsed.map((row2) => {
5462
+ try {
5463
+ return normalizeTask(row2);
5464
+ } catch {
5465
+ return null;
5466
+ }
5467
+ }).filter((row2) => Boolean(row2?.id && row2.prompt));
5468
+ } catch {
5469
+ return [];
5470
+ }
5471
+ }
5472
+ function writeAll(rows) {
5473
+ writePrivateFile(schedulesPath(), `${JSON.stringify(rows, null, 2)}
5474
+ `);
5475
+ }
5476
+ function buildTask(input, now) {
5477
+ const prompt = input.prompt.trim();
5478
+ if (!prompt) throw new Error("Schedule prompt is required");
5479
+ const when = normalizeWhen(input.when);
5480
+ const nextRunAt = computeNextRunAt(when, now).toISOString();
5481
+ const ts = nowIso(now);
5482
+ const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
5483
+ return {
5484
+ id: (0, import_node_crypto5.randomUUID)(),
5485
+ name: input.name?.trim() || defaultScheduleName(prompt),
5486
+ prompt,
5487
+ enabled: input.enabled !== false,
5488
+ when,
5489
+ threadId: input.threadId?.trim() || null,
5490
+ agent,
5491
+ model: input.model?.trim() || null,
5492
+ effort: input.effort != null ? normalizeThinkingEffort(input.effort) : null,
5493
+ fast: normalizeOptionalBool(input.fast),
5494
+ planMode: normalizeOptionalBool(input.planMode),
5495
+ autonomy: normalizeAutonomy2(input.autonomy),
5496
+ attachments: normalizeAttachments(input.attachments),
5497
+ archiveChat: input.archiveChat !== false,
5498
+ nextRunAt,
5499
+ lastRunAt: null,
5500
+ lastThreadId: null,
5501
+ lastError: null,
5502
+ createdBy: input.createdBy,
5503
+ createdAt: ts,
5504
+ updatedAt: ts
5505
+ };
5506
+ }
5507
+ function listSchedules() {
5508
+ return readAll().sort((a, b) => a.nextRunAt.localeCompare(b.nextRunAt));
5509
+ }
5510
+ function hasEnabledSchedules() {
5511
+ return listSchedules().some((row2) => row2.enabled);
5512
+ }
5513
+ function scheduleNeedsCaffeinate(task, now = /* @__PURE__ */ new Date()) {
5514
+ if (!task.enabled) return false;
5515
+ const at = new Date(task.nextRunAt);
5516
+ if (Number.isNaN(at.getTime())) return false;
5517
+ return at.getTime() > now.getTime();
5518
+ }
5519
+ function ensureCaffeinateForFutureSchedule(task) {
5520
+ if (!scheduleNeedsCaffeinate(task)) return;
5521
+ if (caffeinateWhileSchedulesEnabled()) return;
5522
+ updateAdvancedSettings({ caffeinateWhileSchedules: true });
5523
+ }
5524
+ function getSchedule(id) {
5525
+ const key = id.trim();
5526
+ if (!key) return null;
5527
+ return readAll().find(
5528
+ (row2) => row2.id === key || row2.id.startsWith(key) && key.length >= 8
5529
+ ) ?? null;
5530
+ }
5531
+ function createSchedule(input) {
5532
+ const task = buildTask(input, /* @__PURE__ */ new Date());
5533
+ const rows = readAll();
5534
+ rows.push(task);
5535
+ writeAll(rows);
5536
+ ensureCaffeinateForFutureSchedule(task);
5537
+ return task;
5538
+ }
5539
+ function updateSchedule(id, patch) {
5540
+ const rows = readAll();
5541
+ const idx = rows.findIndex((row2) => row2.id === id || id.length >= 8 && row2.id.startsWith(id));
5542
+ if (idx < 0) throw new Error(`Automation not found: ${id}`);
5543
+ const prev = rows[idx];
5544
+ const when = patch.when ? normalizeWhen(patch.when) : prev.when;
5545
+ const prompt = patch.prompt !== void 0 ? patch.prompt.trim() : prev.prompt;
5546
+ if (!prompt) throw new Error("Schedule prompt is required");
5547
+ const whenChanged = patch.when !== void 0;
5548
+ const next = {
5549
+ ...prev,
5550
+ name: patch.name !== void 0 ? patch.name.trim() || defaultScheduleName(prompt) : prev.name,
5551
+ prompt,
5552
+ enabled: patch.enabled ?? prev.enabled,
5553
+ when,
5554
+ threadId: patch.threadId !== void 0 ? patch.threadId?.trim() || null : prev.threadId,
5555
+ agent: patch.agent !== void 0 ? patch.agent ? coerceOrchestratorAgent(patch.agent) : null : prev.agent,
5556
+ model: patch.model !== void 0 ? patch.model?.trim() || null : prev.model,
5557
+ effort: patch.effort !== void 0 ? normalizeThinkingEffort(patch.effort) : prev.effort,
5558
+ fast: patch.fast !== void 0 ? normalizeOptionalBool(patch.fast) : prev.fast,
5559
+ planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
5560
+ autonomy: patch.autonomy !== void 0 ? normalizeAutonomy2(patch.autonomy) : prev.autonomy,
5561
+ attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
5562
+ archiveChat: patch.archiveChat !== void 0 ? patch.archiveChat !== false : prev.archiveChat,
5563
+ nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
5564
+ lastError: whenChanged ? null : prev.lastError,
5565
+ updatedAt: nowIso()
5566
+ };
5567
+ rows[idx] = next;
5568
+ writeAll(rows);
5569
+ ensureCaffeinateForFutureSchedule(next);
5570
+ return next;
5571
+ }
5572
+ function deleteSchedulesForThread(threadId) {
5573
+ const id = threadId.trim();
5574
+ if (!id) return;
5575
+ const rows = readAll();
5576
+ const next = rows.filter((row2) => row2.threadId !== id);
5577
+ if (next.length === rows.length) return;
5578
+ writeAll(next);
5579
+ }
5580
+ function deleteSchedule(id) {
5581
+ const rows = readAll();
5582
+ const next = rows.filter(
5583
+ (row2) => row2.id !== id && !(id.length >= 8 && row2.id.startsWith(id))
5584
+ );
5585
+ if (next.length === rows.length) throw new Error(`Automation not found: ${id}`);
5586
+ writeAll(next);
5587
+ }
5588
+ function recordScheduleRun(id, result) {
5589
+ const rows = readAll();
5590
+ const idx = rows.findIndex((row2) => row2.id === id);
5591
+ if (idx < 0) throw new Error(`Automation not found: ${id}`);
5592
+ const prev = rows[idx];
5593
+ const firedAt = result.firedAt ?? /* @__PURE__ */ new Date();
5594
+ const next = {
5595
+ ...prev,
5596
+ enabled: prev.when.kind === "once" ? false : prev.enabled,
5597
+ nextRunAt: prev.when.kind === "once" ? prev.nextRunAt : computeNextRunAt(prev.when, firedAt).toISOString(),
5598
+ lastRunAt: nowIso(firedAt),
5599
+ lastThreadId: result.lastThreadId !== void 0 ? result.lastThreadId : prev.lastThreadId,
5600
+ lastError: result.lastError ?? null,
5601
+ updatedAt: nowIso(firedAt)
5602
+ };
5603
+ rows[idx] = next;
5604
+ writeAll(rows);
5605
+ return next;
5606
+ }
5607
+ var import_node_crypto5, import_node_fs12, import_node_path15, ATTACHMENT_KINDS;
5608
+ var init_schedules = __esm({
5609
+ "src/store/schedules.ts"() {
5610
+ "use strict";
5611
+ import_node_crypto5 = require("crypto");
5612
+ import_node_fs12 = require("fs");
5613
+ import_node_path15 = require("path");
5614
+ init_schedule_when();
5615
+ init_orchestrator_capable();
5616
+ init_thinking_effort();
5617
+ init_paths();
5618
+ init_private_file();
5619
+ init_app_settings();
5620
+ init_schedule_when();
5621
+ ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
5622
+ "transcript",
5623
+ "file",
5624
+ "issue",
5625
+ "workspace",
5626
+ "diff-comment",
5627
+ "code-ref"
5628
+ ]);
5629
+ }
5630
+ });
5631
+
5188
5632
  // src/store/thread-store.ts
5189
5633
  var thread_store_exports = {};
5190
5634
  __export(thread_store_exports, {
@@ -5205,7 +5649,7 @@ __export(thread_store_exports, {
5205
5649
  withThreadLock: () => withThreadLock,
5206
5650
  writeThread: () => writeThread
5207
5651
  });
5208
- function nowIso() {
5652
+ function nowIso2() {
5209
5653
  return (/* @__PURE__ */ new Date()).toISOString();
5210
5654
  }
5211
5655
  function resolveThreadEffort(raw) {
@@ -5294,9 +5738,9 @@ function normalizeThread(raw) {
5294
5738
  };
5295
5739
  }
5296
5740
  function createEmptyThread(partial) {
5297
- const ts = nowIso();
5741
+ const ts = nowIso2();
5298
5742
  return {
5299
- id: (0, import_node_crypto5.randomUUID)(),
5743
+ id: (0, import_node_crypto6.randomUUID)(),
5300
5744
  sessionId: partial.sessionId ?? null,
5301
5745
  autonomy: partial.autonomy ?? "default",
5302
5746
  model: partial.model ?? null,
@@ -5349,7 +5793,7 @@ function createEmptyThread(partial) {
5349
5793
  }
5350
5794
  async function withThreadLock(id, fn) {
5351
5795
  const lockPath = threadLockPath(id);
5352
- (0, import_node_fs12.writeFileSync)(lockPath, "", { flag: "a" });
5796
+ (0, import_node_fs13.writeFileSync)(lockPath, "", { flag: "a" });
5353
5797
  let release;
5354
5798
  try {
5355
5799
  release = await import_proper_lockfile.default.lock(lockPath, {
@@ -5370,7 +5814,7 @@ function cacheForDir() {
5370
5814
  }
5371
5815
  function fileMtimeMs(path2) {
5372
5816
  try {
5373
- return (0, import_node_fs12.statSync)(path2).mtimeMs;
5817
+ return (0, import_node_fs13.statSync)(path2).mtimeMs;
5374
5818
  } catch {
5375
5819
  return null;
5376
5820
  }
@@ -5403,7 +5847,7 @@ function readThread(id) {
5403
5847
  const cache2 = cacheForDir();
5404
5848
  const cached2 = cache2.byId.get(id);
5405
5849
  if (cached2 && cache2.mtimeMs.get(id) === mtimeMs) return cached2;
5406
- const raw = (0, import_node_fs12.readFileSync)(path2, "utf8");
5850
+ const raw = (0, import_node_fs13.readFileSync)(path2, "utf8");
5407
5851
  const thread = normalizeThread(JSON.parse(raw));
5408
5852
  rememberThread(thread, mtimeMs);
5409
5853
  return thread;
@@ -5411,9 +5855,9 @@ function readThread(id) {
5411
5855
  function writeThread(thread, opts) {
5412
5856
  const path2 = threadFilePath(idPath(thread.id));
5413
5857
  const tmp = `${path2}.${process.pid}.tmp`;
5414
- const next = opts?.touch === false ? { ...thread } : { ...thread, updatedAt: nowIso() };
5415
- (0, import_node_fs12.writeFileSync)(tmp, JSON.stringify(next, null, 2), "utf8");
5416
- (0, import_node_fs12.renameSync)(tmp, path2);
5858
+ const next = opts?.touch === false ? { ...thread } : { ...thread, updatedAt: nowIso2() };
5859
+ (0, import_node_fs13.writeFileSync)(tmp, JSON.stringify(next, null, 2), "utf8");
5860
+ (0, import_node_fs13.renameSync)(tmp, path2);
5417
5861
  const mtimeMs = fileMtimeMs(path2) ?? Date.now();
5418
5862
  rememberThread(next, mtimeMs);
5419
5863
  selfWriteMtimes.set(thread.id, mtimeMs);
@@ -5427,12 +5871,12 @@ function idPath(id) {
5427
5871
  return id;
5428
5872
  }
5429
5873
  function isThreadRecordFile(nameOrPath) {
5430
- const name = (0, import_node_path15.basename)(nameOrPath);
5874
+ const name = (0, import_node_path16.basename)(nameOrPath);
5431
5875
  return name.endsWith(".json") && !name.endsWith(".live.json") && !name.endsWith(".setup.log.json");
5432
5876
  }
5433
5877
  function listThreads(opts) {
5434
5878
  const cache2 = cacheForDir();
5435
- const files = (0, import_node_fs12.readdirSync)(threadsDir()).filter(isThreadRecordFile);
5879
+ const files = (0, import_node_fs13.readdirSync)(threadsDir()).filter(isThreadRecordFile);
5436
5880
  const seen = /* @__PURE__ */ new Set();
5437
5881
  for (const f of files) {
5438
5882
  const id = f.replace(/\.json$/, "");
@@ -5455,30 +5899,29 @@ function listThreads(opts) {
5455
5899
  function deleteThreadRecord(id) {
5456
5900
  forgetThread(id);
5457
5901
  const path2 = threadFilePath(id);
5458
- if ((0, import_node_fs12.existsSync)(path2)) (0, import_node_fs12.unlinkSync)(path2);
5459
- const lock = threadLockPath(id);
5460
- if ((0, import_node_fs12.existsSync)(lock)) {
5461
- try {
5462
- (0, import_node_fs12.unlinkSync)(lock);
5463
- } catch {
5464
- }
5902
+ if ((0, import_node_fs13.existsSync)(path2)) (0, import_node_fs13.unlinkSync)(path2);
5903
+ try {
5904
+ if ((0, import_node_fs13.existsSync)(threadLockPath(id))) (0, import_node_fs13.unlinkSync)(threadLockPath(id));
5905
+ } catch {
5465
5906
  }
5466
5907
  try {
5467
- (0, import_node_fs12.rmSync)(cursorSdkStoreDir(id), { recursive: true, force: true });
5908
+ (0, import_node_fs13.rmSync)(cursorSdkStoreDir(id), { recursive: true, force: true });
5468
5909
  } catch {
5469
5910
  }
5911
+ deleteSchedulesForThread(id);
5470
5912
  }
5471
5913
  function updateThread(id, patch) {
5472
5914
  const current = readThread(id);
5473
5915
  if (!current) throw new Error(`Thread not found: ${id}`);
5474
- const next = { ...current, ...patch, id: current.id, updatedAt: nowIso() };
5916
+ const next = { ...current, ...patch, id: current.id, updatedAt: nowIso2() };
5475
5917
  if (next.status === "archived") {
5476
- next.archivedAt = typeof next.archivedAt === "string" && next.archivedAt.trim() || current.archivedAt || nowIso();
5918
+ next.archivedAt = typeof next.archivedAt === "string" && next.archivedAt.trim() || current.archivedAt || nowIso2();
5477
5919
  } else {
5478
5920
  delete next.archivedAt;
5479
5921
  }
5480
5922
  if (!next.ephemeralAutomationId?.trim()) delete next.ephemeralAutomationId;
5481
5923
  writeThread(next);
5924
+ if (patch.status === "archived" && current.status !== "archived") deleteSchedulesForThread(current.id);
5482
5925
  return next;
5483
5926
  }
5484
5927
  function appendMessage(id, message) {
@@ -5495,19 +5938,20 @@ function findThreadByRef(ref) {
5495
5938
  (t) => t.id === ref || t.id.startsWith(ref) || t.branchName === ref || t.title === ref
5496
5939
  ) ?? null;
5497
5940
  }
5498
- var import_node_crypto5, import_node_fs12, import_node_path15, import_proper_lockfile, listCache, selfWriteMtimes;
5941
+ var import_node_crypto6, import_node_fs13, import_node_path16, import_proper_lockfile, listCache, selfWriteMtimes;
5499
5942
  var init_thread_store = __esm({
5500
5943
  "src/store/thread-store.ts"() {
5501
5944
  "use strict";
5502
- import_node_crypto5 = require("crypto");
5945
+ import_node_crypto6 = require("crypto");
5503
5946
  init_worktree_labels();
5504
- import_node_fs12 = require("fs");
5505
- import_node_path15 = require("path");
5947
+ import_node_fs13 = require("fs");
5948
+ import_node_path16 = require("path");
5506
5949
  import_proper_lockfile = __toESM(require("proper-lockfile"), 1);
5507
5950
  init_thinking_effort();
5508
5951
  init_claude_mcp();
5509
5952
  init_cursor_store();
5510
5953
  init_paths();
5954
+ init_schedules();
5511
5955
  listCache = null;
5512
5956
  selfWriteMtimes = /* @__PURE__ */ new Map();
5513
5957
  }
@@ -8038,11 +8482,11 @@ function materialize(mem) {
8038
8482
  };
8039
8483
  }
8040
8484
  function setupLogPath(threadId) {
8041
- return (0, import_node_path16.join)(threadsDir(), `${threadId}.setup.log.json`);
8485
+ return (0, import_node_path17.join)(threadsDir(), `${threadId}.setup.log.json`);
8042
8486
  }
8043
8487
  function setupLogKeyForWorktree(worktreePath) {
8044
8488
  const norm = normalizeWorktreePath(worktreePath);
8045
- return `wt-${(0, import_node_crypto6.createHash)("sha1").update(norm).digest("hex").slice(0, 16)}`;
8489
+ return `wt-${(0, import_node_crypto7.createHash)("sha1").update(norm).digest("hex").slice(0, 16)}`;
8046
8490
  }
8047
8491
  function emptySetupLog() {
8048
8492
  return { ...EMPTY };
@@ -8064,7 +8508,7 @@ function persistNow(threadId) {
8064
8508
  const mem = memory.get(threadId);
8065
8509
  if (!mem) return;
8066
8510
  try {
8067
- (0, import_node_fs13.writeFileSync)(
8511
+ (0, import_node_fs14.writeFileSync)(
8068
8512
  setupLogPath(threadId),
8069
8513
  `${JSON.stringify(materialize(mem))}
8070
8514
  `,
@@ -8085,9 +8529,9 @@ function schedulePersist(threadId) {
8085
8529
  }
8086
8530
  function loadFromDisk(threadId) {
8087
8531
  const path2 = setupLogPath(threadId);
8088
- if (!(0, import_node_fs13.existsSync)(path2)) return null;
8532
+ if (!(0, import_node_fs14.existsSync)(path2)) return null;
8089
8533
  try {
8090
- const raw = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
8534
+ const raw = JSON.parse((0, import_node_fs14.readFileSync)(path2, "utf8"));
8091
8535
  return {
8092
8536
  output: typeof raw.output === "string" ? raw.output : "",
8093
8537
  running: Boolean(raw.running),
@@ -8143,13 +8587,13 @@ function finishSetupLog(threadId, exitCode, source) {
8143
8587
  persistNow(threadId);
8144
8588
  return materialize(mem);
8145
8589
  }
8146
- var import_node_crypto6, import_node_fs13, import_node_path16, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers, PERSIST_DEBOUNCE_MS;
8590
+ var import_node_crypto7, import_node_fs14, import_node_path17, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers, PERSIST_DEBOUNCE_MS;
8147
8591
  var init_setup_log = __esm({
8148
8592
  "src/store/setup-log.ts"() {
8149
8593
  "use strict";
8150
- import_node_crypto6 = require("crypto");
8151
- import_node_fs13 = require("fs");
8152
- import_node_path16 = require("path");
8594
+ import_node_crypto7 = require("crypto");
8595
+ import_node_fs14 = require("fs");
8596
+ import_node_path17 = require("path");
8153
8597
  init_worktree_labels();
8154
8598
  init_paths();
8155
8599
  EMPTY = {
@@ -8194,11 +8638,11 @@ function safeScriptToken(scriptName) {
8194
8638
  return token || "dev";
8195
8639
  }
8196
8640
  function runLogPath(key) {
8197
- return (0, import_node_path17.join)(threadsDir(), `${key}.run.log.json`);
8641
+ return (0, import_node_path18.join)(threadsDir(), `${key}.run.log.json`);
8198
8642
  }
8199
8643
  function runLogKeyForWorktree(worktreePath, scriptName) {
8200
8644
  const norm = normalizeWorktreePath(worktreePath);
8201
- const hash = (0, import_node_crypto7.createHash)("sha1").update(norm).digest("hex").slice(0, 16);
8645
+ const hash = (0, import_node_crypto8.createHash)("sha1").update(norm).digest("hex").slice(0, 16);
8202
8646
  return `wt-${hash}-run-${safeScriptToken(scriptName)}`;
8203
8647
  }
8204
8648
  function emptyRunLog() {
@@ -8222,7 +8666,7 @@ function persistNow2(key) {
8222
8666
  const mem = memory2.get(key);
8223
8667
  if (!mem) return;
8224
8668
  try {
8225
- (0, import_node_fs14.writeFileSync)(runLogPath(key), `${JSON.stringify(materialize2(mem))}
8669
+ (0, import_node_fs15.writeFileSync)(runLogPath(key), `${JSON.stringify(materialize2(mem))}
8226
8670
  `, "utf8");
8227
8671
  } catch {
8228
8672
  }
@@ -8239,9 +8683,9 @@ function schedulePersist2(key) {
8239
8683
  }
8240
8684
  function loadFromDisk2(key) {
8241
8685
  const path2 = runLogPath(key);
8242
- if (!(0, import_node_fs14.existsSync)(path2)) return null;
8686
+ if (!(0, import_node_fs15.existsSync)(path2)) return null;
8243
8687
  try {
8244
- const raw = JSON.parse((0, import_node_fs14.readFileSync)(path2, "utf8"));
8688
+ const raw = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
8245
8689
  return {
8246
8690
  output: typeof raw.output === "string" ? raw.output : "",
8247
8691
  running: Boolean(raw.running),
@@ -8299,13 +8743,13 @@ function finishRunLog(key, exitCode) {
8299
8743
  function flushRunLog(key) {
8300
8744
  persistNow2(key);
8301
8745
  }
8302
- var import_node_crypto7, import_node_fs14, import_node_path17, EMPTY2, MAX_RUN_LOG_CHARS, DEFAULT_RUN_LOG_TAIL_CHARS, memory2, persistTimers2, PERSIST_DEBOUNCE_MS2;
8746
+ var import_node_crypto8, import_node_fs15, import_node_path18, EMPTY2, MAX_RUN_LOG_CHARS, DEFAULT_RUN_LOG_TAIL_CHARS, memory2, persistTimers2, PERSIST_DEBOUNCE_MS2;
8303
8747
  var init_run_log = __esm({
8304
8748
  "src/store/run-log.ts"() {
8305
8749
  "use strict";
8306
- import_node_crypto7 = require("crypto");
8307
- import_node_fs14 = require("fs");
8308
- import_node_path17 = require("path");
8750
+ import_node_crypto8 = require("crypto");
8751
+ import_node_fs15 = require("fs");
8752
+ import_node_path18 = require("path");
8309
8753
  init_worktree_labels();
8310
8754
  init_setup_log();
8311
8755
  init_paths();
@@ -8323,441 +8767,6 @@ var init_run_log = __esm({
8323
8767
  }
8324
8768
  });
8325
8769
 
8326
- // src/store/schedule-when.ts
8327
- function parseDurationMs(every) {
8328
- const m = every.trim().match(DURATION_RE);
8329
- if (!m) return null;
8330
- const n = Number.parseInt(m[1], 10);
8331
- if (!Number.isFinite(n) || n <= 0) return null;
8332
- const unit = m[2].toLowerCase();
8333
- return n * UNIT_MS[unit];
8334
- }
8335
- function dayIndex(token) {
8336
- const text7 = token.trim().toLowerCase();
8337
- if (/^[0-6]$/.test(text7)) return Number(text7);
8338
- return DAY_ALIASES[text7];
8339
- }
8340
- function normalizeWeekdays(days) {
8341
- const set = /* @__PURE__ */ new Set();
8342
- for (const day of days) {
8343
- if (!Number.isInteger(day) || day < 0 || day > 6) {
8344
- throw new Error("Days must be 0 (Sunday) through 6 (Saturday)");
8345
- }
8346
- set.add(day);
8347
- }
8348
- if (set.size === 0) throw new Error("Pick at least one day");
8349
- return [...set].sort((a, b) => a - b);
8350
- }
8351
- function parseWeeklyDays(raw) {
8352
- const text7 = raw.trim().toLowerCase();
8353
- if (!text7) throw new Error("Pick at least one day");
8354
- if (text7 === "daily" || text7 === "all" || text7 === "every day" || text7 === "everyday") {
8355
- return [0, 1, 2, 3, 4, 5, 6];
8356
- }
8357
- if (text7 === "weekdays" || text7 === "weekday") return [1, 2, 3, 4, 5];
8358
- if (text7 === "weekends" || text7 === "weekend") return [0, 6];
8359
- const days = [];
8360
- for (const part of text7.split(/[\s,]+/).filter(Boolean)) {
8361
- const range = part.split("-");
8362
- if (range.length === 2 && range[0] && range[1]) {
8363
- const start = dayIndex(range[0]);
8364
- const end = dayIndex(range[1]);
8365
- if (start === void 0 || end === void 0) {
8366
- throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
8367
- }
8368
- if (start <= end) {
8369
- for (let d = start; d <= end; d++) days.push(d);
8370
- } else {
8371
- for (let d = start; d <= 6; d++) days.push(d);
8372
- for (let d = 0; d <= end; d++) days.push(d);
8373
- }
8374
- continue;
8375
- }
8376
- const n = dayIndex(part);
8377
- if (n === void 0) {
8378
- throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
8379
- }
8380
- days.push(n);
8381
- }
8382
- return normalizeWeekdays(days);
8383
- }
8384
- function normalizeWeeklyTime(raw) {
8385
- const m = raw.trim().match(TIME_RE);
8386
- if (!m) throw new Error(`Invalid time: ${raw} (use HH:mm, 24-hour)`);
8387
- return `${m[1]}:${m[2]}`;
8388
- }
8389
- function formatWeeklyDays(days) {
8390
- const key = normalizeWeekdays(days).join(",");
8391
- if (key === "0,1,2,3,4,5,6") return "daily";
8392
- if (key === "1,2,3,4,5") return "weekdays";
8393
- if (key === "0,6") return "weekends";
8394
- return normalizeWeekdays(days).map((day) => WEEKDAY_LABELS[day]).join(", ");
8395
- }
8396
- function weeklyCronExpr(when) {
8397
- const time = normalizeWeeklyTime(when.time);
8398
- const [hh, mm] = time.split(":");
8399
- const days = normalizeWeekdays(when.days);
8400
- return `${Number(mm)} ${Number(hh)} * * ${days.join(",")}`;
8401
- }
8402
- function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
8403
- if (when.kind === "once") {
8404
- const at = new Date(when.at);
8405
- if (Number.isNaN(at.getTime())) {
8406
- throw new Error(`Invalid once-at datetime: ${when.at}`);
8407
- }
8408
- return at;
8409
- }
8410
- if (when.kind === "every") {
8411
- const ms = parseDurationMs(when.every);
8412
- if (!ms) {
8413
- throw new Error(`Invalid interval: ${when.every} (use 15m, 1h, 6h, 1d)`);
8414
- }
8415
- return new Date(from.getTime() + ms);
8416
- }
8417
- if (when.kind === "weekly") {
8418
- return computeNextRunAt(
8419
- { kind: "cron", expr: weeklyCronExpr(when), tz: when.tz },
8420
- from
8421
- );
8422
- }
8423
- try {
8424
- const job = new import_croner.Cron(when.expr, {
8425
- timezone: when.tz?.trim() || void 0,
8426
- paused: true
8427
- });
8428
- const next = job.nextRun(from);
8429
- if (!next) {
8430
- throw new Error(`Cron expression has no next run: ${when.expr}`);
8431
- }
8432
- return next;
8433
- } catch (err) {
8434
- const message = err instanceof Error ? err.message : String(err);
8435
- throw new Error(`Invalid cron: ${message}`);
8436
- }
8437
- }
8438
- function formatScheduleWhen(when) {
8439
- if (when.kind === "once") return `once at ${when.at}`;
8440
- if (when.kind === "every") return `every ${when.every}`;
8441
- if (when.kind === "weekly") {
8442
- const label = `${formatWeeklyDays(when.days)} at ${normalizeWeeklyTime(when.time)}`;
8443
- const tz2 = when.tz?.trim();
8444
- return tz2 ? `${label} (${tz2})` : label;
8445
- }
8446
- const tz = when.tz?.trim();
8447
- return tz ? `cron ${when.expr} (${tz})` : `cron ${when.expr}`;
8448
- }
8449
- function normalizeWhen(raw) {
8450
- if (!raw || typeof raw !== "object") {
8451
- throw new Error("Schedule is missing `when`");
8452
- }
8453
- const rec = raw;
8454
- const kind = rec.kind;
8455
- if (kind === "once" && typeof rec.at === "string") {
8456
- return { kind: "once", at: rec.at };
8457
- }
8458
- if (kind === "every" && typeof rec.every === "string") {
8459
- return { kind: "every", every: rec.every };
8460
- }
8461
- if (kind === "cron" && typeof rec.expr === "string") {
8462
- return {
8463
- kind: "cron",
8464
- expr: rec.expr,
8465
- tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
8466
- };
8467
- }
8468
- if (kind === "weekly" && Array.isArray(rec.days) && typeof rec.time === "string") {
8469
- return {
8470
- kind: "weekly",
8471
- days: normalizeWeekdays(rec.days.map((day) => Number(day))),
8472
- time: normalizeWeeklyTime(rec.time),
8473
- tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
8474
- };
8475
- }
8476
- throw new Error("Schedule `when` must be once, every, weekly, or cron");
8477
- }
8478
- var import_croner, WEEKDAY_LABELS, DURATION_RE, UNIT_MS, DAY_ALIASES, TIME_RE;
8479
- var init_schedule_when = __esm({
8480
- "src/store/schedule-when.ts"() {
8481
- "use strict";
8482
- import_croner = require("croner");
8483
- WEEKDAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
8484
- DURATION_RE = /^(\d+)(s|m|h|d)$/i;
8485
- UNIT_MS = {
8486
- s: 1e3,
8487
- m: 6e4,
8488
- h: 36e5,
8489
- d: 864e5
8490
- };
8491
- DAY_ALIASES = {
8492
- sun: 0,
8493
- sunday: 0,
8494
- mon: 1,
8495
- monday: 1,
8496
- tue: 2,
8497
- tues: 2,
8498
- tuesday: 2,
8499
- wed: 3,
8500
- wednesday: 3,
8501
- thu: 4,
8502
- thur: 4,
8503
- thurs: 4,
8504
- thursday: 4,
8505
- fri: 5,
8506
- friday: 5,
8507
- sat: 6,
8508
- saturday: 6
8509
- };
8510
- TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/;
8511
- }
8512
- });
8513
-
8514
- // src/store/schedules.ts
8515
- function schedulesPath() {
8516
- return (0, import_node_path18.join)(appDataDir(), "schedules.json");
8517
- }
8518
- function defaultScheduleName(prompt) {
8519
- const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
8520
- if (!line) return "Untitled automation";
8521
- return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
8522
- }
8523
- function resolveScheduleThreadId(raw, env = process.env) {
8524
- const value = raw?.trim() || "";
8525
- if (!value) return null;
8526
- if (value.toLowerCase() === "self") {
8527
- return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
8528
- }
8529
- return value;
8530
- }
8531
- function nowIso2(d = /* @__PURE__ */ new Date()) {
8532
- return d.toISOString();
8533
- }
8534
- function normalizeOptionalBool(raw) {
8535
- if (raw === true || raw === false) return raw;
8536
- return null;
8537
- }
8538
- function normalizeAutonomy2(raw) {
8539
- return raw === "default" || raw === "full" ? raw : null;
8540
- }
8541
- function normalizeAttachments(raw) {
8542
- if (!Array.isArray(raw)) return [];
8543
- const out = [];
8544
- for (const item of raw) {
8545
- if (!item || typeof item !== "object") continue;
8546
- const row2 = item;
8547
- if (typeof row2.id !== "string" || !row2.id.trim()) continue;
8548
- if (typeof row2.name !== "string" || !row2.name) continue;
8549
- if (typeof row2.content !== "string") continue;
8550
- if (!ATTACHMENT_KINDS.has(row2.kind)) continue;
8551
- const next = {
8552
- id: row2.id,
8553
- name: row2.name,
8554
- kind: row2.kind,
8555
- content: row2.content
8556
- };
8557
- if (typeof row2.path === "string" && row2.path) next.path = row2.path;
8558
- if (typeof row2.previewDataUrl === "string" && row2.previewDataUrl) {
8559
- next.previewDataUrl = row2.previewDataUrl;
8560
- }
8561
- out.push(next);
8562
- }
8563
- return out;
8564
- }
8565
- function normalizeTask(raw) {
8566
- const when = normalizeWhen(raw.when);
8567
- const agent = raw.agent && isOrchestratorCapableAgent(raw.agent) ? raw.agent : null;
8568
- return {
8569
- id: raw.id,
8570
- name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : defaultScheduleName(raw.prompt),
8571
- prompt: String(raw.prompt ?? ""),
8572
- enabled: Boolean(raw.enabled),
8573
- when,
8574
- threadId: raw.threadId?.trim() || null,
8575
- agent,
8576
- model: raw.model?.trim() || null,
8577
- effort: normalizeThinkingEffort(raw.effort),
8578
- fast: normalizeOptionalBool(raw.fast),
8579
- planMode: normalizeOptionalBool(raw.planMode),
8580
- autonomy: normalizeAutonomy2(raw.autonomy),
8581
- attachments: normalizeAttachments(raw.attachments),
8582
- archiveChat: raw.archiveChat !== false,
8583
- nextRunAt: raw.nextRunAt,
8584
- lastRunAt: raw.lastRunAt ?? null,
8585
- lastThreadId: raw.lastThreadId ?? null,
8586
- lastError: raw.lastError ?? null,
8587
- createdBy: raw.createdBy === "mcp" || raw.createdBy === "cli" || raw.createdBy === "ui" ? raw.createdBy : "ui",
8588
- createdAt: raw.createdAt,
8589
- updatedAt: raw.updatedAt
8590
- };
8591
- }
8592
- function readAll() {
8593
- const path2 = schedulesPath();
8594
- if (!(0, import_node_fs15.existsSync)(path2)) return [];
8595
- try {
8596
- const parsed = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
8597
- if (!Array.isArray(parsed)) return [];
8598
- return parsed.map((row2) => {
8599
- try {
8600
- return normalizeTask(row2);
8601
- } catch {
8602
- return null;
8603
- }
8604
- }).filter((row2) => Boolean(row2?.id && row2.prompt));
8605
- } catch {
8606
- return [];
8607
- }
8608
- }
8609
- function writeAll(rows) {
8610
- writePrivateFile(schedulesPath(), `${JSON.stringify(rows, null, 2)}
8611
- `);
8612
- }
8613
- function buildTask(input, now) {
8614
- const prompt = input.prompt.trim();
8615
- if (!prompt) throw new Error("Schedule prompt is required");
8616
- const when = normalizeWhen(input.when);
8617
- const nextRunAt = computeNextRunAt(when, now).toISOString();
8618
- const ts = nowIso2(now);
8619
- const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
8620
- return {
8621
- id: (0, import_node_crypto8.randomUUID)(),
8622
- name: input.name?.trim() || defaultScheduleName(prompt),
8623
- prompt,
8624
- enabled: input.enabled !== false,
8625
- when,
8626
- threadId: input.threadId?.trim() || null,
8627
- agent,
8628
- model: input.model?.trim() || null,
8629
- effort: input.effort != null ? normalizeThinkingEffort(input.effort) : null,
8630
- fast: normalizeOptionalBool(input.fast),
8631
- planMode: normalizeOptionalBool(input.planMode),
8632
- autonomy: normalizeAutonomy2(input.autonomy),
8633
- attachments: normalizeAttachments(input.attachments),
8634
- archiveChat: input.archiveChat !== false,
8635
- nextRunAt,
8636
- lastRunAt: null,
8637
- lastThreadId: null,
8638
- lastError: null,
8639
- createdBy: input.createdBy,
8640
- createdAt: ts,
8641
- updatedAt: ts
8642
- };
8643
- }
8644
- function listSchedules() {
8645
- return readAll().sort((a, b) => a.nextRunAt.localeCompare(b.nextRunAt));
8646
- }
8647
- function hasEnabledSchedules() {
8648
- return listSchedules().some((row2) => row2.enabled);
8649
- }
8650
- function scheduleNeedsCaffeinate(task, now = /* @__PURE__ */ new Date()) {
8651
- if (!task.enabled) return false;
8652
- const at = new Date(task.nextRunAt);
8653
- if (Number.isNaN(at.getTime())) return false;
8654
- return at.getTime() > now.getTime();
8655
- }
8656
- function ensureCaffeinateForFutureSchedule(task) {
8657
- if (!scheduleNeedsCaffeinate(task)) return;
8658
- if (caffeinateWhileSchedulesEnabled()) return;
8659
- updateAdvancedSettings({ caffeinateWhileSchedules: true });
8660
- }
8661
- function getSchedule(id) {
8662
- const key = id.trim();
8663
- if (!key) return null;
8664
- return readAll().find(
8665
- (row2) => row2.id === key || row2.id.startsWith(key) && key.length >= 8
8666
- ) ?? null;
8667
- }
8668
- function createSchedule(input) {
8669
- const task = buildTask(input, /* @__PURE__ */ new Date());
8670
- const rows = readAll();
8671
- rows.push(task);
8672
- writeAll(rows);
8673
- ensureCaffeinateForFutureSchedule(task);
8674
- return task;
8675
- }
8676
- function updateSchedule(id, patch) {
8677
- const rows = readAll();
8678
- const idx = rows.findIndex((row2) => row2.id === id || id.length >= 8 && row2.id.startsWith(id));
8679
- if (idx < 0) throw new Error(`Automation not found: ${id}`);
8680
- const prev = rows[idx];
8681
- const when = patch.when ? normalizeWhen(patch.when) : prev.when;
8682
- const prompt = patch.prompt !== void 0 ? patch.prompt.trim() : prev.prompt;
8683
- if (!prompt) throw new Error("Schedule prompt is required");
8684
- const whenChanged = patch.when !== void 0;
8685
- const next = {
8686
- ...prev,
8687
- name: patch.name !== void 0 ? patch.name.trim() || defaultScheduleName(prompt) : prev.name,
8688
- prompt,
8689
- enabled: patch.enabled ?? prev.enabled,
8690
- when,
8691
- threadId: patch.threadId !== void 0 ? patch.threadId?.trim() || null : prev.threadId,
8692
- agent: patch.agent !== void 0 ? patch.agent ? coerceOrchestratorAgent(patch.agent) : null : prev.agent,
8693
- model: patch.model !== void 0 ? patch.model?.trim() || null : prev.model,
8694
- effort: patch.effort !== void 0 ? normalizeThinkingEffort(patch.effort) : prev.effort,
8695
- fast: patch.fast !== void 0 ? normalizeOptionalBool(patch.fast) : prev.fast,
8696
- planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
8697
- autonomy: patch.autonomy !== void 0 ? normalizeAutonomy2(patch.autonomy) : prev.autonomy,
8698
- attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
8699
- archiveChat: patch.archiveChat !== void 0 ? patch.archiveChat !== false : prev.archiveChat,
8700
- nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
8701
- lastError: whenChanged ? null : prev.lastError,
8702
- updatedAt: nowIso2()
8703
- };
8704
- rows[idx] = next;
8705
- writeAll(rows);
8706
- ensureCaffeinateForFutureSchedule(next);
8707
- return next;
8708
- }
8709
- function deleteSchedule(id) {
8710
- const rows = readAll();
8711
- const next = rows.filter(
8712
- (row2) => row2.id !== id && !(id.length >= 8 && row2.id.startsWith(id))
8713
- );
8714
- if (next.length === rows.length) throw new Error(`Automation not found: ${id}`);
8715
- writeAll(next);
8716
- }
8717
- function recordScheduleRun(id, result) {
8718
- const rows = readAll();
8719
- const idx = rows.findIndex((row2) => row2.id === id);
8720
- if (idx < 0) throw new Error(`Automation not found: ${id}`);
8721
- const prev = rows[idx];
8722
- const firedAt = result.firedAt ?? /* @__PURE__ */ new Date();
8723
- const next = {
8724
- ...prev,
8725
- enabled: prev.when.kind === "once" ? false : prev.enabled,
8726
- nextRunAt: prev.when.kind === "once" ? prev.nextRunAt : computeNextRunAt(prev.when, firedAt).toISOString(),
8727
- lastRunAt: nowIso2(firedAt),
8728
- lastThreadId: result.lastThreadId !== void 0 ? result.lastThreadId : prev.lastThreadId,
8729
- lastError: result.lastError ?? null,
8730
- updatedAt: nowIso2(firedAt)
8731
- };
8732
- rows[idx] = next;
8733
- writeAll(rows);
8734
- return next;
8735
- }
8736
- var import_node_crypto8, import_node_fs15, import_node_path18, ATTACHMENT_KINDS;
8737
- var init_schedules = __esm({
8738
- "src/store/schedules.ts"() {
8739
- "use strict";
8740
- import_node_crypto8 = require("crypto");
8741
- import_node_fs15 = require("fs");
8742
- import_node_path18 = require("path");
8743
- init_schedule_when();
8744
- init_orchestrator_capable();
8745
- init_thinking_effort();
8746
- init_paths();
8747
- init_private_file();
8748
- init_app_settings();
8749
- init_schedule_when();
8750
- ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
8751
- "transcript",
8752
- "file",
8753
- "issue",
8754
- "workspace",
8755
- "diff-comment",
8756
- "code-ref"
8757
- ]);
8758
- }
8759
- });
8760
-
8761
8770
  // src/store/desktop-host.ts
8762
8771
  function desktopHostPidPath() {
8763
8772
  return (0, import_node_path19.join)(appDataDir(), "desktop-host.pid");
@@ -13153,7 +13162,7 @@ var init_coordinator_prompt = __esm({
13153
13162
  "- 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.",
13154
13163
  "- 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.",
13155
13164
  "- 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.",
13156
- "- 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.",
13165
+ "- 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). A job bound to a chat is removed when that chat is archived. Omit chatId only for a Global automation that should outlive the chat. 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 and is removed when that chat is archived). 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.",
13157
13166
  "Projects:",
13158
13167
  "- add_project / remove_project \u2014 register or unregister a git repo",
13159
13168
  "Workspaces and chats:",
@@ -31373,7 +31382,7 @@ function formatWorktreeDirective(thread, opts) {
31373
31382
  lines.push(formatDevAccessHint(opts?.activeRuns));
31374
31383
  lines.push("");
31375
31384
  lines.push(
31376
- "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."
31385
+ "Automations (sidebar, after Board): `create_schedule` / `list_schedules` (`at` / `every` / `cron` / `time`+`days`). `archiveChat=false` keeps the new chat. `chatId=self` is removed when this chat is archived. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled."
31377
31386
  );
31378
31387
  lines.push(
31379
31388
  "`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
@@ -31393,7 +31402,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
31393
31402
  return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
31394
31403
  }
31395
31404
  function formatWorktreeReminder() {
31396
- 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).";
31405
+ 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 ends with the chat). set_workspace_tags updates labels on this workspace (omit ref).";
31397
31406
  }
31398
31407
  function formatClaudeSubagentReminder() {
31399
31408
  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.';
@@ -35451,7 +35460,15 @@ async function fireSchedule(id) {
35451
35460
  const deps = fireHooks ?? await defaultDeps();
35452
35461
  const prompt = formatScheduledPrompt(schedule.name, schedule.prompt);
35453
35462
  const target = schedule.threadId ? deps.findThread(schedule.threadId) : null;
35454
- if (target && target.status !== "archived") {
35463
+ if (schedule.threadId && (!target || target.status === "archived")) {
35464
+ deleteSchedule(schedule.id);
35465
+ return {
35466
+ ...schedule,
35467
+ enabled: false,
35468
+ lastError: "Removed because its chat is archived or gone."
35469
+ };
35470
+ }
35471
+ if (target) {
35455
35472
  const sent = await deps.send(target.id, prompt, {
35456
35473
  attachments: schedule.attachments,
35457
35474
  options: scheduleSendOptions(schedule)
@@ -35461,8 +35478,7 @@ async function fireSchedule(id) {
35461
35478
  lastError: null
35462
35479
  });
35463
35480
  }
35464
- const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
35465
- const archiveFresh = schedule.archiveChat !== false && !pinReplacement;
35481
+ const archiveFresh = schedule.archiveChat !== false;
35466
35482
  const created = await deps.startOrchestration({
35467
35483
  goal: prompt,
35468
35484
  agent: pickAgent(schedule),
@@ -35475,9 +35491,6 @@ async function fireSchedule(id) {
35475
35491
  ...archiveFresh ? { ephemeralAutomationId: schedule.id } : {},
35476
35492
  ephemeralTitle: schedule.name
35477
35493
  });
35478
- if (pinReplacement) {
35479
- updateSchedule(schedule.id, { threadId: created.id });
35480
- }
35481
35494
  return recordScheduleRun(schedule.id, {
35482
35495
  lastThreadId: created.id,
35483
35496
  lastError: null
@@ -35894,6 +35907,7 @@ __export(index_exports, {
35894
35907
  defaultScheduleName: () => defaultScheduleName,
35895
35908
  deleteBranchOnPurgeEnabled: () => deleteBranchOnPurgeEnabled,
35896
35909
  deleteSchedule: () => deleteSchedule,
35910
+ deleteSchedulesForThread: () => deleteSchedulesForThread,
35897
35911
  deleteThreadRecord: () => deleteThreadRecord,
35898
35912
  deriveTaskState: () => deriveTaskState,
35899
35913
  desktopHostPidPath: () => desktopHostPidPath,
@@ -45361,7 +45375,7 @@ function whenFromUpdate(id, args) {
45361
45375
  }
45362
45376
  function createdHint(schedule) {
45363
45377
  const awake = scheduleNeedsCaffeinate(schedule) ? " Caffeinate while automations are enabled is now on so the Mac can stay awake until this job is due." : "";
45364
- 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.";
45378
+ const fresh = schedule.threadId ? " This job is removed when that chat is archived." : 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.";
45365
45379
  return `Fires while Sideboard.app is running.${fresh}${awake}`;
45366
45380
  }
45367
45381
  function registerScheduleTools(server) {
@@ -45379,7 +45393,7 @@ function registerScheduleTools(server) {
45379
45393
  );
45380
45394
  server.tool(
45381
45395
  "create_schedule",
45382
- "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.",
45396
+ "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. A job bound to a chat is removed when that chat is archived or gone; it does not start a new Global chat. Omit chatId only for a Global automation that should outlive this 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.",
45383
45397
  {
45384
45398
  prompt: import_zod10.z.string().describe("User message / goal queued when the automation fires"),
45385
45399
  name: import_zod10.z.string().optional(),
@@ -45393,7 +45407,7 @@ function registerScheduleTools(server) {
45393
45407
  "Archive a new Global chat when the run finishes. Default true. Pass false to leave the chat open."
45394
45408
  ),
45395
45409
  chatId: import_zod10.z.string().optional().describe(
45396
- 'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
45410
+ 'Existing chat id, or "self" for this chat. That job is removed when the chat is archived. Omit only for a Global automation that should outlive this chat.'
45397
45411
  ),
45398
45412
  threadId: import_zod10.z.string().optional().describe("Legacy alias for chatId."),
45399
45413
  agent: import_zod10.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
@@ -45445,7 +45459,7 @@ function registerScheduleTools(server) {
45445
45459
  tz: import_zod10.z.string().optional(),
45446
45460
  archiveChat: import_zod10.z.boolean().optional().describe("Archive a new Global chat when the run finishes. False leaves it open."),
45447
45461
  chatId: import_zod10.z.string().optional().describe(
45448
- 'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
45462
+ 'Existing chat, "self", or empty string for a Global chat each run. A bound job is removed when that chat is archived.'
45449
45463
  ),
45450
45464
  threadId: import_zod10.z.string().optional().describe("Legacy alias for chatId."),
45451
45465
  agent: import_zod10.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
@@ -50039,6 +50053,7 @@ async function pollSlackOutboundWatches(opts) {
50039
50053
  defaultScheduleName,
50040
50054
  deleteBranchOnPurgeEnabled,
50041
50055
  deleteSchedule,
50056
+ deleteSchedulesForThread,
50042
50057
  deleteThreadRecord,
50043
50058
  deriveTaskState,
50044
50059
  desktopHostPidPath,