Skip to content
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

## [Unreleased]

## [v0.51.79] — 2026-05-16 — Release BC (stage-372 — 5-PR batch — text-mode image history fix + Activity-group compression boundary + named custom provider routing + quota chip Settings toggle + RFC docs)

### Added

- **PR #2413** (self-built follow-up to v0.51.78's #2082, closes the quota-chip default-on regression) — New "Show provider quota chip in composer" checkbox in Settings → Preferences, default off. When disabled (the new default), the chip is hidden at all viewports and the `/api/provider/quota` fetch is skipped entirely. When enabled, the existing `@media (max-width:1399.98px)` gate from stage-371 still restricts the chip to wide desktops only. Per Nathan's directive 2026-05-16 immediately after stage-371 shipped — users get explicit agency over an ambient composer-chrome element. Wired through `api/config.py` `_SETTINGS_DEFAULTS`, `static/boot.js`, `static/panels.js` round-trip, `static/ui.js` short-circuit-when-disabled, `static/index.html` Settings field, and 11 locales in `static/i18n.js`.

### Fixed

- **PR #2406** by @Michaelyklam (fixes #2398) — The fallback synchronous `POST /api/chat` route now passes the active WebUI config into the conversation-history sanitizer, so text-mode providers do not receive historical native `image_url` content parts when direct API callers use the legacy chat endpoint. This brings the sync route in line with the streaming chat path fixed for #2297.
- **PR #2408** by @Michaelyklam (fixes #2404) — Auto-compression cards now close the current live Activity burst before rendering, so post-compression tools start a fresh `Activity` row instead of joining the pre-compression tool group across a real timeline/context boundary. Adds a `closeCurrentLiveActivityGroup()` helper that clears the `data-live-activity-current` marker before `appendLiveCompressionCard()` inserts the compression card. Resolves the DEFER from stage-370 Opus advisor review of PR #2390.
- **PR #2411** by @Michaelyklam (fixes #2405) — Named `custom:*` providers no longer lose vendor-prefixed model selections when the static model picker has not hydrated that model yet. The frontend now treats named custom providers as routable aggregators for both mismatch-warning suppression and missing-dropdown fallback, and live-fetched models keep explicit `@custom:name:` provider context so selections persist instead of snapping back to the configured default.

### Documentation

- **PR #2407** by @Michaelyklam — Document the #1925 runtime-adapter gate update: Slice 1 run-journal replay has now passed a 100-trial synthetic replay/restart validation pass on current `origin/master`, #2313's selected-session chat SSE cap is shipped, and Slice 2 is ready for a reversible adapter-seam planning PR without moving execution ownership yet.

### Test infrastructure

- New regression test `tests/test_quota_chip_settings_toggle.py` (6 cases) pins the quota-chip toggle invariants: Settings field present with i18n labels, `show_quota_chip` default-`False` in `_SETTINGS_DEFAULTS` + `_SETTINGS_BOOL_KEYS`, render/refresh both short-circuit when disabled (no wasted API calls), boot initializes `window._showQuotaChip` from settings + default-false on settings-fetch failure, full panels.js round-trip, 11 locale strings present.

## [v0.51.78] — 2026-05-16 — Release BB (stage-371 — stuck-PR sweep salvage — RTL chat + ambient quota chip with composer-clutter gate)

