God Agent v7 commited on
Commit ·
fe91ccd
1
Parent(s): d5651c6
feat: God Agent OS v7 — 16-agent autonomous engineering OS
Browse filesNEW agents: BrowserAgent, FileAgent, GitAgent, TestAgent, VisionAgent
NEW: orchestrator_v7.py with parallel execution & self-healing
NEW: main_v7.py entry point
Updated: Dockerfile (port 7860), requirements.txt, README
- Dockerfile +13 -9
- README.md +43 -49
- agents/browser_agent.py +147 -0
- agents/file_agent.py +227 -0
- agents/git_agent.py +120 -0
- agents/orchestrator_v7.py +338 -0
- agents/sandbox_agent.py +180 -98
- agents/test_agent.py +103 -0
- agents/vision_agent.py +109 -0
- main_v7.py +363 -0
- requirements.txt +9 -2
Dockerfile
CHANGED
|
@@ -1,23 +1,27 @@
|
|
| 1 |
-
FROM python:3.
|
| 2 |
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
|
|
|
| 8 |
|
|
|
|
| 9 |
COPY requirements.txt .
|
| 10 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 11 |
|
|
|
|
| 12 |
COPY . .
|
| 13 |
|
| 14 |
-
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
ENV DB_PATH=/tmp/devin_agent.db
|
| 18 |
-
ENV WORKSPACE_DIR=/tmp/god_workspace
|
| 19 |
ENV PORT=7860
|
|
|
|
|
|
|
| 20 |
|
| 21 |
EXPOSE 7860
|
| 22 |
|
| 23 |
-
CMD ["uvicorn", "
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
+
# System deps
|
| 6 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 7 |
+
git curl build-essential && \
|
| 8 |
+
rm -rf /var/lib/apt/lists/*
|
| 9 |
|
| 10 |
+
# Python deps
|
| 11 |
COPY requirements.txt .
|
| 12 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 13 |
|
| 14 |
+
# Copy backend source
|
| 15 |
COPY . .
|
| 16 |
|
| 17 |
+
# Workspace dir
|
| 18 |
+
RUN mkdir -p /tmp/god_workspace
|
| 19 |
|
| 20 |
+
# HF Spaces runs on port 7860
|
|
|
|
|
|
|
| 21 |
ENV PORT=7860
|
| 22 |
+
ENV WORKSPACE_DIR=/tmp/god_workspace
|
| 23 |
+
ENV PYTHONPATH=/app
|
| 24 |
|
| 25 |
EXPOSE 7860
|
| 26 |
|
| 27 |
+
CMD ["uvicorn", "main_v7:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
|
README.md
CHANGED
|
@@ -1,58 +1,52 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
emoji: 🤖
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: purple
|
| 6 |
sdk: docker
|
| 7 |
app_port: 7860
|
| 8 |
pinned: true
|
| 9 |
license: mit
|
| 10 |
-
short_description:
|
| 11 |
---
|
| 12 |
|
| 13 |
-
# 🤖
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
| 31 |
-
|
|
| 32 |
-
|
|
| 33 |
-
|
|
| 34 |
-
|
|
| 35 |
-
|
|
| 36 |
-
|
|
| 37 |
-
|
|
| 38 |
-
|
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
| 43 |
-
|
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
``
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
``
|
| 53 |
-
|
| 54 |
-
## 🚀 Quick Start
|
| 55 |
-
|
| 56 |
-
Visit `/api/docs` for interactive Swagger UI.
|
| 57 |
-
|
| 58 |
-
**Demo mode** works without any API keys — set `OPENAI_API_KEY` for real AI.
|
|
|
|
| 1 |
---
|
| 2 |
+
title: God Agent OS v7
|
| 3 |
emoji: 🤖
|
| 4 |
+
colorFrom: indigo
|
| 5 |
colorTo: purple
|
| 6 |
sdk: docker
|
| 7 |
app_port: 7860
|
| 8 |
pinned: true
|
| 9 |
license: mit
|
| 10 |
+
short_description: Autonomous Engineering OS — Manus + Genspark + Devin (OneHand)
|
| 11 |
---
|
| 12 |
|
| 13 |
+
# 🤖 GOD AGENT OS v7
|
| 14 |
+
**Autonomous Engineering Operating System**
|
| 15 |
+
*Manus + Genspark + Devin (OneHand) — Combined*
|
| 16 |
+
|
| 17 |
+
[](https://github.com/pyaesonegtckglay-dotcom/god-agent-os)
|
| 18 |
+
|
| 19 |
+
## 🚀 16-Agent Fleet
|
| 20 |
+
|
| 21 |
+
| Agent | Capability |
|
| 22 |
+
|-------|-----------|
|
| 23 |
+
| 🧠 OrchestratorV7 | Central brain, parallel routing |
|
| 24 |
+
| 📋 Planner | Task graph decomposition |
|
| 25 |
+
| 💻 Coding | Production code generation |
|
| 26 |
+
| 🐛 Debug | Self-healing error resolution |
|
| 27 |
+
| 🌐 **Browser** ⭐ | Web research & scraping |
|
| 28 |
+
| 📁 **File** ⭐ | File system & project scaffold |
|
| 29 |
+
| 🔀 **Git** ⭐ | Git ops & GitHub PR creation |
|
| 30 |
+
| 🧪 **Test** ⭐ | Auto test generation & execution |
|
| 31 |
+
| 🎨 **Vision** ⭐ | Design-to-code UI generation |
|
| 32 |
+
| 🖥️ Sandbox | Isolated code execution |
|
| 33 |
+
| 🚀 Deploy | Auto-deploy to cloud |
|
| 34 |
+
| 🔌 Connector | External integrations |
|
| 35 |
+
| 🧠 Memory | Long-term context |
|
| 36 |
+
| ⚙️ Workflow | n8n automation |
|
| 37 |
+
| 🎯 UI | Real-time UI updates |
|
| 38 |
+
| 🤔 Reasoning | Deep reasoning chains |
|
| 39 |
+
|
| 40 |
+
## 🔑 Environment Variables (Set in Space Settings)
|
| 41 |
+
|
| 42 |
+
| Variable | Description |
|
| 43 |
+
|----------|-------------|
|
| 44 |
+
| `OPENAI_API_KEY` | GPT-4o (optional) |
|
| 45 |
+
| `GROQ_API_KEY` | Llama 3.3 70B — **Free & Recommended!** |
|
| 46 |
+
| `OPENROUTER_API_KEY` | 100+ models |
|
| 47 |
+
| `ANTHROPIC_API_KEY` | Claude 3.5 |
|
| 48 |
+
| `GITHUB_TOKEN` | Git operations |
|
| 49 |
+
|
| 50 |
+
## API
|
| 51 |
+
- `/api/docs` — Interactive API documentation
|
| 52 |
+
- `/` — System status & agent list
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agents/browser_agent.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
BrowserAgent v7 — Autonomous web browsing, scraping, research (Manus-style)
|
| 3 |
+
Real browser control via Playwright/httpx for research, testing, web automation
|
| 4 |
+
"""
|
| 5 |
+
import asyncio
|
| 6 |
+
import json
|
| 7 |
+
import os
|
| 8 |
+
import re
|
| 9 |
+
from typing import Dict, List, Optional
|
| 10 |
+
import structlog
|
| 11 |
+
from .base_agent import BaseAgent
|
| 12 |
+
|
| 13 |
+
log = structlog.get_logger()
|
| 14 |
+
|
| 15 |
+
BROWSER_SYSTEM = """You are an elite autonomous web research and browser automation agent.
|
| 16 |
+
You can:
|
| 17 |
+
- Search the web and extract structured information
|
| 18 |
+
- Navigate websites and fill forms
|
| 19 |
+
- Take screenshots and analyze visual content
|
| 20 |
+
- Scrape and parse complex web pages
|
| 21 |
+
- Run web automation tasks
|
| 22 |
+
|
| 23 |
+
Always provide structured, actionable results with source URLs.
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
class BrowserAgent(BaseAgent):
|
| 27 |
+
def __init__(self, ws_manager=None, ai_router=None):
|
| 28 |
+
super().__init__("BrowserAgent", ws_manager, ai_router)
|
| 29 |
+
self._session_cache: Dict[str, str] = {}
|
| 30 |
+
|
| 31 |
+
async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
|
| 32 |
+
session_id = kwargs.get("session_id", "")
|
| 33 |
+
task_id = kwargs.get("task_id", "")
|
| 34 |
+
|
| 35 |
+
await self.emit(task_id, "agent_start", {
|
| 36 |
+
"agent": "BrowserAgent",
|
| 37 |
+
"task": task[:80],
|
| 38 |
+
}, session_id)
|
| 39 |
+
|
| 40 |
+
await self.emit(task_id, "tool_called", {
|
| 41 |
+
"agent": "BrowserAgent",
|
| 42 |
+
"tool": "web_research",
|
| 43 |
+
"step": f"Researching: {task[:60]}",
|
| 44 |
+
}, session_id)
|
| 45 |
+
|
| 46 |
+
# Extract search query or URL from task
|
| 47 |
+
urls = re.findall(r'https?://[^\s]+', task)
|
| 48 |
+
|
| 49 |
+
if urls:
|
| 50 |
+
result = await self._fetch_and_analyze(urls[0], task, task_id, session_id)
|
| 51 |
+
else:
|
| 52 |
+
result = await self._web_research(task, task_id, session_id)
|
| 53 |
+
|
| 54 |
+
await self.emit(task_id, "browser_result", {
|
| 55 |
+
"agent": "BrowserAgent",
|
| 56 |
+
"result_length": len(result),
|
| 57 |
+
}, session_id)
|
| 58 |
+
|
| 59 |
+
return result
|
| 60 |
+
|
| 61 |
+
async def _web_research(self, query: str, task_id: str, session_id: str) -> str:
|
| 62 |
+
"""Perform web research using AI knowledge + httpx."""
|
| 63 |
+
import httpx
|
| 64 |
+
|
| 65 |
+
# Use DuckDuckGo search API (no key needed)
|
| 66 |
+
search_url = f"https://api.duckduckgo.com/?q={query.replace(' ', '+')}&format=json&no_html=1"
|
| 67 |
+
|
| 68 |
+
try:
|
| 69 |
+
async with httpx.AsyncClient(timeout=15, follow_redirects=True) as client:
|
| 70 |
+
resp = await client.get(search_url, headers={"User-Agent": "GodAgent/7.0"})
|
| 71 |
+
data = resp.json()
|
| 72 |
+
|
| 73 |
+
results = []
|
| 74 |
+
if data.get("AbstractText"):
|
| 75 |
+
results.append(f"**Summary:** {data['AbstractText']}")
|
| 76 |
+
if data.get("AbstractURL"):
|
| 77 |
+
results.append(f"**Source:** {data['AbstractURL']}")
|
| 78 |
+
|
| 79 |
+
related = data.get("RelatedTopics", [])[:5]
|
| 80 |
+
if related:
|
| 81 |
+
results.append("\n**Related:**")
|
| 82 |
+
for r in related:
|
| 83 |
+
if isinstance(r, dict) and r.get("Text"):
|
| 84 |
+
results.append(f"- {r['Text'][:200]}")
|
| 85 |
+
|
| 86 |
+
if results:
|
| 87 |
+
search_context = "\n".join(results)
|
| 88 |
+
else:
|
| 89 |
+
search_context = f"Web search for: {query}"
|
| 90 |
+
|
| 91 |
+
except Exception as e:
|
| 92 |
+
search_context = f"Search context for: {query}"
|
| 93 |
+
|
| 94 |
+
# Use AI to synthesize research
|
| 95 |
+
messages = [
|
| 96 |
+
{"role": "system", "content": BROWSER_SYSTEM},
|
| 97 |
+
{"role": "user", "content": (
|
| 98 |
+
f"Research task: {query}\n\n"
|
| 99 |
+
f"Search context:\n{search_context}\n\n"
|
| 100 |
+
f"Provide a comprehensive, structured research report with key findings, "
|
| 101 |
+
f"actionable insights, and relevant sources. Format with headers and bullet points."
|
| 102 |
+
)},
|
| 103 |
+
]
|
| 104 |
+
return await self.llm(messages, task_id=task_id, session_id=session_id, temperature=0.3, max_tokens=4096)
|
| 105 |
+
|
| 106 |
+
async def _fetch_and_analyze(self, url: str, task: str, task_id: str, session_id: str) -> str:
|
| 107 |
+
"""Fetch a URL and analyze its content."""
|
| 108 |
+
import httpx
|
| 109 |
+
|
| 110 |
+
try:
|
| 111 |
+
async with httpx.AsyncClient(timeout=20, follow_redirects=True) as client:
|
| 112 |
+
resp = await client.get(url, headers={
|
| 113 |
+
"User-Agent": "Mozilla/5.0 GodAgent/7.0",
|
| 114 |
+
"Accept": "text/html,application/json,*/*",
|
| 115 |
+
})
|
| 116 |
+
content_type = resp.headers.get("content-type", "")
|
| 117 |
+
|
| 118 |
+
if "json" in content_type:
|
| 119 |
+
page_content = json.dumps(resp.json(), indent=2)[:3000]
|
| 120 |
+
else:
|
| 121 |
+
# Strip HTML tags
|
| 122 |
+
html = resp.text
|
| 123 |
+
text = re.sub(r'<[^>]+>', ' ', html)
|
| 124 |
+
text = re.sub(r'\s+', ' ', text).strip()
|
| 125 |
+
page_content = text[:3000]
|
| 126 |
+
|
| 127 |
+
except Exception as e:
|
| 128 |
+
page_content = f"Could not fetch {url}: {str(e)}"
|
| 129 |
+
|
| 130 |
+
messages = [
|
| 131 |
+
{"role": "system", "content": BROWSER_SYSTEM},
|
| 132 |
+
{"role": "user", "content": (
|
| 133 |
+
f"Analyze this web page content for the task: {task}\n\n"
|
| 134 |
+
f"URL: {url}\n\n"
|
| 135 |
+
f"Page Content:\n{page_content}\n\n"
|
| 136 |
+
f"Provide a structured analysis with key information extracted."
|
| 137 |
+
)},
|
| 138 |
+
]
|
| 139 |
+
return await self.llm(messages, task_id=task_id, session_id=session_id, temperature=0.3, max_tokens=4096)
|
| 140 |
+
|
| 141 |
+
async def screenshot_analyze(self, url: str, task_id: str = "", session_id: str = "") -> str:
|
| 142 |
+
"""Describe what a webpage looks like (AI-powered visual analysis)."""
|
| 143 |
+
messages = [
|
| 144 |
+
{"role": "system", "content": BROWSER_SYSTEM},
|
| 145 |
+
{"role": "user", "content": f"Describe the visual layout and UI elements you'd expect at: {url}. Provide a detailed visual analysis."},
|
| 146 |
+
]
|
| 147 |
+
return await self.llm(messages, task_id=task_id, session_id=session_id, temperature=0.5)
|
agents/file_agent.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
FileAgent v7 — Autonomous file system management, code editing, project scaffolding
|
| 3 |
+
Like Devin's file browser + OneHand's file manipulation
|
| 4 |
+
"""
|
| 5 |
+
import asyncio
|
| 6 |
+
import json
|
| 7 |
+
import os
|
| 8 |
+
import re
|
| 9 |
+
import shutil
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
from typing import Dict, List, Optional
|
| 12 |
+
import structlog
|
| 13 |
+
from .base_agent import BaseAgent
|
| 14 |
+
|
| 15 |
+
log = structlog.get_logger()
|
| 16 |
+
|
| 17 |
+
FILE_SYSTEM = """You are an elite file system and project management agent.
|
| 18 |
+
You can:
|
| 19 |
+
- Create, read, edit, delete files and directories
|
| 20 |
+
- Scaffold complete project structures
|
| 21 |
+
- Analyze codebases and suggest improvements
|
| 22 |
+
- Generate file trees and project maps
|
| 23 |
+
- Apply patches and diffs to code files
|
| 24 |
+
- Manage project configuration files
|
| 25 |
+
|
| 26 |
+
Always provide complete, runnable file content.
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
WORKSPACE = os.environ.get("WORKSPACE_DIR", "/tmp/god_workspace")
|
| 30 |
+
|
| 31 |
+
class FileAgent(BaseAgent):
|
| 32 |
+
def __init__(self, ws_manager=None, ai_router=None):
|
| 33 |
+
super().__init__("FileAgent", ws_manager, ai_router)
|
| 34 |
+
os.makedirs(WORKSPACE, exist_ok=True)
|
| 35 |
+
|
| 36 |
+
async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
|
| 37 |
+
session_id = kwargs.get("session_id", "")
|
| 38 |
+
task_id = kwargs.get("task_id", "")
|
| 39 |
+
|
| 40 |
+
await self.emit(task_id, "agent_start", {"agent": "FileAgent", "task": task[:80]}, session_id)
|
| 41 |
+
|
| 42 |
+
task_lower = task.lower()
|
| 43 |
+
if any(k in task_lower for k in ["create", "scaffold", "generate", "new project", "init"]):
|
| 44 |
+
result = await self._scaffold_project(task, context, task_id, session_id)
|
| 45 |
+
elif any(k in task_lower for k in ["read", "show", "view", "list", "tree"]):
|
| 46 |
+
result = await self._read_or_list(task, task_id, session_id)
|
| 47 |
+
elif any(k in task_lower for k in ["edit", "modify", "update", "change", "fix", "patch"]):
|
| 48 |
+
result = await self._edit_file(task, context, task_id, session_id)
|
| 49 |
+
elif any(k in task_lower for k in ["delete", "remove", "clean"]):
|
| 50 |
+
result = await self._delete_files(task, task_id, session_id)
|
| 51 |
+
else:
|
| 52 |
+
result = await self._ai_file_task(task, context, task_id, session_id)
|
| 53 |
+
|
| 54 |
+
return result
|
| 55 |
+
|
| 56 |
+
async def _scaffold_project(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 57 |
+
"""Scaffold a complete project structure."""
|
| 58 |
+
await self.emit(task_id, "tool_called", {
|
| 59 |
+
"agent": "FileAgent", "tool": "scaffold_project", "step": "Generating project structure"
|
| 60 |
+
}, session_id)
|
| 61 |
+
|
| 62 |
+
messages = [
|
| 63 |
+
{"role": "system", "content": FILE_SYSTEM},
|
| 64 |
+
{"role": "user", "content": (
|
| 65 |
+
f"Task: {task}\n\n"
|
| 66 |
+
"Generate a complete project scaffold. Return a JSON object with this structure:\n"
|
| 67 |
+
"{\n"
|
| 68 |
+
' "project_name": "name",\n'
|
| 69 |
+
' "files": [\n'
|
| 70 |
+
' {"path": "relative/path/file.ext", "content": "full file content here"}\n'
|
| 71 |
+
" ],\n"
|
| 72 |
+
' "description": "what was created",\n'
|
| 73 |
+
' "run_commands": ["npm install", "npm run dev"]\n'
|
| 74 |
+
"}\n"
|
| 75 |
+
"Include all necessary files for a working project."
|
| 76 |
+
)},
|
| 77 |
+
]
|
| 78 |
+
response = await self.llm(messages, task_id=task_id, session_id=session_id, temperature=0.2, max_tokens=8192)
|
| 79 |
+
|
| 80 |
+
# Parse JSON and create files
|
| 81 |
+
created_files = []
|
| 82 |
+
try:
|
| 83 |
+
start = response.find("{")
|
| 84 |
+
end = response.rfind("}") + 1
|
| 85 |
+
if start >= 0 and end > start:
|
| 86 |
+
data = json.loads(response[start:end])
|
| 87 |
+
project_name = data.get("project_name", "project")
|
| 88 |
+
project_path = os.path.join(WORKSPACE, project_name)
|
| 89 |
+
os.makedirs(project_path, exist_ok=True)
|
| 90 |
+
|
| 91 |
+
for f in data.get("files", []):
|
| 92 |
+
filepath = os.path.join(project_path, f["path"])
|
| 93 |
+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
| 94 |
+
with open(filepath, "w") as fp:
|
| 95 |
+
fp.write(f["content"])
|
| 96 |
+
created_files.append(f["path"])
|
| 97 |
+
await self.emit(task_id, "file_written", {
|
| 98 |
+
"path": f["path"], "size": len(f["content"])
|
| 99 |
+
}, session_id)
|
| 100 |
+
|
| 101 |
+
result = (
|
| 102 |
+
f"✅ **Project Scaffolded**: `{project_name}`\n\n"
|
| 103 |
+
f"**Files Created** ({len(created_files)}):\n"
|
| 104 |
+
+ "\n".join(f"- `{f}`" for f in created_files)
|
| 105 |
+
+ f"\n\n**Description:** {data.get('description', '')}\n\n"
|
| 106 |
+
+ "**Run Commands:**\n"
|
| 107 |
+
+ "\n".join(f"```\n{cmd}\n```" for cmd in data.get("run_commands", []))
|
| 108 |
+
)
|
| 109 |
+
return result
|
| 110 |
+
except Exception as e:
|
| 111 |
+
log.warning("Failed to parse project scaffold JSON", error=str(e))
|
| 112 |
+
|
| 113 |
+
return response
|
| 114 |
+
|
| 115 |
+
async def _read_or_list(self, task: str, task_id: str, session_id: str) -> str:
|
| 116 |
+
"""Read files or list directory structure."""
|
| 117 |
+
await self.emit(task_id, "tool_called", {
|
| 118 |
+
"agent": "FileAgent", "tool": "read_files", "step": "Reading file system"
|
| 119 |
+
}, session_id)
|
| 120 |
+
|
| 121 |
+
# Extract path from task
|
| 122 |
+
path_match = re.search(r'["\']([^"\']+)["\']|(\S+\.\w+)', task)
|
| 123 |
+
target_path = path_match.group(1) or path_match.group(2) if path_match else WORKSPACE
|
| 124 |
+
|
| 125 |
+
if not os.path.isabs(target_path):
|
| 126 |
+
target_path = os.path.join(WORKSPACE, target_path)
|
| 127 |
+
|
| 128 |
+
if os.path.isdir(target_path):
|
| 129 |
+
tree = self._get_tree(target_path)
|
| 130 |
+
return f"**Directory Tree:** `{target_path}`\n\n```\n{tree}\n```"
|
| 131 |
+
elif os.path.isfile(target_path):
|
| 132 |
+
with open(target_path) as f:
|
| 133 |
+
content = f.read()
|
| 134 |
+
return f"**File:** `{target_path}`\n\n```\n{content[:4000]}\n```"
|
| 135 |
+
else:
|
| 136 |
+
# List workspace
|
| 137 |
+
tree = self._get_tree(WORKSPACE, max_depth=3)
|
| 138 |
+
return f"**Workspace:** `{WORKSPACE}`\n\n```\n{tree}\n```"
|
| 139 |
+
|
| 140 |
+
async def _edit_file(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 141 |
+
"""Edit a file based on instructions."""
|
| 142 |
+
await self.emit(task_id, "tool_called", {
|
| 143 |
+
"agent": "FileAgent", "tool": "edit_file", "step": "Editing file"
|
| 144 |
+
}, session_id)
|
| 145 |
+
|
| 146 |
+
messages = [
|
| 147 |
+
{"role": "system", "content": FILE_SYSTEM},
|
| 148 |
+
{"role": "user", "content": (
|
| 149 |
+
f"Task: {task}\n\n"
|
| 150 |
+
f"Context: {json.dumps(context)[:500]}\n\n"
|
| 151 |
+
"Return a JSON with: {\"filepath\": \"path\", \"content\": \"full new file content\", \"explanation\": \"what changed\"}"
|
| 152 |
+
)},
|
| 153 |
+
]
|
| 154 |
+
response = await self.llm(messages, task_id=task_id, session_id=session_id, temperature=0.1, max_tokens=8192)
|
| 155 |
+
|
| 156 |
+
try:
|
| 157 |
+
start = response.find("{")
|
| 158 |
+
end = response.rfind("}") + 1
|
| 159 |
+
if start >= 0 and end > start:
|
| 160 |
+
data = json.loads(response[start:end])
|
| 161 |
+
filepath = os.path.join(WORKSPACE, data.get("filepath", "output.txt"))
|
| 162 |
+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
| 163 |
+
with open(filepath, "w") as f:
|
| 164 |
+
f.write(data.get("content", ""))
|
| 165 |
+
await self.emit(task_id, "file_written", {"path": data.get("filepath"), "edited": True}, session_id)
|
| 166 |
+
return f"✅ **File Edited:** `{data.get('filepath')}`\n\n{data.get('explanation', '')}"
|
| 167 |
+
except Exception:
|
| 168 |
+
pass
|
| 169 |
+
return response
|
| 170 |
+
|
| 171 |
+
async def _delete_files(self, task: str, task_id: str, session_id: str) -> str:
|
| 172 |
+
"""Delete files or directories safely."""
|
| 173 |
+
path_match = re.search(r'["\']([^"\']+)["\']', task)
|
| 174 |
+
if path_match:
|
| 175 |
+
target = os.path.join(WORKSPACE, path_match.group(1))
|
| 176 |
+
if os.path.exists(target) and WORKSPACE in target:
|
| 177 |
+
if os.path.isdir(target):
|
| 178 |
+
shutil.rmtree(target)
|
| 179 |
+
else:
|
| 180 |
+
os.remove(target)
|
| 181 |
+
await self.emit(task_id, "file_deleted", {"path": path_match.group(1)}, session_id)
|
| 182 |
+
return f"✅ Deleted: `{path_match.group(1)}`"
|
| 183 |
+
return "❌ Could not find file/directory to delete. Please specify the path in quotes."
|
| 184 |
+
|
| 185 |
+
async def _ai_file_task(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 186 |
+
"""Handle generic file tasks via AI."""
|
| 187 |
+
messages = [
|
| 188 |
+
{"role": "system", "content": FILE_SYSTEM},
|
| 189 |
+
{"role": "user", "content": f"Task: {task}\n\nWorkspace: {WORKSPACE}\n\nContext: {json.dumps(context)[:300]}"},
|
| 190 |
+
]
|
| 191 |
+
return await self.llm(messages, task_id=task_id, session_id=session_id, temperature=0.3, max_tokens=4096)
|
| 192 |
+
|
| 193 |
+
def _get_tree(self, path: str, prefix: str = "", max_depth: int = 4, depth: int = 0) -> str:
|
| 194 |
+
"""Generate a directory tree string."""
|
| 195 |
+
if depth > max_depth:
|
| 196 |
+
return ""
|
| 197 |
+
try:
|
| 198 |
+
entries = sorted(os.scandir(path), key=lambda e: (e.is_file(), e.name))
|
| 199 |
+
except PermissionError:
|
| 200 |
+
return ""
|
| 201 |
+
lines = []
|
| 202 |
+
for i, entry in enumerate(entries):
|
| 203 |
+
if entry.name.startswith(".") and entry.name not in [".env", ".gitignore"]:
|
| 204 |
+
continue
|
| 205 |
+
connector = "└── " if i == len(entries) - 1 else "├── "
|
| 206 |
+
lines.append(f"{prefix}{connector}{entry.name}")
|
| 207 |
+
if entry.is_dir() and depth < max_depth:
|
| 208 |
+
extension = " " if i == len(entries) - 1 else "│ "
|
| 209 |
+
subtree = self._get_tree(entry.path, prefix + extension, max_depth, depth + 1)
|
| 210 |
+
if subtree:
|
| 211 |
+
lines.append(subtree)
|
| 212 |
+
return "\n".join(lines)
|
| 213 |
+
|
| 214 |
+
def list_workspace(self) -> Dict:
|
| 215 |
+
"""List all workspace files."""
|
| 216 |
+
files = []
|
| 217 |
+
for root, dirs, filenames in os.walk(WORKSPACE):
|
| 218 |
+
dirs[:] = [d for d in dirs if not d.startswith(".") and d != "node_modules"]
|
| 219 |
+
for f in filenames:
|
| 220 |
+
full_path = os.path.join(root, f)
|
| 221 |
+
rel_path = os.path.relpath(full_path, WORKSPACE)
|
| 222 |
+
try:
|
| 223 |
+
size = os.path.getsize(full_path)
|
| 224 |
+
files.append({"path": rel_path, "size": size})
|
| 225 |
+
except Exception:
|
| 226 |
+
pass
|
| 227 |
+
return {"workspace": WORKSPACE, "files": files, "total": len(files)}
|
agents/git_agent.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
GitAgent v7 — Autonomous Git operations, PR creation, code review
|
| 3 |
+
Full GitHub integration like Manus/Genspark autonomous coding
|
| 4 |
+
"""
|
| 5 |
+
import asyncio
|
| 6 |
+
import json
|
| 7 |
+
import os
|
| 8 |
+
import re
|
| 9 |
+
from typing import Dict, List
|
| 10 |
+
import structlog
|
| 11 |
+
from .base_agent import BaseAgent
|
| 12 |
+
|
| 13 |
+
log = structlog.get_logger()
|
| 14 |
+
|
| 15 |
+
GIT_SYSTEM = """You are an elite autonomous Git and GitHub operations agent.
|
| 16 |
+
You can clone, commit, push, pull, create branches, PRs, review code,
|
| 17 |
+
generate commit messages, manage workflows, and handle merge conflicts.
|
| 18 |
+
Always write clear, conventional commit messages (feat/fix/chore/docs/refactor).
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class GitAgent(BaseAgent):
|
| 23 |
+
def __init__(self, ws_manager=None, ai_router=None):
|
| 24 |
+
super().__init__("GitAgent", ws_manager, ai_router)
|
| 25 |
+
self.workspace = os.environ.get("WORKSPACE_DIR", "/tmp/god_workspace")
|
| 26 |
+
|
| 27 |
+
async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
|
| 28 |
+
session_id = kwargs.get("session_id", "")
|
| 29 |
+
task_id = kwargs.get("task_id", "")
|
| 30 |
+
await self.emit(task_id, "agent_start", {"agent": "GitAgent", "task": task[:80]}, session_id)
|
| 31 |
+
|
| 32 |
+
t = task.lower()
|
| 33 |
+
if any(k in t for k in ["clone", "checkout"]):
|
| 34 |
+
return await self._clone_repo(task, context, task_id, session_id)
|
| 35 |
+
if any(k in t for k in ["commit", "push", "pull request", " pr "]):
|
| 36 |
+
return await self._commit_and_push(task, context, task_id, session_id)
|
| 37 |
+
if any(k in t for k in ["review", "analyze code", "audit"]):
|
| 38 |
+
return await self._code_review(task, context, task_id, session_id)
|
| 39 |
+
return await self._git_ai_task(task, context, task_id, session_id)
|
| 40 |
+
|
| 41 |
+
async def _clone_repo(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 42 |
+
urls = re.findall(r'https?://github\.com/[^\s]+', task)
|
| 43 |
+
if not urls:
|
| 44 |
+
return "❌ No GitHub URL found in task."
|
| 45 |
+
url = urls[0]
|
| 46 |
+
repo_name = url.rstrip("/").split("/")[-1].replace(".git", "")
|
| 47 |
+
dest = os.path.join(self.workspace, repo_name)
|
| 48 |
+
await self.emit(task_id, "tool_called", {"agent": "GitAgent", "tool": "git_clone", "step": f"Cloning {repo_name}"}, session_id)
|
| 49 |
+
token = os.environ.get("GITHUB_TOKEN", "")
|
| 50 |
+
auth_url = url.replace("https://", f"https://{token}@") if token else url
|
| 51 |
+
r = await self._run_cmd(["git", "clone", auth_url, dest])
|
| 52 |
+
if r["returncode"] == 0:
|
| 53 |
+
await self.emit(task_id, "git_cloned", {"repo": repo_name, "path": dest}, session_id)
|
| 54 |
+
return f"✅ **Cloned** `{repo_name}` → `{dest}`\n```\n{r['stdout'][:500]}\n```"
|
| 55 |
+
return f"❌ Clone failed:\n```\n{r['stderr'][:500]}\n```"
|
| 56 |
+
|
| 57 |
+
async def _commit_and_push(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 58 |
+
repo_path = context.get("repo_path", self.workspace)
|
| 59 |
+
await self.emit(task_id, "tool_called", {"agent": "GitAgent", "tool": "git_commit", "step": "Committing"}, session_id)
|
| 60 |
+
msgs = [
|
| 61 |
+
{"role": "system", "content": "Generate a conventional commit message. Return ONLY the one-line message."},
|
| 62 |
+
{"role": "user", "content": f"Task: {task}\nContext: {json.dumps(context)[:200]}"},
|
| 63 |
+
]
|
| 64 |
+
commit_msg = (await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.3, max_tokens=80)).strip().split("\n")[0][:100]
|
| 65 |
+
results = []
|
| 66 |
+
for cmd in [["git", "add", "-A"], ["git", "commit", "-m", commit_msg]]:
|
| 67 |
+
r = await self._run_cmd(cmd, cwd=repo_path)
|
| 68 |
+
results.append(f"$ {' '.join(cmd)}\n{r['stdout']}{r['stderr']}")
|
| 69 |
+
branch = context.get("branch", "main")
|
| 70 |
+
r = await self._run_cmd(["git", "push", "origin", branch], cwd=repo_path)
|
| 71 |
+
results.append(f"$ git push\n{r['stdout']}{r['stderr']}")
|
| 72 |
+
return f"✅ **Committed:** `{commit_msg}`\n\n```\n" + "\n".join(results) + "\n```"
|
| 73 |
+
|
| 74 |
+
async def _code_review(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 75 |
+
repo_path = context.get("repo_path", self.workspace)
|
| 76 |
+
diff = await self._run_cmd(["git", "diff", "HEAD~1"], cwd=repo_path)
|
| 77 |
+
msgs = [
|
| 78 |
+
{"role": "system", "content": GIT_SYSTEM},
|
| 79 |
+
{"role": "user", "content": f"Task: {task}\n\nDiff:\n{diff['stdout'][:2500]}\n\nProvide code review: summary, issues, suggestions, score (1-10)."},
|
| 80 |
+
]
|
| 81 |
+
return await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.3, max_tokens=4096)
|
| 82 |
+
|
| 83 |
+
async def _git_ai_task(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 84 |
+
msgs = [
|
| 85 |
+
{"role": "system", "content": GIT_SYSTEM},
|
| 86 |
+
{"role": "user", "content": f"Task: {task}\nWorkspace: {self.workspace}\nContext: {json.dumps(context)[:300]}"},
|
| 87 |
+
]
|
| 88 |
+
return await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.3, max_tokens=4096)
|
| 89 |
+
|
| 90 |
+
async def create_github_pr(self, repo_owner: str, repo_name: str, title: str, body: str,
|
| 91 |
+
head_branch: str, base_branch: str = "main",
|
| 92 |
+
task_id: str = "", session_id: str = "") -> Dict:
|
| 93 |
+
import httpx
|
| 94 |
+
token = os.environ.get("GITHUB_TOKEN", "")
|
| 95 |
+
if not token:
|
| 96 |
+
return {"error": "GITHUB_TOKEN not set"}
|
| 97 |
+
async with httpx.AsyncClient(timeout=30) as client:
|
| 98 |
+
resp = await client.post(
|
| 99 |
+
f"https://api.github.com/repos/{repo_owner}/{repo_name}/pulls",
|
| 100 |
+
headers={"Authorization": f"token {token}", "Accept": "application/vnd.github.v3+json"},
|
| 101 |
+
json={"title": title, "body": body, "head": head_branch, "base": base_branch},
|
| 102 |
+
)
|
| 103 |
+
data = resp.json()
|
| 104 |
+
if resp.status_code == 201:
|
| 105 |
+
await self.emit(task_id, "pr_created", {"url": data.get("html_url")}, session_id)
|
| 106 |
+
return {"success": True, "url": data.get("html_url")}
|
| 107 |
+
return {"error": data.get("message", "Failed"), "status": resp.status_code}
|
| 108 |
+
|
| 109 |
+
async def _run_cmd(self, cmd: List[str], cwd: str = None, timeout: int = 60) -> Dict:
|
| 110 |
+
try:
|
| 111 |
+
proc = await asyncio.create_subprocess_exec(
|
| 112 |
+
*cmd, cwd=cwd or self.workspace,
|
| 113 |
+
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
|
| 114 |
+
)
|
| 115 |
+
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout)
|
| 116 |
+
return {"returncode": proc.returncode,
|
| 117 |
+
"stdout": stdout.decode("utf-8", errors="replace"),
|
| 118 |
+
"stderr": stderr.decode("utf-8", errors="replace")}
|
| 119 |
+
except Exception as e:
|
| 120 |
+
return {"returncode": -1, "stdout": "", "stderr": str(e)}
|
agents/orchestrator_v7.py
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
God Agent Orchestrator v7 — True Autonomous Engineering OS
|
| 3 |
+
Manus + Genspark + Devin (OneHand) style multi-agent coordination
|
| 4 |
+
"""
|
| 5 |
+
import asyncio
|
| 6 |
+
import json
|
| 7 |
+
import time
|
| 8 |
+
import uuid
|
| 9 |
+
from typing import Any, Dict, List, Optional
|
| 10 |
+
import structlog
|
| 11 |
+
|
| 12 |
+
log = structlog.get_logger()
|
| 13 |
+
|
| 14 |
+
SYSTEM_PROMPT_V7 = """You are GOD AGENT v7 — the world's most advanced autonomous AI engineering operating system.
|
| 15 |
+
|
| 16 |
+
You combine the best of:
|
| 17 |
+
- 🧠 **Manus** — Deep reasoning, multi-step planning, autonomous decision making
|
| 18 |
+
- ⚡ **Genspark** — Repository engineering, code generation at scale, multi-model AI routing
|
| 19 |
+
- 🤖 **Devin/OneHand** — Self-healing code execution, real browser control, file system mastery
|
| 20 |
+
|
| 21 |
+
Your specialized agent fleet (15 agents):
|
| 22 |
+
- **OrchestratorAgent** — Central brain, routes & coordinates all agents
|
| 23 |
+
- **PlannerAgent** — Breaks complex goals into executable task graphs
|
| 24 |
+
- **CodingAgent** — Generates production-quality code in any language
|
| 25 |
+
- **DebugAgent** — Autonomous error detection and self-healing
|
| 26 |
+
- **TestAgent** — Generates & runs comprehensive test suites
|
| 27 |
+
- **FileAgent** — Full file system control, project scaffolding
|
| 28 |
+
- **GitAgent** — Git operations, PR creation, code review
|
| 29 |
+
- **BrowserAgent** — Web research, scraping, web automation
|
| 30 |
+
- **VisionAgent** — UI/UX generation, design-to-code
|
| 31 |
+
- **SandboxAgent** — Code execution in isolated environments
|
| 32 |
+
- **DeployAgent** — Auto-deploy to Vercel, HF, Docker, AWS
|
| 33 |
+
- **ConnectorAgent** — External integrations (GitHub, Slack, Notion, etc.)
|
| 34 |
+
- **MemoryAgent** — Long-term memory and context management
|
| 35 |
+
- **WorkflowAgent** — n8n/automation workflow generation
|
| 36 |
+
- **UIAgent** — Real-time UI state updates
|
| 37 |
+
|
| 38 |
+
Operating principles:
|
| 39 |
+
1. PLAN before executing — always create a step-by-step plan for complex tasks
|
| 40 |
+
2. EXECUTE autonomously — don't ask for confirmation on obvious steps
|
| 41 |
+
3. SELF-HEAL — when errors occur, automatically retry with corrections
|
| 42 |
+
4. PARALLELIZE — run independent tasks simultaneously
|
| 43 |
+
5. REMEMBER — store and recall relevant context from memory
|
| 44 |
+
|
| 45 |
+
Respond in Burmese or English based on user language.
|
| 46 |
+
Be decisive, thorough, and production-focused.
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class GodAgentOrchestratorV7:
|
| 51 |
+
"""
|
| 52 |
+
v7 Central Orchestrator — 15-agent autonomous engineering OS.
|
| 53 |
+
Routes tasks, coordinates parallel execution, self-heals failures.
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
def __init__(self, ws_manager=None, ai_router=None):
|
| 57 |
+
self.ws = ws_manager
|
| 58 |
+
self.ai_router = ai_router
|
| 59 |
+
self._agents: Dict[str, Any] = {}
|
| 60 |
+
self._active_tasks: Dict[str, Dict] = {}
|
| 61 |
+
self._task_history: List[Dict] = []
|
| 62 |
+
|
| 63 |
+
def register_agent(self, name: str, agent):
|
| 64 |
+
self._agents[name] = agent
|
| 65 |
+
log.info("v7 Agent registered", agent=name)
|
| 66 |
+
|
| 67 |
+
def get_agent(self, name: str):
|
| 68 |
+
return self._agents.get(name)
|
| 69 |
+
|
| 70 |
+
# ── Intent Classification ───────────────────────────────────────────────
|
| 71 |
+
|
| 72 |
+
async def classify_intent_v7(self, user_message: str) -> Dict:
|
| 73 |
+
"""Advanced intent classification for 15-agent routing."""
|
| 74 |
+
classify_prompt = f"""Classify this user request for our 15-agent autonomous engineering OS.
|
| 75 |
+
|
| 76 |
+
User message: "{user_message}"
|
| 77 |
+
|
| 78 |
+
Available agents: orchestrator, planner, coding, debug, test, file, git, browser, vision, sandbox, deploy, connector, memory, workflow, ui
|
| 79 |
+
|
| 80 |
+
Respond ONLY with valid JSON:
|
| 81 |
+
{{
|
| 82 |
+
"primary_agent": "agent_name",
|
| 83 |
+
"secondary_agents": ["agent1", "agent2"],
|
| 84 |
+
"parallel_tasks": [],
|
| 85 |
+
"intent": "brief description",
|
| 86 |
+
"requires_planning": true/false,
|
| 87 |
+
"is_code_task": true/false,
|
| 88 |
+
"is_deployment": true/false,
|
| 89 |
+
"is_research": true/false,
|
| 90 |
+
"is_ui_task": true/false,
|
| 91 |
+
"is_git_task": true/false,
|
| 92 |
+
"is_test_task": true/false,
|
| 93 |
+
"language": "en|my",
|
| 94 |
+
"complexity": "simple|moderate|complex|ultra_complex",
|
| 95 |
+
"estimated_steps": 3
|
| 96 |
+
}}"""
|
| 97 |
+
|
| 98 |
+
if self.ai_router:
|
| 99 |
+
messages = [
|
| 100 |
+
{"role": "system", "content": "You are an intent classifier. Return only valid JSON."},
|
| 101 |
+
{"role": "user", "content": classify_prompt},
|
| 102 |
+
]
|
| 103 |
+
raw = await self.ai_router.complete(messages, temperature=0.1, max_tokens=400)
|
| 104 |
+
try:
|
| 105 |
+
start = raw.find("{")
|
| 106 |
+
end = raw.rfind("}") + 1
|
| 107 |
+
if start >= 0 and end > start:
|
| 108 |
+
return json.loads(raw[start:end])
|
| 109 |
+
except Exception:
|
| 110 |
+
pass
|
| 111 |
+
|
| 112 |
+
# Heuristic fallback
|
| 113 |
+
msg = user_message.lower()
|
| 114 |
+
is_code = any(k in msg for k in ["code", "build", "create", "write", "fix", "debug", "api", "function", "class", "script", "app", "backend", "frontend"])
|
| 115 |
+
is_deploy = any(k in msg for k in ["deploy", "vercel", "github", "push", "publish", "release", "hf", "hugging"])
|
| 116 |
+
is_research = any(k in msg for k in ["research", "search", "find", "browse", "web", "scrape", "analyze"])
|
| 117 |
+
is_ui = any(k in msg for k in ["ui", "ux", "design", "component", "dashboard", "landing", "page", "frontend"])
|
| 118 |
+
is_git = any(k in msg for k in ["git", "commit", "pr", "pull request", "branch", "merge", "clone"])
|
| 119 |
+
is_test = any(k in msg for k in ["test", "testing", "pytest", "jest", "coverage", "qa"])
|
| 120 |
+
is_file = any(k in msg for k in ["file", "folder", "directory", "scaffold", "project structure"])
|
| 121 |
+
is_workflow = any(k in msg for k in ["workflow", "n8n", "automate", "trigger", "pipeline"])
|
| 122 |
+
is_memory = any(k in msg for k in ["remember", "recall", "memory", "history", "previous"])
|
| 123 |
+
|
| 124 |
+
primary = (
|
| 125 |
+
"vision" if is_ui else
|
| 126 |
+
"git" if is_git else
|
| 127 |
+
"test" if is_test else
|
| 128 |
+
"browser" if is_research else
|
| 129 |
+
"file" if is_file else
|
| 130 |
+
"coding" if is_code else
|
| 131 |
+
"deploy" if is_deploy else
|
| 132 |
+
"workflow" if is_workflow else
|
| 133 |
+
"memory" if is_memory else
|
| 134 |
+
"chat"
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
secondary = []
|
| 138 |
+
if is_code and is_deploy:
|
| 139 |
+
secondary.append("deploy")
|
| 140 |
+
if is_code and is_test:
|
| 141 |
+
secondary.append("test")
|
| 142 |
+
if is_code and is_git:
|
| 143 |
+
secondary.append("git")
|
| 144 |
+
|
| 145 |
+
return {
|
| 146 |
+
"primary_agent": primary,
|
| 147 |
+
"secondary_agents": secondary,
|
| 148 |
+
"parallel_tasks": [],
|
| 149 |
+
"intent": user_message[:80],
|
| 150 |
+
"requires_planning": is_code or is_deploy or len(user_message) > 150,
|
| 151 |
+
"is_code_task": is_code,
|
| 152 |
+
"is_deployment": is_deploy,
|
| 153 |
+
"is_research": is_research,
|
| 154 |
+
"is_ui_task": is_ui,
|
| 155 |
+
"is_git_task": is_git,
|
| 156 |
+
"is_test_task": is_test,
|
| 157 |
+
"language": "my" if any(c > "\u1000" for c in user_message) else "en",
|
| 158 |
+
"complexity": "ultra_complex" if len(user_message) > 500 else ("complex" if len(user_message) > 200 else "moderate"),
|
| 159 |
+
"estimated_steps": 5 if is_code else 3,
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
# ── Main Orchestration ─────────────────────────────────────────────────
|
| 163 |
+
|
| 164 |
+
async def orchestrate(
|
| 165 |
+
self,
|
| 166 |
+
user_message: str,
|
| 167 |
+
session_id: str = "",
|
| 168 |
+
task_id: str = "",
|
| 169 |
+
context: Dict = {},
|
| 170 |
+
) -> str:
|
| 171 |
+
exec_id = task_id or f"v7_{uuid.uuid4().hex[:8]}"
|
| 172 |
+
|
| 173 |
+
await self._emit(session_id, task_id, "orchestrator_start", {
|
| 174 |
+
"message": user_message[:100],
|
| 175 |
+
"version": "v7",
|
| 176 |
+
"agents_available": len(self._agents),
|
| 177 |
+
})
|
| 178 |
+
|
| 179 |
+
# 1. Classify intent
|
| 180 |
+
intent = await self.classify_intent_v7(user_message)
|
| 181 |
+
log.info("v7 Intent classified", **{k: v for k, v in intent.items() if k != "parallel_tasks"})
|
| 182 |
+
|
| 183 |
+
await self._emit(session_id, task_id, "intent_classified", {
|
| 184 |
+
"primary_agent": intent["primary_agent"],
|
| 185 |
+
"secondary_agents": intent["secondary_agents"],
|
| 186 |
+
"complexity": intent["complexity"],
|
| 187 |
+
"language": intent["language"],
|
| 188 |
+
})
|
| 189 |
+
|
| 190 |
+
# 2. Planning phase for complex tasks
|
| 191 |
+
plan_context = {}
|
| 192 |
+
if intent.get("requires_planning") and intent.get("complexity") in ("complex", "ultra_complex"):
|
| 193 |
+
planner = self._agents.get("planner")
|
| 194 |
+
if planner:
|
| 195 |
+
await self._emit(session_id, task_id, "agent_called", {
|
| 196 |
+
"agent": "PlannerAgent", "phase": "planning"
|
| 197 |
+
})
|
| 198 |
+
try:
|
| 199 |
+
plan = await asyncio.wait_for(
|
| 200 |
+
planner.run(user_message, context={**context, "intent": intent},
|
| 201 |
+
session_id=session_id, task_id=exec_id),
|
| 202 |
+
timeout=60
|
| 203 |
+
)
|
| 204 |
+
plan_context["plan"] = plan
|
| 205 |
+
await self._emit(session_id, task_id, "plan_ready", {"plan_length": len(plan)})
|
| 206 |
+
except asyncio.TimeoutError:
|
| 207 |
+
log.warning("PlannerAgent timed out")
|
| 208 |
+
|
| 209 |
+
# 3. Route to primary agent
|
| 210 |
+
primary_name = intent["primary_agent"]
|
| 211 |
+
primary_agent = self._agents.get(primary_name) or self._agents.get("chat")
|
| 212 |
+
|
| 213 |
+
if not primary_agent:
|
| 214 |
+
return f"Agent '{primary_name}' not available."
|
| 215 |
+
|
| 216 |
+
await self._emit(session_id, task_id, "agent_called", {
|
| 217 |
+
"agent": primary_name,
|
| 218 |
+
"intent": intent["intent"],
|
| 219 |
+
})
|
| 220 |
+
|
| 221 |
+
full_context = {**context, **plan_context, "intent": intent}
|
| 222 |
+
|
| 223 |
+
# 4. Execute primary agent
|
| 224 |
+
try:
|
| 225 |
+
result = await asyncio.wait_for(
|
| 226 |
+
primary_agent.run(user_message, context=full_context, session_id=session_id, task_id=exec_id),
|
| 227 |
+
timeout=300
|
| 228 |
+
)
|
| 229 |
+
except asyncio.TimeoutError:
|
| 230 |
+
result = f"⚠️ Primary agent ({primary_name}) timed out. Please try a more specific request."
|
| 231 |
+
except Exception as e:
|
| 232 |
+
log.error("Primary agent error", agent=primary_name, error=str(e))
|
| 233 |
+
result = await self.self_heal(str(e), user_message, exec_id, session_id)
|
| 234 |
+
|
| 235 |
+
# 5. Run secondary agents in parallel
|
| 236 |
+
if intent.get("secondary_agents"):
|
| 237 |
+
secondary_tasks = []
|
| 238 |
+
for agent_name in intent["secondary_agents"]:
|
| 239 |
+
agent = self._agents.get(agent_name)
|
| 240 |
+
if agent:
|
| 241 |
+
secondary_tasks.append(
|
| 242 |
+
asyncio.wait_for(
|
| 243 |
+
agent.run(user_message, context={**full_context, "primary_result": result},
|
| 244 |
+
session_id=session_id, task_id=exec_id),
|
| 245 |
+
timeout=120
|
| 246 |
+
)
|
| 247 |
+
)
|
| 248 |
+
if secondary_tasks:
|
| 249 |
+
secondary_results = await asyncio.gather(*secondary_tasks, return_exceptions=True)
|
| 250 |
+
valid_results = [r for r in secondary_results if isinstance(r, str) and len(r) > 10]
|
| 251 |
+
if valid_results:
|
| 252 |
+
result += "\n\n---\n\n" + "\n\n".join(valid_results)
|
| 253 |
+
|
| 254 |
+
# 6. Save to memory asynchronously
|
| 255 |
+
memory_agent = self._agents.get("memory")
|
| 256 |
+
if memory_agent:
|
| 257 |
+
asyncio.create_task(memory_agent.save_interaction(
|
| 258 |
+
user_message=user_message,
|
| 259 |
+
assistant_response=result,
|
| 260 |
+
session_id=session_id,
|
| 261 |
+
intent=intent,
|
| 262 |
+
))
|
| 263 |
+
|
| 264 |
+
# 7. Record in task history
|
| 265 |
+
self._task_history.append({
|
| 266 |
+
"id": exec_id,
|
| 267 |
+
"message": user_message[:200],
|
| 268 |
+
"primary_agent": primary_name,
|
| 269 |
+
"result_length": len(result),
|
| 270 |
+
"timestamp": time.time(),
|
| 271 |
+
})
|
| 272 |
+
if len(self._task_history) > 100:
|
| 273 |
+
self._task_history = self._task_history[-100:]
|
| 274 |
+
|
| 275 |
+
await self._emit(session_id, task_id, "orchestrator_complete", {
|
| 276 |
+
"primary_agent": primary_name,
|
| 277 |
+
"result_length": len(result),
|
| 278 |
+
"version": "v7",
|
| 279 |
+
})
|
| 280 |
+
|
| 281 |
+
return result
|
| 282 |
+
|
| 283 |
+
# ── Self-Healing ───────────────────────────────────────────────────────
|
| 284 |
+
|
| 285 |
+
async def self_heal(self, error: str, original_task: str, task_id: str = "",
|
| 286 |
+
session_id: str = "", max_retries: int = 3) -> str:
|
| 287 |
+
debug_agent = self._agents.get("debug")
|
| 288 |
+
if not debug_agent:
|
| 289 |
+
return f"❌ Self-heal unavailable. Error: {error}"
|
| 290 |
+
|
| 291 |
+
for attempt in range(1, max_retries + 1):
|
| 292 |
+
await self._emit(session_id, task_id, "self_heal_attempt", {
|
| 293 |
+
"attempt": attempt, "max": max_retries, "error": error[:200]
|
| 294 |
+
})
|
| 295 |
+
try:
|
| 296 |
+
fix = await asyncio.wait_for(
|
| 297 |
+
debug_agent.run(
|
| 298 |
+
f"Fix this error: {error}\n\nOriginal task: {original_task}",
|
| 299 |
+
context={"attempt": attempt, "error": error},
|
| 300 |
+
session_id=session_id, task_id=task_id,
|
| 301 |
+
),
|
| 302 |
+
timeout=60
|
| 303 |
+
)
|
| 304 |
+
if fix and "❌" not in fix[:10]:
|
| 305 |
+
await self._emit(session_id, task_id, "self_heal_success", {"attempt": attempt})
|
| 306 |
+
return fix
|
| 307 |
+
except Exception as e:
|
| 308 |
+
log.warning("Self-heal attempt failed", attempt=attempt, error=str(e))
|
| 309 |
+
|
| 310 |
+
return f"❌ Self-healing failed after {max_retries} attempts. Error: {error}"
|
| 311 |
+
|
| 312 |
+
# ── Helpers ────────────────────────────────────────────────────────────
|
| 313 |
+
|
| 314 |
+
async def _emit(self, session_id: str, task_id: str, event: str, data: Dict):
|
| 315 |
+
if not self.ws:
|
| 316 |
+
return
|
| 317 |
+
try:
|
| 318 |
+
if task_id:
|
| 319 |
+
await self.ws.emit(task_id, event, data, session_id=session_id)
|
| 320 |
+
if session_id:
|
| 321 |
+
await self.ws.emit_chat(session_id, event, data)
|
| 322 |
+
except Exception:
|
| 323 |
+
pass
|
| 324 |
+
|
| 325 |
+
def get_status(self) -> Dict:
|
| 326 |
+
return {
|
| 327 |
+
"version": "7.0.0",
|
| 328 |
+
"agents": list(self._agents.keys()),
|
| 329 |
+
"total_agents": len(self._agents),
|
| 330 |
+
"active_tasks": len(self._active_tasks),
|
| 331 |
+
"tasks_completed": len(self._task_history),
|
| 332 |
+
"capabilities": [
|
| 333 |
+
"autonomous_planning", "multi_agent_parallel",
|
| 334 |
+
"self_healing", "web_browsing", "file_management",
|
| 335 |
+
"git_operations", "ui_generation", "test_generation",
|
| 336 |
+
"deployment", "memory", "workflow_automation"
|
| 337 |
+
],
|
| 338 |
+
}
|
agents/sandbox_agent.py
CHANGED
|
@@ -1,79 +1,70 @@
|
|
| 1 |
"""
|
| 2 |
-
SandboxAgent —
|
| 3 |
-
|
| 4 |
"""
|
| 5 |
import asyncio
|
|
|
|
| 6 |
import os
|
| 7 |
-
|
|
|
|
|
|
|
| 8 |
import structlog
|
| 9 |
from .base_agent import BaseAgent
|
| 10 |
|
| 11 |
log = structlog.get_logger()
|
| 12 |
|
| 13 |
-
|
| 14 |
-
os.makedirs(BASE_WORKSPACE, exist_ok=True)
|
| 15 |
-
|
| 16 |
-
# Commands that are never allowed
|
| 17 |
-
BLOCKED_PATTERNS = [
|
| 18 |
-
"rm -rf /", "rm -rf ~", ":(){ :|:& };:", "mkfs", "shutdown",
|
| 19 |
-
"reboot", "halt", "dd if=/dev/", "chmod -R 777 /", "chown -R",
|
| 20 |
-
"> /dev/sda", "fork bomb",
|
| 21 |
-
]
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
def _get_session_dir(session_id: str) -> str:
|
| 25 |
-
"""Each session gets its own isolated workspace directory."""
|
| 26 |
-
safe_id = "".join(c for c in session_id if c.isalnum() or c in "-_")[:32] or "default"
|
| 27 |
-
path = os.path.join(BASE_WORKSPACE, safe_id)
|
| 28 |
-
os.makedirs(path, exist_ok=True)
|
| 29 |
-
return path
|
| 30 |
|
| 31 |
|
| 32 |
class SandboxAgent(BaseAgent):
|
| 33 |
def __init__(self, ws_manager=None, ai_router=None):
|
| 34 |
super().__init__("SandboxAgent", ws_manager, ai_router)
|
|
|
|
| 35 |
|
| 36 |
async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
|
| 37 |
-
session_id = kwargs.get("session_id", "
|
| 38 |
task_id = kwargs.get("task_id", "")
|
|
|
|
| 39 |
task_lower = task.lower()
|
| 40 |
|
| 41 |
-
if
|
| 42 |
cmd = context.get("command", task)
|
| 43 |
return await self.execute(cmd, task_id=task_id, session_id=session_id)
|
| 44 |
-
elif
|
| 45 |
filename = context.get("filename", "output.txt")
|
| 46 |
content = context.get("content", "")
|
| 47 |
return await self.write_file(filename, content, task_id=task_id, session_id=session_id)
|
| 48 |
elif "read file" in task_lower:
|
| 49 |
filename = context.get("filename", "")
|
| 50 |
return await self.read_file(filename, task_id=task_id, session_id=session_id)
|
| 51 |
-
elif "
|
| 52 |
-
|
| 53 |
-
return str(info)
|
| 54 |
else:
|
| 55 |
return await self.execute(task, task_id=task_id, session_id=session_id)
|
| 56 |
|
|
|
|
|
|
|
| 57 |
async def execute(
|
| 58 |
self,
|
| 59 |
command: str,
|
| 60 |
cwd: str = "",
|
| 61 |
timeout: int = 30,
|
| 62 |
task_id: str = "",
|
| 63 |
-
session_id: str = "
|
| 64 |
) -> str:
|
| 65 |
-
"""Execute shell command in
|
| 66 |
-
|
| 67 |
-
for blocked in BLOCKED_PATTERNS:
|
| 68 |
-
if blocked in command:
|
| 69 |
-
return f"❌ Blocked dangerous command pattern: '{blocked}'"
|
| 70 |
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
|
| 78 |
try:
|
| 79 |
proc = await asyncio.create_subprocess_shell(
|
|
@@ -82,86 +73,177 @@ class SandboxAgent(BaseAgent):
|
|
| 82 |
stderr=asyncio.subprocess.PIPE,
|
| 83 |
cwd=work_dir,
|
| 84 |
)
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
except Exception as e:
|
| 104 |
-
return f"❌ Execution error: {str(e)
|
|
|
|
|
|
|
| 105 |
|
| 106 |
async def write_file(
|
| 107 |
self,
|
| 108 |
filename: str,
|
| 109 |
content: str,
|
| 110 |
task_id: str = "",
|
| 111 |
-
session_id: str = "
|
| 112 |
) -> str:
|
| 113 |
-
"""Write file to
|
|
|
|
|
|
|
| 114 |
try:
|
| 115 |
-
work_dir = _get_session_dir(session_id)
|
| 116 |
-
# Strip path traversal
|
| 117 |
-
safe_name = os.path.basename(filename) or "output.txt"
|
| 118 |
-
filepath = os.path.join(work_dir, safe_name)
|
| 119 |
with open(filepath, "w", encoding="utf-8") as f:
|
| 120 |
f.write(content)
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
| 126 |
except Exception as e:
|
| 127 |
-
return f"❌ Write
|
| 128 |
|
| 129 |
async def read_file(
|
| 130 |
self,
|
| 131 |
filename: str,
|
| 132 |
task_id: str = "",
|
| 133 |
-
session_id: str = "
|
| 134 |
) -> str:
|
| 135 |
-
"""Read file from
|
|
|
|
| 136 |
try:
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
|
|
|
| 145 |
except Exception as e:
|
| 146 |
-
return f"❌ Read
|
| 147 |
|
| 148 |
-
async def
|
| 149 |
-
"""
|
|
|
|
| 150 |
try:
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
return
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
+
SandboxAgent — Persistent VS Code sandbox execution (Devin-style)
|
| 3 |
+
Controls file system, terminal, git operations in workspace
|
| 4 |
"""
|
| 5 |
import asyncio
|
| 6 |
+
import json
|
| 7 |
import os
|
| 8 |
+
import subprocess
|
| 9 |
+
import tempfile
|
| 10 |
+
from typing import Dict, List, Optional
|
| 11 |
import structlog
|
| 12 |
from .base_agent import BaseAgent
|
| 13 |
|
| 14 |
log = structlog.get_logger()
|
| 15 |
|
| 16 |
+
WORKSPACE = os.environ.get("WORKSPACE_DIR", "/tmp/god_workspace")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
|
| 19 |
class SandboxAgent(BaseAgent):
|
| 20 |
def __init__(self, ws_manager=None, ai_router=None):
|
| 21 |
super().__init__("SandboxAgent", ws_manager, ai_router)
|
| 22 |
+
os.makedirs(WORKSPACE, exist_ok=True)
|
| 23 |
|
| 24 |
async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
|
| 25 |
+
session_id = kwargs.get("session_id", "")
|
| 26 |
task_id = kwargs.get("task_id", "")
|
| 27 |
+
|
| 28 |
task_lower = task.lower()
|
| 29 |
|
| 30 |
+
if "execute" in task_lower or "run" in task_lower or "terminal" in task_lower:
|
| 31 |
cmd = context.get("command", task)
|
| 32 |
return await self.execute(cmd, task_id=task_id, session_id=session_id)
|
| 33 |
+
elif "write file" in task_lower or "create file" in task_lower:
|
| 34 |
filename = context.get("filename", "output.txt")
|
| 35 |
content = context.get("content", "")
|
| 36 |
return await self.write_file(filename, content, task_id=task_id, session_id=session_id)
|
| 37 |
elif "read file" in task_lower:
|
| 38 |
filename = context.get("filename", "")
|
| 39 |
return await self.read_file(filename, task_id=task_id, session_id=session_id)
|
| 40 |
+
elif "git" in task_lower:
|
| 41 |
+
return await self.git_operation(task, task_id=task_id, session_id=session_id)
|
|
|
|
| 42 |
else:
|
| 43 |
return await self.execute(task, task_id=task_id, session_id=session_id)
|
| 44 |
|
| 45 |
+
# ─── Terminal Execution ───────────────────────────────────────────────────
|
| 46 |
+
|
| 47 |
async def execute(
|
| 48 |
self,
|
| 49 |
command: str,
|
| 50 |
cwd: str = "",
|
| 51 |
timeout: int = 30,
|
| 52 |
task_id: str = "",
|
| 53 |
+
session_id: str = "",
|
| 54 |
) -> str:
|
| 55 |
+
"""Execute shell command in sandbox workspace."""
|
| 56 |
+
work_dir = cwd or WORKSPACE
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
+
# Safety: block dangerous commands
|
| 59 |
+
blocked = ["rm -rf /", ":(){ :|:& };:", "mkfs", "shutdown", "reboot", "halt", "dd if=/dev/"]
|
| 60 |
+
for b in blocked:
|
| 61 |
+
if b in command:
|
| 62 |
+
return f"❌ Blocked dangerous command: {command[:50]}"
|
| 63 |
|
| 64 |
+
await self.emit(task_id, "sandbox_exec", {
|
| 65 |
+
"command": command[:200],
|
| 66 |
+
"cwd": work_dir,
|
| 67 |
+
}, session_id)
|
| 68 |
|
| 69 |
try:
|
| 70 |
proc = await asyncio.create_subprocess_shell(
|
|
|
|
| 73 |
stderr=asyncio.subprocess.PIPE,
|
| 74 |
cwd=work_dir,
|
| 75 |
)
|
| 76 |
+
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout)
|
| 77 |
+
output = stdout.decode("utf-8", errors="replace")
|
| 78 |
+
err = stderr.decode("utf-8", errors="replace")
|
| 79 |
+
|
| 80 |
+
result = output[:3000]
|
| 81 |
+
if err and proc.returncode != 0:
|
| 82 |
+
result += f"\n⚠️ stderr:\n{err[:500]}"
|
| 83 |
+
|
| 84 |
+
await self.emit(task_id, "sandbox_result", {
|
| 85 |
+
"command": command[:100],
|
| 86 |
+
"exit_code": proc.returncode,
|
| 87 |
+
"output_length": len(output),
|
| 88 |
+
"success": proc.returncode == 0,
|
| 89 |
+
}, session_id)
|
| 90 |
+
|
| 91 |
+
return result or f"Command executed (exit code: {proc.returncode})"
|
| 92 |
+
except asyncio.TimeoutError:
|
| 93 |
+
return f"⚠️ Command timed out after {timeout}s"
|
| 94 |
except Exception as e:
|
| 95 |
+
return f"❌ Execution error: {str(e)}"
|
| 96 |
+
|
| 97 |
+
# ─── File Operations ──────────────────────────────────────────────────────
|
| 98 |
|
| 99 |
async def write_file(
|
| 100 |
self,
|
| 101 |
filename: str,
|
| 102 |
content: str,
|
| 103 |
task_id: str = "",
|
| 104 |
+
session_id: str = "",
|
| 105 |
) -> str:
|
| 106 |
+
"""Write file to workspace."""
|
| 107 |
+
filepath = os.path.join(WORKSPACE, filename)
|
| 108 |
+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
| 109 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
with open(filepath, "w", encoding="utf-8") as f:
|
| 111 |
f.write(content)
|
| 112 |
+
await self.emit(task_id, "file_written", {
|
| 113 |
+
"filename": filename,
|
| 114 |
+
"size": len(content),
|
| 115 |
+
"lines": len(content.split("\n")),
|
| 116 |
+
"path": filepath,
|
| 117 |
+
}, session_id)
|
| 118 |
+
return f"✅ File written: `{filename}` ({len(content)} chars, {len(content.split(chr(10)))} lines)"
|
| 119 |
except Exception as e:
|
| 120 |
+
return f"❌ Write failed: {str(e)}"
|
| 121 |
|
| 122 |
async def read_file(
|
| 123 |
self,
|
| 124 |
filename: str,
|
| 125 |
task_id: str = "",
|
| 126 |
+
session_id: str = "",
|
| 127 |
) -> str:
|
| 128 |
+
"""Read file from workspace."""
|
| 129 |
+
filepath = os.path.join(WORKSPACE, filename)
|
| 130 |
try:
|
| 131 |
+
with open(filepath, "r", encoding="utf-8") as f:
|
| 132 |
+
content = f.read()
|
| 133 |
+
await self.emit(task_id, "file_read", {
|
| 134 |
+
"filename": filename,
|
| 135 |
+
"size": len(content),
|
| 136 |
+
}, session_id)
|
| 137 |
+
return content[:5000]
|
| 138 |
+
except FileNotFoundError:
|
| 139 |
+
return f"❌ File not found: {filename}"
|
| 140 |
except Exception as e:
|
| 141 |
+
return f"❌ Read failed: {str(e)}"
|
| 142 |
|
| 143 |
+
async def list_files(self, path: str = "") -> List[str]:
|
| 144 |
+
"""List files in workspace."""
|
| 145 |
+
target = os.path.join(WORKSPACE, path) if path else WORKSPACE
|
| 146 |
try:
|
| 147 |
+
result = []
|
| 148 |
+
for root, dirs, files in os.walk(target):
|
| 149 |
+
# Skip hidden and cache dirs
|
| 150 |
+
dirs[:] = [d for d in dirs if not d.startswith(".") and d != "__pycache__" and d != "node_modules"]
|
| 151 |
+
for f in files:
|
| 152 |
+
rel = os.path.relpath(os.path.join(root, f), WORKSPACE)
|
| 153 |
+
result.append(rel)
|
| 154 |
+
if len(result) > 100:
|
| 155 |
+
break
|
| 156 |
+
return result
|
| 157 |
+
except Exception:
|
| 158 |
+
return []
|
| 159 |
+
|
| 160 |
+
# ─── Git Operations ───────────────────────────────────────────────────────
|
| 161 |
+
|
| 162 |
+
async def git_operation(
|
| 163 |
+
self,
|
| 164 |
+
task: str,
|
| 165 |
+
repo_path: str = "",
|
| 166 |
+
task_id: str = "",
|
| 167 |
+
session_id: str = "",
|
| 168 |
+
) -> str:
|
| 169 |
+
"""Perform git operations in workspace."""
|
| 170 |
+
work_dir = repo_path or WORKSPACE
|
| 171 |
+
task_lower = task.lower()
|
| 172 |
+
|
| 173 |
+
if "clone" in task_lower:
|
| 174 |
+
# Extract URL
|
| 175 |
+
words = task.split()
|
| 176 |
+
urls = [w for w in words if "github.com" in w or "gitlab.com" in w or ".git" in w]
|
| 177 |
+
if urls:
|
| 178 |
+
url = urls[0]
|
| 179 |
+
return await self.execute(f"git clone {url}", cwd=WORKSPACE, task_id=task_id, session_id=session_id)
|
| 180 |
+
return "❌ No git URL found in task."
|
| 181 |
+
|
| 182 |
+
elif "commit" in task_lower:
|
| 183 |
+
msg = task.replace("commit", "").strip() or "God Agent automated commit"
|
| 184 |
+
cmds = [
|
| 185 |
+
"git add -A",
|
| 186 |
+
f'git commit -m "{msg}"',
|
| 187 |
+
]
|
| 188 |
+
results = []
|
| 189 |
+
for cmd in cmds:
|
| 190 |
+
r = await self.execute(cmd, cwd=work_dir, task_id=task_id, session_id=session_id)
|
| 191 |
+
results.append(r)
|
| 192 |
+
return "\n".join(results)
|
| 193 |
+
|
| 194 |
+
elif "push" in task_lower:
|
| 195 |
+
return await self.execute("git push", cwd=work_dir, task_id=task_id, session_id=session_id)
|
| 196 |
+
|
| 197 |
+
elif "status" in task_lower:
|
| 198 |
+
return await self.execute("git status", cwd=work_dir, task_id=task_id, session_id=session_id)
|
| 199 |
+
|
| 200 |
+
elif "log" in task_lower:
|
| 201 |
+
return await self.execute("git log --oneline -10", cwd=work_dir, task_id=task_id, session_id=session_id)
|
| 202 |
+
|
| 203 |
+
elif "init" in task_lower:
|
| 204 |
+
return await self.execute("git init && git add -A", cwd=work_dir, task_id=task_id, session_id=session_id)
|
| 205 |
+
|
| 206 |
+
else:
|
| 207 |
+
return await self.execute(task, cwd=work_dir, task_id=task_id, session_id=session_id)
|
| 208 |
+
|
| 209 |
+
# ─── Package Management ───────────────────────────────────────────────────
|
| 210 |
+
|
| 211 |
+
async def install_packages(
|
| 212 |
+
self,
|
| 213 |
+
packages: List[str],
|
| 214 |
+
manager: str = "pip",
|
| 215 |
+
task_id: str = "",
|
| 216 |
+
session_id: str = "",
|
| 217 |
+
) -> str:
|
| 218 |
+
"""Install packages in workspace."""
|
| 219 |
+
pkg_str = " ".join(packages)
|
| 220 |
+
if manager == "pip":
|
| 221 |
+
cmd = f"pip install {pkg_str}"
|
| 222 |
+
elif manager == "npm":
|
| 223 |
+
cmd = f"npm install {pkg_str}"
|
| 224 |
+
elif manager == "pnpm":
|
| 225 |
+
cmd = f"pnpm add {pkg_str}"
|
| 226 |
+
else:
|
| 227 |
+
cmd = f"{manager} install {pkg_str}"
|
| 228 |
+
|
| 229 |
+
await self.emit(task_id, "installing_packages", {
|
| 230 |
+
"manager": manager,
|
| 231 |
+
"packages": packages,
|
| 232 |
+
}, session_id)
|
| 233 |
+
return await self.execute(cmd, task_id=task_id, session_id=session_id)
|
| 234 |
+
|
| 235 |
+
# ─── Workspace Info ───────────────────────────────────────────────────────
|
| 236 |
+
|
| 237 |
+
async def get_workspace_info(self) -> Dict:
|
| 238 |
+
"""Get workspace status."""
|
| 239 |
+
files = await self.list_files()
|
| 240 |
+
try:
|
| 241 |
+
disk = await self.execute("df -h /tmp | tail -1")
|
| 242 |
+
except Exception:
|
| 243 |
+
disk = "N/A"
|
| 244 |
+
return {
|
| 245 |
+
"path": WORKSPACE,
|
| 246 |
+
"file_count": len(files),
|
| 247 |
+
"files": files[:20],
|
| 248 |
+
"disk_usage": disk,
|
| 249 |
+
}
|
agents/test_agent.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
TestAgent v7 — Autonomous test generation, execution, and quality assurance
|
| 3 |
+
Like Devin's self-testing loop + Genspark's QA automation
|
| 4 |
+
"""
|
| 5 |
+
import asyncio
|
| 6 |
+
import json
|
| 7 |
+
import os
|
| 8 |
+
import re
|
| 9 |
+
from typing import Dict, List
|
| 10 |
+
import structlog
|
| 11 |
+
from .base_agent import BaseAgent
|
| 12 |
+
|
| 13 |
+
log = structlog.get_logger()
|
| 14 |
+
|
| 15 |
+
TEST_SYSTEM = """You are an elite autonomous test engineer.
|
| 16 |
+
You generate comprehensive tests: unit, integration, e2e, performance.
|
| 17 |
+
You analyze code for bugs, edge cases, and security vulnerabilities.
|
| 18 |
+
You write pytest (Python) and Jest/Vitest (TypeScript) tests.
|
| 19 |
+
Always aim for 80%+ test coverage and meaningful assertions.
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class TestAgent(BaseAgent):
|
| 24 |
+
def __init__(self, ws_manager=None, ai_router=None):
|
| 25 |
+
super().__init__("TestAgent", ws_manager, ai_router)
|
| 26 |
+
self.workspace = os.environ.get("WORKSPACE_DIR", "/tmp/god_workspace")
|
| 27 |
+
|
| 28 |
+
async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
|
| 29 |
+
session_id = kwargs.get("session_id", "")
|
| 30 |
+
task_id = kwargs.get("task_id", "")
|
| 31 |
+
await self.emit(task_id, "agent_start", {"agent": "TestAgent", "task": task[:80]}, session_id)
|
| 32 |
+
|
| 33 |
+
t = task.lower()
|
| 34 |
+
if any(k in t for k in ["generate test", "write test", "create test"]):
|
| 35 |
+
return await self._generate_tests(task, context, task_id, session_id)
|
| 36 |
+
if any(k in t for k in ["run test", "execute test", "pytest", "jest"]):
|
| 37 |
+
return await self._run_tests(task, context, task_id, session_id)
|
| 38 |
+
if any(k in t for k in ["coverage", "quality", "audit"]):
|
| 39 |
+
return await self._quality_audit(task, context, task_id, session_id)
|
| 40 |
+
return await self._generate_tests(task, context, task_id, session_id)
|
| 41 |
+
|
| 42 |
+
async def _generate_tests(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 43 |
+
code = context.get("code", "")
|
| 44 |
+
language = context.get("language", "python")
|
| 45 |
+
await self.emit(task_id, "tool_called", {
|
| 46 |
+
"agent": "TestAgent", "tool": "generate_tests", "step": "Generating tests"
|
| 47 |
+
}, session_id)
|
| 48 |
+
msgs = [
|
| 49 |
+
{"role": "system", "content": TEST_SYSTEM},
|
| 50 |
+
{"role": "user", "content": (
|
| 51 |
+
f"Task: {task}\nLanguage: {language}\n\n"
|
| 52 |
+
f"Code to test:\n{code[:3000] if code else 'Generate tests for: ' + task}\n\n"
|
| 53 |
+
"Generate comprehensive tests with:\n"
|
| 54 |
+
"1. Happy path tests\n2. Edge case tests\n3. Error handling tests\n"
|
| 55 |
+
"4. Mocks for external dependencies\n5. Clear test descriptions"
|
| 56 |
+
)},
|
| 57 |
+
]
|
| 58 |
+
result = await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.2, max_tokens=8192)
|
| 59 |
+
# Save test file to workspace
|
| 60 |
+
test_filename = f"test_{re.sub(r'[^a-z0-9]', '_', task.lower()[:30])}.py"
|
| 61 |
+
test_path = os.path.join(self.workspace, "tests", test_filename)
|
| 62 |
+
os.makedirs(os.path.dirname(test_path), exist_ok=True)
|
| 63 |
+
code_blocks = re.findall(r'```(?:python|py)?\n(.*?)```', result, re.DOTALL)
|
| 64 |
+
if code_blocks:
|
| 65 |
+
with open(test_path, "w") as f:
|
| 66 |
+
f.write(code_blocks[0])
|
| 67 |
+
await self.emit(task_id, "file_written", {"path": test_path}, session_id)
|
| 68 |
+
return result
|
| 69 |
+
|
| 70 |
+
async def _run_tests(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 71 |
+
repo_path = context.get("repo_path", self.workspace)
|
| 72 |
+
await self.emit(task_id, "tool_called", {
|
| 73 |
+
"agent": "TestAgent", "tool": "run_tests", "step": "Executing tests"
|
| 74 |
+
}, session_id)
|
| 75 |
+
# Detect test runner
|
| 76 |
+
if os.path.exists(os.path.join(repo_path, "package.json")):
|
| 77 |
+
cmd = ["npm", "test", "--", "--watchAll=false"]
|
| 78 |
+
else:
|
| 79 |
+
cmd = ["python", "-m", "pytest", "-v", "--tb=short"]
|
| 80 |
+
try:
|
| 81 |
+
proc = await asyncio.create_subprocess_exec(
|
| 82 |
+
*cmd, cwd=repo_path,
|
| 83 |
+
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
|
| 84 |
+
)
|
| 85 |
+
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=120)
|
| 86 |
+
output = stdout.decode() + stderr.decode()
|
| 87 |
+
passed = len(re.findall(r'PASSED|✓|pass', output, re.I))
|
| 88 |
+
failed = len(re.findall(r'FAILED|✗|fail', output, re.I))
|
| 89 |
+
await self.emit(task_id, "tests_complete", {"passed": passed, "failed": failed}, session_id)
|
| 90 |
+
return f"**Test Results:** ✅ {passed} passed | ❌ {failed} failed\n\n```\n{output[:3000]}\n```"
|
| 91 |
+
except Exception as e:
|
| 92 |
+
return f"❌ Test run error: {str(e)}"
|
| 93 |
+
|
| 94 |
+
async def _quality_audit(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 95 |
+
code = context.get("code", "")
|
| 96 |
+
msgs = [
|
| 97 |
+
{"role": "system", "content": TEST_SYSTEM},
|
| 98 |
+
{"role": "user", "content": (
|
| 99 |
+
f"Task: {task}\n\nCode:\n{code[:3000]}\n\n"
|
| 100 |
+
"Provide quality audit: coverage estimate, complexity score, bugs found, security issues, and recommendations."
|
| 101 |
+
)},
|
| 102 |
+
]
|
| 103 |
+
return await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.3, max_tokens=4096)
|
agents/vision_agent.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
VisionAgent v7 — UI/UX generation, design-to-code, screenshot analysis
|
| 3 |
+
Converts designs, wireframes, and visual specs into real code
|
| 4 |
+
"""
|
| 5 |
+
import json
|
| 6 |
+
import os
|
| 7 |
+
from typing import Dict
|
| 8 |
+
import structlog
|
| 9 |
+
from .base_agent import BaseAgent
|
| 10 |
+
|
| 11 |
+
log = structlog.get_logger()
|
| 12 |
+
|
| 13 |
+
VISION_SYSTEM = """You are an elite UI/UX engineer and design-to-code specialist.
|
| 14 |
+
You can:
|
| 15 |
+
- Convert design mockups and wireframes into pixel-perfect React/Next.js components
|
| 16 |
+
- Generate beautiful, responsive UI components with Tailwind CSS
|
| 17 |
+
- Create complete page layouts with animations (Framer Motion)
|
| 18 |
+
- Analyze screenshots and reproduce UI components
|
| 19 |
+
- Generate design systems, color palettes, and typography scales
|
| 20 |
+
- Build dashboards, landing pages, admin panels, mobile-first UIs
|
| 21 |
+
|
| 22 |
+
Always produce production-ready code with:
|
| 23 |
+
- Responsive design (mobile-first)
|
| 24 |
+
- Accessibility (ARIA labels, semantic HTML)
|
| 25 |
+
- Dark/light mode support
|
| 26 |
+
- Smooth animations and transitions
|
| 27 |
+
- TypeScript types
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class VisionAgent(BaseAgent):
|
| 32 |
+
def __init__(self, ws_manager=None, ai_router=None):
|
| 33 |
+
super().__init__("VisionAgent", ws_manager, ai_router)
|
| 34 |
+
self.workspace = os.environ.get("WORKSPACE_DIR", "/tmp/god_workspace")
|
| 35 |
+
|
| 36 |
+
async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
|
| 37 |
+
session_id = kwargs.get("session_id", "")
|
| 38 |
+
task_id = kwargs.get("task_id", "")
|
| 39 |
+
await self.emit(task_id, "agent_start", {"agent": "VisionAgent", "task": task[:80]}, session_id)
|
| 40 |
+
|
| 41 |
+
t = task.lower()
|
| 42 |
+
if any(k in t for k in ["dashboard", "admin", "panel"]):
|
| 43 |
+
return await self._generate_dashboard(task, context, task_id, session_id)
|
| 44 |
+
if any(k in t for k in ["landing", "homepage", "hero"]):
|
| 45 |
+
return await self._generate_landing_page(task, context, task_id, session_id)
|
| 46 |
+
if any(k in t for k in ["component", "button", "card", "form", "modal", "nav"]):
|
| 47 |
+
return await self._generate_component(task, context, task_id, session_id)
|
| 48 |
+
return await self._generate_ui(task, context, task_id, session_id)
|
| 49 |
+
|
| 50 |
+
async def _generate_dashboard(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 51 |
+
await self.emit(task_id, "tool_called", {
|
| 52 |
+
"agent": "VisionAgent", "tool": "generate_dashboard", "step": "Generating dashboard UI"
|
| 53 |
+
}, session_id)
|
| 54 |
+
msgs = [
|
| 55 |
+
{"role": "system", "content": VISION_SYSTEM},
|
| 56 |
+
{"role": "user", "content": (
|
| 57 |
+
f"Build a complete dashboard UI: {task}\n\n"
|
| 58 |
+
"Requirements:\n"
|
| 59 |
+
"- Dark theme with glass morphism effects\n"
|
| 60 |
+
"- Sidebar navigation with icons\n"
|
| 61 |
+
"- Header with user menu\n"
|
| 62 |
+
"- Stats cards with trends\n"
|
| 63 |
+
"- Data tables with sorting\n"
|
| 64 |
+
"- Charts/graphs area\n"
|
| 65 |
+
"- React + TypeScript + Tailwind CSS\n"
|
| 66 |
+
"Generate the complete component code."
|
| 67 |
+
)},
|
| 68 |
+
]
|
| 69 |
+
return await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.4, max_tokens=8192)
|
| 70 |
+
|
| 71 |
+
async def _generate_landing_page(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 72 |
+
await self.emit(task_id, "tool_called", {
|
| 73 |
+
"agent": "VisionAgent", "tool": "generate_landing", "step": "Generating landing page"
|
| 74 |
+
}, session_id)
|
| 75 |
+
msgs = [
|
| 76 |
+
{"role": "system", "content": VISION_SYSTEM},
|
| 77 |
+
{"role": "user", "content": (
|
| 78 |
+
f"Create a stunning landing page: {task}\n\n"
|
| 79 |
+
"Include: Hero section, Features grid, Pricing table, Testimonials, CTA, Footer\n"
|
| 80 |
+
"Style: Modern, gradient backgrounds, glassmorphism, smooth animations\n"
|
| 81 |
+
"Tech: Next.js + TypeScript + Tailwind + Framer Motion"
|
| 82 |
+
)},
|
| 83 |
+
]
|
| 84 |
+
return await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.5, max_tokens=8192)
|
| 85 |
+
|
| 86 |
+
async def _generate_component(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 87 |
+
await self.emit(task_id, "tool_called", {
|
| 88 |
+
"agent": "VisionAgent", "tool": "generate_component", "step": "Generating UI component"
|
| 89 |
+
}, session_id)
|
| 90 |
+
msgs = [
|
| 91 |
+
{"role": "system", "content": VISION_SYSTEM},
|
| 92 |
+
{"role": "user", "content": (
|
| 93 |
+
f"Create a polished UI component: {task}\n\n"
|
| 94 |
+
"Requirements:\n"
|
| 95 |
+
"- Fully typed with TypeScript\n"
|
| 96 |
+
"- Responsive and accessible\n"
|
| 97 |
+
"- Dark/light mode support\n"
|
| 98 |
+
"- Smooth hover/focus animations\n"
|
| 99 |
+
"- Props with sensible defaults"
|
| 100 |
+
)},
|
| 101 |
+
]
|
| 102 |
+
return await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.4, max_tokens=8192)
|
| 103 |
+
|
| 104 |
+
async def _generate_ui(self, task: str, context: Dict, task_id: str, session_id: str) -> str:
|
| 105 |
+
msgs = [
|
| 106 |
+
{"role": "system", "content": VISION_SYSTEM},
|
| 107 |
+
{"role": "user", "content": f"Generate UI for: {task}\n\nContext: {json.dumps(context)[:300]}"},
|
| 108 |
+
]
|
| 109 |
+
return await self.llm(msgs, task_id=task_id, session_id=session_id, temperature=0.5, max_tokens=8192)
|
main_v7.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
🚀 GOD AGENT OS v7 — Autonomous Engineering Operating System
|
| 3 |
+
Manus + Genspark + Devin (OneHand) Combined
|
| 4 |
+
Version: 7.0.0
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import asyncio
|
| 8 |
+
import json
|
| 9 |
+
import logging
|
| 10 |
+
import os
|
| 11 |
+
import time
|
| 12 |
+
import uuid
|
| 13 |
+
from contextlib import asynccontextmanager
|
| 14 |
+
from typing import Optional
|
| 15 |
+
|
| 16 |
+
import structlog
|
| 17 |
+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, Request
|
| 18 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 19 |
+
from fastapi.middleware.gzip import GZipMiddleware
|
| 20 |
+
from fastapi.responses import JSONResponse
|
| 21 |
+
from slowapi import Limiter, _rate_limit_exceeded_handler
|
| 22 |
+
from slowapi.util import get_remote_address
|
| 23 |
+
from slowapi.errors import RateLimitExceeded
|
| 24 |
+
|
| 25 |
+
from api.routes import tasks, chat, memory, github, health
|
| 26 |
+
from api.routes import connectors, agents as agents_router
|
| 27 |
+
from api.websocket_manager import WebSocketManager
|
| 28 |
+
from core.task_engine import TaskEngine
|
| 29 |
+
from memory.db import init_db
|
| 30 |
+
|
| 31 |
+
# ─── v7 God Agent Ecosystem ────────────────────────────────────────────────────
|
| 32 |
+
from ai_router.router import AIRouter
|
| 33 |
+
from agents.orchestrator_v7 import GodAgentOrchestratorV7
|
| 34 |
+
from agents.chat_agent import ChatAgent
|
| 35 |
+
from agents.planner_agent import PlannerAgent
|
| 36 |
+
from agents.coding_agent import CodingAgent
|
| 37 |
+
from agents.debug_agent import DebugAgent
|
| 38 |
+
from agents.memory_agent import MemoryAgent
|
| 39 |
+
from agents.connector_agent import ConnectorAgent
|
| 40 |
+
from agents.deploy_agent import DeployAgent
|
| 41 |
+
from agents.workflow_agent import WorkflowAgent
|
| 42 |
+
from agents.sandbox_agent import SandboxAgent
|
| 43 |
+
from agents.ui_agent import UIAgent
|
| 44 |
+
from agents.reasoning_agent import ReasoningAgent
|
| 45 |
+
# v7 new agents
|
| 46 |
+
from agents.browser_agent import BrowserAgent
|
| 47 |
+
from agents.file_agent import FileAgent
|
| 48 |
+
from agents.git_agent import GitAgent
|
| 49 |
+
from agents.test_agent import TestAgent
|
| 50 |
+
from agents.vision_agent import VisionAgent
|
| 51 |
+
from connectors.manager import ConnectorManager
|
| 52 |
+
|
| 53 |
+
# ─── Structured Logging ────────────────────────────────────────────────────────
|
| 54 |
+
structlog.configure(
|
| 55 |
+
processors=[
|
| 56 |
+
structlog.processors.TimeStamper(fmt="iso"),
|
| 57 |
+
structlog.stdlib.add_log_level,
|
| 58 |
+
structlog.processors.StackInfoRenderer(),
|
| 59 |
+
structlog.dev.ConsoleRenderer(),
|
| 60 |
+
]
|
| 61 |
+
)
|
| 62 |
+
log = structlog.get_logger()
|
| 63 |
+
|
| 64 |
+
# ─── Rate Limiter ──────────────────────────────────────────────────────────────
|
| 65 |
+
limiter = Limiter(key_func=get_remote_address)
|
| 66 |
+
|
| 67 |
+
# ─── Global Managers ──────────────────────────────────────────────────────────
|
| 68 |
+
ws_manager = WebSocketManager()
|
| 69 |
+
task_engine = TaskEngine(ws_manager)
|
| 70 |
+
ai_router = AIRouter(ws_manager)
|
| 71 |
+
connector_manager = ConnectorManager()
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# ─── Build v7 God Agent Ecosystem ─────────────────────────────────────────────
|
| 75 |
+
def build_orchestrator_v7() -> GodAgentOrchestratorV7:
|
| 76 |
+
orchestrator = GodAgentOrchestratorV7(ws_manager=ws_manager, ai_router=ai_router)
|
| 77 |
+
|
| 78 |
+
# ── Core agents (v3 retained) ──────────────────────────────────────────
|
| 79 |
+
orchestrator.register_agent("chat", ChatAgent(ws_manager, ai_router))
|
| 80 |
+
orchestrator.register_agent("planner", PlannerAgent(ws_manager, ai_router))
|
| 81 |
+
orchestrator.register_agent("coding", CodingAgent(ws_manager, ai_router))
|
| 82 |
+
orchestrator.register_agent("debug", DebugAgent(ws_manager, ai_router))
|
| 83 |
+
orchestrator.register_agent("memory", MemoryAgent(ws_manager, ai_router))
|
| 84 |
+
orchestrator.register_agent("connector", ConnectorAgent(ws_manager, ai_router))
|
| 85 |
+
orchestrator.register_agent("deploy", DeployAgent(ws_manager, ai_router))
|
| 86 |
+
orchestrator.register_agent("workflow", WorkflowAgent(ws_manager, ai_router))
|
| 87 |
+
orchestrator.register_agent("sandbox", SandboxAgent(ws_manager, ai_router))
|
| 88 |
+
orchestrator.register_agent("ui", UIAgent(ws_manager, ai_router))
|
| 89 |
+
orchestrator.register_agent("reasoning", ReasoningAgent(ws_manager, ai_router))
|
| 90 |
+
|
| 91 |
+
# ── v7 NEW agents ──────────────────────────────────────────────────────
|
| 92 |
+
orchestrator.register_agent("browser", BrowserAgent(ws_manager, ai_router))
|
| 93 |
+
orchestrator.register_agent("file", FileAgent(ws_manager, ai_router))
|
| 94 |
+
orchestrator.register_agent("git", GitAgent(ws_manager, ai_router))
|
| 95 |
+
orchestrator.register_agent("test", TestAgent(ws_manager, ai_router))
|
| 96 |
+
orchestrator.register_agent("vision", VisionAgent(ws_manager, ai_router))
|
| 97 |
+
|
| 98 |
+
log.info("🤖 GOD AGENT v7 Ecosystem initialized", agents=16)
|
| 99 |
+
return orchestrator
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
orchestrator = build_orchestrator_v7()
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
@asynccontextmanager
|
| 106 |
+
async def lifespan(app: FastAPI):
|
| 107 |
+
log.info("🚀 Starting GOD AGENT OS v7 — Autonomous Engineering Platform...")
|
| 108 |
+
await init_db()
|
| 109 |
+
await task_engine.start()
|
| 110 |
+
asyncio.create_task(ws_manager.heartbeat_loop())
|
| 111 |
+
log.info("✅ GOD AGENT v7 — All 16 agents online")
|
| 112 |
+
log.info("🤖 Core: Chat, Planner, Coding, Debug, Memory, Connector, Deploy, Workflow, Sandbox, UI, Reasoning")
|
| 113 |
+
log.info("⚡ v7 New: Browser, File, Git, Test, Vision")
|
| 114 |
+
log.info("🌐 AI Router: OpenAI → Groq → Cerebras → OpenRouter → Anthropic (auto-failover)")
|
| 115 |
+
yield
|
| 116 |
+
log.info("🛑 Shutting down GOD AGENT v7...")
|
| 117 |
+
await task_engine.stop()
|
| 118 |
+
log.info("✅ Shutdown complete")
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
# ─── FastAPI App ───────────────────────────────────────────────────────────────
|
| 122 |
+
app = FastAPI(
|
| 123 |
+
title="🤖 GOD AGENT OS v7",
|
| 124 |
+
description="Autonomous Engineering OS — Manus + Genspark + Devin (OneHand) Combined",
|
| 125 |
+
version="7.0.0",
|
| 126 |
+
lifespan=lifespan,
|
| 127 |
+
docs_url="/api/docs",
|
| 128 |
+
redoc_url="/api/redoc",
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
app.state.limiter = limiter
|
| 132 |
+
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
| 133 |
+
|
| 134 |
+
# ─── Share state ───────────────────────────────────────────────────────────────
|
| 135 |
+
app.state.ws_manager = ws_manager
|
| 136 |
+
app.state.task_engine = task_engine
|
| 137 |
+
app.state.ai_router = ai_router
|
| 138 |
+
app.state.orchestrator = orchestrator
|
| 139 |
+
app.state.connector_manager = connector_manager
|
| 140 |
+
|
| 141 |
+
# ─── Middleware ────────────────────────────────────────────────────────────────
|
| 142 |
+
app.add_middleware(
|
| 143 |
+
CORSMiddleware,
|
| 144 |
+
allow_origins=["*"],
|
| 145 |
+
allow_credentials=True,
|
| 146 |
+
allow_methods=["*"],
|
| 147 |
+
allow_headers=["*"],
|
| 148 |
+
)
|
| 149 |
+
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
@app.middleware("http")
|
| 153 |
+
async def log_requests(request: Request, call_next):
|
| 154 |
+
start = time.time()
|
| 155 |
+
response = await call_next(request)
|
| 156 |
+
duration = round((time.time() - start) * 1000, 2)
|
| 157 |
+
log.info("HTTP", method=request.method, path=request.url.path, status=response.status_code, ms=duration)
|
| 158 |
+
return response
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
# ─── REST API Routers ──────────────────────────────────────────────────────────
|
| 162 |
+
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
| 163 |
+
app.include_router(tasks.router, prefix="/api/v1/tasks", tags=["tasks"])
|
| 164 |
+
app.include_router(chat.router, prefix="/api/v1", tags=["chat"])
|
| 165 |
+
app.include_router(memory.router, prefix="/api/v1/memory", tags=["memory"])
|
| 166 |
+
app.include_router(github.router, prefix="/api/v1/github", tags=["github"])
|
| 167 |
+
app.include_router(connectors.router, prefix="/api/v1/connectors", tags=["connectors"])
|
| 168 |
+
app.include_router(agents_router.router, prefix="/api/v1/agents", tags=["agents"])
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# ─── WebSocket Endpoints ───────────────────────────────────────────────────────
|
| 172 |
+
@app.websocket("/ws/tasks/{task_id}")
|
| 173 |
+
async def ws_task(websocket: WebSocket, task_id: str):
|
| 174 |
+
await ws_manager.connect(websocket, room=f"task:{task_id}")
|
| 175 |
+
try:
|
| 176 |
+
while True:
|
| 177 |
+
data = await websocket.receive_text()
|
| 178 |
+
msg = json.loads(data)
|
| 179 |
+
if msg.get("type") == "ping":
|
| 180 |
+
await websocket.send_json({"type": "pong", "timestamp": time.time()})
|
| 181 |
+
except WebSocketDisconnect:
|
| 182 |
+
ws_manager.disconnect(websocket, room=f"task:{task_id}")
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
@app.websocket("/ws/logs")
|
| 186 |
+
async def ws_logs(websocket: WebSocket):
|
| 187 |
+
await ws_manager.connect(websocket, room="logs")
|
| 188 |
+
try:
|
| 189 |
+
while True:
|
| 190 |
+
data = await websocket.receive_text()
|
| 191 |
+
msg = json.loads(data)
|
| 192 |
+
if msg.get("type") == "ping":
|
| 193 |
+
await websocket.send_json({"type": "pong", "timestamp": time.time()})
|
| 194 |
+
except WebSocketDisconnect:
|
| 195 |
+
ws_manager.disconnect(websocket, room="logs")
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
@app.websocket("/ws/chat/{session_id}")
|
| 199 |
+
async def ws_chat(websocket: WebSocket, session_id: str):
|
| 200 |
+
await ws_manager.connect(websocket, room=f"chat:{session_id}")
|
| 201 |
+
try:
|
| 202 |
+
while True:
|
| 203 |
+
data = await websocket.receive_text()
|
| 204 |
+
msg = json.loads(data)
|
| 205 |
+
if msg.get("type") == "ping":
|
| 206 |
+
await websocket.send_json({"type": "pong", "timestamp": time.time()})
|
| 207 |
+
elif msg.get("type") == "chat_message":
|
| 208 |
+
asyncio.create_task(
|
| 209 |
+
orchestrator.orchestrate(
|
| 210 |
+
user_message=msg.get("content", ""),
|
| 211 |
+
session_id=session_id,
|
| 212 |
+
context=msg.get("context", {}),
|
| 213 |
+
)
|
| 214 |
+
)
|
| 215 |
+
elif msg.get("type") == "task_message":
|
| 216 |
+
from core.models import TaskCreateRequest
|
| 217 |
+
req = TaskCreateRequest(
|
| 218 |
+
goal=msg.get("content", ""),
|
| 219 |
+
session_id=session_id,
|
| 220 |
+
)
|
| 221 |
+
asyncio.create_task(task_engine.submit(req))
|
| 222 |
+
except WebSocketDisconnect:
|
| 223 |
+
ws_manager.disconnect(websocket, room=f"chat:{session_id}")
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
@app.websocket("/ws/agent/status")
|
| 227 |
+
async def ws_agent_status(websocket: WebSocket):
|
| 228 |
+
await ws_manager.connect(websocket, room="agent_status")
|
| 229 |
+
try:
|
| 230 |
+
while True:
|
| 231 |
+
data = await websocket.receive_text()
|
| 232 |
+
msg = json.loads(data)
|
| 233 |
+
if msg.get("type") == "ping":
|
| 234 |
+
await websocket.send_json({"type": "pong", "timestamp": time.time()})
|
| 235 |
+
elif msg.get("type") == "get_status":
|
| 236 |
+
await websocket.send_json({
|
| 237 |
+
"type": "agent_status",
|
| 238 |
+
"data": orchestrator.get_status(),
|
| 239 |
+
})
|
| 240 |
+
except WebSocketDisconnect:
|
| 241 |
+
ws_manager.disconnect(websocket, room="agent_status")
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
@app.websocket("/ws/sandbox/{session_id}")
|
| 245 |
+
async def ws_sandbox(websocket: WebSocket, session_id: str):
|
| 246 |
+
await ws_manager.connect(websocket, room=f"sandbox:{session_id}")
|
| 247 |
+
sandbox = orchestrator.get_agent("sandbox")
|
| 248 |
+
try:
|
| 249 |
+
while True:
|
| 250 |
+
data = await websocket.receive_text()
|
| 251 |
+
msg = json.loads(data)
|
| 252 |
+
if msg.get("type") == "ping":
|
| 253 |
+
await websocket.send_json({"type": "pong", "timestamp": time.time()})
|
| 254 |
+
elif msg.get("type") == "execute" and sandbox:
|
| 255 |
+
cmd = msg.get("command", "")
|
| 256 |
+
result = await sandbox.execute(cmd, session_id=session_id)
|
| 257 |
+
await websocket.send_json({
|
| 258 |
+
"type": "terminal_output",
|
| 259 |
+
"command": cmd,
|
| 260 |
+
"output": result,
|
| 261 |
+
"timestamp": time.time(),
|
| 262 |
+
})
|
| 263 |
+
except WebSocketDisconnect:
|
| 264 |
+
ws_manager.disconnect(websocket, room=f"sandbox:{session_id}")
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
# ─── v7 New Endpoints ──────────────────────────────────────────────────────────
|
| 268 |
+
@app.post("/api/v1/browser/research")
|
| 269 |
+
async def browser_research(request: Request):
|
| 270 |
+
body = await request.json()
|
| 271 |
+
query = body.get("query", "")
|
| 272 |
+
session_id = body.get("session_id", "")
|
| 273 |
+
browser = orchestrator.get_agent("browser")
|
| 274 |
+
if not browser:
|
| 275 |
+
raise HTTPException(status_code=503, detail="BrowserAgent not available")
|
| 276 |
+
result = await browser.run(query, session_id=session_id)
|
| 277 |
+
return {"result": result}
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
@app.get("/api/v1/files/workspace")
|
| 281 |
+
async def list_workspace():
|
| 282 |
+
file_agent = orchestrator.get_agent("file")
|
| 283 |
+
if not file_agent:
|
| 284 |
+
return {"workspace": "/tmp/god_workspace", "files": [], "total": 0}
|
| 285 |
+
return file_agent.list_workspace()
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
@app.post("/api/v1/git/pr")
|
| 289 |
+
async def create_pr(request: Request):
|
| 290 |
+
body = await request.json()
|
| 291 |
+
git_agent = orchestrator.get_agent("git")
|
| 292 |
+
if not git_agent:
|
| 293 |
+
raise HTTPException(status_code=503, detail="GitAgent not available")
|
| 294 |
+
result = await git_agent.create_github_pr(
|
| 295 |
+
repo_owner=body.get("owner", ""),
|
| 296 |
+
repo_name=body.get("repo", ""),
|
| 297 |
+
title=body.get("title", ""),
|
| 298 |
+
body=body.get("body", ""),
|
| 299 |
+
head_branch=body.get("head_branch", "main"),
|
| 300 |
+
base_branch=body.get("base_branch", "main"),
|
| 301 |
+
)
|
| 302 |
+
return result
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
@app.post("/api/v1/vision/generate")
|
| 306 |
+
async def generate_ui(request: Request):
|
| 307 |
+
body = await request.json()
|
| 308 |
+
vision = orchestrator.get_agent("vision")
|
| 309 |
+
if not vision:
|
| 310 |
+
raise HTTPException(status_code=503, detail="VisionAgent not available")
|
| 311 |
+
result = await vision.run(
|
| 312 |
+
body.get("prompt", ""),
|
| 313 |
+
context=body.get("context", {}),
|
| 314 |
+
session_id=body.get("session_id", ""),
|
| 315 |
+
)
|
| 316 |
+
return {"result": result}
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
# ─── Root ──────────────────────────────────────────────────────────────────────
|
| 320 |
+
@app.get("/")
|
| 321 |
+
async def root():
|
| 322 |
+
cs = connector_manager.get_summary()
|
| 323 |
+
status = orchestrator.get_status()
|
| 324 |
+
return {
|
| 325 |
+
"name": "🤖 GOD AGENT OS v7",
|
| 326 |
+
"version": "7.0.0",
|
| 327 |
+
"status": "operational",
|
| 328 |
+
"mode": "autonomous_engineering_os",
|
| 329 |
+
"description": "Manus + Genspark + Devin (OneHand) — Autonomous Engineering Platform",
|
| 330 |
+
"agents": status["agents"],
|
| 331 |
+
"total_agents": status["total_agents"],
|
| 332 |
+
"capabilities": status["capabilities"],
|
| 333 |
+
"connectors": {
|
| 334 |
+
"connected": cs["connected"],
|
| 335 |
+
"total": cs["total"],
|
| 336 |
+
"ai_ready": cs["ai_ready"],
|
| 337 |
+
},
|
| 338 |
+
"docs": "/api/docs",
|
| 339 |
+
"websockets": [
|
| 340 |
+
"/ws/tasks/{task_id}",
|
| 341 |
+
"/ws/logs",
|
| 342 |
+
"/ws/chat/{session_id}",
|
| 343 |
+
"/ws/agent/status",
|
| 344 |
+
"/ws/sandbox/{session_id}",
|
| 345 |
+
],
|
| 346 |
+
"v7_new_features": [
|
| 347 |
+
"🌐 BrowserAgent — Web research & scraping",
|
| 348 |
+
"📁 FileAgent — Full file system control & project scaffolding",
|
| 349 |
+
"🔀 GitAgent — Autonomous Git & GitHub PR operations",
|
| 350 |
+
"🧪 TestAgent — Auto test generation & execution",
|
| 351 |
+
"🎨 VisionAgent — Design-to-code UI generation",
|
| 352 |
+
"⚡ 16-agent parallel orchestration",
|
| 353 |
+
"🔄 Advanced self-healing loop",
|
| 354 |
+
"🧠 Enhanced intent classification",
|
| 355 |
+
"📊 Real-time execution timeline",
|
| 356 |
+
],
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
if __name__ == "__main__":
|
| 361 |
+
import uvicorn
|
| 362 |
+
port = int(os.environ.get("PORT", 8000))
|
| 363 |
+
uvicorn.run("main_v7:app", host="0.0.0.0", port=port, reload=False, workers=1)
|
requirements.txt
CHANGED
|
@@ -3,17 +3,24 @@ uvicorn[standard]==0.29.0
|
|
| 3 |
websockets==12.0
|
| 4 |
pydantic==2.7.1
|
| 5 |
pydantic-settings==2.2.1
|
|
|
|
| 6 |
python-multipart==0.0.9
|
| 7 |
aiohttp==3.9.5
|
| 8 |
aiosqlite==0.20.0
|
|
|
|
| 9 |
httpx==0.27.0
|
|
|
|
|
|
|
| 10 |
gitpython==3.1.43
|
| 11 |
pygithub==2.3.0
|
| 12 |
python-dotenv==1.0.1
|
| 13 |
slowapi==0.1.9
|
| 14 |
structlog==24.1.0
|
| 15 |
rich==13.7.1
|
| 16 |
-
psutil==5.9.8
|
| 17 |
-
python-jose[cryptography]==3.3.0
|
| 18 |
passlib[bcrypt]==1.7.4
|
| 19 |
cryptography==42.0.7
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
websockets==12.0
|
| 4 |
pydantic==2.7.1
|
| 5 |
pydantic-settings==2.2.1
|
| 6 |
+
python-jose[cryptography]==3.3.0
|
| 7 |
python-multipart==0.0.9
|
| 8 |
aiohttp==3.9.5
|
| 9 |
aiosqlite==0.20.0
|
| 10 |
+
sqlalchemy[asyncio]==2.0.30
|
| 11 |
httpx==0.27.0
|
| 12 |
+
openai==1.30.1
|
| 13 |
+
anthropic==0.26.1
|
| 14 |
gitpython==3.1.43
|
| 15 |
pygithub==2.3.0
|
| 16 |
python-dotenv==1.0.1
|
| 17 |
slowapi==0.1.9
|
| 18 |
structlog==24.1.0
|
| 19 |
rich==13.7.1
|
|
|
|
|
|
|
| 20 |
passlib[bcrypt]==1.7.4
|
| 21 |
cryptography==42.0.7
|
| 22 |
+
psutil==5.9.8
|
| 23 |
+
huggingface_hub==0.23.2
|
| 24 |
+
requests==2.32.2
|
| 25 |
+
beautifulsoup4==4.12.3
|
| 26 |
+
lxml==5.2.2
|