UIPackage
Menu

Framework

Change language

Boilerplate repo

Agent Memory Inspector

blockai

Autonomous AI agent memory inspector featuring short-term context buffer utilization, long-term episodic vector memories with confidence scoring, and Neo4j-style entity relationship knowledge graph.

Also available for Vue ->

Installation

$npx shadcn@latest add https://uipkge.dev/r/react/agent-memory-inspector.json
Named registry:npx shadcn@latest add @uipkge-react/agent-memory-inspectorInstalls to:components/blocks/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
classNamestringoptional
initialMemoriesEpisodicMemory[]optional

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

EpisodicMemory
interface EpisodicMemory {
  id: string
  content: string
  category: MemoryCategory
  confidence: number
  lastRecalled: string
  recallsCount: number
  sourceTurn: string
  tags: string[]
  isPinned: boolean
}
MessageTurn
interface MessageTurn {
  id: string
  turnNumber: number
  role: 'user' | 'assistant'
  authorName: string
  avatarText: string
  timestamp: string
  importanceScore: number
  importanceLevel: 'Critical' | 'High' | 'Standard' | 'Action Item'
  tokens: number
  content: string
  extractedFacts: string[]
  embeddingHash: string
}
EntityNode
interface EntityNode {
  id: string
  label: string
  entityType: 'Person' | 'Repository' | 'CSS Engine' | 'Design System' | 'Framework' | 'Standard'
  description: string
  degree: number
  status: 'active' | 'synced' | 'cached'
  embeddingId: string
}
GraphTriplet
interface GraphTriplet {
  id: string
  subject: string
  subjectType: string
  predicate: 'MAINTAINS' | 'USES' | 'CONFIGURES' | 'IMPLEMENTS' | 'MIRRORS' | 'ENFORCES'
  object: string
  objectType: string
  confidence: number
  weight: string
  lastTraversed: string
}

