{% extends "base.html" %}
{% block extra_head %}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<link rel="stylesheet" href="/static/css/hermes.css" />
<link rel="stylesheet" href="/static/css/herman-dashboard.css?v=13" />
<link rel="stylesheet" href="/static/css/agents-mesh.css?v=8" />
<link rel="stylesheet" href="/static/css/agent-characters.css?v=3" />
<script src="/static/js/agent-avatars.js?v=3"></script>
{% endblock %}
{% block content %}
<div class="hm-neo" x-data="agentsSoulHub()" x-init="init()">
  <header class="hm-neo-header page-header">
    <h1>Agents · Soul Registry</h1>
    <p class="hm-sub">Animated team · duidelijke rollen · soul.md · live activiteit</p>
  </header>

  <div id="viz-toolbar" class="viz-toolbar"></div>
  <div class="hm-chart-panel page-viz-panel viz-mode-chart agents-viz-compact" style="margin-bottom:1rem">
    <div class="hm-chart-head"><h4>Agent activiteit (events)</h4><small>5 visualisatie-modi</small></div>
    <div class="page-viz-body">
      <div class="hm-chart-wrap page-viz-canvas"><canvas id="chart-agents-viz"></canvas></div>
      <div id="viz-agents-alt" class="viz-alt"></div>
    </div>
  </div>

  <div class="vtabs-layout" style="--vt-color:#ffd700" x-ref="vtabsWrap">
    <nav class="vtabs-nav" x-ref="vtabsNav" :style="'--vt-indicator-top:'+tabIndicatorTop+'px;--vt-indicator-h:'+tabIndicatorH+'px'">
      <div class="vtabs-nav-header"><strong>Agents</strong></div>
      <button class="vtab-btn" :class="{active: tab==='souls'}" @click="setTab('souls', $el)">
        <span class="vtab-icon">🧬</span><span>Souls</span>
      </button>
      <button class="vtab-btn" :class="{active: tab==='mesh'}" @click="setTab('mesh', $el)">
        <span class="vtab-icon">🕸️</span><span>Netwerk</span>
      </button>
      <button class="vtab-btn" :class="{active: tab==='terminals'}" @click="setTab('terminals', $el)" data-tab="terminals">
        <span class="vtab-icon">🖥️</span><span>Terminals</span>
      </button>
      <button class="vtab-btn" :class="{active: tab==='approvals'}" @click="setTab('approvals', $el)">
        <span class="vtab-icon">✓</span><span>Goedkeuringen</span>
        <span class="vtab-badge" x-show="pendingCount" x-text="pendingCount"></span>
      </button>
    </nav>

    <div class="vtabs-content">
      <section x-show="tab==='souls'" x-cloak>
        <div class="hm-neo-kpi-row" style="grid-template-columns:repeat(4,1fr)">
          <div class="hm-neo-kpi"><div class="hm-neo-kpi-top"><div class="hm-neo-ring" style="--ring-color:#00e5ff;--ring-pct:80%"><div class="hm-neo-ring-inner">🤖</div></div><div><div class="hm-neo-kpi-label">Agents</div><div class="hm-neo-kpi-sub">geregistreerd</div></div></div><div class="hm-neo-kpi-value" x-text="souls.length">0</div><div class="hm-eq"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div></div>
          <div class="hm-neo-kpi"><div class="hm-neo-kpi-top"><div class="hm-neo-ring" style="--ring-color:#b8ff3c;--ring-pct:70%"><div class="hm-neo-ring-inner">⚡</div></div><div><div class="hm-neo-kpi-label">Events</div><div class="hm-neo-kpi-sub">totaal gelogd</div></div></div><div class="hm-neo-kpi-value" x-text="totalEvents">0</div><div class="hm-eq"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div></div>
          <div class="hm-neo-kpi"><div class="hm-neo-kpi-top"><div class="hm-neo-ring" style="--ring-color:#ff9f43;--ring-pct:50%"><div class="hm-neo-ring-inner">✓</div></div><div><div class="hm-neo-kpi-label">Actief</div><div class="hm-neo-kpi-sub">agents online</div></div></div><div class="hm-neo-kpi-value" x-text="activeCount">0</div><div class="hm-eq"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div></div>
          <div class="hm-neo-kpi"><div class="hm-neo-kpi-top"><div class="hm-neo-ring" style="--ring-color:#ffd700;--ring-pct:90%"><div class="hm-neo-ring-inner">👑</div></div><div><div class="hm-neo-kpi-label">Herman</div><div class="hm-neo-kpi-sub">Co-CEO · takenverdeler</div></div></div><div class="hm-neo-kpi-value" style="font-size:1.1rem">Co-CEO</div><div class="hm-eq"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div></div>
        </div>

        <div class="agents-neo-grid">
          <template x-for="s in souls" :key="s.agent_key">
            <article class="agent-soul-card" :class="{ selected: selected?.agent_key === s.agent_key }" @click="select(s.agent_key)">
              <div class="agent-char-wrap" :class="s.agent_key"
                :data-agent-avatar="s.agent_key"
                :data-agent-color="s.avatar_color || '#1e3a5f'"
                x-init="$nextTick(() => window.AgentAvatars && AgentAvatars.paint($el, s.agent_key, s.avatar_color))"></div>
              <strong x-text="s.display_name"></strong>
              <div class="hm-neo-kpi-sub" x-text="s.role_title"></div>
              <span class="agent-status-pill" :class="statusClass(s)">
                <span class="dot"></span>
                <span x-text="statusLabel(s)"></span>
              </span>
              <p class="agent-current-task" x-text="s.current_task || 'Wacht op taak…'"></p>
              <p class="agent-role-desc" x-text="s.responsibilities || '—'"></p>
              <div class="agent-meta-row">
                <button type="button" class="agent-events-link"
                  @click.stop="goTerminal(s.agent_key)"
                  title="Open live terminal">Terminal →</button>
                <button type="button" class="agent-events-link"
                  @click.stop="openEvents(s.agent_key, s.display_name)"
                  x-text="(s.event_count||0) + ' events'"
                  :title="'Bekijk wat ' + s.display_name + ' heeft gedaan'"></button>
                <span class="agent-tag" x-text="s.agent_key"></span>
              </div>
            </article>
          </template>
        </div>

        <div class="agent-soul-editor" x-show="selected" x-cloak>
          <div class="agent-editor-header">
            <div class="agent-char-wrap large" :class="selected?.agent_key"
              :data-agent-avatar="selected?.agent_key"
              :data-agent-color="selected?.avatar_color || '#1e3a5f'"
              x-effect="selected && $nextTick(() => window.AgentAvatars && AgentAvatars.paint($el, selected.agent_key, selected.avatar_color))"></div>
            <div style="flex:1">
              <h2><span x-text="selected?.display_name"></span> — <span x-text="selected?.role_title"></span></h2>
              <p style="margin:0.25rem 0 0;color:#94a3b8;font-size:0.85rem" x-text="selected?.responsibilities"></p>
            </div>
            <div style="display:flex;gap:0.5rem;flex-shrink:0">
              <button class="btn btn-pulse-green" @click="save()" :disabled="saving">Opslaan</button>
              <a href="/settings?tab=permissions" class="btn btn-pulse-purple">Herman rechten →</a>
            </div>
          </div>
          <label>Rol titel<input class="form-input" x-model="form.role_title"></label>
          <label>Functie & werkzaamheden<textarea class="form-input" rows="3" x-model="form.responsibilities"></textarea></label>
          <label>Soul.md (karakter, tone of voice, regels)<textarea x-model="form.soul_md"></textarea></label>
          <div style="display:flex;justify-content:space-between;align-items:center;margin-top:1rem">
            <h3 style="font-size:0.75rem;text-transform:uppercase;color:#64748b;margin:0">Recente activiteit</h3>
            <button type="button" class="agent-events-link" x-show="selected" @click="openEvents(selected.agent_key, selected.display_name)">Alle events →</button>
          </div>
          <template x-for="ev in (selected?.recent_events||[])" :key="ev.id">
            <button type="button" class="hub-feed-item agent-event-clickable" @click="openEvents(selected.agent_key, selected.display_name, ev.id)">
              <span class="agent-event-time" x-text="formatEventTime(ev.created_at)"></span>
              <span class="agent-event-title" x-text="ev.title||ev.event_type"></span>
              <span class="badge badge-sm" x-text="ev.status||'—'"></span>
            </button>
          </template>
          <p x-show="!(selected?.recent_events||[]).length" class="muted">Nog geen events voor deze agent.</p>
        </div>
      </section>

      <section x-show="tab==='mesh'" x-cloak
        x-effect="if (tab === 'mesh') { $nextTick(() => mountMesh()) }">
        <div class="panel">
          <div class="mesh-framework-head">
            <h2 style="margin-top:0;">Agent Communicatie Framework</h2>
            <p class="page-tip" style="margin-top:.25rem;">Kies hoe je het netwerk wilt zien. Klik een agent → live terminal. Alle terminals onder tab <strong>Terminals</strong>.</p>
          </div>

          <div class="mesh-mode-section" id="mesh-mode-picker">
            <label>Communicatiestijl</label>
            <div class="mesh-mode-grid">
              <button type="button" class="mesh-mode-card" data-mesh-mode="pyramid" aria-pressed="false"
                onclick="window.AgentsMesh&&AgentsMesh.pickMode('pyramid')">
                <span class="mesh-mode-check" aria-hidden="true"></span>
                <span class="mesh-mode-icon">◆</span>
                <span class="mesh-mode-title">Piramide</span>
                <span class="mesh-mode-sub">Herman als hub</span>
                <span class="mesh-mode-desc">Rapport ↑ en delegatie ↓. Alleen live handoffs.</span>
              </button>
              <button type="button" class="mesh-mode-card" data-mesh-mode="mesh" aria-pressed="false"
                onclick="window.AgentsMesh&&AgentsMesh.pickMode('mesh')">
                <span class="mesh-mode-check" aria-hidden="true"></span>
                <span class="mesh-mode-icon">⬡</span>
                <span class="mesh-mode-title">Vol netwerk</span>
                <span class="mesh-mode-sub">Alle partners</span>
                <span class="mesh-mode-desc">Volledige samenwerkingsmatrix zichtbaar.</span>
              </button>
              <button type="button" class="mesh-mode-card" data-mesh-mode="org" aria-pressed="false"
                onclick="window.AgentsMesh&&AgentsMesh.pickMode('org')">
                <span class="mesh-mode-check" aria-hidden="true"></span>
                <span class="mesh-mode-icon">▤</span>
                <span class="mesh-mode-title">Organigram</span>
                <span class="mesh-mode-sub">Pure hiërarchie</span>
                <span class="mesh-mode-desc">CEO → Herman → agents, geen peer-lijnen.</span>
              </button>
              <button type="button" class="mesh-mode-card" data-mesh-mode="pulse" aria-pressed="false"
                onclick="window.AgentsMesh&&AgentsMesh.pickMode('pulse')">
                <span class="mesh-mode-check" aria-hidden="true"></span>
                <span class="mesh-mode-icon">◎</span>
                <span class="mesh-mode-title">Live stream</span>
                <span class="mesh-mode-sub">Alleen actief</span>
                <span class="mesh-mode-desc">Geen stille lijnen — alleen wat nu beweegt.</span>
              </button>
              <button type="button" class="mesh-mode-card" data-mesh-mode="clusters" aria-pressed="false"
                onclick="window.AgentsMesh&&AgentsMesh.pickMode('clusters')">
                <span class="mesh-mode-check" aria-hidden="true"></span>
                <span class="mesh-mode-icon">◉</span>
                <span class="mesh-mode-title">Domeinen</span>
                <span class="mesh-mode-sub">Per afdeling</span>
                <span class="mesh-mode-desc">Groei · Product · Operatie gegroepeerd.</span>
              </button>
            </div>
          </div>

          <div class="mesh-toolbar">
            <span id="mesh-mode-badge" class="mesh-mode-badge">Piramide</span>
            <button type="button" class="btn btn-sm" @click="remountMesh()">↻ Verversen</button>
            <button type="button" class="btn btn-sm btn-pulse" @click="setTab('terminals', $refs.vtabsNav?.querySelector('[data-tab=terminals]'))">Alle terminals</button>
          </div>
          <div class="mesh-wrap">
            <svg id="agents-mesh-canvas" class="mesh-svg" viewBox="0 0 1200 860"></svg>
          </div>
          <div class="mesh-legend">
            <span class="leg-report">↑ Rapport</span>
            <span class="leg-delegate">↓ Delegatie</span>
            <span class="leg-peer" x-show="meshLegendPeer">↔ Handoff</span>
            <span class="leg-exec" x-show="meshLegendExec">⇅ Executive</span>
          </div>
          <div class="mesh-terminal-focus" x-show="focusTerminalKey" x-cloak>
            <div class="agent-term-head">
              <span class="term-dot"></span>
              <strong x-text="'Terminal · ' + (focusTerminalName || '')"></strong>
              <button type="button" class="btn btn-sm" @click="clearFocusTerminal()">Sluiten</button>
            </div>
            <div class="agent-term-body" :data-term-key="focusTerminalKey" data-term-focus="1"></div>
          </div>
        </div>
      </section>

      <section x-show="tab==='terminals'" x-cloak>
        <div class="panel">
          <h2 style="margin-top:0;">Agent terminals</h2>
          <p class="agents-terminals-intro">Elke agent heeft een eigen terminal. Typ commando's en druk <kbd>Enter</kbd> — output verschijnt live. Start met <code>help</code>.</p>
          <div class="agents-terminals-grid">
            <template x-for="s in souls" :key="'term-' + s.agent_key">
              <article class="agent-term-window"
                :class="{ 'is-live': (s.events_6h||0) > 0 || s.current_task, 'is-expanded': expandedTerminal === s.agent_key }"
                @click="expandTerminal(s.agent_key)">
                <header class="agent-term-head">
                  <span class="term-dot"></span>
                  <strong x-text="s.display_name"></strong>
                  <small x-text="s.current_task || s.last_event_title || 'Standby'"></small>
                </header>
                <div class="agent-term-body" :data-term-key="s.agent_key"></div>
                <footer class="agent-term-foot" @click.stop>
                  <label class="agent-term-cmd" :for="'term-in-' + s.agent_key">
                    <span class="term-prompt" x-text="s.agent_key + '>'"></span>
                    <input type="text"
                      :id="'term-in-' + s.agent_key"
                      class="agent-term-input"
                      :data-term-input="s.agent_key"
                      :placeholder="s.agent_key === 'herman' ? 'briefing · vraag aan Herman…' : (s.agent_key === 'webbuilder' ? 'build <opdracht> · projects · status …' : 'help · status · handoff …')"
                      autocomplete="off"
                      spellcheck="false" />
                  </label>
                </footer>
              </article>
            </template>
          </div>
        </div>
      </section>

      <section x-show="tab==='approvals'" x-cloak x-data="approvalsPanel()" x-init="load()">
        <div class="panel">
          <div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:0.75rem;margin-bottom:1rem">
            <div>
              <h2 style="margin:0;">Approval queue</h2>
              <p class="page-tip">Agents dienen queries in → jij keurt goed → pas daarna mag de actie uitgevoerd worden.</p>
            </div>
            <button class="btn btn-sm btn-pulse" @click="load()">↻ Verversen</button>
          </div>
          <table class="data-table">
            <thead><tr><th>#</th><th>Agent</th><th>Actie</th><th>Titel</th><th>Status</th><th>Tijd</th><th></th></tr></thead>
            <tbody>
              <template x-for="r in items" :key="r.id">
                <tr>
                  <td x-text="r.id"></td>
                  <td x-text="r.agent_key"></td>
                  <td x-text="r.action_type"></td>
                  <td x-text="r.title"></td>
                  <td><span class="badge" :class="r.status==='pending'?'badge-warn':'badge-positive'" x-text="r.status"></span></td>
                  <td x-text="(r.created_at||'').substring(0,16).replace('T',' ')"></td>
                  <td>
                    <template x-if="r.status==='pending'">
                      <span>
                        <button class="btn btn-sm btn-approve" @click="approve(r.id)">✓</button>
                        <button class="btn btn-sm btn-reject" @click="reject(r.id)">✗</button>
                      </span>
                    </template>
                  </td>
                </tr>
              </template>
            </tbody>
          </table>
          <p x-show="!items.length" class="muted">Geen approval requests.</p>
        </div>
      </section>
    </div>
  </div>

  <div class="agent-events-backdrop" x-show="eventsOpen" x-cloak @click="closeEvents()"></div>
  <aside class="agent-events-drawer" :class="{ open: eventsOpen }" x-cloak>
    <header class="agent-events-head">
      <div>
        <h3 x-text="eventsAgentName || 'Agent'"></h3>
        <p class="muted" x-text="eventsList.length + ' activiteiten gelogd'"></p>
      </div>
      <button type="button" class="btn btn-sm" @click="closeEvents()">✕</button>
    </header>
    <div class="agent-events-body" x-show="!eventsLoading">
      <template x-for="ev in eventsList" :key="ev.id">
        <article class="agent-event-card" :class="{ expanded: selectedEvent?.id === ev.id }" @click="toggleEventDetail(ev)">
          <div class="agent-event-card-top">
            <time x-text="formatEventTime(ev.created_at)"></time>
            <span class="badge badge-sm" :class="eventStatusClass(ev.status)" x-text="ev.status || 'completed'"></span>
          </div>
          <strong x-text="ev.title || ev.event_type"></strong>
          <div class="agent-event-meta">
            <span x-text="ev.event_type"></span>
            <span x-show="ev.channel" x-text="'· ' + ev.channel"></span>
          </div>
          <div class="agent-event-detail" x-show="selectedEvent?.id === ev.id" x-cloak>
            <p x-show="ev.body" x-text="ev.body"></p>
            <pre x-show="!ev.body && ev.metadata && Object.keys(ev.metadata||{}).length" x-text="JSON.stringify(ev.metadata, null, 2)"></pre>
            <p x-show="!ev.body && !(ev.metadata && Object.keys(ev.metadata||{}).length)" class="muted">Geen extra details voor dit event.</p>
          </div>
        </article>
      </template>
      <p x-show="!eventsList.length && !eventsLoading" class="muted" style="padding:1rem">Nog geen events voor deze agent.</p>
    </div>
    <p x-show="eventsLoading" class="muted" style="padding:1.5rem">Events laden…</p>
  </aside>
