Plugin Cowork qui donne à Claude une mémoire persistante avec un cycle de sommeil. Tout tourne sur ta machine, aucun transcript brut ne sort.
À la fin de chaque session, Claude écrit ce qu'il vient d'apprendre dans un dossier local. La nuit (02:05), un processus relit ces notes, les compare à ce que Claude savait déjà, garde ce qui est utile, supprime le bruit, et reconstruit un index court qui sera rechargé au début de la session suivante.
Tu vis une journée, tu accumules des conversations, des décisions, des erreurs. La nuit, ton cerveau ne réécoute pas tout, il trie. Il renforce ce qui revient souvent, il oublie ce qui n'a servi à rien, il rapproche les souvenirs qui se ressemblent. Au réveil, tu as un index résumé. Dream fait la même chose pour Claude.
Les trois phases du cerveau qu'on imite :
| Phase humaine | Équivalent Dream |
|---|---|
| Encodage (vivre la journée) | Capture des évènements dans un buffer JSONL après chaque session |
| Consolidation (sommeil paradoxal + lent) | Cycle nocturne à 02:05 qui rejoue le buffer, fusionne les notes, écrit dans le graphe |
| Récupération (se souvenir le lendemain) | Recherche hybride dans le graphe au début de la session suivante |
Sans Dream, Claude redémarre chaque conversation à zéro. Tu lui réexpliques le contexte du projet à chaque fois, tu lui redonnes les conventions, tu redis "non, pas comme ça, plutôt comme on avait décidé hier".
Avec Dream :
- Claude se souvient de tes décisions de projet (architecture, conventions, choix techniques)
- Il sait qui sont les personnes dont tu parles
- Il garde les corrections que tu as faites
- Il oublie automatiquement les bavardages qui n'ont pas servi
- Tout reste sur ta machine, pas d'envoi cloud
C'est un graphe (réseau de nœuds reliés par des arêtes) avec une dimension temporelle. Chaque nœud est un fait, une décision, un bout de code, une personne, un processus ou une erreur. Chaque arête dit comment deux nœuds sont reliés (l'un implémente l'autre, l'un en dépend, l'un contredit l'autre, l'un remplace l'autre).
Trois stockages travaillent ensemble :
| Stockage | Rôle | Format |
|---|---|---|
SQLite (pgt.sqlite) |
Métadonnées, vitalité, ledger | base relationnelle classique |
LanceDB (vectors.lance/) |
Vecteurs sémantiques pour la recherche par similarité | base vectorielle |
NetworkX (graph.gpickle) |
Le graphe en mémoire pour les parcours | structure en RAM |
Chaque nœud a un score de vitalité entre 0 et 1. Plus il est élevé, plus le nœud est "vivant" et facile d'accès. La formule combine quatre signaux :
- décroissance temporelle (plus c'est ancien, plus ça baisse)
- usage récent (plus on l'a consulté, plus ça remonte)
- alignement avec les objectifs actuels (plus c'est pertinent pour ce que tu fais en ce moment, plus ça remonte)
- contradictions reçues (si un nouveau fait le contredit, ça baisse)
Le résultat décide où le nœud habite :
| Score | Statut | Comportement |
|---|---|---|
| V > 0.85 | chaud | Mis en cache, surfacé dans l'index du jour |
| 0.4 <= V <= 0.85 | actif | Dans le graphe, accessible normalement |
| 0.2 <= V < 0.4 | sombre | Indexé mais hors parcours du graphe |
| V < 0.2 | froid | Archivé sur disque, sortie automatique |
Quand Claude cherche "qu'est-ce que je sais sur X", quatre méthodes tournent en parallèle puis sont mélangées :
| Méthode | Ce qu'elle capte |
|---|---|
| BM25 | Les mots exacts |
| Embeddings (bge-m3) | Le sens, même avec des mots différents |
| Propagation d'activation | Les voisins du graphe, même non recherchés directement |
| Cross-encoder (ms-marco) | Le réordonnancement final pour ne garder que ce qui colle vraiment |
Le score final pondère ces quatre signaux et garde les 5 meilleurs résultats.
Pendant le cycle nocturne, chaque groupe de notes à consolider passe devant un mini-tribunal interne. Quatre instances du même modèle (gemma4:12b) jouent chacune un rôle différent, votent, et un score consensus décide si la note est intégrée :
| Rôle | Mission | Poids du vote |
|---|---|---|
| Archiviste | Capture les faits, ancre les dates en ISO 8601, refuse les paraphrases | 25% |
| Sceptique | Chasse les contradictions et les preuves manquantes | 30% |
| Optimiseur | Compresse à moins de 120 tokens par cluster | 20% |
| Expert Domaine | Vérifie la cohérence avec le contexte projet | 25% |
Si le score consensus dépasse 0.7, la note est acceptée. Entre 0.5 et 0.7, elle est mise en file HITL (Human In The Loop, c'est-à-dire qu'on te demandera ton avis). En dessous de 0.5, elle est rejetée et le débat est archivé pour analyse.
Quand un nœud est tagué comme une erreur ou que son score de consensus est très bas, Dream peut générer 2 ou 3 branches "et si on avait fait autrement". Ces branches sont stockées en lecture seule, avec une date d'expiration. Au bout de la fenêtre, Dream compare ce qui s'est réellement passé avec ce que la branche prédisait. Si la branche avait raison, elle est promue en processus stable. Sinon, elle décay ou elle est élaguée.
Chaque écriture dans le graphe est signée avec une clé Ed25519 locale et ajoutée à une chaîne Merkle. Si jamais le fichier pgt.sqlite est corrompu ou trafiqué, la vérification Merkle détecte la modification en moins de 100 ms. Restauration ponctuelle en moins de 5 secondes par rejeu de la chaîne.
Exemption bootstrap (v0.5.0) : un graphe vide (0 nœud actif) affiche une vitalité moyenne de 0.0 par construction. Ce cas ne déclenche plus SECURISE, sinon la consolidation (seule voie autonome de peuplement du graphe) resterait verrouillée derrière le breaker. Le déclencheur vitalité s'applique dès le premier nœud actif.
Dream surveille en continu sa propre santé. Trois modes possibles :
| Mode | Déclenchement | Comportement |
|---|---|---|
| NORMAL | tout va bien | Pipeline complet : débat + contrefactuel + recherche complète |
| CONSERVATEUR | latence p95 > 500 ms ou consensus < 0.7 | Lecture seule, quorum réduit, contrefactuel coupé |
| SÉCURISÉ | RAM > 15 Go, vitalité moyenne < 0.4 ou ledger Merkle KO | Arrêt débat et contrefactuel, HITL obligatoire, consolidation différée |
Pour remonter d'un cran, il faut 3 sondes vertes consécutives. Pas de bascule brutale en boucle.
Modèles servis via Ollama, tous locaux :
| Modèle | Rôle | RAM active | Disque |
|---|---|---|---|
gemma4:e4b |
Sanitisation (P95 cible <500 ms) | ~5 Go | 9.6 Go |
gemma4:12b |
Consolidation et génération contrefactuelle | ~7.5 Go | 16 Go |
bge-m3 |
Embeddings (1024 dimensions) | ~3 Go CPU | 2 Go |
ms-marco-MiniLM-L-6-v2 |
Reranking | <0.5 Go | 90 Mo |
Sampling Gemma 4 fixé : temperature=1.0, top_p=0.95, top_k=64. Mode "thinking" géré via le token <|think|> dans le system prompt.
| Garantie | Mécanisme |
|---|---|
| Aucun transcript brut ne sort de la machine | Sanitisation par gemma4:e4b + 17 regex déterministes (clés AWS, GitHub, Anthropic, OpenAI, JWT, IBAN, IP, email, etc.) |
| Détection de fuite involontaire | Hash SHA-256 de l'input comparé à une deny-list |
| Intégrité de la mémoire | Ed25519 + Merkle, vérifié à chaque cycle |
| Réseau verrouillé | FastMCP bound sur 127.0.0.1 |
Pré-requis : Windows 10/11, Linux ou macOS, 16 Go RAM minimum, 35 Go de disque libre, Ollama installé.
# 1. dépendances Python
pip install -r requirements.txt
# 2. modèles Ollama
ollama pull gemma4:e4b
ollama pull gemma4:12b
# bge-m3 et ms-marco se téléchargent automatiquement au premier import
# 3. variables d'environnement (optionnel)
export DREAM_HOME="$HOME/.dream"
# 4. enregistrement plateforme
# Windows : python scripts/setup_windows.py
# Linux : python scripts/setup_linux.py
# macOS : python scripts/setup_macos.py
# 5. bootstrap dans Claude
# Demande dans le chat : "initialise dream"
# Le skill dream-init prépare le stockage et signe la clé Ed25519| Variable | Défaut | Rôle |
|---|---|---|
DREAM_HOME |
~/.dream |
Racine des données |
DREAM_REDIS_HOST |
127.0.0.1 |
Cache hot (fallback mémoire si Redis absent) |
DREAM_REDIS_PORT |
6379 |
Cache hot |
DREAM_CONSOLIDATION_MODEL |
gemma4:12b |
Modèle Ollama pour le débat |
DREAM_COUNTERFACTUAL_MODEL |
gemma4:12b |
Modèle Ollama pour le jardin |
DREAM_METRICS_PORT |
9464 |
Port de l'endpoint Prometheus |
DREAM_METRICS_ENABLED |
1 |
Mettre à 0 pour couper l'endpoint Prometheus |
| Skill | À quoi sert |
|---|---|
dream-init |
Bootstrap initial : pull des modèles, init schéma, signature de la clé Ed25519 |
dream-store-event |
Ajouter un fait dans le graphe (sanitisé puis signé) |
dream-search-pgt |
Recherche hybride sur le graphe |
dream-consolidate |
Lancer le cycle nocturne à la demande (autrement automatique à 02:05) |
dream-counterfactual |
Faire pousser une branche alternative sur une erreur |
dream-sanitize |
Nettoyer un texte avant stockage (regex + LLM local) |
dream-health |
État de santé, métriques, mode du circuit breaker |
dream-load-context |
Préparer un bundle de contexte pour la session courante |
dream-admin |
Diagnostic, réparation, cycle forcé, scénarios contrefactuels, arbitrages HITL |
dream-explain-node |
Inspection détaillée d'un nœud (vitalité, tier, edges, historique) |
Au démarrage d'une session Claude, le hook SessionStart charge automatiquement les meilleurs nœuds du graphe (limité à 2k tokens). À la fin, le hook Stop extrait les phrases load-bearing de la conversation et les pousse dans le buffer du jour. Aucune action manuelle requise.
Le scheduler (scripts/scheduler.py) lance le cycle nocturne à 02:05 local. Sur Linux : systemd --user. Sur macOS : launchd. Sur Windows : le Planificateur de tâches.
10 outils côté serveur FastMCP :
store_event, search_semantic, query_relations, update_vitality, propose_counterfactual, sanitize_local, load_context, health_check, set_mode, verify_counterfactual.
Le serveur expose Prometheus sur 127.0.0.1:9464. Métriques clés :
| Métrique | Type | Utilité |
|---|---|---|
dream_cycle_completed_total |
counter | Nombre de cycles nocturnes réussis |
dream_cycle_failed_total{phase} |
counter | Cycles qui ont échoué et à quelle phase |
dream_sanitize_latency_ms |
histogram | Distribution de la latence de sanitisation |
dream_search_latency_ms |
histogram | Distribution de la latence de recherche |
dream_consensus_score |
histogram | Score consensus par cluster pendant le débat |
dream_hitl_pending |
gauge | Nombre de notes en attente d'arbitrage humain |
dream_ram_peak_mb |
gauge | Pic RAM observé par le daemon |
dream_vitality_avg |
gauge | Vitalité moyenne du graphe |
dream_circuit_mode |
gauge | Mode actif (0=NORMAL, 1=CONSERVATEUR, 2=SÉCURISÉ) |
dream_ledger_merkle_ok |
gauge | 1 si l'intégrité Merkle est vérifiée, 0 sinon |
dream_ollama_up |
gauge | 1 si le daemon Ollama local répond, 0 sinon |
| Heure | Évènement |
|---|---|
| 09:00 | Tu ouvres une session Claude. Le hook SessionStart charge l'index du jour (~500 tokens) dans le contexte. |
| Pendant | Claude répond, tu corriges, vous décidez ensemble. Le hook Stop pousse les phrases utiles dans le buffer dès que tu fermes. |
| 02:05 | Le scheduler lance le cycle nocturne. ~10 minutes de débat 4 rôles, écriture du graphe, mise à jour de la vitalité, rebuild de l'index. |
| Lendemain | Tu ouvres une nouvelle session. L'index a été reconstruit avec les apprentissages d'hier. |
Pièges rencontrés en production sur Windows 11, avec leur lecture correcte :
| Symptôme | Lecture correcte |
|---|---|
MCP error -32001 sur store_event ou load_context |
Avant v0.5.0, l'écriture aboutissait souvent côté serveur quelques minutes après le timeout client. Réutiliser le même id (uuid4) en retry est idempotent. Vérification : SELECT COUNT(*) FROM nodes. Le préchargement de l'embedder (v0.5.0) fait disparaître le cas, désactivable via DREAM_PRELOAD_EMBEDDER=0. |
can't open file ... scheduler.py dans logs/nightly.log |
La tâche planifiée pointe vers un cache de plugin supprimé. Relancer python scripts/setup_windows.py : depuis v0.5.0 il déploie une copie stable dans DREAM_HOME\scripts et enregistre tout contre elle. |
CantActivateDocumentInPipeline dans un shell spawné |
PATH et PATHEXT vides dans certains shells automatisés (Desktop Commander). Préfixer chaque commande : $env:PATHEXT=".COM;.EXE;.BAT;.CMD"; $env:PATH="$env:SystemRoot\system32;$env:SystemRoot;$env:PATH". |
Un outil filesystem voit %USERPROFILE%\.dream vide |
Faux négatif observé (taille 0, aucun enfant listé) alors que le dossier est peuplé. Trancher avec Get-ChildItem -Force dans PowerShell avant tout diagnostic. |
| Fichiers vus tronqués depuis un sandbox monté sur OneDrive | Désynchronisation OneDrive. Lire et écrire depuis la machine locale, ou passer par git. |
Erreur 412 d'Ollama sur gemma4:12b |
Version d'Ollama trop ancienne. Requiert >= 0.30.3. |
| RAM saturée pendant le cycle | gemma4:12b chargé laisse environ 0.3 Go libres sur une machine 16 Go. Fermer les applications lourdes avant 02:05 ou définir DREAM_PRELOAD_EMBEDDER=0. |
| Sortie Python invisible en arrière-plan | Les subprocess PowerShell avalent stdout sous redirection. Écrire les résultats dans des fichiers, jamais sur stdout. |
MIT.