codetether-agent 4.7.0-a-002.4

A2A-native AI coding agent for the CodeTether ecosystem
Documentation
//! Bounded payloads for live UI/session events.

use crate::util::truncate_bytes_safe;

const TOOL_OUTPUT_EVENT_MAX_BYTES: usize = 8 * 1024;
const TOOL_ARGUMENTS_EVENT_MAX_BYTES: usize = 4 * 1024;
const TRUNCATED_OUTPUT_MARKER: &str =
    "\n\n[truncated for live TUI event; full output remains in session history]";
const TRUNCATED_ARGUMENTS_MARKER: &str = "\n\n[truncated for live TUI event: tool arguments]";

pub(crate) fn bounded_tool_output(output: &str) -> String {
    bounded(output, TOOL_OUTPUT_EVENT_MAX_BYTES, TRUNCATED_OUTPUT_MARKER)
}

pub(crate) fn bounded_tool_arguments(arguments: &str) -> String {
    bounded(
        arguments,
        TOOL_ARGUMENTS_EVENT_MAX_BYTES,
        TRUNCATED_ARGUMENTS_MARKER,
    )
}

fn bounded(input: &str, max_bytes: usize, marker: &str) -> String {
    if input.len() <= max_bytes {
        return input.to_string();
    }
    let content_bytes = max_bytes.saturating_sub(marker.len());
    let mut bounded = truncate_bytes_safe(input, content_bytes).to_string();
    bounded.push_str(marker);
    bounded
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn keeps_small_output() {
        assert_eq!(bounded_tool_output("ok"), "ok");
    }

    #[test]
    fn truncates_large_output() {
        let text = "a".repeat(TOOL_OUTPUT_EVENT_MAX_BYTES + 1);
        let bounded = bounded_tool_output(&text);
        assert!(bounded.len() < text.len());
        assert!(bounded.contains("truncated for live TUI event"));
    }

    #[test]
    fn truncates_large_arguments() {
        let text = "b".repeat(TOOL_ARGUMENTS_EVENT_MAX_BYTES + 1);
        let bounded = bounded_tool_arguments(&text);
        assert!(bounded.len() < text.len());
        assert!(bounded.contains("tool arguments"));
    }
}