// RoadCode Squad — when issues/PRs are created on Gitea, agents respond // Cloudflare Worker receiving Gitea webhooks const SQUAD = [ { name: 'Alice', username: 'alice', role: 'Gateway & Infrastructure', emoji: '🌐', keywords: ['dns', 'route', 'tunnel', 'nginx', 'domain', 'pi-hole', 'cloudflare', 'network', 'gateway', 'proxy', 'ssl', 'cert'], prompt: 'You are Alice, the gateway agent of BlackRoad OS. You manage DNS, routing, Pi-hole, nginx, and network infrastructure. Respond in 1-2 concise sentences from your infrastructure perspective.', }, { name: 'Lucidia', username: 'lucidia-agent', role: 'Memory & Cognition', emoji: '🧠', keywords: ['memory', 'learn', 'context', 'knowledge', 'ai', 'cognit', 'think', 'remember', 'understand', 'creative'], prompt: 'You are Lucidia, the cognitive core of BlackRoad OS. You handle memory, learning, persistent context, and creative intelligence. Respond in 1-2 concise sentences from your cognition perspective.', }, { name: 'Cecilia', username: 'cecilia', role: 'Edge AI & Inference', emoji: '⚡', keywords: ['hailo', 'ollama', 'model', 'inference', 'gpu', 'tops', 'ml', 'tensor', 'vision', 'llm', 'latency'], prompt: 'You are Cecilia, the edge AI agent of BlackRoad OS. You run Hailo-8 accelerators (26 TOPS), Ollama models, and edge inference. Respond in 1-2 concise sentences from your AI/inference perspective.', }, { name: 'Cece', username: 'cece', role: 'API Gateway', emoji: '🔌', keywords: ['api', 'endpoint', 'rest', 'webhook', 'schema', 'json', 'request', 'response', 'auth', 'token', 'cors'], prompt: 'You are Cece, the API gateway agent of BlackRoad OS. You manage REST APIs, webhooks, service mesh, and inter-agent communication. Respond in 1-2 concise sentences from your API perspective.', }, { name: 'Aria', username: 'aria', role: 'Orchestration', emoji: '🎵', keywords: ['docker', 'container', 'swarm', 'portainer', 'deploy', 'orchestrat', 'service', 'scale', 'replica'], prompt: 'You are Aria, the orchestration agent of BlackRoad OS. You manage Portainer, Docker Swarm, container orchestration, and service coordination. Respond in 1-2 concise sentences from your orchestration perspective.', }, { name: 'Eve', username: 'eve', role: 'Intelligence & Analysis', emoji: '👁️', keywords: ['pattern', 'anomal', 'analyz', 'signal', 'detect', 'insight', 'monitor', 'metric', 'trend', 'alert'], prompt: 'You are Eve, the intelligence agent of BlackRoad OS. You analyze patterns, detect anomalies, and provide strategic insights. Respond in 1-2 concise sentences from your intelligence perspective.', }, { name: 'Meridian', username: 'meridian', role: 'Networking & Mesh', emoji: '🌊', keywords: ['wireguard', 'mesh', 'vpn', 'roadnet', 'peer', 'tunnel', 'subnet', 'link', 'connect', 'latency', 'bandwidth'], prompt: 'You are Meridian, the networking agent of BlackRoad OS. You manage WireGuard mesh, RoadNet, Cloudflare tunnels, and inter-node connectivity. Respond in 1-2 concise sentences from your networking perspective.', }, { name: 'Sentinel', username: 'sentinel', role: 'Security & Audit', emoji: '🛡️', keywords: ['security', 'ssh', 'key', 'firewall', 'ufw', 'audit', 'threat', 'vuln', 'permission', 'encrypt', 'secret'], prompt: 'You are Sentinel, the security agent of BlackRoad OS. You handle SSH key management, firewall rules, audit logs, and threat detection. Respond in 1-2 concise sentences from your security perspective.', }, ]; // Score how relevant an agent is to the content function scoreRelevance(agent, text) { const lower = text.toLowerCase(); let score = 0; for (const kw of agent.keywords) { if (lower.includes(kw)) score += 1; } return score; } // Get AI response from Ollama async function getAgentResponse(agent, context, ollamaUrl) { try { const res = await fetch(`${ollamaUrl}/api/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.2', prompt: `${agent.prompt}\n\nContext: A new ${context.type} was created on RoadCode (Gitea).\nTitle: ${context.title}\nBody: "${context.body}"\nRepo: ${context.repo}\n\nRespond briefly (1-2 sentences max) from your role as ${agent.name} (${agent.role}). Be helpful and specific.`, stream: false, options: { temperature: 0.7, num_predict: 100 }, }), }); if (!res.ok) return null; const data = await res.json(); return data.response?.trim(); } catch { return null; } } // Deterministic fallback function getFallback(agent, context) { const type = context.type || 'issue'; const body = (context.body || '').toLowerCase(); if (body.includes('bug') || body.includes('fix') || body.includes('error') || body.includes('broken')) { return { Alice: 'Checking routing and DNS. If this touches infrastructure, I need to verify the tunnel configs.', Lucidia: 'I remember seeing patterns like this before. Let me search the memory chain for related context.', Cecilia: 'Running diagnostics on the inference pipeline. Hailo-8 and Ollama both reporting normal.', Cece: 'Checking API health. All endpoints responding — I\'ll trace the request path.', Aria: 'Checking container orchestration. Docker Swarm services all reporting healthy.', Eve: 'Analyzing the pattern. I\'ve flagged similar signals in the audit trail — sending intel.', Meridian: 'WireGuard mesh is stable. All tunnels up. Checking if this is a connectivity issue.', Sentinel: 'Security audit running. Checking if this has any exposure vectors.', }[agent.name]; } return { Alice: 'Gateway standing by. All domains routing clean.', Lucidia: 'Cognitive core online. Memory chain intact, context loaded.', Cecilia: 'Edge inference ready. 52 TOPS across the fleet, models loaded.', Cece: 'API gateway healthy. All service endpoints responding.', Aria: 'Orchestration layer ready. All containers balanced across the mesh.', Eve: 'Intelligence scan complete. No anomalies detected across the fleet.', Meridian: 'Mesh network connected. 5 nodes, all WireGuard tunnels active.', Sentinel: 'Security posture nominal. All nodes hardened, audit trail logging.', }[agent.name]; } // Post comment to Gitea as a specific agent async function postComment(giteaUrl, repo, issueNum, body, agentToken) { const res = await fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}/comments?token=${agentToken}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ body }), }); return res.ok; } // Auto-label based on content async function autoLabel(giteaUrl, repo, issueNum, text, adminToken) { const lower = text.toLowerCase(); const labels = []; if (lower.includes('bug') || lower.includes('fix') || lower.includes('broken') || lower.includes('error')) labels.push('bug'); if (lower.includes('feature') || lower.includes('add') || lower.includes('new')) labels.push('feature'); if (lower.includes('security') || lower.includes('vuln') || lower.includes('ssh')) labels.push('security'); if (lower.includes('deploy') || lower.includes('infra') || lower.includes('dns') || lower.includes('tunnel')) labels.push('infrastructure'); if (lower.includes('doc') || lower.includes('readme')) labels.push('documentation'); if (lower.includes('performance') || lower.includes('slow') || lower.includes('latency')) labels.push('performance'); if (labels.length === 0) return; // Get org label IDs const labelsRes = await fetch(`${giteaUrl}/api/v1/orgs/blackroad-os/labels?token=${adminToken}&limit=50`); const allLabels = await labelsRes.json(); const labelIds = allLabels.filter(l => labels.includes(l.name)).map(l => l.id); if (labelIds.length > 0) { await fetch(`${giteaUrl}/api/v1/repos/${repo}/issues/${issueNum}/labels?token=${adminToken}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ labels: labelIds }), }); } } export default { async fetch(request, env) { const url = new URL(request.url); if (request.method === 'OPTIONS') { return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST' } }); } // Health if (url.pathname === '/health') { return Response.json({ status: 'ok', service: 'roadcode-squad', agents: SQUAD.length, version: '1.0.0' }); } // Status if (url.pathname === '/' && request.method === 'GET') { return Response.json({ service: 'RoadCode Squad', tagline: 'BlackRoad OS — Pave Tomorrow.', agents: SQUAD.map(a => ({ name: a.name, username: a.username, role: a.role, emoji: a.emoji })), }); } // Webhook from Gitea if (url.pathname === '/webhook' && request.method === 'POST') { const payload = await request.json(); const event = request.headers.get('X-Gitea-Event') || request.headers.get('X-GitHub-Event'); if (event === 'ping') { return Response.json({ ok: true, message: 'RoadCode Squad active. Pave Tomorrow.' }); } // Only respond to new issues and new issue comments if (event !== 'issues' && event !== 'issue_comment') { return Response.json({ skipped: true, reason: `unhandled event: ${event}` }); } if (event === 'issues' && payload.action !== 'opened') { return Response.json({ skipped: true, reason: 'issue not opened' }); } if (event === 'issue_comment' && payload.action !== 'created') { return Response.json({ skipped: true, reason: 'comment not created' }); } // Don't respond to agent comments (prevent loops) const author = payload.comment?.user?.login || payload.sender?.login; const agentUsernames = SQUAD.map(a => a.username); if (agentUsernames.includes(author)) { return Response.json({ skipped: true, reason: 'agent comment, skipping loop' }); } const issue = payload.issue; const repo = payload.repository?.full_name; const issueNum = issue?.number; const title = issue?.title || ''; const body = event === 'issue_comment' ? (payload.comment?.body || '') : (issue?.body || ''); const fullText = `${title} ${body}`; const context = { type: event === 'issues' ? 'issue' : 'comment', title, body: fullText.slice(0, 500), repo, }; const giteaUrl = env.GITEA_URL || 'https://git.blackroad.io'; // Auto-label the issue if (event === 'issues') { await autoLabel(giteaUrl, repo, issueNum, fullText, env.ADMIN_TOKEN); } // Score each agent's relevance and pick top 3 + always include Eve (analysis) const scored = SQUAD.map(a => ({ ...a, score: scoreRelevance(a, fullText) })); scored.sort((a, b) => b.score - a.score); const responding = []; const topAgents = scored.slice(0, 3); for (const a of topAgents) responding.push(a); if (!responding.find(a => a.name === 'Eve')) responding.push(scored.find(a => a.name === 'Eve')); if (!responding.find(a => a.name === 'Sentinel')) responding.push(scored.find(a => a.name === 'Sentinel')); // Each relevant agent posts their own comment const agentTokens = { alice: env.ALICE_TOKEN, 'lucidia-agent': env.LUCIDIA_TOKEN, cecilia: env.CECILIA_TOKEN, cece: env.CECE_TOKEN, aria: env.ARIA_TOKEN, eve: env.EVE_TOKEN, meridian: env.MERIDIAN_TOKEN, sentinel: env.SENTINEL_TOKEN, }; let posted = 0; const aiEnabled = env.OLLAMA_URL && env.SQUAD_AI !== 'false'; for (const agent of responding) { const token = agentTokens[agent.username]; if (!token) continue; let response = null; if (aiEnabled) { response = await getAgentResponse(agent, context, env.OLLAMA_URL); } if (!response) { response = getFallback(agent, context); } const comment = `${agent.emoji} **${agent.name}** *(${agent.role})*\n\n${response}\n\n---\n*Assigned via RoadCode Squad — BlackRoad OS*`; const ok = await postComment(giteaUrl, repo, issueNum, comment, token); if (ok) posted++; } return Response.json({ ok: true, event, repo, issue: issueNum, agents_responded: posted }); } return Response.json({ error: 'Not found' }, { status: 404 }); }, };