magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
//! Run-scoped approval replies. Default deny on cancellation, expiry or disconnect.
use crate::cancellation::AgentCancellation;
use crossbeam_channel::{Sender, bounded};
use std::{
    sync::Mutex,
    time::{Duration, Instant},
};

pub(super) const APPROVAL_TIMEOUT: Duration = Duration::from_secs(60);
struct PendingApproval {
    id: String,
    deadline: Instant,
    reply: Sender<bool>,
}
#[derive(Default)]
pub(super) struct Approvals {
    pending: Mutex<Option<PendingApproval>>,
}
impl Approvals {
    pub(super) fn answer(
        &self,
        id: &str,
        allow: bool,
        cancellation: &AgentCancellation,
    ) -> Result<(), &'static str> {
        let mut pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
        let approval = pending.as_ref().ok_or("stale_approval")?;
        if approval.id != id || Instant::now() >= approval.deadline || cancellation.is_canceled() {
            return Err("stale_approval");
        }
        pending
            .take()
            .expect("checked pending approval")
            .reply
            .try_send(allow)
            .map_err(|_| "stale_approval")
    }
    pub(super) fn request(
        &self,
        cancellation: &AgentCancellation,
        emit: impl FnOnce(&str) -> anyhow::Result<()>,
    ) -> anyhow::Result<bool> {
        self.wait(cancellation, APPROVAL_TIMEOUT, emit)
    }
    fn wait(
        &self,
        cancellation: &AgentCancellation,
        timeout: Duration,
        emit: impl FnOnce(&str) -> anyhow::Result<()>,
    ) -> anyhow::Result<bool> {
        let id = uuid::Uuid::new_v4().to_string();
        let deadline = Instant::now() + timeout;
        let (reply, receiver) = bounded(1);
        {
            let mut pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
            if pending.is_some() || cancellation.is_canceled() {
                return Ok(false);
            }
            *pending = Some(PendingApproval {
                id: id.clone(),
                deadline,
                reply,
            });
        }
        let result = (|| {
            emit(&id)?;
            loop {
                if cancellation.is_canceled() || Instant::now() >= deadline {
                    return Ok(false);
                }
                match receiver.recv_timeout(
                    Duration::from_millis(20)
                        .min(deadline.saturating_duration_since(Instant::now())),
                ) {
                    Ok(allow) => {
                        return Ok(allow
                            && !cancellation.is_canceled()
                            && Instant::now() < deadline);
                    }
                    Err(crossbeam_channel::RecvTimeoutError::Disconnected) => return Ok(false),
                    Err(crossbeam_channel::RecvTimeoutError::Timeout) => {}
                }
            }
        })();
        let mut pending = self.pending.lock().unwrap_or_else(|e| e.into_inner());
        if pending.as_ref().is_some_and(|p| p.id == id) {
            pending.take();
        }
        result
    }
}