</div>
{% endblock %}
{% block scripts %}
<script src="/static/js/dashboard-layout.js?v=2"></script>
<script src="/static/js/page-viz.js?v=1"></script>
<script src="/static/js/agents-mesh.js?v=11"></script>
<script src="/static/js/agents-terminals.js?v=6"></script>
<script>
function agentsSoulHub() {
  return {
    tab: '{{ active_tab }}',
    tabIndicatorTop: 0,
    tabIndicatorH: 40,
    meshLoaded: false,
    pendingCount: 0,
    souls: [], selected: null, form: {}, saving: false,
    eventsOpen: false, eventsAgentKey: null, eventsAgentName: '', eventsList: [], eventsLoading: false, selectedEvent: null,
    terminalOpen: false, terminalAgentKey: null, terminalAgentName: '', terminalLines: [], _terminalEs: null,
    meshCommMode: 'pyramid',
    meshLegendPeer: true,
    meshLegendExec: true,
    focusTerminalKey: null, focusTerminalName: '', expandedTerminal: null,
    get totalEvents() { return this.souls.reduce((a,s) => a + (s.event_count||0), 0); },
    get activeCount() { return this.souls.filter(s => s.is_active !== false).length; },
    statusClass(s) {
      const st = (s.current_status || '').toLowerCase();
      if (st === 'needs_approval' || st === 'pending') return 'busy';
      if (st === 'error' || st === 'rejected') return 'error';
      if (st === 'completed' || (s.events_6h > 0)) return 'active';
      return 'idle';
    },
    formatEventTime(ts) {
      return (ts || '').substring(0, 16).replace('T', ' ');
    },
    eventStatusClass(status) {
      const s = (status || '').toLowerCase();
      if (s === 'pending' || s === 'needs_approval') return 'badge-warn';
      if (s === 'error' || s === 'rejected') return 'badge-negative';
      if (s === 'approved' || s === 'completed') return 'badge-positive';
      return '';
    },
    async openEvents(key, name, focusId) {
      if (!key) return;
      this.eventsAgentKey = key;
      this.eventsAgentName = name || key;
      this.eventsOpen = true;
      this.eventsLoading = true;
      this.selectedEvent = null;
      try {
        const r = await fetch('/api/agents/souls/' + encodeURIComponent(key) + '/events?limit=50').then(x => x.json());
        this.eventsList = r.items || [];
        if (focusId) {
          const hit = this.eventsList.find(e => e.id === focusId);
          if (hit) this.selectedEvent = hit;
        }
      } catch (e) { this.eventsList = []; }
      this.eventsLoading = false;
    },
    closeEvents() {
      this.eventsOpen = false;
      this.selectedEvent = null;
    },
    toggleEventDetail(ev) {
      this.selectedEvent = this.selectedEvent?.id === ev.id ? null : ev;
    },
    statusLabel(s) {
      const st = (s.current_status || '').toLowerCase();
      if (st === 'needs_approval') return 'Wacht op goedkeuring';
      if (st === 'pending') return 'In wachtrij';
      if (st === 'completed') return 'Klaar';
      if (st === 'error') return 'Fout';
      if (s.current_task) return 'Bezig';
      return 'Standby';
    },
    async init() {
      this.syncMeshMode();
      await this.load();
      await this.loadPendingCount();
      if (window.PageViz) {
        window.PageViz.init('agents', (mode) => this.renderViz(mode));
      }
      this.$nextTick(() => {
        const activeBtn = this.$refs.vtabsNav?.querySelector('.vtab-btn.active');
        if (activeBtn) this.updateTabIndicator(activeBtn);
        const self = this;
        if (window.AgentsMesh) {
          AgentsMesh.bindModePicker({
            onNodeClick: function (node) {
              self.focusTerminal(node.agent_key, node.display_name);
            },
          });
        }
        if (window.AgentsTerminals) AgentsTerminals.startLiveStream();
        if (this.tab === 'mesh') this.mountMesh();
        if (this.tab === 'terminals') this.startTerminals();
      });
    },
    updateTabIndicator(el) {
      if (!el || !this.$refs.vtabsNav) return;
      const navRect = this.$refs.vtabsNav.getBoundingClientRect();
      const btnRect = el.getBoundingClientRect();
      this.tabIndicatorTop = Math.max(0, btnRect.top - navRect.top);
      this.tabIndicatorH = btnRect.height || 40;
    },
    setTab(next, el) {
      this.tab = next;
      this.updateTabIndicator(el);
      const url = new URL(window.location.href);
      url.searchParams.set('tab', next);
      window.history.replaceState({}, '', url.toString());
      if (next === 'mesh') this.mountMesh();
      if (next === 'terminals') this.startTerminals();
      if (next === 'approvals') this.loadPendingCount();
    },
    async loadPendingCount() {
      try {
        const r = await fetch('/api/agents/approvals?status=pending').then(x => x.json());
        this.pendingCount = (r.items || []).length;
      } catch (e) { this.pendingCount = 0; }
    },
    syncMeshMode() {
      if (!window.AgentsMesh) return;
      const self = this;
      this.meshCommMode = AgentsMesh.getSavedMode();
      this.updateMeshLegend();
      AgentsMesh.setModeChangeCallback(function (id) {
        self.meshCommMode = id;
        self.updateMeshLegend();
      });
    },
    updateMeshLegend() {
      const m = (window.AgentsMesh && AgentsMesh.COMM_MODES) ? AgentsMesh.COMM_MODES[this.meshCommMode] : null;
      this.meshLegendPeer = m ? (m.showLivePeers || m.showStaticPeers) : true;
      this.meshLegendExec = m ? m.showExecutive !== false : true;
    },
    setMeshMode(id) {
      if (window.AgentsMesh) AgentsMesh.pickMode(id);
      this.meshCommMode = id;
      this.updateMeshLegend();
    },
    remountMesh() {
      if (window.AgentsMesh && AgentsMesh.refresh) {
        const self = this;
        AgentsMesh.refresh('agents-mesh-canvas', {
          commMode: this.meshCommMode,
          onNodeClick: function (node) {
            self.focusTerminal(node.agent_key, node.display_name);
          },
        });
      } else {
        this.mountMesh();
      }
    },
    mountMesh() {
      if (!window.AgentsMesh) return;
      const self = this;
      const opts = {
        commMode: this.meshCommMode,
        onNodeClick: function (node) {
          self.focusTerminal(node.agent_key, node.display_name);
        },
      };
      window.AgentsMesh.mount('agents-mesh-canvas', opts).catch(function (e) {
        if (window.Cockpit) Cockpit.toast('Mesh: ' + (e.message || 'laden mislukt'), 'error');
      });
      if (this.focusTerminalKey && window.AgentsTerminals) {
        this.$nextTick(function () { AgentsTerminals.mountAll(); });
      }
    },
    focusTerminal(key, name) {
      this.focusTerminalKey = key;
      this.focusTerminalName = name || key;
      this.expandedTerminal = key;
      this.$nextTick(function () {
        if (window.AgentsTerminals) AgentsTerminals.mountAll();
      });
    },
    clearFocusTerminal() {
      this.focusTerminalKey = null;
      this.focusTerminalName = '';
    },
    goTerminal(key) {
      const soul = this.souls.find(function (s) { return s.agent_key === key; });
      const btn = this.$refs.vtabsNav?.querySelector('[data-tab=terminals]');
      this.setTab('terminals', btn);
      this.expandedTerminal = key;
      this.$nextTick(function () {
        const el = document.querySelector('[data-term-key="' + key + '"]');
        if (el) el.closest('.agent-term-window')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
      });
    },
    expandTerminal(key) {
      this.expandedTerminal = this.expandedTerminal === key ? null : key;
    },
    startTerminals() {
      this.$nextTick(function () {
        if (window.AgentsTerminals) {
          AgentsTerminals.mountAll();
          AgentsTerminals.bindInputs();
        }
      });
    },
    async load() {
      const r = await fetch('/api/agents/souls').then(x => x.json());
      this.souls = r.items || [];
      if (this.souls.length && !this.selected) this.select(this.souls[0].agent_key);
      this.renderViz(window.__vizMode || 'neo-bars');
      this.$nextTick(() => window.AgentAvatars && AgentAvatars.paintAll(this.$root));
    },
    renderViz(mode) {
      if (!window.VizEngine) return;
      const rows = (this.souls || []).map(s => ({
        label: s.agent_key,
        value: s.event_count || 0,
        raw: s.event_count || 0,
      })).sort((a, b) => b.raw - a.raw).slice(0, 12);
      if (!rows.length) rows.push({ label: 'geen', value: 0, raw: 0 });
      VizEngine.renderFromRows(mode,
        document.getElementById('chart-agents-viz'),
        document.getElementById('viz-agents-alt'),
        'agents-page', 'Events per agent', rows, 'hm-eq-lime'
      );
    },
    async select(key) {
      const r = await fetch('/api/agents/souls/' + key).then(x => x.json());
      this.selected = r.soul;
      this.form = { role_title: r.soul.role_title, responsibilities: r.soul.responsibilities, soul_md: r.soul.soul_md };
    },
    async save() {
      if (!this.selected) return;
      this.saving = true;
      try {
        await Cockpit.api('/api/agents/souls/' + this.selected.agent_key, { method: 'PUT', body: JSON.stringify(this.form) });
        Cockpit.toast('Soul opgeslagen', 'success');
        await this.load();
        await this.select(this.selected.agent_key);
      } catch (e) { Cockpit.toast(e.message, 'error'); }
      this.saving = false;
    }
  };
}

function approvalsPanel() {
  return {
    items: [],
    async load() {
      try {
        const r = await fetch('/api/agents/approvals?limit=50').then(x => x.json());
        this.items = r.items || [];
      } catch (e) { this.items = []; }
    },
    async approve(id) {
      await Cockpit.api('/api/agents/approvals/' + id + '/approve', { method: 'POST', body: '{}' });
      Cockpit.toast('Goedgekeurd — agent mag query uitvoeren', 'success');
      await this.load();
    },
    async reject(id) {
      const reason = prompt('Reden afwijzing (optioneel):') || '';
      await Cockpit.api('/api/agents/approvals/' + id + '/reject', { method: 'POST', body: JSON.stringify({ reason }) });
      Cockpit.toast('Afgewezen', 'info');
      await this.load();
    },
  };
}
</script>
{% endblock %}
