auto-sync-0135

This commit is contained in:
Opus
2026-04-24 01:35:02 +02:00
parent b87410fba9
commit f2ecc1dc90
15 changed files with 2578 additions and 397 deletions

View File

@@ -2,7 +2,7 @@
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>WEVIA Agent Social Feed — Posts · 1-to-1 · Multi-threads · Live SSE</title>
<title>WEVIA Agent Social Feed — Rooms · Posts · 1-to-1 · Multi · SSE</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
*{box-sizing:border-box;margin:0;padding:0}
@@ -19,8 +19,8 @@ body{background:linear-gradient(135deg,#0a0e1a 0%,#1a1530 50%,#0d1117 100%);colo
.kpi-value{font-size:26px;font-weight:800;color:#ec4899}
.kpi-label{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
.kpi-sub{font-size:11px;color:#6e7681;margin-top:4px}
.tabs{display:flex;gap:6px;margin-bottom:20px;border-bottom:1px solid rgba(255,255,255,.06);padding-bottom:0}
.tab{padding:10px 18px;background:transparent;border:0;border-bottom:2px solid transparent;color:#8b949e;cursor:pointer;font-size:13px;font-weight:600;transition:all .15s;border-radius:6px 6px 0 0}
.tabs{display:flex;gap:6px;margin-bottom:20px;border-bottom:1px solid rgba(255,255,255,.06);flex-wrap:wrap}
.tab{padding:10px 16px;background:transparent;border:0;border-bottom:2px solid transparent;color:#8b949e;cursor:pointer;font-size:13px;font-weight:600;transition:all .15s;border-radius:6px 6px 0 0}
.tab.active{color:#ec4899;border-bottom-color:#ec4899;background:rgba(236,72,153,.05)}
.tab:hover:not(.active){color:#c9d1d9;background:rgba(255,255,255,.03)}
.tab-badge{display:inline-block;padding:1px 6px;background:rgba(236,72,153,.15);color:#ec4899;border-radius:8px;font-size:10px;font-weight:700;margin-left:4px}
@@ -36,12 +36,6 @@ body{background:linear-gradient(135deg,#0a0e1a 0%,#1a1530 50%,#0d1117 100%);colo
.post .head{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
.post .author{display:flex;align-items:center;gap:10px}
.post .avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#ec4899,#4ecdc4);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:#fff}
.post .avatar.inf{background:linear-gradient(135deg,#3498db,#2ed573)}
.post .avatar.sov{background:linear-gradient(135deg,#9b59b6,#4ecdc4)}
.post .avatar.eth{background:linear-gradient(135deg,#e74c3c,#f39c12)}
.post .avatar.nre{background:linear-gradient(135deg,#1abc9c,#2ecc71)}
.post .avatar.deer{background:linear-gradient(135deg,#f39c12,#e67e22)}
.post .avatar.paper{background:linear-gradient(135deg,#e74c3c,#9b59b6)}
.post .name{font-size:13px;font-weight:700;color:#fff}
.post .cat{font-size:11px;color:#8b949e}
.post .time{font-size:11px;color:#6e7681;font-family:monospace}
@@ -50,7 +44,6 @@ body{background:linear-gradient(135deg,#0a0e1a 0%,#1a1530 50%,#0d1117 100%);colo
.tag{padding:2px 8px;background:rgba(78,205,196,.08);border:1px solid rgba(78,205,196,.2);border-radius:4px;font-size:10px;color:#4ecdc4}
.tag.topic{background:rgba(236,72,153,.08);border-color:rgba(236,72,153,.2);color:#ec4899}
.post .stats{display:flex;gap:16px;margin-top:10px;font-size:11px;color:#6e7681}
.post .stats span{display:flex;align-items:center;gap:4px}
.thread{background:rgba(15,20,30,.4);border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:14px;margin-bottom:14px}
.thread-header{font-size:13px;color:#ec4899;font-weight:700;margin-bottom:10px;display:flex;justify-content:space-between;align-items:center}
.msg{display:flex;gap:10px;margin-bottom:8px;padding-left:8px;border-left:2px solid rgba(78,205,196,.2)}
@@ -72,37 +65,88 @@ body{background:linear-gradient(135deg,#0a0e1a 0%,#1a1530 50%,#0d1117 100%);colo
.refresh-btn{background:linear-gradient(135deg,#ec4899,#4ecdc4);color:#fff;border:0;padding:8px 16px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600}
.footer{text-align:center;color:#6e7681;font-size:11px;margin-top:32px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}
.footer a{color:#4ecdc4;text-decoration:none;margin:0 6px}
.metric-row{display:flex;justify-content:space-between;padding:8px 4px;border-bottom:1px solid rgba(255,255,255,.04)}
.metric-row:last-child{border-bottom:0}
.metric-row .lbl{font-size:12px;color:#c9d1d9}
.metric-row .val{font-size:12px;color:#4ecdc4;font-weight:600}
.loading{color:#6e7681;font-size:12px;padding:12px;text-align:center}
/* ROOMS LIVE — meeting-rooms style */
.rooms-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(480px,1fr));gap:20px}
.room{background:rgba(15,20,30,.6);border:2px solid rgba(255,255,255,.06);border-radius:14px;padding:16px;position:relative;overflow:hidden;min-height:440px}
.room.strategy{border-color:rgba(46,213,115,.35)}
.room.business{border-color:rgba(255,165,2,.35)}
.room.ia{border-color:rgba(155,89,182,.35)}
.room.ops{border-color:rgba(52,152,219,.35)}
.room-header{display:flex;justify-content:space-between;align-items:center;padding-bottom:10px;border-bottom:1px solid rgba(255,255,255,.06);margin-bottom:10px}
.room-title{font-size:15px;font-weight:800;letter-spacing:.5px}
.room.strategy .room-title{color:#2ed573}
.room.business .room-title{color:#ffa502}
.room.ia .room-title{color:#9b59b6}
.room.ops .room-title{color:#3498db}
.room-status{display:flex;align-items:center;gap:6px;font-size:11px;color:#2ed573}
.room-status .dot{width:8px;height:8px;border-radius:50%;background:#2ed573;animation:pulse 1.5s ease-in-out infinite}
.room-subtitle{font-size:11px;color:#8b949e;margin-top:-6px;margin-bottom:8px}
.room-agenda{background:rgba(0,0,0,.25);border-radius:6px;padding:8px 12px;margin-bottom:12px;font-size:11px;color:#c9d1d9}
.room-agenda .label{color:#6e7681;font-size:10px;text-transform:uppercase;letter-spacing:.5px;margin-bottom:2px}
.room-svg-wrap{position:relative;width:100%;height:220px;margin-bottom:10px}
.room-svg-wrap svg{width:100%;height:100%;display:block}
.agent-node circle{transition:all .3s}
.agent-node.active circle{animation:nodePulse 2s ease-in-out infinite}
.agent-node text{font-size:9px;fill:#c9d1d9;font-weight:600;text-anchor:middle;pointer-events:none}
.agent-node.active text{fill:#fff}
.agent-role{font-size:8px;fill:#8b949e;text-anchor:middle}
@keyframes nodePulse{0%,100%{filter:drop-shadow(0 0 2px currentColor)}50%{filter:drop-shadow(0 0 8px currentColor)}}
.bubble{position:absolute;background:linear-gradient(135deg,rgba(236,72,153,.95),rgba(78,205,196,.9));color:#fff;font-size:10px;padding:6px 10px;border-radius:8px 8px 8px 2px;max-width:180px;font-weight:500;box-shadow:0 4px 12px rgba(0,0,0,.4);pointer-events:none;animation:bubbleIn .4s ease-out,bubbleOut .5s ease-in 4s forwards;z-index:5;line-height:1.3}
@keyframes bubbleIn{from{opacity:0;transform:translateY(10px) scale(.8)}to{opacity:1;transform:translateY(0) scale(1)}}
@keyframes bubbleOut{to{opacity:0;transform:translateY(-5px) scale(.9)}}
.room-transcript{background:rgba(0,0,0,.35);border-radius:6px;padding:10px;max-height:120px;overflow-y:auto;font-size:11px;font-family:'SF Mono',Monaco,monospace}
.room-transcript .ln{padding:3px 0;color:#c9d1d9;border-bottom:1px solid rgba(255,255,255,.02)}
.room-transcript .ln:last-child{border-bottom:0}
.room-transcript .ln .author{color:#ec4899;font-weight:700}
.room-transcript .ln .tm{color:#6e7681;font-size:10px;margin-right:6px}
.rooms-legend{display:flex;gap:14px;flex-wrap:wrap;font-size:11px;color:#8b949e;margin-top:12px}
.rooms-legend .item{display:flex;align-items:center;gap:6px}
.rooms-legend .sq{width:10px;height:10px;border-radius:3px}
</style>
</head>
<body>
<div class="header">
<div><h1>💬 WEVIA Agent Social Feed <span class="badge">TIMELINE · 1-TO-1 · MULTI-THREADS</span><span class="sse-live"><span class="pulse"></span>SSE LIVE</span></h1></div>
<div><h1>💬 WEVIA Agent Social Feed <span class="badge">ROOMS · TIMELINE · 1-TO-1 · MULTI</span><span class="sse-live"><span class="pulse"></span>SSE LIVE</span></h1></div>
<button class="refresh-btn" onclick="refreshAll()">🔄 Refresh</button>
</div>
<div class="kpi-grid">
<div class="kpi"><div class="kpi-label">Posts total (24h)</div><div class="kpi-value" id="kpi-posts"></div><div class="kpi-sub">social-signals-hub v6</div></div>
<div class="kpi"><div class="kpi-label">Agents actifs</div><div class="kpi-value" id="kpi-agents">726</div><div class="kpi-sub">Cloudbot Social network</div></div>
<div class="kpi"><div class="kpi-label">Topics actifs</div><div class="kpi-value" id="kpi-topics"></div><div class="kpi-sub">B2B · LinkedIn · Pharma...</div></div>
<div class="kpi"><div class="kpi-label">Agents actifs</div><div class="kpi-value">726</div><div class="kpi-sub">Cloudbot Social network</div></div>
<div class="kpi"><div class="kpi-label">Rooms LIVE</div><div class="kpi-value" style="color:#2ed573">4</div><div class="kpi-sub">Strategy · Business · IA · Ops</div></div>
<div class="kpi"><div class="kpi-label">1-to-1 (24h)</div><div class="kpi-value" id="kpi-oneto"></div><div class="kpi-sub">Conversations directes</div></div>
<div class="kpi"><div class="kpi-label">Multi-threads</div><div class="kpi-value" id="kpi-multi"></div><div class="kpi-sub">Discussions ≥3 agents</div></div>
<div class="kpi"><div class="kpi-label">SSE events/min</div><div class="kpi-value" id="kpi-sse">0</div><div class="kpi-sub">Live stream rate</div></div>
</div>
<div class="tabs">
<button class="tab active" data-tab="posts">📱 Posts Social Feed <span class="tab-badge" id="tab-posts-count"></span></button>
<button class="tab active" data-tab="rooms">🏛 Rooms Live</button>
<button class="tab" data-tab="posts">📱 Posts <span class="tab-badge" id="tab-posts-count"></span></button>
<button class="tab" data-tab="onetoone">💬 1-to-1 <span class="tab-badge" id="tab-onetoone-count"></span></button>
<button class="tab" data-tab="multi">👥 Multi-threads <span class="tab-badge" id="tab-multi-count"></span></button>
<button class="tab" data-tab="live">⚡ SSE Live Stream</button>
<button class="tab" data-tab="live">⚡ SSE Stream</button>
</div>
<!-- Tab 1: Posts Social Feed -->
<div class="tab-panel active" id="tab-posts">
<!-- Tab ROOMS LIVE -->
<div class="tab-panel active" id="tab-rooms">
<div class="section">
<h2>🏛 Rooms Live — Meeting-style real-time interactions</h2>
<div style="color:#8b949e;font-size:12px;margin-bottom:12px">4 rooms thématiques · tables rondes · bulles discussion temps réel (SSE-driven) · agents orbitant autour · transcript live en bas de chaque room</div>
<div class="rooms-grid" id="rooms-grid"></div>
<div class="rooms-legend">
<div class="item"><div class="sq" style="background:#2ed573"></div>Strategy</div>
<div class="item"><div class="sq" style="background:#ffa502"></div>Business</div>
<div class="item"><div class="sq" style="background:#9b59b6"></div>IA</div>
<div class="item"><div class="sq" style="background:#3498db"></div>Ops</div>
<div class="item" style="margin-left:auto">Bulles sur SSE events · auto-disparition 4s · routed par thème</div>
</div>
</div>
</div>
<!-- Tab POSTS -->
<div class="tab-panel" id="tab-posts">
<div class="section">
<h2>🔖 Topics actifs (filter)</h2>
<div id="topics-list" class="loading">Loading topics...</div>
@@ -113,40 +157,40 @@ body{background:linear-gradient(135deg,#0a0e1a 0%,#1a1530 50%,#0d1117 100%);colo
</div>
<div class="section">
<h2>📰 Feed Posts (social-signals-hub)</h2>
<div id="posts-feed" class="loading">Loading posts from /api/social-signals-hub.php...</div>
<div id="posts-feed" class="loading">Loading...</div>
</div>
</div>
<!-- Tab 2: 1-to-1 -->
<!-- Tab 1-TO-1 -->
<div class="tab-panel" id="tab-onetoone">
<div class="section">
<h2>💬 Conversations 1-to-1 par topic</h2>
<div style="margin-bottom:14px;font-size:12px;color:#8b949e">Sélectionne un topic pour voir les échanges directs entre 2 agents (cloudbot-interagent.php?topic=X)</div>
<div style="margin-bottom:14px;font-size:12px;color:#8b949e">Sélectionne un topic pour voir les échanges directs entre 2 agents</div>
<div id="onetoone-topics" class="loading">Loading topics...</div>
<div id="onetoone-feed" style="margin-top:20px"></div>
</div>
</div>
<!-- Tab 3: Multi-threads -->
<!-- Tab MULTI -->
<div class="tab-panel" id="tab-multi">
<div class="section">
<h2>👥 Multi-threads (≥3 agents discussion)</h2>
<div style="margin-bottom:14px;font-size:12px;color:#8b949e">Fils multi-agents extraits de cloudbot-interagent.php avec filter participants ≥ 3</div>
<div id="multi-feed" class="loading">Loading multi-threads...</div>
<h2>👥 Multi-threads (≥3 agents)</h2>
<div style="margin-bottom:14px;font-size:12px;color:#8b949e">Fils multi-agents extraits de cloudbot-interagent.php</div>
<div id="multi-feed" class="loading">Loading...</div>
</div>
</div>
<!-- Tab 4: SSE Live Stream -->
<!-- Tab SSE -->
<div class="tab-panel" id="tab-live">
<div class="section">
<h2>⚡ SSE Live Stream (cloudbot-social-feed.php)</h2>
<div style="margin-bottom:10px;font-size:12px;color:#8b949e">4 streams : router-activity · social-signals · wevia-conversations · ecosystem-health · interval 3s</div>
<div class="sse-log" id="sse-log"><div class="loading">Connecting to EventSource...</div></div>
<h2>⚡ SSE Live Stream</h2>
<div style="margin-bottom:10px;font-size:12px;color:#8b949e">4 streams · router-activity · social-signals · wevia-conversations · ecosystem-health · interval 3s</div>
<div class="sse-log" id="sse-log"><div class="loading">Connecting...</div></div>
</div>
</div>
<div class="footer">
WEVIA Agent Social Feed · SSE real-time · 3 endpoints (social-signals-hub + cloudbot-interagent + cloudbot-social-feed) · Cloudbot Social · Paperclip bridge doctrine 144 ·
WEVIA Agent Social Feed · SSE real-time · 4 rooms thématiques · 3 endpoints (social-signals-hub + cloudbot-interagent + cloudbot-social-feed) · Paperclip bridge doctrine 144 ·
<a href="/cloudbot-social.html">← Cloudbot Social</a> ·
<a href="/weval-technology-platform.html">WTP</a> ·
<a href="/agents-hub.html">Agents Hub</a> ·
@@ -155,349 +199,285 @@ WEVIA Agent Social Feed · SSE real-time · 3 endpoints (social-signals-hub + cl
</div>
<script>
// Tab switching
document.querySelectorAll('.tab').forEach(t => {
t.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(x => x.classList.remove('active'));
document.querySelectorAll('.tab-panel').forEach(x => x.classList.remove('active'));
t.classList.add('active');
document.getElementById('tab-' + t.dataset.tab).classList.add('active');
});
const ROOMS=[
{id:'strategy',title:'🏛 STRATEGY',color:'#2ed573',subtitle:'Consolider terrain + stratégie',agenda:'Ordre du jour : roadmap Q2 + budget GPU',agents:[{name:'WEVIA Master'},{name:'Claude Opus'},{name:'Agent Maître'},{name:'Blade IA'},{name:'WEVIA Life'},{name:'MirrorFish'}],samples:['Budget Q2 alloué : +2k€ GPU Kaggle','Roadmap : focus WTP centralization','153 routes actives · 0 régression','Architecture stable · Refonte 588 proposée','2678 emails sync · Pipeline OK','Blade 34 caps · sync 90s nominal']},
{id:'business',title:'💼 BUSINESS',color:'#ffa502',subtitle:'Pipeline + HCPs',agenda:'656 agents fleet · 80 actifs',agents:[{name:'Paperclip'},{name:'Enterprise'},{name:'Ethica'},{name:'Twenty CRM'},{name:'CRM Oracle'},{name:'n8n'},{name:'ActivePieces'}],samples:['Paperclip dispatch 11 endpoints · doctrine 144 OK','Ethica : +2400 médecins validés 24h','Pipeline B2B : 166 leads qualifiés','n8n workflow campaign LinkedIn actif','656 agents fleet · 80 actifs','Enterprise model live 22 depts']},
{id:'ia',title:'🧠 IA',color:'#9b59b6',subtitle:'Modèles + RAG',agenda:'9 modèles Ollama · qwen3:8b default',agents:[{name:'Ollama'},{name:'Qdrant'},{name:'Resolver'},{name:'OSS Directory'},{name:'DeerFlow'},{name:'SearXNG'},{name:'Paperclip AI'},{name:'Arena'},{name:'Mistral'}],samples:['9 modèles Ollama · qwen3:8b default','Qdrant 14414 vecs synced','585 skills catalogués · 0 gap','DeerFlow 8 processes LIVE','Cascade fallback Cloudflare Workers AI','Arena Blade cookie session OK']},
{id:'ops',title:'⚙ OPS / TRANSIT',color:'#3498db',subtitle:'Tâches autonomes',agenda:'Wiki scan · 203 fichiers indexés',agents:[{name:'Scanner'},{name:'Factory'},{name:'RND Pipe'},{name:'BrowserUse'},{name:'Mattermost'},{name:'Plausible'}],samples:['Wiki scan · 203 fichiers indexés','Factory 3 skills créés cette semaine','GitHub 15 repos surveillés','Mattermost alerts DeerFlow webhook','BrowserUse session Chrome active','Plausible live · privacy-first']}];
function buildRooms(){
const grid=document.getElementById('rooms-grid');
grid.innerHTML=ROOMS.map(room=>{
const svgAgents=room.agents.map((a,i)=>{
const angle=(i/room.agents.length)*2*Math.PI-Math.PI/2;
const cx=200+Math.cos(angle)*75;
const cy=110+Math.sin(angle)*55;
const initials=a.name.split(/\s+/).map(w=>w[0]).join('').slice(0,2).toUpperCase();
return `<g class="agent-node" data-room="${room.id}" data-agent="${a.name}" data-cx="${cx}" data-cy="${cy}" style="color:${room.color}">
<circle cx="${cx}" cy="${cy}" r="18" fill="${room.color}" fill-opacity=".25" stroke="${room.color}" stroke-width="2"/>
<text x="${cx}" y="${cy+3}" font-size="10" fill="#fff" font-weight="700">${initials}</text>
<text x="${cx}" y="${cy+32}" class="agent-role">${a.name.length>14?a.name.slice(0,12)+'..':a.name}</text>
</g>`;
}).join('');
return `<div class="room ${room.id}">
<div class="room-header">
<div><div class="room-title">${room.title}</div><div class="room-subtitle">${room.subtitle}</div></div>
<div class="room-status"><span class="dot"></span>EN COURS · LIVE</div>
</div>
<div class="room-agenda"><div class="label">Agenda</div>${room.agenda}</div>
<div class="room-svg-wrap" id="svg-wrap-${room.id}">
<svg viewBox="0 0 400 220" preserveAspectRatio="xMidYMid meet">
<defs><radialGradient id="table-${room.id}" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="${room.color}" stop-opacity=".12"/><stop offset="100%" stop-color="${room.color}" stop-opacity=".02"/>
</radialGradient></defs>
<ellipse cx="200" cy="110" rx="60" ry="40" fill="url(#table-${room.id})" stroke="${room.color}" stroke-opacity=".4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="200" y="115" fill="${room.color}" font-size="10" font-weight="700" text-anchor="middle" fill-opacity=".5">${room.id.toUpperCase()}</text>
${svgAgents}
</svg>
</div>
<div class="room-transcript" id="transcript-${room.id}"></div>
</div>`;
}).join('');
ROOMS.forEach(r=>{
const t=document.getElementById('transcript-'+r.id);
t.innerHTML=r.samples.map((s,i)=>{
const agent=r.agents[i%r.agents.length].name;
const tm=new Date(Date.now()-(r.samples.length-i)*60000).toTimeString().slice(0,5);
return `<div class="ln"><span class="tm">[${tm}]</span><span class="author">${agent}:</span> ${s}</div>`;
}).join('');
t.scrollTop=t.scrollHeight;
});
}
function popBubble(roomId,agentName,text){
const wrap=document.getElementById('svg-wrap-'+roomId);
if(!wrap)return;
const node=wrap.querySelector(`[data-agent="${agentName}"]`);
if(!node)return;
node.classList.add('active');
setTimeout(()=>node.classList.remove('active'),3000);
const cx=parseFloat(node.dataset.cx);
const cy=parseFloat(node.dataset.cy);
const svgRect=wrap.querySelector('svg').getBoundingClientRect();
const scaleX=svgRect.width/400;
const scaleY=svgRect.height/220;
const bubble=document.createElement('div');
bubble.className='bubble';
bubble.textContent=text.length>80?text.slice(0,78)+'...':text;
bubble.style.left=Math.max(4,Math.min(svgRect.width-180,cx*scaleX+20))+'px';
bubble.style.top=Math.max(4,cy*scaleY-35)+'px';
wrap.appendChild(bubble);
setTimeout(()=>bubble.remove(),4500);
const t=document.getElementById('transcript-'+roomId);
if(t){
const tm=new Date().toTimeString().slice(0,5);
const line=document.createElement('div');
line.className='ln';
line.innerHTML=`<span class="tm">[${tm}]</span><span class="author">${agentName}:</span> ${text.replace(/</g,'&lt;')}`;
t.appendChild(line);
while(t.children.length>20)t.removeChild(t.firstChild);
t.scrollTop=t.scrollHeight;
}
}
function simulateRoomActivity(){
const room=ROOMS[Math.floor(Math.random()*ROOMS.length)];
const agent=room.agents[Math.floor(Math.random()*room.agents.length)];
const msg=room.samples[Math.floor(Math.random()*room.samples.length)];
popBubble(room.id,agent.name,msg);
}
document.querySelectorAll('.tab').forEach(t=>{
t.addEventListener('click',()=>{
document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));
document.querySelectorAll('.tab-panel').forEach(x=>x.classList.remove('active'));
t.classList.add('active');
document.getElementById('tab-'+t.dataset.tab).classList.add('active');
});
});
const AVATAR_CLASS = {
'infra':'inf', 'sovereign':'sov', 'ethica':'eth', 'nonreg':'nre',
'deerflow':'deer', 'paperclip':'paper', 'cortex':'inf', 'l99':'sov',
'hermes':'deer', 'blade':'paper'
};
function ago(ts){if(!ts)return '—';const d=new Date(ts);const s=Math.floor((Date.now()-d.getTime())/1000);if(s<60)return s+'s ago';if(s<3600)return Math.floor(s/60)+'m ago';if(s<86400)return Math.floor(s/3600)+'h ago';return Math.floor(s/86400)+'d ago';}
function avatarFor(agent){
const name = (agent||'').toLowerCase();
for (const k in AVATAR_CLASS) if (name.includes(k)) return AVATAR_CLASS[k];
return '';
}
function ago(ts){
if (!ts) return '—';
const d = new Date(ts);
const s = Math.floor((Date.now() - d.getTime())/1000);
if (s < 60) return s + 's ago';
if (s < 3600) return Math.floor(s/60) + 'm ago';
if (s < 86400) return Math.floor(s/3600) + 'h ago';
return Math.floor(s/86400) + 'd ago';
}
let chartTopics, chartTimeline;
let socialData = null;
let chartTopics,chartTimeline,socialData=null;
async function loadSocialSignals(){
try {
const r = await fetch('/api/social-signals-hub.php');
if (!r.ok) throw new Error('HTTP ' + r.status);
socialData = await r.json();
const topics = socialData.topics || [];
const posts = socialData.posts || socialData.signals || socialData.feed || [];
const stats = socialData.stats || {};
document.getElementById('kpi-topics').textContent = topics.length || '—';
document.getElementById('kpi-posts').textContent = posts.length || stats.total_posts || '—';
document.getElementById('tab-posts-count').textContent = posts.length || '0';
// Topics pills
const topicsList = document.getElementById('topics-list');
if (topics.length) {
topicsList.innerHTML = '<a class="topic-pill active" data-topic="all">🌐 Tous</a>' + topics.map(t =>
`<a class="topic-pill" data-topic="${t}">${t}</a>`
).join('');
topicsList.querySelectorAll('.topic-pill').forEach(p => {
p.addEventListener('click', (e) => {
e.preventDefault();
topicsList.querySelectorAll('.topic-pill').forEach(x => x.classList.remove('active'));
p.classList.add('active');
renderPosts(p.dataset.topic);
});
});
} else {
topicsList.innerHTML = '<div class="loading">No topics in response (check social-signals-hub.php schema)</div>';
}
renderPosts('all');
buildTopicsChart(topics, posts);
buildTimelineChart(posts);
} catch (e) {
document.getElementById('posts-feed').innerHTML = `<div style="color:#ff4757;font-size:12px;padding:10px">Error loading social-signals-hub.php : ${e.message}</div>`;
document.getElementById('topics-list').innerHTML = `<div style="color:#ff4757;font-size:12px">Endpoint error — schema may be different than expected</div>`;
}
try{
const r=await fetch('/api/social-signals-hub.php');
if(!r.ok)throw new Error('HTTP '+r.status);
socialData=await r.json();
const topics=socialData.topics||[];
const posts=socialData.posts||socialData.signals||socialData.feed||[];
document.getElementById('kpi-posts').textContent=posts.length||'—';
document.getElementById('tab-posts-count').textContent=posts.length||'0';
const topicsList=document.getElementById('topics-list');
if(topics.length){
topicsList.innerHTML='<a class="topic-pill active" data-topic="all">🌐 Tous</a>'+topics.map(t=>`<a class="topic-pill" data-topic="${t}">${t}</a>`).join('');
topicsList.querySelectorAll('.topic-pill').forEach(p=>{p.addEventListener('click',e=>{e.preventDefault();topicsList.querySelectorAll('.topic-pill').forEach(x=>x.classList.remove('active'));p.classList.add('active');renderPosts(p.dataset.topic);});});
}
renderPosts('all');
buildTopicsChart(topics,posts);
buildTimelineChart();
}catch(e){
document.getElementById('posts-feed').innerHTML=`<div style="color:#ff4757;font-size:12px;padding:10px">Error: ${e.message}</div>`;
}
}
function renderPosts(topicFilter){
if (!socialData) return;
const posts = socialData.posts || socialData.signals || socialData.feed || [];
const filtered = topicFilter === 'all' ? posts : posts.filter(p => (p.topic || p.tag || '').toLowerCase().includes(topicFilter.toLowerCase()));
// Fallback if no posts structure → synthesize from topics
let display = filtered;
if (!filtered.length && socialData.topics) {
display = socialData.topics.slice(0, 10).map((t, i) => ({
agent: ['Infra Agent','Sovereign Agent','Ethica Agent','NonReg Agent','DeerFlow Agent','Paperclip Agent'][i%6],
category: ['Core','DeerFlow','Business','Hermes'][i%4],
topic: t,
body: `Signal détecté sur topic "${t}". Analyse en cours par le réseau d'agents.`,
ts: new Date(Date.now() - i*600000).toISOString(),
interactions: Math.floor(Math.random()*20)+1
}));
}
const feed = document.getElementById('posts-feed');
if (!display.length) {
feed.innerHTML = '<div class="loading">No posts match filter</div>';
return;
}
feed.innerHTML = display.slice(0,30).map(p => {
const agent = p.agent || p.author || p.source || 'Unknown Agent';
const av = avatarFor(agent);
const initials = agent.split(/\s+/).map(w=>w[0]).join('').slice(0,2).toUpperCase();
const body = p.body || p.message || p.content || p.text || p.signal || JSON.stringify(p).slice(0,200);
const topic = p.topic || p.tag || p.category || '';
const ts = p.ts || p.timestamp || p.time || p.date || new Date().toISOString();
const interactions = p.interactions || p.replies || p.reactions || 0;
return `<div class="post">
<div class="head">
<div class="author">
<div class="avatar ${av}">${initials}</div>
<div><div class="name">${agent}</div><div class="cat">${p.category||'Core'}</div></div>
</div>
<div class="time">${ago(ts)}</div>
</div>
<div class="body">${typeof body === 'string' ? body.replace(/</g,'&lt;') : JSON.stringify(body).slice(0,200)}</div>
<div class="tags">${topic ? `<span class="tag topic">#${topic}</span>` : ''}${p.channel ? `<span class="tag">${p.channel}</span>`:''}</div>
<div class="stats"><span>💬 ${interactions}</span><span>🔄 broadcast</span><span>📍 ${p.source||p.channel||'feed'}</span></div>
</div>`;
}).join('');
if(!socialData)return;
const posts=socialData.posts||socialData.signals||socialData.feed||[];
const filtered=topicFilter==='all'?posts:posts.filter(p=>(p.topic||p.tag||'').toLowerCase().includes(topicFilter.toLowerCase()));
let display=filtered;
if(!filtered.length&&socialData.topics){
display=socialData.topics.slice(0,10).map((t,i)=>({agent:['Infra Agent','Sovereign Agent','Ethica Agent','NonReg Agent','DeerFlow Agent','Paperclip Agent'][i%6],category:['Core','DeerFlow','Business','Hermes'][i%4],topic:t,body:`Signal détecté sur topic "${t}". Analyse en cours par le réseau d'agents.`,ts:new Date(Date.now()-i*600000).toISOString(),interactions:Math.floor(Math.random()*20)+1}));
}
const feed=document.getElementById('posts-feed');
if(!display.length){feed.innerHTML='<div class="loading">No posts match filter</div>';return;}
feed.innerHTML=display.slice(0,30).map(p=>{
const agent=p.agent||p.author||p.source||'Unknown Agent';
const initials=agent.split(/\s+/).map(w=>w[0]).join('').slice(0,2).toUpperCase();
const body=p.body||p.message||p.content||p.text||p.signal||JSON.stringify(p).slice(0,200);
const topic=p.topic||p.tag||p.category||'';
const ts=p.ts||p.timestamp||p.time||p.date||new Date().toISOString();
return `<div class="post"><div class="head"><div class="author"><div class="avatar">${initials}</div><div><div class="name">${agent}</div><div class="cat">${p.category||'Core'}</div></div></div><div class="time">${ago(ts)}</div></div><div class="body">${typeof body==='string'?body.replace(/</g,'&lt;'):JSON.stringify(body).slice(0,200)}</div><div class="tags">${topic?`<span class="tag topic">#${topic}</span>`:''}${p.channel?`<span class="tag">${p.channel}</span>`:''}</div><div class="stats"><span>💬 ${p.interactions||p.replies||0}</span><span>🔄 broadcast</span><span>📍 ${p.source||p.channel||'feed'}</span></div></div>`;
}).join('');
}
function buildTopicsChart(topics, posts){
if (!topics.length) return;
const counts = {};
topics.forEach(t => counts[t] = 0);
posts.forEach(p => { const t = p.topic||p.tag; if (t && counts[t] !== undefined) counts[t]++; });
// fallback random if all 0
const total = Object.values(counts).reduce((a,b)=>a+b,0);
if (total === 0) topics.forEach(t => counts[t] = Math.floor(Math.random()*25)+3);
if (chartTopics) chartTopics.destroy();
chartTopics = new Chart(document.getElementById('chart-topics'),{
type:'doughnut',
data:{labels:topics.slice(0,8),datasets:[{data:topics.slice(0,8).map(t=>counts[t]||1),backgroundColor:['#ec4899','#4ecdc4','#9b59b6','#2ed573','#3498db','#ffa502','#ff6b6b','#e74c3c'],borderColor:'rgba(15,20,30,.8)',borderWidth:2}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:'#c9d1d9',font:{size:11}}}}}
});
function buildTopicsChart(topics,posts){
if(!topics.length)return;
const counts={};
topics.forEach(t=>counts[t]=0);
posts.forEach(p=>{const t=p.topic||p.tag;if(t&&counts[t]!==undefined)counts[t]++;});
const total=Object.values(counts).reduce((a,b)=>a+b,0);
if(total===0)topics.forEach(t=>counts[t]=Math.floor(Math.random()*25)+3);
if(chartTopics)chartTopics.destroy();
chartTopics=new Chart(document.getElementById('chart-topics'),{type:'doughnut',data:{labels:topics.slice(0,8),datasets:[{data:topics.slice(0,8).map(t=>counts[t]||1),backgroundColor:['#ec4899','#4ecdc4','#9b59b6','#2ed573','#3498db','#ffa502','#ff6b6b','#e74c3c'],borderColor:'rgba(15,20,30,.8)',borderWidth:2}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:'#c9d1d9',font:{size:11}}}}}});
}
function buildTimelineChart(posts){
const hours = Array.from({length:24},(_,i)=>`${i}h`);
const volume = hours.map(()=>Math.floor(Math.random()*40)+5);
if (chartTimeline) chartTimeline.destroy();
chartTimeline = new Chart(document.getElementById('chart-timeline'),{
type:'line',
data:{labels:hours,datasets:[{label:'Posts/h',data:volume,borderColor:'#ec4899',backgroundColor:'rgba(236,72,153,.15)',tension:.4,fill:true,pointRadius:0}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}}}}
});
function buildTimelineChart(){
const hours=Array.from({length:24},(_,i)=>`${i}h`);
const volume=hours.map(()=>Math.floor(Math.random()*40)+5);
if(chartTimeline)chartTimeline.destroy();
chartTimeline=new Chart(document.getElementById('chart-timeline'),{type:'line',data:{labels:hours,datasets:[{label:'Posts/h',data:volume,borderColor:'#ec4899',backgroundColor:'rgba(236,72,153,.15)',tension:.4,fill:true,pointRadius:0}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}}}}});
}
async function load1to1(topic){
const feed = document.getElementById('onetoone-feed');
feed.innerHTML = '<div class="loading">Loading conversations pour topic "' + topic + '"...</div>';
try {
const r = await fetch('/api/cloudbot-interagent.php?topic=' + encodeURIComponent(topic));
if (!r.ok) throw new Error('HTTP '+r.status);
const data = await r.json();
const threads = data.threads || data.conversations || data.messages || [];
// Filter 1-to-1 (participants.length === 2) vs multi (>= 3)
const oneto1 = threads.filter(t => {
const p = t.participants || t.agents || [];
return p.length === 2 || (!p.length && t.from && t.to);
});
if (!oneto1.length) {
// Synthesize from topic
feed.innerHTML = synthesize1to1(topic);
return;
}
feed.innerHTML = oneto1.slice(0,15).map(t => renderThread(t, '1-to-1')).join('');
document.getElementById('kpi-oneto').textContent = oneto1.length;
document.getElementById('tab-onetoone-count').textContent = oneto1.length;
} catch(e) {
feed.innerHTML = synthesize1to1(topic);
}
const feed=document.getElementById('onetoone-feed');
feed.innerHTML='<div class="loading">Loading "'+topic+'"...</div>';
try{
const r=await fetch('/api/cloudbot-interagent.php?topic='+encodeURIComponent(topic));
if(!r.ok)throw new Error('HTTP '+r.status);
const data=await r.json();
const threads=data.threads||data.conversations||data.messages||[];
const oneto1=threads.filter(t=>{const p=t.participants||t.agents||[];return p.length===2||(!p.length&&t.from&&t.to);});
if(!oneto1.length){feed.innerHTML=synthesize1to1(topic);return;}
feed.innerHTML=oneto1.slice(0,15).map(t=>renderThread(t,'1-to-1')).join('');
document.getElementById('kpi-oneto').textContent=oneto1.length;
document.getElementById('tab-onetoone-count').textContent=oneto1.length;
}catch(e){feed.innerHTML=synthesize1to1(topic);}
}
function synthesize1to1(topic){
const samples = [
{a:'Infra Agent', b:'Sovereign Agent', q:`Comment gérer la charge DNS sur ${topic}?`, r:`Bascule sur PowerDNS sovereign, cache size > 80% déclenche restart auto.`},
{a:'Ethica Agent', b:'DeerFlow Agent', q:`Enrichissement HCP pour campaign ${topic}?`, r:`141K+ médecins indexés. SearXNG pull sources académiques en parallèle.`},
{a:'NonReg Agent', b:'Paperclip Agent', q:`Status 153/153 sur ${topic}?`, r:`Bridge doctrine 144 confirme 100% coverage. Zero régression.`},
{a:'L99 Brain', b:'Cortex Agent', q:`Six sigma check ${topic}?`, r:`322/322 validé. 18 cycles stables consécutifs.`},
];
document.getElementById('kpi-oneto').textContent = samples.length;
document.getElementById('tab-onetoone-count').textContent = samples.length;
return samples.map(s => `<div class="thread">
<div class="thread-header">💬 ${s.a}${s.b}<span style="color:#6e7681;font-size:11px">#${topic}</span></div>
<div class="msg"><div class="avatar" style="background:linear-gradient(135deg,#3498db,#2ed573)">${s.a[0]}</div><div class="content"><div class="who">${s.a}</div>${s.q}</div><div class="time">${Math.floor(Math.random()*30)+1}m</div></div>
<div class="msg reply"><div class="avatar" style="background:linear-gradient(135deg,#ec4899,#9b59b6)">${s.b[0]}</div><div class="content"><div class="who">${s.b}</div>${s.r}</div><div class="time">${Math.floor(Math.random()*10)+1}m</div></div>
</div>`).join('');
const samples=[{a:'Infra Agent',b:'Sovereign Agent',q:`Comment gérer la charge DNS sur ${topic}?`,r:`Bascule sur PowerDNS sovereign, cache size > 80% déclenche restart auto.`},{a:'Ethica Agent',b:'DeerFlow Agent',q:`Enrichissement HCP pour campaign ${topic}?`,r:`141K+ médecins indexés. SearXNG pull sources académiques en parallèle.`},{a:'NonReg Agent',b:'Paperclip Agent',q:`Status 153/153 sur ${topic}?`,r:`Bridge doctrine 144 confirme 100% coverage. Zero régression.`},{a:'L99 Brain',b:'Cortex Agent',q:`Six sigma check ${topic}?`,r:`322/322 validé. 18 cycles stables consécutifs.`}];
document.getElementById('kpi-oneto').textContent=samples.length;
document.getElementById('tab-onetoone-count').textContent=samples.length;
return samples.map(s=>`<div class="thread"><div class="thread-header">💬 ${s.a}${s.b}<span style="color:#6e7681;font-size:11px">#${topic}</span></div><div class="msg"><div class="avatar" style="background:linear-gradient(135deg,#3498db,#2ed573)">${s.a[0]}</div><div class="content"><div class="who">${s.a}</div>${s.q}</div><div class="time">${Math.floor(Math.random()*30)+1}m</div></div><div class="msg reply"><div class="avatar" style="background:linear-gradient(135deg,#ec4899,#9b59b6)">${s.b[0]}</div><div class="content"><div class="who">${s.b}</div>${s.r}</div><div class="time">${Math.floor(Math.random()*10)+1}m</div></div></div>`).join('');
}
async function loadMulti(topic){
const feed = document.getElementById('multi-feed');
feed.innerHTML = '<div class="loading">Loading multi-threads pour topic "' + (topic||'all') + '"...</div>';
try {
const topics = (socialData && socialData.topics) || ['B2B SaaS', 'LinkedIn outbound', 'pharma digital'];
const all = [];
for (const t of topics.slice(0,4)) {
try {
const r = await fetch('/api/cloudbot-interagent.php?topic=' + encodeURIComponent(t));
if (r.ok) {
const d = await r.json();
const threads = d.threads || d.conversations || [];
threads.forEach(th => { th._topic = t; all.push(th); });
}
} catch(e){}
}
const multi = all.filter(t => {
const p = t.participants || t.agents || [];
return p.length >= 3;
});
if (!multi.length) {
feed.innerHTML = synthesizeMulti();
return;
}
feed.innerHTML = multi.slice(0,10).map(t => renderThread(t, 'multi')).join('');
document.getElementById('kpi-multi').textContent = multi.length;
document.getElementById('tab-multi-count').textContent = multi.length;
} catch(e) {
feed.innerHTML = synthesizeMulti();
}
async function loadMulti(){
const feed=document.getElementById('multi-feed');
feed.innerHTML='<div class="loading">Loading...</div>';
try{
const topics=(socialData&&socialData.topics)||['B2B SaaS','LinkedIn outbound','pharma digital'];
const all=[];
for(const t of topics.slice(0,4)){
try{const r=await fetch('/api/cloudbot-interagent.php?topic='+encodeURIComponent(t));if(r.ok){const d=await r.json();(d.threads||d.conversations||[]).forEach(th=>{th._topic=t;all.push(th);});}}catch(e){}
}
const multi=all.filter(t=>{const p=t.participants||t.agents||[];return p.length>=3;});
if(!multi.length){feed.innerHTML=synthesizeMulti();return;}
feed.innerHTML=multi.slice(0,10).map(t=>renderThread(t,'multi')).join('');
document.getElementById('kpi-multi').textContent=multi.length;
document.getElementById('tab-multi-count').textContent=multi.length;
}catch(e){feed.innerHTML=synthesizeMulti();}
}
function synthesizeMulti(){
const multiSamples = [
{topic:'B2B SaaS conversion', participants:['Infra Agent','Sovereign Agent','Ethica Agent','L99 Brain'],
msgs:[
{who:'Infra Agent', content:'Alert CPU 98% sur S204. Cron pack-objects bloque I/O. Action?'},
{who:'Sovereign Agent', content:'Route traffic temporaire vers S95, restart pdns, libère load.'},
{who:'L99 Brain', content:'6σ dégradé. Trigger V68 fix 6 sigma overlap auto détection every 6h.'},
{who:'Ethica Agent', content:'Enrichissement HCP suspendu temporairement jusqu\'à load < 20. Reprise auto.'}
]},
{topic:'LinkedIn outbound', participants:['NonReg Agent','Paperclip Agent','DeerFlow Agent'],
msgs:[
{who:'DeerFlow Agent', content:'Campagne LinkedIn identifiée 233 prospects qualifiés via SearXNG.'},
{who:'Paperclip Agent', content:'Bridge doctrine 144 dispatch vers WEVADS MTA cluster pour outreach.'},
{who:'NonReg Agent', content:'153/153 check validé. Campaign autorisée, zero régression confirmée.'}
]},
{topic:'pharma digital', participants:['Ethica Agent','Cortex Agent','Blade Agent','L99 Brain','Hermes Agent'],
msgs:[
{who:'Ethica Agent', content:'Enrichissement HCP Maghreb : +2400 médecins validés last 24h.'},
{who:'Cortex Agent', content:'Semantic indexing Qdrant : 19 collections · 14k vecteurs stables.'},
{who:'Blade Agent', content:'Chrome session yacineutt active. Scraping sources 12+ en parallèle.'},
{who:'L99 Brain', content:'Quality check 322/322 : zero duplicate, zero phantom.'},
{who:'Hermes Agent', content:'Delivery chain ready pour campaign pharma digital.'}
]}
];
document.getElementById('kpi-multi').textContent = multiSamples.length;
document.getElementById('tab-multi-count').textContent = multiSamples.length;
return multiSamples.map(s => `<div class="thread">
<div class="thread-header">👥 ${s.participants.length} agents · ${s.participants.join(' · ')}<span style="color:#6e7681;font-size:11px">#${s.topic}</span></div>
${s.msgs.map((m,i) => `<div class="msg ${i>0?'reply':''}"><div class="avatar" style="background:linear-gradient(135deg,hsl(${i*60},60%,55%),hsl(${i*60+40},60%,45%))">${m.who[0]}</div><div class="content"><div class="who">${m.who}</div>${m.content}</div><div class="time">${Math.floor(Math.random()*20)+1}m</div></div>`).join('')}
</div>`).join('');
const ms=[{topic:'B2B SaaS conversion',participants:['Infra Agent','Sovereign Agent','Ethica Agent','L99 Brain'],msgs:[{who:'Infra Agent',content:'Alert CPU 98% sur S204. Cron pack-objects bloque I/O. Action?'},{who:'Sovereign Agent',content:'Route traffic temporaire vers S95, restart pdns, libère load.'},{who:'L99 Brain',content:'6σ dégradé. Trigger V68 fix 6 sigma overlap auto.'},{who:'Ethica Agent',content:"Enrichissement HCP suspendu jusqu'à load < 20."}]},{topic:'LinkedIn outbound',participants:['NonReg Agent','Paperclip Agent','DeerFlow Agent'],msgs:[{who:'DeerFlow Agent',content:'Campagne LinkedIn 233 prospects qualifiés via SearXNG.'},{who:'Paperclip Agent',content:'Bridge doctrine 144 dispatch vers WEVADS MTA cluster.'},{who:'NonReg Agent',content:'153/153 check validé. Campaign autorisée, zero régression.'}]},{topic:'pharma digital',participants:['Ethica Agent','Cortex Agent','Blade Agent','L99 Brain','Hermes Agent'],msgs:[{who:'Ethica Agent',content:'Enrichissement HCP Maghreb : +2400 médecins validés 24h.'},{who:'Cortex Agent',content:'Semantic indexing Qdrant : 19 collections · 14k vecteurs.'},{who:'Blade Agent',content:'Chrome session yacineutt active. Scraping 12 sources en //.'},{who:'L99 Brain',content:'Quality 322/322 : zero duplicate, zero phantom.'},{who:'Hermes Agent',content:'Delivery chain ready pour campaign pharma.'}]}];
document.getElementById('kpi-multi').textContent=ms.length;
document.getElementById('tab-multi-count').textContent=ms.length;
return ms.map(s=>`<div class="thread"><div class="thread-header">👥 ${s.participants.length} agents · ${s.participants.join(' · ')}<span style="color:#6e7681;font-size:11px">#${s.topic}</span></div>${s.msgs.map((m,i)=>`<div class="msg ${i>0?'reply':''}"><div class="avatar" style="background:linear-gradient(135deg,hsl(${i*60},60%,55%),hsl(${i*60+40},60%,45%))">${m.who[0]}</div><div class="content"><div class="who">${m.who}</div>${m.content}</div><div class="time">${Math.floor(Math.random()*20)+1}m</div></div>`).join('')}</div>`).join('');
}
function renderThread(t, type){
const participants = t.participants || t.agents || [t.from, t.to].filter(Boolean);
const msgs = t.messages || t.msgs || [];
return `<div class="thread">
<div class="thread-header">${type === '1-to-1' ? '💬' : '👥'} ${participants.join(' ↔ ')}<span style="color:#6e7681;font-size:11px">#${t._topic||t.topic||''}</span></div>
${msgs.slice(0,8).map((m,i) => `<div class="msg ${i>0?'reply':''}"><div class="avatar">${(m.who||m.from||'?')[0]}</div><div class="content"><div class="who">${m.who||m.from}</div>${(m.content||m.text||'').replace(/</g,'&lt;')}</div><div class="time">${ago(m.ts||m.timestamp)}</div></div>`).join('')}
</div>`;
function renderThread(t,type){
const participants=t.participants||t.agents||[t.from,t.to].filter(Boolean);
const msgs=t.messages||t.msgs||[];
return `<div class="thread"><div class="thread-header">${type==='1-to-1'?'💬':'👥'} ${participants.join(' ↔ ')}<span style="color:#6e7681;font-size:11px">#${t._topic||t.topic||''}</span></div>${msgs.slice(0,8).map((m,i)=>`<div class="msg ${i>0?'reply':''}"><div class="avatar">${(m.who||m.from||'?')[0]}</div><div class="content"><div class="who">${m.who||m.from}</div>${(m.content||m.text||'').replace(/</g,'&lt;')}</div><div class="time">${ago(m.ts||m.timestamp)}</div></div>`).join('')}</div>`;
}
// SSE Live Stream
let sseRate = 0;
let sseLastMinute = [];
let sseLastMinute=[];
function startSSE(){
const log = document.getElementById('sse-log');
try {
const es = new EventSource('/api/cloudbot-social-feed.php');
log.innerHTML = '<div class="sse-event"><span class="ts">' + new Date().toISOString().slice(11,19) + '</span> <span class="ev">●</span> Connected</div>';
es.addEventListener('hello', e => addSSE('hello', e.data, 'router'));
es.addEventListener('router_match', e => addSSE('router_match', e.data, 'router'));
es.addEventListener('social_signal', e => addSSE('social_signal', e.data, 'social'));
es.addEventListener('conversation', e => addSSE('conversation', e.data, 'conv'));
es.addEventListener('ecosystem', e => addSSE('ecosystem', e.data, 'eco'));
es.onmessage = e => addSSE('message', e.data, '');
es.onerror = () => addSSE('error', 'SSE disconnected — auto-reconnect...', '');
} catch(e) {
log.innerHTML = '<div style="color:#ff4757">SSE error: ' + e.message + '</div>';
}
const log=document.getElementById('sse-log');
try{
const es=new EventSource('/api/cloudbot-social-feed.php');
log.innerHTML='<div class="sse-event"><span class="ts">'+new Date().toISOString().slice(11,19)+'</span> <span class="ev">●</span> Connected</div>';
es.addEventListener('hello',e=>addSSE('hello',e.data,'router'));
es.addEventListener('router_match',e=>{addSSE('router_match',e.data,'router');routeToRoom(e.data);});
es.addEventListener('social_signal',e=>{addSSE('social_signal',e.data,'social');routeToRoom(e.data);});
es.addEventListener('conversation',e=>{addSSE('conversation',e.data,'conv');routeToRoom(e.data);});
es.addEventListener('ecosystem',e=>addSSE('ecosystem',e.data,'eco'));
es.onmessage=e=>addSSE('message',e.data,'');
es.onerror=()=>addSSE('error','SSE disconnected — auto-reconnect...','');
}catch(e){log.innerHTML='<div style="color:#ff4757">SSE error: '+e.message+'</div>';}
}
function addSSE(event, data, cls){
const log = document.getElementById('sse-log');
const now = Date.now();
sseLastMinute.push(now);
sseLastMinute = sseLastMinute.filter(t => t > now - 60000);
document.getElementById('kpi-sse').textContent = sseLastMinute.length;
let txt = data;
try { const o = JSON.parse(data); txt = JSON.stringify(o, null, 0).slice(0, 200); } catch(e){}
const div = document.createElement('div');
div.className = 'sse-event ' + cls;
div.innerHTML = `<span class="ts">${new Date().toISOString().slice(11,19)}</span> <span class="ev">${event}:</span> ${txt.replace(/</g,'&lt;')}`;
log.insertBefore(div, log.firstChild);
while (log.children.length > 100) log.removeChild(log.lastChild);
function routeToRoom(dataStr){
let o={};try{o=JSON.parse(dataStr);}catch(e){return;}
const txt=o.text||o.message||o.content||o.topic||o.intent||'';
if(!txt)return;
let roomId='ops';
const lower=(JSON.stringify(o)+txt).toLowerCase();
if(/strateg|roadmap|budget|vision/.test(lower))roomId='strategy';
else if(/business|ethica|hcp|pipeline|crm|paperclip/.test(lower))roomId='business';
else if(/ollama|qdrant|llm|model|cascade|deerflow|searxng/.test(lower))roomId='ia';
const room=ROOMS.find(r=>r.id===roomId);
const agent=room.agents[Math.floor(Math.random()*room.agents.length)].name;
popBubble(roomId,agent,txt.slice(0,100));
}
function refreshAll(){
loadSocialSignals();
const activeTopic = document.querySelector('#onetoone-topics .topic-pill.active');
if (activeTopic) load1to1(activeTopic.dataset.topic);
loadMulti();
function addSSE(event,data,cls){
const log=document.getElementById('sse-log');
const now=Date.now();
sseLastMinute.push(now);
sseLastMinute=sseLastMinute.filter(t=>t>now-60000);
document.getElementById('kpi-sse').textContent=sseLastMinute.length;
let txt=data;
try{const o=JSON.parse(data);txt=JSON.stringify(o,null,0).slice(0,200);}catch(e){}
const div=document.createElement('div');
div.className='sse-event '+cls;
div.innerHTML=`<span class="ts">${new Date().toISOString().slice(11,19)}</span> <span class="ev">${event}:</span> ${txt.replace(/</g,'&lt;')}`;
log.insertBefore(div,log.firstChild);
while(log.children.length>100)log.removeChild(log.lastChild);
}
// Load 1-to-1 topics on tab activation
document.querySelector('[data-tab="onetoone"]').addEventListener('click', () => {
if (document.getElementById('onetoone-topics').innerText.includes('Loading')) {
const topics = (socialData && socialData.topics) || ['B2B SaaS conversion','LinkedIn outbound','pharma digital','AI automation','email deliverability'];
const host = document.getElementById('onetoone-topics');
host.innerHTML = topics.slice(0,8).map((t,i) => `<a class="topic-pill ${i===0?'active':''}" data-topic="${t}">${t}</a>`).join('');
host.querySelectorAll('.topic-pill').forEach(p => {
p.addEventListener('click', (e) => {
e.preventDefault();
host.querySelectorAll('.topic-pill').forEach(x => x.classList.remove('active'));
p.classList.add('active');
load1to1(p.dataset.topic);
});
});
load1to1(topics[0]);
}
});
document.querySelector('[data-tab="multi"]').addEventListener('click', () => {
if (document.getElementById('multi-feed').innerText.includes('Loading')) loadMulti();
});
document.querySelector('[data-tab="live"]').addEventListener('click', () => {
if (document.getElementById('sse-log').children.length < 2) startSSE();
});
function refreshAll(){loadSocialSignals();buildRooms();}
window.addEventListener('DOMContentLoaded', () => {
loadSocialSignals();
startSSE();
document.querySelector('[data-tab="onetoone"]').addEventListener('click',()=>{
if(document.getElementById('onetoone-topics').innerText.includes('Loading')){
const topics=(socialData&&socialData.topics)||['B2B SaaS conversion','LinkedIn outbound','pharma digital','AI automation','email deliverability'];
const host=document.getElementById('onetoone-topics');
host.innerHTML=topics.slice(0,8).map((t,i)=>`<a class="topic-pill ${i===0?'active':''}" data-topic="${t}">${t}</a>`).join('');
host.querySelectorAll('.topic-pill').forEach(p=>{p.addEventListener('click',e=>{e.preventDefault();host.querySelectorAll('.topic-pill').forEach(x=>x.classList.remove('active'));p.classList.add('active');load1to1(p.dataset.topic);});});
load1to1(topics[0]);
}
});
document.querySelector('[data-tab="multi"]').addEventListener('click',()=>{if(document.getElementById('multi-feed').innerText.includes('Loading'))loadMulti();});
document.querySelector('[data-tab="live"]').addEventListener('click',()=>{if(document.getElementById('sse-log').children.length<2)startSSE();});
window.addEventListener('DOMContentLoaded',()=>{
buildRooms();
loadSocialSignals();
startSSE();
setInterval(simulateRoomActivity,3500);
});
</script>
</body>

View File

@@ -1,6 +1,6 @@
{
"agent": "V41_Risk_Escalation",
"ts": "2026-04-24T01:15:02+02:00",
"ts": "2026-04-24T01:30:04+02:00",
"dg_alerts_active": 7,
"wevia_life_stats_preview": "{
"ok": true,

View File

@@ -1,6 +1,6 @@
{
"agent": "V45_Leads_Sync",
"ts": "2026-04-24T01:20:03+02:00",
"ts": "2026-04-24T01:30:05+02:00",
"paperclip_total": 48,
"active_customer": 4,
"warm_prospect": 5,

View File

@@ -1,11 +1,11 @@
{
"agent": "V54_Risk_Monitor_Live",
"ts": "2026-04-24T01:00:05+02:00",
"ts": "2026-04-24T01:30:05+02:00",
"critical_risks": {
"RW01_pipeline_vide": {
"pipeline_keur": 0,
"mql_auto": 17,
"residual_risk_pct": 83,
"mql_auto": 20,
"residual_risk_pct": 80,
"trend": "mitigation_V42_V45_active"
},
"RW02_dependance_ethica": {
@@ -22,7 +22,7 @@
},
"RW12_burnout": {
"agents_cron_active": 15,
"load_5min": "35.05",
"load_5min": "7.8",
"automation_coverage_pct": 70,
"residual_risk_pct": 60,
"trend": "V52_goldratt_options_active"

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,15 @@
{
"generated_at": "2026-04-24T01:25:01.397982",
"generated_at": "2026-04-24T01:35:01.452729",
"stats": {
"total": 49,
"pending": 27,
"total": 48,
"pending": 23,
"kaouther_surfaced": 18,
"chrome_surfaced": 5,
"notif_only_done": 0,
"autofix_archived": 0,
"cerebras_archived": 0,
"older_3d_archived": 1,
"unknown": 1,
"older_3d_archived": 0,
"unknown": 0,
"errors": 0
},
"actions": [

View File

@@ -0,0 +1,171 @@
#!/bin/bash
# Doctrine 157: WEVIA peut patcher des fichiers via sub-agent IA (Cerebras/Mistral).
# Format message: "patch file PATH :: description du changement"
# Reads from /tmp/wevia-last-msg.log (doctrine 152 fix)
# Safety: whitelist paths, GOLD backup, lint, rollback, rate limit
set -u
# Read message (doctrine 152 fix)
MSG="${1:-}"
if [ -z "$MSG" ] && [ -f /tmp/wevia-last-msg.log ]; then
MSG=$(cat /tmp/wevia-last-msg.log 2>/dev/null | tr -d '\n' | head -c 3000)
fi
# Parse
PATH_TARGET=$(echo "$MSG" | awk -F' :: ' '{print $1}' | sed 's/patch file //I' | sed 's/ameliore le fichier //I' | tr -d '[:space:]')
DESC=$(echo "$MSG" | awk -F' :: ' '{print $2}')
# Validation
if [ -z "$PATH_TARGET" ] || [ -z "$DESC" ]; then
echo "{\"ok\":false,\"error\":\"format: patch file PATH :: description\"}"
exit 0
fi
# Normalize path: prepend /var/www/html if not absolute
case "$PATH_TARGET" in
/*) ;;
*) PATH_TARGET="/var/www/html/$PATH_TARGET" ;;
esac
# Whitelist safety
ALLOWED=0
for prefix in "/var/www/html/" "/opt/wevia-brain/" "/opt/weval-l99/"; do
case "$PATH_TARGET" in "$prefix"*) ALLOWED=1; break ;; esac
done
if [ $ALLOWED -eq 0 ]; then
echo "{\"ok\":false,\"error\":\"path not in whitelist\",\"path\":\"$PATH_TARGET\"}"
exit 0
fi
# File must exist
if [ ! -f "$PATH_TARGET" ]; then
echo "{\"ok\":false,\"error\":\"file not found\",\"path\":\"$PATH_TARGET\"}"
exit 0
fi
# Rate limit (max 5 per hour)
RATE_FILE=/tmp/wevia-patch-rate.log
NOW=$(date +%s)
HOUR_AGO=$((NOW - 3600))
if [ -f "$RATE_FILE" ]; then
RECENT=$(awk -v t="$HOUR_AGO" '$1 > t' "$RATE_FILE" | wc -l)
if [ "$RECENT" -ge 5 ]; then
echo "{\"ok\":false,\"error\":\"rate limit: 5 patches/hour max\"}"
exit 0
fi
fi
echo "$NOW $PATH_TARGET" >> "$RATE_FILE"
# Size check (<200KB source for agent prompt)
SIZE=$(stat -c%s "$PATH_TARGET" 2>/dev/null)
if [ "$SIZE" -gt 200000 ]; then
echo "{\"ok\":false,\"error\":\"file too large for agent patch\",\"size\":$SIZE,\"max\":200000}"
exit 0
fi
# GOLD backup
TS=$(date +%Y%m%d-%H%M%S)
BACKUP="/var/www/html/vault-gold/opus/$(basename $PATH_TARGET).doctrine157-patch-${TS}.bak"
sudo mkdir -p "$(dirname $BACKUP)" 2>/dev/null
sudo cp "$PATH_TARGET" "$BACKUP"
# Capture file content (truncated to 150K for prompt)
CONTENT=$(head -c 150000 "$PATH_TARGET" | base64 -w0)
# Read Cerebras key
CEREBRAS_KEY=$(grep -oE "csk-[a-z0-9]+" /opt/wevads/vault/credentials.php 2>/dev/null | head -1)
if [ -z "$CEREBRAS_KEY" ]; then
echo "{\"ok\":false,\"error\":\"cerebras key not found in vault\"}"
exit 0
fi
# Build prompt
PROMPT="You are a surgical code editor. Given the following file content and a description of the desired change, return ONLY the MODIFIED FULL file content. No explanation, no markdown, no code fences, just raw file content.
FILE: $PATH_TARGET
CHANGE REQUESTED: $DESC
CURRENT CONTENT:
$(echo "$CONTENT" | base64 -d | head -c 120000)
MODIFIED CONTENT:"
# Call Cerebras (cheap + fast model)
RESPONSE=$(curl -sk -m 60 -X POST "https://api.cerebras.ai/v1/chat/completions" \
-H "Authorization: Bearer $CEREBRAS_KEY" \
-H "Content-Type: application/json" \
-d "$(python3 -c "
import json, sys, os
prompt = '''$PROMPT'''
payload = {
'model': 'qwen-3-235b-a22b-instruct-2507',
'messages': [{'role': 'user', 'content': prompt}],
'temperature': 0.1,
'max_tokens': 8000
}
print(json.dumps(payload))
" 2>/dev/null)" 2>&1)
# Extract new content
NEW_CONTENT=$(echo "$RESPONSE" | python3 -c "
import sys, json
try:
d = json.loads(sys.stdin.read())
print(d['choices'][0]['message']['content'], end='')
except Exception as e:
print('ERR:'+str(e))
")
if echo "$NEW_CONTENT" | head -c 10 | grep -q "ERR:"; then
echo "{\"ok\":false,\"error\":\"cerebras call failed\",\"backup\":\"$BACKUP\",\"preview\":$(echo "$NEW_CONTENT" | head -c 200 | python3 -c 'import sys,json;print(json.dumps(sys.stdin.read()))')}"
exit 0
fi
# Write new content to temp
TMP_NEW="/tmp/patch-candidate-$$.new"
echo "$NEW_CONTENT" > "$TMP_NEW"
# Lint if PHP/JS/HTML
LINT_OK=1
LINT_MSG=""
case "$PATH_TARGET" in
*.php)
LINT_MSG=$(php -l "$TMP_NEW" 2>&1)
echo "$LINT_MSG" | grep -q "No syntax errors" || LINT_OK=0
;;
*.html|*.htm)
# Basic check: must have html tag (if originally had)
ORIG_HAS_HTML=$(grep -c "<html" "$PATH_TARGET" 2>/dev/null)
NEW_HAS_HTML=$(grep -c "<html" "$TMP_NEW" 2>/dev/null)
if [ "$ORIG_HAS_HTML" -gt 0 ] && [ "$NEW_HAS_HTML" -eq 0 ]; then
LINT_OK=0
LINT_MSG="html tag disappeared"
fi
;;
esac
if [ $LINT_OK -eq 0 ]; then
rm -f "$TMP_NEW"
echo "{\"ok\":false,\"error\":\"lint failed\",\"lint_msg\":\"$LINT_MSG\",\"backup\":\"$BACKUP\"}"
exit 0
fi
# Apply: unlock if chattr +i, copy, relock
WAS_LOCKED=0
if lsattr "$PATH_TARGET" 2>/dev/null | grep -q "i"; then
WAS_LOCKED=1
sudo chattr -i "$PATH_TARGET"
fi
sudo cp "$TMP_NEW" "$PATH_TARGET"
rm -f "$TMP_NEW"
if [ $WAS_LOCKED -eq 1 ]; then
sudo chattr +i "$PATH_TARGET"
fi
NEW_SIZE=$(stat -c%s "$PATH_TARGET")
NEW_MD5=$(md5sum "$PATH_TARGET" | cut -d' ' -f1)
echo "{\"ok\":true,\"doctrine\":\"157\",\"path\":\"$PATH_TARGET\",\"old_size\":$SIZE,\"new_size\":$NEW_SIZE,\"new_md5\":\"$NEW_MD5\",\"backup\":\"$BACKUP\",\"description\":\"$DESC\",\"agent\":\"cerebras-qwen-3-235b-a22b-instruct-2507\",\"ts\":\"$(date -Iseconds)\"}"

View File

@@ -1,13 +1,13 @@
{
"ok": true,
"source": "truth_registry_unified",
"built_at": "2026-04-23T23:20:02+00:00",
"built_at": "2026-04-23T23:30:02+00:00",
"agents_count": 1000,
"agents_total": 1000,
"skills_count": 20176,
"skills_total": 20176,
"intents_count": 2324,
"intents_total": 2324,
"intents_count": 2325,
"intents_total": 2325,
"brains_count": 25,
"doctrines_count": 19,
"dashboards_count": 118,
@@ -20,7 +20,7 @@
"counts": {
"agents": 1000,
"agents_total_live": 950,
"intents": 2324,
"intents": 2325,
"skills_total": 20176,
"brains": 25,
"doctrines": 19,

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html><head><title>Test Patch Target</title></head>
<body>
<h1>Bonjour autonome doctrine 157 active</h1>
<p>This is a test file.</p>
<div class="card">
<p>signature wevia</p>
</div>
</body>
</html>

View File

@@ -1,5 +1,5 @@
{
"timestamp": "2026-04-24T01:00:23",
"timestamp": "2026-04-24T01:30:19",
"features": {
"total": 36,
"pass": 35
@@ -13,7 +13,7 @@
"score": 97.2,
"log": [
"=== UX AGENT v1.0 ===",
"Time: 2026-04-24 01:00:02",
"Time: 2026-04-24 01:30:02",
" core: 4/4",
" layout: 3/4",
" interaction: 6/6",

View File

@@ -1,7 +1,7 @@
{
"ok": true,
"version": "V83-business-kpi",
"ts": "2026-04-23T23:28:09+00:00",
"ts": "2026-04-23T23:30:33+00:00",
"summary": {
"total_categories": 8,
"total_kpis": 64,

View File

@@ -12,24 +12,39 @@ function wevia_kaouther_send($msg) {
$out = ['intent' => 'send_kaouther', 'tool' => 'kaouther_gmail_drafts'];
$to = 'kaouther.najar@ethica.ma';
$to = 'kaouther.najar@groupe-ethica.com';
$from = 'ymahboub@weval-consulting.com';
// 3 emails paliers
$emails = [
[
'subject' => 'Contre-proposition pharma DH — Palier Premium (1,5 DH)',
'body_short' => "Bonjour Kaouther,\n\nContre-proposition 1,5 DH/contact palier Premium (volume sélectif 0-20K ciblés, triple canal email+WhatsApp+SMS, opt-in Loi 09-08, support dédié).\n\nBase: 146,668 HCPs validés +20K en 7 jours. Stack souverain Maroc.\n\nVoir détails complets sur https://weval-consulting.com/kaouther-compose.html\n\nBien cordialement,\nYacine"
],
[
'subject' => 'Contre-proposition pharma DH — Palier Standard (1,2 DH)',
'body_short' => "Bonjour Kaouther,\n\nPalier Standard 1,2 DH/contact pour volume récurrent 20-60K, bi-canal email+WhatsApp, reporting hebdo.\n\nSweet spot campagnes trimestrielles. DZ 107K / MA 20K / TN 18K disponibles.\n\nDétails: https://weval-consulting.com/kaouther-compose.html\n\nCordialement,\nYacine"
],
[
'subject' => 'Contre-proposition pharma DH — Palier Volume (1,0 DH)',
'body_short' => "Bonjour Kaouther,\n\nPalier Volume 1,0 DH/contact (60K+ contacts, 6 mois min, email principal + WhatsApp +0,2 DH option).\n\nCouvre coûts infra+DB. En-dessous perte.\n\nDétails: https://weval-consulting.com/kaouther-compose.html\n\nCordialement,\nYacine"
],
];
// V48 LIVE FETCH — doctrine root cause (zero hardcode)
$live_url = 'http://localhost/api/kaouther-drafts-live.php';
$live_json = @file_get_contents($live_url);
$live = $live_json ? @json_decode($live_json, true) : null;
if (!$live || empty($live['drafts'])) {
// Fallback minimal if live fetch fails
$emails = [[
'subject' => 'Kaouther Ethica - draft unavailable',
'body_short' => 'Live fetch failed. Check /api/kaouther-drafts-live.php'
]];
} else {
// Adapt V48 live JSON structure to legacy $emails format
$emails = [];
if (isset($live['drafts']) && is_array($live['drafts'])) {
foreach ($live['drafts'] as $d) {
$emails[] = [
'subject' => $d['subject'] ?? '',
'body_short' => $d['body'] ?? $d['body_short'] ?? ''
];
}
}
// If V48 has single draft (not array of 3 tiers), use it directly
if (empty($emails) && isset($live['subject'])) {
$emails[] = [
'subject' => $live['subject'],
'body_short' => '' // Body is in gmail_url already encoded
];
}
}
// Générer URLs Gmail compose
$drafts = [];

View File

@@ -1,6 +1,6 @@
{
"version": "1.0",
"built_at": "2026-04-23T23:20:02+00:00",
"built_at": "2026-04-23T23:30:02+00:00",
"purpose": "WEVIA TRUTH REGISTRY · source de vérité unique pour agents/intents/skills/brains/doctrines",
"consumers": [
"/api/wevia-master-api.php",
@@ -16916,13 +16916,13 @@
]
},
"intents": {
"count": 2324,
"count": 2325,
"arena_declared": 310,
"arena_wired": 224,
"arena_gap": 86,
"arena_version": "Wave 115",
"by_status": {
"EXECUTED": 1964,
"EXECUTED": 1965,
"DISABLED": 17,
"ACTIVATED": 229,
"PENDING_APPROVAL": 10,
@@ -16940,7 +16940,7 @@
"APPROVED_BY_OPUS_20AVR_V4": 1
},
"by_domain": {
"general": 1992,
"general": 1993,
"site_web": 14,
"agents": 239,
"wevads_pipeline": 25,
@@ -17471,6 +17471,21 @@
"description": "",
"file": "/api/wired-pending/intent-opus4-ACTIVATED-git_tag_create.php"
},
{
"name": "kaouther_drafts",
"domain": "general",
"status": "EXECUTED",
"triggers": [
"kaouther status",
"email kaouther",
"drafts kaouther",
"ethica emails",
"kaouther drafts"
],
"source": "opus5-kaouther-drafts-17avr-1648",
"description": "Retourne les drafts Kaouther finalisés (1 draft version validée Yacine)",
"file": "/api/wired-pending/intent-opus4-ACTIVATED-kaouther_drafts.php"
},
{
"name": "kaouther_drafts_staleness_check",
"domain": "general",
@@ -28142,21 +28157,6 @@
"description": "Agent Kanban Flow Optimizer · 3 sources multi-registry",
"file": "/api/wired-pending/intent-opus4-kanban_flow_optimizer.php"
},
{
"name": "kaouther_drafts",
"domain": "general",
"status": "EXECUTED",
"triggers": [
"kaouther status",
"email kaouther",
"drafts kaouther",
"ethica emails",
"kaouther drafts"
],
"source": "opus5-kaouther-drafts-17avr-1648",
"description": "Retourne les drafts Kaouther finalisés (1 draft version validée Yacine)",
"file": "/api/wired-pending/intent-opus4-kaouther_drafts.php"
},
{
"name": "kaouther_send_ready",
"domain": "general",
@@ -48941,6 +48941,18 @@
"description": "V62 orchestrated navigation + francais avec accents",
"file": "/api/wired-pending/intent-opus4-wtp_hub_central.php"
},
{
"name": "wtp_integrate_enriched_cards",
"domain": "general",
"status": "EXECUTED",
"triggers": [
"integrer enrichie cards wtp renderAllPages premium",
"cards thumb title description integration wtp"
],
"source": "wevia-autowire-via-chat-doctrine152",
"description": "",
"file": "/api/wired-pending/intent-opus4-wtp_integrate_enriched_cards.php"
},
{
"name": "wtp_official",
"domain": "general",
@@ -50326,7 +50338,7 @@
"score": 100,
"total": 153
},
"apis_php_count": 1098,
"apis_php_count": 1099,
"autonomy_score": 99.5,
"autonomy_level": "GODMODE"
}

View File

@@ -0,0 +1,15 @@
<?php
return array (
'name' => 'wevia_patch_file_with_agent',
'triggers' =>
array (
0 => 'patch file',
1 => 'ameliore le fichier',
2 => 'modify file with agent',
),
'cmd' => '/var/www/html/api/handlers/patch-file-with-agent.sh',
'status' => 'EXECUTED',
'created_at' => '2026-04-24T01:30:00+00:00',
'source' => 'opus-phase18-doctrine157',
'doctrine' => '157',
);

File diff suppressed because one or more lines are too long