| 56 | * in the overlay / inline view so the eye spots the expensive branch. |
| 57 | */ |
| 58 | export function aggregate(item: SubagentProgress, children: readonly SubagentNode[]): SubagentAggregate { |
| 59 | let totalTools = item.toolCount ?? 0 |
| 60 | let totalDuration = item.durationSeconds ?? 0 |
| 61 | let descendantCount = 0 |
| 62 | let activeCount = isRunning(item) ? 1 : 0 |
| 63 | let maxDepthFromHere = 0 |
| 64 | let inputTokens = item.inputTokens ?? 0 |
| 65 | let outputTokens = item.outputTokens ?? 0 |
| 66 | let costUsd = item.costUsd ?? 0 |
| 67 | let filesTouched = (item.filesRead?.length ?? 0) + (item.filesWritten?.length ?? 0) |
| 68 | |
| 69 | for (const child of children) { |
| 70 | totalTools += child.aggregate.totalTools |
| 71 | totalDuration += child.aggregate.totalDuration |
| 72 | descendantCount += child.aggregate.descendantCount + 1 |
| 73 | activeCount += child.aggregate.activeCount |
| 74 | maxDepthFromHere = Math.max(maxDepthFromHere, child.aggregate.maxDepthFromHere + 1) |
| 75 | inputTokens += child.aggregate.inputTokens |
| 76 | outputTokens += child.aggregate.outputTokens |
| 77 | costUsd += child.aggregate.costUsd |
| 78 | filesTouched += child.aggregate.filesTouched |
| 79 | } |
| 80 | |
| 81 | const hotness = totalDuration > 0 ? totalTools / totalDuration : 0 |
| 82 | |
| 83 | return { |
| 84 | activeCount, |
| 85 | costUsd, |
| 86 | descendantCount, |
| 87 | filesTouched, |
| 88 | hotness, |
| 89 | inputTokens, |
| 90 | maxDepthFromHere, |
| 91 | outputTokens, |
| 92 | totalDuration, |
| 93 | totalTools |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Count of subagents at each depth level, indexed by depth (0 = top level). |