vtcode_core/subagents/matrix/
projection.rs1use super::*;
2use crate::subagents::{SubagentStatus, SubagentStatusEntry};
3
4impl SubagentController {
5 pub(in crate::subagents) async fn matrix_projection_entries(&self) -> Vec<SubagentStatusEntry> {
7 let guard = self.matrix.state.lock().await;
8 let Some(state) = guard.as_ref() else {
9 return vec![];
10 };
11 let snapshot = state.snapshot();
12 let parent = self.parent_session_id.read().await.clone();
13 let updated_at = *self.matrix.updated_at.read();
14 snapshot
15 .tasks
16 .iter()
17 .zip(&snapshot.spec.tasks)
18 .map(|(state, task)| {
19 let attempt = state.attempts.last();
20 let (status, summary) = match state.status {
21 MatrixTaskStatus::Queued => (
22 SubagentStatus::Queued,
23 if task.dependencies.iter().any(|id| {
24 snapshot.tasks.iter().any(|task| {
25 &task.id == id
26 && !matches!(task.status, MatrixTaskStatus::Executed | MatrixTaskStatus::Verified)
27 })
28 }) {
29 "waiting for dependencies"
30 } else {
31 "waiting for worker, resources or workspace lease"
32 },
33 ),
34 MatrixTaskStatus::Assigned => (
35 SubagentStatus::Running,
36 if attempt.is_some_and(|attempt| attempt.phase == MatrixPhase::Verify) {
37 "final verification in progress"
38 } else {
39 "execution in progress"
40 },
41 ),
42 MatrixTaskStatus::Executed => {
43 (SubagentStatus::Waiting, "execution finished; final verification pending")
44 }
45 MatrixTaskStatus::Verified => (
46 if snapshot.lifecycle == MatrixLifecycle::Succeeded {
47 SubagentStatus::Completed
48 } else {
49 SubagentStatus::Waiting
50 },
51 "verified against current generation",
52 ),
53 MatrixTaskStatus::CleanupUncertain => {
54 (SubagentStatus::Failed, "owned cleanup uncertain; resources held")
55 }
56 _ => (SubagentStatus::Failed, "coordinator decision required"),
57 };
58 let status = if snapshot.lifecycle == MatrixLifecycle::Cancelled {
59 SubagentStatus::Closed
60 } else {
61 status
62 };
63 SubagentStatusEntry {
64 id: format!("matrix-{}-{}", snapshot.spec.id, task.id),
65 session_id: attempt
66 .map(|attempt| format!("matrix-{}-{}", parent, attempt.worker_id))
67 .unwrap_or_default(),
68 parent_thread_id: parent.clone(),
69 agent_name: "matrix".into(),
70 display_label: format!("{} / {}", snapshot.spec.id, task.id),
71 description: task.instructions.clone(),
72 source: "matrix".into(),
73 color: None,
74 status,
75 background: false,
76 depth: self.config.depth + 1,
77 created_at: updated_at,
78 updated_at,
79 completed_at: status.is_terminal().then_some(updated_at),
80 summary: Some(summary.into()),
81 error: self.matrix.error.read().clone(),
82 transcript_path: None,
83 nickname: None,
84 }
85 })
86 .collect()
87 }
88}
89
90pub(super) fn tracker_result(snapshot: &MatrixSnapshot) -> Value {
91 let items = snapshot
92 .tasks
93 .iter()
94 .map(|task| {
95 let status = if snapshot.lifecycle == MatrixLifecycle::Cancelled {
96 "blocked"
97 } else {
98 match task.status {
99 MatrixTaskStatus::Queued => "pending",
100 MatrixTaskStatus::Assigned | MatrixTaskStatus::Executed => "in_progress",
101 MatrixTaskStatus::Verified if snapshot.lifecycle == MatrixLifecycle::Succeeded => "completed",
102 MatrixTaskStatus::Verified => "in_progress",
103 _ => "blocked",
104 }
105 };
106 json!({"description":task.id,"status":status})
107 })
108 .collect::<Vec<_>>();
109 let lines = crate::tools::handlers::task_tracking::compact_task_tree_view_from_items(&items);
110 json!({"success":true,"matrix_id":snapshot.spec.id,"revision":snapshot.revision,
111 "view":{"title":format!("Matrix {}",snapshot.spec.id),"lines":lines},
112 "summary":{"total":items.len(),"completed":items.iter().filter(|item|item["status"]=="completed").count(),"in_progress":items.iter().filter(|item|item["status"]=="in_progress").count(),"pending":items.iter().filter(|item|item["status"]=="pending").count(),"blocked":items.iter().filter(|item|item["status"]=="blocked").count()}})
113}
114
115impl SubagentController {
116 pub async fn matrix_tracker_projection(&self) -> Option<Value> {
117 self.matrix
118 .state
119 .lock()
120 .await
121 .as_ref()
122 .map(|state| tracker_result(state.snapshot()))
123 }
124}