magi-code 0.102.2

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

/// Selection bounds only. Wire consumers must also measure escaped serialized bytes.
pub(super) fn bound_card(card: &mut ToolCard) {
    bound_string(
        &mut card.title,
        256,
        OmissionSubject::Title,
        &mut card.omissions,
    );
    for text in &mut card.metadata {
        bound_string(text, 512, OmissionSubject::Metadata, &mut card.omissions);
    }
    if card.metadata.len() > 8 {
        record_omission(
            &mut card.omissions,
            OmissionSubject::Metadata,
            OmissionReason::SizeLimit,
            Some(card.metadata.len() - 8),
        );
        card.metadata.truncate(8);
    }
    if let Some(summary) = &mut card.public_protection.summary {
        bound_string(summary, 256, OmissionSubject::Metadata, &mut card.omissions);
    }
    bound_string(
        &mut card.body,
        4096,
        OmissionSubject::Text,
        &mut card.omissions,
    );
    for block in &mut card.blocks {
        match block {
            ToolBlock::Text { lines } => {
                if lines.len() > 32 {
                    record_omission(
                        &mut card.omissions,
                        OmissionSubject::Rows,
                        OmissionReason::SizeLimit,
                        Some(lines.len() - 32),
                    );
                    lines.truncate(32);
                }
                for line in lines {
                    bound_line(line, &mut card.omissions);
                }
            }
            ToolBlock::Resources { rows } => {
                if rows.len() > 32 {
                    record_omission(
                        &mut card.omissions,
                        OmissionSubject::Rows,
                        OmissionReason::SizeLimit,
                        Some(rows.len() - 32),
                    );
                    rows.truncate(32);
                }
                for row in rows {
                    bound_string(
                        &mut row.location,
                        512,
                        OmissionSubject::Metadata,
                        &mut card.omissions,
                    );
                    bound_line(&mut row.line, &mut card.omissions);
                }
            }
            ToolBlock::Diff { lines, .. } => {
                for line in lines {
                    bound_line(line, &mut card.omissions);
                }
            }
            ToolBlock::Command {
                command,
                working_directory,
                ..
            } => {
                if let Some(command) = command {
                    bound_string(command, 4096, OmissionSubject::Text, &mut card.omissions);
                }
                if let Some(directory) = working_directory {
                    bound_string(
                        directory,
                        512,
                        OmissionSubject::Metadata,
                        &mut card.omissions,
                    );
                }
            }
            ToolBlock::Results { rows } => {
                for row in rows {
                    bound_string(
                        &mut row.label,
                        1024,
                        OmissionSubject::Text,
                        &mut card.omissions,
                    );
                    if let Some(value) = &mut row.value {
                        bound_string(value, 1024, OmissionSubject::Text, &mut card.omissions);
                        if value.contains("<redacted>") {
                            record_omission(
                                &mut card.omissions,
                                OmissionSubject::Text,
                                OmissionReason::Redacted,
                                None,
                            );
                        }
                    } else {
                        record_omission(
                            &mut card.omissions,
                            OmissionSubject::Rows,
                            OmissionReason::MissingSource,
                            None,
                        );
                    }
                }
            }
            ToolBlock::Progress { label, .. } => {
                bound_string(label, 1024, OmissionSubject::Text, &mut card.omissions)
            }
        }
    }
    if card.blocks.len() > 16 {
        record_omission(
            &mut card.omissions,
            OmissionSubject::Blocks,
            OmissionReason::SizeLimit,
            Some(card.blocks.len() - 16),
        );
        card.blocks.truncate(16);
    }
    for row in &mut card.activity_rows {
        bound_string(
            &mut row.label,
            256,
            OmissionSubject::Children,
            &mut card.omissions,
        );
    }
}

fn bound_line(line: &mut TextLine, omissions: &mut Vec<DisplayOmission>) {
    let source = line.plain_text();
    let mut text = source.clone();
    bound_string(&mut text, 1024, OmissionSubject::Text, omissions);
    if text != source {
        let meaning = line
            .spans
            .first()
            .map(|span| span.meaning)
            .filter(|meaning| {
                matches!(
                    meaning,
                    TextMeaning::Added | TextMeaning::Removed | TextMeaning::Metadata
                )
            })
            .unwrap_or(TextMeaning::Plain);
        *line = TextLine::from_span(text, meaning);
    }
}

fn bound_string(
    text: &mut String,
    max_bytes: usize,
    subject: OmissionSubject,
    omissions: &mut Vec<DisplayOmission>,
) {
    let safe = sanitize_display_text(text);
    if safe != *text {
        record_omission(omissions, subject.clone(), OmissionReason::Redacted, None);
        *text = safe;
    }
    if text.len() <= max_bytes {
        return;
    }
    const MARKER: &str = "… [truncated]";
    let mut boundary = max_bytes.saturating_sub(MARKER.len());
    while !text.is_char_boundary(boundary) {
        boundary -= 1;
    }
    let omitted = text[boundary..].chars().count();
    text.truncate(boundary);
    text.push_str(MARKER);
    record_omission(omissions, subject, OmissionReason::SizeLimit, Some(omitted));
}

pub(super) fn record_omission(
    omissions: &mut Vec<DisplayOmission>,
    subject: OmissionSubject,
    reason: OmissionReason,
    count: Option<usize>,
) {
    if let Some(existing) = omissions
        .iter_mut()
        .find(|omission| omission.subject == subject)
    {
        // Multiple views may describe the same excluded source; do not double count.
        existing.omitted_count = None;
        if reason < existing.reason {
            existing.reason = reason;
        }
    } else {
        omissions.push(DisplayOmission {
            subject,
            reason,
            omitted_count: count,
        });
    }
}