StreamEvent stream into a bounded, multi-line status view you can render in any transport — bots today, TUI or web tomorrow.
The compositor is pure stdlib — zero third-party dependencies, deterministic, and side-effect free. Every call returns a new list; the input is never mutated. The same three exports power the bot progress_style="feed" view.
Quick Start
1
Fold events into lines
2
Wire it into your own callback
StreamEvent stream — even one you produce yourself — folds into the same bounded view.How It Works
A start event and its matching finish event share one correlation id, somerge_progress_line updates the existing line instead of appending a duplicate.
Each event type maps to a line kind and state:
Configuration Options
render_progress bounds the output for edit-in-place delivery.
Each
ProgressLine carries the state for a single step.
State machine rules
Terminal states are sticky, so the feed never lies about an outcome.- Error takes precedence — once a line is
error(✗) it stayserror, even if a laterdoneevent arrives for the same id. - Done is not downgraded — a late
runningevent never resets a completeddone(✓) line back torunning. - Non-progress events are no-ops — events outside the mapping table return the input list unchanged, so unrelated stream traffic never adds noise.
Common Patterns
Build a live TUI panel straight from the agent’s event emitter.merge_progress_line only cares about the event’s type and correlation id.
Override the glyphs by wrapping render_progress with a renderer that walks each ProgressLine.state.
Best Practices
Keep the input list and re-assign the return value
Keep the input list and re-assign the return value
merge_progress_line returns a new list and never mutates its input. Always re-assign — lines = merge_progress_line(lines, event) — so your view reflects the fold.Bound the window for edit-in-place transports
Bound the window for edit-in-place transports
Message-based channels crop long text. Pass a small
max_lines (4–8) to render_progress so the newest steps stay visible while older ones scroll off.Rely on stable correlation ids
Rely on stable correlation ids
A tool’s start and finish events share one id (from
tool_call.id / metadata.id, falling back to tool:<name>), so updates rewrite the same line. You do not need to de-duplicate events yourself.Let the compositor own terminal states
Let the compositor own terminal states
Don’t post-process
error → done. The compositor already guarantees a sticky terminal state, so your renderer can trust ProgressLine.state as-is.Related
Bot Streaming Replies
Enable the feed style on Telegram, Slack, and Discord
Streaming Tool Events
The typed StreamEvents the compositor folds
Tool Progress Streaming
Emit mid-tool progress updates into the stream

