| 1 | import { isActiveSubagentStatus } from "@t3tools/client-runtime/state/subagentRuntime"; |
| 2 | import type { OrchestrationV2Subagent } from "@t3tools/contracts"; |
| 3 | import { formatDuration } from "@t3tools/shared/orchestrationTiming"; |
| 4 | import * as DateTime from "effect/DateTime"; |
| 5 | |
| 6 | export function subagentCardDetail(detail: string | null): string | null { |
| 7 | if (!detail || /^Child task ended with status\b/i.test(detail)) return null; |
| 8 | return ( |
| 9 | detail |
| 10 | .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1") |
| 11 | .replace(/`/g, "") |
| 12 | .replace(/^[ \t]*[-*][ \t]+/gm, "") |
| 13 | .replace(/\s+/g, " ") |
| 14 | .trim() || null |
| 15 | ); |
| 16 | } |
| 17 | |
| 18 | /** A group's wall time spans its first launch to its last completion. */ |
| 19 | export function subagentCardElapsed( |
| 20 | agents: ReadonlyArray<Pick<OrchestrationV2Subagent, "status" | "startedAt" | "completedAt">>, |
| 21 | nowMs: number, |
| 22 | ): string | null { |
| 23 | const starts = agents.flatMap((agent) => |
| 24 | agent.startedAt ? [DateTime.toEpochMillis(agent.startedAt)] : [], |
| 25 | ); |
| 26 | if (starts.length === 0) return null; |
| 27 | const live = agents.some((agent) => isActiveSubagentStatus(agent.status)); |
| 28 | // Settled agents without a completion timestamp must not keep counting their age. |
| 29 | if (!live && agents.some((agent) => agent.completedAt === null)) return null; |
| 30 | const ends = agents.flatMap((agent) => |
| 31 | agent.completedAt ? [DateTime.toEpochMillis(agent.completedAt)] : [], |
| 32 | ); |
| 33 | const duration = (live ? nowMs : Math.max(...ends)) - Math.min(...starts); |
| 34 | return duration > 0 ? formatDuration(duration) : null; |
| 35 | } |