@theihtisham/agent-shadow-brain 5.2.0 → 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +310 -1014
- package/dist/brain/agent-firewall.d.ts +7 -0
- package/dist/brain/agent-firewall.d.ts.map +1 -0
- package/dist/brain/agent-firewall.js +152 -0
- package/dist/brain/agent-firewall.js.map +1 -0
- package/dist/brain/agent-handoff.d.ts +19 -0
- package/dist/brain/agent-handoff.d.ts.map +1 -0
- package/dist/brain/agent-handoff.js +127 -0
- package/dist/brain/agent-handoff.js.map +1 -0
- package/dist/brain/air-gap.d.ts +23 -0
- package/dist/brain/air-gap.d.ts.map +1 -0
- package/dist/brain/air-gap.js +137 -0
- package/dist/brain/air-gap.js.map +1 -0
- package/dist/brain/attention-heatmap.d.ts +24 -0
- package/dist/brain/attention-heatmap.d.ts.map +1 -0
- package/dist/brain/attention-heatmap.js +108 -0
- package/dist/brain/attention-heatmap.js.map +1 -0
- package/dist/brain/auto-setup.js +19 -14
- package/dist/brain/auto-setup.js.map +1 -1
- package/dist/brain/auto-update.js +1 -1
- package/dist/brain/brain-3d.d.ts +79 -0
- package/dist/brain/brain-3d.d.ts.map +1 -0
- package/dist/brain/brain-3d.js +468 -0
- package/dist/brain/brain-3d.js.map +1 -0
- package/dist/brain/brain-active-learner.d.ts +74 -0
- package/dist/brain/brain-active-learner.d.ts.map +1 -0
- package/dist/brain/brain-active-learner.js +425 -0
- package/dist/brain/brain-active-learner.js.map +1 -0
- package/dist/brain/brain-ascii.d.ts +32 -0
- package/dist/brain/brain-ascii.d.ts.map +1 -0
- package/dist/brain/brain-ascii.js +402 -0
- package/dist/brain/brain-ascii.js.map +1 -0
- package/dist/brain/brain-chat.d.ts +42 -0
- package/dist/brain/brain-chat.d.ts.map +1 -0
- package/dist/brain/brain-chat.js +162 -0
- package/dist/brain/brain-chat.js.map +1 -0
- package/dist/brain/brain-cinema.d.ts +45 -0
- package/dist/brain/brain-cinema.d.ts.map +1 -0
- package/dist/brain/brain-cinema.js +393 -0
- package/dist/brain/brain-cinema.js.map +1 -0
- package/dist/brain/brain-coach.d.ts +45 -0
- package/dist/brain/brain-coach.d.ts.map +1 -0
- package/dist/brain/brain-coach.js +383 -0
- package/dist/brain/brain-coach.js.map +1 -0
- package/dist/brain/brain-consolidator.d.ts +56 -0
- package/dist/brain/brain-consolidator.d.ts.map +1 -0
- package/dist/brain/brain-consolidator.js +386 -0
- package/dist/brain/brain-consolidator.js.map +1 -0
- package/dist/brain/brain-contradictions.d.ts +57 -0
- package/dist/brain/brain-contradictions.d.ts.map +1 -0
- package/dist/brain/brain-contradictions.js +443 -0
- package/dist/brain/brain-contradictions.js.map +1 -0
- package/dist/brain/brain-diff.d.ts +61 -0
- package/dist/brain/brain-diff.d.ts.map +1 -0
- package/dist/brain/brain-diff.js +439 -0
- package/dist/brain/brain-diff.js.map +1 -0
- package/dist/brain/brain-dna.d.ts +59 -0
- package/dist/brain/brain-dna.d.ts.map +1 -0
- package/dist/brain/brain-dna.js +336 -0
- package/dist/brain/brain-dna.js.map +1 -0
- package/dist/brain/brain-doppelganger.d.ts +32 -0
- package/dist/brain/brain-doppelganger.d.ts.map +1 -0
- package/dist/brain/brain-doppelganger.js +417 -0
- package/dist/brain/brain-doppelganger.js.map +1 -0
- package/dist/brain/brain-encryption.d.ts +14 -0
- package/dist/brain/brain-encryption.d.ts.map +1 -0
- package/dist/brain/brain-encryption.js +68 -0
- package/dist/brain/brain-encryption.js.map +1 -0
- package/dist/brain/brain-exchange.d.ts +30 -0
- package/dist/brain/brain-exchange.d.ts.map +1 -0
- package/dist/brain/brain-exchange.js +106 -0
- package/dist/brain/brain-exchange.js.map +1 -0
- package/dist/brain/brain-garden.d.ts +18 -0
- package/dist/brain/brain-garden.d.ts.map +1 -0
- package/dist/brain/brain-garden.js +81 -0
- package/dist/brain/brain-garden.js.map +1 -0
- package/dist/brain/brain-graph-walker.d.ts +54 -0
- package/dist/brain/brain-graph-walker.d.ts.map +1 -0
- package/dist/brain/brain-graph-walker.js +412 -0
- package/dist/brain/brain-graph-walker.js.map +1 -0
- package/dist/brain/brain-holiday-card.d.ts +50 -0
- package/dist/brain/brain-holiday-card.d.ts.map +1 -0
- package/dist/brain/brain-holiday-card.js +432 -0
- package/dist/brain/brain-holiday-card.js.map +1 -0
- package/dist/brain/brain-memes.d.ts +44 -0
- package/dist/brain/brain-memes.d.ts.map +1 -0
- package/dist/brain/brain-memes.js +438 -0
- package/dist/brain/brain-memes.js.map +1 -0
- package/dist/brain/brain-memory-tiers.d.ts +67 -0
- package/dist/brain/brain-memory-tiers.d.ts.map +1 -0
- package/dist/brain/brain-memory-tiers.js +292 -0
- package/dist/brain/brain-memory-tiers.js.map +1 -0
- package/dist/brain/brain-notebook.d.ts +68 -0
- package/dist/brain/brain-notebook.d.ts.map +1 -0
- package/dist/brain/brain-notebook.js +379 -0
- package/dist/brain/brain-notebook.js.map +1 -0
- package/dist/brain/brain-portability.js +1 -1
- package/dist/brain/brain-quantize.d.ts +62 -0
- package/dist/brain/brain-quantize.d.ts.map +1 -0
- package/dist/brain/brain-quantize.js +346 -0
- package/dist/brain/brain-quantize.js.map +1 -0
- package/dist/brain/brain-query-expander.d.ts +31 -0
- package/dist/brain/brain-query-expander.d.ts.map +1 -0
- package/dist/brain/brain-query-expander.js +417 -0
- package/dist/brain/brain-query-expander.js.map +1 -0
- package/dist/brain/brain-quest.d.ts +58 -0
- package/dist/brain/brain-quest.d.ts.map +1 -0
- package/dist/brain/brain-quest.js +541 -0
- package/dist/brain/brain-quest.js.map +1 -0
- package/dist/brain/brain-recall-cache.d.ts +73 -0
- package/dist/brain/brain-recall-cache.d.ts.map +1 -0
- package/dist/brain/brain-recall-cache.js +444 -0
- package/dist/brain/brain-recall-cache.js.map +1 -0
- package/dist/brain/brain-reflex.d.ts +55 -0
- package/dist/brain/brain-reflex.d.ts.map +1 -0
- package/dist/brain/brain-reflex.js +364 -0
- package/dist/brain/brain-reflex.js.map +1 -0
- package/dist/brain/brain-replay.d.ts +74 -0
- package/dist/brain/brain-replay.d.ts.map +1 -0
- package/dist/brain/brain-replay.js +336 -0
- package/dist/brain/brain-replay.js.map +1 -0
- package/dist/brain/brain-rerank.d.ts +39 -0
- package/dist/brain/brain-rerank.d.ts.map +1 -0
- package/dist/brain/brain-rerank.js +216 -0
- package/dist/brain/brain-rerank.js.map +1 -0
- package/dist/brain/brain-roast.d.ts +44 -0
- package/dist/brain/brain-roast.d.ts.map +1 -0
- package/dist/brain/brain-roast.js +566 -0
- package/dist/brain/brain-roast.js.map +1 -0
- package/dist/brain/brain-sonification.d.ts +57 -0
- package/dist/brain/brain-sonification.d.ts.map +1 -0
- package/dist/brain/brain-sonification.js +286 -0
- package/dist/brain/brain-sonification.js.map +1 -0
- package/dist/brain/brain-tarot.d.ts +49 -0
- package/dist/brain/brain-tarot.d.ts.map +1 -0
- package/dist/brain/brain-tarot.js +634 -0
- package/dist/brain/brain-tarot.js.map +1 -0
- package/dist/brain/brain-time-capsule.d.ts +80 -0
- package/dist/brain/brain-time-capsule.d.ts.map +1 -0
- package/dist/brain/brain-time-capsule.js +289 -0
- package/dist/brain/brain-time-capsule.js.map +1 -0
- package/dist/brain/brain-trophies.d.ts +60 -0
- package/dist/brain/brain-trophies.d.ts.map +1 -0
- package/dist/brain/brain-trophies.js +431 -0
- package/dist/brain/brain-trophies.js.map +1 -0
- package/dist/brain/brain-working-memory.d.ts +78 -0
- package/dist/brain/brain-working-memory.d.ts.map +1 -0
- package/dist/brain/brain-working-memory.js +367 -0
- package/dist/brain/brain-working-memory.js.map +1 -0
- package/dist/brain/branch-brain.d.ts +23 -0
- package/dist/brain/branch-brain.d.ts.map +1 -0
- package/dist/brain/branch-brain.js +159 -0
- package/dist/brain/branch-brain.js.map +1 -0
- package/dist/brain/calibration-monitor.d.ts +20 -0
- package/dist/brain/calibration-monitor.d.ts.map +1 -0
- package/dist/brain/calibration-monitor.js +144 -0
- package/dist/brain/calibration-monitor.js.map +1 -0
- package/dist/brain/causal-chains.d.ts +37 -0
- package/dist/brain/causal-chains.d.ts.map +1 -0
- package/dist/brain/causal-chains.js +250 -0
- package/dist/brain/causal-chains.js.map +1 -0
- package/dist/brain/collision-detective.d.ts +32 -0
- package/dist/brain/collision-detective.d.ts.map +1 -0
- package/dist/brain/collision-detective.js +233 -0
- package/dist/brain/collision-detective.js.map +1 -0
- package/dist/brain/confidence-gate.d.ts +69 -0
- package/dist/brain/confidence-gate.d.ts.map +1 -0
- package/dist/brain/confidence-gate.js +244 -0
- package/dist/brain/confidence-gate.js.map +1 -0
- package/dist/brain/constitution.d.ts +59 -0
- package/dist/brain/constitution.d.ts.map +1 -0
- package/dist/brain/constitution.js +315 -0
- package/dist/brain/constitution.js.map +1 -0
- package/dist/brain/cost-aware-spawner.d.ts +7 -0
- package/dist/brain/cost-aware-spawner.d.ts.map +1 -0
- package/dist/brain/cost-aware-spawner.js +77 -0
- package/dist/brain/cost-aware-spawner.js.map +1 -0
- package/dist/brain/dream-engine.d.ts +35 -0
- package/dist/brain/dream-engine.d.ts.map +1 -0
- package/dist/brain/dream-engine.js +273 -0
- package/dist/brain/dream-engine.js.map +1 -0
- package/dist/brain/embeddings-v2.d.ts +58 -0
- package/dist/brain/embeddings-v2.d.ts.map +1 -0
- package/dist/brain/embeddings-v2.js +344 -0
- package/dist/brain/embeddings-v2.js.map +1 -0
- package/dist/brain/embeddings.d.ts +27 -0
- package/dist/brain/embeddings.d.ts.map +1 -0
- package/dist/brain/embeddings.js +175 -0
- package/dist/brain/embeddings.js.map +1 -0
- package/dist/brain/forgetting-curve.d.ts +21 -0
- package/dist/brain/forgetting-curve.d.ts.map +1 -0
- package/dist/brain/forgetting-curve.js +174 -0
- package/dist/brain/forgetting-curve.js.map +1 -0
- package/dist/brain/formal-verification-bridge.d.ts +26 -0
- package/dist/brain/formal-verification-bridge.d.ts.map +1 -0
- package/dist/brain/formal-verification-bridge.js +219 -0
- package/dist/brain/formal-verification-bridge.js.map +1 -0
- package/dist/brain/global-brain.d.ts +8 -1
- package/dist/brain/global-brain.d.ts.map +1 -1
- package/dist/brain/global-brain.js +24 -0
- package/dist/brain/global-brain.js.map +1 -1
- package/dist/brain/hallucination-quarantine.d.ts +26 -0
- package/dist/brain/hallucination-quarantine.d.ts.map +1 -0
- package/dist/brain/hallucination-quarantine.js +135 -0
- package/dist/brain/hallucination-quarantine.js.map +1 -0
- package/dist/brain/hive-accelerator.d.ts +39 -0
- package/dist/brain/hive-accelerator.d.ts.map +1 -0
- package/dist/brain/hive-accelerator.js +152 -0
- package/dist/brain/hive-accelerator.js.map +1 -0
- package/dist/brain/hive-voice.d.ts +71 -0
- package/dist/brain/hive-voice.d.ts.map +1 -0
- package/dist/brain/hive-voice.js +352 -0
- package/dist/brain/hive-voice.js.map +1 -0
- package/dist/brain/json-output.d.ts +26 -0
- package/dist/brain/json-output.d.ts.map +1 -0
- package/dist/brain/json-output.js +112 -0
- package/dist/brain/json-output.js.map +1 -0
- package/dist/brain/l0-cache.js +1 -1
- package/dist/brain/l0-cache.js.map +1 -1
- package/dist/brain/local-llm.d.ts +20 -0
- package/dist/brain/local-llm.d.ts.map +1 -0
- package/dist/brain/local-llm.js +132 -0
- package/dist/brain/local-llm.js.map +1 -0
- package/dist/brain/markdown-export.d.ts +48 -0
- package/dist/brain/markdown-export.d.ts.map +1 -0
- package/dist/brain/markdown-export.js +382 -0
- package/dist/brain/markdown-export.js.map +1 -0
- package/dist/brain/mcp-server.d.ts +40 -0
- package/dist/brain/mcp-server.d.ts.map +1 -1
- package/dist/brain/mcp-server.js +395 -12
- package/dist/brain/mcp-server.js.map +1 -1
- package/dist/brain/model-registry.d.ts +96 -0
- package/dist/brain/model-registry.d.ts.map +1 -0
- package/dist/brain/model-registry.js +585 -0
- package/dist/brain/model-registry.js.map +1 -0
- package/dist/brain/multimodal-vision.d.ts +38 -0
- package/dist/brain/multimodal-vision.d.ts.map +1 -0
- package/dist/brain/multimodal-vision.js +257 -0
- package/dist/brain/multimodal-vision.js.map +1 -0
- package/dist/brain/orchestrator.js +1 -1
- package/dist/brain/otel-lite.d.ts +71 -0
- package/dist/brain/otel-lite.d.ts.map +1 -0
- package/dist/brain/otel-lite.js +232 -0
- package/dist/brain/otel-lite.js.map +1 -0
- package/dist/brain/pr-auto-review.d.ts +17 -0
- package/dist/brain/pr-auto-review.d.ts.map +1 -0
- package/dist/brain/pr-auto-review.js +73 -0
- package/dist/brain/pr-auto-review.js.map +1 -0
- package/dist/brain/pre-mortem.d.ts +21 -0
- package/dist/brain/pre-mortem.d.ts.map +1 -0
- package/dist/brain/pre-mortem.js +210 -0
- package/dist/brain/pre-mortem.js.map +1 -0
- package/dist/brain/prompt-cache.d.ts +54 -0
- package/dist/brain/prompt-cache.d.ts.map +1 -0
- package/dist/brain/prompt-cache.js +105 -0
- package/dist/brain/prompt-cache.js.map +1 -0
- package/dist/brain/reputation-ledger.d.ts +42 -0
- package/dist/brain/reputation-ledger.d.ts.map +1 -0
- package/dist/brain/reputation-ledger.js +326 -0
- package/dist/brain/reputation-ledger.js.map +1 -0
- package/dist/brain/session-hooks.d.ts +19 -0
- package/dist/brain/session-hooks.d.ts.map +1 -1
- package/dist/brain/session-hooks.js +249 -18
- package/dist/brain/session-hooks.js.map +1 -1
- package/dist/brain/subagent-bridge.d.ts +46 -0
- package/dist/brain/subagent-bridge.d.ts.map +1 -0
- package/dist/brain/subagent-bridge.js +364 -0
- package/dist/brain/subagent-bridge.js.map +1 -0
- package/dist/brain/swarm-debate.d.ts +22 -0
- package/dist/brain/swarm-debate.d.ts.map +1 -0
- package/dist/brain/swarm-debate.js +134 -0
- package/dist/brain/swarm-debate.js.map +1 -0
- package/dist/brain/team-brain-sync.d.ts +22 -0
- package/dist/brain/team-brain-sync.d.ts.map +1 -0
- package/dist/brain/team-brain-sync.js +132 -0
- package/dist/brain/team-brain-sync.js.map +1 -0
- package/dist/brain/token-economy.d.ts +25 -0
- package/dist/brain/token-economy.d.ts.map +1 -0
- package/dist/brain/token-economy.js +216 -0
- package/dist/brain/token-economy.js.map +1 -0
- package/dist/brain/voice-mode.d.ts +15 -0
- package/dist/brain/voice-mode.d.ts.map +1 -0
- package/dist/brain/voice-mode.js +97 -0
- package/dist/brain/voice-mode.js.map +1 -0
- package/dist/cli.js +1702 -4
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/server.d.ts +6 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +1118 -368
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/v6-dashboard-html.d.ts +2 -0
- package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -0
- package/dist/dashboard/v6-dashboard-html.js +2474 -0
- package/dist/dashboard/v6-dashboard-html.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +518 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +33 -4
|
@@ -0,0 +1,2474 @@
|
|
|
1
|
+
// src/dashboard/v6-dashboard-html.ts — Redesigned v6.0 "Hive Mind" Control Dashboard
|
|
2
|
+
// Responsive, glassmorphism, dark/light, Chart.js analytics, command palette (Cmd+K),
|
|
3
|
+
// toast notifications, Lucide icons, GSAP transitions. Served at / (default) and /hive.
|
|
4
|
+
export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
5
|
+
<html lang="en" data-theme="light">
|
|
6
|
+
<head>
|
|
7
|
+
<meta charset="UTF-8">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
|
|
9
|
+
<meta name="color-scheme" content="dark light">
|
|
10
|
+
<title>Shadow Brain v6.0 — Hive Mind Control</title>
|
|
11
|
+
<link rel="preconnect" href="https://cdn.jsdelivr.net">
|
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
|
|
13
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|
14
|
+
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
|
15
|
+
<style>
|
|
16
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
17
|
+
/* Design System Tokens */
|
|
18
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
19
|
+
:root {
|
|
20
|
+
/* Dark theme */
|
|
21
|
+
--bg: #030509;
|
|
22
|
+
--bg-elev-1: #090d17;
|
|
23
|
+
--bg-elev-2: #10152a;
|
|
24
|
+
--bg-elev-3: #18203b;
|
|
25
|
+
--bg-glass: rgba(15, 20, 38, 0.55);
|
|
26
|
+
--bg-glass-strong: rgba(22, 28, 52, 0.78);
|
|
27
|
+
--border: rgba(255, 255, 255, 0.08);
|
|
28
|
+
--border-strong: rgba(255, 255, 255, 0.14);
|
|
29
|
+
--border-accent: rgba(24, 255, 255, 0.32);
|
|
30
|
+
|
|
31
|
+
--text: #e8ecf4;
|
|
32
|
+
--text-2: #a4afc4;
|
|
33
|
+
--text-3: #64708a;
|
|
34
|
+
--text-dim: #3e4762;
|
|
35
|
+
|
|
36
|
+
--brand-cyan: #18ffff;
|
|
37
|
+
--brand-purple: #a855f7;
|
|
38
|
+
--brand-pink: #ec4899;
|
|
39
|
+
--brand-gradient: linear-gradient(135deg, #18ffff 0%, #a855f7 50%, #ec4899 100%);
|
|
40
|
+
--brand-gradient-soft: linear-gradient(135deg, rgba(24,255,255,0.15), rgba(168,85,247,0.15));
|
|
41
|
+
|
|
42
|
+
--success: #10e68c;
|
|
43
|
+
--warning: #ffb53f;
|
|
44
|
+
--danger: #ff5864;
|
|
45
|
+
--info: #4fb6ff;
|
|
46
|
+
|
|
47
|
+
--radius-sm: 6px;
|
|
48
|
+
--radius: 10px;
|
|
49
|
+
--radius-lg: 14px;
|
|
50
|
+
--radius-xl: 20px;
|
|
51
|
+
|
|
52
|
+
--shadow-sm: 0 1px 2px rgba(0,0,0,0.2);
|
|
53
|
+
--shadow: 0 4px 16px rgba(0,0,0,0.24), 0 0 0 1px var(--border);
|
|
54
|
+
--shadow-lg: 0 12px 48px rgba(0,0,0,0.45), 0 0 0 1px var(--border-strong);
|
|
55
|
+
--shadow-glow: 0 0 40px rgba(24,255,255,0.15);
|
|
56
|
+
|
|
57
|
+
--duration-fast: 120ms;
|
|
58
|
+
--duration: 220ms;
|
|
59
|
+
--duration-slow: 400ms;
|
|
60
|
+
--easing: cubic-bezier(0.4, 0, 0.2, 1);
|
|
61
|
+
--easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
62
|
+
|
|
63
|
+
--sidebar-w: 252px;
|
|
64
|
+
--header-h: 64px;
|
|
65
|
+
--ticker-h: 32px;
|
|
66
|
+
|
|
67
|
+
--font-sans: 'Inter', -apple-system, system-ui, 'Segoe UI', Roboto, sans-serif;
|
|
68
|
+
--font-mono: 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Light theme — clean, high-contrast, professional */
|
|
72
|
+
html[data-theme="light"] {
|
|
73
|
+
--bg: #f7f8fb;
|
|
74
|
+
--bg-elev-1: #ffffff;
|
|
75
|
+
--bg-elev-2: #f2f4f8;
|
|
76
|
+
--bg-elev-3: #e8ecf2;
|
|
77
|
+
--bg-glass: rgba(255, 255, 255, 0.88);
|
|
78
|
+
--bg-glass-strong: rgba(255, 255, 255, 0.96);
|
|
79
|
+
--border: rgba(15, 23, 42, 0.10);
|
|
80
|
+
--border-strong: rgba(15, 23, 42, 0.18);
|
|
81
|
+
--border-accent: rgba(99, 102, 241, 0.38);
|
|
82
|
+
--text: #0a0f1c;
|
|
83
|
+
--text-2: #2b3648;
|
|
84
|
+
--text-3: #55607a;
|
|
85
|
+
--text-dim: #9ca3af;
|
|
86
|
+
--brand-cyan: #0891b2;
|
|
87
|
+
--brand-purple: #7c3aed;
|
|
88
|
+
--brand-pink: #db2777;
|
|
89
|
+
--brand-gradient: linear-gradient(135deg, #0891b2 0%, #7c3aed 55%, #db2777 100%);
|
|
90
|
+
--brand-gradient-soft: linear-gradient(135deg, rgba(8,145,178,0.10), rgba(124,58,237,0.10));
|
|
91
|
+
--success: #059669;
|
|
92
|
+
--warning: #d97706;
|
|
93
|
+
--danger: #dc2626;
|
|
94
|
+
--info: #2563eb;
|
|
95
|
+
--shadow-sm: 0 1px 2px rgba(15,23,42,0.06);
|
|
96
|
+
--shadow: 0 4px 16px rgba(15,23,42,0.08), 0 0 0 1px var(--border);
|
|
97
|
+
--shadow-lg: 0 12px 48px rgba(15,23,42,0.14), 0 0 0 1px var(--border-strong);
|
|
98
|
+
--shadow-glow: 0 0 32px rgba(8,145,178,0.12);
|
|
99
|
+
}
|
|
100
|
+
html[data-theme="light"] body::before {
|
|
101
|
+
background:
|
|
102
|
+
radial-gradient(ellipse at 10% 0%, rgba(124,58,237,0.06) 0%, transparent 45%),
|
|
103
|
+
radial-gradient(ellipse at 90% 100%, rgba(8,145,178,0.05) 0%, transparent 50%);
|
|
104
|
+
}
|
|
105
|
+
html[data-theme="light"] ::-webkit-scrollbar-thumb { background: rgba(15,23,42,0.15); }
|
|
106
|
+
html[data-theme="light"] ::-webkit-scrollbar-thumb:hover { background: rgba(124,58,237,0.35); }
|
|
107
|
+
|
|
108
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
109
|
+
/* Reset + base */
|
|
110
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
111
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
112
|
+
html, body {
|
|
113
|
+
height: 100%;
|
|
114
|
+
font-family: var(--font-sans);
|
|
115
|
+
font-size: 14px;
|
|
116
|
+
background: var(--bg);
|
|
117
|
+
color: var(--text);
|
|
118
|
+
-webkit-font-smoothing: antialiased;
|
|
119
|
+
-moz-osx-font-smoothing: grayscale;
|
|
120
|
+
line-height: 1.5;
|
|
121
|
+
overflow: hidden;
|
|
122
|
+
}
|
|
123
|
+
body { display: flex; flex-direction: column; }
|
|
124
|
+
a { color: var(--brand-cyan); text-decoration: none; }
|
|
125
|
+
a:hover { text-decoration: underline; }
|
|
126
|
+
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
|
|
127
|
+
input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
|
|
128
|
+
|
|
129
|
+
/* Ambient backdrop */
|
|
130
|
+
body::before {
|
|
131
|
+
content: "";
|
|
132
|
+
position: fixed;
|
|
133
|
+
inset: 0;
|
|
134
|
+
pointer-events: none;
|
|
135
|
+
background:
|
|
136
|
+
radial-gradient(ellipse at 10% 0%, rgba(168,85,247,0.10) 0%, transparent 45%),
|
|
137
|
+
radial-gradient(ellipse at 90% 100%, rgba(24,255,255,0.08) 0%, transparent 50%),
|
|
138
|
+
radial-gradient(ellipse at 50% 50%, rgba(236,72,153,0.04) 0%, transparent 60%);
|
|
139
|
+
z-index: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
143
|
+
/* Scrollbars */
|
|
144
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
145
|
+
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
146
|
+
::-webkit-scrollbar-track { background: transparent; }
|
|
147
|
+
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
|
|
148
|
+
::-webkit-scrollbar-thumb:hover { background: rgba(168,85,247,0.4); }
|
|
149
|
+
|
|
150
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
151
|
+
/* Header */
|
|
152
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
153
|
+
header {
|
|
154
|
+
height: var(--header-h);
|
|
155
|
+
background: var(--bg-glass-strong);
|
|
156
|
+
backdrop-filter: blur(24px) saturate(180%);
|
|
157
|
+
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
|
158
|
+
border-bottom: 1px solid var(--border);
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 16px;
|
|
162
|
+
padding: 0 20px;
|
|
163
|
+
position: relative;
|
|
164
|
+
z-index: 100;
|
|
165
|
+
flex-shrink: 0;
|
|
166
|
+
}
|
|
167
|
+
.brain-hero {
|
|
168
|
+
position: relative;
|
|
169
|
+
width: 44px; height: 44px;
|
|
170
|
+
flex-shrink: 0;
|
|
171
|
+
}
|
|
172
|
+
.brain-hero .aura {
|
|
173
|
+
position: absolute; inset: -8px;
|
|
174
|
+
border-radius: 50%;
|
|
175
|
+
background: radial-gradient(circle, rgba(168,85,247,0.55) 0%, rgba(24,255,255,0.15) 40%, transparent 75%);
|
|
176
|
+
animation: auraPulse 3.2s ease-in-out infinite;
|
|
177
|
+
filter: blur(6px);
|
|
178
|
+
}
|
|
179
|
+
@keyframes auraPulse { 50% { transform: scale(1.22); opacity: 1; } }
|
|
180
|
+
.brain-hero svg { position: relative; width: 100%; height: 100%; filter: drop-shadow(0 0 8px rgba(24,255,255,0.4)); }
|
|
181
|
+
.brain-hero .hemi { fill: none; stroke: var(--brand-cyan); stroke-width: 1.4; stroke-linecap: round; opacity: 0.9; }
|
|
182
|
+
.brain-hero .hemi-r { stroke: var(--brand-purple); }
|
|
183
|
+
.brain-hero .synapse { animation: synapse 1.4s ease-in-out infinite; transform-origin: center; transform-box: fill-box; }
|
|
184
|
+
@keyframes synapse { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } }
|
|
185
|
+
.brain-hero .charge { stroke-dasharray: 2 6; animation: rot 2s linear infinite; }
|
|
186
|
+
@keyframes rot { to { stroke-dashoffset: -20; } }
|
|
187
|
+
|
|
188
|
+
.brand {
|
|
189
|
+
display: flex; flex-direction: column; line-height: 1.15;
|
|
190
|
+
}
|
|
191
|
+
.brand .name {
|
|
192
|
+
font-size: 15px; font-weight: 800; letter-spacing: -0.2px;
|
|
193
|
+
background: var(--brand-gradient);
|
|
194
|
+
-webkit-background-clip: text;
|
|
195
|
+
-webkit-text-fill-color: transparent;
|
|
196
|
+
background-clip: text;
|
|
197
|
+
}
|
|
198
|
+
.brand .state {
|
|
199
|
+
font-family: var(--font-mono);
|
|
200
|
+
font-size: 10px;
|
|
201
|
+
color: var(--brand-cyan);
|
|
202
|
+
letter-spacing: 0.08em;
|
|
203
|
+
text-transform: uppercase;
|
|
204
|
+
display: flex; align-items: center; gap: 5px;
|
|
205
|
+
}
|
|
206
|
+
.brand .state::before {
|
|
207
|
+
content: ""; width: 5px; height: 5px; border-radius: 50%;
|
|
208
|
+
background: var(--success); box-shadow: 0 0 6px var(--success);
|
|
209
|
+
animation: pulse 1.5s infinite;
|
|
210
|
+
}
|
|
211
|
+
@keyframes pulse { 50% { opacity: 0.5; } }
|
|
212
|
+
|
|
213
|
+
.header-stats {
|
|
214
|
+
display: flex; gap: 8px; margin-left: 16px;
|
|
215
|
+
flex-wrap: nowrap; overflow-x: auto;
|
|
216
|
+
scrollbar-width: none;
|
|
217
|
+
}
|
|
218
|
+
.header-stats::-webkit-scrollbar { display: none; }
|
|
219
|
+
.stat-pill {
|
|
220
|
+
display: flex; align-items: center; gap: 6px;
|
|
221
|
+
background: var(--bg-glass);
|
|
222
|
+
border: 1px solid var(--border);
|
|
223
|
+
border-radius: 999px;
|
|
224
|
+
padding: 5px 12px;
|
|
225
|
+
font-size: 11px;
|
|
226
|
+
color: var(--text-2);
|
|
227
|
+
white-space: nowrap;
|
|
228
|
+
transition: all var(--duration) var(--easing);
|
|
229
|
+
}
|
|
230
|
+
.stat-pill:hover { border-color: var(--border-strong); color: var(--text); }
|
|
231
|
+
.stat-pill .n { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
|
|
232
|
+
|
|
233
|
+
.header-right {
|
|
234
|
+
margin-left: auto;
|
|
235
|
+
display: flex; gap: 8px; align-items: center;
|
|
236
|
+
}
|
|
237
|
+
.icon-btn {
|
|
238
|
+
width: 34px; height: 34px;
|
|
239
|
+
display: flex; align-items: center; justify-content: center;
|
|
240
|
+
background: var(--bg-glass);
|
|
241
|
+
border: 1px solid var(--border);
|
|
242
|
+
border-radius: 10px;
|
|
243
|
+
color: var(--text-2);
|
|
244
|
+
transition: all var(--duration) var(--easing);
|
|
245
|
+
}
|
|
246
|
+
.icon-btn:hover { background: var(--bg-elev-3); color: var(--text); border-color: var(--border-strong); }
|
|
247
|
+
.icon-btn svg { width: 16px; height: 16px; }
|
|
248
|
+
.kbd-hint {
|
|
249
|
+
background: var(--bg-glass);
|
|
250
|
+
border: 1px solid var(--border);
|
|
251
|
+
border-radius: 8px;
|
|
252
|
+
padding: 5px 10px;
|
|
253
|
+
font-family: var(--font-mono); font-size: 10px;
|
|
254
|
+
color: var(--text-3);
|
|
255
|
+
display: flex; align-items: center; gap: 6px;
|
|
256
|
+
transition: all var(--duration);
|
|
257
|
+
}
|
|
258
|
+
.kbd-hint:hover { color: var(--text); border-color: var(--brand-cyan); }
|
|
259
|
+
kbd {
|
|
260
|
+
background: var(--bg-elev-3);
|
|
261
|
+
border-radius: 4px;
|
|
262
|
+
padding: 1px 5px;
|
|
263
|
+
font-size: 9px;
|
|
264
|
+
font-family: var(--font-mono);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
268
|
+
/* Layout: sidebar + main */
|
|
269
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
270
|
+
.layout {
|
|
271
|
+
display: grid;
|
|
272
|
+
grid-template-columns: var(--sidebar-w) 1fr;
|
|
273
|
+
flex: 1;
|
|
274
|
+
overflow: hidden;
|
|
275
|
+
position: relative;
|
|
276
|
+
z-index: 1;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
aside.sidebar {
|
|
280
|
+
background: var(--bg-glass);
|
|
281
|
+
backdrop-filter: blur(20px);
|
|
282
|
+
-webkit-backdrop-filter: blur(20px);
|
|
283
|
+
border-right: 1px solid var(--border);
|
|
284
|
+
overflow-y: auto;
|
|
285
|
+
padding: 16px 10px;
|
|
286
|
+
}
|
|
287
|
+
.nav-group { margin-bottom: 14px; }
|
|
288
|
+
.nav-group-label {
|
|
289
|
+
font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
|
|
290
|
+
color: var(--text-3); padding: 6px 12px; font-weight: 700;
|
|
291
|
+
}
|
|
292
|
+
.nav-item {
|
|
293
|
+
display: flex; align-items: center; gap: 10px;
|
|
294
|
+
padding: 8px 12px;
|
|
295
|
+
border-radius: 8px;
|
|
296
|
+
cursor: pointer;
|
|
297
|
+
font-size: 12.5px;
|
|
298
|
+
color: var(--text-2);
|
|
299
|
+
transition: all var(--duration-fast) var(--easing);
|
|
300
|
+
position: relative;
|
|
301
|
+
width: 100%; text-align: left;
|
|
302
|
+
font-weight: 500;
|
|
303
|
+
}
|
|
304
|
+
.nav-item:hover { background: var(--bg-elev-2); color: var(--text); }
|
|
305
|
+
.nav-item.active {
|
|
306
|
+
background: var(--brand-gradient-soft);
|
|
307
|
+
color: var(--text);
|
|
308
|
+
}
|
|
309
|
+
.nav-item.active::before {
|
|
310
|
+
content: "";
|
|
311
|
+
position: absolute; left: 0; top: 4px; bottom: 4px;
|
|
312
|
+
width: 3px; border-radius: 2px;
|
|
313
|
+
background: var(--brand-gradient);
|
|
314
|
+
}
|
|
315
|
+
.nav-item svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-3); transition: color var(--duration-fast); }
|
|
316
|
+
.nav-item:hover svg, .nav-item.active svg { color: var(--brand-cyan); }
|
|
317
|
+
.nav-item .badge-count {
|
|
318
|
+
margin-left: auto;
|
|
319
|
+
background: var(--bg-elev-3);
|
|
320
|
+
border-radius: 9px;
|
|
321
|
+
padding: 0px 7px;
|
|
322
|
+
font-size: 10px;
|
|
323
|
+
color: var(--text-3);
|
|
324
|
+
font-variant-numeric: tabular-nums;
|
|
325
|
+
}
|
|
326
|
+
.nav-item.active .badge-count { background: var(--brand-cyan); color: #000; }
|
|
327
|
+
|
|
328
|
+
main {
|
|
329
|
+
overflow-y: auto;
|
|
330
|
+
padding: 20px 26px 48px;
|
|
331
|
+
position: relative;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
335
|
+
/* Sections */
|
|
336
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
337
|
+
.section { display: none; }
|
|
338
|
+
.section.active { display: block; animation: sectionIn var(--duration-slow) var(--easing); }
|
|
339
|
+
@keyframes sectionIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
|
|
340
|
+
|
|
341
|
+
.section-header {
|
|
342
|
+
display: flex; align-items: flex-end; justify-content: space-between;
|
|
343
|
+
margin-bottom: 18px; flex-wrap: wrap; gap: 12px;
|
|
344
|
+
}
|
|
345
|
+
.section-title {
|
|
346
|
+
font-size: 22px; font-weight: 800; letter-spacing: -0.5px;
|
|
347
|
+
display: flex; align-items: center; gap: 10px;
|
|
348
|
+
}
|
|
349
|
+
.section-title svg { color: var(--brand-cyan); width: 22px; height: 22px; }
|
|
350
|
+
.section-subtitle {
|
|
351
|
+
color: var(--text-3); font-size: 12.5px; margin-top: 4px;
|
|
352
|
+
}
|
|
353
|
+
.section-actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
354
|
+
|
|
355
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
356
|
+
/* Cards */
|
|
357
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
358
|
+
.grid {
|
|
359
|
+
display: grid;
|
|
360
|
+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
361
|
+
gap: 14px;
|
|
362
|
+
}
|
|
363
|
+
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
|
|
364
|
+
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
|
|
365
|
+
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
|
|
366
|
+
|
|
367
|
+
.card {
|
|
368
|
+
background: var(--bg-glass);
|
|
369
|
+
backdrop-filter: blur(20px) saturate(150%);
|
|
370
|
+
-webkit-backdrop-filter: blur(20px) saturate(150%);
|
|
371
|
+
border: 1px solid var(--border);
|
|
372
|
+
border-radius: var(--radius-lg);
|
|
373
|
+
padding: 18px;
|
|
374
|
+
transition: all var(--duration) var(--easing);
|
|
375
|
+
position: relative;
|
|
376
|
+
overflow: hidden;
|
|
377
|
+
}
|
|
378
|
+
.card:hover { border-color: var(--border-strong); transform: translateY(-1px); box-shadow: var(--shadow); }
|
|
379
|
+
.card.span-2 { grid-column: span 2; }
|
|
380
|
+
.card.span-full { grid-column: 1 / -1; }
|
|
381
|
+
.card.accent::before {
|
|
382
|
+
content: "";
|
|
383
|
+
position: absolute; inset: 0; pointer-events: none;
|
|
384
|
+
background: linear-gradient(135deg, rgba(24,255,255,0.08), transparent 50%);
|
|
385
|
+
}
|
|
386
|
+
.card-header {
|
|
387
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
388
|
+
margin-bottom: 14px; gap: 8px;
|
|
389
|
+
}
|
|
390
|
+
.card-title {
|
|
391
|
+
font-size: 13px; font-weight: 700; letter-spacing: 0.01em;
|
|
392
|
+
display: flex; align-items: center; gap: 8px;
|
|
393
|
+
}
|
|
394
|
+
.card-title svg { width: 14px; height: 14px; color: var(--brand-cyan); }
|
|
395
|
+
|
|
396
|
+
.stat-card { padding: 18px; }
|
|
397
|
+
.stat-card .label { font-size: 11px; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
|
|
398
|
+
.stat-card .value { font-size: 32px; font-weight: 900; letter-spacing: -1px; margin: 6px 0 4px; font-variant-numeric: tabular-nums; }
|
|
399
|
+
.stat-card .value.accent { background: var(--brand-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
|
400
|
+
.stat-card .trend { font-size: 11px; color: var(--text-2); display: flex; align-items: center; gap: 4px; }
|
|
401
|
+
.stat-card .trend.up { color: var(--success); }
|
|
402
|
+
.stat-card .trend.down { color: var(--danger); }
|
|
403
|
+
|
|
404
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
405
|
+
/* Rows, pills, badges */
|
|
406
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
407
|
+
.row {
|
|
408
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
409
|
+
padding: 8px 0; border-bottom: 1px solid var(--border);
|
|
410
|
+
gap: 8px;
|
|
411
|
+
}
|
|
412
|
+
.row:last-child { border: none; }
|
|
413
|
+
.row .k { font-size: 12px; color: var(--text-2); }
|
|
414
|
+
.row .v { font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
415
|
+
.row .v.good { color: var(--success); }
|
|
416
|
+
.row .v.warn { color: var(--warning); }
|
|
417
|
+
.row .v.bad { color: var(--danger); }
|
|
418
|
+
|
|
419
|
+
.pill {
|
|
420
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
421
|
+
padding: 2px 8px; border-radius: 9px;
|
|
422
|
+
background: var(--bg-elev-3); border: 1px solid var(--border);
|
|
423
|
+
font-size: 10px; font-weight: 600; letter-spacing: 0.02em;
|
|
424
|
+
color: var(--text-2);
|
|
425
|
+
}
|
|
426
|
+
.pill.green { background: rgba(16,230,140,.12); color: var(--success); border-color: rgba(16,230,140,.3); }
|
|
427
|
+
.pill.red { background: rgba(255,88,100,.12); color: var(--danger); border-color: rgba(255,88,100,.3); }
|
|
428
|
+
.pill.yellow { background: rgba(255,181,63,.12); color: var(--warning); border-color: rgba(255,181,63,.3); }
|
|
429
|
+
.pill.purple { background: rgba(168,85,247,.15); color: var(--brand-purple); border-color: rgba(168,85,247,.3); }
|
|
430
|
+
.pill.cyan { background: rgba(24,255,255,.12); color: var(--brand-cyan); border-color: rgba(24,255,255,.3); }
|
|
431
|
+
.pill.pink { background: rgba(236,72,153,.12); color: var(--brand-pink); border-color: rgba(236,72,153,.3); }
|
|
432
|
+
|
|
433
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
434
|
+
/* Buttons */
|
|
435
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
436
|
+
.btn {
|
|
437
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
438
|
+
padding: 8px 14px;
|
|
439
|
+
background: var(--bg-elev-2);
|
|
440
|
+
border: 1px solid var(--border);
|
|
441
|
+
border-radius: 8px;
|
|
442
|
+
color: var(--text);
|
|
443
|
+
font-size: 12.5px;
|
|
444
|
+
font-weight: 600;
|
|
445
|
+
transition: all var(--duration) var(--easing);
|
|
446
|
+
white-space: nowrap;
|
|
447
|
+
}
|
|
448
|
+
.btn:hover { background: var(--bg-elev-3); border-color: var(--border-strong); transform: translateY(-1px); }
|
|
449
|
+
.btn:active { transform: translateY(0); }
|
|
450
|
+
.btn svg { width: 13px; height: 13px; }
|
|
451
|
+
.btn.sm { padding: 4px 10px; font-size: 11px; }
|
|
452
|
+
.btn.lg { padding: 11px 18px; font-size: 13px; }
|
|
453
|
+
.btn.primary {
|
|
454
|
+
background: var(--brand-gradient);
|
|
455
|
+
color: #fff;
|
|
456
|
+
border-color: transparent;
|
|
457
|
+
}
|
|
458
|
+
.btn.primary:hover { filter: brightness(1.08); box-shadow: 0 6px 20px rgba(24,255,255,0.25); }
|
|
459
|
+
.btn.ghost { background: transparent; }
|
|
460
|
+
.btn.ghost:hover { background: var(--bg-elev-2); }
|
|
461
|
+
.btn.danger { color: var(--danger); border-color: rgba(255,88,100,.35); }
|
|
462
|
+
.btn.danger:hover { background: rgba(255,88,100,.1); }
|
|
463
|
+
.btn.success { color: var(--success); border-color: rgba(16,230,140,.35); }
|
|
464
|
+
.btn.success:hover { background: rgba(16,230,140,.1); }
|
|
465
|
+
|
|
466
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
467
|
+
/* Forms */
|
|
468
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
469
|
+
.field { margin-bottom: 12px; }
|
|
470
|
+
.field label {
|
|
471
|
+
display: block; margin-bottom: 5px;
|
|
472
|
+
font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
|
|
473
|
+
color: var(--text-3);
|
|
474
|
+
}
|
|
475
|
+
.field input, .field textarea, .field select {
|
|
476
|
+
width: 100%;
|
|
477
|
+
background: var(--bg-elev-1);
|
|
478
|
+
border: 1px solid var(--border);
|
|
479
|
+
border-radius: 8px;
|
|
480
|
+
padding: 9px 12px;
|
|
481
|
+
font-size: 12.5px;
|
|
482
|
+
color: var(--text);
|
|
483
|
+
transition: all var(--duration) var(--easing);
|
|
484
|
+
}
|
|
485
|
+
.field input:focus, .field textarea:focus, .field select:focus {
|
|
486
|
+
outline: none;
|
|
487
|
+
border-color: var(--brand-cyan);
|
|
488
|
+
box-shadow: 0 0 0 3px rgba(24,255,255,0.12);
|
|
489
|
+
}
|
|
490
|
+
.field textarea { resize: vertical; min-height: 70px; font-family: var(--font-mono); font-size: 12px; }
|
|
491
|
+
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
|
492
|
+
|
|
493
|
+
/* Toggle switch */
|
|
494
|
+
.switch { position: relative; display: inline-block; width: 38px; height: 22px; }
|
|
495
|
+
.switch input { opacity: 0; width: 0; height: 0; }
|
|
496
|
+
.switch .slider {
|
|
497
|
+
position: absolute; inset: 0; cursor: pointer;
|
|
498
|
+
background: var(--bg-elev-3);
|
|
499
|
+
border: 1px solid var(--border);
|
|
500
|
+
transition: var(--duration); border-radius: 12px;
|
|
501
|
+
}
|
|
502
|
+
.switch .slider::before {
|
|
503
|
+
content: ""; position: absolute;
|
|
504
|
+
height: 16px; width: 16px; left: 2px; top: 2px;
|
|
505
|
+
background: var(--text-2); transition: var(--duration); border-radius: 50%;
|
|
506
|
+
}
|
|
507
|
+
.switch input:checked + .slider { background: var(--brand-gradient); border-color: transparent; }
|
|
508
|
+
.switch input:checked + .slider::before { transform: translateX(16px); background: #fff; }
|
|
509
|
+
|
|
510
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
511
|
+
/* Tables */
|
|
512
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
513
|
+
.table { width: 100%; border-collapse: collapse; }
|
|
514
|
+
.table th, .table td { padding: 9px 12px; text-align: left; font-size: 12px; }
|
|
515
|
+
.table th {
|
|
516
|
+
background: var(--bg-elev-2);
|
|
517
|
+
color: var(--text-3);
|
|
518
|
+
font-weight: 700; letter-spacing: 0.06em;
|
|
519
|
+
text-transform: uppercase; font-size: 10px;
|
|
520
|
+
border-bottom: 1px solid var(--border);
|
|
521
|
+
}
|
|
522
|
+
.table tr { border-bottom: 1px solid var(--border); transition: background var(--duration-fast); }
|
|
523
|
+
.table tr:hover { background: var(--bg-elev-2); }
|
|
524
|
+
.table code { color: var(--brand-cyan); font-size: 11px; }
|
|
525
|
+
|
|
526
|
+
.empty {
|
|
527
|
+
padding: 32px 16px; text-align: center;
|
|
528
|
+
color: var(--text-3); font-size: 12px;
|
|
529
|
+
}
|
|
530
|
+
.empty svg { width: 40px; height: 40px; color: var(--text-dim); margin: 0 auto 10px; display: block; }
|
|
531
|
+
|
|
532
|
+
/* Progress meters */
|
|
533
|
+
.meter { height: 6px; background: var(--bg-elev-3); border-radius: 4px; overflow: hidden; position: relative; }
|
|
534
|
+
.meter .fill {
|
|
535
|
+
height: 100%; border-radius: 4px;
|
|
536
|
+
background: var(--brand-gradient);
|
|
537
|
+
transition: width var(--duration-slow) var(--easing);
|
|
538
|
+
}
|
|
539
|
+
.meter .fill.good { background: var(--success); }
|
|
540
|
+
.meter .fill.warn { background: var(--warning); }
|
|
541
|
+
|
|
542
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
543
|
+
/* Feature toggles grid */
|
|
544
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
545
|
+
.feature-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
|
|
546
|
+
.feature-toggle {
|
|
547
|
+
background: var(--bg-elev-1);
|
|
548
|
+
border: 1px solid var(--border);
|
|
549
|
+
border-radius: 10px;
|
|
550
|
+
padding: 12px 14px;
|
|
551
|
+
display: flex; align-items: center; gap: 12px;
|
|
552
|
+
transition: all var(--duration) var(--easing);
|
|
553
|
+
}
|
|
554
|
+
.feature-toggle:hover { border-color: var(--border-strong); }
|
|
555
|
+
.feature-toggle .icon {
|
|
556
|
+
width: 32px; height: 32px;
|
|
557
|
+
background: var(--brand-gradient-soft);
|
|
558
|
+
border-radius: 8px;
|
|
559
|
+
display: flex; align-items: center; justify-content: center;
|
|
560
|
+
color: var(--brand-cyan); flex-shrink: 0;
|
|
561
|
+
}
|
|
562
|
+
.feature-toggle .icon svg { width: 16px; height: 16px; }
|
|
563
|
+
.feature-toggle .meta { flex: 1; min-width: 0; }
|
|
564
|
+
.feature-toggle .meta .name { font-size: 12.5px; font-weight: 600; }
|
|
565
|
+
.feature-toggle .meta .desc { font-size: 10.5px; color: var(--text-3); margin-top: 2px; }
|
|
566
|
+
|
|
567
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
568
|
+
/* Command palette */
|
|
569
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
570
|
+
.cmd-overlay {
|
|
571
|
+
position: fixed; inset: 0;
|
|
572
|
+
background: rgba(3, 5, 9, 0.72);
|
|
573
|
+
backdrop-filter: blur(8px);
|
|
574
|
+
z-index: 2000;
|
|
575
|
+
display: none; align-items: flex-start; justify-content: center;
|
|
576
|
+
padding-top: 12vh;
|
|
577
|
+
animation: fadeIn var(--duration) var(--easing);
|
|
578
|
+
}
|
|
579
|
+
.cmd-overlay.open { display: flex; }
|
|
580
|
+
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
581
|
+
|
|
582
|
+
.cmd-palette {
|
|
583
|
+
width: min(640px, 92vw);
|
|
584
|
+
background: var(--bg-glass-strong);
|
|
585
|
+
backdrop-filter: blur(36px);
|
|
586
|
+
border: 1px solid var(--border-strong);
|
|
587
|
+
border-radius: 14px;
|
|
588
|
+
overflow: hidden;
|
|
589
|
+
box-shadow: var(--shadow-lg);
|
|
590
|
+
animation: paletteIn var(--duration-slow) var(--easing-spring);
|
|
591
|
+
}
|
|
592
|
+
@keyframes paletteIn { from { opacity: 0; transform: translateY(-20px) scale(0.96); } to { opacity: 1; transform: none; } }
|
|
593
|
+
|
|
594
|
+
.cmd-palette input {
|
|
595
|
+
width: 100%;
|
|
596
|
+
background: transparent; border: none; outline: none;
|
|
597
|
+
padding: 16px 20px;
|
|
598
|
+
font-size: 14px; color: var(--text);
|
|
599
|
+
border-bottom: 1px solid var(--border);
|
|
600
|
+
}
|
|
601
|
+
.cmd-palette .results { max-height: 420px; overflow-y: auto; padding: 6px; }
|
|
602
|
+
.cmd-result {
|
|
603
|
+
display: flex; align-items: center; gap: 10px;
|
|
604
|
+
padding: 9px 12px; border-radius: 7px;
|
|
605
|
+
cursor: pointer; color: var(--text-2);
|
|
606
|
+
transition: all var(--duration-fast);
|
|
607
|
+
}
|
|
608
|
+
.cmd-result:hover, .cmd-result.selected { background: var(--bg-elev-2); color: var(--text); }
|
|
609
|
+
.cmd-result svg { width: 15px; height: 15px; color: var(--brand-cyan); flex-shrink: 0; }
|
|
610
|
+
.cmd-result .meta { flex: 1; font-size: 12.5px; }
|
|
611
|
+
.cmd-result .hint { font-size: 10px; color: var(--text-3); font-family: var(--font-mono); }
|
|
612
|
+
|
|
613
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
614
|
+
/* Toasts */
|
|
615
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
616
|
+
.toast-container {
|
|
617
|
+
position: fixed; bottom: 52px; right: 20px;
|
|
618
|
+
display: flex; flex-direction: column; gap: 8px;
|
|
619
|
+
z-index: 1500; pointer-events: none;
|
|
620
|
+
}
|
|
621
|
+
.toast {
|
|
622
|
+
min-width: 260px; max-width: 360px;
|
|
623
|
+
background: var(--bg-glass-strong);
|
|
624
|
+
backdrop-filter: blur(24px);
|
|
625
|
+
border: 1px solid var(--border-strong);
|
|
626
|
+
border-left: 3px solid var(--brand-cyan);
|
|
627
|
+
border-radius: 10px;
|
|
628
|
+
padding: 10px 14px;
|
|
629
|
+
pointer-events: auto;
|
|
630
|
+
display: flex; align-items: flex-start; gap: 10px;
|
|
631
|
+
animation: toastIn var(--duration-slow) var(--easing-spring);
|
|
632
|
+
box-shadow: var(--shadow-lg);
|
|
633
|
+
}
|
|
634
|
+
.toast.success { border-left-color: var(--success); }
|
|
635
|
+
.toast.warn { border-left-color: var(--warning); }
|
|
636
|
+
.toast.error { border-left-color: var(--danger); }
|
|
637
|
+
.toast.removing { animation: toastOut var(--duration-slow) var(--easing) forwards; }
|
|
638
|
+
@keyframes toastIn { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: none; } }
|
|
639
|
+
@keyframes toastOut { to { opacity: 0; transform: translateX(20px); } }
|
|
640
|
+
.toast svg { width: 15px; height: 15px; color: var(--brand-cyan); flex-shrink: 0; margin-top: 1px; }
|
|
641
|
+
.toast.success svg { color: var(--success); }
|
|
642
|
+
.toast.warn svg { color: var(--warning); }
|
|
643
|
+
.toast.error svg { color: var(--danger); }
|
|
644
|
+
.toast .title { font-size: 12.5px; font-weight: 600; margin-bottom: 2px; }
|
|
645
|
+
.toast .body { font-size: 11.5px; color: var(--text-2); line-height: 1.4; }
|
|
646
|
+
|
|
647
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
648
|
+
/* Ticker + log panel */
|
|
649
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
650
|
+
.log-ticker {
|
|
651
|
+
position: fixed; bottom: 0; left: 0; right: 0;
|
|
652
|
+
height: var(--ticker-h);
|
|
653
|
+
background: var(--bg-glass-strong);
|
|
654
|
+
backdrop-filter: blur(24px);
|
|
655
|
+
border-top: 1px solid var(--border);
|
|
656
|
+
display: flex; align-items: center; gap: 12px;
|
|
657
|
+
padding: 0 20px;
|
|
658
|
+
font-family: var(--font-mono); font-size: 10.5px;
|
|
659
|
+
color: var(--text-3);
|
|
660
|
+
z-index: 600;
|
|
661
|
+
}
|
|
662
|
+
.log-ticker .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--success); animation: pulse 1.5s infinite; flex-shrink: 0; }
|
|
663
|
+
.log-ticker .scroll { flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
664
|
+
.log-panel {
|
|
665
|
+
position: fixed; bottom: var(--ticker-h); left: 0; right: 0;
|
|
666
|
+
height: 300px;
|
|
667
|
+
background: var(--bg-elev-1);
|
|
668
|
+
border-top: 1px solid var(--border);
|
|
669
|
+
padding: 16px 20px;
|
|
670
|
+
overflow-y: auto;
|
|
671
|
+
font-family: var(--font-mono); font-size: 11.5px;
|
|
672
|
+
color: var(--text-2);
|
|
673
|
+
z-index: 599;
|
|
674
|
+
transform: translateY(100%);
|
|
675
|
+
transition: transform var(--duration-slow) var(--easing);
|
|
676
|
+
box-shadow: 0 -8px 32px rgba(0,0,0,0.3);
|
|
677
|
+
}
|
|
678
|
+
.log-panel.open { transform: translateY(0); }
|
|
679
|
+
.log-panel .head {
|
|
680
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
681
|
+
margin-bottom: 10px;
|
|
682
|
+
}
|
|
683
|
+
.log-panel .head .title { color: var(--brand-cyan); font-weight: 700; letter-spacing: 0.04em; }
|
|
684
|
+
.log-row {
|
|
685
|
+
padding: 4px 0;
|
|
686
|
+
border-bottom: 1px solid var(--border);
|
|
687
|
+
display: grid; grid-template-columns: 90px 130px 1fr; gap: 10px; align-items: baseline;
|
|
688
|
+
}
|
|
689
|
+
.log-row .t { color: var(--text-dim); font-size: 10px; }
|
|
690
|
+
.log-row .src { color: var(--brand-purple); font-weight: 600; font-size: 10.5px; }
|
|
691
|
+
.log-row .type { color: var(--brand-cyan); font-weight: 600; font-size: 10.5px; }
|
|
692
|
+
|
|
693
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
694
|
+
/* Responsive */
|
|
695
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
696
|
+
.menu-toggle { display: none; }
|
|
697
|
+
|
|
698
|
+
@media (max-width: 1024px) {
|
|
699
|
+
.grid.cols-3, .grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
|
|
700
|
+
.card.span-2 { grid-column: 1 / -1; }
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
@media (max-width: 768px) {
|
|
704
|
+
header { padding: 0 14px; gap: 10px; }
|
|
705
|
+
.header-stats { display: none; }
|
|
706
|
+
.brand .state { display: none; }
|
|
707
|
+
.menu-toggle { display: flex; }
|
|
708
|
+
aside.sidebar {
|
|
709
|
+
position: fixed;
|
|
710
|
+
top: var(--header-h); bottom: 0;
|
|
711
|
+
width: 280px; z-index: 90;
|
|
712
|
+
transform: translateX(-100%);
|
|
713
|
+
transition: transform var(--duration-slow) var(--easing);
|
|
714
|
+
}
|
|
715
|
+
aside.sidebar.open { transform: translateX(0); box-shadow: var(--shadow-lg); }
|
|
716
|
+
.layout { grid-template-columns: 1fr; }
|
|
717
|
+
main { padding: 14px; padding-bottom: 60px; }
|
|
718
|
+
.grid, .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; }
|
|
719
|
+
.card.span-2 { grid-column: auto; }
|
|
720
|
+
.section-title { font-size: 18px; }
|
|
721
|
+
.field-row { grid-template-columns: 1fr; }
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/* Chart canvas wrapper */
|
|
725
|
+
.chart-wrap { position: relative; height: 220px; }
|
|
726
|
+
.chart-wrap.lg { height: 320px; }
|
|
727
|
+
|
|
728
|
+
/* Live Graph canvas */
|
|
729
|
+
.graph-wrap {
|
|
730
|
+
position: relative;
|
|
731
|
+
background: radial-gradient(ellipse at center, rgba(168,85,247,0.04) 0%, transparent 70%);
|
|
732
|
+
border-radius: 12px; overflow: hidden;
|
|
733
|
+
border: 1px solid var(--border);
|
|
734
|
+
}
|
|
735
|
+
.graph-wrap canvas { display: block; width: 100%; height: 560px; }
|
|
736
|
+
|
|
737
|
+
/* Helpers */
|
|
738
|
+
.mono { font-family: var(--font-mono); font-size: 11.5px; }
|
|
739
|
+
.tag-code { font-family: var(--font-mono); background: var(--bg-elev-2); padding: 1px 6px; border-radius: 4px; font-size: 11px; }
|
|
740
|
+
.flex { display: flex; }
|
|
741
|
+
.flex.center { align-items: center; }
|
|
742
|
+
.flex.gap { gap: 8px; }
|
|
743
|
+
.flex.between { justify-content: space-between; }
|
|
744
|
+
.grow { flex: 1; }
|
|
745
|
+
.hidden { display: none; }
|
|
746
|
+
.mt { margin-top: 10px; } .mt-lg { margin-top: 20px; }
|
|
747
|
+
.mb { margin-bottom: 10px; } .mb-lg { margin-bottom: 20px; }
|
|
748
|
+
|
|
749
|
+
/* Accent bar */
|
|
750
|
+
.accent-bar {
|
|
751
|
+
height: 3px; border-radius: 2px;
|
|
752
|
+
background: var(--brand-gradient);
|
|
753
|
+
margin-bottom: 18px;
|
|
754
|
+
box-shadow: 0 0 12px rgba(24,255,255,0.35);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/* Skeleton loader */
|
|
758
|
+
.skel { background: linear-gradient(90deg, var(--bg-elev-2) 0%, var(--bg-elev-3) 50%, var(--bg-elev-2) 100%); background-size: 200% 100%; animation: skel 1.2s linear infinite; border-radius: 6px; }
|
|
759
|
+
@keyframes skel { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
760
|
+
</style>
|
|
761
|
+
</head>
|
|
762
|
+
<body>
|
|
763
|
+
|
|
764
|
+
<!-- ══════════════════════════════════════════════════════════════════════
|
|
765
|
+
Header
|
|
766
|
+
══════════════════════════════════════════════════════════════════════ -->
|
|
767
|
+
<header>
|
|
768
|
+
<button class="icon-btn menu-toggle" onclick="toggleSidebar()" aria-label="Menu">
|
|
769
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
|
|
770
|
+
</button>
|
|
771
|
+
|
|
772
|
+
<div class="brain-hero" title="Hive Mind — learning · reflecting · charging">
|
|
773
|
+
<div class="aura"></div>
|
|
774
|
+
<svg viewBox="0 0 100 100">
|
|
775
|
+
<circle class="hemi charge" cx="50" cy="50" r="46"/>
|
|
776
|
+
<circle class="hemi charge" cx="50" cy="50" r="40" style="animation-duration:3s;animation-direction:reverse"/>
|
|
777
|
+
<path class="hemi" d="M 48 22 C 30 22, 18 32, 18 48 C 18 60, 26 72, 38 78 C 42 82, 46 80, 48 76 L 48 22"/>
|
|
778
|
+
<path class="hemi hemi-r" d="M 52 22 C 70 22, 82 32, 82 48 C 82 60, 74 72, 62 78 C 58 82, 54 80, 52 76 L 52 22"/>
|
|
779
|
+
<path class="hemi" d="M 30 40 Q 38 38, 44 44"/>
|
|
780
|
+
<path class="hemi" d="M 28 55 Q 36 55, 44 58"/>
|
|
781
|
+
<path class="hemi" d="M 32 68 Q 40 66, 46 72"/>
|
|
782
|
+
<path class="hemi hemi-r" d="M 70 40 Q 62 38, 56 44"/>
|
|
783
|
+
<path class="hemi hemi-r" d="M 72 55 Q 64 55, 56 58"/>
|
|
784
|
+
<path class="hemi hemi-r" d="M 68 68 Q 60 66, 54 72"/>
|
|
785
|
+
<circle class="synapse" cx="30" cy="42" r="1.8" fill="#18ffff"/>
|
|
786
|
+
<circle class="synapse" cx="70" cy="42" r="1.8" fill="#a855f7" style="animation-delay:0.3s"/>
|
|
787
|
+
<circle class="synapse" cx="35" cy="62" r="1.6" fill="#10e68c" style="animation-delay:0.6s"/>
|
|
788
|
+
<circle class="synapse" cx="65" cy="62" r="1.6" fill="#ffb53f" style="animation-delay:0.9s"/>
|
|
789
|
+
<circle class="synapse" cx="50" cy="35" r="1.4" fill="#ec4899" style="animation-delay:0.15s"/>
|
|
790
|
+
<circle class="synapse" cx="50" cy="70" r="1.4" fill="#18ffff" style="animation-delay:0.75s"/>
|
|
791
|
+
</svg>
|
|
792
|
+
</div>
|
|
793
|
+
|
|
794
|
+
<div class="brand">
|
|
795
|
+
<div class="name">Shadow Brain <span style="color:var(--text-3);font-size:10px;font-weight:600">v6.0 Hive Mind</span></div>
|
|
796
|
+
<div class="state" id="brain-state">learning · reflecting · charging</div>
|
|
797
|
+
</div>
|
|
798
|
+
|
|
799
|
+
<div class="header-stats" id="header-stats">
|
|
800
|
+
<div class="stat-pill"><span class="n" id="stat-agents">—</span> agents</div>
|
|
801
|
+
<div class="stat-pill"><span class="n" id="stat-memories">—</span> memories</div>
|
|
802
|
+
<div class="stat-pill"><span class="n" id="stat-dreams">—</span> dreams</div>
|
|
803
|
+
<div class="stat-pill"><span class="n" id="stat-savings">$—</span> saved</div>
|
|
804
|
+
</div>
|
|
805
|
+
|
|
806
|
+
<div class="header-right">
|
|
807
|
+
<button class="kbd-hint" onclick="openCmdPalette()" title="Open command palette">
|
|
808
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
|
809
|
+
Search <kbd>⌘K</kbd>
|
|
810
|
+
</button>
|
|
811
|
+
<button class="icon-btn" onclick="toggleTheme()" title="Toggle theme">
|
|
812
|
+
<svg id="theme-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
|
|
813
|
+
</button>
|
|
814
|
+
<button class="icon-btn" onclick="toggleLogPanel()" title="Activity log"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="3" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="21" y1="18" x2="3" y2="18"/></svg></button>
|
|
815
|
+
<button class="btn danger sm" onclick="stopBrain()" title="Stop Shadow Brain (npm process)"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="1"/></svg>Stop</button>
|
|
816
|
+
<button class="btn primary sm" onclick="refresh()" title="Refresh all"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>Refresh</button>
|
|
817
|
+
</div>
|
|
818
|
+
</header>
|
|
819
|
+
|
|
820
|
+
<!-- ══════════════════════════════════════════════════════════════════════
|
|
821
|
+
Layout: sidebar + main
|
|
822
|
+
══════════════════════════════════════════════════════════════════════ -->
|
|
823
|
+
<div class="layout">
|
|
824
|
+
<aside class="sidebar" id="sidebar">
|
|
825
|
+
<div class="nav-group">
|
|
826
|
+
<div class="nav-group-label">Overview</div>
|
|
827
|
+
<button class="nav-item active" data-tab="overview" data-title="Overview" data-icon="activity">
|
|
828
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
|
|
829
|
+
Dashboard
|
|
830
|
+
</button>
|
|
831
|
+
<button class="nav-item" data-tab="graph" data-title="Live Graph">
|
|
832
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="8"/></svg>
|
|
833
|
+
Live Graph
|
|
834
|
+
</button>
|
|
835
|
+
<button class="nav-item" data-tab="agents" data-title="Agents">
|
|
836
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
|
837
|
+
Agents<span class="badge-count" id="badge-agents">0</span>
|
|
838
|
+
</button>
|
|
839
|
+
<button class="nav-item" data-tab="chat" data-title="Chat with Brain"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>Chat with Brain</button>
|
|
840
|
+
<button class="nav-item" data-tab="memory" data-title="Memory Browser">
|
|
841
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>
|
|
842
|
+
Memory Browser
|
|
843
|
+
</button>
|
|
844
|
+
<button class="nav-item" data-tab="activity" data-title="Activity Log">
|
|
845
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
|
846
|
+
Activity Log
|
|
847
|
+
</button>
|
|
848
|
+
</div>
|
|
849
|
+
|
|
850
|
+
<div class="nav-group">
|
|
851
|
+
<div class="nav-group-label">Cognition</div>
|
|
852
|
+
<button class="nav-item" data-tab="sabb" data-title="Sub-Agent Bridge"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>SABB<span class="badge-count" id="badge-sabb">0</span></button>
|
|
853
|
+
<button class="nav-item" data-tab="causal" data-title="Causal Chains"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>Causal<span class="badge-count" id="badge-causal">0</span></button>
|
|
854
|
+
<button class="nav-item" data-tab="dream" data-title="Dream Engine"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>Dream<span class="badge-count" id="badge-dream">0</span></button>
|
|
855
|
+
<button class="nav-item" data-tab="debate" data-title="Swarm Debate"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>Swarm Debate</button>
|
|
856
|
+
<button class="nav-item" data-tab="premortem" data-title="Pre-Mortem"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>Pre-Mortem</button>
|
|
857
|
+
<button class="nav-item" data-tab="branch" data-title="Branch Brains"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>Branch Brains</button>
|
|
858
|
+
<button class="nav-item" data-tab="forget" data-title="Forgetting Curve"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>Forgetting</button>
|
|
859
|
+
</div>
|
|
860
|
+
|
|
861
|
+
<div class="nav-group">
|
|
862
|
+
<div class="nav-group-label">Safety & Trust</div>
|
|
863
|
+
<button class="nav-item" data-tab="collision" data-title="Collision Detective"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>Collisions<span class="badge-count" id="badge-collision">0</span></button>
|
|
864
|
+
<button class="nav-item" data-tab="reputation" data-title="Reputation Ledger"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="7"/><polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"/></svg>Reputation</button>
|
|
865
|
+
<button class="nav-item" data-tab="calibration" data-title="Calibration"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="20" x2="12" y2="10"/><line x1="18" y1="20" x2="18" y2="4"/><line x1="6" y1="20" x2="6" y2="16"/></svg>Calibration</button>
|
|
866
|
+
<button class="nav-item" data-tab="formal" data-title="Formal Rules"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>Formal Rules</button>
|
|
867
|
+
<button class="nav-item" data-tab="privacy" data-title="Privacy & Safety"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>Privacy & Safety</button>
|
|
868
|
+
<button class="nav-item" data-tab="attention" data-title="Attention Heatmap"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="4"/><line x1="21.17" y1="8" x2="12" y2="8"/></svg>Attention</button>
|
|
869
|
+
</div>
|
|
870
|
+
|
|
871
|
+
<div class="nav-group">
|
|
872
|
+
<div class="nav-group-label">Economy & Growth</div>
|
|
873
|
+
<button class="nav-item" data-tab="tokens" data-title="Token Economy"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>Tokens</button>
|
|
874
|
+
<button class="nav-item" data-tab="voice" data-title="Voice Mode"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/></svg>Voice</button>
|
|
875
|
+
<button class="nav-item" data-tab="garden" data-title="Brain Garden"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s-8-4-8-12c0-4 3-7 8-7s8 3 8 7c0 8-8 12-8 12z"/><path d="M12 17v-5M12 7V5"/></svg>Garden</button>
|
|
876
|
+
<button class="nav-item" data-tab="pr" data-title="PR Auto-Review"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/><line x1="6" y1="9" x2="6" y2="21"/></svg>PR Review</button>
|
|
877
|
+
<button class="nav-item" data-tab="team" data-title="Team Sync"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>Team Sync</button>
|
|
878
|
+
<button class="nav-item" data-tab="exchange" data-title="Brain Exchange"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="4" y1="4" x2="9" y2="9"/></svg>Exchange</button>
|
|
879
|
+
</div>
|
|
880
|
+
|
|
881
|
+
<div class="nav-group">
|
|
882
|
+
<div class="nav-group-label">System</div>
|
|
883
|
+
<button class="nav-item" data-tab="features" data-title="Feature Toggles"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="1" y="5" width="22" height="14" rx="7"/><circle cx="16" cy="12" r="3"/></svg>Feature Toggles</button>
|
|
884
|
+
<button class="nav-item" data-tab="models" data-title="Models & Intelligence"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>Models & Intel</button>
|
|
885
|
+
<button class="nav-item" data-tab="config" data-title="Configuration"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>Configuration</button>
|
|
886
|
+
</div>
|
|
887
|
+
</aside>
|
|
888
|
+
|
|
889
|
+
<main id="main">
|
|
890
|
+
|
|
891
|
+
<!-- ════ OVERVIEW ════════════════════════════════════════════════ -->
|
|
892
|
+
<section class="section active" id="tab-overview">
|
|
893
|
+
<div class="accent-bar"></div>
|
|
894
|
+
<div class="section-header">
|
|
895
|
+
<div>
|
|
896
|
+
<h1 class="section-title"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>Hive Mind Overview</h1>
|
|
897
|
+
<div class="section-subtitle">Real-time cross-agent intelligence · 22 modules · local-first</div>
|
|
898
|
+
</div>
|
|
899
|
+
<div class="section-actions">
|
|
900
|
+
<button class="btn" onclick="runDream()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>Dream Now</button>
|
|
901
|
+
<button class="btn" onclick="runForget()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>Consolidate</button>
|
|
902
|
+
<button class="btn danger" onclick="toggleAirGap()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>Air-Gap</button>
|
|
903
|
+
</div>
|
|
904
|
+
</div>
|
|
905
|
+
|
|
906
|
+
<div class="grid cols-4">
|
|
907
|
+
<div class="card stat-card accent">
|
|
908
|
+
<div class="label">Agents Connected</div>
|
|
909
|
+
<div class="value accent" id="ov-agents">—</div>
|
|
910
|
+
<div class="trend">of 10 supported</div>
|
|
911
|
+
</div>
|
|
912
|
+
<div class="card stat-card accent">
|
|
913
|
+
<div class="label">Memories Stored</div>
|
|
914
|
+
<div class="value" id="ov-memories">—</div>
|
|
915
|
+
<div class="trend" id="ov-memories-sub">across all projects</div>
|
|
916
|
+
</div>
|
|
917
|
+
<div class="card stat-card accent">
|
|
918
|
+
<div class="label">Sub-Agents (SABB)</div>
|
|
919
|
+
<div class="value" id="ov-subagents">—</div>
|
|
920
|
+
<div class="trend" id="ov-subagents-sub">graduated / quarantined</div>
|
|
921
|
+
</div>
|
|
922
|
+
<div class="card stat-card accent">
|
|
923
|
+
<div class="label">Monthly Savings</div>
|
|
924
|
+
<div class="value" style="color:var(--success)" id="ov-savings">$—</div>
|
|
925
|
+
<div class="trend up" id="ov-savings-sub">projection</div>
|
|
926
|
+
</div>
|
|
927
|
+
</div>
|
|
928
|
+
|
|
929
|
+
<div class="grid cols-3 mt-lg">
|
|
930
|
+
<div class="card span-2">
|
|
931
|
+
<div class="card-header"><div class="card-title"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="20" x2="12" y2="10"/><line x1="18" y1="20" x2="18" y2="4"/><line x1="6" y1="20" x2="6" y2="16"/></svg>Token Spend (last 7 days)</div></div>
|
|
932
|
+
<div class="chart-wrap"><canvas id="chart-tokens"></canvas></div>
|
|
933
|
+
</div>
|
|
934
|
+
<div class="card">
|
|
935
|
+
<div class="card-header"><div class="card-title"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>Recent Activity</div></div>
|
|
936
|
+
<div id="ov-timeline" style="max-height:220px;overflow-y:auto;font-size:11.5px"></div>
|
|
937
|
+
</div>
|
|
938
|
+
</div>
|
|
939
|
+
|
|
940
|
+
<div class="grid cols-2 mt-lg">
|
|
941
|
+
<div class="card">
|
|
942
|
+
<div class="card-header"><div class="card-title">Module Status</div></div>
|
|
943
|
+
<div id="ov-modules"></div>
|
|
944
|
+
</div>
|
|
945
|
+
<div class="card">
|
|
946
|
+
<div class="card-header"><div class="card-title">System Health</div></div>
|
|
947
|
+
<div id="ov-health"></div>
|
|
948
|
+
</div>
|
|
949
|
+
</div>
|
|
950
|
+
</section>
|
|
951
|
+
|
|
952
|
+
<!-- ════ LIVE GRAPH ══════════════════════════════════════════════ -->
|
|
953
|
+
<section class="section" id="tab-graph">
|
|
954
|
+
<div class="accent-bar"></div>
|
|
955
|
+
<div class="section-header">
|
|
956
|
+
<div>
|
|
957
|
+
<h1 class="section-title">Live Agent Network</h1>
|
|
958
|
+
<div class="section-subtitle">Real-time signal flow: brain ↔ agents ↔ sub-agents</div>
|
|
959
|
+
</div>
|
|
960
|
+
</div>
|
|
961
|
+
<div class="card" style="padding:0">
|
|
962
|
+
<div class="graph-wrap"><canvas id="graph-canvas" width="1600" height="600"></canvas></div>
|
|
963
|
+
<div style="padding:10px 14px;display:flex;gap:16px;flex-wrap:wrap;font-size:11px;color:var(--text-2)">
|
|
964
|
+
<span><span style="color:var(--success)">●</span> memory-write</span>
|
|
965
|
+
<span><span style="color:var(--brand-cyan)">●</span> subagent-spawn</span>
|
|
966
|
+
<span><span style="color:var(--brand-purple)">●</span> dream</span>
|
|
967
|
+
<span><span style="color:var(--danger)">●</span> collision</span>
|
|
968
|
+
<span><span style="color:var(--warning)">●</span> firewall-block</span>
|
|
969
|
+
<span id="graph-meta" style="margin-left:auto;color:var(--text-3)"></span>
|
|
970
|
+
</div>
|
|
971
|
+
</div>
|
|
972
|
+
</section>
|
|
973
|
+
|
|
974
|
+
<!-- ════ AGENTS ══════════════════════════════════════════════════ -->
|
|
975
|
+
<section class="section" id="tab-agents">
|
|
976
|
+
<div class="accent-bar"></div>
|
|
977
|
+
<div class="section-header">
|
|
978
|
+
<div>
|
|
979
|
+
<h1 class="section-title">Agent Connections</h1>
|
|
980
|
+
<div class="section-subtitle">Connect or disconnect Shadow Brain hooks per agent · per-agent native mechanisms</div>
|
|
981
|
+
</div>
|
|
982
|
+
<div class="section-actions">
|
|
983
|
+
<button class="btn success" onclick="attachAll()">Attach All</button>
|
|
984
|
+
<button class="btn danger" onclick="detachAll()">Detach All</button>
|
|
985
|
+
</div>
|
|
986
|
+
</div>
|
|
987
|
+
<div class="card" style="padding:0">
|
|
988
|
+
<table class="table">
|
|
989
|
+
<thead><tr><th>Agent</th><th>Detected</th><th>Hook Status</th><th>Install Path</th><th style="text-align:right">Action</th></tr></thead>
|
|
990
|
+
<tbody id="agents-table"></tbody>
|
|
991
|
+
</table>
|
|
992
|
+
</div>
|
|
993
|
+
</section>
|
|
994
|
+
|
|
995
|
+
<!-- ════ CHAT WITH BRAIN ═════════════════════════════════════════ -->
|
|
996
|
+
<section class="section" id="tab-chat">
|
|
997
|
+
<div class="accent-bar"></div>
|
|
998
|
+
<div class="section-header">
|
|
999
|
+
<div>
|
|
1000
|
+
<h1 class="section-title"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>Chat with Your Brain</h1>
|
|
1001
|
+
<div class="section-subtitle">Ask anything — Shadow Brain retrieves relevant memories via semantic search and cites them. Uses your configured lead model.</div>
|
|
1002
|
+
</div>
|
|
1003
|
+
<div class="section-actions">
|
|
1004
|
+
<button class="btn" onclick="newChat()">New Chat</button>
|
|
1005
|
+
<button class="btn danger" onclick="clearAllChats()">Clear All</button>
|
|
1006
|
+
</div>
|
|
1007
|
+
</div>
|
|
1008
|
+
<div class="grid" style="grid-template-columns: 260px 1fr; gap: 14px">
|
|
1009
|
+
<div class="card" style="height: calc(100vh - 220px); overflow-y: auto; padding: 12px">
|
|
1010
|
+
<div class="card-title" style="margin-bottom: 10px">Conversations</div>
|
|
1011
|
+
<div id="chat-conversations"></div>
|
|
1012
|
+
</div>
|
|
1013
|
+
<div class="card" style="height: calc(100vh - 220px); display: flex; flex-direction: column; padding: 0">
|
|
1014
|
+
<div id="chat-messages" style="flex: 1; overflow-y: auto; padding: 16px"></div>
|
|
1015
|
+
<div style="border-top: 1px solid var(--border); padding: 12px; display: flex; gap: 8px">
|
|
1016
|
+
<input id="chat-input" placeholder="Ask your brain — e.g. 'What did Cursor decide about bcrypt?'" style="flex:1;background:var(--bg-elev-1);border:1px solid var(--border);border-radius:8px;padding:10px 14px;color:var(--text);font-size:13px" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendChat()}"/>
|
|
1017
|
+
<button class="btn primary" onclick="sendChat()" id="chat-send-btn">Send</button>
|
|
1018
|
+
</div>
|
|
1019
|
+
<div class="mono" style="padding: 4px 16px 10px; font-size: 10px; color: var(--text-3)" id="chat-status">idle</div>
|
|
1020
|
+
</div>
|
|
1021
|
+
</div>
|
|
1022
|
+
</section>
|
|
1023
|
+
|
|
1024
|
+
<!-- ════ MEMORY BROWSER ══════════════════════════════════════════ -->
|
|
1025
|
+
<section class="section" id="tab-memory">
|
|
1026
|
+
<div class="accent-bar"></div>
|
|
1027
|
+
<div class="section-header">
|
|
1028
|
+
<div>
|
|
1029
|
+
<h1 class="section-title">Memory Browser</h1>
|
|
1030
|
+
<div class="section-subtitle">Full-text search across every memory in the global brain</div>
|
|
1031
|
+
</div>
|
|
1032
|
+
</div>
|
|
1033
|
+
<div class="card">
|
|
1034
|
+
<div class="grid cols-4">
|
|
1035
|
+
<div class="field"><label>Search query</label><input id="mem-query" placeholder="e.g. auth, react, bcrypt…"/></div>
|
|
1036
|
+
<div class="field"><label>Agent</label><select id="mem-agent"><option value="">all agents</option><option>claude-code</option><option>cursor</option><option>codex</option><option>cline</option><option>copilot</option><option>windsurf</option><option>kilo-code</option><option>roo-code</option><option>aider</option><option>opencode</option></select></div>
|
|
1037
|
+
<div class="field"><label>Category</label><input id="mem-category" placeholder="e.g. security"/></div>
|
|
1038
|
+
<div class="field"><label>Min importance</label><input id="mem-importance" type="number" min="0" max="1" step="0.1" value="0"/></div>
|
|
1039
|
+
</div>
|
|
1040
|
+
<button class="btn primary" onclick="searchMemory()">Search</button>
|
|
1041
|
+
</div>
|
|
1042
|
+
<div class="card mt">
|
|
1043
|
+
<table class="table"><thead><tr><th>ID</th><th>Agent</th><th>Category</th><th>Content</th><th>Importance</th><th>Created</th></tr></thead>
|
|
1044
|
+
<tbody id="mem-results"></tbody>
|
|
1045
|
+
</table>
|
|
1046
|
+
</div>
|
|
1047
|
+
</section>
|
|
1048
|
+
|
|
1049
|
+
<!-- ════ ACTIVITY LOG ════════════════════════════════════════════ -->
|
|
1050
|
+
<section class="section" id="tab-activity">
|
|
1051
|
+
<div class="accent-bar"></div>
|
|
1052
|
+
<div class="section-header">
|
|
1053
|
+
<div>
|
|
1054
|
+
<h1 class="section-title">Activity Log</h1>
|
|
1055
|
+
<div class="section-subtitle">Unified stream: memory writes, SABB spawns, collisions, dreams, quarantine, firewall blocks</div>
|
|
1056
|
+
</div>
|
|
1057
|
+
<div class="section-actions">
|
|
1058
|
+
<select id="act-filter" style="background:var(--bg-elev-2);border:1px solid var(--border);border-radius:8px;padding:7px 10px;color:var(--text);font-size:12px">
|
|
1059
|
+
<option value="">all types</option>
|
|
1060
|
+
<option value="memory-write">memory-write</option>
|
|
1061
|
+
<option value="subagent-spawn">subagent-spawn</option>
|
|
1062
|
+
<option value="collision">collision</option>
|
|
1063
|
+
<option value="dream">dream</option>
|
|
1064
|
+
<option value="quarantine">quarantine</option>
|
|
1065
|
+
</select>
|
|
1066
|
+
<button class="btn" onclick="refreshActivity()">Refresh</button>
|
|
1067
|
+
</div>
|
|
1068
|
+
</div>
|
|
1069
|
+
<div class="card" style="padding:0">
|
|
1070
|
+
<table class="table"><thead><tr><th>Time</th><th>Source</th><th>Type</th><th>Detail</th></tr></thead><tbody id="activity-table"></tbody></table>
|
|
1071
|
+
</div>
|
|
1072
|
+
</section>
|
|
1073
|
+
|
|
1074
|
+
<!-- ════ SABB ════════════════════════════════════════════════════ -->
|
|
1075
|
+
<section class="section" id="tab-sabb">
|
|
1076
|
+
<div class="accent-bar"></div>
|
|
1077
|
+
<div class="section-header">
|
|
1078
|
+
<div>
|
|
1079
|
+
<h1 class="section-title">Sub-Agent Brain Bridge</h1>
|
|
1080
|
+
<div class="section-subtitle">Context slivers + quarantine for Claude Task / Cursor Composer / CrewAI / LangGraph / AutoGen</div>
|
|
1081
|
+
</div>
|
|
1082
|
+
</div>
|
|
1083
|
+
<div class="grid cols-2">
|
|
1084
|
+
<div class="card">
|
|
1085
|
+
<div class="card-header"><div class="card-title">SABB Stats</div></div>
|
|
1086
|
+
<div id="sabb-stats"></div>
|
|
1087
|
+
</div>
|
|
1088
|
+
<div class="card">
|
|
1089
|
+
<div class="card-header"><div class="card-title">Compute Context Sliver</div></div>
|
|
1090
|
+
<div class="field"><label>Parent agent</label><select id="sabb-parent"><option>claude-code</option><option>cursor</option><option>cline</option><option>codex</option></select></div>
|
|
1091
|
+
<div class="field"><label>Framework</label><select id="sabb-framework"><option>claude-code-task</option><option>cursor-composer</option><option>cline-substep</option><option>crewai</option><option>langgraph</option><option>autogen</option><option>generic</option></select></div>
|
|
1092
|
+
<div class="field"><label>Task description</label><input id="sabb-task" placeholder="e.g. refactor auth middleware"/></div>
|
|
1093
|
+
<div class="field-row">
|
|
1094
|
+
<div class="field"><label>Token budget</label><input id="sabb-budget" value="300"/></div>
|
|
1095
|
+
<div class="field"><label> </label><button class="btn primary" onclick="computeSliver()" style="width:100%">Generate</button></div>
|
|
1096
|
+
</div>
|
|
1097
|
+
<pre class="mono" id="sabb-sliver-output" style="margin-top:10px;max-height:220px;overflow:auto;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1098
|
+
</div>
|
|
1099
|
+
</div>
|
|
1100
|
+
<div class="card mt-lg" style="padding:0">
|
|
1101
|
+
<div style="padding:14px 18px 6px"><div class="card-title">Quarantined Memories</div></div>
|
|
1102
|
+
<table class="table"><thead><tr><th>ID</th><th>Sub-Agent</th><th>Category</th><th>Conf.</th><th>Content</th><th style="text-align:right">Action</th></tr></thead><tbody id="sabb-quarantine"></tbody></table>
|
|
1103
|
+
</div>
|
|
1104
|
+
</section>
|
|
1105
|
+
|
|
1106
|
+
<!-- ════ CAUSAL CHAINS ═══════════════════════════════════════════ -->
|
|
1107
|
+
<section class="section" id="tab-causal">
|
|
1108
|
+
<div class="accent-bar"></div>
|
|
1109
|
+
<div class="section-header">
|
|
1110
|
+
<div><h1 class="section-title">Causal Memory Chains</h1><div class="section-subtitle">Trace the full causal history of any decision</div></div>
|
|
1111
|
+
</div>
|
|
1112
|
+
<div class="grid cols-3">
|
|
1113
|
+
<div class="card">
|
|
1114
|
+
<div class="card-header"><div class="card-title">Stats</div></div>
|
|
1115
|
+
<div id="causal-stats"></div>
|
|
1116
|
+
</div>
|
|
1117
|
+
<div class="card span-2">
|
|
1118
|
+
<div class="card-header"><div class="card-title">Trace Ancestors / Descendants</div></div>
|
|
1119
|
+
<div class="field"><label>Memory ID</label><input id="causal-mem-id" placeholder="abc123…"/></div>
|
|
1120
|
+
<div class="field-row">
|
|
1121
|
+
<div class="field"><label>Max depth</label><input id="causal-depth" value="8"/></div>
|
|
1122
|
+
<div class="field"><label> </label><div style="display:flex;gap:6px"><button class="btn primary" onclick="traceCausal()" style="flex:1">Ancestors</button><button class="btn" onclick="influenceCausal()" style="flex:1">Descendants</button></div></div>
|
|
1123
|
+
</div>
|
|
1124
|
+
<pre class="mono" id="causal-output" style="margin-top:10px;max-height:280px;overflow:auto;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1125
|
+
</div>
|
|
1126
|
+
</div>
|
|
1127
|
+
<div class="card mt-lg">
|
|
1128
|
+
<div class="card-header"><div class="card-title">Record a Causal Link</div></div>
|
|
1129
|
+
<div class="grid cols-3">
|
|
1130
|
+
<div class="field"><label>Effect ID</label><input id="causal-effect"/></div>
|
|
1131
|
+
<div class="field"><label>Cause ID</label><input id="causal-cause"/></div>
|
|
1132
|
+
<div class="field"><label>Rationale (optional)</label><input id="causal-rationale"/></div>
|
|
1133
|
+
</div>
|
|
1134
|
+
<button class="btn primary" onclick="linkCausal()">Record Link</button>
|
|
1135
|
+
</div>
|
|
1136
|
+
</section>
|
|
1137
|
+
|
|
1138
|
+
<!-- ════ COLLISION ═══════════════════════════════════════════════ -->
|
|
1139
|
+
<section class="section" id="tab-collision">
|
|
1140
|
+
<div class="accent-bar"></div>
|
|
1141
|
+
<div class="section-header"><div><h1 class="section-title">Agent Collision Detective</h1><div class="section-subtitle">Real-time conflict detection across agent edit intents</div></div></div>
|
|
1142
|
+
<div class="grid cols-2">
|
|
1143
|
+
<div class="card"><div class="card-header"><div class="card-title">Stats</div></div><div id="collision-stats"></div></div>
|
|
1144
|
+
<div class="card"><div class="card-header"><div class="card-title">Active Alerts</div></div><div id="collision-alerts"></div></div>
|
|
1145
|
+
</div>
|
|
1146
|
+
<div class="card mt-lg" style="padding:0">
|
|
1147
|
+
<div style="padding:14px 18px 6px"><div class="card-title">Active Edit Intents</div></div>
|
|
1148
|
+
<table class="table"><thead><tr><th>Agent</th><th>File</th><th>Lines</th><th>Intent</th><th>Expires</th></tr></thead><tbody id="collision-intents"></tbody></table>
|
|
1149
|
+
</div>
|
|
1150
|
+
</section>
|
|
1151
|
+
|
|
1152
|
+
<!-- ════ DREAM ═══════════════════════════════════════════════════ -->
|
|
1153
|
+
<section class="section" id="tab-dream">
|
|
1154
|
+
<div class="accent-bar"></div>
|
|
1155
|
+
<div class="section-header">
|
|
1156
|
+
<div><h1 class="section-title">Dream Engine</h1><div class="section-subtitle">Background idle reflection — local-first via Ollama</div></div>
|
|
1157
|
+
<div class="section-actions"><button class="btn primary" onclick="runDream()">Run Cycle</button><button class="btn" onclick="startDreamLoop()">Start Loop</button></div>
|
|
1158
|
+
</div>
|
|
1159
|
+
<div class="grid cols-3">
|
|
1160
|
+
<div class="card"><div class="card-header"><div class="card-title">Stats</div></div><div id="dream-stats"></div></div>
|
|
1161
|
+
<div class="card span-2"><div class="card-header"><div class="card-title">Dream Type Distribution</div></div><div class="chart-wrap"><canvas id="chart-dreams"></canvas></div></div>
|
|
1162
|
+
</div>
|
|
1163
|
+
<div class="card mt-lg" style="padding:0">
|
|
1164
|
+
<div style="padding:14px 18px 6px"><div class="card-title">Recent Dreams</div></div>
|
|
1165
|
+
<table class="table"><thead><tr><th>Type</th><th>Content</th><th>Conf.</th><th>Action</th><th></th></tr></thead><tbody id="dream-list"></tbody></table>
|
|
1166
|
+
</div>
|
|
1167
|
+
</section>
|
|
1168
|
+
|
|
1169
|
+
<!-- ════ REPUTATION ══════════════════════════════════════════════ -->
|
|
1170
|
+
<section class="section" id="tab-reputation">
|
|
1171
|
+
<div class="accent-bar"></div>
|
|
1172
|
+
<div class="section-header"><div><h1 class="section-title">Agent Reputation Ledger</h1><div class="section-subtitle">Ed25519-signed decision receipts · portable trust score</div></div></div>
|
|
1173
|
+
<div class="grid cols-2">
|
|
1174
|
+
<div class="card"><div class="card-header"><div class="card-title">Ledger Stats</div></div><div id="reputation-stats"></div></div>
|
|
1175
|
+
<div class="card">
|
|
1176
|
+
<div class="card-header"><div class="card-title">Generate Badge</div></div>
|
|
1177
|
+
<div class="field-row">
|
|
1178
|
+
<div class="field"><label>Agent</label><select id="rep-agent"><option>claude-code</option><option>cursor</option><option>codex</option><option>cline</option></select></div>
|
|
1179
|
+
<div class="field"><label>Version</label><input id="rep-ver" placeholder="4.7"/></div>
|
|
1180
|
+
</div>
|
|
1181
|
+
<button class="btn primary" onclick="genBadge()">Generate</button>
|
|
1182
|
+
<pre class="mono" id="rep-badge-output" style="margin-top:10px;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1183
|
+
</div>
|
|
1184
|
+
</div>
|
|
1185
|
+
<div class="card mt-lg" style="padding:0">
|
|
1186
|
+
<div style="padding:14px 18px 6px"><div class="card-title">Agent Leaderboard</div></div>
|
|
1187
|
+
<table class="table"><thead><tr><th>Agent</th><th>Accuracy</th><th>Decisions</th><th>Correct/Incorrect/Partial</th></tr></thead><tbody id="rep-leaderboard"></tbody></table>
|
|
1188
|
+
</div>
|
|
1189
|
+
</section>
|
|
1190
|
+
|
|
1191
|
+
<!-- ════ DEBATE ══════════════════════════════════════════════════ -->
|
|
1192
|
+
<section class="section" id="tab-debate">
|
|
1193
|
+
<div class="accent-bar"></div>
|
|
1194
|
+
<div class="section-header"><div><h1 class="section-title">Swarm Debate Protocol</h1><div class="section-subtitle">Pro / con / arbiter multi-agent debate for critical decisions</div></div></div>
|
|
1195
|
+
<div class="card">
|
|
1196
|
+
<div class="field"><label>Question</label><input id="debate-question" placeholder="e.g. Redis or Postgres for session storage?"/></div>
|
|
1197
|
+
<div class="field"><label>Context</label><textarea id="debate-context" rows="2" placeholder="e.g. 10k users, read-heavy workload"></textarea></div>
|
|
1198
|
+
<div class="field-row"><div class="field"><label>Turns per side</label><input id="debate-turns" value="2"/></div><div class="field"><label> </label><button class="btn primary" onclick="runDebate()" style="width:100%">Run Debate</button></div></div>
|
|
1199
|
+
<pre class="mono" id="debate-output" style="margin-top:12px;max-height:420px;overflow:auto;background:var(--bg-elev-1);padding:12px;border-radius:6px"></pre>
|
|
1200
|
+
</div>
|
|
1201
|
+
</section>
|
|
1202
|
+
|
|
1203
|
+
<!-- ════ PREMORTEM ═══════════════════════════════════════════════ -->
|
|
1204
|
+
<section class="section" id="tab-premortem">
|
|
1205
|
+
<div class="accent-bar"></div>
|
|
1206
|
+
<div class="section-header"><div><h1 class="section-title">Pre-Mortem Assistant</h1><div class="section-subtitle">Surface past failures from YOUR project before significant tasks</div></div></div>
|
|
1207
|
+
<div class="card">
|
|
1208
|
+
<div class="field"><label>Task description</label><input id="pm-task" placeholder="e.g. migrate auth to passkeys"/></div>
|
|
1209
|
+
<button class="btn primary" onclick="runPremortem()">Run Pre-Mortem</button>
|
|
1210
|
+
<div id="pm-output" style="margin-top:14px"></div>
|
|
1211
|
+
</div>
|
|
1212
|
+
</section>
|
|
1213
|
+
|
|
1214
|
+
<!-- ════ ATTENTION ═══════════════════════════════════════════════ -->
|
|
1215
|
+
<section class="section" id="tab-attention">
|
|
1216
|
+
<div class="accent-bar"></div>
|
|
1217
|
+
<div class="section-header"><div><h1 class="section-title">Attention Heatmap</h1><div class="section-subtitle">See which memories shaped a decision — weighted attribution</div></div></div>
|
|
1218
|
+
<div class="card">
|
|
1219
|
+
<div class="field"><label>Decision text</label><input id="att-decision" placeholder="e.g. use parameterized SQL queries"/></div>
|
|
1220
|
+
<div class="field"><label>Candidate memory IDs (comma-separated)</label><input id="att-memories" placeholder="id1, id2, id3"/></div>
|
|
1221
|
+
<button class="btn primary" onclick="runAttention()">Compute Attention</button>
|
|
1222
|
+
<pre class="mono" id="att-output" style="margin-top:12px;max-height:320px;overflow:auto;background:var(--bg-elev-1);padding:12px;border-radius:6px"></pre>
|
|
1223
|
+
</div>
|
|
1224
|
+
</section>
|
|
1225
|
+
|
|
1226
|
+
<!-- ════ TOKENS ══════════════════════════════════════════════════ -->
|
|
1227
|
+
<section class="section" id="tab-tokens">
|
|
1228
|
+
<div class="accent-bar"></div>
|
|
1229
|
+
<div class="section-header"><div><h1 class="section-title">Token Economy Engine</h1><div class="section-subtitle">Cross-agent spend tracking · savings suggestions · routing advice</div></div></div>
|
|
1230
|
+
<div class="grid cols-4">
|
|
1231
|
+
<div class="card stat-card"><div class="label">Total Spend</div><div class="value" id="tok-total">$—</div><div class="trend">all time</div></div>
|
|
1232
|
+
<div class="card stat-card"><div class="label">Monthly Projection</div><div class="value" id="tok-proj">$—</div><div class="trend">from last 7 days</div></div>
|
|
1233
|
+
<div class="card stat-card"><div class="label">Savings Available</div><div class="value" style="color:var(--success)" id="tok-save">$—</div><div class="trend up">via smart routing</div></div>
|
|
1234
|
+
<div class="card stat-card"><div class="label">Total Calls</div><div class="value" id="tok-calls">—</div><div class="trend" id="tok-calls-sub"></div></div>
|
|
1235
|
+
</div>
|
|
1236
|
+
<div class="grid cols-2 mt-lg">
|
|
1237
|
+
<div class="card"><div class="card-header"><div class="card-title">Spend by Model</div></div><div class="chart-wrap"><canvas id="chart-tokens-model"></canvas></div></div>
|
|
1238
|
+
<div class="card"><div class="card-header"><div class="card-title">Savings Suggestions</div></div><div id="tok-suggestions"></div></div>
|
|
1239
|
+
</div>
|
|
1240
|
+
</section>
|
|
1241
|
+
|
|
1242
|
+
<!-- ════ FORGET ══════════════════════════════════════════════════ -->
|
|
1243
|
+
<section class="section" id="tab-forget">
|
|
1244
|
+
<div class="accent-bar"></div>
|
|
1245
|
+
<div class="section-header"><div><h1 class="section-title">Forgetting Curve + Sleep Consolidation</h1><div class="section-subtitle">Ebbinghaus-inspired natural decay · tier promotion</div></div><div class="section-actions"><button class="btn primary" onclick="runForget()">Run Consolidation Cycle</button></div></div>
|
|
1246
|
+
<div class="grid cols-2">
|
|
1247
|
+
<div class="card"><div class="card-header"><div class="card-title">Last Cycle</div></div><div id="forget-output"></div></div>
|
|
1248
|
+
<div class="card"><div class="card-header"><div class="card-title">Memory Strength Distribution</div></div><div class="chart-wrap"><canvas id="chart-forget"></canvas></div></div>
|
|
1249
|
+
</div>
|
|
1250
|
+
</section>
|
|
1251
|
+
|
|
1252
|
+
<!-- ════ FORMAL ══════════════════════════════════════════════════ -->
|
|
1253
|
+
<section class="section" id="tab-formal">
|
|
1254
|
+
<div class="accent-bar"></div>
|
|
1255
|
+
<div class="section-header"><div><h1 class="section-title">Formal Verification Bridge</h1><div class="section-subtitle">Natural-language rules → ESLint / Semgrep / LSP diagnostics</div></div></div>
|
|
1256
|
+
<div class="grid cols-2">
|
|
1257
|
+
<div class="card">
|
|
1258
|
+
<div class="card-header"><div class="card-title">Generate Rule</div></div>
|
|
1259
|
+
<div class="field"><label>Natural-language rule</label><textarea id="formal-text" rows="3" placeholder="e.g. always use parameterized SQL queries"></textarea></div>
|
|
1260
|
+
<button class="btn primary" onclick="genFormal()">Generate</button>
|
|
1261
|
+
<pre class="mono" id="formal-output" style="margin-top:12px;max-height:220px;overflow:auto;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1262
|
+
</div>
|
|
1263
|
+
<div class="card">
|
|
1264
|
+
<div class="card-header"><div class="card-title">Exports</div></div>
|
|
1265
|
+
<div class="flex gap"><button class="btn" onclick="exportEslint()">ESLint</button><button class="btn" onclick="exportSemgrep()">Semgrep YAML</button></div>
|
|
1266
|
+
<pre class="mono" id="formal-export" style="margin-top:12px;max-height:280px;overflow:auto;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1267
|
+
</div>
|
|
1268
|
+
</div>
|
|
1269
|
+
</section>
|
|
1270
|
+
|
|
1271
|
+
<!-- ════ CALIBRATION ═════════════════════════════════════════════ -->
|
|
1272
|
+
<section class="section" id="tab-calibration">
|
|
1273
|
+
<div class="accent-bar"></div>
|
|
1274
|
+
<div class="section-header"><div><h1 class="section-title">Confidence Calibration Monitor</h1><div class="section-subtitle">Brier scores per agent per category</div></div></div>
|
|
1275
|
+
<div class="card" style="padding:0">
|
|
1276
|
+
<table class="table"><thead><tr><th>Agent</th><th>Category</th><th>Brier</th><th>Cal. Error</th><th>Overconf.</th><th>Trust Weight</th><th>Samples</th></tr></thead><tbody id="calibration-table"></tbody></table>
|
|
1277
|
+
</div>
|
|
1278
|
+
</section>
|
|
1279
|
+
|
|
1280
|
+
<!-- ════ BRANCH ══════════════════════════════════════════════════ -->
|
|
1281
|
+
<section class="section" id="tab-branch">
|
|
1282
|
+
<div class="accent-bar"></div>
|
|
1283
|
+
<div class="section-header"><div><h1 class="section-title">Branch Brains</h1><div class="section-subtitle">Git-branch-aware memory context</div></div></div>
|
|
1284
|
+
<div class="card"><div id="branch-state"></div></div>
|
|
1285
|
+
</section>
|
|
1286
|
+
|
|
1287
|
+
<!-- ════ PRIVACY ═════════════════════════════════════════════════ -->
|
|
1288
|
+
<section class="section" id="tab-privacy">
|
|
1289
|
+
<div class="accent-bar"></div>
|
|
1290
|
+
<div class="section-header"><div><h1 class="section-title">Privacy & Safety</h1><div class="section-subtitle">Air-Gap mode · encryption · hallucination quarantine</div></div></div>
|
|
1291
|
+
<div class="grid cols-2">
|
|
1292
|
+
<div class="card">
|
|
1293
|
+
<div class="card-header"><div class="card-title">Air-Gap Mode</div></div>
|
|
1294
|
+
<div id="airgap-state"></div>
|
|
1295
|
+
<div class="flex gap mt"><button class="btn success" onclick="enableAirGap()">Enable (strict)</button><button class="btn danger" onclick="disableAirGap()">Disable</button></div>
|
|
1296
|
+
</div>
|
|
1297
|
+
<div class="card">
|
|
1298
|
+
<div class="card-header"><div class="card-title">Hallucination Quarantine</div></div>
|
|
1299
|
+
<div id="quarantine-state"></div>
|
|
1300
|
+
</div>
|
|
1301
|
+
</div>
|
|
1302
|
+
<div class="card mt-lg" style="padding:0">
|
|
1303
|
+
<div style="padding:14px 18px 6px"><div class="card-title">Pending Quarantine Queue</div></div>
|
|
1304
|
+
<table class="table"><thead><tr><th>ID</th><th>Source</th><th>Claim</th><th>Reason</th><th style="text-align:right">Action</th></tr></thead><tbody id="quarantine-list"></tbody></table>
|
|
1305
|
+
</div>
|
|
1306
|
+
</section>
|
|
1307
|
+
|
|
1308
|
+
<!-- ════ VOICE ═══════════════════════════════════════════════════ -->
|
|
1309
|
+
<section class="section" id="tab-voice">
|
|
1310
|
+
<div class="accent-bar"></div>
|
|
1311
|
+
<div class="section-header"><div><h1 class="section-title">Voice Mode</h1><div class="section-subtitle">Process voice transcripts · local Whisper-compatible</div></div></div>
|
|
1312
|
+
<div class="card">
|
|
1313
|
+
<div class="field"><label>Transcript</label><input id="voice-input" placeholder="e.g. brain status"/></div>
|
|
1314
|
+
<button class="btn primary" onclick="sendVoice()">Process</button>
|
|
1315
|
+
<pre class="mono" id="voice-output" style="margin-top:12px;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1316
|
+
</div>
|
|
1317
|
+
</section>
|
|
1318
|
+
|
|
1319
|
+
<!-- ════ GARDEN ══════════════════════════════════════════════════ -->
|
|
1320
|
+
<section class="section" id="tab-garden">
|
|
1321
|
+
<div class="accent-bar"></div>
|
|
1322
|
+
<div class="section-header"><div><h1 class="section-title">Brain Garden</h1><div class="section-subtitle">Living constellation visualization of the brain</div></div></div>
|
|
1323
|
+
<div class="card">
|
|
1324
|
+
<canvas id="garden-canvas" width="1200" height="500" style="width:100%;background:radial-gradient(ellipse at center, rgba(24,255,255,0.04), transparent);border-radius:8px;display:block"></canvas>
|
|
1325
|
+
<div class="mono mt" id="garden-stats"></div>
|
|
1326
|
+
</div>
|
|
1327
|
+
</section>
|
|
1328
|
+
|
|
1329
|
+
<!-- ════ PR REVIEW ═══════════════════════════════════════════════ -->
|
|
1330
|
+
<section class="section" id="tab-pr">
|
|
1331
|
+
<div class="accent-bar"></div>
|
|
1332
|
+
<div class="section-header"><div><h1 class="section-title">PR Auto-Review</h1><div class="section-subtitle">Generate GitHub PR comments from brain memories</div></div></div>
|
|
1333
|
+
<div class="card">
|
|
1334
|
+
<div class="grid cols-2">
|
|
1335
|
+
<div class="field"><label>Repo (owner/name)</label><input id="pr-repo" placeholder="theihtisham/agent-shadow-brain"/></div>
|
|
1336
|
+
<div class="field"><label>PR number</label><input id="pr-num" placeholder="42"/></div>
|
|
1337
|
+
</div>
|
|
1338
|
+
<div class="field"><label>Changed files (comma-separated)</label><input id="pr-files"/></div>
|
|
1339
|
+
<div class="field"><label>Diff summary</label><textarea id="pr-diff" rows="4"></textarea></div>
|
|
1340
|
+
<button class="btn primary" onclick="genPRReview()">Generate Review</button>
|
|
1341
|
+
<pre class="mono" id="pr-output" style="margin-top:14px;max-height:400px;overflow:auto;background:var(--bg-elev-1);padding:14px;border-radius:6px"></pre>
|
|
1342
|
+
</div>
|
|
1343
|
+
</section>
|
|
1344
|
+
|
|
1345
|
+
<!-- ════ TEAM ════════════════════════════════════════════════════ -->
|
|
1346
|
+
<section class="section" id="tab-team">
|
|
1347
|
+
<div class="accent-bar"></div>
|
|
1348
|
+
<div class="section-header"><div><h1 class="section-title">Team Brain Sync</h1><div class="section-subtitle">Peer-to-peer brain sync (WebRTC, no server)</div></div></div>
|
|
1349
|
+
<div class="grid cols-2">
|
|
1350
|
+
<div class="card"><div class="card-header"><div class="card-title">My Peer Info</div></div><div id="team-self"></div></div>
|
|
1351
|
+
<div class="card"><div class="card-header"><div class="card-title">Connected Peers</div></div><div id="team-peers"></div></div>
|
|
1352
|
+
</div>
|
|
1353
|
+
</section>
|
|
1354
|
+
|
|
1355
|
+
<!-- ════ EXCHANGE ════════════════════════════════════════════════ -->
|
|
1356
|
+
<section class="section" id="tab-exchange">
|
|
1357
|
+
<div class="accent-bar"></div>
|
|
1358
|
+
<div class="section-header"><div><h1 class="section-title">Brain Exchange</h1><div class="section-subtitle">Export / import curated shareable brain slices</div></div></div>
|
|
1359
|
+
<div class="grid cols-2">
|
|
1360
|
+
<div class="card">
|
|
1361
|
+
<div class="card-header"><div class="card-title">Export Package</div></div>
|
|
1362
|
+
<div class="field"><label>Name</label><input id="ex-name" placeholder="react-patterns-v1"/></div>
|
|
1363
|
+
<div class="field"><label>Description</label><input id="ex-desc"/></div>
|
|
1364
|
+
<div class="field-row">
|
|
1365
|
+
<div class="field"><label>Author</label><input id="ex-author" value="anonymous"/></div>
|
|
1366
|
+
<div class="field"><label>Tags (csv)</label><input id="ex-tags" placeholder="react, auth"/></div>
|
|
1367
|
+
</div>
|
|
1368
|
+
<button class="btn primary" onclick="exportPackage()">Export</button>
|
|
1369
|
+
<div class="mono mt" id="ex-out"></div>
|
|
1370
|
+
</div>
|
|
1371
|
+
<div class="card"><div class="card-header"><div class="card-title">Local Packages</div></div><div id="ex-list"></div></div>
|
|
1372
|
+
</div>
|
|
1373
|
+
</section>
|
|
1374
|
+
|
|
1375
|
+
<!-- ════ FEATURE TOGGLES ═════════════════════════════════════════ -->
|
|
1376
|
+
<section class="section" id="tab-features">
|
|
1377
|
+
<div class="accent-bar"></div>
|
|
1378
|
+
<div class="section-header"><div><h1 class="section-title">Feature Toggles</h1><div class="section-subtitle">Enable / disable any v6 module · persisted globally</div></div></div>
|
|
1379
|
+
<div class="card"><div class="feature-grid" id="feature-grid"></div></div>
|
|
1380
|
+
</section>
|
|
1381
|
+
|
|
1382
|
+
<!-- ════ MODELS & INTELLIGENCE ═══════════════════════════════════ -->
|
|
1383
|
+
<section class="section" id="tab-models">
|
|
1384
|
+
<div class="accent-bar"></div>
|
|
1385
|
+
<div class="section-header">
|
|
1386
|
+
<div>
|
|
1387
|
+
<h1 class="section-title">Models & Brain Intelligence</h1>
|
|
1388
|
+
<div class="section-subtitle">Route the Hive Mind through Ollama · Anthropic · OpenAI · OpenRouter · Moonshot/Kimi · Gemini · DeepSeek · Mistral — or reuse your existing agent tools' configuration</div>
|
|
1389
|
+
</div>
|
|
1390
|
+
<div class="section-actions"><button class="btn" onclick="refreshModels()">Refresh</button></div>
|
|
1391
|
+
</div>
|
|
1392
|
+
|
|
1393
|
+
<div class="card mb-lg">
|
|
1394
|
+
<div class="card-header"><div class="card-title">Auto-Discovered Agent Configurations</div></div>
|
|
1395
|
+
<div class="mono mb" style="color:var(--text-2)">Shadow Brain detected these agent tools' configs on your machine — no need to re-enter API keys. Toggle "Use agent-tool models" below to let the brain borrow their intelligence.</div>
|
|
1396
|
+
<div id="discovered-agents"></div>
|
|
1397
|
+
</div>
|
|
1398
|
+
|
|
1399
|
+
<div class="card mb-lg">
|
|
1400
|
+
<div class="card-header"><div class="card-title">Brain Intelligence Configuration</div></div>
|
|
1401
|
+
<div class="grid cols-2">
|
|
1402
|
+
<div class="field"><label>Lead Provider</label><select id="intel-provider"></select></div>
|
|
1403
|
+
<div class="field"><label>Lead Model</label><select id="intel-model"></select></div>
|
|
1404
|
+
</div>
|
|
1405
|
+
<div class="feature-toggle mb"><div class="icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div><div class="meta"><div class="name">Use already-configured agent-tool models</div><div class="desc">Reuse Claude Code / Cursor / Codex / Cline intelligence — no new API keys needed</div></div><label class="switch"><input type="checkbox" id="intel-agent-proxy"><span class="slider"></span></label></div>
|
|
1406
|
+
<div class="feature-toggle"><div class="icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg></div><div class="meta"><div class="name">Prefer Local-First (Ollama)</div><div class="desc">Route everything through Ollama when possible · zero cost · offline-ready</div></div><label class="switch"><input type="checkbox" id="intel-local-first"><span class="slider"></span></label></div>
|
|
1407
|
+
<button class="btn primary mt" onclick="saveIntelligence()">Save Intelligence Config</button>
|
|
1408
|
+
<div class="mono mt" id="intel-result"></div>
|
|
1409
|
+
</div>
|
|
1410
|
+
|
|
1411
|
+
<div class="card mb-lg">
|
|
1412
|
+
<div class="card-header"><div class="card-title">Provider Configuration</div></div>
|
|
1413
|
+
<div class="grid cols-2" id="providers-grid"></div>
|
|
1414
|
+
</div>
|
|
1415
|
+
|
|
1416
|
+
<div class="card">
|
|
1417
|
+
<div class="card-header"><div class="card-title">Custom Agent Paths (Hermes / OpenClaw / Nemoclaw / any)</div></div>
|
|
1418
|
+
<div class="mono mb" style="color:var(--text-2)">Point Shadow Brain at any agent's config file — we'll read the provider, model, and API key if present.</div>
|
|
1419
|
+
<div id="custom-agents-list"></div>
|
|
1420
|
+
<div class="grid cols-3 mt">
|
|
1421
|
+
<div class="field"><label>Agent name</label><input id="ca-name" placeholder="hermes / openclaw / nemoclaw / ..."/></div>
|
|
1422
|
+
<div class="field"><label>Config file path</label><input id="ca-path" placeholder="C:\\Users\\you\\.hermes\\config.json"/></div>
|
|
1423
|
+
<div class="field"><label> </label><button class="btn primary" onclick="addCustomAgent()" style="width:100%">Add</button></div>
|
|
1424
|
+
</div>
|
|
1425
|
+
</div>
|
|
1426
|
+
</section>
|
|
1427
|
+
|
|
1428
|
+
<!-- ════ CONFIG ══════════════════════════════════════════════════ -->
|
|
1429
|
+
<section class="section" id="tab-config">
|
|
1430
|
+
<div class="accent-bar"></div>
|
|
1431
|
+
<div class="section-header"><div><h1 class="section-title">Configuration</h1><div class="section-subtitle">LLM provider · API keys · MCP server · global settings</div></div></div>
|
|
1432
|
+
<div class="grid cols-2">
|
|
1433
|
+
<div class="card">
|
|
1434
|
+
<div class="card-header"><div class="card-title">LLM Provider</div></div>
|
|
1435
|
+
<div class="field"><label>Provider</label><select id="cfg-provider"><option value="ollama">Ollama (local)</option><option value="anthropic">Anthropic</option><option value="openai">OpenAI</option><option value="gemini">Gemini</option><option value="deepseek">DeepSeek</option></select></div>
|
|
1436
|
+
<div class="field"><label>API Key (blank = env var)</label><input type="password" id="cfg-apikey"/></div>
|
|
1437
|
+
<div class="field"><label>Model</label><input id="cfg-model" placeholder="claude-opus-4-7"/></div>
|
|
1438
|
+
<div class="flex gap"><button class="btn primary" onclick="saveConfig()">Save</button><button class="btn" onclick="testConnection()">Test</button></div>
|
|
1439
|
+
<div class="mono mt" id="cfg-result"></div>
|
|
1440
|
+
</div>
|
|
1441
|
+
<div class="card">
|
|
1442
|
+
<div class="card-header"><div class="card-title">MCP Server</div></div>
|
|
1443
|
+
<div class="field"><label>Port</label><input id="mcp-port" value="7342"/></div>
|
|
1444
|
+
<div class="field"><label>Auth Token (optional)</label><input id="mcp-token" type="password"/></div>
|
|
1445
|
+
<div class="flex gap"><button class="btn primary" onclick="startMCP()">Start</button><button class="btn danger" onclick="stopMCP()">Stop</button></div>
|
|
1446
|
+
<div class="mono mt" id="mcp-result"></div>
|
|
1447
|
+
</div>
|
|
1448
|
+
<div class="card span-2">
|
|
1449
|
+
<div class="card-header"><div class="card-title">System Status</div></div>
|
|
1450
|
+
<div id="config-state"></div>
|
|
1451
|
+
</div>
|
|
1452
|
+
</div>
|
|
1453
|
+
</section>
|
|
1454
|
+
|
|
1455
|
+
</main>
|
|
1456
|
+
</div>
|
|
1457
|
+
|
|
1458
|
+
<!-- ══════════════════════════════════════════════════════════════════════
|
|
1459
|
+
Command Palette overlay (Cmd+K)
|
|
1460
|
+
══════════════════════════════════════════════════════════════════════ -->
|
|
1461
|
+
<div class="cmd-overlay" id="cmd-overlay" onclick="if(event.target===this)closeCmdPalette()">
|
|
1462
|
+
<div class="cmd-palette">
|
|
1463
|
+
<input id="cmd-input" placeholder="Search features, actions, memories…" autocomplete="off"/>
|
|
1464
|
+
<div class="results" id="cmd-results"></div>
|
|
1465
|
+
</div>
|
|
1466
|
+
</div>
|
|
1467
|
+
|
|
1468
|
+
<!-- Toast container -->
|
|
1469
|
+
<div class="toast-container" id="toast-container"></div>
|
|
1470
|
+
|
|
1471
|
+
<!-- Log ticker -->
|
|
1472
|
+
<div class="log-ticker">
|
|
1473
|
+
<div class="dot"></div>
|
|
1474
|
+
<div class="scroll" id="log-scroll">Hive Mind initializing…</div>
|
|
1475
|
+
<button class="icon-btn" style="width:28px;height:24px;padding:0" onclick="toggleLogPanel()" title="Open log"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></button>
|
|
1476
|
+
</div>
|
|
1477
|
+
|
|
1478
|
+
<!-- Expandable log panel -->
|
|
1479
|
+
<div class="log-panel" id="log-panel">
|
|
1480
|
+
<div class="head">
|
|
1481
|
+
<div class="title">LIVE ACTIVITY</div>
|
|
1482
|
+
<div class="flex gap">
|
|
1483
|
+
<button class="btn sm ghost" onclick="clearLogs()">Clear</button>
|
|
1484
|
+
<button class="btn sm ghost" onclick="toggleLogPanel()">Close</button>
|
|
1485
|
+
</div>
|
|
1486
|
+
</div>
|
|
1487
|
+
<div id="log-panel-list"></div>
|
|
1488
|
+
</div>
|
|
1489
|
+
|
|
1490
|
+
<!-- ══════════════════════════════════════════════════════════════════════
|
|
1491
|
+
Scripts
|
|
1492
|
+
══════════════════════════════════════════════════════════════════════ -->
|
|
1493
|
+
<script>
|
|
1494
|
+
const FEATURES = [
|
|
1495
|
+
['sabb','Sub-Agent Bridge','Syncs memory to Claude Task, Cursor Composer, CrewAI, LangGraph'],
|
|
1496
|
+
['causal','Causal Chains','Track cause→effect for every decision'],
|
|
1497
|
+
['collision','Collision Detective','Real-time overlap detection'],
|
|
1498
|
+
['dream','Dream Engine','Idle-time reflection via local LLM'],
|
|
1499
|
+
['reputation','Reputation Ledger','Ed25519-signed agent receipts'],
|
|
1500
|
+
['debate','Swarm Debate','Multi-agent debate w/ arbiter'],
|
|
1501
|
+
['premortem','Pre-Mortem','Surface past failures before tasks'],
|
|
1502
|
+
['branch','Branch Brains','Git-branch-aware memory'],
|
|
1503
|
+
['attention','Attention Heatmap','Weighted memory attribution'],
|
|
1504
|
+
['tokens','Token Economy','Spend tracking + savings'],
|
|
1505
|
+
['forget','Forgetting Curve','Ebbinghaus memory decay'],
|
|
1506
|
+
['formal','Formal Bridge','NL → ESLint/Semgrep'],
|
|
1507
|
+
['calibration','Calibration Monitor','Brier score trust weighting'],
|
|
1508
|
+
['airgap','Air-Gap Mode','Zero outbound network'],
|
|
1509
|
+
['encrypt','E2E Encryption','ChaCha20-Poly1305 at rest'],
|
|
1510
|
+
['quarantine','Hallucination Quarantine','Isolate suspect claims'],
|
|
1511
|
+
['voice','Voice Mode','Transcript + intent'],
|
|
1512
|
+
['garden','Brain Garden','Memory visualizer'],
|
|
1513
|
+
['pr','PR Auto-Review','GitHub PR comments'],
|
|
1514
|
+
['team','Team Brain Sync','WebRTC P2P'],
|
|
1515
|
+
['exchange','Brain Exchange','Share brain slices'],
|
|
1516
|
+
['local-llm','Local-First LLM','Ollama-first default'],
|
|
1517
|
+
];
|
|
1518
|
+
|
|
1519
|
+
let ws = null;
|
|
1520
|
+
let state = { hive: null, lastRefreshAt: 0 };
|
|
1521
|
+
let charts = {};
|
|
1522
|
+
|
|
1523
|
+
// ──── Tab switching ────────────────────────────────────────────────────
|
|
1524
|
+
document.querySelectorAll('.nav-item').forEach(el => el.addEventListener('click', () => switchTab(el.dataset.tab, el.dataset.title)));
|
|
1525
|
+
function switchTab(id, title) {
|
|
1526
|
+
document.querySelectorAll('.nav-item').forEach(el => el.classList.toggle('active', el.dataset.tab === id));
|
|
1527
|
+
document.querySelectorAll('.section').forEach(el => el.classList.toggle('active', el.id === 'tab-' + id));
|
|
1528
|
+
if (window.innerWidth <= 768) document.getElementById('sidebar').classList.remove('open');
|
|
1529
|
+
onTabOpen(id);
|
|
1530
|
+
}
|
|
1531
|
+
function onTabOpen(tab) {
|
|
1532
|
+
const fn = { overview: refreshOverview, graph: startGraph, agents: refreshAgents, memory: () => searchMemory(), chat: refreshChat, activity: refreshActivity,
|
|
1533
|
+
sabb: refreshSABB, causal: refreshCausal, collision: refreshCollision, dream: refreshDream, reputation: refreshReputation,
|
|
1534
|
+
tokens: refreshTokens, forget: refreshForget, formal: null, calibration: refreshCalibration, branch: refreshBranch,
|
|
1535
|
+
privacy: refreshPrivacy, garden: refreshGarden, team: refreshTeam, exchange: refreshExchange, features: refreshFeatures,
|
|
1536
|
+
models: refreshModels, config: refreshConfig }[tab];
|
|
1537
|
+
if (fn) fn();
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// ──── Sidebar mobile ───────────────────────────────────────────────────
|
|
1541
|
+
function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); }
|
|
1542
|
+
|
|
1543
|
+
// ──── Theme toggle ─────────────────────────────────────────────────────
|
|
1544
|
+
function toggleTheme() {
|
|
1545
|
+
const html = document.documentElement;
|
|
1546
|
+
const newT = html.dataset.theme === 'light' ? 'dark' : 'light';
|
|
1547
|
+
html.dataset.theme = newT;
|
|
1548
|
+
try { localStorage.setItem('hive.theme', newT); } catch {}
|
|
1549
|
+
if (charts.tokens) updateChartColors();
|
|
1550
|
+
}
|
|
1551
|
+
(function initTheme(){
|
|
1552
|
+
try {
|
|
1553
|
+
const t = localStorage.getItem('hive.theme');
|
|
1554
|
+
// Default to light for better readability
|
|
1555
|
+
document.documentElement.dataset.theme = t || 'light';
|
|
1556
|
+
} catch { document.documentElement.dataset.theme = 'light'; }
|
|
1557
|
+
})();
|
|
1558
|
+
|
|
1559
|
+
// ──── API helpers ──────────────────────────────────────────────────────
|
|
1560
|
+
async function api(path, body) {
|
|
1561
|
+
try {
|
|
1562
|
+
const opts = body !== undefined ? { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) } : {};
|
|
1563
|
+
const res = await fetch(path, opts);
|
|
1564
|
+
if (!res.ok) { toast('API error', res.status + ' ' + path, 'error'); return null; }
|
|
1565
|
+
const ct = res.headers.get('content-type') || '';
|
|
1566
|
+
return ct.includes('application/json') ? await res.json() : await res.text();
|
|
1567
|
+
} catch (err) { toast('Network error', err.message, 'error'); return null; }
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// ──── Toasts ───────────────────────────────────────────────────────────
|
|
1571
|
+
function toast(title, body, variant='success') {
|
|
1572
|
+
const c = document.getElementById('toast-container');
|
|
1573
|
+
const icons = { success:'<path d="M20 6 9 17l-5-5"/>', warn:'<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>', error:'<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>', info:'<circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/>' };
|
|
1574
|
+
const el = document.createElement('div');
|
|
1575
|
+
el.className = 'toast ' + variant;
|
|
1576
|
+
el.innerHTML = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\${icons[variant] || icons.info}</svg><div><div class="title">\${title}</div>\${body ? '<div class="body">' + body + '</div>' : ''}</div>\`;
|
|
1577
|
+
c.appendChild(el);
|
|
1578
|
+
setTimeout(() => { el.classList.add('removing'); setTimeout(() => el.remove(), 400); }, 3800);
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// ──── Animated counters ────────────────────────────────────────────────
|
|
1582
|
+
const _counterState = {};
|
|
1583
|
+
function animateNum(id, target) {
|
|
1584
|
+
const el = document.getElementById(id);
|
|
1585
|
+
if (!el) return;
|
|
1586
|
+
const start = parseFloat((el.textContent || '0').replace(/[^\d.-]/g, '')) || 0;
|
|
1587
|
+
if (start === target) { el.textContent = target.toLocaleString(); return; }
|
|
1588
|
+
if (_counterState[id]) cancelAnimationFrame(_counterState[id]);
|
|
1589
|
+
const startTime = performance.now();
|
|
1590
|
+
const duration = 650;
|
|
1591
|
+
const step = now => {
|
|
1592
|
+
const t = Math.min(1, (now - startTime) / duration);
|
|
1593
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
1594
|
+
const val = start + (target - start) * eased;
|
|
1595
|
+
el.textContent = Math.round(val).toLocaleString();
|
|
1596
|
+
if (t < 1) _counterState[id] = requestAnimationFrame(step);
|
|
1597
|
+
else { el.textContent = target.toLocaleString(); delete _counterState[id]; }
|
|
1598
|
+
};
|
|
1599
|
+
_counterState[id] = requestAnimationFrame(step);
|
|
1600
|
+
}
|
|
1601
|
+
function animateMoney(id, target) {
|
|
1602
|
+
const el = document.getElementById(id);
|
|
1603
|
+
if (!el) return;
|
|
1604
|
+
const start = parseFloat((el.textContent || '0').replace(/[^\d.-]/g, '')) || 0;
|
|
1605
|
+
if (Math.abs(start - target) < 0.001) { el.textContent = '$' + target.toFixed(2); return; }
|
|
1606
|
+
if (_counterState[id]) cancelAnimationFrame(_counterState[id]);
|
|
1607
|
+
const startTime = performance.now();
|
|
1608
|
+
const duration = 650;
|
|
1609
|
+
const step = now => {
|
|
1610
|
+
const t = Math.min(1, (now - startTime) / duration);
|
|
1611
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
1612
|
+
const val = start + (target - start) * eased;
|
|
1613
|
+
el.textContent = '$' + val.toFixed(2);
|
|
1614
|
+
if (t < 1) _counterState[id] = requestAnimationFrame(step);
|
|
1615
|
+
else { el.textContent = '$' + target.toFixed(2); delete _counterState[id]; }
|
|
1616
|
+
};
|
|
1617
|
+
_counterState[id] = requestAnimationFrame(step);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// ──── Main refresh ─────────────────────────────────────────────────────
|
|
1621
|
+
async function refresh() {
|
|
1622
|
+
const hive = await api('/api/v6/hive-status');
|
|
1623
|
+
if (!hive) return;
|
|
1624
|
+
state.hive = hive;
|
|
1625
|
+
state.lastRefreshAt = Date.now();
|
|
1626
|
+
|
|
1627
|
+
animateNum('stat-agents', hive.totalAgentsConnected ?? 0);
|
|
1628
|
+
animateNum('stat-memories', hive.totalMemoriesStored ?? 0);
|
|
1629
|
+
animateNum('stat-dreams', hive.modules.dream?.totalDreams ?? 0);
|
|
1630
|
+
animateMoney('stat-savings', hive.modules.tokenEconomy?.savingsOpportunitiesUsd ?? 0);
|
|
1631
|
+
document.getElementById('badge-sabb').textContent = hive.modules.sabb?.totalSpawns ?? 0;
|
|
1632
|
+
document.getElementById('badge-causal').textContent = hive.modules.causal?.totalLinks ?? 0;
|
|
1633
|
+
document.getElementById('badge-collision').textContent = hive.modules.collision?.collisionsDetected ?? 0;
|
|
1634
|
+
document.getElementById('badge-dream').textContent = hive.modules.dream?.totalDreams ?? 0;
|
|
1635
|
+
const activeTab = document.querySelector('.nav-item.active')?.dataset.tab;
|
|
1636
|
+
if (activeTab) onTabOpen(activeTab);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
// ──── Overview ─────────────────────────────────────────────────────────
|
|
1640
|
+
async function refreshOverview() {
|
|
1641
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
1642
|
+
if (!hive) return;
|
|
1643
|
+
animateNum('ov-agents', hive.totalAgentsConnected ?? 0);
|
|
1644
|
+
animateNum('ov-memories', hive.totalMemoriesStored ?? 0);
|
|
1645
|
+
animateNum('ov-subagents', hive.modules.sabb?.totalSpawns ?? 0);
|
|
1646
|
+
document.getElementById('ov-subagents-sub').textContent = \`grad: \${hive.modules.sabb?.graduated ?? 0} · quar: \${hive.modules.sabb?.quarantined ?? 0}\`;
|
|
1647
|
+
animateMoney('ov-savings', hive.modules.tokenEconomy?.savingsOpportunitiesUsd ?? 0);
|
|
1648
|
+
document.getElementById('ov-savings-sub').textContent = 'projection: $' + (hive.modules.tokenEconomy?.monthlyProjectionUsd?.toFixed?.(2) ?? '0.00');
|
|
1649
|
+
|
|
1650
|
+
const moduleRows = [
|
|
1651
|
+
['SABB spawns', hive.modules.sabb?.totalSpawns ?? 0, 'good'],
|
|
1652
|
+
['Causal links', hive.modules.causal?.totalLinks ?? 0, 'good'],
|
|
1653
|
+
['Collisions detected', hive.modules.collision?.collisionsDetected ?? 0, 'warn'],
|
|
1654
|
+
['Dreams recorded', hive.modules.dream?.totalDreams ?? 0, 'good'],
|
|
1655
|
+
['Reputation receipts', hive.modules.reputation?.totalReceipts ?? 0, 'good'],
|
|
1656
|
+
['Formal rules', hive.modules.formalBridge?.totalRules ?? 0, 'good'],
|
|
1657
|
+
];
|
|
1658
|
+
document.getElementById('ov-modules').innerHTML = moduleRows.map(([k, v, cls]) =>
|
|
1659
|
+
\`<div class="row"><span class="k">\${k}</span><span class="v \${cls || ''}">\${v}</span></div>\`
|
|
1660
|
+
).join('');
|
|
1661
|
+
|
|
1662
|
+
const health = [
|
|
1663
|
+
['Local-first', hive.localFirst ? 'ON' : 'OFF', hive.localFirst ? 'good' : 'warn'],
|
|
1664
|
+
['Air-gap', hive.modules.airGap?.enabled ? 'ENABLED' : 'disabled', hive.modules.airGap?.enabled ? 'good' : ''],
|
|
1665
|
+
['Version', hive.version, ''],
|
|
1666
|
+
['Monthly cost', '$' + (hive.modules.tokenEconomy?.monthlyProjectionUsd?.toFixed?.(2) ?? '0.00'), 'warn'],
|
|
1667
|
+
['Cost saved/mo', '$' + (hive.modules.tokenEconomy?.savingsOpportunitiesUsd?.toFixed?.(2) ?? '0.00'), 'good'],
|
|
1668
|
+
];
|
|
1669
|
+
document.getElementById('ov-health').innerHTML = health.map(([k, v, cls]) => \`<div class="row"><span class="k">\${k}</span><span class="v \${cls}">\${v}</span></div>\`).join('');
|
|
1670
|
+
|
|
1671
|
+
// Tokens chart
|
|
1672
|
+
const tok = hive.modules.tokenEconomy;
|
|
1673
|
+
if (tok && tok.byModel) drawTokensChart(tok.byModel);
|
|
1674
|
+
|
|
1675
|
+
// Timeline
|
|
1676
|
+
const timeline = await api('/api/v6/activity-log?limit=15') ?? [];
|
|
1677
|
+
document.getElementById('ov-timeline').innerHTML = timeline.length
|
|
1678
|
+
? timeline.slice(0, 10).map(e => \`<div style="padding:5px 0;border-bottom:1px solid var(--border);display:flex;gap:8px"><span style="color:var(--text-dim);flex-shrink:0">\${new Date(e.timestamp).toLocaleTimeString()}</span><span class="pill \${typeToPill(e.type)}">\${e.type}</span><span style="color:var(--text-2);font-size:11px">\${(e.detail||'').slice(0,90)}</span></div>\`).join('')
|
|
1679
|
+
: '<div class="empty">No activity yet</div>';
|
|
1680
|
+
}
|
|
1681
|
+
function typeToPill(type) {
|
|
1682
|
+
if (type === 'memory-write') return 'green';
|
|
1683
|
+
if (type === 'subagent-spawn') return 'cyan';
|
|
1684
|
+
if (type === 'collision') return 'red';
|
|
1685
|
+
if (type === 'quarantine') return 'yellow';
|
|
1686
|
+
if (['dream','revisit','counterfactual','consolidation','pattern-discovery','contradiction'].includes(type)) return 'purple';
|
|
1687
|
+
return '';
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// ──── Charts ───────────────────────────────────────────────────────────
|
|
1691
|
+
function chartTheme() {
|
|
1692
|
+
const dark = document.documentElement.dataset.theme !== 'light';
|
|
1693
|
+
return { grid: dark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)', text: dark ? '#a4afc4' : '#3d4864' };
|
|
1694
|
+
}
|
|
1695
|
+
function drawTokensChart(byModel) {
|
|
1696
|
+
const ctx = document.getElementById('chart-tokens');
|
|
1697
|
+
if (!ctx) return;
|
|
1698
|
+
const labels = Object.keys(byModel);
|
|
1699
|
+
const data = labels.map(l => byModel[l].spendUsd);
|
|
1700
|
+
const t = chartTheme();
|
|
1701
|
+
if (charts.tokens) charts.tokens.destroy();
|
|
1702
|
+
charts.tokens = new Chart(ctx, { type: 'bar', data: { labels, datasets: [{ label: 'Spend (USD)', data, backgroundColor: ['#18ffff','#a855f7','#ec4899','#10e68c','#ffb53f','#4fb6ff'], borderRadius: 6 }] }, options: { responsive:true, maintainAspectRatio:false, plugins:{legend:{display:false}}, scales:{x:{grid:{color:t.grid},ticks:{color:t.text}},y:{grid:{color:t.grid},ticks:{color:t.text,callback:v=>'$'+v.toFixed(2)}}} } });
|
|
1703
|
+
}
|
|
1704
|
+
function drawTokensModelChart(byModel) {
|
|
1705
|
+
const ctx = document.getElementById('chart-tokens-model');
|
|
1706
|
+
if (!ctx) return;
|
|
1707
|
+
const labels = Object.keys(byModel);
|
|
1708
|
+
const data = labels.map(l => byModel[l].spendUsd);
|
|
1709
|
+
const t = chartTheme();
|
|
1710
|
+
if (charts.tokensModel) charts.tokensModel.destroy();
|
|
1711
|
+
charts.tokensModel = new Chart(ctx, { type: 'doughnut', data: { labels, datasets: [{ data, backgroundColor: ['#18ffff','#a855f7','#ec4899','#10e68c','#ffb53f','#4fb6ff'], borderWidth:0 }] }, options: { responsive:true, maintainAspectRatio:false, plugins:{legend:{position:'right',labels:{color:t.text,boxWidth:12}}} } });
|
|
1712
|
+
}
|
|
1713
|
+
function drawDreamChart(byType) {
|
|
1714
|
+
const ctx = document.getElementById('chart-dreams');
|
|
1715
|
+
if (!ctx) return;
|
|
1716
|
+
const labels = Object.keys(byType);
|
|
1717
|
+
const data = labels.map(l => byType[l]);
|
|
1718
|
+
const t = chartTheme();
|
|
1719
|
+
if (charts.dreams) charts.dreams.destroy();
|
|
1720
|
+
charts.dreams = new Chart(ctx, { type:'polarArea', data:{labels,datasets:[{data,backgroundColor:['#18ffff88','#a855f788','#ec489988','#10e68c88','#ffb53f88']}]}, options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:t.text}}},scales:{r:{grid:{color:t.grid},ticks:{color:t.text,backdropColor:'transparent'}}}} });
|
|
1721
|
+
}
|
|
1722
|
+
function drawForgetChart(states) {
|
|
1723
|
+
const ctx = document.getElementById('chart-forget');
|
|
1724
|
+
if (!ctx) return;
|
|
1725
|
+
const buckets = { strong: 0, medium: 0, weak: 0, fading: 0 };
|
|
1726
|
+
for (const s of states || []) {
|
|
1727
|
+
if (s.currentStrength >= 0.8) buckets.strong++;
|
|
1728
|
+
else if (s.currentStrength >= 0.5) buckets.medium++;
|
|
1729
|
+
else if (s.currentStrength >= 0.2) buckets.weak++;
|
|
1730
|
+
else buckets.fading++;
|
|
1731
|
+
}
|
|
1732
|
+
const t = chartTheme();
|
|
1733
|
+
if (charts.forget) charts.forget.destroy();
|
|
1734
|
+
charts.forget = new Chart(ctx, { type:'bar', data:{labels:Object.keys(buckets),datasets:[{data:Object.values(buckets),backgroundColor:['#10e68c','#18ffff','#ffb53f','#ff5864'],borderRadius:4}]}, options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},scales:{x:{grid:{color:t.grid},ticks:{color:t.text}},y:{grid:{color:t.grid},ticks:{color:t.text}}}} });
|
|
1735
|
+
}
|
|
1736
|
+
function updateChartColors() { for (const k of Object.keys(charts)) { charts[k].destroy(); delete charts[k]; } refresh(); }
|
|
1737
|
+
|
|
1738
|
+
// ──── SABB ─────────────────────────────────────────────────────────────
|
|
1739
|
+
async function refreshSABB() {
|
|
1740
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
1741
|
+
const s = hive?.modules.sabb ?? {};
|
|
1742
|
+
document.getElementById('sabb-stats').innerHTML = \`
|
|
1743
|
+
<div class="row"><span class="k">Total spawns</span><span class="v">\${s.totalSpawns ?? 0}</span></div>
|
|
1744
|
+
<div class="row"><span class="k">Quarantined</span><span class="v">\${s.quarantined ?? 0}</span></div>
|
|
1745
|
+
<div class="row"><span class="k">Graduated</span><span class="v good">\${s.graduated ?? 0}</span></div>
|
|
1746
|
+
<div class="row"><span class="k">Rejected</span><span class="v bad">\${s.rejected ?? 0}</span></div>
|
|
1747
|
+
<div class="row"><span class="k">Avg sliver tokens</span><span class="v">\${Math.round(s.avgSliverTokens ?? 0)}</span></div>
|
|
1748
|
+
<div class="row"><span class="k">Avg graduation</span><span class="v">\${Math.round(s.avgGraduationMs ?? 0)}ms</span></div>\`;
|
|
1749
|
+
const q = await api('/api/v6/subagent-quarantine') ?? [];
|
|
1750
|
+
document.getElementById('sabb-quarantine').innerHTML = q.length ? q.map(e =>
|
|
1751
|
+
\`<tr><td><code>\${e.id.slice(0,10)}</code></td><td><span class="pill purple">\${e.parentAgent}</span></td><td>\${e.category}</td><td>\${(e.confidence||0).toFixed(2)}</td><td>\${(e.content||'').slice(0,100)}</td><td style="text-align:right"><button class="btn sm success" onclick="graduate('\${e.id}')">Graduate</button> <button class="btn sm danger" onclick="rejectMem('\${e.id}')">Reject</button></td></tr>\`
|
|
1752
|
+
).join('') : '<tr><td colspan="6"><div class="empty">No quarantined memories</div></td></tr>';
|
|
1753
|
+
}
|
|
1754
|
+
async function computeSliver() {
|
|
1755
|
+
const res = await api('/api/v6/subagent-sliver', {
|
|
1756
|
+
parentAgent: document.getElementById('sabb-parent').value,
|
|
1757
|
+
framework: document.getElementById('sabb-framework').value,
|
|
1758
|
+
taskDescription: document.getElementById('sabb-task').value,
|
|
1759
|
+
tokenBudget: parseInt(document.getElementById('sabb-budget').value || '300'),
|
|
1760
|
+
});
|
|
1761
|
+
if (res) { document.getElementById('sabb-sliver-output').textContent = res.markdown; toast('Sliver computed', res.memories.length + ' memories · ' + res.tokenCount + ' tokens'); }
|
|
1762
|
+
}
|
|
1763
|
+
async function graduate(id) { const r = await api('/api/v6/subagent-graduate', { memoryId: id }); if (r?.ok) { toast('Graduated', id.slice(0,10)); refreshSABB(); } }
|
|
1764
|
+
async function rejectMem(id) { const r = await api('/api/v6/subagent-reject', { memoryId: id, reason: 'manual-reject' }); if (r?.ok) { toast('Rejected', id.slice(0,10), 'warn'); refreshSABB(); } }
|
|
1765
|
+
|
|
1766
|
+
// ──── Causal ───────────────────────────────────────────────────────────
|
|
1767
|
+
async function refreshCausal() {
|
|
1768
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
1769
|
+
const s = hive?.modules.causal ?? {};
|
|
1770
|
+
document.getElementById('causal-stats').innerHTML = \`<div class="row"><span class="k">Total links</span><span class="v">\${s.totalLinks ?? 0}</span></div><div class="row"><span class="k">Effects</span><span class="v">\${s.effects ?? 0}</span></div><div class="row"><span class="k">Causes</span><span class="v">\${s.causes ?? 0}</span></div>\`;
|
|
1771
|
+
}
|
|
1772
|
+
async function traceCausal() {
|
|
1773
|
+
const id = document.getElementById('causal-mem-id').value;
|
|
1774
|
+
if (!id) return toast('Missing', 'Enter a memory ID', 'warn');
|
|
1775
|
+
const res = await api('/api/v6/causal-trace', { memoryId: id, maxDepth: parseInt(document.getElementById('causal-depth').value || '8') });
|
|
1776
|
+
if (res) document.getElementById('causal-output').textContent = res.dot;
|
|
1777
|
+
}
|
|
1778
|
+
async function influenceCausal() {
|
|
1779
|
+
const id = document.getElementById('causal-mem-id').value;
|
|
1780
|
+
if (!id) return;
|
|
1781
|
+
const res = await api('/api/v6/causal-influence', { memoryId: id, maxDepth: 6 });
|
|
1782
|
+
if (res) document.getElementById('causal-output').textContent = res.dot;
|
|
1783
|
+
}
|
|
1784
|
+
async function linkCausal() {
|
|
1785
|
+
const r = await api('/api/v6/causal-link', {
|
|
1786
|
+
effectId: document.getElementById('causal-effect').value,
|
|
1787
|
+
causeId: document.getElementById('causal-cause').value,
|
|
1788
|
+
rationale: document.getElementById('causal-rationale').value,
|
|
1789
|
+
});
|
|
1790
|
+
if (r) { toast('Link recorded'); refreshCausal(); }
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
// ──── Collision ────────────────────────────────────────────────────────
|
|
1794
|
+
async function refreshCollision() {
|
|
1795
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
1796
|
+
const s = hive?.modules.collision ?? {};
|
|
1797
|
+
document.getElementById('collision-stats').innerHTML = \`<div class="row"><span class="k">Active intents</span><span class="v">\${s.activeIntents ?? 0}</span></div><div class="row"><span class="k">Collisions detected</span><span class="v warn">\${s.collisionsDetected ?? 0}</span></div><div class="row"><span class="k">Resolved</span><span class="v good">\${s.collisionsResolved ?? 0}</span></div><div class="row"><span class="k">Avg overlap</span><span class="v">\${(s.avgOverlapLines ?? 0).toFixed(1)} lines</span></div>\`;
|
|
1798
|
+
const data = await api('/api/v6/collision-list') ?? { intents: [], alerts: [] };
|
|
1799
|
+
document.getElementById('collision-alerts').innerHTML = data.alerts.length
|
|
1800
|
+
? data.alerts.map(a => \`<div style="padding:10px;background:var(--bg-elev-2);border-left:3px solid var(--danger);border-radius:6px;margin-bottom:6px"><div style="font-weight:600;font-size:12px">\${(a.filePath||'').split(/[\\\\/]/).pop()}</div><div class="mono" style="margin-top:4px;color:var(--text-2)">\${a.suggestedResolution}</div></div>\`).join('')
|
|
1801
|
+
: '<div class="empty">No active alerts</div>';
|
|
1802
|
+
document.getElementById('collision-intents').innerHTML = data.intents.length ? data.intents.map(i =>
|
|
1803
|
+
\`<tr><td><span class="pill cyan">\${i.agentTool}</span></td><td><code>\${(i.filePath||'').split(/[\\\\/]/).pop()}</code></td><td>\${i.startLine}-\${i.endLine}</td><td>\${(i.intent||'').slice(0,80)}</td><td class="mono">\${new Date(i.expiresAt).toLocaleTimeString()}</td></tr>\`
|
|
1804
|
+
).join('') : '<tr><td colspan="5"><div class="empty">No active intents</div></td></tr>';
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// ──── Dream ────────────────────────────────────────────────────────────
|
|
1808
|
+
async function refreshDream() {
|
|
1809
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
1810
|
+
const s = hive?.modules.dream ?? {};
|
|
1811
|
+
document.getElementById('dream-stats').innerHTML = \`<div class="row"><span class="k">Total</span><span class="v">\${s.totalDreams ?? 0}</span></div><div class="row"><span class="k">Actionable</span><span class="v good">\${s.actionableCount ?? 0}</span></div><div class="row"><span class="k">Acknowledged</span><span class="v">\${s.acknowledgedCount ?? 0}</span></div><div class="row"><span class="k">Avg confidence</span><span class="v">\${((s.avgConfidence ?? 0) * 100).toFixed(0)}%</span></div>\`;
|
|
1812
|
+
if (s.byType) drawDreamChart(s.byType);
|
|
1813
|
+
const dreams = await api('/api/v6/dream-list') ?? [];
|
|
1814
|
+
document.getElementById('dream-list').innerHTML = dreams.length ? dreams.map(d =>
|
|
1815
|
+
\`<tr><td><span class="pill purple">\${d.type}</span></td><td>\${(d.content||'').slice(0,160)}</td><td>\${((d.confidence||0)*100).toFixed(0)}%</td><td>\${d.actOnNextSession ? '<span class="pill green">yes</span>' : '—'}</td><td><button class="btn sm" onclick="ackDream('\${d.id}')">Ack</button></td></tr>\`
|
|
1816
|
+
).join('') : '<tr><td colspan="5"><div class="empty">No dreams yet. Run a cycle.</div></td></tr>';
|
|
1817
|
+
}
|
|
1818
|
+
async function runDream() { const d = await api('/api/v6/dream-run', {}); if (d) { toast('Dreams generated', (d.length || 0) + ' new'); refreshDream(); } }
|
|
1819
|
+
async function startDreamLoop() { await api('/api/v6/dream-start', {}); toast('Dream loop', 'Background reflection active'); }
|
|
1820
|
+
async function ackDream(id) { await api('/api/v6/dream-ack', { id }); refreshDream(); }
|
|
1821
|
+
|
|
1822
|
+
// ──── Reputation ───────────────────────────────────────────────────────
|
|
1823
|
+
async function refreshReputation() {
|
|
1824
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
1825
|
+
const s = hive?.modules.reputation ?? {};
|
|
1826
|
+
document.getElementById('reputation-stats').innerHTML = \`<div class="row"><span class="k">Total agents</span><span class="v">\${s.totalAgents ?? 0}</span></div><div class="row"><span class="k">Total receipts</span><span class="v">\${s.totalReceipts ?? 0}</span></div><div class="row"><span class="k">Verified</span><span class="v good">\${s.verifiedReceipts ?? 0}</span></div><div class="row"><span class="k">Avg accuracy</span><span class="v">\${((s.averageAccuracy ?? 0) * 100).toFixed(1)}%</span></div>\`;
|
|
1827
|
+
const top = s.topAgents ?? [];
|
|
1828
|
+
document.getElementById('rep-leaderboard').innerHTML = top.length ? top.map(a =>
|
|
1829
|
+
\`<tr><td><span class="pill cyan">\${a.agentTool}</span></td><td>\${((a.accuracy ?? 0) * 100).toFixed(1)}%</td><td>\${a.decisions}</td><td class="mono">—</td></tr>\`
|
|
1830
|
+
).join('') : '<tr><td colspan="4"><div class="empty">No signed decisions yet</div></td></tr>';
|
|
1831
|
+
}
|
|
1832
|
+
async function genBadge() {
|
|
1833
|
+
const res = await api('/api/v6/reputation-badge', { agentTool: document.getElementById('rep-agent').value, agentVersion: document.getElementById('rep-ver').value || undefined });
|
|
1834
|
+
document.getElementById('rep-badge-output').textContent = res?.badge || 'No reputation data yet. Sign a decision first via CLI: shadow-brain reputation sign --agent ... --ver ...';
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// ──── Debate ───────────────────────────────────────────────────────────
|
|
1838
|
+
async function runDebate() {
|
|
1839
|
+
const res = await api('/api/v6/debate-run', {
|
|
1840
|
+
question: document.getElementById('debate-question').value,
|
|
1841
|
+
context: document.getElementById('debate-context').value,
|
|
1842
|
+
turns: parseInt(document.getElementById('debate-turns').value || '2'),
|
|
1843
|
+
});
|
|
1844
|
+
if (res) {
|
|
1845
|
+
document.getElementById('debate-output').textContent = res.turns.map(t => '[' + t.position + '] ' + t.statement).join('\\n\\n') + '\\n\\nVERDICT: ' + res.verdict;
|
|
1846
|
+
toast('Debate complete', res.durationMs + 'ms');
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
// ──── Pre-mortem ───────────────────────────────────────────────────────
|
|
1851
|
+
async function runPremortem() {
|
|
1852
|
+
const res = await api('/api/v6/premortem-run', { taskDescription: document.getElementById('pm-task').value, projectDir: '' });
|
|
1853
|
+
if (!res) return;
|
|
1854
|
+
document.getElementById('pm-output').innerHTML = \`
|
|
1855
|
+
<div style="margin-bottom:10px">Risk: <strong style="font-size:16px;color:\${res.riskScore > 0.6 ? 'var(--danger)' : res.riskScore > 0.3 ? 'var(--warning)' : 'var(--success)'}">\${Math.round(res.riskScore * 100)}%</strong></div>
|
|
1856
|
+
<div class="mono mb-lg">\${res.summary}</div>
|
|
1857
|
+
\${res.failures.map(f => \`<div style="padding:12px;background:var(--bg-elev-2);border-left:3px solid var(--danger);margin-bottom:8px;border-radius:6px"><div style="display:flex;gap:6px;align-items:center;margin-bottom:4px"><span class="pill red">\${f.severity}</span><span class="pill">p=\${f.probability.toFixed(2)}</span><span class="pill">\${f.source}</span></div><div>\${f.description}</div><div class="mono" style="color:var(--text-2);margin-top:6px">→ \${f.mitigation}</div></div>\`).join('')}\`;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// ──── Attention ────────────────────────────────────────────────────────
|
|
1861
|
+
async function runAttention() {
|
|
1862
|
+
const ids = document.getElementById('att-memories').value.split(',').map(s => s.trim()).filter(Boolean);
|
|
1863
|
+
const res = await api('/api/v6/attention-heatmap', { decisionText: document.getElementById('att-decision').value, candidateMemoryIds: ids });
|
|
1864
|
+
if (res) document.getElementById('att-output').textContent = res.weights.map(w => (w.weight * 100).toFixed(1).padStart(5) + '% ' + '█'.repeat(Math.min(30, Math.round(w.weight * 50))) + ' [' + w.category + '] ' + (w.memoryContent || '').slice(0, 80)).join('\\n');
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
// ──── Tokens ───────────────────────────────────────────────────────────
|
|
1868
|
+
async function refreshTokens() {
|
|
1869
|
+
const res = await api('/api/v6/tokens-report');
|
|
1870
|
+
if (!res) return;
|
|
1871
|
+
document.getElementById('tok-total').textContent = '$' + (res.totalSpendUsd || 0).toFixed(4);
|
|
1872
|
+
document.getElementById('tok-proj').textContent = '$' + (res.monthlyProjectionUsd || 0).toFixed(2);
|
|
1873
|
+
document.getElementById('tok-save').textContent = '$' + (res.savingsOpportunitiesUsd || 0).toFixed(2);
|
|
1874
|
+
const calls = Object.values(res.byAgent || {}).reduce((a, b) => a + b.calls, 0);
|
|
1875
|
+
document.getElementById('tok-calls').textContent = calls;
|
|
1876
|
+
document.getElementById('tok-calls-sub').textContent = \`\${(res.totalInputTokens || 0).toLocaleString()} in · \${(res.totalOutputTokens || 0).toLocaleString()} out\`;
|
|
1877
|
+
if (res.byModel) drawTokensModelChart(res.byModel);
|
|
1878
|
+
document.getElementById('tok-suggestions').innerHTML = (res.suggestions || []).map(s => \`<div style="padding:8px 0;border-bottom:1px solid var(--border);display:flex;gap:8px"><span style="color:var(--success);flex-shrink:0">💡</span><span style="font-size:12px">\${s}</span></div>\`).join('') || '<div class="empty">Running lean — no suggestions</div>';
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// ──── Forget ───────────────────────────────────────────────────────────
|
|
1882
|
+
async function refreshForget() {
|
|
1883
|
+
drawForgetChart([]);
|
|
1884
|
+
}
|
|
1885
|
+
async function runForget() {
|
|
1886
|
+
const r = await api('/api/v6/forget-consolidate', {});
|
|
1887
|
+
if (!r) return;
|
|
1888
|
+
document.getElementById('forget-output').innerHTML = \`
|
|
1889
|
+
<div class="row"><span class="k">Cycle</span><span class="v">\${r.cycle}</span></div>
|
|
1890
|
+
<div class="row"><span class="k">Promoted</span><span class="v good">\${r.promoted}</span></div>
|
|
1891
|
+
<div class="row"><span class="k">Demoted</span><span class="v">\${r.demoted}</span></div>
|
|
1892
|
+
<div class="row"><span class="k">Forgotten</span><span class="v bad">\${r.forgotten}</span></div>
|
|
1893
|
+
<div class="row"><span class="k">Strengthened</span><span class="v good">\${r.strengthened}</span></div>
|
|
1894
|
+
<div class="row"><span class="k">Duration</span><span class="v">\${r.durationMs}ms</span></div>\`;
|
|
1895
|
+
toast('Consolidation cycle', 'cycle #' + r.cycle);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// ──── Formal ───────────────────────────────────────────────────────────
|
|
1899
|
+
async function genFormal() {
|
|
1900
|
+
const res = await api('/api/v6/formal-generate', { text: document.getElementById('formal-text').value });
|
|
1901
|
+
if (res) { document.getElementById('formal-output').textContent = JSON.stringify(res, null, 2); toast('Rule generated'); }
|
|
1902
|
+
}
|
|
1903
|
+
async function exportEslint() {
|
|
1904
|
+
const res = await fetch('/api/v6/formal-export-eslint').then(r => r.text());
|
|
1905
|
+
document.getElementById('formal-export').textContent = res;
|
|
1906
|
+
}
|
|
1907
|
+
async function exportSemgrep() {
|
|
1908
|
+
const res = await fetch('/api/v6/formal-export-semgrep').then(r => r.text());
|
|
1909
|
+
document.getElementById('formal-export').textContent = res;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// ──── Calibration ──────────────────────────────────────────────────────
|
|
1913
|
+
async function refreshCalibration() {
|
|
1914
|
+
const scores = await api('/api/v6/calibration-scores') ?? [];
|
|
1915
|
+
document.getElementById('calibration-table').innerHTML = scores.length ? scores.map(s =>
|
|
1916
|
+
\`<tr><td><span class="pill cyan">\${s.agentTool}</span></td><td>\${s.category}</td><td>\${s.brierScore}</td><td>\${s.calibrationError}</td><td>\${(s.overconfidenceRatio * 100).toFixed(0)}%</td><td class="v \${s.trustWeight > 0.8 ? 'good' : s.trustWeight > 0.5 ? '' : 'warn'}">\${s.trustWeight}</td><td>\${s.sampleSize}</td></tr>\`
|
|
1917
|
+
).join('') : '<tr><td colspan="7"><div class="empty">No calibration data yet</div></td></tr>';
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
// ──── Branch ───────────────────────────────────────────────────────────
|
|
1921
|
+
async function refreshBranch() {
|
|
1922
|
+
const r = await api('/api/v6/branch-state');
|
|
1923
|
+
if (!r) return;
|
|
1924
|
+
document.getElementById('branch-state').innerHTML = \`
|
|
1925
|
+
<div class="row"><span class="k">Current branch</span><span class="v"><code>\${r.currentBranch}</code></span></div>
|
|
1926
|
+
<div class="row"><span class="k">Branch memories</span><span class="v">\${r.branchMemoryCount}</span></div>
|
|
1927
|
+
<div class="row"><span class="k">Global memories</span><span class="v">\${r.globalMemoryCount}</span></div>
|
|
1928
|
+
<div class="row"><span class="k">Active IDs</span><span class="v">\${r.activeMemoryIds.length}</span></div>
|
|
1929
|
+
<div class="row"><span class="k">Categories</span><span class="v mono">\${r.branchSpecificCategories.join(', ') || '—'}</span></div>\`;
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
// ──── Privacy ──────────────────────────────────────────────────────────
|
|
1933
|
+
async function refreshPrivacy() {
|
|
1934
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
1935
|
+
const a = hive?.modules.airGap ?? {};
|
|
1936
|
+
document.getElementById('airgap-state').innerHTML = \`
|
|
1937
|
+
<div class="row"><span class="k">Enabled</span><span class="v \${a.enabled ? 'good' : ''}">\${a.enabled ? 'YES' : 'no'}</span></div>
|
|
1938
|
+
<div class="row"><span class="k">Policy</span><span class="v">\${a.policy || '—'}</span></div>
|
|
1939
|
+
<div class="row"><span class="k">Blocked outbound</span><span class="v bad">\${a.blockedOutboundCount ?? 0}</span></div>
|
|
1940
|
+
<div class="row"><span class="k">Allowed local</span><span class="v good">\${a.allowedLocalCount ?? 0}</span></div>\`;
|
|
1941
|
+
const qlist = await api('/api/v6/quarantine-list?pendingOnly=true') ?? [];
|
|
1942
|
+
document.getElementById('quarantine-state').innerHTML = \`<div class="row"><span class="k">Pending</span><span class="v">\${qlist.length}</span></div><div class="row"><span class="k">Auto-delete after</span><span class="v">7 days</span></div>\`;
|
|
1943
|
+
document.getElementById('quarantine-list').innerHTML = qlist.length ? qlist.map(e =>
|
|
1944
|
+
\`<tr><td><code>\${e.id.slice(0,10)}</code></td><td><span class="pill">\${e.source}</span></td><td>\${(e.claim||'').slice(0,100)}</td><td class="mono">\${e.reasonFlagged}</td><td style="text-align:right"><button class="btn sm success">Promote</button> <button class="btn sm danger">Reject</button></td></tr>\`
|
|
1945
|
+
).join('') : '<tr><td colspan="5"><div class="empty">No quarantined claims</div></td></tr>';
|
|
1946
|
+
}
|
|
1947
|
+
async function enableAirGap() { await api('/api/v6/airgap-enable', { policy: 'strict' }); toast('Air-gap enabled','strict mode'); refresh(); refreshPrivacy(); }
|
|
1948
|
+
async function disableAirGap() { await api('/api/v6/airgap-disable', {}); toast('Air-gap disabled','', 'warn'); refresh(); refreshPrivacy(); }
|
|
1949
|
+
async function toggleAirGap() { const hive = state.hive || await api('/api/v6/hive-status'); if (hive?.modules.airGap?.enabled) disableAirGap(); else enableAirGap(); }
|
|
1950
|
+
|
|
1951
|
+
// ──── Voice ────────────────────────────────────────────────────────────
|
|
1952
|
+
async function sendVoice() {
|
|
1953
|
+
const r = await api('/api/v6/voice-process', { transcript: document.getElementById('voice-input').value });
|
|
1954
|
+
if (r) document.getElementById('voice-output').textContent = \`Intent: \${r.intent}\\n\\n\${r.response}\`;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
// ──── Garden ───────────────────────────────────────────────────────────
|
|
1958
|
+
async function refreshGarden() {
|
|
1959
|
+
const snap = await api('/api/v6/garden-snapshot?limit=200') ?? [];
|
|
1960
|
+
const stats = await api('/api/v6/garden-stats') ?? { nodes: 0, avgBloom: 0, linkedFraction: 0 };
|
|
1961
|
+
document.getElementById('garden-stats').textContent = \`Nodes: \${stats.nodes} · avg bloom: \${stats.avgBloom} · linked: \${Math.round((stats.linkedFraction||0) * 100)}%\`;
|
|
1962
|
+
drawGarden(snap);
|
|
1963
|
+
}
|
|
1964
|
+
function drawGarden(nodes) {
|
|
1965
|
+
const c = document.getElementById('garden-canvas');
|
|
1966
|
+
if (!c) return;
|
|
1967
|
+
const ctx = c.getContext('2d'); const W = c.width, H = c.height;
|
|
1968
|
+
ctx.clearRect(0, 0, W, H);
|
|
1969
|
+
if (!nodes.length) { ctx.fillStyle = '#556080'; ctx.font = '14px "JetBrains Mono", monospace'; ctx.textAlign = 'center'; ctx.fillText('No garden yet. Let the brain grow.', W/2, H/2); return; }
|
|
1970
|
+
const pos = nodes.map((n, i) => {
|
|
1971
|
+
const a = i / nodes.length * Math.PI * 2;
|
|
1972
|
+
const r = 110 + (1 - n.bloom) * 170 + Math.sin(i * 1.3) * 30;
|
|
1973
|
+
return { x: W/2 + Math.cos(a) * r, y: H/2 + Math.sin(a) * r * 0.75, node: n };
|
|
1974
|
+
});
|
|
1975
|
+
ctx.strokeStyle = 'rgba(24,255,255,0.1)'; ctx.lineWidth = 1;
|
|
1976
|
+
for (const p of pos) for (const c of p.node.connections) { const t = pos.find(x => x.node.id === c); if (t) { ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(t.x, t.y); ctx.stroke(); } }
|
|
1977
|
+
for (const p of pos) {
|
|
1978
|
+
const col = p.node.kind === 'decision' ? '#a855f7' : p.node.kind === 'pattern' ? '#18ffff' : '#10e68c';
|
|
1979
|
+
const rad = 4 + p.node.bloom * 9;
|
|
1980
|
+
const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, rad * 2.5);
|
|
1981
|
+
grd.addColorStop(0, col + 'cc'); grd.addColorStop(1, col + '00');
|
|
1982
|
+
ctx.fillStyle = grd; ctx.beginPath(); ctx.arc(p.x, p.y, rad * 2.5, 0, Math.PI * 2); ctx.fill();
|
|
1983
|
+
ctx.fillStyle = col; ctx.globalAlpha = Math.max(0.4, p.node.bloom);
|
|
1984
|
+
ctx.beginPath(); ctx.arc(p.x, p.y, rad, 0, Math.PI * 2); ctx.fill();
|
|
1985
|
+
ctx.globalAlpha = 1;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// ──── PR Review ────────────────────────────────────────────────────────
|
|
1990
|
+
async function genPRReview() {
|
|
1991
|
+
const r = await api('/api/v6/pr-review-generate', {
|
|
1992
|
+
repo: document.getElementById('pr-repo').value,
|
|
1993
|
+
prNumber: parseInt(document.getElementById('pr-num').value || '0'),
|
|
1994
|
+
changedFiles: document.getElementById('pr-files').value.split(',').map(s => s.trim()).filter(Boolean),
|
|
1995
|
+
diffSummary: document.getElementById('pr-diff').value,
|
|
1996
|
+
});
|
|
1997
|
+
if (r) { document.getElementById('pr-output').textContent = r.body; toast('Review generated'); }
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
// ──── Team ─────────────────────────────────────────────────────────────
|
|
2001
|
+
async function refreshTeam() {
|
|
2002
|
+
const self = await api('/api/v6/team-self') ?? {};
|
|
2003
|
+
const peers = await api('/api/v6/team-peers') ?? [];
|
|
2004
|
+
document.getElementById('team-self').innerHTML = \`<div class="row"><span class="k">Peer ID</span><span class="v mono">\${self.peerId || '—'}</span></div><div class="row"><span class="k">Display name</span><span class="v">\${self.displayName || '—'}</span></div>\`;
|
|
2005
|
+
document.getElementById('team-peers').innerHTML = peers.length ? peers.map(p => \`<div class="row"><span class="k">\${p.displayName}</span><span class="v mono">\${p.peerId}</span></div>\`).join('') : '<div class="empty">No peers connected. Connect via WebRTC.</div>';
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
// ──── Exchange ─────────────────────────────────────────────────────────
|
|
2009
|
+
async function refreshExchange() {
|
|
2010
|
+
const packages = await api('/api/v6/exchange-list') ?? [];
|
|
2011
|
+
document.getElementById('ex-list').innerHTML = packages.length ? packages.map(p => \`<div class="row"><span class="k"><code>\${p.name}</code><br/><span style="font-size:10px;color:var(--text-3)">by \${p.author}</span></span><span class="v">\${p.memoryCount} memories</span></div>\`).join('') : '<div class="empty">No local packages yet</div>';
|
|
2012
|
+
}
|
|
2013
|
+
async function exportPackage() {
|
|
2014
|
+
const res = await api('/api/v6/exchange-export', {
|
|
2015
|
+
name: document.getElementById('ex-name').value || 'untitled',
|
|
2016
|
+
description: document.getElementById('ex-desc').value,
|
|
2017
|
+
author: document.getElementById('ex-author').value,
|
|
2018
|
+
tags: document.getElementById('ex-tags').value.split(',').map(s => s.trim()).filter(Boolean),
|
|
2019
|
+
});
|
|
2020
|
+
if (res) { document.getElementById('ex-out').textContent = 'Saved → ' + res.filePath; toast('Package exported'); refreshExchange(); }
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
// ──── Features ─────────────────────────────────────────────────────────
|
|
2024
|
+
async function refreshFeatures() {
|
|
2025
|
+
const cfg = (await api('/api/v6/features-config')) ?? {};
|
|
2026
|
+
document.getElementById('feature-grid').innerHTML = FEATURES.map(([id, name, desc]) => {
|
|
2027
|
+
const on = cfg[id] !== false; // default enabled
|
|
2028
|
+
return \`<div class="feature-toggle" id="ft-\${id}">
|
|
2029
|
+
<div class="icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></div>
|
|
2030
|
+
<div class="meta"><div class="name">\${name}</div><div class="desc">\${desc}</div></div>
|
|
2031
|
+
<label class="switch"><input type="checkbox" \${on ? 'checked' : ''} data-feature="\${id}"><span class="slider"></span></label>
|
|
2032
|
+
</div>\`;
|
|
2033
|
+
}).join('');
|
|
2034
|
+
// Bind listeners cleanly so the toggle always fires
|
|
2035
|
+
document.querySelectorAll('#feature-grid input[data-feature]').forEach(el => {
|
|
2036
|
+
el.addEventListener('change', async e => {
|
|
2037
|
+
const id = e.target.getAttribute('data-feature');
|
|
2038
|
+
const on = e.target.checked;
|
|
2039
|
+
const res = await api('/api/v6/features-config', { [id]: on });
|
|
2040
|
+
toast(on ? 'Enabled' : 'Disabled', id, on ? 'success' : 'warn');
|
|
2041
|
+
if (!res) { e.target.checked = !on; /* revert on error */ }
|
|
2042
|
+
});
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
async function toggleFeature(id, on) {
|
|
2046
|
+
const res = await api('/api/v6/features-config', { [id]: on });
|
|
2047
|
+
if (res) toast(on ? 'Enabled' : 'Disabled', id, on ? 'success' : 'warn');
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
// ──── Models & Intelligence ────────────────────────────────────────────
|
|
2051
|
+
let _providers = [];
|
|
2052
|
+
let _allModels = {};
|
|
2053
|
+
async function refreshModels() {
|
|
2054
|
+
const data = await api('/api/v6/providers');
|
|
2055
|
+
if (!data) return;
|
|
2056
|
+
_providers = data.providers ?? [];
|
|
2057
|
+
|
|
2058
|
+
// Discovered agents section
|
|
2059
|
+
document.getElementById('discovered-agents').innerHTML = data.discovered.length
|
|
2060
|
+
? data.discovered.map(d => \`<div class="row"><span class="k"><span class="pill purple">\${d.agent}</span> → <span class="pill cyan">\${d.provider}</span> <span class="tag-code">\${d.model || '(none)'}</span></span><span class="v mono" style="color:var(--text-3)" title="\${d.sourcePath}">\${d.apiKey ? '🔑 API key available' : 'no API key found'}</span></div>\`).join('')
|
|
2061
|
+
: '<div class="empty">No agent configs found yet. Install Claude Code / Cursor / Cline / Codex / Aider / Kilo / Roo / OpenCode, or add a custom path below.</div>';
|
|
2062
|
+
|
|
2063
|
+
// Provider cards
|
|
2064
|
+
document.getElementById('providers-grid').innerHTML = _providers.map(p => {
|
|
2065
|
+
const okClass = p.apiKey ? 'good' : p.id === 'ollama' ? '' : 'warn';
|
|
2066
|
+
return \`<div class="card" style="padding:14px">
|
|
2067
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px"><div style="font-weight:700;font-size:13px">\${p.displayName}</div><span class="pill \${p.enabled ? 'green' : ''}">\${p.enabled ? 'enabled' : 'off'}</span></div>
|
|
2068
|
+
<div class="field" style="margin-bottom:6px"><label>API Key \${p.source === 'agent-discovered' ? '(from ' + (p.discoveredFrom || 'agent') + ')' : p.source === 'env' ? '(env)' : ''}</label><input type="password" id="pkey-\${p.id}" placeholder="\${p.apiKey ? '••••••••••' : 'sk-...'}" value="\${p.apiKey || ''}"/></div>
|
|
2069
|
+
<div class="field" style="margin-bottom:6px"><label>Default Model</label><input id="pmodel-\${p.id}" value="\${p.defaultModel || ''}"/></div>
|
|
2070
|
+
<div class="flex gap">
|
|
2071
|
+
<button class="btn sm primary" onclick="saveProvider('\${p.id}')">Save</button>
|
|
2072
|
+
<button class="btn sm" onclick="testProvider('\${p.id}')">Test</button>
|
|
2073
|
+
<button class="btn sm" onclick="fetchProviderModels('\${p.id}')">Fetch Models</button>
|
|
2074
|
+
</div>
|
|
2075
|
+
<div class="mono mt" id="pmodels-\${p.id}" style="max-height:120px;overflow-y:auto;font-size:10.5px;color:var(--text-3)"></div>
|
|
2076
|
+
</div>\`;
|
|
2077
|
+
}).join('');
|
|
2078
|
+
|
|
2079
|
+
// Intelligence config
|
|
2080
|
+
const intel = await api('/api/v6/intelligence');
|
|
2081
|
+
if (intel) {
|
|
2082
|
+
const provSel = document.getElementById('intel-provider');
|
|
2083
|
+
provSel.innerHTML = _providers.map(p => \`<option value="\${p.id}" \${intel.leadProvider === p.id ? 'selected' : ''}>\${p.displayName}</option>\`).join('');
|
|
2084
|
+
// Populate model field
|
|
2085
|
+
const modelInput = document.getElementById('intel-model');
|
|
2086
|
+
modelInput.innerHTML = \`<option value="\${intel.leadModel}">\${intel.leadModel}</option>\`;
|
|
2087
|
+
document.getElementById('intel-agent-proxy').checked = intel.useAgentToolModels;
|
|
2088
|
+
document.getElementById('intel-local-first').checked = intel.preferLocalFirst;
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
// Custom agents
|
|
2092
|
+
const ca = await api('/api/v6/custom-agents');
|
|
2093
|
+
const agents = ca?.agents ?? [];
|
|
2094
|
+
document.getElementById('custom-agents-list').innerHTML = agents.length
|
|
2095
|
+
? agents.map((a, i) => \`<div class="row"><span class="k"><span class="pill purple">\${a.name}</span> <span class="tag-code">\${a.path}</span></span><span class="v"><button class="btn sm danger" onclick="removeCustomAgent(\${i})">Remove</button></span></div>\`).join('')
|
|
2096
|
+
: '<div class="empty">No custom agent paths yet</div>';
|
|
2097
|
+
}
|
|
2098
|
+
async function saveProvider(id) {
|
|
2099
|
+
const apiKey = document.getElementById('pkey-' + id).value;
|
|
2100
|
+
const defaultModel = document.getElementById('pmodel-' + id).value;
|
|
2101
|
+
const r = await api('/api/v6/provider-save', { provider: id, apiKey: apiKey || undefined, defaultModel, enabled: true });
|
|
2102
|
+
if (r) { toast('Saved', id); refreshModels(); }
|
|
2103
|
+
}
|
|
2104
|
+
async function testProvider(id) {
|
|
2105
|
+
const r = await api('/api/v6/provider-test', { provider: id });
|
|
2106
|
+
toast(r?.ok ? 'Connected' : 'Failed', r?.ok ? (r.modelCount + ' models available') : (r?.error || 'Unknown error'), r?.ok ? 'success' : 'error');
|
|
2107
|
+
}
|
|
2108
|
+
async function fetchProviderModels(id) {
|
|
2109
|
+
const models = await api('/api/v6/provider-models', { provider: id });
|
|
2110
|
+
if (!models) return;
|
|
2111
|
+
_allModels[id] = models;
|
|
2112
|
+
document.getElementById('pmodels-' + id).innerHTML = models.length
|
|
2113
|
+
? models.slice(0, 50).map(m => \`<div style="padding:3px 0"><span class="tag-code">\${m.id}</span>\${m.tags.length ? ' <span style="color:var(--text-dim)">' + m.tags.join(' · ') + '</span>' : ''}</div>\`).join('')
|
|
2114
|
+
: '<div>No models (check API key + network)</div>';
|
|
2115
|
+
toast('Fetched', models.length + ' models from ' + id);
|
|
2116
|
+
}
|
|
2117
|
+
async function saveIntelligence() {
|
|
2118
|
+
const provId = document.getElementById('intel-provider').value;
|
|
2119
|
+
const model = document.getElementById('intel-model').value;
|
|
2120
|
+
const useAgent = document.getElementById('intel-agent-proxy').checked;
|
|
2121
|
+
const local = document.getElementById('intel-local-first').checked;
|
|
2122
|
+
const r = await api('/api/v6/intelligence', {
|
|
2123
|
+
leadProvider: provId, leadModel: model,
|
|
2124
|
+
useAgentToolModels: useAgent, preferLocalFirst: local,
|
|
2125
|
+
});
|
|
2126
|
+
document.getElementById('intel-result').textContent = r ? '✓ Intelligence config saved · lead: ' + provId + ' / ' + model : '✗ Save failed';
|
|
2127
|
+
if (r) toast('Intelligence updated');
|
|
2128
|
+
}
|
|
2129
|
+
async function addCustomAgent() {
|
|
2130
|
+
const name = document.getElementById('ca-name').value.trim();
|
|
2131
|
+
const p = document.getElementById('ca-path').value.trim();
|
|
2132
|
+
if (!name || !p) return toast('Missing', 'Name + path required', 'warn');
|
|
2133
|
+
const cur = await api('/api/v6/custom-agents');
|
|
2134
|
+
const agents = [...(cur?.agents || []), { name, path: p }];
|
|
2135
|
+
await api('/api/v6/custom-agents', { agents });
|
|
2136
|
+
document.getElementById('ca-name').value = ''; document.getElementById('ca-path').value = '';
|
|
2137
|
+
toast('Added', name); refreshModels();
|
|
2138
|
+
}
|
|
2139
|
+
async function removeCustomAgent(i) {
|
|
2140
|
+
const cur = await api('/api/v6/custom-agents');
|
|
2141
|
+
const agents = (cur?.agents || []).filter((_, idx) => idx !== i);
|
|
2142
|
+
await api('/api/v6/custom-agents', { agents });
|
|
2143
|
+
toast('Removed', '', 'warn'); refreshModels();
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
// ──── Chat with Brain ──────────────────────────────────────────────────
|
|
2147
|
+
let currentConversationId = null;
|
|
2148
|
+
async function refreshChat() {
|
|
2149
|
+
const list = await api('/api/v6/chat-conversations');
|
|
2150
|
+
const el = document.getElementById('chat-conversations');
|
|
2151
|
+
el.innerHTML = (list || []).map(c => \`<div class="row" onclick="loadConv('\${c.id}')" style="cursor:pointer"><span class="k" style="min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">\${c.preview || '(new)'}</span><span class="v mono" style="font-size:10px">\${c.turnCount}</span></div>\`).join('') || '<div class="empty">No chats yet</div>';
|
|
2152
|
+
if (!currentConversationId) renderChatMessages([]);
|
|
2153
|
+
}
|
|
2154
|
+
async function loadConv(id) { currentConversationId = id; const turns = await api('/api/v6/chat-conversation', { conversationId: id }) ?? []; renderChatMessages(turns); }
|
|
2155
|
+
function newChat() { currentConversationId = null; renderChatMessages([]); document.getElementById('chat-input').focus(); }
|
|
2156
|
+
async function clearAllChats() { if (!confirm('Clear ALL chat history?')) return; await api('/api/v6/chat-clear-all', {}); currentConversationId = null; refreshChat(); renderChatMessages([]); toast('Chat history cleared','','warn'); }
|
|
2157
|
+
function renderChatMessages(turns) {
|
|
2158
|
+
const el = document.getElementById('chat-messages');
|
|
2159
|
+
if (!turns.length) { el.innerHTML = '<div class="empty"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>Ask your brain anything.<br/>It will search memories + cite them.</div>'; return; }
|
|
2160
|
+
el.innerHTML = turns.map(t => {
|
|
2161
|
+
const isUser = t.role === 'user';
|
|
2162
|
+
const cites = (t.citations || []).length ? \`<div style="margin-top:8px;padding:8px;background:var(--bg-elev-1);border-radius:6px"><div style="font-size:10px;color:var(--text-3);margin-bottom:4px;text-transform:uppercase;letter-spacing:0.04em">Citations (\${t.citations.length})</div>\${t.citations.map((c, i) => \`<div style="padding:4px 0;border-bottom:1px solid var(--border);font-size:11px"><span class="pill cyan">[\${i+1}]</span> <span class="pill">\${c.agent}/\${c.category}</span> \${c.content.slice(0, 180)} <span style="color:var(--text-dim)">(\${(c.score*100).toFixed(0)}%)</span></div>\`).join('')}</div>\` : '';
|
|
2163
|
+
const meta = !isUser && t.provider ? \`<div class="mono" style="font-size:10px;color:var(--text-3);margin-top:6px">\${t.provider}/\${t.model || ''} · \${t.tokensUsed || 0} tok\${t.cached ? ' · cached' : ''}</div>\` : '';
|
|
2164
|
+
return \`<div style="display:flex;gap:10px;margin-bottom:14px;\${isUser ? '' : 'background:var(--bg-elev-1);padding:12px;border-radius:10px'}"><div style="width:28px;height:28px;border-radius:50%;background:\${isUser ? 'var(--brand-gradient)' : 'var(--bg-elev-3)'};display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:11px;font-weight:700">\${isUser ? 'You' : '🧠'}</div><div style="flex:1;min-width:0"><div style="white-space:pre-wrap;font-size:13px;line-height:1.5">\${(t.content || '').replace(/\\</g, '<')}</div>\${cites}\${meta}</div></div>\`;
|
|
2165
|
+
}).join('');
|
|
2166
|
+
el.scrollTop = el.scrollHeight;
|
|
2167
|
+
}
|
|
2168
|
+
async function sendChat() {
|
|
2169
|
+
const input = document.getElementById('chat-input');
|
|
2170
|
+
const question = input.value.trim();
|
|
2171
|
+
if (!question) return;
|
|
2172
|
+
input.value = '';
|
|
2173
|
+
const btn = document.getElementById('chat-send-btn');
|
|
2174
|
+
btn.disabled = true;
|
|
2175
|
+
document.getElementById('chat-status').textContent = 'Searching memories + calling LLM...';
|
|
2176
|
+
const turns = currentConversationId ? (await api('/api/v6/chat-conversation', { conversationId: currentConversationId }) ?? []) : [];
|
|
2177
|
+
turns.push({ role: 'user', content: question, createdAt: new Date() });
|
|
2178
|
+
renderChatMessages(turns);
|
|
2179
|
+
try {
|
|
2180
|
+
const res = await api('/api/v6/chat', { question, conversationId: currentConversationId, maxCitations: 6 });
|
|
2181
|
+
if (res) {
|
|
2182
|
+
currentConversationId = res.conversationId;
|
|
2183
|
+
const updated = await api('/api/v6/chat-conversation', { conversationId: currentConversationId }) ?? [];
|
|
2184
|
+
renderChatMessages(updated);
|
|
2185
|
+
refreshChat();
|
|
2186
|
+
document.getElementById('chat-status').textContent = 'Answered in ' + (res.ms || '?') + 'ms · ' + (res.citations?.length || 0) + ' citations';
|
|
2187
|
+
}
|
|
2188
|
+
} finally {
|
|
2189
|
+
btn.disabled = false;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// ──── Stop button ──────────────────────────────────────────────────────
|
|
2194
|
+
async function stopBrain() {
|
|
2195
|
+
if (!confirm('Stop Shadow Brain?\\n\\nThe npm process will exit. You will need to restart it from your terminal.')) return;
|
|
2196
|
+
try {
|
|
2197
|
+
await fetch('/api/v6/shutdown', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' });
|
|
2198
|
+
} catch { /* server is shutting down */ }
|
|
2199
|
+
toast('Shutting down', 'Shadow Brain is stopping...', 'warn');
|
|
2200
|
+
setTimeout(() => { document.body.style.filter = 'grayscale(1) brightness(0.4)'; document.body.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100vh;color:#888;font-family:ui-monospace,monospace;font-size:16px;flex-direction:column;gap:16px"><div>⏻ Shadow Brain stopped</div><div style="font-size:12px">Restart with: <code style="background:#222;padding:4px 8px;border-radius:4px">npm run dev</code> or <code style="background:#222;padding:4px 8px;border-radius:4px">shadow-brain dash .</code></div></div>'; }, 1500);
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
// When the intel provider changes, re-fetch its models to populate the model dropdown
|
|
2204
|
+
document.addEventListener('change', async e => {
|
|
2205
|
+
if (e.target && e.target.id === 'intel-provider') {
|
|
2206
|
+
const id = e.target.value;
|
|
2207
|
+
const models = _allModels[id] || await api('/api/v6/provider-models', { provider: id });
|
|
2208
|
+
_allModels[id] = models || [];
|
|
2209
|
+
const sel = document.getElementById('intel-model');
|
|
2210
|
+
if (sel) sel.innerHTML = (models || []).map(m => \`<option value="\${m.id}">\${m.displayName}</option>\`).join('') || '<option>no models available</option>';
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
|
|
2214
|
+
// ──── Config ───────────────────────────────────────────────────────────
|
|
2215
|
+
async function refreshConfig() {
|
|
2216
|
+
const hive = state.hive || await api('/api/v6/hive-status');
|
|
2217
|
+
if (!hive) return;
|
|
2218
|
+
document.getElementById('config-state').innerHTML = \`
|
|
2219
|
+
<div class="row"><span class="k">Version</span><span class="v">\${hive.version}</span></div>
|
|
2220
|
+
<div class="row"><span class="k">Local-first</span><span class="v \${hive.localFirst ? 'good' : 'warn'}">\${hive.localFirst ? 'YES' : 'no'}</span></div>
|
|
2221
|
+
<div class="row"><span class="k">Agents connected</span><span class="v">\${hive.totalAgentsConnected}</span></div>
|
|
2222
|
+
<div class="row"><span class="k">Memories stored</span><span class="v">\${hive.totalMemoriesStored}</span></div>
|
|
2223
|
+
<div class="row"><span class="k">Modules active</span><span class="v good">\${Object.keys(hive.modules || {}).length}</span></div>\`;
|
|
2224
|
+
}
|
|
2225
|
+
async function saveConfig() { const r = await api('/api/v6/config-save', { provider: document.getElementById('cfg-provider').value, apiKey: document.getElementById('cfg-apikey').value || undefined, model: document.getElementById('cfg-model').value || undefined }); document.getElementById('cfg-result').textContent = r?.ok ? '✓ Saved' : '✗ Failed'; if (r?.ok) toast('Config saved'); }
|
|
2226
|
+
async function testConnection() { const r = await api('/api/v6/config-test', { provider: document.getElementById('cfg-provider').value }); document.getElementById('cfg-result').textContent = r?.ok ? '✓ Connected' : '✗ ' + (r?.error || 'Failed'); toast(r?.ok ? 'Connection OK' : 'Connection failed', r?.error || '', r?.ok ? 'success' : 'error'); }
|
|
2227
|
+
async function startMCP() { const r = await api('/api/v6/mcp-start', { port: parseInt(document.getElementById('mcp-port').value || '7342'), authToken: document.getElementById('mcp-token').value || undefined }); document.getElementById('mcp-result').textContent = r?.ok ? '✓ Started at :' + (r.port ?? 7342) : '✗ ' + (r?.error || 'Failed'); toast(r?.ok ? 'MCP started' : 'MCP failed', r?.error, r?.ok ? 'success' : 'error'); }
|
|
2228
|
+
async function stopMCP() { const r = await api('/api/v6/mcp-stop', {}); document.getElementById('mcp-result').textContent = r?.ok ? '✓ Stopped' : '✗ ' + (r?.error || 'Failed'); toast('MCP stopped', '', 'warn'); }
|
|
2229
|
+
|
|
2230
|
+
// ──── Agents ───────────────────────────────────────────────────────────
|
|
2231
|
+
async function refreshAgents() {
|
|
2232
|
+
const list = await api('/api/v6/agents-list', {}) ?? [];
|
|
2233
|
+
document.getElementById('badge-agents').textContent = list.filter(a => a.hookInstalled).length;
|
|
2234
|
+
document.getElementById('agents-table').innerHTML = list.map(a => \`
|
|
2235
|
+
<tr>
|
|
2236
|
+
<td><strong>\${a.displayName}</strong><br/><code style="color:var(--text-3);font-size:10px">\${a.name}</code></td>
|
|
2237
|
+
<td>\${a.detected ? '<span class="pill green">detected</span>' : '<span class="pill">not found</span>'}</td>
|
|
2238
|
+
<td>\${a.hookInstalled ? '<span class="pill cyan">active</span>' : '<span class="pill">not wired</span>'}</td>
|
|
2239
|
+
<td class="mono">\${a.hookPath ? a.hookPath.slice(0, 70) : '—'}</td>
|
|
2240
|
+
<td style="text-align:right">\${a.hookInstalled ? \`<button class="btn sm danger" onclick="detachOne('\${a.name}')">Disconnect</button>\` : \`<button class="btn sm success" onclick="attachOne('\${a.name}')">Connect</button>\`}</td>
|
|
2241
|
+
</tr>\`).join('') || '<tr><td colspan="5"><div class="empty">No agents detected</div></td></tr>';
|
|
2242
|
+
}
|
|
2243
|
+
async function attachOne(agent) { await api('/api/v6/agents-attach', { agent }); toast('Attached', agent); refreshAgents(); }
|
|
2244
|
+
async function detachOne(agent) { await api('/api/v6/agents-detach', { agent }); toast('Detached', agent, 'warn'); refreshAgents(); }
|
|
2245
|
+
async function attachAll() { const r = await api('/api/v6/agents-attach-all', {}); if (r) { toast('Attach All', (r.attached?.length ?? 0) + ' agents wired'); refreshAgents(); } }
|
|
2246
|
+
async function detachAll() { if (!confirm('Detach Shadow Brain hooks from ALL agents?')) return; const r = await api('/api/v6/agents-detach-all', {}); if (r) { toast('Detach All', (r.attached?.length ?? 0) + ' agents', 'warn'); refreshAgents(); } }
|
|
2247
|
+
|
|
2248
|
+
// ──── Memory browser ───────────────────────────────────────────────────
|
|
2249
|
+
async function searchMemory() {
|
|
2250
|
+
const r = await api('/api/v6/memory-browser', {
|
|
2251
|
+
query: document.getElementById('mem-query').value,
|
|
2252
|
+
agent: document.getElementById('mem-agent').value,
|
|
2253
|
+
category: document.getElementById('mem-category').value,
|
|
2254
|
+
minImportance: parseFloat(document.getElementById('mem-importance').value || '0'),
|
|
2255
|
+
});
|
|
2256
|
+
if (!r) return;
|
|
2257
|
+
document.getElementById('mem-results').innerHTML = r.length ? r.map(m =>
|
|
2258
|
+
\`<tr><td><code>\${m.id.slice(0, 10)}</code></td><td><span class="pill cyan">\${m.agentTool}</span></td><td>\${m.category}</td><td>\${(m.content || '').slice(0, 140)}</td><td>\${(m.importance || 0).toFixed(2)}</td><td class="mono">\${new Date(m.createdAt).toLocaleDateString()}</td></tr>\`
|
|
2259
|
+
).join('') : '<tr><td colspan="6"><div class="empty">No memories match your filters</div></td></tr>';
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// ──── Activity ─────────────────────────────────────────────────────────
|
|
2263
|
+
async function refreshActivity() {
|
|
2264
|
+
const filter = document.getElementById('act-filter').value;
|
|
2265
|
+
const log = await api('/api/v6/activity-log?limit=200') ?? [];
|
|
2266
|
+
const filtered = filter ? log.filter(e => e.type === filter) : log;
|
|
2267
|
+
document.getElementById('activity-table').innerHTML = filtered.length ? filtered.map(e => \`<tr><td class="mono">\${new Date(e.timestamp).toLocaleString()}</td><td><span class="pill purple">\${e.source}</span></td><td><span class="pill \${typeToPill(e.type)}">\${e.type}</span></td><td class="mono">\${(e.detail || '').slice(0, 200)}</td></tr>\`).join('') : '<tr><td colspan="4"><div class="empty">No activity</div></td></tr>';
|
|
2268
|
+
}
|
|
2269
|
+
document.getElementById('act-filter').addEventListener('change', refreshActivity);
|
|
2270
|
+
|
|
2271
|
+
// ──── Live Graph ───────────────────────────────────────────────────────
|
|
2272
|
+
let graphFrame = 0, graphNodes = [], graphEdges = [], graphSignals = [], graphTimer = null, graphPullTimer = null;
|
|
2273
|
+
const signalColors = { 'memory-write':'#10e68c','subagent-spawn':'#18ffff','dream':'#a855f7','revisit':'#a855f7','counterfactual':'#a855f7','consolidation':'#a855f7','contradiction':'#ff5864','pattern-discovery':'#10e68c','collision':'#ff5864','quarantine':'#ec4899','firewall-block':'#ffb53f' };
|
|
2274
|
+
async function startGraph() { await pullTopology(); if (graphTimer) cancelAnimationFrame(graphTimer); graphLoop(); if (graphPullTimer) clearInterval(graphPullTimer); graphPullTimer = setInterval(pullTopology, 3000); }
|
|
2275
|
+
async function pullTopology() {
|
|
2276
|
+
const topo = await api('/api/v6/topology', {});
|
|
2277
|
+
if (!topo) return;
|
|
2278
|
+
const canvas = document.getElementById('graph-canvas');
|
|
2279
|
+
if (!canvas) return;
|
|
2280
|
+
const W = canvas.width, H = canvas.height, cx = W/2, cy = H/2;
|
|
2281
|
+
const prev = new Map(graphNodes.map(n => [n.id, { x: n.x, y: n.y }]));
|
|
2282
|
+
graphNodes = [{ id:'brain', label:'HIVE MIND', x: cx, y: cy, r: 50, type:'brain', color:'#a855f7' }];
|
|
2283
|
+
(topo.agents || []).forEach((a, i, arr) => {
|
|
2284
|
+
const ang = (i / arr.length) * Math.PI * 2 - Math.PI / 2, r = 240;
|
|
2285
|
+
const p = prev.get(a.id);
|
|
2286
|
+
graphNodes.push({ id: a.id, label: a.displayName, x: p?.x ?? cx + Math.cos(ang) * r, y: p?.y ?? cy + Math.sin(ang) * r, targetX: cx + Math.cos(ang) * r, targetY: cy + Math.sin(ang) * r, r: a.connected ? 24 : 16, type:'agent', connected: a.connected, color: a.connected ? '#18ffff' : '#556080' });
|
|
2287
|
+
});
|
|
2288
|
+
(topo.subAgents || []).forEach((s, i, arr) => {
|
|
2289
|
+
const parent = graphNodes.find(n => n.id === s.parent), ang = (i / Math.max(arr.length,1)) * Math.PI * 2, r = 130;
|
|
2290
|
+
const p = prev.get(s.id);
|
|
2291
|
+
graphNodes.push({ id: s.id, label: (s.task || '').slice(0, 18), x: p?.x ?? (parent?.x ?? cx) + Math.cos(ang) * 40, y: p?.y ?? (parent?.y ?? cy) + Math.sin(ang) * 40, targetX: cx + Math.cos(ang) * r, targetY: cy + Math.sin(ang) * r, r: 12, type:'subagent', parent: s.parent, color: '#a855f7' });
|
|
2292
|
+
});
|
|
2293
|
+
graphEdges = [];
|
|
2294
|
+
for (const n of graphNodes) {
|
|
2295
|
+
if (n.type === 'agent') graphEdges.push({ from:'brain', to: n.id, active: n.connected });
|
|
2296
|
+
if (n.type === 'subagent') graphEdges.push({ from: n.parent ?? 'brain', to: n.id, active: true });
|
|
2297
|
+
}
|
|
2298
|
+
const now = Date.now();
|
|
2299
|
+
for (const e of (topo.events || [])) {
|
|
2300
|
+
const src = e.source ?? 'brain';
|
|
2301
|
+
const srcN = graphNodes.find(n => n.id === src) ?? graphNodes[0];
|
|
2302
|
+
const dstN = e.type === 'subagent-spawn' ? graphNodes.find(n => n.id === e.target) : graphNodes.find(n => n.id === 'brain');
|
|
2303
|
+
if (!srcN || !dstN) continue;
|
|
2304
|
+
graphSignals.push({ fromId: srcN.id, toId: dstN.id, type: e.type, color: signalColors[e.type] || '#18ffff', progress: 0, spawnedAt: now });
|
|
2305
|
+
}
|
|
2306
|
+
if (graphSignals.length > 100) graphSignals = graphSignals.slice(-100);
|
|
2307
|
+
document.getElementById('graph-meta').textContent = \`\${(topo.agents || []).length} agents · \${(topo.subAgents || []).length} sub-agents · \${graphSignals.length} signals\`;
|
|
2308
|
+
}
|
|
2309
|
+
function graphLoop() {
|
|
2310
|
+
const c = document.getElementById('graph-canvas');
|
|
2311
|
+
if (!c) return;
|
|
2312
|
+
const ctx = c.getContext('2d'), W = c.width, H = c.height;
|
|
2313
|
+
ctx.clearRect(0, 0, W, H);
|
|
2314
|
+
const grd = ctx.createRadialGradient(W/2, H/2, 40, W/2, H/2, 500);
|
|
2315
|
+
grd.addColorStop(0, 'rgba(168,85,247,0.08)'); grd.addColorStop(1, 'rgba(0,0,0,0)');
|
|
2316
|
+
ctx.fillStyle = grd; ctx.fillRect(0, 0, W, H);
|
|
2317
|
+
for (const n of graphNodes) if (n.targetX != null) { n.x += (n.targetX - n.x) * 0.08; n.y += (n.targetY - n.y) * 0.08; }
|
|
2318
|
+
ctx.lineWidth = 1;
|
|
2319
|
+
for (const e of graphEdges) {
|
|
2320
|
+
const a = graphNodes.find(n => n.id === e.from), b = graphNodes.find(n => n.id === e.to);
|
|
2321
|
+
if (!a || !b) continue;
|
|
2322
|
+
ctx.strokeStyle = e.active ? 'rgba(24,255,255,0.3)' : 'rgba(86,96,128,0.15)';
|
|
2323
|
+
ctx.setLineDash(e.active ? [] : [4, 4]);
|
|
2324
|
+
ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
|
|
2325
|
+
}
|
|
2326
|
+
ctx.setLineDash([]);
|
|
2327
|
+
const now = Date.now();
|
|
2328
|
+
graphSignals = graphSignals.filter(s => now - s.spawnedAt < 3500);
|
|
2329
|
+
for (const s of graphSignals) {
|
|
2330
|
+
const from = graphNodes.find(n => n.id === s.fromId), to = graphNodes.find(n => n.id === s.toId);
|
|
2331
|
+
if (!from || !to) continue;
|
|
2332
|
+
s.progress = Math.min(1, (now - s.spawnedAt) / 2400);
|
|
2333
|
+
const t = s.progress, x = from.x + (to.x - from.x) * t, y = from.y + (to.y - from.y) * t;
|
|
2334
|
+
const tail = ctx.createRadialGradient(x, y, 0, x, y, 18);
|
|
2335
|
+
tail.addColorStop(0, s.color); tail.addColorStop(1, 'rgba(0,0,0,0)');
|
|
2336
|
+
ctx.fillStyle = tail; ctx.beginPath(); ctx.arc(x, y, 16, 0, Math.PI * 2); ctx.fill();
|
|
2337
|
+
ctx.fillStyle = s.color;
|
|
2338
|
+
ctx.beginPath(); ctx.arc(x, y, 3.5, 0, Math.PI * 2); ctx.fill();
|
|
2339
|
+
}
|
|
2340
|
+
for (const n of graphNodes) {
|
|
2341
|
+
const pulse = n.type === 'brain' ? Math.sin(graphFrame / 15) * 4 : 0, r = n.r + pulse;
|
|
2342
|
+
const halo = ctx.createRadialGradient(n.x, n.y, r * 0.5, n.x, n.y, r * 2.5);
|
|
2343
|
+
halo.addColorStop(0, n.color + '66'); halo.addColorStop(1, 'rgba(0,0,0,0)');
|
|
2344
|
+
ctx.fillStyle = halo; ctx.beginPath(); ctx.arc(n.x, n.y, r * 2.5, 0, Math.PI * 2); ctx.fill();
|
|
2345
|
+
ctx.fillStyle = n.type === 'brain' ? '#12152a' : '#0b0f1c';
|
|
2346
|
+
ctx.strokeStyle = n.color; ctx.lineWidth = 2;
|
|
2347
|
+
ctx.beginPath(); ctx.arc(n.x, n.y, r, 0, Math.PI * 2); ctx.fill(); ctx.stroke();
|
|
2348
|
+
ctx.fillStyle = n.color;
|
|
2349
|
+
ctx.font = n.type === 'brain' ? 'bold 13px Inter, sans-serif' : '11px Inter, sans-serif';
|
|
2350
|
+
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
|
2351
|
+
ctx.fillText(n.label, n.x, n.y + (n.type === 'brain' ? 0 : r + 14));
|
|
2352
|
+
}
|
|
2353
|
+
graphFrame++;
|
|
2354
|
+
graphTimer = requestAnimationFrame(graphLoop);
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
// ──── Command Palette ──────────────────────────────────────────────────
|
|
2358
|
+
const commands = [
|
|
2359
|
+
...FEATURES.map(([id, name]) => ({ type: 'nav', label: name, target: id, hint: 'Go to' })),
|
|
2360
|
+
{ type: 'nav', label: 'Overview', target: 'overview' },
|
|
2361
|
+
{ type: 'nav', label: 'Live Graph', target: 'graph' },
|
|
2362
|
+
{ type: 'nav', label: 'Agents', target: 'agents' },
|
|
2363
|
+
{ type: 'nav', label: 'Memory Browser', target: 'memory' },
|
|
2364
|
+
{ type: 'nav', label: 'Activity Log', target: 'activity' },
|
|
2365
|
+
{ type: 'action', label: 'Run Dream Cycle', fn: 'runDream' },
|
|
2366
|
+
{ type: 'action', label: 'Run Consolidation', fn: 'runForget' },
|
|
2367
|
+
{ type: 'action', label: 'Toggle Air-Gap', fn: 'toggleAirGap' },
|
|
2368
|
+
{ type: 'action', label: 'Toggle Theme', fn: 'toggleTheme' },
|
|
2369
|
+
{ type: 'action', label: 'Attach All Agents', fn: 'attachAll' },
|
|
2370
|
+
{ type: 'action', label: 'Refresh All', fn: 'refresh' },
|
|
2371
|
+
];
|
|
2372
|
+
let cmdSelected = 0;
|
|
2373
|
+
function openCmdPalette() {
|
|
2374
|
+
document.getElementById('cmd-overlay').classList.add('open');
|
|
2375
|
+
const input = document.getElementById('cmd-input');
|
|
2376
|
+
input.value = ''; cmdSelected = 0;
|
|
2377
|
+
renderCmdResults(commands);
|
|
2378
|
+
setTimeout(() => input.focus(), 50);
|
|
2379
|
+
}
|
|
2380
|
+
function closeCmdPalette() { document.getElementById('cmd-overlay').classList.remove('open'); }
|
|
2381
|
+
function renderCmdResults(items) {
|
|
2382
|
+
document.getElementById('cmd-results').innerHTML = items.map((c, i) => \`<div class="cmd-result \${i === cmdSelected ? 'selected' : ''}" onclick="runCmd(\${i})" data-idx="\${i}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\${c.type === 'nav' ? '<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>' : '<circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/>'}</svg><div class="meta">\${c.label}</div><span class="hint">\${c.hint || c.type}</span></div>\`).join('');
|
|
2383
|
+
}
|
|
2384
|
+
function runCmd(i) {
|
|
2385
|
+
const filtered = filterCmds(document.getElementById('cmd-input').value);
|
|
2386
|
+
const c = filtered[i];
|
|
2387
|
+
if (!c) return;
|
|
2388
|
+
closeCmdPalette();
|
|
2389
|
+
if (c.type === 'nav') switchTab(c.target, '');
|
|
2390
|
+
else if (c.type === 'action' && typeof window[c.fn] === 'function') window[c.fn]();
|
|
2391
|
+
}
|
|
2392
|
+
function filterCmds(q) {
|
|
2393
|
+
if (!q) return commands;
|
|
2394
|
+
const l = q.toLowerCase();
|
|
2395
|
+
return commands.filter(c => c.label.toLowerCase().includes(l));
|
|
2396
|
+
}
|
|
2397
|
+
document.getElementById('cmd-input').addEventListener('input', e => { cmdSelected = 0; renderCmdResults(filterCmds(e.target.value)); });
|
|
2398
|
+
document.getElementById('cmd-input').addEventListener('keydown', e => {
|
|
2399
|
+
const filtered = filterCmds(e.target.value);
|
|
2400
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); cmdSelected = Math.min(cmdSelected + 1, filtered.length - 1); renderCmdResults(filtered); }
|
|
2401
|
+
else if (e.key === 'ArrowUp') { e.preventDefault(); cmdSelected = Math.max(cmdSelected - 1, 0); renderCmdResults(filtered); }
|
|
2402
|
+
else if (e.key === 'Enter') { e.preventDefault(); runCmd(cmdSelected); }
|
|
2403
|
+
else if (e.key === 'Escape') closeCmdPalette();
|
|
2404
|
+
});
|
|
2405
|
+
document.addEventListener('keydown', e => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); openCmdPalette(); } });
|
|
2406
|
+
|
|
2407
|
+
// ──── Live log / ticker ────────────────────────────────────────────────
|
|
2408
|
+
const logEntries = [];
|
|
2409
|
+
function appendLog(source, type, detail) {
|
|
2410
|
+
logEntries.unshift({ t: new Date(), source, type, detail: String(detail || '').slice(0, 320) });
|
|
2411
|
+
if (logEntries.length > 200) logEntries.length = 200;
|
|
2412
|
+
renderTicker(); renderLogPanel(); morphBrainState(type);
|
|
2413
|
+
}
|
|
2414
|
+
function renderTicker() {
|
|
2415
|
+
const el = document.getElementById('log-scroll');
|
|
2416
|
+
if (!el) return;
|
|
2417
|
+
const recent = logEntries.slice(0, 6).map(e => \`\${e.t.toLocaleTimeString()} · \${e.source}/\${e.type} · \${e.detail.slice(0, 60)}\`).join(' || ');
|
|
2418
|
+
el.textContent = recent || 'Awaiting activity…';
|
|
2419
|
+
}
|
|
2420
|
+
function renderLogPanel() {
|
|
2421
|
+
const panel = document.getElementById('log-panel-list');
|
|
2422
|
+
if (!panel) return;
|
|
2423
|
+
panel.innerHTML = logEntries.slice(0, 80).map(e => \`<div class="log-row"><span class="t">\${e.t.toLocaleTimeString()}</span><span class="src">\${e.source}</span><span class="type">\${e.type}</span><span>\${e.detail}</span></div>\`).join('');
|
|
2424
|
+
}
|
|
2425
|
+
function toggleLogPanel() { document.getElementById('log-panel').classList.toggle('open'); if (document.getElementById('log-panel').classList.contains('open')) renderLogPanel(); }
|
|
2426
|
+
function clearLogs() { logEntries.length = 0; renderTicker(); renderLogPanel(); }
|
|
2427
|
+
const BRAIN_STATES = { 'memory-write':'learning · writing memory','subagent-spawn':'sub-agent dispatched','dream':'dreaming · reflecting','revisit':'revisiting recent','counterfactual':'running counterfactual','consolidation':'consolidating','contradiction':'resolving conflict','quarantine':'isolating suspect','collision':'arbitrating collision','firewall-block':'blocking risky action' };
|
|
2428
|
+
let stateTimer = null;
|
|
2429
|
+
function morphBrainState(type) {
|
|
2430
|
+
const el = document.getElementById('brain-state');
|
|
2431
|
+
el.textContent = BRAIN_STATES[type] || 'learning · reflecting · charging';
|
|
2432
|
+
if (stateTimer) clearTimeout(stateTimer);
|
|
2433
|
+
stateTimer = setTimeout(() => { el.textContent = 'learning · reflecting · charging'; }, 6000);
|
|
2434
|
+
}
|
|
2435
|
+
async function pullRecentActivity() {
|
|
2436
|
+
const log = await api('/api/v6/activity-log?limit=40');
|
|
2437
|
+
if (!log) return;
|
|
2438
|
+
const seen = new Set(logEntries.map(e => e.t.toISOString() + '|' + e.type + '|' + e.detail.slice(0, 40)));
|
|
2439
|
+
for (const e of log.reverse()) {
|
|
2440
|
+
const key = (e.timestamp || '') + '|' + e.type + '|' + (e.detail || '').slice(0, 40);
|
|
2441
|
+
if (!seen.has(key)) appendLog(e.source || 'brain', e.type || 'event', e.detail || '');
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
// ──── WebSocket ────────────────────────────────────────────────────────
|
|
2446
|
+
function connectWS() {
|
|
2447
|
+
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
2448
|
+
ws = new WebSocket(\`\${proto}://\${location.host}\`);
|
|
2449
|
+
ws.onopen = () => { appendLog('ws','connected','dashboard WebSocket online'); toast('Connected', 'live updates enabled', 'success'); };
|
|
2450
|
+
ws.onmessage = e => { try { const m = JSON.parse(e.data);
|
|
2451
|
+
if (m.type === 'insights' || m.type === 'modules') refresh();
|
|
2452
|
+
if (m.type === 'insights' && m.insights) for (const ins of m.insights.slice(0,5)) appendLog(ins.sourceAgent || 'brain','insight',ins.title || ins.content);
|
|
2453
|
+
if (m.type === 'injection') appendLog(m.adapter || 'agent','injection', m.insightTitle || 'context injected');
|
|
2454
|
+
if (m.type === 'info') appendLog('brain','info', m.message);
|
|
2455
|
+
if (m.type === 'error') { appendLog('brain','error', m.error); toast('Brain error', m.error, 'error'); }
|
|
2456
|
+
if (m.type === 'agents-detected') appendLog('brain','agents', (m.agents || []).join(', '));
|
|
2457
|
+
} catch {} };
|
|
2458
|
+
ws.onclose = () => { appendLog('ws','disconnected','reconnecting'); setTimeout(connectWS, 2000); };
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
// ──── Bootstrapping ────────────────────────────────────────────────────
|
|
2462
|
+
connectWS();
|
|
2463
|
+
refresh();
|
|
2464
|
+
pullRecentActivity();
|
|
2465
|
+
setInterval(refresh, 5000); // faster heartbeat — real-time feel
|
|
2466
|
+
setInterval(pullRecentActivity, 3000); // faster log stream
|
|
2467
|
+
if (window.lucide) try { window.lucide.createIcons(); } catch {}
|
|
2468
|
+
|
|
2469
|
+
// Welcome toast
|
|
2470
|
+
setTimeout(() => toast('Hive Mind v6.0', 'Press ⌘K or Ctrl+K to open the command palette', 'info'), 800);
|
|
2471
|
+
</script>
|
|
2472
|
+
</body>
|
|
2473
|
+
</html>`;
|
|
2474
|
+
//# sourceMappingURL=v6-dashboard-html.js.map
|