Seed HEARTBEAT.md on first access and skip effectively-empty checklists

The heartbeat feature was dead on arrival: nothing ever created HEARTBEAT.md,
so the runner silently skipped every cycle. Now the workspace returns an
in-memory seed template when the file doesn't exist in the database (no DB
write), and the runner detects "effectively empty" content (headers, HTML
comments, bare list markers) to avoid wasting LLM API calls on placeholder
templates. The user creates the real DB entry via memory_write when they
actually want periodic checks.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-05 19:51:59 -08:00
co-authored by Claude Opus 4.6
parent 1c9f9db420
commit 0ca05e3de3
2 changed files with 168 additions and 2 deletions
+25 -1
View File
@@ -60,6 +60,23 @@ use uuid::Uuid;
use crate::error::WorkspaceError;
/// Default template seeded into HEARTBEAT.md on first access.
///
/// Intentionally comment-only so the heartbeat runner treats it as
/// "effectively empty" and skips the LLM call until the user adds
/// real tasks.
const HEARTBEAT_SEED: &str = "\
# Heartbeat Checklist
<!-- Keep this file empty to skip heartbeat API calls.
Add tasks below when you want the agent to check something periodically.
Example:
- [ ] Check for unread emails needing a reply
- [ ] Review today's calendar for upcoming meetings
- [ ] Check CI build status for main branch
-->";
/// Workspace provides database-backed memory storage for an agent.
///
/// Each workspace is scoped to a user (and optionally an agent).
@@ -246,10 +263,17 @@ impl Workspace {
}
/// Get the heartbeat checklist (HEARTBEAT.md).
///
/// Returns the DB-stored checklist if it exists, otherwise falls back
/// to the in-memory seed template. The seed is never written to the
/// database; the user creates the real file via `memory_write` when
/// they actually want periodic checks. The seed content is all HTML
/// comments, which the heartbeat runner treats as "effectively empty"
/// and skips the LLM call.
pub async fn heartbeat_checklist(&self) -> Result<Option<String>, WorkspaceError> {
match self.read(paths::HEARTBEAT).await {
Ok(doc) => Ok(Some(doc.content)),
Err(WorkspaceError::DocumentNotFound { .. }) => Ok(None),
Err(WorkspaceError::DocumentNotFound { .. }) => Ok(Some(HEARTBEAT_SEED.to_string())),
Err(e) => Err(e),
}
}