mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 06:42:44 +00:00
fix(cron): cancel timed-out runs before side effects (openclaw#22411) thanks @Takhoffman
Verified: - pnpm check - pnpm vitest run src/memory/qmd-manager.test.ts src/cron/service.issue-regressions.test.ts src/cron/isolated-agent.delivers-response-has-heartbeat-ok-but-includes.test.ts --maxWorkers=1 Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -731,6 +731,60 @@ describe("Cron issue regressions", () => {
|
||||
expect(job?.state.lastError).toContain("timed out");
|
||||
});
|
||||
|
||||
it("suppresses isolated follow-up side effects after timeout", async () => {
|
||||
vi.useRealTimers();
|
||||
const store = await makeStorePath();
|
||||
const scheduledAt = Date.parse("2026-02-15T13:00:00.000Z");
|
||||
const enqueueSystemEvent = vi.fn();
|
||||
|
||||
const cronJob = createIsolatedRegressionJob({
|
||||
id: "timeout-side-effects",
|
||||
name: "timeout side effects",
|
||||
scheduledAt,
|
||||
schedule: { kind: "every", everyMs: 60_000, anchorMs: scheduledAt },
|
||||
payload: { kind: "agentTurn", message: "work", timeoutSeconds: 0.01 },
|
||||
state: { nextRunAtMs: scheduledAt },
|
||||
});
|
||||
await writeCronJobs(store.storePath, [cronJob]);
|
||||
|
||||
let now = scheduledAt;
|
||||
const state = createCronServiceState({
|
||||
cronEnabled: true,
|
||||
storePath: store.storePath,
|
||||
log: noopLogger,
|
||||
nowMs: () => now,
|
||||
enqueueSystemEvent,
|
||||
requestHeartbeatNow: vi.fn(),
|
||||
runIsolatedAgentJob: vi.fn(async (params) => {
|
||||
const abortSignal = params.abortSignal;
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const onAbort = () => {
|
||||
abortSignal?.removeEventListener("abort", onAbort);
|
||||
now += 100;
|
||||
reject(new Error("aborted"));
|
||||
};
|
||||
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
||||
});
|
||||
return {
|
||||
status: "ok" as const,
|
||||
summary: "late-summary",
|
||||
delivered: false,
|
||||
error:
|
||||
abortSignal?.aborted && typeof abortSignal.reason === "string"
|
||||
? abortSignal.reason
|
||||
: undefined,
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
await onTimer(state);
|
||||
|
||||
const jobAfterTimeout = state.store?.jobs.find((j) => j.id === "timeout-side-effects");
|
||||
expect(jobAfterTimeout?.state.lastStatus).toBe("error");
|
||||
expect(jobAfterTimeout?.state.lastError).toContain("timed out");
|
||||
expect(enqueueSystemEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("applies timeoutSeconds to manual cron.run isolated executions", async () => {
|
||||
vi.useRealTimers();
|
||||
const store = await makeStorePath();
|
||||
|
||||
Reference in New Issue
Block a user