← Back to PRs

#19270: fix: retry event-driven heartbeats blocked by requests-in-flight

by deggertsen open 2026-02-17 16:02 View on GitHub →
size: XS
## Summary When exec completion, cron, wake, or hook events trigger a heartbeat but the main command lane is busy, the heartbeat was skipped with `requests-in-flight`. The system event text sat in context unprocessed until the next user message or scheduled heartbeat. ## The Fix Exempt event-driven heartbeat reasons (`exec:*`, `cron:*`, `wake`, `hook:*`) from the `requests-in-flight` skip. The heartbeat enqueues a turn in the command lane, which naturally executes after the current one finishes. No retry loops or timers needed. Also fixes a secondary bug: the notify-on-exit handler uses reason format `exec:<id>:exit` but the heartbeat runner only matched exact string `"exec-event"` — so exec completions weren't recognized as exec events for the specialized prompt path either. Now matches both via `startsWith('exec:')`. ## Changes - `src/infra/heartbeat-runner.ts`: Move event-driven reason detection above queue-size check; add `!isEventDriven` exemption; consolidate `isEventDriven` flag for reuse in empty-heartbeat-file check; fix exec reason matching to include `exec:*` prefix. - `src/infra/heartbeat-runner.scheduler.test.ts`: Test that event-driven wake requests are dispatched to runOnce. **2 files changed, +42 −10** ## Related - #8997 — exec completion notifications and agent turn interaction - #14527 — system events silently skipped - [Community report](https://www.answeroverflow.com/m/1469119489009254589) — agents not responding to exec completion events ## Reproduction 1. Start a background exec task (`background: true`) 2. Chat with the agent while the background task runs 3. Let the task finish while agent is mid-turn 4. **Before:** System event in context, agent never responds 5. **After:** Heartbeat goes through, agent processes the event after current turn <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes event-driven heartbeats (exec completions, cron events, wake, hooks) being silently skipped when the main command lane is busy (`requests-in-flight`). Previously, the system event text would sit unprocessed in context until the next scheduled heartbeat or user message. - Moves event-driven reason detection (`exec:*`, `cron:*`, `wake`, `hook:*`) above the queue-size check and exempts these reasons from the `requests-in-flight` skip - Fixes a secondary bug: exec reason matching now recognizes both the legacy `"exec-event"` format (from `server-node-events.ts`) and the `exec:<id>:exit` format (from `bash-tools.exec-runtime.ts` notify-on-exit handler) - Consolidates `isEventDriven` flag to also exempt event-driven reasons from the empty-heartbeat-file skip, replacing the previous three separate boolean checks - Adds a scheduler-level test for the event-driven wake path <h3>Confidence Score: 4/5</h3> - This PR is safe to merge — it fixes a clear bug with a minimal, well-scoped change and includes a test. - The logic change is straightforward and correct: event-driven heartbeat reasons are exempted from the requests-in-flight skip by detecting them earlier and adding a guard. The exec reason matching fix addresses a real inconsistency between two callsites. The only minor gap is that isActionWakeReason in heartbeat-wake.ts was not updated for exec:* prefixes (pre-existing, low practical impact). - No files require special attention. Consider also updating isActionWakeReason in src/infra/heartbeat-wake.ts for consistency. <sub>Last reviewed commit: 716a503</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs