# Agents Page (v2) ### Route: `/agents` ### Source: `packages/web/src/routes/agents.tsx` --- ## v1 -> v2 Changes | Aspect | v1 | v2 | |--------|----|----| | Provider filter | None | Dropdown: All / Claude / Codex / Gemini / etc. | | Mode filter | None | Dropdown: All / discuss / plan / detail / execute / verify / etc. | | Search | None | Text input filtering agent name | | Agent card context | Name + status + provider + mode + time only | Added context row: "Task: ..." or "Initiative: ..." with link | | Agent card actions | Inline in card only | `[...]` dropdown menu (Stop / Dismiss / Delete) in card | | Status dots | Static colored circles | Animated dots: pulsing (running), static (waiting), hollow (exited) | | Agent list width | 320px fixed | 360px fixed — accommodates context rows + action menus | | Output viewer | Basic dark panel | Full terminal: monospace font, ANSI support, auto-scroll, search | | Output viewer header | Name only | Name + status badge + session count + token/cost counters | | Exited agents | Disappear when dismissed | Remain with `neutral` styling; dismissal moves to Dismissed filter | | Loading state | 5 skeleton cards (basic) | 5 skeleton cards with shimmer animation | | Error state | AlertCircle + retry (exists) | Same (unchanged) | | Empty (filtered) | "No agents match this filter" | "No matching agents" + `[Clear filters]` link | | Empty (zero agents) | None | Centered empty state with description | --- ## Default State (with agents) ``` +=====================================================================================+ | [CW] Initiatives [*Agents*] *3 Inbox (2) Settings [cmd-k] [sun] * myws | +=====================================================================================+ | | | Agents (7) [ Refresh ] | | [search___________] [All providers v] [All modes v] | | | | Agent List 360px | Agent Output | | ----------------------------+--------------------------------------------------------| | +---------------------------+ | blue-fox-7 [RUNNING] Session 2 $0.42 12k▼ | | | (*) blue-fox-7 [...] | | ───────────────────────────────────────────────── | | | claude . execute | | | | | Task: PKCE flow | | > Analyzing codebase structure... | | | 2m ago | | > Found 12 files matching pattern | | +---------------------------+ | > Creating src/auth/pkce.ts | | | > Writing test file... | | +---------------------------+ | | | | (-) red-elk-3 [...] | | | [Tool Call] Write | | | claude . plan | | | file_path: src/auth/pkce.ts | | | Init: Auth Overhaul | | | | | 1h ago | | | [Result] 42 lines written | | +---------------------------+ | | | | | | +---------------------------+ | [search] [v] | | | (?) green-bear-1 [...] | | | | | codex . detail | | (*) Following | auto-scrolling to bottom | | | Task: DB schema | | | | | 5m ago | +-----------------------------------------------------+ | | Answer questions -> | | +---------------------------+ +=====================================================================================+ ``` ### Key layout changes from v1 - **Agent list: 360px** (up from 320px) to accommodate context rows, provider/mode badges, and the `[...]` action menu without truncation. - **Output viewer header** now shows: agent name, status badge, session number, cumulative cost, and token count with a down-arrow indicator for input tokens. - **Output viewer footer bar** has terminal search (`[search]`) and scroll-to-bottom (`[v]`). The `(*) Following` indicator replaces the ambiguous Follow/Pause toggle. - **Status indicators** use `(*)` `(?)` `(-)` in ASCII but render as styled dots (see Status Dot section below). --- ## Search + Filter Controls ``` [search___________] [All providers v] [All modes v] ``` ### Filter order rationale Search is leftmost because it is the highest-frequency action: you already know the agent name. Provider second because in a multi-provider setup (5+ accounts across Claude/Codex/Gemini), narrowing by provider is the next most common filter. Mode last because it is the most stable dimension (most users run execute tasks predominantly). ### Search input - Placeholder: "Search agents..." - Client-side filter on `agent.name` (case-insensitive substring match) - Also matches task name and initiative name from the context row - Debounced 150ms - Clear button `[x]` appears when non-empty - Keyboard shortcut: `/` focuses the search input (when no other input is focused) ### Provider filter dropdown `[All providers v]` ``` [All providers v] +-----------------+ | All | | claude (4) | | codex (2) | | gemini (1) | +-----------------+ ``` Populated dynamically from `listProviderNames` tRPC query. Only shows providers that have at least one registered account. Each option shows the count of currently visible agents for that provider in parentheses. ### Mode filter dropdown `[All modes v]` ``` [All modes v] +-----------------+ | All | | execute (3) | | plan (2) | | detail (1) | | verify (1) | | discuss | | refine | | research | | merge | | review | +-----------------+ ``` Static list of all task category values. Modes with active agents show counts; modes with zero agents are dimmed (`text-muted-foreground`) but still selectable. ### Active filter indicator When any filter is non-default, a small `[x Clear]` link appears after the last dropdown to reset all filters at once. This supplements the per-input clear button on search. --- ## Agent Card Anatomy (v2) ### Running agent with task context ``` +----------------------------------+ | (*) blue-fox-7 [...] | | claude . execute | | Task: PKCE flow | <-- clickable, navigates to initiative Execution tab | 2m ago $0.42 12k▼ | <-- cost + input token count +----------------------------------+ ``` ### Running agent with initiative context (no specific task) ``` +----------------------------------+ | (*) blue-fox-7 [...] | | claude . plan | | Init: Auth Overhaul | <-- clickable, navigates to initiative detail | 2m ago $0.18 4k▼ | +----------------------------------+ ``` ### Waiting for input ``` +----------------------------------+ | (?) green-bear-1 [...] | | codex . detail | | Task: DB schema | | 5m ago $0.31 8k▼ | | Answer questions -> | <-- link to /inbox, filtered to this agent +----------------------------------+ ``` ### Exited (completed successfully) ``` +----------------------------------+ | (v) red-elk-3 [...] | | claude . plan | | Init: Auth Overhaul | | 1h ago $1.24 31k▼ | +----------------------------------+ ``` ### Exited (crashed) ``` +----------------------------------+ | (!) pink-owl-9 [...] | | gemini . execute | | Task: API refactor | | 3m ago $0.08 2k▼ | +----------------------------------+ ``` ### Card layout details - Row 1: status dot + agent name (`font-mono text-sm font-medium`) + `[...]` action menu - Row 2: provider badge (`variant="outline" text-xs`) + dot separator + mode badge (`variant="secondary" text-xs`) - Row 3: context row (Task/Init link) — see rules below - Row 4: relative time (`text-xs text-muted-foreground`) left-aligned, cost + tokens right-aligned (`font-mono text-xs text-muted-foreground`) - Row 5 (conditional): "Answer questions ->" for `waiting_for_input` status ### Context row rules - If agent has a `taskId`: show "Task: " as a `` to `/initiatives/$initiativeId?tab=execution` - Else if agent has an `initiativeId`: show "Init: " as a `` to `/initiatives/$initiativeId` - Else: omit the context row entirely (card is 3 rows instead of 4) - Context text: `text-xs text-muted-foreground`, clickable portion gets `hover:underline hover:text-foreground cursor-pointer` - Clicking the context link navigates to the target — it does NOT select the agent card. Use `e.stopPropagation()` on the link click handler. ### Cost + token display - Cost: `$X.XX` format, updates live via subscription data (from `session_end` events) - Tokens: input token count with `▼` suffix (down-arrow = consumed), e.g. `12k▼` - Both use `font-mono text-xs text-muted-foreground` - Hidden when agent has no session data yet (first few seconds after spawn) ### `[...]` Action menu (existing `` component) The three-dot menu renders a `` with context-aware items: ``` [...] +-----------------------+ | Go to Inbox | <-- only for waiting_for_input | ───────────────── | | Stop | <-- only for running / waiting_for_input | ───────────────── | | Dismiss | <-- only for stopped / crashed / idle (not yet dismissed) | Delete | <-- destructive, Shift+click skips confirm dialog +-----------------------+ ``` This matches the existing `AgentActions.tsx` implementation. Delete uses `window.confirm()` with the Shift+click bypass pattern per project conventions. ### Status dot legend | Dot | Color | Token | Animation | Status | |-----|-------|-------|-----------|--------| | `(*)` | Blue | `status-active-dot` | `animate-pulse` (subtle, 2s cycle) | `running` | | `(?)` | Amber | `status-warning-dot` | None (static) | `waiting_for_input` | | `(v)` | Green | `status-success-dot` | None (static) | `completed` | | `(!)` | Red | `status-error-dot` | None (static) | `crashed` | | `(-)` | Grey | `status-neutral-dot` | None (static) | `stopped`, `idle` | The pulsing animation on running agents provides immediate visual scanning — you can spot which agents are active without reading any text. The pulse uses `opacity` animation (0.4 to 1.0) rather than `scale` to avoid layout shifts in the tight card layout. Crashed agents get a distinct red dot (not the same grey as stopped/idle). This was missing in v1 where crashed and stopped were visually identical. --- ## Loading State (5 skeleton cards with shimmer) ``` +=====================================================================================+ | | | Agents [ Refresh ] | | [search___________] [All providers v] [All modes v] | | | | Agent List 360px | Agent Output | | ----------------------------+--------------------------------------------------------| | +---------------------------+ | | | | [.] ░░░░░░░░░░░░ | | | | | ░░░░░ . ░░░░░░ | | | | | ░░░░░░░░░░░░░ | | | | | ░░░░ ░░░ ░░░ | | Select an agent | | +---------------------------+ | to view output | | +---------------------------+ | | | | [.] ░░░░░░░░░░░░ | | | | | ░░░░░ . ░░░░░░ | | | | | ░░░░░░░░░░░░░ | | | | | ░░░░ ░░░ ░░░ | | | | +---------------------------+ | | | +---------------------------+ | | | | [.] ░░░░░░░░░░░░ | | | | | ░░░░░ . ░░░░░░ | | | | | ░░░░░░░░░░░░░ | | | | | ░░░░ ░░░ ░░░ | | | | +---------------------------+ | | +=====================================================================================+ ``` Skeleton cards mirror card anatomy: status dot placeholder, name line, provider/mode line, context line, time + cost line. Shimmer animation sweeps left-to-right on a 1.5s loop. The output viewer right panel shows the "Select an agent" empty state even during loading — it does not show its own skeleton. --- ## Error State ``` +=============================================================================+ | | | Agents [ Refresh ] | | | | | | [AlertCircle] | | Failed to load agents | | | | [ Retry ] | | | +=============================================================================+ ``` - `[AlertCircle]` icon: `h-8 w-8 text-destructive` - Error message: `text-sm text-destructive` - `[ Retry ]` button: `variant="outline" size="sm"`, calls `agentsQuery.refetch()` --- ## Empty State -- No Agents ``` +=============================================================================+ | | | Agents [ Refresh ] | | [search___________] [All providers v] [All modes v] | | | | +-----------------------------------------------------------------------+ | | | | | | | [bot] | | | | No agents yet | | | | Agents appear here when dispatched from initiatives. | | | | | | | +-----------------------------------------------------------------------+ | | | +=============================================================================+ ``` - `[bot]` icon: `Bot` from Lucide, `h-8 w-8 text-muted-foreground` - Shown when the `listAgents` query returns an empty array and no filters are active --- ## Empty State -- Filters Active, No Match ``` +=============================================================================+ | | | Agents [ Refresh ] | | [codex_____________] [claude v] [execute v] | | | | +-----------------------------------------------------------------------+ | | | | | | | No matching agents | | | | Try a different search or filter. | | | | | | | | [Clear filters] | | | | | | | +-----------------------------------------------------------------------+ | | | +=============================================================================+ ``` - "Clear filters" is a text link (`text-primary hover:underline cursor-pointer`) - Resets search input to empty, provider filter to "All", mode filter to "All" --- ## Output Viewer (Terminal Panel) The right panel is a **terminal emulator** — it must look and feel like a real terminal, not a log viewer. This is where operators spend 80% of their time on this page. ### Terminal header ``` +----------------------------------------------------------------------+ | blue-fox-7 [RUNNING] Session 2/2 $0.42 12k▼ [Stop] | +----------------------------------------------------------------------+ ``` - Agent name: `font-mono text-sm font-medium text-terminal-fg` - Status badge: uses status token colors (blue/green/amber/red/grey) - Session indicator: "Session N/M" where M = total sessions (for resumed agents) - Cost: cumulative `$X.XX` from all `session_end` events - Token count: cumulative input tokens with `▼` suffix - `[Stop]` button: `variant="destructive" size="sm"`, only shown for `running` / `waiting_for_input` ### Terminal body ``` +----------------------------------------------------------------------+ | bg: --terminal-bg font-mono | | | | ── Session 1 ─────────────────────────────────────────────────── | | [System] Starting task: PKCE flow | | | | > I'll examine the existing auth code to understand the current | | > structure before implementing PKCE. | | | | | [Read] src/auth/index.ts | | | import { Router } from 'express'; | | | export function setupAuth(app: Application) { ... | | | | | [Write] src/auth/pkce.ts | | | 42 lines written | | | | ── Session 2 ─────────────────────────────────────────────────── | | [System] Resumed — continuing PKCE implementation | | | | > Now I'll add the verification endpoint... | | | +----------------------------------------------------------------------+ ``` ### Terminal styling rules - **Font**: `font-mono` (Geist Mono) everywhere — no sans-serif text inside the terminal - **Background**: `bg-terminal` — always dark, even in light mode (terminal aesthetic) - **Text colors**: use `--terminal-*` tokens from theme.md, NOT raw Tailwind colors - **Agent text**: `text-terminal-fg` (green-on-dark) - **Tool calls**: left border `border-terminal-tool` (blue), tool name in `[ToolName]` badge - **Tool results**: left border `border-terminal-result` (green), collapsible if > 10 lines - **Errors**: left border `border-terminal-error` (red), `bg-red-900/10` tint - **System messages**: `text-terminal-system` (dimmed), `[System]` prefix badge - **Session dividers**: horizontal rule with "Session N" label, `border-terminal-border` - **Whitespace**: `whitespace-pre-wrap` for agent text, `overflow-x-auto` for tool output ### Scroll behavior - **Auto-scroll**: enabled by default. Terminal auto-scrolls to bottom on each new chunk. - **User scroll-up**: when user scrolls up more than 50px from bottom, auto-scroll pauses. A floating `[v Jump to bottom]` button appears at the bottom-right of the terminal. - **Scroll indicator**: bottom bar shows `(*) Following` (green dot, auto-scrolling active) or `(-) Paused` (grey dot, user scrolled up). Clicking toggles. - **Keyboard**: `End` key scrolls to bottom and re-enables auto-follow. ### Terminal footer bar ``` +----------------------------------------------------------------------+ | (*) Following [Cmd+F search...] [v] | +----------------------------------------------------------------------+ ``` - Left: follow status indicator (clickable toggle) - Right: output search field + scroll-to-bottom button - `Cmd+F` / `Ctrl+F` within the terminal panel opens an inline search bar (NOT the browser's native find — the terminal content is a virtual scroll, native find would miss off-screen content) - Search highlights matches in `bg-terminal-selection/20` with the current match in `bg-terminal-selection/40` - `Enter` / `Shift+Enter` navigate between matches - `Esc` closes the search bar ### No agent selected (empty state) ``` +----------------------------------------------------------------------+ | | | | | [Terminal] | | Select an agent | | to view its output | | | | (or press J/K to | | navigate the list) | | | +----------------------------------------------------------------------+ ``` - `[Terminal]` icon: `Terminal` from Lucide, `h-8 w-8 text-terminal-muted` - Background: `bg-terminal` (dark surface even in light mode) - Text: `text-terminal-muted` --- ## Keyboard Shortcuts | Key | Action | |-----|--------| | `J` / `Down` | Select next agent in list (when list is focused) | | `K` / `Up` | Select previous agent in list | | `/` | Focus search input | | `Esc` | Clear search / close terminal search | | `Cmd+F` | Open terminal output search (when output viewer is focused) | | `End` | Scroll terminal to bottom, re-enable auto-follow | These shortcuts are only active when no text input is focused (except `Cmd+F` which is always available within the terminal panel). --- ## Agent Lifecycle Visibility Agents do NOT disappear from the list when they exit. The existing v1 filter tabs (All / Running / Questions / Exited / Dismissed) handle lifecycle: | Status | Visible in "All" | Visible in filter | Styling | |--------|-------------------|-------------------|---------| | `running` | Yes | "Running" | Normal card, pulsing blue dot | | `waiting_for_input` | Yes | "Questions" | Normal card, amber dot, "Answer questions ->" CTA | | `completed` | Yes | "Exited" | Slightly dimmed (`opacity-80`), green dot | | `stopped` | Yes | "Exited" | Slightly dimmed, grey dot | | `crashed` | Yes | "Exited" | Slightly dimmed, red dot, red left-border accent | | `idle` | Yes | "Exited" | Slightly dimmed, grey dot | | Any + `userDismissedAt` | No | "Dismissed" | Full opacity but separated from active view | "All" filter excludes dismissed agents (matching current implementation). Crashed agents get a subtle `border-l-2 border-status-error-dot` left accent on the card to draw attention without being overbearing. --- ## Source - `packages/web/src/routes/agents.tsx` - `packages/web/src/components/AgentOutputViewer.tsx` - `packages/web/src/components/AgentActions.tsx` - `packages/web/src/components/StatusDot.tsx` - `packages/web/src/lib/parse-agent-output.ts` --- ## Design Review Notes Findings from a design review against the mission-control aesthetic and the existing implementation in `agents.tsx` / `AgentOutputViewer.tsx`. ### 1. Agent list width: 320px was too tight (FIXED) Bumped to 360px. The original 320px was fine for name + status dot, but the v2 card now has 4-5 rows: status+name+menu, provider+mode badges, context row, time+cost. At 320px the `[...]` menu was colliding with the provider badge on shorter names. 360px gives breathing room without eating too much from the output viewer. The implementation already uses `lg:grid-cols-[320px_1fr]` — a single value change. ### 2. Output viewer was massively underspecified (FIXED) The original wireframe showed 4 lines of `> text` and nothing else. For a screen where operators spend most of their time, that is unacceptable. Added: - **Terminal header** with agent name, status badge, session counter, cost, tokens - **Terminal body** with explicit styling rules referencing `--terminal-*` tokens from theme.md (the tokens existed but the wireframe never referenced them) - **Terminal footer** with follow indicator, inline search, scroll-to-bottom - **Scroll behavior** spec (auto-scroll, pause on scroll-up, 50px threshold) — the implementation already has this logic but the wireframe didn't document it - **Session dividers** — the implementation supports multi-session agents but the wireframe showed only "Session 1" with no visual separator ### 3. Agent actions were invisible in the wireframe (FIXED) The `[...]` three-dot menu existed in the ASCII art but was never documented. The implementation (`AgentActions.tsx`) has a full dropdown with Stop/Dismiss/Delete and Go to Inbox. Added explicit documentation of the menu items and their visibility rules, including the Shift+click bypass pattern for Delete. ### 4. Status dots were too ambiguous (FIXED) `*` `?` `-` as ASCII symbols mapped to 3 states, but the codebase has 5 distinct agent statuses. The original spec collapsed `completed`, `stopped`, `crashed`, and `idle` into one grey `-`. That is wrong — a crashed agent demands attention, a completed agent is a success signal. Now: - `running` = pulsing blue dot (animation for scannability) - `waiting_for_input` = static amber dot - `completed` = static green dot (success signal) - `crashed` = static red dot (error signal) - `stopped` / `idle` = static grey dot The existing `StatusDot.tsx` already distinguishes `running` (blue), `crashed` (red), `stopped` (grey). The wireframe was behind the implementation. ### 5. Missing cost/token indicators (FIXED) For 10+ agents running in parallel, operators need to know which agents are burning through tokens and which are idle. Added `$X.XX` cost and `Nk▼` token indicators to both the card (row 4) and the output viewer header. Data comes from `session_end` parsed messages which already include `meta.cost`. ### 6. Context row links were not actionable (FIXED) The original spec said "clickable link to task/initiative" but didn't specify the navigation target. Now explicit: Task links go to `/initiatives/$initiativeId?tab=execution`, Initiative links go to `/initiatives/$initiativeId`. Also added `e.stopPropagation()` note so clicking the link doesn't simultaneously select the card. ### 7. Search only matched agent name (FIXED) Searching for "PKCE" should find the agent working on the PKCE task. Extended search to also match task name and initiative name from the context row. ### 8. No keyboard navigation (FIXED) Mission control = keyboard-first. Added J/K for list navigation, `/` for search focus, `Cmd+F` for terminal search, `End` for scroll-to-bottom. These match conventions from tools operators already know (vim, less, tmux). ### 9. Missing terminal search (FIXED) When an agent has been running for 30 minutes and produced 2000 lines of output, you need to search it. Added `Cmd+F` inline search in the terminal footer with match highlighting using `--terminal-selection` token. Browser native find would fail because the terminal should eventually use virtualized scrolling for performance. ### 10. Agent lifecycle visibility was unspecified (FIXED) The wireframe never addressed what happens to exited agents. The implementation has a 5-tab filter system (All/Running/Questions/Exited/Dismissed) that the wireframe completely ignored. Documented the full lifecycle table including styling differences (dimmed opacity for exited, red accent for crashed). ### Still not addressed (future work) These items were considered but intentionally deferred: - **Split-view for comparing two agents' output**: High complexity, low frequency. Most comparisons happen via task results, not raw output. If needed, operators can open two browser tabs. Revisit if user feedback demands it. - **CPU/memory resource indicators**: Requires a monitoring sidecar or proc polling that doesn't exist in the backend. Token count + cost is the more relevant resource metric for AI agents. System resource monitoring belongs in infrastructure tooling, not the orchestrator UI. - **Agent timeline / Gantt view**: Valuable for post-mortem analysis of parallel execution. Deferred to v3 as it requires a new visualization component and historical data aggregation that the current `agent_log_chunks` table doesn't efficiently support.