Manifiesto de skill
Los skills externos son ejecutables en subprocess descritos por un archivo skill.json colocado bajo uno de los directorios de allow-list del manager. El loader recorre esos directorios en el arranque, parsea cada skill.json y registra un SubprocessSkill en el registry global de skills. El LLM ve entonces el skill junto a las herramientas integradas.
Fuente de verdad: internal/skill/loader.go.
Disposición en disco
/etc/ongrid/skills/ ← one entry in $ONGRID_SKILLS_EXTERNAL_DIRS
└── disk-cleaner/
├── skill.json
└── run.sh ← the executableCada skill.json está en la raíz de su propio subdirectorio. El árbol de directorios se recorre recursivamente; cualquier archivo llamado skill.json se trata como un manifiesto. Múltiples skills pueden vivir bajo la misma raíz de allow-list.
Esquema del manifiesto
{
"name": "disk_cleaner",
"description": "Free up disk by clearing stale build caches and journals.",
"schema": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "Root path to clean." },
"dry_run": { "type": "boolean", "default": true }
},
"required": ["path"]
},
"entry": "run.sh",
"env_allow": ["PATH", "HOME"],
"timeout_seconds": 60,
"class": "mutating",
"category": "ops"
}| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | sí | Clave del skill. Debe ser lower_snake que coincida con [a-z0-9_]+. Se convierte en el nombre de función que ve el LLM. |
description | string | sí | Se muestra a humanos (UI) y al LLM (descripción de la función). |
schema | JSON Schema | no | JSON Schema crudo para el objeto args. Ausente = esquema de objeto vacío. |
entry | string | sí | Ruta al ejecutable. Las rutas relativas se resuelven contra el directorio que contiene skill.json. Las rutas absolutas deben estar bajo una raíz de allow-list. |
env_allow | string[] | no | Lista explícita de nombres de variables de entorno reenviadas al hijo. Lista vacía = nada de env (ni siquiera PATH). Añade "PATH" para optar por PATH. |
timeout_seconds | int | no | Timeout del subprocess. Cero recurre a DefaultSubprocessTimeout (30s). |
class | enum | no | safe (por defecto), mutating, dangerous. Ver taxonomía de clases. |
category | string | no | Etiqueta de grupo de forma libre. Por defecto external. La UI agrupa los skills de subprocess por esto. |
Protocolo wire
El subprocess es invocado con stdin = objeto JSON de args, stdout = objeto JSON de resultado, stderr = líneas de log para el manager.
$ cat /tmp/args.json
{"path": "/var/cache", "dry_run": true}
$ run.sh < /tmp/args.json
{"freed_bytes": 1048576, "files_deleted": 17}Un código de salida distinto de cero se trata como fallo. Stderr se captura en la línea de tiempo de eventos de tool-call para que el LLM pueda leer el progreso parcial.
El objeto de resultado se devuelve textualmente al LLM. El kernel del agente lo formatea como la respuesta de la llamada de herramienta; se espera que el LLM razone sobre la forma del JSON.
Taxonomía de clases
La misma taxonomía {safe, mutating, dangerous} aplica a los skills nativos y a los skills de subprocess.
| Clase | Qué puede hacer | Permiso de persona requerido |
|---|---|---|
safe | Solo lectura — sin efectos secundarios en el host ni en sistema alguno. | read-only (cualquier persona) |
mutating | Crea / actualiza estado. Reversible. | mutating-with-confirm o dual-sign-required |
dangerous | Irreversible (deletes, restarts, exec-arbitrary). | dual-sign-required (SOP) |
El campo permission_mode de la persona filtra qué clases pueden ejecutarse sin confirmación. Ver Formato de persona de agente.
Reglas de allow-list
Los operadores configuran los directorios de allow-list vía ONGRID_SKILLS_EXTERNAL_DIRS (rutas absolutas separadas por dos puntos o por comas). El loader las aplica estrictamente:
- Cada directorio debe ser una ruta absoluta. Las rutas relativas se omiten con una línea de log.
- Las rutas inexistentes se omiten (de modo que una instalación fresca sin
/etc/ongrid/skillsarranca bien). - El
entryde cada manifiesto se canoniza confilepath.EvalSymlinksy se verifica que viva bajo la raíz de allow-list. Un symlink que apunta fuera de la raíz es rechazado (entry %s escapes allowlist root %s).
El sandboxing más allá de eso es responsabilidad del subprocess. Si el skill necesita estar fuertemente contenido, ejecútalo bajo un wrapper (bwrap, firejail, nsenter) referenciado como el entry.
Comportamiento del loader
LoadDirs(cfg)
for each dir in cfg.Dirs:
filepath.Walk(dir)
for each skill.json found:
parseManifest(path)
buildSubprocessSkill(manifest, path, root)
if skill already registered: skip (log line)
else Register(skill)- Los fallos de validación por manifiesto se loguean y omiten. Un pack roto no debería bloquear el arranque.
- Los nombres duplicados se omiten en lugar de generar error (de modo que un redeploy que añade un nuevo manifiesto antes de remover el viejo no crashea al manager).
- El loader devuelve el conteo de skills registrados exitosamente. El arranque del manager no se bloquea por el loader de skills: un dir externo vacío es un no-op.
Logging en el arranque
El manager loguea una línea por manifiesto:
skill loader: registered subprocess skill "disk_cleaner" from /etc/ongrid/skills/disk-cleaner/skill.json
skill loader: skill "broken_one" already registered, skipping /etc/ongrid/skills/broken-one/skill.json
skill loader: parse /etc/ongrid/skills/typo/skill.json: invalid character ',' looking for beginning of object key
skill loader: build "bad_path": entry /tmp/escape.sh escapes allowlist root /etc/ongrid/skillsHaz tail a journalctl -u docker-compose@ongrid o docker compose logs ongrid para confirmar qué fue recogido.
Véase también
- Capacidades → Skills — catálogo de skills integrados.
- Formato de persona de agente — cómo una persona elige qué skills puede invocar.
- Marketplace — instalar un pack de skills como unidad.
- REST API →
/v1/skills— listado y ejecución directa.
Metadatos de skill nativo (frontmatter de SKILL.md)
Los skills integrados que se entregan bajo ./skills/ usan un formato SKILL.md con frontmatter YAML más rico que interopera con los ecosistemas de skills de openclaw / claude-code. El esquema se define en internal/manager/biz/aiops/chatruntime/types.go:
---
name: query_promql
description: Run a PromQL query and return the result matrix.
when_to_use: When the user asks for current or recent metric values.
activation:
mode: always
metadata:
os: [linux, darwin]
requires:
bins: []
config: []
ongrid:
scope: manager
activation:
mode: always
tools:
- name: query_promql
impl: builtin:prom.QueryPromQL
class: read
description: Execute a PromQL query and return the matrix.
---
# query_promql
PromQL query tool ...Para skills de subprocess escritos por terceros, prefiere el formato skill.json más simple de arriba. SKILL.md es para skills que se compilan dentro del binario del manager.