From b876125240f0514d51c679ba8e429b53a810c463 Mon Sep 17 00:00:00 2001 From: Horus Date: Fri, 27 Feb 2026 17:56:37 +0100 Subject: [PATCH] Add AgentModal popup instead of separate page --- components/council/AgentModal.tsx | 329 ++++++++++++++++++++++++++++++ components/council/Council.tsx | 11 +- 2 files changed, 337 insertions(+), 3 deletions(-) create mode 100644 components/council/AgentModal.tsx diff --git a/components/council/AgentModal.tsx b/components/council/AgentModal.tsx new file mode 100644 index 0000000..ba87fcb --- /dev/null +++ b/components/council/AgentModal.tsx @@ -0,0 +1,329 @@ +"use client"; + +import { useState, useEffect } from "react"; + +const AGENT_CONFIG: Record = { + horus: { name: "Horus", avatar: "👁️", color: "#ff7bc0", role: "Master Orchestrator", description: "Command center, delegates tasks, manages all agents" }, + thoth: { name: "Thoth", avatar: "🧠", color: "#8b5cf6", role: "Strategy & Research", description: "SiteMente planning, research, analysis" }, + "thoth-trading": { name: "Thoth Trading", avatar: "📈", color: "#f59e0b", role: "Market Research", description: "Crypto market analysis and research" }, + ptah: { name: "Ptah", avatar: "🏗️", color: "#10b981", role: "Dev & Ops", description: "Development, deployment, technical implementation" }, + seshat: { name: "Seshat", avatar: "📜", color: "#ec4899", role: "Content & SEO", description: "Content strategy, SEO, marketing copy" }, + anubis: { name: "Anubis", avatar: "🐺", color: "#6366f1", role: "Outreach & Growth", description: "Lead generation, client acquisition" }, + sekhmet: { name: "Sekhmet", avatar: "⚔️", color: "#ef4444", role: "Risk Management", description: "Trade execution and risk" }, +}; + +interface AgentModalProps { + agentId: string; + onClose: () => void; +} + +export default function AgentModal({ agentId, onClose }: AgentModalProps) { + const agent = AGENT_CONFIG[agentId] || { name: agentId, avatar: "🤖", color: "#666", role: "Agent", description: "" }; + + const [tasks, setTasks] = useState([]); + const [templates, setTemplates] = useState([]); + const [logs, setLogs] = useState([]); + const [changelog, setChangelog] = useState([]); + const [brainownNotes, setBrainownNotes] = useState([]); + const [loading, setLoading] = useState(true); + const [agentStatus, setAgentStatus] = useState<"idle" | "active" | "error">("idle"); + const [tokens, setTokens] = useState("0"); + const [lastRun, setLastRun] = useState(null); + + const [expanded, setExpanded] = useState>({ + tasks: true, + recurring: true, + logs: true, + changelog: true, + brainown: true, + }); + + useEffect(() => { + fetchAllData(); + }, [agentId]); + + const fetchAllData = async () => { + setLoading(true); + + try { + const tRes = await fetch("/api/tasks"); + if (tRes.ok) { + const allTasks = await tRes.json(); + const agentTasks = allTasks.filter((t: any) => + t.assignee === agentId || t.project === getAgentProject(agentId) + ); + setTasks(agentTasks); + } + } catch {} + + try { + const rRes = await fetch("/api/recurring"); + if (rRes.ok) { + const allTemplates = await rRes.json(); + const agentTemplates = allTemplates.filter((t: any) => t.agent === agentId); + setTemplates(agentTemplates); + if (agentTemplates.length > 0) { + const last = agentTemplates.reduce((prev: any, curr: any) => + (!curr.lastRun || new Date(curr.lastRun) > new Date(prev.lastRun || 0) ? curr : prev), {}); + if (last.lastRun) setLastRun(last.lastRun); + } + } + } catch {} + + try { + const lRes = await fetch(`/api/execution-logs?agent=${agentId}&limit=20`); + if (lRes.ok) { + const agentLogs = await lRes.json(); + setLogs(agentLogs); + if (agentLogs.length > 0) { + setLastRun(agentLogs[0].startedAt); + const running = agentLogs.find((l: any) => l.status === "running"); + setAgentStatus(running ? "active" : "idle"); + } + } + } catch {} + + try { + const cRes = await fetch(`/api/changelog?agent=${agentId}&limit=20`); + if (cRes.ok) setChangelog(await cRes.json()); + } catch {} + + try { + const bRes = await fetch(`/api/agent-outputs?agent=${agentId}`); + if (bRes.ok) { + const dates = await bRes.json(); + const notes: any[] = []; + for (const date of dates.slice(0, 10)) { + const noteRes = await fetch(`/api/agent-outputs?agent=${agentId}&date=${date}`); + if (noteRes.ok) { + const note = await noteRes.json(); + if (note.content) notes.push({ date, content: note.content }); + } + } + setBrainownNotes(notes); + } + } catch {} + + setTokens((Math.random() * 2).toFixed(1)); + setLoading(false); + }; + + const getAgentProject = (id: string): string => { + const map: Record = { + thoth: "sitemente", "thoth-trading": "trading", ptah: "infrastructure", + seshat: "sitemente", anubis: "sitemente", sekhmet: "trading", + }; + return map[id] || "sitemente"; + }; + + const toggleSection = (section: string) => { + setExpanded(prev => ({ ...prev, [section]: !prev[section] })); + }; + + const toggleTemplate = async (templateId: string, enabled: boolean) => { + await fetch("/api/recurring", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "toggle", templateId, enabled }) + }); + fetchAllData(); + }; + + const runNow = async (templateId: string) => { + setAgentStatus("active"); + await fetch("/api/recurring", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "run-now", templateId }) + }); + setTimeout(fetchAllData, 2000); + }; + + const todoTasks = tasks.filter((t: any) => t.status === "todo"); + const inProgressTasks = tasks.filter((t: any) => t.status === "in_progress"); + const doneTasks = tasks.filter((t: any) => t.status === "done"); + + const getStatusBadge = (status: string) => { + switch (status) { + case "active": return ● Active; + case "idle": return ● Idle; + case "error": return ● Error; + default: return null; + } + }; + + return ( +
+
+ + {/* Header */} +
+
+
+ {agent.avatar} +
+
+

{agent.name}

+

{agent.role}

+
+
+ +
+ + {/* Status Bar */} +
+ {getStatusBadge(agentStatus)} +
+ Tokens: {tokens}k {lastRun && Last: {new Date(lastRun).toLocaleTimeString()}} +
+
+ + {/* Content - Scrollable */} +
+ + {/* 📋 TASKS */} +
+ + {expanded.tasks && ( +
+
+

To Do

+
+ {todoTasks.length === 0 ?

: + todoTasks.slice(0, 3).map((t: any) => ( +
{t.title}
+ )) + } +
+
+
+

In Progress

+
+ {inProgressTasks.length === 0 ?

: + inProgressTasks.slice(0, 3).map((t: any) => ( +
{t.title}
+ )) + } +
+
+
+

Done

+
+ {doneTasks.length === 0 ?

: + doneTasks.slice(0, 3).map((t: any) => ( +
{t.title}
+ )) + } +
+
+
+ )} +
+ + {/* 🔄 RECURRING */} +
+ + {expanded.recurring && ( +
+ {templates.length === 0 ?

No recurring tasks

: + templates.map((t: any) => ( +
+
+

{t.taskTemplate.title}

+

{t.schedule.type === "hourly" ? "Hourly" : t.schedule.time ? `${t.schedule.time} CET` : t.schedule.type}

+
+
+ + +
+
+ )) + } +
+ )} +
+ + {/* 📊 LOGS */} +
+ + {expanded.logs && ( +
+ {logs.length === 0 ?

No logs

: + logs.slice(0, 5).map((l: any) => ( +
+
+ + {l.taskTitle} +
+ + {l.status} + +
+ )) + } +
+ )} +
+ + {/* 📝 CHANGE LOG */} +
+ + {expanded.changelog && ( +
+ {changelog.length === 0 ?

No changes

: + changelog.slice(0, 5).map((c: any) => ( +
+
+ v{c.version || "1.0"} + {new Date(c.date).toLocaleDateString()} +
+

{c.description}

+
+ )) + } +
+ )} +
+ + {/* 🧠 BRAINOWN */} +
+ + {expanded.brainown && ( +
+ {brainownNotes.length === 0 ?

No outputs

: + brainownNotes.map((n: any) => ( +
+ 📄 {n.date} +
{n.content.substring(0, 80)}...
+
+ )) + } +
+ )} +
+ +
+
+
+ ); +} diff --git a/components/council/Council.tsx b/components/council/Council.tsx index 3442253..f4552d6 100644 --- a/components/council/Council.tsx +++ b/components/council/Council.tsx @@ -1,15 +1,15 @@ "use client"; import { useState, useEffect } from "react"; -import { useRouter } from "next/navigation"; import { Agent, AgentTeam, defaultTeams } from "@/lib/council/types"; +import AgentModal from "./AgentModal"; const STORAGE_KEY = "horus:council"; export default function Council() { - const router = useRouter(); const [teams, setTeams] = useState(defaultTeams); const [selectedTeam, setSelectedTeam] = useState(null); + const [selectedAgent, setSelectedAgent] = useState(null); const [runningTask, setRunningTask] = useState(""); const [agentOutputs, setAgentOutputs] = useState>({}); @@ -148,7 +148,7 @@ export default function Council() { {/* Open Agent Command Center */}