Skip to main content

envoy/
status.rs

1use serde::{Deserialize, Serialize};
2
3/// Agent workflow state, driven by Claude1's complete-agent-workflow checkpoints.
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5#[serde(rename_all = "snake_case")]
6pub enum AgentState {
7    /// Actively executing a task (pre-work → implementation → verification)
8    Working,
9    /// Blocked on another agent or external dependency
10    Blocked,
11    /// Work done, waiting for review/CI/docs
12    WaitingReview,
13    /// No active task
14    Idle,
15}
16
17impl AgentState {
18    pub fn as_str(&self) -> &'static str {
19        match self {
20            Self::Working => "working",
21            Self::Blocked => "blocked",
22            Self::WaitingReview => "waiting_review",
23            Self::Idle => "idle",
24        }
25    }
26}
27
28/// A point-in-time snapshot of an agent's status.
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct AgentStatusSnapshot {
31    pub state: AgentState,
32    pub task_id: Option<String>,
33    pub blocked_reason: Option<String>,
34    /// Agent this one is waiting on (if Blocked)
35    pub waiting_on_agent: Option<String>,
36    /// Workflow checkpoint the agent is currently at
37    pub checkpoint: Option<String>,
38    /// Human-readable description of current work
39    pub working_on: String,
40}
41
42impl Default for AgentStatusSnapshot {
43    fn default() -> Self {
44        Self {
45            state: AgentState::Working,
46            task_id: None,
47            blocked_reason: None,
48            waiting_on_agent: None,
49            checkpoint: None,
50            working_on: "heartbeat via WS".into(),
51        }
52    }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct HeartbeatRequest {
57    pub agent_id: String,
58    pub status: AgentStatusSnapshot,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct HeartbeatResponse {
63    pub accepted: bool,
64    pub nudges: Vec<NudgeMessage>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct NudgeMessage {
69    pub reason: String,
70    pub severity: NudgeSeverity,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75pub enum NudgeSeverity {
76    Info,
77    Warning,
78    Blocking,
79}
80
81/// Configuration for the background nudge loop.
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct NudgeConfig {
84    /// Minutes before an agent without heartbeat is considered stale
85    pub stale_threshold_minutes: i64,
86    /// Minutes between nudge loop checks
87    pub check_interval_seconds: u64,
88}
89
90impl Default for NudgeConfig {
91    fn default() -> Self {
92        Self {
93            stale_threshold_minutes: 5,
94            check_interval_seconds: 30,
95        }
96    }
97}