Ryvos includes two automation systems that run in daemon mode: cron for scheduled tasks and heartbeat for periodic health monitoring.
Cron Jobs
Cron jobs let you schedule recurring agent tasks using standard 5-field cron expressions. Each job runs the agent with a specified prompt at the scheduled time.
Configuration
[[cron.jobs]]
name = "daily-report"
schedule = "0 9 * * *" # Every day at 9:00 AM
prompt = "Generate a summary of yesterday's git commits and open PRs"
channel = "slack" # Optional: route output to a channel
[[cron.jobs]]
name = "hourly-health"
schedule = "0 * * * *" # Every hour
prompt = "Check if the staging server at staging.example.com is responding"
channel = "telegram"
[[cron.jobs]]
name = "weekly-cleanup"
schedule = "0 2 * * 0" # Sundays at 2:00 AM
prompt = "Review and clean up old log files in /var/log/app/"
[[cron.jobs]]
name = "deploy-check"
schedule = "*/15 * * * *" # Every 15 minutes
prompt = "Check if the latest deployment is healthy by calling /api/health"Cron Expression Format
┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sunday=0)
│ │ │ │ │
* * * * *
| Expression | Schedule |
|---|---|
0 9 * * * | Daily at 9:00 AM |
0 */2 * * * | Every 2 hours |
*/15 * * * * | Every 15 minutes |
0 9 * * 1-5 | Weekdays at 9:00 AM |
0 0 1 * * | First day of each month at midnight |
30 8 * * 1 | Every Monday at 8:30 AM |
Channel Routing
When a channel is specified, the cron job output is sent to that channel. Without a channel, the output is logged but not sent anywhere.
[[cron.jobs]]
name = "morning-brief"
schedule = "0 8 * * 1-5"
prompt = "Summarize today's calendar, pending PRs, and any alerts"
channel = "telegram" # Sends the summary to TelegramEvents
Cron jobs emit events on the EventBus:
| Event | When |
|---|---|
CronFired | A cron job has triggered (includes job name and schedule) |
CronJobComplete | The agent finished executing the cron job (includes result) |
Managing Cron Jobs
Config
Edit config.toml directly:
[[cron.jobs]]
name = "my-job"
schedule = "0 9 * * *"
prompt = "Do the thing"Tools
The agent can manage cron jobs via built-in tools:
You > Add a cron job that checks disk usage every 6 hours
Agent > [tool: cron_add] Adding cron job...
Added job "disk-check" with schedule "0 */6 * * *"
Available tools:
cron_list— List all jobs with next fire timescron_add— Add a new jobcron_remove— Remove a job
Heartbeat
The heartbeat system runs periodic mini-agent sessions to check on things you care about. Unlike cron (which runs specific prompts), the heartbeat reads HEARTBEAT.md for its instructions and makes intelligent decisions about what to report.
Configuration
[heartbeat]
enabled = true
interval_secs = 3600 # Check every hour (3600 seconds)
active_hours = "08:00-22:00" # Only run during these hours (local time)
ack_max_chars = 50 # Suppress responses shorter than this
channel = "telegram" # Route alerts to this channelHEARTBEAT.md
Create HEARTBEAT.md in your workspace (or ~/.ryvos/) with instructions for what to check:
# Heartbeat Checks
## Server Health
- Check if https://api.example.com/health returns 200
- Check if disk usage on /home is below 80%
## Git Status
- Check if there are any uncommitted changes in ~/projects/main-app
- Check if main branch is up to date with origin
## Alerts
- If the server is down, mention it urgently
- If disk usage is above 90%, flag as critical
- If everything is fine, just say "all clear" (will be suppressed)Smart Suppression
The ack_max_chars setting suppresses short "all good" responses. When the heartbeat check returns a response shorter than this threshold (e.g., "All systems normal" is 19 chars), it is logged but not sent to the channel.
This prevents notification fatigue. You only get alerted when something actually needs attention.
[heartbeat]
ack_max_chars = 50 # Suppress responses under 50 charactersExamples:
- "All clear." (10 chars) -- suppressed
- "Everything looks good, no issues." (34 chars) -- suppressed
- "WARNING: Disk usage at 92% on /home. Server API returned 503 twice in the last hour." (86 chars) -- sent to channel
Active Hours
The heartbeat only runs during active hours:
[heartbeat]
active_hours = "08:00-22:00" # Local timezoneOutside these hours, heartbeat checks are skipped. This prevents alerts from waking you up at 3 AM.
Events
| Event | When |
|---|---|
HeartbeatFired | A heartbeat check has started |
HeartbeatOk | Check completed, everything normal (may be suppressed) |
HeartbeatAlert | Check found an issue, alert sent to channel |
CLI Session Resumption
If you were interacting with Ryvos via the CLI when the heartbeat fires, the heartbeat runs in a separate session and does not interrupt your conversation. The results are routed to the configured channel.
Cron vs Heartbeat
| Feature | Cron | Heartbeat |
|---|---|---|
| Purpose | Scheduled tasks | Health monitoring |
| Trigger | Cron expression | Fixed interval |
| Instructions | Per-job prompt | HEARTBEAT.md (shared) |
| Suppression | No (always sends output) | Yes (ack_max_chars) |
| Active hours | No (runs on schedule) | Yes (configurable) |
| Multiple jobs | Yes | Single check |
| Channel routing | Per-job | Single channel |
Use cron for specific recurring tasks (reports, cleanups, checks). Use heartbeat for general health monitoring with smart alerting.
Daemon Mode
Both cron and heartbeat require daemon mode:
ryvos daemon --gatewayTo selectively disable:
ryvos daemon --no-cron # Disable cron jobs
ryvos daemon --no-heartbeat # Disable heartbeatNext Steps
- Webhooks & Gateway — HTTP API for programmatic triggering
- Configuration — Full cron and heartbeat config reference
- Channels Overview — Where alerts are routed