use crate::runtime::entry::RuntimeEntryDescriptor;
use crate::skill::manager::{SkillLifecycleAction, SkillManagementAuthority, SkillOperationPlane};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::sync::{Arc, Mutex, OnceLock};
pub type RuntimeSkillLifecycleCallback = Arc<dyn Fn(&RuntimeSkillLifecycleEvent) + Send + Sync>;
pub type RuntimeEntryRegistryCallback = Arc<dyn Fn(&RuntimeEntryRegistryDelta) + Send + Sync>;
pub type RuntimeSkillManagementCallback =
Arc<dyn Fn(&RuntimeSkillManagementRequest) -> Result<Value, String> + Send + Sync>;
pub type RuntimeHostToolCallback =
Arc<dyn Fn(&RuntimeHostToolRequest) -> Result<Value, String> + Send + Sync>;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeHostToolAction {
List,
Has,
Call,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RuntimeHostToolRequest {
pub action: RuntimeHostToolAction,
pub tool_name: Option<String>,
pub args: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeSkillManagementAction {
Install,
Update,
Uninstall,
Enable,
Disable,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RuntimeSkillManagementRequest {
pub action: RuntimeSkillManagementAction,
pub authority: SkillManagementAuthority,
pub input: Value,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct RuntimeSkillLifecycleEvent {
pub plane: SkillOperationPlane,
pub action: SkillLifecycleAction,
pub skill_id: String,
pub root_name: Option<String>,
pub skill_dir: Option<String>,
pub status: String,
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct RuntimeEntryRegistryDelta {
pub added_entries: Vec<RuntimeEntryDescriptor>,
pub removed_entry_names: Vec<String>,
pub updated_entries: Vec<RuntimeEntryDescriptor>,
}
pub fn set_skill_lifecycle_callback(callback: Option<RuntimeSkillLifecycleCallback>) {
let registry = skill_lifecycle_callback_registry();
let mut guard = registry.lock().unwrap();
*guard = callback;
}
pub fn set_entry_registry_callback(callback: Option<RuntimeEntryRegistryCallback>) {
let registry = entry_registry_callback_registry();
let mut guard = registry.lock().unwrap();
*guard = callback;
}
pub fn set_skill_management_callback(callback: Option<RuntimeSkillManagementCallback>) {
let registry = skill_management_callback_registry();
let mut guard = registry.lock().unwrap();
*guard = callback;
}
pub fn set_host_tool_callback(callback: Option<RuntimeHostToolCallback>) {
let registry = host_tool_callback_registry();
let mut guard = registry.lock().unwrap();
*guard = callback;
}
pub(crate) fn emit_skill_lifecycle_event(event: &RuntimeSkillLifecycleEvent) {
let registry = skill_lifecycle_callback_registry();
let callback = {
let guard = registry.lock().unwrap();
guard.clone()
};
if let Some(callback) = callback {
callback(event);
}
}
pub(crate) fn emit_entry_registry_delta(delta: &RuntimeEntryRegistryDelta) {
let registry = entry_registry_callback_registry();
let callback = {
let guard = registry.lock().unwrap();
guard.clone()
};
if let Some(callback) = callback {
callback(delta);
}
}
pub(crate) fn dispatch_skill_management_request(
request: &RuntimeSkillManagementRequest,
) -> Result<Value, String> {
let registry = skill_management_callback_registry();
let callback = {
let guard = registry
.lock()
.map_err(|_| "Skill management callback registry lock poisoned".to_string())?;
guard.clone()
};
let callback = callback.ok_or_else(|| {
"Runtime skill management bridge is enabled but no host callback is registered".to_string()
})?;
callback(request)
}
pub(crate) fn dispatch_host_tool_request(
request: &RuntimeHostToolRequest,
) -> Result<Value, String> {
let registry = host_tool_callback_registry();
let callback = {
let guard = registry
.lock()
.map_err(|_| "Host tool callback registry lock poisoned".to_string())?;
guard.clone()
};
let callback = callback.ok_or_else(|| {
"Host tool bridge is enabled but no host callback is registered".to_string()
})?;
callback(request)
}
pub(crate) fn try_has_skill_management_callback() -> Result<bool, String> {
let registry = skill_management_callback_registry();
let guard = registry
.lock()
.map_err(|_| "Skill management callback registry lock poisoned".to_string())?;
Ok(guard.is_some())
}
pub(crate) fn try_has_host_tool_callback() -> Result<bool, String> {
let registry = host_tool_callback_registry();
let guard = registry
.lock()
.map_err(|_| "Host tool callback registry lock poisoned".to_string())?;
Ok(guard.is_some())
}
fn skill_lifecycle_callback_registry() -> &'static Mutex<Option<RuntimeSkillLifecycleCallback>> {
static REGISTRY: OnceLock<Mutex<Option<RuntimeSkillLifecycleCallback>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(None))
}
fn entry_registry_callback_registry() -> &'static Mutex<Option<RuntimeEntryRegistryCallback>> {
static REGISTRY: OnceLock<Mutex<Option<RuntimeEntryRegistryCallback>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(None))
}
fn skill_management_callback_registry() -> &'static Mutex<Option<RuntimeSkillManagementCallback>> {
static REGISTRY: OnceLock<Mutex<Option<RuntimeSkillManagementCallback>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(None))
}
fn host_tool_callback_registry() -> &'static Mutex<Option<RuntimeHostToolCallback>> {
static REGISTRY: OnceLock<Mutex<Option<RuntimeHostToolCallback>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(None))
}