Skip to content

Commit a87ae2d

Browse files
Revert "fix: preserve session selection on reconnect (#261)"
This reverts commit fdeffea.
1 parent fdeffea commit a87ae2d

5 files changed

Lines changed: 40 additions & 91 deletions

File tree

frontend/src/components/message/PromptInput.tsx

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { useSessionAgent } from '@/hooks/useSessionAgent'
1010
import { useSTT } from '@/hooks/useSTT'
1111

1212
import { useUserBash } from '@/stores/userBashStore'
13-
import { useModelStore, formatModelKey } from '@/stores/modelStore'
13+
import { useModelStore } from '@/stores/modelStore'
1414
import { useSessionAgentStore } from '@/stores/sessionAgentStore'
1515
import { useUIState } from '@/stores/uiStateStore'
1616
import { useMobile } from '@/hooks/useMobile'
@@ -436,9 +436,6 @@ export const PromptInput = memo(forwardRef<PromptInputHandle, PromptInputProps>(
436436
setStoredAgent(sessionID, agentName)
437437
const agent = agents.find(a => a.name === agentName)
438438
if (agent?.model) {
439-
if (sessionModelSyncKey) {
440-
userModelOverrideRef.current[sessionModelSyncKey] = true
441-
}
442439
setStoredModel({ providerID: agent.model.providerID, modelID: agent.model.modelID })
443440
}
444441
}
@@ -994,7 +991,7 @@ if (isIOS && isSecureContext && navigator.clipboard && navigator.clipboard.read)
994991
const sessionAgent = useSessionAgent(opcodeUrl, sessionID, directory)
995992
const currentMode = localMode ?? sessionAgent.agent
996993
const setStoredAgent = useSessionAgentStore((s) => s.setAgent)
997-
const userModelOverrideRef = useRef<Record<string, boolean>>({})
994+
const syncedSessionModelRef = useRef<string | undefined>(undefined)
998995

999996
const client = useOpenCodeClient(opcodeUrl, directory)
1000997
const { data: providersData } = useQuery({
@@ -1012,28 +1009,19 @@ if (isIOS && isSecureContext && navigator.clipboard && navigator.clipboard.read)
10121009

10131010
useEffect(() => {
10141011
if (!sessionModelSyncKey) return
1012+
if (syncedSessionModelRef.current === sessionModelSyncKey) return
10151013
if (!sessionAgent.model) return
10161014

1017-
const sessionModelKey = formatModelKey(sessionAgent.model)
1018-
const activeModelKey = model ? formatModelKey(model) : undefined
1019-
const hasUserOverride = userModelOverrideRef.current[sessionModelSyncKey]
1020-
1021-
if (hasUserOverride && activeModelKey !== sessionModelKey) return
1015+
restoreSessionModel(sessionAgent.model)
10221016

1023-
if (hasUserOverride) {
1024-
delete userModelOverrideRef.current[sessionModelSyncKey]
1025-
}
1026-
1027-
if (activeModelKey !== sessionModelKey) {
1028-
restoreSessionModel(sessionAgent.model)
1029-
}
1017+
syncedSessionModelRef.current = sessionModelSyncKey
10301018

10311019
if (sessionAgent.variant) {
10321020
setStoreVariant(sessionAgent.model, sessionAgent.variant)
10331021
} else {
10341022
clearStoreVariant(sessionAgent.model)
10351023
}
1036-
}, [clearStoreVariant, model, sessionAgent.model, sessionAgent.variant, sessionModelSyncKey, restoreSessionModel, setStoreVariant])
1024+
}, [clearStoreVariant, sessionAgent.model, sessionAgent.variant, sessionModelSyncKey, restoreSessionModel, setStoreVariant])
10371025

10381026
const currentModel = modelString || ''
10391027
const displayModelName = useMemo(() => {
@@ -1270,11 +1258,6 @@ return (
12701258
<ModelQuickSelect
12711259
opcodeUrl={opcodeUrl}
12721260
directory={directory}
1273-
onModelChange={() => {
1274-
if (sessionModelSyncKey) {
1275-
userModelOverrideRef.current[sessionModelSyncKey] = true
1276-
}
1277-
}}
12781261
>
12791262
<button
12801263
className="px-2.5 py-0.5 md:px-3 min-h-[36px] min-w-0 rounded-lg text-xs md:text-sm font-medium border bg-muted border-border text-muted-foreground hover:bg-muted-foreground/10 hover:border-foreground/30 transition-colors cursor-pointer flex-1 md:flex-initial md:w-auto max-w-[110px] md:max-w-[220px] dark:border-white/30 flex flex-col items-start justify-center overflow-hidden"

frontend/src/components/model/ModelQuickSelect.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useCallback, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
22
import { Check, ChevronLeft, ChevronRight, Clock, MoreVertical, Search, Star, Trash2, X } from 'lucide-react'
33
import { useModelSelection } from '@/hooks/useModelSelection'
4-
import { formatModelKey } from '@/stores/modelStore'
54
import { useVariants } from '@/hooks/useVariants'
65
import { formatModelName, formatProviderName, getProviders } from '@/api/providers'
76
import { useQuery } from '@tanstack/react-query'
@@ -21,7 +20,6 @@ interface ModelQuickSelectProps {
2120
opcodeUrl: string | null | undefined
2221
directory?: string
2322
disabled?: boolean
24-
onModelChange?: () => void
2523
children: React.ReactNode
2624
}
2725

@@ -77,7 +75,7 @@ function createModelListItem(provider: Provider, modelID: string, model: Model):
7775
return {
7876
providerID: provider.id,
7977
modelID,
80-
key: formatModelKey({ providerID: provider.id, modelID }),
78+
key: `${provider.id}/${modelID}`,
8179
displayName,
8280
providerName,
8381
searchText: createSearchText(displayName, modelID, providerName, provider.id),
@@ -89,13 +87,17 @@ function createFallbackModelListItem(providerID: string, modelID: string): Model
8987
return {
9088
providerID,
9189
modelID,
92-
key: formatModelKey({ providerID, modelID }),
90+
key: `${providerID}/${modelID}`,
9391
displayName: modelID,
9492
providerName: providerID,
9593
searchText: createSearchText(modelID, providerID),
9694
}
9795
}
9896

97+
function getSelectionKey(selection: { providerID: string, modelID: string }) {
98+
return `${selection.providerID}/${selection.modelID}`
99+
}
100+
99101
function VirtualizedList<T>({
100102
items,
101103
itemHeight,
@@ -183,7 +185,6 @@ export function ModelQuickSelect({
183185
opcodeUrl,
184186
directory,
185187
disabled,
186-
onModelChange,
187188
children,
188189
}: ModelQuickSelectProps) {
189190
const [isOpen, setIsOpen] = useState(false)
@@ -205,11 +206,11 @@ export function ModelQuickSelect({
205206
const providers = providersData?.providers ?? EMPTY_PROVIDERS
206207

207208
const favoriteKeySet = useMemo(() => {
208-
return new Set(favoriteModels.map(formatModelKey))
209+
return new Set(favoriteModels.map(getSelectionKey))
209210
}, [favoriteModels])
210211

211212
const recentKeySet = useMemo(() => {
212-
return new Set(recentModels.map(formatModelKey))
213+
return new Set(recentModels.map(getSelectionKey))
213214
}, [recentModels])
214215

215216
const { providerById, providerItems } = useMemo(() => {
@@ -305,15 +306,15 @@ export function ModelQuickSelect({
305306

306307
const favoriteModelsWithNames = useMemo(() => {
307308
return favoriteModels
308-
.filter(favorite => formatModelKey(favorite) !== modelString)
309+
.filter(favorite => `${favorite.providerID}/${favorite.modelID}` !== modelString)
309310
.slice(0, 5)
310311
.map(toModelListItem)
311312
}, [favoriteModels, modelString, toModelListItem])
312313

313314
const recentModelsWithNames = useMemo(() => {
314315
return recentModels
315316
.filter(recent => {
316-
const key = formatModelKey(recent)
317+
const key = getSelectionKey(recent)
317318
return key !== modelString && !favoriteKeySet.has(key)
318319
})
319320
.slice(0, 5)
@@ -413,7 +414,6 @@ export function ModelQuickSelect({
413414

414415
const handleModelSelect = (providerID: string, modelID: string) => {
415416
setModel({ providerID, modelID })
416-
onModelChange?.()
417417
setShowAllModels(false)
418418
setSearchQuery('')
419419
setSelectedProviderId(null)

frontend/src/hooks/useSessionAgent.test.tsx

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ describe('useSessionAgent', () => {
175175
})
176176
})
177177

178-
it('keeps cached message-derived configuration while refetching', async () => {
178+
it('does not restore model from cached messages while refetching', async () => {
179179
vi.mocked(useMessages).mockReturnValue({
180180
data: [
181181
{
@@ -194,38 +194,7 @@ describe('useSessionAgent', () => {
194194
data: { default_agent: 'code' },
195195
} as ReturnType<typeof useConfig>)
196196
vi.mocked(useAgents).mockReturnValue({
197-
data: [
198-
{ name: 'code', mode: 'primary' },
199-
{ name: 'assistant', mode: 'primary' },
200-
],
201-
isSuccess: true,
202-
} as ReturnType<typeof useAgents>)
203-
204-
const { result } = renderHook(() =>
205-
useSessionAgent('http://localhost:5551', 'session-1', '/assistant')
206-
)
207-
208-
await waitFor(() => {
209-
expect(result.current.agent).toBe('assistant')
210-
expect(result.current.model).toEqual({ providerID: 'provider', modelID: 'stale-model' })
211-
expect(result.current.variant).toBe('stale-variant')
212-
})
213-
})
214-
215-
it('uses stored session agent while initial messages are loading', async () => {
216-
useSessionAgentStore.setState({ agents: { 'session-1': 'assistant' } })
217-
vi.mocked(useMessages).mockReturnValue({
218-
data: undefined,
219-
isLoading: true,
220-
} as ReturnType<typeof useMessages>)
221-
vi.mocked(useConfig).mockReturnValue({
222-
data: { default_agent: 'code' },
223-
} as ReturnType<typeof useConfig>)
224-
vi.mocked(useAgents).mockReturnValue({
225-
data: [
226-
{ name: 'code', mode: 'primary' },
227-
{ name: 'assistant', mode: 'primary' },
228-
],
197+
data: [{ name: 'code', mode: 'primary' }],
229198
isSuccess: true,
230199
} as ReturnType<typeof useAgents>)
231200

@@ -234,7 +203,7 @@ describe('useSessionAgent', () => {
234203
)
235204

236205
await waitFor(() => {
237-
expect(result.current.agent).toBe('assistant')
206+
expect(result.current.agent).toBe('code')
238207
expect(result.current.model).toBeUndefined()
239208
expect(result.current.variant).toBeUndefined()
240209
})

frontend/src/hooks/useSessionAgent.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function useSessionAgent(
6262
sessionID: string | undefined,
6363
directory?: string
6464
) {
65-
const { data: messages, isLoading: messagesLoading } = useMessages(opcodeUrl, sessionID, directory)
65+
const { data: messages, isLoading: messagesLoading, isFetching: messagesFetching } = useMessages(opcodeUrl, sessionID, directory)
6666
const { data: config } = useConfig(opcodeUrl, directory)
6767
const { data: agents, isSuccess: agentsLoaded } = useAgents(opcodeUrl, directory)
6868
const storedAgent = useSessionAgentStore((s) => s.agents[sessionID ?? ''] ?? null)
@@ -75,28 +75,12 @@ export function useSessionAgent(
7575
)
7676

7777
const result = useMemo(() => {
78-
const resolveFallback = (): SessionAgentResult => {
79-
const resolvedStoredAgent = resolveAvailableAgentName(storedAgent, agents, agentsLoaded)
80-
if (resolvedStoredAgent) {
81-
const prev = prevRef.current
82-
if (prev.agent === resolvedStoredAgent && !prev.model && !prev.variant) {
83-
return { ...prev, fromMessage: false }
84-
}
85-
86-
const next: SessionAgentResult = { agent: resolvedStoredAgent, model: undefined, variant: undefined, fromMessage: false }
87-
prevRef.current = next
88-
return next
89-
}
90-
78+
if (messagesLoading || messagesFetching) {
9179
return { agent: defaultAgent, model: undefined, variant: undefined, fromMessage: false }
9280
}
9381

94-
if (messagesLoading && (!messages || messages.length === 0)) {
95-
return resolveFallback()
96-
}
97-
9882
if (!messages || messages.length === 0) {
99-
return resolveFallback()
83+
return { agent: defaultAgent, model: undefined, variant: undefined, fromMessage: false }
10084
}
10185

10286
let latestAgent: string | undefined
@@ -156,7 +140,7 @@ export function useSessionAgent(
156140
}
157141

158142
return { agent: defaultAgent, model: undefined, variant: undefined, fromMessage: false }
159-
}, [messages, messagesLoading, storedAgent, defaultAgent, agents, agentsLoaded])
143+
}, [messages, messagesLoading, messagesFetching, storedAgent, defaultAgent, agents, agentsLoaded])
160144

161145
useEffect(() => {
162146
if (result.agent && sessionID && result.fromMessage) {
@@ -166,3 +150,20 @@ export function useSessionAgent(
166150

167151
return { agent: result.agent, model: result.model, variant: result.variant }
168152
}
153+
154+
export function getSessionAgentFromMessages(
155+
messages: Array<{ role: string; agent?: string }> | undefined
156+
): string | undefined {
157+
if (!messages || messages.length === 0) {
158+
return undefined
159+
}
160+
161+
for (let i = messages.length - 1; i >= 0; i--) {
162+
const msg = messages[i]
163+
if (msg.role === 'user' && 'agent' in msg && msg.agent) {
164+
return msg.agent
165+
}
166+
}
167+
168+
return undefined
169+
}

frontend/src/stores/modelStore.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ interface ModelStore {
2626
clearVariant: (model: ModelSelection) => void
2727
}
2828

29-
export function formatModelKey(model: ModelSelection): string {
30-
return `${model.providerID}/${model.modelID}`
31-
}
32-
3329
export function modelExists(model: ModelSelection | null, providers: Provider[]): boolean {
3430
if (!model) return false
3531
return providers.some(

0 commit comments

Comments
 (0)