Commit Graph

1786 Commits

Author SHA1 Message Date
nesquena-hermes f7e144d59f Release v0.51.566 — isolated-profile opt-in hardening (#4620) (#4642)
* stage #4620 (franksong2702): snapshot isolated-profile opt-in at import (fixes #4590)

Security/deployment-hardening follow-up to #4586/#4589. Snapshots
HERMES_WEBUI_ISOLATED_PROFILE at module import (_INITIAL_ISOLATED_PROFILE_OPT_IN)
so _isolated_profile_opt_in() reads the startup snapshot, not live os.environ — a
pinned profile .env loaded later can't flip the isolation posture. _reload_dotenv
key filter kept as defense-in-depth. Adds one-time warning when HERMES_HOME has a
profile-dir shape but isolated wasn't opted in at startup. Code byte-faithful from
PR head 5ef14264.

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>

* Release v0.51.566 — Release TY (isolated-profile opt-in hardening; #4620)

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-21 17:12:52 -07:00
nesquena-hermes b4c434fb65 Release v0.51.565 — API-server sidecar prune (#4619) + mic insecure-origin message (#4621) (#4639)
* stage batch #4619+#4621 (franksong2702): API-server sidecar orphan-prune + mic insecure-origin message

#4619 (fixes #4591): widen the #3238 orphan-prune to also reconcile API-server
imported sidecars (api/routes.py _is_api_server_sidecar_row). Webui-source guard
intact, fail-closed probe (agent_session_rows_existing degrades to assume-present).
#4621 (fixes #4571): detect insecure non-local http origins for voice dictation +
hands-free, show mic_insecure_origin message, preflight before capture. mic_insecure_origin
added to all 13 locale blocks (zh-Hant real translation + English TODO fallbacks).
Disjoint files (backend routes vs frontend boot/i18n). Code byte-faithful from PR heads.

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>

* Release v0.51.565 — Release TX (API-server sidecar prune #4619 + mic insecure-origin #4621)

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-21 16:50:32 -07:00
nesquena-hermes 0fd9bf3c37 Release v0.51.564 — loopback sidecar diagnostics (#4612) (#4632)
* stage #4612 (santastabber): surface loopback sidecar diagnostics + CHANGELOG

Settings -> Extensions surfaces manifest-declared loopback sidecar companion services
(type:loopback, http(s) on 127.0.0.1/localhost/::1 only, sanitized origin+health_path)
as read-only health cards w/ a browser-side credentials-omitted health probe. Backend
hard-whitelists scheme + loopback host, rejects userinfo/path/query/traversal, rebuilds
output from parsed components (no echo of rejected input); frontend esc()'s every field,
whitelists badge status, fetch never reads body. Purely additive to GET /api/extensions/status.
Code applied byte-faithful from PR head 7080929f37. 33 own tests pass.
Nathan concept-approved (loopback-companion direction).

Co-authored-by: santastabber <santastabber@users.noreply.github.com>

* fix #4612 Codex gate findings: reject encoded query/fragment in health_path + align CSP loopback allowlist

Codex SHIP-WITH-FIXES (2 reproduced SILENT findings):
1. _normalize_sidecar_health_path percent-decoded AFTER the raw query/fragment ban,
   so /health%3Ftoken=abc -> ?token=abc survived into the probed URL. Now re-rejects
   decoded ? and # + regression test (%3F/%23 skipped with sidecar_health_path_rejected).
2. The origin validator accepts https:// and [::1], but CSP connect-src only listed
   http 127.0.0.1/localhost -> accepted https/IPv6 loopback sidecars would be CSP-blocked.
   Aligned _CSP_CONNECT_BASE (added https v4/localhost + http(s) [::1]) + updated the 4
   exact-string CSP tests + a regression test asserting all loopback forms are allowed.

Co-authored-by: santastabber <santastabber@users.noreply.github.com>

* Release v0.51.564 — Release TW (loopback sidecar diagnostics; #4612)

* fix(#4612): drop browser-invalid [::1]:* CSP source (browser-smoke fail)

The CSP-loopback-alignment fix added http(s)://[::1]:* to connect-src, but CSP
host-source grammar can't express a port wildcard on a bracketed IPv6 literal —
Chromium rejects '[::1]:*' as an invalid source, which browser-smoke flags as a
console error (CI fail). Keep the valid https://127.0.0.1:*/localhost:* additions;
drop the IPv6 wildcard entries. IPv6 [::1] sidecars are still accepted by the
validator (displayed in the card); their browser health probe surfaces as
'blocked' under CSP unless an operator adds a specific-port [::1]:<port> via
HERMES_WEBUI_CSP_CONNECT_EXTRA. Tests updated to match + assert [::1]:* absent.

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: santastabber <santastabber@users.noreply.github.com>
2026-06-21 13:07:32 -07:00
nesquena-hermes b549c004cb stage #4598 (Paladin173): composer footer control visibility toggles + rebase + contract test (#4630)
Per-control show/hide for composer footer controls (primary vs situational groups),
per-profile persist + live-apply. Visibility-only — footer layout unchanged.
nesquena-hermes deep-reviewed safe (Codex+Opus+suite 9930). Rebased onto master:
resolved api/config.py boolean-keys conflict (kept both auth_disabled_acknowledged +
the 15 hide_composer_* keys) and panels.js settings-load conflict (kept both
render_user_markdown wiring + composer-control chip render). Contributor closed the
en-only-i18n CI blocker (19 keys x 13 locales verified). Added the missing static-
contract test (sibling pattern test_sidebar_tab_visibility.py).

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: Paladin173 <Paladin173@users.noreply.github.com>
2026-06-21 12:42:42 -07:00
nesquena-hermes ee0b476f62 Release v0.51.561 — context-window indicator stays correct after model switch (#4618) (#4628)
* stage #4618 (allenliang2022): broaden streaming stale-compressor guard to any model-window mismatch + #4618 regression tests + CHANGELOG

Broadens #3256's default-only live-usage guard: the streaming SSE snapshot now
always resolves the real per-model window via the same helper GET /api/session
hydration uses (_context_length_lookup_inputs_for_model + get_model_context_length)
and corrects whenever it differs from the compressor's cached value, with a
TypeError fallback to the legacy 2-arg form. Fixes 'refresh shows 1M, send reverts
to stale 168k + early auto-compress' on model-switched sessions. Per-stream cache
preserved (one lookup/stream). Code byte-identical to PR head 3beb18e92d.

Adds 4 source-structure regression tests (RED-proven on master).

Co-authored-by: allenliang2022 <allenliang2022@users.noreply.github.com>

* fix #4618 gate findings: profile-scoped config (Codex cross-profile) + #4248 256k acceptance gate (Opus downward-clobber)

Codex SHIP-WITH-FIXES: live-snapshot used ambient get_config() which in the
detached streaming worker resolves the process-global/default profile (#3294) ->
for a non-default profile pinning a different per-model context_length it would
surface the WRONG profile's window. Now resolves via get_config_for_profile_home
on the session's own profile home (mirrors the worker's _cfg resolution).

Opus SHIP-WITH-FIXES: broadened guard aligned resolution w/ hydration but not its
#4248 acceptance gate -> a transient low-confidence 256k metadata probe could
clobber a LARGER cached window mid-stream. Now reuses the exact hydration helper
_should_accept_session_context_length_refresh on both modern + legacy paths.

+ regression tests for both. Co-authored-by: allenliang2022 <allenliang2022@users.noreply.github.com>

* fix #4618 Codex re-gate findings: broaden save + SSE-done stale-compressor guards too

Codex re-gate found the broadened live-snapshot guard fixed metering but the two
SIBLING paths still used the old default-only exact-cap test:
  - api/streaming.py final session-save: persisted stale other-model window (168k)
    to s.context_length -> wrong window on reload.
  - api/streaming.py terminal  SSE: emitted stale window -> indicator REVERTS
    on stream end (messages.js overwrites S.lastUsage) = the exact 'send reverts to
    168k' symptom.
Both now resolve the real per-model window via the same hydration helper and honor
the #4248 acceptance gate (no 256k downward-clobber), with legacy 2-arg fallback.
This is the root-cause completion across all 3 paths (live/save/SSE-done).

+ 2 regression tests. Co-authored-by: allenliang2022 <allenliang2022@users.noreply.github.com>

* docs(#4618): note model_changed-omission rationale (Opus) + broaden CHANGELOG to all-3-paths

* Release v0.51.561 — Release TT (context-window indicator stays correct after model switch; #4618)

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: allenliang2022 <allenliang2022@users.noreply.github.com>
2026-06-21 11:36:26 -07:00
nesquena-hermes 215c819fae stage #4608 onto master (v0.51.559) — backfill run-journal-recovered rows into model context
allenliang2022: recovered assistant rows were added to the visible transcript but not context_messages,
so the model lost recovered output. Adds _append_recovered_turn_to_context with a role-dedup guard.
Co-authored-by: allenliang2022 <allenliang2022@users.noreply.github.com>
2026-06-21 10:11:35 +00:00
nesquena-hermes 077de5455c harden(#4581): display-only escape symlinks must not disclose target path/metadata
Codex+Opus gate (info-leak): the display-only escape-target rows still returned the resolved
outside path (target), target-derived is_dir, and target stat size; ui.js showed the outside path
in the open-dialog. Fix: emit ONLY name/path/type/target_outside_workspace/mtime for outside rows
(no target, no size, is_dir=False); dialog uses a generic 'points outside workspace' message (dropped
{target} from external_link_open_confirm across all 13 locales). Containment was already intact (both
gates confirmed); this closes the path-disclosure. Tests updated to assert the no-leak property.
2026-06-21 09:15:22 +00:00
nesquena-hermes 1e18c336f1 stage #4581 onto master (v0.51.556) — surface escape-target symlinks as display-only workspace rows
claw-io: symlinks whose target resolves outside the workspace root were silently dropped; now emitted
with target_outside_workspace=True (display-only — safe_resolve_ws/open_anchored_fd still block
navigation). Dedup-winner over #4573 (stronger tests). Co-authored-by: claw-io <claw-io@users.noreply.github.com>
2026-06-21 09:15:22 +00:00
nesquena-hermes 07e6a839fa stage #4599 onto master (v0.51.555) — add id-ID Edge TTS voice + fix Listen button playing-state (parity)
latipun7: adds id-ID-GadisNeural to the Edge TTS allowlist (Edge TTS is a core builtin -> parity, default-in)
+ picker label + sets speaking-state at start of _playEdgeTtsChunked so the Listen button reflects playback.
Co-authored-by: latipun7 <latipun7@users.noreply.github.com>
2026-06-21 08:35:03 +00:00
nesquena-hermes 64ca1f8686 harden(#2980): profile-scope continuation lookup + safe-sid + no speculative restore write
Gate findings (Codex 2-CORE / Opus SHIP-WITH-FIXES), both fixed:
1. cross-profile leak — _pre_compression_continuation_session_id scanned all profiles' sessions,
   matched only by parent_session_id; now filters children by _profiles_match(child, snapshot)
   + validates returned sid with is_safe_session_id. +cross-profile regression test.
2. restore-state poison — frontend wrote continuationSid to localStorage/URL BEFORE the inner
   loadSession proved it loadable; removed the speculative write (success path at the loaded id
   handles it). Updated the source-grep test to assert the safe form.
Bounded BFS (range(20)+seen) + recursion guard confirmed safe by both gates.
2026-06-21 08:17:32 +00:00
nesquena-hermes dbeb437bcc rebase #2980 onto master (v0.51.548) for ship-gate — recover mobile reloads after compression rotation
george-andra: GET /api/session surfaces continuation_session_id for a hidden pre-compression
snapshot; loadSession follows it so a mobile reload mid-compression recovers to the visible
continuation instead of a hidden snapshot (chat looked 'lost'). Resolved 2 conflicts (1615 behind):
sessions.js merged w/ #2971 re-arm guard; routes.py merged w/ _session_source_is_webui branch.
Co-authored-by: george-andra <george-andra@users.noreply.github.com>
2026-06-21 08:17:32 +00:00
nesquena-hermes 4bf9ff7518 stage #4594 onto master (v0.51.550) — preserve visible transcript after manual /compress (#3133)
Manual /compress wrote the compressed payload into BOTH s.messages (visible) and s.context_messages
(model-facing) + wiped s.tool_calls -> messages vanished from the window. Fix keeps s.messages +
tool history durable, deep-copies compressed only into s.context_messages, anchors off visible msgs.
Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
2026-06-21 06:46:47 +00:00
nesquena-hermes 236b165b7a harden(#4587): align scene-builder final-answer filters to the renderer's >=0.9 ratio
Codex gate (SILENT): the scene-builder/hydrator _anchor_scene_row_looks_like_final_answer
(messages.js + routes.py) used a loose '>=80 + prefix' rule while the renderer
_anchorSceneProseMatchesFinalAnswer uses '>=80 + ratio>=0.9'. A short intermediate-prose row that
was a prefix of the final answer could be dropped from the persisted scene by the looser builder,
leaving the stricter renderer no row to show -> silent prose loss. Aligned both filters to the
renderer's rule (drop fewer -> preserve more intermediate prose). Matrix already GREEN; this closes
the edge the fixture didn't exercise.
2026-06-21 06:03:44 +00:00
nesquena-hermes fba80e6981 harden(#4589): close the SECOND isolation-escape door (runtime-env path)
Codex re-gate (CORE, reproduced): _reload_dotenv() was guarded, but get_profile_runtime_env() also
copies a profile .env into runtime env, which profile_env_for_background_worker()/streaming apply to
os.environ -> HERMES_WEBUI_ISOLATED_PROFILE=0 in a profile .env still escaped isolation on that path.
Fix: filter _PROTECTED_ENV_KEYS in get_profile_runtime_env's .env loop + add the key to
_BLOCKED_RUNTIME_ENV_KEYS (gateway-parity filter). + runtime-path regression test.
2026-06-21 05:23:09 +00:00
nesquena-hermes cb7efedc38 harden(#4589): protect HERMES_WEBUI_ISOLATED_PROFILE from profile-.env override
Codex gate (CORE, reproduced): _reload_dotenv() copies a profile's .env into os.environ, so a
contained user could set HERMES_WEBUI_ISOLATED_PROFILE=0 in their own profile .env and silently
escape isolation. Fix: _PROTECTED_ENV_KEYS — _reload_dotenv refuses to override operator/deployment
keys from a profile .env (logs a warning). + regression test proving .env can't disable isolation.
2026-06-21 05:17:20 +00:00
nesquena-hermes 17eb82f087 stage HOTFIX #4589 onto master (v0.51.548) — require explicit opt-in for isolated profile mode (#4586)
Profile-isolation regression: isolated mode was inferred from HERMES_HOME path shape (*/profiles/<name>),
which the launcher exports for ANY normal named profile -> single-users on a named profile lost profile
switching + saw only 1 profile. Fix: HERMES_WEBUI_ISOLATED_PROFILE explicit opt-in is now the PRIMARY gate.
2026-06-21 05:08:59 +00:00
nesquena-hermes 1b7c1fe619 rebase #4569 onto master (v0.51.547) for ship-gate — extension status diagnostics (#4561 follow-up)
santastabber: authenticated /api/extensions/status endpoint reporting WHY an extension didn't load
(stable codes + coarse sources only, no path/env/URL leak). Co-authored-by: santastabber <santastabber@users.noreply.github.com>
2026-06-21 04:08:44 +00:00
nesquena-hermes 8ea7ad582e rebase #3581 onto master (v0.51.546) for review+ship-gate — require current password before disabling auth
starship-s: sudo-mode re-auth on POST /api/settings before any change/clear/passwordless transition
of password auth, + cosmetic auth-disabled-acknowledged nav badge. Operator-hardening (rubric-named).
Security-boundary + visible UI -> Nathan visual sign-off. Co-authored-by: starship-s <starship-s@users.noreply.github.com>
2026-06-21 02:26:20 +00:00
nesquena-hermes 6b2ac55b8d stage #4566 + stamp v0.51.546 — fix flaky gateway_sync test (#4557)
rodboev: settings-write-version counter widens the session-list cache key + flush CLI-sessions
cache on visibility toggle, killing the mtime_ns-bucket collision flake. Co-authored-by: rodboev <rodboev@users.noreply.github.com>
2026-06-21 02:04:09 +00:00
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