Problem
When a subagent runs background exec commands (e.g. background: true), each exec completion triggers requestHeartbeatNow({reason: "exec-event"}). The heartbeat runner's resolveHeartbeatSession falls back subagent session keys to the main session, causing spurious heartbeat wake-ups on the parent agent.
This results in:
- Frequent unnecessary LLM calls (one per subagent exec completion)
- Spurious
HEARTBEAT_OK messages appearing in the main session (visible in WebChat)
- Wasted API tokens
Reproduction
- Open WebChat for the main agent
- Spawn a subagent that runs multiple background exec commands:
sessions_spawn(task: "...", mode: "run")
- Observe the main session receiving HEARTBEAT_OK responses every 60-90 seconds
Root Cause
In src/agents/bash-tools.exec-runtime.ts, both maybeNotifyOnExit() and emitExecSystemEvent() unconditionally call:
requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" }));
In src/infra/heartbeat-runner.ts, resolveHeartbeatSession() (line 253) falls back subagent session keys:
if (!trimmed || isSubagentSessionKey(trimmed)) {
return { sessionKey: mainSessionKey, ... };
}
This is redundant because:
- Subagents receive exec results via normal
process poll — they are actively waiting
- Subagent completion is communicated to the parent via
subagent_announce
- The heartbeat provides no value for subagent sessions
Proposed Fix
Guard the requestHeartbeatNow calls with isSubagentSessionKey:
if (!isSubagentSessionKey(sessionKey)) {
requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" }));
}
PR incoming.
Problem
When a subagent runs background exec commands (e.g.
background: true), each exec completion triggersrequestHeartbeatNow({reason: "exec-event"}). The heartbeat runner'sresolveHeartbeatSessionfalls back subagent session keys to the main session, causing spurious heartbeat wake-ups on the parent agent.This results in:
HEARTBEAT_OKmessages appearing in the main session (visible in WebChat)Reproduction
Root Cause
In
src/agents/bash-tools.exec-runtime.ts, bothmaybeNotifyOnExit()andemitExecSystemEvent()unconditionally call:In
src/infra/heartbeat-runner.ts,resolveHeartbeatSession()(line 253) falls back subagent session keys:This is redundant because:
process poll— they are actively waitingsubagent_announceProposed Fix
Guard the
requestHeartbeatNowcalls withisSubagentSessionKey:PR incoming.