feat(health): System Health in three groups, a score that sees lag; admin user detail

Dashboard → System Health is grouped into AgentHub, Machine and Agent
activity. The machine (GET /api/host, every signed-in user) is drawn by
components/health/HostHealth.tsx: host card beside the CPU/memory history,
then CPU (threads as compact cells + temperatures/GPUs), disks & network,
busiest processes, and per-user tiles. Concurrency bars each show their own
agent type on hover.

The Health Score (client/src/lib/healthScore.ts) is now 100 minus what each
check costs, between an amber and a red threshold: event-loop p99
(server/lib/loop-delay.js, shared with /api/metrics), Linux PSI pressure for
CPU, memory and disk I/O (/api/host `pressure`; load per core without PSI),
RAM, the database's disk, V8 heap, hooks silent while agents work, and agent
errors in the last 24 h. All-time success rate, API errors and the transcript
cache hit rate no longer count. /api/settings/info adds server.event_loop and
activity {last_event_at, working_agents, agents_24h}.

Administration: a user row opens its detail in place (GET
/api/users/:id/details — spend, account pool share, live panes, workspaces,
newest sessions); the server panel is gone, member tmux folded into the table;
the Sessions list shows each row's owner in server mode.
This commit is contained in:
2026-10-27 16:39:38 +07:00
parent df15789982
commit 1c6dfea5b5
49 changed files with 5154 additions and 1971 deletions
+99 -2
View File
@@ -71,7 +71,7 @@ tags:
- name: Artifacts
description: 'What agents produced, shared as a SNAPSHOT: a claude.ai page link, a pinned or uploaded file, a skill or a plugin. A viewer reads only the snapshot copy (sandbox CSP, nosniff); an install writes into the caller''s own account only. Every write is same-origin.'
- name: Host
description: 'Administration → Server: the machine''s hardware, load, disks and each user''s share of CPU and memory. Read-only, staff only in server mode.'
description: 'Dashboard → System Health: the machine''s hardware, load, disks and each user''s share of CPU and memory. Read-only, every signed-in user.'
- name: Locks
description: Cross-lane named locks (server/lib/named-lock.js). Single-shot — the server never waits for a lock; `agenthub lock acquire` owns the polling loop.
- name: Projects
@@ -262,6 +262,21 @@ components:
agent_count:
type: integer
nullable: true
owner_id:
type: integer
nullable: true
description: 'Server mode, list only: the account owning the session''s workspace (its lane, else the lane of a dashboard run holding it); null when none. Absent in local mode.'
owner_name:
type: string
nullable: true
description: That owner's username.
owner_display_name:
type: string
nullable: true
owner_avatar_at:
type: string
format: date-time
nullable: true
last_activity:
type: string
format: date-time
@@ -1578,6 +1593,32 @@ components:
type: string
ws_connections:
type: integer
event_loop:
type: object
description: Event-loop delay over the last minute in ms (p50, p99, max) — how long requests, hooks and terminal echo waited.
properties:
p50:
type: number
p99:
type: number
max:
type: number
activity:
type: object
description: 'For the System Health score: when the newest event (hook) landed, agents working now, and how the agents that ended in the last 24 h ended (scoped to the caller''s rows).'
properties:
last_event_at:
type: string
nullable: true
working_agents:
type: integer
agents_24h:
type: object
properties:
completed:
type: integer
error:
type: integer
transcript_cache:
type: object
required:
@@ -15014,6 +15055,38 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/users/{id}/details:
get:
tags:
- Auth
operationId: getServerUserDetails
summary: 'One account in full for the Administration page (admin/analyst): sign-ins, session counts, spend today/7d/30d, per local day (`tz_offset`), per AI tool and per model, its workspaces and live panes, newest sessions, the audit entries naming it, and per pool tool its pin, group and the accounts it may run on — each account''s quota beside this user''s own 5 h / 7 d share. Never a token'
parameters:
- name: id
in: path
required: true
schema:
type: integer
- name: tz_offset
in: query
schema:
type: integer
description: The browser's getTimezoneOffset(), for the daily buckets
responses:
'200':
description: Account details
'403':
description: EADMIN — a developer
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: ENOUSER
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/users/{id}:
get:
tags:
@@ -17749,13 +17822,37 @@ paths:
description: 'Cached 3 s. `usage` is null off Linux. Server mode: admin/analyst only (403 EADMIN for a developer).'
responses:
'200':
description: '`{at, hostname, os, uptime, cpu, memory, disks, homesMount, gpus, usage}` — see docs/API.md.'
description: '`{at, hostname, os, uptime, bootTime, cpu (+physicalCores, perCore), memory, disks, homesMount, gpus, usage, network, diskIo, temperatures, pressure, topProcesses, process}` — see docs/API.md.'
content:
application/json:
schema:
type: object
'500':
description: EHOST
/api/host/history:
get:
tags:
- Host
summary: The machine's CPU/memory history since the server started
operationId: getHostHistory
description: 'In memory only: one point every 30 s for 24 h, kept by a server-mode server on Linux (empty otherwise, `running: false`). Averaged down to at most 360 points. Server mode: admin/analyst only.'
parameters:
- name: range
in: query
schema:
type: string
enum:
- 1h
- 6h
- 24h
default: 1h
responses:
'200':
description: '`{range, intervalMs, running, startedAt, points: [{at, cpu, memory, memoryUsed, swapUsed, load1, users: {<userId>: {cpu, memory}}}]}`.'
content:
application/json:
schema:
type: object
/api/lanes:
get:
tags: