Route HEARTBEAT writes to workspace DB and broadcast notifications

- Add dedicated "heartbeat" target in memory_write tool so the LLM
  routes HEARTBEAT.md writes to the database instead of the filesystem
- Update tool description to clarify it's database-backed storage
- Broadcast heartbeat notifications to all channels when no explicit
  notify target is configured, instead of silently logging them

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-05 20:02:33 -08:00
co-authored by Claude Opus 4.6
parent 0ca05e3de3
commit 7fcc2279cc
2 changed files with 33 additions and 10 deletions
+13 -5
View File
@@ -212,11 +212,19 @@ impl Agent {
}
}
_ => {
// No target configured, just log
tracing::info!(
"Heartbeat notification (no target configured): {}",
&response.content
);
// No explicit target, broadcast to all channels
// for the default user so notifications actually
// reach someone instead of vanishing into logs.
let results = channels.broadcast_all("default", response).await;
for (ch, result) in results {
if let Err(e) = result {
tracing::warn!(
"Failed to broadcast heartbeat to {}: {}",
ch,
e
);
}
}
}
}
}
+20 -5
View File
@@ -133,10 +133,11 @@ impl Tool for MemoryWriteTool {
}
fn description(&self) -> &str {
"Write to persistent memory. Use for important facts, decisions, preferences, \
or lessons learned that should be remembered across sessions. Use 'memory' target \
for curated long-term facts, 'daily_log' for timestamped session notes, or \
provide a custom path for arbitrary file creation."
"Write to persistent memory (database-backed, NOT the local filesystem). \
Use for important facts, decisions, preferences, or lessons learned that should \
be remembered across sessions. Targets: 'memory' for curated long-term facts, \
'daily_log' for timestamped session notes, 'heartbeat' for the periodic \
checklist (HEARTBEAT.md), or provide a custom path for arbitrary file creation."
}
fn parameters_schema(&self) -> serde_json::Value {
@@ -149,7 +150,7 @@ impl Tool for MemoryWriteTool {
},
"target": {
"type": "string",
"description": "Where to write: 'memory' for MEMORY.md, 'daily_log' for today's log, or a path like 'projects/alpha/notes.md'",
"description": "Where to write: 'memory' for MEMORY.md, 'daily_log' for today's log, 'heartbeat' for HEARTBEAT.md checklist, or a path like 'projects/alpha/notes.md'",
"default": "daily_log"
},
"append": {
@@ -214,6 +215,20 @@ impl Tool for MemoryWriteTool {
.map_err(|e| ToolError::ExecutionFailed(format!("Write failed: {}", e)))?;
format!("daily/{}.md", chrono::Utc::now().format("%Y-%m-%d"))
}
"heartbeat" => {
if append {
self.workspace
.append(paths::HEARTBEAT, content)
.await
.map_err(|e| ToolError::ExecutionFailed(format!("Write failed: {}", e)))?;
} else {
self.workspace
.write(paths::HEARTBEAT, content)
.await
.map_err(|e| ToolError::ExecutionFailed(format!("Write failed: {}", e)))?;
}
paths::HEARTBEAT.to_string()
}
path => {
if append {
self.workspace