@timmeck/brain-core 2.9.1 → 2.11.0

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.
Files changed (38) hide show
  1. package/consciousness-dashboard.html +1010 -0
  2. package/dist/consciousness/consciousness-server.d.ts +23 -0
  3. package/dist/consciousness/consciousness-server.js +145 -0
  4. package/dist/consciousness/consciousness-server.js.map +1 -0
  5. package/dist/consciousness/index.d.ts +4 -0
  6. package/dist/consciousness/index.js +3 -0
  7. package/dist/consciousness/index.js.map +1 -0
  8. package/dist/consciousness/thought-stream.d.ts +27 -0
  9. package/dist/consciousness/thought-stream.js +119 -0
  10. package/dist/consciousness/thought-stream.js.map +1 -0
  11. package/dist/consciousness/types.d.ts +33 -0
  12. package/dist/consciousness/types.js +3 -0
  13. package/dist/consciousness/types.js.map +1 -0
  14. package/dist/dream/dream-engine.d.ts +4 -0
  15. package/dist/dream/dream-engine.js +18 -0
  16. package/dist/dream/dream-engine.js.map +1 -1
  17. package/dist/index.d.ts +6 -0
  18. package/dist/index.js +6 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/prediction/forecaster.d.ts +22 -0
  21. package/dist/prediction/forecaster.js +120 -0
  22. package/dist/prediction/forecaster.js.map +1 -0
  23. package/dist/prediction/index.d.ts +4 -0
  24. package/dist/prediction/index.js +4 -0
  25. package/dist/prediction/index.js.map +1 -0
  26. package/dist/prediction/prediction-engine.d.ts +59 -0
  27. package/dist/prediction/prediction-engine.js +301 -0
  28. package/dist/prediction/prediction-engine.js.map +1 -0
  29. package/dist/prediction/tracker.d.ts +27 -0
  30. package/dist/prediction/tracker.js +221 -0
  31. package/dist/prediction/tracker.js.map +1 -0
  32. package/dist/prediction/types.d.ts +85 -0
  33. package/dist/prediction/types.js +3 -0
  34. package/dist/prediction/types.js.map +1 -0
  35. package/dist/research/research-orchestrator.d.ts +8 -0
  36. package/dist/research/research-orchestrator.js +65 -1
  37. package/dist/research/research-orchestrator.js.map +1 -1
  38. package/package.json +1 -1
