Admin — Dashboard
The Dashboard is the operator’s day-one landing page. It’s designed to answer one question: “Is the tenant healthy right now?” — not to replace the drill-in pages for threads, agents, or analytics.
Route: /dashboard (_authed/_tenant/dashboard)
What it shows
Section titled “What it shows”The layout is simple: five metric cards across the top, then two paginated lists (Recent Activity, Recent Threads) in a two-column grid on larger screens.
Metric cards
Section titled “Metric cards”| Card | What it shows |
|---|---|
| Agents Online | Count of agents currently in IDLE or BUSY status |
| Open Threads | Count of threads not in DONE or CANCELLED |
| Recent Activity | Count of runs and thread updates in the last 24 hours |
| Spend (MTD) | Month-to-date spend from the cost store |
| Cost / Event | Average cost per turn event over the period |
Cost metrics come from the same useCostStore (Zustand) that the Analytics page uses, so the dashboard does not re-fetch cost data on every navigation — it reads from the already-hydrated store.
Recent Activity list
Section titled “Recent Activity list”A unified feed of recent runs and thread updates, merged by timestamp and paginated client-side at 10 items per page. Each row shows:
- An icon for run vs thread update
- The agent name (for runs) or thread title (for thread updates)
- A status badge (
running,succeeded,failedfor runs; thread status colors for thread updates) - Relative timestamp
Clicking a run row opens the RunDetailDialog — a modal showing the run’s execution details and the final response markdown. Clicking a thread row opens the ThreadDetailDialog — a modal showing thread metadata, description, and recent comments.
Recent Threads list
Section titled “Recent Threads list”A second paginated list, 10 per page, showing the tenant’s most recently-touched threads. This is intentionally narrower than the full Threads page: it exists so the operator can quickly glance at what’s been happening without leaving the dashboard.
Where the data comes from
Section titled “Where the data comes from”The dashboard fires four GraphQL queries on mount:
AgentsListQuery— populates the Agents Online metricThreadsListQuery— powers Recent Threads and half of the activity feedThreadTurnsQuery— powers the run side of the activity feedThreadDetailQuery— lazy-loaded when a thread is clicked into the drill-in dialog
Cost metrics come from the useCostStore hook which internally fetches /api/cost (a REST endpoint) the first time it’s used in the session.
Pagination and refresh behavior
Section titled “Pagination and refresh behavior”Both lists paginate client-side using a custom Pager component. Pagination state is per-list — flipping to page 2 of activity does not affect threads pagination.
Refreshes are push-driven, not polled:
- Any thread-turn event fires
OnThreadTurnUpdatedSubscription, which re-executesThreadTurnsQueryand the activity merge rebuilds - Any thread change fires
OnThreadUpdatedSubscription, which re-executesThreadsListQueryand Recent Threads rebuilds
The dashboard is always within a few seconds of reality. Operators do not need to pull to refresh.
Known limits
Section titled “Known limits”- No dedicated activity-feed query. Client-side merge of runs and threads is the current implementation. A real feed query would let the dashboard show more than just the intersection of runs + thread updates.
- Cost metrics can be undefined on first render. The
useCostStorehydrates asynchronously; components render?? 0placeholders until it lands. This flashes zeros for a tick on cold load. - No per-metric drill-in. Clicking “Agents Online” does not navigate anywhere. The drill-in for agents is Agents; for threads it’s Threads; for spend it’s Analytics.
Related pages
Section titled “Related pages”- Threads — the full thread list and detail views
- Agents — the agent roster
- Analytics — deeper views of activity, cost, and performance
- Authentication & Tenancy — the layout the dashboard renders inside