fix(tui_gateway): honor prefill_messages_file on the Desktop agent path

Desktop and TUI agents are built by tui_gateway.server._make_agent, which
never runs the classic CLI bootstrap and passed no prefill_messages to
AIAgent, so a configured prefill_messages_file was silently ignored there.

Resolve it with the CLI's own resolver/loader (env > prefill_messages_file >
legacy agent.prefill_messages_file). The loader takes an optional base_dir so
relative paths resolve against the profile building the agent, not the
launch home.

Co-authored-by: leakedd <66937184+leakedd@users.noreply.github.com>
This commit is contained in:
Hermes Agent
2026-09-24 23:10:29 -05:00
committed by brooklyn!
parent b148d97602
commit 4503c2458b
4 changed files with 71 additions and 5 deletions

View File

@@ -24,14 +24,16 @@ def _cli():
return cli
def _load_prefill_messages(file_path: str) -> List[Dict[str, Any]]:
"""Load prefill messages (JSON array) from *file_path*; relative to ~/.hermes/; missing/empty -> []."""
from cli import _hermes_home
def _load_prefill_messages(file_path: str, base_dir: Path | None = None) -> List[Dict[str, Any]]:
"""Load prefill messages (JSON array) from *file_path*; missing/empty -> [].
Relative paths resolve against *base_dir*, defaulting to the CLI's hermes home.
"""
if not file_path:
return []
path = Path(file_path).expanduser()
if not path.is_absolute():
path = _hermes_home / path
path = (base_dir or _cli()._hermes_home) / path
if not path.exists():
logger.warning("Prefill messages file not found: %s", path)
return []

View File

@@ -0,0 +1,55 @@
"""Desktop/TUI agents honor ``prefill_messages_file`` from the active profile.
Regression for #60456: ``tui_gateway.server._make_agent`` (the Desktop / ``hermes serve`` agent
factory) never passed ``prefill_messages``, so a configured prefill file only worked in the
classic CLI and the messaging gateway.
"""
from __future__ import annotations
import json
import types
import yaml
from hermes_constants import reset_hermes_home_override, set_hermes_home_override
from tui_gateway import server
def _write_home(root, name: str, content: str):
home = root / name
home.mkdir()
(home / "config.yaml").write_text(yaml.safe_dump({"prefill_messages_file": "prefill.json"}), encoding="utf-8")
(home / "prefill.json").write_text(json.dumps([{"role": "user", "content": content}]), encoding="utf-8")
return home
def _built_prefill(monkeypatch, home):
captured = {}
def fake_agent(**kwargs):
captured.update(kwargs)
return types.SimpleNamespace(model=kwargs.get("model"))
monkeypatch.setattr("run_agent.AIAgent", fake_agent)
token = set_hermes_home_override(home)
try:
server._make_agent("sid", "session-key")
finally:
reset_hermes_home_override(token)
return captured["prefill_messages"]
def test_make_agent_injects_the_active_profiles_prefill(monkeypatch, tmp_path):
monkeypatch.delenv("HERMES_PREFILL_MESSAGES_FILE", raising=False)
monkeypatch.setattr(server, "_resolve_agent_model_runtime", lambda *_a: ("test-model", {}))
monkeypatch.setattr(server, "_load_enabled_toolsets", lambda *_a, **_kw: None)
monkeypatch.setattr(server, "_get_db", lambda: None)
monkeypatch.setattr(server, "_agent_cbs", lambda sid: {})
home_a = _write_home(tmp_path, "a", "prefill from A")
home_b = _write_home(tmp_path, "b", "prefill from B")
# A -> B -> A: the relative path resolves against whichever profile builds the agent.
assert _built_prefill(monkeypatch, home_a) == [{"role": "user", "content": "prefill from A"}]
assert _built_prefill(monkeypatch, home_b) == [{"role": "user", "content": "prefill from B"}]
assert _built_prefill(monkeypatch, home_a) == [{"role": "user", "content": "prefill from A"}]

View File

@@ -330,6 +330,15 @@ def _load_fallback_model():
return get_fallback_chain(_load_cfg())
def _load_prefill_messages() -> list:
"""Configured prefill messages, resolved like the CLI (env > ``prefill_messages_file`` > legacy
``agent.*``). Desktop/TUI agents never run the CLI bootstrap, so without this the setting was
ignored there (#60456). Relative paths resolve against the active profile home, per call."""
from hermes_cli.cli_config_load import _load_prefill_messages as _load, _resolve_prefill_messages_file
from hermes_constants import get_hermes_home
return _load(_resolve_prefill_messages_file(_load_cfg()), get_hermes_home())
def _sync_agent_fallback_with_config(sid: str, session: dict) -> None:
"""Adopt ``fallback_providers`` edits into the cached agent at turn start.

View File

@@ -2548,7 +2548,7 @@ def _make_agent(
checkpoints_enabled=is_truthy_value(os.environ.get("HERMES_TUI_CHECKPOINTS")),
pass_session_id=is_truthy_value(os.environ.get("HERMES_TUI_PASS_SESSION_ID")),
skip_context_files=ignore_rules, skip_memory=ignore_rules, fallback_model=_load_fallback_model(),
**_agent_cbs(sid))
prefill_messages=_load_prefill_messages() or None, **_agent_cbs(sid))
if context_cwd_is_launch_artifact is None:
context_cwd_is_launch_artifact = _context_cwd_is_launch_artifact(session)
agent._context_cwd_is_launch_artifact = bool(context_cwd_is_launch_artifact)