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:
@@ -12,6 +12,7 @@ import type {
|
||||
FeatureUpdate,
|
||||
FeatureBulkCreate,
|
||||
FeatureBulkCreateResponse,
|
||||
DependencyGraph,
|
||||
AgentStatusResponse,
|
||||
AgentActionResponse,
|
||||
SetupStatus,
|
||||
@@ -141,6 +142,50 @@ export async function createFeaturesBulk(
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Dependency Graph API
|
||||
// ============================================================================
|
||||
|
||||
export async function getDependencyGraph(projectName: string): Promise<DependencyGraph> {
|
||||
return fetchJSON(`/projects/${encodeURIComponent(projectName)}/features/graph`)
|
||||
}
|
||||
|
||||
export async function addDependency(
|
||||
projectName: string,
|
||||
featureId: number,
|
||||
dependencyId: number
|
||||
): Promise<{ success: boolean; feature_id: number; dependencies: number[] }> {
|
||||
return fetchJSON(
|
||||
`/projects/${encodeURIComponent(projectName)}/features/${featureId}/dependencies/${dependencyId}`,
|
||||
{ method: 'POST' }
|
||||
)
|
||||
}
|
||||
|
||||
export async function removeDependency(
|
||||
projectName: string,
|
||||
featureId: number,
|
||||
dependencyId: number
|
||||
): Promise<{ success: boolean; feature_id: number; dependencies: number[] }> {
|
||||
return fetchJSON(
|
||||
`/projects/${encodeURIComponent(projectName)}/features/${featureId}/dependencies/${dependencyId}`,
|
||||
{ method: 'DELETE' }
|
||||
)
|
||||
}
|
||||
|
||||
export async function setDependencies(
|
||||
projectName: string,
|
||||
featureId: number,
|
||||
dependencyIds: number[]
|
||||
): Promise<{ success: boolean; feature_id: number; dependencies: number[] }> {
|
||||
return fetchJSON(
|
||||
`/projects/${encodeURIComponent(projectName)}/features/${featureId}/dependencies`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ dependency_ids: dependencyIds }),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Agent API
|
||||
// ============================================================================
|
||||
@@ -151,11 +196,19 @@ export async function getAgentStatus(projectName: string): Promise<AgentStatusRe
|
||||
|
||||
export async function startAgent(
|
||||
projectName: string,
|
||||
yoloMode: boolean = false
|
||||
options: {
|
||||
yoloMode?: boolean
|
||||
parallelMode?: boolean
|
||||
maxConcurrency?: number
|
||||
} = {}
|
||||
): Promise<AgentActionResponse> {
|
||||
return fetchJSON(`/projects/${encodeURIComponent(projectName)}/agent/start`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ yolo_mode: yoloMode }),
|
||||
body: JSON.stringify({
|
||||
yolo_mode: options.yoloMode ?? false,
|
||||
parallel_mode: options.parallelMode ?? false,
|
||||
max_concurrency: options.maxConcurrency,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,32 @@ export interface Feature {
|
||||
steps: string[]
|
||||
passes: boolean
|
||||
in_progress: boolean
|
||||
dependencies?: number[] // Optional for backwards compat
|
||||
blocked?: boolean // Computed by API
|
||||
blocking_dependencies?: number[] // Computed by API
|
||||
}
|
||||
|
||||
// Status type for graph nodes
|
||||
export type FeatureStatus = 'pending' | 'in_progress' | 'done' | 'blocked'
|
||||
|
||||
// Graph visualization types
|
||||
export interface GraphNode {
|
||||
id: number
|
||||
name: string
|
||||
category: string
|
||||
status: FeatureStatus
|
||||
priority: number
|
||||
dependencies: number[]
|
||||
}
|
||||
|
||||
export interface GraphEdge {
|
||||
source: number
|
||||
target: number
|
||||
}
|
||||
|
||||
export interface DependencyGraph {
|
||||
nodes: GraphNode[]
|
||||
edges: GraphEdge[]
|
||||
}
|
||||
|
||||
export interface FeatureListResponse {
|
||||
@@ -80,6 +106,7 @@ export interface FeatureCreate {
|
||||
description: string
|
||||
steps: string[]
|
||||
priority?: number
|
||||
dependencies?: number[]
|
||||
}
|
||||
|
||||
export interface FeatureUpdate {
|
||||
@@ -88,6 +115,7 @@ export interface FeatureUpdate {
|
||||
description?: string
|
||||
steps?: string[]
|
||||
priority?: number
|
||||
dependencies?: number[]
|
||||
}
|
||||
|
||||
// Agent types
|
||||
@@ -99,6 +127,8 @@ export interface AgentStatusResponse {
|
||||
started_at: string | null
|
||||
yolo_mode: boolean
|
||||
model: string | null // Model being used by running agent
|
||||
parallel_mode: boolean
|
||||
max_concurrency: number | null
|
||||
}
|
||||
|
||||
export interface AgentActionResponse {
|
||||
@@ -140,8 +170,26 @@ export interface TerminalInfo {
|
||||
created_at: string
|
||||
}
|
||||
|
||||
// Agent mascot names for multi-agent UI
|
||||
export const AGENT_MASCOTS = ['Spark', 'Fizz', 'Octo', 'Hoot', 'Buzz'] as const
|
||||
export type AgentMascot = typeof AGENT_MASCOTS[number]
|
||||
|
||||
// Agent state for Mission Control
|
||||
export type AgentState = 'idle' | 'thinking' | 'working' | 'testing' | 'success' | 'error' | 'struggling'
|
||||
|
||||
// Agent update from backend
|
||||
export interface ActiveAgent {
|
||||
agentIndex: number
|
||||
agentName: AgentMascot
|
||||
featureId: number
|
||||
featureName: string
|
||||
state: AgentState
|
||||
thought?: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
// WebSocket message types
|
||||
export type WSMessageType = 'progress' | 'feature_update' | 'log' | 'agent_status' | 'pong' | 'dev_log' | 'dev_server_status'
|
||||
export type WSMessageType = 'progress' | 'feature_update' | 'log' | 'agent_status' | 'pong' | 'dev_log' | 'dev_server_status' | 'agent_update'
|
||||
|
||||
export interface WSProgressMessage {
|
||||
type: 'progress'
|
||||
@@ -161,6 +209,20 @@ export interface WSLogMessage {
|
||||
type: 'log'
|
||||
line: string
|
||||
timestamp: string
|
||||
featureId?: number
|
||||
agentIndex?: number
|
||||
agentName?: AgentMascot
|
||||
}
|
||||
|
||||
export interface WSAgentUpdateMessage {
|
||||
type: 'agent_update'
|
||||
agentIndex: number
|
||||
agentName: AgentMascot
|
||||
featureId: number
|
||||
featureName: string
|
||||
state: AgentState
|
||||
thought?: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface WSAgentStatusMessage {
|
||||
@@ -189,6 +251,7 @@ export type WSMessage =
|
||||
| WSFeatureUpdateMessage
|
||||
| WSLogMessage
|
||||
| WSAgentStatusMessage
|
||||
| WSAgentUpdateMessage
|
||||
| WSPongMessage
|
||||
| WSDevLogMessage
|
||||
| WSDevServerStatusMessage
|
||||
|
||||
Reference in New Issue
Block a user