Two fixes for the rendered text views (chat stream, log snapshot):
1. Lost wakeup: blocking stream readers captured the signal channel
after their data check, so a write landing in that window signaled a
now-stale channel and the reader blocked until the next event (the
TUI's last message appeared only after the next prompt). Stream and
BlockOnce now capture the signal channel before reading; chat.go
swaps the channel under chatMu via signalChatLocked and RawLogStream
captures it while holding the lock.
2. No streaming in TUI: textLog only received finalized blocks, so the
chat text stream showed nothing until a block completed. SetPartial
now appends the assistant partial's new suffix to textLog
incrementally (tracking textPartialLen); AppendBlock appends only the
trailing separator on finalization to avoid duplicating the body.
Streaming partials were appended to log.raw per chunk, each carrying the
full cumulative content, so one response left dozens of partial lines
persisted. GUI clients parsing the snapshot re-rendered the growing block
once per partial (O(N^2)) and replayed all historical partials on every
reconnect, causing intermittent rendering loops.
Partials are no longer persisted: AppendBlock writes only finalized
blocks to log.raw; SetPartial broadcasts the in-flight block without
storing it. New chat.raw StreamRaw file is the authoritative live JSONL
source (finalized history replay, then live deltas); log.raw is a
finalized-only one-shot snapshot. GUI streams chat.raw, never polls
log.raw.
- NextBlockID now uses sha256(sessionID:agentID:counter)[:8] for deterministic,
collision-resistant block IDs across sessions and agents
- Remove blockCounter reset on Clear() to maintain ID stability
- Add blockCounter to Agent, generating sequential hex IDs
- Include #blockId in all block headers: [[[role:name#id]]]
- GUI parses server-provided IDs from headers
- Falls back to client-generated IDs for legacy blocks without server IDs
- Block IDs are now deterministic based on chat history order