use crate::tools::ToolRegistry;
pub fn is_coordinator_mode() -> bool {
cfg!(feature = "coordinator-mode")
&& std::env::var("RECURSIVE_COORDINATOR_MODE").as_deref() == Ok("1")
}
pub fn coordinator_tool_set() -> &'static [&'static str] {
&[
"Read",
"Grep",
"Glob",
"shared_memory_read",
"list_workers",
"team_create",
"team_delete",
"send_message",
"task_create",
"task_get",
"task_list",
"task_output",
"task_stop",
"task_update",
"TodoWrite",
"WebFetch",
"WebSearch",
"agent",
"enter_plan_mode",
"exit_plan_mode",
"request_plan_mode",
]
}
pub fn is_coordinator_tool(tool_name: &str) -> bool {
coordinator_tool_set().contains(&tool_name)
}
pub fn coordinator_deny_list() -> &'static [&'static str] {
&[
"Edit", "Write", "Bash",
]
}
pub fn is_allowed_in_coordinator_mode(tool_name: &str) -> bool {
is_coordinator_tool(tool_name) && !coordinator_deny_list().contains(&tool_name)
}
pub fn filter_registry(registry: &mut ToolRegistry) {
if is_coordinator_mode() {
let allow: Vec<String> = coordinator_tool_set()
.iter()
.copied()
.filter(|name| is_allowed_in_coordinator_mode(name))
.map(str::to_string)
.collect();
registry.retain_tools(&allow);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tools::build_standard_tools;
#[test]
fn allow_list_excludes_mutating_tools() {
for forbidden in ["Edit", "Write", "Bash"] {
assert!(
!is_allowed_in_coordinator_mode(forbidden),
"coordinator mode must forbid `{forbidden}`"
);
}
}
#[test]
fn allow_list_includes_team_and_task_tools() {
for required in [
"team_create",
"team_delete",
"send_message",
"task_create",
"task_get",
"task_list",
"task_output",
"task_stop",
"task_update",
] {
assert!(
is_allowed_in_coordinator_mode(required),
"coordinator mode must allow `{required}`"
);
}
}
#[test]
fn allow_list_includes_agent_and_read_only_introspection() {
for required in ["agent", "Read", "Grep", "Glob", "shared_memory_read"] {
assert!(
is_allowed_in_coordinator_mode(required),
"coordinator mode must allow `{required}`"
);
}
}
#[test]
fn deny_list_blocks_even_if_allowlist_grows() {
assert!(coordinator_deny_list().contains(&"Edit"));
assert!(coordinator_deny_list().contains(&"Write"));
assert!(coordinator_deny_list().contains(&"Bash"));
}
#[test]
fn is_coordinator_tool_false_for_unknown_tool() {
assert!(
!is_coordinator_tool("NonExistentTool"),
"unknown tool must not be in the coordinator tool set"
);
}
#[test]
fn is_allowed_in_coordinator_mode_false_for_unknown_tool() {
assert!(
!is_allowed_in_coordinator_mode("NotATool"),
"unknown tool must not be allowed in coordinator mode"
);
}
#[test]
fn is_allowed_requires_both_allowlist_and_not_denylist() {
assert!(
!is_allowed_in_coordinator_mode("Edit"),
"Edit must be blocked regardless of allow list membership"
);
}
#[test]
fn coordinator_tool_set_is_non_empty() {
assert!(
!coordinator_tool_set().is_empty(),
"coordinator tool set must not be empty"
);
}
#[test]
fn is_coordinator_tool_true_for_agent_tool() {
assert!(
is_coordinator_tool("agent"),
"'agent' must be in coordinator tool set"
);
}
#[test]
fn is_coordinator_mode_false_without_env_var() {
let _env_lock = crate::test_util::env_lock();
let prev = std::env::var("RECURSIVE_COORDINATOR_MODE").ok();
std::env::remove_var("RECURSIVE_COORDINATOR_MODE");
let mode = is_coordinator_mode();
if let Some(v) = prev {
std::env::set_var("RECURSIVE_COORDINATOR_MODE", v);
}
if !cfg!(feature = "coordinator-mode") {
assert!(!mode, "coordinator mode must be false without feature");
} else {
assert!(
!mode,
"coordinator mode must be false without the env var even with feature"
);
}
}
#[test]
fn cargo_feature_gate_respected() {
let _env_lock = crate::test_util::env_lock();
let prev = std::env::var("RECURSIVE_COORDINATOR_MODE").ok();
std::env::set_var("RECURSIVE_COORDINATOR_MODE", "1");
let on_with_feature = cfg!(feature = "coordinator-mode");
let _ = is_coordinator_mode(); match prev {
Some(v) => std::env::set_var("RECURSIVE_COORDINATOR_MODE", v),
None => std::env::remove_var("RECURSIVE_COORDINATOR_MODE"),
}
let _ = on_with_feature;
}
#[test]
fn is_coordinator_mode_rejects_non_one_env_value() {
let _env_lock = crate::test_util::env_lock();
let prev = std::env::var("RECURSIVE_COORDINATOR_MODE").ok();
std::env::set_var("RECURSIVE_COORDINATOR_MODE", "0");
let mode = is_coordinator_mode();
match prev {
Some(v) => std::env::set_var("RECURSIVE_COORDINATOR_MODE", v),
None => std::env::remove_var("RECURSIVE_COORDINATOR_MODE"),
}
assert!(
!mode,
"RECURSIVE_COORDINATOR_MODE=0 must not enable coordinator mode"
);
}
#[test]
fn is_coordinator_mode_false_when_env_one_without_cargo_feature() {
let _env_lock = crate::test_util::env_lock();
if cfg!(feature = "coordinator-mode") {
return;
}
let prev = std::env::var("RECURSIVE_COORDINATOR_MODE").ok();
std::env::set_var("RECURSIVE_COORDINATOR_MODE", "1");
assert!(
!is_coordinator_mode(),
"env=1 must not enable coordinator mode without the cargo feature"
);
match prev {
Some(v) => std::env::set_var("RECURSIVE_COORDINATOR_MODE", v),
None => std::env::remove_var("RECURSIVE_COORDINATOR_MODE"),
}
}
#[cfg(feature = "coordinator-mode")]
#[test]
fn is_coordinator_mode_true_when_feature_and_env_one() {
let _env_lock = crate::test_util::env_lock();
let prev = std::env::var("RECURSIVE_COORDINATOR_MODE").ok();
std::env::set_var("RECURSIVE_COORDINATOR_MODE", "1");
assert!(
is_coordinator_mode(),
"coordinator mode must be on when feature and env=1"
);
match prev {
Some(v) => std::env::set_var("RECURSIVE_COORDINATOR_MODE", v),
None => std::env::remove_var("RECURSIVE_COORDINATOR_MODE"),
}
}
#[test]
fn filter_registry_noop_outside_coordinator_mode() {
let _env_lock = crate::test_util::env_lock();
let prev = std::env::var("RECURSIVE_COORDINATOR_MODE").ok();
std::env::remove_var("RECURSIVE_COORDINATOR_MODE");
let tmp = tempfile::tempdir().expect("tempdir");
let mut reg = build_standard_tools(tmp.path(), &[], 30);
assert!(
reg.find_by_name("Edit").is_some(),
"fixture must include Edit before filter"
);
filter_registry(&mut reg);
assert!(
reg.find_by_name("Edit").is_some(),
"filter_registry must be a no-op outside coordinator mode"
);
match prev {
Some(v) => std::env::set_var("RECURSIVE_COORDINATOR_MODE", v),
None => std::env::remove_var("RECURSIVE_COORDINATOR_MODE"),
}
}
#[cfg(feature = "coordinator-mode")]
#[test]
fn filter_registry_prunes_mutating_tools_in_coordinator_mode() {
let _env_lock = crate::test_util::env_lock();
let prev = std::env::var("RECURSIVE_COORDINATOR_MODE").ok();
std::env::set_var("RECURSIVE_COORDINATOR_MODE", "1");
let tmp = tempfile::tempdir().expect("tempdir");
let mut reg = build_standard_tools(tmp.path(), &[], 30);
filter_registry(&mut reg);
assert!(
reg.find_by_name("Edit").is_none(),
"Edit must be pruned in coordinator mode"
);
assert!(
reg.find_by_name("Read").is_some(),
"Read must remain in coordinator mode"
);
match prev {
Some(v) => std::env::set_var("RECURSIVE_COORDINATOR_MODE", v),
None => std::env::remove_var("RECURSIVE_COORDINATOR_MODE"),
}
}
}