Skip to main content

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

time_or_cron
string
required
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)
task_description
string
required
Natural language task description

Return Value

result
string
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

Task Metadata

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).

Metadata

  • 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

cronExpression
string
required
Cron expression for recurrence (see format above)
task_description
string
required
What the agent should do on each execution

Return Value

result
string
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

Metadata

  • 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:
  1. Use schedule_task for one-time future actions
  2. Use heartbeat_schedule for recurring autonomous work
  3. Use polling for waiting on external events
  4. 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