Add heartbeat integration, planning phase, and auto-repair

- Add HeartbeatConfig for proactive periodic execution with channel notifications
- Add use_planning option to Worker for ActionPlan generation before tool execution
- Implement tool failure tracking in database (V3 migration)
- Add auto-repair via Builder for broken WASM tools in self_repair.rs
- Record tool failures in Worker for self-repair tracking
- Update .env.example with new configuration options

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-03 09:32:01 -08:00
co-authored by Claude Opus 4.5
parent 2df4a4f5f0
commit 235f6aae18
11 changed files with 551 additions and 127 deletions
+87
View File
@@ -439,3 +439,90 @@ fn parse_job_state(s: &str) -> JobState {
_ => JobState::Pending,
}
}
// ==================== Tool Failures ====================
use crate::agent::BrokenTool;
impl Store {
/// Record a tool failure (upsert: increment count if exists).
pub async fn record_tool_failure(
&self,
tool_name: &str,
error_message: &str,
) -> Result<(), DatabaseError> {
let conn = self.conn().await?;
conn.execute(
r#"
INSERT INTO tool_failures (tool_name, error_message, error_count, last_failure)
VALUES ($1, $2, 1, NOW())
ON CONFLICT (tool_name) DO UPDATE SET
error_message = $2,
error_count = tool_failures.error_count + 1,
last_failure = NOW()
"#,
&[&tool_name, &error_message],
)
.await?;
Ok(())
}
/// Get tools that have failed more than `threshold` times and haven't been repaired.
pub async fn get_broken_tools(&self, threshold: i32) -> Result<Vec<BrokenTool>, DatabaseError> {
let conn = self.conn().await?;
let rows = conn
.query(
r#"
SELECT tool_name, error_message, error_count, first_failure, last_failure,
last_build_result, repair_attempts
FROM tool_failures
WHERE error_count >= $1 AND repaired_at IS NULL
ORDER BY error_count DESC
"#,
&[&threshold],
)
.await?;
Ok(rows
.iter()
.map(|row| BrokenTool {
name: row.get("tool_name"),
last_error: row.get("error_message"),
failure_count: row.get::<_, i32>("error_count") as u32,
first_failure: row.get("first_failure"),
last_failure: row.get("last_failure"),
last_build_result: row.get("last_build_result"),
repair_attempts: row.get::<_, i32>("repair_attempts") as u32,
})
.collect())
}
/// Mark a tool as repaired.
pub async fn mark_tool_repaired(&self, tool_name: &str) -> Result<(), DatabaseError> {
let conn = self.conn().await?;
conn.execute(
"UPDATE tool_failures SET repaired_at = NOW(), error_count = 0 WHERE tool_name = $1",
&[&tool_name],
)
.await?;
Ok(())
}
/// Increment repair attempts for a tool.
pub async fn increment_repair_attempts(&self, tool_name: &str) -> Result<(), DatabaseError> {
let conn = self.conn().await?;
conn.execute(
"UPDATE tool_failures SET repair_attempts = repair_attempts + 1 WHERE tool_name = $1",
&[&tool_name],
)
.await?;
Ok(())
}
}