1use std::sync::Arc;
2use std::sync::Mutex;
3
4use serde_json::Value;
5use tokio::sync::mpsc;
6use tokio::sync::oneshot;
7
8use crate::core::agent::AgentEvents;
9
10#[derive(Debug, Clone)]
11pub struct SubagentEventItem {
12 pub task_id: String,
13 pub name: String,
14 pub agent_name: String,
15 pub status: String,
16 pub prompt: String,
17 pub depth: usize,
18 pub parent_task_id: Option<String>,
19 pub started_at: u64,
20 pub finished_at: Option<u64>,
21 pub summary: Option<String>,
22 pub error: Option<String>,
23}
24
25type QuestionResponder =
26 Arc<Mutex<Option<oneshot::Sender<anyhow::Result<crate::core::QuestionAnswers>>>>>;
27
28#[derive(Debug)]
29pub struct ScopedTuiEvent {
30 pub session_epoch: u64,
31 pub run_epoch: u64,
32 pub event: TuiEvent,
33}
34
35#[derive(Debug)]
36pub enum TuiEvent {
37 Thinking(String),
38 ToolStart {
39 name: String,
40 args: Value,
41 },
42 ToolEnd {
43 name: String,
44 result: crate::tool::ToolResult,
45 },
46 TodoItemsChanged(Vec<crate::core::TodoItem>),
47 AssistantDelta(String),
48 ContextUsage(usize),
49 AssistantDone,
50 SessionTitle(String),
51 CompactionStart,
52 CompactionDone(String),
53 QuestionPrompt {
54 questions: Vec<crate::core::QuestionPrompt>,
55 responder: QuestionResponder,
56 },
57 SubagentsChanged(Vec<SubagentEventItem>),
58 Error(String),
59 Key(crossterm::event::KeyEvent),
60 Tick,
61}
62
63#[derive(Clone)]
65pub struct TuiEventSender {
66 tx: Arc<mpsc::UnboundedSender<ScopedTuiEvent>>,
67 session_epoch: u64,
68 run_epoch: u64,
69}
70
71impl TuiEventSender {
72 pub fn new(tx: mpsc::UnboundedSender<ScopedTuiEvent>) -> Self {
73 Self {
74 tx: Arc::new(tx),
75 session_epoch: 0,
76 run_epoch: 0,
77 }
78 }
79
80 pub fn scoped(&self, session_epoch: u64, run_epoch: u64) -> Self {
81 Self {
82 tx: Arc::clone(&self.tx),
83 session_epoch,
84 run_epoch,
85 }
86 }
87
88 pub fn send(&self, event: TuiEvent) {
89 let _ = self.tx.send(ScopedTuiEvent {
90 session_epoch: self.session_epoch,
91 run_epoch: self.run_epoch,
92 event,
93 });
94 }
95}
96
97impl AgentEvents for TuiEventSender {
98 fn on_thinking(&self, text: &str) {
99 self.send(TuiEvent::Thinking(text.to_string()));
100 }
101
102 fn on_tool_start(&self, name: &str, args: &Value) {
103 self.send(TuiEvent::ToolStart {
104 name: name.to_string(),
105 args: args.clone(),
106 });
107 }
108
109 fn on_tool_end(&self, name: &str, result: &crate::tool::ToolResult) {
110 self.send(TuiEvent::ToolEnd {
111 name: name.to_string(),
112 result: result.clone(),
113 });
114 }
115
116 fn on_todo_items_changed(&self, items: &[crate::core::TodoItem]) {
117 self.send(TuiEvent::TodoItemsChanged(items.to_vec()));
118 }
119
120 fn on_assistant_delta(&self, delta: &str) {
121 self.send(TuiEvent::AssistantDelta(delta.to_string()));
122 }
123
124 fn on_context_usage(&self, tokens: usize) {
125 self.send(TuiEvent::ContextUsage(tokens));
126 }
127
128 fn on_assistant_done(&self) {
129 self.send(TuiEvent::AssistantDone);
130 }
131}