Skip to main content

enact_runner/
config.rs

1//! Runner configuration
2//!
3//! Defines `RunnerConfig` — the knobs that control the robust agent loop.
4//! Ported from zeroclaw's iteration limits, compaction thresholds, and retry policies.
5
6use std::time::Duration;
7
8/// Observability configuration for the runner
9#[derive(Debug, Clone)]
10pub struct ObservabilityConfig {
11    /// Enable LLM call tracing (start/end/failed events)
12    pub trace_llm_calls: bool,
13
14    /// Log full prompts (expensive - use for debugging only)
15    pub log_full_prompts: bool,
16
17    /// Log full responses (expensive - use for debugging only)
18    pub log_full_responses: bool,
19
20    /// Track token usage and emit token.usage events
21    pub track_token_usage: bool,
22
23    /// Trace memory access (recall/store events)
24    pub trace_memory_access: bool,
25
26    /// Enable context window snapshots
27    pub enable_context_snapshots: bool,
28
29    /// Enable reasoning trace capture
30    pub capture_reasoning_traces: bool,
31
32    /// Maximum content length for logged prompts/responses
33    pub max_content_length: usize,
34
35    /// Model name for cost calculation (optional)
36    pub model_name: Option<String>,
37
38    /// Cost per 1M input tokens (USD) from provider config
39    /// If set, overrides hardcoded model pricing
40    pub cost_per_1m_input: Option<f64>,
41
42    /// Cost per 1M output tokens (USD) from provider config
43    /// If set, overrides hardcoded model pricing
44    pub cost_per_1m_output: Option<f64>,
45}
46
47impl Default for ObservabilityConfig {
48    fn default() -> Self {
49        Self {
50            trace_llm_calls: true,
51            log_full_prompts: false,
52            log_full_responses: false,
53            track_token_usage: true,
54            trace_memory_access: true,
55            enable_context_snapshots: false,
56            capture_reasoning_traces: false,
57            max_content_length: 1000,
58            model_name: None,
59            cost_per_1m_input: None,
60            cost_per_1m_output: None,
61        }
62    }
63}
64
65/// Configuration for the `AgentRunner` loop.
66///
67/// Controls iteration limits, context compaction, retry behavior,
68/// and checkpointing intervals.
69#[derive(Debug, Clone)]
70pub struct RunnerConfig {
71    /// Maximum number of tool-call iterations before the loop terminates.
72    /// Prevents runaway executions.
73    /// Default: 25 (zeroclaw uses configurable `max_tool_iterations`)
74    pub max_iterations: usize,
75
76    /// Maximum wall-clock duration for the entire run.
77    /// Default: 10 minutes
78    pub max_duration: Duration,
79
80    /// Number of messages in history before auto-compaction triggers.
81    /// When exceeded, older messages are summarized into a single context message.
82    /// Default: 40 messages
83    pub compaction_threshold: usize,
84
85    /// How many messages to keep verbatim after compaction.
86    /// The rest are summarized.
87    /// Default: 10 (keep the 10 most recent messages)
88    pub compaction_keep_recent: usize,
89
90    /// Retry configuration for transient errors.
91    pub retry: RetryConfig,
92
93    /// Checkpoint every N steps. `None` disables periodic checkpointing.
94    /// Default: Some(5)
95    pub checkpoint_interval: Option<usize>,
96
97    /// Whether to emit verbose stream events for each iteration.
98    /// Default: true
99    pub emit_events: bool,
100
101    /// Observability configuration
102    pub observability: ObservabilityConfig,
103}
104
105/// Retry configuration for transient errors.
106#[derive(Debug, Clone)]
107pub struct RetryConfig {
108    /// Maximum number of retries for a single operation.
109    /// Default: 3
110    pub max_retries: u32,
111
112    /// Initial delay before the first retry.
113    /// Default: 1 second
114    pub initial_delay: Duration,
115
116    /// Maximum delay between retries (caps exponential growth).
117    /// Default: 30 seconds
118    pub max_delay: Duration,
119
120    /// Multiplier for exponential backoff.
121    /// Default: 2.0
122    pub backoff_multiplier: f64,
123}
124
125impl Default for RunnerConfig {
126    fn default() -> Self {
127        Self {
128            max_iterations: 25,
129            max_duration: Duration::from_secs(600),
130            compaction_threshold: 40,
131            compaction_keep_recent: 10,
132            retry: RetryConfig::default(),
133            checkpoint_interval: Some(5),
134            emit_events: true,
135            observability: ObservabilityConfig::default(),
136        }
137    }
138}
139
140impl Default for RetryConfig {
141    fn default() -> Self {
142        Self {
143            max_retries: 3,
144            initial_delay: Duration::from_secs(1),
145            max_delay: Duration::from_secs(30),
146            backoff_multiplier: 2.0,
147        }
148    }
149}
150
151impl RunnerConfig {
152    /// Create a config tuned for short, interactive sessions.
153    pub fn interactive() -> Self {
154        Self {
155            max_iterations: 10,
156            max_duration: Duration::from_secs(120),
157            compaction_threshold: 20,
158            compaction_keep_recent: 6,
159            checkpoint_interval: None,
160            emit_events: true,
161            retry: RetryConfig::default(),
162            observability: ObservabilityConfig::default(),
163        }
164    }
165
166    /// Create a config tuned for long-running background agents.
167    pub fn long_running() -> Self {
168        Self {
169            max_iterations: 100,
170            max_duration: Duration::from_secs(3600),
171            compaction_threshold: 60,
172            compaction_keep_recent: 15,
173            checkpoint_interval: Some(10),
174            emit_events: true,
175            retry: RetryConfig {
176                max_retries: 5,
177                ..Default::default()
178            },
179            observability: ObservabilityConfig {
180                trace_llm_calls: true,
181                track_token_usage: true,
182                enable_context_snapshots: true,
183                ..Default::default()
184            },
185        }
186    }
187
188    /// Set the model name for cost calculation
189    pub fn with_model(mut self, model: impl Into<String>) -> Self {
190        self.observability.model_name = Some(model.into());
191        self
192    }
193
194    /// Enable full prompt/response logging (debug mode)
195    pub fn with_debug_logging(mut self) -> Self {
196        self.observability.log_full_prompts = true;
197        self.observability.log_full_responses = true;
198        self
199    }
200}