import React, { useState, useEffect } from 'react';
import ChatWindow from '../../chat/components/ChatWindow';
import FileSystemNavigator from '../../../shared/components/FileSystemNavigator';
import { getAgents, getSessionMessages, fetchWithAuth, updateAgentConfig, getUserConfig, clearSessionHistory, getSessionTokenStatus, getAgentTriggers, createAgentTrigger, deleteAgentTrigger, getUserAccessibleNodes } from '../../../services/apiService';
export default function AgentDrillDown({ agentId, onNavigate }) {
const [agent, setAgent] = useState(null);
const [chatHistory, setChatHistory] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [overrideText, setOverrideText] = useState("");
// Day 2 Configurations
const [activeTab, setActiveTab] = useState('config'); // workspace or config
const [editConfig, setEditConfig] = useState(null);
const [saving, setSaving] = useState(false);
const [userConfig, setUserConfig] = useState(null);
const [tokenUsage, setTokenUsage] = useState({ token_count: 0, token_limit: 0, percentage: 0 });
const [clearing, setClearing] = useState(false);
const [triggers, setTriggers] = useState([]);
const [newTriggerType, setNewTriggerType] = useState('webhook');
const [newCronValue, setNewCronValue] = useState('0 * * * *');
const [creatingTrigger, setCreatingTrigger] = useState(false);
const [modalConfig, setModalConfig] = useState(null);
const [nodes, setNodes] = useState([]);
useEffect(() => {
const loadConf = async () => {
try {
const conf = await getUserConfig();
setUserConfig(conf);
} catch (e) {}
try {
const nList = await getUserAccessibleNodes();
setNodes(nList);
} catch (e) {}
};
loadConf();
}, []);
const fetchData = async () => {
try {
// Find agent
const allAgents = await getAgents();
const found = allAgents.find(a => a.id === agentId);
if (!found) throw new Error("Agent not found");
setAgent(found);
// Populate form only on first load using the agent context
setEditConfig(prev => prev || {
name: found.template?.name || "",
system_prompt: found.template?.system_prompt_path || "",
max_loop_iterations: found.template?.max_loop_iterations || 20,
mesh_node_id: found.mesh_node_id || "",
provider_name: ""
});
// Fetch chat history if session exists
if (found.session_id) {
const historyResp = await getSessionMessages(found.session_id);
const formatted = (historyResp.messages || []).map(m => ({
text: m.content,
isUser: m.sender === 'user',
reasoning: m.reasoning_content,
status: null,
sender: m.sender,
timestamp: m.created_at,
id: m.id,
tool_calls: m.tool_calls
}));
setChatHistory(formatted);
try {
const usage = await getSessionTokenStatus(found.session_id);
setTokenUsage(usage);
} catch(e) {}
try {
const tList = await getAgentTriggers(agentId);
setTriggers(tList);
} catch(e) {}
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 3000);
return () => clearInterval(interval);
}, [agentId]);
const handleInjectOverride = async (e) => {
e.preventDefault();
if (!overrideText.trim() || !agent?.session_id) return;
try {
await fetchWithAuth(`/agents/${agentId}/webhook`, {
method: "POST",
body: { override_prompt: overrideText }
});
setOverrideText("");
fetchData();
} catch (err) {
setModalConfig({ title: 'Injection Failed', message: err.message, type: 'error' });
}
};
const handleSaveConfig = async () => {
try {
setSaving(true);
const payload = {
name: editConfig.name,
system_prompt: editConfig.system_prompt,
max_loop_iterations: parseInt(editConfig.max_loop_iterations, 10) || 20,
mesh_node_id: editConfig.mesh_node_id || null
};
if (editConfig.provider_name) {
payload.provider_name = editConfig.provider_name;
}
await updateAgentConfig(agentId, payload);
fetchData();
setModalConfig({ title: 'Success', message: 'Configuration Saved Successfully!', type: 'success' });
} catch (err) {
setModalConfig({ title: 'Save Failed', message: err.message, type: 'error' });
} finally {
setSaving(false);
}
};
const handleAddTrigger = async () => {
try {
setCreatingTrigger(true);
const payload = { trigger_type: newTriggerType };
if (newTriggerType === 'cron') payload.cron_expression = newCronValue;
await createAgentTrigger(agentId, payload);
fetchData();
} catch (err) {
setModalConfig({ title: 'Trigger Failed', message: err.message, type: 'error' });
} finally {
setCreatingTrigger(false);
}
};
const handleDeleteTrigger = async (triggerId) => {
try {
await deleteAgentTrigger(triggerId);
fetchData();
} catch (err) {
setModalConfig({ title: 'Delete Failed', message: err.message, type: 'error' });
}
};
if (loading && !agent) return (
<div className="flex h-screen items-center justify-center bg-[#070b14] text-white">
<div className="animate-spin w-8 h-8 border-4 border-indigo-500 border-t-transparent rounded-full font-mono text-sm ml-4"></div>
</div>
);
if (error) return (
<div className="flex h-screen items-center justify-center bg-gray-950 text-red-500">
Error loading Agent Drilldown: {error}
</div>
);
return (
<div className="flex flex-col flex-grow h-full bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 font-sans overflow-hidden">
{/* Minimal Header */}
<div className="border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-800 px-6 py-4 flex items-center justify-between z-10 shrink-0 shadow-sm">
<div className="flex items-center space-x-4">
<button onClick={() => onNavigate('/agents')} className="text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors p-2 -ml-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" /></svg>
</button>
<div>
<h2 className="text-xl font-bold tracking-tight">{agent?.id?.split('-')[0]} Dashboard</h2>
<div className="flex items-center space-x-2 mt-1">
<span className="flex h-2 w-2 relative">
{agent?.status === 'active' && <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>}
<span className={`relative inline-flex rounded-full h-2 w-2 ${agent?.status === 'active' ? 'bg-emerald-500' : 'bg-amber-500'}`}></span>
</span>
<span className="text-[10px] text-gray-500 dark:text-gray-400 uppercase tracking-widest font-mono">Status: {agent?.status}</span>
</div>
</div>
</div>
<div className="flex items-center space-x-4 font-mono text-xs text-gray-500 dark:text-gray-400">
<span>Node: <span className="text-indigo-600 dark:text-indigo-400 font-bold">{nodes.find(n => n.id === agent?.mesh_node_id)?.name || agent?.mesh_node_id || 'unassigned'}</span></span>
<span>Jail: <span className="text-pink-600 dark:text-pink-400 font-bold">{agent?.current_workspace_jail || '/tmp'}</span></span>
</div>
</div>
{/* Main Content Area - 50/50 Split */}
<div className="flex-1 grid grid-cols-2 overflow-hidden gap-1 p-1 bg-gray-200 dark:bg-gray-950">
{/* Left Pane: Chat Tracker */}
<div className="flex flex-col bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800/80 rounded-xl overflow-hidden relative shadow-lg">
<div className="px-4 py-2 border-b border-gray-200 dark:border-gray-800/80 bg-gray-50 dark:bg-gray-900/50 backdrop-blur-md flex justify-between items-center shrink-0">
<span className="text-[10px] uppercase tracking-[0.2em] font-black text-indigo-400 flex items-center gap-2">
<div className="w-1.5 h-1.5 bg-indigo-500 rounded-full animate-pulse shadow-[0_0_8px_rgba(99,102,241,0.5)]"></div>
Live Thought Process
</span>
</div>
<div className="flex-1 overflow-hidden relative">
{agent?.session_id ? (
<ChatWindow
chatHistory={chatHistory}
maxHeight="100%"
autoCollapse={true}
/>
) : (
<div className="flex h-full items-center justify-center text-gray-500 italic text-sm">
No session bounds established for this agent.
</div>
)}
</div>
{/* Inject Prompt Override */}
<div className="p-4 border-t border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900/90 shrink-0">
<form onSubmit={handleInjectOverride} className="relative flex items-center">
<input
type="text"
value={overrideText}
onChange={(e) => setOverrideText(e.target.value)}
placeholder="Steer agent execution loop..."
className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-xl py-3 pl-4 pr-12 focus:outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 transition-all font-mono"
/>
<button
type="submit"
disabled={!overrideText.trim()}
className="absolute right-2 px-3 py-1.5 bg-indigo-600/20 text-indigo-400 rounded-lg text-xs font-bold hover:bg-indigo-600 hover:text-white transition-colors disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-indigo-400 uppercase tracking-wider"
>
Inject
</button>
</form>
</div>
</div>
{/* Right Pane: Multi-Tab Container */}
<div className="flex flex-col bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800/80 rounded-xl overflow-hidden shadow-lg">
{/* Tab Header */}
<div className="flex border-b border-gray-200 dark:border-gray-800/80 bg-gray-50 dark:bg-gray-900/50 px-2 pt-2 shrink-0">
<button
onClick={() => setActiveTab('config')}
className={`px-4 py-2 text-xs font-bold uppercase tracking-widest rounded-t-lg transition-colors ${activeTab === 'config' ? 'bg-white dark:bg-gray-900 text-indigo-500 border-t border-l border-r border-gray-200 dark:border-gray-800/80 -mb-px' : 'text-gray-500 hover:text-gray-400'}`}
>
Metadata & System
</button>
<button
onClick={() => setActiveTab('workspace')}
className={`px-4 py-2 text-xs font-bold uppercase tracking-widest rounded-t-lg transition-colors ${activeTab === 'workspace' ? 'bg-white dark:bg-gray-900 text-indigo-500 border-t border-l border-r border-gray-200 dark:border-gray-800/80 -mb-px' : 'text-gray-500 hover:text-gray-400'}`}
>
Mesh Workspace
</button>
</div>
{/* Tab Body */}
<div className="flex-1 overflow-auto relative">
{activeTab === 'workspace' && (
agent ? (
<FileSystemNavigator
nodeId={agent.mesh_node_id || "local"}
initialPath={agent.current_workspace_jail || "."}
sessionId={agent.session_id}
showSyncStatus={true}
/>
) : (
<div className="flex h-full items-center justify-center text-gray-500">
Initializing workspace bridge...
</div>
)
)}
{activeTab === 'config' && (
<div className="p-6 flex flex-col gap-6">
{/* Token & History Utilities */}
<div className="p-4 rounded-xl border border-gray-200 dark:border-gray-700/50 bg-gray-50 dark:bg-gray-800/30 flex justify-between items-center">
<div className="flex flex-col">
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold mb-1">Session Context Window</span>
<div className="flex items-center gap-3 w-48">
<div className="flex-1 h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-emerald-500 to-indigo-500"
style={{ width: `${Math.min(tokenUsage.percentage, 100)}%` }}
/>
</div>
<span className="text-xs font-mono whitespace-nowrap text-gray-500">{Math.round(tokenUsage.percentage)}%</span>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Agent Name</span>
<input type="text" value={editConfig?.name || ""} onChange={(e) => setEditConfig({...editConfig, name: e.target.value})} className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2.5 px-3 focus:outline-none focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 shadow-sm" placeholder="e.g. Documentation Assistant" />
</div>
<div>
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Active LLM Provider</span>
<select
value={editConfig?.provider_name || userConfig?.effective?.llm?.active_provider || ""}
onChange={e => setEditConfig({...editConfig, provider_name: e.target.value})}
className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2.5 px-3 focus:outline-none focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 shadow-sm"
>
{userConfig?.effective?.llm?.providers && Object.keys(userConfig.effective.llm.providers).map(pid => (
<option key={pid} value={pid}>{pid} {userConfig.effective.llm.providers[pid].model ? `(${userConfig.effective.llm.providers[pid].model})` : ''}</option>
))}
</select>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Target Mesh Node</span>
<select
value={editConfig?.mesh_node_id || ""}
onChange={(e) => setEditConfig({...editConfig, mesh_node_id: e.target.value})}
className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2.5 px-3 focus:outline-none focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 shadow-sm"
>
<option value="">-- Let System Decide --</option>
{nodes.map(n => <option key={n.id} value={n.id}>{n.name}</option>)}
</select>
</div>
<div>
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Max Iterations</span>
<input type="number" min="1" max="100" value={editConfig?.max_loop_iterations || 20} onChange={(e) => setEditConfig({...editConfig, max_loop_iterations: e.target.value})} className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2.5 px-3 focus:outline-none focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 shadow-sm" />
</div>
</div>
</div>
{/* Execution Triggers Box */}
<div>
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-2">Execution Triggers</span>
<div className="bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 rounded-lg p-4 shadow-sm">
{/* List existing triggers */}
{triggers.length > 0 && (
<div className="mb-4 space-y-2">
{triggers.map(t => (
<div key={t.id} className="flex items-center justify-between bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded px-3 py-2 text-sm">
<div className="flex flex-col">
<span className="font-bold text-gray-800 dark:text-gray-200 uppercase tracking-wider text-[10px]">{t.trigger_type} ID: {t.id.split('-')[0]}</span>
<span className="font-mono text-gray-500 dark:text-gray-400 text-xs">
{t.trigger_type === 'cron' ? `Schedule: ${t.cron_expression}` : `Secret: ${t.webhook_secret}`}
</span>
</div>
<button
onClick={e => { e.preventDefault(); handleDeleteTrigger(t.id); }}
className="text-red-500 hover:text-red-700 font-bold text-xs bg-red-50 dark:bg-red-900/20 px-2 py-1 rounded"
>
Delete
</button>
</div>
))}
</div>
)}
{/* Add new trigger form */}
<div className="flex items-end gap-3 border-t border-gray-200 dark:border-gray-800 pt-4 mt-2">
<div className="w-1/3">
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Type</span>
<select
value={newTriggerType}
onChange={e => setNewTriggerType(e.target.value)}
className="w-full bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2 px-3 focus:outline-none"
>
<option value="webhook">Webhook Listener</option>
<option value="cron">CRON Schedule</option>
</select>
</div>
{newTriggerType === 'cron' && (
<div className="flex-1">
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">CRON Expr</span>
<input
type="text"
value={newCronValue}
onChange={e => setNewCronValue(e.target.value)}
className="w-full bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2 px-3 font-mono focus:outline-none"
/>
</div>
)}
<button
onClick={e => { e.preventDefault(); handleAddTrigger(); }}
disabled={creatingTrigger}
className="px-4 py-2 text-xs font-bold bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-400 border border-indigo-200 dark:border-indigo-800 hover:bg-indigo-200 dark:hover:bg-indigo-800/50 rounded-md transition-colors whitespace-nowrap"
>
+ Add Trigger
</button>
</div>
</div>
</div>
<div className="flex-1 flex flex-col min-h-[300px]">
<span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-2">Core System Instruction Prompt (Modifiable Live)</span>
<textarea value={editConfig?.system_prompt || ""} onChange={(e) => setEditConfig({...editConfig, system_prompt: e.target.value})} className="flex-1 resize-none w-full bg-gray-900 text-emerald-400 p-4 rounded-lg text-sm leading-relaxed font-mono shadow-inner border border-gray-700 focus:outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500" placeholder="You are an expert AI agent..."></textarea>
<p className="text-xs text-gray-500 mt-2 font-mono">Changes to the system prompt will be immediately picked up by the agent loop on its next invocation turn.</p>
</div>
<div className="flex justify-end pt-4">
<button onClick={handleSaveConfig} disabled={saving} className="px-6 py-2.5 text-sm font-bold bg-indigo-600 hover:bg-indigo-700 text-white shadow-md rounded-lg transition-all active:scale-95 flex items-center gap-2">
{saving ? "Saving Configuration..." : "Apply Live Configuration"}
</button>
</div>
</div>
)}
</div>
</div>
</div>
{/* Modal Overlay Component */}
{modalConfig && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/40 backdrop-blur-sm p-4">
<div className="bg-white dark:bg-gray-800 shadow-2xl rounded-2xl w-full max-w-sm overflow-hidden border border-gray-100 dark:border-gray-700 transform transition-all p-6 relative">
<div className={`absolute top-0 right-0 w-64 h-64 ${modalConfig.type === 'error' ? 'bg-red-500/5' : 'bg-emerald-500/5'} blur-[80px] rounded-full pointer-events-none -z-10`} />
<h3 className={`text-xl font-bold mb-2 ${modalConfig.type === 'error' ? 'text-red-500' : 'text-emerald-500'}`}>{modalConfig.title}</h3>
<p className="text-gray-600 dark:text-gray-300 text-sm mb-6 font-mono">{modalConfig.message}</p>
<div className="flex justify-end">
<button onClick={() => setModalConfig(null)} className="px-4 py-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 rounded-lg text-sm font-semibold transition-colors">
Close
</button>
</div>
</div>
</div>
)}
</div>
);
}