- Integrate ActionMonitor into ChatGPT GitOps API for failure tracking - Add ErrorClassifier with permission, git, runtime, system, secret violation error types - Add FailureRouter to route errors to correct recovery agents (openclaw/hermes/windows_codex_app) - Add RecoveryController orchestrating full pipeline: capture→classify→route→plan→execute→validate→record - Update error_classifier to include ClientResponseError, rate limit, and secret_violation patterns - All 8 integration tests passing (ActionMonitor, ErrorClassifier, FailureRouter, RecoveryController, API 500 scenario) - Push baseline to V1R2C2 with tag v1r2c2-baseline
78 lines
2.0 KiB
Python
78 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""AIOS Multi-Agent Consensus Controller
|
|
|
|
This service aggregates decisions from OpenClaw, Codex, and ChatGPT agents
|
|
and computes a bounded consensus decision for GitOps operations.
|
|
|
|
It does NOT directly modify production state. It only produces decision artifacts.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import pathlib
|
|
import time
|
|
from typing import Dict, Any
|
|
|
|
ROOT = pathlib.Path(os.environ.get("AIOS_REPO_ROOT", ".")).resolve()
|
|
CONSENSUS_DIR = ROOT / "reports" / "consensus"
|
|
AGENT_DIRS = {
|
|
"openclaw": ROOT / "reports" / "openclaw",
|
|
"codex": ROOT / "reports" / "codex",
|
|
"chatgpt": ROOT / "reports" / "chatgpt",
|
|
}
|
|
|
|
CONSENSUS_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
def load_latest(path: pathlib.Path) -> Dict[str, Any]:
|
|
if not path.exists():
|
|
return {}
|
|
files = sorted(path.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True)
|
|
if not files:
|
|
return {}
|
|
return json.loads(files[0].read_text(encoding="utf-8"))
|
|
|
|
|
|
def compute_consensus(reports: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
|
|
weights = {"openclaw": 2, "codex": 2, "chatgpt": 1}
|
|
|
|
score = 0
|
|
for agent, report in reports.items():
|
|
status = report.get("status", "unknown")
|
|
if status in ("PASS", "approved", "merge_candidate"):
|
|
score += weights.get(agent, 1)
|
|
elif status in ("FAIL", "blocked"):
|
|
score -= weights.get(agent, 1)
|
|
|
|
if score >= 4:
|
|
decision = "approved"
|
|
elif score >= 2:
|
|
decision = "conditional"
|
|
else:
|
|
decision = "rejected"
|
|
|
|
return {
|
|
"decision": decision,
|
|
"score": score,
|
|
"timestamp": time.time(),
|
|
}
|
|
|
|
|
|
def main():
|
|
reports = {
|
|
"openclaw": load_latest(AGENT_DIRS["openclaw"]),
|
|
"codex": load_latest(AGENT_DIRS["codex"]),
|
|
"chatgpt": load_latest(AGENT_DIRS["chatgpt"]),
|
|
}
|
|
|
|
result = compute_consensus(reports)
|
|
|
|
out_path = CONSENSUS_DIR / "decision.json"
|
|
out_path.write_text(json.dumps(result, indent=2), encoding="utf-8")
|
|
|
|
print("CONSENSUS:", result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|