Skip to content

Gateway: hide NO_REPLY-only entries from chat.history#32082

Closed
liuxiaopai-ai wants to merge 3 commits intoopenclaw:mainfrom
liuxiaopai-ai:codex/webchat-hide-no-reply-history-32015
Closed

Gateway: hide NO_REPLY-only entries from chat.history#32082
liuxiaopai-ai wants to merge 3 commits intoopenclaw:mainfrom
liuxiaopai-ai:codex/webchat-hide-no-reply-history-32015

Conversation

@liuxiaopai-ai
Copy link

Summary

  • filter chat.history responses to drop assistant transcript entries that are exactly the silent token (NO_REPLY)
  • keep substantive assistant replies unchanged (only exact silent-token-only messages are removed)
  • add regression coverage for chat.history to ensure NO_REPLY entries do not appear in returned history

Fixes #32015.

Security Impact

  • None.

Repro + Verification

Repro (before)

  1. Create a session where transcript includes assistant entry with text/content exactly NO_REPLY.
  2. Call chat.history.
  3. Returned messages include NO_REPLY, which can render as visible bubble in Webchat.

Verification (after)

  • pnpm exec vitest run src/gateway/server.chat.gateway-server-chat.test.ts
  • pnpm lint src/gateway/server-methods/chat.ts src/gateway/server.chat.gateway-server-chat.test.ts CHANGELOG.md
  • pnpm tsgo

Human Verification

  • confirmed chat.history excludes assistant NO_REPLY-only messages
  • confirmed normal user and assistant messages remain in order

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: 8b9d06a68c

ℹ️ 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 +226 to +227
if (typed.type !== "text" || typeof typed.text !== "string") {
return undefined;

Choose a reason for hiding this comment

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

P2 Badge Handle output_text blocks in silent history filtering

isSilentAssistantHistoryMessage only treats content blocks with type === "text" as candidates, so assistant entries like content: [{ type: "output_text", text: "NO_REPLY" }] are not filtered and still leak into chat.history. This matters because the gateway/OpenAI paths already use output_text blocks, and downstream history rendering reads .text without requiring type: "text", so users can still see visible NO_REPLY bubbles in those sessions.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 2, 2026

Greptile Summary

This PR filters out assistant messages whose content is exactly the internal NO_REPLY silent token from chat.history responses, preventing them from rendering as visible bubbles in the Webchat UI. The implementation adds two helper functions (isSilentAssistantHistoryMessage and dropSilentAssistantHistoryMessages) that slot cleanly into the existing message-processing pipeline, and a regression test confirms the happy path.

Key findings:

  • Logic bug in isSilentAssistantHistoryMessage (src/gateway/server-methods/chat.ts, lines 211–216): when entry.text is a non-silent string (e.g., "Hello"), the first if block evaluates to false and execution falls through to the entry.content check. If entry.content is "NO_REPLY", the function incorrectly returns true and the message is dropped despite having real content in text. The guard should short-circuit on entry.text alone (see inline comment for suggested fix).
  • The test suite covers the array-of-blocks content format but does not exercise the text-field-plus-content="NO_REPLY" edge case exposed by the above bug.
  • CHANGELOG entry and pipeline ordering look correct.

Confidence Score: 3/5

  • Safe to merge for the primary scenario, but carries a logic bug that could silently drop messages with a real text field and a content of "NO_REPLY".
  • The feature itself is straightforward and the test covers the common case. However, the fall-through from the entry.text guard to the entry.content check is a genuine logic error: a message with substantive content in text but content = "NO_REPLY" would be incorrectly filtered out. This warrants a fix before merging to avoid subtle data-loss bugs in edge-case history entries.
  • src/gateway/server-methods/chat.ts — specifically isSilentAssistantHistoryMessage lines 211–216.

Last reviewed commit: 8b9d06a

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +211 to +216
if (typeof entry.text === "string" && isSilentReplyText(entry.text, SILENT_REPLY_TOKEN)) {
return true;
}
if (typeof entry.content === "string") {
return isSilentReplyText(entry.content, SILENT_REPLY_TOKEN);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Non-silent text field falls through to content check

When entry.text is a non-empty string that is not the silent token (e.g., "Hello"), the first if block evaluates to false and execution falls through to the entry.content check. If entry.content happens to be the string "NO_REPLY", the function returns true and the message is incorrectly dropped — even though the message carries real text in its text field.

The guard on entry.text should be a complete short-circuit: if a text field is present, its value alone should determine whether the message is silent, and the content field should not be consulted at all.

Suggested change
if (typeof entry.text === "string" && isSilentReplyText(entry.text, SILENT_REPLY_TOKEN)) {
return true;
}
if (typeof entry.content === "string") {
return isSilentReplyText(entry.content, SILENT_REPLY_TOKEN);
}
if (typeof entry.text === "string") {
return isSilentReplyText(entry.text, SILENT_REPLY_TOKEN);
}
if (typeof entry.content === "string") {
return isSilentReplyText(entry.content, SILENT_REPLY_TOKEN);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-methods/chat.ts
Line: 211-216

Comment:
**Non-silent `text` field falls through to `content` check**

When `entry.text` is a non-empty string that is *not* the silent token (e.g., `"Hello"`), the first `if` block evaluates to `false` and execution falls through to the `entry.content` check. If `entry.content` happens to be the string `"NO_REPLY"`, the function returns `true` and the message is incorrectly dropped — even though the message carries real text in its `text` field.

The guard on `entry.text` should be a complete short-circuit: if a `text` field is present, its value alone should determine whether the message is silent, and the `content` field should not be consulted at all.

```suggestion
  if (typeof entry.text === "string") {
    return isSilentReplyText(entry.text, SILENT_REPLY_TOKEN);
  }
  if (typeof entry.content === "string") {
    return isSilentReplyText(entry.content, SILENT_REPLY_TOKEN);
  }
```

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

@liuxiaopai-ai
Copy link
Author

Addressed, thanks for the catch.

I pushed a follow-up commit (d14048b) that fixes the precedence bug in isSilentAssistantHistoryMessage:

  • if entry.text is present, we now decide solely from entry.text (short-circuit) instead of falling through to entry.content
  • this prevents dropping messages that have real text but content: "NO_REPLY"

Also added a regression case in server.chat.gateway-server-chat.test.ts:

  • assistant message with text: "real text field reply" and content: "NO_REPLY" remains visible in chat.history

@openclaw-barnacle openclaw-barnacle bot added the channel: telegram Channel integration: telegram label Mar 2, 2026
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: d14048b7ce

ℹ️ 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".

return undefined;
}
const typed = block as { type?: unknown; text?: unknown };
if (typed.type !== "text" || typeof typed.text !== "string") {

Choose a reason for hiding this comment

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

P2 Badge Treat output_text blocks as silent in chat.history

isSilentAssistantHistoryMessage only keeps blocks with type === "text", so assistant entries like content: [{ type: "output_text", text: "NO_REPLY" }] are not filtered and still appear in chat.history. This is a regression for gateway/OpenAI-style transcripts because we already treat output_text as normal textual content elsewhere (see src/gateway/session-utils.fs.ts:379), so users can still get visible NO_REPLY bubbles in sessions that use those block types.

Useful? React with 👍 / 👎.

@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

app: web-ui App: web-ui channel: telegram Channel integration: telegram gateway Gateway runtime size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants