fix(orchestrator): la délégation simple vers UX renvoie son Final structuré (#207, QA verte)
Une demande idea_ask_agent vers UX échouait avec « finished without a structured Final response » alors qu'Architect répondait. Deux causes corrigées: - session/process: un tour CLI terminé en code non nul SANS stdout (ex. erreur 401/auth) renvoyait une sortie vide au lieu du stderr réel; l'orchestrateur concluait alors à un « no Final ». On remonte désormais le stderr effectif (drain sync + async + helper process_exit_message + test dédié). - orchestrator/service: la branche « ask waiting on background » masquait le premier Final structuré; la réponse synchrone redevient le premier Final (suppression de active_rendezvous_has_background_tasks / mark_rendezvous_task_waiting). Nettoyage runtime corollaire (#204): arrêt du live runtime stale (PTY + structuré) d'un agent recréé afin que la délégation route vers l'agent courant (LiveSessions, stop_live_agent_runtime_in_project, wiring backend via DeleteAgent::with_runtime_cleanup, lifecycle, workstate cascade).
This commit is contained in:
@ -4,13 +4,13 @@ use std::sync::Arc;
|
||||
|
||||
use domain::ports::{EventBus, PtyPort, SpawnSpec};
|
||||
use domain::{
|
||||
DomainEvent, NodeId, ProjectPath, PtySize, SessionId, SessionKind, SessionStatus,
|
||||
TerminalSession,
|
||||
AgentId, DomainEvent, NodeId, Project, ProjectPath, PtySize, SessionId, SessionKind,
|
||||
SessionStatus, TerminalSession,
|
||||
};
|
||||
|
||||
use crate::error::AppError;
|
||||
|
||||
use super::registry::TerminalSessions;
|
||||
use super::registry::{LiveSessionKind, LiveSessions, TerminalSessions};
|
||||
|
||||
/// Input for [`OpenTerminal::execute`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -230,6 +230,59 @@ impl CloseTerminal {
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime session stopped for one agent.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct StoppedAgentRuntime {
|
||||
/// Agent whose live runtime was stopped.
|
||||
pub agent_id: AgentId,
|
||||
/// Live session id removed from its registry.
|
||||
pub session_id: SessionId,
|
||||
/// Runtime registry that owned the session.
|
||||
pub kind: LiveSessionKind,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub async fn stop_live_agent_runtime_in_project(
|
||||
project: &Project,
|
||||
agent_id: AgentId,
|
||||
live: &LiveSessions,
|
||||
close: &CloseTerminal,
|
||||
) -> Result<Vec<StoppedAgentRuntime>, AppError> {
|
||||
let mut stopped = Vec::new();
|
||||
|
||||
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()))?;
|
||||
}
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(stopped)
|
||||
}
|
||||
|
||||
/// The platform default interactive shell.
|
||||
///
|
||||
/// Resolution policy lives in the application layer (a metier default), not the
|
||||
|
||||
Reference in New Issue
Block a user