Files
IdeA/.ideai/briefs/d5-frontend-chat.md
Blomios 5059f37890 feat(agent): vue chat frontend + routage cellKind (D5) — §17
Le frontend consomme l'exécution structurée livrée en D4 :
- AgentChatView : jumeau chat de TerminalView. Accumule les textDelta du
  tour courant, badges toolActivity, fige sur final (le contenu final
  remplace les deltas, pas de double rendu). Saisie Enter/Shift+Enter.
  Au montage : reattach (rejoue le scrollback) ou launch ; au démontage :
  détache seulement (jamais de close). Vue pure pilotée par le port.
- LayoutGrid/LeafView : routage cellKind — AgentChatView si "chat",
  TerminalView sinon (chemin PTY inchangé). Cache cellKindBySession pour
  router correctement après ré-attache/navigation.
- Port AgentGateway : sendPrompt / reattachChat / closeAgentSession ;
  cellKind sur TerminalHandle. Adapter Tauri (invoke + Channel<ReplyChunk>)
  et mock (MockChatSession, _setChatAgents) étendus.
- Types TS mirrors : CellKind, ReplyChunk, ReattachChatDto + cellKind sur
  TerminalSession.

Tests (QA) : 21 Vitest verts — routage chat/pty (non-régression terminal),
accumulation/non-doublon (garde anti-always-green delta!=final), ré-attache
sans re-spawn, envoi Enter vs Shift+Enter, badges toolActivity, mock stream.
npx vitest run : 345 passed, 0 failed. npm run build vert.

Reste D6 (messagerie inter-agents via send_blocking) et D7 (menu restreint
Claude/Codex + retrait custom) pour clore §17.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:30:20 +02:00

6.1 KiB

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<ReplyChunk>) -> void
  • reattach_agent_chat(sessionId: string, onReply: Channel<ReplyChunk>) -> ReattachChatDto
  • close_agent_session(sessionId: string) -> void

DTO (crates/app-tauri/src/dto.rs) — sérialisation camelCase tagué kind :

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<void>
    • reattachChat(sessionId: string, onReply: (c: ReplyChunk) => void): Promise<ReplyChunk[]>
    • closeAgentSession(sessionId: string): Promise<void> (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<ReplyChunk>() (modèle déjà présent pour launchAgent/reattach qui utilisent Channel<number[]>).
  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.