Skip to content

fix: strip <relevant-memories> injected by memory plugin from user messages in WebUI#59697

Open
jwchmodx wants to merge 2 commits intoopenclaw:mainfrom
jwchmodx:fix/webui-strip-relevant-memories-from-user-messages
Open

fix: strip <relevant-memories> injected by memory plugin from user messages in WebUI#59697
jwchmodx wants to merge 2 commits intoopenclaw:mainfrom
jwchmodx:fix/webui-strip-relevant-memories-from-user-messages

Conversation

@jwchmodx
Copy link
Copy Markdown
Contributor

@jwchmodx jwchmodx commented Apr 2, 2026

Summary

  • Memory plugin's before_agent_start hook prepends <relevant-memories>...</relevant-memories> to user prompts before they are stored in session history
  • stripEnvelopeFromMessages() (server) and processMessageText (WebUI) only stripped these tags from assistant messages, never from user messages
  • This caused internal memory context to appear in the user message bubble in WebUI

Root cause

stripRelevantMemoriesTags was only reachable via stripAssistantInternalScaffolding, which is only called for role === "assistant". User messages went through stripInboundMetadata + stripEnvelope, neither of which handles <relevant-memories> XML tags.

Fix

  • Export stripRelevantMemoriesTags from assistant-visible-text.ts
  • chat-sanitize.ts (stripEnvelopeFromMessage): apply stripRelevantMemoriesTags to user-role message content — fixes the chat.history RPC path
  • message-extract.ts (processMessageText): apply stripRelevantMemoriesTags for user-role messages — fixes the WebUI rendering path
  • Add regression tests in chat-sanitize.test.ts and message-extract.test.ts for both string content and array content shapes

Test plan

  • npx vitest run src/gateway/chat-sanitize.test.ts — 11 tests pass
  • npx vitest run ui/src/ui/chat/message-extract.test.ts — 6 tests pass
  • Start OpenClaw with memory-lancedb extension enabled, send a message, verify user bubble shows only the actual message text

Fixes #59568

🤖 Generated with Claude Code

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 2, 2026

Greptile Summary

This PR correctly fixes a bug where the <relevant-memories> block injected by the memory plugin into user messages was leaking through into the WebUI user message bubble. The fix exports stripRelevantMemoriesTags and applies it to user-role messages in both the chat.history RPC path (chat-sanitize.ts) and the WebUI rendering path (message-extract.ts), with regression tests for both string and array content shapes.

  • assistant-visible-text.ts: stripRelevantMemoriesTags exported (minimal change)
  • chat-sanitize.ts: Memory tag stripping added to all three user-message content shapes (string, array of text blocks, bare text field)
  • message-extract.ts: Memory tag stripping added to the WebUI processMessageText user path
  • chat-sanitize.test.ts / message-extract.test.ts: Regression tests covering string content and array content
  • Minor behavioural note: .trimStart() is now applied unconditionally to every user message across both files, not only to messages where a memory block was actually removed. This could silently trim intentional leading whitespace from user messages that never contained a <relevant-memories> block.

Confidence Score: 4/5

  • Safe to merge; the fix is targeted and well-tested, with only a minor unconditional trimStart() side-effect on user messages.
  • The logic change is correct and the root cause is accurately described. Two dedicated regression tests cover both content shapes in each code path. The only concern is that .trimStart() is applied to all user messages regardless of whether a memory block was present, which is a pre-existing pattern from stripAssistantInternalScaffolding but is now also applied on the user side — unlikely to cause real-world problems but worth awareness.
  • src/gateway/chat-sanitize.ts and ui/src/ui/chat/message-extract.ts — both now apply trimStart() unconditionally to all user messages.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/chat-sanitize.ts
Line: 53

Comment:
**`trimStart()` applied unconditionally to all user messages**

`.trimStart()` is now unconditionally applied to every user message, not just those from which memory tags were actually removed. If a user message begins with intentional leading whitespace (e.g. a code snippet pasted directly, or a message from a channel adapter that prefixes content with spaces), that whitespace will be silently stripped even when no `<relevant-memories>` block was present.

