89 lines
2.9 KiB
Rust
89 lines
2.9 KiB
Rust
//! Top-level workflow execution functions.
|
|||
|
|
//!
|
||
|
|
//! `run_workflow` and `run_workflow_tracked` are the public entry-points
|
||
|
|
//! for running a complete `WorkflowScript`. They create an isolated findings
|
||
|
|
//! scope and delegate to `execute_primitive`, then format the results into a
|
||
|
|
//! human-readable summary string.
|
||
|
|
|
||
|
|
use crate::app::workflow::script::WorkflowScript;
|
||
|
|
use std::collections::HashMap;
|
||
|
|
use std::sync::{
|
||
|
|
atomic::AtomicBool,
|
||
|
|
Arc, Mutex,
|
||
|
|
};
|
||
|
|
|
||
|
|
use super::primitives::{execute_primitive, PrimitiveCtx};
|
||
|
|
use super::LiveStateFn;
|
||
|
|
|
||
|
|
/// Run a `WorkflowScript` with the given template arguments and produce a
|
||
|
|
/// summary string. Uses no live-state callback.
|
||
|
|
///
|
||
|
|
/// Return: a human-readable summary string.
|
||
|
|
pub fn run_workflow(
|
||
|
|
script: &WorkflowScript,
|
||
|
|
args: &HashMap<String, String>,
|
||
|
|
session_dir: &std::path::Path,
|
||
|
|
workspaces: &[std::path::PathBuf],
|
||
|
|
) -> anyhow::Result<String> {
|
||
|
|
run_workflow_tracked(script, args, &None, None, session_dir, workspaces)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Run a `WorkflowScript` with real-time live-state callbacks so the TUI
|
||
|
|
/// panel updates as each agent transitions between Idle/Running/Done/Failed.
|
||
|
|
///
|
||
|
|
/// Flow: create an empty findings Arc (scoped to this invocation) → cap
|
||
|
|
/// concurrency to 8 → call `execute_primitive` with the live callback and
|
||
|
|
/// findings → format results.
|
||
|
|
///
|
||
|
|
/// Why: findings are scoped to an `Arc<Mutex<Vec<String>>>` rather than a
|
||
|
|
/// global static, so concurrent `run_workflow_tracked` calls from different
|
||
|
|
/// `spawn_agents` invocations remain fully isolated.
|
||
|
|
///
|
||
|
|
/// Return: a human-readable summary string.
|
||
|
|
pub fn run_workflow_tracked(
|
||
|
|
script: &WorkflowScript,
|
||
|
|
args: &HashMap<String, String>,
|
||
|
|
abort_flag: &Option<Arc<AtomicBool>>,
|
||
|
|
live: Option<&LiveStateFn>,
|
||
|
|
session_dir: &std::path::Path,
|
||
|
|
workspaces: &[std::path::PathBuf],
|
||
|
|
) -> anyhow::Result<String> {
|
||
|
|
let concurrency_cap = if script.options.max_concurrency > 0 {
|
||
|
|
script.options.max_concurrency.min(10) // allow up to 10 parallel agents
|
||
|
|
} else {
|
||
|
|
10
|
||
|
|
};
|
||
|
|
|
||
|
|
let findings = Arc::new(Mutex::new(Vec::new()));
|
||
|
|
let results = execute_primitive(PrimitiveCtx {
|
||
|
|
primitive: &script.script,
|
||
|
|
args,
|
||
|
|
concurrency_cap,
|
||
|
|
continue_on_error: script.options.continue_on_error,
|
||
|
|
abort_flag,
|
||
|
|
live,
|
||
|
|
session_dir,
|
||
|
|
workspaces,
|
||
|
|
findings: &findings,
|
||
|
|
timeout_ms: script.options.timeout_ms,
|
||
|
|
})?;
|
||
|
|
|
||
|
|
let summary = if results.is_empty() {
|
||
|
|
"workflow completed with no output".to_string()
|
||
|
|
} else {
|
||
|
|
format!(
|
||
|
|
"workflow '{}' completed. {} agent result(s):\n{}",
|
||
|
|
script.name,
|
||
|
|
results.len(),
|
||
|
|
results
|
||
|
|
.iter()
|
||
|
|
.enumerate()
|
||
|
|
.map(|(i, r)| format!("[{}] {}", i + 1, r.lines().next().unwrap_or(r)))
|
||
|
|
.collect::<Vec<_>>()
|
||
|
|
.join("\n")
|
||
|
|
)
|
||
|
|
};
|
||
|
|
|
||
|
|
Ok(summary)
|
||
|
|
}
|