mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 08:29:42 +02:00
feat: add Orchestrator Loop — phased plan execution with team agents
Adds a new autonomous loop that accepts high-level goals, generates phased execution plans via AI, and executes them step-by-step with verification gates between phases. Core components: - OrchestratorLoop: state machine (idle→planning→approval→executing→verifying→completed) - OrchestratorPlanner: plan generation via PlanOrchestrator, Kahn's algorithm phase grouping - OrchestratorVerifier: phase verification (strict/moderate/lenient modes) - Prompt templates for phase execution, team delegation, verification, replanning API (10 endpoints): - POST start/approve/reject/pause/resume/stop - GET status/plan - POST phase/:id/skip, phase/:id/retry Frontend: orchestrator-panel.js with SSE-driven state, phase progress, task tracking Tests: 22 tests (18 route + 4 unit), all passing. Typecheck/lint/format clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7540,3 +7540,222 @@ kbd {
|
||||
color: #495057;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════
|
||||
Orchestrator Panel
|
||||
═══════════════════════════════════════════════════════════════ */
|
||||
|
||||
.orchestrator-panel {
|
||||
background: var(--bg-card);
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.orchestrator-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.orchestrator-title {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.orchestrator-state-badge {
|
||||
display: inline-block;
|
||||
padding: 1px 8px;
|
||||
border-radius: 10px;
|
||||
color: #fff;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.orchestrator-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.orchestrator-close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
padding: 0 4px;
|
||||
line-height: 1;
|
||||
}
|
||||
.orchestrator-close-btn:hover { color: var(--text-primary); }
|
||||
|
||||
.orch-btn {
|
||||
padding: 2px 8px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.orch-btn:hover { background: var(--bg-hover); }
|
||||
.orch-btn-primary { background: #3b82f6; color: #fff; border-color: #3b82f6; }
|
||||
.orch-btn-primary:hover { background: #2563eb; }
|
||||
.orch-btn-warn { background: #f59e0b; color: #000; border-color: #f59e0b; }
|
||||
.orch-btn-warn:hover { background: #d97706; }
|
||||
.orch-btn-danger { background: #ef4444; color: #fff; border-color: #ef4444; }
|
||||
.orch-btn-danger:hover { background: #dc2626; }
|
||||
|
||||
.orchestrator-body {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.orch-empty {
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.orch-planning {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #f59e0b;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.orch-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid #f59e0b33;
|
||||
border-top-color: #f59e0b;
|
||||
border-radius: 50%;
|
||||
animation: orch-spin 0.8s linear infinite;
|
||||
}
|
||||
@keyframes orch-spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.orch-goal {
|
||||
margin-bottom: 6px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.orch-progress-bar {
|
||||
position: relative;
|
||||
height: 18px;
|
||||
background: var(--bg-input);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.orch-progress-fill {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #3b82f6, #22c55e);
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.orch-progress-bar span {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.65rem;
|
||||
font-weight: 600;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orch-phases {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.orch-phase {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.orch-phase-active { border-color: #3b82f6; }
|
||||
.orch-phase-passed { opacity: 0.7; }
|
||||
.orch-phase-skipped { opacity: 0.5; }
|
||||
.orch-phase-failed { border-color: #ef4444; }
|
||||
|
||||
.orch-phase-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.orch-phase-icon { width: 14px; text-align: center; }
|
||||
.orch-phase-name { flex: 1; color: var(--text-primary); font-weight: 500; }
|
||||
.orch-phase-tasks { color: var(--text-secondary); font-size: 0.65rem; }
|
||||
.orch-phase-duration { color: var(--text-secondary); font-size: 0.65rem; }
|
||||
.orch-phase-actions { display: flex; gap: 2px; }
|
||||
|
||||
.orch-phase-btn {
|
||||
padding: 1px 6px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
.orch-phase-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
|
||||
|
||||
.orch-phase-tasks-list {
|
||||
padding: 4px 8px 4px 26px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.orch-task {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
padding: 2px 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.orch-task-icon { width: 12px; text-align: center; flex-shrink: 0; margin-top: 1px; }
|
||||
.orch-task-prompt { color: var(--text-primary); word-break: break-word; }
|
||||
.orch-task-error { color: #ef4444; font-size: 0.6rem; margin-left: auto; }
|
||||
.orch-task-completed .orch-task-prompt { color: var(--text-secondary); }
|
||||
.orch-task-failed .orch-task-icon { color: #ef4444; }
|
||||
|
||||
.orch-verify-pass { color: #22c55e; padding: 2px 8px; font-size: 0.65rem; }
|
||||
.orch-verify-fail { color: #ef4444; padding: 2px 8px; font-size: 0.65rem; }
|
||||
|
||||
.orch-stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
padding: 6px 0;
|
||||
margin-top: 6px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
.orch-error {
|
||||
color: #ef4444;
|
||||
padding: 4px 0;
|
||||
margin-top: 4px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user