A safer approach would be to conditionally trim only when the strip actually modified the text:

```suggestion
      ? (() => { const s = stripRelevantMemoriesTags(stripMessageIdHints(stripEnvelope(inboundStripped))); return s !== inboundStripped ? s.trimStart() : s; })()
```

Alternatively, `trimStart()` could be applied only inside `stripRelevantMemoriesTags` when it actually removes a block (which would require a small change to that function's return logic). The same concern applies to lines 86 and 101 in this file, and to `message-extract.ts` line 15.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: strip relevant-memories from user m..." | Re-trigger Greptile

Comment thread src/gateway/chat-sanitize.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc9f21c885

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/chat-sanitize.ts Outdated
jwchmodx and others added 2 commits April 28, 2026 18:40
…tory

Memory plugin prepends <relevant-memories>...</relevant-memories> blocks
to user prompts via the before_agent_start hook. These blocks were only
stripped from assistant messages (via stripAssistantInternalScaffolding)
but never from user-role messages, causing them to appear in the WebUI
user message bubble and in chat.history responses.

Changes:
- Export stripRelevantMemoriesTags from assistant-visible-text.ts
- chat-sanitize.ts: apply stripRelevantMemoriesTags when stripping user
  messages in stripEnvelopeFromMessage (covers chat.history server path)
- message-extract.ts: apply stripRelevantMemoriesTags for user-role
  messages in processMessageText (covers WebUI rendering path)
- Add regression tests in both chat-sanitize.test.ts and
  message-extract.test.ts

Fixes openclaw#59568

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vincentkoc vincentkoc force-pushed the fix/webui-strip-relevant-memories-from-user-messages branch from bc9f21c to a1939e4 Compare April 28, 2026 18:40
@vincentkoc
Copy link
Copy Markdown
Member

ProjectClownfish pushed a narrow repair to this branch so the original contributor path can stay canonical.

Source PR: #59697
Validation: pnpm -s vitest run src/gateway/chat-sanitize.test.ts ui/src/ui/chat/message-extract.test.ts; pnpm check:changed
Contributor credit is preserved in the branch history and PR context.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: needs changes before merge.

What this changes:

The PR adds a shared injected-memory-prefix sanitizer, wires it into Gateway chat history and Control UI user-message extraction/normalization, adds regression tests, and updates the changelog.

Required change before merge:

A concrete, narrow PR blocker remains in the helper and tests; an automated repair can adjust the sanitizer without product or security judgment.

Security review:

Security review cleared: The PR changes sanitizer logic, tests, and changelog text only; it does not touch workflows, dependency sources, package resolution, secrets, permissions, or artifact execution paths.

Review findings:

  • [P2] Preserve prompt indentation after removing memory prefix — src/shared/text/assistant-visible-text.ts:591-593
Review details

Best possible solution:

Land a narrow shared sanitizer that recognizes only the memory plugin’s injected leading prefix and generated separator, preserves ordinary user-authored tags and prompt indentation, and covers Gateway history plus both Control UI rendering paths with regression tests.

Do we have a high-confidence way to reproduce the issue?

Yes. A high-confidence static reproduction exists: current main has memory-lancedb generating a leading memory prefix while Gateway and Control UI user display paths omit a memory-prefix stripper; the PR-specific blocker is reproduced by an injected prefix followed by an indented prompt.

Is this the best way to solve the issue?

No, not quite. The PR has the right scope and surfaces, but the helper should remove only the injected block plus generated separator instead of arbitrary whitespace after the block.

Full review comments:

  • [P2] Preserve prompt indentation after removing memory prefix — src/shared/text/assistant-visible-text.ts:591-593
    stripInjectedRelevantMemoriesPrefix skips every whitespace character after the closing memory tag. The runtime stores this as injected block plus generated separator plus user prompt, so an indented prompt such as pasted code loses its leading spaces after the prefix is removed. Strip only the generated separator and add a regression for an injected prefix followed by indented user text.
    Confidence: 0.86

Overall correctness: patch is incorrect
Overall confidence: 0.88

Acceptance criteria:

  • pnpm test src/shared/text/assistant-visible-text.test.ts src/gateway/chat-sanitize.test.ts ui/src/ui/chat/message-extract.test.ts ui/src/ui/chat/message-normalizer.test.ts
  • pnpm check:changed

What I checked:

  • Memory plugin emits the affected prefix: formatRelevantMemoriesContext returns a leading <relevant-memories> block with the untrusted-memory preamble used by the PR's sanitizer. (extensions/memory-lancedb/index.ts:521, a256745323ab)
  • Current main Gateway sanitizer gap: User-role content is stripped for runtime context, inbound metadata, envelope, and message-id hints only; no relevant-memory prefix stripper is called for string, array, or bare text content. (src/gateway/chat-sanitize.ts:55, a256745323ab)
  • chat.history uses Gateway sanitizer: projectChatDisplayMessages routes history through stripEnvelopeFromMessages by default, so the missing user-role prefix stripping affects the Gateway history projection. (src/gateway/chat-display-projection.ts:504, a256745323ab)
  • Current main Control UI extraction gap: processMessageText strips inbound metadata and envelopes for user messages but does not strip memory-plugin <relevant-memories> prefixes. (ui/src/ui/chat/message-extract.ts:17, a256745323ab)
  • Current main Control UI normalizer gap: normalizeMessage strips inbound metadata from user text before grouped rendering but does not remove the injected memory prefix. (ui/src/ui/chat/message-normalizer.ts:377, a256745323ab)
  • PR helper trims user-authored whitespace: The PR's stripInjectedRelevantMemoriesPrefix advances over every \s after the closing tag, which also removes indentation belonging to the original user prompt after the generated separator. (src/shared/text/assistant-visible-text.ts:591, a1939e4a05dc)

Likely related people:

  • steipete: Recent main history on the central Gateway/WebChat sanitizer and assistant-visible-text paths includes multiple commits by steipete, including runtime-context transcript sanitization and shared visible-text sanitizer work. (role: current-main maintainer / adjacent owner; confidence: high; commits: 6e985a421da7, d6e9ae53fee5, c63a4f0f13fc; files: src/gateway/chat-sanitize.ts, ui/src/ui/chat/message-extract.ts, src/shared/text/assistant-visible-text.ts)
  • vincentkoc: vincentkoc appears in current-main history for user-message inbound metadata stripping and in the PR timeline as the person who force-pushed the repaired branch head and recorded validation. (role: recent PR branch repair / metadata sanitizer contributor; confidence: high; commits: 35be87b09b0c, 9a6b26d42776, d55c7ea997c0; files: src/gateway/chat-sanitize.ts, ui/src/ui/chat/message-normalizer.ts, src/shared/text/assistant-visible-text.ts)
  • Mellowambience: The Control UI normalizer behavior for stripping injected inbound metadata from user messages traces to the adjacent UI history commit for fix(ui): strip injected inbound metadata from user messages in history #22142, which is the same display-cleanup boundary this PR extends. (role: introduced adjacent UI user-message metadata stripping; confidence: medium; commits: a4e7e952e143; files: ui/src/ui/chat/message-normalizer.ts, ui/src/ui/chat/message-extract.ts)

Remaining risk / open question:

  • The proposed helper currently strips all whitespace after the injected memory block, so a pasted indented prompt can be displayed differently from what the user sent until the PR is repaired.

Codex review notes: model gpt-5.5, reasoning high; reviewed against a256745323ab.

@vincentkoc vincentkoc added clawsweeper Tracked by ClawSweeper automation and removed clownfish:merge-ready labels Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui clawsweeper Tracked by ClawSweeper automation gateway Gateway runtime size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] WebUI: internal metadata displayed in user message input area

2 participants