use crate::hitl::ConfirmationProvider;
use crate::hooks::HookExecutor;
use crate::run::InMemoryRunStore;
use crate::subagent_task_tracker::{InMemorySubagentTaskTracker, SubagentStatus};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;
pub(crate) struct SessionCloseHandle {
pub(crate) session_id: String,
pub(crate) closed: Arc<AtomicBool>,
pub(crate) session_cancel: CancellationToken,
pub(crate) cancel_token: Arc<Mutex<Option<CancellationToken>>>,
pub(crate) current_run_id: Arc<Mutex<Option<String>>>,
pub(crate) run_store: Arc<InMemoryRunStore>,
pub(crate) subagent_tasks: Arc<InMemorySubagentTaskTracker>,
pub(crate) confirmation_manager: Option<Arc<dyn ConfirmationProvider>>,
pub(crate) hook_executor: Option<Arc<dyn HookExecutor>>,
}
impl SessionCloseHandle {
pub(crate) fn is_closed(&self) -> bool {
self.closed.load(Ordering::Acquire)
}
pub(crate) async fn close(&self) {
if self.closed.swap(true, Ordering::AcqRel) {
return;
}
self.session_cancel.cancel();
let had_active_token = self.cancel_token.lock().await.is_some();
if had_active_token {
if let Some(run_id) = self.current_run_id.lock().await.clone() {
let _ = self.run_store.mark_cancelled(&run_id).await;
if let Some(hook) = &self.hook_executor {
hook.record_run_cancelled(&run_id, &self.session_id, Some("cancelled by host"))
.await;
}
}
}
let pending: Vec<String> = self
.subagent_tasks
.list_for_parent(&self.session_id)
.await
.into_iter()
.filter(|task| task.status == SubagentStatus::Running)
.map(|task| task.task_id)
.collect();
for task_id in pending {
let _ = self.subagent_tasks.cancel(&task_id).await;
}
if let Some(manager) = &self.confirmation_manager {
let _ = manager.cancel_all().await;
}
tracing::info!(session_id = %self.session_id, "AgentSession closed");
}
}