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:
@ -2,7 +2,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::{EventBus, PtyPort, SpawnSpec};
|
||||
use domain::ports::{CancelCurrentTurnOutcome, EventBus, PtyPort, SpawnSpec};
|
||||
use domain::{
|
||||
AgentId, DomainEvent, NodeId, Project, ProjectPath, PtySize, SessionId, SessionKind,
|
||||
SessionStatus, TerminalSession,
|
||||
@ -11,7 +11,54 @@ use domain::{
|
||||
use crate::error::AppError;
|
||||
use crate::managed_temp::{ReleaseManagedTemp, ReleaseManagedTempInput};
|
||||
|
||||
use super::registry::{LiveRuntimeState, LiveSessionKind, LiveSessions, TerminalSessions};
|
||||
use super::registry::{
|
||||
LiveRuntimeState, LiveSessionKind, LiveSessions, StructuredSessions, TerminalSessions,
|
||||
};
|
||||
|
||||
/// Result of cancelling the current request while preserving its structured session.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CancelAgentChatOutput {
|
||||
/// Stable session that remains registered and reusable.
|
||||
pub session_id: SessionId,
|
||||
/// Whether this call cancelled work or observed an already-terminal turn.
|
||||
pub outcome: CancelCurrentTurnOutcome,
|
||||
}
|
||||
|
||||
/// Cancels one structured chat turn without stopping its reusable runtime.
|
||||
pub struct CancelAgentChat {
|
||||
sessions: Arc<StructuredSessions>,
|
||||
}
|
||||
|
||||
impl CancelAgentChat {
|
||||
/// Builds the use case from the structured runtime registry.
|
||||
#[must_use]
|
||||
pub fn new(sessions: Arc<StructuredSessions>) -> Self {
|
||||
Self { sessions }
|
||||
}
|
||||
|
||||
/// Requests cancellation from the live session and deliberately leaves its
|
||||
/// registry entry, conversation identity and runtime state untouched.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns [`AppError::NotFound`] only when the session itself is absent, and
|
||||
/// a process error when the adapter could not interrupt its current child.
|
||||
pub async fn execute(&self, session_id: SessionId) -> Result<CancelAgentChatOutput, AppError> {
|
||||
let (session, outcome) = self
|
||||
.sessions
|
||||
.request_turn_cancel(&session_id)
|
||||
.ok_or_else(|| AppError::NotFound(format!("structured session {session_id}")))?;
|
||||
if outcome == CancelCurrentTurnOutcome::Cancelled {
|
||||
if let Err(error) = session.cancel_current_turn().await {
|
||||
self.sessions.restore_turn_after_cancel_failure(&session_id);
|
||||
return Err(AppError::from(error));
|
||||
}
|
||||
}
|
||||
Ok(CancelAgentChatOutput {
|
||||
session_id,
|
||||
outcome,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Input for [`OpenTerminal::execute`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -281,8 +328,9 @@ pub struct StoppedAgentRuntime {
|
||||
/// Stops one agent's live runtime across both registries.
|
||||
///
|
||||
/// PTY sessions are closed through [`CloseTerminal`]. Structured sessions are
|
||||
/// removed from their registry before `shutdown()` so relaunch guards stop seeing
|
||||
/// them as live even while shutdown runs.
|
||||
/// removed from their registry, have their current turn cancelled, then receive
|
||||
/// `shutdown()` so relaunch guards stop seeing them as live during teardown and
|
||||
/// no per-turn child survives it.
|
||||
pub async fn stop_live_agent_runtime_in_project(
|
||||
project: &Project,
|
||||
agent_id: AgentId,
|
||||
@ -298,6 +346,9 @@ pub async fn stop_live_agent_runtime_in_project(
|
||||
let removed = match snapshot.kind {
|
||||
LiveSessionKind::Structured => {
|
||||
if let Some(removed) = live.structured.remove_runtime(&snapshot.session_id) {
|
||||
if let Err(error) = removed.session.cancel_current_turn().await {
|
||||
first_error.get_or_insert_with(|| AppError::Process(error.to_string()));
|
||||
}
|
||||
if let Err(error) = removed.session.shutdown().await {
|
||||
first_error.get_or_insert_with(|| AppError::Process(error.to_string()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user