Commit Graph

1344 Commits

Author SHA1 Message Date
nesquena-hermes dca4a2a7af fix(#3210 review): convert bare urlopen TimeoutError to ValueError in _joplin_api_get
Codex+Opus pre-release gate both flagged: TimeoutError is now in the
consolidated _CLIENT_DISCONNECT_ERRORS dispatch set, so a bare socket-connect
TimeoutError from Joplins urlopen(timeout=8) — which is NOT always URLError-
wrapped — would escape _handle_notes_search and be swallowed by the dispatch
disconnect handler as a fake client disconnect (silent empty response, no log).
Catch (URLError, TimeoutError) at the route so it surfaces as a clean
"not reachable" ValueError -> JSON error. Adds a regression test.

Co-authored-by: someaka <someaka@users.noreply.github.com>
2026-05-31 01:57:29 +00:00
Ed 79f526b1b2 fix(helpers): use ssl.SSLError instead of broad OSError in disconnect tuple
OSError is too broad — it masks real errors like file-not-found.
ssl.SSLError specifically catches SSL-level disconnects without
swallowing unrelated OSError subtypes.

Closes the test_excludes_broad_oserror CI failure.
2026-05-31 01:56:39 +00:00
Ed 78652ecdea fix: consolidate _CLIENT_DISCONNECT_ERRORS, remove scratch file
Address review feedback from @nesquena-hermes on PR #3210:

1. Deduplicate _CLIENT_DISCONNECT_ERRORS:
   - Single authoritative definition in api/helpers.py
   - api/routes.py now imports from api.helpers instead of defining
     its own copy with different membership
   - Unified tuple uses OSError (covers ssl.SSLError since it
     subclasses OSError) — broad socket-level disconnect coverage

2. Remove github-search-report.md:
   - Research scratch output that doesn't belong in the repo root
   - Content belongs in PR description or a gist

3. Docstring improvement:
   - Added comment explaining why OSError covers ssl.SSLError
   - Documents the errno-level socket errors caught by OSError
2026-05-31 01:56:39 +00:00
Ed 59b7a3ac94 fix: eliminate silent failures in client disconnect handling
- api/helpers.py: _safe_write() now logs disconnects at debug level
  instead of silently passing. No more invisible errors.

- server.py: Restructure exception handlers to catch
  _CLIENT_DISCONNECT_ERRORS first, then Exception. Remove the
  isinstance() filter inside except Exception (LBYL anti-pattern).
  The 500-response fallback now catches _CLIENT_DISCONNECT_ERRORS
  separately (expected) and logs unexpected failures via
  traceback.print_exc() instead of bare except Exception: pass.

- tests/test_broken_pipe_cascade.py: Add coverage for SSL/Timeout
  disconnect routing and 500-response safety (both disconnect
  survival and unexpected error logging).
2026-05-31 01:56:39 +00:00
someaka 2cee44b1b2 fix(helpers): catch client disconnect errors in response write path
Extract _safe_write() helper that wraps end_headers() + wfile.write()
in try/except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError,
TimeoutError, ssl.SSLError).  Both j() and t() now use _safe_write()
instead of raw wfile calls.

Fixes cascading BrokenPipeError + SSL BAD_LENGTH crash when a client
disconnects mid-response and the error handler tries to write a 500
status through the same broken socket.
2026-05-31 01:56:39 +00:00
AJV20 6ecc32be00 Close evicted cached agents on identity mismatch 2026-05-30 19:25:51 -04:00
nesquena-hermes 22e628be07 Merge #3191: guard session cache ownership across compression
# Conflicts:
#	CHANGELOG.md
2026-05-30 21:57:48 +00:00
nesquena-hermes bc50e95ec7 Merge #3166: close evicted agents from WebUI cache (commit memory + shutdown provider + close db) 2026-05-30 21:57:13 +00:00
nesquena-hermes 2a60aa048c Merge #3202: reasoning effort for bare/dot-separated custom-provider model names 2026-05-30 21:42:26 +00:00
ai-ag2026 00e6d953c2 feat: add distinct attention request sound
Adds a distinct two-tone attention sound (880->660Hz) for approval and
clarify prompts so they are not confused with the existing completion sound,
plus sidebar attention badges + colored rails driven by `attention` metadata
on /api/sessions. Includes lock-safety note: the in-lock
publish_session_list_changed() calls in clarify.py are safe because publish()
only takes the leaf _SESSION_EVENTS_LOCK and never re-acquires clarify._lock
(verified by Opus advisor review).

Co-authored-by: ai-ag2026 <261867348+ai-ag2026@users.noreply.github.com>
2026-05-30 19:40:40 +00:00
ai-ag2026 d2aa066862 feat: surface session attention badges 2026-05-30 19:18:13 +00:00
Hermes Agent 06842f6e9f fix(insights): exclude source='webui' from CLI session pass + add tests
The new CLI/gateway insights pass counted ALL state.db sessions, but
WebUI-native sessions are persisted to state.db with source='webui' AND
already counted from the sidecar _index.json first pass — double-counting
them in totals, model breakdown, and daily charts. Add
`AND COALESCE(source, '') != 'webui'` so only CLI/gateway/cron/tui rows are
added by the second pass.

Adds regression tests proving (a) CLI + Telegram sessions appear in totals
and (b) a webui-source state.db row is not double-counted against its
_index.json entry.

Co-authored-by: wind-chant <wind-chant@users.noreply.github.com>
2026-05-30 18:58:20 +00:00
wind-chant 1048d2cb0c feat: include CLI session usage in WebUI Insights
The Insights page () previously only counted WebUI-native
sessions from its own session index. This adds a query to the Hermes
state.db so CLI and gateway sessions (Telegram, Discord, etc.) are also
included in the aggregated token counts, costs, model breakdown, and
daily activity charts.

The state.db query is best-effort: if the file is missing or
unreadable, the existing WebUI-only data is returned as before.
2026-05-30 18:55:50 +00:00
carryzuo00 d6c1b696f5 fix(reasoning): expose effort levels for bare/dot-separated model names on custom providers
Custom API aggregators (New API, One API, etc.) route requests using their
own naming conventions — bare names like ``deepseek-v4-flash`` or dot-separated
names like ``moonshotai.kimi-k2.5`` — rather than the OpenRouter-style
``vendor/model`` slash format that ``_heuristic_reasoning_efforts``'s prefix
list was written for.

Because none of these names match the slash-prefixed patterns, the function
returned ``[]``, hiding the reasoning effort selector in the WebUI even for
models that fully support thinking/reasoning.

Fix: add a secondary check in ``_heuristic_reasoning_efforts`` that strips an
optional dot-vendor prefix and matches the remaining model name against a list
of known thinking-capable bare-name prefixes (``deepseek-v4``, ``deepseek-r1``,
``kimi-k2``, ``qwen3``, ``claude-3/4``, ``o1/o3/o4``), plus a keyword catch
for model names containing ``thinking`` or ``reasoning``.

The slash-prefix path and all provider-specific fast paths (copilot, lmstudio,
models.dev metadata) are unchanged; this block only runs as a final fallback.

Regression tests added in ``tests/test_custom_provider_bare_model_reasoning.py``.
2026-05-31 02:51:26 +08:00
nesquena-hermes db952ac561 Merge branch 'pr-3187' into release/stage-batch55
# Conflicts:
#	CHANGELOG.md
2026-05-30 18:24:29 +00:00
nesquena-hermes 58aec3e3f8 Merge branch 'pr-3184' into release/stage-batch55
# Conflicts:
#	CHANGELOG.md
2026-05-30 18:24:29 +00:00
nesquena-hermes 6d1ae11522 Merge branch 'pr-3180' into release/stage-batch55
# Conflicts:
#	CHANGELOG.md
2026-05-30 18:24:28 +00:00
nesquena-hermes 9e48a2a417 Merge branch 'pr-3183' into release/stage-batch54
# Conflicts:
#	CHANGELOG.md
2026-05-30 17:52:53 +00:00
nesquena-hermes 637ae65383 Merge branch 'pr-3182' into release/stage-batch54 2026-05-30 17:52:53 +00:00
ai-ag2026 9932899b88 fix: guard session cache ownership across compression 2026-05-30 18:19:31 +02:00
mysoul12138 71e6eabb29 fix: let cron sessions bypass CLI_VISIBLE_SESSION_LIMIT for project chips (#3172)
When state.db has many non-cron sessions, the normal sidebar query caps
at CLI_VISIBLE_SESSION_LIMIT (20) rows ordered by latest activity. Older
cron runs get squeezed out before _include_project_hidden_background_sidebar_sessions
can rescue them, making them invisible under their project chip.

Add a second-pass cron-only query with a higher cap (CRON_PROJECT_CHIP_LIMIT=200)
that merges into the CLI session list.  The project-chip rescue layer then
marks them default_hidden so they stay addressable without polluting the
default sidebar window.

Verification: regression test seeds 25+ newer non-cron sessions and asserts
the older messageful cron session still appears with project_id set.
2026-05-30 23:25:41 +08:00
ai-ag2026 d57854807a fix: keep tool cards anchored during history paging 2026-05-30 14:57:00 +02:00
ai-ag2026 09d2f4be2f fix: exclude hidden snapshots from pin quota 2026-05-30 11:48:57 +02:00
ai-ag2026 cb01e041fd fix: preserve tilde workspace suggestions 2026-05-30 11:35:05 +02:00
ai-ag2026 000e1dc9b6 fix: hash dirty WebUI dev builds 2026-05-30 11:25:33 +02:00
ai-ag2026 fb7fa5bcac fix: handle Windows media and turn journal edge cases 2026-05-30 11:15:35 +02:00
ai-ag2026 b3483d943c fix: close evicted agents from WebUI cache 2026-05-30 10:43:13 +02:00
AJV20 5cb82f9da4 fix: make SSE replay cursors run-aware 2026-05-30 04:09:20 +00:00
AJV20 fbc0742621 fix: ignore release tags already contained by head 2026-05-30 03:37:12 +00:00
AJV20 7efb4c5acb fix: scope skill toggles to active profile 2026-05-30 03:36:09 +00:00
nesquena-hermes c2663f827a Merge PR #3152 (keep agent cron sessions in project chips, #3134) into stage-batch48
# Conflicts:
#	CHANGELOG.md
2026-05-30 02:19:28 +00:00
AJV20 e68c670bba fix: keep agent cron sessions in project chips 2026-05-30 02:18:51 +00:00
AJV20 d2686eaedb fix: honor shared OpenCode key at runtime 2026-05-30 02:18:06 +00:00
Sanjays2402 ab4de3bb64 Fix #3103: stop emitting Connection: close on long-lived SSE streams
The gateway SSE handler (`_handle_gateway_sse_stream`) and session-events
SSE handler (`_handle_session_events_stream`) both set
`Connection: close` on the response. On long-lived SSE streams, browsers
(Chrome, Firefox, Safari) interpret that header as 'this is a one-shot
response, the EventSource lifecycle is over the moment the body ends'
and trigger an immediate reconnect when the server-side worker rotates.

The result, with EventSource's auto-reconnect on top, is a tight loop of
connect -> sessions_changed snapshot -> reconnect every ~1s that pegs
the worker, thrashes the session list, and prevents the in-app session
list from ever settling.

Removing the header lets the python BaseHTTPServer close the socket
naturally after the stream ends, which is what EventSource expects.

Regression introduced in 598fd4ff.
2026-05-30 02:02:40 +00:00
nesquena-hermes 694fdd1096 review-3105: bound _wait_until_restart_safe with 300s max-wait + execv fallback (Opus review — avoid self-update soft-jam on long runs) 2026-05-30 01:53:38 +00:00
AJV20 b53bc1a2ea fix: harden webui passive performance paths 2026-05-30 01:39:54 +00:00
AlexeyDsov da9211bf43 fix(session): copy all critical fields on duplicate and branch to prevent state loss 2026-05-30 01:19:50 +00:00
nesquena-hermes bd0e915a5d Merge PR #3142 (session-index metadata refresh perf) into stage-batch42 2026-05-29 23:48:29 +00:00
nesquena-hermes 8933fe9dbe Merge PR #3112 (skills disabled read-path) into stage-batch42 2026-05-29 23:48:24 +00:00
nesquena-hermes 23f5ee15f7 review-3142: align load_metadata_only comment with sidecar-first logic 2026-05-29 23:46:51 +00:00
zapabob 1f51ef14d0 fix: detect shared OpenCode API key 2026-05-29 23:22:15 +00:00
Harlan Zhou 39f09a5b7a fix: read skills disabled state from active profile 2026-05-29 23:21:48 +00:00
ai-ag2026 017179b189 fix: speed up session index metadata refresh 2026-05-29 23:21:48 +00:00
nesquena-hermes 11ba36beb9 Merge PR #3135 into stage-batch41
# Conflicts:
#	CHANGELOG.md
#	api/routes.py
2026-05-29 22:15:46 +00:00
nesquena-hermes 5c96764e3d Merge PR #3099 into stage-batch41
# Conflicts:
#	CHANGELOG.md
2026-05-29 22:13:08 +00:00
nesquena-hermes 9ca0bdccc8 Merge PR #3098 into stage-batch41
# Conflicts:
#	CHANGELOG.md
2026-05-29 22:12:43 +00:00
nesquena-hermes 3206aec3f3 Merge PR #3138 into stage-batch40
# Conflicts:
#	CHANGELOG.md
2026-05-29 22:01:06 +00:00
AJV20 3c2f98eb23 fix: polish WebUI assistant replies 2026-05-29 13:01:14 -04:00
AJV20 ce82ea3fdf fix: add joplin search auth compatibility 2026-05-29 12:36:27 -04:00
AJV20 35f89c4e5b fix: harden external notes guardrails 2026-05-29 12:30:04 -04:00