hermes - 💡(How to fix) Fix Desktop: intermediate assistant text (emitted before a tool call) disappears from the rendered thread when the turn completes [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Fix Action

Fixed

Code Example

user:      <request>
assistant: content="<interim text>"  + tool_calls=[...]
tool:      <result>
assistant: content="<final reply>"

---

const replaceTextPart = (parts: ChatMessagePart[]) => {
  const kept = parts.filter(part => {
    if (part.type === 'text') {
      return false      // earlier interim text parts are dropped here
    }
    ...
  })
  return finalText ? [...kept, assistantTextPart(finalText)] : kept
}
RAW_BUFFERClick to expand / collapse

Environment

  • Surface: Hermes Desktop app, remote gateway mode
  • Backend transport: API server SSE (POST /api/sessions/{id}/chat/stream)
  • display.interim_assistant_messages: true

Steps to reproduce

  1. Send a message that causes the agent to emit visible assistant text and then make a tool call in the same turn.
  2. Watch the thread during streaming.
  3. Wait for the turn to finish.

Actual input (agent turn structure)

user:      <request>
assistant: content="<interim text>"  + tool_calls=[...]
tool:      <result>
assistant: content="<final reply>"

Actual output (desktop)

  • During streaming: interim text renders and streams, followed by the tool-call entry, followed by the final reply.
  • After the turn completes: the interim text disappears. The thread shows only the tool call and the final reply.
  • After closing the app and reopening the session: the interim text reappears. (Confirmed.) The text is persisted; only the live rendered view drops it on completion.

Expected output

The interim assistant text that streamed before the tool call remains visible after the turn completes, matching both the streamed view and the persisted/reloaded session history.

Cross-surface comparison

On Discord, the same interim text persists after completion.

Code pointer (for triage)

apps/desktop/src/app/session/hooks/use-message-stream.ts, message.complete handler (line 750 on origin/main). On completion, replaceTextPart (lines 453–468) removes all parts where part.type === 'text' and appends only finalText. Tool parts are retained; text parts that streamed earlier in the same turn are not retained.

const replaceTextPart = (parts: ChatMessagePart[]) => {
  const kept = parts.filter(part => {
    if (part.type === 'text') {
      return false      // earlier interim text parts are dropped here
    }
    ...
  })
  return finalText ? [...kept, assistantTextPart(finalText)] : kept
}

Note for maintainers: the API server emits a per-turn transcript on the run.completed event (messages field, gateway/platforms/api_server.py _turn_transcript_messages). The message.complete handler in the desktop does not reconcile against it.

Version

Reproduced on origin/main (commit fe709a421); the referenced code is unchanged through current origin/main HEAD.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING