From 6a71576a43181275bc78e4fa61454de55bfd626a Mon Sep 17 00:00:00 2001 From: wsp1911 Date: Tue, 23 Jun 2026 18:42:40 +0800 Subject: [PATCH] feat(agent): add InitMiniApp defaults and simplify assistant config --- .../agentic/agents/definitions/modes/claw.rs | 14 ++ .../agents/definitions/modes/cowork.rs | 14 ++ .../scenes/my-agent/identityDocument.test.ts | 4 - .../app/scenes/my-agent/identityDocument.ts | 17 -- .../src/app/scenes/profile/nurseryStore.ts | 6 +- .../scenes/profile/views/AssistantCard.tsx | 4 - ...nfigPage.tsx => AssistantDefaultsPage.tsx} | 232 ++---------------- .../scenes/profile/views/NurseryGallery.tsx | 74 +++--- .../app/scenes/profile/views/NurseryView.scss | 134 +++++----- .../app/scenes/profile/views/NurseryView.tsx | 6 +- .../src/app/scenes/profile/views/index.ts | 4 +- .../scenes/profile/views/useTokenEstimate.ts | 58 ----- .../flow_chat/components/ModelSelector.tsx | 10 +- .../src/locales/en-US/scenes/profile.json | 11 +- .../src/locales/zh-CN/scenes/profile.json | 11 +- .../src/locales/zh-TW/scenes/profile.json | 11 +- src/web-ui/src/shared/types/global-state.ts | 2 - 17 files changed, 184 insertions(+), 428 deletions(-) rename src/web-ui/src/app/scenes/profile/views/{TemplateConfigPage.tsx => AssistantDefaultsPage.tsx} (75%) delete mode 100644 src/web-ui/src/app/scenes/profile/views/useTokenEstimate.ts diff --git a/src/crates/assembly/core/src/agentic/agents/definitions/modes/claw.rs b/src/crates/assembly/core/src/agentic/agents/definitions/modes/claw.rs index fc7e399e47..8bbbb49f5c 100644 --- a/src/crates/assembly/core/src/agentic/agents/definitions/modes/claw.rs +++ b/src/crates/assembly/core/src/agentic/agents/definitions/modes/claw.rs @@ -37,6 +37,7 @@ impl ClawMode { // Local desktop/system control is delegated to the ComputerUse // agent/tool instead of being surfaced as a ControlHub domain. "ControlHub".to_string(), + "InitMiniApp".to_string(), ], } } @@ -79,3 +80,16 @@ impl Agent for ClawMode { false } } + +#[cfg(test)] +mod tests { + use super::ClawMode; + use crate::agentic::agents::Agent; + + #[test] + fn claw_mode_includes_init_miniapp_in_default_tools() { + assert!(ClawMode::new() + .default_tools() + .contains(&"InitMiniApp".to_string())); + } +} diff --git a/src/crates/assembly/core/src/agentic/agents/definitions/modes/cowork.rs b/src/crates/assembly/core/src/agentic/agents/definitions/modes/cowork.rs index 9cbc912b2d..ccac792df1 100644 --- a/src/crates/assembly/core/src/agentic/agents/definitions/modes/cowork.rs +++ b/src/crates/assembly/core/src/agentic/agents/definitions/modes/cowork.rs @@ -49,6 +49,7 @@ impl CoworkMode { "WebSearch".to_string(), "WebFetch".to_string(), "ControlHub".to_string(), + "InitMiniApp".to_string(), ], } } @@ -95,3 +96,16 @@ impl Agent for CoworkMode { false } } + +#[cfg(test)] +mod tests { + use super::CoworkMode; + use crate::agentic::agents::Agent; + + #[test] + fn cowork_mode_includes_init_miniapp_in_default_tools() { + assert!(CoworkMode::new() + .default_tools() + .contains(&"InitMiniApp".to_string())); + } +} diff --git a/src/web-ui/src/app/scenes/my-agent/identityDocument.test.ts b/src/web-ui/src/app/scenes/my-agent/identityDocument.test.ts index 0910da81f3..ae238afe16 100644 --- a/src/web-ui/src/app/scenes/my-agent/identityDocument.test.ts +++ b/src/web-ui/src/app/scenes/my-agent/identityDocument.test.ts @@ -47,8 +47,6 @@ describe('identityDocument frontmatter helpers', () => { vibe: 'Calm', emoji: '๐Ÿ™‚', body: '# Body\n\nHello', - modelPrimary: '', - modelFast: '', }); expect(serialized).toBe([ @@ -86,8 +84,6 @@ describe('identityDocument frontmatter helpers', () => { vibe: 'Calm', emoji: '๐Ÿ™‚', body: '# Body\n\nHello', - modelPrimary: '', - modelFast: '', }); }); }); diff --git a/src/web-ui/src/app/scenes/my-agent/identityDocument.ts b/src/web-ui/src/app/scenes/my-agent/identityDocument.ts index 1efbee054c..c11e05ce6b 100644 --- a/src/web-ui/src/app/scenes/my-agent/identityDocument.ts +++ b/src/web-ui/src/app/scenes/my-agent/identityDocument.ts @@ -6,10 +6,6 @@ export interface IdentityDocument { vibe: string; emoji: string; body: string; - /** Override for primary model slot. Empty string = inherit from template. */ - modelPrimary?: string; - /** Override for fast model slot. Empty string = inherit from template. */ - modelFast?: string; } export const EMPTY_IDENTITY_DOCUMENT: IdentityDocument = { @@ -18,8 +14,6 @@ export const EMPTY_IDENTITY_DOCUMENT: IdentityDocument = { vibe: '', emoji: '', body: '', - modelPrimary: '', - modelFast: '', }; const FRONTMATTER_FIELDS: Array> = [ @@ -27,8 +21,6 @@ const FRONTMATTER_FIELDS: Array> = [ 'creature', 'vibe', 'emoji', - 'modelPrimary', - 'modelFast', ]; export interface MarkdownFrontmatterSections { @@ -108,8 +100,6 @@ export function parseIdentityDocument(content: string): IdentityDocument { vibe: normalizeShortField(parsed.vibe), emoji: normalizeShortField(parsed.emoji), body: sections.body, - modelPrimary: normalizeShortField(parsed.modelPrimary), - modelFast: normalizeShortField(parsed.modelFast), }; } @@ -120,16 +110,9 @@ export function serializeIdentityDocument(document: IdentityDocument): string { vibe: normalizeShortField(document.vibe), emoji: normalizeShortField(document.emoji), body: normalizeLineEndings(document.body || '').replace(/^\n+/, '').trimEnd(), - modelPrimary: normalizeShortField(document.modelPrimary ?? ''), - modelFast: normalizeShortField(document.modelFast ?? ''), }; - const optionalFields = new Set>(['modelPrimary', 'modelFast']); const frontmatter = FRONTMATTER_FIELDS - .filter((field) => { - if (optionalFields.has(field)) return !!normalized[field]; - return true; - }) .map((field) => { const value = normalized[field]; return value ? `${field}: ${serializeScalar(value)}` : `${field}:`; diff --git a/src/web-ui/src/app/scenes/profile/nurseryStore.ts b/src/web-ui/src/app/scenes/profile/nurseryStore.ts index 464350b791..b34583d69d 100644 --- a/src/web-ui/src/app/scenes/profile/nurseryStore.ts +++ b/src/web-ui/src/app/scenes/profile/nurseryStore.ts @@ -1,12 +1,12 @@ import { create } from 'zustand'; -export type NurseryPage = 'gallery' | 'template' | 'assistant'; +export type NurseryPage = 'gallery' | 'defaults' | 'assistant'; interface NurseryStoreState { page: NurseryPage; activeWorkspaceId: string | null; openGallery: () => void; - openTemplate: () => void; + openDefaults: () => void; openAssistant: (workspaceId: string) => void; } @@ -14,6 +14,6 @@ export const useNurseryStore = create((set) => ({ page: 'gallery', activeWorkspaceId: null, openGallery: () => set({ page: 'gallery', activeWorkspaceId: null }), - openTemplate: () => set({ page: 'template', activeWorkspaceId: null }), + openDefaults: () => set({ page: 'defaults', activeWorkspaceId: null }), openAssistant: (workspaceId) => set({ page: 'assistant', activeWorkspaceId: workspaceId }), })); diff --git a/src/web-ui/src/app/scenes/profile/views/AssistantCard.tsx b/src/web-ui/src/app/scenes/profile/views/AssistantCard.tsx index 17d9b63627..103ebe2f85 100644 --- a/src/web-ui/src/app/scenes/profile/views/AssistantCard.tsx +++ b/src/web-ui/src/app/scenes/profile/views/AssistantCard.tsx @@ -22,8 +22,6 @@ const AssistantCard: React.FC = ({ workspace, onClick, onNew const emoji = identity?.emoji?.trim() ?? ''; const creature = identity?.creature?.trim() || ''; const vibe = identity?.vibe?.trim() || ''; - const modelPrimary = identity?.modelPrimary?.trim() || ''; - const modelFast = identity?.modelFast?.trim() || ''; const gradient = getCardGradient(workspace.id || name); @@ -67,8 +65,6 @@ const AssistantCard: React.FC = ({ workspace, onClick, onNew
{creature && {creature}} - {modelPrimary && {modelPrimary}} - {modelFast && {modelFast}}
diff --git a/src/web-ui/src/app/scenes/profile/views/TemplateConfigPage.tsx b/src/web-ui/src/app/scenes/profile/views/AssistantDefaultsPage.tsx similarity index 75% rename from src/web-ui/src/app/scenes/profile/views/TemplateConfigPage.tsx rename to src/web-ui/src/app/scenes/profile/views/AssistantDefaultsPage.tsx index ff8659a315..173280d8f6 100644 --- a/src/web-ui/src/app/scenes/profile/views/TemplateConfigPage.tsx +++ b/src/web-ui/src/app/scenes/profile/views/AssistantDefaultsPage.tsx @@ -12,19 +12,17 @@ import { } from 'lucide-react'; import { GalleryZone } from '@/app/components'; import '@/app/components/GalleryLayout/GalleryLayout.scss'; -import { Select, Switch, type SelectOption } from '@/component-library'; +import { Switch } from '@/component-library'; import { configAPI } from '@/infrastructure/api/service-api/ConfigAPI'; -import { configManager } from '@/infrastructure/config/services/ConfigManager'; -import type { AIModelConfig, AgentProfileConfigItem, ModeSkillInfo } from '@/infrastructure/config/types'; +import type { AgentProfileConfigItem, ModeSkillInfo } from '@/infrastructure/config/types'; import { MCPAPI, type MCPServerInfo } from '@/infrastructure/api/service-api/MCPAPI'; import { notificationService } from '@/shared/notification-system'; -import { UI_EXCEPTION_ACCENTS } from '@/shared/theme/uiExceptionAccents'; import type { DynamicToolInfo } from '@/shared/types/agent-api'; import { createLogger } from '@/shared/utils/logger'; +import { ModelSelector } from '@/flow_chat/components/ModelSelector'; import { useNurseryStore } from '../nurseryStore'; -import { formatTokenCount } from './useTokenEstimate'; -const log = createLogger('TemplateConfigPage'); +const log = createLogger('AssistantDefaultsPage'); const ASSISTANT_MODE_ID = 'Claw'; interface ToolInfo { @@ -39,8 +37,6 @@ type TemplateDetail = | { type: 'mcpServer'; serverId: string } | { type: 'skill'; skill: ModeSkillInfo }; -type ModelSlot = 'primary' | 'fast'; - function isMcpTool(tool: ToolInfo): boolean { return tool.dynamic_info?.providerKind === 'mcp' && Boolean(tool.dynamic_info.mcp); } @@ -53,45 +49,6 @@ function getMcpShortName(tool: ToolInfo): string { return tool.dynamic_info?.mcp?.toolName ?? tool.name; } -type CtxSegKey = 'systemPrompt' | 'toolInjection' | 'rules' | 'memories'; - -const CTX_SEGMENT_ORDER: readonly CtxSegKey[] = ['systemPrompt', 'toolInjection', 'rules', 'memories']; - -const CTX_SEGMENT_COLORS: Record = { - systemPrompt: 'var(--color-success)', - toolInjection: 'var(--color-accent-500)', - rules: 'var(--color-purple-soft)', - memories: UI_EXCEPTION_ACCENTS.templateContext.memories, -}; - -const CTX_LABEL_I18N_KEY: Record = { - systemPrompt: 'nursery.template.tokenSystemPrompt', - toolInjection: 'nursery.template.tokenToolInjection', - rules: 'nursery.template.tokenRules', - memories: 'nursery.template.tokenMemories', -}; - -function fmtPct(val: number, total: number): string { - if (total === 0) return '0%'; - return `${Math.round((val / total) * 100)}%`; -} - -// โ”€โ”€ Claw agent token estimates (based on actual prompt files) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -// claw_mode.md โ‰ˆ 838 tok + persona files (BOOTSTRAP/SOUL/USER/IDENTITY) โ‰ˆ 600 tok -const CLAW_SYS_TOKENS = 1438; -const TOKENS_PER_TOOL = 45; // matches backend estimation -const TOKENS_PER_RULE = 80; -const TOKENS_PER_MEMORY = 60; -const CTX_WINDOW = 128_000; - -interface MockBreakdown { - systemPrompt: number; - toolInjection: number; - rules: number; - memories: number; - total: number; -} - function buildDuplicateSkillNameSet(skills: ModeSkillInfo[]): Set { const counts = new Map(); for (const skill of skills) { @@ -115,24 +72,10 @@ function formatSkillDisplayName(skill: ModeSkillInfo, duplicateNames: Set { +const AssistantDefaultsPage: React.FC = () => { const { t } = useTranslation('scenes/profile'); const { openGallery } = useNurseryStore(); - const [models, setModels] = useState([]); - const [funcAgentModels, setFuncAgentModels] = useState>({}); const [assistantModeConfig, setAssistantModeConfig] = useState(null); const [availableTools, setAvailableTools] = useState([]); const [mcpServers, setMcpServers] = useState([]); @@ -143,11 +86,6 @@ const TemplateConfigPage: React.FC = () => { const [collapsedGroups, setCollapsedGroups] = useState>(new Set()); const [detail, setDetail] = useState(null); - const enabledToolCount = useMemo( - () => assistantModeConfig?.enabled_tools?.length ?? 0, - [assistantModeConfig], - ); - const skillsEnabled = useMemo( () => modeSkills.filter((skill) => skill.effectiveEnabled), [modeSkills], @@ -162,20 +100,6 @@ const TemplateConfigPage: React.FC = () => { [modeSkills], ); - const tokenBreakdown = useMemo( - () => buildMockBreakdown(enabledToolCount, 0, 0), - [enabledToolCount], - ); - - const ctxSegments = useMemo( - () => CTX_SEGMENT_ORDER.map((key) => ({ - key, - color: CTX_SEGMENT_COLORS[key], - label: t(CTX_LABEL_I18N_KEY[key]), - })), - [t], - ); - // Split tools into built-in vs MCP const builtinTools = useMemo( () => availableTools.filter((tool) => !isMcpTool(tool)), @@ -216,22 +140,18 @@ const TemplateConfigPage: React.FC = () => { setLoading(true); try { const { invoke } = await import('@tauri-apps/api/core'); - const [allModels, funcModels, modeConf, tools, skillList, servers] = await Promise.all([ - configManager.getConfig('ai.models').catch(() => [] as AIModelConfig[]), - configManager.getConfig>('ai.func_agent_models').catch(() => ({} as Record)), + const [modeConf, tools, skillList, servers] = await Promise.all([ configAPI.getAgentProfileConfig(ASSISTANT_MODE_ID).catch(() => null as AgentProfileConfigItem | null), invoke('get_all_tools_info').catch(() => [] as ToolInfo[]), configAPI.getModeSkillConfigs({ modeId: ASSISTANT_MODE_ID }).catch(() => [] as ModeSkillInfo[]), MCPAPI.getServers().catch(() => [] as MCPServerInfo[]), ]); - setModels(allModels ?? []); - setFuncAgentModels(funcModels ?? {}); setAssistantModeConfig(modeConf); setAvailableTools(tools); setModeSkills(skillList ?? []); setMcpServers(servers ?? []); } catch (e) { - log.error('Failed to load template config', e); + log.error('Failed to load assistant defaults config', e); } finally { setLoading(false); } @@ -247,42 +167,6 @@ const TemplateConfigPage: React.FC = () => { return () => window.removeEventListener('keydown', onKey); }, [detail]); - const buildModelOptions = useCallback((slot: ModelSlot): SelectOption[] => { - const presets: SelectOption[] = [ - { value: 'preset:primary', label: t('slotDefault.primary'), group: t('modelGroups.presets') }, - { value: 'preset:fast', label: t('slotDefault.fast'), group: t('modelGroups.presets') }, - ]; - const modelOptions: SelectOption[] = models - .filter((m) => m.enabled && !!m.id) - .map((m) => ({ value: `model:${m.id}`, label: m.name, group: t('modelGroups.models') })); - if (slot === 'fast') return [...presets, ...modelOptions]; - return [presets[0], ...modelOptions]; - }, [models, t]); - - const getSelectedValue = useCallback((slot: ModelSlot): string => { - const id = funcAgentModels[slot] ?? ''; - if (!id) return ''; - return ['primary', 'fast'].includes(id) ? `preset:${id}` : `model:${id}`; - }, [funcAgentModels]); - - const handleModelChange = useCallback(async ( - slot: ModelSlot, - raw: string | number | (string | number)[], - ) => { - if (Array.isArray(raw)) return; - const rawStr = String(raw); - const newId = rawStr.startsWith('preset:') ? rawStr.replace('preset:', '') : rawStr.replace('model:', ''); - const updated = { ...funcAgentModels, [slot]: newId }; - setFuncAgentModels(updated); - try { - await configManager.setConfig('ai.func_agent_models', updated); - notificationService.success(t('notifications.modelUpdated')); - } catch (e) { - log.error('Failed to update model', e); - notificationService.error(t('notifications.updateFailed')); - } - }, [funcAgentModels, t]); - const handleToolToggle = useCallback(async (toolName: string) => { if (!assistantModeConfig) return; setToolsLoading((prev) => ({ ...prev, [toolName]: true })); @@ -376,30 +260,6 @@ const TemplateConfigPage: React.FC = () => { }); }, []); - // Context breakdown: each segment = part / total (composition of consumed tokens) - const ctxTotal = tokenBreakdown.total; - - const segmentWidths = useMemo(() => { - if (ctxTotal === 0) return CTX_SEGMENT_ORDER.map(() => 0); - return CTX_SEGMENT_ORDER.map((key) => { - const val = tokenBreakdown[key]; - return typeof val === 'number' ? (val / ctxTotal) * 100 : 0; - }); - }, [tokenBreakdown, ctxTotal]); - - const contextZoneSubtitle = useMemo( - () => ( - <> - {formatTokenCount(ctxTotal)} - {' tok ยท '} - - {fmtPct(ctxTotal, CTX_WINDOW)} of {formatTokenCount(CTX_WINDOW)} - - - ), - [ctxTotal], - ); - const openToolDetail = useCallback((tool: ToolInfo, isMcp: boolean) => { setDetail((prev) => ( prev?.type === 'tool' && prev.tool.name === tool.name @@ -761,78 +621,24 @@ const TemplateConfigPage: React.FC = () => {
-
-
-
-
- {t('modelSlots.primary.label')} -
- handleModelChange('fast', v)} - placeholder={t('slotDefault.fast')} - /> -
+ tools={( +
+
+
-
- - - -
-
- {ctxTotal === 0 ? ( -
- ) : ctxSegments.map(({ key, color, label }, i) => ( - segmentWidths[i] > 0 && ( -
- ) - ))} -
- -
- {ctxSegments.map(({ key, color, label }) => { - const val = tokenBreakdown[key as keyof typeof tokenBreakdown]; - const num = typeof val === 'number' ? val : 0; - return ( -
- - {label} - {formatTokenCount(num)} - {fmtPct(num, ctxTotal)} -
- ); - })} -
-
+ {null} -
{ ); }; -export default TemplateConfigPage; +export default AssistantDefaultsPage; diff --git a/src/web-ui/src/app/scenes/profile/views/NurseryGallery.tsx b/src/web-ui/src/app/scenes/profile/views/NurseryGallery.tsx index 5f7e8b841e..3311b0f960 100644 --- a/src/web-ui/src/app/scenes/profile/views/NurseryGallery.tsx +++ b/src/web-ui/src/app/scenes/profile/views/NurseryGallery.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Plus, Egg, Settings, Star, Wrench, BarChart2 } from 'lucide-react'; +import { Plus, Egg, Settings, Star, Wrench } from 'lucide-react'; import { GalleryLayout, GalleryPageHeader, @@ -14,11 +14,11 @@ import { flowChatManager } from '@/flow_chat/services/FlowChatManager'; import type { WorkspaceInfo } from '@/shared/types'; import { configAPI } from '@/infrastructure/api/service-api/ConfigAPI'; import { configManager } from '@/infrastructure/config/services/ConfigManager'; -import type { AIModelConfig } from '@/infrastructure/config/types'; +import type { AIModelConfig, DefaultModelsConfig } from '@/infrastructure/config/types'; +import { getModelDisplayName } from '@/infrastructure/config/services/modelConfigs'; import { createLogger } from '@/shared/utils/logger'; import AssistantCard from './AssistantCard'; import { useNurseryStore } from '../nurseryStore'; -import { estimateTokens, formatTokenCount } from './useTokenEstimate'; interface DeleteConfirmState { workspaceId: string; @@ -26,10 +26,10 @@ interface DeleteConfirmState { } const log = createLogger('NurseryGallery'); +const ASSISTANT_MODE_ID = 'Claw'; interface TemplateStats { - primaryModelName: string; - fastModelName: string; + defaultModelName: string; enabledToolCount: number; } @@ -38,7 +38,7 @@ const NurseryGallery: React.FC = () => { const { assistantWorkspacesList, createAssistantWorkspace, setActiveWorkspace, deleteAssistantWorkspace } = useWorkspaceContext(); const openScene = useSceneStore(s => s.openScene); const { switchLeftPanelTab } = useApp(); - const { openTemplate, openAssistant } = useNurseryStore(); + const { openDefaults, openAssistant } = useNurseryStore(); const [creating, setCreating] = useState(false); const [deleting, setDeleting] = useState(false); const [deleteConfirm, setDeleteConfirm] = useState(null); @@ -47,24 +47,43 @@ const NurseryGallery: React.FC = () => { useEffect(() => { (async () => { try { - const [allModels, funcModels, modeConf] = await Promise.all([ + const [allModels, defaultModels, agentModels, modeConf] = await Promise.all([ configManager.getConfig('ai.models').catch(() => [] as AIModelConfig[]), - configManager.getConfig>('ai.func_agent_models').catch(() => ({} as Record)), - configAPI.getAgentProfileConfig('agentic').catch(() => null), + configManager.getConfig('ai.default_models').catch(() => ({} as DefaultModelsConfig)), + configManager.getConfig>('ai.agent_models').catch(() => ({} as Record)), + configAPI.getAgentProfileConfig(ASSISTANT_MODE_ID).catch(() => null), ]); const models = allModels ?? []; - const fm = funcModels ?? {}; + const defaults = defaultModels ?? {}; + const configuredAgentModels = agentModels ?? {}; - const resolveModelName = (slotId: string, fallback: string): string => { - const id = fm[slotId] ?? ''; - if (!id || id === slotId) return fallback; - const found = models.find((m) => m.id === id && m.enabled); - return found?.name ?? fallback; + const findEnabledModelByRef = (modelRef?: string | null): AIModelConfig | null => { + const trimmed = modelRef?.trim(); + if (!trimmed) return null; + return models.find((model) => model.enabled && model.id === trimmed) ?? null; + }; + + const resolveClawDefaultModelName = (): string => { + const configuredModel = configuredAgentModels[ASSISTANT_MODE_ID]?.trim() || 'auto'; + if (configuredModel === 'auto') { + return t('nursery.template.stats.autoDefault'); + } + if (configuredModel === 'primary') { + return findEnabledModelByRef(defaults.primary) + ? getModelDisplayName(findEnabledModelByRef(defaults.primary)!) + : t('nursery.template.stats.primaryDefault'); + } + if (configuredModel === 'fast') { + const fastModel = findEnabledModelByRef(defaults.fast) ?? findEnabledModelByRef(defaults.primary); + return fastModel ? getModelDisplayName(fastModel) : t('nursery.template.stats.fastDefault'); + } + + const explicitModel = findEnabledModelByRef(configuredModel); + return explicitModel ? getModelDisplayName(explicitModel) : configuredModel; }; setTemplateStats({ - primaryModelName: resolveModelName('primary', t('nursery.template.stats.primaryDefault')), - fastModelName: resolveModelName('fast', t('nursery.template.stats.fastDefault')), + defaultModelName: resolveClawDefaultModelName(), enabledToolCount: modeConf?.enabled_tools?.length ?? 0, }); } catch (e) { @@ -73,11 +92,6 @@ const NurseryGallery: React.FC = () => { })(); }, [t]); - const tokenBreakdown = useMemo( - () => (templateStats ? estimateTokens('', templateStats.enabledToolCount, 0, 0) : null), - [templateStats], - ); - const handleCreateAssistant = useCallback(async () => { if (creating) return; setCreating(true); @@ -177,7 +191,7 @@ const NurseryGallery: React.FC = () => {