magi-code 0.77.1

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

fn subagent_card_total_from_fields(node: &crate::tui::activity::ActivityNode) -> Option<usize> {
    node.metadata.fields.iter().find_map(|(key, value)| {
        (key == "tasks")
            .then(|| value.trim().parse::<usize>().ok())
            .flatten()
    })
}

pub(super) fn subagent_children(
    state: &MissionControlState,
    activity_id: Option<&ActivityId>,
    entry_index: usize,
) -> Vec<TranscriptCardChild> {
    let from_activity = activity_id
        .and_then(|id| state.children.get(id))
        .map(|children| {
            children
                .iter()
                .filter_map(|child_id| {
                    let node = state.nodes.get(child_id)?;
                    let label = normalize_inline_separators(&node.metadata.label);
                    let task_id = node
                        .transcript_subagent
                        .as_ref()
                        .map(|summary| summary.task_id.clone())
                        .unwrap_or_default();
                    let intent = node
                        .transcript_subagent
                        .as_ref()
                        .map(|summary| summary.intent.clone())
                        .unwrap_or_else(|| label.clone());
                    let identity = identity_from_fields(&node.metadata.fields);
                    let agent = agent_from_fields(&node.metadata.fields);
                    let model_line = model_line_from_fields(&node.metadata.fields);
                    let status = subagent_card_status_from_activity(node.status);
                    let body = if status == TranscriptCardStatus::Failed {
                        first_non_empty_line(&node.preview).unwrap_or_default()
                    } else {
                        String::new()
                    };
                    let live_tail = task_live_tail(state, child_id);
                    Some(TranscriptCardChild {
                        label,
                        status,
                        metadata: Vec::new(),
                        body,
                        task_id,
                        intent,
                        identity,
                        agent,
                        model_line,
                        usage: node.usage,
                        last_known_cache_percent: node.last_known_cache_percent,
                        last_known_latest_cache_percent: node.last_known_latest_cache_percent,
                        live_tail,
                        activity_id: Some(child_id.clone()),
                    })
                })
                .collect::<Vec<_>>()
        })
        .unwrap_or_default();
    if !from_activity.is_empty() {
        return from_activity;
    }

    state
        .transcript
        .get(entry_index)
        .and_then(TranscriptEntry::tool_data)
        .map(|data| {
            data.children
                .iter()
                .map(|child| TranscriptCardChild {
                    label: child.label.clone(),
                    status: child
                        .status
                        .map(subagent_card_status_from_activity)
                        .unwrap_or(TranscriptCardStatus::None),
                    metadata: Vec::new(),
                    body: String::new(),
                    task_id: child.task_id.clone(),
                    intent: child.intent.clone(),
                    identity: None,
                    agent: None,
                    model_line: None,
                    usage: None,
                    last_known_cache_percent: None,
                    last_known_latest_cache_percent: None,
                    live_tail: Vec::new(),
                    activity_id: None,
                })
                .collect()
        })
        .unwrap_or_default()
}

fn metadata_field_from_fields(fields: &[(String, String)], key: &str) -> Option<String> {
    fields.iter().find_map(|(field, value)| {
        if field != key {
            return None;
        }
        let value = value.trim();
        (!value.is_empty()).then(|| sanitized_single_line(value))
    })
}

fn identity_from_fields(fields: &[(String, String)]) -> Option<String> {
    metadata_field_from_fields(fields, "identity")
}

fn agent_from_fields(fields: &[(String, String)]) -> Option<String> {
    metadata_field_from_fields(fields, "agent")
}

fn model_line_from_fields(fields: &[(String, String)]) -> Option<String> {
    let provider = metadata_field_from_fields(fields, "provider")?;
    let model = metadata_field_from_fields(fields, "model")?;
    let reasoning =
        metadata_field_from_fields(fields, "reasoning").unwrap_or_else(|| "default".to_string());
    Some(format!("{provider}/{model}({reasoning})"))
}

