> ## Documentation Index
> Fetch the complete documentation index at: https://docs.veadk.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Code sandboxes

## Overview

VeADK provides a set of tools that run tasks remotely in an [AgentKit sandbox](https://console.volcengine.com/agentkit):

| Tool | Description |
| :- | :- |
| `run_code` | Execute arbitrary code or shell commands — good for computation, data processing, dependency installation, and so on |
| `execute_skills` | Run an `agent.py` workflow in a prebuilt skills sandbox |
| `coding` | Run a code-generation workflow in an OpenCode sandbox |
| `run_sandbox_agent` | Run `agent.py` in a remote sandbox under any `tool_id` |
| `invoke_skill` | Create a skill task non-blockingly and return the initial task object |
| `poll_skill` | Fetch the current status snapshot of a skill task |

Import paths:

* `from veadk.tools.builtin_tools.run_code import run_code`
* `from veadk.tools.builtin_tools.execute_skills import execute_skills`
* `from veadk.tools.builtin_tools.coding import coding`
* `from veadk.tools.builtin_tools.run_sandbox_agent import run_sandbox_agent`
* `from veadk.tools.builtin_tools.invoke_skill import invoke_skill`
* `from veadk.tools.builtin_tools.poll_skill import poll_skill`

<Note>
  If you need to delegate a complete task to the remote sandbox rather than having the coordinator agent call individual tools, use the `AgentkitRemoteSandboxAgent` sub-agent. See [Remote sandbox sub-agent](/productions/veadk/preview/en/components/agent/remote-sandbox-agent).
</Note>

## Environment & prerequisites

<Warning>
  Requirements:

  1. Configure Volcengine AK / SK.
  2. Configure the API key for the agent's reasoning model.
  3. Configure the AgentKit Tool ID (see below).
</Warning>

Environment variables:

* `MODEL_AGENT_API_KEY`: API key for the agent's reasoning model
* `VOLCENGINE_ACCESS_KEY` / `VOLCENGINE_SECRET_KEY`: Volcengine AK / SK
* `AGENTKIT_TOOL_ID`: default AgentKit sandbox ID, used as the fallback for all sandbox tools
* `AGENTKIT_TOOL_ID_SCRIPT`: sandbox ID dedicated to `run_code`; falls back to `AGENTKIT_TOOL_ID`
* `AGENTKIT_TOOL_ID_SKILLS`: sandbox ID dedicated to `execute_skills`; falls back to `AGENTKIT_TOOL_ID`
* `AGENTKIT_TOOL_ID_OPENCODE`: sandbox ID dedicated to `coding`; falls back to `AGENTKIT_TOOL_ID`
* `AGENTKIT_TOOL_HOST`: endpoint for calling AgentKit Tools
* `AGENTKIT_TOOL_SERVICE_CODE`: service code for calling AgentKit Tools
* `AGENTKIT_TOOL_REGION`: region for calling AgentKit Tools. When unset, `volces` mode falls back to the `REGION` environment variable, then defaults to `cn-beijing`; `byteplus` mode does not read `REGION` and uses the BytePlus default region
* `VEADK_RUN_CODE_ISOLATE_PARALLEL_CALLS`: when multiple `run_code` calls execute in parallel within the same turn, whether each call gets a distinct sandbox session ID. Defaults to `true` (enabled); set to `false` to have all parallel calls share the same session ID

`config.yaml` keys:

```yaml title="config.yaml" lines theme={null}
agentkit:
  tool_id: your-default-tool-id
  tool_id_script: your-script-tool-id
  tool_id_skills: your-skills-tool-id
  tool_id_opencode: your-opencode-tool-id
```

Creating a sandbox:

<Steps>
  <Step title="Create a sandbox tool">
    Create a sandbox tool in the console: give it a name (e.g. `AIO_Sandbox_xxxx`) and choose the "all-in-one tool set" type, which includes Browser, Terminal, and Code runtimes.
  </Step>

  <Step title="Obtain the sandbox ID">
    After creation, obtain the sandbox ID from the console (looks like `t-ye8dj82xxxxx`) and fill it into the environment variables or `config.yaml` above.
  </Step>
</Steps>

For BytePlus, set `CLOUD_PROVIDER=byteplus`, `BYTEPLUS_ACCESS_KEY`, and `BYTEPLUS_SECRET_KEY`, plus `BYTEPLUS_SESSION_TOKEN` for STS. Volcengine STS uses `VOLCENGINE_SESSION_TOKEN`. The Tool ID, region, and endpoint must belong to the same environment. A basic AIO sandbox supplies execution facilities; `execute_skills` additionally requires a running skills A2A service, while `coding` and `run_sandbox_agent` require an executable `agent.py` workflow and dependencies in the target directory.

Start with the Python version check below to confirm connectivity before adding skills or business data.

## Usage

```python title="examples/tools/run_code/agent.py" lines theme={null}
import asyncio

from veadk import Agent, Runner
from veadk.memory.short_term_memory import ShortTermMemory
from veadk.tools.builtin_tools.run_code import run_code
from veadk.tools.builtin_tools.web_search import web_search

agent = Agent(
    name="data_analysis_agent",
    model_name="doubao-seed-2-1-pro-260628",
    description="A data analysis agent for the stock market.",
    instruction=(
        "You are a senior engineer running code in a sandbox. Use web_search to find "
        "company operating data, use libraries such as akshare to download stock data, "
        "and install missing dependencies from within the sandbox via code."
    ),
    tools=[run_code, web_search],
)

runner = Runner(agent=agent, short_term_memory=ShortTermMemory())


async def main():
    response = await runner.run("Analyze the recent stock price trend of Sungrow Power")
    print(response)


if __name__ == "__main__":
    asyncio.run(main())
```

## Running shell commands

In addition to running code such as Python, `run_code` can run shell commands in the sandbox. When `language` is set to `bash` or `shell`, the `code` is executed as a shell command in the remote sandbox — useful for file operations, installing dependencies, or invoking command-line tools.

Shell execution reuses the sandbox ID and credential configuration of `run_code`. The `code` is submitted as a command, so multiple commands can be chained within a single execution.

<Warning>
  Shell can execute arbitrary commands, install dependencies, read and write files, and reach services available from the remote sandbox network. Run only trusted commands and restrict the data, network, and permissions available to the sandbox. Do not put long-lived credentials in commands or `env`; use the sandbox's supported credential-hosting mechanism when credentials are required.
</Warning>

### Parameters

The full signature of `run_code`:

```python lines theme={null}
def run_code(
    code: str,
    language: str,
    tool_context: ToolContext,
    timeout: int = 300,
    exec_dir: str = "/tmp",
    env: dict[str, str] | None = None,
    hard_timeout: int = 300,
    max_output_length: int = 30000,
) -> str:
    ...
```

| Parameter | Type | Default | Description |
| :- | :- | :- | :- |
| `code` | `str` | — | The code or shell command to run. |
| `language` | `str` | — | Execution language. Use `python3` for code, or `bash` / `shell` for shell commands. |
| `tool_context` | `ToolContext` | — | Tool runtime context, injected automatically by VeADK. |
| `timeout` | `int` | `300` | Execution timeout in seconds. Must be between 1 and 300. |
| `exec_dir` | `str` | `/tmp` | Working directory for shell execution. |
| `env` | `dict[str, str] \| None` | `None` | Environment variables for shell execution. |
| `hard_timeout` | `int` | `300` | Hard timeout for shell execution, in seconds. Must be between 1 and 300. |
| `max_output_length` | `int` | `30000` | Maximum length of shell output. |

`exec_dir`, `env`, `hard_timeout`, and `max_output_length` apply only when running shell commands (`language` set to `bash` or `shell`); they are managed by the sandbox runtime and do not apply when running code.

### Example

```python title="run_code_bash.py" lines theme={null}
import asyncio

from google.adk.tools import ToolContext
from veadk import Agent, Runner
from veadk.memory.short_term_memory import ShortTermMemory
from veadk.tools.builtin_tools.run_code import run_code


def check_python(tool_context: ToolContext) -> str:
    """Show the Python version in the remote sandbox."""
    return run_code(
        code="pwd && python --version",
        language="bash",
        tool_context=tool_context,
        timeout=60,
        exec_dir="/tmp",
        env={"APP_ENV": "test"},
        hard_timeout=120,
        max_output_length=5000,
    )


agent = Agent(
    name="shell_agent",
    instruction="Call the tool to show the sandbox working directory and Python version.",
    tools=[check_python],
)
runner = Runner(agent=agent, short_term_memory=ShortTermMemory())


async def main():
    response = await runner.run("Check the sandbox environment")
    print(response)


if __name__ == "__main__":
    asyncio.run(main())
```

## Injecting environment variables into sandbox executions

`run_sandbox_agent` accepts custom environment variables through the `extra_env_vars` parameter that are injected into a single sandbox execution. Use them to pass runtime parameters (configuration values, credential references, feature flags, and so on) to an in-sandbox workflow. Injected variables are scoped to the current execution only and are not persisted to the sandbox base environment.

<Note>
  `execute_skills` no longer supports injecting environment variables. Passing a non-`None` `env_vars` value raises an error.
</Note>

### Parameters

| Parameter | Type | Default | Description |
| :- | :- | :- | :- |
| `extra_env_vars` | `Optional[dict[str, str]]` | `None` | Environment variables injected by `run_sandbox_agent`; keys and values are strings, scoped to the current execution. |

### Validation rules

Injected variables are merged with the framework-managed variables that VeADK sets itself, then passed to the sandbox process under these rules:

* Names must match `^[A-Za-z_][A-Za-z0-9_]*$`; otherwise initialization raises an error.
* `TOOL_USER_SESSION_ID` and `USER_SESSION_ID` are managed by VeADK and cannot be overridden; initialization raises an error if they are set.
* Values must be strings; non-string values raise an error.
* Values must not contain null bytes (`\x00`); otherwise initialization raises an error.
* Custom variables override any same-named variable in the sandbox process environment (including VeADK defaults such as `TOS_SKILLS_DIR` and `SKILL_SPACE_ID`), so resource paths for a single execution can be adjusted on demand.

### Examples

The following example wraps the call as a function tool. `Runner` injects `tool_context` when it invokes a function tool, while the application sets the environment variables for each execution in the wrapper:

```python title="sandbox_env.py" lines theme={null}
import asyncio

from google.adk.tools import ToolContext
from veadk import Agent, Runner
from veadk.memory.short_term_memory import ShortTermMemory
from veadk.tools.builtin_tools.run_sandbox_agent import run_sandbox_agent


def run_custom_workflow(workflow_prompt: str, tool_context: ToolContext) -> str:
    """Run a workflow in a specified AgentKit sandbox."""
    return run_sandbox_agent(
        workflow_prompt=workflow_prompt,
        tool_id="t-your-sandbox-tool-id",
        tool_context=tool_context,
        extra_env_vars={"DATA_SOURCE_URL": "https://example.com/data.csv"},
    )


agent = Agent(
    name="sandbox_agent",
    instruction="Call the appropriate sandbox workflow for the user's request.",
    skills=["space:your-skill-space-id"],
    skills_mode="skills_sandbox",
    tools=[run_custom_workflow],
)
runner = Runner(agent=agent, short_term_memory=ShortTermMemory())


async def main():
    response = await runner.run("Use the skills workflow to summarize the knowledge base")
    print(response)


if __name__ == "__main__":
    asyncio.run(main())
```

<Warning>
  Values in `extra_env_vars` are passed to the remote sandbox process. Do not put long-lived credentials in source code; use the sandbox's supported credential-hosting mechanism when credentials are required.
</Warning>

<Note>
  When `SKILL_SPACE_POLICY` is set, VeADK automatically forwards it to sandbox sessions created by `execute_skills`, `invoke_skill`, `poll_skill`, and `run_sandbox_agent`, so skill loading inside the sandbox follows the same filter as the local environment. No manual pass-through via `extra_env_vars` is needed. See [Skills](/productions/veadk/preview/en/components/agent/skills#skill-space-policy)
</Note>

## Skill sandbox execution

`execute_skills` runs a workflow in the skills sandbox through the A2A protocol. It sends a non-blocking `message/send` request and then polls the task status at exponentially increasing intervals until the task reaches a terminal state or the timeout expires. The maximum execution time is 1800 seconds (30 minutes).

When `execute_skills` is called, VeADK reads the inbound identity credential from the credential service (credential key `inbound_auth`) and forwards it to the skills sandbox in the `inbound_auth` request header, so the sandbox workflow runs under the original user identity. If the current request carries no inbound credential, the header is omitted and that user identity is not forwarded; valid cloud credentials and permissions are still required to access the sandbox.

<Note>
  For the source and configuration of inbound identity credentials, see [Inbound authentication](/productions/veadk/preview/en/components/security/inbound).
</Note>

### Parameters

The full signature of `execute_skills`:

```python lines theme={null}
def execute_skills(
    workflow_prompt: str,
    tool_context: ToolContext,
    env_vars: Optional[dict[str, str]] = None,
    timeout: int = 1800,
) -> str:
    ...
```

| Parameter | Type | Default | Description |
| :- | :- | :- | :- |
| `workflow_prompt` | `str` | — | The skill workflow instruction. |
| `tool_context` | `ToolContext` | — | Tool runtime context, injected automatically by VeADK. |
| `env_vars` | `Optional[dict[str, str]]` | `None` | Unsupported. Passing a non-`None` value raises an error. |
| `timeout` | `int` | `1800` | Maximum execution time in seconds. Must be between 1 and 1800. |

<Note>
  `execute_skills` has removed the `invocation_mode` parameter and the `AGENTKIT_SKILL_INVOCATION_MODE` environment variable. Execution now uses the A2A protocol exclusively.
</Note>

### Example

```python title="execute_skills_timeout.py" lines theme={null}
import asyncio

from google.adk.tools import ToolContext
from veadk import Agent, Runner
from veadk.memory.short_term_memory import ShortTermMemory
from veadk.tools.builtin_tools.execute_skills import execute_skills


def run_skill_workflow(workflow_prompt: str, tool_context: ToolContext) -> str:
    """Run a workflow in the skills sandbox with a 600-second timeout."""
    return execute_skills(
        workflow_prompt=workflow_prompt,
        tool_context=tool_context,
        timeout=600,
    )


agent = Agent(
    name="skill_agent",
    instruction="Call the appropriate skills workflow for the user's request.",
    skills=["space:your-skill-space-id"],
    skills_mode="skills_sandbox",
    tools=[run_skill_workflow],
)
runner = Runner(agent=agent, short_term_memory=ShortTermMemory())


async def main():
    response = await runner.run("Use the skills workflow to summarize the knowledge base")
    print(response)


if __name__ == "__main__":
    asyncio.run(main())
```

## Parallel call isolation

When a [synchronous tool thread pool](/productions/veadk/preview/en/components/agent/runtime#parallel-synchronous-tool-execution) is configured and the model issues multiple `run_code` calls in the same turn, VeADK assigns each call a distinct sandbox session ID by appending its function-call identifier to the base session ID. This ensures that each `run_code` call runs in an isolated sandbox, with no interference between their file systems and runtime environments.

<Note>
  Without a thread pool configured, multiple `run_code` calls in the same turn execute serially using the same base session ID and isolation does not apply.
</Note>

Control this behavior with the `VEADK_RUN_CODE_ISOLATE_PARALLEL_CALLS` environment variable:

| Environment variable | Default | Description |
| :- | :- | :- |
| `VEADK_RUN_CODE_ISOLATE_PARALLEL_CALLS` | `true` | Whether to assign each parallel `run_code` call a distinct sandbox session ID. Set to `false` to have all parallel calls share the same session ID. Accepts `1`, `true`, `yes`, `on` (case-insensitive) as enabled, and `0`, `false`, `no`, `off` as disabled. |

## Complete parameters for a selected sandbox workflow

`run_sandbox_agent` does not create or deploy `agent.py`; the sandbox must already contain that file and its dependencies.

| Parameter | Type | Default | Description |
| :- | :- | :- | :- |
| `workflow_prompt` | `str` | Required | Task instruction for the remote workflow |
| `tool_id` | `str` | Required | Explicit sandbox Tool ID |
| `tool_context` | `ToolContext \| None` | `None` | Required at invocation, injected by the tool runtime |
| `skills` | `list[str] \| None` | `None` | Skill selection passed to the remote workflow |
| `timeout` | `int` | `900` | Workflow waiting time in seconds, subject to service limits |
| `working_dir` | `str` | `/home/gem/veadk_skills` | Sandbox directory containing `agent.py` |
| `extra_env_vars` | `dict[str, str] \| None` | `None` | Environment for this execution, validated as described above |

`coding` accepts required `workflow_prompt: str`, injected `tool_context=None`, and `timeout: int = 900`. It runs a configured workflow through `AGENTKIT_TOOL_ID_OPENCODE` or the default Tool ID, rather than directly executing an arbitrary source-code string.

These tools return execution output, which can include remote errors or response objects. Check execution results, stderr, and expected artifacts before declaring completion. Waiting for a skill can end on required input, required authorization, or timeout without canceling the remote task.
