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>
This commit is contained in:
2026-06-09 23:30:20 +02:00
parent f4d5727a69
commit 5059f37890
11 changed files with 1478 additions and 30 deletions

View File

@ -271,6 +271,49 @@ export interface TerminalSession {
* session assignment and the hosting cell had none yet; absent otherwise.
*/
assignedConversationId?: string;
/**
* How the frontend should render the cell hosting this session (mirror of the
* backend `TerminalSessionDto.cellKind`, ARCHITECTURE §17.6). `"chat"` ⇒ a
* structured AI session driven by `agent_send`/`reattach_agent_chat` (rendered
* as an `AgentChatView`); `"pty"` ⇒ a raw terminal (xterm). **Derived**
* backend-side from the launch routing — a single source of truth.
*/
cellKind: CellKind;
}
/**
* Whether a session's hosting cell renders as a structured chat view or a raw
* terminal (mirror of the backend `CellKind`, ARCHITECTURE §17.6). The
* `LayoutGrid` switches `AgentChatView` vs `TerminalView` on this value.
*/
export type CellKind = "pty" | "chat";
/**
* One incremental event of an AI agent's reply turn (mirror of the backend
* `ReplyChunk`, ARCHITECTURE §17.7). Tagged on `kind` (camelCase on the wire),
* so the view branches without positional parsing:
*
* - `textDelta`: a text fragment of the current turn (accumulated live);
* - `toolActivity`: a human-readable tool-activity badge (best-effort);
* - `final`: the **deterministic** end-of-turn chunk carrying the aggregated
* final content — after it the turn is frozen and the stream ends.
*/
export type ReplyChunk =
| { kind: "textDelta"; text: string }
| { kind: "toolActivity"; label: string }
| { kind: "final"; content: string };
/**
* The retained **conversation scrollback** of a still-live structured session
* (mirror of the backend `ReattachChatDto`, ARCHITECTURE §17.7), returned by
* `reattachChat`. The frontend replays `scrollback` to rebuild the visible turns
* before subsequent chunks arrive over the freshly-registered channel.
*/
export interface ReattachChatDto {
/** The session that was re-attached (echoed back). */
sessionId: string;
/** The chunks already streamed for this conversation, in order. */
scrollback: ReplyChunk[];
}
/**