Files installed (5)

  • components/blocks/AgentMemoryInspector.tsx26.6 kB
    'use client'
    
    import * as React from 'react'
    import {
      Activity,
      AlertCircle,
      Brain,
      Check,
      Clock,
      Cpu,
      Database,
      Network,
      Pencil,
      Plus,
      RefreshCw,
      Search,
      ShieldCheck,
      Star,
      Trash2,
      Workflow,
      X,
    } from 'lucide-react'
    import { cn } from '@/lib/utils'
    import { Avatar, AvatarFallback } from '@/components/ui/avatar'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
    import { Progress } from '@/components/ui/progress'
    import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
    import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
    import { AgentContextBuffer } from './AgentContextBuffer'
    import { AgentEntityGraph } from './AgentEntityGraph'
    import {
      INITIAL_MEMORY_FACTS,
      INITIAL_MESSAGE_TURNS,
      INITIAL_ENTITY_NODES,
      INITIAL_GRAPH_TRIPLETS,
    } from './agent-memory-data'
    import type { EpisodicMemory, MessageTurn, EntityNode, GraphTriplet, MemoryCategory } from './agent-memory-types'
    export type { EpisodicMemory, MessageTurn, EntityNode, GraphTriplet, MemoryCategory } from './agent-memory-types'
    
    export interface AgentMemoryInspectorProps {
      className?: string
      initialMemories?: EpisodicMemory[]
    }
    
    export function AgentMemoryInspector({ className, initialMemories = INITIAL_MEMORY_FACTS }: AgentMemoryInspectorProps) {
      const [memoryFacts, setMemoryFacts] = React.useState<EpisodicMemory[]>(initialMemories)
      const [messageTurns, setMessageTurns] = React.useState<MessageTurn[]>(INITIAL_MESSAGE_TURNS)
      const [entityNodes, setEntityNodes] = React.useState<EntityNode[]>(INITIAL_ENTITY_NODES)
      const [graphTriplets, setGraphTriplets] = React.useState<GraphTriplet[]>(INITIAL_GRAPH_TRIPLETS)
      const [activeTab, setActiveTab] = React.useState('context')
      const [selectedCategory, setSelectedCategory] = React.useState<string>('all')
      const [searchQuery, setSearchQuery] = React.useState('')
      const [selectedTurnId, setSelectedTurnId] = React.useState<string>('turn-4')
      const [selectedNodeId, setSelectedNodeId] = React.useState<string>('node-2')
      const [isWiped, setIsWiped] = React.useState(false)
      const [wipeToastVisible, setWipeToastVisible] = React.useState(false)
      const [isAddingFact, setIsAddingFact] = React.useState(false)
      const [editingFactId, setEditingFactId] = React.useState<string | null>(null)
    
      const [newFactForm, setNewFactForm] = React.useState<{
        content: string
        category: MemoryCategory
        confidence: number
      }>({
        content: '',
        category: 'User Preference',
        confidence: 0.95,
      })
    
      const filteredMemories = React.useMemo(() => {
        return memoryFacts.filter((fact) => {
          const matchesCategory = selectedCategory === 'all' || fact.category === selectedCategory
          const matchesSearch =
            searchQuery.trim() === '' ||
            fact.content.toLowerCase().includes(searchQuery.toLowerCase()) ||
            fact.tags.some((t) => t.toLowerCase().includes(searchQuery.toLowerCase()))
          return matchesCategory && matchesSearch
        })
      }, [memoryFacts, selectedCategory, searchQuery])
    
      const selectedNode = React.useMemo(() => {
        return entityNodes.find((n) => n.id === selectedNodeId) || entityNodes[1]
      }, [entityNodes, selectedNodeId])
    
      const filteredTriplets = React.useMemo(() => {
        if (!selectedNode) return graphTriplets
        return graphTriplets.filter((t) => t.subject === selectedNode.label || t.object === selectedNode.label)
      }, [graphTriplets, selectedNode])
    
      const wipeWorkingMemory = () => {
        setIsWiped(true)
        setWipeToastVisible(true)
        setTimeout(() => {
          setWipeToastVisible(false)
        }, 4000)
      }
    
      const restoreWorkingMemory = () => {
        setIsWiped(false)
        setWipeToastVisible(false)
      }
    
      const deleteFact = (id: string) => {
        setMemoryFacts((prev) => prev.filter((f) => f.id !== id))
      }
    
      const togglePinFact = (id: string) => {
        setMemoryFacts((prev) => prev.map((f) => (f.id === id ? { ...f, isPinned: !f.isPinned } : f)))
      }
    
      const startAddFact = () => {
        setIsAddingFact(true)
        setEditingFactId(null)
        setNewFactForm({
          content: '',
          category: 'User Preference',
          confidence: 0.95,
        })
      }
    
      const editFact = (fact: EpisodicMemory) => {
        setEditingFactId(fact.id)
        setNewFactForm({
          content: fact.content,
          category: fact.category,
          confidence: fact.confidence,
        })
        setIsAddingFact(true)
      }
    
      const saveNewFact = () => {
        if (!newFactForm.content.trim()) return
    
        if (editingFactId) {
          setMemoryFacts((prev) =>
            prev.map((f) =>
              f.id === editingFactId
                ? {
                    ...f,
                    content: newFactForm.content,
                    category: newFactForm.category,
                    confidence: Number(newFactForm.confidence),
                    lastRecalled: 'Just now',
                  }
                : f,
            ),
          )
        } else {
          const newId = `fact-${Date.now()}`
          setMemoryFacts((prev) => [
            {
              id: newId,
              content: newFactForm.content,
              category: newFactForm.category,
              confidence: Number(newFactForm.confidence),
              lastRecalled: 'Just now',
              recallsCount: 1,
              sourceTurn: 'Manual Injection',
              tags: [newFactForm.category.toLowerCase().replace(/\s+/g, '-')],
              isPinned: false,
            },
            ...prev,
          ])
        }
    
        setIsAddingFact(false)
        setEditingFactId(null)
      }
    
      const cancelAddFact = () => {
        setIsAddingFact(false)
        setEditingFactId(null)
      }
    
      const getCategoryVariant = (category: MemoryCategory): 'default' | 'secondary' | 'outline' => {
        switch (category) {
          case 'User Preference':
            return 'default'
          case 'Architecture Rule':
            return 'secondary'
          case 'System Constraint':
            return 'outline'
        }
      }
    
      return (
        <div className={cn('w-full space-y-6 font-sans antialiased', className)}>
          {/* Header Block */}
          <Card className="border-border shadow-xs">
            <CardHeader className="p-6 pb-4">
              <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
                {/* Left: Agent Identity & Engine Details */}
                <div className="flex items-start gap-4">
                  <div className="relative">
                    <Avatar className="border-border bg-muted/60 h-12 w-12 rounded-xl border p-1">
                      <AvatarFallback className="bg-primary/10 text-primary rounded-lg font-mono text-sm font-bold">
                        <Brain className="text-primary h-6 w-6" />
                      </AvatarFallback>
                    </Avatar>
                    <span className="bg-background absolute -right-0.5 -bottom-0.5 flex h-3.5 w-3.5 items-center justify-center rounded-full">
                      <span className="bg-success ring-success/20 h-2 w-2 rounded-full ring-2" />
                    </span>
                  </div>
    
                  <div className="space-y-1">
                    <div className="flex flex-wrap items-center gap-2">
                      <h2 className="text-foreground text-lg font-semibold tracking-tight">
                        Senior Engineering Co-Pilot Agent
                      </h2>
                      <Badge variant="outline" className="gap-1 px-2 py-0.5 text-xs font-medium">
                        <Cpu className="text-warning h-3 w-3" />
                        Autonomous Sync
                      </Badge>
                    </div>
    
                    <div className="text-muted-foreground flex flex-wrap items-center gap-x-3 gap-y-1 text-xs">
                      <span className="text-foreground flex items-center gap-1.5 font-medium">
                        <Database className="text-primary h-3.5 w-3.5" />
                        Mem0 Vector + Neo4j Entity Graph
                      </span>
                      <span className="flex items-center gap-1">
                        <Network className="h-3.5 w-3.5" />
                        142 Episodic Memories · 28 Entity Nodes
                      </span>
                      <span className="text-muted-foreground flex items-center gap-1 font-mono">
                        <Activity className="text-success h-3 w-3" />
                        Recall: 12ms
                      </span>
                    </div>
                  </div>
                </div>
    
                {/* Right: Actions */}
                <div className="flex flex-wrap items-center gap-2">
                  <Button
                    variant="ghost"
                    size="sm"
                    className="text-muted-foreground hover:text-destructive h-9 gap-1.5 text-xs font-medium"
                    onClick={wipeWorkingMemory}
                  >
                    <Trash2 className="h-3.5 w-3.5" />
                    Wipe Working Memory
                  </Button>
    
                  <Button size="sm" className="h-9 gap-1.5 text-xs font-medium shadow-xs" onClick={startAddFact}>
                    <Plus className="h-3.5 w-3.5" />
                    Add Memory Fact
                  </Button>
                </div>
              </div>
            </CardHeader>
    
            {/* Wipe State Feedback Banner */}
            {wipeToastVisible && (
              <div className="border-destructive/20 bg-destructive/5 border-t px-6 py-2.5">
                <div className="text-destructive flex items-center justify-between gap-2 text-xs">
                  <div className="flex items-center gap-2">
                    <AlertCircle className="h-4 w-4 shrink-0" />
                    <span>Working context buffer cache cleared. Persistent episodic facts remain intact.</span>
                  </div>
                  <Button
                    variant="outline"
                    size="sm"
                    className="border-destructive/30 text-destructive hover:bg-destructive/10 h-7 text-xs"
                    onClick={restoreWorkingMemory}
                  >
                    <RefreshCw className="mr-1 h-3 w-3" />
                    Restore
                  </Button>
                </div>
              </div>
            )}
          </Card>
    
          {/* Inline Add/Edit Memory Fact Modal / Drawer */}
          {isAddingFact && (
            <Card className="border-primary/30 bg-card ring-primary/20 shadow-sm ring-1">
              <CardHeader className="p-4 pb-2">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-2">
                    <Brain className="text-primary h-4 w-4" />
                    <CardTitle className="text-sm font-semibold">
                      {editingFactId ? 'Edit Memory Fact' : 'Add Episodic Memory Fact'}
                    </CardTitle>
                  </div>
                  <Button
                    variant="ghost"
                    size="sm"
                    className="h-7 w-7 p-0"
                    aria-label="Cancel adding fact"
                    onClick={cancelAddFact}
                  >
                    <X className="h-4 w-4" />
                  </Button>
                </div>
                <CardDescription className="text-xs">
                  Inject a verified truth into the persistent Mem0 vector embedding space.
                </CardDescription>
              </CardHeader>
    
              <CardContent className="space-y-3 p-4 pt-2">
                <div className="space-y-1.5">
                  <label className="text-foreground text-xs font-medium">Fact Content</label>
                  <textarea
                    value={newFactForm.content}
                    onChange={(e) => setNewFactForm({ ...newFactForm, content: e.target.value })}
                    placeholder="e.g. User enforces strict 120-character printWidth in Prettier..."
                    rows={2}
                    className="border-input bg-background text-foreground placeholder:text-muted-foreground focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-xs focus-visible:ring-1 focus-visible:outline-none"
                  />
                </div>
    
                <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                  <div className="space-y-1.5">
                    <label className="text-foreground text-xs font-medium">Memory Category</label>
                    <select
                      value={newFactForm.category}
                      onChange={(e) => setNewFactForm({ ...newFactForm, category: e.target.value as MemoryCategory })}
                      className="border-input bg-background text-foreground focus-visible:ring-ring w-full rounded-md border px-2.5 py-1.5 text-xs focus-visible:ring-1 focus-visible:outline-none"
                    >
                      <option value="User Preference">User Preference</option>
                      <option value="Architecture Rule">Architecture Rule</option>
                      <option value="System Constraint">System Constraint</option>
                    </select>
                  </div>
    
                  <div className="space-y-1.5">
                    <label className="text-foreground text-xs font-medium">Confidence Score (0.0 - 1.0)</label>
                    <input
                      type="number"
                      step="0.01"
                      min="0"
                      max="1"
                      value={newFactForm.confidence}
                      onChange={(e) => setNewFactForm({ ...newFactForm, confidence: parseFloat(e.target.value) || 0 })}
                      className="border-input bg-background text-foreground focus-visible:ring-ring w-full rounded-md border px-3 py-1.5 font-mono text-xs focus-visible:ring-1 focus-visible:outline-none"
                    />
                  </div>
                </div>
              </CardContent>
    
              <CardFooter className="border-border flex justify-end gap-2 border-t p-3">
                <Button
                  aria-label="Cancel adding fact"
                  variant="ghost"
                  size="sm"
                  className="h-8 text-xs"
                  onClick={cancelAddFact}
                >
                  Cancel
                </Button>
                <Button size="sm" className="h-8 text-xs font-medium" onClick={saveNewFact}>
                  <Check className="mr-1 h-3.5 w-3.5" />
                  {editingFactId ? 'Save Changes' : 'Store Fact'}
                </Button>
              </CardFooter>
            </Card>
          )}
    
          {/* Main Tabs Section */}
          <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full space-y-4">
            <TabsList className="border-border bg-muted/40 grid h-10 w-full grid-cols-3 rounded-lg border p-1">
              <TabsTrigger value="context" className="gap-2 text-xs font-medium">
                <Cpu className="h-3.5 w-3.5" />
                <span>Short-Term Context Buffer</span>
                <Badge variant="secondary" className="ml-1 px-1.5 py-0 font-mono text-xs font-normal">
                  {isWiped ? '0 turns' : '4 turns'}
                </Badge>
              </TabsTrigger>
    
              <TabsTrigger value="episodic" className="gap-2 text-xs font-medium">
                <Database className="h-3.5 w-3.5" />
                <span>Long-Term Episodic Memory</span>
                <Badge variant="secondary" className="ml-1 px-1.5 py-0 font-mono text-xs font-normal">
                  {memoryFacts.length} Facts
                </Badge>
              </TabsTrigger>
    
              <TabsTrigger value="graph" className="gap-2 text-xs font-medium">
                <Network className="h-3.5 w-3.5" />
                <span>Entity Knowledge Graph</span>
                <Badge variant="secondary" className="ml-1 px-1.5 py-0 font-mono text-xs font-normal">
                  {entityNodes.length} Nodes
                </Badge>
              </TabsTrigger>
            </TabsList>
    
            {/* ========================================== */}
            {/* TAB 1: SHORT-TERM CONTEXT BUFFER          */}
            {/* ========================================== */}
            {/* TAB 1: SHORT-TERM CONTEXT BUFFER */}
            <TabsContent value="context" className="space-y-4 outline-none">
              <AgentContextBuffer
                isWiped={isWiped}
                turns={messageTurns}
                selectedTurnId={selectedTurnId}
                onSelectTurn={setSelectedTurnId}
                onRestore={restoreWorkingMemory}
              />
            </TabsContent>
    
            {/* ========================================== */}
            {/* TAB 2: LONG-TERM EPISODIC MEMORY TABLE    */}
            {/* ========================================== */}
            <TabsContent value="episodic" className="space-y-4 outline-none">
              <Card className="border-border shadow-xs">
                {/* Filter and Search Header */}
                <CardHeader className="p-5 pb-3">
                  <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                    <div>
                      <CardTitle className="text-sm font-semibold">Persistent Episodic Facts</CardTitle>
                      <CardDescription className="text-xs">
                        Distilled knowledge facts retrieved via Mem0 vector cosine similarity search.
                      </CardDescription>
                    </div>
    
                    {/* Search & Category Filters */}
                    <div className="flex flex-wrap items-center gap-2">
                      <div className="relative w-full sm:w-64">
                        <Search className="text-muted-foreground absolute top-2.5 left-2.5 h-3.5 w-3.5" />
                        <input
                          type="text"
                          value={searchQuery}
                          onChange={(e) => setSearchQuery(e.target.value)}
                          placeholder="Search memory facts or tags..."
                          className="border-input bg-background text-foreground placeholder:text-muted-foreground focus-visible:ring-ring h-8.5 w-full rounded-md border pr-3 pl-8 text-xs focus-visible:ring-1 focus-visible:outline-none"
                        />
                      </div>
    
                      <div className="border-border bg-muted/40 flex items-center gap-1 rounded-lg border p-0.5">
                        <button
                          type="button"
                          className={cn(
                            'rounded px-2.5 py-1 text-xs font-medium transition-colors',
                            selectedCategory === 'all'
                              ? 'bg-background text-foreground shadow-xs'
                              : 'text-muted-foreground hover:text-foreground',
                          )}
                          onClick={() => setSelectedCategory('all')}
                        >
                          All ({memoryFacts.length})
                        </button>
                        <button
                          type="button"
                          className={cn(
                            'rounded px-2.5 py-1 text-xs font-medium transition-colors',
                            selectedCategory === 'User Preference'
                              ? 'bg-background text-foreground shadow-xs'
                              : 'text-muted-foreground hover:text-foreground',
                          )}
                          onClick={() => setSelectedCategory('User Preference')}
                        >
                          Preference
                        </button>
                        <button
                          type="button"
                          className={cn(
                            'rounded px-2.5 py-1 text-xs font-medium transition-colors',
                            selectedCategory === 'Architecture Rule'
                              ? 'bg-background text-foreground shadow-xs'
                              : 'text-muted-foreground hover:text-foreground',
                          )}
                          onClick={() => setSelectedCategory('Architecture Rule')}
                        >
                          Architecture
                        </button>
                        <button
                          type="button"
                          className={cn(
                            'rounded px-2.5 py-1 text-xs font-medium transition-colors',
                            selectedCategory === 'System Constraint'
                              ? 'bg-background text-foreground shadow-xs'
                              : 'text-muted-foreground hover:text-foreground',
                          )}
                          onClick={() => setSelectedCategory('System Constraint')}
                        >
                          Constraint
                        </button>
                      </div>
                    </div>
                  </div>
                </CardHeader>
    
                {/* Memory Table */}
                <CardContent className="p-0">
                  <div className="overflow-x-auto">
                    <Table>
                      <TableHeader>
                        <TableRow className="hover:bg-transparent">
                          <TableHead className="w-8"></TableHead>
                          <TableHead className="min-w-[280px]">Memory Fact Content</TableHead>
                          <TableHead className="w-[160px]">Category</TableHead>
                          <TableHead className="w-[130px]">Confidence</TableHead>
                          <TableHead className="w-[140px]">Last Recalled</TableHead>
                          <TableHead className="w-[100px] text-right">Actions</TableHead>
                        </TableRow>
                      </TableHeader>
    
                      <TableBody>
                        {filteredMemories.length === 0 ? (
                          <TableRow>
                            <TableCell colSpan={6} className="text-muted-foreground h-28 text-center text-xs">
                              No matching memory facts found for the current query.
                            </TableCell>
                          </TableRow>
                        ) : (
                          filteredMemories.map((fact) => (
                            <TableRow key={fact.id} className="group hover:bg-muted/40">
                              {/* Pin / Star Icon */}
                              <TableCell className="py-3 pr-1 pl-4">
                                <button
                                  type="button"
                                  className="text-muted-foreground hover:text-warning focus-visible:outline-none"
                                  title={fact.isPinned ? 'Unpin fact' : 'Pin fact'}
                                  onClick={() => togglePinFact(fact.id)}
                                >
                                  <Star
                                    className={cn(
                                      'h-4 w-4',
                                      fact.isPinned ? 'fill-warning text-warning' : 'text-muted-foreground/40',
                                    )}
                                  />
                                </button>
                              </TableCell>
    
                              {/* Content & Source Details */}
                              <TableCell className="py-3">
                                <div className="space-y-1">
                                  <div className="text-foreground text-xs leading-relaxed font-medium">{fact.content}</div>
                                  <div className="text-muted-foreground flex flex-wrap items-center gap-1.5 text-xs">
                                    <span className="text-muted-foreground font-mono">{fact.sourceTurn}</span>
                                    <span>·</span>
                                    <div className="flex items-center gap-1">
                                      {fact.tags.map((tag) => (
                                        <span
                                          key={tag}
                                          className="bg-muted py-0.2 text-muted-foreground rounded px-1.5 font-mono text-xs"
                                        >
                                          #{tag}
                                        </span>
                                      ))}
                                    </div>
                                  </div>
                                </div>
                              </TableCell>
    
                              {/* Category Badge */}
                              <TableCell className="py-3">
                                <Badge variant={getCategoryVariant(fact.category)} className="text-xs font-normal">
                                  {fact.category}
                                </Badge>
                              </TableCell>
    
                              {/* Confidence Meter */}
                              <TableCell className="py-3">
                                <div className="space-y-1">
                                  <div className="flex items-center justify-between font-mono text-xs">
                                    <span className="text-foreground font-semibold">
                                      {(fact.confidence * 100).toFixed(0)}%
                                    </span>
                                    <span className="text-muted-foreground">({fact.confidence})</span>
                                  </div>
                                  <Progress value={fact.confidence * 100} className="bg-muted h-1.5 w-20" />
                                </div>
                              </TableCell>
    
                              {/* Last Recalled & Recalls Count */}
                              <TableCell className="py-3">
                                <div className="space-y-0.5 text-xs">
                                  <div className="text-foreground flex items-center gap-1 font-medium">
                                    <Clock className="text-muted-foreground h-3 w-3" />
                                    <span>{fact.lastRecalled}</span>
                                  </div>
                                  <div className="text-muted-foreground font-mono text-xs">
                                    Recalled {fact.recallsCount}×
                                  </div>
                                </div>
                              </TableCell>
    
                              {/* Actions */}
                              <TableCell className="py-3 pr-4 text-right">
                                <div className="flex items-center justify-end gap-1">
                                  <Button
                                    variant="ghost"
                                    size="sm"
                                    className="text-muted-foreground hover:text-foreground h-7 w-7 p-0"
                                    title="Edit fact"
                                    onClick={() => editFact(fact)}
                                  >
                                    <Pencil className="h-3.5 w-3.5" />
                                  </Button>
    
                                  <Button
                                    variant="ghost"
                                    size="sm"
                                    className="text-muted-foreground hover:text-destructive h-7 w-7 p-0"
                                    title="Delete fact"
                                    onClick={() => deleteFact(fact.id)}
                                  >
                                    <Trash2 className="h-3.5 w-3.5" />
                                  </Button>
                                </div>
                              </TableCell>
                            </TableRow>
                          ))
                        )}
                      </TableBody>
                    </Table>
                  </div>
                </CardContent>
              </Card>
            </TabsContent>
    
            {/* TAB 3: SEMANTIC ENTITY GRAPH */}
            <TabsContent value="graph" className="space-y-4 outline-none">
              <AgentEntityGraph
                triplets={graphTriplets}
                nodes={entityNodes}
                selectedNodeId={selectedNodeId}
                onSelectNode={setSelectedNodeId}
              />
            </TabsContent>
          </Tabs>
        </div>
      )
    }
    
  • components/blocks/AgentContextBuffer.tsx9.7 kB
  • components/blocks/AgentEntityGraph.tsx9.6 kB
  • components/blocks/agent-memory-types.ts1.2 kB
  • components/blocks/agent-memory-data.ts7.2 kB

Raw manifest:https://uipkge.dev/r/react/agent-memory-inspector.json