Control UI/Cron: persist delivery mode none on edit (openclaw#31114) thanks @liuxiaopai-ai

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Mark L 2026-03-02 09:58:13 +08:00 committed by GitHub
parent 313a655d13
commit 9670ccfc41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 17 additions and 11 deletions

View File

@ -116,6 +116,7 @@ Docs: https://docs.openclaw.ai
- Node host/service auth env: include `OPENCLAW_GATEWAY_TOKEN` in `openclaw node install` service environments (with `CLAWDBOT_GATEWAY_TOKEN` compatibility fallback) so installed node services keep remote gateway token auth across restart/reboot. Fixes #31041. Thanks @OneStepAt4time for reporting, @byungsker, @liuxiaopai-ai, and @vincentkoc.
- Security/Subagents sandbox inheritance: block sandboxed sessions from spawning cross-agent subagents that would run unsandboxed, preventing runtime sandbox downgrade via `sessions_spawn agentId`. Thanks @tdjackey for reporting.
- Security/Workspace safe writes: harden `writeFileWithinRoot` against symlink-retarget TOCTOU races by opening existing files without truncation, creating missing files with exclusive create, deferring truncation until post-open identity+boundary validation, and removing out-of-root create artifacts on blocked races; added regression tests for truncate/create race paths. This ships in the next npm release (`2026.3.1`). Thanks @tdjackey for reporting.
- Control UI/Cron editor: include `{ mode: "none" }` in `cron.update` patches when editing an existing job and selecting “Result delivery = None (internal)”, so saved jobs no longer keep stale announce delivery mode. Fixes #31075.
- Security/Node metadata policy: harden node platform classification against Unicode confusables and switch unknown platform defaults to a conservative allowlist that excludes `system.run`/`system.which` unless explicitly allowlisted, preventing metadata canonicalization drift from broadening node command permissions. Thanks @tdjackey for reporting.
- Plugins/Discovery precedence: load bundled plugins before auto-discovered global extensions so bundled channel plugins win duplicate-ID resolution by default (explicit `plugins.load.paths` overrides remain highest precedence), with loader regression coverage. Landed from contributor PR #29710 by @Sid-Qin. Thanks @Sid-Qin.
- Discord/Reconnect integrity: release Discord message listener lane immediately while preserving serialized handler execution, add HELLO-stall resume-first recovery with bounded fresh-identify fallback after repeated stalls, and extend lifecycle/listener regression coverage for forced reconnect scenarios. Landed from contributor PR #29508 by @cgdusek. Thanks @cgdusek.

View File

@ -210,6 +210,7 @@ describe("cron controller", () => {
deleteAfterRun: false,
schedule: { kind: "cron", expr: "0 8 * * *", staggerMs: 0 },
payload: { kind: "systemEvent", text: "updated" },
delivery: { mode: "none" },
},
});
expect(state.cronEditingJobId).toBeNull();

View File

@ -614,17 +614,21 @@ export async function addCronJob(state: CronState) {
const payload = buildCronPayload(form);
const selectedDeliveryMode = form.deliveryMode;
const delivery =
selectedDeliveryMode && selectedDeliveryMode !== "none"
? {
mode: selectedDeliveryMode,
channel:
selectedDeliveryMode === "announce"
? form.deliveryChannel.trim() || "last"
: undefined,
to: form.deliveryTo.trim() || undefined,
bestEffort: form.deliveryBestEffort,
}
: undefined;
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,
}
: undefined;
const failureAlert = buildFailureAlert(form);
const agentId = form.clearAgent ? null : form.agentId.trim();
const job = {