-
-
Notifications
You must be signed in to change notification settings - Fork 52.6k
Closed as not planned
Closed as not planned
Copy link
Labels
bugSomething isn't workingSomething isn't workingstaleMarked as stale due to inactivityMarked as stale due to inactivity
Description
Summary
Heartbeat responses are being sent multiple times to users, creating duplicate/repetitive messages. A single heartbeat check can result in 2-4 messages delivered to the user instead of just one HEARTBEAT_OK.
Steps to reproduce
- Configure heartbeat with any model (tested with ministral-3b and gpt-4o-mini)
- Enable heartbeat runner with default settings
- Observe that a single heartbeat check sends multiple messages to the channel
Expected behavior
A heartbeat check should send exactly ONE message to the user:
- Either
HEARTBEAT_OKif nothing needs attention - Or a brief status update if something needs attention
Actual behavior
Multiple messages are sent due to several architectural issues:
- Followup Runner creates additional branches:
finalizeWithFollowup()inagent-runner.jsspawnsrunFollowup()which creates a new agent run with a differentparentId, generating additional response branches - Delivery-Mirror echoes messages: The transcript system records both the original message and a "delivery-mirror" entry, both appearing as assistant messages
- stripHeartbeatToken only strips edges: The function uses regex that only removes HEARTBEAT_OK at the start/end of text, not when embedded in the middle
- Multiple payloads from assistantTexts:
buildPiPayloads()can create multiple payloads from theassistantTextsarray
Root Cause Analysis
Files involved (from source investigation):
src/auto-reply/reply/agent-runner.ts- callsfinalizeWithFollowup()unconditionallysrc/auto-reply/reply/followup-runner.ts- spawns additional agent runssrc/auto-reply/heartbeat.ts-stripHeartbeatToken()uses edge-only regexsrc/config/sessions/transcript.ts- delivery-mirror mechanismsrc/agents/pi-embedded-runner/run/payloads.ts- builds multiple payloads
Proposed Fix
Option 1: Skip followup for heartbeat runs (Recommended)
In agent-runner.ts, add a check before calling finalizeWithFollowup():
// Skip followup for heartbeat - single response only
if (runContext.trigger !== 'heartbeat') {
await finalizeWithFollowup(...)
}Option 2: Add responsePolicy config
Add a responsePolicy: 'single' | 'multi' option to heartbeat config that enforces single-payload delivery.
Option 3: Fix stripHeartbeatToken to handle embedded tokens
// Current (broken for middle occurrences):
text.replace(/^HEARTBEAT_OK\s*|\s*HEARTBEAT_OK$/gi, '')
// Proposed (handles all occurrences):
text.replace(/HEARTBEAT_OK/gi, '').trim()Environment
- Clawdbot version: latest (npm)
- Node.js: 22+
- Channel: Telegram (but affects all channels)
- Model: Tested with ministral-3b-2512 and gpt-4o-mini
Workaround
Until fixed, users can mitigate by:
- Using a model with strong instruction-following (gpt-4o-mini recommended over smaller models)
- Simplifying HEARTBEAT.md to have strict, minimal instructions with explicit "do NOT" examples
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingstaleMarked as stale due to inactivityMarked as stale due to inactivity