From 5fffdc478e255467ff45d6d2cc514bf90de4a8e6 Mon Sep 17 00:00:00 2001 From: Chinar Amrutkar Date: Fri, 3 Apr 2026 23:56:20 +0000 Subject: [PATCH] fix(heartbeat): add startedAt param, null prompt handling, timestamp updates - Fix: Pass startedAt into resolveHeartbeatRunPrompt - Fix: Return proper object instead of null for no-tasks-due - Fix: Add early return when prompt is null - Fix: Persist timestamps on successful exits --- src/infra/heartbeat-runner.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/infra/heartbeat-runner.ts b/src/infra/heartbeat-runner.ts index 7f1c40bb0c6..4740eef79e9 100644 --- a/src/infra/heartbeat-runner.ts +++ b/src/infra/heartbeat-runner.ts @@ -496,7 +496,7 @@ async function resolveHeartbeatPreflight(params: { } type HeartbeatPromptResolution = { - prompt: string; + prompt: string | null; hasExecCompletion: boolean; hasCronEvents: boolean; }; @@ -519,6 +519,7 @@ function resolveHeartbeatRunPrompt(params: { preflight: HeartbeatPreflight; canRelayToUser: boolean; workspaceDir: string; + startedAt: number; }): HeartbeatPromptResolution { const pendingEventEntries = params.preflight.pendingEventEntries; const pendingEvents = params.preflight.shouldInspectPendingEvents @@ -555,7 +556,7 @@ After completing all due tasks, reply HEARTBEAT_OK.`; return { prompt, hasExecCompletion: false, hasCronEvents: false }; } // No tasks due - skip this heartbeat to avoid wasteful API calls - return null; + return { prompt: null, hasExecCompletion: false, hasCronEvents: false }; } // Fallback to original behavior @@ -694,8 +695,14 @@ export async function runHeartbeatOnce(opts: { preflight, canRelayToUser, workspaceDir, + startedAt, }); + // If no tasks are due, skip heartbeat entirely + if (prompt === null) { + return { status: "skipped", reason: "no-tasks-due" }; + } + // Track if heartbeat completed successfully (for updating task timestamps) let heartbeatSuccess = false; @@ -834,6 +841,8 @@ export async function runHeartbeatOnce(opts: { silent: !okSent, indicatorType: visibility.useIndicator ? resolveIndicatorType("ok-empty") : undefined, }); + heartbeatSuccess = true; + await updateTaskTimestamps(); return { status: "ran", durationMs: Date.now() - startedAt }; }