Skip to content

feat(tools): add OpenSandbox sandbox tool#5756

Open
iris-clawd wants to merge 1 commit intomainfrom
feat/open-sandbox-tool
Open

feat(tools): add OpenSandbox sandbox tool#5756
iris-clawd wants to merge 1 commit intomainfrom
feat/open-sandbox-tool

Conversation

@iris-clawd
Copy link
Copy Markdown
Contributor

@iris-clawd iris-clawd commented May 8, 2026

Summary

Adds OpenSandboxExecTool and OpenSandboxFileTool to crewai-tools, following the exact same pattern as the Daytona sandbox tool.

What's new

  • crewai_tools.tools.open_sandbox_tool package with three classes:
    • OpenSandboxBaseTool — base class with lazy SDK import, ConnectionConfigSync client, SandboxSync lifecycle (create/connect/delete), atexit cleanup for persistent sandboxes, threading lock for thread safety
    • OpenSandboxExecTool — runs shell commands, returns {exit_code, result, artifacts}
    • OpenSandboxFileTool — read/write/append/list/delete/mkdir/info file actions

Optional dependency

opensandbox is declared as an optional dep in pyproject.toml:

[project.optional-dependencies]
opensandbox = ["opensandbox>=0.1.8"]

Install with: pip install crewai-tools[opensandbox]

The module is fully importable without opensandbox installed — the ImportError only fires when a tool method is actually called, matching the Daytona pattern.

Env vars

Var Purpose
OPEN_SANDBOX_API_KEY API key (optional depending on server config)
OPEN_SANDBOX_DOMAIN Server host:port (e.g. localhost:8080)

Pattern reference

Modelled directly on daytona_sandbox_tool — same base class structure, same _acquire_sandbox / _release_sandbox lifecycle, same lazy import guard, same persistent / sandbox_id / sandbox_timeout fields.

Testing

  • Import smoke test passes without opensandbox installed
  • ruff check + ruff format clean

Requested by: Lorenze Jay lorenze@crewai.com

Summary by CodeRabbit

  • New Features
    • Added OpenSandbox integration with three new tools: execute shell commands in sandbox environments, perform file system operations (read, write, delete, list directories, manage permissions), and manage sandbox lifecycle with multiple configuration modes
    • Added optional opensandbox dependency for enhanced sandbox capabilities

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces three new OpenSandbox-backed tools for the crewai-tools library: a base class managing sandbox lifecycle (ephemeral, persistent, or external), an execution tool for running shell commands, and a filesystem tool supporting read/write/append/list/delete/mkdir/info operations. The opensandbox SDK is added as an optional dependency, and all tools are exported through the package hierarchy.

Changes

OpenSandbox Tool Integration

Layer / File(s) Summary
Dependencies
lib/crewai-tools/pyproject.toml
Adds opensandbox>=0.1.8 as an optional dependency group.
Base Tool & SDK Integration
lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_base_tool.py
OpenSandboxBaseTool class with lazy SDK import, connection config caching, and three sandbox lifecycle modes: ephemeral (per-call), persistent (cached with atexit hook), and external (by sandbox_id, never killed). Includes thread-safe cleanup and timedelta conversion utility.
Command Execution Tool
lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py
OpenSandboxExecTool executes shell commands with optional cwd, env, and timeout; returns exit code, combined output, and artifacts; always releases sandbox in finally block.
Filesystem Operations Tool
lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py
OpenSandboxFileTool dispatches read/write/append/list/delete/mkdir/info actions; auto-creates parent directories; handles binary content via base64; normalizes metadata with ISO datetime formatting.
Package Exports
lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/__init__.py, lib/crewai-tools/src/crewai_tools/tools/__init__.py, lib/crewai-tools/src/crewai_tools/__init__.py
Registers all three tool classes in __all__ at package hierarchy levels for public import access.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • greysonlalonde

Poem

🐰 Sandboxes bloom where code runs free,
Three tools now dance in harmony—
Base, Exec, and File in sync,
All packed tight in one neat link! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(tools): add OpenSandbox sandbox tool' clearly and directly describes the main change: adding a new OpenSandbox sandbox tool to the tools module.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/open-sandbox-tool

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py (2)

146-162: 💤 Low value

Append is non-atomic (read-modify-write).

The append operation reads the entire file, concatenates, and writes back. For large files this is memory-intensive, and concurrent appends could race. This is acceptable for the documented use case (chunked file uploads), but worth noting in the docstring.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`
around lines 146 - 162, The _append method performs a read-modify-write
(sandbox.files.read_bytes -> concat -> sandbox.files.write_file) which is
non-atomic and can be memory-intensive or race-prone for large files or
concurrent appends; update the method's docstring (near _append in
open_sandbox_file_tool.py) to explicitly state this limitation and recommend its
intended usage (chunked uploads) and/or mention that callers should handle
concurrency or use a more atomic mechanism if needed, referencing
_ensure_parent_dir, sandbox.files.read_bytes, and sandbox.files.write_file so
readers can locate the related operations.

4-4: 💤 Low value

Same unnecessary builtins import as in exec_tool.

See comment on open_sandbox_exec_tool.py — this import can be removed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`
at line 4, Remove the unnecessary import "from builtins import type as type_"
from open_sandbox_file_tool.py; if the module references the alias type_ (search
for "type_"), replace those uses with the built-in type or appropriate
isinstance/type calls (e.g., use type(...) or isinstance(..., type)) and delete
the import line entirely.
lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py (1)

3-3: 💤 Low value

Unnecessary import from builtins.

from builtins import type as type_ is redundant — type is already a builtin available in all Python versions. For the type annotation on line 39, you can use type[BaseModel] directly (Python 3.9+).

♻️ Suggested simplification
-from builtins import type as type_

Then on line 39:

-    args_schema: type_[BaseModel] = OpenSandboxExecToolSchema
+    args_schema: type[BaseModel] = OpenSandboxExecToolSchema
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py`
at line 3, Remove the unnecessary import "from builtins import type as type_"
and update any usage of the alias type_ (e.g., the type annotation currently
written using type_) to the builtin typing form type[BaseModel] (or simply
type[YourModelClass]) as applicable—specifically replace the annotation that
uses type_ on the function/method where BaseModel is referenced with the native
Python 3.9+ generic builtin type and delete the imported symbol type_ from the
module.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`:
- Around line 55-58: The default Unix permission for the Pydantic Field named
mode is currently set as decimal 755; change the default to octal 0o755 and
update every usage that constructs or applies permissions from this field (e.g.,
the code path handling action="mkdir" and any os.chmod/os.makedirs or
permission-bit computations that consume mode) so they treat mode as an octal
permission integer; ensure the Field declaration remains mode: int =
Field(default=0o755, ...) and adjust any tests or comparisons that assumed
decimal 755 accordingly.

---

Nitpick comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py`:
- Line 3: Remove the unnecessary import "from builtins import type as type_" and
update any usage of the alias type_ (e.g., the type annotation currently written
using type_) to the builtin typing form type[BaseModel] (or simply
type[YourModelClass]) as applicable—specifically replace the annotation that
uses type_ on the function/method where BaseModel is referenced with the native
Python 3.9+ generic builtin type and delete the imported symbol type_ from the
module.

In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`:
- Around line 146-162: The _append method performs a read-modify-write
(sandbox.files.read_bytes -> concat -> sandbox.files.write_file) which is
non-atomic and can be memory-intensive or race-prone for large files or
concurrent appends; update the method's docstring (near _append in
open_sandbox_file_tool.py) to explicitly state this limitation and recommend its
intended usage (chunked uploads) and/or mention that callers should handle
concurrency or use a more atomic mechanism if needed, referencing
_ensure_parent_dir, sandbox.files.read_bytes, and sandbox.files.write_file so
readers can locate the related operations.
- Line 4: Remove the unnecessary import "from builtins import type as type_"
from open_sandbox_file_tool.py; if the module references the alias type_ (search
for "type_"), replace those uses with the built-in type or appropriate
isinstance/type calls (e.g., use type(...) or isinstance(..., type)) and delete
the import line entirely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 111d41e9-4ba2-49e0-a795-f8c9c1ff74bd

