81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
|
|
"""Sliced upstream fixture: the exact api/profiles.py fragments the Atlas
|
||
|
|
voice patcher grafts into (session-continuity 409 resilience). Mirrors the
|
||
|
|
pinned Hermes WebUI deployment byte-for-byte for the anchored regions; see
|
||
|
|
dockerfiles/hermes-webui-atlas-patch.py."""
|
||
|
|
|
||
|
|
import logging
|
||
|
|
import threading
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
_root_profile_name_cache = {'default'}
|
||
|
|
_root_profile_name_cache_lock = threading.Lock()
|
||
|
|
_root_profile_name_cache_loaded = False
|
||
|
|
|
||
|
|
|
||
|
|
def list_profiles_api():
|
||
|
|
"""Fixture stand-in for the hermes_cli-backed profile listing."""
|
||
|
|
return []
|
||
|
|
|
||
|
|
|
||
|
|
def _is_root_profile(name: str) -> bool:
|
||
|
|
"""True if *name* resolves to the Hermes Agent root profile (~/.hermes).
|
||
|
|
|
||
|
|
Matches the legacy 'default' alias plus any name where list_profiles_api()
|
||
|
|
reports is_default=True. Memoized; call _invalidate_root_profile_cache()
|
||
|
|
after mutating profile metadata.
|
||
|
|
"""
|
||
|
|
global _root_profile_name_cache_loaded
|
||
|
|
if not name:
|
||
|
|
return False
|
||
|
|
if name == 'default':
|
||
|
|
return True
|
||
|
|
with _root_profile_name_cache_lock:
|
||
|
|
if _root_profile_name_cache_loaded:
|
||
|
|
return name in _root_profile_name_cache
|
||
|
|
# Cache miss — populate from list_profiles_api(). Done outside the lock to
|
||
|
|
# avoid holding it across a hermes_cli subprocess call.
|
||
|
|
try:
|
||
|
|
infos = list_profiles_api()
|
||
|
|
except Exception:
|
||
|
|
logger.debug("Failed to list profiles for root-profile lookup", exc_info=True)
|
||
|
|
return False
|
||
|
|
with _root_profile_name_cache_lock:
|
||
|
|
_root_profile_name_cache.clear()
|
||
|
|
_root_profile_name_cache.add('default')
|
||
|
|
for p in infos:
|
||
|
|
try:
|
||
|
|
if p.get('is_default') and p.get('name'):
|
||
|
|
_root_profile_name_cache.add(p['name'])
|
||
|
|
except (AttributeError, TypeError):
|
||
|
|
continue
|
||
|
|
_root_profile_name_cache_loaded = True
|
||
|
|
return name in _root_profile_name_cache
|
||
|
|
|
||
|
|
|
||
|
|
def _profiles_match(row_profile, active_profile) -> bool:
|
||
|
|
"""Return True if a session/project row's profile matches the active profile.
|
||
|
|
|
||
|
|
Treats both the literal alias 'default' and any renamed-root display name
|
||
|
|
(per _is_root_profile) as equivalent, so legacy rows tagged 'default'
|
||
|
|
still surface when the user has renamed the root profile to e.g. 'kinni',
|
||
|
|
and vice versa.
|
||
|
|
|
||
|
|
A row with no profile (`None` or empty string) is treated as belonging to
|
||
|
|
the root profile — that's the convention used by the legacy backfill at
|
||
|
|
api/models.py::all_sessions, and matches the default seen in
|
||
|
|
`static/sessions.js` (`S.activeProfile||'default'`).
|
||
|
|
|
||
|
|
Originally lived in api/routes.py; relocated here so both routes.py and
|
||
|
|
out-of-process consumers (mcp_server.py) can import the canonical helper
|
||
|
|
instead of duplicating the body. See #1614 for the visibility model.
|
||
|
|
"""
|
||
|
|
row = row_profile or 'default'
|
||
|
|
active = active_profile or 'default'
|
||
|
|
if row == active:
|
||
|
|
return True
|
||
|
|
# Cross-alias the renamed root.
|
||
|
|
if _is_root_profile(row) and _is_root_profile(active):
|
||
|
|
return True
|
||
|
|
return False
|