Skip to content

Filter interrupted recovery control text from visible transcript - #3321

Closed
franksong2702 wants to merge 3 commits into
nesquena:masterfrom
franksong2702:franksong2702/fix-stream-interrupted-recovery-repro
Closed

franksong2702 wants to merge 3 commits into
nesquena:masterfrom
franksong2702:franksong2702/fix-stream-interrupted-recovery-repro

Conversation

@franksong2702

@franksong2702 franksong2702 commented Jun 1, 2026 •

Copy link
Copy Markdown
Contributor

Thinking Path

  • Reproduced the reported shape from session e30f2cc1efe0: interrupted SSE recovery text can be replayed into the visible transcript instead of staying platform-only control state.
  • Traced the lifecycle from backend synthetic run-journal payload (stale_interrupted_event) through static/messages.js SSE settle/error handlers and static/ui.js final transcript filtering.
  • Tightened the first draft after review: the original guard only covered assistant content helpers, so the generic msgContent(m) path could still render the recovery text; it also missed the system-like/user-message leak shape.

Contract Routing

  • Contract area: streaming / SSE recovery / transcript rendering.
  • Mutated state layers: durable run journal payload metadata, frontend S.messages during done / apperror / _restoreSettledSession, and render-time visible transcript indexing.
  • Invariant: recovery-control instructions are never user-visible transcript content, while real user/assistant text, tool calls, and settled transcript restoration remain replayable and idempotent.

What Changed

  • api/run_journal.py
    • Marked stale_interrupted_event synthetic interrupted payloads with recovery_control: True.
  • static/messages.js
    • Added strict recovery-control detection helpers.
    • Filters recovery-control rows from settled frontend state after done, recovery-control apperror, and _restoreSettledSession.
    • Treats backend recovery-control apperror as a restore signal instead of appending a visible synthetic assistant error.
  • static/ui.js
    • Filters recovery-control messages before both visible transcript filtering and cached visible-index rebuild.
    • Handles system-like/user-message recovery prompts as control text while avoiding broad matching on ordinary user phrases.
  • tests/test_live_stream_ux.py and tests/test_session_rotate_url_sync.py
    • Added regression assertions for backend tagging, frontend settle filtering, UI visible-index filtering, and non-broad phrase matching.
    • Updated the rotate-url static assertion window so the added settle filter does not create a false negative.

Why It Matters

  • Prevents platform-injected recovery instructions from being presented as user or assistant transcript text.
  • Keeps done, restore, and recovery-control apperror paths aligned with the same transcript visibility invariant.

Verification

  • python -m pytest tests/test_live_stream_ux.py -q
  • python -m pytest tests/test_live_stream_ux.py tests/test_static_js_runtime_lint.py tests/test_cancelled_turn_status.py -q
  • python -m pytest tests/test_run_journal.py tests/test_run_journal_streaming_static.py tests/test_run_journal_routes.py tests/test_run_journal_frontend_static.py tests/test_issue765_streaming_persistence.py tests/test_stale_stream_pending_recovery.py tests/test_stale_stream_writeback.py -q
  • python -m pytest tests/test_session_rotate_url_sync.py tests/test_live_stream_ux.py -q
  • python -m pytest tests/test_live_stream_ux.py tests/test_session_rotate_url_sync.py tests/test_sprint9.py::test_no_duplicate_function_definitions -q
  • python -m pytest tests/ -q --timeout=60 --shard-id=2 --num-shards=3
  • git diff --check

Risks / Follow-ups

  • Manual SSE reconnect validation is still useful in a stable UI session to confirm the user-facing status wording around restore.
  • If future providers introduce new recovery-control text variants, they should be server-classified with recovery_control: True rather than added as broad frontend phrase matches.

AI usage

  • Provider: OpenAI
  • Model: GPT-5 via Codex
  • Notable tools: local code inspection, targeted patching, pytest execution, GitHub CLI.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Cross reference: root cause and repro details tracked in issue #3320.

