Overview
The sandbox tool built into Makers Agent is underpinned by Tencent Cloud's isolated instances. These instances are specifically designed to host the "side effects" generated during Agent execution—such as running commands, reading/writing files, controlling browsers, and executing code. This enables the LLM to truly possess "hands-on" capabilities, rather than being limited to text output.
Two-Tier API
The platform packages the same sandbox instance into two layers of APIs and exposes them on the
context: one layer for the LLM to invoke as tools, and the other for developers to use directly.Perspective | Field | Target User | Granularity |
LLM Perspective | context.tools.* | Tool list for LLM (wrapped as native objects by framework) | Flattened into atomic tools to allow on-demand expansion of the call scope. |
Developer Perspective | context.sandbox.* | Sandbox atomic APIs directly invoked by developer code | Organized by module: commands / files / browser / runCode top-level methods |
The two layers of APIs share the same underlying sandbox instance. The built-in tools in
context.tools that require sandbox capabilities are implemented by directly calling context.sandbox.*, eliminating the need for two separate sets of logic.Instance Lifecycle
Level | Description |
Instance Ownership | One conversation corresponds to one instance, and it is hosted based on the conversation_id dimension. |
Creation Timing | Lazy loading — invoked by the adaptation layer when the sandbox tool is accessed for the first time. |
Cross-Request Reuse | If the sandbox instance is not terminated, sandbox tool requests with the same conversation_id are routed to the same instance. |
Lifecycle Upper Limit | Controlled by the sandbox.timeout in edgeone.json, and automatically recycled when the time point is reached. |
Isolation | Sandbox instances of different conversations are physically isolated. |
Instance Control
The method is mounted at the top level of
context.sandbox to manage the lifecycle of the sandbox instance itself and its external exposure.Methodology | Purpose |
getHost(port) | Obtain the external access address of the sandbox instance. |
envdAccessToken | Obtain the data plane access token. |
getInfo() | Obtain current sandbox information ( instanceId, expiresAt, and so on) |
extendTimeout(seconds) | Extend the lifetime of the current sandbox instance; returns { instanceId, expiresAt, message? }. |
kill() | Terminate the sandbox instance. |
getHost(port) / get_host(port)
Obtain the externally accessible address of a specific port on the sandbox instance. This address is used to expose the server started within the sandbox to browsers or external callers.
Parameter
Parameter | Type | Required | Description |
port | number / int | Yes | The port number that listens within the sandbox |
Return Value
An external address in the format
https://<port>-<instance>.sandbox.example.com.TS Example:
// Start a vite dev server in the sandbox, then return its access address to the frontend.await context.sandbox.commands.run('nohup npx vite --port 5173 &', { timeout: 10 })const previewUrl = context.sandbox.getHost(5173)return Response.json({ previewUrl })
Python example:
await ctx.sandbox.commands.run('nohup python -m http.server 8000 &', timeout=10)preview_url = ctx.sandbox.get_host(8000)return {'preview_url': preview_url}
envdAccessToken / envd_access_token
Obtain the access token for the sandbox instance's data plane.
Return Value
envd token
TS Example:
const token = context.sandbox.envdAccessToken
Python example:
token = ctx.sandbox.envd_access_token
getInfo() / get_info()
Obtain the sandbox metadata cached for the current session, such as the sandbox instance ID and expiration time.
Return Value
SandboxInfointerface SandboxInfo {instanceId: stringsandboxToken: stringsandboxDomain?: stringenvdVersion?: stringexpiresAt: string // An ISO time string returned by the backend acquire/update operation.}
@dataclassclass SandboxInfo:instance_id: strsandbox_token: strexpires_at: strsandbox_domain: Optional[str] = Noneenvd_version: Optional[str] = None
TS Example:
const info = context.sandbox.getInfo()console.log(info.instanceId, info.expiresAt)const remainingMs = new Date(info.expiresAt).getTime() - Date.now()if (remainingMs < 60_000) {await context.sandbox.extendTimeout(600)}
Python Example:
info = ctx.sandbox.get_info()print(info.instance_id, info.expires_at)
extendTimeout(seconds) / extend_timeout(seconds)
Initiate a renewal request for the current sandbox instance. The actual effective duration is determined by the backend's response. If the number of seconds you request exceeds the remaining renewable window allowed by the backend, the backend truncates the request and informs you of the actual renewal time. The SDK then synchronously updates the local cache using the returned
expiresAt / expires_at.Parameter
Parameter | Type | Required | Description |
seconds | number / int | Yes | The renewal duration in seconds, which must be a positive integer. |
Return Value
UpdateResponse
interface UpdateResponse {instanceId: stringexpiresAt: string // The actual new expiration time confirmed by the backend.message?: string // Returned only when the renewal is truncated by the backend, for example, "extended to max lifetime".}
Python:
dict, with fields instance_id / expires_at / message? (snake_case).TS Example
await context.sandbox.commands.run('echo init', { timeout: 300 })const renewed = await context.sandbox.extendTimeout(600)console.log('new expiresAt:', renewed.expiresAt)
Python Example
await ctx.sandbox.commands.run('echo init', timeout=300)renewed = await ctx.sandbox.extend_timeout(600)print('new expires_at:', renewed['expires_at'])
kill()
Terminate the current sandbox instance proactively.
context.sandbox.kill()
When should you callkill? Call it when a one-time script finishes, or when you confirm that the entire conversation has ended and you want to immediately release the sandbox instance quota.
Error Code Descriptions
Error Code | Description |
SANDBOX_LIMIT_EXCEEDED | Concurrent instance quantity / Total memory-hour quota / Maximum runtime per instance exceeded |
SANDBOX_INVALID_PARAMETER | Parameter validation failed, for example, due to missing conversation_id or authorization Token. |
SANDBOX_AUTHORIZATION_EXPIRED | Authorization Token invalid/expired |
SANDBOX_FAILED_OPERATION | Sandbox service operation failed (such as due to rate limiting/timeout) |
SANDBOX_NETWORK_ERROR | Sandbox service network abnormal. |
SANDBOX_INSTANCE_UNAVAILABLE | Sandbox instance does not exist or has expired. |
SANDBOX_NETWORK_ERROR | Data plane runtime client dependency missing or failed to create. |
SANDBOX_UNKNOWN_ERROR | Fallback error code. For error details, refer to the message field. |
If you encounter the above issues and cannot resolve them, you can join our Discord server EdgeOne to seek technical support.
FAQ
Q:
browser_screenshot returns what? base64 or a URL?A: Returns
{ base64Image } (PNG base64). It does not return a URL, nor does it save the file within the sandbox. If you need to display it on the frontend / persist it, you can upload it to object storage to obtain a URL.Q:
browser_fetch What is the difference between browser_fetch and a direct HTTP request?A:
browser_fetch uses a real Chromium browser for navigation (CDP + Playwright) and can retrieve page metadata and HTML after JavaScript rendering. A regular HTTP fetch can only retrieve the raw response. For scenarios requiring lightweight API scraping, you can use curl within commands.Q: Can the timeout for the sandbox instance be adjusted to 10 hours?
A: Currently, the maximum runtime for a free edition sandbox instance is 1 hour. If you need to extend the instance runtime, you can submit a ticket to request it.
Q: Can only a subset of tools be made available to the LLM?
A: Yes.
context.tools.all() is the complete set. As needed, you can use context.tools.files() / context.tools.browser() to obtain tool groups, or use context.tools.get(name) to assemble a custom subset.