Skip to main content

opendev_tui/event/
mod.rs

1//! Event types for the TUI application.
2//!
3//! Bridges crossterm terminal events with application-level events
4//! (agent messages, tool execution updates, etc.).
5//!
6//! This module is split into focused sub-modules:
7//! - [`handler`] — Crossterm event reader with scroll debouncing
8//! - [`recorder`] — Event recording/replay for debugging
9
10mod handler;
11mod recorder;
12
13pub use handler::EventHandler;
14pub use recorder::{EventRecorder, RecordedEvent, load_recorded_events};
15
16use crossterm::event::{Event as CrosstermEvent, KeyEvent};
17use opendev_models::message::ChatMessage;
18use opendev_runtime::InterruptToken;
19
20/// Application-level events consumed by the main event loop.
21#[derive(Debug)]
22#[allow(clippy::large_enum_variant)]
23pub enum AppEvent {
24    /// Raw terminal event from crossterm.
25    Terminal(CrosstermEvent),
26    /// Key press (extracted from terminal event for convenience).
27    Key(KeyEvent),
28    /// Terminal resize.
29    Resize(u16, u16),
30    /// Mouse-wheel scroll up.
31    ScrollUp,
32    /// Mouse-wheel scroll down.
33    ScrollDown,
34    /// Mouse button pressed at (col, row).
35    MouseDown { col: u16, row: u16 },
36    /// Mouse dragged to (col, row) while button held.
37    MouseDrag { col: u16, row: u16 },
38    /// Mouse button released at (col, row).
39    MouseUp { col: u16, row: u16 },
40    /// Terminal regained focus (user switched back to this tab/window).
41    FocusGained,
42    /// Tick for periodic UI updates (spinner animation, etc.).
43    Tick,
44
45    // -- Agent events --
46    /// Assistant started generating a response.
47    AgentStarted,
48    /// Streaming text chunk from the assistant.
49    AgentChunk(String),
50    /// Complete assistant message received.
51    AgentMessage(ChatMessage),
52    /// Agent finished the current turn.
53    AgentFinished,
54    /// Agent encountered an error.
55    AgentError(String),
56
57    // -- Tool events --
58    /// A tool execution started.
59    ToolStarted {
60        tool_id: String,
61        tool_name: String,
62        args: std::collections::HashMap<String, serde_json::Value>,
63    },
64    /// A tool produced output.
65    ToolOutput { tool_id: String, output: String },
66    /// A tool produced its final result.
67    ToolResult {
68        tool_id: String,
69        tool_name: String,
70        output: String,
71        success: bool,
72        args: std::collections::HashMap<String, serde_json::Value>,
73    },
74    /// A tool execution completed.
75    ToolFinished { tool_id: String, success: bool },
76    /// Tool requires user approval (legacy, no channel — kept for recording compatibility).
77    ToolApprovalRequired {
78        tool_id: String,
79        tool_name: String,
80        description: String,
81    },
82
83    /// Tool approval request with bidirectional channel.
84    ToolApprovalRequested {
85        command: String,
86        working_dir: String,
87        response_tx: tokio::sync::oneshot::Sender<opendev_runtime::ToolApprovalDecision>,
88    },
89
90    /// Ask-user request with bidirectional channel.
91    AskUserRequested {
92        question: String,
93        options: Vec<String>,
94        default: Option<String>,
95        response_tx: tokio::sync::oneshot::Sender<String>,
96    },
97
98    // -- Subagent events --
99    /// A subagent started executing.
100    SubagentStarted {
101        subagent_id: String,
102        subagent_name: String,
103        task: String,
104        cancel_token: Option<tokio_util::sync::CancellationToken>,
105    },
106    /// A subagent made a tool call (for nested display).
107    SubagentToolCall {
108        subagent_id: String,
109        subagent_name: String,
110        tool_name: String,
111        tool_id: String,
112        args: std::collections::HashMap<String, serde_json::Value>,
113    },
114    /// A subagent tool call completed.
115    SubagentToolComplete {
116        subagent_id: String,
117        subagent_name: String,
118        tool_name: String,
119        tool_id: String,
120        success: bool,
121    },
122    /// A subagent finished its task.
123    SubagentFinished {
124        subagent_id: String,
125        subagent_name: String,
126        success: bool,
127        result_summary: String,
128        tool_call_count: usize,
129        shallow_warning: Option<String>,
130    },
131    /// Token usage update from a subagent's LLM call.
132    SubagentTokenUpdate {
133        subagent_id: String,
134        subagent_name: String,
135        input_tokens: u64,
136        output_tokens: u64,
137    },
138    // -- Reasoning events --
139    /// Native reasoning content from LLM response (inline thinking).
140    ReasoningContent(String),
141    /// A new reasoning/thinking block started (separator between interleaved blocks).
142    ReasoningBlockStart,
143
144    // -- Task progress events --
145    /// Agent started working on a task (shows progress bar).
146    TaskProgressStarted { description: String },
147    /// Agent finished the current task (hides progress bar).
148    TaskProgressFinished,
149
150    // -- Budget events --
151    /// Session cost budget has been exhausted. The agent loop should pause.
152    BudgetExhausted { cost_usd: f64, budget_usd: f64 },
153
154    // -- File change events --
155    /// File change summary after a query completes.
156    FileChangeSummary {
157        files: usize,
158        additions: u64,
159        deletions: u64,
160    },
161
162    // -- Context events --
163    /// Context window usage percentage updated (0.0–100.0).
164    ContextUsage(f64),
165
166    // -- Compaction events --
167    /// Manual compaction started (shows compaction spinner).
168    CompactionStarted,
169    /// Manual compaction finished (hides compaction spinner, shows result).
170    CompactionFinished { success: bool, message: String },
171
172    // -- Plan events --
173    /// Plan approval request arrived from the PresentPlanTool.
174    /// Contains the plan content to display and the oneshot sender for the decision.
175    PlanApprovalRequested {
176        plan_content: String,
177        response_tx: tokio::sync::oneshot::Sender<opendev_runtime::PlanDecision>,
178    },
179
180    // -- UI events --
181    /// User submitted a message.
182    UserSubmit(String),
183    /// User requested interrupt (Escape).
184    Interrupt,
185    /// Set the interrupt token for the current query (sent by agent backend).
186    SetInterruptToken(InterruptToken),
187    /// Agent run was interrupted (sent by agent backend after cancellation).
188    AgentInterrupted,
189    /// Mode changed (normal/plan).
190    ModeChanged(String),
191    /// Kill a background task by ID.
192    KillTask(String),
193
194    // -- Background agent events --
195    /// An agent was moved to the background via Ctrl+B.
196    AgentBackgrounded {
197        task_id: String,
198        query_summary: String,
199    },
200    /// A background agent completed its work.
201    BackgroundAgentCompleted {
202        task_id: String,
203        success: bool,
204        result_summary: String,
205        full_result: String,
206        cost_usd: f64,
207        tool_call_count: usize,
208    },
209    /// Progress update from a background agent.
210    BackgroundAgentProgress {
211        task_id: String,
212        tool_name: String,
213        tool_count: usize,
214    },
215    /// A background agent was killed.
216    BackgroundAgentKilled { task_id: String },
217    /// LLM-generated nudge message after backgrounding agents.
218    BackgroundNudge { content: String },
219    /// Activity line from a background agent (tool call, reasoning, etc.).
220    BackgroundAgentActivity { task_id: String, line: String },
221    /// Register a background agent task with its interrupt token (sent from tui_runner).
222    SetBackgroundAgentToken {
223        task_id: String,
224        query: String,
225        session_id: String,
226        interrupt_token: InterruptToken,
227    },
228
229    // -- Undo/Redo events --
230    /// Snapshot was taken (stores tree hash for undo stack).
231    SnapshotTaken { hash: String },
232    /// Undo result from the runtime.
233    UndoResult { success: bool, message: String },
234    /// Redo result from the runtime.
235    RedoResult { success: bool, message: String },
236    /// Share result from the runtime.
237    ShareResult { path: String },
238    /// File watcher detected changes.
239    FileChanged { paths: Vec<String> },
240
241    /// Session title was auto-detected by the topic detector.
242    SessionTitleUpdated(String),
243
244    /// Quit the application.
245    Quit,
246}
247
248#[cfg(test)]
249mod tests;