Problem
When an agent has multiple active sessions (main binding, DM threads via dmPolicy: pairing, catch-all bindings, etc.), the heartbeat timer fires once per session instead of once per agent. All responses route to the :main session, resulting in duplicate HEARTBEAT_OK responses that waste tokens.
Observed behavior
Agent ben has ~7-12 active sessions. With heartbeat.every: "1h", each hourly cycle produces 5-12 separate heartbeat pings to session=agent:ben:main, each with a unique run ID, all within a 2-minute window.
Example from gateway.log at 2pm:
2026-03-27T14:06:39 session=agent:ben:main run=4b4d5226... HEARTBEAT_OK
2026-03-27T14:06:44 session=agent:ben:main run=395f3fae... HEARTBEAT_OK
2026-03-27T14:06:59 session=agent:ben:main run=a851ad73... HEARTBEAT_OK
... (12 total in this cycle)
Comparison across agents in the same cycle:
- Ben: 12 heartbeats (most sessions)
- Derek: 5 heartbeats
- Mary: 3 heartbeats
Config
{
"heartbeat": {
"every": "1h",
"target": "discord",
"to": "channel:..."
}
}
Combined with:
dmPolicy: "pairing"
session.dmScope: "per-channel-peer"
- Multiple bindings per agent (channel-specific + catch-all)
Expected behavior
Heartbeat should fire once per agent per interval, regardless of how many sessions that agent has.
Suggested fix
Add a scope option to heartbeat config:
{
"heartbeat": {
"every": "1h",
"scope": "agent" // default, fires once per agent
}
}
Or simply deduplicate at the gateway level — if agent X already received a heartbeat in this cycle, skip remaining sessions.
Impact
- Token waste: Each duplicate HEARTBEAT_OK costs tokens (model reads HEARTBEAT.md + system prompt each time)
- Confusion: HEARTBEAT.md conditionals may evaluate differently across rapid-fire runs
- Cost: At 12 pings/hour × 24h × multiple agents, this adds up
Environment
- OpenClaw version: 2026.3.23-2
- macOS 14.8.3
- Node 24.13.0
Problem
When an agent has multiple active sessions (main binding, DM threads via
dmPolicy: pairing, catch-all bindings, etc.), the heartbeat timer fires once per session instead of once per agent. All responses route to the:mainsession, resulting in duplicate HEARTBEAT_OK responses that waste tokens.Observed behavior
Agent
benhas ~7-12 active sessions. Withheartbeat.every: "1h", each hourly cycle produces 5-12 separate heartbeat pings tosession=agent:ben:main, each with a unique run ID, all within a 2-minute window.Example from gateway.log at 2pm:
Comparison across agents in the same cycle:
Config
{ "heartbeat": { "every": "1h", "target": "discord", "to": "channel:..." } }Combined with:
dmPolicy: "pairing"session.dmScope: "per-channel-peer"Expected behavior
Heartbeat should fire once per agent per interval, regardless of how many sessions that agent has.
Suggested fix
Add a
scopeoption to heartbeat config:{ "heartbeat": { "every": "1h", "scope": "agent" // default, fires once per agent } }Or simply deduplicate at the gateway level — if agent X already received a heartbeat in this cycle, skip remaining sessions.
Impact
Environment