ligbox-ops-platform/projects/ops-desk/api/app/agents/runner.py
Ligbox Spec Hub fd491e5859 Implement Spec 030 Agentic Ops Mission Board (UI-A/B/C).
Add agent_incidents dedup, overview/incidents/timeline API, mission board UI with fleet rail, kanban, context panel, mobile tabs, poll and keyboard shortcuts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-20 06:49:38 +00:00

91 lines
4.1 KiB
Python

"""Agent orchestration — Spec 029."""
from __future__ import annotations
import os
from pathlib import Path
from app.agents import checks, llm_client, notify, registry, store
from app.agents import messages as agent_messages
from app.agents.catalog import SCENARIO_AGENT_MAP
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"}
agent_id = SCENARIO_AGENT_MAP.get(scenario_id) or (sc.get("config") or {}).get("agent_id") or "sentinel"
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, agent_id=agent_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 ("warn", "high", "critical"):
inc, notify_ops = store.upsert_incident(
conn,
scenario_id=scenario_id,
finding_id=fid,
title=f.get("title", "Finding"),
severity=f.get("severity", "warn"),
primary_agent=agent_id,
suggested_human_action=human,
)
if notify_ops:
agent_messages.notify_finding_to_operators(
conn,
finding_id=fid,
scenario_id=scenario_id,
title=f.get("title", "Finding"),
severity=f.get("severity", "warn"),
human_action=human,
agent_id=agent_id,
thread_id=inc.get("thread_id"),
)
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}, agent_id=agent_id)
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, agent_id=agent_id)
if not raw:
store.resolve_incidents_for_scenario(conn, scenario_id)
conn.commit()
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))}