(items: readonly SubagentProgress[])
| 15 | * and the tree renders flat — matching pre-observability behaviour. |
| 16 | */ |
| 17 | export function buildSubagentTree(items: readonly SubagentProgress[]): SubagentNode[] { |
| 18 | if (!items.length) { |
| 19 | return [] |
| 20 | } |
| 21 | |
| 22 | const byParent = new Map<string, SubagentProgress[]>() |
| 23 | const known = new Set<string>() |
| 24 | |
| 25 | for (const item of items) { |
| 26 | known.add(item.id) |
| 27 | } |
| 28 | |
| 29 | for (const item of items) { |
| 30 | const parentKey = item.parentId && known.has(item.parentId) ? item.parentId : ROOT_KEY |
| 31 | const bucket = byParent.get(parentKey) ?? [] |
| 32 | bucket.push(item) |
| 33 | byParent.set(parentKey, bucket) |
| 34 | } |
| 35 | |
| 36 | for (const bucket of byParent.values()) { |
| 37 | bucket.sort((a, b) => a.depth - b.depth || a.index - b.index) |
| 38 | } |
| 39 | |
| 40 | const build = (item: SubagentProgress): SubagentNode => { |
| 41 | const kids = byParent.get(item.id) ?? [] |
| 42 | const children = kids.map(build) |
| 43 | |
| 44 | return { aggregate: aggregate(item, children), children, item } |
| 45 | } |
| 46 | |
| 47 | return (byParent.get(ROOT_KEY) ?? []).map(build) |
| 48 | } |
| 49 | |
| 50 | /** |
| 51 | * Roll up counts for a node's whole subtree. Kept pure so the live view |
no test coverage detected