feat(kernel): add internal ZCLAW kernel integration with Tauri
Phase 1-3 of independence architecture: - zclaw-types: Add ToolDefinition, ToolResult, KernelConfig, ModelConfig - zclaw-kernel: Fix AgentInfo provider field, export config module - desktop: Add kernel_commands for internal kernel access - Add AgentId FromStr implementation for parsing New Tauri commands: - kernel_init, kernel_status, kernel_shutdown - agent_create, agent_list, agent_get, agent_delete - agent_chat Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
276
desktop/src-tauri/src/kernel_commands.rs
Normal file
276
desktop/src-tauri/src/kernel_commands.rs
Normal file
@@ -0,0 +1,276 @@
|
||||
//! ZCLAW Kernel commands for Tauri
|
||||
//!
|
||||
//! These commands provide direct access to the internal ZCLAW Kernel,
|
||||
//! eliminating the need for external OpenFang process.
|
||||
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
use zclaw_kernel::Kernel;
|
||||
use zclaw_types::{AgentConfig, AgentId, AgentInfo, AgentState};
|
||||
|
||||
/// Kernel state wrapper for Tauri
|
||||
pub type KernelState = Arc<Mutex<Option<Kernel>>>;
|
||||
|
||||
/// Agent creation request
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateAgentRequest {
|
||||
/// Agent name
|
||||
pub name: String,
|
||||
/// Agent description
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
/// System prompt
|
||||
#[serde(default)]
|
||||
pub system_prompt: Option<String>,
|
||||
/// Model provider
|
||||
#[serde(default = "default_provider")]
|
||||
pub provider: String,
|
||||
/// Model identifier
|
||||
#[serde(default = "default_model")]
|
||||
pub model: String,
|
||||
/// Max tokens
|
||||
#[serde(default = "default_max_tokens")]
|
||||
pub max_tokens: u32,
|
||||
/// Temperature
|
||||
#[serde(default = "default_temperature")]
|
||||
pub temperature: f32,
|
||||
}
|
||||
|
||||
fn default_provider() -> String { "anthropic".to_string() }
|
||||
fn default_model() -> String { "claude-sonnet-4-20250514".to_string() }
|
||||
fn default_max_tokens() -> u32 { 4096 }
|
||||
fn default_temperature() -> f32 { 0.7 }
|
||||
|
||||
/// Agent creation response
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateAgentResponse {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
/// Chat request
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChatRequest {
|
||||
/// Agent ID
|
||||
pub agent_id: String,
|
||||
/// Message content
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
/// Chat response
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChatResponse {
|
||||
pub content: String,
|
||||
pub input_tokens: u32,
|
||||
pub output_tokens: u32,
|
||||
}
|
||||
|
||||
/// Kernel status response
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct KernelStatusResponse {
|
||||
pub initialized: bool,
|
||||
pub agent_count: usize,
|
||||
pub database_url: Option<String>,
|
||||
pub default_provider: Option<String>,
|
||||
pub default_model: Option<String>,
|
||||
}
|
||||
|
||||
/// Initialize the internal ZCLAW Kernel
|
||||
#[tauri::command]
|
||||
pub async fn kernel_init(
|
||||
state: State<'_, KernelState>,
|
||||
) -> Result<KernelStatusResponse, String> {
|
||||
let mut kernel_lock = state.lock().await;
|
||||
|
||||
if kernel_lock.is_some() {
|
||||
let kernel = kernel_lock.as_ref().unwrap();
|
||||
return Ok(KernelStatusResponse {
|
||||
initialized: true,
|
||||
agent_count: kernel.list_agents().len(),
|
||||
database_url: None,
|
||||
default_provider: Some("anthropic".to_string()),
|
||||
default_model: Some("claude-sonnet-4-20250514".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
let config = zclaw_kernel::config::KernelConfig::default();
|
||||
|
||||
// Boot kernel
|
||||
let kernel = Kernel::boot(config.clone())
|
||||
.await
|
||||
.map_err(|e| format!("Failed to initialize kernel: {}", e))?;
|
||||
|
||||
let agent_count = kernel.list_agents().len();
|
||||
|
||||
*kernel_lock = Some(kernel);
|
||||
|
||||
Ok(KernelStatusResponse {
|
||||
initialized: true,
|
||||
agent_count,
|
||||
database_url: Some(config.database_url),
|
||||
default_provider: Some(config.default_provider),
|
||||
default_model: Some(config.default_model),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get kernel status
|
||||
#[tauri::command]
|
||||
pub async fn kernel_status(
|
||||
state: State<'_, KernelState>,
|
||||
) -> Result<KernelStatusResponse, String> {
|
||||
let kernel_lock = state.lock().await;
|
||||
|
||||
match kernel_lock.as_ref() {
|
||||
Some(kernel) => Ok(KernelStatusResponse {
|
||||
initialized: true,
|
||||
agent_count: kernel.list_agents().len(),
|
||||
database_url: None,
|
||||
default_provider: Some("anthropic".to_string()),
|
||||
default_model: Some("claude-sonnet-4-20250514".to_string()),
|
||||
}),
|
||||
None => Ok(KernelStatusResponse {
|
||||
initialized: false,
|
||||
agent_count: 0,
|
||||
database_url: None,
|
||||
default_provider: None,
|
||||
default_model: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shutdown the kernel
|
||||
#[tauri::command]
|
||||
pub async fn kernel_shutdown(
|
||||
state: State<'_, KernelState>,
|
||||
) -> Result<(), String> {
|
||||
let mut kernel_lock = state.lock().await;
|
||||
|
||||
if let Some(kernel) = kernel_lock.take() {
|
||||
kernel.shutdown().await.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a new agent
|
||||
#[tauri::command]
|
||||
pub async fn agent_create(
|
||||
state: State<'_, KernelState>,
|
||||
request: CreateAgentRequest,
|
||||
) -> Result<CreateAgentResponse, 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())?;
|
||||
|
||||
// Build agent config
|
||||
let 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);
|
||||
|
||||
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
|
||||
#[tauri::command]
|
||||
pub async fn agent_list(
|
||||
state: State<'_, KernelState>,
|
||||
) -> Result<Vec<AgentInfo>, 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
|
||||
#[tauri::command]
|
||||
pub async fn agent_get(
|
||||
state: State<'_, KernelState>,
|
||||
agent_id: String,
|
||||
) -> Result<Option<AgentInfo>, 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())?;
|
||||
|
||||
let id: AgentId = agent_id.parse()
|
||||
.map_err(|_| "Invalid agent ID format".to_string())?;
|
||||
|
||||
Ok(kernel.get_agent(&id))
|
||||
}
|
||||
|
||||
/// Delete an agent
|
||||
#[tauri::command]
|
||||
pub async fn agent_delete(
|
||||
state: State<'_, KernelState>,
|
||||
agent_id: String,
|
||||
) -> 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())?;
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
/// Send a message to an agent
|
||||
#[tauri::command]
|
||||
pub async fn agent_chat(
|
||||
state: State<'_, KernelState>,
|
||||
request: ChatRequest,
|
||||
) -> Result<ChatResponse, 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())?;
|
||||
|
||||
let id: AgentId = request.agent_id.parse()
|
||||
.map_err(|_| "Invalid agent ID format".to_string())?;
|
||||
|
||||
let response = kernel.send_message(&id, request.message)
|
||||
.await
|
||||
.map_err(|e| format!("Chat failed: {}", e))?;
|
||||
|
||||
Ok(ChatResponse {
|
||||
content: response.content,
|
||||
input_tokens: response.input_tokens,
|
||||
output_tokens: response.output_tokens,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create the kernel state for Tauri
|
||||
pub fn create_kernel_state() -> KernelState {
|
||||
Arc::new(Mutex::new(None))
|
||||
}
|
||||
@@ -24,6 +24,9 @@ mod memory_commands;
|
||||
// Intelligence Layer (migrated from frontend lib/)
|
||||
mod intelligence;
|
||||
|
||||
// Internal ZCLAW Kernel commands (replaces external OpenFang process)
|
||||
mod kernel_commands;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, Value};
|
||||
use std::fs;
|
||||
@@ -1308,6 +1311,9 @@ pub fn run() {
|
||||
let reflection_state: intelligence::ReflectionEngineState = std::sync::Arc::new(tokio::sync::Mutex::new(intelligence::ReflectionEngine::new(None)));
|
||||
let identity_state: intelligence::IdentityManagerState = std::sync::Arc::new(tokio::sync::Mutex::new(intelligence::AgentIdentityManager::new()));
|
||||
|
||||
// Initialize internal ZCLAW Kernel state
|
||||
let kernel_state = kernel_commands::create_kernel_state();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.manage(browser_state)
|
||||
@@ -1315,7 +1321,17 @@ pub fn run() {
|
||||
.manage(heartbeat_state)
|
||||
.manage(reflection_state)
|
||||
.manage(identity_state)
|
||||
.manage(kernel_state)
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
// Internal ZCLAW Kernel commands (preferred)
|
||||
kernel_commands::kernel_init,
|
||||
kernel_commands::kernel_status,
|
||||
kernel_commands::kernel_shutdown,
|
||||
kernel_commands::agent_create,
|
||||
kernel_commands::agent_list,
|
||||
kernel_commands::agent_get,
|
||||
kernel_commands::agent_delete,
|
||||
kernel_commands::agent_chat,
|
||||
// OpenFang commands (new naming)
|
||||
openfang_status,
|
||||
openfang_start,
|
||||
|
||||
Reference in New Issue
Block a user