use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "value")]
pub enum ScheduleExpression {
Once(String),
Cron(String),
Interval(u64),
Condition(ConditionScheduleConfig),
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct ConditionScheduleConfig {
#[serde(default = "default_rearm_delay", alias = "rearm_delay_minutes")]
pub rearm_delay_minutes: u32,
}
impl Default for ConditionScheduleConfig {
fn default() -> Self {
Self {
rearm_delay_minutes: default_rearm_delay(),
}
}
}
fn default_rearm_delay() -> u32 {
60
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct IdleCondition {
#[serde(alias = "enabled")]
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct RepositoryWatch {
#[serde(alias = "path")]
pub path: String,
#[serde(alias = "inactive_minutes")]
pub inactive_minutes: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct RepositoryInactiveCondition {
#[serde(alias = "enabled")]
pub enabled: bool,
#[serde(default, alias = "repositories")]
pub repositories: Vec<RepositoryWatch>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct ScheduleConditions {
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "require_idle"
)]
pub require_idle: Option<IdleCondition>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "require_repo_inactive"
)]
pub require_repo_inactive: Option<RepositoryInactiveCondition>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "timeout_minutes"
)]
pub timeout_minutes: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct ConditionStatus {
#[serde(alias = "waiting_since")]
pub waiting_since: String,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "idle_met")]
pub idle_met: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "repo_inactive_met"
)]
pub repo_inactive_met: Option<Vec<(String, bool)>>,
#[serde(default, alias = "timed_out")]
pub timed_out: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "task_type")]
pub enum ScheduledTaskType {
Workflow {
workflow_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
config_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
monitor_index: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
workflow_id: Option<String>,
},
Prompt {
prompt_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_sessions: Option<u32>,
},
AutoFix {
#[serde(default = "default_true")]
check_findings: bool,
#[serde(default)]
force_run: bool,
},
Watcher {
watcher_id: String,
},
BackgroundCapture {
#[serde(default, skip_serializing_if = "Option::is_none")]
monitor_index: Option<i32>,
#[serde(default = "default_capture_interval")]
capture_interval_secs: u64,
#[serde(default = "default_true")]
capture_on_focus_change: bool,
},
RemoteAgent {
prompt: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
working_directory: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
allowed_tools: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
model: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
mcp_connections: Vec<McpConnectionRef>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_turns: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
timeout_seconds: Option<u64>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct McpConnectionRef {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum CatchUpPolicy {
Run,
Skip,
#[default]
RunOnce,
}
fn default_catch_up_policy() -> CatchUpPolicy {
CatchUpPolicy::RunOnce
}
fn default_catch_up_grace_seconds() -> u32 {
300
}
fn default_consecutive_launch_failures() -> u32 {
0
}
fn default_launch_failure_backoff_seconds() -> u32 {
60
}
fn default_true() -> bool {
true
}
fn default_catch_up_run() -> bool {
false
}
fn default_capture_interval() -> u64 {
30
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum ScheduledTaskStatus {
#[default]
Pending,
Running,
Completed,
Failed,
LaunchFailed,
Skipped,
Cancelled,
MissedRunnerDown,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct TaskExecutionRecord {
#[serde(alias = "execution_id")]
pub execution_id: String,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "session_id")]
pub session_id: Option<String>,
#[serde(alias = "started_at")]
pub started_at: String,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "ended_at")]
pub ended_at: Option<String>,
#[serde(alias = "status")]
pub status: ScheduledTaskStatus,
#[serde(default, alias = "success")]
pub success: bool,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "error_message"
)]
pub error_message: Option<String>,
#[serde(default, alias = "triggered_auto_fix")]
pub triggered_auto_fix: bool,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "auto_fix_session_id"
)]
pub auto_fix_session_id: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "scheduled_for"
)]
pub scheduled_for: Option<String>,
#[serde(default = "default_catch_up_run", alias = "catch_up_run")]
pub catch_up_run: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct ScheduledTask {
#[serde(alias = "id")]
pub id: String,
#[serde(alias = "name")]
pub name: String,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "description"
)]
pub description: Option<String>,
#[serde(default = "default_true", alias = "enabled")]
pub enabled: bool,
#[serde(alias = "schedule")]
pub schedule: ScheduleExpression,
#[serde(alias = "task")]
pub task: ScheduledTaskType,
#[serde(default, alias = "skip_if_completed")]
pub skip_if_completed: bool,
#[serde(default, alias = "auto_fix_on_failure")]
pub auto_fix_on_failure: bool,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "success_criteria"
)]
pub success_criteria: Option<String>,
#[serde(alias = "created_at")]
pub created_at: String,
#[serde(alias = "modified_at")]
pub modified_at: String,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "last_run")]
pub last_run: Option<TaskExecutionRecord>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "next_run")]
pub next_run: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "conditions")]
pub conditions: Option<ScheduleConditions>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "condition_status"
)]
pub condition_status: Option<ConditionStatus>,
#[serde(default = "default_catch_up_policy", alias = "catch_up_policy")]
pub catch_up_policy: CatchUpPolicy,
#[serde(
default = "default_catch_up_grace_seconds",
alias = "catch_up_grace_seconds"
)]
pub catch_up_grace_seconds: u32,
#[serde(
default = "default_consecutive_launch_failures",
alias = "consecutive_launch_failures"
)]
pub consecutive_launch_failures: u32,
#[serde(
default = "default_launch_failure_backoff_seconds",
alias = "launch_failure_backoff_seconds"
)]
pub launch_failure_backoff_seconds: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct SchedulerSettings {
#[serde(default = "default_true", alias = "enabled")]
pub enabled: bool,
#[serde(default = "default_max_concurrent", alias = "max_concurrent")]
pub max_concurrent: u32,
#[serde(default, alias = "default_auto_fix_on_failure")]
pub default_auto_fix_on_failure: bool,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "timezone")]
pub timezone: Option<String>,
}
fn default_max_concurrent() -> u32 {
1
}
impl Default for SchedulerSettings {
fn default() -> Self {
Self {
enabled: true,
max_concurrent: default_max_concurrent(),
default_auto_fix_on_failure: false,
timezone: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct SchedulerStatus {
#[serde(alias = "enabled")]
pub enabled: bool,
#[serde(alias = "running_tasks")]
pub running_tasks: u32,
#[serde(alias = "pending_tasks")]
pub pending_tasks: u32,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "next_task")]
pub next_task: Option<NextTaskInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct NextTaskInfo {
#[serde(alias = "id")]
pub id: String,
#[serde(alias = "name")]
pub name: String,
#[serde(alias = "next_run")]
pub next_run: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct CreateScheduledTaskRequest {
#[serde(alias = "name")]
pub name: String,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "description"
)]
pub description: Option<String>,
#[serde(alias = "schedule")]
pub schedule: ScheduleExpression,
#[serde(alias = "task")]
pub task: ScheduledTaskType,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "skip_if_completed"
)]
pub skip_if_completed: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "auto_fix_on_failure"
)]
pub auto_fix_on_failure: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "success_criteria"
)]
pub success_criteria: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "conditions")]
pub conditions: Option<ScheduleConditions>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "catch_up_policy"
)]
pub catch_up_policy: Option<CatchUpPolicy>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "catch_up_grace_seconds"
)]
pub catch_up_grace_seconds: Option<u32>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "launch_failure_backoff_seconds"
)]
pub launch_failure_backoff_seconds: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct UpdateScheduledTaskRequest {
#[serde(default, skip_serializing_if = "Option::is_none", alias = "name")]
pub name: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "description"
)]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "enabled")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "schedule")]
pub schedule: Option<ScheduleExpression>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "task")]
pub task: Option<ScheduledTaskType>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "skip_if_completed"
)]
pub skip_if_completed: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "auto_fix_on_failure"
)]
pub auto_fix_on_failure: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "success_criteria"
)]
pub success_criteria: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "conditions")]
pub conditions: Option<ScheduleConditions>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "catch_up_policy"
)]
pub catch_up_policy: Option<CatchUpPolicy>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "catch_up_grace_seconds"
)]
pub catch_up_grace_seconds: Option<u32>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "launch_failure_backoff_seconds"
)]
pub launch_failure_backoff_seconds: Option<u32>,
}