Expose l'exécution structurée à l'UI (jumeau du chemin PTY) :
- ChatBridge (chat.rs) : pompe ReplyStream -> Channel<ReplyChunk>,
generation-tracked (anti-double-pompe) ; scrollback de conversation
côté transport (le port AgentSession est sans mémoire), purgé à la
fermeture, préservé à la ré-attache.
- Commandes agent_send / reattach_agent_chat / close_agent_session.
- DTO : ReplyChunk (textDelta/toolActivity/final, camelCase tagué kind),
ReattachChatDto, CellKind {pty,chat} + champ cellKind dérivé sur
TerminalSessionDto (chat ssi LaunchAgentOutput.structured = Some).
- Wiring composition root : StructuredSessions + ChatBridge dans AppState,
via la factory déjà injectée (aucun new d'adapter — règle D §17.8).
- From<AgentSessionError> for AppError (mappe sur PROCESS).
Tests (QA) : 35 unitaires verts — generation supersede validé par mutation
test, séquence deltas*+Final, reattach sans re-spawn, teardown shutdown+
unregister, DTO cellKind + round-trip ReplyChunk, non-régression PTY.
cargo test --workspace : 803 passed, 0 failed.
Reste D5 : AgentChatView (frontend) + routage cellKind dans LayoutGrid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.2 KiB
Brief Dev — Lot D4 : commandes Tauri + bridge chat (§17.9)
Demandé par Main à DevBackend (dev) + QA (test). Cycle §3 : code → tests → vert. Périmètre backend uniquement (
app-tauri). Le frontend chat est D5, hors périmètre ici.
0. Où on en est
Le fil §17 (exécution structurée des agents IA via le port AgentSession) est livré
jusqu'à D3 inclus :
- D0/D1 (
5e10b5e) : portdomain::ports::AgentSession+AgentSessionFactory,ReplyEvent/ReplyStream/AgentSessionError, champAgentProfile.structured_adapter, registreStructuredSessions, agrégateurLiveSessions, helpersend_blocking. - D2 (
751d94d) + spikes S1/S2 (f104862) : adaptersClaudeSdkSession/CodexExecSessiondanscrates/infrastructure/src/session/, fake CLI + harnais de conformité, formats réels Claudestream-json/ Codexexec --jsoncâblés. - D3 (
56913b9) :LaunchAgentroute structuré vs PTY ;LaunchAgentOutputporte désormaisstructured: Option<StructuredSessionDescriptor>.
D4 = exposer tout ça à l'UI : commandes Tauri + pont de streaming, jumeau exact du chemin PTY existant. Aucun chemin PTY (terminal non-IA) ne doit changer ni régresser.
1. Périmètre D4 (réf. §17.7 et tableau §17.9)
À livrer dans crates/app-tauri/src/ :
ChatBridge— jumeau dePtyBridge(crates/app-tauri/src/pty.rs), generation-tracked (même mécanique de génération pour éviter la double-pompe lors d'une ré-attache). Il pompe unReplyStream(eventsReplyEventdu port) vers untauri::ipc::Channel, en émettant desReplyChunk(DTO ci-dessous). Vit à côté dePtyBridge, ne le remplace pas.- Commandes Tauri :
agent_send(sessionId, prompt)→ pompe lesReplyEventdu tour sur leChannel(deltasTextDelta→ chunks,ToolActivity→ chunks d'activité,Final→ chunk final qui fige le tour). S'appuie sur le registreStructuredSessions/send_blockingcôté application (déjà livré en D1).reattach_agent_chat(...)→ renvoie le scrollback de conversation + rebranche leChannel(repeint sans re-spawn ; supersede l'ancienne génération).close_agent_session(sessionId)→shutdown(polymorphe) + unregister du registre.
- DTO (
crates/app-tauri/src/dto.rs) :ReplyChunk: variantes delta texte / activité outil / final (sérialisation camelCase, cohérente avec les DTO existants).ReattachChatDto.cellKindajouté au DTO de session (terminalptyvs chatchat), dérivé de la présence d'un descripteur structuré (LaunchAgentOutput.structured).
- Wiring composition root (
state.rs/lib.rs) : injecter les dépendances nécessaires, enregistrer les nouvelles commandes. Aucunnew ClaudeSdkSessionici — passe par la factory déjà injectée (règle D du §17.8).
2. Contrat / invariants à respecter
- Generation supersede : une ré-attache invalide l'ancienne pompe ; pas de double émission.
cellKindest la seule info dont D5 (frontend) a besoin pour router cellule chat vs terminal. Stable et explicite au DTO.- Isolation parsing : D4 ne parse aucun format CLI — il consomme des
ReplyEventtypés. - Zéro régression PTY : le chemin terminal brut (
PtyBridge, commandes terminal) reste identique. Les tests PTY existants restent verts. - Frontières hexagonales (§17.8) :
app-tauridépend des ports/registres application, jamais des adapters infra concrets.
3. Tests attendus (QA — réf. colonne « Tests attendus » D4 du §17.9)
Crate app-tauri (fakes pour la session structurée, pas de vrai CLI) :
agent_sendpompe les events d'un tour sur leChannel(séquence deltas… puisFinal).- ré-attache → scrollback conversation repeint, sans re-spawn de session.
close_agent_session→shutdownappelé et unregister du registre.- generation supersede : après ré-attache, l'ancienne génération ne pompe plus (pas de
double émission sur le
Channel). - DTO :
cellKind=chatpour une sortieLaunchAgentOutputavecstructured: Some(..),ptysinon ; round-tripReplyChunk(camelCase). - non-régression : un DTO de session PTY existant sérialise toujours pareil (le nouveau champ
cellKindne casse pas les snapshots — vérifier la valeur par défaut/dérivée).
4. Méthode
Cycle §3 strict : DevBackend code → QA écrit + exécute les tests → vert avant de clore.
Rapport d'erreurs clair si rouge → correction → re-test. Commit feat(agent): … (D4) — §17
quand cargo test --workspace est vert. Ne pas push (validation Main requise).
5. Références code
- Jumeau à copier :
crates/app-tauri/src/pty.rs(PtyBridge, generation tracking). - Source du flux :
domain::ports::{AgentSession, ReplyEvent, ReplyStream}; registre/send_blocking:crates/application/src/agent/structured.rs. - Routage déjà fait :
crates/application/src/agent/lifecycle.rs(LaunchAgentOutput.structured). - DTO existants :
crates/app-tauri/src/dto.rs; commandes :crates/app-tauri/src/commands.rs. - Spec complète :
ARCHITECTURE.md§17.7 (commandes & DTO) et tableau §17.9 ligne D4.