mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
Every cache keyed on a file's stat signature had the bug the config caches had: a same-size in-place rewrite in one coarse timestamp tick keeps mtime, ctime, size and inode, so the older parse was served for the rest of the process. Move the helper to utils.file_cache_version (file_signature + content digest while the file is under 2 s old; always_digest for signatures compared much later, where the racy digest going empty would read as a change) and key every cache that can hold a secret, token, pairing/allowlist, route or config on it: - auth.py: global auth-store memo, Nous auth-status memo (was mtime only) - gateway/pairing.py approved-users cache (a revocation kept access) - run_profile_reconcile: served-profile signature (always_digest: a false change reconnects adapters) and the hook-consent signature - webhook dynamic routes (route HMAC secrets), Feishu comment pairing/rules cache (mtime only) - MCP OAuth tokens-file reload check (mtime only; always_digest: a reload tears the session down) - config parse-failure record (always_digest), named-custom-provider memo, model aliases, gateway.standalone memo, profile model read, MoA preset, agent raw config, tui_gateway raw config cache, onboarding due stamp, free-tier inventory stamp, provider model-list credential fingerprint, git safe.directory key, skills prompt manifest config_cache_version takes its stat through config_version, so its errors and seams are unchanged. Left alone: the tool-definition cache key and the platform manifest stamp (a safe worker must never open config.yaml or a plugin file; the MoA preset cache is skipped in one instead) and the CLI MCP watcher seed (pinned to file_signature by a main test).
46 lines
1.8 KiB
Python
46 lines
1.8 KiB
Python
"""Heal mixed ``sys.modules`` after an in-place checkout update.
|
|
|
|
Pre-reexec updaters (Hermes ≤ v2026.9.14) purged only package prefixes
|
|
(``hermes_cli``, ``gateway``, ``tools``, ``tui_gateway``, ``agent``) and left
|
|
root modules like ``utils`` cached in the updater process. The post-pull
|
|
gateway-restart phase then imports new ``hermes_cli.gateway`` /
|
|
``hermes_cli.config`` into that process; those need symbols the stale
|
|
``utils`` lacks (``file_signature``), and ``hermes update`` exits 1 with
|
|
``gateway auto-restart failed: cannot import name 'file_signature' from
|
|
'utils'``.
|
|
|
|
Post-swap hand-off (``hermes_cli.update_handoff``) makes this class dead for
|
|
updaters that already include it. This module is the bridge for the one
|
|
upgrade from a pre-handoff release onto a tree that needs new root symbols:
|
|
freshly imported ``hermes_cli`` code drops the incomplete cache before
|
|
importing ``utils``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from typing import Mapping, Sequence
|
|
|
|
# Root modules the narrow purge left behind, keyed by attributes that must
|
|
# exist on the on-disk copy after this release. Extend when a new root-level
|
|
# symbol would otherwise break the pre-handoff upgrade path.
|
|
_ROOT_MODULE_REQUIRED_ATTRS: dict[str, tuple[str, ...]] = {
|
|
"utils": ("file_signature", "file_cache_version"),
|
|
}
|
|
|
|
|
|
def drop_stale_root_modules(
|
|
required: Mapping[str, Sequence[str]] | None = None,
|
|
) -> list[str]:
|
|
"""Drop cached root modules missing required attrs. Returns dropped names."""
|
|
checks = _ROOT_MODULE_REQUIRED_ATTRS if required is None else required
|
|
dropped: list[str] = []
|
|
for name, attrs in checks.items():
|
|
mod = sys.modules.get(name)
|
|
if mod is None:
|
|
continue
|
|
if any(not hasattr(mod, attr) for attr in attrs):
|
|
sys.modules.pop(name, None)
|
|
dropped.append(name)
|
|
return dropped
|