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#[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}