1use cersei_tools::permissions::{PermissionDecision, PermissionRequest};
4use cersei_tools::PermissionLevel;
5use cersei_types::*;
6use std::time::Duration;
7use tokio::sync::mpsc;
8
9use crate::AgentOutput;
10
11#[derive(Debug, Clone)]
14pub enum AgentEvent {
15 TextDelta(String),
17 ThinkingDelta(String),
18
19 ToolStart {
21 name: String,
22 id: String,
23 input: serde_json::Value,
24 },
25 ToolEnd {
26 name: String,
27 id: String,
28 result: String,
29 is_error: bool,
30 duration: Duration,
31 },
32 ToolPermissionCheck {
33 name: String,
34 id: String,
35 level: PermissionLevel,
36 },
37
38 PermissionRequired(PermissionRequest),
40
41 TurnStart {
43 turn: u32,
44 },
45 TurnComplete {
46 turn: u32,
47 stop_reason: StopReason,
48 usage: Usage,
49 },
50 ModelRequestStart {
51 turn: u32,
52 message_count: usize,
53 token_estimate: u64,
54 },
55 ModelResponseStart {
56 turn: u32,
57 model: String,
58 },
59
60 TokenWarning {
62 pct_used: f64,
63 state: WarningState,
64 },
65 CompactStart {
66 reason: CompactReason,
67 messages_before: usize,
68 },
69 CompactEnd {
70 messages_after: usize,
71 tokens_freed: u64,
72 },
73
74 SessionLoaded {
76 session_id: String,
77 message_count: usize,
78 },
79 SessionSaved {
80 session_id: String,
81 },
82
83 CostUpdate {
85 turn_cost: f64,
86 cumulative_cost: f64,
87 input_tokens: u64,
88 output_tokens: u64,
89 },
90
91 SubAgentSpawned {
93 agent_id: String,
94 prompt: String,
95 },
96 SubAgentComplete {
97 agent_id: String,
98 result: AgentOutput,
99 },
100
101 HookFired {
103 event: cersei_hooks::HookEvent,
104 hook_name: String,
105 },
106 HookBlocked {
107 event: cersei_hooks::HookEvent,
108 hook_name: String,
109 reason: String,
110 },
111
112 Status(String),
114 Error(String),
115 Complete(AgentOutput),
116}
117
118#[derive(Debug, Clone, Copy)]
119pub enum WarningState {
120 Normal,
121 Warning,
122 Critical,
123}
124
125#[derive(Debug, Clone, Copy)]
126pub enum CompactReason {
127 ThresholdExceeded,
128 ManualTrigger,
129 ContextOverflow,
130}
131
132pub struct AgentStream {
137 rx: mpsc::Receiver<AgentEvent>,
138 control_tx: mpsc::Sender<AgentControl>,
139}
140
141impl AgentStream {
142 pub(crate) fn new(
143 rx: mpsc::Receiver<AgentEvent>,
144 control_tx: mpsc::Sender<AgentControl>,
145 ) -> Self {
146 Self { rx, control_tx }
147 }
148
149 pub fn respond_permission(&self, request_id: String, decision: PermissionDecision) {
151 let _ = self.control_tx.try_send(AgentControl::PermissionResponse {
152 request_id,
153 decision,
154 });
155 }
156
157 pub fn cancel(&self) {
159 let _ = self.control_tx.try_send(AgentControl::Cancel);
160 }
161
162 pub fn inject_message(&self, message: String) {
164 let _ = self
165 .control_tx
166 .try_send(AgentControl::InjectMessage(message));
167 }
168
169 pub async fn next(&mut self) -> Option<AgentEvent> {
171 self.rx.recv().await
172 }
173
174 pub async fn collect(mut self) -> cersei_types::Result<AgentOutput> {
176 while let Some(event) = self.rx.recv().await {
177 match event {
178 AgentEvent::Complete(output) => return Ok(output),
179 AgentEvent::Error(e) => return Err(CerseiError::Other(anyhow::anyhow!(e))),
180 _ => continue,
181 }
182 }
183 Err(CerseiError::Cancelled)
184 }
185
186 pub async fn collect_text(mut self) -> cersei_types::Result<String> {
188 let mut text = String::new();
189 while let Some(event) = self.rx.recv().await {
190 match event {
191 AgentEvent::TextDelta(t) => text.push_str(&t),
192 AgentEvent::Complete(_) => return Ok(text),
193 AgentEvent::Error(e) => return Err(CerseiError::Other(anyhow::anyhow!(e))),
194 _ => continue,
195 }
196 }
197 Ok(text)
198 }
199}
200
201#[derive(Debug)]
204pub(crate) enum AgentControl {
205 #[allow(dead_code)]
206 PermissionResponse {
207 request_id: String,
208 decision: PermissionDecision,
209 },
210 Cancel,
211 #[allow(dead_code)]
212 InjectMessage(String),
213}