#19916: fix: strict silent-reply detection to prevent false positives with CJK text
size: S
## Summary
**Problem:** `isSilentReplyText()` uses `\b` and `\W` regex anchors to detect silent reply tokens (NO_REPLY, HEARTBEAT_OK). In JavaScript, `\W` matches any non-`[a-zA-Z0-9_]` character — including all CJK (Chinese/Japanese/Korean) characters. This causes any assistant reply containing `NO_REPLY` followed by CJK text to be falsely detected as a silent reply and silently swallowed.
**Why it matters:** CJK-language users lose messages. The assistant generates a valid reply mentioning NO_REPLY in context, but it never gets delivered. This is a data-loss bug affecting all CJK locales across all channels (Discord, Telegram, Slack, etc.).
**What changed:** Replace the regex-based detection with strict `text.trim() === token` comparison. The system prompt already instructs the model to reply with ONLY `NO_REPLY` as the entire message, so partial matching is unnecessary and harmful.
## Changes
- `src/auto-reply/tokens.ts`: Replace regex with `text.trim() === token` (7 lines removed, 1 added)
- `src/auto-reply/tokens.test.ts`: New test file with 10 cases covering exact match, whitespace, CJK, English sentences, substrings, and custom tokens
## Validation Evidence
Tested locally with 18 cases before this PR (on a running OpenClaw instance):
```
✅ "NO_REPLY" → true
✅ " NO_REPLY " → true
✅ "NO_REPLY 这是中文消息" → false (was: true — the bug)
✅ "中文消息 NO_REPLY" → false (was: true)
✅ "中文NO_REPLY消息" → false (was: true)
✅ "Here is NO_REPLY for you" → false
✅ undefined → false
✅ "" → false
```
## Security Impact
- **Risk:** None
- No new dependencies, no network changes, no permission changes
- Strictly reduces false positives (fewer messages silently dropped)
## Privacy and Data Hygiene
- No PII involved
- Fix *prevents* message loss (improves data delivery)
## Rollback Plan
- Revert single commit to restore regex-based detection
- No migrations or config changes
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixes a data-loss bug where `isSilentReplyText()` used `\b` and `\W` regex anchors that matched CJK characters as word boundaries, causing valid assistant replies containing `NO_REPLY` alongside CJK text to be silently swallowed. The fix replaces the regex with a strict `text.trim() === token` equality check, which aligns with the system prompt's instruction that `NO_REPLY` must be the model's entire message.
- **Bug fix**: `\W` in JavaScript matches all non-ASCII characters including CJK, so `NO_REPLY` followed by Chinese/Japanese/Korean text was falsely detected as a silent reply
- **Approach**: Strict equality after trimming — simple, correct, and well-aligned with the system prompt contract ("It must be your ENTIRE message — nothing else")
- **Tests**: New test file with 10 cases covering exact match, whitespace, CJK text, English sentences, substrings, and custom tokens
- **Minor issue**: Unused `escapeRegExp` import left in `tokens.ts` (line 1) — should be removed to pass linting
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge after removing the unused import — it's a well-scoped bug fix with good test coverage.
- The logic change is correct and well-justified: strict equality matching aligns with the system prompt contract. The only issue is a leftover unused import that will likely fail linting. All 14 call sites were reviewed and none rely on the old partial-matching behavior.
- `src/auto-reply/tokens.ts` has an unused `escapeRegExp` import on line 1 that needs to be removed.
<sub>Last reviewed commit: 6ff6717</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#8504: fix: prevent false positives in isSilentReplyText for CJK content
by hanxiao · 2026-02-04
89.6%
#19576: fix: tighten isSilentReplyText to match whole-text only
by aldoeliacim · 2026-02-18
86.3%
#23761: fix: suppress partial NO_REPLY tokens at lifecycle boundary
by kami-saia · 2026-02-22
81.3%
#19648: fix: suppress silent-reply partial tokens during streaming
by bradleypriest · 2026-02-18
79.8%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
79.4%
#17244: fix: strip TTS tags from agent replies before delivery (#14652)
by robbyczgw-cla · 2026-02-15
78.9%
#8493: fix(tui): filter NO_REPLY token from chat display
by gavinbmoore · 2026-02-04
78.7%
#15118: Fix webchat ghost bubble when model replies with NO_REPLY
by jwchmodx · 2026-02-13
77.7%
#16096: fix(i18n): use Unicode-aware word boundaries for non-ASCII language...
by PeterRosdahl · 2026-02-14
77.1%
#21462: fix(agents): hold back partial NO_REPLY token in pi-embedded streaming
by algal · 2026-02-20
75.8%