Commit Graph

246 Commits

Author SHA1 Message Date
george-andraws b2477974c5 fix: make in-flight recovery storage quota-safe 2026-05-22 19:49:20 +00:00
fxd-jason 56575bd393 feat: sort configured/custom providers to top in model picker and settings 2026-05-22 16:13:46 +00:00
Hermes Agent 905b3eba5e Stage 398: PR #2700 — feat: make pinned session limit configurable (builds on shipped #2614 3-cap)
Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
2026-05-21 17:43:56 +00:00
Hermes Agent 7d3013245a Stage 398: PR #2687 — feat: hide suggestions preference (closes #2679)
Closes #2679

Co-authored-by: Michaelyklam <Michaelyklam@users.noreply.github.com>
2026-05-21 17:43:48 +00:00
nesquena-hermes 38933b288d Stage-394 follow-up: profile-switch reconciliation + a11y switch role + server-side chat/settings filter
Per deep-review verdict SHIP-WITH-FIXES on PR #2636:

1. Profile-switch reconciliation: _refreshProfileSwitchBackground now re-fetches
   /api/settings and re-applies hidden_tabs for the new profile. Without this,
   Profile A's hidden-tabs choice stayed in effect under Profile B until the
   user opened Settings → Appearance.

2. A11y: switched chips from role=button + aria-pressed to role=switch +
   aria-checked. The pressed/not-pressed wording confused screen-reader users
   because chip-off looks like the off state. Added role=group +
   aria-labelledby on the container, and a :focus-visible style on the chips.

3. Server-side belt-and-suspenders: api/config.py now strips 'chat' and
   'settings' from hidden_tabs at validation time, matching the client's apply-
   time filter. A tampered POST can no longer persist the forbidden values.

3 new regression tests added (chat/settings rejection, profile-switch wiring,
chip a11y attributes).

Co-authored-by: FrancescoFarinola <francesco.farinola@example.com>
2026-05-20 23:05:19 +00:00
Francesco Farinola 7f1feca3fe feat: sidebar tab visibility toggle in Settings > Appearance
Add chip row in Settings > Appearance that lets users toggle individual
sidebar/rail tabs on or off. Chat and Settings are always visible.

- Backend: hidden_tabs list setting with validation (no bool coerce)
- Frontend: pill chips that scan rail buttons, autosave via appearance
- Boot: _restoreTabVisibility IIFE applies hidden tabs before first paint
- i18n: 11 locales (label + description)
- Tests: 5 regression tests covering backend, frontend contracts,
  boot restore, i18n coverage, and settings session tracking
2026-05-20 22:57:36 +00:00
nesquena-hermes 4d8b8d0ffe Stage 393: PR #2633
# Conflicts:
#	CHANGELOG.md
2026-05-20 22:23:53 +00:00
Colin Chang 9c3e37d2ee fix: custom_providers models allowlist takes priority over live /v1/models fetch
Custom providers that have a curated models: list in config.yaml
(e.g. ZenMux gateways) should show ONLY those configured models in
the picker dropdown, not the full /v1/models catalog.

Before this fix, _named_custom_groups unconditionally called
_read_custom_endpoint_models() which would pull hundreds of models
from aggregator gateways and overwrite the user's curated list.

Now the build checks if the custom_provider entry has a non-empty
models dict/list in config.yaml — if so, it skips the live fetch
and uses only the configured models (same behavior as hermes-agent
model_switch.py Section 4 patch).

Closes: configure-model-list-should-be-authoritative
2026-05-20 20:22:11 +00:00
dobby-d-elf fd7212b014 Optimize profile switching and session list loading 2026-05-20 08:47:49 -06:00
Michael Lam c3eafa34f8 fix: surface custom provider model endpoint errors 2026-05-20 03:12:33 -07:00
nesquena-hermes a201401236 Stage 388: PR #2524 2026-05-20 00:17:48 +00:00
Eleanor Berger 4598adfd04 feat: add Geist Contrast skin 2026-05-20 00:09:06 +00:00
AJV20 54b6c38578 feat(health): expose WebUI stream runtime diagnostics 2026-05-19 22:48:10 +00:00
nesquena-hermes 54875f2110 Stage 385: PR #2550 2026-05-19 03:13:47 +00:00
Michael Lam 1827ea3efd fix: add Grok OAuth provider catalog support 2026-05-18 19:51:01 -07:00
Michael Lam 6917b9a0e7 fix: sanitize custom provider env hints 2026-05-18 15:18:20 -07:00
keyos b2e1bac149 fix(config): keep anonymous custom endpoints in picker when /v1/models probe fails
When an anonymous custom endpoint (bare base_url, not a named custom_providers[] entry) fails its /v1/models probe, the provider group was silently dropped from the model picker entirely. This made the endpoint unusable even when /v1/chat/completions would work fine.

