mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-02-05 16:33:08 +00:00
feat: add orchestrator observability to Mission Control
Add real-time visibility into the parallel orchestrator's decisions
and state in the Mission Control UI. The orchestrator now has its
own avatar ("Maestro") and displays capacity/queue information.
Backend changes (server/websocket.py):
- Add OrchestratorTracker class that parses orchestrator stdout
- Define regex patterns for key orchestrator events (spawn, complete, capacity)
- Track coding/testing agent counts, ready queue, blocked features
- Emit orchestrator_update WebSocket messages
- Reset tracker state when agent stops or crashes
Frontend changes:
- Add OrchestratorState, OrchestratorStatus, OrchestratorEvent types
- Add WSOrchestratorUpdateMessage to WSMessage union
- Handle orchestrator_update in useWebSocket hook
- Create OrchestratorAvatar component (Maestro - robot conductor)
- Create OrchestratorStatusCard with capacity badges and event ticker
- Update AgentMissionControl to show orchestrator above agent cards
- Add conducting/baton-tap CSS animations for Maestro
The orchestrator status card shows:
- Maestro avatar with state-based animations
- Current orchestrator state and message
- Coding agents, testing agents, ready queue badges
- Blocked features count (when > 0)
- Collapsible recent events list
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ import type {
|
||||
ActiveAgent,
|
||||
AgentMascot,
|
||||
AgentLogEntry,
|
||||
OrchestratorStatus,
|
||||
OrchestratorEvent,
|
||||
} from '../lib/types'
|
||||
|
||||
// Activity item for the feed
|
||||
@@ -48,6 +50,8 @@ interface WebSocketState {
|
||||
// Celebration queue to handle rapid successes without race conditions
|
||||
celebrationQueue: CelebrationTrigger[]
|
||||
celebration: CelebrationTrigger | null
|
||||
// Orchestrator state for Mission Control
|
||||
orchestratorStatus: OrchestratorStatus | null
|
||||
}
|
||||
|
||||
const MAX_LOGS = 100 // Keep last 100 log lines
|
||||
@@ -68,6 +72,7 @@ export function useProjectWebSocket(projectName: string | null) {
|
||||
agentLogs: new Map(),
|
||||
celebrationQueue: [],
|
||||
celebration: null,
|
||||
orchestratorStatus: null,
|
||||
})
|
||||
|
||||
const wsRef = useRef<WebSocket | null>(null)
|
||||
@@ -112,8 +117,12 @@ export function useProjectWebSocket(projectName: string | null) {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
agentStatus: message.status,
|
||||
// Clear active agents when process stops OR crashes to prevent stale UI
|
||||
...((message.status === 'stopped' || message.status === 'crashed') && { activeAgents: [], recentActivity: [] }),
|
||||
// Clear active agents and orchestrator status when process stops OR crashes to prevent stale UI
|
||||
...((message.status === 'stopped' || message.status === 'crashed') && {
|
||||
activeAgents: [],
|
||||
recentActivity: [],
|
||||
orchestratorStatus: null,
|
||||
}),
|
||||
}))
|
||||
break
|
||||
|
||||
@@ -261,6 +270,33 @@ export function useProjectWebSocket(projectName: string | null) {
|
||||
})
|
||||
break
|
||||
|
||||
case 'orchestrator_update':
|
||||
setState(prev => {
|
||||
const newEvent: OrchestratorEvent = {
|
||||
eventType: message.eventType,
|
||||
message: message.message,
|
||||
timestamp: message.timestamp,
|
||||
featureId: message.featureId,
|
||||
featureName: message.featureName,
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
orchestratorStatus: {
|
||||
state: message.state,
|
||||
message: message.message,
|
||||
codingAgents: message.codingAgents ?? prev.orchestratorStatus?.codingAgents ?? 0,
|
||||
testingAgents: message.testingAgents ?? prev.orchestratorStatus?.testingAgents ?? 0,
|
||||
maxConcurrency: message.maxConcurrency ?? prev.orchestratorStatus?.maxConcurrency ?? 3,
|
||||
readyCount: message.readyCount ?? prev.orchestratorStatus?.readyCount ?? 0,
|
||||
blockedCount: message.blockedCount ?? prev.orchestratorStatus?.blockedCount ?? 0,
|
||||
timestamp: message.timestamp,
|
||||
recentEvents: [newEvent, ...(prev.orchestratorStatus?.recentEvents ?? []).slice(0, 4)],
|
||||
},
|
||||
}
|
||||
})
|
||||
break
|
||||
|
||||
case 'dev_log':
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
@@ -346,6 +382,7 @@ export function useProjectWebSocket(projectName: string | null) {
|
||||
agentLogs: new Map(),
|
||||
celebrationQueue: [],
|
||||
celebration: null,
|
||||
orchestratorStatus: null,
|
||||
})
|
||||
|
||||
if (!projectName) {
|
||||
|
||||
Reference in New Issue
Block a user