Skip to main content

bamboo_engine/session_app/
types.rs

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