feat(tui): add usage overlay and sidebar for displaying usage statistics and tasks
feat(tui): implement status bar with connection and turn state indicators feat(tui): create workflow panel for agent status and progress visualization feat(web): introduce web frontend interface with static file serving feat(ws): add WebSocket interface for real-time communication and session management
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
//! Workflow tools — orchestrate multi-step agent workflows and hive-mind convergence.
|
||||
|
||||
use anyhow::Result;
|
||||
use serde_json::{json, Value};
|
||||
use tracing::info;
|
||||
|
||||
use crate::llm::provider::LlmClient;
|
||||
use crate::tools::{arg_str, Tool, ToolCtx};
|
||||
use crate::workflow::engine::execution::execute_workflow;
|
||||
use crate::workflow::hive_mind::cycle::execute_cycle;
|
||||
use crate::workflow::hive_mind::synthesis::synthesize_consensus;
|
||||
use crate::workflow::hive_mind::types::{CognitiveCycle, NodeOutput};
|
||||
use crate::workflow::script::WorkflowScript;
|
||||
|
||||
/// Execute a multi-step workflow defined in YAML.
|
||||
///
|
||||
/// Flow: parse YAML → build plan from script phases → execute via workflow engine.
|
||||
pub struct WorkflowRun;
|
||||
|
||||
impl Tool for WorkflowRun {
|
||||
fn name(&self) -> &'static str {
|
||||
"workflow_run"
|
||||
}
|
||||
|
||||
fn description(&self) -> &'static str {
|
||||
"Execute a multi-step workflow defined in YAML"
|
||||
}
|
||||
|
||||
fn parameters(&self) -> Value {
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"workflow_yaml": {
|
||||
"type": "string",
|
||||
"description": "YAML workflow definition"
|
||||
}
|
||||
},
|
||||
"required": ["workflow_yaml"]
|
||||
})
|
||||
}
|
||||
|
||||
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
|
||||
let yaml = arg_str(args, "workflow_yaml")?;
|
||||
let script = WorkflowScript::parse(&yaml)?;
|
||||
info!(
|
||||
"Workflow started: {} ({} phases)",
|
||||
script.name,
|
||||
script.phases.len()
|
||||
);
|
||||
|
||||
let phase_names: Vec<&str> = script.phases.iter().map(|p| p.name.as_str()).collect();
|
||||
info!(
|
||||
"Workflow '{}' phases: {}",
|
||||
script.name,
|
||||
phase_names.join(", ")
|
||||
);
|
||||
|
||||
let llm_client = LlmClient::new(
|
||||
crate::llm::provider::DEFAULT_API_KEY.to_string(),
|
||||
"deepseek-v4-flash-free".to_string(),
|
||||
None,
|
||||
);
|
||||
let rt = tokio::runtime::Runtime::new()?;
|
||||
let result: Vec<String> =
|
||||
rt.block_on(async { execute_workflow(&script, ctx, &llm_client).await })?;
|
||||
|
||||
Ok(format!(
|
||||
"Workflow '{}' completed.\n\n{}",
|
||||
script.name,
|
||||
result.join("\n---\n")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoteFinding;
|
||||
|
||||
impl Tool for NoteFinding {
|
||||
fn name(&self) -> &'static str {
|
||||
"note_finding"
|
||||
}
|
||||
|
||||
fn description(&self) -> &'static str {
|
||||
"Record a finding during workflow or hive-mind execution"
|
||||
}
|
||||
|
||||
fn parameters(&self) -> Value {
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"finding": {
|
||||
"type": "string",
|
||||
"description": "The finding text"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Category for the finding"
|
||||
}
|
||||
},
|
||||
"required": ["finding"]
|
||||
})
|
||||
}
|
||||
|
||||
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
|
||||
let finding = crate::tools::arg_str(args, "finding")?;
|
||||
|
||||
if let Some(ref findings) = ctx.workflow_findings {
|
||||
if let Ok(mut guard) = findings.lock() {
|
||||
guard.push(finding.clone());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(format!("Finding recorded: {finding}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadFindings;
|
||||
|
||||
impl Tool for ReadFindings {
|
||||
fn name(&self) -> &'static str {
|
||||
"read_findings"
|
||||
}
|
||||
|
||||
fn description(&self) -> &'static str {
|
||||
"Read all findings recorded so far in the current workflow"
|
||||
}
|
||||
|
||||
fn parameters(&self) -> Value {
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
})
|
||||
}
|
||||
|
||||
fn run(&self, ctx: &ToolCtx, _args: &Value) -> Result<String> {
|
||||
let findings = ctx
|
||||
.workflow_findings
|
||||
.as_ref()
|
||||
.and_then(|f| f.lock().ok())
|
||||
.map(|guard| guard.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
if findings.is_empty() {
|
||||
return Ok("No findings recorded yet.".to_string());
|
||||
}
|
||||
|
||||
Ok(format!(
|
||||
"Findings ({}):\n{}",
|
||||
findings.len(),
|
||||
findings.join("\n")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Orchestrate a hive-mind convergence — multiple agents across parallel cycles.
|
||||
///
|
||||
/// Flow: parse cycles from args → execute each cycle via `execute_cycle` →
|
||||
/// collect all node outputs → synthesize consensus → return report.
|
||||
pub struct HiveMind;
|
||||
|
||||
impl Tool for HiveMind {
|
||||
fn name(&self) -> &'static str {
|
||||
"hive_mind"
|
||||
}
|
||||
|
||||
fn description(&self) -> &'static str {
|
||||
"Run a hive-mind convergence with multiple nodes across sequential cycles"
|
||||
}
|
||||
|
||||
fn parameters(&self) -> Value {
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cycles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"directives": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"directive": {"type": "string"},
|
||||
"access": {"type": "string", "enum": ["read", "write", "full"]}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Array of cycles, each with an array of node directives"
|
||||
}
|
||||
},
|
||||
"required": ["cycles"]
|
||||
})
|
||||
}
|
||||
|
||||
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
|
||||
let cycles_val = args
|
||||
.get("cycles")
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or_else(|| anyhow::anyhow!("missing 'cycles' array"))?;
|
||||
|
||||
info!("Hive mind starting with {} cycles", cycles_val.len());
|
||||
let rt = tokio::runtime::Runtime::new()?;
|
||||
let mut all_node_outputs = Vec::new();
|
||||
|
||||
for (cycle_idx, cycle_val) in cycles_val.iter().enumerate() {
|
||||
let directives: Vec<String> = cycle_val
|
||||
.get("directives")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|arr| {
|
||||
arr.iter()
|
||||
.filter_map(|d| d.get("directive").and_then(|v| v.as_str()))
|
||||
.map(String::from)
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let cycle = CognitiveCycle {
|
||||
index: cycle_idx as u32,
|
||||
directives,
|
||||
};
|
||||
|
||||
let nodes: Vec<NodeOutput> =
|
||||
rt.block_on(async { execute_cycle(&cycle, ctx).await })?;
|
||||
all_node_outputs.extend(nodes);
|
||||
}
|
||||
|
||||
let node_count = all_node_outputs.len();
|
||||
let consensus =
|
||||
rt.block_on(async { synthesize_consensus(&all_node_outputs, ctx).await })?;
|
||||
|
||||
let report = format!(
|
||||
"Hive mind convergence completed.\nNodes executed: {}\n\nConsensus:\n{}",
|
||||
node_count, consensus
|
||||
);
|
||||
Ok(report)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user