use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ComponentState {
Disabled,
Starting,
Connected,
Disconnected,
Backoff,
Stopping,
Stopped,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ComponentHealth {
pub id: String,
#[serde(default)]
pub channel: String,
pub account: String,
pub bot_id: Option<String>,
pub user_id: Option<String>,
pub enabled: bool,
pub state: ComponentState,
pub last_success_unix_seconds: Option<u64>,
pub error: Option<String>,
pub restarts: u64,
#[serde(default)]
pub remote_tools: RemoteTools,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub senders: Option<Senders>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub purpose: Option<Purpose>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mail: Option<MailCounts>,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Purpose {
#[default]
Chat,
Mail,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default)]
pub struct MailCounts {
pub claimed: u64,
pub queued: u64,
pub seen_today: u64,
pub triaged_today: u64,
pub reported_today: u64,
pub tokens_today: u64,
pub token_budget: u64,
pub messages_24h: u64,
pub last_check_unix_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub actions: Vec<String>,
#[serde(skip_serializing_if = "is_zero")]
pub actions_open: u64,
#[serde(skip_serializing_if = "is_zero")]
pub actions_executing: u64,
#[serde(skip_serializing_if = "is_zero")]
pub actions_unknown: u64,
#[serde(skip_serializing_if = "is_zero")]
pub actions_done_24h: u64,
}
#[allow(
clippy::trivially_copy_pass_by_ref,
reason = "serde's skip_serializing_if passes a reference"
)]
fn is_zero(value: &u64) -> bool {
*value == 0
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct MailAction {
pub account: String,
pub id: String,
pub kind: String,
pub state: String,
pub created_unix_seconds: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expires_unix_seconds: Option<u64>,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RemoteTools {
#[default]
None,
Owner,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Senders {
#[default]
Owner,
Anyone,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DaemonStatus {
pub version: String,
pub pid: u32,
pub components: Vec<ComponentHealth>,
#[serde(default)]
pub delegations: DelegationSummary,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub restart: Option<RestartInfo>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confirm: Option<ConfirmInfo>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub project: Option<ProjectResponse>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mail_actions: Vec<MailAction>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mail_note: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ProjectPhase {
Discovery,
Planning,
Implementation,
Verification,
Staging,
Production,
Reporting,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ProjectStatus {
Active,
Blocked,
WaitingApproval,
Completed,
Failed,
Archived,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ProjectTaskStatus {
Pending,
Ready,
Running,
Blocked,
Done,
Failed,
Stale,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ProjectRunStatus {
Running,
Succeeded,
Failed,
Cancelled,
Stale,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProjectSummary {
pub id: String,
pub name: String,
pub workspace: String,
pub phase: ProjectPhase,
pub status: ProjectStatus,
pub created_unix_seconds: u64,
pub updated_unix_seconds: u64,
pub task_count: u64,
pub completed_tasks: u64,
pub last_heartbeat_unix_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProjectTask {
pub id: String,
pub title: String,
pub status: ProjectTaskStatus,
#[serde(default)]
pub depends_on: Vec<String>,
pub retries: u32,
pub max_retries: u32,
pub run_id: Option<String>,
pub progress: Option<String>,
pub last_heartbeat_unix_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProjectRun {
pub id: String,
pub task_id: String,
pub agent: String,
pub status: ProjectRunStatus,
pub attempt: u32,
pub started_unix_seconds: u64,
pub updated_unix_seconds: u64,
pub progress: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProjectEvent {
pub sequence: u64,
pub project_id: String,
pub kind: String,
pub timestamp_unix_seconds: u64,
pub data: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProjectReport {
pub project: ProjectSummary,
pub tasks: Vec<ProjectTask>,
pub runs: Vec<ProjectRun>,
pub generated_unix_seconds: u64,
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ProjectResponse {
Created {
project: ProjectSummary,
},
Status {
project: ProjectSummary,
},
Events {
project_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
compacted_before: Option<u64>,
events: Vec<ProjectEvent>,
},
Tasks {
project_id: String,
tasks: Vec<ProjectTask>,
},
Report {
report: ProjectReport,
},
Updated {
project: ProjectSummary,
},
TaskAdded {
project: ProjectSummary,
task_id: String,
},
RunStarted {
project: ProjectSummary,
run_id: String,
},
}
pub const DEFAULT_CONFIRM_SECONDS: u64 = 30 * 60;
pub const MAX_CONFIRM_SECONDS: u64 = 4 * 60 * 60;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ConfirmInfo {
pub id: String,
pub state: ConfirmState,
pub chat: String,
pub deadline_unix_seconds: u64,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ConfirmState {
Pending,
Yes,
No,
Expired,
Withdrawn,
Failed,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RestartInfo {
pub to_version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub waiting_for: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub requester: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub origin: Option<String>,
pub deadline_unix_seconds: u64,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct DelegationSummary {
pub active: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub idle: Option<u64>,
pub reaped: u64,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub entries: Vec<DelegationInfo>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub killed: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DelegationInfo {
pub handle: String,
pub agent: String,
pub session: String,
pub depth: u32,
pub pid: u32,
pub owner_pid: u32,
pub processes: u32,
pub cwd: String,
pub started_unix_seconds: u64,
pub orphaned: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub conversation: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turn: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub idle_since_unix_seconds: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub background_jobs: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "action", rename_all = "snake_case")]
pub enum DaemonCommand {
Status,
Reload,
ChannelSet {
channel: String,
account: String,
enabled: bool,
workspace: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
remote_tools: Option<RemoteTools>,
#[serde(default, skip_serializing_if = "Option::is_none")]
senders: Option<Senders>,
#[serde(default, skip_serializing_if = "Option::is_none")]
purpose: Option<Purpose>,
},
ChannelLogout {
channel: String,
account: String,
},
Delegations {
#[serde(default)]
all: bool,
},
DelegationKill {
#[serde(default, skip_serializing_if = "Option::is_none")]
handle: Option<String>,
#[serde(default)]
orphans: bool,
},
RestartWhenIdle {
#[serde(default, skip_serializing_if = "Option::is_none")]
version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
commit: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
parent: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_wait_seconds: Option<u64>,
},
ConfirmAsk {
question: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
parent: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
timeout_seconds: Option<u64>,
},
ConfirmStatus {
id: String,
},
ProjectCreate {
name: String,
workspace: String,
},
ProjectStatus {
project: String,
},
ProjectEvents {
project: String,
after: Option<u64>,
},
ProjectTasks {
project: String,
},
ProjectReport {
project: String,
},
ProjectTaskAdd {
project: String,
title: String,
depends_on: Vec<String>,
max_retries: u32,
},
ProjectTaskUpdate {
project: String,
task: String,
status: ProjectTaskStatus,
progress: Option<String>,
},
ProjectRunStart {
project: String,
task: String,
agent: String,
},
ProjectRunProgress {
project: String,
run: String,
progress: String,
},
ProjectRunFinish {
project: String,
run: String,
status: ProjectRunStatus,
},
ProjectHeartbeat {
project: String,
task: Option<String>,
run: Option<String>,
},
MailStatus {
#[serde(default, skip_serializing_if = "Option::is_none")]
account: Option<String>,
},
MailCancel {
account: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(default)]
all: bool,
},
}