Recursive divide-conquer-merge agent orchestration engine. One objective in, structured execution tree out — with live graph UI, checker verification, and proposal-based file generation.
FastAPI backend + Next.js frontend.
- Recursive divide-route-execute workflow with complexity-adaptive depth
- Persona-based execution with markdown-backed profiles and persona chaining
- Cost-aware routing: complexity tiers map to configurable LLM models
- Token budget management with per-run and per-node limits
- Live run graph (dagre layout) and SSE event console
- Proposal-based file generation with review-and-apply workflow
- Checker self-heal:
pause(human-in-the-loop) andauto_retrymodes - Sandbox isolation: subprocess (dev) or Docker epicbox (production)
backend/— FastAPI API, orchestration runtime, state management, testsfrontend/— Next.js Mission Control UI, component/E2E testspersonas/— Markdown persona profiles discovered at runtimerun.sh— Linux/macOS launcher (subcommands:setup,full,backend,frontend,stub,menu)run.bat— Windows launcherdocker-compose.yml— PostgreSQL + backend + frontend (requires Dockerfiles)
# Linux/macOS
bash ./run.sh # interactive menu
bash ./run.sh stub # no LLM key needed — deterministic mode# Windows
.\run.batBackend:
cd backend
cp .env.example .env # configure LLM provider
uv sync
uv run uvicorn main:app --reloadFrontend:
cd frontend
npm ci
npm run devThe frontend expects the backend at http://127.0.0.1:8000 by default.
Set LLM_PROVIDER=stub in backend/.env for deterministic local development. The stub returns fixed responses — useful for testing the orchestration flow without API costs.
| Variable | Default | Description |
|---|---|---|
LLM_PROVIDER | bedrock | gemini, groq, bedrock, or stub |
GEMINI_API_KEY | — | Google Gemini API key |
GROQ_API_KEY | — | Groq API key |
AWS_REGION | us-east-1 | AWS region for Bedrock |
BEDROCK_MODEL_ID | anthropic.claude-sonnet-4-20250514-v1:0 | Bedrock model ARN |
LLM_MODEL | — | Override provider default model |
LLM_TEMPERATURE | 0.0 | Default temperature |
LLM_TIMEOUT_SECONDS | 120 | LLM call timeout |
LLM_MAX_RETRIES | 2 | Schema retry attempts |
DATABASE_URL | sqlite:///recursia.db | sqlite:///path or postgresql://... |
SANDBOX_ENABLED | true | Enable execution-based verification |
SANDBOX_BACKEND | local | local (subprocess) or docker (epicbox) |
BACKEND_CORS_ORIGINS | http://127.0.0.1:3000,http://localhost:3000 | Comma-separated allowed origins |
LLM_MODEL_TIER_FAST | — | Model for trivial tasks (complexity < 0.3) |
LLM_MODEL_TIER_STANDARD | — | Model for medium tasks |
LLM_MODEL_TIER_STRONG | — | Model for complex tasks (complexity > 0.7) |
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL | http://127.0.0.1:8000 | Backend URL |
| Method | Path | Description |
|---|---|---|
POST | /api/runs | Create a run (objective, config, base_persona_id) |
GET | /api/runs/{run_id} | Get run graph (nodes + edges) |
GET | /api/runs/{run_id}/result | Get run result with validation |
GET | /api/runs/{run_id}/events | SSE event stream (replay from Last-Event-Id) |
DELETE | /api/runs/{run_id}/nodes/{node_id} | Delete node and descendants |
POST | /api/runs/{run_id}/nodes/{node_id}/interventions | Apply human intervention |
| Method | Path | Description |
|---|---|---|
GET | /health | Liveness probe |
GET | /ready | Readiness gate (503 if provider unhealthy) |
GET | /system/config-summary | Non-secret config snapshot |
GET | /api/personas | List available personas |
{
"checker": {
"enabled": true,
"max_retries_per_node": 3,
"onCheck_fail": "auto_retry"
},
"max_depth": 8,
"max_children_per_node": 10,
"decomposition_candidates": 3,
"re_decompose_after": 2,
"complexity_threshold": 0.6,
"adaptive_depth": false,
"persona_chain": null,
"token_budget": {
"max_total_tokens": 500000,
"max_tokens_per_node": 50000,
"on_exhausted": "fail"
}
}adaptive_depth— use complexity estimator's suggested depth instead of fixed max_depthpersona_chain— array of persona IDs to run sequentially (each refines prior output)token_budget— per-run token cap with fail/warn on exhaustion
graph TD
UI["Mission Control<br/>Next.js + Zustand + React Flow + dagre"]
API["FastAPI routes<br/>runs · events · personas"]
ORCH["Orchestrator<br/>run lifecycle · terminal state"]
EXEC["RecursiveExecutor<br/>divide · execute · merge"]
DIV["DividerService<br/>LLM decomposition"]
WRK["Worker<br/>LLM work plan execution"]
MRG["MergerService<br/>LLM child merging"]
CHK["CheckerService<br/>sandbox execution + LLM fallback"]
CTX["ComplexityEstimator<br/>heuristic scoring · model tiers"]
UI -->|"REST + SSE"| API
API --> ORCH
ORCH --> EXEC
EXEC --> DIV
EXEC --> WRK
EXEC --> MRG
EXEC --> CHK
EXEC --> CTX
DIV -->|"children"| EXEC
CHK -->|"pass/fail"| EXEC
- RecursiveExecutor — orchestrates the divide-execute-merge loop per node. Handles depth limits, token budgets, persona chains, cost-aware routing, checker retries, and HITL blocking.
- DividerService — LLM-driven decomposition with multi-candidate generation and complexity scoring.
- LLMBaseCaseWorker — executes multi-step work plans with persona-aware LLM calls and sliding context.
- MergerService — merges child outputs with conflict detection.
- CheckerService — evaluates node output via sandbox execution or LLM fallback.
- ComplexityEstimator — heuristic scoring (no LLM) for adaptive depth and model tier selection.
- EventStreamService — SSE fanout with sequence-based replay.
Nodes follow a strict transition map defined in app/domain/policies.py:
stateDiagram-v2
[*] --> QUEUED
QUEUED --> RUNNING
RUNNING --> WAITING_CHECK
RUNNING --> ERROR
WAITING_CHECK --> COMPLETED
WAITING_CHECK --> FAILED_CHECK
WAITING_CHECK --> ERROR
FAILED_CHECK --> RUNNING : retry with feedback
FAILED_CHECK --> BLOCKED_HUMAN : 3 consecutive fails
FAILED_CHECK --> ERROR
BLOCKED_HUMAN --> RUNNING : human intervention
BLOCKED_HUMAN --> ERROR
COMPLETED --> [*]
ERROR --> [*]
Transitions are validated by ensure_*_transition() functions. Invalid transitions raise PolicyViolation.
Services are wired in app/__init__.py via _build_services() and stored in the _active dict in app/api/runs.py. Tests use set_runs_services() / reset_runs_services() for DI.
# Backend (132 tests)cd backend && uv run pytest -q
# Frontend component tests (20 tests)cd frontend && npm run test:components
# Frontend typecheckcd frontend && npm run typecheck
# Frontend buildcd frontend && npm run build
# E2E tests (Playwright)cd frontend && npm run test:e2eGitHub Actions runs on push/PR across Windows, macOS, Linux:
- Backend:
ruff check+pytest -q - Frontend:
typecheck+test:components+npm run build - Launcher syntax:
bash -n run.sh
- Backend: Python 3.11, FastAPI, litellm, Pydantic, tenacity, uv
- Frontend: Next.js 15, React 18, Zustand, React Flow, dagre, Vitest, Playwright
- LLM providers: AWS Bedrock, Google Gemini, Groq, or stub (deterministic)
Apache License 2.0