* fix: dynamic version badge — read from git tag, never hardcoded
The settings panel showed v0.50.87 and the HTTP Server: header said
HermesWebUI/0.50.38 — both hardcoded strings that drift further behind
with every release because there was no mechanism to keep them in sync.
Changes:
- api/updates.py: add _run_git() (moved before _detect_webui_version),
_detect_webui_version(), and WEBUI_VERSION module constant resolved
once at import time via 'git describe --tags --always --dirty'.
Fallback chain: git → api/_version.py → 'unknown'.
- api/routes.py: inject webui_version into GET /api/settings response
so the frontend can read it without a separate API call.
- static/panels.js: loadSettingsPanel() populates .settings-version-badge
from settings.webui_version — one line after the existing api() call.
- static/index.html: replace stale hardcoded 'v0.50.87' with '—'
placeholder; JS overwrites it as soon as the settings panel opens.
- server.py: replace hardcoded 'HermesWebUI/0.50.38' server_version with
'HermesWebUI/' + WEBUI_VERSION.lstrip('v') — stays in sync automatically.
- Dockerfile: add ARG HERMES_VERSION=unknown and write api/_version.py
so Docker images (where .git is excluded) still show the correct tag.
- .github/workflows/release.yml: pass build-args: HERMES_VERSION=${{ github.ref_name }}
to the Docker build step on tag pushes.
- .gitignore: exclude api/_version.py (generated by Docker/CI, never committed).
No manual 'update the version badge' step is required going forward.
Tagging is sufficient — the badge and HTTP header update automatically.
Tests: 18 new tests in tests/test_version_badge.py covering the full
resolution chain, /api/settings injection, HTML placeholder, JS wiring,
and server.py import. 1596 tests pass total.
* fix: address review feedback on PR #790
- api/updates.py: replace exec() with regex parse for api/_version.py
(no supply-chain risk from build artifact; exec unnecessary for one assignment)
- api/updates.py: cap git describe timeout at 3s (was 10s — import-time
stall on NFS/.git would block server startup unnecessarily)
- server.py: lstrip('v') → removeprefix('v') (lstrip strips chars not prefix)
- server.py: emit bare 'HermesWebUI' when version is 'unknown' rather than
'HermesWebUI/unknown' (log aggregators expect semver-ish suffix or none)
- CHANGELOG.md: add v0.50.124 entry for this user-visible change
- tests: rename exec-error test to reflect regex behaviour; add tests for
removeprefix usage and unknown-version header guard (1598 tests total)
---------
Co-authored-by: nesquena-hermes <hermes@nesquena.com>
Removes the bubble_layout toggle from Settings, all persistence, CSS, i18n strings, and the UI docs demo. The CSS was already effectively dead. Users with a saved bubble_layout value in settings.json get a clean migration via _SETTINGS_LEGACY_DROP_KEYS.
Credit: @aronprins (PR #760 / #777)
Co-authored-by: aronprins <aronprins@users.noreply.github.com>
Adds compact/detailed toggle for the session list sidebar. Compact is the default (no behavior change for existing users). Detailed mode shows message count and model; profile names only appear when mixing sessions across profiles.
Fixes#673
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
## Summary
Rebased-on-behalf of @franksong2702 (originally PR #728 — had CHANGELOG conflict after #747 merged).
Moves `#cmdDropdown` from outside `composer-box` to inside it, so the `position:absolute` anchor is scoped to the composer width rather than the full chat panel. CSS updated to use `bottom:calc(100% + 4px)` and `width:auto;max-width:100%` for clean upward positioning.
Closes#633
## Changes
- `static/index.html` — moved `cmd-dropdown` div inside `composer-box`
- `static/style.css` — updated `.cmd-dropdown` positioning (remove `margin-bottom`, use `bottom:calc(100% + 4px)`, add `width:auto;max-width:100%`)
- `tests/test_sprint50.py` — 2 new structural tests verifying DOM position and CSS rules
## Tests
1493 passed, 1 warning (2 new tests added)
**Original author:** @franksong2702
Strips <function_calls> XML from assistant messages before rendering, adds workspace file panel empty-state messages, and changes notification description from 'tab' to 'app'. 16 new tests. Fixes#702, #703, #704.
- Live search input in model dropdown (filter by name or ID)
- Provider group headers preserved in filtered view
- Clear button, Escape-to-close, No models found empty state
- i18n EN/ES/zh-CN strings
- CSS uses var(--accent) consistent with current theme system
- zh-CN double-escape fix included
- Provider headers regression fix included
- 1423 tests pass
Co-authored-by: mmartial <mmartial@users.noreply.github.com>
The hermes_cli fast path ignored hermes_home, returning True from real system auth for OAuth providers. Removed — auth now scoped to hermes_home/auth.json only. 1423 passed, 0 failed.
MiniMax M2.7/highspeed added to _FALLBACK_MODELS. MINIMAX_API_KEY and MINIMAX_CN_API_KEY added to env scan tuple so os.environ is checked. 11 tests. Independent review by @nesquena confirmed correct, needed rebase only.
Providers in config.yaml with explicit models: list were silently ignored. Fix extends the model-list builder to check cfg.providers[pid].models, covering both dict and list formats. Also includes providers only in config.yaml (not _PROVIDER_MODELS). 5 regression tests added. Independent review by @nesquena.
Four self-contained CSS/JS fixes: skill click auto-opens workspace panel (ensureWorkspacePreviewVisible before api call), thinking card body scrolls when open (overflow-y:auto), nav tab icons properly centered (display:flex), Safari iOS zoom prevented (textarea 14px->16px). Independent review by @nesquena confirmed all four correct.
DEFAULT_MODEL now defaults to "" instead of "openai/gpt-5.4-mini". Guards added in model-list builder so empty default does not create blank model entries. Adds 3 tests in test_issue646.py. Independent review by @nesquena.
Fixes <|turn|>thinking delimiter (was wrong as <|turn>thinking) in api/streaming.py, static/messages.js, and static/ui.js. Adds 13 regression tests. Independent review by @nesquena.
Independent review by @nesquena confirmed all blockers resolved. Theme×skin two-axis system replaces old monolithic color schemes. Closes#627. Co-Authored-By: aronprins <aronprins@users.noreply.github.com>
Squash-merges PR #613. Adds favicon to the app (was missing entirely — blank tab icon). 1371 tests passing, QA harness green. Review by independent agent (see PR comments). Follow-up commit addresses all three reviewer notes: hoisted _STATIC_MIME to module scope, fixed charset=utf-8 being appended to binary MIME types, confirmed correct MIME types on all three favicon formats.
Co-authored-by: tiansiyuan <tiansiyuan@users.noreply.github.com>
Fixes#569: docker_init.bash auto-detects WANTED_UID/WANTED_GID from the mounted /workspace UID at Phase 1, before usermod remaps the container user. On macOS, host UIDs start at 501 — the default 1024 caused an empty workspace. Guards against root (0). Fallback 1024 preserved. Closes#579: topbar already correctly filters tool messages; sidebar count removed in #584. Regression tests added. Reviewed and approved by @nesquena. 1347 tests passing.
Fixes four bugs + locks in one existing fix with regression tests.
Closes#594 (light theme dialogs), #576 (workspace panel snap), #585 (stale model list after CLI change), #567 (docker-compose macOS UID docs). Confirms and tests #590 (transcribing spinner already present).
Reviewed and approved by @nesquena. 1340 tests passing.
Squash-merges feature from PR #588 by @vcavichini. Dynamic <base href> injection + api() helper slash-stripping enables deploying hermes-webui behind a reverse proxy at any subpath without configuration. Also fixes pre-existing bug: api/upload was using location.origin instead of location.href (closes#596). Co-authored-by: vcavichini <vcavichini@users.noreply.github.com>
Fixes two SKIP_ONBOARDING bugs and eliminates 10 permanently-skipped integration tests.
- SKIP_ONBOARDING=1 now honoured unconditionally (no longer gated on chat_ready)
- apply_onboarding_setup refuses to write config/env files when SKIP_ONBOARDING is set
- TestMediaEndpointIntegration (6) and TestOnboardingGateIntegration (4): collection-time
skip guards removed; server reachability checked at runtime with fail() not skip()
Tests: 1327 passed, 0 skipped.
Admin merge — self-built PR, Nathan authorized full merge process in session.
Admin merge — docs-only follow-up: CHANGELOG entry, version badge v0.50.64, one new test. No code logic. Nathan authorized end-to-end merge in session.
Squash-merges PR #578 (rebased from #574 by @renheqiang + #575 by @nesquena-hermes). MCP server toolsets now included in WebUI sessions; onboarding wizard no longer fires for non-standard providers. 1331 tests pass. Nathan override applied for self-built #575.
Changes _pending from a single overwriting dict value to a list,
so parallel tool calls each get their own approval slot.
api/routes.py:
- Wraps submit_pending() to append to a list and assign a stable
approval_id (uuid4) to each entry.
- _handle_approval_pending() returns the first queued entry plus
pending_count so the UI can show '1 of N'.
- _handle_approval_respond() pops by approval_id (falls back to
oldest entry for backward-compat with old clients).
- Backward-compat: legacy single-dict values in _pending are
handled without crashing.
static/messages.js:
- respondApproval() sends approval_id in the POST body.
- showApprovalCard() accepts pendingCount, shows '1 of N pending'
counter when multiple approvals are queued.
- _approvalCurrentId tracks the approval_id of the displayed card.
- Poll loop passes pending_count to showApprovalCard.
static/index.html:
- Adds approvalCounter element for the '1 of N' display.
tests/test_approval_queue.py:
- 14 tests: static-analysis checks (Python + JS + HTML),
functional tests that inject two simultaneous approvals and
verify both are surfaced and independently resolvable.
The Prism CSS was hardcoded to prism-tomorrow (dark-only), so code
blocks stayed dark even when switching to Light or other non-dark themes.
- Add id='prism-theme' to the <link> element for runtime lookup
- In _applyTheme(), swap href between prism-tomorrow (dark) and
prism (light) based on resolved theme
- Skips DOM write when the target href is already active
Fixes#505
Synthesized from PRs #506, #509, #514 (all by armorbreak001 and cloudyun888).
Implementation:
- static/index.html: flicker-prevention head script resolves 'system' to
'dark'/'light' via matchMedia before first paint. Adds 'System (auto)'
as first option in theme picker. onchange calls _applyTheme().
- static/boot.js: new _applyTheme(name) helper — resolves 'system' via
matchMedia, sets data-theme, registers a MQ change listener so the UI
tracks OS switches live. loadSettings() now calls _applyTheme() instead
of direct data-theme assignment.
- static/commands.js: adds 'system' to valid /theme command names,
delegates apply to _applyTheme().
- static/panels.js: _settingsThemeOnOpen reads from localStorage (preserves
'system' string, not the resolved 'dark'/'light'). _revertSettingsPreview
calls _applyTheme() so reverting to 'system' correctly re-enables OS tracking.
- static/i18n.js: cmd_theme description now lists 'system' first in all 5
locales (en, es, de, zh-Hans, zh-Hant).
Design choices vs submitted PRs:
- No separate system-theme.js file (unnecessary indirection).
- matchMedia listener does NOT POST to /api/settings (OS can change rapidly;
persisting on every OS switch would hammer the server).
Co-authored-by: armorbreak001 <armorbreak001@users.noreply.github.com>
Co-authored-by: cloudyun888 <cloudyun888@users.noreply.github.com>