Commit Graph

871 Commits

Author SHA1 Message Date
Hermes Agent c677c19a8f Merge pull request #2128 into stage-344
Fix manual compression proxy timeouts (closes #2087)

# Conflicts:
#	CHANGELOG.md
2026-05-12 16:13:01 +00:00
Hermes Agent 23425b23c8 Merge pull request #2129 into stage-344
fix: purge missing inflight sessions (closes #2092)
2026-05-12 16:12:57 +00:00
Hermes Agent 8520755bd2 Merge pull request #2130 into stage-344
feat: load full lineage segments on demand
2026-05-12 16:12:52 +00:00
Hermes Agent 13c3646c55 Merge pull request #2121 into stage-344
fix: stack analytics usage cards on mobile (refs #2104) — TEST CONFLICT EXPECTED

# Conflicts:
#	tests/test_insights.py
2026-05-12 16:12:40 +00:00
Hermes Agent 2cccb8abcc Merge pull request #2120 into stage-344
fix: bucket long-range daily token charts (closes #2103)
2026-05-12 16:12:11 +00:00
Hermes Agent 56032151f7 Merge pull request #2143 into stage-344
Fix iPhone PWA chat bottom scroll stutter
2026-05-12 16:12:09 +00:00
Hermes Agent 45ac810a37 Merge pull request #2141 into stage-344
Fix Settings System mobile version wrapping (closes #2102)

# Conflicts:
#	CHANGELOG.md
2026-05-12 16:11:55 +00:00
Hermes Agent 76cf06a1a1 Merge pull request #2137 into stage-344
Fix login health probe credentials (closes #2122)
2026-05-12 16:11:42 +00:00
dobby-d-elf 099fdaf012 fix(ui): stabilize chat bottom scrolling on iPhone PWA 2026-05-12 07:47:21 -06:00
Frank Song b3f8bee96f Fix settings system mobile version wrapping 2026-05-12 20:42:55 +08:00
Frank Song b718220077 Fix login health probe credentials 2026-05-12 20:09:54 +08:00
Dennis Soong f1b2a21bd4 feat: lazy-load full lineage segments 2026-05-12 18:02:49 +08:00
Michael Lam dd543e4175 fix: purge missing inflight sessions 2026-05-12 02:57:37 -07:00
Frank Song 8fa92c680f Fix manual compression proxy timeouts 2026-05-12 17:33:59 +08:00
Michael Lam 7a16d09f10 fix: add Portuguese session management i18n 2026-05-11 23:23:41 -07:00
Michael Lam a41b4d5afc fix: stack analytics usage cards on mobile 2026-05-11 23:07:35 -07:00
Michael Lam 245288c00d fix: bucket long-range daily token charts 2026-05-11 23:01:13 -07:00
dobby-d-elf ff0830de4d fix(ui): smooth iPhone PWA bottom-edge bounce in chat 2026-05-11 22:08:32 -06:00
Frank Song 4e8899592d Prefer worktree retention responses in session UI 2026-05-12 10:17:12 +08:00
nesquena-hermes d75b59135a stage-341: apply Opus SHOULD-FIX (it i18n + short-circuit logger.debug + docstring)
Opus advisor pass on stage-341 found three surgical items:

1. static/i18n.js:it — PR #2064 branched before stage-340 landed the 'it'
   locale (#2067), missing 9 session_*worktree* keys. Mechanical mirror of
   en/ja position. Italian falls back to English silently without this fix.
2. api/streaming.py — PR #2107's new break short-circuit was silent in both
   the aux and agent title-generation paths. Added logger.debug calls before
   each break so production logs surface the exit shape.
3. api/streaming.py — Expanded _title_should_skip_remaining_attempts docstring
   to document the membership criterion explicitly (vs the implicit
   reasoning-only-burn case it ships with today). Future additions
   (llm_safety_blocked, llm_oauth_quota) have a clear inclusion test.

CHANGELOG updated under the Stage-341 maintainer fixes section to mirror
the stage-340 pattern. All targeted tests pass (57/57 in the affected
modules).
2026-05-12 00:16:33 +00:00
Frank Song 2da4f108c5 Clarify worktree session archive/delete semantics
(cherry picked from commit f5c8fb58d1)
2026-05-12 00:05:05 +00:00
nesquena-hermes 84e110db89 i18n(it): complete cron_toast_notifications_* keys
Opus SHOULD-FIX from stage-340 review. PR #2067 added the it locale
between en and ja; PR #2100 added 4 toast keys to 8 other locales but
missed it. Falls back to English via t() defaults so no user-visible
break, but it's an i18n parity hole.

4 LOC, mechanical add inside the it: block at the canonical position
(immediately after cron_profile_server_default_hint, mirroring en/ja).

Co-authored-by: ai-ag2026 <261867348+ai-ag2026@users.noreply.github.com>
Co-authored-by: Samuel Gudi <samuel.gudi.official@gmail.com>
2026-05-11 23:24:42 +00:00
Samuel Gudi ba3cc2c541 feat(i18n): add Italian (it) locale
Adds complete Italian translation for all ~280 UI strings in static/i18n.js
and the login page strings in api/routes.py (_LOGIN_LOCALE).

Ordered alphabetically: en → it → ja in both files.
Preserves all JS function templates, template literals, and plural forms.

(cherry picked from commit c66e04b190)
2026-05-11 23:13:55 +00:00
ai-ag2026 98c9a3de72 test: tighten CI and console hygiene
(cherry picked from commit bd9e6df71c)
2026-05-11 23:13:16 +00:00
ai-ag2026 52fedbc783 feat: add per-cron toast notification toggle 2026-05-11 21:58:35 +02:00
nesquena-hermes 2a1244f342 Merge PR #2089 into stage-339
support slash commands implemented in hermes plugin
by @plerohellec
2026-05-11 17:43:57 +00:00
nesquena-hermes 0456fb5619 Merge PR #2085 into stage-339
fix(logs): clipboard fallback + severity filter for Logs panel (#2081)
by @bergeouss
2026-05-11 17:43:56 +00:00
Philippe Le Rohellec 281a57b60a support slash commands implemented in hermes plugin 2026-05-11 09:42:40 -07:00
bergeouss 85547612fe fix(logs): clipboard fallback + severity filter for Logs panel (#2081)
- replace navigator.clipboard.writeText with _copyText (has textarea fallback)
- add severity filter dropdown (All / Errors / Warnings+)
- add _severityForLine and _filteredLogsLines helpers
- add logsSeverityFilter HTML element + CSS class hooks
- add 5 new i18n keys across all 8 locales
- update test_logs_ui_static.py to match new implementation

Closes #2081
2026-05-11 15:40:49 +00:00
Frank Song 6a52edf2ab Fix stale inflight purge runtime lookup 2026-05-11 21:53:43 +08:00
Frank Song c60078b356 fix(ui): prevent stuck sidebar spinner on completed sessions (closes #2066)
The spinner (.session-state-indicator.is-streaming) can remain spinning
indefinitely on completed sessions when the INFLIGHT in-memory cache is
not cleaned up due to abnormal stream termination (page refresh, network
disconnect, gateway restart).

Add a staleness guard in _isSessionLocallyStreaming: if the server
reports is_streaming=false and last_message_at is older than 5 minutes,
force the streaming state to false regardless of stale INFLIGHT entries.
2026-05-11 17:54:14 +08:00
nesquena-hermes 44e7378be8 Merge PR #2053: feat: worktree-backed session creation
# Conflicts:
#	CHANGELOG.md
2026-05-11 05:12:00 +00:00
Nathan Esquenazi ba66872f70 fix(sidebar): align collapse CSS breakpoint with JS _isDesktopWidth (641px)
`_isDesktopWidth()` in boot.js gates every collapse path on
`matchMedia('(min-width:641px)')` — matching where the rail itself becomes
visible. The CSS rules driving the actual visual collapse were nested inside
the workspace-panel block at `@media(min-width:901px)` — a threshold copied
from the right-panel collapse but with no functional reason to apply here.

Behavioural consequence in the 641–900 px band (tablet portrait + small
laptop windows):

  - Rail is visible, user clicks the active icon
  - JS adds `.layout.sidebar-collapsed` and writes localStorage='1'
  - JS sets aria-expanded='false' on the active rail button
  - CSS at min-width:901px does NOT apply → sidebar stays at 300 px width
  - User sees no visual change; screen reader announces collapsed state for
    a sidebar that is still visible; localStorage silently persists
  - Resize to ≥901 px later → sidebar suddenly collapses (surprise state)

Fix: hoist the three `.sidebar-collapsed` / flash-prevention rules out of
the workspace-panel @media block and into their own `@media(min-width:641px)`
block. The rail visibility breakpoint, the JS gate, and the CSS gate now
all agree.

`:not(.mobile-open)` is preserved on both selectors so the mobile slide-in
overlay (handled in the `max-width:640px` block) is never targeted — the
new @641 boundary doesn't change that contract.

Verified breakpoint matrix end-to-end (Node harness over real boot.js +
style.css):

  Width | JS desktop | CSS applies | Effect
  ------|------------|-------------|------------
   640  | no         | no          | no-op (mobile overlay)
   641  | yes        | yes         | collapses ✓
   700  | yes        | yes         | collapses ✓
   768  | yes        | yes         | collapses ✓
   900  | yes        | yes         | collapses ✓
   1024 | yes        | yes         | collapses ✓

Regression test added: `test_css_breakpoint_matches_js_isdesktopwidth`
parses boot.js for the `_isDesktopWidth` matchMedia query, walks CSS to
find the @media block enclosing `.layout.sidebar-collapsed`, and asserts
the thresholds match. Locks the invariant so a future refactor can't
re-introduce the asymmetric-band silent-state-leak.

Test counts:
  - tests/test_sidebar_collapse_toggle.py: 35/35 pass (was 34, +1 regression)
  - Full suite (Python 3.14, local): 5040 passed, 0 failed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:57:47 -07:00
nesquena-hermes 2dbee503c2 feat(ux): collapse sidebar by clicking the active rail icon (fuses #1884 + #1924)
Lets desktop users collapse the session-list sidebar to maximise the chat
area, without adding any visible UI affordance. Default appearance is
identical to master — only users who actively try to toggle (or know the
keyboard shortcut) ever see a difference.

## Behaviour (desktop only, ≥641px)

| State                              | Action                | Result                                  |
|------------------------------------|-----------------------|-----------------------------------------|
| Sidebar open, click active rail    | Toggle                | Sidebar collapses to width:0            |
| Sidebar open, click different rail | Normal switch         | **Sidebar stays open** (no surprise)    |
| Sidebar collapsed, click any rail  | Expand + switch       | Sidebar expands, then panel switches    |
| Anywhere, Cmd/Ctrl+B               | Toggle                | Same as same-active-rail click          |
| Mobile (<641px), any of the above  | No-op                 | Mobile overlay behaviour unchanged       |

Two discoverability paths, both opt-in. **No new visible buttons.** Users
who never click the active rail icon see zero UI change vs. master.

## Surface-minimal design

The behaviour is contained behind one extra arg on the rail/sidebar-nav
onclick: `switchPanel('chat',{fromRailClick:true})`. Without that flag the
function preserves master's behaviour exactly — every programmatic
`switchPanel(name)` callsite (commands, deeplinks, internal state changes)
is unaffected. The guard chain inside `switchPanel`:

  opts.fromRailClick && _isDesktopWidth() && (
      _isSidebarCollapsed() ? expandSidebar() :
      prevPanel === nextPanel ? (toggleSidebar(true); return false))

is the ONLY new code path that can cause a collapse. Cross-panel clicks
fall through to the existing switch logic untouched.

## Polish from both source PRs

- **Click-active gesture** as the primary toggle (#1884 @jasonjcwu — the
  genuine UX innovation; no extra button needed)
- **Cmd/Ctrl+B keyboard shortcut** (#1924 @spektro33; VS Code convention).
  Guarded against firing when typing in INPUT / TEXTAREA / contenteditable
  so the shortcut never steals from in-progress text editing.
- **Inline flash-prevention `<script>`** in `<head>` (#1924) sets
  `data-sidebar-collapsed='1'` on `<html>` BEFORE the stylesheet loads,
  so cold loads with a persisted-collapsed state paint correctly from
  frame 0 with no flicker. Cleared by JS once the class system takes over.
- **Smooth slide animation** via `.24s cubic-bezier(.22,1,.36,1)`
  (#1924, mirrors the existing workspace-panel collapse on the right)
- **`aria-expanded` mirrored** on the active rail button (#1884) so
  screen readers announce open/collapsed transitions.
- **`body.resizing` transition-suppression** (#1884) keeps the drag-resize
  cursor instant — no animation during a width-resize gesture.
- **bfcache `pageshow` re-sync** (#1884) — if another tab toggled the
  sidebar while this page was frozen, bring it in line on restore.

## Drops vs. #1924

- No persistent rail "toggle sidebar" button (Nathan: keep the UI stealth)
- No close-X button in chat panel head (same reason)
- No i18n keys for the dropped buttons

## What did NOT change

- 22 rail/sidebar-nav `onclick` handlers gained the `{fromRailClick:true}`
  arg — function-call shape, invisible to users
- 1 inline `<script>` in `<head>` (flash prevention) — invisible
- 5 lines of CSS — invisible unless someone collapses

That's the entire visible-UI delta. **23 ins / 22 del on `index.html`,
all string-replace.**

## Verification

- 5,151 pytest passing including a new 34-test structural suite covering
  every contract (CSS rules, JS functions, fromRailClick guard, legacy
  proxy forwarding, flash-prevention `<script>` ordering, mobile
  exclusion via :not(.mobile-open) selector, aria-expanded sync).

- Live browser walkthrough at 1280px verified:
  - Default boot state identical to master (sidebar open, width 300px)
  - Click active rail → collapse (width 1, opacity 0, translateX -14px,
    localStorage='1', aria-expanded=false). Panel unchanged.
  - Click active rail again → expand back to width 300, aria=true
  - Click DIFFERENT rail → normal switch, sidebar stays open (legacy-
    preserving case, verified explicitly)
  - Click rail while collapsed → expand + switch in one gesture
  - Cmd+B toggles correctly
  - Cmd+B inside `<textarea>` → suppressed (defaultPrevented=false)
  - Reload with collapsed state persisted → restores without flash
  - Mobile simulation (matchMedia returns false for min-width:641px):
    same-active-rail click is no-op, Cmd+B is no-op, sidebar stays at 300px

Co-authored-by: jasonjcwu <jasonjcwu@users.noreply.github.com>
Co-authored-by: spektro33 <spektro33@users.noreply.github.com>
Closes #1884
Closes #1924
2026-05-11 04:49:18 +00:00
Frank Song 186453ea0e Add worktree-backed session creation 2026-05-11 12:12:40 +08:00
George Davis 8178c5e57b feat: add slack to cron delivery options 2026-05-11 02:45:46 +00:00
Frank Song a27f1bf7db Clarify one-shot cron schedules 2026-05-11 07:03:17 +08:00
nesquena-hermes 2377216860 Stage 333: PR #2009 — feat(context): live status tracking during streaming by @dobby-d-elf 2026-05-10 18:16:59 +00:00
nesquena-hermes 83bce07d29 Stage 333: PR #2018 — fix(stop): refresh button after chat/start stream id by @rhelmer 2026-05-10 18:16:59 +00:00
nesquena-hermes fe922d83b0 Merge remote-tracking branch 'origin/master' into stage-332
# Conflicts:
#	CHANGELOG.md
2026-05-10 18:07:50 +00:00
nesquena-hermes 22991fa820 Merge remote-tracking branch 'origin/master' into stage-331
# Conflicts:
#	CHANGELOG.md
2026-05-10 18:03:55 +00:00
nesquena-hermes c9d4100218 Merge remote-tracking branch 'origin/master' into stage-332
# Conflicts:
#	CHANGELOG.md
2026-05-10 17:46:34 +00:00
nesquena-hermes 16535e1f66 Merge remote-tracking branch 'origin/master' into stage-331
# Conflicts:
#	CHANGELOG.md
2026-05-10 17:46:10 +00:00
nesquena-hermes 4f900d0763 Merge remote-tracking branch 'origin/master' into stage-330
# Conflicts:
#	CHANGELOG.md
#	static/i18n.js
2026-05-10 17:45:29 +00:00
Robert Helmer ce27499762 Fix Stop button not refreshing after chat/start stream id
Call updateSendBtn after S.activeStreamId is cleared for a new turn and
again after the server returns streamId, since setBusy(true) already
refreshed the button while activeStreamId was still null.

Add regression tests in test_1062_busy_input_modes (TestBusySendButton).
2026-05-10 10:15:16 -07:00
nesquena-hermes cb27ab0142 Stage 332: PR #2013 — fix(sessions): avoid sidebar jumps when active session is visible by @ai-ag2026 2026-05-10 17:09:44 +00:00
nesquena-hermes 2fb29e508c Stage 332: PR #2008 — fix(diff): CLI session patch diff rendering by @franksong2702 2026-05-10 17:09:44 +00:00
nesquena-hermes f5a8a6f9ef Stage 332: PR #2007 — fix(mobile): wrap markdown code blocks on mobile by @insecurejezza 2026-05-10 17:09:44 +00:00
nesquena-hermes 44dc7d05e8 Stage 331: PR #2014 — fix(sessions): keep explicit fork sessions out of compression lineage by @ai-ag2026 2026-05-10 17:09:21 +00:00
nesquena-hermes f4d3e9eed4 Stage 331: PR #2011 — fix(sessions): prefer latest compressed segment by @ai-ag2026 2026-05-10 17:09:21 +00:00