mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
main's code-health ratchet flags 19 BLE001 / 5 S110 on the PR diff in these paths.
Narrowed where the raisable set is known:
- backup_restore._restore_db_pages: the optional pre-restore WAL checkpoint and the pre-fallback
close catch sqlite3.Error and log at debug; the mode carry-over catches OSError and warns (the
data is restored); the backup()-path and fallback handlers catch sqlite3.Error/OSError/ValueError.
Best-effort semantics are unchanged.
- cli_auto_maintenance one-time repairs: sqlite3.Error/OSError plus the SessionDB refusal family
(RuntimeStoreError is a ValueError; hermes_state_errors busy/replaced are RuntimeErrors).
- gateway_chat_view._write_usage_file: transport/refusal errors and a malformed receipt, logged at
debug instead of a bare pass; the ledger is still written from the outcome.
Deliberate catch-all boundaries keep catching everything but now log the traceback
(exc_info): ACP event projection (must wake every waiter), ACP permission viewer detach,
bot_relay.deliver runtime_unavailable refusal, slash-catalog loaders (one bad source must not
blank the palette), gateway service setup and post-import revive, web lifespan shutdown of the
managed local runtime. web_server_app's module logger is renamed _log -> logger (same
"hermes_cli.web_server" logger) so its existing exc_info boundaries are recognised.
worktree_ops: adopt main's own replacement of the summary try/except/pass (3ca97d5e59).
(cherry picked from commit ee38ac1c4eca7b3018141ee93967288c8d4c90f8)
82 lines
3.6 KiB
Python
82 lines
3.6 KiB
Python
"""Startup auto-maintenance for the classic CLI: state-db pack/vacuum and checkpoint pruning run off the prompt thread.
|
|
|
|
Split out of ``cli.py``; ``cli`` re-exports every public name and moved bodies late-bind
|
|
cli-level names through ``from cli import ...`` at call time so facade monkeypatch seams hold.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import sqlite3
|
|
import threading
|
|
|
|
# Log-record parity with the origin module.
|
|
logger = logging.getLogger("cli")
|
|
|
|
|
|
def _run_state_db_auto_maintenance(session_db) -> None:
|
|
"""One-time repairs + auto-archive/prune/vacuum per the ``sessions:`` config. Never raises."""
|
|
if session_db is None:
|
|
return
|
|
try:
|
|
from hermes_cli.config import load_config as _load_full_config
|
|
from hermes_constants import get_hermes_home as _get_hermes_home # lazy: tests patch it
|
|
_hermes_home_maint = _get_hermes_home()
|
|
|
|
# One-time repairs, each latched in state_meta once it has run.
|
|
for meta_key, repair, done_msg, skip_msg in (
|
|
(
|
|
"ghost_session_prune_v1",
|
|
lambda: session_db.prune_empty_ghost_sessions(sessions_dir=_hermes_home_maint / "sessions"),
|
|
"Pruned %d empty TUI ghost sessions", "Ghost session prune skipped: %s",
|
|
),
|
|
(
|
|
"orphaned_compression_finalize_v1",
|
|
session_db.finalize_orphaned_compression_sessions,
|
|
"Finalized %d orphaned compression sessions", "Orphan compression finalize skipped: %s",
|
|
),
|
|
):
|
|
if session_db.get_meta(meta_key):
|
|
continue
|
|
try:
|
|
count = repair()
|
|
# Every SessionDB refusal: sqlite/IO, RuntimeStoreError (ValueError), and the
|
|
# hermes_state_errors busy/replaced family (RuntimeError).
|
|
except (sqlite3.Error, OSError, ValueError, RuntimeError) as _exc:
|
|
# Latched below regardless: a repair this store refuses (live ledger work,
|
|
# locked file) must surface once, not retry silently on every start.
|
|
logger.warning(skip_msg, _exc)
|
|
count = 0
|
|
session_db.set_meta(meta_key, "1")
|
|
if count:
|
|
logger.info(done_msg, count)
|
|
|
|
cfg = (_load_full_config().get("sessions") or {})
|
|
|
|
# Auto-archive is independent of auto_prune: run it before prune's early return.
|
|
if cfg.get("auto_archive", False):
|
|
session_db.maybe_auto_archive(
|
|
idle_days=float(cfg.get("auto_archive_days", 3)),
|
|
min_interval_hours=int(cfg.get("min_interval_hours", 24)),
|
|
)
|
|
|
|
if not cfg.get("auto_prune", False):
|
|
return
|
|
session_db.maybe_auto_prune_and_vacuum(
|
|
retention_days=int(cfg.get("retention_days", 90)),
|
|
min_interval_hours=int(cfg.get("min_interval_hours", 24)),
|
|
min_vacuum_interval_days=int(cfg.get("min_vacuum_interval_days", 30)),
|
|
vacuum=bool(cfg.get("vacuum_after_prune", True)),
|
|
sessions_dir=_hermes_home_maint / "sessions",
|
|
)
|
|
except Exception as exc:
|
|
logger.debug("state.db auto-maintenance skipped: %s", exc)
|
|
|
|
|
|
def _run_checkpoint_auto_maintenance() -> None:
|
|
"""Checkpoint store retention on a daemon thread: its ``git gc`` can block for tens of seconds
|
|
on a large store, which used to stall the prompt once a day. ``auto_prune_from_config`` owns the
|
|
config gate and the 24h marker and never raises."""
|
|
from tools.checkpoint_maintenance import auto_prune_from_config
|
|
threading.Thread(target=auto_prune_from_config, name="checkpoint-auto-prune", daemon=True).start()
|