Sergey попросив research про travel-itinerary skills. Я викликав Agent (subagent) для веб-пошуку — він працював ~35с. У цей час parent-сесійний jsonl не писався (немає нових tool_use/tool_result у мене), тому latest_jsonl_mtime показував idle ~120s.
Watchdog побачив:
Але насправді бот був "alive and working" — просто чекав на subagent.
/srv/services/claude-watchdog/watchdog.py)check_bot_reply_gap() — line 275-355:
REPLY_GAP_THRESHOLD_SEC = 3600 (line 32) — але це нова версія, активний процес стартував до edit'у і працює зі старою (можливо 180s)< 120 → skip (line 302)Я перезапустив сервіс щоб підхопити нову версію (1h threshold) — це знизить частоту false-positives, але не вирішує root cause.
У latest_jsonl_mtime() або новій функції — детектити pending tool_use без matching tool_result в останньому jsonl. Це ознака "бот викликав інструмент і чекає на відповідь" (особливо Agent — субагенти можуть йти 30с-5хв).
def has_pending_tool_use(lookback_lines=200):
"""True якщо останній assistant tool_use не має matching tool_result."""
jsonls = sorted(CLAUDE_SESSIONS_DIR.glob("*.jsonl"), key=lambda p: p.stat().st_mtime)
if not jsonls:
return False
last = jsonls[-1]
pending_ids = set()
with open(last, "r", encoding="utf-8", errors="ignore") as f:
lines = f.readlines()[-lookback_lines:]
for line in lines:
try:
msg = json.loads(line)
content = msg.get("message", {}).get("content", [])
if not isinstance(content, list):
continue
for block in content:
if block.get("type") == "tool_use":
pending_ids.add(block["id"])
elif block.get("type") == "tool_result":
pending_ids.discard(block.get("tool_use_id"))
except Exception:
continue
return bool(pending_ids)
Потім у check_bot_reply_gap (line 302):
if jsonl_idle_sec < 120 or has_pending_tool_use():
return # бот або пише, або чекає на tool — не стук
has_pending_tool_use() у watchdog.pycheck_bot_reply_gap()watchdog.py.bak-20260501-* — можна порівняти що змінювалиsystemctl status claude-watchdog