use std::collections::HashSet;
use uuid::Uuid;
use crate::app::events::{AppEvent, AppTask, BackgroundKind, TASK_LANDED_CAP, TaskList, TaskRun};
use crate::entities::subagent::SubagentRun;
use super::{InflightChild, Orchestrator};
const APP_TASKS: [BackgroundKind; 4] = [
BackgroundKind::Reflection,
BackgroundKind::Consolidation,
BackgroundKind::SelfConsolidation,
BackgroundKind::Compaction,
];
impl Orchestrator {
pub(super) fn emit_task_list(&self) {
let _ = self
.evt_tx
.send(AppEvent::TaskList(Box::new(self.task_list())));
}
pub(super) fn task_list(&self) -> TaskList {
let mut runs: Vec<TaskRun> = Vec::new();
let mut mirrored: HashSet<Uuid> = HashSet::new();
for seat in &self.background_runs {
let Some(row) = self.mirror_row(&seat.child, seat.chat, seat.is_out(), true) else {
continue;
};
mirrored.insert(row.id);
runs.push(row);
}
if let Some(turn) = &self.inflight {
for child in &turn.children {
let running = child.run.outcome.is_none();
let Some(row) = self.mirror_row(child, turn.chat, running, false) else {
continue;
};
mirrored.insert(row.id);
runs.push(row);
}
}
for chat in &self.chats {
for run in chat.children().filter(|r| !mirrored.contains(&r.id)) {
runs.push(record_row(run, chat.id, chat.title.clone(), None, false));
}
}
let (mut running, mut landed): (Vec<TaskRun>, Vec<TaskRun>) =
runs.into_iter().partition(|r| r.running);
running.sort_by_key(|r| std::cmp::Reverse(r.created_at));
landed.sort_by_key(|r| std::cmp::Reverse(r.finished_at.unwrap_or(r.created_at)));
let more_landed = landed.len().saturating_sub(TASK_LANDED_CAP);
landed.truncate(TASK_LANDED_CAP);
running.extend(landed);
let streaming = self
.session_budget_memo
.as_ref()
.and_then(|(_, budget)| budget.silent_streaming());
TaskList {
runs: running,
more_landed,
app: APP_TASKS
.iter()
.map(|&kind| {
let running = self.bg_running(kind);
AppTask {
kind,
running,
waiting: running && streaming != Some(super::background::lane_label(kind)),
}
})
.collect(),
}
}
fn mirror_row(
&self,
child: &InflightChild,
parent: Uuid,
running: bool,
background: bool,
) -> Option<TaskRun> {
let title = self.chats.iter().find(|c| c.id == parent)?.title.clone();
let position = running.then(|| child.position.clone()).flatten();
Some(
record_row(&child.run, parent, title, position, running)
.with_background(background || child.run.background),
)
}
}
fn record_row(
run: &SubagentRun,
parent: Uuid,
parent_title: String,
position: Option<crate::app::events::SubagentProgress>,
running: bool,
) -> TaskRun {
TaskRun {
id: run.id,
kind: run.kind,
title: run.title.clone(),
parent,
parent_title,
created_at: run.created_at,
finished_at: run.finished_at,
outcome: run.outcome,
running,
background: run.background,
tokens: run.tokens,
position,
}
}
impl TaskRun {
fn with_background(mut self, background: bool) -> Self {
self.background = background;
self
}
}