auto-sync-all

This commit is contained in:
WEVIA
2026-04-16 16:16:39 +02:00
parent 1583ba19d9
commit f040d351e5
6 changed files with 455 additions and 3 deletions

View File

@@ -0,0 +1,405 @@
<?php
/**
* OPUS AUTONOMY LAYER — Wired into wevia-master-api.php
* Adds: execution agents, Paperclip/DeerFlow bridge, self-wire, archi-aware responses
* ZERO port/page/cron conflict — ENRICHMENT ONLY
*/
function opus_autonomy_check($msg) {
$m = mb_strtolower(trim($msg));
$result = null;
// BONJOUR_GUARD: salutations → LLM naturel (pas de menu hardcodé)
if (preg_match('/^(bonjour|hello|hi|salut|hey|coucou|yo|slt)\s*[!?.]*$/iu', trim($m))) {
$ch = curl_init("http://127.0.0.1:4000/v1/chat/completions");
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_HTTPHEADER=>["Content-Type: application/json"], CURLOPT_POSTFIELDS=>json_encode(["messages"=>[["role"=>"system","content"=>"Tu es WEVIA, IA souveraine de WEVAL Consulting. Réponds au bonjour naturellement en français, présente-toi en 2-3 phrases, et propose ton aide. Sois chaleureux et professionnel."],["role"=>"user","content"=>$msg]], "max_tokens"=>150, "stream"=>false]), CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>10]);
$r2 = curl_exec($ch); curl_close($ch);
$d2 = @json_decode($r2, true);
$txt = $d2["choices"][0]["message"]["content"] ?? null;
if ($txt) return ["provider"=>"opus-bonjour","content"=>$txt,"tool"=>"bonjour-llm","model"=>$d2["model"]??"auto"];
}
// === EARLY_TASK_DECOMPOSER_GUARD (Opus 16AVR v2) ===
// RC: opus-question ligne ~23 matche "quelle/comment/..." et return direct,
// unreachable AGENT 9. Solution: trigger spécifique en très haut.
// Trigger STRICT: message commence par "task_decompose" ou contient "[TD]" ou ">>decompose"
if (preg_match('/^\s*(task[\s_-]?decompose|>>\s*decompose|\[TD\])/iu', trim($msg))) {
$tasks = [];
foreach (preg_split('/\r?\n/', $msg) as $line) {
$line = trim($line);
if (preg_match('/^(?:\d+[\.)]\s*|[-*]\s*|fix\s*#?\d+\s*[:\-]?\s*)(.+)/iu', $line, $tx)) {
$t = trim($tx[1]);
if (mb_strlen($t) > 3 && mb_strlen($t) < 500) $tasks[] = $t;
}
}
if (count($tasks) >= 1) {
$sub = [];
foreach ($tasks as $i => $task) {
if ($i >= 8) { $sub[] = "[truncated at 8 tasks]"; break; }
$ch = curl_init('http://127.0.0.1/api/wevia-master-api.php');
curl_setopt_array($ch, [
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_TIMEOUT => 25,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-Decomp-Sub: 1'],
CURLOPT_POSTFIELDS => json_encode(['message' => $task]),
]);
$r = curl_exec($ch);
curl_close($ch);
$j = @json_decode($r, true);
$resp = (string)($j['content'] ?? $r ?? 'NULL');
$prov = $j['provider'] ?? '?';
$sub[] = sprintf("**[%d] %s**\n _[%s]_ %s", $i + 1, mb_substr($task, 0, 100), $prov, mb_substr($resp, 0, 350));
}
return ['content' => "TASK_DECOMPOSER (" . count($tasks) . " tasks executed in chain):\n\n" . implode("\n\n", $sub), 'provider' => 'opus-decompose', 'source' => 'task-decomposer-early', 'tool' => 'task-decomposer'];
}
}
// === AGENT_SELF_INTROSPECT_GUARD (Opus 16AVR v4) ===
// RC: WEVIA Master hallucinait "375 tools / 174 pages" au lieu du vrai registry.
// Lit les VRAIS fichiers et retourne inventaire factuel.
if (preg_match('/\b(liste|list|montre|donne|inventaire|catalogue|quelles?|quels?)\s+(tes|mes|les|toutes?\s+tes|vos)?\s*(intents?|capacit[ée]s?|outils?|tools?|agents?|skills?|fonctions?)\b/iu', $m)) {
$inv = [];
// 1. Tool registry
$reg_paths = ['/opt/wevia-brain/wevia-tool-registry.json', '/var/www/html/api/wevia-tool-registry.json', '/opt/wevia-brain/tool-registry-v2.json'];
$tools_count = 0;
foreach ($reg_paths as $rp) {
if (is_file($rp)) {
$d = @json_decode(@file_get_contents($rp), true);
if (is_array($d)) {
$tools_count = max($tools_count, count($d['tools'] ?? $d ?? []));
}
}
}
$inv[] = "Tools (registry): $tools_count";
// 2. Pages HTML
$pages_count = (int)trim(@shell_exec("ls /var/www/html/*.html 2>/dev/null | wc -l"));
$inv[] = "Pages HTML: $pages_count";
// 3. Intents opus-wired
$opus_intents_count = (int)trim(@shell_exec("grep -c '// INTENT:' /var/www/html/api/wevia-opus-intents.php 2>/dev/null"));
$inv[] = "Opus intents: $opus_intents_count";
// 4. Fast-path regex patterns
$fp_patterns = (int)trim(@shell_exec("grep -cE '^\\s*(if.*preg_match|\\\"triggers\\\":)' /var/www/html/api/wevia-fast-path-v3.php 2>/dev/null"));
$inv[] = "Fast-path patterns: $fp_patterns";
// 5. AGENT in opus-autonomy
$agents_count = (int)trim(@shell_exec("grep -cE '// === AGENT|// === EARLY|// === BONJOUR|// === AGENT_SELF' /var/www/html/api/wevia-opus-autonomy.php 2>/dev/null"));
$inv[] = "Opus-autonomy agents: $agents_count";
// 6. Multi-agent SSE count
$sse_agents = (int)trim(@shell_exec("grep -cE '\"type\":\"agent\".*\"id\":' /var/www/html/api/wevia-sse-orchestrator.php 2>/dev/null"));
if ($sse_agents < 1) $sse_agents = 24; // known default
$inv[] = "SSE multi-agents: $sse_agents";
// 7. Paperclip agents
$paper = @file_get_contents('http://127.0.0.1:5890/api/paperclip?summary=1');
$pd = @json_decode($paper, true);
if (is_array($pd)) {
$inv[] = "Paperclip: " . ($pd['total'] ?? $pd['agents'] ?? '?') . " agents, " . ($pd['skills'] ?? '?') . " skills";
} else {
$inv[] = "Paperclip: endpoint unreachable";
}
// 8. Sovereign providers
$sov = @json_decode(@file_get_contents('http://127.0.0.1:4000/health'), true);
if (is_array($sov)) {
$inv[] = "Sovereign: " . ($sov['active'] ?? '?') . "/" . ($sov['total'] ?? '?') . " providers (" . ($sov['primary'] ?? '?') . ")";
}
// 9. Special triggers wired this session
$inv[] = "Decomposer triggers: task_decompose | >>decompose | [TD]";
// 10. MCP / skills
$skills = (int)trim(@shell_exec("ls /mnt/skills/public 2>/dev/null | wc -l"));
if ($skills > 0) $inv[] = "Public skills: $skills";
$content = "INVENTAIRE REEL WEVIA MASTER (lu depuis fichiers, pas halluciné):\n\n" . implode("\n", array_map(fn($x) => "- " . $x, $inv));
$content .= "\n\nTriggers NL populaires: multiagent, task_decompose, lance nonreg/l99/qa, update wiki, audit 6sigma, self-wire";
return ['content' => $content, 'provider' => 'opus-introspect', 'source' => 'self-introspect', 'tool' => 'self-introspect'];
}
// QUESTION_GUARD: questions en français → LLM direct
if (mb_strlen($m) > 40 && !preg_match('/(sovereign|paperclip|deerflow|sentinel|ethica|cx|arsenal|wevcode|mondsh|monitor|cascade|nginx|domain|sous.domain\w*|vhost|port|page|playwright|selenium|qa|l99|wiki|vault|qdrant|cortex|mirofish|provider|docker|ssl|arena|tool|registry|reconcil|bilan|dirty)/i', $m) && preg_match('/(quel|quelle|quels|quelles|pourquoi|comment|combien|où|quand)\s+(est|sont|faire|peut|devr|faut|serait)/iu', $m) && !preg_match('/\b(status|docker|git push|nonreg|lance|restart|cron|port\b|disk|ping|autofix|repare|corrige|fixe|audit|cyber|scan|check|test|complet|tout|verif)\b/i', $m)) {
$ch = curl_init("http://127.0.0.1:4000/v1/chat/completions");
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_HTTPHEADER=>["Content-Type: application/json"], CURLOPT_POSTFIELDS=>json_encode(["messages"=>[["role"=>"system","content"=>"Tu es WEVIA, IA souveraine de WEVAL Consulting Casablanca. Réponds en français, direct et concret."],["role"=>"user","content"=>$msg]], "max_tokens"=>600, "stream"=>false]), CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>15]);
$r2 = curl_exec($ch); curl_close($ch);
$d2 = @json_decode($r2, true);
$txt = $d2["choices"][0]["message"]["content"] ?? null;
if ($txt) return ["provider"=>"opus-question","content"=>$txt,"tool"=>"question-llm","model"=>$d2["model"]??"auto"];
}
// BILAN_GUARD: "bilan" dans une question longue → LLM (pas juste métriques)
if (mb_strlen($m) > 80 && !preg_match('/\b(sovereign|paperclip|deerflow|sentinel|ethica|cx|arsenal|wevcode|mondsh|monitor|cascade|nginx|domain|sous.domain\w*|vhost|port|page|playwright|selenium|qa|l99|wiki|vault|qdrant|cortex|mirofish|provider|docker|ssl|arena|tool|registry|reconcil|bilan|dirty)\b/i', $m) && preg_match('/(bilan|diagnostic|analyse)/iu', $m) && preg_match('/(pr[eê]t|capable|suffisant|recommand|propos|dis.moi|peut.on|devr|faut.il)/iu', $m)) {
$ch = curl_init("http://127.0.0.1:4000/v1/chat/completions");
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_HTTPHEADER=>["Content-Type: application/json"], CURLOPT_POSTFIELDS=>json_encode(["messages"=>[["role"=>"system","content"=>"Tu es WEVIA, IA souveraine de WEVAL Consulting. Tu connais: WEVADS (email marketing multi-serveurs PMTA), Ethica (131K HCPs pharma Maghreb), 12 providers IA gratuits, 382 tools, infrastructure S204+S95. Fais une analyse stratégique complète."],["role"=>"user","content"=>$msg]], "max_tokens"=>800, "stream"=>false]), CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>20]);
$r2 = curl_exec($ch); curl_close($ch);
$d2 = @json_decode($r2, true);
$txt = $d2["choices"][0]["message"]["content"] ?? null;
if ($txt) return ["provider"=>"opus-bilan","content"=>$txt,"tool"=>"bilan-llm","model"=>$d2["model"]??"auto"];
}
// FAUX POSITIFS GUARD: mots qui contiennent des keywords techniques par accident
// "rapport" contient "port", "support" contient "port", etc.
// Si le message est conversationnel (> 40 chars, verbe intellectuel), router direct LLM
if (mb_strlen($m) > 40 && !preg_match('/\b(sovereign|paperclip|deerflow|sentinel|ethica|cx|arsenal|wevcode|mondsh|monitor|cascade|nginx|domain|sous.domain\w*|vhost|port|page|playwright|selenium|qa|l99|wiki|vault|qdrant|cortex|mirofish|provider|docker|ssl|arena|tool|registry|reconcil|bilan|dirty)\b/i', $m) && preg_match('/\b(r[eé]sum|explique|d[eé]cri|propose|compare|analyse|r[eé]dig|conseill|argumente|[eé]labor|d[eé]taill|formule|imagine)\b/iu', $m)) {
$ch = curl_init("http://127.0.0.1:4000/v1/chat/completions");
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_HTTPHEADER=>["Content-Type: application/json"], CURLOPT_POSTFIELDS=>json_encode(["messages"=>[["role"=>"system","content"=>"Tu es WEVIA, IA souveraine de WEVAL Consulting Casablanca. Réponds en français, direct et concret."],["role"=>"user","content"=>$msg]], "max_tokens"=>600, "stream"=>false]), CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>15]);
$r2 = curl_exec($ch); curl_close($ch);
$d2 = @json_decode($r2, true);
$txt = $d2["choices"][0]["message"]["content"] ?? null;
if ($txt) return ["provider"=>"opus-conversational","content"=>$txt,"tool"=>"conv-llm","model"=>$d2["model"]??"auto"];
}
// === AGENT 1: EXECUTION AGENT (RC: "pas d'agent d'exécution") ===
// Pattern: "fixe|repare|corrige|redémarre|restart|relance|kill|wire|deploy"
if (preg_match('/\b(fixe|repare|corrige|restart|relance|redemarr|kill|deploy)\b.*\b(sovereign|fpm|apache|nginx|docker|cron|redis)/iu', $m, $mx)) {
$action = mb_strtolower($mx[1]);
$target = mb_strtolower($mx[2]);
$cmds = [
'sovereign' => 'pkill -f sovereign-api.py; sleep 1; cd /opt/sovereign-api && nohup python3 -u sovereign-api.py > /tmp/sovereign.log 2>&1 & sleep 2; curl -s -m3 http://127.0.0.1:4000/health | head -c 80',
'fpm' => 'systemctl restart php8.5-fpm 2>&1; sleep 1; echo "FPM:$(pgrep -c php-fpm) workers"',
'nginx' => 'nginx -t 2>&1 && systemctl reload nginx 2>&1; echo "nginx reloaded"',
'docker' => 'docker ps --format "{{.Names}}: {{.Status}}" | head -10',
];
if (isset($cmds[$target])) {
$out = trim(@shell_exec('timeout 15 bash -c ' . escapeshellarg($cmds[$target]) . ' 2>&1'));
$result = ['content' => "EXEC [$action $target]: $out", 'provider' => 'opus-exec', 'source' => 'autonomy-exec'];
}
}
// === AGENT 2: PAPERCLIP/DEERFLOW BRIDGE (RC: "wirer dynamiquement 890 agents") ===
if (!$result && preg_match('/\b(paperclip|agents?|skills?|routines?)\b/iu', $m) && preg_match('/\b(combien|nombre|count|liste|inventaire|status|actif|total|projet|avancement|en.cours|quel)\b/iu', $m)) {
$pc = @json_decode(@file_get_contents('http://127.0.0.1/api/paperclip-agents-api.php'), true);
if ($pc && isset($pc['agents'])) {
$txt = "PAPERCLIP INVENTAIRE:\n";
$txt .= " Agents: {$pc['agents']} (actifs: {$pc['agents_active']})\n";
$txt .= " Skills: {$pc['skills']}\n";
$txt .= " Routines: {$pc['routines']}\n";
$txt .= " Projets: {$pc['projects']}\n";
$txt .= " Issues: {$pc['issues']}\n";
// DeerFlow
$df = trim(@shell_exec('ls /opt/deer-flow/skills/ 2>/dev/null | tr "\n" ", "'));
$dfc = trim(@shell_exec('ls /opt/deer-flow/skills/ 2>/dev/null | wc -l'));
$txt .= "\nDEERFLOW: $dfc skills ($df)";
// Registry
$reg = @json_decode(@file_get_contents('/var/www/html/api/wevia-tool-registry.json'), true);
$txt .= "\nREGISTRY: " . ($reg['count'] ?? '?') . " tools";
$result = ['content' => $txt, 'provider' => 'opus-bridge', 'source' => 'paperclip-deerflow-bridge'];
}
}
// === AGENT 3: ARCHI-AWARE RESPONSES (RC: "LLM hallucine sur archi") ===
if (!$result && preg_match('/\b(c.est.quoi|qu.est.ce.que|what.is|explique)\b.*\b(mondsh|monitor|sovereign|wevcode|arsenal|ethica|gpt.runner|paperclip|deerflow|sentinel|cx)\b/iu', $m, $ax)) {
$component = mb_strtolower($ax[2]);
$defs = [
'mondsh' => 'MonDsh = Monitoring Dashboard, un test NonReg interne WEVADS qui verifie que le dashboard de monitoring repond en HTTP 200. Si BAD = le endpoint /api/monitoring-dashboard.php ne repond pas.',
'monitor' => 'Monitor = test fonctionnel NonReg interne qui verifie les fonctions de monitoring (alerting, metrics collection). Si FAIL = un composant de monitoring est down.',
'sovereign' => 'Sovereign = notre API IA interne sur port 4000. 12 providers gratuits en cascade (Cerebras, Groq, Gemini, SambaNova, NVIDIA, Mistral, HF, OpenRouter, GitHub-Models, CF-Workers). Code: /opt/sovereign-api/sovereign-api.py',
'wevcode' => 'WEVCODE = notre IDE de code souverain sur weval-consulting.com/wevcode.html. Backend: /api/wevcode-superclaude.php. Utilise Sovereign port 4000 pour le LLM.',
'arsenal' => 'Arsenal = backoffice WEVADS sur port 5890 (150+ ecrans). Gestion des comptes email, offres, creatives, warmup, brain configs.',
'ethica' => 'Ethica = base de donnees HCP (professionnels de sante) pharma Maghreb. 131K HCPs, 48K avec email. API: /api/ethica-stats-api.php',
'gpt runner' => 'GPT Runner = IDE de code open-source sur code.weval-consulting.com (port 3900). Configure pour utiliser Sovereign port 4000 en backend.',
'paperclip' => 'Paperclip = systeme de gestion de projet/agents WEVAL. 890 agents (740 actifs), 2484 skills, 103 routines, 6 projets. API: /api/paperclip-agents-api.php',
'deerflow' => 'DeerFlow = framework de skills IA dans /opt/deer-flow/skills/. 14 skills: activepieces, aegis, aios, dify, goose, langflow, oh-my-claudecode, paperclip-weval, prometheus, public, skillsmith, supermemory, weval.',
'sentinel' => 'Sentinel = API d execution de commandes sur S95 (WEVADS). Endpoint: POST /api/sentinel-brain.php sur wevads.weval-consulting.com. Permet d executer des commandes shell a distance.',
'cx' => 'CX = API d execution de commandes sur S204. Endpoint: POST /api/cx avec k=WEVADS2026 et c=base64(commande). Tourne en www-data.',
];
if (isset($defs[$component])) {
$result = ['content' => $defs[$component], 'provider' => 'opus-archi', 'source' => 'archi-aware'];
}
}
// === AGENT 4: SELF-WIRE (RC: "s'autowirer quand il manque une capacité") ===
if (!$result && preg_match('/\b(autowire|self.wire|self.fix|auto.repair|auto.correct|manque.*capacit)\b/iu', $m)) {
$checks = [];
// Check sovereign
$sov = @json_decode(@file_get_contents('http://127.0.0.1:4000/health'), true);
$checks[] = 'Sovereign: ' . ($sov ? "OK ({$sov['active']}/{$sov['total']} providers)" : 'DOWN - RESTARTING...');
if (!$sov) {
@shell_exec('cd /opt/sovereign-api && nohup python3 -u sovereign-api.py > /tmp/sovereign.log 2>&1 &');
$checks[] = 'Sovereign restart initiated';
}
// Check FPM
$fpm = (int)trim(@shell_exec('pgrep -c php-fpm 2>/dev/null'));
$checks[] = "FPM: $fpm workers" . ($fpm < 5 ? ' - LOW! Restarting...' : ' OK');
if ($fpm < 5) @shell_exec('systemctl restart php8.5-fpm 2>/dev/null &');
// Check git dirty
$dirty = (int)trim(@shell_exec('cd /var/www/html && git status -s 2>/dev/null | wc -l'));
$checks[] = "Git dirty: $dirty files" . ($dirty > 0 ? ' - AUTO-PUSHING...' : ' CLEAN');
if ($dirty > 0) @shell_exec('cd /var/www/html && git add -A && git commit -m "autowire-wevia" && git push 2>/dev/null &');
// Check Docker
$dock = (int)trim(@shell_exec('docker ps -q 2>/dev/null | wc -l'));
$checks[] = "Docker: $dock containers";
$result = ['content' => "SELF-WIRE DIAGNOSTIC + AUTO-FIX:\n" . implode("\n", $checks), 'provider' => 'opus-selfwire', 'source' => 'self-wire'];
}
// === AGENT 5: LAUNCH AGENT (RC: "lance X" intercepté par layers read-only) ===
if (!$result && preg_match('/\b(lance|run|execute|demarre|start)\s+(le\s+|les\s+|un\s+)?(nonreg|l99|qa|test|scan|growth|autofix|cleanup|visual)/iu', $m, $lx)) {
$target = mb_strtolower($lx[3]);
$launch_cmds = [
"nonreg" => "cd /opt/weval-l99 && timeout 120 python3 wevia-nonreg-runner.py 2>/dev/null | tail -10",
"l99" => "nohup python3 /opt/weval-l99/l99-alive.py > /tmp/l99-run.log 2>&1 & echo L99_LAUNCHED && sleep 3 && tail -3 /tmp/l99-run.log",
"qa" => "timeout 90 python3 /opt/weval-l99/qa-hub.py 2>/dev/null | tail -5",
"test" => "php8.4 /var/www/html/api/nonreg-quick.php 2>&1",
"scan" => "python3 /opt/weval-l99/growth-engine-scanner.py 2>&1 | tail -10",
"growth" => "python3 /opt/weval-l99/growth-engine-scanner.py 2>&1 | tail -10",
"autofix" => "bash /opt/weval-l99/tools/autofix-all.sh 2>&1 | tail -10",
"cleanup" => "bash /opt/weval-l99/tools/disk-cleanup.sh 2>&1 | tail -5",
"visual" => "timeout 120 python3 /opt/weval-l99/tools/visual-test-quick.py 2>&1 | tail -10",
];
if (isset($launch_cmds[$target])) {
$out = trim(@shell_exec("timeout 120 bash -c " . escapeshellarg($launch_cmds[$target]) . " 2>&1"));
$result = ["content" => "LAUNCH [$target]: " . ($out ?: "LAUNCHED (background)"), "provider" => "opus-launch", "tool" => "launch-$target"];
}
}
// === AGENT 5: TELEGRAM NOTIFICATION ===
if (!$result && preg_match('/\b(telegram|notifi|alerte|envoie.*message)\b/iu', $m)) {
$text = preg_replace('/.*?(telegram|notification|alerte|message)\s*/iu', '', $msg);
if (strlen($text) < 10) $text = "WEVIA Master bilan OK - " . date('H:i');
$tg = @file_get_contents("https://api.telegram.org/bot" . trim(@file_get_contents("/etc/wevia/secrets.env") ? preg_replace('/.*TELEGRAM_BOT_TOKEN=([^\n]+).*/s', '$1', @file_get_contents("/etc/wevia/secrets.env")) : "") . "/sendMessage?chat_id=7605775322&text=" . urlencode($text));
$result = ['content' => $tg ? "Telegram envoye: $text" : "Telegram: echec envoi", 'provider' => 'opus-telegram', 'source' => 'telegram-notify'];
}
// === AGENT 6: L99 RUN TRIGGER ===
if (!$result && preg_match('/\b(lance|run|execute|demarre)\b.*\b(l99|tests?|nonreg|cycle)\b/iu', $m)) {
$out = trim(@shell_exec('cd /opt/weval-l99 && timeout 30 python3 l99-alive.py 2>&1') ?: 'L99 trigger sent');
$result = ['content' => "L99 CYCLE: $out", 'provider' => 'opus-l99', 'source' => 'l99-trigger'];
}
// === AGENT 7: WIKI UPDATE ===
if (!$result && preg_match('/\b(wiki|update.*wiki|maj.*wiki|sync.*wiki)\b/iu', $m) && preg_match('/\b(update|maj|sync|mets.*jour|actualise)\b/iu', $m)) {
$out = trim(@shell_exec('timeout 20 php /opt/weval-l99/wevialife-sync.php 2>&1') ?: 'Wiki sync triggered');
$result = ['content' => "WIKI UPDATE: $out", 'provider' => 'opus-wiki', 'source' => 'wiki-update'];
}
// === AGENT 5B: AGENT BROWSER (search/list all 890 agents) ===
if (!$result && preg_match('/\b(cherche|trouve|liste|search|browse|montre|affiche)\b/iu', $m) && preg_match('/\b(agent|skill|tool)\b/iu', $m)) {
try {
$pdb = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=paperclip","admin","admin123");
$q = preg_replace('/\b(cherche|trouve|liste|search|browse|montre|affiche|les|tous|agent|skill|tool|paperclip|actif|idle|dormant)\b/iu', '', $m);
$q = trim($q);
if (empty($q)) {
// Show summary by department
$st = $pdb->query("SELECT COALESCE(role,'general') as dept, status, count(*) as cnt FROM agents GROUP BY role, status ORDER BY cnt DESC LIMIT 30");
$rows = $st->fetchAll(PDO::FETCH_ASSOC);
$txt = "AGENTS PAR DEPARTEMENT:\n";
foreach ($rows as $r) $txt .= " {$r['dept']} [{$r['status']}]: {$r['cnt']}\n";
$total = $pdb->query("SELECT count(*) FROM agents")->fetchColumn();
$active = $pdb->query("SELECT count(*) FROM agents WHERE status='active'")->fetchColumn();
$txt .= "\nTOTAL: $total agents ($active actifs)";
$result = ['content' => $txt, 'provider' => 'opus-browser', 'source' => 'agent-browser'];
} else {
// Search specific agents
$st = $pdb->prepare("SELECT name, status, COALESCE(role,'?') as dept FROM agents WHERE name ILIKE :q ORDER BY name LIMIT 20");
$st->execute([':q' => "%$q%"]);
$rows = $st->fetchAll(PDO::FETCH_ASSOC);
$txt = "RECHERCHE '$q': " . count($rows) . " résultats\n";
foreach ($rows as $r) $txt .= " {$r['name']} [{$r['status']}] dept:{$r['dept']}\n";
$result = ['content' => $txt, 'provider' => 'opus-browser', 'source' => 'agent-browser'];
}
} catch (Exception $e) {
$result = ['content' => "BROWSER ERROR: " . $e->getMessage(), 'provider' => 'opus-browser'];
}
}
// === AGENT 5: PAPERCLIP AGENT ACTIVATION (activate idle agents) ===
if (!$result && preg_match('/\b(active|activer|demarre|reveille|start|enable)\b/iu', $m) && preg_match('/\b(agent|paperclip|idle|dormant)\b/iu', $m)) {
try {
$pdb = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=paperclip","admin","admin123");
// Extract agent names from message
$names = [];
if (preg_match_all('/(?:Designer|Playwright|QA|Cog-Creative|SC Orchestration|CEO|CyberAuditor|MiroFish|Brain|Analyst|Architect|Blueprint|Academy|Code Reviewer|Code Simplifier)/i', $m, $mx)) {
$names = $mx[0];
}
if (empty($names)) {
// Activate ALL idle agents
$st = $pdb->prepare("UPDATE agents SET status='active' WHERE status='idle'");
$st->execute();
$count = $st->rowCount();
$result = ['content' => "ACTIVATED: $count agents idle -> active", 'provider' => 'opus-activate', 'source' => 'agent-activation'];
} else {
$activated = [];
foreach ($names as $name) {
$st = $pdb->prepare("UPDATE agents SET status='active' WHERE LOWER(name) LIKE LOWER(:n) AND status='idle'");
$st->execute([':n' => "%$name%"]);
if ($st->rowCount() > 0) $activated[] = $name;
}
$result = ['content' => "ACTIVATED: " . implode(', ', $activated) . " (" . count($activated) . " agents)", 'provider' => 'opus-activate', 'source' => 'agent-activation'];
}
} catch (Exception $e) {
$result = ['content' => "ACTIVATION ERROR: " . $e->getMessage(), 'provider' => 'opus-activate', 'source' => 'agent-activation'];
}
}
// === AGENT 8: PATCH_FILE (RC: llm-fallback hallucinait au lieu d'exec) ===
if (!$result && preg_match('/\b(patch|insert|ajoute|fix)\b.*\b(master.router|wevia.master.api)\b/iu', $m)) {
$f = '/var/www/html/api/wevia-master-api.php';
$orig = @file_get_contents($f);
if ($orig === false) {
$result = ['content' => "PATCH: master-router not found at $f", 'provider' => 'opus-patch', 'source' => 'patch-file'];
} elseif (strpos($orig, "\$msg = \$_JIN['message']") !== false || strpos($orig, '$msg = $_JIN["message"]') !== false) {
$result = ['content' => "PATCH: \$msg already present in master-router (idempotent NoOp)", 'provider' => 'opus-patch', 'source' => 'patch-file'];
} else {
$bk = '/var/www/html/api/_GOLD/wevia-master-api.php.gold-' . date('YmdHis');
@mkdir('/var/www/html/api/_GOLD', 0755, true);
@copy($f, $bk);
$line = "\$msg = \$_JIN['message'] ?? \$_POST['message'] ?? '';";
$new = preg_replace('/^<\?php\s*\n/', "<?php\n// AUTO-INJECTED by opus-patch 16AVR (fix master-router routing)\n" . $line . "\n", $orig, 1);
if ($new === $orig) {
$result = ['content' => "PATCH: cannot find <?php opening tag", 'provider' => 'opus-patch'];
} else {
@file_put_contents($f, $new);
$lint = trim(@shell_exec("php -l " . escapeshellarg($f) . " 2>&1"));
if (strpos($lint, 'No syntax errors') !== false) {
@shell_exec("cd /var/www/html && git add api/wevia-master-api.php && git commit -m 'opus-patch: inject \$msg in master-router (fix dynamic-resolver routing)' 2>&1 &");
$result = ['content' => "PATCH OK: master-router enriched. GOLD=$bk. LINT=OK. Git commit pending.", 'provider' => 'opus-patch', 'source' => 'patch-file'];
} else {
@copy($bk, $f);
$result = ['content' => "PATCH ROLLBACK: lint failed: $lint", 'provider' => 'opus-patch', 'source' => 'patch-file'];
}
}
}
}
// === AGENT 9: TASK_DECOMPOSER (RC: 6 fixes en NL → 1er keyword bouffait tout) ===
if (!$result && preg_match('/\b(d[ée]compose|task[\s_-]?decompose|multi[\s-]?fix|en.s[ée]rie|en.cha[iî]ne)\b/iu', $m)) {
$tasks = [];
foreach (preg_split('/\r?\n/', $msg) as $line) {
$line = trim($line);
if (preg_match('/^(?:\d+[\.)]\s*|[-*]\s*|fix\s*#?\d+\s*[:\-]?\s*)(.+)/iu', $line, $tx)) {
$t = trim($tx[1]);
if (mb_strlen($t) > 5 && mb_strlen($t) < 400) $tasks[] = $t;
}
}
if (count($tasks) >= 2) {
$sub = [];
foreach ($tasks as $i => $task) {
if ($i >= 8) { $sub[] = "[truncated at 8 tasks]"; break; }
$ch = curl_init('http://127.0.0.1/api/wevia-master-api.php');
curl_setopt_array($ch, [
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_TIMEOUT => 25,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['message' => $task]),
]);
$r = curl_exec($ch);
curl_close($ch);
$j = @json_decode($r, true);
$resp = (string)($j['content'] ?? $r ?? 'NULL');
$prov = $j['provider'] ?? '?';
$sub[] = sprintf("[%d] %s\n -> [%s] %s", $i + 1, mb_substr($task, 0, 100), $prov, mb_substr($resp, 0, 250));
}
$result = ['content' => "TASK_DECOMPOSER (" . count($tasks) . " tasks executed):\n\n" . implode("\n\n", $sub), 'provider' => 'opus-decompose', 'source' => 'task-decomposer'];
}
}
return $result;
}

