mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-01-31 14:43:35 +00:00
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:
@@ -80,6 +80,7 @@ class FeatureBase(BaseModel):
|
||||
name: str
|
||||
description: str
|
||||
steps: list[str]
|
||||
dependencies: list[int] = Field(default_factory=list) # Optional dependencies
|
||||
|
||||
|
||||
class FeatureCreate(FeatureBase):
|
||||
@@ -94,6 +95,7 @@ class FeatureUpdate(BaseModel):
|
||||
description: str | None = None
|
||||
steps: list[str] | None = None
|
||||
priority: int | None = None
|
||||
dependencies: list[int] | None = None # Optional - can update dependencies
|
||||
|
||||
|
||||
class FeatureResponse(FeatureBase):
|
||||
@@ -102,6 +104,8 @@ class FeatureResponse(FeatureBase):
|
||||
priority: int
|
||||
passes: bool
|
||||
in_progress: bool
|
||||
blocked: bool = False # Computed: has unmet dependencies
|
||||
blocking_dependencies: list[int] = Field(default_factory=list) # Computed
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
@@ -126,6 +130,37 @@ class FeatureBulkCreateResponse(BaseModel):
|
||||
features: list[FeatureResponse]
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Dependency Graph Schemas
|
||||
# ============================================================================
|
||||
|
||||
class DependencyGraphNode(BaseModel):
|
||||
"""Minimal node for graph visualization (no description exposed for security)."""
|
||||
id: int
|
||||
name: str
|
||||
category: str
|
||||
status: Literal["pending", "in_progress", "done", "blocked"]
|
||||
priority: int
|
||||
dependencies: list[int]
|
||||
|
||||
|
||||
class DependencyGraphEdge(BaseModel):
|
||||
"""Edge in the dependency graph."""
|
||||
source: int
|
||||
target: int
|
||||
|
||||
|
||||
class DependencyGraphResponse(BaseModel):
|
||||
"""Response for dependency graph visualization."""
|
||||
nodes: list[DependencyGraphNode]
|
||||
edges: list[DependencyGraphEdge]
|
||||
|
||||
|
||||
class DependencyUpdate(BaseModel):
|
||||
"""Request schema for updating a feature's dependencies."""
|
||||
dependency_ids: list[int] = Field(..., max_length=20) # Security: limit
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Agent Schemas
|
||||
# ============================================================================
|
||||
@@ -134,6 +169,8 @@ class AgentStartRequest(BaseModel):
|
||||
"""Request schema for starting the agent."""
|
||||
yolo_mode: bool | None = None # None means use global settings
|
||||
model: str | None = None # None means use global settings
|
||||
parallel_mode: bool | None = None # Enable parallel execution
|
||||
max_concurrency: int | None = None # Max concurrent agents (1-5)
|
||||
|
||||
@field_validator('model')
|
||||
@classmethod
|
||||
@@ -143,6 +180,14 @@ class AgentStartRequest(BaseModel):
|
||||
raise ValueError(f"Invalid model. Must be one of: {VALID_MODELS}")
|
||||
return v
|
||||
|
||||
@field_validator('max_concurrency')
|
||||
@classmethod
|
||||
def validate_concurrency(cls, v: int | None) -> int | None:
|
||||
"""Validate max_concurrency is between 1 and 5."""
|
||||
if v is not None and (v < 1 or v > 5):
|
||||
raise ValueError("max_concurrency must be between 1 and 5")
|
||||
return v
|
||||
|
||||
|
||||
class AgentStatus(BaseModel):
|
||||
"""Current agent status."""
|
||||
@@ -151,6 +196,8 @@ class AgentStatus(BaseModel):
|
||||
started_at: datetime | None = None
|
||||
yolo_mode: bool = False
|
||||
model: str | None = None # Model being used by running agent
|
||||
parallel_mode: bool = False
|
||||
max_concurrency: int | None = None
|
||||
|
||||
|
||||
class AgentActionResponse(BaseModel):
|
||||
@@ -180,6 +227,7 @@ class WSProgressMessage(BaseModel):
|
||||
"""WebSocket message for progress updates."""
|
||||
type: Literal["progress"] = "progress"
|
||||
passing: int
|
||||
in_progress: int
|
||||
total: int
|
||||
percentage: float
|
||||
|
||||
@@ -196,6 +244,8 @@ class WSLogMessage(BaseModel):
|
||||
type: Literal["log"] = "log"
|
||||
line: str
|
||||
timestamp: datetime
|
||||
featureId: int | None = None
|
||||
agentIndex: int | None = None
|
||||
|
||||
|
||||
class WSAgentStatusMessage(BaseModel):
|
||||
@@ -204,6 +254,25 @@ class WSAgentStatusMessage(BaseModel):
|
||||
status: str
|
||||
|
||||
|
||||
# Agent state for multi-agent tracking
|
||||
AgentState = Literal["idle", "thinking", "working", "testing", "success", "error", "struggling"]
|
||||
|
||||
# Agent mascot names assigned by index
|
||||
AGENT_MASCOTS = ["Spark", "Fizz", "Octo", "Hoot", "Buzz"]
|
||||
|
||||
|
||||
class WSAgentUpdateMessage(BaseModel):
|
||||
"""WebSocket message for multi-agent status updates."""
|
||||
type: Literal["agent_update"] = "agent_update"
|
||||
agentIndex: int
|
||||
agentName: str # One of AGENT_MASCOTS
|
||||
featureId: int
|
||||
featureName: str
|
||||
state: AgentState
|
||||
thought: str | None = None
|
||||
timestamp: datetime
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Spec Chat Schemas
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user