> ## 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.

# Skills

Skills package task instructions, reference material, and optional scripts in reusable directories. Agents can discover skills before loading their full instructions. Code execution is involved only when a skill uses scripts and an executor is configured

| Use case | Integration |
| :- | :- |
| Local skills maintained with the application | `load_skill_from_dir` and `SkillToolset` |
| On-demand access to cloud skill spaces | `VeSkillRegistry` and `SkillToolset` |
| Shared skills in a deployed Harness | Harness `skills` configuration |
| Remote skill workflows | `skills_sandbox` mode or a [remote sandbox agent](/productions/veadk/preview/en/components/agent/remote-sandbox-agent) |

The `SkillToolset` examples use the Google ADK 2.2 API. Complete [installation and model configuration](/productions/veadk/preview/en/get-started/quickstart) first

## Local skills

Create `skills/incident-summary/SKILL.md` in your project:

```markdown skills/incident-summary/SKILL.md lines theme={null}
---
name: incident-summary
description: Summarize incident notes into impact, timeline, and follow-up actions
---

# Incident summary

Read the notes provided by the user and return three sections:

- Impact: describe affected users and services
- Timeline: list only times explicitly present in the notes
- Follow-up actions: distinguish completed work from proposed work

Mark missing information as unknown. Do not invent causes or timestamps.
```

Create and run `main.py` from the project root:

```python main.py lines theme={null}
import asyncio
from pathlib import Path
from google.adk.skills import load_skill_from_dir
from google.adk.tools.skill_toolset import SkillToolset
from veadk import Agent, Runner

skill_dir = Path(__file__).parent / "skills" / "incident-summary"
skill = load_skill_from_dir(skill_dir)
agent = Agent(
    name="incident_assistant",
    instruction="Use the incident-summary skill to summarize incident notes.",
    tools=[SkillToolset(skills=[skill])],
)

async def main():
    print(await Runner(agent=agent).run(
        messages="At 09:10 invoice downloads failed. At 09:25 a rollback restored service. The cause is unknown.",
        session_id="skills-demo",
    ))

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

The response should contain impact, timeline, and follow-up sections, marking missing information as unknown. This skill only processes supplied text and needs no code executor

Default tools are `list_skills`, `load_skill`, `load_skill_resource`, and `run_skill_script`. Configuring a registry also adds `search_skills`

### SkillToolset parameters

| Parameter | Type | Default | Description |
| :- | :- | :- | :- |
| `skills` | `list[Skill] \| None` | `None` | Loaded local skills; names must be unique |
| `registry` | `SkillRegistry \| None` | `None` | Source for on-demand discovery and loading |
| `code_executor` | `BaseCodeExecutor \| None` | `None` | Script executor; falls back to the agent's executor when omitted |
| `script_timeout` | `int` | `300` | Shell script timeout in seconds; does not limit Python scripts executed directly by the executor |
| `additional_tools` | `list[ToolUnion] \| None` | `None` | Tools activated through skill `metadata.adk_additional_tools` |
| `tool_name_prefix` | `str \| None` | `None` | Prefix for skill tool names |
| `tool_filter` | `ToolPredicate \| list[str] \| None` | `None` | Restricts tools by predicate or name |

### Run skill scripts

<Warning>
  `UnsafeLocalCodeExecutor` runs skill code in the current process environment without sandbox isolation. Enable it only for reviewed, trusted skills: code can access the process's files, network, and credentials. Use a restricted container or remote sandbox when isolation is required
</Warning>

<Note>
  When a shell script times out or is cancelled, the entire process group (including child processes spawned by the script) is terminated with up to 5 seconds for cleanup, preventing orphaned processes
</Note>

For a skill with executable `scripts/`, replace the toolset and agent configuration after loading `skill` above:

```python lines theme={null}
from google.adk.code_executors import UnsafeLocalCodeExecutor

