Skip to main content

codetether_agent/tui/app/state/
mod.rs

1//! TUI application state — the central `AppState` struct and its methods.
2//!
3//! Methods are split into focused submodules by concern:
4//!
5//! - `agent_profile` — spawned agent types and profile lookup
6//! - `profile_defs` — named profile constants
7//! - `slash_commands` — `/command` constant table
8//! - `input_cursor` — cursor movement and text editing
9//! - `slash_suggest` — slash autocomplete suggestion methods
10//! - `scroll` — chat scroll sentinel scheme and tool-preview scroll
11//! - `session_nav` — session list filtering and selection
12//! - `worker_bridge` — A2A worker connection state
13//! - `history` — command history ↑/↓ navigation
14//! - `model_picker` — async model refresh from providers
15//! - `model_picker_nav` — synchronous model picker navigation
16//! - `timing` — request latency tracking
17//! - `steering` — queued steering messages
18//! - `settings_nav` — settings selection and view-mode switching
19//! - `message_cache` — render-line cache for performance
20
21pub mod agent_profile;
22#[path = "latency/chat.rs"]
23pub mod chat_latency;
24pub mod context_health;
25pub mod default_impl;
26pub mod git_state;
27pub mod history;
28pub mod input_cursor;
29pub mod input_edit;
30pub mod message_cache;
31pub mod model_picker;
32pub mod model_picker_nav;
33pub mod pending_tool;
34pub mod profile_defs;
35pub mod scroll;
36pub mod session_nav;
37pub mod settings_nav;
38pub mod slash_commands;
39pub mod slash_hints;
40pub mod slash_suggest;
41pub mod timing;
42pub mod view_save;
43pub mod worker_bridge;
44
45#[cfg(test)]
46mod tests;
47
48// Re-exports so external `use crate::tui::app::state::X` still works.
49pub use agent_profile::{SpawnedAgent, agent_profile};
50pub use profile_defs::AgentProfile;
51pub use slash_commands::SLASH_COMMANDS;
52
53use std::collections::{HashMap, HashSet, VecDeque};
54use std::sync::Arc;
55use std::time::Instant;
56
57use crate::session::{ImageAttachment, SessionSummary};
58use crate::tui::models::{InputMode, ViewMode};
59use crate::tui::{audit_view::AuditViewState, bus_log::BusLogState};
60use crate::tui::{chat::message::ChatMessage, help::HelpScrollState};
61use crate::tui::{ralph_view::RalphViewState, swarm_view::SwarmViewState};
62use crate::tui::{symbol_search::SymbolSearchState, worker_bridge::IncomingTask};
63
64pub use crate::session::SessionEvent;
65
66#[derive(Default)]
67pub struct App {
68    pub state: AppState,
69}
70
71pub struct AppState {
72    pub view_mode: ViewMode,
73    pub input_mode: InputMode,
74    pub messages: Vec<ChatMessage>,
75    pub input: String,
76    pub input_cursor: usize,
77    pub input_scroll: usize,
78    pub chat_scroll: usize,
79    pub chat_last_max_scroll: usize,
80    /// When true, every session event scrolls chat to the bottom. Set to
81    /// `false` by [`AppState::scroll_up`] so the user's manual scrollback
82    /// position survives streaming output. Re-enabled by
83    /// [`AppState::scroll_to_bottom`] (e.g. End / G keypress).
84    pub chat_auto_follow: bool,
85    pub tool_preview_scroll: usize,
86    pub tool_preview_last_max_scroll: usize,
87    /// Selected index in the protocol/agent registry view (left pane list).
88    pub protocol_selected: usize,
89    /// Vertical scroll offset for the protocol/agent registry detail pane.
90    pub protocol_scroll: usize,
91    pub status: String,
92    pub processing: bool,
93    pub session_id: Option<String>,
94    pub sessions: Vec<SessionSummary>,
95    pub selected_session: usize,
96    pub session_filter: String,
97    pub cwd_display: String,
98    pub bus_log: BusLogState,
99    pub swarm: SwarmViewState,
100    pub audit: AuditViewState,
101    pub git: git_state::GitViewState,
102    pub ralph: RalphViewState,
103    pub symbol_search: SymbolSearchState,
104    pub slash_suggestions: Vec<String>,
105    pub selected_slash_suggestion: usize,
106    pub command_history: Vec<String>,
107    pub history_index: Option<usize>,
108    pub worker_id: Option<String>,
109    pub worker_name: Option<String>,
110    pub a2a_connected: bool,
111    /// Local A2A peer endpoint bound; independent of `a2a_connected` (worker bridge).
112    pub peer_endpoint_ready: bool,
113    pub recent_tasks: Vec<String>,
114    pub worker_bridge_registered_agents: HashSet<String>,
115    pub worker_bridge_processing_state: Option<bool>,
116    pub worker_task_queue: VecDeque<IncomingTask>,
117    /// Tracks the currently executing remote task so a bus reply can
118    /// be sent to `from_agent` upon completion.
119    pub active_remote_task: Option<IncomingTask>,
120    pub help_scroll: HelpScrollState,
121    pub show_help: bool,
122    pub available_models: Vec<String>,
123    pub selected_model_index: usize,
124    pub model_picker_active: bool,
125    pub model_filter: String,
126    pub model_refresh_in_flight: bool,
127    pub model_refresh_rx:
128        Option<tokio::sync::mpsc::UnboundedReceiver<model_picker::ModelRefreshEvent>>,
129    pub model_picker_target_model: Option<String>,
130    pub streaming_text: String,
131    pub processing_started_at: Option<Instant>,
132    pub current_request_first_token_ms: Option<u64>,
133    pub current_request_last_token_ms: Option<u64>,
134    pub last_request_first_token_ms: Option<u64>,
135    pub last_request_last_token_ms: Option<u64>,
136    pub last_completion_model: Option<String>,
137    pub last_completion_latency_ms: Option<u64>,
138    pub last_completion_prompt_tokens: Option<usize>,
139    pub last_completion_output_tokens: Option<usize>,
140    /// Most recent pre-flight token estimate (request side). Surfaced as a
141    /// percentage badge in the chat title bar so users see context pressure
142    /// build up turn-over-turn instead of being surprised by a compaction.
143    pub context_used: Option<usize>,
144    /// Usable token budget paired with [`AppState::context_used`].
145    pub context_budget: Option<usize>,
146    pub context_health: context_health::ContextHealthState,
147    pub last_tool_name: Option<String>,
148    pub last_tool_latency_ms: Option<u64>,
149    pub last_tool_success: Option<bool>,
150    pub pending_tool_name: Option<String>,
151    pub pending_tool_started_at: Option<Instant>,
152    pub chat_latency: chat_latency::ChatLatencyStats,
153    pub pending_images: Vec<ImageAttachment>,
154    /// Sidecar buffer for paste blocks that were too large to inline
155    /// into the input box. Each entry is rendered in the input as a
156    /// short placeholder like `[Pasted text #1: 42 lines, 1.2 KiB]`,
157    /// and the full content is expanded back into the prompt sent to
158    /// the agent at submit time. See
159    /// [`crate::tui::app::input::pasted_text`].
160    pub pending_text_pastes: Vec<crate::tui::app::input::pasted_text::PendingTextPaste>,
161    pub auto_apply_edits: bool,
162    pub allow_network: bool,
163    pub slash_autocomplete: bool,
164    pub use_worktree: bool,
165    pub selected_settings_index: usize,
166    pub mcp_registry: Arc<crate::tui::app::mcp::TuiMcpRegistry>,
167    pub spawned_agents: HashMap<String, SpawnedAgent>,
168    pub active_spawned_agent: Option<String>,
169    pub streaming_agent_texts: HashMap<String, String>,
170    pub cached_message_lines: Vec<ratatui::text::Line<'static>>,
171    pub cached_messages_len: usize,
172    pub cached_max_width: usize,
173    pub cached_streaming_snapshot: Option<String>,
174    pub cached_processing: bool,
175    pub cached_frozen_len: usize,
176    pub watchdog_notification: Option<super::watchdog::WatchdogNotification>,
177    pub main_watchdog_root_prompt: Option<String>,
178    pub main_last_event_at: Option<Instant>,
179    pub main_watchdog_restart_count: u32,
180    pub main_inflight_prompt: Option<String>,
181    pub okr_repository: Option<Arc<crate::okr::OkrRepository>>,
182    pub pending_okr_approval: Option<super::okr_gate::PendingOkrApproval>,
183    pub pending_smart_switch_retry: Option<crate::tui::app::smart_switch::PendingSmartSwitchRetry>,
184    pub smart_switch_retry_count: u32,
185    pub smart_switch_attempted_models: Vec<String>,
186    pub chat_sync_rx:
187        Option<tokio::sync::mpsc::UnboundedReceiver<crate::tui::chat::sync::ChatSyncUiEvent>>,
188    pub chat_sync_status: Option<String>,
189    pub chat_sync_last_success: Option<String>,
190    pub chat_sync_last_error: Option<String>,
191    pub chat_sync_uploaded_bytes: u64,
192    pub chat_sync_uploaded_batches: u64,
193    pub autochat: super::autochat::state::AutochatState,
194    pub file_picker_dir: std::path::PathBuf,
195    pub file_picker_entries: Vec<crate::tui::app::file_picker::FilePickerEntry>,
196    pub file_picker_selected: usize,
197    pub file_picker_filter: String,
198    pub file_picker_active: bool,
199    pub workspace: crate::tui::models::WorkspaceSnapshot,
200    pub chat_layout_mode: crate::tui::ui::webview::layout_mode::ChatLayoutMode,
201    /// Cancel handle for the in-flight provider turn. Notifying this
202    /// interrupts the current LLM stream so a user steering message can
203    /// be applied immediately instead of waiting for the turn to finish.
204    pub current_turn_cancel: Option<Arc<tokio::sync::Notify>>,
205    /// Timestamp of the previous key event. Used to detect paste bursts
206    /// on terminals that don't forward bracketed-paste markers — if a
207    /// bare `Enter` arrives within `PASTE_BURST_WINDOW_MS` of the last
208    /// key, we treat it as an embedded newline in pasted text instead
209    /// of a submit, so pasting multi-line content doesn't emit one
210    /// chat message per line.
211    pub last_key_at: Option<Instant>,
212    /// Stop flag for in-progress voice recording. `Some` means recording.
213    pub recording_stop_flag: Option<Arc<std::sync::atomic::AtomicBool>>,
214    /// Shared slot for the background voice thread to deliver transcribed text.
215    /// The tick loop polls this and injects into [`AppState::input`].
216    pub pending_voice_text: Option<Arc<std::sync::Mutex<Option<String>>>>,
217    /// Saved chat scroll state captured when leaving Chat for another view,
218    /// so streaming output that arrives while the user is in `/bus` etc.
219    /// doesn't lose their scrollback position when they return.
220    pub saved_chat_scroll: usize,
221    pub saved_chat_auto_follow: bool,
222    pub saved_tool_preview_scroll: usize,
223    /// Live throughput tracking for the status-line tok/s badge.
224    pub streaming_start: Option<Instant>,
225    pub streaming_chars: usize,
226}