Description
Heartbeat prompts are being injected into active sub-agent sessions (agent:<id>:subagent:<uuid>), causing the sub-agent to respond HEARTBEAT_OK instead of completing its actual task. This effectively kills any sub-agent run that spans a heartbeat interval.
Steps to Reproduce
- Configure heartbeat at 1hr interval (
agents.defaults.heartbeat.every: "1h")
- Spawn a sub-agent via
sessions_spawn with a task that takes >10 minutes (e.g., building a price cache from yfinance, embedding 175K documents into LanceDB)
- Wait for the next heartbeat to fire
- Observe: the heartbeat prompt is injected into the sub-agent session
- The sub-agent responds
HEARTBEAT_OK
- The announce system picks up
HEARTBEAT_OK as the sub-agent result
- The actual task work is lost
Evidence
Sub-agent session transcript shows the heartbeat injection as the final exchange:
seq 36 (user): "System: [2026-04-04 14:31:58 UTC] Exec completed (calm-ree, code 0) :: ...
Read HEARTBEAT.md if it exists (workspace context). Follow it strictly..."
seq 37 (assistant): "HEARTBEAT_OK"
This happened to 3 separate sub-agent sessions in the same hour:
max-scorecard-v2 — was building a directional call scorecard (task completed but announce returned HEARTBEAT_OK)
max-vectordb-v2 — was embedding 175K docs into LanceDB (task completed but announce returned HEARTBEAT_OK)
sage-token-optimization — was researching token optimization strategies (stalled after heartbeat injection at ~28 min)
All three were running on anthropic/claude-sonnet-4-6 via sessions_spawn with mode: "run".
Expected Behavior
Heartbeat prompts should never be injected into sub-agent sessions (agent:*:subagent:*). Heartbeats are a main-session concern. Sub-agents have their own task context and should run to completion uninterrupted.
Environment
- OpenClaw version: 2026.4.1 (upgraded to 2026.4.2 after observing this)
- OS: Linux 6.8.0-101-generic (x64), Node v22.22.0
- Model: anthropic/claude-opus-4-6 (main), anthropic/claude-sonnet-4-6 (sub-agents)
- Channel: Signal
- Config:
agents.defaults.heartbeat.every: "1h", direct Anthropic API key auth
Related Issues
Suggested Fix
The heartbeat runner should skip any session matching the sub-agent session key pattern (agent:*:subagent:*). Heartbeat should only target the main session (agent:*:main).
Workaround
Extending heartbeat interval to 2hr+ and setting agents.defaults.subagents.runTimeoutSeconds: 600 reduces the collision window but does not eliminate it.
Description
Heartbeat prompts are being injected into active sub-agent sessions (
agent:<id>:subagent:<uuid>), causing the sub-agent to respondHEARTBEAT_OKinstead of completing its actual task. This effectively kills any sub-agent run that spans a heartbeat interval.Steps to Reproduce
agents.defaults.heartbeat.every: "1h")sessions_spawnwith a task that takes >10 minutes (e.g., building a price cache from yfinance, embedding 175K documents into LanceDB)HEARTBEAT_OKHEARTBEAT_OKas the sub-agent resultEvidence
Sub-agent session transcript shows the heartbeat injection as the final exchange:
This happened to 3 separate sub-agent sessions in the same hour:
max-scorecard-v2— was building a directional call scorecard (task completed but announce returned HEARTBEAT_OK)max-vectordb-v2— was embedding 175K docs into LanceDB (task completed but announce returned HEARTBEAT_OK)sage-token-optimization— was researching token optimization strategies (stalled after heartbeat injection at ~28 min)All three were running on
anthropic/claude-sonnet-4-6viasessions_spawnwithmode: "run".Expected Behavior
Heartbeat prompts should never be injected into sub-agent sessions (
agent:*:subagent:*). Heartbeats are a main-session concern. Sub-agents have their own task context and should run to completion uninterrupted.Environment
agents.defaults.heartbeat.every: "1h", direct Anthropic API key authRelated Issues
Suggested Fix
The heartbeat runner should skip any session matching the sub-agent session key pattern (
agent:*:subagent:*). Heartbeat should only target the main session (agent:*:main).Workaround
Extending heartbeat interval to 2hr+ and setting
agents.defaults.subagents.runTimeoutSeconds: 600reduces the collision window but does not eliminate it.