toolset = SkillToolset(
    skills=[skill],
    code_executor=UnsafeLocalCodeExecutor(),
    script_timeout=300,
)
agent = Agent(name="skills_assistant", tools=[toolset])
```

Creating `SkillToolset` directly does not install script dependencies. Relevant loaders in Harness, CLI-generated code, and AgentKit session capability services configure a local executor. Review skill sources before using those entry points as well

## Cloud skill spaces

Prepare a readable source ID and account credentials permitted to list skills and download their files. `SKILL_SOURCE_ID` is an environment variable defined by this example. Each `VeSkillRegistry` accepts exactly one source ID, not a comma-separated list

| Setting | Volcengine | BytePlus |
| :- | :- | :- |
| `CLOUD_PROVIDER` | `volcengine`, default | `byteplus` |
| AccessKey | `VOLCENGINE_ACCESS_KEY` | `BYTEPLUS_ACCESS_KEY` |
| SecretKey | `VOLCENGINE_SECRET_KEY` | `BYTEPLUS_SECRET_KEY` |
| Temporary credential Token | `VOLCENGINE_SESSION_TOKEN` | `BYTEPLUS_SESSION_TOKEN` |
| Default AgentKit skill-space region | `cn-beijing` | `ap-southeast-1` |

If the complete AK/SK pair is absent, the loader tries the environment's IAM Role credentials. Set `AGENTKIT_TOOL_REGION` to select an AgentKit skill-space region. Credentials, resources, region, and platform must match. SkillHub and AgentKit skill-space availability depend on the services enabled for the account

```bash lines theme={null}
export SKILL_SOURCE_ID="<skill-space-id>"
```

```python cloud_skills.py lines theme={null}
import asyncio
import os
from google.adk.tools.skill_toolset import SkillToolset
from veadk import Agent, Runner
from veadk.skills import VeSkillRegistry

registry = VeSkillRegistry(skill_source_id=os.environ["SKILL_SOURCE_ID"])
agent = Agent(
    name="skills_assistant",
    instruction="Search available skills and load the relevant instructions before answering.",
    tools=[SkillToolset(registry=registry)],
)

async def main():
    print(await Runner(agent=agent).run(
        messages="List the available skills and explain what they help with.",
        session_id="registry-demo",
    ))

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

Run `python cloud_skills.py` to inspect discoverable skills

| VeSkillRegistry parameter | Type | Default | Description |
| :- | :- | :- | :- |
| `skill_source_id` | `str` | Required | One source ID; `sp-` identifies a SkillHub space, while other IDs use AgentKit skill-space behavior |
| `cache_dir` | `Path \| None` | `None` | Download cache; defaults to `VEADK_SKILLS_CACHE_DIR`, then `veadk/skills` under the system temporary directory |

Creating a registry does not download every skill. `search_skills` fetches all source metadata and currently does not filter by the query. `get_skill` refreshes metadata and downloads the named skill on demand. Cached versions are reused; changed versions are downloaded again. The cache directory must be writable

Nonstandard cloud frontmatter fields such as `allowed-tools`, `triggers`, and `requires` are retained in `metadata`. Retention does not automatically enforce their permission or dependency declarations

### Skill space policy

Set the `SKILL_SPACE_POLICY` environment variable to filter which skills are loaded from a cloud skill space by skill ID. The variable is a JSON object with the following fields:

| Field | Type | Description |
| :- | :- | :- |
| `mode` | `"allow" \| "deny"` | Filter mode |
| `ids` | `list[str]` | Skill IDs participating in the filter |

When `mode` is `allow`, only skills whose IDs are in the list are loaded. When `mode` is `deny`, skills whose IDs are in the list are excluded. When the variable is not set, all skills are loaded

```bash lines theme={null}
export SKILL_SPACE_POLICY='{"mode": "allow", "ids": ["skill-1", "skill-2"]}'
```

<Note>
  The policy value must not exceed 8192 bytes. A malformed policy disables all cloud skill loading to avoid accidentally exposing an unrestricted set of skills
