fix(custom-cli): le bouton Stop annule le tour courant sans fermer la session et cascade l'annulation aux agents délégués — stop_agent suit l'arbre des dépendances actives (cancel_current_turn + préemption runtime + enfants d'exécution), TurnCancel tue le process du tour en cours, autorisation requester/sous-arbre sur idea_stop_agent, fixtures DTO runtime_state/node_id alignées — #257 (QA verte)

This commit is contained in:
2026-08-28 22:57:32 +02:00
parent 9505a3305d
commit 1aabc3d702
12 changed files with 534 additions and 110 deletions

View File

@ -25,7 +25,7 @@ use domain::ports::{
use domain::sandbox::{SandboxEnforcer, SandboxPlan};
use domain::SessionId;
use super::process::{run_turn, SpawnLine};
use super::process::{run_turn_cancellable, SpawnLine, TurnCancel};
/// Résultat du parsing d'une ligne : **zéro ou plusieurs** événements à émettre et/ou
/// un `session_id` capté (init/result). Permet à [`parse_event`] de rester **pure**
@ -306,6 +306,8 @@ pub struct ClaudeSdkSession {
/// Enforcer OS **par instance** (lot LP4-4), passé à [`run_turn`]. `None` ⇒ pas
/// de sandboxing même si un plan est présent (cohérent avec le chemin PTY).
sandbox_enforcer: Option<Arc<dyn SandboxEnforcer>>,
/// Cancel handle for the process backing the current `claude` turn.
turn_cancel: TurnCancel,
}
impl ClaudeSdkSession {
@ -330,6 +332,7 @@ impl ClaudeSdkSession {
conversation_id: Mutex::new(seed_conversation_id),
sandbox,
sandbox_enforcer,
turn_cancel: TurnCancel::new(),
}
}
@ -391,6 +394,10 @@ impl AgentSession for ClaudeSdkSession {
// tours, donc `shutdown` est intrinsèquement idempotent et sans effet.
Ok(())
}
async fn cancel_current_turn(&self) -> Result<(), AgentSessionError> {
self.turn_cancel.cancel().await
}
}
impl ClaudeSdkSession {
@ -415,7 +422,14 @@ impl ClaudeSdkSession {
});
(Some(line_tx), Some(parser))
});
let raw_result = run_turn(&spec, None, self.sandbox_enforcer.as_ref(), line_tap).await;
let raw_result = run_turn_cancellable(
&spec,
None,
self.sandbox_enforcer.as_ref(),
line_tap,
&self.turn_cancel,
)
.await;
if let Some(parser) = parser_thread {
let _ = parser.join();
}