fn task_live_tail(state: &MissionControlState, task_id: &ActivityId) -> Vec<String> {
    let mut events = state
        .nodes
        .values()
        .filter(|node| node.id == *task_id || activity_is_descendant_of(state, &node.id, task_id))
        .collect::<Vec<_>>();
    events.sort_by(|left, right| {
        right
            .own_update_sequence
            .cmp(&left.own_update_sequence)
            .then_with(|| right.id.as_str().cmp(left.id.as_str()))
    });

    let mut lines = Vec::new();
    for node in events {
        for line in subagent_card_activity_lines(node) {
            lines.push(line);
            if lines.len() == state.subagent_card_rows() {
                lines.reverse();
                return lines;
            }
        }
    }
    lines.reverse();
    lines
}

fn activity_is_descendant_of(
    state: &MissionControlState,
    id: &ActivityId,
    ancestor: &ActivityId,
) -> bool {
    let mut parent = state.nodes.get(id).and_then(|node| node.parent_id.as_ref());
    let mut remaining = state.nodes.len();
    while let Some(parent_id) = parent {
        if parent_id == ancestor {
            return true;
        }
        if remaining == 0 {
            break;
        }
        remaining -= 1;
        parent = state
            .nodes
            .get(parent_id)
            .and_then(|node| node.parent_id.as_ref());
    }
    false
}

fn subagent_card_activity_lines(node: &crate::tui::activity::ActivityNode) -> Vec<String> {
    if let Some(summary) = &node.transcript_reasoning {
        return subagent_card_reasoning_activity_lines(node, summary);
    }
    match node.kind {
        crate::output::ActivityKind::SubagentTask if node.preview.trim().is_empty() => Vec::new(),
        crate::output::ActivityKind::Tool => subagent_card_tool_activity_lines(node),
        crate::output::ActivityKind::Assistant => {
            let source = if node.preview.trim().is_empty() {
                &node.metadata.label
            } else {
                &node.preview
            };
            subagent_card_activity_source_lines(source, "agent")
        }
        crate::output::ActivityKind::SubagentTask => {
            subagent_card_activity_source_lines(&node.preview, "agent")
        }
        crate::output::ActivityKind::Diagnostic => subagent_card_activity_source_lines(
            if node.preview.trim().is_empty() {
                &node.metadata.label
            } else {
                &node.preview
            },
            "diagnostic",
        ),
        crate::output::ActivityKind::Hook => {
            subagent_card_activity_source_lines(&node.metadata.label, "hook")
        }
        crate::output::ActivityKind::ProviderContextInjection => {
            subagent_card_activity_source_lines(&node.metadata.label, "provider context injection")
        }
        crate::output::ActivityKind::SubagentBatch => {
            subagent_card_activity_source_lines(&node.metadata.label, "subagent batch")
        }
        crate::output::ActivityKind::Compaction => {
            subagent_card_activity_source_lines(&node.metadata.label, "compaction")
        }
    }
}

fn subagent_card_reasoning_activity_lines(
    node: &crate::tui::activity::ActivityNode,
    summary: &crate::tui::activity::ActivityReasoningSummary,
) -> Vec<String> {
    let label = subagent_card_sanitized_line(&node.metadata.label)
        .unwrap_or_else(|| "reasoning".to_string());
    let latest = summary
        .latest
        .as_deref()
        .and_then(subagent_card_sanitized_line);
    let count = summary.count;

    match (count, latest) {
        (Some(0 | 1), Some(latest)) => vec![format!("reasoning • {latest}")],
        (Some(count), Some(latest)) => {
            vec![format!("reasoning summaries ×{count}{latest}")]
        }
        (_, Some(latest)) => vec![format!("reasoning • {latest}")],
        _ => vec![label],
    }
}

