Commit graph

4400 commits

Author SHA1 Message Date
nesquena-hermes
4e5eebd0d6 feat(#2941): add read-only LLM Wiki browser in the Insights panel
Surfaces the Hermes agent's LLM Wiki as a read-only knowledge-base
observability section in Insights (status counts + browsable read-only
pages). Unconfigured shows a clear "Unavailable" empty state.

Strictly read-only, auth-gated. The wiki file-serving path is hardened
against the full traversal/symlink/TOCTOU class (Codex + Opus review, each
vector regression-tested + toothless-checked):
- _llm_wiki_page_files resolves the wiki root once as the trust base;
  skips a section whose resolved path escapes the root; requires each *.md
  target's RESOLVED path under both real root and real section; rejects
  dot-prefixed segments on lexical AND resolved-relative paths (closes
  symlink-to-hidden/out-of-tree + symlinked-section escapes). Browse + read
  share this allowlist.
- /api/wiki/page rejects abs paths + a real '..' SEGMENT (legit v1..v2.md
  still opens); requires the resolved target in the allowlist; reads the
  resolved path with O_NOFOLLOW; and fstat-verifies the open fd's
  (st_dev, st_ino) against the identity captured at allowlist time — closing
  final-component AND parent-directory symlink-swap TOCTOU races. Any
  mismatch/vanish -> clean 404, never a 500.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-17 19:47:45 +00:00
nesquena-hermes
d59cae5984
Merge pull request #4371 from nesquena/stage-4364
Release v0.51.477 — QL (gateway runs-API routing now opt-in #4362)
2026-06-17 10:52:43 -07:00
nesquena-hermes
84d3da7230 fix(#4362): gate gateway runs-API routing behind explicit opt-in
The WebUI gateway chat backend routed every browser chat turn through the
gateway's /v1/runs API whenever the gateway advertised approval/runs
support (gateway_chat.py capability gate). The runs API creates an isolated
Api_Server session per turn, so mid-conversation context was lost between
messages. Routing through /v1/runs is now gated behind an explicit opt-in
(_gateway_use_runs_api_enabled: HERMES_WEBUI_GATEWAY_USE_RUNS_API env var or
webui_gateway_use_runs_api config key, env-over-config precedence, default
off). The capability gate now requires BOTH the opt-in flag AND gateway
approval support before using /v1/runs; otherwise normal chat stays on
/v1/chat/completions, preserving the session via X-Hermes-Session-Id.
Operators who want the in-gateway-chat approval-events flow re-enable it
with the flag.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-17 17:41:45 +00:00
nesquena-hermes
bb08d36588
Merge pull request #4370 from nesquena/stage-brick
Release v0.51.476 — QK (cross-provider model-pick #4363 + non-git update-status #4356)
2026-06-17 09:55:32 -07:00
edoumo
a41663da29 docs(changelog): stamp v0.51.476 Release QK (#4363 cross-provider model-pick + #4356 non-git update-status) 2026-06-17 16:36:26 +00:00
edoumo
9194ade18f fix: report non-git installs as "can't check" instead of "up to date" (#4356)
Docker images and pip installs have no .git directory, so _check_repo
returned None and the frontend interpreted both update-target parts being
falsy as "Up to date" — a stale Docker image would misleadingly look
current. _check_repo now returns a minimal sentinel dict ({name, behind:
None, no_git: True}) instead of None so the Settings panel can distinguish
"can't check" from "up to date", and _formatUpdateTargetStatus /
checkUpdatesNow surface a localized "Can't check for updates" state
(settings_update_no_git, added across all 13 locale blocks). In mixed
deployments where one component has a git checkout and the other does not,
the valid git-side update info is still shown. The sentinel's behind: None
flows safely through the existing notification/summary builders, which all
gate on int(info.get('behind') or 0) > 0.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-17 16:36:26 +00:00
edoumo
446cd2cd98 fix: prevent cross-provider model selection from silently reverting to default (#4363)
When the model dropdown rebuilds (panel switch, provider refresh, or a
live-model fetch that times out and serves a fallback catalog), a
cross-provider selection could vanish from the refreshed catalog and the
browser would snap the <select> back to its first <option> (the default
model). _reconcileModelDropdownSelection now routes each selection branch
through an _applyOrEnsure helper that delegates to
_ensureModelOptionInDropdown, re-injecting the chosen model as a custom
option when it is missing from the rebuilt catalog instead of returning
null. Branch order and per-branch model/provider arguments are unchanged.

Maintainer note: simplified the contributor's helper to delegate straight
to _ensureModelOptionInDropdown (which already applies-first-then-injects),
removing a redundant double scan; re-pointed the
test_populate_model_dropdown_reconciles_selection_after_rebuild string
assertions to the new form (the behavioral node-driver tests for boot-default
precedence and in-page selection preservation are unchanged and still pass).

Co-authored-by: Ed <edou@edoudoux.com>
2026-06-17 16:22:51 +00:00
nesquena-hermes
502f806d5b
Merge pull request #4352 from nesquena/stage-3510
Release v0.51.475 — ElevenLabs TTS engine (#3510)
2026-06-16 23:08:13 -07:00
nesquena-hermes
fd014a1143 harden(#3510): re.fullmatch for voice_id validation (Opus review, defense-in-depth) 2026-06-17 05:56:54 +00:00
nesquena-hermes
ef6f43c69c Release v0.51.475 — ElevenLabs TTS engine (#3510)
Ships @linux-fertxo's ElevenLabs TTS engine: selectable alongside Browser and
Edge TTS, using the agent's existing ELEVENLABS_API_KEY + tts.elevenlabs config.
ElevenLabs is a built-in Hermes TTS provider (Anchor A parity). Backend validates
voice_id as a safe path segment, honors the 5000-char cap + rate limit.

Rebased onto current master (552 behind; net code-diff applied, byte-identical to
PR head). Added regression coverage (test_issue3510_elevenlabs_tts.py): missing-key
503, config voice_id traversal rejection, happy-path mp3 stream, shared char-cap.

Co-authored-by: linux-fertxo <linux-fertxo@users.noreply.github.com>
2026-06-17 05:50:37 +00:00
nesquena-hermes
27005e3bbb
Merge pull request #4351 from nesquena/stage-4348-v2
Release v0.51.474 — reconcile WebUI session source flags (#4348)
2026-06-16 22:21:02 -07:00
nesquena-hermes
849aba1c8a Release v0.51.474 — reconcile WebUI session detail source flags (#4348)
Ships @dso2ng's fix: /api/session now reconciles stale CLI source flags
against the state.db WebUI-origin row, and _isExternalSession() refuses to
classify an explicit WebUI-source session as external — so a WebUI-native
transcript with a stale sidecar isn't force-reloaded like an imported one.

Rebased onto master (contributor's net code-diff applied; byte-identical to
PR head). CHANGELOG stamped v0.51.474.

Co-authored-by: dso2ng <dso2ng@users.noreply.github.com>
2026-06-17 05:11:45 +00:00
nesquena-hermes
8d0a3035c4
Merge pull request #4350 from nesquena/stage-4341
Release v0.51.473 — clean cancelled user context chains (#4341)
2026-06-16 22:08:05 -07:00
nesquena-hermes
7ab18c4ae6 docs(changelog): v0.51.473 — clean cancelled user context chains (#4341)
Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-06-17 05:05:00 +00:00
nesquena-hermes
ac6e975771 docs(streaming): clarify the loose-substring branch relies on the suffix invariant (#4341 review)
Opus review note: the substring match in _stale_prefix_matches_prior_user_context
is intentionally loose, but correctness depends on the caller's
suffix-equals-entire-submitted-turn invariant (cleaning can only ever rewrite a
flagged row to the user's actual current turn — never drop legitimate content).
Comment-only; no behavior change.
2026-06-17 05:04:34 +00:00
Hermes Agent
ce3179d64f fix(streaming): clean cancelled user context chains 2026-06-17 04:49:55 +00:00
nesquena-hermes
fa21a4bb81
Merge pull request #4349 from nesquena/stage-4339
Release v0.51.472 — paste image + text together (#4339)
2026-06-16 21:47:26 -07:00
nesquena-hermes
15f85533a5 docs(changelog): v0.51.472 — paste image+text together (#1620 regression, #4339)
Co-authored-by: kaishi00 <kaishi00@users.noreply.github.com>
2026-06-17 04:44:16 +00:00
kaishi00
d882095ca5 test: update #1620 test for new text+image paste contract
- Rename test_handler_skips_attach_when_text_present to
  test_handler_skips_preventDefault_when_text_present: asserts
  early-return on text-only clipboards and that preventDefault is
  gated on !hasText (the new contract)
- Add test_handler_attaches_image_when_both_text_and_image_present:
  positive assertion that addFiles() is reached when both text and
  image items are in the clipboard (the headline fix)

Addresses review feedback: tests now reflect the new behavior matrix
instead of the old if(!imageItems.length || hasText) return guard.
2026-06-17 04:37:29 +00:00
kaishi00
ad99ff4976 fix(composer): attach image AND paste text when clipboard carries both
The paste handler (boot.js) bailed entirely when the clipboard contained
both text and image items, preventing image attachment in the most common
real-world scenario: right-click → Copy Image from browsers, Notes, Slack,
etc. — these almost always carry a text/html or text/plain representation
alongside the image.

Previous behavior (from #1620 fix):
  - clipboard has text only → normal text paste (correct)
  - clipboard has image only → attach image (correct)
  - clipboard has BOTH → text pasted, image silently dropped (wrong)

New behavior:
  - clipboard has text only → normal text paste (unchanged)
  - clipboard has image only → preventDefault + attach image (unchanged)
  - clipboard has BOTH → let text paste normally AND attach image

The key change: preventDefault() is now only called when no text is
present (true screenshot paste), so the browser handles text insertion
naturally while addFiles() runs regardless.

Fixes the regression introduced by the #1620 fix. Closes #1620.
2026-06-17 04:37:29 +00:00
nesquena-hermes
ae8d56080f
Merge pull request #4347 from nesquena/stage-4340-v2
Release v0.51.471 — searchable settings (#3850)
2026-06-16 21:30:33 -07:00
nesquena-hermes
90f51bfd27 test(#3820): anchor fadeTextCb listener assertion on its block, not a byte offset
test_fade_text_effect_uses_dynamic_window_check used PANELS_JS.index(needle,
300000) to skip the payload-builder occurrence and land on the listener block.
The #3850 searchable-settings additions shifted byte offsets so the magic 300000
now lands on the payload-builder occurrence instead → false failure (the actual
listener code is unchanged + correct). Anchor on the listener-specific
'fadeTextCb=...\n    if(fadeTextCb){' instead. No production code change.
2026-06-17 04:20:38 +00:00
nesquena-hermes
95266ef50c fix(#3850): broaden settings-search indexing + invalidate stale searches (review)
Codex+Opus review fixes:
1. Index query missed the common toggle shape <label><input><span data-i18n>>
   — only label[data-i18n] was indexed, so most checkbox settings (Virtualize
   long transcripts, Browser notifications, etc.) were silently unsearchable.
   Broaden to 'label[data-i18n], label [data-i18n], label' and make
   _resolveSettingsField resolve any [data-i18n] node to its .settings-field.
2. Stale async-render race: _beginSettingsPanelSession now bumps
   _settingsSearchSeq + clears the input/results, and the outside-click dismiss
   handler also bumps the seq, so an in-flight index build from a prior session
   can't paint into the dropdown. + 4 regression tests.
2026-06-17 04:12:54 +00:00
nesquena-hermes
757e3739fb Release v0.51.471 — searchable settings (#3850)
Ships @rodboev's searchable-settings feature (#3850/#3936): a search box in
Settings that finds any field across all 5 sections with breadcrumbs +
click-to-navigate+highlight+clear. Rebased fresh onto master (post v0.51.470),
5 feature files byte-identical to PR head, re-stamped v0.51.471 (was v0.51.469,
collided with the shipped transcript-virtualization release).

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-17 04:05:00 +00:00
nesquena-hermes
f863d628a9
Merge pull request #4344 from nesquena/fix/4343-virtualization-default-off
Release v0.51.470 — transcript virtualization experimental/off by default (#4343)
2026-06-16 20:52:07 -07:00
nesquena-hermes
5a8d24c936 fix(#4343): default transcript virtualization OFF (experimental/opt-in) + force-off migration
Virtualization (#500/#4214) caused a scroll-up flicker on long sessions with
tall tool-call rows (variable-height anchor oscillation), distinct from the
stream-completion jump fixed in #4325. Per maintainer decision, flip the
'Virtualize long transcripts' preference to experimental/opt-IN (default OFF)
for everyone until the scroll behavior is reliable.

- api/config.py: virtualize_transcript default True->False; add
  virtualize_transcript_optin marker (both bool-allowlisted); load_settings
  force-off migration — a stored true WITHOUT the post-flip opt-in marker is
  reset to off, so 100% of existing users land on off; an explicit opt-in
  (marker present) is honored.
- boot.js / panels.js: default-off semantics (===true), persist the opt-in
  marker on enable, checkbox not pre-checked.
- index.html / i18n.js (en): relabel experimental + flicker warning.
- Tests: rewrite the #4325 toggle assertions for the opt-in contract + 4 new
  migration tests (unset/stale-true/explicit-optin/marker-without-true).

Phase B (root-cause scroll-stability) tracked separately. Thanks @b3nw.
2026-06-17 03:41:47 +00:00
nesquena-hermes
6340529bce
Merge pull request #4342 from nesquena/mega-4325
Release v0.51.469 — resolve transcript virtualization freeze/jump + add opt-out (#4325)
2026-06-16 19:51:13 -07:00
nesquena-hermes
65c74988a8 fix(#4325): log renderMessages failure on virtualization toggle (greptile P2)
The opt-out toggle's change handler wrapped renderMessages in a silent
empty catch. Keep the defensive try (a toggle must never wedge the
settings handler) but console.warn on failure so a broken render is
diagnosable instead of swallowed.
2026-06-17 02:48:21 +00:00
nesquena-hermes
92df66d199 docs(changelog): v0.51.469 — transcript virtualization fix + opt-out (#4325) 2026-06-17 02:34:55 +00:00
nesquena-hermes
810414b438 feat(#4325): opt-out toggle to disable transcript virtualization
Adds a Preferences setting 'Virtualize long transcripts' (default ON, opt-out).
When disabled, _currentMessageVirtualWindow returns a full non-virtualized window
so the entire transcript renders at once (also short-circuits the virtual
re-measure loop). Server-persisted (virtualize_transcript bool, default True),
hot-applies via renderMessages on toggle. i18n in all 13 locales.

Pairs with @rodboev's #4328 stream-end anchor scroll fix (cherry-picked into
this branch) to fully resolve #4325.
2026-06-17 01:52:06 +00:00
Rod Boev
31191a6c50 fix(scroll): mount virtualized-out anchor row before same-frame restore 2026-06-17 01:42:44 +00:00
Rod Boev
387af5d9c9 fix(scroll): remove synchronous _programmaticScroll toggle around DOM clear 2026-06-17 01:42:44 +00:00
Rod Boev
152b44c138 Clear _programmaticScroll after innerHTML wipe to prevent stuck flag on early-return paths 2026-06-17 01:42:44 +00:00
Rod Boev
b24aa661dc fix(#4325): use semantic viewport anchoring for stream-end scroll restore 2026-06-17 01:42:44 +00:00
nesquena-hermes
ce272d9cd5
Release v0.51.468 — onboarding OAuth single-flight (#3970) (#4338)
Release v0.51.468 — onboarding OAuth single-flight (#3970)
2026-06-16 17:31:44 -07:00
nesquena-hermes
27062fa0e9 release: stamp v0.51.468 (Release QC — onboarding OAuth single-flight) 2026-06-17 00:27:28 +00:00
hinotoi-agent
3c1b7392ba Avoid blocking OAuth flow lock during Codex start 2026-06-17 00:20:49 +00:00
hinotoi-agent
a700e8e0d3 fix(security): reuse pending onboarding oauth flows 2026-06-17 00:20:49 +00:00
nesquena-hermes
933446c5c5
test(#4331): CI coverage for model-picker in-place overflow reveal path (#4333)
test(#4331): CI coverage for model-picker in-place overflow reveal path
2026-06-16 16:57:52 -07:00
nesquena-hermes
ed8ede64b2
Merge branch 'master' into pr/4331-inplace-overflow-tests 2026-06-16 16:53:24 -07:00
Rod Boev
40df3700af fix(review): target errored group wrapper by data-group in endpoint-error driver 2026-06-16 19:43:36 -04:00
nesquena-hermes
7224e76525
Merge pull request #4337 from nesquena/stage/4228-symlink-tree
Release v0.51.467 — symlinks in workspace file tree (#4228)
2026-06-16 16:39:04 -07:00
Rod Boev
840e8a603a fix(review): fix bug #1 selected-group masking in search-clear test 2026-06-16 19:36:42 -04:00
Rod Boev
f90b9b1551 fix(review): harden in-place overflow tests so each fails on its reverted fix 2026-06-16 19:31:11 -04:00
nesquena-hermes
f9937cbb25 docs(changelog): v0.51.467 — symlinks in workspace file tree (#4228) 2026-06-16 23:26:11 +00:00
nesquena-hermes
53adcfc33f feat(#4226): display symlinks in workspace file tree (#4228)
Render symlinks in the workspace file tree with a distinct chain-link
icon (vs regular file/folder icons), and show "Symlink → {target}" on
hover with long targets middle-elided. Directory and file symlinks are
both handled; regular entries keep their normal icons.

Rebased onto master (v0.51.466) and code-diff verified byte-identical to
the contributor's PR head before staging.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-16 23:24:15 +00:00
nesquena-hermes
ede9279c88
Release v0.51.466 — refresh active WebUI transcript on session events (#4208) (#4335)
Release v0.51.466 — refresh active WebUI transcript on session events (#4208)
2026-06-16 15:46:19 -07:00
nesquena-hermes
46b6d059b0 release: stamp v0.51.466 (Release QA — active transcript reconciles on session events) 2026-06-16 22:36:19 +00:00
nesquena-hermes
c96fa3cb1f fix(sessions): refresh active WebUI sessions on session events 2026-06-16 22:35:47 +00:00
nesquena-hermes
67b1c85130
Release v0.51.465 — reject embedded null byte in remote workspace candidate (#4326) (#4334)
Release v0.51.465 — reject embedded null byte in remote workspace candidate (#4326)
2026-06-16 15:34:16 -07:00