fix(cron): add cron edit --clear-model

Completes the CLI half of #91298.
This commit is contained in:
ly-wang19
2026-06-15 23:54:00 +08:00
committed by GitHub
parent 794bd89fa0
commit 0888debcd3
4 changed files with 47 additions and 2 deletions
+4
View File
@@ -157,6 +157,9 @@ If stdout is non-empty, that text is the delivered result. If stdout is empty an
<ParamField path="--model" type="string">
Model override; uses the selected allowed model for the job.
</ParamField>
<ParamField path="--clear-model" type="boolean">
On `cron edit`, removes the per-job model override so the job follows normal cron model-selection precedence (a stored cron-session override if set, otherwise the agent/default model). Cannot be combined with `--model`.
</ParamField>
<ParamField path="--thinking" type="string">
Thinking level override.
</ParamField>
@@ -471,6 +474,7 @@ Model override note:
- If the model is allowed, that exact provider/model reaches the isolated agent run.
- If it is not allowed or cannot be resolved, cron fails the run with an explicit validation error.
- API `cron.update` payload patches can set `model: null` to clear a stored job model override.
- `openclaw cron edit <job-id> --clear-model` clears that override from the CLI (same effect as the `model: null` patch) and cannot be combined with `--model`.
- Configured fallback chains still apply because cron `--model` is a job primary, not a session `/model` override.
- Payload `fallbacks` replaces configured fallbacks for that job; `fallbacks: []` disables fallback and makes the run strict.
- A plain `--model` with no explicit or configured fallback list does not fall through to the agent primary as a silent extra retry target.
+1 -1
View File
@@ -168,7 +168,7 @@ Use `--due` when you want the manual command to run only if the job is currently
## Models
`cron add|edit --model <ref>` selects an allowed model for the job.
`cron add|edit --model <ref>` selects an allowed model for the job. `cron edit <job-id> --clear-model` removes the per-job model override so the job follows normal cron model-selection precedence (a stored cron-session override if present, otherwise the agent/default model); it cannot be combined with `--model`.
<Warning>
If the model is not allowed or cannot be resolved, cron fails the run with an explicit validation error instead of falling back to the job's agent or default model selection.
@@ -200,4 +200,32 @@ describe("cron edit command", () => {
},
);
});
it("clears the model override with --clear-model (CLI parity with cron.update model:null)", async () => {
const program = createCronProgram();
await program.parseAsync(["edit", "job-1", "--clear-model"], { from: "user" });
expect(callGatewayFromCli).toHaveBeenCalledWith(
"cron.update",
expect.objectContaining({ clearModel: true }),
{
id: "job-1",
patch: {
payload: {
kind: "agentTurn",
model: null,
},
},
},
);
});
it("documents the --clear-model flag alongside the sibling --clear-tools", () => {
const editCommand = createCronProgram().commands.find((command) => command.name() === "edit");
const help = editCommand?.helpInformation() ?? "";
expect(help).toContain("--clear-model");
expect(help).toContain("--clear-tools");
});
});
+14 -1
View File
@@ -115,6 +115,11 @@ export function registerCronEditCommand(cron: Command) {
"Thinking level for agent jobs (off|minimal|low|medium|high|xhigh)",
)
.option("--model <model>", "Model override for agent jobs")
.option(
"--clear-model",
"Remove the per-job model override (restore normal cron model precedence)",
false,
)
.option("--timeout-seconds <n>", "Timeout seconds for agent or command jobs")
.option("--no-output-timeout-seconds <n>", "No-output timeout seconds for command jobs")
.option("--output-max-bytes <n>", "Maximum captured stdout/stderr bytes for command jobs")
@@ -279,6 +284,9 @@ export function registerCronEditCommand(cron: Command) {
);
}
const model = normalizeOptionalString(opts.model);
if (model && opts.clearModel) {
throw new Error("Use --model or --clear-model, not both");
}
const thinking = normalizeOptionalString(opts.thinking);
const toolsAllow = parseCronToolsAllow(opts.tools);
const rawTimeoutSeconds =
@@ -346,6 +354,7 @@ export function registerCronEditCommand(cron: Command) {
const hasAgentTurnPayloadField =
typeof opts.message === "string" ||
Boolean(model) ||
Boolean(opts.clearModel) ||
Boolean(thinking) ||
(hasTimeoutSeconds &&
!hasCommandSpecificPayloadField &&
@@ -373,7 +382,11 @@ export function registerCronEditCommand(cron: Command) {
} else if (hasAgentTurnPatch) {
const payload: Record<string, unknown> = { kind: "agentTurn" };
assignIf(payload, "message", String(opts.message), typeof opts.message === "string");
assignIf(payload, "model", model, Boolean(model));
if (opts.clearModel) {
payload.model = null;
} else {
assignIf(payload, "model", model, Boolean(model));
}
assignIf(payload, "thinking", thinking, Boolean(thinking));
assignIf(payload, "timeoutSeconds", timeoutSeconds, hasTimeoutSeconds);
assignIf(