#23144: fix(ui): strip reply directive tags from assistant messages in WebChat (#23053)
app: web-ui
size: S
trusted-contributor
Cluster:
Model Reasoning Fixes
## Summary
Fixes #23053 — `[[reply_to_current]]` (and `[[reply_to:<id>]]`, `[[audio_as_voice]]`) tags appear as raw text in WebChat after a message finishes streaming.
## Root Cause
Two code paths render assistant messages in WebChat:
1. **Streaming path** (`src/gateway/server-chat.ts`): `emitChatDelta` and `emitChatFinal` both call `stripInlineDirectiveTagsForDisplay()` before broadcasting — tags are stripped ✅
2. **History/render path** (`ui/src/ui/chat/message-extract.ts`): `extractText()` only calls `stripThinkingTags()` for assistant messages — tags are **not** stripped ❌
When streaming completes, WebChat reloads history from the session store. The raw messages (with reply tags) pass through `extractText()` → `extractTextCached()` → `grouped-render.ts` without stripping, so `[[reply_to_current]]` becomes visible.
## Fix
Apply `stripInlineDirectiveTagsForDisplay()` alongside `stripThinkingTags()` for assistant messages in `extractText()`. This reuses the same utility already used by the gateway streaming path.
```ts
function stripAssistantDirectives(text: string): string {
return stripInlineDirectiveTagsForDisplay(stripThinkingTags(text)).text;
}
```
## Changes
- `ui/src/ui/chat/message-extract.ts` — import `stripInlineDirectiveTagsForDisplay`, apply to all three assistant text extraction branches
- `ui/src/ui/chat/message-extract.test.ts` — 6 new test cases covering string content, content array, explicit reply ID, audio tag, `.text` field, and user messages (no-op)
## Testing
- `[[reply_to_current]] Hello` → assistant renders `Hello`
- `[[reply_to: msg_123]] Hello` → assistant renders `Hello`
- `[[audio_as_voice]] Hello` → assistant renders `Hello`
- User messages are unaffected (tags preserved as-is)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixes `[[reply_to_current]]` and related directive tags appearing as raw text in WebChat after message streaming completes.
**Root Cause**: The streaming path (`server-chat.ts`) strips directive tags before broadcasting, but the history/render path (`message-extract.ts`) only stripped thinking tags. When WebChat reloads history from session store after streaming, raw directive tags become visible.
**Solution**: Applied `stripInlineDirectiveTagsForDisplay()` alongside `stripThinkingTags()` for all assistant message extraction paths (string content, content array, and `.text` field), reusing the same utility as the gateway streaming path.
**Test Coverage**: Added 6 test cases covering string content, content arrays, explicit reply IDs, audio tags, `.text` field, and verification that user messages are unaffected.
**Minor Note**: One style suggestion regarding whitespace normalization consistency with the gateway code pattern.
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge with low risk
- The fix correctly addresses the root cause by applying the same directive tag stripping utility to both code paths (streaming and history rendering). The implementation reuses existing, well-tested functions and adds comprehensive test coverage. Score is 4/5 rather than 5/5 due to one minor style suggestion about whitespace normalization consistency with the gateway pattern, though this likely has minimal practical impact given markdown rendering behavior.
- No files require special attention - the changes are straightforward and well-tested
<sub>Last reviewed commit: aa76e1e</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#23073: fix(ui): strip reply directive tags from assistant messages in WebC...
by x4v13r1120 · 2026-02-22
91.7%
#20164: fix(webchat): strip reply directive tags before rendering assistant...
by Limitless2023 · 2026-02-18
90.9%
#23246: fix(gateway): strip reply directive tags from finalized webchat mes...
by SidQin-cyber · 2026-02-22
84.9%
#23370: fix: strip [[reply_to_current]] tags from WebChat + validate invoke...
by alexmelges · 2026-02-22
82.8%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
82.4%
#4495: Fix: emit final assistant event when reply tags hide stream
by ukeate · 2026-01-30
79.7%
#17244: fix: strip TTS tags from agent replies before delivery (#14652)
by robbyczgw-cla · 2026-02-15
79.4%
#22832: fix: strip [[reply_to_current]] directive from chat history
by willkriski · 2026-02-21
79.1%
#8353: fix(ui): display tool calls during webchat streaming
by MarvinDontPanic · 2026-02-03
78.6%
#8334: fix(webchat): Filter NO_REPLY messages from chat history
by vishaltandale00 · 2026-02-03
78.3%