@sideboard-ai/core 0.1.322 → 0.1.324
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{abletime-CRRLEYTE.js → abletime-5ZY3ZUUO.js} +4 -4
- package/dist/{abletime-ICW6Q2AB.js → abletime-CU2UPMJF.js} +4 -4
- package/dist/{abletime-oauth-P3KPPJE4.js → abletime-oauth-4TTCR3CT.js} +3 -3
- package/dist/{abletime-oauth-Q76XBSPG.js → abletime-oauth-7EOSBEFM.js} +3 -3
- package/dist/{abletime-rest-DQ3PNKXP.js → abletime-rest-3TG4G7LW.js} +3 -3
- package/dist/{abletime-rest-DNFAYYLP.js → abletime-rest-KOKE7V4B.js} +3 -3
- package/dist/{agents-ROFDNE7Z.js → agents-LUSTCMDN.js} +5 -5
- package/dist/{agents-PFVYC36J.js → agents-YK65UDLT.js} +5 -5
- package/dist/{app-settings-KMPVI2B7.js → app-settings-I5HM5RMP.js} +1 -1
- package/dist/{app-settings-P3H4SEIK.js → app-settings-XIF4EC4K.js} +1 -1
- package/dist/{chunk-BPDVHB4U.js → chunk-5GDJSFCT.js} +2 -2
- package/dist/{chunk-RHJKZOPQ.js → chunk-74OPNF7U.js} +2 -2
- package/dist/{chunk-XDQSHSL6.js → chunk-7D5QIU3N.js} +4 -4
- package/dist/{chunk-45UB2J47.js → chunk-7MQSYPAY.js} +160 -46
- package/dist/{chunk-BTIXTH4N.js → chunk-7PVT73H6.js} +3 -3
- package/dist/{chunk-RGUZPMTF.js → chunk-BYUCNIXK.js} +4 -4
- package/dist/{chunk-LD2DXHBP.js → chunk-DF5BM5OY.js} +182 -50
- package/dist/{chunk-FZKAAAEB.js → chunk-EJ66LEKH.js} +1 -0
- package/dist/{chunk-DFUXW2IT.js → chunk-EKU7FQVG.js} +2 -2
- package/dist/{chunk-5OQONGHO.js → chunk-FR3SAYUL.js} +5 -5
- package/dist/{chunk-UWJUARFA.js → chunk-FUFNMUNR.js} +1 -1
- package/dist/{chunk-TEFFR6PB.js → chunk-INBUV7HY.js} +2 -2
- package/dist/{chunk-H6GXYV5Q.js → chunk-JE3JLHMO.js} +6 -7
- package/dist/{chunk-JNEFEA2A.js → chunk-JH5SMVM5.js} +4 -4
- package/dist/{chunk-7JV5QP5K.js → chunk-MZIZTSR2.js} +2 -2
- package/dist/{chunk-HHPTGIKS.js → chunk-NXUN4U5P.js} +2 -2
- package/dist/{chunk-V76MQ7MH.js → chunk-ODH3XHMI.js} +5 -5
- package/dist/{chunk-IFU4W4C4.js → chunk-OIHRC2ZJ.js} +1 -0
- package/dist/{chunk-56M6VEXI.js → chunk-Q3YJZPQI.js} +3 -3
- package/dist/{chunk-UX6EDDGR.js → chunk-QOS4465Y.js} +1 -1
- package/dist/{chunk-YWBLQBSN.js → chunk-ROOZDDFA.js} +1 -1
- package/dist/{chunk-BANN5HUX.js → chunk-RR4WCGNB.js} +4 -4
- package/dist/{chunk-72PUPPV2.js → chunk-Y3WREIPQ.js} +3 -3
- package/dist/{chunk-SPMIZSKH.js → chunk-ZIRQZUZY.js} +6 -7
- package/dist/{conductor-XLXJU24U.js → conductor-JFABMXDA.js} +4 -4
- package/dist/{conductor-CNT3VL3S.js → conductor-SPZOTC7W.js} +4 -4
- package/dist/{coordinator-prompt-D5EE2YTV.js → coordinator-prompt-K66RGNWK.js} +3 -3
- package/dist/{coordinator-prompt-TU24TQ4I.js → coordinator-prompt-Z6UFHFDH.js} +3 -3
- package/dist/{global-workspace-743TPQNF.js → global-workspace-A6UO6PJV.js} +3 -3
- package/dist/{global-workspace-UPMXDQM7.js → global-workspace-RHEO55B4.js} +3 -3
- package/dist/index.cjs +270 -61
- package/dist/index.d.cts +48 -20
- package/dist/index.d.ts +48 -20
- package/dist/index.js +113 -36
- package/dist/mcp/run-stdio.cjs +245 -61
- package/dist/mcp/run-stdio.js +97 -31
- package/dist/{optional-cli-USD6PROE.js → optional-cli-RMMS5L6F.js} +3 -3
- package/dist/{optional-cli-2YDQQCCD.js → optional-cli-Y3Z3GIPZ.js} +4 -4
- package/dist/{orchestrator-VQ4HRYVB.js → orchestrator-2O6OJQLX.js} +8 -8
- package/dist/{orchestrator-B4STIW4B.js → orchestrator-7VTAUY24.js} +8 -8
- package/dist/{workspaces-47WQL5IG.js → workspaces-42E6AGXD.js} +3 -3
- package/dist/{workspaces-75FD4QUR.js → workspaces-NRKEXOYR.js} +3 -3
- package/dist/{worktree-G3E6WP66.js → worktree-4NGXCCUO.js} +2 -2
- package/dist/{worktree-PDNPL5CQ.js → worktree-ZDA2UJ7C.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -10512,7 +10512,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
10512
10512
|
const goal = opts.goal?.trim();
|
|
10513
10513
|
return [
|
|
10514
10514
|
"Sideboard Orchestration (mandatory):",
|
|
10515
|
-
"- You oversee worktree agents from a synthetic empty cwd (not a git repo). Follow AGENTS.md
|
|
10515
|
+
"- You oversee worktree agents from a synthetic empty cwd (not a git repo). Follow AGENTS.md (CLAUDE.md imports it).",
|
|
10516
10516
|
`- YOUR orchestration chat id is ${opts.parentId} \u2014 pass parentChatId="${opts.parentId}" on create_workspace, or omit it.`,
|
|
10517
10517
|
goal ? `- Goal / title: ${goal}` : null,
|
|
10518
10518
|
accountDefaultsPlaybookLine(),
|
|
@@ -10576,10 +10576,9 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
10576
10576
|
"Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_chat`). If the user gave a goal (Greptile 5/5, CI green), pass that goal through so they watch-fix-push until it lands \u2014 do not start that loop on a plain push, and do not tell the human to poll. Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
|
|
10577
10577
|
].join("\n");
|
|
10578
10578
|
try {
|
|
10579
|
-
(0, import_node_fs21.writeFileSync)((0, import_node_path24.join)(dir, "CLAUDE.md"), `${body}
|
|
10580
|
-
`, "utf8");
|
|
10581
10579
|
(0, import_node_fs21.writeFileSync)((0, import_node_path24.join)(dir, "AGENTS.md"), `${body}
|
|
10582
10580
|
`, "utf8");
|
|
10581
|
+
(0, import_node_fs21.writeFileSync)((0, import_node_path24.join)(dir, "CLAUDE.md"), "@AGENTS.md\n", "utf8");
|
|
10583
10582
|
} catch {
|
|
10584
10583
|
}
|
|
10585
10584
|
return dir;
|
|
@@ -10603,7 +10602,7 @@ function coordinatorSystemPrompt(opts) {
|
|
|
10603
10602
|
...intro,
|
|
10604
10603
|
"You operate across ALL registered workspaces below.",
|
|
10605
10604
|
"You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
|
|
10606
|
-
"Follow AGENTS.md
|
|
10605
|
+
"Follow AGENTS.md in this cwd for the fleet playbook. CLAUDE.md is `@AGENTS.md` so Cursor does not load a second copy.",
|
|
10607
10606
|
`YOUR orchestration chat id is ${opts.parentId} \u2014 pass parentChatId="${opts.parentId}" on create_workspace, or omit parentChatId (Sideboard binds it). Never invent a uuid.`,
|
|
10608
10607
|
`Goal: ${opts.goal}`,
|
|
10609
10608
|
"Registered workspaces:",
|
|
@@ -10641,7 +10640,7 @@ var init_coordinator_prompt = __esm({
|
|
|
10641
10640
|
"- get_viewer_context / update_viewer_context \u2014 read or replace Settings \u2192 Agents (account) and Settings \u2192 Projects (per-repo) context. You can update a project: pass scope=project and repoPath from list_projects (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
|
|
10642
10641
|
"- list_env / set_env / delete_env \u2014 Settings \u2192 Environment. scope=project requires repoPath from list_projects and stores the key under that project prefix (the project name, uppercased, plus _). APPLE_ID for sideboard is SIDEBOARD_APPLE_ID. scope=account is shared by every project and is not prefixed. list_env returns names only.",
|
|
10643
10642
|
"- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for phone remote / away-from-keyboard work, overnight schedules, or when the user will be away. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
|
|
10644
|
-
"- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 automations that send a prompt to a chat (chatId or self) or start a new Global chat (omit chatId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without chatId open a new chat each run and archive it when that run finishes (History keeps the
|
|
10643
|
+
"- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 automations that send a prompt to a chat (chatId or self) or start a new Global chat (omit chatId). One-shot `at`, interval `every` (15m/1h/6h/1d), clock time `time` + `days` (07:30 and weekdays, or mon,wed), or 5-field `cron`. Recurring jobs without chatId open a new chat each run and archive it when that run finishes unless archiveChat is false (leave the chat open to keep talking). History keeps the last 5 archived runs for that automation. Worktree agents have the same tools (`chatId=self` continues that coding chat). Jobs fire only while Sideboard.app is running; sleep skips until wake. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled (or call set_caffeinate). The desktop lists them in the sidebar after Board.",
|
|
10645
10644
|
"Projects:",
|
|
10646
10645
|
"- add_project / remove_project \u2014 register or unregister a git repo",
|
|
10647
10646
|
"Workspaces and chats:",
|
|
@@ -20515,12 +20514,12 @@ function ephemeralRunReadyToArchive(thread) {
|
|
|
20515
20514
|
}
|
|
20516
20515
|
function markEphemeralAutomation(threadId, opts) {
|
|
20517
20516
|
const id = opts.ephemeralAutomationId?.trim();
|
|
20517
|
+
const title = opts.ephemeralTitle?.trim();
|
|
20518
20518
|
const current = readThread(threadId);
|
|
20519
20519
|
if (!current) throw new Error(`Thread not found: ${threadId}`);
|
|
20520
|
-
if (!id) return current;
|
|
20521
|
-
const title = opts.ephemeralTitle?.trim();
|
|
20520
|
+
if (!id && !title) return current;
|
|
20522
20521
|
return updateThread(threadId, {
|
|
20523
|
-
ephemeralAutomationId: id,
|
|
20522
|
+
...id ? { ephemeralAutomationId: id } : {},
|
|
20524
20523
|
...title ? { title, userSetTitle: true } : {}
|
|
20525
20524
|
});
|
|
20526
20525
|
}
|
|
@@ -20555,11 +20554,19 @@ function clearEphemeralAutomation(threadId) {
|
|
|
20555
20554
|
function forgetOlderEphemeralRuns(automationId, keepId) {
|
|
20556
20555
|
const id = automationId.trim();
|
|
20557
20556
|
if (!id || !keepId) return [];
|
|
20557
|
+
const archived = listThreads({ includeArchived: true }).filter(
|
|
20558
|
+
(thread) => thread.status === "archived" && thread.ephemeralAutomationId === id
|
|
20559
|
+
);
|
|
20560
|
+
const ranked = [...archived].sort((a, b) => {
|
|
20561
|
+
if (a.id === keepId) return -1;
|
|
20562
|
+
if (b.id === keepId) return 1;
|
|
20563
|
+
return archivedUpdatedMs(b) - archivedUpdatedMs(a);
|
|
20564
|
+
});
|
|
20565
|
+
const keep = new Set(ranked.slice(0, EPHEMERAL_AUTOMATION_KEEP).map((thread) => thread.id));
|
|
20566
|
+
keep.add(keepId);
|
|
20558
20567
|
const removed = [];
|
|
20559
|
-
for (const thread of
|
|
20560
|
-
if (thread.id
|
|
20561
|
-
if (thread.status !== "archived") continue;
|
|
20562
|
-
if (thread.ephemeralAutomationId !== id) continue;
|
|
20568
|
+
for (const thread of archived) {
|
|
20569
|
+
if (keep.has(thread.id)) continue;
|
|
20563
20570
|
try {
|
|
20564
20571
|
deleteThreadRecord(thread.id);
|
|
20565
20572
|
removed.push(thread.id);
|
|
@@ -20568,10 +20575,13 @@ function forgetOlderEphemeralRuns(automationId, keepId) {
|
|
|
20568
20575
|
}
|
|
20569
20576
|
return removed;
|
|
20570
20577
|
}
|
|
20578
|
+
var EPHEMERAL_AUTOMATION_KEEP;
|
|
20571
20579
|
var init_ephemeral_automation = __esm({
|
|
20572
20580
|
"src/orchestrator/ephemeral-automation.ts"() {
|
|
20573
20581
|
"use strict";
|
|
20582
|
+
init_history_retention();
|
|
20574
20583
|
init_thread_store();
|
|
20584
|
+
EPHEMERAL_AUTOMATION_KEEP = 5;
|
|
20575
20585
|
}
|
|
20576
20586
|
});
|
|
20577
20587
|
|
|
@@ -26727,7 +26737,7 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
26727
26737
|
lines.push(formatDevAccessHint(opts?.activeRuns));
|
|
26728
26738
|
lines.push("");
|
|
26729
26739
|
lines.push(
|
|
26730
|
-
"Automations (sidebar, after Board): `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled."
|
|
26740
|
+
"Automations (sidebar, after Board): `create_schedule` / `list_schedules` (`at` / `every` / `cron` / `time`+`days`). `archiveChat=false` keeps the new chat. `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled."
|
|
26731
26741
|
);
|
|
26732
26742
|
lines.push(
|
|
26733
26743
|
"`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
|
|
@@ -26747,7 +26757,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
|
|
|
26747
26757
|
return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
|
|
26748
26758
|
}
|
|
26749
26759
|
function formatWorktreeReminder() {
|
|
26750
|
-
return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Automations: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
|
|
26760
|
+
return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Automations: create_schedule (at / every / cron, or time + days such as weekdays at 07:30; archiveChat=false leaves the new chat open; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
|
|
26751
26761
|
}
|
|
26752
26762
|
function formatClaudeSubagentReminder() {
|
|
26753
26763
|
return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself. That note is another worker in this chat, or a worker Claude still has marked running. Do not mention it to the user.';
|
|
@@ -27858,10 +27868,7 @@ var init_caffeinate_hold = __esm({
|
|
|
27858
27868
|
}
|
|
27859
27869
|
});
|
|
27860
27870
|
|
|
27861
|
-
// src/store/
|
|
27862
|
-
function schedulesPath() {
|
|
27863
|
-
return (0, import_node_path65.join)(appDataDir(), "schedules.json");
|
|
27864
|
-
}
|
|
27871
|
+
// src/store/schedule-when.ts
|
|
27865
27872
|
function parseDurationMs(every) {
|
|
27866
27873
|
const m = every.trim().match(DURATION_RE);
|
|
27867
27874
|
if (!m) return null;
|
|
@@ -27870,18 +27877,65 @@ function parseDurationMs(every) {
|
|
|
27870
27877
|
const unit = m[2].toLowerCase();
|
|
27871
27878
|
return n * UNIT_MS[unit];
|
|
27872
27879
|
}
|
|
27873
|
-
function
|
|
27874
|
-
const
|
|
27875
|
-
if (
|
|
27876
|
-
return
|
|
27877
|
-
}
|
|
27878
|
-
function
|
|
27879
|
-
const
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27880
|
+
function dayIndex(token) {
|
|
27881
|
+
const text7 = token.trim().toLowerCase();
|
|
27882
|
+
if (/^[0-6]$/.test(text7)) return Number(text7);
|
|
27883
|
+
return DAY_ALIASES[text7];
|
|
27884
|
+
}
|
|
27885
|
+
function normalizeWeekdays(days) {
|
|
27886
|
+
const set = /* @__PURE__ */ new Set();
|
|
27887
|
+
for (const day of days) {
|
|
27888
|
+
if (!Number.isInteger(day) || day < 0 || day > 6) {
|
|
27889
|
+
throw new Error("Days must be 0 (Sunday) through 6 (Saturday)");
|
|
27890
|
+
}
|
|
27891
|
+
set.add(day);
|
|
27892
|
+
}
|
|
27893
|
+
if (set.size === 0) throw new Error("Pick at least one day");
|
|
27894
|
+
return [...set].sort((a, b) => a - b);
|
|
27895
|
+
}
|
|
27896
|
+
function parseWeeklyDays(raw) {
|
|
27897
|
+
const text7 = raw.trim().toLowerCase();
|
|
27898
|
+
if (!text7) throw new Error("Pick at least one day");
|
|
27899
|
+
if (text7 === "daily" || text7 === "all" || text7 === "every day" || text7 === "everyday") {
|
|
27900
|
+
return [0, 1, 2, 3, 4, 5, 6];
|
|
27901
|
+
}
|
|
27902
|
+
if (text7 === "weekdays" || text7 === "weekday") return [1, 2, 3, 4, 5];
|
|
27903
|
+
if (text7 === "weekends" || text7 === "weekend") return [0, 6];
|
|
27904
|
+
const days = [];
|
|
27905
|
+
for (const part of text7.split(/[\s,]+/).filter(Boolean)) {
|
|
27906
|
+
const range = part.split("-");
|
|
27907
|
+
if (range.length === 2 && range[0] && range[1]) {
|
|
27908
|
+
const start = dayIndex(range[0]);
|
|
27909
|
+
const end = dayIndex(range[1]);
|
|
27910
|
+
if (start === void 0 || end === void 0) {
|
|
27911
|
+
throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
|
|
27912
|
+
}
|
|
27913
|
+
if (start <= end) {
|
|
27914
|
+
for (let d = start; d <= end; d++) days.push(d);
|
|
27915
|
+
} else {
|
|
27916
|
+
for (let d = start; d <= 6; d++) days.push(d);
|
|
27917
|
+
for (let d = 0; d <= end; d++) days.push(d);
|
|
27918
|
+
}
|
|
27919
|
+
continue;
|
|
27920
|
+
}
|
|
27921
|
+
const n = dayIndex(part);
|
|
27922
|
+
if (n === void 0) {
|
|
27923
|
+
throw new Error(`Unknown day: ${part} (use mon,tue, weekdays, weekends, or daily)`);
|
|
27924
|
+
}
|
|
27925
|
+
days.push(n);
|
|
27883
27926
|
}
|
|
27884
|
-
return
|
|
27927
|
+
return normalizeWeekdays(days);
|
|
27928
|
+
}
|
|
27929
|
+
function normalizeWeeklyTime(raw) {
|
|
27930
|
+
const m = raw.trim().match(TIME_RE);
|
|
27931
|
+
if (!m) throw new Error(`Invalid time: ${raw} (use HH:mm, 24-hour)`);
|
|
27932
|
+
return `${m[1]}:${m[2]}`;
|
|
27933
|
+
}
|
|
27934
|
+
function weeklyCronExpr(when) {
|
|
27935
|
+
const time = normalizeWeeklyTime(when.time);
|
|
27936
|
+
const [hh, mm] = time.split(":");
|
|
27937
|
+
const days = normalizeWeekdays(when.days);
|
|
27938
|
+
return `${Number(mm)} ${Number(hh)} * * ${days.join(",")}`;
|
|
27885
27939
|
}
|
|
27886
27940
|
function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
|
|
27887
27941
|
if (when.kind === "once") {
|
|
@@ -27898,6 +27952,12 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
|
|
|
27898
27952
|
}
|
|
27899
27953
|
return new Date(from.getTime() + ms);
|
|
27900
27954
|
}
|
|
27955
|
+
if (when.kind === "weekly") {
|
|
27956
|
+
return computeNextRunAt(
|
|
27957
|
+
{ kind: "cron", expr: weeklyCronExpr(when), tz: when.tz },
|
|
27958
|
+
from
|
|
27959
|
+
);
|
|
27960
|
+
}
|
|
27901
27961
|
try {
|
|
27902
27962
|
const job = new import_croner.Cron(when.expr, {
|
|
27903
27963
|
timezone: when.tz?.trim() || void 0,
|
|
@@ -27913,9 +27973,6 @@ function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
|
|
|
27913
27973
|
throw new Error(`Invalid cron: ${message}`);
|
|
27914
27974
|
}
|
|
27915
27975
|
}
|
|
27916
|
-
function nowIso2(d = /* @__PURE__ */ new Date()) {
|
|
27917
|
-
return d.toISOString();
|
|
27918
|
-
}
|
|
27919
27976
|
function normalizeWhen(raw) {
|
|
27920
27977
|
if (!raw || typeof raw !== "object") {
|
|
27921
27978
|
throw new Error("Schedule is missing `when`");
|
|
@@ -27935,7 +27992,70 @@ function normalizeWhen(raw) {
|
|
|
27935
27992
|
tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
|
|
27936
27993
|
};
|
|
27937
27994
|
}
|
|
27938
|
-
|
|
27995
|
+
if (kind === "weekly" && Array.isArray(rec.days) && typeof rec.time === "string") {
|
|
27996
|
+
return {
|
|
27997
|
+
kind: "weekly",
|
|
27998
|
+
days: normalizeWeekdays(rec.days.map((day) => Number(day))),
|
|
27999
|
+
time: normalizeWeeklyTime(rec.time),
|
|
28000
|
+
tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
|
|
28001
|
+
};
|
|
28002
|
+
}
|
|
28003
|
+
throw new Error("Schedule `when` must be once, every, weekly, or cron");
|
|
28004
|
+
}
|
|
28005
|
+
var import_croner, DURATION_RE, UNIT_MS, DAY_ALIASES, TIME_RE;
|
|
28006
|
+
var init_schedule_when = __esm({
|
|
28007
|
+
"src/store/schedule-when.ts"() {
|
|
28008
|
+
"use strict";
|
|
28009
|
+
import_croner = require("croner");
|
|
28010
|
+
DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
28011
|
+
UNIT_MS = {
|
|
28012
|
+
s: 1e3,
|
|
28013
|
+
m: 6e4,
|
|
28014
|
+
h: 36e5,
|
|
28015
|
+
d: 864e5
|
|
28016
|
+
};
|
|
28017
|
+
DAY_ALIASES = {
|
|
28018
|
+
sun: 0,
|
|
28019
|
+
sunday: 0,
|
|
28020
|
+
mon: 1,
|
|
28021
|
+
monday: 1,
|
|
28022
|
+
tue: 2,
|
|
28023
|
+
tues: 2,
|
|
28024
|
+
tuesday: 2,
|
|
28025
|
+
wed: 3,
|
|
28026
|
+
wednesday: 3,
|
|
28027
|
+
thu: 4,
|
|
28028
|
+
thur: 4,
|
|
28029
|
+
thurs: 4,
|
|
28030
|
+
thursday: 4,
|
|
28031
|
+
fri: 5,
|
|
28032
|
+
friday: 5,
|
|
28033
|
+
sat: 6,
|
|
28034
|
+
saturday: 6
|
|
28035
|
+
};
|
|
28036
|
+
TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/;
|
|
28037
|
+
}
|
|
28038
|
+
});
|
|
28039
|
+
|
|
28040
|
+
// src/store/schedules.ts
|
|
28041
|
+
function schedulesPath() {
|
|
28042
|
+
return (0, import_node_path65.join)(appDataDir(), "schedules.json");
|
|
28043
|
+
}
|
|
28044
|
+
function defaultScheduleName(prompt) {
|
|
28045
|
+
const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
28046
|
+
if (!line) return "Untitled automation";
|
|
28047
|
+
return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
|
|
28048
|
+
}
|
|
28049
|
+
function resolveScheduleThreadId(raw, env = process.env) {
|
|
28050
|
+
const value = raw?.trim() || "";
|
|
28051
|
+
if (!value) return null;
|
|
28052
|
+
if (value.toLowerCase() === "self") {
|
|
28053
|
+
return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
|
|
28054
|
+
}
|
|
28055
|
+
return value;
|
|
28056
|
+
}
|
|
28057
|
+
function nowIso2(d = /* @__PURE__ */ new Date()) {
|
|
28058
|
+
return d.toISOString();
|
|
27939
28059
|
}
|
|
27940
28060
|
function normalizeOptionalBool(raw) {
|
|
27941
28061
|
if (raw === true || raw === false) return raw;
|
|
@@ -27985,6 +28105,7 @@ function normalizeTask(raw) {
|
|
|
27985
28105
|
planMode: normalizeOptionalBool(raw.planMode),
|
|
27986
28106
|
autonomy: normalizeAutonomy2(raw.autonomy),
|
|
27987
28107
|
attachments: normalizeAttachments(raw.attachments),
|
|
28108
|
+
archiveChat: raw.archiveChat !== false,
|
|
27988
28109
|
nextRunAt: raw.nextRunAt,
|
|
27989
28110
|
lastRunAt: raw.lastRunAt ?? null,
|
|
27990
28111
|
lastThreadId: raw.lastThreadId ?? null,
|
|
@@ -28036,6 +28157,7 @@ function buildTask(input, now) {
|
|
|
28036
28157
|
planMode: normalizeOptionalBool(input.planMode),
|
|
28037
28158
|
autonomy: normalizeAutonomy2(input.autonomy),
|
|
28038
28159
|
attachments: normalizeAttachments(input.attachments),
|
|
28160
|
+
archiveChat: input.archiveChat !== false,
|
|
28039
28161
|
nextRunAt,
|
|
28040
28162
|
lastRunAt: null,
|
|
28041
28163
|
lastThreadId: null,
|
|
@@ -28097,6 +28219,7 @@ function updateSchedule(id, patch) {
|
|
|
28097
28219
|
planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
|
|
28098
28220
|
autonomy: patch.autonomy !== void 0 ? normalizeAutonomy2(patch.autonomy) : prev.autonomy,
|
|
28099
28221
|
attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
|
|
28222
|
+
archiveChat: patch.archiveChat !== void 0 ? patch.archiveChat !== false : prev.archiveChat,
|
|
28100
28223
|
nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
|
|
28101
28224
|
lastError: whenChanged ? null : prev.lastError,
|
|
28102
28225
|
updatedAt: nowIso2()
|
|
@@ -28133,26 +28256,20 @@ function recordScheduleRun(id, result) {
|
|
|
28133
28256
|
writeAll2(rows);
|
|
28134
28257
|
return next;
|
|
28135
28258
|
}
|
|
28136
|
-
var import_node_crypto17, import_node_fs66, import_node_path65,
|
|
28259
|
+
var import_node_crypto17, import_node_fs66, import_node_path65, ATTACHMENT_KINDS;
|
|
28137
28260
|
var init_schedules = __esm({
|
|
28138
28261
|
"src/store/schedules.ts"() {
|
|
28139
28262
|
"use strict";
|
|
28140
28263
|
import_node_crypto17 = require("crypto");
|
|
28141
28264
|
import_node_fs66 = require("fs");
|
|
28142
28265
|
import_node_path65 = require("path");
|
|
28143
|
-
|
|
28266
|
+
init_schedule_when();
|
|
28144
28267
|
init_orchestrator_capable();
|
|
28145
28268
|
init_thinking_effort();
|
|
28146
28269
|
init_paths();
|
|
28147
28270
|
init_private_file();
|
|
28148
28271
|
init_app_settings();
|
|
28149
|
-
|
|
28150
|
-
UNIT_MS = {
|
|
28151
|
-
s: 1e3,
|
|
28152
|
-
m: 6e4,
|
|
28153
|
-
h: 36e5,
|
|
28154
|
-
d: 864e5
|
|
28155
|
-
};
|
|
28272
|
+
init_schedule_when();
|
|
28156
28273
|
ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
|
|
28157
28274
|
"transcript",
|
|
28158
28275
|
"file",
|
|
@@ -28237,6 +28354,7 @@ async function fireSchedule(id) {
|
|
|
28237
28354
|
});
|
|
28238
28355
|
}
|
|
28239
28356
|
const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
|
|
28357
|
+
const archiveFresh = schedule.archiveChat !== false && !pinReplacement;
|
|
28240
28358
|
const created = await deps.startOrchestration({
|
|
28241
28359
|
goal: prompt,
|
|
28242
28360
|
agent: pickAgent(schedule),
|
|
@@ -28246,7 +28364,8 @@ async function fireSchedule(id) {
|
|
|
28246
28364
|
...schedule.planMode != null ? { planMode: schedule.planMode } : {},
|
|
28247
28365
|
...schedule.autonomy ? { autonomy: schedule.autonomy } : {},
|
|
28248
28366
|
attachments: schedule.attachments,
|
|
28249
|
-
...
|
|
28367
|
+
...archiveFresh ? { ephemeralAutomationId: schedule.id } : {},
|
|
28368
|
+
ephemeralTitle: schedule.name
|
|
28250
28369
|
});
|
|
28251
28370
|
if (pinReplacement) {
|
|
28252
28371
|
updateSchedule(schedule.id, { threadId: created.id });
|
|
@@ -34791,18 +34910,79 @@ function fail4(err) {
|
|
|
34791
34910
|
);
|
|
34792
34911
|
}
|
|
34793
34912
|
function parseWhen(args) {
|
|
34794
|
-
const
|
|
34795
|
-
|
|
34796
|
-
|
|
34913
|
+
const at = args.at?.trim();
|
|
34914
|
+
const every = args.every?.trim();
|
|
34915
|
+
const cron = args.cron?.trim();
|
|
34916
|
+
const time = args.time?.trim();
|
|
34917
|
+
const days = args.days?.trim();
|
|
34918
|
+
if (days && !time && !at && !every && !cron) {
|
|
34919
|
+
throw new Error("days needs time (HH:mm), for example time=07:30 days=weekdays");
|
|
34920
|
+
}
|
|
34921
|
+
const picked = [at, every, cron, time].filter(Boolean);
|
|
34922
|
+
if (picked.length !== 1) {
|
|
34923
|
+
throw new Error(
|
|
34924
|
+
"Pass exactly one of at (ISO datetime), every (15m/1h/6h/1d), time (HH:mm), or cron"
|
|
34925
|
+
);
|
|
34926
|
+
}
|
|
34927
|
+
if (at) return { kind: "once", at };
|
|
34928
|
+
if (every) return { kind: "every", every };
|
|
34929
|
+
if (time) {
|
|
34930
|
+
return {
|
|
34931
|
+
kind: "weekly",
|
|
34932
|
+
time,
|
|
34933
|
+
days: parseWeeklyDays(days || "daily"),
|
|
34934
|
+
tz: args.tz?.trim() || void 0
|
|
34935
|
+
};
|
|
34797
34936
|
}
|
|
34798
|
-
if (args.at?.trim()) return { kind: "once", at: args.at.trim() };
|
|
34799
|
-
if (args.every?.trim()) return { kind: "every", every: args.every.trim() };
|
|
34800
34937
|
return {
|
|
34801
34938
|
kind: "cron",
|
|
34802
|
-
expr:
|
|
34939
|
+
expr: cron,
|
|
34803
34940
|
tz: args.tz?.trim() || void 0
|
|
34804
34941
|
};
|
|
34805
34942
|
}
|
|
34943
|
+
function whenFromUpdate(id, args) {
|
|
34944
|
+
const at = args.at?.trim();
|
|
34945
|
+
const every = args.every?.trim();
|
|
34946
|
+
const cron = args.cron?.trim();
|
|
34947
|
+
const time = args.time?.trim();
|
|
34948
|
+
const days = args.days?.trim();
|
|
34949
|
+
const tzSet = args.tz !== void 0;
|
|
34950
|
+
if (!at && !every && !cron && !time && !days && !tzSet) return void 0;
|
|
34951
|
+
if ((time || days) && !at && !every && !cron) {
|
|
34952
|
+
const current = getSchedule(id);
|
|
34953
|
+
const weekly = current?.when.kind === "weekly" ? current.when : null;
|
|
34954
|
+
if (!weekly && !(time && days)) {
|
|
34955
|
+
const kind = current?.when.kind ?? "missing";
|
|
34956
|
+
throw new Error(
|
|
34957
|
+
`time and days change a days-at-a-time automation. This job is ${kind}. Pass time and days together to switch it, or at / every / cron to replace the cadence.`
|
|
34958
|
+
);
|
|
34959
|
+
}
|
|
34960
|
+
const clock = time || weekly?.time;
|
|
34961
|
+
if (!clock) {
|
|
34962
|
+
throw new Error("time (HH:mm) is required, for example time=07:30 days=weekdays");
|
|
34963
|
+
}
|
|
34964
|
+
const prevTz = current?.when.kind === "weekly" || current?.when.kind === "cron" ? current.when.tz : void 0;
|
|
34965
|
+
return {
|
|
34966
|
+
kind: "weekly",
|
|
34967
|
+
time: clock,
|
|
34968
|
+
days: days ? parseWeeklyDays(days) : weekly?.days ?? parseWeeklyDays("daily"),
|
|
34969
|
+
tz: tzSet ? args.tz?.trim() || void 0 : prevTz
|
|
34970
|
+
};
|
|
34971
|
+
}
|
|
34972
|
+
if (!at && !every && !cron && !time && !days && tzSet) {
|
|
34973
|
+
const current = getSchedule(id);
|
|
34974
|
+
if (current?.when.kind === "weekly" || current?.when.kind === "cron") {
|
|
34975
|
+
return { ...current.when, tz: args.tz?.trim() || void 0 };
|
|
34976
|
+
}
|
|
34977
|
+
return void 0;
|
|
34978
|
+
}
|
|
34979
|
+
return parseWhen(args);
|
|
34980
|
+
}
|
|
34981
|
+
function createdHint(schedule) {
|
|
34982
|
+
const awake = scheduleNeedsCaffeinate(schedule) ? " Caffeinate while automations are enabled is now on so the Mac can stay awake until this job is due." : "";
|
|
34983
|
+
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.";
|
|
34984
|
+
return `Fires while Sideboard.app is running.${fresh}${awake}`;
|
|
34985
|
+
}
|
|
34806
34986
|
function registerScheduleTools(server) {
|
|
34807
34987
|
server.tool(
|
|
34808
34988
|
"list_schedules",
|
|
@@ -34818,14 +34998,19 @@ function registerScheduleTools(server) {
|
|
|
34818
34998
|
);
|
|
34819
34999
|
server.tool(
|
|
34820
35000
|
"create_schedule",
|
|
34821
|
-
"Create a local automation that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without chatId open a new Global chat each run and archive it when that run finishes. History keeps the
|
|
35001
|
+
"Create a local automation that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), time (HH:mm, with days such as weekdays or mon,wed), or cron (5-field). Recurring jobs without chatId open a new Global chat each run and archive it when that run finishes unless archiveChat is false (leave the chat open to keep talking, for example a morning start-my-day run). History keeps the last 5 archived runs for that automation. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
|
|
34822
35002
|
{
|
|
34823
35003
|
prompt: import_zod10.z.string().describe("User message / goal queued when the automation fires"),
|
|
34824
35004
|
name: import_zod10.z.string().optional(),
|
|
34825
35005
|
at: import_zod10.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
34826
35006
|
every: import_zod10.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
35007
|
+
time: import_zod10.z.string().optional().describe("HH:mm (24-hour) clock time. Omit days for every day."),
|
|
35008
|
+
days: import_zod10.z.string().optional().describe("daily, weekdays, weekends, or days such as mon,wed,fri or mon-fri. Use with time."),
|
|
34827
35009
|
cron: import_zod10.z.string().optional().describe("5-field cron expression"),
|
|
34828
|
-
tz: import_zod10.z.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
35010
|
+
tz: import_zod10.z.string().optional().describe("IANA timezone for cron or time (default: system)"),
|
|
35011
|
+
archiveChat: import_zod10.z.boolean().optional().describe(
|
|
35012
|
+
"Archive a new Global chat when the run finishes. Default true. Pass false to leave the chat open."
|
|
35013
|
+
),
|
|
34829
35014
|
chatId: import_zod10.z.string().optional().describe(
|
|
34830
35015
|
'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
|
|
34831
35016
|
),
|
|
@@ -34852,11 +35037,12 @@ function registerScheduleTools(server) {
|
|
|
34852
35037
|
threadId,
|
|
34853
35038
|
agent: args.agent,
|
|
34854
35039
|
model: args.model,
|
|
35040
|
+
archiveChat: args.archiveChat,
|
|
34855
35041
|
createdBy: "mcp"
|
|
34856
35042
|
});
|
|
34857
35043
|
return text6({
|
|
34858
35044
|
schedule,
|
|
34859
|
-
hint:
|
|
35045
|
+
hint: createdHint(schedule)
|
|
34860
35046
|
});
|
|
34861
35047
|
} catch (err) {
|
|
34862
35048
|
return fail4(err);
|
|
@@ -34865,15 +35051,18 @@ function registerScheduleTools(server) {
|
|
|
34865
35051
|
);
|
|
34866
35052
|
server.tool(
|
|
34867
35053
|
"update_schedule",
|
|
34868
|
-
"Update a local automation (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
|
|
35054
|
+
"Update a local automation (prompt, cadence, target chat, archiveChat, enabled). Pass id from list_schedules.",
|
|
34869
35055
|
{
|
|
34870
35056
|
id: import_zod10.z.string(),
|
|
34871
35057
|
prompt: import_zod10.z.string().optional(),
|
|
34872
35058
|
name: import_zod10.z.string().optional(),
|
|
34873
35059
|
at: import_zod10.z.string().optional(),
|
|
34874
35060
|
every: import_zod10.z.string().optional(),
|
|
35061
|
+
time: import_zod10.z.string().optional().describe("HH:mm clock time. Use with days, or alone to change the time of a weekly job."),
|
|
35062
|
+
days: import_zod10.z.string().optional().describe("daily, weekdays, weekends, or mon,wed,fri. Use with time, or alone on an existing weekly job."),
|
|
34875
35063
|
cron: import_zod10.z.string().optional(),
|
|
34876
35064
|
tz: import_zod10.z.string().optional(),
|
|
35065
|
+
archiveChat: import_zod10.z.boolean().optional().describe("Archive a new Global chat when the run finishes. False leaves it open."),
|
|
34877
35066
|
chatId: import_zod10.z.string().optional().describe(
|
|
34878
35067
|
'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
|
|
34879
35068
|
),
|
|
@@ -34884,13 +35073,7 @@ function registerScheduleTools(server) {
|
|
|
34884
35073
|
},
|
|
34885
35074
|
async (args) => {
|
|
34886
35075
|
try {
|
|
34887
|
-
const
|
|
34888
|
-
const when = cadenceSet ? parseWhen({
|
|
34889
|
-
at: args.at,
|
|
34890
|
-
every: args.every,
|
|
34891
|
-
cron: args.cron,
|
|
34892
|
-
tz: args.tz
|
|
34893
|
-
}) : void 0;
|
|
35076
|
+
const when = whenFromUpdate(args.id, args);
|
|
34894
35077
|
let threadId;
|
|
34895
35078
|
const rawChatId = args.chatId ?? args.threadId;
|
|
34896
35079
|
if (rawChatId !== void 0) {
|
|
@@ -34910,7 +35093,8 @@ function registerScheduleTools(server) {
|
|
|
34910
35093
|
threadId,
|
|
34911
35094
|
agent: args.agent,
|
|
34912
35095
|
enabled: args.enabled,
|
|
34913
|
-
model: args.model
|
|
35096
|
+
model: args.model,
|
|
35097
|
+
archiveChat: args.archiveChat
|
|
34914
35098
|
});
|
|
34915
35099
|
return text6({ schedule });
|
|
34916
35100
|
} catch (err) {
|