agent conversation fix

This commit is contained in:
2026-06-27 12:42:37 +02:00
parent c2d1a669c5
commit 1fc7869160
38 changed files with 1173 additions and 1549 deletions

View File

@ -9,9 +9,11 @@
use std::sync::Arc;
use domain::input::InputMediator;
use domain::{AgentId, NodeId, Project, SessionId};
use crate::error::AppError;
use crate::orchestrator::OrchestratorService;
use crate::terminal::{CloseTerminal, CloseTerminalInput, LiveSessionKind, LiveSessions};
/// Input for [`AttachLiveAgent::execute`].
@ -125,13 +127,36 @@ pub struct StopLiveAgentOutput {
pub struct StopLiveAgent {
live: Arc<LiveSessions>,
close: Arc<CloseTerminal>,
input: Option<Arc<dyn InputMediator>>,
waits: Option<Arc<OrchestratorService>>,
}
impl StopLiveAgent {
/// Builds the use case from the live-session registry and the close primitive.
#[must_use]
pub fn new(live: Arc<LiveSessions>, close: Arc<CloseTerminal>) -> Self {
Self { live, close }
Self {
live,
close,
input: None,
waits: None,
}
}
/// Wires cancellation helpers used for user-driven stop cascades.
///
/// Stopping A while A waits on B should stop B too. The orchestrator service is
/// only used as a read-only provider of active wait edges; the input mediator is
/// used to preempt the running turn before the session is torn down.
#[must_use]
pub fn with_cascade(
mut self,
input: Arc<dyn InputMediator>,
waits: Arc<OrchestratorService>,
) -> Self {
self.input = Some(input);
self.waits = Some(waits);
self
}
/// Tears down the agent's live session.
@ -143,20 +168,40 @@ impl StopLiveAgent {
&self,
input: StopLiveAgentInput,
) -> Result<StopLiveAgentOutput, AppError> {
self.stop_dependencies(input.agent_id).await;
if let Some(mediator) = &self.input {
mediator.preempt(input.agent_id);
}
self.stop_one(input.agent_id).await
}
async fn stop_dependencies(&self, agent_id: AgentId) {
let Some(waits) = &self.waits else {
return;
};
for dep in waits.active_wait_dependencies(agent_id) {
if let Some(mediator) = &self.input {
mediator.preempt(dep);
}
let _ = self.stop_one(dep).await;
}
}
async fn stop_one(&self, agent_id: AgentId) -> Result<StopLiveAgentOutput, AppError> {
// PTY first: delegate to the existing close primitive (removes + kills).
if let Some(session_id) = self.live.pty.session_for_agent(&input.agent_id) {
if let Some(session_id) = self.live.pty.session_for_agent(&agent_id) {
self.close
.execute(CloseTerminalInput { session_id })
.await?;
return Ok(StopLiveAgentOutput {
agent_id: input.agent_id,
agent_id,
session_id,
kind: LiveSessionKind::Pty,
});
}
// Structured: remove from the registry first (so the uniqueness guard no
// longer sees a live session), then shut the session down out of the lock.
if let Some(session_id) = self.live.structured.session_id_for_agent(&input.agent_id) {
if let Some(session_id) = self.live.structured.session_id_for_agent(&agent_id) {
if let Some(session) = self.live.structured.remove(&session_id) {
session
.shutdown()
@ -164,14 +209,13 @@ impl StopLiveAgent {
.map_err(|e| AppError::Process(e.to_string()))?;
}
return Ok(StopLiveAgentOutput {
agent_id: input.agent_id,
agent_id,
session_id,
kind: LiveSessionKind::Structured,
});
}
Err(AppError::NotFound(format!(
"running session for agent {}",
input.agent_id
"running session for agent {agent_id}"
)))
}
}