"""Agent orchestration — Spec 029.""" from __future__ import annotations import os from pathlib import Path from app.agents import checks, llm_client, notify, registry, store SPECS = Path(os.getenv("AGENTIC_SPECS_ROOT", "/opt/ligbox-ops-platform/specs")) OPS_API = os.getenv("OPS_API_URL", "http://api:8080") TOKEN = os.getenv("OPS_INTERNAL_TOKEN", "") def sync_registry(conn): for sc in registry.load_registry(): store.upsert_scenario(conn, sc) return len(registry.load_registry()) def index_specs_kb(conn): if not SPECS.exists(): return 0 n = 0 for p in sorted(SPECS.glob("**/*.md")): try: txt = p.read_text(encoding="utf-8", errors="ignore") if len(txt) > 100: store.index_kb_file(conn, str(p.relative_to(SPECS)), txt) n += 1 except OSError: pass return n def run_scenario(conn, scenario_id, *, trigger="cron"): sc = store.get_scenario(conn, scenario_id) if not sc: return {"ok": False, "error": "not found"} fn = checks.SCENARIO_RUNNERS.get(scenario_id) if not fn: return {"ok": False, "error": "no runner"} run_id = store.create_run(conn, scenario_id, trigger) store.log_event(conn, event_type="run.start", message=scenario_id, run_id=run_id) raw = fn(conn, ops_api_url=OPS_API, internal_token=TOKEN) fids = [] for f in raw: kb = store.search_kb(conn, f.get("title", "")) human = f.get("human_action") or "" if f.get("severity") in ("high", "critical", "warn"): advice, model = llm_client.advise_human_action( finding_title=f.get("title",""), finding_detail=f.get("detail_md",""), kb_snippets=[k["snippet"] for k in kb], ) if advice and not human: human = advice fid = store.add_finding(conn, run_id, severity=f.get("severity", sc.get("severity_default","warn")), category=f.get("category","api"), title=f.get("title","Finding"), detail_md=f.get("detail_md",""), evidence=f.get("evidence"), human_action=human, kb_refs=[k["source"] for k in kb]) fids.append(fid) if f.get("severity") in ("high", "critical"): notify.notify_finding({**f, "suggested_human_action": human}) store.log_event(conn, event_type="finding.created", message=f.get("title",""), run_id=run_id, payload={"id": fid}) status = "ok" if not raw else "degraded" store.finish_run(conn, run_id, status=status, summary=f"{len(raw)} finding(s)" if raw else "healthy") store.log_event(conn, event_type="run.finish", message=status, run_id=run_id) return {"ok": True, "run_id": run_id, "scenario_id": scenario_id, "status": status, "findings_count": len(raw), "finding_ids": fids} def run_all_enabled(conn, trigger="cron"): sync_registry(conn) return {"runs": [run_scenario(conn, s["id"], trigger=trigger) for s in store.list_scenarios(conn)], "total": len(store.list_scenarios(conn))}