mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
fix(sessions): always stamp owning profile so cross-profile open works toward default
Session rows served without ?profile= carried no profile field, so in multi-profile desktops the default profile's sessions circulated unowned: resolveStoredSession cached them profile-less, resolveSessionProfile returned undefined, and session.resume targeted whichever gateway was active -- opening a default-profile session from a non-default window failed with "session can't be found" while the reverse direction worked (#67603 family). Server: GET /api/sessions/{id} and GET /api/sessions now stamp profile/ is_default_profile unconditionally -- the serving profile is always known (_cron_default_profile() when the request is unscoped). Renderer: resolveStoredSession treats a profile-less $sessions cache hit as unresolved when >1 profile exists (falls through to the stamped by-id ladder) and back-fills the active profile on bare by-id hits from older backends, so unowned rows are never re-cached. Verified via CDP against a live 4-profile renderer: bare by-id GET returned hasProfileField:false and the stale cache rows matched; with the fix both lookups return the owning profile and the resume routes to the right backend.
This commit is contained in:
parent
015718066a
commit
a8ec50f681
4 changed files with 188 additions and 6 deletions
|
|
@ -4986,14 +4986,17 @@ def get_sessions(
|
|||
exclude_children=True,
|
||||
)
|
||||
now = time.time()
|
||||
# Same ownership contract as get_session_detail: rows are stamped
|
||||
# with the serving profile even when the request wasn't explicitly
|
||||
# scoped, so default-profile rows never circulate unowned.
|
||||
row_profile = profile_name or _cron_default_profile()
|
||||
for s in sessions:
|
||||
s["is_active"] = (
|
||||
s.get("ended_at") is None
|
||||
and (now - s.get("last_active", s.get("started_at", 0))) < 300
|
||||
)
|
||||
if profile_name:
|
||||
s["profile"] = profile_name
|
||||
s["is_default_profile"] = profile_name == "default"
|
||||
s["profile"] = row_profile
|
||||
s["is_default_profile"] = row_profile == "default"
|
||||
# SQLite stores the flag as 0/1; expose a real JSON boolean.
|
||||
s["archived"] = bool(s.get("archived"))
|
||||
if not full:
|
||||
|
|
@ -11986,8 +11989,16 @@ async def get_session_detail(session_id: str, profile: Optional[str] = None):
|
|||
session = db.get_session(sid) if sid else None
|
||||
if not session:
|
||||
raise HTTPException(status_code=404, detail="Session not found")
|
||||
if profile:
|
||||
session["profile"] = _cron_profile_home(profile)[0]
|
||||
# Always stamp the owning profile — the serving profile is known even
|
||||
# when the request carries no ``?profile=`` (it's this process's own
|
||||
# profile). Stamping only on explicit ``?profile=`` left rows for the
|
||||
# default/primary profile systematically unowned, so multi-profile
|
||||
# clients resolved them to whichever gateway happened to be active
|
||||
# (cross-profile open asymmetry, #67603 family).
|
||||
session["profile"] = (
|
||||
_cron_profile_home(profile)[0] if profile else _cron_default_profile()
|
||||
)
|
||||
session["is_default_profile"] = session["profile"] == "default"
|
||||
return session
|
||||
finally:
|
||||
db.close()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue