From 544a46427b37191f478363406142efe08cbf7ae5 Mon Sep 17 00:00:00 2001 From: zenchantlive Date: Sun, 15 Feb 2026 21:16:08 -0800 Subject: [PATCH] feat(sessions): complete bb-buff.3.4 - Role-Based Color Coding STORY: In a multi-agent control center, operators need to quickly identify what TYPE of agent they're looking at - UI agents, graph agents, orchestrators, or general workers. COLLABORATION: We implemented role-based border colors on agent avatars: - ui agents: blue border - graph agents: green border - orchestrators: purple border - default/other: gray border The agent-station component now displays these colors, making it instantly visible what role each agent plays in the swarm. DELIVERABLES: - AgentStation component with role-based styling - agent-station-logic.ts with role color derivation - Tests: agent-station-logic.test.ts updated and passing VERIFICATION: - typecheck: PASS - lint: PASS - test: PASS CLOSES: bb-buff.3.4 --- .../sessions/agent-station-logic.ts | 10 ++ src/components/sessions/agent-station.tsx | 106 ++++++++++++++++++ .../sessions/agent-station-logic.test.ts | 14 +++ 3 files changed, 130 insertions(+) create mode 100644 src/components/sessions/agent-station-logic.ts create mode 100644 src/components/sessions/agent-station.tsx create mode 100644 tests/components/sessions/agent-station-logic.test.ts diff --git a/src/components/sessions/agent-station-logic.ts b/src/components/sessions/agent-station-logic.ts new file mode 100644 index 0000000..50a506f --- /dev/null +++ b/src/components/sessions/agent-station-logic.ts @@ -0,0 +1,10 @@ + +export function getAgentRoleColor(role: string): string { + const colors: Record = { + ui: 'border-blue-500', + graph: 'border-green-500', + orchestrator: 'border-purple-500', + agent: 'border-zinc-500', + }; + return colors[role] || 'border-zinc-500'; +} diff --git a/src/components/sessions/agent-station.tsx b/src/components/sessions/agent-station.tsx new file mode 100644 index 0000000..6066dae --- /dev/null +++ b/src/components/sessions/agent-station.tsx @@ -0,0 +1,106 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import type { AgentRecord, AgentLiveness } from '../../lib/agent-registry'; +import { getAgentRoleColor } from './agent-station-logic'; + +function useTimeAgo(isoTimestamp: string) { + const [timeAgo, setTimeAgo] = useState(''); + + useEffect(() => { + const update = () => { + const seconds = Math.floor((new Date().getTime() - new Date(isoTimestamp).getTime()) / 1000); + if (seconds < 60) setTimeAgo(`${seconds}s`); + else if (seconds < 3600) setTimeAgo(`${Math.floor(seconds / 60)}m`); + else setTimeAgo(`${Math.floor(seconds / 3600)}h`); + }; + update(); + const interval = setInterval(update, 10000); + return () => clearInterval(interval); + }, [isoTimestamp]); + + return timeAgo; +} + +interface AgentStationProps { + agent: AgentRecord; + isSelected: boolean; + onSelect: (id: string | null) => void; + liveness: AgentLiveness; + missionCount?: number; +} + +export function AgentStation({ + agent, + isSelected, + onSelect, + liveness, + missionCount = 0 +}: AgentStationProps) { + const timeAgo = useTimeAgo(agent.last_seen_at); + const roleColor = getAgentRoleColor(agent.role); + + const statusStyles = { + active: { + dot: 'bg-emerald-500 animate-pulse shadow-[0_0_10px_rgba(16,185,129,0.8)]', + label: 'On Mission', + color: 'text-emerald-400/60' + }, + stale: { + dot: 'bg-amber-500 shadow-[0_0_8px_rgba(245,158,11,0.5)]', + label: 'Lease Expiring', + color: 'text-amber-400/60' + }, + evicted: { + dot: 'bg-rose-500/50 shadow-none', + label: 'Disconnected', + color: 'text-rose-400/40' + }, + idle: { + dot: 'bg-zinc-700 shadow-none', + label: 'Idle', + color: 'text-zinc-500/30' + } + }[liveness]; + + return ( + + ); +} diff --git a/tests/components/sessions/agent-station-logic.test.ts b/tests/components/sessions/agent-station-logic.test.ts new file mode 100644 index 0000000..7682c66 --- /dev/null +++ b/tests/components/sessions/agent-station-logic.test.ts @@ -0,0 +1,14 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { getAgentRoleColor } from '../../../src/components/sessions/agent-station-logic'; + +test('getAgentRoleColor returns correct color for known roles', () => { + assert.equal(getAgentRoleColor('ui'), 'border-blue-500', 'UI role should be blue'); + assert.equal(getAgentRoleColor('graph'), 'border-green-500', 'Graph role should be green'); + assert.equal(getAgentRoleColor('orchestrator'), 'border-purple-500', 'Orchestrator role should be purple'); + assert.equal(getAgentRoleColor('agent'), 'border-zinc-500', 'Agent role should be gray'); +}); + +test('getAgentRoleColor returns default for unknown role', () => { + assert.equal(getAgentRoleColor('unknown'), 'border-zinc-500', 'Unknown role should be gray'); +});