You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
End an empty reply on a line the surface receives, not one only the graph sees (#292)
A reply with no text and no tool call ends the graph — the conditional edge
sees no calls and stops. #289 tried to give that run a visible line by
substituting a fallback AIMessage into the graph's state (withVisibleReply),
so the CHANGELOG already promises "an empty reply ends on a visible line
rather than in silence".
It never reached the person. This service streams AG-UI by reading the run's
framework events, and it emits text only from on_chat_model_stream (the model's
own deltas) and tool events from the tools node. A message a graph node
returns is never one of those, so a fallback placed in state is a fallback the
surface never sees: an empty reply still ended on a bare RUN_STARTED/
RUN_FINISHED pair with nothing between them.
Proof, driving the real reader over a real graph with a fake empty model:
the graph's final state held the fallback, and the client received only
["RUN_FINISHED"].
The guard belongs on the wire, where the surface reads. streamRun now tracks
whether anything a person can see reached it — a line of prose or a tool call —
and ends a run that produced neither on the same fallback line. "Visible" is
decided by textOfChunk, the one rule the streamed deltas already use, so a
reply that is only a Responses-API reasoning summary (text the person is never
shown) counts as empty here too — the case the state-based guard got wrong,
since hasVisibleText read any `text` field as visible.
The translation is extracted to stream.ts for the same reason history.ts and
deltas.ts are: index.ts calls serve() at module scope, so importing runAgent
to test it binds a port. stream.ts imports no runtime module beyond deltas, so
its tests need no provider key and no network.
Co-authored-by: kevin9327 <kevin9327@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
0 commit comments