bamboo_server/app_state/mod.rs
1//! Unified application state management for the Bamboo server
2//!
3//! This module provides the central AppState struct that consolidates all
4//! server state including sessions, storage, LLM providers, tools, and metrics.
5//!
6//! # Architecture
7//!
8//! The AppState uses a unified design that eliminates the proxy pattern where
9//! web_service created an AgentAppState that called back via HTTP. Instead, it
10//! provides direct access to all components.
11//!
12//! ```text
13//! ┌────────────────────────────────────────────────────┐
14//! │ AppState (Unified) │
15//! │ │
16//! │ ┌──────────────┐ ┌──────────────┐ │
17//! │ │ Config │ │ Provider │ │
18//! │ │ (Hot-reload)│◄────►│ (LLM) │ │
19//! │ └──────────────┘ └──────────────┘ │
20//! │ │
21//! │ ┌──────────────┐ ┌──────────────┐ │
22//! │ │ Sessions │ │ Storage │ │
23//! │ │ (In-memory) │ │ (Persistent)│ │
24//! │ └──────────────┘ └──────────────┘ │
25//! │ │
26//! │ ┌──────────────┐ ┌──────────────┐ │
27//! │ │ Tools │ │ Skills │ │
28//! │ │ (Builtin+MCP)│ │ Manager │ │
29//! │ └──────────────┘ └──────────────┘ │
30//! │ │
31//! │ ┌──────────────┐ ┌──────────────┐ │
32//! │ │ MCP │ │ Metrics │ │
33//! │ │ Manager │ │ Service │ │
34//! │ └──────────────┘ └──────────────┘ │
35//! └────────────────────────────────────────────────────┘
36//! ```
37//!
38//! # Key Features
39//!
40//! - **Hot-reloadable configuration**: Config and provider can be reloaded at runtime
41//! - **Direct provider access**: No HTTP proxy overhead
42//! - **Session management**: In-memory session cache with persistent storage
43//! - **Tool composition**: Combines built-in and MCP tools
44//! - **Metrics collection**: Integrated metrics and event tracking
45//!
46//! # Usage Example
47//!
48//! ```rust,no_run
49//! use bamboo_server::app_state::AppState;
50//! use std::path::PathBuf;
51//!
52//! #[tokio::main]
53//! async fn main() {
54//! // Initialize app state
55//! let app_data_dir = PathBuf::from("/path/to/.bamboo");
56//! let state = AppState::new(app_data_dir)
57//! .await
58//! .expect("failed to initialize app state");
59//!
60//! // Access components
61//! let provider = state.get_provider().await;
62//! let schemas = state.get_all_tool_schemas();
63//!
64//! // Hot reload configuration
65//! state.reload_config().await;
66//! state.reload_provider().await.ok();
67//! }
68//! ```
69
70use std::collections::HashMap;
71use std::path::PathBuf;
72use std::sync::Arc;
73
74use async_trait::async_trait;
75use tokio::sync::{broadcast, RwLock};
76use tokio_util::sync::CancellationToken;
77
78use crate::error::AppError;
79use crate::schedule_app::{ScheduleManager, ScheduleStore};
80use bamboo_agent_core::storage::Storage;
81use bamboo_agent_core::AgentEvent;
82use bamboo_agent_core::{tools::ToolSchema, Message};
83use bamboo_engine::execution::spawn::SpawnScheduler;
84use bamboo_infrastructure::process::registry::ProcessRegistry;
85use bamboo_llm::Config;
86use bamboo_llm::{LLMError, LLMProvider, LLMStream};
87use bamboo_mcp::manager::McpServerManager;
88use bamboo_metrics::metrics_service::MetricsService;
89use bamboo_skills::SkillManager;
90use bamboo_storage::LockedSessionStore;
91use bamboo_storage::SessionStoreV2;
92
93pub use bamboo_memory::memory_store::MemoryStore;
94
95// Context functions moved to bamboo-agent-runtime::context
96pub use bamboo_engine::context::{
97 build_env_prompt_context, build_workspace_prompt_context, workspace_prompt_guidance,
98 DEFAULT_BASE_PROMPT, ENV_CONTEXT_END_MARKER, ENV_CONTEXT_START_MARKER,
99 WORKSPACE_CONTEXT_END_MARKER, WORKSPACE_CONTEXT_PREFIX, WORKSPACE_CONTEXT_START_MARKER,
100};
101
102/// Placeholder provider used when the configured provider cannot be initialized.
103///
104/// This keeps the server usable for configuration/UX flows while ensuring we fail fast
105/// (instead of silently switching to a different provider or model).
106struct UnconfiguredProvider {
107 message: String,
108}
109
110#[async_trait]
111impl LLMProvider for UnconfiguredProvider {
112 async fn chat_stream(
113 &self,
114 _messages: &[Message],
115 _tools: &[ToolSchema],
116 _max_output_tokens: Option<u32>,
117 _model: &str,
118 ) -> bamboo_llm::provider::Result<LLMStream> {
119 Err(LLMError::Auth(format!(
120 "LLM provider is not configured: {}",
121 self.message
122 )))
123 }
124
125 async fn list_models(&self) -> bamboo_llm::provider::Result<Vec<String>> {
126 Err(LLMError::Auth(format!(
127 "LLM provider is not configured: {}",
128 self.message
129 )))
130 }
131}
132
133// Re-export execution types from the runtime crate.
134pub use bamboo_engine::execution::runner_state::{AgentRunner, AgentStatus};
135
136/// Unified application state consolidating web_service and agent/server state
137///
138/// This struct holds all the state needed to run the Bamboo server, including
139/// configuration, LLM providers, sessions, storage, tools, skills, and metrics.
140///
141/// # Design Goals
142///
143/// - **Direct access**: Components are directly accessible without HTTP proxies
144/// - **Hot reload**: Configuration and providers can be reloaded at runtime
145/// - **Thread safety**: Uses Arc<RwLock> for concurrent access
146/// - **Persistence**: Integrates with JsonlStorage for session persistence
147///
148/// # Component Overview
149///
150/// | Component | Purpose | Thread-Safe |
151/// |-----------|---------|--------------|
152/// | `config` | Application configuration | Yes (RwLock) |
153/// | `provider` | Hot-reloadable LLM provider | Yes (RwLock) |
154/// | `sessions` | Active conversation sessions | Yes (RwLock) |
155/// | `storage` | Persistent session storage | Yes (Arc) |
156/// | `tools` | Tool execution (builtin + MCP) | Yes (Arc) |
157/// | `skill_manager` | Skill registry and execution | Yes (Arc) |
158/// | `mcp_manager` | MCP server lifecycle | Yes (Arc) |
159/// | `metrics_service` | Usage metrics collection | Yes (Arc) |
160/// | `agent_runners` | Active agent executions | Yes (RwLock) |
161pub struct AppState {
162 /// Application data directory (configured via `BAMBOO_DATA_DIR`; default `${HOME}/.bamboo`)
163 pub app_data_dir: PathBuf,
164
165 /// Independent Jiandu store shared by every memory surface in this state.
166 pub memory_store: bamboo_memory::memory_store::MemoryStore,
167
168 /// Instance-local, best-effort tool-event boundary. Production publication
169 /// fans into `tool_event_router`; the additional publisher preserves the
170 /// existing test/embedder injection seam. Never process-global.
171 pub tool_event_publisher: Arc<dyn bamboo_plugin_protocol::ToolEventPublisher>,
172
173 /// Server-owned plugin ToolEvent registry and bounded per-sink delivery
174 /// plane. It is always present but inert until reconciliation installs an
175 /// eligible event-sink declaration.
176 pub tool_event_router: Arc<crate::tool_event_router::ToolEventRouter>,
177
178 /// Hot-reloadable application configuration
179 ///
180 /// Can be reloaded from disk at runtime using `reload_config()`.
181 pub config: Arc<RwLock<Config>>,
182
183 /// Process-owned modular configuration authority. Production bootstrap
184 /// always installs one after the recoverable legacy split; injected test
185 /// states may omit it and retain the compatibility-only config path.
186 pub config_facade: Option<Arc<bamboo_config::ConfigFacade>>,
187
188 /// Serializes a config WRITE's whole [in-memory mutation + disk persist] with
189 /// a `reload_config`'s [disk read + in-memory swap], so a reload can never
190 /// observe an in-flight-but-not-yet-persisted update and clobber it with the
191 /// stale disk copy (the residual of #41). It is NOT the `config` RwLock —
192 /// using a separate mutex keeps config READERS (the hot agent-loop path)
193 /// unblocked during a write's disk I/O. #126.
194 pub config_io_lock: Arc<tokio::sync::Mutex<()>>,
195
196 /// Server-owned live configuration watcher and its health envelope.
197 /// The runtime handle keeps the directory watcher tasks alive.
198 pub config_live_health: Arc<std::sync::RwLock<config_runtime::ConfigLiveHealth>>,
199 /// MCP section health is independent from provider health so an invalid or
200 /// degraded MCP candidate cannot make unrelated sections appear unhealthy.
201 pub mcp_config_live_health: Arc<std::sync::RwLock<config_runtime::ConfigLiveHealth>>,
202 #[allow(dead_code)]
203 config_watcher: config_runtime::ConfigWatcherRuntime,
204 /// Project shared-resource watcher. Held for the server lifetime.
205 #[allow(dead_code)]
206 pub(crate) project_resource_watcher: project_watcher::ProjectResourceWatcher,
207
208 /// Encrypted credential authority exposed only through metadata/replace/clear APIs.
209 pub credential_store: Arc<bamboo_config::CredentialStore>,
210
211 /// Shared Remote Cluster Fabric deploy engine (one worker registry across the
212 /// HTTP operator handlers and the `cluster` agent tool).
213 pub fabric_deployer: Arc<bamboo_server_tools::FabricDeployer>,
214
215 /// In-process mailbox bus (broker), when not externally configured. Held so
216 /// it lives for the server's lifetime (dropping it aborts the bus). `None`
217 /// when an external broker is configured or the bus couldn't bind. Never read
218 /// — its only job is to keep the bus task alive until AppState drops.
219 #[allow(dead_code)]
220 embedded_broker: Option<builder::EmbeddedBroker>,
221
222 /// The cluster health monitor sweep. Lives for the server's lifetime (dropping
223 /// it aborts the sweep). `None` when the monitor is disabled
224 /// (`health_interval_secs = 0`). Never read — held only to keep the task alive.
225 #[allow(dead_code)]
226 health_monitor: Option<builder::HealthMonitor>,
227
228 /// Hot-reloadable LLM provider with direct access
229 ///
230 /// This eliminates the proxy pattern where we created an AgentAppState
231 /// that called back to web_service via HTTP. Now we have direct provider access.
232 pub provider: Arc<RwLock<Arc<dyn LLMProvider>>>,
233
234 /// Stable handle that always delegates to the latest provider in `provider`.
235 ///
236 /// This avoids stale provider snapshots after runtime config updates.
237 provider_handle: Arc<dyn LLMProvider>,
238
239 /// Active conversation sessions (in-memory cache)
240 ///
241 /// Maps session IDs to Session objects. Persisted to storage
242 /// via the `storage` field.
243 pub sessions: bamboo_engine::SessionCache,
244
245 /// Persistent storage backend for sessions (V2).
246 ///
247 /// Implemented as folder-per-session with a global `sessions.json` index.
248 pub storage: Arc<dyn Storage>,
249
250 /// Concrete session store implementation (for index/list/cleanup APIs).
251 pub session_store: Arc<SessionStoreV2>,
252
253 /// Durable, Bamboo-home-scoped idempotency receipts for root-session
254 /// creation. Kept outside each target session directory so deleting a
255 /// session cannot erase retry truth during the retention window.
256 pub(crate) session_create_operations:
257 Arc<session_create_operations::SessionCreateOperationStore>,
258
259 /// Short-lived, process-local response receipts for `POST /chat` and
260 /// `POST /execute`. Raw caller keys and request payloads are never stored.
261 pub(crate) mutation_idempotency: Arc<mutation_idempotency::MutationIdempotencyStore>,
262
263 /// Authoritative first-class Project registry and shared-resource paths.
264 pub project_store: Arc<bamboo_projects::ProjectStore>,
265
266 /// Redacted adapter used by HTTP creation paths and the agent runtime to
267 /// resolve one authoritative Project/workspace identity.
268 pub project_context_resolver: Arc<bamboo_engine::project_context::ProjectContextResolver>,
269
270 /// Instance-scoped live workspace providers used for preview and
271 /// post-persistence publication. The equivalent process-global providers
272 /// remain first-registration-wins; retaining this pair prevents parallel
273 /// test AppStates from resolving through a sibling state's config/root.
274 pub(crate) workspace_resolver: bamboo_agent_core::workspace_state::WorkspaceResolver,
275
276 /// Per-session write serialisation + metadata-merge persistence layer.
277 ///
278 /// Wraps the same [`Storage`] as `self.storage`, adding per-session
279 /// `Mutex` guards and authoritative-metadata-group merge semantics.
280 /// Use `self.persistence.merge_save_runtime(...)` for any write that
281 /// may race with a UI metadata update.
282 pub persistence: Arc<LockedSessionStore>,
283
284 /// Durable logical-session delivery plane. These are internal runtime
285 /// capabilities; no public messaging endpoint is registered.
286 pub session_inbox: Arc<dyn bamboo_domain::SessionInboxPort>,
287 pub session_activation_router: Arc<bamboo_engine::SessionActivationRouter>,
288 pub session_messenger: Arc<bamboo_engine::SessionMessenger>,
289
290 /// Framework-owned session coordinator (cache + storage + persistence).
291 /// The canonical load/save coordination lives here in `bamboo-engine`, not
292 /// on `AppState`; the inherent `AppState::load_session`/`save_and_cache_session`
293 /// methods now delegate to it. Holds clones of the same `Arc`s as the
294 /// `sessions`/`storage`/`persistence` fields above.
295 pub session_repo: bamboo_engine::SessionRepository,
296
297 /// Background scheduler for async sub-session spawning.
298 pub spawn_scheduler: Arc<SpawnScheduler>,
299
300 /// Coordinates child completion notifications into parent resume.
301 pub child_completion_coordinator: Arc<bamboo_engine::ChildCompletionCoordinator>,
302
303 /// Spawner for the guardian adversarial-review child, injected into each run
304 /// so the terminal gate can create a read-only reviewer (the engine runner
305 /// cannot construct a child directly — see [`bamboo_engine::GuardianSpawner`]).
306 /// Backed by a dedicated [`crate::tools::ChildSessionAdapter`].
307 pub guardian_spawner: Arc<dyn bamboo_engine::GuardianSpawner>,
308
309 /// Bash self-resume hook (issue #84 Phase 2b). Backed by the same
310 /// [`ChildCompletionCoordinator`] that handles child-completion resumes —
311 /// it polls the live shell registry and resumes a session once all its
312 /// background bash shells finish.
313 pub bash_resume_hook: Arc<dyn bamboo_engine::BashResumeHook>,
314
315 /// Schedule store (timed tasks).
316 pub schedule_store: Arc<ScheduleStore>,
317
318 /// Background schedule manager that triggers scheduled runs.
319 pub schedule_manager: Arc<ScheduleManager>,
320
321 /// bamboo-connect manager (#452 / epic #447): owns every configured IM
322 /// platform's long-poll/dispatch background task. Fully inert (zero
323 /// tasks) when `config.connect.platforms` is empty. Held so its tasks
324 /// live for the server's lifetime (`ConnectManager::drop` aborts them).
325 pub connect_manager: Arc<crate::connect::ConnectManager>,
326
327 /// Tool surface factory providing pre-built tool executors for each session type.
328 ///
329 /// Use `state.tools_for(ToolSurface::Root)` for root sessions,
330 /// `state.tools_for(ToolSurface::Child)` for child sessions, etc.
331 pub tool_factory: crate::tools::ToolSurfaceFactory,
332
333 /// Shared tool-execution permission checker — the same `Arc` the tool
334 /// executors use. Retained so request handlers can record session grants
335 /// when the user approves a permission prompt (see the respond handler).
336 pub permission_checker: Arc<dyn bamboo_tools::permission::PermissionChecker>,
337
338 /// Durable, revisioned authority for permission policy. The checker is
339 /// updated only after a successful commit to this section.
340 pub permission_section: Arc<bamboo_tools::permission::PermissionSection>,
341
342 /// Serializes the complete permission commit + live-checker publication.
343 pub permission_io_lock: Arc<tokio::sync::Mutex<()>>,
344 pub approval_registry:
345 bamboo_engine::external_agents::approval_registry::SharedApprovalRegistry,
346
347 /// Backend notification policy service (preferences + dedup + per-session
348 /// relays). Classifies agent events into `AgentEvent::Notification` for
349 /// clients to render; preferences are persisted server-side.
350 pub notification_service: Arc<bamboo_notification::NotificationService>,
351
352 /// Live SSE/WS client-subscriber counts per session (see
353 /// [`watchers::SessionWatchers`]). Used to suppress a redundant desktop
354 /// popup for categories the UI already surfaces while a client is
355 /// actively watching a session.
356 pub session_watchers: Arc<watchers::SessionWatchers>,
357
358 /// Cancellation tokens for in-flight requests
359 ///
360 /// Maps request/session IDs to their cancellation tokens,
361 /// allowing graceful shutdown of long-running operations.
362 pub cancel_tokens: Arc<RwLock<HashMap<String, CancellationToken>>>,
363
364 /// Cancels the supervised MCP proxy service (issue #47) on shutdown so the
365 /// reconnect/backoff supervisor stops cleanly instead of looping forever
366 /// after an intended stop. Unused when no broker is configured.
367 pub mcp_proxy_shutdown: CancellationToken,
368
369 /// Skill manager for prompt-based skill execution
370 ///
371 /// Manages the skill registry and handles skill lookup,
372 /// validation, and execution.
373 pub skill_manager: Arc<SkillManager>,
374
375 /// Durable, recovered workflow-run boundary. It owns the production engine
376 /// plus server-derived session/catalog trust adapters.
377 pub workflow_runs: crate::workflow::WorkflowRunAccess,
378
379 /// MCP server manager for external tool servers
380 ///
381 /// Handles lifecycle of Model Context Protocol servers,
382 /// including initialization, tool discovery, and shutdown.
383 pub mcp_manager: Arc<McpServerManager>,
384
385 /// Supervises long-running "service" plugins (issue #479, prereq for
386 /// epic #477). Always constructed, fully inert until a plugin install
387 /// (or the boot-time reconcile) calls `start_service`. See
388 /// `crate::service_manager`'s module docs.
389 pub service_manager: Arc<crate::service_manager::ServiceManager>,
390
391 /// Handle to the background boot-time service reconcile pass
392 /// (`plugin_installer::boot_reconcile_services`, spawned fire-and-forget
393 /// by `app_state::builder` — see its comment). It acquires the same
394 /// `plugin_installer::PLUGIN_OP_LOCK` used by install,
395 /// update, and uninstall before reading provenance, so its service/sink
396 /// plan cannot race a newer plugin generation.
397 /// Production code never touches this field; it exists purely as a
398 /// test-only synchronization point (see
399 /// [`AppState::wait_for_boot_reconcile_services`]) so
400 /// `plugin_installer::tests` can deterministically drain that one-shot
401 /// pass before exercising service install/stop/upgrade, instead of
402 /// racing it under CI scheduling jitter (issue #486).
403 #[doc(hidden)]
404 pub boot_reconcile_services_handle: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
405
406 /// Metrics collection and persistence service
407 ///
408 /// Tracks token usage, costs, and performance metrics
409 /// across all sessions.
410 pub metrics_service: Arc<MetricsService>,
411
412 /// Active agent runners indexed by session ID
413 ///
414 /// Each runner manages event broadcasting and cancellation
415 /// for an active agent execution.
416 pub agent_runners: Arc<RwLock<HashMap<String, AgentRunner>>>,
417
418 /// Reference-counted execute handlers still preparing a runner, keyed by
419 /// session. This server-scoped registry closes the durable pending-turn
420 /// expiry race without leaking state across AppState instances/tests.
421 pub(crate) execute_startups: Arc<std::sync::Mutex<HashMap<String, usize>>>,
422
423 /// Session-scoped event streams (long-lived).
424 ///
425 /// Unlike `agent_runners`, these senders exist even when no agent execution is running.
426 /// They are used for:
427 /// - UI subscriptions to `/api/v1/events/{session_id}` (background tasks, etc.)
428 /// - sub-session forwarding (child -> parent)
429 pub session_event_senders: Arc<RwLock<HashMap<String, broadcast::Sender<AgentEvent>>>>,
430
431 /// Account-scoped durable change feed (powers `GET /api/v1/stream`).
432 ///
433 /// Unlike `session_event_senders`, this is a single account-wide sink: all
434 /// durable change events (message appended, session metadata, task updates,
435 /// terminal status) across every session are sequenced, journaled to disk,
436 /// and broadcast here for resumable multi-client sync.
437 pub account_sink: Arc<bamboo_engine::events::AccountEventSink>,
438
439 /// Registry for tracking external processes.
440 pub process_registry: Arc<ProcessRegistry>,
441
442 /// Optional metrics bus for event streaming
443 ///
444 /// When enabled, allows subscribing to metrics events
445 /// in real-time.
446 pub metrics_bus: Option<bamboo_metrics::bus::MetricsBus>,
447
448 /// Unified agent execution runtime holding shared resources.
449 pub agent: Arc<bamboo_engine::Agent>,
450
451 /// Multi-provider registry (used when features.provider_model_ref is enabled).
452 pub provider_registry: Arc<bamboo_llm::ProviderRegistry>,
453
454 /// Provider/model router (used when features.provider_model_ref is enabled).
455 pub provider_router: Arc<bamboo_llm::ProviderModelRouter>,
456
457 /// Unified model catalog service (used when features.provider_model_ref is enabled).
458 pub model_catalog: Arc<bamboo_llm::ModelCatalogService>,
459
460 /// Tracks session ids whose auto-title generation is currently in flight.
461 ///
462 /// Used by [`crate::title_gen`] to dedupe concurrent invocations
463 /// (e.g. multiple chat messages arriving while a regenerate-title request is running).
464 pub title_gen_in_flight: Arc<dashmap::DashSet<String>>,
465
466 /// v2-P2 (#181, slice 2): in-memory one-time pairing codes. A 6-digit numeric
467 /// code (keyed by the code string) maps to an entry holding its expiry. Codes
468 /// are PROCESS-EPHEMERAL — never persisted to `config.json`; a restart drops
469 /// all outstanding codes by design. Keyed by `Instant`-based expiry; expired
470 /// entries are purged opportunistically on insert/lookup.
471 pub pairing_codes: Arc<dashmap::DashMap<String, crate::handlers::settings::PairingCodeEntry>>,
472
473 /// v2-P2 (#181, slice 2): per-process brute-force guard for the public
474 /// code-redemption path (`POST /v2/pair { code }`). A 6-digit code is only
475 /// ~1M space, so a public redeem endpoint is brute-forceable without a guard.
476 /// Tracks recent FAILED code-redemption attempts and a cooldown deadline.
477 pub pairing_code_guard: Arc<crate::handlers::settings::PairingCodeGuard>,
478
479 /// #190: per-client-IP brute-force guard for the public root-password
480 /// endpoints (`POST /v1/bamboo/access/verify` and the root-password path of
481 /// `POST /v2/pair`). Tracks recent FAILED root-password attempts per IP and a
482 /// per-key cooldown; loopback/desktop requests are exempted by the handlers
483 /// so the desktop can never lock itself out. PROCESS-EPHEMERAL — never
484 /// persisted; a restart clears all counters.
485 pub root_password_guard: Arc<crate::handlers::settings::RootPasswordGuard>,
486
487 /// Process-ephemeral credentials for Codex children that route model calls
488 /// through this server. Tokens are hashed in memory and revoked at the end
489 /// of their owning actor activation.
490 pub(crate) codex_run_tokens: Arc<crate::codex_run_tokens::CodexRunTokenRegistry>,
491}
492
493impl AppState {
494 /// Try to claim the title-generation slot for `session_id`.
495 /// Returns `true` on success, `false` if generation is already in flight.
496 pub fn title_gen_acquire(&self, session_id: &str) -> bool {
497 self.title_gen_in_flight.insert(session_id.to_string())
498 }
499
500 /// Release the title-generation slot for `session_id`. Idempotent.
501 pub fn title_gen_release(&self, session_id: &str) {
502 self.title_gen_in_flight.remove(session_id);
503 }
504
505 /// Test-only synchronization point (see
506 /// [`boot_reconcile_services_handle`](Self::boot_reconcile_services_handle)'s
507 /// doc comment): wait for the background boot-time service reconcile
508 /// pass to finish. Idempotent — a second call (or a call after
509 /// production code never having populated the handle) is a no-op.
510 #[doc(hidden)]
511 pub async fn wait_for_boot_reconcile_services(&self) {
512 let handle = self.boot_reconcile_services_handle.lock().await.take();
513 if let Some(handle) = handle {
514 let _ = handle.await;
515 }
516 }
517}
518
519mod agent_session_context;
520mod builder;
521mod config_runtime;
522pub(crate) use config_runtime::ConfigLiveHealth;
523pub(crate) use config_runtime::ConfigSectionMutationError;
524pub(crate) use config_runtime::CredentialBackedResetCommit;
525pub mod init;
526pub mod parent_approval_reviewer;
527mod persistence;
528mod project_watcher;
529mod provider_api;
530pub mod resume_adapter;
531pub mod runner_lifecycle;
532// `pub` (not `pub(crate)`): `ScheduleContext::notification_relay` (a public
533// field of the public `schedule_app::ScheduleContext`) is typed
534// `session_events::NotificationRelayDeps`, so external callers that build a
535// `ScheduleContext` by hand (e.g. integration tests) need to name it.
536pub(crate) mod mutation_idempotency;
537pub(crate) mod session_create_operations;
538pub mod session_events;
539mod session_loader;
540mod tools;
541pub mod watchers;
542
543#[cfg(test)]
544mod tests;
545
546#[derive(Debug, Clone, Copy)]
547pub struct ConfigUpdateEffects {
548 pub reload_provider: bamboo_config::patch::ReloadMode,
549 pub reconcile_mcp: bamboo_config::patch::ReloadMode,
550}
551
552impl Default for ConfigUpdateEffects {
553 fn default() -> Self {
554 Self {
555 reload_provider: bamboo_config::patch::ReloadMode::None,
556 reconcile_mcp: bamboo_config::patch::ReloadMode::None,
557 }
558 }
559}