use std::sync::Arc;
use theway_core::multiagent::types::{AgentRunParams, AgentRunResolver};
pub const DEFAULT_MAX_ITERATIONS: u32 = 300;
#[derive(Clone, Copy)]
pub struct SubagentSpec {
pub name: &'static str,
pub description: &'static str,
pub system_prompt: &'static str,
pub max_iterations: u32,
}
pub static SUBAGENT_SPECS: [SubagentSpec; 6] = [
SubagentSpec {
name: "explorer",
description: "Investigation: search and read local context.",
system_prompt: "You are an exploration subagent dispatched by a coding agent. \
Gather facts from the codebase and context you are given. \
Stay focused on the prompt; return a concise findings summary.",
max_iterations: DEFAULT_MAX_ITERATIONS,
},
SubagentSpec {
name: "planner",
description: "Planning from local context.",
system_prompt: "You are a planning subagent dispatched by a coding agent. \
Turn the given task into a concrete, step-by-step plan. \
Stay focused on the prompt; return the plan as your final answer.",
max_iterations: DEFAULT_MAX_ITERATIONS,
},
SubagentSpec {
name: "executor-coder",
description: "Full coding agent: read/write/edit/bash plus git.",
system_prompt: "You are a coding subagent dispatched by a coding agent. \
Implement the requested change using your tools. \
Stay focused on the prompt; return a concise summary of what you changed.",
max_iterations: DEFAULT_MAX_ITERATIONS,
},
SubagentSpec {
name: "checker",
description: "Verification: check results with evidence.",
system_prompt: "You are a verification subagent dispatched by a coding agent. \
Check the given work against the task and report pass/fail with evidence. \
Stay focused on the prompt; return a concise verdict.",
max_iterations: DEFAULT_MAX_ITERATIONS,
},
SubagentSpec {
name: "general",
description: "General-purpose research subagent (same as the subagent tool).",
system_prompt: "You are a research subagent dispatched by a coding agent. \
Stay focused on the prompt; return a concise final answer.",
max_iterations: DEFAULT_MAX_ITERATIONS,
},
SubagentSpec {
name: "goal-evaluator",
description: "Goal stop-condition judge (tool-less, single turn).",
system_prompt: theway_core::multiagent::goal::evaluator_system_prompt(),
max_iterations: 1,
},
];
pub fn launch_resolver() -> AgentRunResolver {
Arc::new(|name: &str| {
SUBAGENT_SPECS
.iter()
.find(|s| s.name == name)
.map(|s| AgentRunParams {
name: s.name,
description: s.description,
system_prompt: s.system_prompt,
max_iterations: s.max_iterations,
})
})
}
pub fn spec_names() -> Vec<String> {
SUBAGENT_SPECS.iter().map(|s| s.name.to_string()).collect()
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
#[test]
fn names_are_unique_and_ordered() {
let names = spec_names();
assert_eq!(
names,
vec![
"explorer",
"planner",
"executor-coder",
"checker",
"general",
"goal-evaluator"
]
);
let unique: HashSet<&str> = names.iter().map(String::as_str).collect();
assert_eq!(unique.len(), names.len(), "duplicate spec names");
}
#[test]
fn resolver_roundtrips_all_specs_and_rejects_unknown() {
for spec in &SUBAGENT_SPECS {
let launch = launch_resolver()(spec.name).expect("known name must resolve");
assert_eq!(launch.name, spec.name);
assert_eq!(launch.description, spec.description);
assert_eq!(launch.system_prompt, spec.system_prompt);
assert_eq!(launch.max_iterations, spec.max_iterations);
}
assert!(launch_resolver()("no-such-agent").is_none());
assert!(launch_resolver()("").is_none());
}
}