# Internal Tools

> Built-in tools for planning, memory, widgets, and sub-agent completion.

**URL:** https://inference.sh/docs/api/agent/internal-tools
**Last updated:** 2026-10-11

---

Built-in tools for planning, memory, widgets, and sub-agent completion.

---

## Overview

Most internal tool categories are on when you omit them from `internal_tools`. Five categories (`meta`, `artifact`, `agent`, `remote`, `knowledge`) are opt-in and stay off until you enable them. Configure categories with `internalTools`:

<div data-type="codegroup">

```python Python
from inferencesh import inference, internal_tools

client = inference(api_key="inf_...")
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "internal_tools": internal_tools()
        .plan(True)
        .memory(True)
        .widget(True)
        .build()
})
```

```typescript JavaScript
import { inference, internalTools } from '@inferencesh/sdk';

const client = inference({ apiKey: 'inf_...' });
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  internal_tools: internalTools()
    .plan(true)
    .memory(true)
    .widget(true)
    .build()
});
```

</div>

---

## Default behavior

When a category flag is omitted from `internal_tools`, the runtime applies a built-in default:

| Category | `internal_tools` flag | Default when unset |
|----------|----------------------|--------------------|
| Plan | `plan` | on |
| Memory | `memory` | on |
| Widget | `widget` | on |
| Finish | `finish` | on |
| Skills | `skills` | on |
| Meta | `meta` | off (opt-in) |
| Artifacts | `artifact` | off (opt-in) |
| Agent | `agent` | off (opt-in) |
| Remotes | `remote` | off (opt-in) |
| Knowledge | `knowledge` | off (opt-in) |

An explicit `true` or `false` always wins over the default. An unset flag is not the same as `false`. For example, `{"finish": false, "meta": false}` disables finish and meta tools, but plan, memory, widget, and skills stay enabled because their flags were never set.

