forked from admin/VolcanicAgenticCore
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.
47 lines
2.7 KiB
C#
47 lines
2.7 KiB
C#
namespace Volcanic.AgenticCore.Extensions.Scheduler;
|
|
|
|
/// <summary>The <c>Scheduler</c> configuration section. There is no zone here: zones are per target (the host's <c>IJobDelivery.TimeZoneFor</c>).</summary>
|
|
public sealed class SchedulerOptions
|
|
{
|
|
public const string SectionName = "Scheduler";
|
|
|
|
public bool Enabled { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// The retired shared <c>jobs.json</c>. Jobs are kept per chat (<see cref="JobStore.PathIn"/>); on start the jobs
|
|
/// found here move into their chats' files and this file is renamed <c>jobs.json.migrated-<timestamp></c>.
|
|
/// Empty: the host fills <see cref="DefaultLegacyStorePath"/> under its agent directory.
|
|
/// </summary>
|
|
public string? LegacyStorePath { get; set; }
|
|
|
|
/// <summary>Watcher timeout when the job sets none.</summary>
|
|
public TimeSpan DefaultScriptTimeout { get; set; } = TimeSpan.FromSeconds(300);
|
|
|
|
public int MaxJobsPerTarget { get; set; } = 50;
|
|
|
|
/// <summary>Skill jobs (<c>.volcanic/skills/<skill>/jobs/*.md</c>) scheduled per target, separate from <see cref="MaxJobsPerTarget"/>.</summary>
|
|
public int MaxSkillJobsPerTarget { get; set; } = 50;
|
|
|
|
/// <summary>How often every target's skill job files are re-read (they are also re-read around each agent run).</summary>
|
|
public TimeSpan SkillJobSyncInterval { get; set; } = TimeSpan.FromMinutes(1);
|
|
|
|
/// <summary>Executable and leading arguments; the command is appended. Empty: <c>/bin/sh -c</c>, or <c>cmd.exe /d /s /c</c> on Windows (the <c>bash</c> tool's shell).</summary>
|
|
public List<string> Shell { get; set; } = [];
|
|
|
|
/// <summary><c><agentDir>/scheduler/jobs.json</c>.</summary>
|
|
public static string DefaultLegacyStorePath(string agentDir) => Path.Combine(agentDir, "scheduler", "jobs.json");
|
|
|
|
/// <summary>Configuration problems that must stop the host, empty when valid.</summary>
|
|
public IReadOnlyList<string> Validate()
|
|
{
|
|
var errors = new List<string>();
|
|
if (DefaultScriptTimeout < TimeSpan.FromSeconds(1) || DefaultScriptTimeout > TimeSpan.FromSeconds(JobValidation.MaxTimeoutSeconds))
|
|
errors.Add("Scheduler:DefaultScriptTimeout must be between 1 second and 1 hour.");
|
|
if (MaxJobsPerTarget < 1) errors.Add("Scheduler:MaxJobsPerTarget must be at least 1.");
|
|
if (MaxSkillJobsPerTarget < 0) errors.Add("Scheduler:MaxSkillJobsPerTarget must not be negative.");
|
|
if (SkillJobSyncInterval < TimeSpan.FromSeconds(1)) errors.Add("Scheduler:SkillJobSyncInterval must be at least 1 second.");
|
|
if (Shell.Any(string.IsNullOrWhiteSpace)) errors.Add("Scheduler:Shell entries must not be empty.");
|
|
return errors;
|
|
}
|
|
}
|