@michael-joseph-miller/ant-bot 0.1.0
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/CHANGELOG.md +40 -0
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/browser-OHRD7YI3.js +524 -0
- package/dist/browser-OHRD7YI3.js.map +7 -0
- package/dist/bundled-46DUK5PG.js +133 -0
- package/dist/bundled-46DUK5PG.js.map +7 -0
- package/dist/chunk-7BOHBPB2.js +287 -0
- package/dist/chunk-7BOHBPB2.js.map +7 -0
- package/dist/chunk-AHAON6J7.js +24 -0
- package/dist/chunk-AHAON6J7.js.map +7 -0
- package/dist/chunk-DYIJTUMY.js +321 -0
- package/dist/chunk-DYIJTUMY.js.map +7 -0
- package/dist/chunk-KSQOVWP5.js +310 -0
- package/dist/chunk-KSQOVWP5.js.map +7 -0
- package/dist/chunk-Z2YT2PZN.js +64 -0
- package/dist/chunk-Z2YT2PZN.js.map +7 -0
- package/dist/index.js +1562 -0
- package/dist/index.js.map +7 -0
- package/dist/install-IY2M3OUQ.js +36 -0
- package/dist/install-IY2M3OUQ.js.map +7 -0
- package/dist/plugin-POMHVGD4.js +53 -0
- package/dist/plugin-POMHVGD4.js.map +7 -0
- package/dist/scheduler-Q7OHNGP6.js +274 -0
- package/dist/scheduler-Q7OHNGP6.js.map +7 -0
- package/dist/server.js +3016 -0
- package/dist/server.js.map +7 -0
- package/dist/skills-66WRX64H.js +17 -0
- package/dist/skills-66WRX64H.js.map +7 -0
- package/dist/skills-spec.js +172 -0
- package/dist/skills-spec.js.map +7 -0
- package/dist/tools-P5537ASX.js +148 -0
- package/dist/tools-P5537ASX.js.map +7 -0
- package/package.json +51 -0
- package/skills/README.md +69 -0
- package/skills/SPEC.md +274 -0
- package/skills/bug-repro/SKILL.md +53 -0
- package/skills/deep-research/SKILL.md +451 -0
- package/skills/deep-research/references/V6_1_improvements.md +112 -0
- package/skills/deep-research/references/completeness_review_checklist.md +25 -0
- package/skills/deep-research/references/counter_review_team_guide.md +181 -0
- package/skills/deep-research/references/enterprise_analysis_frameworks.md +135 -0
- package/skills/deep-research/references/enterprise_mode.md +99 -0
- package/skills/deep-research/references/enterprise_quality_checklist.md +160 -0
- package/skills/deep-research/references/enterprise_research_methodology.md +164 -0
- package/skills/deep-research/references/formatting_rules.md +31 -0
- package/skills/deep-research/references/quality_gates.md +77 -0
- package/skills/deep-research/references/report_template_v6.md +82 -0
- package/skills/deep-research/references/research_notes_format.md +147 -0
- package/skills/deep-research/references/research_plan_checklist.md +26 -0
- package/skills/deep-research/references/research_report_template.md +49 -0
- package/skills/deep-research/references/source_accessibility_policy.md +179 -0
- package/skills/deep-research/references/source_quality_rubric.md +28 -0
- package/skills/deep-research/references/subagent_prompt.md +116 -0
- package/skills/inbox-digest/SKILL.md +51 -0
- package/skills/skill-author/SKILL.md +92 -0
- package/skills/weekly-report/SKILL.md +49 -0
- package/web/dist/assets/index-BLQ8rPiN.js +130 -0
- package/web/dist/assets/index-IEIkG_jd.css +2 -0
- package/web/dist/index.html +13 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { createRequire as __antbotCreateRequire } from 'node:module';
|
|
2
|
+
const require = __antbotCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
logger
|
|
5
|
+
} from "./chunk-AHAON6J7.js";
|
|
6
|
+
|
|
7
|
+
// packages/server/src/scheduler/scheduler.ts
|
|
8
|
+
import cron from "node-cron";
|
|
9
|
+
var log = logger("scheduler");
|
|
10
|
+
function parseCronFieldPart(field, min, max) {
|
|
11
|
+
const trimmed = field.trim();
|
|
12
|
+
if (trimmed === "*") {
|
|
13
|
+
const values2 = /* @__PURE__ */ new Set();
|
|
14
|
+
for (let v = min; v <= max; v++) values2.add(v);
|
|
15
|
+
return { values: values2, wildcard: true };
|
|
16
|
+
}
|
|
17
|
+
const values = /* @__PURE__ */ new Set();
|
|
18
|
+
for (const part of trimmed.split(",")) {
|
|
19
|
+
const m = part.match(/^(\*|\d+)(?:-(\d+))?(?:\/(\d+))?$/);
|
|
20
|
+
if (!m) throw new Error(`Invalid cron field: "${part}"`);
|
|
21
|
+
const [, startStr, endStr, stepStr] = m;
|
|
22
|
+
const step = stepStr ? parseInt(stepStr, 10) : 1;
|
|
23
|
+
let start;
|
|
24
|
+
let end;
|
|
25
|
+
if (startStr === "*") {
|
|
26
|
+
start = min;
|
|
27
|
+
end = max;
|
|
28
|
+
} else {
|
|
29
|
+
start = parseInt(startStr, 10);
|
|
30
|
+
end = endStr !== void 0 ? parseInt(endStr, 10) : stepStr ? max : start;
|
|
31
|
+
}
|
|
32
|
+
for (let v = start; v <= end; v += step) values.add(v);
|
|
33
|
+
}
|
|
34
|
+
return { values, wildcard: false };
|
|
35
|
+
}
|
|
36
|
+
function parseCronExpr(expr) {
|
|
37
|
+
const parts = expr.trim().split(/\s+/);
|
|
38
|
+
if (parts.length !== 5) throw new Error(`Cron expression must have 5 fields: "${expr}"`);
|
|
39
|
+
const [minute, hour, dayOfMonth, month, dayOfWeekRaw] = parts;
|
|
40
|
+
const dayOfWeek = parseCronFieldPart(dayOfWeekRaw, 0, 7);
|
|
41
|
+
if (dayOfWeek.values.has(7)) {
|
|
42
|
+
dayOfWeek.values.delete(7);
|
|
43
|
+
dayOfWeek.values.add(0);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
minute: parseCronFieldPart(minute, 0, 59),
|
|
47
|
+
hour: parseCronFieldPart(hour, 0, 23),
|
|
48
|
+
dayOfMonth: parseCronFieldPart(dayOfMonth, 1, 31),
|
|
49
|
+
month: parseCronFieldPart(month, 1, 12),
|
|
50
|
+
dayOfWeek
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
var WEEKDAY_INDEX = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
|
|
54
|
+
function makeTzFormatter(timeZone) {
|
|
55
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
56
|
+
timeZone,
|
|
57
|
+
hour12: false,
|
|
58
|
+
month: "2-digit",
|
|
59
|
+
day: "2-digit",
|
|
60
|
+
hour: "2-digit",
|
|
61
|
+
minute: "2-digit",
|
|
62
|
+
weekday: "short"
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function tzParts(dtf, ms) {
|
|
66
|
+
const parts = dtf.formatToParts(new Date(ms));
|
|
67
|
+
const map = {};
|
|
68
|
+
for (const p of parts) map[p.type] = p.value;
|
|
69
|
+
let hour = parseInt(map.hour ?? "0", 10);
|
|
70
|
+
if (hour === 24) hour = 0;
|
|
71
|
+
return {
|
|
72
|
+
month: parseInt(map.month ?? "1", 10),
|
|
73
|
+
day: parseInt(map.day ?? "1", 10),
|
|
74
|
+
hour,
|
|
75
|
+
minute: parseInt(map.minute ?? "0", 10),
|
|
76
|
+
weekday: WEEKDAY_INDEX[map.weekday ?? "Sun"] ?? 0
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
var MAX_LOOKAHEAD_MINUTES = 366 * 24 * 60;
|
|
80
|
+
function nextRunAt(cronExpr, timezone, fromMs) {
|
|
81
|
+
const fields = parseCronExpr(cronExpr);
|
|
82
|
+
const dtf = makeTzFormatter(timezone);
|
|
83
|
+
const bothWildcard = fields.dayOfMonth.wildcard && fields.dayOfWeek.wildcard;
|
|
84
|
+
let t = Math.floor(fromMs / 6e4) * 6e4 + 6e4;
|
|
85
|
+
for (let i = 0; i < MAX_LOOKAHEAD_MINUTES; i++, t += 6e4) {
|
|
86
|
+
const p = tzParts(dtf, t);
|
|
87
|
+
if (!fields.minute.values.has(p.minute)) continue;
|
|
88
|
+
if (!fields.hour.values.has(p.hour)) continue;
|
|
89
|
+
if (!fields.month.values.has(p.month)) continue;
|
|
90
|
+
const domMatch = fields.dayOfMonth.values.has(p.day);
|
|
91
|
+
const dowMatch = fields.dayOfWeek.values.has(p.weekday);
|
|
92
|
+
const dayOk = bothWildcard ? true : fields.dayOfMonth.wildcard ? dowMatch : fields.dayOfWeek.wildcard ? domMatch : domMatch || dowMatch;
|
|
93
|
+
if (!dayOk) continue;
|
|
94
|
+
return t;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
var DOW_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
99
|
+
function cronToHuman(expr) {
|
|
100
|
+
const parts = expr.trim().split(/\s+/);
|
|
101
|
+
if (parts.length !== 5) return expr;
|
|
102
|
+
const [min, hour, dom, month, dow] = parts;
|
|
103
|
+
const pad = (n) => n.padStart(2, "0");
|
|
104
|
+
if (min.startsWith("*/") && hour === "*" && dom === "*" && month === "*" && dow === "*") {
|
|
105
|
+
return `Every ${min.slice(2)} minutes`;
|
|
106
|
+
}
|
|
107
|
+
if (hour.startsWith("*/") && min === "0" && dom === "*" && month === "*" && dow === "*") {
|
|
108
|
+
return `Every ${hour.slice(2)} hours`;
|
|
109
|
+
}
|
|
110
|
+
if (/^\d+$/.test(min) && /^\d+$/.test(hour)) {
|
|
111
|
+
const time = `${pad(hour)}:${pad(min)}`;
|
|
112
|
+
if (dom === "*" && month === "*") {
|
|
113
|
+
if (dow === "*") return `Every day at ${time}`;
|
|
114
|
+
if (dow === "1-5") return `Every weekday at ${time}`;
|
|
115
|
+
if (dow === "0,6" || dow === "6,0") return `Every weekend day at ${time}`;
|
|
116
|
+
if (/^\d$/.test(dow)) return `Every ${DOW_NAMES[parseInt(dow, 10)]} at ${time}`;
|
|
117
|
+
}
|
|
118
|
+
if (/^\d+$/.test(dom) && month === "*" && dow === "*") {
|
|
119
|
+
return `Monthly on day ${dom} at ${time}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return expr;
|
|
123
|
+
}
|
|
124
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
125
|
+
function checkAwayGuard(lastUserActivityMs, nowMs) {
|
|
126
|
+
const gap = nowMs - lastUserActivityMs;
|
|
127
|
+
if (gap > 14 * DAY_MS) return "pause";
|
|
128
|
+
if (gap > 7 * DAY_MS) return "ask";
|
|
129
|
+
return "ok";
|
|
130
|
+
}
|
|
131
|
+
function buildRoutinePrompt(routine, isTest) {
|
|
132
|
+
const header = isTest ? `This is a **test run** of your routine "${routine.name}". Test runs perform real work \u2014 treat it like a live run, and flag anything before doing something irreversible.` : `This is a **scheduled run** of your routine "${routine.name}" (${routine.cronExpr}, ${routine.timezone}).`;
|
|
133
|
+
return [
|
|
134
|
+
header,
|
|
135
|
+
"",
|
|
136
|
+
"If the source data is unavailable, report the failure instead of using old data.",
|
|
137
|
+
"",
|
|
138
|
+
"---",
|
|
139
|
+
"",
|
|
140
|
+
routine.instructionMd
|
|
141
|
+
].join("\n");
|
|
142
|
+
}
|
|
143
|
+
var Scheduler = class {
|
|
144
|
+
constructor(deps) {
|
|
145
|
+
this.deps = deps;
|
|
146
|
+
}
|
|
147
|
+
deps;
|
|
148
|
+
tasks = /* @__PURE__ */ new Map();
|
|
149
|
+
paused = false;
|
|
150
|
+
/** Load every enabled routine and schedule it with node-cron. */
|
|
151
|
+
start() {
|
|
152
|
+
for (const routine of this.deps.store.listRoutines()) {
|
|
153
|
+
if (routine.enabled) this.scheduleRoutine(routine);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Cancel all scheduled tasks and release their timers. */
|
|
157
|
+
stop() {
|
|
158
|
+
for (const task of this.tasks.values()) {
|
|
159
|
+
task.stop();
|
|
160
|
+
void task.destroy();
|
|
161
|
+
}
|
|
162
|
+
this.tasks.clear();
|
|
163
|
+
}
|
|
164
|
+
/** Re-sync a single routine (call after create/update/delete/enable-toggle). */
|
|
165
|
+
reload(routineId) {
|
|
166
|
+
this.unschedule(routineId);
|
|
167
|
+
const routine = this.deps.store.getRoutine(routineId);
|
|
168
|
+
if (routine?.enabled && !this.paused) this.scheduleRoutine(routine);
|
|
169
|
+
}
|
|
170
|
+
/** Re-sync every routine from scratch. */
|
|
171
|
+
syncAll() {
|
|
172
|
+
this.stop();
|
|
173
|
+
this.start();
|
|
174
|
+
}
|
|
175
|
+
/** Pause every scheduled routine without forgetting its schedule (outline §8 away-guard). */
|
|
176
|
+
pauseAll() {
|
|
177
|
+
this.paused = true;
|
|
178
|
+
for (const task of this.tasks.values()) task.stop();
|
|
179
|
+
}
|
|
180
|
+
/** Resume every scheduled routine after a pause. */
|
|
181
|
+
resumeAll() {
|
|
182
|
+
this.paused = false;
|
|
183
|
+
for (const task of this.tasks.values()) task.start();
|
|
184
|
+
}
|
|
185
|
+
unschedule(routineId) {
|
|
186
|
+
const task = this.tasks.get(routineId);
|
|
187
|
+
if (!task) return;
|
|
188
|
+
task.stop();
|
|
189
|
+
void task.destroy();
|
|
190
|
+
this.tasks.delete(routineId);
|
|
191
|
+
}
|
|
192
|
+
scheduleRoutine(routine) {
|
|
193
|
+
if (!cron.validate(routine.cronExpr)) {
|
|
194
|
+
log.warn(`routine ${routine.id} has an invalid cron expression: "${routine.cronExpr}"`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const task = cron.schedule(
|
|
198
|
+
routine.cronExpr,
|
|
199
|
+
() => {
|
|
200
|
+
this.fire(routine.id, false);
|
|
201
|
+
},
|
|
202
|
+
{ timezone: routine.timezone, unref: true }
|
|
203
|
+
);
|
|
204
|
+
if (this.paused) task.stop();
|
|
205
|
+
this.tasks.set(routine.id, task);
|
|
206
|
+
this.updateNextRunAt(routine);
|
|
207
|
+
}
|
|
208
|
+
updateNextRunAt(routine) {
|
|
209
|
+
const next = nextRunAt(routine.cronExpr, routine.timezone, Date.now());
|
|
210
|
+
this.deps.store.updateRoutine(routine.id, { nextRunAt: next });
|
|
211
|
+
}
|
|
212
|
+
/** Fire a routine now: starts a run record, publishes events, and enqueues the turn. */
|
|
213
|
+
fire(routineId, isTest) {
|
|
214
|
+
const { store, bus, manager } = this.deps;
|
|
215
|
+
const routine = store.getRoutine(routineId);
|
|
216
|
+
if (!routine) throw new Error(`Unknown routine: ${routineId}`);
|
|
217
|
+
const bot = store.getBot(routine.botId);
|
|
218
|
+
const run = store.startRun(routineId, isTest, bot?.threadId ?? void 0);
|
|
219
|
+
bus.publish({ type: "routine.run", threadId: bot?.threadId ?? null, botId: routine.botId, run });
|
|
220
|
+
const next = isTest ? routine.nextRunAt : nextRunAt(routine.cronExpr, routine.timezone, Date.now());
|
|
221
|
+
store.updateRoutine(routineId, { lastRunAt: run.startedAt, nextRunAt: next });
|
|
222
|
+
if (!bot || !bot.threadId) {
|
|
223
|
+
const failed = store.finishRun(run.id, "failed", "Owning bot no longer exists.");
|
|
224
|
+
if (failed) bus.publish({ type: "routine.run", threadId: null, botId: routine.botId, run: failed });
|
|
225
|
+
return run.id;
|
|
226
|
+
}
|
|
227
|
+
manager.enqueue({
|
|
228
|
+
botId: bot.id,
|
|
229
|
+
threadId: bot.threadId,
|
|
230
|
+
prompt: buildRoutinePrompt(routine, isTest),
|
|
231
|
+
origin: "routine",
|
|
232
|
+
hops: 0,
|
|
233
|
+
priority: 10,
|
|
234
|
+
onDone: (summary, ok) => {
|
|
235
|
+
const finished = store.finishRun(run.id, ok ? "ok" : "failed", summary);
|
|
236
|
+
if (finished) bus.publish({ type: "routine.run", threadId: bot.threadId, botId: routine.botId, run: finished });
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
return run.id;
|
|
240
|
+
}
|
|
241
|
+
/** Fire a routine immediately as a test run (real work, marked `isTest`). Returns the run id. */
|
|
242
|
+
testRun(routineId) {
|
|
243
|
+
return this.fire(routineId, true);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Apply the outline §8 away-guard: pause all routines beyond 14 days of inactivity, or notify
|
|
247
|
+
* asking whether to keep them running between 7 and 14 days.
|
|
248
|
+
*/
|
|
249
|
+
checkAndApplyAwayGuard(lastUserActivityMs, nowMs = Date.now()) {
|
|
250
|
+
const result = checkAwayGuard(lastUserActivityMs, nowMs);
|
|
251
|
+
if (result === "pause") {
|
|
252
|
+
this.pauseAll();
|
|
253
|
+
} else if (result === "ask") {
|
|
254
|
+
this.deps.bus.publish({
|
|
255
|
+
type: "notify",
|
|
256
|
+
threadId: null,
|
|
257
|
+
botId: null,
|
|
258
|
+
title: "Still away?",
|
|
259
|
+
body: "It's been a while since you were last active. Keep routines running, or pause them?",
|
|
260
|
+
level: "warn"
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
export {
|
|
267
|
+
Scheduler,
|
|
268
|
+
buildRoutinePrompt,
|
|
269
|
+
checkAwayGuard,
|
|
270
|
+
cronToHuman,
|
|
271
|
+
nextRunAt,
|
|
272
|
+
parseCronExpr
|
|
273
|
+
};
|
|
274
|
+
//# sourceMappingURL=scheduler-Q7OHNGP6.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../packages/server/src/scheduler/scheduler.ts"],
|
|
4
|
+
"sourcesContent": ["import cron, { type ScheduledTask } from 'node-cron';\nimport type { Store } from '../db/store.js';\nimport type { EventBus } from '../util/bus.js';\nimport { logger } from '../util/log.js';\nimport type { Routine, Settings, TurnOrigin } from '@antbot/shared';\n\nconst log = logger('scheduler');\n\n/**\n * The subset of BotManager.enqueue the scheduler needs. Kept minimal and structural (rather than\n * importing BotManager directly) so tests can inject a fake manager without spawning real turns.\n */\nexport interface SchedulerEnqueueArgs {\n botId: string;\n threadId: string;\n prompt: string;\n origin: TurnOrigin;\n hops: number;\n priority?: number;\n onDone?: (summary: string, ok: boolean) => void;\n}\nexport interface SchedulerManager {\n enqueue(job: SchedulerEnqueueArgs): { id: string };\n}\n\nexport interface SchedulerDeps {\n store: Store;\n bus: EventBus;\n manager: SchedulerManager;\n getSettings: () => Settings;\n}\n\n/* ------------------------------- cron parsing ------------------------------- */\n\ninterface CronField {\n values: Set<number>;\n wildcard: boolean;\n}\n\ninterface ParsedCron {\n minute: CronField;\n hour: CronField;\n dayOfMonth: CronField;\n month: CronField;\n dayOfWeek: CronField;\n}\n\nfunction parseCronFieldPart(field: string, min: number, max: number): CronField {\n const trimmed = field.trim();\n if (trimmed === '*') {\n const values = new Set<number>();\n for (let v = min; v <= max; v++) values.add(v);\n return { values, wildcard: true };\n }\n const values = new Set<number>();\n for (const part of trimmed.split(',')) {\n const m = part.match(/^(\\*|\\d+)(?:-(\\d+))?(?:\\/(\\d+))?$/);\n if (!m) throw new Error(`Invalid cron field: \"${part}\"`);\n const [, startStr, endStr, stepStr] = m;\n const step = stepStr ? parseInt(stepStr, 10) : 1;\n let start: number;\n let end: number;\n if (startStr === '*') {\n start = min;\n end = max;\n } else {\n start = parseInt(startStr!, 10);\n end = endStr !== undefined ? parseInt(endStr, 10) : stepStr ? max : start;\n }\n for (let v = start; v <= end; v += step) values.add(v);\n }\n return { values, wildcard: false };\n}\n\n/** Parse a standard 5-field cron expression (minute hour dom month dow). Pure, no I/O. */\nexport function parseCronExpr(expr: string): ParsedCron {\n const parts = expr.trim().split(/\\s+/);\n if (parts.length !== 5) throw new Error(`Cron expression must have 5 fields: \"${expr}\"`);\n const [minute, hour, dayOfMonth, month, dayOfWeekRaw] = parts as [string, string, string, string, string];\n const dayOfWeek = parseCronFieldPart(dayOfWeekRaw, 0, 7);\n // normalize 7 (alt-Sunday) into 0\n if (dayOfWeek.values.has(7)) {\n dayOfWeek.values.delete(7);\n dayOfWeek.values.add(0);\n }\n return {\n minute: parseCronFieldPart(minute, 0, 59),\n hour: parseCronFieldPart(hour, 0, 23),\n dayOfMonth: parseCronFieldPart(dayOfMonth, 1, 31),\n month: parseCronFieldPart(month, 1, 12),\n dayOfWeek,\n };\n}\n\ninterface TzParts {\n month: number;\n day: number;\n hour: number;\n minute: number;\n weekday: number;\n}\n\nconst WEEKDAY_INDEX: Record<string, number> = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };\n\nfunction makeTzFormatter(timeZone: string): Intl.DateTimeFormat {\n return new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour12: false,\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n weekday: 'short',\n });\n}\n\nfunction tzParts(dtf: Intl.DateTimeFormat, ms: number): TzParts {\n const parts = dtf.formatToParts(new Date(ms));\n const map: Record<string, string> = {};\n for (const p of parts) map[p.type] = p.value;\n let hour = parseInt(map.hour ?? '0', 10);\n if (hour === 24) hour = 0; // some ICU builds report midnight as \"24\" under hour12:false\n return {\n month: parseInt(map.month ?? '1', 10),\n day: parseInt(map.day ?? '1', 10),\n hour,\n minute: parseInt(map.minute ?? '0', 10),\n weekday: WEEKDAY_INDEX[map.weekday ?? 'Sun'] ?? 0,\n };\n}\n\nconst MAX_LOOKAHEAD_MINUTES = 366 * 24 * 60;\n\n/**\n * Compute the next fire time (ms, strictly after `fromMs`) for a 5-field cron expression in a\n * given IANA timezone. Implemented by iterating minute-by-minute over a bounded window \u2014 no\n * dependency on node-cron internals. Returns null if nothing matches within 366 days.\n */\nexport function nextRunAt(cronExpr: string, timezone: string, fromMs: number): number | null {\n const fields = parseCronExpr(cronExpr);\n const dtf = makeTzFormatter(timezone);\n const bothWildcard = fields.dayOfMonth.wildcard && fields.dayOfWeek.wildcard;\n\n let t = Math.floor(fromMs / 60000) * 60000 + 60000;\n for (let i = 0; i < MAX_LOOKAHEAD_MINUTES; i++, t += 60000) {\n const p = tzParts(dtf, t);\n if (!fields.minute.values.has(p.minute)) continue;\n if (!fields.hour.values.has(p.hour)) continue;\n if (!fields.month.values.has(p.month)) continue;\n const domMatch = fields.dayOfMonth.values.has(p.day);\n const dowMatch = fields.dayOfWeek.values.has(p.weekday);\n const dayOk = bothWildcard\n ? true\n : fields.dayOfMonth.wildcard\n ? dowMatch\n : fields.dayOfWeek.wildcard\n ? domMatch\n : domMatch || dowMatch;\n if (!dayOk) continue;\n return t;\n }\n return null;\n}\n\n/* ------------------------------- human summary ------------------------------- */\n\nconst DOW_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\n\n/** Render a short human string for common cron shapes; falls back to the raw expression. */\nexport function cronToHuman(expr: string): string {\n const parts = expr.trim().split(/\\s+/);\n if (parts.length !== 5) return expr;\n const [min, hour, dom, month, dow] = parts as [string, string, string, string, string];\n const pad = (n: string): string => n.padStart(2, '0');\n\n if (min.startsWith('*/') && hour === '*' && dom === '*' && month === '*' && dow === '*') {\n return `Every ${min.slice(2)} minutes`;\n }\n if (hour.startsWith('*/') && min === '0' && dom === '*' && month === '*' && dow === '*') {\n return `Every ${hour.slice(2)} hours`;\n }\n if (/^\\d+$/.test(min) && /^\\d+$/.test(hour)) {\n const time = `${pad(hour)}:${pad(min)}`;\n if (dom === '*' && month === '*') {\n if (dow === '*') return `Every day at ${time}`;\n if (dow === '1-5') return `Every weekday at ${time}`;\n if (dow === '0,6' || dow === '6,0') return `Every weekend day at ${time}`;\n if (/^\\d$/.test(dow)) return `Every ${DOW_NAMES[parseInt(dow, 10)]} at ${time}`;\n }\n if (/^\\d+$/.test(dom) && month === '*' && dow === '*') {\n return `Monthly on day ${dom} at ${time}`;\n }\n }\n return expr;\n}\n\n/* --------------------------------- away guard --------------------------------- */\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Outline \u00A78: after a long absence, ask whether to keep routines running, and pause if there's\n * no response. `ok` under 7 days away, `ask` between 7 and 14 days, `pause` beyond 14 days.\n */\nexport function checkAwayGuard(lastUserActivityMs: number, nowMs: number): 'ok' | 'ask' | 'pause' {\n const gap = nowMs - lastUserActivityMs;\n if (gap > 14 * DAY_MS) return 'pause';\n if (gap > 7 * DAY_MS) return 'ask';\n return 'ok';\n}\n\n/* --------------------------------- prompt shape --------------------------------- */\n\n/** Build the turn prompt for a routine fire, per outline \u00A78 (naming + stale-data doctrine). */\nexport function buildRoutinePrompt(routine: Routine, isTest: boolean): string {\n const header = isTest\n ? `This is a **test run** of your routine \"${routine.name}\". Test runs perform real work \u2014 treat it like a live run, and flag anything before doing something irreversible.`\n : `This is a **scheduled run** of your routine \"${routine.name}\" (${routine.cronExpr}, ${routine.timezone}).`;\n return [\n header,\n '',\n 'If the source data is unavailable, report the failure instead of using old data.',\n '',\n '---',\n '',\n routine.instructionMd,\n ].join('\\n');\n}\n\n/* ---------------------------------- scheduler ---------------------------------- */\n\nexport class Scheduler {\n private tasks = new Map<string, ScheduledTask>();\n private paused = false;\n\n constructor(private deps: SchedulerDeps) {}\n\n /** Load every enabled routine and schedule it with node-cron. */\n start(): void {\n for (const routine of this.deps.store.listRoutines()) {\n if (routine.enabled) this.scheduleRoutine(routine);\n }\n }\n\n /** Cancel all scheduled tasks and release their timers. */\n stop(): void {\n for (const task of this.tasks.values()) {\n task.stop();\n void task.destroy();\n }\n this.tasks.clear();\n }\n\n /** Re-sync a single routine (call after create/update/delete/enable-toggle). */\n reload(routineId: string): void {\n this.unschedule(routineId);\n const routine = this.deps.store.getRoutine(routineId);\n if (routine?.enabled && !this.paused) this.scheduleRoutine(routine);\n }\n\n /** Re-sync every routine from scratch. */\n syncAll(): void {\n this.stop();\n this.start();\n }\n\n /** Pause every scheduled routine without forgetting its schedule (outline \u00A78 away-guard). */\n pauseAll(): void {\n this.paused = true;\n for (const task of this.tasks.values()) task.stop();\n }\n\n /** Resume every scheduled routine after a pause. */\n resumeAll(): void {\n this.paused = false;\n for (const task of this.tasks.values()) task.start();\n }\n\n private unschedule(routineId: string): void {\n const task = this.tasks.get(routineId);\n if (!task) return;\n task.stop();\n void task.destroy();\n this.tasks.delete(routineId);\n }\n\n private scheduleRoutine(routine: Routine): void {\n if (!cron.validate(routine.cronExpr)) {\n log.warn(`routine ${routine.id} has an invalid cron expression: \"${routine.cronExpr}\"`);\n return;\n }\n const task = cron.schedule(\n routine.cronExpr,\n () => {\n this.fire(routine.id, false);\n },\n { timezone: routine.timezone, unref: true },\n );\n if (this.paused) task.stop();\n this.tasks.set(routine.id, task);\n this.updateNextRunAt(routine);\n }\n\n private updateNextRunAt(routine: Routine): void {\n const next = nextRunAt(routine.cronExpr, routine.timezone, Date.now());\n this.deps.store.updateRoutine(routine.id, { nextRunAt: next });\n }\n\n /** Fire a routine now: starts a run record, publishes events, and enqueues the turn. */\n private fire(routineId: string, isTest: boolean): string {\n const { store, bus, manager } = this.deps;\n const routine = store.getRoutine(routineId);\n if (!routine) throw new Error(`Unknown routine: ${routineId}`);\n\n const bot = store.getBot(routine.botId);\n const run = store.startRun(routineId, isTest, bot?.threadId ?? undefined);\n bus.publish({ type: 'routine.run', threadId: bot?.threadId ?? null, botId: routine.botId, run });\n\n const next = isTest ? routine.nextRunAt : nextRunAt(routine.cronExpr, routine.timezone, Date.now());\n store.updateRoutine(routineId, { lastRunAt: run.startedAt, nextRunAt: next });\n\n if (!bot || !bot.threadId) {\n const failed = store.finishRun(run.id, 'failed', 'Owning bot no longer exists.');\n if (failed) bus.publish({ type: 'routine.run', threadId: null, botId: routine.botId, run: failed });\n return run.id;\n }\n\n manager.enqueue({\n botId: bot.id,\n threadId: bot.threadId,\n prompt: buildRoutinePrompt(routine, isTest),\n origin: 'routine',\n hops: 0,\n priority: 10,\n onDone: (summary, ok) => {\n const finished = store.finishRun(run.id, ok ? 'ok' : 'failed', summary);\n if (finished) bus.publish({ type: 'routine.run', threadId: bot.threadId, botId: routine.botId, run: finished });\n },\n });\n\n return run.id;\n }\n\n /** Fire a routine immediately as a test run (real work, marked `isTest`). Returns the run id. */\n testRun(routineId: string): string {\n return this.fire(routineId, true);\n }\n\n /**\n * Apply the outline \u00A78 away-guard: pause all routines beyond 14 days of inactivity, or notify\n * asking whether to keep them running between 7 and 14 days.\n */\n checkAndApplyAwayGuard(lastUserActivityMs: number, nowMs = Date.now()): 'ok' | 'ask' | 'pause' {\n const result = checkAwayGuard(lastUserActivityMs, nowMs);\n if (result === 'pause') {\n this.pauseAll();\n } else if (result === 'ask') {\n this.deps.bus.publish({\n type: 'notify',\n threadId: null,\n botId: null,\n title: 'Still away?',\n body: \"It's been a while since you were last active. Keep routines running, or pause them?\",\n level: 'warn',\n });\n }\n return result;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["values"]
|
|
7
|
+
}
|