import { cn } from '@/lib/utils' import './agent-face.css' /** * The live state of an agent, mapped from its latest AgentRun (+ governance hold) onto an expression. * `idle` = nothing in flight; `thinking` = queued; `working` = running; `review` = output held in the * inbox; `done` = just completed & executed; `failed` = the run errored. */ export type FaceState = 'idle' | 'thinking' | 'working' | 'review' | 'done' | 'failed' export type FaceSize = 'sm' | 'md' | 'lg' | 'xl' interface AgentFaceProps { name?: string | null /** Used only to seed the per-agent hue and the accessible label — never drawn on the face. */ monogram?: string | null state?: FaceState size?: FaceSize className?: string } const STATE_LABEL: Record = { idle: 'idle', thinking: 'queued', working: 'working', review: 'awaiting review', done: 'done', failed: 'failed', } /** * Deterministic hue in the indigo–violet band [225, 265] so every agent is distinct yet stays inside * the AI = indigo identity. Seeded by the agent's monogram/name so it is stable across renders and * needs no stored field. */ function hueFor(seed: string): number { let h = 0 for (let i = 0; i < seed.length; i += 1) h = (h * 31 + seed.charCodeAt(i)) >>> 0 return 225 + (h % 41) } /** The expressive Companion face. One component, every surface — sized by `size`, animated by `state`. */ export function AgentFace({ name, monogram, state = 'idle', size = 'md', className }: AgentFaceProps) { const hue = hueFor((monogram || name || 'agent').trim().toLowerCase()) const label = `${name ?? 'AI agent'} — ${STATE_LABEL[state]}` return ( ) }