fix(sessions): compacted display history keeps protected-tail copies in original chronological order

_dedupe_display_generations chose the right representative row per logical
message but sorted the survivors by that representative's id. A protected-tail
copy written into a newer compaction generation has a higher id than messages
emitted after the original, so include_compacted reads came back as C, A, B.
Anchor the sort on the logical message's first-ever row id instead.

Salvaged from #93869 (the tui_gateway half of that PR is superseded by #100504
and #104137); the code moved from hermes_state.py to hermes_state_messages.py
since, so the change is re-applied to its new home with the PR's regression
test verbatim.
This commit is contained in:
ColDSnit
2026-09-06 14:40:39 +05:30
committed by kshitij
parent dd1d7475ca
commit 63316c74a8
2 changed files with 31 additions and 1 deletions

View File

@@ -588,6 +588,7 @@ class SessionMessagesMixin:
into each generation: same role/content/timestamp, different ``active``/id); prefer the live row, then
the newest. The ONE definition every display projection shares. *rows* must be ordered by ``id``."""
seen: Dict[Tuple[Any, ...], Any] = {}
first_id: Dict[Tuple[Any, ...], int] = {}
for row in rows:
dedupe_content = row["content"]
if row["role"] == "user":
@@ -604,7 +605,10 @@ class SessionMessagesMixin:
cur = seen.get(key)
if cur is None or (row["active"], row["id"]) > (cur["active"], cur["id"]):
seen[key] = row
return sorted(seen.values(), key=lambda r: r["id"])
first_id[key] = min(first_id.get(key, row["id"]), row["id"])
# Order by the logical message's FIRST row, not the chosen representative's: a protected-tail
# copy in a newer generation has a higher id than messages emitted after the original.
return [seen[key] for key in sorted(seen, key=first_id.__getitem__)]
def _row_to_message_dict(self, row, *, warn_context: str, summary_flag: bool) -> Dict[str, Any]:
"""``dict(row)`` with content/tool_calls/display_metadata decoded; *summary_flag* keeps