Skip to content

fix(webchat): filter NO_REPLY token from streaming and final replies (#16269)#16286

Merged
steipete merged 1 commit intoopenclaw:mainfrom
robbyczgw-cla:fix/16269-webchat-no-reply
Feb 14, 2026
Merged

fix(webchat): filter NO_REPLY token from streaming and final replies (#16269)#16286
steipete merged 1 commit intoopenclaw:mainfrom
robbyczgw-cla:fix/16269-webchat-no-reply

Conversation

@robbyczgw-cla
Copy link
Contributor

@robbyczgw-cla robbyczgw-cla commented Feb 14, 2026

Summary

The webchat channel sent NO_REPLY as visible text to WebSocket clients instead of suppressing it. Other channels (Telegram, Discord) already filter this token via the reply dispatcher, but the webchat streaming/outbound path bypassed this check.

Root Cause

In src/gateway/server-chat.ts, createAgentEventHandler() forwarded assistant stream text directly to webchat chat events without applying silent-token filtering:

  • emitChatDelta(...) sent state: \"delta\" payloads from evt.data.text as-is.
  • emitChatFinal(...) built final state: \"final\" message from chatRunState.buffers as-is.
  • Call path: agent event handler branch for evt.stream === \"assistant\" (emitChatDelta) and lifecycle end/error (emitChatFinal).

Because this path bypasses the normal reply dispatcher normalization, NO_REPLY could leak both in streaming updates and in the final chat payload.

Changes

  • Added silent-token filtering in webchat WS event path in src/gateway/server-chat.ts:
    • Imported isSilentReplyText and SILENT_REPLY_TOKEN from src/auto-reply/tokens.ts.
    • In emitChatDelta, suppress delta emission when streamed text is silent token.
    • In emitChatFinal, suppress final message when buffered text is silent token.
  • Added focused tests in src/gateway/server-chat.agent-events.test.ts:
    • verifies NO_REPLY is filtered in streaming (no delta broadcast/node send)
    • verifies NO_REPLY is filtered in final (final event emitted with message: undefined)

Testing

  • npm run lint
  • npm test -- src/gateway/server-chat.agent-events.test.ts
  • Attempted requested command npm test -- --testPathPattern=\"web\" ❌ (current Vitest CLI in this repo does not support --testPathPattern; returns Unknown option --testPathPattern)

Edge Cases Considered

  • Partial streaming chunks containing NO_REPLY
  • NO_REPLY with leading/trailing whitespace
  • HEARTBEAT_OK (handled separately by heartbeat visibility suppression logic)
  • Messages containing "NO_REPLY" as substring of legitimate text (not filtered by exact silent-token matcher)

🤖 AI-assisted (Robby + Cami) — verified bug in source, fix follows existing channel patterns

Greptile Overview

Greptile Summary

prevented NO_REPLY token from appearing in webchat stream deltas and final messages by adding isSilentReplyText checks in emitChatDelta and emitChatFinal

Key Changes

  • imported isSilentReplyText and SILENT_REPLY_TOKEN from src/auto-reply/tokens.ts
  • added silent token filter to streaming path (emitChatDelta) and final message path (emitChatFinal)
  • added focused tests verifying NO_REPLY is suppressed in both streaming and final events

Issue Found

  • emitChatDelta deletes the buffer on NO_REPLY, which breaks cumulative text tracking if more text streams after the silent token (see inline comment on src/gateway/server-chat.ts:232)

Confidence Score: 3/5

  • safe to merge after fixing the buffer deletion issue
  • the fix correctly addresses the NO_REPLY leak and follows existing channel patterns, but the buffer deletion in emitChatDelta will break text accumulation if streaming continues after the silent token
  • src/gateway/server-chat.ts requires attention - the buffer deletion on line 233 needs to be removed

Last reviewed commit: 6adf2eb

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.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +232 to +235
if (isSilentReplyText(text, SILENT_REPLY_TOKEN)) {
chatRunState.buffers.delete(clientRunId);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

deleting the buffer here means subsequent text won't accumulate correctly if streaming continues after NO_REPLY

the buffer stores the cumulative text for the run, so deleting it on NO_REPLY breaks future deltas. instead, just return early without modifying the buffer:

Suggested change
if (isSilentReplyText(text, SILENT_REPLY_TOKEN)) {
chatRunState.buffers.delete(clientRunId);
return;
}
if (isSilentReplyText(text, SILENT_REPLY_TOKEN)) {
return;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-chat.ts
Line: 232:235

Comment:
deleting the buffer here means subsequent text won't accumulate correctly if streaming continues after `NO_REPLY`

the buffer stores the cumulative text for the run, so deleting it on `NO_REPLY` breaks future deltas. instead, just return early without modifying the buffer:

```suggestion
    if (isSilentReplyText(text, SILENT_REPLY_TOKEN)) {
      return;
    }
```

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch — updated to return early without modifying the buffer.

@robbyczgw-cla robbyczgw-cla force-pushed the fix/16269-webchat-no-reply branch 2 times, most recently from 235b118 to 8b2e6cd Compare February 14, 2026 16:37
Copy link
Contributor Author

@robbyczgw-cla robbyczgw-cla left a comment

Choose a reason for hiding this comment

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

Good catch — updated to return early without deleting the buffer. Streaming continues correctly now.

@robbyczgw-cla robbyczgw-cla force-pushed the fix/16269-webchat-no-reply branch from 8b2e6cd to d29ca08 Compare February 14, 2026 16:38
The webchat channel sent NO_REPLY as visible text to clients instead
of suppressing it. Other channels (Telegram, Discord) already filter
this token via the reply dispatcher, but the webchat streaming path
bypassed this check.

Fixes openclaw#16269
@robbyczgw-cla robbyczgw-cla force-pushed the fix/16269-webchat-no-reply branch from d29ca08 to 33885cb Compare February 14, 2026 16:44
@steipete steipete merged commit baa3bf2 into openclaw:main Feb 14, 2026
23 checks passed
@steipete
Copy link
Contributor

Landed via squash onto main.

  • Gate: GitHub CI (checks node+bun tests, lint, protocol)
  • Merge commit: $(gh pr view 16286 -R openclaw/openclaw --json mergeCommit --jq '.mergeCommit.oid')

Thanks @robbyczgw-cla!

Swader pushed a commit to Swader/openclaw that referenced this pull request Feb 14, 2026
…penclaw#16286)

The webchat channel sent NO_REPLY as visible text to clients instead
of suppressing it. Other channels (Telegram, Discord) already filter
this token via the reply dispatcher, but the webchat streaming path
bypassed this check.

Fixes openclaw#16269
akoscz pushed a commit to akoscz/openclaw that referenced this pull request Feb 15, 2026
…penclaw#16286)

The webchat channel sent NO_REPLY as visible text to clients instead
of suppressing it. Other channels (Telegram, Discord) already filter
this token via the reply dispatcher, but the webchat streaming path
bypassed this check.

Fixes openclaw#16269
GwonHyeok pushed a commit to learners-superpumped/openclaw that referenced this pull request Feb 15, 2026
…penclaw#16286)

The webchat channel sent NO_REPLY as visible text to clients instead
of suppressing it. Other channels (Telegram, Discord) already filter
this token via the reply dispatcher, but the webchat streaming path
bypassed this check.

Fixes openclaw#16269
vincentkoc pushed a commit to vincentkoc/openclaw that referenced this pull request Feb 15, 2026
…penclaw#16286)

The webchat channel sent NO_REPLY as visible text to clients instead
of suppressing it. Other channels (Telegram, Discord) already filter
this token via the reply dispatcher, but the webchat streaming path
bypassed this check.

Fixes openclaw#16269
snowzlm pushed a commit to snowzlm/openclaw that referenced this pull request Feb 15, 2026
…penclaw#16286)

The webchat channel sent NO_REPLY as visible text to clients instead
of suppressing it. Other channels (Telegram, Discord) already filter
this token via the reply dispatcher, but the webchat streaming path
bypassed this check.

Fixes openclaw#16269
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
…penclaw#16286)

The webchat channel sent NO_REPLY as visible text to clients instead
of suppressing it. Other channels (Telegram, Discord) already filter
this token via the reply dispatcher, but the webchat streaming path
bypassed this check.

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

Labels

gateway Gateway runtime size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants