v0.50.255 — provider context, P3 features, TTS+Ollama hotfixes
Substantial release. Three independently-approved PRs merged into a single batch, plus four Opus pre-release advisor fixes including a path-traversal MUST-FIX in the new rollback API.
Added
-
Insights panel — usage analytics dashboard (#464) — new nav rail entry between Todos and Settings. Stats cards (sessions / messages / tokens / cost), token breakdown row, and ASCII-style horizontal-bar charts for activity by day-of-week + hour. New
GET /api/insights?days=Nendpoint walks_index.json(no full session loads). Period filter (7/30/90 days). (#1405, @bergeouss) -
Rollback UI — restore from agent checkpoints (#466) — new
api/rollback.pyexposesGET /api/rollback/list,GET /api/rollback/diff,POST /api/rollback/restoreover the agent'sCheckpointManagershadow git repos at{hermes_home}/checkpoints/<sha256-of-canonical-workspace>/<commit_hash>/.git. Workspace is allowlisted viaload_workspaces()._validate_checkpoint_id()regex-guards the checkpoint parameter against path-traversal. Restore copies files viashutil.copy2and never deletes; diff usesdifflib.unified_diff. (#1405, @bergeouss) -
Turn-based voice mode — STT + TTS chained flow — new voice-mode button in the composer kicks off a listen → send → think → speak → listen loop. Uses the browser's Web Speech API (gated on both
SpeechRecognitionANDspeechSynthesissupport). Auto-send on 1.8s silence after a final transcript. Honors saved voice preferences. Bails out onnot-allowed/service-not-allowed/audio-captureerrors. (#1405, @bergeouss) -
API redact toggle —
api_redact_enabledsetting (defaults toTrue) lets users opt out of response-layer redaction when piping the API into automation. (#1405, @bergeouss) -
Subagent tree visualization — UI affordance for sessions that spawn subagents. (#1405, @bergeouss)
Fixed
-
Session provider context preserved across model picker → runtime resolution (#1240) — the WebUI model picker can show multiple providers exposing the same bare model id (e.g.
gpt-5.5from OpenAI Codex, OpenRouter, Copilot). Previously sessions persisted only the bare model, so a session selected as "gpt-5.5 from OpenAI Codex" silently rerouted through whatever provider became default after a config change. Newmodel_provider: str | Nonefield on Session is persisted in metadata, threaded through every chat path, and is gated incompact()to emit only when truthy. (#1390, @starship-s) -
TTS toggle: speaker icon never appeared when "Text-to-Speech for responses" was ticked (#1409) —
_applyTtsEnabled()setbtn.style.display=enabled?'':'none'on every.msg-tts-btn. The''branch removes the inline override, after which the.msg-tts-btn{display:none;}rule fromstyle.cssre-hid the button. Both branches left the icon hidden, so the toggle had no visible effect since the feature shipped in #499. Fixed via body-class toggle (body.tts-enabled) plus a compound CSS selector. The new shape bypasses the cascade collision and survives subsequentrenderMd()re-renders without re-querying every button. (#1411, reported by @AvidFuturist via Discord) -
Ollama (local) no longer falsely reports "API key configured" when only Ollama Cloud key is set (#1410) — both providers were mapped to the same
OLLAMA_API_KEYenv var in_PROVIDER_ENV_VAR, so configuring Ollama Cloud lit up the local Ollama card too. The runtime inhermes_cli/runtime_provider.pyonly consumesOLLAMA_API_KEYwhen the base URL hostname isollama.com— local Ollama is keyless by design — so the WebUI was reporting "configured" for a key local Ollama doesn't even read. Dropped the bare"ollama": "OLLAMA_API_KEY"mapping; local Ollama users with explicitproviders.ollama.api_keyinconfig.yamlcontinue to be honored. (#1411, reported by @AvidFuturist via Discord)
Security & performance hardening (Opus pre-release advisor)
-
api/rollback.py— checkpoint id regex validation (path-traversal MUST-FIX) — thecheckpointparameter on/api/rollback/diffand/api/rollback/restorewas joined into the path via_checkpoint_root() / ws_hash / checkpoint.Path("/a/b") / "../escape"does NOT normalize, so an authenticated caller could pass../<other-ws-hash>/<sha>and read or restore from another allowlisted workspace's checkpoint store. New_validate_checkpoint_id()regex-guards with^[A-Za-z0-9_-][A-Za-z0-9_.-]{0,63}$and rejects literal./... -
redact_session_data()readsapi_redact_enabledonce per response, not per string — the new redact toggle introduced a perf cliff:_redact_textrecursed acrossmessages[]andtool_calls[]calling uncachedload_settings()on every nested string. For a 50-message session, that was hundreds of disk reads + JSON parses per/api/session?session_id=Xresponse. Now read once at the top ofredact_session_data()and threaded through via a private_enabledkeyword. -
Voice mode pins active session id at thinking-time — the patched
autoReadLastAssistantfires globally; if the user navigated to a different session between sending a turn and stream completion, TTS would speak the wrong session's last assistant message. New_voiceModeThinkingSidclosure variable capturesS.session.session_idin_voiceModeSend;_speakResponsebails to_startListening()if the current sid no longer matches. -
_inspect_checkpointdrops bareExceptionfrom except tuple — the previousexcept (subprocess.TimeoutExpired, OSError, Exception)made the specific catches redundant and swallowed everything. Now(subprocess.TimeoutExpired, OSError)only.
Tests
3592 passed, 2 skipped, 3 xpassed (master = 3567, +25 net). Browser tests + Agent Browser CDP visual QA all green. Opus advisor reviewed the combined stage diff. Independent end-to-end review by @nesquena on both #1411 (CSS specificity calculation, behavioral matrix) and #1412 (31-case path-traversal behavioral harness on the rollback regex guard).
Contributors
@bergeouss · @starship-s · @AvidFuturist (Discord bug reporter) · @nesquena (review)
Full Changelog: v0.50.254...v0.50.255