Commit Graph

1767 Commits

Author SHA1 Message Date
nesquena-hermes a10f511a13 fix(#4561): catch RecursionError on deeply-nested manifest (BRICK)
Codex+Opus ship-gate: a <=64KB but deeply-nested JSON manifest makes json.loads raise
RecursionError, which escaped _read_manifest_urls into the app-shell route -> every page
load 503s. Add RecursionError to the fail-safe except + regression test (verified
red-without/green-with). Co-authored-by: santastabber <santastabber@users.noreply.github.com>
2026-06-21 00:42:58 +00:00
nesquena-hermes 3e229141af stage #4561 + stamp v0.51.545 — extension manifest asset bundles
santastabber. HERMES_WEBUI_EXTENSION_MANIFEST: bundle extension assets via a 64KB JSON manifest
instead of long URL lists. Same same-origin allowlist validator, traversal-guarded manifest path,
default-off. Nathan blessed (richer extension system). Co-authored-by: santastabber <santastabber@users.noreply.github.com>
2026-06-21 00:37:08 +00:00
nesquena-hermes 2471e277cb rebase #3933 + stamp v0.51.544 — opt-in render markdown in user messages
rodboev. Default-off; OFF path byte-identical to master (cache key 'u' + _renderUserFencedBlocks
unchanged when window._renderUserMarkdown falsy). ON routes user text through the same renderMd
SAFE_TAGS/_isSafeUrl sanitizer as assistant msgs. Nathan override-approved (likes the idea, opt-in).
Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-21 00:03:02 +00:00
nesquena-hermes 81a4945be8 harden(#4411): read-side anchor-scene ref-ambiguity guard (Opus finding)
Mirror the write-side _find_anchor_scene_message ambiguity guard on the read side:
_hydrate_anchor_activity_scenes now counts ref occurrences and skips ref-based
attachment for any ref resolving to >1 assistant message (falls through to the
positional index match), so a duplicate content-digest ref can't double-render the
same worklog scene. Low-severity defense-in-depth on the crown-jewel render surface.
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-20 22:05:01 +00:00
nesquena-hermes 001148d208 fix(#4411): guard /api/session/anchor-scene against cross-profile writes
Codex ship-gate (CORE): _handle_session_anchor_scene loaded the session via
_get_or_materialize_session (by-id, no profile scoping) and persisted
anchor_activity_scenes without the active-profile visibility check GET /api/session
applies — an authenticated request under profile A could write anchor scenes onto a
profile-B session. Add the _session_visible_to_active_profile guard (404, no write),
mirroring routes.py:~8922. Regression test verified red-without/green-with the guard.

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-20 22:02:56 +00:00
nesquena-hermes 6951d10ae5 rebase #4411 onto master (v0.51.542) for deep-review — Stable Assistant Turn Anchors capstone
franksong2702's activity_scene_v1 anchor-scene model: live-stream/settled/refresh/re-entry
show the same ordered turn. Rebased clean (no conflicts) onto current master.
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-20 21:48:55 +00:00
nesquena-hermes acd57d847d rebase #4413 re-pushed head (553e9584) for re-gate
Converged both bounce items: enrich-existing-only (continue on webui_list-is-None,
no new providers -> routing side-effect gone, routes.py no longer touched) + respects
per-provider ID prefix convention (@nous:). Co-authored-by: kaishi00 <kaishi00@users.noreply.github.com>
2026-06-20 21:22:53 +00:00
nesquena-hermes 85e3be6a42 fix(#4555): preserve /api/models aliases contract on disk-cache fallback
Codex ship-gate (SILENT): both disk loaders dropped `aliases` (the save path
never persisted them), so a disk-cache hit — strict cold-path OR the new stale
fallback — served /api/models with empty aliases, silently breaking `/model
<alias>` slash-command resolution (static/commands.js resolves slash aliases
only from /api/models.aliases). Add _model_aliases_from_config() helper and
reconstruct aliases from current config in BOTH loaders (fixes the pre-existing
strict-loader gap too). Adds a regression test.

Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-06-20 19:25:51 +00:00
nesquena-hermes fbafe1e623 harden(#4555): isolate stale-cache path in test fixture + enforce schema-version equality on stale fallback
Applies Opus ship-gate findings: (1) the autouse fixture now points
_get_models_cache_path at an isolated temp file so the over-budget static-fallback
test can't read a real ~/.../models_cache.json and become an order-dependent flake;
(2) _load_stale_models_cache_from_disk rejects a cross-schema cache (its groups/badge
shape may be incompatible with the current picker). Adds a cross-schema-rejection test.

Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-06-20 19:19:52 +00:00
Hermes Agent 3d8f3a1f7b fix(models): use stale disk cache on catalog timeout 2026-06-20 19:11:17 +00:00
Rod Boev ee588ba70e fix(#4550): harden gateway reasoning forwarding follow-up 2026-06-20 18:44:25 +00:00
Rod Boev 26c55404a5 fix(#4550): forward gateway reasoning effort 2026-06-20 18:44:25 +00:00
jja881 53affd6936 fix: address review feedback — remove redundant clause, add guard and tests
- Remove the redundant 4th OR clause from _catalog_has_provider() — the
  existing C1–C3 already handle compound provider IDs correctly (C2 IS
  a normalized-vs-raw comparison; the 4th clause was a strict subset).
- Add && _activeProvider guard to _isCrossProviderPick in messages.js
  to prevent spurious True when the profile has no configured default
  provider (reviewer's optional hardening suggestion).
- Add test_catalog_has_provider_compound_ids.py verifying that compound
  provider IDs like 'custom:glm-free-relay' are correctly recognized by
  the existing C1–C3 clauses.
2026-06-20 18:44:25 +00:00
jja881 96265f6ae4 fix: preserve cross-provider model selection across conversation turns
Two-part fix for the model selector jumping back to the profile default
after the first message when a cross-provider model (e.g. a custom relay
like custom:glm-free-relay) is selected.

Backend (api/routes.py):
- _catalog_has_provider() now matches compound provider IDs (e.g.
  custom:zenmux-relay) whose normalized form (custom) is a raw
  catalog provider_id.  Previously the three existing checks missed
  normalized-vs-raw comparison for compound hints, causing the catalog
  lookup to fail and the fallback branch to silently revert the model
  to the profile default on every non-explicit turn.

Frontend (static/messages.js):
- The explicit_model_pick marker was consumed after the first send(),
  so subsequent sends lost the signal and the server repaired the
  model back to the profile default (#3737 regression).
- Now, when the session has a non-default model from a different
  provider than the profile's active provider, every send() sends
  explicit_model_pick=true so the server honors the user's choice
  across the entire conversation.
- The pending onchange marker is still consumed after the first send
  (unchanged behavior); only the explicit_pick inference is broadened.
2026-06-20 18:44:25 +00:00
MinhoJJang 630fd26eec fix configured provider slash model routing 2026-06-20 17:42:18 +00:00
Rod Boev 6ae0ecca0a fix(#4535): include session_id in Runs API request body 2026-06-20 17:42:18 +00:00
nesquena-hermes 5020f8de9a fix: record gateway run_id on legacy approval event for response relay
The legacy /v1/chat/completions approval handler (added for #4549) rendered
the approval card but never populated _STREAM_RUN_IDS, so /api/approval/respond
could not relay the user's choice back to the gateway to resume the parked run
— it fell through to the local approval path and returned {ok: false}. Record
the run_id from the approval payload (no-op when absent), mirroring the runs-API
path. Adds a behavioral regression test that fails on the pre-fix head.

Codex ship-gate finding (CORE). Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-20 17:26:46 +00:00
Rod Boev 0435dddda1 Move approval handler before tool.progress per GPT-5.5 spec review 2026-06-20 08:59:52 -04:00
Rod Boev 9bc84dcad5 Unify approval handler blocks and add debug logging per review 2026-06-20 08:48:55 -04:00
Rod Boev f19b0ffc67 fix: relay approval SSE events on the legacy gateway chat/completions path 2026-06-20 08:32:02 -04:00
nesquena-hermes 15fe6add30 stage #4524: clarify 409-terminal (id-guarded, loading-cleared) + SSE-notify on expiry (#4504) + v0.51.534 CHANGELOG 2026-06-20 05:36:56 +00:00
nesquena-hermes 90505c0a8e stage #3285: gateway restart from agent-health alert (POST /api/health/restart) + v0.51.533 CHANGELOG 2026-06-20 04:40:49 +00:00
nesquena-hermes e1e2bbc7ef fix(config): order ghost-home guard after ambient short-circuit (#4516 Codex gate)
Codex SILENT finding: the new 'if not target.exists(): return {}' guard ran
BEFORE the ambient-resolver short-circuit, so a matching ambient profile home
whose directory does not physically exist (fresh install / monkeypatched cfg)
returned {} instead of deferring to get_config() — breaking in-memory overrides
and changing the default-profile missing-home path. Move the guard below the
ambient short-circuit; add a regression test (matching-but-nonexistent ambient
home still defers to get_config()).
2026-06-20 03:57:56 +00:00
nesquena-hermes acd2c186ef stage #4516: apply config defaults to non-default profile reads (#4465 / #4513) + v0.51.532 CHANGELOG 2026-06-20 03:51:55 +00:00
nesquena-hermes a60ba78298 stage #4517: plugins panel active-provider badge via is_active_provider (#4496) + v0.51.531 CHANGELOG 2026-06-20 03:35:44 +00:00
nesquena-hermes 51e2fce823 stage #4522: fix /api/profiles UnboundLocalError 500 (regression from v0.51.528) + v0.51.530 CHANGELOG 2026-06-20 03:18:33 +00:00
nesquena-hermes 0952fcc606 fix(routes): drop redundant local _is_isolated_profile_mode import (#4454 gate)
The /api/profiles handler re-imported _is_isolated_profile_mode locally inside
handle_get, which made the name function-local for the ENTIRE handle_get scope
-> the earlier use at the /api/projects branch (routes.py:8000) hit
UnboundLocalError (F823), failing 6 test shards + lint. It's already a
module-level re-export (routes.py:429), so the local re-import is redundant;
removing it lets every in-function use resolve to the module global. Also drop
an unused 'from pathlib import Path' in the new #4449 state-dir test.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-20 00:48:20 +00:00
Rod Boev e77ca8dbac fix(#2698): keep the pinned home across isolated fallbacks 2026-06-20 00:48:20 +00:00
Rod Boev c7375c60c4 fix(#2698): clamp isolated profile lookups to the pinned home 2026-06-20 00:48:20 +00:00
Rod Boev 41f2015a32 fix(#2698): close isolated profile escape hatches 2026-06-20 00:48:20 +00:00
Rod Boev f086158c10 fix(#2698): keep isolated deployments on their own state and profile roots 2026-06-20 00:48:19 +00:00
Rod Boev c00bfab9f5 fix(#2698): keep isolated default-named profiles pinned 2026-06-20 00:48:19 +00:00
Rod Boev 498a5e73e3 fix(#2698): pin isolated runtime home to configured profile 2026-06-20 00:48:19 +00:00
Rod Boev 8da03dbbad Use HERMES_HOME path directly for isolated profile home resolution 2026-06-20 00:48:19 +00:00
Rod Boev 0291af5f85 Derive isolated profile name from HERMES_HOME path, not process-level default 2026-06-20 00:48:19 +00:00
Rod Boev a2ca2ff0c2 fix(profiles): snapshot HERMES_HOME at import time for isolation detection (#2698) 2026-06-20 00:48:19 +00:00
Rod Boev 35eced6f35 fix(profiles): broaden except clause and sanitize error in isolated mode (#2698) 2026-06-20 00:48:19 +00:00
Rod Boev 36dbe1a415 fix(profiles): return 403 for isolated-mode guards, fix fallback identity (#2698) 2026-06-20 00:48:19 +00:00
Rod Boev 963f7c3864 fix(profiles): honor isolated HERMES_HOME profile mode (#2698) 2026-06-20 00:48:19 +00:00
nesquena-hermes 9ade727e63 stage #4494: expose built-in personalities in WebUI config (#4465) + v0.51.525 CHANGELOG 2026-06-19 22:26:30 +00:00
nesquena-hermes 580a157ab1 stage #4447: opt-in chunked SSE for buffering proxies (#4447) + v0.51.524 CHANGELOG 2026-06-19 22:09:17 +00:00
nesquena-hermes 0fe707b2bf fix(wiki): reject hardlinked page files in allowlist + read revalidation (#4375 Codex gate)
Gate-found (Codex CORE, repro'd): a hardlink at a clean *.md page name can
carry an arbitrary inode (incl. one outside the wiki) through the O_NOFOLLOW +
inode-identity read check — O_NOFOLLOW does not block hardlinks. Reject any
page file with st_nlink > 1 in BOTH the allowlist walk and the read-path
revalidation. Pre-existing on master (the inode recapture predates this PR's
cache); closing it here while on the read path. +1 toothed regression test
(prime cache -> hardlink listed page to outside secret -> assert 404, no leak).

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-19 21:11:11 +00:00
Rod Boev 0c4894efe9 fix(wiki): lstat log fallback before reading headings (#4375) 2026-06-19 21:11:11 +00:00
Rod Boev 9242f1facb fix(wiki): bind status metadata to verified file identities (#4375) 2026-06-19 21:11:10 +00:00
Rod Boev 82fd7d0166 fix(wiki): exclude unverified status-file metadata (#4375) 2026-06-19 21:11:10 +00:00
Rod Boev 8e4d390366 fix(wiki): reject post-snapshot metadata races (#4375) 2026-06-19 21:11:10 +00:00
Rod Boev 9a75a85d59 fix(wiki): keep status and browse metadata on verified identities (#4375) 2026-06-19 21:11:10 +00:00
Rod Boev 0542d91824 fix(wiki): recheck status reads against allowlisted identity (#4375) 2026-06-19 21:11:10 +00:00
Rod Boev db0bcaae8f fix(wiki): drop stale cached paths that no longer point to files (#4375) 2026-06-19 21:11:10 +00:00
Rod Boev f93d1332df fix(wiki): align browse and status with page-path rules (#4375) 2026-06-19 21:11:10 +00:00