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