ai-agent 0.88.0

Idiomatic agent sdk inspired by the claude code source leak
Documentation
use std::time::{Duration, Instant};

pub struct TimeoutState {
    start_time: Option<Instant>,
    duration: Duration,
}

impl TimeoutState {
    pub fn new(duration_ms: u64) -> Self {
        Self {
            start_time: Some(Instant::now()),
            duration: Duration::from_millis(duration_ms),
        }
    }

    pub fn is_expired(&self) -> bool {
        if let Some(start) = self.start_time {
            start.elapsed() >= self.duration
        } else {
            false
        }
    }

    pub fn remaining_ms(&self) -> u64 {
        if let Some(start) = self.start_time {
            let elapsed = start.elapsed();
            if elapsed >= self.duration {
                0
            } else {
                (self.duration - elapsed).as_millis() as u64
            }
        } else {
            0
        }
    }

    pub fn reset(&mut self) {
        self.start_time = Some(Instant::now());
    }

    pub fn stop(&mut self) {
        self.start_time = None;
    }
}

pub async fn sleep_ms(ms: u64) {
    tokio::time::sleep(Duration::from_millis(ms)).await
}

pub async fn timeout_ms<T, F>(ms: u64, future: F) -> Result<T, std::io::Error>
where
    F: std::future::Future<Output = T>,
{
    match tokio::time::timeout(Duration::from_millis(ms), future).await {
        Ok(result) => Ok(result),
        Err(_) => Err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout")),
    }
}

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

    #[tokio::test]
    async fn test_timeout_success() {
        let result = timeout_ms(1000, async { 42 }).await;
        assert_eq!(result, Ok(42));
    }

    #[tokio::test]
    async fn test_timeout_expired() {
        let result = timeout_ms(10, async {
            tokio::time::sleep(Duration::from_millis(100)).await;
            42
        })
        .await;
        assert!(result.is_err());
    }
}