feat(tickets): création d'un assistant IA de ticket (backend + frontend)

Ajoute le chat assistant IA attaché à un ticket (#8).

Backend Rust :
- use cases OpenTicketAssistant/CloseTicketAssistant + tests
- politique d'outils par agent (domain/agent_tool_policy) et policy MCP
- store de contexte assistant + gabarit default_ticket_assistant.md + tests
- events TicketAssistantOpened/Closed
- commandes Tauri open_ticket_chat/close_ticket_chat et câblage state/lib/events

Frontend :
- gateway (ports, adapters ticket + mock, domain)
- hook useTicketAssistant + composant TicketAssistantPanel
- intégration dans TicketDetail

Tests verts : vitest tickets.test.tsx (23), cargo test application::ticket_assistant (1),
infrastructure::assistant_context_store (2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 06:06:24 +02:00
parent f1803768fd
commit 1fdf62c089
29 changed files with 2173 additions and 49 deletions

View File

@ -187,6 +187,21 @@ export type DomainEvent =
version: number;
}
| { type: "sprintDeleted"; sprintId: string }
| {
/**
* A ticket AI assistant chat was opened (ticket #8). Mirror of the backend
* `DomainEventDto::TicketAssistantOpened`. `issueRef` is the bound ticket,
* `profileId` the runtime AI profile driving the assistant.
*/
type: "ticketAssistantOpened";
issueRef: string;
profileId: string;
}
| {
/** A ticket AI assistant chat was closed/disposed (ticket #8). */
type: "ticketAssistantClosed";
issueRef: string;
}
| {
/**
* An intermediate assistant announcement emitted **during** an inter-agent
@ -963,3 +978,25 @@ export interface TicketCarnet {
carnet: string;
version: number;
}
/**
* A ticket AI assistant chat session (mirror of the backend `TicketChatDto`,
* ticket #8). `sessionId` is the live structured session driving the assistant;
* `requester` is the assistant agent id; `issueRef` the bound ticket.
*/
export interface TicketChat {
sessionId: string;
requester: string;
issueRef: TicketRef;
}
/**
* 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.
*/
export type ReplyChunk =
| { kind: "textDelta"; text: string }
| { kind: "toolActivity"; label: string }
| { kind: "final"; content: string };