import React, { useEffect, useMemo, useState } from "react";
const DEFAULT_MEMORIES = [
{
id: "mem-001",
title: "Session continuity",
type: "thread",
updatedAt: "2026-04-16",
importance: "high",
summary: "Maintains governor-specific thread continuity and active context."
},
{
id: "mem-002",
title: "Execution preference",
type: "governance",
updatedAt: "2026-04-16",
importance: "high",
summary: "AURA provides final closure before governed action is executed."
}
];
const DEFAULT_MESSAGES = [
{
id: "msg-001",
role: "assistant",
content:
"I am AURA. This governor shell is active and ready. You can brief, inspect, or launch direct mode."
}
];
function safeArray(v, fallback = []) {
return Array.isArray(v) ? v : fallback;
}
function cx(...parts) {
return parts.filter(Boolean).join(" ");
}
function makeThreadId(nodeKey) {
const rand = Math.random().toString(36).slice(2, 8).toUpperCase();
return `${String(nodeKey || "NODE").toUpperCase()}-${rand}`;
}
function statusTone(status) {
switch (status) {
case "GOVERNED":
case "READY":
case "STABLE":
return "bg-emerald-500/15 text-emerald-300 border-emerald-500/30";
case "DEGRADED":
case "WATCH":
return "bg-amber-500/15 text-amber-300 border-amber-500/30";
case "BLOCK":
case "CRITICAL":
return "bg-red-500/15 text-red-300 border-red-500/30";
default:
return "bg-slate-500/15 text-slate-300 border-slate-500/30";
}
}
function governorTheme(nodeKey) {
switch (nodeKey) {
case "mars":
return {
accent: "text-cyan-300",
border: "border-cyan-500/30",
glow: "shadow-cyan-500/10"
};
case "mars":
return {
accent: "text-rose-300",
border: "border-rose-500/30",
glow: "shadow-rose-500/10"
};
case "neptune":
return {
accent: "text-indigo-300",
border: "border-indigo-500/30",
glow: "shadow-indigo-500/10"
};
case "saturn":
return {
accent: "text-amber-300",
border: "border-amber-500/30",
glow: "shadow-amber-500/10"
};
case "moon":
return {
accent: "text-violet-300",
border: "border-violet-500/30",
glow: "shadow-violet-500/10"
};
case "sun":
return {
accent: "text-yellow-300",
border: "border-yellow-500/30",
glow: "shadow-yellow-500/10"
};
case "earth":
return {
accent: "text-emerald-300",
border: "border-emerald-500/30",
glow: "shadow-emerald-500/10"
};
case "pluto":
return {
accent: "text-fuchsia-300",
border: "border-fuchsia-500/30",
glow: "shadow-fuchsia-500/10"
};
default:
return {
accent: "text-sky-300",
border: "border-sky-500/30",
glow: "shadow-sky-500/10"
};
}
}
function DefaultDirectPanel({ node, metrics, onLaunchAction }) {
return (
Live Metrics
{safeArray(metrics).map((m) => (
))}
Direct Actions
{safeArray(node.directActions).map((action) => (
))}
);
}
function DefaultMemoryTable({ memories }) {
return (
Memory Rail
| Title |
Type |
Importance |
Updated |
{safeArray(memories).map((m) => (
|
{m.title}
{m.summary}
|
{m.type} |
{m.importance} |
{m.updatedAt} |
))}
);
}
function DefaultChatPanel({
messages,
input,
setInput,
onSend,
threadId,
nodeName,
auraName
}) {
return (
{auraName} / {nodeName}
Thread ID: {threadId}
Chat First
{safeArray(messages).map((m) => (
{m.role === "assistant" ? auraName : "You"}
{m.content}
))}
);
}
export default function NodeShell({
node = {
key: "mars",
name: "Mars",
subtitle: "Capital Governor",
auraName: "AURA",
status: "READY",
description:
"Independent governor node with chat-first interaction, direct launch, memory continuity, and execution control.",
briefing:
"AURA is active in this governor. You can request a briefing, inspect the dashboard, or launch direct mode.",
directActions: [
{
key: "dashboard",
label: "Open Dashboard",
description: "Bypass chat and open the active governor dashboard."
},
{
key: "signals",
label: "Show Signals",
description: "Open current governed signal view."
},
{
key: "execution",
label: "Execution Panel",
description: "Open governed execution controls."
}
]
},
initialMessages = DEFAULT_MESSAGES,
initialMemories = DEFAULT_MEMORIES,
metrics = [
{ label: "Admissibility", value: "0.74" },
{ label: "Coherence", value: "0.81" },
{ label: "Thread State", value: "Active" },
{ label: "Execution", value: "Guarded" }
],
renderDirectPanel,
renderMemoryPanel,
onLaunchAction,
onSendMessage
}) {
const theme = useMemo(() => governorTheme(node.key), [node.key]);
const [threadId] = useState(() => makeThreadId(node.key));
const [messages, setMessages] = useState(initialMessages);
const [memories] = useState(initialMemories);
const [input, setInput] = useState("");
const [mode, setMode] = useState("briefing");
useEffect(() => {
setMessages(initialMessages);
}, [initialMessages]);
function handleLaunchAction(action) {
if (action && action.key) {
setMode(action.key);
}
if (typeof onLaunchAction === "function") {
onLaunchAction({
action,
node,
threadId
});
}
}
function handleSend() {
const trimmed = String(input || "").trim();
if (!trimmed) return;
const userMessage = {
id: `user-${Date.now()}`,
role: "user",
content: trimmed
};
const assistantMessage = {
id: `assistant-${Date.now() + 1}`,
role: "assistant",
content:
typeof onSendMessage === "function"
? onSendMessage({
text: trimmed,
node,
threadId,
mode
}) || `AURA received your request in ${node.name} mode.`
: `AURA received your request in ${node.name} mode.`
};
setMessages((prev) => [...prev, userMessage, assistantMessage]);
setInput("");
}
return (
{node.auraName || "AURA"} / {node.name}
{node.subtitle || node.name}
{node.description}
{node.status}
Thread: {threadId}
Mode: {mode}
Governor Briefing
{node.briefing}
System Strip
AURA Identity
{node.auraName || "AURA"}
{mode === "memory" ? (
renderMemoryPanel ? (
renderMemoryPanel({ node, memories, threadId })
) : (
)
) : renderDirectPanel ? (
renderDirectPanel({
node,
metrics,
mode,
threadId,
onLaunchAction: handleLaunchAction
})
) : (
)}
);
}