use cersei_tools::Tool;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AgentMode {
Coordinator,
Worker,
Normal,
}
pub const COORDINATOR_ONLY_TOOLS: &[&str] = &[
"Agent",
"SendMessage",
"TaskStop",
"TeamCreate",
"TeamDelete",
"SyntheticOutput",
];
pub fn is_coordinator_mode() -> bool {
match std::env::var("CERSEI_COORDINATOR_MODE") {
Ok(v) => !v.is_empty() && v != "0" && v != "false",
Err(_) => false,
}
}
pub fn filter_tools_for_mode(tools: Vec<Box<dyn Tool>>, mode: AgentMode) -> Vec<Box<dyn Tool>> {
match mode {
AgentMode::Worker => tools
.into_iter()
.filter(|t| !COORDINATOR_ONLY_TOOLS.contains(&t.name()))
.collect(),
AgentMode::Coordinator | AgentMode::Normal => tools,
}
}
pub fn coordinator_system_prompt() -> &'static str {
"## Coordinator Mode\n\n\
You are operating as an orchestrator. Your role is to:\n\
1. Break complex tasks into independent sub-tasks\n\
2. Spawn parallel worker agents using the Agent tool\n\
3. Each worker prompt must be fully self-contained\n\
4. Synthesize findings from all workers before responding\n\
5. Use TaskCreate/TaskUpdate to track parallel work\n\n\
Workers cannot spawn their own sub-agents. They have access to \
filesystem, shell, and web tools only."
}
pub fn coordinator_context(tools: &[Box<dyn Tool>]) -> String {
let tool_list: Vec<String> = tools
.iter()
.filter(|t| !["Agent", "SyntheticOutput"].contains(&t.name()))
.map(|t| format!("- {}: {}", t.name(), t.description()))
.collect();
format!("Available tools for workers:\n{}", tool_list.join("\n"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_filter_worker_tools() {
let tools = cersei_tools::all();
let original_count = tools.len();
let filtered = filter_tools_for_mode(tools, AgentMode::Worker);
assert!(filtered.len() <= original_count);
assert!(filtered
.iter()
.all(|t| !COORDINATOR_ONLY_TOOLS.contains(&t.name())));
}
#[test]
fn test_filter_coordinator_keeps_all() {
let tools = cersei_tools::all();
let count = tools.len();
let filtered = filter_tools_for_mode(tools, AgentMode::Coordinator);
assert_eq!(filtered.len(), count);
}
#[test]
fn test_coordinator_prompt() {
let prompt = coordinator_system_prompt();
assert!(prompt.contains("orchestrator"));
assert!(prompt.contains("sub-tasks"));
}
#[test]
fn test_coordinator_context() {
let tools = cersei_tools::all();
let ctx = coordinator_context(&tools);
assert!(ctx.contains("Available tools"));
assert!(ctx.contains("Read"));
assert!(ctx.contains("Bash"));
}
}