feat(tui): add /swarm WhaleFlow-backed multi-agent overlay (#3178)

/swarm [N] <task> is a high-fanout overlay on the current mode (Agent/Plan/YOLO),
NOT a fourth mode. It mirrors the proven /agent command pattern: it emits a
model instruction to decompose the task, fan out one headless sub-agent per
subtask via agent_open (role/agent_type per subtask, max_depth N for nesting),
run them concurrently, and synthesize from each worker's result SUMMARY (not its
transcript) — the isolation pattern that keeps the orchestrator light.

Registered with aliases fanout/qun; CmdSwarmDescription was already localized in
all locales. Validated by the command-registry completeness tests (unique
names/aliases, complete metadata); broad commands suite 431/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hunter B
2026-06-13 02:09:51 -07:00
parent 04aa64d569
commit 30992aa0e3
@@ -36,6 +36,7 @@ impl CommandGroup for CoreCommands {
Box::new(FunctionCommand::new(&HOOKS_INFO, run_hooks)),
Box::new(FunctionCommand::new(&SUBAGENTS_INFO, run_subagents)),
Box::new(FunctionCommand::new(&AGENT_INFO, run_agent)),
Box::new(FunctionCommand::new(&SWARM_INFO, run_swarm)),
Box::new(FunctionCommand::new(&LINKS_INFO, run_links)),
Box::new(FunctionCommand::new(&FEEDBACK_INFO, run_feedback)),
Box::new(FunctionCommand::new(&HF_INFO, run_hf)),
@@ -123,6 +124,12 @@ static AGENT_INFO: CommandInfo = CommandInfo {
usage: "/agent [N] <task>",
description_id: MessageId::CmdAgentDescription,
};
static SWARM_INFO: CommandInfo = CommandInfo {
name: "swarm",
aliases: &["fanout", "qun"],
usage: "/swarm [N] <task>",
description_id: MessageId::CmdSwarmDescription,
};
static LINKS_INFO: CommandInfo = CommandInfo {
name: "links",
aliases: &["dashboard", "api", "lianjie"],
@@ -230,6 +237,9 @@ fn run_subagents(app: &mut App, arg: Option<&str>) -> CommandResult {
fn run_agent(app: &mut App, arg: Option<&str>) -> CommandResult {
run_registered(app, "agent", arg)
}
fn run_swarm(app: &mut App, arg: Option<&str>) -> CommandResult {
run_registered(app, "swarm", arg)
}
fn run_links(app: &mut App, arg: Option<&str>) -> CommandResult {
run_registered(app, "links", arg)
}
@@ -282,6 +292,7 @@ pub(in crate::commands) fn dispatch(
"hooks" | "hook" | "gouzi" => hooks::hooks(app, arg),
"subagents" | "agents" | "zhinengti" => core::subagents(app),
"agent" | "daili" => agent(app, arg),
"swarm" | "fanout" | "qun" => swarm(app, arg),
"links" | "dashboard" | "api" | "lianjie" => core::deepseek_links(app),
"feedback" => feedback::feedback(app, arg),
"hf" | "huggingface" => hf::hf(app, arg),
@@ -361,6 +372,35 @@ pub fn agent(_app: &mut App, arg: Option<&str>) -> CommandResult {
)
}
/// Run a WhaleFlow-backed multi-agent swarm: high-fanout headless sub-agents
/// over one task. This is an overlay on the current mode (Agent/Plan/YOLO), not
/// a fourth mode — it instructs the model to decompose and fan out, collecting
/// compact result summaries rather than child transcripts (#3178).
pub fn swarm(_app: &mut App, arg: Option<&str>) -> CommandResult {
let (max_depth, task) = match parse_depth_prefixed_arg(arg, 1) {
Ok(parsed) => parsed,
Err(message) => return CommandResult::error(message),
};
let task = match task {
Some(task) if !task.trim().is_empty() => task.trim().to_string(),
_ => {
return CommandResult::error(
"Usage: /swarm [N] <task>\n\n\
Runs a multi-agent swarm: decomposes the task and fans out \
headless sub-agents (recursive depth N, 0-3, default 1), then \
synthesizes their results.",
);
}
};
let message = format!(
"Run a multi-agent swarm for this task: {task:?}. Decompose it into independent, parallelizable subtasks and open one headless sub-agent per subtask with `agent_open` (pass `max_depth: {max_depth}` for nested delegation, and an `agent_type`/role that fits each subtask — explore for research, review for verification, implementer for edits). Run them concurrently; collect each worker's result summary with `agent_eval` (summaries, not full transcripts) and synthesize a single answer. Keep the fanout proportional to the task, and verify any claimed side effects before reporting success."
);
CommandResult::with_message_and_action(
format!("Dispatching a swarm at depth {max_depth}..."),
AppAction::SendMessage(message),
)
}
fn parse_depth_prefixed_arg(
arg: Option<&str>,
default_depth: u32,