use anyhow::Result;
use std::collections::VecDeque;
use std::io::Write;
use std::time::Instant;
use tokio_util::sync::CancellationToken;
use tokio::time::{Duration, sleep, timeout};
use vtcode_core::config::loader::VTCodeConfig;
use vtcode_core::config::resolve_timeout;
use vtcode_core::config::types::AgentConfig as CoreAgentConfig;
use vtcode_core::core::interfaces::session::PlanModeEntrySource;
use vtcode_core::hooks::{SessionEndReason, SessionStartTrigger};
#[derive(Clone, Copy)]
struct IdleDetectionConfig {
timeout_ms: u64,
backoff_ms: u64,
max_cycles: usize,
enabled: bool,
}
use crate::agent::runloop::unified::inline_events::harness::{
HarnessEventEmitter, default_harness_log_dir, resolve_event_log_path,
};
use crate::agent::runloop::unified::run_loop_context::{HarnessTurnState, TurnId, TurnRunId};
use chrono::Utc;
use vtcode_core::exec::events::{ThreadEvent, ThreadStartedEvent};
use vtcode_core::session::SessionId;
use vtcode_core::utils::ansi::MessageStyle;
use vtcode_core::utils::session_archive::{SessionMessage, SessionProgressArgs};
use crate::agent::runloop::ResumeSession;
use crate::agent::runloop::model_picker::ModelPickerState;
use crate::agent::runloop::unified::plan_mode_state::transition_to_plan_mode;
use super::super::context::TurnLoopResult as RunLoopTurnLoopResult;
use super::super::finalization::finalize_session;
use vtcode_core::core::agent::steering::SteeringMessage;
use crate::agent::runloop::unified::palettes::ActivePalette;
use crate::agent::runloop::unified::session_setup::{
SessionState, initialize_session, initialize_session_ui, spawn_signal_handler,
};
use crate::agent::runloop::unified::state::SessionStats;
use crate::agent::runloop::unified::status_line::InputStatusState;
use crate::agent::runloop::unified::workspace_links::LinkedDirectory;
#[path = "session_loop_runner/mod.rs"]
mod session_loop_runner;
const RECENT_MESSAGE_LIMIT: usize = 16;
#[inline]
fn extract_idle_config(vt_cfg: Option<&VTCodeConfig>) -> IdleDetectionConfig {
vt_cfg
.map(|cfg| {
let idle_config = &cfg.optimization.agent_execution;
IdleDetectionConfig {
timeout_ms: idle_config.idle_timeout_ms,
backoff_ms: idle_config.idle_backoff_ms,
max_cycles: idle_config.max_idle_cycles,
enabled: idle_config.idle_timeout_ms > 0,
}
})
.unwrap_or(IdleDetectionConfig {
timeout_ms: 0,
backoff_ms: 0,
max_cycles: 0,
enabled: false,
})
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn run_single_agent_loop_unified(
config: &CoreAgentConfig,
_vt_cfg: Option<VTCodeConfig>,
_skip_confirmations: bool,
full_auto: bool,
plan_mode_entry_source: PlanModeEntrySource,
resume: Option<ResumeSession>,
mut steering_receiver: Option<tokio::sync::mpsc::UnboundedReceiver<SteeringMessage>>,
) -> Result<()> {
session_loop_runner::run_single_agent_loop_unified_impl(
config,
_vt_cfg,
_skip_confirmations,
full_auto,
plan_mode_entry_source,
resume,
&mut steering_receiver,
)
.await
}
struct TerminalCleanupGuard;
impl TerminalCleanupGuard {
fn new() -> Self {
Self
}
}
impl Drop for TerminalCleanupGuard {
fn drop(&mut self) {
let _ = vtcode_tui::panic_hook::restore_tui();
let mut stdout = std::io::stdout();
let _ = stdout.flush();
let delay_ms = std::env::var("VT_TERMINAL_CLEANUP_DELAY_MS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(50);
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
}
}
struct CancelGuard(CancellationToken);
impl Drop for CancelGuard {
fn drop(&mut self) {
self.0.cancel();
}
}