@timmeck/brain-core 2.36.32 → 2.36.34
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/command-center.html +200 -4
- package/dist/codegen/feature-recommender.d.ts +103 -0
- package/dist/codegen/feature-recommender.js +428 -0
- package/dist/codegen/feature-recommender.js.map +1 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +1 -0
- package/dist/codegen/index.js.map +1 -1
- package/dist/cross-brain/client.d.ts +4 -1
- package/dist/cross-brain/client.js +23 -1
- package/dist/cross-brain/client.js.map +1 -1
- package/dist/dashboard/command-center-server.d.ts +2 -0
- package/dist/dashboard/command-center-server.js +14 -1
- package/dist/dashboard/command-center-server.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/research/research-orchestrator.d.ts +2 -0
- package/dist/research/research-orchestrator.js +22 -0
- package/dist/research/research-orchestrator.js.map +1 -1
- package/package.json +1 -1
package/command-center.html
CHANGED
|
@@ -231,6 +231,7 @@ canvas{display:block;width:100%;height:100%}
|
|
|
231
231
|
<div class="sidebar-brand">COMMAND CENTER<span>Brain Ecosystem</span></div>
|
|
232
232
|
<div class="nav-section">Views</div>
|
|
233
233
|
<div class="nav-item active" data-page="overview"><span class="nav-icon">🌍</span><span class="nav-label">Ecosystem</span></div>
|
|
234
|
+
<div class="nav-item" data-page="entity"><span class="nav-icon">👻</span><span class="nav-label" data-t="Bewusstsein">Bewusstsein</span></div>
|
|
234
235
|
<div class="nav-item" data-page="learning"><span class="nav-icon">🧠</span><span class="nav-label" data-t="Lern-Kreislauf">Lern-Kreislauf</span></div>
|
|
235
236
|
<div class="nav-item" data-page="trading"><span class="nav-icon">📈</span><span class="nav-label">Trading Flow</span></div>
|
|
236
237
|
<div class="nav-item" data-page="marketing"><span class="nav-icon">📣</span><span class="nav-label">Marketing Flow</span></div>
|
|
@@ -321,6 +322,18 @@ canvas{display:block;width:100%;height:100%}
|
|
|
321
322
|
</div>
|
|
322
323
|
</div>
|
|
323
324
|
|
|
325
|
+
<!-- ════ Page: Entity — Consciousness ═════════════════ -->
|
|
326
|
+
<div class="page" id="page-entity">
|
|
327
|
+
<div style="position:relative;height:calc(100vh - 160px);min-height:400px">
|
|
328
|
+
<canvas id="entityCanvas" style="position:absolute;top:0;left:0;width:100%;height:100%"></canvas>
|
|
329
|
+
<div style="position:absolute;bottom:20px;left:50%;transform:translateX(-50%);text-align:center;pointer-events:none">
|
|
330
|
+
<div id="entityMoodLabel" style="font-size:20px;font-weight:700;letter-spacing:2px;text-shadow:0 0 20px currentColor"></div>
|
|
331
|
+
<div id="entityScoreLabel" style="font-size:11px;color:var(--text-dim);margin-top:4px"></div>
|
|
332
|
+
</div>
|
|
333
|
+
<div id="entityDimChips" style="position:absolute;bottom:80px;left:50%;transform:translateX(-50%);display:flex;gap:8px;flex-wrap:wrap;justify-content:center;pointer-events:none"></div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
324
337
|
<!-- ════ Page 2: Lern-Kreislauf ═════════════════════ -->
|
|
325
338
|
<div class="page" id="page-learning">
|
|
326
339
|
<div class="section">
|
|
@@ -541,6 +554,23 @@ canvas{display:block;width:100%;height:100%}
|
|
|
541
554
|
</div>
|
|
542
555
|
<div id="userTopTools" style="margin-top:12px"></div>
|
|
543
556
|
</div>
|
|
557
|
+
|
|
558
|
+
<!-- Feature Recommender -->
|
|
559
|
+
<div class="section">
|
|
560
|
+
<div class="section-title"><span class="icon">⭐</span> Feature Recommender — <span data-t="Was Brain sich wünscht">Was Brain sich wünscht</span></div>
|
|
561
|
+
<p style="font-size:12px;color:var(--text-dim);margin-bottom:12px" data-t="Brain erkennt was ihm fehlt und sucht nach passenden Features aus absorbierten Repos.">Brain erkennt was ihm fehlt und sucht nach passenden Features aus absorbierten Repos.</p>
|
|
562
|
+
<div class="grid grid-4">
|
|
563
|
+
<div class="card" style="text-align:center"><div class="card-value" id="int-rec-total" style="color:var(--cyan)">0</div><div class="card-sub" data-t="Wünsche">Wünsche</div></div>
|
|
564
|
+
<div class="card" style="text-align:center"><div class="card-value" id="int-rec-open" style="color:var(--yellow)">0</div><div class="card-sub" data-t="Offen">Offen</div></div>
|
|
565
|
+
<div class="card" style="text-align:center"><div class="card-value" id="int-rec-matched" style="color:var(--green)">0</div><div class="card-sub" data-t="Gefunden">Gefunden</div></div>
|
|
566
|
+
<div class="card" style="text-align:center"><div class="card-value" id="int-rec-adopted" style="color:var(--purple)">0</div><div class="card-sub" data-t="Übernommen">Übernommen</div></div>
|
|
567
|
+
</div>
|
|
568
|
+
<div class="grid grid-2" style="margin-top:8px">
|
|
569
|
+
<div class="card" style="text-align:center"><div class="card-value" id="int-rec-connections" style="color:var(--text-dim)">0</div><div class="card-sub" data-t="Verbindungen">Verbindungen</div></div>
|
|
570
|
+
<div class="card" style="text-align:center"><div class="card-value" id="int-rec-scan" style="color:var(--text-dim);font-size:14px">—</div><div class="card-sub" data-t="Letzter Scan">Letzter Scan</div></div>
|
|
571
|
+
</div>
|
|
572
|
+
<div id="featureWishlist" style="margin-top:12px"></div>
|
|
573
|
+
</div>
|
|
544
574
|
</div>
|
|
545
575
|
|
|
546
576
|
<!-- ════ Page 6: Cross-Brain & Borg ═════════════════ -->
|
|
@@ -691,7 +721,8 @@ canvas{display:block;width:100%;height:100%}
|
|
|
691
721
|
const state = {
|
|
692
722
|
ecosystem:null, engines:[], watchdog:[], plugins:[], borg:null, analytics:null,
|
|
693
723
|
llm:null, thoughts:[], connected:false, lastThoughtTime:0,
|
|
694
|
-
errors:null, selfmod:null, missions:null, knowledge:null, debates:null
|
|
724
|
+
errors:null, selfmod:null, missions:null, knowledge:null, debates:null,
|
|
725
|
+
emotional:null, attention:null
|
|
695
726
|
};
|
|
696
727
|
|
|
697
728
|
// ── i18n ──────────────────────────────────────────────────
|
|
@@ -784,7 +815,11 @@ const translations = { en: {
|
|
|
784
815
|
'Gesamt-Neustarts':'Total Restarts','Längste Laufzeit':'Longest Uptime','Dienste':'Services',
|
|
785
816
|
'Keine Plugins installiert':'No plugins installed','Geladen':'Loaded','Keine Engines':'No engines',
|
|
786
817
|
'Strategien':'Strategies','BORG AKTIV':'BORG ACTIVE','gesendet':'sent','empfangen':'received',
|
|
787
|
-
'Der Lern-Kreislauf':'The Learning Cycle','Aktivität & Missionen':'Activity & Missions',
|
|
818
|
+
'Der Lern-Kreislauf':'The Learning Cycle','Aktivität & Missionen':'Activity & Missions','Bewusstsein':'Consciousness',
|
|
819
|
+
'Was Brain sich wünscht':'What Brain wants','Wünsche':'Wishes','Offen':'Open','Gefunden':'Found','Übernommen':'Adopted',
|
|
820
|
+
'Verbindungen':'Connections','Letzter Scan':'Last scan','Suche läuft':'Searching','Verworfen':'Dismissed','Priorität':'Priority',
|
|
821
|
+
'Noch keine Feature-Wünsche — Brain analysiert automatisch':'No feature wishes yet — Brain analyzes automatically',
|
|
822
|
+
'Brain erkennt was ihm fehlt und sucht nach passenden Features aus absorbierten Repos.':'Brain detects what it needs and searches for matching features from absorbed repos.',
|
|
788
823
|
}};
|
|
789
824
|
let currentLang = localStorage.getItem('brain-lang') || 'en';
|
|
790
825
|
function t(text) { if (currentLang === 'en' && translations.en[text]) return translations.en[text]; return text; }
|
|
@@ -810,13 +845,14 @@ function getLocale() { return currentLang === 'de' ? 'de-DE' : 'en-US'; }
|
|
|
810
845
|
// ── Navigation ────────────────────────────────────────────
|
|
811
846
|
function getTitle(page) {
|
|
812
847
|
const titles = {
|
|
813
|
-
overview:'Ecosystem Overview', learning:t('Der Lern-Kreislauf'), trading:'Trading Flow',
|
|
848
|
+
overview:'Ecosystem Overview', entity:t('Bewusstsein'), learning:t('Der Lern-Kreislauf'), trading:'Trading Flow',
|
|
814
849
|
marketing:'Marketing Flow', intelligence:t('Intelligenz'), crossbrain:'Cross-Brain & Borg', activity:t('Aktivität & Missionen'),
|
|
815
850
|
debates:'Debates & Challenges', infra:t('Infrastruktur')
|
|
816
851
|
}; return titles[page] || '';
|
|
817
852
|
}
|
|
818
853
|
const titles = {
|
|
819
854
|
overview:'Ecosystem Overview',
|
|
855
|
+
entity:'Bewusstsein',
|
|
820
856
|
learning:'Der Lern-Kreislauf',
|
|
821
857
|
trading:'Trading Flow',
|
|
822
858
|
marketing:'Marketing Flow',
|
|
@@ -854,6 +890,7 @@ function connectSSE() {
|
|
|
854
890
|
es.addEventListener('intelligence', e => { state.intelligence = JSON.parse(e.data); renderIntelligence(); });
|
|
855
891
|
es.addEventListener('knowledge', e => { state.knowledge = JSON.parse(e.data); renderKnowledge(); });
|
|
856
892
|
es.addEventListener('debates', e => { state.debates = JSON.parse(e.data); renderDebates(); });
|
|
893
|
+
es.addEventListener('emotional', e => { state.emotional = JSON.parse(e.data); });
|
|
857
894
|
es.onerror = () => { state.connected = false; updateConnection(); };
|
|
858
895
|
}
|
|
859
896
|
|
|
@@ -873,6 +910,7 @@ async function loadInitial() {
|
|
|
873
910
|
state.errors = data.errors; state.selfmod = data.selfmod;
|
|
874
911
|
state.missions = data.missions; state.knowledge = data.knowledge; state.repoAbsorber = data.repoAbsorber; state.intelligence = data.intelligence;
|
|
875
912
|
state.debates = data.debates;
|
|
913
|
+
state.emotional = data.emotional;
|
|
876
914
|
if (data.thoughts) { state.thoughts = data.thoughts; renderThoughts(); }
|
|
877
915
|
renderEcosystem(); renderEngines(); renderWatchdog(); renderPlugins();
|
|
878
916
|
renderBorg(); renderAnalytics(); renderLLM(); renderErrors();
|
|
@@ -1175,6 +1213,39 @@ function renderIntelligence() {
|
|
|
1175
1213
|
tools.map((t,i) => `<span class="badge badge-ok" style="margin:2px">${i+1}. ${escHtml(t)}</span>`).join('');
|
|
1176
1214
|
}
|
|
1177
1215
|
}
|
|
1216
|
+
|
|
1217
|
+
// Feature Recommender
|
|
1218
|
+
if (d.recommender) {
|
|
1219
|
+
const r = d.recommender;
|
|
1220
|
+
setText('int-rec-total', r.totalWishes || 0);
|
|
1221
|
+
setText('int-rec-open', r.openWishes || 0);
|
|
1222
|
+
setText('int-rec-matched', r.matchedWishes || 0);
|
|
1223
|
+
setText('int-rec-adopted', r.adoptedWishes || 0);
|
|
1224
|
+
setText('int-rec-connections', r.totalConnections || 0);
|
|
1225
|
+
setText('int-rec-scan', r.lastScanAt ? new Date(r.lastScanAt).toLocaleString(getLocale(), {day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}) : '—');
|
|
1226
|
+
|
|
1227
|
+
const wishEl = document.getElementById('featureWishlist');
|
|
1228
|
+
const wishes = r.wishlist || [];
|
|
1229
|
+
if (wishes.length) {
|
|
1230
|
+
wishEl.innerHTML = wishes.slice(0, 10).map(w => {
|
|
1231
|
+
const statusColor = w.status === 'matched' ? 'var(--green)' : w.status === 'adopted' ? 'var(--purple)' : w.status === 'dismissed' ? 'var(--text-dim)' : 'var(--yellow)';
|
|
1232
|
+
const statusLabel = w.status === 'matched' ? '✅ ' + escHtml(w.matchedFeatureName || 'Match') : w.status === 'adopted' ? '📦 ' + t('Übernommen') : w.status === 'dismissed' ? '❌ ' + t('Verworfen') : '🔎 ' + t('Suche läuft');
|
|
1233
|
+
const prio = (w.priority * 100).toFixed(0);
|
|
1234
|
+
return `<div class="card" style="padding:8px 12px;margin-bottom:4px">
|
|
1235
|
+
<div style="display:flex;justify-content:space-between;align-items:center">
|
|
1236
|
+
<span style="font-weight:600;color:var(--cyan)">${escHtml(w.need)}</span>
|
|
1237
|
+
<span style="font-size:11px;color:${statusColor}">${statusLabel}</span>
|
|
1238
|
+
</div>
|
|
1239
|
+
<div style="display:flex;justify-content:space-between;margin-top:4px;font-size:11px;color:var(--text-dim)">
|
|
1240
|
+
<span>${escHtml(w.reason || '')}</span>
|
|
1241
|
+
<span>${t('Priorität')}: ${prio}%</span>
|
|
1242
|
+
</div>
|
|
1243
|
+
</div>`;
|
|
1244
|
+
}).join('');
|
|
1245
|
+
} else {
|
|
1246
|
+
wishEl.innerHTML = `<div class="empty">${t('Noch keine Feature-Wünsche — Brain analysiert automatisch')}</div>`;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1178
1249
|
}
|
|
1179
1250
|
|
|
1180
1251
|
// ── Knowledge Growth ──────────────────────────────────────
|
|
@@ -1621,10 +1692,135 @@ function fmtUp(s) { if(s<60) return Math.round(s)+'s'; if(s<3600) return Math.ro
|
|
|
1621
1692
|
function formatK(n) { if(n>=1e6) return (n/1e6).toFixed(1)+'M'; if(n>=1e3) return (n/1e3).toFixed(1)+'K'; return String(n); }
|
|
1622
1693
|
function escHtml(s) { const d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
|
|
1623
1694
|
|
|
1695
|
+
// ═══ THE ENTITY — CONSCIOUSNESS VISUALIZATION ═══
|
|
1696
|
+
var MOOD_COLORS = {
|
|
1697
|
+
flow:{r:0,g:229,b:255,name:'Flow'},excited:{r:255,g:215,b:0,name:'Excited'},
|
|
1698
|
+
anxious:{r:255,g:61,b:61,name:'Anxious'},reflective:{r:179,g:136,b:255,name:'Reflective'},
|
|
1699
|
+
bored:{r:110,g:122,b:138,name:'Bored'},determined:{r:224,g:230,b:240,name:'Determined'}
|
|
1700
|
+
};
|
|
1701
|
+
var entColor={r:179,g:136,b:255}, entTarget={r:179,g:136,b:255};
|
|
1702
|
+
var entParticles=[], entTentacles=[], entFloating=[];
|
|
1703
|
+
for(var ti=0;ti<10;ti++) entTentacles.push({angle:ti/10*Math.PI*2,phase:Math.random()*Math.PI*2,speed:0.3+Math.random()*0.4,lenMul:0.8+Math.random()*0.4,widMul:0.7+Math.random()*0.6});
|
|
1704
|
+
for(var pi=0;pi<80;pi++) entParticles.push({angle:Math.random()*Math.PI*2,dist:0.5+Math.random()*2.5,speed:0.1+Math.random()*0.3,size:0.5+Math.random()*1.5,phase:Math.random()*Math.PI*2,brightness:0.2+Math.random()*0.5});
|
|
1705
|
+
|
|
1706
|
+
function entLerp(a,b,t){return{r:a.r+(b.r-a.r)*t,g:a.g+(b.g-a.g)*t,b:a.b+(b.b-a.b)*t}}
|
|
1707
|
+
function entRgba(c,a){return'rgba('+Math.round(c.r)+','+Math.round(c.g)+','+Math.round(c.b)+','+a+')'}
|
|
1708
|
+
|
|
1709
|
+
function drawEntityCanvas(){
|
|
1710
|
+
var canvas=document.getElementById('entityCanvas');
|
|
1711
|
+
if(!canvas||canvas.offsetParent===null) return;
|
|
1712
|
+
var wrap=canvas.parentElement,dpr=window.devicePixelRatio||1;
|
|
1713
|
+
var ww=wrap.clientWidth,wh=wrap.clientHeight;
|
|
1714
|
+
if(ww<20||wh<20) return;
|
|
1715
|
+
if(Math.abs(canvas.width-ww*dpr)>2){canvas.width=ww*dpr;canvas.height=wh*dpr;canvas.style.width=ww+'px';canvas.style.height=wh+'px'}
|
|
1716
|
+
var ctx=canvas.getContext('2d');ctx.setTransform(dpr,0,0,dpr,0,0);
|
|
1717
|
+
var w=ww,h=wh,cx=w/2,cy=h/2,time=Date.now()/1000;
|
|
1718
|
+
var em=state.emotional||{},dim=em.dimensions||{};
|
|
1719
|
+
var mc=MOOD_COLORS[em.mood]||MOOD_COLORS.reflective;
|
|
1720
|
+
entTarget={r:mc.r,g:mc.g,b:mc.b};entColor=entLerp(entColor,entTarget,0.03);
|
|
1721
|
+
ctx.clearRect(0,0,w,h);
|
|
1722
|
+
// Breathing
|
|
1723
|
+
var breathSpeed=0.4+(dim.stress||0)*1.2,breathAmp=0.06+(dim.curiosity||0)*0.04;
|
|
1724
|
+
var breath=1+Math.sin(time*breathSpeed)*breathAmp;
|
|
1725
|
+
var minDim=Math.min(w,h),baseR=minDim*0.13;
|
|
1726
|
+
var activity=Math.min(1,(state.thoughts.length||0)/80);
|
|
1727
|
+
var entityR=baseR*breath*(0.85+activity*0.15);
|
|
1728
|
+
// Nebula
|
|
1729
|
+
var neb=ctx.createRadialGradient(cx,cy,entityR*0.5,cx,cy,minDim*0.45);
|
|
1730
|
+
neb.addColorStop(0,entRgba(entColor,0.04));neb.addColorStop(0.4,entRgba(entColor,0.015));neb.addColorStop(1,'rgba(5,8,16,0)');
|
|
1731
|
+
ctx.fillStyle=neb;ctx.fillRect(0,0,w,h);
|
|
1732
|
+
// Tentacles
|
|
1733
|
+
var curiosity=dim.curiosity||0.5,creativity=dim.creativity||0.5,stress=dim.stress||0;
|
|
1734
|
+
var tentLen=entityR*(1.5+curiosity*2+creativity*0.8),tentAlpha=0.12+(1-stress)*0.15;
|
|
1735
|
+
for(var t=0;t<entTentacles.length;t++){
|
|
1736
|
+
var tn=entTentacles[t],ba=tn.angle+Math.sin(time*tn.speed+tn.phase)*0.3,len=tentLen*tn.lenMul,wid=(3+creativity*4)*tn.widMul;
|
|
1737
|
+
var sx=cx+Math.cos(ba)*entityR*0.9,sy=cy+Math.sin(ba)*entityR*0.9;
|
|
1738
|
+
var w1=Math.sin(time*0.7+tn.phase*2)*0.4,w2=Math.cos(time*0.5+tn.phase*3)*0.5;
|
|
1739
|
+
var cp1x=cx+Math.cos(ba+w1)*(entityR+len*0.35),cp1y=cy+Math.sin(ba+w1)*(entityR+len*0.35);
|
|
1740
|
+
var cp2x=cx+Math.cos(ba+w2)*(entityR+len*0.7),cp2y=cy+Math.sin(ba+w2)*(entityR+len*0.7);
|
|
1741
|
+
var ex=cx+Math.cos(ba+w1*0.5+w2*0.3)*(entityR+len),ey=cy+Math.sin(ba+w1*0.5+w2*0.3)*(entityR+len);
|
|
1742
|
+
ctx.beginPath();ctx.moveTo(sx,sy);ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,ex,ey);
|
|
1743
|
+
var grad=ctx.createLinearGradient(sx,sy,ex,ey);grad.addColorStop(0,entRgba(entColor,tentAlpha));grad.addColorStop(0.6,entRgba(entColor,tentAlpha*0.4));grad.addColorStop(1,entRgba(entColor,0));
|
|
1744
|
+
ctx.strokeStyle=grad;ctx.lineWidth=wid;ctx.lineCap='round';ctx.stroke();
|
|
1745
|
+
ctx.beginPath();ctx.moveTo(sx,sy);ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,ex,ey);
|
|
1746
|
+
ctx.strokeStyle=entRgba({r:255,g:255,b:255},tentAlpha*0.3);ctx.lineWidth=1;ctx.stroke();
|
|
1747
|
+
}
|
|
1748
|
+
// Dimension Ring
|
|
1749
|
+
var ringR=entityR*2.8;
|
|
1750
|
+
var dNames=['frustration','curiosity','surprise','confidence','satisfaction','stress','momentum','creativity'];
|
|
1751
|
+
var dCols=['#ff3d3d','#40c4ff','#ffab40','#00ff88','#69f0ae','#ff6e40','#b2ff59','#ea80fc'];
|
|
1752
|
+
var arcL=(Math.PI*2)/dNames.length,gap=0.08;
|
|
1753
|
+
for(var di=0;di<dNames.length;di++){
|
|
1754
|
+
var dv=dim[dNames[di]]||0,startA=di*arcL-Math.PI/2+gap/2,endA=startA+arcL-gap;
|
|
1755
|
+
ctx.beginPath();ctx.arc(cx,cy,ringR,startA,endA);
|
|
1756
|
+
ctx.strokeStyle=dCols[di]+Math.round((0.08+dv*0.25)*255).toString(16).padStart(2,'0');
|
|
1757
|
+
ctx.lineWidth=2+dv*4;ctx.lineCap='round';ctx.stroke();
|
|
1758
|
+
if(dv>0.3){var la=(startA+endA)/2,lr=ringR+14;
|
|
1759
|
+
ctx.fillStyle=dCols[di]+'80';ctx.font='8px Segoe UI,sans-serif';ctx.textAlign='center';ctx.textBaseline='middle';
|
|
1760
|
+
ctx.fillText(dNames[di].substring(0,4).toUpperCase(),cx+Math.cos(la)*lr,cy+Math.sin(la)*lr);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
// Particles
|
|
1764
|
+
var momentum=dim.momentum||0;
|
|
1765
|
+
for(var p=0;p<entParticles.length;p++){
|
|
1766
|
+
var pt=entParticles[p];pt.angle+=(pt.speed*(0.5+momentum))*0.016;
|
|
1767
|
+
var pd=entityR*pt.dist+Math.sin(time*0.5+pt.phase)*entityR*0.2;
|
|
1768
|
+
var px=cx+Math.cos(pt.angle)*pd,py=cy+Math.sin(pt.angle)*pd;
|
|
1769
|
+
var pa=pt.brightness*(0.3+Math.sin(time*0.8+pt.phase)*0.2);
|
|
1770
|
+
ctx.beginPath();ctx.arc(px,py,pt.size,0,Math.PI*2);ctx.fillStyle=entRgba(entColor,pa);ctx.fill();
|
|
1771
|
+
}
|
|
1772
|
+
// Outer Glow
|
|
1773
|
+
var glowR=entityR*3.5,og=ctx.createRadialGradient(cx,cy,entityR*0.5,cx,cy,glowR);
|
|
1774
|
+
og.addColorStop(0,entRgba(entColor,0.35));og.addColorStop(0.2,entRgba(entColor,0.15));og.addColorStop(0.5,entRgba(entColor,0.05));og.addColorStop(1,entRgba(entColor,0));
|
|
1775
|
+
ctx.beginPath();ctx.arc(cx,cy,glowR,0,Math.PI*2);ctx.fillStyle=og;ctx.fill();
|
|
1776
|
+
// Main Orb
|
|
1777
|
+
var orbG=ctx.createRadialGradient(cx-entityR*0.25,cy-entityR*0.25,entityR*0.05,cx,cy,entityR);
|
|
1778
|
+
orbG.addColorStop(0,'rgba(255,255,255,0.95)');
|
|
1779
|
+
orbG.addColorStop(0.1,entRgba({r:Math.min(255,entColor.r+100),g:Math.min(255,entColor.g+100),b:Math.min(255,entColor.b+100)},0.9));
|
|
1780
|
+
orbG.addColorStop(0.4,entRgba(entColor,0.85));orbG.addColorStop(0.7,entRgba(entColor,0.6));orbG.addColorStop(1,entRgba(entColor,0.2));
|
|
1781
|
+
ctx.beginPath();ctx.arc(cx,cy,entityR,0,Math.PI*2);ctx.fillStyle=orbG;ctx.fill();
|
|
1782
|
+
ctx.beginPath();ctx.arc(cx,cy,entityR,0,Math.PI*2);ctx.strokeStyle=entRgba(entColor,0.3);ctx.lineWidth=1;ctx.stroke();
|
|
1783
|
+
// Inner Eye
|
|
1784
|
+
var eyeR=entityR*0.2;
|
|
1785
|
+
var eyeG=ctx.createRadialGradient(cx,cy,0,cx,cy,eyeR);
|
|
1786
|
+
eyeG.addColorStop(0,'rgba(255,255,255,0.95)');eyeG.addColorStop(0.4,'rgba(255,255,255,0.5)');eyeG.addColorStop(1,'rgba(255,255,255,0)');
|
|
1787
|
+
ctx.beginPath();ctx.arc(cx,cy,eyeR,0,Math.PI*2);ctx.fillStyle=eyeG;ctx.fill();
|
|
1788
|
+
// Floating thoughts
|
|
1789
|
+
var now=Date.now();
|
|
1790
|
+
for(var fi=entFloating.length-1;fi>=0;fi--){
|
|
1791
|
+
var ft=entFloating[fi],age=now-ft.born;
|
|
1792
|
+
if(age>ft.life){entFloating.splice(fi,1);continue}
|
|
1793
|
+
ft.dist+=ft.speed*0.016;var prog=age/ft.life;
|
|
1794
|
+
ft.alpha=prog<0.15?prog/0.15:prog>0.7?(1-prog)/0.3:1;ft.alpha*=0.7;
|
|
1795
|
+
var ftx=cx+Math.cos(ft.angle)*entityR*(1.5+ft.dist*2),fty=cy+Math.sin(ft.angle)*entityR*(1.5+ft.dist*2);
|
|
1796
|
+
ctx.fillStyle='rgba(200,200,255,'+ft.alpha*0.7+')';ctx.font='9px Segoe UI,sans-serif';ctx.textAlign='center';ctx.textBaseline='middle';
|
|
1797
|
+
ctx.fillText(ft.text,ftx,fty);
|
|
1798
|
+
}
|
|
1799
|
+
// Labels
|
|
1800
|
+
var moodInfo=MOOD_COLORS[em.mood]||MOOD_COLORS.reflective;
|
|
1801
|
+
var moodEl=document.getElementById('entityMoodLabel'),scoreEl=document.getElementById('entityScoreLabel');
|
|
1802
|
+
if(moodEl){moodEl.textContent=moodInfo.name;moodEl.style.color=entRgba(entColor,1)}
|
|
1803
|
+
if(scoreEl) scoreEl.textContent='Score: '+Math.round((em.score||0.5)*100)+'% | Valence: '+(em.valence||0).toFixed(2)+' | Arousal: '+(em.arousal||0).toFixed(2);
|
|
1804
|
+
// Dimension chips
|
|
1805
|
+
var chipEl=document.getElementById('entityDimChips');
|
|
1806
|
+
if(chipEl&&chipEl.childElementCount===0){
|
|
1807
|
+
var ch='';for(var ci=0;ci<dNames.length;ci++){
|
|
1808
|
+
ch+='<div style="background:rgba(10,14,26,0.7);border:1px solid '+dCols[ci]+'30;border-radius:6px;padding:4px 10px;font-size:9px;display:flex;gap:6px;align-items:center">'+
|
|
1809
|
+
'<span style="color:'+dCols[ci]+';text-transform:uppercase;letter-spacing:1px">'+dNames[ci].substring(0,5)+'</span>'+
|
|
1810
|
+
'<span id="entdim-'+dNames[ci]+'" style="color:var(--text-dim)">0%</span></div>';
|
|
1811
|
+
}chipEl.innerHTML=ch;
|
|
1812
|
+
}
|
|
1813
|
+
for(var ui=0;ui<dNames.length;ui++){var dvel=document.getElementById('entdim-'+dNames[ui]);if(dvel)dvel.textContent=Math.round((dim[dNames[ui]]||0)*100)+'%'}
|
|
1814
|
+
}
|
|
1815
|
+
function entityLoop(){drawEntityCanvas();requestAnimationFrame(entityLoop)}
|
|
1816
|
+
// Spawn floating thought on notable thoughts
|
|
1817
|
+
var origAddThought=addThought;
|
|
1818
|
+
addThought=function(t){origAddThought(t);if(t.significance==='notable'||t.significance==='breakthrough'){if(entFloating.length>=12)entFloating.shift();entFloating.push({text:(t.content||'').substring(0,40),angle:Math.random()*Math.PI*2,dist:0.2,speed:0.15+Math.random()*0.1,alpha:1,born:Date.now(),life:6000+Math.random()*4000})}};
|
|
1819
|
+
|
|
1624
1820
|
// ── Init ──────────────────────────────────────────────────
|
|
1625
1821
|
document.getElementById('langToggle').textContent = currentLang.toUpperCase();
|
|
1626
1822
|
updateStaticTexts();
|
|
1627
|
-
connectSSE(); loadInitial(); initCrossBrainCanvas();
|
|
1823
|
+
connectSSE(); loadInitial(); initCrossBrainCanvas(); entityLoop();
|
|
1628
1824
|
window.addEventListener('resize', () => { if(state.ecosystem?.brains) drawPeerGraph(state.ecosystem.brains); });
|
|
1629
1825
|
</script>
|
|
1630
1826
|
</body>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { FeatureExtractor } from './feature-extractor.js';
|
|
3
|
+
import type { RAGEngine } from '../rag/rag-engine.js';
|
|
4
|
+
import type { KnowledgeGraphEngine } from '../knowledge-graph/graph-engine.js';
|
|
5
|
+
import type { ThoughtStream } from '../consciousness/thought-stream.js';
|
|
6
|
+
export interface FeatureWish {
|
|
7
|
+
id: number;
|
|
8
|
+
need: string;
|
|
9
|
+
reason: string;
|
|
10
|
+
priority: number;
|
|
11
|
+
matchedFeatureId: number | null;
|
|
12
|
+
matchedFeatureName: string | null;
|
|
13
|
+
matchScore: number;
|
|
14
|
+
status: 'open' | 'matched' | 'adopted' | 'dismissed';
|
|
15
|
+
createdAt: string;
|
|
16
|
+
updatedAt: string;
|
|
17
|
+
}
|
|
18
|
+
export interface FeatureConnection {
|
|
19
|
+
id: number;
|
|
20
|
+
featureIdA: number;
|
|
21
|
+
featureIdB: number;
|
|
22
|
+
nameA: string;
|
|
23
|
+
nameB: string;
|
|
24
|
+
relationship: string;
|
|
25
|
+
strength: number;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RecommendationResult {
|
|
29
|
+
wishesCreated: number;
|
|
30
|
+
connectionsFound: number;
|
|
31
|
+
matchesFound: number;
|
|
32
|
+
durationMs: number;
|
|
33
|
+
}
|
|
34
|
+
export interface FeatureRecommenderStatus {
|
|
35
|
+
totalWishes: number;
|
|
36
|
+
openWishes: number;
|
|
37
|
+
matchedWishes: number;
|
|
38
|
+
adoptedWishes: number;
|
|
39
|
+
totalConnections: number;
|
|
40
|
+
lastScanAt: string | null;
|
|
41
|
+
}
|
|
42
|
+
export declare class FeatureRecommender {
|
|
43
|
+
private readonly db;
|
|
44
|
+
private readonly log;
|
|
45
|
+
private featureExtractor;
|
|
46
|
+
private ragEngine;
|
|
47
|
+
private knowledgeGraph;
|
|
48
|
+
private thoughtStream;
|
|
49
|
+
private lastScanAt;
|
|
50
|
+
constructor(db: Database.Database);
|
|
51
|
+
setFeatureExtractor(fe: FeatureExtractor): void;
|
|
52
|
+
setRAGEngine(rag: RAGEngine): void;
|
|
53
|
+
setKnowledgeGraph(kg: KnowledgeGraphEngine): void;
|
|
54
|
+
setThoughtStream(ts: ThoughtStream): void;
|
|
55
|
+
private ensureTables;
|
|
56
|
+
/**
|
|
57
|
+
* Full recommendation cycle: detect needs → match features → build connections.
|
|
58
|
+
* Called periodically by ResearchOrchestrator.
|
|
59
|
+
*/
|
|
60
|
+
runCycle(): Promise<RecommendationResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Detect needs by analyzing Brain's own data (errors, tool usage, knowledge gaps).
|
|
63
|
+
*/
|
|
64
|
+
private detectNeeds;
|
|
65
|
+
/**
|
|
66
|
+
* Match open wishes against extracted features.
|
|
67
|
+
*/
|
|
68
|
+
private matchWishesToFeatures;
|
|
69
|
+
/**
|
|
70
|
+
* Build connections between extracted features based on tags and co-occurrence.
|
|
71
|
+
*/
|
|
72
|
+
private buildConnections;
|
|
73
|
+
/**
|
|
74
|
+
* Calculate how well a feature matches a need.
|
|
75
|
+
*/
|
|
76
|
+
private calculateMatchScore;
|
|
77
|
+
/**
|
|
78
|
+
* Get the feature wishlist (what Brain wants).
|
|
79
|
+
*/
|
|
80
|
+
getWishlist(status?: string): FeatureWish[];
|
|
81
|
+
/**
|
|
82
|
+
* Get connections for a specific feature (what goes well with it).
|
|
83
|
+
*/
|
|
84
|
+
getConnections(featureId?: number): FeatureConnection[];
|
|
85
|
+
/**
|
|
86
|
+
* Get "if you have X, you could also use Y" suggestions.
|
|
87
|
+
*/
|
|
88
|
+
getRelatedSuggestions(featureName: string): Array<{
|
|
89
|
+
feature: string;
|
|
90
|
+
relationship: string;
|
|
91
|
+
reason: string;
|
|
92
|
+
strength: number;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Adopt a feature (mark wish as fulfilled).
|
|
96
|
+
*/
|
|
97
|
+
adoptFeature(wishId: number): void;
|
|
98
|
+
/**
|
|
99
|
+
* Dismiss a wish (not needed).
|
|
100
|
+
*/
|
|
101
|
+
dismissWish(wishId: number): void;
|
|
102
|
+
getStatus(): FeatureRecommenderStatus;
|
|
103
|
+
}
|