AgentContextDebuggerTab.tsx5.5 KBView on GitHub import { Hash, Check, Copy, ChevronDown, ChevronRight } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { useCedarStore } from '@/modules/store';
import React, { useState, useMemo } from 'react';
import { JsonTreeView } from './JsonTreeView';
interface AgentContextTabProps {
onCopy: (text: string, id: string) => void;
copiedId: string | null;
}
export const AgentContextDebuggerTab: React.FC<AgentContextTabProps> = ({ onCopy, copiedId }) => {
const [expandedKeys, setExpandedKeys] = useState<Set<string>>(new Set());
// Read the compiled context that gets sent to the LLM
const buildMergedContext = useCedarStore((s) => s.buildMergedContextForMailSendMessage);
const additionalContext = useCedarStore((s) => s.additionalContext);
const excludedManualContextKeys = useCedarStore((s) => s.excludedManualContextKeys);
const activeView = useCedarStore((s) => s.getActiveView());
const activeCanvasId = useCedarStore((s) => s.activeCanvasId);
const activeConversationId = useCedarStore((s) => s.activeConversationId);
const mergedContext = useMemo(() => buildMergedContext(), [
buildMergedContext,
additionalContext,
excludedManualContextKeys,
activeView,
activeCanvasId,
activeConversationId,
]);
const toggleExpanded = (key=[redacted] => {
setExpandedKeys((prev) => {
const next = new Set(prev);
if (next.has(key)) {
next.delete(key);
} else {
next.add(key);
}
return next;
});
};
const getPreview = (value: unknown): string => {
if (value === null) return 'null';
if (value === undefined) return 'undefined';
if (typeof value === 'string')
return `"${value.substring(0, 50)}${value.length > 50 ? '...' : ''}"`;
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
if (Array.isArray(value)) return `Array(${value.length})`;
if (typeof value === 'object') return `Object(${Object.keys(value as Record<string, unknown>).length} keys)`;
return String(value);
};
const entries = Object.entries(mergedContext);
return (
<div className="h-full space-y-1 overflow-y-auto p-2">
{/* Header with view stack info */}
<div className="mb-2 rounded-lg border border-blue-200 bg-blue-50 p-2 dark:border-blue-800 dark:bg-blue-950">
<div className="text-xs font-medium text-blue-700 dark:text-blue-300">
Active View: {activeView ?? 'none'}
</div>
<div className="mt-0.5 text-xs text-blue-600 dark:text-blue-400">
Excluded keys: {excludedManualContextKeys.size > 0 ? Array.from(excludedManualContextKeys).join(', ') : 'none'}
</div>
</div>
{entries.length === 0 ? (
<div className="py-4 text-center text-xs text-gray-500 dark:text-gray-400">
No agent context available
</div>
) : (
entries.map(([key, value]) => {
const isExpanded = expandedKeys.has(key);
const copyId = `ctx-${key}`;
return (
<div
key=[redacted]
className="rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-800 dark:bg-gray-950"
>
<div
className={`flex cursor-pointer items-center justify-between p-2 transition-colors hover:bg-gray-100 dark:hover:bg-gray-900/80 ${
isExpanded ? 'rounded-t-lg' : 'rounded-lg'
}`}
onClick={() => toggleExpanded(key)}
>
<div className="flex flex-1 items-center gap-1.5">
<Hash className="h-3 w-3 text-gray-500" />
<span className="font-mono text-xs font-medium">{key}</span>
{!isExpanded && (
<span className="ml-1 text-xs text-gray-500 dark:text-gray-500">
{getPreview(value)}
</span>
)}
</div>
<div className="flex items-center gap-1">
<button
onClick={(e) => {
e.stopPropagation();
onCopy(JSON.stringify(value, null, 2), copyId);
}}
className="rounded p-0.5 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
>
{copiedId === copyId ? (
<Check className="h-3 w-3 text-green-600" />
) : (
<Copy className="h-3 w-3" />
)}
</button>
{isExpanded ? (
<ChevronDown className="h-3 w-3" />
) : (
<ChevronRight className="h-3 w-3" />
)}
</div>
</div>
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="rounded-b-lg p-3 pt-0">
<JsonTreeView data={value} onCopy={onCopy} copiedId={copiedId} defaultExpandDepth={2} className="max-h-[50vh]" />
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
})
)}
</div>
);
};