Files
html/wevia-master.html
opus e30ddf5007
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
auto-sync via WEVIA git_sync_all intent 2026-04-20T13:11:38+02:00
2026-04-20 13:11:38 +02:00

436 lines
30 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVIA Master AI</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,400;0,500;0,700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<style>
:root{--bg:#080c14;--s1:#0d1219;--s2:#131b27;--s3:#1a2435;--bd:rgba(255,255,255,.06);--tx:#e4e8f0;--dim:#8899af;--dim2:#556677;--ac:#10b981;--ac2:rgba(16,185,129,.1);--rd:#ef4444;--bl:#3b82f6;--vi:#8b5cf6;--w:#f59e0b;--cy:#06b6d4;--mono:'JetBrains Mono',monospace;--r:10px}
*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%;overflow:hidden}
body{background:var(--bg);color:var(--tx);font-family:'DM Sans',sans-serif;display:flex}
.sidebar{width:230px;background:var(--s1);border-right:1px solid var(--bd);display:flex;flex-direction:column;flex-shrink:0}
.sb-head{padding:14px 16px;border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between}
.sb-head h2{font-size:17px;font-weight:700;color:var(--ac)}
.sb-head small{font-size:9px;color:var(--dim2)}
.sb-nav{flex:1;overflow-y:auto;padding:6px 8px}
.sb-nav::-webkit-scrollbar{width:3px}
.sb-nav::-webkit-scrollbar-thumb{background:var(--s3);border-radius:2px}
.sb-label{padding:10px 10px 4px;font-family:var(--mono);font-size:8px;text-transform:uppercase;letter-spacing:1.5px;color:var(--dim2)}
.sb-item{display:flex;align-items:center;gap:7px;padding:6px 10px;border-radius:7px;cursor:pointer;font-size:12px;color:var(--dim);transition:.15s;border:none;background:none;width:100%;text-align:left}
.sb-item:hover{background:var(--ac2);color:var(--tx);padding-left:14px}
.sb-item:active{transform:scale(.98)}
.sb-item .ic{font-size:13px;width:18px;text-align:center;flex-shrink:0}
.sb-foot{padding:10px 14px;border-top:1px solid var(--bd);font-size:9px;color:var(--dim2);line-height:1.6}
.dot{width:6px;height:6px;border-radius:50%;display:inline-block;animation:pulse 2s infinite}
.dot-g{background:var(--ac)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
.main{flex:1;display:flex;flex-direction:column;min-width:0}
.top{height:44px;background:var(--s1);border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between;padding:0 18px;flex-shrink:0}
.top h3{font-size:13px;font-weight:700;display:flex;align-items:center;gap:8px}
.top-r{display:flex;align-items:center;gap:10px;font-size:11px;color:var(--dim)}
.top-r a{color:var(--dim);text-decoration:none;font-size:10px}
.msgs{flex:1;overflow-y:auto;padding:16px 20px;display:flex;flex-direction:column;gap:10px}
.msgs::-webkit-scrollbar{width:4px}
.msgs::-webkit-scrollbar-thumb{background:var(--s3);border-radius:2px}
.msg{max-width:78%;padding:10px 14px;border-radius:12px;font-size:13px;line-height:1.65;animation:fadeUp .25s ease;position:relative}
@keyframes fadeUp{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
.msg-u{align-self:flex-end;background:linear-gradient(135deg,#059669,#10b981);color:#fff;border-bottom-right-radius:3px}
.msg-a{align-self:flex-start;background:var(--s2);border:1px solid var(--bd);border-bottom-left-radius:3px}
.msg-a pre{background:var(--bg);border:1px solid var(--bd);border-radius:6px;padding:10px;margin:8px 0;overflow-x:auto;font-family:var(--mono);font-size:11px;line-height:1.5;position:relative}
.msg-a code{font-family:var(--mono);font-size:11px;background:rgba(255,255,255,.04);padding:1px 4px;border-radius:3px}
.msg-a strong{color:var(--ac)}
.msg-meta{display:flex;align-items:center;gap:8px;margin-top:5px;font-size:9px;color:var(--dim2)}
.msg-meta .engine{color:var(--ac);font-weight:700;font-family:var(--mono)}
.copy-btn{position:absolute;top:6px;right:6px;font-family:var(--mono);font-size:8px;padding:2px 6px;border-radius:4px;border:1px solid var(--bd);background:var(--s3);color:var(--dim);cursor:pointer;opacity:0;transition:.2s}
.msg:hover .copy-btn{opacity:1}
.copy-btn:hover{color:var(--ac);border-color:var(--ac)}
.typing{display:flex;gap:4px;padding:10px 14px;align-self:flex-start}
.typing span{width:5px;height:5px;background:var(--dim2);border-radius:50%;animation:bounce .5s infinite alternate}
.typing span:nth-child(2){animation-delay:.12s}
.typing span:nth-child(3){animation-delay:.24s}
@keyframes bounce{to{transform:translateY(-5px);background:var(--ac)}}
.welcome{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:30px}
.welcome h1{font-size:32px;font-weight:700;background:linear-gradient(135deg,var(--ac),var(--cy));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.welcome p{color:var(--dim);font-size:12px;max-width:450px;text-align:center}
.wcards{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;width:100%;max-width:680px}
.wcard{background:var(--s2);border:1px solid var(--bd);border-radius:var(--r);padding:12px;cursor:pointer;text-align:center;transition:.2s}
.wcard:hover{border-color:rgba(16,185,129,.3);transform:translateY(-2px);box-shadow:0 4px 16px rgba(16,185,129,.06)}
.wcard .em{font-size:20px;margin-bottom:4px}
.wcard b{font-size:11px;display:block}
.wcard small{font-size:9px;color:var(--dim2)}
.input-wrap{padding:10px 18px;border-top:1px solid var(--bd);background:var(--s1);flex-shrink:0}
.input-row{display:flex;gap:8px;max-width:780px;margin:0 auto;position:relative}
.input-row input,.input-row textarea{flex:1;background:var(--s3);border:1px solid var(--bd);border-radius:var(--r);padding:11px 14px 11px 40px;color:var(--tx);font-size:13px;font-family:'DM Sans',sans-serif;outline:none;transition:.2s}
.input-row input:focus,.input-row textarea:focus{border-color:var(--ac);box-shadow:0 0 0 2px var(--ac2)}
.input-row input::placeholder,.input-row textarea::placeholder{color:var(--dim2)}
.attach-btn{position:absolute;left:10px;top:50%;transform:translateY(-50%);border:none;background:none;color:var(--dim);cursor:pointer;padding:4px;transition:.2s}
.attach-btn:hover{color:var(--ac)}
.progress-wrap{margin:8px 0;display:none}
.progress-bar{height:3px;background:rgba(255,255,255,.08);border-radius:3px;overflow:hidden}
.progress-fill{height:100%;background:linear-gradient(90deg,var(--ac),#a78bfa);border-radius:3px;transition:width .3s ease;width:0%}
.progress-status{display:flex;justify-content:space-between;margin-top:4px;font:500 10px var(--mf);color:var(--dim);opacity:.7}
.send-btn{width:40px;height:40px;border-radius:var(--r);background:var(--ac);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s;flex-shrink:0}
.send-btn:hover{filter:brightness(1.1);transform:scale(1.04)}
.send-btn:disabled{opacity:.3;cursor:default;transform:none}
.send-btn svg{width:16px;height:16px;fill:#fff}
.input-hint{text-align:center;font-size:9px;color:var(--dim2);margin-top:4px}
.drop-overlay{display:none;position:fixed;inset:0;z-index:100;background:rgba(16,185,129,.05);backdrop-filter:blur(4px);align-items:center;justify-content:center}
.drop-overlay.show{display:flex}
.drop-box{border:2px dashed var(--ac);border-radius:16px;padding:50px 70px;text-align:center;color:var(--ac);font-size:16px;font-weight:500}
.file-preview{display:flex;gap:6px;padding:6px 0;flex-wrap:wrap}
.file-chip{display:flex;align-items:center;gap:5px;background:var(--s3);border:1px solid var(--bd);border-radius:6px;padding:4px 8px;font-size:10px;color:var(--dim);font-family:var(--mono)}
.file-chip .rm{cursor:pointer;color:var(--rd);margin-left:3px}
@media(max-width:768px){.sidebar{display:none}.wcards{grid-template-columns:repeat(2,1fr)}}
/* LONG STREAM + GROS TEXTE */
#chatArea{max-height:calc(100vh - 200px);overflow-y:auto;scroll-behavior:smooth}
#chatArea .msg-content{max-height:none !important;overflow:visible !important;white-space:pre-wrap;word-break:break-word;font-size:13px;line-height:1.6}
#chatArea .msg-content pre{max-height:600px;overflow:auto;background:var(--s2,#1a1a1f);padding:12px;border-radius:8px;font-size:12px}
#chatArea .msg-content code{font-family:'JetBrains Mono',monospace;font-size:12px}
.msg.assistant{animation:fadeIn .3s ease}
@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1}}
</style>
</head>
<body>
<div class="sidebar" id="sidebar">
<div class="sb-head"><h2>WEVIA</h2><small>Master AI v4</small></div>
<div class="sb-nav">
<div class="sb-label">IA Agents</div>
<button class="sb-item" onclick="q('ethica combien de HCP par pays')"><span class="ic">💊</span>Ethica HCP</button>
<button class="sb-item" onclick="q('deerflow recherche tendances')"><span class="ic">🦌</span>DeerFlow</button>
<button class="sb-item" onclick="q('paperclip status goals')"><span class="ic">📎</span>Paperclip</button>
<button class="sb-item" onclick="q('consensus stratégie')"><span class="ic">⚖️</span>Consensus</button>
<button class="sb-item" onclick="q('blade desktop status')"><span class="ic"></span>Blade IA</button>
<button class="sb-item" onclick="q('director supervision')"><span class="ic">👁️</span>Director</button>
<button class="sb-item" onclick="q('wedroid backend diagnostic')"><span class="ic">🔧</span>WEDROID</button>
<button class="sb-item" onclick="q('openclaw ollama models')"><span class="ic">🐙</span>OpenClaw</button>
<button class="sb-item" onclick="q('wevcode assistant code')"><span class="ic">⚙️</span>WEVCODE</button>
<button class="sb-item" onclick="q('nuclei scan sécurité')"><span class="ic">🔬</span>Nuclei</button>
<div class="sb-label">Actions</div>
<button class="sb-item" onclick="q('audit complet RAM disk Docker')"><span class="ic">🔍</span>Audit Complet</button>
<button class="sb-item" onclick="q('auto-fix repare tout')"><span class="ic">🔧</span>Auto-Fix</button>
<button class="sb-item" onclick="q('lance nonreg')"><span class="ic">🧪</span>NonReg</button>
<button class="sb-item" onclick="q('benchmark classement')"><span class="ic">📊</span>Benchmark</button>
<button class="sb-item" onclick="q('sécurisé firewall')"><span class="ic">🛡️</span>Security</button>
<button class="sb-item" onclick="q('nettoie le disque')"><span class="ic">🧹</span>Disk Clean</button>
<button class="sb-item" onclick="q('lance guardian')"><span class="ic">🔒</span>Guardian</button>
<button class="sb-item" onclick="q('git push status')"><span class="ic">📂</span>Git Push</button>
<button class="sb-item" onclick="q('consolide les crons')"><span class="ic"></span>Crons</button>
<button class="sb-item" onclick="q('test fonctionnel')"><span class="ic"></span>Func Test</button>
<button class="sb-item" onclick="q('vacuum ethica')"><span class="ic">💊</span>Vacuum DB</button>
<div class="sb-label">Outils</div>
<button class="sb-item" onclick="q('wiring connexion agents')"><span class="ic">🔌</span>Wiring Map</button>
<button class="sb-item" onclick="q('rag status qdrant')"><span class="ic">🧠</span>RAG Status</button>
<button class="sb-item" onclick="q('cherche weval consulting')"><span class="ic">🔎</span>Recherche Web</button>
<button class="sb-item" onclick="q('traduis en anglais bonjour')"><span class="ic">🌍</span>Traduction</button>
<button class="sb-item" onclick="q('diagramme architecture')"><span class="ic">📐</span>Diagramme</button>
<button class="sb-item" onclick="q('scraping extraction web')"><span class="ic">🕷️</span>Scraping</button>
<button class="sb-item" onclick="q('port scan ouverts')"><span class="ic">🔌</span>Port Scan</button>
<button class="sb-item" onclick="q('value chain processus')"><span class="ic">📈</span>Value Chain</button>
</div>
<div class="sb-foot">
<span class="dot dot-g"></span> <span id="pc">7</span> providers | 0€<br>
<span id="fs">412 tools | 890 agents · 2484 skills · 16K vectors</span>
</div>
</div>
<div class="main">
<div class="top">
<h3><span class="dot dot-g"></span> WEVIA Master AI</h3>
<div class="top-r"><span id="st">Connecté</span> · <a href="/wevia-master.html">Legacy</a> · <a href="/weval-wiring.html">Wiring</a> · <a href="/ai-benchmark.html">Benchmark</a></div>
</div>
<div class="msgs" id="msgs">
<div class="welcome" id="welcome">
<h1>WEVIA</h1>
<p>IA souveraine · 412 tools · 890 agents · 2484 skills · 14K vectors · 13 cascade · 0€<br>Tapez une commande ou cliquez un raccourci</p>
<div class="wcards">
<div class="wcard" onclick="q('audit complet RAM disk Docker')"><div class="em">🔍</div><b>Audit Infra</b><small>RAM · Disk · Docker</small></div>
<div class="wcard" onclick="q('ethica combien de HCP')"><div class="em">💊</div><b>Ethica</b><small>141K+ HCPs</small></div>
<div class="wcard" onclick="q('créé une API REST Flask Python avec auth JWT')"><div class="em">⚙️</div><b>Code Gen</b><small>Python · Flask · JWT</small></div>
<div class="wcard" onclick="q('benchmark classement IA')"><div class="em">📊</div><b>Benchmark</b><small>39 AIs ranked</small></div>
<div class="wcard" onclick="q('auto-fix repare tout')"><div class="em">🔧</div><b>Auto-Fix</b><small>Detect + Correct</small></div>
<div class="wcard" onclick="q('deerflow recherche tendances LLM 2026')"><div class="em">🦌</div><b>DeerFlow</b><small>Deep Research</small></div>
<div class="wcard" onclick="q('consensus quelle stratégie IA adopter')"><div class="em">⚖️</div><b>Consensus</b><small>Multi-IA MoA</small></div>
<div class="wcard" onclick="q('sécurisé firewall auth')"><div class="em">🛡️</div><b>Security</b><small>HMAC · CORS · Nuclei</small></div>
</div>
</div>
</div>
<div class="drop-overlay" id="dropZone"><div class="drop-box">📎 Déposez vos fichiers ici<br><small>Images, PDF, code — WEVIA analyse tout</small></div></div>
<div class="input-wrap">
<div class="input-row">
<button class="attach-btn" onclick="document.getElementById('fileIn').click()" title="Joindre un fichier">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>
</button>
<input type="file" id="fileIn" style="display:none" multiple>
<textarea id="input" placeholder="Demandez à WEVIA... (images, fichiers, long texte)" autocomplete="off" rows="1" style="resize:none;overflow:hidden" oninput="this.style.height='auto';this.style.height=Math.min(this.scrollHeight,120)+'px'"></textarea>
<button class="send-btn" id="sendBtn" onclick="send()">
<svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
</button>
</div>
<div class="file-preview" id="filePrev"></div>
<div class="input-hint">Enter envoyer · Shift+Enter nouvelle ligne · 📎 Fichiers</div>
</div>
</div>
<script>
const $=id=>document.getElementById(id),msgs=$('msgs'),inp=$('input'),stEl=$('st'),welc=$('welcome');
let busy=false,files=[],chatHistory=[],sessionId="s-"+Date.now();
inp.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey&&!busy){e.preventDefault();send()}});
function q(t){inp.value=t;send()}
// Drag & drop
document.addEventListener('dragover',e=>{e.preventDefault();$('dropZone').classList.add('show')});
document.addEventListener('dragleave',e=>{if(!e.relatedTarget)$('dropZone').classList.remove('show')});
document.addEventListener('drop',e=>{e.preventDefault();$('dropZone').classList.remove('show');addFiles(e.dataTransfer.files)});
$('fileIn').addEventListener('change',e=>addFiles(e.target.files));
async function filesToB64(){const r=[];for(const f of files){const b=await new Promise((ok,no)=>{const rd=new FileReader();rd.onload=()=>ok(rd.result.split(',')[1]);rd.onerror=no;rd.readAsDataURL(f)});r.push({name:f.name,type:f.type,size:f.size,data:b})}return r}
function addFiles(fl){for(const f of fl){files.push(f);const d=document.createElement('div');d.className='file-chip';d.innerHTML=`📄 ${f.name} <span class="rm" onclick="this.parentElement.remove()">✕</span>`;$('filePrev').appendChild(d)}}
function addMsg(text,type,meta){
if(welc)welc.style.display='none';
const d=document.createElement('div');d.className='msg msg-'+type;
if(type==='a'){
d.innerHTML=fmt(text);
const cb=document.createElement('button');cb.className='copy-btn';cb.textContent='Copier';
cb.onclick=()=>{navigator.clipboard.writeText(text);cb.textContent='✓';setTimeout(()=>cb.textContent='Copier',1500)};
d.appendChild(cb);
/* DELETE_BTN_V1 */
const db=document.createElement('button');db.className='copy-btn';db.style.marginLeft='6px';db.style.background='#3a1a1a';db.style.color='#ff6b6b';db.style.borderColor='#5a2a2a';db.textContent='Supprimer';
db.onclick=()=>{d.style.transition='opacity 0.2s';d.style.opacity='0';setTimeout(()=>d.remove(),200)};
d.appendChild(db);
if(meta){const m=document.createElement('div');m.className='msg-meta';m.innerHTML=`<span class="engine">${meta.engine||''}</span><span>${meta.time||''}</span>`;d.appendChild(m)}
}else d.textContent=text;
msgs.appendChild(d);msgs.scrollTop=msgs.scrollHeight;return d
}
function fmt(t){
t=t.replace(/```(\w*)\n?([\s\S]*?)```/g,'<pre><code>$2</code></pre>');
t=t.replace(/`([^`]+)`/g,'<code>$1</code>');
t=t.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>');
t=t.replace(/\n/g,'<br>');return t
}
function showTyping(){const d=document.createElement('div');d.className='typing';d.id='typ';d.innerHTML='<span></span><span></span><span></span>';msgs.appendChild(d);msgs.scrollTop=msgs.scrollHeight}
function hideTyping(){const t=$('typ');if(t)t.remove()}
// Progress bar
function showProgress(status, pct){
let pw=document.getElementById('pw');
if(!pw){
const d=document.createElement('div');d.id='pw';d.className='progress-wrap';
d.innerHTML='<div class="progress-bar"><div class="progress-fill" id="pf"></div></div><div class="progress-status"><span id="ps"></span><span id="pt"></span></div>';
const chat=document.getElementById('msgs');
chat.appendChild(d);chat.scrollTop=chat.scrollHeight;
pw=d;
}
pw.style.display='block';
document.getElementById('pf').style.width=pct+'%';
document.getElementById('ps').textContent=status;
const eta=pct<100?Math.max(1,Math.round((100-pct)/10))+'s':'';
document.getElementById('pt').textContent=eta?'~'+eta:'';
}
function hideProgress(){const pw=document.getElementById('pw');if(pw)pw.remove();}
async function send(){
const text=inp.value.trim();if(!text||busy)return;
busy=true;$('sendBtn').disabled=true;inp.value='';stEl.textContent='Réflexion...';
showProgress('Routing intent...', 5);
addMsg(text,'u');showTyping();
const t0=Date.now();
try{
// Try autonomous.php (SSE)
showProgress('Querying sovereign cascade...', 25);
// MULTIAGENT SSE ROUTING
if(text.match(/multiagent|tout finir|full scan|orchestr/i)){
try{
const sse=await fetch("/api/wevia-sse-orchestrator.php?msg="+encodeURIComponent(text),{signal:AbortSignal.timeout(3600000)});
const reader=sse.body.getReader();const dec=new TextDecoder();let buf="",parts=[];
while(true){const{done,value}=await reader.read();if(done)break;buf+=dec.decode(value,{stream:true});
const lines=buf.split("\n");buf=lines.pop();
for(const l of lines){if(!l.startsWith("data: "))continue;try{const d=JSON.parse(l.slice(6));
if(d.type==="agent"){parts.push("["+(d.id||d.name||"agent")+"] "+(d.result||d.text||d.output||""));hideTyping();addMsg(parts.join("\n"),"a",{engine:"SSE",time:(d.progress||0)+"%"});}
if(d.type==="done"){hideTyping();addMsg("ORCHESTRATOR: "+(d.agents||parts.length)+" agents\n"+parts.join("\n"),"a",{engine:"SSE-Orch",time:((Date.now()-t0)/1000).toFixed(1)+"s"});}
if(d.type==="phase2_priority"&&d.ux_guide){hideTyping();const g=d.ux_guide;let txt="**PHASE 2 CF-BYPASS — "+(d.intent||"?").toUpperCase()+"**\n\n";if(g.phase_2_status){txt+="**Etat actuel**\n";for(const[k,v]of Object.entries(g.phase_2_status))txt+="- "+k+": "+v+"\n";}if(g.preflight){txt+="\n**Pre-flight**\n";for(const[k,v]of Object.entries(g.preflight))txt+="- "+k+": "+v+"\n";}if(g.blockers&&g.blockers.length){txt+="\n**Blockers**: "+g.blockers.join(", ")+"\n";}else if(g.blockers){txt+="\n**Blockers**: aucun\n";}if(g.magic_word){txt+="\n**Mot magique a taper**: `"+g.magic_word+"`\n";}if(g.message){txt+="\n"+g.message+"\n";}if(g.next_steps&&g.next_steps.length){txt+="\n**Prochaines etapes**\n"+g.next_steps.map(s=>"- "+s).join("\n");}addMsg(txt,"a",{engine:"WEVIA-Phase2",time:((Date.now()-t0)/1000).toFixed(1)+"s"});}
if(d.type==="llm_synthesis"&&d.text){hideTyping();addMsg(d.text,"a",{engine:"WEVIA-Synth",time:((Date.now()-t0)/1000).toFixed(1)+"s"});}
}catch(e){}}
}busy=false;$("sendBtn").disabled=false;stEl.textContent="";return;
}catch(e){}
}
const res=await fetch('/api/wevia-master-api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:text,session:sessionId,history:chatHistory.slice(-10),attachments:await filesToB64()}),signal:AbortSignal.timeout(300000)});
hideTyping();
showProgress('Processing response...', 70);
const elapsed=((Date.now()-t0)/1000).toFixed(1)+'s';
let fullText='',engine='',intent='';
// Try JSON first (wevia-master-api returns direct JSON)
let raw='';
raw=await res.text();
for(const line of raw.split('\n')){
if(!line.startsWith('data: '))continue;
try{
const d=JSON.parse(line.slice(6));
if(d.type==='answer'){fullText=d.text||'';engine=d.engine||'';intent=d.intent||''}
else if(d.type==='chunk'||d.type==='llm_chunk'){fullText+=d.text||'';engine=d.engine||d.provider||engine}
else if(d.type==='thinking')stEl.textContent=d.step||'Analyse...'
else if(d.type==='exec_result'){fullText+=(d.desc||'')+': '+(d.output||'')+'\n';engine='Exec'}
else if(d.type==='exec'){engine=d.engine||'Resolver'}
else if(d.type==='token'){fullText+=d.content||'';engine=d.provider||engine}
else if(d.type==='start'){engine=d.provider||engine}
else if(d.type==='done')engine=d.provider||engine
}catch(e){}
}
if(!fullText){
// Fallback: try exec endpoint (JSON)
showProgress('Fallback exec...', 50);
const r2=await fetch('/api/wevia-full-exec.php?m='+encodeURIComponent(text),{signal:AbortSignal.timeout(120000)});
/* HTML_GUARD_V2_BATCH */ const _t_d2=await r2.text(); let d2=null; {var _q=(_t_d2||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d2={error:"[HTTP "+(r2.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d2=JSON.parse(_q)}catch(e){d2={error:"[JSON] "+e.message}}}}fullText=d2.response||'Pas de réponse';engine=d2.provider||'Exec'
}
addMsg(fullText,'a',{engine,time:elapsed});
chatHistory.push({role:"user",content:text},{role:"assistant",content:fullText});
}catch(err){
hideTyping();
try{showProgress('Fallback exec...', 50);
const r2=await fetch('/api/wevia-full-exec.php?m='+encodeURIComponent(text));/* HTML_GUARD_V2_BATCH */ const _t_d2=await r2.text(); let d2=null; {var _q=(_t_d2||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d2={error:"[HTTP "+(r2.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d2=JSON.parse(_q)}catch(e){d2={error:"[JSON] "+e.message}}}}addMsg(d2.response||err.message,'a',{engine:'Fallback',time:((Date.now()-t0)/1000).toFixed(1)+'s'})}
catch(e2){addMsg('Erreur: '+err.message,'a',{engine:'Error'})}
}
stEl.textContent='Connecté';hideProgress();busy=false;$('sendBtn').disabled=false;inp.focus();files=[];$('filePrev').innerHTML=''
}
function scrollToBottom(){const ca=document.getElementById('chatArea');if(ca)ca.scrollTop=ca.scrollHeight;}
setInterval(()=>{if(busy)scrollToBottom()},500);
// Live stats
fetch('/api/source-of-truth.json?t='+Date.now()).then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})).then(d=>{
$('pc').textContent=Object.keys(d.providers||{}).length;
$('fs').textContent=(d.tools_count||360)+' tools | '+(d.agents_count||870)+' agents · '+(d.cascade_count||12)+' cascade'
}).catch(()=>{});
// === VOICE INPUT ===
let isRec=false, recog=null;
function startVoice(){
const btn=document.getElementById('voiceBtn');
if(isRec&&recog){recog.stop();isRec=false;btn.textContent='🎙';return}
const SR=window.SpeechRecognition||window.webkitSpeechRecognition;
if(!SR){btn.textContent='❌';setTimeout(()=>btn.textContent='🎙',1500);return}
recog=new SR();recog.lang='fr-FR';recog.continuous=false;recog.interimResults=true;
recog.onstart=()=>{isRec=true;btn.textContent='🔴';btn.style.animation='pulse 1s infinite'};
recog.onresult=e=>{let f='',t='';for(let i=0;i<e.results.length;i++){if(e.results[i].isFinal)f+=e.results[i][0].transcript;else t+=e.results[i][0].transcript}document.getElementById('input').value=f||t};
recog.onend=()=>{isRec=false;btn.textContent='🎙';btn.style.animation='';if(document.getElementById('input').value.trim())send()};
recog.onerror=()=>{isRec=false;btn.textContent='🎙';btn.style.animation=''};
recog.start();
}
// === TTS ===
let ttsOn=true;
function toggleTTS(){
ttsOn=!ttsOn;
document.getElementById('ttsBtn').style.opacity=ttsOn?1:0.4;
}
function speakResponse(text){
if(!ttsOn||!window.speechSynthesis)return;
try{const u=new SpeechSynthesisUtterance(text.replace(/[#*`]/g,'').substring(0,500));u.lang='fr-FR';u.rate=1.1;speechSynthesis.speak(u)}catch(e){}
}
// === DRAG & DROP ===
const chatArea=document.getElementById('msgs');
if(chatArea){
chatArea.addEventListener('dragover',e=>{e.preventDefault();chatArea.style.outline='2px dashed var(--ac)'});
chatArea.addEventListener('dragleave',()=>{chatArea.style.outline='none'});
chatArea.addEventListener('drop',e=>{e.preventDefault();chatArea.style.outline='none';if(e.dataTransfer.files.length){pendingFiles=[...e.dataTransfer.files];document.getElementById('input').value='Analyse ces fichiers';send()}});
}
// Patch addMsg to auto-TTS
const _origAddA = typeof addMsg === 'function' ? addMsg : null;
</script>
<script src="/js/wevia-artifact-renderer.js"></script>
<!-- CARTO_REMOVED -->
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
<script>
(function(){
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
var d = document;
var m = d.createElement('div');
m.id = 'opus-udrill';
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
var inner = d.createElement('div');
inner.id = 'opus-udrill-in';
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
inner.addEventListener('click', function(e){ e.stopPropagation(); });
m.appendChild(inner);
m.addEventListener('click', function(){ m.style.display='none'; });
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
(d.body || d.documentElement).appendChild(m);
function openCard(card) {
// Clone card content + show close btn + increase font-size
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
// Initial + mutation observer
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
<script src="/api/archi-meta-badge.js" defer></script>
</body>
</html>