fn subagent_card_tool_activity_lines(node: &crate::tui::activity::ActivityNode) -> Vec<String> {
    if node.tool_detail.is_none() && !node.preview.trim().is_empty() {
        return subagent_card_activity_source_lines(&node.preview, "tool");
    }
    let status = match node.status {
        ActivityStatus::Queued => "",
        ActivityStatus::Running | ActivityStatus::Writing => "",
        ActivityStatus::Success => "",
        ActivityStatus::Failed => "",
        ActivityStatus::Canceled => "",
    };
    let (name, label) = node.tool_detail.as_ref().map_or_else(
        || ("tool".to_string(), node.metadata.label.clone()),
        |detail| (detail.tool_name.to_string(), detail.label.to_string()),
    );
    let Some(name) = subagent_card_sanitized_line(&name) else {
        return Vec::new();
    };
    let label = subagent_card_sanitized_line(&label)
        .filter(|label| !label.eq_ignore_ascii_case(&name))
        .map(|label| strip_tool_prefix(&label, &name).to_string());
    let summary = label.map_or_else(|| name.clone(), |label| format!("{name}{label}"));
    vec![format!("{summary}{status}")]
}

fn subagent_card_activity_source_lines(source: &str, prefix: &str) -> Vec<String> {
    source
        .lines()
        .rev()
        .filter_map(subagent_card_sanitized_line)
        .map(|line| {
            if line.eq_ignore_ascii_case(prefix) {
                prefix.to_string()
            } else {
                let line = strip_tool_prefix(&line, prefix);
                format!("{prefix}{line}")
            }
        })
        .collect()
}

fn strip_tool_prefix<'a>(label: &'a str, tool: &str) -> &'a str {
    let Some(rest) = label.get(tool.len()..) else {
        return label;
    };
    if label[..tool.len()].eq_ignore_ascii_case(tool)
        && rest
            .chars()
            .next()
            .is_some_and(|ch| ch.is_whitespace() || ch == '' || ch == '·')
    {
        rest.trim_start_matches([' ', '\t', '', '·'])
    } else {
        label
    }
}

pub(super) fn subagent_card_summary_from_children(
    state: &MissionControlState,
    batch_id: Option<&ActivityId>,
    children: &[TranscriptCardChild],
    node: Option<&crate::tui::activity::ActivityNode>,
) -> SubagentCardSummary {
    let (done, failed, running) = children.iter().fold(
        (0usize, 0usize, 0usize),
        |(done, failed, running), child| match child.status {
            TranscriptCardStatus::Success => (done + 1, failed, running),
            TranscriptCardStatus::Failed => (done, failed + 1, running),
            TranscriptCardStatus::Running => (done, failed, running + 1),
            TranscriptCardStatus::Canceled => (done + 1, failed, running),
            TranscriptCardStatus::None => (done, failed, running),
        },
    );
    let total = children
        .len()
        .max(node.and_then(subagent_card_total_from_fields).unwrap_or(0));
    let selection = batch_id.and_then(|id| state.subagent_card_selection.get(id));
    let selected = batch_id.and_then(|batch_id| {
        let pinned = selection
            .and_then(|selection| selection.task_id.as_ref())
            .and_then(|task_id| {
                children
                    .iter()
                    .find(|child| child.activity_id.as_ref() == Some(task_id))
            });
        pinned.or_else(|| children.first()).and_then(|child| {
            child
                .activity_id
                .clone()
                .or_else(|| (!child.task_id.is_empty()).then(|| batch_id.child(&child.task_id)))
        })
    });

    SubagentCardSummary {
        total: total.max(done + failed + running),
        done,
        failed,
        running,
        selected_task_id: selected
            .as_ref()
            .map(|id| id.as_str().rsplit('/').next().unwrap_or("").to_string()),
        batch_id: batch_id.cloned(),
    }
}

fn subagent_card_status_from_activity(status: ActivityStatus) -> TranscriptCardStatus {
    match status {
        ActivityStatus::Queued => TranscriptCardStatus::None,
        status => transcript_status_from_activity(status),
    }
}

fn subagent_card_sanitized_line(source: &str) -> Option<String> {
    let sanitized = normalize_inline_separators(&transcript::sanitize_preview(
        &redact_sensitive_text(source.trim()),
    ));
    (!sanitized.is_empty())
        .then(|| truncate_preview_line(&sanitized, PREVIEW_LINE_MAX_DISPLAY_WIDTH))
}