fix(honcho): thread logical cwd through agent construction

(cherry picked from commit b1d7207c45311be658592c6ad34ee84634fed0ee)
This commit is contained in:
outpoints
2026-08-23 21:28:09 -07:00
committed by Teknium
parent 231e1c1204
commit 5237cab756
12 changed files with 113 additions and 21 deletions
@@ -5,6 +5,7 @@ import sys
import threading
import time
from pathlib import Path
from types import SimpleNamespace
import pytest
@@ -17,6 +18,47 @@ from tui_gateway.host_supervisor import (
)
def test_agent_build_receives_seeded_workspace_cwd(tmp_path, monkeypatch):
workspace = tmp_path / "workspace"
workspace.mkdir()
monkeypatch.setattr(server, "_sessions", {})
observed: dict[str, str] = {}
def fake_make_agent(*_args, **_kwargs):
observed["cwd"] = _kwargs["cwd_override"]
return SimpleNamespace(session_id="conversation-id")
def fake_init_session(sid, key, agent, history, *, cwd=None, **_kwargs):
server._sessions[sid] = {
"agent": agent,
"session_key": key,
"history": history,
"cwd": cwd,
}
monkeypatch.setattr(server, "_make_agent", fake_make_agent)
monkeypatch.setattr(server, "_transfer_db_to_agent", lambda *_args: False)
monkeypatch.setattr(server, "_init_session", fake_init_session)
host = ComputeHost(stdout=io.StringIO(), heartbeat_secs=0)
try:
host._ensure_server_session(
server,
{
"sid": "ui-session",
"session_key": "conversation-id",
"cwd": str(workspace),
"source": "desktop",
"history": [],
},
)
finally:
host.close()
assert observed["cwd"] == str(workspace)
def _json_lines(out: io.StringIO) -> list[dict]:
frames = []
for line in out.getvalue().splitlines():
@@ -305,7 +305,7 @@ def test_resume_keeps_profile_db_open_after_ownership_transfer(profile_dbs, monk
monkeypatch.setattr("hermes_state_registry.acquire", _factory)
monkeypatch.setattr(server, "_make_agent", _fake_make_agent)
monkeypatch.setattr(server, "_init_session", _fake_init_session)
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, "_stored_session_runtime_overrides", lambda _found: {})
monkeypatch.setattr(server, "_session_info", lambda agent, *a: {"model": "test"})
@@ -352,7 +352,7 @@ def test_resume_drops_half_built_session_when_init_session_raises(
server, "_make_agent", lambda *a, **k: types.SimpleNamespace(model="test")
)
monkeypatch.setattr(server, "_init_session", _fake_init_session)
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, "_stored_session_runtime_overrides", lambda _found: {})
@@ -396,7 +396,7 @@ def test_resume_eager_never_transfers_shared_launch_db(profile_dbs, monkeypatch)
monkeypatch.setattr(server, "_make_agent", _fake_make_agent)
monkeypatch.setattr(server, "_init_session", _fake_init_session)
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, "_stored_session_runtime_overrides", lambda _found: {}
+18 -3
View File
@@ -3760,7 +3760,7 @@ def test_session_resume_follows_compression_tip(monkeypatch, tmp_path):
monkeypatch.setattr(server, "_get_db", lambda: db)
monkeypatch.setattr(server, "_enable_gateway_prompts", lambda: None)
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(
@@ -3821,7 +3821,7 @@ def test_session_resume_passes_stored_runtime_to_agent(monkeypatch):
monkeypatch.setattr(server, "_get_db", lambda: FakeDB())
monkeypatch.setattr(server, "_enable_gateway_prompts", lambda: None)
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, "_session_info", lambda agent, *a: {"model": agent.model, "provider": agent.provider})
@@ -3906,6 +3906,7 @@ def test_session_resume_profile_uses_profile_db_cwd(monkeypatch, tmp_path):
def fake_make_agent(sid, key, session_id=None, session_db=None, **kwargs):
captured["agent_db"] = session_db
captured["agent_cwd"] = kwargs.get("cwd_override")
return types.SimpleNamespace(model="test/model")
monkeypatch.setenv("TERMINAL_CWD", str(launch_cwd))
@@ -3913,7 +3914,11 @@ def test_session_resume_profile_uses_profile_db_cwd(monkeypatch, tmp_path):
monkeypatch.setattr("hermes_state_registry.acquire", lambda db_path=None: profile_db)
monkeypatch.setattr(server, "_get_db", lambda: launch_db)
monkeypatch.setattr(server, "_enable_gateway_prompts", lambda: None)
monkeypatch.setattr(server, "_set_session_context", lambda target: [])
monkeypatch.setattr(
server,
"_set_session_context",
lambda target, cwd=None: captured.setdefault("context_cwd", cwd) or [],
)
monkeypatch.setattr(server, "_clear_session_context", lambda tokens: None)
monkeypatch.setattr(server, "_make_agent", fake_make_agent)
monkeypatch.setattr(server, "_SlashWorker", FakeWorker)
@@ -3944,6 +3949,8 @@ def test_session_resume_profile_uses_profile_db_cwd(monkeypatch, tmp_path):
assert "error" not in resp
sid = resp["result"]["session_id"]
assert captured["agent_db"] is profile_db
assert captured["context_cwd"] == str(profile_cwd)
assert captured["agent_cwd"] == str(profile_cwd)
assert server._sessions[sid]["cwd"] == str(profile_cwd)
assert resp["result"]["info"]["cwd"] == str(profile_cwd)
assert "launch_update" not in captured
@@ -15894,6 +15901,7 @@ def test_session_branch_writes_to_parent_profile_db(monkeypatch, tmp_path):
def _fake_make_agent(*a, **k):
seen["agent_session_db"] = k.get("session_db")
seen["agent_cwd"] = k.get("cwd_override")
return FakeAgent()
monkeypatch.setattr(server, "_make_agent", _fake_make_agent)
@@ -15927,6 +15935,7 @@ def test_session_branch_writes_to_parent_profile_db(monkeypatch, tmp_path):
# not just the row. Otherwise its own flushes (and a later compression
# rotation) land on the launch db, splitting the lineage again.
assert isinstance(seen.get("agent_session_db"), ProfileDB)
assert seen.get("agent_cwd") == str(tmp_path)
finally:
for k in list(server._sessions):
server._sessions.pop(k, None)
@@ -16396,6 +16405,10 @@ def test_session_branch_uses_persisted_display_history_after_compaction(monkeypa
def set_session_title(self, _key, _title):
return True
def set_auto_title(self, _key, _title, *, source="llm"):
seen["title_source"] = source
return True
def get_session(self, key):
return {"id": key, "cwd": str(tmp_path)}
@@ -16450,6 +16463,7 @@ def test_session_branch_uses_persisted_display_history_after_compaction(monkeypa
)
assert "result" in response, response
assert seen.get("title_source") == "derived"
assert [message["content"] for message in seen["msgs"]] == [
"first question",
"first answer",
@@ -21201,6 +21215,7 @@ def test_session_branch_keeps_reasoning_fields(monkeypatch, tmp_path):
)
assert resp.get("result"), f"got error: {resp.get('error')}"
assert db.get_session_title_source("branch-key") == SessionDB.TITLE_SOURCE_DERIVED
assistant = _branched_assistant(db, "branch-key")
assert assistant["reasoning"] == BRANCH_REASONING
assert assistant["reasoning_content"] == BRANCH_REASONING_CONTENT