fix(profiles): profile isolation — new_session uses per-request profile, not process global (#800)

Fixes the multi-client profile isolation bug (#798).

- get_hermes_home_for_profile(): pure path resolver, validates name against
  _PROFILE_ID_RE (rejects path traversal), never mutates os.environ or globals
- new_session() accepts explicit profile= param from POST body (S.activeProfile),
  short-circuits the process-level _active_profile global
- streaming handler resolves HERMES_HOME from s.profile instead of the global
- sessions.js sends profile: S.activeProfile in every new-session POST

10 tests in tests/test_issue798.py including concurrency and traversal coverage.

Co-authored-by: nesquena <nesquena@users.noreply.github.com>
This commit is contained in:
nesquena-hermes
2026-04-21 09:16:51 -07:00
committed by GitHub
parent d527629281
commit cbb4ba3f28
7 changed files with 232 additions and 13 deletions

View File

@@ -834,10 +834,13 @@ def _run_agent_streaming(session_id, msg_text, model, workspace, stream_id, atta
put('cancel', {'message': 'Cancelled before start'})
return
# Resolve profile home for this agent run (snapshot at start)
# Resolve profile home for this agent run — use the session's own profile
# (stamped at new_session() time from the client's S.activeProfile) so that
# two concurrent tabs on different profiles don't clobber each other via the
# process-level active-profile global. Falls back gracefully.
try:
from api.profiles import get_active_hermes_home
_profile_home = str(get_active_hermes_home())
from api.profiles import get_hermes_home_for_profile
_profile_home = str(get_hermes_home_for_profile(getattr(s, 'profile', None)))
except ImportError:
_profile_home = os.environ.get('HERMES_HOME', '')