/**
* Flynn Live Ops Dashboard
*
* Shows core counters, model performance, event stream, active requests,
* and channel status. Fast metrics refresh every 3s, slow health every 10s.
*/
let _fastTimer = null;
let _slowTimer = null;
let _dashboardClient = null;
let _lastPlaybookRollbackPatches = null;
let _lastBriefingTestAt = null;
let _assistantSaveState = null;
let _lastAssistantConfig = null;
let _assistantManualOverrides = new Set();
let _assistantModelDefaultsDraft = null;
let _assistantDraftState = new Map();
let _assistantDraftTouchedAt = 0;
let _lastCouncilTask = '';
let _lastCouncilResult = null;
let _lastCouncilError = null;
let _lastServices = [];
let _serviceConfigState = {
open: false,
serviceName: null,
status: null,
tone: 'neutral',
advancedPatch: '',
};
const MODEL_DEFAULT_TASK_KEYS = ['compaction', 'memory_extraction', 'classification', 'tool_summarisation', 'complex_reasoning'];
const MODEL_DEFAULT_TIER_KEYS = ['default', 'fast', 'complex', 'local'];
const HEARTBEAT_CHECK_KEYS = ['gateway', 'model', 'channels', 'memory', 'disk', 'process_memory', 'backup', 'provider_errors'];
const SERVICE_TOGGLE_PATCH_PATHS = {
heartbeat: 'automation.heartbeat.enabled',
daily_briefing: 'automation.daily_briefing.enabled',
gmail: 'automation.gmail.enabled',
gcal: 'automation.gcal.enabled',
gdocs: 'automation.gdocs.enabled',
gdrive: 'automation.gdrive.enabled',
gtasks: 'automation.gtasks.enabled',
backup: 'backup.enabled',
audio_transcription: 'audio.enabled',
sandbox: 'sandbox.enabled',
};
const ASSISTANT_DRAFT_TTL_MS = 2 * 60 * 1000;
function formatUptime(seconds) {
const d = Math.floor(seconds / 86400);
const h = Math.floor((seconds % 86400) / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
const parts = [];
if (d > 0) {parts.push(`${d}d`);}
if (h > 0) {parts.push(`${h}h`);}
if (m > 0) {parts.push(`${m}m`);}
parts.push(`${s}s`);
return parts.join(' ');
}
function timeAgo(timestamp) {
const secs = Math.floor((Date.now() - timestamp) / 1000);
if (secs < 60) {return `${secs}s ago`;}
if (secs < 3600) {return `${Math.floor(secs / 60)}m ago`;}
return `${Math.floor(secs / 3600)}h ago`;
}
function formatTime(timestamp) {
const d = new Date(timestamp);
return d.toLocaleTimeString('en-GB', { hour12: false });
}
function formatDay(day) {
const parsed = new Date(`${day}T00:00:00`);
if (Number.isNaN(parsed.getTime())) {return day;}
return parsed.toLocaleDateString('en-GB', { day: '2-digit', month: 'short' });
}
function formatNumber(value) {
return (value ?? 0).toLocaleString();
}
function formatSessionDurationFromMessages(avgMessagesPerSession) {
if (!avgMessagesPerSession || avgMessagesPerSession <= 0) {return '—';}
return `${avgMessagesPerSession.toFixed(1)} msgs/session`;
}
function escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function getAssistantStateSnapshot(configData) {
const automation = configData?.automation ?? {};
const memory = configData?.memory ?? {};
const tts = configData?.tts ?? {};
const queue = configData?.server?.queue ?? {};
return {
announce: (automation.delivery_mode ?? 'shared_session') === 'announce',
dailyBriefing: Boolean(automation.daily_briefing?.enabled),
memoryDaily: Boolean(memory.daily_log?.enabled),
memoryProactive: Boolean(memory.proactive_extract?.enabled),
memoryMinToolCalls: Number(memory.proactive_extract?.min_tool_calls ?? 1),
ttsEnabled: Boolean(tts.enabled),
ttsChannels: Array.isArray(tts.enabled_channels) ? tts.enabled_channels : [],
queueMode: queue.mode ?? 'collect',
};
}
function buildPlaybookPatches(playbook) {
if (playbook === 'executive') {
const patches = {
'automation.delivery_mode': 'announce',
'automation.daily_briefing.enabled': true,
'memory.daily_log.enabled': true,
'memory.proactive_extract.enabled': true,
'memory.proactive_extract.min_tool_calls': 1,
'tts.enabled': true,
'tts.enabled_channels': [],
'server.queue.mode': 'interrupt',
};
for (const key of _assistantManualOverrides) {
delete patches[key];
}
return patches;
}
if (playbook === 'operator') {
const patches = {
'automation.delivery_mode': 'announce',
'automation.daily_briefing.enabled': true,
'memory.daily_log.enabled': true,
'memory.proactive_extract.enabled': true,
'memory.proactive_extract.min_tool_calls': 2,
'tts.enabled': false,
'server.queue.mode': 'steer_backlog',
};
for (const key of _assistantManualOverrides) {
delete patches[key];
}
return patches;
}
const patches = {
'automation.delivery_mode': 'shared_session',
'automation.daily_briefing.enabled': false,
'memory.daily_log.enabled': false,
'memory.proactive_extract.enabled': false,
'memory.proactive_extract.min_tool_calls': 3,
'tts.enabled': false,
'server.queue.mode': 'collect',
};
for (const key of _assistantManualOverrides) {
delete patches[key];
}
return patches;
}
function buildRollbackPatchesFromSnapshot(snapshot) {
return {
'automation.delivery_mode': snapshot.announce ? 'announce' : 'shared_session',
'automation.daily_briefing.enabled': snapshot.dailyBriefing,
'memory.daily_log.enabled': snapshot.memoryDaily,
'memory.proactive_extract.enabled': snapshot.memoryProactive,
'memory.proactive_extract.min_tool_calls': Number.isFinite(snapshot.memoryMinToolCalls) ? snapshot.memoryMinToolCalls : 1,
'tts.enabled': snapshot.ttsEnabled,
'tts.enabled_channels': snapshot.ttsChannels,
'server.queue.mode': snapshot.queueMode,
};
}
function getByPath(obj, dottedPath) {
if (!obj || typeof obj !== 'object') {return undefined;}
const parts = dottedPath.split('.');
let cursor = obj;
for (const part of parts) {
if (!cursor || typeof cursor !== 'object' || !(part in cursor)) {
return undefined;
}
cursor = cursor[part];
}
return cursor;
}
function valuesMatch(expected, actual) {
if (Array.isArray(expected) && Array.isArray(actual)) {
if (expected.length !== actual.length) {return false;}
for (let i = 0; i < expected.length; i++) {
if (expected[i] !== actual[i]) {return false;}
}
return true;
}
return expected === actual;
}
function valuesMatchForPath(path, expected, actual) {
if (valuesMatch(expected, actual)) {
return true;
}
// Some optional string paths are normalized by config handlers:
// writing "" is persisted as "unset" (undefined).
if (typeof expected === 'string' && expected.trim().length === 0 && (actual === undefined || actual === null)) {
const optionalStringSuffixes = ['scaffold_path'];
if (optionalStringSuffixes.some((suffix) => path.endsWith(suffix))) {
return true;
}
}
return false;
}
function setAssistantSaveState(message, tone = 'neutral') {
_assistantSaveState = {
message,
tone,
at: Date.now(),
};
}
function writeAssistantDraftValue(control) {
if (!control || !control.id) {return;}
const isCheckbox = control.tagName === 'INPUT' && control.type === 'checkbox';
if (isCheckbox) {
_assistantDraftState.set(control.id, { kind: 'checkbox', value: Boolean(control.checked) });
} else if (control.tagName === 'SELECT') {
const selectedOption = Array.from(control.options ?? []).find((option) => option.selected);
_assistantDraftState.set(control.id, { kind: 'value', value: selectedOption?.value ?? '' });
} else {
_assistantDraftState.set(control.id, { kind: 'value', value: control.value ?? '' });
}
_assistantDraftTouchedAt = Date.now();
}
function bindAssistantDraftTracking(rootEl) {
const controls = rootEl.querySelectorAll('input[id], select[id], textarea[id]');
controls.forEach((control) => {
control.addEventListener('input', () => writeAssistantDraftValue(control));
control.addEventListener('change', () => writeAssistantDraftValue(control));
});
}
function applyAssistantDraftState(rootEl) {
if (_assistantDraftState.size === 0) {return;}
const now = Date.now();
if (_assistantDraftTouchedAt > 0 && (now - _assistantDraftTouchedAt) > ASSISTANT_DRAFT_TTL_MS) {
_assistantDraftState = new Map();
_assistantDraftTouchedAt = 0;
return;
}
for (const [id, draft] of _assistantDraftState.entries()) {
const control = rootEl.querySelector(`#${id}`);
if (!control) {continue;}
const isCheckbox = control.tagName === 'INPUT' && control.type === 'checkbox';
if (draft.kind === 'checkbox' && isCheckbox) {
control.checked = Boolean(draft.value);
} else if (control.tagName === 'SELECT') {
const options = Array.from(control.options ?? []);
const desired = String(draft.value ?? '');
let matchedIndex = -1;
for (let i = 0; i < options.length; i++) {
const option = options[i];
const isSelected = option.value === desired;
option.selected = isSelected;
if (isSelected) {
matchedIndex = i;
}
}
if (options.length > 0) {
const fallbackIndex = matchedIndex >= 0 ? matchedIndex : 0;
options[fallbackIndex].selected = true;
if ('selectedIndex' in control) {
control.selectedIndex = fallbackIndex;
}
}
try {
control.value = desired;
} catch {
// Some DOM shims expose readonly value on select nodes.
}
} else if ('value' in control) {
try {
control.value = String(draft.value ?? '');
} catch {
// Ignore rare non-writable value surfaces from non-browser DOM shims.
}
}
}
}
function renderAssistantSaveState() {
if (!_assistantSaveState) {
return '
No recent save action.
';
}
const toneClass = _assistantSaveState.tone === 'success'
? 'text-green-500'
: _assistantSaveState.tone === 'warning'
? 'text-amber-500'
: _assistantSaveState.tone === 'error'
? 'text-red-500'
: 'text-zinc-500';
const at = new Date(_assistantSaveState.at).toLocaleTimeString(undefined, {
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
});
return `${escapeHtml(_assistantSaveState.message)} (at ${escapeHtml(at)})
`;
}
function extractCouncilResultFromOutput(output) {
if (typeof output !== 'string' || output.trim().length === 0) {
return null;
}
const marker = '{"pipeline_version"';
const idx = output.lastIndexOf(marker);
if (idx < 0) {
return null;
}
try {
return JSON.parse(output.slice(idx));
} catch {
return null;
}
}
// ── Initial full render ─────────────────────────────────────────
function renderSkeleton(el) {
el.innerHTML = `
Live Ops Dashboard
Core Counters
Model Performance
Session Analytics
Context Health
Assistant Health
Event Stream
Active Requests
Services
Click a service card to configure.
`;
}
// ── Section updaters (targeted DOM updates) ─────────────────────
function updateCounters(metrics, health) {
const el = document.getElementById('ops-counters');
if (!el) {return;}
const sessions = health?.sessions ?? 0;
const errCount = metrics?.errors ?? 0;
const cards = [
{ label: 'Messages Processed', value: String(metrics?.messagesProcessed ?? 0), cls: '' },
{ label: 'Active Sessions', value: String(sessions), cls: '' },
{ label: 'Queue Depth', value: String(metrics?.queueDepth ?? 0), cls: '' },
{ label: 'Uptime', value: formatUptime(metrics?.uptime ?? 0), cls: '' },
{ label: 'Active Requests', value: String(metrics?.activeRequests ?? 0), cls: '' },
{ label: 'Errors', value: String(errCount), cls: errCount > 0 ? 'text-red-500' : '' },
];
el.innerHTML = cards.map(c =>
``,
).join('');
}
function updateModelTable(metrics) {
const el = document.getElementById('ops-model-table');
if (!el) {return;}
const mc = metrics?.modelCalls;
const calls = mc?.recentCalls ?? [];
if (calls.length === 0) {
el.innerHTML = 'No model calls recorded yet
';
return;
}
const totalCalls = mc.total ?? 0;
const avgLatency = mc.avgLatency ?? 0;
const errorRate = mc.errorRate ?? 0;
const summaryHtml = `
Total Calls: ${totalCalls}
Avg Latency: ${avgLatency}ms
Error Rate: ${(errorRate * 100).toFixed(2)}%
`;
// Show newest first
const rows = [...calls].reverse().map(c => {
const status = c.error ? '✗' : '✓';
return `
| ${timeAgo(c.timestamp)} |
${escapeHtml(c.provider)} |
${c.latency}ms |
${c.tokensPerSec.toFixed(1)} |
${c.inputTokens}/${c.outputTokens} |
${status} |
`;
}).join('');
el.innerHTML = `
${summaryHtml}
| Time |
Provider |
Latency |
Tokens/sec |
In/Out |
Status |
${rows}
`;
}
function updateEvents(eventsData) {
const el = document.getElementById('ops-events');
if (!el) {return;}
const events = eventsData?.events ?? [];
if (events.length === 0) {
el.innerHTML = 'No events recorded yet
';
return;
}
// Events come newest-first from the API; show newest at bottom for log feel
const reversed = [...events].reverse();
el.innerHTML = reversed.map(e => {
const time = formatTime(e.timestamp);
const level = (e.level || 'info').toUpperCase();
const levelColor = e.level === 'error' ? 'text-red-500' : e.level === 'warn' ? 'text-amber-500' : 'text-zinc-400';
return `[${time}] [${level}] ${escapeHtml(e.source)}: ${escapeHtml(e.message)}
`;
}).join('');
// Auto-scroll to bottom
el.scrollTop = el.scrollHeight;
}
function updateActiveRequests(requestsData) {
const el = document.getElementById('ops-requests');
if (!el) {return;}
const requests = requestsData?.requests ?? [];
if (requests.length === 0) {
el.innerHTML = 'No active requests
';
return;
}
const rows = requests.map(r => {
const duration = r.durationMs < 1000
? `${r.durationMs}ms`
: `${(r.durationMs / 1000).toFixed(1)}s`;
const started = formatTime(r.startedAt);
return `
| ${escapeHtml(r.sessionId)} |
${escapeHtml(r.channel)} |
${duration} |
${started} |
`;
}).join('');
el.innerHTML = `
| Session |
Channel |
Duration |
Started |
${rows}
`;
}
function updateSessionAnalytics(analyticsData) {
const el = document.getElementById('ops-session-analytics');
if (!el) {return;}
const daily = analyticsData?.daily ?? [];
const topSessions = analyticsData?.topSessions ?? [];
const topTools = analyticsData?.topTools ?? [];
const topTopics = analyticsData?.topTopics ?? [];
const totalSessions = analyticsData?.totalSessions ?? 0;
const totalMessages = analyticsData?.totalMessages ?? 0;
const avgMessagesPerSession = analyticsData?.averageMessagesPerSession ?? 0;
const summaryHtml = `
Sessions (Window)
${formatNumber(totalSessions)}
Messages (Window)
${formatNumber(totalMessages)}
Avg Session
${formatSessionDurationFromMessages(avgMessagesPerSession)}
`;
const topToolsHtml = topTools.length > 0
? `${topTools.map((tool) =>
`
${escapeHtml(tool.toolName)} (${formatNumber(tool.executions)})
`,
).join('')}
`
: 'No tool usage captured in this window
';
const topTopicsHtml = topTopics.length > 0
? `${topTopics.map((topic) =>
`
${escapeHtml(topic.topic)} (${formatNumber(topic.occurrences)})
`,
).join('')}
`
: 'No indexed topics captured in this window
';
const topSessionsHtml = topSessions.length > 0
? `
| Session |
Messages |
Last Active |
${topSessions.map((session) => `
| ${escapeHtml(session.sessionId)} |
${formatNumber(session.messages)} |
${timeAgo(session.lastActivity * 1000)} |
`).join('')}
`
: 'No session activity in this window
';
const dailyHtml = daily.length > 0
? `
| Day |
Sessions |
Messages |
${daily.slice(0, 7).map((row) => `
| ${formatDay(row.day)} |
${formatNumber(row.sessions)} |
${formatNumber(row.messages)} |
`).join('')}
`
: 'No daily activity in this window
';
el.innerHTML = `
${summaryHtml}
Top Tools
${topToolsHtml}
Top Topics
${topTopicsHtml}
Top Sessions
${topSessionsHtml}
Daily Trend (Last 7 Rows)
${dailyHtml}
`;
}
function updateContextHealth(contextData) {
const el = document.getElementById('ops-context-health');
if (!el) {return;}
const sessions = contextData?.sessions ?? [];
if (sessions.length === 0) {
el.innerHTML = 'No active context usage snapshots
';
return;
}
const sorted = [...sessions].sort((a, b) => (b.budget?.usagePct ?? 0) - (a.budget?.usagePct ?? 0));
const top = sorted.slice(0, 8);
const highest = top[0]?.budget?.usagePct ?? 0;
const overThreshold = sessions.filter(s => (s.budget?.shouldCompact ?? false)).length;
const summary = `
Highest Usage
${highest.toFixed(1)}%
Sessions Near Limit
${overThreshold}
Active Snapshots
${sessions.length}
`;
const rows = top.map((entry) => {
const budget = entry.budget ?? {};
const usage = budget.usagePct ?? 0;
const cls = usage >= 95 ? 'text-red-500' : usage >= 85 ? 'text-amber-500' : '';
return `
| ${escapeHtml(entry.sessionId)} |
${usage.toFixed(1)}% |
${formatNumber(budget.estimatedTokens ?? 0)} / ${formatNumber(budget.contextWindow ?? 0)} |
${budget.shouldCompact ? 'yes' : 'no'} |
`;
}).join('');
el.innerHTML = `
${summary}
| Session |
Usage |
Estimated Tokens |
Should Compact |
${rows}
`;
}
async function applyAssistantPatch(patches, statusEl) {
if (!_dashboardClient) {
setAssistantSaveState('Save skipped: dashboard client unavailable.', 'error');
return;
}
if (statusEl) {
statusEl.textContent = 'Saving...';
statusEl.className = 'text-sm text-zinc-500';
}
try {
const result = await _dashboardClient.call('config.patch', { patches });
const rejected = result?.rejected ?? [];
const persistError = result?.persistError;
const applied = result?.applied ?? [];
const persisted = result?.persisted === true;
let message = '';
let tone = 'neutral';
if (statusEl) {
if (persistError) {
message = `Save failed: ${persistError}`;
tone = 'error';
} else if (rejected.length > 0) {
message = `Rejected: ${rejected.join(', ')}`;
tone = 'error';
} else if (!persisted) {
message = `Runtime-only save (${applied.length} updated, file persistence unavailable)`;
tone = 'warning';
} else {
// Verify read-after-write so UI cannot claim persistence when value did not stick.
try {
const fresh = await _dashboardClient.call('config.get');
const mismatches = [];
for (const [key, value] of Object.entries(patches)) {
const actual = getByPath(fresh, key);
if (!valuesMatchForPath(key, value, actual)) {
mismatches.push(`${key} expected=${JSON.stringify(value)} actual=${JSON.stringify(actual)}`);
}
}
if (mismatches.length > 0) {
message = `Saved response received but read-back mismatch: ${mismatches.join('; ')}`;
tone = 'error';
} else {
message = `Saved to runtime + config file (${applied.length} updated)`;
tone = 'success';
}
} catch (verifyError) {
message = `Saved response received, but verification failed: ${verifyError instanceof Error ? verifyError.message : String(verifyError)}`;
tone = 'warning';
}
}
setAssistantSaveState(message, tone);
statusEl.textContent = message;
statusEl.className = `text-sm ${tone === 'success' ? 'text-green-500' : tone === 'warning' ? 'text-amber-500' : tone === 'error' ? 'text-red-500' : 'text-zinc-500'}`;
}
return { persisted, applied, rejected, persistError };
} catch (error) {
const msg = `Save error: ${error instanceof Error ? error.message : String(error)}`;
setAssistantSaveState(msg, 'error');
if (statusEl) {
statusEl.textContent = msg;
statusEl.className = 'text-sm text-red-500';
}
return { persisted: false, applied: [], rejected: [], persistError: msg };
}
}
async function triggerDailyBriefingTest(jobName, statusEl) {
if (!_dashboardClient) {return;}
if (statusEl) {
statusEl.textContent = 'Triggering test briefing...';
statusEl.className = 'text-sm text-zinc-500';
}
try {
const result = await _dashboardClient.call('tools.invoke', {
tool: 'cron.trigger',
args: { name: jobName },
});
if (result?.success) {
const output = typeof result.output === 'string' ? result.output : 'Triggered.';
if (statusEl) {
statusEl.textContent = output;
statusEl.className = 'text-sm text-green-500';
}
_lastBriefingTestAt = Date.now();
return true;
}
if (statusEl) {
statusEl.textContent = result?.error ?? 'Failed to trigger briefing.';
statusEl.className = 'text-sm text-red-500';
}
return false;
} catch (error) {
if (statusEl) {
statusEl.textContent = `Trigger error: ${error instanceof Error ? error.message : String(error)}`;
statusEl.className = 'text-sm text-red-500';
}
return false;
}
}
async function triggerCouncilRun(task, statusEl) {
if (!_dashboardClient) {return false;}
if (!task) {
if (statusEl) {
statusEl.textContent = 'Task is required.';
statusEl.className = 'text-sm text-red-500';
}
return false;
}
if (statusEl) {
statusEl.textContent = 'Running council...';
statusEl.className = 'text-sm text-zinc-500';
}
try {
const result = await _dashboardClient.call('tools.invoke', {
tool: 'council.run',
args: { task },
});
if (!result?.success) {
_lastCouncilError = result?.error ?? 'Council run failed.';
_lastCouncilResult = null;
if (statusEl) {
statusEl.textContent = _lastCouncilError;
statusEl.className = 'text-sm text-red-500';
}
return false;
}
const parsed = extractCouncilResultFromOutput(result.output);
if (!parsed) {
_lastCouncilError = 'Council run succeeded but output could not be parsed.';
_lastCouncilResult = null;
if (statusEl) {
statusEl.textContent = _lastCouncilError;
statusEl.className = 'text-sm text-amber-500';
}
return false;
}
_lastCouncilTask = task;
_lastCouncilResult = parsed;
_lastCouncilError = null;
if (statusEl) {
statusEl.textContent = `Council run complete: ${parsed.stop_snapshot?.stop_reason ?? 'ok'}`;
statusEl.className = 'text-sm text-green-500';
}
return true;
} catch (error) {
_lastCouncilError = `Council run error: ${error instanceof Error ? error.message : String(error)}`;
_lastCouncilResult = null;
if (statusEl) {
statusEl.textContent = _lastCouncilError;
statusEl.className = 'text-sm text-red-500';
}
return false;
}
}
function updateAssistantHealth(configData) {
const el = document.getElementById('ops-assistant-health');
if (!el) {return;}
_assistantModelDefaultsDraft = readAssistantModelDefaultsDraft(el) ?? _assistantModelDefaultsDraft;
const snapshot = getAssistantStateSnapshot(configData);
const automation = configData?.automation ?? {};
const memory = configData?.memory ?? {};
const tts = configData?.tts ?? {};
const deliveryMode = automation.delivery_mode ?? 'shared_session';
const announce = deliveryMode === 'announce';
const dailyBriefing = Boolean(automation.daily_briefing?.enabled);
const memoryDaily = Boolean(memory.daily_log?.enabled);
const memoryProactive = Boolean(memory.proactive_extract?.enabled);
const proactiveThreshold = Number(memory.proactive_extract?.min_tool_calls ?? 1);
const ttsEnabled = Boolean(tts.enabled);
const briefing = automation.daily_briefing ?? {};
const briefingName = briefing.name ?? 'daily-briefing';
const briefingSchedule = briefing.schedule ?? '0 8 * * *';
const briefingPrompt = briefing.prompt ?? '';
const briefingOutput = briefing.output ?? null;
const briefingModelTier = briefing.model_tier ?? 'default';
const briefingTimezone = briefing.timezone ?? 'system';
const briefingOutputLabel = briefingOutput?.channel && briefingOutput?.peer
? `${briefingOutput.channel}/${briefingOutput.peer}`
: 'not configured';
const briefingReady = dailyBriefing && Boolean(briefingOutput?.channel && briefingOutput?.peer);
const playbookLikeReady = announce || (memoryDaily && memoryProactive);
const modelTier = _assistantModelDefaultsDraft?.primaryTier ?? configData?.agents?.primary_tier ?? 'default';
const delegation = configData?.agents?.delegation ?? {};
const backgroundModels = configData?.agents?.background_models ?? {};
const councils = configData?.councils ?? {};
const councilsDefaults = councils.defaults ?? {};
const councilsGroups = councils.groups ?? {};
const councilsD = councilsGroups.D ?? {};
const councilsP = councilsGroups.P ?? {};
const tiers = configData?.models ?? {};
const modelCatalog = configData?.__modelCatalog ?? [];
const providerList = modelCatalog.length > 0
? modelCatalog.map((entry) => entry.provider)
: ['anthropic', 'openai', 'gemini', 'openrouter', 'github', 'xai', 'ollama', 'llamacpp', 'bedrock', 'zhipuai', 'minimax', 'moonshot', 'synthetic'];
const modelOptionsByProvider = Object.fromEntries(modelCatalog.map((entry) => [entry.provider, entry.models ?? []]));
const checklistRows = [
{ label: 'Set briefing output channel + peer', done: Boolean(briefingOutput?.channel && briefingOutput?.peer) },
{ label: 'Enable assistant behavior profile', done: playbookLikeReady },
{ label: 'Send a test morning briefing', done: _lastBriefingTestAt !== null },
];
const chip = (label, value) => `
${escapeHtml(label)}
${value ? 'ON' : 'OFF'}
`;
const tierOption = (selected) => ['fast', 'default', 'complex', 'local']
.map((tier) => ``)
.join('');
const providerOption = (selected) => providerList
.map((provider) => ``)
.join('');
const modelDataList = (id, provider, selected) => {
const options = modelOptionsByProvider[provider] ?? [];
return `
`;
};
const taskRows = [
{ key: 'compaction', label: 'Compaction' },
{ key: 'memory_extraction', label: 'Memory extraction' },
{ key: 'classification', label: 'Classification' },
{ key: 'tool_summarisation', label: 'Tool summarisation' },
{ key: 'complex_reasoning', label: 'Complex reasoning' },
];
const councilConversations = Array.isArray(_lastCouncilResult?.conversations) ? _lastCouncilResult.conversations : [];
const councilSummary = _lastCouncilResult?.stop_snapshot
? `Last run: ${_lastCouncilResult.stop_snapshot.stop_reason} (round ${_lastCouncilResult.stop_snapshot.round_reached})`
: (_lastCouncilError ? `Last run failed: ${_lastCouncilError}` : 'No council run yet in this dashboard session.');
el.innerHTML = `
${chip('Announce Mode', announce)}
${chip('Daily Briefing', dailyBriefing)}
${chip('Memory Daily Log', memoryDaily)}
${chip('Proactive Extract', memoryProactive)}
${chip('TTS Replies', ttsEnabled)}
Extract Threshold
${Number.isFinite(proactiveThreshold) ? proactiveThreshold : 1}
Assistant Playbooks
Executive: announce + voice + aggressive interrupt. Operator: announce + memory-first + steer backlog. Focus: reactive, quieter mode.
Model Tier Defaults
Tier provider/model definitions
${MODEL_DEFAULT_TIER_KEYS.map((tier) => {
const cfg = tiers?.[tier] ?? {};
const provider = _assistantModelDefaultsDraft?.tiers?.[tier]?.provider ?? cfg.provider ?? tiers?.default?.provider ?? 'openai';
const model = _assistantModelDefaultsDraft?.tiers?.[tier]?.model ?? cfg.model ?? '';
return `
${escapeHtml(tier)} tier
`;
}).join('')}
Delegation tiers + background model overrides
${taskRows.map((task) => {
const background = backgroundModels?.[task.key] ?? {};
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 `
`;
}).join('')}
Assistant Activation Checklist
${checklistRows.map((row) => `
${row.done ? '✓' : '○'}
${escapeHtml(row.label)}
`).join('')}
Morning Brief Preview
name: ${escapeHtml(briefingName)}
schedule: ${escapeHtml(briefingSchedule)}
timezone: ${escapeHtml(briefingTimezone)}
tier: ${escapeHtml(briefingModelTier)}
output: ${escapeHtml(briefingOutputLabel)}
${escapeHtml(briefingPrompt || 'No daily briefing prompt configured.')}
${briefingReady ? '' : '
Enable daily briefing and set output channel/peer to test-send.
'}
${renderAssistantSaveState()}
`;
// Preserve local unsaved form edits across periodic dashboard refreshes.
applyAssistantDraftState(el);
const updateModelOptions = (inputId, provider) => {
const input = el.querySelector(`#${inputId}`);
const list = el.querySelector(`#${inputId}-list`);
if (!input || !list) {return;}
const options = modelOptionsByProvider[provider] ?? [];
list.innerHTML = options.map((model) => ``).join('');
};
const tierRows = ['default', 'fast', 'complex', 'local'];
for (const tier of tierRows) {
const providerSelect = el.querySelector(`#assist-tier-${tier}-provider`);
if (!providerSelect) {continue;}
providerSelect.addEventListener('change', () => {
updateModelOptions(`assist-tier-${tier}-model`, providerSelect.value);
});
}
const taskRowsForModels = MODEL_DEFAULT_TASK_KEYS;
for (const task of taskRowsForModels) {
const providerSelect = el.querySelector(`#assist-bg-${task}-provider`);
if (!providerSelect) {continue;}
providerSelect.addEventListener('change', () => {
updateModelOptions(`assist-bg-${task}-model`, providerSelect.value);
});
}
// Refresh datalist options after draft re-application in case provider selects were restored.
for (const tier of tierRows) {
const providerSelect = el.querySelector(`#assist-tier-${tier}-provider`);
if (!providerSelect) {continue;}
updateModelOptions(`assist-tier-${tier}-model`, providerSelect.value);
}
for (const task of taskRowsForModels) {
const providerSelect = el.querySelector(`#assist-bg-${task}-provider`);
if (!providerSelect) {continue;}
updateModelOptions(`assist-bg-${task}-model`, providerSelect.value);
}
bindAssistantDraftTracking(el);
const statusEl = el.querySelector('#ops-assistant-status');
const buttons = el.querySelectorAll('.assistant-action-btn');
buttons.forEach((button) => {
button.addEventListener('click', async () => {
const rawAction = button.getAttribute('data-action');
const action = rawAction === 'save-council' ? 'save-councils' : rawAction;
let patches = null;
if (action === 'toggle-announce') {
patches = { 'automation.delivery_mode': announce ? 'shared_session' : 'announce' };
_assistantManualOverrides.add('automation.delivery_mode');
} else if (action === 'toggle-daily-briefing') {
patches = { 'automation.daily_briefing.enabled': !dailyBriefing };
_assistantManualOverrides.add('automation.daily_briefing.enabled');
} else if (action === 'toggle-memory-daily') {
patches = { 'memory.daily_log.enabled': !memoryDaily };
_assistantManualOverrides.add('memory.daily_log.enabled');
} else if (action === 'toggle-memory-proactive') {
patches = { 'memory.proactive_extract.enabled': !memoryProactive };
_assistantManualOverrides.add('memory.proactive_extract.enabled');
} else if (action === 'toggle-tts') {
patches = { 'tts.enabled': !ttsEnabled };
_assistantManualOverrides.add('tts.enabled');
} else if (action === 'test-daily-briefing') {
await triggerDailyBriefingTest(briefingName, statusEl);
} else if (action === 'playbook-executive') {
_lastPlaybookRollbackPatches = buildRollbackPatchesFromSnapshot(snapshot);
patches = buildPlaybookPatches('executive');
} else if (action === 'playbook-operator') {
_lastPlaybookRollbackPatches = buildRollbackPatchesFromSnapshot(snapshot);
patches = buildPlaybookPatches('operator');
} else if (action === 'playbook-focus') {
_lastPlaybookRollbackPatches = buildRollbackPatchesFromSnapshot(snapshot);
patches = buildPlaybookPatches('focus');
} else if (action === 'playbook-undo') {
if (!_lastPlaybookRollbackPatches) {
if (statusEl) {
statusEl.textContent = 'No playbook changes to undo.';
statusEl.className = 'text-sm text-zinc-500';
}
return;
}
patches = _lastPlaybookRollbackPatches;
_lastPlaybookRollbackPatches = null;
} else if (action === 'save-briefing-output') {
const channel = (el.querySelector('#assist-brief-channel')?.value ?? '').trim();
const peer = (el.querySelector('#assist-brief-peer')?.value ?? '').trim();
if (!channel || !peer) {
if (statusEl) {
statusEl.textContent = 'Briefing output channel and peer are required.';
statusEl.className = 'text-sm text-red-500';
}
return;
}
patches = {
'automation.daily_briefing.output.channel': channel,
'automation.daily_briefing.output.peer': peer,
'automation.daily_briefing.enabled': true,
};
_assistantManualOverrides.add('automation.daily_briefing.enabled');
} else if (action === 'save-model-defaults') {
const tasks = MODEL_DEFAULT_TASK_KEYS;
patches = {
'agents.primary_tier': (el.querySelector('#assist-primary-tier')?.value ?? 'default'),
};
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();
if (!provider || (!model && tier !== 'default')) {
continue;
}
if (provider) {
patches[`models.${tier}.provider`] = provider;
}
if (model) {
patches[`models.${tier}.model`] = model;
}
}
for (const task of tasks) {
const delegationTier = el.querySelector(`#assist-delegation-${task}`)?.value ?? 'fast';
const enabled = Boolean(el.querySelector(`#assist-bg-${task}-enabled`)?.checked);
const provider = (el.querySelector(`#assist-bg-${task}-provider`)?.value ?? '').trim();
const model = (el.querySelector(`#assist-bg-${task}-model`)?.value ?? '').trim();
const fallbackTier = el.querySelector(`#assist-bg-${task}-fallback`)?.value ?? 'fast';
patches[`agents.delegation.${task}`] = delegationTier;
patches[`agents.background_models.${task}.enabled`] = enabled;
if (provider) {
patches[`agents.background_models.${task}.provider`] = provider;
}
if (model) {
patches[`agents.background_models.${task}.model`] = model;
}
patches[`agents.background_models.${task}.fallback_tier`] = fallbackTier;
}
} else if (action === 'save-councils') {
const enabled = Boolean(el.querySelector('#assist-councils-enabled')?.checked);
const dTier = el.querySelector('#assist-council-d-tier')?.value ?? 'complex';
const pTier = el.querySelector('#assist-council-p-tier')?.value ?? 'complex';
const metaTier = el.querySelector('#assist-council-meta-tier')?.value ?? 'complex';
const dArbiter = (el.querySelector('#assist-council-d-arbiter')?.value ?? '').trim();
const dFreethinker = (el.querySelector('#assist-council-d-freethinker')?.value ?? '').trim();
const pArbiter = (el.querySelector('#assist-council-p-arbiter')?.value ?? '').trim();
const pFreethinker = (el.querySelector('#assist-council-p-freethinker')?.value ?? '').trim();
const metaArbiter = (el.querySelector('#assist-council-meta-arbiter')?.value ?? '').trim();
const scaffoldPath = (el.querySelector('#assist-council-scaffold')?.value ?? '').trim();
const maxRoundsRaw = Number(el.querySelector('#assist-council-max-rounds')?.value ?? 2);
const maxRounds = Number.isFinite(maxRoundsRaw) ? Math.max(1, Math.min(6, Math.floor(maxRoundsRaw))) : 2;
if (!dArbiter || !dFreethinker || !pArbiter || !pFreethinker || !metaArbiter) {
if (statusEl) {
statusEl.textContent = 'All council agent names are required.';
statusEl.className = 'text-sm text-red-500';
}
return;
}
patches = {
'councils.enabled': enabled,
'councils.defaults.max_rounds': maxRounds,
'councils.groups.D.model_tier': dTier,
'councils.groups.P.model_tier': pTier,
'councils.meta_model_tier': metaTier,
'councils.groups.D.arbiter_agent': dArbiter,
'councils.groups.D.freethinker_agent': dFreethinker,
'councils.groups.P.arbiter_agent': pArbiter,
'councils.groups.P.freethinker_agent': pFreethinker,
'councils.meta_arbiter_agent': metaArbiter,
'councils.scaffold_path': scaffoldPath,
};
} else if (action === 'run-council') {
const councilTask = (el.querySelector('#assist-council-task')?.value ?? '').trim();
const councilStatusEl = el.querySelector('#assist-council-status');
const ok = await triggerCouncilRun(councilTask, councilStatusEl);
if (ok && _lastAssistantConfig) {
updateAssistantHealth(_lastAssistantConfig);
}
return;
}
if (!patches) {return;}
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) {
if (refreshed.config) {
_lastAssistantConfig = refreshed.config;
}
updateServices(refreshed.services);
updateSessionAnalytics(refreshed.sessionAnalytics);
updateContextHealth(refreshed.contextUsage);
// Only re-render assistant controls from a confirmed config snapshot.
if (refreshed.config) {
updateAssistantHealth(_lastAssistantConfig);
} else if (_lastAssistantConfig) {
updateAssistantHealth(_lastAssistantConfig);
}
}
});
});
}
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;}
const channels = channelsData?.channels ?? [];
if (channels.length === 0) {
el.innerHTML = 'No channels registered
';
return;
}
el.innerHTML = channels.map(ch =>
`
${escapeHtml(ch.name)}
`,
).join('');
}
function updateServices(servicesData) {
const el = document.getElementById('ops-services');
if (!el) {return;}
const services = servicesData?.services ?? [];
_lastServices = services;
if (services.length === 0) {
el.innerHTML = 'No services configured
';
renderServiceConfigModal();
return;
}
el.innerHTML = services.map(svc => {
const typeIcon = svc.type === 'channel' ? '📡' : svc.type === 'automation' ? '⚙️' : '🔧';
const borderColor = svc.status === 'connected'
? 'border-l-green-500'
: svc.status === 'configured'
? 'border-l-blue-500'
: svc.status === 'error'
? 'border-l-red-500'
: 'border-l-zinc-600 opacity-60';
const statusColor = svc.status === 'connected'
? 'text-green-500'
: svc.status === 'configured'
? 'text-blue-500'
: svc.status === 'error'
? 'text-red-500'
: 'text-zinc-500';
const itemCount = svc.itemCount ? ` (${svc.itemCount})` : '';
return ``;
}).join('');
el.querySelectorAll('.service-card').forEach((card) => {
card.addEventListener('click', () => {
const serviceName = card.getAttribute('data-service-name');
if (!serviceName) {return;}
_serviceConfigState.open = true;
_serviceConfigState.serviceName = serviceName;
_serviceConfigState.status = null;
_serviceConfigState.tone = 'neutral';
renderServiceConfigModal();
});
});
renderServiceConfigModal();
}
function getConfigValue(path, fallbackValue) {
const value = getByPath(_lastAssistantConfig, path);
return value === undefined ? fallbackValue : value;
}
function renderServiceConfigModal() {
const root = document.getElementById('ops-service-config-modal-root');
if (!root) {return;}
if (!_serviceConfigState.open || !_serviceConfigState.serviceName) {
root.innerHTML = '';
return;
}
const service = _lastServices.find((svc) => svc.name === _serviceConfigState.serviceName);
if (!service) {
_serviceConfigState.open = false;
root.innerHTML = '';
return;
}
const quickTogglePath = SERVICE_TOGGLE_PATCH_PATHS[service.name];
const hasQuickToggle = Boolean(quickTogglePath);
const quickToggleValue = hasQuickToggle ? Boolean(getConfigValue(quickTogglePath, false)) : false;
const heartbeatSection = service.name === 'heartbeat'
? `
`
: '';
const toneClass = _serviceConfigState.tone === 'success'
? 'text-green-500'
: _serviceConfigState.tone === 'error'
? 'text-red-500'
: 'text-zinc-500';
root.innerHTML = `
Configure ${escapeHtml(service.name)}
${escapeHtml(service.description ?? '')}
Quick Settings
${hasQuickToggle ? `
` : '
No quick toggle available for this service.
'}
${heartbeatSection}
Advanced Patch (optional JSON)
${escapeHtml(_serviceConfigState.status ?? '')}
`;
const closeModal = () => {
_serviceConfigState.open = false;
renderServiceConfigModal();
};
root.querySelector('#svc-config-close')?.addEventListener('click', closeModal);
root.querySelector('#svc-config-cancel')?.addEventListener('click', closeModal);
root.querySelector('#svc-config-overlay')?.addEventListener('click', closeModal);
root.querySelector('#svc-config-save')?.addEventListener('click', async () => {
if (!_dashboardClient || !_serviceConfigState.serviceName) {return;}
const patches = {};
if (quickTogglePath) {
patches[quickTogglePath] = Boolean(root.querySelector('#svc-quick-enabled')?.checked);
}
if (_serviceConfigState.serviceName === 'heartbeat') {
patches['automation.heartbeat.interval'] = (root.querySelector('#svc-heartbeat-interval')?.value ?? '5m').trim();
patches['automation.heartbeat.notify_cooldown'] = (root.querySelector('#svc-heartbeat-notify-cooldown')?.value ?? '30m').trim();
patches['automation.heartbeat.failure_threshold'] = Number(root.querySelector('#svc-heartbeat-failure-threshold')?.value ?? 2);
patches['automation.heartbeat.disk_threshold_mb'] = Number(root.querySelector('#svc-heartbeat-disk-threshold')?.value ?? 100);
patches['automation.heartbeat.checks'] = HEARTBEAT_CHECK_KEYS.filter(
(check) => Boolean(root.querySelector(`[data-heartbeat-check="${check}"]`)?.checked),
);
}
const advancedRaw = (root.querySelector('#svc-advanced-patch')?.value ?? '').trim();
_serviceConfigState.advancedPatch = advancedRaw;
if (advancedRaw.length > 0) {
try {
const parsed = JSON.parse(advancedRaw);
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('Advanced patch must be a JSON object');
}
Object.assign(patches, parsed);
} catch (error) {
_serviceConfigState.status = error instanceof Error ? error.message : String(error);
_serviceConfigState.tone = 'error';
renderServiceConfigModal();
return;
}
}
if (Object.keys(patches).length === 0) {
_serviceConfigState.status = 'No changes to save.';
_serviceConfigState.tone = 'neutral';
renderServiceConfigModal();
return;
}
try {
const result = await _dashboardClient.call('config.patch', { patches });
const applied = Array.isArray(result?.applied) ? result.applied : [];
const rejected = Array.isArray(result?.rejected) ? result.rejected : [];
if (applied.length === 0) {
_serviceConfigState.status = rejected.length > 0
? `No changes applied. Rejected: ${rejected.join(', ')}`
: 'No changes were applied.';
_serviceConfigState.tone = 'error';
renderServiceConfigModal();
return;
}
_serviceConfigState.status = `Saved ${applied.length} setting(s).${rejected.length > 0 ? ` Rejected: ${rejected.join(', ')}` : ''}`;
_serviceConfigState.tone = rejected.length > 0 ? 'error' : 'success';
const refreshed = await fetchSlow(_dashboardClient);
if (refreshed) {
updateServices(refreshed.services);
updateSessionAnalytics(refreshed.sessionAnalytics);
updateContextHealth(refreshed.contextUsage);
if (refreshed.config) {
_lastAssistantConfig = refreshed.config;
updateAssistantHealth(_lastAssistantConfig);
}
}
renderServiceConfigModal();
} catch (error) {
_serviceConfigState.status = `Save failed: ${error instanceof Error ? error.message : String(error)}`;
_serviceConfigState.tone = 'error';
renderServiceConfigModal();
}
});
}
// ── Data fetching ───────────────────────────────────────────────
async function fetchFast(client) {
try {
const [metrics, eventsData, requestsData] = await Promise.all([
client.call('system.metrics'),
client.call('system.events', { limit: 50 }),
client.call('system.activeRequests'),
]);
return { metrics, eventsData, requestsData };
} catch {
return null;
}
}
async function fetchSlow(client) {
const [health, services, sessionAnalytics, contextUsage, config, modelCatalog] = await Promise.allSettled([
client.call('system.health'),
client.call('system.services'),
client.call('system.sessionAnalytics', { days: 14, topLimit: 5 }),
client.call('system.contextUsage'),
client.call('config.get'),
client.call('system.modelCatalog'),
]);
const unwrap = (result) => (result.status === 'fulfilled' ? result.value : null);
const configValue = unwrap(config);
const modelCatalogValue = unwrap(modelCatalog);
if (configValue && typeof configValue === 'object') {
configValue.__modelCatalog = Array.isArray(modelCatalogValue?.providers) ? modelCatalogValue.providers : [];
}
return {
health: unwrap(health),
services: unwrap(services),
sessionAnalytics: unwrap(sessionAnalytics),
contextUsage: unwrap(contextUsage),
config: configValue,
};
}
// ── Main load function ──────────────────────────────────────────
let _lastHealth = null;
let _lastMetrics = null;
async function loadDashboard(el, client) {
_dashboardClient = client;
renderSkeleton(el);
// Fetch everything initially
const [fast, slow] = await Promise.all([
fetchFast(client),
fetchSlow(client),
]);
_lastHealth = slow?.health ?? null;
_lastMetrics = fast?.metrics ?? null;
if (fast) {
updateCounters(fast.metrics, _lastHealth);
updateModelTable(fast.metrics);
updateEvents(fast.eventsData);
updateActiveRequests(fast.requestsData);
}
if (slow?.config) {
_lastAssistantConfig = slow.config;
}
if (slow?.services) {
updateServices(slow.services);
}
if (slow?.sessionAnalytics) {
updateSessionAnalytics(slow.sessionAnalytics);
}
if (slow?.contextUsage) {
updateContextHealth(slow.contextUsage);
}
if (slow?.config) {
updateAssistantHealth(_lastAssistantConfig);
}
// Fast refresh: 3 seconds for metrics, events, requests
_fastTimer = setInterval(async () => {
const data = await fetchFast(client);
if (data) {
_lastMetrics = data.metrics;
updateCounters(data.metrics, _lastHealth);
updateModelTable(data.metrics);
updateEvents(data.eventsData);
updateActiveRequests(data.requestsData);
}
}, 3000);
// Slow refresh: 10 seconds for health, services
_slowTimer = setInterval(async () => {
const data = await fetchSlow(client);
if (data.health) {
_lastHealth = data.health;
updateCounters(_lastMetrics, data.health);
}
if (data.config) {
_lastAssistantConfig = data.config;
}
if (data.services) {
updateServices(data.services);
}
if (data.sessionAnalytics) {
updateSessionAnalytics(data.sessionAnalytics);
}
if (data.contextUsage) {
updateContextHealth(data.contextUsage);
}
if (data.config) {
updateAssistantHealth(_lastAssistantConfig);
}
}, 10000);
}
export const DashboardPage = {
async render(el, client) {
await loadDashboard(el, client);
},
teardown() {
if (_fastTimer) {
clearInterval(_fastTimer);
_fastTimer = null;
}
if (_slowTimer) {
clearInterval(_slowTimer);
_slowTimer = null;
}
_lastHealth = null;
_lastMetrics = null;
_dashboardClient = null;
_lastPlaybookRollbackPatches = null;
_lastBriefingTestAt = null;
_assistantSaveState = null;
_lastAssistantConfig = null;
_assistantManualOverrides = new Set();
_assistantModelDefaultsDraft = null;
_assistantDraftState = new Map();
_assistantDraftTouchedAt = 0;
},
};