SideButton Dashboard Knowledge Module
Agents — SideButton Dashboard Knowledge Module
The Agents page manages autonomous AI agent jobs that run in terminal processes. Users can start new agents by selecting a role and providing a prompt, monitor running agents with real-time metrics (…
sidebutton install sidebutton.local What This Is
The Agents page manages autonomous AI agent jobs that run in terminal processes. Users can start new agents by selecting a role and providing a prompt, monitor running agents with real-time metrics (actions, tokens, cost), view completed agent results, stop running agents, and re-queue completed agents. Agents are autonomous workflows that pick up issues and work on them (SE, QA, PM roles). The page splits into two live sections: "Running ({count})" and "Completed Today ({count})". A waiting (queued) state exists between start and active execution.
URL Patterns
| View | URL | Notes |
|---|---|---|
| Agents List | /agents | Shows running + completed agents |
| Agent Detail | /agents/{run_id} | Single agent run log detail (navigated via navigateToRunLogDetail(agent.run_id)) |
Page Structure
+--[Header: "Agents" + Refresh icon + "+ Start Agent" button]--+
| |
| === Running ({count}) === |
| [Agent Card (.agent-card.waiting)] ← queued, not started |
| Status dot (.status-dot.waiting, amber) |
| Title (agent.workflow_title) + role badge (UPPERCASED) |
| "Queued {time}" |
| Prompt (.agent-prompt block, if initial_prompt present) |
| [View Log] [Stop] buttons |
| |
| [Agent Card (.agent-card.running)] ← actively executing |
| Status dot (.status-dot.running, green pulse) |
| Title (agent.workflow_title) + role badge (UPPERCASED) |
| Started time + duration |
| Prompt (.agent-prompt block, if initial_prompt present) |
| Metrics (.agent-metrics): Actions | Tokens | Cost |
| Current task (.agent-output, monospace #1e1e2e bg) |
| [View Log] [Stop] buttons |
| |
| === Completed Today ({count}) === |
| [Agent Card (.agent-card)] |
| Status dot (.status-dot.success or .status-dot.failed) |
| Title (agent.workflow_title) + role badge (UPPERCASED) |
| Duration + completion time |
| Result (.agent-result): "Result: ..." or "Error: ..." |
| [View Log] [Resend / Resending...] buttons |
| |
| [Empty State (.empty-card): "No Agents Running"] |
| Description + "+ Start Agent" button |
| |
| [Loading (.loading div): "Loading agents..."] |
| |
+--[Start Agent Modal]------------------------------------+
| .modal-overlay (click to close) |
| .modal (click stops propagation) |
| "Start Agent" heading |
| Role: native <select> with role options |
| Prompt: textarea with placeholder |
| [Cancel] [Start Agent / Starting...] buttons |
+---------------------------------------------------------+
Key Elements
Header
| Element | Selector | Notes |
|---|---|---|
| Page title | heading "Agents" | H1 in banner |
| Refresh button | button "Refresh agents" (title attr) | Icon button; disabled + SVG spins while isRefreshing. Reloads agents list |
| Start Agent button | button "+ Start Agent" | Opens Start Agent modal |
Waiting Agent Card (queued, not yet started)
| Element | Selector | Notes |
|---|---|---|
| Card container | .agent-card.waiting | Amber left border: border-left: 3px solid var(--color-warning, #f59e0b) |
| Status dot | .status-dot.waiting | Solid amber — not animated |
| Agent title | .agent-title span | Sourced from agent.workflow_title |
| Role badge | .role-badge | Uppercased role slug (e.g., SE, SOFTWARE-ENGINEER) |
| Queued time | text "Queued {time}" | Shown instead of "Started" for waiting agents |
| Prompt block | .agent-prompt | Shows agent.initial_prompt if present |
| View Log button | button "View Log" | Available on waiting agents |
| Stop button | button "Stop" | Available on waiting agents |
Running Agent Card
| Element | Selector | Notes |
|---|---|---|
| Card container | .agent-card.running | Green left border: border-left: 3px solid var(--color-success) |
| Status dot | .status-dot.running | Animated green pulse — indicates agent is active |
| Agent title | .agent-title span | Sourced from agent.workflow_title |
| Role badge | .role-badge | Uppercased role slug — agent.role.toUpperCase() (e.g., SOFTWARE-ENGINEER, QA-AGENT-—-UNIVERSAL-WEB-APP-TESTING) |
| Prompt block | .agent-prompt | Shows agent.initial_prompt — conditionally rendered, omitted if field absent |
| Metrics row | .agent-metrics | Three spans: "Actions: {count}", "Tokens: {formatted}", "Cost: ${amount}" |
| Current task output | .agent-output | Monospace font, dark background #1e1e2e |
| View Log button | button "View Log" | Calls navigateToRunLogDetail(agent.run_id) |
| Stop button | button "Stop" | Triggers confirm("Stop this agent?") then POST /api/agents/{run_id}/stop |
Completed Agent Card
| Element | Selector | Notes |
|---|---|---|
| Card container | .agent-card | No extra class for base completed state |
| Failed card | .agent-card.failed | Red left border: border-left: 3px solid var(--color-error) |
| Status dot (success) | .status-dot.success | Solid green — not animated |
| Status dot (failed) | .status-dot.failed | Solid red |
| Agent title | .agent-title span | Sourced from agent.workflow_title |
| Role badge | .role-badge | Uppercased role slug (same as running card) |
| Result block | .agent-result | Prefixed with "Result:" (success) or "Error:" (failure). Sourced from agent.result_summary. Conditionally rendered |
| View Log button | button "View Log" | Calls navigateToRunLogDetail(agent.run_id) |
| Resend button | button "Resend" | Re-queues completed/failed agent. Shows "Resending..." while in progress (disabled). Toast "Job re-queued" on success |
Start Agent Modal
| Element | Selector | Notes |
|---|---|---|
| Overlay | .modal-overlay | Click anywhere on overlay closes modal (showStartModal = false) |
| Modal container | .modal | Click inside stops propagation — overlay click does not bleed through |
| Modal heading | heading "Start Agent" | Modal title |
| Role label | label "Role" | Above dropdown |
| Role dropdown | native <select> (combobox) | Options sourced from enabled roles (enabled !== false). Option value: role.name.toLowerCase().replace(/\s+/g, '-') — em-dashes in names are preserved, not stripped. Default selectedRole = 'se' but renders blank if no enabled role has slug 'se' |
| Prompt label | label "Prompt" | Above textarea |
| Prompt textarea | textbox with placeholder | Placeholder: "e.g., Pick issue SCRUM-142 from backlog, implement fix..." |
| Cancel button | button "Cancel" | Closes modal |
| Start Agent button | button "Start Agent" | Default state. Submits POST /api/agents/start |
| Starting button | button "Starting..." | Shown during isStarting state — button is disabled |
Empty State
| Element | Selector | Notes |
|---|---|---|
| Empty card | .empty-card | Shown when no running or completed agents |
| Empty heading | heading "No Agents Running" | H3, centered |
| Description | "Agents are autonomous workflows..." | Subtitle text |
| Start Agent button | button "+ Start Agent" | Duplicate CTA inside the empty card |
Loading State
| Element | Selector | Notes |
|---|---|---|
| Loading container | .loading | Centered div |
| Loading text | "Loading agents..." | Visible during initial data fetch |
Data Model
Agent Job (from API response)
| Field | Type | Notes |
|---|---|---|
run_id | string | Primary identifier — used as the agent key and for navigation/stop calls |
workflow_title | string | Display title shown in the card heading |
role | string | Role identifier (e.g., "se", "qa-engineer") |
initial_prompt | string | User-provided task description (shown in .agent-prompt block) |
status | enum | waiting, running, completed, failed, stopped |
started_at | datetime | Job start time (ISO string) |
completed_at | datetime | Job end time — null while running |
duration_ms | number | Elapsed milliseconds (used for duration display) |
metrics.action_count | number | Count of actions taken (running agents only) |
metrics.token_count | number | Total tokens consumed (displayed formatted, e.g., "12.5K") |
metrics.cost_estimate | number | Estimated cost (displayed as "$X.XX") |
current_task | string | Latest terminal output (present for running agents) |
result_summary | string | Final result message (for completed agents — displayed as "Result: ...") |
Role Object (from getRoles() API)
| Field | Type | Notes |
|---|---|---|
name | string | Display name (e.g., "Software Engineer") |
enabled | boolean | Roles with enabled === false are excluded from the dropdown |
Role dropdown <option> value is derived: role.name.toLowerCase().replace(/\s+/g, '-')
(e.g., "Software Engineer" → "software-engineer", "PM — Portal Chat" → "pm-—-portal-chat")
Note: only whitespace is replaced — em-dashes (—) are preserved in the value slug.
States
| State | Trigger | Visual Indicator |
|---|---|---|
| Loading | Initial page load | .loading div with "Loading agents..." |
| Empty | No running or completed agents | .empty-card with "No Agents Running" heading + CTA button |
| Has running agents | running.length > 0 | "Running ({count})" section visible |
| Has completed agents | completed.length > 0 | "Completed Today ({count})" section visible |
| Waiting agent | Agent status is waiting | .agent-card.waiting (amber border), .status-dot.waiting (amber solid), "Queued {time}" |
| Running agent | Agent status is running | .agent-card.running (green border), .status-dot.running (green pulse), metrics visible |
| Completed agent (success) | Agent status is completed | .agent-card, .status-dot.success (solid green), "Result:" prefix in result block |
| Failed agent | Agent status is failed | .agent-card.failed (red border), .status-dot.failed (solid red), "Error:" prefix |
| Modal open | Click "+ Start Agent" | [role="dialog"] visible (.modal-overlay > .modal) with role + prompt form |
| isStarting | Click "Start Agent" in modal | Button text changes to "Starting..." and is disabled |
| Resending | Click "Resend" on completed card | Button shows "Resending..." and is disabled |
| Refreshing | Click Refresh icon | SVG icon gains .spinning class, button is disabled |
| Polling | Always on mount | Background setInterval at 5000ms — unconditional, runs regardless of running count |
Common Tasks
- Start an agent: Click "+ Start Agent" → select Role from dropdown (explicitly — default may be blank) → enter Prompt → click "Start Agent" (button becomes "Starting..." while submitting)
- Monitor running agent: View
.agent-card.running— shows live metrics in.agent-metricsand current terminal output in.agent-output. Agent may show.waitingstate first - Stop running/waiting agent: Click "Stop" → confirm native dialog "Stop this agent?" → toast "Agent stopped" → agent moves to Completed section
- View agent run log: Click "View Log" → navigates to run log detail for
agent.run_id(available on running and completed agents) - Check completed agents: Scroll to "Completed Today ({count})" section — shows result or error for each finished agent
- Read agent result: Look for result block — "Result: ..." for success (
result_summary), "Error: ..." for failure - Re-queue completed agent: Click "Resend" on any completed card → toast "Job re-queued" → agent re-appears in Running section
- Manually refresh: Click Refresh icon button → page data reloads immediately (outside the 5s poll cycle)
Tips
- Auto-polls every 5 seconds unconditionally: Previously gated on
running.length > 0but now polls always — page is never fully static - Role dropdown uses derived values: Option value is
role.name.toLowerCase().replace(/\s+/g, '-'). Em-dashes in role names are preserved (e.g.,pm-—-portal-chat). To select "Software Engineer", use value"software-engineer" - Default role may render blank:
selectedRoleinitializes to'se'in code, but if no enabled role has slug'se', the native<select>renders blank — always explicitly select a role before submitting - Metrics only visible during
runningstatus:.agent-metricsblock is conditionally rendered — absent forwaitingagents - Role badge is always UPPERCASED:
agent.role.toUpperCase()— the badge never shows lowercase - Metrics are formatted on display:
token_countrenders as "12.5K";cost_estimaterenders as "$0.04";$0shown if no cost - Section headings include live count: "Running (3)" and "Completed Today (5)" — count updates with each poll
- ESC key closes modal:
press_key('Escape')on the modal overlay closes it
Gotchas
- Agent identifier is
run_id, notid: All API calls (stop, view log) useagent.run_id. Do not useagent.id - Title field is
workflow_title: The card heading comes fromagent.workflow_title— not a generic "name" or "title" field - Metrics are nested:
agent.metrics.action_count,agent.metrics.token_count,agent.metrics.cost_estimate— not flat top-level fields - Role dropdown is native
<select>: Useselect_option()for automation — click-based interaction does not work on native selects - Modal closes on backdrop click OR ESC: Click
.modal-overlay(outside the white box) or press Escape. Clicking inside.modalstops propagation and does NOT close the modal. No X button - Stop uses
confirm():confirm("Stop this agent?")— native browser dialog. Cannot be intercepted by snapshot/click. Needs browser dialog event handling (Known Blocker) - Stop is irreversible: Killing the agent terminates the terminal process — cannot resume. Use Resend to re-queue from scratch
waitingis a real state, not a loading state: Agents can sit inwaitingfor several seconds before transitioning torunning. Test for this intermediate state — do not assume green dot immediately after startresult_summarynotresult: The data field for completed agent output isresult_summary. The fielderrordoes NOT exist separately — failures also useresult_summarywith "Error:" prefixduration_msnotduration: Duration field is in milliseconds. Formatted display usesformatDuration(started_at, duration_ms)- Completed agents are today-only: Historical agent jobs beyond today's date are not shown
- Polling stops when idle: If the last running agent finishes, the 5s interval is cleared and no further background requests are made until a new agent is started
isStartinglocks the button: During submission, the "Start Agent" button shows "Starting..." and is disabled — prevents double-submit- SPA 404 on direct navigation:
/agentsreturns 404 JSON if accessed directly via address bar — must navigate via the in-app SPA router