Skip to main content

tiny_agent/sandbox/
mod.rs

1mod host;
2
3pub use async_trait::async_trait;
4pub use host::HostSandbox;
5use std::{fmt, sync::Arc};
6
7/// 一次命令执行的结果。
8#[derive(Debug, Clone)]
9pub struct ExecOutput {
10    pub stdout: String,
11    pub stderr: String,
12    pub exit_code: Option<i32>,
13}
14
15impl ExecOutput {
16    /// 退出码为 0 视为成功。
17    pub fn is_success(&self) -> bool {
18        self.exit_code == Some(0)
19    }
20}
21
22/// sandbox 操作可能产生的错误。
23#[derive(Debug)]
24pub enum SandboxError {
25    /// 底层 IO / 进程错误。
26    Io(String),
27    /// 该 sandbox 不支持此能力(例如不支持快照)。
28    Unsupported,
29    /// 其他执行错误。
30    Other(String),
31}
32
33impl fmt::Display for SandboxError {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            SandboxError::Io(e) => write!(f, "sandbox io error: {e}"),
37            SandboxError::Unsupported => {
38                write!(f, "operation not supported by this sandbox")
39            }
40            SandboxError::Other(e) => write!(f, "sandbox error: {e}"),
41        }
42    }
43}
44
45impl std::error::Error for SandboxError {}
46
47impl From<std::io::Error> for SandboxError {
48    fn from(e: std::io::Error) -> Self {
49        SandboxError::Io(e.to_string())
50    }
51}
52
53/// agent 工具的执行环境抽象。
54///
55/// 所有会触达"外部世界"(进程、文件系统)的工具都应通过 [`Sandbox::execute`]
56/// 走这一层,这样替换 sandbox 实现(真机 / bashkit / 容器)时工具代码无需改动。
57///
58/// agent 的执行环境抽象。
59///
60/// 一个 `Sandbox` 实例绑定一个会话:[`execute`](Sandbox::execute) 负责执行,
61/// [`save`](Sandbox::save) 持久化自身状态,[`fork`](Sandbox::fork) 把自身复制给另一个会话。
62///
63/// **框架完全不规定状态怎么序列化**:可能是一坨字节、一个容器 image id、一个远程
64/// 快照引用……都藏在实现内部,trait 不暴露任何序列化格式。不需要持久化的后端
65/// (如 [`HostSandbox`])用默认实现即可。
66///
67/// [`Sandbox::open`] 是按 session 打开 / 恢复 / 初始化执行环境的入口。
68/// 对无会话状态的实现(如 [`HostSandbox`]),可直接返回自己;对需要快照 / 挂载 /
69/// bootstrap 的实现,则在这里按 `session_id` 恢复出真正的 session-bound sandbox。
70#[async_trait]
71pub trait Sandbox: Send + Sync {
72    /// 打开或恢复某个 session 对应的 sandbox。实现应保持幂等:同一 session 多次 open
73    /// 应恢复到同一份状态,并且 bootstrap 逻辑不应重复破坏环境。
74    async fn open(self: Arc<Self>, session_id: &str) -> Result<Arc<dyn Sandbox>, SandboxError>;
75
76    /// 执行一条命令并返回结果。
77    ///
78    /// 文件读写等操作由各工具自行拼成命令(如 `cat`、`printf`),本层只负责执行。
79    async fn execute(&self, command: &str) -> Result<ExecOutput, SandboxError>;
80
81    /// 持久化自身当前状态。实例自己知道 session_id 和存到哪里,故无参。
82    /// 默认不做事(真机文件系统本来就持久)。
83    async fn save(&self) -> Result<(), SandboxError> {
84        Ok(())
85    }
86
87    /// 把自身当前状态复制给另一个会话 `to_session`。默认不支持。
88    async fn fork(&self, _to_session: &str) -> Result<(), SandboxError> {
89        Err(SandboxError::Unsupported)
90    }
91}