// — an agent's subagents, under its own terminal preview in // `AgentsPage`'s detail panel. The list is `GET /api/agents/{name}/subagents`: // the subagents the swarm queue holds terminal rows for, each with the // publish time of its newest row. Newest first, with a ticking age beside // the name; one quiet for `STALE_S` is dimmed. Subagents are spawned on // demand, so `useSubagentList` refetches it. Picking one shows its // terminal in `AgentTermPreview`, expand-to-tab included. Read-only like the // agent's own: subagents take no input. // // Mount it keyed by agent name: the refresh timer is armed once per mount. import { useEffect, useState } from "preact/hooks"; import { ApiErrorPanel } from "@hive/shared/api-error-panel.js"; import { Badge } from "@hive/shared/badge.js"; import { AgentTermPreview } from "./AgentTermPreview.js"; import { STALE_S, runLabel, stillListed } from "./subagent-list.js"; import { useSubagentList } from "./useSubagentList.js"; import { RelativeTime } from "../../ui/relative-time/RelativeTime.js"; import "./SubagentTerms.css"; export function SubagentTerms({ agentName }: { agentName: string }) { const { subagents, error } = useSubagentList(agentName); const [picked, setPicked] = useState(null); // The refresh can drop the picked subagent (its stream is gone). Only a // name still in the list counts as selected, so the terminal closes with // its badge and a later subagent of the same name does not reopen it. const selected = stillListed(picked, subagents); useEffect(() => { if (picked !== null && subagents !== null && selected === null) { setPicked(null); } }, [picked, subagents, selected]); // An agent with no subagents shows nothing here — no heading, no // "none" line — and so does the first fetch while it is in flight. A // failed fetch still shows, so the error is not swallowed. if (!error && (subagents === null || subagents.length === 0)) return null; const nowS = Date.now() / 1000; return (
subagents {subagents?.map((sub) => { const { name, last_row_unix } = sub; const runs = runLabel(sub); const stale = last_row_unix !== null && nowS - last_row_unix > STALE_S; return ( setPicked(name === selected ? null : name)} /> {runs && {runs}} {last_row_unix !== null && ( )} ); })}
{error && ( )} {selected && ( )}
); }