Commit graph

417 commits

Author SHA1 Message Date
nesquena-hermes
ba401d3784 Merge #5079 (OpenAI TTS + SSRF hardening) into stage 2026-06-30 20:39:04 +00:00
nesquena-hermes
027699b570 Merge #4881 (rodboev Kanban modal fields) into stage 2026-06-30 19:50:46 +00:00
nesquena-hermes
492fdfb84a Merge #4574 (rodboev mobile titlebar) into stage 2026-06-30 19:10:38 +00:00
nesquena-hermes
c7767f9295 Merge #5211 (search-ranking) into stage-pq1 2026-06-30 18:06:11 +00:00
nesquena-hermes
121059b384 Merge #5161 (pr/5144-busy-composer-placeholder-hint) into stage-w5 2026-06-30 17:25:03 +00:00
Maude Bot
5263b9acf7 feat: add Webhooks sidebar filter parity
Co-authored-by: Kilian Tyler <kilian@kil.dev>
2026-06-30 17:06:31 +00:00
Stacey2911
6b0d8eaadf fix(webui): make large text paste-as-attachment toggleable via settings
Add a user setting 'large_text_paste_as_attachment' (default: on) that lets
users paste large text inline instead of having it auto-converted to .md file
attachments. The toggle lives in Control Center → Chat tab and persists via
settings.json, following the established upstream pattern for boolean settings.
2026-06-30 14:25:53 +10:00
Rod Boev
efa3ebaac4 fix(#4470): reject ambiguous kanban runtime input 2026-06-29 15:27:17 -04:00
Rod Boev
aaabd32c92 feat(#4470): expose skills, max_runtime_seconds, and parents in Kanban create modal 2026-06-29 15:27:16 -04:00
Rod Boev
d35e3a9d10 refactor(settings): trim unreachable descriptor fallback (#5149) 2026-06-29 11:38:50 -04:00
Rod Boev
533eaf7c26 test(settings): close ranking proof gaps (#5149) 2026-06-29 11:04:59 -04:00
Rod Boev
782335b62a feat(settings): rank settings search results by match source (#5149) 2026-06-29 10:39:08 -04:00
Stacey2911
dd658d7a04 fix(workspace): align Workspace Todos rendering with sidebar 2026-06-29 04:44:16 +00:00
nesquena-hermes
fae5c45395 Merge #5163 into stage/p1a 2026-06-29 04:07:21 +00:00
claw-io
ec0ef4a0d3 fix(profiles): show active profile workspace on blank new-chat (#5169)
Expose a profile-scoped default_workspace on GET /api/profile/active (resolved
by the profile-aware get_last_workspace(), fail-open) and hydrate boot.js from
it, overriding the global GET /api/settings value. GET /api/settings stays
global-only so the settings UI can't clobber the shared default. Combines
claw-io's #5173 (primary) with the fail-open backend hardening + regression test
from #5171.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-06-29 01:25:58 +00:00
Rod Boev
ec00e037f3 fix(settings): widen busy-input settings search coverage (#5148) 2026-06-28 18:47:42 -04:00
Rod Boev
9a1f27a48b feat(settings): add opt-in busy placeholder hint (#5144) 2026-06-28 18:47:42 -04:00
Rod Boev
46d91a7c61 fix(extensions): render installed controls from trusted state (#5094) 2026-06-28 18:15:36 -04:00
Rod Boev
938f721b16 fix(extensions): reload before editing untrusted installs (#5094) 2026-06-28 18:07:52 -04:00
Rod Boev
07ed67a793 fix(extensions): keep runtime settings gated on owned storage (#5094) 2026-06-28 17:44:02 -04:00
Rod Boev
a76c794d6d feat(extensions): add browser-local extension settings surface (#5094) 2026-06-28 17:28:20 -04:00
Rod Boev
21e229cb0f feat(#4982): support OpenAI-compatible TTS backend in WebUI (#4982) 2026-06-28 09:50:51 -04:00
nesquena-hermes
04066b4b43 feat(insights): surface prompt-cache hit rate (salvage of #3912)
Surfaces prompt-cache hit rate on the Insights page so cache-efficiency
(a key cost-optimisation signal) is visible at a glance.

Backend (api/routes.py, _handle_insights):
- Aggregate cache_read_tokens per model and per day, plus a total.
- Graceful fallback for older agent state.db schemas lacking the
  cache_read_tokens column (COALESCE + OperationalError second query).
- New response fields: total_cache_read_tokens, total_cache_hit_percent,
  per-model cache_read_tokens + cache_hit_percent, per-day cache_read_tokens.

Frontend:
- Models table gains a Cache column (panels.js + style.css 6-col grid).
- Daily bar tooltip includes the bounded cache hit rate.
- New i18n keys insights_model_cache / insights_cache_hit in all 13 locales.

Correctness fix (maintainer-flagged on #3912): the original computed
hit-rate as cache_read / input_tokens (reads-over-misses), which EXCEEDS
100% on cache-heavy sessions. This uses the FULL prompt total as the
denominator -- cache_read / (input_tokens + cache_read) -- via the shared
api.usage.prompt_cache_hit_percent helper, so it is bounded 0-100% and
means "% of the prompt served from cache". Returns None (rendered as a
muted dash) when there are no cache reads.

Tests (tests/test_insights.py):
- Updated existing assertions for the new response shape.
- Added a non-vacuous <=100% boundary test: a cache-heavy case
  (cache_read=9900, input=100) where the OLD formula reports 9900% but the
  bounded denominator yields 99%. Verified the test FAILS under the old
  formula and PASSES under the fix.

Salvage note: PR #3912's diff was ~99 releases stale (mostly re-derived old
code, illusory +5405). This ports ONLY the real ~190-line feature fresh
onto current master.

Closes #3911

Co-authored-by: TomBanksAU <TomBanksAU@users.noreply.github.com>
2026-06-28 08:11:22 +00:00
nesquena-hermes
cd71403be9 feat(extensions): TTS-engine registration capability (registerHermesTtsEngine)
Lets a trusted local extension contribute a TTS engine that appears in the
Settings -> TTS Engine dropdown and is used by BOTH playback paths (voice-mode
auto-read in boot.js _speakResponse + the per-message Listen button in ui.js
speakMessage). Mirrors window.registerHermesSkin.

  window.registerHermesTtsEngine({ id, label, synthesize(text, opts) })
    -> Promise<ArrayBuffer|Blob> of audio; core coerces + plays through the same
       <audio> lifecycle as the Edge engine (stop/rearm in voice mode).

Core owns selection, the dropdown <option> (label via textContent, never HTML),
and playback. Built-in ids (browser/edge/elevenlabs) are reserved; invalid
descriptors are rejected; empty/failed synth degrades gracefully. The settings
panel re-adds registered options on render (the markup hardcodes only built-ins).

Unblocks TTS-engine extensions (e.g. a local VOICEVOX engine). Docs in
docs/EXTENSIONS.md; CHANGELOG [Unreleased]. New regression test drives the real
registry via a Node harness (register/validation/reserved-guard/synth coercion).

Full suite: 10893 passed, 0 failed. Verified end-to-end in a live browser with a
companion VOICEVOX extension: engine registers, appears in the dropdown
(browser/edge/elevenlabs/voicevox), and the Listen button routes through the
registered synthesize (both VOICEVOX steps called).
2026-06-28 05:43:02 +00:00
Frank Song
ddb8ddd811 Merge current master into extension gallery source permissions
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-28 13:05:12 +08:00
Frank Song
06465bbd91 Improve extension gallery source and permissions display 2026-06-28 12:44:57 +08:00
nesquena-hermes
5bb03a6322 feat(settings): sync a Hermes Dashboard chip with Appearance dropdown (#4936)
Adds a "Hermes Dashboard" chip to the Settings > Appearance "Sidebar tabs"
grid, bidirectionally synced with the existing dashboard-mode dropdown
(never/always/auto). Chip off -> dropdown "never"; chip on -> restore last
non-never mode; dropdown change -> chip re-render.

The chip is a proxy for the dashboard config via saveDashboardSettings ->
POST /api/dashboard/config; it NEVER mutates hidden_tabs/tab_order (separate
toggle fn from _toggleTabVisibilityChip). loadDashboardSettings guards a stale
config GET from clobbering a newer save (_dashboardSettingsLoadSeq /
_dashboardSettingsWriteSeq), and a failed chip save rolls the dropdown/chip
back. Chip label via textContent (XSS-safe).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-27 20:33:51 +00:00
nesquena-hermes
20d0313d0c feat(settings): expose max_tokens override in Settings (#5010)
The streaming path already honored a max_tokens cap from the active profile's
config.yaml, but there was no UI to manage it. Settings > Preferences now has a
"Max output tokens" field that writes a root-level max_tokens override through
the profile config (not settings.json, which the streaming worker never reads
for this), preserving unrelated YAML keys under _cfg_lock. Blank clears the
override so agent.max_tokens fallback resumes; get_max_tokens_status() surfaces
root / effective / fallback so the field shows what a new turn would use.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-27 20:27:18 +00:00
Rod Boev
f65543f88e fix(#5045): add memory-section hover path titles 2026-06-27 06:05:28 -04:00
nesquena-hermes
11260c8df5 feat(extensions): show post-install next steps in the gallery (#4964)
Renders generic registry post_install guidance + lifecycle requirements in the
Settings extensions gallery card (manifest-driven, no vendor special-casing).
Fixes #4959. Rebased onto current master (squash-applied #4964's net diff so it
coexists with the #4979 sidecar-runtime-status feature that landed in v0.51.697).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-27 05:44:10 +00:00
nesquena-hermes
64e877b432 fix(extensions): don't preserve loading/error placeholder as existing diagnostics
Codex gate finding on #4979: loadExtensionsPanel({preserveExisting:true}) on the
diagnostics tab treated the 'Loading extension diagnostics…' placeholder as real
content, so a failed refresh returned early (preserveExisting && innerHTML.trim())
and left the panel stuck on 'Loading…' instead of rendering the error. Exclude the
.extensions-loading/.extensions-error placeholders from the preserve check + add a
regression assertion.

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-27 05:21:45 +00:00
Frank Song
33e95277ca Honor preserved extension diagnostics refresh failures
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-27 05:13:10 +00:00
Frank Song
71dd4a6ab8 Polish extension sidecar diagnostics refresh
Keep the Diagnostics tab content visible while status refreshes on repeat visits, and reject malformed or far-future sidecar runtime timestamps before rendering relative age text.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-27 05:12:50 +00:00
Frank Song
d2eedd85f8 Show extension sidecar runtime status 2026-06-27 05:12:50 +00:00
Rod Boev
19b3ad0893 test(#4999): cover every memory path branch 2026-06-26 19:42:03 -04:00
Rod Boev
e2e2182bc0 fix(#4999): show on-disk memory file paths 2026-06-26 19:14:36 -04:00
luandnh
046a0f68b8 fix: render cron run output as literal pre code, not via renderMd
Cron run output is never authored Markdown -- it is logs/JSON/plain text.
Using renderMd() with any heuristic or synthetic fence is fragile:
- Content starting with #, |, > becomes headings/tables/blockquotes
- Embedded triple-backtick in content breaks the synthetic fence

Fix: use DOM-created pre/code with textContent throughout:
- No heuristic, no synthetic fence -- every byte renders verbatim
- Both render paths (initial load + View full output) use the same approach
2026-06-26 11:56:09 +00:00
luandnh
ca1a4ee2dd fix: extract _wrapCronOutput helper, expand heuristic per review
- Extracted duplicated detection logic into shared _wrapCronOutput(text)
- Expanded heuristic to cover ~~~, -, *, _, [ markers
- Added empty-output guard (returns '' for falsy/empty text)
- Fixes Greptile review P2-P3
2026-06-26 11:56:09 +00:00
luandnh
92e8b6a0ba fix: wrap raw cron run output in code block for proper formatting
When viewing cron job run logs (prompt/response), the raw content
was passed through renderMd() which broke formatting for JSON and
plain text (single newlines collapsed, special chars interpreted).
Now detects if content looks like markdown and wraps raw content
in triple backticks so it renders as a proper code block with
preserved whitespace and formatting.
2026-06-26 11:56:09 +00:00
Rod Boev
61ea7360a0 fix(#4536): reject openai-codex provider before empty-model early return 2026-06-26 01:46:57 +00:00
Rod Boev
4d64c18832 fix(#4536): block Codex models from priority service tier 2026-06-26 01:46:57 +00:00
Rod Boev
cae551f4a6 fix(#4536): match service-tier UI gate to backend 2026-06-26 01:46:57 +00:00
Rod Boev
24cf7069a6 fix(#4536): persist OpenAI priority tier in main model settings 2026-06-26 01:46:57 +00:00
nesquena-hermes
856aa1d48d fix(#4891): show header in read-only Memory sections + Profiles help view
Gate (Codex) caught two SILENT regressions in the original fix: explicitly
hiding the header in the 'else'/'empty' branches also hid populated read views.
- _setMemoryHeaderButtons('read') now shows the header for ALL read views
  (incl. read-only Project Context / External Notes), gating only the Edit btn.
- _renderProfileConceptHelp() now uses a new 'help' header mode (shows header,
  hides all action buttons) instead of 'empty' (which hid the help title).
Adds tests/test_issue4891_detail_header_visibility.py (proven non-vacuous).

Co-authored-by: luandnh <luandnh@users.noreply.github.com>
2026-06-25 06:20:20 +00:00
luandnh
561369a261 fix: task detail actions hidden on mobile PWA
The main-view header (and its action buttons) used a CSS :has(.main-view-title:empty)
rule to stay hidden until the title populated; on some mobile browsers / PWA
WebViews that pseudo-class doesn't re-evaluate after textContent is set
dynamically, leaving the header permanently hidden. Explicitly set header
display in the read/create/edit/closed header-button setters (tasks, skills,
memory, workspaces) instead of relying on :has() reactivity.

Co-authored-by: luandnh <luandnh@users.noreply.github.com>
2026-06-25 06:08:00 +00:00
Rod Boev
ee7ff6ed33 fix(extensions): harden gallery against live registry and security review findings 2026-06-25 04:21:50 +00:00
Rod Boev
ddd5c8d936 feat(extensions): add gallery browse, install, and uninstall (#4746) 2026-06-25 04:21:50 +00:00
Brett Yelverton
dfcd94a808 fix(kanban): show unassigned tasks as explicit profile lane on mobile
- Add KANBAN_UNASSIGNED_LANE constant and _kanbanLaneKey/_kanbanLaneLabel
  helpers so unassigned tasks map to a stable internal key '__unassigned__'
  rather than a locale-dependent translated string
- _kanbanLaneNames now places 'default' first, other assignees alphabetically,
  and the Unassigned lane last
- _kanbanRenderProfileLanes filters by _kanbanLaneKey, renders label via
  _kanbanLaneLabel, and adds kanban-profile-lane-unassigned CSS class
- _kanbanTaskMeta always emits the unassigned label so sidebar shows it too
- _kanbanRenderBoard checks unfiltered total and shows a 'Tasks hidden by
  filters' state with a Clear filters button when text search hides all tasks
- Add kanban_tasks_hidden_by_filters and kanban_clear_filters i18n keys to
  all 13 locale blocks
- Add .kanban-profile-lane-unassigned header border using warning token

Fixes: on Dynasty board mobile, Ready=3 CLI but default lane showed Ready=0
because those tasks were unassigned and had no visible lane.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 03:54:32 +00:00
Rod Boev
c109ed9113 fix(plugins): align activation-state sort with badge precedence (#4713) 2026-06-25 02:57:06 +00:00
Rod Boev
e3a1540d32 fix: align sort partition with badge for inactive exclusive providers 2026-06-25 02:57:06 +00:00