Agent editors and UIs should call [`GET /agents/internal-tools`](/docs/api/rest/agents#list-internal-tool-definitions) (JavaScript SDK: `client.agents.getInternalTools()`). Each definition includes `default_enabled`, which matches this table. Render capability switches from that field.

### Tool names

A tool you define on an agent cannot share a name with a built-in tool that the agent has turned on. Saving such a version fails with an error naming the `internal_tools` flag; rename your tool or turn that category off. A built-in whose category is off leaves its name free, so a tool named `agent` is fine on an agent that does not enable the `agent` category. If an older saved version still carries a clashing tool, the runtime leaves out that one tool, logs a warning, and keeps the rest.

---

## Available Internal Tools

| Category | Tools | Scope |
|----------|-------|-------|
| **Plan** | `plan_create`, `plan_update`, `plan_load` | All agents |
| **Memory** | `memory_set`, `memory_get`, `memory_getall` | All agents |
| **Widget** | `widget` | Top-level only |
| **Finish** | `finish` | All agents |
| **Skills** | `skill_get` | All agents |
| **Meta** | `search_tools`, `list_tools`, `execute_tool` | All agents (opt-in) |
| **Artifacts** | `artifact_guidance`, `artifact_publish`, `artifact_get`, `artifact_list`, `artifact_comments`, `artifact_reply`, `artifact_resolve` | All agents (opt-in) |
| **Agent** | `agent` | All agents below the delegation depth limit (opt-in) |
| **Remotes** | `remote_list`, `remote_exec` | All agents (opt-in) |
| **Knowledge** | `knowledge_search`, `knowledge_get`, `knowledge_save` | All agents (opt-in) |

---

## Meta tools

Enable with `meta: true` in `internal_tools`. Meta tools are opt-in.

Use meta tools when an agent needs access to many platform resources without listing every app tool, agent tool, or knowledge ref in its config. Three tools cover the catalog of apps, agents, flows, skills, and knowledge. Every entry is addressed as `namespace/name`.

| Tool | Parameters | Purpose |
|------|------------|---------|
| `search_tools` | `query`, optional `kind` | Search the platform catalog by keyword |
| `list_tools` | `names`, optional `kind` | Describe matches with full argument schemas |
| `execute_tool` | `name`, `arguments`, optional `kind` | Run an app (as a task) or delegate to an agent |

**Catalog kinds:** every match carries a `source` field that says what it is.

| `source` | What it is | Runnable via `execute_tool`? |
|----------|------------|------------------------------|
| `app` | A platform app (possibly multi-function) | Yes. Runs as a task |
| `agent` | An agent you can delegate to | Yes. Same call shape as a configured agent tool (`task`, `context`, optional `images`) |
| `flow` | A saved multi-step pipeline | No. `list_tools` describes it; `execute_tool` returns an error |
| `skill` | Registry or workspace skill (instructions to follow) | No. `list_tools` returns the content inline |
| `knowledge` | Stored reference material | No. `list_tools` returns the content inline |

Pass the `source` value as `kind` on `list_tools` and `execute_tool`. Without `kind`, the runtime tries each kind in turn, and can describe or run the wrong thing when a name exists in more than one kind.

`kind` on `search_tools` narrows the search to one kind (`app`, `agent`, `flow`, `skill`, or `knowledge`). Omit it to search everything. An unknown `kind` returns an error.

**Catalog scope:** search uses the same visibility as the rest of the platform: your workspace's resources plus public resources your credentials can read. Private resources owned by other workspaces do not appear. The `search_tools` description states how many apps the catalog holds.

**`search_tools` query matching:** catalog search uses the platform's hybrid keyword and semantic search, the same index behind [`/search`](/docs/api/rest/search). Queries are passed verbatim, so describe the capability in a few words. Results are ranked by relevance, with a per-kind cap so one kind cannot fill the list.

**`search_tools` response:**

| Field | Meaning |
|-------|---------|
| `matches` | Array of `{ name, description, source }` entries |
| `total` | Number of matches returned (the length of `matches`) |
| `query` | Echo of the search term |
| `kind` | Echo of the filter. Present only when `kind` was passed |

A multi-function app appears once in search results. Call `list_tools` on it to see its functions and schemas.

**`list_tools` response:** call this before `execute_tool` for apps and agents, because search results do not include argument schemas.

| Field | Meaning |
|-------|---------|
| `tools` | Array of `{ name, description, source, input_schema? }` entries |
| `total` | Number of entries returned |

- For [multi-function apps](/docs/extend/multi-function-apps), the response has one entry per function, each with its own `input_schema`. The default function is listed first and keeps the bare `namespace/name` ref. Other functions are named `namespace/name:function`. If `names` already includes a function ref (for example `google/gmail:send_message`), only that function is returned.
- For agents, `list_tools` returns the sub-agent call schema (`task`, `context`, `images`), which is what `execute_tool` expects. It does not return the agent's configuration.
- For skills and knowledge, `list_tools` returns the content in `description`.

**Typical flow (app):**

```
Agent: [calls search_tools(query="gmail")]
> matches: { name: "google/gmail", source: "app", description: "..." }

Agent: [calls list_tools(names=["google/gmail"], kind="app")]
> google/gmail: input_schema { q: string }
> google/gmail:list_messages: input_schema { max_results: integer }
> google/gmail:send_message: input_schema { to: string }

Agent: [calls execute_tool(name="google/gmail:send_message", kind="app", arguments={ to: "..." })]
> task output
```

**Typical flow (agent delegation):**

```
Agent: [calls search_tools(query="code review", kind="agent")]
> matches: { name: "acme/reviewer", source: "agent", description: "..." }

Agent: [calls list_tools(names=["acme/reviewer"], kind="agent")]
> acme/reviewer: input_schema { task, context, images }

Agent: [calls execute_tool(name="acme/reviewer", kind="agent", arguments={ task: "Review the diff", context: "..." })]
> sub-agent result
```

`execute_tool` resolves `name` in this order:

1. Tools already declared on the agent config
2. The platform catalog by ref. Apps run as tasks. Agents delegate through the same path as configured sub-agents, with the same depth and fan-out limits

Meta tools are added alongside your configured tools. They do not remove tools you declare explicitly.

<div data-type="codegroup">

```python Python
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "system_prompt": "Discover and run platform apps as needed.",
    "internal_tools": {"meta": True},
})
```

```typescript JavaScript
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  system_prompt: 'Discover and run platform apps as needed.',
  internal_tools: internalTools().meta().build(),
});
```

</div>

---

## Host context tools (deprecated)

Host context is disabled. `internal_tools.host_context` is accepted and ignored, and no agent is offered `get_host_context` or `send_to_host`. These tools ran in the browser, and only the hosted embed page answered them, so on any other surface the call never completed and the chat stalled. A replacement for passing host page context to embedded agents is planned.

---

## Artifact tools

Enable with `artifact: true` in `internal_tools`. Artifact tools are opt-in.

Agents use these tools to publish small self-contained HTML or Markdown pages to a shareable URL that updates in place, and to answer the comment threads people leave on them:

| Tool | Purpose |
|------|---------|
| `artifact_guidance` | Return the page contract and the skills that carry the detail. Meant to be called before writing a page |
| `artifact_publish` | Create an artifact, or update one in place by `artifact_id` or `name`. Returns the artifact id, URL, and version |
| `artifact_get` | Read an artifact's metadata (title, URL, version, visibility) and page source |
| `artifact_list` | List artifacts the user owns or that were shared with them, newest first, with optional search |
| `artifact_comments` | Read the comment threads on an artifact. Each thread says whether it was sent to the agent |
| `artifact_reply` | Reply into one comment thread. Only works on a thread that was sent to the agent |
| `artifact_resolve` | Mark a comment thread resolved. Only works on a thread that was sent to the agent |
| `artifact_asset_upload` | Store a file beside an artifact and return the URL its page loads it by. Takes `file_url`: a file on inference.sh the credential can read, such as an app tool's output. Up to 25 MiB |
| `artifact_asset_list` | List the files stored beside an artifact and what it uses against its budget |
| `artifact_asset_delete` | Permanently delete one stored file |

A page loads stored files only when it was published with the `assets` capability.

Enabling artifact tools also offers the artifact guidance skills through `skill_get`, even when the agent has no skills of its own.

See [Publishing artifacts](/docs/artifacts/publishing) for publishing, visibility, and sharing outside the agent runtime.

<div data-type="codegroup">

```python Python
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "internal_tools": {"artifact": True},
})
```

```typescript JavaScript
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  internal_tools: { artifact: true },
});
```

</div>

---

## Agent tool

Enable with `agent: true` in `internal_tools`. The agent tool is opt-in. It was called `spawn` before; `internal_tools.spawn` is still read as `agent`.

The `agent` tool runs a copy of the calling agent on a self-contained side task. The copy has the same version, system prompt, and tools, but a fresh context with none of the parent conversation. Use it to keep a long side task, such as reading many files or trying an approach that may not work, out of the parent's context window.

| Parameter | Required | Description |
|-----------|----------|-------------|
| `task` | Yes | What the copy should do and what to report back, stated so it stands on its own |
| `context` | No | Everything the copy needs that it cannot see: findings so far, constraints, file paths, identifiers |
| `images` | No | Image URLs to pass along |

The copy cannot ask the parent follow-up questions, so include everything it needs in `task` and `context`. It returns its result through the same path as a [configured sub-agent](/docs/agents/sub-agents).

**Depth limit:** a chat a person starts is depth 0, and each delegation adds one. The agent tool is offered at any depth below 3, so a copy can start another copy until the chain reaches depth 3. An agent at depth 3 gets neither `agent` nor its configured agent tools, and a delegation call at that depth returns an error. Configured sub-agents and spawned copies share this limit.

**Fan-out limit:** a chat can have at most 2 delegated sub-chats running at once, counting both spawned copies and configured sub-agents. A further call returns an error until one of them reports back.

**Agent tool vs configured sub-agents:** configured sub-agents are separate agents you add as agent-type tools, which is useful when you need a different system prompt or tool set. The agent tool reuses the current agent without a second agent definition. Both can be enabled on the same agent.

<div data-type="codegroup">

```python Python
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "internal_tools": {"agent": True},
})
```

```typescript JavaScript
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  internal_tools: { agent: true },
});
```

</div>

---

## Remote tools

Enable with `remote: true` in `internal_tools`. Remote tools are opt-in.

Use remote tools when an agent should run shell commands on machines the user connected with [`belt remote`](/docs/extend/remote). The same exec-run path backs these tools as [`belt remote exec`](/docs/extend/remote#run-a-command) and [`POST /remotes/{id}/execs`](/docs/api/rest/remotes). Runs started from the agent loop are recorded with `requested_by=loop` and tied to the active agent run.

Enabling the category is necessary but not sufficient: the remote daemon must be connected and started with `--exec`, and the credential running the agent needs the same scopes as the REST routes (`remotes:read` to list, `remotes:write` to dispatch execs).

| Tool | Parameters | Purpose |
|------|------------|---------|
| `remote_list` | none | List remotes (id, name, status, OS, `exec_enabled`) |
| `remote_exec` | `remote`, `command`; optional `description`, `cwd`, `timeout_seconds` | Run a shell line on a remote |

**Choosing a remote:** pass the id from `remote_list`, or a display name (case-insensitive). If the name is ambiguous, use the id.

**Command shape:** `remote_exec` runs the line through `sh -c` on Unix (`cmd /C` on Windows), so pipes, `&&`, and redirects work as in a normal shell.

**Approval card:** Pass `description`, a one-line summary of what the command does and why (for example `Install dependencies before building`). It is stored in the tool arguments, never sent to the shell, and shown above the command when the call pauses for approval.

**How the turn waits:** `remote_exec` behaves like an app tool. The runtime dispatches the exec run, leaves the tool invocation in progress, and returns. The turn continues when the run reaches a terminal status; the completed invocation includes JSON with `exec_run_id`, `status`, optional `exit_code`, and the last 30,000 characters of each of `stdout` and `stderr` (tails are kept so errors near the end are not dropped). Non-zero exits, timeouts, kills, and disconnects are reported in that result — they complete the tool call with information for the model, not a separate tool failure.

**Approvals:** With no matching chat or governance rule, `remote_exec` **asks** before dispatch. Add an allow rule on the chat, or test a command against the rules, with [Chat permissions rules](/docs/api/rest/agents#chat-permissions-rules) (`RemoteExec` kind). Rules and always-allow options can target specific commands in a compound line (for example `git clone:*`) instead of the whole remote; commands the policy engine cannot fully analyse ask for approval.

**Timeouts:** `timeout_seconds` kills the remote command after that many seconds (default 600).

**Trace:** Each in-chat `remote_exec` gets an execution graph node (like an app tool's task). Use [`GET /chats/:id/trace`](/docs/api/rest/agents#get-chat-trace) or the workspace chat trace to see the run (`resource_type` `exec_run`, label such as `exec on my-laptop`) while it is in progress.

**Stopping a chat:** Stopping the chat sweeps running execution graph nodes. An in-flight exec run is signaled the same way as a running app task, so remote commands do not keep running in the background after you stop.

**Disconnected remote:** If the remote drops before the command finishes, the exec run fails with a clear error (`remote disconnected before the command finished`) so the turn cannot hang on a laptop that went offline.

<div data-type="codegroup">

```python Python
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "internal_tools": {"remote": True},
})
```

```typescript JavaScript
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  internal_tools: { remote: true },
});
```

</div>

→ [Remotes guide](/docs/extend/remote) · [Remotes API](/docs/api/rest/remotes) · [Exec runs API](/docs/api/rest/exec-runs)

---

## Knowledge tools

Enable with `knowledge: true` in `internal_tools`. Knowledge tools are opt-in.

Use knowledge tools when an agent should find, load, or record skills and knowledge entries in the user's registry: the same entries as `belt knowledge`, `belt skill`, and the MCP `knowledge_*` / `skill_*` tools. `skill_get` only serves the skills configured on the agent; knowledge tools reach any entry the credential can see. The credential running the agent needs `knowledge:read` to search and load, and `knowledge:write` to save.

| Tool | Arguments | Description |
|------|-----------|-------------|
| `knowledge_search` | optional `query`, `source`, `type`, `limit` | Search entries (or list the most recent when `query` is omitted). `source: "store"` searches the public store of approved skills instead of the user's own. Returns `ref`, `type`, `description` |
| `knowledge_get` | `ref`; optional `path` | Load an entry's content and the paths of its supporting files, or one supporting file with `path` |
| `knowledge_save` | `name`, `content`; optional `type`, `description`, `allowed_tools`, `files` | Create an entry in the team's namespace. Saving under an existing name adds a new version; identical content is a no-op |
| `knowledge_delete` | `ref` | Permanently delete one of the user's entries with all its versions. Needs `knowledge:write` |

`type` is one of `skill`, `concept`, `observation` (default), `preference`, `reference`, `person`, `project`. `files` and `allowed_tools` apply to skills only.

<div data-type="codegroup">

```python Python
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "internal_tools": {"knowledge": True},
})
```

```typescript JavaScript
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  internal_tools: internalTools().knowledge().build(),
});
```

</div>

---

## Plan Tools

Help agents organize multi-step tasks:

### plan_create
Create a new plan with steps:
```
Agent creates: ["Research topic", "Write outline", "Draft content"]
```

### plan_update
Update step status (pending, in_progress, completed, failed):
```
Agent marks step 1 as completed
```

### plan_load
Load the current plan to see progress.

**Example conversation:**
```
User: "Write me a blog post about AI"

Agent: [calls plan_create with steps]
> Plan created:
> [ ] Research AI trends
> [ ] Create outline  
> [ ] Write draft
> [ ] Review and polish

Agent: [calls plan_update to mark step 1 in progress]
> Plan updated:
> [/] Research AI trends ← in progress
> [ ] Create outline
> ...
```

---

## Memory Tools

Scratch key-value notes for **this chat only**. Values persist across turns of the chat, and only this chat sees them. Use memory for working state during a task (intermediate results, IDs to reuse on the next turn). Lasting facts, preferences, and reference material belong in the [Knowledge tools](#knowledge-tools) registry, not memory.

### memory_set
Save a scratch note under a key for this chat:
```
Agent calls: memory_set(key="browser_session", value="sess_abc123")
```

### memory_get
Read the scratch note saved under a key:
```
Agent calls: memory_get(key="browser_session")
Returns: "sess_abc123"
```

### memory_getall
Read every scratch note in this chat:
```
Returns: {"browser_session": "sess_abc123", "draft_outline": "..."}
```

**Good fits:**
- Intermediate results while a multi-step task runs in one chat
- Session IDs or partial outputs to carry to the next turn

**Use knowledge instead when:**
- The agent should remember something in a **later chat** (user preferences, project facts, saved procedures)

---

## Widget Tool

Render interactive UI elements (top-level agents only):

### widget
Create UI components:
- Forms with inputs
- Buttons and actions
- Cards and layouts
- Status displays

**Example widget call:**
```json
{
  "type": "ui",
  "title": "Order Confirmation",
  "children": [
    {"type": "text", "value": "Your order is ready"},
    {"type": "badge", "label": "confirmed", "variant": "secondary"},
    {"type": "row", "children": [
      {"type": "button", "label": "Track Order", "action": "track"},
      {"type": "button", "label": "Cancel", "action": "cancel", "variant": "destructive"}
    ]}
  ]
}
```

See the Widget System documentation for full component reference.

---

## Finish Tool

Report completion with structured output. Works on all agents. When called, the output is stored on `chat.output`.

### finish
Report status and result:

```json
{
  "status": "succeeded",  // or "failed", "cancelled"
  "result": "Research complete. Found 5 key trends..."
}
```

The finish output is stored on the chat and accessible after completion. Use `run()` to get it directly:

<div data-type="codegroup">

```python Python
output = agent.run("Research AI trends")
print(output)  # {"status": "succeeded", "result": "Research complete. Found 5 key trends..."}
```

```typescript JavaScript
const output = await agent.run('Research AI trends');
console.log(output); // {"status": "succeeded", "result": "Research complete. Found 5 key trends..."}
```

</div>

**With `output_schema` for structured output:**

When `output_schema` is set on the agent config, the finish tool requires the result to match the schema. This gives you typed, predictable JSON output from your agent. See [Structured Output](/docs/api/agent/structured-output) for details.

<div data-type="codegroup">

```python Python
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "system_prompt": "Analyze the input text.",
    "output_schema": {
        "type": "object",
        "properties": {
            "summary": {"type": "string", "description": "Brief summary"},
            "confidence": {"type": "number", "description": "Confidence 0-1"}
        },
        "required": ["summary", "confidence"]
    },
    "internal_tools": { "finish": True }
})

output = agent.run("The weather is great today!")
print(output)  # {"summary": "Positive weather comment", "confidence": 0.95}
```

```typescript JavaScript
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  system_prompt: 'Analyze the input text.',
  output_schema: {
    type: 'object',
    properties: {
      summary: { type: 'string', description: 'Brief summary' },
      confidence: { type: 'number', description: 'Confidence 0-1' }
    },
    required: ['summary', 'confidence']
  },
  internal_tools: { finish: true }
});

const output = await agent.run('The weather is great today!');
console.log(output); // {"summary": "Positive weather comment", "confidence": 0.95}
```

</div>

---

## SDK builders

Use `internalTools()` / `internal_tools()` to build the `internal_tools` object on ad-hoc agents. The runtime also accepts a plain JSON object (REST and YAML agent configs).

**JavaScript (`@inferencesh/sdk`):** chain methods on `internalTools()`:

| Method | Sets |
|--------|------|
| `.plan()`, `.memory()`, `.widget()`, `.finish()`, `.skills()` | Default-on categories |
| `.meta()` | Meta tools (opt-in; available since v0.6.42) |
| `.artifact()`, `.agent()`, `.remote()`, `.knowledge()` | Opt-in categories (**v0.14.1+**; use a plain object on older SDKs) |
| `.all()` | Enables plan, memory, widget, and finish only (not opt-in categories) |
| `.none()` | Disables plan, memory, widget, and finish |

**v0.14.1** also removed `internalTools().hostContext()` (the API ignores `host_context`). The serialized field is `agent`, not `spawn`. Older stored configs may still use `"spawn": true`; the API treats it as `agent`.

**Python (`inferencesh` v0.14.1+):** chain methods on `internal_tools()` (same categories as JavaScript):

| Method | Sets |
|--------|------|
| `.plan()`, `.memory()`, `.widget()`, `.finish()`, `.skills()` | Default-on categories |
| `.meta()`, `.artifact()`, `.agent()`, `.remote()`, `.knowledge()` | Opt-in categories |
| `.all()` | Enables plan, memory, widget, and finish only (not opt-in categories) |
| `.none()` | Disables plan, memory, widget, and finish |

`InternalToolsConfig` in `inferencesh.types` uses `agent` (not `spawn`) and includes optional `knowledge`. On older Python SDKs, set opt-in flags with a plain dict: `{**internal_tools().memory().build(), "knowledge": True, "agent": True}`.

→ [JavaScript SDK — Tool Builder](/docs/api/sdk-javascript#tool-builder) · [Python SDK — Tool Builder](/docs/api/sdk-python#tool-builder)

---

## Configuration Examples

<div data-type="codegroup">

```python Python
# Disable all internal tools
internal_tools().none().build()

# Enable only memory
internal_tools().plan(False).memory(True).widget(False).build()

# Enable plan, memory, widget, and finish (does not set the opt-in categories)
internal_tools().all().build()

# Opt-in categories: set the flags in the agent config
"internal_tools": {"meta": True, "artifact": True, "agent": True, "remote": True, "knowledge": True}
```

```typescript JavaScript
// Disable all internal tools
internalTools().none().build()

// Enable only memory
internalTools().plan(false).memory(true).widget(false).build()

// Enable plan, memory, widget, and finish (does not set the opt-in categories)
internalTools().all().build()

// Opt-in categories
internalTools().meta().artifact().agent().remote().knowledge().build()
```

</div>

---

## Skills Tool

Load skill content on-demand instead of embedding everything in the system prompt.

### Overview

Skills are reusable packages of instructions and context that agents can retrieve when needed. This follows the **progressive disclosure** pattern—agents see available skills in the tool description and load full content only when required.

### skill_get

Retrieve a skill's full content:

```
Agent calls: skill_get(skill="code-review")
Returns: "# Code Review Guidelines\n\nWhen reviewing code..."
```

### Defining Skills

Skills are defined in the agent config, not in `internal_tools`:

<div data-type="codegroup">

```python Python
agent = client.agent({
    "core_app": { "ref": "infsh/claude-sonnet-4@latest" },
    "system_prompt": "You are a helpful assistant.",
    "skills": [
        {
            "name": "code-review",
            "description": "Guidelines for reviewing pull requests",
            "content": "# Code Review\n\nWhen reviewing code, check for..."
        },
        {
            "name": "api-docs",
            "description": "API documentation for the project",
            "url": "https://example.com/skills/api-docs.md"
        }
    ]
})
```

```typescript JavaScript
const agent = client.agent({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  system_prompt: 'You are a helpful assistant.',
  skills: [
    {
      name: 'code-review',
      description: 'Guidelines for reviewing pull requests',
      content: '# Code Review\n\nWhen reviewing code, check for...'
    },
    {
      name: 'api-docs',
      description: 'API documentation for the project',
      url: 'https://example.com/skills/api-docs.md'
    }
  ]
});
```

</div>

### Skill Sources

| Source | Description |
|--------|-------------|
| `content` | Inline text/markdown content |
| `url` | URL to fetch skill content from (cached per session) |

### How the Agent Sees Skills

The `skill_get` tool description includes all available skills:

```
skill_get: Retrieves the full content of a skill.

Available skills:
- code-review: Guidelines for reviewing pull requests
- api-docs: API documentation for the project

Parameters:
- skill: The name of the skill to retrieve
```

### Example Conversation

```
User: "Review this pull request"

Agent: [calls skill_get(skill="code-review")]
> # Code Review Guidelines
> When reviewing code, check for:
> 1. Security vulnerabilities
> 2. Performance issues
> ...

Agent: "Based on the code review guidelines, here's my review..."
```

**Use cases:**
- Documentation and guidelines
- Domain-specific knowledge
- Multi-step procedures
- Context that's only needed sometimes

---

## Scope Rules

| Tool Category | Top-level Agent | Sub-agent |
|--------------|-----------------|-----------|
| Plan | ✓ | ✓ |
| Memory | ✓ | ✓ |
| Widget | ✓ | ✗ |
| Finish | ✓ | ✓ |
| Skills | ✓ | ✓ |
| Meta | ✓ | ✓ |
| Artifacts | ✓ | ✓ |
| Agent | ✓ | ✓ (below depth 3) |
