Plugin hooks
Les hooks de plugin sont des points d’extension en processus pour les plugins OpenClaw. Utilisez-les lorsqu’un plugin a besoin d’inspecter ou de modifier les exécutions d’agent, les appels d’outils, le flux de messages, le cycle de vie de la session, le routage des sous-agents, les installations ou le démarrage du Gateway.
Utilisez plutôt les internal hooks lorsque vous souhaitez un petit script HOOK.md installé par l’opérateur pour les événements de commande et de Gateway tels que /new, /reset, /stop, agent:bootstrap ou gateway:startup.
Quick start
Section intitulée « Quick start »Enregistrez les hooks de plugin typés avec api.on(...) depuis le point d’entrée de votre plugin :
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
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 }, ); },});Les gestionnaires de hooks s’exécutent séquentiellement par priority décroissant. Les hooks de même priorité
conservent l’ordre d’enregistrement.
api.on(name, handler, opts?) accepte :
priority- ordre d’exécution des gestionnaires (le plus élevé s’exécute en premier).timeoutMs- budget optionnel par hook. Lorsqu’il est défini, le lanceur de hooks interrompt ce gestionnaire après l’expiration du budget et passe au suivant, au lieu de permettre à une configuration lente ou à un travail de rappel de consommer le délai d’expiration du model configuré par l’appelant. Omettez-le pour utiliser le délai d’expiration d’observation/décision par défaut que le lanceur de hooks applique de manière générique.
Les opérateurs peuvent également définir des budgets de hooks sans modifier le code du plugin :
{ "plugins": { "entries": { "my-plugin": { "hooks": { "timeoutMs": 30000, "timeouts": { "before_prompt_build": 90000, "agent_end": 60000 } } } } }}hooks.timeouts.<hookName> remplace hooks.timeoutMs, qui remplace la valeur
api.on(..., { timeoutMs }) définie par le plugin. Chaque valeur configurée doit
être un entier positif ne dépassant pas 600 000 millisecondes. Préférez les remplacements
par hook pour les hooks connus comme étant lents, afin qu’un seul plugin n’obtienne pas un budget plus long
tout le temps.
Chaque hook reçoit event.context.pluginConfig, la configuration résolue pour le
plugin qui a enregistré ce gestionnaire. Utilisez-la pour les décisions de hook qui nécessitent
les options actuelles du plugin ; OpenClaw l’injecte pour chaque gestionnaire sans modifier l’
objet d’événement partagé vu par les autres plugins.
Hook catalog
Section intitulée « Hook catalog »Les hooks sont regroupés par la surface qu’ils étendent. Les noms en gras acceptent un résultat de décision (bloquer, annuler, remplacer ou exiger une approbation) ; tous les autres sont d’observation uniquement.
Agent turn
before_model_resolve- remplacer le provider ou le model avant le chargement des messages de sessionagent_turn_prepare- consommer les injections de tour de plugin en file d’attente et ajouter du contexte de même tour avant les hooks de promptbefore_prompt_build- ajouter du contexte dynamique ou du texte de système de prompt avant l’appel au modelbefore_agent_start- phase combinée uniquement pour compatibilité ; préférez les deux hooks ci-dessusbefore_agent_run- inspecter le prompt final et les messages de session avant la soumission au model et bloquer facultativement l’exécutionbefore_agent_reply- court-circuiter le tour du model avec une réponse synthétique ou du silencebefore_agent_finalize- inspecter la réponse finale naturelle et demander un passage supplémentaire du modelagent_end- observer les messages finaux, l’état de succès et la durée d’exécutionheartbeat_prompt_contribution- ajouter un contexte de battement de cœur uniquement pour les plugins de surveillance en arrière-plan et de cycle de vie
Observation de la conversation
model_call_started/model_call_ended- observer les métadonnées de l’appel provider/model nettoyées, la minutage, le résultat et les hachages d’ID de demande bornés sans le contenu du prompt ou de la réponsellm_input- observer l’entrée du provider (system prompt, prompt, historique)llm_output- observer la sortie du provider, l’utilisation et lecontextTokenBudgetrésolu si disponible
Outils
before_tool_call- réécrire les paramètres de l’outil, bloquer l’exécution ou exiger une approbationafter_tool_call- observer les résultats de l’outil, les erreurs et la duréetool_result_persist- réécrire le message de l’assistant produit à partir d’un résultat de l’outilbefore_message_write- inspecter ou bloquer une écriture de message en cours (rare)
Messages et livraison
inbound_claim- réclamer un message entrant avant le routage de l’agent (réponses synthétiques)message_received- observer le contenu entrant, l’expéditeur, le fil de discussion et les métadonnéesmessage_sending- réécrire le contenu sortant ou annuler la livraisonmessage_sent- observer le succès ou l’échec de la livraison sortantebefore_dispatch- inspecter ou réécrire une expédition sortante avant le transfert au channelreply_dispatch- participer au pipeline final de réponse-expédition
Sessions et compactage
session_start/session_end- suivre les limites du cycle de vie de session. Lereasonde l’événement est l’un des suivants :new,reset,idle,daily,compaction,deleted,shutdown,restartouunknown. Les valeursshutdownetrestartsont déclenchées par le finaliseur d’arrêt de la passerelle lorsque le processus est arrêté ou redémarré alors que des sessions sont toujours actives, afin que les plugins en aval (tels que les magasins de mémoire ou de transcriptions) puissent finaliser les lignes fantômes qui resteraient sinon dans un état ouvert après les redémarrages. Le finaliseur est borné, ce qui empêche un plugin lent de bloquer SIGTERM/SIGINT.before_compaction/after_compaction- observer ou annoter les cycles de compactagebefore_reset- observer les événements de réinitialisation de session (/reset, réinitialisations programmatiques)
Sous-agents
subagent_spawning/subagent_delivery_target/subagent_spawned/subagent_ended- coordonner le routage des sous-agents et la livraison des achèvements
Cycle de vie
gateway_start/gateway_stop- démarrer ou arrêter les services détenus par le plugin avec le Gatewaydeactivate- alias de compatibilité obsolète pourgateway_stop; utilisezgateway_stopdans les nouveaux pluginscron_changed- observer les changements du cycle de vie cron détenus par la passerelle (ajoutés, mis à jour, supprimés, démarrés, terminés, programmés)before_install- inspecter les analyses d’installation de compétences ou de plugins et éventuellement bloquer
Déboguer les hooks d’exécution
Section intitulée « Déboguer les hooks d’exécution »Utilisez before_model_resolve lorsqu’un plugin doit changer le fournisseur ou le model pour un tour d’agent. Il s’exécute avant la résolution du model ; llm_output ne s’exécute qu’après qu’une tentative de model ait produit une sortie de l’assistant.
Pour prouver l’efficacité du modèle de session, inspectez les enregistrements d’exécution, puis
utilisez openclaw sessions ou les surfaces session/status du Gateway. Lors du débogage
des payloads du provider, démarrez le Gateway avec --raw-stream et
--raw-stream-path <path> ; ces indicateurs écrivent les événements bruts du flux du model dans un
fichier l.
Stratégie d’appel de tool
Section intitulée « Stratégie d’appel de tool »before_tool_call reçoit :
event.toolNameevent.params- optionnel
event.derivedPaths, contenant des indications de chemin cible dérivées de l’hôte au mieux pour les enveloppes de tools connues commeapply_patch; lorsqu’elles sont présentes, ces chemins peuvent être incomplets ou surestimer ce que le tool touchera réellement (par exemple, avec des entrées malformées ou partielles) - optionnel
event.runId - optionnel
event.toolCallId - champs de contexte tels que
ctx.agentId,ctx.sessionKey,ctx.sessionId,ctx.runId,ctx.jobId(défini sur les exécutions pilotées par cron), etctx.tracede diagnostic
Il peut retourner :
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; };};Comportement du garde-hook pour les hooks de cycle de vie typés :
block: trueest terminal et ignore les gestionnaires de priorité inférieure.block: falseest traité comme l’absence de décision.paramsréécrit les paramètres du tool pour l’exécution.requireApprovalmet en pause l’exécution de l’agent et demande à l’utilisateur via les approbations du plugin. La commande/approvepeut approuver à la fois les approbations d’exécution et de plugin.- Un
block: truede priorité inférieure peut toujours bloquer après qu’un hook de priorité supérieure a demandé une approbation. onResolutionreçoit la décision d’approbation résolue -allow-once,allow-always,deny,timeoutoucancelled.
Les plugins groupés nécessitant une politique au niveau de l’hôte peuvent enregistrer des stratégies d’outil de confiance avec api.registerTrustedToolPolicy(...). Celles-ci s’exécutent avant les hooks before_tool_call ordinaires et avant les décisions des plugins externes. Utilisez-les uniquement pour les portes de confiance de l’hôte telles que la politique de l’espace de travail, l’application du budget ou la sécurité des flux de travail réservés. Les plugins externes doivent utiliser les hooks before_tool_call normaux.
Persistance des résultats d’outils
Section intitulée « Persistance des résultats d’outils »Les résultats d’outils peuvent inclure des details structurées pour le rendu de l’interface utilisateur, les diagnostics, le routage des médias ou les métadonnées propres au plugin. Traitez details comme des métadonnées d’exécution, et non comme du contenu d’invite :
- OpenClaw supprime
toolResult.detailsavant la relecture par le fournisseur et la saisie de compactage afin que les métadonnées ne deviennent pas un contexte de model. - Les entrées de session persistantes ne conservent que des
detailslimitées. Les détails trop volumineux sont remplacés par un résumé compact etpersistedDetailsTruncated: true. tool_result_persistetbefore_message_writes’exécutent avant la limite de persistance finale. Les hooks doivent quand même garder lesdetailsrenvoyées petites et éviter de placer du texte pertinent pour l’invite uniquement dansdetails; placez la sortie de l’outil visible par le model danscontent.
Hooks d’invite et de model
Section intitulée « Hooks d’invite et de model »Utilisez les hooks spécifiques à la phase pour les nouveaux plugins :
before_model_resolve: reçoit uniquement l’invite actuelle et les métadonnées des pièces jointes. RetournezproviderOverrideoumodelOverride.agent_turn_prepare: reçoit l’invite actuelle, les messages de session préparés et toutes les injections mises en file d’attente exactement une fois drainées pour cette session. RetournezprependContextouappendContext.before_prompt_build: reçoit l’invite actuelle et les messages de session. RetournezprependContext,appendContext,systemPrompt,prependSystemContextouappendSystemContext.heartbeat_prompt_contribution: ne s’exécute que pour les tours de pulsation (heartbeat) et renvoieprependContextouappendContext. Il est destiné aux moniteurs d’arrière-plan qui doivent résumer l’état actuel sans modifier les tours initiés par l’utilisateur.
before_agent_start reste pour la compatibilité. Préférez les hooks explicites ci-dessus pour que votre plugin ne dépende pas d’une phase combinée héritée.
before_agent_run s’exécute après la construction du prompt et avant toute entrée du modèle, y compris le chargement d’images local au prompt et l’observation llm_input. Il reçoit l’entrée utilisateur actuelle sous forme de prompt, plus l’historique de session chargé dans messages et le prompt système actif. Renoyez { outcome: "block", reason, message? } pour arrêter l’exécution avant que le modèle ne puisse lire le prompt. reason est interne ; message est le remplacement orienté utilisateur. Les seuls résultats pris en charge sont pass et block ; les formes de décision non prises en charge échouent de manière fermée.
Lorsqu’une exécution est bloquée, OpenClaw ne stocke que le texte de remplacement dans message.content, ainsi que les métadonnées de blocage non sensibles telles que l’identifiant du plugin de blocage et l’horodatage. Le texte utilisateur d’origine n’est pas conservé dans la transcription ou le contexte futur. Les raisons de blocage internes sont traitées comme sensibles et exclues des charges utiles de transcription, d’historique, de diffusion, de journal et de diagnostic. L’observabilité doit utiliser des champs assainis tels que l’identifiant du bloqueur, le résultat, l’horodatage ou une catégorie sûre.
before_agent_start et agent_end incluent event.runId lorsque OpenClaw peut identifier l’exécution active. La même valeur est également disponible sur ctx.runId. Les exécutions pilotées par Cron exposent également ctx.jobId (l’identifiant de la tâche cron d’origine) afin que les hooks de plugin puissent délimiter les métriques, les effets secondaires ou l’état à une tâche planifiée spécifique.
Pour les exécutions originaires de canaux, ctx.messageProvider est la surface du fournisseur telle
que discord ou telegram, tandis que ctx.channelId est l’identifiant de la
cible de conversation lorsqu’OpenClaw peut en dériver un à partir de la clé de session ou des
métadonnées de livraison.
agent_end est un hook d’observation et s’exécute en mode fire-and-forget (tirer et oublier) après le tour. Le
moteur de hooks applique un délai d’attente de 30 secondes pour qu’un plugin bloqué ou un point
d’accès d’intégration ne puisse pas laisser la promesse du hook en attente indéfiniment. Un dépassement de
délai est journalisé et OpenClaw continue ; il n’annule pas le travail réseau appartenant au plugin
à moins que le plugin n’utilise également son propre signal d’abandon.
Utilisez model_call_started et model_call_ended pour la télémétrie des appels au fournisseur
qui ne doit pas recevoir de prompts bruts, d’historique, de réponses, d’en-têtes, de corps de
requête ou d’ID de requête fournisseur. Ces hooks incluent des métadonnées stables telles que
runId, callId, provider, model, api/transport facultatifs,
durationMs/outcome terminaux, et upstreamRequestIdHash lorsqu’OpenClaw peut dériver un
haché d’ID de requête fournisseur borné. Lorsque le runtime a résolu les métadonnées de la fenêtre de
contexte, l’événement et le contexte du hook incluent également contextTokenBudget, le
budget effectif de jetons après les limites du modèle/configuration/agent, ainsi que
contextWindowSource et contextWindowReferenceTokens lorsqu’une limite inférieure a
été appliquée.
before_agent_finalize ne s’exécute que lorsqu’un harnais est sur le point d’accepter une réponse
finale naturelle de l’assistant. Ce n’est pas le chemin d’annulation /stop et ne s’exécute pas
lorsque l’utilisateur abandonne un tour. Renvoyez { action: "revise", reason } pour demander
au harnais une passe de modèle supplémentaire avant la finalisation, { action: "finalize", reason? } pour forcer la finalisation, ou omettez un résultat pour continuer.
Les hooks Stop natifs Codex sont relayés vers ce hook en tant que décisions
OpenClaw before_agent_finalize.
Lorsqu’ils renvoient action: "revise", les plugins peuvent inclure des métadonnées retry pour rendre
la passe supplémentaire du modèle bornée et sécurisée pour la relecture :
type BeforeAgentFinalizeRetry = { instruction: string; idempotencyKey?: string; maxAttempts?: number;};instruction est ajouté à la raison de la révision envoyée au harnais.
idempotencyKey permet à l’hôte de compter les nouvelles tentatives pour la même demande de plugin à travers
des décisions de finalisation équivalentes, et maxAttempts limite le nombre de passes supplémentaires que
l’hôte autorisera avant de continuer avec la réponse finale naturelle.
Les plugins non regroupés qui ont besoin de crochets de conversation brute (before_model_resolve,
before_agent_reply, llm_input, llm_output, before_agent_finalize,
agent_end, ou before_agent_run) doivent définir :
{ "plugins": { "entries": { "my-plugin": { "hooks": { "allowConversationAccess": true } } } }}Les crochets de modification de prompt et les injections durables au tour suivant peuvent être désactivés par plugin
avec plugins.entries.<id>.hooks.allowPromptInjection=false.
Extensions de session et injections au tour suivant
Section intitulée « Extensions de session et injections au tour suivant »Les plugins de workflow peuvent rendre persistant un petit état de session compatible JSON avec
api.registerSessionExtension(...) et le mettre à jour via la méthode
Gatewaysessions.pluginPatch. Les lignes de session projettent l’état d’extension enregistré
à travers pluginExtensions, permettant à l’interface de contrôle et autres clients de restituer
le statut appartenant au plugin sans apprendre les éléments internes du plugin.
Utilisez api.enqueueNextTurnInjection(...) lorsqu’un plugin a besoin d’un contexte durable pour
atteindre le tour de modèle suivant exactement une fois. OpenClaw draine les injections mises en file d’attente avant
les crochets de prompt, ignore les injections expirées, et déduplique par idempotencyKey
par plugin. C’est le bon endroit pour les reprises d’approbation, les résumés de politique,
les deltas de moniteur en arrière-plan, et les continuations de commande qui doivent être visibles par
le modèle au tour suivant mais ne doivent pas devenir du texte de prompt système permanent.
La sémantique de nettoyage fait partie du contrat. Les rappels de nettoyage de l’extension de session et du cycle de vie d’exécution reçoivent reset, delete, disable ou restart. L’hôte supprime l’état persistant de l’extension de session du plugin propriétaire et les injections de tour suivant en attente pour la réinitialisation/suppression/désactivation ; le redémarrage conserve l’état durable de la session, tandis que les rappels de nettoyage permettent aux plugins de libérer les tâches du planificateur, le contexte d’exécution et d’autres ressources hors bande pour l’ancienne génération d’exécution.
Hooks de message
Section intitulée « Hooks de message »Utilisez les hooks de message pour le routage et la politique de livraison au niveau du canal :
message_received: observer le contenu entrant, l’expéditeur,threadId,messageId,senderId, la corrélation optionnelle exécution/session, et les métadonnées.message_sending: réécrirecontentou retourner{ cancel: true }.message_sent: observer le succès ou l’échec final.
Pour les réponses TTS audio uniquement, content peut contenir la transcription parlée masquée
même lorsque la charge utile du canal n’a pas de texte/légende visible. La réécriture de ce
content met à jour uniquement la transcription visible par le hook ; elle n’est pas rendue comme une
légende média.
Les contextes de hooks de message exposent des champs de corrélation stables lorsqu’ils sont disponibles :
ctx.sessionKey, ctx.runId, ctx.messageId, ctx.senderId, ctx.trace,
ctx.traceId, ctx.spanId, ctx.parentSpanId et ctx.callDepth. Privilégiez
ces champs de première classe avant de lire les métadonnées héritées.
Privilégiez les champs typés threadId et replyToId avant d’utiliser les métadonnées spécifiques au canal.
Règles de décision :
message_sendingaveccancel: trueest terminal.message_sendingaveccancel: falseest traité comme une absence de décision.- Le
contentréécrit continue vers les hooks de priorité inférieure à moins qu’un hook ultérieur n’annule la livraison. message_sendingpeut retournercancelReasonetmetadataborné lors d’une annulation. Les nouvelles API de cycle de vie des messages exposent cela comme un résultat de livraison supprimé avec la raisoncancelled_by_message_sending_hook; la livraison directe héritée continue de retourner un tableau de résultats vide pour la compatibilité.message_sentest en observation seule. Les échecs du gestionnaire sont consignés et ne modifient pas le résultat de la livraison.
Hooks d’installation
Section intitulée « Hooks d’installation »before_install s’exécute après l’analyse intégrée des installations de compétences et de plugins. Retournez des résultats supplémentaires ou { block: true, blockReason } pour arrêter l’installation.
block: true est terminal. block: false est traité comme une absence de décision.
Cycle de vie du Gateway
Section intitulée « Cycle de vie du Gateway »Utilisez gateway_start pour les services de plugin qui nécessitent un état détenu par le Gateway. Le contexte expose ctx.config, ctx.workspaceDir et ctx.getCron?.() pour l’inspection et les mises à jour de cron. Utilisez gateway_stop pour nettoyer les ressources de longue durée.
Ne comptez pas sur le hook interne gateway:startup pour les services d’exécution détenus par le plugin.
cron_changed se déclenche pour les événements du cycle de vie cron appartenant à la passerelle avec une charge utile d’événement typée couvrant added, updated, removed, started, finished,
et les raisons scheduled. L’événement transporte un instantané PluginHookGatewayCronJob
(y compris state.nextRunAtMs, state.lastRunStatus et
state.lastError si présents) ainsi qu’un PluginHookGatewayCronDeliveryStatus
de not-requested | delivered | not-delivered | unknown. Les événements
de suppression transportent toujours l’instantané de la tâche supprimée afin que les planificateurs externes puissent
réconcilier l’état. Utilisez ctx.getCron?.() et ctx.config depuis le contexte
d’exécution lors de la synchronisation des planificateurs de réveil externes, et gardez OpenClaw comme
source de vérité pour les vérifications d’échéance et l’exécution.
Dépréciations à venir
Section intitulée « Dépréciations à venir »Quelques surfaces adjacentes aux crochets (hooks) sont dépréciées mais toujours prises en charge. Migrez avant la prochaine version majeure :
- Enveloppes de canal en texte brut dans les gestionnaires
inbound_claimetmessage_received. LisezBodyForAgentet les blocs de contexte utilisateur structurés au lieu d’analyser le texte d’enveloppe plat. Voir Plaintext channel envelopes → BodyForAgent. before_agent_startreste pour compatibilité. Les nouveaux plugins doivent utiliserbefore_model_resolveetbefore_prompt_buildau lieu de la phase combinée.deactivatereste un alias de compatibilité de nettoyage déprécié jusqu’au-delà du 16 août 2026. Les nouveaux plugins doivent utilisergateway_stop.onResolutiondansbefore_tool_callutilise désormais l’union typéePluginApprovalResolution(allow-once/allow-always/deny/timeout/cancelled) au lieu d’unstringlibre.
Pour la liste complète - inscription des capacités mémoire, profil de réflexion du provider, fournisseurs d’authentification externes, types de découverte de provider, accesseurs d’exécution de tâche, et le renommage command-auth → command-status - voir Plugin SDK migration → Active deprecations.
Connexes
Section intitulée « Connexes »- Plugin SDK migration - dépréciations actives et calendrier de suppression
- Building plugins
- Plugin SDK overview
- Plugin entry points
- Internal hooks
- Plugin architecture internals