fix(ticket-assistant): rendre visible l'échec « aucune réponse » de l'assistant de ticket (#60)

Introduit un variant terminal `ReplyEvent::Error { message }` (domain/ports)
propagé jusqu'au chat : l'adapter OpenAI-compat récupère `reasoning_content`
quand `content` est vide, et un `Final` vide est converti en `Error` visible
plutôt qu'un tour silencieux qui pend. Le front (`ReplyChunk` + useTicketAssistant)
rend ce message d'erreur dans la cellule.

- domain: variant `ReplyEvent::Error`, bras non terminal (readiness, structured drain)
- infra/session: parse `reasoning_content` (delta/completion), fallback visible
- app-tauri: mapping ReplyEvent::Error -> ReplyChunk::Error, Final vide -> Error
- frontend: ReplyChunk `error`, rendu dans useTicketAssistant (+ test .test.tsx)

NB: commit sur la feature branch, PAS un merge. #60 reste inProgress.
Les 10 tests `cargo test -p infrastructure --lib session` échouent SOUS SANDBOX
uniquement (bind loopback 127.0.0.1:0 interdit -> Os PermissionDenied), pas une
régression : revérification hors-sandbox requise avant tout merge vers develop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 08:32:59 +02:00
parent ce5aa2811c
commit 2f7f111545
14 changed files with 571 additions and 25 deletions

View File

@ -1221,9 +1221,12 @@ export interface TicketChat {
* One streamed chunk of an assistant turn (mirror of the backend `ReplyChunk`,
* tagged on `kind`). `textDelta` is an incremental text fragment; `toolActivity`
* a best-effort activity badge; `final` the deterministic end-of-turn chunk
* carrying the aggregated content — after it the turn is frozen.
* carrying the aggregated content — after it the turn is frozen. `error` is a
* visible terminal fallback (ticket #60): the model produced no usable answer,
* so the turn ends with a human-readable explanation instead of a silent hang.
*/
export type ReplyChunk =
| { kind: "textDelta"; text: string }
| { kind: "toolActivity"; label: string }
| { kind: "final"; content: string };
| { kind: "final"; content: string }
| { kind: "error"; message: string };