Commit Graph

589 Commits

Author SHA1 Message Date
nesquena-hermes
629d4290ed fix(tests): restore conftest default model in test_default_model_updates_hermes_config — fixes CI ordering failure
The test was restoring original_model from /api/models, but after prior runs
the config.yaml model.default field could be stale, causing the restore to
bake in the wrong value. Fix: always restore to TEST_DEFAULT_MODEL (the
conftest-injected env value) for deterministic ordering-independent cleanup.

Also exposes TEST_DEFAULT_MODEL from _pytest_port.py so other tests that
mutate the default model can use it for clean teardown.

TESTING.md: update automated test count from 1353 to 1578.
2026-04-21 02:25:14 +00:00
nesquena-hermes
28b4777b5a fix(ui): hide duplicate close button in workspace header at mobile width (#783)
At the @media(max-width:900px) breakpoint both .close-preview and .mobile-close-btn were visible simultaneously. Since boot.js wires both to handleWorkspaceClose(), only the mobile-close-btn needs to show at that width. Adds .close-preview{display:none} to the 900px media block.

Fixes #781
2026-04-21 00:58:02 +00:00
nesquena-hermes
b6d335feaa perf: TTL cache for model list + incremental session index (#780)
Fixes AWS IMDS timeout on model dropdown. Incremental index writes.

Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-04-21 00:33:03 +00:00
nesquena-hermes
a7e8b1ab83 fix(streaming): eagerly release session lock in cancel_stream() (#778)
cancel_stream() now pops STREAMS/CANCEL_FLAGS/AGENT_INSTANCES and clears session.active_stream_id immediately after signalling cancel. Fixes sessions permanently stuck at 409 when the agent thread is blocked in a bad tool call. Session cleanup runs outside STREAMS_LOCK to preserve lock ordering.

Fixes #653

Co-authored-by: bergeouss <bergeouss@users.noreply.github.com>
2026-04-20 23:54:40 +00:00
nesquena-hermes
c34892be44 fix(streaming): guard newer AIAgent kwargs with inspect for hermes-agent compat (#775)
Uses inspect.signature() to check which params AIAgent accepts. Fixes #772.
2026-04-20 23:23:19 +00:00
nesquena-hermes
98cd318413 fix(sessions): surface get_cli_sessions() failures via logger.warning (#769)
Logs warnings instead of silently returning [] on DB errors. Fixes #634.
2026-04-20 23:13:54 +00:00
nesquena-hermes
94a04ddd40 fix(ui): persist session queue to sessionStorage across page refresh (#768)
Queued follow-up messages now survive page refresh. Persisted atomically in queueSessionMessage/shiftQueuedSessionMessage. On reload: if agent still active, queue is silently hydrated (done handler drains it); if idle, first entry is restored as a composer draft with a toast. Stale entries discarded.

Fixes #660
2026-04-20 23:04:09 +00:00
nesquena-hermes
765d8520d4 fix(streaming): quota error detection, error persistence, stream_end session_id fix (#767)
- quota_exhausted error type: distinguishes credit exhaustion from rate limits
- Streaming errors persisted to session file so they survive page reload
- _error flag excludes persisted errors from subsequent LLM API calls
- stream_end and title SSE events use original session_id (not s.session_id which rotates during context compaction)

Fixes #739, #652, #653
2026-04-20 22:48:19 +00:00
nesquena-hermes
76e602af25 feat: remove bubble_layout setting end-to-end (#777)
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>
2026-04-20 22:34:45 +00:00
nesquena-hermes
63f9b719bb fix(config): use Hermes config.yaml as single source of default model (#773)
Removes split-brain where WebUI Settings persisted default_model separately from Hermes runtime config.yaml. New POST /api/default-model endpoint writes to config.yaml. Existing saved values migrated on first load.

Fixes #761

Co-authored-by: aronprins <aronprins@users.noreply.github.com>
2026-04-20 22:12:01 +00:00
nesquena-hermes
f35ac3a727 fix(ui): streamline slash sub-argument autocomplete (#771)
Adds sub-argument suggestions for /model, /personality, /reasoning slash commands. /reasoning is now discoverable from the first slash. Keyboard navigation pre-selects the first item. Fixes bug where no-arg commands (/clear, /new, /stop, etc.) would loop the dropdown on selection.

Fixes #632

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-04-20 22:04:28 +00:00
Frank Song
0dd5d6f21c feat(ui): add sidebar density mode to session list (#764)
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>
2026-04-20 19:43:40 +00:00
nesquena-hermes
a8979f74d5 fix(ui): dark-mode user bubbles use subtle tint + thinking card collapsible — v0.50.111 (#759)
## Summary

Rebased on behalf of @aronprins from fork branch `codex/dark-user-bubbles`. Two asset-only commits (PR screenshot add/remove) were dropped; the two code commits are applied cleanly on top of current master (v0.50.110).

### What changed

**Dark-mode user bubbles** (`static/style.css`):
- `:root.dark` now overrides `--user-bubble-bg`/`--user-bubble-border` to `var(--accent-bg-strong)` (a 15% opacity tint) — keeps the bubble visually subdued in dark skins instead of a glaring bright accent fill
- Removes 6 per-skin `--user-bubble-text` hacks (ares, mono, slate, poseidon, sisyphus, charizard); text falls back to `var(--text)` which is already correct in dark mode
- Adds `--user-bubble-placeholder` token; edit-area box-shadow now uses `--focus-ring` instead of hardcoded `rgba(255,255,255,.15)`

**Thinking card collapsibility** (`static/ui.js` + `static/style.css`):
- `_thinkingMarkup()` now includes `onclick` toggle and chevron affordance, matching the compression reference card pattern
- `.thinking-card-header` gets `display:flex; gap:8px` for proper icon/label/chevron alignment

**Tests**: 2 new in `test_bugbatch_apr2026.py` (dark bubble token contract + no-per-skin-hack assertion), 2 updated in `test_ui_card_animation.py` (flex header layout + onclick pattern).

1520 passed. QA 20/20. Browser verified: dark mode bubble uses subtle tint, thinking card toggles correctly.

(credit: @aronprins)
2026-04-20 01:12:45 -07:00
nesquena-hermes
711d8bb6c0 fix(ui): hover-only footer chrome with timestamps for both user and assistant — v0.50.110 (fixes #680) (#758)
Squash merge of PR #717 — rebased on behalf of @franksong2702.

## What it does

Fixes #680. Footer chrome (timestamps, copy, edit, regenerate) is now hover-only for both user and assistant message rows, consistent throughout the conversation. The last assistant turn keeps cumulative usage visible at rest; timestamp and actions are revealed inline on hover in the same row.

Key changes:
- `static/ui.js`: new `_formatMessageFooterTimestamp()` (local timezone, cross-day fuller format); `timeHtml` no longer gated to user-only; last assistant usage moved from separate `.msg-usage` div to inline `.msg-usage-inline` span in the footer
- `static/style.css`: `.msg-foot-with-usage` class + rules; assistant footer opacity changed from 0.45 to 0 (hover-only); `:focus-within` alongside `:hover` for keyboard users
- `api/streaming.py`: `_restore_reasoning_metadata()` now preserves `_ts`/`timestamp` for unchanged historical messages
- `tests/test_sprint49.py`: 8 new tests covering rendering contract, hover CSS, timestamp preservation

Tests: 1518 passed. QA: 20/20. Browser verified. Reviewed and approved by @nesquena and @aronprins.
2026-04-20 00:53:19 -07:00
nesquena-hermes
a1c5c395e5 fix(tests): pin _cfg_mtime in _models_with_cfg to prevent ordering-dependent failure — v0.50.109 (fixes #754) (#756)
## Summary

Fixes the ordering-dependent test failure in `test_custom_provider_display_name.py` (issue #754).

**Root cause:** `_models_with_cfg()` patches `config.cfg` then calls `get_available_models()`. That function checks `config.yaml`'s mtime on every call — if it has changed since the last `reload_config()`, it calls `reload_config()` again, which reads from disk and silently overwrites the patch. Any test that writes `config.yaml` (e.g. via `save_settings()`) before this test runs changes the mtime and triggers the reload.

**Fix:** Pin `config._cfg_mtime` to the current `config.yaml` mtime before calling `get_available_models()`, then restore it in the `finally` block. This is the same pattern already used in `test_model_resolver.py` (lines 249, 393).

**Also restores `_cfg_mtime`** in the `finally` block so the patch leaves no side effects on subsequent tests.

## Tests

1510 passed — the previously-flaky test now passes regardless of which tests ran before it.

Closes #754
2026-04-20 00:39:24 -07:00
nesquena-hermes
69570ca77c release: v0.50.102–v0.50.108 batch (code blocks, utf-8, image URLs, deletion warning, PermissionError, Docker docs, kimi-k2.5) (#755)
## Batch release: v0.50.102 – v0.50.108

Seven self-built PRs reviewed and approved by @nesquena, now consolidated into a single release branch.

### Included fixes

| Version | PR | What it fixes |
|---|---|---|
| v0.50.102 | #746 | Code blocks lose newlines when not preceded by blank line (fixes #745) |
| v0.50.103 | #743 | `encoding='utf-8'` on `write_text()` in `api/profiles.py` — Windows `.env` detection (fixes #741) |
| v0.50.104 | #735 | Agent `MEDIA:localhost:*` image URLs rewritten to `document.baseURI` — remote users get working images (fixes #642) |
| v0.50.105 | #736 | Profile deletion warning strengthened: "permanently deleted, cannot be undone" across all 6 locales (fixes #637) |
| v0.50.106 | #738 | Catch `PermissionError` in `_signing_key()` — three-container Docker UID mismatch no longer crashes all HTTP requests |
| v0.50.107 | #737 | Docs: three-container UID/GID alignment guide in README + `HERMES_UID`/`HERMES_GID` forwarded in compose (fixes #645) |
| v0.50.108 | #742 | Add `kimi-k2.5` to Kimi/Moonshot provider model list (fixes #740) |

### Testing
- **pytest**: 1510 passed, 1 warning (1 pre-existing unrelated failure excluded)
- **QA harness**: 20/20 passed (`~/WebUI/scripts/run-browser-tests.sh`)
- **Browser**: layout, slash autocomplete width, edit button, image URL rewrite, profile deletion dialog all verified

All PRs reviewed and approved by @nesquena. Ready to merge and tag **v0.50.108**.
2026-04-20 00:26:55 -07:00
nesquena-hermes
aa767d28d0 fix(renderer): preserve newlines in code blocks during paragraph split (#745) (#746)
Squash merge PR #746: fix(renderer): preserve newlines in code blocks — v0.50.102

All tests pass (1510). Browser QA verified. Reviewed and approved by @nesquena.
2026-04-20 00:04:27 -07:00
nesquena-hermes
78c4f1e425 fix: null/empty session model must not trigger index rebuild — v0.50.101 (#753)
## Summary

Follow-up to #751/#752. Code review identified a case where `_normalize_session_model_in_place` could call `session.save()` (which triggers a full session index rebuild) for sessions with `model: null` or missing model field.

Root cause: `_resolve_compatible_session_model(None)` returns `(default_model, True)` when a default exists — which was interpreted as "changed, needs save." But there's nothing to correct for a session with no model; the default is just a fallback for display purposes, not a cross-provider correction worth persisting.

Fix: capture `original_model` before calling `_resolve_compatible_session_model`. Only call `session.save()` if `original_model` was non-empty and actually changed.

Adds a test asserting `save_calls == []` when `session.model is None`.

No behavior change for sessions with a real model (the primary use case of #751 is unaffected).
2026-04-19 23:44:46 -07:00
nesquena-hermes
81ba420716 fix: custom/unknown model prefixes must not be stripped on provider switch — v0.50.100 (#752)
## Summary

Regression fix for #751.

Models with custom or unrecognized prefixes (e.g. `custom-provider/my-model`, `test/import-model`) were being incorrectly replaced with the active provider default. Root cause: `_normalize_provider_id("custom-provider")` matched the `"custom"` prefix and returned `"custom"`, which ≠ `active_provider` → normalization fired.

Two-part fix:
1. Add `"custom"` and `"openrouter"` to the `model_provider` exclusion set in `_resolve_compatible_session_model` (parallel to the existing `active_provider` guard)
2. Return `""` for unknown prefixes in `_normalize_provider_id` so the `if model_provider` truthiness check safely short-circuits

Adds a regression test covering `custom-provider/`, `test/`, `my-local-llm/`, and `lmstudio-community/` prefixes.

## Tests

1499 passed, 0 failures (was 2 failures before this fix)
2026-04-19 23:27:24 -07:00
nesquena-hermes
7f16a41a31 fix: normalize stale session models after provider switch — v0.50.99 (#751)
## Summary

Rebased-on-behalf of @likawa3b (originally PR #748 — stale base).

Sessions can outlive provider changes. When an old session still points to a model from a previous provider (e.g. `gemini-3.1-pro-preview` after switching the agent to OpenAI Codex), starting a chat hits the wrong backend and fails silently.

This PR adds a lightweight normalization pass:
- `_normalize_provider_id()` maps common prefixes to canonical provider IDs
- `_resolve_compatible_session_model()` checks the session model's provider against `active_provider` and returns the default model if they differ
- `_normalize_session_model_in_place()` is called at GET `/api/session` — corrects and persists stale models once
- Chat start also normalizes via `_resolve_compatible_session_model()` and returns `effective_model` in the response
- `messages.js` applies `effective_model` back to the UI/localStorage/dropdown if set

Closes #748

## Tests

1498 passed (2 pre-existing ordering failures unrelated to this PR; 5 new tests added in `test_provider_mismatch.py`).

**Original author:** @likawa3b
2026-04-19 23:22:26 -07:00
nesquena-hermes
c68420d9aa fix(ui): constrain slash autocomplete width to composer — v0.50.98 (closes #633) (#750)
## 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
2026-04-19 23:17:00 -07:00
Frank Song
aa78175cca fix(ui): restrict edit to latest user message (#747)
fix(ui): restrict edit to latest user message (#747)

Only the latest user turn shows the pencil/edit affordance. Older user
messages remain read-only (copy + timestamp still work). Avoids the
misleading implication that historical messages can be lightly edited
when the actual action truncates the session and restarts the
conversation from that point.

Closes #744

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-04-19 23:11:49 -07:00
nesquena-hermes
da1fdca22c docs: fix docker-compose files + add three-container config — v0.50.96 (PR #708)
Fixes gateway port exposure, workspace path expansion, HERMES_WEBUI_STATE_DIR default, and adds three-container reference config with dashboard. All ports localhost-bound by default.
2026-04-19 07:10:05 +00:00
nesquena-hermes
067d96bb30 feat: add full Russian (ru-RU) localization — v0.50.95 (PR #713)
Full Russian locale — 389/389 English keys, Slavic plural forms, native Cyrillic. Rebased from PR #605 with rebase artifacts fixed. Login page Russian added to api/routes.py. Credits: @DrMaks22 (translation), @renheqiang (PR #605 author).

Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com>
Co-authored-by: renheqiang <renheqiang@users.noreply.github.com>
2026-04-19 06:47:24 +00:00
nesquena-hermes
e637965388 fix: robust mic toggle + Tailscale MediaRecorder fallback — v0.50.94 (PR #715)
Fixes and extends PR #683 (MatzAgent). recognition.start() is now a real call. _isRecording race guard added with correct reset in all paths. localStorage persistence of fallback flag. Closes #683.

Co-authored-by: MatzAgent <MatzAgent@users.noreply.github.com>
2026-04-19 06:28:14 +00:00
nesquena-hermes
66fbfbaa2b fix: gateway sync race condition + hybrid session data loss — v0.50.93 (PR #714)
Fixes and extends PR #676 (yunyunyunyun-yun). Race guard in sessions.js SSE handler; prefix-equality check in routes.py _handle_session_import_cli. Closes #676.

Co-authored-by: yunyunyunyun-yun <yunyunyunyun-yun@users.noreply.github.com>
2026-04-19 06:18:28 +00:00
nesquena-hermes
877a32f49c fix: XML tool-call leak + workspace empty-state + notification text — v0.50.92 (PR #712)
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.
2026-04-19 05:40:37 +00:00
nesquena-hermes
0386dc261a feat: slash command parity + skill autocomplete — v0.50.91 (PR #711)
Combines PR #618 (@renheqiang) slash command parity (/retry /undo /stop /title /status /voice) with PR #701 (@franksong2702) skill autocomplete. 1469 tests pass. Closes #460.

Co-authored-by: renheqiang <renheqiang@users.noreply.github.com>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-04-19 05:37:44 +00:00
nesquena-hermes
17e965b52f chore: block agent-local files from git (.claude/ CLAUDE.md AGENTS.md etc)
Removes stale webui-mvp AGENTS.md and expands .gitignore to block all agent-local context files from being committed. Fixes .claude/* → .claude/ (directory block).
2026-04-19 05:37:42 +00:00
nesquena-hermes
d3a686a266 fix(compress): prefer persisted reference handoff after completion — v0.50.90 (PR #699 by @franksong2702)
Fixes the /compress reference card showing only a short 3-line summary immediately after compression. Now prefers the persisted compaction message (full handoff) over the raw API summary, matching what is shown after page reload. Closes #695.
2026-04-19 04:29:07 +00:00
nesquena-hermes
3cd38b2b31 chore: add CHANGELOG entries for v0.50.88 and v0.50.89
Adds entries for #672 (model dropdown fix) and #700 (UTF-8 encoding fix). CHANGELOG-only change.
2026-04-19 04:23:38 +00:00
woaijiadanoo
d7071cd424 fix: explicit UTF-8 encoding on all read_text() calls — v0.50.89 (PR #700 by @woaijiadanoo)
Fixes config loading failures on Windows with non-UTF-8 default locales (GBK, Shift_JIS etc). All Path.read_text() calls in api/config.py and api/profiles.py now specify encoding='utf-8'.
2026-04-19 04:22:28 +00:00
nesquena-hermes
e0ad593801 fix(model dropdown): stop injecting default_model into unrelated providers — v0.50.88 (PR #672 by @franksong2702) 2026-04-19 04:18:43 +00:00
Frank Song
75e4f8b201 fix(model dropdown): stop injecting default into unrelated providers 2026-04-19 08:18:24 +08:00
nesquena-hermes
352354790f fix: streaming scroll override, Gemini 3.x models, read-only workspace, two-container UID — v0.50.87 (closes #677 #669 #670 #668)
- #677: renderMessages() and appendThinking() use scrollIfPinned() during stream; scroll threshold 80→150px; floating ↓ scroll-to-bottom button added
- #669: Gemini 3.1 Pro Preview, 3 Flash Preview, 3.1 Flash Lite Preview added to all provider sections; gemini-3.1-flash-lite-preview was the missing ID causing API_KEY_INVALID; GEMINI_API_KEY env var detection added
- #670: docker_init.bash guards chown/write-test with [ -w ]; :ro workspace mounts no longer crash startup
- #668: UID/GID auto-detect probes /home/hermeswebui/.hermes and HERMES_HOME before /workspace; two-container Zeabur/Compose setups inherit correct UID automatically
- 18 new tests; 1441 total passing
2026-04-18 17:09:59 +00:00
nesquena-hermes
5266ee26bd feat(ui): searchable model picker with provider group headers — v0.50.86 (PR #659 by @mmartial)
- 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>
2026-04-18 16:27:36 +00:00
nesquena-hermes
5c2840e2da fix(onboarding): remove CLI fast path from _provider_oauth_authenticated — fixes 4 test failures
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.
2026-04-18 07:23:16 +00:00
nesquena-hermes
75e6595e06 feat: add MiniMax M2.7 to fallback model list and fix env var detection — PR #650 by @octo-patch
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.
2026-04-18 07:18:20 +00:00
nesquena-hermes
20a5f48a1f fix(config): load provider models from config.yaml in model dropdown — PR #644 by @ccqqlo
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.
2026-04-18 07:14:03 +00:00
nesquena-hermes
ad6e76e48e chore: reorder CHANGELOG v0.50.77-v0.50.82 in descending order
Co-authored-by: nesquena-hermes <hermes@nesquena.com>
2026-04-18 07:09:06 +00:00
nesquena-hermes
b49de92893 feat(/compress): manual session compression with focus topic — closes #469 (PR #619 by @franksong2702)
POST /api/session/compress with optional focus_topic. Transcript-inline cards: command, running, complete (collapsible green), reference. /compact alias kept. Fixes: var(--green) undefined color, focus_topic 500-char cap. Independent review by @nesquena (4 passes).
2026-04-18 06:55:04 +00:00
nesquena-hermes
b1aa1cfa4d fix(title): auto-title extraction for tool-heavy first turns — closes #639 (PR #640 by @franksong2702)
The auto-title extractor now uses _looks_invalid_generated_title() to distinguish tool-call preambles from substantive agentic replies. Fixes _is_provisional_title() whitespace normalization. 5 regression tests added. Independent review by @nesquena (a553b2b+a0ca9fe).
2026-04-18 06:52:45 +00:00
nesquena-hermes
8c68ea8823 fix: skill panel auto-open, thinking scroll, nav icon alignment, Safari zoom — closes #643 #638 #636 #630 (PR #647)
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.
2026-04-18 06:50:14 +00:00
nesquena-hermes
ec48c482e2 fix(config): default model empty string — no unavailable OpenAI model for non-OpenAI users — closes #646 (PR #649)
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.
2026-04-18 06:46:43 +00:00
nesquena-hermes
bded1cf906 fix(streaming): strip Gemma 4 thinking token delimiter in all paths — closes #607
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.
2026-04-18 06:45:39 +00:00
Aron Prins
7cb5547056 feat(theme): replace color scheme system with light/dark + accent skins (PR #627 by @aronprins)
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>
2026-04-18 06:37:09 +00:00
nesquena-hermes
f3f23abd4e fix(csp): allow external https images in img-src — closes #608
Co-authored-by: Hermes Agent <agent@hermes>
2026-04-16 23:34:21 -07:00
nesquena-hermes
d6267f4d31 chore: CHANGELOG v0.50.75 + version badge (#620 test isolation fix) (#621)
Co-authored-by: Hermes Agent <agent@hermes>
2026-04-16 23:06:16 -07:00
nesquena-hermes
e7b8ab4d70 fix: harden test server isolation — HERMES_BASE_HOME + strip provider keys + mock _get_active_hermes_home in unit tests (#620)
Fixes the root cause of OPENROUTER_API_KEY being overwritten with test-key-fresh on every pytest run.

Three-layer fix:
1. Unit tests: mock _get_active_hermes_home in TestApplyOnboardingSetupGuard so .env writes land in /tmp, never ~/.hermes
2. Test server subprocess: add HERMES_BASE_HOME=TEST_STATE_DIR to hard-lock profile resolution inside the server process
3. Test server subprocess: strip real provider keys (OPENROUTER_API_KEY etc.) from the inherited env before server starts

Reviewed and approved by @nesquena. 1373 passed, 0 skipped.
2026-04-16 23:03:32 -07:00
nesquena-hermes
79428f93c6 fix: catch OSError from SETTINGS_FILE.exists() — Docker UID-mismatch 500 crash (#614)
Squash-merges PR #614. Fixes Docker 500-on-every-request crash from PermissionError in load_settings() (issue #570 follow-up).

Both SETTINGS_FILE.exists() call sites now catch OSError and fall back to defaults. Reviewer nits addressed: removed unused imports/var in tests, improved log message to say "inaccessible?" instead of "permission denied?". Rebased clean onto v0.50.73. 1373 tests passing, QA harness green.
2026-04-16 20:16:07 -07:00