1import {
2 isActiveSubagentStatus,
3 isTerminalSubagentStatus,
4 type RuntimeSubagent,
5} from "@t3tools/client-runtime/state/subagentRuntime";
6
7/** Summarize observed states without treating idle or missing agents as completed. */
8export function deriveAgentSpawnSummary({
9 agents,
10 agentCount,
11 coordinatorStatus,
12}: {
13 agents: ReadonlyArray<Pick<RuntimeSubagent, "kind" | "status">>;
14 agentCount: number;
15 coordinatorStatus?: RuntimeSubagent["status"] | undefined;
16}) {
17 const working = agents.filter((agent) => isActiveSubagentStatus(agent.status)).length;
18 const failed = agents.filter((agent) => agent.status === "failed").length;
19 const idle = agents.filter((agent) => agent.status === "idle").length;
20 const stopped = agents.filter(
21 (agent) => agent.status === "cancelled" || agent.status === "interrupted",
22 ).length;
23 const batches = agents.filter((agent) => agent.kind === "subagent_batch").length;
24 const individuals = agentCount - batches;
25 // Workflow coordinators can keep running between dynamic member launches.
26 const live =
27 coordinatorStatus !== undefined ? !isTerminalSubagentStatus(coordinatorStatus) : working > 0;
28 const subjects = [
29 individuals > 0 ? `${individuals} subagent${individuals === 1 ? "" : "s"}` : null,
30 batches > 0
31 ? `${batches} ${individuals > 0 ? "" : "subagent "}batch${batches === 1 ? "" : "es"}`
32 : null,
33 ]
34 .filter(Boolean)
35 .join(" and ");
36 const lead = `${batches > 0 ? "Launched" : live ? "Kicked off" : "Ran"} ${subjects || "subagents"}`;
37
38 const status = live
39 ? working > 0
40 ? `${working} working`
41 : "working"
42 : coordinatorStatus === "failed"
43 ? "Workflow failed"
44 : coordinatorStatus === "cancelled" || coordinatorStatus === "interrupted"
45 ? "Workflow stopped"
46 : failed > 0
47 ? `${failed} failed`
48 : stopped > 0
49 ? `${stopped} stopped`
50 : idle > 0
51 ? `${idle} idle`
52 : coordinatorStatus !== "completed" &&
53 (agents.length === 0 || agents.length < agentCount)
54 ? "Status unavailable"
55 : "✓ completed";
56 const tone = live
57 ? "working"
58 : failed > 0 || coordinatorStatus === "failed"
59 ? "failed"
60 : status === "✓ completed"
61 ? "completed"
62 : "inactive";
63 return { live, lead, status, tone };
64}