The fix adds an elif branch: if pid == 'custom', a cfg_base_url is configured, but no models were returned by the probe, the group is still added with an empty model list. Users can then select the Custom group and type a model ID manually in the picker's free-form input.

Closes #2542.
2026-05-18 19:28:46 +00:00
nesquena-hermes c7badae039 Stage 383: PR #2515
# Conflicts:
#	CHANGELOG.md
2026-05-18 16:44:35 +00:00
Michael Lam 037652308d fix: load remote models for named custom providers 2026-05-18 01:08:09 -07:00
junjunjunbong 3a53592107 Add previous messaging session controls 2026-05-17 21:27:32 -07:00
nesquena-hermes b861422045 Stage 379: PR #2473 2026-05-17 23:35:18 +00:00
nesquena-hermes 935d9e6402 Stage 379: PR #2461
# Conflicts:
#	CHANGELOG.md
2026-05-17 23:35:18 +00:00
ts2111 64db8bd794 fix: support /model alias switch for cross-provider custom models
Backend (api/config.py):
- resolve_model_provider(): check custom_providers for prefix match
  BEFORE the config_base_url branch. Previously, providers with a
  base_url set (e.g. deepseek) would catch all slash-delimited model
  ids and return the config provider, preventing custom provider
  routing.
- get_available_models(): include model aliases in response so the
  frontend can resolve them on /model commands.

Frontend (static/commands.js):
- cmdModel(): resolve aliases by fetching /api/models before fuzzy
  matching the dropdown.
- Add bare-model fallback when the alias resolves to a slash-delimited
  provider/model id (e.g. "deepseek/deepseek-v4-flash").
- Add cross-provider fallback: when the model is from a custom provider
  not in the active provider dropdown, call /api/session/update directly
  with the provider/model id and provider override.
2026-05-17 21:22:06 +02:00
nesquena-hermes a2920c99bc Stage 376: PR #2466
# Conflicts:
#	CHANGELOG.md
2026-05-17 16:42:11 +00:00
Frank Song 7a53fd4542 Clarify compact activity timeline semantics 2026-05-17 23:03:56 +08:00
starship-s aecad0f427 [verified] Fix WebUI memory session lifecycle commits 2026-05-17 03:30:06 -06:00
Michael Lam cdbb785037 fix: invalidate model cache on catalog changes 2026-05-16 22:24:12 -07:00
nesquena-hermes e9c6b7f06c Stage 375: PR #2432 — feat(theme): add Catppuccin appearance skin (Latte + Mocha palettes) by @Michaelyklam (closes #2426)
Co-authored-by: Michael Lam <michael@example.local>
2026-05-17 03:35:19 +00:00
nesquena-hermes 54f1a2acae Stage 373: PR #2415 — fix: ignore provider config flags in model picker by @Michaelyklam (fixes #2399) 2026-05-17 00:21:50 +00:00
nesquena-hermes 3480e75e13 Stage 372: PR #2413 — feat(quota-chip): add Settings toggle, flip default to off 2026-05-16 23:05:09 +00:00
nesquena-hermes a4ab7d4d27 Stage 371: PR #2409 — Stuck-PR sweep: salvage RTL chat from #1721 + override quota chip from #2082 by @malulian and @ai-ag2026
Co-authored-by: malulian <malulian@users.noreply.github.com>
Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
2026-05-16 22:04:56 +00:00
BonyFish f82a763dfb fix: support list format for custom_providers.models in model dropdown
The get_available_models() function only handled dict-format models
(`{model_id: {}}`) for custom_providers entries, silently dropping
models specified as YAML lists (`[model1, model2]`) or list of dicts
(`[{id: ..., label: ...}]`).

This caused users who define their custom providers with list-format
model declarations to see zero or incomplete model entries in both
Settings → Preferences → Default Model dropdown and the chat
interface model picker.

