Commit Graph

1419 Commits

Author SHA1 Message Date
nesquena-hermes ec403fa3cf fix(routes): persist openai-codex provider unconditionally on stale-session repair (Opus stage-303 follow-up)
Opus advisor on stage-303 (#1738 verification Q4) flagged that the
catalog-coverage branch produces a redundant repair-write per chat-start
when the active Codex default is itself slash-prefixed: the repair sets
`provider_context = None`, the next chat-start hits the same branch
because `requested_provider is None` again, and the repair fires repeatedly.

In practice Codex `default_model` is always a bare `gpt-...` ID from the
Codex catalog, so this is theoretical. But once we've decided this session
belongs to Codex, we should persist that decision. Drop the conditional
catalog-coverage check and unconditionally attach `raw_active_provider`
("openai-codex") on this repair path. The shape is now stable across
resolutions.

Absorb-in-release per Opus stage-303 verdict — small, defensive, ≤10 LOC.
2026-05-06 15:18:34 +00:00
test bccb1a06d6 Stage 303: PR #1738 — fix: repair stale OpenAI session models for Codex by @Michaelyklam 2026-05-06 14:53:40 +00:00
test 043b2ecfaa Stage 303: PR #1735 — fix(streaming): keep saved running sessions sidebar-only on root boot by @dso2ng 2026-05-06 14:53:40 +00:00
Michael Lam 3e2a945501 fix: repair stale OpenAI session models for Codex 2026-05-06 14:53:40 +00:00
Dennis Soong 8138ca8479 fix: keep saved running sessions sidebar-only on root boot
Root page loads should not automatically project a localStorage-saved running session into the active pane. Keep explicit /session/<sid> behavior unchanged while leaving the saved session discoverable from the sidebar.

(cherry picked from commit bb60cf21d911a84e285363bcecf46fb441181fb9)
2026-05-06 14:53:40 +00:00
nesquena-hermes 85d0279fbb Merge pull request #1737 from nesquena/stage-302
v0.51.8 — 7-PR batch (Activity row, OAuth, scroll, profile context, CLI catalogs, sidebar hover)
v0.51.8
2026-05-06 01:29:59 -07:00
nesquena-hermes 62bcf513c3 chore(release): stamp v0.51.8 — 7-PR full-sweep batch + Opus follow-up + test-isolation fix
Constituent PRs:
- #1725 (@Michaelyklam) — simplify compact Activity row summary
- #1726 (@Michaelyklam) — delegate generic provider catalogs to Hermes CLI (slice of #1240)
- #1727 (@Michaelyklam) — link Claude Code OAuth in onboarding (closes #1362)
- #1728 (@starship-s) — preserve profile context when starting chats
- #1729 (@Michaelyklam) — persist compact Activity disclosure state
- #1730 (@Michaelyklam) — prevent sticky sidebar hover drag state
- #1732 (@Sanjays2402) — unpin scroll on small upward motion during streaming (closes #1731)

Plus 2 in-stage absorbed fixes:
- test-isolation fix: monkeypatch.setattr(config, 'cfg', X) survives PR #1728's
  path/mtime-aware get_config() reload. Mandatory before tag (Opus stage-302).
- Opus SHOULD-FIX #1: _lastScrollTop reset on session switch (#1732 follow-up).

Tests: 4537 → 4584 passing (+47). 0 regressions. Full suite ~128s. Stably green.

Pre-release verification:
- All 7 PRs CI-green individually + rebased onto master
- pytest 4584 passed, 0 failed (multiple runs)
- node -c clean on all 4 modified .js files
- 11/11 browser API endpoints PASS on isolated port 8789
- 20 QA tests via webui_qa_agent.sh PASS
- Opus advisor: SHIP, 5/5 verification clean, 0 MUST-FIX, 1 SHOULD-FIX absorbed
  (_lastScrollTop reset), 1 SHOULD-FIX deferred (#1736 — _clear_anthropic_env_values
  race, onboarding-time-only)

Closes #1362, #1731.
2026-05-06 08:27:37 +00:00
nesquena-hermes 93f30ecfda fix(scroll): reset _lastScrollTop on session switch (Opus stage-302 follow-up)
Opus advisor on stage-302 (#1732 verification Q5) flagged that
_lastScrollTop is module-global and persists across chat switches. When
the user switches sessions, the new chat's first user scroll compares
against the previous chat's last scrollTop. If the previous was deep-
scrolled (e.g. 5000) and the new chat starts at top=0, scrolling down
to 100 would evaluate as movedUp=true → false-unpin, blocking auto-
scroll on the new chat's first incoming token.

Fix: expose _resetScrollDirectionTracker() from static/ui.js on window
so static/sessions.js loadSession() can reset _lastScrollTop=null when
S.session is reassigned. The scroll listener's existing _lastScrollTop!==null
guard then handles the first sample after reset correctly (no false-trigger
on the very first scroll event in the new chat).

Absorb-in-release per Opus stage-302 verdict — small, defensive, ≤20 LOC.
2026-05-06 08:21:42 +00:00
nesquena-hermes 97aa3247e1 fix(test-isolation): in-stage fixes for stage-302 pre-release gate
PR #1728's path/mtime-aware get_config() reload broke the common test
idiom monkeypatch.setattr(config, 'cfg', {...}). The cfg = _cfg_cache
alias bound at import time means the rebinding only changes the module
attribute; _cfg_cache stays unchanged, so _cfg_has_in_memory_overrides()
returned False and the path-aware reload silently overwrote the test's
override. test_issue1426_openrouter_* and test_issue1680_codex_* failed
in the full suite while passing standalone — exact polluter signature.

Fix:
- _cfg_has_in_memory_overrides() now also detects cfg-rebind via
  cfg is not _cfg_cache.
- get_config() returns cfg (the override) when it differs from
  _cfg_cache, so callers see the test's intended override.
- 4 new regression tests pin both prongs in
  test_stage302_config_override_regression.py.

Defense-in-depth (prong 2 of test-isolation-flake-recipe):
- test_sprint3.py::test_skills_list and test_skills_list_has_required_fields
  now skip on empty skills list rather than asserting > 0 / IndexError, so
  future profile-switch / SKILLS_DIR repointing pollutions don't break
  the build. The contract under test is 'API returns a non-empty list
  when there are entries' — empty list signals a polluter elsewhere.

Pre-existing wall-clock flake fix (absorb-in-release):
- test_issue1144_session_time_sync.py::test_relative_time_uses_server_clock
  now pins Date.now() to a fixed instant. Without pinning, when CI runs
  near 08:00 UTC the projected server time crosses midnight and '5 minutes
  ago' silently becomes '1d'. Same time-of-day-pin pattern as the sibling
  test_session_bucket_uses_server_clock used.

Test count: 4580 → 4584 (+4 regression tests). 0 failures, stably green
across multiple runs.
2026-05-06 08:10:08 +00:00
test a25383d998 Stage 302: PR #1729 — fix: persist compact activity disclosure state by @Michaelyklam 2026-05-06 06:30:45 +00:00
Michael Lam ee9ae29596 fix: persist activity disclosure state 2026-05-06 06:30:32 +00:00
test a215444e5a Stage 302: PR #1725 — fix: simplify compact activity summaries by @Michaelyklam 2026-05-06 06:27:14 +00:00
Michael Lam 47a3073882 docs: add compact activity summary screenshots 2026-05-06 06:27:14 +00:00
Michael Lam a7b6cd2cda fix: simplify compact activity summaries 2026-05-06 06:27:13 +00:00
test 41df566d28 Stage 302: PR #1728 — fix(profile): preserve context when starting chats by @starship-s 2026-05-06 06:27:00 +00:00
starship-s 74eb55d986 fix(profile): preserve context when starting chats 2026-05-06 06:27:00 +00:00
test c280248a94 Stage 302: PR #1726 — fix(models): delegate generic provider catalogs to Hermes CLI by @Michaelyklam 2026-05-06 06:26:44 +00:00
test 857f536f82 Stage 302: PR #1727 — feat: link Claude Code OAuth in onboarding by @Michaelyklam 2026-05-06 06:26:44 +00:00
Michael Lam 63239d5b3c fix(models): delegate generic provider catalogs to Hermes CLI 2026-05-06 06:26:44 +00:00
Michael Lam 5272215e7c docs: clarify Anthropic auth choices in onboarding 2026-05-06 06:26:43 +00:00
Michael Lam e509faec44 feat: link Claude Code OAuth in onboarding 2026-05-06 06:26:43 +00:00
test 4dca3d9b96 Stage 302: PR #1732 — fix(streaming): unpin scroll on small upward motion during streaming (#1731) by @Sanjays2402 2026-05-06 06:26:28 +00:00
Sanjays2402 9bb4fad0e8 fix(streaming): unpin scroll on small upward motion during streaming (#1731)
The streaming scroll listener applied hysteresis symmetrically: an
upward scroll that landed inside the 250px near-bottom dead zone still
reported the user as near the bottom, so _nearBottomCount kept
incrementing and _scrollPinned stayed true. The next streaming token
snapped the user back to the bottom. The user effectively had to escape
the 250px zone in one fling to read earlier output.

The 250px dead zone itself is required by #1360 / #677 (macOS small
window + trackpad momentum re-pin protection) so the fix is direction
detection, not threshold relaxation: track _lastScrollTop and unpin
immediately on an explicit upward movement (>2px decrease), while
downward / stationary movement keeps the original hysteresis re-pin
path so the macOS momentum protection is preserved.

Programmatic scrolls are still masked by the existing _programmaticScroll
guard, so scrollToBottom() never updates _lastScrollTop and never
spuriously unpins.

Adds tests/test_issue1731_upward_scroll_unpins.py covering: direction
tracker exists, upward branch sets _scrollPinned=false and resets the
counter without hysteresis, downward branch preserves the >=2
hysteresis re-pin requirement, the 250px threshold remains, and the
_programmaticScroll bail still runs before the rAF schedule.

Closes #1731.

Co-Authored-By: Potato (OpenClaw assistant) <noreply@openclaw.ai>
2026-05-06 06:26:28 +00:00
test 93df84a24d Stage 302: PR #1730 — fix: prevent sticky sidebar hover drag state by @Michaelyklam 2026-05-06 06:26:15 +00:00
Michael Lam ecdbc8d4df fix: prevent sticky sidebar hover drag state 2026-05-05 19:17:27 -07:00
nesquena-hermes d8cd5567e0 Merge pull request #1723 from nesquena/docs/1695-aiagent-troubleshooting
docs(troubleshooting): bake the #1695 diagnostic flow into the error message + a new troubleshooting doc
v0.51.7
2026-05-05 15:16:08 -07:00
nesquena-hermes 29878259ca docs(troubleshooting): bake the #1695 diagnostic flow into the error message + a new troubleshooting doc
Closes #1695.

@Patrick-81 reported the bare "AIAgent not available -- check that
hermes-agent is on sys.path" error on a symlinked install (~/Programmes/hermes-agent
linked to ~/hermes-agent). The maintainer's response — three diagnostic
commands plus `pip install -e .` in the agent dir — fixed it for them.
This PR captures both halves of that learning so the next user with the
same shape doesn't have to file a new issue:

1. **Error message diagnostic block.** New helper
   `_aiagent_import_error_detail()` in api/streaming.py builds a multi-line
   diagnostic when the import fails, including:
     - the running Python interpreter
     - HERMES_WEBUI_AGENT_DIR (set value, or "(not set)")
     - sys.path entries that mention hermes/agent (or "no entries mention..."
       — itself a strong diagnostic signal)
     - the most-common fix (`pip install -e .` in the agent dir)
     - a pointer to docs/troubleshooting.md

   The original error message string is preserved as the FIRST line so
   existing log scrapers and docs-search keep matching.

   Helper is kept as a separate function so it stays out of the hot path
   until we actually need to raise — building it on every successful import
   would be wasted work.

2. **New docs/troubleshooting.md.** Symptom → Why → Diagnostic commands →
   Fix → When-to-file-a-bug template, with one entry to start: the
   "AIAgent not available" flow Patrick-81 walked through. Future
   recurring failure modes follow the same template. Required a one-line
   addition to .gitignore — docs/* is gitignored with an allowlist, and
   the new file needed `!docs/troubleshooting.md` to be tracked.

3. **README link.** docs/troubleshooting.md added to the `## Docs` section
   so users know where to look first.

13 regression tests in tests/test_1695_aiagent_import_error_detail.py:
9 for the helper output shape (preserves original message line, includes
running python, shows HERMES_WEBUI_AGENT_DIR set/unset both ways, includes
pip-install-e hint, points at troubleshooting doc, lists relevant sys.path
entries when present, says "no entries..." when absent, output is multi-line)
plus 4 for the docs-presence regression (file exists, has the AIAgent
section, includes pip install -e ., describes the diagnostic chain with
readlink + agent/__init__.py verification).

190 streaming/aiagent tests pass after the change. ast.parse on
api/streaming.py clean.

CI failure on prior push was due to the docs/* gitignore swallowing the
new troubleshooting.md file silently — this commit adds the allowlist
entry so the file is tracked.
2026-05-05 22:14:07 +00:00
nesquena-hermes a6e2bbb263 Merge pull request #1724 from nesquena/stage-303
v0.51.6 — 5-PR full-sweep batch
v0.51.6
2026-05-05 15:11:06 -07:00
Nathan Esquenazi 23bca0d955 chore(release): stamp v0.51.6 — 5-PR full-sweep batch
5 PRs (1 surface addition, 4 fixes):
- #1717 preserve imported session lineage (@ai-ag2026)
- #1718 preserve Activity count across focus changes (@Michaelyklam, closes #1715)
- #1719 elapsed timer in compact activity (@Michaelyklam, closes #1716)
- #1720 backend tool snippet cap raised to 4000 (@Michaelyklam, closes #1714)
- #1722 suppress stale preserved task lists (@ai-ag2026)

Tests: 4527 → 4537 (+10). Opus: SHIP, 6/6 verification clean.

Co-authored-by: ai-ag2026 <noreply@github.com>
Co-authored-by: Michael Lam <Michaelyklam1@gmail.com>
2026-05-05 22:09:08 +00:00
Nathan Esquenazi b6567addb1 Stage 303: PR #1719 2026-05-05 21:58:21 +00:00
Nathan Esquenazi cbdf770d36 Stage 303: PR #1722 2026-05-05 21:58:21 +00:00
Nathan Esquenazi afe0c26df9 Stage 303: PR #1720 2026-05-05 21:58:21 +00:00
Nathan Esquenazi 220bd50795 Stage 303: PR #1717 2026-05-05 21:58:21 +00:00
Nathan Esquenazi fb9823ea2e Stage 303: PR #1718 2026-05-05 21:58:20 +00:00
ai-ag2026 b66e720673 fix: suppress stale preserved task lists
Hide preserved compression task lists when the latest todo tool state
shows no pending or in-progress items. This prevents completed tasks from
reappearing after reloads or context compaction.

Tests: uv run --with pytest --with pyyaml python -m pytest -q tests/test_auto_compression_card.py
Tests: node --check static/ui.js
2026-05-05 23:00:18 +02:00
Michael Lam f97b040985 fix: raise persisted tool snippet cap 2026-05-05 13:46:54 -07:00
Michael Lam 2c5acb9725 feat: show active elapsed timer in compact activity 2026-05-05 13:42:47 -07:00
Michael Lam dd2bc38473 fix: preserve activity count across chat focus changes 2026-05-05 13:42:45 -07:00
ai-ag2026 8b34a79f02 fix: preserve imported session lineage visibility 2026-05-05 22:32:19 +02:00
nesquena-hermes 0ea3dfbdd1 Merge pull request #1713 from nesquena/stage-302
v0.51.5 — 4-PR full-sweep batch
v0.51.5
2026-05-05 11:00:37 -07:00
Nathan Esquenazi b59c6975a2 chore(release): stamp v0.51.5 — 4-PR full-sweep batch
4 PRs (1 surface addition, 3 fixes):
- #1688 VPS resource health Insights panel (@Michaelyklam, closes #693)
- #1709 preserve scroll on stream completion (@Michaelyklam, closes #1690)
- #1711 hide rename tooltip on folders (@nesquena-hermes, closes #1710)
- #1712 guard localStorage.setItem against QuotaExceededError (@24601)

Tests: 4504 → 4527 (+23). Opus: SHIP, 6/6 verification clean.

Held back: #1686 (Docker enhance) — Opus flagged sibling-repo dep that
breaks standalone clones. Left open for follow-up.

Co-authored-by: Michael Lam <Michaelyklam1@gmail.com>
Co-authored-by: 24601 <noreply@github.com>
2026-05-05 17:54:15 +00:00
test b59164b0a8 Stage 302: PR #1688 2026-05-05 17:31:01 +00:00
Michael Lam fe9e4645ac fix: move system health panel into insights 2026-05-05 17:30:56 +00:00
Michael Lam fdeac578da feat: add VPS resource health panel 2026-05-05 17:30:56 +00:00
Nathan Esquenazi 967f7876e9 Stage 302: PR #1709 2026-05-05 17:29:47 +00:00
Nathan Esquenazi 77052fd4ec Stage 302: PR #1711 2026-05-05 17:29:47 +00:00
Nathan Esquenazi bedcc41b08 Stage 302: PR #1712 2026-05-05 17:29:47 +00:00
Basit Mustafa 9a0a6214cf fix: guard localStorage.setItem('hermes-webui-model') against QuotaExceededError
On some setups the localStorage quota is exhausted; the bare setItem
call throws an unhandled DOMException that breaks model selection and
prevents the chat UI from loading.

Wrap both call-sites (boot.js model-select onChange, onboarding.js
_saveOnboardingDefaults) in try/catch so the error is logged to the
console as a warning instead of surfacing as a fatal exception.

Fixes: 'Failed to execute setItem on Storage: Setting the value of
hermes-webui-model exceeded the quota.'
2026-05-05 17:29:47 +00:00
nesquena-hermes d3c8a7c6a5 fix(workspace): hide 'Double-click to rename' tooltip on folders (#1710)
The file-tree row tooltip says 'Double-click to rename' on every entry,
but folders don't actually rename on double-click — they navigate via
loadDir(). The tooltip is therefore misleading on directory rows.

Reported by @Deor in the WebUI Discord testers thread (May 5 2026):
'Ah that works yeah. May want to change the popup text as it also says
double click at the moment.'

Fix: gate the tooltip on item.type !== 'dir' so it only attaches to file
rows, where double-click does what the hint advertises. Folder rename
still reachable via the right-click context menu (unchanged).

Companion to #1698/#1702/#1707 — completes the rename-affordance triage:
- #1698 fixed: dblclick rename was unreachable on files (preview hijacked)
- #1707 fixed: single-click on filename did nothing (over-aggressive guard)
- #1710 (this PR): tooltip claimed dblclick-rename on folders too

Closes #1710

Tests: 4 source-level regression tests in tests/test_1710_folder_tooltip.py
guard the gate, the unchanged dir-dblclick navigate behaviour, the i18n key,
and that files still receive the tooltip. All 13 file-tree handler tests
(4 new + 9 from #1707) pass.
2026-05-05 16:41:30 +00:00
Michael Lam 311e69b0ba fix: preserve scroll on stream completion 2026-05-05 09:23:29 -07:00