Commit graph

2178 commits

Author SHA1 Message Date
nesquena-hermes
8fe2d63ec0 Merge remote-tracking branch 'origin/master' into release/stage-p1c
# Conflicts:
#	CHANGELOG.md
2026-07-03 20:44:31 +00:00
nesquena-hermes
797eb4a1b9 Merge PR #5464 from ai-ag2026: open session-listing projection read-only (#5455) 2026-07-03 20:16:35 +00:00
nesquena-hermes
8120e69e1d Merge PR #5442 from rodboev: persist TTS and voice preferences server-side (#5435) 2026-07-03 20:04:55 +00:00
nesquena-hermes
f2ab07757f ship-pass: #5453 overflow-path regression test + drop #5449 dead comment
- Add test_profile_model_selection_accepts_model_in_overflow_extra_models
  (the extra_models overflow-bucket regression test the warm dossier flagged
  as the #5453 ship-pass note).
- Drop the stray dead pseudo-code comment left in handle_post branch route
  (#5449 cosmetic nit).
2026-07-03 19:45:37 +00:00
nesquena-hermes
00e7203ec8 Merge PR #5449 from rodboev: hide read-only cron fork affordances (#5439) 2026-07-03 19:44:33 +00:00
TARS
10ecfbb376 fix: encode read-only session DB URI 2026-07-03 20:04:41 +02:00
TARS
855b7f1ea4 perf(sessions): open the session-listing projection read-only (#5455)
read_importable_agent_session_rows() is a pure read, but it opened a
read-write sqlite connection on the live multi-GB WAL state.db and re-ran a
defensive CREATE INDEX self-heal on every sidebar build. Holding a
write-capable handle while the agent streams into the same DB adds needless
checkpoint/lock surface.

Open the DB read-only (file:...?mode=ro) for the projection, and self-heal a
missing idx_messages_session through a separate short-lived writable
connection only. With the index present (the normal case) the read path
performs zero writes; when it is missing the self-heal still runs and rows
still come back.

Behavior-neutral: identical rows when the index exists; self-heal preserved
when missing; read-only-fs degradation to the pre-aggregated path unchanged.

Tests: tests/test_issue5455_listing_readonly_connection.py (read-only open,
no writable connection when index present, self-heal when missing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 18:41:49 +02:00
nesquena-hermes
654daf8f2a fix(profiles): include extra_models in profile model validation
Clean rebase of WallaceWebster's #5453 (rebase-first).

Co-authored-by: WallaceWebster <WallaceWebster@users.noreply.github.com>
2026-07-03 03:26:25 +00:00
Rod Boev
fdacb4b922 fix(#5435): stop startup workspace repair from persisting speech defaults 2026-07-02 21:28:46 -04:00
nesquena-hermes
0735a1b853 fix(#5439): hide read-only cron fork affordances
Clean rebase of rodboev's #5449 (rebase-first).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-03 00:58:30 +00:00
Rod Boev
8f7b9c1d0d fix(#5435): drop dead persisted-key state from config load 2026-07-02 19:38:35 -04:00
Rod Boev
3b344b1d9a fix(#5435): preserve local speech prefs until settings.json owns them 2026-07-02 19:33:13 -04:00
nesquena-hermes
2dcf7f529e Merge #5437: harden ElevenLabs TTS transport guards (#5430) [rodboev] 2026-07-02 21:35:42 +00:00
nesquena-hermes
8689c40995 Merge #5408: self-hosted provider setup (Ollama/LM Studio) in Settings (#3260) [rodboev] 2026-07-02 20:20:12 +00:00
Rod Boev
4993eed120 refactor(#5430): reuse shared TTS transport scaffolding 2026-07-02 15:43:42 -04:00
Rod Boev
91ae1dede9 fix(#5435): persist TTS and voice preferences server-side 2026-07-02 15:43:24 -04:00
Rod Boev
fc37919952 fix(#5430): harden ElevenLabs TTS transport guards 2026-07-02 15:10:01 -04:00
nesquena-hermes
bf1ca227dd fix(tts): block non-globally-routable addrs (CGNAT/Tailscale 100.64.0.0/10) via not-is_global backstop + tests (Opus follow-up on #5407) 2026-07-02 18:04:28 +00:00
nesquena-hermes
24eb618571 Merge #5407: pin OpenAI TTS proxy connect target vs DNS-rebind TOCTOU (#5291) [rodboev] 2026-07-02 17:45:04 +00:00
nesquena-hermes
b184588e8a Merge #5424: Bearer auth on remote /health/detailed probe (#5418) [nankingjing] 2026-07-02 17:07:51 +00:00
nesquena-hermes
32baf1302b fix(routes): unblock session/new after profile switch (#5420)
Clean rebase of nankingjing's #5423 (rebase-first).

Co-authored-by: nankingjing <nankingjing@users.noreply.github.com>
2026-07-02 16:34:37 +00:00
nesquena-hermes
4d7fad06e6 fix(health): send Bearer auth on remote /health/detailed probe (#5418)
Clean rebase of nankingjing's #5424 (rebase-first).

Co-authored-by: nankingjing <nankingjing@users.noreply.github.com>
2026-07-02 16:24:39 +00:00
nesquena-hermes
56889fa35e fix(#5291): pin OpenAI TTS proxy + iterate vetted RRset (DNS-rebind TOCTOU)
Clean rebase of rodboev's #5407 (rebase-first).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-02 14:38:09 +00:00
nesquena-hermes
7220747a1d feat(#3260): self-hosted provider setup (Ollama/LM Studio) in Settings (i18n complete)
Clean rebase of rodboev's #5408 (rebase-first).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-07-02 08:17:44 +00:00
nesquena-hermes
b931ae2f9b fix(settings): reconcile #5145 rename+steer-flip onto master's #5170 mirror
Rebase PR #5162 (rename busy_input_mode -> default_message_mode; flip the
default from 'queue' to 'steer') onto current origin/master WITHOUT dropping
the shipped #5170 localStorage persistence mirror.

Rename the mirror machinery to the new setting name for consistency:
  _BUSY_INPUT_MODES        -> _DEFAULT_MESSAGE_MODES        (values unchanged)
  _normalizeBusyInputMode  -> _normalizeDefaultMessageMode  (fallback now 'steer')
  _persistBusyInputMode    -> _persistDefaultMessageMode
  _readPersistedBusyInputMode -> _readPersistedDefaultMessageMode
  window._busyInputMode    -> window._defaultMessageMode (+ renamed exports)

localStorage: write the new 'hermes-default-message-mode' key; read it with a
fallback to the legacy 'hermes-busy-input-mode' key so an existing user's
persisted preference survives the rename.

Preserve #5170 behavior at every mirror site under the new names:
  - boot success  -> window._defaultMessageMode=_persistDefaultMessageMode(...)
  - boot FAILURE  -> window._defaultMessageMode=_readPersistedDefaultMessageMode()
    (NOT a hardcoded 'steer' — a saved 'interrupt'/'queue' must still apply when
    the server is unreachable; do not regress #5167/#5132)
  - preferences autosave, settings-panel load, and _applySavedSettingsUi all
    persist through _persistDefaultMessageMode(...)

Tests updated for the rename while keeping the persistence-behavior assertions
(test_1062, test_5145, test_5167); test_5167 gains explicit guards that the
load-failure path reads the persisted pref and never hardcodes a literal mode,
plus autosave/panel-load mirror-write coverage.

Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-07-02 05:44:48 +00:00
Rod Boev
8e398cd1b3 fix(cron): stop auto-creating the Cron Jobs project without project opt-in (#5379) 2026-07-02 04:36:45 +00:00
nesquena-hermes
b31f3fab3b #4968: export chat to self-contained themed HTML, rebased on master 2026-07-02 02:36:09 +00:00
nesquena-hermes
4bbb64bcc1 #5213: Claude Code sidebar visibility toggle (#4714), rebased on master 2026-07-02 01:40:19 +00:00
nesquena-hermes
76d6cc7f74 #5142: office-doc preview + safe docx editing (#540), rebased on master; optional deps 2026-07-02 01:15:57 +00:00
nesquena-hermes
728caf5ec6 #4682: surface read-only other-profile cron jobs in Tasks panel (#3947), rebased on master 2026-07-02 00:44:24 +00:00
nesquena-hermes
b709ae6fc2 #5228: require browser provenance on all proxy methods (close GET/POST asymmetry from gate); CHANGELOG 2026-07-02 00:23:10 +00:00
nesquena-hermes
3d99763f3a #5228: opt-in extension loopback proxy (#4747), rebased on master; union urllib imports 2026-07-01 23:49:25 +00:00
nesquena-hermes
9fb2731848 #5310: push-to-talk hold gesture (#3700) 2026-07-01 23:26:15 +00:00
nesquena-hermes
36643867f4 #5153: MoA gateway fail-closed routing 2026-07-01 23:24:38 +00:00
nesquena-hermes
d43618beca stage #5301 2026-07-01 22:42:35 +00:00
nesquena-hermes
c191141615 fix #5368 2026-07-01 21:35:11 +00:00
nesquena-hermes
2574e6bef9 stage #5317 2026-07-01 21:10:21 +00:00
nesquena-hermes
4a3b820e22 stage #5370 2026-07-01 21:06:10 +00:00
Rod Boev
f3c01812b7 fix(#4251): drop dead post-repair ownership writes 2026-07-01 16:20:31 -04:00
Rod Boev
a609b99e76 fix(#4251): guard provider ownership under the session lock 2026-07-01 16:19:53 -04:00
b3nw
a477069e3a fix(model): address gate feedback #4857080754 — slug names, list models, get_config, single YAML parse
- _repair_bare_custom_provider_model matches display-named providers via
  _custom_provider_slug_from_name (custom:my-proxy matches 'My Proxy').
- _ordered_custom_provider_model_ids now handles dict keys, list strings,
  and list dicts with id/model/name, aligned with api/config.py catalog.
- config_obj=None fallback uses get_config() instead of raw cfg.
- _read_profile_model_config returns profile config dict too, avoiding a
  second YAML parse on hot display paths.
- Tests added for slug matching, list-form models, and get_config fallback.

Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-07-01 20:12:38 +00:00
Rod Boev
5c7ff9c70e fix(#4251): preserve raced picker ownership through profile repair 2026-07-01 15:42:57 -04:00
nesquena-hermes
ab38ffe25f Fix profile skills-stats thundering herd at cold startup (#5364)
The two-tier mtime cache from #4783 fixed the per-request SKILL.md rescan
but left two concurrency holes that only bite at container cold start,
when the frontend fires several profile-data requests at once and the
caches are empty:

1. `_get_profile_skills_stats()` had no lock, so concurrent misses on the
   same profile each ran `os.walk(followlinks=True)` + parsed every
   SKILL.md simultaneously.
2. `_build_profile_rows_fast()` ran outside `_LIST_PROFILES_CACHE_LOCK`
   in `list_profiles_api()`, so every concurrent request rebuilt all rows
   (each walking every profile's skill tree) at once.

With ThreadingHTTPServer (one OS thread per request) and Docker overlay2,
this stacked thousands of concurrent stat() calls and stalled workers
57-70s (per the report's thread dumps).

Fix:
- Add a per-profile compute lock (registry guarded by a meta-lock) and
  use double-checked locking in `_get_profile_skills_stats()`: concurrent
  misses on one profile collapse to a single compute, while independent
  profiles still compute in parallel.
- Single-flight the row build in `list_profiles_api()` by holding
  `_LIST_PROFILES_CACHE_LOCK` across the build + cache write. Lock order
  is strictly list-lock -> per-profile skills-lock, so no deadlock.

The report's third suggestion (debounce the mtime probe) is deliberately
NOT taken: the every-call cheap probe is the #4783 out-of-band
change-detection contract (test_issue4783 asserts it MUST run on every
call). Serializing the misses removes the herd without weakening that
contract, since only the expensive compute is guarded, not the probe.

Adds tests/test_issue5364_skills_stats_thundering_herd.py proving the
herd collapses (single compute / single build under a concurrent burst),
independent profiles still parallelize, and the every-call probe contract
is preserved. All existing #4783 contract tests still pass.

Co-authored-by: claw-io <claw-io@users.noreply.github.com>
2026-07-01 19:42:11 +00:00
nesquena-hermes
17b0998c0b stage #5313 2026-07-01 19:35:42 +00:00
Rod Boev
160a397c48 fix(#4251): stop in-flight turns from reverting picker model choice 2026-07-01 15:35:08 -04:00
nesquena-hermes
2de28f5e14 stage #5360 2026-07-01 19:08:24 +00:00
nesquena-hermes
dd9216c770 stage #5357 2026-07-01 18:46:44 +00:00
hermes-agent
741f8fed70 fix(webui): align reconciliation dedup key with workspace-prefix stripping (#5339)
_session_message_content_key (the state.db reconciliation key in
api/models.py) normalized whitespace only, while the streaming-side
identity _message_identity strips the workspace prefix for user turns.
WebUI sends the model a workspace-prefixed user_message
([Workspace::v1: /path]\n<text>) while the visible/optimistic bubble and
sidecar row carry the bare <text>. The mismatch made a prefixed state.db
row and a bare sidecar row key differently, so state_db_delta_after_context
failed to align them, treated the state.db copy as new, and appended a
duplicate user turn. The agent-side merge then concatenated the two
adjacent user rows into a permanent composite -- the post-restart
stale-user-prepend bug.

Fix: strip the workspace prefix for role=='user' in the reconciliation
key, reusing the same _strip_workspace_prefix helper the streaming side
uses (lazy import to avoid the api.streaming -> api.models cycle) so the
two dedup layers can't drift again. Assistant/tool keys are unchanged and
prefix-free user messages key identically (idempotent).

Fixes #5339.
2026-07-01 18:45:18 +00:00
nesquena-hermes
2693a75d89 stage #5354 2026-07-01 18:41:03 +00:00
nesquena-hermes
30d8edc5ea stage #5353 2026-07-01 18:36:29 +00:00