Reference docs fixes: - cli-commands.md: remove non-existent --provider alibaba, add hermes profile/completion/plugins/mcp to top-level table, add --profile/-p global flag, add --source chat option - slash-commands.md: add /yolo and /commands, fix /q alias conflict (resolves to /queue not /quit), add missing aliases (/bg, /set-home, /reload_mcp, /gateway) - toolsets-reference.md: fix hermes-api-server (not same as hermes-cli, omits clarify/send_message/text_to_speech) - profile-commands.md: fix show name required not optional, --clone-from not --from, add --remove/--name to alias, fix alias path, fix export/ import arg types, remove non-existent fish completion - tools-reference.md: add EXA_API_KEY to web tools requires_env - mcp-config-reference.md: add auth key for OAuth, tool name sanitization - environment-variables.md: add EXA_API_KEY, update provider values - plugins.md: remove non-existent ctx.register_command(), add ctx.inject_message() Feature docs additions: - security.md: add /yolo mode, approval modes (manual/smart/off), configurable timeout, expanded dangerous patterns table - cron.md: add wrap_response config, [SILENT] suppression - mcp.md: add dynamic tool discovery, MCP sampling support - cli.md: add Ctrl+Z suspend, busy_input_mode, tool_preview_length - docker.md: add skills/credential file mounting Messaging platform docs: - telegram.md: add webhook mode, DoH fallback IPs - slack.md: add multi-workspace OAuth support - discord.md: add DISCORD_IGNORE_NO_MENTION - matrix.md: add MSC3245 native voice messages - feishu.md: expand from 129 to 365 lines (encrypt key, verification token, group policy, card actions, media, rate limiting, markdown, troubleshooting) - wecom.md: expand from 86 to 264 lines (per-group allowlists, media, AES decryption, stream replies, reconnection, troubleshooting) Configuration docs: - quickstart.md: add DeepSeek, Copilot, Copilot ACP providers - configuration.md: add DeepSeek provider, Exa web backend, terminal env_passthrough/images, browser.command_timeout, compression params, discord config, security/tirith config, timezone, auxiliary models 21 files changed, ~1000 lines added
320 lines
9.4 KiB
Markdown
320 lines
9.4 KiB
Markdown
---
|
|
sidebar_position: 5
|
|
title: "Scheduled Tasks (Cron)"
|
|
description: "Schedule automated tasks with natural language, manage them with one cron tool, and attach one or more skills"
|
|
---
|
|
|
|
# Scheduled Tasks (Cron)
|
|
|
|
Schedule tasks to run automatically with natural language or cron expressions. Hermes exposes cron management through a single `cronjob` tool with action-style operations instead of separate schedule/list/remove tools.
|
|
|
|
## What cron can do now
|
|
|
|
Cron jobs can:
|
|
|
|
- schedule one-shot or recurring tasks
|
|
- pause, resume, edit, trigger, and remove jobs
|
|
- attach zero, one, or multiple skills to a job
|
|
- deliver results back to the origin chat, local files, or configured platform targets
|
|
- run in fresh agent sessions with the normal static tool list
|
|
|
|
:::warning
|
|
Cron-run sessions cannot recursively create more cron jobs. Hermes disables cron management tools inside cron executions to prevent runaway scheduling loops.
|
|
:::
|
|
|
|
## Creating scheduled tasks
|
|
|
|
### In chat with `/cron`
|
|
|
|
```bash
|
|
/cron add 30m "Remind me to check the build"
|
|
/cron add "every 2h" "Check server status"
|
|
/cron add "every 1h" "Summarize new feed items" --skill blogwatcher
|
|
/cron add "every 1h" "Use both skills and combine the result" --skill blogwatcher --skill find-nearby
|
|
```
|
|
|
|
### From the standalone CLI
|
|
|
|
```bash
|
|
hermes cron create "every 2h" "Check server status"
|
|
hermes cron create "every 1h" "Summarize new feed items" --skill blogwatcher
|
|
hermes cron create "every 1h" "Use both skills and combine the result" \
|
|
--skill blogwatcher \
|
|
--skill find-nearby \
|
|
--name "Skill combo"
|
|
```
|
|
|
|
### Through natural conversation
|
|
|
|
Ask Hermes normally:
|
|
|
|
```text
|
|
Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.
|
|
```
|
|
|
|
Hermes will use the unified `cronjob` tool internally.
|
|
|
|
## Skill-backed cron jobs
|
|
|
|
A cron job can load one or more skills before it runs the prompt.
|
|
|
|
### Single skill
|
|
|
|
```python
|
|
cronjob(
|
|
action="create",
|
|
skill="blogwatcher",
|
|
prompt="Check the configured feeds and summarize anything new.",
|
|
schedule="0 9 * * *",
|
|
name="Morning feeds",
|
|
)
|
|
```
|
|
|
|
### Multiple skills
|
|
|
|
Skills are loaded in order. The prompt becomes the task instruction layered on top of those skills.
|
|
|
|
```python
|
|
cronjob(
|
|
action="create",
|
|
skills=["blogwatcher", "find-nearby"],
|
|
prompt="Look for new local events and interesting nearby places, then combine them into one short brief.",
|
|
schedule="every 6h",
|
|
name="Local brief",
|
|
)
|
|
```
|
|
|
|
This is useful when you want a scheduled agent to inherit reusable workflows without stuffing the full skill text into the cron prompt itself.
|
|
|
|
## Editing jobs
|
|
|
|
You do not need to delete and recreate jobs just to change them.
|
|
|
|
### Chat
|
|
|
|
```bash
|
|
/cron edit <job_id> --schedule "every 4h"
|
|
/cron edit <job_id> --prompt "Use the revised task"
|
|
/cron edit <job_id> --skill blogwatcher --skill find-nearby
|
|
/cron edit <job_id> --remove-skill blogwatcher
|
|
/cron edit <job_id> --clear-skills
|
|
```
|
|
|
|
### Standalone CLI
|
|
|
|
```bash
|
|
hermes cron edit <job_id> --schedule "every 4h"
|
|
hermes cron edit <job_id> --prompt "Use the revised task"
|
|
hermes cron edit <job_id> --skill blogwatcher --skill find-nearby
|
|
hermes cron edit <job_id> --add-skill find-nearby
|
|
hermes cron edit <job_id> --remove-skill blogwatcher
|
|
hermes cron edit <job_id> --clear-skills
|
|
```
|
|
|
|
Notes:
|
|
|
|
- repeated `--skill` replaces the job's attached skill list
|
|
- `--add-skill` appends to the existing list without replacing it
|
|
- `--remove-skill` removes specific attached skills
|
|
- `--clear-skills` removes all attached skills
|
|
|
|
## Lifecycle actions
|
|
|
|
Cron jobs now have a fuller lifecycle than just create/remove.
|
|
|
|
### Chat
|
|
|
|
```bash
|
|
/cron list
|
|
/cron pause <job_id>
|
|
/cron resume <job_id>
|
|
/cron run <job_id>
|
|
/cron remove <job_id>
|
|
```
|
|
|
|
### Standalone CLI
|
|
|
|
```bash
|
|
hermes cron list
|
|
hermes cron pause <job_id>
|
|
hermes cron resume <job_id>
|
|
hermes cron run <job_id>
|
|
hermes cron remove <job_id>
|
|
hermes cron status
|
|
hermes cron tick
|
|
```
|
|
|
|
What they do:
|
|
|
|
- `pause` — keep the job but stop scheduling it
|
|
- `resume` — re-enable the job and compute the next future run
|
|
- `run` — trigger the job on the next scheduler tick
|
|
- `remove` — delete it entirely
|
|
|
|
## How it works
|
|
|
|
**Cron execution is handled by the gateway daemon.** The gateway ticks the scheduler every 60 seconds, running any due jobs in isolated agent sessions.
|
|
|
|
```bash
|
|
hermes gateway install # Install as a user service
|
|
sudo hermes gateway install --system # Linux: boot-time system service for servers
|
|
hermes gateway # Or run in foreground
|
|
|
|
hermes cron list
|
|
hermes cron status
|
|
```
|
|
|
|
### Gateway scheduler behavior
|
|
|
|
On each tick Hermes:
|
|
|
|
1. loads jobs from `~/.hermes/cron/jobs.json`
|
|
2. checks `next_run_at` against the current time
|
|
3. starts a fresh `AIAgent` session for each due job
|
|
4. optionally injects one or more attached skills into that fresh session
|
|
5. runs the prompt to completion
|
|
6. delivers the final response
|
|
7. updates run metadata and the next scheduled time
|
|
|
|
A file lock at `~/.hermes/cron/.tick.lock` prevents overlapping scheduler ticks from double-running the same job batch.
|
|
|
|
## Delivery options
|
|
|
|
When scheduling jobs, you specify where the output goes:
|
|
|
|
| Option | Description | Example |
|
|
|--------|-------------|---------|
|
|
| `"origin"` | Back to where the job was created | Default on messaging platforms |
|
|
| `"local"` | Save to local files only (`~/.hermes/cron/output/`) | Default on CLI |
|
|
| `"telegram"` | Telegram home channel | Uses `TELEGRAM_HOME_CHANNEL` |
|
|
| `"discord"` | Discord home channel | Uses `DISCORD_HOME_CHANNEL` |
|
|
| `"telegram:123456"` | Specific Telegram chat by ID | Direct delivery |
|
|
| `"discord:987654"` | Specific Discord channel by ID | Direct delivery |
|
|
|
|
The agent's final response is automatically delivered. You do not need to call `send_message` in the cron prompt.
|
|
|
|
### Response wrapping
|
|
|
|
By default, delivered cron output is wrapped with a header and footer so the recipient knows it came from a scheduled task:
|
|
|
|
```
|
|
Cronjob Response: Morning feeds
|
|
-------------
|
|
|
|
<agent output here>
|
|
|
|
Note: The agent cannot see this message, and therefore cannot respond to it.
|
|
```
|
|
|
|
To deliver the raw agent output without the wrapper, set `cron.wrap_response` to `false`:
|
|
|
|
```yaml
|
|
# ~/.hermes/config.yaml
|
|
cron:
|
|
wrap_response: false
|
|
```
|
|
|
|
### Silent suppression
|
|
|
|
If the agent's final response starts with `[SILENT]`, delivery is suppressed entirely. The output is still saved locally for audit (in `~/.hermes/cron/output/`), but no message is sent to the delivery target.
|
|
|
|
This is useful for monitoring jobs that should only report when something is wrong:
|
|
|
|
```text
|
|
Check if nginx is running. If everything is healthy, respond with only [SILENT].
|
|
Otherwise, report the issue.
|
|
```
|
|
|
|
Failed jobs always deliver regardless of the `[SILENT]` marker — only successful runs can be silenced.
|
|
|
|
## Schedule formats
|
|
|
|
The agent's final response is automatically delivered — you do **not** need to include `send_message` in the cron prompt for that same destination. If a cron run calls `send_message` to the exact target the scheduler will already deliver to, Hermes skips that duplicate send and tells the model to put the user-facing content in the final response instead. Use `send_message` only for additional or different targets.
|
|
|
|
### Relative delays (one-shot)
|
|
|
|
```text
|
|
30m → Run once in 30 minutes
|
|
2h → Run once in 2 hours
|
|
1d → Run once in 1 day
|
|
```
|
|
|
|
### Intervals (recurring)
|
|
|
|
```text
|
|
every 30m → Every 30 minutes
|
|
every 2h → Every 2 hours
|
|
every 1d → Every day
|
|
```
|
|
|
|
### Cron expressions
|
|
|
|
```text
|
|
0 9 * * * → Daily at 9:00 AM
|
|
0 9 * * 1-5 → Weekdays at 9:00 AM
|
|
0 */6 * * * → Every 6 hours
|
|
30 8 1 * * → First of every month at 8:30 AM
|
|
0 0 * * 0 → Every Sunday at midnight
|
|
```
|
|
|
|
### ISO timestamps
|
|
|
|
```text
|
|
2026-03-15T09:00:00 → One-time at March 15, 2026 9:00 AM
|
|
```
|
|
|
|
## Repeat behavior
|
|
|
|
| Schedule type | Default repeat | Behavior |
|
|
|--------------|----------------|----------|
|
|
| One-shot (`30m`, timestamp) | 1 | Runs once |
|
|
| Interval (`every 2h`) | forever | Runs until removed |
|
|
| Cron expression | forever | Runs until removed |
|
|
|
|
You can override it:
|
|
|
|
```python
|
|
cronjob(
|
|
action="create",
|
|
prompt="...",
|
|
schedule="every 2h",
|
|
repeat=5,
|
|
)
|
|
```
|
|
|
|
## Managing jobs programmatically
|
|
|
|
The agent-facing API is one tool:
|
|
|
|
```python
|
|
cronjob(action="create", ...)
|
|
cronjob(action="list")
|
|
cronjob(action="update", job_id="...")
|
|
cronjob(action="pause", job_id="...")
|
|
cronjob(action="resume", job_id="...")
|
|
cronjob(action="run", job_id="...")
|
|
cronjob(action="remove", job_id="...")
|
|
```
|
|
|
|
For `update`, pass `skills=[]` to remove all attached skills.
|
|
|
|
## Job storage
|
|
|
|
Jobs are stored in `~/.hermes/cron/jobs.json`. Output from job runs is saved to `~/.hermes/cron/output/{job_id}/{timestamp}.md`.
|
|
|
|
The storage uses atomic file writes so interrupted writes do not leave a partially written job file behind.
|
|
|
|
## Self-contained prompts still matter
|
|
|
|
:::warning Important
|
|
Cron jobs run in a completely fresh agent session. The prompt must contain everything the agent needs that is not already provided by attached skills.
|
|
:::
|
|
|
|
**BAD:** `"Check on that server issue"`
|
|
|
|
**GOOD:** `"SSH into server 192.168.1.100 as user 'deploy', check if nginx is running with 'systemctl status nginx', and verify https://example.com returns HTTP 200."`
|
|
|
|
## Security
|
|
|
|
Scheduled task prompts are scanned for prompt-injection and credential-exfiltration patterns at creation and update time. Prompts containing invisible Unicode tricks, SSH backdoor attempts, or obvious secret-exfiltration payloads are blocked.
|