"""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 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", ), ): try: if not session_db.get_meta(meta_key): count = repair() session_db.set_meta(meta_key, "1") if count: logger.info(done_msg, count) except Exception as _exc: logger.debug(skip_msg, _exc) 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_manager import auto_prune_from_config threading.Thread(target=auto_prune_from_config, name="checkpoint-auto-prune", daemon=True).start()