mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 14:58:26 +00:00
refactor(reply): split abort cutoff and timeout policy modules
This commit is contained in:
49
src/cron/service/timeout-policy.test.ts
Normal file
49
src/cron/service/timeout-policy.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { CronJob } from "../types.js";
|
||||
import {
|
||||
AGENT_TURN_SAFETY_TIMEOUT_MS,
|
||||
DEFAULT_JOB_TIMEOUT_MS,
|
||||
resolveCronJobTimeoutMs,
|
||||
} from "./timeout-policy.js";
|
||||
|
||||
function makeJob(payload: CronJob["payload"]): CronJob {
|
||||
const sessionTarget = payload.kind === "agentTurn" ? "isolated" : "main";
|
||||
return {
|
||||
id: "job-1",
|
||||
name: "job",
|
||||
createdAtMs: 0,
|
||||
updatedAtMs: 0,
|
||||
enabled: true,
|
||||
schedule: { kind: "every", everyMs: 60_000 },
|
||||
sessionTarget,
|
||||
wakeMode: "next-heartbeat",
|
||||
payload,
|
||||
state: {},
|
||||
};
|
||||
}
|
||||
|
||||
describe("timeout-policy", () => {
|
||||
it("uses default timeout for non-agent jobs", () => {
|
||||
const timeout = resolveCronJobTimeoutMs(makeJob({ kind: "systemEvent", text: "hello" }));
|
||||
expect(timeout).toBe(DEFAULT_JOB_TIMEOUT_MS);
|
||||
});
|
||||
|
||||
it("uses expanded safety timeout for agentTurn jobs without explicit timeout", () => {
|
||||
const timeout = resolveCronJobTimeoutMs(makeJob({ kind: "agentTurn", message: "hi" }));
|
||||
expect(timeout).toBe(AGENT_TURN_SAFETY_TIMEOUT_MS);
|
||||
});
|
||||
|
||||
it("disables timeout when timeoutSeconds <= 0", () => {
|
||||
const timeout = resolveCronJobTimeoutMs(
|
||||
makeJob({ kind: "agentTurn", message: "hi", timeoutSeconds: 0 }),
|
||||
);
|
||||
expect(timeout).toBeUndefined();
|
||||
});
|
||||
|
||||
it("applies explicit timeoutSeconds when positive", () => {
|
||||
const timeout = resolveCronJobTimeoutMs(
|
||||
makeJob({ kind: "agentTurn", message: "hi", timeoutSeconds: 1.9 }),
|
||||
);
|
||||
expect(timeout).toBe(1_900);
|
||||
});
|
||||
});
|
||||
25
src/cron/service/timeout-policy.ts
Normal file
25
src/cron/service/timeout-policy.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { CronJob } from "../types.js";
|
||||
|
||||
/**
|
||||
* Maximum wall-clock time for a single job execution. Acts as a safety net
|
||||
* on top of per-provider/per-agent timeouts to prevent one stuck job from
|
||||
* wedging the entire cron lane.
|
||||
*/
|
||||
export const DEFAULT_JOB_TIMEOUT_MS = 10 * 60_000; // 10 minutes
|
||||
|
||||
/**
|
||||
* Agent turns can legitimately run much longer than generic cron jobs.
|
||||
* Use a larger safety ceiling when no explicit timeout is set.
|
||||
*/
|
||||
export const AGENT_TURN_SAFETY_TIMEOUT_MS = 60 * 60_000; // 60 minutes
|
||||
|
||||
export function resolveCronJobTimeoutMs(job: CronJob): number | undefined {
|
||||
const configuredTimeoutMs =
|
||||
job.payload.kind === "agentTurn" && typeof job.payload.timeoutSeconds === "number"
|
||||
? Math.floor(job.payload.timeoutSeconds * 1_000)
|
||||
: undefined;
|
||||
if (configuredTimeoutMs === undefined) {
|
||||
return job.payload.kind === "agentTurn" ? AGENT_TURN_SAFETY_TIMEOUT_MS : DEFAULT_JOB_TIMEOUT_MS;
|
||||
}
|
||||
return configuredTimeoutMs <= 0 ? undefined : configuredTimeoutMs;
|
||||
}
|
||||
@@ -18,6 +18,9 @@ import {
|
||||
import { locked } from "./locked.js";
|
||||
import type { CronEvent, CronServiceState } from "./state.js";
|
||||
import { ensureLoaded, persist } from "./store.js";
|
||||
import { DEFAULT_JOB_TIMEOUT_MS, resolveCronJobTimeoutMs } from "./timeout-policy.js";
|
||||
|
||||
export { DEFAULT_JOB_TIMEOUT_MS } from "./timeout-policy.js";
|
||||
|
||||
const MAX_TIMER_DELAY_MS = 60_000;
|
||||
|
||||
@@ -30,14 +33,6 @@ const MAX_TIMER_DELAY_MS = 60_000;
|
||||
*/
|
||||
const MIN_REFIRE_GAP_MS = 2_000;
|
||||
|
||||
/**
|
||||
* Maximum wall-clock time for a single job execution. Acts as a safety net
|
||||
* on top of the per-provider / per-agent timeouts to prevent one stuck job
|
||||
* from wedging the entire cron lane.
|
||||
*/
|
||||
export const DEFAULT_JOB_TIMEOUT_MS = 10 * 60_000; // 10 minutes
|
||||
const AGENT_TURN_SAFETY_TIMEOUT_MS = 60 * 60_000; // 60 minutes
|
||||
|
||||
type TimedCronRunOutcome = CronRunOutcome &
|
||||
CronRunTelemetry & {
|
||||
jobId: string;
|
||||
@@ -47,17 +42,6 @@ type TimedCronRunOutcome = CronRunOutcome &
|
||||
endedAt: number;
|
||||
};
|
||||
|
||||
function resolveCronJobTimeoutMs(job: CronJob): number | undefined {
|
||||
const configuredTimeoutMs =
|
||||
job.payload.kind === "agentTurn" && typeof job.payload.timeoutSeconds === "number"
|
||||
? Math.floor(job.payload.timeoutSeconds * 1_000)
|
||||
: undefined;
|
||||
if (configuredTimeoutMs === undefined) {
|
||||
return job.payload.kind === "agentTurn" ? AGENT_TURN_SAFETY_TIMEOUT_MS : DEFAULT_JOB_TIMEOUT_MS;
|
||||
}
|
||||
return configuredTimeoutMs <= 0 ? undefined : configuredTimeoutMs;
|
||||
}
|
||||
|
||||
export async function executeJobCoreWithTimeout(
|
||||
state: CronServiceState,
|
||||
job: CronJob,
|
||||
|
||||
Reference in New Issue
Block a user