The fix adds an `elif isinstance(_cp_models_dict, list)` branch with
support for three list sub-formats:
  - Plain string list: `models: [m1, m2]`
  - Dict list: `models: [{id: m1, label: ...}]`
  - Mixed: `models: [m1, {id: m2}]`

Refs: hermes-agent issue where YAML list models were invisible
2026-05-16 05:43:09 +00:00
Hermes Agent b293bf8bc5 stage-364: Opus-caught live SSE event_id fix (side-channel approach)
Replace the earlier frontend-reset approach with a backend side-channel
approach that preserves the queue (event, data) tuple shape.

Problem (Opus catch):
- Live SSE frames emitted by _sse() in api/streaming.py:2296 carried no
  'id:' field. Only journal-replay frames (via _sse_with_id) emitted IDs.
- Frontend's _lastRunJournalSeq cursor stayed at 0 during live streaming.
- Mid-stream error → reconnect-to-replay arrived with after_seq=0.
- Server replayed every journaled event from seq 1.
- assistantText (closure-scoped) had accumulated all live tokens already
  → double-rendered output.

Fix:
- api/config.py: STREAM_LAST_EVENT_ID: dict = {} module-level dict.
- api/streaming.py put(): capture journal event_id, write to
  STREAM_LAST_EVENT_ID[stream_id]. Keep queue tuple as (event, data).
- api/routes.py _handle_sse_stream: read STREAM_LAST_EVENT_ID[stream_id]
  at emit time, use _sse_with_id when set.
- api/streaming.py finally block: pop STREAM_LAST_EVENT_ID for cleanup.

Why side-channel instead of 3-tuple:
- Earlier attempt (queue tuple → (event, data, event_id)) broke 4 existing
  tests: test_cancel_interrupt, test_sprint42, test_sprint51,
  test_issue1857_usage_overwrite. These all unpack 'event, data = q.get()'.
- Frontend-reset approach (reset assistantText before replay) broke 3
  other tests: test_smooth_text_fade, test_streaming_markdown,
  test_streaming_race_fix. _wireSSE must NOT reset accumulators because
  legacy reconnect doesn't replay events; only journal-replay does.

Side-channel preserves both invariants:
- Queue contract stays (event, data) — legacy consumers unbroken.
- Frontend accumulators stay alive on _wireSSE — legacy reconnect unbroken.
- Live SSE emits 'id:' so the journal cursor advances correctly.

6 regression tests added in test_stage364_opus_live_sse_event_id.py.
1 existing test (test_run_journal_streaming_static.test_streaming_journals_sse_events_before_queue_delivery) updated to be tuple-shape-agnostic.

