Plugins
Ganchos de Plugin
Los hooks de Plugin son puntos de extensión en proceso para los plugins de OpenClaw. Úsalos cuando un plugin necesita inspeccionar o cambiar ejecuciones de agentes, llamadas a herramientas, flujo de mensajes, ciclo de vida de sesiones, enrutamiento de subagentes, instalaciones o arranque del Gateway.
Usa hooks internos en su lugar cuando quieras un pequeño
script HOOK.md instalado por el operador para comandos y eventos del Gateway como
/new, /reset, /stop, agent:bootstrap o gateway:startup.
Inicio rápido
Registra hooks tipados de plugin con api.on(...) desde la entrada de tu plugin:
export default definePluginEntry({
id: "tool-preflight",
name: "Tool Preflight",
register(api) {
api.on(
"before_tool_call",
async (event) => {
if (event.toolName !== "web_search") {
return;
}
return {
requireApproval: {
title: "Run web search",
description: `Allow search query: ${String(event.params.query ?? "")}`,
severity: "info",
timeoutMs: 60_000,
timeoutBehavior: "deny",
},
};
},
{ priority: 50 },
);
},
});
Los manejadores de hooks se ejecutan secuencialmente en orden descendente de priority. Los hooks con la misma prioridad
mantienen el orden de registro.
api.on(name, handler, opts?) acepta:
priority- orden del manejador (los valores más altos se ejecutan primero).timeoutMs- presupuesto opcional por hook. Cuando se define, el ejecutor de hooks aborta ese manejador después de que vence el presupuesto y continúa con el siguiente, en vez de permitir que una preparación lenta o trabajo de recuperación consuma el tiempo de espera de modelo configurado por el llamador. Omítelo para usar el tiempo de espera predeterminado de observación/decisión que el ejecutor de hooks aplica de forma genérica.
Los operadores también pueden definir presupuestos de hooks sin modificar el código del plugin:
{
"plugins": {
"entries": {
"my-plugin": {
"hooks": {
"timeoutMs": 30000,
"timeouts": {
"before_prompt_build": 90000,
"agent_end": 60000
}
}
}
}
}
}
hooks.timeouts.<hookName> sobrescribe hooks.timeoutMs, que sobrescribe el valor
api.on(..., { timeoutMs }) definido por el plugin. Cada valor configurado debe
ser un entero positivo no mayor que 600000 milisegundos. Prefiere sobrescrituras por hook
para hooks lentos conocidos, de modo que un plugin no reciba un presupuesto más largo
en todas partes.
Cada hook recibe event.context.pluginConfig, la configuración resuelta para el
plugin que registró ese manejador. Úsala para decisiones de hooks que necesiten
opciones actuales del plugin; OpenClaw la inyecta por manejador sin mutar el
objeto de evento compartido que ven otros plugins.
Catálogo de hooks
Los hooks se agrupan por la superficie que extienden. Los nombres en negrita aceptan un resultado de decisión (bloquear, cancelar, sobrescribir o requerir aprobación); todos los demás son solo de observación.
Turno del agente
before_model_resolve- sobrescribe el proveedor o el modelo antes de que se carguen los mensajes de sesiónagent_turn_prepare- consume inyecciones de turno de plugin en cola y añade contexto del mismo turno antes de los hooks de promptbefore_prompt_build- añade contexto dinámico o texto de prompt de sistema antes de la llamada al modelobefore_agent_start- fase combinada solo por compatibilidad; prefiere los dos hooks anterioresbefore_agent_run- inspecciona el prompt final y los mensajes de sesión antes del envío al modelo y, opcionalmente, bloquea la ejecuciónbefore_agent_reply- interrumpe el turno del modelo con una respuesta sintética o silenciobefore_agent_finalize- inspecciona la respuesta final natural y solicita una pasada más del modeloagent_end- observa mensajes finales, estado de éxito y duración de la ejecuciónheartbeat_prompt_contribution- añade contexto solo de Heartbeat para plugins de monitor en segundo plano y ciclo de vida
Observación de conversación
model_call_started/model_call_ended- observa metadatos saneados de llamada a proveedor/modelo, tiempos, resultado y hashes acotados de identificadores de solicitud sin contenido de prompt ni de respuestallm_input- observa la entrada del proveedor (prompt de sistema, prompt, historial)llm_output- observa la salida del proveedor
Herramientas
before_tool_call- reescribe parámetros de herramienta, bloquea la ejecución o requiere aprobaciónafter_tool_call- observa resultados de herramienta, errores y duracióntool_result_persist- reescribe el mensaje del asistente producido a partir de un resultado de herramientabefore_message_write- inspecciona o bloquea una escritura de mensaje en curso (raro)
Mensajes y entrega
inbound_claim- reclama un mensaje entrante antes del enrutamiento del agente (respuestas sintéticas)message_received- observa contenido entrante, remitente, hilo y metadatosmessage_sending- reescribe contenido saliente o cancela la entregamessage_sent- observa el éxito o fallo de la entrega salientebefore_dispatch- inspecciona o reescribe un despacho saliente antes del traspaso al canalreply_dispatch- participa en la canalización final de despacho de respuestas
Sesiones y Compaction
session_start/session_end- rastrea límites del ciclo de vida de la sesiónbefore_compaction/after_compaction- observa o anota ciclos de Compactionbefore_reset- observa eventos de restablecimiento de sesión (/reset, restablecimientos programáticos)
Subagentes
subagent_spawning/subagent_delivery_target/subagent_spawned/subagent_ended- coordinan el enrutamiento de subagentes y la entrega de finalización
Ciclo de vida
gateway_start/gateway_stop- inicia o detiene servicios propiedad del plugin con el Gatewaycron_changed- observa cambios del ciclo de vida de cron propiedad del gateway (añadido, actualizado, eliminado, iniciado, finalizado, programado)before_install- inspecciona escaneos de instalación de Skills o plugins y, opcionalmente, bloquea
Política de llamadas a herramientas
before_tool_call recibe:
event.toolNameevent.paramsevent.runIdopcionalevent.toolCallIdopcional- campos de contexto como
ctx.agentId,ctx.sessionKey,ctx.sessionId,ctx.runId,ctx.jobId(definido en ejecuciones impulsadas por cron) yctx.tracede diagnóstico
Puede devolver:
type BeforeToolCallResult = {
params?: Record<string, unknown>;
block?: boolean;
blockReason?: string;
requireApproval?: {
title: string;
description: string;
severity?: "info" | "warning" | "critical";
timeoutMs?: number;
timeoutBehavior?: "allow" | "deny";
pluginId?: string;
onResolution?: (
decision: "allow-once" | "allow-always" | "deny" | "timeout" | "cancelled",
) => Promise<void> | void;
};
};
Reglas:
block: truees terminal y omite manejadores de menor prioridad.block: falsese trata como ausencia de decisión.paramsreescribe los parámetros de la herramienta para la ejecución.requireApprovalpausa la ejecución del agente y pregunta al usuario mediante aprobaciones de plugin. El comando/approvepuede aprobar tanto aprobaciones de exec como de plugin.- Un
block: truede menor prioridad todavía puede bloquear después de que un hook de mayor prioridad haya solicitado aprobación. onResolutionrecibe la decisión de aprobación resuelta:allow-once,allow-always,deny,timeoutocancelled.
Los plugins incluidos que necesitan política de nivel de host pueden registrar políticas de herramientas de confianza
con api.registerTrustedToolPolicy(...). Estas se ejecutan antes de los hooks ordinarios
before_tool_call y antes de las decisiones de plugins externos. Úsalas solo
para controles confiados por el host, como política de espacio de trabajo, aplicación de presupuesto o
seguridad de flujos de trabajo reservados. Los plugins externos deben usar hooks normales before_tool_call.
Persistencia de resultados de herramientas
Los resultados de herramientas pueden incluir details estructurados para renderizado de interfaz de usuario, diagnósticos,
enrutamiento de medios o metadatos propiedad del plugin. Trata details como metadatos de runtime,
no como contenido de prompt:
- OpenClaw elimina
toolResult.detailsantes de la reproducción al proveedor y de la entrada de Compaction para que los metadatos no se conviertan en contexto del modelo. - Las entradas de sesión persistidas conservan solo
detailsacotados. Los detalles demasiado grandes se reemplazan con un resumen compacto ypersistedDetailsTruncated: true. tool_result_persistybefore_message_writese ejecutan antes del límite final de persistencia. Aun así, los hooks deben mantener pequeños losdetailsdevueltos y evitar colocar texto relevante para el prompt solo endetails; coloca la salida de herramienta visible para el modelo encontent.
Hooks de prompt y modelo
Usa los hooks específicos de fase para plugins nuevos:
before_model_resolve: recibe solo el prompt actual y los metadatos de adjuntos. DevuelveproviderOverrideomodelOverride.agent_turn_prepare: recibe el prompt actual, los mensajes de sesión preparados y cualquier inyección en cola de exactamente una vez drenada para esta sesión. DevuelveprependContextoappendContext.before_prompt_build: recibe el prompt actual y los mensajes de sesión. DevuelveprependContext,appendContext,systemPrompt,prependSystemContextoappendSystemContext.heartbeat_prompt_contribution: se ejecuta solo para turnos de Heartbeat y devuelveprependContextoappendContext. Está pensado para monitores en segundo plano que necesitan resumir el estado actual sin cambiar turnos iniciados por el usuario.
before_agent_start permanece por compatibilidad. Prefiere los hooks explícitos anteriores
para que tu plugin no dependa de una fase combinada heredada.
before_agent_run se ejecuta después de la construcción del prompt y antes de cualquier entrada de modelo,
incluida la carga de imágenes locales al prompt y la observación llm_input. Recibe
la entrada actual del usuario como prompt, además del historial de sesión cargado en messages
y el prompt de sistema activo. Devuelve { outcome: "block", reason, message? }
para detener la ejecución antes de que el modelo pueda leer el prompt. reason es interno;
message es el reemplazo orientado al usuario. Los únicos resultados admitidos son
pass y block; las formas de decisión no admitidas fallan cerradas.
Cuando se bloquea una ejecución, OpenClaw almacena solo el texto de reemplazo en
message.content más metadatos de bloqueo no sensibles, como el id del plugin bloqueante
y la marca de tiempo. El texto original del usuario no se conserva en la transcripción ni en el contexto
futuro. Las razones internas de bloqueo se tratan como sensibles y se excluyen de
transcripciones, historial, difusión, registros y cargas de diagnóstico. La observabilidad
debe usar campos saneados como id del bloqueador, resultado, marca de tiempo o una categoría
segura.
before_agent_start y agent_end incluyen event.runId cuando OpenClaw puede
identificar la ejecución activa. El mismo valor también está disponible en ctx.runId.
Las ejecuciones impulsadas por Cron también exponen ctx.jobId (el id del trabajo cron de origen) para que
los hooks de plugins puedan acotar métricas, efectos secundarios o estado a un trabajo programado
específico.
Para ejecuciones originadas en canales, ctx.messageProvider es la superficie del proveedor, como
discord o telegram, mientras que ctx.channelId es el identificador de destino de la conversación
cuando OpenClaw puede derivarlo de la clave de sesión o de los metadatos de entrega.
agent_end es un hook de observación y se ejecuta en segundo plano después del turno. El
ejecutor de hooks aplica un tiempo de espera de 30 segundos para que un plugin bloqueado o un endpoint
de embeddings no pueda dejar la promesa del hook pendiente para siempre. Un tiempo de espera se registra y
OpenClaw continúa; no cancela trabajo de red propiedad del plugin a menos que el
plugin también use su propia señal de aborto.
Usa model_call_started y model_call_ended para telemetría de llamadas a proveedor
que no debe recibir prompts sin procesar, historial, respuestas, encabezados, cuerpos de solicitud
ni identificadores de solicitud del proveedor. Estos hooks incluyen metadatos estables como
runId, callId, provider, model, api/transport opcionales,
durationMs/outcome terminales y upstreamRequestIdHash cuando OpenClaw puede derivar un
hash acotado del identificador de solicitud del proveedor.
before_agent_finalize se ejecuta solo cuando un arnés está a punto de aceptar una
respuesta final natural del asistente. No es la ruta de cancelación /stop y no
se ejecuta cuando el usuario aborta un turno. Devuelve { action: "revise", reason } para pedir
al arnés una pasada más del modelo antes de la finalización, { action: "finalize", reason? } para forzar la finalización, u omite un resultado para continuar.
Los hooks nativos Stop de Codex se retransmiten a este hook como decisiones
before_agent_finalize de OpenClaw.
Al devolver action: "revise", los plugins pueden incluir metadatos retry para hacer
que la pasada adicional del modelo esté acotada y sea segura para reproducción:
type BeforeAgentFinalizeRetry = {
instruction: string;
idempotencyKey?: string;
maxAttempts?: number;
};
instruction se agrega al motivo de revisión enviado al arnés.
idempotencyKey permite al host contar los reintentos para la misma solicitud de Plugin en decisiones de finalización equivalentes, y maxAttempts limita cuántas pasadas adicionales permitirá el host antes de continuar con la respuesta final natural.
Los Plugins no incluidos que necesitan hooks de conversación sin procesar (before_model_resolve,
before_agent_reply, llm_input, llm_output, before_agent_finalize,
agent_end o before_agent_run) deben definir:
{
"plugins": {
"entries": {
"my-plugin": {
"hooks": {
"allowConversationAccess": true
}
}
}
}
}
Los hooks que modifican prompts y las inyecciones duraderas del siguiente turno pueden deshabilitarse por Plugin con plugins.entries.<id>.hooks.allowPromptInjection=false.
Extensiones de sesión e inyecciones del siguiente turno
Los Plugins de flujo de trabajo pueden persistir un pequeño estado de sesión compatible con JSON mediante api.registerSessionExtension(...) y actualizarlo a través del método sessions.pluginPatch del Gateway. Las filas de sesión proyectan el estado de extensión registrado mediante pluginExtensions, lo que permite que Control UI y otros clientes representen el estado propiedad del Plugin sin conocer sus detalles internos.
Usa api.enqueueNextTurnInjection(...) cuando un Plugin necesita que un contexto duradero llegue exactamente una vez al siguiente turno del modelo. OpenClaw consume las inyecciones en cola antes de los hooks de prompt, descarta las inyecciones caducadas y deduplica por idempotencyKey por Plugin. Esta es la interfaz adecuada para reanudaciones de aprobación, resúmenes de políticas, deltas de monitores en segundo plano y continuaciones de comandos que deben ser visibles para el modelo en el siguiente turno, pero no deben convertirse en texto permanente del prompt del sistema.
La semántica de limpieza forma parte del contrato. La limpieza de extensiones de sesión y las callbacks de limpieza del ciclo de vida en tiempo de ejecución reciben reset, delete, disable o restart. El host elimina el estado persistente de extensión de sesión del Plugin propietario y las inyecciones pendientes del siguiente turno para reset/delete/disable; restart conserva el estado duradero de sesión mientras las callbacks de limpieza permiten que los Plugins liberen trabajos del planificador, contexto de ejecución y otros recursos fuera de banda de la generación anterior del runtime.
Hooks de mensajes
Usa hooks de mensajes para enrutamiento a nivel de canal y políticas de entrega:
message_received: observa contenido entrante, remitente,threadId,messageId,senderId, correlación opcional de ejecución/sesión y metadatos.message_sending: reescribecontento devuelve{ cancel: true }.message_sent: observa el éxito o fallo final.
Para respuestas TTS solo de audio, content puede contener la transcripción hablada oculta incluso cuando la carga útil del canal no tiene texto/caption visible. Reescribir ese content actualiza solo la transcripción visible para el hook; no se renderiza como caption multimedia.
Los contextos de hooks de mensajes exponen campos de correlación estables cuando están disponibles:
ctx.sessionKey, ctx.runId, ctx.messageId, ctx.senderId, ctx.trace,
ctx.traceId, ctx.spanId, ctx.parentSpanId y ctx.callDepth. Prefiere estos campos de primera clase antes de leer metadatos heredados.
Prefiere los campos tipados threadId y replyToId antes de usar metadatos específicos del canal.
Reglas de decisión:
message_sendingconcancel: truees terminal.message_sendingconcancel: falsese trata como ausencia de decisión.- El
contentreescrito continúa hacia hooks de menor prioridad a menos que un hook posterior cancele la entrega.
Hooks de instalación
before_install se ejecuta después del escaneo integrado de instalaciones de Skills y Plugins.
Devuelve hallazgos adicionales o { block: true, blockReason } para detener la instalación.
block: true es terminal. block: false se trata como ausencia de decisión.
Ciclo de vida del Gateway
Usa gateway_start para servicios de Plugin que necesitan estado propiedad del Gateway. El contexto expone ctx.config, ctx.workspaceDir y ctx.getCron?.() para inspección y actualizaciones de cron. Usa gateway_stop para limpiar recursos de larga duración.
No dependas del hook interno gateway:startup para servicios en tiempo de ejecución propiedad del Plugin.
cron_changed se dispara para eventos del ciclo de vida de cron propiedad del gateway con una carga útil de evento tipada que cubre los motivos added, updated, removed, started, finished y scheduled. El evento contiene una instantánea PluginHookGatewayCronJob (incluidos state.nextRunAtMs, state.lastRunStatus y state.lastError cuando estén presentes), además de un PluginHookGatewayCronDeliveryStatus de not-requested | delivered | not-delivered | unknown. Los eventos eliminados siguen conteniendo la instantánea del trabajo eliminado para que los planificadores externos puedan reconciliar el estado. Usa ctx.getCron?.() y ctx.config del contexto en tiempo de ejecución al sincronizar planificadores de activación externos, y mantén OpenClaw como la fuente de verdad para las comprobaciones de vencimiento y la ejecución.
Próximas obsolescencias
Algunas superficies adyacentes a hooks están obsoletas, pero siguen siendo compatibles. Migra antes de la próxima versión mayor:
- Envoltorios de canal en texto sin formato en manejadores
inbound_claimymessage_received. LeeBodyForAgenty los bloques estructurados de contexto de usuario en lugar de analizar texto plano de envoltorio. Consulta Envoltorios de canal en texto sin formato → BodyForAgent. before_agent_startpermanece por compatibilidad. Los nuevos Plugins deben usarbefore_model_resolveybefore_prompt_builden lugar de la fase combinada.onResolutionenbefore_tool_callahora usa la unión tipadaPluginApprovalResolution(allow-once/allow-always/deny/timeout/cancelled) en lugar de unstringde formato libre.
Para la lista completa - registro de capacidades de memoria, perfil de razonamiento del proveedor, proveedores de autenticación externos, tipos de descubrimiento de proveedores, accesores de runtime de tareas y el cambio de nombre de command-auth → command-status - consulta
Migración del Plugin SDK → Obsolescencias activas.
Relacionado
- Migración del Plugin SDK - obsolescencias activas y calendario de eliminación
- Creación de Plugins
- Descripción general del Plugin SDK
- Puntos de entrada de Plugin
- Hooks internos
- Detalles internos de la arquitectura de Plugins