💡 Prerequisites: This chapter assumes you've completed Chapter 8: Building Agentic UIs with Cedar. You should understand Cedar basics,
useRegisterState
, and the fundamentals of agentic UI design before tackling these advanced patterns.
Diff & History Management
When agents modify state, users need visibility and control. Cedar tracks changes automatically.
The Problem
Agent: "I've updated your tasks"
User: What exactly changed?
User: Can I undo this?
Without diff tracking, users are blind to agent changes.
User Approval Flows
import { useRegisterState } from 'cedar-os'; export function ApprovalFlow() { const [pendingChanges, setPendingChanges] = useState([]); const [documents, setDocuments] = useState([...]); useRegisterState({ key: 'documents', value: documents, setValue: setDocuments, requireApproval: true, approvalThreshold: 0.7, // Confidence threshold stateSetters: { updateDocument: { name: 'updateDocument', execute: async (currentDocs, args, confidence) => { if (confidence < 0.7) { // Queue for approval setPendingChanges(prev => [...prev, { id: crypto.randomUUID(), action: 'updateDocument', data: args, confidence, }]); return { status: 'pending_approval' }; } // High confidence - execute immediately setDocuments(docs => docs.map(doc => doc.id === args.id ? { ...doc, ...args } : doc )); return { status: 'executed' }; }, }, }, }); return ( <div> {pendingChanges.map(change => ( <div key={change.id} className="border p-4 rounded"> <p>{change.action}</p> <p>Confidence: {(change.confidence * 100).toFixed(0)}%</p> <button onClick={() => approveChange(change)}>Approve</button> <button onClick={() => rejectChange(change.id)}>Reject</button> </div> ))} </div> ); }
History Tracking
import { useCedarHistory } from 'cedar-os'; export function HistoryPanel({ stateKey }: { stateKey: string }) { const { entries, undo, redo, canUndo, canRedo } = useCedarHistory(stateKey); return ( <div> <button onClick={undo} disabled={!canUndo}>↶ Undo</button> <button onClick={redo} disabled={!canRedo}>↷ Redo</button> {entries.map(entry => ( <div key={entry.id}> <span>{entry.action}</span> <span>{entry.timestamp.toLocaleString()}</span> <span>{entry.user}</span> {/* 'human' or 'agent' */} </div> ))} </div> ); }
Human-in-the-Loop UI
Confidence Indicator
export function ConfidenceIndicator({ confidence }: { confidence: number }) { const getColor = () => { if (confidence >= 0.7) return 'green'; if (confidence >= 0.5) return 'yellow'; return 'red'; }; return ( <div className={`border-l-4 border-${getColor()}-500 p-3`}> <div className="flex justify-between"> <span>AI Confidence</span> <span className="font-bold">{(confidence * 100).toFixed(0)}%</span> </div> <div className="w-full bg-gray-200 rounded-full h-2 mt-2"> <div className={`bg-${getColor()}-500 h-2 rounded-full`} style={{ width: `${confidence * 100}%` }} /> </div> </div> ); }
Agent Activity Log
export function AgentActivityLog() { const [activities, setActivities] = useState([]); useEffect(() => { const unsubscribe = useCedarStore.subscribe( (state) => state.stateHistory, (history) => { setActivities(history.filter(h => h.source === 'agent')); } ); return unsubscribe; }, []); return ( <div> <h3>Agent Activity</h3> {activities.map(activity => ( <div key={activity.id}> <p>{activity.action}</p> <p>{activity.timestamp.toLocaleString()}</p> {activity.requiresReview && <span>Needs review</span>} </div> ))} </div> ); }
Voice Integration
Cedar has built-in voice support for hands-free interactions.
Quick Start
import { CedarCopilot } from 'cedar-os'; export default function RootLayout({ children }) { return ( <CedarCopilot llmProvider={{ provider: 'openai', apiKey: '...' }} voiceSettings={{ language: 'en-US', voiceId: 'alloy', // alloy, echo, fable, onyx, nova, shimmer useBrowserTTS: false, autoAddToMessages: true, }} > {children} </CedarCopilot> ); }
Voice Controls
import { useCedarStore, VoiceIndicator } from 'cedar-os'; export function VoiceChat() { const voice = useCedarStore((state) => state.voice); const handleEnableVoice = async () => { if (!voice.checkVoiceSupport()) { alert('Voice not supported'); return; } await voice.requestVoicePermission(); }; return ( <div> <VoiceIndicator voiceState={voice} /> <button onClick={() => voice.toggleVoice()} disabled={voice.voicePermissionStatus !== 'granted'} > {voice.isListening ? 'Stop' : 'Start'} Listening </button> {voice.currentTranscript && ( <p>You said: {voice.currentTranscript}</p> )} </div> ); }
Mastra Voice Backend
<CedarCopilot llmProvider={{ provider: 'mastra', baseURL: '/api', voiceRoute: '/chat/voice-execute', }} /> // Backend handles transcription + TTS
Spells: Complex Workflows
Spells activate AI through gestures and shortcuts.
RadialMenuSpell
import { RadialMenuSpell, Hotkey } from 'cedar-os'; export function DocumentEditor() { return ( <RadialMenuSpell activationKey={Hotkey.RIGHT_CLICK} items={[ { icon: '✍️', label: 'Rewrite', action: () => {} }, { icon: '🎨', label: 'Improve Style', action: () => {} }, { icon: '📝', label: 'Summarize', action: () => {} }, ]} /> ); }
TooltipMenuSpell
import { TooltipMenuSpell } from 'cedar-os'; export function TextEditor() { return ( <TooltipMenuSpell actions={[ { label: 'Ask AI', icon: '🤖', onSelect: (text) => openAIPrompt(text), }, ]} /> ); }
Custom Spell
import { useSpell, Hotkey, ActivationMode } from 'cedar-os'; export function AICommandSpell() { const { isActive, activate, deactivate } = useSpell({ id: 'ai-command', activationConditions: { events: [Hotkey.CMD_K], mode: ActivationMode.TOGGLE, }, }); if (!isActive) return null; return ( <div className="fixed inset-0 flex items-center justify-center"> <input placeholder="Ask AI anything..." onKeyDown={(e) => { if (e.key === 'Enter') handleSubmit(); if (e.key === 'Escape') deactivate(); }} autoFocus /> </div> ); }
Multi-Agent UI Coordination
Agent Selector
type Agent = { id: string; name: string; avatar: string; specialization: string[]; }; export function MultiAgentChat() { const [activeAgent, setActiveAgent] = useState('general'); const agents: Agent[] = [ { id: 'general', name: 'General', avatar: '🤖', specialization: [] }, { id: 'code', name: 'Code Expert', avatar: '💻', specialization: ['coding'] }, { id: 'data', name: 'Data Analyst', avatar: '📊', specialization: ['data'] }, ]; return ( <div className="flex"> <div className="w-64 border-r p-4"> {agents.map(agent => ( <button key={agent.id} onClick={() => setActiveAgent(agent.id)} className={activeAgent === agent.id ? 'bg-blue-600 text-white' : ''} > {agent.avatar} {agent.name} </button> ))} </div> <div className="flex-1"> <FloatingCedarChat agentContext={{ systemPrompt: getPromptForAgent(activeAgent), }} /> </div> </div> ); }
Agent Handoffs
export function AgentHandoff() { const handleHandoff = async (fromAgent: string, toAgent: string, context: any) => { // Transfer conversation context await transferContext(fromAgent, toAgent, context); // Notify user addMessage({ role: 'system', content: `Transferred to ${toAgent} agent`, }); }; return ( <div> {/* Current agent can suggest handoff */} <button onClick={() => handleHandoff('general', 'code', conversationContext)}> Switch to Code Expert </button> </div> ); }
Production Deployment
Deployment Checklist
# 1. Environment variables NEXT_PUBLIC_OPENAI_API_KEY=sk-... MASTRA_URL=https://api.yourapp.com # 2. Build optimization npm run build # 3. Deploy to Vercel vercel --prod # 4. Configure caching # Use Edge caching for static Cedar components # 5. Monitor performance # Set up logging for Cedar state changes
Error Handling
import { CedarCopilot } from 'cedar-os'; export default function RootLayout({ children }) { return ( <CedarCopilot errorHandler={(error) => { console.error('Cedar error:', error); // Send to monitoring service captureException(error); }} fallback={<div>AI features unavailable</div>} > {children} </CedarCopilot> ); }
Performance Optimization
// Lazy load Cedar for better initial load const FloatingCedarChat = dynamic( () => import('cedar-os').then(mod => mod.FloatingCedarChat), { ssr: false } ); // State persistence useRegisterState({ key: 'todos', value: todos, persistence: { enabled: true, storage: 'localStorage', key: 'app-todos', }, });
Complete Example: Document Editor Copilot
Production-ready document editor with AI assistance.
// app/editor/page.tsx 'use client'; import { useState } from 'react'; import { useRegisterState } from 'cedar-os'; import { FloatingCedarChat, RadialMenuSpell } from 'cedar-os'; export default function DocumentEditor() { const [document, setDocument] = useState({ id: '1', title: 'Untitled', content: '', history: [], }); useRegisterState({ key: 'document', value: document, setValue: setDocument, enableHistory: true, stateSetters: { updateContent: { name: 'updateContent', execute: (current, args) => { setDocument(prev => ({ ...prev, content: args.content, history: [...prev.history, { timestamp: new Date(), content: prev.content, }], })); }, }, rewriteSection: { name: 'rewriteSection', execute: async (current, args) => { const rewritten = await rewriteText(args.text, args.style); return { rewritten }; }, }, }, }); return ( <div className="h-screen flex flex-col"> <header className="border-b p-4"> <input type="text" value={document.title} onChange={(e) => setDocument(prev => ({ ...prev, title: e.target.value }))} className="text-2xl font-bold" /> </header> <div className="flex-1 p-8"> <textarea value={document.content} onChange={(e) => setDocument(prev => ({ ...prev, content: e.target.value }))} className="w-full h-full resize-none" /> <RadialMenuSpell activationKey="right-click" items={[ { icon: '✍️', label: 'Rewrite', action: () => {} }, { icon: '📝', label: 'Summarize', action: () => {} }, ]} /> </div> <FloatingCedarChat title="Writing Assistant" agentContext={{ systemPrompt: `You are a writing assistant. Help with: - Rewriting and improving content - Grammar and style - Document structure`, }} /> </div> ); }
Key Takeaways
- Diff & History - Track all agent changes, enable undo/redo
- Human-in-the-Loop - Confidence thresholds, approval flows
- Voice - Built-in, works with Mastra or OpenAI
- Spells - Contextual AI activation via gestures
- Multi-Agent - Coordinate multiple specialized agents
- Production - Error handling, monitoring, optimization
Cedar provides everything needed for production agentic UIs.
Get chapter updates & code samples
We’ll email diagrams, code snippets, and additions.