pub mod factories;
pub mod prompts;
use serde::{Deserialize, Serialize};
use crate::config::ActiveModel;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[serde(rename_all = "snake_case")]
pub enum AgentType {
General,
Explorer,
Librarian,
Oracle,
Designer,
Fixer,
}
impl AgentType {
pub fn all() -> &'static [Self] {
&[
Self::General,
Self::Explorer,
Self::Librarian,
Self::Oracle,
Self::Designer,
Self::Fixer,
]
}
pub fn display_name(self) -> &'static str {
match self {
Self::General => "general",
Self::Explorer => "explorer",
Self::Librarian => "librarian",
Self::Oracle => "oracle",
Self::Designer => "designer",
Self::Fixer => "fixer",
}
}
pub fn description(self) -> &'static str {
match self {
Self::General => "General-purpose assistant with multi-agent delegation",
Self::Explorer => {
"Fast codebase search specialist: grep, glob, and read to discover code patterns"
}
Self::Librarian => {
"Documentation and library research: fetches official docs, API references, examples"
}
Self::Oracle => {
"Strategic technical advisor: architecture decisions, code review, complex debugging"
}
Self::Designer => "UI/UX design specialist: frontend design, styling, user experience",
Self::Fixer => {
"Implementation specialist: executes code changes efficiently with full context"
}
}
}
pub fn is_read_only(self) -> bool {
matches!(self, Self::Explorer | Self::Librarian | Self::Oracle)
}
pub fn default_tool_restrictions(self) -> Option<&'static [&'static str]> {
match self {
Self::General => None,
Self::Explorer => Some(&["read", "list", "glob", "grep", "websearch", "webfetch"]),
Self::Librarian => Some(&[
"read",
"list",
"glob",
"grep",
"websearch",
"webfetch",
"question",
]),
Self::Oracle => Some(&[
"read",
"list",
"glob",
"grep",
"websearch",
"webfetch",
"question",
]),
Self::Designer => Some(&[
"read",
"list",
"glob",
"grep",
"write",
"edit",
"bash",
"websearch",
"webfetch",
"question",
"apply_patch",
]),
Self::Fixer => None,
}
}
pub fn default_temperature(self) -> f32 {
match self {
Self::Explorer | Self::Librarian | Self::Oracle => 0.1,
Self::Fixer => 0.2,
Self::Designer => 0.7,
Self::General => 0.3,
}
}
pub fn parse(s: &str) -> Option<Self> {
let s = s.trim().to_ascii_lowercase();
let s = s.strip_prefix('@').unwrap_or(&s);
match s {
"general" => Some(Self::General),
"explorer" => Some(Self::Explorer),
"librarian" => Some(Self::Librarian),
"oracle" => Some(Self::Oracle),
"designer" => Some(Self::Designer),
"fixer" => Some(Self::Fixer),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct AgentDefinition {
pub agent_type: AgentType,
pub display_name: String,
pub description: String,
pub system_prompt: String,
pub allowed_tools: Option<Vec<String>>,
pub model_override: Option<ActiveModel>,
pub temperature: Option<f32>,
pub read_only: bool,
}
impl AgentDefinition {
pub fn new(agent_type: AgentType) -> Self {
let system_prompt = prompts::system_prompt(agent_type);
Self {
agent_type,
display_name: agent_type.display_name().to_string(),
description: agent_type.description().to_string(),
system_prompt,
allowed_tools: agent_type
.default_tool_restrictions()
.map(|tools| tools.iter().map(|s| s.to_string()).collect()),
model_override: None,
temperature: None,
read_only: agent_type.is_read_only(),
}
}
pub fn bootstrap_content(&self) -> String {
self.system_prompt.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_type_parse() {
assert_eq!(AgentType::parse("explorer"), Some(AgentType::Explorer));
assert_eq!(AgentType::parse("@explorer"), Some(AgentType::Explorer));
assert_eq!(AgentType::parse("EXPLORER"), Some(AgentType::Explorer));
assert_eq!(AgentType::parse("unknown"), None);
}
#[test]
fn test_agent_type_read_only() {
assert!(AgentType::Explorer.is_read_only());
assert!(AgentType::Librarian.is_read_only());
assert!(AgentType::Oracle.is_read_only());
assert!(!AgentType::Fixer.is_read_only());
assert!(!AgentType::General.is_read_only());
assert!(!AgentType::Designer.is_read_only());
}
#[test]
fn test_agent_definition_defaults() {
let def = AgentDefinition::new(AgentType::Explorer);
assert_eq!(def.display_name, "explorer");
assert!(def.read_only);
assert!(def.allowed_tools.is_some());
let tools = def.allowed_tools.as_ref().unwrap();
assert!(tools.contains(&"grep".to_string()));
assert!(!tools.contains(&"write".to_string()));
}
}