Skip to content

feat: add activity feed default expansion setting - #3080

Closed
AJV20 wants to merge 21 commits into
nesquena:masterfrom
AJV20:feat/activity-feed-default-open
Closed

AJV20 wants to merge 21 commits into
nesquena:masterfrom
AJV20:feat/activity-feed-default-open

Conversation

@AJV20

@AJV20 AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a Settings → Appearance checkbox to expand new Activity disclosures by default.
  • Preserves manual per-turn collapse/expand state so an explicit user toggle still wins.
  • Makes live “Waiting on model” activity rows explain what the agent is doing before and after tool calls.

Test plan

  • node --check static/ui.js
  • node --check static/panels.js
  • node --check static/boot.js
  • node --check static/i18n.js
  • python3 -m pytest tests/test_live_activity_timeline.py -q
  • git diff --check

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Thanks for the PR — I read the full diff against origin/master (git -C /tmp/wt-cron-3080 diff origin/master...HEAD) and traced the activity-group lifecycle in static/ui.js. The wiring through api/config.py, static/boot.js, static/panels.js, and the appearance form looks clean and consistent with the existing session_endless_scroll precedent right next to it. The new _activityWaitingDetail() copy in particular is a nice quality-of-life touch over the previous flat "No tool activity has been reported yet." string.

One real bug: the setting works at group creation, then finalizeThinkingCard() slams it shut at end-of-turn

The new opt-in in ensureActivityGroup (static/ui.js:5447-5448):

let collapsed=opts.collapsed!==false;
if(window._activityFeedExpandedDefault===true) collapsed=false;
const savedState=_readActivityDisclosureState(activityKey);
if(live && _liveActivityUserExpanded === true) collapsed=false;
else if(live && _liveActivityUserExpanded === false) collapsed=true;

correctly opens new groups, but finalizeThinkingCard() at static/ui.js:7850-7864 runs at the end of every turn and force-collapses unless the user has explicitly clicked to expand:

if(_liveActivityUserExpanded !== true){
  group.classList.add('tool-call-group-collapsed');
  const summary=group.querySelector('.tool-call-group-summary');
  if(summary) summary.setAttribute('aria-expanded','false');
}

