feat(swarm): add /swarm command with sequential|mixture|distill|deliberate modes (Phase A foundation, #303)

This commit is contained in:
Hunter Bown
2026-05-01 23:48:24 -05:00
parent 359c27437b
commit 87f42656a7
3 changed files with 124 additions and 0 deletions
+103
View File
@@ -144,6 +144,109 @@ pub fn deepseek_links(app: &mut App) -> CommandResult {
))
}
/// Collaboration pattern for `/swarm` multi-agent turns.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SwarmMode {
/// Linear pipeline: Planner → Critic → Solver
Sequential,
/// Parallel ensemble: domain specialists + synthesizer
Mixture,
/// ExpertLearner pair for knowledge distillation
Distill,
/// Reflector + Tool-Caller iterative deliberation
Deliberate,
}
impl SwarmMode {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"sequential" | "seq" | "pipeline" => Some(Self::Sequential),
"mixture" | "mix" | "ensemble" | "parallel" => Some(Self::Mixture),
"distill" | "distillation" | "transfer" => Some(Self::Distill),
"deliberate" | "deliberation" | "dialectic" | "reflect" => Some(Self::Deliberate),
_ => None,
}
}
pub fn label(self) -> &'static str {
match self {
Self::Sequential => "sequential",
Self::Mixture => "mixture",
Self::Distill => "distill",
Self::Deliberate => "deliberate",
}
}
pub fn description(self) -> &'static str {
match self {
Self::Sequential => "Linear pipeline: Planner → Critic → Solver",
Self::Mixture => "Parallel ensemble: domain specialists + synthesizer",
Self::Distill => "ExpertLearner pair for knowledge distillation",
Self::Deliberate => "Reflector + Tool-Caller iterative deliberation",
}
}
}
/// Initiate a multi-agent swarm turn with the requested collaboration pattern.
///
/// Phase A foundation — currently sets up the prompt context so the model
/// uses `agent_swarm` with the appropriate topology. Direct orchestration
/// from the slash command (bypassing the model) is planned for Phase B.
pub fn swarm(app: &mut App, arg: Option<&str>) -> CommandResult {
let raw = arg.map(str::trim).unwrap_or("");
let mut parts = raw.splitn(2, char::is_whitespace);
let mode_str = parts.next().unwrap_or("");
let description = parts.next().map(str::trim).unwrap_or("");
if mode_str.is_empty() {
let mut help = String::from("Usage: /swarm <mode> [description]\n\nModes:\n");
for mode in [
SwarmMode::Sequential,
SwarmMode::Mixture,
SwarmMode::Distill,
SwarmMode::Deliberate,
] {
help.push_str(&format!(" {}{}\n", mode.label(), mode.description()));
}
return CommandResult::message(help);
}
let Some(mode) = SwarmMode::from_str(mode_str) else {
return CommandResult::error(format!(
"Unknown swarm mode: {mode_str}. Try /swarm for a list of modes."
));
};
let msg = if description.is_empty() {
format!(
"Swarm mode: {}. Describe the task and I will delegate it using the {} pattern.",
mode.label(),
mode.label()
)
} else {
format!(
"Swarm mode: {}. Delegating using the {} pattern:\n{}",
mode.label(),
mode.label(),
description
)
};
// Queue a system message that primes the model to use agent_swarm
// with the requested topology. In Phase B this will be replaced by
// direct engine-side orchestration.
let system_hint = format!(
"The user has requested a {} swarm. Use the agent_swarm tool with the appropriate topology. \
For sequential: use depends_on chains. For mixture: spawn specialists in parallel then synthesize. \
For distill: spawn an expert and a learner, then have the learner absorb the expert's output. \
For deliberate: spawn a reflector and a tool-caller in a critique→refine loop.",
mode.label()
);
app.system_prompt = Some(crate::models::SystemPrompt::Text(system_hint));
CommandResult::message(msg)
}
/// Show home dashboard with stats and quick actions
pub fn home_dashboard(app: &mut App) -> CommandResult {
let locale = app.ui_locale;
+7
View File
@@ -390,6 +390,12 @@ pub const COMMANDS: &[CommandInfo] = &[
usage: "/cache [count]",
description_id: MessageId::CmdCacheDescription,
},
CommandInfo {
name: "swarm",
aliases: &[],
usage: "/swarm <sequential|mixture|distill|deliberate> [description]",
description_id: MessageId::CmdSwarmDescription,
},
];
/// Execute a slash command
@@ -410,6 +416,7 @@ pub fn execute(cmd: &str, app: &mut App) -> CommandResult {
"provider" => provider::provider(app, arg),
"queue" | "queued" => queue::queue(app, arg),
"subagents" | "agents" => core::subagents(app),
"swarm" => core::swarm(app, arg),
"links" | "dashboard" | "api" => core::deepseek_links(app),
"home" | "stats" | "overview" => core::home_dashboard(app),
"note" => note::note(app, arg),
+14
View File
@@ -252,6 +252,7 @@ pub enum MessageId {
CmdSkillsDescription,
CmdStatuslineDescription,
CmdSubagentsDescription,
CmdSwarmDescription,
CmdSystemDescription,
CmdTaskDescription,
CmdTokensDescription,
@@ -430,6 +431,7 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[
MessageId::CmdSkillsDescription,
MessageId::CmdStatuslineDescription,
MessageId::CmdSubagentsDescription,
MessageId::CmdSwarmDescription,
MessageId::CmdSystemDescription,
MessageId::CmdTaskDescription,
MessageId::CmdTokensDescription,
@@ -748,6 +750,9 @@ fn english(id: MessageId) -> &'static str {
}
MessageId::CmdStatuslineDescription => "Configure which items appear in the footer",
MessageId::CmdSubagentsDescription => "List sub-agent status",
MessageId::CmdSwarmDescription => {
"Run a multi-agent swarm turn (sequential | mixture | distill | deliberate)"
}
MessageId::CmdSystemDescription => "Show current system prompt",
MessageId::CmdTaskDescription => "Manage background tasks",
MessageId::CmdTokensDescription => "Show token usage for session",
@@ -1012,6 +1017,9 @@ fn japanese(id: MessageId) -> Option<&'static str> {
}
MessageId::CmdStatuslineDescription => "フッターに表示する項目を設定",
MessageId::CmdSubagentsDescription => "サブエージェントの状態を一覧表示",
MessageId::CmdSwarmDescription => {
"マルチエージェントのスワームターンを実行(sequential | mixture | distill | deliberate"
}
MessageId::CmdSystemDescription => "現在のシステムプロンプトを表示",
MessageId::CmdTaskDescription => "バックグラウンドタスクを管理",
MessageId::CmdTokensDescription => "セッションのトークン使用量を表示",
@@ -1254,6 +1262,9 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> {
MessageId::CmdSkillsDescription => "列出本地技能(或使用 --remote 浏览精选注册表)",
MessageId::CmdStatuslineDescription => "配置底栏要显示哪些条目",
MessageId::CmdSubagentsDescription => "列出子代理状态",
MessageId::CmdSwarmDescription => {
"运行多代理集群轮次(sequential | mixture | distill | deliberate"
}
MessageId::CmdSystemDescription => "显示当前系统提示词",
MessageId::CmdTaskDescription => "管理后台任务",
MessageId::CmdTokensDescription => "显示本次会话的 token 用量",
@@ -1502,6 +1513,9 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> {
}
MessageId::CmdStatuslineDescription => "Configurar quais itens aparecem no rodapé",
MessageId::CmdSubagentsDescription => "Listar o status dos sub-agentes",
MessageId::CmdSwarmDescription => {
"Executar turno de enxame multi-agente (sequential | mixture | distill | deliberate)"
}
MessageId::CmdSystemDescription => "Exibir o prompt de sistema atual",
MessageId::CmdTaskDescription => "Gerenciar tarefas em segundo plano",
MessageId::CmdTokensDescription => "Exibir o uso de tokens da sessão",