diff --git a/docs/automation/auth-monitoring.md b/docs/automation/auth-monitoring.md index 877a1c2ce21..ae07f43c43e 100644 --- a/docs/automation/auth-monitoring.md +++ b/docs/automation/auth-monitoring.md @@ -1,44 +1,8 @@ --- -summary: "Monitor OAuth expiry for model providers" -read_when: - - Setting up auth expiry monitoring or alerts - - Automating Claude Code / Codex OAuth refresh checks +summary: "Redirect to /gateway/authentication" title: "Auth Monitoring" --- -# Auth monitoring +# Auth Monitoring -OpenClaw exposes OAuth expiry health via `openclaw models status`. Use that for -automation and alerting; scripts are optional extras for phone workflows. - -## Preferred: CLI check (portable) - -```bash -openclaw models status --check -``` - -Exit codes: - -- `0`: OK -- `1`: expired or missing credentials -- `2`: expiring soon (within 24h) - -This works in cron/systemd and requires no extra scripts. - -## Optional scripts (ops / phone workflows) - -These live under `scripts/` and are **optional**. They assume SSH access to the -gateway host and are tuned for systemd + Termux. - -- `scripts/claude-auth-status.sh` now uses `openclaw models status --json` as the - source of truth (falling back to direct file reads if the CLI is unavailable), - so keep `openclaw` on `PATH` for timers. -- `scripts/auth-monitor.sh`: cron/systemd timer target; sends alerts (ntfy or phone). -- `scripts/systemd/openclaw-auth-monitor.{service,timer}`: systemd user timer. -- `scripts/claude-auth-status.sh`: Claude Code + OpenClaw auth checker (full/json/simple). -- `scripts/mobile-reauth.sh`: guided re‑auth flow over SSH. -- `scripts/termux-quick-auth.sh`: one‑tap widget status + open auth URL. -- `scripts/termux-auth-widget.sh`: full guided widget flow. -- `scripts/termux-sync-widget.sh`: sync Claude Code creds → OpenClaw. - -If you don’t need phone automation or systemd timers, skip these scripts. +This page moved to [Authentication](/gateway/authentication). See [Authentication](/gateway/authentication) for auth monitoring documentation. diff --git a/docs/automation/cron-jobs.md b/docs/automation/cron-jobs.md index 9af16a72f7f..a9938f0d58a 100644 --- a/docs/automation/cron-jobs.md +++ b/docs/automation/cron-jobs.md @@ -1,48 +1,20 @@ --- -summary: "Cron jobs + wakeups for the Gateway scheduler" +summary: "Scheduled jobs, webhooks, and Gmail PubSub triggers for the Gateway scheduler" read_when: - Scheduling background jobs or wakeups - - Wiring automation that should run with or alongside heartbeats + - Wiring external triggers (webhooks, Gmail) into OpenClaw - Deciding between heartbeat and cron for scheduled tasks -title: "Cron Jobs" +title: "Scheduled Tasks" --- -# Cron jobs (Gateway scheduler) +# Scheduled Tasks (Cron) -> **Cron vs Heartbeat?** See [Cron vs Heartbeat](/automation/cron-vs-heartbeat) for guidance on when to use each. +Cron is the Gateway's built-in scheduler. It persists jobs, wakes the agent at the right time, and can deliver output back to a chat channel or webhook endpoint. -Cron is the Gateway’s built-in scheduler. It persists jobs, wakes the agent at -the right time, and can optionally deliver output back to a chat. - -All cron executions create [background task](/automation/tasks) records. The key difference is visibility: - -- `sessionTarget: "main"` creates a task with `silent` notify policy — it schedules a system event for the main session and heartbeat flow but does not generate notifications. -- `sessionTarget: "isolated"` or `sessionTarget: "session:..."` creates a visible task that shows up in `openclaw tasks` with delivery notifications. - -If you want _“run this every morning”_ or _“poke the agent in 20 minutes”_, -cron is the mechanism. - -Troubleshooting: [/automation/troubleshooting](/automation/troubleshooting) - -## TL;DR - -- Cron runs **inside the Gateway** (not inside the model). -- Jobs persist under `~/.openclaw/cron/` so restarts don’t lose schedules. -- Two execution styles: - - **Main session**: enqueue a system event, then run on the next heartbeat. - - **Isolated**: run a dedicated agent turn in `cron:` or a custom session, with delivery (announce by default or none). - - **Current session**: bind to the session where the cron is created (`sessionTarget: "current"`). - - **Custom session**: run in a persistent named session (`sessionTarget: "session:custom-id"`). -- Wakeups are first-class: a job can request “wake now” vs “next heartbeat”. -- Webhook posting is per job via `delivery.mode = "webhook"` + `delivery.to = ""`. -- Legacy fallback remains for stored jobs with `notify: true` when `cron.webhook` is set, migrate those jobs to webhook delivery mode. -- For upgrades, `openclaw doctor --fix` can normalize legacy cron store fields, including old top-level delivery hints such as `threadId`. - -## Quick start (actionable) - -Create a one-shot reminder, verify it exists, and run it immediately: +## Quick start ```bash +# Add a one-shot reminder openclaw cron add \ --name "Reminder" \ --at "2026-02-01T16:00:00Z" \ @@ -51,12 +23,48 @@ openclaw cron add \ --wake now \ --delete-after-run +# Check your jobs openclaw cron list -openclaw cron run + +# See run history openclaw cron runs --id ``` -Schedule a recurring isolated job with delivery: +## How cron works + +- Cron runs **inside the Gateway** process (not inside the model). +- Jobs persist at `~/.openclaw/cron/jobs.json` so restarts do not lose schedules. +- All cron executions create [background task](/automation/tasks) records. +- One-shot jobs (`--at`) auto-delete after success by default. + +## Adding jobs + +### Schedule types + +| Kind | CLI flag | Description | +| ------- | --------- | ------------------------------------------------------- | +| `at` | `--at` | One-shot timestamp (ISO 8601 or relative like `20m`) | +| `every` | `--every` | Fixed interval | +| `cron` | `--cron` | 5-field or 6-field cron expression with optional `--tz` | + +Timestamps without a timezone are treated as UTC. Add `--tz America/New_York` for local wall-clock scheduling. + +Recurring top-of-hour expressions are automatically staggered by up to 5 minutes to reduce load spikes. Use `--exact` to force precise timing or `--stagger 30s` for an explicit window. + +### CLI examples + +One-shot reminder (main session): + +```bash +openclaw cron add \ + --name "Calendar check" \ + --at "20m" \ + --session main \ + --system-event "Next heartbeat: check calendar." \ + --wake now +``` + +Recurring isolated job with delivery: ```bash openclaw cron add \ @@ -70,246 +78,211 @@ openclaw cron add \ --to "channel:C1234567890" ``` -## Tool-call equivalents (Gateway cron tool) +Isolated job with model and thinking override: -For the canonical JSON shapes and examples, see [JSON schema for tool calls](/automation/cron-jobs#json-schema-for-tool-calls). +```bash +openclaw cron add \ + --name "Deep analysis" \ + --cron "0 6 * * 1" \ + --tz "America/Los_Angeles" \ + --session isolated \ + --message "Weekly deep analysis of project progress." \ + --model "opus" \ + --thinking high \ + --announce +``` -## Where cron jobs are stored +## Execution styles -Cron jobs are persisted on the Gateway host at `~/.openclaw/cron/jobs.json` by default. -The Gateway loads the file into memory and writes it back on changes, so manual edits -are only safe when the Gateway is stopped. Prefer `openclaw cron add/edit` or the cron -tool call API for changes. +| Style | `--session` value | Runs in | Best for | +| --------------- | ------------------- | ------------------------ | ------------------------------- | +| Main session | `main` | Next heartbeat turn | Reminders, system events | +| Isolated | `isolated` | Dedicated `cron:` | Reports, background chores | +| Current session | `current` | Bound at creation time | Context-aware recurring work | +| Custom session | `session:custom-id` | Persistent named session | Workflows that build on history | -## Beginner-friendly overview +**Main session** jobs enqueue a system event and optionally wake the heartbeat (`--wake now` or `--wake next-heartbeat`). They use `payload.kind = "systemEvent"`. -Think of a cron job as: **when** to run + **what** to do. +**Isolated** jobs run a dedicated agent turn. Each run starts a fresh session (no carry-over) unless using a custom session. Default delivery is `announce` (summary to chat). -1. **Choose a schedule** - - One-shot reminder → `schedule.kind = "at"` (CLI: `--at`) - - Repeating job → `schedule.kind = "every"` or `schedule.kind = "cron"` - - If your ISO timestamp omits a timezone, it is treated as **UTC**. +**Custom sessions** (`session:xxx`) persist context across runs, enabling workflows like daily standups that build on previous summaries. -2. **Choose where it runs** - - `sessionTarget: "main"` → run during the next heartbeat with main context. - - `sessionTarget: "isolated"` → run a dedicated agent turn in `cron:`. - - `sessionTarget: "current"` → bind to the current session (resolved at creation time to `session:`). - - `sessionTarget: "session:custom-id"` → run in a persistent named session that maintains context across runs. +### Payload options for isolated jobs - Default behavior (unchanged): - - `systemEvent` payloads default to `main` - - `agentTurn` payloads default to `isolated` +- `--message`: prompt text (required for isolated) +- `--model` / `--thinking`: model and thinking level overrides +- `--light-context`: skip workspace bootstrap file injection +- `--tools exec,read`: restrict which tools the job can use - To use current session binding, explicitly set `sessionTarget: "current"`. +## Delivery and output -3. **Choose the payload** - - Main session → `payload.kind = "systemEvent"` - - Isolated session → `payload.kind = "agentTurn"` +| Mode | What happens | +| ---------- | -------------------------------------------------------- | +| `announce` | Deliver summary to target channel (default for isolated) | +| `webhook` | POST finished event payload to a URL | +| `none` | Internal only, no delivery | -Optional: one-shot jobs (`schedule.kind = "at"`) delete after success by default. Set -`deleteAfterRun: false` to keep them (they will disable after success). +Use `--announce --channel telegram --to "-1001234567890"` for channel delivery. -## Concepts +For Telegram forum topics, use `-1001234567890:topic:123`. Slack/Discord/Mattermost targets should use explicit prefixes (`channel:`, `user:`). -### Jobs +## Webhooks -A cron job is a stored record with: +Gateway can expose HTTP webhook endpoints for external triggers. Enable in config: -- a **schedule** (when it should run), -- a **payload** (what it should do), -- 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. +```json5 +{ + hooks: { + enabled: true, + token: "shared-secret", + path: "/hooks", + }, +} +``` -Jobs are identified by a stable `jobId` (used by CLI/Gateway APIs). -In agent tool calls, `jobId` is canonical; legacy `id` is accepted for compatibility. -One-shot jobs auto-delete after success by default; set `deleteAfterRun: false` to keep them. +### Authentication -### Schedules +Every request must include the hook token via header: -Cron supports three schedule kinds: +- `Authorization: Bearer ` (recommended) +- `x-openclaw-token: ` -- `at`: one-shot timestamp via `schedule.at` (ISO 8601). -- `every`: fixed interval (ms). -- `cron`: 5-field cron expression (or 6-field with seconds) with optional IANA timezone. +Query-string tokens are rejected. -Cron expressions use `croner`. If a timezone is omitted, the Gateway host’s -local timezone is used. +### POST /hooks/wake -To reduce top-of-hour load spikes across many gateways, OpenClaw applies a -deterministic per-job stagger window of up to 5 minutes for recurring -top-of-hour expressions (for example `0 * * * *`, `0 */2 * * *`). Fixed-hour -expressions such as `0 7 * * *` remain exact. +Enqueue a system event for the main session: -For any cron schedule, you can set an explicit stagger window with `schedule.staggerMs` -(`0` keeps exact timing). CLI shortcuts: +```bash +curl -X POST http://127.0.0.1:18789/hooks/wake \ + -H 'Authorization: Bearer SECRET' \ + -H 'Content-Type: application/json' \ + -d '{"text":"New email received","mode":"now"}' +``` -- `--stagger 30s` (or `1m`, `5m`) to set an explicit stagger window. -- `--exact` to force `staggerMs = 0`. +- `text` (required): event description +- `mode` (optional): `now` (default) or `next-heartbeat` -### Main vs isolated execution +### POST /hooks/agent -#### Main session jobs (system events) +Run an isolated agent turn: -Main jobs enqueue a system event and optionally wake the heartbeat runner. -They must use `payload.kind = "systemEvent"`. +```bash +curl -X POST http://127.0.0.1:18789/hooks/agent \ + -H 'Authorization: Bearer SECRET' \ + -H 'Content-Type: application/json' \ + -d '{"message":"Summarize inbox","name":"Email","model":"openai/gpt-5.2-mini"}' +``` -- `wakeMode: "now"` (default): event triggers an immediate heartbeat run. -- `wakeMode: "next-heartbeat"`: event waits for the next scheduled heartbeat. +Fields: `message` (required), `name`, `agentId`, `wakeMode`, `deliver`, `channel`, `to`, `model`, `thinking`, `timeoutSeconds`. -This is the best fit when you want the normal heartbeat prompt + main-session context. -See [Heartbeat](/gateway/heartbeat). +### Mapped hooks (POST /hooks/\) -Main-session cron jobs create [background task](/automation/tasks) records with `silent` notify policy (no notifications by default). They appear in `openclaw tasks list` but do not generate delivery messages. +Custom hook names are resolved via `hooks.mappings` in config. Mappings can transform arbitrary payloads into `wake` or `agent` actions with templates or code transforms. -#### Isolated jobs (dedicated cron sessions) +### Security -Isolated jobs run a dedicated agent turn in session `cron:` or a custom session. +- Keep hook endpoints behind loopback, tailnet, or trusted reverse proxy. +- Use a dedicated hook token; do not reuse gateway auth tokens. +- Set `hooks.allowedAgentIds` to limit explicit `agentId` routing. +- Keep `hooks.allowRequestSessionKey=false` unless you require caller-selected sessions. +- Hook payloads are wrapped with safety boundaries by default. -Key behaviors: +## Gmail PubSub integration -- Prompt is prefixed with `[cron: ]` for traceability. -- Each run starts a **fresh session id** (no prior conversation carry-over), unless using a custom session. -- Custom sessions (`session:xxx`) persist context across runs, enabling workflows like daily standups that build on previous summaries. -- Default behavior: if `delivery` is omitted, isolated jobs announce a summary (`delivery.mode = "announce"`). -- `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` when the finished event includes a summary. - - `none`: internal only (no delivery, no main-session summary). -- `wakeMode` controls when the main-session summary posts: - - `now`: immediate heartbeat. - - `next-heartbeat`: waits for the next scheduled heartbeat. +Wire Gmail inbox triggers to OpenClaw via Google PubSub. -Use isolated jobs for noisy, frequent, or "background chores" that shouldn't spam -your main chat history. +**Prerequisites**: `gcloud` CLI, `gog` (gogcli), OpenClaw hooks enabled, Tailscale for the public HTTPS endpoint. -These detached runs create [background task](/automation/tasks) records visible in `openclaw tasks` and subject to task audit and maintenance. +### Wizard setup (recommended) -### Payload shapes (what runs) +```bash +openclaw webhooks gmail setup --account openclaw@gmail.com +``` -Two payload kinds are supported: +This writes `hooks.gmail` config, enables the Gmail preset, and uses Tailscale Funnel for the push endpoint. -- `systemEvent`: main-session only, routed through the heartbeat prompt. -- `agentTurn`: isolated-session only, runs a dedicated agent turn. +### Gateway auto-start -Common `agentTurn` fields: +When `hooks.enabled=true` and `hooks.gmail.account` is set, the Gateway starts `gog gmail watch serve` on boot and auto-renews the watch. Set `OPENCLAW_SKIP_GMAIL_WATCHER=1` to opt out. -- `message`: required text prompt. -- `model` / `thinking`: optional overrides (see below). -- `timeoutSeconds`: optional timeout override. -- `lightContext`: optional lightweight bootstrap mode for jobs that do not need workspace bootstrap file injection. -- `toolsAllow`: optional array of tool names to restrict which tools the job can use (e.g. `["exec", "read", "write"]`). +### Manual one-time setup -Delivery config: +1. Select the GCP project that owns the OAuth client used by `gog`: -- `delivery.mode`: `none` | `announce` | `webhook`. -- `delivery.channel`: `last` or a specific channel. -- `delivery.to`: channel-specific target (announce) or webhook URL (webhook mode). -- `delivery.threadId`: optional explicit thread or topic id when the target channel supports threaded delivery. -- `delivery.bestEffort`: avoid failing the job if announce delivery fails. +```bash +gcloud auth login +gcloud config set project +gcloud services enable gmail.googleapis.com pubsub.googleapis.com +``` -Announce delivery suppresses messaging tool sends for the run; use `delivery.channel`/`delivery.to` -to target the chat instead. When `delivery.mode = "none"`, no summary is posted to the main session. +2. Create topic and grant Gmail push access: -If `delivery` is omitted for isolated jobs, OpenClaw defaults to `announce`. +```bash +gcloud pubsub topics create gog-gmail-watch +gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \ + --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \ + --role=roles/pubsub.publisher +``` -#### Announce delivery flow +3. Start the watch: -When `delivery.mode = "announce"`, cron delivers directly via the outbound channel adapters. -The main agent is not spun up to craft or forward the message. +```bash +gog gmail watch start \ + --account openclaw@gmail.com \ + --label INBOX \ + --topic projects//topics/gog-gmail-watch +``` -Behavior details: +### Gmail model override -- Content: delivery uses the isolated run's outbound payloads (text/media) with normal chunking and - channel formatting. -- Heartbeat-only responses (`HEARTBEAT_OK` with no real content) are not delivered. -- If the isolated run already sent a message to the same target via the message tool, delivery is - skipped to avoid duplicates. -- Missing or invalid delivery targets fail the job unless `delivery.bestEffort = true`. -- A short summary is posted to the main session only when `delivery.mode = "announce"`. -- The main-session summary respects `wakeMode`: `now` triggers an immediate heartbeat and - `next-heartbeat` waits for the next scheduled heartbeat. +```json5 +{ + hooks: { + gmail: { + model: "openrouter/meta-llama/llama-3.3-70b-instruct:free", + thinking: "off", + }, + }, +} +``` -#### Webhook delivery flow +### Test -When `delivery.mode = "webhook"`, cron posts the finished event payload to `delivery.to` when the finished event includes a summary. +```bash +gog gmail send --account openclaw@gmail.com --to openclaw@gmail.com --subject "watch test" --body "ping" +gog gmail watch status --account openclaw@gmail.com +``` -Behavior details: +## Managing jobs -- 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 `. -- 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"`. +```bash +# List all jobs +openclaw cron list -### Model and thinking overrides +# Edit a job +openclaw cron edit --message "Updated prompt" --model "opus" -Isolated jobs (`agentTurn`) can override the model and thinking level: +# Force run a job now +openclaw cron run -- `model`: Provider/model string (e.g., `anthropic/claude-sonnet-4-20250514`) or alias (e.g., `opus`) -- `thinking`: Thinking level (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`; GPT-5.2 + Codex models only) +# Run only if due +openclaw cron run --due -Note: You can set `model` on main-session jobs too, but it changes the shared main -session model. We recommend model overrides only for isolated jobs to avoid -unexpected context shifts. +# View run history +openclaw cron runs --id --limit 50 -Resolution priority: +# Delete a job +openclaw cron remove -1. Job payload override (highest) -2. Hook-specific defaults (e.g., `hooks.gmail.model`) -3. Agent config default - -### Lightweight bootstrap context - -Isolated jobs (`agentTurn`) can set `lightContext: true` to run with lightweight bootstrap context. - -- Use this for scheduled chores that do not need workspace bootstrap file injection. -- In practice, the embedded runtime runs with `bootstrapContextMode: "lightweight"`, which keeps cron bootstrap context empty on purpose. -- CLI equivalents: `openclaw cron add --light-context ...` and `openclaw cron edit --light-context`. - -### Delivery (channel + target) - -Isolated jobs can deliver output to a channel via the top-level `delivery` config: - -- `delivery.mode`: `announce` (channel delivery), `webhook` (HTTP POST), or `none`. -- `delivery.channel`: `last` or any deliverable channel id, for example `discord`, `matrix`, `telegram`, or `whatsapp`. -- `delivery.to`: channel-specific recipient target. -- `delivery.threadId`: optional thread/topic override for channels like Telegram, Slack, Discord, or Matrix when you want a specific thread without encoding it into `delivery.to`. - -`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 session’s -“last route” (the last place the agent replied). - -Target format reminders: - -- Slack/Discord/Mattermost (plugin) targets should use explicit prefixes (e.g. `channel:`, `user:`) to avoid ambiguity. - Mattermost bare 26-char IDs are resolved **user-first** (DM if user exists, channel otherwise) — use `user:` or `channel:` for deterministic routing. -- Telegram topics should use the `:topic:` form (see below). - -#### Telegram delivery targets (topics / forum threads) - -Telegram supports forum topics via `message_thread_id`. For cron delivery, you can encode -the topic/thread into the `to` field: - -- `-1001234567890` (chat id only) -- `-1001234567890:topic:123` (preferred: explicit topic marker) -- `-1001234567890:123` (shorthand: numeric suffix) - -Prefixed targets like `telegram:...` / `telegram:group:...` are also accepted: - -- `telegram:group:-1001234567890:topic:123` +# Agent selection (multi-agent setups) +openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops +openclaw cron edit --clear-agent +``` ## JSON schema for tool calls -Use these shapes when calling Gateway `cron.*` tools directly (agent tool calls or RPC). -CLI flags accept human durations like `20m`, but tool calls should use an ISO 8601 string -for `schedule.at` and milliseconds for `schedule.everyMs`. - -### cron.add params - -One-shot, main session job (system event): +One-shot main session job: ```json { @@ -322,440 +295,94 @@ One-shot, main session job (system event): } ``` -Recurring, isolated job with delivery: +Recurring isolated job with delivery: ```json { "name": "Morning brief", "schedule": { "kind": "cron", "expr": "0 7 * * *", "tz": "America/Los_Angeles" }, "sessionTarget": "isolated", - "wakeMode": "next-heartbeat", - "payload": { - "kind": "agentTurn", - "message": "Summarize overnight updates.", - "lightContext": true - }, - "delivery": { - "mode": "announce", - "channel": "slack", - "to": "channel:C1234567890", - "bestEffort": true - } + "payload": { "kind": "agentTurn", "message": "Summarize overnight updates." }, + "delivery": { "mode": "announce", "channel": "slack", "to": "channel:C1234567890" } } ``` -Recurring job bound to current session (auto-resolved at creation): - -```json -{ - "name": "Daily standup", - "schedule": { "kind": "cron", "expr": "0 9 * * *" }, - "sessionTarget": "current", - "payload": { - "kind": "agentTurn", - "message": "Summarize yesterday's progress." - } -} -``` - -Recurring job in a custom persistent session: - -```json -{ - "name": "Project monitor", - "schedule": { "kind": "every", "everyMs": 300000 }, - "sessionTarget": "session:project-alpha-monitor", - "payload": { - "kind": "agentTurn", - "message": "Check project status and update the running log." - } -} -``` - -Notes: - -- `schedule.kind`: `at` (`at`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`). -- `schedule.at` accepts ISO 8601. Tool/API values without a timezone are treated as UTC; the CLI also accepts `openclaw cron add|edit --at "" --tz ` for local wall-clock one-shots. -- `everyMs` is milliseconds. -- `sessionTarget`: `"main"`, `"isolated"`, `"current"`, or `"session:"`. -- `"current"` is resolved to `"session:"` at creation time. -- Custom sessions (`session:xxx`) maintain persistent context across runs. -- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun` (defaults to true for `at`), - `delivery`, `toolsAllow`. -- `toolsAllow`: optional array of tool names to restrict which tools the job can use (e.g. `["exec", "read"]`). Omit or set `null` to use all tools. -- `wakeMode` defaults to `"now"` when omitted. - -### cron.update params - -```json -{ - "jobId": "job-123", - "patch": { - "enabled": false, - "schedule": { "kind": "every", "everyMs": 3600000 } - } -} -``` - -Notes: - -- `jobId` is canonical; `id` is accepted for compatibility. -- Use `agentId: null` in the patch to clear an agent binding. - -### cron.run and cron.remove params - -```json -{ "jobId": "job-123", "mode": "force" } -``` - -```json -{ "jobId": "job-123" } -``` - -## Storage & history - -- Job store: `~/.openclaw/cron/jobs.json` (Gateway-managed JSON). -- Run history: `~/.openclaw/cron/runs/.jsonl` (JSONL, auto-pruned by size and line count). -- Isolated cron run sessions in `sessions.json` are pruned by `cron.sessionRetention` (default `24h`; set `false` to disable). -- Override store path: `cron.store` in config. - -## Retry policy - -When a job fails, OpenClaw classifies errors as **transient** (retryable) or **permanent** (disable immediately). - -### Transient errors (retried) - -- Rate limit (429, too many requests, resource exhausted) -- Provider overload (for example Anthropic `529 overloaded_error`, overload fallback summaries) -- Network errors (timeout, ECONNRESET, fetch failed, socket) -- Server errors (5xx) -- Cloudflare-related errors - -### Permanent errors (no retry) - -- Auth failures (invalid API key, unauthorized) -- Config or validation errors -- Other non-transient errors - -### Default behavior (no config) - -**One-shot jobs (`schedule.kind: "at"`):** - -- On transient error: retry up to 3 times with exponential backoff (30s → 1m → 5m). -- On permanent error: disable immediately. -- On success or skip: disable (or delete if `deleteAfterRun: true`). - -**Recurring jobs (`cron` / `every`):** - -- On any error: apply exponential backoff (30s → 1m → 5m → 15m → 60m) before the next scheduled run. -- Job stays enabled; backoff resets after the next successful run. - -Configure `cron.retry` to override these defaults (see [Configuration](/automation/cron-jobs#configuration)). - ## Configuration ```json5 { cron: { - enabled: true, // default true + enabled: true, store: "~/.openclaw/cron/jobs.json", - maxConcurrentRuns: 1, // default 1 - // Optional: override retry policy for one-shot jobs + maxConcurrentRuns: 1, retry: { maxAttempts: 3, backoffMs: [60000, 120000, 300000], retryOn: ["rate_limit", "overloaded", "network", "server_error"], }, - webhook: "https://example.invalid/legacy", // deprecated fallback for stored notify:true jobs - webhookToken: "replace-with-dedicated-webhook-token", // optional bearer token for webhook mode - sessionRetention: "24h", // duration string or false - runLog: { - maxBytes: "2mb", // default 2_000_000 bytes - keepLines: 2000, // default 2000 - }, + webhookToken: "replace-with-dedicated-webhook-token", + sessionRetention: "24h", + runLog: { maxBytes: "2mb", keepLines: 2000 }, }, } ``` -Run-log pruning behavior: +Disable cron: `cron.enabled: false` or `OPENCLAW_SKIP_CRON=1`. -- `cron.runLog.maxBytes`: max run-log file size before pruning. -- `cron.runLog.keepLines`: when pruning, keep only the newest N lines. -- Both apply to `cron/runs/.jsonl` files. +### Retry policy -Webhook behavior: +**One-shot jobs**: retry transient errors (rate limit, overload, network, server error) up to 3 times with exponential backoff. Permanent errors disable immediately. -- Preferred: set `delivery.mode: "webhook"` with `delivery.to: "https://..."` per job. -- Webhook URLs must be valid `http://` or `https://` URLs. -- When posted, payload is the cron finished event JSON. -- If `cron.webhookToken` is set, auth header is `Authorization: Bearer `. -- 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. +**Recurring jobs**: exponential backoff (30s to 60m) between retries. Backoff resets after the next successful run. -Disable cron entirely: +### Maintenance -- `cron.enabled: false` (config) -- `OPENCLAW_SKIP_CRON=1` (env) - -## Maintenance - -Cron has two built-in maintenance paths: isolated run-session retention and run-log pruning. - -### Defaults - -- `cron.sessionRetention`: `24h` (set `false` to disable run-session pruning) -- `cron.runLog.maxBytes`: `2_000_000` bytes -- `cron.runLog.keepLines`: `2000` - -### How it works - -- Isolated runs create session entries (`...:cron::run:`) and transcript files. -- The reaper removes expired run-session entries older than `cron.sessionRetention`. -- For removed run sessions no longer referenced by the session store, OpenClaw archives transcript files and purges old deleted archives on the same retention window. -- After each run append, `cron/runs/.jsonl` is size-checked: - - if file size exceeds `runLog.maxBytes`, it is trimmed to the newest `runLog.keepLines` lines. - -### Performance caveat for high volume schedulers - -High-frequency cron setups can generate large run-session and run-log footprints. Maintenance is built in, but loose limits can still create avoidable IO and cleanup work. - -What to watch: - -- long `cron.sessionRetention` windows with many isolated runs -- high `cron.runLog.keepLines` combined with large `runLog.maxBytes` -- many noisy recurring jobs writing to the same `cron/runs/.jsonl` - -What to do: - -- keep `cron.sessionRetention` as short as your debugging/audit needs allow -- keep run logs bounded with moderate `runLog.maxBytes` and `runLog.keepLines` -- move noisy background jobs to isolated mode with delivery rules that avoid unnecessary chatter -- review growth periodically with `openclaw cron runs` and adjust retention before logs become large - -### Customize examples - -Keep run sessions for a week and allow bigger run logs: - -```json5 -{ - cron: { - sessionRetention: "7d", - runLog: { - maxBytes: "10mb", - keepLines: 5000, - }, - }, -} -``` - -Disable isolated run-session pruning but keep run-log pruning: - -```json5 -{ - cron: { - sessionRetention: false, - runLog: { - maxBytes: "5mb", - keepLines: 3000, - }, - }, -} -``` - -Tune for high-volume cron usage (example): - -```json5 -{ - cron: { - sessionRetention: "12h", - runLog: { - maxBytes: "3mb", - keepLines: 1500, - }, - }, -} -``` - -## CLI quickstart - -One-shot reminder (UTC ISO, auto-delete after success): - -```bash -openclaw cron add \ - --name "Send reminder" \ - --at "2026-01-12T18:00:00Z" \ - --session main \ - --system-event "Reminder: submit expense report." \ - --wake now \ - --delete-after-run -``` - -One-shot reminder (main session, wake immediately): - -```bash -openclaw cron add \ - --name "Calendar check" \ - --at "20m" \ - --session main \ - --system-event "Next heartbeat: check calendar." \ - --wake now -``` - -Recurring isolated job (announce to WhatsApp): - -```bash -openclaw cron add \ - --name "Morning status" \ - --cron "0 7 * * *" \ - --tz "America/Los_Angeles" \ - --session isolated \ - --message "Summarize inbox + calendar for today." \ - --announce \ - --channel whatsapp \ - --to "+15551234567" -``` - -Recurring cron job with explicit 30-second stagger: - -```bash -openclaw cron add \ - --name "Minute watcher" \ - --cron "0 * * * * *" \ - --tz "UTC" \ - --stagger 30s \ - --session isolated \ - --message "Run minute watcher checks." \ - --announce -``` - -Recurring isolated job (deliver to a Telegram topic): - -```bash -openclaw cron add \ - --name "Nightly summary (topic)" \ - --cron "0 22 * * *" \ - --tz "America/Los_Angeles" \ - --session isolated \ - --message "Summarize today; send to the nightly topic." \ - --announce \ - --channel telegram \ - --to "-1001234567890:topic:123" -``` - -Isolated job with model and thinking override: - -```bash -openclaw cron add \ - --name "Deep analysis" \ - --cron "0 6 * * 1" \ - --tz "America/Los_Angeles" \ - --session isolated \ - --message "Weekly deep analysis of project progress." \ - --model "opus" \ - --thinking high \ - --announce \ - --channel whatsapp \ - --to "+15551234567" -``` - -Agent selection (multi-agent setups): - -```bash -# Pin a job to agent "ops" (falls back to default if that agent is missing) -openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops - -# Switch or clear the agent on an existing job -openclaw cron edit --agent ops -openclaw cron edit --clear-agent -``` - -Tool allowlists (restrict which tools a job can use): - -```bash -# Only allow exec and read tools for this job -openclaw cron add --name "Scoped job" --cron "0 8 * * *" --session isolated --message "Run scoped checks" --tools exec,read - -# Update an existing job's tool allowlist -openclaw cron edit --tools exec,read,write - -# Remove a tool allowlist (use all tools) -openclaw cron edit --clear-tools -``` - -Manual run (force is the default, use `--due` to only run when due): - -```bash -openclaw cron run -openclaw cron run --due -``` - -`cron.run` now acknowledges once the manual run is queued, not after the job finishes. Successful queue responses look like `{ ok: true, enqueued: true, runId }`. If the job is already running or `--due` finds nothing due, the response stays `{ ok: true, ran: false, reason }`. Use `openclaw cron runs --id ` or the `cron.runs` gateway method to inspect the eventual finished entry. - -Edit an existing job (patch fields): - -```bash -openclaw cron edit \ - --message "Updated prompt" \ - --model "opus" \ - --thinking low -``` - -Force an existing cron job to run exactly on schedule (no stagger): - -```bash -openclaw cron edit --exact -``` - -Run history: - -```bash -openclaw cron runs --id --limit 50 -``` - -Immediate system event without creating a job: - -```bash -openclaw system event --mode now --text "Next heartbeat: check battery." -``` - -## Gateway API surface - -- `cron.list`, `cron.status`, `cron.add`, `cron.update`, `cron.remove` -- `cron.run` (force or due), `cron.runs` - For immediate system events without a job, use [`openclaw system event`](/cli/system). +- `cron.sessionRetention` (default `24h`): prune isolated run-session entries. +- `cron.runLog.maxBytes` / `cron.runLog.keepLines`: auto-prune run-log files. ## Troubleshooting -### "Nothing runs" +### Command ladder -- Check cron is enabled: `cron.enabled` and `OPENCLAW_SKIP_CRON`. -- Check the Gateway is running continuously (cron runs inside the Gateway process). -- For `cron` schedules: confirm timezone (`--tz`) vs the host timezone. +```bash +openclaw status +openclaw gateway status +openclaw cron status +openclaw cron list +openclaw cron runs --id --limit 20 +openclaw system heartbeat last +openclaw logs --follow +openclaw doctor +``` -### A recurring job keeps delaying after failures +### Cron not firing -- OpenClaw applies exponential retry backoff for recurring jobs after consecutive errors: - 30s, 1m, 5m, 15m, then 60m between retries. -- Backoff resets automatically after the next successful run. -- One-shot (`at`) jobs retry transient errors (rate limit, overloaded, network, server_error) up to 3 times with backoff; permanent errors disable immediately. See [Retry policy](/automation/cron-jobs#retry-policy). +- Check `cron.enabled` and `OPENCLAW_SKIP_CRON` env var. +- Confirm the Gateway is running continuously. +- For `cron` schedules, verify timezone (`--tz`) vs the host timezone. +- `reason: not-due` in run output means manual run called without `--force`. -### Telegram delivers to the wrong place +### Cron fired but no delivery -- For forum topics, use `-100…:topic:` so it’s explicit and unambiguous. -- If you see `telegram:...` prefixes in logs or stored “last route” targets, that’s normal; - cron delivery accepts them and still parses topic IDs correctly. +- Run succeeded but delivery mode is `none` means no external message is expected. +- Delivery target missing/invalid (`channel`/`to`) means outbound was skipped. +- Channel auth errors (`unauthorized`, `Forbidden`) mean delivery was blocked by credentials. -### Subagent announce delivery retries +### Heartbeat suppressed or skipped -- When a subagent run completes, the gateway announces the result to the requester session. -- If the announce flow returns `false` (e.g. requester session is busy), the gateway retries up to 3 times with tracking via `announceRetryCount`. -- Announces older than 5 minutes past `endedAt` are force-expired to prevent stale entries from looping indefinitely. -- If you see repeated announce deliveries in logs, check the subagent registry for entries with high `announceRetryCount` values. +- `reason=quiet-hours`: outside `activeHours`. +- `requests-in-flight`: main lane busy, heartbeat deferred. +- `empty-heartbeat-file`: `HEARTBEAT.md` has no actionable content and no cron event is queued. + +### Timezone gotchas + +- Cron without `--tz` uses the gateway host timezone. +- `at` schedules without timezone are treated as UTC. +- Heartbeat `activeHours` uses configured timezone resolution. ## Related -- [Automation Overview](/automation) — all automation mechanisms at a glance -- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) — when to use each +- [Automation & Tasks](/automation) — all automation mechanisms at a glance - [Background Tasks](/automation/tasks) — task ledger for cron executions - [Heartbeat](/gateway/heartbeat) — periodic main-session turns -- [Troubleshooting](/automation/troubleshooting) — debugging automation issues +- [Timezone](/concepts/timezone) — timezone configuration diff --git a/docs/automation/cron-vs-heartbeat.md b/docs/automation/cron-vs-heartbeat.md index 96e7837c0dd..2e411d3febf 100644 --- a/docs/automation/cron-vs-heartbeat.md +++ b/docs/automation/cron-vs-heartbeat.md @@ -1,299 +1,8 @@ --- -summary: "Guidance for choosing between heartbeat and cron jobs for automation" -read_when: - - Deciding how to schedule recurring tasks - - Setting up background monitoring or notifications - - Optimizing token usage for periodic checks +summary: "Redirect to /automation" title: "Cron vs Heartbeat" --- -# Cron vs Heartbeat: When to Use Each +# Cron vs Heartbeat -Both heartbeats and cron jobs let you run tasks on a schedule. This guide helps you choose the right mechanism for your use case. - -One important distinction: - -- **Heartbeat** is a scheduled **main-session turn** — no task record created. -- **Cron (main)** is a scheduled **system event into the main session** — creates a task record with `silent` notify policy. -- **Cron (isolated)** is a scheduled **background run** — creates a task record tracked in `openclaw tasks`. - -All cron job executions (main and isolated) create [task records](/automation/tasks). Heartbeat turns do not. Main-session cron tasks use `silent` notify policy by default so they do not generate notifications. - -## Quick Decision Guide - -| Use Case | Recommended | Why | -| ------------------------------------ | ------------------- | ---------------------------------------- | -| Check inbox every 30 min | Heartbeat | Batches with other checks, context-aware | -| Send daily report at 9am sharp | Cron (isolated) | Exact timing needed | -| Monitor calendar for upcoming events | Heartbeat | Natural fit for periodic awareness | -| Run weekly deep analysis | Cron (isolated) | Standalone task, can use different model | -| Remind me in 20 minutes | Cron (main, `--at`) | One-shot with precise timing | -| Background project health check | Heartbeat | Piggybacks on existing cycle | - -## Heartbeat: Periodic Awareness - -Heartbeats run in the **main session** at a regular interval (default: 30 min). They're designed for the agent to check on things and surface anything important. - -### When to use heartbeat - -- **Multiple periodic checks**: Instead of 5 separate cron jobs checking inbox, calendar, weather, notifications, and project status, a single heartbeat can batch all of these. -- **Context-aware decisions**: The agent has full main-session context, so it can make smart decisions about what's urgent vs. what can wait. -- **Conversational continuity**: Heartbeat runs share the same session, so the agent remembers recent conversations and can follow up naturally. -- **Low-overhead monitoring**: One heartbeat replaces many small polling tasks. - -### Heartbeat advantages - -- **Batches multiple checks**: One agent turn can review inbox, calendar, and notifications together. -- **Reduces API calls**: A single heartbeat is cheaper than 5 isolated cron jobs. -- **Context-aware**: The agent knows what you've been working on and can prioritize accordingly. -- **Smart suppression**: If nothing needs attention, the agent replies `HEARTBEAT_OK` and no message is delivered. -- **Natural timing**: Drifts slightly based on queue load, which is fine for most monitoring. -- **No task record**: heartbeat turns stay in main-session history (see [Background Tasks](/automation/tasks)). - -### Heartbeat example: HEARTBEAT.md checklist - -```md -# Heartbeat checklist - -- Check email for urgent messages -- Review calendar for events in next 2 hours -- If a background task finished, summarize results -- If idle for 8+ hours, send a brief check-in -``` - -The agent reads this on each heartbeat and handles all items in one turn. - -### Configuring heartbeat - -```json5 -{ - agents: { - defaults: { - heartbeat: { - every: "30m", // interval - target: "last", // explicit alert delivery target (default is "none") - activeHours: { start: "08:00", end: "22:00" }, // optional - }, - }, - }, -} -``` - -See [Heartbeat](/gateway/heartbeat) for full configuration. - -## Cron: Precise Scheduling - -Cron jobs run at precise times and can run in isolated sessions without affecting main context. -Recurring top-of-hour schedules are automatically spread by a deterministic -per-job offset in a 0-5 minute window. - -### When to use cron - -- **Exact timing required**: "Send this at 9:00 AM every Monday" (not "sometime around 9"). -- **Standalone tasks**: Tasks that don't need conversational context. -- **Different model/thinking**: Heavy analysis that warrants a more powerful model. -- **One-shot reminders**: "Remind me in 20 minutes" with `--at`. -- **Noisy/frequent tasks**: Tasks that would clutter main session history. -- **External triggers**: Tasks that should run independently of whether the agent is otherwise active. - -### Cron advantages - -- **Precise timing**: 5-field or 6-field (seconds) cron expressions with timezone support. -- **Built-in load spreading**: recurring top-of-hour schedules are staggered by up to 5 minutes by default. -- **Per-job control**: override stagger with `--stagger ` or force exact timing with `--exact`. -- **Session isolation**: Runs in `cron:` without polluting main history. -- **Model overrides**: Use a cheaper or more powerful model per job. -- **Delivery control**: Isolated jobs default to `announce` (summary); choose `none` as needed. -- **Immediate delivery**: Announce mode posts directly without waiting for heartbeat. -- **No agent context needed**: Runs even if main session is idle or compacted. -- **One-shot support**: `--at` for precise future timestamps. -- **Task tracking**: isolated jobs create [background task](/automation/tasks) records visible in `openclaw tasks` and `openclaw tasks audit`. - -### Cron example: Daily morning briefing - -```bash -openclaw cron add \ - --name "Morning briefing" \ - --cron "0 7 * * *" \ - --tz "America/New_York" \ - --session isolated \ - --message "Generate today's briefing: weather, calendar, top emails, news summary." \ - --model opus \ - --announce \ - --channel whatsapp \ - --to "+15551234567" -``` - -This runs at exactly 7:00 AM New York time, uses Opus for quality, and announces a summary directly to WhatsApp. - -### Cron example: One-shot reminder - -```bash -openclaw cron add \ - --name "Meeting reminder" \ - --at "20m" \ - --session main \ - --system-event "Reminder: standup meeting starts in 10 minutes." \ - --wake now \ - --delete-after-run -``` - -See [Cron jobs](/automation/cron-jobs) for full CLI reference. - -## Decision Flowchart - -``` -Does the task need to run at an EXACT time? - YES -> Use cron - NO -> Continue... - -Does the task need isolation from main session? - YES -> Use cron (isolated) - NO -> Continue... - -Can this task be batched with other periodic checks? - YES -> Use heartbeat (add to HEARTBEAT.md) - NO -> Use cron - -Is this a one-shot reminder? - YES -> Use cron with --at - NO -> Continue... - -Does it need a different model or thinking level? - YES -> Use cron (isolated) with --model/--thinking - NO -> Use heartbeat -``` - -## Combining Both - -The most efficient setup uses **both**: - -1. **Heartbeat** handles routine monitoring (inbox, calendar, notifications) in one batched turn every 30 minutes. -2. **Cron** handles precise schedules (daily reports, weekly reviews) and one-shot reminders. - -### Example: Efficient automation setup - -**HEARTBEAT.md** (checked every 30 min): - -```md -# Heartbeat checklist - -- Scan inbox for urgent emails -- Check calendar for events in next 2h -- Review any pending tasks -- Light check-in if quiet for 8+ hours -``` - -**Cron jobs** (precise timing): - -```bash -# Daily morning briefing at 7am -openclaw cron add --name "Morning brief" --cron "0 7 * * *" --session isolated --message "..." --announce - -# Weekly project review on Mondays at 9am -openclaw cron add --name "Weekly review" --cron "0 9 * * 1" --session isolated --message "..." --model opus - -# One-shot reminder -openclaw cron add --name "Call back" --at "2h" --session main --system-event "Call back the client" --wake now -``` - -## Lobster: Deterministic workflows with approvals - -Lobster is the workflow runtime for **multi-step tool pipelines** that need deterministic execution and explicit approvals. -Use it when the task is more than a single agent turn, and you want a resumable workflow with human checkpoints. - -### When Lobster fits - -- **Multi-step automation**: You need a fixed pipeline of tool calls, not a one-off prompt. -- **Approval gates**: Side effects should pause until you approve, then resume. -- **Resumable runs**: Continue a paused workflow without re-running earlier steps. - -### How it pairs with heartbeat and cron - -- **Heartbeat/cron** decide _when_ a run happens. -- **Lobster** defines _what steps_ happen once the run starts. - -For scheduled workflows, use cron or heartbeat to trigger an agent turn that calls Lobster. -For ad-hoc workflows, call Lobster directly. - -### Operational notes (from the code) - -- Lobster runs as a **local subprocess** (`lobster` CLI) in tool mode and returns a **JSON envelope**. -- If the tool returns `needs_approval`, you resume with a `resumeToken` and `approve` flag. -- The tool is an **optional plugin**; enable it additively via `tools.alsoAllow: ["lobster"]` (recommended). -- Lobster expects the `lobster` CLI to be available on `PATH`. - -See [Lobster](/tools/lobster) for full usage and examples. - -## Main Session vs Isolated Session - -Both heartbeat and cron can interact with the main session, but differently: - -| | Heartbeat | Cron (main) | Cron (isolated) | -| -------------------------- | ------------------------------- | ------------------------ | ----------------------------------------------- | -| Session | Main | Main (via system event) | `cron:` or custom session | -| History | Shared | Shared | Fresh each run (isolated) / Persistent (custom) | -| Context | Full | Full | None (isolated) / Cumulative (custom) | -| Model | Main session model | Main session model | Can override | -| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Announce summary (default) | -| [Tasks](/automation/tasks) | No task record | Task record (silent) | Task record (visible in `openclaw tasks`) | - -### When to use main session cron - -Use `--session main` with `--system-event` when you want: - -- The reminder/event to appear in main session context -- The agent to handle it during the next heartbeat with full context -- No separate isolated run - -```bash -openclaw cron add \ - --name "Check project" \ - --every "4h" \ - --session main \ - --system-event "Time for a project health check" \ - --wake now -``` - -### When to use isolated cron - -Use `--session isolated` when you want: - -- A clean slate without prior context -- Different model or thinking settings -- Announce summaries directly to a channel -- History that doesn't clutter main session - -```bash -openclaw cron add \ - --name "Deep analysis" \ - --cron "0 6 * * 0" \ - --session isolated \ - --message "Weekly codebase analysis..." \ - --model opus \ - --thinking high \ - --announce -``` - -## Cost Considerations - -| Mechanism | Cost Profile | -| --------------- | ------------------------------------------------------- | -| Heartbeat | One turn every N minutes; scales with HEARTBEAT.md size | -| Cron (main) | Adds event to next heartbeat (no isolated turn) | -| Cron (isolated) | Full agent turn per job; can use cheaper model | - -**Tips**: - -- Keep `HEARTBEAT.md` small to minimize token overhead. -- Batch similar checks into heartbeat instead of multiple cron jobs. -- Use `target: "none"` on heartbeat if you only want internal processing. -- Use isolated cron with a cheaper model for routine tasks. - -## Related - -- [Automation Overview](/automation) — all automation mechanisms at a glance -- [Heartbeat](/gateway/heartbeat) — full heartbeat configuration -- [Cron jobs](/automation/cron-jobs) — full cron CLI and API reference -- [Background Tasks](/automation/tasks) — task ledger, audit, and lifecycle -- [System](/cli/system) — system events + heartbeat controls +This page moved to [Automation & Tasks](/automation). See [Automation & Tasks](/automation) for the decision guide comparing cron and heartbeat. diff --git a/docs/automation/gmail-pubsub.md b/docs/automation/gmail-pubsub.md index b853b995599..57dff64eaaf 100644 --- a/docs/automation/gmail-pubsub.md +++ b/docs/automation/gmail-pubsub.md @@ -1,256 +1,8 @@ --- -summary: "Gmail Pub/Sub push wired into OpenClaw webhooks via gogcli" -read_when: - - Wiring Gmail inbox triggers to OpenClaw - - Setting up Pub/Sub push for agent wake +summary: "Redirect to /automation/cron-jobs" title: "Gmail PubSub" --- -# Gmail Pub/Sub -> OpenClaw +# Gmail PubSub -Goal: Gmail watch -> Pub/Sub push -> `gog gmail watch serve` -> OpenClaw webhook. - -## Prereqs - -- `gcloud` installed and logged in ([install guide](https://docs.cloud.google.com/sdk/docs/install-sdk)). -- `gog` (gogcli) installed and authorized for the Gmail account ([gogcli.sh](https://gogcli.sh/)). -- OpenClaw hooks enabled (see [Webhooks](/automation/webhook)). -- `tailscale` logged in ([tailscale.com](https://tailscale.com/)). Supported setup uses Tailscale Funnel for the public HTTPS endpoint. - Other tunnel services can work, but are DIY/unsupported and require manual wiring. - Right now, Tailscale is what we support. - -Example hook config (enable Gmail preset mapping): - -```json5 -{ - hooks: { - enabled: true, - token: "OPENCLAW_HOOK_TOKEN", - path: "/hooks", - presets: ["gmail"], - }, -} -``` - -To deliver the Gmail summary to a chat surface, override the preset with a mapping -that sets `deliver` + optional `channel`/`to`: - -```json5 -{ - hooks: { - enabled: true, - token: "OPENCLAW_HOOK_TOKEN", - presets: ["gmail"], - mappings: [ - { - match: { path: "gmail" }, - action: "agent", - wakeMode: "now", - name: "Gmail", - sessionKey: "hook:gmail:{{messages[0].id}}", - messageTemplate: "New email from {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}\n{{messages[0].body}}", - model: "openai/gpt-5.2-mini", - deliver: true, - channel: "last", - // to: "+15551234567" - }, - ], - }, -} -``` - -If you want a fixed channel, set `channel` + `to`. Otherwise `channel: "last"` -uses the last delivery route (falls back to WhatsApp). - -To force a cheaper model for Gmail runs, set `model` in the mapping -(`provider/model` or alias). If you enforce `agents.defaults.models`, include it there. - -To set a default model and thinking level specifically for Gmail hooks, add -`hooks.gmail.model` / `hooks.gmail.thinking` in your config: - -```json5 -{ - hooks: { - gmail: { - model: "openrouter/meta-llama/llama-3.3-70b-instruct:free", - thinking: "off", - }, - }, -} -``` - -Notes: - -- Per-hook `model`/`thinking` in the mapping still overrides these defaults. -- Fallback order: `hooks.gmail.model` → `agents.defaults.model.fallbacks` → primary (auth/rate-limit/timeouts). -- If `agents.defaults.models` is set, the Gmail model must be in the allowlist. -- Gmail hook content is wrapped with external-content safety boundaries by default. - To disable (dangerous), set `hooks.gmail.allowUnsafeExternalContent: true`. - -To customize payload handling further, add `hooks.mappings` or a JS/TS transform module -under `~/.openclaw/hooks/transforms` (see [Webhooks](/automation/webhook)). - -## Wizard (recommended) - -Use the OpenClaw helper to wire everything together (installs deps on macOS via brew): - -```bash -openclaw webhooks gmail setup \ - --account openclaw@gmail.com -``` - -Defaults: - -- Uses Tailscale Funnel for the public push endpoint. -- Writes `hooks.gmail` config for `openclaw webhooks gmail run`. -- Enables the Gmail hook preset (`hooks.presets: ["gmail"]`). - -Path note: when `tailscale.mode` is enabled, OpenClaw automatically sets -`hooks.gmail.serve.path` to `/` and keeps the public path at -`hooks.gmail.tailscale.path` (default `/gmail-pubsub`) because Tailscale -strips the set-path prefix before proxying. -If you need the backend to receive the prefixed path, set -`hooks.gmail.tailscale.target` (or `--tailscale-target`) to a full URL like -`http://127.0.0.1:8788/gmail-pubsub` and match `hooks.gmail.serve.path`. - -Want a custom endpoint? Use `--push-endpoint ` or `--tailscale off`. - -Platform note: on macOS the wizard installs `gcloud`, `gogcli`, and `tailscale` -via Homebrew; on Linux install them manually first. - -Gateway auto-start (recommended): - -- When `hooks.enabled=true` and `hooks.gmail.account` is set, the Gateway starts - `gog gmail watch serve` on boot and auto-renews the watch. -- Set `OPENCLAW_SKIP_GMAIL_WATCHER=1` to opt out (useful if you run the daemon yourself). -- Do not run the manual daemon at the same time, or you will hit - `listen tcp 127.0.0.1:8788: bind: address already in use`. - -Manual daemon (starts `gog gmail watch serve` + auto-renew): - -```bash -openclaw webhooks gmail run -``` - -## One-time setup - -1. Select the GCP project **that owns the OAuth client** used by `gog`. - -```bash -gcloud auth login -gcloud config set project -``` - -Note: Gmail watch requires the Pub/Sub topic to live in the same project as the OAuth client. - -2. Enable APIs: - -```bash -gcloud services enable gmail.googleapis.com pubsub.googleapis.com -``` - -3. Create a topic: - -```bash -gcloud pubsub topics create gog-gmail-watch -``` - -4. Allow Gmail push to publish: - -```bash -gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \ - --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \ - --role=roles/pubsub.publisher -``` - -## Start the watch - -```bash -gog gmail watch start \ - --account openclaw@gmail.com \ - --label INBOX \ - --topic projects//topics/gog-gmail-watch -``` - -Save the `history_id` from the output (for debugging). - -## Run the push handler - -Local example (shared token auth): - -```bash -gog gmail watch serve \ - --account openclaw@gmail.com \ - --bind 127.0.0.1 \ - --port 8788 \ - --path /gmail-pubsub \ - --token \ - --hook-url http://127.0.0.1:18789/hooks/gmail \ - --hook-token OPENCLAW_HOOK_TOKEN \ - --include-body \ - --max-bytes 20000 -``` - -Notes: - -- `--token` protects the push endpoint (`x-gog-token` or `?token=`). -- `--hook-url` points to OpenClaw `/hooks/gmail` (mapped; isolated run + summary to main). -- `--include-body` and `--max-bytes` control the body snippet sent to OpenClaw. - -Recommended: `openclaw webhooks gmail run` wraps the same flow and auto-renews the watch. - -## Expose the handler (advanced, unsupported) - -If you need a non-Tailscale tunnel, wire it manually and use the public URL in the push -subscription (unsupported, no guardrails): - -```bash -cloudflared tunnel --url http://127.0.0.1:8788 --no-autoupdate -``` - -Use the generated URL as the push endpoint: - -```bash -gcloud pubsub subscriptions create gog-gmail-watch-push \ - --topic gog-gmail-watch \ - --push-endpoint "https:///gmail-pubsub?token=" -``` - -Production: use a stable HTTPS endpoint and configure Pub/Sub OIDC JWT, then run: - -```bash -gog gmail watch serve --verify-oidc --oidc-email -``` - -## Test - -Send a message to the watched inbox: - -```bash -gog gmail send \ - --account openclaw@gmail.com \ - --to openclaw@gmail.com \ - --subject "watch test" \ - --body "ping" -``` - -Check watch state and history: - -```bash -gog gmail watch status --account openclaw@gmail.com -gog gmail history --account openclaw@gmail.com --since -``` - -## Troubleshooting - -- `Invalid topicName`: project mismatch (topic not in the OAuth client project). -- `User not authorized`: missing `roles/pubsub.publisher` on the topic. -- Empty messages: Gmail push only provides `historyId`; fetch via `gog gmail history`. - -## Cleanup - -```bash -gog gmail watch stop --account openclaw@gmail.com -gcloud pubsub subscriptions delete gog-gmail-watch-push -gcloud pubsub topics delete gog-gmail-watch -``` +This page moved to [Scheduled Tasks](/automation/cron-jobs#gmail-pubsub-integration). See [Scheduled Tasks](/automation/cron-jobs#gmail-pubsub-integration) for Gmail PubSub documentation. diff --git a/docs/automation/hooks.md b/docs/automation/hooks.md index 57dca031832..ef4197ea033 100644 --- a/docs/automation/hooks.md +++ b/docs/automation/hooks.md @@ -15,7 +15,7 @@ Hooks provide an extensible event-driven system for automating actions in respon Hooks are small scripts that run when something happens. There are two kinds: - **Hooks** (this page): run inside the Gateway when agent events fire, like `/new`, `/reset`, `/stop`, or lifecycle events. -- **Webhooks**: external HTTP webhooks that let other systems trigger work in OpenClaw. See [Webhook Hooks](/automation/webhook) or use `openclaw webhooks` for Gmail helper commands. +- **Webhooks**: external HTTP webhooks that let other systems trigger work in OpenClaw. See [Webhooks](/automation/cron-jobs#webhooks) or use `openclaw webhooks` for Gmail helper commands. Hooks can also be bundled inside plugins; see [Plugin hooks](/plugins/architecture#provider-runtime-hooks). `openclaw hooks list` shows both standalone hooks and plugin-managed hooks. @@ -1372,5 +1372,5 @@ node -e "import('./path/to/handler.ts').then(console.log)" - [CLI Reference: hooks](/cli/hooks) - [Bundled Hooks README](https://github.com/openclaw/openclaw/tree/main/src/hooks/bundled) -- [Webhook Hooks](/automation/webhook) +- [Webhooks](/automation/cron-jobs#webhooks) - [Configuration](/gateway/configuration-reference#hooks) diff --git a/docs/automation/index.md b/docs/automation/index.md index 203dd0b83c0..fdd7c0b8454 100644 --- a/docs/automation/index.md +++ b/docs/automation/index.md @@ -1,15 +1,15 @@ --- -summary: "Overview of all automation mechanisms: heartbeat, cron, tasks, hooks, webhooks, and more" +summary: "Overview of automation mechanisms: tasks, cron, hooks, standing orders, and TaskFlow" read_when: - Deciding how to automate work with OpenClaw - - Choosing between heartbeat, cron, hooks, and webhooks + - Choosing between heartbeat, cron, hooks, and standing orders - Looking for the right automation entry point -title: "Automation Overview" +title: "Automation & Tasks" --- -# Automation +# Automation & Tasks -OpenClaw provides several automation mechanisms, each suited to different use cases. This page helps you choose the right one. +OpenClaw runs work in the background through tasks, scheduled jobs, event hooks, and standing instructions. This page helps you choose the right mechanism and understand how they fit together. ## Quick decision guide @@ -23,44 +23,65 @@ flowchart TD C -->|No| G[Standing Orders] ``` -## Mechanisms at a glance +| Use case | Recommended | Why | +| ------------------------------------ | ------------------- | ---------------------------------------- | +| Check inbox every 30 min | Heartbeat | Batches with other checks, context-aware | +| Send daily report at 9 AM sharp | Cron (isolated) | Exact timing needed | +| Monitor calendar for upcoming events | Heartbeat | Natural fit for periodic awareness | +| Run weekly deep analysis | Cron (isolated) | Standalone task, can use different model | +| Remind me in 20 minutes | Cron (main, `--at`) | One-shot with precise timing | +| React to commands or lifecycle | Hooks | Event-driven, runs custom scripts | +| Persistent agent instructions | Standing orders | Injected into every session | -| Mechanism | What it does | Runs in | Creates task record | -| ---------------------------------------------- | -------------------------------------------------------- | ------------------------ | ------------------- | -| [Heartbeat](/gateway/heartbeat) | Periodic main-session turn — batches multiple checks | Main session | No | -| [Cron](/automation/cron-jobs) | Scheduled jobs with precise timing | Main or isolated session | Yes (all types) | -| [Background Tasks](/automation/tasks) | Tracks detached work (cron, ACP, subagents, CLI) | N/A (ledger) | N/A | -| [Hooks](/automation/hooks) | Event-driven scripts triggered by agent lifecycle events | Hook runner | No | -| [Standing Orders](/automation/standing-orders) | Persistent instructions injected into the system prompt | Main session | No | -| [Webhooks](/automation/webhook) | Receive inbound HTTP events and route to the agent | Gateway HTTP | No | +## Core concepts -### Specialized automation +### Tasks -| Mechanism | What it does | -| ---------------------------------------------- | ----------------------------------------------- | -| [Gmail PubSub](/automation/gmail-pubsub) | Real-time Gmail notifications via Google PubSub | -| [Polling](/automation/poll) | Periodic data source checks (RSS, APIs, etc.) | -| [Auth Monitoring](/automation/auth-monitoring) | Credential health and expiry alerts | +The background task ledger tracks all detached work: ACP runs, subagent spawns, isolated cron executions, and CLI operations. Tasks are records, not schedulers. Use `openclaw tasks list` and `openclaw tasks audit` to inspect them. + +See [Background Tasks](/automation/tasks). + +### Scheduled tasks (cron) + +Cron is the Gateway's built-in scheduler for precise timing. It persists jobs, wakes the agent at the right time, and can deliver output to a chat channel or webhook. Supports one-shot reminders, recurring expressions, and inbound webhook triggers. + +See [Scheduled Tasks](/automation/cron-jobs). + +### TaskFlow + +TaskFlow is the flow orchestration substrate above background tasks. It manages durable multi-step flows with managed and mirrored sync modes, revision tracking, and `openclaw tasks flow list|show|cancel` for inspection. + +See [TaskFlow](/automation/taskflow). + +### Heartbeat + +Heartbeat is a periodic main-session turn (default every 30 minutes). It batches multiple checks (inbox, calendar, notifications) in one agent turn with full session context. Heartbeat turns do not create task records. Use `HEARTBEAT.md` to define what the agent checks. + +See [Heartbeat](/gateway/heartbeat). + +### Hooks + +Hooks are event-driven scripts triggered by agent lifecycle events (`/new`, `/reset`, `/stop`), session compaction, gateway startup, message flow, and tool calls. Hooks are automatically discovered from directories and can be managed with `openclaw hooks`. + +See [Hooks](/automation/hooks). + +### Standing orders + +Standing orders grant the agent permanent operating authority for defined programs. They live in workspace files (typically `AGENTS.md`) and are injected into every session. Combine with cron for time-based enforcement. + +See [Standing Orders](/automation/standing-orders). ## How they work together -The most effective setups combine multiple mechanisms: - -1. **Heartbeat** handles routine monitoring (inbox, calendar, notifications) in one batched turn every 30 minutes. -2. **Cron** handles precise schedules (daily reports, weekly reviews) and one-shot reminders. -3. **Hooks** react to specific events (tool calls, session resets, compaction) with custom scripts. -4. **Standing Orders** give the agent persistent context ("always check the project board before replying"). -5. **Background Tasks** automatically track all detached work so you can inspect and audit it. - -See [Cron vs Heartbeat](/automation/cron-vs-heartbeat) for a detailed comparison of the two scheduling mechanisms. - -## TaskFlow - -[TaskFlow](/automation/taskflow) is the flow orchestration substrate above background tasks. It manages durable multi-step flows with managed and mirrored sync modes, and exposes `openclaw tasks flow list|show|cancel` for inspection and recovery. See [TaskFlow](/automation/taskflow) for details. +- **Heartbeat** handles routine monitoring (inbox, calendar, notifications) in one batched turn every 30 minutes. +- **Cron** handles precise schedules (daily reports, weekly reviews) and one-shot reminders. All cron executions create task records. +- **Hooks** react to specific events (tool calls, session resets, compaction) with custom scripts. +- **Standing orders** give the agent persistent context and authority boundaries. +- **TaskFlow** coordinates multi-step flows above individual tasks. +- **Tasks** automatically track all detached work so you can inspect and audit it. ## Related -- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) — detailed comparison guide - [TaskFlow](/automation/taskflow) — flow orchestration above tasks -- [Troubleshooting](/automation/troubleshooting) — debugging automation issues +- [Heartbeat](/gateway/heartbeat) — periodic main-session turns - [Configuration Reference](/gateway/configuration-reference) — all config keys diff --git a/docs/automation/poll.md b/docs/automation/poll.md index de666c7acba..d990cffbf4e 100644 --- a/docs/automation/poll.md +++ b/docs/automation/poll.md @@ -1,86 +1,8 @@ --- -summary: "Poll sending via gateway + CLI" -read_when: - - Adding or modifying poll support - - Debugging poll sends from the CLI or gateway +summary: "Redirect to /tools/message" title: "Polls" --- # Polls -## Supported channels - -- Telegram -- WhatsApp (web channel) -- Discord -- Microsoft Teams (Adaptive Cards) - -## CLI - -```bash -# Telegram -openclaw message poll --channel telegram --target 123456789 \ - --poll-question "Ship it?" --poll-option "Yes" --poll-option "No" -openclaw message poll --channel telegram --target -1001234567890:topic:42 \ - --poll-question "Pick a time" --poll-option "10am" --poll-option "2pm" \ - --poll-duration-seconds 300 - -# WhatsApp -openclaw message poll --target +15555550123 \ - --poll-question "Lunch today?" --poll-option "Yes" --poll-option "No" --poll-option "Maybe" -openclaw message poll --target 123456789@g.us \ - --poll-question "Meeting time?" --poll-option "10am" --poll-option "2pm" --poll-option "4pm" --poll-multi - -# Discord -openclaw message poll --channel discord --target channel:123456789 \ - --poll-question "Snack?" --poll-option "Pizza" --poll-option "Sushi" -openclaw message poll --channel discord --target channel:123456789 \ - --poll-question "Plan?" --poll-option "A" --poll-option "B" --poll-duration-hours 48 - -# Microsoft Teams -openclaw message poll --channel msteams --target conversation:19:abc@thread.tacv2 \ - --poll-question "Lunch?" --poll-option "Pizza" --poll-option "Sushi" -``` - -Options: - -- `--channel`: `whatsapp` (default), `telegram`, `discord`, or `msteams` -- `--poll-multi`: allow selecting multiple options -- `--poll-duration-hours`: Discord-only (defaults to 24 when omitted) -- `--poll-duration-seconds`: Telegram-only (5-600 seconds) -- `--poll-anonymous` / `--poll-public`: Telegram-only poll visibility - -## Gateway RPC - -Method: `poll` - -Params: - -- `to` (string, required) -- `question` (string, required) -- `options` (string[], required) -- `maxSelections` (number, optional) -- `durationHours` (number, optional) -- `durationSeconds` (number, optional, Telegram-only) -- `isAnonymous` (boolean, optional, Telegram-only) -- `channel` (string, optional, default: `whatsapp`) -- `idempotencyKey` (string, required) - -## Channel differences - -- Telegram: 2-10 options. Supports forum topics via `threadId` or `:topic:` targets. Uses `durationSeconds` instead of `durationHours`, limited to 5-600 seconds. Supports anonymous and public polls. -- WhatsApp: 2-12 options, `maxSelections` must be within option count, ignores `durationHours`. -- Discord: 2-10 options, `durationHours` clamped to 1-768 hours (default 24). `maxSelections > 1` enables multi-select; Discord does not support a strict selection count. -- Microsoft Teams: Adaptive Card polls (OpenClaw-managed). No native poll API; `durationHours` is ignored. - -## Agent tool (Message) - -Use the `message` tool with `poll` action (`to`, `pollQuestion`, `pollOption`, optional `pollMulti`, `pollDurationHours`, `channel`). - -For Telegram, the tool also accepts `pollDurationSeconds`, `pollAnonymous`, and `pollPublic`. - -Use `action: "poll"` for poll creation. Poll fields passed with `action: "send"` are rejected. - -Note: Discord has no “pick exactly N” mode; `pollMulti` maps to multi-select. -Teams polls are rendered as Adaptive Cards and require the gateway to stay online -to record votes in `~/.openclaw/msteams-polls.json`. +This page moved to [Message tool](/cli/message). See [Message tool](/cli/message) for poll documentation. diff --git a/docs/automation/standing-orders.md b/docs/automation/standing-orders.md index f1239e80367..3190f31ff98 100644 --- a/docs/automation/standing-orders.md +++ b/docs/automation/standing-orders.md @@ -247,8 +247,8 @@ Each program should have: ## Related -- [Automation Overview](/automation) — all automation mechanisms at a glance +- [Automation & Tasks](/automation) — all automation mechanisms at a glance - [Cron Jobs](/automation/cron-jobs) — schedule enforcement for standing orders - [Hooks](/automation/hooks) — event-driven scripts for agent lifecycle events -- [Webhooks](/automation/webhook) — inbound HTTP event triggers +- [Webhooks](/automation/cron-jobs#webhooks) — inbound HTTP event triggers - [Agent Workspace](/concepts/agent-workspace) — where standing orders live, including the full list of auto-injected bootstrap files (AGENTS.md, SOUL.md, etc.) diff --git a/docs/automation/tasks.md b/docs/automation/tasks.md index 1bc3375447b..83cca1cd67a 100644 --- a/docs/automation/tasks.md +++ b/docs/automation/tasks.md @@ -9,7 +9,7 @@ title: "Background Tasks" # Background Tasks -> **Cron vs Heartbeat vs Tasks?** See [Cron vs Heartbeat](/automation/cron-vs-heartbeat) for choosing the right scheduling mechanism. This page covers **tracking** background work, not scheduling it. +> **Looking for scheduling?** See [Automation & Tasks](/automation) for choosing the right mechanism. This page covers **tracking** background work, not scheduling it. Background tasks track work that runs **outside your main conversation session**: ACP runs, subagent spawns, isolated cron job executions, and CLI-initiated operations. @@ -252,10 +252,8 @@ A task's `runId` links to the agent run doing the work. Agent lifecycle events ( ## Related -- [Automation Overview](/automation) — all automation mechanisms at a glance +- [Automation & Tasks](/automation) — all automation mechanisms at a glance - [TaskFlow](/automation/taskflow) — flow orchestration above tasks -- [Cron Jobs](/automation/cron-jobs) — scheduling background work -- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) — choosing the right mechanism +- [Scheduled Tasks](/automation/cron-jobs) — scheduling background work - [Heartbeat](/gateway/heartbeat) — periodic main-session turns -- [CLI: tasks](/cli/index#tasks) — CLI reference for `openclaw tasks flow` - [CLI: Tasks](/cli/index#tasks) — CLI command reference diff --git a/docs/automation/troubleshooting.md b/docs/automation/troubleshooting.md index 12a00f9c422..f9e935d2323 100644 --- a/docs/automation/troubleshooting.md +++ b/docs/automation/troubleshooting.md @@ -1,122 +1,8 @@ --- -summary: "Troubleshoot cron and heartbeat scheduling and delivery" -read_when: - - Cron did not run - - Cron ran but no message was delivered - - Heartbeat seems silent or skipped +summary: "Redirect to /automation/cron-jobs" title: "Automation Troubleshooting" --- -# Automation troubleshooting +# Automation Troubleshooting -Use this page for scheduler and delivery issues (`cron` + `heartbeat`). - -## Command ladder - -```bash -openclaw status -openclaw gateway status -openclaw logs --follow -openclaw doctor -openclaw channels status --probe -``` - -Then run automation checks: - -```bash -openclaw cron status -openclaw cron list -openclaw system heartbeat last -``` - -## Cron not firing - -```bash -openclaw cron status -openclaw cron list -openclaw cron runs --id --limit 20 -openclaw logs --follow -``` - -Good output looks like: - -- `cron status` reports enabled and a future `nextWakeAtMs`. -- Job is enabled and has a valid schedule/timezone. -- `cron runs` shows `ok` or explicit skip reason. - -Common signatures: - -- `cron: scheduler disabled; jobs will not run automatically` → cron disabled in config/env. -- `cron: timer tick failed` → scheduler tick crashed; inspect surrounding stack/log context. -- `reason: not-due` in run output → manual run called without `--force` and job not due yet. - -## Cron fired but no delivery - -```bash -openclaw cron runs --id --limit 20 -openclaw cron list -openclaw channels status --probe -openclaw logs --follow -``` - -Good output looks like: - -- Run status is `ok`. -- Delivery mode/target are set for isolated jobs. -- Channel probe reports target channel connected. - -Common signatures: - -- Run succeeded but delivery mode is `none` → no external message is expected. -- Delivery target missing/invalid (`channel`/`to`) → run may succeed internally but skip outbound. -- Channel auth errors (`unauthorized`, `missing_scope`, `Forbidden`) → delivery blocked by channel credentials/permissions. - -## Heartbeat suppressed or skipped - -```bash -openclaw system heartbeat last -openclaw logs --follow -openclaw config get agents.defaults.heartbeat -openclaw channels status --probe -``` - -Good output looks like: - -- Heartbeat enabled with non-zero interval. -- Last heartbeat result is `ran` (or skip reason is understood). - -Common signatures: - -- `heartbeat skipped` with `reason=quiet-hours` → outside `activeHours`. -- `requests-in-flight` → main lane busy; heartbeat deferred. -- `empty-heartbeat-file` → interval heartbeat skipped because `HEARTBEAT.md` has no actionable content and no tagged cron event is queued. -- `alerts-disabled` → visibility settings suppress outbound heartbeat messages. - -## Timezone and activeHours gotchas - -```bash -openclaw config get agents.defaults.heartbeat.activeHours -openclaw config get agents.defaults.heartbeat.activeHours.timezone -openclaw config get agents.defaults.userTimezone || echo "agents.defaults.userTimezone not set" -openclaw cron list -openclaw logs --follow -``` - -Quick rules: - -- `Config path not found: agents.defaults.userTimezone` means the key is unset; heartbeat falls back to host timezone (or `activeHours.timezone` if set). -- Cron without `--tz` uses gateway host timezone. -- Heartbeat `activeHours` uses configured timezone resolution (`user`, `local`, or explicit IANA tz). -- Cron `at` schedules treat ISO timestamps without timezone as UTC unless you used CLI `--at "" --tz `. - -Common signatures: - -- Jobs run at the wrong wall-clock time after host timezone changes. -- Heartbeat always skipped during your daytime because `activeHours.timezone` is wrong. - -Related: - -- [/automation/cron-jobs](/automation/cron-jobs) -- [/gateway/heartbeat](/gateway/heartbeat) -- [/automation/cron-vs-heartbeat](/automation/cron-vs-heartbeat) -- [/concepts/timezone](/concepts/timezone) +This page moved to [Scheduled Tasks](/automation/cron-jobs#troubleshooting). See [Scheduled Tasks](/automation/cron-jobs#troubleshooting) for troubleshooting documentation. diff --git a/docs/automation/webhook.md b/docs/automation/webhook.md index 4b829886639..7f9e16fe31b 100644 --- a/docs/automation/webhook.md +++ b/docs/automation/webhook.md @@ -1,217 +1,8 @@ --- -summary: "Webhook ingress for wake and isolated agent runs" -read_when: - - Adding or changing webhook endpoints - - Wiring external systems into OpenClaw +summary: "Redirect to /automation/cron-jobs" title: "Webhooks" --- # Webhooks -Gateway can expose a small HTTP webhook endpoint for external triggers. - -## Enable - -```json5 -{ - hooks: { - enabled: true, - token: "shared-secret", - path: "/hooks", - // Optional: restrict explicit `agentId` routing to this allowlist. - // Omit or include "*" to allow any agent. - // Set [] to deny all explicit `agentId` routing. - allowedAgentIds: ["hooks", "main"], - }, -} -``` - -Notes: - -- `hooks.token` is required when `hooks.enabled=true`. -- `hooks.path` defaults to `/hooks`. - -## Auth - -Every request must include the hook token. Prefer headers: - -- `Authorization: Bearer ` (recommended) -- `x-openclaw-token: ` -- Query-string tokens are rejected (`?token=...` returns `400`). -- Treat `hooks.token` holders as full-trust callers for the hook ingress surface on that gateway. Hook payload content is still untrusted, but this is not a separate non-owner auth boundary. - -## Endpoints - -### `POST /hooks/wake` - -Payload: - -```json -{ "text": "System line", "mode": "now" } -``` - -- `text` **required** (string): The description of the event (e.g., "New email received"). -- `mode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check. - -Effect: - -- Enqueues a system event for the **main** session -- If `mode=now`, triggers an immediate heartbeat - -### `POST /hooks/agent` - -Payload: - -```json -{ - "message": "Run this", - "name": "Email", - "agentId": "hooks", - "sessionKey": "hook:email:msg-123", - "wakeMode": "now", - "deliver": true, - "channel": "last", - "to": "+15551234567", - "model": "openai/gpt-5.2-mini", - "thinking": "low", - "timeoutSeconds": 120 -} -``` - -- `message` **required** (string): The prompt or message for the agent to process. -- `name` optional (string): Human-readable name for the hook (e.g., "GitHub"), used as a prefix in session summaries. -- `agentId` optional (string): Route this hook to a specific agent. Unknown IDs fall back to the default agent. When set, the hook runs using the resolved agent's workspace and configuration. -- `sessionKey` optional (string): The key used to identify the agent's session. By default this field is rejected unless `hooks.allowRequestSessionKey=true`. -- `wakeMode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check. -- `deliver` optional (boolean): If `true`, the agent's response will be sent to the messaging channel. Defaults to `true`. Responses that are only heartbeat acknowledgments are automatically skipped. -- `channel` optional (string): The messaging channel for delivery. Use `last` or any configured channel or plugin id, for example `discord`, `matrix`, `telegram`, or `whatsapp`. Defaults to `last`. -- `to` optional (string): The recipient identifier for the channel (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack/Mattermost (plugin), conversation ID for Microsoft Teams). Defaults to the last recipient in the main session. -- `model` optional (string): Model override (e.g., `anthropic/claude-sonnet-4-6` or an alias). Must be in the allowed model list if restricted. -- `thinking` optional (string): Thinking level override (e.g., `low`, `medium`, `high`). -- `timeoutSeconds` optional (number): Maximum duration for the agent run in seconds. - -Effect: - -- Runs an **isolated** agent turn (own session key) -- Always posts a summary into the **main** session -- If `wakeMode=now`, triggers an immediate heartbeat - -## Session key policy (breaking change) - -`/hooks/agent` payload `sessionKey` overrides are disabled by default. - -- Recommended: set a fixed `hooks.defaultSessionKey` and keep request overrides off. -- Optional: allow request overrides only when needed, and restrict prefixes. - -Recommended config: - -```json5 -{ - hooks: { - enabled: true, - token: "${OPENCLAW_HOOKS_TOKEN}", - defaultSessionKey: "hook:ingress", - allowRequestSessionKey: false, - allowedSessionKeyPrefixes: ["hook:"], - }, -} -``` - -Compatibility config (legacy behavior): - -```json5 -{ - hooks: { - enabled: true, - token: "${OPENCLAW_HOOKS_TOKEN}", - allowRequestSessionKey: true, - allowedSessionKeyPrefixes: ["hook:"], // strongly recommended - }, -} -``` - -### `POST /hooks/` (mapped) - -Custom hook names are resolved via `hooks.mappings` (see configuration). A mapping can -turn arbitrary payloads into `wake` or `agent` actions, with optional templates or -code transforms. - -Mapping options (summary): - -- `hooks.presets: ["gmail"]` enables the built-in Gmail mapping. -- `hooks.mappings` lets you define `match`, `action`, and templates in config. -- `hooks.transformsDir` + `transform.module` loads a JS/TS module for custom logic. - - `hooks.transformsDir` (if set) must stay within the transforms root under your OpenClaw config directory (typically `~/.openclaw/hooks/transforms`). - - `transform.module` must resolve within the effective transforms directory (traversal/escape paths are rejected). -- Use `match.source` to keep a generic ingest endpoint (payload-driven routing). -- TS transforms require a TS loader (e.g. `bun` or `tsx`) or precompiled `.js` at runtime. -- Set `deliver: true` + `channel`/`to` on mappings to route replies to a chat surface - (`channel` defaults to `last` and falls back to WhatsApp). -- `agentId` routes the hook to a specific agent; unknown IDs fall back to the default agent. -- `hooks.allowedAgentIds` restricts explicit `agentId` routing. Omit it (or include `*`) to allow any agent. Set `[]` to deny explicit `agentId` routing. -- `hooks.defaultSessionKey` sets the default session for hook agent runs when no explicit key is provided. -- `hooks.allowRequestSessionKey` controls whether `/hooks/agent` payloads may set `sessionKey` (default: `false`). -- `hooks.allowedSessionKeyPrefixes` optionally restricts explicit `sessionKey` values from request payloads and mappings. -- `allowUnsafeExternalContent: true` disables the external content safety wrapper for that hook - (dangerous; only for trusted internal sources). -- `openclaw webhooks gmail setup` writes `hooks.gmail` config for `openclaw webhooks gmail run`. - See [Gmail Pub/Sub](/automation/gmail-pubsub) for the full Gmail watch flow. - -## Responses - -- `200` for `/hooks/wake` -- `200` for `/hooks/agent` (async run accepted) -- `401` on auth failure -- `429` after repeated auth failures from the same client (check `Retry-After`) -- `400` on invalid payload -- `413` on oversized payloads - -## Examples - -```bash -curl -X POST http://127.0.0.1:18789/hooks/wake \ - -H 'Authorization: Bearer SECRET' \ - -H 'Content-Type: application/json' \ - -d '{"text":"New email received","mode":"now"}' -``` - -```bash -curl -X POST http://127.0.0.1:18789/hooks/agent \ - -H 'x-openclaw-token: SECRET' \ - -H 'Content-Type: application/json' \ - -d '{"message":"Summarize inbox","name":"Email","wakeMode":"next-heartbeat"}' -``` - -### Use a different model - -Add `model` to the agent payload (or mapping) to override the model for that run: - -```bash -curl -X POST http://127.0.0.1:18789/hooks/agent \ - -H 'x-openclaw-token: SECRET' \ - -H 'Content-Type: application/json' \ - -d '{"message":"Summarize inbox","name":"Email","model":"openai/gpt-5.2-mini"}' -``` - -If you enforce `agents.defaults.models`, make sure the override model is included there. - -```bash -curl -X POST http://127.0.0.1:18789/hooks/gmail \ - -H 'Authorization: Bearer SECRET' \ - -H 'Content-Type: application/json' \ - -d '{"source":"gmail","messages":[{"from":"Ada","subject":"Hello","snippet":"Hi"}]}' -``` - -## Security - -- Keep hook endpoints behind loopback, tailnet, or trusted reverse proxy. -- Use a dedicated hook token; do not reuse gateway auth tokens. -- Prefer a dedicated hook agent with strict `tools.profile` and sandboxing so hook ingress has a narrower blast radius. -- Repeated auth failures are rate-limited per client address to slow brute-force attempts. -- If you use multi-agent routing, set `hooks.allowedAgentIds` to limit explicit `agentId` selection. -- Keep `hooks.allowRequestSessionKey=false` unless you require caller-selected sessions. -- If you enable request `sessionKey`, restrict `hooks.allowedSessionKeyPrefixes` (for example, `["hook:"]`). -- Avoid including sensitive raw payloads in webhook logs. -- Hook payloads are treated as untrusted and wrapped with safety boundaries by default. - If you must disable this for a specific hook, set `allowUnsafeExternalContent: true` - in that hook's mapping (dangerous). +This page moved to [Scheduled Tasks](/automation/cron-jobs#webhooks). See [Scheduled Tasks](/automation/cron-jobs#webhooks) for webhook documentation. diff --git a/docs/cli/index.md b/docs/cli/index.md index 20ee06d3821..2dbd127d4ac 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -566,7 +566,7 @@ Subcommands: ### `webhooks gmail` -Gmail Pub/Sub hook setup + runner. See [/automation/gmail-pubsub](/automation/gmail-pubsub). +Gmail Pub/Sub hook setup + runner. See [Gmail Pub/Sub](/automation/cron-jobs#gmail-pubsub-integration). Subcommands: diff --git a/docs/cli/webhooks.md b/docs/cli/webhooks.md index 6069865e96d..20eafecafe4 100644 --- a/docs/cli/webhooks.md +++ b/docs/cli/webhooks.md @@ -12,8 +12,8 @@ Webhook helpers and integrations (Gmail Pub/Sub, webhook helpers). Related: -- Webhooks: [Webhook](/automation/webhook) -- Gmail Pub/Sub: [Gmail Pub/Sub](/automation/gmail-pubsub) +- Webhooks: [Webhooks](/automation/cron-jobs#webhooks) +- Gmail Pub/Sub: [Gmail Pub/Sub](/automation/cron-jobs#gmail-pubsub-integration) ## Gmail @@ -22,4 +22,4 @@ openclaw webhooks gmail setup --account you@example.com openclaw webhooks gmail run ``` -See [Gmail Pub/Sub documentation](/automation/gmail-pubsub) for details. +See [Gmail Pub/Sub documentation](/automation/cron-jobs#gmail-pubsub-integration) for details. diff --git a/docs/docs.json b/docs/docs.json index 1461e10c0a0..662dc2b4f40 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -334,7 +334,7 @@ }, { "source": "/auth-monitoring", - "destination": "/automation/auth-monitoring" + "destination": "/gateway/authentication" }, { "source": "/camera", @@ -382,7 +382,7 @@ }, { "source": "/cron-vs-heartbeat", - "destination": "/automation/cron-vs-heartbeat" + "destination": "/automation" }, { "source": "/dashboard", @@ -438,7 +438,7 @@ }, { "source": "/gmail-pubsub", - "destination": "/automation/gmail-pubsub" + "destination": "/automation/cron-jobs" }, { "source": "/grammy", @@ -618,7 +618,7 @@ }, { "source": "/poll", - "destination": "/automation/poll" + "destination": "/cli/message" }, { "source": "/presence", @@ -778,7 +778,7 @@ }, { "source": "/webhook", - "destination": "/automation/webhook" + "destination": "/automation/cron-jobs" }, { "source": "/whatsapp", @@ -883,6 +883,30 @@ { "source": "/automation/clawflow", "destination": "/automation/taskflow" + }, + { + "source": "/automation/poll", + "destination": "/cli/message" + }, + { + "source": "/automation/auth-monitoring", + "destination": "/gateway/authentication" + }, + { + "source": "/automation/troubleshooting", + "destination": "/automation/cron-jobs" + }, + { + "source": "/automation/cron-vs-heartbeat", + "destination": "/automation" + }, + { + "source": "/automation/webhook", + "destination": "/automation/cron-jobs" + }, + { + "source": "/automation/gmail-pubsub", + "destination": "/automation/cron-jobs" } ], "navigation": { @@ -1118,20 +1142,14 @@ ] }, { - "group": "Automation", + "group": "Automation & Tasks", "pages": [ "automation/index", - "automation/hooks", - "automation/standing-orders", - "automation/cron-jobs", - "automation/cron-vs-heartbeat", "automation/tasks", + "automation/cron-jobs", "automation/taskflow", - "automation/troubleshooting", - "automation/webhook", - "automation/gmail-pubsub", - "automation/poll", - "automation/auth-monitoring" + "automation/hooks", + "automation/standing-orders" ] }, { @@ -1793,17 +1811,8 @@ ] }, { - "group": "自动化", - "pages": [ - "zh-CN/automation/hooks", - "zh-CN/automation/cron-jobs", - "zh-CN/automation/cron-vs-heartbeat", - "zh-CN/automation/troubleshooting", - "zh-CN/automation/webhook", - "zh-CN/automation/gmail-pubsub", - "zh-CN/automation/poll", - "zh-CN/automation/auth-monitoring" - ] + "group": "自动化与任务", + "pages": ["zh-CN/automation/hooks", "zh-CN/automation/cron-jobs"] }, { "group": "媒体与设备", diff --git a/docs/gateway/authentication.md b/docs/gateway/authentication.md index 947f36cd2a8..14bda3caea5 100644 --- a/docs/gateway/authentication.md +++ b/docs/gateway/authentication.md @@ -114,7 +114,7 @@ openclaw models status --check ``` Optional ops scripts (systemd/Termux) are documented here: -[/automation/auth-monitoring](/automation/auth-monitoring) +[Auth monitoring scripts](/help/scripts#auth-monitoring-scripts) > `claude setup-token` requires an interactive TTY. diff --git a/docs/gateway/heartbeat.md b/docs/gateway/heartbeat.md index 9e792f62317..4a46f426d48 100644 --- a/docs/gateway/heartbeat.md +++ b/docs/gateway/heartbeat.md @@ -8,7 +8,7 @@ title: "Heartbeat" # Heartbeat (Gateway) -> **Heartbeat vs Cron?** See [Cron vs Heartbeat](/automation/cron-vs-heartbeat) for guidance on when to use each. +> **Heartbeat vs Cron?** See [Automation & Tasks](/automation) for guidance on when to use each. Heartbeat runs **periodic agent turns** in the main session so the model can surface anything that needs attention without spamming you. @@ -16,7 +16,7 @@ surface anything that needs attention without spamming you. Heartbeat is a scheduled main-session turn — it does **not** create [background task](/automation/tasks) records. Task records are for detached work (ACP runs, subagents, isolated cron jobs). -Troubleshooting: [/automation/troubleshooting](/automation/troubleshooting) +Troubleshooting: [Scheduled Tasks](/automation/cron-jobs#troubleshooting) ## Quick start (beginner) @@ -400,8 +400,7 @@ Heartbeats run full agent turns. Shorter intervals burn more tokens. To reduce c ## Related -- [Automation Overview](/automation) — all automation mechanisms at a glance -- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) — when to use each +- [Automation & Tasks](/automation) — all automation mechanisms at a glance - [Background Tasks](/automation/tasks) — how detached work is tracked - [Timezone](/concepts/timezone) — how timezone affects heartbeat scheduling -- [Troubleshooting](/automation/troubleshooting) — debugging automation issues +- [Troubleshooting](/automation/cron-jobs#troubleshooting) — debugging automation issues diff --git a/docs/gateway/troubleshooting.md b/docs/gateway/troubleshooting.md index 2da1a25d507..98aa4768876 100644 --- a/docs/gateway/troubleshooting.md +++ b/docs/gateway/troubleshooting.md @@ -239,7 +239,7 @@ Common signatures: Related: -- [/automation/troubleshooting](/automation/troubleshooting) +- [/automation/cron-jobs#troubleshooting](/automation/cron-jobs#troubleshooting) - [/automation/cron-jobs](/automation/cron-jobs) - [/gateway/heartbeat](/gateway/heartbeat) diff --git a/docs/help/faq.md b/docs/help/faq.md index d16e96e8a7f..5cf6e13b924 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -1016,7 +1016,7 @@ for usage/billing and raise limits as needed. openclaw cron runs --id --limit 50 ``` - Docs: [Cron jobs](/automation/cron-jobs), [Cron vs Heartbeat](/automation/cron-vs-heartbeat). + Docs: [Cron jobs](/automation/cron-jobs), [Automation & Tasks](/automation). @@ -1041,7 +1041,7 @@ for usage/billing and raise limits as needed. - **Heartbeat** for "main session" periodic checks. - **Isolated jobs** for autonomous agents that post summaries or deliver to chats. - Docs: [Cron jobs](/automation/cron-jobs), [Cron vs Heartbeat](/automation/cron-vs-heartbeat), + Docs: [Cron jobs](/automation/cron-jobs), [Automation & Tasks](/automation), [Heartbeat](/gateway/heartbeat). diff --git a/docs/help/scripts.md b/docs/help/scripts.md index 56d74df8884..4f41954f727 100644 --- a/docs/help/scripts.md +++ b/docs/help/scripts.md @@ -19,8 +19,7 @@ Use these when a task is clearly tied to a script; otherwise prefer the CLI. ## Auth monitoring scripts -Auth monitoring scripts are documented here: -[/automation/auth-monitoring](/automation/auth-monitoring) +Auth monitoring is covered in [Authentication](/gateway/authentication). The scripts under `scripts/` are optional extras for systemd/Termux phone workflows. ## When adding scripts diff --git a/docs/help/troubleshooting.md b/docs/help/troubleshooting.md index eec340839b1..58865b27b8b 100644 --- a/docs/help/troubleshooting.md +++ b/docs/help/troubleshooting.md @@ -231,7 +231,7 @@ flowchart TD Deep pages: - [/gateway/troubleshooting#cron-and-heartbeat-delivery](/gateway/troubleshooting#cron-and-heartbeat-delivery) - - [/automation/troubleshooting](/automation/troubleshooting) + - [/automation/cron-jobs#troubleshooting](/automation/cron-jobs#troubleshooting) - [/gateway/heartbeat](/gateway/heartbeat) @@ -350,4 +350,4 @@ flowchart TD - [Gateway Troubleshooting](/gateway/troubleshooting) — gateway-specific issues - [Doctor](/gateway/doctor) — automated health checks and repairs - [Channel Troubleshooting](/channels/troubleshooting) — channel connectivity issues -- [Automation Troubleshooting](/automation/troubleshooting) — cron and heartbeat issues +- [Automation Troubleshooting](/automation/cron-jobs#troubleshooting) — cron and heartbeat issues diff --git a/docs/start/docs-directory.md b/docs/start/docs-directory.md index cbd9524f369..1dcb94ca48b 100644 --- a/docs/start/docs-directory.md +++ b/docs/start/docs-directory.md @@ -60,7 +60,7 @@ For a complete map of the docs, see [Docs hubs](/start/hubs). - [Sessions](/concepts/session) - [Cron jobs](/automation/cron-jobs) -- [Webhooks](/automation/webhook) -- [Gmail hooks (Pub/Sub)](/automation/gmail-pubsub) +- [Webhooks](/automation/cron-jobs#webhooks) +- [Gmail hooks (Pub/Sub)](/automation/cron-jobs#gmail-pubsub-integration) - [Security](/gateway/security) - [Troubleshooting](/gateway/troubleshooting) diff --git a/docs/start/hubs.md b/docs/start/hubs.md index 754957a96d6..9ff1784f539 100644 --- a/docs/start/hubs.md +++ b/docs/start/hubs.md @@ -79,8 +79,8 @@ Use these hubs to discover every page, including deep dives and reference docs t - [iMessage (legacy)](/channels/imessage) - [Location parsing](/channels/location) - [WebChat](/web/webchat) -- [Webhooks](/automation/webhook) -- [Gmail Pub/Sub](/automation/gmail-pubsub) +- [Webhooks](/automation/cron-jobs#webhooks) +- [Gmail Pub/Sub](/automation/cron-jobs#gmail-pubsub-integration) ## Gateway + operations @@ -111,7 +111,7 @@ Use these hubs to discover every page, including deep dives and reference docs t - [PDF tool](/tools/pdf) - [Elevated mode](/tools/elevated) - [Cron jobs](/automation/cron-jobs) -- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) +- [Automation & Tasks](/automation) - [Thinking + verbose](/tools/thinking) - [Models](/concepts/models) - [Sub-agents](/tools/subagents) @@ -119,7 +119,7 @@ Use these hubs to discover every page, including deep dives and reference docs t - [Terminal UI](/web/tui) - [Browser control](/tools/browser) - [Browser (Linux troubleshooting)](/tools/browser-linux-troubleshooting) -- [Polls](/automation/poll) +- [Polls](/cli/message) ## Nodes, media, voice diff --git a/docs/tools/lobster.md b/docs/tools/lobster.md index f71f749b393..01bec8e50a1 100644 --- a/docs/tools/lobster.md +++ b/docs/tools/lobster.md @@ -343,6 +343,6 @@ One public example: a “second brain” CLI + Lobster pipelines that manage thr ## Related -- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) — scheduling Lobster workflows +- [Automation & Tasks](/automation) — scheduling Lobster workflows - [Automation Overview](/automation) — all automation mechanisms - [Tools Overview](/tools) — all available agent tools