// Agent Insights — live AI-agent usage analytics. // Answers: who I chat with most, who spends the most time, who's best // suited for what, and which models I use per agent. All derived from // real data via /api/agent-insights. function fmtDuration(sec) { sec = Math.max(0, Math.round(sec || 0)); const h = Math.floor(sec / 3600); const m = Math.floor((sec % 3600) / 60); const s = sec % 60; if (h > 0) return `${h}h ${m}m`; if (m > 0) return `${m}m ${s}s`; return `${s}s`; } function agentColor(agent) { const palette = { opencode: '#6c5ce7', hermes: '#00b894', gemini: '#0984e3', jarvis: '#fd79a8', kilocode: '#e17055', codex: '#fdcb6e', system: '#636e72', test: '#b2bec3', }; return palette[agent] || '#74b9ff'; } let _charts = {}; function destroyCharts() { Object.values(_charts).forEach(c => { try { c.destroy(); } catch (e) {} }); _charts = {}; } async function renderAgentInsights() { const content = document.getElementById('pageContent'); content.innerHTML = `
Live analytics on how you actually use your AI agents
Generated ${formatDate(data.generated_at)} · data: chat-history, cost-history, agent-time, agent-registry, router-keywords
`; // Charts const chats = (data.most_chatted || []).slice(0, 8); const times = (data.most_time || []).slice(0, 8); _charts.chats = new Chart(document.getElementById('chatsChart'), { type: 'bar', data: { labels: chats.map(c => c.display_name || c.agent), datasets: [{ label: 'Your chat turns', data: chats.map(c => c.user_turns), backgroundColor: chats.map(c => agentColor(c.agent)), borderRadius: 6, }], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { precision: 0 } } } }, }); _charts.time = new Chart(document.getElementById('timeChart'), { type: 'bar', data: { labels: times.map(c => c.display_name || c.agent), datasets: [{ label: 'Time spent', data: times.map(c => Math.round(c.time_seconds / 60)), backgroundColor: times.map(c => agentColor(c.agent)), borderRadius: 6, }], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { callbacks: { label: (ctx) => `${ctx.parsed.y} min` } } }, scales: { y: { beginAtZero: true, title: { display: true, text: 'minutes' } } } }, }); // Suite cards const suiteGrid = document.getElementById('suiteGrid'); const suiteAgents = Object.values(pa) .filter(a => (a.suite && (a.suite.description || a.suite.keywords?.length))) .sort((a, b) => (b.user_turns + b.time_seconds / 60) - (a.user_turns + a.time_seconds / 60)); if (suiteAgents.length === 0) { suiteGrid.innerHTML = '| Agent | Top models (by uses) | Tokens | Cost |
|---|---|---|---|
| ${escapeHtml(a.display_name || a.agent)} | ${a.top_models.slice(0, 4).map(([m, c]) => `${escapeHtml(m)} ×${c}`).join(' ')} | ${(a.tokens || 0).toLocaleString()} | $${(a.cost || 0).toFixed(4)} |
| Agent | Chat turns | Msgs | Time | Sessions | Top model | First seen | Last seen |
|---|---|---|---|---|---|---|---|
| ${escapeHtml(a.display_name || a.agent)} | ${a.user_turns} | ${a.chat_messages} | ${fmtDuration(a.time_seconds)} | ${a.sessions} | ${a.top_models && a.top_models.length ? escapeHtml(a.top_models[0][0]) : '—'} | ${a.first_seen ? formatDate(a.first_seen) : '—'} | ${a.last_seen ? formatDate(a.last_seen) : '—'} |