status usage bars should all line up

This commit is contained in:
Ian Gulliver
2026-10-18 23:26:12 -07:00
parent a11c899f14
commit 9625be25fd
2 changed files with 46 additions and 47 deletions
+1 -1
View File
@@ -242,7 +242,7 @@ Agent health is `internal/agents/health.go`:
- **Watching.** `Manager.Watch` recomputes health every `healthInterval`. When the set of problems (non-`ok` processes and zombies) changes, it logs each new one as `agent health problem` and broadcasts an unstored `admin.health` event (`seq` 0). Nothing is killed or repaired automatically.
- **Protocol errors.** `internal/claude` reports a stdout line that isn't a JSON object, doesn't parse, or has a `type` outside `knownTypes` (the line is still passed on), and the manager adds lines whose content it fails to decode. `protocol_errors.counts` counts them per vendor since byre started; `recent` holds the last `keptBadLines`, newest first, each with session, time, reason, the raw line cut to `badLineBytes` and its full `size`. They live in memory only, and each one also broadcasts `admin.health`.
The left pane's **Status** tab shows, from the top: a Problems section only when there are zombies or protocol errors (one line per zombie, and per vendor the error count with the latest line folded under it); each running turn, linking to its task, with the time since its process's last event and `stuck` or `exited` when it is; each vendor's rate-limit status with its reset time and, per window, the share used, the share of the window's time elapsed (from `resetsAt` and `windowMins`), and a pace bar whose fill is the share used and whose vertical line is the share elapsed, the fill warn-coloured when usage is ahead of the line; a Clients table, one row per entry in `clients`, with the Mac, user, the build's commit (cut to 8 characters, the full one in its tooltip), the time since it connected and the time since its last action; and the server counts. It refetches the status when a `session.created`, `session.state`, `task.*`, `project.created`, `admin.health` or `admin.clients` event arrives, when an `agent.vendor` event carries a `rate_limit_event`, and when the SSE stream reconnects. Other events advance the last-event seq it shows, and any event carrying a process's `agent_session_id` (stored or not, such as `agent.delta`) moves that process's last-event time forward, re-rendering at most once a second per process. A re-render keeps the scroll position and which folded lines are open. There is no timer, so "time since last event" and the "in 2h 10m" beside a reset time are as of the last render; a process going quiet long enough to be stuck shows up when the next health check broadcasts `admin.health`.
The left pane's **Status** tab shows, from the top: a Problems section only when there are zombies or protocol errors (one line per zombie, and per vendor the error count with the latest line folded under it); each running turn, linking to its task, with the time since its process's last event and `stuck` or `exited` when it is; each vendor's rate-limit status with its reset time and, per window, the share used, the share of the window's time elapsed (from `resetsAt` and `windowMins`), and a pace bar whose fill is the share used and whose vertical line is the share elapsed, the fill warn-coloured when usage is ahead of the line, all vendors in one table so their columns and pace bars line up; a Clients table, one row per entry in `clients`, with the Mac, user, the build's commit (cut to 8 characters, the full one in its tooltip), the time since it connected and the time since its last action; and the server counts. It refetches the status when a `session.created`, `session.state`, `task.*`, `project.created`, `admin.health` or `admin.clients` event arrives, when an `agent.vendor` event carries a `rate_limit_event`, and when the SSE stream reconnects. Other events advance the last-event seq it shows, and any event carrying a process's `agent_session_id` (stored or not, such as `agent.delta`) moves that process's last-event time forward, re-rendering at most once a second per process. A re-render keeps the scroll position and which folded lines are open. There is no timer, so "time since last event" and the "in 2h 10m" beside a reset time are as of the last render; a process going quiet long enough to be stuck shows up when the next health check broadcasts `admin.health`.
Agent activity becomes events with `agent_session_id` set: `user.message`, `agent.message`, `agent.thinking`, `agent.tool_call`, `agent.tool_result`, `turn.ended`, `session.state`, `session.subagents`, `session.model`, and `session.context`. Anything else Claude Code emits (rate-limit status, task progress, state changes) is recorded unchanged as `agent.vendor`. Codex items map onto the same kinds (`handleCodex` in `internal/agents/codex.go`): an `agentMessage` becomes `agent.message`, a `reasoning` item's summary `agent.thinking`, and `commandExecution` (as `Bash`), `fileChange` (as `apply_patch`), `dynamicToolCall`, `mcpToolCall` and `webSearch` items an `agent.tool_call` when they start and an `agent.tool_result` when they complete. Other completed items and the `error` and `warning` notifications are recorded as `agent.vendor`; the remaining notifications (thread status, MCP startup and the like) aren't recorded. `item/agentMessage/delta` becomes `agent.delta`, and `thread/tokenUsage/updated` `session.context`. Antigravity steps map the same way (`handleAntigravity` in `internal/agents/antigravity.go`): an `agent_response` step's `text_delta`s become `agent.delta` and its finished text `agent.message`, and a `tool` step an `agent.tool_call` when it starts and an `agent.tool_result` when it is done, with `run_command` recorded as `Bash` and `call_mcp_tool` as `mcp__<server>__<tool>` with its `Arguments` as input; `user_input` steps aren't recorded, and other step types are recorded as `agent.vendor`. The one exception is partial output: each top-level `text_delta` is broadcast to SSE clients as an `agent.delta` event (`payload.text`, `seq` 0) that is never stored, and the transcript grows a live message from them until the finished `agent.message` replaces it. A `turn.ended` event carries `summary: true` when its `result` is the turn's summary (see above). Activity inside a subagent (`agent.message`, `agent.thinking`, `agent.tool_call`, `agent.tool_result`) carries `payload.parent_tool_use_id`, the ID of the tool call that started the subagent, and never updates `session.context`. `session.context` records the context size whenever it changes: `used` is the last assistant message's input, cache-read, cache-creation and output tokens, and `window` is the model's context window from the turn's `result` (`modelUsage[model].contextWindow`), so it is 0 until the session's first turn after a server start ends. The task header's **Compact** button shows the latest one as a background fill across the button, proportional to the window, tinted amber from 70% and red from 90%; the exact count is in its tooltip.