feat(memory): config embedders (LOT C2) + suggestion contextuelle (LOT C3) + contexte projet partagé
- LOT C2 (§14.5.3) : use cases de configuration des embedders déclaratifs (List/Save/Delete + DescribeEmbedderEngines : modèles ONNX recommandés, environnement local détecté, stratégies compilées). UI EmbedderSettings. - LOT C3 (§14.5.5) : suggestion contextuelle best-effort à l'activation quand la mémoire dépasse le budget de recall sans embedder configuré (event EmbedderSuggested, anti-spam 1×/session, « ne plus demander »). - Contexte projet partagé .ideai/CONTEXT.md (model-agnostic) injecté à tous les agents/profils au lancement, avant la persona. UI ProjectContextPanel. Tests : backend workspace vert (0 échec) ; frontend 306/306. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
122
ARCHITECTURE.md
122
ARCHITECTURE.md
@ -114,7 +114,9 @@ Workspace 1───* Window 1───* Tab 1───1 Project
|
||||
(LayoutNode récursif) ├──1 RemoteHost (Local | Ssh | Wsl)
|
||||
│ feuilles └──1 AgentManifest (.ideai/agents.json)
|
||||
▼
|
||||
TerminalSession 1───? Agent (si lancé par un agent)
|
||||
TerminalSession 1───? AgentSession 1───1 Agent
|
||||
│
|
||||
└──? CellBinding (0 ou 1 cellule visible)
|
||||
```
|
||||
|
||||
### 3.2 Entités & Value Objects (avec invariants)
|
||||
@ -131,6 +133,18 @@ Workspace 1───* Window 1───* Tab 1───1 Project
|
||||
- Champs : `id`, `name`, `context: AgentContextRef` (chemin du `.md` dans `.ideai/`), `profile_id: ProfileId`, `origin: AgentOrigin` (`Scratch` | `FromTemplate { template_id, synced_version }`), `synchronized: bool`.
|
||||
- Invariants : `synchronized == true` ⇒ `origin == FromTemplate{..}` (on ne peut pas synchroniser un agent créé from scratch). `context` doit exister à l'activation. `profile_id` doit référencer un `AgentProfile` connu.
|
||||
|
||||
**`AgentSession`** (entité — exécution active d'un agent IdeA)
|
||||
- Champs : `id`, `agent_id`, `profile_id`, `terminal_session_id`, `requested_by: AgentRequester` (`User` | `Agent { agent_id, session_id? }`), `task: Option<String>`, `visibility: AgentVisibility`, `status` (`Starting|Running|WaitingForUser|Failed|Stopped|Exited{code}`).
|
||||
- Invariants : une session active référence **un seul** `Agent` et **un seul** `AgentProfile` résolu au lancement ; elle peut être visible dans **0 ou 1** cellule. Le profil de l'agent demandeur ne contraint jamais le profil de l'agent cible.
|
||||
|
||||
**`AgentVisibility` / `CellBinding`** (VO)
|
||||
```
|
||||
AgentVisibility =
|
||||
| Background
|
||||
| Visible { node_id: NodeId }
|
||||
```
|
||||
- Invariants : une cellule peut afficher 0 ou 1 `AgentSession` active ; une `AgentSession` active peut être attachée à 0 ou 1 cellule visible. Fermer une cellule **détache** la session (`Visible` → `Background`) sans arrêter le process. Ouvrir une cellule sur une session déjà active **réattache** la session et affiche le travail en cours.
|
||||
|
||||
**`AgentTemplate`** (entité, store global)
|
||||
- Champs : `id`, `name`, `content_md: MarkdownDoc`, `version: TemplateVersion`, `default_profile_id`.
|
||||
- Invariants : `version` **monotone croissante** ; toute modification du `content_md` ⇒ `version + 1` (voir §8).
|
||||
@ -150,8 +164,8 @@ ContextInjection =
|
||||
- Invariants : `ConventionFile.target` est un nom de fichier relatif (pas de `..`, pas absolu) ; `Env.var` est un identifiant d'env valide ; `Flag.flag` non vide.
|
||||
|
||||
**`TerminalSession`** (entité)
|
||||
- Champs : `id`, `node_id` (cellule du layout qui l'héberge), `cwd: ProjectPath`, `kind: SessionKind` (`Plain` | `Agent { agent_id }`), `pty_size: PtySize { rows, cols }`, `status` (`Starting|Running|Exited{code}`).
|
||||
- Invariants : une cellule (feuille de layout) héberge **au plus une** `TerminalSession` active. `pty_size.rows>0 && cols>0`.
|
||||
- Champs : `id`, `node_id: Option<NodeId>` (cellule visible qui l'héberge, absente si arrière-plan), `cwd: ProjectPath`, `kind: SessionKind` (`Plain` | `Agent { session_id }`), `pty_size: PtySize { rows, cols }`, `status` (`Starting|Running|Exited{code}`).
|
||||
- Invariants : une cellule (feuille de layout) héberge **au plus une** `TerminalSession` active. Une session agent peut conserver son PTY sans cellule visible. `pty_size.rows>0 && cols>0`.
|
||||
|
||||
**`LayoutNode` / `LayoutTree`** (VO récursif — voir §7 pour le détail complet)
|
||||
- Invariants : poids relatifs strictement positifs ; somme normalisable ; pas de fusion qui chevauche deux conteneurs distincts ; un `Leaf` référence 0 ou 1 `SessionId`.
|
||||
@ -184,7 +198,7 @@ RemoteRef =
|
||||
- Invariants : `name` non vide ; `content_md` non vide.
|
||||
- Un agent référence 0..N skills (dans l'`AgentManifest`). Les skills assignés sont injectés dans son convention file à l'activation.
|
||||
|
||||
**`DomainEvent`** (enum) — `ProjectCreated`, `AgentLaunched`, `AgentExited`, `TemplateUpdated`, `AgentDriftDetected`, `SkillAssigned`, `LayoutChanged`, `RemoteConnected`, `GitStateChanged`, `PtyOutput{session_id, bytes}`, `OrchestratorRequest{requester_id, action}` (ce dernier souvent court-circuité vers un Channel).
|
||||
**`DomainEvent`** (enum) — `ProjectCreated`, `AgentLaunched`, `AgentSessionAttached`, `AgentSessionDetached`, `AgentExited`, `TemplateUpdated`, `AgentDriftDetected`, `SkillAssigned`, `LayoutChanged`, `RemoteConnected`, `GitStateChanged`, `PtyOutput{session_id, bytes}`, `OrchestratorRequest{requester_id, action}` (ce dernier souvent court-circuité vers un Channel).
|
||||
|
||||
---
|
||||
|
||||
@ -344,7 +358,11 @@ RemoteRef =
|
||||
| `UpdateTemplate` | Modifie un template, **bump version**, signale drift aux agents liés. | `TemplateStore`, `EventBus` |
|
||||
| `DetectAgentDrift` | Compare `synced_template_version` vs `template.version`. | `TemplateStore`, `AgentContextStore` |
|
||||
| `SyncAgentWithTemplate` | Applique la MAJ template→agent si `synchronized`. | `TemplateStore`, `AgentContextStore`, `EventBus` |
|
||||
| `LaunchAgent` | Résout profil+contexte, prépare injection, ouvre cellule PTY au bon `cwd`, spawn CLI. | `AgentRuntime`, `AgentContextStore`, `RemoteHost`→`PtyPort`/`FileSystem`, `EventBus` |
|
||||
| `RequestAgentWork` | Demande structurée utilisateur/agent pour faire travailler un agent IdeA cible, sans subagent natif fournisseur. | `AgentContextStore`, `AgentSessionStore`, `AgentRequestQueue`, `EventBus` |
|
||||
| `LaunchAgentSession` / `LaunchAgent` | Résout profil+contexte+mémoire, prépare injection, crée ou reprend une session agent, spawn CLI si nécessaire. | `AgentRuntime`, `AgentContextStore`, `AgentSessionStore`, `RemoteHost`→`PtyPort`/`FileSystem`, `EventBus` |
|
||||
| `AttachAgentSessionToCell` / `DetachAgentSessionFromCell` / `MoveAgentSessionToCell` | Rend visible, détache ou déplace une session active dans la grille sans tuer le process. | `AgentSessionStore`, `ProjectStore`, `EventBus` |
|
||||
| `ListAgentSessions` / `ObserveAgentSession` | Liste les sessions visibles/arrière-plan et observe leur état/logs. | `AgentSessionStore`, `EventBus` |
|
||||
| `StopAgentSession` | Arrête explicitement une session agent et son PTY. | `AgentSessionStore`, `PtyPort`, `EventBus` |
|
||||
| `OpenTerminal` | Ouvre un PTY simple dans une cellule. | `RemoteHost`→`PtyPort`, `EventBus` |
|
||||
| `WriteToTerminal` / `ResizeTerminal` / `CloseTerminal` | I/O PTY. | `PtyPort` |
|
||||
| `MutateLayout` (split/merge/resize/move) | Applique une opération sur le `LayoutTree` (logique **pure** dans le domaine, persistée ici). | `ProjectStore` (persistance) |
|
||||
@ -402,6 +420,7 @@ struct GridCell {
|
||||
- Tous les `weight > 0`. Les poids sont **relatifs** (l'UI normalise pour le rendu).
|
||||
- Dans un `GridContainer` : aucune superposition de spans ; toute la surface couverte ; `row+row_span ≤ rows`, `col+col_span ≤ cols`.
|
||||
- Un `SessionId` n'apparaît que dans **un seul** `Leaf`.
|
||||
- Pour une session agent, retirer le `SessionId` d'un `Leaf` détache l'affichage seulement ; l'arrêt du process passe par `StopAgentSession`/`CloseTerminal`, jamais par la fermeture visuelle de cellule.
|
||||
- Les opérations `split`, `merge`, `resize`, `move` sont des **fonctions pures** `LayoutTree -> Result<LayoutTree, LayoutError>` (immutabilité ⇒ testabilité, undo/redo facile).
|
||||
|
||||
### 7.3 Sérialisation & persistance
|
||||
@ -458,6 +477,11 @@ drift(agent) =
|
||||
│ ├── agents.json # AgentManifest (mapping md ↔ template ↔ sync ↔ version)
|
||||
│ ├── layout.json # LayoutTree de l'onglet (sérialisé)
|
||||
│ ├── project.json # méta projet local (nom, profil par défaut, remote ref)
|
||||
│ ├── CONTEXT.md # contexte projet partagé, injecté à tous les agents/profils
|
||||
│ ├── memory/
|
||||
│ │ ├── MEMORY.md # index dérivé de rappel mémoire
|
||||
│ │ ├── <slug>.md # notes mémoire, source de vérité
|
||||
│ │ └── .index/ # index vectoriel dérivé/reconstructible
|
||||
│ ├── agents/
|
||||
│ │ ├── reviewer.md # contexte d'un agent de projet
|
||||
│ │ ├── backend-dev.md
|
||||
@ -470,9 +494,16 @@ drift(agent) =
|
||||
│ ├── <agent-id>/ # cwd isolé par agent actif (créé à l'activation)
|
||||
│ │ └── CLAUDE.md # fichier de convention généré par IdeA (profil-dépendant)
|
||||
│ └── ...
|
||||
└── (aucun CLAUDE.md/AGENTS.md/GEMINI.md à la racine — jamais — voir §14.1)
|
||||
└── (aucun CONTEXT.md/CLAUDE.md/AGENTS.md/GEMINI.md utilisé par IdeA à la racine — voir §14.1)
|
||||
```
|
||||
|
||||
**Règle de désinstallation projet** : tout artefact projet utilisé par IdeA vit
|
||||
sous `.ideai/`. Si l'utilisateur ne veut plus utiliser IdeA sur un projet, il
|
||||
supprime `.ideai/` : contexte projet, agents, skills projet, mémoire, layouts,
|
||||
requêtes d'orchestration et dossiers d'exécution disparaissent ensemble. Les
|
||||
stores machine-locaux (profils globaux, templates globaux, registre des projets
|
||||
récents) ne sont pas des artefacts du projet.
|
||||
|
||||
**Schéma `agents.json`** :
|
||||
```json
|
||||
{
|
||||
@ -599,7 +630,7 @@ IdeA/
|
||||
| L10 | **Fenêtres & multi-window** | `Workspace`/`Window`/`Tab`, `MoveTabToNewWindow`, drag d'onglet → nouvelle fenêtre OS Tauri. | `application`, `app-tauri`, `frontend/app` |
|
||||
| L11 | **Packaging & livraison** | Tauri bundle : NSIS `setup.exe`, **AppImage** multi-distro, CI Linux+Windows. | `app-tauri`, CI |
|
||||
| L12 | **Skills** | Entité `Skill`, `SkillStore`, CRUD skills global+projet, assignation agent↔skills, injection dans convention file à l'activation. UI onglet Skills. | `domain/skill`, `application/skill`, `infrastructure/store`, `frontend/features/skills` |
|
||||
| L13 | **OrchestratorApi** | File-watcher `.ideai/requests/`, port `OrchestratorApi`, adapter `FsOrchestratorAdapter`, actions `spawn_agent`/`stop_agent`/`update_agent_context`. | `infrastructure/orchestrator`, `application/agent`, `app-tauri` |
|
||||
| L13 | **OrchestratorApi** | File-watcher `.ideai/requests/`, port `OrchestratorApi`, adapter `FsOrchestratorAdapter`, protocole `agent.run`/`agent.stop`/`agent.attach`/`agent.detach`/`agent.message`, sessions agent visibles ou arrière-plan. | `domain/agent`, `application/agent`, `infrastructure/orchestrator`, `app-tauri`, `frontend/features/agents` |
|
||||
| L14 | **Mémoire** | Base de connaissance projet model-agnostic. Modèle 2 étages : `.md` source de vérité (port `MemoryStore`), rappel adaptatif (port `MemoryRecall`), embeddings déclaratifs (port `Embedder`), bascule auto sur seuil. Découpé en sous-lots **A/B/C** (voir §14.5). | `domain/memory`, `application/memory`, `infrastructure/store`, `frontend/features/memory` |
|
||||
|
||||
---
|
||||
@ -613,10 +644,12 @@ IdeA/
|
||||
**Décision** : le cwd du PTY d'un agent n'est **jamais** le project root. C'est `.ideai/run/<agent-id>/`, un dossier créé par IdeA à l'activation et nettoyé à la fermeture.
|
||||
|
||||
**Convention file généré par IdeA** : IdeA écrit dans ce dossier le fichier conventionnel attendu par le profil (`CLAUDE.md`, `AGENTS.md`, etc.). Ce fichier contient :
|
||||
1. La **persona/rôle** de l'agent (son `.md` dans `.ideai/agents/`).
|
||||
2. Le **chemin absolu du project root** (pour que l'agent sache où opérer).
|
||||
3. Les **skills actifs** assignés à cet agent (voir §14.2).
|
||||
4. Le **rappel mémoire** du projet (index/hooks), si présent (voir §14.5.4).
|
||||
1. Le **chemin absolu du project root** (pour que l'agent sache où opérer).
|
||||
2. Le contrat d'**orchestration IdeA** (délégation via `.ideai/requests`, pas via les subagents natifs du fournisseur).
|
||||
3. Le **contexte projet partagé** (`.ideai/CONTEXT.md`), si présent.
|
||||
4. La **persona/rôle** de l'agent (son `.md` dans `.ideai/agents/`).
|
||||
5. Les **skills actifs** assignés à cet agent (voir §14.2).
|
||||
6. Le **rappel mémoire** du projet (index/hooks), si présent (voir §14.5.4).
|
||||
|
||||
**Avantages** :
|
||||
- Zéro collision entre agents, même N instances du même profil.
|
||||
@ -645,26 +678,65 @@ IdeA/
|
||||
|
||||
---
|
||||
|
||||
### 14.3 OrchestratorApi — spawn d'agents depuis un agent ou depuis l'UI
|
||||
### 14.3 OrchestratorApi — IdeA orchestre les agents, pas les CLIs fournisseurs
|
||||
|
||||
**Objectif** : qu'un agent orchestrateur puisse demander à IdeA de créer un nouvel agent (visible dans la grille et dans l'onglet Agents), exactement comme le ferait l'utilisateur via l'UI.
|
||||
**Objectif** : qu'un agent ou l'utilisateur puisse demander à IdeA de faire travailler un autre agent IdeA, visible dans la grille ou en arrière-plan, sans passer par les subagents natifs d'un fournisseur IA.
|
||||
|
||||
**Mécanisme** : file-watching sur `.ideai/requests/<requester-id>/`. L'orchestrateur écrit un fichier JSON de requête :
|
||||
```json
|
||||
{ "action": "spawn_agent", "name": "dev-backend", "profile": "claude-code", "context": "agents/dev-backend.md" }
|
||||
**Décision** : IdeA est l'**orchestrateur unique** du cycle de vie des agents. Un agent Claude, Codex, Gemini ou custom ne lance jamais directement un subagent natif de son fournisseur. Il écrit une demande d'orchestration IdeA ; IdeA résout l'agent cible, son `AgentProfile`, son contexte, ses skills et sa mémoire, puis lance ou réattache la session via le runtime adapté au profil cible.
|
||||
|
||||
Conséquence : le profil IA de l'agent demandeur ne contraint pas le profil IA de l'agent cible. Exemple valide :
|
||||
```text
|
||||
Main = Codex
|
||||
Architect = Claude
|
||||
DevBackend = Codex
|
||||
Ask = Gemini
|
||||
```
|
||||
IdeA détecte le fichier, exécute la même logique que `LaunchAgent` déclenché depuis l'UI, crée la cellule terminal, inscrit l'agent dans l'onglet Agents, puis supprime le fichier et écrit une réponse.
|
||||
Si `Main` demande à `Architect` de travailler, IdeA lance/réattache `Architect` avec son profil Claude. `Main` ne connaît ni la commande Claude ni son format de contexte.
|
||||
|
||||
**Règle** : l'orchestrateur ne spawne jamais lui-même un process CLI — il **délègue à IdeA**. IdeA reste l'unique source de vérité du cycle de vie des agents.
|
||||
|
||||
**Port `OrchestratorApi`** (adapter entrant, driven by file-watcher) : surveille `.ideai/requests/`, désérialise les commandes, les traduit en appels de use cases (`LaunchAgent`, `StopAgent`…). Implémenté dans `infrastructure/orchestrator`.
|
||||
|
||||
**Actions supportées (v1)** : `spawn_agent`, `stop_agent`, `update_agent_context`, `create_skill`.
|
||||
|
||||
`create_skill` permet à un orchestrateur de créer un skill réutilisable exactement comme l'UI (use case `CreateSkill`). Champs : `name`, `context` (corps Markdown du skill), `scope` optionnel (`"global"` | `"project"`, défaut `project`). Exemple :
|
||||
**Mécanisme** : file-watching sur `.ideai/requests/<requester-id>/`. L'agent écrit un fichier JSON de requête stable, consommé par IdeA :
|
||||
```json
|
||||
{ "action": "create_skill", "name": "deploy", "context": "# Étapes de déploiement…", "scope": "project" }
|
||||
{
|
||||
"type": "agent.run",
|
||||
"requestedBy": "Main",
|
||||
"targetAgent": "Architect",
|
||||
"task": "Analyser la décision d'architecture multi-agents",
|
||||
"visibility": "background",
|
||||
"attachToCell": null
|
||||
}
|
||||
```
|
||||
IdeA détecte le fichier, exécute les use cases d'orchestration, écrit une réponse, puis archive ou supprime la requête traitée. Le même chemin applicatif est utilisé depuis l'UI.
|
||||
|
||||
**Contrat session/cellule** :
|
||||
- `Agent` = définition stable (contexte `.ideai/agents/<agent>.md`, profil IA, origine template).
|
||||
- `AgentSession` = exécution active d'un agent.
|
||||
- `CellBinding` = affichage optionnel d'une session dans une cellule.
|
||||
|
||||
Invariants :
|
||||
- une `AgentSession` active peut être attachée à **0 ou 1** cellule visible ;
|
||||
- une cellule peut afficher **0 ou 1** session active ;
|
||||
- fermer une cellule détache la session (`Visible` → `Background`) et ne tue jamais l'agent ;
|
||||
- ouvrir une cellule sur une session déjà active réattache la cellule à cette session et montre le travail en cours ;
|
||||
- arrêter une session est une action explicite (`agent.stop`), distincte de la fermeture UI.
|
||||
|
||||
**Port `OrchestratorApi`** (adapter entrant, driven by file-watcher) : surveille `.ideai/requests/`, désérialise les commandes, les traduit en appels de use cases (`RequestAgentWork`, `LaunchAgentSession`, `AttachAgentSessionToCell`, `DetachAgentSessionFromCell`, `StopAgentSession`…). Implémenté dans `infrastructure/orchestrator`.
|
||||
|
||||
**Actions supportées (v2 cible)** :
|
||||
- `agent.run` : lance ou reprend une session pour un agent cible ; `visibility` vaut `background` ou `visible`.
|
||||
- `agent.stop` : arrête explicitement une session agent et son PTY.
|
||||
- `agent.attach` : attache une session active à une cellule.
|
||||
- `agent.detach` : détache une session active vers l'arrière-plan.
|
||||
- `agent.message` : transmet une tâche/message à une session existante.
|
||||
- `agent.update_context` : demande une mise à jour de contexte via les use cases IdeA, pas par écriture sauvage hors store.
|
||||
- `skill.create` : crée un skill réutilisable comme l'UI (use case `CreateSkill`).
|
||||
|
||||
Exemple `skill.create` :
|
||||
```json
|
||||
{ "type": "skill.create", "name": "deploy", "context": "# Étapes de déploiement…", "scope": "project" }
|
||||
```
|
||||
|
||||
**Instruction injectée aux agents** : les convention files générés (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`…) doivent contenir une règle explicite : pour déléguer une tâche, l'agent utilise le protocole IdeA `.ideai/requests` et n'utilise pas les subagents natifs du fournisseur. Cela garantit que l'IDE garde l'identité des agents, leur mémoire, leur contexte et leur observabilité UI.
|
||||
|
||||
**Impact UI/UX** : le layout n'est pas la source de vérité du travail agent. La grille affiche des vues attachées aux sessions. L'UI doit exposer un registre des sessions visibles et arrière-plan, avec actions ouvrir dans une cellule, détacher, déplacer, arrêter, et afficher la relation `requestedBy`/`targetAgent` quand elle existe.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user