feat(dashboard): add assistant activation checklist and guided controls
This commit is contained in:
@@ -487,7 +487,7 @@ Flynn includes a built-in web control dashboard served by the WebSocket gateway.
|
||||
|
||||
| Page | Description |
|
||||
|------|-------------|
|
||||
| **Dashboard** | System health cards, channel status, usage stats, assistant-health quick actions, playbook presets with rollback, and daily briefing preview/test-send. Auto-refreshes every 10s |
|
||||
| **Dashboard** | System health cards, channel status, usage stats, assistant-health quick actions, playbook presets with rollback, activation checklist, and daily briefing preview/test-send. Auto-refreshes every 10s |
|
||||
| **Chat** | Session selector, streaming tool events, markdown rendering with syntax highlighting |
|
||||
| **Sessions** | Browse all sessions, view message history, delete sessions |
|
||||
| **Usage** | Token usage summary cards, per-session breakdown table, auto-refresh |
|
||||
|
||||
@@ -5506,6 +5506,25 @@
|
||||
"docs/plans/state.json"
|
||||
],
|
||||
"test_status": "pnpm typecheck passing"
|
||||
},
|
||||
"assistant-activation-checklist-and-setup-defaults": {
|
||||
"status": "completed",
|
||||
"date": "2026-02-18",
|
||||
"updated": "2026-02-18",
|
||||
"summary": "Completed assistant productization pass: Dashboard now includes a 3-step activation checklist with briefing output save controls, playbook/test-send integration, and persisted assistant status cues; runtime config.patch now supports daily briefing output/schedule/prompt/tier updates; setup/operator-pack defaults now enable announce delivery plus memory daily/proactive extraction cadence for assistant-first behavior out of the box.",
|
||||
"files_modified": [
|
||||
"src/gateway/ui/pages/dashboard.js",
|
||||
"src/gateway/ui/style.css",
|
||||
"src/gateway/ui/pages/settings.js",
|
||||
"src/gateway/handlers/config.ts",
|
||||
"src/gateway/handlers/handlers.test.ts",
|
||||
"src/cli/setup/config.ts",
|
||||
"src/cli/setup/config.test.ts",
|
||||
"src/cli/setup/automation.test.ts",
|
||||
"README.md",
|
||||
"docs/plans/state.json"
|
||||
],
|
||||
"test_status": "pnpm test:run src/gateway/handlers/handlers.test.ts src/cli/setup/config.test.ts src/cli/setup/automation.test.ts + pnpm typecheck passing"
|
||||
}
|
||||
},
|
||||
"overall_progress": {
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user