use super::*;
const CHILD_RECENT_SECS: i64 = 15;
#[derive(Debug, Clone)]
pub(crate) struct ChildState {
pub(crate) session_id: String,
pub(crate) state: &'static str,
pub(crate) detail: String,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct ChildrenReport {
pub(crate) children: Vec<ChildState>,
pub(crate) journal_in_flight: usize,
pub(crate) live_count: usize,
}
pub(crate) fn children_report(main_jsonl: &Path) -> Result<ChildrenReport> {
let mut report = ChildrenReport::default();
let subs = crate::subagent::subagent_transcript_files(main_jsonl).unwrap_or_default();
for sub in &subs {
let sid = crate::subagent::session_id_from_path(sub);
let shape = tail_shape(sub)?;
let mtime_age = std::fs::metadata(sub)
.ok()
.and_then(|m| m.modified().ok())
.and_then(|t| t.elapsed().ok())
.map(|d| i64::try_from(d.as_secs()).unwrap_or(i64::MAX));
let (state, detail) = if let Some((tool, ts)) = &shape.unreturned_use {
(
"in-flight",
format!(
"unreturned {tool} call{}",
age_secs(ts.as_deref())
.map(|a| format!(" ({a}s ago)"))
.unwrap_or_default()
),
)
} else if mtime_age.is_some_and(|a| a <= CHILD_RECENT_SECS) {
(
"active",
format!("transcript grew {}s ago", mtime_age.unwrap_or(0)),
)
} else {
(
"settled",
shape
.last_ts_utc
.as_deref()
.and_then(|t| age_secs(Some(t)))
.map_or_else(
|| "no timestamped tail".to_string(),
|a| format!("last record {a}s ago"),
),
)
};
if state != "settled" {
report.live_count += 1;
}
report.children.push(ChildState {
session_id: sid,
state,
detail,
});
}
let sub_root = main_jsonl
.with_extension("")
.join("subagents")
.join("workflows");
if sub_root.is_dir() {
for wf in std::fs::read_dir(&sub_root)? {
let wf = wf?;
let journal = wf.path().join("journal.jsonl");
if !journal.is_file() {
continue;
}
let Ok(raw) = std::fs::read_to_string(&journal) else {
continue;
};
let mut started = 0usize;
let mut resulted = 0usize;
for line in raw.lines() {
let Ok(v) = serde_json::from_str::<serde_json::Value>(line) else {
continue;
};
match v.get("type").and_then(serde_json::Value::as_str) {
Some("started") => started += 1,
Some("result") => resulted += 1,
_ => {}
}
}
report.journal_in_flight += started.saturating_sub(resulted);
}
}
if report.journal_in_flight > 0 {
report.live_count += report.journal_in_flight;
}
Ok(report)
}