#14966: fix(webchat): preserve user message visibility after chat.send
app: web-ui
size: S
## Summary
Fixes #14928
When sending a message in webchat, the user's message would disappear after submission.
## Root Cause
When a chat message is sent:
1. User message is added to local `chatMessages` array
2. `chat.send` request is made to server
3. On `final` event, `loadChatHistory()` is called
4. `loadChatHistory()` **replaces** local messages with server data
5. If server hasn't persisted the user message yet → **message disappears**
## Solution
Added `preserveLocalUser` option to `loadChatHistory()` that:
- Captures local user messages (those without IDs, indicating they're local-only)
- After fetching server history, merges any missing local user messages back
- Uses timestamp matching to avoid duplicates
## Changes
`ui/src/ui/controllers/chat.ts`:
- Extended `loadChatHistory()` with `opts?: { preserveLocalUser?: boolean }`
- Added logic to preserve and merge local user messages
`ui/src/ui/app-gateway.ts`:
- Pass `{ preserveLocalUser: true }` when calling `loadChatHistory()` on final events
## Testing
- [x] `pnpm build` - ✅ Success
- [x] `pnpm check` - ✅ Success
- [x] `pnpm test` - ✅ 270 tests passed
## AI Disclosure
🤖 AI-assisted (Claude), fully tested locally before submission.
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR updates the webchat final-event flow to avoid a just-sent user message disappearing when `loadChatHistory()` refreshes from the server before the message has been persisted.
Changes include:
- `ui/src/ui/controllers/chat.ts`: adds an optional `preserveLocalUser` flag to `loadChatHistory()` that captures local-only user messages (role `user` without an `id`) and merges any missing ones back after fetching `chat.history`.
- `ui/src/ui/app-gateway.ts`: passes `{ preserveLocalUser: true }` when refreshing history on `final` chat events.
The approach fits into the existing pattern where the gateway event handler triggers a history reload on `final` to keep the chat UI up to date, but now attempts to retain local optimistic messages during that reload.
<h3>Confidence Score: 3/5</h3>
- This PR is likely safe to merge after fixing message ordering and improving duplicate detection for preserved local messages.
- Core change is small and localized, but the merge strategy can reorder chat history and the timestamp-only de-duplication can incorrectly drop or duplicate messages in realistic edge cases (multiple sends within the same ms or timestamp normalization differences).
- ui/src/ui/controllers/chat.ts
<sub>Last reviewed commit: 4cbec25</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#3721: fix(ui): webchat not displaying chat responses
by maxmaxrouge-rgb · 2026-01-29
84.0%
#13104: fix: persist user command message in chat transcript
by mcaxtr · 2026-02-10
83.5%
#8344: fix: persist webchat message queue to localStorage across browser r...
by vishaltandale00 · 2026-02-03
83.1%
#14309: fix(ui): resolve chat event session key mismatch
by justonlyforyou · 2026-02-11
82.9%
#19343: Refactor chat state management: reset chat messages and queue on se...
by saurav470 · 2026-02-17
82.0%
#15564: fix: webchat messages disappear during concurrent session activity
by Automatedcapitalist · 2026-02-13
81.4%
#7522: fix(webchat): auto-scroll when message queue changes
by alsoknownasfoo · 2026-02-02
81.3%
#8742: fix(webchat): hide internal system messages from UI (#7440)
by revenuestack · 2026-02-04
80.5%
#8284: Fix: Webchat images now persist after sending
by vishaltandale00 · 2026-02-03
80.2%
#6611: fix(webchat): persist assistant messages for CLI backends
by JorgeAlan · 2026-02-01
79.9%