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:
arkon
2026-03-21 07:20:18 +01:00
co-authored by Claude Opus 4.6
parent 497ca4891a
commit 61b5ec095c
27 changed files with 4718 additions and 1 deletions
+219
View File
@@ -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;
}