fix(cron): reject sessionTarget "main" for non-default agents at creation time (openclaw#30217) thanks @liaosvcaf

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: liaosvcaf <51533973+liaosvcaf@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
C. Liao
2026-03-01 17:54:53 -08:00
committed by GitHub
parent e70fc5eb62
commit 313a655d13
5 changed files with 130 additions and 30 deletions

View File

@@ -257,14 +257,105 @@ describe("applyJobPatch", () => {
});
});
function createMockState(now: number): CronServiceState {
function createMockState(now: number, opts?: { defaultAgentId?: string }): CronServiceState {
return {
deps: {
nowMs: () => now,
defaultAgentId: opts?.defaultAgentId,
},
} as unknown as CronServiceState;
}
describe("createJob rejects sessionTarget main for non-default agents", () => {
const now = Date.parse("2026-02-28T12:00:00.000Z");
const mainJobInput = (agentId?: string) => ({
name: "my-main-job",
enabled: true,
schedule: { kind: "every" as const, everyMs: 60_000 },
sessionTarget: "main" as const,
wakeMode: "now" as const,
payload: { kind: "systemEvent" as const, text: "tick" },
...(agentId !== undefined ? { agentId } : {}),
});
it("allows creating a main-session job for the default agent", () => {
const state = createMockState(now, { defaultAgentId: "main" });
expect(() => createJob(state, mainJobInput())).not.toThrow();
expect(() => createJob(state, mainJobInput("main"))).not.toThrow();
});
it("allows creating a main-session job when defaultAgentId matches (case-insensitive)", () => {
const state = createMockState(now, { defaultAgentId: "Main" });
expect(() => createJob(state, mainJobInput("MAIN"))).not.toThrow();
});
it("rejects creating a main-session job for a non-default agentId", () => {
const state = createMockState(now, { defaultAgentId: "main" });
expect(() => createJob(state, mainJobInput("custom-agent"))).toThrow(
'cron: sessionTarget "main" is only valid for the default agent',
);
});
it("rejects main-session job for non-default agent even without explicit defaultAgentId", () => {
const state = createMockState(now);
expect(() => createJob(state, mainJobInput("custom-agent"))).toThrow(
'cron: sessionTarget "main" is only valid for the default agent',
);
});
it("allows isolated session job for non-default agents", () => {
const state = createMockState(now, { defaultAgentId: "main" });
expect(() =>
createJob(state, {
name: "isolated-job",
enabled: true,
schedule: { kind: "every", everyMs: 60_000 },
sessionTarget: "isolated",
wakeMode: "now",
payload: { kind: "agentTurn", message: "do it" },
agentId: "custom-agent",
}),
).not.toThrow();
});
});
describe("applyJobPatch rejects sessionTarget main for non-default agents", () => {
const now = Date.now();
const createMainJob = (agentId?: string): CronJob => ({
id: "job-main-agent-check",
name: "main-agent-check",
enabled: true,
createdAtMs: now,
updatedAtMs: now,
schedule: { kind: "every", everyMs: 60_000 },
sessionTarget: "main",
wakeMode: "now",
payload: { kind: "systemEvent", text: "tick" },
state: {},
agentId,
});
it("rejects patching agentId to non-default on a main-session job", () => {
const job = createMainJob();
expect(() =>
applyJobPatch(job, { agentId: "custom-agent" } as CronJobPatch, {
defaultAgentId: "main",
}),
).toThrow('cron: sessionTarget "main" is only valid for the default agent');
});
it("allows patching agentId to the default agent on a main-session job", () => {
const job = createMainJob();
expect(() =>
applyJobPatch(job, { agentId: "main" } as CronJobPatch, {
defaultAgentId: "main",
}),
).not.toThrow();
});
});
describe("cron stagger defaults", () => {
it("defaults top-of-hour cron jobs to 5m stagger", () => {
const now = Date.parse("2026-02-08T10:00:00.000Z");