Fix WebUI reconciliation after Desktop session continuation - #4834
franksong2702 wants to merge 3 commits into
Conversation
| ### Fixed | ||
|
|
||
| - **WebUI-created sessions stay in sync after the official Hermes Desktop App continues the same Hermes Agent session.** The conversation sidebar now refreshes WebUI-origin rows from settled `state.db` message counts/timestamps even when external/CLI sessions are hidden, full session loads avoid duplicating the sidecar prefix when merging `state.db`, and the next WebUI turn saves a single reconciled transcript instead of re-writing duplicated history. |
There was a problem hiding this comment.
CHANGELOG.md should not be touched by contributor PRs
Per the project's process, CHANGELOG.md is maintained exclusively by the release agent via release: vX.Y.Z commits. Individual contributor PRs must not edit this file directly. Please revert this change; the release automation will pick up the entry when the next version ships.
Rule Used: Do not flag missing CHANGELOG.md updates on indivi... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
d3c63ab to
0d7ccc7
Compare
|
Read the diff at HEAD against The overlay guardThe count/time overlay in if state_count > current_count and (state_last <= 0 or state_last > current_last):
session['message_count'] = state_count
...with The edgeBecause The no-duplicate-prefix merge assertion in |
|
Thanks @franksong2702 — the Desktop-continuation reconciliation logic itself is sound (Codex SAFE + Opus SAFE: the new sidebar overlay is read-only, content-free, and structurally can't cause the ghost/message-loss class). But it can't ship yet: it introduces a parallel test-isolation regression that the full sharded suite reproduces. The failure
Likely causeThe change removes the Fix directionMake the new always-on state.db stamp read isolation-safe under the test suite, or update the single-flight cache tests to reset/scope the shared Everything else checks out — both gates SAFE on the reconciliation logic, 31 reconciliation tests green, serial full suite green. It's specifically the parallel-isolation interaction with the sidebar-cache single-flight test. Re-gate when the full xdist suite is green. Keeping |
06fec29 to
873ca8e
Compare
873ca8e to
cdb1118
Compare
…tate-db-reconcile # Conflicts: # CHANGELOG.md
|
Updated this branch against current master and resolved the remaining DIRTY state. The conflict was limited to CHANGELOG.md; the session reconciliation code paths auto-merged cleanly. Local verification:
GitHub CI is now green on 0d6ffcb. I also ran the full local suite. It is not clean on this machine due to failures that reproduce on clean origin/master as well, so I am relying on the PR-specific local slice plus GitHub CI for the final gate. Ready for re-review. |
|
Addressed the review items on this branch. What changed:
Verification:
|
|
Shipped in v0.51.668 (Release XX, just deployed) — thanks @franksong2702! Fixes #4833. WebUI-origin sessions now reconcile correctly after the Desktop app continues them (sidebar refreshes from settled state.db count/time even with CLI sessions hidden; metadata-only overlay, strictly-grew, active-stream hold-down preserved). The prior bounce (parallel test-isolation regression) is resolved — the follower-wait test now passes under the full xdist suite. Gate: Codex SAFE + Opus SHIP IT (test-isolation fix confirmed test-only; overlay read-only/no-resurrection), suite 10631. Verified on prod. |
v0.51.668 — Release XX: WebUI<->Desktop session reconciliation (nesquena#4834, fixes nesquena#4833) # Conflicts: # CHANGELOG.md # tests/test_webui_state_db_reconciliation.py
Thinking Path
/api/sessionsfrom a WebUI sidecar, then simulates the official Hermes Desktop App appending settled rows to the same Hermes Agentstate.dbsession./api/sessionsthrough the sidebar cache andall_sessions(include_lineage_metadata=False)path.state.dbentirely when external/CLI sessions were hidden./api/sessionsstarted observingstate.dbstamps, a stamp mismatch could discard the stale payload before a follower request could use it, forcing that follower to wait behind a slow owner rebuild./api/sessionbut missing from/api/sessions, traced the index path: it pruned stale rows and overlaid active in-memory sessions, but did not recover persisted sidecar JSON files missing from a non-empty_index.jsononce the runtime overlay was gone.What Changed
state.db.messagescount and max timestamp in the existing sidebar state-db override helper.state.dbcount/time onto stale WebUI sidecar rows when the DB has strictly grown beyond the sidecar._index.json, using metadata-only loads, then write those recovered rows back to the index./api/sessionscache stamps observestate.dbeven when CLI/external sessions are hidden, while preserving the existing active-stream hold-down behavior.try/finallycleanup so failed assertions release the owner builder and join threads instead of orphaning them until CI timeout./api/sessionsmetadata updating after Desktop-settledstate.dbappend./api/session?messages=1returning one reconciled transcript instead of duplicating a sidecar prefix.Session.load()can read still appearing in sidebar rows even when a non-empty_index.jsonmissed it and the in-memory runtime overlay is gone.CHANGELOG.md.Why It Matters
Users can continue a WebUI-created session from the official Hermes Desktop App and then return to WebUI. After the Desktop turn settles, WebUI now shows the complete final transcript in the sidebar/detail view and uses that settled history as context for the next WebUI message, without duplicating old rows in the sidecar.
It also keeps completed worker sessions discoverable in the sidebar after the active runtime overlay disappears, as long as their sidecar JSON remains readable by session detail.
Fixes #4833.
Verification
./scripts/test.sh tests/test_streaming_session_sidebar.py::test_messageful_session_missing_from_index_is_recovered_in_sidebar -q— 1 passed../scripts/test.sh tests/test_session_sidebar_cache.py::test_session_list_cache_follower_reuses_stale_payload_during_slow_rebuild -q— 1 passed../scripts/test.sh tests/test_session_sidebar_cache.py::test_session_list_cache_follower_wait_stage_when_rebuild_inflight tests/test_session_sidebar_cache.py::test_session_list_cache_follower_reuses_stale_payload_during_slow_rebuild -q— 2 passed../scripts/test.sh tests/test_streaming_session_sidebar.py tests/test_session_sidebar_cache.py tests/test_webui_state_db_reconciliation.py tests/test_webui_state_db_context_reconciliation.py tests/test_session_db_sidecar_reconciliation.py tests/test_issue4766_sidebar_source_pushdown.py tests/test_cli_sessions_cache_fingerprint.py tests/test_issue3930_source_filter_pushdown.py tests/test_issue1494_state_db_fd_leak.py -q— 90 passed..venv/bin/python -m py_compile api/models.py api/routes.py tests/test_streaming_session_sidebar.py tests/test_session_sidebar_cache.py tests/test_webui_state_db_reconciliation.py tests/test_webui_state_db_context_reconciliation.py— passed.git diff --check— passed.Risks / Follow-ups
/api/sessionsnow observesstate.dbfor WebUI-only sidebar views. The existing active-stream hold-down remains in place to avoid per-token cache churn during WebUI streaming.state.dbrows and only when state.db has grown beyond the sidecar, to avoid resurrecting restamped/replayed histories.Model Used
OpenAI Codex GPT-5.5