* refactor: use canonical re-export names
* fix(test): classify suite support as test source
* fix(agents): retarget gateway stub session-entry import
* test(gateway): retarget session-utils mock keys after alias removal
Scheduling an automation against a channel account the operator has turned
off succeeded. The job was stored and scheduled against a route its owner had
already disabled, and nothing reported that until it fired.
`assertValidCronFailureAlert` in this file already applies the rule to the
channel on the same route: an explicit unknown channel must not be "stored and
only fails later as `channel_not_found`". The account had no equivalent check.
Reject a `delivery.accountId` whose `accounts.<id>.enabled` is `false`, beside
the existing channel checks.
The check is scoped to that account entry and nothing else, because the wider
signals all have false positives here:
- Cron delivery ids are not always operator-typed - `delivery-context.ts`
copies the current context account into inferred jobs.
- Channel `isEnabled` adapters report unlisted or credential-suppressed
accounts as not enabled (twitch resolves any unlisted id to `enabled: false`).
- A top-level `channels.<id>.enabled: false` is not uniformly channel-wide -
twitch resolves named accounts from `accounts` alone.
Only the account entry itself is an unambiguous statement about this route, so
only that blocks a mutation. No channel plugin is resolved on this path.
Both cron mutation paths share this validator and the update caller is already
gated on `"delivery" in patch`, so edits that do not touch routing stay
unblocked by an id stored before this validation existed.
`cron-error-classification.ts` learns the field name so the precondition
recheck inside `updateWithPrecondition` still classifies it as
`INVALID_REQUEST`.
`delivery.failureDestination.accountId` is intentionally out of scope: its
effective route layers over the global failure-destination config, so it needs
route resolution before validation rather than a raw check on the partial
override.
* feat(cli): add openclaw automations alias and reword cron display prose
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhJ8EiMXue6ADLmHfb7FL6
* test(doctor): update cron doctor prose assertions for automations wording
* fix(plugins): include command aliases in plugin CLI collision detection
Codex review finding on the automations alias: plugin CLI registration
seeded existingCommands from command names only, so a plugin exposing a
top-level command matching an alias-only root name (automations, terminal,
chat) would crash Commander at startup instead of being skipped. Seed from
names plus aliases; regression test covers the alias path.
* fix(cli): rename residual cron prose in CLI and gateway RPC errors
Found in combined dev-gateway E2E: automation not found / unknown automation
id errors, add/edit prose, docs tip, skills-cli mention, and the gateway RPC
not-found message. The CLI missing-get matcher accepts both message forms so
older gateways keep resolving name lookups.
* fix(gateway): keep cron.get missing wording as a wire contract for older CLI matchers
ClawSweeper rank-up: shipped CLI matchers parse 'cron job not found: <id>'
before the name-lookup fallback; the rename stays CLI-display only. Adds a
regression pinning the exact wire form.
* fix(cli): rename doctor and task-summary cron prose flagged in review
Repair-plan advisories, session-registry task summary, and the heartbeat
migration health check now say automations; recreate hints use the
openclaw automations CLI form.
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(cron): validate failureAlert channel at the gateway boundary
`--failure-alert-channel` writes patch.failureAlert.channel, a field distinct from
delivery (own store columns, own delivery path in service/failure-alerts.ts). The
gateway validator only ran channel checks for delivery patches, so a
failure-alert-only patch stored an unknown channel unvalidated and only failed
later at delivery time with channel_not_found.
Add assertValidCronFailureAlert (reusing the announce-channel validation), wired
into create + update. It mirrors resolveFailureAlert() runtime resolution:
resolves the effective mode (job or global cron failure-alert mode; webhook alerts
skip channel-type validation), inherits the job delivery channel/target when the
alert names none (rejecting a routing-changing edit that would activate a
legacy-invalid inherited channel, while unrelated/threshold edits and already-
active alerts are not blocked), resolves a provider-prefixed target like the
delivery/failureDestination paths, and honors the channel:null last-fallback. Maps
failureAlert.channel errors to INVALID_REQUEST. Regression tests included.
Closes#103864
* fix(cron): align failure alert route validation
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Run script payloads through the shared headless code-mode executor with payload-grade budgets and success-only trigger.state persistence.
Reuse cron delivery, wake, pacing, and dangerous trigger-gate contracts for notify, wake, and nextCheck results.
* fix(cron): keep valid command env vars when a sibling value is non-string (#105433)
normalizeTrimmedStringRecord returned undefined whenever any env value was non-string, so payload normalization deleted the entire env map before gateway schema validation, silently dropping valid siblings (e.g. env {DEBUG:true, PATH:'/bin'} lost PATH). It now filters invalid entries and only normalizes the map away when no valid entries remain, matching the sibling normalizeTrimmedStringArray behavior in the same file.
* fix(cron): reject malformed command env maps
Co-authored-by: MoerAI <friendnt@g.skku.edu>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(cron): reject non-string values in delivery target validation
assertNonBlankStringField had inverted logic — non-string values
(number, boolean, object) were silently accepted because typeof !== 'string'
was grouped with the undefined/null early return. The function name says
'assert non-blank string field' but it was bypassing validation for any
non-string type, defeating the purpose of input validation.
* test(cron): cover invalid delivery fields at entrypoints
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(cron): clear delivery routing fields from cron edit
cron edit could set delivery channel/to/thread-id/account but could not unset them: an empty value (e.g. --to "") builds delivery.X = undefined, which is omitted from the JSON-RPC patch, so mergeCronDelivery never sees the key and the field is silently kept. The gateway RPC already accepts an explicit null to clear each field (CronDeliveryPatchSchema + mergeCronDelivery via normalizeOptionalString); the CLI just never sent it.
Add --clear-channel/--clear-to/--clear-thread-id/--clear-account, each emitting null (mirroring the existing --clear-model), with mutual-exclusion guards against the matching set flag and against --webhook.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cron): preserve delivery defaults when clearing routes
* fix(cron): validate cleared prefixed routes
---------
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Capture the originating sessionKey and agentId for cron wake tool calls so non-main session and multi-agent wakes return to the conversation lane that requested them.
Carry stored delivery context through queued wake events so topic/thread replies route correctly, while preserving the default no-origin wake behavior and explicit target:none opt-out.
Refs #46886.
Refs #64556.
Thanks @anagnorisis2peripeteia.
Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>
Reject whitespace-only cron delivery target strings before cron input normalization can trim and drop them, so bad delivery targets return INVALID_REQUEST instead of behaving as omitted fields.
Keep explicit null update clears for delivery, failure destination, and completion destination fields.
Co-authored-by: gaozixiang1 <gaozixiang1@xiaomi.com>
Co-authored-by: Lanzhi <lizhan3@xiaomi.com>