@sideboard-ai/core 0.1.325 → 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 (76) hide show
  1. package/dist/{abletime-M7STTMZT.js → abletime-NW4PJGHN.js} +4 -5
  2. package/dist/{abletime-VL234MCP.js → abletime-OW7LTFQD.js} +4 -5
  3. package/dist/{abletime-oauth-LMMGMAPA.js → abletime-oauth-KWQQRHOQ.js} +3 -4
  4. package/dist/{abletime-oauth-P4ZFBEAL.js → abletime-oauth-TPR2QDRI.js} +3 -4
  5. package/dist/{abletime-rest-DLV7EIWS.js → abletime-rest-5DCWN3P4.js} +3 -4
  6. package/dist/{abletime-rest-GOI7HELR.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/cursor-runner.cjs +1 -1
  10. package/dist/agents/cursor-runner.js +1 -1
  11. package/dist/{agents-MBIEAETM.js → agents-IYBY3JT5.js} +9 -10
  12. package/dist/{agents-LDRM3BTY.js → agents-XWIPONJR.js} +8 -9
  13. package/dist/{app-settings-UQHUNOLV.js → app-settings-A5D364JD.js} +1 -2
  14. package/dist/{app-settings-E7DMMEKC.js → app-settings-SA4HX3ZQ.js} +1 -2
  15. package/dist/{chunk-BOFYI6LC.js → chunk-2GEUFPQQ.js} +1 -1
  16. package/dist/{chunk-F65JMKUR.js → chunk-4F7THWIO.js} +2 -2
  17. package/dist/{chunk-A76MKMSF.js → chunk-4G3W23X3.js} +3 -3
  18. package/dist/{chunk-Q3FHTGIW.js → chunk-5Y2C7Q55.js} +441 -20
  19. package/dist/{chunk-PPTJMBUY.js → chunk-ATQLP22S.js} +1 -1
  20. package/dist/{chunk-NWGINMEA.js → chunk-CHYUUETG.js} +3 -3
  21. package/dist/{chunk-RZJTGBXY.js → chunk-D446IWPP.js} +50 -4
  22. package/dist/{chunk-ZEUMWHOH.js → chunk-DACWFN4O.js} +5 -5
  23. package/dist/{chunk-SACHHTBJ.js → chunk-DEB47BII.js} +52 -488
  24. package/dist/{chunk-2UP3XD2D.js → chunk-ECY7WL6T.js} +2 -2
  25. package/dist/{chunk-CI4M54LS.js → chunk-EJRCHCXM.js} +21 -4
  26. package/dist/{chunk-H36GKGU2.js → chunk-EUIVQAPK.js} +2 -2
  27. package/dist/{chunk-WYEST76H.js → chunk-IXWPCOLQ.js} +19 -203
  28. package/dist/{chunk-EO6ZFLG6.js → chunk-JKHCADKB.js} +5 -5
  29. package/dist/{chunk-SK6NYGPD.js → chunk-KLVW3J2B.js} +2 -2
  30. package/dist/{chunk-HG6KS7OT.js → chunk-MO46K6WK.js} +22 -18
  31. package/dist/{chunk-F2PVRF3B.js → chunk-N6ZF3XRP.js} +2 -1
  32. package/dist/{chunk-MFMDZAA6.js → chunk-NQGLDQYB.js} +16 -12
  33. package/dist/{chunk-U5WO6MEB.js → chunk-NURZOXGN.js} +1 -1
  34. package/dist/{chunk-ZHDXLDLA.js → chunk-Q4UPRQOS.js} +12 -12
  35. package/dist/{chunk-SQQBU7Z6.js → chunk-QMLDR63X.js} +2 -2
  36. package/dist/{chunk-BTF3ATST.js → chunk-R2C45NHT.js} +2 -2
  37. package/dist/{chunk-APKUI4BP.js → chunk-RDXODLEN.js} +4 -4
  38. package/dist/{chunk-7G27TB4M.js → chunk-RZJ3QJZT.js} +18 -14
  39. package/dist/{chunk-AQEKPJWU.js → chunk-SB5ADEZZ.js} +5 -5
  40. package/dist/{chunk-7LUOLYFM.js → chunk-SFXGD5KJ.js} +1 -1
  41. package/dist/{chunk-EZR6W4B3.js → chunk-TUH4ICHQ.js} +1 -1
  42. package/dist/chunk-ULWPSBAB.js +192 -0
  43. package/dist/{chunk-MBT6WDAB.js → chunk-UMXBH344.js} +1 -1
  44. package/dist/{chunk-SS6CYGLF.js → chunk-XBBZZHJY.js} +3 -3
  45. package/dist/{chunk-ZSHS46V5.js → chunk-Y6OHM672.js} +476 -20
  46. package/dist/{chunk-GNLKBS3U.js → chunk-YUHZEIVG.js} +69 -473
  47. package/dist/{chunk-VAFQS4D2.js → chunk-ZPBOWO64.js} +2 -2
  48. package/dist/{conductor-KB7L7NQN.js → conductor-CP7TW6TN.js} +7 -7
  49. package/dist/{conductor-D4KFZF54.js → conductor-D62Q2XJC.js} +6 -7
  50. package/dist/{coordinator-prompt-OBXEC63K.js → coordinator-prompt-3PJLU3UH.js} +5 -6
  51. package/dist/{coordinator-prompt-SUO4FRY3.js → coordinator-prompt-J7O7JB26.js} +5 -6
  52. package/dist/{global-workspace-Z2SURNHA.js → global-workspace-4CTLU2BV.js} +5 -6
  53. package/dist/{global-workspace-UHU2FXTY.js → global-workspace-Y3UDZYJX.js} +5 -6
  54. package/dist/index.cjs +527 -506
  55. package/dist/index.d.cts +16 -10
  56. package/dist/index.d.ts +16 -10
  57. package/dist/index.js +68 -66
  58. package/dist/{linear-SIHEBU3X.js → linear-CXWGH2TW.js} +2 -3
  59. package/dist/{linear-HFUOUJ5H.js → linear-WLDCJ3AF.js} +2 -3
  60. package/dist/{linear-list-JFV3EES6.js → linear-list-52LEDEWO.js} +1 -1
  61. package/dist/{linear-list-63I7QUC6.js → linear-list-FGVN65JU.js} +1 -1
  62. package/dist/mcp/run-stdio.cjs +19655 -19635
  63. package/dist/mcp/run-stdio.js +33 -34
  64. package/dist/{optional-cli-ULDP3O5S.js → optional-cli-FN4MGB6W.js} +4 -5
  65. package/dist/{optional-cli-U2GUA2X6.js → optional-cli-INRVBBBG.js} +3 -4
  66. package/dist/{orchestrator-3H4KNGPN.js → orchestrator-EN3ZKD73.js} +13 -13
  67. package/dist/{orchestrator-6ROMEWBP.js → orchestrator-W4TZBBDO.js} +11 -12
  68. package/dist/{thread-store-4ZN3DS2W.js → thread-store-4WE24XA2.js} +6 -2
  69. package/dist/{thread-store-IC4BQXDW.js → thread-store-K2ZNGUSM.js} +7 -3
  70. package/dist/{workspaces-MG5JHRKW.js → workspaces-MM7CQYR6.js} +5 -6
  71. package/dist/{workspaces-QJNKEGEM.js → workspaces-WYCVZ6XH.js} +5 -6
  72. package/dist/{worktree-MYJYUHZM.js → worktree-JBNEMJ5Z.js} +7 -8
  73. package/dist/{worktree-KSJPTHGZ.js → worktree-NNVRGPP4.js} +4 -5
  74. package/package.json +1 -1
  75. package/dist/chunk-77WWLBCI.js +0 -50
  76. 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 = [
@@ -1796,10 +1797,11 @@ function applyTurnUsage(current, incoming, scope = "request") {
1796
1797
  ...incoming.sessionCostUsd != null ? { sessionCostUsd: incoming.sessionCostUsd } : {}
1797
1798
  };
1798
1799
  }
1800
+ const explicit = incoming.lastRequestTokens != null && incoming.lastRequestTokens > 0 ? incoming.lastRequestTokens : void 0;
1799
1801
  return {
1800
1802
  ...incoming,
1801
1803
  costUsd: incoming.costUsd ?? current?.costUsd,
1802
- lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming),
1804
+ lastRequestTokens: current?.lastRequestTokens ?? explicit,
1803
1805
  ...incoming.sessionCostUsd == null && current?.sessionCostUsd != null ? { sessionCostUsd: current.sessionCostUsd } : {}
1804
1806
  };
