Commit graph

4856 commits

Author SHA1 Message Date
nesquena-hermes
fa3d612a4e
Merge pull request #4764 from nesquena/stage-4451-release
Release v0.51.601 — Release VH (sidebar lineage running/unread state when continuation hidden, #4451)
2026-06-22 23:21:45 -07:00
nesquena-hermes
c7034655b6 Release v0.51.601 — Release VH (sidebar lineage running/unread state when continuation hidden, #4451) 2026-06-23 06:17:44 +00:00
nesquena-hermes
c8ccfa93b1 stage #4451 (santastabber): bubble hidden lineage stream state
# Conflicts:
#	CHANGELOG.md
2026-06-23 06:06:23 +00:00
nesquena-hermes
34d8470009
Merge pull request #4763 from nesquena/stage-4474-release
Release v0.51.600 — Release VG (suppress footer jitter during virtual-scroll measurement, #4474)
2026-06-22 22:54:00 -07:00
nesquena-hermes
f748854a69 Release v0.51.600 — Release VG (suppress footer jitter during virtual-scroll measurement, #4474) 2026-06-23 05:49:23 +00:00
Rod Boev
c1355f9e6a Clarify the measurement transition guard and make its test precise 2026-06-23 05:48:36 +00:00
Rod Boev
2f7fe9da32 fix(#4346): harden footer jitter cleanup test 2026-06-23 05:48:36 +00:00
Rod Boev
8b74045bf8 fix(#4346): address review findings — classList fakes, .msg-time guard, test precision 2026-06-23 05:48:36 +00:00
Rod Boev
028fb61f3a fix(#4346): suppress footer jitter during virtual scroll measurement re-renders 2026-06-23 05:48:36 +00:00
nesquena-hermes
0953e7ca8e
test(#4740): fix two order/load-dependent full-suite flakes (test-only) (#4762)
* test(#4740): pin HERMES_HOME in readonly-scope pool test to fix order-dependent flake

test_active_request_readonly_scope_blocks_pool_env_seed failed only under
full-suite ordering (passed in isolation). Root cause, captured via an
assertion-time diagnostic: the readonly scope correctly blocks the process-env
credential fallback (branch 2 returns ''), but _has_explicit_pool_credentials
(branch 3) calls read_credential_pool, which falls back to the GLOBAL-root
auth.json via get_default_hermes_root() — and that reads os.environ['HERMES_HOME']
DIRECTLY, not the thread-local/scoped home. Under suite ordering HERMES_HOME
pointed at the shared persistent test state dir, where an earlier test had
persisted an openrouter credential_pool entry (source: env:OPENROUTER_API_KEY)
into auth.json. The global-root fallback found that leaked entry, so
_provider_has_key('openrouter') returned True and the assertion failed.

Fix: monkeypatch.setenv('HERMES_HOME', <tmp base>) so the global-root fallback
resolves to this test's own empty tmp dir with no leaked auth.json. Test-only;
no product change. Verified: passes in isolation AND the target test now passes
under the full suite (the only remaining full-suite reds were unrelated
subprocess-timeout SIGKILLs in test_tls_support under load, which pass on re-run).

* test(#4740): retry config-probe subprocesses in test_tls_support under load

The 3 TestTLSConfigFlag tests spawn a 'python -c' subprocess that imports
api.config (heavyweight) with a 10s timeout. Under the fully parallel full
suite the box can be saturated enough that the import exceeds 10s and the
runner SIGKILLs the child (returncode -9) — a pure resource-contention flake
(passes in isolation). Add _run_config_probe(): 60s timeout + retry on
TimeoutExpired/SIGKILL, returning a real non-zero exit immediately so genuine
failures still surface. Zero-flake-tolerance sweep (#4740).

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-06-22 22:41:42 -07:00
nesquena-hermes
45c8113f8b
Merge pull request #4760 from nesquena/stage-4758-release
Release v0.51.599 — Release VF (dedupe live progress reasoning echoes, #4758)
2026-06-22 22:14:37 -07:00
nesquena-hermes
9795fbac51 Release v0.51.599 — Release VF (dedupe live progress reasoning echoes, #4758) 2026-06-23 05:10:50 +00:00
nesquena-hermes
39eb7c40d2 stage #4758 (franksong2702): dedupe live progress reasoning echoes 2026-06-23 04:54:48 +00:00
Frank Song
a4470bd30c Deduplicate live progress reasoning echoes 2026-06-23 12:51:16 +08:00
nesquena-hermes
529eb51932
Merge pull request #4751 from nesquena/stage-4743-release
Release v0.51.598 — Release VE (preserve scroll across live worklog rebuilds, #4743)
2026-06-22 21:39:12 -07:00
nesquena-hermes
ecc00d56ac Release v0.51.598 — Release VE (preserve scroll across live worklog rebuilds, #4743) 2026-06-23 04:25:25 +00:00
nesquena-hermes
9fb371addd stage #4743 (franksong2702): preserve scroll across live Compact Worklog rebuilds 2026-06-23 04:14:05 +00:00
nesquena-hermes
9570b9bce4
Merge pull request #4744 from nesquena/stage-footer-wrap
Release v0.51.597 — Release VD (message footer wraps instead of overflowing, #4724-followup)
2026-06-22 21:10:14 -07:00
nesquena-hermes
064118d329 fix(ui): wrap message footer on narrow screens instead of overflowing (#4724-followup)
The assistant message footer (model · duration · tokens · cost · cache% · time · actions)
could overflow its row on mobile/narrow panes, pushing the per-message action buttons off
the right edge where they were unreachable. #4724 proposed ellipsing every stat, which
shrank them into unreadable stubs (D…, 98…). This instead lets .msg-foot flex-wrap to a
second line: every stat stays full-size + readable, the actions/time are pinned flex:0 0
auto so they never shrink and stay on-screen, and on wider screens where it already fits
wrap never triggers. Pure CSS. + source-structure regression test.

Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-06-23 03:54:20 +00:00
Frank Song
4bb6fd132f Drop contributor changelog entry 2026-06-23 11:52:08 +08:00
Frank Song
61a7881056 Preserve scroll across live Worklog rebuilds 2026-06-23 11:42:36 +08:00
nesquena-hermes
6a0a215b55
Merge pull request #4739 from nesquena/stage-4729-release
Release v0.51.596 — Release VC (throttle reasoning SSE, #4729)
2026-06-22 20:05:21 -07:00
nesquena-hermes
9d3d6d4847 test(#4729): drop unused 're' import (ruff F401 lint gate) 2026-06-23 03:02:06 +00:00
nesquena-hermes
40cdd88406 Release v0.51.596 — Release VC (throttle reasoning SSE to stop tab freeze, #4729) 2026-06-23 02:56:16 +00:00
nesquena-hermes
8f5ffea4a1 fix(#4729): guaranteed-exit reasoning flush in inner finally (Codex re-gate 2)
Codex re-gate 2: post-run flush is bypassed on exception (jumps to outer except) + retry
paths, so a buffered reasoning tail was lost before apperror there. Added a guarded
_flush_reasoning_buffer() at the top of the inner finally (guaranteed exit for
normal/exception/retry) so the tail always reaches the live Thinking view before teardown.
+ regression asserting the finally flush.

Co-authored-by: wlknight <wlknight@users.noreply.github.com>
2026-06-23 02:44:34 +00:00
nesquena-hermes
fa51e3413d fix(#4729): flush reasoning buffer at real boundaries, not just on_reasoning(None)
Codex re-gate caught a SILENT gap: the tail-flush only ran on on_reasoning(text=None),
but the agent never calls reasoning_callback(None) — so the last sub-100ms coalesced
buffer was silently lost, and a reasoning→tool/output transition stranded the tail.

Added a shared _flush_reasoning_buffer() helper called at every boundary that closes or
reorders the live reasoning stream: on_token (visible output starting), on_tool (tool
boundary), the defensive on_reasoning(None), and right after agent.run_conversation()
returns (terminal catch-all for a turn that ends on reasoning with no trailing token/tool).
Each flush emits the buffered chunk then clears it (since-last-flush delta semantics, so
the frontend's reasoningText += text stays correct). + regression test for all sites.

Co-authored-by: wlknight <wlknight@users.noreply.github.com>
2026-06-23 02:28:52 +00:00
nesquena-hermes
36de84f6b8 test(#4729): pin reasoning-SSE coalescing contract (accumulate+flush+tail, not drop)
wlknight's re-push implemented the coalesce fix-spec from the bounce faithfully
(buffer accumulates deltas, flush emits+clears the buffer, tail flushed on text=None).
The PR shipped no test for it; added a source-structure regression pinning the three
correctness properties so it can't silently regress to the drop-based version that
would lose live reasoning text (frontend appends deltas).

Co-authored-by: wlknight <wlknight@users.noreply.github.com>
2026-06-23 02:14:13 +00:00
nesquena-hermes
eabe1426a4 stage #4729 (wlknight): coalesce-throttle reasoning SSE to ~10Hz (fixes browser tab freeze)
# Conflicts:
#	CHANGELOG.md
2026-06-23 02:12:38 +00:00
nesquena-hermes
81f5ecc82d
Merge pull request #4736 from nesquena/stage-4727-release
Release v0.51.595 — Release VB (TLS handshake no longer stalls accept loop, #4727)
2026-06-22 19:05:36 -07:00
nesquena-hermes
65dcb07805 Release v0.51.595 — Release VB (TLS handshake no longer stalls the accept loop, #4727) 2026-06-23 01:58:47 +00:00
wlknight
b85e2c84ae fix: throttle reasoning SSE events to ~10 Hz using coalescing buffer 2026-06-23 09:48:02 +08:00
nesquena-hermes
a43a93650a stage #4727 (jcarvine): prevent TLS handshake from stalling the accept loop 2026-06-23 01:47:45 +00:00
nesquena-hermes
f1c0ae47f2
Merge pull request #4735 from nesquena/stage-4671-release
Release v0.51.594 — Release VA (profile-switch loading skeletons, #4671/#4662)
2026-06-22 18:43:23 -07:00
nesquena-hermes
12180079ab Release v0.51.594 — Release VA (profile-switch loading skeletons, #4671/#4662 phase 1) 2026-06-23 01:39:03 +00:00
nesquena-hermes
749f95d01c docs(#4671): clarify dead-assignment backstop + add four-guard contract comment (Opus polish)
Opus ship-gate non-blocking observations (no MUST-FIX), applied to lower future-maintainer
risk on the crown-jewel profile-switch surface:
- clarified the belt-and-suspenders _sessionListSkeletonActive=false at the real-paint site
- added a FOUR-GUARD CONTRACT doc-comment at the top of switchToProfile naming the four
  interacting state pieces (embargo / invalidation / skeleton-flag / _wsTreeGen + _switchGen)
  so a future edit touching one without the others doesn't silently reopen a window.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-23 01:38:25 +00:00
nesquena-hermes
710f2c6934 test(#4671): parse full switchToProfile body instead of a fixed char window
Codex re-gate 5: the only remaining finding — my race-guard work grew switchToProfile
past the 6500-char window (applyBotName now at offset ~6609). Instead of bumping the
window again (it'll drift), bound the slice on the next top-level function
(openProfileCreate) so the test reads the whole body and can't break on growth.
No race/logic findings this pass — the profile-switch races are converged clean.
2026-06-23 01:27:11 +00:00
nesquena-hermes
1b0c64a604 fix(#4671): embargo also guards the fetch-failure path + setup moved inside try (Codex re-gate 4)
Two more edge cases from re-gate 4:
(1) SILENT: a mid-switch /api/sessions that FAILS bypassed the embargo (the drop-check
was only on the success path), so its catch cleared the skeleton + rendered old-profile
cache. Added 'if(_profileSwitchListEmbargo) return;' after the gen guard in the catch too.
(2) CORE: the embargo was set BEFORE the try, so a throw in the synchronous setup could
leak it (the guarded finally only covered the try). Moved the invalidate/embargo/skeleton/
ws-gen setup INSIDE the try so the _switchGen-guarded finally always lifts the embargo.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-23 01:18:58 +00:00
nesquena-hermes
ce0ef36ab1 fix(#4671): embargo session-list renders during the switch window (Codex re-gate 3)
Codex re-gate 3 found a third clobber variant: point-in-time invalidation isn't enough
because a renderSessionList() can START after the skeleton shows but BEFORE
/api/profile/switch returns. Since the new-profile cookie is only set by the switch
response, that GET fetches the OLD profile's rows, passes the (now-current) generation
guard, and clobbers the skeleton.

Fix: a profile-switch session-list EMBARGO. _setProfileSwitchListEmbargo(true) before
showSessionListSkeleton(); _runRenderSessionListRefresh drops ALL payloads (after the
gen guard, before apply) while embargoed; the switch lifts it immediately before its own
authoritative renderSessionList() (both branches), in the failure-restore, and in a
_switchGen-guarded finally as a safety net so it can't freeze the sidebar on an
early-return/throw. + regression test.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-23 01:11:24 +00:00
nesquena-hermes
0a64d7d7c6 fix(#4671): invalidate in-flight session-list renders at switch start (Codex re-gate 2)
Codex re-gate 2 found a remaining CORE clobber variant: a renderSessionList() already
in flight BEFORE the switch (background poll fetching the OLD profile's /api/sessions)
would resolve after the skeleton appears but before the switch's own renderSessionList()
bumped _renderSessionListGen — so it passed the generation guard, cleared the skeleton
flag in _applySessionListPayload, and painted stale rows.

Fix: _invalidateSessionListRenders() bumps _renderSessionListGen + clears
_pendingSessionListPayload / _renderSessionListQueuedRequest, called at switch start
BEFORE showSessionListSkeleton(). Any outstanding pre-switch response is now stale
(its captured _gen < current) and discarded by the existing guard; deferred/queued
applies are dropped. + regression test asserting the helper exists and runs before the
skeleton.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-23 00:56:30 +00:00
nesquena-hermes
66786143a8 fix(#4671): close 2 profile-switch render races via skeleton-flag + ws generation token
Reworked from the Codex re-gate (rebased onto fresh master so #4730's gateway-poll
work is preserved, not reverted). Two CORE races + a test-window break:

(1) Skeleton-clobber (sessions.js): renderSessionListFromCache() now bails while
_sessionListSkeletonActive — _allSessions still holds the PREVIOUS profile until
/api/sessions resolves, so a gateway-poll/SSE/timer/cache render mid-switch would
repaint the wrong profile's rows over the skeleton. _applySessionListPayload clears
the flag right before painting (fresh data in hand; superseded responses already
discarded by the generation guard); the fetch-failure path and switch-failure catch
clear it too so it can't strand. NO signature change (kept renderSessionListFromCache()
and _applySessionListPayload(sessData,projData) so the ~10 static tests that anchor on
those markers stay green).

(2) Workspace wrong-profile tree (workspace.js + panels.js): an empty-session switch
reuses the same session_id, so loadDir()'s id guard can't reject a stale pre-switch
/api/list. Added _wsTreeGen generation token + bumpWorkspaceTreeGen(), bumped
UNCONDITIONALLY at switch start (even panel-closed, since loadDir('.') still runs),
captured in loadDir() and re-checked after both awaited /api/list points.

(3) test_issue1116 proximity window 5000->6500 (applyBotName now ~char 5618, still fires).

+ regression tests: skeleton-flag bail/clear lifecycle, ws generation token, failure
restore clears flag; updated #1785 stale-session guard test for the compound condition.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-23 00:46:14 +00:00
nesquena-hermes
a8805bb658 stage #4671 (nesquena-hermes, nesquena+rodboev reviewed): profile-switch loading skeletons (#4662 phase 1)
# Conflicts:
#	CHANGELOG.md
2026-06-23 00:17:17 +00:00
nesquena-hermes
3d2cd8bce5
Merge pull request #4734 from nesquena/stage-4730b-release
Release v0.51.593 — Release UZ (gateway-poll listener cleanup, #4730 follow-up)
2026-06-22 16:53:22 -07:00
nesquena-hermes
a32c3a3f1e Release v0.51.593 — Release UZ (gateway-poll visibilitychange listener cleanup, #4730 follow-up)
Co-authored-by: akrhin <akrhin@users.noreply.github.com>
2026-06-22 23:50:09 +00:00
nesquena-hermes
9430bda43d
Merge pull request #4733 from nesquena/stage-4730-release
Release v0.51.592 — Release UY (gateway-poll skip + smd re-init, #4730)
2026-06-22 16:46:18 -07:00
nesquena-hermes
938ae25697 Release v0.51.592 — Release UY (skip gateway poll when hidden/streaming + smd re-init, #4730) 2026-06-22 23:42:30 +00:00
nesquena-hermes
20c70c3505 fix(#4730): clear assistantBody before smd re-init to prevent duplicate live text
Codex gate found: when window.smd becomes available after a segment already
rendered through the renderMd() fallback, _smdNewParser resets _smdWrittenText
to '' but does NOT clear the element, so the following _smdWrite(displayText)
appends the full accumulated segment on top of the existing fallback render —
duplicating the live assistant text. Clear assistantBody.innerHTML before re-init.

Co-authored-by: akrhin <akrhin@users.noreply.github.com>
2026-06-22 23:27:23 +00:00
nesquena-hermes
e571590c9b stage #4730 (akrhin): skip gateway poll when hidden/streaming + smd re-init before innerHTML fallback 2026-06-22 23:22:23 +00:00
sintez
f2abe50b7c perf: skip gateway poll when hidden/streaming, re-init smd parser before innerHTML fallback 2026-06-23 02:13:34 +03:00
Hermes Agent
8fdbb23bc9 Fix TLS accept loop stalls 2026-06-23 00:31:42 +02:00
nesquena-hermes
3bf0e194f5
Merge pull request #4726 from nesquena/stage-4721-release
Release v0.51.591 — Release UX (stop transcript jumping to first message, #4721/#4720)
2026-06-22 14:31:35 -07:00