fix(chat): le bouton Stop de la CLI custom arrête l'agent en cascade (#187)
Le Stop du CustomAgentChatView n'annulait que le tour courant sans libérer la session ni toucher aux tâches enfants ou appels MCP en cours. Il appelle désormais stop_live_agent sur l'agent vivant, qui: - ajoute le port StopAgentExecutionCascade (best-effort, idempotent), implémenté par OrchestratorService pour annuler les idea_run_in_background rattachés au rendez-vous actif (via BackgroundTaskRunner.cancel); - cascade l'arrêt aux wait-dépendances (stop_turn_watch + preempt) pour interrompre les appels MCP et les attentes inter-agents; - libère et supprime la session structurée. UI: nouvel état "Arrêt en cours..." (bouton disabled + loading) pendant l'arrêt, libération du sessionId, label expliquant la cascade demandée. La Tauri command cancel_agent_chat est alignée sur stop_live_agent + stop_turn_watch agent et dépendances. Tests: SpyBackgroundRunner (cascade n'annule que les tâches rattachées), workstate_actions cascade best-effort, CustomAgentChatView stop+cascade feedback et fallback NOT_FOUND.
This commit is contained in:
@ -9,6 +9,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use domain::input::InputMediator;
|
||||
use domain::{AgentId, NodeId, Project, RuntimeAgentKey, SessionId};
|
||||
|
||||
@ -116,6 +117,18 @@ pub struct StopLiveAgentOutput {
|
||||
pub kind: LiveSessionKind,
|
||||
}
|
||||
|
||||
/// Best-effort stopper for execution resources owned by one live agent turn.
|
||||
///
|
||||
/// Implementations must be idempotent: a missing, already-finished or already-stopped
|
||||
/// child resource is a successful no-op. Failures are deliberately not propagated by
|
||||
/// [`StopLiveAgent`], because the product contract is "stop the agent; cascade related
|
||||
/// work best-effort".
|
||||
#[async_trait]
|
||||
pub trait StopAgentExecutionCascade: Send + Sync {
|
||||
/// Requests cancellation of child execution resources for `agent_id` in `project`.
|
||||
async fn stop_agent_execution_children(&self, project: &Project, agent_id: AgentId);
|
||||
}
|
||||
|
||||
/// Stops an already-running agent's live session, polymorphically.
|
||||
///
|
||||
/// Resolves the session by agent (PTY first, then structured). A PTY session is
|
||||
@ -129,6 +142,7 @@ pub struct StopLiveAgent {
|
||||
close: Arc<CloseTerminal>,
|
||||
input: Option<Arc<dyn InputMediator>>,
|
||||
waits: Option<Arc<OrchestratorService>>,
|
||||
cascade: Option<Arc<dyn StopAgentExecutionCascade>>,
|
||||
}
|
||||
|
||||
impl StopLiveAgent {
|
||||
@ -140,6 +154,7 @@ impl StopLiveAgent {
|
||||
close,
|
||||
input: None,
|
||||
waits: None,
|
||||
cascade: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,6 +174,13 @@ impl StopLiveAgent {
|
||||
self
|
||||
}
|
||||
|
||||
/// Wires best-effort cancellation of child resources spawned by the running turn.
|
||||
#[must_use]
|
||||
pub fn with_execution_cascade(mut self, cascade: Arc<dyn StopAgentExecutionCascade>) -> Self {
|
||||
self.cascade = Some(cascade);
|
||||
self
|
||||
}
|
||||
|
||||
/// Tears down the agent's live session.
|
||||
///
|
||||
/// # Errors
|
||||
@ -177,10 +199,18 @@ impl StopLiveAgent {
|
||||
}
|
||||
|
||||
async fn stop_dependencies(&self, project: &Project, agent_id: AgentId) {
|
||||
if let Some(cascade) = &self.cascade {
|
||||
cascade
|
||||
.stop_agent_execution_children(project, agent_id)
|
||||
.await;
|
||||
}
|
||||
let Some(waits) = &self.waits else {
|
||||
return;
|
||||
};
|
||||
for dep in waits.active_wait_dependencies(agent_id) {
|
||||
if let Some(cascade) = &self.cascade {
|
||||
cascade.stop_agent_execution_children(project, dep).await;
|
||||
}
|
||||
if let Some(mediator) = &self.input {
|
||||
mediator.preempt(RuntimeAgentKey::new(project.id, dep));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user