mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-02 00:41:43 +08:00
Broad drift audit against origin/main (b52b63396).
Reference pages (most user-visible drift):
- slash-commands: add /busy, /curator, /footer, /indicator, /redraw, /steer
that were missing; drop non-existent /terminal-setup; fix /q footnote
(resolves to /queue, not /quit); extend CLI-only list with all 24
CLI-only commands in the registry
- cli-commands: add dedicated sections for hermes curator / fallback /
hooks (new subcommands not previously documented); remove stale
hermes honcho standalone section (the plugin registers dynamically
via hermes memory); list curator/fallback/hooks in top-level table;
fix completion to include fish
- toolsets-reference: document the real 52-toolset count; split browser
vs browser-cdp; add discord / discord_admin / spotify / yuanbao;
correct hermes-cli tool count from 36 to 38; fix misleading claim
that hermes-homeassistant adds tools (it's identical to hermes-cli)
- tools-reference: bump tool count 55 -> 68; add 7 Spotify, 5 Yuanbao,
2 Discord toolsets; move browser_cdp/browser_dialog to their own
browser-cdp toolset section
- environment-variables: add 40+ user-facing HERMES_* vars that were
undocumented (--yolo, --accept-hooks, --ignore-*, inference model
override, agent/stream/checkpoint timeouts, OAuth trace, per-platform
batch tuning for Telegram/Discord/Matrix/Feishu/WeCom, cron knobs,
gateway restart/connect timeouts); dedupe the Cron Scheduler section;
replace stale QQ_SANDBOX with QQ_PORTAL_HOST
User-guide (top level):
- cli.md: compression preserves last 20 turns, not 4 (protect_last_n: 20)
- configuration.md: display.platforms is the canonical per-platform
override key; tool_progress_overrides is deprecated and auto-migrated
- profiles.md: model.default is the config key, not model.model
- sessions.md: CLI/TUI session IDs use 6-char hex, gateway uses 8
- checkpoints-and-rollback.md: destructive-command list now matches
_DESTRUCTIVE_PATTERNS (adds rmdir, cp, install, dd)
- docker.md: the container runs as non-root hermes (UID 10000) via
gosu; fix install command (uv pip); add missing --insecure on the
dashboard compose example (required for non-loopback bind)
- security.md: systemctl danger pattern also matches 'restart'
- index.md: built-in tool count 47 -> 68
- integrations/index.md: 6 STT providers, 8 memory providers
- integrations/providers.md: drop fictional dashscope/qwen aliases
Features:
- overview.md: 9 image models (not 8), 9 TTS providers (not 5),
8 memory providers (Supermemory was missing)
- tool-gateway.md: 9 image models
- tools.md: extend common-toolsets list with search / messaging /
spotify / discord / debugging / safe
- fallback-providers.md: add 6 real providers from PROVIDER_REGISTRY
(lmstudio, kimi-coding-cn, stepfun, alibaba-coding-plan,
tencent-tokenhub, azure-foundry)
- plugins.md: Available Hooks table now includes on_session_finalize,
on_session_reset, subagent_stop
- built-in-plugins.md: add the 7 bundled plugins the page didn't
mention (spotify, google_meet, three image_gen providers, two
dashboard examples)
- web-dashboard.md: add --insecure and --tui flags
- cron.md: hermes cron create takes positional schedule/prompt, not
flags
Messaging:
- telegram.md: TELEGRAM_WEBHOOK_SECRET is now REQUIRED when
TELEGRAM_WEBHOOK_URL is set (gateway refuses to start without it
per GHSA-3vpc-7q5r-276h). Biggest user-visible drift in the batch.
- discord.md: HERMES_DISCORD_TEXT_BATCH_SPLIT_DELAY_SECONDS default
is 2.0, not 0.1
- dingtalk.md: document DINGTALK_REQUIRE_MENTION /
FREE_RESPONSE_CHATS / MENTION_PATTERNS / HOME_CHANNEL /
ALLOW_ALL_USERS that the adapter supports
- bluebubbles.md: drop fictional BLUEBUBBLES_SEND_READ_RECEIPTS env
var; the setting lives in platforms.bluebubbles.extra only
- qqbot.md: drop dead QQ_SANDBOX; add real QQ_PORTAL_HOST and
QQ_GROUP_ALLOWED_USERS
- wecom-callback.md: replace 'hermes gateway start' (service-only)
with 'hermes gateway' for first-time setup
Developer-guide:
- architecture.md: refresh tool/toolset counts (61/52), terminal
backend count (7), line counts for run_agent.py (~13.7k), cli.py
(~11.5k), main.py (~10.4k), setup.py (~3.5k), gateway/run.py
(~12.2k), mcp_tool.py (~3.1k); add yuanbao adapter, bump platform
adapter count 18 -> 20
- agent-loop.md: run_agent.py line count 10.7k -> 13.7k
- tools-runtime.md: add vercel_sandbox backend
- adding-tools.md: remove stale 'Discovery import added to
model_tools.py' checklist item (registry auto-discovery)
- adding-platform-adapters.md: mark send_typing / get_chat_info as
concrete base methods; only connect/disconnect/send are abstract
- acp-internals.md: ACP sessions now persist to SessionDB
(~/.hermes/state.db); acp.run_agent call uses
use_unstable_protocol=True
- cron-internals.md: gateway runs scheduler in a dedicated background
thread via _start_cron_ticker, not on a maintenance cycle; locking
is cross-process via fcntl.flock (Unix) / msvcrt.locking (Windows)
- gateway-internals.md: gateway/run.py ~12k lines
- provider-runtime.md: cron DOES support fallback (run_job reads
fallback_providers from config)
- session-storage.md: SCHEMA_VERSION = 11 (not 9); add migrations
10 and 11 (trigram FTS, inline-mode FTS5 re-index); add
api_call_count column to Sessions DDL; document messages_fts_trigram
and state_meta in the architecture tree
- context-compression-and-caching.md: remove the obsolete 'context
pressure warnings' section (warnings were removed for causing
models to give up early)
- context-engine-plugin.md: compress() signature now includes
focus_topic param
- extending-the-cli.md: _build_tui_layout_children signature now
includes model_picker_widget; add to default layout
Also fixed three pre-existing broken links/anchors the build warned
about (docker.md -> api-server.md, yuanbao.md -> cron-jobs.md and
tips#background-tasks, nix-setup.md -> #container-aware-cli).
Regenerated per-skill pages via website/scripts/generate-skill-docs.py
so catalog tables and sidebar are consistent with current SKILL.md
frontmatter.
docusaurus build: clean, no broken links or anchors.
448 lines
15 KiB
Markdown
448 lines
15 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 maps
|
||
```
|
||
|
||
### 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 maps \
|
||
--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", "maps"],
|
||
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.
|
||
|
||
## Running a job inside a project directory
|
||
|
||
Cron jobs default to running detached from any repo — no `AGENTS.md`, `CLAUDE.md`, or `.cursorrules` is loaded, and the terminal / file / code-exec tools run from whatever working directory the gateway started in. Pass `--workdir` (CLI) or `workdir=` (tool call) to change that:
|
||
|
||
```bash
|
||
# Standalone CLI (schedule and prompt are positional)
|
||
hermes cron create "every 1d at 09:00" \
|
||
"Audit open PRs, summarize CI health, and post to #eng" \
|
||
--workdir /home/me/projects/acme
|
||
```
|
||
|
||
```python
|
||
# From a chat, via the cronjob tool
|
||
cronjob(
|
||
action="create",
|
||
schedule="every 1d at 09:00",
|
||
workdir="/home/me/projects/acme",
|
||
prompt="Audit open PRs, summarize CI health, and post to #eng",
|
||
)
|
||
```
|
||
|
||
When `workdir` is set:
|
||
|
||
- `AGENTS.md`, `CLAUDE.md`, and `.cursorrules` from that directory are injected into the system prompt (same discovery order as the interactive CLI)
|
||
- `terminal`, `read_file`, `write_file`, `patch`, `search_files`, and `execute_code` all use that directory as their working directory (via `TERMINAL_CWD`)
|
||
- The path must be an absolute directory that exists — relative paths and missing directories are rejected at create / update time
|
||
- Pass `--workdir ""` (or `workdir=""` via the tool) on edit to clear it and restore the old behaviour
|
||
|
||
:::note Serialization
|
||
Jobs with a `workdir` run sequentially on the scheduler tick, not in the parallel pool. This is deliberate — `TERMINAL_CWD` is process-global, so two workdir jobs running at the same time would corrupt each other's cwd. Workdir-less jobs still run in parallel as before.
|
||
:::
|
||
|
||
## 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 maps
|
||
/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 maps
|
||
hermes cron edit <job_id> --add-skill maps
|
||
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` |
|
||
| `"telegram:123456"` | Specific Telegram chat by ID | Direct delivery |
|
||
| `"telegram:-100123:17585"` | Specific Telegram topic | `chat_id:thread_id` format |
|
||
| `"discord"` | Discord home channel | Uses `DISCORD_HOME_CHANNEL` |
|
||
| `"discord:#engineering"` | Specific Discord channel | By channel name |
|
||
| `"slack"` | Slack home channel | |
|
||
| `"whatsapp"` | WhatsApp home | |
|
||
| `"signal"` | Signal | |
|
||
| `"matrix"` | Matrix home room | |
|
||
| `"mattermost"` | Mattermost home channel | |
|
||
| `"email"` | Email | |
|
||
| `"sms"` | SMS via Twilio | |
|
||
| `"homeassistant"` | Home Assistant | |
|
||
| `"dingtalk"` | DingTalk | |
|
||
| `"feishu"` | Feishu/Lark | |
|
||
| `"wecom"` | WeCom | |
|
||
| `"weixin"` | Weixin (WeChat) | |
|
||
| `"bluebubbles"` | BlueBubbles (iMessage) | |
|
||
| `"qqbot"` | QQ Bot (Tencent QQ) | |
|
||
|
||
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.
|
||
|
||
## Script timeout
|
||
|
||
Pre-run scripts (attached via the `script` parameter) have a default timeout of 120 seconds. If your scripts need longer — for example, to include randomized delays that avoid bot-like timing patterns — you can increase this:
|
||
|
||
```yaml
|
||
# ~/.hermes/config.yaml
|
||
cron:
|
||
script_timeout_seconds: 300 # 5 minutes
|
||
```
|
||
|
||
Or set the `HERMES_CRON_SCRIPT_TIMEOUT` environment variable. The resolution order is: env var → config.yaml → 120s default.
|
||
|
||
## Provider recovery
|
||
|
||
Cron jobs inherit your configured fallback providers and credential pool rotation. If the primary API key is rate-limited or the provider returns an error, the cron agent can:
|
||
|
||
- **Fall back to an alternate provider** if you have `fallback_providers` (or the legacy `fallback_model`) configured in `config.yaml`
|
||
- **Rotate to the next credential** in your [credential pool](/docs/user-guide/configuration#credential-pool-strategies) for the same provider
|
||
|
||
This means cron jobs that run at high frequency or during peak hours are more resilient — a single rate-limited key won't fail the entire run.
|
||
|
||
## 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.
|
||
|
||
## Toolsets available to cron jobs
|
||
|
||
Cron runs each job in a fresh agent session with no chat platform attached. By default the cron agent gets **the toolset you configured for the `cron` platform in `hermes tools`** — not the CLI default, not everything under the sun.
|
||
|
||
```bash
|
||
hermes tools
|
||
# → pick the "cron" platform in the curses UI
|
||
# → toggle toolsets on/off just like you would for Telegram/Discord/etc.
|
||
```
|
||
|
||
Tighter per-job control is available via the `enabled_toolsets` field on `cronjob.create` (or on an existing job via `cronjob.update`):
|
||
|
||
```text
|
||
cronjob(action="create", name="weekly-news-summary",
|
||
schedule="every sunday 9am",
|
||
enabled_toolsets=["web", "file"], # just web + file, no terminal/browser/etc.
|
||
prompt="Summarize this week's AI news: ...")
|
||
```
|
||
|
||
When `enabled_toolsets` is set on a job it wins; otherwise the `hermes tools` cron-platform config wins; otherwise Hermes falls back to the built-in defaults. This matters for cost control: carrying `moa`, `browser`, `delegation` into every tiny "fetch news" job bloats the tool-schema prompt on every LLM call.
|
||
|
||
### Skipping the agent entirely: `wakeAgent`
|
||
|
||
If your cron job attaches a pre-check script (via `script=`), the script can decide at runtime whether Hermes should even invoke the agent. Emit a final stdout line of the form:
|
||
|
||
```text
|
||
{"wakeAgent": false}
|
||
```
|
||
|
||
…and cron skips the agent run entirely for this tick. Useful for frequent polls (every 1–5 min) that only need to wake the LLM when state actually changed — otherwise you pay for zero-content agent turns over and over.
|
||
|
||
```python
|
||
# pre-check script
|
||
import json, sys
|
||
latest = fetch_latest_issue_count()
|
||
prev = read_state("issue_count")
|
||
if latest == prev:
|
||
print(json.dumps({"wakeAgent": False})) # skip this tick
|
||
sys.exit(0)
|
||
write_state("issue_count", latest)
|
||
print(json.dumps({"wakeAgent": True, "context": {"new_issues": latest - prev}}))
|
||
```
|
||
|
||
When `wakeAgent` is omitted, the default is `true` (wake the agent as usual).
|
||
|
||
### Chaining jobs: `context_from`
|
||
|
||
A cron job can consume the most recent successful output of one or more other jobs by listing their names (or IDs) in `context_from`:
|
||
|
||
```text
|
||
cronjob(action="create", name="daily-digest",
|
||
schedule="every day 7am",
|
||
context_from=["ai-news-fetch", "github-prs-fetch"],
|
||
prompt="Write the daily digest using the outputs above.")
|
||
```
|
||
|
||
The referenced jobs' most recent completed outputs are injected above the prompt as context for this run. Each upstream entry must be a valid job ID or name (see `cronjob action="list"`). Note: chaining reads the *most recent completed* output — it does not wait for upstream jobs that are running in the same tick.
|
||
|
||
## Job storage
|
||
|
||
Jobs are stored in `~/.hermes/cron/jobs.json`. Output from job runs is saved to `~/.hermes/cron/output/{job_id}/{timestamp}.md`.
|
||
|
||
Jobs may store `model` and `provider` as `null`. When those fields are omitted, Hermes resolves them at execution time from the global configuration. They only appear in the job record when a per-job override is set.
|
||
|
||
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.
|