Commit graph

7306 commits

Author SHA1 Message Date
Frank Song
6e3543de16 merge master into provider-aware model selection
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-16 23:44:01 +08:00
Frank Song
aafa6735aa test(models): harden provider selection harness
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-16 23:24:42 +08:00
nesquena-hermes
df0c204d73
docs(rfc): reconcile Live-to-Final assistant-reply contracts with shipped behavior (#6144) (#6147)
Docs-only reconciliation of the Stable Assistant Turn Anchors / Live-to-Final
RFCs + CONTRACTS.md + Phase 0 inventory from proposed/scaffold to
accepted/implemented framing. No runtime/CI change; one comment-header fix in
static/assistant_turn_anchors.js. Remaining hardening tracked under #3400.

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-07-16 08:19:43 -07:00
Frank Song
be21deb966 fix(models): preserve provider-aware duplicate selections 2026-07-16 20:05:58 +08:00
nesquena-hermes
6ed474ca54
fix(models): route list-shaped custom provider models (#6127) (#6128)
Centralize custom-provider model-ID extraction into _configured_model_ids()
+ _configured_model_options(), used by both catalog construction and runtime
routing so the picker and resolver can't drift. Fixes list-shaped
custom_providers[].models allowlists silently staying on the default provider
instead of routing to their own (#6121). Existing mapping / list-of-strings
configs unaffected.

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-07-16 00:22:50 -07:00
nesquena-hermes
c39e8edaf1
fix(#6100): clear stale gateway approval attention badge (#6117) (#6125)
Reconcile the gateway pending-mirror in _session_attention_summary()
before counting sidebar approval attention, so a parent session's red
approval dot clears once the underlying gateway approval has drained.
Live approvals stay visible; clarify fallback unchanged. WebUI read-path
mitigation; child-session context-propagation root cause stays in #6100.

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-15 22:34:19 -07:00
nesquena-hermes
6e60ae5e98
Merge pull request #6119 from nesquena/stage/6013
Release exp: cache GET /api/providers per-profile with short TTL (#6013)
2026-07-15 19:50:58 -07:00
nesquena-hermes
d9f6a4ecb6 Release exp: cache GET /api/providers per-profile with short TTL (#6013). Thanks @SeanWit. 2026-07-16 02:45:50 +00:00
nesquena-hermes
500fa485d2 Merge commit 'refs/pull/6013/head' of github.com:nesquena/hermes-webui into stage/6013 2026-07-16 02:28:15 +00:00
nesquena-hermes
1845e63931
Merge pull request #6116 from nesquena/fix/6105-reparent
Release exp: reparent mobile model picker to body so it never opens off-screen (#6105/#6080)
2026-07-15 18:31:31 -07:00
nesquena-hermes
a69bc3a264 fix(#6105): close the drawer-over-picker stacking class via z-index, not per-site guards
Codex found the body-reparented picker (z-200, later body child) could be revealed
UNDER a full-screen drawer/panel via multiple paths (swipe, breakpoint resize,
profile-switch). Rather than guard each open-site (whack-a-mole + my resize guard
over-fired on keyboard reflows), fix the class once: .model-dropdown--floating gets
z-index:190 — above the composer-mobile-config-panel (z-180) it opens from, below
the mobile sidebar + workspace panel (both z-200) — so any drawer/panel that opens
covers the picker, matching master's stacking intent. Reverted the two boot.js
guards. Co-authored-by webtecnica.
2026-07-16 01:15:11 +00:00
nesquena-hermes
7ed58510bc fix(#6105): also close floating picker on breakpoint-crossing resize that reveals a drawer
Codex found a sibling of the swipe-stacking edge: a direct landscape/tablet->phone
resize can re-reveal an already-open mobile sidebar or workspace panel under the
body-reparented picker (z-200 siblings, no click to trigger outside-close). The
window resize handler now guardedly closes+restores the picker before re-syncing
drawer state, gated on entering phone width AND a drawer/panel actually being open,
so ordinary keyboard/URL-bar reflows don't dismiss the picker mid-use.
2026-07-16 00:59:01 +00:00
nesquena-hermes
7091c4aa84 fix(#6105): close floating model picker before gesture-opening mobile sidebar
Codex re-gate found the body-reparented picker (z-index:200, body child) would
paint on top of the mobile sidebar drawer when a left-edge swipe opened the
sidebar while the picker was open. _openMobileSidebarFromGesture() now calls
guarded closeModelDropdown() first (which also restores the dropdown to the
footer), so the swipe reveals the session drawer cleanly. Co-authored-by webtecnica.
2026-07-16 00:48:27 +00:00
nesquena-hermes
6e30fd21f8 docs: CHANGELOG for #6105 model-picker reparent 2026-07-16 00:29:07 +00:00
nesquena-hermes
fca5056589 fix: reparent mobile model picker to body so fixed positioning escapes footer (#6080)
The composer model dropdown (#composerModelDropdown) is a child of
.composer-footer, which sets container-type:inline-size (and, under the Geist
Contrast skin, a backdrop-filter). Both establish a fixed containing block, so
the phone position:fixed dropdown resolved against the FOOTER (pinned to the
bottom of the screen) instead of the visual viewport — dropping the menu below
the fold on all skins (the "tapping the model picker does nothing / opens
off-screen" symptom in #6080).

Fix (Fable UX gate recommendation on PR #6105): on the phone path
(<=640px) reparent the dropdown to <body> and switch it to position:fixed via a
.model-dropdown--floating class, exactly the working #profileDropdown idiom, so
fixed positioning is viewport-relative on every skin. The visualViewport math
from PR #6105 is preserved (viewport-relative coordinates, above/below flip
preferring the larger space, width = viewport - 16px, titlebar-bottom clamp) and
so are the rAF-coalesced visualViewport resize/scroll listeners.

Desktop (>640px) is unchanged: closeModelDropdown() and the desktop branch of
_positionModelDropdown() restore the element into its original .composer-footer
slot and clear the phone inline styles, so the desktop popover stays an
absolutely positioned footer child, byte-for-byte identical to master. Because
the menu now leaves the footer on phones, no per-skin backdrop-filter override
is needed.

Adds tests/test_issue6080_model_picker_reparent.py, including a node-executed
DOM-parentage guard that runs _positionModelDropdown()/closeModelDropdown()
against a stub DOM and asserts the open phone dropdown has NO .composer-footer
ancestor (escaping the container-type trap) and is restored into the footer on
close and on desktop.

Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>
2026-07-16 00:27:47 +00:00
nesquena-hermes
7a862c9f6d
Merge pull request #6115 from nesquena/stage/css2-batch
Release exp: narrow-screen CSS fixes — popup min-width (#6104) + timestamp foldable layout (#6106)
2026-07-15 17:23:14 -07:00
nesquena-hermes
ee7ec5e5d7 Release exp batch: narrow-screen CSS fixes — approval/clarify popup min-width (#6104) + stream-timestamp foldable layout (#6106).
Scoped to the two stated fixes only: strips the #composerModelDropdown position:fixed
mobile override that #6104's branch had bundled in (that's the #6080 model-picker
mechanism, being reworked separately to reparent to body level — the fixed override
is trapped by .composer-footer's container-type so it doesn't belong here), and drops
the <=360px tool-name-hiding rules (header overflow-clip prevents the spill without
losing tool identity). Thanks @webtecnica.
2026-07-16 00:10:25 +00:00
nesquena-hermes
928646a57c Merge commit 'refs/pull/6106/head' of github.com:nesquena/hermes-webui into stage/css2-batch 2026-07-15 23:58:12 +00:00
nesquena-hermes
89e443674e Merge commit 'refs/pull/6104/head' of github.com:nesquena/hermes-webui into stage/css2-batch 2026-07-15 23:58:10 +00:00
nesquena-hermes
a155a616c7
Merge pull request #6111 from nesquena/stage/5913-pinchzoom
Release exp: pinch-to-zoom for Mermaid diagrams on touch devices (#5913)
2026-07-15 16:06:58 -07:00
nesquena-hermes
9c0e0986c3 Release exp: pinch-to-zoom for Mermaid diagrams on touch devices (#5913). Thanks @silent-reader-cn. 2026-07-15 23:02:13 +00:00
silent-reader-cn
0403f00789 fix: address Greptile review and fix CI test mock for touch handlers
Three fixes in one commit:

1. **CI test mock** — Added addEventListener/removeEventListener to the
   Node.js makeElement mock so viewport.addEventListener() calls in the
   touch handler registration don't crash with 'not a function'.

2. **Two-finger pan during pinch** — Replaced _setScale() call with
   direct transform math. The new formula computes scale and position
   simultaneously:
     ratio = boundedScale / pinchStartScale
     x = currMidX - (startMidX - startPosX) * ratio
   This keeps the diagram under both fingers regardless of midpoint
   drift, enabling simultaneous zoom + pan.

3. **Post-pinch click suppression** — _onTouchEnd now sets dragged=true
   when exiting pinch mode so any synthesized click event is caught
   by the existing dragged guard in _openViewerOnClick instead of
   opening the lightbox.
2026-07-15 23:02:13 +00:00
silent-reader-cn
7419b1bb51 feat: add pinch-to-zoom support for Mermaid diagrams on touch devices
Adds touch event handlers (touchstart/touchmove/touchend/touchcancel) to
the Mermaid viewer viewport for two-finger pinch zoom, using the existing
_setScale() function with midpoint anchoring.

- Guards pointer handlers (down/move/click) during active pinch to prevent
  interference between touch and pointer event systems
- Uses {passive: false} on touchstart/touchmove to allow preventDefault()
- Falls back gracefully on non-touch devices — touch listeners are inert
  without touch hardware

Fixes: Mermaid diagrams in Hermes WebUI were not zoomable via multi-touch
pinch on mobile/tablet devices, frustrating users with complex diagrams.
2026-07-15 23:02:13 +00:00
nesquena-hermes
a486c2f583
Merge pull request #6110 from nesquena/stage/6102-cachettl
Release exp: hold streaming CLI-cache TTL above sidebar poll cadence (#6102)
2026-07-15 15:55:24 -07:00
nesquena-hermes
a2d70b9e53 Release exp: hold streaming CLI-cache TTL above sidebar poll cadence (#6102). Thanks @starship-s. 2026-07-15 22:40:48 +00:00
nesquena-hermes
b877c9aa40 Merge commit 'refs/pull/6102/head' of github.com:nesquena/hermes-webui into stage/6102-cachettl 2026-07-15 22:40:21 +00:00
webtecnica
9ac68feecb fix: stream event timestamps layout on narrow foldable screens (#6099) 2026-07-15 18:31:51 -03:00
webtecnica
2143563109 fix: ensure approval/clarify popups have min-width on narrow screens (#6094) 2026-07-15 18:30:54 -03:00
nesquena-hermes
0f0f733b83
Merge pull request #6103 from nesquena/stage/6056-delegtitles
Release exp: delegated subagent sidebar titles (#6056) + sidebar title-redaction hardening
2026-07-15 14:20:17 -07:00
nesquena-hermes
38119fac25 fix(#6056): extend sidebar title-field redaction to /api/sessions/search (Codex class-fix)
Codex re-gate found the same derived-title leak on the sibling /api/sessions/search
endpoint (3 branches redacted only 'title'). Extract _redact_sidebar_title_fields()
shared helper, apply it in both _sidebar_session_response_item and all 3 search
branches, read api_redact_enabled once per search response (mirrors #4662). +helper
test + source-guard that every search branch calls the redactor.
2026-07-15 21:06:36 +00:00
starship-s
f5ef3688c6 fix(cache): keep streaming TTLs above poll cadence 2026-07-15 20:59:59 +00:00
nesquena-hermes
ffbc91343a fix(#6056): redact display_title/_state_db_title/parent_title on the sidebar API
Codex gate found the #6056 derived display_title (from raw user-message content)
bypassed sidebar redaction — the serializer redacted only 'title', so a credential
in a delegated goal leaked to the sidebar even with api_redact_enabled=True.
Redact display_title + _state_db_title + parent_title on the same path; +regression
test (proven to fail on the unfixed serializer).
2026-07-15 20:57:31 +00:00
nesquena-hermes
80cb79c7f7 Release exp: show delegated goal titles for generic subagent child sessions (#6056). Thanks @rodboev. 2026-07-15 20:47:11 +00:00
nesquena-hermes
b96405ef29 Merge commit 'refs/pull/6056/head' of github.com:nesquena/hermes-webui into stage/6056-delegtitles 2026-07-15 20:46:34 +00:00
nesquena-hermes
93a9bdcc3f
Merge pull request #6101 from nesquena/stage/6062-readrange
Release exp: show requested line range in collapsed read_file tool labels (#6062)
2026-07-15 13:41:13 -07:00
nesquena-hermes
6efbf899c9 Release exp: show requested line range in collapsed read_file tool labels (#6062). Thanks @franksong2702. 2026-07-15 20:37:32 +00:00
Frank Song
59c8f988ef fix(ui): reject invalid read range limits 2026-07-15 20:37:02 +00:00
Frank Song
5b46c54c92 feat(ui): show read ranges in collapsed tool labels 2026-07-15 20:37:02 +00:00
nesquena-hermes
3c9304aef5
Merge pull request #6098 from nesquena/stage/6088-manualupd
Release exp: allow manual update check when auto-update is disabled (#6088)
2026-07-15 13:20:26 -07:00
nesquena-hermes
2e93d04a57 Release exp: allow manual update check when auto-update is disabled (#6088) + regression test. Thanks @webtecnica. 2026-07-15 20:02:35 +00:00
nesquena-hermes
754dcca048 Merge commit 'refs/pull/6088/head' of github.com:nesquena/hermes-webui into stage/6088-manualupd 2026-07-15 20:02:02 +00:00
nesquena-hermes
ad88366baa
Merge pull request #6097 from nesquena/stage/6081-noresp
Release exp: suppress false no-response after tool-limit turn with a final answer (#6081)
2026-07-15 12:43:22 -07:00
nesquena-hermes
e41507d164 Release exp: suppress false no-response after tool-limit turn with a final answer (#6081). Thanks @coyaSONG. 2026-07-15 19:39:02 +00:00
chsong
139c76535b fix tool-limit final answer classification 2026-07-15 19:38:25 +00:00
nesquena-hermes
57e8aafb16
Merge pull request #6096 from nesquena/stage/6083-brick
Release exp: fix new-conversations-unstartable brick + eviction leak bound (#6083)
2026-07-15 12:21:19 -07:00
nesquena-hermes
30059085c2 fix(#6083): only grace-evict CONFIRMED never-persisted shells (Codex finding #2)
Codex re-gate asked to distinguish a confirmed-absent sidecar from an
indeterminate/unreadable one before entering the abandoned-shell grace path.
Add _session_sidecar_exists() (True/False/None) and only grace-evict when the
sidecar is confirmed absent (False); a present-or-indeterminate sidecar keeps
the session resident. Age-window bound retained (fully closes the leak, no
data-loss edge); an SSE-mounted-lease refinement is noted as optional follow-up.
2026-07-15 19:06:10 +00:00
nesquena-hermes
02e5acb890 fix(#6083 follow-up): bound never-persisted empty session shells
Codex gate found the eviction-reorder made never-saved empty shells immortal
(abandoned /api/session/new tabs accumulate unbounded past sessions_cache_max).
Add a grace-window bound: protect a never-persisted empty shell only while fresh
(<30min old) OR while it carries a composer draft; a stale, empty, draftless,
never-saved shell is treated as abandoned and becomes evictable again. A real
compose persists a draft on the first keystroke (disk-backed + reloadable), so
the brick fix is preserved while the leak is closed. +2 regression tests.
2026-07-15 18:44:21 +00:00
nesquena-hermes
0881b8ec0c Release exp: fix new-conversations-unstartable (password-manager autofill evicts unsaved session) (#6083). Thanks @ai-ag2026. 2026-07-15 18:24:11 +00:00
nesquena-hermes
078a64cabe Merge commit 'refs/pull/6083/head' of github.com:nesquena/hermes-webui into stage/6083-brick 2026-07-15 18:23:03 +00:00
Sean
2a191007f1
Merge branch 'master' into fix/6010-cache-providers 2026-07-16 00:54:24 +08:00