Files
html/api/wevia-opus-autonomy.php
2026-04-16 16:16:39 +02:00

432 lines
30 KiB
PHP
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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'];
}
// === 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)) {
$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;
}