Skip to main content

vtcode_config/constants/
output_limits.rs

1//! Output truncation limits inspired by OpenAI Codex multi-tier truncation.
2//! Prevents OOM with three independent size limits.
3//! Reference: https://openai.com/index/unrolling-the-codex-agent-loop/
4
5/// Maximum size for single agent message payloads (bytes) - 10 MB.
6pub const MAX_AGENT_MESSAGES_SIZE: usize = 10 * 1024 * 1024;
7
8/// Maximum size for entire message history payloads (bytes) - 50 MB.
9pub const MAX_ALL_MESSAGES_SIZE: usize = 50 * 1024 * 1024;
10
11/// Maximum size per line (bytes) - 1 MB.
12/// Prevents OOM on malformed output with very long lines.
13pub const MAX_LINE_LENGTH: usize = 1024 * 1024;
14
15/// Default message count limit for history.
16pub const DEFAULT_MESSAGE_LIMIT: usize = 10_000;
17
18/// Maximum message count limit.
19pub const MAX_MESSAGE_LIMIT: usize = 50_000;
20
21/// Truncation marker appended when content is cut off.
22pub const TRUNCATION_MARKER: &str = "\n[... content truncated due to size limit ...]";
23
24/// Collect content with lazy truncation (Codex pattern).
25/// Marks truncated but continues draining to prevent pipe blocking.
26///
27/// # Arguments
28/// * `output` - Accumulated output buffer
29/// * `new_content` - New content to append
30/// * `max_size` - Maximum allowed size
31/// * `truncated` - Mutable flag tracking truncation state
32///
33/// # Returns
34/// `true` if content was appended, `false` if truncated
35#[inline]
36pub fn collect_with_truncation(
37    output: &mut String,
38    new_content: &str,
39    max_size: usize,
40    truncated: &mut bool,
41) -> bool {
42    let new_size = output.len() + new_content.len();
43
44    if new_size > max_size {
45        if !*truncated {
46            output.push_str(TRUNCATION_MARKER);
47            *truncated = true;
48        }
49        return false;
50    }
51
52    output.push_str(new_content);
53    true
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_collect_within_limit() {
62        let mut output = String::new();
63        let mut truncated = false;
64
65        assert!(collect_with_truncation(
66            &mut output,
67            "hello",
68            100,
69            &mut truncated
70        ));
71        assert_eq!(output, "hello");
72        assert!(!truncated);
73    }
74
75    #[test]
76    fn test_collect_at_limit_triggers_truncation() {
77        let mut output = String::from("hello");
78        let mut truncated = false;
79
80        assert!(!collect_with_truncation(
81            &mut output,
82            " world that exceeds",
83            10,
84            &mut truncated
85        ));
86        assert!(output.contains(TRUNCATION_MARKER));
87        assert!(truncated);
88    }
89
90    #[test]
91    fn test_truncation_marker_appended_only_once() {
92        let mut output = String::new();
93        let mut truncated = false;
94
95        collect_with_truncation(&mut output, "first", 8, &mut truncated);
96        let len_after_first = output.len();
97
98        collect_with_truncation(&mut output, "second", 8, &mut truncated);
99        assert_eq!(output.len(), len_after_first);
100        assert!(truncated);
101    }
102}