Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
ai company
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
ai company
Commits
1331fe8d
Commit
1331fe8d
authored
May 28, 2026
by
Admin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: Route waked agent command and file executions to CubeSandbox microVM
parent
48965032
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
168 additions
and
2 deletions
+168
-2
workspace_tools.py
backend/agent/tools/workspace_tools.py
+91
-2
agents.py
backend/api/routes/agents.py
+18
-0
test_cubesandbox.py
backend/tests/test_cubesandbox.py
+59
-0
No files found.
backend/agent/tools/workspace_tools.py
View file @
1331fe8d
...
...
@@ -4,8 +4,50 @@ import sys
from
typing
import
Optional
from
langchain_core.tools
import
StructuredTool
def
_run_in_sandbox
(
sandbox_id
:
str
,
command
:
str
)
->
dict
:
import
asyncio
import
concurrent.futures
from
services.cubesandbox
import
CubeSandboxService
service
=
CubeSandboxService
()
async
def
_execute
():
return
await
service
.
execute_command
(
sandbox_id
,
command
)
try
:
loop
=
asyncio
.
get_running_loop
()
except
RuntimeError
:
loop
=
None
if
loop
and
loop
.
is_running
():
with
concurrent
.
futures
.
ThreadPoolExecutor
()
as
executor
:
future
=
executor
.
submit
(
asyncio
.
run
,
_execute
())
return
future
.
result
()
else
:
return
asyncio
.
run
(
_execute
())
def
run_command
(
command
:
str
,
cwd
:
Optional
[
str
]
=
None
)
->
str
:
"""Run a shell command in the local workspace. Returns stdout and stderr."""
"""Run a shell command in the local workspace terminal or inside CubeSandbox if configured. Returns stdout and stderr."""
sandbox_id
=
os
.
environ
.
get
(
"CUBE_SANDBOX_ID"
)
if
sandbox_id
:
try
:
if
cwd
:
cwd_cleaned
=
cwd
.
replace
(
"
\\
"
,
"/"
)
command
=
f
"cd '{cwd_cleaned}' && {command}"
res
=
_run_in_sandbox
(
sandbox_id
,
command
)
output
=
[]
if
res
.
get
(
"stdout"
):
output
.
append
(
res
[
"stdout"
])
if
res
.
get
(
"stderr"
):
output
.
append
(
f
"STDERR:
\n
{res['stderr']}"
)
if
res
.
get
(
"exit_code"
,
0
)
!=
0
:
output
.
append
(
f
"Exit Code: {res['exit_code']}"
)
return
"
\n
"
.
join
(
output
)
if
output
else
"Command executed successfully in CubeSandbox with no output."
except
Exception
as
e
:
return
f
"Error executing command in CubeSandbox: {str(e)}"
try
:
# Default cwd to the repository root
if
not
cwd
:
...
...
@@ -46,7 +88,18 @@ def _resolve_path(path: str) -> str:
return
path
def
read_file
(
path
:
str
)
->
str
:
"""Read the contents of a file in the workspace."""
"""Read the contents of a file in the workspace or inside CubeSandbox if configured."""
sandbox_id
=
os
.
environ
.
get
(
"CUBE_SANDBOX_ID"
)
if
sandbox_id
:
try
:
guest_path
=
path
.
replace
(
"
\\
"
,
"/"
)
res
=
_run_in_sandbox
(
sandbox_id
,
f
"cat '{guest_path}'"
)
if
res
.
get
(
"exit_code"
,
0
)
!=
0
:
return
f
"Error: File '{path}' does not exist inside sandbox."
return
res
.
get
(
"stdout"
,
""
)
except
Exception
as
e
:
return
f
"Error reading file from sandbox: {str(e)}"
try
:
path
=
_resolve_path
(
path
)
...
...
@@ -60,6 +113,23 @@ def read_file(path: str) -> str:
def
write_file
(
path
:
str
,
content
:
str
)
->
str
:
"""Create or overwrite a file in the workspace with new content."""
sandbox_id
=
os
.
environ
.
get
(
"CUBE_SANDBOX_ID"
)
if
sandbox_id
:
try
:
import
base64
guest_path
=
path
.
replace
(
"
\\
"
,
"/"
)
dir_name
=
os
.
path
.
dirname
(
guest_path
)
.
replace
(
"
\\
"
,
"/"
)
b64_content
=
base64
.
b64encode
(
content
.
encode
(
"utf-8"
))
.
decode
(
"utf-8"
)
cmd
=
f
"mkdir -p '{dir_name}' && echo '{b64_content}' | base64 -d > '{guest_path}'"
res
=
_run_in_sandbox
(
sandbox_id
,
cmd
)
if
res
.
get
(
"exit_code"
,
0
)
!=
0
:
return
f
"Error writing file inside sandbox: {res.get('stderr', '')}"
return
f
"Successfully wrote {len(content)} characters to {path} inside sandbox microVM"
except
Exception
as
e
:
return
f
"Error writing file inside sandbox: {str(e)}"
try
:
path
=
_resolve_path
(
path
)
...
...
@@ -74,6 +144,25 @@ def write_file(path: str, content: str) -> str:
def
replace_content
(
path
:
str
,
target
:
str
,
replacement
:
str
)
->
str
:
"""Replace a specific unique string in a file with new content."""
sandbox_id
=
os
.
environ
.
get
(
"CUBE_SANDBOX_ID"
)
if
sandbox_id
:
try
:
content
=
read_file
(
path
)
if
content
.
startswith
(
"Error:"
):
return
content
if
target
not
in
content
:
return
f
"Error: Target content to replace was not found in '{path}' inside sandbox."
occurrences
=
content
.
count
(
target
)
if
occurrences
>
1
:
return
f
"Error: Target content occurs {occurrences} times in '{path}' inside sandbox. Must be unique to avoid accidental replacements."
new_content
=
content
.
replace
(
target
,
replacement
)
return
write_file
(
path
,
new_content
)
except
Exception
as
e
:
return
f
"Error replacing content inside sandbox: {str(e)}"
try
:
path
=
_resolve_path
(
path
)
...
...
backend/api/routes/agents.py
View file @
1331fe8d
...
...
@@ -328,6 +328,16 @@ async def run_agent_in_background(
elif
base_url
.
endswith
(
"/v1/"
):
base_url
=
base_url
[:
-
4
]
# Provision CubeSandbox microVM for this agent execution run
try
:
from
services.cubesandbox
import
CubeSandboxService
sandbox_service
=
CubeSandboxService
()
sandbox_instance
=
await
sandbox_service
.
create_instance
(
template_id
=
"sandbox-code-latest"
)
os
.
environ
[
"CUBE_SANDBOX_ID"
]
=
sandbox_instance
[
"id"
]
await
log_writer
.
_write_log
(
"info"
,
f
"Bound CubeSandbox isolated microVM: {sandbox_instance['id']} (<60ms startup)"
)
except
Exception
as
e
:
logging
.
getLogger
(
__name__
)
.
error
(
f
"Failed to spawn CubeSandbox instance: {e}"
,
exc_info
=
True
)
ai_agent
=
AIAgent
(
model_name
=
model_name
,
company_id
=
company_id
,
...
...
@@ -377,6 +387,14 @@ async def run_agent_in_background(
db_run
.
completed_at
=
datetime
.
utcnow
()
db_run
.
error_message
=
str
(
e
)
finally
:
if
"CUBE_SANDBOX_ID"
in
os
.
environ
:
try
:
from
services.cubesandbox
import
CubeSandboxService
sandbox_service
=
CubeSandboxService
()
await
sandbox_service
.
delete_instance
(
os
.
environ
[
"CUBE_SANDBOX_ID"
])
del
os
.
environ
[
"CUBE_SANDBOX_ID"
]
except
Exception
as
e
:
logging
.
getLogger
(
__name__
)
.
error
(
f
"Failed to delete CubeSandbox instance: {e}"
,
exc_info
=
True
)
if
workspace
:
await
WorkspaceManager
.
cleanup
(
company_id
,
run_id
)
...
...
backend/tests/test_cubesandbox.py
View file @
1331fe8d
...
...
@@ -83,3 +83,62 @@ async def test_cubesandbox_endpoints():
finally
:
app
.
dependency_overrides
.
clear
()
@
pytest
.
mark
.
asyncio
async
def
test_workspace_tools_sandbox_routing
():
import
os
from
unittest.mock
import
patch
,
AsyncMock
from
agent.tools.workspace_tools
import
run_command
,
write_file
,
read_file
,
replace_content
# Set CUBE_SANDBOX_ID env var
os
.
environ
[
"CUBE_SANDBOX_ID"
]
=
"test-mock-sandbox-123"
try
:
# Mock CubeSandboxService.execute_command
mock_execute
=
AsyncMock
(
return_value
=
{
"stdout"
:
"mocked output
\n
"
,
"stderr"
:
""
,
"exit_code"
:
0
})
with
patch
(
"services.cubesandbox.CubeSandboxService.execute_command"
,
mock_execute
):
# Test run_command routing
res
=
run_command
(
"echo hello"
)
assert
"mocked output"
in
res
mock_execute
.
assert_called_with
(
"test-mock-sandbox-123"
,
"echo hello"
)
# Test write_file routing
mock_execute
.
reset_mock
()
res
=
write_file
(
"test_dir/file.txt"
,
"hello content"
)
assert
"Successfully wrote"
in
res
# Check mock execute was called with base64 write command
args
=
mock_execute
.
call_args
[
0
]
assert
args
[
0
]
==
"test-mock-sandbox-123"
assert
"base64 -d"
in
args
[
1
]
# Test read_file routing
mock_execute
.
reset_mock
()
mock_execute
.
return_value
=
{
"stdout"
:
"file content inside sandbox"
,
"stderr"
:
""
,
"exit_code"
:
0
}
res
=
read_file
(
"test_dir/file.txt"
)
assert
res
==
"file content inside sandbox"
mock_execute
.
assert_called_with
(
"test-mock-sandbox-123"
,
"cat 'test_dir/file.txt'"
)
# Test replace_content routing
mock_execute
.
reset_mock
()
# 1st call: read (cat), 2nd call: write (base64)
mock_execute
.
side_effect
=
[
{
"stdout"
:
"original line content"
,
"stderr"
:
""
,
"exit_code"
:
0
},
{
"stdout"
:
"done"
,
"stderr"
:
""
,
"exit_code"
:
0
}
]
res
=
replace_content
(
"file.txt"
,
"original"
,
"replaced"
)
assert
"Successfully wrote"
in
res
finally
:
if
"CUBE_SANDBOX_ID"
in
os
.
environ
:
del
os
.
environ
[
"CUBE_SANDBOX_ID"
]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment