Skip to main content

bamboo_server/session_app/
types.rs

1//! Value types for session use cases.
2
3use std::collections::BTreeSet;
4use std::sync::Arc;
5
6use bamboo_domain::reasoning::ReasoningEffort;
7use bamboo_domain::ProviderModelRef;
8use bamboo_domain::Session;
9use bamboo_engine::ImageFallbackConfig;
10use bamboo_infrastructure::LLMProvider;
11
12/// Resolved configuration snapshot for execution.
13///
14/// Built from `Config` in the handler layer and passed to use cases.
15/// This avoids leaking `bamboo-infrastructure-config` into the crate.
16#[derive(Clone, Default)]
17pub struct ExecutionConfigSnapshot {
18    pub default_model: Option<String>,
19    pub default_model_ref: Option<ProviderModelRef>,
20    pub default_reasoning_effort: Option<ReasoningEffort>,
21    pub disabled_tools: Vec<String>,
22    pub disabled_skill_ids: Vec<String>,
23    pub provider_name: String,
24    pub fast_model: Option<String>,
25    pub fast_model_ref: Option<ProviderModelRef>,
26    pub image_fallback: Option<ImageFallbackConfig>,
27    pub provider_model_ref_enabled: bool,
28}
29
30// ---- Chat types ----
31
32/// Input for the chat turn use case.
33pub struct ChatTurnInput {
34    pub session_id: String,
35    pub model: String,
36    pub model_ref: Option<ProviderModelRef>,
37    pub provider: Option<String>,
38    pub message: String,
39    pub system_prompt: Option<String>,
40    pub enhance_prompt: Option<String>,
41    pub workspace_path: Option<String>,
42    pub selected_skill_ids: Option<Vec<String>>,
43    pub copilot_conclusion_with_options_enhancement_enabled: Option<bool>,
44    /// Optional data directory for workspace path fallback when neither request
45    /// nor metadata provides one.
46    pub data_dir: Option<std::path::PathBuf>,
47}
48
49/// Outcome of preparing a chat turn.
50pub struct PreparedChatTurn {
51    pub session: Session,
52}
53
54// ---- Execute types ----
55
56/// Input for the execute preparation use case.
57pub struct ExecuteInput {
58    pub session_id: String,
59    pub request_model: Option<String>,
60    pub request_model_ref: Option<ProviderModelRef>,
61    pub request_provider: Option<String>,
62    pub request_reasoning_effort: Option<ReasoningEffort>,
63    pub request_skill_mode: Option<String>,
64    pub client_sync: Option<ExecuteClientSync>,
65}
66
67/// Client-side sync state sent with execute requests.
68#[derive(Debug, Clone)]
69pub struct ExecuteClientSync {
70    pub client_message_count: usize,
71    pub client_last_message_id: Option<String>,
72    pub client_has_pending_question: bool,
73    pub client_pending_question_tool_call_id: Option<String>,
74}
75
76/// Reason for a sync mismatch between client and server.
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum ExecuteSyncReason {
79    PendingQuestionMismatch,
80    MessageCountMismatch,
81    LastMessageIdMismatch,
82}
83
84impl ExecuteSyncReason {
85    pub fn as_str(&self) -> &'static str {
86        match self {
87            Self::PendingQuestionMismatch => "pending_question_mismatch",
88            Self::MessageCountMismatch => "message_count_mismatch",
89            Self::LastMessageIdMismatch => "last_message_id_mismatch",
90        }
91    }
92}
93
94/// Server-side snapshot of session state used for sync comparison.
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct ServerExecuteSnapshot {
97    pub message_count: usize,
98    pub last_message_id: Option<String>,
99    pub has_pending_question: bool,
100    pub pending_question_tool_call_id: Option<String>,
101    pub has_pending_user_message: bool,
102}
103
104/// Sync info to include in execute responses.
105#[derive(Debug, Clone)]
106pub struct ExecuteSyncInfo {
107    pub need_sync: bool,
108    pub reason: Option<ExecuteSyncReason>,
109    pub server_message_count: usize,
110    pub server_last_message_id: Option<String>,
111    pub has_pending_question: bool,
112    pub pending_question_tool_call_id: Option<String>,
113    pub has_pending_user_message: bool,
114}
115
116impl ServerExecuteSnapshot {
117    pub fn to_sync_info(&self, reason: Option<ExecuteSyncReason>) -> ExecuteSyncInfo {
118        ExecuteSyncInfo {
119            need_sync: reason.is_some(),
120            reason,
121            server_message_count: self.message_count,
122            server_last_message_id: self.last_message_id.clone(),
123            has_pending_question: self.has_pending_question,
124            pending_question_tool_call_id: self.pending_question_tool_call_id.clone(),
125            has_pending_user_message: self.has_pending_user_message,
126        }
127    }
128}
129
130/// Outcome of preparing an execute.
131pub enum ExecutePreparationOutcome {
132    /// Session is ready for agent execution.
133    Ready {
134        session: Box<Session>,
135        effective_model: String,
136        effective_reasoning_effort: Option<ReasoningEffort>,
137        model_source: &'static str,
138        reasoning_source: &'static str,
139        is_child_session: bool,
140    },
141    /// Agent is already running for this session.
142    AlreadyRunning {
143        server_snapshot: ServerExecuteSnapshot,
144    },
145    /// No pending user message, nothing to execute.
146    NoPendingMessage {
147        server_snapshot: ServerExecuteSnapshot,
148    },
149    /// Client/server state mismatch detected.
150    SyncMismatch {
151        reason: ExecuteSyncReason,
152        server_snapshot: ServerExecuteSnapshot,
153    },
154    /// No model could be resolved.
155    ModelRequired,
156    /// Image fallback validation failed.
157    ImageFallbackError(String),
158}
159
160// ---- Respond types ----
161
162/// Input for the respond use case.
163pub struct RespondInput {
164    pub session_id: String,
165    pub user_response: String,
166    pub model: Option<String>,
167    pub model_ref: Option<ProviderModelRef>,
168    pub provider: Option<String>,
169    pub reasoning_effort: Option<ReasoningEffort>,
170}
171
172/// Outcome of submitting a pending response.
173pub struct SubmitResponseOutcome {
174    pub session: Session,
175    pub user_response: String,
176}
177
178// ---- Resume types ----
179
180/// Resolved configuration snapshot for resume execution.
181///
182/// Captures the subset of config needed to spawn a resumed agent loop,
183/// decoupled from the full server config.
184#[derive(Clone)]
185pub struct ResumeConfigSnapshot {
186    pub provider_name: String,
187    pub fast_model: Option<String>,
188    pub fast_model_ref: Option<ProviderModelRef>,
189    pub background_model_provider: Option<Arc<dyn LLMProvider>>,
190    pub disabled_tools: BTreeSet<String>,
191    pub disabled_skill_ids: BTreeSet<String>,
192    pub image_fallback: Option<ImageFallbackConfig>,
193}
194
195/// Outcome of a resume attempt.
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub enum ResumeOutcome {
198    /// Execution spawned successfully.
199    Started,
200    /// A runner is already active for this session.
201    AlreadyRunning,
202    /// No pending user message, nothing to execute.
203    Completed,
204    /// Session not found.
205    NotFound,
206}
207
208impl ResumeOutcome {
209    pub fn as_str(&self) -> &'static str {
210        match self {
211            Self::Started => "started",
212            Self::AlreadyRunning => "already_running",
213            Self::Completed => "completed",
214            Self::NotFound => "error: session not found",
215        }
216    }
217}