UNPKG

tlnt

Version:

TLNT - HMS-Powered Multi-Agent Platform with Government Agency Analysis, Deep Research, and Enterprise-Ready Deployment. Self-optimizing multi-domain AI agent with continuous learning and enterprise-grade performance monitoring.

234 lines 11.8 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useEffect, useCallback } from 'react'; import { Box, Text, useInput, useApp } from 'ink'; import { AgentState } from './controlAwareAgent.js'; /** * Terminal User Interface for Agent Watch Mode * Provides real-time monitoring and control of HMS Dev agents */ export const AgentWatchTUI = ({ messageBus, watchdog }) => { const { exit } = useApp(); const [state, setState] = useState({ agents: new Map(), events: [], alerts: [], selectedAgent: null, mode: 'agents', filter: { severity: [], eventType: [], source: [] } }); // Update agents list const updateAgents = useCallback(async () => { try { const activeAgents = await messageBus.getActiveAgents(); const agentMap = new Map(); for (const agentId of activeAgents) { const status = await messageBus.getAgentStatus(agentId); if (status) { agentMap.set(agentId, { agentId, state: status.state, lastHeartbeat: new Date(status.lastHeartbeat), currentTask: status.currentTask, progress: status.progress, metadata: status.metadata }); } } setState(prev => ({ ...prev, agents: agentMap })); } catch (error) { console.error('Failed to update agents:', error); } }, [messageBus]); // Update events list const updateEvents = useCallback(() => { const events = watchdog.getRecentEvents(50, { severity: state.filter.severity.length > 0 ? state.filter.severity : undefined, eventType: state.filter.eventType.length > 0 ? state.filter.eventType : undefined, source: state.filter.source.length > 0 ? state.filter.source : undefined }); setState(prev => ({ ...prev, events })); }, [watchdog, state.filter]); // Update alerts list const updateAlerts = useCallback(() => { const alerts = watchdog.getActiveAlerts(); setState(prev => ({ ...prev, alerts })); }, [watchdog]); // Handle keyboard input useInput((input, key) => { if (key.escape || input === 'q') { exit(); return; } switch (input.toLowerCase()) { case '1': setState(prev => ({ ...prev, mode: 'agents' })); break; case '2': setState(prev => ({ ...prev, mode: 'events' })); break; case '3': setState(prev => ({ ...prev, mode: 'alerts' })); break; case 'p': handleAgentControl('PAUSE'); break; case 'r': handleAgentControl('RESUME'); break; case 'm': handleAgentControl('MUTE'); break; case 'u': handleAgentControl('UNMUTE'); break; case 'k': handleAgentControl('KILL'); break; case 'e': handleAgentControl('ESCALATE'); break; case 'a': acknowledgeAlert(); break; case 'f': cycleFilter(); break; } // Agent selection with arrow keys if (state.mode === 'agents') { const agentIds = Array.from(state.agents.keys()); const currentIndex = state.selectedAgent ? agentIds.indexOf(state.selectedAgent) : -1; if (key.upArrow && currentIndex > 0) { setState(prev => ({ ...prev, selectedAgent: agentIds[currentIndex - 1] })); } else if (key.downArrow && currentIndex < agentIds.length - 1) { setState(prev => ({ ...prev, selectedAgent: agentIds[currentIndex + 1] })); } } }); // Handle agent control commands const handleAgentControl = async (command) => { if (!state.selectedAgent) return; try { await messageBus.sendControl(state.selectedAgent, { command: command, agentId: state.selectedAgent, operatorId: 'tui_operator', reason: `Manual ${command.toLowerCase()} from TUI` }); } catch (error) { console.error(`Failed to send ${command} to agent:`, error); } }; // Acknowledge selected alert const acknowledgeAlert = () => { if (state.alerts.length > 0) { const alert = state.alerts[0]; // Acknowledge first alert for simplicity watchdog.acknowledgeAlert(alert.alertId); updateAlerts(); } }; // Cycle through filter options const cycleFilter = () => { // Simplified filter cycling - in real implementation would have proper filter UI setState(prev => ({ ...prev, filter: { severity: prev.filter.severity.length === 0 ? ['error', 'critical'] : [], eventType: [], source: [] } })); }; // Set up periodic updates useEffect(() => { const interval = setInterval(() => { updateAgents(); updateEvents(); updateAlerts(); }, 2000); // Initial update updateAgents(); updateEvents(); updateAlerts(); return () => clearInterval(interval); }, [updateAgents, updateEvents, updateAlerts]); // Render header const renderHeader = () => (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "blue", children: [_jsx(Box, { children: _jsx(Text, { bold: true, color: "cyan", children: "HMS Dev Agent Watch Mode" }) }), _jsx(Box, { children: _jsx(Text, { children: "[1] Agents | [2] Events | [3] Alerts | [Q] Quit" }) }), _jsx(Box, { children: _jsx(Text, { children: "Controls: [P]ause [R]esume [M]ute [U]nmute [K]ill [E]scalate [A]ck Alert [F]ilter" }) })] })); // Render agents view const renderAgents = () => (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "green", children: [_jsx(Box, { children: _jsxs(Text, { bold: true, color: "green", children: ["Active Agents (", state.agents.size, ")"] }) }), Array.from(state.agents.entries()).map(([agentId, status], index) => (_jsxs(Box, { children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: getStateColor(status.state), children: agentId }) }), _jsx(Box, { width: 12, children: _jsx(Text, { color: getStateColor(status.state), children: status.state.toUpperCase() }) }), _jsx(Box, { width: 8, children: _jsxs(Text, { children: [(status.progress * 100).toFixed(0), "%"] }) }), _jsx(Box, { width: 30, children: _jsx(Text, { children: status.currentTask || 'No task' }) }), _jsx(Box, { children: _jsx(Text, { color: "gray", children: getTimeSince(status.lastHeartbeat) }) })] }, agentId))), state.agents.size === 0 && (_jsx(Box, { children: _jsx(Text, { color: "yellow", children: "No active agents" }) }))] })); // Render events view const renderEvents = () => (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", children: [_jsx(Box, { children: _jsxs(Text, { bold: true, color: "yellow", children: ["Recent Events (", state.events.length, ")", state.filter.severity.length > 0 && (_jsxs(Text, { color: "gray", children: [" [Filtered: ", state.filter.severity.join(', '), "]"] }))] }) }), state.events.slice(0, 15).map((event, index) => (_jsxs(Box, { children: [_jsx(Box, { width: 8, children: _jsx(Text, { color: getSeverityColor(event.severity), children: event.severity.toUpperCase() }) }), _jsx(Box, { width: 15, children: _jsx(Text, { children: event.source }) }), _jsx(Box, { width: 15, children: _jsx(Text, { children: event.eventType }) }), _jsx(Box, { width: 12, children: _jsx(Text, { color: "gray", children: event.timestamp.toLocaleTimeString() }) }), _jsx(Box, { children: _jsx(Text, { children: getEventSummary(event) }) })] }, event.eventId))), state.events.length === 0 && (_jsx(Box, { children: _jsx(Text, { color: "yellow", children: "No events" }) }))] })); // Render alerts view const renderAlerts = () => (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "red", children: [_jsx(Box, { children: _jsxs(Text, { bold: true, color: "red", children: ["Active Alerts (", state.alerts.length, ")"] }) }), state.alerts.map((alert, index) => (_jsxs(Box, { children: [_jsx(Box, { width: 10, children: _jsx(Text, { color: getSeverityColor(alert.severity), children: alert.severity.toUpperCase() }) }), _jsx(Box, { width: 15, children: _jsx(Text, { children: alert.type }) }), _jsx(Box, { width: 12, children: _jsx(Text, { color: "gray", children: alert.timestamp.toLocaleTimeString() }) }), _jsx(Box, { children: _jsx(Text, { children: alert.title }) })] }, alert.alertId))), state.alerts.length === 0 && (_jsx(Box, { children: _jsx(Text, { color: "green", children: "No active alerts" }) }))] })); // Render status bar const renderStatusBar = () => (_jsxs(Box, { borderStyle: "single", borderColor: "gray", children: [_jsx(Box, { width: 20, children: _jsxs(Text, { children: ["Mode: ", state.mode.toUpperCase()] }) }), _jsx(Box, { width: 25, children: _jsxs(Text, { children: ["Selected: ", state.selectedAgent || 'None'] }) }), _jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["Last update: ", new Date().toLocaleTimeString()] }) })] })); return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [renderHeader(), _jsxs(Box, { flexGrow: 1, children: [state.mode === 'agents' && renderAgents(), state.mode === 'events' && renderEvents(), state.mode === 'alerts' && renderAlerts()] }), renderStatusBar()] })); }; // Helper functions function getStateColor(state) { switch (state) { case AgentState.RUNNING: return 'green'; case AgentState.PAUSED: return 'yellow'; case AgentState.MUTED: return 'blue'; case AgentState.KILLED: return 'red'; case AgentState.ESCALATED: return 'magenta'; case AgentState.ERROR: return 'red'; default: return 'gray'; } } function getSeverityColor(severity) { switch (severity) { case 'critical': return 'red'; case 'error': return 'red'; case 'warning': return 'yellow'; case 'info': return 'blue'; case 'debug': return 'gray'; default: return 'white'; } } function getTimeSince(date) { const seconds = Math.floor((Date.now() - date.getTime()) / 1000); if (seconds < 60) return `${seconds}s`; if (seconds < 3600) return `${Math.floor(seconds / 60)}m`; if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`; return `${Math.floor(seconds / 86400)}d`; } function getEventSummary(event) { // Extract meaningful summary from event data if (event.data.message) { return String(event.data.message).substring(0, 40); } if (event.data.action) { return `Action: ${event.data.action}`; } if (event.data.error) { return `Error: ${String(event.data.error).substring(0, 30)}`; } return `${event.eventType} event`; } export default AgentWatchTUI; //# sourceMappingURL=agentWatchTui.js.map