Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions plugins/memory/honcho/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,12 @@ def search_context(
search_query=query,
target=target,
)
# In directional observation mode the observer->target slot
# (e.g. hermes-about-Jesse) is often empty while the data lives
# on the target peer's own self-representation. Mirror the
# get_peer_card fallback so semantic search still returns results.
if not ctx["representation"] and not ctx["card"] and target and target != observer_peer_id:
ctx = self._fetch_peer_context(target, search_query=query)
parts = []
if ctx["representation"]:
parts.append(ctx["representation"])
Expand Down
38 changes: 38 additions & 0 deletions tests/honcho_plugin/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,44 @@ def test_search_context_accepts_explicit_ai_peer_id(self):
search_query="assistant",
)

def test_search_context_falls_back_to_target_self_context_when_directional_empty(self):
# In directional mode the observer->target slot (assistant-about-user)
# is often empty because conclusions are auto-derived onto the target
# peer's own self-representation, not written through the directional
# path. Mirror the get_peer_card fallback: when the directional fetch
# yields no representation and no card, refetch the target peer's own
# self-context so honcho_search still returns results.
mgr, session = self._make_cached_manager()
assistant_peer = MagicMock()
assistant_peer.context.return_value = SimpleNamespace(
representation="", # directional observer->target slot empty
peer_card=[],
)
assistant_peer.representation.return_value = ""
assistant_peer.get_card.return_value = None # directional card slot empty
user_peer = MagicMock()
user_peer.context.return_value = SimpleNamespace(
representation="Robert runs neuralancer",
peer_card=["Location: Melbourne"],
)

def _peer(peer_id: str) -> MagicMock:
return assistant_peer if peer_id == session.assistant_peer_id else user_peer

mgr._get_or_create_peer = MagicMock(side_effect=_peer)

result = mgr.search_context(session.key, "neuralancer")

assert "Robert runs neuralancer" in result
assert "- Location: Melbourne" in result
# Directional fetch attempted first against the user target...
assistant_peer.context.assert_called_once_with(
target=session.user_peer_id,
search_query="neuralancer",
)
# ...then fell back to the target peer's own self-context.
user_peer.context.assert_called_once_with(search_query="neuralancer")

def test_get_prefetch_context_fetches_user_and_ai_from_peer_api(self):
mgr, session = self._make_cached_manager()
user_peer = MagicMock()
Expand Down