Écrire un Skill Bundle
Un paquet de compétences est le seul type de plugin qui ne contient aucun code WebAssembly. Il s’agit d’un répertoire de compétences Markdown, empaqueté et distribué via le mécanisme des plugins : même manifeste, même découverte, même politique de signature, même zeroclaw plugin install. Utilisez-le lorsque la fonctionnalité que vous ajoutez consiste en des instructions, des invites et des workflows plutôt qu’en du code, et que vous préférez les sémantiques de distribution de plugins (signature, installation via le registre, gestion des versions) à des fichiers isolés dans un répertoire de compétences.
Vérifiez d’abord votre binaire. Les bundles de compétences reposent sur le mécanisme des plugins. Les archives de publication pour les cibles desktop et serveur 64 bits intègrent l’hôte de plugins. Les deux archives ARM 32 bits, l’archive Android expérimentale et les compilations depuis les sources avec les valeurs par défaut de Cargo (
cargo install zeroclaw,install.sh --source) ne l’intègrent pas : dans ce cas,zeroclaw plugin ...est une sous-commande non reconnue et les compétences fournies par des plugins ne se chargent pas. Chaque version exécute un plugin de chaque archive qui intègre l’hôte ; le tableau par artefact les répertorie tous. Sur un binaire sans l’hôte, compilez soit depuis les sources avec un backend d’exécution de plugins, par exemplecargo build --release --features plugins-wasm-cranelift, soit utilisez plutôt les bundles natifs décrits dans Skills :zeroclaw skills bundle add <alias>en crée un etzeroclaw skills install <source> --bundle <alias>installe dans celui-ci. Vous obtenez ainsi les mêmes compétences, sans la sémantique de distribution par plugin.
Ce guide est vérifié par rapport au chemin de validation dans crates/zeroclaw-plugins/src/host.rs (validate_skill_bundle, validate_skill_md_frontmatter) et le chargeur dans crates/zeroclaw-runtime/src/skills/mod.rs.
Pour savoir ce qu’est une compétence et comment les agents les utilisent, lisez d’abord Skills. Cette page ne traite que de l’empaquetage des bundles.
Disposition
Un plugin ne contenant que des compétences omet wasm_path et inclut un répertoire skills/ au format agentskills.io :
my-toolkit/
manifest.toml # capabilities = skill only, no wasm_path
README.md # optional bundle-level overview
skills/
design-review/
SKILL.md
scripts/ # optional
references/ # optional
code-review/
SKILL.md
data-analysis/
SKILL.md
references/
Validation : ce que la découverte impose
L’hôte valide la forme du bundle lors de la découverte et de l’installation, et rejette l’ensemble du plugin à la première défaillance (validate_skill_bundle dans host.rs). Les règles exactes :
skills/doit exister et être un répertoire.- Il doit contenir au moins un sous-répertoire. Un
skills/vide est un manifeste invalide, et non un bundle vide. - Chaque sous-répertoire doit contenir un
SKILL.md. - Chaque
SKILL.mddoit commencer par un frontmatter YAML (un délimiteur---sur la première ligne, clôturé par un---), et ce frontmatter doit déclarer les clésnameetdescriptionnon vides.
La vérification du frontmatter s’exécute volontairement au moment de la découverte : un bundle dont les skills omettent name ou description échoue lors du chargement du plugin, et non lorsqu’un agent invoque la skill pour la première fois en cours de conversation.
Un en-tête de compétence valide :
---
name: design-review
description: Structured design review workflow for architecture proposals.
---
# Design Review
...instructions...
Namespacing
Les compétences du bundle chargé sont enregistrées sous des identifiants qualifiés par le plugin : plugin:<plugin-name>/<skill-name>, par exemple plugin:my-toolkit/design-review (namespace_plugin_skill dans skills/mod.rs). Chaque compétence reçoit également un tag plugin:<plugin-name>. Cela évite les conflits avec les compétences créées par les utilisateurs et entre les bundles : deux bundles peuvent à la fois fournir une compétence code-review et coexister.
Le namespacing interagit avec la priorité des skills : dans la résolution des skills effectives de l’agent, les skills de même nom provenant de sources différentes sont dédupliqués par priorité et les perdants sont enregistrés comme masqués. Le qualificatif de plugin maintient votre bundle entièrement hors de ce conflit, sauf si une autre copie du même nom de bundle est en jeu.
Scripts
Une compétence peut comporter un répertoire scripts/. Le chargement des compétences contenant des scripts est régi par le paramètre skills.allow_scripts de l’opérateur, que le chargeur de compétences de plugin transmet sans modification (discover_plugin_skills dans skills/mod.rs) : une compétence bundle avec des scripts est soumise aux mêmes règles d’audit et de suppression qu’une compétence workspace. Ne supposez pas que vos scripts s’exécutent simplement parce que le bundle a été installé.
Manifeste
Le manifeste est le fichier nommé manifest.toml dans le répertoire du plugin. Ses champs sont la surface serde de PluginManifest dans crates/zeroclaw-plugins/src/lib.rs, qui est la source de vérité :
| Champ | Obligatoire | Signification |
|---|---|---|
name | oui | Slug canonique unique du package et composant du package de chaque clé de configuration d’instance dérivée. Il ne constitue pas lui-même une clé de configuration d’opérateur. Utilisez 1–128 caractères ASCII en minuscules ; commencez et terminez par [a-z0-9], avec uniquement [a-z0-9._-] entre les deux. La découverte rejette les noms non valides ou en double. |
version | oui | Chaîne de version, p. ex. 0.1.0. |
description | non | Description lisible affichée par zeroclaw plugin list. |
author | non | Nom de l’auteur ou de l’organisation. |
wasm_path | pour les capacités WASM | Nom du fichier du composant, relatif au répertoire du plugin. Obligatoire sauf si la seule capacité est skill. La découverte ignore le plugin si le fichier nommé n’existe pas. |
wasm_sha256 | dans le mode strict pour les capacités WASM | Empreinte SHA-256 hexadécimale des octets exacts du composant. Lorsqu’elle est présente, elle est toujours appliquée ; la politique de signature stricte l’exige afin que le manifeste signé lie l’exécutable admis. |
capabilities | oui, non vide | Ce qu’est le plugin : l’un de tool, channel, memory, observer, skill (PluginCapability, sérialisé en snake_case). |
provides | non | Pour un plugin channel qui est un remplaçant direct (drop-in) d’un canal intégré à la compilation : l’identifiant de configuration en snake_case qu’il reproduit (telegram, gmail_push). L’hôte admet une instance logique par [channels.<id>.<alias>] configuré et activé qu’un agent activé route vers, de sorte que la configuration canonique du canal reste le seul emplacement de ces paramètres. Nécessite config_read. La famille plugin est réservée et refusée ; un identifiant non défini par cette version du build est refusé ; deux paquets installés revendiquant le même identifiant sont tous deux refusés. Absent (valeur par défaut) signifie un canal inédit configuré depuis [channels.plugin.<alias>]. Les miroirs sont admis mais pas encore construits ; voir le plan d’activation dans crates/zeroclaw-runtime/src/plugin_runtime.rs. |
permissions | non | Services hôtes auxquels le code peut accéder : http_client, config_read, state_read, state_write, file_read, file_write, memory_read, memory_write (PluginPermission). Les permissions HTTP, de configuration et d’état d’instance exacte sont appliquées pour les adaptateurs d’outils et de canaux ; les permissions de système de fichiers et de mémoire restent sans effet. La déclaration de config_read nécessite config_schema. |
config_schema | exactement avec config_read | Brouillon 2020-12 de JSON Schema pour la configuration privée de ce plugin ; il est inclus dans les octets canoniques du manifeste et est donc couvert lors de la signature du manifeste. La racine doit être un objet avec une map properties et additionalProperties = false. Chaque propriété de premier niveau doit avoir un type explicite pris en charge, directement ou via un JSON Pointer local : string, boolean, integer, number, array ou object. Les consommateurs d’outils et de canaux peuvent définir x-secret = true directement sur une propriété string de premier niveau afin de la retirer de la configuration publique et de l’exposer via l’import hôte secrets.get limité à sa portée. Un nom de propriété secrète est un nom ASCII portable de 1 à 128 octets ne contenant que des lettres, des chiffres, _, - ou .. Les outils reçoivent la configuration publique sous __config et peuvent lire les secrets pendant execute. Les canaux lisent l’objet public courant via config.get et les secrets via secrets.get pendant configure et les appels opérationnels ; les deux imports sont indisponibles pendant l’instanciation et la découverte des métadonnées statiques. Les marqueurs secrets imbriqués, false ou non booléens, ainsi que les propriétés secrètes non string, sont rejetés. Un schéma sans config_read, ou un config_read sans schéma, est rejeté. |
signature | non | Signature Ed25519 en Base64url sur les octets canoniques du manifest. Défini lors de la signature pour la distribution. |
publisher_key | non | Clé publique Ed25519 encodée en hexadécimal du signataire. |
| sortie | non | Table [egress] facultative contenant une liste hosts : les destinations que ce plugin déclare requérir, sous forme d’hôtes exacts (api.example.com) ou de motifs de suffixe explicites (*.example.com, qui correspondent aux sous-domaines mais pas au domaine apex). Elle fait partie des octets canoniques du manifeste : elle est donc couverte lors de la signature du manifeste, et toute modification exige une nouvelle signature. Une grammaire invalide rejette l’intégralité du manifeste lors de la découverte et de l’installation. L’absence de la table, une table vide et une table qui « ne déclare rien » correspondent à un même état. |
Déclarez uniquement les permissions effectivement utilisées par le code. Une permission non déclarée est une surface d’hôte que le composant ne peut pas atteindre ; une permission déclarée de manière superflue est une surface d’attaque que vous avez sollicitée et une charge d’audit pour quiconque révise votre plugin.
[egress] est une déclaration, et non une autorisation. Rien dans un manifeste n’accorde d’accès réseau : la liste d’autorisation appliquée par l’hôte est plugins.entries[].egress_hosts de l’opérateur, sur la ligne zpi1_… de l’instance elle-même. Ainsi, un composant non signé qui écrit sa propre table [egress] n’atteint toujours rien. Ce que la déclaration apporte, c’est le processus d’installation : zeroclaw plugin install l’insère dans une ligne qu’il crée et affiche ce qui a été accordé, et il n’élargit jamais une ligne déjà existante. Voir Déclarer et accorder l’accès réseau sortant.
Les valeurs des opérateurs restent des chaînes dans plugins.entries et sont chiffrées lors de leur persistance, avec pour clé une chaîne zpi1_… versionnée dérivée de l’identité du package détenu par l’hôte, de la capacité et de la liaison (l’installation affiche et initialise la clé d’instance complète de la liaison d’outil par défaut) : les chaînes sont stockées telles quelles, les booléens et les nombres sont représentés par du texte scalaire JSON, et les tableaux et les objets par du texte JSON. Avant l’exécution de tout code invité, l’hôte matérialise ces chaînes selon les types du schéma du package et valide l’objet complet pour les adaptateurs d’outils et de canaux. Les propriétés non secrètes de l’outil constituent __config ; un canal obtient l’objet non secret via config.get. Une propriété marquée x-secret = true est exclue des deux surfaces publiques et n’est accessible que via secrets.get("property") dans un cadre de service autorisé. Les lectures publiques et secrètes d’un canal au cours d’un même appel partagent une révision canonique unique, et l’hôte supprime cette vue matérialisée à la fin de l’appel. Un plugin de canal conforme doit résoudre les deux à chaque point d’utilisation et ne doit pas conserver les valeurs de configuration ou d’identifiants dans l’état invité conservé à chaud ; renvoyer du texte en clair à l’invité signifie que l’hôte ne peut pas imposer la non-rétention face à du code malveillant. Si config_read a été demandé mais n’a pas été effectivement accordé, l’hôte valide un objet vide ; par conséquent, un schéma comportant des propriétés obligatoires échoue en mode sécurisé au lieu de démarrer sans la configuration requise. Si l’objet vide est valide, un outil omet __config lorsqu’il est vide et les imports config/secret du canal renvoient access-denied ; les appels effectués hors d’un cadre autorisé, les échecs de résolution et l’épuisement du budget d’appels à l’hôte renvoient unavailable.
Pour un bundle de compétences : capabilities contenant exactement skill, aucun wasm_path, et généralement aucun permissions ; le bundle est une donnée, et l’ensemble des permissions contrôle l’accès aux fonctions hôtes que markdown n’appelle jamais.
Un plugin aux capacités mixtes (par exemple tool + skill) est autorisé : il doit alors contenir un wasm_path valide pour le monde des outils et un bundle skills/ valide, et les deux validations sont exécutées.
Installer et vérifier
Ces commandes nécessitent un binaire avec l’hôte de plugins compilé. Les archives de publication pour les cibles de bureau et serveur 64 bits (Linux x86_64 et aarch64 avec glibc ou musl, macOS sur Apple silicon et Intel, Windows x86_64) le contiennent, et chaque publication exécute un plugin depuis chacune de ces archives avant de la publier. Les deux archives ARM 32 bits et l’archive Android expérimentale ne le contiennent pas, pas plus qu’une compilation depuis les sources avec les valeurs par défaut de Cargo (
cargo install zeroclaw,install.sh --source, oucargo buildsans fonctionnalités) : là,zeroclaw plugin ...est une sous-commande non reconnue et les plugins installés ne sont jamais découverts. Dans ces cas, compilez depuis les sources avec un backend d’exécution de plugins, par exemplecargo build --release --features plugins-wasm-cranelift. La page du protocole indique la prise en charge des plugins pour chaque artefact.
Chaque plugin réside dans son propre sous-répertoire du répertoire des plugins (par défaut ~/.zeroclaw/plugins/, résolu via plugins.plugins_dir), contenant le manifeste et le composant dont le nom correspond au wasm_path du manifeste :
~/.zeroclaw/plugins/
└── my-plugin/
├── manifest.toml
└── my-plugin.wasm
Installer à partir d’un répertoire local (cela valide la structure du manifeste et applique la politique de signature avant de copier quoi que ce soit) :
zeroclaw plugin install ./my-plugin/
Activer le système de plugins et confirmer la découverte :
zeroclaw config set plugins.enabled true
zeroclaw plugin list
zeroclaw plugin info my-plugin
zeroclaw plugin list et zeroclaw plugin info confirment qu’un package est installé et détectable, mais la découverte n’équivaut pas à l’activation. plugins.enabled = true active l’hôte de plugins ; les fonctionnalités d’outils et de compétences découvertes automatiquement sont chargées à l’exécution uniquement lorsque plugins.auto_discover = true l’est également, et cet indicateur vaut false par défaut (refus en cas d’échec) :
zeroclaw config set plugins.auto_discover true
Ainsi, plugins.enabled = true à lui seul vous fournit les canaux que vous déclarez sous [channels.plugin.<alias>], mais aucun outil ni aucune compétence de plugin : un paquet d’outils ou de compétences peut apparaître dans zeroclaw plugin list tout en n’apportant rien à l’exécution. Les liaisons de canaux explicites sont nommées par l’opérateur plutôt que découvertes automatiquement ; elles n’ont donc pas besoin de auto_discover ; cet indicateur contrôle uniquement les outils et compétences découverts automatiquement.
Un plugin absent de zeroclaw plugin list a été ignoré lors de la découverte : consultez le journal de démarrage pour l’avertissement d’ignorance (manifeste corrompu, fichier wasm_path manquant ou rejet par la politique de signature).
Après leur découverte, les compétences apparaissent avec un espace de noms sur les interfaces de compétences (la liste des compétences, le tableau de bord) sous la forme plugin:<your-bundle>/<skill>. Demandez à l’agent d’en utiliser un pour valider le fonctionnement de bout en bout.
Suivant
- Distribution de plugins : un skill bundle est la chose la plus simple à publier, et le processus de signature est identique à celui des plugins WASM.