-
fix(tui,sse): inline Text streaming + empty-id keepalive skip (v0.8.1)
released this
2026-05-24 21:39:02 -07:00 | 248 commits to main since this releaseTwo related fixes for the same user-reported bug pattern from a
running session against ratatoskr:sindra (qwen3.6-35-a3b-heretic):1. Streaming text overlapping the transcript
Operator: "new text comes at the bottom and overwrites the existing
pane information instead of pushing it up naturally."Root cause: the v0.6.0
#current-textStatic wasdock: bottom
withheight: auto, sitting between the transcript RichLog (1fr)
and the prompt Input (dock: bottom). As text streamed, the Static
grew UPWARD but Textual didn't dynamically resize the 1fr transcript
to accommodate — the growing Static visually OVERLAPPED the
transcript's bottom rows. On Done,current_text.update("")snapped
it to height 0 and the transcript re-laid-out — "boom, everything
updates."Fix: remove
#current-textStatic entirely. Apply the same
coalesce-on-newline pattern v0.7.1 used for thinking — Text deltas
accumulate inTuiPresenterState.text_chunk_buffer, flushing whole
lines (each\nboundary) directly tolog(transcript). On Done:
flush remaining tail, then [done] label + Rule + Markdown body.Trade-off accepted: streamed lines + post-Done Markdown body are
both in the transcript (some content duplication). The Markdown
body re-renders the same content with proper formatting (lists,
bold, code blocks). Acceptable — operator gets both the live-progress
streaming AND the canonical rendered version.2. MalformedSseId raw='' crashing every turn
Operator: "current session is erroring on every turn with
[malformed_sse_id] raw=''"Worldtree's qwen3.6-35-a3b-heretic provider emits some events
withoutid:lines (observed 2026-05-25 mid-stream). When the FIRST
such event arrives before any prior id has been seen, httpx_sse's
ServerSentEvent.idis""._parse_sse_id('')raised ValueError
→ MalformedSseId → turn worker bailed → operator saw the label
every turn.Per SSE RFC, events without
id:are legitimate (they just don't
update Last-Event-ID). Issue #7 already covered the empty-DATA
keepalive case with skip-silently semantics. Empty-id is the same
shape of wire weirdness; same fix shape:if sse.id == "":
continue # treat as keepaliveOrdered AFTER the empty-data branch so an empty-data + empty-id
event still gets skipped on the data check.Tests + smoke
287/287 GREEN (was 286, +1 for empty-id skip; +1 net Text-flow test
adjustments). Ruff clean.Verified Worldtree alive when the user hit the empty-id bug
(/healthz returned ok in 18ms) — not a server-down issue, just
wire-format mid-stream.Caveats
The fix doesn't recover content from the dropped empty-id event.
If the event happened to carry meaningful data (not a true
keepalive), we silently lose it. Acceptable trade-off: pre-v0.8.1
EVERY turn died on the offending agent; post-v0.8.1 the turn
continues and any single dropped frame is recoverable from logs if
debugging. Worldtree-side fix (always emit ids) is the right
upstream answer; ratatoskr just stops panicking on wire weirdness.Patch bump (v0.8.0 → v0.8.1) — both fixes are bug fixes; no public
API change. TheTuiPresenterState.rendersignature loses the
current_textparameter (was added v0.6.0), but presenter is an
internal contract; no external callers.Downloads