Skip to content

fix(webchat): suppress NO_REPLY control token in chat UI#32045

Closed
liuxiaopai-ai wants to merge 1 commit intoopenclaw:mainfrom
liuxiaopai-ai:codex/webchat-silent-no-reply-32015
Closed

fix(webchat): suppress NO_REPLY control token in chat UI#32045
liuxiaopai-ai wants to merge 1 commit intoopenclaw:mainfrom
liuxiaopai-ai:codex/webchat-silent-no-reply-32015

Conversation

@liuxiaopai-ai
Copy link

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: Control UI chat history/event handling can surface assistant messages whose text is exactly NO_REPLY, leaking a silent control token into visible chat.
  • Why it matters: users see confusing ghost assistant replies during housekeeping turns (for example compaction/memory flush), even though these turns are meant to stay silent.
  • What changed: add controller-side filtering for silent assistant payloads in both history load (chat.history) and live chat events (delta/final/aborted fallback paths).
  • What did NOT change (scope boundary): gateway-side token semantics, core auto-reply token behavior, and non-UI delivery channels.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

Control UI no longer renders assistant messages that are exact NO_REPLY control tokens when loading chat history or processing live chat events.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 + pnpm
  • Model/provider: N/A
  • Integration/channel (if any): Control UI / webchat
  • Relevant config (redacted): local gateway + chat history/event flow

Steps

  1. Load Control UI chat history containing assistant messages with exact text NO_REPLY.
  2. Observe live chat.event flows where assistant delta/final/aborted fallback can carry NO_REPLY text.
  3. Validate rendered message history and stream state after this PR.

Expected

  • NO_REPLY control-token messages are not visible in chat history.
  • Live stream state does not adopt exact NO_REPLY token text.

Actual

  • Before fix: NO_REPLY could appear as a visible assistant message.
  • After fix: silent token payloads are filtered at controller boundaries.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Validation run on this branch:

  • pnpm --dir ui exec vitest run src/ui/controllers/chat.test.ts --config vitest.config.ts
  • pnpm lint ui/src/ui/controllers/chat.ts ui/src/ui/controllers/chat.test.ts CHANGELOG.md
  • pnpm tsgo
  • pnpm exec oxfmt --check ui/src/ui/controllers/chat.ts ui/src/ui/controllers/chat.test.ts CHANGELOG.md

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: NO_REPLY filtering on history load; NO_REPLY ignored on live delta; NO_REPLY final/aborted fallback payloads are not appended.
  • Edge cases checked: non-assistant messages remain untouched; normal assistant replies continue to append normally.
  • What you did not verify: full end-to-end browser interaction against a running gateway in this iteration.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR commit.
  • Files/config to restore: ui/src/ui/controllers/chat.ts, ui/src/ui/controllers/chat.test.ts, CHANGELOG.md.
  • Known bad symptoms reviewers should watch for: legitimate assistant replies being suppressed when they are not exact control tokens.

Risks and Mitigations

  • Risk: over-filtering could suppress legitimate assistant content.
    • Mitigation: filter condition is strict exact-token match with surrounding whitespace only; coverage added for normal message paths.

Copy link

@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: 260cd5982d

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +255 to 258
if (finalMessage && !isSilentAssistantMessage(finalMessage)) {
state.chatMessages = [...state.chatMessages, finalMessage];
return null;
}

Choose a reason for hiding this comment

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

P2 Badge Return null for silent final events from other runs

In the cross-run branch (payload.runId !== state.chatRunId), a final assistant message that is filtered as NO_REPLY now falls through to return "final" instead of returning null. That return value is treated as a terminal event by handleTerminalChatEvent (in ui/src/ui/app-gateway.ts), which always calls resetToolStream and flushChatQueueForEvent, so background housekeeping finals can clear tool-stream UI for an unrelated active run. Since non-silent cross-run finals already return null, silent filtered finals should follow the same non-terminal path.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 2, 2026

Greptile Summary

Fixes the visibility of NO_REPLY control tokens in the webchat UI by adding strict filtering at three key boundaries: chat history loading, live delta streaming, and final/aborted event handling.

Key changes:

  • Added isSilentReplyTokenText() helper using strict regex match /^\s*NO_REPLY\s*$/ to identify exact control tokens
  • Added isSilentAssistantMessage() to check if assistant messages contain only the silent token
  • Applied filtering in loadChatHistory() to exclude NO_REPLY messages from historical data
  • Updated handleChatEvent() to suppress NO_REPLY in delta updates, final payloads, and aborted fallbacks
  • Comprehensive test coverage added for all filtering scenarios

Implementation quality:

  • The regex pattern matches the proven approach used in backend code (src/auto-reply/tokens.ts:isSilentReplyText)
  • Filtering is appropriately strict (exact match only) to avoid suppressing legitimate messages containing NO_REPLY as part of substantive content
  • Test coverage validates both filtering behavior and that normal messages continue to work correctly

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk - it's a focused bug fix with narrow scope and comprehensive test coverage
  • Score reflects several positive factors: (1) well-defined problem with clear fix scope, (2) filtering logic mirrors proven backend implementation, (3) strict regex prevents false positives, (4) comprehensive test coverage validates all code paths, (5) no changes to gateway semantics or external interfaces, (6) backward compatible with existing chat flows
  • No files require special attention

Last reviewed commit: 260cd59

@Takhoffman
Copy link
Contributor

Closing as superseded by #32183, which is now merged and covers the NO_REPLY history/UI guardrails with regression tests.

@Takhoffman Takhoffman closed this Mar 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Webchat renders NO_REPLY as visible “NO” (silent token leak)

3 participants