Streaming
Pass a block to run and it receives every event in the exchange, as it
happens. Text, thinking, and half-written tool arguments stream mid-turn;
the loop adds its own events around tools, approvals, and compaction.
agent.run("Investigate the latency alert and post what you find.") do |event| case event.type when :text_delta then print event.delta when :tool_result then render(event.message.ui) when :approval_needed then page_on_call(event) endendThe event types
Stream events from the provider:
:start:text_start :text_delta :text_end:thinking_start :thinking_delta :thinking_end:toolcall_start :toolcall_delta :toolcall_end:done :error :retryLoop events from the harness: :tool_result after each tool runs,
:approval_needed when a gated call parks, and :compacting /
:compaction around a context fold. Events carry origin when they come
from a sub-agent, duration where timing is meaningful, and attempt /
max_attempts / delay on :retry.
Sinks
Sinks bridge the event stream to a transport and compose as blocks:
cable = Mistri::Sinks::ActionCable.new("agent_#{session.id}")sink = Mistri::Sinks::Coalesced.new(cable) # merge token bursts
agent.run(input, &sink)Mistri::Sinks::SSE.new(response.stream) does the same for
ActionController::Live.
For a host sink that translates events into your own wire format, with a
reconnecting affordance on :retry, see the recipe
Stream events to a live UI.