Commit graph

3732 commits

Author SHA1 Message Date
nesquena-hermes
358c77d775 Merge PR #5668 into stage 2026-07-06 06:13:26 +00:00
nesquena-hermes
51dffad1ae Merge PR #5594 into visible-batch stage 2026-07-06 05:37:45 +00:00
nesquena-hermes
db87fd3a07 Merge PR #5630 into visible-batch stage 2026-07-06 05:37:45 +00:00
nesquena-hermes
49afd5c0c2 Merge PR #5605 into visible-batch stage 2026-07-06 05:37:44 +00:00
Rod Boev
e82cb801de feat(#5665): add shared layout-assertion helpers for browser tests 2026-07-06 01:23:21 -04:00
nesquena-hermes
6416192ceb Merge PR #5644 into phase1 stage 2026-07-06 03:52:38 +00:00
Frank Song
18a2078ebb fix(streaming): guard empty terminal error hints 2026-07-06 11:15:31 +08:00
nesquena-hermes
e7fa04ef24 merge master into review-5655 (base refresh for up-to-date branch protection) 2026-07-06 03:05:41 +00:00
nesquena-hermes
9738ebb614
fix(tests): sync on subscriber count in multitab SSE test (#5628) (#5660)
test_same_stream_in_two_tabs_receives_identical_token_sequence started both
SSE-handler threads then immediately put_nowait'd the tokens + stream_end
without waiting for both threads to subscribe. StreamChannel.put_nowait()
buffers events only while there are zero subscribers and clears that buffer
the moment the first subscriber attaches and broadcasts live — so producing
events in the window after tab A subscribes but before tab B does orphans
tab B (subscribes to a cleared buffer, never sees stream_end, hangs ->
thread.join timeout -> 'every tab should finish' fails). ~20% timing flake
on slow CI runners (3.11 shard-4).

Wait until len(stream._subscribers) == len(handlers) (bounded 5s) before
producing events, so delivery is deterministic regardless of thread-start
timing. Test-only; no production change (buffer-then-broadcast is correct for
the real reconnect case).

Verified: original flakes 2/30 under CPU contention; fixed 30/30.

Fixes #5628

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-07-05 19:52:16 -07:00
nesquena-hermes
8929977b5b review: merge #5655 (77f54eeb) 2026-07-06 02:46:52 +00:00
nesquena-hermes
7d3057c7c9 release: reload config cache on profile switch (#5645, #5619)
Root fix for the #5619 profile-switch config-cache bug: split the reload gate so a profile-PATH change forces reload unconditionally (was wrongly suppressed by in-memory overrides → stale default-profile config/models/keys), while a file-mtime change still respects overrides. Folded in a test-isolation fix (Codex): the stage302 regression test leaked _cfg_cache/_cfg_mtime/_cfg_path/_cfg_fingerprint/cfg globals (deepcopy snapshot + clear/update restore, since reload_config mutates _cfg_cache in place). Full gate: Codex SAFE (2 rounds), suite 12178/0, 121 profile/provider tests.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-06 02:33:45 +00:00
nesquena-hermes
f7fe895c63 review: merge #5645 (00a69521) 2026-07-06 02:09:22 +00:00
nesquena-hermes
c601e11324 release: fork read-only cron sessions into a new writable chat (#5555)
Relaxes the /api/session/branch read-only gate so a canonical-cron session can be forked into a new WebUI-owned session (source never saved). Gate hardening folded in: the read-only branch gate now covers BOTH the synthesized/missing-sidecar path AND the persisted/loaded path (Codex found a stored read-only non-cron session could be branched + saved); relaxation keys on the server-authoritative resolved source (source_tag/raw_source/source == 'cron'), never the id prefix. Full gate: Codex adversarial SAFE, 84 branch/claim tests + suite 12175/0, browser clean.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-06 02:02:52 +00:00
nesquena-hermes
f10cb2a35c review: merge #5555 (11eed2af) 2026-07-06 01:44:21 +00:00
Frank Song
77f54eeb5a Render session recovery card from active session state 2026-07-06 09:43:10 +08:00
nesquena-hermes
936c5b051b release: redesign cron preset schedule builder — sentence UI with time picker + day dropdowns (#5554)
Reworks the #5552 preset builder per maintainer UX feedback + Fable design advisory: one natural 'Schedule: [preset] on [day] at [time]' row; native time picker (themed) replacing hour/minute number boxes; day-of-week + day-of-month dropdowns; raw cron field shown only on Custom; live cron preview; Custom last; new jobs default to Daily 09:00; keystroke-clamp + mid-typing-hide bugs fixed. New i18n keys across all 14 locales. Full gate: Codex SAFE, Fable SHIP-UX, suite 12160/0, browser clean.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-06 01:35:55 +00:00
nesquena-hermes
13ecbd79fb review: merge #5554 (2e27ff64) 2026-07-06 00:38:30 +00:00
nesquena-hermes
e6b2ac9119 review: merge #5506 (0c1bdce0) 2026-07-06 00:16:29 +00:00
nesquena-hermes
c2e9277c46 release: reuse the rich model picker in Preferences (#5502)
Generalizes renderModelDropdown() to serve both the composer and Preferences pickers (backward-compatible defaults keep the composer identical). Folded in Fable UX fixes: label->chip association, downward shadow for the down-opening settings picker, and mobile keyboard-pop suppression across all 3 render paths (open/internal-filter/refresh) with focus preserved while typing. Full gate (4 rounds): Codex SAFE, Fable SHIP-UX, suite 12153/0, browser clean; composer picker verified unchanged.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-05 23:22:31 +00:00
Rod Boev
9f9ebc9746 fix(#5583): keep mobile nav actions visible 2026-07-05 18:40:41 -04:00
nesquena-hermes
d5dae70c43 review: merge #5502 (087c694c) 2026-07-05 22:35:58 +00:00
nesquena-hermes
eef981d3dc release: copyable workspace relative paths (#5548)
Adds 'Copy relative path' to the workspace file-tree context menu + preview header. Folded in gate fixes: preview-header button folds to icon-only on a narrow pane via @container rightpanel (not viewport @media), with data-i18n-title/aria-label so the tooltip+accessible name localize. Full gate: Codex SAFE, Fable SHIP-UX, suite 12147/0, browser clean.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-05 22:26:31 +00:00
nesquena-hermes
f53bfb6c97 review: merge #5548 (bac619e767) 2026-07-05 21:57:42 +00:00
Rod Boev
00a6952191 test(#5619): carry config path through quota helper state 2026-07-05 17:24:41 -04:00
Rod Boev
b7234ee72a test(#5619): pin named custom helper to active config path 2026-07-05 17:18:07 -04:00
nesquena-hermes
76639b34f0 release: compression-recovery action — one-click focused continuation on compression_exhausted (#5538)
Folds in gate fixes: map the stale-card 409 to a neutral info toast + retire the card (finally-guard so it stays retired), + consumed-state CSS. Full gate: Codex SAFE, Fable SHIP-UX, suite 12140/0, browser-smoke clean.

Co-authored-by: Frank Song <franksong2702@gmail.com>
2026-07-05 21:14:53 +00:00
Rod Boev
05e14b6339 test(#5619): pin same-path model helper fixtures 2026-07-05 17:13:07 -04:00
Rod Boev
29e9a27299 fix(#5619): preserve same-path cfg overrides on reload 2026-07-05 17:08:20 -04:00
Rod Boev
17ee8b5edc test(#5619): pin codex helper to the active config path 2026-07-05 17:01:39 -04:00
Rod Boev
1124fd882a fix(#5619): reload model payloads on profile switch 2026-07-05 16:44:33 -04:00
Rod Boev
c83ccf069a fix(#5641): keep table boundary scoped to spaces 2026-07-05 16:39:54 -04:00
Rod Boev
2f815f040c fix(#5619): clear stale cfg overrides on profile switch 2026-07-05 16:33:01 -04:00
Rod Boev
1075d6e3a1 fix(#5641): tolerate edge whitespace in markdown table parsing 2026-07-05 16:25:59 -04:00
Rod Boev
4c3a4e011e fix(#5619): reload config cache on profile switch 2026-07-05 16:22:35 -04:00
Frank Song
d1ed4a0b49 Merge latest master into compression recovery action
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-06 01:44:58 +08:00
nesquena-hermes
74d807a07c stage: merge #5638 (f657014aa3) 2026-07-05 17:32:22 +00:00
nesquena-hermes
c0b0321081 stage: merge #5635 (7811b13f49) 2026-07-05 17:32:22 +00:00
nesquena-hermes
2fe1976eac stage: merge #5589 (cc7896cfab) 2026-07-05 17:10:06 +00:00
nesquena-hermes
dc4c31ee1b stage: merge #5549 (0ec29b14ec) 2026-07-05 17:10:06 +00:00
nesquena-hermes
278e506468 stage: merge #5537 (0fd954fe78) 2026-07-05 17:10:06 +00:00
Anthony Ruiz
5901ed68e1 fix(steer): scope upload UX to owner session 2026-07-05 16:02:54 +00:00
allenliang2022
f657014aa3 fix(webui): stop mobile scroll jump-back — scope content-visibility + hold unpinned position (#5637)
Two linked causes of the mobile scroll jump-back, both confirmed by the
maintainer against origin/master in #5637.

Primary (CSS): the @media (pointer: coarse) block applied a flat
`content-visibility: auto; contain-intrinsic-size: auto 1px` to every .msg-row.
Off-screen assistant rows can be multi-thousand px (tool-result / long answers),
so a 1px reservation collapses their off-screen height; scrollHeight lurches down
by tens of thousands of px on a long transcript, the browser force-clamps
scrollTop, and the viewport is thrown to the top. A flat intrinsic-size cannot
both reserve tall rows and keep scrollHeight stable for iOS flick momentum (why
1px was chosen), so content-visibility is scoped to the short, size-predictable
user rows; assistant rows keep real rendered height. Mirrors the existing
#liveAssistantTurn opt-out pattern.

Secondary (JS): _restoreMessageScrollSnapshotSameFrame fell back to an absolute
snapshot.top when the semantic anchor restore failed. During streaming the live
activity-scene refresh fires this every tick; for a reader scrolled up into
history (unpinned), snapping to a stale absolute top nudges the viewport backward
by an amount that grows with scrollHeight. Now: hold position (no scrollTop write)
for the unpinned/anchor-failed case and let the browser's own scroll anchoring
keep the reader put. Pinned/near-bottom readers keep the tail-relative restore.

Adds tests/test_issue5637_mobile_scroll_clamp_jumpback.py (mutation-checked:
reverting either fix fails the matching assertion).
2026-07-05 22:52:59 +08:00
allenliang2022
7811b13f49 test(webui): harden extractFunc to skip braces in string/regex/comment literals
Address greptile P2 on the vscroll recycled-anchor test harness: the
brace-counting function extractor desynced on a bare { or } inside a string,
template, regex, or comment within the extracted function body, silently
truncating the extract. Skip those spans so brace matching runs on real code
structure only, and add a regression test with a function whose string/regex/
comment literals contain unbalanced-looking braces.

Verified: the naive extractor fails all four tests (the compensation function
now contains querySelector attribute-selector strings that desync it); the
hardened extractor passes.
2026-07-05 20:14:38 +08:00
allenliang2022
ad1843f31b fix(webui): recover virtual-scroll compensation when the anchor row is recycled
_compensateScrollForMeasurementDelta re-found its viewport anchor only by
rawIdx and bailed with a bare 'if(!row) return' when that row was recycled
out of the render window. On long sessions containing multi-thousand-px
turns, a large scroll delta recycles the anchor row, so the estimated->measured
topPad swap (a scrollHeight change of tens of thousands of px) hit scrollTop
uncompensated and threw the viewport to the top -- a mobile scroll jump-back
that did not reproduce on desktop (where the anchor row stays rendered).

Fall back when the rawIdx row is gone: (1) re-find the same row by its stable
data-session-msg-idx and compensate by its measured offset delta; (2) if that
row is also unrendered, compensate by the top-spacer (topPad) height delta
captured before the re-render. _captureMessageViewportAnchor now records
topPadBefore so the fallback has a reference.

Adds tests/test_issue4346_vscroll_recycled_anchor_jumpback.py (Node-harness
behavioral tests). All fail on the pre-fix bare-return and pass on the fix;
existing vscroll suites stay green.

Relates to #4346.
2026-07-05 19:53:47 +08:00
Frank Song
322e5a44fa Merge latest master into compression recovery action
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-05 17:23:05 +08:00
Frank Song
90b38cf1fe Merge latest master into compression recovery action
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-05 17:02:55 +08:00
nesquena-hermes
1165bd9ca2 fix(#5626): ignore inactive compacted state-db rows in context replay
get_state_db_session_messages() returned archived (active=0) rows that in-place
compaction had marked inactive. WebUI reconciliation feeds this reader straight
into the next model-facing context (reconciled_state_db_messages_for_session,
prefer_context=True), so those archived rows got pulled back in — resurrecting
pre-compaction history and making every later turn re-trigger compression. Now,
WHEN the messages table exposes an `active` column, inactive rows are excluded by
default (`AND (active IS NULL OR active != 0)` — NULL/legacy rows preserved), with
an `include_inactive=True` escape hatch for explicit recovery/audit callers.
Schemas without the column are unaffected (guarded by `'active' in available`).

Contributor stage; ungated on arrival. Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
2026-07-05 08:59:21 +00:00
nesquena-hermes
4c9690bf59 fix(#5588): reset memoized password-hash cache around every test (isolation flake)
tests/test_issue803.py::TestProfileCookieHelpers failed when run after
tests/test_session_static_assets.py (passed in isolation). Root cause:
api.auth.get_password_hash() memoizes the resolved hash process-wide
(_AUTH_HASH_CACHE / _AUTH_HASH_COMPUTED) and is NOT keyed on the
HERMES_WEBUI_PASSWORD env var. test_session_static_auth_exemption sets that env
var + populates the cache; monkeypatch pops the env on teardown but the cache
stays populated, so is_auth_enabled() reads stale True and build_profile_cookie
raises "requires a request handler when auth is enabled".

Fix: an autouse conftest fixture (_reset_password_hash_cache) that invalidates the
cache before AND after every test — matching the existing _isolate_hermes_config_path
/ _restore_profile_home_globals autouse-reset convention. Fixes the whole class
regardless of which test sets the password; no-op when auth is off. Adds a
direct regression test (cache a password, pop env, assert auth reads disabled +
build_profile_cookie doesn't raise). Paired-run repro now passes.
2026-07-05 08:35:14 +00:00
Frank Song
0b339a0e8a Merge latest master into compression recovery action
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-05 16:22:50 +08:00
nesquena-hermes
dbc2345fea fix(#5571): keep fork children out of compression stitch
_webui_sidecar_lineage_messages_for_display stitches a session's pre-compression
snapshot parents for full-transcript display. A cleared fork child could resurrect
its ORIGINAL (non-fork) parent's transcript when that parent was later compressed.
Now, when the root session is itself a fork (session_source=='fork'), the stitch
walk allows fork-sourced snapshot parents but stops at the first non-fork
ancestor — so a compressed fork continuation still shows its own pre-compression
turns, while an ordinary fork child stays isolated from the original parent.
Additive guard (one extra break condition); non-fork lineage display unchanged.

Contributor stage; gate-pass. Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-05 08:07:38 +00:00