v0.50.292 — 12-PR batch (multi-tab SSE + subpath routes + UX polish + 3 follow-ups)
12 PRs from 5 contributors. Closes #1578, #1583, #1584, #1595, #1613, #1620.
Hot path
-
Multi-tab SSE no longer splits stream tokens (#1598 by @Michaelyklam, closes #1584). Same session in two tabs was racing on a single
queue.Queue— one tab might receiveHwhile the other receivedallo. NewStreamChannelbroadcast class buffers events while no subscribers connected (so the first/reconnected tab still sees the stream tail) and broadcasts to all tabs once subscribed. Per Opus advisor: replay-inside-subscribe-lock prevents an event-ordering inversion when a 2nd tab subscribes mid-stream. -
Frontend routes work under subpath mounts like
/hermes/(#1601 by @Michaelyklam). Auth redirect Location, 401 redirects, fetch URLs, EventSource URLs, SMD module import — all switched from root-absolute to mount-relative anchored againstdocument.baseURI || location.href. Self-hosters running WebUI behind a reverse proxy or container ingress now work without Caddy/nginx rewrite workarounds. -
macOS keepalive actually works now (#1609 by @franksong2702, closes #1583). v0.50.289 added TCP keepalive, but on macOS the entire
tryblock was aborted byAttributeErrorfromTCP_KEEPIDLE(Linux-only constant), so SO_KEEPALIVE was never set on Mac. Now: TCP_NODELAY, then SO_KEEPALIVE in own try, then per-platform timing — Linux usesTCP_KEEPIDLE/INTVL/CNT, macOS usesTCP_KEEPALIVE.
UX
- Cross-source session continuations stay separate in the sidebar (#1602 by @ai-ag2026). A WebUI session continuing from a Telegram/CLI compression-chained parent stays as its own WebUI row instead of inheriting Telegram's title and source metadata.
- Paste no longer drops text when clipboard has both (#1622 by @s905060, closes #1620). Pasting from rich-text sources (Notes, Word, Slack, browser selections) attaches a rendered preview alongside
text/plain— handler used to swallow text and only attach the rogue image. Now defers to browser default text-paste when text is present; only intercepts on image-only (true screenshot paste). - Update banner shows tracked branch labels (#1605 by @ai-ag2026).
WebUI (origin/master): 0 updates, Agent (origin/main): 32 updatesinstead of the ambiguousAgent: 32 updates. - Forked session sidebar indicator is recognizable and less noisy (#1621 by @franksong2702, fixes #1613). Replaces
⑂glyph withgit-branchSVG, subtle until row hover, parent-title tooltip, removes hidden click-to-parent behavior. - Streaming markdown formats live segments under subpath mounts (#1600 by @Michaelyklam). First live segment now goes through
renderMd()in the SMD-unavailable fallback path.
Bug fixes / hardening
- Update compare URLs preserve git remote names ending in g/i/t (#1603 by @ai-ag2026).
'hermes-webui.git'.rstrip('.git')was producing'hermes-webu'—rstrip('.git')is a CHARACTER-CLASS strip, not a suffix strip. Now usesendswith('.git')slice. - Cron worker no longer silently ignores profile-context failures (#1608 by @franksong2702, closes #1578).
_run_cron_tracked()was wrappingcron_profile_context_for_home(...)intry/except Exceptionthat silently setctx=None, leaving the worker thread unpinned against process-globalHERMES_HOME. Same class as #1573. Lets exceptions propagate and kill the worker thread instead. _pending_started_attruthy-check fallback (#1599 by @Sanjays2402, closes our own follow-up #1595). Now handlesNone, missing-attr, and explicit0uniformly. Closes the v0.50.290 retro loop.- pytest config-path isolation (#1597 by @Michaelyklam). Hermes Agent sessions could leak
HERMES_CONFIG_PATH=~/.hermes/config.yamlinto pytest, letting onboarding/provider tests touch the developer's live config. Override fixed at conftest module load before product imports.
Tests
4117 → 4142 passing (+25). 0 regressions. Full suite ~125s.
Pre-release verification
- Opus advisor: SHIP verdict on full stage diff. Two SHOULD-FIX absorbed in-release per <20-LOC defensive policy:
api/config.pyStreamChannel.subscribe()replay-inside-lock (closes the ordering inversion race)static/sessions.js:1440gateway SSE probedocument.baseURI || location.hrefparity fix
- JS syntax check: 6 modified .js files clean.
- Browser API sanity: 11/11 endpoints OK on stage server.
- Conflict resolution:
static/index.htmlSMD import competing forms from #1600 (./static/...) and #1601 (static/...) — resolved to #1601's form (passes both PRs' tests via<base href>resolution).
Authors
- @Michaelyklam — 4 PRs (#1597, #1598, #1600, #1601)
- @ai-ag2026 — 3 PRs (#1602, #1603, #1605) — also reporter of #1579 in v0.50.291
- @franksong2702 — 3 PRs (#1608, #1609, #1621)
- @Sanjays2402 — 1 PR (#1599)
- @s905060 — 1 PR (#1622)
Three of 12 PRs close our own follow-up issues filed in prior releases (#1578, #1583, #1595).
Full changelog: https://github.com/nesquena/hermes-webui/blob/master/CHANGELOG.md