diff --git a/desktop/src-tauri/src/intelligence/reflection.rs b/desktop/src-tauri/src/intelligence/reflection.rs index 9213cb8..0163e71 100644 --- a/desktop/src-tauri/src/intelligence/reflection.rs +++ b/desktop/src-tauri/src/intelligence/reflection.rs @@ -760,9 +760,14 @@ pub async fn reflection_reflect( agent_id: String, memories: Vec, state: tauri::State<'_, ReflectionEngineState>, + kernel_state: tauri::State<'_, crate::kernel_commands::KernelState>, ) -> Result { + let driver = { + let kernel_lock = kernel_state.lock().await; + kernel_lock.as_ref().map(|k| k.driver()) + }; let mut engine = state.lock().await; - Ok(engine.reflect(&agent_id, &memories, None).await) + Ok(engine.reflect(&agent_id, &memories, driver).await) } /// Get reflection history diff --git a/desktop/src-tauri/src/kernel_commands/hand.rs b/desktop/src-tauri/src/kernel_commands/hand.rs index 82fdd93..e3dd5ae 100644 --- a/desktop/src-tauri/src/kernel_commands/hand.rs +++ b/desktop/src-tauri/src/kernel_commands/hand.rs @@ -93,6 +93,7 @@ pub struct HandResult { pub output: serde_json::Value, pub error: Option, pub duration_ms: Option, + pub run_id: Option, } impl From for HandResult { @@ -102,6 +103,7 @@ impl From for HandResult { output: result.output, error: result.error, duration_ms: result.duration_ms, + run_id: None, } } } @@ -155,6 +157,7 @@ pub async fn hand_execute( }), error: None, duration_ms: None, + run_id: None, }); } @@ -175,16 +178,19 @@ pub async fn hand_execute( }), error: None, duration_ms: None, + run_id: None, }); } } } // Execute hand directly (returns result + run_id for tracking) - let (result, _run_id) = kernel.execute_hand(&id, input).await + let (result, run_id) = kernel.execute_hand(&id, input).await .map_err(|e| format!("Failed to execute hand: {}", e))?; - Ok(HandResult::from(result)) + let mut hand_result = HandResult::from(result); + hand_result.run_id = Some(run_id.to_string()); + Ok(hand_result) } /// Approve a hand execution diff --git a/desktop/src-tauri/src/pipeline_commands/discovery.rs b/desktop/src-tauri/src/pipeline_commands/discovery.rs index 2d2da92..d214c04 100644 --- a/desktop/src-tauri/src/pipeline_commands/discovery.rs +++ b/desktop/src-tauri/src/pipeline_commands/discovery.rs @@ -6,6 +6,7 @@ use tauri::{AppHandle, Emitter, State}; use zclaw_pipeline::{ RunStatus, parse_pipeline_yaml, + parse_pipeline_v2_yaml, PipelineExecutor, ActionRegistry, LlmActionDriver, @@ -15,7 +16,7 @@ use zclaw_pipeline::{ use super::{PipelineState, PipelineInfo, PipelineRunResponse, RunPipelineResponse, RunPipelineRequest}; use super::adapters::{RuntimeLlmAdapter, PipelineSkillDriver, PipelineHandDriver}; -use super::helpers::{get_pipelines_directory, scan_pipelines_with_paths, scan_pipelines_full_sync, pipeline_to_info}; +use super::helpers::{get_pipelines_directory, scan_pipelines_with_paths, scan_pipelines_full_sync, pipeline_to_info, pipeline_v2_to_info}; use crate::kernel_commands::KernelState; @@ -60,6 +61,8 @@ pub async fn pipeline_list( state_pipelines.insert(info.id.clone(), pipeline); state_paths.insert(info.id.clone(), path.clone()); } + // v2 pipelines are listed but not stored in v1 state; + // they can be discovered and displayed but execution requires v2 engine support. } result.push(info.clone()); } diff --git a/desktop/src-tauri/src/pipeline_commands/helpers.rs b/desktop/src-tauri/src/pipeline_commands/helpers.rs index fafd173..b460f07 100644 --- a/desktop/src-tauri/src/pipeline_commands/helpers.rs +++ b/desktop/src-tauri/src/pipeline_commands/helpers.rs @@ -5,6 +5,8 @@ use std::path::PathBuf; use zclaw_pipeline::{ Pipeline, parse_pipeline_yaml, + parse_pipeline_v2_yaml, + PipelineV2, }; use super::types::{PipelineInfo, PipelineInputInfo}; @@ -88,8 +90,29 @@ pub(crate) fn scan_pipelines_with_paths( tracing::debug!("[scan] Found pipeline: {} at {:?}", pipeline.metadata.name, path); pipelines.push((pipeline_to_info(&pipeline), path)); } - Err(e) => { - tracing::error!("[scan] Failed to parse pipeline at {:?}: {}", path, e); + Err(_) => { + // Try v2 parser as fallback + match parse_pipeline_v2_yaml(&content) { + Ok(v2) => { + tracing::debug!( + "[scan] Parsed v2 YAML: {} -> category: {:?}", + v2.metadata.name, + v2.metadata.category + ); + + if let Some(filter) = category_filter { + if v2.metadata.category.as_deref() != Some(filter) { + continue; + } + } + + tracing::debug!("[scan] Found v2 pipeline: {} at {:?}", v2.metadata.name, path); + pipelines.push((pipeline_v2_to_info(&v2), path)); + } + Err(e2) => { + tracing::error!("[scan] Failed to parse pipeline at {:?}: v1 error, v2: {}", path, e2); + } + } } } } @@ -117,6 +140,8 @@ pub(crate) fn scan_pipelines_full_sync( if let Ok(pipeline) = parse_pipeline_yaml(&content) { pipelines.push((path, pipeline)); } + // v2 pipelines are not stored in the v1 state vector; + // scan_pipelines_with_paths handles v2 discovery separately. } } } @@ -165,3 +190,40 @@ pub(crate) fn pipeline_to_info(pipeline: &Pipeline) -> PipelineInfo { }).collect(), } } + +/// Convert a PipelineV2 to PipelineInfo for listing purposes. +/// V2 pipelines are discoverable but not stored in state (no v1 Pipeline to execute). +pub(crate) fn pipeline_v2_to_info(v2: &PipelineV2) -> PipelineInfo { + use zclaw_pipeline::ParamType; + PipelineInfo { + id: v2.metadata.name.clone(), + display_name: v2.metadata.display_name.clone() + .unwrap_or_else(|| v2.metadata.name.clone()), + description: v2.metadata.description.clone().unwrap_or_default(), + category: v2.metadata.category.clone().unwrap_or_default(), + industry: v2.metadata.industry.clone().unwrap_or_default(), + tags: v2.metadata.tags.clone(), + icon: v2.metadata.icon.clone().unwrap_or_else(|| "📦".to_string()), + version: v2.metadata.version.clone(), + author: String::new(), + inputs: v2.params.iter().map(|param| { + PipelineInputInfo { + name: param.name.clone(), + input_type: match ¶m.param_type { + ParamType::String => "string".to_string(), + ParamType::Number => "number".to_string(), + ParamType::Boolean => "boolean".to_string(), + ParamType::Select => "select".to_string(), + ParamType::MultiSelect => "multi-select".to_string(), + ParamType::File => "file".to_string(), + ParamType::Text => "text".to_string(), + }, + required: param.required, + label: param.label.clone().unwrap_or_else(|| param.name.clone()), + placeholder: param.placeholder.clone(), + default: param.default.clone(), + options: param.options.clone(), + } + }).collect(), + } +} \ No newline at end of file diff --git a/desktop/src/lib/skill-discovery.ts b/desktop/src/lib/skill-discovery.ts index 5a5397f..5cb834a 100644 --- a/desktop/src/lib/skill-discovery.ts +++ b/desktop/src/lib/skill-discovery.ts @@ -41,6 +41,7 @@ interface BackendSkillInfo { version: string; capabilities: string[]; tags: string[]; + triggers: string[]; mode: string; enabled: boolean; } @@ -113,10 +114,10 @@ export class SkillDiscoveryEngine { name: backend.name, description: backend.description, version: backend.version, - triggers: backend.tags, // Use tags as triggers + triggers: backend.triggers?.length ? backend.triggers : backend.tags, capabilities: backend.capabilities, mode: backend.mode, - toolDeps: [], // Backend doesn't have this field + toolDeps: [], installed: backend.enabled, category: backend.tags[0] || 'general', };