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:
@ -50,6 +50,20 @@ pub enum DomainEventDto {
|
||||
/// Exit code.
|
||||
code: i32,
|
||||
},
|
||||
/// A ticket assistant chat was opened.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
TicketAssistantOpened {
|
||||
/// Bound ticket reference.
|
||||
issue_ref: String,
|
||||
/// Runtime profile id.
|
||||
profile_id: String,
|
||||
},
|
||||
/// A ticket assistant chat was closed.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
TicketAssistantClosed {
|
||||
/// Bound ticket reference.
|
||||
issue_ref: String,
|
||||
},
|
||||
/// An agent's busy/idle state changed (cadrage C4 §4.2). The frontend dims
|
||||
/// "Envoyer" while `busy` is `true`.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -551,6 +565,16 @@ impl From<&DomainEvent> for DomainEventDto {
|
||||
agent_id: agent_id.to_string(),
|
||||
code: *code,
|
||||
},
|
||||
DomainEvent::TicketAssistantOpened {
|
||||
issue_ref,
|
||||
profile_id,
|
||||
} => Self::TicketAssistantOpened {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
profile_id: profile_id.to_string(),
|
||||
},
|
||||
DomainEvent::TicketAssistantClosed { issue_ref } => Self::TicketAssistantClosed {
|
||||
issue_ref: issue_ref.to_string(),
|
||||
},
|
||||
DomainEvent::AgentBusyChanged { agent_id, busy } => Self::AgentBusyChanged {
|
||||
agent_id: agent_id.to_string(),
|
||||
busy: *busy,
|
||||
@ -1081,4 +1105,24 @@ mod tests {
|
||||
assert_eq!(json["to"], to.to_string());
|
||||
assert_eq!(json["version"], 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ticket_assistant_events_relay_to_dto_and_wire() {
|
||||
let profile_id = domain::ProfileId::from_uuid(uuid::Uuid::from_u128(9));
|
||||
let opened = DomainEventDto::from(&DomainEvent::TicketAssistantOpened {
|
||||
issue_ref: domain::IssueRef::from(domain::IssueNumber::new(7).unwrap()),
|
||||
profile_id,
|
||||
});
|
||||
let opened = serde_json::to_value(&opened).expect("serialisable");
|
||||
assert_eq!(opened["type"], "ticketAssistantOpened");
|
||||
assert_eq!(opened["issueRef"], "#7");
|
||||
assert_eq!(opened["profileId"], profile_id.to_string());
|
||||
|
||||
let closed = DomainEventDto::from(&DomainEvent::TicketAssistantClosed {
|
||||
issue_ref: domain::IssueRef::from(domain::IssueNumber::new(7).unwrap()),
|
||||
});
|
||||
let closed = serde_json::to_value(&closed).expect("serialisable");
|
||||
assert_eq!(closed["type"], "ticketAssistantClosed");
|
||||
assert_eq!(closed["issueRef"], "#7");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user