magi-code 0.103.0

Repository-aware CLI coding agent for terminal work
Documentation
//! Immutable saved-card selection from exact chronological chat attachments.
use super::{bound_card, initial_card, omit, project_detail};
use crate::{
    output::ActivityStatus,
    sessions::{
        SessionEvent, SessionEventKind,
        chat::{self, ChatMessage},
    },
};
use serde_json::{Value, json};
use std::collections::HashMap;

fn subagent_tasks(event: &SessionEvent) -> Option<crate::subagents::SubagentsArgs> {
    if event.payload["name"] != "subagents" {
        return None;
    }
    crate::subagents::SubagentsArgs::from_recorded_call(event.payload["arguments"].clone())
}

pub(in crate::local_agent) fn expand_tasks(
    events: &[SessionEvent],
    messages: Vec<ChatMessage>,
) -> Result<Vec<ChatMessage>, &'static str> {
    let mut expanded = Vec::with_capacity(messages.len());
    let mut text_bytes: usize = messages.iter().map(|message| message.text.len()).sum();
    for message in messages {
        let source = message
            .activity
            .as_ref()
            .filter(|source| source.task_index.is_none());
        let tasks = source
            .and_then(|source| source.call)
            .and_then(|index| subagent_tasks(&events[index]));
        let source = source.cloned();
        if expanded.len() == chat::CHAT_MAX_ENTRIES {
            return Err("history_limit");
        }
        expanded.push(message);
        if let (Some(args), Some(source)) = (tasks, source) {
            for (index, task) in args.tasks.iter().enumerate() {
                let mut activity = source.clone();
                activity.task_index = Some(index);
                let text = crate::output::sanitize_display_text(&task.intent);
                text_bytes = text_bytes.saturating_add(text.len());
                if text_bytes > chat::CHAT_MAX_BYTES {
                    return Err("history_limit");
                }
                expanded.push(ChatMessage {
                    kind: "activity",
                    text,
                    partial: false,
                    activity: Some(activity),
                });
                if expanded.len() > chat::CHAT_MAX_ENTRIES {
                    return Err("history_limit");
                }
            }
        }
    }
    Ok(expanded)
}

