docs: UI-audit fixes in CLAUDE.md, API, OpenAPI, LANES and READMEs
New params and errors (agents session_ids, sessions awaiting/from/to, stats tool counts, event labels, cleanup dry_run/INVALID_PURGE_DAYS, lane start slot auto/ESESSIONLIVE/EUNSUPPORTED), terminal and pane shortcuts, the shared UI primitives, accent vs accent-solid, lazy routes and the Settings groups. openapi.yaml regenerated.
This commit is contained in:
+4
-4
@@ -448,17 +448,17 @@ The OpenAPI spec is generated from `server/openapi.js` (`createOpenApiSpec()`),
|
||||
| Method | Path | Description |
|
||||
| ------- | ------------------- | ------------------------------------------------ |
|
||||
| `GET` | `/api/health` | Server health check (`status`, `version`, `timestamp`) |
|
||||
| `GET` | `/api/sessions` | List sessions (`status`, `limit`, `offset`; `tool=claude\|codex\|antigravity` filters by CLI before the limit) |
|
||||
| `GET` | `/api/sessions` | List sessions (`status`, `limit`, `offset`; `tool=claude\|codex\|antigravity`, `awaiting=1` and `from`/`to` on `started_at` filter before the limit) |
|
||||
| `GET` | `/api/sessions/:id` | Session detail (includes `agents` + `events`) |
|
||||
| `POST` | `/api/sessions` | Create session (idempotent by `id`) |
|
||||
| `PATCH` | `/api/sessions/:id` | Update session |
|
||||
| `GET` | `/api/sessions/:id/transcripts` | List the session's transcript files (main + sub-agents) |
|
||||
| `GET` | `/api/sessions/:id/transcript` | Cursor-paginated message stream for one transcript |
|
||||
| `GET` | `/api/agents` | List agents (`status`, `session_id`, pagination)|
|
||||
| `GET` | `/api/agents` | List agents (`status`, `session_id`, pagination; `session_ids=a,b` — ≤ 200 — wins over the rest)|
|
||||
| `GET` | `/api/agents/:id` | Agent detail |
|
||||
| `POST` | `/api/agents` | Create agent (idempotent by `id`) |
|
||||
| `PATCH` | `/api/agents/:id` | Update agent |
|
||||
| `GET` | `/api/events` | List events (`session_id`, `limit`, `offset`) |
|
||||
| `GET` | `/api/events` | List events (`session_id`, `limit`, `offset`); each row also carries `session_name`/`session_cwd`/`session_lane_id`/`agent_name`/`agent_subagent_type` (REST only, `new_event` unchanged) |
|
||||
| `GET` | `/api/stats` | Dashboard aggregate counters |
|
||||
| `GET` | `/api/analytics` | Analytics aggregates for charts/trends |
|
||||
| `GET` | `/api/metrics` | Prometheus / OpenMetrics exposition (text; v0.0.4) |
|
||||
@@ -693,7 +693,7 @@ Because sync runs non-interactively (`ssh -o BatchMode=yes`), the connection mus
|
||||
| `DELETE` | `/api/settings/antigravity-hooks` | Remove the `agenthub` hook only (`sameOriginGuard`) |
|
||||
| `GET` | `/api/settings/export` | Export all data (sessions, agents, events, token_usage, workflows, dashboard_runs, alert_rules, model_pricing) as one versioned JSON attachment |
|
||||
| `POST` | `/api/settings/import` | Restore a bundle from `/export`. Multipart `file`, or JSON `{ path }` (server reads it). Idempotent + non-destructive: sessions already present are skipped whole |
|
||||
| `POST` | `/api/settings/cleanup` | Abandon stale sessions and purge old data |
|
||||
| `POST` | `/api/settings/cleanup` | Abandon stale sessions and purge old data (`purge_days` whole ≥ 1, else `400 INVALID_PURGE_DAYS`; `dry_run: true` counts only) |
|
||||
|
||||
### Claude Config Explorer (`/api/cc-config`)
|
||||
|
||||
|
||||
@@ -55,7 +55,12 @@ const paths = {
|
||||
"ignored rather than rejected. `limit` is clamped to 1–500 (default 50) and " +
|
||||
"`offset` is clamped to >= 0 (default 0).\n\n" +
|
||||
"Note: each returned event's `data` field is a **JSON-encoded string**, not a " +
|
||||
"nested object — callers must `JSON.parse` it to inspect the payload.",
|
||||
"nested object — callers must `JSON.parse` it to inspect the payload.\n\n" +
|
||||
"Each row also carries its labels, so a list page need not fetch every session " +
|
||||
"and agent to name them: `session_name`, `session_cwd`, `session_lane_id`, " +
|
||||
"`agent_name` and `agent_subagent_type` (nullable; `null` when the session or " +
|
||||
"agent row is missing). They are REST-only — the WebSocket `new_event` frame " +
|
||||
"is unchanged.",
|
||||
parameters: [
|
||||
{
|
||||
in: "query",
|
||||
|
||||
@@ -1205,7 +1205,7 @@ const paths = {
|
||||
tags: ["Settings"],
|
||||
summary: "Abandon stale sessions and optionally purge old history",
|
||||
description:
|
||||
"Two-phase maintenance. Phase 1 (`abandon_hours`, non-destructive): marks any still-`active` session with no events newer than that many hours as `abandoned`, and completes its lingering agents — a tidy-up of crashed/orphaned sessions. Phase 2 (`purge_days`) is ⚠ DESTRUCTIVE and IRREVERSIBLE: it permanently DELETES completed/error/abandoned sessions (and their events, agents, and token_usage) whose `started_at` is older than that many days. Active sessions are NEVER purged. Both fields are optional and independent — send only `abandon_hours` for a safe tidy-up, or include `purge_days` to also reclaim disk. Export first if the purged history matters. The response reports counts for each phase.",
|
||||
"Two-phase maintenance. Phase 1 (`abandon_hours`, non-destructive): marks any still-`active` session with no events newer than that many hours as `abandoned`, and completes its lingering agents — a tidy-up of crashed/orphaned sessions. Phase 2 (`purge_days`) is ⚠ DESTRUCTIVE and IRREVERSIBLE: it permanently DELETES completed/error/abandoned sessions (and their events, agents, and token_usage) whose `started_at` is older than that many days. Active sessions are NEVER purged. Both fields are optional and independent — send only `abandon_hours` for a safe tidy-up, or include `purge_days` to also reclaim disk. Export first if the purged history matters. `purge_days` must be a whole number ≥ 1, else 400 `INVALID_PURGE_DAYS` (a fraction like 0.1 would purge almost everything). `dry_run: true` answers the same counts (plus `dry_run: true`) and changes nothing — preview a purge before running it. The response reports counts for each phase.",
|
||||
operationId: "cleanupData",
|
||||
requestBody: {
|
||||
required: true,
|
||||
@@ -1222,6 +1222,10 @@ const paths = {
|
||||
"Abandon stale sessions, then purge history older than 90 days (destructive)",
|
||||
value: { abandon_hours: 12, purge_days: 90 },
|
||||
},
|
||||
preview: {
|
||||
summary: "Dry run: count what a 90-day purge would delete, change nothing",
|
||||
value: { purge_days: 90, dry_run: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1236,6 +1240,20 @@ const paths = {
|
||||
},
|
||||
},
|
||||
},
|
||||
400: {
|
||||
description: "purge_days is not a whole number of days, 1 or more",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
||||
example: {
|
||||
error: {
|
||||
code: "INVALID_PURGE_DAYS",
|
||||
message: "purge_days must be a whole number of days, 1 or more",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -146,6 +146,30 @@ const paths = {
|
||||
"Only the sessions of one CLI. Applied before `limit`, so a busy Codex lane cannot push Claude sessions off a page.",
|
||||
example: "claude",
|
||||
},
|
||||
{
|
||||
name: "awaiting",
|
||||
in: "query",
|
||||
schema: { type: "string", enum: ["1"] },
|
||||
description:
|
||||
"`1` = only sessions blocked on the user (`awaiting_input_since IS NOT NULL AND status = 'active'`), the UI's Waiting filter. Applied before `limit`/`offset`, so `total` stays exact.",
|
||||
example: "1",
|
||||
},
|
||||
{
|
||||
name: "from",
|
||||
in: "query",
|
||||
schema: { type: "string", format: "date-time" },
|
||||
description:
|
||||
"Inclusive lower bound on `started_at` (ISO 8601). An unparseable value is ignored. Applied before `limit`/`offset`.",
|
||||
example: "2026-10-01T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
name: "to",
|
||||
in: "query",
|
||||
schema: { type: "string", format: "date-time" },
|
||||
description:
|
||||
"Inclusive upper bound on `started_at` (ISO 8601). An unparseable value is ignored. Applied before `limit`/`offset`.",
|
||||
example: "2026-10-05T23:59:59.999Z",
|
||||
},
|
||||
{
|
||||
$ref: "#/components/parameters/SourcesQuery",
|
||||
example: "local,4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
|
||||
@@ -334,7 +358,7 @@ const paths = {
|
||||
tags: ["Sessions"],
|
||||
summary: "Get aggregated session stats",
|
||||
description:
|
||||
"Returns aggregated counts for the SessionOverview panel: total events, events-by-type, the top 15 tools by usage, an error count (events whose `event_type`/`summary` match /error/i or /failed/i), the event time range, agent type/status counts, the subagent-type breakdown (excluding the special `compaction` type, which is surfaced under `agents.compaction`), and token totals. All aggregation runs in SQL, so it stays cheap even for sessions with tens of thousands of events; the endpoint is read-only with no side effects. The frontend debounces calls on `new_event` / `agent_*` / `session_updated` websocket frames so the counters track a running session. Returns 404 with code `NOT_FOUND` when the session does not exist.",
|
||||
"Returns aggregated counts for the SessionOverview panel: total events, events-by-type, the top 15 tools by usage, the exact `tool_call_count` (PreToolUse events with a `tool_name`, or PostToolUse when the session has none, e.g. an imported one) and `distinct_tool_count` (different tool names) — `tools_used` is a top-15 list, so its sum is not the count —, an error count (events whose `event_type`/`summary` match /error/i or /failed/i), the event time range, agent type/status counts, the subagent-type breakdown (excluding the special `compaction` type, which is surfaced under `agents.compaction`), and token totals. All aggregation runs in SQL, so it stays cheap even for sessions with tens of thousands of events; the endpoint is read-only with no side effects. The frontend debounces calls on `new_event` / `agent_*` / `session_updated` websocket frames so the counters track a running session. Returns 404 with code `NOT_FOUND` when the session does not exist.",
|
||||
operationId: "getSessionStats",
|
||||
parameters: [
|
||||
{
|
||||
@@ -363,6 +387,8 @@ const paths = {
|
||||
{ tool_name: "Read", count: 121 },
|
||||
{ tool_name: "Grep", count: 77 },
|
||||
],
|
||||
tool_call_count: 612,
|
||||
distinct_tool_count: 9,
|
||||
error_count: 6,
|
||||
first_event_at: "2026-06-25T14:02:11.052Z",
|
||||
last_event_at: "2026-06-25T14:31:50.119Z",
|
||||
@@ -580,7 +606,7 @@ const paths = {
|
||||
tags: ["Agents"],
|
||||
summary: "List agents",
|
||||
description:
|
||||
"Returns agents, most recent first. Filters are applied with precedence rather than composition: when `session_id` is supplied it wins and returns every agent for that session (ignoring `status` and pagination); otherwise a `status` filter returns paginated agents in that lifecycle state; otherwise all agents are returned paginated. `limit` defaults to 10000 when not a positive integer. Read-only, no side effects. Each agent's `metadata` is returned as a raw JSON-encoded string, not a parsed object.",
|
||||
"Returns agents, most recent first. Filters are applied with precedence rather than composition: `session_ids` (comma-separated, duplicates and blanks dropped, at most 200 ids, `limit` applied but not `offset`, newest `started_at` first; an empty list answers no agents) wins over everything else and returns every agent of those sessions in one call; next, when `session_id` is supplied it wins and returns every agent for that session (ignoring `status` and pagination); otherwise a `status` filter returns paginated agents in that lifecycle state; otherwise all agents are returned paginated. `limit` defaults to 10000 when not a positive integer. Read-only, no side effects. Each agent's `metadata` is returned as a raw JSON-encoded string, not a parsed object.",
|
||||
operationId: "listAgents",
|
||||
parameters: [
|
||||
{ $ref: "#/components/parameters/AgentStatusQuery", example: "working" },
|
||||
@@ -588,6 +614,14 @@ const paths = {
|
||||
$ref: "#/components/parameters/SessionFilterQuery",
|
||||
example: "b7f3a2c1-4e5d-4a8b-9c2f-1d6e8a0b3c4d",
|
||||
},
|
||||
{
|
||||
name: "session_ids",
|
||||
in: "query",
|
||||
schema: { type: "string" },
|
||||
description:
|
||||
"Comma-separated session ids: every agent of those sessions in one call (the Dashboard's active trees). Duplicates and blanks are dropped, at most 200 ids are read, `limit` still applies and `offset` does not; ordered by `started_at` DESC. Takes precedence over `session_id`, `status` and `sources`. An empty list answers `{agents: []}`.",
|
||||
example: "b7f3a2c1-4e5d-4a8b-9c2f-1d6e8a0b3c4d,0c4e9a1b-2d3f-4a5b-8c6d-7e8f9a0b1c2d",
|
||||
},
|
||||
{
|
||||
$ref: "#/components/parameters/SourcesQuery",
|
||||
example: "local,4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
|
||||
|
||||
+23
-3
@@ -554,6 +554,17 @@ function createOpenApiSpec() {
|
||||
},
|
||||
},
|
||||
},
|
||||
tool_call_count: {
|
||||
type: "integer",
|
||||
minimum: 0,
|
||||
description:
|
||||
"Exact tool calls: PreToolUse events with a tool_name, or PostToolUse ones when the session has no PreToolUse (an imported session). tools_used is a top-15 list, so its sum is not this.",
|
||||
},
|
||||
distinct_tool_count: {
|
||||
type: "integer",
|
||||
minimum: 0,
|
||||
description: "Number of different tool names this session called.",
|
||||
},
|
||||
error_count: {
|
||||
type: "integer",
|
||||
minimum: 0,
|
||||
@@ -1363,10 +1374,14 @@ function createOpenApiSpec() {
|
||||
description: "Mark active sessions abandoned if stale for this many hours",
|
||||
},
|
||||
purge_days: {
|
||||
type: "number",
|
||||
minimum: 0,
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
description:
|
||||
"Delete old completed/error/abandoned sessions older than this many days",
|
||||
"Delete old completed/error/abandoned sessions older than this many days. A whole number ≥ 1, else 400 INVALID_PURGE_DAYS (a fraction would purge almost everything).",
|
||||
},
|
||||
dry_run: {
|
||||
type: "boolean",
|
||||
description: "true = answer the same counts and change nothing.",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1379,6 +1394,11 @@ function createOpenApiSpec() {
|
||||
purged_sessions: { type: "integer" },
|
||||
purged_events: { type: "integer" },
|
||||
purged_agents: { type: "integer" },
|
||||
dry_run: {
|
||||
type: "boolean",
|
||||
enum: [true],
|
||||
description: "Present (true) only on a dry run: the counts were not applied.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user