Skip to main content

oxicode_agent/tools/browse/
config.rs

1//! Configuration for browser tools behavior.
2//!
3//! Centralizes all tunable parameters so no values are hardcoded.
4//! Default values are suitable for most use cases; override via
5//! `settings.toml` or programmatically.
6
7use serde::{Deserialize, Serialize};
8
9/// Configuration for browser tools behavior.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct BrowseConfig {
12    /// Default `wait_for` timeout in milliseconds.
13    #[serde(default = "default_wait_timeout_ms")]
14    pub default_wait_timeout_ms: u64,
15
16    /// Default page load timeout in seconds.
17    #[serde(default = "default_page_timeout_secs")]
18    pub page_timeout_secs: u64,
19
20    /// Screenshot width in pixels.
21    #[serde(default = "default_screenshot_width")]
22    pub screenshot_width: u32,
23
24    /// Maximum script steps per execution.
25    #[serde(default = "default_max_script_steps")]
26    pub max_script_steps: usize,
27
28    /// Render cache TTL in seconds (0 = disabled).
29    #[serde(default = "default_cache_ttl_secs")]
30    pub cache_ttl_secs: u64,
31
32    /// Maximum render cache entries.
33    #[serde(default = "default_cache_max_entries")]
34    pub cache_max_entries: usize,
35
36    /// Maximum concurrent tabs.
37    #[serde(default = "default_max_concurrent_tabs")]
38    pub max_concurrent_tabs: usize,
39
40    /// Maximum output size in bytes (truncation threshold).
41    #[serde(default = "default_max_output_bytes")]
42    pub max_output_bytes: usize,
43
44    /// Maximum idle time (seconds) before a browse session auto-closes.
45    /// 0 = no timeout (not recommended).
46    #[serde(default = "default_session_idle_timeout_secs")]
47    pub session_idle_timeout_secs: u64,
48
49    /// Custom User-Agent string. `None` uses the browser default.
50    #[serde(default)]
51    pub user_agent: Option<String>,
52
53    /// Whether to respect robots.txt. Defaults to `true`.
54    #[serde(default = "default_obey_robots")]
55    pub obey_robots: bool,
56
57    /// JavaScript evaluation timeout in milliseconds.
58    #[serde(default = "default_js_timeout_ms")]
59    pub js_timeout_ms: u64,
60}
61
62impl Default for BrowseConfig {
63    fn default() -> Self {
64        Self {
65            default_wait_timeout_ms: default_wait_timeout_ms(),
66            page_timeout_secs: default_page_timeout_secs(),
67            screenshot_width: default_screenshot_width(),
68            max_script_steps: default_max_script_steps(),
69            cache_ttl_secs: default_cache_ttl_secs(),
70            cache_max_entries: default_cache_max_entries(),
71            max_concurrent_tabs: default_max_concurrent_tabs(),
72            max_output_bytes: default_max_output_bytes(),
73            session_idle_timeout_secs: default_session_idle_timeout_secs(),
74            user_agent: None,
75            obey_robots: default_obey_robots(),
76            js_timeout_ms: default_js_timeout_ms(),
77        }
78    }
79}
80
81// ── Default value functions (for serde defaults) ─────────────────
82
83fn default_wait_timeout_ms() -> u64 {
84    10_000
85}
86fn default_page_timeout_secs() -> u64 {
87    30
88}
89fn default_screenshot_width() -> u32 {
90    800
91}
92fn default_max_script_steps() -> usize {
93    100
94}
95fn default_cache_ttl_secs() -> u64 {
96    300
97}
98fn default_cache_max_entries() -> usize {
99    50
100}
101fn default_max_concurrent_tabs() -> usize {
102    4
103}
104fn default_max_output_bytes() -> usize {
105    512_000
106}
107fn default_session_idle_timeout_secs() -> u64 {
108    300 // 5 minutes
109}
110fn default_obey_robots() -> bool {
111    true
112}
113fn default_js_timeout_ms() -> u64 {
114    10_000
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_default_config() {
123        let config = BrowseConfig::default();
124        assert_eq!(config.default_wait_timeout_ms, 10_000);
125        assert_eq!(config.page_timeout_secs, 30);
126        assert_eq!(config.screenshot_width, 800);
127        assert_eq!(config.max_script_steps, 100);
128        assert_eq!(config.cache_ttl_secs, 300);
129        assert_eq!(config.cache_max_entries, 50);
130        assert_eq!(config.max_concurrent_tabs, 4);
131        assert_eq!(config.max_output_bytes, 512_000);
132        assert_eq!(config.session_idle_timeout_secs, 300);
133        assert!(config.user_agent.is_none());
134        assert!(config.obey_robots);
135        assert_eq!(config.js_timeout_ms, 10_000);
136    }
137
138    #[test]
139    fn test_config_serde_roundtrip() {
140        let config = BrowseConfig::default();
141        let json = serde_json::to_string(&config).unwrap();
142        let restored: BrowseConfig = serde_json::from_str(&json).unwrap();
143        assert_eq!(
144            restored.default_wait_timeout_ms,
145            config.default_wait_timeout_ms
146        );
147        assert_eq!(restored.max_concurrent_tabs, config.max_concurrent_tabs);
148    }
149
150    #[test]
151    fn test_config_custom_values() {
152        let config = BrowseConfig {
153            default_wait_timeout_ms: 30_000,
154            max_concurrent_tabs: 8,
155            cache_ttl_secs: 0, // disabled
156            session_idle_timeout_secs: 600,
157            ..Default::default()
158        };
159        assert_eq!(config.default_wait_timeout_ms, 30_000);
160        assert_eq!(config.max_concurrent_tabs, 8);
161        assert_eq!(config.cache_ttl_secs, 0);
162        assert_eq!(config.session_idle_timeout_secs, 600);
163    }
164}