← Back to PRs

#23144: fix(ui): strip reply directive tags from assistant messages in WebChat (#23053)

by echoVic open 2026-02-22 02:24 View on GitHub →
app: web-ui size: S trusted-contributor
## 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