import { createElement } from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; import type { AgentState } from "@/features/agents/state/store"; import { AgentBrainPanel } from "@/features/agents/components/AgentInspectPanels"; import type { GatewayClient } from "@/lib/gateway/GatewayClient"; const createAgent = (agentId: string, name: string, sessionKey: string): AgentState => ({ agentId, name, sessionKey, status: "idle", sessionCreated: true, awaitingUserInput: false, hasUnseenActivity: false, outputLines: [], lastResult: null, lastDiff: null, runId: null, runStartedAt: null, streamText: null, thinkingTrace: null, latestOverride: null, latestOverrideKind: null, lastAssistantMessageAt: null, lastActivityAt: null, latestPreview: null, lastUserMessage: null, draft: "", sessionSettingsSynced: true, historyLoadedAt: null, historyFetchLimit: null, historyFetchedCount: null, historyMaybeTruncated: false, toolCallingEnabled: true, showThinkingTraces: true, model: null, thinkingLevel: null, avatarSeed: `seed-${agentId}`, avatarUrl: null, }); const createMockClient = () => { const filesByAgent: Record> = { "agent-1": { "AGENTS.md": "alpha agents", "SOUL.md": "# SOUL.md - Who You Are\n\n## Core Truths\n\nBe useful.", "IDENTITY.md": "# IDENTITY.md - Who Am I?\n\n- Name: Alpha\n- Creature: droid\n- Vibe: calm\n- Emoji: 🤖\n", "USER.md": "# USER.md - About Your Human\n\n- Name: George\n- What to call them: GP\n\n## Context\n\nBuilding Claw3D.", "TOOLS.md": "tool notes", "HEARTBEAT.md": "heartbeat notes", "MEMORY.md": "durable memory", }, "agent-2": { "AGENTS.md": "beta agents", }, }; const calls: Array<{ method: string; params: unknown }> = []; const client = { call: vi.fn(async (method: string, params: unknown) => { calls.push({ method, params }); if (method === "agents.files.get") { const record = params && typeof params === "object" ? (params as Record) : {}; const agentId = typeof record.agentId === "string" ? record.agentId : ""; const name = typeof record.name === "string" ? record.name : ""; const content = filesByAgent[agentId]?.[name]; if (typeof content !== "string") { return { file: { name, missing: true } }; } return { file: { name, missing: false, content } }; } if (method === "agents.files.set") { const record = params && typeof params === "object" ? (params as Record) : {}; const agentId = typeof record.agentId === "string" ? record.agentId : ""; const name = typeof record.name === "string" ? record.name : ""; const content = typeof record.content === "string" ? record.content : ""; if (!filesByAgent[agentId]) { filesByAgent[agentId] = {}; } filesByAgent[agentId][name] = content; return { ok: true }; } return {}; }), } as unknown as GatewayClient; return { client, calls, filesByAgent }; }; describe("AgentBrainPanel", () => { afterEach(() => { cleanup(); }); it("renders_behavior_sections_and_loads_agent_files", async () => { const { client } = createMockClient(); const agents = [ createAgent("agent-1", "Alpha", "session-1"), createAgent("agent-2", "Beta", "session-2"), ]; render( createElement(AgentBrainPanel, { client, agents, selectedAgentId: "agent-1", }) ); await waitFor(() => { expect(screen.getByRole("heading", { name: "Persona" })).toBeInTheDocument(); }); expect(screen.getByRole("heading", { name: "Directives" })).toBeInTheDocument(); expect(screen.getByRole("heading", { name: "Context" })).toBeInTheDocument(); expect(screen.getByRole("heading", { name: "Identity" })).toBeInTheDocument(); expect(screen.getByLabelText("Directives")).toHaveValue("alpha agents"); expect(screen.getByLabelText("Persona")).toHaveValue( "# SOUL.md - Who You Are\n\n## Core Truths\n\nBe useful." ); expect(screen.getByLabelText("Name")).toHaveValue("Alpha"); }); it("shows_actionable_message_when_session_key_missing", async () => { const { client } = createMockClient(); const agents = [createAgent("", "Alpha", "session-1")]; render( createElement(AgentBrainPanel, { client, agents, selectedAgentId: "", }) ); await waitFor(() => { expect(screen.getByText("Agent ID is missing for this agent.")).toBeInTheDocument(); }); }); it("saves_updated_behavior_files", async () => { const { client, calls, filesByAgent } = createMockClient(); const agents = [createAgent("agent-1", "Alpha", "session-1")]; render( createElement(AgentBrainPanel, { client, agents, selectedAgentId: "agent-1", }) ); await waitFor(() => { expect(screen.getByLabelText("Directives")).toBeInTheDocument(); }); fireEvent.change(screen.getByLabelText("Directives"), { target: { value: "alpha directives updated" }, }); const saveButton = screen.getByRole("button", { name: "Save" }); expect(saveButton).not.toBeDisabled(); fireEvent.click(saveButton); await waitFor(() => { expect(calls.some((entry) => entry.method === "agents.files.set")).toBe(true); }); expect(filesByAgent["agent-1"]["AGENTS.md"]).toBe("alpha directives updated"); }); it("discards_unsaved_changes_without_writing_files", async () => { const { client, calls } = createMockClient(); const agents = [createAgent("agent-1", "Alpha", "session-1")]; render( createElement(AgentBrainPanel, { client, agents, selectedAgentId: "agent-1", }) ); await waitFor(() => { expect(screen.getByLabelText("Name")).toBeInTheDocument(); }); fireEvent.change(screen.getByLabelText("Name"), { target: { value: "Alpha Prime" }, }); expect(screen.getByLabelText("Name")).toHaveValue("Alpha Prime"); fireEvent.click(screen.getByRole("button", { name: "Discard" })); expect(screen.getByLabelText("Name")).toHaveValue("Alpha"); expect(calls.some((entry) => entry.method === "agents.files.set")).toBe(false); }); it("does_not_render_name_editor_in_personality_panel", async () => { const { client } = createMockClient(); const agents = [createAgent("agent-1", "Alpha", "session-1")]; render( createElement(AgentBrainPanel, { client, agents, selectedAgentId: "agent-1", }) ); await waitFor(() => { expect(screen.getByRole("heading", { name: "Persona" })).toBeInTheDocument(); }); expect(screen.queryByLabelText("Agent name")).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Update Name" })).not.toBeInTheDocument(); }); });