feat(v9): Phase 8 — Cockpit zeigt Lernen & Aktivität

- hermes_control.py: learned_profile() liest USER.md (Curator-Nutzerprofil,
  § -getrennt) + MEMORY.md; insights() zieht Sessions/Tokens/Tool-Calls +
  Top-Tools aus `hermes insights` (Regex, best effort).
- routers/hermes.py: GET /api/hermes/{learned,insights}.
- HermesPanel-Cockpit: Kachel "Aktivität" (Kennzahlen + Top-Tool-Balken) und
  "Was Hermes über dich gelernt hat" (USER.md-Fakten) — beantwortet sichtbar,
  dass der Agent automatisch mitlernt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-24 17:06:51 +02:00
parent 7e8ed585a9
commit d4aa7eb8cf
4 changed files with 163 additions and 40 deletions
+55 -1
View File
@@ -10,6 +10,8 @@
let agent = $state<any>(null)
let cronJobs = $state<any[]>([])
let skills = $state<any[]>([])
let learned = $state<any>(null)
let insights = $state<any>(null)
let pubkey = $state('')
// ---- Status laden ----
@@ -29,14 +31,18 @@
async function loadCockpit() {
cockpitLoading = true
try {
const [a, c, s] = await Promise.all([
const [a, c, s, l, i] = await Promise.all([
api('/api/hermes/agent'),
api('/api/hermes/cron'),
api('/api/hermes/skills'),
api('/api/hermes/learned'),
api('/api/hermes/insights'),
])
agent = a
cronJobs = c.jobs || []
skills = s.skills || []
learned = l
insights = i
} catch { agent = null }
cockpitLoading = false
}
@@ -118,6 +124,54 @@
</div>
</div>
<!-- Aktivität (Phase 8) -->
{#if insights && (insights.sessions || insights.total_tokens)}
<div class="tile" style="margin-bottom:10px">
<div style="margin-bottom:8px"><b>Aktivität</b> <span class="card-sub" style="font-size:11px">(letzte 30 Tage)</span></div>
<div style="display:flex;gap:18px;flex-wrap:wrap;font-size:12.5px;margin-bottom:8px">
{#if insights.sessions}<span><b style="color:var(--accent)">{insights.sessions}</b> <span class="card-sub">Sessions</span></span>{/if}
{#if insights.messages}<span><b style="color:var(--accent)">{insights.messages}</b> <span class="card-sub">Nachrichten</span></span>{/if}
{#if insights.tool_calls}<span><b style="color:var(--accent)">{insights.tool_calls}</b> <span class="card-sub">Tool-Calls</span></span>{/if}
{#if insights.total_tokens}<span><b style="color:var(--accent)">{insights.total_tokens}</b> <span class="card-sub">Tokens</span></span>{/if}
{#if insights.active_time}<span><b style="color:var(--accent)">{insights.active_time}</b> <span class="card-sub">aktiv</span></span>{/if}
</div>
{#if insights.top_tools?.length}
<div class="card-sub" style="font-size:11px;margin-bottom:4px">Meistgenutzte Tools</div>
<div style="display:flex;flex-direction:column;gap:3px">
{#each insights.top_tools.slice(0, 6) as t}
<div style="display:flex;align-items:center;gap:8px;font-size:12px">
<span style="min-width:230px">{t.name}</span>
<span style="flex:1;height:6px;background:rgba(255,255,255,.05);border-radius:3px;overflow:hidden">
<span style="display:block;height:100%;width:{t.pct}%;background:var(--accent)"></span>
</span>
<span class="card-sub" style="font-size:11px;min-width:64px;text-align:right">{t.calls}× · {t.pct}%</span>
</div>
{/each}
</div>
{/if}
</div>
{/if}
<!-- Was Hermes gelernt hat (Phase 8) -->
{#if learned?.user_facts?.length || learned?.memory}
<div class="tile" style="margin-bottom:10px">
<div style="margin-bottom:6px"><b>Was Hermes über dich gelernt hat</b>
<span class="card-sub" style="font-size:11px">(automatisches Profil)</span></div>
{#if learned.user_facts?.length}
<ul style="margin:0 0 6px;padding-left:18px;display:flex;flex-direction:column;gap:3px">
{#each learned.user_facts as f}
<li style="font-size:12.5px;line-height:1.45">{f}</li>
{/each}
</ul>
{/if}
{#if learned.memory}
<div class="card-sub" style="font-size:11.5px;border-top:1px solid rgba(255,255,255,.06);padding-top:6px;margin-top:2px">
{learned.memory}
</div>
{/if}
</div>
{/if}
<!-- Cron-Scheduler -->
<div class="tile" style="margin-bottom:10px">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">