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

@ -10,13 +10,15 @@
* {@link isTicketVersionConflict} to branch on it in the UI.
*/
import { invoke } from "@tauri-apps/api/core";
import { Channel, invoke } from "@tauri-apps/api/core";
import type {
GatewayError,
ReplyChunk,
Sprint,
Ticket,
TicketCarnet,
TicketChat,
TicketLinkKind,
TicketList,
} from "@/domain";
@ -182,4 +184,38 @@ export class TauriTicketGateway implements TicketGateway {
request: { projectId, sprintId },
});
}
async openTicketChat(
projectId: string,
issueRef: string,
profileId: string,
): Promise<TicketChat> {
return invoke<TicketChat>("open_ticket_chat", {
request: { projectId, issueRef, profileId },
});
}
async closeTicketChat(projectId: string, issueRef: string): Promise<void> {
await invoke<void>("close_ticket_chat", {
request: { projectId, issueRef },
});
}
async sendTicketChat(
sessionId: string,
message: string,
onChunk: (chunk: ReplyChunk) => void,
): Promise<void> {
// Per-turn reply channel, tagged on `kind` (`textDelta`|`toolActivity`|
// `final`). Reuses the shared `agent_send` chat transport (the same command
// and ChatBridge the structured chat cell uses). Wire the handler before the
// invoke so no early chunk is missed.
const channel = new Channel<ReplyChunk>();
channel.onmessage = (chunk) => onChunk(chunk);
await invoke<void>("agent_send", {
sessionId,
prompt: message,
onReply: channel,
});
}
}