Skip to content

Example Plugins

Dev edited this page Sep 9, 2026 · 1 revision

English · 中文

Example plugins

Tracks v0.12.0 (8dd8ae9). Every tool, command, event and grant below was read out of the plugin sources, not remembered. The sources are under examples/ in the repo — if this page disagrees with them, the repo wins.


The five, and which one you want

Three of them demonstrate the mechanism. Two are meant to be installed and used.

Plugin What it is for Tools Commands Events
wasm-plugin-minimal The skeleton to copy. The smallest plugin that still exercises a capability gate — one pure tool, one gated one, split into "boilerplate" and "your part" by a single marker comment. greet, fetch_head — —
wasm-plugin The reference. Four tools, slash commands, and all three command actions (print / send_user_message / error). wordcount, readfile, fetch, rot13 /todo, /explain —
wasm-plugin-events Event subscription, and the two-list grant: the plugin's list-events and the config's events must agree, or nothing is delivered. Also carries the trap/spin fixtures the hang-breaker tests use. events_seen, busy, greet (none — an [] stub, the ladder's cost) tool_execution_start, turn_start, input
wasm-plugin-memory Durable project memory. Facts become markdown files under .nanopi/memory/ that you can read, edit, diff and commit. Only the compact index enters the model's context; it calls recall for the full text of whatever looks relevant. remember, recall, forget /memory —
wasm-plugin-report Where the time went. Per-tool call counts, duration and failures, kept across sessions. Exists because tool_execution_end carries a duration_ms that nothing else persists — so "why was that slow" and "what keeps failing" otherwise have no answer at all. tool_stats /tools-report tool_execution_end, session_start

Start from wasm-plugin-minimal. It is the one designed to be copied: everything above its YOUR TOOLS marker is boilerplate you keep verbatim, everything below it is yours. Read wasm-plugin next when you need commands or a second capability.

Read wasm-plugin-memory and wasm-plugin-report if you want to see the capabilities actually used rather than demonstrated. They are deliberately complementary: between the two of them they exercise the whole outbound surface (see Plugin Capabilities).

Grants each one needs

A plugin reaches nothing it was not granted. These go in its [[extensions]] entry; each plugin's own README spells out the exact block.

Plugin Grants Host imports it calls
wasm-plugin-minimal allow_network + url_allowlist host-log, host-http-get
wasm-plugin allow_fs, allow_network + url_allowlist host-log, host-fs-read, host-http-get
wasm-plugin-events events = [...] host-log
wasm-plugin-memory allow_fs, allow_tools = ["write"], allow_context host-log, host-fs-read, host-notify, host-set-context, host-call-tool
wasm-plugin-report allow_store, events = [...] host-log, host-notify, host-store-get, host-store-set

Two details in that table are easy to get wrong:

  • wasm-plugin-minimal does not need allow_fs, even though its source declares host-fs-read. The declaration sits behind #[allow(dead_code)] and nothing calls it, so LTO drops the import and it is absent from the built world. wasm-tools component wit on the component is the way to check what a plugin really imports — the source can over-declare.
  • host-notify needs no grant. It is output, not access: it reaches nothing and reads nothing. It is bounded by a per-turn rate limit instead, and the host applies the attribution prefix so one plugin cannot pose as another.

Worlds and builds

The WIT worlds are a linear ladder — extension ⊂ extension-commands ⊂ extension-events — so a plugin targets the lowest one that covers what it exports. Targeting a higher world than you need means owing stub exports for the parts you do not use.

Plugin World Build
wasm-plugin-minimal extension no make target — by hand, see its README
wasm-plugin extension-commands make plugin
wasm-plugin-events extension-events make plugin-events
wasm-plugin-memory extension-commands make plugin-memory
wasm-plugin-report extension-events make plugin-report

Always build from the repo root. wasm-tools component embed wit/ resolves wit/ against the working directory, so building from inside an example silently embeds a stale module. Output lands in dist/.

The host needs --features wasm; the stock release binary has no WASM runtime and will tell you so — it prints how many [[extensions]] entries it ignored rather than failing silently.

Shell hooks are the other half, not the lesser one

examples/hooks/ holds scripts for [[hooks.*]] — check-rm-rf.sh, protected-paths.sh, redact-secrets.sh, dirty-repo-guard.sh, audit-log.sh, session-log.sh. See Hooks.

Two things only a hook can do, and both are design decisions rather than gaps:

  • Veto. A hook can block or rewrite a tool call. Plugins observe events and cannot refuse them. If you want to stop something from happening, it is a hook.
  • Never miss an event. Hooks are synchronous. Plugin event delivery is drop-on-busy and does not queue — a plugin already inside a guest call misses the next event entirely. So anything that has to be complete belongs in a hook. wasm-plugin-report says exactly this in its own output, and prints ≥ on its counts, because a statistic that quietly undercounts is worse than one that admits it.

Not here on purpose

tests/fixtures/runaway-plugin/ is a plugin whose execute-tool never returns, used to prove the epoch hang-breaker fires. Its own header says why it is not an example: "Deliberately NOT in examples/ — nothing here is worth copying." A directory whose other READMEs say "copy this as a starting point" is the wrong home for a component that hangs.

See also

Clone this wiki locally