-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
fix: auto-switch profiles for session links #5419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1176,6 +1176,41 @@ function _rearmActiveSessionStream(){ | |||||||||||||||||||||
| if(activeSid) startSessionStream(activeSid); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| function _sessionProfileMismatchFromError(e){ | ||||||||||||||||||||||
| if(!e || e.status!==409 || !e.body) return null; | ||||||||||||||||||||||
| try{ | ||||||||||||||||||||||
| const body=JSON.parse(e.body); | ||||||||||||||||||||||
| if(body && body.code==='session_profile_mismatch' && body.profile){ | ||||||||||||||||||||||
| return {profile:String(body.profile), session_id:String(body.session_id||'')}; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }catch(_){ } | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| async function _switchProfileForSessionLoad(profile){ | ||||||||||||||||||||||
| const name=String(profile||'').trim(); | ||||||||||||||||||||||
| if(!name) throw new Error('missing profile'); | ||||||||||||||||||||||
| if(name===S.activeProfile) return; | ||||||||||||||||||||||
| if(typeof _invalidateSessionListRenders==='function') _invalidateSessionListRenders(); | ||||||||||||||||||||||
| if(typeof _setProfileSwitchListEmbargo==='function') _setProfileSwitchListEmbargo(true); | ||||||||||||||||||||||
| if(typeof showSessionListSkeleton==='function') showSessionListSkeleton(name); | ||||||||||||||||||||||
| try{ | ||||||||||||||||||||||
| const data=await api('/api/profile/switch',{method:'POST',body:JSON.stringify({name}),timeoutToast:false}); | ||||||||||||||||||||||
| S.activeProfile=data.active||name; | ||||||||||||||||||||||
| S.activeProfileIsDefault=!!data.is_default; | ||||||||||||||||||||||
| if(typeof _clearPersistedModelState==='function') _clearPersistedModelState(); | ||||||||||||||||||||||
| else localStorage.removeItem('hermes-webui-model'); | ||||||||||||||||||||||
| if(data.default_model) window._defaultModel=data.default_model; | ||||||||||||||||||||||
| if(data.default_model_provider) window._activeProvider=data.default_model_provider; | ||||||||||||||||||||||
| if(typeof startGatewaySSE==='function') startGatewaySSE(); | ||||||||||||||||||||||
| if(typeof syncTopbar==='function') syncTopbar(); | ||||||||||||||||||||||
| if(typeof _setProfileSwitchListEmbargo==='function') _setProfileSwitchListEmbargo(false); | ||||||||||||||||||||||
| if(typeof renderSessionList==='function') await renderSessionList(); | ||||||||||||||||||||||
| }finally{ | ||||||||||||||||||||||
| if(typeof _setProfileSwitchListEmbargo==='function') _setProfileSwitchListEmbargo(false); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| async function loadSession(sid){ | ||||||||||||||||||||||
| const opts = arguments[1] || {}; | ||||||||||||||||||||||
| if(!opts.skipLineageResolve && typeof _resolveSessionIdFromSidebarLineage==='function'){ | ||||||||||||||||||||||
|
|
@@ -1304,6 +1339,21 @@ async function loadSession(sid){ | |||||||||||||||||||||
| try { | ||||||||||||||||||||||
| data = await api(`/api/session?session_id=${encodeURIComponent(sid)}&messages=0&resolve_model=0`); | ||||||||||||||||||||||
| } catch(e) { | ||||||||||||||||||||||
| const profileMismatch=_sessionProfileMismatchFromError(e); | ||||||||||||||||||||||
| if(profileMismatch && profileMismatch.profile && !opts.skipProfileResolve){ | ||||||||||||||||||||||
| if (_loadingSessionId !== sid) { | ||||||||||||||||||||||
| _rearmActiveSessionStream(); | ||||||||||||||||||||||
| return; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| try{ | ||||||||||||||||||||||
| if(typeof showToast==='function') showToast(`Switching to ${profileMismatch.profile} profile for this session…`,2200); | ||||||||||||||||||||||
| await _switchProfileForSessionLoad(profileMismatch.profile); | ||||||||||||||||||||||
| if (_loadingSessionId === sid) _loadingSessionId = null; | ||||||||||||||||||||||
| return loadSession(sid,{...opts,skipProfileResolve:true,force:true}); | ||||||||||||||||||||||
|
Comment on lines
+1350
to
+1352
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
| }catch(switchErr){ | ||||||||||||||||||||||
| e=switchErr; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| const _msgInner = $('msgInner'); | ||||||||||||||||||||||
| // Stale-load guard (Codex): a newer loadSession() may have started while this | ||||||||||||||||||||||
| // request was awaiting (e.g. the user clicked a healthy session during a | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
"profile": _session_profilefield lets any authenticated session probe session IDs it doesn't own and discover the names of other profiles. In the cross-profile auto-switch flow the frontend only needs to receive the profile name whencode == "session_profile_mismatch", so the exposure is intentional — but worth confirming this is acceptable given your threat model. The same pattern is repeated at line 11919.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!