Skip to content

UI Streaming and Rendering ​

How TUI and web render harness events, and how to debug “stuck” or garbled UI.

Stream Normalization ​

Core streaming sanitization removes common malformed glyph patterns from token deltas before UI rendering.

Event Ordering ​

High-throughput streams can interleave text and structural events (tool cards, traces, retries).

Mitigation:

  • queue text/trace deltas briefly
  • flush buffered deltas before structural events (tool_start, tool_result, etc.)
  • maintain channel separation (user vs trace)

Web: SSE and busy state ​

Client: packages/web/client/useSSE.ts. Server: packages/web/server/sse.ts.

Compose dock ​

When the agent streams write_file, edit_file, or mail compose tools, web and desktop open a right-hand compose dock with live preview (compose_preview + tool_delta). See Compose dock & attachments.

Transport vs API vs HUD “STANDBY” ​

Treat these separately:

LayerSignalTypical meaning
REST /api/statusbusy, lastTurnEndedAt, clientsExpress up; harness may be mid-turn even when SSE hiccups.
SSE /api/streamconnected, sseTransport, sseTransportDetailLive event delivery; reconnects briefly during API restarts or web:dev proxy ECONNRESET.
HUD orb “STANDBY”idle, not necessarily brokenIdle between turns — not diagnostics for crashes.

Client behaviour (web):

  • CONNECTING / WAIT API appears before the first successful SSE handshake (sseHasOpenedOnce); REST may already be reachable.
  • DEGRADED means /api/status OK while the EventSource layer is reconnecting / offline, including when the harness is busy (copy notes that events may pause).
  • OFFLINE means /api/status could not be reached — kill :3001, wrong proxy target, VPN, firewall, etc.
  • Busy stall watchdog: if busy and there are no streamed assistant/tool events (tokens, tool cards, subtask lines, etc.) for ~52s, a [UI] trace fires; it repeats about every 4 minutes while the stall continues. Periodic harness_running alone does not reset this timer — it only proves the SSE socket is warm. Use RAW for trace lines when AGENT_UI_VERBOSITY=quiet.
  • web:dev (Vite on :5173 proxying to Express :3001) often logs http proxy error ... ECONNRESET when Express restarts; the UI probes /api/status on SSE interruption so SIGNAL does not jump straight to misleading OFFLINE when the API is still healthy.

turn_end and “stuck processing” ​

Symptom: trace shows [UI] Server reports idle — cleared stuck "processing" or the composer stays disabled after a long turn.

Causes:

  • Very large RAW transcripts delaying turn_end delivery
  • SSE reconnect missing a single turn_end event
  • Heavy parallel tools (e.g. several web_fetch or hung read_artifact attempts)

Mitigations (client):

  • Track expected turn_end after each user send
  • Poll GET /api/status with consecutive idle samples + grace period before forcing idle
  • Compare server lastTurnEndedAt (set on turn_end in agentBridge) before unlocking UI

Mitigations (operator):

Session bootstrap ​

GET /api/config, POST /api/message, and POST /api/persona/bootstrap await whenSessionReady() so early requests do not race harness init. See Web API.

TUI Rendering Model ​

Reducer-driven message list, bounded window, stable modal heights — packages/tui/src/useAgent.ts.

Common Artifact Patterns ​

  1. Channel bleed: trace/user text inside tool lines
  2. Fragment splits: malformed glyph separators
  3. Repaint flicker: frequent tiny updates
  4. Stale tool cards: tool_start without matching tool_result after crash — refresh session

Debug flow ​

  1. Confirm artifact origin (model text vs UI composition)
  2. Inspect SSE event sequence around corruption (RAW toggle in web)
  3. Verify turn_end and /api/status alignment
  4. Test with AGENT_UI_VERBOSITY=quiet to reduce trace noise

API reference: Web API.

Liminal AI · Beta docs (v0.1.2) · Marketing: vireondynamics.com/liminal · Compare: vireondynamics.com/liminal/compare