magi-code 0.63.1

Repository-aware CLI coding agent for terminal work
Documentation
use crate::{
    output::ContextUsageSource,
    tui::{PADDED_INLINE_SEPARATOR, normalize_inline_separators},
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ContextUsageState {
    pub(crate) current_tokens: usize,
    pub(crate) max_tokens: usize,
    pub(crate) reasoning_tokens: Option<usize>,
    pub(crate) source: ContextUsageSource,
    pub(crate) request_sequence: u64,
}

impl Default for ContextUsageState {
    fn default() -> Self {
        Self {
            current_tokens: 0,
            max_tokens: 0,
            reasoning_tokens: None,
            source: ContextUsageSource::FallbackEstimate,
            request_sequence: 0,
        }
    }
}

pub(super) fn merge_context_usage(
    previous: Option<ContextUsageState>,
    incoming: ContextUsageState,
) -> ContextUsageState {
    let Some(previous) = previous else {
        return incoming;
    };

    let mut merged = previous;
    if incoming.request_sequence > previous.request_sequence {
        let accepts_new_sequence = incoming.current_tokens > 0
            || matches!(
                incoming.source,
                ContextUsageSource::ProviderExact | ContextUsageSource::ProviderPartial
            );
        if accepts_new_sequence {
            merged = incoming;
        }
    } else if incoming.request_sequence == previous.request_sequence {
        if incoming.source == ContextUsageSource::ProviderPartial {
            merged.max_tokens = incoming.max_tokens;
            merged.reasoning_tokens = incoming.reasoning_tokens;
        } else if context_usage_source_strength(incoming.source)
            >= context_usage_source_strength(previous.source)
            && !(incoming.current_tokens == 0
                && incoming.source != ContextUsageSource::ProviderExact
                && previous.current_tokens > 0)
        {
            merged = incoming;
        }
    }

    if !matches!(
        incoming.source,
        ContextUsageSource::ProviderExact | ContextUsageSource::ProviderPartial
    ) {
        merged.reasoning_tokens = previous.reasoning_tokens;
    }
    merged
}

fn context_usage_source_strength(source: ContextUsageSource) -> u8 {
    match source {
        ContextUsageSource::ProviderExact => 4,
        ContextUsageSource::LastProviderUsage
        | ContextUsageSource::TokenizerEstimate
        | ContextUsageSource::TokenizerProjection => 3,
        ContextUsageSource::FallbackEstimate | ContextUsageSource::FallbackProjection => 2,
        ContextUsageSource::ProviderPartial => 1,
    }
}

pub(super) fn transcript_tool_row(
    call: &crate::providers::ToolCall,
    summary: &crate::output::ToolDisplaySummary,
) -> String {
    let mut parts = vec![format!(
        "{} {}",
        summary.unicode_mark,
        transcript_tool_label(call, summary)
    )];
    parts.extend(transcript_tool_metadata(summary).into_iter().take(4));
    parts.join(PADDED_INLINE_SEPARATOR)
}

fn transcript_tool_label(
    call: &crate::providers::ToolCall,
    summary: &crate::output::ToolDisplaySummary,
) -> String {
    let tool_name = if call.name.trim().is_empty() {
        summary.tool_name.as_str()
    } else {
        call.name.as_str()
    };

    match tool_name {
        "bash" | "shell" => tool_name.to_string(),
        _ => normalize_inline_separators(&summary.label),
    }
}

pub(super) fn transcript_tool_metadata(summary: &crate::output::ToolDisplaySummary) -> Vec<String> {
    summary
        .metadata
        .iter()
        .filter(|(key, _)| !(key == "tasks" && transcript_label_has_task_count(&summary.label)))
        .filter_map(|(key, value)| transcript_tool_metadata_part(key, value))
        .collect()
}

pub(super) fn transcript_metadata_fields(fields: &[(String, String)]) -> Vec<String> {
    fields
        .iter()
        .filter_map(|(key, value)| transcript_tool_metadata_part(key, value))
        .collect()
}

fn transcript_label_has_task_count(label: &str) -> bool {
    label
        .split([crate::tui::INLINE_SEPARATOR_CHAR, '·'])
        .any(|part| {
            let part = part.trim();
            part.strip_suffix(" tasks")
                .or_else(|| part.strip_suffix(" task"))
                .is_some_and(|count| count.trim().parse::<usize>().is_ok())
        })
}

fn transcript_count_label(value: &str, singular: &str, plural: &str) -> String {
    let unit = if value == "1" { singular } else { plural };
    format!("{value} {unit}")
}

pub(super) fn is_transient_status_error(message: &str) -> bool {
    matches!(message.trim(), "running prompt…" | "running prompt...")
}

pub(super) fn transcript_tool_metadata_part(key: &str, value: &str) -> Option<String> {
    match key {
        "exit_code" => Some(format!("exit {value}")),
        "output_lines" => Some(transcript_count_label(value, "line", "lines")),
        "edit_count" => Some(transcript_count_label(value, "edit", "edits")),
        "matches_returned" => Some(transcript_count_label(value, "match", "matches")),
        "bytes" => Some(format!("{value} bytes")),
        "total_tokens" => Some(format!("{value} tokens")),
        "tasks" => Some(transcript_count_label(value, "task", "tasks")),
        "completed" => Some(format!("{value} completed")),
        "failed" => Some(format!("{value} failed")),
        "identities" => Some(format!("identities {value}")),
        "reference" => Some(format!("reference {value}")),
        "timed_out" if value == "true" => Some("timed out".to_string()),
        _ => None,
    }
}