#10677: fix(heartbeat): mirror outbound messages to session transcript
stale
Cluster:
Heartbeat Message Filtering
## Problem
Heartbeat-initiated outbound messages were not being mirrored back into the session transcript, causing context confusion when users replied to heartbeat messages. The agent had no memory of what it sent.
### Reproduction scenario:
1. Heartbeat triggers agent to send a proactive message (e.g., "was machst du grad?")
2. User receives the message via Telegram/etc
3. User replies to that message
4. Agent has no context about the original message it sent
5. Agent is confused about what the user is replying to
## Fix
This adds the `mirror` parameter to the `deliverOutboundPayloads` call in `runHeartbeatOnce`, matching the pattern established by #1031 for other delivery paths (routeReply, message tool, gateway send API).
The `mirror` parameter was already implemented and working for other delivery paths — heartbeat just wasn't using it.
## Changes
- Added `mirror` parameter to the main heartbeat delivery call
- Only mirrors when there's actual content to send (`shouldSkipMain` is false)
- Passes `sessionKey`, `agentId`, `text`, and `mediaUrls` for proper transcript recording
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
- Updates the heartbeat delivery path to pass `mirror` options into `deliverOutboundPayloads`, so outbound heartbeat messages are also appended to the session transcript.
- Mirrors `sessionKey`/`agentId` plus the heartbeat’s normalized text and media URLs, aligning heartbeat behavior with other outbound delivery paths.
- Avoids mirroring when the main heartbeat message is skipped (`shouldSkipMain`).
<h3>Confidence Score: 4/5</h3>
- This PR is likely safe to merge, but has a functional gap for heartbeats that deliver reasoning-only messages.
- Change is small and follows existing mirroring mechanism, but the mirroring payload does not reflect all delivered outbound heartbeat content when `includeReasoning` is enabled, so the stated context-confusion bug can still occur in that mode.
- src/infra/heartbeat-runner.ts
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#19486: fix(gateway): mirror delivered agent replies into routed target ses...
by kite010kite · 2026-02-17
84.2%
#17340: fix(auto-reply): prevent duplicate transcript entries for followup ...
by Facens · 2026-02-15
78.7%
#13524: feat: conditional bootstrap file loading for heartbeat vs DM sessions
by tarun131313 · 2026-02-10
74.9%
#11647: fix(webchat): filter HEARTBEAT_OK messages from chat.history response
by liuxiaopai-ai · 2026-02-08
74.2%
#11661: fix: Filter HEARTBEAT_OK from chat.history when showOk is false
by veast · 2026-02-08
74.0%
#14241: fix(heartbeat): propagate originating session key for exec event qu...
by aldoeliacim · 2026-02-11
73.7%
#21682: fix(heartbeat): propagate sessionKey in exec/hooks to fix async con...
by eviaaaaa · 2026-02-20
73.7%
#23759: fix: prevent heartbeat/internal providers from corrupting session l...
by kami-saia · 2026-02-22
73.5%
#19406: fix(heartbeat): filter error payloads from heartbeat reply selection
by namabile · 2026-02-17
73.2%
#12786: fix: drop heartbeat runs that arrive while another run is active
by mcaxtr · 2026-02-09
73.1%