hermes-agent/tests/tui_gateway
ethernet 3133af8215
fix(desktop): prevent duplicate messages when verification candidates are persisted (#68149)
The display_history_prefix calculation used by session.resume's
_live_session_payload was display_history[:len(display) - len(raw)].
This assumed the model (repaired) history is always a suffix of the
display history — i.e., repair_message_sequence only removes messages
from the tail. That assumption broke when verification candidates
(finish_reason=verification_required) were persisted to state.db (#65919):

  - repair collapses consecutive assistant messages, removing the
    verification candidate from the MODEL history
  - the candidate stays in the DISPLAY history (it's real persisted content)
  - the length gap (gap = len(display) - len(raw)) counts BOTH ancestor
    messages AND repair-removed tip messages
  - the prefix = display[:gap] grabs the first N display messages, which
    are tip messages (not ancestors) when there are no compression ancestors
  - _live_session_payload concatenates prefix + model_history, duplicating
    the first N messages

On session 20260720_110036_a33889 (8 verification candidates), this
duplicated the first 8 messages in every warm-cache session.activate
response, producing visible duplicate user messages in the desktop.

Fix: add SessionDB.get_ancestor_display_prefix() which returns ONLY
genuine ancestor messages (rows where session_id != tip_session_id),
identified at the row level before _rows_to_conversation strips
session_id. Both resume paths (eager + deferred) now use this instead
of the length-slice heuristic.

Tests:
  - test_get_ancestor_display_prefix_single_session_returns_empty
  - test_get_ancestor_display_prefix_returns_ancestor_only_messages
  - Updated 12 mock DBs across test_protocol.py + test_tui_gateway_server.py
  - 848 passed (run_tests.sh), 0 regressions
2026-07-20 17:14:41 +00:00
..
__init__.py
…
test_billing_rpc.py
…
test_codex_app_server_live_events.py
…
test_compaction_status.py
…
test_compute_host.py
…
test_compute_host_phase1.py
…
test_custom_provider_session_persistence.py
…
test_delegation_session_lifecycle.py
…
test_entry_sys_path.py
…
test_fast_session_scope.py
…
test_finalize_session_persist.py
…
test_gateway_owned_session_reap.py
…
test_goal_command.py
…
test_image_routing_stale_model.py
…
test_inline_rpc_gil_starvation.py
…
test_interim_assistant_callback.py
…
test_iso_certify_seam.py
…
test_make_agent_provider.py
…
test_mcp_late_refresh_thread_owner.py
…
test_moa_reference_emit.py
…
test_model_switch_marker_role.py
…
test_pet_generate_rpc.py
…
test_project_tree.py
…
test_projects_rpc.py
…
test_protocol.py
…
test_reasoning_config_per_model.py
…
test_reasoning_session_scope.py
…
test_render.py
…
test_review_summary_callback.py
…
test_session_platform_resolution.py
…
test_slash_worker_ansi.py
…
test_slash_worker_mcp_discovery.py
…
test_slash_worker_profile_home.py
…
test_slash_worker_sys_path.py
…
test_subagent_child_mirror.py
…
test_undo_command.py
…
test_wait_for_mcp_discovery.py
…