magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use crate::providers::{MessageRole, ProviderConversationItem};
use serde_json::{Value, json};
use std::collections::HashMap;

/// Keep both ends of large output without cutting its enclosing JSON record.
pub(crate) fn excerpt(text: &str, max_bytes: usize) -> String {
    if text.len() <= max_bytes {
        return text.to_string();
    }
    const MARKER: &str = "\n[content omitted]\n";
    if max_bytes < MARKER.len() {
        return String::new();
    }
    let available = max_bytes - MARKER.len();
    let mut head = available / 2;
    while !text.is_char_boundary(head) {
        head -= 1;
    }
    let mut tail = text.len() - (available - available / 2);
    while !text.is_char_boundary(tail) {
        tail += 1;
    }
    format!("{}{MARKER}{}", &text[..head], &text[tail..])
}

/// Select recent, intact evidence records. Tool receipts carry their matching call.
pub(crate) fn conversation_evidence<'a>(
    items: impl IntoIterator<Item = &'a ProviderConversationItem>,
    max_bytes: usize,
) -> Value {
    let items = items.into_iter().collect::<Vec<_>>();
    let calls = items
        .iter()
        .filter_map(|item| match item {
            ProviderConversationItem::ResponseItem(value)
                if value["type"].as_str() == Some("function_call") =>
            {
                Some((value["call_id"].as_str()?, value))
            }
            _ => None,
        })
        .collect::<HashMap<_, _>>();
    let text_limit = (max_bytes / 4).clamp(128, 4096);
    let mut records = Vec::new();
    let mut omitted = 0;
    let mut truncated = 0;
    for item in items.iter().rev() {
        let (record, was_truncated) = match item {
            ProviderConversationItem::Message(message)
                if matches!(message.role, MessageRole::User | MessageRole::Assistant) =>
            {
                (
                    json!({
                        "kind": if message.role == MessageRole::User { "user_request" } else { "assistant_statement" },
                        "text": excerpt(&message.content, text_limit),
                        "truncated": message.content.len() > text_limit,
                    }),
                    message.content.len() > text_limit,
                )
            }
            ProviderConversationItem::ToolResult(result) => {
                let call = calls.get(result.call_id.as_str());
                let arguments = call.map(|value| match &value["arguments"] {
                    Value::String(text) => text.clone(),
                    value => value.to_string(),
                });
                let cut = result.output.len() > text_limit
                    || arguments
                        .as_ref()
                        .is_some_and(|text| text.len() > text_limit);
                (
                    json!({
                        "kind": "tool_receipt",
                        "call_id": result.call_id,
                        "tool": result.tool_name,
                        "arguments": arguments.as_ref().map(|text| excerpt(text, text_limit)),
                        "success": result.success,
                        "output": excerpt(&result.output, text_limit),
                        "truncated": cut,
                    }),
                    cut,
                )
            }
            _ => continue,
        };
        records.push(record);
        let candidate = json!({"records": records, "omitted_records": omitted, "truncated_records": truncated + usize::from(was_truncated)});
        // Leave space for the final omission counts to grow.
        if candidate.to_string().len().saturating_add(64) > max_bytes {
            records.pop();
            omitted += 1;
        } else {
            truncated += usize::from(was_truncated);
        }
    }
    records.reverse();
    json!({"records": records, "omitted_records": omitted, "truncated_records": truncated})
}