Files
zclaw_openfang/desktop/src-tauri/src/kernel_commands/a2a.rs
iven 0a04b260a4
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
refactor(desktop): ChatStore structured split + IDB persistence + stream cancel
Split monolithic chatStore.ts (908 lines) into 4 focused stores:
- chatStore.ts: facade layer, owns messages[], backward-compatible selectors
- conversationStore.ts: conversation CRUD, agent switching, IndexedDB persistence
- streamStore.ts: streaming orchestration, chat mode, suggestions
- messageStore.ts: token tracking

Key fixes from 3-round deep audit:
- C1: Fix Rust serde camelCase vs TS snake_case mismatch (toolStart/toolEnd/iterationStart)
- C2: Fix IDB async rehydration race with persist.hasHydrated() subscribe
- C3: Add sessionKey to partialize to survive page refresh
- H3: Fix IDB migration retry on failure (don't set migrated=true in catch)
- M3: Fix ToolCallStep deduplication (toolStart creates, toolEnd updates)
- M-NEW-2: Clear sessionKey on cancelStream

Also adds:
- Rust backend stream cancellation via AtomicBool + cancel_stream command
- IndexedDB storage adapter with one-time localStorage migration
- HMR cleanup for cross-store subscriptions
2026-04-03 00:24:16 +08:00

119 lines
3.7 KiB
Rust

//! A2A (Agent-to-Agent) commands — gated behind `multi-agent` feature
use serde_json;
use tauri::State;
use zclaw_types::AgentId;
use super::KernelState;
// ============================================================
// A2A (Agent-to-Agent) Commands — gated behind multi-agent feature
// ============================================================
#[cfg(feature = "multi-agent")]
/// Send a direct A2A message from one agent to another
// @connected
#[tauri::command]
pub async fn agent_a2a_send(
state: State<'_, KernelState>,
from: String,
to: String,
payload: serde_json::Value,
message_type: Option<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 from_id: AgentId = from.parse()
.map_err(|_| format!("Invalid from agent ID: {}", from))?;
let to_id: AgentId = to.parse()
.map_err(|_| format!("Invalid to agent ID: {}", to))?;
let msg_type = message_type.map(|mt| match mt.as_str() {
"request" => zclaw_kernel::A2aMessageType::Request,
"notification" => zclaw_kernel::A2aMessageType::Notification,
"task" => zclaw_kernel::A2aMessageType::Task,
_ => zclaw_kernel::A2aMessageType::Notification,
});
kernel.a2a_send(&from_id, &to_id, payload, msg_type).await
.map_err(|e| format!("A2A send failed: {}", e))?;
Ok(())
}
/// Broadcast a message from one agent to all other agents
#[cfg(feature = "multi-agent")]
// @connected
#[tauri::command]
pub async fn agent_a2a_broadcast(
state: State<'_, KernelState>,
from: String,
payload: serde_json::Value,
) -> 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 from_id: AgentId = from.parse()
.map_err(|_| format!("Invalid from agent ID: {}", from))?;
kernel.a2a_broadcast(&from_id, payload).await
.map_err(|e| format!("A2A broadcast failed: {}", e))?;
Ok(())
}
/// Discover agents with a specific capability
#[cfg(feature = "multi-agent")]
// @connected
#[tauri::command]
pub async fn agent_a2a_discover(
state: State<'_, KernelState>,
capability: String,
) -> Result<Vec<serde_json::Value>, 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 profiles = kernel.a2a_discover(&capability).await
.map_err(|e| format!("A2A discover failed: {}", e))?;
let result: Vec<serde_json::Value> = profiles.iter()
.filter_map(|p| serde_json::to_value(p).ok())
.collect();
Ok(result)
}
/// Delegate a task to another agent and wait for response
#[cfg(feature = "multi-agent")]
// @connected
#[tauri::command]
pub async fn agent_a2a_delegate_task(
state: State<'_, KernelState>,
from: String,
to: String,
task: String,
timeout_ms: Option<u64>,
) -> Result<serde_json::Value, 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 from_id: AgentId = from.parse()
.map_err(|_| format!("Invalid from agent ID: {}", from))?;
let to_id: AgentId = to.parse()
.map_err(|_| format!("Invalid to agent ID: {}", to))?;
let timeout = timeout_ms.unwrap_or(30_000);
// 30 seconds default
let response = kernel.a2a_delegate_task(&from_id, &to_id, task, timeout).await
.map_err(|e| format!("A2A task delegation failed: {}", e))?;
Ok(response)
}