mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-04 16:38:49 +00:00
168 lines
7.3 KiB
Markdown
168 lines
7.3 KiB
Markdown
# 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
|
|
contribution as one capability supplied by that unit. This distinction keeps
|
|
the agent core small without forcing tools, channels, providers, skills, MCP
|
|
servers, hooks, commands, and WebUI code into one artificial runtime interface.
|
|
|
|
## Architecture
|
|
|
|
The extension platform is a control plane over existing native registries:
|
|
|
|
```text
|
|
package / workspace directory / compatibility package
|
|
|
|
|
v
|
|
ExtensionManifest
|
|
|
|
|
v
|
|
ExtensionRegistry
|
|
selection, policy, ownership
|
|
|
|
|
+----------------+----------------+
|
|
| | |
|
|
v v v
|
|
native adapters Pi adapter OpenClaw adapter
|
|
| | |
|
|
+----------------+----------------+
|
|
|
|
|
v
|
|
executable native adapters + inspectable metadata
|
|
```
|
|
|
|
`ExtensionManifest` is dependency-free metadata. Discovery can inspect it
|
|
without importing optional SDKs or executing plugin code. `ExtensionRegistry`
|
|
selects the active installation, applies allow/deny policy, and resolves
|
|
contribution ownership. Runtime adapters activate only the contributions the
|
|
host supports.
|
|
|
|
Packages expose this metadata as `nanobot.extension.json`. The same canonical
|
|
JSON shape is used on disk, over the Node sidecar protocol, and in market
|
|
indexes. Unknown fields are rejected so a misspelled permission or contribution
|
|
cannot silently change behavior.
|
|
|
|
The agent loop does not discover or execute plugins. Assembly code resolves
|
|
extensions before constructing the runtime and passes native tools, hooks, and
|
|
other contributions through the interfaces those subsystems already expose.
|
|
|
|
## Identity and precedence
|
|
|
|
An extension ID is stable across installations. The same ID may exist in three
|
|
scopes:
|
|
|
|
1. `builtin`
|
|
2. `user`
|
|
3. `workspace`
|
|
|
|
The nearest policy-eligible scope wins for the same extension ID. A disabled,
|
|
untrusted, denied, or invalid higher-scope copy does not shadow an eligible
|
|
lower copy. Different extensions may not take over the same contribution name.
|
|
Conflicts become diagnostics instead of crashing unrelated extensions.
|
|
Extension API v1 deliberately has no override mechanism because runtime
|
|
replacement must be both transactional and reversible.
|
|
|
|
## Compatibility runtimes
|
|
|
|
Pi and OpenClaw extensions are JavaScript or TypeScript programs, so Python
|
|
cannot import them as native nanobot modules. Compatibility runs them in a
|
|
Node.js sidecar and projects supported registrations into nanobot's native
|
|
registries over a versioned protocol.
|
|
|
|
Compatibility is capability-based rather than all-or-nothing:
|
|
|
|
- A package may load while one unsupported contribution is disabled.
|
|
- Inspection reports every supported, translated, degraded, and unsupported
|
|
contribution.
|
|
- UI- or host-specific behavior is never reported as working when nanobot
|
|
cannot provide the required host interface.
|
|
- Plugin failures are isolated from the agent process and produce actionable
|
|
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).
|
|
Generated Pi and OpenClaw manifests always request `runtime.node`, so process
|
|
execution is visible and requires explicit consent even though that permission
|
|
is not an OS-level confinement mechanism.
|
|
|
|
## Security model
|
|
|
|
Extensions are trusted code, not prompts or static skills. Installation and
|
|
activation are separate actions. The host records source, version, requested
|
|
permissions, dependency state, and trust scope before executing code.
|
|
|
|
Project-local extensions require workspace trust. Contribution conflicts never
|
|
grant an implicit override. Secrets remain in nanobot provider or host config
|
|
unless the operator explicitly passes values through extension config. Native
|
|
Python code and compatible Node processes are trusted code and may still
|
|
inspect their process environment or filesystem directly; permission
|
|
declarations are review and activation gates, not technical confinement.
|
|
Existing workspace, network, SSRF, and shell restrictions apply only when an
|
|
extension uses host-provided operations.
|
|
|
|
Untrusted packages remain visible in the catalog with an inactive state. They
|
|
do not own active contributions and their runtime is not imported. Built-in
|
|
capabilities are trusted by construction; installed and workspace packages
|
|
need an explicit trusted entry or an allowed workspace trust policy.
|
|
|
|
The root `extensions` config controls explicit search paths, allow/deny policy,
|
|
per-extension enablement, package-owned config, and workspace trust. Discovery
|
|
does not import extension code. Installation does not imply workspace trust,
|
|
and activation does not rewrite `config.json` behind the user's back.
|
|
|
|
The activation gates are deliberately independent:
|
|
|
|
```text
|
|
installed -> integrity verified -> active 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
|
|
|
|
The market is an index, not a runtime. Extension API v1 searches npm for
|
|
nanobot, Pi, and OpenClaw package keywords. Git and local directories are
|
|
install sources but are not searchable catalogs. Installing a listing still
|
|
goes through the local installer, integrity record, policy, dependency checks,
|
|
and trust flow. The boundary permits more catalog adapters later without
|
|
coupling package discovery to execution.
|
|
|
|
## 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.
|