@franksong2702
franksong2702 marked this pull request as draft June 1, 2026 09:09
@franksong2702
franksong2702 marked this pull request as ready for review June 1, 2026 09:33
nesquena-hermes added a commit that referenced this pull request Jun 1, 2026
v0.51.202: filter interrupted-recovery control text from visible transcript (#3321)
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Shipped in v0.51.202 (stage-batch14, via #3358). Thanks @franksong2702 — the recovery_control server marker + the narrow anchored-text fallback is the right approach for keeping interrupted-recovery control text out of the transcript.

One maintainer fix on the way in (flagged by both the Codex and Opus pre-release gates): _isRecoveryControlMessage / _streamRecoveryControlMessage also fell back to matching provider_details_label === 'interruption details'. But a genuine "Response interrupted" card (Stop button, real provider crash) carries that exact label — so on the next render/restore the filter would have dropped a real user-facing interruption from the transcript (the inverse of the data-loss class we just held #3300 for). I removed the provider_details_label heuristic from both predicates, keeping the explicit recovery_control marker + the two fully-anchored synthetic-text matches (pre-marker backward-compat). Added a node-driven regression test (revert-verified) asserting a label-only interruption card and a real user turn stay visible while marker + strict text are filtered. Co-authored-by preserved. 🎉

sabadia pushed a commit to sabadia/hermes-webui that referenced this pull request Jun 1, 2026
…nesquena#3321)

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
sabadia pushed a commit to sabadia/hermes-webui that referenced this pull request Jun 1, 2026
sabadia pushed a commit to sabadia/hermes-webui that referenced this pull request Jun 1, 2026
…istic (over-filtered genuine interruptions)

Codex+Opus regression-gate finding: _isRecoveryControlMessage /
_streamRecoveryControlMessage fell back to matching
provider_details_label==='interruption details'. But a GENUINE 'Response
interrupted' card (Stop button, real provider crash) carries that exact label,
so the filter would drop a real user-facing interruption from the transcript on
the next render/restore — the inverse of the nesquena#3300 data-loss class. Require the
explicit server-set recovery_control marker; keep only the two fully-anchored
synthetic-text matches for pre-marker backward-compat. Adds a node-driven
regression test (revert-verified) asserting a label-only interruption card and a
real user turn stay visible while marker + strict text are filtered.
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request Jun 2, 2026
…➔ 0.51.210) (#782)

This PR contains the following updates:

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

---

### Release Notes

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

### [`v0.51.210`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051210--2026-06-02--Release-GD-stage-batch1--model-picker-multi-slash-fix--extensionless-preview-highlighting)

[Compare Source](nesquena/hermes-webui@v0.51.209...v0.51.210)

##### Fixed

- Model picker no longer snaps to the wrong model when multiple multi-slash model IDs from the same proxy provider share the same base name. Exact-match priority in `_findModelInDropdown` and first-segment-only stripping in `_normalizeConfiguredModelKey` / `_norm_model_id` prevent collisions in selection, badge assignment, and configured-entry dedup ([#&#8203;3360](nesquena/hermes-webui#3360), [@&#8203;b3nw](https://github.com/b3nw)).
- Workspace file previews now syntax-highlight common code/config filenames without useful extensions, including `Dockerfile`, `Dockerfile.*`, `Makefile`, `GNUmakefile`, `CMakeLists.txt`, `.gitignore`, and `.dockerignore` ([#&#8203;3365](nesquena/hermes-webui#3365), [@&#8203;AJV20](https://github.com/AJV20)).

### [`v0.51.209`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051209--2026-06-02--Release-GC-WebUI-dashboard-plugin-system-with-iframe-isolation)

[Compare Source](nesquena/hermes-webui@v0.51.208...v0.51.209)

##### Added

- WebUI dashboard plugins: plugins that ship a UI under `~/.hermes/plugins/<name>/dashboard/` (with a `manifest.json`) now appear as opt-in cards in Settings → Plugins (default off). Once enabled, an **Open** button renders the plugin page inside a sandboxed iframe (`sandbox="allow-scripts allow-forms allow-popups"` — no `allow-same-origin`, so plugin JS/CSS/modals stay fully isolated from the parent app). New `/plugins/` (shared assets) and `/dashboard-plugins/<name>/` (per-plugin assets) static routes serve only built `dist/`/`static/` files with path-traversal, dotfile, and extension-allowlist protection (plugin source/config such as `plugin_api.py`/`manifest.json`/`.env` is never served), and both the page and asset routes are gated server-side on the enable state + an HTTP `sandbox` CSP + `nosniff`. Plugin `name` and `tab.path` are validated at load. Display-only — no plugin backend/subprocess execution ([#&#8203;2622](nesquena/hermes-webui#2622), [@&#8203;pix0127](https://github.com/pix0127)).

### [`v0.51.208`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051208--2026-06-02--Release-GB-workspace-upload-hardening-hotfix)

[Compare Source](nesquena/hermes-webui@v0.51.207...v0.51.208)

##### Fixed

- Hardened the workspace file-upload surface ([#&#8203;3104](nesquena/hermes-webui#3104) follow-up): (1) a negative `Content-Length` no longer bypasses the size cap and triggers an unbounded `rfile.read(-1)` — the length is now validated `[0, MAX_UPLOAD_BYTES]` centrally in `parse_multipart` for every upload handler; (2) `.tar`, `.tbz2`, and `.txz` archives now auto-extract (the upload handler's archive-suffix set was narrower than `extract_archive`'s, so those silently landed as raw files); (3) a rejected archive (zip-slip / zip-bomb / corrupt / too-many-members) now surfaces an error toast in the workspace panel instead of a misleading "Uploaded" success; (4) an in-workspace symlink subpath can no longer make the upload target `mkdir`/write outside the workspace root. Regression tests added.

### [`v0.51.207`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051207--2026-06-02--Release-GA-Edge-TTS-as-an-alternative-speech-engine)

[Compare Source](nesquena/hermes-webui@v0.51.206...v0.51.207)

##### Added

- Added an optional server-side **Edge TTS** speech engine (Microsoft neural voices) selectable in Settings → Preferences → TTS Engine, alongside the existing browser speech synthesis. The voice list switches to the Edge neural voices when selected. A new `POST /api/tts` endpoint streams the audio, gated by the same-origin CSRF check + session auth, a per-client rate limit, a 5000-character cap, and a voice allowlist. `edge-tts` is an optional dependency — the endpoint returns a clear install hint (503) when it isn't present, so existing installs are unaffected ([#&#8203;2931](nesquena/hermes-webui#2931), [@&#8203;liuqiangweb-svg](https://github.com/liuqiangweb-svg)).

### [`v0.51.206`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051206--2026-06-02--Release-FZ-workspace-file-upload--drag-and-drop-with-archive-extraction)

[Compare Source](nesquena/hermes-webui@v0.51.205...v0.51.206)

##### Added

- Workspace file panel: an **Upload** button and drag-and-drop that POST to a new `/api/workspace/upload` endpoint. Files land in the session workspace (resolved via the trusted-workspace guard), are de-duplicated with `-1`/`-2` suffixes, and archives (`.zip`/`.tar.*`) are auto-extracted into the target subdirectory with zip-bomb (size-cap + member-count-cap) and zip-slip (path-containment) protections. The extraction size cap is tunable via `HERMES_WEBUI_MAX_EXTRACTED_MB` (defaults to 10× the upload cap). Extraction errors are surfaced to the frontend instead of being silently swallowed, and the archive is removed on failure ([#&#8203;3104](nesquena/hermes-webui#3104), [@&#8203;antoniocarlos97ss](https://github.com/antoniocarlos97ss)).

### [`v0.51.205`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051205--2026-06-01--Release-FY-stage-hi1--workspace-syntax-highlighting--generated-image-cards--manual-title-regeneration)

[Compare Source](nesquena/hermes-webui@v0.51.204...v0.51.205)

##### Added

- Workspace file previews now render with syntax highlighting via Prism.js (already loaded for chat code blocks), covering common languages (Python, JS/TS, CSS, JSON, SQL, shell, and more) and degrading gracefully to plain text for unknown/plain files and when offline. The preview code surface uses a single uniform background across light and dark themes ([#&#8203;3337](nesquena/hermes-webui#3337), [@&#8203;mysoul12138](https://github.com/mysoul12138)).
- Generated local image artifacts now render as a clean inline image (with click-to-zoom lightbox) plus a hover/focus-revealed **Download** action overlaid on the image, served through authenticated `/api/media` URLs — matching the common AI-chat pattern of letting the image be the hero rather than wrapping it in a permanent card ([#&#8203;3220](nesquena/hermes-webui#3220), [@&#8203;AJV20](https://github.com/AJV20)).
- The session action menu can regenerate conversation titles on demand from the saved transcript, updating the sidebar without touching conversation chronology and syncing the new title through to state.db when Insights sync is enabled. The menu was also streamlined to a compact icon + label layout (descriptions move to hover tooltips). Closes [#&#8203;3106](nesquena/hermes-webui#3106) ([#&#8203;3223](nesquena/hermes-webui#3223), [@&#8203;AJV20](https://github.com/AJV20)).

### [`v0.51.204`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051204--2026-06-01--Release-FX-stage-batch17--projectsession-operations-honor-the-sessions-own-profile)

[Compare Source](nesquena/hermes-webui@v0.51.203...v0.51.204)

##### Fixed

- Project and session operations (project create/rename/recolor/delete/unassign, session move, and the profile chip label) now key on the session's own profile (`S.session.profile`) instead of the global active profile, so switching between sessions from different profiles no longer causes silent 404s, misleading chip labels, or project-picker entries from the wrong profile. The project picker also filters to the session's profile and surfaces an error toast on failure instead of a silent no-op ([#&#8203;3331](nesquena/hermes-webui#3331), [@&#8203;PINKIIILQWQ](https://github.com/PINKIIILQWQ)).

### [`v0.51.203`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051203--2026-06-01--Release-FW-stage-batch15--sticky-manual-unpin-for-streaming-chat-scroll)

[Compare Source](nesquena/hermes-webui@v0.51.202...v0.51.203)

##### Changed

- Streaming chat scroll now uses a sticky manual-unpin model: once you scroll up to read earlier content during a streaming response, the view stays put and no longer auto-follows the live tail until you scroll back to the bottom (near-bottom hysteresis on downward motion) or click the scroll-to-bottom control. Tool cards, token updates, and layout growth no longer re-pin the viewport after a reading pause. This replaces the [#&#8203;3250](nesquena/hermes-webui#3250) upward-intent timeout and supersedes the v0.51.199 proximity-re-pin ([#&#8203;3330](nesquena/hermes-webui#3330)), matching the streaming-scroll behavior of ChatGPT/Claude/Codex. Fresh streams reset the follow state on attach ([#&#8203;3343](nesquena/hermes-webui#3343), [@&#8203;pamnard](https://github.com/pamnard)).

### [`v0.51.202`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051202--2026-06-01--Release-FV-stage-batch14--filter-interrupted-recovery-control-text-from-visible-transcript)

[Compare Source](nesquena/hermes-webui@v0.51.201...v0.51.202)

##### Fixed

- Interrupted SSE-recovery control text (the synthetic `stale_interrupted_event` run-journal payload) is now kept out of the visible chat transcript instead of being replayed as a message: it's marked `recovery_control` on the backend and filtered across the `msgContent()` render path, the SSE settle/error handlers, and final transcript filtering, so platform-only control state no longer leaks into the conversation ([#&#8203;3321](nesquena/hermes-webui#3321), [@&#8203;franksong2702](https://github.com/franksong2702)).

### [`v0.51.201`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051201--2026-06-01--Release-FU-stage-batch13--colored-diff-lines-in-tool-card-snippets)

[Compare Source](nesquena/hermes-webui@v0.51.200...v0.51.201)

##### Added

- Tool-card result snippets that contain a unified diff now render with the same green/red/cyan diff coloring already used for diffs in chat messages (reusing the existing `.diff-block` styles), with an expand/collapse toggle that preserves the coloring. Non-diff snippets are unchanged ([#&#8203;3336](nesquena/hermes-webui#3336), [@&#8203;mysoul12138](https://github.com/mysoul12138)).

### [`v0.51.200`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051200--2026-06-01--Release-FT-stage-batch12--remote-gateway-health-probe--ephemeral-turn-field-preservation)

[Compare Source](nesquena/hermes-webui@v0.51.199...v0.51.200)

##### Fixed

- The Tasks/Cron panel no longer shows a spurious "Gateway not configured" banner in multi-container Docker deployments where the WebUI image doesn't ship the `gateway` Python package: agent-health now probes the remote gateway via `HERMES_API_URL` before falling back to the local `gateway.status` import. Closes [#&#8203;3281](nesquena/hermes-webui#3281) ([#&#8203;3312](nesquena/hermes-webui#3312), [@&#8203;Sanjays2402](https://github.com/Sanjays2402)).
- Force-reloading the active session (`loadSession(sid, {forceReload:true})`) no longer drops ephemeral turn fields (`_turnUsage`, `_turnDuration`, `_turnTps`, `_gatewayRouting`, `_statusCard`): the ephemeral-field carry-forward now reads the prior `S.messages` before it's reset, so the token-usage badge and status cards survive an external refresh. Closes [#&#8203;3306](nesquena/hermes-webui#3306) ([#&#8203;3313](nesquena/hermes-webui#3313), [@&#8203;Sanjays2402](https://github.com/Sanjays2402)).

### [`v0.51.199`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051199--2026-06-01--Release-FS-stage-batch11--pinned-scroll-recovery--inline-math-currency-false-positive)

[Compare Source](nesquena/hermes-webui@v0.51.198...v0.51.199)

##### Fixed

- Pinned chat now recovers its scroll position after a DOM rebuild: `_setMessageScrollToBottom` retries on the next layout frame, and `scrollIfPinned` re-pins when the pane has drifted more than 500px from the bottom, so a message-list rebuild no longer leaves a pinned conversation stranded mid-scroll. Closes [#&#8203;3319](nesquena/hermes-webui#3319) ([#&#8203;3330](nesquena/hermes-webui#3330), [@&#8203;jianongHe](https://github.com/jianongHe)).
- The `$...$` inline-math renderer no longer treats currency like `$1,000 xuống ~$95` as math: the opening `$` followed by a digit is now rejected (aligning with smd's `se()` guard), so dollar amounts render as plain text. Digit-leading inline math (e.g. `$2x = 4$`) should now use the LaTeX-style `\(2x = 4\)` or display `$$2x = 4$$` delimiters ([#&#8203;3311](nesquena/hermes-webui#3311), [@&#8203;toanalien](https://github.com/toanalien)).

### [`v0.51.198`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051198--2026-06-01--Release-FR-stage-batch10--custom-provider-reasoning-model-id-normalize--profile-skill-counts--run-adapter-RFC-slice)

[Compare Source](nesquena/hermes-webui@v0.51.197...v0.51.198)

##### Fixed

- Reasoning-effort detection for named `custom:*` providers now normalizes non-slash model ids before applying its fallback family heuristics, so separator variants such as `deepseek.v3.2`, `deepseek_v4_flash`, and vendor-namespaced ids like `vendor.deepseek.v3.2` resolve the same way as `deepseek-v4-flash`. The keyword fallback is now token-aware rather than substring-based, preserving names like `model-thinking-preview` without falsely enabling reasoning for unrelated prefixes such as `thinkinghub.llama-3.1-70b` ([#&#8203;3327](nesquena/hermes-webui#3327), [@&#8203;Carry00](https://github.com/Carry00)).
- Profile cards now show enabled vs compatible skill counts (computed with an 8s TTL cache that clears on profile switch) instead of a single ambiguous count. Closes [#&#8203;3339](nesquena/hermes-webui#3339) ([#&#8203;3341](nesquena/hermes-webui#3341), [@&#8203;b3nw](https://github.com/b3nw)).

##### Changed

- The [#&#8203;1925](nesquena/hermes-webui#1925) runtime-adapter RFC now marks the configured runner-client boundary as shipped in v0.51.188 ([#&#8203;3073](nesquena/hermes-webui#3073) / [#&#8203;3274](nesquena/hermes-webui#3274)) and defines the next Slice 4g gate for a supervised local runner process harness: real runner-owned `AIAgent` execution, restart/reattach proof, bounded runner health diagnostics, and no new WebUI runtime-surrogate globals ([#&#8203;3334](nesquena/hermes-webui#3334), [@&#8203;Michaelyklam](https://github.com/Michaelyklam)).

</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/782
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
…nesquena#3321)

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
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
…istic (over-filtered genuine interruptions)

Codex+Opus regression-gate finding: _isRecoveryControlMessage /
_streamRecoveryControlMessage fell back to matching
provider_details_label==='interruption details'. But a GENUINE 'Response
interrupted' card (Stop button, real provider crash) carries that exact label,
so the filter would drop a real user-facing interruption from the transcript on
the next render/restore — the inverse of the nesquena#3300 data-loss class. Require the
explicit server-set recovery_control marker; keep only the two fully-anchored
synthetic-text matches for pre-marker backward-compat. Adds a node-driven
regression test (revert-verified) asserting a label-only interruption card and a
real user turn stay visible while marker + strict text are filtered.
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
v0.51.202: filter interrupted-recovery control text from visible transcript (nesquena#3321)
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.

2 participants