Skip to main content

agent_code_lib/services/
output_store.rs

1//! Output persistence for large tool results.
2//!
3//! When a tool produces output larger than the inline threshold,
4//! it's persisted to disk and a reference is returned instead.
5//! This prevents large outputs from bloating the context window.
6
7use std::path::PathBuf;
8
9/// Maximum inline output size (64KB). Larger results are persisted.
10const INLINE_THRESHOLD: usize = 64 * 1024;
11
12/// Persist large output to disk and return a summary reference.
13///
14/// If the content is under the threshold, returns it unchanged.
15/// Otherwise, writes to the output store and returns a truncated
16/// version with a file path reference.
17pub fn persist_if_large(content: &str, _tool_name: &str, tool_use_id: &str) -> String {
18    if content.len() <= INLINE_THRESHOLD {
19        return content.to_string();
20    }
21
22    let store_dir = output_store_dir();
23    let _ = std::fs::create_dir_all(&store_dir);
24
25    let filename = format!("{tool_use_id}.txt");
26    let path = store_dir.join(&filename);
27
28    match std::fs::write(&path, content) {
29        Ok(()) => {
30            let preview = &content[..INLINE_THRESHOLD.min(content.len())];
31            format!(
32                "{preview}\n\n(Output truncated. Full result ({} bytes) saved to {})",
33                content.len(),
34                path.display()
35            )
36        }
37        Err(_) => {
38            // Can't persist — truncate inline.
39            let preview = &content[..INLINE_THRESHOLD.min(content.len())];
40            format!(
41                "{preview}\n\n(Output truncated: {} bytes total)",
42                content.len()
43            )
44        }
45    }
46}
47
48/// Read a persisted output by tool_use_id.
49pub fn read_persisted(tool_use_id: &str) -> Option<String> {
50    let path = output_store_dir().join(format!("{tool_use_id}.txt"));
51    std::fs::read_to_string(path).ok()
52}
53
54/// Clean up old persisted outputs (older than 24 hours).
55pub fn cleanup_old_outputs() {
56    let dir = output_store_dir();
57    if !dir.is_dir() {
58        return;
59    }
60
61    let cutoff = std::time::SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60);
62
63    if let Ok(entries) = std::fs::read_dir(&dir) {
64        for entry in entries.flatten() {
65            if let Ok(meta) = entry.metadata()
66                && let Ok(modified) = meta.modified()
67                && modified < cutoff
68            {
69                let _ = std::fs::remove_file(entry.path());
70            }
71        }
72    }
73}
74
75fn output_store_dir() -> PathBuf {
76    dirs::cache_dir()
77        .unwrap_or_else(|| PathBuf::from("/tmp"))
78        .join("agent-code")
79        .join("tool-results")
80}