# Realtime Agents Activity View **Date:** 2026-03-14 **Status:** Approved ## Overview Replace the single-timeline Agents page with a per-agent lane layout showing live presence, in-progress operations, and recent events grouped by VM (client_id). ## Data Model: Live State Tracker Track open events (start without matching end) to derive live status: - **Active sessions**: `session.start` events keyed by `session_id`, removed on `session.end`. Each knows its VM, start time, framework. - **In-progress operations**: `span.start`/`run.start` keyed by `span_id`/`run_id`. Marked complete on matching end event. Show elapsed timer while open. - **Per-agent grouping**: All state grouped by `client_id` (zap, orb, sun). Initial REST load seeds state, WebSocket updates keep it current. ## Layout: Per-Agent Lanes Three columns (one per VM), replacing the single timeline + stats sidebar: ``` ┌─────────────────────────────────────────────────┐ │ Agents ● Live │ ├───────────────┬───────────────┬─────────────────┤ │ ZAP │ ORB │ SUN │ │ ● 2 sessions │ ● 1 session │ ○ idle │ │ ───────────── │ ───────────── │ ─────────────── │ │ ▶ Bash (3.2s) │ ▶ Read (1.1s) │ │ │ ▶ Grep (...) │ │ (recent events) │ │ ───────────── │ ───────────── │ │ │ recent events │ recent events │ │ └───────────────┴───────────────┴─────────────────┘ ``` Each lane has: - **Header**: VM name, online/offline dot, active session count - **Active operations**: In-progress spans/runs with pulsing dot + live elapsed timer - **Recent events**: Completed events, same card style, scoped to this agent Summary stats (messages/tools/errors) move to a compact row above the lanes. Responsive: lanes stack vertically below 900px. ## Active Operations Display - `span.start` (tool): pulsing green dot + tool name + elapsed counter (1s setInterval) - `run.start`: shows as "Thinking..." until `run.end` - On matching end event: pill fades out, completed event appears in recent list - If session has both active run + active tool spans, show tool spans (more specific) - Stale guard: after 5 minutes with no update, dim and show "(stale?)" ## Implementation Notes - All changes in `style.css` and `app.js` (no backend changes needed) - Reuse existing WebSocket subscription and REST API calls - Existing event envelope fields (`correlation.session_id`, `correlation.span_id`, `correlation.run_id`, `source.client_id`) provide all grouping keys