# Brief Dev — Lot D5 : frontend chat (§17.9) > Demandé par **Main** à **DevFrontend** (dev) + **QA** (test). Cycle §3 : code → tests → vert. > Périmètre **frontend uniquement** (`frontend/src`). Le backend D4 est livré et committé (`f4d5727`). ## 0. Où on en est Le fil §17 (exécution structurée des agents IA) est livré jusqu'à **D4 inclus** côté backend : les commandes Tauri `agent_send` / `reattach_agent_chat` / `close_agent_session` existent et streament des `ReplyChunk` sur un `Channel`. D5 = **la vue chat React** qui consomme ça, plus le **routage par type de cellule** dans le layout. ### Contrats backend exacts à mirrorer (déjà livrés) Commandes Tauri (`crates/app-tauri/src/commands.rs`) : - `agent_send(sessionId: string, prompt: string, onReply: Channel) -> void` - `reattach_agent_chat(sessionId: string, onReply: Channel) -> ReattachChatDto` - `close_agent_session(sessionId: string) -> void` DTO (`crates/app-tauri/src/dto.rs`) — **sérialisation camelCase tagué `kind`** : ```ts type ReplyChunk = | { kind: "textDelta"; text: string } | { kind: "toolActivity"; label: string } | { kind: "final"; content: string }; // ReattachChatDto : le scrollback de conversation (chunks déjà streamés) interface ReattachChatDto { sessionId: string; scrollback: ReplyChunk[]; } // Et surtout : le DTO de session porte désormais cellKind type CellKind = "pty" | "chat"; // toujours présent sur TerminalSessionDto ``` > `cellKind` vaut `"chat"` quand l'agent est piloté en mode structuré (profil Claude/Codex), > `"pty"` pour un terminal brut. C'est **la seule info dont le frontend a besoin** pour router > cellule chat vs terminal. ## 1. Périmètre D5 (réf. tableau §17.9 ligne D5) 1. **`AgentChatView`** (nouveau, `frontend/src/features/chat/`) : vue de conversation IdeA. - Affiche les **deltas live** (accumulation `textDelta` → texte du tour en cours), l'**activité d'outil** (`toolActivity`), et **fige le tour** sur `final`. - Zone de **saisie** d'un prompt → appelle `AgentGateway.sendPrompt`. - **Scrollback de conversation** : à l'attache, repeint l'historique renvoyé par `reattachChat` ; survit à un changement d'onglet/layout (ré-attache, pas re-spawn). - C'est le **jumeau chat** de `TerminalView` (`frontend/src/features/terminals/TerminalView.tsx`, 283 l.) — inspire-toi de sa gestion de cycle de vie (mount/attach/detach), mais pour un flux de messages structuré au lieu d'octets xterm. 2. **Routage par `cellKind` dans `LayoutGrid`** (`frontend/src/features/layout/LayoutGrid.tsx`, fonction `LeafView` ~l.159) : une cellule rend `AgentChatView` si `cellKind === "chat"`, sinon `TerminalView` (comportement actuel inchangé). Le `cellKind` arrive sur le handle/session au lancement (sortie de `launchAgent`) — propage-le jusqu'au leaf. 3. **Port `AgentGateway`** (`frontend/src/ports/index.ts`, interface ~l.76) : ajoute - `sendPrompt(sessionId: string, prompt: string, onReply: (c: ReplyChunk) => void): Promise` - `reattachChat(sessionId: string, onReply: (c: ReplyChunk) => void): Promise` - `closeAgentSession(sessionId: string): Promise` (signatures à aligner avec le style des méthodes existantes `launchAgent`/`reattach`). 4. **Adapter Tauri** (`frontend/src/adapters/agent.ts`, classe `TauriAgentGateway`) : implémente les 3 méthodes via `invoke(...)` + `new Channel()` (modèle déjà présent pour `launchAgent`/`reattach` qui utilisent `Channel`). 5. **Mock gateway** (`frontend/src/adapters/mock/index.ts`) : streame des `ReplyChunk` scriptés (quelques `textDelta` puis un `final`) pour les tests et le dev hors-Tauri. 6. **Types TS** : ajoute `ReplyChunk`, `CellKind`, `ReattachChatDto` aux types partagés (là où vivent les autres mirrors de DTO), et le champ `cellKind` sur le type de session/handle. ## 2. Invariants à respecter - **Zéro régression terminal** : `TerminalView` et le chemin PTY de `LayoutGrid` restent identiques ; une cellule `pty` se comporte exactement comme avant. - **Ré-attache ≠ re-spawn** : changer d'onglet puis revenir repeint la conversation depuis le scrollback renvoyé par `reattachChat`, sans relancer de tour (miroir du PTY reattach). - **Accumulation correcte** : les `textDelta` s'accumulent dans le tour courant ; `final` clôt le tour (le texte final fait foi). Pas de doublon delta/final affiché deux fois. - Frontières : la vue dépend du **port** `AgentGateway`, jamais directement de `invoke`/Tauri (c'est l'adapter qui parle à Tauri). Hexagonal côté front respecté. ## 3. Tests attendus (QA — Vitest, réf. colonne « Tests attendus » D5 du §17.9) Aligne-toi sur le style des `*.test.tsx` existants (`LayoutGrid.test.tsx`, `TerminalView.test.tsx`, `adapters/agent.test.ts`), avec le **mock gateway** : - cellule `cellKind:"chat"` rend `AgentChatView` ; `cellKind:"pty"` rend `TerminalView`. - les `textDelta` s'accumulent à l'écran → `final` fige le tour. - ré-attache repeint le scrollback **sans** re-spawn (le mock ne reçoit pas de nouveau `sendPrompt`). - envoi d'un prompt → `AgentGateway.sendPrompt` appelé avec les bons args. - `toolActivity` affiché comme activité d'outil. - mock gateway streame bien une séquence `ReplyChunk` (deltas… puis final). ## 4. Méthode Cycle §3 strict : DevFrontend code → QA écrit + exécute les tests Vitest → vert avant de clore. Vérifie `npm run build` (tsc --noEmit + vite) **et** `npx vitest run` verts. **Ne pas committer, ne pas push** — Main relit et commit. Rapport d'erreurs clair si rouge → correction → re-test. ## 5. Références - Jumeau à copier : `frontend/src/features/terminals/TerminalView.tsx` (cycle de vie attach/detach). - Routage cellule : `frontend/src/features/layout/LayoutGrid.tsx` (`LeafView`). - Port : `frontend/src/ports/index.ts` (`AgentGateway`) ; adapter : `frontend/src/adapters/agent.ts` ; mock : `frontend/src/adapters/mock/index.ts`. - Backend déjà livré : commit `f4d5727`, `crates/app-tauri/src/{chat,commands,dto}.rs`. - Spec : `ARCHITECTURE.md` §17.6 (deux types de cellules) et tableau §17.9 ligne **D5**.