feat(tools): add OpenSandbox sandbox tool#5756
Conversation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds an optional opensandbox dependency and three new OpenSandbox-backed tools: OpenSandboxBaseTool (SDK lifecycle/connection), OpenSandboxExecTool (run shell commands), and OpenSandboxFileTool (filesystem actions). Updates tool specs JSON and exposes the tools via package-level all across module hierarchy. ChangesOpenSandbox Tool Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
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 valueAppend 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 valueSame unnecessary
builtinsimport 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 valueUnnecessary import from
builtins.
from builtins import type as type_is redundant —typeis already a builtin available in all Python versions. For the type annotation on line 39, you can usetype[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
📒 Files selected for processing (7)
lib/crewai-tools/pyproject.tomllib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_base_tool.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_exec_tool.pylib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/open_sandbox_file_tool.py
| mode: int = Field( | ||
| default=755, | ||
| description="For action='mkdir': Unix file mode as an integer (default 755).", | ||
| ) |
There was a problem hiding this comment.
🧩 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:
- 1: https://mintlify.com/alibaba/OpenSandbox/examples/file-operations
- 2: https://github.com/alibaba/OpenSandbox/blob/main/sdks/sandbox/python/README.md
- 3: https://www.mintlify.com/alibaba/OpenSandbox/api/execution/filesystem
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.
| 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.
|
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
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
5 similar comments
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/tool.specs.json`:
- Around line 7429-7493: The schema currently makes path, paths, pattern,
replacement nullable and only requires action, so add JSON Schema conditional
validation blocks (if/then) keyed on the "action" property to enforce
action-specific required properties and types: e.g. add an if:
{properties:{action:{const:"move"}}} then: {required:["path","destination"],
properties:{path:{type:"string"}, destination:{type:"string"}}}; for "find"
require path and pattern (pattern as string); for "search" require path and
pattern (pattern as string); for "replace" require paths (array of strings) and
replacement (string) and pattern (string); for "delete" require path and
recursive (boolean optional) as needed; ensure each then block also tightens
types (no null) for the referenced properties and use else/elseif blocks for
other actions to reject missing/nullable fields so invalid payloads (e.g.
{"action":"move","path":"/tmp/a"} or {"action":"find"}) fail schema validation
up front.
- Around line 16636-16640: OpenSandboxFileTool's schema currently defines "mode"
as a decimal integer (default 755); change its schema to use type "string",
description "Octal permission string", and default null to match the
DaytonaFileTool pattern, and then update the Python implementation of
OpenSandboxFileTool where WriteEntry(mode=mode) is called to accept a string
mode (or null) and convert it to an int with base-8 (e.g., int(mode, 8)) before
passing to WriteEntry, handling null/None by omitting the mode or passing None;
ensure the conversion and null-check occur inside the OpenSandboxFileTool method
that constructs the WriteEntry call.
🪄 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: 71e30e3f-97fe-4af1-b9b6-4ddd919aad17
📒 Files selected for processing (4)
lib/crewai-tools/pyproject.tomllib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/tool.specs.json
✅ Files skipped from review due to trivial changes (1)
- lib/crewai-tools/pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (2)
- lib/crewai-tools/src/crewai_tools/tools/init.py
- lib/crewai-tools/src/crewai_tools/init.py
| "default": null, | ||
| "description": "Optional kwargs forwarded to SandboxSync.create when creating a sandbox (e.g. image, env, resource, metadata, entrypoint).", | ||
| "title": "Create Params" | ||
| }, | ||
| "domain": { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -p 'open_sandbox_file_tool.py' | while read -r file; do
echo "== $file =="
rg -n -C3 '\bmode\b|mkdir|chmod|os\.makedirs|Path\.mkdir' "$file"
doneRepository: crewAIInc/crewAI
Length of output: 3458
🏁 Script executed:
# Find and examine the current specs.json file
fd -p 'tool\.specs\.json' | head -5Repository: crewAIInc/crewAI
Length of output: 93
🏁 Script executed:
# Look at the specific lines mentioned in the review (16636-16640)
fd -p 'tool\.specs\.json' | while read -r file; do
echo "=== File: $file ==="
wc -l "$file"
echo "=== Lines around 16636-16640 ==="
sed -n '16630,16650p' "$file"
doneRepository: crewAIInc/crewAI
Length of output: 896
🏁 Script executed:
# Search for "mode" definition in the specs.json file
fd -p 'tool\.specs\.json' | while read -r file; do
echo "=== Searching for mode in $file ==="
rg -n '"mode"' "$file" | head -20
doneRepository: crewAIInc/crewAI
Length of output: 200
🏁 Script executed:
# Look at all three mode definitions in the specs.json
fd -p 'tool\.specs\.json' | while read -r file; do
echo "=== Mode at line 7403 ==="
sed -n '7400,7415p' "$file"
echo ""
echo "=== Mode at line 16736 ==="
sed -n '16733,16748p' "$file"
echo ""
echo "=== Mode at line 24503 ==="
sed -n '24500,24515p' "$file"
doneRepository: crewAIInc/crewAI
Length of output: 2129
🏁 Script executed:
# Search for which tool has the mode field near line 16736
fd -p 'tool\.specs\.json' | while read -r file; do
echo "=== Context around line 16736 to identify tool ==="
sed -n '16700,16760p' "$file" | head -40
doneRepository: crewAIInc/crewAI
Length of output: 1848
🏁 Script executed:
# Identify which tools these mode definitions belong to
fd -p 'tool\.specs\.json' | while read -r file; do
echo "=== Tool at line 7403 ==="
sed -n '7300,7410p' "$file" | grep -E '"(name|title|properties)"|"mode"' | head -10
echo ""
echo "=== Tool at line 16736 ==="
sed -n '16650,16750p' "$file" | grep -E '"(name|title|properties)"|"mode"' | head -10
doneRepository: crewAIInc/crewAI
Length of output: 773
🏁 Script executed:
# Look at the actual tool definitions to identify them clearly
fd -p 'tool\.specs\.json' | while read -r file; do
echo "=== Searching for tool definitions around line 7403 ==="
sed -n '7100,7150p' "$file" | grep -E '"(name|title|description)"'
echo ""
echo "=== Searching for tool definitions around line 16736 ==="
sed -n '16550,16600p' "$file" | grep -E '"(name|title|description)"'
doneRepository: crewAIInc/crewAI
Length of output: 1613
🏁 Script executed:
# Search for which SDK or lib the code uses for WriteEntry to understand mode handling
fd -p '\.py$' lib/crewai-tools/src/crewai_tools/ | xargs rg -l 'WriteEntry' | head -5Repository: crewAIInc/crewAI
Length of output: 228
🏁 Script executed:
# Look at the open_sandbox_base_tool.py to see how WriteEntry is used
fd -p 'open_sandbox_base_tool.py' | while read -r file; do
rg -n -B2 -A5 'WriteEntry|mode' "$file" | head -40
doneRepository: crewAIInc/crewAI
Length of output: 1659
🏁 Script executed:
# Search for SDK or library that provides WriteEntry
rg -n 'WriteEntry' lib/crewai-tools/src/ --type py | head -20Repository: crewAIInc/crewAI
Length of output: 701
🏁 Script executed:
# Look for import of WriteEntry or opensandbox SDK
fd -p '\.py$' lib/crewai-tools/src/crewai_tools/tools/open_sandbox_tool/ | xargs rg -n 'import|from.*opensandbox|WriteEntry'Repository: crewAIInc/crewAI
Length of output: 5213
Fix the mode field in OpenSandboxFileTool schema to use octal strings instead of decimal integer.
The current schema at lines 16736-16748 defines mode as a decimal integer with default 755, but the Python implementation passes this value directly to WriteEntry(mode=mode) without conversion. This causes a mismatch: callers expect Unix permissions (where 755 is octal, equivalent to 0o755 = 493 in decimal), but the integer 755 is interpreted as decimal, resulting in invalid permission bits.
The fix should follow the pattern already established in DaytonaFileTool (line 7403), which correctly uses an octal string type with description "Octal permission string" and default null. Update OpenSandboxFileTool to match:
Suggested spec fix
"mode": {
- "default": 755,
- "description": "For action='mkdir': Unix file mode as an integer (default 755).",
- "title": "Mode",
- "type": "integer"
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "For action='mkdir': octal permission string (for example '0755').",
+ "title": "Mode"
},Also update the Python implementation to accept string mode and convert it before passing to the SDK.
🤖 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/tool.specs.json` around lines 16636 - 16640,
OpenSandboxFileTool's schema currently defines "mode" as a decimal integer
(default 755); change its schema to use type "string", description "Octal
permission string", and default null to match the DaytonaFileTool pattern, and
then update the Python implementation of OpenSandboxFileTool where
WriteEntry(mode=mode) is called to accept a string mode (or null) and convert it
to an int with base-8 (e.g., int(mode, 8)) before passing to WriteEntry,
handling null/None by omitting the mode or passing None; ensure the conversion
and null-check occur inside the OpenSandboxFileTool method that constructs the
WriteEntry call.
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
3 similar comments
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
3 similar comments
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
6 similar comments
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
Summary
Adds
OpenSandboxExecToolandOpenSandboxFileTooltocrewai-tools, following the exact same pattern as the Daytona sandbox tool.What's new
crewai_tools.tools.open_sandbox_toolpackage with three classes:OpenSandboxBaseTool— base class with lazy SDK import,ConnectionConfigSyncclient,SandboxSynclifecycle (create/connect/delete), atexit cleanup for persistent sandboxes, threading lock for thread safetyOpenSandboxExecTool— runs shell commands, returns{exit_code, result, artifacts}OpenSandboxFileTool— read/write/append/list/delete/mkdir/info file actionsOptional dependency
opensandboxis declared as an optional dep inpyproject.toml:Install with:
pip install crewai-tools[opensandbox]The module is fully importable without
opensandboxinstalled — theImportErroronly fires when a tool method is actually called, matching the Daytona pattern.Env vars
OPEN_SANDBOX_API_KEYOPEN_SANDBOX_DOMAINlocalhost:8080)Pattern reference
Modelled directly on
daytona_sandbox_tool— same base class structure, same_acquire_sandbox/_release_sandboxlifecycle, same lazy import guard, samepersistent/sandbox_id/sandbox_timeoutfields.Testing
opensandboxinstalledruff check+ruff formatcleanSummary by CodeRabbit
New Features
Chores