cb410e5007
When the model interleaves narration text with tool calls within one
turn ("text → tool → more text"), the assistant text was rendered split
across the tool boundary — a word could be cut in half, e.g. the part
before the tool call ending mid-word and the remainder appearing after
the tool card.
Root cause: the agent bridge (`hermes_bridge.py`) accumulated streamed
text in `RunRecord.deltas` and tool/lifecycle events in
`RunRecord.events` as two parallel lists with no relative ordering. On
poll, the aggregated text (`"".join(deltas)`) and the events were
delivered separately, and the Node consumer (`handle-bridge-run.ts`)
processed all `chunk.events` (including `tool.started`) before the
aggregated `chunk.delta`. The real interleaving of text and tool calls
was therefore lost, splitting the text around the tool boundary.
Fix:
- Bridge: `stream_callback` now also appends each text chunk as an
ordered `stream.delta` event into the same `events` list as
tool.started/tool.completed, preserving true interleaving. `deltas`
is still kept for the aggregated `output`/resume snapshot.
- Node: process `stream.delta` events inline within the events loop (in
true order), and skip the aggregated `chunk.delta` when ordered
`stream.delta` events were present for that chunk (avoids duplicate
text). Text-delta handling was extracted into `processBridgeTextDelta`
and reused by both paths.
Verified end-to-end: narration that calls a tool mid-sentence now
streams and persists as coherent text in the exact order produced, with
no word split across the tool boundary.
Co-authored-by: Paulo Cavallari <paulocavallari@users.noreply.github.com>