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 */}
+
+ {/* Agent Modal */}
+ {selectedAgent && (
+ setSelectedAgent(null)} />
+ )}
);
}