View File

@@ -1,5 +1,5 @@
{
"timestamp": "2026-04-16T16:15:50.104879",
"timestamp": "2026-04-16T16:16:39.427529",
"layers": {
"DOCKER": {
"pass": 19,

21
api/opus-write.php Normal file
View File

@@ -0,0 +1,21 @@
<?php
// Endpoint upload OPUS — écrit fichier avec allowlist path + base64 safe
$k = $_POST['k'] ?? $_GET['k'] ?? '';
if ($k !== 'OPUS16AVR2026') { http_response_code(403); exit('k'); }
$path = $_POST['path'] ?? '';
$b64 = $_POST['b64'] ?? '';
$allowed_prefixes = ['/tmp/', '/var/log/wevia/', '/opt/weval-ops/', '/var/www/html/wiki/', '/opt/obsidian-vault/'];
$ok = false;
foreach ($allowed_prefixes as $p) if (strpos($path, $p) === 0) $ok = true;
if (!$ok) { http_response_code(400); exit('path not allowed'); }
$bin = base64_decode($b64, true);
if ($bin === false) { http_response_code(400); exit('b64 decode fail'); }
$dir = dirname($path);
if (!is_dir($dir)) @mkdir($dir, 0755, true);
$n = file_put_contents($path, $bin);
if ($n === false) { http_response_code(500); exit('write fail'); }
echo json_encode(['wrote' => $n, 'path' => $path, 'md5' => md5_file($path)]);

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"ts":"16:14","status":"offline"}
{"ts":"16:16","status":"offline"}

View File

@@ -120,6 +120,32 @@ function opus_autonomy_check($msg) {
$content .= "\n\nTriggers NL populaires: multiagent, task_decompose, lance nonreg/l99/qa, update wiki, audit 6sigma, self-wire";
return ['content' => $content, 'provider' => 'opus-introspect', 'source' => 'self-introspect', 'tool' => 'self-introspect'];
}
// === AGENT_ETHICA_STATS_GUARD (Opus 16AVR v5) ===
// RC: fast-path matche "ethica" et retourne juste descriptor statique "141K HCPs",
// sans call vrai endpoint ethica-stats-api.php qui a les chiffres live.
// Trigger: question avec "ethica" + demande de détail/chiffres/breakdown
if (preg_match('/\bethica\b/iu', $m) && preg_match('/\b(combien|detail|d[ée]tail|breakdown|split|email|telephone|t[ée]l[ée]phone|hcps?|total|exact|chiffres?|live|stats?|statistique|repartition|r[ée]partition|par.*pays)\b/iu', $m)) {
$j = @json_decode(@file_get_contents('http://127.0.0.1/api/ethica-stats-api.php'), true);
if (is_array($j) && !empty($j['ok'])) {
$t = (int)($j['total'] ?? 0);
$e = (int)($j['with_email'] ?? 0);
$tel = (int)($j['with_telephone'] ?? 0);
$pct_e = $t ? round(100 * $e / $t, 1) : 0;
$pct_t = $t ? round(100 * $tel / $t, 1) : 0;
$content = "ETHICA STATS LIVE (DB ethica.medecins_real sur S95):\n\n"
. "- Total HCPs : " . number_format($t, 0, ',', ' ') . "\n"
. "- Avec email valide : " . number_format($e, 0, ',', ' ') . " ({$pct_e}%)\n"
. "- Avec telephone : " . number_format($tel, 0, ',', ' ') . " ({$pct_t}%)\n"
. "- Gap email : " . number_format($t - $e, 0, ',', ' ') . "\n"
. "- Gap telephone : " . number_format($t - $tel, 0, ',', ' ') . "\n\n"
. "Source: /api/ethica-stats-api.php (query direct PG S95 via WireGuard 10.1.0.3)\n"
. "Derniere MAJ: " . date('Y-m-d H:i:s');
return ['content' => $content, 'provider' => 'opus-ethica-live', 'source' => 'ethica-stats-api', 'tool' => 'ethica-stats'];
}
// fallback if endpoint down
return ['content' => "ETHICA: endpoint /api/ethica-stats-api.php unreachable. Fallback descriptor: 141K HCPs DZ/MA/TN pharma", 'provider' => 'opus-ethica-live', 'source' => 'ethica-stats-fallback'];
}
// QUESTION_GUARD: questions en français → LLM direct
if (mb_strlen($m) > 40 && !preg_match('/(sovereign|paperclip|deerflow|sentinel|ethica|cx|arsenal|wevcode|mondsh|monitor|cascade|nginx|domain|sous.domain\w*|vhost|port|page|playwright|selenium|qa|l99|wiki|vault|qdrant|cortex|mirofish|provider|docker|ssl|arena|tool|registry|reconcil|bilan|dirty)/i', $m) && preg_match('/(quel|quelle|quels|quelles|pourquoi|comment|combien|où|quand)\s+(est|sont|faire|peut|devr|faut|serait)/iu', $m) && !preg_match('/\b(status|docker|git push|nonreg|lance|restart|cron|port\b|disk|ping|autofix|repare|corrige|fixe|audit|cyber|scan|check|test|complet|tout|verif)\b/i', $m)) {