fix(cron): persist payload.fallbacks in cron.update API

The `fallbacks` field was read at runtime by `runWithModelFallback()` but
silently dropped when updating a cron job. Add it to both
`mergeCronPayload()` (same-kind merge) and `buildPayloadFromPatch()`
(kind-change rebuild), following the same pattern as the `lightContext`
fix (#31425).

Closes #36263

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Strider 2026-03-11 21:13:38 +08:00
parent 4c60956d8e
commit 02896ea17a
2 changed files with 51 additions and 0 deletions

View File

@ -214,6 +214,53 @@ describe("applyJobPatch", () => {
}
});
it("persists agentTurn payload.fallbacks updates when editing existing jobs", () => {
const job = createIsolatedAgentTurnJob("job-fallbacks", {
mode: "announce",
channel: "telegram",
});
job.payload = {
kind: "agentTurn",
message: "do it",
fallbacks: ["anthropic/claude-sonnet-4-6"],
};
applyJobPatch(job, {
payload: {
kind: "agentTurn",
message: "do it",
fallbacks: ["openai/gpt-4o", "anthropic/claude-haiku-4-5"],
},
});
expect(job.payload.kind).toBe("agentTurn");
if (job.payload.kind === "agentTurn") {
expect(job.payload.fallbacks).toEqual(["openai/gpt-4o", "anthropic/claude-haiku-4-5"]);
}
});
it("applies payload.fallbacks when replacing payload kind via patch", () => {
const job = createIsolatedAgentTurnJob("job-fallbacks-switch", {
mode: "announce",
channel: "telegram",
});
job.payload = { kind: "systemEvent", text: "ping" };
applyJobPatch(job, {
payload: {
kind: "agentTurn",
message: "do it",
fallbacks: ["anthropic/claude-sonnet-4-6"],
},
});
const payload = job.payload as CronJob["payload"];
expect(payload.kind).toBe("agentTurn");
if (payload.kind === "agentTurn") {
expect(payload.fallbacks).toEqual(["anthropic/claude-sonnet-4-6"]);
}
});
it("rejects webhook delivery without a valid http(s) target URL", () => {
const expectedError = "cron webhook delivery requires delivery.to to be a valid http(s) URL";
const cases = [

View File

@ -704,6 +704,9 @@ function mergeCronPayload(existing: CronPayload, patch: CronPayloadPatch): CronP
if (typeof patch.bestEffortDeliver === "boolean") {
next.bestEffortDeliver = patch.bestEffortDeliver;
}
if (Array.isArray(patch.fallbacks)) {
next.fallbacks = patch.fallbacks;
}
return next;
}
@ -772,6 +775,7 @@ function buildPayloadFromPatch(patch: CronPayloadPatch): CronPayload {
channel: patch.channel,
to: patch.to,
bestEffortDeliver: patch.bestEffortDeliver,
fallbacks: patch.fallbacks,
};
}