Repository navigation
feat(performance): attribute memory and CPU to agents in the monitor overview - #986
Merged
Merged
Conversation
The Performance Monitor (issue #951) became the single performance surface in Settings, the palette and Settings Bar, but the two surfaces it replaced stayed in the tree unreferenced: the verbose Performance panel (features/performance) and the system-perf badge/popover with its 1 Hz poller. Nothing mounted either one; only the feature-reference catalog still imported them. Deleting them takes their backend endpoints with it: performance:pane-stats (ProcessTelemetry still serves sessionManager) and performance:system-stats (MainProbe still feeds the monitor). The "performance" feature-reference page moves to the monitor directory that now owns its commands, keeping the historical page id so existing links keep resolving. Refs #984
The monitor could report what all of Agent Code used, but not which agent was responsible, and a user opening it needs the trend from BEFORE they opened it. AgentUsageHistory derives a per-session and per-owner composition from each process page the coordinator already caches, keeping 15 minutes (the monitor's recent window) in main. Attribution rules, unit-tested: a process owned by exactly one session counts toward that agent (its children included); a process reachable from several sessions is counted once as shared, never once per owner; app/agents/terminals/shared/other always sum to the page total; a placeholder row for an undiscoverable root is visible as a session but is not a process. Sessions absent from the newest page drop out of the ranking so an exited agent's last reading cannot linger. It lives in main, not the helper: the work is one linear pass per sample, and a helper query reply is capped at 256 KiB — too small for a large fleet's 15 minutes of history. Served read-only via performance:monitor-agents with one shared in-flight preload promise. Refs #984
TimeSeriesChart, BarChart and Sparkline replace the monitor's static SVG pictures. The old charts had no hover, no axis units, and scaled every series to its own peak, so a calm line and a stall drew the same shape — height carried no information. TimeSeriesChart: real pixel axes with rounded ceilings, runs that break at missing readings and time gaps instead of drawing through them, a crosshair that snaps to real samples (never interpolated values), an optional shared hover so charts over the same window move one crosshair, thresholds and incident markers, and full keyboard access (arrows, Home, End, Escape) with a live screen-reader readout. BarChart draws nested series (wall-clock inside agent-hours) with the same readout. Sparkline draws a trend on a caller-supplied shared ceiling so heights compare across table rows. Geometry rules (ceilings, wall-aligned time ticks, gap detection, nearest-sample search) are pure functions in chartMath with unit tests; shared by the Performance Monitor and Agent Analytics so both behave the same way. Refs #984
The overview now answers, in reading order: is the app heavy, where did memory and CPU go, which agent is responsible, is the UI keeping up. - Totals strip: app memory with share of system RAM and 15-minute delta, app CPU, heaviest agent, running counts, UI responsiveness, main heap, incidents — toned with the incident engine's own thresholds so a color means the same thing everywhere. - Memory and CPU stacked by owner with one crosshair across both. - Agents by resource use: every running session under its workspace label (A15) with title, provider and tab; memory and share of the whole app; signed growth; CPU; process count; sparkline on a shared scale. Sortable by memory, CPU or growth — a leaking agent is often not the largest. Go to wakes a detached agent, focuses it and closes the monitor only when navigation succeeds. - Timeline on interactive lanes with 1h/6h ranges, Earlier/Later panning and incident markers that select their incident. - Processes shows agent label and title instead of session-ID prefixes; labels come from the same resolution the workspace uses, tested to navigate where they claim. Drag-to-zoom on the timeline is a follow-up. Refs #984
Per-day bars move to the shared BarChart: wall-clock draws inside agent-hours (the containment is real — agent-hours always include the wall-clock union), so one bar shows both and the gap between them is the parallelism. Hover or arrow keys read both figures; the old native SVG titles appeared only after a delay and were unreachable from the keyboard. Adds a parallelism tile and per-project share of all agent-hours. Local-date parsing avoids the UTC off-by-one that would print the previous day west of Greenwich. Refs #984
Removing performance:pane-stats in the panel deletion left the chain behind it fully dead: the ProcessTelemetry class had no importer but its own test, PerformanceService.recordPaneProcessStats had no caller, and PanePerformanceStats/PanePerformanceSnapshot existed only to serve them. Dead code that keeps a test green is worse than none — it reads as a protected contract. Found by review; same dead-chain scope the PR already claimed. Also repoints a transcriptParser WHY comment at the Performance Monitor now that the system-perf popover it cited is gone. Refs #986
- Heaviest-agent tile computes the max instead of trusting the
sessions array's sort order, which is a main-side presentation
choice no contract pinned.
- The growth column labels the window actually measured ('N min'),
not a promised 15 minutes the data does not have right after launch.
- A zero-measured share row paints no bar sliver.
- Comments now record three deliberate decisions: decimal GB labels
matching Activity Monitor (users cross-check against it), incident
markers being a pointer shortcut while the incident list is the
accessible path, and pinned dispatch labels (★1) being unresolvable
by design so navigation must stay sessionId-based.
Refs #986
Three contracts the reviews found untested: sessions rank by memory descending (the heaviest-agent tile and ranking table both treat sessions[0] as the biggest consumer); the 256-session map cap evicts the oldest-seen and re-admits a live evicted session with fresh history; and the coordinator records usage only from validated complete process pages (children summed into their owner) and clears on stop. Mirrors the one-shared-in-flight read test for getMonitorAgentUsage, whose comment claimed that contract. Refs #986
…tor-ux # Conflicts: # src/renderer/src/features/system-perf/ui/SystemPerfBadge.tsx # src/renderer/src/features/system-perf/ui/SystemPerfPopover.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #984
Refs #944
Problem
The Performance Monitor (#951) could say Agent Code was heavy, but not which agent was responsible. Its charts were static pictures: no hover, no y-axis units, and each line scaled to its own peak, so a calm series and a stall drew the same shape. The process table showed raw session-ID prefixes nobody can find on screen. Two superseded debug surfaces (the old Performance panel and the system-perf badge/popover) and their backend endpoints were still in the tree, unreferenced.
Someone opening the monitor needs answers in order: is the app heavy right now, where did memory and CPU go over the last minutes, which agent is responsible (under the label the workspace already shows, e.g.
A15), and is the UI keeping up.What changed
Per-agent history (main).
AgentUsageHistorykeeps 15 minutes of per-session memory and CPU plus a per-owner composition, derived from the process pages the coordinator already caches. Attribution rules, unit-tested:It lives in main rather than the helper because the question needs the minutes before the monitor opened, and helper query replies are capped at 256 KiB — too small for a large fleet's history. Served read-only via
performance:monitor-agents, so it never waits on the helper.Shared charts (
components/charts).TimeSeriesChart(real pixel axes with rounded ceilings; runs break at missing readings and time gaps instead of drawing through them; crosshair snaps to real samples; one shared hover across charts covering the same window; thresholds; incident markers; full keyboard access with a live screen-reader readout),BarChart(nested series) andSparkline(fixed caller-supplied ceiling so heights compare across rows). Geometry rules are pure, unit-tested functions inchartMath.Monitor Overview rebuilt. Totals strip (app memory with share of system RAM and 15-minute delta, app CPU, heaviest agent, running counts, UI responsiveness, main heap, incidents — toned with the incident engine's own thresholds), stacked memory/CPU charts with one crosshair, then Agents by resource use: every running session under its workspace label (
A15) with title, provider and tab; memory and share of the whole app; signed growth; CPU; process count; sparkline. Sortable by memory, CPU or growth — a leaking agent is often not the largest. Go to wakes a detached agent, focuses it, and closes the monitor only when navigation succeeds.Timeline and Processes. Timeline moved onto the same charts: memory/CPU/responsiveness lanes, 1h and 6h ranges, Earlier/Later panning (the 7-day retention is finally reachable), and incident markers that select their incident. Processes shows agent label and title instead of session-ID prefixes; labels resolve through the workspace's own pane-label resolution, tested to navigate where they claim.
Agent Analytics uses the same chart components: per-day bars with wall-clock nested inside agent-hours (the containment is real; the gap is the parallelism), hover/keyboard readout, a parallelism tile, per-project share of all agent-hours.
Dead code removed. The old Performance panel (
features/performance) and system-perf badge/popover (features/system-perf) with their endpointsperformance:pane-statsandperformance:system-stats, plus — found by review — the fully orphaned chain behind pane-stats: theProcessTelemetryclass,PerformanceService.recordPaneProcessStats, and thePanePerformanceStats/PanePerformanceSnapshottypes. The "performance" feature-reference page moved to the monitor directory that now owns its commands, keeping the historical page id.Design decisions
Independent reviews
Three independent Agent Code orchestration reviews ran on this PR (data layer, renderer/charts, PR-level conventions). All three verdicts: approve-with-comments, no blockers. Every blocker/major/minor finding was fixed in response:
Tests and verification
NODE_ENV=test npx vitest runon: charts (geometry + renderer interaction),AgentUsageHistory(attribution, ordering, eviction, ranking freshness),MonitorCoordinator(incl. usage wiring), performance-monitor (overview ranking, labels, Go-to), agent-analytics, feature-reference coverage, preload performance API — 9 files, 39 tests, all passing.npx tsc -bclean (full monorepo build).Limitations and follow-ups
useMonitor(pre-existing) stops polling if a snapshot invoke never settles; a watchdog re-arm is a recorded follow-up.