fix(domain,application,infrastructure,backend,app-tauri,frontend): Stop demande agent préserve la conversation ouverte — arrêt sans fermeture/clôture du chat, états cohérents après cancel sur les 3 adapters de session, cellule et workstate synchronisés — #343 (QA verte)
This commit is contained in:
@ -12,16 +12,19 @@ use serde_json::Value;
|
||||
|
||||
use application::{
|
||||
AgentBackgroundTaskState, AgentTicketState, AppError, AppExitWorkGuardDetail,
|
||||
AppExitWorkGuardState, AttachLiveAgentOutput, BackgroundTaskKindLabel, CellAgentPresentation,
|
||||
ConversationPreviewStatus, ConversationTurnWorkPreview, ConversationWorkSummary,
|
||||
CreateProjectInput, CreateProjectOutput, ExecuteSlashCommandOutput, GitGraphOutput,
|
||||
HealthInput, HealthReport, LayoutKind, ListProjectsOutput, ListSlashCommandsOutput,
|
||||
LiveRuntimeState, LiveSessionKind, LiveSessionSnapshot, OpenProjectOutput, ProjectWorkState,
|
||||
RestartCellAgentRuntimeOutput, RestoreOpenProjectFailure, RestoreOpenProjectsOutput,
|
||||
SlashCommandEffect, StopLiveAgentOutput, TicketWorkSource, TicketWorkStatus, TurnPage,
|
||||
TurnSource, TurnView,
|
||||
AppExitWorkGuardState, AttachLiveAgentOutput, BackgroundTaskKindLabel, CancelAgentChatOutput,
|
||||
CellAgentPresentation, ConversationPreviewStatus, ConversationTurnWorkPreview,
|
||||
ConversationWorkSummary, CreateProjectInput, CreateProjectOutput, ExecuteSlashCommandOutput,
|
||||
GitGraphOutput, HealthInput, HealthReport, LayoutKind, ListProjectsOutput,
|
||||
ListSlashCommandsOutput, LiveRuntimeState, LiveSessionKind, LiveSessionSnapshot,
|
||||
OpenProjectOutput, ProjectWorkState, RestartCellAgentRuntimeOutput, RestoreOpenProjectFailure,
|
||||
RestoreOpenProjectsOutput, SlashCommandEffect, StopLiveAgentOutput, TicketWorkSource,
|
||||
TicketWorkStatus, TurnPage, TurnSource, TurnView,
|
||||
};
|
||||
use domain::ports::{
|
||||
CancelCurrentTurnOutcome, ReplyProgress, ReplyProgressKind, ReplyProgressSource,
|
||||
ReplyProgressStage,
|
||||
};
|
||||
use domain::ports::{ReplyProgress, ReplyProgressKind, ReplyProgressSource, ReplyProgressStage};
|
||||
use domain::{
|
||||
AgentBusyState, PageCursor, PageDirection, Project, ProjectId, ProjectSystemPermissions,
|
||||
ResolvedAgentSystemPermissions, SlashCommand, SystemPermissionSet, TurnRole,
|
||||
@ -4763,6 +4766,45 @@ pub struct StopLiveAgentResponseDto {
|
||||
pub already_stopped: bool,
|
||||
}
|
||||
|
||||
/// Response DTO for a non-destructive `cancel_agent_chat` request.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CancelAgentChatResponseDto {
|
||||
/// Stable structured session, still live after the operation.
|
||||
pub session_id: String,
|
||||
/// Idempotent turn-level result.
|
||||
pub outcome: CancelAgentChatOutcomeDto,
|
||||
}
|
||||
|
||||
/// Public cancellation outcomes, distinct from a missing-session error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum CancelAgentChatOutcomeDto {
|
||||
/// A running request was cancelled.
|
||||
Cancelled,
|
||||
/// That request had already been cancelled.
|
||||
AlreadyCancelled,
|
||||
/// The live session currently had no running request.
|
||||
AlreadyCompleted,
|
||||
}
|
||||
|
||||
impl From<CancelAgentChatOutput> for CancelAgentChatResponseDto {
|
||||
fn from(output: CancelAgentChatOutput) -> Self {
|
||||
Self {
|
||||
session_id: output.session_id.to_string(),
|
||||
outcome: match output.outcome {
|
||||
CancelCurrentTurnOutcome::Cancelled => CancelAgentChatOutcomeDto::Cancelled,
|
||||
CancelCurrentTurnOutcome::AlreadyCancelled => {
|
||||
CancelAgentChatOutcomeDto::AlreadyCancelled
|
||||
}
|
||||
CancelCurrentTurnOutcome::AlreadyCompleted => {
|
||||
CancelAgentChatOutcomeDto::AlreadyCompleted
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// One stopped runtime in an agent-level stop response.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -6298,6 +6340,22 @@ mod tests {
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[test]
|
||||
fn cancel_agent_chat_outcomes_are_stable_camel_case_values() {
|
||||
let response = CancelAgentChatResponseDto::from(CancelAgentChatOutput {
|
||||
session_id: domain::SessionId::from_uuid(Uuid::from_u128(343)),
|
||||
outcome: CancelCurrentTurnOutcome::AlreadyCancelled,
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(response).unwrap(),
|
||||
json!({
|
||||
"sessionId": Uuid::from_u128(343).to_string(),
|
||||
"outcome": "alreadyCancelled"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn settings_dtos_are_additive_and_validate_language_and_icon_contracts() {
|
||||
let legacy_user: UserSettingsDto = serde_json::from_value(json!({})).unwrap();
|
||||
|
||||
@ -17,21 +17,21 @@ use application::{
|
||||
AgentWakeService, AllocateManagedTemp, AppError, AssignIssueAgent, AssignSkillToAgent,
|
||||
AssignTicketToSprint, AssignedSkillResolver, AttachLiveAgent, AuthenticateSession,
|
||||
BackgroundCommandArchive, BulkDeleteIssues, BulkUpdateIssuePriority, BulkUpdateIssueStatus,
|
||||
CancelBackgroundTask, ChangeAgentProfile, CheckEmbedderSuggestion, ClearChatDraft,
|
||||
CloneOpenCodeProfileFromSeed, CloneProfileFromSeed, CloseProject, CloseTab, CloseTerminal,
|
||||
CloseTicketAssistant, ConfigureProfiles, ContextGuardUseCases, CreateAgentFromScratch,
|
||||
CreateAgentFromTemplate, CreateIssue, CreateLayout, CreateMemory, CreateProject, CreateSkill,
|
||||
CreateSprint, CreateTemplate, DeleteAgent, DeleteEmbedderProfile, DeleteIssue, DeleteLayout,
|
||||
DeleteMemory, DeleteModelArtifact, DeleteModelServer, DeleteProfile, DeleteSkill, DeleteSprint,
|
||||
DeleteTemplate, DescribeEmbedderEngines, DetectAgentDrift, DetectProfiles,
|
||||
DismissEmbedderSuggestion, EnsureLocalModelServer, ExecuteSlashCommand, FirstRunState,
|
||||
GcManagedTemp, GetAppExitWorkGuardState, GetLiveStateLean, GetMemory, GetProjectPermissions,
|
||||
GetProjectSystemPermissions, GetProjectWorkState, GitBranches, GitCheckout, GitCommit,
|
||||
GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage, HarvestMemoryFromTurn,
|
||||
HealthUseCase, ImportChatAttachments, InspectConversation, InstallPluginFromArchive,
|
||||
InstallPluginFromDirectory, JsonPluginManifestValidator, LaunchAgent, LaunchAgentInput,
|
||||
LinkIssues, ListAgents, ListAgentsInput, ListClaudeModels, ListCodexModels, ListDevices,
|
||||
ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
|
||||
CancelAgentChat, CancelBackgroundTask, ChangeAgentProfile, CheckEmbedderSuggestion,
|
||||
ClearChatDraft, CloneOpenCodeProfileFromSeed, CloneProfileFromSeed, CloseProject, CloseTab,
|
||||
CloseTerminal, CloseTicketAssistant, ConfigureProfiles, ContextGuardUseCases,
|
||||
CreateAgentFromScratch, CreateAgentFromTemplate, CreateIssue, CreateLayout, CreateMemory,
|
||||
CreateProject, CreateSkill, CreateSprint, CreateTemplate, DeleteAgent, DeleteEmbedderProfile,
|
||||
DeleteIssue, DeleteLayout, DeleteMemory, DeleteModelArtifact, DeleteModelServer, DeleteProfile,
|
||||
DeleteSkill, DeleteSprint, DeleteTemplate, DescribeEmbedderEngines, DetectAgentDrift,
|
||||
DetectProfiles, DismissEmbedderSuggestion, EnsureLocalModelServer, ExecuteSlashCommand,
|
||||
FirstRunState, GcManagedTemp, GetAppExitWorkGuardState, GetLiveStateLean, GetMemory,
|
||||
GetProjectPermissions, GetProjectSystemPermissions, GetProjectWorkState, GitBranches,
|
||||
GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage,
|
||||
HarvestMemoryFromTurn, HealthUseCase, ImportChatAttachments, InspectConversation,
|
||||
InstallPluginFromArchive, InstallPluginFromDirectory, JsonPluginManifestValidator, LaunchAgent,
|
||||
LaunchAgentInput, LinkIssues, ListAgents, ListAgentsInput, ListClaudeModels, ListCodexModels,
|
||||
ListDevices, ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
|
||||
ListOpenCodeProviders, ListPluginRuntimeContributions, ListPlugins, ListProfiles,
|
||||
ListProjectFiles, ListProjectPluginSettings, ListProjects, ListResumableAgents, ListSkills,
|
||||
ListSlashCommands, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions,
|
||||
@ -2207,6 +2207,8 @@ pub struct BackendCore {
|
||||
/// `LaunchAgent`/`ChangeAgentProfile` ; consommé par les commandes de chat (D4)
|
||||
/// pour résoudre la session vivante d'un `sessionId` et l'arrêter à la fermeture.
|
||||
pub structured_sessions: Arc<StructuredSessions>,
|
||||
/// Non-destructive cancellation of the current structured chat request.
|
||||
pub cancel_agent_chat: Arc<CancelAgentChat>,
|
||||
// --- Agents (L6) ---
|
||||
/// Create a project agent from scratch.
|
||||
pub create_agent: Arc<CreateAgentFromScratch>,
|
||||
@ -2752,6 +2754,7 @@ impl BackendCore {
|
||||
// `profile.structured_adapter`. Injectés dans LaunchAgent (routage §17.4) et
|
||||
// ChangeAgentProfile (shutdown polymorphe au hot-swap).
|
||||
let structured_sessions = Arc::new(StructuredSessions::new());
|
||||
let cancel_agent_chat = Arc::new(CancelAgentChat::new(Arc::clone(&structured_sessions)));
|
||||
let live_sessions = Arc::new(LiveSessions::new(
|
||||
Arc::clone(&terminal_sessions),
|
||||
Arc::clone(&structured_sessions),
|
||||
@ -4462,6 +4465,7 @@ impl BackendCore {
|
||||
update_agent_mcp_tool_permissions,
|
||||
set_agent_mcp_tool_allowed,
|
||||
structured_sessions,
|
||||
cancel_agent_chat,
|
||||
create_agent,
|
||||
list_agents,
|
||||
read_agent_context,
|
||||
|
||||
Reference in New Issue
Block a user