feat: add per-agent logging UI and fix stuck agent issues

Changes:
- Add per-agent log viewer with copy-to-clipboard functionality
  - New AgentLogEntry type for structured log entries
  - Logs stored per-agent in WebSocket state (up to 500 entries)
  - Log modal rendered via React Portal to avoid overflow issues
  - Click log icon on agent card to view full activity history

- Fix agents getting stuck in "failed" state
  - Wrap client context manager in try/except (agent.py)
  - Remove failed agents from UI on error state (useWebSocket.ts)
  - Handle permanently failed features in get_all_complete()

- Add friendlier agent state labels
  - "Hit an issue" → "Trying plan B..."
  - "Retrying..." → "Being persistent..."
  - Softer colors (yellow/orange instead of red)

- Add scheduling scores for smarter feature ordering
  - compute_scheduling_scores() in dependency_resolver.py
  - Features that unblock others get higher priority

- Update CLAUDE.md with parallel mode documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-17 14:11:24 +02:00
parent 85f6940a54
commit bf3a6b0b73
10 changed files with 387 additions and 49 deletions

View File

@@ -1,8 +1,8 @@
import { Rocket, ChevronDown, ChevronUp, Activity } from 'lucide-react'
import { useState } from 'react'
import { AgentCard } from './AgentCard'
import { AgentCard, AgentLogModal } from './AgentCard'
import { ActivityFeed } from './ActivityFeed'
import type { ActiveAgent } from '../lib/types'
import type { ActiveAgent, AgentLogEntry } from '../lib/types'
const ACTIVITY_COLLAPSED_KEY = 'autocoder-activity-collapsed'
@@ -15,12 +15,14 @@ interface AgentMissionControlProps {
featureId: number
}>
isExpanded?: boolean
getAgentLogs?: (agentIndex: number) => AgentLogEntry[]
}
export function AgentMissionControl({
agents,
recentActivity,
isExpanded: defaultExpanded = true,
getAgentLogs,
}: AgentMissionControlProps) {
const [isExpanded, setIsExpanded] = useState(defaultExpanded)
const [activityCollapsed, setActivityCollapsed] = useState(() => {
@@ -30,6 +32,8 @@ export function AgentMissionControl({
return false
}
})
// State for log modal
const [selectedAgentForLogs, setSelectedAgentForLogs] = useState<ActiveAgent | null>(null)
const toggleActivityCollapsed = () => {
const newValue = !activityCollapsed
@@ -80,7 +84,16 @@ export function AgentMissionControl({
{/* Agent Cards Row */}
<div className="flex gap-4 overflow-x-auto pb-4 scrollbar-thin">
{agents.map((agent) => (
<AgentCard key={`agent-${agent.agentIndex}`} agent={agent} />
<AgentCard
key={`agent-${agent.agentIndex}`}
agent={agent}
onShowLogs={(agentIndex) => {
const agentToShow = agents.find(a => a.agentIndex === agentIndex)
if (agentToShow) {
setSelectedAgentForLogs(agentToShow)
}
}}
/>
))}
</div>
@@ -116,6 +129,15 @@ export function AgentMissionControl({
)}
</div>
</div>
{/* Log Modal */}
{selectedAgentForLogs && getAgentLogs && (
<AgentLogModal
agent={selectedAgentForLogs}
logs={getAgentLogs(selectedAgentForLogs.agentIndex)}
onClose={() => setSelectedAgentForLogs(null)}
/>
)}
</div>
)
}