fix(runtime): isolate agent state by project (#101)
This commit is contained in:
@ -10,7 +10,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::input::InputMediator;
|
||||
use domain::{AgentId, NodeId, Project, SessionId};
|
||||
use domain::{AgentId, NodeId, Project, RuntimeAgentKey, SessionId};
|
||||
|
||||
use crate::error::AppError;
|
||||
use crate::orchestrator::OrchestratorService;
|
||||
@ -65,11 +65,11 @@ impl AttachLiveAgent {
|
||||
/// [`AppError::NotFound`] when the agent has no live session in either registry.
|
||||
pub fn execute(&self, input: AttachLiveAgentInput) -> Result<AttachLiveAgentOutput, AppError> {
|
||||
// PTY first, then structured (one-live-session-per-agent ⇒ at most one match).
|
||||
if let Some(session) = self
|
||||
.live
|
||||
.pty
|
||||
.rebind_agent_node(&input.agent_id, input.node_id)
|
||||
{
|
||||
if let Some(session) = self.live.pty.rebind_agent_node_in_project(
|
||||
input.project.id,
|
||||
&input.agent_id,
|
||||
input.node_id,
|
||||
) {
|
||||
return Ok(AttachLiveAgentOutput {
|
||||
agent_id: input.agent_id,
|
||||
node_id: session.node_id,
|
||||
@ -77,11 +77,11 @@ impl AttachLiveAgent {
|
||||
kind: LiveSessionKind::Pty,
|
||||
});
|
||||
}
|
||||
if let Some(session) = self
|
||||
.live
|
||||
.structured
|
||||
.rebind_agent_node(&input.agent_id, input.node_id)
|
||||
{
|
||||
if let Some(session) = self.live.structured.rebind_agent_node_in_project(
|
||||
input.project.id,
|
||||
&input.agent_id,
|
||||
input.node_id,
|
||||
) {
|
||||
return Ok(AttachLiveAgentOutput {
|
||||
agent_id: input.agent_id,
|
||||
node_id: input.node_id,
|
||||
@ -168,28 +168,37 @@ impl StopLiveAgent {
|
||||
&self,
|
||||
input: StopLiveAgentInput,
|
||||
) -> Result<StopLiveAgentOutput, AppError> {
|
||||
self.stop_dependencies(input.agent_id).await;
|
||||
self.stop_dependencies(&input.project, input.agent_id).await;
|
||||
if let Some(mediator) = &self.input {
|
||||
mediator.preempt(input.agent_id);
|
||||
mediator.preempt(RuntimeAgentKey::new(input.project.id, input.agent_id));
|
||||
}
|
||||
self.stop_one(input.agent_id).await
|
||||
self.stop_one_in_project(&input.project, input.agent_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn stop_dependencies(&self, agent_id: AgentId) {
|
||||
async fn stop_dependencies(&self, project: &Project, 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);
|
||||
mediator.preempt(RuntimeAgentKey::new(project.id, dep));
|
||||
}
|
||||
let _ = self.stop_one(dep).await;
|
||||
let _ = self.stop_one_in_project(project, dep).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn stop_one(&self, agent_id: AgentId) -> Result<StopLiveAgentOutput, AppError> {
|
||||
async fn stop_one_in_project(
|
||||
&self,
|
||||
project: &Project,
|
||||
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(&agent_id) {
|
||||
if let Some(session_id) = self
|
||||
.live
|
||||
.pty
|
||||
.session_for_agent_in_project(project.id, &agent_id)
|
||||
{
|
||||
self.close
|
||||
.execute(CloseTerminalInput { session_id })
|
||||
.await?;
|
||||
@ -201,7 +210,11 @@ impl StopLiveAgent {
|
||||
}
|
||||
// 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(&agent_id) {
|
||||
if let Some(session_id) = self
|
||||
.live
|
||||
.structured
|
||||
.session_id_for_agent_in_project(project.id, &agent_id)
|
||||
{
|
||||
if let Some(session) = self.live.structured.remove(&session_id) {
|
||||
session
|
||||
.shutdown()
|
||||
|
||||
Reference in New Issue
Block a user