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
Batch fix covering multiple modules:
- P2-01: HandRegistry Semaphore-based max_concurrent enforcement
- P2-03: Populate toolCount/metricCount from Hand trait methods
- P2-06: heartbeat_update_config minimum interval validation
- P2-07: ReflectionResult used_fallback marker for rule-based fallback
- P2-08/09: identity_propose_change parameter naming consistency
- P2-10: ClassroomMetadata is_placeholder flag for LLM failure
- P2-11: classroomStore userDidCloseDuringGeneration intent tracking
- P2-12: workflowStore pipeline_create sends actionType
- P2-13/14: PipelineInfo step_count + PipelineStepInfo for proper step mapping
- P2-15: Pipe transform support in context.resolve (8 transforms)
- P2-16: Mustache {{...}} → \${...} auto-normalization
- P2-17: SaaSLogin password placeholder 6→8
- P2-19: serialize_skill_md + update_skill preserve tools field
- P2-22: ToolOutputGuard sensitive patterns from warn→block
- P2-23: Mutex::unwrap() → unwrap_or_else in relay/service.rs
- P3-01/03/07/08/09: Various P3 fixes
- DEFECT_LIST.md: comprehensive status sync (43/51 fixed, 8 remaining)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
190 lines
4.8 KiB
Rust
190 lines
4.8 KiB
Rust
//! Hand definition and types
|
|
|
|
use async_trait::async_trait;
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::Value;
|
|
use zclaw_types::{Result, AgentId};
|
|
|
|
/// Hand configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HandConfig {
|
|
/// Unique hand identifier
|
|
pub id: String,
|
|
/// Human-readable name
|
|
pub name: String,
|
|
/// Hand description
|
|
pub description: String,
|
|
/// Whether this hand needs approval before execution
|
|
#[serde(default)]
|
|
pub needs_approval: bool,
|
|
/// Required dependencies
|
|
#[serde(default)]
|
|
pub dependencies: Vec<String>,
|
|
/// Input schema
|
|
#[serde(default)]
|
|
pub input_schema: Option<Value>,
|
|
/// Tags for categorization
|
|
#[serde(default)]
|
|
pub tags: Vec<String>,
|
|
/// Whether the hand is enabled
|
|
#[serde(default = "default_enabled")]
|
|
pub enabled: bool,
|
|
/// Maximum concurrent executions for this hand (0 = unlimited)
|
|
#[serde(default)]
|
|
pub max_concurrent: u32,
|
|
/// Timeout in seconds for each execution (0 = use HandContext default)
|
|
#[serde(default)]
|
|
pub timeout_secs: u64,
|
|
}
|
|
|
|
impl Default for HandConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
id: String::new(),
|
|
name: String::new(),
|
|
description: String::new(),
|
|
needs_approval: false,
|
|
dependencies: Vec::new(),
|
|
input_schema: None,
|
|
tags: Vec::new(),
|
|
enabled: true,
|
|
max_concurrent: 0,
|
|
timeout_secs: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn default_enabled() -> bool { true }
|
|
|
|
/// Hand execution context
|
|
#[derive(Debug, Clone)]
|
|
pub struct HandContext {
|
|
/// Agent ID executing the hand
|
|
pub agent_id: AgentId,
|
|
/// Working directory
|
|
pub working_dir: Option<std::path::PathBuf>,
|
|
/// Environment variables
|
|
pub env: std::collections::HashMap<String, String>,
|
|
/// Timeout in seconds
|
|
pub timeout_secs: u64,
|
|
/// Callback URL for async results
|
|
pub callback_url: Option<String>,
|
|
}
|
|
|
|
impl Default for HandContext {
|
|
fn default() -> Self {
|
|
Self {
|
|
agent_id: AgentId::new(),
|
|
working_dir: None,
|
|
env: std::collections::HashMap::new(),
|
|
timeout_secs: 300,
|
|
callback_url: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Hand execution result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HandResult {
|
|
/// Whether execution succeeded
|
|
pub success: bool,
|
|
/// Output data
|
|
pub output: Value,
|
|
/// Error message if failed
|
|
#[serde(default)]
|
|
pub error: Option<String>,
|
|
/// Execution duration in milliseconds
|
|
#[serde(default)]
|
|
pub duration_ms: Option<u64>,
|
|
/// Status message
|
|
#[serde(default)]
|
|
pub status: String,
|
|
}
|
|
|
|
impl HandResult {
|
|
pub fn success(output: Value) -> Self {
|
|
Self {
|
|
success: true,
|
|
output,
|
|
error: None,
|
|
duration_ms: None,
|
|
status: "completed".to_string(),
|
|
}
|
|
}
|
|
|
|
pub fn error(message: impl Into<String>) -> Self {
|
|
Self {
|
|
success: false,
|
|
output: Value::Null,
|
|
error: Some(message.into()),
|
|
duration_ms: None,
|
|
status: "failed".to_string(),
|
|
}
|
|
}
|
|
|
|
pub fn pending(status: impl Into<String>) -> Self {
|
|
Self {
|
|
success: true,
|
|
output: Value::Null,
|
|
error: None,
|
|
duration_ms: None,
|
|
status: status.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Hand execution status
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum HandStatus {
|
|
Idle,
|
|
Running,
|
|
PendingApproval,
|
|
Completed,
|
|
Failed,
|
|
}
|
|
|
|
/// Hand trait - autonomous capability
|
|
#[async_trait]
|
|
pub trait Hand: Send + Sync {
|
|
/// Get the hand configuration
|
|
fn config(&self) -> &HandConfig;
|
|
|
|
/// Execute the hand
|
|
async fn execute(&self, context: &HandContext, input: Value) -> Result<HandResult>;
|
|
|
|
/// Check if the hand needs approval
|
|
fn needs_approval(&self) -> bool {
|
|
self.config().needs_approval
|
|
}
|
|
|
|
/// Check dependencies
|
|
fn check_dependencies(&self) -> Result<Vec<String>> {
|
|
let missing: Vec<String> = self.config().dependencies.iter()
|
|
.filter(|dep| !self.is_dependency_available(dep))
|
|
.cloned()
|
|
.collect();
|
|
Ok(missing)
|
|
}
|
|
|
|
/// Check if a specific dependency is available
|
|
fn is_dependency_available(&self, _dep: &str) -> bool {
|
|
true // Default implementation
|
|
}
|
|
|
|
/// Get current status
|
|
fn status(&self) -> HandStatus {
|
|
HandStatus::Idle
|
|
}
|
|
|
|
/// P2-03: Get the number of tools this hand exposes (default: 0)
|
|
fn tool_count(&self) -> u32 {
|
|
0
|
|
}
|
|
|
|
/// P2-03: Get the number of metrics this hand tracks (default: 0)
|
|
fn metric_count(&self) -> u32 {
|
|
0
|
|
}
|
|
}
|