← Back to PRs

#20164: fix(webchat): strip reply directive tags before rendering assistant messages

by Limitless2023 open 2026-02-18 16:07 View on GitHub →
app: web-ui size: S
## Problem Closes #20063 In webchat (and `openclaw-control-ui`), assistant messages that include `[[reply_to_current]]` or `[[reply_to: <id>]]` reply directive tags are rendered with the raw tag text visible to the user: ``` [[reply_to_current]] Here is my response... ``` These tags are routing directives that instruct the gateway where to send a quoted/threaded reply on supported channels (Telegram, Discord, etc.). They are stripped during outbound delivery on those channels, but the webchat UI's `extractText()` pipeline never applied equivalent stripping. ## Root Cause `ui/src/ui/chat/message-extract.ts` `extractText()` applies `stripThinkingTags()` for assistant messages, but has no equivalent step for inline directive tags. The backend stripping in `src/utils/directive-tags.ts` (`parseInlineDirectives`) is never called on the UI side. ## Fix - **`ui/src/ui/format.ts`**: Add `stripReplyTags()` — a mirror of `REPLY_TAG_RE` from `directive-tags.ts` — to strip `[[reply_to_current]]` and `[[reply_to: <id>]]` patterns. - **`ui/src/ui/chat/message-extract.ts`**: Apply `stripReplyTags()` after `stripThinkingTags()` for all assistant message content paths (string, array, `.text` fallback). - **`ui/src/ui/chat/message-extract.test.ts`**: Add test cases covering all stripping scenarios. ## Behaviour | Before | After | |--------|-------| | `[[reply_to_current]] Here is my response.` | `Here is my response.` | | `[[ reply_to: abc123 ]] Here is my response.` | `Here is my response.` | Non-assistant messages (user, system) are unaffected. <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds UI-side stripping of `[[reply_to_current]]` and `[[reply_to: <id>]]` directive tags from assistant messages in the webchat UI, mirroring the backend stripping already done during outbound channel delivery. The fix adds a `stripReplyTags()` function in `ui/src/ui/format.ts` and applies it in all three content extraction paths of `extractText()` in `message-extract.ts`. - The regex in the UI (`REPLY_TAG_RE`) correctly mirrors the backend pattern from `src/utils/directive-tags.ts`, minus the capture group since the UI only needs stripping, not ID extraction. - Stripping is applied consistently across all assistant message content paths (string, array, `.text` fallback) and correctly excluded from non-assistant (user/system) messages. - Test coverage is solid, covering `reply_to_current`, `reply_to: <id>`, whitespace variants, user message passthrough, and content arrays. - Note: `[[audio_as_voice]]` is another directive tag that is similarly not stripped in the UI, but this is out of scope for the reported issue (#20063). <h3>Confidence Score: 5/5</h3> - This PR is safe to merge — it adds display-only stripping of directive tags with no risk to backend logic or data flow. - The change is narrow and well-scoped: a single new pure function (stripReplyTags) applied consistently in one file. The regex correctly mirrors the backend pattern. Tests cover all relevant scenarios. No side effects on non-assistant messages. No changes to backend logic or data models. - No files require special attention. <sub>Last reviewed commit: 64e6c77</sub> <!-- greptile_other_comments_section --> **Context used:** - Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8)) <!-- /greptile_comment -->

Most Similar PRs