Skip to main content
The heartbeat service enables Weaver agents to proactively check for tasks, monitor systems, and take actions at regular intervals without explicit user prompts.

Overview

Heartbeat runs a periodic check loop that:
  1. Reads tasks from HEARTBEAT.md
  2. Executes agent with heartbeat prompt
  3. Processes tool calls and responses
  4. Reports results to user (optional)
Use Cases:
  • Monitoring device status
  • Checking for notifications
  • Periodic system health checks
  • Automated maintenance tasks
  • Scheduled reports

Configuration

Service Creation

import "github.com/operatoronline/weaver/pkg/heartbeat"

service := heartbeat.NewHeartbeatService(
    workspace,      // Workspace directory
    30,            // Interval in minutes
    true,          // Enabled
)

Parameters

  • workspace (string) - Working directory containing HEARTBEAT.md
  • intervalMinutes (int) - Check interval (minimum: 5 minutes)
  • enabled (bool) - Service enabled status
Defaults:
  • Minimum interval: 5 minutes
  • Default interval: 30 minutes
  • Initial delay: 1 second

Setting Up Dependencies

// Set message bus for sending results
service.SetBus(messageBus)

// Set heartbeat handler
handler := func(prompt, channel, chatID string) *tools.ToolResult {
    // Execute agent with heartbeat prompt
    return agentExecute(prompt, channel, chatID)
}
service.SetHandler(handler)

Heartbeat File

HEARTBEAT.md Format

The heartbeat file contains tasks for the agent to check:
# Heartbeat Check List

This file contains tasks for the heartbeat service to check periodically.

## Examples

- Check for unread messages
- Review upcoming calendar events  
- Check device status (e.g., MaixCam)

## Instructions

- Execute ALL tasks listed below. Do NOT skip any task.
- For simple tasks (e.g., report current time), respond directly.
- For complex tasks that may take time, use the spawn tool to create a subagent.
- The spawn tool is async - subagent results will be sent to the user automatically.
- After spawning a subagent, CONTINUE to process remaining tasks.
- Only respond with HEARTBEAT_OK when ALL tasks are done AND nothing needs attention.

---

Add your heartbeat tasks below this line:

- Check MaixCam status every 30 minutes
- Review email for urgent messages
- Monitor disk space on server

Auto-Generation

If HEARTBEAT.md doesn’t exist, a default template is created on first run:
service.Start() // Creates HEARTBEAT.md if missing

Service Lifecycle

Starting the Service

err := service.Start()
if err != nil {
    log.Fatalf("Failed to start heartbeat: %v", err)
}
Behavior:
  • Loads configuration
  • Starts background ticker
  • Executes first heartbeat after 1 second
  • Runs subsequent checks at configured interval

Stopping the Service

service.Stop()
Behavior:
  • Gracefully stops ticker
  • Waits for current heartbeat to complete
  • Closes background goroutine

Checking Status

if service.IsRunning() {
    fmt.Println("Heartbeat service is active")
}

Execution Flow

1. Heartbeat Trigger

Every interval (e.g., 30 minutes):
ticker := time.NewTicker(interval)
for {
    select {
    case <-ticker.C:
        executeHeartbeat()
    }
}

2. Prompt Building

Heartbeat reads HEARTBEAT.md and builds the prompt:
func (hs *HeartbeatService) buildPrompt() string {
    content := readFile("HEARTBEAT.md")
    now := time.Now().Format("2006-01-02 15:04:05")
    
    return fmt.Sprintf(`# Heartbeat Check

Current time: %s

You are a proactive AI assistant. This is a scheduled heartbeat check.
Review the following tasks and execute any necessary actions using available skills.
If there is nothing that requires attention, respond ONLY with: HEARTBEAT_OK

%s
`, now, content)
}

3. Handler Execution

The handler processes the heartbeat:
handler := func(prompt, channel, chatID string) *tools.ToolResult {
    // Agent processes heartbeat prompt
    // Can call tools, spawn subagents, etc.
    result := agent.Process(prompt)
    return result
}

4. Result Processing

Silent (HEARTBEAT_OK):
if result.Silent {
    // No action needed, user not notified
    return
}
Async (spawned subagent):
if result.Async {
    // Subagent running in background
    // Will notify user when complete
    return
}
User Notification:
if result.ForUser != "" {
    msgBus.PublishOutbound(bus.OutboundMessage{
        Channel: lastChannel,
        ChatID:  lastChatID,
        Content: result.ForUser,
    })
}

Channel Resolution

Heartbeat uses the last active channel for notifications:

State Management

lastChannel := state.GetLastChannel()
// Format: "platform:user_id" (e.g., "telegram:123456")

platform, userID := parseLastChannel(lastChannel)

Channel Validation

Internal channels are filtered:
if constants.IsInternalChannel(platform) {
    // Skip cli, system, cron channels
    return
}
Valid channels:
  • telegram
  • whatsapp
  • discord
  • slack
  • etc.
Invalid (internal) channels:
  • cli
  • system
  • cron
  • heartbeat

Advanced Usage

Multi-Task Heartbeats

Execute multiple checks in one heartbeat:
## Tasks

1. Check MaixCam camera status
2. Review email inbox for priority items
3. Monitor server disk space (spawn if over 80%)
4. Check for GitHub notifications

