198 lines
11 KiB
PHP
198 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* WEVIA DAILY STANDUP v1.0 — Agent Meetings System
|
|
*
|
|
* SCHEDULE:
|
|
* - 2x/jour SQUAD meetings (08h + 14h) — agents par domaine
|
|
* - 1x/semaine WEEKLY (lundi 09h) — Agent Chef + tous les agents
|
|
*
|
|
* PROCESS:
|
|
* 1. Agent Chef collecte le status de chaque agent
|
|
* 2. Chaque agent rapporte: done, blockers, next
|
|
* 3. Cerebras 235B synthétise + détecte conflits
|
|
* 4. Auto-résolution conflits entre agents
|
|
* 5. Action items assignés + Mattermost report
|
|
*/
|
|
header("Content-Type: application/json; charset=utf-8");
|
|
$action = $_GET["action"] ?? "daily";
|
|
$secrets = [];
|
|
foreach (file("/etc/weval/secrets.env", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $l) {
|
|
if (strpos($l, "=") !== false) { list($k, $v) = explode("=", $l, 2); $secrets[trim($k)] = trim($v, " \t\"'"); }
|
|
}
|
|
|
|
function llm_call($prompt) {
|
|
global $secrets;
|
|
foreach (["CEREBRAS_KEY_2", "CEREBRAS_KEY2", "CEREBRAS_API_KEY"] as $kn) {
|
|
$key = $secrets[$kn] ?? "";
|
|
if (strlen($key) < 10) continue;
|
|
$ch = curl_init("https://api.cerebras.ai/v1/chat/completions");
|
|
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_HTTPHEADER => ["Content-Type: application/json", "Authorization: Bearer $key"],
|
|
CURLOPT_POSTFIELDS => json_encode(["model" => "qwen-3-235b-a22b-instruct-2507",
|
|
"messages" => [["role" => "system", "content" => "Tu es l'Agent Chef WEVIA. Tu animes les daily standups entre agents IA. Sois concis, identifie conflits et propose résolutions."],
|
|
["role" => "user", "content" => $prompt]], "max_tokens" => 800])]);
|
|
$r = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
|
|
if ($code >= 200 && $code < 300) {
|
|
$d = json_decode($r, true);
|
|
return $d["choices"][0]["message"]["content"] ?? null;
|
|
}
|
|
}
|
|
// Fallback Ollama
|
|
$ch = curl_init("http://127.0.0.1:11434/v1/chat/completions");
|
|
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
|
|
CURLOPT_POSTFIELDS => json_encode(["model" => "weval-brain-v3",
|
|
"messages" => [["role" => "user", "content" => $prompt]], "max_tokens" => 500])]);
|
|
$r = curl_exec($ch); curl_close($ch);
|
|
$d = json_decode($r, true);
|
|
return $d["choices"][0]["message"]["content"] ?? "LLM unavailable";
|
|
}
|
|
|
|
function collect_agent_status($agent_name) {
|
|
$statuses = [
|
|
"cortex" => function() {
|
|
$r = json_decode(@file_get_contents("/var/www/html/api/cortex-report.json") ?: "{}", true);
|
|
return ["done" => "13 checks, services " . ($r["checks"]["services"] ?? "?"),
|
|
"blockers" => ($r["checks"]["cerebras"] ?? "") === "unavailable" ? "Cerebras 403" : "none",
|
|
"next" => "Continue monitoring */4h"];
|
|
},
|
|
"gap_detector" => function() {
|
|
$r = json_decode(@file_get_contents("/var/www/html/api/gap-detector.json") ?: "{}", true);
|
|
return ["done" => "Score " . ($r["score"] ?? "?") . "% (" . ($r["wired"] ?? "?") . "/" . ($r["opt_tools_total"] ?? "?") . " wired)",
|
|
"blockers" => ($r["not_wired_count"] ?? 0) > 0 ? ($r["not_wired_count"] ?? 0) . " tools not wired" : "none",
|
|
"next" => "Wire remaining tools"];
|
|
},
|
|
"l99" => function() {
|
|
$r = json_decode(@file_get_contents("/var/www/html/api/l99-results.json") ?: "{}", true);
|
|
$dark = json_decode(@file_get_contents("/var/www/html/api/l99-dark-results.json") ?: "{}", true);
|
|
return ["done" => "Functional " . ($r["passed"] ?? "?") . "/" . ($r["total"] ?? "?") . ", Dark " . ($dark["pass"] ?? "?") . "/" . (($dark["pass"]??0)+($dark["fail"]??0)),
|
|
"blockers" => "gitleaks+trivy version format mismatch",
|
|
"next" => "Fix dark test parsing"];
|
|
},
|
|
"nonreg" => function() {
|
|
$r = json_decode(@file_get_contents("https://weval-consulting.com/api/nonreg-api.php?cat=all") ?: "{}", true);
|
|
return ["done" => ($r["pass"] ?? "?") . "/" . ($r["total"] ?? "?") . " (" . ($r["score"] ?? "?") . "%)",
|
|
"blockers" => "none", "next" => "Maintain 100%"];
|
|
},
|
|
"evolution" => function() {
|
|
$r = json_decode(@file_get_contents("/var/www/html/api/agent-evolution-report.json") ?: "{}", true);
|
|
return ["done" => "System: " . ($r["system"]["routes"] ?? "?") . " routes, " . ($r["system"]["skills"] ?? "?") . " skills",
|
|
"blockers" => "Agent PHP syntax to fix",
|
|
"next" => "Generate 5 evolution proposals"];
|
|
},
|
|
"scanner" => function() {
|
|
return ["done" => "12 sections wiki scanned", "blockers" => "none", "next" => "Continue */2h"];
|
|
},
|
|
"security" => function() {
|
|
return ["done" => "0 leaks, 92 expositions redacted, CrowdSec active",
|
|
"blockers" => "none", "next" => "Continuous monitoring"];
|
|
},
|
|
"mirofish" => function() {
|
|
$up = @file_get_contents("http://127.0.0.1:5001/") !== false;
|
|
return ["done" => $up ? "MiroFish UP :5001" : "MiroFish DOWN",
|
|
"blockers" => $up ? "none" : "Service down", "next" => "Collaborative tools"];
|
|
},
|
|
];
|
|
if (isset($statuses[$agent_name])) return ($statuses[$agent_name])();
|
|
return ["done" => "unknown", "blockers" => "unknown", "next" => "unknown"];
|
|
}
|
|
|
|
function notify_mattermost($text) {
|
|
$ch = curl_init("http://localhost:8065/hooks/pt54hzthf3b6pe6rgp1ionipnh");
|
|
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["text" => $text]),
|
|
CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5]);
|
|
curl_exec($ch); curl_close($ch);
|
|
}
|
|
|
|
switch ($action) {
|
|
case "daily":
|
|
$squads = [
|
|
"infra" => ["cortex", "gap_detector", "nonreg", "security"],
|
|
"innovation" => ["evolution", "scanner", "l99", "mirofish"],
|
|
];
|
|
$squad = $_GET["squad"] ?? "all";
|
|
$report = ["type" => "DAILY STANDUP", "timestamp" => date("Y-m-d H:i"), "squad" => $squad, "agents" => [], "conflicts" => [], "actions" => []];
|
|
|
|
$agents_to_check = ($squad === "all") ? array_merge(...array_values($squads)) : ($squads[$squad] ?? []);
|
|
|
|
// Collect status from each agent
|
|
foreach ($agents_to_check as $agent) {
|
|
$status = collect_agent_status($agent);
|
|
$report["agents"][$agent] = $status;
|
|
}
|
|
|
|
// Ask LLM to detect conflicts and propose resolutions
|
|
$agent_summary = json_encode($report["agents"], JSON_UNESCAPED_UNICODE);
|
|
$analysis = llm_call("Voici le status de chaque agent WEVIA:\n$agent_summary\n\nIdentifie:\n1. CONFLITS entre agents (ex: un agent qui bloque un autre)\n2. PROBLEMES communs\n3. ACTIONS PRIORITAIRES pour aujourd'hui\n4. CONCILIATION si besoin\nRéponds en JSON: {conflicts:[], common_issues:[], priority_actions:[], conciliation:[]}");
|
|
|
|
$report["ai_analysis"] = $analysis;
|
|
|
|
// Save report
|
|
$date = date("Y-m-d");
|
|
$time = date("H-i");
|
|
@mkdir("/var/www/html/api/meetings", 0755, true);
|
|
file_put_contents("/var/www/html/api/meetings/daily-{$date}-{$time}.json", json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
file_put_contents("/var/www/html/api/meetings/latest-daily.json", json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
|
|
// Mattermost notification
|
|
$agent_lines = [];
|
|
foreach ($report["agents"] as $name => $s) {
|
|
$emoji = $s["blockers"] === "none" ? "✅" : "⚠️";
|
|
$agent_lines[] = "$emoji **$name**: {$s['done']} | Blockers: {$s['blockers']}";
|
|
}
|
|
$mm = "📋 **DAILY STANDUP** — " . date("d/m H:i") . " ($squad)\n" . implode("\n", $agent_lines);
|
|
if ($analysis) $mm .= "\n\n🤖 **Agent Chef Analysis:**\n" . substr($analysis, 0, 500);
|
|
notify_mattermost($mm);
|
|
|
|
echo json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
break;
|
|
|
|
case "weekly":
|
|
// Weekly = daily ALL + evolution proposals + architecture review
|
|
$report = ["type" => "WEEKLY", "timestamp" => date("Y-m-d H:i"), "agents" => []];
|
|
foreach (["cortex", "gap_detector", "nonreg", "security", "evolution", "scanner", "l99", "mirofish"] as $agent) {
|
|
$report["agents"][$agent] = collect_agent_status($agent);
|
|
}
|
|
// Add system metrics
|
|
$report["system"] = [
|
|
"routes" => intval(trim(shell_exec("grep -c '// Route' /var/www/html/api/weval-ia-fast.php 2>/dev/null"))),
|
|
"skills" => intval(trim(shell_exec("ls -d /opt/deer-flow/skills/weval/*/ 2>/dev/null | wc -l"))),
|
|
"wiki" => intval(trim(shell_exec("ls /opt/weval-l99/wiki/*.json 2>/dev/null | wc -l"))),
|
|
"crons" => intval(trim(shell_exec("ls /etc/cron.d/weval-* 2>/dev/null | wc -l"))),
|
|
"docker" => intval(trim(shell_exec("docker ps | tail -n+2 | wc -l"))),
|
|
"disk" => trim(shell_exec("df -h / | tail -1 | awk '{print \$5}'")),
|
|
];
|
|
// Ask LLM for weekly review
|
|
$summary = json_encode(["agents" => $report["agents"], "system" => $report["system"]], JSON_UNESCAPED_UNICODE);
|
|
$report["ai_review"] = llm_call("Weekly review WEVIA:\n$summary\n\nDonne: 1) Bilan semaine 2) Top 3 réussites 3) Top 3 problèmes 4) Plan semaine prochaine. Format markdown.");
|
|
|
|
@mkdir("/var/www/html/api/meetings", 0755, true);
|
|
file_put_contents("/var/www/html/api/meetings/weekly-" . date("Y-m-d") . ".json", json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
file_put_contents("/var/www/html/api/meetings/latest-weekly.json", json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
|
|
notify_mattermost("📊 **WEEKLY REVIEW** — " . date("d/m") . "\nRoutes: " . $report["system"]["routes"] . " | Skills: " . $report["system"]["skills"] . " | Wiki: " . $report["system"]["wiki"] . "\n\n" . substr($report["ai_review"] ?? "", 0, 800));
|
|
|
|
echo json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
break;
|
|
|
|
case "history":
|
|
$files = glob("/var/www/html/api/meetings/*.json");
|
|
$history = [];
|
|
foreach (array_slice(array_reverse($files), 0, 20) as $f) {
|
|
$d = json_decode(file_get_contents($f), true);
|
|
$history[] = ["file" => basename($f), "type" => $d["type"] ?? "?", "timestamp" => $d["timestamp"] ?? "?", "agents" => count($d["agents"] ?? [])];
|
|
}
|
|
echo json_encode(["meetings" => $history, "total" => count($files)], JSON_PRETTY_PRINT);
|
|
break;
|
|
|
|
case "status":
|
|
$latest = json_decode(@file_get_contents("/var/www/html/api/meetings/latest-daily.json") ?: "{}", true);
|
|
echo json_encode($latest ?: ["status" => "no meeting yet"]);
|
|
break;
|
|
|
|
default:
|
|
echo json_encode(["module" => "DAILY STANDUP v1.0", "actions" => ["daily", "weekly", "history", "status"],
|
|
"params" => ["daily?squad=infra", "daily?squad=innovation", "daily?squad=all"],
|
|
"schedule" => ["squad_infra" => "08:00+14:00", "squad_innovation" => "08:30+14:30", "weekly" => "Monday 09:00"]]);
|
|
}
|