use crate::core::Prompt;
use crate::mcp::{McpConfig, ToolFilter};
use llm::{LlmModel, ModelSettings, ProviderConnectionOverrides, ReasoningEffort};
#[derive(Debug, Clone)]
pub struct AgentSpec {
pub name: String,
pub description: String,
pub model: String,
pub reasoning_effort: Option<ReasoningEffort>,
pub model_settings: ModelSettings,
pub context_window: Option<u32>,
pub prompts: Vec<Prompt>,
pub provider_connections: ProviderConnectionOverrides,
pub mcp: McpConfig,
pub exposure: AgentSpecExposure,
pub tools: ToolFilter,
}
impl AgentSpec {
pub fn bare(model: &LlmModel, reasoning_effort: Option<ReasoningEffort>, prompts: Vec<Prompt>) -> Self {
Self {
name: "__default__".to_string(),
description: "Default agent".to_string(),
model: model.to_string(),
reasoning_effort,
model_settings: ModelSettings::default(),
context_window: None,
prompts,
provider_connections: ProviderConnectionOverrides::default(),
mcp: McpConfig::default(),
exposure: AgentSpecExposure::none(),
tools: ToolFilter::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AgentSpecExposure {
pub user_invocable: bool,
pub agent_invocable: bool,
}
impl AgentSpecExposure {
pub fn none() -> Self {
Self { user_invocable: false, agent_invocable: false }
}
pub fn user_only() -> Self {
Self { user_invocable: true, agent_invocable: false }
}
pub fn agent_only() -> Self {
Self { user_invocable: false, agent_invocable: true }
}
pub fn both() -> Self {
Self { user_invocable: true, agent_invocable: true }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn default_spec_has_expected_fields() {
let model: LlmModel = "anthropic:claude-sonnet-4-5".parse().unwrap();
let prompts = vec![Prompt::file(PathBuf::from("/tmp/BASE.md"), PathBuf::from("/tmp"))];
let spec = AgentSpec::bare(&model, None, prompts.clone());
assert_eq!(spec.name, "__default__");
assert_eq!(spec.description, "Default agent");
assert_eq!(spec.model, model.to_string());
assert!(spec.reasoning_effort.is_none());
assert_eq!(spec.prompts.len(), 1);
assert!(spec.mcp.is_empty());
assert_eq!(spec.exposure, AgentSpecExposure::none());
}
}