fix(#5626): ignore inactive compacted state-db rows in context replay

get_state_db_session_messages() returned archived (active=0) rows that in-place
compaction had marked inactive. WebUI reconciliation feeds this reader straight
into the next model-facing context (reconciled_state_db_messages_for_session,
prefer_context=True), so those archived rows got pulled back in — resurrecting
pre-compaction history and making every later turn re-trigger compression. Now,
WHEN the messages table exposes an `active` column, inactive rows are excluded by
default (`AND (active IS NULL OR active != 0)` — NULL/legacy rows preserved), with
an `include_inactive=True` escape hatch for explicit recovery/audit callers.
Schemas without the column are unaffected (guarded by `'active' in available`).

Contributor stage; ungated on arrival. Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
This commit is contained in:
nesquena-hermes committed 2026-07-05 08:59:21 +00:00
1 parent 35464be634
commit 1165bd9ca2
2 files changed
+89

No files matched your search

+12
View File
@@ -6143,6 +6143,7 @@ def get_state_db_session_messages(
stitch_continuations: bool = False,
profile=None,
since_timestamp=None,
include_inactive: bool = False,
) -> list:
"""Read messages for a Hermes session from state.db.
@@ -6158,6 +6159,13 @@ def get_state_db_session_messages(
raw state.db scan to rows at or after a sidecar-derived timestamp floor while
preserving the caller's normal merge/window logic. Full-history callers must
leave it unset.
When the messages table exposes an ``active`` column, inactive rows are
compacted/archived history and are intentionally excluded by default. WebUI
reconciliation feeds this reader straight into the next model context; pulling
``active=0`` archive rows back in resurrects pre-compaction history and can
make every later turn re-trigger compression. Pass ``include_inactive=True``
only for explicit recovery/audit views.
"""
try:
import sqlite3
@@ -6249,11 +6257,15 @@ def get_state_db_session_messages(
if since_ts is not None:
since_clause = " AND (timestamp IS NULL OR timestamp >= ?)"
params.append(since_ts)
active_clause = ""
if 'active' in available and not include_inactive:
active_clause = " AND (active IS NULL OR active != 0)"
cur.execute(f"""
SELECT {', '.join(selected)}, session_id
FROM messages
WHERE session_id IN ({placeholders})
{since_clause}
{active_clause}
ORDER BY timestamp ASC, id ASC
""", params)
msgs = []