diff --git a/plugins/memory/honcho/__init__.py b/plugins/memory/honcho/__init__.py index 4b11f65bb1..daada37284 100644 --- a/plugins/memory/honcho/__init__.py +++ b/plugins/memory/honcho/__init__.py @@ -563,9 +563,9 @@ class HonchoMemoryProvider(DialecticMixin, MemoryProvider): if self._recall_sync: from plugins.memory.honcho.recall_sync import prefetch_sync - notice = self._pop_auth_notice() - result = prefetch_sync(self, query) - return "\n\n".join(part for part in (notice, result) if part) + notice = self._pop_auth_notice() or self._pop_peer_notice() + payload = "\n\n".join(part for part in (notice, prefetch_sync(self, query)) if part) + return self._log_injection("injected" if payload else "recall-sync-empty", payload) first_turn_base_deadline = (time.monotonic() + self._first_turn_wait(self._FIRST_TURN_BASE_TIMEOUT) if self._turn_count <= 1 else None) diff --git a/plugins/memory/honcho/recall_sync.py b/plugins/memory/honcho/recall_sync.py index 297b3f1505..d31956c4e9 100644 --- a/plugins/memory/honcho/recall_sync.py +++ b/plugins/memory/honcho/recall_sync.py @@ -34,7 +34,7 @@ def prefetch_sync(provider, query: str) -> str: if provider._init_thread is not None: provider._init_thread.join(timeout=max(0.0, deadline - time.monotonic())) if not provider._session_ready(): - return provider._pop_auth_notice() + return provider._pop_auth_notice() or provider._pop_peer_notice() manager = provider._manager if (generation is not provider._recall_generation or session != provider._session_key or turn != provider._turn_count or time.monotonic() >= deadline): diff --git a/tests/honcho_plugin/test_recall_sync.py b/tests/honcho_plugin/test_recall_sync.py index 5586095bff..03f3befeec 100644 --- a/tests/honcho_plugin/test_recall_sync.py +++ b/tests/honcho_plugin/test_recall_sync.py @@ -1,4 +1,5 @@ """Opt-in current-query alignment and bounded late-worker isolation.""" +import json import threading import time @@ -127,3 +128,27 @@ def test_timeout_keeps_single_flight_and_late_result_cannot_publish(): provider._manager.get_prefetch_context = superseded assert provider.prefetch("Plan the garden") == "" assert provider._last_context_turn == provider._last_dialectic_turn == -999 + + +def test_missing_peer_notice_surfaces_once(): + provider = HonchoMemoryProvider() + provider._config = HonchoClientConfig(timeout=0.05) + provider._recall_sync = True + provider._init_peer_failure = "No runtime identity or peerName" + first = provider.prefetch("What did we decide about the schema?") + assert "Honcho memory is off" in first + assert "hermes honcho peer --user" in first + assert provider.prefetch("And the index?") == "" + + +def test_prefetch_writes_the_injection_log(tmp_path): + provider = make_provider() + provider._injection_log_path = str(tmp_path / "injection.log") + provider.on_turn_start(1, "Plan the garden") + result = provider.prefetch("Plan the garden") + assert "base:Plan the garden" in result + assert provider.prefetch("Plan the garden") == "" + records = [json.loads(line) for line in (tmp_path / "injection.log").read_text().splitlines()] + assert [r["reason"] for r in records] == ["injected", "recall-sync-empty"] + assert records[0]["payload"] == result and records[0]["turn"] == 1 + assert records[1]["payload"] == "" and records[1]["bytes"] == 0