#11859: fix: filter HEARTBEAT_OK messages from chat.history when showOk is false
app: web-ui
gateway
stale
Cluster:
Heartbeat Message Filtering
## Summary
Fixes #11630.
`chat.history` returns raw transcript messages without heartbeat filtering, causing HEARTBEAT_OK messages to appear in the control-ui chat tab after page refresh — even though the live broadcast path correctly suppresses them via `shouldSuppressHeartbeatBroadcast()`.
## Changes
### `src/gateway/chat-sanitize.ts`
- Added `filterHeartbeatOkMessages()` that identifies and removes HEARTBEAT_OK response messages and their corresponding heartbeat prompt messages from the message array
- Detection uses content-based matching (checks for `HEARTBEAT_TOKEN` in assistant messages and `HEARTBEAT_PROMPT` in user messages), consistent with how `stripHeartbeatToken()` in `heartbeat.ts` handles markup normalization
- Heartbeat runs that produced actual content (alerts) are preserved
### `src/gateway/server-methods/chat.ts`
- After sanitization, applies `filterHeartbeatOkMessages()` when `resolveHeartbeatVisibility()` returns `showOk: false` for the webchat channel
- When `showOk` is true, no filtering is applied (all messages returned as before)
### `src/gateway/chat-sanitize.test.ts`
- 8 new test cases covering: prompt+response pair removal, standalone HEARTBEAT_OK removal, alert preservation, markdown markup wrapping, content array format, identity reference preservation, empty input, and multiple heartbeat pairs
## Design Notes
- The heartbeat prompt and HEARTBEAT_OK response are identified by content matching rather than metadata flags, since `isHeartbeat` is a runtime `AgentRunContext` property that is not persisted to the JSONL transcript
- The markup stripping in `isHeartbeatOkResponse()` mirrors the edge-stripping approach in `heartbeat.ts` (`stripHeartbeatToken`), removing `*`, backtick, `~` wrappers and HTML tags while preserving underscores in the token itself
- Filtering is applied after `stripEnvelopeFromMessages()` but before `capArrayByJsonBytes()`, so byte budget calculation uses the filtered (smaller) set
## Testing
```
✓ src/gateway/chat-sanitize.test.ts (12 tests) 4ms
✓ src/infra/heartbeat-visibility.test.ts (13 tests) 3ms
✓ src/auto-reply/heartbeat.test.ts (20 tests) 4ms
```
---
感谢 @AnthonyFrancis 提交了清晰的 bug report!
Thanks @AnthonyFrancis for the clear bug report!
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adds transcript-side filtering of heartbeat noise in `chat.history`: after stripping envelope headers, it optionally removes HEARTBEAT prompt/HEARTBEAT_OK pairs when heartbeat visibility resolves to `showOk: false`, and introduces unit tests around the new filter.
The change plugs a gap where live broadcast already suppressed HEARTBEAT_OK but history replay returned raw transcript messages, causing the control-ui chat tab to show HEARTBEAT_OK on refresh.
<h3>Confidence Score: 3/5</h3>
- Moderately safe, but two logic issues can cause the bug to persist or change behavior for non-webchat consumers.
- Core idea and tests look reasonable, but HEARTBEAT_OK detection diverges from existing markup normalization (misses underscore emphasis), and `chat.history` now applies webchat heartbeat visibility unconditionally which can unintentionally filter history for other clients/tools.
- src/gateway/chat-sanitize.ts and src/gateway/server-methods/chat.ts
<!-- 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
#11647: fix(webchat): filter HEARTBEAT_OK messages from chat.history response
by liuxiaopai-ai · 2026-02-08
93.6%
#11889: fix(chat): filter HEARTBEAT_OK messages in chat.history when showOk...
by bendyclaw · 2026-02-08
91.8%
#11661: fix: Filter HEARTBEAT_OK from chat.history when showOk is false
by veast · 2026-02-08
90.9%
#12774: fix: webchat heartbeat should respect showAlerts config
by a2093930 · 2026-02-09
85.6%
#12240: fix: suppress heartbeat agent events from webchat broadcast
by Yida-Dev · 2026-02-09
84.0%
#8334: fix(webchat): Filter NO_REPLY messages from chat history
by vishaltandale00 · 2026-02-03
81.4%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
79.4%
#15575: fix(heartbeat): suppress prefixed HEARTBEAT_OK ack replies (#15505)
by TsekaLuk · 2026-02-13
79.0%
#17371: fix(heartbeat): always strip HEARTBEAT_OK token from reply text
by BinHPdev · 2026-02-15
78.5%
#8742: fix(webchat): hide internal system messages from UI (#7440)
by revenuestack · 2026-02-04
78.4%