Skip to content

v0.50.279 — 8-PR batch from full PR sweep + Opus MUST-FIX caught#1534

Merged
nesquena-hermes merged 19 commits into
masterfrom
stage-279
May 3, 2026
Merged

v0.50.279 — 8-PR batch from full PR sweep + Opus MUST-FIX caught#1534
nesquena-hermes merged 19 commits into
masterfrom
stage-279

Conversation

@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Release v0.50.279 — 8-PR batch from full PR sweep

End-to-end PR sweep of all 16 open PRs. Routes:

Constituent PRs (8 merged)

Polish / UX (5 small)

PR Author LOC What
#1523 @franksong2702 2/2 Fork-indicator glyph: \u2482\u2442 (was PARENTHESIZED DIGIT FIFTEEN, now OCR FORK). Closes #1522.
#1519 @franksong2702 1/1 Onboarding API-key field stops losing focus during 400ms-debounced probe re-render — moved probe trigger from oninput to onblur. Closes #1503.
#1518 @franksong2702 5/2 Voice-mode pref toggle-off now stops the SpeechRecognition (was leaving recognizer running with no UI to stop it). Closes #1491.
#1516 @franksong2702 4/0 YAML code blocks render with newlines (Prism token spans had white-space: normal collapsing \n). Closes #1463.
#1517 @franksong2702 13/13 Consolidate __CACHE_VERSION____WEBUI_VERSION__ placeholder name (pure rename, no behavior change). Closes #1509.

State recovery + provider quota (3 backend)

PR Author LOC What
#1532 @ai-ag2026 38/3 Recover WebUI-origin state.db sessions when JSON sidecar missing (slice 1 of #1471). Removes hard-coded s.source != 'webui' predicate from read_importable_agent_session_rows().
#1525 @ai-ag2026 85/2 Clear stale runtime stream state proactively (slice 2 of #1471). New _clear_stale_stream_state() helper runs at /sessions/<id> GET boundary + before new turns. Frontend half: INFLIGHT[sid] cleared when server reports no active_stream_id.
#1526 @ai-ag2026 68/0 Forward configured max_tokens into AIAgent (was using provider-native ceilings → OpenRouter 402 quota errors). Adds 3 new quota-phrase classifiers. Refs #1524.

Test status

3936 → 3946 tests passing (+9 from constituent PRs + 1 conflict-marker regression guard added in-release per Opus MUST-FIX). Zero regressions.

Pre-release Opus advisor pass — MUST-FIX caught and absorbed

This release was nearly broken. Opus pass on stage-279 caught that the merge-conflict resolution between #1525 (cache-name manual suffix) and #1517 (rename) had never actually landedstatic/sw.js lines 10-14 still contained the literal <<<<<<< HEAD / ======= / >>>>>>> pr-1525 markers. The substring-based source-string tests still passed because __WEBUI_VERSION__ WAS in the file (just inside the conflict block).

