Skip to main content

aether_project/
agent_config.rs

1use crate::{McpSourceSpec, PromptSource};
2use aether_core::agent_spec::ToolFilter;
3use llm::{ProviderConnectionOverrides, ReasoningEffort};
4
5#[doc = include_str!("docs/agent_config.md")]
6#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
7#[serde(rename_all = "camelCase", deny_unknown_fields)]
8#[schemars(transform = require_agent_invocation_surface_schema)]
9pub struct AgentConfig {
10    /// Agent identifier. Names are trimmed for merge and lookup.
11    #[schemars(length(min = 1))]
12    pub name: String,
13    /// Human-readable description shown in UIs and sub-agent listings.
14    #[schemars(length(min = 1))]
15    pub description: String,
16    /// Model spec for this agent, in `provider:model-id` form. Accepts a
17    /// comma-separated alloy of specs to round-robin across turns.
18    #[schemars(length(min = 1))]
19    pub model: String,
20    /// Optional thinking budget for providers that support extended reasoning.
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub reasoning_effort: Option<ReasoningEffort>,
23    /// Override for the model's context window, in tokens. Defaults to the
24    /// model's advertised window.
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    #[schemars(range(min = 1))]
27    pub context_window: Option<u32>,
28    /// Exposes the agent as a user-selectable mode.
29    #[serde(default)]
30    pub user_invocable: bool,
31    /// Exposes the agent to the `subagents` MCP server so other agents can spawn it.
32    #[serde(default)]
33    pub agent_invocable: bool,
34    /// Per-agent provider overrides, merged over the top-level `providers`.
35    #[serde(default, skip_serializing_if = "ProviderConnectionOverrides::is_empty")]
36    pub providers: ProviderConnectionOverrides,
37    /// Agent-specific prompt sources. A non-empty array replaces the top-level `prompts`.
38    #[serde(default, skip_serializing_if = "Vec::is_empty")]
39    #[schemars(length(min = 1))]
40    pub prompts: Vec<PromptSource>,
41    /// Agent-specific MCP sources. A non-empty array replaces the top-level `mcps`.
42    #[serde(default, skip_serializing_if = "Vec::is_empty")]
43    pub mcps: Vec<McpSourceSpec>,
44    /// Per-agent MCP tool filter (allow/deny lists).
45    #[serde(default, skip_serializing_if = "ToolFilter::is_empty")]
46    pub tools: ToolFilter,
47}
48
49fn require_agent_invocation_surface_schema(schema: &mut schemars::Schema) {
50    schema.insert(
51        "anyOf".to_string(),
52        serde_json::json!([
53            { "required": ["userInvocable"], "properties": { "userInvocable": { "const": true } } },
54            { "required": ["agentInvocable"], "properties": { "agentInvocable": { "const": true } } }
55        ]),
56    );
57}