use crate::agent::runloop::unified::async_mcp_manager::AsyncMcpManager;
use crate::agent::runloop::unified::state::{CtrlCSignal, CtrlCState};
use crate::agent::runloop::unified::stop_requests::request_local_stop;
use std::sync::Arc;
use tokio::sync::Notify;
use tokio_util::sync::CancellationToken;
use vtcode_core::notifications::set_global_terminal_focused;
pub(crate) type SignalHandlerGuard = vtcode_commons::TaskGuard;
static EXIT_POSTAMBLE_ARMED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
pub(crate) fn mark_exit_postamble_armed() {
EXIT_POSTAMBLE_ARMED.store(true, std::sync::atomic::Ordering::SeqCst);
}
fn is_exit_postamble_armed() -> bool {
EXIT_POSTAMBLE_ARMED.load(std::sync::atomic::Ordering::SeqCst)
}
pub(crate) fn spawn_signal_handler(
ctrl_c_state: Arc<CtrlCState>,
ctrl_c_notify: Arc<Notify>,
async_mcp_manager: Option<Arc<AsyncMcpManager>>,
cancel_token: CancellationToken,
) -> SignalHandlerGuard {
let handle = tokio::spawn(async move {
loop {
tokio::select! {
_ = vtcode_core::shutdown::shutdown_signal() => {
let signal = request_local_stop(&ctrl_c_state, &ctrl_c_notify);
if matches!(signal, CtrlCSignal::Exit) {
if is_exit_postamble_armed() {
tracing::debug!(
"double interrupt during graceful exit postamble; letting the exit summary finish"
);
continue;
}
if let Some(mcp_manager) = &async_mcp_manager {
let _ = tokio::time::timeout(
std::time::Duration::from_millis(500),
mcp_manager.shutdown(),
)
.await;
}
emergency_terminal_cleanup();
break;
}
if let Some(mcp_manager) = &async_mcp_manager {
let mcp = Arc::clone(mcp_manager);
tokio::spawn(async move {
let _ = tokio::time::timeout(
std::time::Duration::from_secs(2),
mcp.shutdown(),
).await;
});
}
}
_ = cancel_token.cancelled() => {
break;
}
}
}
});
SignalHandlerGuard::new(handle)
}
fn emergency_terminal_cleanup() {
set_global_terminal_focused(false);
let _ = vtcode_ui::tui::panic_hook::restore_tui();
vtcode_commons::trace_flush::flush_trace_log();
eprintln!("\r\nInterrupted — exiting (Ctrl+C pressed twice). Terminal restored.");
let _ = std::io::Write::flush(&mut std::io::stdout());
let _ = std::io::Write::flush(&mut std::io::stderr());
std::process::exit(130);
}