fix(state): skip out-of-window timestamps when computing last_active

This commit is contained in:
Hermes Agent
2026-09-24 23:31:38 -05:00
committed by brooklyn!
parent 5307e93252
commit f27bde1fdd
2 changed files with 32 additions and 4 deletions

View File

@@ -10,6 +10,7 @@ import sys
import time
from typing import Any
from hermes_cli.timefmt import EPOCH_MAX, EPOCH_MIN
from agent.skill_commands import SKILL_EXCERPT_JOINT, SKILL_SCAFFOLD_SQL_LIKE, describe_skill_invocation
from agent.context_compressor import (LEGACY_SUMMARY_PREFIX, SUMMARY_PREFIX, _MERGED_PRIOR_CONTEXT_HEADER,
_MERGED_SUMMARY_DELIMITER, _SUMMARY_END_MARKER)
@@ -218,10 +219,14 @@ def _ephemeral_child_sql(alias: str = "s") -> str:
def _sql_freshest_of(activity: str, session_id_expr: str, started: str) -> str:
"""Freshest of *activity* and the latest message timestamp for *session_id_expr*, else *started*.
Heartbeats are rate-limited (~60s) so ``last_activity_at`` can lag a newer message; never use it alone."""
msg_max = f"(SELECT MAX(_act_m.timestamp) FROM messages _act_m WHERE _act_m.session_id = {session_id_expr})"
return (f"COALESCE((SELECT MAX(_act_v.v) FROM (SELECT {activity} AS v UNION ALL SELECT {msg_max}) _act_v), "
f"{started})")
Heartbeats are rate-limited (~60s) so ``last_activity_at`` can lag a newer message; never use it alone.
Cells outside the ``coerce_epoch`` window (garbage doubles salvaged from a damaged page, TEXT) are
skipped, or one bad message row pins the session's recency to ``5e+246`` (#91536)."""
in_window = f"BETWEEN {EPOCH_MIN!r} AND {EPOCH_MAX!r}"
msg_max = (f"(SELECT MAX(_act_m.timestamp) FROM messages _act_m WHERE _act_m.session_id = {session_id_expr}"
f" AND _act_m.timestamp {in_window})")
return (f"COALESCE((SELECT MAX(_act_v.v) FROM (SELECT {activity} AS v UNION ALL SELECT {msg_max}) _act_v"
f" WHERE _act_v.v {in_window}), {started})")
def _sql_session_last_active(alias: str = "s") -> str:

View File

@@ -57,6 +57,29 @@ def test_list_export_and_insights_survive_corrupt_timestamp_rows(corrupt_db, cap
assert any("bad-huge" in rec.getMessage() for rec in caplog.records)
def test_last_active_skips_a_garbage_message_timestamp(tmp_path):
"""The Desktop sessions pane reads ``last_active`` straight from ``list_sessions_rich`` and builds
``new Date(last_active * 1000)``; one salvaged garbage double must not become the session's recency
(#91536)."""
db = SessionDB(db_path=tmp_path / "state.db")
try:
db.create_session("recovered", "cli")
for text in ("first", "second", "third"):
db.append_message("recovered", "user", text)
good = max(row["timestamp"] for row in db.get_messages("recovered"))
db._execute_write(lambda conn: conn.execute(
"UPDATE messages SET timestamp = 5.4905047707024164e+246 WHERE session_id = 'recovered' "
"AND id = (SELECT MIN(id) FROM messages WHERE session_id = 'recovered')"))
db._execute_write(lambda conn: conn.execute(
"UPDATE sessions SET last_activity_at = 'not-a-timestamp' WHERE id = 'recovered'"))
rows = {r["id"]: r for r in db.list_sessions_rich(limit=10)}
tip_rows = {r["id"]: r for r in db.list_sessions_rich(limit=10, order_by_last_active=True)}
finally:
db.close()
assert rows["recovered"]["last_active"] == good
assert tip_rows["recovered"]["last_active"] == good
def test_writers_never_persist_an_out_of_window_timestamp(tmp_path):
db = SessionDB(db_path=tmp_path / "state.db")
try: