From 021ab58a23a680049d453d8c8715ac92ad75b891 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:07:23 -0700 Subject: [PATCH] fix: hindsight update-time dep resolution survives a BOM in config.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_provider_pip_dependencies` still read ~/.hermes/hindsight/config.json with strict utf-8 inside a bare `except Exception`, so a Windows-editor BOM made the `mode` lookup silently fail and `hermes update` reinstalled only `hindsight-client`, leaving the embedded daemon broken — the exact #70636 symptom this helper exists to prevent. Route it through the shared `read_json_or_empty` (utf-8-sig, {} on missing/corrupt) like the other memory readers in this PR, and add the reader to the parametrized BOM invariant. --- hermes_cli/memory_setup.py | 15 +++++-------- .../memory/test_bom_tolerant_config_reads.py | 22 ++++++++++++++----- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/hermes_cli/memory_setup.py b/hermes_cli/memory_setup.py index 07f4d52666..a7aab868c4 100644 --- a/hermes_cli/memory_setup.py +++ b/hermes_cli/memory_setup.py @@ -32,15 +32,12 @@ def _provider_pip_dependencies(provider_name: str, declared: list) -> list: """ deps = list(declared or []) if provider_name == "hindsight": - try: - import json - cfg_path = get_hermes_home() / "hindsight" / "config.json" - cfg = json.loads(cfg_path.read_text(encoding="utf-8")) if cfg_path.exists() else {} - # "local" is a legacy alias for "local_embedded" - if cfg.get("mode", "") in {"local", "local_embedded"}: - deps.append("hindsight-all") - except Exception: - pass + from utils import read_json_or_empty # BOM-tolerant; {} on missing/corrupt + + cfg = read_json_or_empty(get_hermes_home() / "hindsight" / "config.json") + # "local" is a legacy alias for "local_embedded" + if cfg.get("mode", "") in {"local", "local_embedded"}: + deps.append("hindsight-all") return deps diff --git a/tests/plugins/memory/test_bom_tolerant_config_reads.py b/tests/plugins/memory/test_bom_tolerant_config_reads.py index cced09860c..93925a4fd8 100644 --- a/tests/plugins/memory/test_bom_tolerant_config_reads.py +++ b/tests/plugins/memory/test_bom_tolerant_config_reads.py @@ -19,17 +19,24 @@ def _write_bom_json(path: Path, payload: dict) -> Path: return path -def _via_shared_reader(p: Path) -> dict: +def _via_shared_reader(p: Path, monkeypatch) -> dict: from utils import read_json_or_empty # mem0 / hindsight / honcho CLI all read through this return read_json_or_empty(p) -def _via_supermemory(p: Path) -> dict: +def _via_supermemory(p: Path, monkeypatch) -> dict: from plugins.memory.supermemory import _load_supermemory_config return _load_supermemory_config(str(p.parent)) -def _via_honcho_client(p: Path) -> dict: +def _via_hindsight_runtime_deps(p: Path, monkeypatch) -> dict: + import hermes_cli.memory_setup as ms + monkeypatch.setattr(ms, "get_hermes_home", lambda: p.parent.parent) # /hindsight/config.json + deps = ms._provider_pip_dependencies("hindsight", ["hindsight-client"]) + return {"workspace": "bom-ws" if "hindsight-all" in deps else None, "container_tag": None} + + +def _via_honcho_client(p: Path, monkeypatch) -> dict: from plugins.memory.honcho.client import HonchoClientConfig cfg = HonchoClientConfig.from_global_config(config_path=p) return {"workspace": cfg.workspace_id, "container_tag": None} @@ -39,10 +46,13 @@ def _via_honcho_client(p: Path) -> dict: ("mem0.json", _via_shared_reader), ("supermemory.json", _via_supermemory), ("honcho.json", _via_honcho_client), + ("hindsight/config.json", _via_hindsight_runtime_deps), ]) -def test_plugin_config_json_tolerates_bom(tmp_path, filename, loader): - _write_bom_json(tmp_path / filename, {"workspace": "bom-ws", "container_tag": "bom-tag", "enabled": True}) - loaded = loader(tmp_path / filename) +def test_plugin_config_json_tolerates_bom(tmp_path, monkeypatch, filename, loader): + target = tmp_path / filename + target.parent.mkdir(parents=True, exist_ok=True) + _write_bom_json(target, {"workspace": "bom-ws", "container_tag": "bom-tag", "enabled": True, "mode": "local_embedded"}) + loaded = loader(target, monkeypatch) assert "bom" in str(loaded.get("workspace") or loaded.get("container_tag"))