Heartbeat
The heartbeat is Alice's autonomous market monitoring system. Built on top of the cron engine, it periodically wakes Alice up, lets her evaluate the situation, and decides whether you need to know about anything.
How It Works
The heartbeat registers a special cron job (__heartbeat__) that fires at a configured interval. On each fire:
- Active hours guard — If you've set an active window, fires outside it are skipped silently
- AI call — Alice receives the heartbeat prompt and processes it through the full pipeline (can use all tools — check portfolio, scan news, calculate indicators)
- Response parsing — Alice's response is parsed for the structured protocol
- Dedup — Identical messages within 24 hours are suppressed
- Delivery — Routed through ConnectorCenter to wherever you last chatted
Response Protocol
Alice responds using a structured format:
STATUS: HEARTBEAT_OK
REASON: All positions within normal range, no notable news.
STATUS: CHAT_YES
REASON: Significant price movement detected.
CONTENT: BTC just dropped 8% in the last hour — now at $87,200.
Your stop-loss on the long position may trigger soon.
| Status | Meaning | What Happens |
|---|---|---|
HEARTBEAT_OK | Nothing to report | Logged silently, no notification sent |
CHAT_YES | Worth telling you | CONTENT is delivered to your last-interacted channel |
Fail-open — If Alice's response can't be parsed into the structured format, the entire text is treated as CHAT_YES and delivered. Better to over-notify than to swallow something important.
Configuration
data/config/heartbeat.json:
{
"enabled": true,
"every": "30m",
"activeHours": {
"start": "9:00",
"end": "22:00",
"timezone": "America/New_York"
}
}
| Field | Description |
|---|---|
enabled | Master switch |
every | Check interval (default: "30m") |
activeHours | Time window. null = always active. |
Active Hours
Restrict heartbeat checks to specific hours:
{
"activeHours": {
"start": "9:00",
"end": "22:00",
"timezone": "America/New_York"
}
}
Overnight ranges work too: "start": "22:00", "end": "06:00" covers the overnight window.
Outside the active window, fires are skipped with a heartbeat.skip event.
Customizing the Prompt
The heartbeat prompt defines what Alice checks and how she formats her response. It lives at data/brain/heartbeat.md (gitignored). The default is copied from default/heartbeat.default.md on first run.
Edit it to change behavior — for example, you could make Alice focus on specific positions, check particular news sources, or apply different thresholds for what's worth reporting.
Dedup
If Alice would send the exact same message she sent within the last 24 hours, it's suppressed. This prevents "BTC is still down" every 30 minutes when nothing has actually changed.
Events
The heartbeat writes events to the EventLog for observability:
| Event | When |
|---|---|
heartbeat.skip | Skipped (outside active hours, HEARTBEAT_OK, empty, or duplicate) |
heartbeat.done | Message delivered (includes reply text, reason, duration) |
heartbeat.error | AI call or delivery failed |
Hot-Toggle
Enable or disable at runtime without restart:
You: Turn off the heartbeat.
Alice: Heartbeat disabled.
This persists to heartbeat.json and toggles the __heartbeat__ cron job. You can also toggle it from the Web UI settings panel.