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:
@ -135,19 +135,40 @@ export function useTicketAssistant(
|
||||
} else if (chunk.kind === "toolActivity") {
|
||||
// Best-effort inline activity badge.
|
||||
next[i] = { ...next[i], text: `${next[i].text}\n· ${chunk.label}` };
|
||||
} else if (chunk.kind === "error") {
|
||||
// Terminal fallback (ticket #60): the model returned no usable answer.
|
||||
// Freeze the turn with the readable explanation so the bubble is never
|
||||
// left empty and pending — the assistant no longer hangs silently.
|
||||
next[i] = {
|
||||
role: "assistant",
|
||||
text: `⚠️ ${chunk.message}`,
|
||||
pending: false,
|
||||
};
|
||||
} else {
|
||||
// `final`: freeze the turn with the aggregated content.
|
||||
next[i] = { role: "assistant", text: chunk.content, pending: false };
|
||||
}
|
||||
return next;
|
||||
});
|
||||
if (chunk.kind === "final") setBusy(false);
|
||||
// `busy` tracks the REAL end-of-turn: a `final` or an `error` terminal —
|
||||
// never merely `agent_send` returning (the stream is still flowing then,
|
||||
// ticket #60). The input stays disabled until one of them arrives.
|
||||
if (chunk.kind === "final" || chunk.kind === "error") setBusy(false);
|
||||
};
|
||||
try {
|
||||
await ticket.sendTicketChat(sessionId, text, applyChunk);
|
||||
} catch (e) {
|
||||
// Transport failure: no terminal chunk will arrive, so close the pending
|
||||
// turn and drop `busy` here to guarantee the assistant never hangs.
|
||||
setError(describe(e));
|
||||
} finally {
|
||||
setTurns((prev) => {
|
||||
const next = [...prev];
|
||||
const i = next.length - 1;
|
||||
if (i >= 0 && next[i].role === "assistant" && next[i].pending) {
|
||||
next[i] = { ...next[i], pending: false };
|
||||
}
|
||||
return next;
|
||||
});
|
||||
setBusy(false);
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user