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:
@ -11,8 +11,8 @@ use std::sync::{Arc, Mutex};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use application::{
|
||||
AttachLiveAgent, AttachLiveAgentInput, CloseTerminal, LiveSessionKind, LiveSessions,
|
||||
StopAgentExecutionCascade, StopLiveAgent, StopLiveAgentInput, StructuredSessions,
|
||||
AttachLiveAgent, AttachLiveAgentInput, CloseTerminal, LiveRuntimeState, LiveSessionKind,
|
||||
LiveSessions, StopAgentExecutionCascade, StopLiveAgent, StopLiveAgentInput, StructuredSessions,
|
||||
TerminalSessions,
|
||||
};
|
||||
use domain::ports::{
|
||||
@ -329,7 +329,9 @@ async fn stop_pty_kills_and_removes_session() {
|
||||
|
||||
assert_eq!(out.agent_id, a);
|
||||
assert_eq!(out.session_id, sid(1));
|
||||
assert_eq!(out.node_id, nid(100));
|
||||
assert_eq!(out.kind, LiveSessionKind::Pty);
|
||||
assert_eq!(out.runtime_state, LiveRuntimeState::Stopped);
|
||||
// Delegated to the close primitive: process killed and registry emptied.
|
||||
assert_eq!(f.pty_port.kills(), vec![sid(1)]);
|
||||
assert!(f.pty.is_empty(), "live session removed from the registry");
|
||||
@ -353,6 +355,7 @@ async fn stop_structured_shuts_down_and_removes_session() {
|
||||
|
||||
assert_eq!(out.session_id, sid(2));
|
||||
assert_eq!(out.kind, LiveSessionKind::Structured);
|
||||
assert_eq!(out.runtime_state, LiveRuntimeState::Stopped);
|
||||
assert!(flag.load(Ordering::SeqCst), "session.shutdown() was called");
|
||||
assert!(f.structured.is_empty(), "live session removed");
|
||||
assert_eq!(
|
||||
@ -388,6 +391,34 @@ async fn stop_structured_runs_execution_cascade_best_effort() {
|
||||
assert!(f.structured.is_empty(), "live session removed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stop_pty_is_idempotent_after_success() {
|
||||
let f = fixture();
|
||||
let a = aid(10);
|
||||
insert_pty(&f.pty, sid(1), a, nid(100));
|
||||
|
||||
let first = f
|
||||
.stop
|
||||
.execute(StopLiveAgentInput {
|
||||
project: project(),
|
||||
agent_id: a,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let second = f
|
||||
.stop
|
||||
.execute(StopLiveAgentInput {
|
||||
project: project(),
|
||||
agent_id: a,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(first, second);
|
||||
assert_eq!(second.runtime_state, LiveRuntimeState::Stopped);
|
||||
assert_eq!(f.pty_port.kills(), vec![sid(1)], "kill is not repeated");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stop_unknown_agent_is_not_found() {
|
||||
let f = fixture();
|
||||
|
||||
Reference in New Issue
Block a user