### Added
Expand Down
2 changes: 2 additions & 0 deletions api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4019,6 +4019,7 @@ def _get_session_agent_lock(session_id: str) -> threading.Lock:
"onboarding_completed": False,
"send_key": "enter", # 'enter' or 'ctrl+enter'
"show_token_usage": False, # show input/output token badge below assistant messages
"show_quota_chip": False, # show ambient provider quota chip in composer footer (default off; wide desktop only when enabled, see style.css @media)
"show_tps": False, # show tokens-per-second chip in assistant message headers
"fade_text_effect": False, # animate newly streamed words with a lightweight fade-in effect
"show_cli_sessions": False, # merge CLI sessions from state.db into the sidebar
Expand Down Expand Up @@ -4152,6 +4153,7 @@ def load_settings() -> dict:
_SETTINGS_BOOL_KEYS = {
"onboarding_completed",
"show_token_usage",
"show_quota_chip",
"show_tps",
"fade_text_effect",
"show_cli_sessions",
Expand Down
2 changes: 1 addition & 1 deletion api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7902,7 +7902,7 @@ def _handle_chat_sync(handler, body):
result = agent.run_conversation(
user_message=workspace_ctx + msg,
system_message=workspace_system_msg,
conversation_history=_sanitize_messages_for_api(_previous_context_messages),
conversation_history=_sanitize_messages_for_api(_previous_context_messages, cfg=get_config()),
task_id=s.session_id,
persist_user_message=msg,
)
Expand Down
29 changes: 28 additions & 1 deletion docs/rfcs/hermes-run-adapter-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- **Author:** @Michaelyklam
- **Updated by:** @franksong2702
- **Created:** 2026-05-11
- **Revised:** 2026-05-14
- **Revised:** 2026-05-16
- **Tracking issue:** [#1925](https://github.com/nesquena/hermes-webui/issues/1925)

## Credit and Scope
Expand Down Expand Up @@ -49,6 +49,29 @@ The immediate goal is not to build a sidecar. The immediate goal is to define th
browser contract, classify current runtime state, and gate the first reversible
journal slice.

## Current Gate State — 2026-05-16

Slice 1 is now past the first active validation gate:

- #2283 shipped the run-journal replay layer in v0.51.71.
- A 100-trial synthetic replay/restart validation pass against current
`origin/master` passed on 2026-05-16. The matrix covered completed-run replay,
interrupted stale-pending recovery, fresh-pending grace handling, StreamChannel
reconnect ordering, duplicate-prevention merge behavior, many-session recovery,
large-journal derivation, and stream-to-turn-id lifecycle linking.
- The focused regression set
`tests/test_turn_journal.py tests/test_turn_journal_lifecycle.py tests/test_stale_stream_pending_recovery.py`
also passed on the same worktree.
- #2393, shipped through v0.51.76, capped live chat token SSE transports to the
selected conversation pane. Background sessions now rely on existing
status/replay/reattach behavior instead of keeping one live `/api/chat/stream`
EventSource per active session.

This evidence does not prove the future runner/sidecar path. It does mean the
project should stop treating Slice 1 as purely passive observation and can move to
Slice 2 planning: introduce the adapter seam over the still-legacy journaled path
without moving execution ownership yet.

## Goals

- Preserve the current rich WebUI workbench experience.
Expand Down Expand Up @@ -269,6 +292,10 @@ Success criterion:

### Slice 2: Adapter interface over the journaled legacy path

Status as of 2026-05-16: ready for a planning/adapter-seam PR after the active
Slice 1 validation pass and the #2313 selected-session stream cap. Slice 2 should
still be a reversible boundary change, not a sidecar or execution-ownership move.

Scope:

- introduce the `RuntimeAdapter` interface only after Slice 1 proves replay,
Expand Down
2 changes: 2 additions & 0 deletions static/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,7 @@ function applyBotName(){
_bootSettings=s;
window._sendKey=s.send_key||'enter';
window._showTokenUsage=!!s.show_token_usage;
window._showQuotaChip=s.show_quota_chip===true;
window._showTps=!!s.show_tps;
window._fadeTextEffect=!!s.fade_text_effect;
window._showCliSessions=!!s.show_cli_sessions;
Expand Down Expand Up @@ -1452,6 +1453,7 @@ function applyBotName(){
}catch(e){
window._sendKey='enter';
window._showTokenUsage=false;
window._showQuotaChip=false;
window._showTps=false;
window._fadeTextEffect=false;
window._showCliSessions=false;
Expand Down
22 changes: 22 additions & 0 deletions static/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ const LOCALES = {
settings_autosave_failed: 'Save failed',
settings_autosave_retry: 'Retry',
settings_label_language: 'Language',
settings_label_quota_chip: 'Show provider quota chip in composer',
settings_desc_quota_chip: 'Displays an ambient remaining-quota indicator (e.g. OpenRouter credit balance) in the composer footer. Default off. Only visible on wide displays (≥1400px) when enabled, to keep the composer uncluttered on laptop and standard desktop widths.',
settings_label_token_usage: 'Show token usage',
settings_label_sidebar_density: 'Sidebar density',
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
Expand Down Expand Up @@ -1723,6 +1725,8 @@ const LOCALES = {
settings_autosave_failed: 'Salvataggio fallito',
settings_autosave_retry: 'Riprova',
settings_label_language: 'Lingua',
settings_label_quota_chip: 'Mostra il chip della quota del provider nel compositore',
settings_desc_quota_chip: "Mostra un indicatore di quota residua (es. saldo crediti OpenRouter) nel piè di pagina del compositore. Predefinito disattivato. Visibile solo su schermi larghi (≥1400px) quando attivato, per mantenere il compositore non affollato su laptop e desktop standard.",
settings_label_token_usage: 'Mostra uso token',
settings_label_sidebar_density: 'Densità sidebar',
cmd_reasoning: 'Mostra/nascondi ragionamento, imposta livello sforzo o controlla stato attuale',
Expand Down Expand Up @@ -2911,6 +2915,8 @@ const LOCALES = {
settings_autosave_failed: '保存失敗',
settings_autosave_retry: '再試行',
settings_label_language: '言語',
settings_label_quota_chip: 'コンポーザーにプロバイダーのクォータチップを表示',
settings_desc_quota_chip: 'コンポーザーのフッターに残りクォータインジケーター(例: OpenRouter のクレジット残高)を表示します。デフォルトはオフ。有効にした場合、ラップトップや標準デスクトップの幅でコンポーザーが混雑しないよう、ワイドディスプレイ(≥1400px)でのみ表示されます。',
settings_label_token_usage: 'トークン使用量を表示',
settings_label_sidebar_density: 'サイドバー密度',
cmd_reasoning: '思考表示の切り替え (表示/非表示)、努力レベル設定、現在状態の確認',
Expand Down Expand Up @@ -3923,6 +3929,8 @@ const LOCALES = {
settings_label_send_key: 'Клавиша отправки',
settings_label_theme: 'Тема',
settings_label_language: 'Язык',
settings_label_quota_chip: 'Показывать чип квоты провайдера в композиторе',
settings_desc_quota_chip: 'Отображает фоновый индикатор остатка квоты (например, баланс кредитов OpenRouter) в подвале композитора. По умолчанию отключено. Виден только на широких экранах (≥1400px) при включении, чтобы не загромождать композитор на ноутбуках и стандартных мониторах.',
settings_label_token_usage: 'Показывать использование токенов',
settings_label_sidebar_density: 'Плотность боковой панели',
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
Expand Down Expand Up @@ -5040,6 +5048,8 @@ const LOCALES = {
settings_autosave_failed: 'Error al guardar',
settings_autosave_retry: 'Reintentar',
settings_label_language: 'Idioma',
settings_label_quota_chip: 'Mostrar el chip de cuota del proveedor en el compositor',
settings_desc_quota_chip: 'Muestra un indicador ambiental de cuota restante (por ejemplo, saldo de crédito de OpenRouter) en el pie del compositor. Predeterminado: desactivado. Solo visible en pantallas anchas (≥1400px) cuando se activa, para mantener el compositor despejado en portátiles y monitores estándar.',
settings_label_token_usage: 'Mostrar uso de tokens',
settings_label_sidebar_density: 'Densidad de la barra lateral',
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
Expand Down Expand Up @@ -6150,6 +6160,8 @@ const LOCALES = {
settings_autosave_failed: 'Speichern fehlgeschlagen',
settings_autosave_retry: 'Wiederholen',
settings_label_language: 'Sprache',
settings_label_quota_chip: 'Anbieter-Kontingent-Chip im Editor anzeigen',
settings_desc_quota_chip: 'Zeigt einen Hintergrund-Indikator des verbleibenden Kontingents (z. B. OpenRouter-Guthaben) in der Editor-Fußzeile an. Standardmäßig deaktiviert. Bei Aktivierung nur auf breiten Bildschirmen (≥1400px) sichtbar, damit der Editor auf Laptops und Standard-Desktops übersichtlich bleibt.',
settings_label_token_usage: 'Token-Verbrauch anzeigen',
settings_label_sidebar_density: 'Seitenleistendichte',
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
Expand Down Expand Up @@ -7301,6 +7313,8 @@ const LOCALES = {
settings_autosave_failed: '保存失败',
settings_autosave_retry: '重试',
settings_label_language: '语言',
settings_label_quota_chip: '在编辑器中显示供应商配额标签',
settings_desc_quota_chip: '在编辑器底部显示剩余配额指示器(如 OpenRouter 信用余额)。默认关闭。启用时仅在宽屏(≥1400px)显示,以保持笔记本和标准桌面屏幕上编辑器的整洁。',
settings_label_token_usage: '显示 token 用量',
settings_label_sidebar_density: '侧边栏密度',
cmd_reasoning: '切换思维可见性(显示/隐藏)、设置工作强度或查看当前状态',
Expand Down Expand Up @@ -8436,6 +8450,8 @@ const LOCALES = {
settings_autosave_failed: '\u5132\u5b58\u5931\u6557',
settings_autosave_retry: '\u91cd\u8a66',
settings_label_language: '\u8a9e\u8a00',
settings_label_quota_chip: '在編輯器中顯示供應商配額標籤',
settings_desc_quota_chip: '在編輯器底部顯示剩餘配額指示器(如 OpenRouter 點數餘額)。預設關閉。啟用時僅在寬螢幕(≥1400px)顯示,以保持筆記型電腦和標準桌面螢幕上編輯器的整潔。',
settings_label_token_usage: '\u986f\u793a token \u7528\u91cf',
settings_label_sidebar_density: '側邊欄密度',
cmd_reasoning: '切換思考區塊可見性(顯示/隱藏)或設定努力等級',
Expand Down Expand Up @@ -9712,6 +9728,8 @@ const LOCALES = {
settings_autosave_failed: 'Falha ao salvar',
settings_autosave_retry: 'Tentar novamente',
settings_label_language: 'Idioma',
settings_label_quota_chip: 'Mostrar o chip de cota do provedor no compositor',
settings_desc_quota_chip: 'Exibe um indicador ambiente de cota restante (por exemplo, saldo de crédito do OpenRouter) no rodapé do compositor. Desativado por padrão. Visível apenas em telas largas (≥1400px) quando ativado, para manter o compositor livre em laptops e monitores padrão.',
settings_label_token_usage: 'Mostrar uso de tokens',
settings_label_sidebar_density: 'Densidade da sidebar',
cmd_reasoning: 'Alternar visibilidade do pensamento (mostrar/ocultar)',
Expand Down Expand Up @@ -10803,6 +10821,8 @@ const LOCALES = {
settings_autosave_failed: '저장 실패',
settings_autosave_retry: '다시 시도',
settings_label_language: '언어',
settings_label_quota_chip: '작성기에 공급자 할당량 칩 표시',
settings_desc_quota_chip: '작성기 푸터에 남은 할당량 표시기(예: OpenRouter 크레딧 잔액)를 표시합니다. 기본값은 끔. 활성화 시 노트북과 표준 데스크톱에서 작성기가 복잡해지지 않도록 와이드 디스플레이(≥1400px)에서만 표시됩니다.',
settings_label_token_usage: '토큰 사용량 표시',
settings_label_sidebar_density: '사이드바 밀도',
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
Expand Down Expand Up @@ -11911,6 +11931,8 @@ const LOCALES = {
settings_autosave_failed: 'Échec de l\'enregistrement',
settings_autosave_retry: 'Réessayer',
settings_label_language: 'Langue',
settings_label_quota_chip: 'Afficher la pastille de quota du fournisseur dans le compositeur',
settings_desc_quota_chip: "Affiche un indicateur ambiant de quota restant (par ex. solde de crédit OpenRouter) dans le pied du compositeur. Désactivé par défaut. Visible uniquement sur les écrans larges (≥1400px) lorsqu'activé, pour garder le compositeur dégagé sur les ordinateurs portables et les bureaux standard.",
settings_label_token_usage: 'Afficher l\'utilisation du jeton',
settings_label_sidebar_density: 'Densité de la barre latérale',
cmd_reasoning: 'Basculez la visibilité de la réflexion (afficher/masquer), définir le niveau d\'effort ou vérifier l\'état actuel',
Expand Down
7 changes: 7 additions & 0 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,13 @@ <h2 data-i18n="empty_title">What can I help with?</h2>
</label>
<div style="font-size:11px;color:var(--muted);margin-top:4px" data-i18n="settings_desc_token_usage">Displays input/output token count below each assistant reply. Also toggled with <code>/usage</code>.</div>
</div>
<div class="settings-field">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
<input type="checkbox" id="settingsShowQuotaChip" style="width:15px;height:15px;accent-color:var(--accent)">
<span data-i18n="settings_label_quota_chip">Show provider quota chip in composer</span>
</label>
<div style="font-size:11px;color:var(--muted);margin-top:4px" data-i18n="settings_desc_quota_chip">Displays an ambient remaining-quota indicator (e.g. OpenRouter credit balance) in the composer footer. Default off. Only visible on wide displays (≥1400px) when enabled, to keep the composer uncluttered on laptop and standard desktop widths.</div>
</div>
<div class="settings-field">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
<input type="checkbox" id="settingsShowTps" style="width:15px;height:15px;accent-color:var(--accent)">
Expand Down
Loading
Loading