Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch - #2596
Merged
Merged
Conversation
When API server runs append messages directly to state.db, reconcile WebUI sidecar sessions with those canonical rows across API responses, model-facing streaming context, and active browser refresh. Add append-only state.db merge helpers, metadata-only counts for refresh polling, and regression coverage for API visibility, context incorporation, and frontend refresh behavior.
Force same-session external refreshes to dismiss stale approval and clarification prompts immediately so completed state.db updates do not leave the composer blocked.
When a queued message was waiting for the active stream to finish, the 120ms setTimeout drain in setBusy(false) would write the queued text to the shared #msg composer and call send(), which reads S.session.session_id at call time. If the user switched to a different chat during the 120ms window, the queued message was sent to the wrong session. Two fixes: 1. setBusy(false) drain: guard the setTimeout callback — if the currently viewed session no longer matches the drain session, put the message back into the original session's queue instead of sending it. 2. _sendInProgress re-queue: track _sendInProgressSid alongside _sendInProgress so that when a concurrent send() is caught by the guard, the re-queued message targets the in-flight session rather than the currently viewed one.
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # api/streaming.py
# Conflicts: # CHANGELOG.md
SysAdminDoc
pushed a commit
to SysAdminDoc/hermes-webui
that referenced
this pull request
Jun 26, 2026
Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch
bernyforce
pushed a commit
to bernyforce/hermes-webui
that referenced
this pull request
Jul 29, 2026
Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stage 386 / v0.51.93 — Release BQ — 10-PR full sweep batch
Ten contributor PRs across RFC docs, workspace UX polish, prompt-cache visibility, terminal hygiene, provider-routing, profile-aware naming, state.db reconciliation slice 1, queued-message race, and stale-stream supersede.
PRs in this batch
Fixed
--file-tree-toggle-widthCSS variable so the file-row placeholder can't silently desync from the directory toggle. Closes the followup issue filed against PR fix: align workspace tree file icons #2563 / v0.51.92.file://markdown links in settled rendering by rewriting them to/api/media?path=...&inline=1before the sanitizer drops them. Streamed and settled paths are now symmetric.cache_read / prompt_total), not the misleadingcache_read / (cache_read + cache_write). Newapi/usage.pyhelper matches the Hermes Agent log convention.PR_SET_PDEATHSIG. Post-SIGKILLwait was added so timed-out shells don't remain unreaped.assistantDisplayName()helper. The saved assistant-name preference applies only to the literaldefaultprofile; named profiles use their own profile name. Routes composer placeholder,document.title, message role labels, browser notifications, cancel-copy fallback, and empty-state through the same helper.setBusy(false)window. Drain-time guard re-queues againstsid;_sendInProgressSidcaptures activeSid at the commit point._getOptionProviderId()now readsdata-providerfrom temporary<option data-custom="1">rows (configured-model picker rows created outside the native catalog), so the next send routes through the correct provider.Changed
_new_turn_context_from_messagesextraction so both legacy and streaming paths share the_drop_checkpointed_current_user_from_context+ casual-fresh-chat suppression behavior (refs Webui lost my prompts #1217 / Compressed sessions can hijack ordinary chat by resuming stale agent tasks #2308). 923 LOC acrossapi/models.py,api/routes.py,api/streaming.py,static/sessions.js+ four new test files; second-pass agent diff review LGTM after the streaming-path regression was caught and fixed.Documentation
Verification
Pre-Opus 7-point gate — green
ast.parseover allapi/*.py+tests/*.py— cleanTests
6018 passed, 6 skipped, 3 xpassed, 8 subtests passed in 136.70s (0:02:16)— +33 tests vs v0.51.92Opus advisor — GO-WITH-FOLLOWUP (1 filed as tracker issue)
Verified each of five self-verify concerns with file:line citations:
Recovery slice 1 from the closed reconciliation pr #2581 + Fix stale stream recovery writeback race #2587 composition on api/streaming.py — Safe.
_previous_messages(line 4023-4028) is a local at turn start, doesn't mutates.messages. The recovery marker ons.messagesis detected directly by_stream_writeback_can_supersede_recovery_marker(line 2372). Merge function at line 4198 drops the marker from the merged result since_previous_messagesdoesn't carry it. ✅Recovery slice 1 from the closed reconciliation pr #2581 metadata fast-path safety — Mostly safe. Sidecar-only legacy session case (no state.db row) is safe —
get_state_db_session_summaryreturns{}and_summary_message_countisNone. One narrow theoretical edge case filed as tracker issue Metadata fast-path message_count can diverge from merged count when state.db retains old rows #2594: if state.db retains rows the sidecar lost AND those rows are timestamp-filtered bymerge_session_messages_append_only,state_count > merged_countkeepsremoteCount > localCountfiring every 5s. Atypical (both stores normally written from same source) but worth a precise fix.Fix profile-aware assistant display names #2583
assistantDisplayName()consistency — Clean.grep window._botNamereturns 4 hits, all are assignments (no stray reads). All 8 read sites use the new helper. Profile switch correctly re-invokesapplyBotName(). ✅fix: prevent queued follow-up message from draining into wrong chat #2584 queued-message guard tightness — The gap between
_sendInProgress=trueand_sendInProgressSid=activeSidexists but is bounded by two layers of defense: concurrent rejectedsend()falls back toS.session.session_id, in-flight send eventually capturesactiveSid, and the 120ms drain guard re-queues if mismatched. Tightening is a refinement, not a correctness fix. Acceptable as shipped. ✅fix: reap terminal shells on shutdown #2582 pdeathsig portability — Safe. On macOS,
libc.prctlraisesAttributeError, caught by the bareexcept Exception. Child proceeds normally. On Linux,prctl(PR_SET_PDEATHSIG, SIGTERM)succeeds. Reasonable belt-and-suspenders withwait(timeout=1.0)after SIGKILL andatexit.register(close_all_terminals). ✅Triage actions taken this sweep (not in the batch)
maintainer-reviewapplied: feat(workspace): add integrated Git controls #2578 (@stocky789 workspace Git controls, +5390 LOC across 19 files) — scope-splittable into backend+tests / UI slices, no CI run yet, hooks executing under WebUI process is a trust-boundary expansion, no coordination with active agent runs. Posted detailed split-into-A-and-B plan + 5 concrete pre-merge mitigations.agent_session_keyround-trip code that is dead on the WebUI streaming path. Asked contributor to re-scope to peek-only or fully wire round-trip through_approval_notify_cb.download_folderkey. Pointed contributor at the// TODO: translatefallback pattern used elsewhere.Notes
api/streaming.pyconflict between Recovery slice 1 from the closed reconciliation pr #2581 and the already-merged fix(usage): display prompt-cache coverage against prompt total #2579 — both added imports near the same line. Resolved by accepting both imports.[Unreleased]block. Standard pattern from prior sweeps.