agent-workspace-contract 0.5.0

Transport-neutral contracts for Agent Infra workspace APIs
Documentation
use super::*;

/// Narrow file capability for consumers that must not gain command execution.
#[async_trait]
pub trait WorkspaceFiles: Send + Sync + Debug {
    async fn file_read(&self, path: &str) -> Result<String>;
    async fn file_write(&self, path: &str, content: &str) -> Result<()>;
    async fn directory_create_all(&self, path: &str) -> Result<()>;
    async fn file_remove(&self, path: &str) -> Result<()>;
    async fn path_exists(&self, path: &str) -> Result<bool>;
    async fn directory_list(&self, path: &str) -> Result<Vec<DirEntry>>;
}

/// Narrow bounded-search capability.
#[async_trait]
pub trait WorkspaceSearch: Send + Sync + Debug {
    async fn search_walk_tree(&self, path: &str, max_depth: usize) -> Result<Vec<String>>;
    async fn search_find_files(&self, pattern: &str, path: &str) -> Result<Vec<String>>;
    async fn search_grep(
        &self,
        pattern: &str,
        path: &str,
        include: Option<&str>,
    ) -> Result<Vec<GrepMatch>>;
}

/// Narrow command capability. Possession of this port is an explicit privilege.
#[async_trait]
pub trait WorkspaceCommands: Send + Sync + Debug {
    async fn command_exec(&self, command: &str, cwd: Option<&str>) -> Result<CmdOutput>;
}

/// Provider-neutral failure signal consumed by transport resilience policies.
///
/// Keeping the decision input free of `reqwest` (or any other HTTP type) lets
/// deployments replace the default backoff/circuit algorithm without pulling
/// transport implementation into the contract or application layers.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProviderRequestFailure {
    pub status_code: Option<u16>,
    pub transport_failure: bool,
    pub retry_after_ms: Option<u64>,
}

/// Narrow strategy port for provider retry and circuit-opening decisions.
pub trait ProviderResiliencePolicy: Send + Sync + Debug {
    /// Return the delay before the next attempt, or `None` to fail closed.
    /// `completed_attempts` starts at one after the first failed attempt.
    fn retry_delay_ms(
        &self,
        failure: ProviderRequestFailure,
        completed_attempts: u32,
        idempotent: bool,
    ) -> Option<u64>;

    /// Return how long to open the circuit after this many consecutive
    /// retryable request failures, or `None` to keep it closed.
    fn circuit_open_ms(&self, consecutive_failures: u32) -> Option<u64>;
}

#[async_trait]
impl<T: Workspace + ?Sized> WorkspaceFiles for T {
    async fn file_read(&self, path: &str) -> Result<String> {
        Workspace::read_file(self, path).await
    }
    async fn file_write(&self, path: &str, content: &str) -> Result<()> {
        Workspace::write_file(self, path, content).await
    }
    async fn directory_create_all(&self, path: &str) -> Result<()> {
        Workspace::create_dir_all(self, path).await
    }
    async fn file_remove(&self, path: &str) -> Result<()> {
        Workspace::remove_file(self, path).await
    }
    async fn path_exists(&self, path: &str) -> Result<bool> {
        Workspace::exists(self, path).await
    }
    async fn directory_list(&self, path: &str) -> Result<Vec<DirEntry>> {
        Workspace::list_dir(self, path).await
    }
}

#[async_trait]
impl<T: Workspace + ?Sized> WorkspaceSearch for T {
    async fn search_walk_tree(&self, path: &str, max_depth: usize) -> Result<Vec<String>> {
        Workspace::walk_tree(self, path, max_depth).await
    }
    async fn search_find_files(&self, pattern: &str, path: &str) -> Result<Vec<String>> {
        Workspace::find_files(self, pattern, path).await
    }
    async fn search_grep(
        &self,
        pattern: &str,
        path: &str,
        include: Option<&str>,
    ) -> Result<Vec<GrepMatch>> {
        Workspace::grep(self, pattern, path, include).await
    }
}

#[async_trait]
impl<T: Workspace + ?Sized> WorkspaceCommands for T {
    async fn command_exec(&self, command: &str, cwd: Option<&str>) -> Result<CmdOutput> {
        Workspace::exec(self, command, cwd).await
    }
}