Skip to content

feat(desktop): add compression lineage navigation - #81561

Open
acellm wants to merge 1 commit into
NousResearch:mainfrom
acellm:feat/desktop-context-lineage-navigation
Open

acellm wants to merge 1 commit into
NousResearch:mainfrom
acellm:feat/desktop-context-lineage-navigation

Conversation

@acellm

@acellm acellm commented Aug 8, 2026

Copy link
Copy Markdown

What does this PR do?

Hermes already preserves earlier messages when compression rotates a session into a child session, but Desktop only exposes the live tip. This PR adds a compact compression-lineage navigator so users can inspect and branch from exact historical context segments without changing the live runtime.

The selected session row shows the current segment and two recent historical segments. A timeline sheet exposes the complete lineage and lazy-loads one exact, read-only segment at a time.

Related Issue

Related to #48404, #44913, #50192, #79565, and #80680.

Relationship to existing open PRs

The backend files overlap, but the UX and API contract here are complementary rather than a copy of those approaches.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Add strict compression-continuation traversal that excludes ordinary branches, delegates/subagents, tool sessions, and stale orphan-reaped children while preserving the existing ID-list API.
  • Add structured, paginated lineage metadata and a bounded exact segment-messages endpoint that does not auto-resolve historical reads to the live tip; Desktop aggregates all message pages before rendering.
  • Handle inherited markers, invalid historical JSON, parent-pointer cycles, ambiguous continuations, unknown closed children, broken integrity, and lineages longer than 100 segments with bounded fail-closed semantics.
  • Add a compact three-segment sidebar projection plus a complete timeline sheet without inserting physical compression segments into the logical $sessions tree.
  • Reuse the assistant-ui transcript in explicit spectator mode: no composer, reactions, edits, reloads, restores, timeline, inherited live transcript window, global thread-scroll/edit listeners, or live-state mutations.
  • Add segment load error/retry behavior and keep branching disabled until the exact segment loads successfully.
  • Branch from the exact selected historical segment, including root and middle segments, without aliasing provenance to the live tip; inherit workspace metadata from the projected lineage owner.
  • Preserve only trusted display_kind=hidden compaction checkpoints across Desktop payload coercion, optimistic preview, Gateway persistence, SQLite, and reload; user text cannot forge hidden context.
  • Key historical lineage/message/error caches by profile plus lineage identity so same-ID sessions cannot cross profile boundaries.
  • Count exact transcript pages with the same active-row predicate used to fetch them, so rewound segments terminate pagination correctly.
  • Dynamically measure expanded virtual-list rows and aggregate all backend pages for 101+ segment lineages.
  • Add English, Simplified Chinese, Traditional Chinese, and Japanese strings plus dialog accessibility metadata.

How to Test

  1. Open a Desktop session whose compression config uses session rotation (compression.in_place: false) and which has multiple persisted compression children.
  2. Confirm the selected sidebar row shows the current context segment, two recent historical segments, and a complete-segment count button.
  3. Open the timeline sheet, select a historical segment, and verify the live route/session remains unchanged while an exact read-only transcript loads.
  4. Create a branch from that segment, send a message, reload it, and verify its parent_session_id is the selected segment and its compaction checkpoint remains hidden in the UI.
  5. Run the automated checks listed below.

Automated/local verification:

  • Desktop targeted tests: 8 files / 218 tests passed, including read-only reaction, spectator scroll/edit/timeline/transcript-window isolation, profile cache isolation, and exact middle-segment branching.
  • Python lineage/API/Gateway targeted tests: 46 passed, including cyclic lineage and rewound pagination regressions.
  • Renderer, Electron, and E2E TypeScript typechecks passed.
  • ESLint passed with 0 errors; Ruff check and Python compile passed.
  • Prettier, git diff --check, and added-lines secret scan passed.
  • Production Desktop build passed.
  • Isolated real Electron dogfood passed on a 14-segment lineage; an exact historical segment returned 258 messages without resolving to the tip.
  • Branch reload preserved one hidden checkpoint in the API while rendering zero checkpoint markers in the UI.

Broader local suites retain unrelated pre-existing/environment failures outside this diff:

  • Desktop: 3586 tests passed; 3 unchanged locale/billing tests failed.
  • Python: 729 passed, 4 skipped; 8 unchanged environment/baseline tests failed (Windows live-DB guards, a theme SPA fixture, and local toolset expectations).

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
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass (see documented pre-existing/environment failures above)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Windows 10, isolated Hermes home and Desktop user-data

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A; behavior is self-contained in Desktop UI/API docstrings
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A; no config keys changed
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A; no contributor workflow changed
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — no platform-specific runtime behavior was added
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A; no agent tool schema changed

Screenshots / Logs

The feature was visually dogfooded in a real isolated Electron instance. The timeline sheet displayed all 14 segments, the selected historical transcript remained read-only, and branching/reload persistence was verified against the isolated SQLite copy.

Notes for Reviewers

  • This first version represents cross-session rotation lineage. Reconstructing multiple in-place compression generations requires a persisted generation/boundary schema and is intentionally out of scope.
  • Exact segment messages are bounded to 500 rows per response and expose total/has_more; Desktop follows pages and presents one complete exact segment. Tail-first or virtualized transcript loading can be added separately for unusually large segments.
  • Compression segments remain out of $sessions and the generic branch tree so logical session identity, grouping, drag/drop, and virtualization semantics stay stable.

Expose persisted compression segments through exact lineage APIs and a
read-only Desktop timeline. Preserve logical session identity, strict
continuation semantics, exact branch provenance, and hidden checkpoints
across Gateway and SQLite persistence.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/compression Context compression and continuation sessions sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 8, 2026
@spfcraze

spfcraze commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Summary:
For a compressed parent with a live continuation child and a stale closed sibling, resolve_resume_session_id now fails closed to the compacted parent, where main's get_compression_tip preferred the live child.

Problems:

  • _get_compression_continuation_state (hermes_state.py) reports invalid_closed_child when any candidate child has a non-compression end_reason, even when a valid live continuation exists in the same candidate set; only ws_orphan_reap siblings are excluded.
  • resolve_resume_session_id then returns the requested id unchanged whenever lineage integrity is not ok, so a resume of the compacted parent reloads the pre-compression transcript while the post-compression turns sit in the continuation.
  • Main's get_compression_tip (hermes_state.py:6365) ranked children compression > live > closed (WHEN child.ended_at IS NULL THEN 1), so a live continuation always won over a stale session_reset sibling — the shape this PR now fails. The comment above resolve_resume_session_id (hermes_state.py:7835) records that walk as the fix for the desktop "I came back and the reply isn't there" report.

Solution:
Fail closed only for a parent whose candidate children are all closed; when a live or compression-ended child exists, select it over closed siblings as the old ORDER BY did.


Checked against 8a25bb4 — the tip of feat/desktop-context-lineage-navigation when this was written — and fe54ab4, main at the same moment.

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 comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants