Skip to main content

everruns_integrations_e2b/
lib.rs

1//! E2B cloud sandboxes for Everruns agents.
2//!
3//! `everruns-integrations-e2b` is part of the [Everruns](https://everruns.com)
4//! ecosystem. It gives agents cloud sandboxes backed by [E2B](https://e2b.dev),
5//! so they can create isolated environments, run processes, and read or write
6//! files without touching the host. Sandboxes are managed per session with
7//! leased-resource cleanup, and authenticate with a user-supplied E2B API key.
8//!
9//! # Example
10//!
11//! ```
12//! use everruns_core::capabilities::Capability;
13//! use everruns_integrations_e2b::E2BCapability;
14//!
15//! let capability = E2BCapability;
16//! assert_eq!(capability.id(), "e2b");
17//! ```
18//!
19//! # Design notes
20//!
21//! - External integration crate, auto-registered via the inventory plugin system.
22//! - Bring-your-own API-key connection model: credentials resolve from the user
23//!   connection only and fail with `ConnectionRequired` if not configured.
24//! - Session-scoped sandbox state with leased-resource cleanup, similar to
25//!   Daytona.
26
27pub mod client;
28pub mod connection;
29pub mod state;
30mod tools;
31
32use everruns_core::LEASED_RESOURCES_FEATURE;
33use everruns_core::capabilities::{
34    Capability, CapabilityLocalization, CapabilityStatus, IntegrationPlugin, RiskLevel,
35};
36use everruns_core::connector::ConnectorPlugin;
37use everruns_core::tools::Tool;
38
39use std::sync::LazyLock;
40
41use connection::E2BConnector;
42use tools::{
43    E2BCreateSandboxTool, E2BExecTool, E2BListSandboxesTool, E2BManageSandboxTool, E2BReadFileTool,
44    E2BWriteFileTool,
45};
46
47inventory::submit! {
48    IntegrationPlugin {
49        experimental_only: false,
50        feature_flag: None,
51        factory: || Box::new(E2BCapability),
52    }
53}
54
55inventory::submit! {
56    ConnectorPlugin {
57        experimental_only: false,
58        factory: || Box::new(E2BConnector),
59    }
60}
61
62pub const E2B_API_BASE: &str = "https://api.e2b.app";
63pub const E2B_SANDBOX_SECRET_PREFIX: &str = "e2b_sandbox:";
64pub const E2B_DEFAULT_TEMPLATE: &str = "base";
65pub const E2B_DEFAULT_TIMEOUT_SECS: u64 = 60 * 60;
66pub const E2B_DEFAULT_WORKSPACE_PATH: &str = "/home/user";
67pub const E2B_ENVD_PORT: u16 = 49_983;
68
69static SYSTEM_PROMPT: LazyLock<String> = LazyLock::new(|| {
70    let mut prompt = String::from(
71        "E2B sandboxes are isolated networked Linux environments. Create or select a sandbox before sandbox-scoped operations, prefer `/home/user` as workspace root, and pause or delete sandboxes when done.",
72    );
73    prompt.push_str(everruns_core::tool_output_sanitizer::EXEC_OUTPUT_HINT);
74    prompt
75});
76
77pub struct E2BCapability;
78
79impl Capability for E2BCapability {
80    fn id(&self) -> &str {
81        "e2b"
82    }
83
84    fn name(&self) -> &str {
85        "E2B"
86    }
87
88    fn description(&self) -> &str {
89        "Run code in cloud sandboxes powered by E2B. Create isolated Linux environments, execute commands, and manage sandbox files."
90    }
91
92    fn localizations(&self) -> Vec<CapabilityLocalization> {
93        vec![CapabilityLocalization::text(
94            "uk",
95            "E2B",
96            "Запускайте код у хмарних пісочницях на основі E2B. Створюйте ізольовані \
97             середовища Linux, виконуйте команди та керуйте файлами пісочниці.",
98        )]
99    }
100
101    fn status(&self) -> CapabilityStatus {
102        CapabilityStatus::Available
103    }
104
105    fn risk_level(&self) -> RiskLevel {
106        RiskLevel::High
107    }
108
109    fn icon(&self) -> Option<&str> {
110        Some("cloud")
111    }
112
113    fn category(&self) -> Option<&str> {
114        Some("Sandboxes")
115    }
116
117    fn system_prompt_addition(&self) -> Option<&str> {
118        Some(&SYSTEM_PROMPT)
119    }
120
121    fn tools(&self) -> Vec<Box<dyn Tool>> {
122        vec![
123            Box::new(E2BCreateSandboxTool),
124            Box::new(E2BExecTool),
125            Box::new(E2BReadFileTool),
126            Box::new(E2BWriteFileTool),
127            Box::new(E2BListSandboxesTool),
128            Box::new(E2BManageSandboxTool),
129        ]
130    }
131
132    fn dependencies(&self) -> Vec<&'static str> {
133        vec!["session_storage"]
134    }
135
136    fn features(&self) -> Vec<&'static str> {
137        vec![LEASED_RESOURCES_FEATURE]
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn capability_metadata() {
147        let cap = E2BCapability;
148        assert_eq!(cap.id(), "e2b");
149        assert_eq!(cap.name(), "E2B");
150        assert_eq!(cap.icon(), Some("cloud"));
151        assert_eq!(cap.category(), Some("Sandboxes"));
152        assert_eq!(cap.dependencies(), vec!["session_storage"]);
153    }
154
155    #[test]
156    fn capability_tools() {
157        let cap = E2BCapability;
158        let names: Vec<_> = cap
159            .tools()
160            .into_iter()
161            .map(|tool| tool.name().to_string())
162            .collect();
163        assert!(names.contains(&"e2b_create_sandbox".to_string()));
164        assert!(names.contains(&"e2b_exec".to_string()));
165        assert!(names.contains(&"e2b_read_file".to_string()));
166        assert!(names.contains(&"e2b_write_file".to_string()));
167        assert!(names.contains(&"e2b_list_sandboxes".to_string()));
168        assert!(names.contains(&"e2b_manage_sandbox".to_string()));
169    }
170
171    #[tokio::test]
172    async fn system_prompt_within_budget() {
173        let cap = E2BCapability;
174        let ctx = everruns_core::capabilities::SystemPromptContext::without_file_store(
175            everruns_core::SessionId::new(),
176        );
177        let prompt = cap.system_prompt_contribution(&ctx).await.unwrap();
178        // Bumped 1000 → 1300: EVE-778 grew the shared EXEC_OUTPUT_HINT with the
179        // single-read/contextual-search policy (+438 bytes), taking this
180        // contribution to 1255 bytes.
181        assert!(prompt.len() <= 1300, "prompt is {} bytes", prompt.len());
182    }
183}