Commit graph

3938 commits

Author SHA1 Message Date
Mohamed Habib
b42b17fbbf fix: address transparent stream review findings 2026-06-12 11:10:43 +03:00
Mohamed Habib
7359f85c64 fix: align transparent thinking event header 2026-06-12 09:05:22 +03:00
Mohamed Habib
306a148fbf merge: sync transparent stream branch with master 2026-06-12 05:09:01 +03:00
Mohamed Habib
fcfaa4686a feat: add transparent stream activity display 2026-06-12 04:45:41 +03:00
nesquena-hermes
c275db0970
Merge pull request #4019 from nesquena/stage-md-fixes
Release MD — v0.51.365 — lineage-segment open + reasoning chip fixes (#4009, #4015)
2026-06-11 16:01:18 -07:00
nesquena-hermes
fbd7f82478 docs(changelog): stamp #4009 + #4015 fix batch as v0.51.365 (Release MD) 2026-06-11 22:55:04 +00:00
nesquena-hermes
ff3de220d8 Merge #4015 — keep reasoning effort status on the active session model (#3958) 2026-06-11 22:48:36 +00:00
nesquena-hermes
8a849a1bb8 Merge #4009 — open clicked lineage segments without sid rewrite (#4003) 2026-06-11 22:48:36 +00:00
Rod Boev
a82eb1622f fix(#3958): keep reasoning effort status on the active session model 2026-06-11 18:46:17 -04:00
nesquena-hermes
b2482913e9
Merge pull request #4014 from nesquena/stage-3993
Release MC — v0.51.364 — self-heal stuck session loads (#3993)
2026-06-11 15:45:43 -07:00
nesquena-hermes
cb93a8014e docs(changelog): stamp #3993 session-load self-heal as v0.51.364 (Release MC) 2026-06-11 22:43:07 +00:00
nesquena-hermes
952ac146e0 test(#3993): re-anchor _load_session_error_block past the new stale-load guard
The stale-load guard (#3993 Codex race fix) adds an early  at the top
of the loadSession catch block; the test helper anchored  on the first
 after the catch, which now truncated at the guard. Anchor the body
extraction past the guard (from ) so it captures the 404/non-404
self-heal body as before. Behavior unchanged.
2026-06-11 22:37:09 +00:00
nesquena-hermes
8e7d3b086d fix(#3993): add stale-load guard before self-heal so a superseded boot load can't wipe a healthy session (Codex CORE race)
currentSid is snapshotted before the awaited /api/session fetch; if the user
clicks a healthy session while a boot-time restore is in flight and that boot
load then fails non-404, _clearStuckSessionOnBoot(sid, null) would wipe the
healthy session's localStorage/URL. Guard the catch block on
_loadingSessionId !== sid (a newer load superseded this one) — re-arm the active
stream and bail before any self-heal/DOM mutation. Protects both the non-404 and
404 inline self-heal paths. + regression test.
2026-06-11 22:27:01 +00:00
nesquena-hermes
7e341a0d70 test(#3993): cover _clearStuckSessionOnBoot self-heal guards (clears on boot-failure, preserves a live session) 2026-06-11 22:20:57 +00:00
nesquena-hermes
63c62194a1 Merge #3993 — self-heal stuck session loads on non-404 failures 2026-06-11 22:19:14 +00:00
nesquena-hermes
8669b6b8b5
Merge pull request #4013 from nesquena/stage-3994
Release MB — v0.51.363 — materialize CLI sessions on rename/move/update (#3994, fixes #3985)
2026-06-11 14:54:06 -07:00
nesquena-hermes
1e61aea10e docs(changelog): stamp #3994 materialize-CLI-sessions fix as v0.51.363 (Release MB) 2026-06-11 21:51:29 +00:00
nesquena-hermes
7a5c3bc374 fix(#3994): scope happy-path guard to read_only flag; complete #1436 mock stub
Refined the Codex-CORE fix: the happy-path (already-stored session) guard rejects
only an explicit read_only flag — a stored messaging session already owns its
sidecar, so the messaging-fork risk is specific to the materialize FALLBACK
(which creates a sidecar) where the _is_messaging_session_record check stays.
Also completed test_issue1436's _stub_session MagicMock (read_only=False,
_loaded_metadata_only=False) — bare MagicMock auto-attrs were truthy, tripping
the new read-only guard + _ensure_full_session_before_mutation reload.
2026-06-11 21:45:36 +00:00
nesquena-hermes
6f41fe7490 fix(#3994): reject read-only + messaging sessions on BOTH happy-path and materialize fallback (Codex CORE x2)
Codex caught two data-integrity gaps in the contributor's guard: (1) the
get_session() happy path returned a stored session without checking read_only/
messaging, so an already-imported read-only session could be mutated via
rename/update/move; (2) the fallback only checked cli_meta.read_only, but agent
rows normalize messaging sources WITHOUT setting read_only — materializing a
writable sidecar for a state.db-owned messaging session forks its title/state.
Now reject getattr(s,'read_only') OR _is_messaging_session_record on the happy
path, and cli_meta.read_only OR _is_messaging_session_record(cli_meta) in the
fallback. Replaced the messaging-stub test with 3 regression tests (stored
read-only, stored messaging, messaging cli_meta without read_only flag).
2026-06-11 21:41:51 +00:00
nesquena-hermes
e934fdde70 test(#3994): cover _get_or_materialize_session (in-store, missing, read-only-403, CLI-import, messaging-stub) 2026-06-11 21:36:28 +00:00
nesquena-hermes
89fbc898f3 Merge #3994 — materialize CLI sessions on rename/move/update (#3985) 2026-06-11 21:35:13 +00:00
nesquena-hermes
ca2538f43c
Merge pull request #4012 from nesquena/stage-4008
Release MA — v0.51.362 — malformed providers config no longer crashes (#4008, fixes #3979)
2026-06-11 14:17:46 -07:00
nesquena-hermes
951abb7753 docs(changelog): stamp #4008 malformed-providers-config crash fix as v0.51.362 (Release MA) 2026-06-11 21:15:11 +00:00
nesquena-hermes
c572e63c50 Merge #4008 — treat malformed providers config as unconfigured (#3979) 2026-06-11 21:08:43 +00:00
Rod Boev
4dc4786aae fix(#3979): treat malformed providers config as unconfigured 2026-06-11 16:55:14 -04:00
Rod Boev
749f97144b fix(#4003): open clicked lineage segments without sid rewrite 2026-06-11 16:28:33 -04:00
nesquena-hermes
a023650330
Merge pull request #4007 from nesquena/stage-b1-cookie-pytest
Release LZ — v0.51.361 — configurable session cookie name (#3981)
2026-06-11 13:24:36 -07:00
nesquena-hermes
0ec59de3ac docs(changelog): stamp #3981 configurable cookie name as v0.51.361 (Release LZ) 2026-06-11 20:21:37 +00:00
nesquena-hermes
de7c7f98ea Merge #3981 — configurable session cookie name via env var 2026-06-11 20:15:18 +00:00
nesquena-hermes
b1236bf3f9
Merge pull request #4004 from nesquena/stage-3996
Release LY — v0.51.360 — close idle SSE on hidden tabs (#3996, fixes #3992)
2026-06-11 12:37:09 -07:00
nesquena-hermes
6f410a81b5 docs(changelog): stamp #3996 SSE-visibility fix as v0.51.360 (Release LY) 2026-06-11 19:33:44 +00:00
nesquena-hermes
2cbae63a8d fix(#3996): preserve pending sid on hidden-tab skip so a bg-tab session reopens (Codex CORE)
If startSessionStream(sid) runs while the tab is ALREADY hidden (session loaded/
restored in a background tab), it set _sessionStreamSessionId then returned at the
hidden-skip WITHOUT recording _sessionStreamHiddenSid — so the visibility handler
(which reopens only from the holder) never reattached, silently dropping
bg_task_complete/server_turn_started until the next explicit startSessionStream.
Set _sessionStreamHiddenSid = sid on the hidden-skip return. Adds a regression test.
2026-06-11 19:17:11 +00:00
nesquena-hermes
8a22540df4 test(#3996): cover SSE visibility hooks + per-session reopen-on-re-show regression 2026-06-11 19:13:03 +00:00
nesquena-hermes
5fdfb70bce fix(#3996): reopen per-session SSE on tab re-show (reopen-bug fix)
#3996 closes the gateway + per-session SSE on hidden tabs to free HTTP/1.1
connection-pool slots (#3992). But the per-session-stream visibility hook
guarded its reopen on _sessionStreamSessionId, which stopSessionStream() nulls —
so the per-session SSE closed on hide but never reopened on re-show (regressing
live-stream reattach). Capture the id into a dedicated _sessionStreamHiddenSid
before the close and reopen from it on visibility return. Gateway hook (sessions.js)
was already correct (reopens unconditionally + re-guards internally).
2026-06-11 19:10:38 +00:00
nesquena-hermes
202d6df310 Merge #3996 — close idle SSE on hidden tabs 2026-06-11 19:08:59 +00:00
John Torcivia
8eadfb048f fix(sessions): remove self-heal from if(!data) 401 path
On 401, api() redirects to /login and returns undefined. The browser
navigates away immediately, so this code rarely runs. But even if it
did, clearing localStorage on transient auth expiry is wrong — it wipes
the saved session id and sends users to empty state after re-login.

Keep the self-heal in the catch-block else branch for non-401, non-404
errors (400/403/500/network) which genuinely trap the user on recurring
boot failures. 401 has no such property because the redirect already
breaks the retry loop.

Greptile: 'Keep the if (!data) early-return clearing-free (its old
behavior), and only call _clearStuckSessionOnBoot() from the else
branch of the catch.'

(#4028 follow-up)
2026-06-11 14:54:42 -04:00
John Torcivia
3bf3d6004e fix(sessions): remove unreachable e.status===401 branch in loadSession catch
The api() function returns undefined on 401 (redirects to login) rather
than throwing. So the 401 path exits via the if(!data) guard at line ~988,
not through the catch block — the e.status===401 branch inside the catch
is dead code (#4028 follow-up).
2026-06-11 14:52:25 -04:00
John Torcivia
cea9a749d5 fix(sessions): tighten _clearStuckSessionOnBoot guard to !currentSid only
Greptile flagged that currentSid===sid also fires on same-session
force-reloads (e.g. background poll). If the server returns a transient
500 or network error, currentSid===sid is true — the helper wipes
hermes-webui-session from localStorage and resets the URL, even though
the session still exists on the server.

Limit the self-heal to boot-time failures only (!currentSid), where the
stored session ID is definitely stale. When currentSid is set (already
viewing a session), a non-404 failure could be transient and wiping
localStorage is unnecessarily destructive.

The 404 inline self-heal (line 932) already uses this tighter guard,
so this brings the non-404 path into alignment.
2026-06-11 14:49:32 -04:00
Dima Diall
832ab09324 fix(visibility): close gateway/session SSE on hidden tabs to prevent connection pool exhaustion (#3992) 2026-06-11 18:10:29 +01:00
Dima Diall
46f87028c2 fix(routes): materialize CLI sessions on rename/move/update (fixes #3985)
When a session exists in Hermes Agent state.db but has no WebUI sidecar
(SESSION_DIR/{sid}.json), mutation routes (rename, move, update) would
return 404 "Session not found" despite the session appearing in the sidebar.

This mirrors the existing fallback in /api/session/archive:
- Try get_session() first (WebUI store)
- On KeyError, look up CLI metadata via _lookup_cli_session_metadata()
- For messaging/Claude Code (read_only): return 403 instead of silent 404
- For regular CLI sessions: import_cli_session() to materialize sidecar
- Preserve source_tag/raw_source/session_source/etc. for lineage

Routes updated:
- /api/session/rename
- /api/session/update (workspace switch)
- /api/session/move

Refs: #3746 (same class: session discovery vs mutation mismatch),
#3915 (session store empty but data exists in Agent store)
2026-06-11 17:06:16 +01:00
John Torcivia
4193c38b61
Merge pull request #1 from syntheticgio/fix/session-load-failure-self-heal
fix: self-heal on non-404 session load failures
2026-06-11 11:44:24 -04:00
John Torcivia
906c2ef9fd Merge remote-tracking branch 'origin/master' into fix/session-load-failure-self-heal
# Conflicts:
#	static/sessions.js
2026-06-11 11:43:35 -04:00
John Torcivia
3bca16fdc2 fix: self-heal on non-404 session load failures
When loadSession() fails during boot with a non-404 error (401, 400,
500, network), the session ID stays stuck in localStorage and the URL,
causing repeated failures on every page refresh.

The 404 path already had inline self-heal (clears localStorage + URL).
This extends it to all error cases:

- Added _clearStuckSessionOnBoot() helper to consolidate the self-heal
  logic for non-404 errors.
- On 401 redirect (api() returns undefined): clears the stuck session
  ID and shows a more informative message.
- On other non-404 errors: clears the stuck session ID and shows a
  better error message distinguishing auth failures from other errors.

Preserves the guard that prevents clearing localStorage when clicking
into a *different* dead session while already viewing a healthy one.
2026-06-11 11:35:08 -04:00
gakugaku
d9c35f58dd feat(auth): make session cookie name configurable via env var
Add HERMES_WEBUI_COOKIE_NAME so multiple WebUI instances sharing a
hostname (different ports) can use distinct auth cookie names. Browsers
scope cookies by host, not host+port (RFC 6265), so same-host instances
otherwise trample each other's `hermes_session` cookie and log users out.

- Resolve the cookie name via _resolve_cookie_name(): env > default,
  mirroring the existing _resolve_session_ttl() pattern.
- Keep `hermes_session` as the default for backwards compatibility.
- Validate against the RFC 6265 token grammar; fall back to the default
  with a logged warning on empty or malformed values.
- Cover default, override, empty, invalid, and Set-Cookie paths in
  tests/test_auth_sessions.py::TestCookieNameResolution.
2026-06-11 08:15:35 +00:00
nesquena-hermes
48860418c9
Release LW stage — v0.51.359 (#3962 assistant turn anchor phase 0 scaffold) (#3977)
* feat(anchor): add stable assistant turn phase 0 scaffold

* chore: stamp v0.51.359 — Release LW (assistant turn anchor phase 0 scaffold, #3962)

---------

Co-authored-by: Frank Song <franksong2702@gmail.com>
Co-authored-by: nesquena-hermes <agent@local>
2026-06-10 23:14:18 -07:00
nesquena-hermes
1126e54132
Merge PR #3973 — v0.51.358 first-run password bootstrap hardening (#3964)
Release LV — v0.51.358 (first-run password bootstrap hardening, #3964)
2026-06-10 21:54:09 -07:00
Hermes Agent
f2ef2851d3 Release LV stage — v0.51.358 (#3964 first-run password bootstrap local gate)
#3964 [security] gate first-password bootstrap (_set_password on POST /api/settings
while auth disabled) to local clients — blocks remote unauth first-run ownership.
Uses request-start auth snapshot (auth_enabled_before), so no mid-request TOCTOU.
Self-rebased onto v0.51.357 (8-behind, 3-dot fidelity verified byte-identical).

#3970 (oauth single-flight) DROPPED from this stage: Codex+Opus both caught a
check-then-insert race — _pending_oauth_flow_for releases the lock before the
device-code request + flow insertion, so concurrent unauth starts still spawn
multiple workers (Codex empirically reproduced w/ 2 threads). Returned to author
w/ the atomic per-(provider,home) start-lock fix. Re-gating the (N-1) stage.

Co-authored-by: Hinotoi-agent <Hinotoi-agent@users.noreply.github.com>
2026-06-11 04:39:34 +00:00
nesquena-hermes
5dceb2993c
Merge PR #3963 — v0.51.357 mid-stream flicker tie fix (#3877)
fix(streaming): re-attach preserved live turn on length tie — residual mid-stream flicker (#3877 reopen)
2026-06-10 21:06:28 -07:00
Hermes Agent
4857f928bd docs(changelog): stamp #3877 flicker fix as v0.51.357 (Release LU) 2026-06-11 03:58:00 +00:00
Hermes Agent
0a561f240a chore(streaming): move dataset.sessionId stamp to the whole-turn restore paths only (Opus/Codex nit)
Both reviewers noted the _preservedLiveTurn.dataset.sessionId stamp was a dead
write in the segment-swap path (only the segment enters the DOM there; the
rebuilt turn was already stamped at build time). Move the stamp into the two
whole-turn paths where the preserved turn actually enters the DOM. No behavior
change; removes the dead write.
2026-06-11 03:20:17 +00:00