use brainwires_core::Tool;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AgentRole {
Exploration,
Planning,
Verification,
Execution,
}
impl AgentRole {
pub fn allowed_tools(self) -> Option<&'static [&'static str]> {
match self {
Self::Exploration => Some(&[
"read_file",
"list_directory",
"search_code",
"query_codebase",
"fetch_url",
"web_search",
"glob",
"grep",
"context_recall",
"task_get",
"task_list",
]),
Self::Planning => Some(&[
"read_file",
"list_directory",
"glob",
"grep",
"task_create",
"task_update",
"task_add_subtask",
"task_list",
"task_get",
"plan_task",
"context_recall",
]),
Self::Verification => Some(&[
"read_file",
"list_directory",
"glob",
"grep",
"execute_command",
"check_duplicates",
"verify_build",
"check_syntax",
"task_get",
"task_list",
"context_recall",
]),
Self::Execution => None,
}
}
pub fn filter_tools(self, tools: &[Tool]) -> Vec<Tool> {
match self.allowed_tools() {
None => tools.to_vec(),
Some(allow) => tools
.iter()
.filter(|t| allow.contains(&t.name.as_str()))
.cloned()
.collect(),
}
}
pub fn system_prompt_suffix(self) -> &'static str {
match self {
Self::Exploration => {
"\n\n[ROLE: Exploration] You may only read files and search. \
Do not attempt to write files, run commands, or spawn agents."
}
Self::Planning => {
"\n\n[ROLE: Planning] You may read files and manage tasks. \
Do not write files or execute code — produce a plan only."
}
Self::Verification => {
"\n\n[ROLE: Verification] You may read files and run build/test commands. \
Do not write or delete files."
}
Self::Execution => "",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fake_tool(name: &str) -> Tool {
Tool {
name: name.to_string(),
description: String::new(),
input_schema: brainwires_core::ToolInputSchema::default(),
requires_approval: false,
defer_loading: false,
allowed_callers: vec![],
input_examples: vec![],
serialize: false,
}
}
#[test]
fn exploration_filters_write_tools() {
let tools = vec![
fake_tool("read_file"),
fake_tool("write_file"),
fake_tool("execute_command"),
fake_tool("glob"),
];
let filtered = AgentRole::Exploration.filter_tools(&tools);
let names: Vec<&str> = filtered.iter().map(|t| t.name.as_str()).collect();
assert!(names.contains(&"read_file"));
assert!(names.contains(&"glob"));
assert!(!names.contains(&"write_file"));
assert!(!names.contains(&"execute_command"));
}
#[test]
fn execution_passes_all_tools() {
let tools = vec![fake_tool("read_file"), fake_tool("write_file")];
let filtered = AgentRole::Execution.filter_tools(&tools);
assert_eq!(filtered.len(), 2);
}
#[test]
fn planning_allows_task_tools_not_write_or_execute() {
let tools = vec![
fake_tool("read_file"),
fake_tool("task_create"),
fake_tool("task_update"),
fake_tool("plan_task"),
fake_tool("write_file"),
fake_tool("execute_command"),
];
let filtered = AgentRole::Planning.filter_tools(&tools);
let names: Vec<&str> = filtered.iter().map(|t| t.name.as_str()).collect();
assert!(names.contains(&"read_file"));
assert!(names.contains(&"task_create"));
assert!(names.contains(&"task_update"));
assert!(names.contains(&"plan_task"));
assert!(!names.contains(&"write_file"));
assert!(!names.contains(&"execute_command"));
}
#[test]
fn verification_allows_execute_command_not_write() {
let tools = vec![
fake_tool("read_file"),
fake_tool("execute_command"),
fake_tool("verify_build"),
fake_tool("write_file"),
fake_tool("task_create"),
];
let filtered = AgentRole::Verification.filter_tools(&tools);
let names: Vec<&str> = filtered.iter().map(|t| t.name.as_str()).collect();
assert!(names.contains(&"read_file"));
assert!(names.contains(&"execute_command"));
assert!(names.contains(&"verify_build"));
assert!(!names.contains(&"write_file"));
assert!(!names.contains(&"task_create"));
}
#[test]
fn system_prompt_suffix_non_empty_for_constrained_roles() {
assert!(!AgentRole::Exploration.system_prompt_suffix().is_empty());
assert!(!AgentRole::Planning.system_prompt_suffix().is_empty());
assert!(!AgentRole::Verification.system_prompt_suffix().is_empty());
assert_eq!(AgentRole::Execution.system_prompt_suffix(), "");
}
}