Concrete impact pre-fix when shipped:

  • Service worker install event would throw on script load (SyntaxError: Unexpected token '<<')
  • SW would never reach activated state
  • Old SW (from v0.50.278) would keep controlling the page indefinitely
  • Frontend cache-bust pathway silently broken
  • The INFLIGHT[sid] clear (the frontend half of fix: clear stale WebUI stream state #1525's stale-stream cleanup) would never deliver to existing browsers

Resolution applied in stage: sw.js conflict markers removed, kept HEAD's CACHE_NAME = 'hermes-shell-__WEBUI_VERSION__' (post-#1517 rename) over the PR's -stale-stream-cleanup1 manual suffix. Natural version-token bump preserves the cache-invalidation guarantee.

Regression guard added in-release: new test_sw_js_has_no_merge_conflict_markers test scans for <<<<<<<, =======, >>>>>>> markers in static/sw.js. This couldn't happen silently again — a future merge that leaves conflict markers will fail CI.

Opus SHOULD-FIX deferred to follow-up

Filed as #1533: _clear_stale_stream_state() mutates session.active_stream_id outside the per-session lock (release pattern: hold STREAMS_LOCK only across the registry lookup, then mutate after release). Race window between registry-check and session-mutate could clobber a concurrent _handle_chat_start registration. Effect bounded (orphaned stream → retry, no data corruption). Opus explicitly said "probably fine to defer given the narrow window."

Diff stats

api/agent_sessions.py                    | 6 +-
api/routes.py                            | 30 +++-
api/streaming.py                         | 32 +++++-
static/boot.js                           | 7 +-
static/onboarding.js                     | 2 +-
static/sessions.js                       | ~20
static/style.css                         | 4 +
static/sw.js                             | 6 +-
tests/* (8 new files + edits)            | ~250 lines
CHANGELOG.md                             | +
ROADMAP.md                               | 2 +-
TESTING.md                               | 4 +-

Sweep methodology

Followed webui-full-pr-triage-release/references/extended-sweep-procedure.md Phases 0-9. Phase 0 fit-screen on all 16 open PRs, hold-readiness sweep, UX gate, merge queue build (small-first), stage merges (1 conflict resolved live), pre-release gate (clean state pytest + QA harness), Opus pass (MUST-FIX caught and absorbed), stamp + push.

Branches & cleanup

Frank Song and others added 19 commits May 3, 2026 14:54
#1463)

Prism's YAML grammar wraps tokens in <span> elements where white-space
defaults to normal, collapsing \n characters into spaces. The DOM
textContent is correct (confirmed by reporter's probe), so the bug is
purely CSS.

Force white-space:pre on .token elements inside language-yaml code
blocks for both .msg-body and .preview-md contexts.
__CACHE_VERSION__ (sw.js) and __WEBUI_VERSION__ (index.html) are
functionally identical — both resolve to quote(WEBUI_VERSION, safe='')
at request time. Two names exist for historical reasons (different files
added at different times).

Rename __CACHE_VERSION__ → __WEBUI_VERSION__ in:
- static/sw.js (CACHE_NAME + VQ constant + comment)
- api/routes.py (substitution string)
- tests/test_pwa_manifest_sw.py (all assertions)

Single canonical name. No behavior change — same ?v=vX.Y.Z query strings
on the same URLs.
When a user disables 'Hands-free voice mode' in Settings while voice
mode is active, the button hides but the SpeechRecognition keeps
running — the user can't stop it because the button is invisible.

Fix: _applyVoiceModePref() now checks if voice mode is active and
calls _deactivate() when the pref is toggled off. Move
_voiceModeActive declaration above the function to avoid TDZ.

Also removes a duplicate window._applyVoiceModePref assignment.
The onboarding wizard's API-key input calls _scheduleOnboardingProbe()
on every keystroke (oninput). When the 400ms-debounced probe completes,
_setOnboardingProbeState() calls _renderOnboardingBody() which rebuilds
the entire form — destroying and recreating the <input> element. The
user's focus and cursor position are lost.

On fast connections (localhost) the probe completes between keystrokes
so the bug window is narrow. On slow networks (VPN, corporate proxy,
cold-start vLLM) the re-render routinely lands mid-typing.

Fix: remove _scheduleOnboardingProbe() from the api-key input's
oninput handler. The probe still fires on:
- baseUrl input change (oninput + debounce, unchanged)
- api-key field blur (onblur, added)
- 'Test connection' button click (unchanged)
- nextOnboardingStep() before Continue (unchanged)

The baseUrl input retains the oninput probe because the UX trade-off
is acceptable there (text input preserves visible content on re-render).
\u2482 (PARENTHESIZED DIGIT FIFTEEN, displayed as ⒂) → \u2442 (OCR FORK, displayed as ⑂)

Fixes #1522
Clear persisted active_stream_id and pending runtime fields when the server no longer has the referenced live stream. Also drop browser-side INFLIGHT state when the server reports a session idle and bump the service-worker cache so the frontend fix is delivered.

Adds regression coverage for backend stale-stream cleanup, frontend inflight invalidation, and cache busting.
Read configured max_tokens from config.yaml, pass it into WebUI-created AIAgent instances when supported, and include it in the agent cache signature. Also classify OpenRouter quota phrasing such as more credits, can only afford, and fewer max_tokens.

Adds regression coverage for max_tokens propagation, cache signature isolation, and quota error classification.
…es (Prism token white-space) — closes #1463
…ely (refs #1471)

Merge conflict resolution: kept HEAD's `CACHE_NAME = 'hermes-shell-__WEBUI_VERSION__'` (post-#1517 rename) over PR #1525's `'hermes-shell-__CACHE_VERSION__-stale-stream-cleanup1'` manual suffix. The renamed placeholder still auto-bumps with each release through the `quote(WEBUI_VERSION, safe="")` substitution, so the manual `-stale-stream-cleanup1` suffix is no longer needed to force-update existing service workers — the natural version bump (v0.50.278 → v0.50.279) already invalidates the old cache via `caches.delete(k)` for `k !== CACHE_NAME` in the SW activate handler. No behavioral regression: the SW cache still bumps on this release, just via the canonical version-token path.

Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
Opus advisor flagged that the conflict-marker resolution from PR #1525's
merge had not actually landed — static/sw.js still contained the literal
<<<<<<< HEAD / ======= / >>>>>>> pr-1525 markers, which made the file
fail to parse as JavaScript even though the substring-based source-string
tests still passed (the __WEBUI_VERSION__ token was present, just inside
the conflict block).

Concrete impact pre-fix when shipped:
- Service worker install handler would throw on script load
- SW would never reach activated state
- Old SW (from v0.50.278) would keep controlling the page indefinitely
- Frontend cache-bust pathway silently broken
- The INFLIGHT[sid] clear in static/sessions.js (the frontend half of
  PR #1525's stale-stream cleanup) would never deliver to existing
  browsers because the new SW would never activate

Fix:
- Resolve sw.js conflict to keep CACHE_NAME = 'hermes-shell-__WEBUI_VERSION__'
  (the post-#1517 rename, with the manual -stale-stream-cleanup1 suffix
  dropped as redundant — natural version-token bump invalidates old caches).
- Add tests/test_pwa_manifest_sw.py::test_sw_js_has_no_merge_conflict_markers
  regression guard that scans for <<<<<<<, =======, >>>>>>> in sw.js source.
- Update tests/test_stale_stream_cleanup.py::test_service_worker_cache_
  bumped_for_frontend_fix_delivery to assert the canonical version-token
  CACHE_NAME pattern instead of the (now-removed) -stale-stream-cleanup1
  manual suffix.

3945 → 3946 tests passing (+1 from the new conflict-marker guard).

This issue would have shipped a broken service worker if Opus hadn't
caught it. The new test_sw_js_has_no_merge_conflict_markers test would
have flagged it earlier in the pipeline.

Caught-by: Opus advisor pass on stage-279 brief
Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
…sorbed

CHANGELOG, ROADMAP, TESTING bumped (3936 \u2192 3946).

8 constituent PRs:
- #1523 (@franksong2702) branch indicator codepoint fix
- #1519 (@franksong2702) onboarding API-key focus loss fix
- #1518 (@franksong2702) voice-mode toggle-off recognizer stop
- #1516 (@franksong2702) YAML newline CSS rules
- #1517 (@franksong2702) __CACHE_VERSION__ \u2192 __WEBUI_VERSION__ rename
- #1532 (@ai-ag2026) state.db WebUI session recovery
- #1525 (@ai-ag2026) stale stream state proactive cleanup
- #1526 (@ai-ag2026) max_tokens forwarding + OpenRouter quota classifier

Opus MUST-FIX absorbed: sw.js conflict-marker cleanup + regression guard.
Opus SHOULD-FIX deferred to follow-up #1533 (race in _clear_stale_stream_state).

2 closed as duplicates: #1528 (identical to #1517), #1529 (superseded by #1516).
1 maintainer-review label: #1531 (Asunfly stowaway change in force-push).
5 stay on hold: #1418 #1464 #1404 #1353 #1311.
@nesquena-hermes nesquena-hermes merged commit f8ed6da into master May 3, 2026
3 checks passed
@nesquena-hermes nesquena-hermes deleted the stage-279 branch May 3, 2026 16:27
@AllynSheep
Copy link
Copy Markdown

When will these changes be released?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment