2026-03-19 10:23:13 +01:00
import { useState , useMemo } from 'react'
import { useParams , Link } from 'react-router-dom'
2026-03-27 23:25:43 +01:00
import { ChevronRight } from 'lucide-react'
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
import styles from './AgentHealth.module.css'
// Layout
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
// Composites
2026-03-18 18:22:14 +01:00
import { GroupCard } from '../../design-system/composites/GroupCard/GroupCard'
2026-03-24 12:29:45 +01:00
import { DataTable } from '../../design-system/composites/DataTable/DataTable'
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
import { LineChart } from '../../design-system/composites/LineChart/LineChart'
2026-03-18 18:22:14 +01:00
import { EventFeed } from '../../design-system/composites/EventFeed/EventFeed'
2026-03-19 10:23:13 +01:00
import { DetailPanel } from '../../design-system/composites/DetailPanel/DetailPanel'
2026-03-24 12:29:45 +01:00
import type { Column } from '../../design-system/composites/DataTable/types'
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
// Primitives
import { StatusDot } from '../../design-system/primitives/StatusDot/StatusDot'
import { MonoText } from '../../design-system/primitives/MonoText/MonoText'
import { Badge } from '../../design-system/primitives/Badge/Badge'
2026-03-18 18:22:14 +01:00
import { StatCard } from '../../design-system/primitives/StatCard/StatCard'
2026-03-19 10:23:13 +01:00
import { ProgressBar } from '../../design-system/primitives/ProgressBar/ProgressBar'
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
2026-03-18 20:06:25 +01:00
// Global filters
import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProvider'
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
// Mock data
2026-03-18 18:22:14 +01:00
import { agents , type AgentHealth as AgentHealthData } from '../../mocks/agents'
import { agentEvents } from '../../mocks/agentEvents'
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
2026-03-18 18:22:14 +01:00
// ── URL scope parsing ────────────────────────────────────────────────────────
type Scope =
| { level : 'all' }
| { level : 'app' ; appId : string }
function useScope ( ) : Scope {
const { '*' : rest } = useParams ( )
const segments = rest ? . split ( '/' ) . filter ( Boolean ) ? ? [ ]
2026-03-19 10:23:13 +01:00
if ( segments . length >= 1 ) return { level : 'app' , appId : segments [ 0 ] }
2026-03-18 18:22:14 +01:00
return { level : 'all' }
}
// ── Data grouping ────────────────────────────────────────────────────────────
interface AppGroup {
appId : string
instances : AgentHealthData [ ]
liveCount : number
staleCount : number
deadCount : number
totalTps : number
totalActiveRoutes : number
totalRoutes : number
}
function groupByApp ( agentList : AgentHealthData [ ] ) : AppGroup [ ] {
const map = new Map < string , AgentHealthData [ ] > ( )
for ( const a of agentList ) {
const list = map . get ( a . appId ) ? ? [ ]
list . push ( a )
map . set ( a . appId , list )
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
}
2026-03-18 18:22:14 +01:00
return Array . from ( map . entries ( ) ) . map ( ( [ appId , instances ] ) = > ( {
appId ,
instances ,
liveCount : instances.filter ( ( i ) = > i . status === 'live' ) . length ,
staleCount : instances.filter ( ( i ) = > i . status === 'stale' ) . length ,
deadCount : instances.filter ( ( i ) = > i . status === 'dead' ) . length ,
totalTps : instances.reduce ( ( s , i ) = > s + i . tps , 0 ) ,
totalActiveRoutes : instances.reduce ( ( s , i ) = > s + i . activeRoutes , 0 ) ,
totalRoutes : instances.reduce ( ( s , i ) = > s + i . totalRoutes , 0 ) ,
} ) )
}
function appHealth ( group : AppGroup ) : 'success' | 'warning' | 'error' {
if ( group . deadCount > 0 ) return 'error'
if ( group . staleCount > 0 ) return 'warning'
return 'success'
}
// ── Trend data (mock) ────────────────────────────────────────────────────────
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
2026-03-18 18:22:14 +01:00
function buildTrendData ( agent : AgentHealthData ) {
const now = Date . now ( )
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
const points = 20
2026-03-18 18:22:14 +01:00
const interval = ( 3 * 60 * 60 * 1000 ) / points
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
2026-03-18 18:22:14 +01:00
const throughput = Array . from ( { length : points } , ( _ , i ) = > ( {
x : new Date ( now - ( points - i ) * interval ) ,
y : Math.max ( 0 , agent . tps + ( Math . random ( ) - 0.5 ) * 4 ) ,
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
} ) )
2026-03-18 18:22:14 +01:00
const errorRate = Array . from ( { length : points } , ( _ , i ) = > ( {
x : new Date ( now - ( points - i ) * interval ) ,
y : Math.max ( 0 , ( agent . errorRate ? parseFloat ( agent . errorRate ) : 0.5 ) + ( Math . random ( ) - 0.5 ) * 2 ) ,
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
} ) )
2026-03-18 18:22:14 +01:00
return { throughput , errorRate }
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
}
2026-03-18 18:22:14 +01:00
// ── Breadcrumb ───────────────────────────────────────────────────────────────
function buildBreadcrumb ( scope : Scope ) {
const crumbs : { label : string ; href? : string } [ ] = [
2026-03-18 19:26:27 +01:00
{ label : 'Applications' , href : '/apps' } ,
2026-03-18 18:22:14 +01:00
{ label : 'Agents' , href : '/agents' } ,
]
2026-03-19 10:23:13 +01:00
if ( scope . level === 'app' ) {
crumbs . push ( { label : scope.appId } )
2026-03-18 18:22:14 +01:00
}
return crumbs
}
// ── AgentHealth page ─────────────────────────────────────────────────────────
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
export function AgentHealth() {
2026-03-18 18:22:14 +01:00
const scope = useScope ( )
2026-03-18 20:06:25 +01:00
const { isInTimeRange } = useGlobalFilters ( )
2026-03-19 10:23:13 +01:00
const [ selectedInstance , setSelectedInstance ] = useState < AgentHealthData | null > ( null )
const [ panelOpen , setPanelOpen ] = useState ( false )
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
2026-03-18 18:22:14 +01:00
// Filter agents by scope
const filteredAgents = useMemo ( ( ) = > {
if ( scope . level === 'all' ) return agents
2026-03-19 10:23:13 +01:00
return agents . filter ( ( a ) = > a . appId === scope . appId )
2026-03-18 18:22:14 +01:00
} , [ scope ] )
const groups = useMemo ( ( ) = > groupByApp ( filteredAgents ) , [ filteredAgents ] )
// Aggregate stats
const totalInstances = filteredAgents . length
const liveCount = filteredAgents . filter ( ( a ) = > a . status === 'live' ) . length
const staleCount = filteredAgents . filter ( ( a ) = > a . status === 'stale' ) . length
const deadCount = filteredAgents . filter ( ( a ) = > a . status === 'dead' ) . length
const totalTps = filteredAgents . reduce ( ( s , a ) = > s + a . tps , 0 )
const totalActiveRoutes = filteredAgents . reduce ( ( s , a ) = > s + a . activeRoutes , 0 )
2026-03-19 10:23:13 +01:00
const totalRoutes = filteredAgents . reduce ( ( s , a ) = > s + a . totalRoutes , 0 )
2026-03-18 18:22:14 +01:00
2026-03-18 20:06:25 +01:00
// Filter events by global time range
const filteredEvents = agentEvents . filter ( ( e ) = > isInTimeRange ( e . timestamp ) )
2026-03-18 18:22:14 +01:00
2026-03-19 10:23:13 +01:00
// Build trend data for selected instance
const trendData = selectedInstance ? buildTrendData ( selectedInstance ) : null
2026-03-24 12:29:45 +01:00
// Column definitions for the instance DataTable
const instanceColumns : Column < AgentHealthData > [ ] = useMemo ( ( ) = > [
{
key : 'status' ,
header : '' ,
width : '12px' ,
render : ( _val , row ) = > (
< StatusDot variant = { row . status === 'live' ? 'live' : row . status === 'stale' ? 'stale' : 'dead' } / >
) ,
} ,
{
key : 'name' ,
header : 'Instance' ,
render : ( _val , row ) = > (
< MonoText size = "sm" className = { styles . instanceName } > { row . name } < / MonoText >
) ,
} ,
{
key : 'state' ,
header : 'State' ,
render : ( _val , row ) = > (
< Badge
label = { row . status . toUpperCase ( ) }
color = { row . status === 'live' ? 'success' : row . status === 'stale' ? 'warning' : 'error' }
variant = "filled"
/ >
) ,
} ,
{
key : 'uptime' ,
header : 'Uptime' ,
render : ( _val , row ) = > (
< MonoText size = "xs" className = { styles . instanceMeta } > { row . uptime } < / MonoText >
) ,
} ,
{
key : 'tps' ,
header : 'TPS' ,
render : ( _val , row ) = > (
< MonoText size = "xs" className = { styles . instanceMeta } > { row . tps . toFixed ( 1 ) } / s < / MonoText >
) ,
} ,
{
key : 'errorRate' ,
header : 'Errors' ,
render : ( _val , row ) = > (
< MonoText size = "xs" className = { row . errorRate ? styles.instanceError : styles.instanceMeta } >
{ row . errorRate ? ? '0 err/h' }
< / MonoText >
) ,
} ,
{
key : 'lastSeen' ,
header : 'Heartbeat' ,
render : ( _val , row ) = > (
< MonoText size = "xs" className = {
row . status === 'dead' ? styles . instanceHeartbeatDead :
row . status === 'stale' ? styles . instanceHeartbeatStale :
styles . instanceMeta
} >
{ row . lastSeen }
< / MonoText >
) ,
} ,
] , [ ] )
2026-03-19 10:23:13 +01:00
function handleInstanceClick ( inst : AgentHealthData ) {
setSelectedInstance ( inst )
setPanelOpen ( true )
}
// Detail panel tabs
const detailTabs = selectedInstance
? [
{
label : 'Overview' ,
value : 'overview' ,
content : (
< div className = { styles . detailContent } >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Status < / span >
< Badge
label = { selectedInstance . status . toUpperCase ( ) }
color = { selectedInstance . status === 'live' ? 'success' : selectedInstance . status === 'stale' ? 'warning' : 'error' }
/ >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Application < / span >
< MonoText size = "xs" > { selectedInstance . appId } < / MonoText >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Version < / span >
< MonoText size = "xs" > { selectedInstance . version } < / MonoText >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Uptime < / span >
< MonoText size = "xs" > { selectedInstance . uptime } < / MonoText >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Last Seen < / span >
< MonoText size = "xs" > { selectedInstance . lastSeen } < / MonoText >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Throughput < / span >
< MonoText size = "xs" > { selectedInstance . tps . toFixed ( 1 ) } / s < / MonoText >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Errors < / span >
< MonoText size = "xs" className = { selectedInstance . errorRate ? styles.instanceError : undefined } >
{ selectedInstance . errorRate ? ? '0 err/h' }
< / MonoText >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Routes < / span >
< span > { selectedInstance . activeRoutes } / { selectedInstance . totalRoutes } active < / span >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > Memory < / span >
< div className = { styles . detailProgress } >
< ProgressBar
value = { selectedInstance . memoryUsagePct }
variant = { selectedInstance . memoryUsagePct > 85 ? 'error' : selectedInstance . memoryUsagePct > 70 ? 'warning' : 'success' }
/ >
< MonoText size = "xs" > { selectedInstance . memoryUsagePct } % < / MonoText >
< / div >
< / div >
< div className = { styles . detailRow } >
< span className = { styles . detailLabel } > CPU < / span >
< div className = { styles . detailProgress } >
< ProgressBar
value = { selectedInstance . cpuUsagePct }
variant = { selectedInstance . cpuUsagePct > 85 ? 'error' : selectedInstance . cpuUsagePct > 70 ? 'warning' : 'success' }
/ >
< MonoText size = "xs" > { selectedInstance . cpuUsagePct } % < / MonoText >
< / div >
< / div >
< / div >
) ,
} ,
{
label : 'Performance' ,
value : 'performance' ,
content : trendData ? (
< div className = { styles . detailContent } >
< div className = { styles . chartPanel } >
< div className = { styles . chartTitle } > Throughput ( msg / s ) < / div >
< LineChart
series = { [ { label : 'tps' , data : trendData.throughput } ] }
height = { 160 }
width = { 360 }
yLabel = "msg/s"
/ >
< / div >
< div className = { styles . chartPanel } >
< div className = { styles . chartTitle } > Error Rate ( err / h ) < / div >
< LineChart
series = { [ { label : 'errors' , data : trendData.errorRate , color : 'var(--error)' } ] }
height = { 160 }
width = { 360 }
yLabel = "err/h"
/ >
< / div >
< / div >
) : null ,
} ,
]
: [ ]
2026-03-18 18:22:14 +01:00
const isFullWidth = scope . level !== 'all'
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
return (
2026-04-02 18:09:16 +02:00
< >
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
< TopBar
2026-03-18 18:22:14 +01:00
breadcrumb = { buildBreadcrumb ( scope ) }
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
environment = "PRODUCTION"
user = { { name : 'hendrik' } }
/ >
< div className = { styles . content } >
2026-03-18 18:22:14 +01:00
{ /* Stat strip */ }
< div className = { styles . statStrip } >
2026-03-19 10:23:13 +01:00
< StatCard
label = "Total Agents"
value = { String ( totalInstances ) }
accent = { deadCount > 0 ? 'warning' : 'amber' }
detail = {
< span className = { styles . breakdown } >
< span className = { styles . bpLive } > < StatusDot variant = "live" / > { liveCount } live < / span >
< span className = { styles . bpStale } > < StatusDot variant = "stale" / > { staleCount } stale < / span >
< span className = { styles . bpDead } > < StatusDot variant = "dead" / > { deadCount } dead < / span >
< / span >
}
/ >
< StatCard
label = "Applications"
value = { String ( groups . length ) }
accent = "running"
detail = {
< span className = { styles . breakdown } >
< span className = { styles . bpLive } > < StatusDot variant = "live" / > { groups . filter ( ( g ) = > g . deadCount === 0 && g . staleCount === 0 ) . length } healthy < / span >
< span className = { styles . bpStale } > < StatusDot variant = "stale" / > { groups . filter ( ( g ) = > g . staleCount > 0 && g . deadCount === 0 ) . length } degraded < / span >
< span className = { styles . bpDead } > < StatusDot variant = "dead" / > { groups . filter ( ( g ) = > g . deadCount > 0 ) . length } critical < / span >
< / span >
}
/ >
< StatCard
label = "Active Routes"
value = { < span className = { styles [ totalActiveRoutes === 0 ? 'routesError' : totalActiveRoutes < totalRoutes ? 'routesWarning' : 'routesSuccess' ] } > { totalActiveRoutes } / { totalRoutes } < / span > }
accent = { totalActiveRoutes === 0 ? 'error' : totalActiveRoutes < totalRoutes ? 'warning' : 'success' }
detail = { totalActiveRoutes < totalRoutes ? ` ${ totalRoutes - totalActiveRoutes } suspended ` : 'all routes active' }
/ >
< StatCard
label = "Total TPS"
value = { totalTps . toFixed ( 1 ) }
accent = "amber"
detail = "msg/s"
trend = "up"
trendValue = "4.2%"
/ >
< StatCard
label = "Dead"
value = { String ( deadCount ) }
accent = { deadCount > 0 ? 'error' : 'success' }
detail = { deadCount > 0 ? 'requires attention' : 'all healthy' }
/ >
2026-03-18 18:22:14 +01:00
< / div >
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
feat: add AgentInstance detail page and improve AgentHealth
- New /agents/:appId/:instanceId page with process info, 3x2 charts
grid (CPU, memory, throughput, errors, threads, GC), application
log viewer with level filtering, and instance-scoped timeline
- AgentHealth now uses slide-in DetailPanel for quick instance preview
- Stat strip enhanced: colored StatusDot breakdowns, route ratio with
state-colored values, Groups renamed to Applications
- Unified page structure: stat strip → scope trail with inline badges
(removed duplicate section headers from both pages)
- StatCard value/detail props now accept ReactNode
- Log and timeline displayed side by side
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:51:13 +01:00
{ /* Scope trail + badges */ }
< div className = { styles . scopeTrail } >
{ scope . level !== 'all' && (
< >
< Link to = "/agents" className = { styles . scopeLink } > All Agents < / Link >
2026-03-27 23:25:43 +01:00
< span className = { styles . scopeSep } > < ChevronRight size = { 12 } / > < / span >
feat: add AgentInstance detail page and improve AgentHealth
- New /agents/:appId/:instanceId page with process info, 3x2 charts
grid (CPU, memory, throughput, errors, threads, GC), application
log viewer with level filtering, and instance-scoped timeline
- AgentHealth now uses slide-in DetailPanel for quick instance preview
- Stat strip enhanced: colored StatusDot breakdowns, route ratio with
state-colored values, Groups renamed to Applications
- Unified page structure: stat strip → scope trail with inline badges
(removed duplicate section headers from both pages)
- StatCard value/detail props now accept ReactNode
- Log and timeline displayed side by side
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:51:13 +01:00
< span className = { styles . scopeCurrent } > { scope . appId } < / span >
< / >
) }
2026-03-18 19:11:58 +01:00
< Badge
label = { ` ${ liveCount } / ${ totalInstances } live ` }
color = { deadCount > 0 ? 'error' : staleCount > 0 ? 'warning' : 'success' }
variant = "filled"
/ >
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
< / div >
2026-03-18 18:22:14 +01:00
{ /* Group cards grid */ }
< div className = { isFullWidth ? styles.groupGridSingle : styles.groupGrid } >
{ groups . map ( ( group ) = > (
< GroupCard
key = { group . appId }
title = { group . appId }
accent = { appHealth ( group ) }
headerRight = {
2026-03-18 19:11:58 +01:00
< Badge
label = { ` ${ group . liveCount } / ${ group . instances . length } LIVE ` }
color = { appHealth ( group ) }
variant = "filled"
/ >
2026-03-18 18:22:14 +01:00
}
meta = {
< div className = { styles . groupMeta } >
< span > < strong > { group . totalTps . toFixed ( 1 ) } < / strong > msg / s < / span >
< span > < strong > { group . totalActiveRoutes } < / strong > / { group . totalRoutes } routes < / span >
< span >
< StatusDot variant = { appHealth ( group ) === 'success' ? 'live' : appHealth ( group ) === 'warning' ? 'stale' : 'dead' } / >
< / span >
< / div >
}
footer = { group . deadCount > 0 ? (
< div className = { styles . alertBanner } >
< span className = { styles . alertIcon } > & # 9888 ; < / span >
< span > Single point of failure — { group . deadCount === group . instances . length ? 'no redundancy' : ` ${ group . deadCount } dead instance ${ group . deadCount > 1 ? 's' : '' } ` } < / span >
< / div >
) : undefined }
>
2026-03-24 12:29:45 +01:00
< DataTable < AgentHealthData >
columns = { instanceColumns }
data = { group . instances }
onRowClick = { handleInstanceClick }
selectedId = { panelOpen ? selectedInstance?.id : undefined }
pageSize = { 50 }
flush
/ >
2026-03-18 18:22:14 +01:00
< / GroupCard >
) ) }
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
< / div >
2026-03-18 18:22:14 +01:00
{ /* EventFeed */ }
{ filteredEvents . length > 0 && (
2026-03-18 19:11:58 +01:00
< div className = { styles . eventCard } >
< div className = { styles . eventCardHeader } >
2026-03-18 18:22:14 +01:00
< span className = { styles . sectionTitle } > Timeline < / span >
2026-03-18 19:11:58 +01:00
< span className = { styles . sectionMeta } > { filteredEvents . length } events < / span >
2026-03-18 18:22:14 +01:00
< / div >
< EventFeed events = { filteredEvents } / >
< / div >
) }
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
< / div >
2026-04-02 18:09:16 +02:00
{ /* Detail panel (portals itself) */ }
{ selectedInstance && (
< DetailPanel
open = { panelOpen }
onClose = { ( ) = > setPanelOpen ( false ) }
title = { selectedInstance . name }
tabs = { detailTabs }
/ >
) }
< / >
feat: ExchangeDetail and AgentHealth pages
ExchangeDetail (/exchanges/:id): exchange header card with ID/route/
status/duration, ProcessorTimeline for the specific exchange, step-by-
step exchange inspector using Collapsible+CodeBlock for headers/body at
each processor step, and error details block for failed exchanges.
AgentHealth (/agents): 6-card system overview strip, 2-column grid of
agent cards (StatusDot, name, version, tps, uptime, last-seen, CPU/mem
usage, active routes), expandable per-agent LineCharts for throughput
and error rate trends. Both pages use AppShell + shared Sidebar layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:22:11 +01:00
)
}