"use client";
// The agent's "anatomy" as a grid of compact icon cards. Each card opens a modal
// with the full, pretty-formatted section content. The four brain text files
// (system prompt / how it operates / personality / skills) are editable in the
// modal and saved back to the .brain via PATCH /api/claws/{id}/brain; the derived
// sections (capabilities / tools / memory / safety) render read-only (tools keeps
// its "add tool" affordance).
import { useState, type ReactNode } from "react";
import { Brain, Check, Cpu, Database, Drama, Pencil, Plus, ScrollText, ShieldCheck, Workflow, Wrench, X, Zap } from "lucide-react";
import type { DemoAgent } from "@/lib/dashboard-demo";
import { MarkdownText, PersonalityBody, mono, tag, type RawBrain } from "./anatomy-cards";
type EditField = "system_prompt" | "agent_md" | "persona" | "skills_md";
interface Section {
key: string;
label: string;
icon: ReactNode;
tint: string;
status?: string; // small hint on the card (count / "editable")
field?: EditField; // present ⇒ editable + saved to the brain
value?: string; // editable source text
placeholder?: string;
body: ReactNode; // formatted, read-only display for the modal
}
function IconCard({ section, onClick }: { section: Section; onClick: () => void }) {
return (
);
}
function SectionModal({ section, clawId, onClose, onSaved, onAddTool }: { section: Section; clawId: string; onClose: () => void; onSaved?: () => void; onAddTool?: () => void }) {
const [editing, setEditing] = useState(false);
const [draft, setDraft] = useState(section.value ?? "");
const [saving, setSaving] = useState(false);
const save = async () => {
if (!section.field) return;
setSaving(true);
try {
await fetch(`/api/claws/${clawId}/brain`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ [section.field]: draft }) });
setEditing(false);
onSaved?.();
} catch {
/* keep the editor open on failure */
} finally {
setSaving(false);
}
};
return (
e.stopPropagation()} style={{ width: "min(760px, 94vw)", maxHeight: "84vh", display: "flex", flexDirection: "column", borderRadius: 16, background: "#0f0f13", border: `1px solid ${section.tint}45`, boxShadow: "0 30px 80px rgba(0,0,0,.6)" }}>
{/* Header */}
{section.icon}
{section.label}
{section.field && !editing ? (
) : null}
{/* Body */}
{editing ? (
) : (
section.body
)}
{/* Footer affordance (tools) */}
{section.key === "tools" && onAddTool && !editing ? (
) : null}
);
}
const muted = (t: string): ReactNode => {t};
const row = (a: string, b: string, bc = "#cfcfd5"): ReactNode => (
{a}{b}
);
export function AnatomyGrid({ agent, brain, onSaved, onAddTool }: { agent: DemoAgent; brain?: RawBrain; onSaved?: () => void; onAddTool?: () => void }) {
const [open, setOpen] = useState(null);
const c = agent.compartments;
const systemPrompt = brain?.system_prompt ?? agent.systemPrompt ?? "";
const skillsList = brain?.skills ?? [];
const sections: Section[] = [
{
key: "system_prompt", label: "SYSTEM PROMPT", icon: , tint: "#ff6f61",
field: "system_prompt", value: systemPrompt, placeholder: "System prompt / job description…",
body: systemPrompt.trim() ? : muted("No system prompt set — click Edit to write one."),
},
{
key: "agent_md", label: "HOW IT OPERATES", icon: , tint: "#ff8a7a",
field: "agent_md", value: brain?.agent_md ?? "", placeholder: "AGENTS.md — how this agent operates (workflow, rules)…",
body: brain?.agent_md?.trim() ? : muted("No AGENTS.md yet — click Edit to define how this agent operates."),
},
{
key: "personality", label: "PERSONALITY", icon: , tint: "#c98af0",
field: "persona", value: brain?.personality ?? "", placeholder: "Personality — tone, traits, voice…",
body: ,
},
{
key: "skills", label: "SKILLS", icon: , tint: "#ff6f61",
status: String(skillsList.length || c.skills.length), field: "skills_md", value: brain?.skills_md ?? "",
placeholder: "Skills — a narrative of what this agent is good at…",
body: brain?.skills_md?.trim() ? (
) : skillsList.length ? (
{skillsList.map((s) => (
))}
) : c.skills.length ? (
{c.skills.map((s) => {s})}
) : muted("No skills yet — click Edit to describe this agent's skills."),
},
{
key: "capabilities", label: "CAPABILITIES", icon: , tint: "#e8b465",
status: String(c.capabilities.length),
body: c.capabilities.length ? {c.capabilities.map((p) => {p})}
: muted("No capabilities listed."),
},
{
key: "tools", label: "TOOLS", icon: , tint: "#5ec8d8",
status: String(c.tools.length),
body: c.tools.length ? (
{c.tools.map((t) => row(t.name, t.state === "gated" ? "gated ✓" : "blocked ⨯", t.state === "gated" ? "#5fd08a" : "#e8b465"))}
) : muted("No tools yet — use Add tool below."),
},
{
key: "memory", label: "MEMORY", icon: , tint: "#5fd08a",
status: brain ? `${brain.stats.memories}` : undefined,
body: brain && brain.memory.length ? (
{brain.memory.map((m, i) =>
{m}
)}
) : muted("No memories yet — they accrue as this agent runs and chats."),
},
{
key: "safety", label: "SAFETY · §15", icon: , tint: "#6fd0c0",
body: {row("Sandbox", c.safety.sandbox, "#6fd0c0")}{row("Network", c.safety.network, "#6fd0c0")}
,
},
];
const active = sections.find((s) => s.key === open) ?? null;
return (
<>
{/* The .brain container — brain icon + label, then a 4×2 grid of section cards. */}
.brain
{sections.map((s) => setOpen(s.key)} />)}
{active ? setOpen(null)} onSaved={onSaved} onAddTool={onAddTool} /> : null}
>
);
}