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