fix(honcho): preserve deferred routing invariants
Thread logical session cwd through deferred Desktop/TUI builds, normalize absent cwd during construction, and share title provenance constants between SessionDB and Honcho. (cherry picked from commit 2693f4f27c776ac819d92c9b52e8a03ad2a985d8)
This commit is contained in:
@@ -8,6 +8,8 @@ import json
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from agent.memory_provider import MemoryProvider
|
||||
from agent.memory_manager import MemoryManager
|
||||
|
||||
@@ -108,13 +110,13 @@ class TestMemoryManagerUserIdThreading:
|
||||
session_db.get_session_title_source.return_value = "llm"
|
||||
|
||||
with patch(
|
||||
"run_agent.get_tool_definitions",
|
||||
"model_tools.get_tool_definitions",
|
||||
return_value=[],
|
||||
), patch(
|
||||
"run_agent.check_toolset_requirements",
|
||||
"model_tools.check_toolset_requirements",
|
||||
return_value={},
|
||||
), patch(
|
||||
"run_agent.OpenAI",
|
||||
"agent.process_bootstrap.OpenAI",
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"memory": {"provider": "recording"}},
|
||||
@@ -143,6 +145,42 @@ class TestMemoryManagerUserIdThreading:
|
||||
assert provider._init_kwargs["cwd"] == str(tmp_path)
|
||||
agent.close()
|
||||
|
||||
@pytest.mark.parametrize("cwd", [None, ""])
|
||||
def test_cwdless_agent_still_initializes_memory_provider(self, cwd):
|
||||
from run_agent import AIAgent
|
||||
|
||||
provider = RecordingProvider()
|
||||
|
||||
with patch(
|
||||
"model_tools.get_tool_definitions",
|
||||
return_value=[],
|
||||
), patch(
|
||||
"model_tools.check_toolset_requirements",
|
||||
return_value={},
|
||||
), patch(
|
||||
"agent.process_bootstrap.OpenAI",
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"memory": {"provider": "recording"}},
|
||||
), patch(
|
||||
"plugins.memory.load_memory_provider",
|
||||
return_value=provider,
|
||||
):
|
||||
agent = AIAgent(
|
||||
api_key="test-key-1234567890",
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
quiet_mode=True,
|
||||
skip_context_files=True,
|
||||
platform="cli",
|
||||
session_id="session-without-cwd",
|
||||
cwd=cwd,
|
||||
)
|
||||
|
||||
assert agent.session_cwd is None
|
||||
assert provider._init_session_id == "session-without-cwd"
|
||||
assert "cwd" not in provider._init_kwargs
|
||||
agent.close()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mem0 provider user_id tests
|
||||
|
||||
@@ -12,6 +12,9 @@ from hermes_cli.profiles import _get_default_hermes_home
|
||||
|
||||
import pytest
|
||||
|
||||
import hermes_state_common
|
||||
from hermes_state import SessionDB
|
||||
from plugins.memory.honcho import client as honcho_client
|
||||
from plugins.memory.honcho.client import (
|
||||
HonchoClientConfig,
|
||||
get_honcho_client,
|
||||
@@ -24,6 +27,19 @@ from plugins.memory.honcho.client import (
|
||||
|
||||
|
||||
class TestHonchoClientConfigDefaults:
|
||||
def test_automatic_title_sources_share_state_store_vocabulary(self):
|
||||
automatic_sources = frozenset(
|
||||
{
|
||||
hermes_state_common.TITLE_SOURCE_DERIVED,
|
||||
hermes_state_common.TITLE_SOURCE_LLM,
|
||||
}
|
||||
)
|
||||
|
||||
assert honcho_client._AUTOMATIC_SESSION_TITLE_SOURCES == automatic_sources
|
||||
assert SessionDB.TITLE_SOURCE_DERIVED == hermes_state_common.TITLE_SOURCE_DERIVED
|
||||
assert SessionDB.TITLE_SOURCE_LLM == hermes_state_common.TITLE_SOURCE_LLM
|
||||
assert SessionDB.TITLE_SOURCE_USER == hermes_state_common.TITLE_SOURCE_USER
|
||||
|
||||
def test_default_values(self):
|
||||
config = HonchoClientConfig()
|
||||
assert config.host == "hermes"
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Regression coverage for logical cwd propagation in deferred TUI/Desktop builds."""
|
||||
|
||||
import threading
|
||||
import uuid
|
||||
from types import SimpleNamespace
|
||||
|
||||
from tui_gateway import server
|
||||
|
||||
|
||||
def test_deferred_agent_build_threads_session_cwd(monkeypatch, tmp_path):
|
||||
"""A cold Desktop build must not fall back to the serve process cwd."""
|
||||
workspace = tmp_path / "workspace"
|
||||
workspace.mkdir()
|
||||
captured = {}
|
||||
built = threading.Event()
|
||||
ready = threading.Event()
|
||||
sid = f"cwd-build-{uuid.uuid4().hex[:8]}"
|
||||
session = {
|
||||
"agent_ready": ready,
|
||||
"session_key": f"cwd-key-{uuid.uuid4().hex[:8]}",
|
||||
"cwd": str(workspace),
|
||||
}
|
||||
|
||||
def fake_set_session_context(key, cwd=None):
|
||||
captured["context_key"] = key
|
||||
captured["context_cwd"] = cwd
|
||||
return []
|
||||
|
||||
def fake_make_agent(*args, **kwargs):
|
||||
captured["agent_cwd"] = kwargs.get("cwd_override")
|
||||
built.set()
|
||||
return SimpleNamespace(model="test", session_id=session["session_key"])
|
||||
|
||||
monkeypatch.setattr(server, "_set_session_context", fake_set_session_context)
|
||||
monkeypatch.setattr(server, "_clear_session_context", lambda _tokens: None)
|
||||
monkeypatch.setattr(server, "_make_agent", fake_make_agent)
|
||||
monkeypatch.setattr(
|
||||
"tui_gateway.entry.ensure_mcp_discovery_started", lambda: None
|
||||
)
|
||||
monkeypatch.setattr(server, "_wire_callbacks", lambda _sid: None)
|
||||
monkeypatch.setattr(server, "_config_model_target", lambda: ("", ""))
|
||||
monkeypatch.setattr(server, "_start_notification_poller", lambda *a, **k: None)
|
||||
monkeypatch.setattr(server, "_schedule_mcp_late_refresh", lambda *a, **k: None)
|
||||
monkeypatch.setattr(server, "_emit", lambda *a, **k: None)
|
||||
monkeypatch.setattr(server, "_notify_session_boundary", lambda *a, **k: None)
|
||||
|
||||
server._sessions[sid] = session
|
||||
try:
|
||||
server._start_agent_build(sid, session)
|
||||
assert built.wait(timeout=15), "agent build thread never called _make_agent"
|
||||
assert ready.wait(timeout=5), "agent_ready never set after build"
|
||||
finally:
|
||||
server._sessions.pop(sid, None)
|
||||
from tools.approval import unregister_gateway_notify
|
||||
|
||||
unregister_gateway_notify(session["session_key"])
|
||||
|
||||
assert captured["context_cwd"] == str(workspace)
|
||||
assert captured["agent_cwd"] == str(workspace)
|
||||
@@ -19682,7 +19682,9 @@ def test_start_agent_build_passes_session_model_override(
|
||||
captured.update(kwargs)
|
||||
return types.SimpleNamespace(model="claude-sonnet-4.6")
|
||||
|
||||
monkeypatch.setattr(server, "_set_session_context", lambda target: [])
|
||||
monkeypatch.setattr(
|
||||
server, "_set_session_context", lambda target, cwd=None: []
|
||||
)
|
||||
monkeypatch.setattr(server, "_clear_session_context", lambda tokens: None)
|
||||
monkeypatch.setattr(server, "_make_agent", fake_make_agent)
|
||||
monkeypatch.setattr(server, "_SlashWorker", FakeWorker)
|
||||
|
||||
Reference in New Issue
Block a user