tiny-agent 0.1.0

一个小而完整的 Rust LLM Agent 运行时:可中断、可恢复、可观测、可插拔的 agent loop / A small but complete LLM agent runtime in Rust — an interruptible, resumable, observable, pluggable agent loop.
Documentation
use super::{ExecOutput, Sandbox, SandboxError};
use async_trait::async_trait;
use std::process::Stdio;
use std::sync::Arc;
use tokio::process::Command;

/// 直接在宿主机上执行命令、读写真实文件系统的 sandbox。
///
/// 这是默认实现,行为等价于"不沙箱",不提供任何隔离。
#[derive(Default)]
pub struct HostSandbox;

impl HostSandbox {
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl Sandbox for HostSandbox {
    async fn open(self: Arc<Self>, _session_id: &str) -> Result<Arc<dyn Sandbox>, SandboxError> {
        Ok(self)
    }

    async fn execute(&self, command: &str) -> Result<ExecOutput, SandboxError> {
        let output = Command::new("bash")
            .arg("-c")
            .arg(command)
            .stdin(Stdio::null())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .output()
            .await?;
        Ok(ExecOutput {
            stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
            stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
            exit_code: output.status.code(),
        })
    }
}

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

    #[tokio::test]
    async fn execute_captures_stdout_and_exit_code() {
        let sb = HostSandbox::new();
        let out = sb.execute("echo hello").await.unwrap();
        assert_eq!(out.stdout, "hello\n");
        assert!(out.is_success());
    }

    #[tokio::test]
    async fn execute_reports_nonzero_exit() {
        let sb = HostSandbox::new();
        let out = sb.execute("exit 3").await.unwrap();
        assert_eq!(out.exit_code, Some(3));
        assert!(!out.is_success());
    }
}