Skip to main content

bamboo_engine/session_app/
types.rs

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