fix(runtime): stop/restart d'agent visible robuste — kill réel, état stopped, relance froide (#196, QA verte)
Solidifie le cycle stop/restart d'un agent visible en cellule: - terminal/registry: kill effectif de la TUI native et de la CLI custom, arrêt idempotent du live runtime (PTY + structuré), distinction nette « agent stoppé » vs « pas de session ». - workstate + lifecycle: projection d'un état runtime explicite « stopped » (AgentRuntimeState: running/stopping/stopped/failedStart), réconcilié avec le live state; traitement des courses « agent not found ». - relance froide via freshStart: repart de zéro sans réattacher une session morte et recharge la configuration courante de l'agent (y compris changement de modèle). - contrat projeté côté adapters frontend (ports/domain/normalization/mock) pour la consommation UI future (#197/#198), sans surface UI ici. Tests QA verts: workstate_actions, orchestrator_service stop_agent, agent_lifecycle (reopen/fresh_start), workstate stopped runtime state, vitest adapter stop_live_agent.
This commit is contained in:
@ -10,7 +10,7 @@ use domain::{
|
||||
|
||||
use crate::error::AppError;
|
||||
|
||||
use super::registry::{LiveSessionKind, LiveSessions, TerminalSessions};
|
||||
use super::registry::{LiveRuntimeState, LiveSessionKind, LiveSessions, TerminalSessions};
|
||||
|
||||
/// Input for [`OpenTerminal::execute`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -223,9 +223,10 @@ impl CloseTerminal {
|
||||
) -> Result<CloseTerminalOutput, AppError> {
|
||||
let handle = self
|
||||
.sessions
|
||||
.remove(&input.session_id)
|
||||
.handle(&input.session_id)
|
||||
.ok_or_else(|| AppError::NotFound(format!("terminal session {}", input.session_id)))?;
|
||||
let status = self.pty.kill(&handle).await?;
|
||||
self.sessions.remove(&input.session_id);
|
||||
Ok(CloseTerminalOutput { code: status.code })
|
||||
}
|
||||
}
|
||||
@ -237,8 +238,12 @@ pub struct StoppedAgentRuntime {
|
||||
pub agent_id: AgentId,
|
||||
/// Live session id removed from its registry.
|
||||
pub session_id: SessionId,
|
||||
/// Last known layout node hosting the runtime view.
|
||||
pub node_id: NodeId,
|
||||
/// Runtime registry that owned the session.
|
||||
pub kind: LiveSessionKind,
|
||||
/// Lifecycle state reached by this stop operation.
|
||||
pub state: LiveRuntimeState,
|
||||
}
|
||||
|
||||
/// Stops one agent's live runtime across both registries.
|
||||
@ -253,30 +258,34 @@ pub async fn stop_live_agent_runtime_in_project(
|
||||
close: &CloseTerminal,
|
||||
) -> Result<Vec<StoppedAgentRuntime>, AppError> {
|
||||
let mut stopped = Vec::new();
|
||||
let snapshots = live.live_agent_snapshots_for(project.id, &agent_id);
|
||||
|
||||
if let Some(session_id) = live
|
||||
.structured
|
||||
.session_id_for_agent_in_project(project.id, &agent_id)
|
||||
{
|
||||
if let Some(session) = live.structured.remove(&session_id) {
|
||||
session
|
||||
.shutdown()
|
||||
.await
|
||||
.map_err(|e| AppError::Process(e.to_string()))?;
|
||||
for snapshot in snapshots {
|
||||
live.mark_runtime_state(snapshot, LiveRuntimeState::Stopping);
|
||||
match snapshot.kind {
|
||||
LiveSessionKind::Structured => {
|
||||
if let Some(session) = live.structured.remove(&snapshot.session_id) {
|
||||
session
|
||||
.shutdown()
|
||||
.await
|
||||
.map_err(|e| AppError::Process(e.to_string()))?;
|
||||
}
|
||||
}
|
||||
LiveSessionKind::Pty => {
|
||||
close
|
||||
.execute(CloseTerminalInput {
|
||||
session_id: snapshot.session_id,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
live.mark_runtime_state(snapshot, LiveRuntimeState::Stopped);
|
||||
stopped.push(StoppedAgentRuntime {
|
||||
agent_id,
|
||||
session_id,
|
||||
kind: LiveSessionKind::Structured,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(session_id) = live.pty.session_for_agent_in_project(project.id, &agent_id) {
|
||||
close.execute(CloseTerminalInput { session_id }).await?;
|
||||
stopped.push(StoppedAgentRuntime {
|
||||
agent_id,
|
||||
session_id,
|
||||
kind: LiveSessionKind::Pty,
|
||||
agent_id: snapshot.agent_id,
|
||||
session_id: snapshot.session_id,
|
||||
node_id: snapshot.node_id,
|
||||
kind: snapshot.kind,
|
||||
state: LiveRuntimeState::Stopped,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user