vtcode_config/
timeouts.rs1use anyhow::{Result, ensure};
2use serde::{Deserialize, Serialize};
3
4#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
5#[derive(Debug, Clone, Deserialize, Serialize)]
6pub struct TimeoutsConfig {
7 #[serde(default = "TimeoutsConfig::default_default_ceiling_seconds")]
9 pub default_ceiling_seconds: u64,
10 #[serde(default = "TimeoutsConfig::default_pty_ceiling_seconds")]
12 pub pty_ceiling_seconds: u64,
13 #[serde(default = "TimeoutsConfig::default_mcp_ceiling_seconds")]
15 pub mcp_ceiling_seconds: u64,
16 #[serde(default = "TimeoutsConfig::default_streaming_ceiling_seconds")]
18 pub streaming_ceiling_seconds: u64,
19 #[serde(default = "TimeoutsConfig::default_warning_threshold_percent")]
21 pub warning_threshold_percent: u8,
22}
23
24impl Default for TimeoutsConfig {
25 fn default() -> Self {
26 Self {
27 default_ceiling_seconds: Self::default_default_ceiling_seconds(),
28 pty_ceiling_seconds: Self::default_pty_ceiling_seconds(),
29 mcp_ceiling_seconds: Self::default_mcp_ceiling_seconds(),
30 streaming_ceiling_seconds: Self::default_streaming_ceiling_seconds(),
31 warning_threshold_percent: Self::default_warning_threshold_percent(),
32 }
33 }
34}
35
36impl TimeoutsConfig {
37 const MIN_CEILING_SECONDS: u64 = 15;
38
39 const fn default_default_ceiling_seconds() -> u64 {
40 180
41 }
42
43 const fn default_pty_ceiling_seconds() -> u64 {
44 300
45 }
46
47 const fn default_mcp_ceiling_seconds() -> u64 {
48 120
49 }
50
51 const fn default_streaming_ceiling_seconds() -> u64 {
52 600
53 }
54
55 const fn default_warning_threshold_percent() -> u8 {
56 80
57 }
58
59 pub fn warning_threshold_fraction(&self) -> f32 {
61 f32::from(self.warning_threshold_percent) / 100.0
62 }
63
64 pub fn ceiling_duration(&self, seconds: u64) -> Option<std::time::Duration> {
66 if seconds == 0 {
67 None
68 } else {
69 Some(std::time::Duration::from_secs(seconds))
70 }
71 }
72
73 pub fn validate(&self) -> Result<()> {
74 ensure!(
75 self.warning_threshold_percent > 0 && self.warning_threshold_percent < 100,
76 "timeouts.warning_threshold_percent must be between 1 and 99",
77 );
78
79 ensure!(
80 self.default_ceiling_seconds == 0
81 || self.default_ceiling_seconds >= Self::MIN_CEILING_SECONDS,
82 "timeouts.default_ceiling_seconds must be at least {} seconds (or 0 to disable)",
83 Self::MIN_CEILING_SECONDS
84 );
85
86 ensure!(
87 self.pty_ceiling_seconds == 0 || self.pty_ceiling_seconds >= Self::MIN_CEILING_SECONDS,
88 "timeouts.pty_ceiling_seconds must be at least {} seconds (or 0 to disable)",
89 Self::MIN_CEILING_SECONDS
90 );
91
92 ensure!(
93 self.mcp_ceiling_seconds == 0 || self.mcp_ceiling_seconds >= Self::MIN_CEILING_SECONDS,
94 "timeouts.mcp_ceiling_seconds must be at least {} seconds (or 0 to disable)",
95 Self::MIN_CEILING_SECONDS
96 );
97
98 ensure!(
99 self.streaming_ceiling_seconds == 0
100 || self.streaming_ceiling_seconds >= Self::MIN_CEILING_SECONDS,
101 "timeouts.streaming_ceiling_seconds must be at least {} seconds (or 0 to disable)",
102 Self::MIN_CEILING_SECONDS
103 );
104
105 Ok(())
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::TimeoutsConfig;
112
113 #[test]
114 fn default_values_are_safe() {
115 let config = TimeoutsConfig::default();
116 assert_eq!(config.default_ceiling_seconds, 180);
117 assert_eq!(config.pty_ceiling_seconds, 300);
118 assert_eq!(config.mcp_ceiling_seconds, 120);
119 assert_eq!(config.streaming_ceiling_seconds, 600);
120 assert_eq!(config.warning_threshold_percent, 80);
121 assert!(config.validate().is_ok());
122 }
123
124 #[test]
125 fn zero_ceiling_disables_limit() {
126 let mut config = TimeoutsConfig::default();
127 config.default_ceiling_seconds = 0;
128 assert!(config.validate().is_ok());
129 assert!(
130 config
131 .ceiling_duration(config.default_ceiling_seconds)
132 .is_none()
133 );
134 }
135
136 #[test]
137 fn warning_threshold_bounds_are_enforced() {
138 let mut config = TimeoutsConfig::default();
139 config.warning_threshold_percent = 0;
140 assert!(config.validate().is_err());
141
142 config.warning_threshold_percent = 100;
143 assert!(config.validate().is_err());
144 }
145}