fix(heartbeat): bypass empty-file check for cron-triggered wakes

Cron reminders were silently skipped when heartbeat.md had no actionable
content. Now cron: and exec-event reasons both bypass the empty-file
guard so scheduled reminders always reach the agent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jiang Bohan 2026-02-09 15:08:04 +08:00
parent 94d6e15117
commit 19fed71f09
2 changed files with 14 additions and 2 deletions

View file

@ -65,6 +65,18 @@ describe("heartbeat runner", () => {
}
});
it("bypasses empty-heartbeat-file check for cron-triggered wakes", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "heartbeat-test-"));
try {
await writeFile(path.join(dir, "heartbeat.md"), "# keep empty\n", "utf-8");
const agent = createStubAgent({ profileDir: dir, replyText: "HEARTBEAT_OK" });
const result = await runHeartbeatOnce({ agent: agent as any, reason: "cron:test-job-id" });
expect(result.status).toBe("ran");
} finally {
await rm(dir, { recursive: true, force: true });
}
});
it("runs and returns ran for heartbeat acknowledgements", async () => {
const agent = createStubAgent({ replyText: "HEARTBEAT_OK" });
const result = await runHeartbeatOnce({ agent: agent as any, reason: "manual" });

View file

@ -156,8 +156,8 @@ export async function runHeartbeatOnce(opts: {
}
try {
const isExecEvent = opts.reason === "exec-event";
if (!isExecEvent && (await isHeartbeatFileEmpty(agent))) {
const isForcedWake = opts.reason === "exec-event" || opts.reason?.startsWith("cron:");
if (!isForcedWake && (await isHeartbeatFileEmpty(agent))) {
emitHeartbeatEvent({
status: "skipped",
reason: "empty-heartbeat-file",