6.5 KiB
Scheduling Tasks (Cron & Heartbeat)
LettaBot supports two types of background tasks:
- Cron jobs: Send scheduled messages at specific times
- Heartbeats: Periodic agent check-ins
Enabling Background Tasks
Add to your lettabot.yaml:
features:
cron: true
heartbeat:
enabled: true
intervalMin: 60 # Every 60 minutes
Or via environment variables:
CRON_ENABLED=true
HEARTBEAT_ENABLED=true
HEARTBEAT_INTERVAL_MIN=60
Cron Jobs
Schedule tasks that send you messages at specific times.
Creating a Job
lettabot-cron create \
--name "Morning Briefing" \
--schedule "0 8 * * *" \
--message "Good morning! Review tasks for today." \
--deliver telegram:123456789
Options:
--name- Job name (required)--schedule- Cron expression (required)--message- Message sent when job runs (required)--deliver- Where to send:channel:chatId(defaults to last messaged chat at creation time; falls back to last messaged chat at runtime)--silent- Do not deliver response automatically (agent must uselettabot-message send)
Managing Jobs
lettabot-cron list # Show all jobs
lettabot-cron update <id> ... # Update job properties (--deliver, --name, --message, etc.)
lettabot-cron delete <id> # Delete a job
lettabot-cron enable <id> # Enable a job
lettabot-cron disable <id> # Disable a job
Cron Expression Syntax
┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sun=0)
* * * * *
Examples:
| Expression | When |
|---|---|
0 8 * * * |
Daily at 8:00 AM |
0 9 * * 1-5 |
Weekdays at 9:00 AM |
0 */2 * * * |
Every 2 hours |
30 17 * * 5 |
Fridays at 5:30 PM |
0 0 1 * * |
First of month at midnight |
Example Jobs
Daily morning check-in:
lettabot-cron create \
-n "Morning" \
-s "0 8 * * *" \
-m "Good morning! What's on today's agenda?"
Weekly review:
lettabot-cron create \
-n "Weekly Review" \
-s "0 17 * * 5" \
-m "Friday wrap-up: What did we accomplish this week?"
Hourly reminder:
lettabot-cron create \
-n "Hydration" \
-s "0 * * * *" \
-m "Time to drink water!"
Heartbeats
Heartbeats are periodic check-ins where the agent can:
- Review pending tasks
- Check reminders
- Perform proactive actions
Configuration
features:
heartbeat:
enabled: true
intervalMin: 60 # Default: 60 minutes
skipRecentPolicy: fraction # fixed | fraction | off
skipRecentFraction: 0.5 # Used when policy=fraction (0-1)
# skipRecentUserMin: 5 # Used when policy=fixed (0 disables)
interruptOnUserMessage: true # Cancel in-flight heartbeat when user messages arrive
By default, automatic heartbeats are skipped for half the heartbeat interval (skipRecentPolicy: fraction, skipRecentFraction: 0.5).
- Use
skipRecentPolicy: fixed+skipRecentUserMinfor a fixed window. - Use
skipRecentPolicy: offto disable recent-user skipping. interruptOnUserMessage: trueprioritizes live user messages by cancelling in-flight heartbeat runs on the same key.- Manual
/heartbeatalways bypasses the skip check.
Manual Trigger
You can trigger a heartbeat manually via the /heartbeat command in any channel.
How It Works
- At each interval (or when
/heartbeatis called), the agent receives a heartbeat message - The agent runs in Silent Mode - responses are not automatically delivered
- If the agent wants to message you, it must use
lettabot-message send
This prevents unwanted messages while allowing proactive behavior when needed.
Heartbeat To-Dos
Heartbeats include a PENDING TO-DOS section when actionable tasks exist. Tasks can come from:
lettabot todo ...CLI commands- The
manage_todotool - Built-in Letta Code todo tools (
TodoWrite,WriteTodos,write_todos), which are synced into LettaBot's persistent todo store
Only actionable tasks are shown in the heartbeat prompt:
completed: falsesnoozed_untilnot set, or already in the past
Delivery Behavior
Cron Jobs
Cron jobs deliver responses automatically:
- If
--deliverwas specified at creation, responses go to that channel/chat - If
--deliverwas omitted, the CLI auto-fills from the last messaged chat - At runtime, if a job has no configured delivery target, it falls back to the most recent message target
- Use
--silentat creation to explicitly opt out of automatic delivery
Heartbeats (Silent Mode)
Heartbeats run in Silent Mode -- responses are NOT automatically delivered:
- The agent sees a
[SILENT MODE]banner with instructions - To send messages, the agent must explicitly run:
lettabot-message send --text "Your message here"
Requirements for background messaging:
- Bash tool must be enabled for the agent
- A user must have messaged the bot at least once (establishes delivery target)
Monitoring & Logs
Check Job Status
lettabot-cron list
Shows:
- Job ID, name, schedule
- Next run time
- Last run status
Log Files
cron-jobs.json- Job configurationscron-log.jsonl- Execution logs
Cron Storage Path
Cron state is resolved with deterministic precedence:
RAILWAY_VOLUME_MOUNT_PATHDATA_DIRWORKING_DIR/tmp/lettabot
Migration note:
- Older versions used
process.cwd()/cron-jobs.jsonwhenDATA_DIRwas not set. - On first run after upgrade, LettaBot auto-copies that legacy file into the new canonical cron path.
Troubleshooting
Cron jobs not running
- Check
features.cron: truein config - Verify schedule expression is valid
- Check
lettabot-cron listfor next run time
Agent not sending messages during heartbeat
- Check if Bash tool is enabled (agent needs to run CLI)
- Verify a user has messaged the bot at least once
- Check the ADE to see agent activity
Jobs running but no messages received
- Check
lettabot-cron list-- does the job show a delivery target? - If delivery shows
(none), the job was created without--deliverand no user had messaged the bot yet - Fix:
lettabot-cron update <id> --deliver telegram:123456789(or your channel:chatId) - Alternatively, send the bot any message to establish a last-message target -- new runs will auto-deliver
- Check logs for
"mode":"silent"entries -- this confirms the job ran but had nowhere to send the response