fix(infra): heartbeat runs honor timeoutSeconds 0 as unlimited (#119297)

* fix(infra): preserve timeoutSeconds 0 as unlimited for heartbeat runs

* test(infra): consolidate heartbeat timeout coverage

---------

Co-authored-by: Dallin Romney <dallinromney@gmail.com>
This commit is contained in:
Peter Lee
2026-08-10 03:21:14 -05:00
committed by GitHub
parent 7d4d02fff0
commit f8cfd54f41
2 changed files with 14 additions and 8 deletions
+4 -1
View File
@@ -52,7 +52,10 @@ export function resolveHeartbeatTimeoutOverrideSeconds(
typeof agentDefaultTimeoutSeconds === "number" &&
Number.isFinite(agentDefaultTimeoutSeconds)
) {
return Math.max(1, Math.floor(agentDefaultTimeoutSeconds));
// Preserve the unlimited sentinel consumed by resolveAgentTimeoutMs.
return agentDefaultTimeoutSeconds === 0
? 0
: Math.max(1, Math.floor(agentDefaultTimeoutSeconds));
}
// The wake dispatcher awaits heartbeat turns serially. Keep unset heartbeat
// timeouts tied to the cadence instead of the 48h built-in agent default.
@@ -227,13 +227,16 @@ describe("runHeartbeatOnce heartbeat model override", () => {
});
});
it("preserves explicit default agent timeout for heartbeat runs", async () => {
const replyOpts = await runDefaultsHeartbeat({ defaultTimeoutSeconds: 60, every: "30m" });
expectReplyOptions(replyOpts, {
isHeartbeat: true,
timeoutOverrideSeconds: 60,
});
});
it.each([0, 60])(
"preserves explicit default agent timeout %d for heartbeat runs",
async (defaultTimeoutSeconds) => {
const replyOpts = await runDefaultsHeartbeat({ defaultTimeoutSeconds, every: "30m" });
expectReplyOptions(replyOpts, {
isHeartbeat: true,
timeoutOverrideSeconds: defaultTimeoutSeconds,
});
},
);
it("passes bootstrapContextMode when heartbeat lightContext is enabled", async () => {
const replyOpts = await runDefaultsHeartbeat({ lightContext: true });