Skip to content

Document WebUI run state consistency contract - #2363

Closed
franksong2702 wants to merge 1 commit into
nesquena:masterfrom
franksong2702:franksong2702/run-state-consistency-contract
Closed

franksong2702 wants to merge 1 commit into
nesquena:masterfrom
franksong2702:franksong2702/run-state-consistency-contract

Conversation

@franksong2702

Copy link
Copy Markdown
Contributor

Thinking Path

Recent WebUI bugs around streaming, session switching, replay, compression, recovered pending turns, and sidebar ordering all expose the same product boundary: a single agent turn is represented in several places at once.

The visible transcript, model context, pending turn metadata, live stream, run journal, compression handoff, browser timeline cache, and sidebar metadata can each be locally correct while the user experience is still wrong if they disagree with each other.

This PR documents that shared consistency contract instead of treating each symptom as an unrelated one-off bug. It complements #1925: the run adapter RFC explains where execution ownership should move, while this RFC explains what must stay coherent across the current and future state layers.

Refs #2361.
Related to #1925.

What Changed

Why It Matters

This gives reviewers and contributors a concrete way to ask whether a WebUI state change is safe:

  • Which layer is being changed?
  • Which layer is source of truth after the change?
  • Can the user-visible transcript and model context diverge?
  • What happens after refresh, reconnect, session switch, WebUI restart, and stale cleanup?
  • Does replay duplicate or reorder visible content?
  • Can compression or cleanup accidentally look like current user activity?

That is the missing connective tissue between the individual fixes and the larger #1925 runtime-boundary direction.

Verification

  • git diff --check
  • Verified referenced open/closed GitHub issues and PRs before linking them in the RFC.

Risks / Follow-ups

  • This is a Draft PR because the contract should be reviewed before it becomes an accepted project rule.
  • This PR intentionally does not implement runtime behavior.
  • Follow-up code PRs should reference the specific invariant they preserve or change.

Model Used

GPT-5.5 via Codex.

@Michaelyklam

Copy link
Copy Markdown
Contributor

From the #1925/runtime-boundary side, this is a useful companion contract and it stays in the right lane: docs-only, no runner/sidecar, no streaming rewrite, and no claim that WebUI execution survives restart before ownership actually moves.

A couple of scope notes I would keep explicit while this is draft:

  1. Treat this as a consistency/review contract, not a new prerequisite that blocks the already-shipped slice-1 journal observation window. Slice 1 is still in production observation after v0.51.71.
  2. For the future RuntimeAdapter slice, this should reinforce the same guardrail as RFC: Make WebUI a thin observability/control client over Hermes Agent runtime #1925: the adapter/journal path is allowed to translate and observe runtime state, but it must not recreate STREAMS, CANCEL_FLAGS, cached AIAgent, approval queues, or other WebUI-owned runtime truth under new names.
  3. The new model-cache example on Define WebUI run state consistency across transcript, context, streams, and replay #2361 is a good example of “state layers can diverge,” but it may be broader config/catalog cache coherence rather than run state. I would either keep this RFC tightly scoped to active/recovered agent turns, or add one short boundary note saying non-run cache layers should get their own issue/contract unless they affect transcript/context/stream/replay behavior.

Checks are green and the PR is mergeable at f3c7baa; no #1925 blocker from my side if maintainers want this contract merged after draft review.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Shipped in v0.51.74 ✅

Stage-367 release shipped at #2369. Merged commit e3035b3 into master. Tag v0.51.74 pushed.

