@mastra/code-sdk 1.9.0-alpha.9 → 1.10.0-alpha.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/README.md +23 -0
- package/dist/agent-connections/tools.d.ts.map +1 -1
- package/dist/agent-connections/tools.js +5 -5
- package/dist/agent-connections/tools.js.map +1 -1
- package/dist/agents/tools.d.ts +1 -1
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +2 -1
- package/dist/agents/tools.js.map +1 -1
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +2 -3
- package/dist/analytics.js.map +1 -1
- package/dist/auth/account-rotation-processor.d.ts.map +1 -1
- package/dist/auth/account-rotation-processor.js +17 -10
- package/dist/auth/account-rotation-processor.js.map +1 -1
- package/dist/auth/providers/kimi-coding.d.ts.map +1 -1
- package/dist/auth/providers/kimi-coding.js +1 -2
- package/dist/auth/providers/kimi-coding.js.map +1 -1
- package/dist/auth/providers/openai-codex.d.ts +1 -1
- package/dist/auth/providers/openai-codex.d.ts.map +1 -1
- package/dist/auth/providers/openai-codex.js +7 -19
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/storage.js +3 -3
- package/dist/auth/storage.js.map +1 -1
- package/dist/experimental-agent.d.ts +11 -0
- package/dist/experimental-agent.d.ts.map +1 -0
- package/dist/experimental-agent.js +36 -0
- package/dist/experimental-agent.js.map +1 -0
- package/dist/goal-manager.d.ts +39 -3
- package/dist/goal-manager.d.ts.map +1 -1
- package/dist/goal-manager.js +113 -31
- package/dist/goal-manager.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -12
- package/dist/index.js.map +1 -1
- package/dist/knowledge-inspector.d.ts.map +1 -1
- package/dist/knowledge-inspector.js +1 -2
- package/dist/knowledge-inspector.js.map +1 -1
- package/dist/onboarding/settings.d.ts +11 -0
- package/dist/onboarding/settings.d.ts.map +1 -1
- package/dist/onboarding/settings.js +39 -5
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +6 -1
- package/dist/permissions.js.map +1 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +1 -2
- package/dist/plugins/loader.js.map +1 -1
- package/dist/process-memory-diagnostics.d.ts.map +1 -1
- package/dist/process-memory-diagnostics.js +2 -3
- package/dist/process-memory-diagnostics.js.map +1 -1
- package/dist/schedules/index.d.ts +10 -0
- package/dist/schedules/index.d.ts.map +1 -0
- package/dist/schedules/index.js +6 -0
- package/dist/schedules/interval.d.ts +49 -0
- package/dist/schedules/interval.d.ts.map +1 -0
- package/dist/schedules/interval.js +215 -0
- package/dist/schedules/interval.js.map +1 -0
- package/dist/schedules/prompt.d.ts +36 -0
- package/dist/schedules/prompt.d.ts.map +1 -0
- package/dist/schedules/prompt.js +39 -0
- package/dist/schedules/prompt.js.map +1 -0
- package/dist/schedules/run-script.d.ts +8 -0
- package/dist/schedules/run-script.d.ts.map +1 -0
- package/dist/schedules/run-script.js +83 -0
- package/dist/schedules/run-script.js.map +1 -0
- package/dist/schedules/scheduler.d.ts +101 -0
- package/dist/schedules/scheduler.d.ts.map +1 -0
- package/dist/schedules/scheduler.js +169 -0
- package/dist/schedules/scheduler.js.map +1 -0
- package/dist/schedules/tools.d.ts +57 -0
- package/dist/schedules/tools.d.ts.map +1 -0
- package/dist/schedules/tools.js +220 -0
- package/dist/schedules/tools.js.map +1 -0
- package/dist/tools/workflows/ephemeral-memory.d.ts +21 -0
- package/dist/tools/workflows/ephemeral-memory.d.ts.map +1 -1
- package/dist/tools/workflows/ephemeral-memory.js +1 -2
- package/dist/tools/workflows/ephemeral-memory.js.map +1 -1
- package/dist/utils/discarding-scores-storage.d.ts +33 -0
- package/dist/utils/discarding-scores-storage.d.ts.map +1 -0
- package/dist/utils/discarding-scores-storage.js +54 -0
- package/dist/utils/discarding-scores-storage.js.map +1 -0
- package/dist/utils/maintenance-lock.d.ts +42 -0
- package/dist/utils/maintenance-lock.d.ts.map +1 -0
- package/dist/utils/maintenance-lock.js +199 -0
- package/dist/utils/maintenance-lock.js.map +1 -0
- package/dist/utils/prune-cli.d.ts +24 -0
- package/dist/utils/prune-cli.d.ts.map +1 -0
- package/dist/utils/prune-cli.js +125 -0
- package/dist/utils/prune-cli.js.map +1 -0
- package/dist/utils/signals-pubsub.d.ts.map +1 -1
- package/dist/utils/signals-pubsub.js +1 -2
- package/dist/utils/signals-pubsub.js.map +1 -1
- package/dist/utils/storage-maintenance.d.ts.map +1 -1
- package/dist/utils/storage-maintenance.js +33 -3
- package/dist/utils/storage-maintenance.js.map +1 -1
- package/package.json +21 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interval.js","names":[],"sources":["../../src/schedules/interval.ts"],"sourcesContent":["/**\n * Interval parsing and fire-time math for `/schedules`.\n *\n * Users type `5m`, `2h`, `1 day`. Schedules fire on local wall-clock\n * boundaries (5m fires at :00, :05, ...), so only intervals that divide the\n * hour (minutes) or the day (hours) give a stable cadence; anything else is\n * rejected with the nearest valid suggestion instead of silently drifting.\n */\n\nconst SECOND_MS = 1_000;\nconst MINUTE_MS = 60 * SECOND_MS;\nconst HOUR_MS = 60 * MINUTE_MS;\nconst DAY_MS = 24 * HOUR_MS;\n\nexport type ParsedInterval = { ms: number; label: string };\nexport type IntervalError = { error: string };\n\nconst UNIT_MS: Record<string, { ms: number; short: string }> = {\n s: { ms: SECOND_MS, short: 's' },\n sec: { ms: SECOND_MS, short: 's' },\n secs: { ms: SECOND_MS, short: 's' },\n second: { ms: SECOND_MS, short: 's' },\n seconds: { ms: SECOND_MS, short: 's' },\n m: { ms: MINUTE_MS, short: 'm' },\n min: { ms: MINUTE_MS, short: 'm' },\n mins: { ms: MINUTE_MS, short: 'm' },\n minute: { ms: MINUTE_MS, short: 'm' },\n minutes: { ms: MINUTE_MS, short: 'm' },\n h: { ms: HOUR_MS, short: 'h' },\n hr: { ms: HOUR_MS, short: 'h' },\n hrs: { ms: HOUR_MS, short: 'h' },\n hour: { ms: HOUR_MS, short: 'h' },\n hours: { ms: HOUR_MS, short: 'h' },\n d: { ms: DAY_MS, short: 'd' },\n day: { ms: DAY_MS, short: 'd' },\n days: { ms: DAY_MS, short: 'd' },\n};\n\nconst INTERVAL_RE = /^(\\d+)\\s*([a-z]+)$/i;\n\n/** Parse `5m`, `2h`, `1 day`, `90 minutes` into milliseconds plus a normalized label. */\nexport function parseInterval(text: string): ParsedInterval | IntervalError {\n const match = INTERVAL_RE.exec(text.trim());\n if (!match) {\n return { error: `Invalid interval \"${text}\". Use a number followed by a unit, e.g. 5m, 2h, 1d.` };\n }\n const value = Number(match[1]);\n const unit = UNIT_MS[match[2]!.toLowerCase()];\n if (!unit) {\n return { error: `Unknown interval unit \"${match[2]}\". Use s, m, h, or d.` };\n }\n if (value <= 0) {\n return { error: 'Interval must be greater than zero.' };\n }\n return { ms: value * unit.ms, label: `${value}${unit.short}` };\n}\n\nexport type IntervalCheck = { ok: true } | { error: string; suggestion?: string };\n\nconst MINUTE_DIVISORS = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60];\nconst HOUR_DIVISORS = [1, 2, 3, 4, 6, 8, 12, 24];\n\nfunction nearest(candidates: number[], target: number): number {\n return candidates.reduce((best, candidate) =>\n Math.abs(candidate - target) < Math.abs(best - target) ? candidate : best,\n );\n}\n\n/**\n * Check that an interval fires on stable clock boundaries: whole minutes that\n * divide the hour, whole hours that divide the day, or exactly one day.\n */\nexport function validateInterval({ ms }: { ms: number }): IntervalCheck {\n if (ms < MINUTE_MS) {\n return { error: 'Schedules fire at most once per minute.', suggestion: '1m' };\n }\n if (ms % MINUTE_MS !== 0) {\n const minutes = Math.round(ms / MINUTE_MS);\n return {\n error: 'Interval must be a whole number of minutes.',\n suggestion: `${Math.max(1, nearest(MINUTE_DIVISORS, minutes))}m`,\n };\n }\n const minutes = ms / MINUTE_MS;\n if (minutes < 60) {\n if (60 % minutes === 0) {\n return { ok: true };\n }\n return {\n error: `${minutes}m does not divide an hour evenly.`,\n suggestion: `${nearest(MINUTE_DIVISORS, minutes)}m`,\n };\n }\n if (minutes % 60 !== 0) {\n const hours = Math.round(minutes / 60);\n return {\n error: `${minutes}m is not a whole number of hours.`,\n suggestion: hours < 24 ? `${nearest(HOUR_DIVISORS, hours)}h` : '1d',\n };\n }\n const hours = minutes / 60;\n if (hours < 24) {\n if (24 % hours === 0) {\n return { ok: true };\n }\n return {\n error: `${hours}h does not divide a day evenly.`,\n suggestion: `${nearest(HOUR_DIVISORS, hours)}h`,\n };\n }\n if (hours === 24) {\n return { ok: true };\n }\n return {\n error: 'Intervals longer than one day are not supported.',\n suggestion: '1d',\n };\n}\n\n/** Hour-and-longer cadences: which local wall-clock slot a minute belongs to. */\nfunction hourSlotKey(date: Date, stepHours: number): number {\n return (\n ((date.getFullYear() * 12 + date.getMonth()) * 31 + date.getDate()) * 24 +\n Math.floor(date.getHours() / stepHours) * stepHours\n );\n}\n\n/**\n * The first boundary strictly after `after`, on the local wall clock\n * (5m → :00, :05, ...; 2h → 00:00, 02:00, ...; 1d → midnight). `intervalMs`\n * must pass {@link validateInterval}.\n *\n * Around daylight-saving changes this behaves like cron:\n * - Sub-hour cadences follow the minute hand, so they keep firing every N real\n * minutes through both the skipped and the repeated hour. Across a\n * half-hour change, the gap between fires never exceeds the interval.\n * - Hourly and daily cadences fire once per wall-clock slot. A boundary that\n * falls in a skipped hour fires at the first minute after the jump (02:00 →\n * 03:00), and a repeated hour does not fire a second time.\n *\n * Computing from `after` rather than from the previous fire means a late\n * timer (sleep, busy event loop) skips missed boundaries instead of bursting\n * to catch up.\n */\nexport function nextFireTime(intervalMs: number, after: number): number {\n // Truncate in UTC: local setters resolve a repeated hour to its first occurrence.\n const cursor = new Date(Math.floor(after / MINUTE_MS) * MINUTE_MS);\n const stepMinutes = intervalMs / MINUTE_MS;\n // Walk minute by minute: at most an hour for sub-hour cadences, ~a day otherwise.\n if (stepMinutes < 60) {\n // Never wait longer than one interval: a half-hour clock change (Lord Howe\n // Island) can otherwise shift the minute hand past the next boundary.\n const latest = cursor.getTime() + intervalMs;\n do cursor.setTime(cursor.getTime() + MINUTE_MS);\n while (cursor.getMinutes() % stepMinutes !== 0 && cursor.getTime() < latest);\n return cursor.getTime();\n }\n const stepHours = stepMinutes / 60;\n const startSlot = hourSlotKey(new Date(after), stepHours);\n do cursor.setTime(cursor.getTime() + MINUTE_MS);\n while (hourSlotKey(cursor, stepHours) <= startSlot);\n return cursor.getTime();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-time prompt assembly for Mastra Code schedules.
|
|
3
|
+
*
|
|
4
|
+
* File-backed schedules only hold a descriptor; each fire runs the script or
|
|
5
|
+
* re-reads the file so the model always sees current output. Failures become
|
|
6
|
+
* the prompt text rather than a skipped fire — a schedule that silently stops
|
|
7
|
+
* delivering is worse than one that reports "the script broke".
|
|
8
|
+
*/
|
|
9
|
+
import type { ThreadSchedule } from './scheduler.js';
|
|
10
|
+
export declare const SCRIPT_TIMEOUT_MS = 60000;
|
|
11
|
+
export type ScriptResult = {
|
|
12
|
+
stdout: string;
|
|
13
|
+
stderr: string;
|
|
14
|
+
exitCode: number | null;
|
|
15
|
+
};
|
|
16
|
+
export type RunScript = (absPath: string, options: {
|
|
17
|
+
cwd: string;
|
|
18
|
+
timeoutMs: number;
|
|
19
|
+
}) => Promise<ScriptResult>;
|
|
20
|
+
/**
|
|
21
|
+
* One fire's prompt. `outcome` summarizes a file-backed fire for the
|
|
22
|
+
* transcript header: `exit 0`, `exit 3`, `killed` (timeout/signal), or
|
|
23
|
+
* `failed` (the file could not be run or read).
|
|
24
|
+
*/
|
|
25
|
+
export type AssembledPrompt = {
|
|
26
|
+
prompt: string;
|
|
27
|
+
outcome?: string;
|
|
28
|
+
};
|
|
29
|
+
export type AssemblePromptOptions = {
|
|
30
|
+
cwd: string;
|
|
31
|
+
runScript: RunScript;
|
|
32
|
+
readFile: (absPath: string) => Promise<string>;
|
|
33
|
+
};
|
|
34
|
+
export declare function formatScriptOutput(displayPath: string, result: ScriptResult): string;
|
|
35
|
+
export declare function assembleSchedulePrompt(spec: Pick<ThreadSchedule, 'prompt' | 'file' | 'extraPrompt'>, options: AssemblePromptOptions): Promise<AssembledPrompt>;
|
|
36
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/schedules/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEvF,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEhH;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC;AAMF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAMpF;AAMD,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAC,EAC7D,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAmB1B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/schedules/prompt.ts
|
|
2
|
+
const SCRIPT_TIMEOUT_MS = 6e4;
|
|
3
|
+
function appendExtra(text, extra) {
|
|
4
|
+
return extra ? `${text}\n\n${extra}` : text;
|
|
5
|
+
}
|
|
6
|
+
function formatScriptOutput(displayPath, result) {
|
|
7
|
+
const body = [result.stdout, result.stderr].map((part) => part.trimEnd()).filter(Boolean).join("\n");
|
|
8
|
+
return `Output of ${displayPath} (exit ${result.exitCode ?? "null"}):\n${body || "(no output)"}`;
|
|
9
|
+
}
|
|
10
|
+
function errorMessage(error) {
|
|
11
|
+
return error instanceof Error ? error.message : String(error);
|
|
12
|
+
}
|
|
13
|
+
async function assembleSchedulePrompt(spec, options) {
|
|
14
|
+
const { file } = spec;
|
|
15
|
+
if (!file) return { prompt: spec.prompt ?? "" };
|
|
16
|
+
let text;
|
|
17
|
+
let outcome;
|
|
18
|
+
try {
|
|
19
|
+
if (file.mode === "script") {
|
|
20
|
+
const result = await options.runScript(file.path, {
|
|
21
|
+
cwd: options.cwd,
|
|
22
|
+
timeoutMs: SCRIPT_TIMEOUT_MS
|
|
23
|
+
});
|
|
24
|
+
text = formatScriptOutput(file.displayPath, result);
|
|
25
|
+
outcome = result.exitCode === null ? "killed" : `exit ${result.exitCode}`;
|
|
26
|
+
} else text = (await options.readFile(file.path)).trim();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
text = `Schedule ${file.displayPath} failed: ${errorMessage(error)}`;
|
|
29
|
+
outcome = "failed";
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
prompt: appendExtra(text, spec.extraPrompt),
|
|
33
|
+
...outcome ? { outcome } : {}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { SCRIPT_TIMEOUT_MS, assembleSchedulePrompt, formatScriptOutput };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","names":[],"sources":["../../src/schedules/prompt.ts"],"sourcesContent":["/**\n * Fire-time prompt assembly for Mastra Code schedules.\n *\n * File-backed schedules only hold a descriptor; each fire runs the script or\n * re-reads the file so the model always sees current output. Failures become\n * the prompt text rather than a skipped fire — a schedule that silently stops\n * delivering is worse than one that reports \"the script broke\".\n */\nimport type { ThreadSchedule } from './scheduler.js';\n\nexport const SCRIPT_TIMEOUT_MS = 60_000;\n\nexport type ScriptResult = { stdout: string; stderr: string; exitCode: number | null };\n\nexport type RunScript = (absPath: string, options: { cwd: string; timeoutMs: number }) => Promise<ScriptResult>;\n\n/**\n * One fire's prompt. `outcome` summarizes a file-backed fire for the\n * transcript header: `exit 0`, `exit 3`, `killed` (timeout/signal), or\n * `failed` (the file could not be run or read).\n */\nexport type AssembledPrompt = { prompt: string; outcome?: string };\n\nexport type AssemblePromptOptions = {\n cwd: string;\n runScript: RunScript;\n readFile: (absPath: string) => Promise<string>;\n};\n\nfunction appendExtra(text: string, extra: string | undefined): string {\n return extra ? `${text}\\n\\n${extra}` : text;\n}\n\nexport function formatScriptOutput(displayPath: string, result: ScriptResult): string {\n const body = [result.stdout, result.stderr]\n .map(part => part.trimEnd())\n .filter(Boolean)\n .join('\\n');\n return `Output of ${displayPath} (exit ${result.exitCode ?? 'null'}):\\n${body || '(no output)'}`;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport async function assembleSchedulePrompt(\n spec: Pick<ThreadSchedule, 'prompt' | 'file' | 'extraPrompt'>,\n options: AssemblePromptOptions,\n): Promise<AssembledPrompt> {\n const { file } = spec;\n if (!file) return { prompt: spec.prompt ?? '' };\n\n let text: string;\n let outcome: string | undefined;\n try {\n if (file.mode === 'script') {\n const result = await options.runScript(file.path, { cwd: options.cwd, timeoutMs: SCRIPT_TIMEOUT_MS });\n text = formatScriptOutput(file.displayPath, result);\n outcome = result.exitCode === null ? 'killed' : `exit ${result.exitCode}`;\n } else {\n text = (await options.readFile(file.path)).trim();\n }\n } catch (error) {\n text = `Schedule ${file.displayPath} failed: ${errorMessage(error)}`;\n outcome = 'failed';\n }\n return { prompt: appendExtra(text, spec.extraPrompt), ...(outcome ? { outcome } : {}) };\n}\n"],"mappings":";AAUA,MAAa,oBAAoB;AAmBjC,SAAS,YAAY,MAAc,OAAmC;CACpE,OAAO,QAAQ,GAAG,KAAK,MAAM,UAAU;AACzC;AAEA,SAAgB,mBAAmB,aAAqB,QAA8B;CACpF,MAAM,OAAO,CAAC,OAAO,QAAQ,OAAO,MAAM,CAAC,CACxC,KAAI,SAAQ,KAAK,QAAQ,CAAC,CAAC,CAC3B,OAAO,OAAO,CAAC,CACf,KAAK,IAAI;CACZ,OAAO,aAAa,YAAY,SAAS,OAAO,YAAY,OAAO,MAAM,QAAQ;AACnF;AAEA,SAAS,aAAa,OAAwB;CAC5C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,eAAsB,uBACpB,MACA,SAC0B;CAC1B,MAAM,EAAE,SAAS;CACjB,IAAI,CAAC,MAAM,OAAO,EAAE,QAAQ,KAAK,UAAU,GAAG;CAE9C,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,IAAI,KAAK,SAAS,UAAU;GAC1B,MAAM,SAAS,MAAM,QAAQ,UAAU,KAAK,MAAM;IAAE,KAAK,QAAQ;IAAK,WAAW;GAAkB,CAAC;GACpG,OAAO,mBAAmB,KAAK,aAAa,MAAM;GAClD,UAAU,OAAO,aAAa,OAAO,WAAW,QAAQ,OAAO;EACjE,OACE,QAAQ,MAAM,QAAQ,SAAS,KAAK,IAAI,EAAA,CAAG,KAAK;CAEpD,SAAS,OAAO;EACd,OAAO,YAAY,KAAK,YAAY,WAAW,aAAa,KAAK;EACjE,UAAU;CACZ;CACA,OAAO;EAAE,QAAQ,YAAY,MAAM,KAAK,WAAW;EAAG,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;CAAG;AACxF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RunScript } from './prompt.js';
|
|
2
|
+
export declare function isExecutableFile(absPath: string): boolean;
|
|
3
|
+
export declare function resolveScriptCommand(absPath: string): {
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare const runScript: RunScript;
|
|
8
|
+
//# sourceMappingURL=run-script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-script.d.ts","sourceRoot":"","sources":["../../src/schedules/run-script.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAgB,MAAM,aAAa,CAAC;AAc3D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOzD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAOzF;AAED,eAAO,MAAM,SAAS,EAAE,SAyBvB,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as path$1 from "path";
|
|
2
|
+
import { execFile } from "child_process";
|
|
3
|
+
import * as fs$1 from "fs";
|
|
4
|
+
//#region src/schedules/run-script.ts
|
|
5
|
+
/**
|
|
6
|
+
* Runs a schedule's script file at fire time.
|
|
7
|
+
*
|
|
8
|
+
* The file path is always passed as the literal command (or as the single
|
|
9
|
+
* argument to a fixed interpreter) — never through a shell string — so the
|
|
10
|
+
* user's extra prompt can never be interpolated into a command line.
|
|
11
|
+
*/
|
|
12
|
+
/** Interpreter for scripts that lack an executable bit, keyed by extension. */
|
|
13
|
+
const INTERPRETERS = {
|
|
14
|
+
".sh": () => "/bin/sh",
|
|
15
|
+
".js": () => process.execPath,
|
|
16
|
+
".mjs": () => process.execPath,
|
|
17
|
+
".cjs": () => process.execPath,
|
|
18
|
+
".ts": () => process.execPath,
|
|
19
|
+
".py": () => "python3"
|
|
20
|
+
};
|
|
21
|
+
const MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
22
|
+
function isExecutableFile(absPath) {
|
|
23
|
+
try {
|
|
24
|
+
fs$1.accessSync(absPath, fs$1.constants.X_OK);
|
|
25
|
+
return fs$1.statSync(absPath).isFile();
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function resolveScriptCommand(absPath) {
|
|
31
|
+
if (isExecutableFile(absPath)) return {
|
|
32
|
+
command: absPath,
|
|
33
|
+
args: []
|
|
34
|
+
};
|
|
35
|
+
const interpreter = INTERPRETERS[path$1.extname(absPath).toLowerCase()];
|
|
36
|
+
if (!interpreter) throw new Error(`${absPath} is not executable and has no known interpreter`);
|
|
37
|
+
return {
|
|
38
|
+
command: interpreter(),
|
|
39
|
+
args: [absPath]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const runScript = async (absPath, { cwd, timeoutMs }) => {
|
|
43
|
+
const { command, args } = resolveScriptCommand(absPath);
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
execFile(command, args, {
|
|
46
|
+
cwd,
|
|
47
|
+
timeout: timeoutMs,
|
|
48
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
49
|
+
windowsHide: true
|
|
50
|
+
}, (error, stdout, stderr) => {
|
|
51
|
+
if (!error) {
|
|
52
|
+
resolve({
|
|
53
|
+
stdout,
|
|
54
|
+
stderr,
|
|
55
|
+
exitCode: 0
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const spawnError = error;
|
|
60
|
+
if (typeof spawnError.code === "number") {
|
|
61
|
+
resolve({
|
|
62
|
+
stdout,
|
|
63
|
+
stderr,
|
|
64
|
+
exitCode: spawnError.code
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (spawnError.killed) {
|
|
69
|
+
resolve({
|
|
70
|
+
stdout,
|
|
71
|
+
stderr: `${stderr}\n[timed out after ${timeoutMs}ms]`.trim(),
|
|
72
|
+
exitCode: null
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
reject(error);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
//#endregion
|
|
81
|
+
export { isExecutableFile, resolveScriptCommand, runScript };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=run-script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-script.js","names":["fs","path"],"sources":["../../src/schedules/run-script.ts"],"sourcesContent":["/**\n * Runs a schedule's script file at fire time.\n *\n * The file path is always passed as the literal command (or as the single\n * argument to a fixed interpreter) — never through a shell string — so the\n * user's extra prompt can never be interpolated into a command line.\n */\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { RunScript, ScriptResult } from './prompt.js';\n\n/** Interpreter for scripts that lack an executable bit, keyed by extension. */\nconst INTERPRETERS: Record<string, () => string> = {\n '.sh': () => '/bin/sh',\n '.js': () => process.execPath,\n '.mjs': () => process.execPath,\n '.cjs': () => process.execPath,\n '.ts': () => process.execPath,\n '.py': () => 'python3',\n};\n\nconst MAX_OUTPUT_BYTES = 1024 * 1024;\n\nexport function isExecutableFile(absPath: string): boolean {\n try {\n fs.accessSync(absPath, fs.constants.X_OK);\n return fs.statSync(absPath).isFile();\n } catch {\n return false;\n }\n}\n\nexport function resolveScriptCommand(absPath: string): { command: string; args: string[] } {\n if (isExecutableFile(absPath)) return { command: absPath, args: [] };\n const interpreter = INTERPRETERS[path.extname(absPath).toLowerCase()];\n if (!interpreter) {\n throw new Error(`${absPath} is not executable and has no known interpreter`);\n }\n return { command: interpreter(), args: [absPath] };\n}\n\nexport const runScript: RunScript = async (absPath, { cwd, timeoutMs }) => {\n const { command, args } = resolveScriptCommand(absPath);\n return new Promise<ScriptResult>((resolve, reject) => {\n execFile(\n command,\n args,\n { cwd, timeout: timeoutMs, maxBuffer: MAX_OUTPUT_BYTES, windowsHide: true },\n (error, stdout, stderr) => {\n if (!error) {\n resolve({ stdout, stderr, exitCode: 0 });\n return;\n }\n const spawnError = error as NodeJS.ErrnoException & { code?: number | string; killed?: boolean };\n if (typeof spawnError.code === 'number') {\n resolve({ stdout, stderr, exitCode: spawnError.code });\n return;\n }\n if (spawnError.killed) {\n resolve({ stdout, stderr: `${stderr}\\n[timed out after ${timeoutMs}ms]`.trim(), exitCode: null });\n return;\n }\n reject(error);\n },\n );\n });\n};\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,eAA6C;CACjD,aAAa;CACb,aAAa,QAAQ;CACrB,cAAc,QAAQ;CACtB,cAAc,QAAQ;CACtB,aAAa,QAAQ;CACrB,aAAa;AACf;AAEA,MAAM,mBAAmB,OAAO;AAEhC,SAAgB,iBAAiB,SAA0B;CACzD,IAAI;EACF,KAAG,WAAW,SAASA,KAAG,UAAU,IAAI;EACxC,OAAOA,KAAG,SAAS,OAAO,CAAC,CAAC,OAAO;CACrC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,qBAAqB,SAAsD;CACzF,IAAI,iBAAiB,OAAO,GAAG,OAAO;EAAE,SAAS;EAAS,MAAM,CAAC;CAAE;CACnE,MAAM,cAAc,aAAaC,OAAK,QAAQ,OAAO,CAAC,CAAC,YAAY;CACnE,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,GAAG,QAAQ,gDAAgD;CAE7E,OAAO;EAAE,SAAS,YAAY;EAAG,MAAM,CAAC,OAAO;CAAE;AACnD;AAEA,MAAa,YAAuB,OAAO,SAAS,EAAE,KAAK,gBAAgB;CACzE,MAAM,EAAE,SAAS,SAAS,qBAAqB,OAAO;CACtD,OAAO,IAAI,SAAuB,SAAS,WAAW;EACpD,SACE,SACA,MACA;GAAE;GAAK,SAAS;GAAW,WAAW;GAAkB,aAAa;EAAK,IACzE,OAAO,QAAQ,WAAW;GACzB,IAAI,CAAC,OAAO;IACV,QAAQ;KAAE;KAAQ;KAAQ,UAAU;IAAE,CAAC;IACvC;GACF;GACA,MAAM,aAAa;GACnB,IAAI,OAAO,WAAW,SAAS,UAAU;IACvC,QAAQ;KAAE;KAAQ;KAAQ,UAAU,WAAW;IAAK,CAAC;IACrD;GACF;GACA,IAAI,WAAW,QAAQ;IACrB,QAAQ;KAAE;KAAQ,QAAQ,GAAG,OAAO,qBAAqB,UAAU,KAAK,KAAK;KAAG,UAAU;IAAK,CAAC;IAChG;GACF;GACA,OAAO,KAAK;EACd,CACF;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ParsedInterval } from './interval.js';
|
|
2
|
+
import type { AssembledPrompt } from './prompt.js';
|
|
3
|
+
/** `source` attribute on fired signals; the TUI labels these turns `schedule`. */
|
|
4
|
+
export declare const SCHEDULE_SIGNAL_SOURCE = "schedule";
|
|
5
|
+
/** When a schedule fires. Only recurring `every` triggers exist today. */
|
|
6
|
+
export type ScheduleTrigger = {
|
|
7
|
+
kind: 'every';
|
|
8
|
+
interval: ParsedInterval;
|
|
9
|
+
};
|
|
10
|
+
/** A file a schedule runs as a script, or re-reads and sends as the prompt, on every fire. */
|
|
11
|
+
export type ScheduleFile = {
|
|
12
|
+
path: string;
|
|
13
|
+
displayPath: string;
|
|
14
|
+
mode: 'script' | 'prompt';
|
|
15
|
+
};
|
|
16
|
+
/** What to send on each fire: `prompt` text, or a `file` (plus `extraPrompt` appended after it). */
|
|
17
|
+
export type ThreadScheduleSource = {
|
|
18
|
+
prompt: string;
|
|
19
|
+
} | {
|
|
20
|
+
file: ScheduleFile;
|
|
21
|
+
extraPrompt?: string;
|
|
22
|
+
};
|
|
23
|
+
export type ThreadScheduleSpec = ThreadScheduleSource & {
|
|
24
|
+
trigger: ScheduleTrigger;
|
|
25
|
+
/** Who is creating it: the user via `/schedules`, or the agent via its schedule tools. Defaults to `user`. */
|
|
26
|
+
createdBy?: 'user' | 'agent';
|
|
27
|
+
};
|
|
28
|
+
export type ThreadSchedule = {
|
|
29
|
+
id: string;
|
|
30
|
+
threadId: string;
|
|
31
|
+
resourceId: string;
|
|
32
|
+
trigger: ScheduleTrigger;
|
|
33
|
+
prompt?: string;
|
|
34
|
+
file?: ScheduleFile;
|
|
35
|
+
extraPrompt?: string;
|
|
36
|
+
createdBy: 'user' | 'agent';
|
|
37
|
+
status: 'active' | 'paused';
|
|
38
|
+
/** Next boundary this schedule fires at; only meaningful while active. */
|
|
39
|
+
nextFireAt: number;
|
|
40
|
+
createdAt: number;
|
|
41
|
+
};
|
|
42
|
+
export type ThreadSchedulerOptions = {
|
|
43
|
+
/** Build the prompt for one fire (runs scripts / reads files). */
|
|
44
|
+
assemblePrompt: (schedule: ThreadSchedule) => Promise<AssembledPrompt>;
|
|
45
|
+
/** Hand the prompt to the agent for the schedule's thread. */
|
|
46
|
+
deliver: (schedule: ThreadSchedule, assembled: AssembledPrompt) => Promise<void>;
|
|
47
|
+
/** A fire failed before reaching the agent. */
|
|
48
|
+
onError?: (error: unknown, schedule: ThreadSchedule) => void;
|
|
49
|
+
now?: () => number;
|
|
50
|
+
};
|
|
51
|
+
/** How late a timer may run before its fire counts as missed (sleep, stalled event loop). */
|
|
52
|
+
export declare const LATE_FIRE_GRACE_MS = 60000;
|
|
53
|
+
/**
|
|
54
|
+
* Outcome of a manual `run()`. `failed` means nothing reached the agent: the
|
|
55
|
+
* prompt could not be built or the agent refused it.
|
|
56
|
+
*/
|
|
57
|
+
export type ScheduleRunResult = {
|
|
58
|
+
status: 'fired';
|
|
59
|
+
} | {
|
|
60
|
+
status: 'failed';
|
|
61
|
+
error: unknown;
|
|
62
|
+
} | {
|
|
63
|
+
status: 'busy';
|
|
64
|
+
} | {
|
|
65
|
+
status: 'not-found';
|
|
66
|
+
};
|
|
67
|
+
export declare class ThreadScheduler {
|
|
68
|
+
#private;
|
|
69
|
+
constructor(options: ThreadSchedulerOptions);
|
|
70
|
+
/** Throws when the interval is not a supported wall-clock cadence (see `validateInterval`) or the prompt is empty. */
|
|
71
|
+
create(spec: ThreadScheduleSpec, target: {
|
|
72
|
+
threadId: string;
|
|
73
|
+
resourceId: string;
|
|
74
|
+
}): ThreadSchedule;
|
|
75
|
+
list(filter?: {
|
|
76
|
+
threadId?: string;
|
|
77
|
+
}): ThreadSchedule[];
|
|
78
|
+
delete(id: string): boolean;
|
|
79
|
+
pause(id: string): boolean;
|
|
80
|
+
resume(id: string): boolean;
|
|
81
|
+
/** Whether a fire of this schedule (timer or manual) is still assembling or delivering. */
|
|
82
|
+
isFiring(id: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Fire once now, outside the cadence. Returns `busy` without firing when a
|
|
85
|
+
* fire of this schedule is still running (a slow script, say), so the same
|
|
86
|
+
* script never runs twice at once. Resolves once the fire finished; a fire
|
|
87
|
+
* that never reached the agent resolves `failed` instead of calling `onError`.
|
|
88
|
+
*/
|
|
89
|
+
run(id: string): Promise<ScheduleRunResult>;
|
|
90
|
+
/** Drop every schedule and timer (process shutdown). */
|
|
91
|
+
stop(): void;
|
|
92
|
+
}
|
|
93
|
+
export declare function shortScheduleId(id: string): string;
|
|
94
|
+
/** One-line human summary of what a schedule sends: `run ./check.sh + "extra"`, `read notes.md`, `"prompt"`. */
|
|
95
|
+
export declare function describeScheduleSource(schedule: Pick<ThreadSchedule, 'prompt' | 'file' | 'extraPrompt'>): string;
|
|
96
|
+
/**
|
|
97
|
+
* Attributes on a fired schedule signal. They carry everything the transcript
|
|
98
|
+
* header needs, so reloaded history renders without the process-local entry.
|
|
99
|
+
*/
|
|
100
|
+
export declare function scheduleSignalAttributes(schedule: ThreadSchedule, assembled?: Pick<AssembledPrompt, 'outcome'>): Record<string, string>;
|
|
101
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/schedules/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { nextFireTime, validateInterval } from "./interval.js";
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
//#region src/schedules/scheduler.ts
|
|
4
|
+
/**
|
|
5
|
+
* Process-local scheduler behind `/schedules`.
|
|
6
|
+
*
|
|
7
|
+
* Schedules live only in this process: timers fire them and they die with it.
|
|
8
|
+
* Nothing is written to shared storage, so other Mastra Code processes on the
|
|
9
|
+
* same database never see — or fire — this session's schedules.
|
|
10
|
+
*/
|
|
11
|
+
/** `source` attribute on fired signals; the TUI labels these turns `schedule`. */
|
|
12
|
+
const SCHEDULE_SIGNAL_SOURCE = "schedule";
|
|
13
|
+
/** How late a timer may run before its fire counts as missed (sleep, stalled event loop). */
|
|
14
|
+
const LATE_FIRE_GRACE_MS = 6e4;
|
|
15
|
+
var ThreadScheduler = class {
|
|
16
|
+
#entries = /* @__PURE__ */ new Map();
|
|
17
|
+
#options;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.#options = options;
|
|
20
|
+
}
|
|
21
|
+
#now() {
|
|
22
|
+
return this.#options.now?.() ?? Date.now();
|
|
23
|
+
}
|
|
24
|
+
/** Throws when the interval is not a supported wall-clock cadence (see `validateInterval`) or the prompt is empty. */
|
|
25
|
+
create(spec, target) {
|
|
26
|
+
const { interval } = spec.trigger;
|
|
27
|
+
const check = validateInterval(interval);
|
|
28
|
+
if ("error" in check) throw new Error(`Invalid schedule interval: ${check.error}${check.suggestion ? ` Try ${check.suggestion}.` : ""}`);
|
|
29
|
+
if ("prompt" in spec && !spec.prompt.trim()) throw new Error("Schedule prompt is empty.");
|
|
30
|
+
const now = this.#now();
|
|
31
|
+
const schedule = {
|
|
32
|
+
id: randomUUID(),
|
|
33
|
+
threadId: target.threadId,
|
|
34
|
+
resourceId: target.resourceId,
|
|
35
|
+
trigger: {
|
|
36
|
+
kind: "every",
|
|
37
|
+
interval: { ...interval }
|
|
38
|
+
},
|
|
39
|
+
..."file" in spec ? {
|
|
40
|
+
file: { ...spec.file },
|
|
41
|
+
...spec.extraPrompt ? { extraPrompt: spec.extraPrompt } : {}
|
|
42
|
+
} : { prompt: spec.prompt },
|
|
43
|
+
createdBy: spec.createdBy ?? "user",
|
|
44
|
+
status: "active",
|
|
45
|
+
nextFireAt: nextFireTime(interval.ms, now),
|
|
46
|
+
createdAt: now
|
|
47
|
+
};
|
|
48
|
+
const entry = {
|
|
49
|
+
schedule,
|
|
50
|
+
inFlight: 0
|
|
51
|
+
};
|
|
52
|
+
this.#entries.set(schedule.id, entry);
|
|
53
|
+
this.#arm(entry);
|
|
54
|
+
return { ...schedule };
|
|
55
|
+
}
|
|
56
|
+
list(filter = {}) {
|
|
57
|
+
return [...this.#entries.values()].map((entry) => ({ ...entry.schedule })).filter((schedule) => !filter.threadId || schedule.threadId === filter.threadId).sort((a, b) => a.createdAt - b.createdAt);
|
|
58
|
+
}
|
|
59
|
+
delete(id) {
|
|
60
|
+
const entry = this.#entries.get(id);
|
|
61
|
+
if (!entry) return false;
|
|
62
|
+
clearTimeout(entry.timer);
|
|
63
|
+
this.#entries.delete(id);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
pause(id) {
|
|
67
|
+
const entry = this.#entries.get(id);
|
|
68
|
+
if (!entry) return false;
|
|
69
|
+
clearTimeout(entry.timer);
|
|
70
|
+
entry.timer = void 0;
|
|
71
|
+
entry.schedule.status = "paused";
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
resume(id) {
|
|
75
|
+
const entry = this.#entries.get(id);
|
|
76
|
+
if (!entry) return false;
|
|
77
|
+
if (entry.schedule.status === "active") return true;
|
|
78
|
+
entry.schedule.status = "active";
|
|
79
|
+
entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, this.#now());
|
|
80
|
+
this.#arm(entry);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/** Whether a fire of this schedule (timer or manual) is still assembling or delivering. */
|
|
84
|
+
isFiring(id) {
|
|
85
|
+
return (this.#entries.get(id)?.inFlight ?? 0) > 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Fire once now, outside the cadence. Returns `busy` without firing when a
|
|
89
|
+
* fire of this schedule is still running (a slow script, say), so the same
|
|
90
|
+
* script never runs twice at once. Resolves once the fire finished; a fire
|
|
91
|
+
* that never reached the agent resolves `failed` instead of calling `onError`.
|
|
92
|
+
*/
|
|
93
|
+
async run(id) {
|
|
94
|
+
const entry = this.#entries.get(id);
|
|
95
|
+
if (!entry) return { status: "not-found" };
|
|
96
|
+
if (entry.inFlight > 0) return { status: "busy" };
|
|
97
|
+
const result = await this.#fire(entry);
|
|
98
|
+
if (result.kind === "failed") return {
|
|
99
|
+
status: "failed",
|
|
100
|
+
error: result.error
|
|
101
|
+
};
|
|
102
|
+
return result.kind === "delivered" ? { status: "fired" } : { status: "not-found" };
|
|
103
|
+
}
|
|
104
|
+
/** Drop every schedule and timer (process shutdown). */
|
|
105
|
+
stop() {
|
|
106
|
+
for (const entry of this.#entries.values()) clearTimeout(entry.timer);
|
|
107
|
+
this.#entries.clear();
|
|
108
|
+
}
|
|
109
|
+
#arm(entry) {
|
|
110
|
+
clearTimeout(entry.timer);
|
|
111
|
+
const scheduledAt = entry.schedule.nextFireAt;
|
|
112
|
+
entry.timer = setTimeout(() => {
|
|
113
|
+
if (this.#entries.get(entry.schedule.id) !== entry || entry.schedule.status !== "active") return;
|
|
114
|
+
const now = this.#now();
|
|
115
|
+
entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, Math.max(now, scheduledAt));
|
|
116
|
+
this.#arm(entry);
|
|
117
|
+
if (now - scheduledAt > 6e4) return;
|
|
118
|
+
if (entry.inFlight === 0) this.#fire(entry).then((result) => {
|
|
119
|
+
if (result.kind === "failed") this.#options.onError?.(result.error, { ...entry.schedule });
|
|
120
|
+
});
|
|
121
|
+
}, Math.max(0, scheduledAt - this.#now()));
|
|
122
|
+
entry.timer.unref?.();
|
|
123
|
+
}
|
|
124
|
+
async #fire(entry) {
|
|
125
|
+
const schedule = { ...entry.schedule };
|
|
126
|
+
entry.inFlight++;
|
|
127
|
+
try {
|
|
128
|
+
const assembled = await this.#options.assemblePrompt(schedule);
|
|
129
|
+
if (this.#entries.get(schedule.id) !== entry) return { kind: "deleted" };
|
|
130
|
+
await this.#options.deliver(schedule, assembled);
|
|
131
|
+
return { kind: "delivered" };
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return {
|
|
134
|
+
kind: "failed",
|
|
135
|
+
error
|
|
136
|
+
};
|
|
137
|
+
} finally {
|
|
138
|
+
entry.inFlight--;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
function shortScheduleId(id) {
|
|
143
|
+
return id.slice(0, 8);
|
|
144
|
+
}
|
|
145
|
+
/** One-line human summary of what a schedule sends: `run ./check.sh + "extra"`, `read notes.md`, `"prompt"`. */
|
|
146
|
+
function describeScheduleSource(schedule) {
|
|
147
|
+
if (!schedule.file) return `"${schedule.prompt ?? ""}"`;
|
|
148
|
+
const verb = schedule.file.mode === "script" ? "run" : "read";
|
|
149
|
+
const extra = schedule.extraPrompt ? ` + "${schedule.extraPrompt}"` : "";
|
|
150
|
+
return `${verb} ${schedule.file.displayPath}${extra}`;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Attributes on a fired schedule signal. They carry everything the transcript
|
|
154
|
+
* header needs, so reloaded history renders without the process-local entry.
|
|
155
|
+
*/
|
|
156
|
+
function scheduleSignalAttributes(schedule, assembled = {}) {
|
|
157
|
+
return {
|
|
158
|
+
source: SCHEDULE_SIGNAL_SOURCE,
|
|
159
|
+
scheduleId: schedule.id,
|
|
160
|
+
scheduleCadence: schedule.trigger.interval.label,
|
|
161
|
+
scheduleSource: describeScheduleSource(schedule),
|
|
162
|
+
scheduleCreatedBy: schedule.createdBy,
|
|
163
|
+
...assembled.outcome ? { scheduleOutcome: assembled.outcome } : {}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
export { LATE_FIRE_GRACE_MS, SCHEDULE_SIGNAL_SOURCE, ThreadScheduler, describeScheduleSource, scheduleSignalAttributes, shortScheduleId };
|
|
168
|
+
|
|
169
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","names":["#options","#now","#entries","#arm","#fire"],"sources":["../../src/schedules/scheduler.ts"],"sourcesContent":["/**\n * Process-local scheduler behind `/schedules`.\n *\n * Schedules live only in this process: timers fire them and they die with it.\n * Nothing is written to shared storage, so other Mastra Code processes on the\n * same database never see — or fire — this session's schedules.\n */\nimport { randomUUID } from 'node:crypto';\nimport { nextFireTime, validateInterval } from './interval.js';\nimport type { ParsedInterval } from './interval.js';\nimport type { AssembledPrompt } from './prompt.js';\n\n/** `source` attribute on fired signals; the TUI labels these turns `schedule`. */\nexport const SCHEDULE_SIGNAL_SOURCE = 'schedule';\n\n/** When a schedule fires. Only recurring `every` triggers exist today. */\nexport type ScheduleTrigger = { kind: 'every'; interval: ParsedInterval };\n\n/** A file a schedule runs as a script, or re-reads and sends as the prompt, on every fire. */\nexport type ScheduleFile = { path: string; displayPath: string; mode: 'script' | 'prompt' };\n\n/** What to send on each fire: `prompt` text, or a `file` (plus `extraPrompt` appended after it). */\nexport type ThreadScheduleSource = { prompt: string } | { file: ScheduleFile; extraPrompt?: string };\n\nexport type ThreadScheduleSpec = ThreadScheduleSource & {\n trigger: ScheduleTrigger;\n /** Who is creating it: the user via `/schedules`, or the agent via its schedule tools. Defaults to `user`. */\n createdBy?: 'user' | 'agent';\n};\n\nexport type ThreadSchedule = {\n id: string;\n threadId: string;\n resourceId: string;\n trigger: ScheduleTrigger;\n prompt?: string;\n file?: ScheduleFile;\n extraPrompt?: string;\n createdBy: 'user' | 'agent';\n status: 'active' | 'paused';\n /** Next boundary this schedule fires at; only meaningful while active. */\n nextFireAt: number;\n createdAt: number;\n};\n\nexport type ThreadSchedulerOptions = {\n /** Build the prompt for one fire (runs scripts / reads files). */\n assemblePrompt: (schedule: ThreadSchedule) => Promise<AssembledPrompt>;\n /** Hand the prompt to the agent for the schedule's thread. */\n deliver: (schedule: ThreadSchedule, assembled: AssembledPrompt) => Promise<void>;\n /** A fire failed before reaching the agent. */\n onError?: (error: unknown, schedule: ThreadSchedule) => void;\n now?: () => number;\n};\n\n/** How late a timer may run before its fire counts as missed (sleep, stalled event loop). */\nexport const LATE_FIRE_GRACE_MS = 60_000;\n\n/**\n * Outcome of a manual `run()`. `failed` means nothing reached the agent: the\n * prompt could not be built or the agent refused it.\n */\nexport type ScheduleRunResult =\n | { status: 'fired' }\n | { status: 'failed'; error: unknown }\n | { status: 'busy' }\n | { status: 'not-found' };\n\ntype FireOutcome = { kind: 'delivered' } | { kind: 'deleted' } | { kind: 'failed'; error: unknown };\n\ntype Entry = { schedule: ThreadSchedule; timer?: ReturnType<typeof setTimeout>; inFlight: number };\n\nexport class ThreadScheduler {\n #entries = new Map<string, Entry>();\n #options: ThreadSchedulerOptions;\n\n constructor(options: ThreadSchedulerOptions) {\n this.#options = options;\n }\n\n #now(): number {\n return this.#options.now?.() ?? Date.now();\n }\n\n /** Throws when the interval is not a supported wall-clock cadence (see `validateInterval`) or the prompt is empty. */\n create(spec: ThreadScheduleSpec, target: { threadId: string; resourceId: string }): ThreadSchedule {\n const { interval } = spec.trigger;\n const check = validateInterval(interval);\n if ('error' in check) {\n throw new Error(\n `Invalid schedule interval: ${check.error}${check.suggestion ? ` Try ${check.suggestion}.` : ''}`,\n );\n }\n if ('prompt' in spec && !spec.prompt.trim()) throw new Error('Schedule prompt is empty.');\n const now = this.#now();\n const schedule: ThreadSchedule = {\n id: randomUUID(),\n threadId: target.threadId,\n resourceId: target.resourceId,\n trigger: { kind: 'every', interval: { ...interval } },\n ...('file' in spec\n ? { file: { ...spec.file }, ...(spec.extraPrompt ? { extraPrompt: spec.extraPrompt } : {}) }\n : { prompt: spec.prompt }),\n createdBy: spec.createdBy ?? 'user',\n status: 'active',\n nextFireAt: nextFireTime(interval.ms, now),\n createdAt: now,\n };\n const entry: Entry = { schedule, inFlight: 0 };\n this.#entries.set(schedule.id, entry);\n this.#arm(entry);\n return { ...schedule };\n }\n\n list(filter: { threadId?: string } = {}): ThreadSchedule[] {\n return [...this.#entries.values()]\n .map(entry => ({ ...entry.schedule }))\n .filter(schedule => !filter.threadId || schedule.threadId === filter.threadId)\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n delete(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n clearTimeout(entry.timer);\n this.#entries.delete(id);\n return true;\n }\n\n pause(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n clearTimeout(entry.timer);\n entry.timer = undefined;\n entry.schedule.status = 'paused';\n return true;\n }\n\n resume(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n if (entry.schedule.status === 'active') return true;\n entry.schedule.status = 'active';\n entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, this.#now());\n this.#arm(entry);\n return true;\n }\n\n /** Whether a fire of this schedule (timer or manual) is still assembling or delivering. */\n isFiring(id: string): boolean {\n return (this.#entries.get(id)?.inFlight ?? 0) > 0;\n }\n\n /**\n * Fire once now, outside the cadence. Returns `busy` without firing when a\n * fire of this schedule is still running (a slow script, say), so the same\n * script never runs twice at once. Resolves once the fire finished; a fire\n * that never reached the agent resolves `failed` instead of calling `onError`.\n */\n async run(id: string): Promise<ScheduleRunResult> {\n const entry = this.#entries.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.inFlight > 0) return { status: 'busy' };\n const result = await this.#fire(entry);\n if (result.kind === 'failed') return { status: 'failed', error: result.error };\n return result.kind === 'delivered' ? { status: 'fired' } : { status: 'not-found' };\n }\n\n /** Drop every schedule and timer (process shutdown). */\n stop(): void {\n for (const entry of this.#entries.values()) clearTimeout(entry.timer);\n this.#entries.clear();\n }\n\n #arm(entry: Entry): void {\n clearTimeout(entry.timer);\n const scheduledAt = entry.schedule.nextFireAt;\n entry.timer = setTimeout(\n () => {\n if (this.#entries.get(entry.schedule.id) !== entry || entry.schedule.status !== 'active') return;\n // Timers can fire a hair early; step from the later of now and the\n // intended boundary so the same boundary is never fired twice.\n const now = this.#now();\n entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, Math.max(now, scheduledAt));\n this.#arm(entry);\n // A timer that ran long after its boundary (machine asleep) is a missed fire, not a late one.\n if (now - scheduledAt > LATE_FIRE_GRACE_MS) return;\n // A slow script can outlast a short interval; don't stack fires.\n if (entry.inFlight === 0) {\n void this.#fire(entry).then(result => {\n if (result.kind === 'failed') this.#options.onError?.(result.error, { ...entry.schedule });\n });\n }\n },\n Math.max(0, scheduledAt - this.#now()),\n );\n entry.timer.unref?.();\n }\n\n async #fire(entry: Entry): Promise<FireOutcome> {\n const schedule = { ...entry.schedule };\n entry.inFlight++;\n try {\n const assembled = await this.#options.assemblePrompt(schedule);\n // Deleted while its prompt was being built: drop the fire quietly.\n if (this.#entries.get(schedule.id) !== entry) return { kind: 'deleted' };\n await this.#options.deliver(schedule, assembled);\n return { kind: 'delivered' };\n } catch (error) {\n return { kind: 'failed', error };\n } finally {\n entry.inFlight--;\n }\n }\n}\n\nexport function shortScheduleId(id: string): string {\n return id.slice(0, 8);\n}\n\n/** One-line human summary of what a schedule sends: `run ./check.sh + \"extra\"`, `read notes.md`, `\"prompt\"`. */\nexport function describeScheduleSource(schedule: Pick<ThreadSchedule, 'prompt' | 'file' | 'extraPrompt'>): string {\n if (!schedule.file) return `\"${schedule.prompt ?? ''}\"`;\n const verb = schedule.file.mode === 'script' ? 'run' : 'read';\n const extra = schedule.extraPrompt ? ` + \"${schedule.extraPrompt}\"` : '';\n return `${verb} ${schedule.file.displayPath}${extra}`;\n}\n\n/**\n * Attributes on a fired schedule signal. They carry everything the transcript\n * header needs, so reloaded history renders without the process-local entry.\n */\nexport function scheduleSignalAttributes(\n schedule: ThreadSchedule,\n assembled: Pick<AssembledPrompt, 'outcome'> = {},\n): Record<string, string> {\n return {\n source: SCHEDULE_SIGNAL_SOURCE,\n scheduleId: schedule.id,\n scheduleCadence: schedule.trigger.interval.label,\n scheduleSource: describeScheduleSource(schedule),\n scheduleCreatedBy: schedule.createdBy,\n ...(assembled.outcome ? { scheduleOutcome: assembled.outcome } : {}),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ThreadScheduler } from './scheduler.js';
|
|
2
|
+
export declare const SCHEDULE_TOOL_IDS: {
|
|
3
|
+
readonly create: "schedule_create";
|
|
4
|
+
readonly list: "schedule_list";
|
|
5
|
+
readonly update: "schedule_update";
|
|
6
|
+
readonly resume: "schedule_resume";
|
|
7
|
+
readonly run: "schedule_run";
|
|
8
|
+
};
|
|
9
|
+
export type ScheduleFileOptions = {
|
|
10
|
+
/** Directory relative paths resolve against (the project root). */
|
|
11
|
+
cwd: string;
|
|
12
|
+
homeDir?: string;
|
|
13
|
+
fileExists: (absPath: string) => boolean;
|
|
14
|
+
};
|
|
15
|
+
export type ScheduleToolsOptions = {
|
|
16
|
+
scheduler: ThreadScheduler;
|
|
17
|
+
fileOptions: () => ScheduleFileOptions;
|
|
18
|
+
};
|
|
19
|
+
export declare function createScheduleTools({ scheduler, fileOptions }: ScheduleToolsOptions): {
|
|
20
|
+
schedule_create: import("@mastra/core/tools").Tool<{
|
|
21
|
+
trigger: {
|
|
22
|
+
kind: "every";
|
|
23
|
+
interval: import("./interval.js").ParsedInterval;
|
|
24
|
+
};
|
|
25
|
+
prompt?: string | undefined;
|
|
26
|
+
promptFile?: string | undefined;
|
|
27
|
+
script?: string | undefined;
|
|
28
|
+
extraPrompt?: string | undefined;
|
|
29
|
+
}, {
|
|
30
|
+
content: string;
|
|
31
|
+
isError?: boolean | undefined;
|
|
32
|
+
}, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "schedule_create", unknown>;
|
|
33
|
+
schedule_list: import("@mastra/core/tools").Tool<Record<string, never>, {
|
|
34
|
+
content: string;
|
|
35
|
+
isError?: boolean | undefined;
|
|
36
|
+
}, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "schedule_list", unknown>;
|
|
37
|
+
schedule_update: import("@mastra/core/tools").Tool<{
|
|
38
|
+
id: string;
|
|
39
|
+
action: "pause" | "delete";
|
|
40
|
+
}, {
|
|
41
|
+
content: string;
|
|
42
|
+
isError?: boolean | undefined;
|
|
43
|
+
}, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "schedule_update", unknown>;
|
|
44
|
+
schedule_resume: import("@mastra/core/tools").Tool<{
|
|
45
|
+
id: string;
|
|
46
|
+
}, {
|
|
47
|
+
content: string;
|
|
48
|
+
isError?: boolean | undefined;
|
|
49
|
+
}, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "schedule_resume", unknown>;
|
|
50
|
+
schedule_run: import("@mastra/core/tools").Tool<{
|
|
51
|
+
id: string;
|
|
52
|
+
}, {
|
|
53
|
+
content: string;
|
|
54
|
+
isError?: boolean | undefined;
|
|
55
|
+
}, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "schedule_run", unknown>;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/schedules/tools.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAgC,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpF,eAAO,MAAM,iBAAiB;;;;;;CAMpB,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG;IAChC,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,eAAe,CAAC;IAC3B,WAAW,EAAE,MAAM,mBAAmB,CAAC;CACxC,CAAC;AAiFF,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4InF"}
|