mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(cron): land #31145 explicit delivery none in editor (@byungsker)
Landed from contributor PR #31145 by @byungsker. Co-authored-by: byungsker <byungsker@users.noreply.github.com>
This commit is contained in:
@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Windows/Plugin install: avoid `spawn EINVAL` on Windows npm/npx invocations by resolving to `node` + npm CLI scripts instead of spawning `.cmd` directly. Landed from contributor PR #31147 by @codertony. Thanks @codertony.
|
||||
- LINE/Voice transcription: classify M4A voice media as `audio/mp4` (not `video/mp4`) by checking the MPEG-4 `ftyp` major brand (`M4A ` / `M4B `), restoring voice transcription for LINE voice messages. Landed from contributor PR #31151 by @scoootscooob. Thanks @scoootscooob.
|
||||
- Cron/Delivery mode none: send explicit `delivery: { mode: "none" }` from cron editor for both add and update flows so previous announce delivery is actually cleared. Landed from contributor PR #31145 by @byungsker. Thanks @byungsker.
|
||||
- Agents/Thinking fallback: when providers reject unsupported thinking levels without enumerating alternatives, retry with `think=off` to avoid hard failure during model/provider fallback chains. Landed from contributor PR #31002 by @yfge. Thanks @yfge.
|
||||
- Agents/Failover reason classification: avoid false rate-limit classification from incidental `tpm` substrings by matching TPM as a standalone token/phrase and keeping auth-context errors on the auth path. Landed from contributor PR #31007 by @HOYALIM. Thanks @HOYALIM.
|
||||
- Slack/Announce target account routing: enable session-backed announce-target lookup for Slack so multi-account announces resolve the correct `accountId` instead of defaulting to bot-token context. Landed from contributor PR #31028 by @taw0002. Thanks @taw0002.
|
||||
|
||||
@@ -119,6 +119,91 @@ describe("cron controller", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('sends delivery: { mode: "none" } explicitly in cron.add payload', async () => {
|
||||
const request = vi.fn(async (method: string, _payload?: unknown) => {
|
||||
if (method === "cron.add") {
|
||||
return { id: "job-none-add" };
|
||||
}
|
||||
if (method === "cron.list") {
|
||||
return { jobs: [] };
|
||||
}
|
||||
if (method === "cron.status") {
|
||||
return { enabled: true, jobs: 0, nextWakeAtMs: null };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const state = createState({
|
||||
client: {
|
||||
request,
|
||||
} as unknown as CronState["client"],
|
||||
cronForm: {
|
||||
...DEFAULT_CRON_FORM,
|
||||
name: "none delivery job",
|
||||
scheduleKind: "every",
|
||||
everyAmount: "1",
|
||||
everyUnit: "minutes",
|
||||
sessionTarget: "isolated",
|
||||
wakeMode: "next-heartbeat",
|
||||
payloadKind: "agentTurn",
|
||||
payloadText: "run this",
|
||||
deliveryMode: "none",
|
||||
},
|
||||
});
|
||||
|
||||
await addCronJob(state);
|
||||
|
||||
const addCall = request.mock.calls.find(([method]) => method === "cron.add");
|
||||
expect(addCall).toBeDefined();
|
||||
expect((addCall?.[1] as { delivery?: unknown } | undefined)?.delivery).toEqual({
|
||||
mode: "none",
|
||||
});
|
||||
});
|
||||
|
||||
it('sends delivery: { mode: "none" } explicitly in cron.update patch', async () => {
|
||||
const request = vi.fn(async (method: string, _payload?: unknown) => {
|
||||
if (method === "cron.update") {
|
||||
return { id: "job-none-update" };
|
||||
}
|
||||
if (method === "cron.list") {
|
||||
return { jobs: [{ id: "job-none-update" }] };
|
||||
}
|
||||
if (method === "cron.status") {
|
||||
return { enabled: true, jobs: 1, nextWakeAtMs: null };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const state = createState({
|
||||
client: {
|
||||
request,
|
||||
} as unknown as CronState["client"],
|
||||
cronEditingJobId: "job-none-update",
|
||||
cronForm: {
|
||||
...DEFAULT_CRON_FORM,
|
||||
name: "switch to none",
|
||||
scheduleKind: "every",
|
||||
everyAmount: "30",
|
||||
everyUnit: "minutes",
|
||||
sessionTarget: "isolated",
|
||||
wakeMode: "next-heartbeat",
|
||||
payloadKind: "agentTurn",
|
||||
payloadText: "do work",
|
||||
deliveryMode: "none",
|
||||
},
|
||||
});
|
||||
|
||||
await addCronJob(state);
|
||||
|
||||
const updateCall = request.mock.calls.find(([method]) => method === "cron.update");
|
||||
expect(updateCall).toBeDefined();
|
||||
expect(
|
||||
(updateCall?.[1] as { patch?: { delivery?: unknown } } | undefined)?.patch?.delivery,
|
||||
).toEqual({
|
||||
mode: "none",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not submit stale announce delivery when unsupported", async () => {
|
||||
const request = vi.fn(async (method: string, _payload?: unknown) => {
|
||||
if (method === "cron.add") {
|
||||
@@ -159,8 +244,13 @@ describe("cron controller", () => {
|
||||
expect(addCall?.[1]).toMatchObject({
|
||||
name: "main job",
|
||||
});
|
||||
expect((addCall?.[1] as { delivery?: unknown } | undefined)?.delivery).toBeUndefined();
|
||||
expect(state.cronForm.deliveryMode).toBe("none");
|
||||
// Delivery is explicitly sent as { mode: "none" } to clear the announce delivery on the backend.
|
||||
// Previously this was sent as undefined, which left announce in place (bug #31075).
|
||||
expect((addCall?.[1] as { delivery?: unknown } | undefined)?.delivery).toEqual({
|
||||
mode: "none",
|
||||
});
|
||||
// After submit, form is reset to defaults (deliveryMode = "announce" from DEFAULT_CRON_FORM).
|
||||
expect(state.cronForm.deliveryMode).toBe("announce");
|
||||
});
|
||||
|
||||
it("submits cron.update when editing an existing job", async () => {
|
||||
|
||||
@@ -614,20 +614,18 @@ export async function addCronJob(state: CronState) {
|
||||
const payload = buildCronPayload(form);
|
||||
const selectedDeliveryMode = form.deliveryMode;
|
||||
const delivery =
|
||||
selectedDeliveryMode === "none"
|
||||
? state.cronEditingJobId
|
||||
? { mode: "none" as const }
|
||||
: undefined
|
||||
: selectedDeliveryMode
|
||||
? {
|
||||
mode: selectedDeliveryMode,
|
||||
channel:
|
||||
selectedDeliveryMode === "announce"
|
||||
? form.deliveryChannel.trim() || "last"
|
||||
: undefined,
|
||||
to: form.deliveryTo.trim() || undefined,
|
||||
bestEffort: form.deliveryBestEffort,
|
||||
}
|
||||
selectedDeliveryMode && selectedDeliveryMode !== "none"
|
||||
? {
|
||||
mode: selectedDeliveryMode,
|
||||
channel:
|
||||
selectedDeliveryMode === "announce"
|
||||
? form.deliveryChannel.trim() || "last"
|
||||
: undefined,
|
||||
to: form.deliveryTo.trim() || undefined,
|
||||
bestEffort: form.deliveryBestEffort,
|
||||
}
|
||||
: selectedDeliveryMode === "none"
|
||||
? ({ mode: "none" } as const)
|
||||
: undefined;
|
||||
const failureAlert = buildFailureAlert(form);
|
||||
const agentId = form.clearAgent ? null : form.agentId.trim();
|
||||
|
||||
Reference in New Issue
Block a user