Two settle paths could each paint a second bubble for one stored row:
1. use-message-stream: a rewritten final (response_previewed /
response_transformed) shares no text prefix with the streamed interim, so
the prefix heuristics missed and the volatile boundary flag could not
speak for it once a chained message.start reset it. The terminal frame
names the stored row it settled (persisted_turn.final_assistant_row_id);
settle onto the display-only interim keyed on that durable receipt instead
of appending a duplicate.
2. use-session-actions overlayConcurrentMessageChanges: the committed row
and the settled live row capture the same reply at two moments while it
kept streaming, so the strict text-equality guard rejected one as a
duplicate of the other. Accept either as a forward text-extension of the
other via isStrictAnswerTextExtension, the same trade
removeRepresentedLocalLiveProjection already made.
Four new tests cover the rewritten-previewed, rewritten-transformed,
lagging-live-row, and ran-past-committed-row shapes.
Fixes https://github.com/NousResearch/hermes-agent/issues/123993
Fixes https://github.com/NousResearch/hermes-agent/issues/124128
Refs https://github.com/NousResearch/hermes-agent/issues/122167