cron: separate webhook POST delivery from announce (#17901)

* cron: split webhook delivery from announce mode

* cron: validate webhook delivery target

* cron: remove legacy webhook fallback config

* fix: finalize cron webhook delivery prep (#17901) (thanks @advaitpaliwal)

---------

Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
This commit is contained in:
Advait Paliwal
2026-02-16 02:36:00 -08:00
committed by GitHub
parent d841c9b26b
commit bc67af6ad8
33 changed files with 698 additions and 236 deletions

View File

@@ -27,7 +27,8 @@ Troubleshooting: [/automation/troubleshooting](/automation/troubleshooting)
- **Main session**: enqueue a system event, then run on the next heartbeat.
- **Isolated**: run a dedicated agent turn in `cron:<jobId>`, with delivery (announce by default or none).
- Wakeups are first-class: a job can request “wake now” vs “next heartbeat”.
- Webhook posting is opt-in per job: set `notify: true` and configure `cron.webhook`.
- Webhook posting is per job via `delivery.mode = "webhook"` + `delivery.to = "<url>"`.
- Legacy fallback remains for stored jobs with `notify: true` when `cron.webhook` is set, migrate those jobs to webhook delivery mode.
## Quick start (actionable)
@@ -100,7 +101,7 @@ A cron job is a stored record with:
- a **schedule** (when it should run),
- a **payload** (what it should do),
- optional **delivery mode** (announce or none).
- optional **delivery mode** (`announce`, `webhook`, or `none`).
- optional **agent binding** (`agentId`): run the job under a specific agent; if
missing or unknown, the gateway falls back to the default agent.
@@ -141,8 +142,9 @@ Key behaviors:
- Prompt is prefixed with `[cron:<jobId> <job name>]` for traceability.
- Each run starts a **fresh session id** (no prior conversation carry-over).
- Default behavior: if `delivery` is omitted, isolated jobs announce a summary (`delivery.mode = "announce"`).
- `delivery.mode` (isolated-only) chooses what happens:
- `delivery.mode` chooses what happens:
- `announce`: deliver a summary to the target channel and post a brief summary to the main session.
- `webhook`: POST the finished event payload to `delivery.to`.
- `none`: internal only (no delivery, no main-session summary).
- `wakeMode` controls when the main-session summary posts:
- `now`: immediate heartbeat.
@@ -164,11 +166,11 @@ Common `agentTurn` fields:
- `model` / `thinking`: optional overrides (see below).
- `timeoutSeconds`: optional timeout override.
Delivery config (isolated jobs only):
Delivery config:
- `delivery.mode`: `none` | `announce`.
- `delivery.mode`: `none` | `announce` | `webhook`.
- `delivery.channel`: `last` or a specific channel.
- `delivery.to`: channel-specific target (phone/chat/channel id).
- `delivery.to`: channel-specific target (announce) or webhook URL (webhook mode).
- `delivery.bestEffort`: avoid failing the job if announce delivery fails.
Announce delivery suppresses messaging tool sends for the run; use `delivery.channel`/`delivery.to`
@@ -193,6 +195,18 @@ Behavior details:
- The main-session summary respects `wakeMode`: `now` triggers an immediate heartbeat and
`next-heartbeat` waits for the next scheduled heartbeat.
#### Webhook delivery flow
When `delivery.mode = "webhook"`, cron posts the finished event payload to `delivery.to`.
Behavior details:
- The endpoint must be a valid HTTP(S) URL.
- No channel delivery is attempted in webhook mode.
- No main-session summary is posted in webhook mode.
- If `cron.webhookToken` is set, auth header is `Authorization: Bearer <cron.webhookToken>`.
- Deprecated fallback: stored legacy jobs with `notify: true` still post to `cron.webhook` (if configured), with a warning so you can migrate to `delivery.mode = "webhook"`.
### Model and thinking overrides
Isolated jobs (`agentTurn`) can override the model and thinking level:
@@ -214,11 +228,12 @@ Resolution priority:
Isolated jobs can deliver output to a channel via the top-level `delivery` config:
- `delivery.mode`: `announce` (deliver a summary) or `none`.
- `delivery.mode`: `announce` (channel delivery), `webhook` (HTTP POST), or `none`.
- `delivery.channel`: `whatsapp` / `telegram` / `discord` / `slack` / `mattermost` (plugin) / `signal` / `imessage` / `last`.
- `delivery.to`: channel-specific recipient target.
Delivery config is only valid for isolated jobs (`sessionTarget: "isolated"`).
`announce` delivery is only valid for isolated jobs (`sessionTarget: "isolated"`).
`webhook` delivery is valid for both main and isolated jobs.
If `delivery.channel` or `delivery.to` is omitted, cron can fall back to the main sessions
“last route” (the last place the agent replied).
@@ -289,7 +304,7 @@ Notes:
- `schedule.at` accepts ISO 8601 (timezone optional; treated as UTC when omitted).
- `everyMs` is milliseconds.
- `sessionTarget` must be `"main"` or `"isolated"` and must match `payload.kind`.
- Optional fields: `agentId`, `description`, `enabled`, `notify`, `deleteAfterRun` (defaults to true for `at`),
- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun` (defaults to true for `at`),
`delivery`.
- `wakeMode` defaults to `"now"` when omitted.
@@ -334,18 +349,20 @@ Notes:
enabled: true, // default true
store: "~/.openclaw/cron/jobs.json",
maxConcurrentRuns: 1, // default 1
webhook: "https://example.invalid/cron-finished", // optional finished-run webhook endpoint
webhookToken: "replace-with-dedicated-webhook-token", // optional, do not reuse gateway auth token
webhook: "https://example.invalid/legacy", // deprecated fallback for stored notify:true jobs
webhookToken: "replace-with-dedicated-webhook-token", // optional bearer token for webhook mode
},
}
```
Webhook behavior:
- The Gateway posts finished run events to `cron.webhook` only when the job has `notify: true`.
- Preferred: set `delivery.mode: "webhook"` with `delivery.to: "https://..."` per job.
- Webhook URLs must be valid `http://` or `https://` URLs.
- Payload is the cron finished event JSON.
- If `cron.webhookToken` is set, auth header is `Authorization: Bearer <cron.webhookToken>`.
- If `cron.webhookToken` is not set, no `Authorization` header is sent.
- Deprecated fallback: stored legacy jobs with `notify: true` still use `cron.webhook` when present.
Disable cron entirely: