feat: add concurrent agents with dependency system and delightful UI

Major feature implementation for parallel agent execution with dependency-aware
scheduling and an engaging multi-agent UI experience.

Backend Changes:
- Add parallel_orchestrator.py for concurrent feature processing
- Add api/dependency_resolver.py with cycle detection (Kahn's algorithm + DFS)
- Add atomic feature_claim_next() with retry limit and exponential backoff
- Fix circular dependency check arguments in 4 locations
- Add AgentTracker class for parsing agent output and emitting updates
- Add browser isolation with --isolated flag for Playwright MCP
- Extend WebSocket protocol with agent_update messages and log attribution
- Add WSAgentUpdateMessage schema with agent states and mascot names
- Fix WSProgressMessage to include in_progress field

New UI Components:
- AgentMissionControl: Dashboard showing active agents with collapsible activity
- AgentCard: Individual agent status with avatar and thought bubble
- AgentAvatar: SVG mascots (Spark, Fizz, Octo, Hoot, Buzz) with animations
- ActivityFeed: Recent activity stream with stable keys (no flickering)
- CelebrationOverlay: Confetti animation with click/Escape dismiss
- DependencyGraph: Interactive node graph visualization with dagre layout
- DependencyBadge: Visual indicator for feature dependencies
- ViewToggle: Switch between Kanban and Graph views
- KeyboardShortcutsHelp: Help overlay accessible via ? key

UI/UX Improvements:
- Celebration queue system to handle rapid success messages
- Accessibility attributes on AgentAvatar (role, aria-label, aria-live)
- Collapsible Recent Activity section with persisted preference
- Agent count display in header
- Keyboard shortcut G to toggle Kanban/Graph view
- Real-time thought bubbles and state animations

Bug Fixes:
- Fix circular dependency validation (swapped source/target arguments)
- Add MAX_CLAIM_RETRIES=10 to prevent stack overflow under contention
- Fix THOUGHT_PATTERNS to match actual [Tool: name] format
- Fix ActivityFeed key prop to prevent re-renders on new items
- Add featureId/agentIndex to log messages for proper attribution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-17 12:59:42 +02:00
parent 91cc00a9d0
commit 85f6940a54
39 changed files with 4532 additions and 157 deletions

View File

@@ -0,0 +1,99 @@
import { MessageCircle } from 'lucide-react'
import { AgentAvatar } from './AgentAvatar'
import type { ActiveAgent } from '../lib/types'
interface AgentCardProps {
agent: ActiveAgent
}
// Get a friendly state description
function getStateText(state: ActiveAgent['state']): string {
switch (state) {
case 'idle':
return 'Waiting...'
case 'thinking':
return 'Thinking...'
case 'working':
return 'Coding...'
case 'testing':
return 'Testing...'
case 'success':
return 'Done!'
case 'error':
return 'Hit an issue'
case 'struggling':
return 'Retrying...'
default:
return 'Working...'
}
}
// Get state color
function getStateColor(state: ActiveAgent['state']): string {
switch (state) {
case 'success':
return 'text-neo-done'
case 'error':
case 'struggling':
return 'text-neo-danger'
case 'working':
case 'testing':
return 'text-neo-progress'
case 'thinking':
return 'text-neo-pending'
default:
return 'text-neo-text-secondary'
}
}
export function AgentCard({ agent }: AgentCardProps) {
const isActive = ['thinking', 'working', 'testing'].includes(agent.state)
return (
<div
className={`
neo-card p-3 min-w-[180px] max-w-[220px]
${isActive ? 'animate-pulse-neo' : ''}
transition-all duration-300
`}
>
{/* Header with avatar and name */}
<div className="flex items-center gap-2 mb-2">
<AgentAvatar name={agent.agentName} state={agent.state} size="sm" />
<div className="flex-1 min-w-0">
<div className="font-display font-bold text-sm truncate">
{agent.agentName}
</div>
<div className={`text-xs ${getStateColor(agent.state)}`}>
{getStateText(agent.state)}
</div>
</div>
</div>
{/* Feature info */}
<div className="mb-2">
<div className="text-xs text-neo-text-secondary mb-0.5">
Feature #{agent.featureId}
</div>
<div className="text-sm font-medium truncate" title={agent.featureName}>
{agent.featureName}
</div>
</div>
{/* Thought bubble */}
{agent.thought && (
<div className="relative mt-2 pt-2 border-t-2 border-neo-border/30">
<div className="flex items-start gap-1.5">
<MessageCircle size={14} className="text-neo-progress shrink-0 mt-0.5" />
<p
className="text-xs text-neo-text-secondary line-clamp-2 italic"
title={agent.thought}
>
{agent.thought}
</p>
</div>
</div>
)}
</div>
)
}