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:
1from inferencesh import inference, internal_tools23client = inference(api_key="inf_...")4agent = client.agent({5 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },6 "internal_tools": internal_tools()7 .plan(True)8 .memory(True)9 .widget(True)10 .build()11})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 (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. 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, the response has one entry per function, each with its own
input_schema. The default function is listed first and keeps the barenamespace/nameref. Other functions are namednamespace/name:function. Ifnamesalready includes a function ref (for examplegoogle/gmail:send_message), only that function is returned. - For agents,
list_toolsreturns the sub-agent call schema (task,context,images), which is whatexecute_toolexpects. It does not return the agent's configuration. - For skills and knowledge,
list_toolsreturns the content indescription.
Typical flow (app):
1Agent: [calls search_tools(query="gmail")]2> matches: { name: "google/gmail", source: "app", description: "..." }34Agent: [calls list_tools(names=["google/gmail"], kind="app")]5> google/gmail: input_schema { q: string }6> google/gmail:list_messages: input_schema { max_results: integer }7> google/gmail:send_message: input_schema { to: string }89Agent: [calls execute_tool(name="google/gmail:send_message", kind="app", arguments={ to: "..." })]10> task outputTypical flow (agent delegation):
1Agent: [calls search_tools(query="code review", kind="agent")]2> matches: { name: "acme/reviewer", source: "agent", description: "..." }34Agent: [calls list_tools(names=["acme/reviewer"], kind="agent")]5> acme/reviewer: input_schema { task, context, images }67Agent: [calls execute_tool(name="acme/reviewer", kind="agent", arguments={ task: "Review the diff", context: "..." })]8> sub-agent resultexecute_tool resolves name in this order:
- Tools already declared on the agent config
- 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.
1agent = client.agent({2 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },3 "system_prompt": "Discover and run platform apps as needed.",4 "internal_tools": {"meta": True},5})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 for publishing, visibility, and sharing outside the agent runtime.
1agent = client.agent({2 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },3 "internal_tools": {"artifact": True},4})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.
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.
1agent = client.agent({2 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },3 "internal_tools": {"agent": True},4})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. The same exec-run path backs these tools as belt remote exec and POST /remotes/{id}/execs. 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 (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 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.
1agent = client.agent({2 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },3 "internal_tools": {"remote": True},4})→ Remotes guide · Remotes API · Exec runs API
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.
1agent = client.agent({2 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },3 "internal_tools": {"knowledge": True},4})Plan Tools
Help agents organize multi-step tasks:
plan_create
Create a new plan with steps:
1Agent creates: ["Research topic", "Write outline", "Draft content"]plan_update
Update step status (pending, in_progress, completed, failed):
1Agent marks step 1 as completedplan_load
Load the current plan to see progress.
Example conversation:
1User: "Write me a blog post about AI"23Agent: [calls plan_create with steps]4> Plan created:5> [ ] Research AI trends6> [ ] Create outline 7> [ ] Write draft8> [ ] Review and polish910Agent: [calls plan_update to mark step 1 in progress]11> Plan updated:12> [/] Research AI trends ← in progress13> [ ] Create outline14> ...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 registry, not memory.
memory_set
Save a scratch note under a key for this chat:
1Agent calls: memory_set(key="browser_session", value="sess_abc123")memory_get
Read the scratch note saved under a key:
1Agent calls: memory_get(key="browser_session")2Returns: "sess_abc123"memory_getall
Read every scratch note in this chat:
1Returns: {"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:
1{2 "type": "ui",3 "title": "Order Confirmation",4 "children": [5 {"type": "text", "value": "Your order is ready"},6 {"type": "badge", "label": "confirmed", "variant": "secondary"},7 {"type": "row", "children": [8 {"type": "button", "label": "Track Order", "action": "track"},9 {"type": "button", "label": "Cancel", "action": "cancel", "variant": "destructive"}10 ]}11 ]12}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:
1{2 "status": "succeeded", // or "failed", "cancelled"3 "result": "Research complete. Found 5 key trends..."4}The finish output is stored on the chat and accessible after completion. Use run() to get it directly:
1output = agent.run("Research AI trends")2print(output) # {"status": "succeeded", "result": "Research complete. Found 5 key trends..."}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 for details.
1agent = client.agent({2 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },3 "system_prompt": "Analyze the input text.",4 "output_schema": {5 "type": "object",6 "properties": {7 "summary": {"type": "string", "description": "Brief summary"},8 "confidence": {"type": "number", "description": "Confidence 0-1"}9 },10 "required": ["summary", "confidence"]11 },12 "internal_tools": { "finish": True }13})1415output = agent.run("The weather is great today!")16print(output) # {"summary": "Positive weather comment", "confidence": 0.95}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 · Python SDK — Tool Builder
Configuration Examples
1# Disable all internal tools2internal_tools().none().build()34# Enable only memory5internal_tools().plan(False).memory(True).widget(False).build()67# Enable plan, memory, widget, and finish (does not set the opt-in categories)8internal_tools().all().build()910# Opt-in categories: set the flags in the agent config11"internal_tools": {"meta": True, "artifact": True, "agent": True, "remote": True, "knowledge": True}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:
1Agent calls: skill_get(skill="code-review")2Returns: "# Code Review Guidelines\n\nWhen reviewing code..."Defining Skills
Skills are defined in the agent config, not in internal_tools:
1agent = client.agent({2 "core_app": { "ref": "infsh/claude-sonnet-4@latest" },3 "system_prompt": "You are a helpful assistant.",4 "skills": [5 {6 "name": "code-review",7 "description": "Guidelines for reviewing pull requests",8 "content": "# Code Review\n\nWhen reviewing code, check for..."9 },10 {11 "name": "api-docs",12 "description": "API documentation for the project",13 "url": "https://example.com/skills/api-docs.md"14 }15 ]16})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:
1skill_get: Retrieves the full content of a skill.23Available skills:4- code-review: Guidelines for reviewing pull requests5- api-docs: API documentation for the project67Parameters:8- skill: The name of the skill to retrieveExample Conversation
1User: "Review this pull request"23Agent: [calls skill_get(skill="code-review")]4> # Code Review Guidelines5> When reviewing code, check for:6> 1. Security vulnerabilities7> 2. Performance issues8> ...910Agent: "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) |