Plugins (extensiones)

Inicio rápido (¿nuevo con plugins?)

Un plugin es simplemente un pequeño módulo de código que extiende OpenClaw con funciones extra (comandos, herramientas y RPC del Gateway).

La mayoría de las veces, usarás plugins cuando quieras una función que aún no está integrada en el core de OpenClaw (o quieras mantener funciones opcionales fuera de tu instalación principal).

Ruta rápida:

  1. Mira lo que ya está cargado:
openclaw plugins list
  1. Instala un plugin oficial (ejemplo: Voice Call):
openclaw plugins install @openclaw/voice-call

Las especificaciones npm son solo de registro (nombre de paquete + versión exacta opcional o dist-tag). Las especificaciones Git/URL/archivo y los rangos semver se rechazan.

Las especificaciones sin decorar y @latest se mantienen en el canal estable. Si npm resuelve cualquiera de esas a un prerelease, OpenClaw se detiene y te pide que optes explícitamente con un tag de prerelease como @beta/@rc o una versión de prerelease exacta.

  1. Reinicia el Gateway, luego configura bajo plugins.entries.<id>.config.

Consulta Voice Call para un ejemplo concreto de plugin. ¿Buscas listados de terceros? Consulta Plugins de la comunidad.

Arquitectura

El sistema de plugins de OpenClaw tiene cuatro capas:

  1. Manifiesto + descubrimiento OpenClaw encuentra plugins candidatos desde rutas configuradas, raíces de workspace, raíces de extensiones globales y extensiones incluidas. El descubrimiento lee openclaw.plugin.json más los metadatos del paquete primero.
  2. Habilitación + validación El core decide si un plugin descubierto está habilitado, deshabilitado, bloqueado o seleccionado para un slot exclusivo como memory.
  3. Carga en runtime Los plugins habilitados se cargan en proceso mediante jiti y registran capacidades en un registro central.
  4. Consumo de superficie El resto de OpenClaw lee el registro para exponer herramientas, canales, configuración de proveedor, hooks, rutas HTTP, comandos CLI y servicios.

El límite de diseño importante:

  • el descubrimiento + validación de configuración debe funcionar desde metadatos de manifiesto/esquema sin ejecutar código del plugin
  • el comportamiento en runtime viene de la ruta register(api) del módulo del plugin

Esa separación permite a OpenClaw validar configuración, explicar plugins faltantes/deshabilitados, y construir hints de UI/esquema antes de que el runtime completo esté activo.

Modelo de ejecución

Los plugins se ejecutan en proceso con el Gateway. No están en sandbox. Un plugin cargado tiene el mismo límite de confianza a nivel de proceso que el código core.

Implicaciones:

  • un plugin puede registrar herramientas, handlers de red, hooks y servicios
  • un bug de plugin puede crashear o desestabilizar el gateway
  • un plugin malicioso equivale a ejecución arbitraria de código dentro del proceso de OpenClaw

Usa listas de permitidos y rutas explícitas de instalación/carga para plugins no incluidos. Trata los plugins de workspace como código de desarrollo, no como valores por defecto de producción.

Nota importante de confianza:

  • plugins.allow confía en ids de plugins, no en procedencia de origen.
  • Un plugin de workspace con el mismo id que un plugin incluido intencionalmente sobreescribe la copia incluida cuando ese plugin de workspace está habilitado/en lista de permitidos.
  • Esto es normal y útil para desarrollo local, pruebas de parches y hotfixes.

Plugins disponibles (oficiales)

  • Microsoft Teams es solo plugin desde 2026.1.15; instala @openclaw/msteams si usas Teams.
  • Memory (Core) — plugin de búsqueda de memoria incluido (habilitado por defecto con plugins.slots.memory)
  • Memory (LanceDB) — plugin de memoria a largo plazo incluido (auto-recall/capture; configura plugins.slots.memory = "memory-lancedb")
  • Voice Call@openclaw/voice-call
  • Zalo Personal@openclaw/zalouser
  • Matrix@openclaw/matrix
  • Nostr@openclaw/nostr
  • Zalo@openclaw/zalo
  • Microsoft Teams@openclaw/msteams
  • Google Antigravity OAuth (auth de proveedor) — incluido como google-antigravity-auth (deshabilitado por defecto)
  • Gemini CLI OAuth (auth de proveedor) — incluido como google-gemini-cli-auth (deshabilitado por defecto)
  • Qwen OAuth (auth de proveedor) — incluido como qwen-portal-auth (deshabilitado por defecto)
  • Copilot Proxy (auth de proveedor) — puente local de VS Code Copilot Proxy; distinto del login por dispositivo github-copilot integrado (incluido, deshabilitado por defecto)

Los plugins de OpenClaw son módulos TypeScript cargados en runtime mediante jiti. La validación de configuración no ejecuta código del plugin; usa el manifiesto del plugin y JSON Schema en su lugar. Consulta Manifiesto de plugin.

Los plugins pueden registrar:

  • Métodos RPC del Gateway
  • Rutas HTTP del Gateway
  • Herramientas de agente
  • Comandos CLI
  • Servicios en segundo plano
  • Motores de contexto
  • Validación de configuración opcional
  • Skills (listando directorios skills en el manifiesto del plugin)
  • Comandos de auto-respuesta (se ejecutan sin invocar al agente de IA)

Los plugins se ejecutan en proceso con el Gateway, así que trátalos como código confiable. Guía de autoría de herramientas: Herramientas de agente de plugin.

Pipeline de carga

Al iniciar, OpenClaw hace aproximadamente esto:

  1. descubrir raíces candidatas de plugins
  2. leer openclaw.plugin.json y metadatos del paquete
  3. rechazar candidatos inseguros
  4. normalizar configuración de plugins (plugins.enabled, allow, deny, entries, slots, load.paths)
  5. decidir habilitación para cada candidato
  6. cargar módulos habilitados mediante jiti
  7. llamar register(api) y recoger registros en el registro de plugins
  8. exponer el registro a comandos/superficies de runtime

Las puertas de seguridad ocurren antes de la ejecución en runtime. Los candidatos se bloquean cuando la entrada escapa de la raíz del plugin, la ruta es world-writable, o la propiedad de la ruta parece sospechosa para plugins no incluidos.

