@@ -4,7 +4,7 @@ import Chart from 'react-apexcharts';
44import type { ApexOptions } from 'apexcharts' ;
55import { Bot , MessageSquare , Star , AlertTriangle , Shield , RefreshCw , Eye , Radio , Clock , Zap } from 'lucide-react' ;
66import { Card , StatCard , Badge , Button , PageHeader , Table , StatusDot , Tabs } from '../../components/ui' ;
7- import { useSessions , useAgents , useMonitorHealth , useAlertRules } from '../../hooks/useApi' ;
7+ import { useSessions , useAgents , useMonitorHealth , useAlertRules , useRuntimeEvents } from '../../hooks/useApi' ;
88import { CHANNEL_LABELS } from '../../types' ;
99import type { ChannelType } from '../../types' ;
1010import SessionDetail from './SessionDetail' ;
@@ -28,6 +28,7 @@ export default function Monitor() {
2828 const navigate = useNavigate ( ) ;
2929 const { data : healthData , refetch : refetchHealth } = useMonitorHealth ( ) ;
3030 const { data : alertRules = [ ] , refetch : refetchAlerts } = useAlertRules ( ) ;
31+ const { data : runtimeData } = useRuntimeEvents ( 60 ) ;
3132 const [ selectedSession , setSelectedSession ] = useState < string | null > ( null ) ;
3233 const [ activeTab , setActiveTab ] = useState ( 'sessions' ) ;
3334
@@ -107,6 +108,7 @@ export default function Monitor() {
107108 { id : 'sessions' , label : 'Live Sessions' , count : sessions . length } ,
108109 { id : 'health' , label : 'Agent Health' , count : health . length } ,
109110 { id : 'alerts' , label : 'Alert Rules' , count : alertCount || undefined } ,
111+ { id : 'runtime' , label : 'Runtime Events' } ,
110112 ] }
111113 activeTab = { activeTab }
112114 onChange = { setActiveTab }
@@ -244,6 +246,71 @@ export default function Monitor() {
244246 </ div >
245247 </ div >
246248 ) }
249+
250+ { activeTab === 'runtime' && (
251+ < div >
252+ { /* Summary cards */ }
253+ < div className = "grid grid-cols-2 gap-3 sm:grid-cols-5 mb-4" >
254+ < div className = "rounded-lg bg-dark-bg border border-dark-border p-3 text-center" >
255+ < div className = "text-lg font-bold text-primary" > { runtimeData ?. summary ?. invocations || 0 } </ div >
256+ < div className = "text-[10px] text-text-muted" > Invocations</ div >
257+ </ div >
258+ < div className = "rounded-lg bg-dark-bg border border-dark-border p-3 text-center" >
259+ < div className = "text-lg font-bold text-amber-400" > { runtimeData ?. summary ?. coldStarts || 0 } </ div >
260+ < div className = "text-[10px] text-text-muted" > Cold Starts</ div >
261+ </ div >
262+ < div className = "rounded-lg bg-dark-bg border border-dark-border p-3 text-center" >
263+ < div className = "text-lg font-bold text-red-400" > { runtimeData ?. summary ?. releases || 0 } </ div >
264+ < div className = "text-[10px] text-text-muted" > VM Releases</ div >
265+ </ div >
266+ < div className = "rounded-lg bg-dark-bg border border-dark-border p-3 text-center" >
267+ < div className = "text-lg font-bold text-green-400" > { runtimeData ?. summary ?. activeTenants || 0 } </ div >
268+ < div className = "text-[10px] text-text-muted" > Active Tenants</ div >
269+ </ div >
270+ < div className = "rounded-lg bg-dark-bg border border-dark-border p-3 text-center" >
271+ < div className = "text-lg font-bold text-text-muted" > { runtimeData ?. summary ?. timeRangeMinutes || 60 } m</ div >
272+ < div className = "text-[10px] text-text-muted" > Time Range</ div >
273+ </ div >
274+ </ div >
275+
276+ { /* Event timeline */ }
277+ < p className = "text-sm text-text-secondary mb-3" > Real-time AgentCore microVM lifecycle events from CloudWatch Logs</ p >
278+ < div className = "space-y-1.5 max-h-[500px] overflow-y-auto" >
279+ { ( runtimeData ?. events || [ ] ) . map ( ( e , i ) => {
280+ const typeConfig : Record < string , { color : string ; icon : string } > = {
281+ invocation : { color : 'text-primary' , icon : '→' } ,
282+ response : { color : 'text-green-400' , icon : '←' } ,
283+ cold_start : { color : 'text-amber-400' , icon : '🔥' } ,
284+ release : { color : 'text-red-400' , icon : '⏹' } ,
285+ ready : { color : 'text-green-400' , icon : '✓' } ,
286+ sync : { color : 'text-cyan-400' , icon : '↻' } ,
287+ plan_a : { color : 'text-orange-400' , icon : '🛡' } ,
288+ usage : { color : 'text-blue-400' , icon : '📊' } ,
289+ mapping : { color : 'text-purple-400' , icon : '🔗' } ,
290+ } ;
291+ const cfg = typeConfig [ e . type ] || { color : 'text-text-muted' , icon : '·' } ;
292+ return (
293+ < div key = { i } className = "flex items-start gap-2 rounded-lg bg-dark-bg/50 px-3 py-2 text-xs hover:bg-dark-bg transition-colors" >
294+ < span className = { `${ cfg . color } font-mono w-4 shrink-0 text-center` } > { cfg . icon } </ span >
295+ < span className = "text-text-muted shrink-0 w-20 font-mono" > { new Date ( e . timestamp ) . toLocaleTimeString ( ) } </ span >
296+ < Badge color = { e . type === 'invocation' ? 'primary' : e . type === 'response' ? 'success' : e . type === 'cold_start' ? 'warning' : e . type === 'release' ? 'danger' : 'default' } >
297+ { e . type }
298+ </ Badge >
299+ < span className = "text-text-secondary flex-1" > { e . message } </ span >
300+ { e . tenant && < code className = "text-[10px] text-text-muted bg-dark-card px-1 rounded truncate max-w-[200px]" > { e . tenant } </ code > }
301+ </ div >
302+ ) ;
303+ } ) }
304+ { ( ! runtimeData ?. events || runtimeData . events . length === 0 ) && (
305+ < div className = "text-center py-8 text-text-muted" >
306+ < Radio size = { 24 } className = "mx-auto mb-2 opacity-50" />
307+ < p className = "text-sm" > No runtime events in the last { runtimeData ?. summary ?. timeRangeMinutes || 60 } minutes</ p >
308+ < p className = "text-xs mt-1" > Events appear when agents are invoked via IM channels or Portal</ p >
309+ </ div >
310+ ) }
311+ </ div >
312+ </ div >
313+ ) }
247314 </ div >
248315 </ Card >
249316 </ div >
0 commit comments