@@ -0,0 +1,1010 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Consciousness Dashboard</title>
7
+ <style>
8
+ :root {
9
+ --bg-primary: #0a0a1a;
10
+ --bg-secondary: #0f1128;
11
+ --bg-card: rgba(15, 17, 40, 0.85);
12
+ --border: rgba(100, 120, 255, 0.15);
13
+ --text-primary: #e0e4ff;
14
+ --text-secondary: #8890b5;
15
+ --text-muted: #555a7a;
16
+ --cyan: #00e5ff;
17
+ --magenta: #ff0090;
18
+ --gold: #ffd700;
19
+ --green: #00ff88;
20
+ --purple: #b388ff;
21
+ --orange: #ff9100;
22
+ --blue: #448aff;
23
+ --red: #ff5252;
24
+ --pink: #ff4081;
25
+ --glow-cyan: 0 0 20px rgba(0, 229, 255, 0.4);
26
+ --glow-magenta: 0 0 20px rgba(255, 0, 144, 0.4);
27
+ --glow-gold: 0 0 20px rgba(255, 215, 0, 0.4);
28
+ }
29
+ * { margin: 0; padding: 0; box-sizing: border-box; }
30
+ body {
31
+ font-family: 'SF Mono', 'Cascadia Code', 'JetBrains Mono', 'Fira Code', monospace;
32
+ background: var(--bg-primary);
33
+ color: var(--text-primary);
34
+ overflow: hidden;
35
+ height: 100vh;
36
+ }
37
+
38
+ /* ── Layout ─────────────────────────────────── */
39
+ .dashboard {
40
+ display: grid;
41
+ grid-template-columns: 280px 1fr 280px;
42
+ grid-template-rows: 1fr 48px;
43
+ height: 100vh;
44
+ gap: 2px;
45
+ }
46
+
47
+ /* ── Thought Stream (Left) ──────────────────── */
48
+ .thought-panel {
49
+ grid-row: 1;
50
+ grid-column: 1;
51
+ background: var(--bg-secondary);
52
+ border-right: 1px solid var(--border);
53
+ display: flex;
54
+ flex-direction: column;
55
+ overflow: hidden;
56
+ }
57
+ .panel-header {
58
+ padding: 12px 16px;
59
+ border-bottom: 1px solid var(--border);
60
+ font-size: 11px;
61
+ text-transform: uppercase;
62
+ letter-spacing: 2px;
63
+ color: var(--text-secondary);
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 8px;
67
+ flex-shrink: 0;
68
+ }
69
+ .panel-header .dot {
70
+ width: 6px; height: 6px; border-radius: 50%;
71
+ background: var(--cyan);
72
+ box-shadow: var(--glow-cyan);
73
+ animation: pulse-dot 2s ease-in-out infinite;
74
+ }
75
+ @keyframes pulse-dot {
76
+ 0%, 100% { opacity: 1; }
77
+ 50% { opacity: 0.4; }
78
+ }
79
+ .thought-list {
80
+ flex: 1;
81
+ overflow-y: auto;
82
+ padding: 8px;
83
+ }
84
+ .thought-list::-webkit-scrollbar { width: 4px; }
85
+ .thought-list::-webkit-scrollbar-track { background: transparent; }
86
+ .thought-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
87
+
88
+ .thought-item {
89
+ padding: 8px 10px;
90
+ margin-bottom: 4px;
91
+ border-radius: 6px;
92
+ border-left: 3px solid var(--text-muted);
93
+ background: rgba(255, 255, 255, 0.02);
94
+ font-size: 11px;
95
+ line-height: 1.5;
96
+ animation: thought-in 0.3s ease-out;
97
+ transition: background 0.2s;
98
+ }
99
+ .thought-item:hover { background: rgba(255, 255, 255, 0.05); }
100
+ @keyframes thought-in {
101
+ from { opacity: 0; transform: translateX(-10px); }
102
+ to { opacity: 1; transform: translateX(0); }
103
+ }
104
+ .thought-engine {
105
+ font-size: 9px;
106
+ text-transform: uppercase;
107
+ letter-spacing: 1px;
108
+ margin-bottom: 2px;
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 6px;
112
+ }
113
+ .thought-time { color: var(--text-muted); font-size: 9px; }
114
+ .thought-content { color: var(--text-primary); word-break: break-word; }
115
+ .thought-badge {
116
+ display: inline-block;
117
+ font-size: 8px;
118
+ padding: 1px 5px;
119
+ border-radius: 3px;
120
+ text-transform: uppercase;
121
+ letter-spacing: 0.5px;
122
+ }
123
+ .badge-routine { background: rgba(85, 90, 122, 0.3); color: var(--text-muted); }
124
+ .badge-notable { background: rgba(255, 215, 0, 0.15); color: var(--gold); }
125
+ .badge-breakthrough {
126
+ background: rgba(255, 215, 0, 0.25); color: var(--gold);
127
+ box-shadow: var(--glow-gold);
128
+ animation: glow-pulse 1.5s ease-in-out infinite;
129
+ }
130
+ @keyframes glow-pulse {
131
+ 0%, 100% { box-shadow: 0 0 5px rgba(255, 215, 0, 0.3); }
132
+ 50% { box-shadow: 0 0 15px rgba(255, 215, 0, 0.6); }
133
+ }
134
+
135
+ /* Engine colors */
136
+ .engine-orchestrator { border-left-color: var(--cyan); }
137
+ .engine-orchestrator .thought-engine { color: var(--cyan); }
138
+ .engine-self_observer { border-left-color: var(--blue); }
139
+ .engine-self_observer .thought-engine { color: var(--blue); }
140
+ .engine-anomaly_detective { border-left-color: var(--orange); }
141
+ .engine-anomaly_detective .thought-engine { color: var(--orange); }
142
+ .engine-dream { border-left-color: var(--purple); }
143
+ .engine-dream .thought-engine { color: var(--purple); }
144
+ .engine-cross_domain { border-left-color: var(--pink); }
145
+ .engine-cross_domain .thought-engine { color: var(--pink); }
146
+ .engine-journal { border-left-color: var(--green); }
147
+ .engine-journal .thought-engine { color: var(--green); }
148
+ .engine-knowledge_distiller { border-left-color: var(--gold); }
149
+ .engine-knowledge_distiller .thought-engine { color: var(--gold); }
150
+ .engine-experiment { border-left-color: var(--red); }
151
+ .engine-experiment .thought-engine { color: var(--red); }
152
+ .engine-adaptive_strategy { border-left-color: #69f0ae; }
153
+ .engine-adaptive_strategy .thought-engine { color: #69f0ae; }
154
+ .engine-research_agenda { border-left-color: #ea80fc; }
155
+ .engine-research_agenda .thought-engine { color: #ea80fc; }
156
+ .engine-counterfactual { border-left-color: #80d8ff; }
157
+ .engine-counterfactual .thought-engine { color: #80d8ff; }
158
+ .engine-data_miner { border-left-color: #ccff90; }
159
+ .engine-data_miner .thought-engine { color: #ccff90; }
160
+
161
+ /* ── Neural Canvas (Center) ─────────────────── */
162
+ .neural-canvas {
163
+ grid-row: 1;
164
+ grid-column: 2;
165
+ position: relative;
166
+ background: var(--bg-primary);
167
+ overflow: hidden;
168
+ }
169
+ #canvas {
170
+ width: 100%; height: 100%;
171
+ display: block;
172
+ }
173
+ .canvas-overlay {
174
+ position: absolute;
175
+ top: 12px; left: 12px;
176
+ font-size: 10px;
177
+ color: var(--text-muted);
178
+ pointer-events: none;
179
+ }
180
+ .canvas-legend {
181
+ position: absolute;
182
+ bottom: 12px; left: 12px;
183
+ font-size: 9px;
184
+ color: var(--text-muted);
185
+ display: flex;
186
+ gap: 12px;
187
+ pointer-events: none;
188
+ }
189
+ .legend-item { display: flex; align-items: center; gap: 4px; }
190
+ .legend-dot { width: 8px; height: 8px; border-radius: 50%; }
191
+
192
+ /* ── Engine Status (Right) ──────────────────── */
193
+ .engine-panel {
194
+ grid-row: 1;
195
+ grid-column: 3;
196
+ background: var(--bg-secondary);
197
+ border-left: 1px solid var(--border);
198
+ display: flex;
199
+ flex-direction: column;
200
+ overflow: hidden;
201
+ }
202
+ .engine-list {
203
+ flex: 1;
204
+ overflow-y: auto;
205
+ padding: 8px;
206
+ }
207
+ .engine-list::-webkit-scrollbar { width: 4px; }
208
+ .engine-list::-webkit-scrollbar-track { background: transparent; }
209
+ .engine-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
210
+
211
+ .engine-card {
212
+ padding: 10px 12px;
213
+ margin-bottom: 6px;
214
+ border-radius: 8px;
215
+ background: var(--bg-card);
216
+ border: 1px solid var(--border);
217
+ transition: all 0.3s ease;
218
+ }
219
+ .engine-card.active {
220
+ border-color: rgba(0, 229, 255, 0.4);
221
+ box-shadow: 0 0 12px rgba(0, 229, 255, 0.15);
222
+ }
223
+ .engine-card.sleeping {
224
+ opacity: 0.5;
225
+ }
226
+ .engine-card-header {
227
+ display: flex;
228
+ justify-content: space-between;
229
+ align-items: center;
230
+ margin-bottom: 6px;
231
+ }
232
+ .engine-card-name {
233
+ font-size: 10px;
234
+ text-transform: uppercase;
235
+ letter-spacing: 1px;
236
+ font-weight: 600;
237
+ }
238
+ .engine-card-status {
239
+ font-size: 8px;
240
+ padding: 2px 6px;
241
+ border-radius: 3px;
242
+ text-transform: uppercase;
243
+ letter-spacing: 0.5px;
244
+ }
245
+ .status-active { background: rgba(0, 229, 255, 0.2); color: var(--cyan); }
246
+ .status-idle { background: rgba(136, 144, 181, 0.2); color: var(--text-secondary); }
247
+ .status-sleeping { background: rgba(179, 136, 255, 0.15); color: var(--purple); }
248
+ .engine-card-metrics {
249
+ display: flex;
250
+ gap: 12px;
251
+ font-size: 9px;
252
+ color: var(--text-muted);
253
+ }
254
+ .engine-metric-value { color: var(--text-secondary); font-weight: 600; }
255
+
256
+ /* ── Status Bar (Bottom) ────────────────────── */
257
+ .status-bar {
258
+ grid-row: 2;
259
+ grid-column: 1 / -1;
260
+ background: var(--bg-secondary);
261
+ border-top: 1px solid var(--border);
262
+ display: flex;
263
+ align-items: center;
264
+ justify-content: space-between;
265
+ padding: 0 16px;
266
+ font-size: 10px;
267
+ color: var(--text-secondary);
268
+ }
269
+ .status-left, .status-right {
270
+ display: flex;
271
+ gap: 20px;
272
+ align-items: center;
273
+ }
274
+ .status-item { display: flex; align-items: center; gap: 5px; }
275
+ .status-value { color: var(--text-primary); font-weight: 600; }
276
+ .status-label { color: var(--text-muted); }
277
+ .health-indicator {
278
+ width: 8px; height: 8px; border-radius: 50%;
279
+ background: var(--green);
280
+ box-shadow: 0 0 8px rgba(0, 255, 136, 0.5);
281
+ }
282
+ .connection-status { display: flex; align-items: center; gap: 4px; }
283
+ .connection-dot {
284
+ width: 6px; height: 6px; border-radius: 50%;
285
+ background: var(--red);
286
+ transition: background 0.3s;
287
+ }
288
+ .connection-dot.connected { background: var(--green); box-shadow: 0 0 6px rgba(0, 255, 136, 0.5); }
289
+
290
+ /* ── Dream Mode Overlay ─────────────────────── */
291
+ .dream-overlay {
292
+ position: absolute;
293
+ top: 0; left: 0; right: 0; bottom: 0;
294
+ pointer-events: none;
295
+ opacity: 0;
296
+ transition: opacity 1s ease;
297
+ background: radial-gradient(ellipse at center, rgba(179, 136, 255, 0.05) 0%, transparent 70%);
298
+ }
299
+ .dream-overlay.active {
300
+ opacity: 1;
301
+ animation: dream-breathe 4s ease-in-out infinite;
302
+ }
303
+ @keyframes dream-breathe {
304
+ 0%, 100% { opacity: 0.3; }
305
+ 50% { opacity: 0.8; }
306
+ }
307
+
308
+ /* ── Flash Effect (Breakthrough) ─────────────── */
309
+ .flash-overlay {
310
+ position: absolute;
311
+ top: 0; left: 0; right: 0; bottom: 0;
312
+ pointer-events: none;
313
+ opacity: 0;
314
+ background: radial-gradient(ellipse at center, rgba(255, 215, 0, 0.3) 0%, transparent 60%);
315
+ transition: opacity 0.1s;
316
+ }
317
+ .flash-overlay.active {
318
+ opacity: 1;
319
+ animation: flash-out 0.8s ease-out forwards;
320
+ }
321
+ @keyframes flash-out {
322
+ 0% { opacity: 1; }
323
+ 100% { opacity: 0; }
324
+ }
325
+
326
+ /* ── Particle Background ─────────────────────── */
327
+ .particles {
328
+ position: absolute;
329
+ top: 0; left: 0; right: 0; bottom: 0;
330
+ pointer-events: none;
331
+ overflow: hidden;
332
+ }
333
+ </style>
334
+ </head>
335
+ <body>
336
+ <div class="dashboard">
337
+ <!-- Left: Thought Stream -->
338
+ <div class="thought-panel">
339
+ <div class="panel-header">
340
+ <div class="dot"></div>
341
+ <span>Thought Stream</span>
342
+ <span id="thought-count" style="margin-left:auto; color:var(--text-muted);">0</span>
343
+ </div>
344
+ <div class="thought-list" id="thought-list"></div>
345
+ </div>
346
+
347
+ <!-- Center: Neural Canvas -->
348
+ <div class="neural-canvas">
349
+ <div class="particles" id="particles"></div>
350
+ <canvas id="canvas"></canvas>
351
+ <div class="dream-overlay" id="dream-overlay"></div>
352
+ <div class="flash-overlay" id="flash-overlay"></div>
353
+ <div class="canvas-overlay" id="canvas-info">Neural Network — Loading...</div>
354
+ <div class="canvas-legend">
355
+ <div class="legend-item"><div class="legend-dot" style="background:var(--cyan)"></div>Memory</div>
356
+ <div class="legend-item"><div class="legend-dot" style="background:var(--magenta)"></div>Rule</div>
357
+ <div class="legend-item"><div class="legend-dot" style="background:var(--gold)"></div>Principle</div>
358
+ <div class="legend-item"><div class="legend-dot" style="background:var(--green)"></div>Insight</div>
359
+ <div class="legend-item"><div class="legend-dot" style="background:var(--purple)"></div>Pattern</div>
360
+ </div>
361
+ </div>
362
+
363
+ <!-- Right: Engine Status -->
364
+ <div class="engine-panel">
365
+ <div class="panel-header">
366
+ <div class="dot" style="background:var(--magenta); box-shadow:var(--glow-magenta)"></div>
367
+ <span>Engine Status</span>
368
+ </div>
369
+ <div class="engine-list" id="engine-list"></div>
370
+ </div>
371
+
372
+ <!-- Bottom: Status Bar -->
373
+ <div class="status-bar">
374
+ <div class="status-left">
375
+ <div class="status-item">
376
+ <div class="health-indicator" id="health-dot"></div>
377
+ <span class="status-value" id="health-score">—</span>
378
+ <span class="status-label">Health</span>
379
+ </div>
380
+ <div class="status-item">
381
+ <span class="status-value" id="total-thoughts">0</span>
382
+ <span class="status-label">Thoughts</span>
383
+ </div>
384
+ <div class="status-item">
385
+ <span class="status-value" id="total-nodes">0</span>
386
+ <span class="status-label">Nodes</span>
387
+ </div>
388
+ <div class="status-item">
389
+ <span class="status-value" id="total-synapses">0</span>
390
+ <span class="status-label">Synapses</span>
391
+ </div>
392
+ <div class="status-item">
393
+ <span class="status-value" id="total-discoveries">0</span>
394
+ <span class="status-label">Discoveries</span>
395
+ </div>
396
+ </div>
397
+ <div class="status-right">
398
+ <div class="status-item">
399
+ <span class="status-label">Uptime</span>
400
+ <span class="status-value" id="uptime">0s</span>
401
+ </div>
402
+ <div class="status-item">
403
+ <span class="status-label">Engines</span>
404
+ <span class="status-value" id="active-engines">0</span>
405
+ </div>
406
+ <div class="connection-status">
407
+ <div class="connection-dot" id="conn-dot"></div>
408
+ <span id="conn-text">Disconnected</span>
409
+ </div>
410
+ </div>
411
+ </div>
412
+ </div>
413
+
414
+ <script>
415
+ (function() {
416
+ 'use strict';
417
+
418
+ // ── State ──────────────────────────────────────────────
419
+ let thoughts = [];
420
+ let networkNodes = [];
421
+ let networkEdges = [];
422
+ let engines = [];
423
+ let status = {};
424
+ let isDreaming = false;
425
+ let canvasNodes = [];
426
+ let canvasEdges = [];
427
+ let activatedNodes = new Set();
428
+ let hoveredNode = null;
429
+ let dragNode = null;
430
+ let offsetX = 0, offsetY = 0;
431
+ let scale = 1;
432
+ let panX = 0, panY = 0;
433
+ let lastPanX = 0, lastPanY = 0;
434
+ let isPanning = false;
435
+ let panStartX = 0, panStartY = 0;
436
+
437
+ // ── Engine Color Map ───────────────────────────────────
438
+ const engineColors = {
439
+ orchestrator: '#00e5ff',
440
+ self_observer: '#448aff',
441
+ anomaly_detective: '#ff9100',
442
+ dream: '#b388ff',
443
+ cross_domain: '#ff4081',
444
+ journal: '#00ff88',
445
+ knowledge_distiller: '#ffd700',
446
+ experiment: '#ff5252',
447
+ adaptive_strategy: '#69f0ae',
448
+ research_agenda: '#ea80fc',
449
+ counterfactual: '#80d8ff',
450
+ data_miner: '#ccff90',
451
+ };
452
+
453
+ const nodeColors = {
454
+ memory: '#00e5ff',
455
+ rule: '#ff0090',
456
+ principle: '#ffd700',
457
+ insight: '#00ff88',
458
+ pattern: '#b388ff',
459
+ error: '#ff5252',
460
+ solution: '#69f0ae',
461
+ project: '#448aff',
462
+ code_module: '#80d8ff',
463
+ decision: '#ea80fc',
464
+ default: '#555a7a',
465
+ };
466
+
467
+ const typeIcons = {
468
+ perceiving: '👁',
469
+ analyzing: '🔬',
470
+ discovering: '💡',
471
+ hypothesizing: '🤔',
472
+ experimenting: '🧪',
473
+ dreaming: '💭',
474
+ reflecting: '📝',
475
+ correlating: '🔗',
476
+ };
477
+
478
+ // ── DOM ────────────────────────────────────────────────
479
+ const thoughtList = document.getElementById('thought-list');
480
+ const thoughtCount = document.getElementById('thought-count');
481
+ const engineList = document.getElementById('engine-list');
482
+ const canvas = document.getElementById('canvas');
483
+ const ctx = canvas.getContext('2d');
484
+ const canvasInfo = document.getElementById('canvas-info');
485
+ const dreamOverlay = document.getElementById('dream-overlay');
486
+ const flashOverlay = document.getElementById('flash-overlay');
487
+ const connDot = document.getElementById('conn-dot');
488
+ const connText = document.getElementById('conn-text');
489
+
490
+ // ── Canvas Setup ───────────────────────────────────────
491
+ function resizeCanvas() {
492
+ const rect = canvas.parentElement.getBoundingClientRect();
493
+ canvas.width = rect.width * window.devicePixelRatio;
494
+ canvas.height = rect.height * window.devicePixelRatio;
495
+ canvas.style.width = rect.width + 'px';
496
+ canvas.style.height = rect.height + 'px';
497
+ ctx.setTransform(window.devicePixelRatio, 0, 0, window.devicePixelRatio, 0, 0);
498
+ }
499
+ window.addEventListener('resize', resizeCanvas);
500
+ resizeCanvas();
501
+
502
+ // ── Neural Canvas: Force-Directed Graph ────────────────
503
+ function initCanvasNodes(nodes, edges) {
504
+ const w = canvas.width / window.devicePixelRatio;
505
+ const h = canvas.height / window.devicePixelRatio;
506
+
507
+ canvasNodes = nodes.map((n, i) => ({
508
+ id: n.id || n.source_id || String(i),
509
+ label: n.label || n.title || n.content?.substring(0, 20) || `Node ${i}`,
510
+ type: n.type || n.node_type || 'default',
511
+ x: w / 2 + (Math.random() - 0.5) * w * 0.6,
512
+ y: h / 2 + (Math.random() - 0.5) * h * 0.6,
513
+ vx: 0, vy: 0,
514
+ radius: Math.min(8, 3 + (n.importance || n.weight || 1) * 0.8),
515
+ activated: false,
516
+ activationTime: 0,
517
+ importance: n.importance || 1,
518
+ }));
519
+
520
+ const nodeMap = new Map(canvasNodes.map(n => [n.id, n]));
521
+ canvasEdges = [];
522
+ for (const e of edges) {
523
+ const src = nodeMap.get(String(e.source_id || e.from_id || e.source));
524
+ const tgt = nodeMap.get(String(e.target_id || e.to_id || e.target));
525
+ if (src && tgt) {
526
+ canvasEdges.push({
527
+ source: src,
528
+ target: tgt,
529
+ weight: e.weight || 0.5,
530
+ activated: false,
531
+ activationTime: 0,
532
+ });
533
+ }
534
+ }
535
+ }
536
+
537
+ function simulateForces() {
538
+ const w = canvas.width / window.devicePixelRatio;
539
+ const h = canvas.height / window.devicePixelRatio;
540
+ const centerX = w / 2, centerY = h / 2;
541
+ const repulsion = 800;
542
+ const springLength = 60;
543
+ const springStrength = 0.01;
544
+ const damping = 0.92;
545
+ const gravity = 0.001;
546
+
547
+ // Repulsion
548
+ for (let i = 0; i < canvasNodes.length; i++) {
549
+ const a = canvasNodes[i];
550
+ if (a === dragNode) continue;
551
+ for (let j = i + 1; j < canvasNodes.length; j++) {
552
+ const b = canvasNodes[j];
553
+ if (b === dragNode) continue;
554
+ let dx = b.x - a.x, dy = b.y - a.y;
555
+ let dist = Math.sqrt(dx * dx + dy * dy) || 1;
556
+ let force = repulsion / (dist * dist);
557
+ let fx = (dx / dist) * force;
558
+ let fy = (dy / dist) * force;
559
+ a.vx -= fx; a.vy -= fy;
560
+ b.vx += fx; b.vy += fy;
561
+ }
562
+ }
563
+
564
+ // Springs (edges)
565
+ for (const e of canvasEdges) {
566
+ let dx = e.target.x - e.source.x;
567
+ let dy = e.target.y - e.source.y;
568
+ let dist = Math.sqrt(dx * dx + dy * dy) || 1;
569
+ let force = (dist - springLength) * springStrength * e.weight;
570
+ let fx = (dx / dist) * force;
571
+ let fy = (dy / dist) * force;
572
+ if (e.source !== dragNode) { e.source.vx += fx; e.source.vy += fy; }
573
+ if (e.target !== dragNode) { e.target.vx -= fx; e.target.vy -= fy; }
574
+ }
575
+
576
+ // Gravity + damping
577
+ for (const n of canvasNodes) {
578
+ if (n === dragNode) continue;
579
+ n.vx += (centerX - n.x) * gravity;
580
+ n.vy += (centerY - n.y) * gravity;
581
+ n.vx *= damping;
582
+ n.vy *= damping;
583
+ n.x += n.vx;
584
+ n.y += n.vy;
585
+ // Bounds
586
+ n.x = Math.max(20, Math.min(w - 20, n.x));
587
+ n.y = Math.max(20, Math.min(h - 20, n.y));
588
+ }
589
+ }
590
+
591
+ function drawCanvas() {
592
+ const w = canvas.width / window.devicePixelRatio;
593
+ const h = canvas.height / window.devicePixelRatio;
594
+ const now = Date.now();
595
+
596
+ ctx.clearRect(0, 0, w, h);
597
+ ctx.save();
598
+ ctx.translate(panX, panY);
599
+ ctx.scale(scale, scale);
600
+
601
+ // Draw edges
602
+ for (const e of canvasEdges) {
603
+ const alpha = 0.1 + e.weight * 0.3;
604
+ let color = `rgba(100, 120, 255, ${alpha})`;
605
+ let lineWidth = 0.5 + e.weight * 1.5;
606
+
607
+ // Activation glow
608
+ if (e.activated && now - e.activationTime < 2000) {
609
+ const fade = 1 - (now - e.activationTime) / 2000;
610
+ color = `rgba(0, 229, 255, ${0.3 + fade * 0.7})`;
611
+ lineWidth = 1 + fade * 3;
612
+ }
613
+
614
+ ctx.beginPath();
615
+ ctx.moveTo(e.source.x, e.source.y);
616
+ ctx.lineTo(e.target.x, e.target.y);
617
+ ctx.strokeStyle = color;
618
+ ctx.lineWidth = lineWidth;
619
+ ctx.stroke();
620
+ }
621
+
622
+ // Draw nodes
623
+ for (const n of canvasNodes) {
624
+ const baseColor = nodeColors[n.type] || nodeColors.default;
625
+ let radius = n.radius;
626
+ let glowRadius = 0;
627
+
628
+ // Activation effect
629
+ if (n.activated && now - n.activationTime < 3000) {
630
+ const fade = 1 - (now - n.activationTime) / 3000;
631
+ glowRadius = radius * 3 * fade;
632
+ radius = n.radius * (1 + fade * 0.5);
633
+ }
634
+
635
+ // Hover effect
636
+ if (n === hoveredNode) {
637
+ glowRadius = Math.max(glowRadius, radius * 2.5);
638
+ }
639
+
640
+ // Glow
641
+ if (glowRadius > 0) {
642
+ const grad = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, glowRadius);
643
+ grad.addColorStop(0, baseColor + '60');
644
+ grad.addColorStop(1, baseColor + '00');
645
+ ctx.beginPath();
646
+ ctx.arc(n.x, n.y, glowRadius, 0, Math.PI * 2);
647
+ ctx.fillStyle = grad;
648
+ ctx.fill();
649
+ }
650
+
651
+ // Node circle
652
+ ctx.beginPath();
653
+ ctx.arc(n.x, n.y, radius, 0, Math.PI * 2);
654
+ ctx.fillStyle = baseColor;
655
+ ctx.fill();
656
+
657
+ // Hover label
658
+ if (n === hoveredNode) {
659
+ ctx.font = '10px monospace';
660
+ ctx.fillStyle = '#e0e4ff';
661
+ ctx.textAlign = 'center';
662
+ ctx.fillText(n.label, n.x, n.y - radius - 8);
663
+ }
664
+ }
665
+
666
+ ctx.restore();
667
+
668
+ // Update info overlay
669
+ canvasInfo.textContent = `Neural Network — ${canvasNodes.length} nodes, ${canvasEdges.length} synapses`;
670
+ }
671
+
672
+ // Canvas animation loop
673
+ function animateCanvas() {
674
+ simulateForces();
675
+ drawCanvas();
676
+ requestAnimationFrame(animateCanvas);
677
+ }
678
+
679
+ // ── Canvas Interaction ─────────────────────────────────
680
+ function getCanvasPos(e) {
681
+ const rect = canvas.getBoundingClientRect();
682
+ return {
683
+ x: (e.clientX - rect.left - panX) / scale,
684
+ y: (e.clientY - rect.top - panY) / scale
685
+ };
686
+ }
687
+
688
+ function findNodeAt(x, y) {
689
+ for (let i = canvasNodes.length - 1; i >= 0; i--) {
690
+ const n = canvasNodes[i];
691
+ const dx = n.x - x, dy = n.y - y;
692
+ if (dx * dx + dy * dy < (n.radius + 5) * (n.radius + 5)) return n;
693
+ }
694
+ return null;
695
+ }
696
+
697
+ canvas.addEventListener('mousedown', (e) => {
698
+ const pos = getCanvasPos(e);
699
+ const node = findNodeAt(pos.x, pos.y);
700
+ if (node) {
701
+ dragNode = node;
702
+ offsetX = pos.x - node.x;
703
+ offsetY = pos.y - node.y;
704
+ } else {
705
+ isPanning = true;
706
+ panStartX = e.clientX - panX;
707
+ panStartY = e.clientY - panY;
708
+ }
709
+ });
710
+
711
+ canvas.addEventListener('mousemove', (e) => {
712
+ const pos = getCanvasPos(e);
713
+ if (dragNode) {
714
+ dragNode.x = pos.x - offsetX;
715
+ dragNode.y = pos.y - offsetY;
716
+ dragNode.vx = 0;
717
+ dragNode.vy = 0;
718
+ } else if (isPanning) {
719
+ panX = e.clientX - panStartX;
720
+ panY = e.clientY - panStartY;
721
+ } else {
722
+ hoveredNode = findNodeAt(pos.x, pos.y);
723
+ canvas.style.cursor = hoveredNode ? 'pointer' : 'default';
724
+ }
725
+ });
726
+
727
+ canvas.addEventListener('mouseup', () => {
728
+ dragNode = null;
729
+ isPanning = false;
730
+ });
731
+
732
+ canvas.addEventListener('wheel', (e) => {
733
+ e.preventDefault();
734
+ const delta = e.deltaY > 0 ? 0.9 : 1.1;
735
+ scale = Math.max(0.2, Math.min(5, scale * delta));
736
+ }, { passive: false });
737
+
738
+ // ── Thought Rendering ─────────────────────────────────
739
+ function renderThought(thought) {
740
+ const el = document.createElement('div');
741
+ const engineClass = `engine-${thought.engine.replace(/[^a-z0-9_]/gi, '_')}`;
742
+ el.className = `thought-item ${engineClass}`;
743
+ const icon = typeIcons[thought.type] || '⚡';
744
+ const time = new Date(thought.timestamp).toLocaleTimeString();
745
+ const badgeClass = `badge-${thought.significance}`;
746
+ el.innerHTML = `
747
+ <div class="thought-engine">
748
+ <span>${icon} ${thought.engine}</span>
749
+ <span class="thought-badge ${badgeClass}">${thought.significance}</span>
750
+ <span class="thought-time">${time}</span>
751
+ </div>
752
+ <div class="thought-content">${escapeHtml(thought.content)}</div>
753
+ `;
754
+ return el;
755
+ }
756
+
757
+ function addThought(thought) {
758
+ thoughts.unshift(thought);
759
+ if (thoughts.length > 200) thoughts.pop();
760
+
761
+ const el = renderThought(thought);
762
+ thoughtList.insertBefore(el, thoughtList.firstChild);
763
+
764
+ // Trim DOM
765
+ while (thoughtList.children.length > 200) {
766
+ thoughtList.removeChild(thoughtList.lastChild);
767
+ }
768
+ thoughtCount.textContent = thoughts.length;
769
+
770
+ // Activate nodes on canvas related to this thought
771
+ activateRelatedNodes(thought);
772
+
773
+ // Dream mode detection
774
+ if (thought.engine === 'dream') {
775
+ isDreaming = true;
776
+ dreamOverlay.classList.add('active');
777
+ } else {
778
+ isDreaming = false;
779
+ dreamOverlay.classList.remove('active');
780
+ }
781
+
782
+ // Breakthrough flash
783
+ if (thought.significance === 'breakthrough') {
784
+ flashOverlay.classList.remove('active');
785
+ void flashOverlay.offsetWidth;
786
+ flashOverlay.classList.add('active');
787
+ }
788
+ }
789
+
790
+ function activateRelatedNodes(thought) {
791
+ const now = Date.now();
792
+ // Activate a few random nodes to simulate neural activity
793
+ const count = thought.significance === 'breakthrough' ? 8 :
794
+ thought.significance === 'notable' ? 4 : 2;
795
+ for (let i = 0; i < Math.min(count, canvasNodes.length); i++) {
796
+ const idx = Math.floor(Math.random() * canvasNodes.length);
797
+ canvasNodes[idx].activated = true;
798
+ canvasNodes[idx].activationTime = now;
799
+ }
800
+ // Also activate some edges
801
+ for (let i = 0; i < Math.min(count, canvasEdges.length); i++) {
802
+ const idx = Math.floor(Math.random() * canvasEdges.length);
803
+ canvasEdges[idx].activated = true;
804
+ canvasEdges[idx].activationTime = now;
805
+ }
806
+ }
807
+
808
+ // ── Engine Status Rendering ────────────────────────────
809
+ function renderEngines(engineData) {
810
+ engineList.innerHTML = '';
811
+ const allEngines = [
812
+ 'orchestrator', 'self_observer', 'anomaly_detective', 'cross_domain',
813
+ 'adaptive_strategy', 'experiment', 'knowledge_distiller', 'research_agenda',
814
+ 'counterfactual', 'dream', 'journal', 'data_miner',
815
+ ];
816
+
817
+ const engineMap = new Map((engineData || []).map(e => [e.engine, e]));
818
+
819
+ for (const name of allEngines) {
820
+ const data = engineMap.get(name) || {
821
+ engine: name,
822
+ status: 'sleeping',
823
+ lastActive: null,
824
+ metrics: { totalThoughts: 0, discoveries: 0, breakthroughs: 0 }
825
+ };
826
+
827
+ const card = document.createElement('div');
828
+ card.className = `engine-card ${data.status}`;
829
+ const color = engineColors[name] || '#555a7a';
830
+ const lastActive = data.lastActive
831
+ ? timeSince(data.lastActive) + ' ago'
832
+ : 'never';
833
+
834
+ card.innerHTML = `
835
+ <div class="engine-card-header">
836
+ <span class="engine-card-name" style="color:${color}">${formatEngineName(name)}</span>
837
+ <span class="engine-card-status status-${data.status}">${data.status}</span>
838
+ </div>
839
+ <div class="engine-card-metrics">
840
+ <span><span class="engine-metric-value">${data.metrics.totalThoughts}</span> thoughts</span>
841
+ <span><span class="engine-metric-value">${data.metrics.discoveries}</span> disc.</span>
842
+ <span title="Last active">${lastActive}</span>
843
+ </div>
844
+ `;
845
+ engineList.appendChild(card);
846
+ }
847
+ }
848
+
849
+ // ── Status Bar Updates ─────────────────────────────────
850
+ function updateStatusBar(s) {
851
+ if (!s) return;
852
+ document.getElementById('total-thoughts').textContent = s.totalThoughts || 0;
853
+ document.getElementById('active-engines').textContent = (s.activeEngines || []).length;
854
+ document.getElementById('uptime').textContent = formatDuration(s.uptime || 0);
855
+
856
+ const disc = (s.thoughtsPerType?.discovering || 0);
857
+ document.getElementById('total-discoveries').textContent = disc;
858
+ }
859
+
860
+ function updateNetwork(net) {
861
+ if (!net) return;
862
+ const nodes = net.nodes || [];
863
+ const edges = net.edges || [];
864
+ document.getElementById('total-nodes').textContent = nodes.length;
865
+ document.getElementById('total-synapses').textContent = edges.length;
866
+
867
+ // Only re-init if node count changed significantly
868
+ if (Math.abs(canvasNodes.length - nodes.length) > 5 || canvasNodes.length === 0) {
869
+ initCanvasNodes(nodes, edges);
870
+ }
871
+ }
872
+
873
+ // ── SSE Connection ─────────────────────────────────────
874
+ let evtSource = null;
875
+ let reconnectTimer = null;
876
+
877
+ function connect() {
878
+ if (evtSource) { evtSource.close(); }
879
+ evtSource = new EventSource('/events');
880
+
881
+ evtSource.addEventListener('connected', () => {
882
+ connDot.classList.add('connected');
883
+ connText.textContent = 'Connected';
884
+ });
885
+
886
+ evtSource.addEventListener('thought', (e) => {
887
+ try { addThought(JSON.parse(e.data)); } catch {}
888
+ });
889
+
890
+ evtSource.addEventListener('network', (e) => {
891
+ try { updateNetwork(JSON.parse(e.data)); } catch {}
892
+ });
893
+
894
+ evtSource.addEventListener('engines', (e) => {
895
+ try {
896
+ const d = JSON.parse(e.data);
897
+ renderEngines(d.engines);
898
+ updateStatusBar(d.status);
899
+ } catch {}
900
+ });
901
+
902
+ evtSource.addEventListener('heartbeat', () => {
903
+ // keepalive — no action needed
904
+ });
905
+
906
+ evtSource.onerror = () => {
907
+ connDot.classList.remove('connected');
908
+ connText.textContent = 'Reconnecting...';
909
+ evtSource.close();
910
+ clearTimeout(reconnectTimer);
911
+ reconnectTimer = setTimeout(connect, 3000);
912
+ };
913
+ }
914
+
915
+ // ── Initial Load ───────────────────────────────────────
916
+ async function loadInitialState() {
917
+ try {
918
+ const res = await fetch('/api/state');
919
+ const state = await res.json();
920
+
921
+ // Load thoughts
922
+ if (state.thoughts) {
923
+ for (const t of state.thoughts.reverse()) {
924
+ addThought(t);
925
+ }
926
+ }
927
+
928
+ // Load network
929
+ updateNetwork(state.network);
930
+
931
+ // Load engines
932
+ renderEngines(state.engines);
933
+ updateStatusBar(state.status);
934
+ } catch (err) {
935
+ console.warn('Failed to load initial state:', err);
936
+ }
937
+ }
938
+
939
+ // ── Particles Background ───────────────────────────────
940
+ function initParticles() {
941
+ const container = document.getElementById('particles');
942
+ for (let i = 0; i < 30; i++) {
943
+ const particle = document.createElement('div');
944
+ const size = 1 + Math.random() * 2;
945
+ particle.style.cssText = `
946
+ position:absolute;
947
+ width:${size}px;height:${size}px;
948
+ background:rgba(100,120,255,${0.1 + Math.random() * 0.15});
949
+ border-radius:50%;
950
+ left:${Math.random() * 100}%;
951
+ top:${Math.random() * 100}%;
952
+ animation:particle-float ${10 + Math.random() * 20}s linear infinite;
953
+ animation-delay:-${Math.random() * 20}s;
954
+ `;
955
+ container.appendChild(particle);
956
+ }
957
+ // Add the keyframes
958
+ const style = document.createElement('style');
959
+ style.textContent = `
960
+ @keyframes particle-float {
961
+ 0% { transform: translate(0, 0) scale(1); opacity: 0.3; }
962
+ 25% { transform: translate(${50 + Math.random()*50}px, -${50 + Math.random()*80}px) scale(1.2); opacity: 0.5; }
963
+ 50% { transform: translate(-${30 + Math.random()*40}px, ${30 + Math.random()*60}px) scale(0.8); opacity: 0.2; }
964
+ 75% { transform: translate(${20 + Math.random()*30}px, ${40 + Math.random()*40}px) scale(1.1); opacity: 0.4; }
965
+ 100% { transform: translate(0, 0) scale(1); opacity: 0.3; }
966
+ }
967
+ `;
968
+ document.head.appendChild(style);
969
+ }
970
+
971
+ // ── Helpers ────────────────────────────────────────────
972
+ function escapeHtml(text) {
973
+ const div = document.createElement('div');
974
+ div.textContent = text;
975
+ return div.innerHTML;
976
+ }
977
+
978
+ function formatEngineName(name) {
979
+ return name.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
980
+ }
981
+
982
+ function timeSince(ts) {
983
+ const diff = Date.now() - ts;
984
+ if (diff < 1000) return 'just now';
985
+ if (diff < 60000) return Math.floor(diff / 1000) + 's';
986
+ if (diff < 3600000) return Math.floor(diff / 60000) + 'm';
987
+ return Math.floor(diff / 3600000) + 'h';
988
+ }
989
+
990
+ function formatDuration(ms) {
991
+ const s = Math.floor(ms / 1000);
992
+ if (s < 60) return s + 's';
993
+ const m = Math.floor(s / 60);
994
+ if (m < 60) return m + 'm ' + (s % 60) + 's';
995
+ const h = Math.floor(m / 60);
996
+ return h + 'h ' + (m % 60) + 'm';
997
+ }
998
+
999
+ // ── Boot ───────────────────────────────────────────────
1000
+ initParticles();
1001
+ renderEngines([]);
1002
+ loadInitialState().then(() => {
1003
+ connect();
1004
+ animateCanvas();
1005
+ });
1006
+
1007
+ })();
1008
+ </script>
1009
+ </body>
1010
+ </html>