Blueprint/Chapter 20
Chapter 20

Advanced Agentic UI Patterns with Cedar

By Zavier SandersSeptember 21, 2025

Master advanced Cedar features: Diff & History, Voice Integration, Spells, Multi-Agent UIs, and production deployment.

Prefer something you can ship today? Start with theQuickstart: Ship One Agent with Mastra— then come back here to deepen the concepts.

💡 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

  1. Diff & History - Track all agent changes, enable undo/redo
  2. Human-in-the-Loop - Confidence thresholds, approval flows
  3. Voice - Built-in, works with Mastra or OpenAI
  4. Spells - Contextual AI activation via gestures
  5. Multi-Agent - Coordinate multiple specialized agents
  6. 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.