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#[non_exhaustive]
20#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
21pub enum SubAgentState {
22    /// The agent has been enqueued but the tokio task has not started yet.
23    Submitted,
24    /// The agent loop is actively executing LLM turns and tool calls.
25    Working,
26    /// The agent loop finished successfully.
27    Completed,
28    /// The agent loop returned an error or the task panicked.
29    Failed,
30    /// The agent was cancelled via [`SubAgentManager::cancel`][crate::SubAgentManager].
31    Canceled,
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn test_all_variants_debug() {
40        assert_eq!(format!("{:?}", SubAgentState::Submitted), "Submitted");
41        assert_eq!(format!("{:?}", SubAgentState::Working), "Working");
42        assert_eq!(format!("{:?}", SubAgentState::Completed), "Completed");
43        assert_eq!(format!("{:?}", SubAgentState::Failed), "Failed");
44        assert_eq!(format!("{:?}", SubAgentState::Canceled), "Canceled");
45    }
46
47    #[test]
48    fn test_clone_and_copy() {
49        let state = SubAgentState::Working;
50        let cloned = state;
51        assert_eq!(state, cloned);
52        let copied: SubAgentState = state;
53        assert_eq!(copied, SubAgentState::Working);
54    }
55
56    #[test]
57    fn test_partial_eq() {
58        assert_eq!(SubAgentState::Completed, SubAgentState::Completed);
59        assert_ne!(SubAgentState::Submitted, SubAgentState::Failed);
60    }
61
62    #[test]
63    fn test_terminal_states_are_distinct_from_active() {
64        let active = [SubAgentState::Submitted, SubAgentState::Working];
65        let terminal = [
66            SubAgentState::Completed,
67            SubAgentState::Failed,
68            SubAgentState::Canceled,
69        ];
70        for a in active {
71            for t in terminal {
72                assert_ne!(a, t);
73            }
74        }
75    }
76}