docs(web-ui): document turn start/end times + duration on the agent terminal

Follow-up to the turn-time feature: the terminal now stamps a wall-clock
time on the turn-start / turn-end rows and an elapsed duration on
turn-end, sourced from a per-event `ts` (unix seconds) that the backend
serializes as a flattened sibling of the event tag on both the live SSE
frame and each history row.

- terminal-rendering.md: add the `.turn-time` row to the taxonomy table
  + a note on the ts source and the graceful degradation when a frame
  omits ts.
- web-ui/agent.md: note the ts sibling on the emitted events, and the
  time/duration suffixes on the TurnStart / TurnEnd renderings.
This commit is contained in:
iris 2026-06-10 15:38:04 +02:00
commit 6b4b0f11c0
2 changed files with 19 additions and 3 deletions

View file

@ -38,6 +38,7 @@ parent's negative pull.
| `.turn-body` | (child div under turn-start) | fg | same | the wake-prompt body |
| `.turn-end-ok` | `✓ turn ok` | green, left rule | `LiveEvent::TurnEnd { ok: true }` | harness |
| `.turn-end-fail` | `✗ turn fail — note` | red, left rule | `LiveEvent::TurnEnd { ok: false }` | harness |
| `.turn-time` | `· HH:MM:SS` on turn-start; `· HH:MM:SS · <dur>` on turn-end (child span) | muted, smaller | per-event `ts` (unix seconds) on the live frame + history row | harness |
| `.text` | (no prefix; markdown body) | fg | claude `assistant.content[].text` | stream-json |
| `.thinking` | `· thinking …` | muted, italic | claude `assistant.content[].thinking` | stream-json |
| `.tool-use` (flat) | `→ Name args…` | cyan | tool_use w/o rich renderer | stream-json |
@ -55,6 +56,15 @@ parent's negative pull.
| `.sys` | `! {json…}` | amber/orange | catch-all for stream shapes `renderStream` didn't classify | catch-all |
| Banner shimmer | mauve | turn in flight (ref-counted) | — | `setBannerActive` |
The `.turn-time` span is appended to the turn-start / turn-end rows from
the event's `ts` (unix seconds), which the backend serializes as a
flattened sibling of `kind` on both the live SSE frame and each history
row — so the same renderer path stamps live tail and replayed scrollback
identically. Turn-end also shows the elapsed duration (end start),
paired against the most recent open turn-start. The read is guarded on a
numeric `ts`: if a frame omits it the rows render without the time
suffix, so the terminal degrades cleanly against older event shapes.
## Renderer dispatch
`renderStream(v, api)` walks each stream-json line:

View file

@ -182,7 +182,9 @@ Each agent runs an `events::Bus`: a `tokio::sync::broadcast<LiveEvent>`
plus a sqlite-backed history at `/state/hyperhive-events.sqlite`.
The harness emits `TurnStart { from, body, unread }`,
`Stream(value)` (one per parsed stream-json line), `Note`,
`TurnEnd { ok, note }`. The web UI:
`TurnEnd { ok, note }`. Each event also carries a `ts` (unix
seconds) — a flattened sibling of the event tag on both the live
SSE frame and the replayed history rows. The web UI:
- fetches `GET /events/history` on page load and replays the last
2000 events (oldest first, with `.no-anim` so they don't
@ -212,8 +214,12 @@ Per-stream rendering:
- `Stream` `system init`, `result`, `rate_limit_event` are
dropped — too noisy.
- `Note``· text`.
- `TurnEnd``✓ turn ok` / `✗ turn fail — note`, triggers a
`refreshState()`.
- `TurnStart``◆ TURN ← <from>` with the wake-prompt body; a
muted `· HH:MM:SS` time suffix from the event `ts`.
- `TurnEnd``✓ turn ok` / `✗ turn fail — note`, with a
`· HH:MM:SS · <duration>` suffix (duration = end the paired
turn-start), triggers a `refreshState()`. The time suffix is
guarded on a numeric `ts`, so rows degrade cleanly when absent.
## Terminal-embedded prompt