Comportamiento manifiesto-primero

El manifiesto es la fuente de verdad del plano de control. OpenClaw lo usa para:

  • identificar el plugin
  • descubrir canales/skills/esquema de configuración declarados
  • validar plugins.entries.<id>.config
  • enriquecer labels/placeholders de la UI de Control
  • mostrar metadatos de instalación/catálogo

El módulo de runtime es la parte del plano de datos. Registra comportamiento real como hooks, herramientas, comandos o flujos de proveedor.

Lo que cachea el loader

OpenClaw mantiene cachés cortas en proceso para:

  • resultados de descubrimiento
  • datos del registro de manifiestos
  • registros de plugins cargados

Estas cachés reducen la sobrecarga de inicio bursty y comandos repetidos. Son seguras de considerar como cachés de rendimiento de corta vida, no persistencia.

Helpers de runtime

Los plugins pueden acceder a helpers core selectos mediante api.runtime. Para TTS de telefonía:

const result = await api.runtime.tts.textToSpeechTelephony({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

Notas:

  • Usa la configuración core de messages.tts (OpenAI o ElevenLabs).
  • Devuelve buffer de audio PCM + sample rate. Los plugins deben re-muestrear/codificar para proveedores.
  • Edge TTS no está admitido para telefonía.

Para STT/transcripción, los plugins pueden llamar:

const { text } = await api.runtime.stt.transcribeAudioFile({
  filePath: "/tmp/inbound-audio.ogg",
  cfg: api.config,
  // Opcional cuando el MIME no se puede inferir de forma confiable:
  mime: "audio/ogg",
});

Notas:

  • Usa la configuración core de comprensión de medios de audio (tools.media.audio) y el orden de fallback de proveedores.
  • Devuelve { text: undefined } cuando no se produce salida de transcripción (por ejemplo, entrada omitida/no admitida).

Rutas HTTP del Gateway

Los plugins pueden exponer endpoints HTTP con api.registerHttpRoute(...).

api.registerHttpRoute({
  path: "/acme/webhook",
  auth: "plugin",
  match: "exact",
  handler: async (_req, res) => {
    res.statusCode = 200;
    res.end("ok");
    return true;
  },
});

Campos de ruta:

  • path: ruta bajo el servidor HTTP del gateway.
  • auth: obligatorio. Usa "gateway" para requerir auth normal del gateway, o "plugin" para auth/verificación de webhook gestionada por el plugin.
  • match: opcional. "exact" (por defecto) o "prefix".
  • replaceExisting: opcional. Permite al mismo plugin reemplazar su propio registro de ruta existente.
  • handler: devuelve true cuando la ruta gestionó la solicitud.

Notas:

  • api.registerHttpHandler(...) está obsoleto. Usa api.registerHttpRoute(...).
  • Las rutas de plugins deben declarar auth explícitamente.
  • Los conflictos exactos de path + match se rechazan a menos que replaceExisting: true, y un plugin no puede reemplazar la ruta de otro plugin.
  • Las rutas solapadas con diferentes niveles de auth se rechazan. Mantén las cadenas de fallthrough exact/prefix en el mismo nivel de auth solamente.

Rutas de importación del SDK de plugins

Usa subrutas del SDK en lugar de la importación monolítica openclaw/plugin-sdk cuando desarrolles plugins:

  • openclaw/plugin-sdk/core para APIs genéricas de plugins, tipos de auth de proveedor y helpers compartidos.
  • openclaw/plugin-sdk/compat para código de plugins incluidos/internos que necesita helpers de runtime compartidos más amplios que core.
  • openclaw/plugin-sdk/telegram para plugins de canal de Telegram.
  • openclaw/plugin-sdk/discord para plugins de canal de Discord.
  • openclaw/plugin-sdk/slack para plugins de canal de Slack.
  • openclaw/plugin-sdk/signal para plugins de canal de Signal.
  • openclaw/plugin-sdk/imessage para plugins de canal de iMessage.
  • openclaw/plugin-sdk/whatsapp para plugins de canal de WhatsApp.
  • openclaw/plugin-sdk/line para plugins de canal de LINE.
  • openclaw/plugin-sdk/msteams para la superficie del plugin de Microsoft Teams incluido.
  • También están disponibles subrutas específicas de extensiones incluidas: openclaw/plugin-sdk/acpx, openclaw/plugin-sdk/bluebubbles, openclaw/plugin-sdk/copilot-proxy, openclaw/plugin-sdk/device-pair, openclaw/plugin-sdk/diagnostics-otel, openclaw/plugin-sdk/diffs, openclaw/plugin-sdk/feishu, openclaw/plugin-sdk/google-gemini-cli-auth, openclaw/plugin-sdk/googlechat, openclaw/plugin-sdk/irc, openclaw/plugin-sdk/llm-task, openclaw/plugin-sdk/lobster, openclaw/plugin-sdk/matrix, openclaw/plugin-sdk/mattermost, openclaw/plugin-sdk/memory-core, openclaw/plugin-sdk/memory-lancedb, openclaw/plugin-sdk/minimax-portal-auth, openclaw/plugin-sdk/nextcloud-talk, openclaw/plugin-sdk/nostr, openclaw/plugin-sdk/open-prose, openclaw/plugin-sdk/phone-control, openclaw/plugin-sdk/qwen-portal-auth, openclaw/plugin-sdk/synology-chat, openclaw/plugin-sdk/talk-voice, openclaw/plugin-sdk/test-utils, openclaw/plugin-sdk/thread-ownership, openclaw/plugin-sdk/tlon, openclaw/plugin-sdk/twitch, openclaw/plugin-sdk/voice-call, openclaw/plugin-sdk/zalo y openclaw/plugin-sdk/zalouser.

Nota de compatibilidad:

  • openclaw/plugin-sdk sigue admitido para plugins externos existentes.
  • Los plugins nuevos y migrados incluidos deben usar subrutas específicas de canal o extensión; usa core para superficies genéricas y compat solo cuando se necesiten helpers compartidos más amplios.

Inspección de canal de solo lectura

Si tu plugin registra un canal, prefiere implementar plugin.config.inspectAccount(cfg, accountId) junto con resolveAccount(...).

Por qué:

  • resolveAccount(...) es la ruta de runtime. Puede asumir que las credenciales están completamente materializadas y puede fallar rápido cuando faltan secretos requeridos.
  • Las rutas de comandos de solo lectura como openclaw status, openclaw status --all, openclaw channels status, openclaw channels resolve y los flujos de doctor/reparación de config no deberían necesitar materializar credenciales de runtime solo para describir la configuración.

Comportamiento recomendado de inspectAccount(...):

  • Devolver solo estado descriptivo de la cuenta.
  • Preservar enabled y configured.
  • Incluir campos de origen/estado de credenciales cuando sea relevante, como:
    • tokenSource, tokenStatus
    • botTokenSource, botTokenStatus
    • appTokenSource, appTokenStatus
    • signingSecretSource, signingSecretStatus
  • No necesitas devolver valores raw de tokens solo para reportar disponibilidad de solo lectura. Devolver tokenStatus: "available" (y el campo de origen correspondiente) es suficiente para comandos tipo status.
  • Usa configured_unavailable cuando una credencial está configurada mediante SecretRef pero no está disponible en la ruta de comando actual.

Esto permite que los comandos de solo lectura reporten “configurado pero no disponible en esta ruta de comando” en vez de crashear o reportar incorrectamente la cuenta como no configurada.

Nota de rendimiento:

  • El descubrimiento de plugins y los metadatos de manifiestos usan cachés cortas en proceso para reducir el trabajo bursty de inicio/recarga.
  • Configura OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1 o OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1 para desactivar estas cachés.
  • Ajusta las ventanas de caché con OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS y OPENCLAW_PLUGIN_MANIFEST_CACHE_MS.

Descubrimiento y precedencia

OpenClaw escanea, en orden:

  1. Rutas de configuración
  • plugins.load.paths (archivo o directorio)
  1. Extensiones de workspace
  • <workspace>/.openclaw/extensions/*.ts
  • <workspace>/.openclaw/extensions/*/index.ts
  1. Extensiones globales
  • ~/.openclaw/extensions/*.ts
  • ~/.openclaw/extensions/*/index.ts
  1. Extensiones incluidas (distribuidas con OpenClaw, mayormente deshabilitadas por defecto)
  • <openclaw>/extensions/*

La mayoría de los plugins incluidos deben habilitarse explícitamente mediante plugins.entries.<id>.enabled o openclaw plugins enable <id>.

Excepciones de plugins incluidos habilitados por defecto:

  • device-pair
  • phone-control
  • talk-voice
  • plugin de slot de memoria activo (slot por defecto: memory-core)

Los plugins instalados están habilitados por defecto, pero se pueden deshabilitar de la misma forma.

Los plugins de workspace están deshabilitados por defecto a menos que los habilites o los pongas en la lista de permitidos explícitamente. Esto es intencional: un repositorio clonado no debería convertirse silenciosamente en código del gateway de producción.

Notas de fortalecimiento:

  • Si plugins.allow está vacío y hay plugins no incluidos descubribles, OpenClaw registra una advertencia de inicio con ids y orígenes de plugins.
  • Las rutas candidatas se verifican por seguridad antes de la admisión al descubrimiento. OpenClaw bloquea candidatos cuando:
    • la entrada de extensión se resuelve fuera de la raíz del plugin (incluyendo escapes por symlink/traversal de ruta),
    • la raíz/ruta de origen del plugin es world-writable,
    • la propiedad de la ruta es sospechosa para plugins no incluidos (el propietario POSIX no es ni el uid actual ni root).
  • Los plugins no incluidos cargados sin procedencia de install/load-path emiten una advertencia para que puedas fijar confianza (plugins.allow) o seguimiento de instalación (plugins.installs).

Cada plugin debe incluir un archivo openclaw.plugin.json en su raíz. Si una ruta apunta a un archivo, la raíz del plugin es el directorio del archivo y debe contener el manifiesto.

Si múltiples plugins se resuelven al mismo id, la primera coincidencia en el orden anterior gana y las copias de menor precedencia se ignoran.

Esto significa:

  • los plugins de workspace intencionalmente sobreescriben plugins incluidos con el mismo id
  • plugins.allow: ["foo"] autoriza el plugin foo activo por id, incluso cuando la copia activa viene del workspace en vez de la raíz de extensiones incluidas
  • si necesitas control de procedencia más estricto, usa rutas explícitas de install/load e inspecciona el origen del plugin resuelto antes de habilitarlo

Reglas de habilitación

La habilitación se resuelve después del descubrimiento:

  • plugins.enabled: false deshabilita todos los plugins
  • plugins.deny siempre gana
  • plugins.entries.<id>.enabled: false deshabilita ese plugin
  • los plugins de origen workspace están deshabilitados por defecto
  • las listas de permitidos restringen el conjunto activo cuando plugins.allow no está vacío
  • las listas de permitidos son basadas en id, no en origen
  • los plugins incluidos están deshabilitados por defecto a menos que:
    • el id incluido esté en el conjunto de habilitados por defecto, o
    • lo habilites explícitamente, o
    • la configuración de canal habilite implícitamente el plugin de canal incluido
  • los slots exclusivos pueden forzar-habilitar el plugin seleccionado para ese slot

En el core actual, los ids incluidos habilitados por defecto incluyen helpers locales/de proveedor como ollama, sglang, vllm, más device-pair, phone-control y talk-voice.

Packs de paquetes

Un directorio de plugin puede incluir un package.json con openclaw.extensions:

{
  "name": "my-pack",
  "openclaw": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"]
  }
}

Cada entrada se convierte en un plugin. Si el pack lista múltiples extensiones, el id del plugin se convierte en name/<fileBase>.

Si tu plugin importa dependencias npm, instálalas en ese directorio para que node_modules esté disponible (npm install / pnpm install).

Protección de seguridad: cada entrada de openclaw.extensions debe permanecer dentro del directorio del plugin después de la resolución de symlinks. Las entradas que escapan del directorio del paquete se rechazan.

Nota de seguridad: openclaw plugins install instala dependencias de plugins con npm install --ignore-scripts (sin scripts de ciclo de vida). Mantén los árboles de dependencias de plugins “JS/TS puros” y evita paquetes que requieran builds postinstall.

Metadatos de catálogo de canal

Los plugins de canal pueden anunciar metadatos de onboarding mediante openclaw.channel e indicaciones de instalación mediante openclaw.install. Esto mantiene el catálogo core libre de datos.

Ejemplo:

{
  "name": "@openclaw/nextcloud-talk",
  "openclaw": {
    "extensions": ["./index.ts"],
    "channel": {
      "id": "nextcloud-talk",
      "label": "Nextcloud Talk",
      "selectionLabel": "Nextcloud Talk (self-hosted)",
      "docsPath": "/channels/nextcloud-talk",
      "docsLabel": "nextcloud-talk",
      "blurb": "Self-hosted chat via Nextcloud Talk webhook bots.",
      "order": 65,
      "aliases": ["nc-talk", "nc"]
    },
    "install": {
      "npmSpec": "@openclaw/nextcloud-talk",
      "localPath": "extensions/nextcloud-talk",
      "defaultChoice": "npm"
    }
  }
}

OpenClaw también puede fusionar catálogos de canal externos (por ejemplo, una exportación de registro MPM). Coloca un archivo JSON en una de estas ubicaciones:

  • ~/.openclaw/mpm/plugins.json
  • ~/.openclaw/mpm/catalog.json
  • ~/.openclaw/plugins/catalog.json

O apunta OPENCLAW_PLUGIN_CATALOG_PATHS (o OPENCLAW_MPM_CATALOG_PATHS) a uno o más archivos JSON (delimitados por coma/punto y coma/PATH). Cada archivo debe contener { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }.

IDs de plugins

IDs de plugin por defecto:

  • Packs de paquetes: package.json name
  • Archivo standalone: nombre base del archivo (~/.../voice-call.ts -> voice-call)

Si un plugin exporta id, OpenClaw lo usa pero advierte cuando no coincide con el id configurado.

Modelo de registro

Los plugins cargados no mutan directamente globals core arbitrarios. Se registran en un registro central de plugins.

El registro rastrea:

  • registros de plugins (identidad, origen, procedencia, estado, diagnósticos)
  • herramientas
  • hooks legacy y hooks tipados
  • canales
  • proveedores
  • handlers RPC del gateway
  • rutas HTTP
  • registradores CLI
  • servicios en segundo plano
  • comandos propiedad de plugins

Las funciones core entonces leen de ese registro en vez de hablar directamente con módulos de plugins. Esto mantiene la carga unidireccional:

  • módulo de plugin -> registro en el registro
  • runtime core -> consumo del registro

Esa separación importa para la mantenibilidad. Significa que la mayoría de las superficies core solo necesitan un punto de integración: “leer el registro”, no “caso especial para cada módulo de plugin”.

Configuración

{
  plugins: {
    enabled: true,
    allow: ["voice-call"],
    deny: ["untrusted-plugin"],
    load: { paths: ["~/Projects/oss/voice-call-extension"] },
    entries: {
      "voice-call": { enabled: true, config: { provider: "twilio" } },
    },
  },
}

Campos:

  • enabled: toggle principal (por defecto: true)
  • allow: lista de permitidos (opcional)
  • deny: lista de denegados (opcional; deny gana)
  • load.paths: archivos/directorios extra de plugins
  • slots: selectores de slots exclusivos como memory y contextEngine
  • entries.<id>: toggles por plugin + configuración

Los cambios de configuración requieren reinicio del gateway.

Reglas de validación (estrictas):

  • Los ids de plugins desconocidos en entries, allow, deny o slots son errores.
  • Las claves channels.<id> desconocidas son errores a menos que un manifiesto de plugin declare el id del canal.
  • La configuración del plugin se valida usando el JSON Schema embebido en openclaw.plugin.json (configSchema).
  • Si un plugin está deshabilitado, su configuración se preserva y se emite una advertencia.

Deshabilitado vs faltante vs inválido

Estos estados son intencionalmente diferentes:

  • deshabilitado: el plugin existe, pero las reglas de habilitación lo desactivaron
  • faltante: la configuración referencia un id de plugin que el descubrimiento no encontró
  • inválido: el plugin existe, pero su configuración no coincide con el esquema declarado

OpenClaw preserva la configuración de plugins deshabilitados para que reactivarlos no sea destructivo.

Slots de plugins (categorías exclusivas)

Algunas categorías de plugins son exclusivas (solo una activa a la vez). Usa plugins.slots para seleccionar qué plugin posee el slot:

{
  plugins: {
    slots: {
      memory: "memory-core", // o "none" para deshabilitar plugins de memoria
      contextEngine: "legacy", // o un id de plugin como "lossless-claw"
    },
  },
}

Slots exclusivos admitidos:

  • memory: plugin de memoria activo ("none" deshabilita plugins de memoria)
  • contextEngine: plugin de motor de contexto activo ("legacy" es el valor integrado por defecto)

Si múltiples plugins declaran kind: "memory" o kind: "context-engine", solo el plugin seleccionado se carga para ese slot. Los demás se deshabilitan con diagnósticos.

Plugins de motor de contexto

Los plugins de motor de contexto gestionan la orquestación de contexto de sesión para ingestión, ensamblaje y compactación. Regístralos desde tu plugin con api.registerContextEngine(id, factory), luego selecciona el motor activo con plugins.slots.contextEngine.

Usa esto cuando tu plugin necesite reemplazar o extender el pipeline de contexto por defecto en vez de solo agregar búsqueda de memoria o hooks.

UI de Control (esquema + labels)

La UI de Control usa config.schema (JSON Schema + uiHints) para renderizar mejores formularios.

OpenClaw enriquece uiHints en runtime basándose en los plugins descubiertos:

  • Agrega labels por plugin para plugins.entries.<id> / .enabled / .config
  • Fusiona hints opcionales de campos de configuración provistos por el plugin bajo: plugins.entries.<id>.config.<field>

Si quieres que los campos de configuración de tu plugin muestren buenos labels/placeholders (y marquen secretos como sensibles), proporciona uiHints junto a tu JSON Schema en el manifiesto del plugin.

Ejemplo:

{
  "id": "my-plugin",
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "apiKey": { "type": "string" },
      "region": { "type": "string" }
    }
  },
  "uiHints": {
    "apiKey": { "label": "API Key", "sensitive": true },
    "region": { "label": "Region", "placeholder": "us-east-1" }
  }
}

CLI

openclaw plugins list
openclaw plugins info <id>
openclaw plugins install <path>                 # copiar un archivo/dir local a ~/.openclaw/extensions/<id>
openclaw plugins install ./extensions/voice-call # ruta relativa ok
openclaw plugins install ./plugin.tgz           # instalar desde un tarball local
openclaw plugins install ./plugin.zip           # instalar desde un zip local
openclaw plugins install -l ./extensions/voice-call # link (sin copia) para desarrollo
openclaw plugins install @openclaw/voice-call # instalar desde npm
openclaw plugins install @openclaw/voice-call --pin # almacenar nombre@versión exacta resuelta
openclaw plugins update <id>
openclaw plugins update --all
openclaw plugins enable <id>
openclaw plugins disable <id>
openclaw plugins doctor

plugins update solo funciona para instalaciones npm rastreadas bajo plugins.installs. Si los metadatos de integridad almacenados cambian entre actualizaciones, OpenClaw advierte y pide confirmación (usa --yes global para omitir prompts).

Los plugins también pueden registrar sus propios comandos de nivel superior (ejemplo: openclaw voicecall).

API de plugins (resumen)

Los plugins exportan ya sea:

  • Una función: (api) => { ... }
  • Un objeto: { id, name, configSchema, register(api) { ... } }

register(api) es donde los plugins adjuntan comportamiento. Los registros comunes incluyen:

  • registerTool
  • registerHook
  • on(...) para hooks tipados de ciclo de vida
  • registerChannel
  • registerProvider
  • registerHttpRoute
  • registerCommand
  • registerCli
  • registerContextEngine
  • registerService

Los plugins de motor de contexto también pueden registrar un gestor de contexto propiedad del runtime:

export default function (api) {
  api.registerContextEngine("lossless-claw", () => ({
    info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages }) {
      return { messages, estimatedTokens: 0 };
    },
    async compact() {
      return { ok: true, compacted: false };
    },
  }));
}

Luego habilítalo en la configuración:

{
  plugins: {
    slots: {
      contextEngine: "lossless-claw",
    },
  },
}

Hooks de plugins

Los plugins pueden registrar hooks en runtime. Esto permite a un plugin incluir automatización basada en eventos sin una instalación separada de pack de hooks.

Ejemplo

export default function register(api) {
  api.registerHook(
    "command:new",
    async () => {
      // Lógica del hook aquí.
    },
    {
      name: "my-plugin.command-new",
      description: "Runs when /new is invoked",
    },
  );
}

Notas:

  • Registra hooks explícitamente mediante api.registerHook(...).
  • Las reglas de elegibilidad de hooks siguen aplicándose (requisitos de OS/bins/env/config).
  • Los hooks gestionados por plugins aparecen en openclaw hooks list con plugin:<id>.
  • No puedes habilitar/deshabilitar hooks gestionados por plugins mediante openclaw hooks; habilita/deshabilita el plugin en su lugar.

Hooks de ciclo de vida del agente (api.on)

Para hooks tipados de ciclo de vida del runtime, usa api.on(...):

export default function register(api) {
  api.on(
    "before_prompt_build",
    (event, ctx) => {
      return {
        prependSystemContext: "Follow company style guide.",
      };
    },
    { priority: 10 },
  );
}

Hooks importantes para la construcción de prompts:

  • before_model_resolve: se ejecuta antes de cargar la sesión (messages no están disponibles). Úsalo para sobreescribir determinísticamente modelOverride o providerOverride.
  • before_prompt_build: se ejecuta después de cargar la sesión (messages están disponibles). Úsalo para dar forma a la entrada del prompt.
  • before_agent_start: hook de compatibilidad legacy. Prefiere los dos hooks explícitos anteriores.

Política de hooks impuesta por el core:

  • Los operadores pueden deshabilitar hooks de mutación de prompt por plugin con plugins.entries.<id>.hooks.allowPromptInjection: false.
  • Cuando está deshabilitado, OpenClaw bloquea before_prompt_build e ignora campos que mutan prompts devueltos desde before_agent_start legacy mientras preserva modelOverride y providerOverride legacy.

Campos de resultado de before_prompt_build:

  • prependContext: antepone texto al prompt de usuario para esta ejecución. Mejor para contenido específico del turno o dinámico.
  • systemPrompt: sobreescritura completa del prompt del sistema.
  • prependSystemContext: antepone texto al prompt del sistema actual.
  • appendSystemContext: agrega texto al prompt del sistema actual.

Orden de construcción del prompt en runtime embebido:

  1. Aplicar prependContext al prompt de usuario.
  2. Aplicar sobreescritura de systemPrompt cuando se proporcione.
  3. Aplicar prependSystemContext + prompt del sistema actual + appendSystemContext.

Notas de merge y precedencia:

  • Los handlers de hooks se ejecutan por prioridad (mayor primero).
  • Para campos de contexto fusionados, los valores se concatenan en orden de ejecución.
  • Los valores de before_prompt_build se aplican antes de los valores de fallback de before_agent_start legacy.

Guía de migración:

  • Mueve las guías estáticas de prependContext a prependSystemContext (o appendSystemContext) para que los proveedores puedan cachear contenido estable del prefijo del sistema.
  • Mantén prependContext para contexto dinámico por turno que deba permanecer vinculado al mensaje de usuario.

Plugins de proveedor (auth de modelo)

Los plugins pueden registrar proveedores de modelos para que los usuarios puedan ejecutar OAuth o configuración de clave API dentro de OpenClaw, mostrar la configuración del proveedor en onboarding/selectores de modelo, y contribuir al descubrimiento implícito de proveedores.

Los plugins de proveedor son la costura de extensión modular para la configuración de proveedores de modelos. Ya no son solo “helpers de OAuth”.

Ciclo de vida del plugin de proveedor

Un plugin de proveedor puede participar en cinco fases distintas:

  1. Auth auth[].run(ctx) realiza OAuth, captura de clave API, código de dispositivo, o configuración personalizada y devuelve perfiles de auth más parches de config opcionales.
  2. Configuración no interactiva auth[].runNonInteractive(ctx) gestiona openclaw onboard --non-interactive sin prompts. Usa esto cuando el proveedor necesita configuración headless personalizada más allá de las rutas simples integradas de clave API.
  3. Integración con wizard wizard.onboarding agrega una entrada a openclaw onboard. wizard.modelPicker agrega una entrada de configuración al selector de modelos.
  4. Descubrimiento implícito discovery.run(ctx) puede contribuir configuración de proveedor automáticamente durante la resolución/listado de modelos.
  5. Seguimiento post-selección onModelSelected(ctx) se ejecuta después de elegir un modelo. Úsalo para trabajo específico del proveedor como descargar un modelo local.

Esta es la separación recomendada porque estas fases tienen diferentes requisitos de ciclo de vida:

  • auth es interactivo y escribe credenciales/config
  • la configuración no interactiva se basa en flags/env y no debe hacer prompts
  • los metadatos del wizard son estáticos y orientados a la UI
  • el descubrimiento debe ser seguro, rápido y tolerante a fallos
  • los hooks post-selección son efectos secundarios vinculados al modelo elegido

Contrato de auth de proveedor

auth[].run(ctx) devuelve:

  • profiles: perfiles de auth para escribir
  • configPatch: cambios opcionales a openclaw.json
  • defaultModel: referencia provider/model opcional
  • notes: notas opcionales orientadas al usuario

El core entonces:

  1. escribe los perfiles de auth devueltos
  2. aplica el cableado de config de perfil de auth
  3. fusiona el parche de config
  4. opcionalmente aplica el modelo por defecto
  5. ejecuta el hook onModelSelected del proveedor cuando es apropiado

Esto significa que un plugin de proveedor posee la lógica de configuración específica del proveedor, mientras el core posee la ruta genérica de persistencia y merge de config.

Contrato no interactivo de proveedor

auth[].runNonInteractive(ctx) es opcional. Impleméntalo cuando el proveedor necesite configuración headless que no se puede expresar mediante los flujos genéricos integrados de clave API.

El contexto no interactivo incluye:

  • la config actual y base
  • opciones CLI de onboarding parseadas
  • helpers de logging/error en runtime
  • directorios de agente/workspace
  • resolveApiKey(...) para leer claves de proveedor desde flags, env o perfiles de auth existentes respetando --secret-input-mode
  • toApiKeyCredential(...) para convertir una clave resuelta en una credencial de perfil de auth con el almacenamiento correcto de texto plano vs secret-ref

Usa esta superficie para proveedores como:

  • runtimes auto-alojados compatibles con OpenAI que necesitan --custom-base-url + --custom-model-id
  • verificación no interactiva o síntesis de config específica del proveedor

No hagas prompts desde runNonInteractive. Rechaza entradas faltantes con errores accionables en su lugar.

Metadatos del wizard de proveedor

wizard.onboarding controla cómo aparece el proveedor en el onboarding agrupado:

  • choiceId: valor de auth-choice
  • choiceLabel: label de opción
  • choiceHint: indicación corta
  • groupId: id de bucket de grupo
  • groupLabel: label de grupo
  • groupHint: indicación de grupo
  • methodId: método de auth a ejecutar

wizard.modelPicker controla cómo aparece un proveedor como entrada de “configurar esto ahora” en la selección de modelo:

  • label
  • hint
  • methodId

Cuando un proveedor tiene múltiples métodos de auth, el wizard puede apuntar a un método explícito o dejar que OpenClaw sintetice opciones por método.

OpenClaw valida los metadatos del wizard de proveedor cuando el plugin se registra:

  • los ids de método de auth duplicados o en blanco se rechazan
  • los metadatos del wizard se ignoran cuando el proveedor no tiene métodos de auth
  • los bindings de methodId inválidos se degradan a advertencias y recurren a los métodos de auth restantes del proveedor

Contrato de descubrimiento de proveedor

discovery.run(ctx) devuelve uno de:

  • { provider }
  • { providers }
  • null

Usa { provider } para el caso común donde el plugin posee un id de proveedor. Usa { providers } cuando un plugin descubre múltiples entradas de proveedor.

El contexto de descubrimiento incluye:

  • la config actual
  • directorios de agente/workspace
  • env del proceso
  • un helper para resolver la clave API del proveedor y un valor de clave API seguro para descubrimiento

El descubrimiento debe ser:

  • rápido
  • best-effort
  • seguro de omitir en caso de fallo
  • cuidadoso con efectos secundarios

No debe depender de prompts o configuración de larga duración.

Ordenamiento del descubrimiento

El descubrimiento de proveedores se ejecuta en fases ordenadas:

  • simple
  • profile
  • paired
  • late

Usa:

  • simple para descubrimiento barato solo de entorno
  • profile cuando el descubrimiento depende de perfiles de auth
  • paired para proveedores que necesitan coordinarse con otro paso de descubrimiento
  • late para sondeo costoso o de red local

La mayoría de los proveedores auto-alojados deben usar late.

Buenos límites de plugins de proveedor

Buen encaje para plugins de proveedor:

  • proveedores locales/auto-alojados con flujos de configuración personalizados
  • OAuth/código de dispositivo específico del proveedor
  • descubrimiento implícito de servidores de modelos locales
  • efectos secundarios post-selección como pulls de modelos

Encaje menos convincente:

  • proveedores triviales de solo clave API que difieren solo en variable de env, URL base y un modelo por defecto

Esos aún pueden ser plugins, pero el principal beneficio de modularidad viene de extraer primero los proveedores ricos en comportamiento.

Registra un proveedor mediante api.registerProvider(...). Cada proveedor expone uno o más métodos de auth (OAuth, clave API, código de dispositivo, etc.). Esos métodos pueden alimentar:

  • openclaw models auth login --provider <id> [--method <id>]
  • openclaw onboard
  • entradas de configuración de “proveedor personalizado” en el selector de modelos
  • descubrimiento implícito de proveedores durante resolución/listado de modelos

Ejemplo:

api.registerProvider({
  id: "acme",
  label: "AcmeAI",
  auth: [
    {
      id: "oauth",
      label: "OAuth",
      kind: "oauth",
      run: async (ctx) => {
        // Ejecutar flujo OAuth y devolver perfiles de auth.
        return {
          profiles: [
            {
              profileId: "acme:default",
              credential: {
                type: "oauth",
                provider: "acme",
                access: "...",
                refresh: "...",
                expires: Date.now() + 3600 * 1000,
              },
            },
          ],
          defaultModel: "acme/opus-1",
        };
      },
    },
  ],
  wizard: {
    onboarding: {
      choiceId: "acme",
      choiceLabel: "AcmeAI",
      groupId: "acme",
      groupLabel: "AcmeAI",
      methodId: "oauth",
    },
    modelPicker: {
      label: "AcmeAI (custom)",
      hint: "Connect a self-hosted AcmeAI endpoint",
      methodId: "oauth",
    },
  },
  discovery: {
    order: "late",
    run: async () => ({
      provider: {
        baseUrl: "https://acme.example/v1",
        api: "openai-completions",
        apiKey: "${ACME_API_KEY}",
        models: [],
      },
    }),
  },
});

Notas:

  • run recibe un ProviderAuthContext con helpers prompter, runtime, openUrl y oauth.createVpsAwareHandlers.
  • runNonInteractive recibe un ProviderAuthMethodNonInteractiveContext con helpers opts, resolveApiKey y toApiKeyCredential para onboarding headless.
  • Devuelve configPatch cuando necesites agregar modelos por defecto o config de proveedor.
  • Devuelve defaultModel para que --set-default pueda actualizar los defaults del agente.
  • wizard.onboarding agrega una opción de proveedor a openclaw onboard.
  • wizard.modelPicker agrega una entrada de “configurar este proveedor” al selector de modelos.
  • discovery.run devuelve ya sea { provider } para el id de proveedor propio del plugin o { providers } para descubrimiento multi-proveedor.
  • discovery.order controla cuándo se ejecuta el proveedor en relación a las fases de descubrimiento integradas: simple, profile, paired o late.
  • onModelSelected es el hook post-selección para trabajo de seguimiento específico del proveedor como descargar un modelo local.

Registrar un canal de mensajería

Los plugins pueden registrar plugins de canal que se comportan como canales integrados (WhatsApp, Telegram, etc.). La configuración del canal vive bajo channels.<id> y es validada por el código de tu plugin de canal.

const myChannel = {
  id: "acmechat",
  meta: {
    id: "acmechat",
    label: "AcmeChat",
    selectionLabel: "AcmeChat (API)",
    docsPath: "/channels/acmechat",
    blurb: "demo channel plugin.",
    aliases: ["acme"],
  },
  capabilities: { chatTypes: ["direct"] },
  config: {
    listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
    resolveAccount: (cfg, accountId) =>
      cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? {
        accountId,
      },
  },
  outbound: {
    deliveryMode: "direct",
    sendText: async () => ({ ok: true }),
  },
};

export default function (api) {
  api.registerChannel({ plugin: myChannel });
}

Notas:

  • Pon la config bajo channels.<id> (no plugins.entries).
  • meta.label se usa para labels en listas CLI/UI.
  • meta.aliases agrega ids alternativos para normalización e inputs CLI.
  • meta.preferOver lista ids de canal a omitir en auto-enable cuando ambos están configurados.
  • meta.detailLabel y meta.systemImage permiten que las UIs muestren labels/iconos de canal más ricos.

Hooks de onboarding de canal

Los plugins de canal pueden definir hooks opcionales de onboarding en plugin.onboarding:

  • configure(ctx) es el flujo de configuración base.
  • configureInteractive(ctx) puede poseer completamente la configuración interactiva tanto para estados configurados como no configurados.
  • configureWhenConfigured(ctx) puede sobreescribir el comportamiento solo para canales ya configurados.

Precedencia de hooks en el wizard:

  1. configureInteractive (si está presente)
  2. configureWhenConfigured (solo cuando el estado del canal ya está configurado)
  3. fallback a configure

Detalles del contexto:

  • configureInteractive y configureWhenConfigured reciben:
    • configured (true o false)
    • label (nombre del canal orientado al usuario utilizado por prompts)
    • más los campos compartidos de config/runtime/prompter/options
  • Devolver "skip" deja la selección y el seguimiento de cuenta sin cambios.
  • Devolver { cfg, accountId? } aplica actualizaciones de config y registra la selección de cuenta.

Escribir un nuevo canal de mensajería (paso a paso)

Usa esto cuando quieras una nueva superficie de chat (un “canal de mensajería”), no un proveedor de modelos. La documentación de proveedores de modelos vive bajo /providers/*.

  1. Elige un id + forma de config
  • Toda la config del canal vive bajo channels.<id>.
  • Prefiere channels.<id>.accounts.<accountId> para configuraciones multi-cuenta.
  1. Define los metadatos del canal
  • meta.label, meta.selectionLabel, meta.docsPath, meta.blurb controlan las listas CLI/UI.
  • meta.docsPath debe apuntar a una página de docs como /channels/<id>.
  • meta.preferOver permite que un plugin reemplace otro canal (auto-enable lo prefiere).
  • meta.detailLabel y meta.systemImage son usados por UIs para texto/iconos de detalle.
  1. Implementa los adaptadores requeridos
  • config.listAccountIds + config.resolveAccount
  • capabilities (tipos de chat, media, hilos, etc.)
  • outbound.deliveryMode + outbound.sendText (para envío básico)
  1. Agrega adaptadores opcionales según necesidad
  • setup (wizard), security (política DM), status (salud/diagnósticos)
  • gateway (start/stop/login), mentions, threading, streaming
  • actions (acciones de mensaje), commands (comportamiento de comandos nativos)
  1. Registra el canal en tu plugin
  • api.registerChannel({ plugin })

Ejemplo mínimo de config:

{
  channels: {
    acmechat: {
      accounts: {
        default: { token: "ACME_TOKEN", enabled: true },
      },
    },
  },
}

Plugin mínimo de canal (solo saliente):

const plugin = {
  id: "acmechat",
  meta: {
    id: "acmechat",
    label: "AcmeChat",
    selectionLabel: "AcmeChat (API)",
    docsPath: "/channels/acmechat",
    blurb: "AcmeChat messaging channel.",
    aliases: ["acme"],
  },
  capabilities: { chatTypes: ["direct"] },
  config: {
    listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
    resolveAccount: (cfg, accountId) =>
      cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? {
        accountId,
      },
  },
  outbound: {
    deliveryMode: "direct",
    sendText: async ({ text }) => {
      // entregar `text` a tu canal aquí
      return { ok: true };
    },
  },
};

export default function (api) {
  api.registerChannel({ plugin });
}

Carga el plugin (directorio de extensiones o plugins.load.paths), reinicia el gateway, luego configura channels.<id> en tu config.

Herramientas de agente

Consulta la guía dedicada: Herramientas de agente de plugin.

Registrar un método RPC del gateway

export default function (api) {
  api.registerGatewayMethod("myplugin.status", ({ respond }) => {
    respond(true, { ok: true });
  });
}

Registrar comandos CLI

export default function (api) {
  api.registerCli(
    ({ program }) => {
      program.command("mycmd").action(() => {
        console.log("Hello");
      });
    },
    { commands: ["mycmd"] },
  );
}

Registrar comandos de auto-respuesta

Los plugins pueden registrar comandos slash personalizados que se ejecutan sin invocar al agente de IA. Esto es útil para comandos toggle, verificaciones de estado o acciones rápidas que no necesitan procesamiento LLM.

export default function (api) {
  api.registerCommand({
    name: "mystatus",
    description: "Show plugin status",
    handler: (ctx) => ({
      text: `Plugin is running! Channel: ${ctx.channel}`,
    }),
  });
}

Contexto del handler de comando:

  • senderId: El ID del remitente (si está disponible)
  • channel: El canal donde se envió el comando
  • isAuthorizedSender: Si el remitente es un usuario autorizado
  • args: Argumentos pasados después del comando (si acceptsArgs: true)
  • commandBody: El texto completo del comando
  • config: La configuración actual de OpenClaw

Opciones del comando:

  • name: Nombre del comando (sin la / inicial)
  • nativeNames: Aliases opcionales de comandos nativos para superficies slash/menú. Usa default para todos los proveedores nativos, o claves específicas de proveedor como discord
  • description: Texto de ayuda mostrado en listas de comandos
  • acceptsArgs: Si el comando acepta argumentos (por defecto: false). Si es false y se proporcionan argumentos, el comando no coincide y el mensaje pasa a otros handlers
  • requireAuth: Si requiere remitente autorizado (por defecto: true)
  • handler: Función que devuelve { text: string } (puede ser async)

Ejemplo con autorización y argumentos:

api.registerCommand({
  name: "setmode",
  description: "Set plugin mode",
  acceptsArgs: true,
  requireAuth: true,
  handler: async (ctx) => {
    const mode = ctx.args?.trim() || "default";
    await saveMode(mode);
    return { text: `Mode set to: ${mode}` };
  },
});

Notas:

  • Los comandos de plugins se procesan antes que los comandos integrados y el agente de IA
  • Los comandos se registran globalmente y funcionan en todos los canales
  • Los nombres de comandos no distinguen mayúsculas de minúsculas (/MyStatus coincide con /mystatus)
  • Los nombres de comandos deben empezar con una letra y contener solo letras, números, guiones y guiones bajos
  • Los nombres de comandos reservados (como help, status, reset, etc.) no pueden ser sobreescritos por plugins
  • El registro duplicado de comandos entre plugins fallará con un error de diagnóstico

Registrar servicios en segundo plano

export default function (api) {
  api.registerService({
    id: "my-service",
    start: () => api.logger.info("ready"),
    stop: () => api.logger.info("bye"),
  });
}

Convenciones de nombres

  • Métodos del gateway: pluginId.action (ejemplo: voicecall.status)
  • Herramientas: snake_case (ejemplo: voice_call)
  • Comandos CLI: kebab o camel, pero evita colisiones con comandos core

Skills

Los plugins pueden incluir un skill en el repositorio (skills/<name>/SKILL.md). Habilítalo con plugins.entries.<id>.enabled (u otros controles de config) y asegúrate de que esté presente en las ubicaciones de skills de tu workspace/gestionados.

Distribución (npm)

Empaquetado recomendado:

  • Paquete principal: openclaw (este repositorio)
  • Plugins: paquetes npm separados bajo @openclaw/* (ejemplo: @openclaw/voice-call)

Contrato de publicación:

  • El package.json del plugin debe incluir openclaw.extensions con uno o más archivos de entrada.
  • Los archivos de entrada pueden ser .js o .ts (jiti carga TS en runtime).
  • openclaw plugins install <npm-spec> usa npm pack, extrae en ~/.openclaw/extensions/<id>/ y lo habilita en la config.
  • Estabilidad de claves de config: los paquetes con scope se normalizan al id sin scope para plugins.entries.*.

Plugin de ejemplo: Voice Call

Este repositorio incluye un plugin de llamada de voz (Twilio o log fallback):

  • Fuente: extensions/voice-call
  • Skill: skills/voice-call
  • CLI: openclaw voicecall start|status
  • Herramienta: voice_call
  • RPC: voicecall.start, voicecall.status
  • Config (twilio): provider: "twilio" + twilio.accountSid/authToken/from (opcionales statusCallbackUrl, twimlUrl)
  • Config (dev): provider: "log" (sin red)

Consulta Voice Call y extensions/voice-call/README.md para configuración y uso.

Notas de seguridad

Los plugins se ejecutan en proceso con el Gateway. Trátalos como código confiable:

  • Solo instala plugins en los que confíes.
  • Prefiere listas de permitidos plugins.allow.
  • Recuerda que plugins.allow es basado en id, así que un plugin de workspace habilitado puede intencionalmente sobreescribir un plugin incluido con el mismo id.
  • Reinicia el Gateway después de cambios.

Probar plugins

Los plugins pueden (y deberían) incluir tests:

  • Los plugins in-repo pueden mantener tests de Vitest bajo src/** (ejemplo: src/plugins/voice-call.plugin.test.ts).
  • Los plugins publicados por separado deben ejecutar su propio CI (lint/build/test) y validar que openclaw.extensions apunte al entrypoint construido (dist/index.js).