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