fix(gateway): persist streamed text when webchat final event lacks message#31920
Conversation
Greptile SummaryAdded client-side stream persistence logic to prevent loss of streamed text in webchat when tool calls follow immediately. The change mirrors the existing "aborted" handler's pattern. Key Changes:
Code Quality:
Confidence Score: 5/5
Last reviewed commit: 52cd5cc |
…ssage When an agent streams text and then immediately runs tool calls, the webchat UI drops the streamed content: the "final" event arrives with message: undefined (buffer consumed by sub-run), and the client clears chatStream without saving it to chatMessages. Before clearing chatStream on a "final" event, check whether the stream buffer has content. If no finalMessage was provided but the stream is non-empty, synthesize an assistant message from the buffered text — mirroring the existing "aborted" handler's preservation logic. Closes openclaw#31895
52cd5cc to
a708323
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a708323b19
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } else if (state.chatStream?.trim()) { | ||
| state.chatMessages = [ |
There was a problem hiding this comment.
Prevent synthesized stream message from being overwritten
When a final chat event arrives without payload.message, this new branch appends state.chatStream locally, but handleChatEvent still returns "final", so ui/src/ui/app-gateway.ts immediately calls loadChatHistory because shouldReloadHistoryForFinalEvent returns true for missing messages; loadChatHistory replaces chatMessages wholesale, which drops the synthesized assistant message in the same text-then-tool-turn scenario this fix is targeting. Either skip that reload when we synthesize from chatStream, or persist/merge in a way that survives the reload.
Useful? React with 👍 / 👎.
Summary
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
Security Impact (required)
Repro + Verification
Environment
Steps
Expected
Actual
Evidence
Client-side change follows the same pattern as the existing "aborted" handler which already persists streamed text. Added 4 tests: persist on missing finalMessage, skip empty/whitespace streams, skip null stream, prefer finalMessage over stream.
Human Verification (required)
Compatibility / Migration
Failure Recovery (if this breaks)
Risks and Mitigations