pub(in crate::local_agent) fn project(
    events: &[SessionEvent],
    messages: &[ChatMessage],
    ids: &[String],
    budget: usize,
) -> Result<Vec<Option<Value>>, &'static str> {
    let previews = crate::sessions::tool_display::RecordedPreviews::new(events);
    let assessments = crate::protection::prompt_injection::RecordedAssessments::new(events);
    let execution_summaries = crate::code_mode::audit::recorded_summaries(events);
    let mut calls = HashMap::<(usize, &str), Option<usize>>::new();
    let mut by_event = HashMap::new();
    for (index, message) in messages.iter().enumerate() {
        if let Some(source) = &message.activity
            && source.task_index.is_none()
            && let Some(call_index) = source.call
            && let Some(id) = events[call_index].payload["id"].as_str()
        {
            by_event.insert(call_index, index);
            if events[call_index].kind() == Some(SessionEventKind::ToolCall)
                && events[call_index].payload["name"] == crate::code_mode::TOOL_NAME
            {
                calls
                    .entry((source.turn, id))
                    .and_modify(|value| *value = None)
                    .or_insert(Some(index));
            }
        }
    }
    let mut cards = Vec::with_capacity(messages.len());
    let mut retained_bytes = 0;
    let mut parents = vec![None; messages.len()];
    for (index, message) in messages.iter().enumerate() {
        let Some(source) = &message.activity else {
            cards.push(None);
            continue;
        };
        let call = source
            .call
            .and_then(|index| chat::tool_call(&events[index]))
            .or_else(|| {
                source.result.and_then(|index| {
                    chat::tool_result(&events[index], &HashMap::new())
                        .ok()
                        .map(|(call, _)| call)
                })
            });
        let mut card = initial_card(&ids[index], source.task_index.is_some());
        card["origin"] = json!("saved");
        if let Some(task_index) = source.task_index {
            card["omissions"] = json!([]);
            let args = source.call.and_then(|index| subagent_tasks(&events[index]));
            let task = args.as_ref().and_then(|args| args.tasks.get(task_index));
            let report = source.result.and_then(|index| {
                crate::output::tool_card::TaskBatchTelemetry::parse_for_tasks(
                    &events[index].payload["result"]["metadata"]["subagent_telemetry"],
                    &events[source.call?].payload["arguments"],
                )
            });
            let recorded_task = report
                .as_ref()
                .and_then(|report| report.tasks.get(task_index));
            if let Some(task) = task {
                card["title"] = json!(crate::output::sanitize_display_text(&task.intent));
            }
            card["task_id"] = json!(ids[index]);
            parents[index] = source.call.and_then(|call| by_event.get(&call).copied());
            let mut legacy = crate::output::tool_card::TaskTelemetry::new(task_index);
            if let Some(task) = task {
                legacy.set_intent(&task.intent);
                let mut metadata = crate::output::ActivityMetadata::new("");
                if let Some(profile) = &task.identity {
                    metadata.fields.push(("identity".into(), profile.clone()));
                }
                if let Some(agent) = &task.agent {
                    metadata.fields.push(("agent".into(), agent.clone()));
                }
                legacy.metadata(&metadata);
            }
            if let Some(result_index) = source.result {
                let content = events[result_index].payload["result"]["content"]
                    .as_str()
                    .filter(|text| text.len() <= 4 * 1024 * 1024);
                if let Some(output) = content
                    .and_then(|text| {
                        serde_json::from_str::<crate::subagents::SubagentsOutput>(text).ok()
                    })
                    .filter(|output| output.results.len() <= crate::subagents::MAX_SUBAGENT_TASKS)
                {
                    let task_id = format!("g{}", task_index + 1);
                    let matches: Vec<_> = output
                        .results
                        .iter()
                        .filter(|task| task.id == task_id)
                        .collect();
                    if let [result] = matches.as_slice() {
                        card["outcome"] = json!(match result.status {
                            crate::subagents::SubagentStatus::Completed => "success",
                            crate::subagents::SubagentStatus::Failed => "failure",
                        });
                        legacy.outcome = card["outcome"].as_str().map(str::to_owned);
                        legacy.usage = result.usage;
                        legacy.changed_paths = Some(result.changed_files.len() as u64);
                        if !result.changed_files.is_empty() {
                            card["changes"] = json!(if result.status
                                == crate::subagents::SubagentStatus::Completed
                            {
                                "applied"
                            } else {
                                "partial"
                            });
                        }
                    }
                }
            }
            let selected = recorded_task.unwrap_or(&legacy);
            if let Some(outcome) = &selected.outcome {
                card["outcome"] = json!(outcome);
            }
            super::project_task_telemetry(
                &mut card,
                selected,
                if recorded_task.is_some() {
                    "recorded"
                } else {
                    "not_recorded"
                },
            );
            if recorded_task.is_none() {
                omit(&mut card, "rows", "not_recorded", None);
            }
        } else if let Some(call) = call {
            let detail = if let Some(result_index) = source.result {
                let event = &events[result_index];
                let mut matched_calls = HashMap::new();
                matched_calls.insert(call.id.clone(), call.clone());
                chat::tool_result(event, &matched_calls)
                    .ok()
                    .map(|(_, mut result)| {
                        previews.apply(result_index, event, &mut result);
                        let assessment = assessments.for_result(result_index);
                        if !assessment.is_null() {
                            result.metadata["prompt_injection_protection"] = assessment.clone();
                        }
                        let label = crate::output::tool_display_label(&call);
                        let mut detail = crate::output::tool_display::tool_activity_detail(
                            &call,
                            &result,
                            label,
                            if result.success {
                                ActivityStatus::Success
                            } else {
                                ActivityStatus::Failed
                            },
                        );
                        crate::output::tool_card::ResourceReport::restore(
                            &mut detail,
                            &event.payload,
                        );
                        if matches!(call.name.as_str(), "code_mode" | "subagents") {
                            detail.metadata["telemetry_persistence"] = json!("recorded");
                        }
                        detail
                    })
            } else {
                let label = crate::output::tool_display_label(&call);
                Some(crate::output::tool_display::tool_activity_detail_pending(
                    &call,
                    label,
                    ActivityStatus::Running,
                ))
            };
            if let Some(mut detail) = detail {
                if source.result.is_none()
                    && let Some(summary) = source
                        .call
                        .and_then(|index| execution_summaries.get(&index))
                {
                    detail.metadata["code_mode_execution"] = summary.clone();
                    detail.metadata["telemetry_persistence"] = json!("recorded");
                }
                project_detail(
                    &mut card,
                    &detail,
                    true,
                    &mut super::BashNoticeState::default(),
                );
            }
            if let Some(call_index) = source.call {
                let event = &events[call_index];
                if event.kind() == Some(SessionEventKind::CodeModeToolCall) {
                    parents[index] = event.payload["parent_call_id"]
                        .as_str()
                        .and_then(|parent| calls.get(&(source.turn, parent)).copied().flatten())
                        .filter(|&parent| {
                            messages[parent]
                                .activity
                                .as_ref()
                                .and_then(|source| source.call)
                                .is_some_and(|parent_call| parent_call < call_index)
                        });
                    if parents[index].is_none() {
                        card["parent_state"] = json!("missing");
                        omit(&mut card, "ancestry", "missing_source", None);
                    }
                }
            }
            if source.call.is_none() {
                card["parent_state"] = json!("missing");
                omit(&mut card, "ancestry", "missing_source", None);
                omit(&mut card, "metadata", "not_recorded", None);
            }
        } else {
            omit(&mut card, "blocks", "missing_source", None);
            card["parent_state"] = json!("missing");
            omit(&mut card, "ancestry", "missing_source", None);
        }
        card["origin"] = json!("saved");
        card["state"] = json!("finished");
        card["revision"] = json!(0);
        if card["outcome"].is_null() {
            card["outcome"] = json!(if source.interrupted {
                "interrupted"
            } else {
                "unknown"
            });
        }
        bound_card(&mut card);
        retained_bytes += super::bytes(&card);
        cards.push(Some(card));
        limit_retention(&mut cards, &mut retained_bytes, budget)?;
    }
    // Reject cycles/depth before creating any mapped references.
    for index in 0..parents.len() {
        let mut path = vec![index];
        let mut cursor = index;
        while let Some(parent) = parents[cursor] {
            if path.contains(&parent) || path.len() > 8 {
                parents[index] = None;
                if let Some(card) = &mut cards[index] {
                    let previous = super::bytes(card);
                    card["parent_state"] = json!("omitted");
                    omit(card, "ancestry", "size_limit", None);
                    retained_bytes = retained_bytes - previous + super::bytes(card);
                }
                limit_retention(&mut cards, &mut retained_bytes, budget)?;
                break;
            }
            path.push(parent);
            cursor = parent;
        }
    }
    let mut children = vec![Vec::new(); cards.len()];
    for (index, parent) in parents.iter().enumerate() {
        if let Some(parent) = parent {
            children[*parent].push(index);
        }
    }
    for index in 0..cards.len() {
        let rows: Vec<_> = children[index].iter().take(32).filter_map(|&child| cards[child].as_ref().map(|card| {
            json!({"activity_id":ids[child],"task_id":card["task_id"],"title":card["title"],"state":"finished","outcome":card["outcome"],"changes":card["changes"]})
        })).collect();
        let task_children: Vec<_> = children[index]
            .iter()
            .copied()
            .filter(|&child| {
                messages[child]
                    .activity
                    .as_ref()
                    .is_some_and(|source| source.task_index.is_some())
            })
            .collect();
        let finished_tasks = task_children
            .iter()
            .filter(|&&child| {
                cards[child].as_ref().is_some_and(|card| {
                    matches!(
                        card["outcome"].as_str(),
                        Some("success" | "failure" | "cancelled")
                    )
                })
            })
            .count();
        let Some(card) = &mut cards[index] else {
            continue;
        };
        let previous = super::bytes(card);
        if let Some(parent) = parents[index] {
            card["parent_activity_id"] = json!(ids[parent]);
            card["parent_state"] = json!("available");
        }
        if !rows.is_empty() {
            if card["blocks"].as_array().unwrap().len() == 16 {
                card["blocks"].as_array_mut().unwrap().pop();
                omit(card, "blocks", "size_limit", None);
            }
            card["blocks"]
                .as_array_mut()
                .unwrap()
                .push(json!({"type":"children","children":rows,"total":children[index].len()}));
            if children[index].len() > 32 {
                omit(
                    card,
                    "children",
                    "size_limit",
                    Some(children[index].len() - 32),
                );
            }
        }
        if !task_children.is_empty() {
            if card["blocks"].as_array().unwrap().len() == 16 {
                card["blocks"].as_array_mut().unwrap().pop();
                omit(card, "blocks", "size_limit", None);
            }
            card["blocks"].as_array_mut().unwrap().push(json!({"type":"progress","label":"Tasks finished","completed":finished_tasks,"total":task_children.len(),"unit":"items"}));
        }
        bound_card(card);
        retained_bytes = retained_bytes - previous + super::bytes(card);
        limit_retention(&mut cards, &mut retained_bytes, budget)?;
    }
    Ok(cards)
}

fn limit_retention(
    cards: &mut [Option<Value>],
    retained: &mut usize,
    budget: usize,
) -> Result<(), &'static str> {
    if *retained <= budget {
        return Ok(());
    }
    for card in cards.iter_mut().rev().flatten() {
        let previous = super::bytes(card);
        super::make_core(card, "budget_limit");
        *retained = *retained - previous + super::bytes(card);
        if *retained <= budget {
            return Ok(());
        }
    }
    Err("history_limit")
}