Test results:
- Full pytest: 5713 passed, 10 skipped, 1 xfailed, 2 xpassed, 0 failed
- Previously-failing 5 tests: ALL PASS
- 6 new regression tests: ALL PASS
2026-05-16 03:58:54 +00:00
Hermes Agent 5ab2ebed2e Merge pull request #2322 into stage-362
fix: route endpoint-discovered Ollama models correctly (Michaelyklam)
2026-05-15 22:55:30 +00:00
Michael Lam 2fdc1d99e2 fix: expand legacy Hermes CLI toolset alias 2026-05-15 13:08:22 -07:00
Michael Lam 512c401e8a fix: route endpoint-discovered Ollama models correctly 2026-05-15 12:16:23 -07:00
Hermes Agent ad76db8651 Merge pull request #2291 into stage-359
feat: add Nous Research skin (linuxid10t)
2026-05-15 14:55:10 +00:00
linuxid10t b2d4f13c5b feat: add Nous Research skin
Adds a cold steel-blue/monospace skin inspired by nousresearch.com:
- Steel-blue accent (#4682B4) replacing warm gold
- Monospace typography (SF Mono, Roboto Mono, Courier New)
- Sharp corners, technical dashed borders
- Dark navy palette (#0A0E14) for dark mode

Files changed:
- static/style.css — Nous skin CSS variables and component overrides
- static/boot.js — Nous skin entry in _SKINS array
- static/index.html — nous in inline skin validation list
- api/config.py — nous + sienna in server-side _SETTINGS_SKIN_VALUES
2026-05-15 00:28:34 -05:00
Yao Ning b1bf800fa4 feat: make upload size limit runtime-configurable
Signed-off-by: Yao Ning <zay11022@gmail.com>
2026-05-15 11:39:23 +08:00
Hermes Agent ec689e32be Merge pull request #2099 into stage-358
feat: add opt-in streaming text fade (dobby-d-elf, off-by-default)
2026-05-14 21:27:52 +00:00
Hermes Agent 612480ce56 Merge pull request #2165 into stage-358
feat(providers): show pooled Codex quota status (starship-s, post-review follow-up)
2026-05-14 21:27:51 +00:00
Michael Lam d246bf2654 fix: canonicalize configured provider model lookup 2026-05-14 09:05:13 -07:00
Frank Song e2f319d730 Add extra large font size option 2026-05-14 11:09:21 +08:00
Jordan SkyLF bec21eafa0 Add What's New summary toggle 2026-05-13 15:53:01 -07:00
Hermes Agent ca82f60144 Merge pull request #2191 into stage-350
fix(auth) 1/3: thread-safe login rate limiter + PBKDF2 key separation + transparent migration (lucasrc)
2026-05-13 20:41:36 +00:00
Michael Lam 1e17760a04 Fix opencode-go provider overlap routing
Closes #1894
2026-05-13 12:13:37 -07:00
Lucas Coutinho 2bcf411519 fix(auth): invalidate password hash cache in save_settings() on password change 2026-05-13 14:08:37 -03:00
Hermes Agent 8060b2ba3a Merge pull request #2179 into stage-347
fix(config): preserve nvidia/ prefix on NVIDIA NIM (closes #2177)

Self-built. nesquena APPROVED with extensive end-to-end trace including
cross-tool agent CLI verification and 12-shape behavioural harness.
2026-05-13 07:33:45 +00:00
nesquena-hermes 9b1d786459 fix(config): preserve nvidia/ prefix on NVIDIA NIM (closes #2177)
Move the `_PORTAL_PROVIDERS` guard in `resolve_model_provider()` to run
BEFORE the `prefix == config_provider` strip branch. The guard was added
for NVIDIA (along with the Nous portal cases in #854 / #894) but was
placed after the strip, so it never fired when `config_provider == "nvidia"`
and the model id started with `nvidia/`.

For `model_id="nvidia/nemotron-3-super-120b-a12b"`,
`config_provider="nvidia"`:
  - prefix = "nvidia", bare = "nemotron-3-super-120b-a12b"
  - prefix == config_provider → True → strip branch returned bare name
  - `_PORTAL_PROVIDERS` guard never reached
  - bare "nemotron-3-super-120b-a12b" sent to NVIDIA NIM → HTTP 404

NIM requires the full namespaced path. The fix moves the portal guard
to run first, so all portal providers (Nous, OpenCode-Zen, OpenCode-Go,
NVIDIA NIM) always preserve the full `provider/model` id regardless of
whether the prefix happens to equal the provider name.

This also closes a latent symmetric bug for the Nous case if a
`nous/<model>` id ever existed in the catalog.

Test plan:
- New `tests/test_issue2177_nvidia_prefix_preservation.py` covers:
  - nvidia/nemotron-... under nvidia (the reported case)
  - cross-namespace qwen/ and meta/ under nvidia (regression pin)
  - every static nvidia model in `_PROVIDER_MODELS` resolves to itself
  - latent nous/<model> under nous (structural ordering pin)
  - non-portal providers (anthropic) still strip — fix doesn't over-correct
- Existing portal-routing suites (test_nous_portal_routing.py,
  test_issue895_894_nous_prefix.py) continue to pass.
- Full test suite: 5320 passed, 4 skipped, 3 xpassed.

Reported on Discord by @vishnu (Nathan forwarded as #2177).
2026-05-13 07:05:57 +00:00
MrFant a4417d11f9 fix: handle dict model entries in provider models list
When a provider's 'models' config contains dicts (e.g. {"id": "x", "label": "y"})
instead of plain strings, _apply_provider_prefix() crashes with:
  AttributeError: 'dict' object has no attribute 'startswith'

This happens because the list comprehension at line 3505 passes the raw dict
as the model ID. The fix extracts 'id' and 'label' from dict entries while
keeping string entries as-is.

Fixes the /api/models and /api/onboarding/status 500 errors.
2026-05-13 13:49:40 +08:00