Skip to main content

vtcode_commons/ui_protocol/
activity.rs

1//! Explicit global activity states shared by the runloop and terminal UI.
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
4pub enum ActivityState {
5    #[default]
6    Idle,
7    /// The agent is working inside the plan workflow (research/synthesis).
8    Planning,
9    PreparingFreshExecutionThread,
10    RestoringApprovedPlan,
11    /// The agent is executing an approved plan.
12    Building,
13    StartingBuild,
14}
15
16impl ActivityState {
17    /// Transient handoff states that must block input and mode switches.
18    pub const fn is_busy(self) -> bool {
19        matches!(self, Self::PreparingFreshExecutionThread | Self::RestoringApprovedPlan | Self::StartingBuild)
20    }
21
22    /// Long-lived display stages that keep input enabled between turns.
23    pub const fn is_stage(self) -> bool {
24        matches!(self, Self::Planning | Self::Building)
25    }
26
27    pub const fn status(self) -> Option<&'static str> {
28        match self {
29            Self::Idle => None,
30            Self::Planning => Some("Planning..."),
31            Self::PreparingFreshExecutionThread => Some("Preparing fresh execution thread..."),
32            Self::RestoringApprovedPlan => Some("Restoring approved plan..."),
33            Self::Building => Some("Building..."),
34            Self::StartingBuild => Some("Starting build..."),
35        }
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::ActivityState;
42
43    #[test]
44    fn idle_has_no_status_and_is_not_busy_or_stage() {
45        assert_eq!(ActivityState::Idle.status(), None);
46        assert!(!ActivityState::Idle.is_busy());
47        assert!(!ActivityState::Idle.is_stage());
48    }
49
50    #[test]
51    fn stages_are_not_busy_but_keep_a_status() {
52        for state in [ActivityState::Planning, ActivityState::Building] {
53            assert!(!state.is_busy());
54            assert!(state.is_stage());
55            assert!(state.status().is_some());
56        }
57        assert_eq!(ActivityState::Planning.status(), Some("Planning..."));
58        assert_eq!(ActivityState::Building.status(), Some("Building..."));
59    }
60
61    #[test]
62    fn transient_handoffs_are_busy_but_not_stages() {
63        for state in [
64            ActivityState::PreparingFreshExecutionThread,
65            ActivityState::RestoringApprovedPlan,
66            ActivityState::StartingBuild,
67        ] {
68            assert!(state.is_busy());
69            assert!(!state.is_stage());
70            assert!(state.status().is_some());
71        }
72    }
73}