feat(agent): conversation par paire + entrée médiée + pivot terminal/MCP
Coeur inter-agents consolidé et surface front réalignée sur la décision "terminal natif PTY, pas d'UI chat" (Option 1). Domaine - nouveaux modules conversation, mailbox, input, fileguard (ports + types) - orchestrator/profile/events étendus (conversation par paire, FIFO) Application / Infrastructure - orchestrator/service + context_guard : sérialisation FIFO par agent, garde RW mémoire/contexte, dispatch ask/reply - adapters in-memory conversation / mailbox / input / fileguard - registry session + lifecycle agent durcis (1 agent = 1 session vivante) - outils MCP idea_* alignés sur le nouveau dispatch Frontend - MediatedInput + useAgentBusy : entrée utilisateur médiée par IdeA, terminal = vue sortie inchangée - suppression de la vue chat structurée (AgentChatView) — abandonnée - adapter input + ports mis à jour Divers - .ideai/ : mémoire projet + briefs de cadrage versionnés ; requests/ runtime ignoré ; agents projet réels (DevBackend/DevFrontend/QA) Tests : Rust (domain/application/infrastructure/app-tauri) + front (346) verts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -14,6 +14,27 @@
|
||||
"mdPath": "agents/architect.md",
|
||||
"profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4",
|
||||
"synchronized": false
|
||||
},
|
||||
{
|
||||
"agentId": "73c853d1-c0fd-463b-ad17-1d24fefa371f",
|
||||
"name": "DevBackend",
|
||||
"mdPath": "agents/devbackend.md",
|
||||
"profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4",
|
||||
"synchronized": false
|
||||
},
|
||||
{
|
||||
"agentId": "af7f86da-76bc-48e1-9900-71f45a624800",
|
||||
"name": "DevFrontend",
|
||||
"mdPath": "agents/devfrontend.md",
|
||||
"profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4",
|
||||
"synchronized": false
|
||||
},
|
||||
{
|
||||
"agentId": "aefdbd61-e3d4-4bc1-9f42-c259446a97b5",
|
||||
"name": "QA",
|
||||
"mdPath": "agents/qa.md",
|
||||
"profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4",
|
||||
"synchronized": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
515
.ideai/briefs/conversation-pair-cadrage.md
Normal file
515
.ideai/briefs/conversation-pair-cadrage.md
Normal file
@ -0,0 +1,515 @@
|
||||
# Conversation par paire — cadrage d'architecture (multi-agent solide par construction)
|
||||
|
||||
> **Agent Architecture.** Ce document tranche le modèle qui rend le multi-agent
|
||||
> **solide par construction** : l'utilisateur n'a plus à « faire les choses dans le
|
||||
> bon ordre ». Aucun code de production ici — décisions, contrats (ports/entités),
|
||||
> découpage en lots testables, frontière backend/frontend.
|
||||
>
|
||||
> **Décisions produit arbitrées (NON négociables, rappel) :** (A) conversation par
|
||||
> paire = un fil entre deux parties, session propre, matérialisation paresseuse ;
|
||||
> (B) entrée médiée par IdeA (le terminal xterm reste la vue de sortie brute
|
||||
> INCHANGÉE, seule l'entrée change de chemin), Envoyer=enqueue / Interrompre=préempte ;
|
||||
> (C) FileGuard borné aux `.md` de contexte + la mémoire, via outils MCP, verrou
|
||||
> lecteurs/écrivain ; (D) zéro git, hexagonal+SOLID stricts, corrélation par ticket,
|
||||
> MCP Claude-only, fix `bind_endpoint`, abandon du band-aid `\n`→`\r`.
|
||||
>
|
||||
> **État du terrain (lu, pas présumé).** L'essentiel des briques existe déjà :
|
||||
> `domain/src/mailbox.rs` (`AgentMailbox`, `Ticket`, `TicketId`, `PendingReply`,
|
||||
> `MailboxError`) ; `infrastructure/src/mailbox/mod.rs` (`InMemoryMailbox`, FIFO par
|
||||
> agent + `oneshot`) ; `application/src/orchestrator/service.rs` (`ask_agent`,
|
||||
> `reply`, `ensure_live_pty`, verrou de tour `ask_locks`) ; surface MCP complète
|
||||
> (`mcp/tools.rs`, `mcp/server.rs`) ; transport bindé (`app-tauri/src/mcp_endpoint.rs`,
|
||||
> `state.rs::bind_endpoint`/`ensure_mcp_server`/`serve_peer`). **Ce cadrage
|
||||
> formalise et complète ; il ne réécrit pas.**
|
||||
|
||||
---
|
||||
|
||||
## 0. Synthèse exécutive (décisions tranchées)
|
||||
|
||||
1. **La conversation devient une entité de premier plan** (`Conversation` + `ConversationId`),
|
||||
absente aujourd'hui. Le couplage actuel « 1 session vivante / agent »
|
||||
(`session-registry-agent-ambiguity`) est **remplacé** par « 1 session vivante /
|
||||
**conversation** ». Un agent peut donc avoir **N sessions** simultanées (une par
|
||||
fil), mais **une seule tâche traitée à la fois** (l'entrée reste sérialisée, §B).
|
||||
C'est ce qui supprime la fuite de contexte : la délégation A→B n'emprunte plus la
|
||||
conversation User↔B.
|
||||
|
||||
2. **L'entrée passe par un `InputMediator`** (nouveau port application) : toutes les
|
||||
entrées (humaine **et** inter-agents) convergent vers **une file FIFO unique par
|
||||
agent**, `enqueue`/`preempt` distincts. Le terminal xterm n'écrit **plus jamais
|
||||
en direct dans le PTY** ; il devient une **vue de sortie pure**. La file existante
|
||||
(`AgentMailbox` + `ask_locks`) est **absorbée** par le `InputMediator` : la
|
||||
messagerie inter-agents n'est qu'une **source d'entrée parmi deux**.
|
||||
|
||||
3. **`FileGuard` (nouveau port domaine)** : un verrou lecteurs/écrivain **borné** aux
|
||||
fichiers qu'IdeA possède (`.md` de contexte d'agent + mémoire). Les agents perdent
|
||||
l'accès fs brut à ces chemins et passent par de **nouveaux outils MCP**
|
||||
`idea_context_read/propose` et `idea_memory_read/write`. Le contexte **global
|
||||
projet** est **mono-écrivain (l'orchestrateur)** ; les autres *proposent*.
|
||||
|
||||
4. **Détection occupé/libre = double signal avec fallback sûr** : (a) **retour-de-prompt**
|
||||
détecté par motif déclaré dans le profil CLI, (b) **signal explicite** de l'agent
|
||||
(un `idea_reply`, ou fin de tour MCP). **En cas de doute → forwarder** (on
|
||||
enqueue ; jamais piéger un message). L'occupé/libre remonte au front via un
|
||||
`DomainEvent` (Channel Tauri), pas via parsing front.
|
||||
|
||||
5. **Fixes durables embarqués** : `bind_endpoint` unlink déjà le socket cadavre
|
||||
(`reclaim_name(true)`, état OK — on **verrouille ce comportement par un test de
|
||||
non-régression**) ; le band-aid `\n`→`\r` et l'« injection PTV » de
|
||||
`service.rs:459` **disparaissent** (l'entrée passe désormais par le `InputMediator`,
|
||||
pas par une écriture PTY préfixée d'un orchestrateur).
|
||||
|
||||
6. **Garde-fous d'orchestration** : timeout par tour (déjà), **plafond d'attente en
|
||||
file** (déjà, `ASK_QUEUE_WAIT_CAP`), **détection de cycle** sur un graphe wait-for
|
||||
(nouveau, dans le domaine — pur, testable) pour refuser une délégation
|
||||
ré-entrante (A→B→A) avant deadlock.
|
||||
|
||||
---
|
||||
|
||||
## 1. Modèle de domaine
|
||||
|
||||
### 1.1 Nouvelles entités / VO
|
||||
|
||||
#### `ConversationId` (VO)
|
||||
- `newtype(uuid::Uuid)`, calqué sur `TicketId`/`AgentId`. Immuable, non vide.
|
||||
- **Implémenté** : `crates/domain/src/conversation.rs` (nouveau module, à exporter
|
||||
dans `lib.rs` à côté de `mailbox`).
|
||||
|
||||
#### `ConversationParty` (VO, enum)
|
||||
```text
|
||||
ConversationParty =
|
||||
| User // l'humain (une seule instance logique côté IdeA)
|
||||
| Agent(AgentId) // un agent du projet
|
||||
```
|
||||
- Invariant : une `Conversation` relie **deux parties distinctes** (jamais
|
||||
`Agent(x)↔Agent(x)`, jamais `User↔User`).
|
||||
|
||||
#### `Conversation` (entité)
|
||||
```text
|
||||
Conversation {
|
||||
id: ConversationId,
|
||||
left: ConversationParty,
|
||||
right: ConversationParty,
|
||||
session: ConversationSession, // état d'I/O (voir 1.2)
|
||||
resumable_id: Option<String>, // session-id reprenable de la CLI (suspend = stocke)
|
||||
}
|
||||
```
|
||||
- **Invariants** : `left != right` ; au plus **une** des deux parties est `User` ;
|
||||
identité d'une conversation = la **paire non ordonnée** `{left, right}` pour un
|
||||
agent donné (deux paires identiques ⇒ même conversation — clé de la matérialisation
|
||||
paresseuse). Pur, I/O-free.
|
||||
- **Matérialisation paresseuse** : une `Conversation` `Agent↔Agent` n'existe en
|
||||
registre que s'il y a **au moins une tâche** ; suspendue, elle ne garde que
|
||||
`resumable_id` (pas de session vivante). C'est une **règle du `ConversationRegistry`**
|
||||
(application), pas un champ persistant lourd.
|
||||
|
||||
#### `ConversationSession` (VO, enum — l'état d'I/O du fil)
|
||||
```text
|
||||
ConversationSession =
|
||||
| Dormant // jamais lancée, ou suspendue (resumable_id seul)
|
||||
| Live { handle_ref: SessionRef } // un flux d'I/O vivant (PTY ou structuré)
|
||||
```
|
||||
- `SessionRef` = abstraction d'un handle de session (référence vers une `TerminalSession`
|
||||
existante, cf. `domain/src/terminal.rs`). Le domaine ne tient **pas** le PTY (infra).
|
||||
|
||||
#### `Task` / `Ticket` (extension de l'existant)
|
||||
- `Ticket` (`domain/src/mailbox.rs`) est **étendu** pour porter **l'origine** et la
|
||||
**conversation cible** :
|
||||
```text
|
||||
Ticket {
|
||||
id: TicketId, // existant
|
||||
source: InputSource, // NOUVEAU : Human | Agent(AgentId)
|
||||
conversation: ConversationId, // NOUVEAU : le fil dans lequel la tâche entre
|
||||
requester: String, // existant (label d'affichage du préfixe)
|
||||
task: String, // existant
|
||||
}
|
||||
```
|
||||
- `InputSource` (VO, enum) : `Human | Agent(AgentId)`. Remplace l'actuel
|
||||
`requester: String` libre comme **source de vérité** (le `String` reste un label
|
||||
d'affichage dérivé). Permet de **propager l'identité du demandeur** (D) et
|
||||
d'alimenter le graphe wait-for (détection de cycle).
|
||||
- **Compat** : `Ticket::new` garde sa signature ; on ajoute `Ticket::from_human(...)`
|
||||
et `Ticket::from_agent(source, conversation, ...)` (Open/Closed, pas de breaking).
|
||||
|
||||
#### File FIFO + état occupé/libre (VO)
|
||||
- `AgentInbox` (concept porté par le port `InputMediator`, pas une entité persistée) :
|
||||
**une file FIFO par `AgentId`**, **une tâche en cours à la fois**.
|
||||
- `AgentBusyState` (VO, enum) : `Idle | Busy { ticket: TicketId, since_ms: u64 }`.
|
||||
Dérivé, publié au front. Invariant : un agent passe à `Busy` **à l'enqueue qui
|
||||
démarre un tour** ; revient `Idle` sur **retour-de-prompt** OU **signal explicite**
|
||||
(cf. §6) ; **en cas de doute, reste `Busy`** mais la file **continue d'accepter**
|
||||
(forward, jamais bloquer l'émetteur).
|
||||
|
||||
#### `WaitForGraph` (VO pur — détection de cycle)
|
||||
- `domain/src/conversation.rs` : structure pure `wait_edges: Vec<(AgentId, AgentId)>`
|
||||
(« A attend B »). Fonction pure `would_cycle(graph, from, to) -> bool`.
|
||||
- Invariant : une `AskAgent` de `A` vers `B` est **refusée** (`MailboxError`/`AppError`
|
||||
typé) si elle crée un cycle dans le graphe d'attente (A→B alors que B→…→A).
|
||||
100 % testable sans I/O.
|
||||
|
||||
### 1.2 Invariants transverses
|
||||
|
||||
- **1 session vivante / conversation** (remplace « 1 / agent »). `session_for(conversation)`
|
||||
est déterministe ; `sessions_for_agent(agent)` peut renvoyer N (une par fil actif).
|
||||
- **1 tâche traitée à la fois / agent** : l'`InputMediator` sérialise l'entrée. Deux
|
||||
fils d'un même agent partagent **la même file d'entrée** (le process CLI sous-jacent
|
||||
est unique — « 1 agent = 1 employé »). *Conséquence assumée : un agent occupé par
|
||||
son fil User retarde une délégation entrante — c'est voulu (un employé, une tâche).*
|
||||
- **Séparation stricte des contextes** : écrire dans la conversation `A↔B` ne touche
|
||||
jamais `User↔B`. Garanti par le fait que la session reprise (`resumable_id`) est
|
||||
**par conversation**, pas par agent.
|
||||
|
||||
---
|
||||
|
||||
## 2. Ports (traits domaine)
|
||||
|
||||
> Signatures **conceptuelles**. « Consommé par » = application ; « Implémenté par » = infra/app-tauri.
|
||||
|
||||
### `ConversationRegistry` (NOUVEAU — domaine, `conversation.rs`)
|
||||
- **Rôle** : résoudre/ouvrir paresseusement une conversation pour une paire, tenir son
|
||||
`session`/`resumable_id`, suspendre/reprendre.
|
||||
```rust
|
||||
trait ConversationRegistry: Send + Sync {
|
||||
/// Get-or-create paresseux : retourne le fil de la paire {a,b}, en l'ouvrant
|
||||
/// (Dormant) s'il n'existait pas. Pur registre — n'ouvre AUCUNE session.
|
||||
fn resolve(&self, a: ConversationParty, b: ConversationParty) -> Conversation;
|
||||
/// Marque une conversation Live avec la session donnée.
|
||||
fn bind_session(&self, id: ConversationId, session: SessionRef);
|
||||
/// Suspend : passe Dormant, conserve le resumable_id rendu par la CLI.
|
||||
fn suspend(&self, id: ConversationId, resumable_id: Option<String>);
|
||||
fn get(&self, id: ConversationId) -> Option<Conversation>;
|
||||
}
|
||||
```
|
||||
- **Consommé par** : `OrchestratorService` (au lieu de `session_for_agent` brut),
|
||||
`LaunchAgent`, la reprise au redémarrage.
|
||||
- **Implémenté par** : `InMemoryConversationRegistry` (infra) — `HashMap` + mutex sync,
|
||||
jamais tenu en travers d'un `.await` (cf. `ask_locks` existant).
|
||||
|
||||
### `InputMediator` (NOUVEAU — domaine ou application ; **décision : domaine**, `input.rs`)
|
||||
- **Rôle** : le point de convergence de **toutes** les entrées d'un agent (FIFO unique),
|
||||
avec `enqueue` (Envoyer) et `preempt` (Interrompre) **distincts**, plus l'état busy.
|
||||
```rust
|
||||
trait InputMediator: Send + Sync {
|
||||
/// Envoyer = enqueue : ajoute la tâche en queue FIFO de l'agent, retourne le
|
||||
/// PendingReply à attendre (réutilise le type mailbox existant).
|
||||
fn enqueue(&self, agent: AgentId, ticket: Ticket) -> PendingReply;
|
||||
/// Interrompre = préempte : signale au tour en cours de s'arrêter (Échap/stop).
|
||||
/// N'est PAS un enqueue ; ne corrèle aucun ticket.
|
||||
fn preempt(&self, agent: AgentId);
|
||||
/// Marque l'agent libre (retour-de-prompt ou signal explicite) ⇒ avance la file.
|
||||
fn mark_idle(&self, agent: AgentId);
|
||||
fn busy_state(&self, agent: AgentId) -> AgentBusyState;
|
||||
}
|
||||
```
|
||||
- **Décision frontière** : `InputMediator` **absorbe** `AgentMailbox`. Le mailbox
|
||||
existant devient le **moteur de corrélation par ticket** *interne* à
|
||||
l'implémentation du `InputMediator` (l'`InMemoryMailbox` est réutilisé tel quel, sa
|
||||
FIFO + `oneshot` sont exactement ce qu'il faut). On **n'a donc pas** deux files
|
||||
concurrentes : `ask_locks` (verrou de tour) + `InMemoryMailbox` (slots de réponse)
|
||||
sont unifiés derrière ce port. *(Voir §5 pour le chemin de migration.)*
|
||||
- **Consommé par** : `OrchestratorService::ask_agent` (source = `Agent`), et le
|
||||
**nouveau** use case `SubmitHumanInput` (source = `Human`).
|
||||
- **Implémenté par** : `MediatedInbox` (infra) composant `InMemoryMailbox` + le
|
||||
registre de verrous de tour + l'état busy.
|
||||
|
||||
### `FileGuard` (NOUVEAU — domaine, `fileguard.rs`)
|
||||
- **Rôle** : verrou **lecteurs/écrivain par fichier** sur le périmètre **borné**
|
||||
(contexte `.md` + mémoire). N lecteurs OU 1 écrivain ; mono-écrivain pour le
|
||||
contexte global (l'orchestrateur).
|
||||
```rust
|
||||
enum GuardedResource { // VO — le périmètre borné, fermé
|
||||
AgentContext(AgentId),
|
||||
ProjectContext, // mono-écrivain : orchestrateur uniquement
|
||||
Memory(MemorySlug),
|
||||
}
|
||||
trait FileGuard: Send + Sync {
|
||||
async fn acquire_read(&self, who: ConversationParty, res: GuardedResource)
|
||||
-> Result<ReadLease, GuardError>;
|
||||
async fn acquire_write(&self, who: ConversationParty, res: GuardedResource)
|
||||
-> Result<WriteLease, GuardError>;
|
||||
}
|
||||
```
|
||||
- `ReadLease`/`WriteLease` = gardes RAII (libèrent à la fin de portée). `GuardError`
|
||||
typé : `Busy` (attendre), `Forbidden` (un agent ≠ orchestrateur veut écrire
|
||||
`ProjectContext` ⇒ refus, doit *proposer*).
|
||||
- **Invariant clé** : toute lecture/écriture des ressources gardées **transite par ce
|
||||
port** ; l'accès fs brut à ces chemins est retiré aux agents (cf. §3 outils MCP).
|
||||
- **Consommé par** : `UpdateAgentContext`, `MemoryStore`-consumers, les nouveaux
|
||||
use cases `ReadContext`/`ProposeContext`/`ReadMemory`/`WriteMemory`.
|
||||
- **Implémenté par** : `RwFileGuard` (infra) — `HashMap<GuardedResource, RwLock-like>`
|
||||
(tokio `RwLock` ou sémaphore), + la règle mono-écrivain pour `ProjectContext`.
|
||||
|
||||
### `AgentMailbox` (existant — **conservé**, statut révisé)
|
||||
- Reste le **contrat de rendez-vous par ticket** (corrélation **par `TicketId`**, voir
|
||||
§3.3 — on **abandonne** la corrélation purement positionnelle « tête de file » dès
|
||||
qu'un agent peut avoir plusieurs fils). Devient un **détail d'implémentation** du
|
||||
`InputMediator` ; n'est plus injecté seul dans `OrchestratorService`.
|
||||
|
||||
### Ports inchangés réutilisés
|
||||
- `PtyPort` (écriture du tour dans le PTY = désormais le **seul** chemin d'écriture,
|
||||
piloté par le `InputMediator`, plus par `ask_agent` directement).
|
||||
- `ProfileStore` (porte le **motif de retour-de-prompt** par profil, §6).
|
||||
- `EventBus` (publie `AgentBusyChanged`, `AgentReplied`).
|
||||
|
||||
---
|
||||
|
||||
## 3. Adapters (infra) + outils MCP
|
||||
|
||||
### 3.1 Adapters
|
||||
| Port | Adapter | Notes |
|
||||
|---|---|---|
|
||||
| `ConversationRegistry` | `InMemoryConversationRegistry` | `HashMap<ConversationId, Conversation>` + index paire→id ; mutex sync. |
|
||||
| `InputMediator` | `MediatedInbox` | compose `InMemoryMailbox` (existant) + verrous de tour + état busy ; publie `AgentBusyChanged`. |
|
||||
| `FileGuard` | `RwFileGuard` | `RwLock` par `GuardedResource` ; règle mono-écrivain `ProjectContext`. |
|
||||
| `AgentMailbox` | `InMemoryMailbox` | **inchangé** (réutilisé sous `MediatedInbox`). |
|
||||
|
||||
### 3.2 Nouveaux outils MCP (`infrastructure/src/orchestrator/mcp/tools.rs`)
|
||||
Ajouts **purement additifs** au `catalogue()` (Open/Closed — le dispatch reste intact) :
|
||||
|
||||
- **`idea_context_read { target? }`** → action wire `context.read` →
|
||||
`OrchestratorCommand::ReadContext { target }`. `target` absent = le contexte **global
|
||||
projet** ; sinon le `.md` d'un agent. Passe par `FileGuard::acquire_read`.
|
||||
- **`idea_context_propose { target?, content }`** → `context.propose` →
|
||||
`OrchestratorCommand::ProposeContext`. Pour un agent : écriture directe sous verrou
|
||||
écrivain. Pour le **global** : ce n'est **pas** une écriture, c'est une **proposition**
|
||||
(déposée pour validation par l'orchestrateur/UI ; `FileGuard` refuse l'écriture
|
||||
directe avec `Forbidden`).
|
||||
- **`idea_memory_read { slug? }`** → `memory.read` → `ReadMemory` (sous `FileGuard`).
|
||||
- **`idea_memory_write { slug, content }`** → `memory.write` → `WriteMemory` (verrou
|
||||
écrivain ; mémoire = partagée projet, cf. `shared-project-memory`).
|
||||
|
||||
Chaque outil suit le **patron existant** : `map_tool_call` construit un
|
||||
`OrchestratorRequest`, `validate()` reste l'**unique autorité** de validation, le
|
||||
`requester` du handshake porte l'identité (`ConversationParty::Agent`).
|
||||
|
||||
### 3.3 Corrélation `idea_reply` **par ticket** (D)
|
||||
- **Changement** : aujourd'hui `idea_reply` corrèle **positionnellement** (tête de la
|
||||
file de l'émetteur — `mailbox.resolve(from, result)`). Dès qu'un agent peut avoir
|
||||
**plusieurs fils**, la tête de « sa » file est ambiguë.
|
||||
- **Décision** : le préfixe injecté dans le PTY (`[IdeA · tâche de A · ticket <id>]`)
|
||||
porte **déjà** le `ticket_id`. On expose un champ **optionnel** `ticket` au schéma de
|
||||
`idea_reply` (`{ result, ticket? }`) ; quand présent, `resolve` corrèle **par
|
||||
`TicketId`** (déterministe, multi-fil) ; absent, on **retombe** sur la tête de file
|
||||
(compat agents simples, mono-fil). Le préfixe doit donc **demander à l'agent de
|
||||
renvoyer le `ticket`** (mise à jour de la description outil + protocole §B-5
|
||||
existant). `AgentMailbox::resolve` gagne une variante `resolve_ticket(agent,
|
||||
ticket_id, result)`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontière front : vue de sortie (xterm inchangé) / entrée médiée
|
||||
|
||||
### 4.1 État actuel à modifier
|
||||
`frontend/src/features/terminals/TerminalView.tsx` câble aujourd'hui **directement**
|
||||
les frappes au PTY :
|
||||
```ts
|
||||
const onKey = term.onData((data) => {
|
||||
if (handle) void handle.write(encoder.encode(data)); // ← chemin à couper
|
||||
});
|
||||
```
|
||||
C'est **exactement** le couplage que le Modèle B retire.
|
||||
|
||||
### 4.2 Décision frontend
|
||||
1. **xterm reste la vue de sortie brute, INCHANGÉE** : `onData (PTY) → term.write`
|
||||
conservé tel quel. **Interdiction** de ressusciter `AgentChatView` (déjà supprimé
|
||||
dans le diff courant — ne pas le réintroduire).
|
||||
2. **`term.onData` (frappes) n'écrit plus dans le PTY** pour une cellule **agent**.
|
||||
Deux modes :
|
||||
- **Cellule terminal simple (non-agent)** : comportement actuel conservé (écriture
|
||||
directe — pas de médiation, c'est un shell brut).
|
||||
- **Cellule agent** : les frappes vont dans un **champ de saisie géré par IdeA**
|
||||
(composant `MediatedInput`, rendu **sous** le terminal), pas dans le PTY. xterm
|
||||
passe en lecture seule pour l'entrée (sortie toujours live).
|
||||
3. **Nouveau port UI `InputGateway`** (`frontend/src/ports/index.ts`) :
|
||||
```ts
|
||||
interface InputGateway {
|
||||
submit(projectId: string, agentId: string, text: string): Promise<void>; // Envoyer = enqueue
|
||||
interrupt(projectId: string, agentId: string): Promise<void>; // Interrompre = preempt
|
||||
}
|
||||
```
|
||||
Adapter Tauri : `invoke("submit_agent_input", …)` / `invoke("interrupt_agent", …)`
|
||||
(nouvelles commands app-tauri → `SubmitHumanInput` / `preempt`). Mock pour tests.
|
||||
4. **Occupé/libre remonte par event** : un `DomainEvent::AgentBusyChanged { agent_id,
|
||||
busy }` relayé en event Tauri (pas un Channel haute-fréquence — événement discret).
|
||||
Le `MediatedInput` désactive « Envoyer » pendant `Busy` mais **autorise toujours
|
||||
l'enqueue** (le bouton enfile derrière ; jamais bloqué — fallback « forward »), et
|
||||
active « Interrompre ». Le front **ne parse jamais** la sortie pour deviner l'état.
|
||||
|
||||
### 4.3 Composants/state touchés
|
||||
- `features/terminals/TerminalView.tsx` : brancher le mode agent (entrée détournée).
|
||||
- `features/terminals/MediatedInput.tsx` (**nouveau**) : champ + boutons Envoyer/Interrompre.
|
||||
- `features/layout/LayoutGrid.tsx` : déjà route vers `TerminalView` ; ajoute le
|
||||
`MediatedInput` sous le terminal quand `agent != null`.
|
||||
- `ports/index.ts` + `adapters/agent.ts` (ou nouvel `adapters/input.ts`) + mock.
|
||||
- state : un store léger `agentBusy: Record<agentId, boolean>` alimenté par l'event.
|
||||
|
||||
---
|
||||
|
||||
## 5. Impact sur le code existant
|
||||
|
||||
### 5.1 Supprimé / retiré
|
||||
- **L'écriture PTY préfixée par `ask_agent`** (`service.rs` ~459 :
|
||||
`pty.write(&handle, "[IdeA · tâche …]\n")`) **n'est plus le chemin d'entrée**. La
|
||||
tâche déléguée entre désormais par `InputMediator::enqueue` (qui, dans son impl,
|
||||
écrira la ligne dans le PTY — mais **sérialisée derrière l'entrée humaine** du même
|
||||
agent, ce qui n'était pas le cas avant). → la logique d'écriture **déménage** de
|
||||
`ask_agent` vers l'impl `MediatedInbox`.
|
||||
- **Band-aid `\n`→`\r`** : abandonné (le « mode injection PTV » disparaît). Plus de
|
||||
réécriture de fin de ligne ad hoc.
|
||||
- **`AgentChatView`** (front) : déjà supprimé dans le diff courant — **rester** supprimé.
|
||||
|
||||
### 5.2 Modifié
|
||||
- **`OrchestratorService`** : ne reçoit plus `with_mailbox(mailbox, pty)` séparément
|
||||
mais `with_input_mediator(Arc<dyn InputMediator>)` + `with_conversations(Arc<dyn
|
||||
ConversationRegistry>)`. `ask_agent` devient : résoudre la **conversation A↔B**
|
||||
(paresseux), vérifier le **graphe wait-for** (refus si cycle), `enqueue` la tâche
|
||||
(source = `Agent`), `await PendingReply` borné. `reply` corrèle **par ticket** (§3.3).
|
||||
`ensure_live_pty` reste, mais branché sur `session_for(conversation)` au lieu de
|
||||
`session_for_agent`.
|
||||
- **`session_for_agent`** (registre `terminal/registry.rs`) : devient
|
||||
`session_for(conversation_id)` ; `sessions_for_agent` (pluriel) ajouté. Lève
|
||||
l'ambiguïté `session-registry-agent-ambiguity` **par construction** (la clé est la
|
||||
conversation, pas l'agent).
|
||||
- **`bind_endpoint`** (`state.rs`) : **déjà** `reclaim_name(true)` ⇒ unlink du cadavre.
|
||||
**Action = verrouiller par un test** (ouvrir/fermer/SIGKILL simulé/rebind sans
|
||||
`EADDRINUSE`). Pas de changement de code attendu, sauf si le test révèle un trou.
|
||||
- **`idea_reply`** (tools.rs / orchestrator.rs / server.rs) : champ `ticket?` ajouté,
|
||||
`Reply { from, ticket: Option<TicketId>, result }`, `map_tool_call` le propage.
|
||||
- **`Ticket`** (`mailbox.rs`) : champs `source: InputSource`, `conversation:
|
||||
ConversationId` ajoutés (constructeurs additifs).
|
||||
|
||||
### 5.3 Ajouté
|
||||
- Domaine : `conversation.rs` (`ConversationId`, `Conversation`, `ConversationParty`,
|
||||
`ConversationSession`, `WaitForGraph`), `input.rs` (`InputMediator`, `InputSource`,
|
||||
`AgentBusyState`), `fileguard.rs` (`FileGuard`, `GuardedResource`, leases).
|
||||
- Application : use cases `SubmitHumanInput`, `ReadContext`/`ProposeContext`,
|
||||
`ReadMemory`/`WriteMemory` ; détection de cycle câblée dans `ask_agent`.
|
||||
- Infra : `InMemoryConversationRegistry`, `MediatedInbox`, `RwFileGuard` ; outils MCP
|
||||
`idea_context_*` / `idea_memory_*`.
|
||||
- app-tauri : commands `submit_agent_input`, `interrupt_agent` ; relais event
|
||||
`AgentBusyChanged` ; câblage des nouveaux ports au composition root (`state.rs`).
|
||||
- Front : `MediatedInput`, `InputGateway` + adapter + mock + store busy.
|
||||
|
||||
---
|
||||
|
||||
## 6. Détection occupé/libre
|
||||
|
||||
**Mécanisme retenu = double signal, OR, avec fallback sûr.**
|
||||
|
||||
| Signal | Source | Fiabilité |
|
||||
|---|---|---|
|
||||
| **Retour-de-prompt** | motif (regex/literal) déclaré dans le **profil CLI** (`AgentProfile`, nouveau champ `prompt_ready_pattern: Option<String>`), détecté sur le flux PTY par l'impl `MediatedInbox` | bon pour un shell/CLI au prompt stable ; faillible (motif dans la sortie) |
|
||||
| **Signal explicite** | l'agent appelle `idea_reply` (fin d'une délégation) **ou** un signal de fin-de-tour MCP | déterministe quand l'agent coopère |
|
||||
|
||||
- Transition `Busy → Idle` = **premier** des deux signaux qui arrive.
|
||||
- **Fallback « en cas de doute → forwarder »** : si **aucun** signal n'est sûr (motif
|
||||
absent du profil, agent muet), l'agent **reste marqué `Busy`** mais la file
|
||||
**continue d'accepter** les `enqueue` ; un message entrant **n'est jamais rejeté**,
|
||||
il patiente dans la FIFO. On ne « piège » donc jamais un message ; au pire il attend.
|
||||
- **Garde-fou anti-blocage** : le timeout par tour (`ASK_AGENT_TIMEOUT`, existant)
|
||||
retire le ticket de tête et **relâche** le tour même si aucun signal n'est venu ⇒
|
||||
la file avance. L'agent reste vivant.
|
||||
- Le motif vit **dans le profil** (donnée, pas code) ⇒ ajouter une CLI = éditer un
|
||||
profil (Open/Closed, cohérent §9 CLAUDE.md).
|
||||
|
||||
---
|
||||
|
||||
## 7. Découpage en lots livrables (ordonnés par dépendance)
|
||||
|
||||
> Chaque lot = binôme dev/test. **B = DevBackend (Rust)**, **F = DevFrontend (TS/React)**.
|
||||
> Chemin critique : C1 → C2 → C3 → C4. FileGuard (C6) et front (F1/F2) parallélisables.
|
||||
|
||||
### Bloc Conversation (cœur — backend)
|
||||
| Lot | Côté | Périmètre | Tests |
|
||||
|---|---|---|---|
|
||||
| **C1** | B (domaine) | `conversation.rs` : `ConversationId`, `ConversationParty`, `Conversation`, `ConversationSession`, `WaitForGraph::would_cycle`. `input.rs` : `InputSource`, `AgentBusyState`. Extension `Ticket` (source+conversation, ctors additifs). | invariants paire (left≠right, ≤1 User) ; identité = paire non ordonnée ; `would_cycle` (A→B→A refusé, A→B→C ok) ; ticket porte source+conversation. Pur, sans I/O. |
|
||||
| **C2** | B (domaine+infra) | Ports `ConversationRegistry` + `InputMediator` (domaine) ; adapters `InMemoryConversationRegistry` + `MediatedInbox` (compose `InMemoryMailbox` existant). | resolve paresseux (même paire ⇒ même id) ; enqueue→PendingReply ; preempt distinct d'enqueue ; busy_state transitions ; 2 enqueue même agent sérialisés ; agents ≠ parallèles. |
|
||||
| **C3** | B (application) | `OrchestratorService` : `with_input_mediator`+`with_conversations` ; `ask_agent` réécrit (résout conversation A↔B, garde wait-for, enqueue source=Agent, await) ; `reply` par ticket. `session_for(conversation)`. Retrait écriture PTY directe + band-aid `\r`. | ask A→B route dans la bonne conversation (pas User↔B) ; cycle A→B→A ⇒ erreur typée avant deadlock ; reply corrèle par ticket (multi-fil) ; reply sans ticket = fallback tête ; timeout libère file, cible vivante. |
|
||||
| **C4** | B (application+app-tauri) | Use case `SubmitHumanInput` (source=Human) + commands `submit_agent_input`/`interrupt_agent` ; event `AgentBusyChanged` relayé. Câblage composition root (`state.rs`). | submit humain enfile dans la **même** FIFO que les délégations ; interrupt = preempt (pas enqueue) ; busy event émis aux bons moments ; câblage : un ask et un submit concurrents sur A sérialisent. |
|
||||
|
||||
### Bloc détection occupé/libre (backend)
|
||||
| Lot | Côté | Périmètre | Tests |
|
||||
|---|---|---|---|
|
||||
| **C5** | B (domaine+infra) | Champ profil `prompt_ready_pattern` ; détection retour-de-prompt dans `MediatedInbox` ; OR avec signal explicite ; fallback « reste Busy mais accepte ». | motif détecté ⇒ Idle ; idea_reply ⇒ Idle ; ni l'un ni l'autre ⇒ Busy mais enqueue accepté ; timeout ⇒ file avance. |
|
||||
|
||||
### Bloc FileGuard (backend — parallélisable après C1)
|
||||
| Lot | Côté | Périmètre | Tests |
|
||||
|---|---|---|---|
|
||||
| **C6** | B (domaine+infra) | `fileguard.rs` (port + `GuardedResource` + leases) ; `RwFileGuard` ; règle mono-écrivain `ProjectContext`. | N lecteurs concurrents OK ; 1 écrivain exclusif ; agent≠orchestrateur écrit ProjectContext ⇒ `Forbidden` ; lease RAII libère. |
|
||||
| **C7** | B (application+infra MCP) | Use cases `ReadContext`/`ProposeContext`/`ReadMemory`/`WriteMemory` sous FileGuard ; outils MCP `idea_context_*`/`idea_memory_*` ; retrait accès fs brut de ces chemins. | map_tool_call → command ; validate exige `content` ; propose global ≠ write direct ; lecture concurrente non bloquante ; écriture sérialisée. |
|
||||
|
||||
### Bloc frontend
|
||||
| Lot | Côté | Périmètre | Tests (Vitest/RTL, gateways mock) |
|
||||
|---|---|---|---|
|
||||
| **F1** | F | `InputGateway` (port+adapter+mock) ; `MediatedInput` (Envoyer=submit / Interrompre=interrupt) ; store busy alimenté par event. | submit appelle gateway.submit ; interrupt appelle interrupt ; busy event désactive Envoyer (mais enqueue possible), active Interrompre. |
|
||||
| **F2** | F | `TerminalView` mode agent : frappes → `MediatedInput` (plus le PTY) ; xterm reste sortie live INCHANGÉE pour le non-agent. `LayoutGrid` monte `MediatedInput` sous le terminal si `agent != null`. | cellule agent ⇒ onData ne write pas le PTY ; cellule simple ⇒ comportement actuel ; sortie PTY toujours peinte ; jamais d'AgentChatView. |
|
||||
|
||||
### Bloc durcissement
|
||||
| Lot | Côté | Périmètre | Tests |
|
||||
|---|---|---|---|
|
||||
| **D1** | B (app-tauri) | Test de non-régression `bind_endpoint` : bind → drop (SIGKILL simulé : laisser le fichier socket) → rebind **sans** `EADDRINUSE`. Verrouille `reclaim_name(true)`. | rebind après cadavre OK ; idempotent ; pas de fuite de fichier après close. |
|
||||
|
||||
**Ordre recommandé** : **C1 → C2 → C3 → C4** (cœur), **C5** après C2, **C6 → C7**
|
||||
en parallèle (après C1), **F1 → F2** dès que les commands C4 existent (mock avant),
|
||||
**D1** isolé n'importe quand.
|
||||
|
||||
---
|
||||
|
||||
## 8. Stratégie de tests par couche
|
||||
|
||||
| Couche | Type | Comment |
|
||||
|---|---|---|
|
||||
| **domaine** (`conversation`, `input`, `fileguard`, `mailbox` étendu) | unitaires **purs**, sans I/O ni async là où possible | invariants de paire, `would_cycle`, transitions `AgentBusyState`, ctors `Ticket`. Déterministe. C'est là que vit la garantie « solide par construction ». |
|
||||
| **application** (`OrchestratorService`, `SubmitHumanInput`, use cases FileGuard) | unitaires avec **ports mockés** (fakes manuels, façon `service.rs` actuel) | ask route la bonne conversation ; cycle refusé ; reply par ticket ; submit+ask sérialisés ; FileGuard mono-écrivain. **Aucun vrai PTY/fs/MCP.** |
|
||||
| **infra** (`MediatedInbox`, `RwFileGuard`, `InMemoryConversationRegistry`, outils MCP) | intégration **ciblée** | FIFO réelle + `oneshot` ; RwLock concurrence ; `map_tool_call` round-trip ; `bind_endpoint` (D1). Réutilise les tests `InMemoryMailbox` existants. |
|
||||
| **app-tauri** | commands ↔ use cases | `submit_agent_input`/`interrupt_agent` mappent bien ; event `AgentBusyChanged` émis ; câblage composition root cohérent (endpoint partagé). |
|
||||
| **frontend** (`MediatedInput`, `TerminalView`) | Vitest + RTL, **gateways mock** | entrée détournée hors PTY ; busy désactive Envoyer sans bloquer enqueue ; xterm sortie inchangée ; **sans backend**. |
|
||||
|
||||
---
|
||||
|
||||
## 9. Risques / points ouverts
|
||||
|
||||
1. **Fiabilité de la détection retour-de-prompt** (C5) — le plus dur. Un motif dans la
|
||||
sortie d'un agent peut **faussement** signaler Idle (libère trop tôt) ou ne jamais
|
||||
matcher (reste Busy). *Mitigation* : OR avec le signal explicite `idea_reply` +
|
||||
fallback « reste Busy mais accepte » + timeout par tour. *Reste ouvert* : faut-il un
|
||||
« heartbeat » MCP de fin-de-tour côté CLI ? (hors périmètre immédiat, Claude-only).
|
||||
|
||||
2. **Suspension/reprise de session par conversation** (`resumable_id`) — un agent à N
|
||||
fils doit reprendre **le bon** session-id par fil au redémarrage. Dépend du
|
||||
`session{assignFlag,resumeFlag}` du profil (cf. `conversation-resume-architecture`).
|
||||
*Ouvert* : capacité réelle des CLI à tenir N conversations resumables simultanées
|
||||
pour un même process « 1 agent = 1 employé » — possible conflit entre « N fils » et
|
||||
« 1 process ». **Décision de cadrage** : **1 process/agent**, les fils **partagent
|
||||
la file d'entrée** (sérialisés) ; le `resumable_id` par conversation sert surtout à
|
||||
la **reprise au redémarrage**, pas à du vrai parallélisme intra-process.
|
||||
|
||||
3. **Deadlock & détection de cycle** (`WaitForGraph`) — couvre A→B→A directs et
|
||||
transitifs, mais le graphe doit être **alimenté en temps réel** (arête posée à
|
||||
l'enqueue, retirée au reply/timeout). *Risque* : arête fantôme si un reply se perd
|
||||
⇒ faux positif de cycle. *Mitigation* : retrait d'arête garanti par le RAII du tour
|
||||
(comme `_turn` aujourd'hui) + timeout.
|
||||
|
||||
4. **Corrélation par ticket vs agents « simples »** — un agent qui ne renvoie pas le
|
||||
`ticket` dans `idea_reply` retombe sur la corrélation positionnelle (tête de file),
|
||||
ambiguë en multi-fil. *Mitigation* : protocole §B-5 (description outil) **insiste**
|
||||
sur le renvoi du ticket ; mono-fil reste correct sans. *Ouvert* : forcer le ticket
|
||||
requis casserait des agents simples — on garde optionnel.
|
||||
|
||||
5. **Périmètre FileGuard contournable** — tant que l'agent garde un shell brut (PTY),
|
||||
il peut écrire les `.md`/mémoire **par le filesystem** malgré le verrou MCP. Le
|
||||
verrou n'est étanche que si l'accès fs à ces chemins est **réellement** retiré
|
||||
(sandbox, cf. `agent-permissions-architecture` / Landlock). *Ouvert* : sans sandbox
|
||||
OS, le `FileGuard` est **coopératif** (protège des collisions IdeA↔IdeA, pas d'un
|
||||
agent qui contourne). À acter : FileGuard = correction des collisions **dans le
|
||||
chemin IdeA** d'abord ; étanchéité réelle = lot sandbox ultérieur.
|
||||
|
||||
6. **Migration `AgentMailbox` → `InputMediator`** — risque de double-file transitoire.
|
||||
*Mitigation* : `MediatedInbox` **enveloppe** `InMemoryMailbox` (pas de réécriture),
|
||||
`OrchestratorService` bascule d'un `with_mailbox` vers `with_input_mediator` en un
|
||||
lot (C2→C3), tests existants `InMemoryMailbox` conservés verts.
|
||||
|
||||
---
|
||||
|
||||
*Document maintenu par l'Agent Architecture — cadrage « conversation par paire »,
|
||||
base des lots C1→C7 / F1→F2 / D1 avant tout code.*
|
||||
48
.ideai/briefs/option1-terminal-mcp-design.md
Normal file
48
.ideai/briefs/option1-terminal-mcp-design.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Design — Option 1 « Terminal + MCP » (orchestration inter-agents)
|
||||
|
||||
> Décision produit arbitrée (2026-06-11). Remplace la vue chat structurée par le
|
||||
> terminal natif + délégation inter-agents par outils MCP. Source : agent Architecte.
|
||||
> Statut : **design validé, dev NON commencé** (limite de session atteinte le 2026-06-11,
|
||||
> reset 3:40am Europe/Paris). Reprendre par les lots backend B-0→B-5 et frontend F-1.
|
||||
|
||||
## Objectif
|
||||
- **Vue humaine = terminal brut natif** (PTY interactif). Réflexion live + Échap = natifs CLI, zéro parsing par modèle. On abandonne `AgentChatView`/stream-json comme vue.
|
||||
- **Délégation cross-model via MCP** : `idea_ask_agent(target, task)` bloquant → la cible traite quand libre (FIFO) → rend son résultat via NOUVEL outil `idea_reply(result)` → IdeA débloque l'appelant. Fin-de-tour = signal MCP explicite.
|
||||
- Principes : 1 agent = 1 employé (1 process/session, input FIFO) ; hexagonal + SOLID stricts ; plus aucun `parse_event` requis pour vue ni orchestration.
|
||||
|
||||
## Découvertes clés de l'architecte (état réel du code)
|
||||
1. La **file FIFO existe déjà** : `OrchestratorService` (`crates/application/src/orchestrator/service.rs`) a `ask_locks: Mutex<HashMap<AgentId, Arc<AsyncMutex<()>>>>` + `ask_lock_for()` + `ASK_QUEUE_WAIT_CAP` (600s) + `ASK_AGENT_TIMEOUT` (300s). On la formalise en port `AgentMailbox` (pour porter un `oneshot` de réponse).
|
||||
2. `idea_ask_agent` → `agent.message` → `OrchestratorCommand::AskAgent{target_agent, task}` **déjà câblé** (mcp/tools.rs, domain/orchestrator.rs, service.rs). On réimplémente le **corps** de `ask_agent()`.
|
||||
3. Aujourd'hui `ask_agent` **exige une session structurée** et renvoie `AppError::Invalid` si la cible est en PTY brut (service.rs ~400-410). **Inverser cette branche** : PTY vivant = canal normal.
|
||||
4. Routage structuré dans `crates/application/src/agent/lifecycle.rs` (`LaunchAgent` ~1100). Levier de bascule : **ne plus injecter la fabrique structurée au composition root** (`crates/app-tauri/src/state.rs`, `with_structured`).
|
||||
5. `apply_mcp_config` (lifecycle.rs ~1391) écrit déjà `.mcp.json` + `--mcp-config` AVANT le spawn, **chemin PTY inclus** → la CLI PTY a déjà le serveur MCP IdeA (à vérifier par test B-0). Vigilance : `ensure_mcp_server` doit piloter `McpServer::serve` sur le loopback.
|
||||
6. `idea_reply` n'existe nulle part : seul vrai ajout de surface.
|
||||
|
||||
## Lots BACKEND (Rust — agent dev backend) ; NE PAS faire B-6 (nettoyage) avant coordination
|
||||
- **B-0** Prérequis transport MCP : garantir CLI PTY reçoit `--mcp-config <path>` (endpoint/project/requester) + `serve` piloté loopback. Test : CLI factice PTY appelle `idea_list_agents`, reçoit réponse.
|
||||
- **B-1** Port `AgentMailbox` + `InMemoryMailbox`. Domaine pur (`crates/domain/src/mailbox.rs` ou ports.rs) : trait + `Ticket{id,requester,task}`, `TicketId`, `MailboxError`. Infra (`crates/infrastructure/src/mailbox/`) : `HashMap<AgentId, VecDeque<(Ticket, oneshot::Sender<String>)>>` + mutex ; `enqueue` rend `PendingReply` (sur `oneshot::Receiver`). Tests : FIFO ; `resolve` réveille le bon pending ; 2 ask même cible sérialisés ; cibles ≠ non bloquants ; timeout retire ticket de tête.
|
||||
- **B-2** Bascule routage : tous en PTY. `state.rs` : retirer `with_structured` de `LaunchAgent`/`OrchestratorService`/`ChangeAgentProfile`. Tests : profil Claude → PTY ; DTO renvoie `CellKind::Pty`. Ne pas supprimer `launch_structured` (mort-code, nettoyage ultérieur).
|
||||
- **B-3** Réimplémenter `ask_agent` : résoudre id → `mailbox.enqueue` → ticket en tête → garantir cible vivante PTY (sinon LaunchAgent PTY bg) → `PtyPort::write` préfixe `[IdeA · tâche de {A} · ticket {id}] {task}\n` → `await PendingReply` borné `ASK_AGENT_TIMEOUT`. PTY vivant = normal. Timeout : garder agent vivant, retirer ticket de tête. Publier `AgentReplied`. Injecter `Arc<dyn AgentMailbox>` + `Arc<dyn PtyPort>`. Tests : injection bon handle ; agent mort relancé ; timeout libère file ; AgentReplied.
|
||||
- **B-4** Outil/action `idea_reply` : `ToolDef idea_reply` (schéma `{result:string}` seul, pas de ticket_id exposé), action wire `agent.reply`, `OrchestratorCommand::Reply{from:AgentId, result}`, `validate`, `map_tool_call` (passe `requester` du handshake comme `from`), bras dispatch → `mailbox.resolve(from, result)`. Corrélation implicite : `idea_reply` résout le ticket en tête de la file de l'émetteur (identité via handshake, pas via id géré par le modèle). `tool_returns_reply` : idea_reply = ACK sans inline. Tests : mapping ; validate exige result ; resolve corrèle tête ; reply sans ask = erreur typée (pas de panic).
|
||||
- **B-5** Protocole délégation dans le contexte : injecter dans convention file (`apply_injection`) + description outil : « reçois `[IdeA · tâche …]` → traite → appelle IMPÉRATIVEMENT `idea_reply(result=…)` ; ne réponds jamais qu'en texte. » Test : convention file contient l'instruction.
|
||||
|
||||
## Lots FRONTEND (TS/React — agent dev frontend) ; NE PAS faire F-2 (suppression) avant coordination
|
||||
- **F-1** Router toute cellule agent vers `TerminalView` (jamais `AgentChatView`) ; ré-attache PTY + scrollback OK. Backend renverra `cellKind:"pty"`. Lire `frontend/src/features/layout/LayoutGrid.tsx`, `features/chat/AgentChatView.tsx`, `TerminalView`, `adapters/agent.ts`, `ports/index.ts`, `domain/index.ts`. Laisser `AgentChatView` inerte (non monté), pas supprimé. Tests Vitest : agent rend `TerminalView`, jamais `AgentChatView` ; re-mount repeint pty.
|
||||
|
||||
## Ordre / dépendances
|
||||
```
|
||||
B-0 ─┬─ B-2 ─┬─ B-3 ─ B-4 ─ B-5
|
||||
B-1 ─┘ └─ F-1
|
||||
Nettoyage (B-6, F-2) en dernier, coordonné.
|
||||
```
|
||||
Chemin critique : B-0 → B-2 → B-3 → B-4 → B-5. B-1 ∥ B-0. F-1 dès B-2.
|
||||
|
||||
## Cohérence
|
||||
Domaine sans I/O (port + entités pures) ; oneshot/PTY/MCP = infra ; application via ports. Open/Closed (idea_reply = ajout, dispatch intact) ; Liskov (Claude/Codex identiques derrière PTY+MCP) ; 1 process/agent préservé.
|
||||
|
||||
## Fichiers à toucher
|
||||
- Domaine : `mailbox.rs` (nouveau) / `ports.rs` ; `orchestrator.rs` (variante `Reply` + action `agent.reply`).
|
||||
- Application : `orchestrator/service.rs` (ask_agent + reply + injection ports) ; `agent/structured.rs` (supprimé au nettoyage) ; `agent/lifecycle.rs` (routage).
|
||||
- Infra : `mailbox/` (nouveau) ; `orchestrator/mcp/tools.rs` (idea_reply) ; `orchestrator/mcp/server.rs` (passer requester).
|
||||
- app-tauri : `state.rs` (retrait with_structured + injection mailbox + ensure_mcp_server) ; `commands.rs`/`dto.rs` (nettoyage ultérieur).
|
||||
- Frontend : `features/layout/LayoutGrid.tsx` (routage TerminalView) ; `features/chat/*` (nettoyage ultérieur).
|
||||
35
.ideai/briefs/validation-reelle-inter-agents.md
Normal file
35
.ideai/briefs/validation-reelle-inter-agents.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Protocole — Validation réelle de la conversation inter-agents via IdeA
|
||||
|
||||
> À exécuter depuis la **nouvelle AppImage** (build 2026-06-10 18:23, contenant R0+A0+M5,
|
||||
> commits `37e7274` / `6ca519b` / `cf89b3b`). L'ancienne image (testée avant) ne contenait
|
||||
> pas ce code et a renvoyé l'erreur attendue « agent Ask pas pilotable en mode structuré ».
|
||||
|
||||
## But
|
||||
Prouver en conditions réelles qu'un agent (Main/Claude) peut **demander** une tâche à un autre
|
||||
agent (Ask/Codex) **via IdeA** et **recevoir sa réponse inline** — pas seulement par tests à fakes.
|
||||
|
||||
## Pré-requis pour que le `ask` aboutisse
|
||||
- La cible (**Ask**) doit être pilotée en **mode structuré** (profil Codex avec adapter structuré).
|
||||
Le menu de sélection d'agent ne propose normalement que des profils structurés (Claude/Codex).
|
||||
- Ask **ne doit pas** déjà tourner comme **terminal brut** (PTY) dans une cellule : sinon
|
||||
`ask_agent` refuse (invariant « 1 session/agent », cible PTY brut = pas de canal de réponse).
|
||||
- Le plus simple : **laisser Ask éteint** et laisser `ask_agent` le **lancer lui-même** en
|
||||
structuré (sémantique : cible morte ⇒ launch structuré background ⇒ send ⇒ Final).
|
||||
|
||||
## Procédure (protocole fichier, identique au test précédent)
|
||||
1. Déposer `.ideai/requests/main/<nom>.json` :
|
||||
```json
|
||||
{ "type": "agent.message", "requestedBy": "Main", "targetAgent": "Ask",
|
||||
"task": "Petite recherche, pas de code : résume en 3 points ce que fait le module
|
||||
crates/infrastructure/src/orchestrator/mcp/ et liste les outils idea_*." }
|
||||
```
|
||||
2. Attendre l'apparition de `.ideai/requests/main/<nom>.json.response.json`.
|
||||
|
||||
## Succès attendu
|
||||
`{ "ok": true, "action": "agent.message", "reply": "<réponse de Codex>" }` — le champ **`reply`**
|
||||
porte le contenu produit par Ask. (Échec précédent = `ok:false` + erreur PTY brut.)
|
||||
|
||||
## Voie native MCP (bonus)
|
||||
Le bind transport S-MCP (M5) est livré : un agent lancé avec un profil MCP voit les outils
|
||||
`idea_*` (dont `idea_ask_agent`) et le résultat revient inline. À valider quand un profil MCP
|
||||
est branché sur Claude/Codex. Voir `.ideai/briefs/orchestration-v5-transport-bind-cadrage.md`.
|
||||
@ -1,25 +1,26 @@
|
||||
{
|
||||
"version": 1,
|
||||
"activeId": "eed26045-b208-47ff-98ee-ca0d6e5933b3",
|
||||
"activeId": "2fc8a7df-0bf6-4116-acd6-895ae04aa3e5",
|
||||
"layouts": [
|
||||
{
|
||||
"id": "eed26045-b208-47ff-98ee-ca0d6e5933b3",
|
||||
"id": "2fc8a7df-0bf6-4116-acd6-895ae04aa3e5",
|
||||
"name": "Default",
|
||||
"kind": "terminal",
|
||||
"tree": {
|
||||
"root": {
|
||||
"type": "split",
|
||||
"node": {
|
||||
"id": "8cbe4c7c-357f-49ad-ac20-c96802a84684",
|
||||
"id": "1eb96c70-e954-42d0-907b-f8528d0442bf",
|
||||
"direction": "row",
|
||||
"children": [
|
||||
{
|
||||
"node": {
|
||||
"type": "leaf",
|
||||
"node": {
|
||||
"id": "e8933dbd-1c53-4342-96ca-020b9a9b7970",
|
||||
"session": "51c96008-f875-4515-9b5b-50c4217f64d6",
|
||||
"agent": "a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
"id": "db40e3de-4980-4bed-b6fa-1c136f49d30e",
|
||||
"session": "d7d4c099-eda3-4b33-82e6-e6f8d57b8b97",
|
||||
"agent": "a6ced819-b893-4213-b003-9e9dc79b9641",
|
||||
"agentWasRunning": true
|
||||
}
|
||||
},
|
||||
"weight": 1.0
|
||||
@ -28,8 +29,10 @@
|
||||
"node": {
|
||||
"type": "leaf",
|
||||
"node": {
|
||||
"id": "50da405b-18f3-4273-9fe0-57bb242cb2f7",
|
||||
"session": "5a796d0e-433e-4ded-9955-ed02b389d9c1"
|
||||
"id": "92826533-1a7c-4d9e-b6b4-f1e904ddc81a",
|
||||
"session": "5d20f53a-ac76-4677-9a6f-064d3319cc73",
|
||||
"agent": "edce8090-4c57-47c5-a319-c08fd172438b",
|
||||
"agentWasRunning": true
|
||||
}
|
||||
},
|
||||
"weight": 1.0
|
||||
|
||||
5
.ideai/memory/MEMORY.md
Normal file
5
.ideai/memory/MEMORY.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Memory Index
|
||||
|
||||
- [agent-context-memory-and-profile-handoff](agent-context-memory-and-profile-handoff.md) — Decisions sur l'injection de contexte, la memoire durable, l'etat live et le handoff de profil entre agents IA.
|
||||
- [idea-product-directives-main-handoff](idea-product-directives-main-handoff.md) — Directives produit consolidees pour guider Main sur la robustesse, la persistance, le handoff cross-profile et la sobriete UX.
|
||||
- [remaining-work-idea-agent-control-ide](remaining-work-idea-agent-control-ide.md) — Etat des lieux des acquis et des chantiers restants pour aligner IdeA avec la cible d'IDE de controle d'agents IA.
|
||||
106
.ideai/memory/agent-context-memory-and-profile-handoff.md
Normal file
106
.ideai/memory/agent-context-memory-and-profile-handoff.md
Normal file
@ -0,0 +1,106 @@
|
||||
---
|
||||
name: agent-context-memory-and-profile-handoff
|
||||
description: Decisions sur l'injection de contexte, la memoire durable, l'etat live et le handoff de profil entre agents IA.
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# Agent Context, Memory, and Profile Handoff
|
||||
|
||||
## Resume
|
||||
|
||||
This note captures the current product direction for IdeA around agent context injection, project memory, live state, and profile handoff between AI providers.
|
||||
|
||||
See also:
|
||||
|
||||
- `idea-product-directives-main-handoff` for product priorities and UX constraints.
|
||||
- `remaining-work-idea-agent-control-ide` for the current implementation status and remaining work.
|
||||
|
||||
## Context Injection
|
||||
|
||||
- Agent context must be injected by IdeA at launch time; the model should not be expected to discover `AGENTS.md` or `CLAUDE.md` by itself.
|
||||
- The existing `contextInjection` architecture is the right mechanism, especially `conventionFile` for providers that support a conventional file in the run directory.
|
||||
- The current implementation is strongest for `conventionFile`; `flag`, `stdin`, and `env` do not yet receive the same fully-composed IdeA context.
|
||||
- The `flag` strategy appears fragile with the isolated run directory model because the relative path passed to the CLI may not resolve from the run directory.
|
||||
|
||||
## Shared Project Context
|
||||
|
||||
- `.ideai/CONTEXT.md` is intended as shared project context.
|
||||
- It is not created automatically; it only exists if something writes it.
|
||||
- It should carry active project constraints and contribution rules.
|
||||
- Example content for `CONTEXT.md`: architectural constraints, workflow rules, and operating conventions that every agent must apply immediately.
|
||||
|
||||
## Durable Memory
|
||||
|
||||
- `.ideai/memory/` is intended as durable, project-scoped memory shared by all agents of the same project.
|
||||
- It is not created automatically; it only exists once at least one memory note is saved.
|
||||
- The durable memory is a knowledge base, not a live activity log.
|
||||
- It should contain stabilised knowledge such as:
|
||||
- architecture decisions
|
||||
- feature summaries to implement later
|
||||
- user preferences that persist across sessions
|
||||
- important project facts and references
|
||||
- Durable memory should stay curated and low-noise.
|
||||
|
||||
## Live State Versus Durable Memory
|
||||
|
||||
- Durable memory should not be used as a shared real-time state feed for all agents.
|
||||
- IdeA should distinguish between:
|
||||
- global context (`.ideai/CONTEXT.md`)
|
||||
- durable memory (`.ideai/memory/`)
|
||||
- live operational state (separate store)
|
||||
- handoff summaries between sessions or profiles
|
||||
- Real-time work tracking, who-is-doing-what, and transient status should live in a dedicated state mechanism, not in durable memory.
|
||||
|
||||
## Memory Consumption by Agents
|
||||
|
||||
- The current launcher reads shared project context from `.ideai/CONTEXT.md` if present.
|
||||
- It also recalls project memory via `MemoryRecall` and injects a `# Memoire projet` section into the convention file.
|
||||
- This injection currently happens only for `conventionFile` profiles.
|
||||
- The recalled memory is shared at the project level, but each agent may receive a different subset depending on its persona and recall query.
|
||||
- Memory recall is computed at launch time and written into the generated context file.
|
||||
- There is currently no automatic live refresh when `.ideai/memory/` changes during an active session.
|
||||
|
||||
## Recommendation for Live Memory Refresh
|
||||
|
||||
- Automatic memory refresh could be useful, but it should be explicit and controlled.
|
||||
- If IdeA wants agents to benefit from memory changes while they are active, it should regenerate their effective context when needed instead of treating durable memory as a constantly streaming log.
|
||||
- For PTY agents, no automatic reread exists today.
|
||||
- For structured Claude/Codex sessions, each turn relaunches the CLI, but the generated convention file is not automatically rewritten when durable memory changes.
|
||||
|
||||
## Profile Handoff and Session Continuity
|
||||
|
||||
- A direct native session transfer from Claude to Codex is not the right mental model.
|
||||
- The correct model is continuity of work state, not native provider-session continuity.
|
||||
- IdeA should persist:
|
||||
- a canonical conversation log
|
||||
- a cumulative handoff summary
|
||||
- the agent state
|
||||
- the provider conversation id when useful
|
||||
- On provider swap, IdeA should launch the new profile with:
|
||||
- regenerated project context
|
||||
- regenerated durable memory recall
|
||||
- the current agent persona
|
||||
- a handoff summary plus recent transcript
|
||||
- The handoff summary should not be created only at the moment of swap.
|
||||
- IdeA should maintain summaries incrementally or at checkpoints so that a swap is still possible when the current provider is near a token or session limit.
|
||||
|
||||
## Agent Ability To Write Durable Memory
|
||||
|
||||
- Agents should be allowed to promote important knowledge into durable memory.
|
||||
- This should not rely on the agent guessing the capability.
|
||||
- IdeA should expose the capability explicitly through tools or commands and should inject a clear rule explaining when an agent may save durable knowledge.
|
||||
- This write ability should be constrained to stable, high-value information, not transient state.
|
||||
|
||||
## Practical Classification Rule
|
||||
|
||||
- Put immediate project rules and operating constraints in `.ideai/CONTEXT.md`.
|
||||
- Put stable, reusable project knowledge in `.ideai/memory/`.
|
||||
- Put current activity and coordination state in a separate live-state mechanism.
|
||||
- Put cross-session or cross-profile recovery material in a handoff/session layer.
|
||||
|
||||
## Current Product Direction
|
||||
|
||||
- Keep `CONTEXT.md` for project rules.
|
||||
- Keep `.ideai/memory/` for curated durable knowledge.
|
||||
- Introduce a separate live-state mechanism if agents must stay aligned on in-progress work.
|
||||
- Introduce persistent conversation logs and incremental handoff summaries to support profile swaps such as Claude to Codex.
|
||||
206
.ideai/memory/idea-product-directives-main-handoff.md
Normal file
206
.ideai/memory/idea-product-directives-main-handoff.md
Normal file
@ -0,0 +1,206 @@
|
||||
---
|
||||
name: idea-product-directives-main-handoff
|
||||
description: Directives produit consolidees pour guider Main sur la robustesse, la persistance, le handoff cross-profile et la sobriete UX.
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# IdeA Product Directives For Main
|
||||
|
||||
## Resume
|
||||
|
||||
Cette note consolide les arbitrages produit explicites donnes par l'utilisateur pour aider `Main` a poursuivre le projet sans ambiguite.
|
||||
|
||||
See also:
|
||||
|
||||
- `agent-context-memory-and-profile-handoff` for the structural model of context, durable memory, live state, and handoff.
|
||||
- `remaining-work-idea-agent-control-ide` for the current implementation status and remaining work.
|
||||
|
||||
Elle ne remplace pas les notes techniques existantes. Elle sert de reference prioritaire sur:
|
||||
|
||||
- la robustesse attendue,
|
||||
- la persistance et la reprise,
|
||||
- le handoff entre profils IA,
|
||||
- la memoire projet partagee,
|
||||
- le live state projet,
|
||||
- la sobriete UX.
|
||||
|
||||
## Priorite Absolue
|
||||
|
||||
La priorite produit numero un est la robustesse.
|
||||
|
||||
Ordre de priorite impose:
|
||||
|
||||
1. robustesse et solidite avant tout
|
||||
2. persistance et reprise
|
||||
3. handoff cross-profile
|
||||
4. live state projet
|
||||
5. reste des features et du polish
|
||||
|
||||
Regle de pilotage:
|
||||
|
||||
- un systeme incomplet mais solide vaut mieux qu'un systeme riche mais fragile
|
||||
- `Main` doit privilegier les architectures et comportements qui reduisent les crashes, les incoherences d'etat et les flows difficiles a reprendre
|
||||
|
||||
## Reprise Et Persistance
|
||||
|
||||
Quand IdeA redemarre, l'objectif n'est pas seulement de rouvrir une UI ou de restaurer des handles techniques.
|
||||
|
||||
La cible produit est:
|
||||
|
||||
- qu'un agent sache compactement sur quoi il travaillait
|
||||
- qu'IdeA fournisse ce materiel de reprise automatiquement
|
||||
- que la reprise soit exploitable meme si la conversation visible precedente n'est pas restauree a l'identique
|
||||
|
||||
Le bon modele est:
|
||||
|
||||
- un log canonique IdeA comme source durable
|
||||
- un resume/handoff genere par IdeA comme couche compacte de reprise
|
||||
|
||||
Le resume/handoff n'est pas un confort secondaire. Il fait partie du comportement normal du produit.
|
||||
|
||||
## Handoff Cross-Profile
|
||||
|
||||
La cible ideale est double:
|
||||
|
||||
- reprendre correctement le travail
|
||||
- donner si possible une impression de continuite presque sans rupture
|
||||
|
||||
Mais en cas de compromis, la priorite doit etre:
|
||||
|
||||
- fidelite operationnelle du travail repris
|
||||
- avant la parfaite illusion de continuite terminale ou conversationnelle
|
||||
|
||||
Autrement dit:
|
||||
|
||||
- si un agent passe de Claude a Codex, IdeA doit d'abord garantir que Codex puisse reprendre le plus fidelement possible le travail utile
|
||||
- l'absence de restauration parfaite de l'ancien terminal est acceptable si le handoff reste bon
|
||||
|
||||
## Perimetre Profils
|
||||
|
||||
Le perimetre de reference immediat est:
|
||||
|
||||
- Claude
|
||||
- Codex
|
||||
|
||||
Toute fonctionnalite importante doit etre faisable pour ces deux profils.
|
||||
|
||||
Directive associée:
|
||||
|
||||
- reduire au maximum les dependances a des commandes, flags ou comportements specifiques a un profil
|
||||
- construire un noyau le plus generique possible tout en restant concretement compatible Claude/Codex
|
||||
- les autres profils pourront etre ajoutes plus tard si possible, mais ne doivent pas detourner le coeur du chantier actuel
|
||||
|
||||
## Memoire Projet Partagee
|
||||
|
||||
La memoire projet partagee doit rester petite, stable et utile.
|
||||
|
||||
Elle ne doit pas devenir un gros bloc qui siphonne les tokens de l'utilisateur a chaque requete.
|
||||
|
||||
Ce qu'un agent peut ecrire automatiquement dans la memoire partagee si c'est stable et utile:
|
||||
|
||||
- decisions durables d'architecture ou d'organisation
|
||||
- preferences utilisateur durables
|
||||
- regles de workflow durables
|
||||
- references importantes a conserver
|
||||
- resumes de handoff utiles a la reprise inter-session ou inter-profil
|
||||
|
||||
Ce qu'un agent ne doit pas y ecrire automatiquement:
|
||||
|
||||
- conversations brutes
|
||||
- journaux detailles de travail
|
||||
- etats temporaires
|
||||
- files d'attente
|
||||
- coordination temps reel
|
||||
- essais/erreurs locaux
|
||||
- hypotheses non stabilisees
|
||||
- contenu redondant ou reconstructible ailleurs
|
||||
|
||||
Principe de fond:
|
||||
|
||||
- memoire durable = savoir stable
|
||||
- log canonique = historique
|
||||
- handoff = reprise compacte
|
||||
- live state = coordination vivante
|
||||
|
||||
Ces couches doivent rester separees.
|
||||
|
||||
## Live State Projet
|
||||
|
||||
Le live state projet partage doit exister comme mecanisme interne d'IdeA.
|
||||
|
||||
Contraintes produit:
|
||||
|
||||
- il doit rester invisible pour l'utilisateur
|
||||
- il doit survivre au redemarrage d'IdeA
|
||||
|
||||
Il ne doit pas se transformer en UI verbeuse ni en mecanisme demandant une intervention explicite de l'utilisateur.
|
||||
|
||||
## UX Et Philosophie Produit
|
||||
|
||||
IdeA doit etre tres facile d'utilisation.
|
||||
|
||||
Objectif UX:
|
||||
|
||||
- plug and play
|
||||
- pas de sensation de parametrage impose
|
||||
- pas de surcharge de tuto au premier lancement
|
||||
- pas d'impression que le produit force des comportements internes a l'utilisateur
|
||||
|
||||
Ligne directrice souhaitee:
|
||||
|
||||
- esprit "maniere Linux"
|
||||
- comportement simple et utile par defaut
|
||||
- pas de contrainte tant qu'il n'y a pas un vrai besoin
|
||||
- suggestion discrete seulement si IdeA detecte qu'une aide ou une optimisation devient utile
|
||||
|
||||
Le precedent du compactage de contexte est considere comme la bonne direction:
|
||||
|
||||
- pas de compactage impose d'emblee
|
||||
- une popup proposee seulement si IdeA sent un besoin
|
||||
|
||||
## Transparence Des Mecanismes Internes
|
||||
|
||||
Les mecanismes suivants doivent rester quasi invisibles pour l'utilisateur:
|
||||
|
||||
- delegations inter-agents
|
||||
- FIFO
|
||||
- handoffs
|
||||
|
||||
Ils peuvent devenir visibles en debug ou quand le produit a une bonne raison UX de les exposer, mais ils ne doivent pas etre ressentis comme une charge cognitive normale d'utilisation.
|
||||
|
||||
## Frontiere Avec Le Chantier Inter-Agents De Main
|
||||
|
||||
Les choix fins touchant la communication entre agents ne doivent pas etre recadres ici si `Main` est deja en train de les traiter.
|
||||
|
||||
Cette note ne doit donc pas etre lue comme une specification d'implementation inter-agents detaillee.
|
||||
|
||||
Elle fixe seulement les invariants produit suivants:
|
||||
|
||||
- robustesse avant richesse fonctionnelle
|
||||
- reprise automatique par IdeA
|
||||
- log canonique + handoff genere par IdeA
|
||||
- support de reference pour Claude et Codex
|
||||
- memoire durable compacte et curatee
|
||||
- live state interne et persistant
|
||||
- UX discrete, simple et peu intrusive
|
||||
|
||||
## Directive Finale Pour Main
|
||||
|
||||
Si un arbitrage technique oppose:
|
||||
|
||||
- elegance theorique
|
||||
- ou livraison rapide
|
||||
|
||||
contre:
|
||||
|
||||
- robustesse
|
||||
- reprise fiable
|
||||
- sobriete UX
|
||||
|
||||
alors `Main` doit privilegier:
|
||||
|
||||
- robustesse
|
||||
- reprise fiable
|
||||
- sobriete UX
|
||||
|
||||
avant le reste.
|
||||
273
.ideai/memory/remaining-work-idea-agent-control-ide.md
Normal file
273
.ideai/memory/remaining-work-idea-agent-control-ide.md
Normal file
@ -0,0 +1,273 @@
|
||||
---
|
||||
name: remaining-work-idea-agent-control-ide
|
||||
description: Etat des lieux des acquis et des chantiers restants pour aligner IdeA avec la cible d'IDE de controle d'agents IA.
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# Remaining Work For IdeA Agent Control IDE
|
||||
|
||||
## Resume
|
||||
|
||||
Cette note sert de point de reprise pour l'agent `Main`. Elle distingue ce qui est deja implemente, ce qui reste a stabiliser, et ce qui reste a construire pour que IdeA corresponde pleinement a la vision "patron + employes IA" avec memoire projet partagee, contexte partage, messagerie inter-agents transparente, FIFO par agent, et persistance de reprise.
|
||||
|
||||
See also:
|
||||
|
||||
- `idea-product-directives-main-handoff` for product priorities and UX constraints.
|
||||
- `agent-context-memory-and-profile-handoff` for the structural model separating context, durable memory, live state, and handoff.
|
||||
|
||||
Etat observe le 2026-06-11 sur le depot local:
|
||||
|
||||
- L'orchestration inter-agents synchrone est deja reelle cote application.
|
||||
- La FIFO par agent, la conversation par paire et `idea_reply` sont deja couvertes par des tests verts.
|
||||
- Le transport MCP natif par projet et le loopback sont testes verts localement.
|
||||
- La reprise de conversation cote session/layout est largement presente dans le code.
|
||||
- En revanche, plusieurs briques produit restent inachevees ou non consolidees de bout en bout.
|
||||
|
||||
## Deja Livre Ou Tres Avance
|
||||
|
||||
### 1. Artefacts projet dans `.ideai/`
|
||||
|
||||
- Les agents projet sont persistes dans `.ideai/agents.json` et `.ideai/agents/*.md`.
|
||||
- Le contexte projet partage est modelise via `.ideai/CONTEXT.md`.
|
||||
- La memoire projet partagee est modelisee via `.ideai/memory/*.md` + `.ideai/memory/MEMORY.md`.
|
||||
- Les requetes d'orchestration fichier vivent sous `.ideai/requests/`.
|
||||
|
||||
Conclusion: la direction "tout ce qui releve d'IdeA pour un projet doit vivre dans `.ideai/`" est deja la bonne direction architecturale. Il reste surtout a eliminer les ecarts pratiques et a consolider l'usage reel.
|
||||
|
||||
### 2. Memoire projet partagee
|
||||
|
||||
- `FsMemoryStore` et `MemoryRecall` existent.
|
||||
- Les agents peuvent recevoir un rappel de memoire projet a l'activation.
|
||||
- Le frontend et les use cases CRUD memoire existent deja.
|
||||
|
||||
Conclusion: la memoire partagee du projet n'est plus un concept a inventer. Le travail restant est plutot sur la qualite du rappel, la curation, et l'usage continu pendant la vie des sessions.
|
||||
|
||||
### 3. Contexte partage et contexte agent
|
||||
|
||||
- Le contexte partage projet est separe du contexte agent.
|
||||
- Les contextes agent sont persistes sous `.ideai/agents/*.md`.
|
||||
- Le launcher injecte deja le contexte compose au demarrage.
|
||||
|
||||
Conclusion: la separation `contexte projet` / `contexte agent` est en place.
|
||||
|
||||
### 4. Messagerie inter-agents transparente
|
||||
|
||||
- `AgentMailbox` + `InMemoryMailbox` existent.
|
||||
- `ConversationRegistry` + `InMemoryConversationRegistry` existent.
|
||||
- `OrchestratorService::ask_agent` et `reply` existent.
|
||||
- Les outils MCP `idea_ask_agent`, `idea_reply`, `idea_launch_agent`, `idea_list_agents`, `idea_stop_agent`, `idea_update_context`, `idea_create_skill` existent.
|
||||
- Les tests applicatifs passent sur FIFO, reponse synchrone, prevention de cycle, timeout, parallélisme entre cibles differentes.
|
||||
|
||||
Verification locale du 2026-06-11:
|
||||
|
||||
- `cargo test -p application --test orchestrator_service` : OK
|
||||
- `cargo test -p infrastructure --test mcp_server` : OK
|
||||
- `cargo test -p app-tauri --test orchestrator_wiring` : OK
|
||||
|
||||
Conclusion: le coeur de la communication inter-agents n'est plus un chantier de conception. Il est deja implementé et teste.
|
||||
|
||||
### 5. FIFO transparente quand un agent est occupe
|
||||
|
||||
- La file d'entree par agent existe deja.
|
||||
- La serialisation des tours vers une meme cible existe.
|
||||
- Les `ask` concurrents vers des agents differents peuvent tourner en parallele.
|
||||
|
||||
Conclusion: l'exigence "si l'utilisateur ou un autre agent parle a un agent deja occupe, la requete part en file FIFO de maniere transparente" est deja largement satisfaite au niveau coeur applicatif.
|
||||
|
||||
### 6. Reprise de conversation et persistance de session
|
||||
|
||||
- Les cellules/layouts persistent `conversation_id` et `agent_was_running`.
|
||||
- Les use cases de reprise (`ListResumableAgents`, popup de reprise, relance avec `conversation_id`) existent.
|
||||
- Les sessions structurees Claude/Codex savent porter un `conversation_id`.
|
||||
|
||||
Conclusion: la persistance de reprise a deja une base concrete et substantielle.
|
||||
|
||||
## Reste A Faire En Priorite
|
||||
|
||||
### 1. Consolider la persistance "conversation continue" au niveau produit, pas seulement "resume technique"
|
||||
|
||||
Le code sait deja reprendre une conversation via `conversation_id`, mais la cible produit demande plus qu'une simple reprise technique:
|
||||
|
||||
- conserver une vraie continuite de conversation lisible pour l'utilisateur au redemarrage,
|
||||
- permettre au nouvel agent/profil de repartir avec l'etat utile,
|
||||
- rendre la reprise completement transparente dans l'UX.
|
||||
|
||||
Reste donc a verrouiller:
|
||||
|
||||
- la persistance canonique des conversations exploitable au niveau produit,
|
||||
- la strategie de resume/handoff quand on change de profil IA,
|
||||
- la coherence UX entre reprise de cellule, reprise de conversation, et reprise de travail.
|
||||
|
||||
### 2. Implementer une couche persistante de handoff / resume cross-profile
|
||||
|
||||
La memoire partagee existante dit explicitement qu'il faut persister:
|
||||
|
||||
- un canonical conversation log,
|
||||
- un cumulative handoff summary,
|
||||
- l'etat agent,
|
||||
- les identifiants de conversation utiles par provider.
|
||||
|
||||
Ce point n'apparait pas comme livre de bout en bout dans le depot actuel.
|
||||
|
||||
Le besoin produit reste ouvert:
|
||||
|
||||
- si un agent passe de Claude a Codex, IdeA doit reconstituer l'etat de travail sans dependre d'une session native transferable,
|
||||
- le handoff doit etre incremental, pas fabrique seulement au moment de la panne ou du swap.
|
||||
|
||||
### 3. Introduire un vrai live-state partage au niveau projet
|
||||
|
||||
La memoire durable ne doit pas servir de journal temps reel. La note memoire existante le dit deja.
|
||||
|
||||
Il manque encore une couche explicite de "live operational state" pour:
|
||||
|
||||
- qui travaille sur quoi,
|
||||
- tickets/intentions en cours,
|
||||
- etat d'avancement d'un agent,
|
||||
- derniere delegation utile,
|
||||
- elements transitoires de coordination inter-agents.
|
||||
|
||||
Sans cette couche, une partie de la coordination reste soit volatile, soit repoussee dans des endroits qui ne sont pas faits pour ca.
|
||||
|
||||
### 4. Verifier et finir l'integration MCP natif "IdeA-only" de bout en bout dans le flux reel de l'application
|
||||
|
||||
Les tests locaux du transport MCP passent, ce qui place cette zone en fin de chantier plutot qu'au debut.
|
||||
|
||||
Mais il reste a confirmer en situation reelle utilisateur:
|
||||
|
||||
- qu'un agent lance par IdeA voit effectivement ses outils MCP sans action manuelle,
|
||||
- que les profils supportes utilisent bien cette voie par defaut,
|
||||
- que le fallback fichier+prose reste coherent quand MCP n'est pas disponible,
|
||||
- que l'observabilite UI des delegations et des replies est suffisamment claire.
|
||||
|
||||
Point important: l'architecture historique qui mentionne encore un verrou M5 ouvert est probablement en retard par rapport au worktree local. Avant de planifier le prochain lot, `Main` doit revalider la documentation d'architecture a la lumiere du code/tests actuels.
|
||||
|
||||
### 5. Stabiliser le registre de sessions et clarifier le modele singleton d'agent
|
||||
|
||||
Le produit veut "1 agent = 1 employe". Cela impose une verite unique sur:
|
||||
|
||||
- la session vivante de l'agent,
|
||||
- sa conversation courante,
|
||||
- sa cellule visible ou son execution en arriere-plan,
|
||||
- son etat occupé/libre/interrompu.
|
||||
|
||||
Le code a deja beaucoup avance sur ce point, mais le worktree local montre encore un chantier actif autour de:
|
||||
|
||||
- `application/src/terminal/registry.rs`
|
||||
- `application/src/orchestrator/service.rs`
|
||||
- `application/src/agent/lifecycle.rs`
|
||||
- `app-tauri/src/state.rs`
|
||||
|
||||
Conclusion: ne pas considerer le sujet comme totalement clos tant que le worktree n'est pas nettoye et que la suite de tests ciblee n'est pas executee sur l'ensemble du flux concerne.
|
||||
|
||||
### 6. Rendre la mise a jour de memoire/contexte vraiment automatique pendant la vie d'un agent
|
||||
|
||||
La cible utilisateur dit qu'il ne doit jamais demander:
|
||||
|
||||
- de charger une memoire,
|
||||
- de charger un contexte,
|
||||
- de mettre a jour la memoire,
|
||||
- de mettre a jour le contexte.
|
||||
|
||||
Le lancement injecte deja beaucoup de choses automatiquement, mais il reste a verrouiller le comportement "pendant la vie" d'un agent:
|
||||
|
||||
- quand regenerer le contexte effectif,
|
||||
- quand promouvoir une information stable vers la memoire durable,
|
||||
- comment distinguer signal utile et bruit,
|
||||
- comment eviter de compter sur des consignes manuelles a l'utilisateur.
|
||||
|
||||
### 7. Unifier la conversation utilisateur <-> agent et agent <-> agent dans l'UX
|
||||
|
||||
Le backend sait deja distinguer `User<->Agent` et `Agent<->Agent`.
|
||||
|
||||
Le travail restant est surtout produit/frontend:
|
||||
|
||||
- affichage clair des delegations et des retours,
|
||||
- visualisation non confuse des conversations par paire,
|
||||
- reprise lisible des threads,
|
||||
- transparence totale pour l'utilisateur final.
|
||||
|
||||
Le diff local frontend suggere justement un remaniement en cours de la surface terminal/chat.
|
||||
|
||||
### 8. Consolider la restriction et l'affordance des profils supportes
|
||||
|
||||
Le modele actuel oriente fortement vers Claude/Codex structures, ce qui est coherent avec la fiabilite attendue.
|
||||
|
||||
Reste a clarifier produit:
|
||||
|
||||
- quels profils sont officiellement "employes IdeA" de premiere classe,
|
||||
- quel fallback proposer pour les profils non structures,
|
||||
- quelle UI montrer quand un profil ne supporte pas la delegation native fiable.
|
||||
|
||||
### 9. Persistance conversationnelle globale de l'application
|
||||
|
||||
La demande utilisateur mentionne explicitement qu'en relancant IdeA il faut retrouver la conversation.
|
||||
|
||||
La reprise par `conversation_id` et layouts existe, mais il reste a confirmer ou completer:
|
||||
|
||||
- la persistance lisible de l'historique conversationnel pour l'utilisateur,
|
||||
- la restauration des vues au redemarrage,
|
||||
- la coherence entre session technique, resume visuel et histoire de travail.
|
||||
|
||||
Autrement dit: "reprendre une session moteur" n'est pas encore automatiquement equivalent a "retrouver sa conversation produit" dans tous les cas.
|
||||
|
||||
## Chantiers Secondaires Mais Importants
|
||||
|
||||
### 1. Mettre la documentation d'architecture a jour
|
||||
|
||||
Le code local et les tests verts semblent avoir depasse certains passages de `ARCHITECTURE.md` et de briefs anciens.
|
||||
|
||||
Il faut une passe de synchronisation documentaire pour eviter que `Main` suive un etat obsolete, en particulier sur:
|
||||
|
||||
- statut reel du transport MCP,
|
||||
- statut reel de la FIFO inter-agents,
|
||||
- statut reel des conversations par paire,
|
||||
- ce qui reste vraiment ouvert entre handoff, live-state et UX.
|
||||
|
||||
### 2. Curater le dossier `.ideai/`
|
||||
|
||||
Le principe "tout ce qui est IdeA-projet va dans `.ideai/`" est bon, mais il faudra surveiller:
|
||||
|
||||
- la proliferation de fichiers run/request/debug,
|
||||
- ce qui est durable vs derivable,
|
||||
- ce qui doit etre committe vs ignore.
|
||||
|
||||
### 3. Formaliser les regles de promotion memoire
|
||||
|
||||
Le systeme doit savoir quand enregistrer une connaissance stable sans polluer la memoire partagee.
|
||||
|
||||
Il manque probablement encore:
|
||||
|
||||
- une politique claire de promotion,
|
||||
- des heuristiques/outils explicites pour les agents,
|
||||
- des garde-fous contre la memoire bruit.
|
||||
|
||||
## Worktree Local A Prendre En Compte
|
||||
|
||||
Le depot local est actuellement dirty avec un chantier large non committe autour de:
|
||||
|
||||
- orchestration MCP / loopback / serveur Tauri,
|
||||
- mailbox / conversations / session registry,
|
||||
- adaptation frontend terminal/chat/layout,
|
||||
- fichiers `.ideai/` du projet lui-meme.
|
||||
|
||||
Consequence pour `Main`:
|
||||
|
||||
- ne pas planifier a partir de `ARCHITECTURE.md` seulement,
|
||||
- d'abord relire le diff local,
|
||||
- ensuite reexecuter la suite de tests ciblee des zones touchees,
|
||||
- puis seulement decider si le prochain lot est "finition", "integration UI", ou "harden/persistence".
|
||||
|
||||
## Ordre Recommande Pour La Suite
|
||||
|
||||
1. Revalider et documenter l'etat reel du chantier MCP/orchestration a partir du code courant, puis remettre `ARCHITECTURE.md` a jour.
|
||||
2. Fermer proprement le sujet "1 agent = 1 session vivante coherente" en nettoyant le registre/session lifecycle encore en mouvement.
|
||||
3. Concevoir puis implementer une vraie couche de live-state partage projet.
|
||||
4. Concevoir puis implementer la couche persistante de handoff/canonical conversation log cross-session et cross-profile.
|
||||
5. Finir l'integration UX/frontend pour que toute cette orchestration reste invisible et naturelle pour l'utilisateur final.
|
||||
|
||||
## Synthese Courte
|
||||
|
||||
Le plus gros changement de perception pour `Main` est le suivant:
|
||||
|
||||
- IdeA n'est plus au stade "il faut inventer la delegation inter-agents".
|
||||
- IdeA est plutot au stade "le coeur de delegation existe deja; il faut maintenant le consolider, le documenter, le rendre pleinement persistant, et le rendre transparent dans l'UX".
|
||||
Reference in New Issue
Block a user