//! Agent CRUD commands: create, list, get, delete, update, export, import use std::path::PathBuf; use serde::{Deserialize, Serialize}; use tauri::State; use zclaw_types::{AgentConfig, AgentId, AgentInfo}; use super::{validate_agent_id, KernelState}; use crate::intelligence::validation::validate_string_length; // --------------------------------------------------------------------------- // Request / Response types // --------------------------------------------------------------------------- fn default_provider() -> String { "openai".to_string() } fn default_model() -> String { "gpt-4o-mini".to_string() } fn default_max_tokens() -> u32 { 4096 } fn default_temperature() -> f32 { 0.7 } /// Agent creation request #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CreateAgentRequest { pub name: String, #[serde(default)] pub description: Option, #[serde(default)] pub system_prompt: Option, #[serde(default = "default_provider")] pub provider: String, #[serde(default = "default_model")] pub model: String, #[serde(default = "default_max_tokens")] pub max_tokens: u32, #[serde(default = "default_temperature")] pub temperature: f32, #[serde(default)] pub workspace: Option, } /// Agent creation response #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CreateAgentResponse { pub id: String, pub name: String, pub state: String, } /// Agent update request #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AgentUpdateRequest { pub name: Option, pub description: Option, pub system_prompt: Option, pub model: Option, pub provider: Option, pub max_tokens: Option, pub temperature: Option, } // --------------------------------------------------------------------------- // Commands // --------------------------------------------------------------------------- /// Create a new agent // @connected #[tauri::command] pub async fn agent_create( state: State<'_, KernelState>, request: CreateAgentRequest, ) -> Result { let kernel_lock = state.lock().await; let kernel = kernel_lock.as_ref() .ok_or_else(|| "Kernel not initialized. Call kernel_init first.".to_string())?; let mut config = AgentConfig::new(&request.name) .with_description(request.description.unwrap_or_default()) .with_system_prompt(request.system_prompt.unwrap_or_default()) .with_model(zclaw_types::ModelConfig { provider: request.provider, model: request.model, api_key_env: None, base_url: None, }) .with_max_tokens(request.max_tokens) .with_temperature(request.temperature); if let Some(workspace) = request.workspace { config.workspace = Some(workspace); } let id = kernel.spawn_agent(config) .await .map_err(|e| format!("Failed to create agent: {}", e))?; Ok(CreateAgentResponse { id: id.to_string(), name: request.name, state: "running".to_string(), }) } /// List all agents // @connected #[tauri::command] pub async fn agent_list( state: State<'_, KernelState>, ) -> Result, String> { let kernel_lock = state.lock().await; let kernel = kernel_lock.as_ref() .ok_or_else(|| "Kernel not initialized. Call kernel_init first.".to_string())?; Ok(kernel.list_agents()) } /// Get agent info // @connected #[tauri::command] pub async fn agent_get( state: State<'_, KernelState>, agent_id: String, ) -> Result, String> { let agent_id = validate_agent_id(&agent_id)?; let kernel_lock = state.lock().await; let kernel = kernel_lock.as_ref() .ok_or_else(|| "Kernel not initialized. Call kernel_init first.".to_string())?; let id: AgentId = agent_id.parse() .map_err(|_| "Invalid agent ID format".to_string())?; Ok(kernel.get_agent(&id)) } /// Delete an agent // @connected #[tauri::command] pub async fn agent_delete( state: State<'_, KernelState>, agent_id: String, ) -> Result<(), String> { let agent_id = validate_agent_id(&agent_id)?; let kernel_lock = state.lock().await; let kernel = kernel_lock.as_ref() .ok_or_else(|| "Kernel not initialized. Call kernel_init first.".to_string())?; let id: AgentId = agent_id.parse() .map_err(|_| "Invalid agent ID format".to_string())?; kernel.kill_agent(&id) .await .map_err(|e| format!("Failed to delete agent: {}", e)) } /// Update an agent's configuration // @connected #[tauri::command] pub async fn agent_update( state: State<'_, KernelState>, agent_id: String, updates: AgentUpdateRequest, ) -> Result { let agent_id = validate_agent_id(&agent_id)?; let kernel_lock = state.lock().await; let kernel = kernel_lock.as_ref() .ok_or_else(|| "Kernel not initialized. Call kernel_init first.".to_string())?; let id: AgentId = agent_id.parse() .map_err(|_| "Invalid agent ID format".to_string())?; // Get existing config let mut config = kernel.get_agent_config(&id) .ok_or_else(|| format!("Agent not found: {}", agent_id))?; // Apply updates if let Some(name) = updates.name { config.name = name; } if let Some(description) = updates.description { config.description = Some(description); } if let Some(system_prompt) = updates.system_prompt { config.system_prompt = Some(system_prompt); } if let Some(model) = updates.model { config.model.model = model; } if let Some(provider) = updates.provider { config.model.provider = provider; } if let Some(max_tokens) = updates.max_tokens { config.max_tokens = Some(max_tokens); } if let Some(temperature) = updates.temperature { config.temperature = Some(temperature); } // Save updated config kernel.update_agent(config) .await .map_err(|e| format!("Failed to update agent: {}", e))?; // Return updated info kernel.get_agent(&id) .ok_or_else(|| format!("Agent not found after update: {}", agent_id)) } /// Export an agent configuration as JSON // @reserved: 暂无前端集成 #[tauri::command] pub async fn agent_export( state: State<'_, KernelState>, agent_id: String, ) -> Result { let agent_id = validate_agent_id(&agent_id)?; let kernel_lock = state.lock().await; let kernel = kernel_lock.as_ref() .ok_or_else(|| "Kernel not initialized. Call kernel_init first.".to_string())?; let id: AgentId = agent_id.parse() .map_err(|_| "Invalid agent ID format".to_string())?; let config = kernel.get_agent_config(&id) .ok_or_else(|| format!("Agent not found: {}", agent_id))?; serde_json::to_string_pretty(&config) .map_err(|e| format!("Failed to serialize agent config: {}", e)) } /// Import an agent from JSON configuration // @reserved: 暂无前端集成 #[tauri::command] pub async fn agent_import( state: State<'_, KernelState>, config_json: String, ) -> Result { validate_string_length(&config_json, "config_json", 1_000_000) .map_err(|e| format!("{}", e))?; let mut config: AgentConfig = serde_json::from_str(&config_json) .map_err(|e| format!("Invalid agent config JSON: {}", e))?; // Regenerate ID to avoid collisions config.id = AgentId::new(); let kernel_lock = state.lock().await; let kernel = kernel_lock.as_ref() .ok_or_else(|| "Kernel not initialized. Call kernel_init first.".to_string())?; let new_id = kernel.spawn_agent(config).await .map_err(|e| format!("Failed to import agent: {}", e))?; kernel.get_agent(&new_id) .ok_or_else(|| "Agent was created but could not be retrieved".to_string()) }