feat: custom heartbeat prompt via YAML config or file (#233)

* feat: custom heartbeat prompt via YAML config or file

Wire up the existing but unused HeartbeatConfig.prompt field so users
can customize what the agent sees during heartbeats. Adds three ways
to set it: inline YAML (prompt), file-based (promptFile, re-read each
tick for live editing), and env var (HEARTBEAT_PROMPT). Also documents
the <no-reply/> opt-out behavior.

Fixes #232

Written by Cameron ◯ Letta Code

"The only way to do great work is to love what you do." -- Steve Jobs

* test: add coverage for heartbeat prompt resolution

Tests buildCustomHeartbeatPrompt and HeartbeatService prompt resolution:
- default prompt fallback
- inline prompt usage
- promptFile loading
- inline > promptFile precedence
- live reload (file re-read each tick)
- graceful fallback on missing file
- empty file falls back to default

Written by Cameron ◯ Letta Code

"The only way to do great work is to love what you do." -- Steve Jobs
This commit is contained in:
Cameron
2026-02-09 10:01:15 -08:00
committed by GitHub
parent 999ee89cb0
commit 673f247793
6 changed files with 301 additions and 5 deletions

View File

@@ -289,6 +289,43 @@ features:
Heartbeats are background tasks where the agent can review pending work.
#### Custom Heartbeat Prompt
You can customize what the agent is told during heartbeats. The custom text replaces the default body while keeping the silent mode envelope (time, trigger metadata, and messaging instructions).
Inline in YAML:
```yaml
features:
heartbeat:
enabled: true
intervalMin: 60
prompt: "Check your todo list and work on the highest priority item."
```
From a file (re-read each tick, so edits take effect without restart):
```yaml
features:
heartbeat:
enabled: true
intervalMin: 60
promptFile: ./prompts/heartbeat.md
```
Via environment variable:
```bash
HEARTBEAT_PROMPT="Review recent conversations" npm start
```
Precedence: `prompt` (inline YAML) > `HEARTBEAT_PROMPT` (env var) > `promptFile` (file) > built-in default.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `features.heartbeat.prompt` | string | _(none)_ | Custom heartbeat prompt text |
| `features.heartbeat.promptFile` | string | _(none)_ | Path to prompt file (relative to working dir) |
### Cron Jobs
```yaml
@@ -298,6 +335,23 @@ features:
Enable scheduled tasks. See [Cron Setup](./cron-setup.md).
### No-Reply (Opt-Out)
The agent can choose not to respond to a message by sending exactly:
```
<no-reply/>
```
When the bot receives this marker, it suppresses the response and nothing is sent to the channel. This is useful in group chats where the agent shouldn't reply to every message.
The agent is taught about this behavior in two places:
- **System prompt**: A "Choosing Not to Reply" section explains when to use it (messages not directed at the agent, simple acknowledgments, conversations between other users, etc.)
- **Message envelope**: Group messages include a hint reminding the agent of the `<no-reply/>` option. DMs do not include this hint.
The bot also handles this gracefully during streaming -- it holds back partial output while the response could still become `<no-reply/>`, so users never see a partial match leak through.
## Polling Configuration
Background polling for integrations like Gmail. Runs independently of agent cron jobs.