Files
Playa/extensions/Volcanic.AgenticCore.Extensions.Scheduler/JobMessages.cs
EugeneTes 6e3241fed9 feat(scheduler): scheduled jobs inside skills (.volcanic/skills/<skill>/jobs/<job>.md)
Skill directories may now declare prompt or watcher jobs as files (cron or one-off at).
The file is the source of truth: the scheduler re-reads every chat's jobs/ every minute,
around agent runs and before /jobs and /skills, and schedules, re-plans or removes them.
Ids are <skill>/<job>; run state lives in .volcanic/scheduler/skill-jobs-state.json, never
in jobs.json. Watchers run from the skill directory with VOLCANIC_WORKSPACE and
VOLCANIC_SKILL_DIR. Broken files are shown with the reason in /jobs, /skills, schedule list
and the new <skill_jobs> prompt section; cancel answers with the file to edit. Every part of
a skill directory is optional; file reading and frontmatter parsing move to Abstractions.
2026-10-14 12:54:40 +00:00

20 lines
1.2 KiB
C#

namespace Volcanic.AgenticCore.Extensions.Scheduler;
/// <summary>The texts a fire sends (spec §4 table).</summary>
internal static class JobMessages
{
public static string Prompt(ScheduledJob job, DateTimeOffset? lateDue, TimeZoneInfo zone) =>
$"{Header(job)}\n" + (lateDue is { } due ? $"This was due at {JobFormat.DayTime(due, zone)}; the bot was offline.\n" : "") + job.Text;
public static string WatcherMatch(ScheduledJob job, string output) => $"{Header(job)} The watcher script printed:\n{output}";
public static string Failure(ScheduledJob job, JobResult result) =>
$"⚠️ Job {Label(job)} failed ({JobFormat.FailureReason(result)}):\n```\n{(string.IsNullOrWhiteSpace(result.StderrTail) ? "(no output on stderr)" : result.StderrTail)}\n```";
private static string Header(ScheduledJob job) => $"[Scheduled job {Label(job)}]";
/// <summary><c>"Name" (id)</c>; a skill job: <c>"skill/job" (skill job, .volcanic/skills/skill/jobs/job.md)</c>, so the agent knows which file to edit.</summary>
private static string Label(ScheduledJob job) =>
job.Skill is { } skill ? $"\"{job.Id}\" (skill job, {skill.File})" : $"\"{job.Name}\" ({job.Id})";
}