import { useAgent } from "agents/react"; import { nanoid } from "nanoid"; import { useState, useEffect, useCallback } from "react"; import { Button, Surface, Empty, Text } from "@cloudflare/kumo"; import { DemoWrapper } from "../../layout"; import { LogPanel, ConnectionStatus, CodeExplanation, type CodeSection } from "../../components"; import { useLogs, useUserId } from "../../hooks"; import type { ChildState } from "./child-agent"; import type { SupervisorAgent, SupervisorState } from "./supervisor-agent"; interface ChildInfo { id: string; state: ChildState; } const codeSections: CodeSection[] = [ { title: "Spawn child agents with getAgentByName", description: "The supervisor creates child agents by calling getAgentByName(). Each child is a separate Durable Object with its own state and lifecycle.", code: `import { Agent, callable, getAgentByName } from "agents"; class SupervisorAgent extends Agent { @callable() async createChild(childId: string) { const child = await getAgentByName(this.env.ChildAgent, childId); await child.initialize({ createdBy: this.name }); return { id: childId, status: "created" }; } }` }, { title: "Coordinate across children", description: "The supervisor can call methods on any child via Durable Object RPC. Fan out to all children with Promise.all() for parallel operations.", code: ` @callable() async incrementAll() { const results = await Promise.all( this.state.childIds.map(async (id) => { const child = await getAgentByName(this.env.ChildAgent, id); return child.increment(); }) ); return { updated: results.length }; }` } ]; export function SupervisorDemo() { const userId = useUserId(); const { logs, addLog, clearLogs } = useLogs(); const [children, setChildren] = useState([]); const [stats, setStats] = useState({ totalChildren: 0, totalCounter: 0 }); const agent = useAgent({ agent: "supervisor-agent", name: `demo-supervisor-${userId}`, onOpen: () => { addLog("info", "connected"); refreshStats(); }, onClose: () => addLog("info", "disconnected"), onError: () => addLog("error", "error", "Connection error") }); const refreshStats = useCallback(async () => { try { const result = await agent.call("getStats"); setChildren(result.children); setStats({ totalChildren: result.totalChildren, totalCounter: result.totalCounter }); addLog("in", "stats", result); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }, [agent, addLog]); const handleCreateChild = async () => { const childId = `child-${nanoid(6)}`; addLog("out", "call", `createChild("${childId}")`); try { const result = await agent.call("createChild", [childId]); addLog("in", "result", result); await refreshStats(); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const handleIncrementChild = async (childId: string) => { addLog("out", "call", `incrementChild("${childId}")`); try { const result = await agent.call("incrementChild", [childId]); addLog("in", "result", result); await refreshStats(); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const handleIncrementAll = async () => { addLog("out", "call", "incrementAll()"); try { const result = await agent.call("incrementAll"); addLog("in", "result", result); await refreshStats(); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const handleRemoveChild = async (childId: string) => { addLog("out", "call", `removeChild("${childId}")`); try { await agent.call("removeChild", [childId]); await refreshStats(); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const handleClearAll = async () => { addLog("out", "call", "clearChildren()"); try { await agent.call("clearChildren"); setChildren([]); setStats({ totalChildren: 0, totalCounter: 0 }); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; useEffect(() => { if (agent.readyState === WebSocket.OPEN) { refreshStats(); } }, [agent.readyState, refreshStats]); return ( A supervisor agent creates and manages child agents using{" "} getAgentByName() . Each child is a separate Durable Object with its own state and lifecycle. The supervisor coordinates them via Durable Object RPC — calling methods, aggregating results, and tracking their state. Create a few children below and increment them individually or all at once. } statusIndicator={ } >
{/* Controls */}
{/* Connection & Stats */} {/* Stats Bar */}
{stats.totalChildren}
Children
{stats.totalCounter}
Total Counter
{/* Actions */}
{/* Children Grid */}
Child Agents ({children.length}) {children.length > 0 && ( )}
{children.length > 0 ? (
{children.map((child) => (
{child.id}
{child.state.counter}
{child.state.createdAt && (
{new Date(child.state.createdAt).toLocaleTimeString()}
)}
))}
) : ( )}
{/* Logs */}
); }