magi-code 0.96.1

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

impl MissionControlState {
    pub(super) fn update_subagent_transcript_started(
        &mut self,
        id: &ActivityId,
        kind: &ActivityKind,
        status: ActivityStatus,
        label: &str,
    ) {
        self.update_subagent_transcript(id, |task, is_task| {
            if is_task {
                task.status = status;
                if matches!(kind, ActivityKind::SubagentTask) {
                    task.push_recent(format!("started {}", bounded_transcript_label(label, 80)));
                }
            } else {
                task.push_recent(format!(
                    "{}: {}",
                    activity_kind_text(kind),
                    bounded_transcript_label(label, 80)
                ));
            }
        });
    }

    pub(super) fn update_subagent_transcript_final_preview(
        &mut self,
        id: &ActivityId,
        metadata: &ActivityMetadata,
        status: ActivityStatus,
    ) {
        self.update_subagent_transcript(id, |task, _| {
            let summary = metadata
                .detail
                .as_deref()
                .unwrap_or(metadata.label.as_str())
                .trim();
            if summary.is_empty() {
                task.push_recent(format!("final: {}", activity_status_text(status)));
            } else {
                task.push_recent(format!("final: {}", bounded_transcript_label(summary, 100)));
            }
        });
    }

    pub(super) fn update_subagent_transcript_finished(
        &mut self,
        id: &ActivityId,
        status: ActivityStatus,
        metadata: Option<&ActivityMetadata>,
    ) {
        self.update_subagent_transcript(id, |task, is_task| {
            if is_task {
                task.status = status;
            }
            let label = metadata
                .map(|metadata| metadata.label.as_str())
                .unwrap_or_default()
                .trim();
            if label.is_empty() || is_task {
                task.push_recent(format!("finished: {}", activity_status_text(status)));
            } else {
                task.push_recent(format!(
                    "finished {}: {}",
                    activity_status_text(status),
                    bounded_transcript_label(label, 80)
                ));
            }
        });
    }

    pub(super) fn update_subagent_transcript_usage(
        &mut self,
        id: &ActivityId,
        current_tokens: usize,
        max_tokens: usize,
        reasoning_tokens: Option<usize>,
        source: ContextUsageSource,
        request_sequence: u64,
    ) {
        self.update_subagent_transcript(id, |task, _| {
            task.update_usage(
                current_tokens,
                max_tokens,
                reasoning_tokens,
                source,
                request_sequence,
            );
        });
    }

    pub(super) fn update_subagent_transcript(
        &mut self,
        id: &ActivityId,
        update: impl FnOnce(&mut ActiveSubagentTask, bool),
    ) {
        self.retain_valid_active_tool_transcripts();
        let mut update = Some(update);
        let parent_ids = self
            .active_tool_transcripts
            .keys()
            .cloned()
            .collect::<Vec<_>>();
        let rendered = parent_ids.into_iter().find_map(|parent_id| {
            let task_id = {
                let active = self.active_tool_transcripts.get(&parent_id)?;
                let subagents = active.subagents.as_ref()?;
                self.resolve_subagent_transcript_task(&parent_id, subagents, id)?
            };
            let active = self.active_tool_transcripts.get_mut(&parent_id)?;
            let subagents = active.subagents.as_mut()?;
            subagents.ensure_task_from_descendant(&parent_id, &task_id);
            let task = subagents.task_for_descendant_mut(&task_id)?;
            let is_task = task.id == *id;
            let update = update.take()?;
            update(task, is_task);
            Some((active.index, active.render_entry()))
        });

        if let Some((absolute_index, entry)) = rendered
            && let Some(index) = self.transcript_local_index(absolute_index)
            && let Some(transcript_entry) = self.transcript.get_mut(index)
        {
            *transcript_entry = entry;
            self.invalidate_transcript_entry_cache(index);
        }
    }

    pub(super) fn has_complete_activity_parent_graph(&self, id: &ActivityId) -> bool {
        let Some(_) = self.nodes.get(id) else {
            return false;
        };
        let mut current = id;
        let mut visited = HashSet::new();
        loop {
            if !visited.insert(current.clone()) {
                return true;
            }
            let Some(node) = self.nodes.get(current) else {
                return false;
            };
            match &node.parent_provenance {
                ActivityParentProvenance::Unavailable => return false,
                ActivityParentProvenance::ExplicitRoot => return true,
                ActivityParentProvenance::Declared(declared_parent) => {
                    if node.parent_id.as_ref() != Some(declared_parent)
                        || !self.nodes.contains_key(declared_parent)
                    {
                        return false;
                    }
                    current = declared_parent;
                }
            }
        }
    }

    pub(super) fn activity_path_fallback_allowed(&self, id: &ActivityId) -> bool {
        self.nodes.get(id).is_none_or(|node| {
            matches!(
                &node.parent_provenance,
                ActivityParentProvenance::Unavailable
            )
        })
    }

    pub(super) fn resolve_subagent_transcript_task(
        &self,
        parent_id: &ActivityId,
        subagents: &ActiveSubagentTranscript,
        id: &ActivityId,
    ) -> Option<ActivityId> {
        if let Some(task) = subagents.tasks.iter().find(|task| task.id == *id) {
            return Some(task.id.clone());
        }
        if let Some(task) = subagents
            .tasks
            .iter()
            .find(|task| self.activity_is_descendant_of(id, &task.id))
        {
            return Some(task.id.clone());
        }
        if self.has_complete_activity_parent_graph(id) || !self.activity_path_fallback_allowed(id) {
            return None;
        }
        if let Some(task) = subagents.task_for_descendant(id) {
            return Some(task.id.clone());
        }
        subagents.task_id_from_prefixed_descendant(parent_id, id)
    }

    pub(super) fn activity_child_progress(&self, parent_id: &ActivityId) -> String {
        let children = self.children.get(parent_id).cloned().unwrap_or_default();
        if children.is_empty() {
            return "running".to_string();
        }
        let total = children.len();
        let completed = children
            .iter()
            .filter(|id| {
                self.nodes.get(id).is_some_and(|node| {
                    matches!(
                        node.status,
                        ActivityStatus::Success | ActivityStatus::Failed | ActivityStatus::Canceled
                    )
                })
            })
            .count();
        let failed = children
            .iter()
            .filter(|id| self.nodes.get(id).map(|node| node.status) == Some(ActivityStatus::Failed))
            .count();
        let running = children.iter().find_map(|id| {
            let node = self.nodes.get(id)?;
            matches!(
                node.status,
                ActivityStatus::Running | ActivityStatus::Writing
            )
            .then(|| node.metadata.label.clone())
        });
        match running {
            Some(label) => format!(
                "{completed}/{total} complete{PADDED_INLINE_SEPARATOR}current: {}",
                normalize_inline_separators(&label)
            ),
            None if failed > 0 => {
                format!("{completed}/{total} complete{PADDED_INLINE_SEPARATOR}{failed} failed")
            }
            None => format!("{completed}/{total} complete"),
        }
    }
}