Skip to content

fix(desktop): surface compaction-archived messages in transcript reads (#80680) - #83757

Closed
zuowen7 wants to merge 2 commits into
NousResearch:mainfrom
zuowen7:fix/80680-include-compacted-display-history
Closed

fix(desktop): surface compaction-archived messages in transcript reads (#80680)#83757
zuowen7 wants to merge 2 commits into
NousResearch:mainfrom
zuowen7:fix/80680-include-compacted-display-history

Conversation

@zuowen7

@zuowen7 zuowen7 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #80680 — after in-place context compaction, earlier turns of a session become unreachable in Hermes Desktop: the transcript silently ends at the compaction boundary, "Show earlier messages" disappears, and no UI path recovers the history.

Root cause: SessionDB.get_messages() defaults to active rows only (AND active = 1). In-place compaction archives earlier turns as active=0, compacted=1 rows — durable display history, not soft-deleted rows — but the desktop transcript read (getLatestSessionMessagesGET /api/sessions/{id}/messages) never asked for them. The UI exhausts its active-only array, the window reports olderAvailable=false, and the control disappears even though older durable history exists.

Real-world evidence (independent Windows reproduction in the issue thread): a session with message_count=58 held 509 rows — 451 of them active=0, compacted=1 and invisible.

Related Issue

Fixes #80680

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • hermes_state.pySessionDB.get_messages() gains include_compacted: bool = False: when set, the read covers active = 1 OR compacted = 1. Soft-deleted Undo/Rewind rows (active=0, compacted=0) stay excluded — that remains the job of include_inactive (audit/debug reads), so the naive broadening the issue warns about is avoided.
  • hermes_cli/web_routers/sessions.pyGET /api/sessions/{id}/messages exposes include_compacted (default false; dashboard default view unchanged).
  • apps/desktop/src/hermes.ts — desktop transcript loaders (getLatestSessionMessages, getAllSessionMessages) opt in, restoring the full display history.

Note: enabling includeCompacted on getAllSessionMessages is a deliberate side effect — exports/artifact generation now include archived (compacted) display history, deduplicated, instead of live rows only. This matches the transcript view; if a caller ever needs live-only rows again it can pass includeCompacted: false.

Deduplication across compaction epochs: each compaction epoch copies the protected tail into the new generation, so the same logical message exists as several rows with identical role/content/timestamp (verified on a real state.db: one message appeared 4× across generations, 322 duplicate rows in a single session). include_compacted reads the full display set, dedupes on (role, content, timestamp) preferring the live row then the newest generation, and only then applies paging — so offset pages can never surface a duplicate. Genuine repeats with different timestamps are preserved.

Rotating-compression lineage reconstruction (parent sessions) and server-authoritative has_more are intentionally out of scope — this is the minimal fix for the in-place compaction boundary, which is the observed failure mode.

Relationship to the existing resume display lineage: session.resume already serves a display transcript via get_resume_conversations/get_ancestor_display_prefix — but that reads rotating-compression ancestors (session_id IN (ancestors…) AND active = 1), i.e. earlier turns stored in parent sessions. It does not read active=0, compacted=1 rows, which is the in-place compaction half of the same problem. This PR is complementary: the REST transcript endpoint (the desktop's primary display path) now surfaces the in-place-archived rows the resume lineage never covered. The dedupe strategy here (prefer live row, then newest generation) matches the ancestor/tip dedupe #65919 applied to the resume path.

How to Test

  1. pytest tests/hermes_state/test_get_messages_include_compacted.py -q → 9 passed (default-read regression guard, archived rows surfaced, soft-deleted rows excluded, include_inactive semantics unchanged, latest paging, limit/offset paging, dedupe across generations).
  2. pytest tests/hermes_cli/test_web_server.py -q -k "compacted" → 2 passed (endpoint default hides, include_compacted=true surfaces archived rows in insertion order).
  3. Verified against a real state.db with 451 hidden archived rows (active=0, compacted=1) behind 58 live rows — include_compacted=true returns the full display history deduplicated (on a 1,100-row session: 1,026 raw rows → 704 logical messages).

Note on the full suite: on a machine whose default DB path is a real Hermes home (like this dev machine), tests/hermes_state/test_live_db_isolation_guard.py and a few environment-dependent tests fail identically with and without this change (verified via git stash comparison) — they are unrelated pre-existing environment failures.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate (open + merged, multiple keyword passes)
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run the relevant pytest suites and they pass (see How to Test; full-suite caveat documented above)
  • I've added tests for my changes (required for bug fixes)
  • I've tested on my platform: Windows 11

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — docstrings for the new parameter cover the semantics — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — the change is a SQL filter + query parameter, platform-neutral — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Screenshots / Logs

Real state.db evidence (from the #80680 thread):

SELECT message_count FROM sessions WHERE id = '<affected-session>';
-- 58

SELECT active, compacted, COUNT(*) FROM messages
WHERE session_id = '<affected-session>'
GROUP BY active, compacted;
-- 0 | 1 | 451
-- 1 | 0 | 58

@zuowen7
zuowen7 force-pushed the fix/80680-include-compacted-display-history branch from 93a2c0b to 454dbb2 Compare August 11, 2026 07:54
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard area/sessions Session lifecycle, resume, persistence, history area/compression Context compression and continuation sessions sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 11, 2026
@zuowen7
zuowen7 force-pushed the fix/80680-include-compacted-display-history branch from 454dbb2 to 28ca92d Compare August 11, 2026 08:11
…ed (NousResearch#80680)

Dedupe key now includes tool_call_id/tool_calls/tool_name: compaction
copies carry those fields verbatim, so identical tool messages across
generations still collapse, while distinct tool calls sharing
role/content/timestamp are never merged. Add endpoint-level coverage
for the desktop's real read path (limit + order=latest +
include_compacted=true).
@teknium1

Copy link
Copy Markdown
Contributor

Thank you for this fix! It was salvaged into #86595 (cherry-picked onto current main with your authorship preserved in the commit history) and is now merged. Closing this PR since the work has landed.

@teknium1 teknium1 closed this Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/compression Context compression and continuation sessions area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Desktop “Show earlier messages” disappears before reaching the true session start

3 participants