1805
1807
  }
@@ -5184,6 +5186,449 @@ var init_cursor_store = __esm({
5184
5186
  }
5185
5187
  });
5186
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
+
5187
5632
  // src/store/thread-store.ts
5188
5633
  var thread_store_exports = {};
5189
5634
  __export(thread_store_exports, {
@@ -5204,7 +5649,7 @@ __export(thread_store_exports, {
5204
5649
  withThreadLock: () => withThreadLock,
5205
5650
  writeThread: () => writeThread
5206
5651
  });
5207
- function nowIso() {
5652
+ function nowIso2() {
5208
5653
  return (/* @__PURE__ */ new Date()).toISOString();
5209
5654
  }
5210
5655
  function resolveThreadEffort(raw) {
@@ -5293,9 +5738,9 @@ function normalizeThread(raw) {
5293
5738
  };
5294
5739
  }
5295
5740
  function createEmptyThread(partial) {
5296
- const ts = nowIso();
5741
+ const ts = nowIso2();
5297
5742
  return {
5298
- id: (0, import_node_crypto5.randomUUID)(),
5743
+ id: (0, import_node_crypto6.randomUUID)(),
5299
5744
  sessionId: partial.sessionId ?? null,
5300
5745
  autonomy: partial.autonomy ?? "default",
5301
5746
  model: partial.model ?? null,
@@ -5348,7 +5793,7 @@ function createEmptyThread(partial) {
5348
5793
  }
5349
5794
  async function withThreadLock(id, fn) {
5350
5795
  const lockPath = threadLockPath(id);
5351
- (0, import_node_fs12.writeFileSync)(lockPath, "", { flag: "a" });
5796
+ (0, import_node_fs13.writeFileSync)(lockPath, "", { flag: "a" });
5352
5797
  let release;
5353
5798
  try {
5354
5799
  release = await import_proper_lockfile.default.lock(lockPath, {
@@ -5369,7 +5814,7 @@ function cacheForDir() {
5369
5814
  }
5370
5815
  function fileMtimeMs(path2) {
5371
5816
  try {
5372
- return (0, import_node_fs12.statSync)(path2).mtimeMs;
5817
+ return (0, import_node_fs13.statSync)(path2).mtimeMs;
5373
5818
  } catch {
5374
5819
  return null;
5375
5820
  }
@@ -5402,7 +5847,7 @@ function readThread(id) {
5402
5847
  const cache2 = cacheForDir();
5403
5848
  const cached2 = cache2.byId.get(id);
5404
5849
  if (cached2 && cache2.mtimeMs.get(id) === mtimeMs) return cached2;
5405
- const raw = (0, import_node_fs12.readFileSync)(path2, "utf8");
5850
+ const raw = (0, import_node_fs13.readFileSync)(path2, "utf8");
5406
5851
  const thread = normalizeThread(JSON.parse(raw));
5407
5852
  rememberThread(thread, mtimeMs);
5408
5853
  return thread;
@@ -5410,9 +5855,9 @@ function readThread(id) {
5410
5855
  function writeThread(thread, opts) {
5411
5856
  const path2 = threadFilePath(idPath(thread.id));
5412
5857
  const tmp = `${path2}.${process.pid}.tmp`;
5413
- const next = opts?.touch === false ? { ...thread } : { ...thread, updatedAt: nowIso() };
5414
- (0, import_node_fs12.writeFileSync)(tmp, JSON.stringify(next, null, 2), "utf8");
5415
- (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);
5416
5861
  const mtimeMs = fileMtimeMs(path2) ?? Date.now();
5417
5862
  rememberThread(next, mtimeMs);
5418
5863
  selfWriteMtimes.set(thread.id, mtimeMs);
@@ -5426,12 +5871,12 @@ function idPath(id) {
5426
5871
  return id;
5427
5872
  }
5428
5873
  function isThreadRecordFile(nameOrPath) {
5429
- const name = (0, import_node_path15.basename)(nameOrPath);
5874
+ const name = (0, import_node_path16.basename)(nameOrPath);
5430
5875
  return name.endsWith(".json") && !name.endsWith(".live.json") && !name.endsWith(".setup.log.json");
5431
5876
  }
5432
5877
  function listThreads(opts) {
5433
5878
  const cache2 = cacheForDir();
5434
- const files = (0, import_node_fs12.readdirSync)(threadsDir()).filter(isThreadRecordFile);
5879
+ const files = (0, import_node_fs13.readdirSync)(threadsDir()).filter(isThreadRecordFile);
5435
5880
  const seen = /* @__PURE__ */ new Set();
5436
5881
  for (const f of files) {
5437
5882
  const id = f.replace(/\.json$/, "");
@@ -5454,30 +5899,29 @@ function listThreads(opts) {
5454
5899
  function deleteThreadRecord(id) {
5455
5900
  forgetThread(id);
5456
5901
  const path2 = threadFilePath(id);
5457
- if ((0, import_node_fs12.existsSync)(path2)) (0, import_node_fs12.unlinkSync)(path2);
5458
- const lock = threadLockPath(id);
5459
- if ((0, import_node_fs12.existsSync)(lock)) {
5460
- try {
5461
- (0, import_node_fs12.unlinkSync)(lock);
5462
- } catch {
5463
- }
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 {
5464
5906
  }
5465
5907
  try {
5466
- (0, import_node_fs12.rmSync)(cursorSdkStoreDir(id), { recursive: true, force: true });
5908
+ (0, import_node_fs13.rmSync)(cursorSdkStoreDir(id), { recursive: true, force: true });
5467
5909
  } catch {
5468
5910
  }
5911
+ deleteSchedulesForThread(id);
5469
5912
  }
5470
5913
  function updateThread(id, patch) {
5471
5914
  const current = readThread(id);
5472
5915
  if (!current) throw new Error(`Thread not found: ${id}`);
5473
- const next = { ...current, ...patch, id: current.id, updatedAt: nowIso() };
5916
+ const next = { ...current, ...patch, id: current.id, updatedAt: nowIso2() };
5474
5917
  if (next.status === "archived") {
5475
- 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();
5476
5919
  } else {
5477
5920
  delete next.archivedAt;
5478
5921
  }
5479
5922
  if (!next.ephemeralAutomationId?.trim()) delete next.ephemeralAutomationId;
5480
5923
  writeThread(next);
5924
+ if (patch.status === "archived" && current.status !== "archived") deleteSchedulesForThread(current.id);
5481
5925
  return next;
5482
5926
  }
5483
5927
  function appendMessage(id, message) {
@@ -5494,19 +5938,20 @@ function findThreadByRef(ref) {
5494
5938
  (t) => t.id === ref || t.id.startsWith(ref) || t.branchName === ref || t.title === ref
5495
5939
  ) ?? null;
5496
5940
  }
5497
- 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;
5498
5942
  var init_thread_store = __esm({
5499
5943
  "src/store/thread-store.ts"() {
5500
5944
  "use strict";
5501
- import_node_crypto5 = require("crypto");
5945
+ import_node_crypto6 = require("crypto");
5502
5946
  init_worktree_labels();
5503
- import_node_fs12 = require("fs");
5504
- import_node_path15 = require("path");
5947
+ import_node_fs13 = require("fs");
5948
+ import_node_path16 = require("path");
5505
5949
  import_proper_lockfile = __toESM(require("proper-lockfile"), 1);
5506
5950
  init_thinking_effort();
5507
5951
  init_claude_mcp();
5508
5952
  init_cursor_store();
5509
5953
  init_paths();
5954
+ init_schedules();
5510
5955
  listCache = null;
5511
5956
  selfWriteMtimes = /* @__PURE__ */ new Map();
5512
5957
  }
@@ -8037,11 +8482,11 @@ function materialize(mem) {
8037
8482
  };
8038
8483
  }
8039
8484
  function setupLogPath(threadId) {
8040
- return (0, import_node_path16.join)(threadsDir(), `${threadId}.setup.log.json`);
8485
+ return (0, import_node_path17.join)(threadsDir(), `${threadId}.setup.log.json`);
8041
8486
  }
8042
8487
  function setupLogKeyForWorktree(worktreePath) {
8043
8488
  const norm = normalizeWorktreePath(worktreePath);
8044
- 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)}`;
8045
8490
  }
8046
8491
  function emptySetupLog() {
8047
8492
  return { ...EMPTY };
@@ -8063,7 +8508,7 @@ function persistNow(threadId) {
8063
8508
  const mem = memory.get(threadId);
8064
8509
  if (!mem) return;
8065
8510
  try {
8066
- (0, import_node_fs13.writeFileSync)(
8511
+ (0, import_node_fs14.writeFileSync)(
8067
8512
  setupLogPath(threadId),
8068
8513
  `${JSON.stringify(materialize(mem))}
8069
8514
  `,
@@ -8084,9 +8529,9 @@ function schedulePersist(threadId) {
8084
8529
  }
8085
8530
  function loadFromDisk(threadId) {
8086
8531
  const path2 = setupLogPath(threadId);
8087
- if (!(0, import_node_fs13.existsSync)(path2)) return null;
8532
+ if (!(0, import_node_fs14.existsSync)(path2)) return null;
8088
8533
  try {
8089
- const raw = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
8534
+ const raw = JSON.parse((0, import_node_fs14.readFileSync)(path2, "utf8"));
8090
8535
  return {
8091
8536
  output: typeof raw.output === "string" ? raw.output : "",
8092
8537
  running: Boolean(raw.running),
@@ -8142,13 +8587,13 @@ function finishSetupLog(threadId, exitCode, source) {
8142
8587
  persistNow(threadId);
8143
8588
  return materialize(mem);
8144
8589
  }
8145
- 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;
8146
8591
  var init_setup_log = __esm({
8147
8592
  "src/store/setup-log.ts"() {
8148
8593
  "use strict";
8149
- import_node_crypto6 = require("crypto");
8150
- import_node_fs13 = require("fs");
8151
- import_node_path16 = require("path");
8594
+ import_node_crypto7 = require("crypto");
8595
+ import_node_fs14 = require("fs");
8596
+ import_node_path17 = require("path");
8152
8597
  init_worktree_labels();
8153
8598
  init_paths();
8154
8599
  EMPTY = {
@@ -8193,11 +8638,11 @@ function safeScriptToken(scriptName) {
8193
8638
  return token || "dev";
8194
8639
  }
8195
8640
  function runLogPath(key) {
8196
- return (0, import_node_path17.join)(threadsDir(), `${key}.run.log.json`);
8641
+ return (0, import_node_path18.join)(threadsDir(), `${key}.run.log.json`);
8197
8642
  }
8198
8643
  function runLogKeyForWorktree(worktreePath, scriptName) {
8199
8644
  const norm = normalizeWorktreePath(worktreePath);
8200
- 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);
8201
8646
  return `wt-${hash}-run-${safeScriptToken(scriptName)}`;
8202
8647
  }
8203
8648
  function emptyRunLog() {
@@ -8221,7 +8666,7 @@ function persistNow2(key) {
8221
8666
  const mem = memory2.get(key);
8222
8667
  if (!mem) return;
8223
8668
  try {
8224
- (0, import_node_fs14.writeFileSync)(runLogPath(key), `${JSON.stringify(materialize2(mem))}
8669
+ (0, import_node_fs15.writeFileSync)(runLogPath(key), `${JSON.stringify(materialize2(mem))}
8225
8670
  `, "utf8");
8226
8671
  } catch {
8227
8672
  }
@@ -8238,9 +8683,9 @@ function schedulePersist2(key) {
8238
8683
  }
8239
8684
  function loadFromDisk2(key) {
8240
8685
  const path2 = runLogPath(key);
8241
- if (!(0, import_node_fs14.existsSync)(path2)) return null;
8686
+ if (!(0, import_node_fs15.existsSync)(path2)) return null;
8242
8687
  try {
8243
- const raw = JSON.parse((0, import_node_fs14.readFileSync)(path2, "utf8"));
8688
+ const raw = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
8244
8689
  return {
8245
8690
  output: typeof raw.output === "string" ? raw.output : "",
8246
8691
  running: Boolean(raw.running),
@@ -8298,13 +8743,13 @@ function finishRunLog(key, exitCode) {
8298
8743
  function flushRunLog(key) {
8299
8744
  persistNow2(key);
8300
8745
  }
8301
- 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;
8302
8747
  var init_run_log = __esm({
8303
8748
  "src/store/run-log.ts"() {
8304
8749
  "use strict";
8305
- import_node_crypto7 = require("crypto");
8306
- import_node_fs14 = require("fs");
8307
- import_node_path17 = require("path");
8750
+ import_node_crypto8 = require("crypto");
8751
+ import_node_fs15 = require("fs");
8752
+ import_node_path18 = require("path");
8308
8753
  init_worktree_labels();
8309
8754
  init_setup_log();
8310
8755
  init_paths();
@@ -8322,441 +8767,6 @@ var init_run_log = __esm({
8322
8767
  }
8323
8768
  });
8324
8769
 
8325
- // src/store/schedule-when.ts
8326
- function parseDurationMs(every) {
8327
- const m = every.trim().match(DURATION_RE);
8328
- if (!m) return null;
8329
- const n = Number.parseInt(m[1], 10);
8330
- if (!Number.isFinite(n) || n <= 0) return null;
8331
- const unit = m[2].toLowerCase();
8332
- return n * UNIT_MS[unit];
8333
- }
8334
- function dayIndex(token) {
8335
- const text7 = token.trim().toLowerCase();
8336
- if (/^[0-6]$/.test(text7)) return Number(text7);
8337
- return DAY_ALIASES[text7];
8338
- }
8339
- function normalizeWeekdays(days) {
8340
- const set = /* @__PURE__ */ new Set();
8341
- for (const day of days) {
8342
- if (!Number.isInteger(day) || day < 0 || day > 6) {
8343
- throw new Error("Days must be 0 (Sunday) through 6 (Saturday)");
8344
- }
8345
- set.add(day);
8346
- }
8347
- if (set.size === 0) throw new Error("Pick at least one day");
8348
- return [...set].sort((a, b) => a - b);
8349
- }
8350
- function parseWeeklyDays(raw) {
8351
- const text7 = raw.trim().toLowerCase();
8352
- if (!text7) throw new Error("Pick at least one day");
8353
- if (text7 === "daily" || text7 === "all" || text7 === "every day" || text7 === "everyday") {
8354
- return [0, 1, 2, 3, 4, 5, 6];
8355
- }
8356
- if (text7 === "weekdays" || text7 === "weekday") return [1, 2, 3, 4, 5];
8357
- if (text7 === "weekends" || text7 === "weekend") return [0, 6];
8358
- const days = [];
8359
- for (const part of text7.split(/[\s,]+/).filter(Boolean)) {
8360
- const range = part.split("-");
8361
- if (range.length === 2 && range[0] && range[1]) {
8362
- const start = dayIndex(range[0]);
8363
- const end = dayIndex(range[1]);
8364
- if (start === void 0 || end === void 0) {
8365
- throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
8366
- }
8367
- if (start <= end) {
8368
- for (let d = start; d <= end; d++) days.push(d);
8369
- } else {
8370
- for (let d = start; d <= 6; d++) days.push(d);
8371
- for (let d = 0; d <= end; d++) days.push(d);
8372
- }
8373
- continue;
8374
- }
8375
- const n = dayIndex(part);
8376
- if (n === void 0) {
8377
- throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
8378
- }
8379
- days.push(n);
8380
- }
8381
- return normalizeWeekdays(days);
8382
- }
8383
- function normalizeWeeklyTime(raw) {
8384
- const m = raw.trim().match(TIME_RE);
8385
- if (!m) throw new Error(`Invalid time: ${raw} (use HH:mm, 24-hour)`);
8386
- return `${m[1]}:${m[2]}`;
8387
- }
8388
- function formatWeeklyDays(days) {
8389
- const key = normalizeWeekdays(days).join(",");
8390
- if (key === "0,1,2,3,4,5,6") return "daily";
8391
- if (key === "1,2,3,4,5") return "weekdays";
8392
- if (key === "0,6") return "weekends";
8393
- return normalizeWeekdays(days).map((day) => WEEKDAY_LABELS[day]).join(", ");
8394
- }
8395
- function weeklyCronExpr(when) {
8396
- const time = normalizeWeeklyTime(when.time);
8397
- const [hh, mm] = time.split(":");
8398
- const days = normalizeWeekdays(when.days);
8399
- return `${Number(mm)} ${Number(hh)} * * ${days.join(",")}`;
8400
- }
8401
- function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
8402
- if (when.kind === "once") {
8403
- const at = new Date(when.at);
8404
- if (Number.isNaN(at.getTime())) {
8405
- throw new Error(`Invalid once-at datetime: ${when.at}`);
8406
- }
8407
- return at;
8408
- }
8409
- if (when.kind === "every") {
8410
- const ms = parseDurationMs(when.every);
8411
- if (!ms) {
8412
- throw new Error(`Invalid interval: ${when.every} (use 15m, 1h, 6h, 1d)`);
8413
- }
8414
- return new Date(from.getTime() + ms);
8415
- }
8416
- if (when.kind === "weekly") {
8417
- return computeNextRunAt(
8418
- { kind: "cron", expr: weeklyCronExpr(when), tz: when.tz },
8419
- from
8420
- );
8421
- }
8422
- try {
8423
- const job = new import_croner.Cron(when.expr, {
8424
- timezone: when.tz?.trim() || void 0,
8425
- paused: true
8426
- });
8427
- const next = job.nextRun(from);
8428
- if (!next) {
8429
- throw new Error(`Cron expression has no next run: ${when.expr}`);
8430
- }
8431
- return next;
8432
- } catch (err) {
8433
- const message = err instanceof Error ? err.message : String(err);
8434
- throw new Error(`Invalid cron: ${message}`);
8435
- }
8436
- }
8437
- function formatScheduleWhen(when) {
8438
- if (when.kind === "once") return `once at ${when.at}`;
8439
- if (when.kind === "every") return `every ${when.every}`;
8440
- if (when.kind === "weekly") {
8441
- const label = `${formatWeeklyDays(when.days)} at ${normalizeWeeklyTime(when.time)}`;
8442
- const tz2 = when.tz?.trim();
8443
- return tz2 ? `${label} (${tz2})` : label;
8444
- }
8445
- const tz = when.tz?.trim();
8446
- return tz ? `cron ${when.expr} (${tz})` : `cron ${when.expr}`;
8447
- }
8448
- function normalizeWhen(raw) {
8449
- if (!raw || typeof raw !== "object") {
8450
- throw new Error("Schedule is missing `when`");
8451
- }
8452
- const rec = raw;
8453
- const kind = rec.kind;
8454
- if (kind === "once" && typeof rec.at === "string") {
8455
- return { kind: "once", at: rec.at };
8456
- }
8457
- if (kind === "every" && typeof rec.every === "string") {
8458
- return { kind: "every", every: rec.every };
8459
- }
8460
- if (kind === "cron" && typeof rec.expr === "string") {
8461
- return {
8462
- kind: "cron",
8463
- expr: rec.expr,
8464
- tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
8465
- };
8466
- }
8467
- if (kind === "weekly" && Array.isArray(rec.days) && typeof rec.time === "string") {
8468
- return {
8469
- kind: "weekly",
8470
- days: normalizeWeekdays(rec.days.map((day) => Number(day))),
8471
- time: normalizeWeeklyTime(rec.time),
8472
- tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
8473
- };
8474
- }
8475
- throw new Error("Schedule `when` must be once, every, weekly, or cron");
8476
- }
8477
- var import_croner, WEEKDAY_LABELS, DURATION_RE, UNIT_MS, DAY_ALIASES, TIME_RE;
8478
- var init_schedule_when = __esm({
8479
- "src/store/schedule-when.ts"() {
8480
- "use strict";
8481
- import_croner = require("croner");
8482
- WEEKDAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
8483
- DURATION_RE = /^(\d+)(s|m|h|d)$/i;
8484
- UNIT_MS = {
8485
- s: 1e3,
8486
- m: 6e4,
8487
- h: 36e5,
8488
- d: 864e5
8489
- };
8490
- DAY_ALIASES = {
8491
- sun: 0,
8492
- sunday: 0,
8493
- mon: 1,
8494
- monday: 1,
8495
- tue: 2,
8496
- tues: 2,
8497
- tuesday: 2,
8498
- wed: 3,
8499
- wednesday: 3,
8500
- thu: 4,
8501
- thur: 4,
8502
- thurs: 4,
8503
- thursday: 4,
8504
- fri: 5,
8505
- friday: 5,
8506
- sat: 6,
8507
- saturday: 6
8508
- };
8509
- TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/;
8510
- }
8511
- });
8512
-
8513
- // src/store/schedules.ts
8514
- function schedulesPath() {
8515
- return (0, import_node_path18.join)(appDataDir(), "schedules.json");
8516
- }
8517
- function defaultScheduleName(prompt) {
8518
- const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
8519
- if (!line) return "Untitled automation";
8520
- return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
8521
- }
8522
- function resolveScheduleThreadId(raw, env = process.env) {
8523
- const value = raw?.trim() || "";
8524
- if (!value) return null;
8525
- if (value.toLowerCase() === "self") {
8526
- return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
8527
- }
8528
- return value;
8529
- }
8530
- function nowIso2(d = /* @__PURE__ */ new Date()) {
8531
- return d.toISOString();
8532
- }
8533
- function normalizeOptionalBool(raw) {
8534
- if (raw === true || raw === false) return raw;
8535
- return null;
8536
- }
8537
- function normalizeAutonomy2(raw) {
8538
- return raw === "default" || raw === "full" ? raw : null;
8539
- }
8540
- function normalizeAttachments(raw) {
8541
- if (!Array.isArray(raw)) return [];
8542
- const out = [];
8543
- for (const item of raw) {
8544
- if (!item || typeof item !== "object") continue;
8545
- const row2 = item;
8546
- if (typeof row2.id !== "string" || !row2.id.trim()) continue;
8547
- if (typeof row2.name !== "string" || !row2.name) continue;
8548
- if (typeof row2.content !== "string") continue;
8549
- if (!ATTACHMENT_KINDS.has(row2.kind)) continue;
8550
- const next = {
8551
- id: row2.id,
8552
- name: row2.name,
8553
- kind: row2.kind,
8554
- content: row2.content
8555
- };
8556
- if (typeof row2.path === "string" && row2.path) next.path = row2.path;
8557
- if (typeof row2.previewDataUrl === "string" && row2.previewDataUrl) {
8558
- next.previewDataUrl = row2.previewDataUrl;
8559
- }
8560
- out.push(next);
8561
- }
8562
- return out;
8563
- }
8564
- function normalizeTask(raw) {
8565
- const when = normalizeWhen(raw.when);
8566
- const agent = raw.agent && isOrchestratorCapableAgent(raw.agent) ? raw.agent : null;
8567
- return {
8568
- id: raw.id,
8569
- name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : defaultScheduleName(raw.prompt),
8570
- prompt: String(raw.prompt ?? ""),
8571
- enabled: Boolean(raw.enabled),
8572
- when,
8573
- threadId: raw.threadId?.trim() || null,
8574
- agent,
8575
- model: raw.model?.trim() || null,
8576
- effort: normalizeThinkingEffort(raw.effort),
8577
- fast: normalizeOptionalBool(raw.fast),
8578
- planMode: normalizeOptionalBool(raw.planMode),
8579
- autonomy: normalizeAutonomy2(raw.autonomy),
8580
- attachments: normalizeAttachments(raw.attachments),
8581
- archiveChat: raw.archiveChat !== false,
8582
- nextRunAt: raw.nextRunAt,
8583
- lastRunAt: raw.lastRunAt ?? null,
8584
- lastThreadId: raw.lastThreadId ?? null,
8585
- lastError: raw.lastError ?? null,
8586
- createdBy: raw.createdBy === "mcp" || raw.createdBy === "cli" || raw.createdBy === "ui" ? raw.createdBy : "ui",
8587
- createdAt: raw.createdAt,
8588
- updatedAt: raw.updatedAt
8589
- };
8590
- }
8591
- function readAll() {
8592
- const path2 = schedulesPath();
8593
- if (!(0, import_node_fs15.existsSync)(path2)) return [];
8594
- try {
8595
- const parsed = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
8596
- if (!Array.isArray(parsed)) return [];
8597
- return parsed.map((row2) => {
8598
- try {
8599
- return normalizeTask(row2);
8600
- } catch {
8601
- return null;
8602
- }
8603
- }).filter((row2) => Boolean(row2?.id && row2.prompt));
8604
- } catch {
8605
- return [];
8606
- }
8607
- }
8608
- function writeAll(rows) {
8609
- writePrivateFile(schedulesPath(), `${JSON.stringify(rows, null, 2)}
8610
- `);
8611
- }
8612
- function buildTask(input, now) {
8613
- const prompt = input.prompt.trim();
8614
- if (!prompt) throw new Error("Schedule prompt is required");
8615
- const when = normalizeWhen(input.when);
8616
- const nextRunAt = computeNextRunAt(when, now).toISOString();
8617
- const ts = nowIso2(now);
8618
- const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
8619
- return {
8620
- id: (0, import_node_crypto8.randomUUID)(),
8621
- name: input.name?.trim() || defaultScheduleName(prompt),
8622
- prompt,
8623
- enabled: input.enabled !== false,
8624
- when,
8625
- threadId: input.threadId?.trim() || null,
8626
- agent,
8627
- model: input.model?.trim() || null,
8628
- effort: input.effort != null ? normalizeThinkingEffort(input.effort) : null,
8629
- fast: normalizeOptionalBool(input.fast),
8630
- planMode: normalizeOptionalBool(input.planMode),
8631
- autonomy: normalizeAutonomy2(input.autonomy),
8632
- attachments: normalizeAttachments(input.attachments),
8633
- archiveChat: input.archiveChat !== false,
8634
- nextRunAt,
8635
- lastRunAt: null,
8636
- lastThreadId: null,
8637
- lastError: null,
8638
- createdBy: input.createdBy,
8639
- createdAt: ts,
8640
- updatedAt: ts
8641
- };
8642
- }
8643
- function listSchedules() {
8644
- return readAll().sort((a, b) => a.nextRunAt.localeCompare(b.nextRunAt));
8645
- }
8646
- function hasEnabledSchedules() {
8647
- return listSchedules().some((row2) => row2.enabled);
8648
- }
8649
- function scheduleNeedsCaffeinate(task, now = /* @__PURE__ */ new Date()) {
8650
- if (!task.enabled) return false;
8651
- const at = new Date(task.nextRunAt);
8652
- if (Number.isNaN(at.getTime())) return false;
8653
- return at.getTime() > now.getTime();
8654
- }
8655
- function ensureCaffeinateForFutureSchedule(task) {
8656
- if (!scheduleNeedsCaffeinate(task)) return;
8657
- if (caffeinateWhileSchedulesEnabled()) return;
8658
- updateAdvancedSettings({ caffeinateWhileSchedules: true });
8659
- }
8660
- function getSchedule(id) {
8661
- const key = id.trim();
8662
- if (!key) return null;
8663
- return readAll().find(
8664
- (row2) => row2.id === key || row2.id.startsWith(key) && key.length >= 8
8665
- ) ?? null;
8666
- }
8667
- function createSchedule(input) {
8668
- const task = buildTask(input, /* @__PURE__ */ new Date());
8669
- const rows = readAll();
8670
- rows.push(task);
8671
- writeAll(rows);
8672
- ensureCaffeinateForFutureSchedule(task);
8673
- return task;
8674
- }
8675
- function updateSchedule(id, patch) {
8676
- const rows = readAll();
8677
- const idx = rows.findIndex((row2) => row2.id === id || id.length >= 8 && row2.id.startsWith(id));
8678
- if (idx < 0) throw new Error(`Automation not found: ${id}`);
8679
- const prev = rows[idx];
8680
- const when = patch.when ? normalizeWhen(patch.when) : prev.when;
8681
- const prompt = patch.prompt !== void 0 ? patch.prompt.trim() : prev.prompt;
8682
- if (!prompt) throw new Error("Schedule prompt is required");
8683
- const whenChanged = patch.when !== void 0;
8684
- const next = {
8685
- ...prev,
8686
- name: patch.name !== void 0 ? patch.name.trim() || defaultScheduleName(prompt) : prev.name,
8687
- prompt,
8688
- enabled: patch.enabled ?? prev.enabled,
8689
- when,
8690
- threadId: patch.threadId !== void 0 ? patch.threadId?.trim() || null : prev.threadId,
8691
- agent: patch.agent !== void 0 ? patch.agent ? coerceOrchestratorAgent(patch.agent) : null : prev.agent,
8692
- model: patch.model !== void 0 ? patch.model?.trim() || null : prev.model,
8693
- effort: patch.effort !== void 0 ? normalizeThinkingEffort(patch.effort) : prev.effort,
8694
- fast: patch.fast !== void 0 ? normalizeOptionalBool(patch.fast) : prev.fast,
8695
- planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
8696
- autonomy: patch.autonomy !== void 0 ? normalizeAutonomy2(patch.autonomy) : prev.autonomy,
8697
- attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
8698
- archiveChat: patch.archiveChat !== void 0 ? patch.archiveChat !== false : prev.archiveChat,
8699
- nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
8700
- lastError: whenChanged ? null : prev.lastError,
8701
- updatedAt: nowIso2()
8702
- };
8703
- rows[idx] = next;
8704
- writeAll(rows);
8705
- ensureCaffeinateForFutureSchedule(next);
8706
- return next;
8707
- }
8708
- function deleteSchedule(id) {
8709
- const rows = readAll();
8710
- const next = rows.filter(
8711
- (row2) => row2.id !== id && !(id.length >= 8 && row2.id.startsWith(id))
8712
- );
8713
- if (next.length === rows.length) throw new Error(`Automation not found: ${id}`);
8714
- writeAll(next);
8715
- }
8716
- function recordScheduleRun(id, result) {
8717
- const rows = readAll();
8718
- const idx = rows.findIndex((row2) => row2.id === id);
8719
- if (idx < 0) throw new Error(`Automation not found: ${id}`);
8720
- const prev = rows[idx];
8721
- const firedAt = result.firedAt ?? /* @__PURE__ */ new Date();
8722
- const next = {
8723
- ...prev,
8724
- enabled: prev.when.kind === "once" ? false : prev.enabled,
8725
- nextRunAt: prev.when.kind === "once" ? prev.nextRunAt : computeNextRunAt(prev.when, firedAt).toISOString(),
8726
- lastRunAt: nowIso2(firedAt),
8727
- lastThreadId: result.lastThreadId !== void 0 ? result.lastThreadId : prev.lastThreadId,
8728
- lastError: result.lastError ?? null,
8729
- updatedAt: nowIso2(firedAt)
8730
- };
8731
- rows[idx] = next;
8732
- writeAll(rows);
8733
- return next;
8734
- }
8735
- var import_node_crypto8, import_node_fs15, import_node_path18, ATTACHMENT_KINDS;
8736
- var init_schedules = __esm({
8737
- "src/store/schedules.ts"() {
8738
- "use strict";
8739
- import_node_crypto8 = require("crypto");
8740
- import_node_fs15 = require("fs");
8741
- import_node_path18 = require("path");
8742
- init_schedule_when();
8743
- init_orchestrator_capable();
8744
- init_thinking_effort();
8745
- init_paths();
8746
- init_private_file();
8747
- init_app_settings();
8748
- init_schedule_when();
8749
- ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
8750
- "transcript",
8751
- "file",
8752
- "issue",
8753
- "workspace",
8754
- "diff-comment",
8755
- "code-ref"
8756
- ]);
8757
- }
8758
- });
8759
-
8760
8770
  // src/store/desktop-host.ts
8761
8771
  function desktopHostPidPath() {
8762
8772
  return (0, import_node_path19.join)(appDataDir(), "desktop-host.pid");
@@ -13152,7 +13162,7 @@ var init_coordinator_prompt = __esm({
13152
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.",
13153
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.",
13154
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.",
13155
- "- 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.",
13156
13166
  "Projects:",
13157
13167
  "- add_project / remove_project \u2014 register or unregister a git repo",
13158
13168
  "Workspaces and chats:",
@@ -19535,13 +19545,16 @@ async function listCodexModels() {
19535
19545
  return cachedCodexModels?.models ?? FALLBACK_CODEX_MODELS;
19536
19546
  }
19537
19547
  }
19538
- function usageFromCodex(usage) {
19548
+ function usageFromCodex(usage, asContext = false) {
19539
19549
  if (!usage) return null;
19540
- return fromInclusiveInputUsage({
19550
+ const mapped = fromInclusiveInputUsage({
19541
19551
  inputTokens: Number(usage.input_tokens ?? 0),
19542
19552
  outputTokens: Number(usage.output_tokens ?? 0),
19543
19553
  cachedInputTokens: Number(usage.cached_input_tokens ?? 0)
19544
19554
  });
19555
+ if (!mapped || !asContext) return mapped;
19556
+ const occ = requestOccupancy(mapped);
19557
+ return occ > 0 ? { ...mapped, lastRequestTokens: occ } : mapped;
19545
19558
  }
19546
19559
  function codexHomeDir() {
19547
19560
  return process.env.CODEX_HOME?.trim() || (0, import_node_path45.join)((0, import_node_os17.homedir)(), ".codex");
@@ -19837,7 +19850,7 @@ var init_codex = __esm({
19837
19850
  }
19838
19851
  if (type === "turn.completed" || type === "turn_completed") {
19839
19852
  const payload = obj;
19840
- const usage = usageFromCodex(payload.last_token_usage ?? payload.usage);
19853
+ const usage = payload.last_token_usage ? usageFromCodex(payload.last_token_usage, true) : usageFromCodex(payload.usage);
19841
19854
  return usage ? { type: "usage", data: usage, scope: "turn" } : null;
19842
19855
  }
19843
19856
  if (typeof obj.content === "string" && obj.content.trim()) {
@@ -20175,7 +20188,7 @@ function cursorSdkMessageToEvents(msg) {
20175
20188
  }
20176
20189
  if (msg.type === "usage") {
20177
20190
  const usage = usageFromCursor(msg.usage);
20178
- if (usage) return [{ type: "usage", data: usage, scope: "request" }];
20191
+ if (usage) return [{ type: "usage", data: usage, scope: "turn" }];
20179
20192
  }
20180
20193
  if (msg.type === "status" && msg.status === "ERROR") {
20181
20194
  const rawMessage = msg.message;
@@ -22812,13 +22825,12 @@ function estimateOccupancyTokens(messages) {
22812
22825
  function threadHasCompactedContext(messages) {
22813
22826
  return messages.some((m) => m.role === "summary");
22814
22827
  }
22815
- function forwardOccupancyTokens(messages, usage, windowTokens = 1e6) {
22828
+ function forwardOccupancyTokens(messages, usage, windowTokens = 1e6, opts) {
22816
22829
  const estimated = estimateOccupancyTokens(messages);
22830
+ if (opts?.trustProviderOccupancy === false) return estimated;
22817
22831
  if (threadHasCompactedContext(messages) && estimated > 0) return estimated;
22818
- if (usage) {
22819
- const last = contextTokens(usage);
22820
- if (last > 0 && last <= windowTokens) return last;
22821
- }
22832
+ const explicit = usage?.lastRequestTokens;
22833
+ if (explicit != null && explicit > 0 && explicit <= windowTokens) return explicit;
22822
22834
  return estimated;
22823
22835
  }
22824
22836
  function forwardContextUsage(usage, messages) {
@@ -23212,11 +23224,15 @@ function applyCompaction(messages, summaryText, thresholds = {}) {
23212
23224
  return [summary, ...recent];
23213
23225
  }
23214
23226
  function lastRequestOccupancy(thread) {
23227
+ if (thread.agent === "cursor") return estimateOccupancyTokens(thread.messages);
23215
23228
  for (let i = thread.messages.length - 1; i >= 0; i--) {
23216
23229
  const usage = thread.messages[i]?.usage;
23217
- if (usage) return contextTokens(usage);
23230
+ if (!usage) continue;
23231
+ const n = usage.lastRequestTokens;
23232
+ if (n != null && n > 0) return n;
23233
+ if (thread.agent === "codex") return estimateOccupancyTokens(thread.messages);
23218
23234
  }
23219
- return 0;
23235
+ return thread.agent === "codex" ? estimateOccupancyTokens(thread.messages) : 0;
23220
23236
  }
23221
23237
  function shouldResetSessionForOccupancy(thread, occupancyTokens = SESSION_RESET_OCCUPANCY_TOKENS) {
23222
23238
  return lastRequestOccupancy(thread) >= occupancyTokens;
@@ -23235,7 +23251,7 @@ async function maybeCompactContext(thread, thresholds = {}, summarize2 = summari
23235
23251
  });
23236
23252
  let messages = applyCompaction(thread.messages, summary, thresholds);
23237
23253
  const compactedMessages = appendCompactedMessages(thread.compactedMessages, older);
23238
- const resetSession = shouldResetSessionForOccupancy({ messages: thread.messages });
23254
+ const resetSession = shouldResetSessionForOccupancy(thread);
23239
23255
  if (resetSession) {
23240
23256
  messages = applyForwardOccupancy(messages);
23241
23257
  }
@@ -23260,7 +23276,6 @@ var init_context_compact = __esm({
23260
23276
  "use strict";
23261
23277
  init_message_parts();
23262
23278
  init_error_detail();
23263
- init_usage();
23264
23279
  init_context_estimate();
23265
23280
  init_summarize();
23266
23281
  init_context_estimate();
@@ -31367,7 +31382,7 @@ function formatWorktreeDirective(thread, opts) {
31367
31382
  lines.push(formatDevAccessHint(opts?.activeRuns));
31368
31383
  lines.push("");
31369
31384
  lines.push(
31370
- "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."
31371
31386
  );
31372
31387
  lines.push(
31373
31388
  "`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
@@ -31387,7 +31402,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
31387
31402
  return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
31388
31403
  }
31389
31404
  function formatWorktreeReminder() {
31390
- 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).";
31391
31406
  }
31392
31407
  function formatClaudeSubagentReminder() {
31393
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.';
@@ -35445,7 +35460,15 @@ async function fireSchedule(id) {
35445
35460
  const deps = fireHooks ?? await defaultDeps();
35446
35461
  const prompt = formatScheduledPrompt(schedule.name, schedule.prompt);
35447
35462
  const target = schedule.threadId ? deps.findThread(schedule.threadId) : null;
35448
- 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) {
35449
35472
  const sent = await deps.send(target.id, prompt, {
35450
35473
  attachments: schedule.attachments,
35451
35474
  options: scheduleSendOptions(schedule)
@@ -35455,8 +35478,7 @@ async function fireSchedule(id) {
35455
35478
  lastError: null
35456
35479
  });
35457
35480
  }
35458
- const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
35459
- const archiveFresh = schedule.archiveChat !== false && !pinReplacement;
35481
+ const archiveFresh = schedule.archiveChat !== false;
35460
35482
  const created = await deps.startOrchestration({
35461
35483
  goal: prompt,
35462
35484
  agent: pickAgent(schedule),
@@ -35469,9 +35491,6 @@ async function fireSchedule(id) {
35469
35491
  ...archiveFresh ? { ephemeralAutomationId: schedule.id } : {},
35470
35492
  ephemeralTitle: schedule.name
35471
35493
  });
35472
- if (pinReplacement) {
35473
- updateSchedule(schedule.id, { threadId: created.id });
35474
- }
35475
35494
  return recordScheduleRun(schedule.id, {
35476
35495
  lastThreadId: created.id,
35477
35496
  lastError: null
@@ -35888,6 +35907,7 @@ __export(index_exports, {
35888
35907
  defaultScheduleName: () => defaultScheduleName,
35889
35908
  deleteBranchOnPurgeEnabled: () => deleteBranchOnPurgeEnabled,
35890
35909
  deleteSchedule: () => deleteSchedule,
35910
+ deleteSchedulesForThread: () => deleteSchedulesForThread,
35891
35911
  deleteThreadRecord: () => deleteThreadRecord,
35892
35912
  deriveTaskState: () => deriveTaskState,
35893
35913
  desktopHostPidPath: () => desktopHostPidPath,
@@ -45355,7 +45375,7 @@ function whenFromUpdate(id, args) {
45355
45375
  }
45356
45376
  function createdHint(schedule) {
45357
45377
  const awake = scheduleNeedsCaffeinate(schedule) ? " Caffeinate while automations are enabled is now on so the Mac can stay awake until this job is due." : "";
45358
- 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.";
45359
45379
  return `Fires while Sideboard.app is running.${fresh}${awake}`;
45360
45380
  }
45361
45381
  function registerScheduleTools(server) {
@@ -45373,7 +45393,7 @@ function registerScheduleTools(server) {
45373
45393
  );
45374
45394
  server.tool(
45375
45395
  "create_schedule",
45376
- "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.",
45377
45397
  {
45378
45398
  prompt: import_zod10.z.string().describe("User message / goal queued when the automation fires"),
45379
45399
  name: import_zod10.z.string().optional(),
@@ -45387,7 +45407,7 @@ function registerScheduleTools(server) {
45387
45407
  "Archive a new Global chat when the run finishes. Default true. Pass false to leave the chat open."
45388
45408
  ),
45389
45409
  chatId: import_zod10.z.string().optional().describe(
45390
- '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.'
45391
45411
  ),
45392
45412
  threadId: import_zod10.z.string().optional().describe("Legacy alias for chatId."),
45393
45413
  agent: import_zod10.z.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
@@ -45439,7 +45459,7 @@ function registerScheduleTools(server) {
45439
45459
  tz: import_zod10.z.string().optional(),
45440
45460
  archiveChat: import_zod10.z.boolean().optional().describe("Archive a new Global chat when the run finishes. False leaves it open."),
45441
45461
  chatId: import_zod10.z.string().optional().describe(
45442
- '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.'
45443
45463
  ),
45444
45464
  threadId: import_zod10.z.string().optional().describe("Legacy alias for chatId."),
45445
45465
  agent: import_zod10.z.enum(["claude", "cursor", "codex", "opencode"]).optional(),
@@ -50033,6 +50053,7 @@ async function pollSlackOutboundWatches(opts) {
50033
50053
  defaultScheduleName,
50034
50054
  deleteBranchOnPurgeEnabled,
50035
50055
  deleteSchedule,
50056
+ deleteSchedulesForThread,
50036
50057
  deleteThreadRecord,
50037
50058
  deriveTaskState,
50038
50059
  desktopHostPidPath,