@theihtisham/agent-shadow-brain 6.0.3 → 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 +40 -40
- package/README.md +427 -366
- 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/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/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 +26 -0
- package/dist/brain/global-brain.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.map +1 -1
- package/dist/brain/memory-seeder.js +23 -0
- package/dist/brain/memory-seeder.js.map +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/cli.js +1 -1
- package/dist/dashboard/server.d.ts +19 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +169 -5
- 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 +2129 -942
- package/dist/dashboard/v6-dashboard-html.js.map +1 -1
- package/docs/launch/RELEASE_CHECKLIST_v6.0.3.md +102 -102
- package/docs/launch/RELEASE_NOTES_v6.0.3.md +111 -111
- 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 +27 -25
- package/tools/export-resume-pack.mjs +56 -56
- package/tools/record-global-memory.mjs +72 -72
|
@@ -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);
|
|
377
398
|
}
|
|
378
|
-
.card
|
|
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;
|
|
404
|
+
}
|
|
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;
|
|
@@ -397,98 +427,100 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
397
427
|
.stat-card .label { font-size: 11px; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
|
|
398
428
|
.stat-card .value { font-size: 32px; font-weight: 900; letter-spacing: -1px; margin: 6px 0 4px; font-variant-numeric: tabular-nums; }
|
|
399
429
|
.stat-card .value.accent { background: var(--brand-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
|
400
|
-
.stat-card .trend { font-size: 11px; color: var(--text-2); display: flex; align-items: center; gap: 4px; }
|
|
401
|
-
.stat-card .trend.up { color: var(--success); }
|
|
402
|
-
.stat-card .trend.down { color: var(--danger); }
|
|
403
|
-
|
|
404
|
-
.workflow-strip {
|
|
405
|
-
display: grid;
|
|
406
|
-
grid-template-columns: minmax(0, 1fr) auto;
|
|
407
|
-
gap: 12px;
|
|
408
|
-
align-items: end;
|
|
409
|
-
margin-bottom: 16px;
|
|
410
|
-
}
|
|
411
|
-
.workflow-path {
|
|
412
|
-
font-family: var(--font-mono);
|
|
413
|
-
font-size: 12px;
|
|
414
|
-
color: var(--text-2);
|
|
415
|
-
background: var(--bg-elev-1);
|
|
416
|
-
border: 1px solid var(--border);
|
|
417
|
-
border-radius: 8px;
|
|
418
|
-
padding: 10px 12px;
|
|
419
|
-
width: 100%;
|
|
420
|
-
}
|
|
421
|
-
.warning-stack { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
|
|
422
|
-
.warning-banner {
|
|
423
|
-
display: flex;
|
|
424
|
-
gap: 10px;
|
|
425
|
-
align-items: flex-start;
|
|
426
|
-
border: 1px solid rgba(217, 119, 6, 0.38);
|
|
427
|
-
background: rgba(217, 119, 6, 0.10);
|
|
428
|
-
color: var(--warning);
|
|
429
|
-
border-radius: 8px;
|
|
430
|
-
padding: 10px 12px;
|
|
431
|
-
font-size: 12px;
|
|
432
|
-
}
|
|
433
|
-
.brain-feed { display: flex; flex-direction: column; gap: 10px; }
|
|
434
|
-
.stack { display: flex; flex-direction: column; gap: 8px; }
|
|
435
|
-
.memory-item {
|
|
436
|
-
border: 1px solid var(--border);
|
|
437
|
-
background: var(--bg-elev-1);
|
|
438
|
-
border-radius: 8px;
|
|
439
|
-
padding: 10px 12px;
|
|
440
|
-
}
|
|
441
|
-
.memory-item .meta {
|
|
442
|
-
display: flex;
|
|
443
|
-
gap: 8px;
|
|
444
|
-
flex-wrap: wrap;
|
|
445
|
-
align-items: center;
|
|
446
|
-
margin-bottom: 6px;
|
|
447
|
-
font-size: 11px;
|
|
448
|
-
color: var(--text-3);
|
|
449
|
-
}
|
|
450
|
-
.memory-item .content {
|
|
451
|
-
color: var(--text-2);
|
|
452
|
-
font-size: 12px;
|
|
453
|
-
line-height: 1.45;
|
|
454
|
-
word-break: break-word;
|
|
455
|
-
}
|
|
456
|
-
.ops-agent-grid {
|
|
457
|
-
display: grid;
|
|
458
|
-
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
459
|
-
gap: 8px;
|
|
460
|
-
}
|
|
461
|
-
.ops-agent {
|
|
462
|
-
border: 1px solid var(--border);
|
|
463
|
-
background: var(--bg-elev-1);
|
|
464
|
-
border-radius: 8px;
|
|
465
|
-
padding: 9px 10px;
|
|
466
|
-
font-size: 12px;
|
|
467
|
-
}
|
|
468
|
-
.ops-agent .name { font-weight: 700; margin-bottom: 5px; }
|
|
469
|
-
.ops-agent .status { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
470
|
-
.empty-tight { color: var(--text-3); padding: 18px 4px; font-size: 12px; text-align: center; }
|
|
471
|
-
|
|
472
|
-
/* ──────────────────────────────────────────────────────────────────── */
|
|
473
|
-
/* Rows, pills, badges */
|
|
430
|
+
.stat-card .trend { font-size: 11px; color: var(--text-2); display: flex; align-items: center; gap: 4px; }
|
|
431
|
+
.stat-card .trend.up { color: var(--success); }
|
|
432
|
+
.stat-card .trend.down { color: var(--danger); }
|
|
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
|
+
|
|
502
|
+
/* ──────────────────────────────────────────────────────────────────── */
|
|
503
|
+
/* Rows, pills, badges */
|
|
474
504
|
/* ──────────────────────────────────────────────────────────────────── */
|
|
475
505
|
.row {
|
|
476
506
|
display: flex; justify-content: space-between; align-items: center;
|
|
477
|
-
padding:
|
|
478
|
-
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);
|
|
479
510
|
}
|
|
511
|
+
.row:hover { background: var(--bg-elev-2); }
|
|
480
512
|
.row:last-child { border: none; }
|
|
481
513
|
.row .k { font-size: 12px; color: var(--text-2); }
|
|
482
|
-
.row .v { font-size: 12.5px; font-weight:
|
|
514
|
+
.row .v { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
|
|
483
515
|
.row .v.good { color: var(--success); }
|
|
484
516
|
.row .v.warn { color: var(--warning); }
|
|
485
517
|
.row .v.bad { color: var(--danger); }
|
|
486
518
|
|
|
487
519
|
.pill {
|
|
488
520
|
display: inline-flex; align-items: center; gap: 4px;
|
|
489
|
-
padding:
|
|
521
|
+
padding: 3px 9px; border-radius: 999px;
|
|
490
522
|
background: var(--bg-elev-3); border: 1px solid var(--border);
|
|
491
|
-
font-size: 10px; font-weight:
|
|
523
|
+
font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
|
|
492
524
|
color: var(--text-2);
|
|
493
525
|
}
|
|
494
526
|
.pill.green { background: rgba(16,230,140,.12); color: var(--success); border-color: rgba(16,230,140,.3); }
|
|
@@ -506,24 +538,26 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
506
538
|
padding: 8px 14px;
|
|
507
539
|
background: var(--bg-elev-2);
|
|
508
540
|
border: 1px solid var(--border);
|
|
509
|
-
border-radius:
|
|
541
|
+
border-radius: 9px;
|
|
510
542
|
color: var(--text);
|
|
511
543
|
font-size: 12.5px;
|
|
512
544
|
font-weight: 600;
|
|
513
|
-
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);
|
|
514
546
|
white-space: nowrap;
|
|
547
|
+
box-shadow: var(--shadow-sm);
|
|
515
548
|
}
|
|
516
|
-
.btn:hover { background: var(--bg-elev-3); border-color: var(--border-strong); transform: translateY(-1px); }
|
|
517
|
-
.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); }
|
|
518
551
|
.btn svg { width: 13px; height: 13px; }
|
|
519
|
-
.btn.sm { padding: 4px 10px; font-size: 11px; }
|
|
520
|
-
.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; }
|
|
521
554
|
.btn.primary {
|
|
522
555
|
background: var(--brand-gradient);
|
|
523
556
|
color: #fff;
|
|
524
557
|
border-color: transparent;
|
|
558
|
+
box-shadow: 0 4px 14px -4px rgba(168,85,247,0.5);
|
|
525
559
|
}
|
|
526
|
-
.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); }
|
|
527
561
|
.btn.ghost { background: transparent; }
|
|
528
562
|
.btn.ghost:hover { background: var(--bg-elev-2); }
|
|
529
563
|
.btn.danger { color: var(--danger); border-color: rgba(255,88,100,.35); }
|
|
@@ -579,16 +613,18 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
579
613
|
/* Tables */
|
|
580
614
|
/* ──────────────────────────────────────────────────────────────────── */
|
|
581
615
|
.table { width: 100%; border-collapse: collapse; }
|
|
582
|
-
.table th, .table td { padding:
|
|
616
|
+
.table th, .table td { padding: 11px 14px; text-align: left; font-size: 12px; }
|
|
583
617
|
.table th {
|
|
584
618
|
background: var(--bg-elev-2);
|
|
585
619
|
color: var(--text-3);
|
|
586
620
|
font-weight: 700; letter-spacing: 0.06em;
|
|
587
621
|
text-transform: uppercase; font-size: 10px;
|
|
588
|
-
border-bottom: 1px solid var(--border);
|
|
622
|
+
border-bottom: 1px solid var(--border-strong);
|
|
623
|
+
position: sticky; top: 0; z-index: 1;
|
|
589
624
|
}
|
|
590
|
-
.table tr { border-bottom: 1px solid var(--border); transition: background var(--duration-fast); }
|
|
591
|
-
.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); }
|
|
592
628
|
.table code { color: var(--brand-cyan); font-size: 11px; }
|
|
593
629
|
|
|
594
630
|
.empty {
|
|
@@ -825,6 +861,181 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
825
861
|
/* Skeleton loader */
|
|
826
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; }
|
|
827
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; }
|
|
828
1039
|
</style>
|
|
829
1040
|
</head>
|
|
830
1041
|
<body>
|
|
@@ -860,18 +1071,19 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
860
1071
|
</div>
|
|
861
1072
|
|
|
862
1073
|
<div class="brand">
|
|
863
|
-
<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>
|
|
864
1075
|
<div class="state" id="brain-state">learning · reflecting · charging</div>
|
|
865
1076
|
</div>
|
|
866
1077
|
|
|
867
1078
|
<div class="header-stats" id="header-stats">
|
|
868
1079
|
<div class="stat-pill"><span class="n" id="stat-agents">—</span> agents</div>
|
|
869
1080
|
<div class="stat-pill"><span class="n" id="stat-memories">—</span> memories</div>
|
|
870
|
-
<div class="stat-pill"><span class="n" id="stat-dreams">—</span>
|
|
871
|
-
<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>
|
|
872
1083
|
</div>
|
|
873
1084
|
|
|
874
1085
|
<div class="header-right">
|
|
1086
|
+
<span class="conn-dot" id="conn-dot" title="Realtime connection: connecting"></span>
|
|
875
1087
|
<button class="kbd-hint" onclick="openCmdPalette()" title="Open command palette">
|
|
876
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>
|
|
877
1089
|
Search <kbd>⌘K</kbd>
|
|
@@ -890,79 +1102,161 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
890
1102
|
══════════════════════════════════════════════════════════════════════ -->
|
|
891
1103
|
<div class="layout">
|
|
892
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
|
+
|
|
893
1110
|
<div class="nav-group">
|
|
894
|
-
<div class="nav-group-label">
|
|
895
|
-
<button class="nav-item active" data-tab="overview" data-title="
|
|
896
|
-
<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
|
-
|
|
898
|
-
</button>
|
|
899
|
-
<button class="nav-item" data-tab="
|
|
900
|
-
<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"><
|
|
901
|
-
|
|
902
|
-
</button>
|
|
903
|
-
<button class="nav-item" data-tab="
|
|
904
|
-
<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"><
|
|
905
|
-
|
|
906
|
-
</button>
|
|
907
|
-
<button class="nav-item" data-tab="
|
|
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
|
|
1115
|
+
</button>
|
|
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
|
|
1127
|
+
</button>
|
|
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.">
|
|
908
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>
|
|
909
|
-
|
|
1134
|
+
Connected Tools<span class="badge-count" id="badge-agents">0</span>
|
|
1135
|
+
</button>
|
|
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
|
|
910
1139
|
</button>
|
|
911
|
-
<button class="nav-item" data-tab="
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
Memory Browser
|
|
915
|
-
</button>
|
|
916
|
-
<button class="nav-item" data-tab="seeder" data-title="Memory Seeder">
|
|
917
|
-
<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>
|
|
918
|
-
Memory Seeder<span class="badge-count" id="badge-seeder">0</span>
|
|
919
|
-
</button>
|
|
920
|
-
<button class="nav-item" data-tab="livebrain" data-title="Live Brain">
|
|
921
|
-
<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 2v4"/><path d="M12 18v4"/><path d="m4.93 4.93 2.83 2.83"/><path d="m16.24 16.24 2.83 2.83"/><path d="M2 12h4"/><path d="M18 12h4"/><circle cx="12" cy="12" r="5"/></svg>
|
|
922
|
-
Live Brain
|
|
923
|
-
</button>
|
|
924
|
-
<button class="nav-item" data-tab="activity" data-title="Activity Log">
|
|
925
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
|
926
|
-
Activity Log
|
|
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
|
|
927
1143
|
</button>
|
|
928
1144
|
</div>
|
|
929
1145
|
|
|
930
|
-
<div class="nav-group">
|
|
931
|
-
<div class="nav-group-label">
|
|
932
|
-
<button class="nav-item" data-tab="
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
<button class="nav-item" data-tab="
|
|
937
|
-
|
|
938
|
-
|
|
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>
|
|
939
1164
|
</div>
|
|
940
1165
|
|
|
941
|
-
<div class="nav-group">
|
|
942
|
-
<div class="nav-group-label">
|
|
943
|
-
<button class="nav-item" data-tab="
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
<button class="nav-item" data-tab="
|
|
948
|
-
|
|
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>
|
|
949
1196
|
</div>
|
|
950
1197
|
|
|
951
|
-
<div class="nav-group">
|
|
952
|
-
<div class="nav-group-label">
|
|
953
|
-
<button class="nav-item" data-tab="
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
<button class="nav-item" data-tab="
|
|
958
|
-
|
|
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>
|
|
959
1224
|
</div>
|
|
960
1225
|
|
|
961
|
-
<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">
|
|
962
1251
|
<div class="nav-group-label">System</div>
|
|
963
|
-
<button class="nav-item" data-tab="features" data-title="
|
|
964
|
-
|
|
965
|
-
|
|
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>
|
|
966
1260
|
</div>
|
|
967
1261
|
</aside>
|
|
968
1262
|
|
|
@@ -973,21 +1267,21 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
973
1267
|
<div class="accent-bar"></div>
|
|
974
1268
|
<div class="section-header">
|
|
975
1269
|
<div>
|
|
976
|
-
<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"><
|
|
977
|
-
<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>
|
|
978
1272
|
</div>
|
|
979
1273
|
<div class="section-actions">
|
|
980
|
-
<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>
|
|
981
|
-
<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>
|
|
982
|
-
<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>
|
|
983
1277
|
</div>
|
|
984
1278
|
</div>
|
|
985
1279
|
|
|
986
1280
|
<div class="grid cols-4">
|
|
987
1281
|
<div class="card stat-card accent">
|
|
988
|
-
<div class="label">
|
|
1282
|
+
<div class="label">Tools Connected</div>
|
|
989
1283
|
<div class="value accent" id="ov-agents">—</div>
|
|
990
|
-
<div class="trend">
|
|
1284
|
+
<div class="trend">AI tools linked to your brain</div>
|
|
991
1285
|
</div>
|
|
992
1286
|
<div class="card stat-card accent">
|
|
993
1287
|
<div class="label">Memories Stored</div>
|
|
@@ -995,20 +1289,46 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
995
1289
|
<div class="trend" id="ov-memories-sub">across all projects</div>
|
|
996
1290
|
</div>
|
|
997
1291
|
<div class="card stat-card accent">
|
|
998
|
-
<div class="label">
|
|
1292
|
+
<div class="label">Helper Agents</div>
|
|
999
1293
|
<div class="value" id="ov-subagents">—</div>
|
|
1000
1294
|
<div class="trend" id="ov-subagents-sub">graduated / quarantined</div>
|
|
1001
1295
|
</div>
|
|
1002
1296
|
<div class="card stat-card accent">
|
|
1003
|
-
<div class="label">
|
|
1004
|
-
<div class="value" style="
|
|
1005
|
-
<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>
|
|
1006
1326
|
</div>
|
|
1007
1327
|
</div>
|
|
1008
1328
|
|
|
1009
1329
|
<div class="grid cols-3 mt-lg">
|
|
1010
1330
|
<div class="card span-2">
|
|
1011
|
-
<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>
|
|
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>
|
|
1012
1332
|
<div class="chart-wrap"><canvas id="chart-tokens"></canvas></div>
|
|
1013
1333
|
</div>
|
|
1014
1334
|
<div class="card">
|
|
@@ -1018,6 +1338,13 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1018
1338
|
</div>
|
|
1019
1339
|
|
|
1020
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>
|
|
1021
1348
|
<div class="card">
|
|
1022
1349
|
<div class="card-header"><div class="card-title">Module Status</div></div>
|
|
1023
1350
|
<div id="ov-modules"></div>
|
|
@@ -1029,106 +1356,170 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1029
1356
|
</div>
|
|
1030
1357
|
</section>
|
|
1031
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
|
+
|
|
1032
1423
|
<!-- ════ LIVE GRAPH ══════════════════════════════════════════════ -->
|
|
1033
|
-
<!-- WW2BR BRAIN -->
|
|
1034
|
-
<section class="section" id="tab-ww2br">
|
|
1035
|
-
<div class="accent-bar"></div>
|
|
1036
|
-
<div class="section-header">
|
|
1037
|
-
<div>
|
|
1038
|
-
<h1 class="section-title">WW2BR Brain View</h1>
|
|
1039
|
-
<div class="section-subtitle">Live project memory, recall, and integration status for the current game-development workflow</div>
|
|
1040
|
-
</div>
|
|
1041
|
-
<div class="section-actions">
|
|
1042
|
-
<button class="btn primary" onclick="refreshWW2BR()">Refresh</button>
|
|
1043
|
-
</div>
|
|
1044
|
-
</div>
|
|
1045
|
-
|
|
1046
|
-
<div class="workflow-strip">
|
|
1047
|
-
<div>
|
|
1048
|
-
<div class="field" style="margin:0">
|
|
1049
|
-
<label>Active project path</label>
|
|
1050
|
-
<input class="workflow-path" id="ww2-project-path" value="D:\\New Game"/>
|
|
1051
|
-
</div>
|
|
1052
|
-
</div>
|
|
1053
|
-
<button class="btn" onclick="refreshWW2BR()">Load</button>
|
|
1054
|
-
</div>
|
|
1055
|
-
|
|
1056
|
-
<div id="ww2-warnings" class="warning-stack"></div>
|
|
1057
|
-
|
|
1058
|
-
<div class="grid cols-4">
|
|
1059
|
-
<div class="card stat-card">
|
|
1060
|
-
<div class="label">Global Memories</div>
|
|
1061
|
-
<div class="value accent" id="ww2-stat-memories">0</div>
|
|
1062
|
-
<div class="trend" id="ww2-stat-scope">project scoped</div>
|
|
1063
|
-
</div>
|
|
1064
|
-
<div class="card stat-card">
|
|
1065
|
-
<div class="label">Brain Projects</div>
|
|
1066
|
-
<div class="value" id="ww2-stat-projects">0</div>
|
|
1067
|
-
<div class="trend">known roots</div>
|
|
1068
|
-
</div>
|
|
1069
|
-
<div class="card stat-card">
|
|
1070
|
-
<div class="label">Agent Hooks</div>
|
|
1071
|
-
<div class="value" id="ww2-stat-hooks">0</div>
|
|
1072
|
-
<div class="trend">wired for this path</div>
|
|
1073
|
-
</div>
|
|
1074
|
-
<div class="card stat-card">
|
|
1075
|
-
<div class="label">Hit Rate</div>
|
|
1076
|
-
<div class="value" id="ww2-stat-hit">0%</div>
|
|
1077
|
-
<div class="trend" id="ww2-stat-sync">last sync unknown</div>
|
|
1078
|
-
</div>
|
|
1079
|
-
</div>
|
|
1080
|
-
|
|
1081
|
-
<div class="grid cols-3 mt-lg">
|
|
1082
|
-
<div class="card">
|
|
1083
|
-
<div class="card-header"><div class="card-title">Memory Quality</div></div>
|
|
1084
|
-
<div id="ww2-quality" class="stack"></div>
|
|
1085
|
-
</div>
|
|
1086
|
-
<div class="card span-2">
|
|
1087
|
-
<div class="card-header">
|
|
1088
|
-
<div class="card-title">Context Continuity Pack</div>
|
|
1089
|
-
<button class="btn sm" onclick="openWW2ResumePack()">Markdown</button>
|
|
1090
|
-
</div>
|
|
1091
|
-
<div id="ww2-resume-pack"></div>
|
|
1092
|
-
</div>
|
|
1093
|
-
</div>
|
|
1094
|
-
|
|
1095
|
-
<div class="grid cols-3 mt-lg">
|
|
1096
|
-
<div class="card span-2">
|
|
1097
|
-
<div class="card-header">
|
|
1098
|
-
<div class="card-title">Recent Brain Timeline</div>
|
|
1099
|
-
<button class="btn sm" onclick="refreshWW2BR()">Refresh</button>
|
|
1100
|
-
</div>
|
|
1101
|
-
<div id="ww2-timeline" class="brain-feed"></div>
|
|
1102
|
-
</div>
|
|
1103
|
-
<div class="card">
|
|
1104
|
-
<div class="card-header"><div class="card-title">Agent Integrations</div></div>
|
|
1105
|
-
<div id="ww2-agents" class="ops-agent-grid"></div>
|
|
1106
|
-
</div>
|
|
1107
|
-
</div>
|
|
1108
|
-
|
|
1109
|
-
<div class="grid cols-3 mt-lg">
|
|
1110
|
-
<div class="card">
|
|
1111
|
-
<div class="card-header"><div class="card-title">Memory Categories</div></div>
|
|
1112
|
-
<div id="ww2-categories"></div>
|
|
1113
|
-
</div>
|
|
1114
|
-
<div class="card span-2">
|
|
1115
|
-
<div class="card-header"><div class="card-title">Recall Query</div></div>
|
|
1116
|
-
<div class="grid cols-3">
|
|
1117
|
-
<div class="field span-2"><label>Query</label><input id="ww2-query" placeholder="Gate 2, operator proof, lobby blocker"/></div>
|
|
1118
|
-
<div class="field"><label>Category</label><input id="ww2-category" placeholder="optional"/></div>
|
|
1119
|
-
</div>
|
|
1120
|
-
<div class="flex gap">
|
|
1121
|
-
<button class="btn primary" onclick="runWW2Recall()">Recall</button>
|
|
1122
|
-
<button class="btn" onclick="setWW2Query('Gate 2 visual proof')">Gate 2</button>
|
|
1123
|
-
<button class="btn" onclick="setWW2Query('operator hands rifle hold')">Operator</button>
|
|
1124
|
-
<button class="btn" onclick="setWW2Query('lobby verification blockers')">Lobby</button>
|
|
1125
|
-
</div>
|
|
1126
|
-
<div id="ww2-recall-results" class="brain-feed mt"></div>
|
|
1127
|
-
</div>
|
|
1128
|
-
</div>
|
|
1129
|
-
</section>
|
|
1130
|
-
|
|
1131
|
-
<section class="section" id="tab-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
|
+
|
|
1522
|
+
<section class="section" id="tab-graph">
|
|
1132
1523
|
<div class="accent-bar"></div>
|
|
1133
1524
|
<div class="section-header">
|
|
1134
1525
|
<div>
|
|
@@ -1205,7 +1596,7 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1205
1596
|
<div class="section-header">
|
|
1206
1597
|
<div>
|
|
1207
1598
|
<h1 class="section-title">Memory Browser</h1>
|
|
1208
|
-
<div class="section-subtitle">Full-text search plus live memory quality, provenance, freshness, and agent coverage</div>
|
|
1599
|
+
<div class="section-subtitle">Full-text search plus live memory quality, provenance, freshness, and agent coverage</div>
|
|
1209
1600
|
</div>
|
|
1210
1601
|
</div>
|
|
1211
1602
|
<div class="card">
|
|
@@ -1222,137 +1613,155 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1222
1613
|
<tbody id="mem-results"></tbody>
|
|
1223
1614
|
</table>
|
|
1224
1615
|
</div>
|
|
1225
|
-
</section>
|
|
1226
|
-
|
|
1227
|
-
<!-- ════ LIVE BRAIN ═══════════════════════════════════════════════ -->
|
|
1228
|
-
<!-- MEMORY SEEDER -->
|
|
1229
|
-
<section class="section" id="tab-seeder">
|
|
1230
|
-
<div class="accent-bar"></div>
|
|
1231
|
-
<div class="section-header">
|
|
1232
|
-
<div>
|
|
1233
|
-
<h1 class="section-title">Memory Seeder</h1>
|
|
1234
|
-
<div class="section-subtitle">Research drafts, approval gates, and source-backed global brain writes</div>
|
|
1235
|
-
</div>
|
|
1236
|
-
<div class="section-actions">
|
|
1237
|
-
<button class="btn" onclick="refreshSeedDrafts()">Refresh</button>
|
|
1238
|
-
</div>
|
|
1239
|
-
</div>
|
|
1240
|
-
|
|
1241
|
-
<div class="grid cols-3">
|
|
1242
|
-
<div class="card span-2">
|
|
1243
|
-
<div class="card-header"><div class="card-title">Seed Research Draft</div></div>
|
|
1244
|
-
<div class="grid cols-2">
|
|
1245
|
-
<div class="field"><label>Project path</label><input id="seed-project-path" placeholder="C:\\path\\to\\project"/></div>
|
|
1246
|
-
<div class="field"><label>Topic</label><input id="seed-topic" placeholder="memory routing, architecture, release, debugging"/></div>
|
|
1247
|
-
</div>
|
|
1248
|
-
<div class="field"><label>Objective</label><input id="seed-objective" placeholder="what the next agent must learn and reuse"/></div>
|
|
1249
|
-
<div class="grid cols-3">
|
|
1250
|
-
<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>
|
|
1251
|
-
<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>
|
|
1252
|
-
<div class="field"><label>Approval mode</label><select id="seed-approval-mode"><option>user</option><option>agent</option><option>dual</option></select></div>
|
|
1253
|
-
</div>
|
|
1254
|
-
<div class="grid cols-3">
|
|
1255
|
-
<div class="field"><label>Category</label><input id="seed-category" value="project.research"/></div>
|
|
1256
|
-
<div class="field"><label>Tags</label><input id="seed-tags" placeholder="enterprise,memory,release"/></div>
|
|
1257
|
-
<div class="field"><label>Importance</label><input id="seed-importance" type="number" min="0.05" max="1" step="0.05" value="0.78"/></div>
|
|
1258
|
-
</div>
|
|
1259
|
-
<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>
|
|
1260
|
-
<div class="field"><label>Research notes</label><textarea id="seed-content" rows="4" placeholder="facts, constraints, tradeoffs, exact commands, source-backed decisions"></textarea></div>
|
|
1261
|
-
<div class="flex gap">
|
|
1262
|
-
<button class="btn primary" onclick="
|
|
1263
|
-
<button class="btn" onclick="
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
<div class="card stat-card"><div class="label">
|
|
1297
|
-
<div class="card stat-card"><div class="label">
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
<div class="card"><div class="card-header"><div class="card-title">Top
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
<div class="card stat-card"><div class="label">
|
|
1314
|
-
<div class="card stat-card"><div class="label">
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1616
|
+
</section>
|
|
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
|
+
|
|
1729
|
+
<!-- ════ ACTIVITY LOG ════════════════════════════════════════════ -->
|
|
1730
|
+
<section class="section" id="tab-activity">
|
|
1338
1731
|
<div class="accent-bar"></div>
|
|
1339
1732
|
<div class="section-header">
|
|
1340
1733
|
<div>
|
|
1341
|
-
<h1 class="section-title">Activity Log</h1>
|
|
1342
|
-
<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>
|
|
1343
1736
|
</div>
|
|
1344
1737
|
<div class="section-actions">
|
|
1345
|
-
<
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
<option value="subagent-spawn">subagent-spawn</option>
|
|
1349
|
-
<option value="collision">collision</option>
|
|
1350
|
-
<option value="dream">dream</option>
|
|
1351
|
-
<option value="quarantine">quarantine</option>
|
|
1352
|
-
</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>
|
|
1353
1741
|
<button class="btn" onclick="refreshActivity()">Refresh</button>
|
|
1354
1742
|
</div>
|
|
1355
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>
|
|
1356
1765
|
<div class="card" style="padding:0">
|
|
1357
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>
|
|
1358
1767
|
</div>
|
|
@@ -1408,7 +1817,13 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1408
1817
|
<div class="field"><label>Max depth</label><input id="causal-depth" value="8"/></div>
|
|
1409
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>
|
|
1410
1819
|
</div>
|
|
1411
|
-
|
|
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>
|
|
1412
1827
|
</div>
|
|
1413
1828
|
</div>
|
|
1414
1829
|
<div class="card mt-lg">
|
|
@@ -1469,6 +1884,10 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1469
1884
|
<pre class="mono" id="rep-badge-output" style="margin-top:10px;background:var(--bg-elev-1);padding:10px;border-radius:6px"></pre>
|
|
1470
1885
|
</div>
|
|
1471
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>
|
|
1472
1891
|
<div class="card mt-lg" style="padding:0">
|
|
1473
1892
|
<div style="padding:14px 18px 6px"><div class="card-title">Agent Leaderboard</div></div>
|
|
1474
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>
|
|
@@ -1504,9 +1923,9 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1504
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>
|
|
1505
1924
|
<div class="card">
|
|
1506
1925
|
<div class="field"><label>Decision text</label><input id="att-decision" placeholder="e.g. use parameterized SQL queries"/></div>
|
|
1507
|
-
<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>
|
|
1508
1927
|
<button class="btn primary" onclick="runAttention()">Compute Attention</button>
|
|
1509
|
-
<
|
|
1928
|
+
<div id="att-output" style="margin-top:14px"></div>
|
|
1510
1929
|
</div>
|
|
1511
1930
|
</section>
|
|
1512
1931
|
|
|
@@ -1514,22 +1933,22 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1514
1933
|
<section class="section" id="tab-tokens">
|
|
1515
1934
|
<div class="accent-bar"></div>
|
|
1516
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>
|
|
1517
|
-
<div class="grid cols-4">
|
|
1518
|
-
<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>
|
|
1519
|
-
<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>
|
|
1520
|
-
<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>
|
|
1521
|
-
<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>
|
|
1522
|
-
</div>
|
|
1523
|
-
<div class="grid cols-2 mt">
|
|
1524
|
-
<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>
|
|
1525
|
-
<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>
|
|
1526
|
-
</div>
|
|
1527
|
-
<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>
|
|
1528
|
-
<div class="grid cols-2 mt-lg">
|
|
1529
|
-
<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>
|
|
1530
|
-
<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>
|
|
1531
|
-
<div class="card span-2"><div class="card-header"><div class="card-title">Savings Suggestions</div></div><div id="tok-suggestions"></div></div>
|
|
1532
|
-
</div>
|
|
1936
|
+
<div class="grid cols-4">
|
|
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">
|
|
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>
|
|
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>
|
|
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>
|
|
1947
|
+
<div class="grid cols-2 mt-lg">
|
|
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>
|
|
1951
|
+
</div>
|
|
1533
1952
|
</section>
|
|
1534
1953
|
|
|
1535
1954
|
<!-- ════ FORGET ══════════════════════════════════════════════════ -->
|
|
@@ -1564,7 +1983,11 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1564
1983
|
<!-- ════ CALIBRATION ═════════════════════════════════════════════ -->
|
|
1565
1984
|
<section class="section" id="tab-calibration">
|
|
1566
1985
|
<div class="accent-bar"></div>
|
|
1567
|
-
<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>
|
|
1568
1991
|
<div class="card" style="padding:0">
|
|
1569
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>
|
|
1570
1993
|
</div>
|
|
@@ -1574,7 +1997,8 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1574
1997
|
<section class="section" id="tab-branch">
|
|
1575
1998
|
<div class="accent-bar"></div>
|
|
1576
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>
|
|
1577
|
-
<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>
|
|
1578
2002
|
</section>
|
|
1579
2003
|
|
|
1580
2004
|
<!-- ════ PRIVACY ═════════════════════════════════════════════════ -->
|
|
@@ -1785,28 +2209,38 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1785
2209
|
══════════════════════════════════════════════════════════════════════ -->
|
|
1786
2210
|
<script>
|
|
1787
2211
|
const FEATURES = [
|
|
1788
|
-
['
|
|
1789
|
-
['
|
|
1790
|
-
['
|
|
1791
|
-
['
|
|
1792
|
-
['
|
|
1793
|
-
['
|
|
1794
|
-
['
|
|
1795
|
-
['
|
|
1796
|
-
['
|
|
1797
|
-
['
|
|
1798
|
-
['
|
|
1799
|
-
['
|
|
1800
|
-
['
|
|
1801
|
-
['
|
|
1802
|
-
['
|
|
1803
|
-
['
|
|
1804
|
-
['
|
|
1805
|
-
['
|
|
1806
|
-
['
|
|
1807
|
-
['
|
|
1808
|
-
['
|
|
1809
|
-
['
|
|
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'],
|
|
1810
2244
|
];
|
|
1811
2245
|
|
|
1812
2246
|
let ws = null;
|
|
@@ -1820,9 +2254,21 @@ function switchTab(id, title) {
|
|
|
1820
2254
|
document.querySelectorAll('.section').forEach(el => el.classList.toggle('active', el.id === 'tab-' + id));
|
|
1821
2255
|
if (window.innerWidth <= 768) document.getElementById('sidebar').classList.remove('open');
|
|
1822
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) {}
|
|
1823
2269
|
}
|
|
1824
|
-
function onTabOpen(tab) {
|
|
1825
|
-
const fn = { overview: refreshOverview, ww2br: refreshWW2BR, graph: startGraph, agents: refreshAgents, memory: () => { refreshMemoryIntelligence(); searchMemory(); }, seeder: refreshSeedDrafts, livebrain: refreshLiveBrain, chat: refreshChat, activity: refreshActivity,
|
|
2270
|
+
function onTabOpen(tab) {
|
|
2271
|
+
const fn = { overview: refreshOverview, intelligence: refreshIntelligence, ww2br: refreshWW2BR, graph: startGraph, agents: refreshAgents, memory: () => { refreshMemoryIntelligence(); searchMemory(); }, seeder: refreshSeedDrafts, livebrain: refreshLiveBrain, chat: refreshChat, activity: refreshActivity,
|
|
1826
2272
|
sabb: refreshSABB, causal: refreshCausal, collision: refreshCollision, dream: refreshDream, reputation: refreshReputation,
|
|
1827
2273
|
tokens: refreshTokens, forget: refreshForget, formal: null, calibration: refreshCalibration, branch: refreshBranch,
|
|
1828
2274
|
privacy: refreshPrivacy, garden: refreshGarden, team: refreshTeam, exchange: refreshExchange, features: refreshFeatures,
|
|
@@ -1849,6 +2295,24 @@ function toggleTheme() {
|
|
|
1849
2295
|
} catch { document.documentElement.dataset.theme = 'light'; }
|
|
1850
2296
|
})();
|
|
1851
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
|
+
|
|
1852
2316
|
// ──── API helpers ──────────────────────────────────────────────────────
|
|
1853
2317
|
async function api(path, body) {
|
|
1854
2318
|
try {
|
|
@@ -1857,22 +2321,41 @@ async function api(path, body) {
|
|
|
1857
2321
|
if (!res.ok) { toast('API error', res.status + ' ' + path, 'error'); return null; }
|
|
1858
2322
|
const ct = res.headers.get('content-type') || '';
|
|
1859
2323
|
return ct.includes('application/json') ? await res.json() : await res.text();
|
|
1860
|
-
} catch (err) { toast('Network error', err.message, 'error'); return null; }
|
|
1861
|
-
}
|
|
1862
|
-
function htmlEscape(value) {
|
|
1863
|
-
return String(value ?? '')
|
|
1864
|
-
.replace(/&/g, '&')
|
|
1865
|
-
.replace(/</g, '<')
|
|
1866
|
-
.replace(/>/g, '>')
|
|
1867
|
-
.replace(/"/g, '"')
|
|
1868
|
-
.replace(/'/g, ''');
|
|
1869
|
-
}
|
|
1870
|
-
function fmtDate(value) {
|
|
1871
|
-
const d = new Date(value);
|
|
1872
|
-
return Number.isNaN(d.getTime()) ? 'unknown' : d.toLocaleString();
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
//
|
|
2324
|
+
} catch (err) { toast('Network error', err.message, 'error'); return null; }
|
|
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
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// ──── Toasts ───────────────────────────────────────────────────────────
|
|
1876
2359
|
function toast(title, body, variant='success') {
|
|
1877
2360
|
const c = document.getElementById('toast-container');
|
|
1878
2361
|
const icons = { success:'<path d="M20 6 9 17l-5-5"/>', warn:'<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>', error:'<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>', info:'<circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/>' };
|
|
@@ -1932,7 +2415,7 @@ async function refresh() {
|
|
|
1932
2415
|
animateNum('stat-agents', hive.totalAgentsConnected ?? 0);
|
|
1933
2416
|
animateNum('stat-memories', hive.totalMemoriesStored ?? 0);
|
|
1934
2417
|
animateNum('stat-dreams', hive.modules.dream?.totalDreams ?? 0);
|
|
1935
|
-
|
|
2418
|
+
animateNum('stat-tools', hive.totalAgentsConnected ?? 0);
|
|
1936
2419
|
document.getElementById('badge-sabb').textContent = hive.modules.sabb?.totalSpawns ?? 0;
|
|
1937
2420
|
document.getElementById('badge-causal').textContent = hive.modules.causal?.totalLinks ?? 0;
|
|
1938
2421
|
document.getElementById('badge-collision').textContent = hive.modules.collision?.collisionsDetected ?? 0;
|
|
@@ -1949,8 +2432,10 @@ async function refreshOverview() {
|
|
|
1949
2432
|
animateNum('ov-memories', hive.totalMemoriesStored ?? 0);
|
|
1950
2433
|
animateNum('ov-subagents', hive.modules.sabb?.totalSpawns ?? 0);
|
|
1951
2434
|
document.getElementById('ov-subagents-sub').textContent = \`grad: \${hive.modules.sabb?.graduated ?? 0} · quar: \${hive.modules.sabb?.quarantined ?? 0}\`;
|
|
1952
|
-
|
|
1953
|
-
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();
|
|
1954
2439
|
|
|
1955
2440
|
const moduleRows = [
|
|
1956
2441
|
['SABB spawns', hive.modules.sabb?.totalSpawns ?? 0, 'good'],
|
|
@@ -1964,19 +2449,19 @@ async function refreshOverview() {
|
|
|
1964
2449
|
\`<div class="row"><span class="k">\${k}</span><span class="v \${cls || ''}">\${v}</span></div>\`
|
|
1965
2450
|
).join('');
|
|
1966
2451
|
|
|
1967
|
-
const health = [
|
|
1968
|
-
['Local-first', hive.localFirst ? 'ON' : 'OFF', hive.localFirst ? 'good' : 'warn'],
|
|
1969
|
-
['Current agent', hive.runtime?.currentAgentDisplay || 'Unknown', hive.runtime?.currentAgentTool === 'codex' ? 'good' : ''],
|
|
1970
|
-
['Air-gap', hive.modules.airGap?.enabled ? 'ENABLED' : 'disabled', hive.modules.airGap?.enabled ? 'good' : ''],
|
|
1971
|
-
['Version', hive.version, ''],
|
|
1972
|
-
['Monthly cost', '$' + (hive.modules.tokenEconomy?.monthlyProjectionUsd?.toFixed?.(2) ?? '0.00'), 'warn'],
|
|
2452
|
+
const health = [
|
|
2453
|
+
['Local-first', hive.localFirst ? 'ON' : 'OFF', hive.localFirst ? 'good' : 'warn'],
|
|
2454
|
+
['Current agent', hive.runtime?.currentAgentDisplay || 'Unknown', hive.runtime?.currentAgentTool === 'codex' ? 'good' : ''],
|
|
2455
|
+
['Air-gap', hive.modules.airGap?.enabled ? 'ENABLED' : 'disabled', hive.modules.airGap?.enabled ? 'good' : ''],
|
|
2456
|
+
['Version', hive.version, ''],
|
|
2457
|
+
['Monthly cost', '$' + (hive.modules.tokenEconomy?.monthlyProjectionUsd?.toFixed?.(2) ?? '0.00'), 'warn'],
|
|
1973
2458
|
['Cost saved/mo', '$' + (hive.modules.tokenEconomy?.savingsOpportunitiesUsd?.toFixed?.(2) ?? '0.00'), 'good'],
|
|
1974
2459
|
];
|
|
1975
2460
|
document.getElementById('ov-health').innerHTML = health.map(([k, v, cls]) => \`<div class="row"><span class="k">\${k}</span><span class="v \${cls}">\${v}</span></div>\`).join('');
|
|
1976
2461
|
|
|
1977
2462
|
// Tokens chart
|
|
1978
|
-
const tok = hive.modules.tokenEconomy;
|
|
1979
|
-
if (tok) drawTokensChart(tok.recentByAgent || tok.byAgent || {});
|
|
2463
|
+
const tok = hive.modules.tokenEconomy;
|
|
2464
|
+
if (tok) drawTokensChart(tok.recentByAgent || tok.byAgent || {});
|
|
1980
2465
|
|
|
1981
2466
|
// Timeline
|
|
1982
2467
|
const timeline = await api('/api/v6/activity-log?limit=15') ?? [];
|
|
@@ -1984,191 +2469,191 @@ async function refreshOverview() {
|
|
|
1984
2469
|
? timeline.slice(0, 10).map(e => \`<div style="padding:5px 0;border-bottom:1px solid var(--border);display:flex;gap:8px"><span style="color:var(--text-dim);flex-shrink:0">\${new Date(e.timestamp).toLocaleTimeString()}</span><span class="pill \${typeToPill(e.type)}">\${e.type}</span><span style="color:var(--text-2);font-size:11px">\${(e.detail||'').slice(0,90)}</span></div>\`).join('')
|
|
1985
2470
|
: '<div class="empty">No activity yet</div>';
|
|
1986
2471
|
}
|
|
1987
|
-
function typeToPill(type) {
|
|
1988
|
-
if (type === 'memory-write') return 'green';
|
|
1989
|
-
if (type === 'subagent-spawn') return 'cyan';
|
|
1990
|
-
if (type === 'collision') return 'red';
|
|
1991
|
-
if (type === 'quarantine') return 'yellow';
|
|
1992
|
-
if (type === 'seed-draft') return 'yellow';
|
|
1993
|
-
if (type === 'memory-seeded') return 'green';
|
|
1994
|
-
if (['dream','revisit','counterfactual','consolidation','pattern-discovery','contradiction'].includes(type)) return 'purple';
|
|
1995
|
-
return '';
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
function getWW2ProjectPath() {
|
|
1999
|
-
return document.getElementById('ww2-project-path')?.value?.trim() || 'D:\\\\New Game';
|
|
2000
|
-
}
|
|
2001
|
-
async function refreshWW2BR() {
|
|
2002
|
-
const projectDir = getWW2ProjectPath();
|
|
2003
|
-
const data = await api('/api/v6/ww2br-ops?projectDir=' + encodeURIComponent(projectDir));
|
|
2004
|
-
if (!data) return;
|
|
2005
|
-
|
|
2006
|
-
const stats = data.stats || {};
|
|
2007
|
-
const hooked = (data.agents || []).filter(a => a.hookInstalled).length;
|
|
2008
|
-
document.getElementById('badge-ww2br').textContent = data.recent?.length ?? 0;
|
|
2009
|
-
animateNum('ww2-stat-memories', stats.totalEntries ?? 0);
|
|
2010
|
-
animateNum('ww2-stat-projects', stats.totalProjects ?? 0);
|
|
2011
|
-
animateNum('ww2-stat-hooks', hooked);
|
|
2012
|
-
document.getElementById('ww2-stat-hit').textContent = Math.round((stats.hitRate ?? 0) * 100) + '%';
|
|
2013
|
-
document.getElementById('ww2-stat-scope').textContent = data.projectScoped ? 'project scoped' : 'global fallback';
|
|
2014
|
-
document.getElementById('ww2-stat-sync').textContent = stats.lastSync ? 'sync ' + fmtDate(stats.lastSync) : 'last sync unknown';
|
|
2015
|
-
|
|
2016
|
-
const warnings = data.warnings || [];
|
|
2017
|
-
document.getElementById('ww2-warnings').innerHTML = warnings.length
|
|
2018
|
-
? warnings.map(w => '<div class="warning-banner"><strong>Warning</strong><span>' + htmlEscape(w) + '</span></div>').join('')
|
|
2019
|
-
: '';
|
|
2020
|
-
|
|
2021
|
-
document.getElementById('ww2-timeline').innerHTML = renderMemoryItems(data.recent || [], 'No memories found for this workflow yet.');
|
|
2022
|
-
document.getElementById('ww2-agents').innerHTML = renderWW2Agents(data.agents || []);
|
|
2023
|
-
document.getElementById('ww2-categories').innerHTML = renderWW2Categories(data.categories || {});
|
|
2024
|
-
document.getElementById('ww2-quality').innerHTML = renderWW2Quality(data.quality);
|
|
2025
|
-
document.getElementById('ww2-resume-pack').innerHTML = renderWW2ResumePack(data.resumePack);
|
|
2026
|
-
|
|
2027
|
-
const query = document.getElementById('ww2-query');
|
|
2028
|
-
if (query && !query.value) query.value = 'Gate 2 visual proof';
|
|
2029
|
-
if (!document.getElementById('ww2-recall-results').innerHTML.trim()) {
|
|
2030
|
-
document.getElementById('ww2-recall-results').innerHTML = '<div class="empty-tight">Run a recall query to inspect relevant brain memory.</div>';
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
function renderMemoryItems(items, emptyMessage) {
|
|
2034
|
-
if (!items || !items.length) return '<div class="empty-tight">' + htmlEscape(emptyMessage) + '</div>';
|
|
2035
|
-
return items.map(item => {
|
|
2036
|
-
const confidence = item.importance !== undefined ? Math.round((item.importance || 0) * 100) + '%' : '';
|
|
2037
|
-
const sources = item.sources || (item.metadata && item.metadata.sources) || [];
|
|
2038
|
-
const tags = item.tags || (item.metadata && item.metadata.tags) || [];
|
|
2039
|
-
const quality = item.qualityScore !== undefined ? '<span class="pill">Q ' + htmlEscape(item.qualityScore) + '</span>' : '';
|
|
2040
|
-
const sourceLine = sources.length
|
|
2041
|
-
? '<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>'
|
|
2042
|
-
: '<div class="content" style="font-size:10.5px;color:var(--warning);margin-top:6px">Sources: none recorded</div>';
|
|
2043
|
-
const tagLine = tags.length ? '<span class="pill">' + tags.slice(0, 3).map(htmlEscape).join(', ') + '</span>' : '';
|
|
2044
|
-
return '<div class="memory-item">' +
|
|
2045
|
-
'<div class="meta">' +
|
|
2046
|
-
'<span class="pill cyan">' + htmlEscape(item.agentTool || item.agent || 'brain') + '</span>' +
|
|
2047
|
-
'<span class="pill">' + htmlEscape(item.category || 'memory') + '</span>' +
|
|
2048
|
-
(confidence ? '<span class="pill green">' + confidence + '</span>' : '') +
|
|
2049
|
-
quality +
|
|
2050
|
-
tagLine +
|
|
2051
|
-
'<span>' + fmtDate(item.createdAt || item.lastAccessed) + '</span>' +
|
|
2052
|
-
'</div>' +
|
|
2053
|
-
'<div class="content">' + htmlEscape(item.content || item.detail || '').slice(0, 420) + '</div>' +
|
|
2054
|
-
sourceLine +
|
|
2055
|
-
'</div>';
|
|
2056
|
-
}).join('');
|
|
2057
|
-
}
|
|
2058
|
-
function renderWW2Quality(quality) {
|
|
2059
|
-
if (!quality) return '<div class="empty-tight">No memory quality data.</div>';
|
|
2060
|
-
const sourcePct = Math.round((quality.sourceCoverage || 0) * 100);
|
|
2061
|
-
const scoreCls = quality.avgScore >= 75 ? 'good' : quality.avgScore >= 55 ? 'warn' : 'bad';
|
|
2062
|
-
return [
|
|
2063
|
-
'<div class="row"><span class="k">Average score</span><span class="v ' + scoreCls + '">' + htmlEscape(quality.avgScore ?? 0) + '/100</span></div>',
|
|
2064
|
-
'<div class="row"><span class="k">Source coverage</span><span class="v ' + (sourcePct >= 70 ? 'good' : 'warn') + '">' + sourcePct + '%</span></div>',
|
|
2065
|
-
'<div class="row"><span class="k">Stale memories</span><span class="v ' + (quality.stale ? 'warn' : 'good') + '">' + htmlEscape(quality.stale || 0) + '</span></div>',
|
|
2066
|
-
'<div class="row"><span class="k">Duplicate clusters</span><span class="v ' + (quality.duplicates?.length ? 'warn' : 'good') + '">' + htmlEscape(quality.duplicates?.length || 0) + '</span></div>',
|
|
2067
|
-
].join('');
|
|
2068
|
-
}
|
|
2069
|
-
function renderWW2ResumePack(pack) {
|
|
2070
|
-
if (!pack) return '<div class="empty-tight">No resume pack generated.</div>';
|
|
2071
|
-
const warnings = pack.warnings && pack.warnings.length
|
|
2072
|
-
? '<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>'
|
|
2073
|
-
: '';
|
|
2074
|
-
const blockers = pack.blockers && pack.blockers.length
|
|
2075
|
-
? '<div class="brain-feed mt">' + renderMemoryItems(pack.blockers.slice(0, 3), 'No blockers in pack.') + '</div>'
|
|
2076
|
-
: '<div class="empty-tight">No blockers in pack.</div>';
|
|
2077
|
-
const stats = pack.stats || {};
|
|
2078
|
-
return '<div class="grid cols-3">' +
|
|
2079
|
-
'<div class="row"><span class="k">Docs</span><span class="v">' + htmlEscape(stats.docCount ?? 0) + '</span></div>' +
|
|
2080
|
-
'<div class="row"><span class="k">Memories</span><span class="v">' + htmlEscape(stats.memoryCount ?? 0) + '</span></div>' +
|
|
2081
|
-
'<div class="row"><span class="k">Quality</span><span class="v">' + htmlEscape(stats.avgQualityScore ?? 0) + '/100</span></div>' +
|
|
2082
|
-
'</div>' +
|
|
2083
|
-
'<div class="mt" style="font-size:12px;color:var(--text-2)">Generated ' + htmlEscape(fmtDate(pack.generatedAt)) + '. Uses visible docs and stored memories only.</div>' +
|
|
2084
|
-
warnings +
|
|
2085
|
-
'<div class="mt">' + blockers + '</div>';
|
|
2086
|
-
}
|
|
2087
|
-
function openWW2ResumePack() {
|
|
2088
|
-
const url = '/api/v6/resume-pack?format=markdown&projectDir=' + encodeURIComponent(getWW2ProjectPath());
|
|
2089
|
-
window.open(url, '_blank', 'noopener');
|
|
2090
|
-
}
|
|
2091
|
-
function renderWW2Agents(agents) {
|
|
2092
|
-
if (!agents.length) return '<div class="empty-tight">No known agent integrations returned.</div>';
|
|
2093
|
-
return agents.map(agent => {
|
|
2094
|
-
const cls = agent.hookInstalled ? 'green' : agent.detected ? 'cyan' : '';
|
|
2095
|
-
const hook = agent.hookInstalled ? 'hooked' : 'not wired';
|
|
2096
|
-
const detected = agent.detected ? 'detected' : 'not found';
|
|
2097
|
-
return '<div class="ops-agent">' +
|
|
2098
|
-
'<div class="name">' + htmlEscape(agent.displayName || agent.name) + '</div>' +
|
|
2099
|
-
'<div class="status">' +
|
|
2100
|
-
'<span class="pill ' + cls + '">' + hook + '</span>' +
|
|
2101
|
-
'<span class="pill">' + detected + '</span>' +
|
|
2102
|
-
'</div>' +
|
|
2103
|
-
'</div>';
|
|
2104
|
-
}).join('');
|
|
2105
|
-
}
|
|
2106
|
-
function renderWW2Categories(categories) {
|
|
2107
|
-
const rows = Object.entries(categories).sort((a, b) => b[1] - a[1]);
|
|
2108
|
-
if (!rows.length) return '<div class="empty-tight">No categories yet.</div>';
|
|
2109
|
-
return rows.map(([name, count]) => '<div class="row"><span class="k">' + htmlEscape(name) + '</span><span class="v">' + count + '</span></div>').join('');
|
|
2110
|
-
}
|
|
2111
|
-
function setWW2Query(query) {
|
|
2112
|
-
document.getElementById('ww2-query').value = query;
|
|
2113
|
-
runWW2Recall();
|
|
2114
|
-
}
|
|
2115
|
-
async function runWW2Recall() {
|
|
2116
|
-
const resultsEl = document.getElementById('ww2-recall-results');
|
|
2117
|
-
const query = document.getElementById('ww2-query').value.trim();
|
|
2118
|
-
const category = document.getElementById('ww2-category').value.trim();
|
|
2119
|
-
resultsEl.innerHTML = '<div class="empty-tight">Searching brain memory...</div>';
|
|
2120
|
-
const data = await api('/api/v6/ww2br-recall', {
|
|
2121
|
-
projectDir: getWW2ProjectPath(),
|
|
2122
|
-
query,
|
|
2123
|
-
category: category || undefined,
|
|
2124
|
-
limit: 12,
|
|
2125
|
-
});
|
|
2126
|
-
if (!data) return;
|
|
2127
|
-
const prefix = data.projectScoped ? '' : '<div class="warning-banner"><strong>Fallback</strong><span>No project-scoped matches; showing global brain recall results.</span></div>';
|
|
2128
|
-
resultsEl.innerHTML = prefix + renderMemoryItems(data.results || [], 'No recall matches for that query.');
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
// ──── Charts ───────────────────────────────────────────────────────────
|
|
2472
|
+
function typeToPill(type) {
|
|
2473
|
+
if (type === 'memory-write') return 'green';
|
|
2474
|
+
if (type === 'subagent-spawn') return 'cyan';
|
|
2475
|
+
if (type === 'collision') return 'red';
|
|
2476
|
+
if (type === 'quarantine') return 'yellow';
|
|
2477
|
+
if (type === 'seed-draft') return 'yellow';
|
|
2478
|
+
if (type === 'memory-seeded') return 'green';
|
|
2479
|
+
if (['dream','revisit','counterfactual','consolidation','pattern-discovery','contradiction'].includes(type)) return 'purple';
|
|
2480
|
+
return '';
|
|
2481
|
+
}
|
|
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
|
+
|
|
2616
|
+
// ──── Charts ───────────────────────────────────────────────────────────
|
|
2132
2617
|
function chartTheme() {
|
|
2133
2618
|
const dark = document.documentElement.dataset.theme !== 'light';
|
|
2134
2619
|
return { grid: dark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)', text: dark ? '#a4afc4' : '#3d4864' };
|
|
2135
2620
|
}
|
|
2136
|
-
function spendLabels(buckets) {
|
|
2137
|
-
return Object.keys(buckets || {}).sort((a, b) => (buckets[b]?.spendUsd || 0) - (buckets[a]?.spendUsd || 0));
|
|
2138
|
-
}
|
|
2139
|
-
function drawSpendBarChart(canvasId, chartKey, buckets) {
|
|
2140
|
-
const ctx = document.getElementById(canvasId);
|
|
2141
|
-
if (!ctx) return;
|
|
2142
|
-
const labels = spendLabels(buckets);
|
|
2143
|
-
const data = labels.map(l => buckets[l].spendUsd || 0);
|
|
2144
|
-
const t = chartTheme();
|
|
2145
|
-
if (charts[chartKey]) charts[chartKey].destroy();
|
|
2146
|
-
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)}}} } });
|
|
2147
|
-
}
|
|
2148
|
-
function drawTokensChart(byAgent) {
|
|
2149
|
-
drawSpendBarChart('chart-tokens', 'tokens', byAgent || {});
|
|
2150
|
-
}
|
|
2151
|
-
function drawTokensAgentChart(byAgent) {
|
|
2152
|
-
drawSpendBarChart('chart-tokens-agent', 'tokensAgent', byAgent || {});
|
|
2153
|
-
}
|
|
2154
|
-
function drawTokensModelChart(byModel) {
|
|
2155
|
-
const ctx = document.getElementById('chart-tokens-model');
|
|
2156
|
-
if (!ctx) return;
|
|
2157
|
-
const labels = spendLabels(byModel);
|
|
2158
|
-
const data = labels.map(l => byModel[l].spendUsd || 0);
|
|
2159
|
-
const t = chartTheme();
|
|
2160
|
-
if (charts.tokensModel) charts.tokensModel.destroy();
|
|
2161
|
-
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}}} } });
|
|
2162
|
-
}
|
|
2163
|
-
function renderSpendRows(buckets, emptyMessage) {
|
|
2164
|
-
const labels = spendLabels(buckets).slice(0, 8);
|
|
2165
|
-
if (!labels.length) return '<div class="empty-tight">' + htmlEscape(emptyMessage) + '</div>';
|
|
2166
|
-
return labels.map(label => {
|
|
2167
|
-
const b = buckets[label] || {};
|
|
2168
|
-
const last = b.lastSeen ? new Date(b.lastSeen).toLocaleString() : '—';
|
|
2169
|
-
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>\`;
|
|
2170
|
-
}).join('');
|
|
2171
|
-
}
|
|
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);
|
|
2626
|
+
if (!ctx) return;
|
|
2627
|
+
const labels = spendLabels(buckets);
|
|
2628
|
+
const data = labels.map(l => buckets[l].spendUsd || 0);
|
|
2629
|
+
const t = chartTheme();
|
|
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 || {});
|
|
2638
|
+
}
|
|
2639
|
+
function drawTokensModelChart(byModel) {
|
|
2640
|
+
const ctx = document.getElementById('chart-tokens-model');
|
|
2641
|
+
if (!ctx) return;
|
|
2642
|
+
const labels = spendLabels(byModel);
|
|
2643
|
+
const data = labels.map(l => byModel[l].spendUsd || 0);
|
|
2644
|
+
const t = chartTheme();
|
|
2645
|
+
if (charts.tokensModel) charts.tokensModel.destroy();
|
|
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}}} } });
|
|
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
|
+
}
|
|
2172
2657
|
function drawDreamChart(byType) {
|
|
2173
2658
|
const ctx = document.getElementById('chart-dreams');
|
|
2174
2659
|
if (!ctx) return;
|
|
@@ -2232,13 +2717,64 @@ async function traceCausal() {
|
|
|
2232
2717
|
const id = document.getElementById('causal-mem-id').value;
|
|
2233
2718
|
if (!id) return toast('Missing', 'Enter a memory ID', 'warn');
|
|
2234
2719
|
const res = await api('/api/v6/causal-trace', { memoryId: id, maxDepth: parseInt(document.getElementById('causal-depth').value || '8') });
|
|
2235
|
-
if (res)
|
|
2720
|
+
if (res) renderCausalDag(res, id);
|
|
2236
2721
|
}
|
|
2237
2722
|
async function influenceCausal() {
|
|
2238
2723
|
const id = document.getElementById('causal-mem-id').value;
|
|
2239
|
-
if (!id) return;
|
|
2724
|
+
if (!id) return toast('Missing', 'Enter a memory ID', 'warn');
|
|
2240
2725
|
const res = await api('/api/v6/causal-influence', { memoryId: id, maxDepth: 6 });
|
|
2241
|
-
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');
|
|
2242
2778
|
}
|
|
2243
2779
|
async function linkCausal() {
|
|
2244
2780
|
const r = await api('/api/v6/causal-link', {
|
|
@@ -2287,6 +2823,28 @@ async function refreshReputation() {
|
|
|
2287
2823
|
document.getElementById('rep-leaderboard').innerHTML = top.length ? top.map(a =>
|
|
2288
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>\`
|
|
2289
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();
|
|
2290
2848
|
}
|
|
2291
2849
|
async function genBadge() {
|
|
2292
2850
|
const res = await api('/api/v6/reputation-badge', { agentTool: document.getElementById('rep-agent').value, agentVersion: document.getElementById('rep-ver').value || undefined });
|
|
@@ -2308,40 +2866,91 @@ async function runDebate() {
|
|
|
2308
2866
|
|
|
2309
2867
|
// ──── Pre-mortem ───────────────────────────────────────────────────────
|
|
2310
2868
|
async function runPremortem() {
|
|
2311
|
-
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: '' });
|
|
2312
2872
|
if (!res) return;
|
|
2313
|
-
document.getElementById('pm-output')
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
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();
|
|
2317
2918
|
}
|
|
2318
2919
|
|
|
2319
2920
|
// ──── Attention ────────────────────────────────────────────────────────
|
|
2320
2921
|
async function runAttention() {
|
|
2321
2922
|
const ids = document.getElementById('att-memories').value.split(',').map(s => s.trim()).filter(Boolean);
|
|
2322
2923
|
const res = await api('/api/v6/attention-heatmap', { decisionText: document.getElementById('att-decision').value, candidateMemoryIds: ids });
|
|
2323
|
-
|
|
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('');
|
|
2324
2933
|
}
|
|
2325
2934
|
|
|
2326
2935
|
// ──── Tokens ───────────────────────────────────────────────────────────
|
|
2327
|
-
async function refreshTokens() {
|
|
2328
|
-
const res = await api('/api/v6/tokens-report');
|
|
2329
|
-
if (!res) return;
|
|
2330
|
-
const runtime = res.runtime || {};
|
|
2331
|
-
document.getElementById('tok-agent').textContent = runtime.currentAgentDisplay || 'Unknown';
|
|
2332
|
-
document.getElementById('tok-runtime').textContent = (runtime.provider || 'provider') + (runtime.model ? ' / ' + runtime.model : '');
|
|
2333
|
-
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>\`;
|
|
2334
|
-
document.getElementById('tok-total').textContent = '$' + (res.totalSpendUsd || 0).toFixed(4);
|
|
2335
|
-
document.getElementById('tok-recent').textContent = '$' + (res.recentSpendUsd || 0).toFixed(4);
|
|
2336
|
-
document.getElementById('tok-proj').textContent = '$' + (res.monthlyProjectionUsd || 0).toFixed(2);
|
|
2936
|
+
async function refreshTokens() {
|
|
2937
|
+
const res = await api('/api/v6/tokens-report');
|
|
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>\`;
|
|
2943
|
+
document.getElementById('tok-total').textContent = '$' + (res.totalSpendUsd || 0).toFixed(4);
|
|
2944
|
+
document.getElementById('tok-recent').textContent = '$' + (res.recentSpendUsd || 0).toFixed(4);
|
|
2945
|
+
document.getElementById('tok-proj').textContent = '$' + (res.monthlyProjectionUsd || 0).toFixed(2);
|
|
2337
2946
|
document.getElementById('tok-save').textContent = '$' + (res.savingsOpportunitiesUsd || 0).toFixed(2);
|
|
2338
2947
|
const calls = Object.values(res.byAgent || {}).reduce((a, b) => a + b.calls, 0);
|
|
2339
2948
|
document.getElementById('tok-calls').textContent = calls;
|
|
2340
2949
|
document.getElementById('tok-calls-sub').textContent = \`\${(res.totalInputTokens || 0).toLocaleString()} in · \${(res.totalOutputTokens || 0).toLocaleString()} out\`;
|
|
2341
|
-
drawTokensAgentChart(res.recentByAgent || res.byAgent || {});
|
|
2342
|
-
drawTokensModelChart(res.byModel || {});
|
|
2343
|
-
document.getElementById('tok-agent-table').innerHTML = renderSpendRows(res.recentByAgent || res.byAgent || {}, 'No recent agent spend records.');
|
|
2344
|
-
document.getElementById('tok-model-table').innerHTML = renderSpendRows(res.byModel || {}, 'No model spend records.');
|
|
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.');
|
|
2345
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>';
|
|
2346
2955
|
}
|
|
2347
2956
|
|
|
@@ -2382,18 +2991,39 @@ async function refreshCalibration() {
|
|
|
2382
2991
|
document.getElementById('calibration-table').innerHTML = scores.length ? scores.map(s =>
|
|
2383
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>\`
|
|
2384
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
|
+
});
|
|
2385
3010
|
}
|
|
2386
3011
|
|
|
2387
3012
|
// ──── Branch ───────────────────────────────────────────────────────────
|
|
2388
3013
|
async function refreshBranch() {
|
|
2389
3014
|
const r = await api('/api/v6/branch-state');
|
|
2390
|
-
|
|
2391
|
-
document.getElementById('branch-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
<div class="
|
|
2395
|
-
<div class="
|
|
2396
|
-
<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
|
+
}
|
|
2397
3027
|
}
|
|
2398
3028
|
|
|
2399
3029
|
// ──── Privacy ──────────────────────────────────────────────────────────
|
|
@@ -2713,26 +3343,26 @@ async function attachAll() { const r = await api('/api/v6/agents-attach-all', {}
|
|
|
2713
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(); } }
|
|
2714
3344
|
|
|
2715
3345
|
// ──── Memory browser ───────────────────────────────────────────────────
|
|
2716
|
-
function renderCountRows(rows, emptyMessage) {
|
|
2717
|
-
if (!rows || !rows.length) return '<div class="empty-tight">' + htmlEscape(emptyMessage) + '</div>';
|
|
2718
|
-
return rows.slice(0, 8).map(row =>
|
|
2719
|
-
'<div class="row"><span class="k">' + htmlEscape(row.name) + '</span><span class="v">' + htmlEscape(row.count) + '</span></div>'
|
|
2720
|
-
).join('');
|
|
2721
|
-
}
|
|
2722
|
-
async function refreshMemoryIntelligence() {
|
|
2723
|
-
const data = await api('/api/v6/memory-intelligence?limit=500');
|
|
2724
|
-
if (!data) return;
|
|
2725
|
-
document.getElementById('mem-total').textContent = data.totalEntries ?? 0;
|
|
2726
|
-
document.getElementById('mem-sampled').textContent = (data.sampledEntries ?? 0) + ' shown in intelligence sample';
|
|
2727
|
-
document.getElementById('mem-source').textContent = Math.round((data.sourceCoverage || 0) * 100) + '%';
|
|
2728
|
-
document.getElementById('mem-fresh').textContent = data.recent7d ?? 0;
|
|
2729
|
-
document.getElementById('mem-seed-open').textContent = data.openSeedDrafts ?? 0;
|
|
2730
|
-
document.getElementById('mem-seed-sub').textContent = (data.pendingApprovals ?? 0) + ' need approval';
|
|
2731
|
-
document.getElementById('mem-categories').innerHTML = renderCountRows(data.byCategory, 'No categories yet.');
|
|
2732
|
-
document.getElementById('mem-agents').innerHTML = renderCountRows(data.byAgent, 'No agent memory writes yet.');
|
|
2733
|
-
document.getElementById('mem-tags').innerHTML = renderCountRows(data.topTags, 'No tags yet.');
|
|
2734
|
-
}
|
|
2735
|
-
async function searchMemory() {
|
|
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
|
+
}
|
|
3365
|
+
async function searchMemory() {
|
|
2736
3366
|
const r = await api('/api/v6/memory-browser', {
|
|
2737
3367
|
query: document.getElementById('mem-query').value,
|
|
2738
3368
|
agent: document.getElementById('mem-agent').value,
|
|
@@ -2746,197 +3376,352 @@ async function searchMemory() {
|
|
|
2746
3376
|
}
|
|
2747
3377
|
|
|
2748
3378
|
// ──── Activity ─────────────────────────────────────────────────────────
|
|
2749
|
-
// Live Brain
|
|
2750
|
-
// Memory Seeder
|
|
2751
|
-
function seedProjectPath() {
|
|
2752
|
-
return document.getElementById('seed-project-path')?.value?.trim()
|
|
2753
|
-
|| document.getElementById('live-project-path')?.value?.trim()
|
|
2754
|
-
|| document.getElementById('ww2-project-path')?.value?.trim()
|
|
2755
|
-
|| '';
|
|
2756
|
-
}
|
|
2757
|
-
function copyLivePathToSeeder() {
|
|
2758
|
-
const value = document.getElementById('live-project-path')?.value?.trim()
|
|
2759
|
-
|| document.getElementById('ww2-project-path')?.value?.trim()
|
|
2760
|
-
|| '';
|
|
2761
|
-
if (value) document.getElementById('seed-project-path').value = value;
|
|
2762
|
-
}
|
|
2763
|
-
function openSeederForLiveBrain() {
|
|
2764
|
-
const projectDir = liveProjectPath() || document.getElementById('ww2-project-path')?.value?.trim() || '';
|
|
2765
|
-
switchTab('seeder', 'Memory Seeder');
|
|
2766
|
-
if (projectDir) document.getElementById('seed-project-path').value = projectDir;
|
|
2767
|
-
const topic = document.getElementById('seed-topic');
|
|
2768
|
-
const objective = document.getElementById('seed-objective');
|
|
2769
|
-
if (topic && !topic.value) topic.value = 'Source-backed live brain improvement';
|
|
2770
|
-
if (objective && !objective.value) objective.value = 'Capture verified project knowledge that future agents should recall before complex work.';
|
|
2771
|
-
refreshSeedDrafts();
|
|
2772
|
-
}
|
|
2773
|
-
function seedDraftStatusClass(status) {
|
|
2774
|
-
if (status === 'seeded') return 'green';
|
|
2775
|
-
if (status === 'approved') return 'cyan';
|
|
2776
|
-
if (status === 'rejected') return 'red';
|
|
2777
|
-
return 'yellow';
|
|
2778
|
-
}
|
|
2779
|
-
function renderSeedStatus(drafts) {
|
|
2780
|
-
const counts = { draft: 0, approved: 0, seeded: 0, rejected: 0 };
|
|
2781
|
-
for (const d of drafts || []) counts[d.status] = (counts[d.status] || 0) + 1;
|
|
2782
|
-
return [
|
|
2783
|
-
'<div class="row"><span class="k">Open drafts</span><span class="v warn">' + counts.draft + '</span></div>',
|
|
2784
|
-
'<div class="row"><span class="k">Approved</span><span class="v good">' + counts.approved + '</span></div>',
|
|
2785
|
-
'<div class="row"><span class="k">Seeded</span><span class="v good">' + counts.seeded + '</span></div>',
|
|
2786
|
-
'<div class="row"><span class="k">Rejected</span><span class="v bad">' + counts.rejected + '</span></div>',
|
|
2787
|
-
'<div class="row"><span class="k">Current project</span><span class="v mono">' + htmlEscape(seedProjectPath() || 'dashboard default') + '</span></div>',
|
|
2788
|
-
].join('');
|
|
2789
|
-
}
|
|
2790
|
-
function seedJsArg(value) {
|
|
2791
|
-
return JSON.stringify(String(value || ''));
|
|
2792
|
-
}
|
|
2793
|
-
function renderSeedDrafts(drafts) {
|
|
2794
|
-
if (!drafts || !drafts.length) return '<div class="empty-tight">No seed drafts yet.</div>';
|
|
2795
|
-
return drafts.map(d => {
|
|
2796
|
-
const approvedBy = new Set((d.approvals || []).map(a => a.by));
|
|
2797
|
-
const required = d.requiredApprovals || [];
|
|
2798
|
-
const pending = required.filter(a => !approvedBy.has(a));
|
|
2799
|
-
const agentApprover = d.approverAgent && d.approverAgent !== 'user' ? d.approverAgent : d.researchAgent;
|
|
2800
|
-
const warnings = (d.warnings || []).length
|
|
2801
|
-
? '<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>'
|
|
2802
|
-
: '';
|
|
2803
|
-
const sources = (d.sources || []).slice(0, 5).map(s => '<code>' + htmlEscape(s) + '</code>').join(' ');
|
|
2804
|
-
let actions = '';
|
|
2805
|
-
if (d.status === 'draft') {
|
|
2806
|
-
actions += '<button class="btn sm success" onclick="approveSeedDraft(' + seedJsArg(d.id) + ',' + seedJsArg('user') + ')">User Approve</button> ';
|
|
2807
|
-
actions += '<button class="btn sm" onclick="approveSeedDraft(' + seedJsArg(d.id) + ',' + seedJsArg(agentApprover) + ')">Agent Approve</button> ';
|
|
2808
|
-
actions += '<button class="btn sm danger" onclick="rejectSeedDraft(' + seedJsArg(d.id) + ')">Reject</button>';
|
|
2809
|
-
}
|
|
2810
|
-
if (d.status === 'approved') {
|
|
2811
|
-
actions += '<button class="btn sm primary" onclick="commitSeedDraft(' + seedJsArg(d.id) + ')">Seed to Brain</button> ';
|
|
2812
|
-
actions += '<button class="btn sm danger" onclick="rejectSeedDraft(' + seedJsArg(d.id) + ')">Reject</button>';
|
|
2813
|
-
}
|
|
2814
|
-
if (d.status === 'seeded') {
|
|
2815
|
-
actions += '<span class="pill green">memory ' + htmlEscape((d.seededMemoryId || '').slice(0, 10)) + '</span>';
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
'<span class="pill
|
|
2821
|
-
'<span class="pill
|
|
2822
|
-
'<span>' + htmlEscape(
|
|
2823
|
-
|
|
2824
|
-
'
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
'<div
|
|
2828
|
-
'<div class="
|
|
2829
|
-
'<div style="font-size:11px;color:var(--text-3);margin-top:6px">' + (
|
|
2830
|
-
|
|
2831
|
-
'
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
}
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
const
|
|
2843
|
-
const
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
}
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
if (draft)
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
}
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
document.getElementById('
|
|
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
|
+
}
|
|
3720
|
+
async function refreshActivity() {
|
|
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();
|
|
3724
|
+
}
|
|
2940
3725
|
|
|
2941
3726
|
// ──── Live Graph ───────────────────────────────────────────────────────
|
|
2942
3727
|
let graphFrame = 0, graphNodes = [], graphEdges = [], graphSignals = [], graphTimer = null, graphPullTimer = null;
|
|
@@ -2980,64 +3765,449 @@ function graphLoop() {
|
|
|
2980
3765
|
const c = document.getElementById('graph-canvas');
|
|
2981
3766
|
if (!c) return;
|
|
2982
3767
|
const ctx = c.getContext('2d'), W = c.width, H = c.height;
|
|
3768
|
+
const now = Date.now();
|
|
2983
3769
|
ctx.clearRect(0, 0, W, H);
|
|
2984
|
-
|
|
2985
|
-
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)');
|
|
2986
3773
|
ctx.fillStyle = grd; ctx.fillRect(0, 0, W, H);
|
|
3774
|
+
|
|
2987
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; }
|
|
2988
|
-
|
|
3776
|
+
|
|
3777
|
+
// ── Edges: curved, with a gentle animated dash flow on active links ──────
|
|
2989
3778
|
for (const e of graphEdges) {
|
|
2990
3779
|
const a = graphNodes.find(n => n.id === e.from), b = graphNodes.find(n => n.id === e.to);
|
|
2991
3780
|
if (!a || !b) continue;
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
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();
|
|
2995
3794
|
}
|
|
2996
|
-
ctx.setLineDash([]);
|
|
2997
|
-
|
|
3795
|
+
ctx.setLineDash([]); ctx.lineDashOffset = 0;
|
|
3796
|
+
|
|
3797
|
+
// ── Signals: glowing head + fading motion trail ──────────────────────────
|
|
2998
3798
|
graphSignals = graphSignals.filter(s => now - s.spawnedAt < 3500);
|
|
2999
3799
|
for (const s of graphSignals) {
|
|
3000
3800
|
const from = graphNodes.find(n => n.id === s.fromId), to = graphNodes.find(n => n.id === s.toId);
|
|
3001
3801
|
if (!from || !to) continue;
|
|
3002
3802
|
s.progress = Math.min(1, (now - s.spawnedAt) / 2400);
|
|
3003
|
-
const t = s.progress
|
|
3004
|
-
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);
|
|
3005
3817
|
tail.addColorStop(0, s.color); tail.addColorStop(1, 'rgba(0,0,0,0)');
|
|
3006
|
-
ctx.fillStyle = tail; ctx.beginPath(); ctx.arc(
|
|
3007
|
-
ctx.fillStyle =
|
|
3008
|
-
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();
|
|
3009
3820
|
}
|
|
3821
|
+
|
|
3822
|
+
// ── Nodes: layered glow, ring, core, label chip ──────────────────────────
|
|
3010
3823
|
for (const n of graphNodes) {
|
|
3011
|
-
const
|
|
3012
|
-
const
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
ctx.
|
|
3017
|
-
|
|
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
|
|
3018
3844
|
ctx.fillStyle = n.color;
|
|
3019
|
-
ctx.font =
|
|
3845
|
+
ctx.font = isBrain ? '800 13px Inter, sans-serif' : '600 11px Inter, sans-serif';
|
|
3020
3846
|
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
|
3021
|
-
ctx.fillText(n.label, n.x, n.y
|
|
3847
|
+
ctx.fillText(n.label, n.x, isBrain ? n.y : n.y + r + 13);
|
|
3022
3848
|
}
|
|
3023
3849
|
graphFrame++;
|
|
3024
3850
|
graphTimer = requestAnimationFrame(graphLoop);
|
|
3025
3851
|
}
|
|
3026
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
|
+
|
|
3027
4203
|
// ──── Command Palette ──────────────────────────────────────────────────
|
|
3028
4204
|
const commands = [
|
|
3029
|
-
...FEATURES.map(([id, name]) => ({ type: 'nav', label: name, target: id, hint: 'Go to' })),
|
|
3030
|
-
{ type: '
|
|
3031
|
-
{ type: '
|
|
3032
|
-
{ type: '
|
|
3033
|
-
{ type: '
|
|
3034
|
-
{ type: '
|
|
3035
|
-
{ type: 'action', label: 'Run Dream Cycle', fn: 'runDream' },
|
|
3036
|
-
{ type: 'action', label: 'Run Consolidation', fn: 'runForget' },
|
|
3037
|
-
{ type: 'action', label: 'Toggle Air-Gap', fn: 'toggleAirGap' },
|
|
3038
|
-
{ type: 'action', label: 'Toggle Theme', fn: 'toggleTheme' },
|
|
3039
|
-
{ type: 'action', label: 'Attach All Agents', fn: 'attachAll' },
|
|
3040
|
-
{ 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' },
|
|
3041
4211
|
];
|
|
3042
4212
|
let cmdSelected = 0;
|
|
3043
4213
|
function openCmdPalette() {
|
|
@@ -3103,21 +4273,36 @@ function morphBrainState(type) {
|
|
|
3103
4273
|
stateTimer = setTimeout(() => { el.textContent = 'learning · reflecting · charging'; }, 6000);
|
|
3104
4274
|
}
|
|
3105
4275
|
async function pullRecentActivity() {
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
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();
|
|
3113
4284
|
}
|
|
3114
4285
|
|
|
3115
4286
|
// ──── WebSocket ────────────────────────────────────────────────────────
|
|
3116
4287
|
function connectWS() {
|
|
3117
4288
|
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
3118
4289
|
ws = new WebSocket(\`\${proto}://\${location.host}\`);
|
|
3119
|
-
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'); };
|
|
3120
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
|
+
}
|
|
3121
4306
|
if (m.type === 'insights' || m.type === 'modules') refresh();
|
|
3122
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);
|
|
3123
4308
|
if (m.type === 'injection') appendLog(m.adapter || 'agent','injection', m.insightTitle || 'context injected');
|
|
@@ -3125,19 +4310,21 @@ function connectWS() {
|
|
|
3125
4310
|
if (m.type === 'error') { appendLog('brain','error', m.error); toast('Brain error', m.error, 'error'); }
|
|
3126
4311
|
if (m.type === 'agents-detected') appendLog('brain','agents', (m.agents || []).join(', '));
|
|
3127
4312
|
} catch {} };
|
|
3128
|
-
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); };
|
|
3129
4315
|
}
|
|
3130
4316
|
|
|
3131
4317
|
// ──── Bootstrapping ────────────────────────────────────────────────────
|
|
3132
4318
|
connectWS();
|
|
3133
4319
|
refresh();
|
|
3134
4320
|
pullRecentActivity();
|
|
3135
|
-
setInterval(refresh, 5000);
|
|
3136
|
-
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
|
|
3137
4324
|
if (window.lucide) try { window.lucide.createIcons(); } catch {}
|
|
3138
4325
|
|
|
3139
4326
|
// Welcome toast
|
|
3140
|
-
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);
|
|
3141
4328
|
</script>
|
|
3142
4329
|
</body>
|
|
3143
4330
|
</html>`;
|