## Response Rules

- Process ALL tasks before responding
- Spawn subagents for time-consuming tasks
- Only say HEARTBEAT_OK if nothing needs attention

Using Spawn Tool

For long-running tasks, use the spawn tool: Agent’s heartbeat response:
{
  "tool_calls": [
    {
      "name": "spawn",
      "args": {
        "task": "Check all security cameras and report any offline devices",
        "label": "camera-check"
      }
    }
  ]
}
Result:
  • Heartbeat completes immediately (async)
  • Subagent runs in background
  • User notified when subagent finishes

Conditional Responses

Respond only when action is needed:
## Disk Space Check

- Check disk usage with `df -h`
- If any partition is over 85%, spawn cleanup task
- Otherwise, respond HEARTBEAT_OK
Agent logic:
  1. Call exec tool: df -h
  2. Parse output
  3. If usage > 85%: spawn cleanup subagent
  4. If usage < 85%: return HEARTBEAT_OK

Custom Intervals

Adjust interval based on task urgency:
// Critical monitoring: 5 minutes
service := heartbeat.NewHeartbeatService(workspace, 5, true)

// Regular checks: 30 minutes (default)
service := heartbeat.NewHeartbeatService(workspace, 30, true)

// Infrequent checks: 120 minutes
service := heartbeat.NewHeartbeatService(workspace, 120, true)

Logging

Heartbeat maintains a log file: Location: <workspace>/heartbeat.log Format:
[2026-03-01 10:30:00] [INFO] Heartbeat service started
[2026-03-01 10:30:01] [INFO] Executing heartbeat
[2026-03-01 10:30:01] [INFO] Resolved channel: telegram, chatID: 123456
[2026-03-01 10:30:02] [INFO] Async task started: Spawned subagent 'camera-check'
[2026-03-01 10:31:00] [INFO] Heartbeat completed: HEARTBEAT_OK
[2026-03-01 11:00:00] [ERROR] Heartbeat error: agent timeout
Log Levels:
  • INFO - Normal operations
  • ERROR - Execution failures

Error Handling

Handler Errors

if result.IsError {
    log.Printf("Heartbeat error: %s", result.ForLLM)
    // Error logged, service continues
    // Next heartbeat will retry
}

Missing HEARTBEAT.md

if !fileExists("HEARTBEAT.md") {
    createDefaultTemplate()
    return "" // Skip this heartbeat
}

Channel Resolution Failure

if channel == "" || chatID == "" {
    // Result not sent to user
    // Logged for debugging
    return
}

Performance Considerations

Heartbeat Execution Time

  • Simple checks: < 1 second
  • With tools: 2-5 seconds
  • With spawn: 1-2 seconds (async)

Resource Usage

  • Memory: Minimal (single goroutine)
  • CPU: Idle between ticks, active during execution
  • Disk I/O: Read HEARTBEAT.md, write logs

Optimization Tips

  1. Use spawn for heavy tasks:
    - Spawn subagent for backup verification (don't block heartbeat)
    
  2. Combine related checks:
    - Check all device statuses in one tool call
    
  3. Adjust interval appropriately:
    • Critical: 5-10 minutes
    • Standard: 30 minutes
    • Non-urgent: 60+ minutes

Best Practices

  1. Keep tasks atomic:
    • One clear objective per task
    • Easy to validate completion
  2. Use HEARTBEAT_OK liberally:
    • Only notify user when action is needed
    • Reduces notification fatigue
  3. Leverage async operations:
    • Use spawn for time-consuming tasks
    • Keep heartbeat execution fast
  4. Document task expectations:
    • Clear success criteria
    • Failure handling instructions
  5. Monitor heartbeat logs:
    • Check for recurring errors
    • Adjust tasks based on results
  6. Test before deployment:
    • Run manual heartbeat check
    • Verify tool availability
    • Confirm channel routing

Example Configurations

DevOps Monitoring

# Heartbeat Tasks

- Check server CPU/memory with `top -b -n 1`
- Monitor docker containers: `docker ps --filter status=exited`
- Check disk space: `df -h`
- Review nginx error logs for 5xx errors
- If critical issues found, spawn alert subagent

Personal Assistant

# Heartbeat Tasks

- Check calendar for events in next 2 hours
- Review unread emails (priority inbox)
- Check weather forecast for travel planning
- Monitor package delivery status
- Update daily task list

IoT Device Management

# Heartbeat Tasks

- Check MaixCam status: `curl http://maixcam.local/status`
- Read temperature sensor via I2C (bus 1, addr 0x38)
- Monitor security camera connectivity
- Check smart home hub connection
- Report any offline devices

Integration with Other Features

Heartbeat + Cron

Use cron for specific schedules, heartbeat for general monitoring: Cron: “Daily backup at 2 AM” Heartbeat: “Check if backup completed successfully”

Heartbeat + Skills

Load skills for domain-specific heartbeats:
# Heartbeat Tasks (requires hardware skill)

- Use I2C tool to read AHT20 sensor
- Check temperature is within normal range (0-50°C)
- If abnormal, spawn alert subagent

Heartbeat + Device Monitoring

Respond to device events:
# Heartbeat Tasks

- Check if any USB devices were hotplugged
- Verify MaixCam is still connected
- Restart camera service if needed

Build docs developers (and LLMs) love