📥 Commits

Reviewing files that changed from the base of the PR and between e4a91cd and 9c981e1.

📒 Files selected for processing (7)
  • lib/crewai-tools/pyproject.toml
  • lib/crewai-tools/src/crewai_tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_base_tool.py
  • lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.py
  • lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py

Comment on lines +55 to +58
mode: int = Field(
default=755,
description="For action='mkdir': Unix file mode as an integer (default 755).",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

opensandbox python SDK WriteEntry mode parameter documentation

💡 Result:

In the OpenSandbox Python SDK, WriteEntry.mode is the Unix file permission bits (octal integer). It’s passed as an integer like 644 or 755 when creating a WriteEntry for sandbox.files.write_files. [1][2] Documented usage (examples): - mode=644 creates a normal data file with rw-r--r-- permissions. [1] - mode=755 creates an executable script with rwxr-xr-x permissions. [1] - Common recommended values include 600 (rw-------) for sensitive files. [1] Permission meanings (octal): - 755 => rwxr-xr-x (owner read/write/execute; group/others read/execute) [3] - 644 => rw-r--r-- (owner read/write; group/others read only) [3] - 600 => rw------- (only owner read/write) [3] Therefore, WriteEntry.mode expects a numeric octal Unix mode/permission value (e.g., 644, 755, 600), and it controls the file’s permission bits inside the sandbox. [1][3]

Citations:


Fix Unix file mode to use octal notation.

mode: int = 755 is the decimal value 755, not octal. Unix file permissions should be specified in octal: 0o755 (octal) = 493 (decimal) = rwxr-xr-x permissions. The SDK interprets mode as octal Unix permission bits; passing 755 decimal would set permissions to 0o1363 instead of 0o755, resulting in incorrect file permissions.

Update the default value and all usages:

Suggested fixes
     mode: int = Field(
-        default=755,
+        default=0o755,
         description="For action='mkdir': Unix file mode as an integer (default 755).",
     )

And update line 96 and line 175:

-        mode: int = 755,
+        mode: int = 0o755,
-            sandbox.files.create_directories([sdk["WriteEntry"](path=parent, mode=755)])
+            sandbox.files.create_directories([sdk["WriteEntry"](path=parent, mode=0o755)])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mode: int = Field(
default=755,
description="For action='mkdir': Unix file mode as an integer (default 755).",
)
mode: int = Field(
default=0o755,
description="For action='mkdir': Unix file mode as an integer (default 755).",
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py`
around lines 55 - 58, The default Unix permission for the Pydantic Field named
mode is currently set as decimal 755; change the default to octal 0o755 and
update every usage that constructs or applies permissions from this field (e.g.,
the code path handling action="mkdir" and any os.chmod/os.makedirs or
permission-bit computations that consume mode) so they treat mode as an octal
permission integer; ensure the Field declaration remains mode: int =
Field(default=0o755, ...) and adjust any tests or comparisons that assumed
decimal 755 accordingly.

@iris-clawd
Copy link
Copy Markdown
Contributor Author

requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out?

4 similar comments
@iris-clawd
Copy link
Copy Markdown
Contributor Author

requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out?

@iris-clawd
Copy link
Copy Markdown
Contributor Author

requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out?

@iris-clawd
Copy link
Copy Markdown
Contributor Author

requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out?

@iris-clawd
Copy link
Copy Markdown
Contributor Author

requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out?

@iris-clawd
Copy link
Copy Markdown
Contributor Author

requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant