overmind-mcp
Version:
Orchestrateur universel agents IA multi-modeles via MCP. Inclut le protocole 'Custom-Nickname' pour identifier vos agents avec des surnoms originaux (The Chaos Prophet, Shadow Sniper, etc.), l'isolation mémoire (Private Memory Context) et le support pour
545 lines (419 loc) • 22.7 kB
Markdown
# Migration Overmind v4.0 — Wrapper Multi-Agent Persistant
> **Statut** : Plan de migration détaillé (4 phases)
> **Date** : 2026-07-10
> **Source** : v3.7.0 → v4.0.0
> **Pré-requis** : v3.7.0 publié (✅ en cours de pipeline)
---
## 🎯 Vision : pourquoi pivoter
### Le problème actuel (v3.7.0)
OverMind est positionné "orchestrateur universel" mais il fait 2 choses contradictoires :
1. **Wrapper multi-runner** (utile) — spawn Claude/Hermes/Kilo/Gemini, gère profils
2. **Couche d'orchestration centrale** (redondante avec Hermes) — dispatcher, kanban adapter, scenarios
La couche (2) réinvente des choses qu'Hermes fait **nativement** depuis la v0.13+ :
- Kanban natif (`hermes kanban`)
- Dispatcher (loop gateway 60s tick)
- Pool credentials avec rotation round-robin
- Sessions persistantes par profile
**Résultat** : 2 sources de vérité pour les mêmes concepts, dette technique, divergence de comportement.
### La cible (v4.0)
**OverMind = factory à agents persistants. Point.**
```
┌─────────────────────────────────────────────────────────────┐
│ OVERMIND v4.0 WRAPPER │
│ │
│ 1 process FastMCP sur :3099 — pure factory │
│ │
│ In: create_agent(name, prompt, model) │
│ Out: profil Hermes autonome + mémoire + state + skills │
│ │
│ Puis l'agent vit sa vie via: │
│ • run_agent (invoke direct) │
│ • a2a_hub (HTTP peer-to-peer) │
│ • bridge par-agent (1 port = 1 agent, peer-to-peer) │
└─────────────────────────────────────────────────────────────┘
```
**Ce qui disparaît d'Overmind** (laissé à Hermes, sauf Kanban qui devient optionnel) :
- ❌ Kanban central → **`kanban_hub` MCP thin wrapper OPTIONNEL** (défaut OFF, opt-in via `OVERMIND_KANBAN_ENABLED=1`)
- ❌ Dispatcher → gateway loop natif
- ❌ ScenarioLoader → kanban pipeline natif (uniquement si Kanban activé)
- ❌ YOLO_CONFIG retry/circuit → dispatcher natif
- ❌ pgvector memory central → `memory.provider: postgres` dans chaque profil
**Ce qui reste d'Overmind** (son vrai rôle) :
- ✅ `create_agent` — thin wrapper sur `hermes profile create`
- ✅ `list_agents` / `delete_agent` / `update_agent_config` / `get_agent_configs`
- ✅ `run_agent` / `run_agents_parallel` — multi-runner (Claude, Kilo, etc.)
- ✅ `a2a_hub` — inter-agent HTTP (déjà peer-to-peer)
- ✅ `agent_control` — process lifecycle
- ✅ `memory_search` / `memory_store` — fallback pour les anciens clients
- 🔶 `kanban_hub` — **OPTIONNEL** (désactivé par défaut), uniquement si Kanban est dans le scope utilisateur
---
## 📐 Architecture cible
```
┌────────────────────────────────────────────────────────────────┐
│ v3.7.0 (actuel) — Orchestrateur central │
│ │
│ Client → MCP:3099 (Overmind) → orchestration/dispatcher │
│ → KanbanAdapter │
│ → ScenarioLoader │
│ → HermesRunner spawn │
└────────────────────────────────────────────────────────────────┘
↓ REFACTOR ↓
┌────────────────────────────────────────────────────────────────┐
│ v4.0 (cible) — Wrapper factory │
│ │
│ Client → MCP:3099 (Overmind factory) │
│ ├─ create_agent(name) → hermes profile create │
│ ├─ run_agent(name) → HTTP :8642 + X-Hermes-Profile │
│ ├─ a2a_hub → HTTP peer-to-peer │
│ └─ kanban_hub → hermes kanban CLI [OPTIONNEL] │
│ │
│ Hermes Gateway (:8642, multi-profile) │
│ Hermes Pool (credentials natifs, rotation auto) │
└────────────────────────────────────────────────────────────────┘
> **Kanban est optionnel** : `kanban_hub` n'est exposé que si `OVERMIND_KANBAN_ENABLED=1`.
> Défaut = OFF pour rester minimaliste. Les utilisateurs qui n'en ont pas besoin
> ne tirent jamais la dépendance `hermes kanban` et n'ont pas la surface d'attaque.
```
### Différence conceptuelle clé
| Aspect | v3.7.0 orchestrateur | v4.0 wrapper |
|--------|----------------------|--------------|
| **Source of truth agents** | Overmind registry + db | Profils Hermes natifs (`~/.hermes/profiles/<name>/`) |
| **Source of truth state** | PostgreSQL central | `state.db` par profile (SQLite Hermes) |
| **Source of truth memory** | pgvector central | `memory.provider: postgres` par profile OU pgvector partagé en fallback |
| **Source of truth credentials** | `.env` par profile | Pool Hermes natif (`hermes auth add`) |
| **Source of truth workflows** | ScenarioLoader.json | Kanban Hermes |
| **Communication inter-agent** | Bridge central → RPC | HTTP peer-to-peer via bridge par-agent |
| **Échelle** | Limitée par le central | N agents, 1 process chacun, linéaire |
---
## 🗺️ Phases d'implémentation
### **Phase 1a — Audit & nettoyage minimal (semaine 1)** ⚡ OBLIGATOIRE
**Objectif** : supprimer le bloc `dispatcher + YOLO_CONFIG` (le seul vrai code d'orchestration custom). **Garde `KanbanAdapter.ts` et `ScenarioLoader.ts` intacts** pour ne rien casser aux utilisateurs Kanban existants.
#### 1a.1 Suppressions (SEULEMENT le couple dispatcher/YOLO)
| Fichier | LOC | Remplacé par | Risque |
|---------|-----|-------------|--------|
| `src/lib/orchestration/dispatcher.ts` | 283 | Gateway loop Hermes | Faible — seul `index.ts` + `run_agents_parallel.ts` |
| `YOLO_CONFIG` (inline) | ~50 | retry/circuit-breaker Hermes | Aucun — config isolée |
**Garde-fous** :
- `src/services/KanbanAdapter.ts` ← **CONSERVÉ** (utilisé si Kanban activé)
- `src/bridge/ScenarioLoader.ts` ← **CONSERVÉ** (utilisé si Kanban activé)
#### 1a.2 Étapes concrètes
```bash
# 1. Backup l'état actuel
git checkout -b chore/v4-phase1a-cleanup
git tag v3.7.0-baseline
# 2. Supprimer seulement dispatcher.ts et YOLO_CONFIG
rm src/lib/orchestration/dispatcher.ts
rmdir src/lib/orchestration
# Fix imports dans src/index.ts, src/tools/run_agents_parallel.ts
grep -r "YOLO" src/ # doit retourner 0 résultat
# 3. Build + tests
pnpm run rebuild && pnpm run test && pnpm run lint
# 4. Merge
git checkout main
git merge --no-ff chore/v4-phase1a-cleanup
git tag v4.0.0-phase1a
```
#### 1a.3 Critères de succès Phase 1a
| Métrique | Avant | Après |
|----------|-------|-------|
| Services files | 17 | 17 (inchangé — 0 suppression) |
| LOC total src/ | ~12,500 | ~12,200 (-300) |
| Concept "dispatcher" présent | 1 fichier | 0 |
| Tests passent | 68 | 68 |
---
### **Phase 1b — Suppression Kanban historique (semaine 1+) — OPTIONNEL**
> **Statut** : À faire **UNIQUEMENT** si on confirme via télémétrie/usage
> que personne n'utilise Kanban via Overmind (peu probable — Nexus l'utilise).
> Si Kanban est utilisé par ≥1 user → **sauter cette phase**, garder le code legacy.
**Pré-requis avant suppression** :
- ✅ Phase 2 livrée (`kanban_hub` MCP tool disponible)
- ✅ Au moins 1 release avec Kanban en opt-in sans régression
- ✅ Communication utilisateurs (CHANGELOG deprecation notice, 1 release de grâce)
Si OK pour suppression, déplacer `KanbanAdapter.ts` + `ScenarioLoader.ts` derrière le flag `OVERMIND_KANBAN_ENABLED=1` (déjà ce que fait Phase 2), puis supprimer après 1 release.
#### 1b.1 Suppressions conditionnelles
| Fichier | LOC | Remplacé par | Risque |
|---------|-----|-------------|--------|
| `src/services/KanbanAdapter.ts` | 420 | `tools/kanban_hub.ts` opt-in (Phase 2) | Moyen — utilisé par `index.ts`, `ScenarioLoader.ts` |
| `src/bridge/ScenarioLoader.ts` | 508 | `hermes kanban pipeline` | Moyen — utilisé par `overmind-bridge.ts` |
---
### **Phase 2 — Support Kanban OPTIONNEL (opt-in) (semaine 2)**
> **Statut** : À faire UNIQUEMENT si des utilisateurs demandent Kanban.
> Si personne ne l'utilise, sauter cette phase. Les autres phases (1, 3, 4) ne
> dépendent pas de Kanban.
**Opt-in** : `OVERMIND_KANBAN_ENABLED=1` dans `.env` (ou flag CLI `--with-kanban`).
**Objectif** : remplacer `KanbanAdapter` par 1 outil MCP unifié, conforme au pattern `a2a_hub` / `agent_control`. Uniquement chargé si activé.
#### 2.1 Spécification de l'outil
```typescript
// src/tools/kanban_hub.ts
import { z } from 'zod';
export const kanbanHubTool = {
name: 'kanban_hub',
description: `Hermes Kanban — manage tasks, boards and pipelines.
Actions: init, create, list, show, update, complete, block, unblock,
comment, assign, link, dispatch, stats, archive, boards, watch.`,
inputSchema: z.object({
action: z.enum([
'init', 'create', 'list', 'show', 'update', 'complete',
'block', 'unblock', 'comment', 'assign', 'link',
'dispatch', 'stats', 'archive', 'boards', 'watch',
]),
// Champs communs
board: z.string().optional(), // board name (multi-board)
taskId: z.string().optional(), // t_abc123
// Champs spécifiques par action
title: z.string().optional(),
body: z.string().optional(),
assignee: z.string().optional(),
parents: z.array(z.string()).optional(),
reason: z.string().optional(),
status: z.enum(['pending', 'running', 'blocked', 'done', 'archived']).optional(),
agentName: z.string().optional(),
due: z.string().optional(),
tags: z.array(z.string()).optional(),
}),
handler: async (args) => {
// Wraps `hermes kanban <subcommand>` CLI
return await HermesKanbanClient.execute(args);
},
};
```
#### 2.2 Implémentation — `HermesKanbanClient`
```typescript
// src/services/HermesKanbanClient.ts
export class HermesKanbanClient {
static async execute(args: KanbanArgs): Promise<KanbanResult> {
const cmd = this.buildCommand(args);
const { stdout, stderr } = await execAsync(cmd);
return this.parseOutput(args.action, stdout, stderr);
}
static buildCommand(args: KanbanArgs): string {
const sub = `${args.board ? `--board "${args.board}" ` : ''}${args.action}`;
const flags = this.buildFlags(args);
return `hermes kanban ${sub} ${flags}`.trim();
}
static parseOutput(action: string, stdout: string, stderr: string): KanbanResult {
// Parse le format tabulaire d'`hermes kanban list/show`, JSON pour le reste
return { success: !stderr, action, data: this.parseStructured(action, stdout) };
}
}
```
#### 2.3 Migration des callers
| Caller actuel | Devient |
|--------------|---------|
| `index.ts` export de `kanbanAdapter` | Export de `HermesKanbanClient` |
| Appels directs `kanban.create(...)` | `kanban_hub({ action: 'create', ... })` |
| `ScenarioLoader.load(path)` | `kanban_hub({ action: 'load', path })` (futur) |
#### 2.4 Tests
- [ ] `kanban_hub({ action: 'list' })` retourne les tâches
- [ ] `kanban_hub({ action: 'create', title, assignee })` crée une tâche
- [ ] Erreurs explicites si `hermes kanban` CLI échoue
- [ ] Format de sortie cohérent avec les autres tools `*_hub`
#### 2.5 Critères de succès Phase 2
- [ ] `KanbanAdapter.ts` supprimé
- [ ] `tools/kanban_hub.ts` créé avec 16 actions
- [ ] Tests passent 68 → 75+
- [ ] `tools/list` montre 15 outils au lieu de 14 (+1 kanban_hub)
---
### **Phase 3 — Decentralized bridges + multi-profile gateway (semaine 3-4)**
**Objectif** : passer d'un `OverBridgeServer` centralisé à un bridge par agent (déjà documenté dans `overmind-bridge-guide.md` mais pas encore implémenté).
#### 3.1 État cible
```
Agent 1 = profil Hermes "alpha"
├─ hermes gateway --profile alpha --port 8742 (1 instance par agent)
└─ overmind-bridge --agent alpha --port 3101
Agent 2 = profil Hermes "beta"
├─ hermes gateway --profile beta --port 8743
└─ overmind-bridge --agent beta --port 3102
```
**OU** (variante multiplexing) : 1 seul gateway, routing par `X-Hermes-Profile` :
```
1 gateway process :8642
├─ profile alpha → sessions alpha
└─ profile beta → sessions beta
overmind-bridge par-agent (3101, 3102)
→ POST http://127.0.0.1:8642/v1/chat/completions
+ header `X-Hermes-Profile: alpha`
```
#### 3.2 Work items
| Item | Effort | Dépendance |
|------|--------|-----------|
| `HermesGatewayManager.spawn(profile)` auto-start | 2h | Phase 1 ✅ |
| `HermesGatewayRunner` accepte `profile` option | 1h | — |
| Template `overmind-bridge-<agent>.ts` (peer-to-peer sans central) | 4h | Phase 1 ✅ |
| Suppression de `OverBridgeServer` (central) | 8h | Smoke test E2E réussi d'abord |
| `BridgeProxy` discovery service (trouve les peers par registry) | 3h | — |
| Refactor `a2a_hub` pour parler aux bridge ports (3101+) et plus au central | 4h | Découverte du peer |
#### 3.3 Stratégie d'exécution (crucial — éviter le Big Bang)
**Étape 1 — drapeau de feature**
```typescript
// src/lib/config.ts
process.env.OVERMIND_BRIDGE_MODE = 'centralized' | 'decentralized' | 'hybrid';
```
**Étape 2 — les deux modes coexistent**
- Mode A (`centralized`) : code actuel, default
- Mode B (`decentralized`) : nouveau, opt-in via env var
- Mode C (`hybrid`) : A pour les anciens agents, B pour les nouveaux
**Étape 3 — bascule progressive**
1. Déployer en `hybrid`
2. Migrer les 8 agents existants (Nexus Master, Trader, Healer, etc.) un par un
3. Quand tous sont migrés → flag = `decentralized` par défaut
4. Supprimer le code `centralized` (Phase 3 finale)
#### 3.4 Critères de succès Phase 3
- [ ] 1 bridge isolé par agent (8 agents = 8 bridges sur 3101-3108)
- [ ] A2A fonctionne entre bridges (peer-to-peer HTTP direct)
- [ ] Aucun agent ne dépend du `OverBridgeServer` central
- [ ] `OverBridgeServer` peut être supprimé sans casser les tests
- [ ] `pnpm run test` reste vert
---
### **Phase 4 — Mémoire par-profile (semaine 4-5)**
**Objectif** : chaque agent utilise son propre memory provider, au lieu du pgvector central.
#### 4.1 Le problème actuel
`PostgresMemoryProvider` est utilisé par :
- `tools/memory_search.ts` (MCP tool)
- `tools/memory_store.ts` (MCP tool)
- Auto-injecté à chaque agent via `mcp_servers: { memory: { url: :3099/mcp } }`
**Conséquence** : tous les agents partagent la même DB pgvector. Pas d'isolation mémoire entre agents. C'est exactement ce que le wrapper multi-agent persistant doit éviter.
#### 4.2 Solution
Hermes supporte `memory.provider` dans `config.yaml` (natif depuis v0.15+) :
```yaml
# config.yaml du profil
memory:
provider: postgres
postgres:
url: postgresql://user:pass@localhost/memory_<profile_name>
```
Chaque agent a sa **propre DB**, nommée d'après le profil.
#### 4.3 Étapes
```typescript
// src/services/HermesProfileManager.ts — ajout méthode
static async setMemoryProvider(
name: string,
opts: { provider: 'postgres' | 'sqlite' | 'file'; url?: string } = { provider: 'sqlite' },
): Promise<void> {
// 1. Si postgres, créer DB dédiée
if (opts.provider === 'postgres' && !opts.url) {
const dbName = `overmind_memory_${name}`;
await PostgresAdmin.createDatabase(dbName);
opts.url = `postgresql://${process.env.PG_USER}:${process.env.PG_PASS}@localhost/${dbName}`;
}
// 2. hermes -p <name> config set memory.provider <provider>
await execAsync(`hermes -p "${name}" config set memory.provider "${opts.provider}"`);
if (opts.url) {
await execAsync(`hermes -p "${name}" config set memory.postgres.url "${opts.url}"`);
}
}
```
#### 4.4 Compatibilité ascendante
`memory_search` / `memory_store` MCP tools restent mais deviennent **passerelles** :
- Si l'agent appelant a `memory.provider` → query sa DB
- Sinon → fallback pgvector central (legacy)
```typescript
// src/tools/memory_search.ts
handler: async (args) => {
const callerAgent = getCallerAgent();
if (callerAgent?.memoryProvider === 'postgres') {
return await queryAgentMemory(callerAgent, args);
}
// Legacy fallback
return await getMemoryProvider().search(args);
};
```
#### 4.5 Critères de succès Phase 4
- [ ] `HermesProfileManager.setMemoryProvider()` fonctionne
- [ ] Chaque agent a sa propre DB
- [ ] `memory_search` retourne des résultats **propres à l'agent** (pas de pollution croisée)
- [ ] Test E2E : `agent_a` stocke "secret-X", `agent_b` ne le voit pas
- [ ] `PostgresMemoryProvider` reste pour le fallback legacy mais marqué deprecated
---
## 🔬 Plan de tests de régression
À exécuter **après chaque phase** :
### Smoke test (5 min)
```bash
# 1. Build + lint + tests
pnpm run rebuild && pnpm run lint && pnpm run test
# 2. E2E manuel via MCP
overmind --transport httpStream --port 3099 &
sleep 3
# 3. Créer un agent via MCP tool
curl -X POST http://localhost:3099/mcp \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"create_agent","arguments":{"name":"smoke_test_$(date +%s)","runner":"hermes","model":"glm-4.6","prompt":"You are a smoke test agent"}},"id":1}'
# 4. Lui poser une question
curl -X POST http://localhost:3099/mcp \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"run_hermes","arguments":{"agentName":"<created>","prompt":"Reply: SMOKE_OK"}},"id":2}'
# 5. Cleanup
curl -X POST http://localhost:3099/mcp \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"delete_agent","arguments":{"name":"<created>"}},"id":3}'
```
### Tests unitaires à préserver/ajouter
| Phase | Tests à préserver | Tests à ajouter |
|-------|------------------|-----------------|
| **Phase 1** | 68 existants | 0 (que suppressions) |
| **Phase 2** | 68 | +8 `HermesKanbanClient` |
| **Phase 3** | 76 | +12 `BridgeProxy` discovery |
| **Phase 4** | 88 | +10 `memoryProvider` isolation |
---
## 📅 Timeline globale
| Semaine | Phase | Livrable | Version |
|---------|-------|----------|---------|
| 1 | Phase 1a — Nettoyage dispatcher/YOLO (obligatoire) | -300 LOC, 1 fichier | v4.0.0-alpha |
| 2 | Phase 2 — Support Kanban OPTIONNEL *(si demandé)* | +1 tool chargé opt-in | v4.0.0-beta |
| 3-4 | Phase 3 — Decentralized bridges + multi-profile gateway | 1 bridge/agent | v4.0.0-rc |
| 4-5 | Phase 4 — Mémoire par-profile | isolation mémoire | v4.0.0 |
| quand prêt | Phase 1b — Suppression Kanban historique *(opt-in après 1 release grâce)* | -930 LOC | v4.1.0 |
> **Note** : Phase 2 est conditionnelle. Si Kanban n'est pas dans le scope,
> on peut release v4.0.0 final après la Phase 3 + Phase 4 sans Phase 2.
---
## ⚠️ Risques identifiés
| Risque | Impact | Mitigation |
|--------|--------|-----------|
| **Rétro-compat callers KanbanAdapter** | Élevé — utilisé par 4 fichiers | Wrapper compat pendant 1 release, déprecation warning |
| **Migration DB pgvector** | Élevé — données existantes | Dual-write 1 release, outil de migration `pnpm run migrate:memory:v4` |
| **OverBridgeServer supprimé trop tôt** | Élevé — bridge central = SPOF | Feature flag `OVERMIND_BRIDGE_MODE`, bascule progressive |
| **Hermes API change** (versions futures) | Moyen — pool gateway déjà adapté | Pin Hermes version dans `setup.mjs`, tests d'intégration |
---
## 🤝 Décisions à prendre avant de commencer
1. **Maintenir `OverBridgeServer` central en parallèle** ? Oui, le temps de la migration
2. **Migration auto des agents existants** ? Oui — script `migrate-agents-v4.ts`
3. **Deprecation warnings ou rupture nette** ? Deprecation 2 releases (v4.0 + v4.1)
4. **Documentation site web** : régénérer `docs/index.html` avec nouvelle vision
---
## 📚 Annexes
### A. Inventaire des fichiers (split obligatoire / optionnel)
**OBLIGATOIRE (Phase 1a)** :
```
src/lib/orchestration/dispatcher.ts (283 LOC) ← SUPPRIMER
src/lib/orchestration/ (dossier) ← SUPPRIMER
YOLO_CONFIG (inline dans KanbanAdapter) (~50 LOC) ← SUPPRIMER
```
**OPTIONNEL — conservé tant que Kanban est utilisé** :
```
src/services/KanbanAdapter.ts (420 LOC) ← CONSERVÉ
src/bridge/ScenarioLoader.ts (508 LOC) ← CONSERVÉ
```
**Suppression totale si Kanban confirmé inutilisé (Phase 1b)** :
```
src/services/KanbanAdapter.ts (420 LOC) ← SUPPRIMER (v4.1.0)
src/bridge/ScenarioLoader.ts (508 LOC) ← SUPPRIMER (v4.1.0)
```
### B. Inventaire des fichiers à créer (tous OPTIONNELS sauf ceux de Phase 3-4)
```
# OPTIONNEL (Phase 2 — opt-in Kanban)
src/tools/kanban_hub.ts (NOUVEAU — 250 LOC) [chargé si OVERMIND_KANBAN_ENABLED=1]
src/services/HermesKanbanClient.ts (NOUVEAU — 180 LOC) [chargé opt-in]
src/tools/kanban_hub.test.ts (NOUVEAU — 200 LOC, 8 tests) [opt-in]
# OBLIGATOIRE (Phase 3-4 — wrapper pur)
src/bridge/BridgeProxy.ts (NOUVEAU — 150 LOC)
src/services/HermesProfileManager.setMemoryProvider (méthode ajoutée)
scripts/migrate-agents-v4.ts (NOUVEAU — 100 LOC)
docs/MIGRATION_V4_WRAPPER.md (CE DOCUMENT — déjà créé)
```
### C. Checklist pre-merge
- [ ] Tous les tests passent (`pnpm run test`)
- [ ] Lint clean (`pnpm run lint`)
- [ ] Build compile (`pnpm run rebuild`)
- [ ] Smoke test E2E réussi
- [ ] Documentation à jour (`docs/`, `README.md`)
- [ ] CHANGELOG.md mis à jour
- [ ] Tag git posé
---
_Migration planifiée par DeaMoN888 — 2026-07-10_