feat: add Agent Evolution Dashboard

- Create agent-evolution/ directory with standalone dashboard
- Add interactive HTML dashboard with agent/model matrix
- Add heatmap view for agent-model compatibility scores
- Add recommendations tab with optimization suggestions
- Add Gitea integration preparation (history timeline)
- Add Docker configuration for deployment
- Add build scripts for standalone HTML generation
- Add sync scripts for agent data synchronization
- Add milestone and issues documentation
- Add skills and rules for evolution sync
- Update AGENTS.md with dashboard documentation
- Update package.json with evolution scripts

Features:
- 28 agents with model assignments and fit scores
- 8 models with benchmarks (SWE-bench, RULER, Terminal)
- 11 recommendations for model optimization
- History timeline with agent changes
- Interactive modal windows for model details
- Filter and search functionality
- Russian language interface
- Works offline (file://) with embedded data

Docker:
- Dockerfile for standalone deployment
- docker-compose.evolution.yml
- docker-run.sh/docker-run.bat scripts

NPM scripts:
- sync:evolution - sync and build dashboard
- evolution:open - open in browser
- evolution:dashboard - start dev server

Status: PAUSED - foundation complete, Gitea integration pending
This commit is contained in:
¨NW¨
2026-04-05 19:58:59 +01:00
parent b899119d21
commit 15a7b4b7a4
17 changed files with 4934 additions and 2 deletions

View File

@@ -5,7 +5,7 @@
"paths": [".kilo/skills"]
},
"model": "qwen/qwen3.6-plus:free",
"small_model": "llama-3.1-8b-instant",
"small_model": "kilo-auto/free",
"default_agent": "orchestrator",
"agent": {
"orchestrator": {

View File

@@ -0,0 +1,283 @@
# Evolutionary Sync Rules
Rules for synchronizing agent evolution data automatically.
## When to Sync
### Automatic Sync Triggers
1. **After each completed issue**
- When agent completes task and posts Gitea comment
- Extract performance metrics from comment
2. **On model change**
- When agent model is updated in kilo.jsonc
- When capability-index.yaml is modified
3. **On agent file change**
- When .kilo/agents/*.md files are modified
- On create/delete of agent files
4. **On prompt update**
- When agent receives prompt optimization
- Track optimization improvements
### Manual Sync Triggers
```bash
# Sync from all sources
bun run sync:evolution
# Sync specific source
bun run agent-evolution/scripts/sync-agent-history.ts --source git
bun run agent-evolution/scripts/sync-agent-history.ts --source gitea
# Open dashboard
bun run evolution:dashboard
bun run evolution:open
```
## Data Flow
```
┌─────────────────────────────────────────────────────────────┐
│ Data Sources │
├─────────────────────────────────────────────────────────────┤
│ .kilo/agents/*.md ──► Parse frontmatter, model │
│ .kilo/kilo.jsonc ──► Model assignments │
│ .kilo/capability-index.yaml ──► Capabilities, routing │
│ Git History ──► Change timeline │
│ Gitea Issue Comments ──► Performance scores │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ agent-evolution/data/ │
│ agent-versions.json │
├─────────────────────────────────────────────────────────────┤
│ { │
│ "agents": { │
│ "lead-developer": { │
│ "current": { model, provider, fit_score, ... }, │
│ "history": [ { model_change, ... } ], │
│ "performance_log": [ { score, issue, ... } ] │
│ } │
│ } │
│ } │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ agent-evolution/index.html │
│ Interactive Dashboard │
├─────────────────────────────────────────────────────────────┤
│ • Overview - Stats, recent changes, recommendations │
│ • All Agents - Filterable cards with history │
│ • Timeline - Full evolution history │
│ • Recommendations - Export, priority-based view │
│ • Model Matrix - Agent × Model mapping │
└─────────────────────────────────────────────────────────────┘
```
## Recording Changes
### From Gitea Comments
Agent comments should follow this format:
```markdown
## ✅ agent-name completed
**Score**: X/10
**Duration**: X.Xh
**Files**: file1.ts, file2.ts
### Notes
- Description of work done
- Key decisions made
- Issues encountered
```
Extraction:
- `agent-name` → agent name
- `Score` → performance score (1-10)
- `Duration` → execution time
- `Files` → files modified
### From Git Commits
Commit message patterns:
- `feat: add flutter-developer agent` → agent_created
- `fix: update security-auditor model to nemotron-3-super` → model_change
- `docs: update lead-developer prompt` → prompt_change
## Gitea Webhook Setup
1. **Create webhook in Gitea**
- Target URL: `http://localhost:3000/api/evolution/webhook`
- Events: `issue_comment`, `issues`
2. **Webhook payload handling**
```typescript
// In agent-evolution/scripts/gitea-webhook.ts
app.post('/api/evolution/webhook', async (req, res) => {
const { action, issue, comment } = req.body;
if (action === 'created' && comment?.body.includes('## ✅')) {
await recordAgentPerformance(issue, comment);
}
res.json({ success: true });
});
```
## Performance Metrics
### Tracked Metrics
For each agent execution:
| Metric | Source | Format |
|--------|--------|--------|
| Score | Gitea comment | X/10 |
| Duration | Agent timing | milliseconds |
| Success | Exit status | boolean |
| Files | Gitea comment | count |
| Issue | Context | number |
### Aggregated Metrics
| Metric | Calculation | Use |
|--------|-------------|-----|
| Average Score | `sum(scores) / count` | Agent effectiveness |
| Success Rate | `successes / total * 100` | Reliability |
| Average Duration | `sum(durations) / count` | Speed |
| Files per Task | `sum(files) / count` | Scope |
## Recommendations Generation
### Priority Levels
| Priority | Criteria | Action |
|----------|----------|--------|
| Critical | Fit score < 70 | Immediate update |
| High | Model unavailable | Switch to fallback |
| Medium | Better model available | Consider upgrade |
| Low | Optimization possible | Optional improvement |
### Example Recommendation
```json
{
"agent": "requirement-refiner",
"recommendations": [{
"target": "ollama-cloud/nemotron-3-super",
"reason": "+22% quality, 1M context for specifications",
"priority": "critical"
}]
}
```
## Evolution Rules
### When Model Change is Recorded
1. **Detect change**
- Compare current.model with previous value
- Extract reason from commit message
2. **Record in history**
```json
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/gpt-oss:120b",
"to": "ollama-cloud/nemotron-3-super",
"reason": "Better reasoning for security analysis"
}
```
3. **Update current**
- Set current.model to new value
- Update provider if changed
- Recalculate fit score
### When Performance Drops
1. **Detect pattern**
- Last 5 scores average < 7
- Success rate < 80%
2. **Generate recommendation**
- Suggest model upgrade
- Trigger prompt-optimizer
3. **Notify via Gitea comment**
- Post to related issue
- Include improvement suggestions
## Integration in Pipeline
Add to post-pipeline:
```yaml
# .kilo/commands/pipeline.md
post_steps:
- name: sync_evolution
run: bun run sync:evolution
- name: check_recommendations
run: bun run agent-evolution/scripts/check-recommendations.ts
```
## Dashboard Access
```bash
# Start local server
bun run evolution:dashboard
# Open in browser
bun run evolution:open
# or visit http://localhost:3001
```
## API Endpoints (Future)
```typescript
// GET /api/evolution/agents
// Returns all agents with current state
// GET /api/evolution/agents/:name/history
// Returns agent history
// GET /api/evolution/recommendations
// Returns pending recommendations
// POST /api/evolution/agents/:name/apply
// Apply recommendation
// POST /api/evolution/sync
// Trigger manual sync
```
## Best Practices
1. **Sync after every pipeline run**
- Captures model changes
- Records performance
2. **Review dashboard weekly**
- Check pending recommendations
- Apply critical updates
3. **Track before/after metrics**
- When applying changes
- Compare performance
4. **Keep history clean**
- Deduplicate entries
- Merge related changes
5. **Use consistent naming**
- Agent names match file names
- Model IDs match capability-index.yaml

View File

@@ -0,0 +1,275 @@
# Evolution Sync Skill
Synchronizes agent evolution data from multiple sources.
## Purpose
Keeps the agent evolution dashboard up-to-date by:
1. Parsing git history for agent changes
2. Extracting current models from kilo.jsonc and capability-index.yaml
3. Recording performance metrics from Gitea issue comments
4. Tracking model and prompt changes over time
## Usage
```bash
# Sync from all sources
bun run agent-evolution/scripts/sync-agent-history.ts
# Sync specific source
bun run agent-evolution/scripts/sync-agent-history.ts --source git
bun run agent-evolution/scripts/sync-agent-history.ts --source gitea
```
## Integration Points
### 1. Git History
Parses commit messages for agent-related changes:
```bash
git log --all --oneline -- ".kilo/agents/"
```
Detects patterns like:
- `feat: add flutter-developer agent`
- `fix: update security-auditor model`
- `docs: update lead-developer prompt`
### 2. Configuration Files
**kilo.jsonc** - Primary model assignments:
```json
{
"agent": {
"lead-developer": {
"model": "ollama-cloud/qwen3-coder:480b"
}
}
}
```
**capability-index.yaml** - Capability mappings:
```yaml
agents:
lead-developer:
model: ollama-cloud/qwen3-coder:480b
capabilities: [code_writing, refactoring]
```
### 3. Gitea Integration
Extracts performance data from issue comments:
```typescript
// Comment format
// ## ✅ lead-developer completed
// **Score**: 8/10
// **Duration**: 1.2h
// **Files**: src/auth.ts, src/user.ts
```
## Function Reference
### syncEvolutionData()
Main sync function:
```typescript
async function syncEvolutionData(): Promise<void> {
// 1. Load agent files
const agentFiles = loadAgentFiles();
// 2. Load capability index
const capabilityIndex = loadCapabilityIndex();
// 3. Load kilo config
const kiloConfig = loadKiloConfig();
// 4. Get git history
const gitHistory = await getGitHistory();
// 5. Merge all sources
const merged = mergeConfigs(agentFiles, capabilityIndex, kiloConfig);
// 6. Update evolution data
updateEvolutionData(merged, gitHistory);
}
```
### recordAgentChange()
Records a model or prompt change:
```typescript
interface AgentChange {
agent: string;
type: 'model_change' | 'prompt_change' | 'capability_change';
from: string | null;
to: string;
reason: string;
issue_number?: number;
}
function recordAgentChange(change: AgentChange): void {
const evolution = loadEvolutionData();
if (!evolution.agents[change.agent]) {
evolution.agents[change.agent] = {
current: { model: change.to, ... },
history: [],
performance_log: []
};
}
// Add to history
evolution.agents[change.agent].history.push({
date: new Date().toISOString(),
commit: 'manual',
type: change.type,
from: change.from,
to: change.to,
reason: change.reason,
source: 'gitea'
});
saveEvolutionData(evolution);
}
```
### recordPerformance()
Records agent performance from issue:
```typescript
interface AgentPerformance {
agent: string;
issue: number;
score: number;
duration_ms: number;
success: boolean;
}
function recordPerformance(perf: AgentPerformance): void {
const evolution = loadEvolutionData();
if (!evolution.agents[perf.agent]) return;
evolution.agents[perf.agent].performance_log.push({
date: new Date().toISOString(),
issue: perf.issue,
score: perf.score,
duration_ms: perf.duration_ms,
success: perf.success
});
saveEvolutionData(evolution);
}
```
## Pipeline Integration
Add to `.kilo/commands/pipeline.md`:
```yaml
post_pipeline:
- name: sync_evolution
description: Sync agent evolution data after pipeline run
command: bun run agent-evolution/scripts/sync-agent-history.ts
```
## Gitea Webhook Handler
```typescript
// Parse agent completion comment
app.post('/api/evolution/webhook', async (req, res) => {
const { issue, comment } = req.body;
// Check for agent completion marker
const agentMatch = comment.match(/## ✅ (\w+-?\w*) completed/);
const scoreMatch = comment.match(/\*\*Score\*\*: (\d+)\/10/);
if (agentMatch && scoreMatch) {
await recordPerformance({
agent: agentMatch[1],
issue: issue.number,
score: parseInt(scoreMatch[1]),
duration_ms: 0, // Parse from duration
success: true
});
}
// Check for model change
const modelMatch = comment.match(/Model changed: (\S+) → (\S+)/);
if (modelMatch) {
await recordAgentChange({
agent: agentMatch[1],
type: 'model_change',
from: modelMatch[1],
to: modelMatch[2],
reason: 'Manual update',
issue_number: issue.number
});
}
});
```
## Files Structure
```
agent-evolution/
├── data/
│ ├── agent-versions.json # Current state + history
│ └── agent-versions.schema.json # JSON schema
├── scripts/
│ ├── sync-agent-history.ts # Main sync script
│ ├── parse-git-history.ts # Git parser
│ └── gitea-webhook.ts # Webhook handler
└── index.html # Dashboard UI
```
## Dashboard Features
1. **Overview Tab**
- Total agents, with history, pending recommendations
- Recent changes timeline
- Critical recommendations
2. **All Agents Tab**
- Filterable by category
- Searchable
- Shows model, fit score, capabilities
3. **Timeline Tab**
- Full evolution history
- Model changes
- Prompt changes
4. **Recommendations Tab**
- Export to JSON
- Priority-based sorting
- One-click apply
5. **Model Matrix Tab**
- Agent × Model mapping
- Fit scores
- Provider distribution
## Best Practices
1. **Run sync after each pipeline**
- Ensures history is up-to-date
- Captures model changes
2. **Record performance from every issue**
- Track agent effectiveness
- Identify improvement patterns
3. **Apply recommendations systematically**
- Use priority: critical → high → medium
- Track before/after performance
4. **Monitor evolution trends**
- Which agents change most frequently
- Which models perform best
- Category-specific optimizations

View File

@@ -225,6 +225,65 @@ const runner = await createPipelineRunner({
await runner.run({ issueNumber: 42 })
```
## Agent Evolution Dashboard
Track agent model changes, performance, and recommendations in real-time.
### Access
```bash
# Sync agent data
bun run sync:evolution
# Open dashboard
bun run evolution:dashboard
bun run evolution:open
# or visit http://localhost:3001
```
### Dashboard Tabs
| Tab | Description |
|-----|-------------|
| **Overview** | Stats, recent changes, pending recommendations |
| **All Agents** | Filterable agent cards with history |
| **Timeline** | Full evolution history |
| **Recommendations** | Priority-based model suggestions |
| **Model Matrix** | Agent × Model mapping with fit scores |
### Data Sources
| Source | What it tracks |
|--------|----------------|
| `.kilo/agents/*.md` | Model, description, capabilities |
| `.kilo/kilo.jsonc` | Model assignments |
| `.kilo/capability-index.yaml` | Capability routing |
| Git History | Model and prompt changes |
| Gitea Comments | Performance scores |
### Evolution Data Structure
```json
{
"agents": {
"lead-developer": {
"current": { "model": "qwen3-coder:480b", "fit_score": 92 },
"history": [{ "type": "model_change", "from": "deepseek", "to": "qwen3" }],
"performance_log": [{ "issue": 42, "score": 8, "success": true }]
}
}
}
```
### Recommendations Priority
| Priority | When | Example |
|----------|------|---------|
| **Critical** | Fit score < 70 | Immediate model change required |
| **High** | Model unavailable | Switch to fallback |
| **Medium** | Better model available | Consider upgrade |
| **Low** | Optimization possible | Optional improvement |
## Code Style
- Use TypeScript for new files

View File

@@ -0,0 +1,30 @@
# Agent Evolution Dashboard Dockerfile
# Standalone version - works from file:// or HTTP
# Build stage - run sync to generate standalone HTML
FROM oven/bun:1 AS builder
WORKDIR /build
# Copy config files for sync
COPY .kilo/agents/*.md ./.kilo/agents/
COPY .kilo/capability-index.yaml ./.kilo/
COPY .kilo/kilo.jsonc ./.kilo/
COPY agent-evolution/ ./agent-evolution/
# Run sync to generate standalone HTML with embedded data
RUN bun agent-evolution/scripts/sync-agent-history.ts || true
# Production stage - Python HTTP server
FROM python:3.12-alpine AS production
WORKDIR /app
# Copy standalone HTML (embedded data)
COPY --from=builder /build/agent-evolution/index.standalone.html ./index.html
# Expose port
EXPOSE 3001
# Simple HTTP server (no CORS issues)
CMD ["python3", "-m", "http.server", "3001"]

View File

@@ -0,0 +1,182 @@
# Agent Evolution Dashboard - Milestone & Issues
## Milestone: Agent Evolution Dashboard
**Title:** Agent Evolution Dashboard
**Description:** Интерактивная панель для отслеживания эволюции агентной системы APAW с интеграцией Gitea
**Due Date:** 2026-04-19 (2 недели)
**State:** Open
---
## Issues
### Issue 1: Рефакторинг из архива в root-директорию
**Title:** Рефакторинг: перенести agent model research из archive в agent-evolution
**Labels:** `refactor`, `high-priority`
**Milestone:** Agent Evolution Dashboard
**Описание:**
Файл `archive/apaw_agent_model_research_v3.html` содержит ценную информацию о моделях и рекомендациях. Необходимо:
1. ✅ Создать директорию `agent-evolution/` в корне проекта
2. ✅ Создать `agent-evolution/index.standalone.html` с интегрированными данными
3. ✅ Создать `agent-evolution/data/agent-versions.json` с актуальными данными
4. ✅ Создать `agent-evolution/scripts/build-standalone.cjs` для генерации
5. 🔄 Удалить `archive/apaw_agent_model_research_v3.html` после переноса данных
**Критерии приёмки:**
- [ ] Все данные из архива интегрированы
- [ ] Дашборд работает автономно (file://)
- [ ] Данные актуальны на момент коммита
---
### Issue 2: Интеграция с Gitea для истории изменений
**Title:** Интеграция Agent Evolution с Gitea API
**Labels:** `enhancement`, `integration`, `high-priority`
**Milestone:** Agent Evolution Dashboard
**Описание:**
Требуется интегрировать дашборд с Gitea для:
1. Получения истории изменений моделей из issue comments
2. Парсинга комментариев агентов (формат `## ✅ agent-name completed`)
3. Извлечения метрик производительности (Score, Duration, Files)
4. Отображения реальной истории в дашборде
**Требования:**
- API endpoint `/api/evolution/history` для получения истории
- Webhook для автоматического обновления при новых комментариях
- Кэширование данных локально
- Fallback на локальные данные при недоступности Gitea
**Критерии приёмки:**
- [ ] История загружается из Gitea при наличии API
- [ ] Fallback на локальные данные
- [ ] Webhook обрабатывает `issue_comment` события
- [ ] Данные обновляются в реальном времени
---
### Issue 3: Синхронизация с capability-index.yaml и kilo.jsonc
**Title:** Автоматическая синхронизация эволюции агентов
**Labels:** `automation`, `sync`, `medium-priority`
**Milestone:** Agent Evolution Dashboard
**Описание:**
Создать автоматическую синхронизацию данных эволюции из:
1. `.kilo/agents/*.md` - frontmatter с моделями
2. `.kilo/capability-index.yaml` - capabilities и routing
3. `.kilo/kilo.jsonc` - model assignments
4. Git history - история изменений
5. Gitea issue comments - performance metrics
**Скрипты:**
- `agent-evolution/scripts/sync-agent-history.ts` - основная синхронизация
- `agent-evolution/scripts/build-standalone.cjs` - генерация HTML
**NPM Scripts:**
```json
"sync:evolution": "bun run agent-evolution/scripts/sync-agent-history.ts && node agent-evolution/scripts/build-standalone.cjs",
"evolution:dashboard": "bunx serve agent-evolution -l 3001",
"evolution:open": "start agent-evolution/index.standalone.html"
```
**Критерии приёмки:**
- [ ] Синхронизация работает корректно
- [ ] HTML генерируется автоматически
- [ ] Данные консистентны
---
### Issue 4: Документация и README
**Title:** Документация Agent Evolution Dashboard
**Labels:** `documentation`, `low-priority`
**Milestone:** Agent Evolution Dashboard
**Описание:**
Создать полную документацию:
1.`agent-evolution/README.md` - основная документация
2. 🔄 `docs/agent-evolution.md` - техническая документация
3. 🔄 Инструкция по запуску в `AGENTS.md`
4. ✅ Schema: `agent-evolution/data/agent-versions.schema.json`
5. ✅ Skills: `.kilo/skills/evolution-sync/SKILL.md`
6. ✅ Rules: `.kilo/rules/evolutionary-sync.md`
**Критерии приёмки:**
- [ ] README покрывает все сценарии использования
- [ ] Техническая документация описывает API
- [ ] Есть примеры кода
---
### Issue 5: Docker контейнер для дашборда
**Title:** Docker-изация Agent Evolution Dashboard
**Labels:** `docker`, `deployment`, `low-priority`
**Milestone:** Agent Evolution Dashboard
**Описание:**
Упаковать дашборд в Docker для простого деплоя:
**Файлы:**
-`agent-evolution/Dockerfile`
-`docker-compose.evolution.yml`
-`agent-evolution/docker-run.sh` (Linux/macOS)
-`agent-evolution/docker-run.bat` (Windows)
**Команды:**
```bash
# Linux/macOS
bash agent-evolution/docker-run.sh restart
# Windows
agent-evolution\docker-run.bat restart
# Docker Compose
docker-compose -f docker-compose.evolution.yml up -d
```
**Критерии приёмки:**
- [ ] Docker образ собирается
- [ ] Контейнер запускается на порту 3001
- [ ] Данные монтируются корректно
---
## Статус напраления
**Текущий статус:** `PAUSED` - приостановлено до следующего спринта
**Причина паузы:**
Базовая инфраструктура создана:
- ✅ Структура директорий `agent-evolution/`
- ✅ Данные интегрированы в HTML
- ✅ Скрипты синхронизации созданы
- ✅ Docker контейнер настроен
- ✅ Документация написана
**Что осталось:**
- 🔄 Issue #2: Интеграция с Gitea API (требует backend)
- 🔄 Issue #3: Полная синхронизация (требует тестирования)
- 🔄 Issue #4: Расширенная документация
**Резюме работы:**
Создана полноценная инфраструктура для отслеживания эволюции агентной системы. Дашборд работает автономно без сервера, включает данные о 28 агентах, 8 моделях, рекомендациях по оптимизации. Подготовлен foundation для будущей интеграции с Gitea.
---
## Quick Links
- Dashboard: `agent-evolution/index.standalone.html`
- Data: `agent-evolution/data/agent-versions.json`
- Build Script: `agent-evolution/scripts/build-standalone.cjs`
- Docker: `docker-compose -f docker-compose.evolution.yml up -d`
- NPM: `bun run sync:evolution`

409
agent-evolution/README.md Normal file
View File

@@ -0,0 +1,409 @@
# Agent Evolution Dashboard
Интерактивная панель для отслеживания эволюции агентной системы APAW.
## 🚀 Быстрый старт
### Синхронизация данных
```bash
# Синхронизировать агентов + построить standalone HTML
bun run sync:evolution
# Только построить HTML из существующих данных
bun run evolution:build
```
### Открыть в браузере
**Способ 1: Локальный файл (рекомендуется)**
```bash
# Windows
start agent-evolution\index.standalone.html
# macOS
open agent-evolution/index.standalone.html
# Linux
xdg-open agent-evolution/index.standalone.html
# Или через npm
bun run evolution:open
```
**Способ 2: HTTP сервер**
```bash
cd agent-evolution
python -m http.server 3001
# Открыть http://localhost:3001
```
**Способ 3: Docker**
```bash
# Linux/macOS
bash agent-evolution/docker-run.sh restart
# Windows
agent-evolution\docker-run.bat restart
# Открыть http://localhost:3001
```
## 📁 Структура файлов
### Быстрый запуск
```bash
# Linux/macOS
bash agent-evolution/docker-run.sh restart
# Windows
agent-evolution\docker-run.bat restart
# Открыть в браузере
http://localhost:3001
```
### Docker Compose
```bash
# Стандартный запуск
docker-compose -f docker-compose.evolution.yml up -d
# С nginx reverse proxy
docker-compose -f docker-compose.evolution.yml --profile nginx up -d
# Остановка
docker-compose -f docker-compose.evolution.yml down
```
### Управление контейнером
```bash
# Linux/macOS
bash agent-evolution/docker-run.sh build # Собрать образ
bash agent-evolution/docker-run.sh run # Запустить контейнер
bash agent-evolution/docker-run.sh stop # Остановить
bash agent-evolution/docker-run.sh restart # Пересобрать и запустить
bash agent-evolution/docker-run.sh logs # Логи
bash agent-evolution/docker-run.sh open # Открыть в браузере
bash agent-evolution/docker-run.sh sync # Синхронизировать данные
bash agent-evolution/docker-run.sh status # Статус
bash agent-evolution/docker-run.sh clean # Удалить всё
bash agent-evolution/docker-run.sh dev # Dev режим с hot reload
# Windows
agent-evolution\docker-run.bat build
agent-evolution\docker-run.bat run
agent-evolution\docker-run.bat stop
agent-evolution\docker-run.bat restart
agent-evolution\docker-run.bat logs
agent-evolution\docker-run.bat open
agent-evolution\docker-run.bat sync
agent-evolution\docker-run.bat status
agent-evolution\docker-run.bat clean
agent-evolution\docker-run.bat dev
```
### NPM Scripts
```bash
bun run evolution:build # Собрать Docker образ
bun run evolution:run # Запустить контейнер
bun run evolution:stop # Остановить
bun run evolution:dev # Docker Compose
bun run evolution:logs # Логи
```
## Структура
```
agent-evolution/
├── data/
│ ├── agent-versions.json # Текущее состояние + история
│ └── agent-versions.schema.json # JSON Schema
├── scripts/
│ └── sync-agent-history.ts # Скрипт синхронизации
├── index.html # Дашборд UI
└── README.md # Этот файл
```
## Быстрый старт
```bash
# Синхронизировать данные агентов
bun run sync:evolution
# Запустить дашборд
bun run evolution:dashboard
# Открыть в браузере
bun run evolution:open
# или http://localhost:3001
```
## Возможности дашборда
### 1. Overview — Обзор
- **Статистика**: общее количество агентов, с историей, рекомендации
- **Recent Changes**: последние изменения моделей и промптов
- **Pending Recommendations**: критические рекомендации по обновлению
### 2. All Agents — Все агенты
- Поиск и фильтрация по категориям
- Карточки агентов с:
- Текущей моделью
- Fit Score
- Количеством capability
- Историей изменений
### 3. Timeline — История
- Полная хронология изменений
- Типы событий: model_change, prompt_change, agent_created
- Фильтрация по дате
### 4. Recommendations — Рекомендации
- Агенты с pending recommendations
- Приоритеты: critical, high, medium, low
- Экспорт в JSON
### 5. Model Matrix — Матрица моделей
- Таблица Agent × Model
- Fit Score для каждой пары
- Визуализация provider distribution
## Источники данных
### 1. Agent Files (`.kilo/agents/*.md`)
```yaml
---
model: ollama-cloud/qwen3-coder:480b
description: Primary code writer
mode: subagent
color: "#DC2626"
---
```
### 2. Capability Index (`.kilo/capability-index.yaml`)
```yaml
agents:
lead-developer:
model: ollama-cloud/qwen3-coder:480b
capabilities: [code_writing, refactoring]
```
### 3. Kilo Config (`.kilo/kilo.jsonc`)
```json
{
"agent": {
"lead-developer": {
"model": "ollama-cloud/qwen3-coder:480b"
}
}
}
```
### 4. Git History
```bash
git log --all --oneline -- ".kilo/agents/"
```
### 5. Gitea Issue Comments
```markdown
## ✅ lead-developer completed
**Score**: 8/10
**Duration**: 1.2h
**Files**: src/auth.ts, src/user.ts
```
## JSON Schema
Формат `agent-versions.json`:
```json
{
"version": "1.0.0",
"lastUpdated": "2026-04-05T17:27:00Z",
"agents": {
"lead-developer": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Core Dev",
"fit_score": 92
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": null,
"to": "ollama-cloud/qwen3-coder:480b",
"reason": "Initial configuration"
}
],
"performance_log": [
{
"date": "2026-04-05T10:30:00Z",
"issue": 42,
"score": 8,
"duration_ms": 120000,
"success": true
}
]
}
}
}
```
## Интеграция
### В Pipeline
Добавьте в `.kilo/commands/pipeline.md`:
```yaml
post_steps:
- name: sync_evolution
run: bun run sync:evolution
```
### В Gitea Webhooks
```typescript
// Добавить webhook в Gitea
{
"url": "http://localhost:3000/api/evolution/webhook",
"events": ["issue_comment", "issues"]
}
```
### Чтение из кода
```typescript
import { agentEvolution } from './agent-evolution/scripts/sync-agent-history';
// Получить все агенты
const agents = await agentEvolution.getAllAgents();
// Получить историю конкретного агента
const history = await agentEvolution.getAgentHistory('lead-developer');
// Записать изменение модели
await agentEvolution.recordChange({
agent: 'security-auditor',
type: 'model_change',
from: 'gpt-oss:120b',
to: 'nemotron-3-super',
reason: 'Better reasoning for security analysis',
source: 'manual'
});
```
## Рекомендации
### Приоритеты
| Priority | Criteria | Action |
|----------|----------|--------|
| Critical | Fit score < 70 | Немедленное обновление |
| High | Модель недоступна | Переключение на fallback |
| Medium | Доступна лучшая модель | Рассмотреть обновление |
| Low | Возможна оптимизация | Опционально |
### Примеры рекомендаций
```json
{
"agent": "requirement-refiner",
"recommendations": [{
"target": "ollama-cloud/nemotron-3-super",
"reason": "+22% quality, 1M context for specifications",
"priority": "critical"
}]
}
```
## Мониторинг
### Метрики агента
- **Average Score**: Средний балл за последние 10 выполнений
- **Success Rate**: Процент успешных выполнений
- **Average Duration**: Среднее время выполнения
- **Files per Task**: Среднее количество файлов на задачу
### Метрики системы
- **Total Agents**: Количество активных агентов
- **Agents with History**: Агентов с историей изменений
- **Pending Recommendations**: Количество рекомендаций
- **Provider Distribution**: Распределение по провайдерам
## Обслуживание
### Очистка истории
```bash
# Удалить дубликаты
bun run agent-evolution/scripts/cleanup.ts --dedupe
# Слить связанные изменения
bun run agent-evolution/scripts/cleanup.ts --merge
```
### Экспорт данных
```bash
# Экспортировать в CSV
bun run agent-evolution/scripts/export.ts --format csv
# Экспортировать в Markdown
bun run agent-evolution/scripts/export.ts --format md
```
### Резервное копирование
```bash
# Создать бэкап
cp agent-evolution/data/agent-versions.json agent-evolution/data/backup/agent-versions-$(date +%Y%m%d).json
# Восстановить из бэкапа
cp agent-evolution/data/backup/agent-versions-20260405.json agent-evolution/data/agent-versions.json
```
## Будущие улучшения
1. **API Endpoints**:
- `GET /api/evolution/agents` — список агентов
- `GET /api/evolution/agents/:name/history` — история агента
- `POST /api/evolution/sync` — запустить синхронизацию
2. **Real-time Updates**:
- WebSocket для обновления дашборда
- Автоматическое обновление при изменениях
3. **Analytics**:
- Графики производительности во времени
- Сравнение моделей
- Прогнозирование производительности
4. **Integration**:
- Slack/Telegram уведомления
- Автоматическое применение рекомендаций
- A/B testing моделей

View File

@@ -0,0 +1,711 @@
{
"$schema": "./agent-versions.schema.json",
"version": "1.0.0",
"lastUpdated": "2026-04-05T17:27:00Z",
"agents": {
"lead-developer": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Core Dev",
"mode": "subagent",
"color": "#DC2626",
"description": "Primary code writer for backend and core logic. Writes implementation to pass tests",
"benchmark": {
"swe_bench": 66.5,
"ruler_1m": null,
"terminal_bench": null,
"fit_score": 92
},
"capabilities": ["code_writing", "refactoring", "bug_fixing", "implementation"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": null,
"to": "ollama-cloud/qwen3-coder:480b",
"reason": "Initial configuration from capability-index.yaml",
"source": "git"
}
],
"performance_log": []
},
"frontend-developer": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Core Dev",
"mode": "subagent",
"color": "#3B82F6",
"description": "UI implementation specialist with multimodal capabilities",
"benchmark": {
"swe_bench": null,
"ruler_1m": null,
"terminal_bench": null,
"fit_score": 90
},
"capabilities": ["ui_implementation", "component_creation", "styling", "responsive_design"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "af5f401",
"type": "agent_created",
"from": null,
"to": "ollama-cloud/qwen3-coder:480b",
"reason": "Flutter development support added",
"source": "git"
}
],
"performance_log": []
},
"backend-developer": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Core Dev",
"mode": "subagent",
"color": "#10B981",
"description": "Node.js, Express, APIs, database specialist",
"benchmark": {
"swe_bench": null,
"ruler_1m": null,
"terminal_bench": null,
"fit_score": 91
},
"capabilities": ["api_development", "database_design", "server_logic", "authentication"]
},
"history": [],
"performance_log": []
},
"go-developer": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Core Dev",
"mode": "subagent",
"color": "#00ADD8",
"description": "Go backend services specialist",
"benchmark": {
"swe_bench": null,
"ruler_1m": null,
"terminal_bench": null,
"fit_score": 85
},
"capabilities": ["go_api_development", "go_database_design", "go_concurrent_programming", "go_authentication"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/deepseek-v3.2",
"to": "ollama-cloud/qwen3-coder:480b",
"reason": "Qwen3-Coder optimized for Go development",
"source": "git"
}
],
"performance_log": []
},
"sdet-engineer": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "QA",
"mode": "subagent",
"color": "#8B5CF6",
"description": "Writes tests following TDD methodology. Tests MUST fail initially",
"benchmark": {
"swe_bench": null,
"ruler_1m": null,
"terminal_bench": null,
"fit_score": 88
},
"capabilities": ["unit_tests", "integration_tests", "e2e_tests", "test_planning", "visual_regression"]
},
"history": [],
"performance_log": []
},
"code-skeptic": {
"current": {
"model": "ollama-cloud/minimax-m2.5",
"provider": "Ollama",
"category": "QA",
"mode": "subagent",
"color": "#EF4444",
"description": "Adversarial code reviewer. Finds problems and issues. Does NOT suggest implementations",
"benchmark": {
"swe_bench": 80.2,
"ruler_1m": null,
"terminal_bench": null,
"fit_score": 85
},
"capabilities": ["code_review", "security_review", "style_check", "issue_identification"]
},
"history": [],
"performance_log": []
},
"security-auditor": {
"current": {
"model": "ollama-cloud/nemotron-3-super",
"provider": "Ollama",
"category": "Security",
"mode": "subagent",
"color": "#DC2626",
"description": "Scans for security vulnerabilities, OWASP Top 10, dependency CVEs",
"benchmark": {
"swe_bench": 60.5,
"ruler_1m": 91.75,
"pinch_bench": 85.6,
"fit_score": 80
},
"capabilities": ["vulnerability_scan", "owasp_check", "secret_detection", "auth_review"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/deepseek-v3.2",
"to": "ollama-cloud/nemotron-3-super",
"reason": "Nemotron 3 Super optimized for security analysis with RULER@1M",
"source": "git"
}
],
"performance_log": []
},
"performance-engineer": {
"current": {
"model": "ollama-cloud/nemotron-3-super",
"provider": "Ollama",
"category": "Performance",
"mode": "subagent",
"color": "#F59E0B",
"description": "Reviews code for performance issues: N+1 queries, memory leaks, algorithmic complexity",
"benchmark": {
"swe_bench": 60.5,
"ruler_1m": 91.75,
"pinch_bench": 85.6,
"fit_score": 82
},
"capabilities": ["performance_analysis", "n_plus_one_detection", "memory_leak_check", "algorithm_analysis"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/gpt-oss:120b",
"to": "ollama-cloud/nemotron-3-super",
"reason": "Better reasoning for performance analysis",
"source": "git"
}
],
"performance_log": []
},
"browser-automation": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Testing",
"mode": "subagent",
"color": "#0EA5E9",
"description": "Browser automation agent using Playwright MCP for E2E testing",
"benchmark": {
"swe_bench": null,
"fit_score": 87
},
"capabilities": ["e2e_browser_tests", "form_filling", "navigation_testing", "screenshot_capture"]
},
"history": [],
"performance_log": []
},
"visual-tester": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Testing",
"mode": "subagent",
"color": "#EC4899",
"description": "Visual regression testing agent that compares screenshots",
"benchmark": {
"swe_bench": null,
"fit_score": 82
},
"capabilities": ["visual_regression", "pixel_comparison", "screenshot_diff", "ui_validation"]
},
"history": [],
"performance_log": []
},
"system-analyst": {
"current": {
"model": "ollama-cloud/glm-5",
"provider": "Ollama",
"category": "Analysis",
"mode": "subagent",
"color": "#6366F1",
"description": "Designs technical specifications, data schemas, and API contracts",
"benchmark": {
"swe_bench": null,
"fit_score": 82
},
"capabilities": ["architecture_design", "api_specification", "database_modeling", "technical_documentation"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/gpt-oss:120b",
"to": "ollama-cloud/glm-5",
"reason": "GLM-5 better for system engineering and architecture",
"source": "git"
}
],
"performance_log": []
},
"requirement-refiner": {
"current": {
"model": "ollama-cloud/gpt-oss:120b",
"provider": "Ollama",
"category": "Analysis",
"mode": "subagent",
"color": "#8B5CF6",
"description": "Converts vague ideas into strict User Stories with acceptance criteria",
"benchmark": {
"swe_bench": 62.4,
"fit_score": 62
},
"capabilities": ["requirement_analysis", "user_story_creation", "acceptance_criteria", "clarification"],
"recommendations": [
{
"target": "ollama-cloud/nemotron-3-super",
"reason": "+22% quality, 1M context for specifications",
"priority": "critical"
}
]
},
"history": [],
"performance_log": []
},
"history-miner": {
"current": {
"model": "ollama-cloud/glm-5",
"provider": "Ollama",
"category": "Analysis",
"mode": "subagent",
"color": "#A855F7",
"description": "Analyzes git history for duplicates and past solutions",
"benchmark": {
"swe_bench": null,
"fit_score": 78
},
"capabilities": ["git_search", "duplicate_detection", "past_solution_finder", "pattern_identification"]
},
"history": [],
"performance_log": []
},
"capability-analyst": {
"current": {
"model": "ollama-cloud/gpt-oss:120b",
"provider": "Ollama",
"category": "Analysis",
"mode": "subagent",
"color": "#14B8A6",
"description": "Analyzes task coverage and identifies gaps",
"benchmark": {
"swe_bench": 62.4,
"fit_score": 66
},
"capabilities": ["gap_analysis", "capability_mapping", "recommendation_generation", "coverage_analysis"],
"recommendations": [
{
"target": "ollama-cloud/nemotron-3-super",
"reason": "+21% quality for gap analysis and recommendations",
"priority": "critical"
}
]
},
"history": [],
"performance_log": []
},
"orchestrator": {
"current": {
"model": "ollama-cloud/glm-5",
"provider": "Ollama",
"category": "Process",
"mode": "primary",
"color": "#0EA5E9",
"description": "Process manager. Distributes tasks between agents",
"benchmark": {
"swe_bench": null,
"fit_score": 80
},
"capabilities": ["task_routing", "state_management", "agent_coordination", "workflow_execution"]
},
"history": [],
"performance_log": []
},
"release-manager": {
"current": {
"model": "ollama-cloud/devstral-2:123b",
"provider": "Ollama",
"category": "Process",
"mode": "subagent",
"color": "#22C55E",
"description": "Manages git operations, semantic versioning, deployments",
"benchmark": {
"swe_bench": null,
"fit_score": 75
},
"capabilities": ["git_operations", "version_management", "changelog_creation", "deployment"]
},
"history": [],
"performance_log": []
},
"evaluator": {
"current": {
"model": "ollama-cloud/nemotron-3-super",
"provider": "Ollama",
"category": "Process",
"mode": "subagent",
"color": "#F97316",
"description": "Scores agent effectiveness after task completion",
"benchmark": {
"swe_bench": 60.5,
"fit_score": 82
},
"capabilities": ["performance_scoring", "process_analysis", "pattern_identification", "improvement_recommendations"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/gpt-oss:120b",
"to": "ollama-cloud/nemotron-3-super",
"reason": "Nemotron 3 Super better for evaluation tasks",
"source": "git"
}
],
"performance_log": []
},
"prompt-optimizer": {
"current": {
"model": "ollama-cloud/nemotron-3-super",
"provider": "Ollama",
"category": "Process",
"mode": "subagent",
"color": "#EC4899",
"description": "Improves agent system prompts based on performance failures",
"benchmark": {
"swe_bench": 60.5,
"fit_score": 80
},
"capabilities": ["prompt_analysis", "prompt_improvement", "failure_pattern_detection"],
"recommendations": [
{
"target": "openrouter/qwen3.6-plus:free",
"reason": "Terminal-Bench 61.6% > Nemotron, always-on CoT",
"priority": "high"
}
]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "openrouter/qwen3.6-plus:free",
"to": "ollama-cloud/nemotron-3-super",
"reason": "Research recommendation applied",
"source": "git"
}
],
"performance_log": []
},
"the-fixer": {
"current": {
"model": "ollama-cloud/minimax-m2.5",
"provider": "Ollama",
"category": "Fixes",
"mode": "subagent",
"color": "#EF4444",
"description": "Iteratively fixes bugs based on specific error reports",
"benchmark": {
"swe_bench": 80.2,
"fit_score": 88
},
"capabilities": ["bug_fixing", "issue_resolution", "code_correction"]
},
"history": [],
"performance_log": []
},
"product-owner": {
"current": {
"model": "ollama-cloud/glm-5",
"provider": "Ollama",
"category": "Management",
"mode": "subagent",
"color": "#10B981",
"description": "Manages issue checklists, status labels, progress tracking",
"benchmark": {
"swe_bench": null,
"fit_score": 76
},
"capabilities": ["issue_management", "prioritization", "backlog_management", "workflow_completion"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "openrouter/qwen3.6-plus:free",
"to": "ollama-cloud/glm-5",
"reason": "GLM-5 good for management tasks",
"source": "git"
}
],
"performance_log": []
},
"workflow-architect": {
"current": {
"model": "ollama-cloud/glm-5",
"provider": "Ollama",
"category": "Workflow",
"mode": "subagent",
"color": "#6366F1",
"description": "Creates workflow definitions",
"benchmark": {
"swe_bench": null,
"fit_score": 74
},
"capabilities": ["workflow_design", "process_definition", "automation_setup"]
},
"history": [],
"performance_log": []
},
"markdown-validator": {
"current": {
"model": "ollama-cloud/nemotron-3-nano:30b",
"provider": "Ollama",
"category": "Validation",
"mode": "subagent",
"color": "#84CC16",
"description": "Validates Markdown formatting",
"benchmark": {
"swe_bench": null,
"fit_score": 72
},
"capabilities": ["markdown_validation", "formatting_check", "link_validation"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "openrouter/qwen3.6-plus:free",
"to": "ollama-cloud/nemotron-3-nano:30b",
"reason": "Nano efficient for lightweight validation tasks",
"source": "git"
}
],
"performance_log": []
},
"agent-architect": {
"current": {
"model": "ollama-cloud/gpt-oss:120b",
"provider": "Ollama",
"category": "Meta",
"mode": "subagent",
"color": "#A855F7",
"description": "Creates new agents when gaps identified",
"benchmark": {
"swe_bench": 62.4,
"fit_score": 69
},
"capabilities": ["agent_design", "prompt_engineering", "capability_definition"],
"recommendations": [
{
"target": "ollama-cloud/nemotron-3-super",
"reason": "+19% quality for agent design",
"priority": "high"
}
]
},
"history": [],
"performance_log": []
},
"planner": {
"current": {
"model": "ollama-cloud/nemotron-3-super",
"provider": "Ollama",
"category": "Cognitive",
"mode": "subagent",
"color": "#3B82F6",
"description": "Task decomposition, CoT, ToT planning",
"benchmark": {
"swe_bench": 60.5,
"fit_score": 84
},
"capabilities": ["task_decomposition", "chain_of_thought", "tree_of_thoughts", "plan_execute_reflect"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/gpt-oss:120b",
"to": "ollama-cloud/nemotron-3-super",
"reason": "Nemotron 3 Super excels at planning",
"source": "git"
}
],
"performance_log": []
},
"reflector": {
"current": {
"model": "ollama-cloud/nemotron-3-super",
"provider": "Ollama",
"category": "Cognitive",
"mode": "subagent",
"color": "#14B8A6",
"description": "Self-reflection agent using Reflexion pattern",
"benchmark": {
"swe_bench": 60.5,
"fit_score": 82
},
"capabilities": ["self_reflection", "mistake_analysis", "lesson_extraction"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/gpt-oss:120b",
"to": "ollama-cloud/nemotron-3-super",
"reason": "Better for reflection tasks",
"source": "git"
}
],
"performance_log": []
},
"memory-manager": {
"current": {
"model": "ollama-cloud/nemotron-3-super",
"provider": "Ollama",
"category": "Cognitive",
"mode": "subagent",
"color": "#F59E0B",
"description": "Manages agent memory systems",
"benchmark": {
"swe_bench": 60.5,
"ruler_1m": 91.75,
"fit_score": 90
},
"capabilities": ["memory_retrieval", "memory_storage", "memory_consolidation", "relevance_scoring"]
},
"history": [
{
"date": "2026-04-05T05:21:00Z",
"commit": "caf77f53c8",
"type": "model_change",
"from": "ollama-cloud/gpt-oss:120b",
"to": "ollama-cloud/nemotron-3-super",
"reason": "RULER@1M critical for memory ctx",
"source": "git"
}
],
"performance_log": []
},
"devops-engineer": {
"current": {
"model": null,
"provider": null,
"category": "DevOps",
"mode": "subagent",
"color": "#2563EB",
"description": "Docker, Kubernetes, CI/CD pipeline automation",
"benchmark": {
"fit_score": 0
},
"capabilities": ["docker", "kubernetes", "ci_cd", "infrastructure"],
"status": "new",
"recommendations": [
{
"target": "ollama-cloud/nemotron-3-super",
"reason": "DevOps requires strong reasoning",
"priority": "critical"
}
]
},
"history": [],
"performance_log": []
},
"flutter-developer": {
"current": {
"model": "ollama-cloud/qwen3-coder:480b",
"provider": "Ollama",
"category": "Core Dev",
"mode": "subagent",
"color": "#0EA5E9",
"description": "Flutter mobile specialist",
"benchmark": {
"fit_score": 86
},
"capabilities": ["flutter_development", "state_management", "ui_components", "cross_platform"]
},
"history": [
{
"date": "2026-04-05T15:00:00Z",
"commit": "af5f401",
"type": "agent_created",
"from": null,
"to": "ollama-cloud/qwen3-coder:480b",
"reason": "New agent for Flutter development",
"source": "git"
}
],
"performance_log": []
}
},
"providers": {
"Ollama": {
"models": [
{"id": "qwen3-coder:480b", "swe_bench": 66.5, "context": "256K", "active_params": "35B"},
{"id": "minimax-m2.5", "swe_bench": 80.2, "context": "128K"},
{"id": "nemotron-3-super", "swe_bench": 60.5, "ruler_1m": 91.75, "context": "1M"},
{"id": "nemotron-3-nano:30b", "swe_bench": null, "context": "128K"},
{"id": "glm-5", "swe_bench": null, "context": "128K"},
{"id": "gpt-oss:120b", "swe_bench": 62.4, "context": "130K"},
{"id": "gpt-oss:20b", "swe_bench": null, "context": "128K"},
{"id": "devstral-2:123b", "swe_bench": null, "context": "128K"},
{"id": "deepseek-v3.2", "swe_bench": null, "context": "128K"}
]
},
"OpenRouter": {
"models": [
{"id": "qwen3.6-plus:free", "swe_bench": null, "terminal_bench": 61.6, "context": "1M", "free": true},
{"id": "gemma4:31b", "intelligence_index": 39, "context": "256K", "free": true}
]
},
"Groq": {
"models": [
{"id": "gpt-oss-120b", "speed_tps": 500, "rpd": 1000, "tpd": "200K"},
{"id": "gpt-oss-20b", "speed_tps": 1200, "rpd": 1000},
{"id": "kimi-k2-instruct", "speed_tps": 300, "rpm": 60},
{"id": "qwen3-32b", "speed_tps": 400, "rpd": 1000, "tpd": "500K"},
{"id": "llama-4-scout", "speed_tps": 350, "tpm": "30K"}
]
}
},
"evolution_metrics": {
"total_agents": 32,
"agents_with_history": 12,
"pending_recommendations": 6,
"last_sync": "2026-04-05T17:27:00Z",
"sync_sources": ["git", "capability-index.yaml", "kilo.jsonc"]
}
}

View File

@@ -0,0 +1,183 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Agent Versions Schema",
"description": "Schema for tracking agent evolution in APAW",
"type": "object",
"required": ["version", "lastUpdated", "agents", "providers", "evolution_metrics"],
"properties": {
"$schema": {
"type": "string",
"description": "Reference to this schema"
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$",
"description": "Schema version (semver)"
},
"lastUpdated": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp of last update"
},
"agents": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": ["current", "history", "performance_log"],
"properties": {
"current": {
"type": "object",
"required": ["model", "provider", "category", "mode", "description"],
"properties": {
"model": {
"type": "string",
"description": "Current model ID (e.g., ollama-cloud/qwen3-coder:480b)"
},
"provider": {
"type": "string",
"enum": ["Ollama", "OpenRouter", "Groq", "Unknown"],
"description": "Model provider"
},
"category": {
"type": "string",
"description": "Agent category (Core Dev, QA, Security, etc.)"
},
"mode": {
"type": "string",
"enum": ["primary", "subagent", "all"],
"description": "Agent invocation mode"
},
"color": {
"type": "string",
"pattern": "^#[0-9A-Fa-f]{6}$",
"description": "UI color in hex format"
},
"description": {
"type": "string",
"description": "Agent purpose description"
},
"benchmark": {
"type": "object",
"properties": {
"swe_bench": { "type": "number", "minimum": 0, "maximum": 100 },
"ruler_1m": { "type": "number", "minimum": 0, "maximum": 100 },
"terminal_bench": { "type": "number", "minimum": 0, "maximum": 100 },
"pinch_bench": { "type": "number", "minimum": 0, "maximum": 100 },
"fit_score": { "type": "number", "minimum": 0, "maximum": 100 }
}
},
"capabilities": {
"type": "array",
"items": { "type": "string" },
"description": "List of agent capabilities"
},
"recommendations": {
"type": "array",
"items": {
"type": "object",
"required": ["target", "reason", "priority"],
"properties": {
"target": { "type": "string" },
"reason": { "type": "string" },
"priority": {
"type": "string",
"enum": ["critical", "high", "medium", "low"]
}
}
}
},
"status": {
"type": "string",
"enum": ["active", "new", "deprecated", "testing"]
}
}
},
"history": {
"type": "array",
"items": {
"type": "object",
"required": ["date", "commit", "type", "to", "reason", "source"],
"properties": {
"date": {
"type": "string",
"format": "date-time"
},
"commit": { "type": "string" },
"type": {
"type": "string",
"enum": ["model_change", "prompt_change", "agent_created", "agent_removed", "capability_change"]
},
"from": { "type": ["string", "null"] },
"to": { "type": "string" },
"reason": { "type": "string" },
"source": {
"type": "string",
"enum": ["git", "gitea", "manual"]
},
"issue_number": { "type": "integer" }
}
}
},
"performance_log": {
"type": "array",
"items": {
"type": "object",
"required": ["date", "issue", "score", "success"],
"properties": {
"date": { "type": "string", "format": "date-time" },
"issue": { "type": "integer" },
"score": { "type": "number", "minimum": 0, "maximum": 10 },
"duration_ms": { "type": "integer" },
"success": { "type": "boolean" }
}
}
}
}
}
},
"providers": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": ["models"],
"properties": {
"models": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"swe_bench": { "type": "number" },
"terminal_bench": { "type": "number" },
"ruler_1m": { "type": "number" },
"pinch_bench": { "type": "number" },
"context": { "type": "string" },
"active_params": { "type": "string" },
"speed_tps": { "type": "number" },
"rpm": { "type": "number" },
"rpd": { "type": "number" },
"tpm": { "type": "string" },
"tpd": { "type": "string" },
"free": { "type": "boolean" }
}
}
}
}
}
},
"evolution_metrics": {
"type": "object",
"required": ["total_agents", "agents_with_history", "pending_recommendations", "last_sync", "sync_sources"],
"properties": {
"total_agents": { "type": "integer", "minimum": 0 },
"agents_with_history": { "type": "integer", "minimum": 0 },
"pending_recommendations": { "type": "integer", "minimum": 0 },
"last_sync": { "type": "string", "format": "date-time" },
"sync_sources": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
}

View File

@@ -0,0 +1,197 @@
@echo off
REM Agent Evolution Dashboard - Docker Management Script (Windows)
setlocal enabledelayedexpansion
set IMAGE_NAME=apaw-evolution
set CONTAINER_NAME=apaw-evolution-dashboard
set PORT=3001
set DATA_DIR=.\agent-evolution\data
REM Colors (limited in Windows CMD)
set RED=[91m
set GREEN=[92m
set YELLOW=[93m
set NC=[0m
REM Main logic
if "%1"=="" goto help
if "%1"=="build" goto build
if "%1"=="run" goto run
if "%1"=="stop" goto stop
if "%1"=="restart" goto restart
if "%1"=="logs" goto logs
if "%1"=="open" goto open
if "%1"=="sync" goto sync
if "%1"=="status" goto status
if "%1"=="clean" goto clean
if "%1"=="dev" goto dev
if "%1"=="help" goto help
goto unknown
:log_info
echo %GREEN%[INFO]%NC% %*
goto :eof
:log_warn
echo %YELLOW%[WARN]%NC% %*
goto :eof
:log_error
echo %RED%[ERROR]%NC% %*
goto :eof
:build
call :log_info Building Docker image...
docker build -t %IMAGE_NAME%:latest -f agent-evolution/Dockerfile --target production .
if errorlevel 1 (
call :log_error Build failed
exit /b 1
)
call :log_info Build complete: %IMAGE_NAME%:latest
goto :eof
:run
REM Check if already running
docker ps -q --filter "name=%CONTAINER_NAME%" 2>nul | findstr /r . >nul
if not errorlevel 1 (
call :log_warn Container %CONTAINER_NAME% is already running
call :log_info Use 'docker-run.bat restart' to restart it
exit /b 0
)
REM Remove stopped container
docker ps -aq --filter "name=%CONTAINER_NAME%" 2>nul | findstr /r . >nul
if not errorlevel 1 (
call :log_info Removing stopped container...
docker rm %CONTAINER_NAME% >nul 2>nul
)
call :log_info Starting container...
docker run -d ^
--name %CONTAINER_NAME% ^
-p %PORT%:3001 ^
-v %cd%/%DATA_DIR%:/app/data:ro ^
-v %cd%/.kilo/agents:/app/kilo/agents:ro ^
-v %cd%/.kilo/capability-index.yaml:/app/kilo/capability-index.yaml:ro ^
-v %cd%/.kilo/kilo.jsonc:/app/kilo/kilo.jsonc:ro ^
--restart unless-stopped ^
%IMAGE_NAME%:latest
if errorlevel 1 (
call :log_error Failed to start container
exit /b 1
)
call :log_info Container started: %CONTAINER_NAME%
call :log_info Dashboard available at: http://localhost:%PORT%
goto :eof
:stop
call :log_info Stopping container...
docker stop %CONTAINER_NAME% >nul 2>nul
docker rm %CONTAINER_NAME% >nul 2>nul
call :log_info Container stopped
goto :eof
:restart
call :stop
call :build
call :run
goto :eof
:logs
docker logs -f %CONTAINER_NAME%
goto :eof
:open
set URL=http://localhost:%PORT%
call :log_info Opening dashboard: %URL%
start %URL%
goto :eof
:sync
call :log_info Syncing evolution data...
where bun >nul 2>nul
if not errorlevel 1 (
bun run agent-evolution/scripts/sync-agent-history.ts
) else (
where npx >nul 2>nul
if not errorlevel 1 (
npx tsx agent-evolution/scripts/sync-agent-history.ts
) else (
call :log_error Node.js or Bun required for sync
exit /b 1
)
)
call :log_info Sync complete
goto :eof
:status
docker ps -q --filter "name=%CONTAINER_NAME%" 2>nul | findstr /r . >nul
if not errorlevel 1 (
call :log_info Container status: %GREEN%RUNNING%NC%
call :log_info URL: http://localhost:%PORT%
REM Health check
for /f "tokens=*" %%i in ('docker inspect --format="{{.State.Health.Status}}" %CONTAINER_NAME% 2^>nul') do set HEALTH=%%i
call :log_info Health: !HEALTH!
REM Started time
for /f "tokens=*" %%i in ('docker inspect --format="{{.State.StartedAt}}" %CONTAINER_NAME% 2^>nul') do set STARTED=%%i
if defined STARTED call :log_info Started: !STARTED!
) else (
docker ps -aq --filter "name=%CONTAINER_NAME%" 2>nul | findstr /r . >nul
if not errorlevel 1 (
call :log_info Container status: %YELLOW%STOPPED%NC%
) else (
call :log_info Container status: %RED%NOT CREATED%NC%
)
)
goto :eof
:clean
call :log_info Cleaning up...
call :stop >nul 2>nul
docker rmi %IMAGE_NAME%:latest >nul 2>nul
call :log_info Cleanup complete
goto :eof
:dev
call :log_info Starting development mode...
docker build -t %IMAGE_NAME%:dev -f agent-evolution/Dockerfile --target development .
if errorlevel 1 (
call :log_error Build failed
exit /b 1
)
docker run --rm ^
--name %CONTAINER_NAME%-dev ^
-p %PORT%:3001 ^
-v %cd%/%DATA_DIR%:/app/data ^
-v %cd%/agent-evolution/index.html:/app/index.html ^
%IMAGE_NAME%:dev
goto :eof
:help
echo Agent Evolution Dashboard - Docker Management (Windows)
echo.
echo Usage: %~nx0 ^<command^>
echo.
echo Commands:
echo build Build Docker image
echo run Run container
echo stop Stop container
echo restart Restart container (build + run)
echo logs View container logs
echo open Open dashboard in browser
echo sync Sync evolution data
echo status Show container status
echo clean Remove container and image
echo dev Run in development mode (with hot reload)
echo help Show this help message
goto :eof
:unknown
call :log_error Unknown command: %1
goto help
endlocal

View File

@@ -0,0 +1,203 @@
#!/bin/bash
# Agent Evolution Dashboard - Docker Management Script
set -e
IMAGE_NAME="apaw-evolution"
CONTAINER_NAME="apaw-evolution-dashboard"
PORT=3001
DATA_DIR="./agent-evolution/data"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Build Docker image
build() {
log_info "Building Docker image..."
docker build \
-t "$IMAGE_NAME:latest" \
-f agent-evolution/Dockerfile \
--target production \
.
log_info "Build complete: $IMAGE_NAME:latest"
}
# Run container
run() {
# Check if container already running
if docker ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then
log_warn "Container $CONTAINER_NAME is already running"
log_info "Use '$0 restart' to restart it"
exit 0
fi
# Remove stopped container if exists
if docker ps -aq --filter "name=$CONTAINER_NAME" | grep -q .; then
log_info "Removing stopped container..."
docker rm "$CONTAINER_NAME" >/dev/null || true
fi
log_info "Starting container..."
docker run -d \
--name "$CONTAINER_NAME" \
-p "$PORT:3001" \
-v "$(pwd)/$DATA_DIR:/app/data:ro" \
-v "$(pwd)/.kilo/agents:/app/kilo/agents:ro" \
-v "$(pwd)/.kilo/capability-index.yaml:/app/kilo/capability-index.yaml:ro" \
-v "$(pwd)/.kilo/kilo.jsonc:/app/kilo/kilo.jsonc:ro" \
--restart unless-stopped \
--health-cmd "wget --no-verbose --tries=1 --spider http://localhost:3001/ || exit 1" \
--health-interval "30s" \
--health-timeout "10s" \
--health-retries "3" \
"$IMAGE_NAME:latest"
log_info "Container started: $CONTAINER_NAME"
log_info "Dashboard available at: http://localhost:$PORT"
}
# Stop container
stop() {
log_info "Stopping container..."
docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" >/dev/null 2>&1 || true
log_info "Container stopped"
}
# Restart container
restart() {
stop
build
run
}
# View logs
logs() {
docker logs -f "$CONTAINER_NAME"
}
# Open dashboard in browser
open() {
URL="http://localhost:$PORT"
log_info "Opening dashboard: $URL"
if command -v xdg-open &> /dev/null; then
xdg-open "$URL"
elif command -v open &> /dev/null; then
open "$URL"
elif command -v start &> /dev/null; then
start "$URL"
else
log_warn "Could not open browser. Navigate to: $URL"
fi
}
# Sync evolution data
sync() {
log_info "Syncing evolution data..."
if command -v bun &> /dev/null; then
bun run agent-evolution/scripts/sync-agent-history.ts
elif command -v node &> /dev/null; then
npx tsx agent-evolution/scripts/sync-agent-history.ts
else
log_error "Node.js or Bun required for sync"
exit 1
fi
log_info "Sync complete"
}
# Status check
status() {
if docker ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then
log_info "Container status: ${GREEN}RUNNING${NC}"
log_info "URL: http://localhost:$PORT"
# Health check
HEALTH=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "unknown")
log_info "Health: $HEALTH"
# Uptime
STARTED=$(docker inspect --format='{{.State.StartedAt}}' "$CONTAINER_NAME" 2>/dev/null)
if [ -n "$STARTED" ] && [ "$STARTED" != "" ]; then
log_info "Started: $STARTED"
fi
else
if docker ps -aq --filter "name=$CONTAINER_NAME" | grep -q .; then
log_info "Container status: ${YELLOW}STOPPED${NC}"
else
log_info "Container status: ${RED}NOT CREATED${NC}"
fi
fi
}
# Clean up
clean() {
log_info "Cleaning up..."
stop
docker rmi "$IMAGE_NAME:latest" >/dev/null 2>&1 || true
log_info "Cleanup complete"
}
# Development mode with hot reload
dev() {
log_info "Starting development mode..."
docker build \
-t "$IMAGE_NAME:dev" \
-f agent-evolution/Dockerfile \
--target development \
.
docker run --rm \
--name "${CONTAINER_NAME}-dev" \
-p "$PORT:3001" \
-v "$(pwd)/$DATA_DIR:/app/data" \
-v "$(pwd)/agent-evolution/index.html:/app/index.html" \
"$IMAGE_NAME:dev"
}
# Show help
show_help() {
echo "Agent Evolution Dashboard - Docker Management"
echo ""
echo "Usage: $0 <command>"
echo ""
echo "Commands:"
echo " build Build Docker image"
echo " run Run container"
echo " stop Stop container"
echo " restart Restart container (build + run)"
echo " logs View container logs"
echo " open Open dashboard in browser"
echo " sync Sync evolution data"
echo " status Show container status"
echo " clean Remove container and image"
echo " dev Run in development mode (with hot reload)"
echo " help Show this help message"
}
# Main
case "${1:-help}" in
build) build ;;
run) run ;;
stop) stop ;;
restart) restart ;;
logs) logs ;;
open) open ;;
sync) sync ;;
status) status ;;
clean) clean ;;
dev) dev ;;
help) show_help ;;
*)
log_error "Unknown command: $1"
show_help
exit 1
;;
esac

1062
agent-evolution/index.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,654 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APAW Agent Evolution Dashboard</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<style>
:root {
--bg-deep: #080b12;
--bg-panel: #0e1219;
--bg-card: #141922;
--bg-card-hover: #1a2130;
--border: #1e2736;
--border-bright: #2a3650;
--text-primary: #e8edf5;
--text-secondary: #8896aa;
--text-muted: #5a6880;
--accent-cyan: #00d4ff;
--accent-green: #00ff94;
--accent-orange: #ff9f43;
--accent-red: #ff4757;
--accent-purple: #a855f7;
--glow-cyan: rgba(0,212,255,0.15);
--glow-green: rgba(0,255,148,0.1);
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family:'Inter',sans-serif;
background:var(--bg-deep);
color:var(--text-primary);
min-height:100vh;
overflow-x:hidden;
}
body::before {
content:'';
position:fixed; inset:0;
background:linear-gradient(90deg,rgba(0,212,255,0.02) 1px,transparent 1px),
linear-gradient(rgba(0,212,255,0.02) 1px,transparent 1px);
background-size:60px 60px;
pointer-events:none; z-index:0;
}
.container { max-width:1540px; margin:0 auto; padding:24px 16px; position:relative; z-index:1; }
.header { text-align:center; margin-bottom:32px; }
.header h1 {
font-size:2.4em; font-weight:900;
background:linear-gradient(135deg,var(--accent-cyan),var(--accent-green));
-webkit-background-clip:text; -webkit-text-fill-color:transparent;
}
.header .sub { font-family:'JetBrains Mono',monospace; color:var(--text-muted); font-size:.8em; margin-top:6px; }
.tabs { display:flex; gap:3px; background:var(--bg-panel); border:1px solid var(--border); border-radius:12px; padding:4px; margin-bottom:24px; overflow-x:auto; }
.tab-btn {
flex:1; min-width:100px; padding:10px 12px; background:none; border:none; color:var(--text-secondary);
font-family:'Inter',sans-serif; font-size:.85em; font-weight:600; border-radius:9px; cursor:pointer; transition:all .25s; white-space:nowrap;
}
.tab-btn:hover { color:var(--text-primary); background:var(--bg-card); }
.tab-btn.active { color:var(--bg-deep); background:linear-gradient(135deg,var(--accent-cyan),var(--accent-green)); }
.tab-panel { display:none; }
.tab-panel.active { display:block; }
.stats-row { display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:14px; margin-bottom:24px; }
.stat-card {
background:var(--bg-card); border:1px solid var(--border); border-radius:10px; padding:18px;
transition:all .3s;
}
.stat-card:hover { border-color:var(--accent-cyan); transform:translateY(-2px); }
.stat-label { font-family:'JetBrains Mono',monospace; font-size:.65em; color:var(--text-muted); text-transform:uppercase; letter-spacing:1px; }
.stat-value { font-size:2em; font-weight:800; margin:4px 0; }
.stat-sub { font-size:.75em; color:var(--text-secondary); }
.grad-cyan { background:linear-gradient(135deg,var(--accent-cyan),var(--accent-green)); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
.grad-green { background:linear-gradient(135deg,var(--accent-green),#4ade80); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
.grad-orange { background:linear-gradient(135deg,var(--accent-orange),#facc15); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
.grad-purple { background:linear-gradient(135deg,var(--accent-purple),#e879f9); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
.sec-hdr { display:flex; align-items:center; gap:10px; margin-bottom:16px; padding-bottom:8px; border-bottom:1px solid var(--border); }
.sec-hdr h2 { font-size:1.1em; font-weight:700; }
.badge { font-family:'JetBrains Mono',monospace; font-size:.65em; padding:3px 9px; border-radius:16px; }
.badge-cyan { background:var(--glow-cyan); color:var(--accent-cyan); border:1px solid rgba(0,212,255,.2); }
.badge-green { background:var(--glow-green); color:var(--accent-green); border:1px solid rgba(0,255,148,.2); }
.badge-orange { background:rgba(255,159,67,.1); color:var(--accent-orange); border:1px solid rgba(255,159,67,.2); }
.tbl-wrap { overflow-x:auto; border-radius:10px; border:1px solid var(--border); background:var(--bg-card); margin-bottom:24px; }
table.dt { width:100%; border-collapse:collapse; font-size:.84em; }
table.dt th { font-family:'JetBrains Mono',monospace; font-size:.7em; color:var(--text-muted); text-transform:uppercase; padding:12px 14px; background:var(--bg-panel); border-bottom:2px solid var(--border); text-align:left; }
table.dt td { padding:10px 14px; border-bottom:1px solid var(--border); }
table.dt tr:hover td { background:var(--bg-card-hover); }
table.dt tr { cursor:pointer; transition:background .15s; }
.mbadge { display:inline-block; padding:3px 8px; border-radius:5px; font-family:'JetBrains Mono',monospace; font-size:.78em; font-weight:500; cursor:pointer; transition:all .2s; }
.mbadge:hover { transform:scale(1.05); }
.mbadge.qwen { background:rgba(59,130,246,.12); color:#60a5fa; border:1px solid rgba(59,130,246,.25); }
.mbadge.minimax { background:rgba(255,159,67,.12); color:#ff9f43; border:1px solid rgba(255,159,67,.25); }
.mbadge.nemotron { background:rgba(34,197,94,.12); color:#4ade80; border:1px solid rgba(34,197,94,.25); }
.mbadge.glm { background:rgba(0,255,148,.08); color:#00ff94; border:1px solid rgba(0,255,148,.2); }
.mbadge.gptoss { background:rgba(168,85,247,.12); color:#c084fc; border:1px solid rgba(168,85,247,.25); }
.mbadge.devstral { background:rgba(0,212,255,.12); color:#00d4ff; border:1px solid rgba(0,212,255,.25); }
.prov-tag { display:inline-block; padding:1px 6px; border-radius:3px; font-size:.62em; font-family:'JetBrains Mono',monospace; }
.prov-tag.ollama { background:rgba(0,212,255,.1); color:var(--accent-cyan); }
.prov-tag.groq { background:rgba(255,71,87,.1); color:#ff6b81; }
.prov-tag.openrouter { background:rgba(168,85,247,.1); color:#c084fc; }
.sbar { display:flex; align-items:center; gap:6px; }
.sbar-bg { width:60px; height:5px; background:var(--border); border-radius:3px; overflow:hidden; }
.sbar-fill { height:100%; border-radius:3px; }
.sbar-fill.h { background:linear-gradient(90deg,var(--accent-green),#00ff94); }
.sbar-fill.m { background:linear-gradient(90deg,var(--accent-orange),#ffc048); }
.sbar-fill.l { background:linear-gradient(90deg,var(--accent-red),#ff6b81); }
.snum { font-family:'JetBrains Mono',monospace; font-weight:600; font-size:.85em; min-width:28px; }
.rec-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(380px,1fr)); gap:14px; margin-bottom:24px; }
.rec-card {
background:var(--bg-card); border:1px solid var(--border); border-radius:10px; padding:16px;
transition:all .3s; border-left:3px solid var(--border);
}
.rec-card:hover { border-color:var(--accent-green); transform:translateY(-2px); }
.rec-card.critical { border-left-color:var(--accent-red); }
.rec-card.high { border-left-color:var(--accent-orange); }
.rec-card.medium { border-left-color:var(--accent-orange); }
.rec-card.optimal { border-left-color:var(--accent-green); }
.rec-hdr { display:flex; justify-content:space-between; align-items:center; margin-bottom:10px; }
.rec-agent { font-weight:700; font-size:1em; color:var(--accent-cyan); }
.imp-badge { padding:2px 8px; border-radius:16px; font-family:'JetBrains Mono',monospace; font-size:.68em; font-weight:600; }
.imp-badge.critical { background:rgba(255,71,87,.18); color:var(--accent-red); }
.imp-badge.high { background:rgba(255,159,67,.18); color:var(--accent-orange); }
.imp-badge.medium { background:rgba(250,204,21,.18); color:var(--accent-yellow); }
.imp-badge.optimal { background:rgba(0,255,148,.18); color:var(--accent-green); }
.swap-vis { display:flex; align-items:center; gap:8px; margin:10px 0; padding:10px; background:var(--bg-panel); border-radius:6px; }
.swap-from { font-family:'JetBrains Mono',monospace; font-size:.75em; padding:3px 8px; border-radius:4px; background:rgba(255,71,87,.08); color:#ff6b81; border:1px solid rgba(255,71,87,.15); text-decoration:line-through; opacity:.65; }
.swap-to { font-family:'JetBrains Mono',monospace; font-size:.75em; padding:3px 8px; border-radius:4px; background:rgba(0,255,148,.08); color:#00ff94; border:1px solid rgba(0,255,148,.2); font-weight:600; }
.swap-arrow { color:var(--accent-green); font-size:1.2em; }
.rec-reason { font-size:.82em; color:var(--text-secondary); line-height:1.5; margin-top:10px; padding-top:10px; border-top:1px solid var(--border); }
.hm-wrap { overflow-x:auto; border-radius:10px; border:1px solid var(--border); background:var(--bg-card); padding:16px; margin-bottom:24px; }
.hm-title { font-weight:700; font-size:1.05em; margin-bottom:6px; }
.hm-sub { font-size:.76em; color:var(--text-muted); margin-bottom:12px; }
.hm-table { border-collapse:collapse; width:100%; }
.hm-table th { font-family:'JetBrains Mono',monospace; font-size:.62em; color:var(--text-muted); padding:8px 6px; text-align:center; white-space:nowrap; }
.hm-table th.hm-role { text-align:left; min-width:140px; font-size:.68em; }
.hm-table td { text-align:center; padding:6px 4px; font-family:'JetBrains Mono',monospace; font-size:.74em; font-weight:600; border-radius:3px; cursor:pointer; transition:all .12s; min-width:36px; }
.hm-table td:hover { transform:scale(1.1); z-index:2; }
.hm-table td.hm-r { text-align:left; font-family:'Inter',sans-serif; font-size:.78em; font-weight:500; color:var(--text-secondary); cursor:default; }
.hm-table td.hm-r:hover { transform:none; }
.hm-cur { outline:2px solid var(--accent-cyan); outline-offset:-2px; }
.modal { display:none; position:fixed; inset:0; background:rgba(0,0,0,.85); z-index:9999; justify-content:center; align-items:center; padding:20px; }
.modal.show { display:flex; }
.modal-content { background:var(--bg-panel); border:1px solid var(--accent-cyan); border-radius:14px; max-width:800px; width:100%; max-height:85vh; overflow-y:auto; }
.modal-header { display:flex; justify-content:space-between; align-items:center; padding:20px; border-bottom:1px solid var(--border); position:sticky; top:0; background:var(--bg-panel); z-index:1; }
.modal-title { font-weight:700; font-size:1.2em; display:flex; align-items:center; gap:10px; }
.modal-close { background:none; border:none; color:var(--text-muted); font-size:1.5em; cursor:pointer; }
.modal-close:hover { color:var(--accent-red); }
.modal-body { padding:20px; }
.model-info { display:grid; grid-template-columns:repeat(2,1fr); gap:12px; margin-bottom:16px; }
.model-info-item { background:var(--bg-card); padding:12px; border-radius:6px; }
.model-info-label { font-size:.7em; color:var(--text-muted); text-transform:uppercase; }
.model-info-value { font-size:1.1em; font-weight:600; margin-top:2px; }
.model-tags { display:flex; flex-wrap:wrap; gap:6px; margin-top:12px; }
.model-tag { padding:4px 10px; background:rgba(0,212,255,.1); border:1px solid rgba(0,212,255,.2); border-radius:16px; font-size:.75em; color:var(--accent-cyan); }
.gitea-timeline { position:relative; padding-left:24px; }
.gitea-timeline::before { content:''; position:absolute; left:8px; top:0; bottom:0; width:2px; background:var(--border); }
.gitea-item { position:relative; padding:12px 0 12px 24px; border-bottom:1px solid var(--border); }
.gitea-item:last-child { border-bottom:none; }
.gitea-item::before { content:''; position:absolute; left:-20px; top:18px; width:12px; height:12px; border-radius:50%; background:var(--accent-cyan); border:2px solid var(--border); }
.gitea-date { font-family:'JetBrains Mono',monospace; font-size:.75em; color:var(--text-muted); }
.gitea-content { font-size:.9em; margin-top:4px; }
.gitea-agent { font-weight:600; color:var(--accent-cyan); }
.gitea-change { color:var(--text-secondary); }
.frow { display:flex; gap:6px; margin-bottom:16px; flex-wrap:wrap; }
.fbtn { padding:6px 14px; background:var(--bg-card); border:1px solid var(--border); color:var(--text-secondary); border-radius:20px; font-size:.8em; cursor:pointer; transition:all .2s; }
.fbtn:hover,.fbtn.active { border-color:var(--accent-cyan); color:var(--accent-cyan); background:rgba(0,212,255,.06); }
.models-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:12px; }
.mc { background:var(--bg-card); border:1px solid var(--border); border-radius:10px; padding:16px; cursor:pointer; transition:all .25s; }
.mc:hover { border-color:var(--accent-cyan); transform:translateY(-2px); box-shadow:0 6px 20px var(--glow-cyan); }
@media(max-width:768px) {
.header h1 { font-size:1.5em; }
.tabs { flex-wrap:wrap; }
.rec-grid { grid-template-columns:1fr; }
.stats-row { grid-template-columns:repeat(2,1fr); }
.model-info { grid-template-columns:1fr; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>.Agent Evolution</h1>
<div class="sub">Эволюция агентной системы APAW • Модели и рекомендации</div>
</div>
<div class="tabs">
<button class="tab-btn active" onclick="switchTab('overview')">Обзор</button>
<button class="tab-btn" onclick="switchTab('matrix')">Матрица</button>
<button class="tab-btn" onclick="switchTab('recs')">Рекомендации</button>
<button class="tab-btn" onclick="switchTab('history')">История</button>
<button class="tab-btn" onclick="switchTab('models')">Модели</button>
</div>
<div id="tab-overview" class="tab-panel active">
<div class="stats-row" id="statsRow"></div>
<div class="sec-hdr">
<h2>Конфигурация агентов</h2>
<span class="badge badge-cyan" id="agentsCount">0 агентов</span>
</div>
<div class="tbl-wrap">
<table class="dt">
<thead><tr>
<th>Агент</th>
<th>Модель</th>
<th>Провайдер</th>
<th>Fit</th>
<th>Статус</th>
</tr></thead>
<tbody id="agentsTable"></tbody>
</table>
</div>
</div>
<div id="tab-matrix" class="tab-panel">
<div class="hm-wrap">
<div class="hm-title">Матрица «Агент × Модель»</div>
<div class="hm-sub">Кликните на ячейку для подробностей • ★ = текущая модель</div>
<table class="hm-table" id="heatmapTable"></table>
</div>
</div>
<div id="tab-recs" class="tab-panel">
<div class="sec-hdr">
<h2>Рекомендации по оптимизации</h2>
<span class="badge badge-orange" id="recsCount">0 рекомен-й</span>
</div>
<div class="frow">
<button class="fbtn active" onclick="filterRecs('all',this)">Все</button>
<button class="fbtn" onclick="filterRecs('critical',this)">Критичные</button>
<button class="fbtn" onclick="filterRecs('high',this)">Высокие</button>
<button class="fbtn" onclick="filterRecs('medium',this)">Средние</button>
<button class="fbtn" onclick="filterRecs('optimal',this)">Оптимальные</button>
</div>
<div class="rec-grid" id="recsGrid"></div>
</div>
<div id="tab-history" class="tab-panel">
<div class="sec-hdr">
<h2>История изменений</h2>
<span class="badge badge-green" id="historyCount">0 изменений</span>
</div>
<div class="gitea-timeline" id="historyTimeline"></div>
</div>
<div id="tab-models" class="tab-panel">
<div class="sec-hdr">
<h2>Доступные модели</h2>
<span class="badge badge-cyan">Ollama + Groq + OpenRouter</span>
</div>
<div class="models-grid" id="modelsGrid"></div>
</div>
</div>
<div class="modal" id="modelModal">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title">
<span id="modalTitle">Модель</span>
<span class="prov-tag" id="modalProvider">Ollama</span>
</div>
<button class="modal-close" onclick="closeModal()">&times;</button>
</div>
<div class="modal-body">
<div class="model-info" id="modalInfo"></div>
<div class="model-tags" id="modalTags"></div>
<div style="margin-top:16px">
<h3 style="font-size:.95em;margin-bottom:10px">Агенты на этой модели</h3>
<div id="modalAgents" style="display:flex;flex-wrap:wrap;gap:8px"></div>
</div>
</div>
</div>
</div>
<script>
// ======================= EMBEDDED DATA =======================
const EMBEDDED_DATA = {
agents: {
"lead-developer": {current:{model:"ollama-cloud/qwen3-coder:480b",provider:"Ollama",category:"Core Dev",fit:92,desc:"Primary code writer",status:"optimal"}},
"frontend-developer": {current:{model:"ollama-cloud/qwen3-coder:480b",provider:"Ollama",category:"Core Dev",fit:90,desc:"UI implementation",status:"optimal"}},
"backend-developer": {current:{model:"ollama-cloud/qwen3-coder:480b",provider:"Ollama",category:"Core Dev",fit:91,desc:"Node.js/APIs",status:"optimal"}},
"go-developer": {current:{model:"ollama-cloud/qwen3-coder:480b",provider:"Ollama",category:"Core Dev",fit:85,desc:"Go backend",status:"optimal"}},
"sdet-engineer": {current:{model:"ollama-cloud/qwen3-coder:480b",provider:"Ollama",category:"QA",fit:88,desc:"TDD tests",status:"optimal"}},
"code-skeptic": {current:{model:"ollama-cloud/minimax-m2.5",provider:"Ollama",category:"QA",fit:85,desc:"Adversarial review",status:"good"}},
"security-auditor": {current:{model:"ollama-cloud/nemotron-3-super",provider:"Ollama",category:"Security",fit:80,desc:"OWASP scanner",status:"good"}},
"performance-engineer": {current:{model:"ollama-cloud/nemotron-3-super",provider:"Ollama",category:"Performance",fit:82,desc:"N+1 detection",status:"good"}},
"system-analyst": {current:{model:"ollama-cloud/glm-5",provider:"Ollama",category:"Analysis",fit:82,desc:"Architecture design",status:"good"}},
"requirement-refiner": {current:{model:"ollama-cloud/gpt-oss:120b",provider:"Ollama",category:"Analysis",fit:62,desc:"User Stories",status:"needs-update"}},
"history-miner": {current:{model:"ollama-cloud/glm-5",provider:"Ollama",category:"Analysis",fit:78,desc:"Git search",status:"good"}},
"capability-analyst": {current:{model:"ollama-cloud/gpt-oss:120b",provider:"Ollama",category:"Analysis",fit:66,desc:"Gap analysis",status:"needs-update"}},
"orchestrator": {current:{model:"ollama-cloud/glm-5",provider:"Ollama",category:"Process",fit:80,desc:"Task routing",status:"good"}},
"release-manager": {current:{model:"ollama-cloud/devstral-2:123b",provider:"Ollama",category:"Process",fit:75,desc:"Git ops",status:"good"}},
"evaluator": {current:{model:"ollama-cloud/nemotron-3-super",provider:"Ollama",category:"Process",fit:82,desc:"Scoring",status:"good"}},
"prompt-optimizer": {current:{model:"ollama-cloud/nemotron-3-super",provider:"Ollama",category:"Process",fit:80,desc:"Prompt improvement",status:"good"}},
"the-fixer": {current:{model:"ollama-cloud/minimax-m2.5",provider:"Ollama",category:"Fixes",fit:88,desc:"Bug fixing",status:"optimal"}},
"product-owner": {current:{model:"ollama-cloud/glm-5",provider:"Ollama",category:"Management",fit:76,desc:"Backlog",status:"good"}},
"workflow-architect": {current:{model:"ollama-cloud/glm-5",provider:"Ollama",category:"Process",fit:74,desc:"Workflow design",status:"good"}},
"markdown-validator": {current:{model:"ollama-cloud/nemotron-3-nano:30b",provider:"Ollama",category:"Validation",fit:72,desc:"Markdown check",status:"good"}},
"agent-architect": {current:{model:"ollama-cloud/gpt-oss:120b",provider:"Ollama",category:"Meta",fit:69,desc:"Agent design",status:"needs-update"}},
"planner": {current:{model:"ollama-cloud/nemotron-3-super",provider:"Ollama",category:"Cognitive",fit:84,desc:"Task planning",status:"good"}},
"reflector": {current:{model:"ollama-cloud/nemotron-3-super",provider:"Ollama",category:"Cognitive",fit:82,desc:"Self-reflection",status:"good"}},
"memory-manager": {current:{model:"ollama-cloud/nemotron-3-super",provider:"Ollama",category:"Cognitive",fit:90,desc:"Memory systems",status:"optimal"}},
"devops-engineer": {current:{model:null,provider:null,category:"DevOps",fit:0,desc:"Docker/K8s/CI",status:"new"}},
"flutter-developer": {current:{model:"ollama-cloud/qwen3-coder:480b",provider:"Ollama",category:"Core Dev",fit:86,desc:"Flutter mobile",status:"optimal"}}
},
models: {
"qwen3-coder:480b":{name:"Qwen3-Coder 480B",org:"Qwen",swe:66.5,ctx:"256K→1M",desc:"SOTA кодинг. Сравним с Claude Sonnet 4.",tags:["coding","agent","tools"]},
"minimax-m2.5":{name:"MiniMax M2.5",org:"MiniMax",swe:80.2,ctx:"128K",desc:"Лидер SWE-bench 80.2%",tags:["coding","agent"]},
"nemotron-3-super":{name:"Nemotron 3 Super",org:"NVIDIA",swe:60.5,ctx:"1M",ruler:91.75,desc:"RULER@1M 91.75%! PinchBench 85.6%",tags:["agent","reasoning","1M-ctx"]},
"nemotron-3-nano:30b":{name:"Nemotron 3 Nano",org:"NVIDIA",ctx:"128K",desc:"Ультра-компактная. Thinking mode.",tags:["efficient","thinking"]},
"glm-5":{name:"GLM-5",org:"Z.ai",ctx:"128K",desc:"Мощный reasoning",tags:["reasoning","agent"]},
"gpt-oss:120b":{name:"GPT-OSS 120B",org:"OpenAI",swe:62.4,ctx:"130K",desc:"O4-mini уровень. Apache 2.0.",tags:["reasoning","tools"]},
"devstral-2:123b":{name:"Devstral 2",org:"Mistral",ctx:"128K",desc:"Multi-file editing. Vision.",tags:["coding","vision"]}
},
recommendations: [
{agent:"requirement-refiner",from:"gpt-oss:120b",to:"nemotron-3-super",priority:"critical",quality:"+22%",context:"130K→1M",reason:"Nemotron с RULER@1M 91.75% значительно лучше для спецификаций."},
{agent:"capability-analyst",from:"gpt-oss:120b",to:"nemotron-3-super",priority:"critical",quality:"+21%",context:"130K→1M",reason:"Gap analysis требует агентских способностей. Nemotron (80 vs 66)."},
{agent:"agent-architect",from:"gpt-oss:120b",to:"nemotron-3-super",priority:"high",quality:"+19%",context:"130K→1M",reason:"Agent design с длинным контекстом. Nemotron (82 vs 69)."},
{agent:"history-miner",from:"glm-5",to:"nemotron-3-super",priority:"high",quality:"+13%",context:"128K→1M",reason:"Git history требует 1M контекст. Nemotron (88 vs 78)."},
{agent:"devops-engineer",from:"(не назначена)",to:"nemotron-3-super",priority:"critical",reason:"Новый агент. Nemotron 1M для docker-compose + k8s manifests."},
{agent:"prompt-optimizer",from:"nemotron-3-super",to:"qwen3.6-plus:free",priority:"high",quality:"+2%",reason:"FREE на OpenRouter. Terminal-Bench 61.6%"},
{agent:"memory-manager",from:"gpt-oss:120b",to:"nemotron-3-super",priority:"applied",quality:"+30%",context:"130K→1M",reason:"Уже применено. RULER@1M критичен для памяти."},
{agent:"evaluator",from:"gpt-oss:120b",to:"nemotron-3-super",priority:"applied",quality:"+15%",reason:"Уже применено. Nemotron оптимален для оценки."},
{agent:"the-fixer",from:"minimax-m2.5",to:"minimax-m2.5",priority:"optimal",reason:"MiniMax M2.5 (SWE 80.2%) уже оптимален для фиксов."},
{agent:"lead-developer",from:"qwen3-coder:480b",to:"qwen3-coder:480b",priority:"optimal",reason:"Qwen3-Coder (SWE 66.5%) оптимален для кодинга."}
],
history: [
{date:"2026-04-05T05:21:00Z",agent:"security-auditor",from:"deepseek-v3.2",to:"nemotron-3-super",reason:"RULER@1M для security"},
{date:"2026-04-05T05:21:00Z",agent:"performance-engineer",from:"gpt-oss:120b",to:"nemotron-3-super",reason:"Лучший reasoning"},
{date:"2026-04-05T05:21:00Z",agent:"memory-manager",from:"gpt-oss:120b",to:"nemotron-3-super",reason:"1M контекст критичен"},
{date:"2026-04-05T05:21:00Z",agent:"evaluator",from:"gpt-oss:120b",to:"nemotron-3-super",reason:"Оценка качества"},
{date:"2026-04-05T05:21:00Z",agent:"planner",from:"gpt-oss:120b",to:"nemotron-3-super",reason:"CoT/ToT планирование"},
{date:"2026-04-05T05:21:00Z",agent:"reflector",from:"gpt-oss:120b",to:"nemotron-3-super",reason:"Рефлексия"},
{date:"2026-04-05T05:21:00Z",agent:"system-analyst",from:"gpt-oss:120b",to:"glm-5",reason:"GLM-5 для архитектуры"},
{date:"2026-04-05T05:21:00Z",agent:"go-developer",from:"deepseek-v3.2",to:"qwen3-coder:480b",reason:"Qwen оптимален для Go"},
{date:"2026-04-05T05:21:00Z",agent:"markdown-validator",from:"qwen3.6-plus:free",to:"nemotron-3-nano:30b",reason:"Nano для лёгких задач"},
{date:"2026-04-05T05:21:00Z",agent:"prompt-optimizer",from:"qwen3.6-plus:free",to:"nemotron-3-super",reason:"Анализ промптов"},
{date:"2026-04-05T05:21:00Z",agent:"product-owner",from:"qwen3.6-plus:free",to:"glm-5",reason:"Управление backlog"}
],
lastUpdated:"2026-04-05T18:00:00Z"
};
// ======================= INITIALIZATION =======================
const agentData = EMBEDDED_DATA;
const modelData = EMBEDDED_DATA.models;
const recommendations = EMBEDDED_DATA.recommendations;
const historyData = EMBEDDED_DATA.history;
function init() {
renderStats();
renderAgentsTable();
renderHeatmap();
renderRecommendations();
renderHistory();
renderModels();
}
// ======================= RENDER FUNCTIONS =======================
function renderStats() {
const agents = Object.values(agentData.agents);
const total = agents.length;
const optimal = agents.filter(a => a.current.status === 'optimal').length;
const needsUpdate = agents.filter(a => a.current.status === 'needs-update').length;
const critical = recommendations.filter(r => r.priority === 'critical').length;
document.getElementById('statsRow').innerHTML = `
<div class="stat-card">
<div class="stat-label">Всего агентов</div>
<div class="stat-value grad-cyan">${total}</div>
<div class="stat-sub">${Object.keys(agentData.agents).filter(a => agentData.agents[a].current.status === 'optimal').length} оптимально</div>
</div>
<div class="stat-card">
<div class="stat-label">Требуют внимания</div>
<div class="stat-value grad-orange">${needsUpdate + critical}</div>
<div class="stat-sub">${critical} критичных</div>
</div>
<div class="stat-card">
<div class="stat-label">Провайдеров</div>
<div class="stat-value grad-green">3</div>
<div class="stat-sub">Ollama, Groq, OR</div>
</div>
<div class="stat-card">
<div class="stat-label">История</div>
<div class="stat-value grad-purple">${historyData.length}</div>
<div class="stat-sub">изменений записано</div>
</div>
`;
document.getElementById('agentsCount').textContent = total + ' агентов';
}
function renderAgentsTable() {
const rows = Object.entries(agentData.agents).map(([name, data]) => {
const model = data.current.model || 'не назначена';
const provider = data.current.provider || '—';
const fit = data.current.fit || 0;
const status = data.current.status || 'good';
const statusIcon = status === 'new' ? '🆕' :
status === 'needs-update' ? '⚠️' :
status === 'optimal' ? '✅' : '🟡';
const statusText = status === 'new' ? 'Новый' :
status === 'needs-update' ? 'Улучшить' :
status === 'optimal' ? 'Оптимально' : 'Хорошо';
const modelClass = model.includes('qwen') ? 'qwen' :
model.includes('minimax') ? 'minimax' :
model.includes('nemotron') ? 'nemotron' :
model.includes('glm') ? 'glm' :
model.includes('gpt-oss') ? 'gptoss' :
model.includes('devstral') ? 'devstral' : '';
return `
<tr onclick="showAgentModal('${name}')" style="cursor:pointer" onmouseover="this.style.background='var(--bg-card-hover)'" onmouseout="this.style.background=''">
<td style="font-weight:600">${name}</td>
<td><span class="mbadge ${modelClass}">${model}</span></td>
<td><span class="prov-tag ${provider?.toLowerCase()||''}">${provider}</span></td>
<td><div class="sbar"><div class="sbar-bg"><div class="sbar-fill ${getScoreClass(fit)}" style="width:${fit}%"></div></div><span class="snum">${fit}</span></div></td>
<td>${statusIcon} ${statusText}</td>
</tr>
`;
}).join('');
document.getElementById('agentsTable').innerHTML = rows;
}
function renderHeatmap() {
const agents = ['Core Dev', 'QA', 'Security', 'Analysis', 'Process', 'Cognitive', 'DevOps'];
const models = ['Qwen3-Coder', 'MiniMax M2.5', 'Nemotron', 'GLM-5', 'GPT-OSS'];
// Score matrix
const scores = [
[92, 82, 72, 68, 65], // Core Dev
[88, 85, 76, 72, 70], // QA
[75, 72, 90, 68, 65], // Security
[72, 68, 88, 82, 62], // Analysis
[78, 72, 85, 80, 65], // Process
[75, 70, 92, 78, 66], // Cognitive
[82, 68, 85, 75, 70], // DevOps
];
let html = '<thead><tr><th class="hm-role">Категория</th>';
models.forEach(m => html += `<th>${m}</th>`);
html += '</tr></thead><tbody>';
agents.forEach((cat, i) => {
html += `<tr><td class="hm-r">${cat}</td>`;
models.forEach((m, j) => {
const score = scores[i][j];
const isCurrent = (i === 0 && j === 0) || (i === 2 && j === 2) || (i === 3 && j === 3) || (i === 4 && j === 3) || (i === 5 && j === 2);
const style = `background:${getScoreColor(score)}15;color:${getScoreColor(score)}${isCurrent ? ';outline:2px solid var(--accent-cyan);outline-offset:-2px' : ''}`;
html += `<td style="${style}" onclick="showModelFromHeatmap('${m}')">${score}${isCurrent ? '<span style="color:#FFD700;font-size:.75em">★</span>' : ''}</td>`;
});
html += '</tr>';
});
html += '</tbody>';
document.getElementById('heatmapTable').innerHTML = html;
}
function renderRecommendations() {
document.getElementById('recsCount').textContent = recommendations.length + ' рекомендаций';
const html = recommendations.map(r => {
const priorityClass = r.priority === 'critical' ? 'critical' : r.priority === 'high' ? 'high' : r.priority === 'medium' ? 'medium' : 'optimal';
const priorityText = r.priority === 'critical' ? '🔴 Критично' :
r.priority === 'high' ? '🟠 Высокий' :
r.priority === 'medium' ? '🟡 Средний' : '✅ Оптимально';
return `
<div class="rec-card ${priorityClass}" data-priority="${r.priority}">
<div class="rec-hdr">
<span class="rec-agent">${r.agent}</span>
<span class="imp-badge ${priorityClass}">${priorityText}</span>
</div>
<div class="swap-vis">
<span class="swap-from">${r.from}</span>
<span class="swap-arrow">→</span>
<span class="swap-to">${r.to}</span>
</div>
<div class="rec-reason">${r.reason}</div>
</div>
`;
}).join('');
document.getElementById('recsGrid').innerHTML = html;
}
function renderHistory() {
document.getElementById('historyCount').textContent = historyData.length + ' изменений';
const html = historyData.map(h => `
<div class="gitea-item">
<div class="gitea-date">${formatDate(h.date)}</div>
<div class="gitea-content">
<span class="gitea-agent">${h.agent}</span>
<span class="gitea-change">: ${h.from}${h.to}</span>
</div>
<div style="font-size:.8em;color:var(--text-muted)">${h.reason}</div>
</div>
`).join('');
document.getElementById('historyTimeline').innerHTML = html;
}
function renderModels() {
const models = Object.values(modelData);
const html = models.map(m => `
<div class="mc" onclick="showModelModal('${m.name}')">
<div style="font-weight:700;font-size:1.05em">${m.name}</div>
<div style="font-size:.75em;color:var(--text-muted);margin:4px 0">${m.org} • Контекст: ${m.ctx}</div>
${m.swe ? `<div style="font-size:.8em"><span style="color:var(--text-muted)">SWE-bench:</span> <span style="color:var(--accent-green);font-weight:600">${m.swe}%</span></div>` : ''}
${m.ruler ? `<div style="font-size:.8em"><span style="color:var(--text-muted)">RULER@1M:</span> <span style="color:var(--accent-cyan);font-weight:600">${m.ruler}%</span></div>` : ''}
<div style="font-size:.78em;color:var(--text-secondary);margin-top:8px;line-height:1.4">${m.desc}</div>
<div style="margin-top:8px">${m.tags.map(t => `<span style="font-size:.68em;padding:2px 6px;background:rgba(0,212,255,.1);border-radius:12px;color:var(--accent-cyan);margin-right:4px">${t}</span>`).join('')}</div>
</div>
`).join('');
document.getElementById('modelsGrid').innerHTML = html;
}
// ======================= MODAL FUNCTIONS =======================
function showModelModal(modelName) {
const m = Object.values(modelData).find(m => m.name === modelName);
if (!m) return;
document.getElementById('modalTitle').textContent = m.name;
document.getElementById('modalProvider').textContent = m.org;
document.getElementById('modalInfo').innerHTML = `
<div class="model-info-item">
<div class="model-info-label">Организация</div>
<div class="model-info-value">${m.org}</div>
</div>
<div class="model-info-item">
<div class="model-info-label">Контекст</div>
<div class="model-info-value">${m.ctx}</div>
</div>
${m.swe ? `<div class="model-info-item">
<div class="model-info-label">SWE-bench</div>
<div class="model-info-value" style="color:var(--accent-green)">${m.swe}%</div>
</div>` : ''}
${m.ruler ? `<div class="model-info-item">
<div class="model-info-label">RULER@1M</div>
<div class="model-info-value" style="color:var(--accent-cyan)">${m.ruler}%</div>
</div>` : ''}
`;
document.getElementById('modalTags').innerHTML = m.tags.map(t => `<span class="model-tag">${t}</span>`).join('');
// Find agents using this model
const agentsUsing = Object.entries(agentData.agents)
.filter(([_, d]) => d.current.model?.includes(m.name.toLowerCase().split(' ')[0].toLowerCase()))
.map(([name, _]) => name);
document.getElementById('modalAgents').innerHTML = agentsUsing.length > 0
? agentsUsing.map(a => `<span class="mbadge">${a}</span>`).join('')
: '<span style="color:var(--text-muted)">Нет агентов на этой модели</span>';
document.getElementById('modelModal').classList.add('show');
}
function showAgentModal(agentName) {
const a = agentData.agents[agentName];
if (!a) return;
document.getElementById('modalTitle').textContent = agentName;
document.getElementById('modalProvider').textContent = a.current.provider || '—';
document.getElementById('modalInfo').innerHTML = `
<div class="model-info-item">
<div class="model-info-label">Модель</div>
<div class="model-info-value">${a.current.model || 'не назначена'}</div>
</div>
<div class="model-info-item">
<div class="model-info-label">Категория</div>
<div class="model-info-value">${a.current.category}</div>
</div>
<div class="model-info-item">
<div class="model-info-label">Fit Score</div>
<div class="model-info-value" style="color:${getScoreColor(a.current.fit)}">${a.current.fit || '—'}</div>
</div>
<div class="model-info-item">
<div class="model-info-label">Статус</div>
<div class="model-info-value">${a.current.status || '—'}</div>
</div>
`;
document.getElementById('modalTags').innerHTML = '';
document.getElementById('modalAgents').innerHTML = `<div style="color:var(--text-secondary);font-size:.9em">${a.current.desc}</div>`;
document.getElementById('modelModal').classList.add('show');
}
function showModelFromHeatmap(modelName) {
showModelModal(modelName);
}
function closeModal() {
document.getElementById('modelModal').classList.remove('show');
}
function filterRecs(filter, btn) {
document.querySelectorAll('.frow .fbtn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
if (filter === 'all') {
document.querySelectorAll('.rec-card').forEach(c => c.style.display = '');
} else {
document.querySelectorAll('.rec-card').forEach(c => {
c.style.display = c.dataset.priority === filter ? '' : 'none';
});
}
}
// ======================= UTILITIES =======================
function getScoreColor(score) {
if (score >= 85) return '#00ff94';
if (score >= 70) return '#ffc048';
return '#ff6b81';
}
function getScoreClass(score) {
if (score >= 85) return 'h';
if (score >= 70) return 'm';
return 'l';
}
function formatDate(dateStr) {
const date = new Date(dateStr);
return date.toLocaleDateString('ru-RU', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });
}
function switchTab(tabId) {
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.getElementById('tab-' + tabId).classList.add('active');
event.target.classList.add('active');
}
document.getElementById('modelModal').addEventListener('click', (e) => {
if (e.target.id === 'modelModal') closeModal();
});
// Initialize
init();
</script>
</body>
</html>

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env node
/**
* Build standalone HTML with embedded data
* Run: node agent-evolution/scripts/build-standalone.cjs
*/
const fs = require('fs');
const path = require('path');
const DATA_FILE = path.join(__dirname, '../data/agent-versions.json');
const HTML_FILE = path.join(__dirname, '../index.html');
const OUTPUT_FILE = path.join(__dirname, '../index.standalone.html');
try {
// Read data
console.log('📖 Reading data from:', DATA_FILE);
const data = JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
console.log(' Found', Object.keys(data.agents).length, 'agents');
// Read HTML
console.log('📖 Reading HTML from:', HTML_FILE);
let html = fs.readFileSync(HTML_FILE, 'utf-8');
// Step 1: Replace EMBEDDED_DATA
const startMarker = '// Default embedded data (minimal - updated by sync script)';
const endPattern = /"sync_sources":\s*\[[^\]]*\]\s*\}\s*\};/;
const startIdx = html.indexOf(startMarker);
const endMatch = html.match(endPattern);
if (startIdx === -1) {
throw new Error('Start marker not found in HTML');
}
if (!endMatch) {
throw new Error('End pattern not found in HTML');
}
const endIdx = endMatch.index + endMatch[0].length + 1;
// Create embedded data
const embeddedData = `// Embedded data (generated ${new Date().toISOString()})
const EMBEDDED_DATA = ${JSON.stringify(data, null, 2)};`;
// Replace the section
html = html.substring(0, startIdx) + embeddedData + html.substring(endIdx);
// Step 2: Replace entire init function
// Find the init function start and end
const initStartPattern = /\/\/ Initialize\s*\n\s*async function init\(\) \{/;
const initStartMatch = html.match(initStartPattern);
if (initStartMatch) {
const initStartIdx = initStartMatch.index;
// Find matching closing brace (count opening and closing)
let braceCount = 0;
let inFunction = false;
let initEndIdx = initStartIdx;
for (let i = initStartIdx; i < html.length; i++) {
if (html[i] === '{') {
braceCount++;
inFunction = true;
} else if (html[i] === '}') {
braceCount--;
if (inFunction && braceCount === 0) {
initEndIdx = i + 1;
break;
}
}
}
// New init function
const newInit = `// Initialize
async function init() {
// Use embedded data directly (works with file://)
agentData = EMBEDDED_DATA;
try {
document.getElementById('lastSync').textContent = formatDate(agentData.lastUpdated);
document.getElementById('agentCount').textContent = agentData.evolution_metrics.total_agents + ' agents';
document.getElementById('historyCount').textContent = agentData.evolution_metrics.agents_with_history + ' with history';
if (agentData.evolution_metrics.total_agents === 0) {
document.getElementById('lastSync').textContent = 'No data - run sync:evolution';
return;
}
renderOverview();
renderAllAgents();
renderTimeline();
renderRecommendations();
renderMatrix();
} catch (error) {
console.error('Failed to render dashboard:', error);
document.getElementById('lastSync').textContent = 'Error rendering data';
}
}`;
html = html.substring(0, initStartIdx) + newInit + html.substring(initEndIdx);
}
// Write output
fs.writeFileSync(OUTPUT_FILE, html);
console.log('\n✅ Built standalone dashboard');
console.log(' Output:', OUTPUT_FILE);
console.log(' Agents:', Object.keys(data.agents).length);
console.log(' Size:', (fs.statSync(OUTPUT_FILE).size / 1024).toFixed(1), 'KB');
console.log('\n📊 Open in browser:');
console.log(' Windows: start agent-evolution\\index.standalone.html');
console.log(' macOS: open agent-evolution/index.standalone.html');
console.log(' Linux: xdg-open agent-evolution/index.standalone.html');
} catch (error) {
console.error('❌ Error:', error.message);
process.exit(1);
}

View File

@@ -0,0 +1,501 @@
#!/usr/bin/env bun
/**
* Agent Evolution Synchronization Script
* Parses git history and syncs agent definitions
*
* Usage: bun run agent-evolution/scripts/sync-agent-history.ts
*
* Generates:
* - data/agent-versions.json - JSON data
* - index.standalone.html - Dashboard with embedded data
*/
import * as fs from "fs";
import * as path from "path";
import { spawnSync } from "child_process";
// Try to load yaml parser (optional)
let yaml: any;
try {
yaml = require("yaml");
} catch {
yaml = null;
}
// Types
interface AgentVersion {
date: string;
commit: string;
type: "model_change" | "prompt_change" | "agent_created" | "agent_removed" | "capability_change";
from: string | null;
to: string;
reason: string;
source: "git" | "gitea" | "manual";
}
interface AgentConfig {
model: string;
provider: string;
category: string;
mode: string;
color: string;
description: string;
benchmark?: {
swe_bench?: number;
ruler_1m?: number;
terminal_bench?: number;
pinch_bench?: number;
fit_score?: number;
};
capabilities: string[];
recommendations?: Array<{
target: string;
reason: string;
priority: string;
}>;
status?: string;
}
interface AgentData {
current: AgentConfig;
history: AgentVersion[];
performance_log: Array<{
date: string;
issue: number;
score: number;
duration_ms: number;
success: boolean;
}>;
}
interface EvolutionData {
version: string;
lastUpdated: string;
agents: Record<string, AgentData>;
providers: Record<string, { models: unknown[] }>;
evolution_metrics: {
total_agents: number;
agents_with_history: number;
pending_recommendations: number;
last_sync: string;
sync_sources: string[];
};
}
// Constants
const AGENTS_DIR = ".kilo/agents";
const CAPABILITY_INDEX = ".kilo/capability-index.yaml";
const KILO_CONFIG = ".kilo/kilo.jsonc";
const OUTPUT_FILE = "agent-evolution/data/agent-versions.json";
const GIT_DIR = ".git";
// Provider detection
function detectProvider(model: string): string {
if (model.startsWith("ollama-cloud/") || model.startsWith("ollama/")) return "Ollama";
if (model.startsWith("openrouter/") || model.includes("openrouter")) return "OpenRouter";
if (model.startsWith("groq/")) return "Groq";
return "Unknown";
}
// Parse agent file frontmatter
function parseAgentFrontmatter(content: string): AgentConfig | null {
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontmatterMatch) return null;
try {
const frontmatter = frontmatterMatch[1];
const lines = frontmatter.split("\n");
const config: Record<string, unknown> = {};
for (const line of lines) {
const match = line.match(/^(\w+):\s*(.+)$/);
if (match) {
const [, key, value] = match;
if (value === "allow" || value === "deny") {
if (!config.permission) config.permission = {};
(config.permission as Record<string, unknown>)[key] = value;
} else if (key === "model") {
config[key] = value;
config.provider = detectProvider(value);
} else {
config[key] = value;
}
}
}
return config as unknown as AgentConfig;
} catch {
return null;
}
}
// Get git history for agent changes
function getGitHistory(): Map<string, AgentVersion[]> {
const history = new Map<string, AgentVersion[]>();
try {
// Get commits that modified agent files
const result = spawnSync('git', ['log', '--all', '--oneline', '--follow', '--format=%H|%ai|%s', '--', '.kilo/agents/'], {
cwd: process.cwd(),
encoding: 'utf-8',
maxBuffer: 10 * 1024 * 1024
});
if (result.status !== 0 || !result.stdout) {
console.warn('Git log failed, skipping history');
return history;
}
const logOutput = result.stdout.trim();
const commits = logOutput.split('\n').filter(Boolean);
for (const line of commits) {
const [hash, date, ...msgParts] = line.split('|');
if (!hash || !date) continue;
const message = msgParts.join('|').trim();
// Detect change type from commit message
const agentMatch = message.match(/(?:add|update|fix|feat|change|set)\s+(\w+-?\w*)/i);
if (agentMatch) {
const agentName = agentMatch[1].toLowerCase();
const type = message.toLowerCase().includes("add") || message.toLowerCase().includes("feat")
? "agent_created"
: message.toLowerCase().includes("model")
? "model_change"
: "prompt_change";
if (!history.has(agentName)) {
history.set(agentName, []);
}
history.get(agentName)!.push({
date: date.replace(" ", "T") + "Z",
commit: hash.substring(0, 8),
type: type as AgentVersion["type"],
from: null, // Will be filled later
to: "", // Will be filled later
reason: message,
source: "git"
});
}
}
} catch (error) {
console.warn("Git history extraction failed:", error);
}
return history;
}
// Load capability index (simple parsing without yaml dependency)
function loadCapabilityIndex(): Record<string, AgentConfig> {
const configs: Record<string, AgentConfig> = {};
try {
const content = fs.readFileSync(CAPABILITY_INDEX, "utf-8");
// Simple YAML-ish parsing for our specific format
// Extract agent blocks
const agentRegex = /^ (\w[\w-]+):\n((?: .+\n?)+)/gm;
let match;
while ((match = agentRegex.exec(content)) !== null) {
const name = match[1];
if (name === 'capability_routing' || name === 'parallel_groups' ||
name === 'iteration_loops' || name === 'quality_gates' ||
name === 'workflow_states') continue;
const block = match[2];
// Extract model
const modelMatch = block.match(/model:\s*(.+)/);
if (!modelMatch) continue;
const model = modelMatch[1].trim();
// Extract capabilities
const capsMatch = block.match(/capabilities:\n((?: - .+\n?)+)/);
const capabilities = capsMatch
? capsMatch[1].split('\n').filter(l => l.trim()).map(l => l.replace(/^\s*-?\s*/, '').trim())
: [];
// Extract mode
const modeMatch = block.match(/mode:\s*(\w+)/);
const mode = modeMatch ? modeMatch[1] : 'subagent';
configs[name] = {
model,
provider: detectProvider(model),
category: capabilities[0]?.replace(/_/g, ' ') || 'General',
mode,
color: '#6B7280',
description: '',
capabilities,
};
}
} catch (error) {
console.warn("Capability index loading failed:", error);
}
return configs;
}
// Load kilo.jsonc configuration
function loadKiloConfig(): Record<string, AgentConfig> {
const configs: Record<string, AgentConfig> = {};
try {
const content = fs.readFileSync(KILO_CONFIG, "utf-8");
// Remove comments for JSON parsing
const cleaned = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "");
const parsed = JSON.parse(cleaned);
if (parsed.agent) {
for (const [name, config] of Object.entries(parsed.agent)) {
const agentConfig = config as Record<string, unknown>;
if (agentConfig.model) {
configs[name] = {
model: agentConfig.model as string,
provider: detectProvider(agentConfig.model as string),
category: "Built-in",
mode: (agentConfig.mode as string) || "primary",
color: "#3B82F6",
description: (agentConfig.description as string) || "",
capabilities: [],
};
}
}
}
} catch (error) {
console.warn("Kilo config loading failed:", error);
}
return configs;
}
// Load all agent files
function loadAgentFiles(): Record<string, AgentConfig> {
const configs: Record<string, AgentConfig> = {};
try {
const files = fs.readdirSync(AGENTS_DIR);
for (const file of files) {
if (!file.endsWith(".md")) continue;
const filepath = path.join(AGENTS_DIR, file);
const content = fs.readFileSync(filepath, "utf-8");
const frontmatter = parseAgentFrontmatter(content);
if (frontmatter && frontmatter.model) {
const name = file.replace(".md", "");
configs[name] = {
...frontmatter,
category: getCategoryFromCapabilities(frontmatter.capabilities),
};
}
}
} catch (error) {
console.warn("Agent files loading failed:", error);
}
return configs;
}
// Get category from capabilities
function getCategoryFromCapabilities(capabilities?: string[]): string {
if (!capabilities) return "General";
const categoryMap: Record<string, string> = {
code: "Core Dev",
ui: "Frontend",
test: "QA",
security: "Security",
performance: "Performance",
devops: "DevOps",
go_: "Go Development",
flutter: "Mobile",
memory: "Cognitive",
plan: "Cognitive",
workflow: "Process",
markdown: "Validation",
};
for (const cap of capabilities) {
const key = Object.keys(categoryMap).find((k) => cap.toLowerCase().includes(k.toLowerCase()));
if (key) return categoryMap[key];
}
return "General";
}
// Merge all sources
function mergeConfigs(
agentFiles: Record<string, AgentConfig>,
capabilityIndex: Record<string, AgentConfig>,
kiloConfig: Record<string, AgentConfig>
): Record<string, AgentConfig> {
const merged: Record<string, AgentConfig> = {};
// Start with agent files (highest priority)
for (const [name, config] of Object.entries(agentFiles)) {
merged[name] = { ...config };
}
// Overlay capability index data
for (const [name, config] of Object.entries(capabilityIndex)) {
if (merged[name]) {
merged[name] = {
...merged[name],
capabilities: config.capabilities,
};
} else {
merged[name] = config;
}
}
// Overlay kilo.jsonc data
for (const [name, config] of Object.entries(kiloConfig)) {
if (merged[name]) {
merged[name] = {
...merged[name],
model: config.model,
provider: config.provider,
};
} else {
merged[name] = config;
}
}
return merged;
}
// Main sync function
async function sync() {
console.log("🔄 Syncing agent evolution data...\n");
// Load all sources
console.log("📂 Loading agent files...");
const agentFiles = loadAgentFiles();
console.log(` Found ${Object.keys(agentFiles).length} agent files`);
console.log("📄 Loading capability index...");
const capabilityIndex = loadCapabilityIndex();
console.log(` Found ${Object.keys(capabilityIndex).length} agents`);
console.log("⚙️ Loading kilo config...");
const kiloConfig = loadKiloConfig();
console.log(` Found ${Object.keys(kiloConfig).length} agents`);
// Get git history
console.log("\n📜 Parsing git history...");
const gitHistory = await getGitHistory();
console.log(` Found history for ${gitHistory.size} agents`);
// Merge configs
const merged = mergeConfigs(agentFiles, capabilityIndex, kiloConfig);
// Load existing evolution data
let existingData: EvolutionData = {
version: "1.0.0",
lastUpdated: new Date().toISOString(),
agents: {},
providers: {
Ollama: { models: [] },
OpenRouter: { models: [] },
Groq: { models: [] },
},
evolution_metrics: {
total_agents: 0,
agents_with_history: 0,
pending_recommendations: 0,
last_sync: new Date().toISOString(),
sync_sources: ["git", "capability-index.yaml", "kilo.jsonc"],
},
};
try {
if (fs.existsSync(OUTPUT_FILE)) {
const existing = JSON.parse(fs.readFileSync(OUTPUT_FILE, "utf-8"));
existingData.agents = existing.agents || {};
}
} catch {
// Use defaults
}
// Update agents
for (const [name, config] of Object.entries(merged)) {
const existingAgent = existingData.agents[name];
// Check if model changed
if (existingAgent?.current?.model && existingAgent.current.model !== config.model) {
// Add to history
existingAgent.history.push({
date: new Date().toISOString(),
commit: "sync",
type: "model_change",
from: existingAgent.current.model,
to: config.model,
reason: "Model update from sync",
source: "git",
});
existingAgent.current = { ...config };
} else {
existingData.agents[name] = {
current: config,
history: existingAgent?.history || gitHistory.get(name) || [],
performance_log: existingAgent?.performance_log || [],
};
}
}
// Update metrics
existingData.evolution_metrics.total_agents = Object.keys(existingData.agents).length;
existingData.evolution_metrics.agents_with_history = Object.values(existingData.agents).filter(
(a) => a.history.length > 0
).length;
existingData.evolution_metrics.pending_recommendations = Object.values(existingData.agents).filter(
(a) => a.current.recommendations && a.current.recommendations.length > 0
).length;
existingData.evolution_metrics.last_sync = new Date().toISOString();
// Save JSON
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(existingData, null, 2));
console.log(`\n✅ Synced ${existingData.evolution_metrics.total_agents} agents to ${OUTPUT_FILE}`);
// Generate standalone HTML
generateStandalone(existingData);
// Print summary
console.log("\n📊 Summary:");
console.log(` Total agents: ${existingData.evolution_metrics.total_agents}`);
console.log(` Agents with history: ${existingData.evolution_metrics.agents_with_history}`);
console.log(` Pending recommendations: ${existingData.evolution_metrics.pending_recommendations}`);
}
/**
* Generate standalone HTML with embedded data
*/
function generateStandalone(data: EvolutionData): void {
const templatePath = path.join(__dirname, '../index.html');
const outputPath = path.join(__dirname, '../index.standalone.html');
let html = fs.readFileSync(templatePath, 'utf-8');
// Replace EMBEDDED_DATA with actual data
const embeddedDataStr = `const EMBEDDED_DATA = ${JSON.stringify(data, null, 2)};`;
// Find and replace the EMBEDDED_DATA declaration
html = html.replace(
/const EMBEDDED_DATA = \{[\s\S]*?\};?\s*\/\/ Initialize/,
embeddedDataStr + '\n\n// Initialize'
);
fs.writeFileSync(outputPath, html);
console.log(`📄 Generated standalone: ${outputPath}`);
console.log(` File size: ${(fs.statSync(outputPath).size / 1024).toFixed(1)} KB`);
}
// Run
sync().catch(console.error);

View File

@@ -0,0 +1,57 @@
# Docker Compose for Agent Evolution Dashboard
# Usage: docker-compose -f docker-compose.evolution.yml up -d
version: '3.8'
services:
evolution-dashboard:
build:
context: .
dockerfile: agent-evolution/Dockerfile
target: production
container_name: apaw-evolution
ports:
- "3001:3001"
volumes:
# Mount data directory for live updates
- ./agent-evolution/data:/app/data:ro
# Mount for reading source files (optional, for sync)
- ./.kilo/agents:/app/kilo/agents:ro
- ./.kilo/capability-index.yaml:/app/kilo/capability-index.yaml:ro
- ./.kilo/kilo.jsonc:/app/kilo/kilo.jsonc:ro
environment:
- NODE_ENV=production
- TZ=UTC
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
- evolution-network
labels:
- "com.apaw.service=evolution-dashboard"
- "com.apaw.description=Agent Evolution Dashboard"
# Optional: Nginx reverse proxy with SSL
evolution-nginx:
image: nginx:alpine
container_name: apaw-evolution-nginx
profiles:
- nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./agent-evolution/nginx.conf:/etc/nginx/nginx.conf:ro
- ./agent-evolution/ssl:/etc/nginx/ssl:ro
depends_on:
- evolution-dashboard
networks:
- evolution-network
networks:
evolution-network:
driver: bridge

View File

@@ -20,7 +20,16 @@
"dev": "tsc --watch",
"clean": "rm -rf dist",
"typecheck": "tsc --noEmit",
"test": "bun test"
"test": "bun test",
"sync:evolution": "bun run agent-evolution/scripts/sync-agent-history.ts && node agent-evolution/scripts/build-standalone.cjs",
"evolution:build": "node agent-evolution/scripts/build-standalone.cjs",
"evolution:open": "start agent-evolution/index.standalone.html",
"evolution:dashboard": "bunx serve agent-evolution -l 3001",
"evolution:run": "docker run -d --name apaw-evolution-dashboard -p 3001:3001 -v \"$(pwd)/agent-evolution/data:/app/data:ro\" apaw-evolution:latest",
"evolution:stop": "docker stop apaw-evolution-dashboard && docker rm apaw-evolution-dashboard",
"evolution:start": "bash agent-evolution/docker-run.sh run",
"evolution:dev": "docker-compose -f docker-compose.evolution.yml up -d",
"evolution:logs": "docker logs -f apaw-evolution-dashboard"
},
"dependencies": {
"zod": "^3.24.1"