//! Best-effort MCP prewarming. //! //! A bounded channel coalesces refresh requests. The worker only prepares the //! newest thread state; exact model steps remain the correctness path. use super::*; impl Session { pub(crate) fn request_mcp_runtime_refresh(&self) { self.mark_mcp_runtime_dirty(); self.schedule_mcp_prewarm(); } pub(super) fn start_mcp_prewarm_worker( self: &Arc, requests: async_channel::Receiver<()>, mut auth_changes: tokio::sync::watch::Receiver, ) { let session = Arc::downgrade(self); let shutdown = self.mcp_prewarm_shutdown.clone(); let worker = self.services.runtime_handle.spawn(async move { loop { let auth_changed = tokio::select! { biased; _ = shutdown.cancelled() => break, request = requests.recv() => { if request.is_err() { break; } false }, auth_change = auth_changes.changed() => { if auth_change.is_err() { break; } true }, }; let Some(session) = session.upgrade() else { break; }; if auth_changed { session.mark_mcp_runtime_dirty(); } tokio::select! { biased; _ = shutdown.cancelled() => break, _ = session.refresh_mcp_if_dirty() => {}, } } }); *self .mcp_prewarm_task .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(worker); } pub(super) fn schedule_mcp_prewarm(&self) { let _ = self.mcp_prewarm_tx.try_send(()); } pub(super) async fn stop_mcp_prewarm_worker(&self) { self.mcp_prewarm_shutdown.cancel(); let worker = self .mcp_prewarm_task .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .take(); if let Some(worker) = worker && let Err(error) = worker.await { warn!(%error, "MCP prewarm worker stopped unexpectedly"); } } }