Skip to content

fix: normalize visible assistant output before delivery#68986

Draft
dutifulbob wants to merge 32 commits into
openclaw:mainfrom
osolmaz:fix/gemma-discord-visible-output
Draft

fix: normalize visible assistant output before delivery#68986
dutifulbob wants to merge 32 commits into
openclaw:mainfrom
osolmaz:fix/gemma-discord-visible-output

Conversation

@dutifulbob

@dutifulbob dutifulbob commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Gemma was sometimes leaking hidden internal text into Discord replies.
This PR makes OpenClaw clean the final reply in one shared place before sending it.
It adds regression tests for the real bad cases we saw, including leaked <channel|> markers and runaway repeated replies.
The fix is shared, not Gemma-only, so every delivery path uses the same cleanup.

What changed

  • teach the shared visible-text normalizer to:
    • strip leaked <channel|> control delimiters when they behave like leaked scaffolding
    • preserve literal <channel|> mentions in ordinary prose
    • preserve explicitly requested quoted targets, including full repeated strings and visible outputs that end with <channel|>
    • honor explicit repeat-count requests like repeat "ABCD-1234" exactly twice
    • drop truncated trailing leaked delimiters after long internal preambles
    • keep real leaks visible even when the preamble previously mentioned <channel|> literally
    • prefer the minimal repeated unit when a no-delimiter runaway answer repeats a mistakenly doubled exact target many times
    • preserve meaningful leading indentation in delivery mode while still trimming incidental single-space padding
  • apply delivery-mode sanitization in the final delivery paths, while keeping stream-update sanitization lighter
  • defer chunked text_end delivery until the final flush so partial raw scaffold chunks are never emitted early
  • mark chunked message_end drains as final delivery so the delivery sanitizer runs on the last emitted chunks
  • preserve plain streamed chunks without directive parsing when no inline directives are present
  • preserve legacy text_end reply directives and keep intermediate chunk drains in history mode until final delivery
  • reset blockState when text_end replaces the block buffer with already-sanitized final text, so stale reasoning-tag state cannot strip the final answer on flush

Regression tests added first

  • long prose that literally mentions <channel|> stays intact
  • plan: <channel|>visible is treated as leaked scaffolding
  • explicitly requested repeated outputs stay intact when named literally, in quotes, or via repeat-count instructions
  • explicitly requested visible outputs that end with <channel|> stay intact
  • literal marker explanations after an initial leaked delimiter stay intact
  • truncated leaked delimiters after long preambles are dropped
  • leaked delimiters are still stripped when earlier preamble text mentioned <channel|> literally
  • repeated exact-target salvage prefers the minimal unit when a no-delimiter runaway answer duplicates the target in the model preamble and then repeats it many times
  • legacy text_end reply directives still work
  • chunked message_end drains are marked as final delivery
  • intermediate chunk drains keep indentation before text_end
  • delivery-mode sanitization keeps meaningful leading indentation while still trimming incidental padding
  • chunk-boundary reasoning tags do not eat the final flushed answer

Verification

  • failing regressions were added first and reproduced locally before the fixes
  • focused tests:
    • pnpm exec vitest run src/shared/text/repeated-visible-suffix.test.ts src/shared/text/assistant-visible-text.test.ts
    • pnpm exec vitest run src/shared/text/repeated-visible-suffix.test.ts src/shared/text/assistant-visible-text.test.ts src/agents/pi-embedded-utils.strip-model-special-tokens.test.ts src/agents/pi-embedded-subscribe.handlers.messages.test.ts src/agents/pi-embedded-subscribe.reply-tags.test.ts src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.test.ts src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-indented-fenced-blocks-intact.test.ts src/auto-reply/reply/reply-utils.test.ts
  • pnpm build
  • live Discord probe on vllm/gemma4-e4b after restarting the local gateway on this branch:
    • Looper prompt: reply with exactly pr68986-live2-1776657698 and nothing else
    • Bob visible Discord reply: pr68986-live2-1776657698

@greptile-apps

greptile-apps Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds sanitizeAssistantVisibleText to the streaming delta (handleMessageUpdate) and block-chunk (emitBlockChunk) emission paths so that Gemma-style <channel|> delimiters and other model control tokens are stripped before Discord block replies are sent. It also makes stripModelSpecialTokens recurse after a channel delimiter (to catch subsequent <|...|> tokens) and adds an indentation-preserving trim for leading fenced blocks in the delivery profile.

Confidence Score: 5/5

Safe to merge; the only finding is a pre-existing harmless redundancy now made more visible.

The sanitization additions are correct, message_end paths are already covered via extractAssistantVisibleTextsanitizeAssistantText, the recursive channel-delimiter stripping is sound, and the indented-fence trim logic is tested. The sole P2 note is a pre-existing double call to stripDowngradedToolCallText that does no harm.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-subscribe.ts
Line: 570-572

Comment:
**Redundant `stripDowngradedToolCallText` call**

`stripDowngradedToolCallText` is already part of the `sanitizeAssistantVisibleText` pipeline (under the `delivery` profile, `preserveDowngradedToolText` is false), so the explicit pre-call here is a no-op. The text passes through `stripDowngradedToolCallText` twice. Consider removing the outer explicit call now that the full sanitizer is in place:

```suggestion
    const chunk = sanitizeAssistantVisibleText(
      stripBlockTags(text, state.blockState),
    );
```

If keeping the explicit call is intentional (e.g., for clarity), a short comment explaining why both are needed would help future readers.

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

Reviews (1): Last reviewed commit: "fix: sanitize visible gemma discord repl..." | Re-trigger Greptile

Comment thread src/agents/pi-embedded-subscribe.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: 2c067c7fbc

ℹ️ 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/agents/pi-embedded-subscribe.ts Outdated
@dutifulbob
dutifulbob force-pushed the fix/gemma-discord-visible-output branch from 2c067c7 to 12d2cf2 Compare April 19, 2026 20:40
@dutifulbob dutifulbob changed the title fix: sanitize visible gemma discord replies fix: normalize visible model output before delivery Apr 19, 2026

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

Copy link
Copy Markdown
Contributor

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: 6388970086

ℹ️ 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/shared/text/model-special-tokens.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: 3a665e9973

ℹ️ 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/shared/text/repeated-visible-suffix.ts Outdated
Comment thread src/agents/pi-embedded-subscribe.handlers.messages.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: a1641981d9

ℹ️ 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/shared/text/model-special-tokens.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: e4f32f120e

ℹ️ 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/shared/text/model-special-tokens.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: 0d5bf4a9d0

ℹ️ 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/shared/text/repeated-visible-suffix.ts Outdated
Comment thread src/shared/text/assistant-visible-text.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: a999caca29

ℹ️ 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/shared/text/model-special-tokens.ts Outdated

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

Copy link
Copy Markdown
Contributor

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: cbcb606480

ℹ️ 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/shared/text/model-special-tokens.ts Outdated
Comment thread src/agents/pi-embedded-subscribe.ts
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels May 20, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🔥 Warming up: real-behavior proof passed; findings, security review, or rank-up moves are still in progress.

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.
What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels May 20, 2026
@giodl73-repo giodl73-repo removed the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 21, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 21, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. proof: sufficient ClawSweeper judged the real behavior proof convincing. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels May 21, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XL status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants