`hermes chat -q`/`--oneshot`/`-Q` and `hermes -z` (both set HERMES_SINGLE_QUERY_SESSION=1) persisted their session as `cli` — and, before the first pass, as the inherited `tui`/`desktop` transport label — so finite automation runs sat in the TUI, Desktop and dashboard session pickers next to real conversations (#112550). - run_agent._session_source_for_agent: a single-query run whose source is empty (or an inherited UI transport label without an explicit --source) resolves to `oneshot`; the platform gate keeps delegate children (`subagent`) untouched; an explicit `--source` (HERMES_SESSION_SOURCE_EXPLICIT=1 from main.py) still wins. - hermes_state_sessions.INTERNAL_LISTING_SOURCES = (kanban, tool, oneshot) replaces the three copied `["kanban", "tool"]` literals (tui_gateway session.list, console `sessions list`/`stats`, in-chat /sessions), and the Desktop project tree / sidebar recents and the dashboard automation set exclude `oneshot` too. - `hermes -c` / `--resume latest` still chain on the previous one-shot (PR #105957's documented flow): the CLI MRU lookup matches the cli family {cli, oneshot} and search_sessions accepts several sources; one-shots keep stamping their launch cwd so the workspace-scoped lookup keeps working. - Compression child: the rotated child is published with the PARENT ROW's persisted source instead of bare agent.platform, so a `--source tool` / `oneshot` / inherited `kanban` session does not degrade to a picker-visible `cli` row after compaction. - Docs: sessions source table (+ oneshot/kanban/tool rows, compression note) and the `--source` flag reference (explicit flag always stored as given).
46 lines
1.7 KiB
Python
46 lines
1.7 KiB
Python
"""A compression child is the same conversation as its parent: it keeps the parent's persisted source
|
|
(``--source tool``, ``oneshot``, an inherited ``kanban``) instead of degrading to ``agent.platform`` (#112550)."""
|
|
|
|
import types
|
|
|
|
import pytest
|
|
|
|
from agent import conversation_compression as cc
|
|
from hermes_state import SessionDB
|
|
|
|
|
|
@pytest.fixture
|
|
def db(tmp_path):
|
|
database = SessionDB(tmp_path / "state.db")
|
|
try:
|
|
yield database
|
|
finally:
|
|
database.close()
|
|
|
|
|
|
def _agent(db, session_id):
|
|
return types.SimpleNamespace(
|
|
_session_db=db, session_id=session_id, platform="cli", model="m", _session_init_model_config=None,
|
|
working_directory=None, _memory_manager=None, context_compressor=types.SimpleNamespace(),
|
|
_flush_messages_to_session_db=lambda *a, **k: None, _persist_user_message_idx=None,
|
|
_session_messages=None, _gateway_session_key=None, _cached_system_prompt="sys",
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("parent_source", ["tool", "oneshot", "kanban"])
|
|
def test_compression_child_keeps_parent_source(db, parent_source):
|
|
db.create_session("parent", source=parent_source)
|
|
db.append_message("parent", "user", "hello")
|
|
db.append_message("parent", "assistant", "world")
|
|
agent = _agent(db, "parent")
|
|
|
|
cc._publish_rotated_compaction(
|
|
agent, [{"role": "user", "content": "hello"}, {"role": "assistant", "content": "world"}],
|
|
[{"role": "user", "content": "[handoff]"}], new_system_prompt="sys",
|
|
lease=types.SimpleNamespace(holder=None, ttl=60.0, watermark=None),
|
|
old_session_id="parent", compressed_user_turn_outcome="none",
|
|
)
|
|
|
|
assert agent.session_id != "parent"
|
|
assert db.get_session(agent.session_id)["source"] == parent_source
|