Commit Graph

4450 Commits

Author SHA1 Message Date
Rod Boev d85979cd69 fix(#4373): mark process-wakeup turns as synthetic and filter from visible transcript 2026-06-18 15:47:06 -04:00
nesquena-hermes 6f233d3a20 Merge pull request #4431 from nesquena/release/stage-4419b
Release v0.51.496 — RF (compact provider quota chip on narrow composer, #4419)
v0.51.496
2026-06-18 11:56:48 -07:00
nesquena-hermes 3cc4c859cf release: v0.51.496 — compact provider quota chip on narrow composer (#4419)
Co-authored-by: bergeouss <bergeouss@users.noreply.github.com>

Closes #4419
Closes #4358
2026-06-18 18:48:11 +00:00
nesquena-hermes 6f699642d5 Merge pull request #4429 from nesquena/release/stage-4422
Release v0.51.495 — RE (suppress ERR_ABORTED console noise on SSE close, #4422)
v0.51.495
2026-06-18 11:41:17 -07:00
nesquena-hermes df16a2eb10 release: v0.51.495 — guard EventSource.close() to suppress ERR_ABORTED noise (#4422)
Co-authored-by: bergeouss <bergeouss@users.noreply.github.com>

Closes #4422
2026-06-18 18:23:41 +00:00
nesquena-hermes 348078a617 Merge commit 'refs/pull/4422/head' of github.com:nesquena/hermes-webui into release/stage-4422 2026-06-18 18:22:37 +00:00
nesquena-hermes 1a6fcc115b Merge pull request #4427 from nesquena/release/stage-4412c
Release v0.51.494 — RD (TLS-aware launcher health probes, #4412)
v0.51.494
2026-06-18 11:20:44 -07:00
nesquena-hermes a308fdd5e8 release: v0.51.494 — TLS-aware launcher health probes (#4412)
Absorbs #4412 (tomas-forgac) rebased onto fresh master, plus two
Codex-regression-gate fixes: (1) use the scheme that actually answered the
probe for the ready/already-up URL + browser-open, (2) honor an explicit
HERMES_WEBUI_HEALTH_URL override in the WSL autostart probe.

Co-authored-by: tomas-forgac <tomas-forgac@users.noreply.github.com>

Closes #4412
2026-06-18 18:13:46 +00:00
nesquena-hermes 67f81b60a2 Merge pull request #4426 from nesquena/release/stage-4417
Release v0.51.493 — RC (ignore malformed background-process wakeup events, #4417)
v0.51.493
2026-06-18 11:12:06 -07:00
nesquena-hermes 18d5df74a9 release: v0.51.493 — ignore malformed background-process wakeup events (#4417) 2026-06-18 18:04:28 +00:00
nesquena-hermes d228195da9 merge #4417: ignore malformed process wakeup events (resolve CHANGELOG) 2026-06-18 17:56:40 +00:00
nesquena-hermes 8bd0b38abe Merge pull request #4425 from nesquena/release/stage-4418
Release v0.51.492 — RB (large context window preserved on session reload, #4248)
v0.51.492
2026-06-18 10:46:13 -07:00
nesquena-hermes c7fc114c85 release: v0.51.492 — preserve large context window on session reload (#4248, #4418) 2026-06-18 17:39:50 +00:00
nesquena-hermes 561843ee9d fix(#4248): normalize slash-qualified model id in reload identity check
Codex regression gate found that _session_model_identity_matches normalized
only the @provider:model form, not provider/model (slash). A session stored as
'deepseek/deepseek-v4-1m' resolving to bare 'deepseek-v4-1m' was treated as a
model change -> the 256k fallback bypassed the accept-guard and clobbered the
persisted 1M window (the same #4248 bug on slash-qualified IDs). Mirror the
slash-aware split already used by _model_matches_configured_default. Adds a
route-level regression + unit test for the slash shape.

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-18 17:37:52 +00:00
ai-ag2026 352e1893cb test: cover malformed wakeup edge cases 2026-06-18 19:30:24 +02:00
bergeouss d812f70116 fix(#4313): guard EventSource.close() calls to suppress ERR_ABORTED noise
Add readyState !== CLOSED (2) guards to all EventSource.close() calls across
messages.js, sessions.js, panels.js, and terminal.js. Chromium reports
net::ERR_ABORTED when close() is called on an already-closed EventSource,
polluting the console during normal lifecycle transitions (session switch,
panel close, stream teardown).

Key hotspots:
- closeLiveStream() double-close via onerror → _closeSource chain
- _closeSessionEventsSSE, _gatewaySSE.onerror missing try/catch
- _kanbanStopPolling, terminal close handlers

20 close() calls guarded across 4 files, no structural changes to stream
lifecycle.
2026-06-18 17:25:18 +00:00
Frank Song 531c5787c6 fix: refine context reload fallback guard 2026-06-19 00:05:52 +08:00
Frank Song 9f174f6cc3 test: update session context fallback guard 2026-06-18 23:19:58 +08:00
Frank Song f9d6b127e3 fix: preserve large context window on session reload 2026-06-18 23:13:19 +08:00
ai-ag2026 ff0ac7c7a4 fix: ignore malformed process wakeup events 2026-06-18 16:17:27 +02:00
nesquena-hermes bc11cfe6d2 Merge pull request #4409 from nesquena/stage-4405
Release v0.51.491 — RA (rollback restore writes contained to workspace #4405)
v0.51.489 v0.51.490 v0.51.491
2026-06-17 22:15:46 -07:00
nesquena-hermes 330e0672b8 fix(#4405): contain rollback checkpoint restore writes to the workspace
Restore previously used pathname-based shutil.copy2() which follows an in-tree
symlink whose target escapes the workspace. Route restore writes through the
workspace-anchored file helpers so a restored file always lands inside the
workspace tree. +regression test (escaping symlink refused, normal restore ok).

Co-authored-by: hinotoi-agent <paperlantern.agent@gmail.com>
2026-06-18 05:12:49 +00:00
nesquena-hermes f46a447dc3 Merge pull request #4408 from nesquena/stage-4372
Release v0.51.490 — QZ (large plain-text pastes become .md attachments #4372)
2026-06-17 22:12:03 -07:00
nesquena-hermes 299fab5631 feat(#4372): attach large composer text pastes as .md files
Pastes over 4000 chars or 100 lines attach as a timestamped pasted-text-*.md in
the upload tray instead of flooding the composer. Image-paste path unchanged; an
oversize paste that exceeds MAX_UPLOAD_BYTES falls through to a normal inline
paste so nothing is lost.

Co-authored-by: santastabber <204424800+santastabber@users.noreply.github.com>
2026-06-18 05:09:11 +00:00
nesquena-hermes 9f14dd6686 Merge pull request #4407 from nesquena/stage-4402
Release v0.51.489 — QY (outline button no longer collides with scroll control #2124)
2026-06-17 21:58:19 -07:00
nesquena-hermes 4ac9684223 fix(#2124): stop the conversation-outline button colliding with the scroll control
The outline FAB was position:fixed at z-index 9999 and could stack on top of the
scroll-to-bottom button. Anchor it inside the messages shell (position:absolute,
z-index 12) so the two controls don't overlap.

Co-authored-by: Mohamed Habib <Habib1001-m@users.noreply.github.com>
2026-06-18 04:46:50 +00:00
nesquena-hermes d5f9e98252 Merge pull request #4406 from nesquena/stage-4216
Release v0.51.488 — QW (rescue state.db user prompts before newer sidecar tail #4216)
v0.51.488
2026-06-17 21:37:46 -07:00
nesquena-hermes c592ae3acc fix(#4216): insert state.db-only user prompts chronologically in merge_session_messages_append_only
A user prompt that exists only in Hermes state.db (no sidecar copy) and falls
chronologically BEFORE a newer sidecar message tail was being appended after the
tail (or skipped) by merge_session_messages_append_only — losing or mis-ordering
it on load. The merge now inserts such a state.db user message at its correct
chronological position via _insert_state_message_chronologically, with a
role-aware tie-break for equal timestamps. Append-only invariant + dedup
(seen_content_keys) preserved.

Deterministic-ordering guard (maintainer add, addresses Codex review): on an
equal-timestamp collision the insert no longer lands at a slot whose left
neighbour shares the message's role — it advances past the same-role
equal-timestamp run, so an already-answered earlier turn can't be re-ordered
into user,user,assistant. (The agent core merges adjacent users before send, so
this was benign in practice, but the guard keeps the merged transcript correctly
ordered regardless.) +1 same-second multi-turn regression test, toothless-checked.

Co-authored-by: Dennis Soong <dso2ng@gmail.com>
2026-06-18 04:25:11 +00:00
nesquena-hermes cbf6f747b4 Merge pull request #4404 from nesquena/stage-4398
Release v0.51.487 — QX (multi-container Docker: staged source writable #4395)
v0.51.487
2026-06-17 21:19:45 -07:00
nesquena-hermes 60218c302d fix(#4395): make staged hermes-agent source writable after rsync/cp in docker_init
Multi-container Docker deploys died at startup with 'could not create
hermes_agent.egg-info: Permission denied'. docker_init.bash stages the :ro
hermes-agent-src into /tmp/hermes-agent-build to avoid EROFS, but rsync -a/cp -a
preserve mode bits — a source mounted 555 left the staged copy 555, so
setuptools couldn't write egg-info next to the package. chmod -R u+w the staged
tree after the copy (error_exit on failure) so the build dir is genuinely
writable, not just owned by hermeswebui.

Co-authored-by: enihcam <enihcam@users.noreply.github.com>
2026-06-18 04:15:08 +00:00
nesquena-hermes 5ced430628 Merge pull request #4403 from nesquena/stage-4399
Release v0.51.486 — QV (archived cron runs stay hidden on state.db re-projection #4397)
v0.51.486
2026-06-17 20:45:50 -07:00
nesquena-hermes 5c62f849fa fix(#4397): keep archived cron runs hidden on state.db re-projection
Follow-up to #4385/#4388: that fix stopped stale cron sidecars from being
treated as CLI rows, but get_cli_sessions() still re-projected the same cron
run from Hermes state.db with a hardcoded archived: false after all_sessions()
omitted the hidden sidecar — so an archived run reappeared on refresh.

- New _state_projection_sidecar_metadata(sid) helper returns UI-owned title +
  archived flag from the WebUI sidecar (load_metadata_only), degrading
  gracefully to {title:None, archived:False} on any miss.
- Both state.db-projection paths (standard CLI + cron fallback) now use it and
  carry the archived flag onto the projected row (was hardcoded False), so an
  archived cron/tool/API run stays hidden regardless of which path surfaces it.
  Title-preference behavior (#1486 rename persistence) is preserved.

Co-authored-by: TomBanksAU <246584738+TomBanksAU@users.noreply.github.com>
2026-06-18 03:33:44 +00:00
nesquena-hermes f5f882b1dd docs: fairly count absorbed-but-closed contributor PRs (backfill @Isla-Liu trio) (#4401) 2026-06-17 20:08:08 -07:00
nesquena-hermes a6214d0927 Merge pull request #4400 from nesquena/stage-4394
Release v0.51.485 — QU (mobile: 44px hamburger + browser edge-swipe #4394)
v0.51.485
2026-06-17 19:50:29 -07:00
nesquena-hermes ed827248ba feat(#4394): mobile — 44px hamburger tap target + browser edge-swipe sidebar
- Hamburger button: 32x32 -> 44x44 tap target (iOS HIG minimum), SVG icon
  20 -> 24px. flex-shrink:0 preserved; spacer keeps the title centered.
- Edge-swipe-to-open-sidebar: edge zone 28 -> 80px, trigger 72 -> 64px, max
  vertical 48 -> 56px; removes the _isPwaStandalone() gate in
  _onPwaSidebarSwipeStart so the gesture works in a normal mobile browser, not
  only the installed PWA. Still _isDesktopWidth()-blocked, touch/pen-only,
  interactive-target-excluded, and requires horizontal intent (dx>=64 &&
  dx>|dy|*1.5), so scroll/tap won't trigger it.

Known tradeoff (Nathan-approved ship-as-is): in mobile BROWSER the 80px left
edge zone overlaps iOS Safari's back-swipe; degrades gracefully (PWA, the
primary mobile target, has no back gesture).

Verified live at 390x844: hamburger renders 44x44 + tap opens sidebar; a
left-edge swipe opens the sidebar in a non-PWA context. 66 existing mobile/
swipe/titlebar tests pass.

Co-authored-by: kaishi00 <kaishi00@users.noreply.github.com>
2026-06-18 02:40:13 +00:00
nesquena-hermes 265786c2fe Merge pull request #4396 from nesquena/stage-4223
Release v0.51.484 — QT (collapsible paused cron section #4026)
v0.51.484
2026-06-17 19:18:42 -07:00
nesquena-hermes a195c9f589 feat(#4026): collapse paused cron jobs into a collapsible sidebar section
Paused/disabled cron jobs are partitioned out of the active list into a
collapsible 'Paused (N)' <details> section so they don't drown the active
jobs. _cronList stays the single source of truth — only the render is split,
so openCronDetail / _cronNewJobIds / detail-refresh are untouched. Reuses the
already-translated cron_status_paused label (zero new i18n keys). Collapsed
state persisted in localStorage.

Maintainer polish on absorb: wrapped both localStorage read/write in try/catch
(repo convention — a storage throw in private mode must not dump the cron
sidebar into the error banner) + added the CHANGELOG entry.

Co-authored-by: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com>
2026-06-18 02:08:28 +00:00
nesquena-hermes f5efa1f89f Merge pull request #4392 from nesquena/fix-gateway-sync-flake
test: fix recurring gateway_sync StopIteration flake (show_cli_sessions isolation)
2026-06-17 18:33:08 -07:00
nesquena-hermes 1c3b1ecdc9 test(gateway_sync): fix intermittent StopIteration flake (test-isolation root cause)
The A3 flake (test_sessions_response_backfills_imported_messaging_source_metadata
failing with StopIteration on a sharded CI run while passing in isolation +
sequentially) had two compounding causes, both fixed:

1. ROOT CAUSE — show_cli_sessions setting leak across shard-ordered tests.
   33 gateway_sync tests flip show_cli_sessions=True but only ~30 reset it,
   and cleanup_test_sessions didn't reset it. Under pytest-shard ordering a
   test could run with the setting in an unexpected state, so its session was
   occasionally absent from /api/sessions. cleanup_test_sessions now resets
   show_cli_sessions to its default (off) after every test, the same way it
   already resets last_workspace.

2. SYMPTOM — bare next(generator) with no default raised an opaque
   StopIteration (read as a mystery flake) instead of a diagnosable failure
   when the session was absent. The 5 bare next() call sites over /api/sessions
   results now use next(..., None) + an explicit assertion naming the missing
   session, so any future isolation regression fails clearly instead of
   masquerading as a flake.

No production code change; tests + fixture only.
2026-06-18 01:19:20 +00:00
nesquena-hermes f5512aa507 Merge pull request #4389 from nesquena/stage-4346-cluster
Release v0.51.483 — QS (virtual-scroll scroll-jump fixes #4367 + #4368)
v0.51.483
2026-06-17 18:04:14 -07:00
nesquena-hermes f4d014194c fix(#4346): virtual-scroll height + measurement scroll-jump cluster (#4367, #4368)
Two coherent fixes for the #4346 transcript-virtualization scroll-jump
cluster, shipped together (contributor-approved) because both touch
static/ui.js + the same virtualization test file and interact (the
cache-reset path uses the per-role default from #4367). Both only run when
the default-OFF experimental "Virtualize long transcripts" setting is on.

#4367 — per-role virtual scroll height estimates:
  Replaces the flat 140px unmeasured-row estimate with per-role fallbacks
  (user 120 / assistant 160 / tool_call 400 / default 140) via
  _messageVirtualDefaultHeightForRole + _messageVirtualRoleForEntry, so
  tool-call rows (which measure 500-2000px) aren't under-counted before
  measurement. Additive + backward-compat (opts.roleForIdx optional → flat
  default when absent). The two sibling offset calculators route through the
  per-role defaults too.

#4368 — anchor-based scroll compensation during measurement:
  Adds _compensateScrollForMeasurementDelta (capture anchor + scrollTop →
  render → re-pin scrollTop by the anchor row's measured delta; null/missing/
  <2px guards; programmatic-scroll flag managed) wrapping
  renderMessages({preserveScroll:true}) in _scheduleMessageVirtualizedRender,
  plus a 150ms scroll-active guard deferring measurement refresh while the user
  scrolls, and session-switch cleanup of the three scroll-settle globals.

Maintainer note: combining the two required the test harness for
test_clear_height_cache_resets_scroll_settle_globals (#4368) to also eval
_messageVirtualDefaultHeightForRole + define MESSAGE_VIRTUAL_DEFAULT_ROW_HEIGHTS
(#4367), since the combined _clearMessageVirtualHeightCache resets the estimate
via the per-role helper. Both PRs' test sets retained.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-18 00:50:53 +00:00
nesquena-hermes a34bcc16c1 Merge pull request #4388 from nesquena/stage-4386
Release v0.51.482 — QR (archived cron sessions stay hidden #4385)
v0.51.482
2026-06-17 17:46:55 -07:00
nesquena-hermes 15da64f6ba fix(#4385): keep archived cron sessions hidden after list refresh
A stale sidecar with is_cli_session=true could make an archived
cron/tool/API session resurface in the Sessions tab on refresh. The
session loader now treats explicit cron/tool/api source metadata as
non-CLI even when the sidecar claims is_cli_session, and preserves cron
identity when the archive/materialization fallback creates a session.

Co-authored-by: TomBanksAU <246584738+TomBanksAU@users.noreply.github.com>
2026-06-18 00:28:02 +00:00
nesquena-hermes 9a66a18c44 Merge pull request #4382 from nesquena/stage-3628
Release v0.51.481 — QP (gateway start/stop/restart controls #3628)
v0.51.481
2026-06-17 15:18:23 -07:00
nesquena-hermes a41a1bf393 feat(#3628): gateway start/stop/restart controls in Settings → System
Adds WebUI controls to manage the messaging gateway lifecycle without a
terminal. _handle_gateway_lifecycle routes start/stop/restart to the
existing platform-aware `hermes gateway {action}` CLI via a list-argv
subprocess (no shell), scoped to the active profile, with bounded
timeout/error handling and a status-card refresh. Auth + CSRF gated;
profile name charset-validated; stdout/stderr never leaked to the client.

Maintainer hardening (warm-up SHOULD-FIX): added _GATEWAY_ACTION_LOCK, a
server-side single-flight guard. The client disables its button in-flight,
but a scripted authed client could otherwise fire overlapping
start/stop/restart calls and spawn concurrent gateway subprocesses. A
non-blocking acquire now returns 409 on contention (mirrors the self-update
_apply_lock pattern), released in finally. Added a regression test
(toothless-checked: without the lock the 2nd action spawns a subprocess +
returns 200).

Cross-platform: delegates to the mature hermes_cli gateway subcommand
(systemctl/launchctl), pathlib paths, PYTHONUTF8=1 + BROWSER=echo in env.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-17 22:08:33 +00:00
nesquena-hermes 41d51ecbcf Merge pull request #4379 from nesquena/stage-4353
Release v0.51.480 — QO (inline PDF preview renders all pages #4353)
v0.51.480
2026-06-17 14:14:18 -07:00
nesquena-hermes 98a81118a1 fix(#4353): render all PDF pages in a scrollable inline preview
The inline PDF preview rendered only page 1, so multi-page documents
looked clipped. loadPdfInline now renders every page (capped at MAX_PAGES=20
for memory) sequentially into a scrollable .pdf-preview-body, shows the page
count in the header, and appends a "Showing the first N of M pages" notice
(pdf_truncated, 13 locales) when the PDF exceeds the cap.

Robustness: renderPage(i) attaches each canvas only AFTER its render
promise resolves, and a per-page failure is caught and skipped
(`.then(()=>renderPage(i+1)).catch(()=>renderPage(i+1))`) so one corrupt
page can't halt the preview or surface an unhandled rejection. XSS-safe
(esc(fname), t() with numeric args).

Co-authored-by: Gouranga satapathy <29521030+gourangasatapathyvit@users.noreply.github.com>
2026-06-17 20:57:38 +00:00
nesquena-hermes 4f776a12fd Merge pull request #4376 from nesquena/stage-3567
Release v0.51.479 — QN (named context blocks in the composer #2543)
v0.51.479
2026-06-17 13:25:10 -07:00
nesquena-hermes 8246b4aad4 feat(#2543): collect selected chat text as named context blocks
"Reply with selection" no longer appends the selected text straight into
the composer. It now adds a named chip ("Context N") to a row above the
message box (composerSelectionChips). Chips can be renamed (double-click,
esc()-escaped) or removed (x); multiple stack. On send(),
_flushSelectionBlocksToComposer() materializes pending blocks into the
composer as **Name:** + markdown-quoted text, then the normal send path
runs unchanged on composer.value.

Regression-safety:
- _flushSelectionBlocksToComposer early-returns when no blocks are pending,
  so send() is byte-identical for anyone who never uses the feature.
- loadSession() AND newSession() both clear pending blocks before replacing
  S.session, so chips never leak across a sidebar switch OR a New Chat
  (the New Chat clear + its regression test were added per Codex review).
- chip name/text rendered via esc(); XSS-safe.

Verified live (headless CDP drive): add/render/remove/rename, flush
produces correct markdown, empty-flush no-op, session-switch clears,
injected <img onerror=...> chip-name escaped (not executed).

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-06-17 20:15:02 +00:00
nesquena-hermes 0d202e6f77 Merge pull request #4374 from nesquena/stage-3576
Release v0.51.478 — QM (read-only LLM Wiki browser in Insights #2941)
v0.51.478
2026-06-17 12:56:59 -07:00