diff --git a/CHANGELOG.md b/CHANGELOG.md index ec14937b503..324abe1c247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Docs: https://docs.openclaw.ai - Commitments: strip malformed optional reminder scope metadata from persisted commitments before matching pending follow-ups. - Config persistence: normalize malformed auth profile credential fields/state, skip JSON-valid garbage transcript checkpoint rows, and let `openclaw doctor --fix` remove unrepairable cron job rows. - Cron: skip persisted job rows with malformed schedule or payload shapes in memory, leaving the store for `openclaw doctor --fix` instead of hydrating them into runtime state. +- Cron: keep legacy string schedules and blank system-event jobs available for runtime repair/skip handling instead of dropping them as malformed persisted rows. - Task persistence: drop malformed array/scalar requester-origin JSON from task and task-flow SQLite sidecars instead of restoring it as delivery metadata. - Agents/timeouts: clarify model idle-timeout errors and docs so provider `timeoutSeconds` is shown as bounded by the whole agent/run timeout ceiling. - Release tooling: align the published launcher Node floor, `npm start`, package script checks, sharded lint locking, Vitest root project coverage, and plugin-SDK declaration build cache metadata so release/package validation does not silently skip or ship stale surfaces. diff --git a/extensions/discord/src/monitor/model-picker.view.ts b/extensions/discord/src/monitor/model-picker.view.ts index 56c4025e377..54369a52c42 100644 --- a/extensions/discord/src/monitor/model-picker.view.ts +++ b/extensions/discord/src/monitor/model-picker.view.ts @@ -370,12 +370,17 @@ function buildModelRows(params: { modelIndex: params.pendingModelIndex, userId: params.userId, }), - options: runtimeChoices.map((choice) => ({ - label: choice.label, - value: choice.id, - default: choice.id === selectedRuntime, - ...(choice.description ? { description: choice.description } : {}), - })), + options: runtimeChoices.map((choice) => { + const option: APISelectMenuOption = { + label: choice.label, + value: choice.id, + default: choice.id === selectedRuntime, + }; + if (choice.description) { + option.description = choice.description; + } + return option; + }), placeholder: "Select runtime", }), ]), diff --git a/src/cron/persisted-shape.ts b/src/cron/persisted-shape.ts index 1b59b6ac547..ebcd558798b 100644 --- a/src/cron/persisted-shape.ts +++ b/src/cron/persisted-shape.ts @@ -15,7 +15,13 @@ export function getInvalidPersistedCronJobReason( return "missing-id"; } const schedule = candidate.schedule; - if (!schedule || typeof schedule !== "object" || Array.isArray(schedule)) { + if (!schedule || Array.isArray(schedule)) { + return "missing-schedule"; + } + if (typeof schedule === "string") { + return null; + } + if (typeof schedule !== "object") { return "missing-schedule"; } const scheduleRecord = schedule as Record; @@ -52,7 +58,7 @@ export function getInvalidPersistedCronJobReason( } if (payloadKind === "systemEvent") { const text = payloadRecord.text; - if (typeof text !== "string" || text.trim().length === 0) { + if (typeof text !== "string") { return "invalid-payload"; } } diff --git a/src/pairing/pairing-store.test.ts b/src/pairing/pairing-store.test.ts index 68084cfd3e9..03f305b180b 100644 --- a/src/pairing/pairing-store.test.ts +++ b/src/pairing/pairing-store.test.ts @@ -301,7 +301,7 @@ async function expectPendingPairingRequestsIsolatedByAccount(params: { describe("pairing store", () => { it("skips malformed persisted pairing requests while approving valid codes", async () => { await withTempStateDir(async (stateDir) => { - const now = new Date("2026-05-16T05:20:00.000Z").toISOString(); + const now = new Date().toISOString(); writeJsonFixture(resolvePairingFilePath(stateDir, "telegram"), { version: 1, requests: [