#![allow(dead_code)]
use std::sync::Arc;
use std::collections::HashMap;
use super::load_agents_dir::AgentDefinition;
#[derive(Debug, Clone)]
pub struct AgentSourceGroup {
pub label: &'static str,
pub source: &'static str,
}
pub const AGENT_SOURCE_GROUPS: &[AgentSourceGroup] = &[
AgentSourceGroup {
label: "User agents",
source: "userSettings",
},
AgentSourceGroup {
label: "Project agents",
source: "projectSettings",
},
AgentSourceGroup {
label: "Local agents",
source: "localSettings",
},
AgentSourceGroup {
label: "Managed agents",
source: "policySettings",
},
AgentSourceGroup {
label: "Plugin agents",
source: "plugin",
},
AgentSourceGroup {
label: "CLI arg agents",
source: "flagSettings",
},
AgentSourceGroup {
label: "Built-in agents",
source: "built-in",
},
];
#[derive(Debug, Clone)]
pub struct ResolvedAgent {
pub definition: AgentDefinition,
pub overridden_by: Option<String>,
}
pub fn resolve_agent_overrides(
all_agents: Vec<AgentDefinition>,
active_agents: &[AgentDefinition],
) -> Vec<ResolvedAgent> {
let mut active_map: HashMap<String, &AgentDefinition> = HashMap::new();
for agent in active_agents {
active_map.insert(agent.agent_type.clone(), agent);
}
let mut seen: HashMap<String, bool> = HashMap::new();
let mut resolved: Vec<ResolvedAgent> = Vec::new();
for agent in all_agents {
let key = format!("{}:{}", agent.agent_type, agent.source);
if seen.contains_key(&key) {
continue;
}
seen.insert(key, true);
let overridden_by = active_map.get(&agent.agent_type).and_then(|active| {
if active.source != agent.source {
Some(active.source.clone())
} else {
None
}
});
resolved.push(ResolvedAgent {
definition: agent,
overridden_by,
});
}
resolved
}
pub fn resolve_agent_model_display(agent: &AgentDefinition) -> Option<String> {
let model = agent
.model
.as_deref()
.unwrap_or_else(|| get_default_subagent_model().unwrap_or("sonnet"));
if model.is_empty() {
return None;
}
Some(if model == "inherit" {
"inherit".to_string()
} else {
model.to_string()
})
}
fn get_default_subagent_model() -> Option<&'static str> {
Some("sonnet")
}
pub fn get_override_source_label(source: &str) -> String {
get_source_display_name(source).to_lowercase()
}
fn get_source_display_name(source: &str) -> &str {
match source {
"userSettings" => "User",
"projectSettings" => "Project",
"localSettings" => "Local",
"policySettings" => "Managed",
"plugin" => "Plugin",
"flagSettings" => "CLI",
"built-in" => "Built-in",
_ => source,
}
}
pub fn compare_agents_by_name(a: &AgentDefinition, b: &AgentDefinition) -> std::cmp::Ordering {
a.agent_type
.to_lowercase()
.cmp(&b.agent_type.to_lowercase())
}
pub fn format_agent_line(agent: &AgentDefinition) -> String {
let tools_description = get_tools_description(agent);
format!(
"- {}: {} (Tools: {})",
agent.agent_type, agent.when_to_use, tools_description
)
}
fn get_tools_description(agent: &AgentDefinition) -> String {
let has_allowlist = agent.tools.iter().any(|t| !t.starts_with('-'));
let denylist: Vec<&String> = agent
.disallowed_tools
.iter()
.filter(|t| t.starts_with('-'))
.collect();
let has_denylist = !denylist.is_empty();
if has_allowlist && has_denylist {
let deny_set: std::collections::HashSet<&str> =
denylist.iter().map(|t| t.as_str()).collect();
let effective: Vec<&String> = agent
.tools
.iter()
.filter(|t| !deny_set.contains(t.as_str()))
.collect();
if effective.is_empty() {
return "None".to_string();
}
effective
.iter()
.map(|t| t.as_str())
.collect::<Vec<_>>()
.join(", ")
} else if has_allowlist {
agent.tools.join(", ")
} else if has_denylist {
let tools: Vec<&str> = denylist.iter().map(|t| t.as_str()).collect();
format!("All tools except {}", tools.join(", "))
} else {
"All tools".to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_agent(agent_type: &str, source: &str) -> AgentDefinition {
AgentDefinition {
agent_type: agent_type.to_string(),
when_to_use: "test".to_string(),
tools: vec!["*".to_string()],
source: source.to_string(),
base_dir: "built-in".to_string(),
get_system_prompt: Arc::new(|| String::new()),
model: None,
disallowed_tools: vec![],
max_turns: None,
permission_mode: None,
effort: None,
color: None,
mcp_servers: vec![],
hooks: None,
skills: vec![],
background: false,
initial_prompt: None,
memory: None,
isolation: None,
required_mcp_servers: vec![],
omit_claude_md: false,
critical_system_reminder_experimental: None,
}
}
#[test]
fn test_resolve_overrides() {
let all_agents = vec![
make_agent("test", "built-in"),
make_agent("test", "userSettings"),
];
let active = vec![make_agent("test", "userSettings")];
let resolved = resolve_agent_overrides(all_agents, &active);
assert_eq!(resolved.len(), 2);
}
#[test]
fn test_compare_agents_by_name() {
let a = make_agent("Beta", "built-in");
let b = make_agent("alpha", "built-in");
assert_eq!(compare_agents_by_name(&a, &b), std::cmp::Ordering::Greater);
}
#[test]
fn test_resolve_agent_model_display_inherit() {
let agent = make_agent("test", "built-in");
assert_eq!(
resolve_agent_model_display(&agent),
Some("sonnet".to_string())
);
}
}