Scheduling & Automation
OrcBot provides three scheduling mechanisms: one-time task scheduling, recurring heartbeat tasks, and event-driven polling. Together, these enable fully autonomous operation.
schedule_task
Schedule a one-time task for future execution.
Parameters
When to run the task. Supports:
Relative time: "in 2 hours", "in 30 minutes", "in 1 day"
Absolute time: "2025-01-15T14:30:00Z" (ISO 8601)
Cron expression: "0 9 * * 1-5" (weekdays at 9 AM)
Natural language task description
Return Value
Confirmation with scheduled time and task ID
Relative Time Parsing
Supported formats:
"in N minutes" - N minutes from now
"in N hours" - N hours from now
"in N days" - N days from now
"in N weeks" - N weeks from now
Cron Expressions
Standard cron format:
* * * * *
│ │ │ │ │
│ │ │ │ └─ Day of week (0-7, 0=Sunday)
│ │ │ └─── Month (1-12)
│ │ └───── Day of month (1-31)
│ └─────── Hour (0-23)
└───────── Minute (0-59)
Example Usage
Relative time:
{
"skill" : "schedule_task" ,
"args" : {
"time_or_cron" : "in 2 hours" ,
"task_description" : "Send daily report to team via Telegram"
}
}
Absolute time:
{
"skill" : "schedule_task" ,
"args" : {
"time_or_cron" : "2025-01-15T14:30:00Z" ,
"task_description" : "Deploy production release"
}
}
Cron expression:
{
"skill" : "schedule_task" ,
"args" : {
"time_or_cron" : "0 9 * * 1-5" ,
"task_description" : "Morning standup reminder (weekdays at 9 AM)"
}
}
Response Example
Task scheduled for 2025-01-15T14:30:00Z (in 2 hours).
Task ID: scheduled-task-1736954400000
Description: Send daily report to team via Telegram
Scheduled tasks are stored in scheduled-tasks.json with:
id : Unique task identifier
cronExpression : Cron pattern
description : Task description
nextRun : Next execution timestamp
createdAt : Creation timestamp
Canceling Tasks
Scheduled tasks can be canceled by modifying scheduled-tasks.json manually (future skill planned).
isDeep : false
isDangerous : false
Use schedule_task for one-time future tasks. For recurring automation, use heartbeat_schedule instead.
heartbeat_schedule
Schedule a recurring autonomous task.
Parameters
Cron expression for recurrence (see format above)
What the agent should do on each execution
Return Value
Confirmation with next run time and schedule ID
Heartbeat vs Autonomy
Heartbeat tasks:
Triggered by cron schedule
Task-specific and focused
Runs even when queue is empty
Survives restarts (persisted to heartbeat-schedules.json)
Autonomy mode:
Triggered by interval timer
Context-aware and exploratory
Only runs when idle
Governed by autonomyEnabled config
Example Usage
Daily report:
{
"skill" : "heartbeat_schedule" ,
"args" : {
"cronExpression" : "0 18 * * 1-5" ,
"task_description" : "Generate daily work summary and send to Frederick via Telegram"
}
}
Hourly check:
{
"skill" : "heartbeat_schedule" ,
"args" : {
"cronExpression" : "0 * * * *" ,
"task_description" : "Check server health and alert if CPU >90%"
}
}
Weekly backup:
{
"skill" : "heartbeat_schedule" ,
"args" : {
"cronExpression" : "0 2 * * 0" ,
"task_description" : "Backup project files to cloud storage"
}
}
Every 15 minutes:
{
"skill" : "heartbeat_schedule" ,
"args" : {
"cronExpression" : "*/15 * * * *" ,
"task_description" : "Check for new GitHub pull requests and notify team"
}
}
Response Example
Heartbeat schedule created:
Cron: 0 18 * * 1-5 (weekdays at 6 PM)
Task: Generate daily work summary and send to Frederick via Telegram
Next run: 2025-01-15T18:00:00Z
Schedule ID: heartbeat-1736954400000
Persistence
Heartbeat schedules are saved to heartbeat-schedules.json and survive agent restarts.
Smart Heartbeat Features
Exponential Backoff:
When heartbeat tasks are unproductive (no meaningful work done), the interval automatically increases:
First idle: 2x interval
Second idle: 4x interval
Third idle: 8x interval
Max: 16x original interval
Productivity resets the interval to baseline.
Productivity Tracking:
The system tracks whether heartbeat tasks result in:
User-visible messages sent
Files created
Commands executed
Research completed
Unproductive tasks (e.g., “nothing to do”) trigger backoff.
Context-Aware Execution:
Heartbeat tasks have access to:
Recent conversation context
User preferences from USER.md
Past actions from memory
Current time and date
isDeep : false
isDangerous : false
Heartbeat tasks run autonomously. Ensure they have clear success criteria and don’t spam users. Use autonomyAllowedChannels to control which channels receive proactive messages.
Polling System
Event-driven condition monitoring without busy-waiting loops.
Architecture
The polling system provides:
Condition checking : Evaluate a condition function repeatedly
Success/failure callbacks : Trigger actions when condition met
Timeout handling : Maximum wait time before giving up
Interval control : How often to check the condition
Event bus integration : Emits events for monitoring
Creating a Poll
Polls are created programmatically (not via a skill). Example:
import { PollingManager } from './core/PollingManager' ;
const pollingManager = new PollingManager ();
const pollId = pollingManager . createPoll ({
condition : async () => {
// Check if file exists
return fs . existsSync ( '/tmp/deployment-complete.flag' );
},
onSuccess : async () => {
console . log ( 'Deployment complete!' );
// Send notification
},
onFailure : async () => {
console . log ( 'Deployment timed out' );
// Send alert
},
intervalMs: 5000 , // Check every 5 seconds
timeoutMs: 300000 , // Give up after 5 minutes
metadata: { task: 'deployment-watch' }
});
// Later: cancel if needed
pollingManager . cancelPoll ( pollId );
Use Cases
Wait for file creation:
condition : async () => fs . existsSync ( '/tmp/report.pdf' )
Wait for API response:
condition : async () => {
const res = await fetch ( 'https://api.example.com/status' );
const data = await res . json ();
return data . status === 'complete' ;
}
Wait for process completion:
condition : async () => {
const { stdout } = await exec ( 'ps aux | grep deploy' );
return ! stdout . includes ( 'deploy-script' );
}
Event Bus Events
eventBus . on ( 'poll:created' , ( pollId , config ) => { /* ... */ });
eventBus . on ( 'poll:checked' , ( pollId , result ) => { /* ... */ });
eventBus . on ( 'poll:success' , ( pollId ) => { /* ... */ });
eventBus . on ( 'poll:timeout' , ( pollId ) => { /* ... */ });
eventBus . on ( 'poll:cancelled' , ( pollId ) => { /* ... */ });
Best Practices
Efficient polling intervals:
Fast events (1-10s): 1-2 second intervals
Medium events (1-5min): 5-10 second intervals
Slow events (>5min): 30-60 second intervals
Set reasonable timeouts. Polls consume memory until they complete or time out. Always set a maximum wait time.
Common Workflows
Deployment Automation
// 1. Schedule deployment
schedule_task ( "2025-01-15T18:00:00Z" , "Deploy production release" )
// 2. In deployment task:
run_command ( "./deploy.sh" )
// 3. Create poll to wait for completion
pollingManager . createPoll ({
condition : async () => fs . existsSync ( '/tmp/deploy-complete' ),
onSuccess : async () => {
send_telegram ( teamChatId , "Deployment successful! ✅" )
},
intervalMs: 5000 ,
timeoutMs: 600000 // 10 minutes
})
Daily Reports
// Create heartbeat for daily summary
heartbeat_schedule ( "0 18 * * 1-5" ,
"Search chat history for today's conversations, generate summary, send to Frederick"
)
// Heartbeat task execution:
// 1. search_chat_history(jid, query="", daysAgo=1)
// 2. generate report with LLM
// 3. send_telegram(chatId, report)
Monitoring Integration
// Check server health every hour
heartbeat_schedule ( "0 * * * *" , "Check server metrics and alert if abnormal" )
// Implementation:
// 1. http_fetch("https://monitoring.example.com/api/metrics")
// 2. Analyze response
// 3. If CPU >90% or memory >85%: send_telegram(adminChatId, "Alert: High resource usage")
Reminder System
// User: "Remind me to call Alice in 2 hours"
schedule_task ( "in 2 hours" , "Send reminder to call Alice" )
// At execution time:
send_telegram ( userChatId , "⏰ Reminder: Call Alice" )
Configuration
Autonomy Settings
# Enable autonomous heartbeat
autonomyEnabled : true
# Base interval between heartbeats (milliseconds)
autonomyInterval : 300000 # 5 minutes
# Maximum backlog before pausing autonomy
autonomyBacklogLimit : 5
# Channels where autonomy can send proactive messages
autonomyAllowedChannels :
- telegram
- discord
Heartbeat Backoff
# Consecutive idle heartbeats before backoff
heartbeatIdleThreshold : 3
# Maximum backoff multiplier
heartbeatMaxBackoff : 16
Polling Defaults
# Default polling interval (milliseconds)
pollingDefaultInterval : 5000
# Default polling timeout (milliseconds)
pollingDefaultTimeout : 300000
Best Practices
Scheduling strategy:
Use schedule_task for one-time future actions
Use heartbeat_schedule for recurring autonomous work
Use polling for waiting on external events
Combine all three for complex workflows
Avoid heartbeat spam:
Set realistic cron intervals (not every minute unless necessary)
Use specific task descriptions so the agent knows when to skip
Configure autonomyAllowedChannels to control where messages go
Monitor productivity metrics to detect noisy tasks
Heartbeat tasks have full skill access. They can search the web, read files, send messages, and execute commands just like user-initiated tasks.
Troubleshooting
”Heartbeat not triggering”
Cause : Cron expression invalid or agent not running
Fix : Validate cron with https://crontab.guru/ , check daemon status
”Polling timed out”
Cause : Condition never became true within timeout
Fix : Increase timeout or check condition logic
”Autonomy paused”
Cause : Backlog limit reached
Fix : Process pending tasks or increase autonomyBacklogLimit
”Scheduled task not executing”
Cause : Task ID collision or scheduler not started
Fix : Check scheduled-tasks.json for duplicates, restart agent
Memory Store context for autonomous tasks
Orchestration Spawn agents for parallel scheduled work