@theihtisham/agent-shadow-brain 6.0.2 → 6.2.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.
- package/CHANGELOG.md +372 -0
- package/README.md +330 -358
- package/dist/brain/auto-update.js +1 -1
- package/dist/brain/brain-bus.d.ts +40 -0
- package/dist/brain/brain-bus.d.ts.map +1 -0
- package/dist/brain/brain-bus.js +94 -0
- package/dist/brain/brain-bus.js.map +1 -0
- package/dist/brain/brain-portability.js +3 -3
- package/dist/brain/brain-portability.js.map +1 -1
- package/dist/brain/collision-detective.d.ts.map +1 -1
- package/dist/brain/collision-detective.js +16 -0
- package/dist/brain/collision-detective.js.map +1 -1
- package/dist/brain/context-continuity.d.ts +78 -0
- package/dist/brain/context-continuity.d.ts.map +1 -0
- package/dist/brain/context-continuity.js +341 -0
- package/dist/brain/context-continuity.js.map +1 -0
- package/dist/brain/dream-engine.d.ts.map +1 -1
- package/dist/brain/dream-engine.js +12 -0
- package/dist/brain/dream-engine.js.map +1 -1
- package/dist/brain/forgetting-curve.d.ts.map +1 -1
- package/dist/brain/forgetting-curve.js +19 -0
- package/dist/brain/forgetting-curve.js.map +1 -1
- package/dist/brain/global-brain.d.ts +4 -0
- package/dist/brain/global-brain.d.ts.map +1 -1
- package/dist/brain/global-brain.js +61 -9
- package/dist/brain/global-brain.js.map +1 -1
- package/dist/brain/live-brain.d.ts +119 -0
- package/dist/brain/live-brain.d.ts.map +1 -0
- package/dist/brain/live-brain.js +405 -0
- package/dist/brain/live-brain.js.map +1 -0
- package/dist/brain/mcp-server.d.ts +4 -0
- package/dist/brain/mcp-server.d.ts.map +1 -1
- package/dist/brain/mcp-server.js +81 -5
- package/dist/brain/mcp-server.js.map +1 -1
- package/dist/brain/memory-graph.d.ts +51 -0
- package/dist/brain/memory-graph.d.ts.map +1 -0
- package/dist/brain/memory-graph.js +267 -0
- package/dist/brain/memory-graph.js.map +1 -0
- package/dist/brain/memory-seeder.d.ts +78 -0
- package/dist/brain/memory-seeder.d.ts.map +1 -0
- package/dist/brain/memory-seeder.js +349 -0
- package/dist/brain/memory-seeder.js.map +1 -0
- package/dist/brain/orchestrator.js +1 -1
- package/dist/brain/subagent-bridge.d.ts.map +1 -1
- package/dist/brain/subagent-bridge.js +12 -0
- package/dist/brain/subagent-bridge.js.map +1 -1
- package/dist/brain/token-economy.d.ts +2 -0
- package/dist/brain/token-economy.d.ts.map +1 -1
- package/dist/brain/token-economy.js +80 -24
- package/dist/brain/token-economy.js.map +1 -1
- package/dist/cli.js +240 -31
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/server.d.ts +24 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +524 -7
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/v6-dashboard-html.d.ts +1 -1
- package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -1
- package/dist/dashboard/v6-dashboard-html.js +2070 -213
- package/dist/dashboard/v6-dashboard-html.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +17 -12
- package/dist/types.d.ts.map +1 -1
- package/docs/launch/README.md +61 -0
- package/docs/launch/RELEASE_CHECKLIST_v6.0.3.md +102 -0
- package/docs/launch/RELEASE_NOTES_v6.0.3.md +111 -0
- package/docs/launch/RELEASE_NOTES_v6.1.0.md +38 -0
- package/docs/launch/RELEASE_NOTES_v6.2.0.md +54 -0
- package/package.json +18 -4
- package/tools/export-resume-pack.mjs +56 -0
- package/tools/record-global-memory.mjs +72 -0
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// Responsive, glassmorphism, dark/light, Chart.js analytics, command palette (Cmd+K),
|
|
3
3
|
// toast notifications, Lucide icons, GSAP transitions. Served at / (default) and /hive.
|
|
4
4
|
export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
5
|
-
<html lang="en" data-theme="light">
|
|
5
|
+
<html lang="en" data-theme="light" data-navmode="simple">
|
|
6
6
|
<head>
|
|
7
7
|
<meta charset="UTF-8">
|
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
|
|
9
9
|
<meta name="color-scheme" content="dark light">
|
|
10
|
-
<title>Shadow Brain v6.
|
|
10
|
+
<title>Shadow Brain v6.2 — Hive Mind Control</title>
|
|
11
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='22' fill='%230a0e1a'/><path d='M50 50 L24 30 M50 50 L76 30 M50 50 L24 70 M50 50 L76 70' stroke='%23ffffff' stroke-opacity='0.28' stroke-width='3'/><circle cx='50' cy='50' r='12' fill='%2318ffff'/><circle cx='24' cy='30' r='7' fill='%2310e68c'/><circle cx='76' cy='30' r='7' fill='%23ffb53f'/><circle cx='24' cy='70' r='7' fill='%23a855f7'/><circle cx='76' cy='70' r='7' fill='%23ec4899'/></svg>">
|
|
11
12
|
<link rel="preconnect" href="https://cdn.jsdelivr.net">
|
|
12
13
|
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
|
|
13
14
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|
@@ -49,10 +50,11 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
49
50
|
--radius-lg: 14px;
|
|
50
51
|
--radius-xl: 20px;
|
|
51
52
|
|
|
52
|
-
--shadow-sm: 0 1px 2px rgba(0,0,0,0.
|
|
53
|
-
--shadow: 0
|
|
54
|
-
--shadow-lg: 0 12px
|
|
53
|
+
--shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
|
|
54
|
+
--shadow: 0 6px 24px -6px rgba(0,0,0,0.5), 0 0 0 1px var(--border);
|
|
55
|
+
--shadow-lg: 0 18px 56px -12px rgba(0,0,0,0.6), 0 0 0 1px var(--border-strong);
|
|
55
56
|
--shadow-glow: 0 0 40px rgba(24,255,255,0.15);
|
|
57
|
+
--card-sheen: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0) 42%);
|
|
56
58
|
|
|
57
59
|
--duration-fast: 120ms;
|
|
58
60
|
--duration: 220ms;
|
|
@@ -93,9 +95,10 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
93
95
|
--danger: #dc2626;
|
|
94
96
|
--info: #2563eb;
|
|
95
97
|
--shadow-sm: 0 1px 2px rgba(15,23,42,0.06);
|
|
96
|
-
--shadow: 0
|
|
97
|
-
--shadow-lg: 0 12px
|
|
98
|
+
--shadow: 0 6px 22px -6px rgba(15,23,42,0.14), 0 0 0 1px var(--border);
|
|
99
|
+
--shadow-lg: 0 18px 50px -12px rgba(15,23,42,0.2), 0 0 0 1px var(--border-strong);
|
|
98
100
|
--shadow-glow: 0 0 32px rgba(8,145,178,0.12);
|
|
101
|
+
--card-sheen: linear-gradient(180deg, rgba(255,255,255,0.5), rgba(255,255,255,0) 42%);
|
|
99
102
|
}
|
|
100
103
|
html[data-theme="light"] body::before {
|
|
101
104
|
background:
|
|
@@ -289,28 +292,45 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
289
292
|
font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
|
|
290
293
|
color: var(--text-3); padding: 6px 12px; font-weight: 700;
|
|
291
294
|
}
|
|
295
|
+
/* Simple / Advanced mode toggle */
|
|
296
|
+
.mode-toggle {
|
|
297
|
+
display: flex; gap: 3px; padding: 3px;
|
|
298
|
+
background: var(--bg-elev-1); border: 1px solid var(--border);
|
|
299
|
+
border-radius: 999px; margin: 0 6px 14px;
|
|
300
|
+
}
|
|
301
|
+
.mode-btn {
|
|
302
|
+
flex: 1; padding: 6px 10px; border-radius: 999px;
|
|
303
|
+
font-size: 11.5px; font-weight: 700; color: var(--text-3);
|
|
304
|
+
transition: all var(--duration-fast) var(--easing);
|
|
305
|
+
}
|
|
306
|
+
.mode-btn:hover { color: var(--text-2); }
|
|
307
|
+
.mode-btn.active { background: var(--brand-gradient); color: #fff; box-shadow: 0 2px 10px -2px rgba(168,85,247,0.5); }
|
|
308
|
+
/* In simple mode, advanced groups are hidden entirely. */
|
|
309
|
+
html[data-navmode="simple"] [data-adv] { display: none; }
|
|
292
310
|
.nav-item {
|
|
293
311
|
display: flex; align-items: center; gap: 10px;
|
|
294
312
|
padding: 8px 12px;
|
|
295
|
-
border-radius:
|
|
313
|
+
border-radius: 9px;
|
|
296
314
|
cursor: pointer;
|
|
297
315
|
font-size: 12.5px;
|
|
298
316
|
color: var(--text-2);
|
|
299
|
-
transition:
|
|
317
|
+
transition: background var(--duration-fast) var(--easing), color var(--duration-fast) var(--easing), transform var(--duration-fast) var(--easing);
|
|
300
318
|
position: relative;
|
|
301
319
|
width: 100%; text-align: left;
|
|
302
320
|
font-weight: 500;
|
|
303
321
|
}
|
|
304
|
-
.nav-item:hover { background: var(--bg-elev-2); color: var(--text); }
|
|
322
|
+
.nav-item:hover { background: var(--bg-elev-2); color: var(--text); transform: translateX(2px); }
|
|
305
323
|
.nav-item.active {
|
|
306
324
|
background: var(--brand-gradient-soft);
|
|
307
325
|
color: var(--text);
|
|
326
|
+
font-weight: 600;
|
|
308
327
|
}
|
|
309
328
|
.nav-item.active::before {
|
|
310
329
|
content: "";
|
|
311
|
-
position: absolute; left: 0; top:
|
|
312
|
-
width: 3px; border-radius:
|
|
330
|
+
position: absolute; left: 0; top: 5px; bottom: 5px;
|
|
331
|
+
width: 3px; border-radius: 0 3px 3px 0;
|
|
313
332
|
background: var(--brand-gradient);
|
|
333
|
+
box-shadow: 0 0 10px var(--brand-cyan);
|
|
314
334
|
}
|
|
315
335
|
.nav-item svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-3); transition: color var(--duration-fast); }
|
|
316
336
|
.nav-item:hover svg, .nav-item.active svg { color: var(--brand-cyan); }
|
|
@@ -371,11 +391,18 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
371
391
|
border: 1px solid var(--border);
|
|
372
392
|
border-radius: var(--radius-lg);
|
|
373
393
|
padding: 18px;
|
|
374
|
-
transition:
|
|
394
|
+
transition: transform var(--duration) var(--easing), box-shadow var(--duration) var(--easing), border-color var(--duration) var(--easing);
|
|
375
395
|
position: relative;
|
|
376
396
|
overflow: hidden;
|
|
397
|
+
box-shadow: var(--shadow-sm);
|
|
398
|
+
}
|
|
399
|
+
.card::after {
|
|
400
|
+
content: "";
|
|
401
|
+
position: absolute; inset: 0 0 auto 0; height: 50%;
|
|
402
|
+
background: var(--card-sheen);
|
|
403
|
+
pointer-events: none; border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
|
377
404
|
}
|
|
378
|
-
.card:hover { border-color: var(--border-strong); transform: translateY(-
|
|
405
|
+
.card:hover { border-color: var(--border-strong); transform: translateY(-2px); box-shadow: var(--shadow); }
|
|
379
406
|
.card.span-2 { grid-column: span 2; }
|
|
380
407
|
.card.span-full { grid-column: 1 / -1; }
|
|
381
408
|
.card.accent::before {
|
|
@@ -383,6 +410,9 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
383
410
|
position: absolute; inset: 0; pointer-events: none;
|
|
384
411
|
background: linear-gradient(135deg, rgba(24,255,255,0.08), transparent 50%);
|
|
385
412
|
}
|
|
413
|
+
/* Canvas-bearing cards (constellation, charts, graphs) skip the top sheen so
|
|
414
|
+
visualizations stay crisp — especially in light theme. */
|
|
415
|
+
.card:has(canvas)::after { display: none; }
|
|
386
416
|
.card-header {
|
|
387
417
|
display: flex; align-items: center; justify-content: space-between;
|
|
388
418
|
margin-bottom: 14px; gap: 8px;
|
|
@@ -401,26 +431,96 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
401
431
|
.stat-card .trend.up { color: var(--success); }
|
|
402
432
|
.stat-card .trend.down { color: var(--danger); }
|
|
403
433
|
|
|
434
|
+
.workflow-strip {
|
|
435
|
+
display: grid;
|
|
436
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
437
|
+
gap: 12px;
|
|
438
|
+
align-items: end;
|
|
439
|
+
margin-bottom: 16px;
|
|
440
|
+
}
|
|
441
|
+
.workflow-path {
|
|
442
|
+
font-family: var(--font-mono);
|
|
443
|
+
font-size: 12px;
|
|
444
|
+
color: var(--text-2);
|
|
445
|
+
background: var(--bg-elev-1);
|
|
446
|
+
border: 1px solid var(--border);
|
|
447
|
+
border-radius: 8px;
|
|
448
|
+
padding: 10px 12px;
|
|
449
|
+
width: 100%;
|
|
450
|
+
}
|
|
451
|
+
.warning-stack { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
|
|
452
|
+
.warning-banner {
|
|
453
|
+
display: flex;
|
|
454
|
+
gap: 10px;
|
|
455
|
+
align-items: flex-start;
|
|
456
|
+
border: 1px solid rgba(217, 119, 6, 0.38);
|
|
457
|
+
background: rgba(217, 119, 6, 0.10);
|
|
458
|
+
color: var(--warning);
|
|
459
|
+
border-radius: 8px;
|
|
460
|
+
padding: 10px 12px;
|
|
461
|
+
font-size: 12px;
|
|
462
|
+
}
|
|
463
|
+
.brain-feed { display: flex; flex-direction: column; gap: 10px; }
|
|
464
|
+
.stack { display: flex; flex-direction: column; gap: 8px; }
|
|
465
|
+
.memory-item {
|
|
466
|
+
border: 1px solid var(--border);
|
|
467
|
+
background: var(--bg-elev-1);
|
|
468
|
+
border-radius: 8px;
|
|
469
|
+
padding: 10px 12px;
|
|
470
|
+
}
|
|
471
|
+
.memory-item .meta {
|
|
472
|
+
display: flex;
|
|
473
|
+
gap: 8px;
|
|
474
|
+
flex-wrap: wrap;
|
|
475
|
+
align-items: center;
|
|
476
|
+
margin-bottom: 6px;
|
|
477
|
+
font-size: 11px;
|
|
478
|
+
color: var(--text-3);
|
|
479
|
+
}
|
|
480
|
+
.memory-item .content {
|
|
481
|
+
color: var(--text-2);
|
|
482
|
+
font-size: 12px;
|
|
483
|
+
line-height: 1.45;
|
|
484
|
+
word-break: break-word;
|
|
485
|
+
}
|
|
486
|
+
.ops-agent-grid {
|
|
487
|
+
display: grid;
|
|
488
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
489
|
+
gap: 8px;
|
|
490
|
+
}
|
|
491
|
+
.ops-agent {
|
|
492
|
+
border: 1px solid var(--border);
|
|
493
|
+
background: var(--bg-elev-1);
|
|
494
|
+
border-radius: 8px;
|
|
495
|
+
padding: 9px 10px;
|
|
496
|
+
font-size: 12px;
|
|
497
|
+
}
|
|
498
|
+
.ops-agent .name { font-weight: 700; margin-bottom: 5px; }
|
|
499
|
+
.ops-agent .status { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
500
|
+
.empty-tight { color: var(--text-3); padding: 18px 4px; font-size: 12px; text-align: center; }
|
|
501
|
+
|
|
404
502
|
/* ──────────────────────────────────────────────────────────────────── */
|
|
405
503
|
/* Rows, pills, badges */
|
|
406
504
|
/* ──────────────────────────────────────────────────────────────────── */
|
|
407
505
|
.row {
|
|
408
506
|
display: flex; justify-content: space-between; align-items: center;
|
|
409
|
-
padding:
|
|
410
|
-
gap: 8px;
|
|
507
|
+
padding: 9px 6px; border-bottom: 1px solid var(--border);
|
|
508
|
+
gap: 8px; border-radius: 6px;
|
|
509
|
+
transition: background var(--duration-fast) var(--easing);
|
|
411
510
|
}
|
|
511
|
+
.row:hover { background: var(--bg-elev-2); }
|
|
412
512
|
.row:last-child { border: none; }
|
|
413
513
|
.row .k { font-size: 12px; color: var(--text-2); }
|
|
414
|
-
.row .v { font-size: 12.5px; font-weight:
|
|
514
|
+
.row .v { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
|
|
415
515
|
.row .v.good { color: var(--success); }
|
|
416
516
|
.row .v.warn { color: var(--warning); }
|
|
417
517
|
.row .v.bad { color: var(--danger); }
|
|
418
518
|
|
|
419
519
|
.pill {
|
|
420
520
|
display: inline-flex; align-items: center; gap: 4px;
|
|
421
|
-
padding:
|
|
521
|
+
padding: 3px 9px; border-radius: 999px;
|
|
422
522
|
background: var(--bg-elev-3); border: 1px solid var(--border);
|
|
423
|
-
font-size: 10px; font-weight:
|
|
523
|
+
font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
|
|
424
524
|
color: var(--text-2);
|
|
425
525
|
}
|
|
426
526
|
.pill.green { background: rgba(16,230,140,.12); color: var(--success); border-color: rgba(16,230,140,.3); }
|
|
@@ -438,24 +538,26 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
438
538
|
padding: 8px 14px;
|
|
439
539
|
background: var(--bg-elev-2);
|
|
440
540
|
border: 1px solid var(--border);
|
|
441
|
-
border-radius:
|
|
541
|
+
border-radius: 9px;
|
|
442
542
|
color: var(--text);
|
|
443
543
|
font-size: 12.5px;
|
|
444
544
|
font-weight: 600;
|
|
445
|
-
transition:
|
|
545
|
+
transition: transform var(--duration-fast) var(--easing), box-shadow var(--duration) var(--easing), background var(--duration) var(--easing), border-color var(--duration) var(--easing), filter var(--duration) var(--easing);
|
|
446
546
|
white-space: nowrap;
|
|
547
|
+
box-shadow: var(--shadow-sm);
|
|
447
548
|
}
|
|
448
|
-
.btn:hover { background: var(--bg-elev-3); border-color: var(--border-strong); transform: translateY(-1px); }
|
|
449
|
-
.btn:active { transform: translateY(0); }
|
|
549
|
+
.btn:hover { background: var(--bg-elev-3); border-color: var(--border-strong); transform: translateY(-1px); box-shadow: var(--shadow); }
|
|
550
|
+
.btn:active { transform: translateY(0) scale(0.98); }
|
|
450
551
|
.btn svg { width: 13px; height: 13px; }
|
|
451
|
-
.btn.sm { padding: 4px 10px; font-size: 11px; }
|
|
452
|
-
.btn.lg { padding: 11px 18px; font-size: 13px; }
|
|
552
|
+
.btn.sm { padding: 4px 10px; font-size: 11px; border-radius: 7px; }
|
|
553
|
+
.btn.lg { padding: 11px 18px; font-size: 13px; border-radius: 11px; }
|
|
453
554
|
.btn.primary {
|
|
454
555
|
background: var(--brand-gradient);
|
|
455
556
|
color: #fff;
|
|
456
557
|
border-color: transparent;
|
|
558
|
+
box-shadow: 0 4px 14px -4px rgba(168,85,247,0.5);
|
|
457
559
|
}
|
|
458
|
-
.btn.primary:hover { filter: brightness(1.08); box-shadow: 0
|
|
560
|
+
.btn.primary:hover { filter: brightness(1.08); transform: translateY(-1px); box-shadow: 0 8px 24px -4px rgba(24,255,255,0.4); }
|
|
459
561
|
.btn.ghost { background: transparent; }
|
|
460
562
|
.btn.ghost:hover { background: var(--bg-elev-2); }
|
|
461
563
|
.btn.danger { color: var(--danger); border-color: rgba(255,88,100,.35); }
|
|
@@ -511,16 +613,18 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
511
613
|
/* Tables */
|
|
512
614
|
/* ──────────────────────────────────────────────────────────────────── */
|
|
513
615
|
.table { width: 100%; border-collapse: collapse; }
|
|
514
|
-
.table th, .table td { padding:
|
|
616
|
+
.table th, .table td { padding: 11px 14px; text-align: left; font-size: 12px; }
|
|
515
617
|
.table th {
|
|
516
618
|
background: var(--bg-elev-2);
|
|
517
619
|
color: var(--text-3);
|
|
518
620
|
font-weight: 700; letter-spacing: 0.06em;
|
|
519
621
|
text-transform: uppercase; font-size: 10px;
|
|
520
|
-
border-bottom: 1px solid var(--border);
|
|
622
|
+
border-bottom: 1px solid var(--border-strong);
|
|
623
|
+
position: sticky; top: 0; z-index: 1;
|
|
521
624
|
}
|
|
522
|
-
.table tr { border-bottom: 1px solid var(--border); transition: background var(--duration-fast); }
|
|
523
|
-
.table tr:
|
|
625
|
+
.table tbody tr { border-bottom: 1px solid var(--border); transition: background var(--duration-fast), box-shadow var(--duration-fast); }
|
|
626
|
+
.table tbody tr:last-child { border-bottom: none; }
|
|
627
|
+
.table tbody tr:hover { background: var(--brand-gradient-soft); box-shadow: inset 3px 0 0 var(--brand-cyan); }
|
|
524
628
|
.table code { color: var(--brand-cyan); font-size: 11px; }
|
|
525
629
|
|
|
526
630
|
.empty {
|
|
@@ -757,6 +861,181 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
757
861
|
/* Skeleton loader */
|
|
758
862
|
.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
863
|
@keyframes skel { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
864
|
+
|
|
865
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
866
|
+
/* Realtime activity (v6.1) */
|
|
867
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
868
|
+
.live-badge {
|
|
869
|
+
display: inline-flex; align-items: center; gap: 5px;
|
|
870
|
+
font-family: var(--font-mono); font-size: 9px; font-weight: 800; letter-spacing: 0.12em;
|
|
871
|
+
color: var(--success); background: rgba(16,230,140,.12);
|
|
872
|
+
border: 1px solid rgba(16,230,140,.32); border-radius: 999px;
|
|
873
|
+
padding: 2px 8px; vertical-align: middle;
|
|
874
|
+
}
|
|
875
|
+
.live-badge::before {
|
|
876
|
+
content: ""; width: 6px; height: 6px; border-radius: 50%;
|
|
877
|
+
background: var(--success); box-shadow: 0 0 6px var(--success);
|
|
878
|
+
animation: pulse 1.3s infinite;
|
|
879
|
+
}
|
|
880
|
+
.live-badge.paused { color: var(--warning); background: rgba(255,181,63,.12); border-color: rgba(255,181,63,.32); }
|
|
881
|
+
.live-badge.paused::before { background: var(--warning); box-shadow: none; animation: none; }
|
|
882
|
+
.filter-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
|
|
883
|
+
.chip {
|
|
884
|
+
padding: 5px 12px; border-radius: 999px; font-size: 11.5px; font-weight: 600;
|
|
885
|
+
background: var(--bg-elev-2); border: 1px solid var(--border); color: var(--text-2);
|
|
886
|
+
transition: all var(--duration-fast) var(--easing);
|
|
887
|
+
}
|
|
888
|
+
.chip:hover { border-color: var(--border-strong); color: var(--text); }
|
|
889
|
+
.chip.active { background: var(--brand-gradient); color: #fff; border-color: transparent; }
|
|
890
|
+
.act-search {
|
|
891
|
+
background: var(--bg-elev-1); border: 1px solid var(--border); border-radius: 8px;
|
|
892
|
+
padding: 7px 12px; color: var(--text); font-size: 12px; min-width: 180px;
|
|
893
|
+
}
|
|
894
|
+
.act-search:focus { outline: none; border-color: var(--brand-cyan); box-shadow: 0 0 0 3px rgba(24,255,255,0.12); }
|
|
895
|
+
tr.act-fresh { animation: actFreshIn var(--duration-slow) var(--easing); }
|
|
896
|
+
@keyframes actFreshIn { from { background: rgba(24,255,255,0.12); opacity: 0; transform: translateY(-4px); } to { background: transparent; opacity: 1; transform: none; } }
|
|
897
|
+
.conn-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--success); box-shadow: 0 0 6px var(--success); transition: all var(--duration); }
|
|
898
|
+
.conn-dot.offline { background: var(--warning); box-shadow: none; animation: pulse 1s infinite; }
|
|
899
|
+
|
|
900
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
901
|
+
/* Memory Constellation (v6.2) */
|
|
902
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
903
|
+
.const-wrap {
|
|
904
|
+
position: relative;
|
|
905
|
+
height: 600px;
|
|
906
|
+
background:
|
|
907
|
+
radial-gradient(ellipse at 30% 20%, rgba(24,255,255,0.05) 0%, transparent 55%),
|
|
908
|
+
radial-gradient(ellipse at 75% 80%, rgba(168,85,247,0.06) 0%, transparent 55%);
|
|
909
|
+
overflow: hidden;
|
|
910
|
+
cursor: grab;
|
|
911
|
+
}
|
|
912
|
+
.const-wrap.dragging { cursor: grabbing; }
|
|
913
|
+
.const-wrap canvas { display: block; width: 100%; height: 100%; }
|
|
914
|
+
.const-legend {
|
|
915
|
+
position: absolute; top: 12px; left: 12px;
|
|
916
|
+
display: flex; flex-direction: column; gap: 5px;
|
|
917
|
+
background: var(--bg-glass-strong); backdrop-filter: blur(12px);
|
|
918
|
+
border: 1px solid var(--border); border-radius: 8px; padding: 8px 11px;
|
|
919
|
+
font-size: 11px; color: var(--text-2); pointer-events: none;
|
|
920
|
+
}
|
|
921
|
+
.const-legend .lg-row { display: flex; align-items: center; gap: 7px; }
|
|
922
|
+
.const-legend .lg-dot { width: 9px; height: 9px; border-radius: 50%; box-shadow: 0 0 6px currentColor; }
|
|
923
|
+
.const-meta {
|
|
924
|
+
position: absolute; top: 12px; right: 12px;
|
|
925
|
+
background: var(--bg-glass-strong); backdrop-filter: blur(12px);
|
|
926
|
+
border: 1px solid var(--border); border-radius: 8px; padding: 6px 11px;
|
|
927
|
+
font-family: var(--font-mono); font-size: 10.5px; color: var(--brand-cyan); pointer-events: none;
|
|
928
|
+
}
|
|
929
|
+
.const-hint {
|
|
930
|
+
position: absolute; bottom: 10px; left: 12px;
|
|
931
|
+
font-size: 10.5px; color: var(--text-3); pointer-events: none;
|
|
932
|
+
background: var(--bg-glass); border-radius: 6px; padding: 3px 9px;
|
|
933
|
+
}
|
|
934
|
+
.const-empty {
|
|
935
|
+
position: absolute; inset: 0; display: none;
|
|
936
|
+
flex-direction: column; align-items: center; justify-content: center;
|
|
937
|
+
text-align: center; gap: 8px; padding: 24px;
|
|
938
|
+
}
|
|
939
|
+
.const-empty.show { display: flex; }
|
|
940
|
+
.const-empty svg { width: 52px; height: 52px; color: var(--text-dim); margin-bottom: 4px; }
|
|
941
|
+
.const-empty .ce-title { font-size: 15px; font-weight: 700; color: var(--text-2); }
|
|
942
|
+
.const-empty .ce-msg { font-size: 12px; color: var(--text-3); max-width: 360px; line-height: 1.5; }
|
|
943
|
+
.const-drawer {
|
|
944
|
+
position: absolute; top: 0; right: 0; bottom: 0; width: min(340px, 80%);
|
|
945
|
+
background: var(--bg-glass-strong); backdrop-filter: blur(28px);
|
|
946
|
+
border-left: 1px solid var(--border-strong);
|
|
947
|
+
transform: translateX(100%); transition: transform var(--duration-slow) var(--easing);
|
|
948
|
+
padding: 16px; overflow-y: auto; z-index: 5;
|
|
949
|
+
}
|
|
950
|
+
.const-drawer.open { transform: translateX(0); box-shadow: -12px 0 40px rgba(0,0,0,0.35); }
|
|
951
|
+
.const-drawer-close {
|
|
952
|
+
position: absolute; top: 10px; right: 12px;
|
|
953
|
+
width: 26px; height: 26px; border-radius: 6px; font-size: 18px; line-height: 1;
|
|
954
|
+
color: var(--text-2); background: var(--bg-elev-2); border: 1px solid var(--border);
|
|
955
|
+
}
|
|
956
|
+
.const-drawer-close:hover { color: var(--text); background: var(--bg-elev-3); }
|
|
957
|
+
.cd-type { font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; }
|
|
958
|
+
.cd-title { font-size: 15px; font-weight: 700; margin: 6px 0 12px; line-height: 1.35; word-break: break-word; }
|
|
959
|
+
.cd-section { margin-top: 12px; }
|
|
960
|
+
.cd-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-3); margin-bottom: 4px; font-weight: 700; }
|
|
961
|
+
.cd-content { font-size: 12px; color: var(--text-2); line-height: 1.5; word-break: break-word; }
|
|
962
|
+
.const-tip {
|
|
963
|
+
position: absolute; pointer-events: none; z-index: 6; display: none;
|
|
964
|
+
background: var(--bg-glass-strong); backdrop-filter: blur(12px);
|
|
965
|
+
border: 1px solid var(--border-strong); border-radius: 7px; padding: 6px 10px;
|
|
966
|
+
font-size: 11px; color: var(--text); max-width: 260px;
|
|
967
|
+
transform: translate(-50%, calc(-100% - 12px));
|
|
968
|
+
}
|
|
969
|
+
.const-tip.show { display: block; }
|
|
970
|
+
.tag-cloud { display: flex; flex-wrap: wrap; gap: 7px; }
|
|
971
|
+
.tag-cloud .tc {
|
|
972
|
+
border: 1px solid var(--border); border-radius: 999px; padding: 4px 11px;
|
|
973
|
+
font-size: 12px; color: var(--text-2); background: var(--bg-elev-1);
|
|
974
|
+
}
|
|
975
|
+
.tag-cloud .tc b { color: var(--brand-cyan); font-variant-numeric: tabular-nums; }
|
|
976
|
+
|
|
977
|
+
/* Overview constellation hero */
|
|
978
|
+
.const-hero { background: linear-gradient(120deg, rgba(24,255,255,0.08), rgba(168,85,247,0.08) 60%, transparent); }
|
|
979
|
+
.const-hero:hover { border-color: var(--border-accent); box-shadow: var(--shadow-glow); }
|
|
980
|
+
.const-hero-inner { display: flex; align-items: center; gap: 20px; }
|
|
981
|
+
.const-hero-art { flex-shrink: 0; width: 132px; height: 96px; }
|
|
982
|
+
.const-hero-art svg { width: 100%; height: 100%; filter: drop-shadow(0 0 8px rgba(24,255,255,0.35)); }
|
|
983
|
+
.const-hero-art .he { stroke: rgba(24,255,255,0.4); stroke-width: 1.2; }
|
|
984
|
+
.const-hero-art circle { animation: synapse 1.6s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
|
|
985
|
+
.const-hero-body { flex: 1; min-width: 0; }
|
|
986
|
+
.const-hero-title { font-size: 19px; font-weight: 800; letter-spacing: -0.4px; background: var(--brand-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
|
987
|
+
.const-hero-sub { font-size: 12.5px; color: var(--text-3); margin: 4px 0 10px; }
|
|
988
|
+
.const-hero-stats { display: flex; gap: 18px; font-size: 12px; color: var(--text-2); }
|
|
989
|
+
.const-hero-stats b { font-size: 17px; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; margin-right: 4px; }
|
|
990
|
+
@media (max-width: 768px) { .const-hero-art { display: none; } .const-hero-inner { gap: 12px; } }
|
|
991
|
+
|
|
992
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
993
|
+
/* Revived tab visualizations (v6.2) */
|
|
994
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
995
|
+
/* Reusable empty-state */
|
|
996
|
+
.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 8px; padding: 36px 20px; }
|
|
997
|
+
.empty-state svg { width: 44px; height: 44px; color: var(--text-dim); margin-bottom: 4px; }
|
|
998
|
+
.empty-state .es-title { font-size: 14px; font-weight: 700; color: var(--text-2); }
|
|
999
|
+
.empty-state .es-msg { font-size: 12px; color: var(--text-3); max-width: 380px; line-height: 1.5; }
|
|
1000
|
+
.empty-state .es-cta { margin-top: 6px; }
|
|
1001
|
+
|
|
1002
|
+
/* Risk gauge + matrix (Pre-Mortem) */
|
|
1003
|
+
.risk-head { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; margin-bottom: 16px; }
|
|
1004
|
+
.risk-gauge { position: relative; width: 96px; height: 96px; flex-shrink: 0; }
|
|
1005
|
+
.risk-gauge canvas { width: 96px; height: 96px; }
|
|
1006
|
+
.risk-gauge .rg-num { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 900; }
|
|
1007
|
+
.risk-matrix { display: grid; grid-template-columns: 64px repeat(4, 1fr); gap: 5px; margin: 14px 0; }
|
|
1008
|
+
.risk-matrix .rm-h { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-3); display: flex; align-items: center; justify-content: center; padding: 4px; }
|
|
1009
|
+
.risk-matrix .rm-yl { font-size: 10px; color: var(--text-3); display: flex; align-items: center; justify-content: flex-end; padding-right: 6px; }
|
|
1010
|
+
.risk-matrix .rm-cell { aspect-ratio: 2.6/1; border-radius: 6px; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 13px; color: #fff; min-height: 30px; transition: all var(--duration); }
|
|
1011
|
+
.risk-matrix .rm-cell.empty { background: var(--bg-elev-1); color: var(--text-dim); font-weight: 400; }
|
|
1012
|
+
.fail-card { padding: 12px 14px; background: var(--bg-elev-1); border: 1px solid var(--border); border-left-width: 3px; border-radius: 8px; margin-bottom: 8px; }
|
|
1013
|
+
.fail-card .fc-head { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }
|
|
1014
|
+
.fail-card .fc-desc { font-size: 12.5px; color: var(--text); line-height: 1.45; }
|
|
1015
|
+
.fail-card .fc-mit { font-size: 11.5px; color: var(--text-2); margin-top: 6px; padding-left: 14px; border-left: 2px solid var(--success); }
|
|
1016
|
+
|
|
1017
|
+
/* Attention heat bars */
|
|
1018
|
+
.heat-row { margin-bottom: 10px; }
|
|
1019
|
+
.heat-row .hr-top { display: flex; justify-content: space-between; align-items: center; font-size: 11.5px; margin-bottom: 4px; gap: 8px; }
|
|
1020
|
+
.heat-row .hr-pct { font-family: var(--font-mono); font-weight: 800; flex-shrink: 0; }
|
|
1021
|
+
.heat-row .hr-track { height: 22px; background: var(--bg-elev-1); border-radius: 5px; overflow: hidden; border: 1px solid var(--border); }
|
|
1022
|
+
.heat-row .hr-fill { height: 100%; border-radius: 5px 0 0 5px; transition: width var(--duration-slow) var(--easing); }
|
|
1023
|
+
.heat-row .hr-reason { font-size: 10.5px; color: var(--text-3); margin-top: 3px; }
|
|
1024
|
+
|
|
1025
|
+
/* Reputation trust cards */
|
|
1026
|
+
.trust-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
|
|
1027
|
+
.trust-card { background: var(--bg-elev-1); border: 1px solid var(--border); border-radius: 12px; padding: 16px; display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; transition: all var(--duration); }
|
|
1028
|
+
.trust-card:hover { border-color: var(--border-accent); transform: translateY(-2px); }
|
|
1029
|
+
.trust-ring { position: relative; width: 92px; height: 92px; }
|
|
1030
|
+
.trust-ring canvas { width: 92px; height: 92px; }
|
|
1031
|
+
.trust-ring .tr-pct { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 19px; font-weight: 900; }
|
|
1032
|
+
.trust-card .tc-name { font-weight: 700; font-size: 13px; }
|
|
1033
|
+
.trust-card .tc-sub { font-size: 11px; color: var(--text-3); }
|
|
1034
|
+
|
|
1035
|
+
/* Causal / DAG canvas */
|
|
1036
|
+
.dag-wrap { position: relative; height: 420px; background: radial-gradient(ellipse at center, rgba(168,85,247,0.04), transparent 70%); border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
|
|
1037
|
+
.dag-wrap canvas { display: block; width: 100%; height: 100%; }
|
|
1038
|
+
.dag-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--text-3); font-size: 12.5px; text-align: center; padding: 20px; }
|
|
760
1039
|
</style>
|
|
761
1040
|
</head>
|
|
762
1041
|
<body>
|
|
@@ -792,18 +1071,19 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
792
1071
|
</div>
|
|
793
1072
|
|
|
794
1073
|
<div class="brand">
|
|
795
|
-
<div class="name">Shadow Brain <span style="color:var(--text-3);font-size:10px;font-weight:600">v6.
|
|
1074
|
+
<div class="name">Shadow Brain <span style="color:var(--text-3);font-size:10px;font-weight:600">v6.2 Hive Mind</span></div>
|
|
796
1075
|
<div class="state" id="brain-state">learning · reflecting · charging</div>
|
|
797
1076
|
</div>
|
|
798
1077
|
|
|
799
1078
|
<div class="header-stats" id="header-stats">
|
|
800
1079
|
<div class="stat-pill"><span class="n" id="stat-agents">—</span> agents</div>
|
|
801
1080
|
<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>
|
|
803
|
-
<div class="stat-pill"><span class="n" id="stat-
|
|
1081
|
+
<div class="stat-pill"><span class="n" id="stat-dreams">—</span> reflections</div>
|
|
1082
|
+
<div class="stat-pill"><span class="n" id="stat-tools">—</span> tools</div>
|
|
804
1083
|
</div>
|
|
805
1084
|
|
|
806
1085
|
<div class="header-right">
|
|
1086
|
+
<span class="conn-dot" id="conn-dot" title="Realtime connection: connecting"></span>
|
|
807
1087
|
<button class="kbd-hint" onclick="openCmdPalette()" title="Open command palette">
|
|
808
1088
|
<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
1089
|
Search <kbd>⌘K</kbd>
|
|
@@ -822,67 +1102,161 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
822
1102
|
══════════════════════════════════════════════════════════════════════ -->
|
|
823
1103
|
<div class="layout">
|
|
824
1104
|
<aside class="sidebar" id="sidebar">
|
|
1105
|
+
<div class="mode-toggle" id="mode-toggle" title="Simple shows the essentials. Advanced reveals every feature.">
|
|
1106
|
+
<button class="mode-btn active" data-mode="simple" onclick="setMode('simple')">Simple</button>
|
|
1107
|
+
<button class="mode-btn" data-mode="advanced" onclick="setMode('advanced')">Advanced</button>
|
|
1108
|
+
</div>
|
|
1109
|
+
|
|
825
1110
|
<div class="nav-group">
|
|
826
|
-
<div class="nav-group-label">
|
|
827
|
-
<button class="nav-item active" data-tab="overview" data-title="
|
|
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"><
|
|
829
|
-
|
|
1111
|
+
<div class="nav-group-label">Main</div>
|
|
1112
|
+
<button class="nav-item active" data-tab="overview" data-title="Home" title="Your starting point — a quick summary of everything the brain knows and is doing.">
|
|
1113
|
+
<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="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
|
1114
|
+
Home
|
|
830
1115
|
</button>
|
|
831
|
-
<button class="nav-item" data-tab="
|
|
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="
|
|
833
|
-
|
|
1116
|
+
<button class="nav-item" data-tab="intelligence" data-title="Brain Map" title="A visual map of everything the brain remembers — memories, projects, and the tools that created them.">
|
|
1117
|
+
<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="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="5" cy="18" r="2"/><circle cx="19" cy="18" r="2"/><line x1="7" y1="7" x2="10" y2="10.5"/><line x1="17" y1="7" x2="14" y2="10.5"/><line x1="7" y1="17" x2="10" y2="13.5"/><line x1="17" y1="17" x2="14" y2="13.5"/></svg>
|
|
1118
|
+
Brain Map<span class="badge-count" id="badge-intelligence">0</span>
|
|
1119
|
+
</button>
|
|
1120
|
+
<button class="nav-item" data-tab="memory" data-title="Memories" title="Browse and search everything the brain has learned and saved.">
|
|
1121
|
+
<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>
|
|
1122
|
+
Memories
|
|
1123
|
+
</button>
|
|
1124
|
+
<button class="nav-item" data-tab="chat" data-title="Ask the Brain" title="Ask a question in plain language and get answers from the brain's memory.">
|
|
1125
|
+
<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>
|
|
1126
|
+
Ask the Brain
|
|
834
1127
|
</button>
|
|
835
|
-
<button class="nav-item" data-tab="
|
|
1128
|
+
<button class="nav-item" data-tab="activity" data-title="Activity" title="A live feed of what the brain and your tools are doing right now.">
|
|
1129
|
+
<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>
|
|
1130
|
+
Activity
|
|
1131
|
+
</button>
|
|
1132
|
+
<button class="nav-item" data-tab="agents" data-title="Connected Tools" title="See which AI coding tools (Claude, Cursor, Codex…) are connected to your brain.">
|
|
836
1133
|
<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
|
-
|
|
1134
|
+
Connected Tools<span class="badge-count" id="badge-agents">0</span>
|
|
838
1135
|
</button>
|
|
839
|
-
<button class="nav-item" data-tab="
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
Memory Browser
|
|
1136
|
+
<button class="nav-item" data-tab="livebrain" data-title="Brain Health" title="A health check of your brain's memory quality, with tips to improve it.">
|
|
1137
|
+
<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>
|
|
1138
|
+
Brain Health
|
|
843
1139
|
</button>
|
|
844
|
-
<button class="nav-item" data-tab="
|
|
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"><
|
|
846
|
-
|
|
1140
|
+
<button class="nav-item" data-tab="config" data-title="Settings" title="Set up and adjust how Shadow Brain works.">
|
|
1141
|
+
<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>
|
|
1142
|
+
Settings
|
|
847
1143
|
</button>
|
|
848
1144
|
</div>
|
|
849
1145
|
|
|
850
|
-
<div class="nav-group">
|
|
851
|
-
<div class="nav-group-label">
|
|
852
|
-
<button class="nav-item" data-tab="
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
<button class="nav-item" data-tab="
|
|
857
|
-
|
|
858
|
-
|
|
1146
|
+
<div class="nav-group" data-adv="1">
|
|
1147
|
+
<div class="nav-group-label">Explore</div>
|
|
1148
|
+
<button class="nav-item" data-tab="ww2br" data-title="Project Memory" title="Memory and status for one specific project.">
|
|
1149
|
+
<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="M4 19V5"/><path d="M4 6h9l1 2h6v8h-9l-1-2H4"/></svg>
|
|
1150
|
+
Project Memory<span class="badge-count" id="badge-ww2br">0</span>
|
|
1151
|
+
</button>
|
|
1152
|
+
<button class="nav-item" data-tab="graph" data-title="Live Network" title="A live animation of your tools talking to the brain.">
|
|
1153
|
+
<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>
|
|
1154
|
+
Live Network
|
|
1155
|
+
</button>
|
|
1156
|
+
<button class="nav-item" data-tab="seeder" data-title="Add Knowledge" title="Research a topic and save it as verified, source-backed knowledge.">
|
|
1157
|
+
<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 2v20"/><path d="M5 9c0-3 2-5 7-7 5 2 7 4 7 7 0 5-4 8-7 8s-7-3-7-8z"/><path d="M8 12h8"/></svg>
|
|
1158
|
+
Add Knowledge<span class="badge-count" id="badge-seeder">0</span>
|
|
1159
|
+
</button>
|
|
1160
|
+
<button class="nav-item" data-tab="garden" data-title="Memory Garden" title="A relaxing visual of your memories growing over time.">
|
|
1161
|
+
<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>
|
|
1162
|
+
Memory Garden
|
|
1163
|
+
</button>
|
|
859
1164
|
</div>
|
|
860
1165
|
|
|
861
|
-
<div class="nav-group">
|
|
862
|
-
<div class="nav-group-label">
|
|
863
|
-
<button class="nav-item" data-tab="
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
<button class="nav-item" data-tab="
|
|
868
|
-
|
|
1166
|
+
<div class="nav-group" data-adv="1">
|
|
1167
|
+
<div class="nav-group-label">How the Brain Thinks</div>
|
|
1168
|
+
<button class="nav-item" data-tab="sabb" data-title="Sub-Agents" title="When a tool spawns helper agents, the brain shares the right context with them.">
|
|
1169
|
+
<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>
|
|
1170
|
+
Sub-Agents<span class="badge-count" id="badge-sabb">0</span>
|
|
1171
|
+
</button>
|
|
1172
|
+
<button class="nav-item" data-tab="causal" data-title="Decision Trails" title="Trace why a decision was made — what led to it and what it caused.">
|
|
1173
|
+
<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>
|
|
1174
|
+
Decision Trails<span class="badge-count" id="badge-causal">0</span>
|
|
1175
|
+
</button>
|
|
1176
|
+
<button class="nav-item" data-tab="dream" data-title="Reflections" title="While idle, the brain reviews past work and finds patterns and lessons.">
|
|
1177
|
+
<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>
|
|
1178
|
+
Reflections<span class="badge-count" id="badge-dream">0</span>
|
|
1179
|
+
</button>
|
|
1180
|
+
<button class="nav-item" data-tab="debate" data-title="Debates" title="Have multiple AI viewpoints argue a tough decision, then get a verdict.">
|
|
1181
|
+
<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>
|
|
1182
|
+
Debates
|
|
1183
|
+
</button>
|
|
1184
|
+
<button class="nav-item" data-tab="premortem" data-title="Risk Check" title="Before a big task, see what went wrong last time so you can avoid it.">
|
|
1185
|
+
<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>
|
|
1186
|
+
Risk Check
|
|
1187
|
+
</button>
|
|
1188
|
+
<button class="nav-item" data-tab="branch" data-title="Branch Memory" title="Memory that follows your git branch, so each branch has its own context.">
|
|
1189
|
+
<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>
|
|
1190
|
+
Branch Memory
|
|
1191
|
+
</button>
|
|
1192
|
+
<button class="nav-item" data-tab="forget" data-title="Memory Cleanup" title="Old, unused memories fade while important ones are kept — like human memory.">
|
|
1193
|
+
<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>
|
|
1194
|
+
Memory Cleanup
|
|
1195
|
+
</button>
|
|
869
1196
|
</div>
|
|
870
1197
|
|
|
871
|
-
<div class="nav-group">
|
|
872
|
-
<div class="nav-group-label">
|
|
873
|
-
<button class="nav-item" data-tab="
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
<button class="nav-item" data-tab="
|
|
878
|
-
|
|
1198
|
+
<div class="nav-group" data-adv="1">
|
|
1199
|
+
<div class="nav-group-label">Safety & Trust</div>
|
|
1200
|
+
<button class="nav-item" data-tab="collision" data-title="Conflicts" title="Catches two tools about to edit the same code at the same time.">
|
|
1201
|
+
<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>
|
|
1202
|
+
Conflicts<span class="badge-count" id="badge-collision">0</span>
|
|
1203
|
+
</button>
|
|
1204
|
+
<button class="nav-item" data-tab="reputation" data-title="Trust Scores" title="How reliable each AI tool has been, based on its track record.">
|
|
1205
|
+
<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>
|
|
1206
|
+
Trust Scores
|
|
1207
|
+
</button>
|
|
1208
|
+
<button class="nav-item" data-tab="calibration" data-title="Accuracy" title="How well each tool's confidence matches how often it's actually right.">
|
|
1209
|
+
<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>
|
|
1210
|
+
Accuracy
|
|
1211
|
+
</button>
|
|
1212
|
+
<button class="nav-item" data-tab="attention" data-title="Focus Map" title="Shows which memories most influenced a particular decision.">
|
|
1213
|
+
<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>
|
|
1214
|
+
Focus Map
|
|
1215
|
+
</button>
|
|
1216
|
+
<button class="nav-item" data-tab="formal" data-title="Code Rules" title="Turn plain-language rules into checks your linters can enforce.">
|
|
1217
|
+
<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>
|
|
1218
|
+
Code Rules
|
|
1219
|
+
</button>
|
|
1220
|
+
<button class="nav-item" data-tab="privacy" data-title="Privacy & Safety" title="Keep everything offline and block risky actions.">
|
|
1221
|
+
<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>
|
|
1222
|
+
Privacy & Safety
|
|
1223
|
+
</button>
|
|
879
1224
|
</div>
|
|
880
1225
|
|
|
881
|
-
<div class="nav-group">
|
|
1226
|
+
<div class="nav-group" data-adv="1">
|
|
1227
|
+
<div class="nav-group-label">Cost & Sharing</div>
|
|
1228
|
+
<button class="nav-item" data-tab="tokens" data-title="Cost & Usage" title="Track how much your AI tools are spending and where you can save.">
|
|
1229
|
+
<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>
|
|
1230
|
+
Cost & Usage
|
|
1231
|
+
</button>
|
|
1232
|
+
<button class="nav-item" data-tab="pr" data-title="Code Review" title="Generate a review for a pull request using what the brain knows.">
|
|
1233
|
+
<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>
|
|
1234
|
+
Code Review
|
|
1235
|
+
</button>
|
|
1236
|
+
<button class="nav-item" data-tab="voice" data-title="Voice" title="Talk to the brain and hear answers, hands-free.">
|
|
1237
|
+
<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>
|
|
1238
|
+
Voice
|
|
1239
|
+
</button>
|
|
1240
|
+
<button class="nav-item" data-tab="team" data-title="Team Sync" title="Share brain memory with your teammates.">
|
|
1241
|
+
<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>
|
|
1242
|
+
Team Sync
|
|
1243
|
+
</button>
|
|
1244
|
+
<button class="nav-item" data-tab="exchange" data-title="Share Brain" title="Export a memory pack to share, or import one from someone else.">
|
|
1245
|
+
<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>
|
|
1246
|
+
Share Brain
|
|
1247
|
+
</button>
|
|
1248
|
+
</div>
|
|
1249
|
+
|
|
1250
|
+
<div class="nav-group" data-adv="1">
|
|
882
1251
|
<div class="nav-group-label">System</div>
|
|
883
|
-
<button class="nav-item" data-tab="features" data-title="
|
|
884
|
-
|
|
885
|
-
|
|
1252
|
+
<button class="nav-item" data-tab="features" data-title="Features" title="Turn individual brain features on or off.">
|
|
1253
|
+
<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>
|
|
1254
|
+
Features
|
|
1255
|
+
</button>
|
|
1256
|
+
<button class="nav-item" data-tab="models" data-title="AI Models" title="Choose and configure which AI models the brain uses.">
|
|
1257
|
+
<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>
|
|
1258
|
+
AI Models
|
|
1259
|
+
</button>
|
|
886
1260
|
</div>
|
|
887
1261
|
</aside>
|
|
888
1262
|
|
|
@@ -893,21 +1267,21 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
893
1267
|
<div class="accent-bar"></div>
|
|
894
1268
|
<div class="section-header">
|
|
895
1269
|
<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"><
|
|
897
|
-
<div class="section-subtitle">
|
|
1270
|
+
<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="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>Welcome to your Shadow Brain</h1>
|
|
1271
|
+
<div class="section-subtitle">A shared memory for your AI coding tools — private, on your machine. Here's what it knows and is doing right now.</div>
|
|
898
1272
|
</div>
|
|
899
1273
|
<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>
|
|
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>
|
|
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>
|
|
1274
|
+
<button class="btn" onclick="runDream()" title="Have the brain review recent work and find patterns"><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>Reflect Now</button>
|
|
1275
|
+
<button class="btn" onclick="runForget()" title="Tidy memory — fade old entries, keep the important ones"><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>Clean Up</button>
|
|
1276
|
+
<button class="btn danger" onclick="toggleAirGap()" title="Block all outgoing network — fully offline"><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>Go Offline</button>
|
|
903
1277
|
</div>
|
|
904
1278
|
</div>
|
|
905
1279
|
|
|
906
1280
|
<div class="grid cols-4">
|
|
907
1281
|
<div class="card stat-card accent">
|
|
908
|
-
<div class="label">
|
|
1282
|
+
<div class="label">Tools Connected</div>
|
|
909
1283
|
<div class="value accent" id="ov-agents">—</div>
|
|
910
|
-
<div class="trend">
|
|
1284
|
+
<div class="trend">AI tools linked to your brain</div>
|
|
911
1285
|
</div>
|
|
912
1286
|
<div class="card stat-card accent">
|
|
913
1287
|
<div class="label">Memories Stored</div>
|
|
@@ -915,20 +1289,46 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
915
1289
|
<div class="trend" id="ov-memories-sub">across all projects</div>
|
|
916
1290
|
</div>
|
|
917
1291
|
<div class="card stat-card accent">
|
|
918
|
-
<div class="label">
|
|
1292
|
+
<div class="label">Helper Agents</div>
|
|
919
1293
|
<div class="value" id="ov-subagents">—</div>
|
|
920
1294
|
<div class="trend" id="ov-subagents-sub">graduated / quarantined</div>
|
|
921
1295
|
</div>
|
|
922
1296
|
<div class="card stat-card accent">
|
|
923
|
-
<div class="label">
|
|
924
|
-
<div class="value" style="
|
|
925
|
-
<div class="trend
|
|
1297
|
+
<div class="label">Active Tool</div>
|
|
1298
|
+
<div class="value" style="font-size:22px" id="ov-current-agent">—</div>
|
|
1299
|
+
<div class="trend" id="ov-current-agent-sub">using the brain right now</div>
|
|
1300
|
+
</div>
|
|
1301
|
+
</div>
|
|
1302
|
+
|
|
1303
|
+
<div class="card accent span-full mt-lg const-hero" onclick="switchTab('intelligence','')" style="cursor:pointer">
|
|
1304
|
+
<div class="const-hero-inner">
|
|
1305
|
+
<div class="const-hero-art">
|
|
1306
|
+
<svg viewBox="0 0 120 90" xmlns="http://www.w3.org/2000/svg">
|
|
1307
|
+
<line x1="60" y1="45" x2="24" y2="20" class="he"/><line x1="60" y1="45" x2="98" y2="22" class="he"/>
|
|
1308
|
+
<line x1="60" y1="45" x2="22" y2="68" class="he"/><line x1="60" y1="45" x2="100" y2="66" class="he"/>
|
|
1309
|
+
<line x1="60" y1="45" x2="60" y2="14" class="he"/><line x1="24" y1="20" x2="22" y2="68" class="he"/>
|
|
1310
|
+
<circle cx="60" cy="45" r="9" fill="#18ffff"/>
|
|
1311
|
+
<circle cx="24" cy="20" r="5" fill="#10e68c"/><circle cx="98" cy="22" r="5" fill="#ffb53f"/>
|
|
1312
|
+
<circle cx="22" cy="68" r="5" fill="#a855f7"/><circle cx="100" cy="66" r="4" fill="#18ffff"/>
|
|
1313
|
+
<circle cx="60" cy="14" r="4" fill="#ec4899"/>
|
|
1314
|
+
</svg>
|
|
1315
|
+
</div>
|
|
1316
|
+
<div class="const-hero-body">
|
|
1317
|
+
<div class="const-hero-title">Memory Constellation</div>
|
|
1318
|
+
<div class="const-hero-sub">Explore every memory, project, agent & tag as a living, importance-ranked graph.</div>
|
|
1319
|
+
<div class="const-hero-stats">
|
|
1320
|
+
<span><b id="hero-const-nodes">—</b> nodes</span>
|
|
1321
|
+
<span><b id="hero-const-edges">—</b> links</span>
|
|
1322
|
+
<span><b id="hero-const-mem">—</b> memories</span>
|
|
1323
|
+
</div>
|
|
1324
|
+
</div>
|
|
1325
|
+
<button class="btn primary lg" onclick="event.stopPropagation();switchTab('intelligence','')">Explore <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="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg></button>
|
|
926
1326
|
</div>
|
|
927
1327
|
</div>
|
|
928
1328
|
|
|
929
1329
|
<div class="grid cols-3 mt-lg">
|
|
930
1330
|
<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
|
|
1331
|
+
<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>Reported Token Usage by Tool</div></div>
|
|
932
1332
|
<div class="chart-wrap"><canvas id="chart-tokens"></canvas></div>
|
|
933
1333
|
</div>
|
|
934
1334
|
<div class="card">
|
|
@@ -938,6 +1338,13 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
938
1338
|
</div>
|
|
939
1339
|
|
|
940
1340
|
<div class="grid cols-2 mt-lg">
|
|
1341
|
+
<div class="card span-full accent">
|
|
1342
|
+
<div class="card-header">
|
|
1343
|
+
<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"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>Live Pulse <span class="live-badge" id="ov-pulse-badge">LIVE</span></div>
|
|
1344
|
+
<span class="pill cyan" id="ov-pulse-rate">0 / min</span>
|
|
1345
|
+
</div>
|
|
1346
|
+
<div id="ov-pulse" class="brain-feed" style="max-height:300px;overflow-y:auto"><div class="empty-tight">Awaiting live activity…</div></div>
|
|
1347
|
+
</div>
|
|
941
1348
|
<div class="card">
|
|
942
1349
|
<div class="card-header"><div class="card-title">Module Status</div></div>
|
|
943
1350
|
<div id="ov-modules"></div>
|
|
@@ -949,7 +1356,169 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
949
1356
|
</div>
|
|
950
1357
|
</section>
|
|
951
1358
|
|
|
1359
|
+
<!-- ════ INTELLIGENCE (Memory Constellation + Analytics) ═════════ -->
|
|
1360
|
+
<section class="section" id="tab-intelligence">
|
|
1361
|
+
<div class="accent-bar"></div>
|
|
1362
|
+
<div class="section-header">
|
|
1363
|
+
<div>
|
|
1364
|
+
<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"><circle cx="12" cy="12" r="3"/><circle cx="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="5" cy="18" r="2"/><circle cx="19" cy="18" r="2"/><line x1="7" y1="7" x2="10" y2="10.5"/><line x1="17" y1="7" x2="14" y2="10.5"/><line x1="7" y1="17" x2="10" y2="13.5"/><line x1="17" y1="17" x2="14" y2="13.5"/></svg>Intelligence</h1>
|
|
1365
|
+
<div class="section-subtitle">Memory Constellation — every memory, project, agent & tag as a living graph, sized by importance</div>
|
|
1366
|
+
</div>
|
|
1367
|
+
<div class="section-actions">
|
|
1368
|
+
<input id="const-search" class="act-search" placeholder="Search nodes…" oninput="highlightConstSearch()">
|
|
1369
|
+
<button class="btn" onclick="rebuildConstellation()" title="Rebuild from the latest brain state"><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"/><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>Rebuild</button>
|
|
1370
|
+
<button class="btn" onclick="resetConstellationView()" title="Reset zoom & pan"><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="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>Reset</button>
|
|
1371
|
+
</div>
|
|
1372
|
+
</div>
|
|
1373
|
+
|
|
1374
|
+
<div class="filter-chips" id="const-chips">
|
|
1375
|
+
<button class="chip active" data-ntype="" onclick="setConstFilter(this,'')">All</button>
|
|
1376
|
+
<button class="chip" data-ntype="memory" onclick="setConstFilter(this,'memory')">Memories</button>
|
|
1377
|
+
<button class="chip" data-ntype="project" onclick="setConstFilter(this,'project')">Projects</button>
|
|
1378
|
+
<button class="chip" data-ntype="agent" onclick="setConstFilter(this,'agent')">Agents</button>
|
|
1379
|
+
<button class="chip" data-ntype="tag" onclick="setConstFilter(this,'tag')">Tags</button>
|
|
1380
|
+
</div>
|
|
1381
|
+
|
|
1382
|
+
<div class="card" style="padding:0;overflow:hidden">
|
|
1383
|
+
<div class="const-wrap" id="const-wrap">
|
|
1384
|
+
<canvas id="const-canvas"></canvas>
|
|
1385
|
+
<div class="const-legend">
|
|
1386
|
+
<div class="lg-row"><span class="lg-dot" style="background:#18ffff"></span>Memory</div>
|
|
1387
|
+
<div class="lg-row"><span class="lg-dot" style="background:#10e68c"></span>Project</div>
|
|
1388
|
+
<div class="lg-row"><span class="lg-dot" style="background:#ffb53f"></span>Agent</div>
|
|
1389
|
+
<div class="lg-row"><span class="lg-dot" style="background:#a855f7"></span>Tag</div>
|
|
1390
|
+
</div>
|
|
1391
|
+
<div class="const-meta" id="const-meta">—</div>
|
|
1392
|
+
<div class="const-hint">scroll to zoom · drag to pan · click a node</div>
|
|
1393
|
+
<div class="const-empty" id="const-empty">
|
|
1394
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><circle cx="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="5" cy="18" r="2"/><circle cx="19" cy="18" r="2"/></svg>
|
|
1395
|
+
<div class="ce-title">No memories yet</div>
|
|
1396
|
+
<div class="ce-msg">Attach Shadow Brain to your agents and let them work — memories will appear here as a living constellation.</div>
|
|
1397
|
+
</div>
|
|
1398
|
+
<div class="const-drawer" id="const-drawer">
|
|
1399
|
+
<button class="const-drawer-close" onclick="closeConstDrawer()" aria-label="Close">×</button>
|
|
1400
|
+
<div id="const-drawer-body"></div>
|
|
1401
|
+
</div>
|
|
1402
|
+
<div class="const-tip" id="const-tip"></div>
|
|
1403
|
+
</div>
|
|
1404
|
+
</div>
|
|
1405
|
+
|
|
1406
|
+
<!-- Analytics strip -->
|
|
1407
|
+
<div class="grid cols-4 mt-lg">
|
|
1408
|
+
<div class="card stat-card accent"><div class="label">Total Memories</div><div class="value accent" id="intel-total">—</div><div class="trend" id="intel-total-sub">in the brain</div></div>
|
|
1409
|
+
<div class="card stat-card accent"><div class="label">Source Coverage</div><div class="value" id="intel-coverage">—</div><div class="meter mt"><div class="fill" id="intel-coverage-meter" style="width:0%"></div></div></div>
|
|
1410
|
+
<div class="card stat-card accent"><div class="label">Fresh (24h)</div><div class="value" id="intel-recent">—</div><div class="trend up" id="intel-recent-sub">new memories</div></div>
|
|
1411
|
+
<div class="card stat-card accent"><div class="label">High Importance</div><div class="value" id="intel-highimp">—</div><div class="trend" id="intel-highimp-sub">score ≥ 0.75</div></div>
|
|
1412
|
+
</div>
|
|
1413
|
+
<div class="grid cols-2 mt-lg">
|
|
1414
|
+
<div class="card"><div class="card-header"><div class="card-title">Top Agents by Contribution</div></div><div class="chart-wrap"><canvas id="chart-intel-agents"></canvas></div></div>
|
|
1415
|
+
<div class="card"><div class="card-header"><div class="card-title">Memory Categories</div></div><div class="chart-wrap"><canvas id="chart-intel-cats"></canvas></div></div>
|
|
1416
|
+
</div>
|
|
1417
|
+
<div class="grid cols-2 mt-lg">
|
|
1418
|
+
<div class="card"><div class="card-header"><div class="card-title">Top Tags</div></div><div id="intel-tags" class="tag-cloud"></div></div>
|
|
1419
|
+
<div class="card"><div class="card-header"><div class="card-title">Recent Memories</div></div><div id="intel-recent-list" class="stack" style="max-height:260px;overflow-y:auto"></div></div>
|
|
1420
|
+
</div>
|
|
1421
|
+
</section>
|
|
1422
|
+
|
|
952
1423
|
<!-- ════ LIVE GRAPH ══════════════════════════════════════════════ -->
|
|
1424
|
+
<!-- WW2BR BRAIN -->
|
|
1425
|
+
<section class="section" id="tab-ww2br">
|
|
1426
|
+
<div class="accent-bar"></div>
|
|
1427
|
+
<div class="section-header">
|
|
1428
|
+
<div>
|
|
1429
|
+
<h1 class="section-title">WW2BR Brain View</h1>
|
|
1430
|
+
<div class="section-subtitle">Live project memory, recall, and integration status for the current game-development workflow</div>
|
|
1431
|
+
</div>
|
|
1432
|
+
<div class="section-actions">
|
|
1433
|
+
<button class="btn primary" onclick="refreshWW2BR()">Refresh</button>
|
|
1434
|
+
</div>
|
|
1435
|
+
</div>
|
|
1436
|
+
|
|
1437
|
+
<div class="workflow-strip">
|
|
1438
|
+
<div>
|
|
1439
|
+
<div class="field" style="margin:0">
|
|
1440
|
+
<label>Active project path</label>
|
|
1441
|
+
<input class="workflow-path" id="ww2-project-path" value="D:\\New Game"/>
|
|
1442
|
+
</div>
|
|
1443
|
+
</div>
|
|
1444
|
+
<button class="btn" onclick="refreshWW2BR()">Load</button>
|
|
1445
|
+
</div>
|
|
1446
|
+
|
|
1447
|
+
<div id="ww2-warnings" class="warning-stack"></div>
|
|
1448
|
+
|
|
1449
|
+
<div class="grid cols-4">
|
|
1450
|
+
<div class="card stat-card">
|
|
1451
|
+
<div class="label">Global Memories</div>
|
|
1452
|
+
<div class="value accent" id="ww2-stat-memories">0</div>
|
|
1453
|
+
<div class="trend" id="ww2-stat-scope">project scoped</div>
|
|
1454
|
+
</div>
|
|
1455
|
+
<div class="card stat-card">
|
|
1456
|
+
<div class="label">Brain Projects</div>
|
|
1457
|
+
<div class="value" id="ww2-stat-projects">0</div>
|
|
1458
|
+
<div class="trend">known roots</div>
|
|
1459
|
+
</div>
|
|
1460
|
+
<div class="card stat-card">
|
|
1461
|
+
<div class="label">Agent Hooks</div>
|
|
1462
|
+
<div class="value" id="ww2-stat-hooks">0</div>
|
|
1463
|
+
<div class="trend">wired for this path</div>
|
|
1464
|
+
</div>
|
|
1465
|
+
<div class="card stat-card">
|
|
1466
|
+
<div class="label">Hit Rate</div>
|
|
1467
|
+
<div class="value" id="ww2-stat-hit">0%</div>
|
|
1468
|
+
<div class="trend" id="ww2-stat-sync">last sync unknown</div>
|
|
1469
|
+
</div>
|
|
1470
|
+
</div>
|
|
1471
|
+
|
|
1472
|
+
<div class="grid cols-3 mt-lg">
|
|
1473
|
+
<div class="card">
|
|
1474
|
+
<div class="card-header"><div class="card-title">Memory Quality</div></div>
|
|
1475
|
+
<div id="ww2-quality" class="stack"></div>
|
|
1476
|
+
</div>
|
|
1477
|
+
<div class="card span-2">
|
|
1478
|
+
<div class="card-header">
|
|
1479
|
+
<div class="card-title">Context Continuity Pack</div>
|
|
1480
|
+
<button class="btn sm" onclick="openWW2ResumePack()">Markdown</button>
|
|
1481
|
+
</div>
|
|
1482
|
+
<div id="ww2-resume-pack"></div>
|
|
1483
|
+
</div>
|
|
1484
|
+
</div>
|
|
1485
|
+
|
|
1486
|
+
<div class="grid cols-3 mt-lg">
|
|
1487
|
+
<div class="card span-2">
|
|
1488
|
+
<div class="card-header">
|
|
1489
|
+
<div class="card-title">Recent Brain Timeline</div>
|
|
1490
|
+
<button class="btn sm" onclick="refreshWW2BR()">Refresh</button>
|
|
1491
|
+
</div>
|
|
1492
|
+
<div id="ww2-timeline" class="brain-feed"></div>
|
|
1493
|
+
</div>
|
|
1494
|
+
<div class="card">
|
|
1495
|
+
<div class="card-header"><div class="card-title">Agent Integrations</div></div>
|
|
1496
|
+
<div id="ww2-agents" class="ops-agent-grid"></div>
|
|
1497
|
+
</div>
|
|
1498
|
+
</div>
|
|
1499
|
+
|
|
1500
|
+
<div class="grid cols-3 mt-lg">
|
|
1501
|
+
<div class="card">
|
|
1502
|
+
<div class="card-header"><div class="card-title">Memory Categories</div></div>
|
|
1503
|
+
<div id="ww2-categories"></div>
|
|
1504
|
+
</div>
|
|
1505
|
+
<div class="card span-2">
|
|
1506
|
+
<div class="card-header"><div class="card-title">Recall Query</div></div>
|
|
1507
|
+
<div class="grid cols-3">
|
|
1508
|
+
<div class="field span-2"><label>Query</label><input id="ww2-query" placeholder="Gate 2, operator proof, lobby blocker"/></div>
|
|
1509
|
+
<div class="field"><label>Category</label><input id="ww2-category" placeholder="optional"/></div>
|
|
1510
|
+
</div>
|
|
1511
|
+
<div class="flex gap">
|
|
1512
|
+
<button class="btn primary" onclick="runWW2Recall()">Recall</button>
|
|
1513
|
+
<button class="btn" onclick="setWW2Query('Gate 2 visual proof')">Gate 2</button>
|
|
1514
|
+
<button class="btn" onclick="setWW2Query('operator hands rifle hold')">Operator</button>
|
|
1515
|
+
<button class="btn" onclick="setWW2Query('lobby verification blockers')">Lobby</button>
|
|
1516
|
+
</div>
|
|
1517
|
+
<div id="ww2-recall-results" class="brain-feed mt"></div>
|
|
1518
|
+
</div>
|
|
1519
|
+
</div>
|
|
1520
|
+
</section>
|
|
1521
|
+
|
|
953
1522
|
<section class="section" id="tab-graph">
|
|
954
1523
|
<div class="accent-bar"></div>
|
|
955
1524
|
<div class="section-header">
|
|
@@ -1027,7 +1596,7 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1027
1596
|
<div class="section-header">
|
|
1028
1597
|
<div>
|
|
1029
1598
|
<h1 class="section-title">Memory Browser</h1>
|
|
1030
|
-
<div class="section-subtitle">Full-text search
|
|
1599
|
+
<div class="section-subtitle">Full-text search plus live memory quality, provenance, freshness, and agent coverage</div>
|
|
1031
1600
|
</div>
|
|
1032
1601
|
</div>
|
|
1033
1602
|
<div class="card">
|
|
@@ -1046,26 +1615,153 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1046
1615
|
</div>
|
|
1047
1616
|
</section>
|
|
1048
1617
|
|
|
1618
|
+
<!-- ════ LIVE BRAIN ═══════════════════════════════════════════════ -->
|
|
1619
|
+
<!-- MEMORY SEEDER -->
|
|
1620
|
+
<section class="section" id="tab-seeder">
|
|
1621
|
+
<div class="accent-bar"></div>
|
|
1622
|
+
<div class="section-header">
|
|
1623
|
+
<div>
|
|
1624
|
+
<h1 class="section-title">Memory Seeder</h1>
|
|
1625
|
+
<div class="section-subtitle">Research drafts, approval gates, and source-backed global brain writes</div>
|
|
1626
|
+
</div>
|
|
1627
|
+
<div class="section-actions">
|
|
1628
|
+
<button class="btn" onclick="refreshSeedDrafts()">Refresh</button>
|
|
1629
|
+
</div>
|
|
1630
|
+
</div>
|
|
1631
|
+
|
|
1632
|
+
<div class="grid cols-3">
|
|
1633
|
+
<div class="card span-2">
|
|
1634
|
+
<div class="card-header"><div class="card-title">Seed Research Draft</div></div>
|
|
1635
|
+
<div class="grid cols-2">
|
|
1636
|
+
<div class="field"><label>Project path</label><input id="seed-project-path" placeholder="C:\\path\\to\\project"/></div>
|
|
1637
|
+
<div class="field"><label>Topic</label><input id="seed-topic" placeholder="memory routing, architecture, release, debugging"/></div>
|
|
1638
|
+
</div>
|
|
1639
|
+
<div class="field"><label>Objective</label><input id="seed-objective" placeholder="what the next agent must learn and reuse"/></div>
|
|
1640
|
+
<div class="grid cols-3">
|
|
1641
|
+
<div class="field"><label>Research agent</label><select id="seed-research-agent"><option>codex</option><option>claude-code</option><option>cursor</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>
|
|
1642
|
+
<div class="field"><label>Approver</label><select id="seed-approver-agent"><option>user</option><option>codex</option><option>claude-code</option><option>cursor</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>
|
|
1643
|
+
<div class="field"><label>Approval mode</label><select id="seed-approval-mode"><option>user</option><option>agent</option><option>dual</option></select></div>
|
|
1644
|
+
</div>
|
|
1645
|
+
<div class="grid cols-3">
|
|
1646
|
+
<div class="field"><label>Category</label><input id="seed-category" value="project.research"/></div>
|
|
1647
|
+
<div class="field"><label>Tags</label><input id="seed-tags" placeholder="enterprise,memory,release"/></div>
|
|
1648
|
+
<div class="field"><label>Importance</label><input id="seed-importance" type="number" min="0.05" max="1" step="0.05" value="0.78"/></div>
|
|
1649
|
+
</div>
|
|
1650
|
+
<div class="field"><label>Sources</label><textarea id="seed-sources" rows="2" placeholder="README.md, CHANGELOG.md, docs/launch/RELEASE_NOTES_v6.0.3.md"></textarea></div>
|
|
1651
|
+
<div class="field"><label>Research notes</label><textarea id="seed-content" rows="4" placeholder="facts, constraints, tradeoffs, exact commands, source-backed decisions"></textarea></div>
|
|
1652
|
+
<div class="flex gap">
|
|
1653
|
+
<button class="btn primary" onclick="quickSeed()" title="Create, approve, and save to the brain in one step">⚡ Quick Save</button>
|
|
1654
|
+
<button class="btn" onclick="createSeedDraft()" title="Create a draft that waits for approval">Create Draft (review first)</button>
|
|
1655
|
+
<button class="btn" onclick="copyLivePathToSeeder()">Use Live Brain Path</button>
|
|
1656
|
+
</div>
|
|
1657
|
+
<div class="section-subtitle" style="margin-top:8px">Quick Save is the fast, one-click path. Use "Create Draft" when you want a review/approval step before it's saved.</div>
|
|
1658
|
+
</div>
|
|
1659
|
+
<div class="card">
|
|
1660
|
+
<div class="card-header"><div class="card-title">Seeder Status</div></div>
|
|
1661
|
+
<div id="seed-status" class="stack"></div>
|
|
1662
|
+
</div>
|
|
1663
|
+
</div>
|
|
1664
|
+
|
|
1665
|
+
<div class="card mt-lg">
|
|
1666
|
+
<div class="card-header">
|
|
1667
|
+
<div class="card-title">Approval Queue</div>
|
|
1668
|
+
<button class="btn sm" onclick="refreshSeedDrafts()">Refresh</button>
|
|
1669
|
+
</div>
|
|
1670
|
+
<div id="seed-drafts" class="brain-feed"></div>
|
|
1671
|
+
</div>
|
|
1672
|
+
</section>
|
|
1673
|
+
|
|
1674
|
+
<section class="section" id="tab-livebrain">
|
|
1675
|
+
<div class="accent-bar"></div>
|
|
1676
|
+
<div class="section-header">
|
|
1677
|
+
<div>
|
|
1678
|
+
<h1 class="section-title">Live Brain</h1>
|
|
1679
|
+
<div class="section-subtitle">Memory health, source coverage, recall eval, and agent briefing for the current workspace</div>
|
|
1680
|
+
</div>
|
|
1681
|
+
<div class="section-actions">
|
|
1682
|
+
<button class="btn" onclick="openSeederForLiveBrain()">Seed Memory</button>
|
|
1683
|
+
<button class="btn primary" onclick="refreshLiveBrain()">Refresh</button>
|
|
1684
|
+
</div>
|
|
1685
|
+
</div>
|
|
1686
|
+
<div class="grid cols-4">
|
|
1687
|
+
<div class="card stat-card"><div class="label">Total Memories</div><div class="value" id="mem-total">0</div><div class="trend" id="mem-sampled">sampled</div></div>
|
|
1688
|
+
<div class="card stat-card"><div class="label">Source Coverage</div><div class="value" id="mem-source">0%</div><div class="trend">evidence-backed records</div></div>
|
|
1689
|
+
<div class="card stat-card"><div class="label">Fresh 7 Days</div><div class="value" id="mem-fresh">0</div><div class="trend">recent learning</div></div>
|
|
1690
|
+
<div class="card stat-card"><div class="label">Seed Queue</div><div class="value" id="mem-seed-open">0</div><div class="trend" id="mem-seed-sub">approval queue</div></div>
|
|
1691
|
+
</div>
|
|
1692
|
+
<div class="grid cols-3 mt">
|
|
1693
|
+
<div class="card"><div class="card-header"><div class="card-title">Top Categories</div></div><div id="mem-categories"></div></div>
|
|
1694
|
+
<div class="card"><div class="card-header"><div class="card-title">Agent Coverage</div></div><div id="mem-agents"></div></div>
|
|
1695
|
+
<div class="card"><div class="card-header"><div class="card-title">Top Tags</div></div><div id="mem-tags"></div></div>
|
|
1696
|
+
</div>
|
|
1697
|
+
<div class="card mt">
|
|
1698
|
+
<div class="field-row">
|
|
1699
|
+
<div class="field"><label>Project path</label><input id="live-project-path" placeholder="C:\\path\\to\\project"/></div>
|
|
1700
|
+
<div class="field"><label>Eval queries</label><input id="live-queries" placeholder="current blocker|do not repeat|verified command"/></div>
|
|
1701
|
+
</div>
|
|
1702
|
+
</div>
|
|
1703
|
+
<div class="grid cols-4 mt">
|
|
1704
|
+
<div class="card stat-card"><div class="label">Memories</div><div class="value" id="live-memories">0</div><div class="trend" id="live-scope">scope</div></div>
|
|
1705
|
+
<div class="card stat-card"><div class="label">Quality</div><div class="value" id="live-quality">0</div><div class="trend">average score</div></div>
|
|
1706
|
+
<div class="card stat-card"><div class="label">Sources</div><div class="value" id="live-sources">0%</div><div class="trend">coverage</div></div>
|
|
1707
|
+
<div class="card stat-card"><div class="label">Recall</div><div class="value" id="live-recall">0</div><div class="trend">avg hits/query</div></div>
|
|
1708
|
+
</div>
|
|
1709
|
+
<div class="grid cols-2 mt-lg">
|
|
1710
|
+
<div class="card">
|
|
1711
|
+
<div class="card-header"><div class="card-title">Recommended Actions</div></div>
|
|
1712
|
+
<div id="live-actions" class="stack"></div>
|
|
1713
|
+
</div>
|
|
1714
|
+
<div class="card">
|
|
1715
|
+
<div class="card-header"><div class="card-title">Recall Eval</div></div>
|
|
1716
|
+
<div id="live-eval" class="stack"></div>
|
|
1717
|
+
</div>
|
|
1718
|
+
<div class="card">
|
|
1719
|
+
<div class="card-header"><div class="card-title">Top Memories</div></div>
|
|
1720
|
+
<div id="live-top" class="stack"></div>
|
|
1721
|
+
</div>
|
|
1722
|
+
<div class="card">
|
|
1723
|
+
<div class="card-header"><div class="card-title">Agent Briefing</div></div>
|
|
1724
|
+
<pre class="mono" id="live-brief" style="max-height:360px;overflow:auto;background:var(--bg-elev-1);padding:12px;border-radius:6px;white-space:pre-wrap"></pre>
|
|
1725
|
+
</div>
|
|
1726
|
+
</div>
|
|
1727
|
+
</section>
|
|
1728
|
+
|
|
1049
1729
|
<!-- ════ ACTIVITY LOG ════════════════════════════════════════════ -->
|
|
1050
1730
|
<section class="section" id="tab-activity">
|
|
1051
1731
|
<div class="accent-bar"></div>
|
|
1052
1732
|
<div class="section-header">
|
|
1053
1733
|
<div>
|
|
1054
|
-
<h1 class="section-title">Activity Log</h1>
|
|
1055
|
-
<div class="section-subtitle">
|
|
1734
|
+
<h1 class="section-title">Activity Log <span class="live-badge" id="act-live-badge" title="Streaming live over WebSocket">LIVE</span></h1>
|
|
1735
|
+
<div class="section-subtitle">Realtime stream: memory writes, SABB spawns, collisions, dreams, seeds, consolidations, firewall blocks</div>
|
|
1056
1736
|
</div>
|
|
1057
1737
|
<div class="section-actions">
|
|
1058
|
-
<
|
|
1059
|
-
|
|
1060
|
-
|
|
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>
|
|
1738
|
+
<input id="act-search" class="act-search" placeholder="Search activity…" oninput="renderActivityTable()">
|
|
1739
|
+
<button class="btn" id="act-pause-btn" onclick="toggleActivityPause()" title="Pause/resume the live stream">Pause</button>
|
|
1740
|
+
<button class="btn danger" onclick="clearActivity()" title="Clear the activity view">Clear</button>
|
|
1066
1741
|
<button class="btn" onclick="refreshActivity()">Refresh</button>
|
|
1067
1742
|
</div>
|
|
1068
1743
|
</div>
|
|
1744
|
+
<div class="filter-chips" id="act-chips">
|
|
1745
|
+
<button class="chip active" data-type="" onclick="setActivityFilter(this,'')">All</button>
|
|
1746
|
+
<button class="chip" data-type="memory-write" onclick="setActivityFilter(this,'memory-write')">Memory</button>
|
|
1747
|
+
<button class="chip" data-type="dream" onclick="setActivityFilter(this,'dream')">Dreams</button>
|
|
1748
|
+
<button class="chip" data-type="seed-draft" onclick="setActivityFilter(this,'seed-draft')">Seeds</button>
|
|
1749
|
+
<button class="chip" data-type="collision" onclick="setActivityFilter(this,'collision')">Collisions</button>
|
|
1750
|
+
<button class="chip" data-type="subagent-spawn" onclick="setActivityFilter(this,'subagent-spawn')">Sub-Agents</button>
|
|
1751
|
+
<button class="chip" data-type="consolidation" onclick="setActivityFilter(this,'consolidation')">Consolidation</button>
|
|
1752
|
+
</div>
|
|
1753
|
+
<div class="grid cols-2 mb">
|
|
1754
|
+
<div class="card">
|
|
1755
|
+
<div class="card-header"><div class="card-title">Events / min</div><span class="pill cyan" id="act-rate-pill">0 / min</span></div>
|
|
1756
|
+
<div class="chart-wrap" style="height:120px"><canvas id="act-spark"></canvas></div>
|
|
1757
|
+
</div>
|
|
1758
|
+
<div class="card">
|
|
1759
|
+
<div class="card-header"><div class="card-title">Stream Stats</div></div>
|
|
1760
|
+
<div class="row"><span class="k">Events buffered</span><span class="v" id="act-stat-total">0</span></div>
|
|
1761
|
+
<div class="row"><span class="k">Live status</span><span class="v good" id="act-stat-live">streaming</span></div>
|
|
1762
|
+
<div class="row"><span class="k">Last event</span><span class="v" id="act-stat-last">—</span></div>
|
|
1763
|
+
</div>
|
|
1764
|
+
</div>
|
|
1069
1765
|
<div class="card" style="padding:0">
|
|
1070
1766
|
<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
1767
|
</div>
|
|
@@ -1121,7 +1817,13 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1121
1817
|
<div class="field"><label>Max depth</label><input id="causal-depth" value="8"/></div>
|
|
1122
1818
|
<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
1819
|
</div>
|
|
1124
|
-
|
|
1820
|
+
</div>
|
|
1821
|
+
</div>
|
|
1822
|
+
<div class="card mt-lg" style="padding:0">
|
|
1823
|
+
<div style="padding:14px 18px 6px"><div class="card-title">Causal Chain Graph</div></div>
|
|
1824
|
+
<div class="dag-wrap" id="causal-dag-wrap" style="margin:0 14px 14px">
|
|
1825
|
+
<canvas id="causal-dag"></canvas>
|
|
1826
|
+
<div class="dag-empty" id="causal-dag-empty">Enter a memory ID and trace ancestors or descendants to render its causal chain.</div>
|
|
1125
1827
|
</div>
|
|
1126
1828
|
</div>
|
|
1127
1829
|
<div class="card mt-lg">
|
|
@@ -1182,6 +1884,10 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1182
1884
|
<pre class="mono" id="rep-badge-output" style="margin-top:10px;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1183
1885
|
</div>
|
|
1184
1886
|
</div>
|
|
1887
|
+
<div class="card mt-lg">
|
|
1888
|
+
<div class="card-header"><div class="card-title">Agent Trust</div></div>
|
|
1889
|
+
<div id="rep-trust-grid" class="trust-grid"></div>
|
|
1890
|
+
</div>
|
|
1185
1891
|
<div class="card mt-lg" style="padding:0">
|
|
1186
1892
|
<div style="padding:14px 18px 6px"><div class="card-title">Agent Leaderboard</div></div>
|
|
1187
1893
|
<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>
|
|
@@ -1217,9 +1923,9 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1217
1923
|
<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
1924
|
<div class="card">
|
|
1219
1925
|
<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>
|
|
1926
|
+
<div class="field"><label>Candidate memory IDs (comma-separated, optional — blank uses recent)</label><input id="att-memories" placeholder="id1, id2, id3"/></div>
|
|
1221
1927
|
<button class="btn primary" onclick="runAttention()">Compute Attention</button>
|
|
1222
|
-
<
|
|
1928
|
+
<div id="att-output" style="margin-top:14px"></div>
|
|
1223
1929
|
</div>
|
|
1224
1930
|
</section>
|
|
1225
1931
|
|
|
@@ -1228,14 +1934,20 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1228
1934
|
<div class="accent-bar"></div>
|
|
1229
1935
|
<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
1936
|
<div class="grid cols-4">
|
|
1231
|
-
<div class="card stat-card"><div class="label">
|
|
1937
|
+
<div class="card stat-card"><div class="label">Current Agent</div><div class="value" id="tok-agent">—</div><div class="trend" id="tok-runtime">runtime context</div></div>
|
|
1938
|
+
<div class="card stat-card"><div class="label">Total Spend</div><div class="value" id="tok-total">$—</div><div class="trend">all historical records</div></div>
|
|
1939
|
+
<div class="card stat-card"><div class="label">Last 7 Days</div><div class="value" id="tok-recent">$—</div><div class="trend">recent records only</div></div>
|
|
1940
|
+
<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>
|
|
1941
|
+
</div>
|
|
1942
|
+
<div class="grid cols-2 mt">
|
|
1232
1943
|
<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
1944
|
<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
1945
|
</div>
|
|
1946
|
+
<div class="warning-banner mt" id="tok-runtime-note"><strong>Accuracy</strong><span>Token records are historical. Current agent and model spend are shown separately.</span></div>
|
|
1236
1947
|
<div class="grid cols-2 mt-lg">
|
|
1237
|
-
<div class="card"><div class="card-header"><div class="card-title">Spend by
|
|
1238
|
-
<div class="card"><div class="card-header"><div class="card-title">
|
|
1948
|
+
<div class="card"><div class="card-header"><div class="card-title">Recent Spend by Agent</div></div><div class="chart-wrap"><canvas id="chart-tokens-agent"></canvas></div><div id="tok-agent-table" class="mt"></div></div>
|
|
1949
|
+
<div class="card"><div class="card-header"><div class="card-title">Historical Spend by Model</div></div><div class="chart-wrap"><canvas id="chart-tokens-model"></canvas></div><div id="tok-model-table" class="mt"></div></div>
|
|
1950
|
+
<div class="card span-2"><div class="card-header"><div class="card-title">Savings Suggestions</div></div><div id="tok-suggestions"></div></div>
|
|
1239
1951
|
</div>
|
|
1240
1952
|
</section>
|
|
1241
1953
|
|
|
@@ -1271,7 +1983,11 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1271
1983
|
<!-- ════ CALIBRATION ═════════════════════════════════════════════ -->
|
|
1272
1984
|
<section class="section" id="tab-calibration">
|
|
1273
1985
|
<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>
|
|
1986
|
+
<div class="section-header"><div><h1 class="section-title">Confidence Calibration Monitor</h1><div class="section-subtitle">Brier scores & trust weight per agent per category</div></div></div>
|
|
1987
|
+
<div class="card mb-lg">
|
|
1988
|
+
<div class="card-header"><div class="card-title">Brier Score vs Trust Weight by Agent</div></div>
|
|
1989
|
+
<div class="chart-wrap"><canvas id="chart-calibration"></canvas></div>
|
|
1990
|
+
</div>
|
|
1275
1991
|
<div class="card" style="padding:0">
|
|
1276
1992
|
<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
1993
|
</div>
|
|
@@ -1281,7 +1997,8 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1281
1997
|
<section class="section" id="tab-branch">
|
|
1282
1998
|
<div class="accent-bar"></div>
|
|
1283
1999
|
<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="
|
|
2000
|
+
<div class="grid cols-4" id="branch-cards"></div>
|
|
2001
|
+
<div class="card mt-lg"><div class="card-header"><div class="card-title">Branch-Specific Categories</div></div><div id="branch-cats" class="tag-cloud"></div></div>
|
|
1285
2002
|
</section>
|
|
1286
2003
|
|
|
1287
2004
|
<!-- ════ PRIVACY ═════════════════════════════════════════════════ -->
|
|
@@ -1492,28 +2209,38 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1492
2209
|
══════════════════════════════════════════════════════════════════════ -->
|
|
1493
2210
|
<script>
|
|
1494
2211
|
const FEATURES = [
|
|
1495
|
-
['
|
|
1496
|
-
['
|
|
1497
|
-
['
|
|
1498
|
-
['
|
|
1499
|
-
['
|
|
1500
|
-
['
|
|
1501
|
-
['
|
|
1502
|
-
['
|
|
1503
|
-
['
|
|
1504
|
-
['
|
|
1505
|
-
['
|
|
1506
|
-
['
|
|
1507
|
-
['
|
|
1508
|
-
['
|
|
1509
|
-
['
|
|
1510
|
-
['
|
|
1511
|
-
['
|
|
1512
|
-
['
|
|
1513
|
-
['
|
|
1514
|
-
['
|
|
1515
|
-
['
|
|
1516
|
-
['
|
|
2212
|
+
['intelligence','Brain Map','Visual map of every memory, project and tool'],
|
|
2213
|
+
['memory','Memories','Browse and search saved knowledge'],
|
|
2214
|
+
['chat','Ask the Brain','Ask questions in plain language'],
|
|
2215
|
+
['activity','Activity','Live feed of what the brain is doing'],
|
|
2216
|
+
['agents','Connected Tools','Which AI tools are linked to the brain'],
|
|
2217
|
+
['livebrain','Brain Health','Memory quality with tips to improve'],
|
|
2218
|
+
['ww2br','Project Memory','Memory and status for one project'],
|
|
2219
|
+
['graph','Live Network','Live animation of tools talking to the brain'],
|
|
2220
|
+
['seeder','Add Knowledge','Research a topic and save it as verified knowledge'],
|
|
2221
|
+
['garden','Memory Garden','Watch your memories grow over time'],
|
|
2222
|
+
['sabb','Sub-Agents','Share the right context with helper agents'],
|
|
2223
|
+
['causal','Decision Trails','Trace why a decision was made'],
|
|
2224
|
+
['dream','Reflections','Idle-time review that finds patterns and lessons'],
|
|
2225
|
+
['debate','Debates','Multiple AI viewpoints argue a tough decision'],
|
|
2226
|
+
['premortem','Risk Check','See what went wrong last time before a big task'],
|
|
2227
|
+
['branch','Branch Memory','Memory that follows your git branch'],
|
|
2228
|
+
['forget','Memory Cleanup','Old memories fade, important ones stay'],
|
|
2229
|
+
['collision','Conflicts','Catch two tools editing the same code'],
|
|
2230
|
+
['reputation','Trust Scores','How reliable each AI tool has been'],
|
|
2231
|
+
['calibration','Accuracy','How well confidence matches being right'],
|
|
2232
|
+
['attention','Focus Map','Which memories shaped a decision'],
|
|
2233
|
+
['formal','Code Rules','Turn plain rules into linter checks'],
|
|
2234
|
+
['privacy','Privacy & Safety','Stay offline and block risky actions'],
|
|
2235
|
+
['tokens','Cost & Usage','Track AI spend and find savings'],
|
|
2236
|
+
['pr','Code Review','Generate a pull-request review'],
|
|
2237
|
+
['voice','Voice','Talk to the brain hands-free'],
|
|
2238
|
+
['team','Team Sync','Share memory with teammates'],
|
|
2239
|
+
['exchange','Share Brain','Export or import memory packs'],
|
|
2240
|
+
['features','Features','Turn individual features on or off'],
|
|
2241
|
+
['models','AI Models','Choose which AI models the brain uses'],
|
|
2242
|
+
['config','Settings','Set up and adjust Shadow Brain'],
|
|
2243
|
+
['overview','Home','Your starting point and summary'],
|
|
1517
2244
|
];
|
|
1518
2245
|
|
|
1519
2246
|
let ws = null;
|
|
@@ -1527,9 +2254,21 @@ function switchTab(id, title) {
|
|
|
1527
2254
|
document.querySelectorAll('.section').forEach(el => el.classList.toggle('active', el.id === 'tab-' + id));
|
|
1528
2255
|
if (window.innerWidth <= 768) document.getElementById('sidebar').classList.remove('open');
|
|
1529
2256
|
onTabOpen(id);
|
|
2257
|
+
animateSectionIn(id);
|
|
2258
|
+
}
|
|
2259
|
+
// Subtle staggered card entrance on real tab switches. Guarded so it no-ops
|
|
2260
|
+
// if GSAP's CDN failed to load. NOT called from the 5s refresh loop, so cards
|
|
2261
|
+
// don't re-fade on every poll.
|
|
2262
|
+
function animateSectionIn(id) {
|
|
2263
|
+
if (!window.gsap) return;
|
|
2264
|
+
const sec = document.getElementById('tab-' + id);
|
|
2265
|
+
if (!sec) return;
|
|
2266
|
+
const cards = sec.querySelectorAll('.card');
|
|
2267
|
+
if (!cards.length) return;
|
|
2268
|
+
try { window.gsap.from(cards, { opacity: 0, y: 12, duration: 0.34, stagger: 0.035, ease: 'power2.out', clearProps: 'opacity,transform' }); } catch (e) {}
|
|
1530
2269
|
}
|
|
1531
2270
|
function onTabOpen(tab) {
|
|
1532
|
-
const fn = { overview: refreshOverview, graph: startGraph, agents: refreshAgents, memory: () => searchMemory(), chat: refreshChat, activity: refreshActivity,
|
|
2271
|
+
const fn = { overview: refreshOverview, intelligence: refreshIntelligence, ww2br: refreshWW2BR, graph: startGraph, agents: refreshAgents, memory: () => { refreshMemoryIntelligence(); searchMemory(); }, seeder: refreshSeedDrafts, livebrain: refreshLiveBrain, chat: refreshChat, activity: refreshActivity,
|
|
1533
2272
|
sabb: refreshSABB, causal: refreshCausal, collision: refreshCollision, dream: refreshDream, reputation: refreshReputation,
|
|
1534
2273
|
tokens: refreshTokens, forget: refreshForget, formal: null, calibration: refreshCalibration, branch: refreshBranch,
|
|
1535
2274
|
privacy: refreshPrivacy, garden: refreshGarden, team: refreshTeam, exchange: refreshExchange, features: refreshFeatures,
|
|
@@ -1556,6 +2295,24 @@ function toggleTheme() {
|
|
|
1556
2295
|
} catch { document.documentElement.dataset.theme = 'light'; }
|
|
1557
2296
|
})();
|
|
1558
2297
|
|
|
2298
|
+
// ──── Simple / Advanced navigation mode ────────────────────────────────
|
|
2299
|
+
function setMode(mode) {
|
|
2300
|
+
const m = mode === 'advanced' ? 'advanced' : 'simple';
|
|
2301
|
+
document.documentElement.dataset.navmode = m;
|
|
2302
|
+
try { localStorage.setItem('hive.navmode', m); } catch {}
|
|
2303
|
+
document.querySelectorAll('.mode-btn').forEach(b => b.classList.toggle('active', b.dataset.mode === m));
|
|
2304
|
+
// If we just hid the tab the user is on, gently return them to Home.
|
|
2305
|
+
if (m === 'simple') {
|
|
2306
|
+
const active = document.querySelector('.nav-item.active');
|
|
2307
|
+
if (active && active.closest('[data-adv]')) switchTab('overview', 'Home');
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
(function initMode(){
|
|
2311
|
+
let m = 'simple';
|
|
2312
|
+
try { m = localStorage.getItem('hive.navmode') || 'simple'; } catch {}
|
|
2313
|
+
setMode(m);
|
|
2314
|
+
})();
|
|
2315
|
+
|
|
1559
2316
|
// ──── API helpers ──────────────────────────────────────────────────────
|
|
1560
2317
|
async function api(path, body) {
|
|
1561
2318
|
try {
|
|
@@ -1566,6 +2323,37 @@ async function api(path, body) {
|
|
|
1566
2323
|
return ct.includes('application/json') ? await res.json() : await res.text();
|
|
1567
2324
|
} catch (err) { toast('Network error', err.message, 'error'); return null; }
|
|
1568
2325
|
}
|
|
2326
|
+
function htmlEscape(value) {
|
|
2327
|
+
return String(value ?? '')
|
|
2328
|
+
.replace(/&/g, '&')
|
|
2329
|
+
.replace(/</g, '<')
|
|
2330
|
+
.replace(/>/g, '>')
|
|
2331
|
+
.replace(/"/g, '"')
|
|
2332
|
+
.replace(/'/g, ''');
|
|
2333
|
+
}
|
|
2334
|
+
function fmtDate(value) {
|
|
2335
|
+
const d = new Date(value);
|
|
2336
|
+
return Number.isNaN(d.getTime()) ? 'unknown' : d.toLocaleString();
|
|
2337
|
+
}
|
|
2338
|
+
// Reusable animated empty-state. cta = {label, onclick} or null.
|
|
2339
|
+
// title/msg are inserted as-is (allow pre-escaped entities) — author-controlled strings only.
|
|
2340
|
+
function emptyState(icon, title, msg, cta) {
|
|
2341
|
+
const icons = {
|
|
2342
|
+
shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
|
|
2343
|
+
check: '<polyline points="20 6 9 17 4 12"/>',
|
|
2344
|
+
target: '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>',
|
|
2345
|
+
'git-branch': '<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"/>',
|
|
2346
|
+
brain: '<circle cx="12" cy="12" r="3"/><circle cx="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="5" cy="18" r="2"/><circle cx="19" cy="18" r="2"/>',
|
|
2347
|
+
inbox: '<polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/>',
|
|
2348
|
+
};
|
|
2349
|
+
const path = icons[icon] || icons.inbox;
|
|
2350
|
+
let btn = '';
|
|
2351
|
+
if (cta && cta.label) {
|
|
2352
|
+
const handler = cta.onclick ? ' onclick="' + cta.onclick + '"' : '';
|
|
2353
|
+
btn = '<button class="btn primary es-cta"' + handler + '>' + htmlEscape(cta.label) + '</button>';
|
|
2354
|
+
}
|
|
2355
|
+
return '<div class="empty-state"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">' + path + '</svg><div class="es-title">' + title + '</div><div class="es-msg">' + msg + '</div>' + btn + '</div>';
|
|
2356
|
+
}
|
|
1569
2357
|
|
|
1570
2358
|
// ──── Toasts ───────────────────────────────────────────────────────────
|
|
1571
2359
|
function toast(title, body, variant='success') {
|
|
@@ -1627,7 +2415,7 @@ async function refresh() {
|
|
|
1627
2415
|
animateNum('stat-agents', hive.totalAgentsConnected ?? 0);
|
|
1628
2416
|
animateNum('stat-memories', hive.totalMemoriesStored ?? 0);
|
|
1629
2417
|
animateNum('stat-dreams', hive.modules.dream?.totalDreams ?? 0);
|
|
1630
|
-
|
|
2418
|
+
animateNum('stat-tools', hive.totalAgentsConnected ?? 0);
|
|
1631
2419
|
document.getElementById('badge-sabb').textContent = hive.modules.sabb?.totalSpawns ?? 0;
|
|
1632
2420
|
document.getElementById('badge-causal').textContent = hive.modules.causal?.totalLinks ?? 0;
|
|
1633
2421
|
document.getElementById('badge-collision').textContent = hive.modules.collision?.collisionsDetected ?? 0;
|
|
@@ -1644,8 +2432,10 @@ async function refreshOverview() {
|
|
|
1644
2432
|
animateNum('ov-memories', hive.totalMemoriesStored ?? 0);
|
|
1645
2433
|
animateNum('ov-subagents', hive.modules.sabb?.totalSpawns ?? 0);
|
|
1646
2434
|
document.getElementById('ov-subagents-sub').textContent = \`grad: \${hive.modules.sabb?.graduated ?? 0} · quar: \${hive.modules.sabb?.quarantined ?? 0}\`;
|
|
1647
|
-
|
|
1648
|
-
document.getElementById('ov-
|
|
2435
|
+
const rt = hive.runtime || {};
|
|
2436
|
+
document.getElementById('ov-current-agent').textContent = rt.currentAgentDisplay || 'Unknown';
|
|
2437
|
+
document.getElementById('ov-current-agent-sub').textContent = rt.provider ? (rt.provider + (rt.model ? ' / ' + rt.model : '')) : 'using the brain right now';
|
|
2438
|
+
loadConstHeroStats();
|
|
1649
2439
|
|
|
1650
2440
|
const moduleRows = [
|
|
1651
2441
|
['SABB spawns', hive.modules.sabb?.totalSpawns ?? 0, 'good'],
|
|
@@ -1661,6 +2451,7 @@ async function refreshOverview() {
|
|
|
1661
2451
|
|
|
1662
2452
|
const health = [
|
|
1663
2453
|
['Local-first', hive.localFirst ? 'ON' : 'OFF', hive.localFirst ? 'good' : 'warn'],
|
|
2454
|
+
['Current agent', hive.runtime?.currentAgentDisplay || 'Unknown', hive.runtime?.currentAgentTool === 'codex' ? 'good' : ''],
|
|
1664
2455
|
['Air-gap', hive.modules.airGap?.enabled ? 'ENABLED' : 'disabled', hive.modules.airGap?.enabled ? 'good' : ''],
|
|
1665
2456
|
['Version', hive.version, ''],
|
|
1666
2457
|
['Monthly cost', '$' + (hive.modules.tokenEconomy?.monthlyProjectionUsd?.toFixed?.(2) ?? '0.00'), 'warn'],
|
|
@@ -1670,7 +2461,7 @@ async function refreshOverview() {
|
|
|
1670
2461
|
|
|
1671
2462
|
// Tokens chart
|
|
1672
2463
|
const tok = hive.modules.tokenEconomy;
|
|
1673
|
-
if (tok
|
|
2464
|
+
if (tok) drawTokensChart(tok.recentByAgent || tok.byAgent || {});
|
|
1674
2465
|
|
|
1675
2466
|
// Timeline
|
|
1676
2467
|
const timeline = await api('/api/v6/activity-log?limit=15') ?? [];
|
|
@@ -1683,33 +2474,186 @@ function typeToPill(type) {
|
|
|
1683
2474
|
if (type === 'subagent-spawn') return 'cyan';
|
|
1684
2475
|
if (type === 'collision') return 'red';
|
|
1685
2476
|
if (type === 'quarantine') return 'yellow';
|
|
2477
|
+
if (type === 'seed-draft') return 'yellow';
|
|
2478
|
+
if (type === 'memory-seeded') return 'green';
|
|
1686
2479
|
if (['dream','revisit','counterfactual','consolidation','pattern-discovery','contradiction'].includes(type)) return 'purple';
|
|
1687
2480
|
return '';
|
|
1688
2481
|
}
|
|
1689
2482
|
|
|
2483
|
+
function getWW2ProjectPath() {
|
|
2484
|
+
return document.getElementById('ww2-project-path')?.value?.trim() || 'D:\\\\New Game';
|
|
2485
|
+
}
|
|
2486
|
+
async function refreshWW2BR() {
|
|
2487
|
+
const projectDir = getWW2ProjectPath();
|
|
2488
|
+
const data = await api('/api/v6/ww2br-ops?projectDir=' + encodeURIComponent(projectDir));
|
|
2489
|
+
if (!data) return;
|
|
2490
|
+
|
|
2491
|
+
const stats = data.stats || {};
|
|
2492
|
+
const hooked = (data.agents || []).filter(a => a.hookInstalled).length;
|
|
2493
|
+
document.getElementById('badge-ww2br').textContent = data.recent?.length ?? 0;
|
|
2494
|
+
animateNum('ww2-stat-memories', stats.totalEntries ?? 0);
|
|
2495
|
+
animateNum('ww2-stat-projects', stats.totalProjects ?? 0);
|
|
2496
|
+
animateNum('ww2-stat-hooks', hooked);
|
|
2497
|
+
document.getElementById('ww2-stat-hit').textContent = Math.round((stats.hitRate ?? 0) * 100) + '%';
|
|
2498
|
+
document.getElementById('ww2-stat-scope').textContent = data.projectScoped ? 'project scoped' : 'global fallback';
|
|
2499
|
+
document.getElementById('ww2-stat-sync').textContent = stats.lastSync ? 'sync ' + fmtDate(stats.lastSync) : 'last sync unknown';
|
|
2500
|
+
|
|
2501
|
+
const warnings = data.warnings || [];
|
|
2502
|
+
document.getElementById('ww2-warnings').innerHTML = warnings.length
|
|
2503
|
+
? warnings.map(w => '<div class="warning-banner"><strong>Warning</strong><span>' + htmlEscape(w) + '</span></div>').join('')
|
|
2504
|
+
: '';
|
|
2505
|
+
|
|
2506
|
+
document.getElementById('ww2-timeline').innerHTML = renderMemoryItems(data.recent || [], 'No memories found for this workflow yet.');
|
|
2507
|
+
document.getElementById('ww2-agents').innerHTML = renderWW2Agents(data.agents || []);
|
|
2508
|
+
document.getElementById('ww2-categories').innerHTML = renderWW2Categories(data.categories || {});
|
|
2509
|
+
document.getElementById('ww2-quality').innerHTML = renderWW2Quality(data.quality);
|
|
2510
|
+
document.getElementById('ww2-resume-pack').innerHTML = renderWW2ResumePack(data.resumePack);
|
|
2511
|
+
|
|
2512
|
+
const query = document.getElementById('ww2-query');
|
|
2513
|
+
if (query && !query.value) query.value = 'Gate 2 visual proof';
|
|
2514
|
+
if (!document.getElementById('ww2-recall-results').innerHTML.trim()) {
|
|
2515
|
+
document.getElementById('ww2-recall-results').innerHTML = '<div class="empty-tight">Run a recall query to inspect relevant brain memory.</div>';
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
function renderMemoryItems(items, emptyMessage) {
|
|
2519
|
+
if (!items || !items.length) return '<div class="empty-tight">' + htmlEscape(emptyMessage) + '</div>';
|
|
2520
|
+
return items.map(item => {
|
|
2521
|
+
const confidence = item.importance !== undefined ? Math.round((item.importance || 0) * 100) + '%' : '';
|
|
2522
|
+
const sources = item.sources || (item.metadata && item.metadata.sources) || [];
|
|
2523
|
+
const tags = item.tags || (item.metadata && item.metadata.tags) || [];
|
|
2524
|
+
const quality = item.qualityScore !== undefined ? '<span class="pill">Q ' + htmlEscape(item.qualityScore) + '</span>' : '';
|
|
2525
|
+
const sourceLine = sources.length
|
|
2526
|
+
? '<div class="content" style="font-size:10.5px;color:var(--text-3);margin-top:6px">Sources: ' + sources.slice(0, 3).map(s => '<code>' + htmlEscape(s) + '</code>').join(' · ') + '</div>'
|
|
2527
|
+
: '<div class="content" style="font-size:10.5px;color:var(--warning);margin-top:6px">Sources: none recorded</div>';
|
|
2528
|
+
const tagLine = tags.length ? '<span class="pill">' + tags.slice(0, 3).map(htmlEscape).join(', ') + '</span>' : '';
|
|
2529
|
+
return '<div class="memory-item">' +
|
|
2530
|
+
'<div class="meta">' +
|
|
2531
|
+
'<span class="pill cyan">' + htmlEscape(item.agentTool || item.agent || 'brain') + '</span>' +
|
|
2532
|
+
'<span class="pill">' + htmlEscape(item.category || 'memory') + '</span>' +
|
|
2533
|
+
(confidence ? '<span class="pill green">' + confidence + '</span>' : '') +
|
|
2534
|
+
quality +
|
|
2535
|
+
tagLine +
|
|
2536
|
+
'<span>' + fmtDate(item.createdAt || item.lastAccessed) + '</span>' +
|
|
2537
|
+
'</div>' +
|
|
2538
|
+
'<div class="content">' + htmlEscape(item.content || item.detail || '').slice(0, 420) + '</div>' +
|
|
2539
|
+
sourceLine +
|
|
2540
|
+
'</div>';
|
|
2541
|
+
}).join('');
|
|
2542
|
+
}
|
|
2543
|
+
function renderWW2Quality(quality) {
|
|
2544
|
+
if (!quality) return '<div class="empty-tight">No memory quality data.</div>';
|
|
2545
|
+
const sourcePct = Math.round((quality.sourceCoverage || 0) * 100);
|
|
2546
|
+
const scoreCls = quality.avgScore >= 75 ? 'good' : quality.avgScore >= 55 ? 'warn' : 'bad';
|
|
2547
|
+
return [
|
|
2548
|
+
'<div class="row"><span class="k">Average score</span><span class="v ' + scoreCls + '">' + htmlEscape(quality.avgScore ?? 0) + '/100</span></div>',
|
|
2549
|
+
'<div class="row"><span class="k">Source coverage</span><span class="v ' + (sourcePct >= 70 ? 'good' : 'warn') + '">' + sourcePct + '%</span></div>',
|
|
2550
|
+
'<div class="row"><span class="k">Stale memories</span><span class="v ' + (quality.stale ? 'warn' : 'good') + '">' + htmlEscape(quality.stale || 0) + '</span></div>',
|
|
2551
|
+
'<div class="row"><span class="k">Duplicate clusters</span><span class="v ' + (quality.duplicates?.length ? 'warn' : 'good') + '">' + htmlEscape(quality.duplicates?.length || 0) + '</span></div>',
|
|
2552
|
+
].join('');
|
|
2553
|
+
}
|
|
2554
|
+
function renderWW2ResumePack(pack) {
|
|
2555
|
+
if (!pack) return '<div class="empty-tight">No resume pack generated.</div>';
|
|
2556
|
+
const warnings = pack.warnings && pack.warnings.length
|
|
2557
|
+
? '<div class="warning-stack" style="margin:10px 0 0">' + pack.warnings.slice(0, 3).map(w => '<div class="warning-banner"><strong>Review</strong><span>' + htmlEscape(w) + '</span></div>').join('') + '</div>'
|
|
2558
|
+
: '';
|
|
2559
|
+
const blockers = pack.blockers && pack.blockers.length
|
|
2560
|
+
? '<div class="brain-feed mt">' + renderMemoryItems(pack.blockers.slice(0, 3), 'No blockers in pack.') + '</div>'
|
|
2561
|
+
: '<div class="empty-tight">No blockers in pack.</div>';
|
|
2562
|
+
const stats = pack.stats || {};
|
|
2563
|
+
return '<div class="grid cols-3">' +
|
|
2564
|
+
'<div class="row"><span class="k">Docs</span><span class="v">' + htmlEscape(stats.docCount ?? 0) + '</span></div>' +
|
|
2565
|
+
'<div class="row"><span class="k">Memories</span><span class="v">' + htmlEscape(stats.memoryCount ?? 0) + '</span></div>' +
|
|
2566
|
+
'<div class="row"><span class="k">Quality</span><span class="v">' + htmlEscape(stats.avgQualityScore ?? 0) + '/100</span></div>' +
|
|
2567
|
+
'</div>' +
|
|
2568
|
+
'<div class="mt" style="font-size:12px;color:var(--text-2)">Generated ' + htmlEscape(fmtDate(pack.generatedAt)) + '. Uses visible docs and stored memories only.</div>' +
|
|
2569
|
+
warnings +
|
|
2570
|
+
'<div class="mt">' + blockers + '</div>';
|
|
2571
|
+
}
|
|
2572
|
+
function openWW2ResumePack() {
|
|
2573
|
+
const url = '/api/v6/resume-pack?format=markdown&projectDir=' + encodeURIComponent(getWW2ProjectPath());
|
|
2574
|
+
window.open(url, '_blank', 'noopener');
|
|
2575
|
+
}
|
|
2576
|
+
function renderWW2Agents(agents) {
|
|
2577
|
+
if (!agents.length) return '<div class="empty-tight">No known agent integrations returned.</div>';
|
|
2578
|
+
return agents.map(agent => {
|
|
2579
|
+
const cls = agent.hookInstalled ? 'green' : agent.detected ? 'cyan' : '';
|
|
2580
|
+
const hook = agent.hookInstalled ? 'hooked' : 'not wired';
|
|
2581
|
+
const detected = agent.detected ? 'detected' : 'not found';
|
|
2582
|
+
return '<div class="ops-agent">' +
|
|
2583
|
+
'<div class="name">' + htmlEscape(agent.displayName || agent.name) + '</div>' +
|
|
2584
|
+
'<div class="status">' +
|
|
2585
|
+
'<span class="pill ' + cls + '">' + hook + '</span>' +
|
|
2586
|
+
'<span class="pill">' + detected + '</span>' +
|
|
2587
|
+
'</div>' +
|
|
2588
|
+
'</div>';
|
|
2589
|
+
}).join('');
|
|
2590
|
+
}
|
|
2591
|
+
function renderWW2Categories(categories) {
|
|
2592
|
+
const rows = Object.entries(categories).sort((a, b) => b[1] - a[1]);
|
|
2593
|
+
if (!rows.length) return '<div class="empty-tight">No categories yet.</div>';
|
|
2594
|
+
return rows.map(([name, count]) => '<div class="row"><span class="k">' + htmlEscape(name) + '</span><span class="v">' + count + '</span></div>').join('');
|
|
2595
|
+
}
|
|
2596
|
+
function setWW2Query(query) {
|
|
2597
|
+
document.getElementById('ww2-query').value = query;
|
|
2598
|
+
runWW2Recall();
|
|
2599
|
+
}
|
|
2600
|
+
async function runWW2Recall() {
|
|
2601
|
+
const resultsEl = document.getElementById('ww2-recall-results');
|
|
2602
|
+
const query = document.getElementById('ww2-query').value.trim();
|
|
2603
|
+
const category = document.getElementById('ww2-category').value.trim();
|
|
2604
|
+
resultsEl.innerHTML = '<div class="empty-tight">Searching brain memory...</div>';
|
|
2605
|
+
const data = await api('/api/v6/ww2br-recall', {
|
|
2606
|
+
projectDir: getWW2ProjectPath(),
|
|
2607
|
+
query,
|
|
2608
|
+
category: category || undefined,
|
|
2609
|
+
limit: 12,
|
|
2610
|
+
});
|
|
2611
|
+
if (!data) return;
|
|
2612
|
+
const prefix = data.projectScoped ? '' : '<div class="warning-banner"><strong>Fallback</strong><span>No project-scoped matches; showing global brain recall results.</span></div>';
|
|
2613
|
+
resultsEl.innerHTML = prefix + renderMemoryItems(data.results || [], 'No recall matches for that query.');
|
|
2614
|
+
}
|
|
2615
|
+
|
|
1690
2616
|
// ──── Charts ───────────────────────────────────────────────────────────
|
|
1691
2617
|
function chartTheme() {
|
|
1692
2618
|
const dark = document.documentElement.dataset.theme !== 'light';
|
|
1693
2619
|
return { grid: dark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)', text: dark ? '#a4afc4' : '#3d4864' };
|
|
1694
2620
|
}
|
|
1695
|
-
function
|
|
1696
|
-
|
|
2621
|
+
function spendLabels(buckets) {
|
|
2622
|
+
return Object.keys(buckets || {}).sort((a, b) => (buckets[b]?.spendUsd || 0) - (buckets[a]?.spendUsd || 0));
|
|
2623
|
+
}
|
|
2624
|
+
function drawSpendBarChart(canvasId, chartKey, buckets) {
|
|
2625
|
+
const ctx = document.getElementById(canvasId);
|
|
1697
2626
|
if (!ctx) return;
|
|
1698
|
-
const labels =
|
|
1699
|
-
const data = labels.map(l =>
|
|
2627
|
+
const labels = spendLabels(buckets);
|
|
2628
|
+
const data = labels.map(l => buckets[l].spendUsd || 0);
|
|
1700
2629
|
const t = chartTheme();
|
|
1701
|
-
if (charts
|
|
1702
|
-
charts
|
|
2630
|
+
if (charts[chartKey]) charts[chartKey].destroy();
|
|
2631
|
+
charts[chartKey] = 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=>'$'+Number(v).toFixed(2)}}} } });
|
|
2632
|
+
}
|
|
2633
|
+
function drawTokensChart(byAgent) {
|
|
2634
|
+
drawSpendBarChart('chart-tokens', 'tokens', byAgent || {});
|
|
2635
|
+
}
|
|
2636
|
+
function drawTokensAgentChart(byAgent) {
|
|
2637
|
+
drawSpendBarChart('chart-tokens-agent', 'tokensAgent', byAgent || {});
|
|
1703
2638
|
}
|
|
1704
2639
|
function drawTokensModelChart(byModel) {
|
|
1705
2640
|
const ctx = document.getElementById('chart-tokens-model');
|
|
1706
2641
|
if (!ctx) return;
|
|
1707
|
-
const labels =
|
|
1708
|
-
const data = labels.map(l => byModel[l].spendUsd);
|
|
2642
|
+
const labels = spendLabels(byModel);
|
|
2643
|
+
const data = labels.map(l => byModel[l].spendUsd || 0);
|
|
1709
2644
|
const t = chartTheme();
|
|
1710
2645
|
if (charts.tokensModel) charts.tokensModel.destroy();
|
|
1711
2646
|
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
2647
|
}
|
|
2648
|
+
function renderSpendRows(buckets, emptyMessage) {
|
|
2649
|
+
const labels = spendLabels(buckets).slice(0, 8);
|
|
2650
|
+
if (!labels.length) return '<div class="empty-tight">' + htmlEscape(emptyMessage) + '</div>';
|
|
2651
|
+
return labels.map(label => {
|
|
2652
|
+
const b = buckets[label] || {};
|
|
2653
|
+
const last = b.lastSeen ? new Date(b.lastSeen).toLocaleString() : '—';
|
|
2654
|
+
return \`<div class="row"><span class="k">\${htmlEscape(label)} <span class="tag-code">\${b.calls || 0} calls</span></span><span class="v">$\${Number(b.spendUsd || 0).toFixed(4)} <span class="mono" style="color:var(--text-3)">\${last}</span></span></div>\`;
|
|
2655
|
+
}).join('');
|
|
2656
|
+
}
|
|
1713
2657
|
function drawDreamChart(byType) {
|
|
1714
2658
|
const ctx = document.getElementById('chart-dreams');
|
|
1715
2659
|
if (!ctx) return;
|
|
@@ -1773,13 +2717,64 @@ async function traceCausal() {
|
|
|
1773
2717
|
const id = document.getElementById('causal-mem-id').value;
|
|
1774
2718
|
if (!id) return toast('Missing', 'Enter a memory ID', 'warn');
|
|
1775
2719
|
const res = await api('/api/v6/causal-trace', { memoryId: id, maxDepth: parseInt(document.getElementById('causal-depth').value || '8') });
|
|
1776
|
-
if (res)
|
|
2720
|
+
if (res) renderCausalDag(res, id);
|
|
1777
2721
|
}
|
|
1778
2722
|
async function influenceCausal() {
|
|
1779
2723
|
const id = document.getElementById('causal-mem-id').value;
|
|
1780
|
-
if (!id) return;
|
|
2724
|
+
if (!id) return toast('Missing', 'Enter a memory ID', 'warn');
|
|
1781
2725
|
const res = await api('/api/v6/causal-influence', { memoryId: id, maxDepth: 6 });
|
|
1782
|
-
if (res)
|
|
2726
|
+
if (res) renderCausalDag(res, id);
|
|
2727
|
+
}
|
|
2728
|
+
function renderCausalDag(chain, rootId) {
|
|
2729
|
+
const wrap = document.getElementById('causal-dag-wrap');
|
|
2730
|
+
const cv = document.getElementById('causal-dag');
|
|
2731
|
+
const empty = document.getElementById('causal-dag-empty');
|
|
2732
|
+
if (!cv || !wrap) return;
|
|
2733
|
+
const nodes = (chain && chain.nodes) || [];
|
|
2734
|
+
if (!nodes.length) { if (empty) { empty.style.display = 'flex'; empty.textContent = 'No causal links recorded for this memory yet.'; } return; }
|
|
2735
|
+
if (empty) empty.style.display = 'none';
|
|
2736
|
+
cv.width = wrap.clientWidth || 800; cv.height = wrap.clientHeight || 420;
|
|
2737
|
+
const W = cv.width, H = cv.height, ctx = cv.getContext('2d');
|
|
2738
|
+
// Layer nodes by depth
|
|
2739
|
+
const byDepth = {};
|
|
2740
|
+
let maxDepth = 0;
|
|
2741
|
+
for (const n of nodes) { const d = n.depth || 0; (byDepth[d] = byDepth[d] || []).push(n); if (d > maxDepth) maxDepth = d; }
|
|
2742
|
+
const pos = new Map();
|
|
2743
|
+
const layers = maxDepth + 1;
|
|
2744
|
+
for (let d = 0; d <= maxDepth; d++) {
|
|
2745
|
+
const row = byDepth[d] || [];
|
|
2746
|
+
const y = layers === 1 ? H / 2 : 40 + (d / Math.max(1, maxDepth)) * (H - 80);
|
|
2747
|
+
row.forEach((n, i) => { pos.set(n.id, { x: (i + 1) / (row.length + 1) * W, y: y, n: n }); });
|
|
2748
|
+
}
|
|
2749
|
+
ctx.clearRect(0, 0, W, H);
|
|
2750
|
+
// Links
|
|
2751
|
+
for (const l of (chain.links || [])) {
|
|
2752
|
+
const a = pos.get(l.causeId), b = pos.get(l.effectId);
|
|
2753
|
+
if (!a || !b) continue;
|
|
2754
|
+
ctx.strokeStyle = 'rgba(24,255,255,' + (0.25 + 0.5 * (l.strength || 0.5)) + ')';
|
|
2755
|
+
ctx.lineWidth = 1 + (l.strength || 0.5) * 2;
|
|
2756
|
+
ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
|
|
2757
|
+
// arrowhead
|
|
2758
|
+
const ang = Math.atan2(b.y - a.y, b.x - a.x);
|
|
2759
|
+
ctx.fillStyle = 'rgba(24,255,255,0.7)';
|
|
2760
|
+
ctx.beginPath(); ctx.moveTo(b.x - 11 * Math.cos(ang - 0.4), b.y - 11 * Math.sin(ang - 0.4)); ctx.lineTo(b.x, b.y); ctx.lineTo(b.x - 11 * Math.cos(ang + 0.4), b.y - 11 * Math.sin(ang + 0.4)); ctx.fill();
|
|
2761
|
+
}
|
|
2762
|
+
// Nodes
|
|
2763
|
+
for (const p of pos.values()) {
|
|
2764
|
+
const isRoot = p.n.id === rootId;
|
|
2765
|
+
const col = isRoot ? '#ec4899' : '#a855f7';
|
|
2766
|
+
const r = isRoot ? 13 : 9;
|
|
2767
|
+
const halo = ctx.createRadialGradient(p.x, p.y, r * 0.4, p.x, p.y, r * 2.4);
|
|
2768
|
+
halo.addColorStop(0, col + '88'); halo.addColorStop(1, 'rgba(0,0,0,0)');
|
|
2769
|
+
ctx.fillStyle = halo; ctx.beginPath(); ctx.arc(p.x, p.y, r * 2.4, 0, Math.PI * 2); ctx.fill();
|
|
2770
|
+
ctx.fillStyle = '#0b1020'; ctx.beginPath(); ctx.arc(p.x, p.y, r, 0, Math.PI * 2); ctx.fill();
|
|
2771
|
+
ctx.strokeStyle = col; ctx.lineWidth = 2; ctx.stroke();
|
|
2772
|
+
ctx.fillStyle = document.documentElement.dataset.theme === 'light' ? '#1a2030' : '#e8ecf4';
|
|
2773
|
+
ctx.font = '10px Inter, sans-serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
|
2774
|
+
const lbl = String(p.n.content || p.n.category || p.n.id).slice(0, 22);
|
|
2775
|
+
ctx.fillText(lbl, p.x, p.y + r + 10);
|
|
2776
|
+
}
|
|
2777
|
+
toast('Causal chain', nodes.length + ' nodes · ' + ((chain.links || []).length) + ' links');
|
|
1783
2778
|
}
|
|
1784
2779
|
async function linkCausal() {
|
|
1785
2780
|
const r = await api('/api/v6/causal-link', {
|
|
@@ -1828,6 +2823,28 @@ async function refreshReputation() {
|
|
|
1828
2823
|
document.getElementById('rep-leaderboard').innerHTML = top.length ? top.map(a =>
|
|
1829
2824
|
\`<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
2825
|
).join('') : '<tr><td colspan="4"><div class="empty">No signed decisions yet</div></td></tr>';
|
|
2826
|
+
const grid = document.getElementById('rep-trust-grid');
|
|
2827
|
+
if (grid) {
|
|
2828
|
+
if (!top.length) {
|
|
2829
|
+
grid.innerHTML = emptyState('shield', 'No trust receipts yet', 'Agents build a portable trust score by signing decision receipts. Run: shadow-brain reputation sign --agent <name>', null);
|
|
2830
|
+
} else {
|
|
2831
|
+
grid.innerHTML = top.map(function (a, i) {
|
|
2832
|
+
return '<div class="trust-card"><div class="trust-ring"><canvas id="trust-ring-' + i + '"></canvas><div class="tr-pct">' + Math.round((a.accuracy || 0) * 100) + '</div></div><div class="tc-name">' + htmlEscape(a.agentTool) + '</div><div class="tc-sub">' + a.decisions + ' decisions</div></div>';
|
|
2833
|
+
}).join('');
|
|
2834
|
+
top.forEach(function (a, i) { drawTrustRing('trust-ring-' + i, a.accuracy || 0); });
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
function drawTrustRing(id, frac) {
|
|
2839
|
+
const cv = document.getElementById(id); if (!cv || !cv.getContext) return;
|
|
2840
|
+
cv.width = 92; cv.height = 92;
|
|
2841
|
+
const ctx = cv.getContext('2d'), cx = 46, cy = 46, r = 38;
|
|
2842
|
+
ctx.clearRect(0, 0, 92, 92);
|
|
2843
|
+
ctx.lineWidth = 8; ctx.strokeStyle = 'rgba(120,140,180,0.18)';
|
|
2844
|
+
ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.stroke();
|
|
2845
|
+
const col = frac >= 0.8 ? '#10e68c' : frac >= 0.5 ? '#ffb53f' : '#ff5864';
|
|
2846
|
+
ctx.strokeStyle = col; ctx.lineCap = 'round';
|
|
2847
|
+
ctx.beginPath(); ctx.arc(cx, cy, r, -Math.PI / 2, -Math.PI / 2 + frac * Math.PI * 2); ctx.stroke();
|
|
1831
2848
|
}
|
|
1832
2849
|
async function genBadge() {
|
|
1833
2850
|
const res = await api('/api/v6/reputation-badge', { agentTool: document.getElementById('rep-agent').value, agentVersion: document.getElementById('rep-ver').value || undefined });
|
|
@@ -1849,32 +2866,91 @@ async function runDebate() {
|
|
|
1849
2866
|
|
|
1850
2867
|
// ──── Pre-mortem ───────────────────────────────────────────────────────
|
|
1851
2868
|
async function runPremortem() {
|
|
1852
|
-
const
|
|
2869
|
+
const task = document.getElementById('pm-task').value;
|
|
2870
|
+
if (!task.trim()) return toast('Missing', 'Describe the task first', 'warn');
|
|
2871
|
+
const res = await api('/api/v6/premortem-run', { taskDescription: task, projectDir: '' });
|
|
1853
2872
|
if (!res) return;
|
|
1854
|
-
document.getElementById('pm-output')
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
2873
|
+
const out = document.getElementById('pm-output');
|
|
2874
|
+
const sevColor = { critical: '#ff5864', high: '#ffb53f', medium: '#ffca28', low: '#10e68c' };
|
|
2875
|
+
const riskCol = res.riskScore > 0.6 ? '#ff5864' : res.riskScore > 0.3 ? '#ffb53f' : '#10e68c';
|
|
2876
|
+
const failures = res.failures || [];
|
|
2877
|
+
// Risk matrix buckets: rows = severity (crit..low), cols = probability quartile
|
|
2878
|
+
const sevOrder = ['critical', 'high', 'medium', 'low'];
|
|
2879
|
+
const cells = {};
|
|
2880
|
+
for (const f of failures) {
|
|
2881
|
+
const col = Math.min(3, Math.floor((f.probability || 0) * 4));
|
|
2882
|
+
const key = (f.severity || 'low') + ':' + col;
|
|
2883
|
+
cells[key] = (cells[key] || 0) + 1;
|
|
2884
|
+
}
|
|
2885
|
+
const cellColor = (sev, col) => {
|
|
2886
|
+
const sevW = { critical: 3, high: 2, medium: 1, low: 0 }[sev];
|
|
2887
|
+
const risk = (sevW + col) / 6;
|
|
2888
|
+
return risk > 0.6 ? 'rgba(255,88,100,0.85)' : risk > 0.35 ? 'rgba(255,181,63,0.8)' : 'rgba(16,230,140,0.7)';
|
|
2889
|
+
};
|
|
2890
|
+
let matrix = '<div class="risk-matrix"><div class="rm-h"></div><div class="rm-h">P 0-25%</div><div class="rm-h">25-50%</div><div class="rm-h">50-75%</div><div class="rm-h">75-100%</div>';
|
|
2891
|
+
for (const sev of sevOrder) {
|
|
2892
|
+
matrix += '<div class="rm-yl">' + sev + '</div>';
|
|
2893
|
+
for (let col = 0; col < 4; col++) {
|
|
2894
|
+
const c = cells[sev + ':' + col] || 0;
|
|
2895
|
+
matrix += c ? '<div class="rm-cell" style="background:' + cellColor(sev, col) + '">' + c + '</div>' : '<div class="rm-cell empty">·</div>';
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
matrix += '</div>';
|
|
2899
|
+
let html = '<div class="risk-head"><div class="risk-gauge"><canvas id="pm-gauge"></canvas><div class="rg-num" style="color:' + riskCol + '">' + Math.round(res.riskScore * 100) + '</div></div>';
|
|
2900
|
+
html += '<div><div style="font-size:13px;font-weight:700;margin-bottom:4px">Overall Risk Score</div><div style="font-size:12px;color:var(--text-2);max-width:520px;line-height:1.5">' + htmlEscape(res.summary || '') + '</div></div></div>';
|
|
2901
|
+
html += matrix;
|
|
2902
|
+
html += failures.map(function (f) {
|
|
2903
|
+
const col = sevColor[f.severity] || '#10e68c';
|
|
2904
|
+
return '<div class="fail-card" style="border-left-color:' + col + '"><div class="fc-head"><span class="pill" style="color:' + col + ';border-color:' + col + '">' + htmlEscape(f.severity) + '</span><span class="pill">p=' + (f.probability || 0).toFixed(2) + '</span><span class="pill">' + htmlEscape(f.source || '') + '</span></div><div class="fc-desc">' + htmlEscape(f.description || '') + '</div>' + (f.mitigation ? '<div class="fc-mit">' + htmlEscape(f.mitigation) + '</div>' : '') + '</div>';
|
|
2905
|
+
}).join('') || emptyState('check', 'No predicted failures', 'The pre-mortem surfaced no significant risks for this task based on past incidents and similar projects.', null);
|
|
2906
|
+
out.innerHTML = html;
|
|
2907
|
+
drawRiskGauge('pm-gauge', res.riskScore, riskCol);
|
|
2908
|
+
}
|
|
2909
|
+
function drawRiskGauge(id, frac, col) {
|
|
2910
|
+
const cv = document.getElementById(id); if (!cv || !cv.getContext) return;
|
|
2911
|
+
cv.width = 96; cv.height = 96;
|
|
2912
|
+
const ctx = cv.getContext('2d'), cx = 48, cy = 48, r = 38;
|
|
2913
|
+
ctx.clearRect(0, 0, 96, 96);
|
|
2914
|
+
ctx.lineWidth = 9; ctx.strokeStyle = 'rgba(120,140,180,0.18)';
|
|
2915
|
+
ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.stroke();
|
|
2916
|
+
ctx.strokeStyle = col; ctx.lineCap = 'round';
|
|
2917
|
+
ctx.beginPath(); ctx.arc(cx, cy, r, -Math.PI / 2, -Math.PI / 2 + Math.max(0.02, frac) * Math.PI * 2); ctx.stroke();
|
|
1858
2918
|
}
|
|
1859
2919
|
|
|
1860
2920
|
// ──── Attention ────────────────────────────────────────────────────────
|
|
1861
2921
|
async function runAttention() {
|
|
1862
2922
|
const ids = document.getElementById('att-memories').value.split(',').map(s => s.trim()).filter(Boolean);
|
|
1863
2923
|
const res = await api('/api/v6/attention-heatmap', { decisionText: document.getElementById('att-decision').value, candidateMemoryIds: ids });
|
|
1864
|
-
|
|
2924
|
+
const out = document.getElementById('att-output');
|
|
2925
|
+
if (!res) return;
|
|
2926
|
+
const weights = res.weights || [];
|
|
2927
|
+
if (!weights.length) { out.innerHTML = emptyState('target', 'No attention weights', 'Provide a decision and some candidate memory IDs (or let it use recent memories) to see which ones carried the most weight.', null); return; }
|
|
2928
|
+
out.innerHTML = '<div style="font-size:11.5px;color:var(--text-3);margin-bottom:10px">' + res.totalMemoriesConsidered + ' memories considered for: <strong style="color:var(--text-2)">' + htmlEscape(res.decisionText || '') + '</strong></div>' + weights.map(function (w) {
|
|
2929
|
+
const pct = (w.weight || 0) * 100;
|
|
2930
|
+
const col = pct > 60 ? '#ff5864' : pct > 30 ? '#ffb53f' : '#18ffff';
|
|
2931
|
+
return '<div class="heat-row"><div class="hr-top"><span>' + htmlEscape((w.memoryContent || '').slice(0, 80)) + '</span><span class="hr-pct" style="color:' + col + '">' + pct.toFixed(1) + '%</span></div><div class="hr-track"><div class="hr-fill" style="width:' + Math.max(2, pct) + '%;background:' + col + '"></div></div>' + (w.reasoning ? '<div class="hr-reason">' + htmlEscape(w.reasoning) + '</div>' : '') + '</div>';
|
|
2932
|
+
}).join('');
|
|
1865
2933
|
}
|
|
1866
2934
|
|
|
1867
2935
|
// ──── Tokens ───────────────────────────────────────────────────────────
|
|
1868
2936
|
async function refreshTokens() {
|
|
1869
2937
|
const res = await api('/api/v6/tokens-report');
|
|
1870
2938
|
if (!res) return;
|
|
2939
|
+
const runtime = res.runtime || {};
|
|
2940
|
+
document.getElementById('tok-agent').textContent = runtime.currentAgentDisplay || 'Unknown';
|
|
2941
|
+
document.getElementById('tok-runtime').textContent = (runtime.provider || 'provider') + (runtime.model ? ' / ' + runtime.model : '');
|
|
2942
|
+
document.getElementById('tok-runtime-note').innerHTML = \`<strong>Accuracy</strong><span>\${htmlEscape(runtime.note || 'Token records are historical. Current agent and model spend are shown separately.')}</span>\`;
|
|
1871
2943
|
document.getElementById('tok-total').textContent = '$' + (res.totalSpendUsd || 0).toFixed(4);
|
|
2944
|
+
document.getElementById('tok-recent').textContent = '$' + (res.recentSpendUsd || 0).toFixed(4);
|
|
1872
2945
|
document.getElementById('tok-proj').textContent = '$' + (res.monthlyProjectionUsd || 0).toFixed(2);
|
|
1873
2946
|
document.getElementById('tok-save').textContent = '$' + (res.savingsOpportunitiesUsd || 0).toFixed(2);
|
|
1874
2947
|
const calls = Object.values(res.byAgent || {}).reduce((a, b) => a + b.calls, 0);
|
|
1875
2948
|
document.getElementById('tok-calls').textContent = calls;
|
|
1876
2949
|
document.getElementById('tok-calls-sub').textContent = \`\${(res.totalInputTokens || 0).toLocaleString()} in · \${(res.totalOutputTokens || 0).toLocaleString()} out\`;
|
|
1877
|
-
|
|
2950
|
+
drawTokensAgentChart(res.recentByAgent || res.byAgent || {});
|
|
2951
|
+
drawTokensModelChart(res.byModel || {});
|
|
2952
|
+
document.getElementById('tok-agent-table').innerHTML = renderSpendRows(res.recentByAgent || res.byAgent || {}, 'No recent agent spend records.');
|
|
2953
|
+
document.getElementById('tok-model-table').innerHTML = renderSpendRows(res.byModel || {}, 'No model spend records.');
|
|
1878
2954
|
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
2955
|
}
|
|
1880
2956
|
|
|
@@ -1915,18 +2991,39 @@ async function refreshCalibration() {
|
|
|
1915
2991
|
document.getElementById('calibration-table').innerHTML = scores.length ? scores.map(s =>
|
|
1916
2992
|
\`<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
2993
|
).join('') : '<tr><td colspan="7"><div class="empty">No calibration data yet</div></td></tr>';
|
|
2994
|
+
drawCalibrationChart(scores);
|
|
2995
|
+
}
|
|
2996
|
+
function drawCalibrationChart(scores) {
|
|
2997
|
+
const cv = document.getElementById('chart-calibration'); if (!cv || !window.Chart) return;
|
|
2998
|
+
const t = chartTheme();
|
|
2999
|
+
if (charts.calibration) charts.calibration.destroy();
|
|
3000
|
+
const rows = (scores || []).slice(0, 8);
|
|
3001
|
+
const labels = rows.map(function (s) { return s.agentTool + '/' + String(s.category).slice(0, 10); });
|
|
3002
|
+
charts.calibration = new Chart(cv, {
|
|
3003
|
+
type: 'bar',
|
|
3004
|
+
data: { labels: labels, datasets: [
|
|
3005
|
+
{ label: 'Brier (lower better)', data: rows.map(function (s) { return s.brierScore; }), backgroundColor: '#ff5864', borderRadius: 4 },
|
|
3006
|
+
{ label: 'Trust weight', data: rows.map(function (s) { return s.trustWeight; }), backgroundColor: '#10e68c', borderRadius: 4 },
|
|
3007
|
+
] },
|
|
3008
|
+
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: t.text } } }, scales: { x: { grid: { color: t.grid }, ticks: { color: t.text } }, y: { beginAtZero: true, max: 1, grid: { color: t.grid }, ticks: { color: t.text } } } },
|
|
3009
|
+
});
|
|
1918
3010
|
}
|
|
1919
3011
|
|
|
1920
3012
|
// ──── Branch ───────────────────────────────────────────────────────────
|
|
1921
3013
|
async function refreshBranch() {
|
|
1922
3014
|
const r = await api('/api/v6/branch-state');
|
|
1923
|
-
|
|
1924
|
-
document.getElementById('branch-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
<div class="
|
|
1928
|
-
<div class="
|
|
1929
|
-
<div class="
|
|
3015
|
+
const cards = document.getElementById('branch-cards');
|
|
3016
|
+
const cats = document.getElementById('branch-cats');
|
|
3017
|
+
if (!r) { if (cards) cards.innerHTML = emptyState('git-branch', 'No branch data', 'Branch-aware memory activates inside a git repository. Open a project with branches to see scoped memory here.', null); return; }
|
|
3018
|
+
if (cards) cards.innerHTML =
|
|
3019
|
+
'<div class="card stat-card accent"><div class="label">Current Branch</div><div class="value" style="font-size:20px" id="br-cur">' + htmlEscape(r.currentBranch || '—') + '</div><div class="trend">active context</div></div>' +
|
|
3020
|
+
'<div class="card stat-card accent"><div class="label">Branch Memories</div><div class="value">' + (r.branchMemoryCount ?? 0) + '</div><div class="trend">scoped to branch</div></div>' +
|
|
3021
|
+
'<div class="card stat-card accent"><div class="label">Global Memories</div><div class="value">' + (r.globalMemoryCount ?? 0) + '</div><div class="trend">all branches</div></div>' +
|
|
3022
|
+
'<div class="card stat-card accent"><div class="label">Active IDs</div><div class="value">' + ((r.activeMemoryIds || []).length) + '</div><div class="trend">in working set</div></div>';
|
|
3023
|
+
if (cats) {
|
|
3024
|
+
const list = r.branchSpecificCategories || [];
|
|
3025
|
+
cats.innerHTML = list.length ? list.map(function (c) { return '<span class="tc">' + htmlEscape(String(c)) + '</span>'; }).join('') : '<div class="empty-tight">No branch-specific categories.</div>';
|
|
3026
|
+
}
|
|
1930
3027
|
}
|
|
1931
3028
|
|
|
1932
3029
|
// ──── Privacy ──────────────────────────────────────────────────────────
|
|
@@ -2246,6 +3343,25 @@ async function attachAll() { const r = await api('/api/v6/agents-attach-all', {}
|
|
|
2246
3343
|
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
3344
|
|
|
2248
3345
|
// ──── Memory browser ───────────────────────────────────────────────────
|
|
3346
|
+
function renderCountRows(rows, emptyMessage) {
|
|
3347
|
+
if (!rows || !rows.length) return '<div class="empty-tight">' + htmlEscape(emptyMessage) + '</div>';
|
|
3348
|
+
return rows.slice(0, 8).map(row =>
|
|
3349
|
+
'<div class="row"><span class="k">' + htmlEscape(row.name) + '</span><span class="v">' + htmlEscape(row.count) + '</span></div>'
|
|
3350
|
+
).join('');
|
|
3351
|
+
}
|
|
3352
|
+
async function refreshMemoryIntelligence() {
|
|
3353
|
+
const data = await api('/api/v6/memory-intelligence?limit=500');
|
|
3354
|
+
if (!data) return;
|
|
3355
|
+
document.getElementById('mem-total').textContent = data.totalEntries ?? 0;
|
|
3356
|
+
document.getElementById('mem-sampled').textContent = (data.sampledEntries ?? 0) + ' shown in intelligence sample';
|
|
3357
|
+
document.getElementById('mem-source').textContent = Math.round((data.sourceCoverage || 0) * 100) + '%';
|
|
3358
|
+
document.getElementById('mem-fresh').textContent = data.recent7d ?? 0;
|
|
3359
|
+
document.getElementById('mem-seed-open').textContent = data.openSeedDrafts ?? 0;
|
|
3360
|
+
document.getElementById('mem-seed-sub').textContent = (data.pendingApprovals ?? 0) + ' need approval';
|
|
3361
|
+
document.getElementById('mem-categories').innerHTML = renderCountRows(data.byCategory, 'No categories yet.');
|
|
3362
|
+
document.getElementById('mem-agents').innerHTML = renderCountRows(data.byAgent, 'No agent memory writes yet.');
|
|
3363
|
+
document.getElementById('mem-tags').innerHTML = renderCountRows(data.topTags, 'No tags yet.');
|
|
3364
|
+
}
|
|
2249
3365
|
async function searchMemory() {
|
|
2250
3366
|
const r = await api('/api/v6/memory-browser', {
|
|
2251
3367
|
query: document.getElementById('mem-query').value,
|
|
@@ -2260,13 +3376,352 @@ async function searchMemory() {
|
|
|
2260
3376
|
}
|
|
2261
3377
|
|
|
2262
3378
|
// ──── Activity ─────────────────────────────────────────────────────────
|
|
3379
|
+
// Live Brain
|
|
3380
|
+
// Memory Seeder
|
|
3381
|
+
function seedProjectPath() {
|
|
3382
|
+
return document.getElementById('seed-project-path')?.value?.trim()
|
|
3383
|
+
|| document.getElementById('live-project-path')?.value?.trim()
|
|
3384
|
+
|| document.getElementById('ww2-project-path')?.value?.trim()
|
|
3385
|
+
|| '';
|
|
3386
|
+
}
|
|
3387
|
+
function copyLivePathToSeeder() {
|
|
3388
|
+
const value = document.getElementById('live-project-path')?.value?.trim()
|
|
3389
|
+
|| document.getElementById('ww2-project-path')?.value?.trim()
|
|
3390
|
+
|| '';
|
|
3391
|
+
if (value) document.getElementById('seed-project-path').value = value;
|
|
3392
|
+
}
|
|
3393
|
+
function openSeederForLiveBrain() {
|
|
3394
|
+
const projectDir = liveProjectPath() || document.getElementById('ww2-project-path')?.value?.trim() || '';
|
|
3395
|
+
switchTab('seeder', 'Memory Seeder');
|
|
3396
|
+
if (projectDir) document.getElementById('seed-project-path').value = projectDir;
|
|
3397
|
+
const topic = document.getElementById('seed-topic');
|
|
3398
|
+
const objective = document.getElementById('seed-objective');
|
|
3399
|
+
if (topic && !topic.value) topic.value = 'Source-backed live brain improvement';
|
|
3400
|
+
if (objective && !objective.value) objective.value = 'Capture verified project knowledge that future agents should recall before complex work.';
|
|
3401
|
+
refreshSeedDrafts();
|
|
3402
|
+
}
|
|
3403
|
+
function seedDraftStatusClass(status) {
|
|
3404
|
+
if (status === 'seeded') return 'green';
|
|
3405
|
+
if (status === 'approved') return 'cyan';
|
|
3406
|
+
if (status === 'rejected') return 'red';
|
|
3407
|
+
return 'yellow';
|
|
3408
|
+
}
|
|
3409
|
+
function renderSeedStatus(drafts) {
|
|
3410
|
+
const counts = { draft: 0, approved: 0, seeded: 0, rejected: 0 };
|
|
3411
|
+
for (const d of drafts || []) counts[d.status] = (counts[d.status] || 0) + 1;
|
|
3412
|
+
return [
|
|
3413
|
+
'<div class="row"><span class="k">Open drafts</span><span class="v warn">' + counts.draft + '</span></div>',
|
|
3414
|
+
'<div class="row"><span class="k">Approved</span><span class="v good">' + counts.approved + '</span></div>',
|
|
3415
|
+
'<div class="row"><span class="k">Seeded</span><span class="v good">' + counts.seeded + '</span></div>',
|
|
3416
|
+
'<div class="row"><span class="k">Rejected</span><span class="v bad">' + counts.rejected + '</span></div>',
|
|
3417
|
+
'<div class="row"><span class="k">Current project</span><span class="v mono">' + htmlEscape(seedProjectPath() || 'dashboard default') + '</span></div>',
|
|
3418
|
+
].join('');
|
|
3419
|
+
}
|
|
3420
|
+
function seedJsArg(value) {
|
|
3421
|
+
return JSON.stringify(String(value || ''));
|
|
3422
|
+
}
|
|
3423
|
+
function renderSeedDrafts(drafts) {
|
|
3424
|
+
if (!drafts || !drafts.length) return '<div class="empty-tight">No seed drafts yet.</div>';
|
|
3425
|
+
return drafts.map(d => {
|
|
3426
|
+
const approvedBy = new Set((d.approvals || []).map(a => a.by));
|
|
3427
|
+
const required = d.requiredApprovals || [];
|
|
3428
|
+
const pending = required.filter(a => !approvedBy.has(a));
|
|
3429
|
+
const agentApprover = d.approverAgent && d.approverAgent !== 'user' ? d.approverAgent : d.researchAgent;
|
|
3430
|
+
const warnings = (d.warnings || []).length
|
|
3431
|
+
? '<div class="warning-stack" style="margin-top:8px">' + d.warnings.map(w => '<div class="warning-banner"><strong>Review</strong><span>' + htmlEscape(w) + '</span></div>').join('') + '</div>'
|
|
3432
|
+
: '';
|
|
3433
|
+
const sources = (d.sources || []).slice(0, 5).map(s => '<code>' + htmlEscape(s) + '</code>').join(' ');
|
|
3434
|
+
let actions = '';
|
|
3435
|
+
if (d.status === 'draft') {
|
|
3436
|
+
actions += '<button class="btn sm success" onclick="approveSeedDraft(' + seedJsArg(d.id) + ',' + seedJsArg('user') + ')">User Approve</button> ';
|
|
3437
|
+
actions += '<button class="btn sm" onclick="approveSeedDraft(' + seedJsArg(d.id) + ',' + seedJsArg(agentApprover) + ')">Agent Approve</button> ';
|
|
3438
|
+
actions += '<button class="btn sm danger" onclick="rejectSeedDraft(' + seedJsArg(d.id) + ')">Reject</button>';
|
|
3439
|
+
}
|
|
3440
|
+
if (d.status === 'approved') {
|
|
3441
|
+
actions += '<button class="btn sm primary" onclick="commitSeedDraft(' + seedJsArg(d.id) + ')">Seed to Brain</button> ';
|
|
3442
|
+
actions += '<button class="btn sm danger" onclick="rejectSeedDraft(' + seedJsArg(d.id) + ')">Reject</button>';
|
|
3443
|
+
}
|
|
3444
|
+
if (d.status === 'seeded') {
|
|
3445
|
+
actions += '<span class="pill green">memory ' + htmlEscape((d.seededMemoryId || '').slice(0, 10)) + '</span> ';
|
|
3446
|
+
actions += '<button class="btn sm" onclick="handoffToAgent(' + seedJsArg(d.researchAgent) + ')" title="Generate a briefing to hand this knowledge back to an agent">↩ Hand to Agent</button>';
|
|
3447
|
+
}
|
|
3448
|
+
return '<div class="memory-item">' +
|
|
3449
|
+
'<div class="meta">' +
|
|
3450
|
+
'<span class="pill ' + seedDraftStatusClass(d.status) + '">' + htmlEscape(d.status) + '</span>' +
|
|
3451
|
+
'<span class="pill purple">' + htmlEscape(d.researchAgent) + '</span>' +
|
|
3452
|
+
'<span class="pill cyan">' + htmlEscape(d.approvalMode) + '</span>' +
|
|
3453
|
+
'<span>' + htmlEscape(new Date(d.updatedAt || d.createdAt).toLocaleString()) + '</span>' +
|
|
3454
|
+
'</div>' +
|
|
3455
|
+
'<div style="display:flex;gap:12px;align-items:flex-start;justify-content:space-between;flex-wrap:wrap">' +
|
|
3456
|
+
'<div style="min-width:260px;flex:1">' +
|
|
3457
|
+
'<div style="font-weight:700;margin-bottom:4px">' + htmlEscape(d.topic) + '</div>' +
|
|
3458
|
+
'<div class="content">' + htmlEscape(d.objective || '').slice(0, 260) + '</div>' +
|
|
3459
|
+
'<div class="mono" style="font-size:11px;color:var(--text-3);margin-top:6px">pending: ' + htmlEscape(pending.join(', ') || 'none') + ' | category: ' + htmlEscape(d.category) + '</div>' +
|
|
3460
|
+
'<div style="font-size:11px;color:var(--text-3);margin-top:6px">' + (sources || 'No sources') + '</div>' +
|
|
3461
|
+
'</div>' +
|
|
3462
|
+
'<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end">' + actions + '</div>' +
|
|
3463
|
+
'</div>' +
|
|
3464
|
+
warnings +
|
|
3465
|
+
'<pre class="mono" style="margin-top:10px;max-height:180px;overflow:auto;background:var(--bg-elev-1);padding:10px;border-radius:6px;white-space:pre-wrap">' + htmlEscape(d.content || '') + '</pre>' +
|
|
3466
|
+
'</div>';
|
|
3467
|
+
}).join('');
|
|
3468
|
+
}
|
|
3469
|
+
async function refreshSeedDrafts() {
|
|
3470
|
+
const input = document.getElementById('seed-project-path');
|
|
3471
|
+
if (input && !input.value) input.value = seedProjectPath();
|
|
3472
|
+
const drafts = await api('/api/v6/seed-drafts?limit=50') ?? [];
|
|
3473
|
+
const open = drafts.filter(d => d.status === 'draft' || d.status === 'approved').length;
|
|
3474
|
+
const badge = document.getElementById('badge-seeder');
|
|
3475
|
+
if (badge) badge.textContent = open;
|
|
3476
|
+
const status = document.getElementById('seed-status');
|
|
3477
|
+
if (status) status.innerHTML = renderSeedStatus(drafts);
|
|
3478
|
+
const list = document.getElementById('seed-drafts');
|
|
3479
|
+
if (list) list.innerHTML = renderSeedDrafts(drafts);
|
|
3480
|
+
}
|
|
3481
|
+
async function createSeedDraft() {
|
|
3482
|
+
const topic = document.getElementById('seed-topic').value.trim();
|
|
3483
|
+
if (!topic) { toast('Topic required', 'Add a seed topic before creating a draft.', 'warn'); return; }
|
|
3484
|
+
const draft = await api('/api/v6/seed-draft', {
|
|
3485
|
+
projectDir: seedProjectPath(),
|
|
3486
|
+
topic,
|
|
3487
|
+
objective: document.getElementById('seed-objective').value,
|
|
3488
|
+
researchAgent: document.getElementById('seed-research-agent').value,
|
|
3489
|
+
approverAgent: document.getElementById('seed-approver-agent').value,
|
|
3490
|
+
approvalMode: document.getElementById('seed-approval-mode').value,
|
|
3491
|
+
category: document.getElementById('seed-category').value,
|
|
3492
|
+
tags: document.getElementById('seed-tags').value,
|
|
3493
|
+
importance: parseFloat(document.getElementById('seed-importance').value || '0.78'),
|
|
3494
|
+
sources: document.getElementById('seed-sources').value,
|
|
3495
|
+
content: document.getElementById('seed-content').value,
|
|
3496
|
+
});
|
|
3497
|
+
if (draft) {
|
|
3498
|
+
toast('Seed draft created', draft.status);
|
|
3499
|
+
refreshSeedDrafts();
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
// One-click path: create → approve → save to brain. The "single seed".
|
|
3503
|
+
async function quickSeed() {
|
|
3504
|
+
const topic = document.getElementById('seed-topic').value.trim();
|
|
3505
|
+
if (!topic) { toast('Topic required', 'Add a topic before quick-saving.', 'warn'); return; }
|
|
3506
|
+
const draft = await api('/api/v6/seed-draft', {
|
|
3507
|
+
projectDir: seedProjectPath(),
|
|
3508
|
+
topic,
|
|
3509
|
+
objective: document.getElementById('seed-objective').value,
|
|
3510
|
+
researchAgent: document.getElementById('seed-research-agent').value,
|
|
3511
|
+
approverAgent: 'user',
|
|
3512
|
+
approvalMode: 'user',
|
|
3513
|
+
category: document.getElementById('seed-category').value,
|
|
3514
|
+
tags: document.getElementById('seed-tags').value,
|
|
3515
|
+
importance: parseFloat(document.getElementById('seed-importance').value || '0.78'),
|
|
3516
|
+
sources: document.getElementById('seed-sources').value,
|
|
3517
|
+
content: document.getElementById('seed-content').value,
|
|
3518
|
+
});
|
|
3519
|
+
if (!draft || !draft.id) return;
|
|
3520
|
+
const approved = await api('/api/v6/seed-approve', { id: draft.id, approvedBy: 'user' });
|
|
3521
|
+
if (!approved) { toast('Saved as draft', 'Approve it in the queue to finish.', 'warn'); refreshSeedDrafts(); return; }
|
|
3522
|
+
const committed = await api('/api/v6/seed-commit', { id: draft.id, seededBy: 'user' });
|
|
3523
|
+
if (committed) {
|
|
3524
|
+
toast('Saved to brain ⚡', 'Knowledge is now in memory.', 'success');
|
|
3525
|
+
refreshSeedDrafts();
|
|
3526
|
+
try { refreshLiveBrain(); } catch (e) {}
|
|
3527
|
+
} else {
|
|
3528
|
+
toast('Approved', 'Couldn\\'t finalize — finish from the queue.', 'warn');
|
|
3529
|
+
refreshSeedDrafts();
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
async function approveSeedDraft(id, approvedBy) {
|
|
3533
|
+
const draft = await api('/api/v6/seed-approve', { id, approvedBy });
|
|
3534
|
+
if (draft) {
|
|
3535
|
+
toast('Approval recorded', draft.status);
|
|
3536
|
+
refreshSeedDrafts();
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
async function rejectSeedDraft(id) {
|
|
3540
|
+
const reason = prompt('Reject reason', 'Not verified enough');
|
|
3541
|
+
if (reason === null) return;
|
|
3542
|
+
const draft = await api('/api/v6/seed-reject', { id, reason, rejectedBy: 'user' });
|
|
3543
|
+
if (draft) {
|
|
3544
|
+
toast('Draft rejected', draft.topic, 'warn');
|
|
3545
|
+
refreshSeedDrafts();
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
async function commitSeedDraft(id) {
|
|
3549
|
+
const draft = await api('/api/v6/seed-commit', { id, seededBy: 'user' });
|
|
3550
|
+
if (draft) {
|
|
3551
|
+
toast('Seeded to brain', (draft.seededMemoryId || '').slice(0, 10));
|
|
3552
|
+
refreshSeedDrafts();
|
|
3553
|
+
refreshLiveBrain();
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
// Two-way: hand the brain's knowledge back to an agent as a ready briefing.
|
|
3557
|
+
async function handoffToAgent(toAgent) {
|
|
3558
|
+
const md = await api('/api/v6/agent-handoff', { projectDir: seedProjectPath(), fromAgent: 'user', toAgent: toAgent || 'claude-code', format: 'markdown' });
|
|
3559
|
+
if (!md) return;
|
|
3560
|
+
try {
|
|
3561
|
+
await navigator.clipboard.writeText(typeof md === 'string' ? md : JSON.stringify(md, null, 2));
|
|
3562
|
+
toast('Handoff copied', 'Briefing for ' + (toAgent || 'agent') + ' is on your clipboard — paste it into the agent.', 'success');
|
|
3563
|
+
} catch (e) {
|
|
3564
|
+
toast('Handoff ready', 'Copy it from the Activity log if clipboard is blocked.', 'warn');
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
function liveProjectPath() {
|
|
3569
|
+
return document.getElementById('live-project-path')?.value?.trim() || '';
|
|
3570
|
+
}
|
|
3571
|
+
function liveActionPill(priority) {
|
|
3572
|
+
if (priority === 'critical' || priority === 'high') return 'red';
|
|
3573
|
+
if (priority === 'medium') return 'yellow';
|
|
3574
|
+
return 'cyan';
|
|
3575
|
+
}
|
|
3576
|
+
async function refreshLiveBrain() {
|
|
3577
|
+
const projectDir = liveProjectPath();
|
|
3578
|
+
const queries = document.getElementById('live-queries')?.value?.trim() || '';
|
|
3579
|
+
const url = '/api/v6/live-brain?projectDir=' + encodeURIComponent(projectDir) + (queries ? '&queries=' + encodeURIComponent(queries) : '');
|
|
3580
|
+
const data = await api(url);
|
|
3581
|
+
if (!data) return;
|
|
3582
|
+
|
|
3583
|
+
const quality = data.memoryQuality || {};
|
|
3584
|
+
document.getElementById('live-memories').textContent = quality.totalEntries ?? 0;
|
|
3585
|
+
document.getElementById('live-quality').textContent = (quality.avgScore ?? 0) + '/100';
|
|
3586
|
+
document.getElementById('live-sources').textContent = Math.round((quality.sourceCoverage ?? 0) * 100) + '%';
|
|
3587
|
+
document.getElementById('live-recall').textContent = data.recallEval?.avgHitCount ?? 0;
|
|
3588
|
+
document.getElementById('live-scope').textContent = data.projectScoped ? 'project scoped' : 'global fallback';
|
|
3589
|
+
|
|
3590
|
+
document.getElementById('live-actions').innerHTML = (data.learningActions || []).length
|
|
3591
|
+
? data.learningActions.map(a => \`<div class="memory-item"><div class="meta"><span class="pill \${liveActionPill(a.priority)}">\${a.priority}</span><span>\${a.title}</span></div><div class="content">\${a.reason}</div>\${a.command ? '<code>' + a.command + '</code>' : ''}</div>\`).join('')
|
|
3592
|
+
: '<div class="empty">No immediate hygiene actions.</div>';
|
|
3593
|
+
|
|
3594
|
+
document.getElementById('live-eval').innerHTML = (data.recallEval?.cases || []).map(c =>
|
|
3595
|
+
\`<div class="row"><span class="k">\${c.query}</span><span class="v \${c.hitCount ? 'good' : 'bad'}">\${c.hitCount} hits - \${c.sourceBackedHits} sourced</span></div>\`
|
|
3596
|
+
).join('') || '<div class="empty">No eval cases.</div>';
|
|
3597
|
+
|
|
3598
|
+
document.getElementById('live-top').innerHTML = (data.topMemories || []).slice(0, 8).map(m =>
|
|
3599
|
+
\`<div class="memory-item"><div class="meta"><span class="pill purple">\${m.agentTool}</span><span>\${m.category}</span><span>score \${m.qualityScore}/100</span></div><div class="content">\${(m.content || '').slice(0, 260)}</div></div>\`
|
|
3600
|
+
).join('') || '<div class="empty">No memories.</div>';
|
|
3601
|
+
|
|
3602
|
+
document.getElementById('live-brief').textContent = data.agentBriefing || '';
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
// ──── Realtime Activity Stream (v6.1) ──────────────────────────────────
|
|
3606
|
+
let activityBuffer = [];
|
|
3607
|
+
const activitySeen = new Set();
|
|
3608
|
+
const activitySeenOrder = [];
|
|
3609
|
+
let activityFilter = '';
|
|
3610
|
+
let activityPaused = false;
|
|
3611
|
+
let activityRate = [];
|
|
3612
|
+
let lastIngestFresh = false;
|
|
3613
|
+
|
|
3614
|
+
function activityKey(e) { return (e.type || '') + '|' + (e.source || '') + '|' + String(e.detail || '').slice(0, 60); }
|
|
3615
|
+
function actEpoch(e) {
|
|
3616
|
+
if (typeof e.ts === 'number') return e.ts;
|
|
3617
|
+
if (e.timestamp) { const t = Date.parse(e.timestamp); if (!isNaN(t)) return t; }
|
|
3618
|
+
return Date.now();
|
|
3619
|
+
}
|
|
3620
|
+
function ingestActivity(raw, fresh) {
|
|
3621
|
+
const e = { ts: actEpoch(raw), type: raw.type || 'event', source: raw.source || 'brain', detail: String(raw.detail || '') };
|
|
3622
|
+
const key = activityKey(e);
|
|
3623
|
+
if (activitySeen.has(key)) return false;
|
|
3624
|
+
activitySeen.add(key); activitySeenOrder.push(key);
|
|
3625
|
+
if (activitySeenOrder.length > 1200) { const drop = activitySeenOrder.splice(0, activitySeenOrder.length - 1200); for (const k of drop) activitySeen.delete(k); }
|
|
3626
|
+
activityBuffer.unshift(e);
|
|
3627
|
+
if (activityBuffer.length > 500) activityBuffer.length = 500;
|
|
3628
|
+
activityRate.push(e.ts);
|
|
3629
|
+
const cutoff = Date.now() - 30 * 60 * 1000;
|
|
3630
|
+
while (activityRate.length && activityRate[0] < cutoff) activityRate.shift();
|
|
3631
|
+
if (!activityPaused) {
|
|
3632
|
+
appendLog(e.source, e.type, e.detail);
|
|
3633
|
+
if (fresh) {
|
|
3634
|
+
try {
|
|
3635
|
+
if (typeof graphTimer !== 'undefined' && graphTimer) {
|
|
3636
|
+
const col = signalColors[e.type] || '#18ffff';
|
|
3637
|
+
const srcExists = graphNodes.find(n => n.id === e.source);
|
|
3638
|
+
graphSignals.push({ fromId: srcExists ? e.source : 'brain', toId: 'brain', type: e.type, color: col, progress: 0, spawnedAt: Date.now() });
|
|
3639
|
+
if (graphSignals.length > 100) graphSignals = graphSignals.slice(-100);
|
|
3640
|
+
}
|
|
3641
|
+
} catch (err) {}
|
|
3642
|
+
lastIngestFresh = true;
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
return true;
|
|
3646
|
+
}
|
|
3647
|
+
function typeMatchesFilter(type, filter) {
|
|
3648
|
+
if (!filter) return true;
|
|
3649
|
+
if (filter === 'seed-draft') return type === 'seed-draft' || type === 'seed-commit';
|
|
3650
|
+
return type === filter;
|
|
3651
|
+
}
|
|
3652
|
+
function renderActivityTable() {
|
|
3653
|
+
const tbody = document.getElementById('activity-table');
|
|
3654
|
+
if (!tbody) return;
|
|
3655
|
+
const searchEl = document.getElementById('act-search');
|
|
3656
|
+
const q = (searchEl ? searchEl.value : '').toLowerCase().trim();
|
|
3657
|
+
let rows = activityBuffer.filter(e => typeMatchesFilter(e.type, activityFilter));
|
|
3658
|
+
if (q) rows = rows.filter(e => (e.source + ' ' + e.type + ' ' + e.detail).toLowerCase().includes(q));
|
|
3659
|
+
const fresh = lastIngestFresh; lastIngestFresh = false;
|
|
3660
|
+
rows = rows.slice(0, 200);
|
|
3661
|
+
tbody.innerHTML = rows.length
|
|
3662
|
+
? rows.map((e, i) => '<tr' + (i === 0 && fresh ? ' class="act-fresh"' : '') + '><td class="mono">' + new Date(e.ts).toLocaleTimeString() + '</td><td><span class="pill purple">' + htmlEscape(e.source) + '</span></td><td><span class="pill ' + typeToPill(e.type) + '">' + htmlEscape(e.type) + '</span></td><td class="mono">' + htmlEscape(e.detail.slice(0, 200)) + '</td></tr>').join('')
|
|
3663
|
+
: '<tr><td colspan="4"><div class="empty">No activity</div></td></tr>';
|
|
3664
|
+
const total = document.getElementById('act-stat-total'); if (total) total.textContent = activityBuffer.length;
|
|
3665
|
+
const last = document.getElementById('act-stat-last'); if (last) last.textContent = activityBuffer[0] ? new Date(activityBuffer[0].ts).toLocaleTimeString() : '—';
|
|
3666
|
+
}
|
|
3667
|
+
function eventsPerMin() { const c = Date.now() - 60000; return activityRate.filter(t => t >= c).length; }
|
|
3668
|
+
function renderSparkline() {
|
|
3669
|
+
const rate = eventsPerMin();
|
|
3670
|
+
const ratePill = document.getElementById('act-rate-pill'); if (ratePill) ratePill.textContent = rate + ' / min';
|
|
3671
|
+
const ovRate = document.getElementById('ov-pulse-rate'); if (ovRate) ovRate.textContent = rate + ' / min';
|
|
3672
|
+
const cv = document.getElementById('act-spark');
|
|
3673
|
+
if (!cv || !cv.getContext) return;
|
|
3674
|
+
const now = Date.now();
|
|
3675
|
+
const buckets = new Array(10).fill(0);
|
|
3676
|
+
for (const t of activityRate) { const age = now - t; if (age < 0 || age > 10 * 60000) continue; const idx = 9 - Math.floor(age / 60000); if (idx >= 0 && idx < 10) buckets[idx]++; }
|
|
3677
|
+
const pw = (cv.parentElement ? cv.parentElement.clientWidth : 0) || 600;
|
|
3678
|
+
cv.width = pw; cv.height = 120;
|
|
3679
|
+
const ctx = cv.getContext('2d'); const W = cv.width, H = cv.height; ctx.clearRect(0, 0, W, H);
|
|
3680
|
+
let max = 1; for (const b of buckets) if (b > max) max = b;
|
|
3681
|
+
const bw = W / 10;
|
|
3682
|
+
for (let i = 0; i < 10; i++) {
|
|
3683
|
+
const h = (buckets[i] / max) * (H - 12);
|
|
3684
|
+
const x = i * bw + 3, y = H - h;
|
|
3685
|
+
ctx.fillStyle = i === 9 ? '#18ffff' : 'rgba(168,85,247,0.55)';
|
|
3686
|
+
ctx.fillRect(x, y, bw - 6, Math.max(0, h));
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
function renderLivePulse() {
|
|
3690
|
+
const el = document.getElementById('ov-pulse');
|
|
3691
|
+
if (!el) return;
|
|
3692
|
+
const items = activityBuffer.slice(0, 8);
|
|
3693
|
+
el.innerHTML = items.length
|
|
3694
|
+
? items.map(e => '<div class="memory-item"><div class="meta"><span class="pill ' + typeToPill(e.type) + '">' + htmlEscape(e.type) + '</span><span class="pill purple">' + htmlEscape(e.source) + '</span><span>' + new Date(e.ts).toLocaleTimeString() + '</span></div><div class="content">' + htmlEscape(e.detail.slice(0, 200)) + '</div></div>').join('')
|
|
3695
|
+
: '<div class="empty-tight">Awaiting live activity…</div>';
|
|
3696
|
+
}
|
|
3697
|
+
function setActivityFilter(btn, type) {
|
|
3698
|
+
activityFilter = type;
|
|
3699
|
+
const chips = document.querySelectorAll('#act-chips .chip');
|
|
3700
|
+
chips.forEach(c => c.classList.toggle('active', c === btn));
|
|
3701
|
+
renderActivityTable();
|
|
3702
|
+
}
|
|
3703
|
+
function toggleActivityPause() {
|
|
3704
|
+
activityPaused = !activityPaused;
|
|
3705
|
+
const btn = document.getElementById('act-pause-btn'); if (btn) btn.textContent = activityPaused ? 'Resume' : 'Pause';
|
|
3706
|
+
const badge = document.getElementById('act-live-badge'); if (badge) { badge.classList.toggle('paused', activityPaused); badge.textContent = activityPaused ? 'PAUSED' : 'LIVE'; }
|
|
3707
|
+
const live = document.getElementById('act-stat-live'); if (live) { live.textContent = activityPaused ? 'paused' : 'streaming'; live.className = 'v ' + (activityPaused ? 'warn' : 'good'); }
|
|
3708
|
+
if (!activityPaused) renderActivityTable();
|
|
3709
|
+
}
|
|
3710
|
+
function clearActivity() {
|
|
3711
|
+
activityBuffer = []; activitySeen.clear(); activitySeenOrder.length = 0; activityRate = [];
|
|
3712
|
+
renderActivityTable(); renderSparkline(); renderLivePulse();
|
|
3713
|
+
}
|
|
3714
|
+
function setConn(online) {
|
|
3715
|
+
const d = document.getElementById('conn-dot'); if (d) { d.classList.toggle('offline', !online); d.title = 'Realtime connection: ' + (online ? 'live' : 'reconnecting'); }
|
|
3716
|
+
if (activityPaused) return;
|
|
3717
|
+
const badge = document.getElementById('act-live-badge'); if (badge) { badge.classList.toggle('paused', !online); badge.textContent = online ? 'LIVE' : 'OFFLINE'; }
|
|
3718
|
+
const live = document.getElementById('act-stat-live'); if (live) { live.textContent = online ? 'streaming' : 'reconnecting'; live.className = 'v ' + (online ? 'good' : 'bad'); }
|
|
3719
|
+
}
|
|
2263
3720
|
async function refreshActivity() {
|
|
2264
|
-
const
|
|
2265
|
-
|
|
2266
|
-
|
|
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>';
|
|
3721
|
+
const log = await api('/api/v6/activity-log?limit=200');
|
|
3722
|
+
if (Array.isArray(log)) for (let i = log.length - 1; i >= 0; i--) ingestActivity(log[i], false);
|
|
3723
|
+
renderActivityTable(); renderSparkline();
|
|
2268
3724
|
}
|
|
2269
|
-
document.getElementById('act-filter').addEventListener('change', refreshActivity);
|
|
2270
3725
|
|
|
2271
3726
|
// ──── Live Graph ───────────────────────────────────────────────────────
|
|
2272
3727
|
let graphFrame = 0, graphNodes = [], graphEdges = [], graphSignals = [], graphTimer = null, graphPullTimer = null;
|
|
@@ -2310,64 +3765,449 @@ function graphLoop() {
|
|
|
2310
3765
|
const c = document.getElementById('graph-canvas');
|
|
2311
3766
|
if (!c) return;
|
|
2312
3767
|
const ctx = c.getContext('2d'), W = c.width, H = c.height;
|
|
3768
|
+
const now = Date.now();
|
|
2313
3769
|
ctx.clearRect(0, 0, W, H);
|
|
2314
|
-
|
|
2315
|
-
grd.
|
|
3770
|
+
// Ambient backdrop with a soft vignette.
|
|
3771
|
+
const grd = ctx.createRadialGradient(W/2, H/2, 40, W/2, H/2, Math.max(W,H)/1.4);
|
|
3772
|
+
grd.addColorStop(0, 'rgba(168,85,247,0.10)'); grd.addColorStop(0.5, 'rgba(24,255,255,0.03)'); grd.addColorStop(1, 'rgba(0,0,0,0)');
|
|
2316
3773
|
ctx.fillStyle = grd; ctx.fillRect(0, 0, W, H);
|
|
3774
|
+
|
|
2317
3775
|
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
|
-
|
|
3776
|
+
|
|
3777
|
+
// ── Edges: curved, with a gentle animated dash flow on active links ──────
|
|
2319
3778
|
for (const e of graphEdges) {
|
|
2320
3779
|
const a = graphNodes.find(n => n.id === e.from), b = graphNodes.find(n => n.id === e.to);
|
|
2321
3780
|
if (!a || !b) continue;
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
3781
|
+
const mx = (a.x + b.x) / 2, my = (a.y + b.y) / 2;
|
|
3782
|
+
// Curve control point nudged perpendicular for an organic arc.
|
|
3783
|
+
const dx = b.x - a.x, dy = b.y - a.y, len = Math.sqrt(dx*dx + dy*dy) || 1;
|
|
3784
|
+
const cxp = mx - (dy / len) * 26, cyp = my + (dx / len) * 26;
|
|
3785
|
+
if (e.active) {
|
|
3786
|
+
const g = ctx.createLinearGradient(a.x, a.y, b.x, b.y);
|
|
3787
|
+
g.addColorStop(0, 'rgba(24,255,255,0.05)'); g.addColorStop(0.5, 'rgba(24,255,255,0.34)'); g.addColorStop(1, 'rgba(168,85,247,0.18)');
|
|
3788
|
+
ctx.strokeStyle = g; ctx.lineWidth = 1.6;
|
|
3789
|
+
ctx.setLineDash([5, 7]); ctx.lineDashOffset = -(now / 45) % 12;
|
|
3790
|
+
} else {
|
|
3791
|
+
ctx.strokeStyle = 'rgba(96,108,140,0.14)'; ctx.lineWidth = 1; ctx.setLineDash([3, 5]); ctx.lineDashOffset = 0;
|
|
3792
|
+
}
|
|
3793
|
+
ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.quadraticCurveTo(cxp, cyp, b.x, b.y); ctx.stroke();
|
|
2325
3794
|
}
|
|
2326
|
-
ctx.setLineDash([]);
|
|
2327
|
-
|
|
3795
|
+
ctx.setLineDash([]); ctx.lineDashOffset = 0;
|
|
3796
|
+
|
|
3797
|
+
// ── Signals: glowing head + fading motion trail ──────────────────────────
|
|
2328
3798
|
graphSignals = graphSignals.filter(s => now - s.spawnedAt < 3500);
|
|
2329
3799
|
for (const s of graphSignals) {
|
|
2330
3800
|
const from = graphNodes.find(n => n.id === s.fromId), to = graphNodes.find(n => n.id === s.toId);
|
|
2331
3801
|
if (!from || !to) continue;
|
|
2332
3802
|
s.progress = Math.min(1, (now - s.spawnedAt) / 2400);
|
|
2333
|
-
const t = s.progress
|
|
2334
|
-
const
|
|
3803
|
+
const t = s.progress;
|
|
3804
|
+
const ease = t < 0.5 ? 2*t*t : 1 - Math.pow(-2*t+2, 2)/2;
|
|
3805
|
+
// Trail: several fading dots behind the head.
|
|
3806
|
+
for (let k = 0; k < 5; k++) {
|
|
3807
|
+
const tt = Math.max(0, ease - k * 0.045);
|
|
3808
|
+
const x = from.x + (to.x - from.x) * tt, y = from.y + (to.y - from.y) * tt;
|
|
3809
|
+
const alpha = (1 - k / 5) * (1 - Math.abs(t - 0.5)) * 0.9;
|
|
3810
|
+
ctx.globalAlpha = Math.max(0, alpha);
|
|
3811
|
+
ctx.fillStyle = s.color;
|
|
3812
|
+
ctx.beginPath(); ctx.arc(x, y, 3.2 - k * 0.4, 0, Math.PI * 2); ctx.fill();
|
|
3813
|
+
}
|
|
3814
|
+
ctx.globalAlpha = 1;
|
|
3815
|
+
const hx = from.x + (to.x - from.x) * ease, hy = from.y + (to.y - from.y) * ease;
|
|
3816
|
+
const tail = ctx.createRadialGradient(hx, hy, 0, hx, hy, 20);
|
|
2335
3817
|
tail.addColorStop(0, s.color); tail.addColorStop(1, 'rgba(0,0,0,0)');
|
|
2336
|
-
ctx.fillStyle = tail; ctx.beginPath(); ctx.arc(
|
|
2337
|
-
ctx.fillStyle =
|
|
2338
|
-
ctx.beginPath(); ctx.arc(x, y, 3.5, 0, Math.PI * 2); ctx.fill();
|
|
3818
|
+
ctx.fillStyle = tail; ctx.beginPath(); ctx.arc(hx, hy, 18, 0, Math.PI * 2); ctx.fill();
|
|
3819
|
+
ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(hx, hy, 2.2, 0, Math.PI * 2); ctx.fill();
|
|
2339
3820
|
}
|
|
3821
|
+
|
|
3822
|
+
// ── Nodes: layered glow, ring, core, label chip ──────────────────────────
|
|
2340
3823
|
for (const n of graphNodes) {
|
|
2341
|
-
const
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
ctx.
|
|
2347
|
-
|
|
3824
|
+
const isBrain = n.type === 'brain';
|
|
3825
|
+
const pulse = isBrain ? Math.sin(graphFrame / 15) * 4 : Math.sin((graphFrame + (n.x|0)) / 22) * 1.5;
|
|
3826
|
+
const r = n.r + pulse;
|
|
3827
|
+
const halo = ctx.createRadialGradient(n.x, n.y, r * 0.4, n.x, n.y, r * 2.8);
|
|
3828
|
+
halo.addColorStop(0, n.color + '77'); halo.addColorStop(0.6, n.color + '1a'); halo.addColorStop(1, 'rgba(0,0,0,0)');
|
|
3829
|
+
ctx.fillStyle = halo; ctx.beginPath(); ctx.arc(n.x, n.y, r * 2.8, 0, Math.PI * 2); ctx.fill();
|
|
3830
|
+
// Rotating accent ring on the brain hub.
|
|
3831
|
+
if (isBrain) {
|
|
3832
|
+
ctx.save(); ctx.translate(n.x, n.y); ctx.rotate((now / 1600) % (Math.PI * 2));
|
|
3833
|
+
ctx.strokeStyle = n.color + '88'; ctx.lineWidth = 1.5; ctx.setLineDash([3, 9]);
|
|
3834
|
+
ctx.beginPath(); ctx.arc(0, 0, r + 10, 0, Math.PI * 2); ctx.stroke();
|
|
3835
|
+
ctx.restore(); ctx.setLineDash([]);
|
|
3836
|
+
}
|
|
3837
|
+
const fill = ctx.createRadialGradient(n.x - r*0.3, n.y - r*0.3, r*0.2, n.x, n.y, r);
|
|
3838
|
+
fill.addColorStop(0, isBrain ? '#1b2142' : '#121a30'); fill.addColorStop(1, isBrain ? '#0a0e1f' : '#080c16');
|
|
3839
|
+
ctx.fillStyle = fill;
|
|
3840
|
+
ctx.beginPath(); ctx.arc(n.x, n.y, r, 0, Math.PI * 2); ctx.fill();
|
|
3841
|
+
ctx.strokeStyle = n.color; ctx.lineWidth = isBrain ? 2.5 : 1.8; ctx.stroke();
|
|
3842
|
+
ctx.fillStyle = n.color; ctx.beginPath(); ctx.arc(n.x, n.y, Math.max(2, r * 0.34), 0, Math.PI * 2); ctx.fill();
|
|
3843
|
+
// Label
|
|
2348
3844
|
ctx.fillStyle = n.color;
|
|
2349
|
-
ctx.font =
|
|
3845
|
+
ctx.font = isBrain ? '800 13px Inter, sans-serif' : '600 11px Inter, sans-serif';
|
|
2350
3846
|
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
|
2351
|
-
ctx.fillText(n.label, n.x, n.y
|
|
3847
|
+
ctx.fillText(n.label, n.x, isBrain ? n.y : n.y + r + 13);
|
|
2352
3848
|
}
|
|
2353
3849
|
graphFrame++;
|
|
2354
3850
|
graphTimer = requestAnimationFrame(graphLoop);
|
|
2355
3851
|
}
|
|
2356
3852
|
|
|
3853
|
+
// ──── Memory Constellation (Intelligence hub) ──────────────────────────
|
|
3854
|
+
let constNodes = [], constEdges = [], constStats = null, constIdMap = new Map();
|
|
3855
|
+
let constView = { zoom: 1, panX: 0, panY: 0 };
|
|
3856
|
+
let constSim = { running: false, alpha: 1, raf: null };
|
|
3857
|
+
let constFilter = '', constFocus = null, constHover = null;
|
|
3858
|
+
let constDragNode = null, constPanning = false, constLoaded = false, constMoved = false;
|
|
3859
|
+
let constLast = { x: 0, y: 0 };
|
|
3860
|
+
let constLiveTimer = null;
|
|
3861
|
+
const NODE_COLORS = { memory: '#18ffff', project: '#10e68c', agent: '#ffb53f', tag: '#a855f7' };
|
|
3862
|
+
|
|
3863
|
+
async function refreshIntelligence() {
|
|
3864
|
+
bindConstellation();
|
|
3865
|
+
refreshIntelAnalytics();
|
|
3866
|
+
if (!constLoaded) { await loadConstellation(); }
|
|
3867
|
+
else { sizeConstCanvas(); startConstSim(); }
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
async function loadConstellation() {
|
|
3871
|
+
const snap = await api('/api/v6/memory-graph?limit=400&maxNodes=180');
|
|
3872
|
+
applyConstellation(snap);
|
|
3873
|
+
constLoaded = true;
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
function applyConstellation(snap) {
|
|
3877
|
+
const cv = document.getElementById('const-canvas');
|
|
3878
|
+
const empty = document.getElementById('const-empty');
|
|
3879
|
+
constNodes = []; constEdges = []; constIdMap = new Map();
|
|
3880
|
+
const nodes = (snap && snap.nodes) || [];
|
|
3881
|
+
constStats = (snap && snap.stats) || null;
|
|
3882
|
+
if (empty) empty.classList.toggle('show', nodes.length === 0);
|
|
3883
|
+
const badge = document.getElementById('badge-intelligence');
|
|
3884
|
+
if (badge) badge.textContent = constStats ? constStats.memories : 0;
|
|
3885
|
+
if (!nodes.length) { drawConstellation(); return; }
|
|
3886
|
+
sizeConstCanvas();
|
|
3887
|
+
const W = cv ? cv.width : 800, H = cv ? cv.height : 600, cx = W / 2, cy = H / 2;
|
|
3888
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
3889
|
+
const n = nodes[i];
|
|
3890
|
+
const ang = (i / nodes.length) * Math.PI * 2;
|
|
3891
|
+
const rad = 60 + Math.random() * Math.min(W, H) * 0.32;
|
|
3892
|
+
constNodes.push({
|
|
3893
|
+
id: n.id, label: n.label || '', type: n.type, score: n.score || 0,
|
|
3894
|
+
size: n.size || 9, degree: n.degree || 0, meta: n.meta || {},
|
|
3895
|
+
x: cx + Math.cos(ang) * rad, y: cy + Math.sin(ang) * rad, vx: 0, vy: 0,
|
|
3896
|
+
});
|
|
3897
|
+
}
|
|
3898
|
+
for (const node of constNodes) constIdMap.set(node.id, node);
|
|
3899
|
+
for (const e of (snap.edges || [])) {
|
|
3900
|
+
const sn = constIdMap.get(e.source), tn = constIdMap.get(e.target);
|
|
3901
|
+
if (sn && tn) constEdges.push({ sn: sn, tn: tn, w: e.weight || 1, type: e.type });
|
|
3902
|
+
}
|
|
3903
|
+
const meta = document.getElementById('const-meta');
|
|
3904
|
+
if (meta && constStats) meta.textContent = constStats.memories + ' mem · ' + constStats.projects + ' proj · ' + constStats.agents + ' agent · ' + constStats.tags + ' tag · ' + constStats.edges + ' links' + (constStats.droppedNodes ? ' (+' + constStats.droppedNodes + ' hidden)' : '');
|
|
3905
|
+
updateConstHero();
|
|
3906
|
+
constView = { zoom: 1, panX: 0, panY: 0 };
|
|
3907
|
+
constSim.alpha = 1;
|
|
3908
|
+
startConstSim();
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3911
|
+
function sizeConstCanvas() {
|
|
3912
|
+
const cv = document.getElementById('const-canvas'), wrap = document.getElementById('const-wrap');
|
|
3913
|
+
if (!cv || !wrap) return;
|
|
3914
|
+
cv.width = wrap.clientWidth || 800;
|
|
3915
|
+
cv.height = wrap.clientHeight || 600;
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
function startConstSim() {
|
|
3919
|
+
if (constSim.running || !constNodes.length) return;
|
|
3920
|
+
constSim.running = true;
|
|
3921
|
+
constSim.alpha = Math.max(constSim.alpha, 0.5);
|
|
3922
|
+
constLoop();
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
function constLoop() {
|
|
3926
|
+
const sec = document.getElementById('tab-intelligence');
|
|
3927
|
+
if (!sec || !sec.classList.contains('active')) { constSim.running = false; constSim.raf = null; return; }
|
|
3928
|
+
stepConstPhysics();
|
|
3929
|
+
drawConstellation();
|
|
3930
|
+
constSim.raf = requestAnimationFrame(constLoop);
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
function stepConstPhysics() {
|
|
3934
|
+
const cv = document.getElementById('const-canvas'); if (!cv) return;
|
|
3935
|
+
const W = cv.width, H = cv.height, cx = W / 2, cy = H / 2;
|
|
3936
|
+
const a = constSim.alpha;
|
|
3937
|
+
const N = constNodes.length;
|
|
3938
|
+
if (a > 0.01) {
|
|
3939
|
+
for (let i = 0; i < N; i++) {
|
|
3940
|
+
const ni = constNodes[i];
|
|
3941
|
+
for (let j = i + 1; j < N; j++) {
|
|
3942
|
+
const nj = constNodes[j];
|
|
3943
|
+
let dx = ni.x - nj.x, dy = ni.y - nj.y;
|
|
3944
|
+
let d2 = dx * dx + dy * dy; if (d2 < 1) d2 = 1;
|
|
3945
|
+
const d = Math.sqrt(d2);
|
|
3946
|
+
let rep = (3600 * a) / d2;
|
|
3947
|
+
// Hard separation: keep node circles from visually overlapping.
|
|
3948
|
+
const minD = ni.size + nj.size + 12;
|
|
3949
|
+
if (d < minD) rep += (minD - d) * 0.28 * a;
|
|
3950
|
+
const ux = dx / d, uy = dy / d;
|
|
3951
|
+
ni.vx += ux * rep; ni.vy += uy * rep;
|
|
3952
|
+
nj.vx -= ux * rep; nj.vy -= uy * rep;
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
for (const e of constEdges) {
|
|
3956
|
+
let dx = e.tn.x - e.sn.x, dy = e.tn.y - e.sn.y;
|
|
3957
|
+
let d = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
3958
|
+
const ideal = 94;
|
|
3959
|
+
const f = ((d - ideal) * 0.015 * a) * Math.min(2, e.w);
|
|
3960
|
+
const ux = dx / d, uy = dy / d;
|
|
3961
|
+
e.sn.vx += ux * f; e.sn.vy += uy * f;
|
|
3962
|
+
e.tn.vx -= ux * f; e.tn.vy -= uy * f;
|
|
3963
|
+
}
|
|
3964
|
+
for (const n of constNodes) {
|
|
3965
|
+
if (n === constDragNode) continue;
|
|
3966
|
+
n.vx += (cx - n.x) * 0.010 * a;
|
|
3967
|
+
n.vy += (cy - n.y) * 0.010 * a;
|
|
3968
|
+
n.vx *= 0.86; n.vy *= 0.86;
|
|
3969
|
+
const sp = Math.sqrt(n.vx * n.vx + n.vy * n.vy);
|
|
3970
|
+
if (sp > 20) { n.vx = n.vx / sp * 20; n.vy = n.vy / sp * 20; }
|
|
3971
|
+
n.x += n.vx; n.y += n.vy;
|
|
3972
|
+
}
|
|
3973
|
+
constSim.alpha *= 0.99;
|
|
3974
|
+
if (constSim.alpha < 0.01) constSim.alpha = 0.01;
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
|
|
3978
|
+
function constNodeOpacity(n) {
|
|
3979
|
+
if (constFilter && n.type !== constFilter) return 0.12;
|
|
3980
|
+
if (constFocus) {
|
|
3981
|
+
if (n.id === constFocus) return 1;
|
|
3982
|
+
const f = constIdMap.get(constFocus);
|
|
3983
|
+
if (f && f._neighbors && f._neighbors.has(n.id)) return 0.95;
|
|
3984
|
+
return 0.14;
|
|
3985
|
+
}
|
|
3986
|
+
return 1;
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3989
|
+
function drawConstellation() {
|
|
3990
|
+
const cv = document.getElementById('const-canvas'); if (!cv || !cv.getContext) return;
|
|
3991
|
+
const ctx = cv.getContext('2d'), W = cv.width, H = cv.height;
|
|
3992
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
3993
|
+
ctx.clearRect(0, 0, W, H);
|
|
3994
|
+
ctx.translate(constView.panX, constView.panY);
|
|
3995
|
+
ctx.scale(constView.zoom, constView.zoom);
|
|
3996
|
+
|
|
3997
|
+
for (const e of constEdges) {
|
|
3998
|
+
const oa = Math.min(constNodeOpacity(e.sn), constNodeOpacity(e.tn));
|
|
3999
|
+
const lit = constFocus && (e.sn.id === constFocus || e.tn.id === constFocus);
|
|
4000
|
+
ctx.strokeStyle = lit ? 'rgba(24,255,255,' + (0.55 * oa) + ')' : 'rgba(120,140,180,' + (0.16 * oa) + ')';
|
|
4001
|
+
ctx.lineWidth = lit ? 1.6 : 0.7;
|
|
4002
|
+
ctx.beginPath(); ctx.moveTo(e.sn.x, e.sn.y); ctx.lineTo(e.tn.x, e.tn.y); ctx.stroke();
|
|
4003
|
+
}
|
|
4004
|
+
for (const n of constNodes) {
|
|
4005
|
+
const op = constNodeOpacity(n);
|
|
4006
|
+
const col = NODE_COLORS[n.type] || '#9aa7c0';
|
|
4007
|
+
const r = n.size;
|
|
4008
|
+
const halo = ctx.createRadialGradient(n.x, n.y, r * 0.4, n.x, n.y, r * 2.4);
|
|
4009
|
+
halo.addColorStop(0, col + Math.round(op * 90).toString(16).padStart(2, '0'));
|
|
4010
|
+
halo.addColorStop(1, 'rgba(0,0,0,0)');
|
|
4011
|
+
ctx.fillStyle = halo; ctx.beginPath(); ctx.arc(n.x, n.y, r * 2.4, 0, Math.PI * 2); ctx.fill();
|
|
4012
|
+
ctx.globalAlpha = op;
|
|
4013
|
+
ctx.fillStyle = '#0b1020'; ctx.beginPath(); ctx.arc(n.x, n.y, r, 0, Math.PI * 2); ctx.fill();
|
|
4014
|
+
ctx.strokeStyle = col; ctx.lineWidth = (n.id === constFocus || n.id === constHover) ? 3 : 1.6;
|
|
4015
|
+
ctx.stroke();
|
|
4016
|
+
ctx.fillStyle = col; ctx.beginPath(); ctx.arc(n.x, n.y, Math.max(2, r * 0.42), 0, Math.PI * 2); ctx.fill();
|
|
4017
|
+
const showLabel = r > 15 || n.id === constHover || n.id === constFocus || n.type !== 'memory';
|
|
4018
|
+
if (showLabel && op > 0.4) {
|
|
4019
|
+
ctx.globalAlpha = op;
|
|
4020
|
+
ctx.fillStyle = document.documentElement.dataset.theme === 'light' ? '#1a2030' : '#e8ecf4';
|
|
4021
|
+
ctx.font = (n.id === constFocus ? 'bold ' : '') + Math.max(10, Math.min(14, 9 + r * 0.18)) + 'px Inter, sans-serif';
|
|
4022
|
+
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
|
4023
|
+
const lbl = n.label.length > 26 ? n.label.slice(0, 25) + '…' : n.label;
|
|
4024
|
+
ctx.fillText(lbl, n.x, n.y + r + 9);
|
|
4025
|
+
}
|
|
4026
|
+
ctx.globalAlpha = 1;
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
|
|
4030
|
+
function constMouseWorld(e) {
|
|
4031
|
+
const cv = document.getElementById('const-canvas'); const rect = cv.getBoundingClientRect();
|
|
4032
|
+
const sx = (e.clientX - rect.left) * (cv.width / rect.width);
|
|
4033
|
+
const sy = (e.clientY - rect.top) * (cv.height / rect.height);
|
|
4034
|
+
return { sx: sx, sy: sy, wx: (sx - constView.panX) / constView.zoom, wy: (sy - constView.panY) / constView.zoom };
|
|
4035
|
+
}
|
|
4036
|
+
function constHitTest(wx, wy) {
|
|
4037
|
+
for (let i = constNodes.length - 1; i >= 0; i--) {
|
|
4038
|
+
const n = constNodes[i];
|
|
4039
|
+
const dx = wx - n.x, dy = wy - n.y;
|
|
4040
|
+
if (dx * dx + dy * dy <= (n.size + 4) * (n.size + 4)) return n;
|
|
4041
|
+
}
|
|
4042
|
+
return null;
|
|
4043
|
+
}
|
|
4044
|
+
function computeNeighbors(id) {
|
|
4045
|
+
const n = constIdMap.get(id); if (!n) return;
|
|
4046
|
+
if (n._neighbors) return;
|
|
4047
|
+
const set = new Set();
|
|
4048
|
+
for (const e of constEdges) { if (e.sn.id === id) set.add(e.tn.id); if (e.tn.id === id) set.add(e.sn.id); }
|
|
4049
|
+
n._neighbors = set;
|
|
4050
|
+
}
|
|
4051
|
+
function openConstDrawer(n) {
|
|
4052
|
+
const drawer = document.getElementById('const-drawer'), body = document.getElementById('const-drawer-body');
|
|
4053
|
+
if (!drawer || !body) return;
|
|
4054
|
+
const m = n.meta || {};
|
|
4055
|
+
let h = '<div class="cd-type" style="color:' + (NODE_COLORS[n.type] || '#9aa7c0') + '">' + n.type + '</div>';
|
|
4056
|
+
h += '<div class="cd-title">' + htmlEscape(n.label) + '</div>';
|
|
4057
|
+
h += '<div class="row"><span class="k">Importance</span><span class="v">' + (n.score * 100).toFixed(0) + '/100</span></div>';
|
|
4058
|
+
h += '<div class="row"><span class="k">Connections</span><span class="v">' + n.degree + '</span></div>';
|
|
4059
|
+
if (n.type === 'memory') {
|
|
4060
|
+
if (m.category) h += '<div class="row"><span class="k">Category</span><span class="v">' + htmlEscape(String(m.category)) + '</span></div>';
|
|
4061
|
+
if (m.agentTool) h += '<div class="row"><span class="k">Agent</span><span class="v">' + htmlEscape(String(m.agentTool)) + '</span></div>';
|
|
4062
|
+
if (m.projectName) h += '<div class="row"><span class="k">Project</span><span class="v">' + htmlEscape(String(m.projectName)) + '</span></div>';
|
|
4063
|
+
if (m.content) h += '<div class="cd-section"><div class="cd-label">Content</div><div class="cd-content">' + htmlEscape(String(m.content)) + '</div></div>';
|
|
4064
|
+
const tags = Array.isArray(m.tags) ? m.tags : [];
|
|
4065
|
+
if (tags.length) h += '<div class="cd-section"><div class="cd-label">Tags</div><div class="tag-cloud">' + tags.map(function (t) { return '<span class="tc">#' + htmlEscape(String(t)) + '</span>'; }).join('') + '</div></div>';
|
|
4066
|
+
const src = Array.isArray(m.sources) ? m.sources : [];
|
|
4067
|
+
if (src.length) h += '<div class="cd-section"><div class="cd-label">Sources</div><div class="cd-content mono">' + src.map(function (s) { return htmlEscape(String(s)); }).join('<br>') + '</div></div>';
|
|
4068
|
+
} else {
|
|
4069
|
+
h += '<div class="cd-section"><div class="cd-content">This ' + n.type + ' node links ' + n.degree + ' memories in the constellation. Click connected nodes to explore.</div></div>';
|
|
4070
|
+
}
|
|
4071
|
+
body.innerHTML = h;
|
|
4072
|
+
drawer.classList.add('open');
|
|
4073
|
+
}
|
|
4074
|
+
function closeConstDrawer() {
|
|
4075
|
+
const drawer = document.getElementById('const-drawer'); if (drawer) drawer.classList.remove('open');
|
|
4076
|
+
constFocus = null;
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4079
|
+
function bindConstellation() {
|
|
4080
|
+
const wrap = document.getElementById('const-wrap'); if (!wrap || wrap._bound) return;
|
|
4081
|
+
wrap._bound = true;
|
|
4082
|
+
const cv = document.getElementById('const-canvas');
|
|
4083
|
+
wrap.addEventListener('wheel', function (e) {
|
|
4084
|
+
e.preventDefault();
|
|
4085
|
+
const p = constMouseWorld(e);
|
|
4086
|
+
const factor = e.deltaY < 0 ? 1.12 : 0.89;
|
|
4087
|
+
const nz = Math.max(0.25, Math.min(4, constView.zoom * factor));
|
|
4088
|
+
constView.panX = p.sx - p.wx * nz;
|
|
4089
|
+
constView.panY = p.sy - p.wy * nz;
|
|
4090
|
+
constView.zoom = nz;
|
|
4091
|
+
if (!constSim.running) drawConstellation();
|
|
4092
|
+
}, { passive: false });
|
|
4093
|
+
wrap.addEventListener('mousedown', function (e) {
|
|
4094
|
+
const p = constMouseWorld(e); const hit = constHitTest(p.wx, p.wy);
|
|
4095
|
+
constMoved = false; constLast = { x: e.clientX, y: e.clientY };
|
|
4096
|
+
if (hit) { constDragNode = hit; constSim.alpha = Math.max(constSim.alpha, 0.4); startConstSim(); }
|
|
4097
|
+
else { constPanning = true; wrap.classList.add('dragging'); }
|
|
4098
|
+
});
|
|
4099
|
+
window.addEventListener('mousemove', function (e) {
|
|
4100
|
+
if (constDragNode) {
|
|
4101
|
+
const p = constMouseWorld(e); constDragNode.x = p.wx; constDragNode.y = p.wy; constDragNode.vx = 0; constDragNode.vy = 0; constMoved = true;
|
|
4102
|
+
return;
|
|
4103
|
+
}
|
|
4104
|
+
if (constPanning) {
|
|
4105
|
+
constView.panX += e.clientX - constLast.x; constView.panY += e.clientY - constLast.y;
|
|
4106
|
+
constLast = { x: e.clientX, y: e.clientY }; constMoved = true;
|
|
4107
|
+
if (!constSim.running) drawConstellation();
|
|
4108
|
+
return;
|
|
4109
|
+
}
|
|
4110
|
+
const sec = document.getElementById('tab-intelligence');
|
|
4111
|
+
if (!sec || !sec.classList.contains('active')) return;
|
|
4112
|
+
const p = constMouseWorld(e); const hit = constHitTest(p.wx, p.wy);
|
|
4113
|
+
const tip = document.getElementById('const-tip');
|
|
4114
|
+
constHover = hit ? hit.id : null;
|
|
4115
|
+
if (hit && tip) {
|
|
4116
|
+
tip.innerHTML = '<b style="color:' + (NODE_COLORS[hit.type] || '#fff') + '">' + htmlEscape(hit.label) + '</b><br>' + hit.type + ' · ' + (hit.score * 100).toFixed(0) + '/100 · ' + hit.degree + ' links';
|
|
4117
|
+
const rect = cv.getBoundingClientRect();
|
|
4118
|
+
tip.style.left = (e.clientX - rect.left) + 'px'; tip.style.top = (e.clientY - rect.top) + 'px';
|
|
4119
|
+
tip.classList.add('show');
|
|
4120
|
+
} else if (tip) tip.classList.remove('show');
|
|
4121
|
+
});
|
|
4122
|
+
window.addEventListener('mouseup', function (e) {
|
|
4123
|
+
if (constDragNode && !constMoved) { constFocus = constDragNode.id; computeNeighbors(constFocus); openConstDrawer(constDragNode); }
|
|
4124
|
+
else if (constPanning && !constMoved) {
|
|
4125
|
+
const p = constMouseWorld(e); const hit = constHitTest(p.wx, p.wy);
|
|
4126
|
+
if (!hit) { constFocus = null; closeConstDrawer(); }
|
|
4127
|
+
}
|
|
4128
|
+
constDragNode = null; constPanning = false; wrap.classList.remove('dragging');
|
|
4129
|
+
});
|
|
4130
|
+
}
|
|
4131
|
+
function setConstFilter(btn, type) {
|
|
4132
|
+
constFilter = type;
|
|
4133
|
+
const chips = document.querySelectorAll('#const-chips .chip');
|
|
4134
|
+
chips.forEach(function (c) { c.classList.toggle('active', c === btn); });
|
|
4135
|
+
if (!constSim.running) drawConstellation();
|
|
4136
|
+
}
|
|
4137
|
+
function highlightConstSearch() {
|
|
4138
|
+
const q = (document.getElementById('const-search').value || '').toLowerCase().trim();
|
|
4139
|
+
if (!q) { constFocus = null; if (!constSim.running) drawConstellation(); return; }
|
|
4140
|
+
const hit = constNodes.find(function (n) { return n.label.toLowerCase().includes(q); });
|
|
4141
|
+
if (hit) { constFocus = hit.id; computeNeighbors(hit.id); openConstDrawer(hit); }
|
|
4142
|
+
if (!constSim.running) drawConstellation();
|
|
4143
|
+
}
|
|
4144
|
+
function rebuildConstellation() { constLoaded = false; toast('Rebuilding', 'Recomputing the memory constellation…', 'info'); loadConstellation(); }
|
|
4145
|
+
function resetConstellationView() { constView = { zoom: 1, panX: 0, panY: 0 }; constFocus = null; closeConstDrawer(); constSim.alpha = 0.6; startConstSim(); }
|
|
4146
|
+
function constLiveBump() {
|
|
4147
|
+
if (!constLoaded) return;
|
|
4148
|
+
constSim.alpha = Math.max(constSim.alpha, 0.5); startConstSim();
|
|
4149
|
+
if (constLiveTimer) clearTimeout(constLiveTimer);
|
|
4150
|
+
constLiveTimer = setTimeout(function () { constLoaded = false; loadConstellation(); }, 4000);
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4153
|
+
// ──── Intelligence analytics strip ─────────────────────────────────────
|
|
4154
|
+
async function refreshIntelAnalytics() {
|
|
4155
|
+
const d = await api('/api/v6/memory-intelligence');
|
|
4156
|
+
if (!d) return;
|
|
4157
|
+
animateNum('intel-total', d.totalEntries || 0);
|
|
4158
|
+
const cov = Math.round((d.sourceCoverage || 0) * 100);
|
|
4159
|
+
const covEl = document.getElementById('intel-coverage'); if (covEl) covEl.textContent = cov + '%';
|
|
4160
|
+
const covM = document.getElementById('intel-coverage-meter'); if (covM) covM.style.width = cov + '%';
|
|
4161
|
+
animateNum('intel-recent', d.recent24h || 0);
|
|
4162
|
+
animateNum('intel-highimp', d.highImportance || 0);
|
|
4163
|
+
drawIntelAgents(d.byAgent || []);
|
|
4164
|
+
drawIntelCats(d.byCategory || []);
|
|
4165
|
+
renderTagCloud(d.topTags || []);
|
|
4166
|
+
renderIntelRecent(d.recent || []);
|
|
4167
|
+
}
|
|
4168
|
+
function drawIntelAgents(rows) {
|
|
4169
|
+
const cv = document.getElementById('chart-intel-agents'); if (!cv || !window.Chart) return;
|
|
4170
|
+
const t = chartTheme(); const top = rows.slice(0, 6);
|
|
4171
|
+
if (charts.intelAgents) charts.intelAgents.destroy();
|
|
4172
|
+
charts.intelAgents = new Chart(cv, { type: 'bar', data: { labels: top.map(function (r) { return r.name; }), datasets: [{ data: top.map(function (r) { return r.count; }), backgroundColor: '#18ffff', borderRadius: 5 }] }, options: { indexAxis: 'y', responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { grid: { color: t.grid }, ticks: { color: t.text } }, y: { grid: { display: false }, ticks: { color: t.text } } } } });
|
|
4173
|
+
}
|
|
4174
|
+
function drawIntelCats(rows) {
|
|
4175
|
+
const cv = document.getElementById('chart-intel-cats'); if (!cv || !window.Chart) return;
|
|
4176
|
+
const t = chartTheme(); const top = rows.slice(0, 6);
|
|
4177
|
+
if (charts.intelCats) charts.intelCats.destroy();
|
|
4178
|
+
charts.intelCats = new Chart(cv, { type: 'doughnut', data: { labels: top.map(function (r) { return r.name; }), datasets: [{ data: top.map(function (r) { return r.count; }), backgroundColor: ['#18ffff', '#a855f7', '#ec4899', '#10e68c', '#ffb53f', '#4fb6ff'], borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right', labels: { color: t.text, boxWidth: 12 } } } } });
|
|
4179
|
+
}
|
|
4180
|
+
function renderTagCloud(rows) {
|
|
4181
|
+
const el = document.getElementById('intel-tags'); if (!el) return;
|
|
4182
|
+
el.innerHTML = rows.length ? rows.slice(0, 18).map(function (r) { return '<span class="tc">#' + htmlEscape(r.name) + ' <b>' + r.count + '</b></span>'; }).join('') : '<div class="empty-tight">No tags yet.</div>';
|
|
4183
|
+
}
|
|
4184
|
+
function renderIntelRecent(rows) {
|
|
4185
|
+
const el = document.getElementById('intel-recent-list'); if (!el) return;
|
|
4186
|
+
el.innerHTML = rows.length ? rows.slice(0, 10).map(function (e) { return '<div class="memory-item"><div class="meta"><span class="pill purple">' + htmlEscape(e.agentTool || 'agent') + '</span><span>' + htmlEscape(e.category || '') + '</span></div><div class="content">' + htmlEscape(String(e.content || '').slice(0, 160)) + '</div></div>'; }).join('') : '<div class="empty-tight">No memories yet.</div>';
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
// ──── Overview constellation hero ──────────────────────────────────────
|
|
4190
|
+
function updateConstHero() {
|
|
4191
|
+
if (!constStats) return;
|
|
4192
|
+
const nodes = constStats.memories + constStats.projects + constStats.agents + constStats.tags;
|
|
4193
|
+
const n = document.getElementById('hero-const-nodes'); if (n) n.textContent = nodes;
|
|
4194
|
+
const e = document.getElementById('hero-const-edges'); if (e) e.textContent = constStats.edges;
|
|
4195
|
+
const m = document.getElementById('hero-const-mem'); if (m) m.textContent = constStats.memories;
|
|
4196
|
+
}
|
|
4197
|
+
async function loadConstHeroStats() {
|
|
4198
|
+
if (constStats) { updateConstHero(); return; }
|
|
4199
|
+
const snap = await api('/api/v6/memory-graph?limit=400&maxNodes=180');
|
|
4200
|
+
if (snap && snap.stats) { constStats = snap.stats; updateConstHero(); }
|
|
4201
|
+
}
|
|
4202
|
+
|
|
2357
4203
|
// ──── Command Palette ──────────────────────────────────────────────────
|
|
2358
4204
|
const commands = [
|
|
2359
|
-
...FEATURES.map(([id, name]) => ({ type: 'nav', label: name, target: id, hint: 'Go to' })),
|
|
2360
|
-
{ type: '
|
|
2361
|
-
{ type: '
|
|
2362
|
-
{ type: '
|
|
2363
|
-
{ type: '
|
|
2364
|
-
{ type: '
|
|
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' },
|
|
4205
|
+
...FEATURES.map(([id, name, desc]) => ({ type: 'nav', label: name, target: id, hint: desc || 'Go to' })),
|
|
4206
|
+
{ type: 'action', label: 'Run Reflections now', fn: 'runDream' },
|
|
4207
|
+
{ type: 'action', label: 'Clean up memory now', fn: 'runForget' },
|
|
4208
|
+
{ type: 'action', label: 'Toggle dark / light theme', fn: 'toggleTheme' },
|
|
4209
|
+
{ type: 'action', label: 'Connect all tools', fn: 'attachAll' },
|
|
4210
|
+
{ type: 'action', label: 'Refresh everything', fn: 'refresh' },
|
|
2371
4211
|
];
|
|
2372
4212
|
let cmdSelected = 0;
|
|
2373
4213
|
function openCmdPalette() {
|
|
@@ -2433,21 +4273,36 @@ function morphBrainState(type) {
|
|
|
2433
4273
|
stateTimer = setTimeout(() => { el.textContent = 'learning · reflecting · charging'; }, 6000);
|
|
2434
4274
|
}
|
|
2435
4275
|
async function pullRecentActivity() {
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
4276
|
+
// Reconciliation safety net: pick up anything the WebSocket missed
|
|
4277
|
+
// (e.g. a dropped frame). ingestActivity dedups, so live events that
|
|
4278
|
+
// already arrived over the socket are ignored here.
|
|
4279
|
+
const log = await api('/api/v6/activity-log?limit=60');
|
|
4280
|
+
if (!Array.isArray(log)) return;
|
|
4281
|
+
for (let i = log.length - 1; i >= 0; i--) ingestActivity(log[i], false);
|
|
4282
|
+
renderSparkline(); renderLivePulse();
|
|
4283
|
+
if (document.getElementById('tab-activity')?.classList.contains('active')) renderActivityTable();
|
|
2443
4284
|
}
|
|
2444
4285
|
|
|
2445
4286
|
// ──── WebSocket ────────────────────────────────────────────────────────
|
|
2446
4287
|
function connectWS() {
|
|
2447
4288
|
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
2448
4289
|
ws = new WebSocket(\`\${proto}://\${location.host}\`);
|
|
2449
|
-
ws.onopen = () => { appendLog('ws','connected','dashboard WebSocket online'); toast('Connected', 'live updates enabled', 'success'); };
|
|
4290
|
+
ws.onopen = () => { appendLog('ws','connected','dashboard WebSocket online'); setConn(true); toast('Connected', 'live updates enabled', 'success'); };
|
|
2450
4291
|
ws.onmessage = e => { try { const m = JSON.parse(e.data);
|
|
4292
|
+
if (m.type === 'activity' && m.event) {
|
|
4293
|
+
if (ingestActivity(m.event, true)) {
|
|
4294
|
+
renderSparkline(); renderLivePulse();
|
|
4295
|
+
if (document.getElementById('tab-activity')?.classList.contains('active')) renderActivityTable();
|
|
4296
|
+
if (m.event.type === 'memory-write' && document.getElementById('tab-intelligence')?.classList.contains('active')) constLiveBump();
|
|
4297
|
+
}
|
|
4298
|
+
return;
|
|
4299
|
+
}
|
|
4300
|
+
if (m.type === 'activity-backlog' && Array.isArray(m.events)) {
|
|
4301
|
+
for (let i = m.events.length - 1; i >= 0; i--) ingestActivity(m.events[i], false);
|
|
4302
|
+
renderSparkline(); renderLivePulse();
|
|
4303
|
+
if (document.getElementById('tab-activity')?.classList.contains('active')) renderActivityTable();
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
2451
4306
|
if (m.type === 'insights' || m.type === 'modules') refresh();
|
|
2452
4307
|
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
4308
|
if (m.type === 'injection') appendLog(m.adapter || 'agent','injection', m.insightTitle || 'context injected');
|
|
@@ -2455,19 +4310,21 @@ function connectWS() {
|
|
|
2455
4310
|
if (m.type === 'error') { appendLog('brain','error', m.error); toast('Brain error', m.error, 'error'); }
|
|
2456
4311
|
if (m.type === 'agents-detected') appendLog('brain','agents', (m.agents || []).join(', '));
|
|
2457
4312
|
} catch {} };
|
|
2458
|
-
ws.onclose = () => { appendLog('ws','disconnected','reconnecting'); setTimeout(connectWS, 2000); };
|
|
4313
|
+
ws.onclose = () => { appendLog('ws','disconnected','reconnecting'); setConn(false); setTimeout(connectWS, 2000); };
|
|
4314
|
+
ws.onerror = () => { setConn(false); };
|
|
2459
4315
|
}
|
|
2460
4316
|
|
|
2461
4317
|
// ──── Bootstrapping ────────────────────────────────────────────────────
|
|
2462
4318
|
connectWS();
|
|
2463
4319
|
refresh();
|
|
2464
4320
|
pullRecentActivity();
|
|
2465
|
-
setInterval(refresh, 5000);
|
|
2466
|
-
setInterval(pullRecentActivity,
|
|
4321
|
+
setInterval(refresh, 5000); // dashboard cards heartbeat
|
|
4322
|
+
setInterval(pullRecentActivity, 15000); // reconciliation only — live events arrive via WebSocket
|
|
4323
|
+
setInterval(renderSparkline, 10000); // keep the events/min window current
|
|
2467
4324
|
if (window.lucide) try { window.lucide.createIcons(); } catch {}
|
|
2468
4325
|
|
|
2469
4326
|
// Welcome toast
|
|
2470
|
-
setTimeout(() => toast('
|
|
4327
|
+
setTimeout(() => toast('Welcome to Shadow Brain', 'Showing the essentials. Flip to Advanced (top-left) for every feature, or press ⌘K to search.', 'info'), 800);
|
|
2471
4328
|
</script>
|
|
2472
4329
|
</body>
|
|
2473
4330
|
</html>`;
|