vtcode 0.136.4

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use std::time::Duration;

use vtcode_commons::stop_hints::STOP_HINT_INLINE;

pub(crate) const WAIT_KEEPALIVE_INITIAL: Duration = Duration::from_secs(5);
pub(crate) const WAIT_KEEPALIVE_INTERVAL: Duration = Duration::from_secs(10);

pub(crate) fn wait_keepalive_message(subject: &str, elapsed: Duration) -> String {
    format!("{subject} is still running after {}s. {}.", elapsed.as_secs(), STOP_HINT_INLINE)
}

pub(crate) fn wait_timeout_warning_message(subject: &str, timeout: Duration, remaining: Duration) -> String {
    format!(
        "{subject} is nearing the {}s timeout ({}s remaining). {}.",
        timeout.as_secs(),
        remaining.as_secs(),
        STOP_HINT_INLINE
    )
}

pub(crate) fn resolve_warning_delay(
    total_timeout: Duration,
    desired_delay: Duration,
    min_headroom: Duration,
) -> Option<Duration> {
    if total_timeout.is_zero() {
        return None;
    }

    let warning_delay = desired_delay.min(total_timeout.saturating_sub(min_headroom));
    (!warning_delay.is_zero()).then_some(warning_delay)
}

pub(crate) fn resolve_fractional_warning_delay(
    total_timeout: Duration,
    warning_fraction: f32,
    min_headroom: Duration,
) -> Option<Duration> {
    let fraction = warning_fraction.clamp(0.0, 0.99);
    resolve_warning_delay(total_timeout, total_timeout.mul_f32(fraction), min_headroom)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn keepalive_cadence_uses_five_then_ten_seconds() {
        assert_eq!(WAIT_KEEPALIVE_INITIAL, Duration::from_secs(5));
        assert_eq!(WAIT_KEEPALIVE_INTERVAL, Duration::from_secs(10));
    }

    #[test]
    fn warning_delay_prefers_earlier_desired_delay() {
        assert_eq!(
            resolve_warning_delay(Duration::from_secs(60), Duration::from_secs(12), Duration::from_secs(5)),
            Some(Duration::from_secs(12))
        );
    }

    #[test]
    fn warning_delay_caps_at_timeout_headroom() {
        assert_eq!(
            resolve_warning_delay(Duration::from_secs(60), Duration::from_secs(58), Duration::from_secs(10)),
            Some(Duration::from_secs(50))
        );
    }

    #[test]
    fn warning_delay_skips_zero_delay() {
        assert_eq!(resolve_warning_delay(Duration::from_secs(5), Duration::from_secs(5), Duration::from_secs(5)), None);
    }

    #[test]
    fn fractional_warning_delay_targets_three_quarters_budget() {
        assert_eq!(
            resolve_fractional_warning_delay(Duration::from_secs(60), 0.75, Duration::from_secs(15)),
            Some(Duration::from_secs(45))
        );
    }
}