Skip to main content

zeph_subagent/
state.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4/// Lifecycle state of a sub-agent task.
5///
6/// States flow in one direction: `Submitted → Working → {Completed | Failed | Canceled}`.
7/// A handle whose state is `Canceled` may briefly lag the background task's own state
8/// because [`SubAgentManager::cancel`][crate::SubAgentManager] updates the handle
9/// synchronously while the task observes the cancellation token asynchronously.
10///
11/// # Examples
12///
13/// ```rust
14/// use zeph_subagent::SubAgentState;
15///
16/// let state = SubAgentState::Working;
17/// assert_ne!(state, SubAgentState::Completed);
18/// ```
19#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
20pub enum SubAgentState {
21    /// The agent has been enqueued but the tokio task has not started yet.
22    Submitted,
23    /// The agent loop is actively executing LLM turns and tool calls.
24    Working,
25    /// The agent loop finished successfully.
26    Completed,
27    /// The agent loop returned an error or the task panicked.
28    Failed,
29    /// The agent was cancelled via [`SubAgentManager::cancel`][crate::SubAgentManager].
30    Canceled,
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn test_all_variants_debug() {
39        assert_eq!(format!("{:?}", SubAgentState::Submitted), "Submitted");
40        assert_eq!(format!("{:?}", SubAgentState::Working), "Working");
41        assert_eq!(format!("{:?}", SubAgentState::Completed), "Completed");
42        assert_eq!(format!("{:?}", SubAgentState::Failed), "Failed");
43        assert_eq!(format!("{:?}", SubAgentState::Canceled), "Canceled");
44    }
45
46    #[test]
47    fn test_clone_and_copy() {
48        let state = SubAgentState::Working;
49        let cloned = state;
50        assert_eq!(state, cloned);
51        let copied: SubAgentState = state;
52        assert_eq!(copied, SubAgentState::Working);
53    }
54
55    #[test]
56    fn test_partial_eq() {
57        assert_eq!(SubAgentState::Completed, SubAgentState::Completed);
58        assert_ne!(SubAgentState::Submitted, SubAgentState::Failed);
59    }
60
61    #[test]
62    fn test_terminal_states_are_distinct_from_active() {
63        let active = [SubAgentState::Submitted, SubAgentState::Working];
64        let terminal = [
65            SubAgentState::Completed,
66            SubAgentState::Failed,
67            SubAgentState::Canceled,
68        ];
69        for a in active {
70            for t in terminal {
71                assert_ne!(a, t);
72            }
73        }
74    }
75}