Skip to main content

crabtalk_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::{AgentId, config::hooks::HooksConfig, 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/// Default max byte length for tool results during compaction.
16const DEFAULT_COMPACT_TOOL_MAX_LEN: usize = 1024;
17
18/// Serializable agent configuration.
19///
20/// Contains all parameters for an agent: identity, system prompt, model,
21/// iteration limits, and delegation scope. Used both as the
22/// TOML deserialization target and the runtime agent definition.
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct AgentConfig {
25    /// Stable ULID identity. A fresh ULID is generated by
26    /// [`AgentConfig::new`] / the protocol create handler when missing.
27    #[serde(default)]
28    pub id: AgentId,
29    /// Agent name. Derived from TOML key, not stored in TOML.
30    #[serde(skip)]
31    pub name: String,
32    /// Human-readable description.
33    #[serde(default)]
34    pub description: String,
35    /// System prompt sent before each LLM request. Loaded from .md file.
36    #[serde(skip)]
37    pub system_prompt: String,
38    /// Model to use from the registry. Required — every agent runs against
39    /// a specific model. Empty string is rejected by the provider registry.
40    #[serde(default)]
41    pub model: String,
42    /// Maximum iterations before stopping.
43    #[serde(default = "default_max_iterations")]
44    pub max_iterations: usize,
45    /// Controls which tool the model calls. Defaults to `Auto`.
46    #[serde(default)]
47    pub tool_choice: ToolChoice,
48    /// Whether to enable thinking/reasoning mode.
49    #[serde(default)]
50    pub thinking: bool,
51    /// Agents this agent can delegate to via spawn_task. Empty = no delegation.
52    #[serde(default)]
53    pub members: Vec<String>,
54    /// Skill names this agent can access. Empty = all skills (crabtalk default).
55    #[serde(default)]
56    pub skills: Vec<String>,
57    /// MCP server names this agent can access. Empty = all MCPs (crabtalk default).
58    #[serde(default)]
59    pub mcps: Vec<String>,
60    /// Computed tool whitelist. Empty = all tools. Not serialized.
61    #[serde(skip)]
62    pub tools: Vec<String>,
63    /// Token count threshold for automatic context compaction.
64    /// When history exceeds this, the agent compacts automatically.
65    /// None = disabled. Defaults to 100_000.
66    #[serde(default = "default_compact_threshold")]
67    pub compact_threshold: Option<usize>,
68    /// Max byte length to keep from tool-role messages when compacting.
69    /// Longer results are truncated before sending to the compaction LLM.
70    #[serde(default = "default_compact_tool_max_len")]
71    pub compact_tool_max_len: usize,
72    /// Hook configuration for this agent (bash deny rules, memory recall
73    /// limit, etc.). Each agent owns its own hook state — there is no
74    /// global override.
75    #[serde(default)]
76    pub hooks: HooksConfig,
77}
78
79fn default_max_iterations() -> usize {
80    DEFAULT_MAX_ITERATIONS
81}
82
83fn default_compact_threshold() -> Option<usize> {
84    Some(DEFAULT_COMPACT_THRESHOLD)
85}
86
87fn default_compact_tool_max_len() -> usize {
88    DEFAULT_COMPACT_TOOL_MAX_LEN
89}
90
91impl Default for AgentConfig {
92    fn default() -> Self {
93        Self {
94            id: AgentId::nil(),
95            name: String::new(),
96            description: String::new(),
97            system_prompt: String::new(),
98            model: String::new(),
99            max_iterations: DEFAULT_MAX_ITERATIONS,
100            tool_choice: ToolChoice::Auto,
101            thinking: false,
102            members: Vec::new(),
103            skills: Vec::new(),
104            mcps: Vec::new(),
105            tools: Vec::new(),
106            compact_threshold: default_compact_threshold(),
107            compact_tool_max_len: DEFAULT_COMPACT_TOOL_MAX_LEN,
108            hooks: HooksConfig::default(),
109        }
110    }
111}
112
113impl AgentConfig {
114    /// Create a new config with the given name, a fresh ULID, and
115    /// defaults for everything else.
116    pub fn new(name: impl Into<String>) -> Self {
117        Self {
118            id: AgentId::new(),
119            name: name.into(),
120            ..Default::default()
121        }
122    }
123
124    /// Set the system prompt.
125    pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
126        self.system_prompt = prompt.into();
127        self
128    }
129
130    /// Set the description.
131    pub fn description(mut self, desc: impl Into<String>) -> Self {
132        self.description = desc.into();
133        self
134    }
135
136    /// Set the model to use from the registry.
137    pub fn model(mut self, name: impl Into<String>) -> Self {
138        self.model = name.into();
139        self
140    }
141
142    /// Enable or disable thinking/reasoning mode.
143    pub fn thinking(mut self, enabled: bool) -> Self {
144        self.thinking = enabled;
145        self
146    }
147}