+import { computed } from 'vue'
+import {
+ SelectContent,
+ SelectItem,
+ SelectItemText,
+ SelectPortal,
+ SelectRoot,
+ SelectTrigger,
+ SelectViewport
+} from 'reka-ui'
+
+import { useI18n } from '@open-pencil/vue'
+
+import { aiModelSettings, setModelRoleAssignment } from '@/app/ai/models'
+import type { AIModelProfileId } from '@/app/ai/models'
+import AppBadge from '@/components/ui/AppBadge.vue'
+import { useSelectUI } from '@/components/ui/select'
+
+const { dialogs } = useI18n()
+
+// Only profiles that can drive the design agent — setModelRoleAssignment rejects the rest.
+const profiles = computed(() =>
+ aiModelSettings.value.models.filter((profile) => profile.capabilities.includes('tools'))
+)
+
+const selectedProfileId = computed({
+ get: () => aiModelSettings.value.assignments.design,
+ set: (profileId: AIModelProfileId) => setModelRoleAssignment('design', profileId)
+})
+
+const selectCls = useSelectUI({
+ trigger: 'gap-1 rounded border-none bg-transparent px-1.5 py-0.5 text-[10px] text-muted',
+ content: 'max-h-60 overflow-y-auto',
+ item: 'gap-2 rounded px-2 py-1.5 text-[11px]'
+})
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ profile.name }}
+
+ {{ dialogs.modelCapabilityVisionShort }}
+
+
+
+
+
+
+
diff --git a/tests/engine/app/ai/model-roles.test.ts b/tests/engine/app/ai/model-roles.test.ts
index 1adae8eb8..677bd87ba 100644
--- a/tests/engine/app/ai/model-roles.test.ts
+++ b/tests/engine/app/ai/model-roles.test.ts
@@ -1,6 +1,7 @@
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
import {
+ aiModelSettings,
createAIModelRuntime,
createModelProfileDraft,
modelSettingsSnapshot,
@@ -99,6 +100,36 @@ describe('AI model profiles and role assignments', () => {
expect(resolveAIModelRole('vision')).toBeNull()
})
+ test('switches the design agent between saved profiles', () => {
+ // Backs the chat model switcher: it lists tools-capable profiles and reassigns the design role.
+ const switchable = aiModelSettings.value.models.filter((profile) =>
+ profile.capabilities.includes('tools')
+ )
+ expect(switchable.map((profile) => profile.id)).toEqual(['model-design', 'model-fast'])
+
+ setModelRoleAssignment('design', 'model-fast')
+ expect(resolveAIModelRole('design')?.profile.id).toBe('model-fast')
+ expect(resolveAIModelRole('design')?.connection.id).toBe('connection-google')
+
+ setModelRoleAssignment('design', 'model-design')
+ expect(resolveAIModelRole('design')?.profile.id).toBe('model-design')
+ })
+
+ test('refuses to assign a profile that cannot use tools as the design agent', () => {
+ aiModelSettings.value.models.push({
+ id: 'model-textonly',
+ name: 'Text only',
+ connectionId: 'connection-google',
+ modelID: 'text-only',
+ customModelID: '',
+ maxOutputTokens: 4096,
+ capabilities: []
+ })
+
+ setModelRoleAssignment('design', 'model-textonly')
+ expect(resolveAIModelRole('design')?.profile.id).toBe('model-design')
+ })
+
test('reuses matching provider connections when adding models', () => {
const draft = createModelProfileDraft()
draft.name = 'Review model'