diff --git a/plugins/memory/honcho/session.py b/plugins/memory/honcho/session.py index 2cd4c5bd2f516..ce7d758203fff 100644 --- a/plugins/memory/honcho/session.py +++ b/plugins/memory/honcho/session.py @@ -880,14 +880,28 @@ def _fetch_peer_card(self, peer_id: str) -> list[str]: return [] - def _fetch_peer_context(self, peer_id: str, search_query: str | None = None) -> dict[str, Any]: - """Fetch representation + peer card directly from a peer object.""" + def _fetch_peer_context( + self, + peer_id: str, + search_query: str | None = None, + target_id: str | None = None, + ) -> dict[str, Any]: + """Fetch representation + peer card directly from a peer object. + + When ``target_id`` is provided, fetch context from the observer peer's + perspective about that target peer (observer -> observed). + """ peer = self._get_or_create_peer(peer_id) representation = "" card: list[str] = [] try: - ctx = peer.context(search_query=search_query) if search_query else peer.context() + kwargs: dict[str, Any] = {} + if target_id: + kwargs["target"] = target_id + if search_query: + kwargs["search_query"] = search_query + ctx = peer.context(**kwargs) if kwargs else peer.context() representation = ( getattr(ctx, "representation", None) or getattr(ctx, "peer_representation", None) @@ -899,7 +913,15 @@ def _fetch_peer_context(self, peer_id: str, search_query: str | None = None) -> if not representation: try: - representation = peer.representation() or "" + if target_id: + ctx = peer.context(target=target_id) + representation = ( + getattr(ctx, "representation", None) + or getattr(ctx, "peer_representation", None) + or "" + ) + else: + representation = peer.representation() or "" except Exception as e: logger.debug("Direct peer.representation() failed for '%s': %s", peer_id, e) @@ -950,7 +972,21 @@ def search_context(self, session_key: str, query: str, max_tokens: int = 800) -> return "" try: - ctx = self._fetch_peer_context(session.user_peer_id, search_query=query) + # Align retrieval direction with how conclusions are stored. + # - directional mode (default): assistant observes user + # - unified mode: user self-observes + if self._ai_observe_others: + observer_peer_id = session.assistant_peer_id + target_id = session.user_peer_id + else: + observer_peer_id = session.user_peer_id + target_id = None + + ctx = self._fetch_peer_context( + observer_peer_id, + search_query=query, + target_id=target_id, + ) parts = [] if ctx["representation"]: parts.append(ctx["representation"]) diff --git a/tests/honcho_plugin/test_session.py b/tests/honcho_plugin/test_session.py index e3452cf6cb8d9..53a4ad16a941e 100644 --- a/tests/honcho_plugin/test_session.py +++ b/tests/honcho_plugin/test_session.py @@ -212,20 +212,38 @@ def test_get_peer_card_uses_direct_peer_lookup(self): assert mgr.get_peer_card(session.key) == ["Name: Robert"] user_peer.get_card.assert_called_once_with() - def test_search_context_uses_peer_context_response(self): + def test_search_context_uses_assistant_perspective_with_target(self): mgr, session = self._make_cached_manager() - user_peer = MagicMock() - user_peer.context.return_value = SimpleNamespace( + assistant_peer = MagicMock() + assistant_peer.context.return_value = SimpleNamespace( representation="Robert runs neuralancer", peer_card=["Location: Melbourne"], ) - mgr._get_or_create_peer = MagicMock(return_value=user_peer) + mgr._get_or_create_peer = MagicMock(return_value=assistant_peer) result = mgr.search_context(session.key, "neuralancer") assert "Robert runs neuralancer" in result assert "- Location: Melbourne" in result - user_peer.context.assert_called_once_with(search_query="neuralancer") + assistant_peer.context.assert_called_once_with( + target="robert", + search_query="neuralancer", + ) + + def test_search_context_unified_mode_uses_user_self_context(self): + mgr, session = self._make_cached_manager() + mgr._ai_observe_others = False + user_peer = MagicMock() + user_peer.context.return_value = SimpleNamespace( + representation="Unified self context", + peer_card=["Name: Robert"], + ) + mgr._get_or_create_peer = MagicMock(return_value=user_peer) + + result = mgr.search_context(session.key, "self") + + assert "Unified self context" in result + user_peer.context.assert_called_once_with(search_query="self") def test_get_prefetch_context_fetches_user_and_ai_from_peer_api(self): mgr, session = self._make_cached_manager()