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