mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-26 11:40:26 +00:00
Stamp CHANGELOG for stage-388 (Release BS — 5-PR batch)
Unreleased section now reflects: - PR #2598 live tool event dedup (AJV20) - PR #2533 browser dashboard links (AJV20) - PR #2607 messaging transcript dedup (AJV20) - PR #2521 Geist Contrast skin (intellectronica) - PR #2524 SSE runtime diagnostics endpoint (AJV20) Removed merge markers and consolidated stray entries that leaked into the v0.51.94 release block.
This commit is contained in:
+7
-58
@@ -3,15 +3,16 @@
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
- Deduplicate messaging/CLI session transcript rows when the sidecar and state store encode the same no-id message with equivalent timestamps in different formats, preventing repeated visible chat messages after session reconstruction.
|
||||
>>>>>>> stage388-pr2607
|
||||
|
||||
- Surface live tool activity when Hermes Agent reports tools through its dedicated `tool_start_callback` / `tool_complete_callback` path, so browser chat shows the existing running tool cards instead of appearing idle until the final answer.
|
||||
- **PR #2598** by @AJV20 — Surface live tool activity when Hermes Agent reports tools through its dedicated `tool_start_callback` / `tool_complete_callback` path, so browser chat shows the existing running tool cards instead of appearing idle until the final answer. The legacy `on_tool` callback path now early-returns for `tool.started` and `tool.completed` events when the structured callback path is already wired, preventing the same tool event from being emitted twice to the SSE stream.
|
||||
- **PR #2533** by @AJV20 — Allow Settings → System to save public browser-only Official Hermes Dashboard links (for reverse-proxy URLs) without treating them as server-side probe targets. URL sanitization runs against the configured link before save; the dashboard probe is skipped for browser-only links.
|
||||
- **PR #2607** by @AJV20 — Deduplicate messaging/CLI session transcript rows when the sidecar and state store encode the same no-id message with equivalent timestamps in different formats (e.g. `"10.0"` vs `10`), preventing repeated visible chat rows after session reconstruction. The messaging-display merge now reuses `api.models._session_message_merge_key(...)` instead of an ad-hoc dedup key, aligning with the existing append-only merge path.
|
||||
|
||||
### Added
|
||||
|
||||
- **PR #2521** by @intellectronica — Add the Geist Contrast skin to the appearance picker. New light + dark variant pair with a high-contrast yellow-on-black accent and Geist editorial typography. Default unchanged — opt-in via Settings → Appearance → Skin → Geist Contrast. Slash command `/theme geist-contrast` now resolves correctly because the lookup matches against `skin.value` rather than `skin.name`. Documented in `THEMES.md` with a forward-compatible skin count (no hard-coded value).
|
||||
- **PR #2524** by @AJV20 — Add non-sensitive SSE stream runtime diagnostics to deep health checks, including active stream count, subscriber totals, and offline buffered-event counts for stuck or slow WebUI chat investigations. New `/api/webui/health/runtime` endpoint exposes the counters as read-only telemetry; existing surfaces unchanged.
|
||||
|
||||
<<<<<<< HEAD
|
||||
## [v0.51.94] — 2026-05-19 — Release BR (stage-387 — 10-PR full sweep batch — Slice 4b runner adapter facade + folder zip download + partial recovery marker dedupe + browser api() client-side timeout + auto-compression card rotation finish + composer draft rollback fix + metadata count reconciliation + active-session refresh on external sidecar updates + indexed context metadata + gateway-queues approval peek)
|
||||
|
||||
### Fixed
|
||||
@@ -25,17 +26,11 @@
|
||||
- **PR #2605** by @LumenYoung (refs #2581) — Make the metadata-only `/api/session?messages=0&resolve_model=0` path return the persisted sidecar `message_count` from `Session._metadata_message_count` when no session-index entry exists, so the active-session external-refresh signal still trips on legacy sessions whose sidecar contains externally-appended content. Composed cleanly with #2604 (the legacy-fallback applies only when the reconciled merged count is zero).
|
||||
- **PR #2573** by @espokaos-ops (closes #2510) — Persist session-level approvals when a "Allow for this session" click lands while a stream is active and `_pending` is empty. The approval flow now peeks `_gateway_queues[sid]` to recover the queued `_ApprovalEntry`'s `pattern_keys` so `approve_session()` records the approval; the next dangerous command in the same session no longer asks again. Reduced scope to peek-only per prior review note; the `agent_session_key` round-trip plumbing was dropped (it was dead on the WebUI streaming path).
|
||||
|
||||
|
||||
|
||||
- Allow Settings → System to save public browser-only Official Hermes Dashboard links (for reverse-proxy URLs) without treating them as server-side probe targets.
|
||||
### Added
|
||||
|
||||
- **PR #2599** by @Michaelyklam (refs #1925) — Add the Slice 4b `RunnerRuntimeAdapter` facade — a protocol-translator client over a future runner/sidecar backend. The facade delegates `start_run`, `observe_run`, `get_run`, and control calls to an injected runner client, normalizes results into the existing `RunStartResult`/`RunEventStream`/`RunStatus`/`ControlResult` dataclasses, carries explicit `profile`/`workspace`/`model` payload fields, and returns bounded `unsupported` control results without owning `AIAgent`, stream lifecycle, cancel/approval/clarify queues, goal state, or cached-agent table. No route wiring, no default-on runner mode, no public response-shape change.
|
||||
- **PR #2600** by @LumenYoung (refs #2266) — Slimmer WebUI follow-up from the closed LCM/context-engine PR #2266. Adds rendering and persistence for context-engine compression-anchor metadata (when present on a session or live compression event) including an "Indexed context" detail line on auto-compression cards. No agent-layer clone orchestration; WebUI-only metadata surface.
|
||||
|
||||
|
||||
|
||||
- Add non-sensitive SSE stream runtime diagnostics to deep health checks, including active stream count, subscriber totals, and offline buffered-event counts for stuck or slow WebUI chat investigations.
|
||||
## [v0.51.93] — 2026-05-19 — Release BQ (stage-386 — 10-PR full sweep batch — RFC Slice 4 runner/sidecar gate + workspace tree toggle width CSS variable + settled file:// markdown link rendering + prompt-cache coverage percentage fix + terminal shell shutdown reap + configured model picker provider preservation + profile-aware assistant display names + state.db reconciliation slice 1 + queued-message cross-session drain fix + stale-stream writeback supersede)
|
||||
|
||||
### Fixed
|
||||
@@ -49,7 +44,6 @@
|
||||
- **PR #2587** by @AJV20 — Allow a still-running stream that was mistakenly marked interrupted by stale-pending recovery to replace its own recovery marker when it later finishes, while continuing to block stale writeback after any newer turn appends transcript content. Three new tests in `tests/test_session_sidecar_repair.py` cover the supersede-allowed and the two refuse cases.
|
||||
- **PR #2588** by @Michaelyklam (refs #2569) — Preserve the configured provider when choosing a configured model from the composer picker. `_getOptionProviderId()` now reads `data-provider` from temporary `<option data-custom="1">` rows (created by `selectModelFromDropdown` for configured models outside the native catalog), so the next send routes through the correct provider instead of falling back to whatever provider was already active.
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- **PR #2581** by @LumenYoung (refs #2194) — First recovery slice from the closed reconciliation PR #2194. Routes streaming session reconstruction and sidebar metadata through the reconciled state.db/session-summary path with a metadata-only fast path for sidebar polls and a single-snapshot reuse on the streaming hot path. Includes the reviewer-requested `_new_turn_context_from_messages` extraction so both legacy and streaming paths share the `_drop_checkpointed_current_user_from_context` + casual-fresh-chat suppression behavior (refs #1217 / #2308). 923 LOC across `api/models.py`, `api/routes.py`, `api/streaming.py`, `static/sessions.js` + four new test files; second-pass agent diff review LGTM after the streaming-path regression was caught and fixed.
|
||||
@@ -57,8 +51,6 @@
|
||||
### Documentation
|
||||
|
||||
- **PR #2575** by @Michaelyklam (refs #1925) — Advance the runtime-adapter RFC to the Slice 4 runner/sidecar planning gate after #2560 shipped the queue-staging clarification. The RFC now marks queue routing as staged by default, defines Slice 4a as a docs/test contract before any runner code lands, and pins default-off feature-flagging, restart/reattach success criteria, control parity, profile/workspace payload isolation, and explicit non-goals for legacy-backend removal or server-side queue scheduler work.
|
||||
=======
|
||||
>>>>>>> 2ecdd66d (feat: add Geist Contrast skin)
|
||||
|
||||
## [v0.51.92] — 2026-05-19 — Release BP (stage-385 — 7-PR full sweep batch — RFC Slice 3c clarification + workspace tree icon alignment + project move cache refresh + auto-compression handoff metadata + Grok OAuth provider catalog + anonymous custom endpoint picker fallback + PWA standalone reload + pull-to-refresh)
|
||||
|
||||
@@ -171,7 +163,6 @@
|
||||
|
||||
- **PR #2490** by @nesquena-hermes — Add a Docker runtime smoke gate (`.github/workflows/docker-smoke.yml`) triggered on PRs and pushes to `master` that modify `Dockerfile`, `docker_init.bash`, `docker-compose*.yml`, `.dockerignore`, or `.env.docker.example`. Validates every compose file parses (`docker compose config`), then matrix-runs the single, two-container, and three-container variants end-to-end: rebuilds the local `Dockerfile` and re-tags it as `ghcr.io/nesquena/hermes-webui:latest` so the multi-container variants exercise PR-level changes rather than the previously-released registry image, `docker compose up -d --wait`s with a 120s health window, probes `/health`, and greps startup logs for known-bad signatures (`EROFS`, `Traceback`, `PermissionError`, `error_exit`, `!! ERROR`, `!! Exiting script`, `groupmod: cannot`, `usermod: cannot`, `Failed to set`). Closes the source-only-test gap that let v0.51.84's `:ro`-mount × `chown -h ... {} +` startup regression reach review with 5800+ green pytests. Workflow runs with `permissions: contents: read`, uses per-run project names and a pre-flight orphan reaper for safe concurrency, and unconditionally tears down all volumes/networks in an `EXIT` trap. Two new source-level invariants in `tests/test_docker_docs_and_readonly.py` pin the staging path so the underlying `:ro`-incompatible call doesn't regress.
|
||||
|
||||
|
||||
## [v0.51.86] — 2026-05-17 — Release BJ (stage-379 — 4-PR review-bypass batch — memory-provider session lifecycle + cross-provider /model alias + RuntimeAdapter cancel seam + Fork-from-here messaging coord)
|
||||
|
||||
### Fixed
|
||||
@@ -184,7 +175,6 @@
|
||||
|
||||
- **PR #2479** by @Michaelyklam (refs #1925) — Route Stop Generation through the default-off `RuntimeAdapter.cancel_run(...)` seam when `HERMES_WEBUI_RUNTIME_ADAPTER=legacy-journal` is enabled. Implements the first code slice of the Slice 3a cancel-control gate accepted in #2469 / v0.51.85. The default `legacy-direct` path still calls `cancel_stream(...)` directly; the adapter branch preserves the existing `{ok, cancelled, stream_id}` JSON response contract. No new cancellation registry, runner, sidecar, approval/clarify, queue/goal, or cached-agent state is introduced — adapter remains a pure protocol translator.
|
||||
|
||||
|
||||
## [v0.51.85] — 2026-05-17 — Release BI (stage-378 — 3-PR batch — workspace-prefix display leakage fix + release-tag update banner + Slice 3a cancel-control gate RFC)
|
||||
|
||||
### Fixed
|
||||
@@ -441,7 +431,6 @@
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
- **PR #2319** by @Michaelyklam — Chat file uploads now land in a session-scoped attachment inbox instead of cluttering the active workspace root. By default uploads are stored under `~/.hermes/webui/attachments/<session_id>/`; operators can override the root with `HERMES_WEBUI_ATTACHMENT_DIR`, and the agent still receives the absolute uploaded file path for context. Archive extraction stays workspace-scoped (it's an explicit workspace operation). README updated to document the new default location. **Stage-361 maintainer fix applied inline**: Opus advisor caught that `_build_native_multimodal_message` at `api/streaming.py:787` required uploads to be under `workspace_root`, which would have silently dropped every image upload for vision-capable models once the inbox moved outside the workspace. The fix adds `_attachment_root()` (from `api/upload.py`) as a second allowed location, with 3 regression tests covering the new code path AND verifying the original workspace + cross-root rejection paths still work.
|
||||
|
||||
### Fixed
|
||||
@@ -468,7 +457,6 @@
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
- **PR #2287** by @mslovy (refs #2284) — Upload size limit is now runtime-configurable via the `HERMES_WEBUI_MAX_UPLOAD_MB` environment variable. Previously the effective 20 MB cap was hard-coded across multiple layers. Server-side upload limit moves to runtime config; browser-side preflight check stays aligned with the effective backend limit; archive extraction guard continues to scale with the same configured cap. New `_env_mb_bytes()` helper in `api/config.py` parses `HERMES_WEBUI_MAX_UPLOAD_MB`.
|
||||
|
||||
- **PR #2291** by @linuxid10t — New "Nous Research" skin option in the Settings → Appearance picker, inspired by [nousresearch.com](https://nousresearch.com). Monospace typography, steel blue (#4682B4) accent, cool gray text, sharp 1-2px corners, thin dashed borders, technical aesthetic.
|
||||
@@ -479,7 +467,6 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
- **PR #2275** by @ai-ag2026 — CLI/messaging continuation sessions (sessions stitched from a `parent_session_id` chain) now return their full transcript instead of an empty list. Pre-fix, `get_cli_session_messages()` called `_is_continuation_session()` while walking the parent chain, but `api/models.py` didn't import that helper. The exception was swallowed by `except Exception: return []`, so valid external sessions could fall through silently. Adds regression coverage that a stitched continuation chain returns a non-empty transcript.
|
||||
|
||||
- **PR #2277** by @eleboucher — Rootless container runtimes (k8s `runAsNonRoot: true`, OpenShift restricted SCC, `docker --user`, rootless Podman) no longer hit a cascade of permission errors at startup. Pre-fix, the rootless branch skipped the root init phase entirely, but root init also did rsync, `/uv_cache` permissions, `~/hermeswebui` home directory creation, and `/workspace` writability. `docker_init.bash` now distinguishes "no root init available" from "root init available but skipped", running the work that doesn't need root in the rootless branch too.
|
||||
@@ -810,7 +797,6 @@
|
||||
|
||||
- **PR #2116** by @starship-s — OpenAI Codex provider quota card no longer reports "unavailable" when Codex chat requests actually work. Runtime requests authenticate via the modern `agent.credential_pool`, but the account-usage probe only tried the legacy singleton Codex token path. Adds a Codex-only credential-pool fallback inside the existing isolated `_account_usage_subprocess`: when `agent.account_usage.fetch_account_usage()` returns no available snapshot, the fallback selects the active `openai-codex` credential-pool entry, derives the Codex usage endpoint from the runtime base URL (handles `/backend-api/codex` → `/wham/usage` and custom bases → `/api/codex/usage`), and serializes the existing snapshot shape expected by the WebUI. Stays inside the child process so active Hermes profile context remains isolated; legacy unavailable diagnostics preserved when the pool fallback can't produce a usable result; non-Codex providers unchanged. Returns only quota display data — never credential labels, access tokens, or raw exception strings. 151-line regression suite covers the success path, both URL-resolution branches, and the unavailable-fallthrough case.
|
||||
|
||||
|
||||
### Stage-342 maintainer fixes
|
||||
|
||||
- **`api/worktrees.py:_run_git` default timeout 5s → 2s** — Opus SHOULD-FIX from stage-342 review: PR #2109's new `/api/session/worktree/status` endpoint runs up to four `git` subprocess calls per request, each defaulting to a 5-second timeout. Worst case 20 seconds per polling request piling up on the `ThreadingHTTPServer` thread pool is risky given today's `_cron_env_lock` near-miss on production 8787. Status probes should fail fast — a worktree that takes longer than 2 seconds to enumerate is already in trouble, and the client can retry. Mechanical 1-LOC default-arg change; all four call sites already pass `cwd` positionally and rely on the default. ~1 LOC.
|
||||
@@ -835,7 +821,6 @@
|
||||
|
||||
- **`api/streaming.py` short-circuit observability** — Opus SHOULD-FIX from stage-341 review: PR #2107's new `_title_should_skip_remaining_attempts` short-circuit `break` was silent in both the aux and agent title-generation paths. Added a `logger.debug` call before each `break` so production logs surface why the prompt-iteration loop exited early (nesquena flagged this as non-blocking; landed as polish in the same release). Also expanded the function's docstring to document the membership criterion explicitly so future additions (`llm_safety_blocked`, `llm_oauth_quota`, etc.) have a clear inclusion test. ~16 LOC.
|
||||
|
||||
|
||||
## [v0.51.47] — 2026-05-11 — Release W (4-PR contributor batch — per-cron toast toggle + Italian locale + stale-gateway agent-health fix + CI/console hygiene)
|
||||
|
||||
### Added
|
||||
@@ -916,7 +901,6 @@
|
||||
|
||||
- **`api/providers.py:1049`** — Custom provider entries that slugify to an empty string were silently dropped, which made misconfigurations hard to diagnose. `logger.warning()` now surfaces the bad config entry. ~4 LOC; pure observability change.
|
||||
|
||||
|
||||
## [v0.51.44] — 2026-05-11 — Release T (5-PR contributor batch — security + worktree sessions + LM Studio + onboarding docs + transcript dedup, plus comprehensive test-suite network isolation)
|
||||
|
||||
### Added
|
||||
@@ -1043,7 +1027,6 @@
|
||||
|
||||
- Test isolation: at least one test in `test_update_banner_fixes.py` or `test_updates.py` triggers a real `os.execv` that re-executes the entire pytest suite. Suite still passes (~5108 each loop) but full run takes 4× the time. Worth a targeted fix in the next maintenance batch.
|
||||
|
||||
|
||||
## [v0.51.40] — 2026-05-11 — Release P (4-PR contributor batch — quota subprocess hardening + env-lock prewarm + cron one-shot warning + Xiaomi env key)
|
||||
|
||||
### Fixed
|
||||
@@ -1350,7 +1333,6 @@ Three duplicate-PR clusters consolidated to one canonical PR each, with `Co-auth
|
||||
- **Race between endless-scroll prefetch and Start-jump's `_ensureAllMessagesLoaded`** — with both opt-ins ON, an in-flight prefetch (started by 1.5x-viewport trigger) racing with `jumpToSessionStart` → `_ensureAllMessagesLoaded` could produce duplicate messages if the prefetch resolves last. Narrow window, but the fix is to gate `_ensureAllMessagesLoaded` on the existing `_loadingOlder` flag. **Resolved in Unreleased — see #1937 entry above; final fix uses generation-token + mutex rather than the originally-suggested flag gate, which would not have closed the race.**
|
||||
- **#1928 locale parity** — `session_jump_*` and `settings_*_session_jump_buttons` keys are English literals in ja/ru/es/de/zh/zh-Hant/pt/ko. Default-OFF + English fallback works, but breaks the locale-parity standard set by #1929 and #1891 in the same release.
|
||||
|
||||
|
||||
### Added (1 PR)
|
||||
|
||||
- **PR #1919** by @franksong2702 — Persist login rate limit attempts (closes #1910). Stores failed-login buckets in `STATE_DIR/.login_attempts.json` instead of in-process memory, so password-auth deployments keep the same failed-attempt window across restarts. Atomic temp+rename writes, `0600` permissions, prunes expired entries on load. If the file is missing, malformed, or unwritable, the auth path falls back to current in-memory behavior with debug-level logging — no infinite-loop risk.
|
||||
@@ -1381,7 +1363,6 @@ Three duplicate-PR clusters consolidated to one canonical PR each, with `Co-auth
|
||||
- v0.51.28 carry-overs verified preserved (no in-batch changes to `api/routes.py:_strip_workspace_prefix`, `api/streaming.py:evaluate_goal_after_turn`, `api/profiles.py:_profiles_match`, `tests/test_mcp_server.py` module-restoration logic).
|
||||
- Pre-stamp re-fetch of all 6 PR heads: no contributor force-push during Opus window.
|
||||
|
||||
|
||||
### Added (2 PRs)
|
||||
|
||||
- **PR #1895** by @samuelgudi — MCP server Option A rewrite (#1616). Replaces the fragile MCP integration with a clean `mcp_server.py` (567 LOC) implementing project CRUD, session listing, and session mutations (rename/move) over Hermes's HTTP API. Imports `api.models` / `api.profiles` canonically rather than carrying duplicate slug-matching helpers. Relocates `_profiles_match` from `api/routes.py` into `api/profiles.py` as the single source of truth (mcp_server.py and api/routes.py both now import the canonical helper — re-introducing a local copy in either module trips a parity test immediately). Adds env-aware WEBUI_URL (`HERMES_WEBUI_HOST` / `HERMES_WEBUI_PORT`). New behaviour: `delete_project` REFUSES to touch session JSONs when `HERMES_WEBUI_PASSWORD` is unset, returning `{ok:true, unassigned_sessions:0, warning:"…"}` instead — preventing data-loss when an MCP client tries to delete a project on an unauthenticated server. 53-test coverage in `tests/test_mcp_server.py` (914 LOC) including HTTP wire-format integration tests, profile-scoped isolation, legacy untagged row visibility, and `--profile foo` CLI ordering regression. Closes #1616.
|
||||
@@ -1413,7 +1394,6 @@ Three duplicate-PR clusters consolidated to one canonical PR each, with `Co-auth
|
||||
- **Goal hook fires on unrelated turns** — while a goal is `active`, every completed assistant turn runs `evaluate_goal_after_turn` and ticks `state.turns_used += 1`, even on user messages unrelated to the goal. UX surprise but not bug-broken; consider gating on `user_initiated` or a goal-context flag.
|
||||
- **English-only runtime strings in goal UI** — `messages.js:889` ("Evaluating goal progress…"), `commands.js:651` ("Working toward goal…"), `messages.js:914` ("Continuing toward goal…" toast); also backend strings in `goals.py` (`status_line`, "⊙ Goal set …", "⏸ Goal paused …", "↻ Continuing …"). The `cmd_goal` autocomplete description IS localized across all 9 locales — only the runtime status strings are missed.
|
||||
|
||||
|
||||
### Fixed (4 PRs)
|
||||
|
||||
- **PR #1916** by @Michaelyklam — Make Kanban detail view scrollable. The app shell sets `body { overflow: hidden }`, so the Kanban main view must own vertical scrolling. Pre-fix, a selected task with a long body could push the board below the viewport with no way to reach it. Fix: add `overflow-y: auto` to `main.main.showing-kanban > #mainKanban` (one CSS property + regression test). Closes #1915.
|
||||
@@ -2134,7 +2114,6 @@ Closes #1362, #1731.
|
||||
|
||||
Closes #1714, #1715, #1716.
|
||||
|
||||
|
||||
## [v0.51.5] — 2026-05-05 — 4-PR full-sweep batch
|
||||
|
||||
### Added
|
||||
@@ -2175,7 +2154,6 @@ Closes #1714, #1715, #1716.
|
||||
|
||||
Closes #693, #1690, #1710.
|
||||
|
||||
|
||||
## [v0.51.5] — 2026-05-05 — single-PR hotfix (#1707)
|
||||
|
||||
### Fixed
|
||||
@@ -2216,7 +2194,6 @@ Closes #693, #1690, #1710.
|
||||
|
||||
@Michaelyklam filed PR #1706 with a functional Node-driven regression test (extracts the real paste handler and asserts two pasted image items become two pending attachments) replacing my own #1705 which used static-source-string assertions. Same code fix, better test approach. Closed #1705 and absorbed #1706 into stage-301.
|
||||
|
||||
|
||||
## [v0.51.3] — 2026-05-04 — 3-PR follow-up batch (#1671, #1673, #1676) + test-fragility fix
|
||||
|
||||
### Added
|
||||
@@ -2262,7 +2239,6 @@ PRs #1671, #1673, #1676 had been UX-gated in the v0.51.1 sweep, then on second-l
|
||||
|
||||
UX label cleared, Aron stand-down comments deleted on all 3, all 3 swept into this batch.
|
||||
|
||||
|
||||
## [v0.51.2] — 2026-05-04 — 3-PR follow-up batch (deferred from v0.51.1) + sidebar scroll hotfix
|
||||
|
||||
### Fixed
|
||||
@@ -2307,7 +2283,6 @@ All 3 PRs branched off pre-Kanban-v1 master, producing multi-region conflicts in
|
||||
|
||||
Both #1664 and #1662 rebased branches were force-pushed back to @Michaelyklam's fork via maintainer write access (preserving `Co-authored-by:` attribution). #1587 stayed local since the maintainer token doesn't have write access to franksong2702's fork.
|
||||
|
||||
|
||||
## [v0.51.1] — 2026-05-04 — 11-PR contributor batch from @Michaelyklam
|
||||
|
||||
### Added — 11 PRs from a single overnight burst, all per-PR Phase-0 fit-screened
|
||||
@@ -2369,7 +2344,6 @@ Both #1664 and #1662 rebased branches were force-pushed back to @Michaelyklam's
|
||||
|
||||
This release ships a contributor-burst pattern (17 PRs from @Michaelyklam in 51 minutes overnight). Despite the volume, per-PR claim-vs-diff verification showed no AI-tells, all PR descriptions matched their diffs, all `closes #N` references pointed at real open issues, and security-relevant code paths (file-system reads, outbound HTTP, PID handling, schema redaction) check out under independent review. Eleven PRs landed cleanly in this batch; the remaining six were either deferred for conflict resolution or already in held-state with maintainer-review labels.
|
||||
|
||||
|
||||
## [v0.51.0] — 2026-05-04 — Kanban v1
|
||||
|
||||
### Added — Kanban v1: complete first-party Kanban for Hermes (closes #1645, #1646, #1647, #1649, #1654, #1655, #1660, #1675)
|
||||
@@ -2697,7 +2671,6 @@ Closes #1360, #1451, #1463, #1618, #1619 (5 issues).
|
||||
|
||||
Closes #1578, #1583, #1584, #1595, #1613, #1620.
|
||||
|
||||
|
||||
## [v0.50.291] — 2026-05-04
|
||||
|
||||
### Fixed (1 PR — "What's new?" link 404 — closes #1579)
|
||||
@@ -2804,7 +2777,6 @@ Two stale source-string assertions were broken by #1591's compact() and messages
|
||||
- **PR rebase verified clean** (REBASE-DEFAULT rule applied). All 3 PR branches were on or near current master; rebase was no-op.
|
||||
- **#1571 post-review fix combination**: contributor's `df03055` (post-review tightening) was on `pull/1571/head` while reviewer's `d83e1d8` (test-skip-on-missing-agent) was on `origin/fix/scheduled-jobs-profile-isolation`. Cherry-picked the test-skip commit onto the contributor branch to combine both fixes before merging into stage.
|
||||
|
||||
|
||||
## [v0.50.287] — 2026-05-03
|
||||
|
||||
### Fixed (1 PR — closes another vector for the pending-message-loss class)
|
||||
@@ -2822,7 +2794,6 @@ Two stale source-string assertions were broken by #1591's compact() and messages
|
||||
- Browser sanity (HTTP API checks against port 8789): 11/11 endpoints verified.
|
||||
- Pre-release Opus advisor: SHIP AS-IS — all 5 verification questions resolved (race-window bounded, lock ordering safe, no deadlock, frontend integration clean, test isolation robust against assertion failures).
|
||||
|
||||
|
||||
## [v0.50.286] — 2026-05-03
|
||||
|
||||
### Fixed (1 PR — closes #1560)
|
||||
@@ -2841,7 +2812,6 @@ Two stale source-string assertions were broken by #1591's compact() and messages
|
||||
- All modified JS files (`static/i18n.js`, `static/panels.js`) pass `node -c` syntax check.
|
||||
- PR rebase verified clean: `git diff origin/master --stat` shows ONLY the 6 files PR #1561 touches (no spurious deletions of v0.50.284/v0.50.285 test files that the older PR base would have dropped).
|
||||
|
||||
|
||||
## [v0.50.285] — 2026-05-03
|
||||
|
||||
### Fixed (1 PR — same-day hotfix-of-hotfix)
|
||||
@@ -2862,7 +2832,6 @@ Two stale source-string assertions were broken by #1591's compact() and messages
|
||||
|
||||
v0.50.284 promised that "the first server start after deploying v0.50.284 will auto-restore any session that was wiped between deploys." That promise was broken in production by the `_index.json` shape mismatch — the recovery silently never fired. Affected users (the original reporter on v0.50.282 with the 1000+ message session that disappeared) had `<sid>.json.bak` files on disk but those files would never be processed. Same-day hotfix restores the promise.
|
||||
|
||||
|
||||
## [v0.50.284] — 2026-05-03
|
||||
|
||||
### Fixed (2 PRs — P0 streaming hotfix batch — closes #1533, #1558)
|
||||
@@ -2892,7 +2861,6 @@ v0.50.284 promised that "the first server start after deploying v0.50.284 will a
|
||||
- Browser sanity (HTTP API checks against port 8789): 11 endpoints verified.
|
||||
- Pre-release Opus advisor: SHIP AS-IS — all 5 verification questions cleared (conflict-resolution order, deadlock risk none, Layer 3 backup interaction, startup self-heal vs concurrent saves, test-isolation fix correctness). Two SHOULD-FIX items absorbed in-release.
|
||||
|
||||
|
||||
## [v0.50.283] — 2026-05-03
|
||||
|
||||
### Fixed (8 PRs — full sweep batch — closes #1426, #1481, #1512, #1468, #1424, #1457, #1401)
|
||||
@@ -2929,7 +2897,6 @@ v0.50.284 promised that "the first server start after deploying v0.50.284 will a
|
||||
- `static/i18n.js`: pt locale `reveal_in_finder` / `reveal_failed` translations added (Opus-flagged, 2 LOC).
|
||||
- `tests/test_minimax_provider.py::test_minimax_fallback_provider_label` — scoped to direct-MiniMax routes (filter by `minimax/` prefix, exclude `:free`) since #1548's `minimax/minimax-m2.5:free` correctly carries `provider='OpenRouter'` (it routes via OpenRouter, not direct MiniMax).
|
||||
|
||||
|
||||
## [v0.50.282] — 2026-05-03
|
||||
|
||||
### Fixed (1 PR — closes #1538, #1539)
|
||||
@@ -3170,7 +3137,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
- **MIME map expansion** — adds `ttf` (`font/ttf`), `otf` (`font/otf`), and `wasm` (`application/wasm`) to the served-MIME table. `.wasm` specifically would fail to instantiate in Chrome served as `text/plain`; the others are ergonomic for older font formats. (`api/extensions.py`)
|
||||
- **5 regression tests** in `tests/test_pr1445_opus_followups.py` pin the new invariants: quadruple-encoded `..` collapses correctly, the same URL is now rejected by the validator, URL list caps at the configured max with a warning log, rejected URLs log exactly once per process, and the expanded MIME map serves `.ttf`/`.otf`/`.wasm` with the correct Content-Type without charset suffixes for binary types. (`tests/test_pr1445_opus_followups.py`)
|
||||
|
||||
|
||||
## [v0.50.264] — 2026-05-02
|
||||
|
||||
### Added
|
||||
@@ -3243,7 +3209,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
### Changed (Opus pre-release advisor)
|
||||
- **`HERMES_HOME_MODE` semantic asymmetry warning** — Opus advisor caught a footgun in my initial draft: `HERMES_HOME_MODE` means **different things** in the WebUI vs. the agent image. WebUI's `HERMES_HOME_MODE` is a credential-FILE mode threshold (e.g. `0640` allows group bits on `.env`), but the agent's `HERMES_HOME_MODE` is the HERMES_HOME *directory* mode (default `0700`). `0640` on a directory has no owner-execute bit, so the agent can't traverse its own home directory and bricks. My initial draft recommended `HERMES_HOME_MODE=0640` as the example value in agent service blocks — corrected to `0750` (group-traversable) for multi-container setups. All three surfaces now match: compose files (per-service comments), `.env.docker.example` (multi-container warning section), `docs/docker.md` (failure mode #2 callout). 3 new regression tests pin the asymmetry: `test_agent_service_does_not_recommend_invalid_home_mode`, `test_compose_files_warn_about_home_mode_asymmetry`, `test_env_docker_example_warns_about_home_mode_asymmetry`. (`docker-compose.two-container.yml`, `docker-compose.three-container.yml`, `.env.docker.example`, `docs/docker.md`, `tests/test_v050260_docker_invariants.py`)
|
||||
|
||||
|
||||
## [v0.50.259] — 2026-05-01
|
||||
|
||||
### Fixed
|
||||
@@ -3252,7 +3217,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
### Changed (Opus pre-release advisor)
|
||||
- **Same FD-leak fix applied to LRU eviction path** — `SESSION_AGENT_CACHE.popitem(last=False)` was dropping the evicted agent on the floor with `evicted_sid, _ = ...`. The agent's `_session_db` would only release its FDs when GC eventually finalized the agent — which on a long-running server may be never. Now captures the evicted entry, calls `_evicted_agent._session_db.close()` explicitly. Same shape as #1421's fix on the cached-agent reuse path. 5 regression tests in `test_v050259_sessiondb_fd_leak.py` cover both paths plus `SessionDB.close()` idempotency. (`api/streaming.py`, `tests/test_v050259_sessiondb_fd_leak.py`)
|
||||
|
||||
|
||||
## [v0.50.258] — 2026-05-01
|
||||
|
||||
### Fixed
|
||||
@@ -3261,7 +3225,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
### Changed (Opus pre-release advisor)
|
||||
- **Login redirect URL encoding fix — multi-param queries no longer truncated** — the original PR #1419 implementation built the outer `?next=` parameter via `quote(path, safe='/:@!$&\'()*+,;=')` which kept `?` and `&` literal. Two problems: (a) paths with multi-param queries (e.g. `/api/sessions?limit=50&offset=0`) round-tripped as `/api/sessions?limit=50` because the inner `&` terminated the outer `next` value, (b) attacker-controlled paths with embedded `&next=...` injected a second top-level `next` parameter (browsers parse first-match, Python parse_qs parses last-match — parser-divergence footgun even though `_safeNextPath()` rejects the actual exploit). Fix encodes the entire `path?query` blob with `safe='/'` so `?`, `&`, `=` all percent-encode. The outer `next` then holds exactly one path-with-query string. 6 regression tests in `test_v050258_opus_followups.py` pin the round-trip behavior across simple paths, single-query paths, multi-param queries, and attacker-injection neutralization. (`api/auth.py`, `tests/test_v050258_opus_followups.py`)
|
||||
|
||||
|
||||
## [v0.50.257] — 2026-05-01
|
||||
|
||||
### Added
|
||||
@@ -3283,7 +3246,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
|
||||
- **CRITICAL: per-session toolset override (#493) was non-functional** — `_run_agent_streaming` called `_session_meta.get('enabled_toolsets')` on the result of `Session.load_metadata_only()`, which returns a Session **instance** (not a dict). The `AttributeError` was swallowed by the surrounding `except Exception:` block, so the user's toolset chip silently no-op'd every time and the agent always ran with the global toolsets. Caught by Opus pre-release advisor on the empirical streaming path (CI green, contributor tests green — would have shipped non-functional). Fix uses `getattr(_session_meta, 'enabled_toolsets', None)`. Source-level negative-pattern test prevents the dict-access shape from returning. (`api/streaming.py`, `tests/test_v050257_opus_followups.py`)
|
||||
|
||||
|
||||
## [v0.50.256] — 2026-05-01
|
||||
|
||||
### Fixed
|
||||
@@ -3337,7 +3299,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
|
||||
- **`popstate` handler refuses to switch sessions mid-stream** — Opus pre-release follow-up. Mirrors the same `S.busy` guard the cross-tab storage handler had. A user mid-stream who absent-mindedly hits browser Back used to lose their active turn (PR #1392 introduced the popstate listener without the guard). Now shows a toast and stays on the current session. 1 regression test in `test_v050254_opus_followups.py`. (`static/sessions.js`)
|
||||
|
||||
|
||||
### Added
|
||||
- **Messaging sessions get a WebUI handoff path without exposing every raw channel segment** — Weixin and Telegram sessions imported from Hermes Agent are now treated as messaging-source conversations: sidebar results keep only the latest visible session per channel, preserve source metadata through compact/import paths, and avoid destructive/duplicating menu actions that would imply WebUI owns the external channel history. Messaging sessions with enough external conversation rounds show a composer-docked handoff prompt; clicking it generates a transcript card summary for the user without inserting a fake command bubble. This is PR2 for the #1013 channel-handoff direction and intentionally does not cover the separate CLI Session follow-up. (`api/models.py`, `api/routes.py`, `static/index.html`, `static/messages.js`, `static/sessions.js`, `static/style.css`, `static/ui.js`, `tests/test_gateway_sync.py`, `tests/test_issue1013_handoff_dock.py`) @franksong2702 — refs #1013
|
||||
|
||||
@@ -3356,7 +3317,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
|
||||
- **Strip 9 orphan `wiki_*` i18n keys** — Opus pre-release follow-up. Commit `52bfcea` (#1342) leaked `wiki_panel_title`, `wiki_panel_desc`, `wiki_status_label`, `wiki_entry_count`, `wiki_last_modified`, `wiki_not_available`, `wiki_enabled`, `wiki_disabled`, `wiki_toggle_failed` across all 8 locales (72 lines total) from a different branch — zero references outside `i18n.js`. Stripped, with regression test pinning that they don't return. (`static/i18n.js`, `tests/test_v050253_opus_followups.py`)
|
||||
|
||||
|
||||
## [v0.50.252] — 2026-05-01
|
||||
|
||||
### Fixed
|
||||
@@ -3380,7 +3340,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
- **Reasoning trace, tool calls, and partial output preserved on Stop/Cancel** — three distinct data-loss paths fixed: §A reasoning text accumulated in a thread-local `_reasoning_text` was invisible to `cancel_stream()` because it went out of scope when the thread was interrupted; §B live tool calls in thread-local `_live_tool_calls` were similarly lost; §C reasoning-only streams (no visible tokens) produced no partial assistant message because the thinking-block regex strip returned empty string and the `if _stripped:` guard skipped the append. The fix mirrors the existing `STREAM_PARTIAL_TEXT` pattern (#893) by adding two new shared dicts (`STREAM_REASONING_TEXT`, `STREAM_LIVE_TOOL_CALLS`) populated during streaming and read by `cancel_stream()`. The cancel path now appends the partial assistant message when content text, reasoning trace, OR tool calls exist (not just text). Eliminates "paid tokens disappeared" reports on Stop. 8 regression tests covering all three sections plus tools+text combinations. (`api/config.py`, `api/streaming.py`, `tests/test_issue1361_cancel_data_loss.py`) @bergeouss — PR #1375, fixes #1361
|
||||
- **New profiles route sessions to the profile dir on first use, not back to default** — `get_hermes_home_for_profile()` had a `if profile_dir.is_dir(): return profile_dir; return _DEFAULT_HERMES_HOME` fallback. New profiles (no session yet, so no dir) routed every session back to default until the directory existed on disk — making profile switching silently broken for the first session of every new profile. Removed the `is_dir()` guard; the profile path is now returned unconditionally and the directory is created on first use by the agent/session layer. Path traversal is still blocked by the `_PROFILE_ID_RE` regex (`^[a-z0-9][a-z0-9_-]{0,63}$`); R19j tests were updated to pin that the regex is now the sole defense. R19c was tightened to assert the new behavior. 5 regression tests in `test_issue1195_session_profile_routing.py` covering existing-profile, non-existent-profile (the core fix), None, empty-string, and 'default' return paths. (`api/profiles.py`, `tests/test_issue798.py`, `tests/test_issue1195_session_profile_routing.py`) @bergeouss — PR #1373, fixes #1195
|
||||
|
||||
|
||||
## [v0.50.250] — 2026-04-30
|
||||
|
||||
### Fixed
|
||||
@@ -3641,7 +3600,6 @@ This release is the first under the May 2 2026 auto-rebase + auto-fix policy: co
|
||||
- **Pasted/dragged images render inline** — image attachments now show as `<img>` with click-to-fullscreen instead of a paperclip badge. Hoisted `_IMAGE_EXTS` to module scope (was causing `ReferenceError` in `renderMessages`); added `avif` support. (`static/ui.js`) [#1109 @bergeouss] Closes #1095
|
||||
- **Copy buttons on HTTP** — `_copyText()` helper checks `isSecureContext` and falls back to `execCommand('copy')` for plain-HTTP self-hosted installs. Silent failure in `addCopyButtons` fixed with error feedback. All 6 locales get `copy_failed` key. (`static/ui.js`, `static/i18n.js`) [#1107 @bergeouss] Closes #1096
|
||||
|
||||
|
||||
## v0.50.220 — 2026-04-26
|
||||
|
||||
### Fixed
|
||||
@@ -4364,7 +4322,6 @@ Co-authored by @bergeouss.
|
||||
newly created ones). Added `autocomplete="off"` to the search input and an explicit
|
||||
value-clear at boot before the first render. Closes #822. (#830)
|
||||
|
||||
|
||||
## [v0.50.140] — 2026-04-22
|
||||
|
||||
### Fixed
|
||||
@@ -4691,7 +4648,6 @@ Co-authored by @bergeouss.
|
||||
### Fixed
|
||||
- **Auto-title extraction improved for tool-heavy first turns** — sessions where the agent's first response involved tool calls (e.g. memory lookups, file reads) were generating poor titles because the title extractor skipped all assistant messages with `tool_calls`, even when those messages contained substantive visible text. The extractor now picks the first pure (non-tool-call) assistant reply as the title source, using `_looks_invalid_generated_title()` to distinguish meta-reasoning preambles from real agentic replies. Also fixes `_is_provisional_title()` to normalize whitespace before comparing, so CJK text truncated at 64 characters correctly re-triggers title updates. (Closes #639, PR #640 by @franksong2702)
|
||||
|
||||
|
||||
## [v0.50.80] — 2026-04-18
|
||||
|
||||
### Fixed
|
||||
@@ -4786,7 +4742,6 @@ Co-authored by @bergeouss.
|
||||
- **`HERMES_WEBUI_SKIP_ONBOARDING=1` now works unconditionally** — previously the env var was gated on `chat_ready=True`, so hosting providers (e.g. Agent37) that set it but hadn't yet wired up a provider key would still see the wizard on every page load. The var is now honoured as a hard operator override regardless of `chat_ready`. If you set it, the wizard is gone. (Fixes skip-onboarding regression)
|
||||
- **Onboarding wizard can no longer overwrite config or env files when `SKIP_ONBOARDING` is set** — `apply_onboarding_setup` now checks the env var first and refuses to touch `config.yaml` or `.env` if it is set. This is a belt-and-suspenders guard: even if a stale JS bundle somehow triggers the setup endpoint while `SKIP_ONBOARDING` is active, no files are written.
|
||||
|
||||
|
||||
## [v0.50.64] — 2026-04-16
|
||||
|
||||
### Changed
|
||||
@@ -4890,7 +4845,6 @@ calls `sel.onchange`), and uses `S.messages` not `S.session.messages`.
|
||||
|
||||
Total tests: 1272 (was 1268)
|
||||
|
||||
|
||||
## [v0.50.47] fix/feat: batch fixes — root workspace, custom providers, cron cache, system theme
|
||||
|
||||
Synthesized from PRs #506, #507, #508, #509, #510, #514, #515, #519, #521.
|
||||
@@ -4941,7 +4895,6 @@ via `window.matchMedia`. Changes:
|
||||
|
||||
Total tests: 1268 (was 1246)
|
||||
|
||||
|
||||
## [v0.50.46] feat: clarify dialog flow and refresh recovery (#520)
|
||||
|
||||
Adds a full clarify dialog UX for interactive agent questions — modeled after
|
||||
@@ -4996,7 +4949,6 @@ Total tests: 1246 (was 1209)
|
||||
|
||||
Co-authored-by: franksong2702
|
||||
|
||||
|
||||
## [v0.50.45] fix: suppress N/A source_tag in session list (#429)
|
||||
|
||||
Feishu and WeChat sessions (and any session with an unrecognised or legacy
|
||||
@@ -5209,7 +5161,6 @@ New i18n keys for password replacement/keep-existing states (en, es, de, zh, zh-
|
||||
|
||||
- Total tests: 1078 (was 1075)
|
||||
|
||||
|
||||
## [v0.50.38] feat: mobile nav cleanup, Prism syntax highlighting, zh-CN/zh-Hant i18n
|
||||
|
||||
Three community contributions combined:
|
||||
@@ -5261,7 +5212,6 @@ and could silently overwrite their working config.
|
||||
|
||||
- Total tests: 1063 (was 1055)
|
||||
|
||||
|
||||
## [v0.50.36] fix: workspace list cleaner — allow own-profile paths, remove brittle string filter
|
||||
|
||||
Two bugs in `_clean_workspace_list()` caused workspace additions to silently disappear on the next `load_workspaces()` call, breaking `test_workspace_add_no_duplicate` and `test_workspace_rename` (and potentially causing real-world workspace list corruption):
|
||||
@@ -5473,7 +5423,6 @@ The hardcoded lists in `_PROVIDER_MODELS` remain as credential-missing / network
|
||||
- **`static/ui.js`**: `populateModelDropdown()` now calls `_fetchLiveModels()` in the background after rendering the static list. Live models that aren't already in the dropdown are appended to the provider's optgroup. Results are cached per session so only one fetch per provider per page load. Skips Anthropic and Google (unsupported). Falls back to static list silently if the fetch fails.
|
||||
- 25 new tests in `tests/test_issues_373_374_375.py`; 949 tests total (up from 924)
|
||||
|
||||
|
||||
## [v0.50.19] Fix UnicodeEncodeError when downloading files with non-ASCII filenames (PR #378)
|
||||
|
||||
- **Workspace file downloads no longer crash for Unicode filenames** (`api/routes.py`): Clicking a PDF or other file with Chinese, Japanese, Arabic, or other non-ASCII characters in its name caused a `UnicodeEncodeError` because Python's HTTP server requires header values to be latin-1 encodable. A new `_content_disposition_value(disposition, filename)` helper centralises `Content-Disposition` generation: it strips CR/LF (injection guard), builds an ASCII fallback for the legacy `filename=` parameter (non-ASCII chars replaced with `_`), and preserves the full UTF-8 name in `filename*=UTF-8''...` per RFC 5987. Both `attachment` and `inline` responses use it.
|
||||
|
||||
Reference in New Issue
Block a user