diff --git a/web/src/components/NewSession/index.test.tsx b/web/src/components/NewSession/index.test.tsx
index 4f244ef0..ef29fb1d 100644
--- a/web/src/components/NewSession/index.test.tsx
+++ b/web/src/components/NewSession/index.test.tsx
@@ -170,19 +170,24 @@ vi.mock('./ModelSelector', () => ({
isDisabled: boolean
isLoading?: boolean
onModelChange: (model: string) => void
- }) => (
- <>
-
-
{props.options?.map((option) => option.label).join(',')}
- >
- )
+ }) => {
+ const displayedModel = props.options && !props.options.some((option) => option.value === props.model)
+ ? props.options[0]?.value ?? props.model
+ : props.model
+ return (
+ <>
+
+ {props.options?.map((option) => option.label).join(',')}
+ >
+ )
+ }
}))
vi.mock('./ReasoningEffortSelector', () => ({
ReasoningEffortSelector: (props: { value: string; onChange: (effort: string) => void }) => (
@@ -631,4 +636,49 @@ describe('NewSession launch preferences', () => {
expect(screen.getByTestId('reasoning')).toHaveTextContent('max')
})
})
+
+ it('keeps a browse-return custom Claude model visible when it is no longer configured', async () => {
+ savePreferredAgent('claude')
+ saveNewSessionFormDraft({
+ agent: 'claude',
+ model: 'deepseek-v4-flash[1m]',
+ cursorSelectedBase: 'auto',
+ machineId: 'machine-1',
+ effort: 'high',
+ modelReasoningEffort: 'default',
+ serviceTier: 'standard',
+ collaborationMode: 'default',
+ copilotAgentMode: 'interactive',
+ yoloMode: false,
+ codexFamilyPermissionMode: 'default',
+ grokPermissionMode: 'default',
+ sessionType: 'simple',
+ worktreeName: ''
+ })
+ mocks.spawnSession.mockResolvedValue({ type: 'success', sessionId: 'session-1' })
+ const claudeApi = {
+ getClaudeCustomModels: vi.fn().mockResolvedValue({ models: [] })
+ } as unknown as ApiClient
+
+ render(
+ {}}
+ />
+ )
+
+ await waitFor(() => {
+ expect(screen.getByTestId('model')).toHaveTextContent('deepseek-v4-flash[1m]')
+ expect(screen.getByTestId('create')).toBeEnabled()
+ })
+ fireEvent.click(screen.getByTestId('create'))
+
+ await waitFor(() => expect(mocks.spawnSession).toHaveBeenCalledWith(
+ expect.objectContaining({ model: 'deepseek-v4-flash[1m]' })
+ ))
+ })
})
diff --git a/web/src/components/NewSession/index.tsx b/web/src/components/NewSession/index.tsx
index 0213b8fe..ed5734a4 100644
--- a/web/src/components/NewSession/index.tsx
+++ b/web/src/components/NewSession/index.tsx
@@ -302,11 +302,20 @@ export function NewSession(props: {
options.push({ value: modelName, label: modelName })
}
}
+ if (
+ agent === 'claude'
+ && model !== 'auto'
+ && !options.some((option) => option.value === model)
+ ) {
+ options.splice(1, 0, { value: model, label: model })
+ }
return options
- }, [claudeCustomModels])
+ }, [agent, claudeCustomModels, model])
const claudePreferredModelValues = useMemo(
- () => agent === 'claude' ? claudeModelOptions.map((option) => option.value) : null,
- [agent, claudeModelOptions]
+ () => agent === 'claude'
+ ? [...MODEL_OPTIONS.claude.map((option) => option.value), ...claudeCustomModels]
+ : null,
+ [agent, claudeCustomModels]
)
const claudeModelsLoading = agent === 'claude' && !claudeModelsLoaded
const preferredModelCatalogReady = !claudeModelsLoading