1use super::init::{
2 build_provider_handles, build_schedule_manager, build_spawn_scheduler, init_mcp_manager,
3 init_metrics_service, init_schedule_store, init_skill_manager, init_storage,
4 load_permission_checker, spawn_runner_cleanup_task,
5};
6use super::tools::{build_base_tools, build_root_tools};
7use super::*;
8use crate::tools::OptionalSubagentModelResolver;
9use bamboo_agent_core::storage::Storage;
10
11impl AppState {
12 pub async fn new(bamboo_home_dir: PathBuf) -> Result<Self, AppError> {
47 bamboo_infrastructure::paths::init_bamboo_dir(bamboo_home_dir.clone());
50
51 let config = Config::from_data_dir(Some(bamboo_home_dir.clone()));
53
54 let provider_registry = match bamboo_infrastructure::ProviderRegistry::from_config(
55 &config,
56 bamboo_home_dir.clone(),
57 )
58 .await
59 {
60 Ok(registry) => Arc::new(registry),
61 Err(e) => {
62 tracing::error!("Failed to create provider registry: {}", e);
63 Arc::new(
64 bamboo_infrastructure::ProviderRegistry::from_config(
65 &Config::default(),
66 bamboo_home_dir.clone(),
67 )
68 .await
69 .expect("Cannot create even an empty provider registry"),
70 )
71 }
72 };
73
74 let provider = provider_registry.get_default().unwrap_or_else(|| {
75 let default_provider_name = provider_registry.default_provider_name();
76 let message = if config.has_provider_instances() {
77 format!(
78 "Default provider instance '{}' is not available or failed to initialize",
79 default_provider_name
80 )
81 } else {
82 format!(
83 "Provider '{}' is not available or failed to initialize",
84 config.provider
85 )
86 };
87 Arc::new(UnconfiguredProvider { message }) as Arc<dyn LLMProvider>
88 });
89
90 Self::new_with_provider(bamboo_home_dir, config, provider).await
91 }
92
93 pub async fn new_with_provider(
108 bamboo_home_dir: PathBuf,
109 config: Config,
110 provider: Arc<dyn LLMProvider>,
111 ) -> Result<Self, AppError> {
112 bamboo_agent_core::workspace_state::set_default_workspace_provider(|| {
116 bamboo_infrastructure::Config::from_data_dir(Some(
117 bamboo_infrastructure::paths::bamboo_dir(),
118 ))
119 .get_default_work_area_path()
120 });
121
122 let data_dir = bamboo_home_dir.clone();
123 let (session_store, storage) = init_storage(&data_dir).await?;
124 let persistence = Arc::new(LockedSessionStore::new(storage.clone()));
125
126 let sessions: Arc<RwLock<HashMap<String, bamboo_agent_core::Session>>> =
128 Arc::new(RwLock::new(HashMap::new()));
129
130 let config = Arc::new(RwLock::new(config));
131
132 let permission_checker = load_permission_checker(&bamboo_home_dir).await;
133 let mcp_manager = init_mcp_manager(config.clone());
134 let skill_manager = init_skill_manager(&data_dir).await;
135 let metrics_service = init_metrics_service(&data_dir).await?;
136
137 let startup_sessions = {
138 let entries = session_store.list_index_entries().await;
139 let mut sessions = Vec::new();
140 for entry in entries {
141 if let Some(session) = session_store
142 .load_session(&entry.id)
143 .await
144 .map_err(AppError::StorageError)?
145 {
146 sessions.push(session);
147 }
148 }
149 sessions
150 };
151 metrics_service
152 .reconcile_startup_sessions(startup_sessions, &[])
153 .await
154 .map_err(|error| {
155 AppError::InternalError(anyhow::anyhow!(
156 "Failed to reconcile stale metrics state on startup: {error}"
157 ))
158 })?;
159
160 let agent_runners: Arc<RwLock<HashMap<String, AgentRunner>>> =
161 Arc::new(RwLock::new(HashMap::new()));
162 spawn_runner_cleanup_task(agent_runners.clone(), None);
163
164 let process_registry = Arc::new(ProcessRegistry::new());
165 let (provider_lock, provider_handle) = build_provider_handles(provider);
166
167 let config_snapshot = config.read().await;
169 let provider_registry = match bamboo_infrastructure::ProviderRegistry::from_config(
170 &config_snapshot,
171 bamboo_home_dir.clone(),
172 )
173 .await
174 {
175 Ok(registry) => Arc::new(registry),
176 Err(e) => {
177 tracing::error!("Failed to create provider registry: {}", e);
178 Arc::new(
179 bamboo_infrastructure::ProviderRegistry::from_config(
180 &Config::default(),
181 bamboo_home_dir.clone(),
182 )
183 .await
184 .expect("Cannot create even an empty provider registry"),
185 )
186 }
187 };
188 drop(config_snapshot);
189
190 let provider_router = Arc::new(bamboo_infrastructure::ProviderModelRouter::new(
191 provider_registry.clone(),
192 ));
193 let model_catalog = Arc::new(bamboo_infrastructure::ModelCatalogService::new(
194 provider_registry.clone(),
195 ));
196
197 let base_tools = build_base_tools(
198 config.clone(),
199 permission_checker,
200 mcp_manager.clone(),
201 skill_manager.clone(),
202 storage.clone(),
203 persistence.clone(),
204 sessions.clone(),
205 bamboo_home_dir.clone(),
206 );
207
208 let session_event_senders: Arc<RwLock<HashMap<String, broadcast::Sender<AgentEvent>>>> =
210 Arc::new(RwLock::new(HashMap::new()));
211
212 let account_sink = crate::events::AccountEventSink::new(data_dir.join("events"))
215 .map_err(|e| {
216 AppError::InternalError(anyhow::anyhow!(
217 "failed to initialize account change-feed journal: {e}"
218 ))
219 })?;
220
221 let subagent_profiles = crate::subagent_profiles::load_registry(&bamboo_home_dir, None)
229 .map_err(|e| {
230 crate::error::AppError::InternalError(anyhow::anyhow!(
231 "failed to load subagent profile registry: {e}"
232 ))
233 })?;
234
235 let child_tools: Arc<dyn bamboo_agent_core::tools::ToolExecutor> =
240 Arc::new(crate::tools::PolicyAwareToolExecutor::new(
241 base_tools.clone(),
242 subagent_profiles.clone(),
243 sessions.clone(),
244 ));
245
246 let agent = Arc::new(
251 bamboo_engine::Agent::builder()
252 .storage(storage.clone())
253 .persistence(persistence.clone())
254 .attachment_reader(session_store.clone())
255 .skill_manager(skill_manager.clone())
256 .metrics_collector(metrics_service.collector())
257 .config(config.clone())
258 .provider(provider_handle.clone())
259 .default_tools(base_tools.clone())
260 .build()
261 .expect("agent runtime should be fully configured"),
262 );
263
264 let child_completion_coordinator = Arc::new(
265 super::child_completion_coordinator::ChildCompletionCoordinator::new(
266 storage.clone(),
267 persistence.clone(),
268 sessions.clone(),
269 agent_runners.clone(),
270 session_event_senders.clone(),
271 agent.clone(),
272 config.clone(),
273 provider_registry.clone(),
274 provider_router.clone(),
275 data_dir.clone(),
276 Some(account_sink.inbox()),
277 ),
278 );
279
280 let config_snapshot = config.read().await.clone();
282 let external_runner =
283 crate::external_agents::runtime::build_external_child_runner(&config_snapshot);
284 let spawn_scheduler = build_spawn_scheduler(
285 agent.clone(),
286 child_tools,
287 sessions.clone(),
288 agent_runners.clone(),
289 session_event_senders.clone(),
290 external_runner,
291 Some(provider_router.clone()),
292 Some(child_completion_coordinator.clone()),
293 Some(data_dir.clone()),
294 Some(account_sink.inbox()),
295 );
296
297 let tools_with_task = base_tools.clone();
298
299 let schedule_store = init_schedule_store(&data_dir).await?;
300 let schedule_manager = build_schedule_manager(
301 schedule_store.clone(),
302 agent.clone(),
303 tools_with_task.clone(),
304 sessions.clone(),
305 agent_runners.clone(),
306 session_event_senders.clone(),
307 persistence.clone(),
308 config.clone(),
309 provider_registry.clone(),
310 Some(data_dir.clone()),
311 Some(account_sink.inbox()),
312 );
313
314 bamboo_engine::auto_dream::spawn_auto_dream_task(
315 bamboo_engine::auto_dream::AutoDreamContext {
316 session_store: session_store.clone(),
317 storage: storage.clone(),
318 provider: provider_handle.clone(),
319 config: config.clone(),
320 provider_registry: provider_registry.clone(),
321 },
322 );
323
324 let config_for_resolver = config.clone();
325 let subagent_model_resolver: OptionalSubagentModelResolver = {
326 let registry = provider_registry.clone();
327 Some(Arc::new(
328 move |subagent_type: String| -> futures::future::BoxFuture<
329 'static,
330 Option<bamboo_domain::ProviderModelRef>,
331 > {
332 let config_for_resolver = config_for_resolver.clone();
333 let registry = registry.clone();
334 Box::pin(async move {
335 let config_snap = config_for_resolver.read().await.clone();
336 bamboo_engine::model_config_helper::resolve_subagent_model_ref(
337 &config_snap,
338 &config_snap.provider,
339 ®istry,
340 &subagent_type,
341 )
342 })
343 },
344 ))
345 };
346
347 let tools = build_root_tools(
348 tools_with_task.clone(),
349 schedule_store.clone(),
350 schedule_manager.clone(),
351 session_store.clone(),
352 storage.clone(),
353 persistence.clone(),
354 spawn_scheduler.clone(),
355 sessions.clone(),
356 agent_runners.clone(),
357 session_event_senders.clone(),
358 subagent_model_resolver,
359 config.clone(),
360 subagent_profiles.clone(),
361 );
362
363 child_completion_coordinator
364 .set_root_tools(tools.clone())
365 .await;
366
367 let tool_factory =
368 crate::tools::ToolSurfaceFactory::new(base_tools, tools_with_task, tools);
369
370 Ok(Self {
371 app_data_dir: bamboo_home_dir,
372 config,
373 provider: provider_lock,
374 provider_handle,
375 sessions,
376 storage,
377 session_store,
378 persistence,
379 spawn_scheduler,
380 child_completion_coordinator,
381 schedule_store,
382 schedule_manager,
383 tool_factory,
384 subagent_profiles,
385 cancel_tokens: Arc::new(RwLock::new(HashMap::new())),
386 skill_manager,
387 mcp_manager,
388 metrics_service,
389 agent_runners,
390 session_event_senders,
391 account_sink,
392 process_registry,
393 metrics_bus: None, agent,
395 provider_registry,
396 provider_router,
397 model_catalog,
398 title_gen_in_flight: Arc::new(dashmap::DashSet::new()),
399 })
400 }
401}