Skip to main content

capo_agent/
events.rs

1use motosan_agent_tool::ToolResult;
2use serde_json::Value;
3
4#[derive(Debug, Clone)]
5pub enum ProgressChunk {
6    Stdout(Vec<u8>),
7    Stderr(Vec<u8>),
8    Status(String),
9}
10
11impl From<crate::tools::ToolProgressChunk> for ProgressChunk {
12    fn from(c: crate::tools::ToolProgressChunk) -> Self {
13        use crate::tools::ToolProgressChunk as TPC;
14        match c {
15            TPC::Stdout(b) => Self::Stdout(b),
16            TPC::Stderr(b) => Self::Stderr(b),
17            TPC::Status(s) => Self::Status(s),
18        }
19    }
20}
21
22/// User's answer to a `PermissionRequested` prompt, carried back from the
23/// front-end to the binary so the binary can update the session cache.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct PermissionResolution {
26    pub tool: String,
27    pub args: serde_json::Value,
28    pub choice: PermissionChoice,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum PermissionChoice {
33    AllowOnce,
34    AllowSession,
35    Deny,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum Command {
40    SendUserMessage(String),
41    CancelAgent,
42    Quit,
43    ResolvePermission(PermissionResolution),
44}
45
46#[derive(Debug)]
47pub enum UiEvent {
48    AgentTurnStarted,
49    AgentThinking,
50    AgentTextDelta(String),
51    AgentMessageComplete(String),
52    ToolCallStarted {
53        id: String,
54        name: String,
55        args: Value,
56    },
57    ToolCallProgress {
58        id: String,
59        chunk: ProgressChunk,
60    },
61    ToolCallCompleted {
62        id: String,
63        result: UiToolResult,
64    },
65    AgentTurnComplete,
66    PermissionRequested {
67        tool: String,
68        args: serde_json::Value,
69        resolver: tokio::sync::oneshot::Sender<crate::permissions::Decision>,
70    },
71    Error(String),
72}
73
74#[derive(Debug, Clone)]
75pub struct UiToolResult {
76    pub is_error: bool,
77    pub text: String,
78}
79
80impl From<&ToolResult> for UiToolResult {
81    fn from(r: &ToolResult) -> Self {
82        Self {
83            is_error: r.is_error,
84            text: format!("{r:?}"),
85        }
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn permission_protocol_variants_are_constructible() {
95        let command = Command::ResolvePermission(PermissionResolution {
96            tool: "bash".into(),
97            args: serde_json::json!({"command": "echo hi"}),
98            choice: PermissionChoice::AllowSession,
99        });
100        assert!(format!("{command:?}").contains("ResolvePermission"));
101        assert!(format!("{command:?}").contains("AllowSession"));
102
103        let (resolver, _rx) = tokio::sync::oneshot::channel::<crate::permissions::Decision>();
104        let event = UiEvent::PermissionRequested {
105            tool: "bash".into(),
106            args: serde_json::json!({"command": "echo hi"}),
107            resolver,
108        };
109        assert!(format!("{event:?}").contains("PermissionRequested"));
110    }
111}