← Back to PRs

#19745: fix(heartbeat): enforce interval check regardless of trigger source

by misterdas open 2026-02-18 04:19 View on GitHub →
size: S
## Problem Heartbeat polls were firing every few seconds instead of the configured interval (e.g., 30 minutes). This was reported in issue #14440. ## Root Cause In `src/infra/heartbeat-runner.ts`, the interval check was: ```typescript if (isInterval && now < agent.nextDueMs) { ``` This only enforced the interval when the trigger reason was `"interval"` (timer-based). When triggered by other events like exec completions, cron wakes, or subagent completions, `isInterval` was `false`, causing the check to be bypassed entirely. ## Fix Remove the `isInterval &&` condition so the interval is always enforced: ```typescript if (now < agent.nextDueMs) { ``` This ensures the heartbeat only fires when the configured interval has elapsed, regardless of what triggers the check. ## Testing After applying this fix: - `every: "30m"` config is now respected - Exec completions no longer bypass the interval - Heartbeats fire at the configured interval, not on every activity event Fixes #14440 <!-- greptile_comment --> <h3>Greptile Summary</h3> Fixes a heartbeat scheduling bug where the interval check (`nextDueMs`) was only enforced for timer-based (`"interval"`) triggers. Non-interval triggers like exec completions and wake events bypassed the `isInterval &&` guard, causing heartbeats to fire on every activity event instead of at the configured interval. The one-line fix removes `isInterval &&` from the condition in the broadcast agent loop, so `now < agent.nextDueMs` is always enforced regardless of the trigger reason. - **Targeted wake requests are unaffected**: Triggers with `agentId`/`sessionKey` (e.g., cron jobs) use a separate code path (line 1025) that already bypasses this loop - **Behavioral change for exec-events**: `exec-event` triggers from `server-node-events.ts` and `bash-tools.exec-runtime.ts` do not include `agentId`/`sessionKey`, so they enter the broadcast loop and will now be subject to the interval check — exec results may be delayed until the next scheduled heartbeat (explicitly noted as intended in the PR description) - The `isInterval` variable on line 1020 is still used at line 1096 for the return reason, so it's not dead code <h3>Confidence Score: 4/5</h3> - This PR is safe to merge — the fix is minimal, targeted, and addresses a clear bug with excessive heartbeat polling. - Single-line change that correctly removes a faulty guard condition. The fix is well-motivated by the reported issue (#14440). Score is 4 rather than 5 because untargeted exec-event triggers will now be delayed until the next interval, which could impact user experience for async command results, and no tests were added for the specific scenario fixed. - No files require special attention — the change is a single condition removal in `src/infra/heartbeat-runner.ts`. <sub>Last reviewed commit: a793ae1</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs