Files
aiturk-mobil/app/schemas/com.m57.hermescontrol.data.local.HermesDatabase
M57andM57 adde03a4de fix(chat): stop duplicating interim commentary before tool calls (issue #842) (#854)
* fix(chat): stop duplicating interim commentary before tool calls (issue #842)

Interim commentary the agent narrates before each tool call rendered
TWICE. Two mechanisms, both now covered:

1. The old tool.start orphan seal + message.complete's full text: when the
   complete payload repeats the commentary, the final bubble duplicated
   it. onMessageComplete now strips the sealed-orphan prefix (tracked in
   StreamingState) from the final text.

2. On-device capture + DB forensics (2026-08-08) exposed the real
   repeat offender: the app seals the RAW streamed text — which carries
   leading blank lines the model emits before its narration — while the
   backend persists a CLEANED copy (leading whitespace stripped). A
   transcript reload ~10s later merged the REST page, exact-content
   matching failed, and the cleaned REST copy rendered on top of the
   live bubble. sameLogicalMessage now compares trimmed content, so the
   sealed live bubble is covered by the REST row.

Also:
- session.interrupt seals the in-flight streaming message before
  clearing streaming state, so interrupting a commentary turn keeps the
  partial text
- regression tests cover both turn shapes (commentary in the final row →
  stripped; absent → kept) and the whitespace-drift reload case

* fix(chat): match tool rows by gateway call id, drop reasoning-echo dupes (#842)

REST transcript rows carry tool_call_id; live WS bubbles keep tool_id from
tool.start. Match tool rows on that 1:1 identity so MCP/web rows (stored as
raw <untrusted_tool_result> text with no JSON key) dedupe during the
post-turn transcript merge instead of ghosting after the final message.

reasoning.available echo (narration text == message body, sent ~40ms before
each tool.start) no longer overwrites the real reasoning trace, so narration
bubbles don't render their own text twice (body + Reasoning card).

Room v5 migration adds tool_call_id; regression tests use captured
on-device payload shapes.

* fix(chat): flush throttled tokens before orphan seal + prefix-safe dedupe (#842)

The tool.start seal raced the 33ms throttled token flush: deltas landing just
before the transition stayed in the buffer, so the sealed narration orphan came
out truncated (on-device capture: orphan ended '...chickpea', backend persisted
'...chickpea goodness:') and failed the logical-match against the clean REST
row — the commentary ghosted on the next transcript sync.

- ChatStreamingController.flushPendingTokens(): force-flushes the throttled
  TOKEN buffer on MessageStart/MessageComplete/MessageDone/ToolStart so the
  reducer seals the COMPLETE narration.
- sameLogicalMessage: accept prefix-covering (both sides >=40 chars) so a
  residual seal drift can never re-add a substantial narration.
- Regression tests: controller flush + truncated-orphan prefix match.

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-08 23:34:58 +03:00
..