</Note>

When `SKILL_SPACE_POLICY` is set, VeADK automatically forwards it to remote sandbox sessions so that skill loading inside the sandbox follows the same filter. No manual pass-through is needed when using `execute_skills`, `invoke_skill`, `poll_skill`, or `run_sandbox_agent`; see [Code sandboxes](/productions/veadk/preview/en/components/tools/code-sandbox)

## Harness skills center

Create a project and configure deployment credentials using [Harness deployment](/productions/veadk/preview/en/deploy/harness). Replace the example names and IDs with accessible skills. Plain names or slugs identify Skill Hub skills; the `space:` prefix identifies an AgentKit skill space

```bash lines theme={null}
cd my-harness
veadk harness add --skills "data-visualization-cloud,space:ss-example"
```

The generated `harness.yaml` stores sources as a list:

```yaml lines theme={null}
skills:
  - data-visualization-cloud
  - space:ss-example
```

Harness loads configured skills on startup; a `space:` source loads every skill in that space. An invocation can add skills for that request:

```bash lines theme={null}
veadk harness invoke   --name research-agent   --message "Summarize the supplied incident notes"   --skills "space:ss-example"
```

| Example option | Purpose |
| :- | :- |
| `add --skills` | Saves base Harness skill sources |
| `invoke --name` | Selects the Harness agent |
| `invoke --message` | Supplies task text |
| `invoke --skills` | Adds skills for this request without changing `harness.yaml` |

If any skill cannot be downloaded, lacks a valid `SKILL.md`, or has an invalid format, loading fails instead of continuing with an incomplete set. Skills-center access requires permission to read the space and its object-storage contents

## Skill directory layout

Each skill has its own directory containing at least `SKILL.md`. Its `name` should match the directory name, and its `description` should explain its purpose

```text lines theme={null}
skills/
└── incident-summary/
    ├── SKILL.md
    ├── references/
    ├── assets/
    └── scripts/
```

Use `references/` for material read on demand, `assets/` for templates or media, and `scripts/` for executable scripts. Create these optional directories only as needed and explain when to use their files in the skill instructions

## Legacy local entry (deprecated)

`Agent(skills=..., skills_mode="local")` remains compatible but is deprecated. Migrate local skills to `SkillToolset`

| `skills_mode` | Behavior |
| :- | :- |
| `local` | Legacy local entry; migration recommended |
| `skills_sandbox` | Executes through `execute_skills` in a remote skill sandbox |
| `aio_sandbox` | Uses an AgentKit All-in-one tool sandbox |

Sandbox modes remain available. Before running this example, configure `SKILL_SOURCE_ID`, the model, and the credentials and Tool ID required by [code sandbox](/productions/veadk/preview/en/components/tools/code-sandbox). Confirm that the sandbox has the required skills and network permissions:

```python sandbox_skills.py lines theme={null}
import asyncio
import os
from veadk import Agent, Runner
from veadk.tools.builtin_tools.execute_skills import execute_skills

agent = Agent(
    name="sandbox_assistant",
    skills=[os.environ["SKILL_SOURCE_ID"]],
    skills_mode="skills_sandbox",
    tools=[execute_skills],
)

async def main():
    print(await Runner(agent=agent).run(
        messages="List your available skills.", session_id="sandbox-skills"
    ))

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

### Dynamic skill loading

`enable_dynamic_load_skills=True` applies to the legacy `Agent.skills` entry. It rereads sources before each turn and updates skill lists, instructions, and tools. The toolset is initialized even if `skills` starts empty. Applications sharing a mutable agent across concurrent calls must manage refresh and invocation concurrency

The flag does not hot-reload local objects passed directly to `SkillToolset(skills=[...])`. Registry-based loading does not require this flag. See [runtime compatibility](/productions/veadk/preview/en/components/agent/runtime#runtime-compatibility) for external-runtime restrictions on legacy skill modes
