mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-07 09:58:34 +00:00
docs(extensions): document management and compatibility
This commit is contained in:
parent
41bebdcdb5
commit
bbaafd0f4f
@ -33,6 +33,7 @@ Pick the row that matches what you want to accomplish next:
|
|||||||
| Add model fallbacks | [Configure Model Fallback](./guides/configure-model-fallback.md) |
|
| Add model fallbacks | [Configure Model Fallback](./guides/configure-model-fallback.md) |
|
||||||
| Enable web search | [Configure Web Search](./guides/configure-web-search.md) |
|
| Enable web search | [Configure Web Search](./guides/configure-web-search.md) |
|
||||||
| Add an MCP tool server | [Configure MCP Tools](./guides/configure-mcp-tools.md) |
|
| Add an MCP tool server | [Configure MCP Tools](./guides/configure-mcp-tools.md) |
|
||||||
|
| Install a nanobot, Pi, or OpenClaw extension | [Extensions](./extensions.md) |
|
||||||
| Generate images | [Image Generation](./image-generation.md) |
|
| Generate images | [Image Generation](./image-generation.md) |
|
||||||
| Schedule work or create a local trigger | [Automations](./automations.md) |
|
| Schedule work or create a local trigger | [Automations](./automations.md) |
|
||||||
| Understand and manage long-term memory | [Memory](./memory.md) |
|
| Understand and manage long-term memory | [Memory](./memory.md) |
|
||||||
@ -79,6 +80,8 @@ These pages explain implementation and extension points. You do not need them to
|
|||||||
| Understand source ownership and runtime flow | [Architecture](./architecture.md) |
|
| Understand source ownership and runtime flow | [Architecture](./architecture.md) |
|
||||||
| Set up a development environment | [Development](./development.md) and [CONTRIBUTING.md](../CONTRIBUTING.md) |
|
| Set up a development environment | [Development](./development.md) and [CONTRIBUTING.md](../CONTRIBUTING.md) |
|
||||||
| Add a channel package | [Channel Package Guide](./channel-package-guide.md) |
|
| Add a channel package | [Channel Package Guide](./channel-package-guide.md) |
|
||||||
|
| Publish an extension package | [Extension Authoring](./extension-authoring.md) |
|
||||||
|
| Understand the extension control plane | [Extension System](./extension-system.md) |
|
||||||
| Build the WebUI source | [WebUI Development](../webui/README.md) |
|
| Build the WebUI source | [WebUI Development](../webui/README.md) |
|
||||||
|
|
||||||
If a command or screen no longer matches these docs, please [open an issue](https://github.com/HKUDS/nanobot/issues) with your nanobot version, operating system, and the page that needs correction.
|
If a command or screen no longer matches these docs, please [open an issue](https://github.com/HKUDS/nanobot/issues) with your nanobot version, operating system, and the page that needs correction.
|
||||||
|
|||||||
@ -18,6 +18,7 @@ Use this page when you know what you want to run and need the command shape. For
|
|||||||
| Deliver a local trigger | `nanobot trigger <id> "message"` | Created first with `/trigger <name>` in the target chat/session |
|
| Deliver a local trigger | `nanobot trigger <id> "message"` | Created first with `/trigger <name>` in the target chat/session |
|
||||||
| Serve an OpenAI-compatible API | `nanobot serve` | Starts `/v1/chat/completions`, `/v1/models`, and `/health` |
|
| Serve an OpenAI-compatible API | `nanobot serve` | Starts `/v1/chat/completions`, `/v1/models`, and `/health` |
|
||||||
| Check chat channel setup | `nanobot channels status` | Useful before starting `nanobot gateway` |
|
| Check chat channel setup | `nanobot channels status` | Useful before starting `nanobot gateway` |
|
||||||
|
| Manage extension packages | `nanobot extensions list` | Install, inspect, trust, enable, and remove nanobot, Pi, or OpenClaw packages |
|
||||||
| Manage optional features | `nanobot plugins list` | Shows channels and optional capabilities you can turn on |
|
| Manage optional features | `nanobot plugins list` | Shows channels and optional capabilities you can turn on |
|
||||||
| Log in to QR/OAuth-style channels | `nanobot channels login <channel>` | Used by channels such as WhatsApp and WeChat |
|
| Log in to QR/OAuth-style channels | `nanobot channels login <channel>` | Used by channels such as WhatsApp and WeChat |
|
||||||
| Log in to OAuth model providers | `nanobot provider login <provider>` | Used by OpenAI Codex, xAI subscription, and GitHub Copilot providers |
|
| Log in to OAuth model providers | `nanobot provider login <provider>` | Used by OpenAI Codex, xAI subscription, and GitHub Copilot providers |
|
||||||
@ -248,6 +249,42 @@ nanobot channels status
|
|||||||
|
|
||||||
See [`chat-apps.md`](./chat-apps.md) for channel-specific setup.
|
See [`chat-apps.md`](./chat-apps.md) for channel-specific setup.
|
||||||
|
|
||||||
|
## Extensions
|
||||||
|
|
||||||
|
Extension installation, trust, permission grants, and enablement are separate
|
||||||
|
operations:
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---|---|
|
||||||
|
| `nanobot extensions list` | Show installed packages and activation policy |
|
||||||
|
| `nanobot extensions inspect <id>` | Show contributions, dependencies, requested permissions, and diagnostics |
|
||||||
|
| `nanobot extensions search [query]` | Search compatible packages on npm |
|
||||||
|
| `nanobot extensions search [query] --ecosystem <name>` | Filter to `nanobot`, `pi`, or `openclaw` |
|
||||||
|
| `nanobot extensions install <npm-spec>` | Install an npm package as untrusted |
|
||||||
|
| `nanobot extensions install <url> --kind git [--ref <ref>]` | Install from a Git branch, tag, or commit |
|
||||||
|
| `nanobot extensions install <path> --kind local` | Install from a local package directory |
|
||||||
|
| `nanobot extensions permissions <id> [permissions...]` | Replace the exact granted permission set; omit values to revoke all |
|
||||||
|
| `nanobot extensions trust <id>` | Approve executing the installed package |
|
||||||
|
| `nanobot extensions untrust <id>` | Revoke trust and stop activation |
|
||||||
|
| `nanobot extensions enable <id>` | Allow activation when every other gate passes |
|
||||||
|
| `nanobot extensions disable <id>` | Stop activation without uninstalling |
|
||||||
|
| `nanobot extensions uninstall <id>` | Remove the user-scope package after confirmation |
|
||||||
|
| `nanobot extensions uninstall <id> --yes` | Remove without an interactive confirmation |
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nanobot extensions install @acme/pi-review
|
||||||
|
nanobot extensions inspect pi.acme.pi-review
|
||||||
|
nanobot extensions permissions pi.acme.pi-review workspace.read
|
||||||
|
nanobot extensions trust pi.acme.pi-review
|
||||||
|
nanobot extensions enable pi.acme.pi-review
|
||||||
|
```
|
||||||
|
|
||||||
|
Installed packages live under `~/.nanobot/extensions/`. They do not execute
|
||||||
|
until trusted. See [Extensions](./extensions.md) for the safety model and
|
||||||
|
[Extension Authoring](./extension-authoring.md) for package compatibility.
|
||||||
|
|
||||||
## Optional Features
|
## Optional Features
|
||||||
|
|
||||||
Use these commands when you want nanobot to add or remove a built-in capability
|
Use these commands when you want nanobot to add or remove a built-in capability
|
||||||
|
|||||||
@ -27,6 +27,7 @@ the focused guides first and come back here for exact fields and defaults.
|
|||||||
| Configure model fallback | [`guides/configure-model-fallback.md`](./guides/configure-model-fallback.md) |
|
| Configure model fallback | [`guides/configure-model-fallback.md`](./guides/configure-model-fallback.md) |
|
||||||
| Add an OpenAI-compatible provider | [`guides/configure-openai-compatible-provider.md`](./guides/configure-openai-compatible-provider.md) |
|
| Add an OpenAI-compatible provider | [`guides/configure-openai-compatible-provider.md`](./guides/configure-openai-compatible-provider.md) |
|
||||||
| Add Langfuse observability | [`guides/configure-langfuse-observability.md`](./guides/configure-langfuse-observability.md) |
|
| Add Langfuse observability | [`guides/configure-langfuse-observability.md`](./guides/configure-langfuse-observability.md) |
|
||||||
|
| Install and govern extensions | [`extensions.md`](./extensions.md) |
|
||||||
| Secure a local AI agent | [`guides/secure-local-ai-agent.md`](./guides/secure-local-ai-agent.md) |
|
| Secure a local AI agent | [`guides/secure-local-ai-agent.md`](./guides/secure-local-ai-agent.md) |
|
||||||
| Deploy the gateway | [`guides/deploy-nanobot-gateway.md`](./guides/deploy-nanobot-gateway.md) |
|
| Deploy the gateway | [`guides/deploy-nanobot-gateway.md`](./guides/deploy-nanobot-gateway.md) |
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ the focused guides first and come back here for exact fields and defaults.
|
|||||||
| Configure web search and fetch | [Web Tools](#web-tools) |
|
| Configure web search and fetch | [Web Tools](#web-tools) |
|
||||||
| Enable image generation | [Image Generation](#image-generation) |
|
| Enable image generation | [Image Generation](#image-generation) |
|
||||||
| Add MCP servers | [MCP](#mcp-model-context-protocol) |
|
| Add MCP servers | [MCP](#mcp-model-context-protocol) |
|
||||||
|
| Configure extension discovery and policy | [Extensions](#extensions) |
|
||||||
| Review shell, workspace, and SSRF controls | [Security](#security) |
|
| Review shell, workspace, and SSRF controls | [Security](#security) |
|
||||||
| Control access and pairing | [Pairing](#pairing) |
|
| Control access and pairing | [Pairing](#pairing) |
|
||||||
| Tune gateway jobs, sessions, and tools | [Gateway Heartbeat](#gateway-heartbeat), [Auto Compact](#auto-compact), [Unified Session](#unified-session), [Tool Hint Max Length](#tool-hint-max-length) |
|
| Tune gateway jobs, sessions, and tools | [Gateway Heartbeat](#gateway-heartbeat), [Auto Compact](#auto-compact), [Unified Session](#unified-session), [Tool Hint Max Length](#tool-hint-max-length) |
|
||||||
@ -64,6 +66,7 @@ If the WebUI does not expose the option you need, start from the task below. Mos
|
|||||||
| Enable web search or fetch | `tools.web.search.*`, `tools.web.fetch.*`, optional `tools.ssrfWhitelist` | Ask a question that requires current web information, then inspect logs if needed | [Web Tools](#web-tools), [Security](#security) |
|
| Enable web search or fetch | `tools.web.search.*`, `tools.web.fetch.*`, optional `tools.ssrfWhitelist` | Ask a question that requires current web information, then inspect logs if needed | [Web Tools](#web-tools), [Security](#security) |
|
||||||
| Enable image generation | `tools.imageGeneration.enabled`, `tools.imageGeneration.provider`, `tools.imageGeneration.model`, matching provider credentials | Enable Image Generation in the WebUI and send one image request | [Image Generation](#image-generation) |
|
| Enable image generation | `tools.imageGeneration.enabled`, `tools.imageGeneration.provider`, `tools.imageGeneration.model`, matching provider credentials | Enable Image Generation in the WebUI and send one image request | [Image Generation](#image-generation) |
|
||||||
| Add external tools through MCP | `tools.mcpServers.<name>` | Start `nanobot gateway --verbose` and check startup/tool logs | [MCP](#mcp-model-context-protocol) |
|
| Add external tools through MCP | `tools.mcpServers.<name>` | Start `nanobot gateway --verbose` and check startup/tool logs | [MCP](#mcp-model-context-protocol) |
|
||||||
|
| Discover and govern extension packages | `extensions.*` | `nanobot extensions list`, then inspect the package | [Extensions](#extensions), [Extension guide](./extensions.md) |
|
||||||
| Tighten tool and network safety | `tools.restrictToWorkspace`, `tools.exec.sandbox`, `tools.ssrfWhitelist`, `channels.*.allowFrom` | Run the same workflow through the channel or CLI you plan to expose | [Security](#security), [Pairing](#pairing) |
|
| Tighten tool and network safety | `tools.restrictToWorkspace`, `tools.exec.sandbox`, `tools.ssrfWhitelist`, `channels.*.allowFrom` | Run the same workflow through the channel or CLI you plan to expose | [Security](#security), [Pairing](#pairing) |
|
||||||
| Tune request timeouts or process concurrency | `NANOBOT_LLM_TIMEOUT_S`, `NANOBOT_STREAM_IDLE_TIMEOUT_S`, `NANOBOT_MAX_CONCURRENT_REQUESTS` | Start nanobot from the same environment and inspect startup/runtime logs | [Runtime Environment Variables](#runtime-environment-variables) |
|
| Tune request timeouts or process concurrency | `NANOBOT_LLM_TIMEOUT_S`, `NANOBOT_STREAM_IDLE_TIMEOUT_S`, `NANOBOT_MAX_CONCURRENT_REQUESTS` | Start nanobot from the same environment and inspect startup/runtime logs | [Runtime Environment Variables](#runtime-environment-variables) |
|
||||||
| Run multiple isolated bots | separate `--config` and `--workspace` paths, plus distinct `gateway.port` or channel ports when processes run together | Use the same explicit paths with `nanobot status`, `agent`, `webui`, `gateway`, and `serve` | [Multiple Instances](./multiple-instances.md), [CLI Reference](./cli-reference.md) |
|
| Run multiple isolated bots | separate `--config` and `--workspace` paths, plus distinct `gateway.port` or channel ports when processes run together | Use the same explicit paths with `nanobot status`, `agent`, `webui`, `gateway`, and `serve` | [Multiple Instances](./multiple-instances.md), [CLI Reference](./cli-reference.md) |
|
||||||
@ -2228,6 +2231,53 @@ When enabled, all incoming messages — regardless of which channel they arrive
|
|||||||
|
|
||||||
> This is designed for single-user, multi-device setups. It is **off by default** — existing users see zero behavior change.
|
> This is designed for single-user, multi-device setups. It is **off by default** — existing users see zero behavior change.
|
||||||
|
|
||||||
|
## Extensions
|
||||||
|
|
||||||
|
Use the WebUI **Extensions** page or `nanobot extensions` commands for normal
|
||||||
|
installation and trust decisions. The top-level `extensions` object is for
|
||||||
|
advanced discovery and policy:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"enabled": true,
|
||||||
|
"paths": ["/opt/nanobot/extensions"],
|
||||||
|
"allow": [],
|
||||||
|
"deny": ["acme.blocked"],
|
||||||
|
"workspaceTrust": "ask",
|
||||||
|
"entries": {
|
||||||
|
"acme.review": {
|
||||||
|
"enabled": true,
|
||||||
|
"trusted": true,
|
||||||
|
"permissions": ["workspace.read"],
|
||||||
|
"config": {
|
||||||
|
"mode": "strict"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Option | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `extensions.enabled` | `true` | Enable external extension discovery and activation |
|
||||||
|
| `extensions.paths` | `[]` | Additional manifest roots; each root may be one package or contain package directories |
|
||||||
|
| `extensions.allow` | `[]` | Optional extension ID allowlist; empty allows all IDs not denied |
|
||||||
|
| `extensions.deny` | `[]` | Extension IDs that must remain inactive |
|
||||||
|
| `extensions.workspaceTrust` | `"ask"` | Workspace extension policy: `"ask"`, `"allow"`, or `"deny"` |
|
||||||
|
| `extensions.entries.<id>.enabled` | `true` | Per-extension activation switch |
|
||||||
|
| `extensions.entries.<id>.trusted` | `false` | Approve executing that extension's code |
|
||||||
|
| `extensions.entries.<id>.permissions` | `[]` | Exact host permissions granted to the extension |
|
||||||
|
| `extensions.entries.<id>.config` | `{}` | Package-owned configuration passed to its runtime |
|
||||||
|
|
||||||
|
User-installed packages and their interactive trust state live under
|
||||||
|
`~/.nanobot/extensions/`. Config policy is merged at discovery time; nanobot
|
||||||
|
does not silently rewrite `config.json` when you install a package.
|
||||||
|
|
||||||
|
See [Extensions](./extensions.md) for the safe install flow and
|
||||||
|
[Extension Authoring](./extension-authoring.md) for the package contract.
|
||||||
|
|
||||||
## Disabled Skills
|
## Disabled Skills
|
||||||
|
|
||||||
nanobot ships with built-in skills, and your workspace can also define custom skills under `skills/`. If you want to hide specific skills from the agent, set `agents.defaults.disabledSkills` to a list of skill directory names:
|
nanobot ships with built-in skills, and your workspace can also define custom skills under `skills/`. If you want to hide specific skills from the agent, set `agents.defaults.disabledSkills` to a list of skill directory names:
|
||||||
|
|||||||
294
docs/extension-authoring.md
Normal file
294
docs/extension-authoring.md
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
# Extension Authoring
|
||||||
|
|
||||||
|
nanobot extensions are packages with a strict `nanobot.extension.json`
|
||||||
|
manifest. The manifest can be inspected without importing optional SDKs or
|
||||||
|
executing package code. Runtime activation then projects each contribution into
|
||||||
|
the native nanobot registry that owns that capability.
|
||||||
|
|
||||||
|
This guide covers native Python extensions and the compatibility boundary for
|
||||||
|
Pi and OpenClaw packages. Users installing packages should read
|
||||||
|
[Extensions](./extensions.md).
|
||||||
|
|
||||||
|
## Package Layout
|
||||||
|
|
||||||
|
A minimal native package is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-extension/
|
||||||
|
|-- nanobot.extension.json
|
||||||
|
`-- my_extension/
|
||||||
|
`-- __init__.py
|
||||||
|
```
|
||||||
|
|
||||||
|
`nanobot.extension.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "acme.review",
|
||||||
|
"name": "Acme Review",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"apiVersion": 1,
|
||||||
|
"runtime": "python",
|
||||||
|
"entry": "my_extension:register",
|
||||||
|
"description": "Adds a review tool and command.",
|
||||||
|
"homepage": "https://example.com/acme-review",
|
||||||
|
"license": "MIT",
|
||||||
|
"contributions": [
|
||||||
|
{
|
||||||
|
"kind": "tool",
|
||||||
|
"name": "review_code",
|
||||||
|
"description": "Review a local change."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "command",
|
||||||
|
"name": "review",
|
||||||
|
"description": "Start a review from chat."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"kind": "python",
|
||||||
|
"name": "acme-review-core",
|
||||||
|
"specifier": ">=1,<2",
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"name": "workspace.read",
|
||||||
|
"reason": "Read files selected for review."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Unknown fields are rejected. This is deliberate: a misspelled permission,
|
||||||
|
dependency, or contribution must not silently change package behavior.
|
||||||
|
|
||||||
|
## Manifest Reference
|
||||||
|
|
||||||
|
### Top-level fields
|
||||||
|
|
||||||
|
| Field | Required | Meaning |
|
||||||
|
|---|---:|---|
|
||||||
|
| `id` | yes | Stable lowercase package identity |
|
||||||
|
| `name` | yes | Human-readable name |
|
||||||
|
| `version` | yes | Installed package version |
|
||||||
|
| `apiVersion` | no | Manifest API, currently `1` |
|
||||||
|
| `runtime` | yes | `python`, `pi`, `openclaw`, or `declarative` |
|
||||||
|
| `entry` | runtime-dependent | One activation entry |
|
||||||
|
| `entries` | runtime-dependent | Multiple Pi/OpenClaw entries; takes precedence over `entry` |
|
||||||
|
| `contributions` | no | Capabilities owned by the package |
|
||||||
|
| `dependencies` | no | Activation prerequisites |
|
||||||
|
| `permissions` | no | Privileged host capabilities requested from the user |
|
||||||
|
| `description` | no | Catalog summary |
|
||||||
|
| `homepage` | no | Project or documentation URL |
|
||||||
|
| `license` | no | SPDX-style license label |
|
||||||
|
|
||||||
|
Entries must be relative to the package root and cannot contain `..`.
|
||||||
|
|
||||||
|
### Contributions
|
||||||
|
|
||||||
|
Supported `kind` values are:
|
||||||
|
|
||||||
|
```text
|
||||||
|
tool
|
||||||
|
skill
|
||||||
|
channel
|
||||||
|
llm_provider
|
||||||
|
transcription_provider
|
||||||
|
image_generation_provider
|
||||||
|
web_search_provider
|
||||||
|
mcp_server
|
||||||
|
hook
|
||||||
|
command
|
||||||
|
webui
|
||||||
|
```
|
||||||
|
|
||||||
|
Each contribution has a stable `name`, optional runtime `target`, optional
|
||||||
|
`description`, and optional `replaces` list. `replaces` contains extension IDs,
|
||||||
|
not contribution names. Replacement is still checked against scope precedence.
|
||||||
|
|
||||||
|
The manifest declares ownership. It does not create an implementation by
|
||||||
|
itself. A runtime must register the corresponding native capability.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
| Kind | `name` identifies | Version behavior |
|
||||||
|
|---|---|---|
|
||||||
|
| `python` | Installed Python distribution | PEP 440 specifier |
|
||||||
|
| `npm` | Package under the extension's `node_modules` | npm installation constraint |
|
||||||
|
| `executable` | Command on `PATH` | No version probe |
|
||||||
|
| `environment` | Environment variable | Must be non-empty |
|
||||||
|
| `extension` | Another extension ID | Installed extension version |
|
||||||
|
|
||||||
|
Set `optional: true` when the extension can activate without the dependency.
|
||||||
|
Do not put API keys in the manifest.
|
||||||
|
|
||||||
|
### Permissions
|
||||||
|
|
||||||
|
Permission names are lowercase namespaced identifiers such as
|
||||||
|
`workspace.read`, `workspace.write`, or `network.http`. Include a concrete
|
||||||
|
reason the user can evaluate. Activation requires every requested permission to
|
||||||
|
be granted.
|
||||||
|
|
||||||
|
Permissions describe host policy; they are not an OS sandbox. Keep the request
|
||||||
|
set minimal and use native host operations when one exists.
|
||||||
|
|
||||||
|
## Native Python Runtime
|
||||||
|
|
||||||
|
The Python entry is `module[:attribute]`; the default attribute is `register`.
|
||||||
|
The function is synchronous and must return `None`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from nanobot.agent.tools.base import Tool
|
||||||
|
|
||||||
|
|
||||||
|
class ReviewTool(Tool):
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "review_code"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return "Review a local code change."
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parameters(self) -> dict:
|
||||||
|
return {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {"type": "string"},
|
||||||
|
},
|
||||||
|
"required": ["path"],
|
||||||
|
}
|
||||||
|
|
||||||
|
async def execute(self, path: str) -> str:
|
||||||
|
return f"Review requested for {path}"
|
||||||
|
|
||||||
|
|
||||||
|
def register(api) -> None:
|
||||||
|
api.register_tool(ReviewTool())
|
||||||
|
```
|
||||||
|
|
||||||
|
The v1 Python API exposes:
|
||||||
|
|
||||||
|
- `register_tool(tool)`
|
||||||
|
- `register_command(command, handler, prefix=False)`
|
||||||
|
- `register_hook_factory(factory)`
|
||||||
|
|
||||||
|
These methods use existing nanobot registries. Do not import or patch
|
||||||
|
`AgentLoop`, reach into WebSocket internals, or create a second tool registry.
|
||||||
|
If a new contribution kind needs execution support, add an adapter at the
|
||||||
|
native subsystem boundary and keep the manifest API independent from that
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
## Publish and Discover
|
||||||
|
|
||||||
|
For npm discovery, include the exact `nanobot-extension` keyword:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"keywords": [
|
||||||
|
"nanobot-extension"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Compatibility packages use their upstream metadata and keyword:
|
||||||
|
|
||||||
|
- Pi: `pi-package` plus `pi.extensions`
|
||||||
|
- OpenClaw: `openclaw-plugin` plus `openclaw.extensions` or
|
||||||
|
`openclaw.runtimeExtensions`
|
||||||
|
|
||||||
|
Native Python packages can be installed from a local directory or Git source.
|
||||||
|
The market is an index; installation always passes through local validation,
|
||||||
|
dependency checks, permission review, trust, and activation.
|
||||||
|
|
||||||
|
## Compatibility Matrix
|
||||||
|
|
||||||
|
The matrix describes the current adapter, not an intention to support every
|
||||||
|
upstream API.
|
||||||
|
|
||||||
|
| Upstream capability | Pi | OpenClaw | nanobot behavior |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Tool registration and calls | Executable | Executable | Projected as native `Tool`; implementation stays in Node sidecar |
|
||||||
|
| Slash commands | Executable | Executable | Registered in native command router |
|
||||||
|
| Supported lifecycle events | Observation-only | Observation-only | Receives serialized run/tool events; cannot mutate native context |
|
||||||
|
| Provider registration | Metadata only | Metadata only | Visible in catalog and diagnostics; not an executable provider |
|
||||||
|
| Transcription/image/web-search provider contracts | N/A | Metadata only | Visible but not projected into native provider registries |
|
||||||
|
| Skills declared in plugin metadata | N/A | Metadata only | Catalog ownership only; runtime skill installation is not synthesized |
|
||||||
|
| Channels | N/A | Metadata only | No OpenClaw channel host emulation |
|
||||||
|
| Session tree and custom entries | Unsupported | Unsupported | No compatible host surface |
|
||||||
|
| Terminal UI, widgets, renderers, shortcuts | Unsupported | Unsupported | WebUI and CLI have different rendering contracts |
|
||||||
|
| Model selection and thinking control | Unsupported | Unsupported | Remains owned by nanobot model presets and request policy |
|
||||||
|
| Arbitrary upstream host services | Unsupported | Unsupported | Reported as diagnostics rather than silently emulated |
|
||||||
|
|
||||||
|
### Pi package shape
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@acme/pi-review",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"keywords": ["pi-package"],
|
||||||
|
"pi": {
|
||||||
|
"extensions": ["./index.ts"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The sidecar supports `registerTool`, `registerCommand`, and selected `on(...)`
|
||||||
|
lifecycle handlers. TypeScript uses Node's native type stripping when
|
||||||
|
available, with `jiti` as a fallback installed with the package runtime.
|
||||||
|
|
||||||
|
### OpenClaw package shape
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@acme/openclaw-review",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"keywords": ["openclaw-plugin"],
|
||||||
|
"openclaw": {
|
||||||
|
"runtimeExtensions": ["./dist/index.js"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If present, `openclaw.plugin.json` supplies catalog identity, contribution
|
||||||
|
contracts, command aliases, and compatibility diagnostics. The OpenClaw
|
||||||
|
`register` function must complete synchronously during load.
|
||||||
|
|
||||||
|
## Test an Extension
|
||||||
|
|
||||||
|
Use an isolated config and workspace while developing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nanobot extensions install ./my-extension --kind local
|
||||||
|
nanobot extensions inspect acme.review
|
||||||
|
nanobot extensions permissions acme.review workspace.read
|
||||||
|
nanobot extensions trust acme.review
|
||||||
|
nanobot extensions enable acme.review
|
||||||
|
nanobot agent -m "Use review_code on README.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
Also test:
|
||||||
|
|
||||||
|
- install while untrusted does not execute code;
|
||||||
|
- missing hard dependencies leave the package inactive;
|
||||||
|
- denied or missing permissions prevent activation;
|
||||||
|
- duplicate contribution names become diagnostics;
|
||||||
|
- disable, untrust, reload, and uninstall remove runtime registrations;
|
||||||
|
- one broken package does not block unrelated packages.
|
||||||
|
|
||||||
|
For nanobot itself, extension tests live under `tests/extensions/`. Keep
|
||||||
|
compatibility fixtures small and assert diagnostics for unsupported APIs.
|
||||||
|
|
||||||
|
## Design Rules
|
||||||
|
|
||||||
|
1. Extend the control plane, not the core loop.
|
||||||
|
2. Keep one native owner for each capability.
|
||||||
|
3. Make discovery metadata-only.
|
||||||
|
4. Separate install, permission grant, trust, and enable.
|
||||||
|
5. Report partial compatibility honestly.
|
||||||
|
6. Keep market metadata independent from runtime execution.
|
||||||
|
7. Roll back all registrations owned by a failed or unloaded extension.
|
||||||
@ -1,5 +1,9 @@
|
|||||||
# Extension system
|
# Extension system
|
||||||
|
|
||||||
|
This page documents the maintainer-facing architecture. For installation and
|
||||||
|
operations, read [Extensions](./extensions.md). For the package contract, read
|
||||||
|
[Extension Authoring](./extension-authoring.md).
|
||||||
|
|
||||||
nanobot treats an extension as an installable, governable unit and a
|
nanobot treats an extension as an installable, governable unit and a
|
||||||
contribution as one capability supplied by that unit. This distinction keeps
|
contribution as one capability supplied by that unit. This distinction keeps
|
||||||
the agent core small without forcing tools, channels, providers, skills, MCP
|
the agent core small without forcing tools, channels, providers, skills, MCP
|
||||||
@ -77,6 +81,10 @@ Compatibility is capability-based rather than all-or-nothing:
|
|||||||
- Plugin failures are isolated from the agent process and produce actionable
|
- Plugin failures are isolated from the agent process and produce actionable
|
||||||
diagnostics.
|
diagnostics.
|
||||||
|
|
||||||
|
The compatibility sidecar is a failure-isolation boundary, not a security
|
||||||
|
sandbox. The exact executable and metadata-only surfaces are listed in the
|
||||||
|
[compatibility matrix](./extension-authoring.md#compatibility-matrix).
|
||||||
|
|
||||||
## Security model
|
## Security model
|
||||||
|
|
||||||
Extensions are trusted code, not prompts or static skills. Installation and
|
Extensions are trusted code, not prompts or static skills. Installation and
|
||||||
@ -99,6 +107,16 @@ per-extension enablement, package-owned config, and workspace trust. Discovery
|
|||||||
does not import extension code. Installation does not imply workspace trust,
|
does not import extension code. Installation does not imply workspace trust,
|
||||||
and activation does not rewrite `config.json` behind the user's back.
|
and activation does not rewrite `config.json` behind the user's back.
|
||||||
|
|
||||||
|
The activation gates are deliberately independent:
|
||||||
|
|
||||||
|
```text
|
||||||
|
installed -> dependencies ready -> permissions granted -> trusted + enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
Only candidates that pass every gate own active contributions. Reload first
|
||||||
|
rolls back registrations by extension owner and then activates the new
|
||||||
|
snapshot. A failed activation is converted into a diagnostic.
|
||||||
|
|
||||||
## Market boundary
|
## Market boundary
|
||||||
|
|
||||||
The market is an index, not a runtime. It describes packages available from
|
The market is an index, not a runtime. It describes packages available from
|
||||||
@ -107,3 +125,35 @@ shape. Installing a listing still goes through the local installer, policy,
|
|||||||
dependency checks, and trust flow. This keeps discovery independent from code
|
dependency checks, and trust flow. This keeps discovery independent from code
|
||||||
execution and allows multiple catalogs without coupling the agent to one
|
execution and allows multiple catalogs without coupling the agent to one
|
||||||
store.
|
store.
|
||||||
|
|
||||||
|
## Ownership boundaries
|
||||||
|
|
||||||
|
The extension package owns identity, policy, and contribution declarations.
|
||||||
|
Native subsystems continue to own execution:
|
||||||
|
|
||||||
|
| Concern | Owner |
|
||||||
|
|---|---|
|
||||||
|
| Package identity, source, trust, permissions | `nanobot.extensions` |
|
||||||
|
| Tool execution contract | `nanobot.agent.tools` |
|
||||||
|
| Commands | `nanobot.command` |
|
||||||
|
| Agent lifecycle hooks | `nanobot.agent.hook` |
|
||||||
|
| Providers | `nanobot.providers` |
|
||||||
|
| Channels | `nanobot.channels` |
|
||||||
|
| Skills | `nanobot.skills` |
|
||||||
|
| Browser management surface | `webui` |
|
||||||
|
|
||||||
|
Do not add extension discovery or compatibility branching to `AgentLoop`.
|
||||||
|
Runtime assembly creates an `ExtensionHost`, projects supported registrations
|
||||||
|
through native APIs, and closes the host with the surrounding runtime.
|
||||||
|
|
||||||
|
## Protocol boundary
|
||||||
|
|
||||||
|
Pi and OpenClaw entries run behind a versioned NDJSON request/response protocol.
|
||||||
|
The Python process sends load, call, lifecycle event, and close requests. The
|
||||||
|
Node process returns registrations, results, outputs, and diagnostics. Protocol
|
||||||
|
messages contain JSON-compatible values only.
|
||||||
|
|
||||||
|
The adapter must reject malformed messages, time out stalled requests, and
|
||||||
|
close the sidecar when activation fails. Unsupported upstream methods are
|
||||||
|
either explicit no-ops with diagnostics or rejected; they must never be
|
||||||
|
reported as executable contributions.
|
||||||
|
|||||||
189
docs/extensions.md
Normal file
189
docs/extensions.md
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# Extensions
|
||||||
|
|
||||||
|
Extensions add capabilities to nanobot without modifying the agent loop. One
|
||||||
|
extension package can contribute tools, commands, hooks, skills, channels,
|
||||||
|
providers, MCP servers, or WebUI surfaces. nanobot also recognizes compatible
|
||||||
|
Pi packages and OpenClaw plugins, with capability-by-capability diagnostics.
|
||||||
|
|
||||||
|
Use this page to install and manage extensions. To publish one, read
|
||||||
|
[Extension Authoring](./extension-authoring.md). For the internal design, read
|
||||||
|
[Extension System](./extension-system.md).
|
||||||
|
|
||||||
|
## Install Safely
|
||||||
|
|
||||||
|
The WebUI **Extensions** page and the `nanobot extensions` commands use the same
|
||||||
|
local extension store. The safe lifecycle is:
|
||||||
|
|
||||||
|
1. Find or install a package.
|
||||||
|
2. Inspect its source, runtime, dependencies, requested permissions, and
|
||||||
|
diagnostics.
|
||||||
|
3. Grant only the permissions you understand.
|
||||||
|
4. Mark the package trusted.
|
||||||
|
5. Enable it.
|
||||||
|
|
||||||
|
Installation does not grant trust. An installed package remains visible but
|
||||||
|
inactive until it is enabled, trusted, has all requested permissions, and
|
||||||
|
passes dependency checks.
|
||||||
|
|
||||||
|
### WebUI
|
||||||
|
|
||||||
|
Open **Extensions** in the sidebar:
|
||||||
|
|
||||||
|
- **Installed** manages packages in `~/.nanobot/extensions/`.
|
||||||
|
- **Discover** searches npm packages marked for nanobot, Pi, or OpenClaw.
|
||||||
|
- **Built in** shows native nanobot capabilities projected into the same
|
||||||
|
catalog. Built-in entries are informational and are not uninstallable.
|
||||||
|
|
||||||
|
Select an entry to see its contributions, dependencies, permission reasons,
|
||||||
|
source, compatibility notices, and activation errors. Trust and permission
|
||||||
|
controls are intentionally separate.
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search all supported npm ecosystems
|
||||||
|
nanobot extensions search "memory"
|
||||||
|
|
||||||
|
# Search one compatibility ecosystem
|
||||||
|
nanobot extensions search "review" --ecosystem pi
|
||||||
|
|
||||||
|
# Install without trusting the package
|
||||||
|
nanobot extensions install @scope/package
|
||||||
|
|
||||||
|
# Inspect before activation
|
||||||
|
nanobot extensions inspect pi.scope.package
|
||||||
|
|
||||||
|
# Grant exactly the requested host permissions
|
||||||
|
nanobot extensions permissions pi.scope.package network.http workspace.read
|
||||||
|
|
||||||
|
# Trust and enable
|
||||||
|
nanobot extensions trust pi.scope.package
|
||||||
|
nanobot extensions enable pi.scope.package
|
||||||
|
```
|
||||||
|
|
||||||
|
Install from Git or a local directory when the package is not published:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nanobot extensions install https://github.com/acme/nanobot-tool.git --kind git
|
||||||
|
nanobot extensions install ./my-extension --kind local
|
||||||
|
```
|
||||||
|
|
||||||
|
Local-directory installation is available only to local WebUI requests. Remote
|
||||||
|
browser clients cannot ask the gateway to read an arbitrary server path.
|
||||||
|
|
||||||
|
## Sources and Scopes
|
||||||
|
|
||||||
|
Extensions can come from three scopes:
|
||||||
|
|
||||||
|
| Scope | Typical source | Behavior |
|
||||||
|
|---|---|---|
|
||||||
|
| Built in | nanobot package | Trusted by construction; shown for ownership and diagnostics |
|
||||||
|
| User | `~/.nanobot/extensions/` | Installed and governed through the WebUI or CLI |
|
||||||
|
| Workspace | `<workspace>/.nanobot/extensions/` | Project-local code; controlled by workspace trust policy |
|
||||||
|
|
||||||
|
When the same extension ID exists in multiple scopes, the nearest scope wins:
|
||||||
|
workspace over user, user over built in. A contribution cannot silently replace
|
||||||
|
another extension's contribution. Explicit replacement metadata and sufficient
|
||||||
|
scope are required.
|
||||||
|
|
||||||
|
## Pi and OpenClaw Packages
|
||||||
|
|
||||||
|
nanobot reads native Pi and OpenClaw package metadata and runs supported
|
||||||
|
JavaScript or TypeScript entries in a Node.js sidecar. Compatibility is not
|
||||||
|
all-or-nothing:
|
||||||
|
|
||||||
|
- Tools, slash commands, and supported lifecycle observation hooks can run.
|
||||||
|
- Provider-like registrations and several host-specific capabilities may be
|
||||||
|
cataloged but not executable.
|
||||||
|
- Terminal UI, session-tree, renderer, shortcut, and model-control APIs do not
|
||||||
|
have equivalent nanobot host surfaces.
|
||||||
|
- Every degraded or unsupported registration appears in diagnostics.
|
||||||
|
|
||||||
|
Check the [compatibility matrix](./extension-authoring.md#compatibility-matrix)
|
||||||
|
before depending on a package. A package appearing in search results means its
|
||||||
|
metadata is recognizable, not that every upstream API is implemented.
|
||||||
|
|
||||||
|
## Trust and Permissions
|
||||||
|
|
||||||
|
An extension is executable code. Review it with the same care as a Python or npm
|
||||||
|
dependency.
|
||||||
|
|
||||||
|
- **Enabled** says the extension may activate.
|
||||||
|
- **Trusted** says you approve executing its code.
|
||||||
|
- **Granted permissions** are exact host capabilities approved for that
|
||||||
|
extension.
|
||||||
|
- **Dependencies** must be present before activation.
|
||||||
|
|
||||||
|
Permissions are host policy, not an operating-system sandbox. A trusted native
|
||||||
|
Python extension executes in the nanobot process. A Pi or OpenClaw extension
|
||||||
|
executes in a separate Node.js process, which improves failure isolation but is
|
||||||
|
not a strong OS security boundary. Use containers or another OS sandbox for
|
||||||
|
untrusted third-party code.
|
||||||
|
|
||||||
|
npm installation uses lifecycle scripts disabled. This prevents package
|
||||||
|
`preinstall` and `postinstall` scripts from running during installation, but the
|
||||||
|
extension entry itself will run after you explicitly trust and enable it.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Most users should manage installed packages in the WebUI or CLI. Advanced
|
||||||
|
deployments can also define extension policy in `~/.nanobot/config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"enabled": true,
|
||||||
|
"paths": ["/opt/nanobot/extensions"],
|
||||||
|
"allow": [],
|
||||||
|
"deny": ["acme.blocked"],
|
||||||
|
"workspaceTrust": "ask",
|
||||||
|
"entries": {
|
||||||
|
"acme.review": {
|
||||||
|
"enabled": true,
|
||||||
|
"trusted": true,
|
||||||
|
"permissions": ["workspace.read"],
|
||||||
|
"config": {
|
||||||
|
"mode": "strict"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Config entries do not rewrite the installation registry. See
|
||||||
|
[Configuration](./configuration.md#extensions) for exact fields.
|
||||||
|
|
||||||
|
## Diagnose an Inactive Extension
|
||||||
|
|
||||||
|
Start with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nanobot extensions list
|
||||||
|
nanobot extensions inspect <extension-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
Common causes:
|
||||||
|
|
||||||
|
| State or diagnostic | What to do |
|
||||||
|
|---|---|
|
||||||
|
| Untrusted | Review the package, then use `trust` |
|
||||||
|
| Requested permission pending | Grant the exact requested permission set |
|
||||||
|
| Disabled | Use `enable` or remove it from `extensions.deny` |
|
||||||
|
| Missing dependency | Install the named package, executable, environment variable, or extension |
|
||||||
|
| Contribution conflict | Disable one owner or use an explicit replacement from an appropriate scope |
|
||||||
|
| Compatibility notice | Read which upstream API was translated, degraded, or unsupported |
|
||||||
|
| Activation failed | Check the package entry, runtime dependency, and gateway logs |
|
||||||
|
|
||||||
|
Policy changes reload the active extension host. A broken extension becomes a
|
||||||
|
diagnostic and does not prevent unrelated extensions from being discovered.
|
||||||
|
|
||||||
|
## Remove an Extension
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nanobot extensions disable <extension-id>
|
||||||
|
nanobot extensions uninstall <extension-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
Uninstall removes the user-scope package and its local policy record. It does
|
||||||
|
not remove a built-in capability or a separately configured workspace copy.
|
||||||
Loading…
x
Reference in New Issue
Block a user