For someone who turned the setting on, _liveActivityUserExpanded stays undefined (they never had to click — that's the whole point of the toggle), so the group expands while streaming and then snaps shut the moment the assistant's response finalizes. That's almost certainly not the intent, and it's the exact failure mode the _liveActivityUserExpanded mechanism was built to prevent (the #1298 comment block at ui.js:5381-5394 calls this out explicitly).

Suggested fix — relax the force-collapse to also honor the default-expanded setting:

const shouldKeepOpen = (_liveActivityUserExpanded === true)
  || (window._activityFeedExpandedDefault === true && _liveActivityUserExpanded !== false);
if(!shouldKeepOpen){
  group.classList.add('tool-call-group-collapsed');
  const summary=group.querySelector('.tool-call-group-summary');
  if(summary) summary.setAttribute('aria-expanded','false');
}

The _liveActivityUserExpanded !== false clause preserves the existing "user explicitly collapsed mid-turn" semantics: if they actively close it during streaming, finalize still respects that.

Smaller note: persisted disclosure state for settled turns

The new branch runs before _readActivityDisclosureState() at ui.js:5448-5454:

let collapsed=opts.collapsed!==false;
if(window._activityFeedExpandedDefault===true) collapsed=false;
const savedState=_readActivityDisclosureState(activityKey);

if(savedState==='open') collapsed=false;
else if(savedState==='closed') collapsed=true;

That ordering is correct (saved closed-state wins for settled assistant:${aIdx} groups the user has explicitly closed). Worth a one-line comment though, since "default expanded loses to explicit closed" is exactly the right behavior but isn't obvious from the diff alone — easy to mis-refactor later.

Test coverage

tests/test_activity_feed_default_expand_setting_is_wired checks the literal if(window._activityFeedExpandedDefault===true) collapsed=false; substring is present. That catches the wiring but not the finalize regression above — a stronger assertion would be a regex-style check that finalizeThinkingCard's collapse guard mentions _activityFeedExpandedDefault, e.g.:

assert "_activityFeedExpandedDefault" in UI_JS.split("function finalizeThinkingCard")[1].split("\nfunction ")[0]

so the test fails if someone touches finalizeThinkingCard without updating the guard.

Verification plan

  1. Enable the new setting in appearance.
  2. Send a message that triggers a thinking → tool → thinking → response sequence.
  3. Confirm the Activity panel stays open after the response settles, not just during streaming.
  4. Manually click to collapse during streaming → confirm finalize leaves it collapsed.

Without (1+2+3) you'll get the "looks fine while streaming, snaps shut on done" UX which is more annoying than not having the setting at all.

@AJV20

AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in fa318778.

What changed:

  • finalizeThinkingCard() now keeps the live Activity group open when activity_feed_expanded_default is enabled, unless the user explicitly collapsed it during the turn.
  • Added a guard assertion so the default-expanded setting remains represented in the finalize path.
  • Added a comment clarifying that saved settled-turn disclosure state still wins over the default-expanded preference.

Verification:

  • node --check static/ui.js
  • python3 -m pytest tests/test_live_activity_timeline.py -q
  • python3 -m pytest tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py -q
  • git diff --check

@AJV20

AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up CI fix in 8f9c0a78.

The full suite had a static contract test that still expects the explicit _liveActivityUserExpanded !== true collapse guard in finalizeThinkingCard(). I rewrote the new default-expanded exception into that existing guard shape instead of hiding it behind a derived variable.

Additional verification:

  • node --check static/ui.js
  • python3 -m pytest tests/test_live_activity_timeline.py tests/test_issue1298_cancel_and_activity.py::TestIssue1298ActivityGroupExpandPersistence::test_finalize_thinking_card_respects_user_expand tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py -q
  • git diff --check

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Triage: HOLD pending UX review — labels: hold, ux

Two pieces here:

  1. Settings → Appearance toggle to expand Activity disclosures by default. Visible settings UI surface — needs the standard screenshot pass at 390/1280/1440/1920 plus a comparison of new-turn activity behavior with and without the toggle. Per-turn manual collapse/expand winning over the default is the right contract; want to see it in action.

  2. Waiting-on-model subtext. Replacing "Waiting on model" with a step hint (prompt/context review, latest tool action) is a meaningful change to the always-visible streaming UI. We'd like to see screenshots of each subtext state including the "before any tool ran" pre-step case to confirm the copy reads cleanly.

When you can share evidence at the four viewports for both pieces, we'll take another look. The backend additions (i18n strings, config knob, settings validator) look fine in isolation.

@AJV20

AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

UX evidence pass for the Activity default-expanded setting.

I re-checked the branch after the finalize-guard follow-up and exercised the requested viewport matrix at 390, 1280, 1440, and 1920 px. For each viewport I compared three states:

  • default off: new Activity disclosure stays collapsed (aria-expanded=false, body hidden)
  • default on: new Activity disclosure opens automatically (aria-expanded=true, body visible)
  • default on + manual collapse: explicit per-turn collapse still wins (aria-expanded=false, body hidden)

Observed state matrix:

  • 390 px: off collapsed; on open; manual collapse closed
  • 1280 px: off collapsed; on open; manual collapse closed
  • 1440 px: off collapsed; on open; manual collapse closed
  • 1920 px: off collapsed; on open; manual collapse closed

Additional verification:

node --check static/ui.js
python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_issue1298_cancel_and_activity.py::TestIssue1298ActivityGroupExpandPersistence -q -o addopts=
# 10 passed
git diff --check origin/master...HEAD

So the UX contract I see is: the setting only changes the initial state for newly-created Activity groups, while saved/manual per-turn disclosure state still overrides it. That matches the helper text in Settings → Appearance.

@AJV20

AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master to clear the merge-conflict blocker after the release batch.

Verification on the refreshed head:

  • node --check static/ui.js static/boot.js static/panels.js
  • python3 -m py_compile api/config.py
  • pytest tests/test_live_activity_timeline.py tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py -q (29 passed)
  • git diff --check origin/master...HEAD

Current note: this remains on UX hold pending the requested review; I only resolved the base conflict and kept the existing Activity/default-expanded scope.

@AJV20

AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current master again to clear the new changelog conflict after the latest release batch.

Verification on head 601687af:

  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • python3.11 -m py_compile api/config.py
  • python3.11 -m pytest tests/test_live_activity_timeline.py -q -o addopts=
  • git diff --check

The UX-review hold still stands; this update only removes the merge-conflict blocker.

@AJV20

AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current master after release EC moved the base.

Verification on head d0fc4709:

  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • python3.11 -m py_compile api/config.py
  • python3.11 -m pytest tests/test_live_activity_timeline.py -q -o addopts=
  • git diff --check

The UX-review hold still stands; this update only removes the merge-conflict blocker.

@AJV20

AJV20 commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

UX evidence follow-up for the Activity default-expanded setting.

I ran a fresh isolated browser pass on the current head and captured the requested viewport/state matrix at 390, 1280, 1440, and 1920 px. For each viewport I compared:

  • default off: new live Activity disclosure remains collapsed (aria-expanded=false, body hidden)
  • default on: new live Activity disclosure opens automatically (aria-expanded=true, body visible)
  • default on + manual collapse: explicit user collapse still wins (aria-expanded=false, body hidden)
  • default on + running tool: live summary updates from the generic pre-tool state to the concrete tool progress state

Observed DOM/accessibility matrix:

390 px:  off collapsed; on open; manual collapse closed; running tool open with summary "Activity: 1 tool Reading files"
1280 px: off collapsed; on open; manual collapse closed; running tool open with summary "Activity: 1 tool Reading files"
1440 px: off collapsed; on open; manual collapse closed; running tool open with summary "Activity: 1 tool Reading files"
1920 px: off collapsed; on open; manual collapse closed; running tool open with summary "Activity: 1 tool Reading files"

The pre-tool visible subtext remains: Reviewing the prompt and context, then choosing the next action or composing the response. After a running read_file card is present, the summary reports the concrete tool action (Reading files) instead of a generic waiting state.

Additional verification on the same head:

python3.11 -m py_compile api/config.py
node --check static/ui.js
node --check static/boot.js
node --check static/panels.js
python3.11 -m pytest tests/test_live_activity_timeline.py -q -o addopts=
# 5 passed

Conclusion: the setting behaves consistently across the requested viewport widths, and the user’s explicit per-turn collapse still takes precedence over the default-expanded preference.

@AJV20

AJV20 commented May 29, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master to resolve the CHANGELOG.md conflict from the latest release batch.

Verification on the refreshed head 2f0d8046:

  • python3.11 -m py_compile api/routes.py api/streaming.py
  • node --check static/boot.js
  • node --check static/sessions.js
  • node --check static/ui.js
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_ui_tool_call_cleanup.py -q (26 passed)
  • git diff --check

No scope change beyond merging current master and preserving the PR's Unreleased changelog entry.

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master to clear the release-batch CHANGELOG.md conflict. The existing head still preserves the default-expanded Activity behavior through finalizeThinkingCard() unless the user manually collapses the live group. Verification on refreshed head 2813a65a:

  • python3.11 -m py_compile api/config.py
  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_issue1298_cancel_and_activity.py tests/test_ui_tool_call_cleanup.py -q -o addopts=
  • python3.11 -m pytest tests/test_1003_appearance_autosave.py tests/test_1003_preferences_autosave.py tests/test_batch_fixes.py -q -o addopts=

Results: 36 passed, 1 warning and 42 passed, 1 warning.

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master again to clear the release-batch CHANGELOG.md conflict.

Verification on refreshed head d03d4fef:

  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3.11 -m py_compile api/config.py
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py -q -o addopts=
  • git diff --check

The default-expanded Activity behavior and waiting-detail coverage are unchanged; this push only refreshes the PR onto the latest base.

…default-open

# Conflicts:
#	CHANGELOG.md
#	static/ui.js
#	tests/test_live_activity_timeline.py
@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master to clear the latest release-batch conflicts in CHANGELOG.md, static/ui.js, and the live-activity regression tests.

The merge keeps the Activity default-expanded behavior through finalizeThinkingCard() unless the user manually collapses the live group, while also preserving the newer stream-start / first-token / post-tool waiting states from master.

Verification on refreshed head 44dd039c:

  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3.11 -m py_compile api/config.py
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py -q -o addopts= (30 passed, 1 warning)
  • git diff --check

The existing UX-review hold remains unchanged; this update only removes the merge-conflict blocker.

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master after the release batch and resolved the CHANGELOG.md conflict by keeping this PR's Unreleased activity-setting entries above the new release history.

Verification on head c28f5949:

  • git diff --check
  • python3 -m py_compile api/config.py
  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_issue1413_li_path_coverage.py tests/test_ui_tool_call_cleanup.py -q (30 passed)

I also re-checked the earlier end-of-turn expansion concern: finalizeThinkingCard() now preserves the default-expanded preference unless the user manually collapsed the live Activity group, and the focused static regression asserts that branch.

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master to clear the latest release-batch conflict while preserving this PR's Unreleased Activity-setting entries above the new release history.

Verification on refreshed head 9680b08b:

  • python3.11 -m py_compile api/config.py
  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_1003_appearance_autosave.py tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py
  • git diff --check

The maintainer UX hold still needs the requested screenshot/evidence pass; this refresh only removes the merge-conflict blocker.

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

UX evidence follow-up for the Activity disclosure setting and live status copy.

I ran a fresh isolated browser pass on the current head and checked the requested viewport matrix at 390, 1280, 1440, and 1920 px. The pass opened Settings → Appearance, verified the new toggle is present/visible, then compared new-turn Activity behavior with the default off, default on, a manual collapse override, and live running/post-tool states.

Observed DOM/accessibility matrix:

  • 390 px: settings toggle visible; default off → aria-expanded=false, collapsed class present, body hidden; default on → aria-expanded=true, collapsed class absent, body visible; default on + manual collapse → aria-expanded=false, body hidden; live tool states showed Waiting on tool result with Searching workspace: search_files. Results will appear here. and post-tool Waiting on model with Last step: Searching workspace (search_files); now choosing the next action or composing a response.
  • 1280 px: same results as 390 px.
  • 1440 px: same results as 390 px.
  • 1920 px: same results as 390 px.

The pre-tool state remained visible as Waiting for first model token with Stream connected; no model output has arrived yet. when Activity was expanded, and stayed hidden from the body when the disclosure was collapsed.

Additional verification on the same head:

python3.11 -m py_compile api/config.py
node --check static/ui.js
node --check static/boot.js
node --check static/panels.js
python3.11 -m pytest tests/test_live_activity_timeline.py -q
# 6 passed, 1 warning
git diff --check

Conclusion: the expanded-by-default preference is visible in Appearance across the requested widths, applies only as the initial new-turn disclosure state, preserves manual per-turn collapse, and the live status subtext remains concrete across pre-tool, running-tool, and post-tool waiting states.

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master after the latest release batch; head is now cbd3bc64.

Conflict resolution was limited to CHANGELOG.md: kept this PR's ## [Unreleased] Activity-setting entries above the new release history.

Verification on refreshed head:

  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3.11 -m py_compile api/config.py
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_1003_appearance_autosave.py tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py -q -o addopts= (36 passed, 1 warning)
  • git diff --check

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master after the latest release batch; head is now e844f620. Conflict resolution was limited to CHANGELOG.md: kept this PR's ## [Unreleased] Activity-setting entries above the new release history.

Verification on refreshed head:

  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3.11 -m py_compile api/config.py
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_1003_appearance_autosave.py tests/test_issue1413_li_path_coverage.py tests/test_ui_tool_call_cleanup.py -q -o addopts= (36 passed, 1 warning)
  • git diff --check

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master after the latest release batch; head is now 17e1adc7.

Conflict resolution was limited to CHANGELOG.md: kept this PR's ## [Unreleased] Activity-setting entries above the new release history.

Verification on refreshed head:

  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3.11 -m py_compile api/config.py
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_1003_appearance_autosave.py tests/test_ui_tool_call_cleanup.py tests/test_issue1413_li_path_coverage.py -q -o addopts= (36 passed, 1 warning)
  • git diff --check

The UX hold remains; this only clears the merge-conflict blocker.

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master after the latest release batch; head is now 22153594.

Conflict resolution was limited to CHANGELOG.md: kept this PR's ## [Unreleased] Activity-setting entries above the new release history.

Verification on refreshed head:

  • git diff --check
  • python3.11 -m py_compile api/config.py
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/ui.js
  • node --check static/i18n.js
  • python3.11 -m pytest tests/test_live_activity_timeline.py -q -o addopts= → 6 passed

@AJV20

AJV20 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current origin/master after the latest release batch; head is now e0d787a9.

Conflict resolution was limited to CHANGELOG.md: kept this PR's Activity-setting ## [Unreleased] entries above the current release history.

Verification on refreshed head:

  • git diff --check
  • python3.11 -m py_compile api/config.py
  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3.11 -m pytest tests/test_live_activity_timeline.py tests/test_issue1298_cancel_and_activity.py tests/test_ui_tool_call_cleanup.py -q -o addopts= → 37 passed, 1 warning

@AJV20

AJV20 commented May 31, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current master to clear the changelog conflict after the latest release batch.

Head: 994598be

Verification:

  • python3 -m py_compile api/config.py api/streaming.py
  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3 -m pytest tests/test_live_activity_timeline.py tests/test_issue1413_li_path_coverage.py tests/test_ui_tool_call_cleanup.py -q -o addopts= → 30 passed
  • git diff --check
  • Isolated rerun for the transient CI failure: python3 -m pytest tests/test_tls_support.py::TestTLSEndToEnd::test_http_without_tls_still_works -q -o addopts= → 1 passed

The failed-job rerun API requires admin rights, so I amended/re-pushed the same merge resolution to retrigger checks.

@AJV20
AJV20 force-pushed the feat/activity-feed-default-open branch from 0ef756b to 994598b Compare May 31, 2026 00:58
@AJV20

AJV20 commented May 31, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against current master to clear the changelog conflict after the latest release batch. The conflict resolution preserved this PR's Activity-setting ## [Unreleased] entries and kept the new upstream browser-smoke entry above the release history.

Head: b97be0c1

Verification:

  • python3 -m py_compile api/config.py api/streaming.py
  • node --check static/ui.js
  • node --check static/boot.js
  • node --check static/panels.js
  • node --check static/i18n.js
  • python3 -m pytest tests/test_live_activity_timeline.py tests/test_issue1413_li_path_coverage.py tests/test_ui_tool_call_cleanup.py -q -o addopts= → 30 passed, 1 warning
  • git diff --check

nesquena-hermes added a commit that referenced this pull request Jun 3, 2026
…age ring #3062 + activity-feed default-expand setting #3080) (#3459)

* feat(composer): replace mobile config-button sliders icon with context-usage ring (#3062, @NottheGuy007)

Squashed net diff of #3062 (the PR branch's tip commits were deletes of files not
present in our tree). Replaces the composerMobileCtxBadge text badge with an SVG
progress ring (ctx-arc + ctx-num) showing real-time context-window usage: ring
fill via stroke-dashoffset, centered percentage, color-coded green<=50%
orange<=85% red>85%. Ring resets to 0%/green on new session.

* feat(activity): add 'expand activity feed by default' appearance setting (#3080, @AJV20)

Squashed net diff of #3080. Adds a Settings -> Appearance checkbox
(activity_feed_expanded_default, default off) to expand new Activity disclosures
by default; preserves manual per-turn collapse/expand (explicit toggle still
wins); live 'Waiting on model' rows explain what the agent is doing before/after
tool calls. i18n keys for all locales.

* docs(changelog): v0.51.226 — context-usage ring (#3062) + activity-feed default-expand setting (#3080)

* test(mobile): update touch-target test for #3062 ring (badge removed)

#3062 replaced the composerMobileCtxBadge text badge with the SVG context-usage
ring (composerMobileCtxRing) but left 3 stale references in tests/test_mobile_layout.py.
The full suite caught test_mobile_composer_primary_controls_keep_touch_friendly_sizing
asserting the removed .composer-mobile-ctx-badge CSS rule + #composerMobileCtxBadge
element. Updated the assertion to the new ring: confirm composerMobileCtxRing exists,
the old badge is fully gone (not dangling), and the ring SVG is aria-hidden so it
stays a decorative overlay that doesn't steal the config button's 44px touch target
(which is still asserted via .composer-mobile-config-btn above). 56/56 mobile-layout
tests pass. Codex: no production JS dereferences the removed badge.

---------

Co-authored-by: nesquena-hermes <[email protected]>
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Shipped in v0.51.226 ✅ — cherry-picked (squashed net diff) onto release stage-p9, screenshot-approved. The 'Expand activity feed by default' Settings → Appearance checkbox is live. Thanks @AJV20! Closing as merged-via-release.

eleboucher pushed a commit to eleboucher/homelab that referenced this pull request Jun 3, 2026
…➔ 0.51.230) (#798)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.210` → `0.51.230` |

---

### Release Notes

<details>
<summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary>

### [`v0.51.230`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051230--2026-06-03--Release-GX-stage-p14--extract-think-blocks-to-mreasoning--LLM-Wiki-last-writer)

[Compare Source](nesquena/hermes-webui@v0.51.229...v0.51.230)

##### Fixed

- Assistant message `<think>…</think>` blocks are now extracted into `m.reasoning` instead of being stored inline in `m.content` — **both client-side (streaming/inflight state) and server-side at save time**. Reasoning-only providers such as `MiniMax-M3` (OpenAI-compat) previously left the thinking trace inside the assistant content, bloating persisted session files by 30–50% and bypassing the `m.reasoning` field the thinking card reads on reload. A new `_splitThinkFromContent()` (in `static/messages.js`) and its server-side twin `_split_thinking_from_content()` (in `api/streaming.py`, applied to the final assistant message before `s.save()`) extract a single **leading** block (after lstrip) for all three known tag pairs, matching the live renderer's `_streamDisplay`/`_parseStreamState` semantics exactly: a closed `<think>…</think>` that appears mid-body (e.g. a literal tag inside a fenced code block) stays visible content and is never moved into reasoning, a partial/unclosed block is left intact, and any pre-existing `m.reasoning` (from a separate `on_reasoning` stream) is preserved/merged. So the persisted session file — not just the in-browser copy — is compacted on reload ([#&#8203;3455](nesquena/hermes-webui#3455) part 1, [@&#8203;gsurenull](https://github.com/gsurenull)).
- The LLM Wiki status panel's `Last writer` field is now populated (it always showed `Not available` since the panel shipped in [#&#8203;1257](nesquena/hermes-webui#1257)). The reader uses a 3-tier fallback — most-recent page frontmatter (`updated_by`/`writer`/`author`), the most recent `log.md` action verb, then a static `ai-agent` fallback — and reads only frontmatter + log headings, never page bodies, preserving the private-safe status contract ([#&#8203;3455](nesquena/hermes-webui#3455) part 2, [@&#8203;gsurenull](https://github.com/gsurenull); closes [#&#8203;1257](nesquena/hermes-webui#1257)).

### [`v0.51.229`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051229--2026-06-03--Release-GW-stage-p13--model-never-silently-snaps-a-versioned-name-to-a--tier-variant)

[Compare Source](nesquena/hermes-webui@v0.51.228...v0.51.229)

##### Fixed

- `/model <name>` no longer silently snaps a complete versioned model name to a longer `-tier` variant (and a different price tier). When the typed name ends in a version number (e.g. `mimo-v2.5`) and the catalog has only a longer suffixed variant (e.g. `xiaomi/mimo-v2.5-pro`), both the dropdown matcher (`_findModelInDropdown`) and the command fallback (`_bestModelMatch`) now reject the snap unless the extra text *continues the version* (`.` + digit), rather than upgrading the user to a `-pro`/`-flash` tier they did not type. When nothing matches cleanly, `/model` now shows a *"No model matching … — did you mean …?"* suggestion toast instead of silently switching. Legitimate fuzzy shorthand is preserved (`/model gpt-5` → `gpt-5.4-mini`, `/model claude` → `claude-opus-4.6`, `/model mimo-v2` → `mimo-v2.5-pro`), as is exact-match priority ([#&#8203;3368](nesquena/hermes-webui#3368), with [@&#8203;garyd9](https://github.com/garyd9); thanks [@&#8203;yutaotie](https://github.com/yutaotie) for confirmation).

### [`v0.51.228`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051228--2026-06-03--Release-GV-stage-p12--workspace-file-tree-drop--large-markdown-preview)

[Compare Source](nesquena/hermes-webui@v0.51.227...v0.51.228)

##### Fixed

- Dropping an OS file onto the **workspace file tree** now uploads it into the workspace only, instead of *also* attaching it to the chat composer. The tree's drag handlers now stop event propagation for OS `Files` drops so the document-level composer drop handler no longer fires for the same drop ([#&#8203;3411](nesquena/hermes-webui#3411), [@&#8203;pamnard](https://github.com/pamnard)).
- Moderately large Markdown documents in the **workspace preview** are no longer forced into plain-text too early. The rich-render ceiling is raised (64 KB / 1500 lines → 256 KB / 5000 lines, and the backend file-read limit 200 KB → 400 KB), and files above the limit gain a **"Render as markdown anyway"** button that force-renders the already-loaded content without a second fetch ([#&#8203;3378](nesquena/hermes-webui#3378), [@&#8203;starGazerK](https://github.com/starGazerK)).

### [`v0.51.227`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051227--2026-06-03--Release-GU-stage-p11--keep-the-active-New-Chat-visible-in-the-sidebar)

[Compare Source](nesquena/hermes-webui@v0.51.226...v0.51.227)

##### Fixed

- A freshly-created **New Chat** now stays visible and selected in the sidebar before its first message is sent. The sidebar intentionally filters inactive 0-message sessions, but that filter also hid the *currently active* blank chat until the user sent a turn — so starting a New Chat could make the selected row vanish from the list. The active ephemeral session is now injected into the sidebar render rows (only when the server-side list omits it), while inactive empty sessions stay filtered as before. Starting a New Chat from a CLI-filtered sidebar also switches the source filter back to WebUI so the active chat isn't immediately hidden ([#&#8203;3408](nesquena/hermes-webui#3408), [@&#8203;AJV20](https://github.com/AJV20)).

### [`v0.51.226`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051226--2026-06-03--Release-GT-stage-p9--mobile-composer-context-usage-ring--activity-feed-default-expand-setting)

[Compare Source](nesquena/hermes-webui@v0.51.225...v0.51.226)

##### Added

- **Settings → Appearance: "Expand activity feed by default"** — a new checkbox (default off) that expands new Activity disclosures by default as turns arrive. Manual per-turn collapse/expand still wins (an explicit user toggle is preserved), and live "Waiting on model" rows now explain what the agent is doing before and after tool calls ([#&#8203;3080](nesquena/hermes-webui#3080), [@&#8203;AJV20](https://github.com/AJV20)).

##### Changed

- The mobile composer's config button now shows a **context-usage ring** (an SVG progress ring with a centered percentage) in place of the static sliders icon, color-coded green (≤50%) / orange (≤85%) / red (>85%) and reset to 0% on a new session, so context-window pressure is visible at a glance on mobile ([#&#8203;3062](nesquena/hermes-webui#3062), [@&#8203;NottheGuy007](https://github.com/NottheGuy007)).

### [`v0.51.225`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051225--2026-06-03--Release-GS-stage-p7--remote-gateway-health-probe-resolves-gatewaystate)

[Compare Source](nesquena/hermes-webui@v0.51.224...v0.51.225)

##### Fixed

- The remote-gateway health probe now correctly reports `gateway_state`, so the Tasks/Cron banner lights up for Docker / remote-gateway deployments. The probe previously hit `/health` and `/status` (neither returns `gateway_state`) and never queried `/health/detailed` (which does), so `gateway_state == "running"` was never observed remotely. The probe now tries `/health/detailed` first, parses the JSON body of a 2xx response to extract `gateway_state`, and unifies the gateway base-URL env precedence to `GATEWAY_HEALTH_URL` > `HERMES_GATEWAY_HEALTH_URL` > `HERMES_API_URL` ([#&#8203;3355](nesquena/hermes-webui#3355), [@&#8203;rodboev](https://github.com/rodboev)).

### [`v0.51.224`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051224--2026-06-03--Release-GR-stage-p6--profile-toolskill-config-authoritative-on-the-streaming-worker)

[Compare Source](nesquena/hermes-webui@v0.51.223...v0.51.224)

##### Fixed

- Profile tool/skill restrictions are now respected for WebUI chats even when the per-session "Tool Restrictions" field is left blank. The streaming agent runs on a detached worker thread that does not inherit the per-request thread-local profile context, so the ambient `get_config()` resolved the process-global `default` profile and loaded its `platform_toolsets.cli` (all tools) instead of the session profile's configured list — inflating a tools-disabled profile's prompt from \~400 to \~15K input tokens. The worker now reads the session's own profile config explicitly via a new `get_config_for_profile_home()` helper (a race-free direct disk read with no shared-cache mutation), so toolsets, prefill context, and fallback chains all match the profile the session actually runs under ([#&#8203;3294](nesquena/hermes-webui#3294), [@&#8203;nesquena-hermes](https://github.com/nesquena-hermes)).

### [`v0.51.223`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051223--2026-06-02--Release-GQ-stage-p5--openai-api-first-class-picker-provider--MiniMax-M3)

[Compare Source](nesquena/hermes-webui@v0.51.222...v0.51.223)

##### Fixed

- GPT models now appear in the model picker when hermes-agent exposes its built-in OpenAI provider under the `openai-api` slug (the one activated by `OPENAI_API_KEY` / `OPENAI_BASE_URL`, distinct from `openai-codex`). `openai-api` is now a first-class picker provider in `_PROVIDER_DISPLAY` / `_PROVIDER_MODELS` rather than an alias of `openai` — an alias would have fixed the display but broken the send path, since the agent registry has `openai-api` and not `openai`. Env detection for `OPENAI_API_KEY` was also corrected to surface `openai-api` instead of a bare `openai` the agent registry can't resolve ([#&#8203;3443](nesquena/hermes-webui#3443), [@&#8203;rodboev](https://github.com/rodboev)).

##### Changed

- MiniMax default model catalog upgraded to M3 in the model picker ([#&#8203;3374](nesquena/hermes-webui#3374), [@&#8203;octo-patch](https://github.com/octo-patch)).

### [`v0.51.222`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051222--2026-06-02--Release-GP-stage-p4--backend-bugfix-batch-title-language-drift--orphaned-CLI-sidecar-prune--pin-quota-lineage)

[Compare Source](nesquena/hermes-webui@v0.51.221...v0.51.222)

##### Fixed

- Auto-generated session titles no longer persist in the wrong language. The title-language guard previously only rejected English titles for *German* conversation starts, so an English chat whose LLM-generated title came back in Chinese, Russian, or another script sailed through and was saved. `_title_language_mismatch` now also does a language-agnostic cross-script check: when the conversation start has a clear dominant writing script and the generated title introduces a substantial amount of a different script (CJK / Cyrillic / Arabic / etc.), the title is rejected and generation falls back to the deterministic topic title. The threshold tolerates a borrowed technical term (a CJK title with one English word still trips; an English title with a single foreign place-name does not), and the legacy German→English heuristic is preserved ([#&#8203;3293](nesquena/hermes-webui#3293)).
- WebUI sidebar now reconciles orphaned imported-CLI sessions. When a CLI/agent session is opened in the WebUI it gets a WebUI-owned sidecar so it can render and reopen; previously, if the user then deleted that session from the CLI / local Hermes storage, nothing pruned the sidecar and the stale row lingered in the sidebar indefinitely (there is no WebUI delete affordance for CLI rows). Orphaned sidecars whose backing session no longer exists are now pruned on reconciliation ([#&#8203;3238](nesquena/hermes-webui#3238)).
- Pin quota is now counted by visible session lineage rather than raw session rows, so continuation siblings in the same sidebar-visible lineage no longer each consume a separate pin slot. Previously a pinned session that had been compressed/continued into multiple rows could exhaust the pin limit with what the user sees as a single pinned conversation. The limit check now collapses each lineage to its visible root before counting against `pinned_sessions_limit` ([#&#8203;3288](nesquena/hermes-webui#3288), [@&#8203;andrewkangkr](https://github.com/andrewkangkr)).

### [`v0.51.221`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051221--2026-06-02--Release-GO-stage-p3e--block-all-workspace-symlink-escapes-security)

[Compare Source](nesquena/hermes-webui@v0.51.220...v0.51.221)

##### Security

- The workspace file API now blocks **all** symlink escapes from the selected workspace, not just symlinks pointing at system directories. Previously a symlink placed inside a workspace could resolve to an arbitrary external host path (e.g. `~/.ssh`, `~/.hermes/auth.json`) and be read through `/api/list` / `read_file_content` — and since that API is reachable by LLM agent tool calls, an imported or crafted workspace could expose credentials. `safe_resolve_ws` now requires the resolved path stay under the workspace root, `list_dir` hides escaping symlinks (they could never be opened anyway), and `read_file_content` rejects them. Symlinks that resolve back under the workspace still work normally. The directory-list, file-read, file-upload, and archive-extraction paths are additionally hardened against a symlink-swap **TOCTOU** race: each path is opened component-by-component from the workspace root with `O_NOFOLLOW` (an anchored `openat` walk on Linux/macOS, with a plain-open fallback on platforms without `dir_fd` support such as Windows, where creating symlinks needs admin anyway), so a symlink raced into any component after the containment check cannot redirect the read/list/write outside the workspace. Note: an intentional in-workspace symlink pointing to an external directory is no longer followed ([#&#8203;3398](nesquena/hermes-webui#3398), [@&#8203;Hinotoi-agent](https://github.com/Hinotoi-agent)).

### [`v0.51.220`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051220--2026-06-02--Release-GN-stage-p3c--fix-aux-title-generation-with-provider-model-ids)

[Compare Source](nesquena/hermes-webui@v0.51.219...v0.51.220)

##### Fixed

- Manual session-title regeneration and background auxiliary title generation no longer fail with `422` / `llm_error_aux` when `auxiliary.title_generation.model` in `config.yaml` is set using the WebUI model-picker's `@provider:model` format (e.g. `@gemini:gemini-3.1-flash-lite`). The `@provider:` prefix is now normalized away via the canonical helper before the id reaches the provider API ([#&#8203;3430](nesquena/hermes-webui#3430), [@&#8203;pamnard](https://github.com/pamnard)).

### [`v0.51.219`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051219--2026-06-02--Release-GM-stage-p3b--extend-URI-scheme-model-ID-fix-to-backend-normalization--matching)

[Compare Source](nesquena/hermes-webui@v0.51.218...v0.51.219)

##### Fixed

- Extended the [#&#8203;3429](nesquena/hermes-webui#3429) URI-scheme fix beyond the visible model chip (fixed in v0.51.218) to the model-identity normalization and matching paths: `api/config.py` `_norm_model_id` / `_get_label_for_model` and `static/ui.js` `_normalizeConfiguredModelKey` no longer strip the first `/`-segment of a `scheme://` id (e.g. `gpt://${FOLDER}/model/latest`), where the slashes are path separators rather than a provider prefix. This prevents the [#&#8203;3360](nesquena/hermes-webui#3360 identity collision/mislabel for URI-shaped model IDs in dropdown matching, badge assignment, and configured-entry dedup. Backend/front-end parity is covered by tests ([#&#8203;3436](nesquena/hermes-webui#3436), [@&#8203;b3nw](https://github.com/b3nw)).

### [`v0.51.218`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051218--2026-06-02--Release-GL-stage-p3a--fix-getModelLabel-mangling-URI-scheme-model-IDs)

[Compare Source](nesquena/hermes-webui@v0.51.217...v0.51.218)

##### Fixed

- The composer model chip no longer shows env-var path junk for model IDs that use a URI scheme (e.g. Yandex `gpt://${FOLDER}/deepseek-v4-flash/latest`). A regression from [#&#8203;3366](nesquena/hermes-webui#3366) (v0.51.210): `getModelLabel()` stripped the first `/`-segment, which for a `scheme://` id landed inside the `://` and left `/${FOLDER}/…`. The label now detects a URI scheme, drops scheme + authority, and takes the last meaningful path segment (skipping `${…}` placeholders and bare version tails like `latest`); non-URI multi-slash IDs keep their [#&#8203;3360](nesquena/hermes-webui#3360) behavior ([#&#8203;3429](nesquena/hermes-webui#3429)).

### [`v0.51.217`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051217--2026-06-02--Release-GK-stage-p2f--decode-and-complete-zh-Hant-locale-strings)

[Compare Source](nesquena/hermes-webui@v0.51.216...v0.51.217)

##### Changed

- Decoded the `zh-Hant` (Traditional Chinese) locale block from `\u`-escaped sequences to literal Chinese text and backfilled missing keys so `zh-Hant` now has full coverage of the English key set. Makes future locale review readable and prevents newer UI keys from falling back to English for Traditional Chinese users. Locale-only — no runtime behavior change ([#&#8203;3414](nesquena/hermes-webui#3414), [@&#8203;PeterDaveHello](https://github.com/PeterDaveHello)).

##### Fixed

- Added the missing `provider_mismatch_warning` string to the French (`fr`) locale. It was absent entirely; the gap was masked by a stale duplicate of the same key in the `zh-Hant` block that [#&#8203;3414](nesquena/hermes-webui#3414) removed, so all locales now carry the key.

### [`v0.51.216`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051216--2026-06-02--Release-GJ-stage-p2e--fix-consecutive-user-turn-rejection-on-strict-chat-templates)

[Compare Source](nesquena/hermes-webui@v0.51.215...v0.51.216)

##### Fixed

- WebUI session/delivery context (connected platforms, home channels, scheduled-task delivery hints) is now injected into the ephemeral **system prompt** instead of being appended as a prefill `user` message. The old prefill produced two consecutive `user` turns (session context + the actual message), which models with strict chat templates (Mistral, Gemma via llama.cpp) reject with a Jinja 500. The same context is preserved — just delivered in a role-alternation-safe place ([#&#8203;3324](nesquena/hermes-webui#3324), [@&#8203;aether-agent](https://github.com/aether-agent), closes [#&#8203;3276](nesquena/hermes-webui#3276)).

### [`v0.51.215`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051215--2026-06-02--Release-GI-stage-p2d--deduplicate-legacy-messages-in-append-only-merge)

[Compare Source](nesquena/hermes-webui@v0.51.214...v0.51.215)

##### Fixed

- `merge_session_messages_append_only` now deduplicates true duplicate legacy messages (same role, content, AND exact timestamp) that could accumulate in state, while preserving legitimately-repeated identical turns whose timestamps differ even slightly. This avoids both the stale-duplicate buildup and the data-loss class where collapsing same-second distinct turns would drop real messages ([#&#8203;3393](nesquena/hermes-webui#3393), [@&#8203;thanhtoantnt](https://github.com/thanhtoantnt), closes [#&#8203;3346](nesquena/hermes-webui#3346)).

### [`v0.51.214`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051214--2026-06-02--Release-GH-stage-p2c--preserve-loaded-transcript-width-on-same-session-external-refresh)

[Compare Source](nesquena/hermes-webui@v0.51.213...v0.51.214)

##### Fixed

- A same-session external refresh (e.g. a background poll triggering a force-reload of the conversation you're reading) no longer collapses a long transcript back to the default 30-message tail window and jumps the viewport to a different slice. The already-loaded transcript width and scroll position are now captured before the in-memory transcript is cleared and preserved across the authoritative reload ([#&#8203;3326](nesquena/hermes-webui#3326), [@&#8203;viraatdas](https://github.com/viraatdas), closes [#&#8203;3239](nesquena/hermes-webui#3239)).

### [`v0.51.213`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051213--2026-06-02--Release-GG-stage-p2b--keep-gateway-context-visible-in-chat-transcripts)

[Compare Source](nesquena/hermes-webui@v0.51.212...v0.51.213)

##### Fixed

- Gateway-backed chat now backfills model-context turns into the visible transcript before saving the latest reply, while keeping hidden `[context compaction]` markers out of the visible transcript. Previously a context-compacted gateway session could collapse the sidebar/header message count to a two-message conversation (and drop older visible turns) while the assistant was responding to hidden prior context. Older visible turns are preserved and compaction markers stay hidden from `saved.messages` ([#&#8203;3300](nesquena/hermes-webui#3300), [@&#8203;AJV20](https://github.com/AJV20)).

### [`v0.51.212`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051212--2026-06-02--Release-GF-stage-batch2--i18n-regenerate-title-strings--self-restart-argv--todos-cold-load)

[Compare Source](nesquena/hermes-webui@v0.51.211...v0.51.212)

##### Fixed

- Localized the five `session_title_regenerate*` session-menu strings (the "Regenerate title" action, its description, and the regenerating/regenerated/failed states) that shipped as English text in every non-English locale. Translated across it, ja, ru, es, de, zh, zh-Hant, pt, ko, fr, and tr, matching each locale's existing terminology; `zh`/`zh-Hant` keep the `\u`-escaped style of those blocks ([#&#8203;3396](nesquena/hermes-webui#3396), [@&#8203;vanshaj-pahwa](https://github.com/vanshaj-pahwa), closes [#&#8203;3364](nesquena/hermes-webui#3364)).
- Self-update re-exec now distinguishes source checkouts from frozen/packaged builds: a frozen binary (`sys.frozen`) re-execs with `sys.argv` as-is, while source checkouts keep the `[sys.executable] + sys.argv` CPython idiom. Previously the frozen path re-inserted the binary as `argv[1]`, turning re-exec into a no-op that left the WebUI stuck "offline" after every self-update ([#&#8203;3395](nesquena/hermes-webui#3395), [@&#8203;PatrickNoFilter](https://github.com/PatrickNoFilter)).
- The Todos panel now hydrates correctly on a cold session load (page refresh) even when the latest todo tool result is outside the truncated display window: `/api/session` derives a compact `todo_state` sidecar from the full settled transcript, and an explicit empty todo list is honored as the current state instead of falling through to an older non-empty write. A malformed historical tool message can never break session loading ([#&#8203;3373](nesquena/hermes-webui#3373), [@&#8203;v2psv](https://github.com/v2psv)).

### [`v0.51.211`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051211--2026-06-02--Release-GE-stage-batch1--reasoning-heuristics--model-shortest-match--Copilot-env-token-filter)

[Compare Source](nesquena/hermes-webui@v0.51.210...v0.51.211)

##### Fixed

- Generalized reasoning-effort capability checks in `_candidate_supports_reasoning` to target whole model families (GPT-5+, Claude 4/3.7, Qwen-3, Kimi, Minimax, Mimo, GLM, Step, and DeepSeek) instead of anchoring on hardcoded version numbers or vendor formats. This prevents the thinking-level configuration selector from being hidden on custom providers, new model releases, or when names carry suffixes like `-free` or `:free` (common on integrations such as Kilo Code or OpenCode Zen). The GPT heuristic is now version-anchored (5+) to avoid falsely enabling reasoning\_effort for gpt-4o/4.1/3.5 on aggregator providers ([#&#8203;3379](nesquena/hermes-webui#3379), [@&#8203;b3nw](https://github.com/b3nw), closes [#&#8203;3377](nesquena/hermes-webui#3377)).
- The `/model` slash command no longer selects a longer model variant when a shorter name is a prefix of it (e.g. `/model mimo-v2.5` selecting `mimo-v2.5-pro`). The fuzzy fallback now prefers an exact id/label match and otherwise the shortest matching option, applied to both the main and bare-name (`provider/...`) fallbacks ([#&#8203;3394](nesquena/hermes-webui#3394), [@&#8203;vanshaj-pahwa](https://github.com/vanshaj-pahwa), closes [#&#8203;3368](nesquena/hermes-webui#3368)).
- `GITHUB_TOKEN` and `GH_TOKEN` environment variables are now filtered from the Copilot credential pool alongside the seeded `gh`-CLI token, so a classic PAT (`ghp_*`) auto-detected from the environment no longer makes Copilot appear in the model picker when the Copilot API can't use it. User-specific `COPILOT_GITHUB_TOKEN` is still respected ([#&#8203;3382](nesquena/hermes-webui#3382), [@&#8203;happy5318](https://github.com/happy5318)).

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/798
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
…age ring nesquena#3062 + activity-feed default-expand setting nesquena#3080) (nesquena#3459)

* feat(composer): replace mobile config-button sliders icon with context-usage ring (nesquena#3062, @NottheGuy007)

Squashed net diff of nesquena#3062 (the PR branch's tip commits were deletes of files not
present in our tree). Replaces the composerMobileCtxBadge text badge with an SVG
progress ring (ctx-arc + ctx-num) showing real-time context-window usage: ring
fill via stroke-dashoffset, centered percentage, color-coded green<=50%
orange<=85% red>85%. Ring resets to 0%/green on new session.

* feat(activity): add 'expand activity feed by default' appearance setting (nesquena#3080, @AJV20)

Squashed net diff of nesquena#3080. Adds a Settings -> Appearance checkbox
(activity_feed_expanded_default, default off) to expand new Activity disclosures
by default; preserves manual per-turn collapse/expand (explicit toggle still
wins); live 'Waiting on model' rows explain what the agent is doing before/after
tool calls. i18n keys for all locales.

* docs(changelog): v0.51.226 — context-usage ring (nesquena#3062) + activity-feed default-expand setting (nesquena#3080)

* test(mobile): update touch-target test for nesquena#3062 ring (badge removed)

nesquena#3062 replaced the composerMobileCtxBadge text badge with the SVG context-usage
ring (composerMobileCtxRing) but left 3 stale references in tests/test_mobile_layout.py.
The full suite caught test_mobile_composer_primary_controls_keep_touch_friendly_sizing
asserting the removed .composer-mobile-ctx-badge CSS rule + #composerMobileCtxBadge
element. Updated the assertion to the new ring: confirm composerMobileCtxRing exists,
the old badge is fully gone (not dangling), and the ring SVG is aria-hidden so it
stays a decorative overlay that doesn't steal the config button's 44px touch target
(which is still asserted via .composer-mobile-config-btn above). 56/56 mobile-layout
tests pass. Codex: no production JS dereferences the removed badge.

---------

Co-authored-by: nesquena-hermes <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hold ux User experience / visual polish

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants