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