(Refs #2361 and #1925)

Verified before release:

  • Full pytest: 5725 passed, 0 failed
  • 45/45 PR-specific + locale parity tests pass
  • QA harness: 20/20 pass
  • Opus advisor: independently verified all 5 specific checks, APPROVE
  • Browser sanity: verified live on local 8789 (settings sidebar i18n keys + table-cell margin reset + Chinese locale translation 插件 for new key)

Thanks for the contribution! Closing now.

nikolaef43 pushed a commit to nikolaef43/hermes-webui that referenced this pull request May 16, 2026
nikolaef43 pushed a commit to nikolaef43/hermes-webui that referenced this pull request May 16, 2026
v0.51.74 — Release AX:
- PR nesquena#2362 (fixes nesquena#2360) — Markdown table cell paragraph spacing
- PR nesquena#2363 (refs nesquena#2361 + nesquena#1925) — Run state consistency RFC docs
- PR nesquena#2365 (fixes nesquena#1106) — custom_providers.models YAML list format
- PR nesquena#2367 — Settings sidebar i18n (with maintainer locale-parity fix)

None of the PRs touched CHANGELOG.md, so adding the canonical entries
manually with proper PR # attribution and issue refs.

Stage-367 maintainer fix on nesquena#2367: CI failed on 5 locale-parity tests
because the PR added settings_tab_plugins to English only. Added
translations to all 10 non-English locales (commit ddf8eb5 on the
PR's integration branch).
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request May 16, 2026
… 0.51.74) (#501)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.67` → `0.51.74` |

---

### Release Notes

<details>
<summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary>

### [`v0.51.74`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05174--2026-05-16--Release-AX-stage-367--4-PR-safe-lane-batch--2362-table-cell-spacing--2363-run-state-consistency-RFC--2365-customproviders-list-format--2367-settings-sidebar-i18n)

[Compare Source](nesquena/hermes-webui@v0.51.73...v0.51.74)

##### Added

- **PR [#&#8203;2363](nesquena/hermes-webui#2363 by [@&#8203;franksong2702](https://github.com/franksong2702) (refs [#&#8203;2361](nesquena/hermes-webui#2361), refs [#&#8203;1925](nesquena/hermes-webui#1925)) — Adds `docs/rfcs/webui-run-state-consistency-contract.md` as a documentation companion to the [#&#8203;1925](nesquena/hermes-webui#1925) runtime-boundary RFC. Documents the shared coherence contract across visible transcript, model context, pending turn metadata, live stream, run journal, compression handoff, browser timeline cache, and sidebar metadata. Complementary to [#&#8203;1925](nesquena/hermes-webui#1925): that RFC says where execution ownership should move, this one says what must stay coherent across the current and future state layers.

##### Fixed

- **PR [#&#8203;2362](nesquena/hermes-webui#2362 by [@&#8203;franksong2702](https://github.com/franksong2702) (fixes [#&#8203;2360](nesquena/hermes-webui#2360)) — Markdown table rows no longer become too tall when cell text is wrapped in paragraph tags by the renderer. Adds a table-specific CSS reset for `.msg-body td p` and `.msg-body th p` so the global `margin-bottom: 10px` rule on `.msg-body p` doesn't add unwanted vertical space inside table cells. Especially visible on narrow viewports such as iPad Safari/Chrome.

- **PR [#&#8203;2365](nesquena/hermes-webui#2365 by [@&#8203;mccxj](https://github.com/mccxj) (fixes [#&#8203;1106](nesquena/hermes-webui#1106)) — `get_available_models()` now handles YAML-list format `custom_providers.models` entries in addition to dict format. Pre-fix, declaring models as a list (`[m1, m2]`) or list-of-dicts (`[{id: m1, label: ...}]`) in `config.yaml` silently discarded every model from that provider in the picker dropdown because the code only recognized dict shape (`{model_id: {}}`). Now supports all three YAML shapes consistently with existing provider-config and live-models-fallback handlers.

- **PR [#&#8203;2367](nesquena/hermes-webui#2367 by [@&#8203;mccxj](https://github.com/mccxj) — Settings sidebar menu items (Conversation, Appearance, Preferences, Plugins, System) now respect locale selection. Pre-fix these were hardcoded English; only Providers had `data-i18n`. Adds `data-i18n` attributes plus the missing `settings_tab_plugins` key. **Stage-367 maintainer fix applied inline**: the PR only added the new key to English, breaking 5 locale-parity tests. Added `settings_tab_plugins` translations to all 10 non-English locales (it/ja/ru/es/de/zh/zh-TW/pt/ko/fr).

### [`v0.51.73`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05173--2026-05-16--Release-AW-stage-366--1-PR-safe-lane-batch--2357-compression-reference-card-anchoring-fix)

[Compare Source](nesquena/hermes-webui@v0.51.72...v0.51.73)

##### Fixed

- **PR [#&#8203;2357](nesquena/hermes-webui#2357 by [@&#8203;franksong2702](https://github.com/franksong2702) (fixes [#&#8203;2355](nesquena/hermes-webui#2355)) — Auto-compression reference cards no longer get mixed into the final answer turn after a session rotation. Pre-fix, `_insertCompressionLikeNodeByRawIdx()` appended the compression-reference node to the future assistant anchor turn's blocks, which projected the `[CONTEXT COMPACTION — REFERENCE ONLY]` card into the live tail. The fix inserts the node *before* the anchor segment so the reference card stays a sibling, not a child of the answer turn.

### [`v0.51.72`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05172--2026-05-16--Release-AV-stage-365--2-PR-safe-lane-batch--2354-recovered-pending-turn-context-fix--2348-Thinking-card-interim-text-echo-suppression)

[Compare Source](nesquena/hermes-webui@v0.51.71...v0.51.72)

##### Fixed

- **PR [#&#8203;2354](nesquena/hermes-webui#2354 by [@&#8203;franksong2702](https://github.com/franksong2702) (fixes [#&#8203;2353](nesquena/hermes-webui#2353)) — Stale stream recovery now keeps a recovered pending user turn in the model context (`context_messages`) as well as the visible transcript. Pre-fix, a server restart during an in-flight turn could restore the user's message in WebUI while omitting it from `context_messages`, so the next agent turn could forget a prompt that was visibly present just above it. The repair path now appends the recovered user turn to both surfaces with 8-message lookback dedup so already-checkpointed entries are not duplicated.

- **PR [#&#8203;2348](nesquena/hermes-webui#2348 by [@&#8203;franksong2702](https://github.com/franksong2702) (fixes [#&#8203;2346](nesquena/hermes-webui#2346)) — Thinking cards now suppress exact snippets that are already shown as user-visible interim assistant text, avoiding duplicated progress lines when an agent emits the same sentence through both reasoning and interim-assistant callbacks. Tracks `_liveThinkingText` during the live stream to strip the visible echo from the live Thinking card display; applies the same suppression in the settled-transcript path so reload/session-switch sees the cleaned-up view too.

### [`v0.51.71`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05171--2026-05-16--Release-AU-stage-364--3-PR-batch--2349-stale-stream-cleanup-non-touching--2343-profiles-vs-workspaces-help-card--2283-run-event-journal-replay-refs-1925-RFC-slice-1--with-Opus-caught-replay-double-render-fix)

[Compare Source](nesquena/hermes-webui@v0.51.70...v0.51.71)

##### Added

- **PR [#&#8203;2343](nesquena/hermes-webui#2343 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;2147](nesquena/hermes-webui#2147)) — The Profiles panel now includes an inline "Profiles vs workspaces" explainer. The copy clarifies that profiles control how the agent works — identity, memory, skills, model/provider config, and tools — while workspaces control what project/files a session operates on, making the OpenClaw-style role/profile mental model easier to map onto Hermes WebUI.

- **PR [#&#8203;2283](nesquena/hermes-webui#2283 by [@&#8203;franksong2702](https://github.com/franksong2702) (refs [#&#8203;1925](nesquena/hermes-webui#1925)) — Adds an append-only WebUI run event journal for browser-originated chat streams (refs [#&#8203;1925](nesquena/hermes-webui#1925)). Every SSE event emitted by the legacy in-process runner is mirrored to a per-session JSONL file, `/api/chat/stream/status` reports when replay is available for a dead stream, `/api/chat/stream` can replay journaled events with SSE event IDs and a clear stale-restart diagnostic, and the frontend reattach path uses that replay before clearing local running state. Reconnect replay uses the last rendered SSE event id as its `after_seq` cursor so it does not replay already-rendered events, and journal fsync defaults to terminal events only (`HERMES_WEBUI_RUN_JOURNAL_FSYNC=eager` restores per-event fsync). This is the first compatibility slice only: it preserves the existing WebUI runner and does not make active execution survive a WebUI restart. **Stage-364 maintainer fix applied inline**: Opus advisor caught that live SSE frames emitted by `_sse()` in `api/streaming.py:2296` carry no `id:` field, so the frontend's `_lastRunJournalSeq` cursor stayed at 0 during live streaming and a mid-stream error→replay would arrive with `after_seq=0`, replaying every journaled event from seq 1 and double-rendering tokens. The fix adds `STREAM_LAST_EVENT_ID: dict = {}` as a per-stream side-channel in `api/config.py`; `put()` writes the journal's `event_id` to that dict on every event; `_handle_sse_stream` reads it at SSE emit time and uses `_sse_with_id(handler, event, data, event_id)` when present. The queue tuple shape is preserved as `(event, data)` so existing queue consumers (cancel sentinel, sprint42/51 tests, etc.) are not broken. Cleaned up in the worker's finally block alongside the other STREAM\_\* dicts. 6 regression tests added covering side-channel dict declaration, writer/reader paths, tuple shape preservation, and cleanup.

##### Fixed

- **PR [#&#8203;2349](nesquena/hermes-webui#2349 by [@&#8203;franksong2702](https://github.com/franksong2702) (fixes [#&#8203;2345](nesquena/hermes-webui#2345)) — Clearing stale stream runtime flags no longer refreshes a session's `updated_at`, so old compressed continuations should not jump back to the top of the sidebar just because WebUI repaired a dead `active_stream_id` during a read/list request.

### [`v0.51.70`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05170--2026-05-16--Release-AS-stage-363--4-PR-snapshotjournalUI-batch--2337-compression-snapshot-runtime-clear--2334-turn-journal-fcntl-lock--2342-INFLIGHT-reattach-pending-row--2339-workspace-panel-edge-toggle)

[Compare Source](nesquena/hermes-webui@v0.51.69...v0.51.70)

##### Added

- **PR [#&#8203;2339](nesquena/hermes-webui#2339 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;2211](nesquena/hermes-webui#2211)) — The workspace panel now has a small desktop edge toggle that remains clickable after the right panel is hidden, making it possible to reopen the workspace browser without returning to Settings. The existing panel close button and composer workspace button remain unchanged; the new affordance only appears when the workspace panel is closed on desktop widths.

##### Fixed

- **PR [#&#8203;2337](nesquena/hermes-webui#2337 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2336](nesquena/hermes-webui#2336)) — Pre-compression snapshot preservation now also clears stale runtime stream fields when the existing on-disk snapshot is already as complete as the in-memory session. This keeps the load-and-mark branch aligned with the full-save branch and adds regression coverage so archived parent snapshots cannot retain stale `active_stream_id` / `pending_*` state.

- **PR [#&#8203;2342](nesquena/hermes-webui#2342 by [@&#8203;franksong2702](https://github.com/franksong2702) (fixes [#&#8203;2341](nesquena/hermes-webui#2341)) — Reattaching to an active streaming session now keeps the user prompt that started the running turn visible. Pre-fix, reload/session-switch restore could hydrate from the browser's INFLIGHT stream cache while the backend still held the initiating prompt only as `pending_user_message`, so the transcript showed assistant Thinking/Tool activity without the user's just-submitted message. The restore path now merges that pending user row into the live transcript before rendering and updates the INFLIGHT cache, while duplicate suppression checks the current message array so final session payloads do not show the prompt twice.

- **PR [#&#8203;2334](nesquena/hermes-webui#2334 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;2097](nesquena/hermes-webui#2097)) — Turn journal appends now take an advisory `flock` around each JSONL event write and fsync when Unix file locks are available. This keeps oversized submitted-message events from interleaving at the byte level if a future deployment runs multiple WebUI worker processes against the same state directory, while preserving the previous best-effort append path on platforms without `fcntl`.

### [`v0.51.69`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05169--2026-05-15--Release-AT-stage-362--8-PR-follow-up-batch--Ollama-routing--legacy-toolset--cancel-copy--cleanup--custom-provider-mismatch--cron-metadata--dead-code-removal-2323-reverted-after-Opus-caught-silent-regression-refiled-as-2321-reopen)

[Compare Source](nesquena/hermes-webui@v0.51.68...v0.51.69)

##### Added

- **PR [#&#8203;2332](nesquena/hermes-webui#2332 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;2290](nesquena/hermes-webui#2290)) — Cron run history/output cards now surface token/cost metadata when the underlying cron output markdown includes it. The backend parses optional model/token/cost/duration frontmatter from cron output files and returns it from `/api/crons/history` and `/api/crons/run`; the Tasks panel renders a compact usage strip beside run rows and below expanded output without affecting older outputs that lack usage metadata.

##### Fixed

- **PR [#&#8203;2322](nesquena/hermes-webui#2322 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;2271](nesquena/hermes-webui#2271)) — LAN Ollama models selected from endpoint-discovered `custom:<host>-<port>` / `custom:<host>:<port>` picker entries now route through the configured `ollama` provider and base URL instead of surfacing a missing `CUSTOM_*_API_KEY` error. The picker still surfaces endpoint-discovered entries; the fix is to recognize them as UI routing hints matching the configured local-server base URL and resolve them via the actual `ollama` provider.

- **PR [#&#8203;2326](nesquena/hermes-webui#2326 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2232](nesquena/hermes-webui#2232)) — Legacy `hermes` CLI toolset alias is now normalized to `hermes-cli` + `hermes-api-server` when WebUI resolves CLI toolsets from shared Hermes config. Modern Hermes Agent exposes the composite under those two names; older configs that still contain the legacy `hermes` toolset name no longer surface as "unknown toolset" warnings.

- **PR [#&#8203;2327](nesquena/hermes-webui#2327 by [@&#8203;dotBeeps](https://github.com/dotBeeps) — Cancel-mid-stream messaging now uses the user's configured assistant name (e.g. "Hermes") instead of hardcoded "Skyly". Preferences allow defining an Assistant Name that persists throughout the UI; the cancel copy was the last place still showing the persona placeholder. Backend persisted-cancelled-turn text and frontend live-cancel toast both now read from the same `botName` setting.

- **PR [#&#8203;2328](nesquena/hermes-webui#2328 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2325](nesquena/hermes-webui#2325)) — Two cleanup follow-ups from v0.51.68 stage-361 review: (a) when a session is deleted via `/api/session/delete`, its `~/.hermes/webui/attachments/<sid>/` inbox is also removed (orphan accumulation prevention); (b) the deferred stream-recovery listener bound by `_deferStreamErrorIfPageHidden()` now bails out when the user switches sessions in the same tab — the recovery would otherwise fire `setComposerStatus('Reconnected')` for a stream the user has moved past. Both fixes are narrow cleanup with regression tests.

- **PR [#&#8203;2330](nesquena/hermes-webui#2330 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2329](nesquena/hermes-webui#2329)) — Provider mismatch warnings now skip named custom providers such as `custom:zenmux`. Custom aggregators can legitimately route vendor-prefixed models like `google/gemini-3.1-flash-lite`, so `_checkProviderMismatch()` now treats `custom:<name>` the same as bare `custom` and avoids false-positive "may not work with your configured provider" warnings.

- **PR [#&#8203;2331](nesquena/hermes-webui#2331 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) — Live activity row now shows a transient human-readable progress phrase derived from the current tool category (e.g. "Reading file…", "Searching files…", "Running command…") instead of only the elapsed-time counter `Working 1m 23s`. Compact transcript view unchanged.

- **PR [#&#8203;2333](nesquena/hermes-webui#2333 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2312](nesquena/hermes-webui#2312) follow-up [#&#8203;1](nesquena/hermes-webui#1)) — Removed dead production helper `_save_pre_compression_snapshot()` at `api/streaming.py:1945`. The production path now uses `_preserve_pre_compression_snapshot()` exclusively (which must index snapshots with `skip_index=False` for sidebar filtering). The dead helper was only called from `tests/test_compression_snapshot_runtime_clear.py`; the test is retargeted to exercise the actual production helper instead. Closes follow-up item [#&#8203;1](nesquena/hermes-webui#1) from the v0.51.66 review ([#&#8203;2312](nesquena/hermes-webui#2312)).

### [`v0.51.68`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05168--2026-05-15--Release-AR-stage-361--4-PR-follow-up-batch--2315-profile-skill-seeding--2317-theme-fallback--2318-mobile-stream-defer--2319-chat-upload-relocation--with-Opus-caught-vision-model-regression-fix)

[Compare Source](nesquena/hermes-webui@v0.51.67...v0.51.68)

##### Added

- **PR [#&#8203;2319](nesquena/hermes-webui#2319 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) — Chat file uploads now land in a session-scoped attachment inbox instead of cluttering the active workspace root. By default uploads are stored under `~/.hermes/webui/attachments/<session_id>/`; operators can override the root with `HERMES_WEBUI_ATTACHMENT_DIR`, and the agent still receives the absolute uploaded file path for context. Archive extraction stays workspace-scoped (it's an explicit workspace operation). README updated to document the new default location. **Stage-361 maintainer fix applied inline**: Opus advisor caught that `_build_native_multimodal_message` at `api/streaming.py:787` required uploads to be under `workspace_root`, which would have silently dropped every image upload for vision-capable models once the inbox moved outside the workspace. The fix adds `_attachment_root()` (from `api/upload.py`) as a second allowed location, with 3 regression tests covering the new code path AND verifying the original workspace + cross-root rejection paths still work.

##### Fixed

- **PR [#&#8203;2315](nesquena/hermes-webui#2315 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2305](nesquena/hermes-webui#2305), refs [#&#8203;749](nesquena/hermes-webui#749)) — WebUI profile creation now seeds bundled profile skills for newly-created non-cloned profiles, matching the CLI's `hermes profile create` behaviour. Pre-fix, creating a profile via Settings → New Profile (without checking "Clone from active profile") left the profile's `skills/` directory empty, which was inconsistent with CLI-created profiles that get the full bundled-skills overlay. The fix calls `seed_profile_skills(profile_path, quiet=True)` after `profile_path.mkdir()` when `clone_from is None`. Cloned profiles still inherit skills from their source — they don't get a second bundled-skills overlay. Seed failures (e.g. `hermes_cli` unavailable in Docker fallback) are logged as warnings, not fatal — profile creation still succeeds.

- **PR [#&#8203;2317](nesquena/hermes-webui#2317 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;2312](nesquena/hermes-webui#2312) follow-up [#&#8203;2](nesquena/hermes-webui#2)) — Appearance boot reconciliation now treats explicit `light`, `dark`, and `system` localStorage theme values as user selections when a prior Settings autosave failed. Pre-fix, the predicate `lsHasExplicitTheme = lsTheme === 'system'` only treated 'system' as explicit, so a user who picked `light` on a server defaulted to `dark` (or vice versa) with a failed autosave still reverted to the server default on refresh. Now broadened to `['system','light','dark'].includes(lsTheme)`. Skin handling was already correct (`lsSkin !== 'default'`). Closes follow-up item [#&#8203;2](nesquena/hermes-webui#2) from the v0.51.66 review ([#&#8203;2312](nesquena/hermes-webui#2312)).

- **PR [#&#8203;2318](nesquena/hermes-webui#2318 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2307](nesquena/hermes-webui#2307)) — Mobile/Android backgrounded tabs no longer show a permanent `**Error:** Connection lost` banner when the backend stream is still alive and able to replay buffered events. Pre-fix, the SSE error finalization fired regardless of page visibility state, so any tab discarded by the mobile OS (battery saver, tab compression, brief switch to another app) showed a permanent error even though the stream could be re-attached on visibility return. The fix defers inline stream error rendering while `document.visibilityState === 'hidden'` or `document.wasDiscarded === true`, then on visibility return polls `/api/chat/stream/status?stream_id=...`. If the stream is still active, reattaches with a fresh `EventSource`. If not, falls back to the settled-session restore path. If both paths fail, falls back to the original error rendering. Behaviour on desktop and on tabs that ARE visible is unchanged.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/501
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request May 16, 2026
… 0.51.75) (#527)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.74` → `0.51.75` |

---

### Release Notes

<details>
<summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary>

### [`v0.51.75`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05175--2026-05-16--Release-AY-stage-368--11-PR-safe-lane-batch--storage--i18n--run-journal-parity--attachments--compression-sidebar--restart-recovery--text-mode-images--tables--settings-i18n--German-labels)

[Compare Source](nesquena/hermes-webui@v0.51.74...v0.51.75)

##### Test infrastructure

- Stage-368 maintainer fix — pytest no longer self-loops on the `_schedule_restart` daemon thread. Several existing tests in `tests/test_update_banner_fixes.py` call `api.updates._schedule_restart()`, which spawns a daemon thread that eventually calls `os.execv()`. Those tests monkeypatch `os.execv` for the test scope, but monkeypatch teardown can win the race against the daemon thread, restoring the real `os.execv` before the thread fires it — at which point the daemon re-execs the entire pytest process with the original argv, looking from the outside like pytest hangs at 99 % then restarts the suite from 0 % in an infinite loop. `tests/conftest.py` now installs a permanent no-op wrapper on `os.execv` at module-import time so late-firing daemon threads cannot re-exec pytest. New `tests/test_pytest_execv_guard.py` pins the guard against future regressions.

##### Added

- **PR [#&#8203;2377](nesquena/hermes-webui#2377 by [@&#8203;franksong2702](https://github.com/franksong2702) (refs [#&#8203;2283](nesquena/hermes-webui#2283), refs [#&#8203;2363](nesquena/hermes-webui#2363), refs [#&#8203;1925](nesquena/hermes-webui#1925)) — Run-journal replay timeline parity checks. After [#&#8203;2283](nesquena/hermes-webui#2283) shipped the first run-journal replay slice and [#&#8203;2363](nesquena/hermes-webui#2363) documented the cross-layer state consistency contract, this PR adds explicit parity assertions over the replayed timeline so divergences between the journal and the visible transcript (Thinking → tool calls → assistant text) surface as test failures instead of silent drift.

##### Fixed

- **PR [#&#8203;2391](nesquena/hermes-webui#2391 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (fixes [#&#8203;2389](nesquena/hermes-webui#2389)) — Reduce browser storage pressure during service-worker updates and over long-running sessions. `static/sw.js` now calls `deleteOldShellCaches()` BEFORE `caches.open(CACHE_NAME)` in the install handler so the new \~2.2 MB shell cache no longer overlaps the old one during a version bump (especially painful on shared-origin quota accounting). A new `_clearSessionViewedCount()` helper plus extended `_clearHandoffStorageForSession()` prune `hermes-session-viewed-counts`, `hermes-session-completion-unread`, and `hermes-session-observed-streaming` on every single-session delete and batch-delete so per-session tracking maps no longer grow unbounded.

- **PR [#&#8203;2387](nesquena/hermes-webui#2387 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (fixes [#&#8203;2386](nesquena/hermes-webui#2386)) — Guard `localStorage.setItem('hermes-webui-session', ...)` and workspace-panel runtime-state writes with `try { … } catch (_) {}` across `static/boot.js`, `static/sessions.js`, `static/commands.js`, and `static/messages.js`. These convenience writes were previously fatal UI operations on quota-exhausted browsers (especially Firefox public-domain setups where shared quota fills up after a service-worker shell rotation).

- **PR [#&#8203;2368](nesquena/hermes-webui#2368 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) — Hybridize background profile env routing so background title generation, manual compression, and update-summary workers honor a session's non-default profile. The pure thread-local refactor for [#&#8203;2321](nesquena/hermes-webui#2321) was reverted because `hermes_cli.config.load_config()` still reads `HERMES_HOME` from process env. This PR keeps the thread-local layer for WebUI helpers and adds an `os.environ.update(runtime_env)` mirror under a narrow `_ENV_LOCK` for the worker body, with proper restore of prior values. New test asserts `OPENROUTER_API_KEY` is visible from the worker against a non-default profile.

- **PR [#&#8203;2382](nesquena/hermes-webui#2382 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (fixes [#&#8203;2380](nesquena/hermes-webui#2380)) — Serve raw chat attachments from the per-session inbox in addition to the session workspace. Chat uploads were intentionally moved out of workspaces into a per-session attachment inbox in an earlier release; the transcript renderer still emits stable `api/file/raw?session_id=...&path=<filename>` URLs, but `_handle_file_raw` only checked `session.workspace` so inbox-backed uploads rendered as broken images. The URL surface is preserved and a session-attachment fallback is added with path-traversal guards intact.

- **PR [#&#8203;2385](nesquena/hermes-webui#2385 by [@&#8203;franksong2702](https://github.com/franksong2702) — Keep fuller compression snapshots reachable in the sidebar. The default behavior hides `pre_compression_snapshot: true` rows so archived compression segments do not duplicate the active continuation. A real long Kanban session exposed a narrower failure: the fuller transcript was still present on disk but remained marked as `pre_compression_snapshot`, so the sidebar surfaced a shorter row and the fuller transcript became unreachable. The fix preserves discoverability without re-introducing duplication in normal cases.

- **PR [#&#8203;2371](nesquena/hermes-webui#2371 by [@&#8203;franksong2702](https://github.com/franksong2702) — Clarify interrupted turn recovery after a WebUI restart. WebUI executes browser-originated agent turns inside the WebUI process; if that process restarts mid-turn, the worker dies with it. Run journal replay can only replay events that were already emitted, so the stale-pending repair path is now annotated and refined to make the post-restart state explicit (interrupted, recoverable, or terminal) instead of leaving the user with a half-rendered turn and no signal.

- **PR [#&#8203;2378](nesquena/hermes-webui#2378 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) — Strip historical images in text-only mode. Current-turn uploads already respect `agent.image_input_mode: text`, but saved conversation history still passed native `image_url` content parts back into later provider calls, breaking text-only providers on replayed turns. `_sanitize_messages_for_api()` gains a `cfg=` keyword argument so the API-history sanitizer can strip historical native image parts when the mode is text. Default `cfg=None` preserves prior behavior for callers that don't pass the new argument.

- **PR [#&#8203;2375](nesquena/hermes-webui#2375 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) — Keep Markdown tables block-level. Pipe tables were already converted to `<table>` markup, but the final paragraph pass did not treat generated tables as block-level output, occasionally wrapping them in `<p>` and breaking the surrounding layout. The fix isolates generated tables and adds `table` to the paragraph-wrap skip list so valid CommonMark tables render predictably.

- **PR [#&#8203;2372](nesquena/hermes-webui#2372 by [@&#8203;mccxj](https://github.com/mccxj) — Settings → Conversation page action buttons now respect locale selection. Pre-fix, the JSON export, MD export, and Copy buttons had hardcoded English labels/titles. Adds `data-i18n` / `data-i18n-title` attributes plus the missing translation keys so non-English locales no longer see English labels stuck in the middle of a translated screen.

- **PR [#&#8203;2381](nesquena/hermes-webui#2381 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (fixes [#&#8203;2379](nesquena/hermes-webui#2379)) — German relative session-time labels now interpolate the elapsed value instead of rendering the literal `{n}` placeholder in the sidebar/header. The German locale now uses function-valued translations for minutes, hours, and days, matching the other locale bundles.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/527
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
v0.51.74 — Release AX:
- PR nesquena#2362 (fixes nesquena#2360) — Markdown table cell paragraph spacing
- PR nesquena#2363 (refs nesquena#2361 + nesquena#1925) — Run state consistency RFC docs
- PR nesquena#2365 (fixes nesquena#1106) — custom_providers.models YAML list format
- PR nesquena#2367 — Settings sidebar i18n (with maintainer locale-parity fix)

None of the PRs touched CHANGELOG.md, so adding the canonical entries
manually with proper PR # attribution and issue refs.

Stage-367 maintainer fix on nesquena#2367: CI failed on 5 locale-parity tests
because the PR added settings_tab_plugins to English only. Added
translations to all 10 non-English locales (commit 496a211 on the
PR's integration branch).
bernyforce pushed a commit to bernyforce/hermes-webui that referenced this pull request Jul 29, 2026
bernyforce pushed a commit to bernyforce/hermes-webui that referenced this pull request Jul 29, 2026
v0.51.74 — Release AX:
- PR nesquena#2362 (fixes nesquena#2360) — Markdown table cell paragraph spacing
- PR nesquena#2363 (refs nesquena#2361 + nesquena#1925) — Run state consistency RFC docs
- PR nesquena#2365 (fixes nesquena#1106) — custom_providers.models YAML list format
- PR nesquena#2367 — Settings sidebar i18n (with maintainer locale-parity fix)

None of the PRs touched CHANGELOG.md, so adding the canonical entries
manually with proper PR # attribution and issue refs.

Stage-367 maintainer fix on nesquena#2367: CI failed on 5 locale-parity tests
because the PR added settings_tab_plugins to English only. Added
translations to all 10 non-English locales (commit 674f3e8 on the
PR's integration branch).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants