Skip to main content

walrus_core/agent/
config.rs

1//! Agent configuration.
2//!
3//! [`AgentConfig`] is a serializable struct holding all agent parameters.
4//! Used by [`super::AgentBuilder`] to construct an [`super::Agent`].
5
6use crate::model::ToolChoice;
7use compact_str::CompactString;
8use serde::{Deserialize, Serialize};
9
10/// Default maximum iterations for agent execution.
11const DEFAULT_MAX_ITERATIONS: usize = 16;
12
13/// Default compact threshold in estimated tokens (~100k).
14const DEFAULT_COMPACT_THRESHOLD: usize = 100_000;
15
16/// Serializable agent configuration.
17///
18/// Contains all parameters for an agent: identity, system prompt, model,
19/// iteration limits, heartbeat, and delegation scope. Used both as the
20/// TOML deserialization target and the runtime agent definition.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct AgentConfig {
23    /// Agent identifier. Derived from TOML key, not stored in TOML.
24    #[serde(skip)]
25    pub name: CompactString,
26    /// Human-readable description.
27    #[serde(default)]
28    pub description: CompactString,
29    /// System prompt sent before each LLM request. Loaded from .md file.
30    #[serde(skip)]
31    pub system_prompt: String,
32    /// Model to use from the registry. None = registry's active/default.
33    #[serde(default)]
34    pub model: Option<CompactString>,
35    /// Maximum iterations before stopping.
36    #[serde(default = "default_max_iterations")]
37    pub max_iterations: usize,
38    /// Controls which tool the model calls.
39    #[serde(skip)]
40    pub tool_choice: ToolChoice,
41    /// Whether to enable thinking/reasoning mode.
42    #[serde(default)]
43    pub thinking: bool,
44    /// Heartbeat configuration. Interval 0 (the default) means no heartbeat.
45    #[serde(default)]
46    pub heartbeat: HeartbeatConfig,
47    /// Agents this agent can delegate to via spawn_task. Empty = no delegation.
48    #[serde(default)]
49    pub members: Vec<String>,
50    /// Skill names this agent can access. Empty = all skills (walrus default).
51    #[serde(default)]
52    pub skills: Vec<String>,
53    /// MCP server names this agent can access. Empty = all MCPs (walrus default).
54    #[serde(default)]
55    pub mcps: Vec<String>,
56    /// Computed tool whitelist. Empty = all tools. Not serialized.
57    #[serde(skip)]
58    pub tools: Vec<CompactString>,
59    /// Token count threshold for automatic context compaction.
60    /// When history exceeds this, the agent compacts automatically.
61    /// None = disabled. Defaults to 100_000.
62    #[serde(default = "default_compact_threshold")]
63    pub compact_threshold: Option<usize>,
64}
65
66fn default_max_iterations() -> usize {
67    DEFAULT_MAX_ITERATIONS
68}
69
70fn default_compact_threshold() -> Option<usize> {
71    Some(DEFAULT_COMPACT_THRESHOLD)
72}
73
74impl Default for AgentConfig {
75    fn default() -> Self {
76        Self {
77            name: CompactString::default(),
78            description: CompactString::default(),
79            system_prompt: String::new(),
80            model: None,
81            max_iterations: DEFAULT_MAX_ITERATIONS,
82            tool_choice: ToolChoice::Auto,
83            thinking: false,
84            heartbeat: HeartbeatConfig::default(),
85            members: Vec::new(),
86            skills: Vec::new(),
87            mcps: Vec::new(),
88            tools: Vec::new(),
89            compact_threshold: default_compact_threshold(),
90        }
91    }
92}
93
94impl AgentConfig {
95    /// Create a new config with the given name and defaults for everything else.
96    pub fn new(name: impl Into<CompactString>) -> Self {
97        Self {
98            name: name.into(),
99            ..Default::default()
100        }
101    }
102
103    /// Set the system prompt.
104    pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
105        self.system_prompt = prompt.into();
106        self
107    }
108
109    /// Set the description.
110    pub fn description(mut self, desc: impl Into<CompactString>) -> Self {
111        self.description = desc.into();
112        self
113    }
114
115    /// Set the model to use from the registry.
116    pub fn model(mut self, name: impl Into<CompactString>) -> Self {
117        self.model = Some(name.into());
118        self
119    }
120
121    /// Enable or disable thinking/reasoning mode.
122    pub fn thinking(mut self, enabled: bool) -> Self {
123        self.thinking = enabled;
124        self
125    }
126}
127
128/// Heartbeat timer configuration. Interval 0 = disabled.
129#[derive(Debug, Clone, Serialize, Deserialize, Default)]
130pub struct HeartbeatConfig {
131    /// Interval in minutes (0 = disabled).
132    #[serde(default)]
133    pub interval: u64,
134    /// System prompt for heartbeat-triggered agent runs.
135    #[serde(default)]
136    pub prompt: String,
137}