use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock};
pub type SessionId = String;
#[derive(Debug, Clone)]
pub struct SessionContext {
pub session_id: SessionId,
pub role: String,
pub project_id: String,
pub workdir: PathBuf,
}
impl SessionContext {
pub fn new(session_id: SessionId, role: String, project_id: String, workdir: PathBuf) -> Self {
Self {
session_id,
role,
project_id,
workdir,
}
}
pub fn for_session(session_id: &str, role: &str) -> Self {
let project_id = crate::mcp::process::derive_project_id();
let workdir = std::env::current_dir().unwrap_or_default();
Self::new(
session_id.to_string(),
role.to_string(),
project_id,
workdir,
)
}
}
tokio::task_local! {
pub static CURRENT_SESSION_ID: Arc<SessionId>;
}
pub async fn with_session_id<F, T>(session_id: SessionId, f: F) -> T
where
F: std::future::Future<Output = T>,
{
let id = Arc::new(session_id);
CURRENT_SESSION_ID.scope(id, f).await
}
pub fn current_session_id() -> Option<SessionId> {
CURRENT_SESSION_ID.try_with(|id| (**id).clone()).ok()
}
pub fn expect_session_id() -> SessionId {
current_session_id().expect("not in a session context - call with_session_id first")
}
pub type NotificationSender = tokio::sync::mpsc::UnboundedSender<crate::websocket::ServerMessage>;
static NOTIFICATION_SENDERS: RwLock<Option<HashMap<SessionId, NotificationSender>>> =
RwLock::new(None);
pub fn init_notification_registry() {
let mut guard = NOTIFICATION_SENDERS.write().unwrap();
if guard.is_none() {
*guard = Some(HashMap::new());
}
}
pub fn set_notification_sender_for_session(session_id: &SessionId, tx: NotificationSender) {
let mut guard = NOTIFICATION_SENDERS.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.insert(session_id.clone(), tx);
}
pub fn clear_notification_sender_for_session(session_id: &SessionId) {
if let Ok(mut guard) = NOTIFICATION_SENDERS.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
pub fn get_notification_sender() -> Option<NotificationSender> {
let session_id = current_session_id()?;
let guard = NOTIFICATION_SENDERS.read().ok()?;
let registry = guard.as_ref()?;
registry.get(&session_id).cloned()
}
pub fn send_notification(msg: crate::websocket::ServerMessage) {
if let Some(tx) = get_notification_sender() {
let _ = tx.send(msg);
}
}
pub fn register_notification_sender(session_id: SessionId, tx: NotificationSender) {
set_notification_sender_for_session(&session_id, tx);
}
pub fn unregister_notification_sender(session_id: SessionId) {
clear_notification_sender_for_session(&session_id);
}
pub fn get_notification_sender_by_id(session_id: &SessionId) -> Option<NotificationSender> {
let guard = NOTIFICATION_SENDERS.read().ok()?;
let registry = guard.as_ref()?;
registry.get(session_id).cloned()
}
static SESSION_WORKDIRS: RwLock<Option<HashMap<SessionId, (PathBuf, PathBuf)>>> = RwLock::new(None);
pub fn set_session_workdir(session_id: &SessionId, path: PathBuf) {
let mut guard = SESSION_WORKDIRS.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.insert(session_id.clone(), (path.clone(), path));
}
pub fn set_current_workdir(session_id: &SessionId, path: PathBuf) {
if let Ok(mut guard) = SESSION_WORKDIRS.write() {
if let Some(registry) = guard.as_mut() {
if let Some((session, _)) = registry.get(session_id) {
registry.insert(session_id.clone(), (session.clone(), path));
}
}
}
}
pub fn get_current_workdir(session_id: &SessionId) -> Option<PathBuf> {
let guard = SESSION_WORKDIRS.read().ok()?;
let registry = guard.as_ref()?;
registry.get(session_id).map(|(_, current)| current.clone())
}
pub fn get_session_workdir_anchor(session_id: &SessionId) -> Option<PathBuf> {
let guard = SESSION_WORKDIRS.read().ok()?;
let registry = guard.as_ref()?;
registry.get(session_id).map(|(session, _)| session.clone())
}
pub fn clear_session_workdir(session_id: &SessionId) {
if let Ok(mut guard) = SESSION_WORKDIRS.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
static SESSION_ROLES: RwLock<Option<HashMap<SessionId, String>>> = RwLock::new(None);
use crate::config::Config;
static SESSION_CONFIGS: RwLock<Option<HashMap<SessionId, Config>>> = RwLock::new(None);
pub fn set_session_role(session_id: &SessionId, role: &str) {
let mut guard = SESSION_ROLES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.insert(session_id.clone(), role.to_string());
}
pub fn get_session_role(session_id: &SessionId) -> Option<String> {
let guard = SESSION_ROLES.read().ok()?;
let registry = guard.as_ref()?;
registry.get(session_id).cloned()
}
pub fn clear_session_role(session_id: &SessionId) {
if let Ok(mut guard) = SESSION_ROLES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
pub fn set_session_config(session_id: &SessionId, config: &Config) {
let mut guard = SESSION_CONFIGS.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.insert(session_id.clone(), config.clone());
}
pub fn get_session_config(session_id: &SessionId) -> Option<Config> {
let guard = SESSION_CONFIGS.read().ok()?;
let registry = guard.as_ref()?;
registry.get(session_id).cloned()
}
pub fn clear_session_config(session_id: &SessionId) {
if let Ok(mut guard) = SESSION_CONFIGS.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
use crate::mcp::core::plan::memory_storage::MemoryPlanStorage;
static PLAN_REGISTRIES: RwLock<Option<HashMap<SessionId, Arc<Mutex<MemoryPlanStorage>>>>> =
RwLock::new(None);
pub fn get_plan_storage(session_id: &SessionId) -> Arc<Mutex<MemoryPlanStorage>> {
{
let guard = PLAN_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some(storage) = registry.get(session_id) {
return storage.clone();
}
}
}
let mut guard = PLAN_REGISTRIES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let storage = Arc::new(Mutex::new(MemoryPlanStorage::new()));
registry.insert(session_id.clone(), storage.clone());
storage
}
pub fn clear_plan_storage(session_id: &SessionId) {
if let Ok(mut guard) = PLAN_REGISTRIES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
static TASK_START_INDICES: RwLock<Option<HashMap<SessionId, usize>>> = RwLock::new(None);
pub fn set_task_start_index(session_id: &SessionId, index: usize) {
let mut guard = TASK_START_INDICES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.insert(session_id.clone(), index);
crate::log_debug!(
"Plan task start index set to: {} for session: {}",
index,
session_id
);
}
pub fn get_task_start_index(session_id: &SessionId) -> Option<usize> {
let guard = TASK_START_INDICES.read().ok()?;
let registry = guard.as_ref()?;
registry.get(session_id).copied()
}
pub fn take_task_start_index(session_id: &SessionId) -> Option<usize> {
let mut guard = TASK_START_INDICES.write().ok()?;
let registry = guard.as_mut()?;
registry.remove(session_id)
}
pub fn clear_task_start_index(session_id: &SessionId) {
if let Ok(mut guard) = TASK_START_INDICES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
use crate::mcp::core::schedule::storage::ScheduleStore;
static SCHEDULE_REGISTRIES: RwLock<Option<HashMap<SessionId, Arc<Mutex<ScheduleStore>>>>> =
RwLock::new(None);
pub fn get_schedule_storage(session_id: &SessionId) -> Arc<Mutex<ScheduleStore>> {
{
let guard = SCHEDULE_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some(storage) = registry.get(session_id) {
return storage.clone();
}
}
}
let mut guard = SCHEDULE_REGISTRIES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let storage = Arc::new(Mutex::new(ScheduleStore::new()));
registry.insert(session_id.clone(), storage.clone());
storage
}
use tokio::sync::Notify;
static SCHEDULE_NOTIFIES: RwLock<Option<HashMap<SessionId, Arc<Notify>>>> = RwLock::new(None);
pub fn get_schedule_notify(session_id: &SessionId) -> Arc<Notify> {
{
let guard = SCHEDULE_NOTIFIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some(notify) = registry.get(session_id) {
return notify.clone();
}
}
}
let mut guard = SCHEDULE_NOTIFIES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let notify = Arc::new(Notify::new());
registry.insert(session_id.clone(), notify.clone());
notify
}
pub fn notify_schedule_change(session_id: &SessionId) {
let guard = SCHEDULE_NOTIFIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some(notify) = registry.get(session_id) {
notify.notify_one();
}
}
}
pub fn clear_schedule_notify(session_id: &SessionId) {
if let Ok(mut guard) = SCHEDULE_NOTIFIES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
pub fn clear_schedule_storage(session_id: &SessionId) {
if let Ok(mut guard) = SCHEDULE_REGISTRIES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
static HINTS_REGISTRIES: RwLock<Option<HashMap<SessionId, Vec<String>>>> = RwLock::new(None);
pub fn push_hint_for_session(session_id: &SessionId, hint: String) {
let mut guard = HINTS_REGISTRIES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.entry(session_id.clone()).or_default().push(hint);
}
pub fn drain_hints_for_session(session_id: &SessionId) -> Vec<String> {
let mut guard = HINTS_REGISTRIES.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some(hints) = registry.get_mut(session_id) {
let mut seen = std::collections::HashSet::new();
return hints.drain(..).filter(|h| seen.insert(h.clone())).collect();
}
}
Vec::new()
}
pub fn has_hints_for_session(session_id: &SessionId) -> bool {
let guard = HINTS_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some(hints) = registry.get(session_id) {
return !hints.is_empty();
}
}
false
}
pub fn clear_hints_for_session(session_id: &SessionId) {
if let Ok(mut guard) = HINTS_REGISTRIES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
use crate::mcp::core::dynamic_agents::DynamicAgentConfig;
type DynamicAgentState = (HashMap<String, DynamicAgentConfig>, HashMap<String, bool>);
static DYNAMIC_AGENT_REGISTRIES: RwLock<Option<HashMap<SessionId, DynamicAgentState>>> =
RwLock::new(None);
pub fn register_dynamic_agent_for_session(session_id: &SessionId, agent: DynamicAgentConfig) {
let agent_name = agent.name.clone();
let mut guard = DYNAMIC_AGENT_REGISTRIES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let (agents, enabled) = registry
.entry(session_id.clone())
.or_insert_with(|| (HashMap::new(), HashMap::new()));
agents.insert(agent_name.clone(), agent);
enabled.insert(agent_name, false);
}
pub fn enable_dynamic_agent_for_session(session_id: &SessionId, agent_name: &str) -> bool {
let mut guard = DYNAMIC_AGENT_REGISTRIES.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some((agents, enabled)) = registry.get_mut(session_id) {
if agents.contains_key(agent_name) {
enabled.insert(agent_name.to_string(), true);
return true;
}
}
}
false
}
pub fn disable_dynamic_agent_for_session(session_id: &SessionId, agent_name: &str) -> bool {
let mut guard = DYNAMIC_AGENT_REGISTRIES.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some((_, enabled)) = registry.get_mut(session_id) {
if enabled.contains_key(agent_name) {
enabled.insert(agent_name.to_string(), false);
return true;
}
}
}
false
}
pub fn remove_dynamic_agent_for_session(
session_id: &SessionId,
agent_name: &str,
) -> Option<DynamicAgentConfig> {
let mut guard = DYNAMIC_AGENT_REGISTRIES.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some((agents, enabled)) = registry.get_mut(session_id) {
enabled.remove(agent_name);
return agents.remove(agent_name);
}
}
None
}
pub fn get_dynamic_agents_for_session(
session_id: &SessionId,
) -> Vec<(String, DynamicAgentConfig, bool)> {
let guard = DYNAMIC_AGENT_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((agents, enabled)) = registry.get(session_id) {
return agents
.iter()
.map(|(name, config)| {
let is_enabled = *enabled.get(name).unwrap_or(&false);
(name.clone(), config.clone(), is_enabled)
})
.collect();
}
}
Vec::new()
}
pub fn has_dynamic_agent(session_id: &SessionId, agent_name: &str) -> bool {
let guard = DYNAMIC_AGENT_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((agents, _)) = registry.get(session_id) {
return agents.contains_key(agent_name);
}
}
false
}
pub fn is_dynamic_agent_enabled(session_id: &SessionId, agent_name: &str) -> bool {
let guard = DYNAMIC_AGENT_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((_, enabled)) = registry.get(session_id) {
return *enabled.get(agent_name).unwrap_or(&false);
}
}
false
}
pub fn clear_dynamic_agents_for_session(session_id: &SessionId) {
if let Ok(mut guard) = DYNAMIC_AGENT_REGISTRIES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
use crate::config::McpServerConfig;
use crate::mcp::McpFunction;
type DynamicServerState = (
HashMap<String, McpServerConfig>,
HashMap<String, Vec<McpFunction>>,
HashMap<String, bool>,
);
static DYNAMIC_SERVER_REGISTRIES: RwLock<Option<HashMap<SessionId, DynamicServerState>>> =
RwLock::new(None);
pub fn register_dynamic_server_for_session(session_id: &SessionId, server: McpServerConfig) {
let server_name = server.name().to_string();
let mut guard = DYNAMIC_SERVER_REGISTRIES.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let (servers, _functions, enabled) = registry
.entry(session_id.clone())
.or_insert_with(|| (HashMap::new(), HashMap::new(), HashMap::new()));
servers.insert(server_name.clone(), server);
enabled.insert(server_name, false);
}
pub fn enable_dynamic_server_for_session(
session_id: &SessionId,
server_name: &str,
funcs: Vec<McpFunction>,
) -> bool {
let mut guard = DYNAMIC_SERVER_REGISTRIES.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some((servers, functions, enabled)) = registry.get_mut(session_id) {
if servers.contains_key(server_name) {
functions.insert(server_name.to_string(), funcs);
enabled.insert(server_name.to_string(), true);
return true;
}
}
}
false
}
pub fn disable_dynamic_server_for_session(session_id: &SessionId, server_name: &str) -> bool {
let mut guard = DYNAMIC_SERVER_REGISTRIES.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some((_, functions, enabled)) = registry.get_mut(session_id) {
if enabled.contains_key(server_name) {
enabled.insert(server_name.to_string(), false);
functions.remove(server_name);
return true;
}
}
}
false
}
pub fn remove_dynamic_server_for_session(
session_id: &SessionId,
server_name: &str,
) -> Option<McpServerConfig> {
let mut guard = DYNAMIC_SERVER_REGISTRIES.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some((servers, functions, enabled)) = registry.get_mut(session_id) {
functions.remove(server_name);
enabled.remove(server_name);
return servers.remove(server_name);
}
}
None
}
pub fn get_dynamic_servers_for_session(session_id: &SessionId) -> Vec<(String, Vec<String>, bool)> {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((servers, _functions, enabled)) = registry.get(session_id) {
return servers
.iter()
.map(|(name, config)| {
let tools = config.tools().to_vec();
let is_enabled = *enabled.get(name).unwrap_or(&false);
(name.clone(), tools, is_enabled)
})
.collect();
}
}
Vec::new()
}
pub fn get_dynamic_server_functions_for_session(
session_id: &SessionId,
server_name: &str,
) -> Option<Vec<McpFunction>> {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((_, functions, _)) = registry.get(session_id) {
return functions.get(server_name).cloned();
}
}
None
}
pub fn get_all_dynamic_server_configs_for_session(session_id: &SessionId) -> Vec<McpServerConfig> {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((servers, _, enabled)) = registry.get(session_id) {
return servers
.iter()
.filter(|(name, _)| *enabled.get(*name).unwrap_or(&false))
.map(|(_, config)| config.clone())
.collect();
}
}
Vec::new()
}
pub fn get_all_dynamic_server_functions_for_session(session_id: &SessionId) -> Vec<McpFunction> {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((_, functions, enabled)) = registry.get(session_id) {
return functions
.iter()
.filter(|(name, _)| *enabled.get(*name).unwrap_or(&false))
.flat_map(|(_, funcs)| funcs.iter().cloned())
.collect();
}
}
Vec::new()
}
pub fn has_dynamic_server(session_id: &SessionId, server_name: &str) -> bool {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((servers, _, _)) = registry.get(session_id) {
return servers.contains_key(server_name);
}
}
false
}
pub fn is_dynamic_server_tool(session_id: &SessionId, tool_name: &str) -> bool {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((_, functions, _)) = registry.get(session_id) {
return functions
.values()
.any(|funcs| funcs.iter().any(|f| f.name == tool_name));
}
}
false
}
pub fn get_dynamic_server_name_by_tool(session_id: &SessionId, tool_name: &str) -> Option<String> {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((_, functions, _)) = registry.get(session_id) {
for (server_name, funcs) in functions {
if funcs.iter().any(|f| f.name == tool_name) {
return Some(server_name.clone());
}
}
}
}
None
}
pub fn get_dynamic_server_for_session(
session_id: &SessionId,
server_name: &str,
) -> Option<(McpServerConfig, bool)> {
let guard = DYNAMIC_SERVER_REGISTRIES.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some((servers, _, enabled)) = registry.get(session_id) {
if let Some(config) = servers.get(server_name) {
let is_enabled = *enabled.get(server_name).unwrap_or(&false);
return Some((config.clone(), is_enabled));
}
}
}
None
}
pub fn clear_dynamic_servers_for_session(session_id: &SessionId) {
if let Ok(mut guard) = DYNAMIC_SERVER_REGISTRIES.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
use crate::session::background_jobs::BackgroundJobManager;
static JOB_MANAGERS: RwLock<Option<HashMap<SessionId, Arc<BackgroundJobManager>>>> =
RwLock::new(None);
pub fn init_job_manager_for_session(session_id: &SessionId) {
let max_concurrent = std::thread::available_parallelism()
.map(|p| p.get())
.unwrap_or(4);
let manager = BackgroundJobManager::new(max_concurrent);
let mut guard = JOB_MANAGERS.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.insert(session_id.clone(), Arc::new(manager));
}
pub fn get_job_manager_for_session() -> Option<Arc<BackgroundJobManager>> {
let session_id = current_session_id()?;
let guard = JOB_MANAGERS.read().ok()?;
let registry = guard.as_ref()?;
registry.get(&session_id).cloned()
}
pub fn kill_all_jobs_for_session(session_id: &SessionId) {
if let Ok(mut guard) = JOB_MANAGERS.write() {
if let Some(registry) = guard.as_mut() {
if let Some(manager) = registry.remove(session_id) {
manager.kill_all();
}
}
}
}
pub fn clear_job_manager_for_session(session_id: &SessionId) {
kill_all_jobs_for_session(session_id);
}
static ACTIVE_SKILLS: RwLock<Option<HashMap<SessionId, Vec<String>>>> = RwLock::new(None);
pub fn add_active_skill(session_id: &SessionId, skill_name: &str) {
let mut guard = ACTIVE_SKILLS.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let skills = registry.entry(session_id.clone()).or_default();
if !skills.contains(&skill_name.to_string()) {
skills.push(skill_name.to_string());
}
}
pub fn remove_active_skill(session_id: &SessionId, skill_name: &str) {
let mut guard = ACTIVE_SKILLS.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some(skills) = registry.get_mut(session_id) {
skills.retain(|s| s != skill_name);
}
}
}
pub fn get_active_skills(session_id: &SessionId) -> Vec<String> {
let guard = ACTIVE_SKILLS.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some(skills) = registry.get(session_id) {
return skills.clone();
}
}
Vec::new()
}
pub fn has_active_skill(session_id: &SessionId, skill_name: &str) -> bool {
let guard = ACTIVE_SKILLS.read().unwrap();
if let Some(registry) = guard.as_ref() {
if let Some(skills) = registry.get(session_id) {
return skills.iter().any(|s| s == skill_name);
}
}
false
}
pub fn clear_active_skills(session_id: &SessionId) {
if let Ok(mut guard) = ACTIVE_SKILLS.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
type SkillServerMap = HashMap<SessionId, HashMap<String, Vec<String>>>;
static CAPABILITY_REFCOUNTS: RwLock<Option<HashMap<SessionId, HashMap<String, usize>>>> =
RwLock::new(None);
static SKILL_CAPABILITY_SERVERS: RwLock<Option<SkillServerMap>> = RwLock::new(None);
pub fn increment_capability_refcount(session_id: &SessionId, server_name: &str) -> usize {
let mut guard = CAPABILITY_REFCOUNTS.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let counts = registry.entry(session_id.clone()).or_default();
let count = counts.entry(server_name.to_string()).or_insert(0);
*count += 1;
*count
}
pub fn decrement_capability_refcount(session_id: &SessionId, server_name: &str) -> usize {
let mut guard = CAPABILITY_REFCOUNTS.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some(counts) = registry.get_mut(session_id) {
if let Some(count) = counts.get_mut(server_name) {
*count = count.saturating_sub(1);
let result = *count;
if result == 0 {
counts.remove(server_name);
}
return result;
}
}
}
0
}
pub fn set_skill_capability_servers(
session_id: &SessionId,
skill_name: &str,
servers: Vec<String>,
) {
if servers.is_empty() {
return;
}
let mut guard = SKILL_CAPABILITY_SERVERS.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
let map = registry.entry(session_id.clone()).or_default();
map.insert(skill_name.to_string(), servers);
}
pub fn take_skill_capability_servers(session_id: &SessionId, skill_name: &str) -> Vec<String> {
let mut guard = SKILL_CAPABILITY_SERVERS.write().unwrap();
if let Some(registry) = guard.as_mut() {
if let Some(map) = registry.get_mut(session_id) {
return map.remove(skill_name).unwrap_or_default();
}
}
Vec::new()
}
pub fn clear_capability_refcounts(session_id: &SessionId) {
if let Ok(mut guard) = CAPABILITY_REFCOUNTS.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
pub fn clear_skill_capability_servers(session_id: &SessionId) {
if let Ok(mut guard) = SKILL_CAPABILITY_SERVERS.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
static SKILL_COMPRESS_REQUESTED: RwLock<Option<HashMap<SessionId, bool>>> = RwLock::new(None);
pub fn request_skill_compression(session_id: &SessionId) {
let mut guard = SKILL_COMPRESS_REQUESTED.write().unwrap();
let registry = guard.get_or_insert_with(HashMap::new);
registry.insert(session_id.clone(), true);
}
pub fn take_skill_compress_request(session_id: &SessionId) -> bool {
let mut guard = SKILL_COMPRESS_REQUESTED.write().unwrap();
if let Some(registry) = guard.as_mut() {
return registry.remove(session_id).unwrap_or(false);
}
false
}
fn clear_skill_compress_requests(session_id: &SessionId) {
if let Ok(mut guard) = SKILL_COMPRESS_REQUESTED.write() {
if let Some(registry) = guard.as_mut() {
registry.remove(session_id);
}
}
}
pub fn cleanup_session(session_id: &SessionId) {
clear_notification_sender_for_session(session_id);
clear_session_workdir(session_id);
clear_session_role(session_id);
clear_session_config(session_id);
clear_plan_storage(session_id);
clear_task_start_index(session_id);
clear_schedule_storage(session_id);
clear_hints_for_session(session_id);
clear_dynamic_agents_for_session(session_id);
clear_dynamic_servers_for_session(session_id);
clear_job_manager_for_session(session_id);
clear_active_skills(session_id);
clear_capability_refcounts(session_id);
clear_skill_capability_servers(session_id);
clear_skill_compress_requests(session_id);
crate::session::inbox::clear_inbox_for_session(session_id);
crate::mcp::core::plan::compression::cleanup_compression_state(session_id);
clear_schedule_notify(session_id);
}
pub fn init_session_services(role: &str) {
crate::session::inbox::init_inbox_for_session();
crate::mcp::agent::functions::init_job_manager();
let domain = role.split(':').next().unwrap_or(role);
crate::mcp::core::skill_auto::init_pool(domain);
}