[codex] Fix profile-aware session deep links (#962)
* feat: add session deep links for chats * feat: add deep links for history and group chat * Fix profile-aware session deep links --------- Co-authored-by: Maxim Kirilyuk <werserk@inbox.ru>
This commit is contained in:
@@ -336,6 +336,14 @@ function setItemBestEffort(key: string, value: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function getItemBestEffort(key: string): string | null {
|
||||
try {
|
||||
return localStorage.getItem(key)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function removeItem(key: string) {
|
||||
try {
|
||||
localStorage.removeItem(key)
|
||||
@@ -422,7 +430,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
removeItem(storageKey())
|
||||
}
|
||||
|
||||
async function loadSessions(profile?: string | null) {
|
||||
async function loadSessions(profile?: string | null, preferredSessionId?: string | null) {
|
||||
isLoadingSessions.value = true
|
||||
try {
|
||||
const list = await fetchSessions(undefined, undefined, profile || undefined)
|
||||
@@ -436,11 +444,19 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
sessions.value = fresh
|
||||
|
||||
// Restore last active session, fallback to most recent
|
||||
const savedId = activeSessionId.value
|
||||
const targetId = savedId && sessions.value.some(s => s.id === savedId)
|
||||
? savedId
|
||||
: sessions.value[0]?.id
|
||||
// Restore route-selected session first (tab-local source of truth),
|
||||
// then current in-memory session, then persisted legacy/default choice,
|
||||
// then fallback to the most recent session.
|
||||
const currentId = activeSessionId.value
|
||||
const legacyActiveKey = legacyStorageKey()
|
||||
const storedId = getItemBestEffort(storageKey()) || (legacyActiveKey ? getItemBestEffort(LEGACY_STORAGE_KEY) : null)
|
||||
const targetId = preferredSessionId && sessions.value.some(s => s.id === preferredSessionId)
|
||||
? preferredSessionId
|
||||
: currentId && sessions.value.some(s => s.id === currentId)
|
||||
? currentId
|
||||
: storedId && sessions.value.some(s => s.id === storedId)
|
||||
? storedId
|
||||
: sessions.value[0]?.id
|
||||
if (targetId) {
|
||||
await switchSession(targetId)
|
||||
} else {
|
||||
@@ -459,7 +475,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
const sid = activeSessionId.value
|
||||
if (!sid) return false
|
||||
try {
|
||||
const detail = await fetchSession(sid)
|
||||
const detail = await fetchSession(sid, activeSession.value?.profile)
|
||||
if (!detail) return false
|
||||
const target = sessions.value.find(s => s.id === sid)
|
||||
if (!target) return false
|
||||
@@ -533,6 +549,15 @@ export const useChatStore = defineStore('chat', () => {
|
||||
const timeout = setTimeout(() => reject(new Error('resume timeout')), 15_000)
|
||||
resumeSession(sessionId, (data) => {
|
||||
clearTimeout(timeout)
|
||||
if (data.session_id !== sessionId || activeSessionId.value !== sessionId) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
const target = sessions.value.find(s => s.id === sessionId)
|
||||
if (!target) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
if (data.isWorking) {
|
||||
serverWorking.value.add(sessionId)
|
||||
} else {
|
||||
@@ -553,19 +578,20 @@ export const useChatStore = defineStore('chat', () => {
|
||||
} else if (!data.isWorking) {
|
||||
setAbortState(null)
|
||||
}
|
||||
if (data.inputTokens != null) activeSession.value!.inputTokens = data.inputTokens
|
||||
if (data.outputTokens != null) activeSession.value!.outputTokens = data.outputTokens
|
||||
if ((data as any).contextTokens != null) activeSession.value!.contextTokens = (data as any).contextTokens
|
||||
if (data.inputTokens != null) target.inputTokens = data.inputTokens
|
||||
if (data.outputTokens != null) target.outputTokens = data.outputTokens
|
||||
if ((data as any).contextTokens != null) target.contextTokens = (data as any).contextTokens
|
||||
if (data.messages?.length) {
|
||||
activeSession.value!.messages = mapHermesMessages(data.messages as any[])
|
||||
target.messages = mapHermesMessages(data.messages as any[])
|
||||
}
|
||||
if (!activeSession.value!.title) {
|
||||
const firstUser = activeSession.value!.messages.find(m => m.role === 'user')
|
||||
if (!target.title) {
|
||||
const firstUser = target.messages.find(m => m.role === 'user')
|
||||
if (firstUser) {
|
||||
const t = firstUser.content.slice(0, 40)
|
||||
activeSession.value!.title = t + (firstUser.content.length > 40 ? '...' : '')
|
||||
target.title = t + (firstUser.content.length > 40 ? '...' : '')
|
||||
}
|
||||
}
|
||||
activeSession.value = target
|
||||
// Process replayed events (compression state etc.)
|
||||
if (data.events?.length) {
|
||||
for (const evt of data.events) {
|
||||
@@ -588,7 +614,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
compressed: e.compressed ?? false,
|
||||
error: e.error,
|
||||
})
|
||||
if (e.contextTokens != null) activeSession.value!.contextTokens = e.contextTokens
|
||||
if (e.contextTokens != null) target.contextTokens = e.contextTokens
|
||||
} else if (e.event === 'abort.started') {
|
||||
setAbortState({ aborting: true, synced: null })
|
||||
} else if (e.event === 'abort.completed') {
|
||||
@@ -645,7 +671,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
}, activeSession.value?.profile)
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Failed to load session messages via resume:', err)
|
||||
@@ -654,17 +680,20 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
|
||||
// Resume in-flight run event listeners if needed
|
||||
resumeServerWorkingRun(sessionId)
|
||||
if (activeSessionId.value === sessionId) {
|
||||
resumeServerWorkingRun(sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
function newChat(options: { profile?: string; model?: string; provider?: string } = {}) {
|
||||
function newChat(options: { profile?: string; model?: string; provider?: string } = {}): Session {
|
||||
const appStore = useAppStore()
|
||||
const session = createSession({
|
||||
profile: options.profile,
|
||||
model: options.model || appStore.selectedModel || undefined,
|
||||
provider: options.provider || appStore.selectedProvider || '',
|
||||
})
|
||||
switchSession(session.id)
|
||||
void switchSession(session.id)
|
||||
return session
|
||||
}
|
||||
|
||||
async function switchSessionModel(modelId: string, provider?: string, sessionId?: string): Promise<boolean> {
|
||||
@@ -685,7 +714,8 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
|
||||
async function deleteSession(sessionId: string) {
|
||||
await deleteSessionApi(sessionId)
|
||||
const target = sessions.value.find(s => s.id === sessionId)
|
||||
await deleteSessionApi(sessionId, target?.profile)
|
||||
sessions.value = sessions.value.filter(s => s.id !== sessionId)
|
||||
if (activeSessionId.value === sessionId) {
|
||||
if (sessions.value.length > 0) {
|
||||
@@ -1078,7 +1108,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
const runPayload = {
|
||||
input,
|
||||
session_id: sid,
|
||||
profile: shouldSendInitialSessionConfig ? activeSession.value?.profile || undefined : undefined,
|
||||
profile: activeSession.value?.profile || useProfilesStore().activeProfileName || undefined,
|
||||
model: shouldSendInitialSessionConfig ? sessionModel || undefined : undefined,
|
||||
provider: shouldSendInitialSessionConfig ? sessionProvider || undefined : undefined,
|
||||
model_groups: appStore.modelGroups.map(group => ({
|
||||
@@ -2054,7 +2084,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
activeSession.value.messages = mapHermesMessages(data.messages as any[])
|
||||
}
|
||||
resumeServerWorkingRun(sid)
|
||||
})
|
||||
}, activeSession.value?.profile)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user