feat(dashboard): add assistant activation checklist and guided controls

This commit is contained in:
William Valentin
2026-02-18 12:15:42 -08:00
parent 182827d612
commit 68ce0f77c4
5 changed files with 131 additions and 3 deletions
+53 -2
View File
@@ -9,6 +9,7 @@ let _fastTimer = null;
let _slowTimer = null;
let _dashboardClient = null;
let _lastPlaybookRollbackPatches = null;
let _lastBriefingTestAt = null;
function formatUptime(seconds) {
const d = Math.floor(seconds / 86400);
@@ -537,18 +538,21 @@ async function triggerDailyBriefingTest(jobName, statusEl) {
statusEl.textContent = output;
statusEl.className = 'text-sm text-success';
}
return;
_lastBriefingTestAt = Date.now();
return true;
}
if (statusEl) {
statusEl.textContent = result?.error ?? 'Failed to trigger briefing.';
statusEl.className = 'text-sm text-error';
}
return false;
} catch (error) {
if (statusEl) {
statusEl.textContent = `Trigger error: ${error instanceof Error ? error.message : String(error)}`;
statusEl.className = 'text-sm text-error';
}
return false;
}
}
@@ -580,6 +584,12 @@ function updateAssistantHealth(configData) {
? `${briefingOutput.channel}/${briefingOutput.peer}`
: 'not configured';
const briefingReady = dailyBriefing && Boolean(briefingOutput?.channel && briefingOutput?.peer);
const playbookLikeReady = announce || (memoryDaily && memoryProactive);
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) => `
<div class="assistant-chip">
@@ -635,6 +645,32 @@ function updateAssistantHealth(configData) {
</div>
<div class="text-sm text-muted">Executive: announce + voice + aggressive interrupt. Operator: announce + memory-first + steer backlog. Focus: reactive, quieter mode.</div>
</div>
<div class="assistant-setup">
<div class="assistant-preview-title">Assistant Activation Checklist</div>
<ul class="assistant-checklist">
${checklistRows.map((row) => `
<li class="${row.done ? 'done' : ''}">
<span class="assistant-check">${row.done ? '✓' : '○'}</span>
<span>${escapeHtml(row.label)}</span>
</li>
`).join('')}
</ul>
<div class="assistant-setup-grid">
<label class="assistant-field">
<span>Briefing output channel</span>
<input id="assist-brief-channel" type="text" value="${escapeHtml(briefingOutput?.channel ?? '')}" placeholder="telegram" />
</label>
<label class="assistant-field">
<span>Briefing output peer/chat id</span>
<input id="assist-brief-peer" type="text" value="${escapeHtml(briefingOutput?.peer ?? '')}" placeholder="123456789" />
</label>
</div>
<div class="assistant-actions">
<button class="btn btn-secondary assistant-action-btn" data-action="save-briefing-output">
Save Briefing Output
</button>
</div>
</div>
<div class="assistant-preview">
<div class="assistant-preview-header">
<div class="assistant-preview-title">Morning Brief Preview</div>
@@ -673,7 +709,6 @@ function updateAssistantHealth(configData) {
patches = { 'tts.enabled': !ttsEnabled };
} else if (action === 'test-daily-briefing') {
await triggerDailyBriefingTest(briefingName, statusEl);
return;
} else if (action === 'playbook-executive') {
_lastPlaybookRollbackPatches = buildRollbackPatchesFromSnapshot(snapshot);
patches = buildPlaybookPatches('executive');
@@ -693,6 +728,21 @@ function updateAssistantHealth(configData) {
}
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-error';
}
return;
}
patches = {
'automation.daily_briefing.output.channel': channel,
'automation.daily_briefing.output.peer': peer,
'automation.daily_briefing.enabled': true,
};
}
if (!patches) {return;}
await applyAssistantPatch(patches, statusEl);
@@ -864,5 +914,6 @@ export const DashboardPage = {
_lastMetrics = null;
_dashboardClient = null;
_lastPlaybookRollbackPatches = null;
_lastBriefingTestAt = null;
},
};
+18
View File
@@ -127,6 +127,8 @@ async function loadSettings() {
const proactiveMinToolCalls = Number(memory.proactive_extract?.min_tool_calls ?? 1);
const ttsEnabled = Boolean(tts.enabled);
const ttsChannelText = Array.isArray(tts.enabled_channels) ? tts.enabled_channels.join(', ') : '';
const briefingOutputChannel = automation.daily_briefing?.output?.channel ?? '';
const briefingOutputPeer = automation.daily_briefing?.output?.peer ?? '';
// Build config view (redacted JSON)
const configJson = JSON.stringify(config, null, 2);
@@ -171,6 +173,14 @@ async function loadSettings() {
<span>TTS channels (comma-separated, blank = all)</span>
<input id="assist-tts-channels" type="text" value="${escapeHtml(ttsChannelText)}" placeholder="telegram,discord,whatsapp" />
</label>
<label class="assistant-field">
<span>Briefing output channel</span>
<input id="assist-briefing-channel" type="text" value="${escapeHtml(briefingOutputChannel)}" placeholder="telegram" />
</label>
<label class="assistant-field">
<span>Briefing output peer/chat id</span>
<input id="assist-briefing-peer" type="text" value="${escapeHtml(briefingOutputPeer)}" placeholder="123456789" />
</label>
</div>
<div class="assistant-actions">
<button id="assistant-mode-save" class="btn btn-primary">Save Assistant Mode</button>
@@ -288,6 +298,8 @@ async function saveAssistantMode() {
const minToolsRaw = Number.parseInt(_el.querySelector('#assist-memory-min-tools')?.value ?? '1', 10);
const minTools = Number.isFinite(minToolsRaw) ? Math.min(50, Math.max(0, minToolsRaw)) : 1;
const ttsChannelsRaw = _el.querySelector('#assist-tts-channels')?.value ?? '';
const briefingChannel = (_el.querySelector('#assist-briefing-channel')?.value ?? '').trim();
const briefingPeer = (_el.querySelector('#assist-briefing-peer')?.value ?? '').trim();
const ttsChannels = ttsChannelsRaw
.split(',')
.map((value) => value.trim())
@@ -302,6 +314,12 @@ async function saveAssistantMode() {
'tts.enabled': ttsEnabled,
'tts.enabled_channels': ttsChannels,
};
if (briefingChannel) {
patches['automation.daily_briefing.output.channel'] = briefingChannel;
}
if (briefingPeer) {
patches['automation.daily_briefing.output.peer'] = briefingPeer;
}
try {
const result = await _client.call('config.patch', { patches });
+40
View File
@@ -1594,6 +1594,46 @@ tr:hover td {
margin: 10px 0;
}
.assistant-setup {
margin-top: 12px;
padding: 12px;
border: 1px solid var(--border);
border-radius: var(--radius);
background-color: var(--bg-secondary);
}
.assistant-checklist {
list-style: none;
margin: 10px 0;
padding: 0;
display: grid;
gap: 6px;
}
.assistant-checklist li {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-secondary);
font-size: var(--font-size-sm);
}
.assistant-checklist li.done {
color: var(--text-primary);
}
.assistant-check {
width: 18px;
text-align: center;
font-weight: 700;
}
.assistant-setup-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 8px;
}
.assistant-preview-header {
display: flex;
justify-content: space-between;