diff --git a/docs/plans/state.json b/docs/plans/state.json index 250c344..956fdee 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -5710,6 +5710,17 @@ "docs/plans/state.json" ], "test_status": "pnpm test:run src/gateway/handlers/handlers.test.ts + pnpm typecheck passing" + }, + "dashboard-model-defaults-draft-preservation": { + "status": "completed", + "date": "2026-02-19", + "updated": "2026-02-19", + "summary": "Fixed Model Tier Defaults dropdown reset during periodic dashboard refresh by preserving unsaved form draft state (tiers, delegation, and background override settings) across Assistant Health rerenders until saved.", + "files_modified": [ + "src/gateway/ui/pages/dashboard.js", + "docs/plans/state.json" + ], + "test_status": "pnpm typecheck passing" } }, "overall_progress": { diff --git a/src/gateway/ui/pages/dashboard.js b/src/gateway/ui/pages/dashboard.js index 10d5822..0949fdd 100644 --- a/src/gateway/ui/pages/dashboard.js +++ b/src/gateway/ui/pages/dashboard.js @@ -13,6 +13,10 @@ let _lastBriefingTestAt = null; let _assistantSaveState = null; let _lastAssistantConfig = null; let _assistantManualOverrides = new Set(); +let _assistantModelDefaultsDraft = null; + +const MODEL_DEFAULT_TASK_KEYS = ['compaction', 'memory_extraction', 'classification', 'tool_summarisation', 'complex_reasoning']; +const MODEL_DEFAULT_TIER_KEYS = ['default', 'fast', 'complex', 'local']; function formatUptime(seconds) { const d = Math.floor(seconds / 86400); @@ -666,6 +670,7 @@ async function triggerDailyBriefingTest(jobName, statusEl) { function updateAssistantHealth(configData) { const el = document.getElementById('ops-assistant-health'); if (!el) {return;} + _assistantModelDefaultsDraft = readAssistantModelDefaultsDraft(el) ?? _assistantModelDefaultsDraft; const snapshot = getAssistantStateSnapshot(configData); @@ -692,7 +697,7 @@ function updateAssistantHealth(configData) { : 'not configured'; const briefingReady = dailyBriefing && Boolean(briefingOutput?.channel && briefingOutput?.peer); const playbookLikeReady = announce || (memoryDaily && memoryProactive); - const modelTier = configData?.agents?.primary_tier ?? 'default'; + const modelTier = _assistantModelDefaultsDraft?.primaryTier ?? configData?.agents?.primary_tier ?? 'default'; const delegation = configData?.agents?.delegation ?? {}; const backgroundModels = configData?.agents?.background_models ?? {}; const tiers = configData?.models ?? {}; @@ -787,10 +792,10 @@ function updateAssistantHealth(configData) {
Model Tier Defaults
Tier provider/model definitions
- ${['default', 'fast', 'complex', 'local'].map((tier) => { + ${MODEL_DEFAULT_TIER_KEYS.map((tier) => { const cfg = tiers?.[tier] ?? {}; - const provider = cfg.provider ?? tiers?.default?.provider ?? 'openai'; - const model = cfg.model ?? ''; + const provider = _assistantModelDefaultsDraft?.tiers?.[tier]?.provider ?? cfg.provider ?? tiers?.default?.provider ?? 'openai'; + const model = _assistantModelDefaultsDraft?.tiers?.[tier]?.model ?? cfg.model ?? ''; return `
${escapeHtml(tier)} tier
@@ -822,8 +827,9 @@ function updateAssistantHealth(configData) {
${taskRows.map((task) => { const background = backgroundModels?.[task.key] ?? {}; - const delegationTier = delegation?.[task.key] ?? 'fast'; - const backgroundEnabled = Boolean(backgroundModels?.[task.key] && background?.enabled !== false); + const draftTask = _assistantModelDefaultsDraft?.tasks?.[task.key] ?? {}; + const delegationTier = draftTask.delegationTier ?? delegation?.[task.key] ?? 'fast'; + const backgroundEnabled = draftTask.backgroundEnabled ?? Boolean(backgroundModels?.[task.key] && background?.enabled !== false); return `
${escapeHtml(task.label)}
@@ -841,17 +847,21 @@ function updateAssistantHealth(configData) {
@@ -928,7 +938,7 @@ function updateAssistantHealth(configData) { }); } - const taskRowsForModels = ['compaction', 'memory_extraction', 'classification', 'tool_summarisation', 'complex_reasoning']; + const taskRowsForModels = MODEL_DEFAULT_TASK_KEYS; for (const task of taskRowsForModels) { const providerSelect = el.querySelector(`#assist-bg-${task}-provider`); if (!providerSelect) {continue;} @@ -996,11 +1006,11 @@ function updateAssistantHealth(configData) { }; _assistantManualOverrides.add('automation.daily_briefing.enabled'); } else if (action === 'save-model-defaults') { - const tasks = ['compaction', 'memory_extraction', 'classification', 'tool_summarisation', 'complex_reasoning']; + const tasks = MODEL_DEFAULT_TASK_KEYS; patches = { 'agents.primary_tier': (el.querySelector('#assist-primary-tier')?.value ?? 'default'), }; - const tiers = ['default', 'fast', 'complex', 'local']; + const tiers = MODEL_DEFAULT_TIER_KEYS; for (const tier of tiers) { const provider = (el.querySelector(`#assist-tier-${tier}-provider`)?.value ?? '').trim(); const model = (el.querySelector(`#assist-tier-${tier}-model`)?.value ?? '').trim(); @@ -1033,7 +1043,12 @@ function updateAssistantHealth(configData) { } } if (!patches) {return;} - await applyAssistantPatch(patches, statusEl); + const patchResult = await applyAssistantPatch(patches, statusEl); + if (action === 'save-model-defaults' && patchResult.applied.length > 0 && patchResult.rejected.length === 0) { + _assistantModelDefaultsDraft = null; + } else if (action === 'save-model-defaults') { + _assistantModelDefaultsDraft = readAssistantModelDefaultsDraft(el) ?? _assistantModelDefaultsDraft; + } // Force immediate refresh of slow sections after applying. const refreshed = await fetchSlow(_dashboardClient); if (refreshed) { @@ -1052,6 +1067,38 @@ function updateAssistantHealth(configData) { }); } +function readAssistantModelDefaultsDraft(rootEl) { + const primaryTier = rootEl.querySelector('#assist-primary-tier')?.value; + if (!primaryTier) { + return null; + } + + const tiers = {}; + for (const tier of MODEL_DEFAULT_TIER_KEYS) { + const provider = (rootEl.querySelector(`#assist-tier-${tier}-provider`)?.value ?? '').trim(); + const model = (rootEl.querySelector(`#assist-tier-${tier}-model`)?.value ?? '').trim(); + if (!provider && !model) {continue;} + tiers[tier] = { provider, model }; + } + + const tasks = {}; + for (const task of MODEL_DEFAULT_TASK_KEYS) { + tasks[task] = { + delegationTier: rootEl.querySelector(`#assist-delegation-${task}`)?.value ?? 'fast', + backgroundEnabled: Boolean(rootEl.querySelector(`#assist-bg-${task}-enabled`)?.checked), + provider: (rootEl.querySelector(`#assist-bg-${task}-provider`)?.value ?? '').trim(), + model: (rootEl.querySelector(`#assist-bg-${task}-model`)?.value ?? '').trim(), + fallbackTier: rootEl.querySelector(`#assist-bg-${task}-fallback`)?.value ?? 'fast', + }; + } + + return { + primaryTier, + tiers, + tasks, + }; +} + function _updateChannels(channelsData) { const el = document.getElementById('ops-channels'); if (!el) {return;} @@ -1248,5 +1295,6 @@ export const DashboardPage = { _assistantSaveState = null; _lastAssistantConfig = null; _assistantManualOverrides = new Set(); + _assistantModelDefaultsDraft = null; }, };