Skip to content

HEARTBEAT_OK no-op exchanges accumulate in session context, wasting tokens #11140

@VentifactBot

Description

@VentifactBot

Problem

When a heartbeat fires and the agent responds with HEARTBEAT_OK (no actionable tasks), the full heartbeat prompt + response pair is persisted in the session transcript:

user: "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly..."  (~30 tokens)
assistant: "HEARTBEAT_OK"  (~5 tokens)

These no-op exchanges accumulate over time. With the default 30-minute heartbeat interval, this adds ~1,700 tokens/day of zero-information-content messages to the session history.

Impact

  • Context waste: In long-running sessions (e.g., Discord DMs that persist for days), hundreds of HEARTBEAT_OK pairs consume significant context window space.
  • Compaction inefficiency: When compaction triggers, it must summarize these no-op exchanges alongside real conversation. The compaction summary ends up including noise like "the agent checked heartbeat multiple times and found nothing to do."
  • Contributes to context overflow: Combined with other context growth, heartbeat accumulation can push sessions toward the context limit faster than necessary, especially on models with smaller context windows (e.g., 200k).

Expected Behavior

HEARTBEAT_OK exchanges where no action was taken should either:

  1. Be stripped from the session transcript after delivery (they have been processed, there is no reason to keep them in context), or
  2. Be marked as pruneable so compaction can drop them entirely rather than summarizing them, or
  3. Not be persisted to the transcript at all when the response is purely HEARTBEAT_OK with no other content.

Current Behavior

The stripHeartbeatToken function strips the HEARTBEAT_OK token from the reply text before delivery, but the full user prompt + assistant response pair remains in the session transcript and is included in future LLM context.

Suggested Fix

Before appending a heartbeat turn to the session transcript, check if the response is effectively just HEARTBEAT_OK (using the existing isHeartbeatContentEffectivelyEmpty or similar). If so, skip persisting both the heartbeat prompt and the response to the transcript.

Alternatively, add a post-hoc pruning step that removes completed HEARTBEAT_OK pairs from the message history before sending context to the LLM.

Environment

  • OpenClaw 2026.2.x
  • Any model/provider
  • Heartbeat interval: 30m (default)
  • Long-running sessions (multi-day)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstaleMarked as stale due to inactivity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions