AiAgent WASM Plugin Development

Write a resident WASM plugin on the AI agent: subscribe to TCP messages, use aia_* host functions (including sandboxed file access), and what goes into the package.

最后更新 2026-10-13

This document is for third-party developers writing a resident WASM plugin that runs on the AI agent (AiAgent) and handles TCP commands. Its runtime model is nearly identical to the controller plugin (subscription + stdin/stdout line protocol) — code migrates directly.

1. What it is, and when to write one

An AiAgent plugin is not an "AiAgent external tool":

AiAgent plugin (this doc)AiAgent external tool (see)
Host roleA resident business plugin receiving TCP commandsA tool the LLM can call on demand
LoadingDistributed to AiConfig/plugins/<uuid>/ and loaded by the hostAiConfig/externaltools/<toolDir>/, called by the model
Entrymain() (_start), reads stdin, writes stdoutWASI command module: args JSON in, result out
Good forResident behavior, subscribing to platform events, local resourcesAdding a "function" for the model

Write one when the behavior must happen on the machine where the AiAgent runs (e.g. reading/writing that machine's hosts), triggered by platform events. If you only need another callable tool, use an external tool.

2. Relationship of the four endpoints

Plugin/
  gui/<uuid>/         page plugin (iframe + tss.*)
  Controller/<uuid>/  controller WASM plugin (ctl_*)
  scan/<uuid>/        scan-node WASM app plugin (app_*)
  aiagent/<uuid>/     this doc (aia_*)

installTargets in info.yaml lists the endpoints to install (gui, controller, scan, aiagent). Each endpoint acts on its own machine, which is why the plugin directory sandbox grants permissions per source endpoint: the same path on gui and aiagent are two independent records.

3. Package layout

Plugin/aiagent/<uuid>/
  build/aiagent.wasm   build output (preferred; any .wasm under build/ is also accepted)
  src/                 sources
  info.yaml            metadata (one per package, under Controller/<uuid>/)

4. Runtime model

For every TCP message the host: writes the message JSON to stdin, instantiates the module and calls _start (main()); the plugin reads stdin and writes stdout lines ([INIT] / [RESP]). handled=true in [RESP] means the plugin consumed the message. Subscriptions are declared in init (matched on Command1/Command2/CommandA; empty = wildcard).

5. Minimal project

aiacore.go holds the runtime skeleton (SetInfo / Subscribe / Capability / Handle, the aia_* imports and main); main.go holds your logic:

//go:build ignore
package main

func init() {
    SetInfo("My AiAgent plugin", "1.0.0")
    Subscribe("", "", "MyCommand")
    Capability("tcp.handle")
    Handle(func(msg Msg) bool {
        if msg.CommandA != "MyCommand" { return false }
        return true
    })
}

6. Build

GOWORK=off GOOS=wasip1 GOARCH=wasm go build -o build/aiagent.wasm .

Two pitfalls: use GOWORK=off; and do not pass -buildmode=c-shared (the AiAgent host drives _start). Note that scan-node app plugins are the opposite — they require c-shared.

提示

Let the build service/script pick the right mode for you: add //tss:buildmode command to your source's header comment; both the plugin-store build service and the local build-plugins.sh read it and build an ordinary command module. This is the "same source, different build mode per endpoint" trap: both modes compile, but only the right one loads. The build service parses the artifact's export symbols after compiling (this endpoint requires _start or plugin_handle) and fails the build on a mismatch, moving the trap to build time.

7. Host API (aia_*)

Exported by the host in the env namespace: aia_log, aia_read_file, aia_write_file, aia_register_fs_entry, aia_set_timeout. The host bumps HostApiVersion when the export set changes; declare a minimum with minAgentHostApi in info.yaml.

aia_read_file

Reads a file on the AiAgent machine (sandbox-gated). Returns JSON {"ok":bool,"data":<base64>,"size":n,"error":"..."}. Logical keys: system.hosts, system.resolv, system.tmp, user.home. Relative paths mean files inside the plugin's own directory.

aia_write_file

Writes a file on the AiAgent machine (full overwrite, same gating). Write and read bits are independent; nothing is written when denied.

aia_register_fs_entry

Registers an external path in the plugin directory sandbox (idempotent; registration is not permission).

aia_log

Structured logging: writes a line into the AiAgent log (system log and the "developer mode" live stream). Do not log heavily on hot paths.

aia_set_timeout

Requests a per-invocation timeout for this plugin (milliseconds). On the AiAgent side execution currently uses the host default, so this is a capability placeholder / intent declaration; the host enforces a hard cap on a single invocation.

8. Sandboxed file access

Reading/writing outside the plugin's own directory requires the "Plugin Store > Settings > Plugin Directory Sandbox" grant, matched on source endpoint aiagent + plugin id + normalized path + permission bits — identical to the other endpoints. Dangerous paths are rejected at registration and never allowed. Relative paths are always inside the plugin's own directory.

aia_dns_get

Reads the per-adapter DNS of the AiAgent machine.

aia_dns_set

Sets the per-adapter DNS of the AiAgent machine. DNS is not a file (registry on Windows, scutil on macOS), so it is a dedicated capability: authorization uses the plugin directory sandbox keyed by system.dns (kind=capability) with the aiagent endpoint's read/write grant; unregistered access auto-registers and is denied. Dns: [] restores an adapter to automatic (DHCP).

9. Debugging

Plugin logs go through aia_log; a failed plugin is skipped in isolation without affecting other plugins or the main flow.

本文档随 SDK 源码同步更新;新增或修改公开接口后会同步到此页。