Commit graph

266 commits

Author SHA1 Message Date
nesquena-hermes
6903891139
Release v0.51.257 — Release HY (stage-r5) (#3598)
## Release v0.51.257 — Release HY (stage-r5)

Two rebased ★★★ fixes.

### Fixed
| Issue | Author | Fix |
|-------|--------|-----|
| #3546 | @rodboev | **"Refresh Models" on a provider card no longer returns "Error: Not found".** Sent `POST /api/models/refresh` but no route matched (404). Added the route, wired to the existing `invalidate_provider_models_cache(provider_id)`. |
| #3548 | @franksong2702 | **Credential self-heal no longer writes to a dead `SessionDB` handle.** A credential-refresh evicted/closed the cached agent, but the retry rebuilt a new agent from kwargs still holding the old closed `SessionDB` → persistence silently targeted a dead handle. Per-request `SessionDB` construction centralized + refreshed on the retry. |

### Dropped from this batch
- **#2660** (session-event SSE profile scoping) — the Codex regression gate found **two SILENT dropped-refresh bugs** the scoping introduced: (1) the `maxsize=1` subscriber-queue coalescing overwrites a pending profile-A event with a profile-B event → A-tabs filter B out and never refresh for the A change; (2) renamed-root/`default` alias mismatch (backend `_profiles_match` treats them equal, the client filter uses a literal `!==`). A dropped refresh (stale sidebar) is worse than the extra refreshes the PR removes. Held with `changes-requested` + repros + the fail-safe fix (coalesce to unscoped on a profile mismatch; normalize root aliases).

### Gate
- Full pytest suite: **7622 passed, 0 failed**
- ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN
- Codex (regression): SHIP-ONLY-WITH-FIXES (#2660 dropped-refresh bugs) → #2660 dropped + held → **SAFE TO SHIP**

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-04 13:04:01 -07:00
nesquena-hermes
0501564098
Release v0.51.249 — Release HQ (stage-q21) (#3523)
## Release v0.51.249 — Release HQ (stage-q21)

Small opt-in feature. UX-approved (screenshot of the toggle in Settings → Preferences).

### Added
| Issue | Author | Feature |
|-------|--------|---------|
| #2974 | @rodboev | **"Auto-expand terminal on output"** preference (Settings → Preferences, **off by default**). When enabled, the collapsed embedded terminal panel expands automatically the first time a running command emits output. Fires once per stream (guarded on open && collapsed — not per chunk), and uses `expandComposerTerminal({focus:false})` so it doesn't steal focus from the composer. Backend-persisted boolean mirroring the `simplified_tool_calling` pattern; default-off = no behavior change on upgrade. |

### Gate
- Full pytest suite: **7557 passed, 0 failed**
- ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN · screenshot vision-verified (toggle renders cleanly under "Compact tool activity", unchecked by default)
- Codex (regression): **SAFE TO SHIP** (clean first pass) — no-arg `expandComposerTerminal` callers unchanged, default-off incl. settings-load-failure path, all 5 plumb sites mirror the existing pattern

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-03 20:15:53 -07:00
nesquena-hermes
c243126ccb
Release v0.51.245 — Release HM (stage-q17) (#3511)
## Release v0.51.245 — Release HM (stage-q17)

Small UX bug-fix.

### Fixed
| Issue | Author | Fix |
|-------|--------|-----|
| #3338 | @rodboev (#3502) | **Messaging sessions (Telegram, Discord, WeChat, …) now show their platform source badge in the chat-pane topbar**, not just the sidebar. The topbar badge was gated on `is_cli_session` (intentionally `false` for messaging sources), so it vanished once the session opened. Gate removed; a recovered native session stamped `source_label:"WebUI"` stays un-badged. Reuses the existing `.topbar-source-badge` styling — no new chrome. |

Picked #3502 over the duplicate **#3499** (same issue/files) — #3502 adds the `WebUI` self-source suppression and a stronger regression test. #3499 closed as superseded with credit.

### Gate
- Full pytest suite: **7543 passed, 0 failed** (first run hit the known boot-cascade flake — 376 connection-refused across 29 files; clean on re-run, as expected for a JS-only change)
- ESLint: CLEAN · browser-smoke: CLEAN · 8 source-contract tests pin the fix
- Codex (regression): **SAFE TO SHIP** — both renderers (panels.js + ui.js) fixed consistently, WebUI-suppression correct, read-only suffix intact, `textContent` XSS-safe

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-03 18:46:42 -07:00
nesquena-hermes
c8f8ca3428
Release v0.51.244 — Release HL (stage-q16) (#3509)
## Release v0.51.244 — Release HL (stage-q16)

UX-approved direction via Telegram (workspace drag-drop polish you requested). All 4 drag-drop flows verified live in-browser.

### Added
| PR | Author | Feature |
|----|--------|---------|
| #3402 / #3424 | @pamnard | **Drop OS files/folders onto a specific workspace folder row or breadcrumb** to upload into that directory (not just the current dir). OS folder drops are traversed (`webkitGetAsEntry`/`readEntries`) preserving nested structure. Uploads via the existing `/api/workspace/upload` (no new backend). |

### Fixed
- **Composer drop-zone jank**: dragging a workspace file (or OS file) over the composer footer rendered a translucent overlay that let the textarea/chips/icons bleed through and collide with the hint text. Now a clean, fully-opaque box with a single centered **context-aware** label — *"Drop to insert workspace reference"* (workspace file → `@path` insert) vs *"Drop files to attach"* (OS file → message attach).
- **Drag-drop handler coexistence (CORE, caught in review)**: #3424's OS-upload binding assigned `el.ondrop` on folder rows, which **overwrote** the drag-to-move handler from #3422 (also `el.ondrop`) — silently breaking move-to-folder (the ws-path drop fell through to the composer as an `@path` insert). Fixed by binding the OS-upload handlers via `addEventListener` so they compose; each handler gates on its own drag type.

### Drag-drop matrix — all verified LIVE in-browser (real drag→drop, asserted on disk)
| Flow | Result |
|------|--------|
| OS image → composer footer | ✓ attaches |
| workspace file → composer footer | ✓ inserts `@path` |
| workspace file → workspace folder | ✓ moves on disk (report.md → docs/) |
| OS file → workspace folder | ✓ uploads into target folder |

### Scope note
#3424's PR branch carried the OLD pre-hardening `_handle_file_move`. Applied **frontend-only** — master's hardened move backend (v0.51.243, TOCTOU/symlink fixes) is untouched (Codex confirmed no `api/routes.py` diff).

### Gate
- Full pytest suite: **7542 passed, 9 skipped, 3 xpassed, 0 failed**
- ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN
- Codex (regression): CORE handler-clobber → fixed → **SAFE TO SHIP**

Co-authored-by: pamnard <pamnard@users.noreply.github.com>
2026-06-03 18:19:09 -07:00
nesquena-hermes
0b0a179e4c
Release v0.51.231 — Release GY (stage-q1) (#3484)
## Release v0.51.231 — Release GY (stage-q1)

Four independent, low-risk fixes batched into one release. All gates green.

### Fixes
| PR | Author | Fix |
|----|--------|-----|
| #3368 | @nesquena-hermes | `/model` resolves models from the truncated `extra_models` tail of a large provider catalog (completes the half-done #3368 fix; @garyd9's `mimo-v2.5` case) |
| #3457 | @pix0127 | Auto-hide the Plugins settings tab when no plugins are installed; deep-link fallback to Conversation |
| #3474 | @Mubashirrrr | Guard malformed/negative `depth` on `/api/sessions/search` (no more 500 on `?depth=deep`, no silent newest-message drop on negative) |
| #3433 | @sjh9714 | Resolve symlinked home before the trust comparison so `~/` workspace suggestions work when the process home is a symlink |

### Note on #3457
The original contributor patch reassigned a `const section` (a **runtime `TypeError` brick** on the settings panel) and placed the hidden-tab fallback *after* the panes were already toggled. Refactored to `let` + hoist the fallback above all consumers of `section`. ESLint runtime gate now clean.

### Gate results
- **Full pytest suite**: 7441 passed, 7 skipped, 3 xpassed, **0 failed**
- **ESLint runtime gate**: CLEAN (caught + fixed the #3457 const-reassign)
- **ruff forward gate**: CLEAN (no new violations on changed lines)
- **browser-smoke gate**: CLEAN (`/`, `/#settings`, `/#sessions` — zero console errors)
- **Codex (regression)**: SAFE TO SHIP
- **Opus (correctness)**: SAFE TO SHIP

Closes #3368. Closes #3457. Closes #3433.

Co-authored-by: pix0127 <pix0127@users.noreply.github.com>
Co-authored-by: Mubashirrrr <Mubashirrrr@users.noreply.github.com>
Co-authored-by: sjh9714 <sjh9714@users.noreply.github.com>
2026-06-03 09:13:01 -07:00
nesquena-hermes
0b5458f3da
Release v0.51.226 — Release GT (stage-p9 — mobile composer context-usage 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]>
2026-06-02 19:48:01 -07:00
Qi Zhou
5c595c08d6 fix(todos): hydrate cold-load state from session snapshot 2026-06-02 17:33:49 +00:00
nesquena-hermes
be4496d23f fix(#2622): plugin card UX — legible Open button, single badge, visible toggle + reject protocol-relative tab.path
Nathan screenshot feedback on the Plugins card:
- Open button rendered as a yellow block with INVISIBLE text: --accent-text
  resolves to the same gold as --accent in the default theme (text==bg). Switched
  to a ghost/outline button (accent text + border on the card surface; fills on
  hover) — always legible regardless of theme.
- Removed the redundant DOUBLE 'Enabled' badge (the dashboard-specific badge
  duplicated the generic activation badge; kept the generic one).
- Toggle slider knob was hard to see on the gold 'on' state; added a drop shadow.
Also Opus SHOULD-FIX: _VALID_PLUGIN_TAB_PATH now rejects a leading '//'
(protocol-relative URL → remote origin in iframe.src). Test updated.
2026-06-02 03:58:02 +00:00
nesquena-hermes
816a4a93f9 harden(#2622): DOM-bound plugin handlers + tab.path validation (Codex round-2)
Codex found two more once the config-guard bug was fixed (Opus concurred on #2):
1. panels.js _buildPluginCard built the Open button + enable toggle with inline
   onclick/onchange that interpolated tab.path / plugin.key into a JS-string-in-
   attribute context — HTML-escaping is insufficient there (quote breakout).
   Now rendered inert + bound via addEventListener with RAW closure values.
2. tab.path was unvalidated. Added _VALID_PLUGIN_TAB_PATH (^/[A-Za-z0-9._~/-]{0,255}$)
   in load_plugins() — absolute, no quotes/query/fragment/control chars.
Also Opus nit: deep-merge now coerces dashboard_plugins values to bool + str keys.
Regression tests added for both.
2026-06-02 03:39:41 +00:00
nesquena-hermes
bf3ff69c5e feat(plugins): add WebUI dashboard plugin system with iframe isolation (#2622, @pix0127) 2026-06-02 02:48:11 +00:00
nesquena-hermes
1c29d6ac9c feat: add Edge TTS as alternative speech engine (#2931, @liuqiangweb-svg) 2026-06-02 01:21:39 +00:00
nesquena-hermes
c5d4806a64 fix(#3331): gate findings — empty-session profile retag, root-alias project filter, create-profile validation
Codex+Opus gate findings on the profile-scoping PR:
1. panels.js: retag S.session.profile on ANY profile switch (was inside the
   if(data.default_model) block, so model-less profile switches left a stale chip).
2. sessions.js: project-picker filter now mirrors the server's root-alias
   tolerance (default <-> renamed-root) so a server-approved 'default' project
   isn't hidden for a renamed-root session.
3. routes.py /api/projects/create: validate the optional client-supplied profile
   via _PROFILE_ID_RE before stamping (was trusting raw client input -> could
   create hidden cross-profile rows). Updated the PR's string-assertion test.
2026-06-01 21:05:32 +00:00
nesquena-hermes
3cf4d15dc6 fix: align project/session operations with session profile instead of global active profile (#3331)
Co-authored-by: PINKIIILQWQ <PINKIIILQWQ@users.noreply.github.com>
2026-06-01 20:57:40 +00:00
nesquena-hermes
de22c607bc stage-batch10: #3327 model-id normalize + #3334 RFC slice doc + #3341 profile skill counts
#3327 fix(reasoning): normalize custom-provider model ids for fallback heuristics
Co-authored-by: Carry00 <Carry00@users.noreply.github.com>

#3334 docs(rfc): mark run-adapter Slice 4f shipped, define Slice 4g gate
Co-authored-by: Michaelyklam <Michaelyklam@users.noreply.github.com>

#3341 fix(profiles): show enabled vs compatible skill counts
Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-06-01 18:24:05 +00:00
Pamnard
f24d633189 Fix skills detail markdown styling with preview-md wrapper
Skill detail and linked markdown files now use the same preview-md
pipeline as Memory/Notes, with code highlighting and KaTeX enhancement.
2026-05-31 23:02:23 +00:00
Dennis Soong
5f901f579a fix: suppress timeout toasts for passive pollers 2026-05-28 11:13:00 +08:00
nesquena-hermes
7006bb5c7a Merge pull request #3008 2026-05-28 01:43:22 +00:00
BonyFish
3a676b704a feat: add skill usage counter to insights page
- Add api/skill_usage.py for .usage.json read/write with atomic updates
- Track skill_view (view_count), skill_manage (use_count), skill_patch
  (patch_count) invocations per SSE stream completion
- Add GET /api/skills/usage endpoint returning per-skill usage stats
- Render Skill Usage card in Insights panel after LLM Wiki card
- Add i18n keys for en, zh, zh-Hant, ja, it locales
- Add 17 tests covering read/increment logic, API route, and streaming hook
2026-05-27 14:44:28 +08:00
BonyFish
ea3d4ec0b3 feat(cron): dynamic delivery options from API instead of hardcoded select
Replace the hardcoded 4-option deliver dropdown (local/discord/telegram/slack)
with a dynamic select populated from a new GET /api/crons/delivery-options
endpoint that reads _KNOWN_DELIVERY_PLATFORMS from hermes-agent.

Key changes:
- Add GET /api/crons/delivery-options endpoint returning all known platforms
- Frontend loads options asynchronously on first cron form open, with caching
- Enable deliver editing for existing jobs (was previously disabled)
- Include deliver in update payload when editing cron jobs
- Fallback to local-only if API unavailable
- Custom deliver values (e.g. feishu:oc_xxx) shown with * suffix
- Add cron_deliver_custom i18n key to all 12 locales
- Add 5 integration tests for the new endpoint
2026-05-27 10:58:49 +08:00
nesquena-hermes
c7e7029ab4 Merge pull request #2950
# Conflicts:
#	CHANGELOG.md
2026-05-26 23:51:39 +00:00
Sanjay Santhanam
cf94f2f7c4 docs(docker): promote scheduled-jobs section to H2 + runnable verify command (review feedback from @nesquena-hermes) 2026-05-25 23:11:59 -07:00
Sanjay Santhanam
0211e1e012 fix(providers): pass through server CSRF reason instead of one-size message (review feedback from @nesquena-hermes) 2026-05-25 23:11:11 -07:00
Sanjay Santhanam
655b5f9101 docs(docker): point gateway-not-configured banner at a real fix
Scheduled cron jobs created in the Tasks panel never tick on a
single-container Docker install because the WebUI doesn't run the
gateway daemon itself. The maintainer's analysis on #2785 spells this
out: the gateway ticks the scheduler every 60s, and without it
'Gateway not configured' just sits there.

The Tasks panel already shows a banner explaining this, but doesn't
give the user anywhere to go. Two small docs-shaped changes:

1. Add a 'Scheduled jobs require a gateway daemon' section to
   docs/docker.md under 'What goes wrong' with the two-container
   compose command and a verify step. Cross-linked from the existing
   short paragraph higher up so both entry points land on the same
   fix.

2. Append a 'How to enable scheduled jobs in Docker' link to the
   cron panel banner (loadCronGatewayNotice) pointing at the new
   docs anchor when the gateway is unconfigured. The banner text
   itself is unchanged.

Verified locally by serving the WebUI without a gateway, opening
Tasks, and confirming the banner now shows the new link; clicked it
and confirmed it lands on the new docs section. With the gateway
running the banner stays hidden as before.

Refs #2785
2026-05-25 11:35:24 -07:00
Sanjay Santhanam
51f333fef0 fix(providers): clearer toast on 403 when removing a provider key
The Remove button under Settings -> Providers calls
POST /api/providers/delete, which runs through _check_csrf. When the
CSRF cookie/header pair has drifted (typically a tab opened before the
most recent login or cookie rotation), the server returns 403 with the
string 'Cross-origin request rejected'. That string reads like a
reverse-proxy deployment problem and gives the user no next step (#2572).

Surface a recovery-shaped toast on 403 from this endpoint:
'Session expired. Reload the page and try again.' The underlying
server response is unchanged so logs/diagnostics still see the original
string; only the user-facing toast is replaced for this code path.

Verified locally by patching _check_csrf to return False, clicking
Remove on a provider card, and confirming the toast now reads the new
message instead of the raw cross-origin string.

Refs #2572
2026-05-25 11:28:14 -07:00
nesquena-hermes
1c2d574882 Stage-batch14: Opus advisor SHOULD-FIX patches (UX + defense-in-depth)
Inline fixes for 4 of 5 Opus SHOULD-FIX items before tag:

1. /api/auth/status now gates passkeys_enabled / passwordless_enabled on
   _passkey_feature_flag_enabled() — when flag is off, status reports
   no credentials even if passkeys.json has legacy entries. New
   passkey_feature_flag field added to the response for the frontend.

2. Settings → System Passkeys block (passkeysSettingsBlock) now starts
   display:none and loadPasskeys() reveals it only when the server
   confirms passkey_feature_flag === true AND /api/auth/passkeys
   doesn't return {disabled: true}. Stops the broken-affordance trap
   where users would see Add passkey → click → 404.

3. /api/settings/save now refuses to set passwordless mode when the
   passkey feature flag is off. Closes the auth-bypass path Opus flagged:
   user goes passwordless while flag on → admin unsets flag → restart
   serves the WebUI fully unauthenticated.

4. CHANGELOG entries added for PR #2685 (replayed-context dedup +
   per-turn metering cap) and PR #2824 (Stop server affordance,
   relocated to Settings) — both PRs had functional changes but no
   release-notes entries. Also enriched the rate-limit detail on the
   #2739 entry (30 events / 60s / 4KB body cap).

Deferred to follow-up issue (#5 in Opus review):
- Live tool metering cumulative cap across many tool calls — non-trivial
  refactor of _bump_live_prompt_estimate, will be a separate PR
2026-05-25 00:26:40 +00:00
AJV20
1b48643f63 feat: support passkey-only auth 2026-05-25 00:14:38 +00:00
AJV20
7c257ae8f9 fix: avoid prompt in passkey registration 2026-05-25 00:14:38 +00:00
AJV20
c60ff543b5 feat: add passkey sign-in 2026-05-25 00:14:38 +00:00
AJV20
24979c8af1 Merge remote-tracking branch 'origin/master' into maint/pr-2527
# Conflicts:
#	CHANGELOG.md
2026-05-24 17:48:09 -04:00
hermes-agent
a86b378036 Stage 405: PR #2680 — feat: add Auxiliary Models settings card by @mccxj
Cherry-picked via 3-way apply (rebase had failed on static/index.html
conflict when applied via rebase commit chain; 3-way of the net delta
against stage HEAD applied cleanly).

Co-authored-by: mccxj <mccxj@github.users.noreply.github.com>
2026-05-24 18:28:26 +00:00
hermes-agent
9d95ba0b92 Stage 404: PR #2716 — Performance optimizations by @dobby-d-elf
nesquena APPROVED 2026-05-22. Cherry-picked onto post-v0.51.127
master via 3-way apply. Resolved api/routes.py conflict: master had
the inline correctness fix from the deep-review iteration; PR
refactors it into _metadata_only_message_summary() helper. Took the
helper AND added profile= threading (post-#2827 master adds
profile-aware state.db reads). Kept master's pre-existing
test_api_session_reload_drops_stale_cached_user_tail_after_saved_assistant
alongside the PR's new test_metadata_fast_path_matches_reconciliation_for_restamped_replays.

Co-authored-by: dobby-d-elf <dobby.the.agent@gmail.com>
2026-05-24 18:08:41 +00:00
Rory Ford
7be9a26018 feat: PATCH /api/mcp/servers/{name} — enable/disable toggle
Add `PATCH /api/mcp/servers/{name}` endpoint that accepts `{"enabled": bool}`,
updates `mcp_servers.<name>.enabled` in config.yaml, and calls `reload_config()`.
Mirrors the existing DELETE pattern.

Also wire the previously-defined-but-unrouted `_handle_mcp_server_delete` into
`handle_delete`, and `_handle_mcp_server_update` into a new `handle_put` +
`do_PUT` in server.py — fixing a pre-existing bug where those handlers existed
but were never reachable over HTTP.

UI: add a toggle button in each MCP server row in the system settings panel
(panels.js). Clicking it calls PATCH and reloads the list. Toggle button is
styled with `.mcp-toggle-enabled` / `.mcp-toggle-disabled` CSS classes. The
`toggle_supported` flag in the list response is now `True`.

i18n: add 5 new keys (`mcp_enable_server`, `mcp_disable_server`,
`mcp_enabled_toast`, `mcp_disabled_toast`, `mcp_toggle_failed`) to all 9
non-English locales (English values as placeholder translations).

Tests: add `TestMcpToggle` class with 7 tests covering disable, enable,
404-not-found, empty name, missing field, response payload, and URL-encoded name.
Update `test_empty_config` and visibility panel assertions to reflect
`toggle_supported: True` and the new toggle button in panels.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 17:13:32 +00:00
humayunak
7983e025c4 kanban: full markdown rendering for task description and comments
- Rewrote _kanbanRenderMarkdown() from basic paragraph wrapper to a
  line-by-line block processor supporting headings, code blocks, lists,
  task lists, tables, blockquotes, horizontal rules, and strikethrough.
- Added CSS for all new elements (table borders, code blocks, checkboxes,
  blockquote accent, heading sizing, etc.).
- Dropped white-space: pre-wrap from .kanban-task-preview-body and
  .kanban-detail-row-main since markdown now handles layout.
- Applied _kanbanRenderMarkdown() to task description (was esc()) and
  comment body (was esc()) in the task detail view.
2026-05-24 16:12:34 +00:00
Frank Song
f1586daa3b fix(cron): surface gateway scheduling guidance 2026-05-24 15:52:35 +00:00
AJV20
b6f7412b53 Add option to ignore agent updates 2026-05-24 15:52:34 +00:00
tn801534
2e876ea229 fix: kanban worker log URL double query param on non-default boards 2026-05-24 15:52:00 +00:00
gavinssr
75fdadd477 feat: add Hepburn skin (magenta-rose palette)
Add Hepburn skin with full light/dark palette derived from the
Hepburn TUI theme. Brand color #c6246a with pink-magenta accents.

- Light: soft pink surfaces (#fff3f7 / #fbe4ed)
- Dark: deep aubergine (#110a0f / #1e0f19)
- Accent: #d44a7a (light) / #f278ad (dark)
- Styled: send button, new chat button, tool cards, session indicator

Also fix settings panel skin picker to prioritize localStorage
over server defaults, so newly selected skins reflect correctly
in the dropdown.
2026-05-24 03:03:32 +00:00
AJV20
86b5a8fcff Merge remote-tracking branch 'origin/master' into feat/webui-notes-sources
# Conflicts:
#	CHANGELOG.md
2026-05-22 17:35:18 -04:00
lucasrc
35adc3a473 feat: add per-skill enable/disable toggle in Skills panel
Original PR: #2676 by @lucasrc

Adds POST /api/skills/toggle endpoint that flips skills.disabled in
config.yaml, and a UI toggle in the Skills panel that shows all skills
(including disabled ones) with a per-skill on/off control.

- Backend: new endpoint validates skill exists in filesystem before
  toggling. Read-modify-write wrapped in _cfg_lock for thread safety.
  Writes through to platform_disabled.webui when present.
- Frontend: each skill-item now has a toggle switch; disabled skills
  appear muted but still listed (previously they were filtered out).
- i18n: new toggle keys translated across all 9 non-English locales.
- Tests: round-trip test for disabled list normalization + toggle
  endpoint behavior.

Squash-merged from contributor's branch (19 commits + 1 merge commit)
onto current master via the cherry-pick-stale-contributor-prs procedure.
2026-05-22 19:43:00 +00:00
AJV20
7305d470b9 feat(memory): gate third-party notes drawer 2026-05-22 14:54:41 -04:00
AJV20
e794b54e18 Merge remote-tracking branch 'origin/master' into feat/webui-notes-sources
# Conflicts:
#	CHANGELOG.md
2026-05-22 14:14:15 -04:00
ai-ag2026
77b0ca6e02 fix(chat): preserve session actions after panel resync 2026-05-22 09:25:15 +02:00
ai-ag2026
f81d24ec4c fix(chat): resync sidebar after panel switch 2026-05-22 09:25:15 +02:00
Hermes Agent
b14aae4ee5 Stage 400: PR #2717 — fix: surface update check fetch errors instead of failing silently
Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
2026-05-21 22:59:54 +00:00
Hermes Agent
905b3eba5e Stage 398: PR #2700 — feat: make pinned session limit configurable (builds on shipped #2614 3-cap)
Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
2026-05-21 17:43:56 +00:00
Hermes Agent
7d3013245a Stage 398: PR #2687 — feat: hide suggestions preference (closes #2679)
Closes #2679

Co-authored-by: Michaelyklam <Michaelyklam@users.noreply.github.com>
2026-05-21 17:43:48 +00:00
Hermes Agent
12a92dd50a Stage 397: PR #2671 — fix: SSE reconnect jitter + cron run row inline expansion
Closes #2629
Closes #2661

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

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

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

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

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

Co-authored-by: FrancescoFarinola <francesco.farinola@example.com>
2026-05-20 23:05:19 +00:00
Francesco Farinola
5491a54285 fix: address PR review feedback on sidebar tab visibility
Three tweaks from reviewer:

1. Harden _applyTabVisibility to skip always-visible panels even if
   they appear in hidden_tabs (localStorage tampering, stale server
   data). Forces shouldHide=false so stale nav-tab-hidden classes
   on chat/settings get removed, not just skipped.

2. Add synchronous inline <script> flash-prevention after sidebar-nav
   in index.html. On slow networks, defer scripts run after the
   browser incrementally renders the DOM, causing hidden tabs to
   flash visible before JS executes. The inline script reads
   hermes-webui-hidden-tabs from localStorage and applies
   nav-tab-hidden classes before first paint, mirroring the existing
   theme/skin/font-size pattern. The boot.js IIFE becomes a secondary
   fallback (comment updated).

3. Remove _settingsHiddenTabsOnOpen dead state. It was tracked but
   never read for revert — _revertSettingsPreview is intentionally
   a no-op for appearance autosave. Removing the tracking makes
   the code honest about what it actually does. Also removes the
   test_settings_session_tracking test that validated this dead code.
2026-05-20 22:57:36 +00:00