Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion nanobot/heartbeat/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,25 @@ def _read_heartbeat_file(self) -> str | None:
return None
return None

@staticmethod
def _has_active_tasks(content: str) -> bool:
"""Return True only if there are non-empty, non-comment lines under ## Active Tasks."""
in_active = False
for line in content.splitlines():
stripped = line.strip()
if stripped.startswith("## "):
in_active = stripped == "## Active Tasks"
continue
if not in_active:
continue
if not stripped:
continue
if stripped.startswith("<!--") and stripped.endswith("-->"):
continue
# Real task content found
return True
return False

async def _decide(self, content: str) -> tuple[str, str]:
"""Phase 1: ask LLM to decide skip/run via virtual tool call.

Expand Down Expand Up @@ -144,6 +163,10 @@ async def _tick(self) -> None:
logger.debug("Heartbeat: HEARTBEAT.md missing or empty")
return

if not self._has_active_tasks(content):
logger.debug("Heartbeat: no active tasks, skipping LLM check")
return

logger.info("Heartbeat: checking for tasks...")

try:
Expand All @@ -165,7 +188,7 @@ async def _tick(self) -> None:
async def trigger_now(self) -> str | None:
"""Manually trigger a heartbeat."""
content = self._read_heartbeat_file()
if not content:
if not content or not self._has_active_tasks(content):
return None
action, tasks = await self._decide(content)
if action != "run" or not self.on_execute:
Expand Down