wisp/session/
session_model.rs1use crate::session::session_config_view::{LocalConfigKind, LocalConfigOption};
2use crate::session::session_loading_buffer::SessionLoadingBuffer;
3use crate::session::workspace_status::WorkspaceStatus;
4use acp_utils::notifications::{AetherCapabilities, McpServerStatus, McpServerStatusEntry};
5use agent_client_protocol::schema::v1::{self as acp, SessionId, SessionUpdate};
6use std::path::{Path, PathBuf};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum WorkspaceMoveState {
10 Idle,
11 Listing,
12 Picking,
13 Moving,
14 LoadingSession,
15}
16
17impl WorkspaceMoveState {
18 pub fn is_idle(self) -> bool {
19 matches!(self, Self::Idle)
20 }
21}
22
23pub struct SessionModel {
24 session_id: SessionId,
25 agent_name: String,
26 working_dir: PathBuf,
27 workspace_status: WorkspaceStatus,
28 prompt_capabilities: acp::PromptCapabilities,
29 capabilities: AetherCapabilities,
30 config_options: Vec<LocalConfigOption>,
31 auth_methods: Vec<acp::AuthMethod>,
32 loading_buffer: SessionLoadingBuffer,
33 workspace_move_state: WorkspaceMoveState,
34 server_statuses: Vec<McpServerStatusEntry>,
35}
36
37impl SessionModel {
38 pub fn from_config(config: crate::app::AppConfig, capabilities: AetherCapabilities) -> Self {
39 let crate::app::AppConfig {
40 session_id,
41 agent_name,
42 working_dir,
43 workspace_status,
44 prompt_capabilities,
45 config_options,
46 auth_methods,
47 ..
48 } = config;
49 Self {
50 session_id,
51 agent_name,
52 working_dir,
53 workspace_status,
54 prompt_capabilities,
55 capabilities,
56 config_options: config_options.into_iter().map(LocalConfigOption::from_acp).collect(),
57 auth_methods,
58 loading_buffer: SessionLoadingBuffer::default(),
59 workspace_move_state: WorkspaceMoveState::Idle,
60 server_statuses: Vec::new(),
61 }
62 }
63
64 pub fn session_id(&self) -> &SessionId {
65 &self.session_id
66 }
67
68 pub fn agent_name(&self) -> &str {
69 &self.agent_name
70 }
71
72 pub fn working_dir(&self) -> &Path {
73 &self.working_dir
74 }
75
76 pub fn prompt_capabilities(&self) -> &acp::PromptCapabilities {
77 &self.prompt_capabilities
78 }
79
80 pub fn capabilities(&self) -> &AetherCapabilities {
81 &self.capabilities
82 }
83
84 pub fn config_options(&self) -> &[LocalConfigOption] {
85 &self.config_options
86 }
87
88 pub fn auth_methods(&self) -> &[acp::AuthMethod] {
89 &self.auth_methods
90 }
91
92 pub fn workspace_status(&self) -> &WorkspaceStatus {
93 &self.workspace_status
94 }
95
96 pub fn workspace_move_state(&self) -> WorkspaceMoveState {
97 self.workspace_move_state
98 }
99
100 pub fn begin_workspace_listing(&mut self) {
101 self.workspace_move_state = WorkspaceMoveState::Listing;
102 }
103
104 pub fn begin_workspace_picking(&mut self) {
105 self.workspace_move_state = WorkspaceMoveState::Picking;
106 }
107
108 pub fn begin_workspace_move(&mut self) {
109 self.workspace_move_state = WorkspaceMoveState::Moving;
110 }
111
112 pub fn begin_workspace_load(&mut self) {
114 self.workspace_move_state = WorkspaceMoveState::LoadingSession;
115 }
116
117 pub fn end_workspace_move(&mut self) {
120 self.workspace_move_state = WorkspaceMoveState::Idle;
121 }
122
123 pub fn cancel_workspace_picking(&mut self) {
126 if self.workspace_move_state == WorkspaceMoveState::Picking {
127 self.workspace_move_state = WorkspaceMoveState::Idle;
128 }
129 }
130
131 pub fn abandon_workspace_load(&mut self) {
133 if self.workspace_move_state == WorkspaceMoveState::LoadingSession {
134 self.workspace_move_state = WorkspaceMoveState::Idle;
135 }
136 }
137
138 pub fn server_statuses(&self) -> &[McpServerStatusEntry] {
139 &self.server_statuses
140 }
141
142 pub fn unhealthy_server_count(&self) -> usize {
143 self.server_statuses.iter().filter(|server| !matches!(server.status, McpServerStatus::Connected { .. })).count()
144 }
145
146 pub fn update_server_statuses(&mut self, statuses: &[McpServerStatusEntry]) {
147 self.server_statuses = statuses.to_vec();
148 }
149
150 pub fn set_auth_methods(&mut self, auth_methods: &[acp::AuthMethod]) {
151 self.auth_methods = auth_methods.to_vec();
152 }
153
154 pub fn update_config_options(&mut self, config_options: Vec<acp::SessionConfigOption>) {
158 let next = config_options.into_iter().map(LocalConfigOption::from_acp).collect::<Vec<_>>();
159 self.config_options = next;
160 }
161
162 pub fn update_config_option_value(&mut self, config_id: &str, value: &str) {
163 if let Some(option) = self.config_options.iter_mut().find(|option| option.id == config_id)
164 && let LocalConfigKind::Select { current_value, .. } = &mut option.kind
165 {
166 current_value.clear();
167 current_value.push_str(value);
168 }
169 }
170
171 pub fn begin_load(&mut self, session_id: SessionId) {
172 self.loading_buffer.begin_load(session_id);
173 }
174
175 pub fn buffer_update(&mut self, session_id: &SessionId, update: SessionUpdate) -> Option<SessionUpdate> {
176 self.loading_buffer.push(session_id, update)
177 }
178
179 pub fn take_buffered_updates(&mut self, session_id: &SessionId) -> Vec<SessionUpdate> {
180 self.loading_buffer.take(session_id)
181 }
182
183 pub fn clear_loads(&mut self) {
184 self.loading_buffer.clear();
185 }
186
187 pub fn set_session(&mut self, session_id: SessionId, config_options: Vec<acp::SessionConfigOption>) {
188 self.session_id = session_id;
189 self.config_options = config_options.into_iter().map(LocalConfigOption::from_acp).collect();
190 }
191
192 pub fn set_working_dir(&mut self, working_dir: PathBuf) {
193 self.working_dir = working_dir;
194 }
195
196 pub fn set_workspace_status(&mut self, workspace_status: WorkspaceStatus) {
197 self.workspace_status = workspace_status;
198 }
199}