From 29dd62149813ecda1eb4e396b774149258cdd7b4 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Fri, 24 Jul 2026 10:05:05 -0500 Subject: [PATCH] fix(tui_gateway): bind the branched agent to the parent profile's home + state.db MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit session.branch wrote the child ROW into the parent's profile db but built the live agent with the launch defaults: _make_agent fell back to _get_db() and no HERMES_HOME override was active. The branched agent's own message flushes — and any later compression rotation it performed — therefore landed back on the launch profile, splitting the lineage one turn after the branch. Mirror session.create/resume: open the parent profile's SessionDB for the agent and hold the home override across the build, so config/skills/memory resolve to the profile too. Spotted in #70605's sibling implementation of the same fix. Co-authored-by: HexLab98 --- tests/test_tui_gateway_server.py | 15 ++++++--- tui_gateway/server.py | 53 ++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/tests/test_tui_gateway_server.py b/tests/test_tui_gateway_server.py index ca03dceb3958..044be273fc17 100644 --- a/tests/test_tui_gateway_server.py +++ b/tests/test_tui_gateway_server.py @@ -9151,11 +9151,12 @@ def test_session_branch_writes_to_parent_profile_db(monkeypatch, tmp_path): monkeypatch.setattr(server, "_get_db", lambda: LaunchDB()) monkeypatch.setattr("hermes_state.SessionDB", ProfileDB) monkeypatch.setattr(server, "_claim_active_session_slot", lambda *a, **k: (None, None)) - monkeypatch.setattr( - server, - "_make_agent", - lambda *a, **k: FakeAgent(), - ) + + def _fake_make_agent(*a, **k): + seen["agent_session_db"] = k.get("session_db") + return FakeAgent() + + monkeypatch.setattr(server, "_make_agent", _fake_make_agent) monkeypatch.setattr(server, "_set_session_context", lambda *a, **k: {}) monkeypatch.setattr(server, "_clear_session_context", lambda *a, **k: None) monkeypatch.setattr(server, "_resolve_model", lambda: "test-model") @@ -9182,6 +9183,10 @@ def test_session_branch_writes_to_parent_profile_db(monkeypatch, tmp_path): assert seen.get("launch_create") is None child_sid = resp["result"]["session_id"] assert server._sessions[child_sid]["profile_home"] == str(profile_home) + # The branched AGENT must be bound to the parent profile's state.db — + # 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) finally: for k in list(server._sessions): server._sessions.pop(k, None) diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 6776606d5e60..09749e0a47ca 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -9734,27 +9734,48 @@ def _(rid, params: dict) -> dict: lease.release() return _err(rid, 5008, f"branch failed: {e}") try: - tokens = _set_session_context(new_key) + # Bind the branched AGENT to the parent's profile, mirroring + # session.create/resume: home override so config/skills/memory resolve + # to the profile during the build, and the profile's own state.db + # handle so the live agent's message flushes — and any later + # compression rotation — persist there. Writing only the row to the + # parent's db while the agent stayed on the launch handle would + # recreate the cross-profile split one turn later. + parent_home = session.get("profile_home") + branch_db = None + if parent_home: + from hermes_state import SessionDB + + branch_db = SessionDB(db_path=Path(parent_home) / "state.db") + home_token = ( + set_hermes_home_override(parent_home) if parent_home else None + ) try: - agent = _make_agent( + tokens = _set_session_context(new_key) + try: + agent = _make_agent( + new_sid, + new_key, + session_id=new_key, + session_db=branch_db, + platform_override=source, + ) + finally: + _clear_session_context(tokens) + _init_session( new_sid, new_key, - session_id=new_key, - platform_override=source, + agent, + list(history), + cols=session.get("cols", 80), + cwd=_session_cwd(session), + session_db=branch_db, + source=source, + profile_home=parent_home, ) finally: - _clear_session_context(tokens) - parent_home = session.get("profile_home") - _init_session( - new_sid, - new_key, - agent, - list(history), - cols=session.get("cols", 80), - cwd=_session_cwd(session), - source=source, - profile_home=parent_home, - ) + if home_token is not None: + reset_hermes_home_override(home_token) if new_sid in _sessions: _sessions[new_sid]["active_session_lease"] = lease except Exception as e: