Commit Graph

2051 Commits

Author SHA1 Message Date
Rod Boev 51a0fca24a fix(#5092): continue WebUI goals after gateway turns 2026-06-30 05:04:32 +00:00
nesquena-hermes 468b61e513 fix(auth): release STREAM_SESSION_OWNERS on worker early-return + stale-run prune
Gate hardening for #5198: the new stream-owner map leaked entries on three
paths that bypass the teardown finally — worker early-return when the stream
was cancelled before startup (streaming.py, gateway_chat.py) and the direct
ACTIVE_RUNS stale-zombie prune (routes.py). Each now calls unregister_stream_owner.
Adds a regression test proving the early-return path no longer leaks.

Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-06-30 04:39:00 +00:00
Hermes Agent fabd461dcd fix(auth): guard profile-owned stream IDs 2026-06-30 04:28:15 +00:00
Hermes Agent a993fee060 docs(auth): document request session guard scope 2026-06-30 04:28:15 +00:00
Hermes Agent f26896f96a refactor(auth): remove unreachable chat retag branch 2026-06-30 04:28:15 +00:00
Hermes Agent 9e861b5651 fix(auth): tolerate query-less route inputs 2026-06-30 04:28:15 +00:00
Hermes Agent c068dbc39b fix(auth): enforce active-profile session IDs 2026-06-30 04:28:15 +00:00
hinotoi-agent 41ec8238ba chore: log file manager profile denials 2026-06-30 04:12:27 +00:00
hinotoi-agent beb39f1ef2 fix: scope file manager sessions to active profile 2026-06-30 04:12:27 +00:00
Rod Boev 07a8c7dc6f fix(session): preserve kept tool rows on ambiguous truncate (#5134) 2026-06-30 01:40:07 +00:00
Rod Boev e9387f8aa5 fix(session): fail closed on ambiguous truncate context matches (#5134) 2026-06-30 01:40:07 +00:00
Rod Boev 02a3750488 fix(session): keep truncate rewind on complete turns (#5134) 2026-06-30 01:40:07 +00:00
Rod Boev 3627c9e5d8 fix(session): drop interleaved tool rows after mid-history truncate (#5134) 2026-06-30 01:40:07 +00:00
b3nw da43de72b8 fix(model): repair bare custom-provider suffix on resolver slow path (#5225)
Mirror the #5128 fast-path suffix repair when profile context is present
but requested_provider is omitted, which async delegation completion can
trigger via process_wakeup.

Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-06-30 01:01:17 +00:00
Jamie (Hermes) 0674a0198c perf(sessions): page archived sidebar rows 2026-06-29 22:24:35 +00:00
Rod Boev affdb40a1c fix(config): route stale model reads through shared reload gate (#5220) 2026-06-29 20:50:34 +00:00
Rod Boev 6653b7bd2f fix(config): collapse post-restart config reload stampede (#5220) 2026-06-29 20:50:34 +00:00
Brett Yelverton 0c338711df feat: add Neuralwatt provider support (env var mapping) 2026-06-29 20:41:16 +00:00
Frank Song 47d8ac94d3 Harden connection flapping aftercare
Keep sidebar retries boot-scoped, preserve immediate source-change cache rebuilds, and tighten session stream ownership before closing duplicate SSE sockets.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-29 19:52:02 +00:00
Frank Song cbb644da69 Reduce connection lost flapping under large session state 2026-06-29 19:52:02 +00:00
AnhNVT 9ad1fa0372 fix(redaction): memoize deterministic API response redactor
The combined credential redactor (~15 regex passes per string) runs on the
full message history of every /api/session response. Under concurrent
dashboard polling of unchanged sessions this became the dominant CPU cost
and, behind the GIL in the single-process server, serialized requests to
10-44s each — surfacing as repeated 'connection lost' in the browser as
the proxy/browser timed out and the frontend retried, piling on threads.

The redactor is pure and deterministic (force=True, fixed masking), so
identical strings always map to identical output and are safe to memoize
without invalidation. Wrap it in an lru_cache(maxsize=4096), gated to
strings <=16KB so a few giant tool-output dumps can't evict the many small
recurring strings or balloon RSS. Repeated polls become O(1) cache hits
(measured 3.7ms -> 0.002ms per 3KB marker string).

_redact_fn_cached is preserved as the call-site name so the existing
monkeypatch-based redaction tests bypass the cache unchanged (69/69 pass).
2026-06-29 18:22:18 +00:00
wangyichen 17f2d898f3 fix: retry os.replace() on Windows WinError 5 (PermissionError)
Windows locks session JSON files briefly during antivirus scans or
browser polling, causing os.replace() to raise WinError 5
(ERROR_ACCESS_DENIED). Add _safe_replace() helper that retries with
exponential backoff (50ms to 800ms, max 5 attempts) on PermissionError.
No-op on non-Windows platforms.

Replaces all 5 os.replace() call sites in models.py:
- Session.save() (main file + backup)
- _write_session_index() (full rebuild + fast path)
- prune_session_from_index()
2026-06-29 17:53:41 +00:00
allenliang2022 ada8c262ca fix(webui): render server-initiated turns in a hidden tab without a refresh
A turn started SERVER-SIDE (self-wake, cron, restart hook) fans a
`server_turn_started` frame onto the per-session live-view SSE channel so an
open tab renders it live. But while a tab is HIDDEN the WebUI deliberately does
NOT hold that persistent SSE open (connection-pool budget — #3992 / #4151), so
`get_session_channel()` returns None at fan-out time and the turn is dropped for
that tab. The user only saw the turn after a manual interaction (refresh / send)
re-subscribed the channel and replayed it.

Bridge the gap with a lightweight poll of `/api/session/status` (one short GET
per ~6s tick — NOT a held connection, so the connection-pool budget the hidden
guard protects is preserved) that attaches the existing live renderer when it
sees a *live* active stream.

Backend (`api/session_ops.py`):
- `session_status()` now exposes `active_stream_id`, derived through a new
  `_live_active_stream_id()` helper that only returns the id when the stream is
  genuinely live (present in STREAMS / ACTIVE_RUNS). A stale id left by a
  crashed/restarted run surfaces as None, so the poller never attaches a
  renderer to a dead stream. Additive field — existing consumers ignore it.

Frontend (`static/messages.js`):
- `_startHiddenActiveStreamPoll` / `_stopHiddenActiveStreamPoll` /
  `_attachServerInitiatedStream` implement the poll lifecycle. The poll fires
  one immediate tick on hide (so an in-flight turn is caught without waiting a
  full interval), attaches mid-flight turns via the reconnecting/replay path,
  and stops on re-show (the real SSE takes over), session switch, or once a
  stream is rendering.
- Started on BOTH hidden paths: the `visibilitychange` hook's hidden branch (a
  visible tab going to background) and `startSessionStream`'s hidden
  early-return skip (a session opened while already hidden). The visible path
  and `stopSessionStream` clear it.

Tests:
- `tests/test_hidden_tab_server_initiated_turn.py` — backend live-validation
  (stale id → None; STREAMS/ACTIVE_RUNS id returned) and frontend lifecycle /
  both-hidden-paths / replay-attach source locks.
- Widened the brittle fixed-width source-window slices in
  `tests/test_issue3996_sse_visibility.py` (1600/1700 → 2400) so the existing
  `visibilitychange` / hidden-skip assertions still find their markers after the
  poll-start lines were inserted into `startSessionStream`.
2026-06-29 16:28:30 +00:00
santastabber 178db13512 fix: authorize session html media artifacts 2026-06-29 05:17:33 +00:00
Rod Boev a052f390f8 fix(updates): resolve git under macOS launchd (#5175) 2026-06-29 04:22:51 +00:00
Rod Boev 9d3f26a718 refactor(updates): drop unreachable agent restart fallback branch (#5156) 2026-06-29 03:44:06 +00:00
Rod Boev 31a443ff88 fix(health): preserve restart failure contract and proofs (#5156) 2026-06-29 03:44:06 +00:00
Rod Boev 6d80dd41d6 fix(updates): restart gateway after in-app agent update (#5156) 2026-06-29 03:44:06 +00:00
claw-io 159298d319 fix(profiles): use profile-scoped resolver for /api/profile/active default_workspace (Codex gate #5169)
Codex flagged that get_last_workspace() falls back to the GLOBAL last_workspace.txt
before the profile config.yaml — so a named profile without its own
last_workspace.txt would leak the global path, re-introducing the #5169 bug.
Add get_profile_default_workspace() (profile last_workspace.txt -> config.yaml ->
terminal.cwd -> default, NO global fallback) and call it from /api/profile/active.
Adds a regression test: global last_workspace.txt present + named profile with
only config.yaml workspace must return the profile path, never the global.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-06-29 01:45:17 +00:00
claw-io ec0ef4a0d3 fix(profiles): show active profile workspace on blank new-chat (#5169)
Expose a profile-scoped default_workspace on GET /api/profile/active (resolved
by the profile-aware get_last_workspace(), fail-open) and hydrate boot.js from
it, overriding the global GET /api/settings value. GET /api/settings stays
global-only so the settings UI can't clobber the shared default. Combines
claw-io's #5173 (primary) with the fail-open backend hardening + regression test
from #5171.

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-06-29 01:25:58 +00:00
nesquena-hermes c344844766 fix(#5132): split state.db sidebar override — source uncapped, count capped
Codex deep-gate caught a CORE regression in the original top-N cap: capping
_apply_sidebar_state_db_overrides also capped the SOURCE classification that
/api/sessions filters on BEFORE the lazy lineage correction, so a stale-CLI JSON
row whose state.db source is webui could be silently filtered out of a WebUI-only
sidebar once it fell beyond the cap. Split the read: the indexed sessions-table
source/title lookup runs for ALL rows; only the expensive messages COUNT/MAX
aggregation (the actual 5-18s bottleneck) is capped to top-N. Added a 500-row
end-to-end regression with a real state.db proving the beyond-cap stale-CLI row
stays webui.
2026-06-29 00:54:15 +00:00
nesquena-hermes c646d92206 perf(#5132): cap /api/sessions state.db source/title overrides to top-N
On power-user instances with thousands of sessions, GET /api/sessions
blocked 5-18s in the all_sessions.state_db_overrides stage:
_apply_sidebar_state_db_overrides read state.db for every row (2400+ ids)
on every concurrent poll. With the gateway-watcher reading and the agent
writing the same state.db, this became lock/IO contention that piled up
concurrent polls and flapped the UI to 'Connection lost'.

Cap the override lookup to the top-N most-recent (paint-priority) rows
that are actually visible, mirroring the existing lineage-enrichment cap
(#4638). The caller passes an already pinned-first/newest-first sorted
list, so the top-N covers the visible window; rows beyond the cap keep
their JSON source/title and are corrected lazily on history-panel open.
Cap defaults to 300, env-configurable via
HERMES_WEBUI_STATE_DB_OVERRIDE_TOP_N (0 disables). Fails open.

Reported by @latipun, reproduced by @b3nw.

Co-authored-by: latipun <latipun@users.noreply.github.com>
2026-06-29 00:40:10 +00:00
santastabber 5ed0ba6ef6 fix(sidebar): keep hidden archived parents as child references 2026-06-29 00:13:32 +00:00
nesquena-hermes 4d79de266c Merge master into stage-5155 (catch up to v0.51.728) 2026-06-28 23:25:57 +00:00
nesquena-hermes f968de8d35 merge #5155 extension settings surface 2026-06-28 23:07:00 +00:00
nesquena-hermes 9736e943bb merge #5151 distinguish offline gateway approval warnings 2026-06-28 22:54:54 +00:00
nesquena-hermes 751ba20fec merge #5129 surface provider auth failures in chat 2026-06-28 22:54:54 +00:00
nesquena-hermes 1a4dcd4fb5 merge #5128 custom-provider bare-model wakeup repair 2026-06-28 22:54:54 +00:00
Rod Boev 234fefe6ee fix(#5139): keep timeout capability probes on the unsupported path 2026-06-28 18:29:37 -04:00
Rod Boev aef8e524fd fix(extensions): keep settings-only installs canonical (#5094) 2026-06-28 18:25:47 -04:00
Rod Boev 23b7a7935a fix(#5139): keep older gateways on the unsupported warning path 2026-06-28 18:18:47 -04:00
Rod Boev 938f721b16 fix(extensions): reload before editing untrusted installs (#5094) 2026-06-28 18:07:52 -04:00
Rod Boev 615b023eb0 fix(extensions): pin schemas to trusted bootstrap state (#5094) 2026-06-28 17:59:36 -04:00
Rod Boev 343a8b50bf fix(#5139): distinguish offline gateway approval warnings 2026-06-28 17:53:37 -04:00
b3nw 7a04b7e9aa fix: gate bare-model repair on profile provider match (#5128 review)
When session model_provider differs from profile model.provider, do not
expose profile_default_model for suffix repair. Fast path also requires
profile_provider to match requested_provider when profile_provider is set.

Addresses cross-provider mis-resolution (custom:other-proxy + bare suffix).

Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-06-28 21:52:06 +00:00
Rod Boev cb96a5cf39 fix(extensions): trust only bootstrapped extension ids (#5094) 2026-06-28 17:51:42 -04:00
Rod Boev 07ed67a793 fix(extensions): keep runtime settings gated on owned storage (#5094) 2026-06-28 17:44:02 -04:00
Rod Boev 62be5b02ea fix(#5135): clear stale gateway approval mirrors on stream teardown 2026-06-28 17:32:55 -04:00
Rod Boev a76c794d6d feat(extensions): add browser-local extension settings surface (#5094) 2026-06-28 17:28:20 -04:00
nesquena-hermes fb7b16e723 Merge branch 'pr-5131-head' into stage/5131-cron 2026-06-28 20:51:44 +00:00