59 lines
2.6 KiB
Python
59 lines
2.6 KiB
Python
"""Diagnostic work snapshot for a Desktop-pooled ``hermes serve`` child.
|
|
|
|
Reads process admission, session/worker, delegation, cron and human-input ledgers. Unreadable
|
|
state is indeterminate, never idle. This snapshot alone is NOT permission to retire: only
|
|
``backend_retirement.RetirementFence.prepare`` freezes admission before taking the snapshot,
|
|
and only a confirmed commit permits the client to stop that exact backend generation.
|
|
The separate messaging gateway process owns its own lifecycle and drain protocol.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Callable, Optional
|
|
|
|
from hermes_cli.web_server_idle_exit import busy_ledger
|
|
|
|
_log = logging.getLogger(__name__)
|
|
|
|
_input_probe_failure_logged = False
|
|
|
|
|
|
def pending_human_input() -> Optional[int]:
|
|
"""Count of prompts waiting on a human (clarify/approval/sudo/secret requests plus queued
|
|
gateway approvals); ``None`` when a ledger cannot be read."""
|
|
global _input_probe_failure_logged
|
|
try:
|
|
from tui_gateway import server_requests
|
|
from tools.approval import pending_gateway_approval_count
|
|
|
|
return server_requests.open_request_count() + pending_gateway_approval_count()
|
|
except Exception:
|
|
if not _input_probe_failure_logged:
|
|
_input_probe_failure_logged = True
|
|
_log.warning("idle-proof input probe unavailable; this backend will report indeterminate",
|
|
exc_info=True)
|
|
return None
|
|
|
|
|
|
def idle_proof(turn_probe: Callable[[], bool | str | None] = busy_ledger,
|
|
input_probe: Callable[[], Optional[int]] = pending_human_input) -> dict:
|
|
"""``{"idle": True | False | None, "reason": str | None}`` plus ``"detail"`` naming the busy ledger.
|
|
|
|
``True`` only when no turn is in flight (session table AND cron ledger) and nothing is waiting
|
|
on a human. ``None`` whenever either probe is indeterminate — the caller treats it exactly like
|
|
busy. ``turn_probe`` may answer with a bool or with :func:`busy_ledger`'s name; the name is
|
|
surfaced as ``detail`` so a backend that refuses to retire says which work it is protecting.
|
|
"""
|
|
turn = turn_probe()
|
|
if turn is None:
|
|
return {"idle": None, "reason": "turn_probe_unavailable"}
|
|
if turn:
|
|
return {"idle": False, "reason": "turn_in_flight", "detail": turn if isinstance(turn, str) else None}
|
|
pending = input_probe()
|
|
if pending is None:
|
|
return {"idle": None, "reason": "input_probe_unavailable"}
|
|
if pending:
|
|
return {"idle": False, "reason": "awaiting_human_input"}
|
|
return {"idle": True, "reason": None}
|