Custom writer events and SSE into the browser
Custom events are how you surface domain-specific progress. Inside a node, config.writer emits an event into the custom stream.
That is how the dice weights and the roll outcome render distinctly from the token stream: they are custom events, not messages tokens.
When you want every internal event, model start, tool start, token, node end, rather than node-level deltas, streamEvents is the fine-grained firehose.
SSE, Server-Sent Events, is a one-way stream over HTTP: the server holds the connection open and pushes data: lines. Your route iterates graph.stream(...) and writes each chunk as an SSE message; the browser's EventSource receives them and updates the panel.
Try it yourself
Why combined stream modes arrive as tuples
The alternative to compare against is a single merged stream with no tags on it.
Why do combined stream modes arrive as [mode, chunk] tuples rather than as one flat merged stream?
Why dice events stay off the token channel
The dice weights and roll outcome go out as custom events through config.writer. Answer in terms of what the messages channel is for.
Why not put dice progress info into the messages stream?
Stream the dice graph into a live panel
Build an SSE endpoint that runs the module 3 impulse graph and streams its progress into a browser panel.
Tick every step to confirm you did it.
Live panel shows node-by-node progress
Confirm the streaming panel behaves as expected.
Each node visibly lights up in the panel as the graph executes it, and custom writer events (dice weights, roll outcome) are rendered distinctly from the token stream.