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:
2026-10-05 13:22:13 +07:00
parent d4961d94ef
commit 442f133930
12 changed files with 230 additions and 31 deletions
+6 -1
View File
@@ -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",
+19 -1
View File
@@ -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",